35#include <libxml/parser.h>
36#include <libxml/xpath.h>
42#include <sys/sysctl.h>
51#include "acfutils/png.h"
52#include "acfutils/stat.h"
54#include "acfutils/worker.h"
56#include "chartdb_impl.h"
57#include "chart_prov_common.h"
58#include "chart_prov_faa.h"
59#include "chart_prov_autorouter.h"
60#include "chart_prov_navigraph.h"
62#define MAX_METAR_AGE 60
63#define MAX_TAF_AGE 300
64#define RETRY_INTVAL 30
65#define WRITE_BUFSZ 4096
66#define READ_BUFSZ 4096
68#define DESTROY_HANDLE(__handle__) \
70 if ((__handle__) != NULL) { \
71 CloseHandle((__handle__)); \
72 (__handle__) = NULL; \
78 .name =
"aeronav.faa.gov",
79 .init = chart_faa_init,
80 .fini = chart_faa_fini,
81 .get_chart = chart_faa_get_chart
84 .name =
"autorouter.aero",
85 .init = chart_autorouter_init,
86 .fini = chart_autorouter_fini,
87 .get_chart = chart_autorouter_get_chart,
88 .arpt_lazyload = chart_autorouter_arpt_lazyload,
89 .test_conn = chart_autorouter_test_conn
92 .name =
"navigraph.com",
93 .init = chart_navigraph_init,
94 .fini = chart_navigraph_fini,
95 .get_chart = chart_navigraph_get_chart,
96 .watermark_chart = chart_navigraph_watermark_chart,
97 .arpt_lazy_discover = chart_navigraph_arpt_lazy_discover,
98 .pending_ext_account_setup = chart_navigraph_pending_ext_account_setup
102static chart_arpt_t *arpt_find(chartdb_t *cdb,
const char *icao);
103static char *download_metar(chartdb_t *cdb,
const char *icao);
104static char *download_taf(chartdb_t *cdb,
const char *icao);
111 MEMORYSTATUSEX status;
112 status.dwLength =
sizeof(status);
113 VERIFY(GlobalMemoryStatusEx(&status));
114 return (status.ullTotalPhys);
122 int mib[2] = { CTL_HW, HW_MEMSIZE };
124 size_t length =
sizeof(int64_t);
125 sysctl(mib, 2, &mem, &length, NULL, 0);
134 uint64_t pages = sysconf(_SC_PHYS_PAGES);
135 uint64_t page_size = sysconf(_SC_PAGE_SIZE);
136 return (pages * page_size);
142chart_name_compar(
const void *a,
const void *b)
144 const chart_t *ca = a, *cb = b;
145 int res = strcmp(ca->name, cb->name);
155arpt_compar(
const void *a,
const void *b)
157 const chart_arpt_t *ca = a, *cb = b;
158 int res = strcmp(ca->icao, cb->icao);
168chartdb_chart_destroy(chart_t *chart)
172 if (chart->surf != NULL)
173 cairo_surface_destroy(chart->surf);
174 if (chart->png_data != NULL) {
175 memset(chart->png_data, 0, chart->png_data_len);
176 free(chart->png_data);
179 free(chart->codename);
180 free(chart->filename);
181 free(chart->filename_night);
186arpt_destroy(chart_arpt_t *arpt)
193 chartdb_chart_destroy(chart);
200 free(arpt->codename);
206remove_old_airacs(chartdb_t *cdb)
208 char *dpath =
mkpathname(cdb->path, cdb->prov_name, NULL);
211 time_t now = time(NULL);
218 logMsg(
"Error accessing directory %s: %s", dpath,
222 while ((de = readdir(dp)) != NULL) {
227 if (strlen(de->d_name) != 4 ||
228 sscanf(de->d_name,
"%u", &nr) != 1 ||
229 nr < 1000 || nr >= cdb->airac) {
232 subpath =
mkpathname(dpath, de->d_name, NULL);
233 if (stat(subpath, &st) == 0) {
234 if (now - st.st_mtime > 30 * 86400)
246loader_init(
void *userinfo)
248 chartdb_t *cdb = userinfo;
251 ASSERT3U(cdb->prov, <, NUM_PROVIDERS);
254 remove_old_airacs(cdb);
256 if (!prov[cdb->prov].init(cdb))
260 cdb->init_complete = B_TRUE;
267loader_purge(chartdb_t *cdb)
269 for (chart_arpt_t *arpt =
avl_first(&cdb->arpts); arpt != NULL;
270 arpt =
AVL_NEXT(&cdb->arpts, arpt)) {
271 for (chart_t *chart =
avl_first(&arpt->charts); chart != NULL;
272 chart =
AVL_NEXT(&arpt->charts, chart)) {
273 if (chart->surf != NULL) {
274 cairo_surface_destroy(chart->surf);
277 if (chart->png_data != NULL) {
278 free(chart->png_data);
279 chart->png_data = NULL;
280 chart->png_data_len = 0;
289chartdb_add_arpt(chartdb_t *cdb,
const char *icao,
const char *name,
290 const char *city_name,
const char *state_id)
292 chart_arpt_t *arpt, srch;
300 arpt =
avl_find(&cdb->arpts, &srch, &where);
303 avl_create(&arpt->charts, chart_name_compar, sizeof (chart_t),
304 offsetof(chart_t, node));
308 lacf_strlcpy(arpt->state, state_id, sizeof (arpt->state));
318chartdb_add_chart(chart_arpt_t *arpt, chart_t *chart)
321 chartdb_t *cdb = arpt->db;
326 if (
avl_find(&arpt->charts, chart, &where) != NULL) {
332 chart->num_pages = -1;
333 arpt->load_complete = B_TRUE;
340chartdb_mkpath(chart_t *chart)
342 chart_arpt_t *arpt = chart->arpt;
350 snprintf(airac_nr,
sizeof (airac_nr),
"%d", cdb->airac);
352 return (
mkpathname(cdb->path, prov[cdb->prov].name, airac_nr,
353 chart->filename, NULL));
355 return (
mkpathname(cdb->path, prov[cdb->prov].name, airac_nr,
356 arpt->icao, chart->filename, NULL));
361chartdb_pdf_count_pages_direct(
const char *pdfinfo_path,
const uint8_t *buf,
366 int fd_in = -1, fd_out = -1;
368 size_t cap = 0, fill = 0;
370 char *out_buf = NULL;
375 SECURITY_ATTRIBUTES sa;
376 HANDLE stdin_rd_handle = NULL;
377 HANDLE stdin_wr_handle = NULL;
378 HANDLE stdout_rd_handle = NULL;
379 HANDLE stdout_wr_handle = NULL;
380 PROCESS_INFORMATION pi;
382 char cmd[3 * MAX_PATH];
383 TCHAR cmdT[3 * MAX_PATH];
385 memset(&pi, 0,
sizeof (pi));
387 snprintf(cmd,
sizeof (cmd),
"\"%s\" fd://0", pdfinfo_path);
388 MultiByteToWideChar(CP_UTF8, 0, cmd, -1, cmdT, 3 * MAX_PATH);
390 sa.nLength =
sizeof(sa);
391 sa.bInheritHandle = TRUE;
392 sa.lpSecurityDescriptor = NULL;
394 if (!CreatePipe(&stdout_rd_handle, &stdout_wr_handle, &sa, 0) ||
395 !SetHandleInformation(stdout_rd_handle, HANDLE_FLAG_INHERIT, 0) ||
396 !CreatePipe(&stdin_rd_handle, &stdin_wr_handle, &sa, 0) ||
397 !SetHandleInformation(stdin_wr_handle, HANDLE_FLAG_INHERIT, 0)) {
398 win_perror(GetLastError(),
"Error creating pipe");
401 ZeroMemory(&pi,
sizeof(PROCESS_INFORMATION));
402 ZeroMemory(&si,
sizeof(STARTUPINFO));
403 si.cb =
sizeof(STARTUPINFO);
404 si.hStdInput = stdin_rd_handle;
405 si.hStdOutput = stdout_wr_handle;
406 si.dwFlags |= STARTF_USESTDHANDLES;
408 fd_in = _open_osfhandle((intptr_t)stdin_wr_handle, _O_WRONLY);
409 fd_out = _open_osfhandle((intptr_t)stdout_rd_handle, _O_RDONLY);
410 if (fd_in == -1 || fd_out == -1) {
411 win_perror(GetLastError(),
"Error opening pipe as fd");
417 stdin_wr_handle = NULL;
418 stdout_rd_handle = NULL;
420 if (!CreateProcess(NULL, cmdT, NULL, NULL, TRUE,
421 CREATE_NO_WINDOW | BELOW_NORMAL_PRIORITY_CLASS,
422 NULL, NULL, &si, &pi)) {
423 win_perror(GetLastError(),
"Error invoking %s", pdfinfo_path);
428 int stdin_pipe[2] = { -1, -1 };
429 int stdout_pipe[2] = { -1, -1 };
431 if (pipe(stdin_pipe) < 0 || pipe(stdout_pipe) < 0) {
432 logMsg(
"Error counting PDF pages: pipe failed: %s\n",
440 logMsg(
"Error counting PDF pages: fork failed: %s\n",
444 dup2(stdin_pipe[0], STDIN_FILENO);
445 dup2(stdout_pipe[1], STDOUT_FILENO);
446 for (
int i = 0; i < 2; i++) {
447 close(stdin_pipe[i]);
448 close(stdout_pipe[i]);
451 setenv(
"DYLD_LIBRARY_PATH", dpath, 1);
453 setenv(
"LD_LIBRARY_PATH", dpath, 1);
455 execl(pdfinfo_path, pdfinfo_path,
"fd://0", NULL);
456 logMsg(
"Error counting PDF pages: execl failed: %s\n",
460 fd_in = dup(stdin_pipe[1]);
461 fd_out = dup(stdout_pipe[0]);
464 for (
int i = 0; i < 2; i++) {
465 close(stdin_pipe[i]);
467 close(stdout_pipe[i]);
474 while (written < len) {
475 int n = write(fd_in, &buf[written], len - written);
477 logMsg(
"write error: %s", strerror(errno));
490 if (cap - fill < READ_BUFSZ) {
494 to_read = cap - fill;
495 n = read(fd_out, &out_buf[fill], to_read);
497 logMsg(
"read error: %s", strerror(errno));
517 lines =
strsplit(out_buf,
"\n", B_TRUE, &num_lines);
518 for (
size_t i = 0; i < num_lines; i++) {
519 char *line = lines[i];
520 if (strncmp(line,
"Pages:", 6) == 0) {
525 comps =
strsplit(line,
" ", B_TRUE, &n_comps);
527 num_pages = atoi(comps[1]);
534 WaitForSingleObject(pi.hProcess, INFINITE);
537 waitpid(child_pid, NULL, 0);
548 DESTROY_HANDLE(stdin_rd_handle);
549 DESTROY_HANDLE(stdin_wr_handle);
550 DESTROY_HANDLE(stdout_rd_handle);
551 DESTROY_HANDLE(stdout_wr_handle);
552 DESTROY_HANDLE(pi.hProcess);
553 DESTROY_HANDLE(pi.hThread);
555 if (stdin_pipe[0] != -1) {
556 close(stdin_pipe[0]);
557 close(stdin_pipe[1]);
559 if (stdout_pipe[0] != -1) {
560 close(stdout_pipe[0]);
561 close(stdout_pipe[1]);
569 logMsg(
"Unable to read page count");
575chartdb_pdf_count_pages_file(
const char *pdfinfo_path,
const char *path)
578 size_t len = 0, fill = 0;
579 FILE *infp = fopen(path,
"rb");
583 logMsg(
"Error counting PDF pages %s: can't read input: %s",
584 path, strerror(errno));
590 if (len - fill < READ_BUFSZ) {
594 ret = fread(&buf[fill], 1, len - fill, infp);
597 if (ret < READ_BUFSZ) {
599 logMsg(
"Error counting PDF pages %s: "
600 "error reading input", path);
606 pages = chartdb_pdf_count_pages_direct(pdfinfo_path, buf, fill);
615chartdb_pdf_convert_file(
const char *pdftoppm_path,
char *old_path,
int page,
618 char *ext = NULL, *new_path = NULL;
619 uint8_t *pdf_buf = NULL;
620 size_t pdf_len = 0, pdf_fill = 0;
621 uint8_t *png_buf = NULL;
623 FILE *infp = NULL, *outfp = NULL;
625 infp = fopen(old_path,
"rb");
627 logMsg(
"Error converting chart %s: can't read input: %s",
628 old_path, strerror(errno));
634 if (pdf_len - pdf_fill < READ_BUFSZ) {
635 pdf_len += READ_BUFSZ;
638 ret = fread(&pdf_buf[pdf_fill], 1, pdf_len - pdf_fill, infp);
641 if (ret < READ_BUFSZ) {
643 logMsg(
"Error converting chart %s: "
644 "error reading input", old_path);
653 png_buf = chartdb_pdf_convert_direct(pdftoppm_path, pdf_buf, pdf_fill,
654 page, zoom, &png_len);
659 ext = strrchr(new_path,
'.');
663 outfp = fopen(new_path,
"wb");
665 logMsg(
"Error converting chart %s: can't write output "
666 "file %s: %s", old_path, new_path, strerror(errno));
669 if (fwrite(png_buf, 1, png_len, outfp) < png_len) {
670 logMsg(
"Error converting chart %s: can't write output to "
671 "file %s", old_path, new_path);
693chartdb_pdf_convert_direct(
const char *pdftoppm_path,
const uint8_t *pdf_data,
694 size_t len,
int page,
double zoom,
size_t *out_len)
697 int fd_in = -1, fd_out = -1;
703 uint8_t *png_buf = NULL;
704 int png_buf_sz = 0, png_buf_fill = 0;
706 zoom =
clamp(zoom, 0.1, 10.0);
715 SECURITY_ATTRIBUTES sa;
716 HANDLE stdin_rd_handle = NULL;
717 HANDLE stdin_wr_handle = NULL;
718 HANDLE stdout_rd_handle = NULL;
719 HANDLE stdout_wr_handle = NULL;
720 PROCESS_INFORMATION pi;
722 char cmd[3 * MAX_PATH];
723 TCHAR cmdT[3 * MAX_PATH];
726 sa.nLength =
sizeof(sa);
727 sa.bInheritHandle = TRUE;
728 sa.lpSecurityDescriptor = NULL;
730 if (!CreatePipe(&stdout_rd_handle, &stdout_wr_handle, &sa, 0) ||
731 !SetHandleInformation(stdout_rd_handle, HANDLE_FLAG_INHERIT, 0) ||
732 !CreatePipe(&stdin_rd_handle, &stdin_wr_handle, &sa, 0) ||
733 !SetHandleInformation(stdin_wr_handle, HANDLE_FLAG_INHERIT, 0)) {
734 win_perror(GetLastError(),
"Error creating pipes");
737 ZeroMemory(&pi,
sizeof(PROCESS_INFORMATION));
738 ZeroMemory(&si,
sizeof(STARTUPINFO));
739 si.cb =
sizeof(STARTUPINFO);
740 si.hStdInput = stdin_rd_handle;
741 si.hStdOutput = stdout_wr_handle;
742 si.dwFlags |= STARTF_USESTDHANDLES;
744 fd_in = _open_osfhandle((intptr_t)stdin_wr_handle, _O_WRONLY);
745 fd_out = _open_osfhandle((intptr_t)stdout_rd_handle, _O_RDONLY);
746 if (fd_in == -1 || fd_out == -1) {
747 win_perror(GetLastError(),
"Error opening pipe as fd");
753 stdin_wr_handle = NULL;
754 stdout_rd_handle = NULL;
756 snprintf(cmd,
sizeof (cmd),
"\"%s\" -png -f %d -l %d -r %d -cropbox",
757 pdftoppm_path, page + 1, page + 1, (
int)(100 * zoom));
758 MultiByteToWideChar(CP_UTF8, 0, cmd, -1, cmdT, 3 * MAX_PATH);
760 if (!CreateProcess(NULL, cmdT, NULL, NULL, TRUE,
761 CREATE_NO_WINDOW | BELOW_NORMAL_PRIORITY_CLASS,
762 NULL, NULL, &si, &pi)) {
763 win_perror(GetLastError(),
"Error converting chart to PNG");
767 int stdin_pipe[2] = { -1, -1 };
768 int stdout_pipe[2] = { -1, -1 };
769 char page_nr[8], zoom_nr[8];
771 if (pipe(stdin_pipe) < 0 || pipe(stdout_pipe) < 0) {
772 logMsg(
"Error converting chart to PNG: "
773 "error creating pipes: %s", strerror(errno));
776 snprintf(page_nr,
sizeof (page_nr),
"%d", page + 1);
777 snprintf(zoom_nr,
sizeof (zoom_nr),
"%d", (
int)(100 * zoom));
782 logMsg(
"Error converting chart to PNG: fork failed: %s",
786 dup2(stdin_pipe[0], STDIN_FILENO);
787 dup2(stdout_pipe[1], STDOUT_FILENO);
788 for (
int i = 0; i < 2; i++) {
789 close(stdin_pipe[i]);
790 close(stdout_pipe[i]);
793 setenv(
"DYLD_LIBRARY_PATH", dpath, 1);
795 setenv(
"LD_LIBRARY_PATH", dpath, 1);
799 execl(pdftoppm_path, pdftoppm_path,
"-png",
"-f", page_nr,
800 "-l", page_nr,
"-r", zoom_nr,
"-cropbox", NULL);
801 logMsg(
"Error converting chart to PNG: execv failed: %s",
805 fd_in = dup(stdin_pipe[1]);
806 fd_out = dup(stdout_pipe[0]);
807 for (
int i = 0; i < 2; i++) {
808 close(stdin_pipe[i]);
810 close(stdout_pipe[i]);
817 while (written < len) {
818 int n = write(fd_in, &pdf_data[written], len - written);
820 logMsg(
"write error: %s", strerror(errno));
833 if (png_buf_sz - png_buf_fill < READ_BUFSZ) {
834 png_buf_sz += READ_BUFSZ;
837 to_read = png_buf_sz - png_buf_fill;
838 n = read(fd_out, &png_buf[png_buf_fill], to_read);
855 DESTROY_HANDLE(stdin_rd_handle);
856 DESTROY_HANDLE(stdin_wr_handle);
857 DESTROY_HANDLE(stdout_rd_handle);
858 DESTROY_HANDLE(stdout_wr_handle);
859 stdin_rd_handle = NULL;
860 stdin_wr_handle = NULL;
861 stdout_rd_handle = NULL;
862 stdout_wr_handle = NULL;
864 WaitForSingleObject(pi.hProcess, INFINITE);
865 VERIFY(GetExitCodeProcess(pi.hProcess, &exit_code_win));
866 exit_code = exit_code_win;
867 DESTROY_HANDLE(pi.hProcess);
868 DESTROY_HANDLE(pi.hThread);
870 while (waitpid(child_pid, &exit_code, 0) < 0) {
871 if (errno != EINTR) {
872 logMsg(
"Error converting chart to PNG: "
873 "waitpid failed: %s", strerror(errno));
878 exit_code = WEXITSTATUS(exit_code);
881 if (exit_code != 0) {
882 logMsg(
"Error converting chart to PNG. Command returned "
883 "error code %d", exit_code);
888 *out_len = png_buf_fill;
896 DESTROY_HANDLE(stdout_rd_handle);
897 DESTROY_HANDLE(stdout_wr_handle);
898 DESTROY_HANDLE(stdin_rd_handle);
899 DESTROY_HANDLE(stdin_wr_handle);
901 if (stdin_pipe[0] != -1) {
902 close(stdin_pipe[0]);
903 close(stdin_pipe[1]);
905 if (stdout_pipe[0] != -1) {
906 close(stdout_pipe[0]);
907 close(stdout_pipe[1]);
916invert_surface(cairo_surface_t *surf)
918 uint8_t *data = cairo_image_surface_get_data(surf);
919 int stride = cairo_image_surface_get_stride(surf);
920 int width = cairo_image_surface_get_width(surf);
921 int height = cairo_image_surface_get_height(surf);
923 cairo_surface_flush(surf);
925 switch (cairo_image_surface_get_format(surf)) {
926 case CAIRO_FORMAT_ARGB32:
927 case CAIRO_FORMAT_RGB24:
928 for (
int y = 0; y < height; y++) {
929 uint8_t *p = data + y * stride;
931 for (
int x = 0; x < width; x++) {
932#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
946 logMsg(
"Unable to invert surface colors: unsupported "
947 "format %x", cairo_image_surface_get_format(surf));
950 cairo_surface_mark_dirty(surf);
953static cairo_surface_t *
954chart_get_surface_nocache(chartdb_t *cdb, chart_t *chart)
958 cairo_surface_t *surf;
964 if (chart->png_data == NULL)
966 png_pixels = png_load_from_buffer_cairo_argb32(chart->png_data,
967 chart->png_data_len, &width, &height);
968 if (png_pixels == NULL)
970 surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
971 surf_data = (uint32_t *)cairo_image_surface_get_data(surf);
972 ASSERT(surf_data != NULL);
973 memcpy(surf_data, png_pixels, width * height * 4);
974 cairo_surface_mark_dirty(surf);
982chart_needs_get(chartdb_t *cdb, chart_t *chart)
986 if (!cdb->disallow_caching) {
991 char *path = chartdb_mkpath(chart);
992 bool_t result = (!chart->refreshed || !
file_exists(path, NULL));
1002 return (chart->png_data == NULL ||
1003 (chart->filename_night != NULL &&
1004 chart->night_prev != chart->night));
1008static cairo_surface_t *
1009chart_get_surface(chartdb_t *cdb, chart_t *chart)
1011 char *path = NULL, *ext = NULL;
1012 cairo_surface_t *surf = NULL;
1017 if (chart->load_cb != NULL)
1018 return (chart->load_cb(chart));
1019 if (chart_needs_get(cdb, chart)) {
1020 chart->refreshed = B_TRUE;
1021 if (!prov[cdb->prov].get_chart(chart)) {
1023 chart->load_error = B_TRUE;
1028 chart->night_prev = chart->night;
1029 if (cdb->disallow_caching) {
1030 surf = chart_get_surface_nocache(cdb, chart);
1033 path = chartdb_mkpath(chart);
1034 ext = strrchr(path,
'.');
1036 (strcmp(&ext[1],
"pdf") == 0 || strcmp(&ext[1],
"PDF") == 0)) {
1037 if (cdb->pdfinfo_path == NULL ||
1038 cdb->pdftoppm_path == NULL) {
1039 logMsg(
"Attempted to load PDF chart, but this chart "
1040 "DB instance doesn't support PDF conversion");
1042 chart->load_error = B_TRUE;
1046 if (chart->num_pages == -1) {
1047 chart->num_pages = chartdb_pdf_count_pages_file(
1048 cdb->pdfinfo_path, path);
1050 if (chart->num_pages == -1) {
1052 chart->load_error = B_TRUE;
1056 path = chartdb_pdf_convert_file(cdb->pdftoppm_path, path,
1057 chart->load_page, chart->zoom);
1060 chart->load_page = chart->cur_page;
1061 chart->load_error = B_TRUE;
1066 surf = cairo_image_surface_create_from_png(path);
1073loader_load(chartdb_t *cdb, chart_t *chart)
1075 cairo_surface_t *surf = chart_get_surface(cdb, chart);
1080 if ((st = cairo_surface_status(surf)) == CAIRO_STATUS_SUCCESS) {
1086 if (chart->night && chart->filename_night == NULL)
1087 invert_surface(surf);
1088 if (prov[cdb->prov].watermark_chart != NULL)
1089 prov[cdb->prov].watermark_chart(chart, surf);
1093 chart->cur_page = chart->load_page;
1096 logMsg(
"Can't load chart %s PNG file %s", chart->name,
1097 cairo_status_to_string(st));
1099 chart->load_error = B_TRUE;
1105chart_mem_usage(chartdb_t *cdb)
1109 for (chart_t *c =
list_head(&cdb->load_seq); c != NULL;
1111 if (c->surf != NULL) {
1112 unsigned w = cairo_image_surface_get_stride(c->surf);
1113 unsigned h = cairo_image_surface_get_height(c->surf);
1117 total += c->png_data_len;
1124loader(
void *userinfo)
1126 chartdb_t *cdb = userinfo;
1132 if (arpt->load_complete ||
1133 prov[cdb->prov].arpt_lazyload == NULL)
1136 prov[cdb->prov].arpt_lazyload(arpt);
1140 if (chart == &cdb->loader_cmd_purge) {
1142 }
else if (chart == &cdb->loader_cmd_metar) {
1145 chart->arpt->metar_load_t = time(NULL);
1147 metar = download_metar(cdb, chart->arpt->icao);
1149 if (chart->arpt->metar != NULL)
1150 free(chart->arpt->metar);
1151 chart->arpt->metar = metar;
1152 if (metar == NULL) {
1153 chart->arpt->metar_load_t = time(NULL) -
1154 (MAX_METAR_AGE - RETRY_INTVAL);
1156 }
else if (chart == &cdb->loader_cmd_taf) {
1159 chart->arpt->taf_load_t = time(NULL);
1161 taf = download_taf(cdb, chart->arpt->icao);
1163 if (chart->arpt->taf != NULL)
1164 free(chart->arpt->taf);
1165 chart->arpt->taf = taf;
1167 chart->arpt->taf_load_t = time(NULL) -
1168 (MAX_TAF_AGE - RETRY_INTVAL);
1172 loader_load(cdb, chart);
1180 chart_mem_usage(cdb) > cdb->load_limit) {
1183 if (c->surf != NULL) {
1184 cairo_surface_destroy(c->surf);
1187 if (c->png_data != NULL) {
1190 c->png_data_len = 0;
1202loader_fini(
void *userinfo)
1204 chartdb_t *cdb = userinfo;
1207 ASSERT3U(cdb->prov, <, NUM_PROVIDERS);
1208 prov[cdb->prov].fini(cdb);
1213 const char *pdfinfo_path,
unsigned airac,
const char *provider_name,
1217 chart_prov_id_t pid;
1219 ASSERT(cache_path != NULL);
1222 ASSERT(provider_name != NULL);
1225 for (pid = 0; pid < NUM_PROVIDERS; pid++) {
1226 if (strcmp(provider_name, prov[pid].name) == 0)
1229 if (pid >= NUM_PROVIDERS)
1234 avl_create(&cdb->arpts, arpt_compar, sizeof (chart_arpt_t),
1235 offsetof(chart_arpt_t, node));
1237 if (pdftoppm_path != NULL)
1239 if (pdfinfo_path != NULL)
1244 if (provider_login != NULL) {
1245 cdb->prov_login =
safe_calloc(1,
sizeof (*cdb->prov_login));
1246 if (provider_login->
username != NULL) {
1250 if (provider_login->
password != NULL) {
1254 if (provider_login->
cainfo != NULL) {
1259 cdb->normalize_non_icao = B_TRUE;
1261 cdb->load_limit = MIN(physmem() >> 5, 256 << 20);
1262 lacf_strlcpy(cdb->prov_name, provider_name, sizeof (cdb->prov_name));
1265 offsetof(chart_t, loader_node));
1266 list_create(&cdb->loader_arpt_queue, sizeof (chart_arpt_t),
1267 offsetof(chart_arpt_t, loader_node));
1269 offsetof(chart_t, load_seq_node));
1271 worker_init2(&cdb->loader, loader_init, loader, loader_fini, 0, cdb,
1283 worker_fini(&cdb->loader);
1303 free(cdb->pdftoppm_path);
1304 free(cdb->pdfinfo_path);
1305 if (cdb->prov_login != NULL) {
1306 if (cdb->prov_login->username != NULL) {
1308 strlen(cdb->prov_login->username));
1310 if (cdb->prov_login->password != NULL) {
1312 strlen(cdb->prov_login->password));
1314 if (cdb->prov_login->cainfo != NULL) {
1316 strlen(cdb->prov_login->cainfo));
1334 ASSERT(provider_name != NULL);
1338 for (chart_prov_id_t pid = 0; pid < NUM_PROVIDERS; pid++) {
1339 if (strcmp(provider_name, prov[pid].name) == 0) {
1340 if (prov[pid].test_conn == NULL)
1342 return (prov[pid].test_conn(creds, proxy));
1351 bytes = MAX(bytes, 16 << 20);
1352 if (cdb->load_limit != bytes) {
1353 cdb->load_limit = bytes;
1354 worker_wake_up(&cdb->loader);
1367 worker_wake_up(&cdb->loader);
1390 ASSERT(proxy != NULL || cap == 0);
1393 if (cdb->proxy != NULL) {
1394 len = strlen(cdb->proxy) + 1;
1416 arpt = arpt_find(cdb, icao);
1422 if (!arpt->load_complete) {
1432 worker_wake_up(&cdb->loader);
1438 for (chart =
avl_first(&arpt->charts), num = 0; chart != NULL;
1439 chart =
AVL_NEXT(&arpt->charts, chart)) {
1440 if ((chart->type & type) != 0)
1449 for (chart =
avl_first(&arpt->charts), i = 0; chart != NULL;
1450 chart =
AVL_NEXT(&arpt->charts, chart)) {
1451 if ((chart->type & type) != 0) {
1453 ASSERT(chart->name[0] !=
'\0');
1458 if (cdb->chart_sort_func != NULL) {
1460 cdb->chart_sort_func, cdb);
1476static chart_arpt_t *
1477arpt_find(chartdb_t *cdb,
const char *icao)
1479 chart_arpt_t srch = {};
1484 if (cdb->normalize_non_icao) {
1485 switch (strlen(icao)) {
1491 snprintf(srch.icao, sizeof (srch.icao),
"K%s", icao);
1502 arpt =
avl_find(&cdb->arpts, &srch, NULL);
1503 if (arpt == NULL && prov[cdb->prov].arpt_lazy_discover != NULL)
1504 arpt = prov[cdb->prov].arpt_lazy_discover(cdb, icao);
1509chart_find(chartdb_t *cdb,
const char *icao,
const char *chart_name)
1511 chart_arpt_t *arpt = arpt_find(cdb, icao);
1512 chart_t srch_chart = { .name = (
char *)chart_name };
1515 return (
avl_find(&arpt->charts, &srch_chart, NULL));
1520 const char *chart_name)
1523 char *codename = NULL;
1527 ASSERT(chart_name != NULL);
1531 chart = chart_find(cdb, icao, chart_name);
1532 if (chart == NULL || chart->load_error) {
1536 if (chart->codename != NULL)
1551 ASSERT(chart_name != NULL);
1554 chart = chart_find(cdb, icao, chart_name);
1555 if (chart == NULL || chart->load_error) {
1557 return (CHART_TYPE_UNKNOWN);
1567 const char *chart_name)
1574 ASSERT(chart_name != NULL);
1577 chart = chart_find(cdb, icao, chart_name);
1578 if (chart == NULL || chart->load_error) {
1582 georef = chart->georef;
1597 ASSERT(chart_name != NULL);
1601 chart = chart_find(cdb, icao, chart_name);
1602 if (chart == NULL || chart->load_error) {
1606 bbox = chart->views[view];
1614 const char *chart_name)
1621 ASSERT(chart_name != NULL);
1624 chart = chart_find(cdb, icao, chart_name);
1625 if (chart == NULL || chart->load_error) {
1629 procs = chart->procs;
1637 const char *chart_name,
int page,
double zoom, bool_t night,
1638 cairo_surface_t **surf,
int *num_pages)
1644 ASSERT(chart_name != NULL);
1649 if (num_pages != NULL)
1654 chart = chart_find(cdb, icao, chart_name);
1655 if (chart == NULL || chart->load_error) {
1660 if ((chart->surf == NULL || chart->zoom != zoom ||
1661 chart->night != night || chart->cur_page != page) &&
1664 chart->load_page = page;
1665 chart->night = night;
1673 worker_wake_up(&cdb->loader);
1676 if (chart->surf != NULL && page == chart->cur_page &&
1677 chart->night == night) {
1678 *surf = cairo_surface_reference(chart->surf);
1682 if (num_pages != NULL)
1683 *num_pages = chart->num_pages;
1691get_metar_taf_common(chartdb_t *cdb,
const char *icao, bool_t metar)
1693 char *result = NULL;
1694 time_t now = time(NULL);
1699 arpt = arpt_find(cdb, icao);
1710 if (metar && now - arpt->metar_load_t < MAX_METAR_AGE) {
1712 if (arpt->metar != NULL)
1714 }
else if (!metar && now - arpt->taf_load_t < MAX_TAF_AGE) {
1716 if (arpt->taf != NULL)
1721 &cdb->loader_cmd_metar.loader_node)) {
1723 cdb->loader_cmd_metar.arpt = arpt;
1725 &cdb->loader_cmd_metar);
1726 worker_wake_up(&cdb->loader);
1729 if (arpt->metar != NULL)
1733 &cdb->loader_cmd_taf.loader_node)) {
1735 cdb->loader_cmd_taf.arpt = arpt;
1737 &cdb->loader_cmd_taf);
1738 worker_wake_up(&cdb->loader);
1741 if (arpt->taf != NULL)
1753 bool_t init_complete;
1755 init_complete = cdb->init_complete;
1757 return (init_complete);
1765 arpt = arpt_find(cdb, icao);
1767 return (arpt != NULL);
1770#define ARPT_GET_COMMON(field_name) \
1772 chart_arpt_t *arpt; \
1774 mutex_enter(&cdb->lock); \
1775 arpt = arpt_find(cdb, icao); \
1776 if (arpt == NULL) { \
1777 mutex_exit(&cdb->lock); \
1780 field_name = safe_strdup(arpt->field_name); \
1781 mutex_exit(&cdb->lock); \
1782 return (field_name); \
1788 ARPT_GET_COMMON(name);
1794 ARPT_GET_COMMON(city);
1800 ARPT_GET_COMMON(state);
1806 return (get_metar_taf_common(cdb, icao, B_TRUE));
1812 return (get_metar_taf_common(cdb, icao, B_FALSE));
1816download_metar_taf_common(chartdb_t *cdb,
const char *icao,
const char *source,
1817 const char *node_name)
1821 char error_reason[128];
1823 xmlXPathContext *xpath_ctx = NULL;
1824 xmlXPathObject *xpath_obj = NULL;
1829 if (strcmp(source,
"metars") == 0) {
1830 snprintf(url,
sizeof (url),
"https://aviationweather.gov/api/"
1831 "data/metar?format=xml&ids=%s&taf=false&hours=2", icao);
1832 }
else if (strcmp(source,
"tafs") == 0) {
1833 snprintf(url,
sizeof (url),
"https://aviationweather.gov/api/"
1834 "data/taf?format=xml&ids=%s&metar=false", icao);
1838 snprintf(error_reason,
sizeof (error_reason),
"Error downloading %s",
1840 snprintf(query,
sizeof (query),
"/response/data/%s/raw_text",
1843 if (cdb->prov_login != NULL) {
1850 login.
cainfo = cdb->prov_login->cainfo;
1853 if (!chart_download(cdb, url, NULL, &login, error_reason, &info))
1855 doc = xmlParseMemory((
char *)info.buf, info.bufsz);
1857 logMsg(
"Error parsing %s: XML parsing error", node_name);
1860 xpath_ctx = xmlXPathNewContext(doc);
1861 if (xpath_ctx == NULL) {
1862 logMsg(
"Error creating XPath context for XML");
1865 xpath_obj = xmlXPathEvalExpression((xmlChar *)query, xpath_ctx);
1866 if (xpath_obj->nodesetval->nodeNr == 0 ||
1867 xpath_obj->nodesetval->nodeTab[0]->children == NULL ||
1868 xpath_obj->nodesetval->nodeTab[0]->children->content == NULL) {
1869 char *path =
mkpathname(cdb->path,
"metar.xml", NULL);
1871 logMsg(
"Error parsing %s, valid but incorrect XML structure. "
1872 "For debugging purposes, I'm going to dump the raw data "
1873 "into a file named %s.", node_name, path);
1874 FILE *fp = fopen(path,
"wb");
1876 fwrite(info.buf, 1, info.bufsz, fp);
1883 (
char *)xpath_obj->nodesetval->nodeTab[0]->children->content);
1884 xmlXPathFreeObject(xpath_obj);
1885 xmlXPathFreeContext(xpath_ctx);
1890 if (xpath_obj != NULL)
1891 xmlXPathFreeObject(xpath_obj);
1892 if (xpath_ctx != NULL)
1893 xmlXPathFreeContext(xpath_ctx);
1902download_metar(chartdb_t *cdb,
const char *icao)
1904 return (download_metar_taf_common(cdb, icao,
"metars",
"METAR"));
1908download_taf(chartdb_t *cdb,
const char *icao)
1910 return (download_metar_taf_common(cdb, icao,
"tafs",
"TAF"));
1917 if (prov[cdb->prov].pending_ext_account_setup != NULL)
1918 return (prov[cdb->prov].pending_ext_account_setup(cdb));
#define ASSERT3U(x, op, y)
void * avl_destroy_nodes(avl_tree_t *tree, void **cookie)
void * avl_first(const avl_tree_t *tree)
void avl_insert(avl_tree_t *tree, void *node, avl_index_t where)
void avl_create(avl_tree_t *tree, int(*compar)(const void *, const void *), size_t size, size_t offset)
#define AVL_NEXT(tree, node)
void avl_destroy(avl_tree_t *tree)
void * avl_find(const avl_tree_t *tree, const void *node, avl_index_t *where)
#define CAIRO_SURFACE_DESTROY(surf)
char * chartdb_get_chart_codename(chartdb_t *cdb, const char *icao, const char *chart_name)
bool_t chartdb_pending_ext_account_setup(chartdb_t *cdb)
chart_georef_t chartdb_get_chart_georef(chartdb_t *cdb, const char *icao, const char *chart_name)
bool_t chartdb_test_connection2(const char *provider_name, const chart_prov_info_login_t *creds, const char *proxy)
char * chartdb_get_taf(chartdb_t *cdb, const char *icao)
char * chartdb_get_arpt_city(chartdb_t *cdb, const char *icao)
bool_t chartdb_is_ready(chartdb_t *cdb)
char * chartdb_get_metar(chartdb_t *cdb, const char *icao)
chartdb_t * chartdb_init(const char *cache_path, const char *pdftoppm_path, const char *pdfinfo_path, unsigned airac, const char *provider_name, const chart_prov_info_login_t *provider_login)
void chartdb_fini(chartdb_t *cdb)
chart_bbox_t chartdb_get_chart_view(chartdb_t *cdb, const char *icao, const char *chart_name, chart_view_t view)
chart_type_t chartdb_get_chart_type(chartdb_t *cdb, const char *icao, const char *chart_name)
size_t chartdb_get_proxy(chartdb_t *cdb, char *proxy, size_t cap)
char * chartdb_get_arpt_name(chartdb_t *cdb, const char *icao)
void chartdb_free_str_list(char **name_list, size_t num)
bool_t chartdb_test_connection(const char *provider_name, const chart_prov_info_login_t *creds)
char * chartdb_get_arpt_state(chartdb_t *cdb, const char *icao)
char ** chartdb_get_chart_names(chartdb_t *cdb, const char *icao, chart_type_t type, size_t *num_charts)
chart_procs_t chartdb_get_chart_procs(chartdb_t *cdb, const char *icao, const char *chart_name)
void chartdb_set_proxy(chartdb_t *cdb, const char *proxy)
void chartdb_set_load_limit(chartdb_t *cdb, uint64_t bytes)
bool_t chartdb_is_arpt_known(chartdb_t *cdb, const char *icao)
void chartdb_purge(chartdb_t *cdb)
bool_t chartdb_get_chart_surface(chartdb_t *cdb, const char *icao, const char *chart_name, int page, double zoom, bool_t night, cairo_surface_t **surf, int *num_pages)
#define ARRAY_NUM_ELEM(_array)
#define LACF_DESTROY(ptr)
char * lacf_dirname(const char *filename)
char ** strsplit(const char *input, const char *sep, bool_t skip_empty, size_t *num)
char * mkpathname(const char *comp,...)
bool_t remove_directory(const char *dirname)
void lacf_qsort_r(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *, void *), void *arg)
void lacf_strlcpy(char *dest, const char *src, size_t cap)
void free_strlist(char **comps, size_t num)
bool_t file_exists(const char *path, bool_t *isdir)
int list_link_active(const list_node_t *)
void * list_tail(const list_t *)
void list_destroy(list_t *)
void * list_head(const list_t *)
void list_create(list_t *, size_t, size_t)
void list_insert_head(list_t *, void *)
void * list_next(const list_t *, const void *)
size_t list_count(const list_t *)
void list_remove(list_t *, void *)
void * list_remove_head(list_t *)
void list_insert_tail(list_t *, void *)
static double clamp(double x, double min_val, double max_val)
static void strip_space(char *line)
static void * safe_realloc(void *oldptr, size_t size)
static char * safe_strdup(const char *str2)
static void * safe_calloc(size_t nmemb, size_t size)
#define ZERO_FREE_N(ptr, num)
static void mutex_destroy(mutex_t *mtx)
static void mutex_enter(mutex_t *mtx)
static void mutex_exit(mutex_t *mtx)
static void mutex_init(mutex_t *mtx)