libacfutils
A general purpose library of utility functions designed to make it easier to develop addons for the X-Plane flight simulator.
Loading...
Searching...
No Matches
odb.c
1/*
2 * CDDL HEADER START
3 *
4 * This file and its contents are supplied under the terms of the
5 * Common Development and Distribution License ("CDDL"), version 1.0.
6 * You may only use this file in accordance with the terms of version
7 * 1.0 of the CDDL.
8 *
9 * A full copy of the text of the CDDL should have accompanied this
10 * source. A copy of the CDDL is also available via the Internet at
11 * http://www.illumos.org/license/CDDL.
12 *
13 * CDDL HEADER END
14 */
15/*
16 * Copyright 2018 Saso Kiselkov. All rights reserved.
17 */
18
19#include <stddef.h>
20#include <string.h>
21#include <errno.h>
22
23#include <curl/curl.h>
24
25#include "acfutils/assert.h"
26#include "acfutils/avl.h"
27#include "acfutils/compress.h"
28#include "acfutils/list.h"
29#include "acfutils/odb.h"
30#include "acfutils/perf.h"
31#include "acfutils/safe_alloc.h"
32#include "acfutils/thread.h"
33#include "acfutils/time.h"
34
35#include "chart_prov_common.h"
36
37#define DEFAULT_UNLOAD_DELAY 60 /* seconds */
38
39#define DL_TIMEOUT 300L /* seconds */
40#define LOW_SPD_LIM 4096L /* bytes/s */
41#define LOW_SPD_TIME 30L /* seconds */
42
43#define REALLOC_STEP (8 << 20) /* 1 MiB */
44
45#define FAA_DOF_URL "https://aeronav.faa.gov/Obst_Data/DAILY_DOF_CSV.ZIP"
46
47enum {
48 ODB_REGION_US, /* USA */
49 NUM_ODB_REGIONS
50};
51
52typedef struct {
53 odb_t *odb;
54 uint8_t *buf;
55 size_t bufsz;
56 size_t bufcap;
57} dl_info_t;
58
59typedef struct {
60 obst_type_t type;
61 geo_pos3_t pos;
62 float agl;
63 obst_light_t light;
64 unsigned quant;
65
66 list_node_t node;
67} obst_t;
68
69typedef struct {
70 odb_t *odb;
71 int lat, lon;
72 time_t access_t;
73 list_t obst;
74 avl_node_t node;
76
77struct odb_s {
78 char *cache_dir;
79 char *cainfo;
80 unsigned unload_delay;
81
82 mutex_t tiles_lock;
83 avl_tree_t tiles;
84
85 mutex_t refresh_lock;
86 thread_t refresh_thr;
87 bool_t refresh_run;
88
89 time_t refresh_times[NUM_ODB_REGIONS];
90
91 mutex_t proxy_lock;
92 char *proxy;
93};
94
95static void add_obst_to_odb(obst_type_t type, geo_pos3_t pos, float agl,
96 obst_light_t light, unsigned quant, void *userinfo);
97static odb_tile_t *load_tile(odb_t *odb, int lat, int lon,
98 bool_t load_from_db);
99static void odb_flush_tiles(odb_t *odb);
100
101static int
102tile_compar(const void *a, const void *b)
103{
104 const odb_tile_t *ta = a, *tb = b;
105
106 if (ta->lat < tb->lat)
107 return (-1);
108 if (ta->lat > tb->lat)
109 return (1);
110 if (ta->lon < tb->lon)
111 return (-1);
112 if (ta->lon > tb->lon)
113 return (1);
114
115 return (0);
116}
117
118static void
119latlon2path(int lat, int lon, char dname[32])
120{
121 ASSERT(dname != NULL);
122 snprintf(dname, 32, "%+03d%+04d%c%+03d%+04d",
123 (int)floor(lat / 10.0) * 10, (int)floor(lon / 10.0) * 10,
124 DIRSEP, lat, lon);
125}
126
127static inline obst_type_t
128dof2type(const char *type)
129{
130 if (strcmp(type, "BLDG") == 0)
131 return (OBST_BLDG);
132 if (strcmp(type, "TOWER") == 0 || strstr(type, "TWR") != NULL)
133 return (OBST_TOWER);
134 if (strcmp(type, "STACK") == 0)
135 return (OBST_STACK);
136 if (strcmp(type, "RIG") == 0)
137 return (OBST_RIG);
138 if (strstr(type, "POLE") != NULL)
139 return (OBST_POLE);
140 return (OBST_OTHER);
141}
142
143static inline obst_light_t
144dof2light(const char *light)
145{
146 switch (light[0]) {
147 case 'R':
148 return (OBST_LIGHT_RED);
149 case 'D':
150 return (OBST_LIGHT_STROBE_WR_MED);
151 case 'H':
152 return (OBST_LIGHT_STROBE_WR_HI);
153 case 'M':
154 return (OBST_LIGHT_STROBE_W_MED);
155 case 'S':
156 return (OBST_LIGHT_STROBE_W_HI);
157 case 'F':
158 return (OBST_LIGHT_FLOOD);
159 case 'C':
160 return (OBST_LIGHT_DUAL_MED_CAT);
161 case 'W':
162 return (OBST_LIGHT_SYNC_RED);
163 case 'L':
164 return (OBST_LIGHT_LIGHTED);
165 case 'N':
166 return (OBST_LIGHT_NONE);
167 default:
168 return (OBST_LIGHT_UNK);
169 }
170}
171
172static inline const char *
173type2dof(obst_type_t type)
174{
175 switch (type) {
176 case OBST_BLDG:
177 return ("BLDG");
178 case OBST_TOWER:
179 return ("TOWER");
180 case OBST_STACK:
181 return ("STACK");
182 case OBST_RIG:
183 return ("RIG");
184 case OBST_POLE:
185 return ("POLE");
186 default:
187 return ("OTHER");
188 }
189}
190
191static inline char
192light2dof(obst_light_t light)
193{
194 switch (light) {
195 case OBST_LIGHT_RED:
196 return ('R');
197 case OBST_LIGHT_STROBE_WR_MED:
198 return ('D');
199 case OBST_LIGHT_STROBE_WR_HI:
200 return ('H');
201 case OBST_LIGHT_STROBE_W_MED:
202 return ('M');
203 case OBST_LIGHT_STROBE_W_HI:
204 return ('S');
205 case OBST_LIGHT_FLOOD:
206 return ('F');
207 case OBST_LIGHT_DUAL_MED_CAT:
208 return ('C');
209 case OBST_LIGHT_SYNC_RED:
210 return ('W');
211 case OBST_LIGHT_LIGHTED:
212 return ('L');
213 case OBST_LIGHT_NONE:
214 return ('N');
215 default:
216 return ('U');
217 }
218}
219
220static bool_t
221odb_proc_us_dof_impl(const char *buf, size_t len, add_obst_cb_t cb,
222 void *userinfo)
223{
224 ASSERT(buf != NULL);
225 ASSERT(cb != NULL);
226
227 for (const char *line_start = buf, *line_end = buf;
228 line_start < buf + len; line_start = line_end + 1) {
229 char **comps;
230 size_t n_comps;
231 obst_type_t type;
232 obst_light_t light;
233 geo_pos3_t pos;
234 float agl, amsl;
235 unsigned quant;
236 char line[256];
237
238 line_end = strchr(line_start, '\n');
239 if (line_end == NULL)
240 line_end = buf + len;
241 lacf_strlcpy(line, line_start, MIN((int)sizeof (line),
242 (line_end - line_start) + 1));
243
244 strip_space(line);
245
246 comps = strsplit(line, ",", B_FALSE, &n_comps);
247
248 if (n_comps < 19 || strcmp(comps[0], "OAS") == 0)
249 goto next;
250
251 quant = atoi(comps[10]);
252 agl = FEET2MET(atof(comps[11]));
253 amsl = FEET2MET(atof(comps[12]));
254 type = dof2type(comps[9]);
255 light = dof2light(comps[13]);
256 pos.lat = atof(comps[5]);
257 pos.lon = atof(comps[6]);
258 pos.elev = amsl - agl;
259
260 if (!is_valid_lat(pos.lat) || !is_valid_lon(pos.lon) ||
261 agl < 0 || !is_valid_alt_m(agl) || !is_valid_alt_m(amsl) ||
262 quant == 0)
263 goto next;
264
265 cb(type, pos, agl, light, quant, userinfo);
266next:
267 free_strlist(comps, n_comps);
268 }
269
270 return (B_TRUE);
271}
272
273static bool_t
274odb_proc_us_dof(const char *path, add_obst_cb_t cb, void *userinfo)
275{
276 char *str;
277 bool_t res;
278
279 ASSERT(path != NULL);
280 str = file2str(path, NULL);
281
282 if (str == NULL)
283 return (B_FALSE);
284 res = odb_proc_us_dof_impl(str, strlen(str), cb, userinfo);
285 LACF_DESTROY(str);
286
287 return (res);
288}
289
290static void
291free_tile(odb_tile_t *tile)
292{
293 obst_t *obst;
294
295 ASSERT(tile != NULL);
296
297 while ((obst = list_remove_head(&tile->obst)) != NULL)
298 free(obst);
299 list_destroy(&tile->obst);
300 memset(tile, 0, sizeof (*tile));
301 free(tile);
302}
303
304odb_t *
305odb_init(const char *xpdir, const char *cainfo)
306{
307 odb_t *odb = safe_calloc(1, sizeof (*odb));
308
309 ASSERT(xpdir != NULL);
310
311 odb->cache_dir = mkpathname(xpdir, "Output", "caches",
312 "obstacle.db", NULL);
313 if (cainfo != NULL)
314 odb->cainfo = strdup(cainfo);
315 odb->unload_delay = DEFAULT_UNLOAD_DELAY;
316
317 mutex_init(&odb->tiles_lock);
318 avl_create(&odb->tiles, tile_compar, sizeof (odb_tile_t),
319 offsetof(odb_tile_t, node));
320
321 mutex_init(&odb->refresh_lock);
322
323 mutex_init(&odb->proxy_lock);
324
325 return (odb);
326}
327
328void
329odb_fini(odb_t *odb)
330{
331 if (odb == NULL)
332 return;
333
334 if (odb->refresh_run) {
335 odb->refresh_run = B_FALSE;
336 thread_join(&odb->refresh_thr);
337 }
338 mutex_destroy(&odb->refresh_lock);
339
340 mutex_enter(&odb->tiles_lock);
341 odb_flush_tiles(odb);
342 mutex_exit(&odb->tiles_lock);
343
344 avl_destroy(&odb->tiles);
345 mutex_destroy(&odb->tiles_lock);
346
347 free(odb->proxy);
348 mutex_destroy(&odb->proxy_lock);
349
350 free(odb->cainfo);
351 free(odb->cache_dir);
352 memset(odb, 0, sizeof (*odb));
353 free(odb);
354}
355
356void
357odb_set_unload_delay(odb_t *odb, unsigned seconds)
358{
359 ASSERT(odb != NULL);
360 odb->unload_delay = seconds;
361}
362
363time_t
364odb_get_cc_refresh_date_impl(odb_t* odb, const char *cc)
365{
366 char *str;
367 time_t res = 0;
368
369 ASSERT(odb != NULL);
370 ASSERT(cc != NULL);
371 ASSERT_MSG(strlen(cc) == 2 && strchr(cc, DIRSEP) == NULL,
372 "invalid country code \"%s\" passed", cc);
373
374 str = file2str(odb->cache_dir, cc, "refresh.txt", NULL);
375
376 if (str != NULL) {
377 res = atoll(str);
378 lacf_free(str);
379 }
380
381 return (res);
382}
383
384time_t
385odb_get_cc_refresh_date(odb_t *odb, const char *cc)
386{
387 ASSERT(odb != NULL);
388 ASSERT(cc != NULL);
389
390 if (strcmp(cc, "US") == 0) {
391 if (odb->refresh_times[ODB_REGION_US] == 0) {
392 odb->refresh_times[ODB_REGION_US] =
393 odb_get_cc_refresh_date_impl(odb, cc);
394 }
395 return odb->refresh_times[ODB_REGION_US];
396 } else {
397 return (0);
398 }
399}
400
401static size_t
402dl_write(char *ptr, size_t size, size_t nmemb, void *userdata)
403{
404 dl_info_t *dl_info;
405 size_t bytes = size * nmemb;
406
407 ASSERT(userdata != NULL);
408 dl_info = userdata;
409
410 /* Respond to an early termination request */
411 if (!dl_info->odb->refresh_run)
412 return (0);
413
414 if (dl_info->bufcap < dl_info->bufsz + bytes) {
415 do {
416 dl_info->bufcap += REALLOC_STEP;
417 } while (dl_info->bufcap < dl_info->bufsz + bytes);
418 dl_info->buf = realloc(dl_info->buf, dl_info->bufcap);
419 }
420 memcpy(&dl_info->buf[dl_info->bufsz], ptr, bytes);
421 dl_info->bufsz += bytes;
422
423 return (bytes);
424}
425
426static bool_t
427write_tile(odb_t *odb, odb_tile_t *tile, const char *cc)
428{
429 char subpath[32];
430 char *path, *dirpath, *p;
431 FILE *fp = NULL;
432 bool_t res = B_FALSE;
433
434 ASSERT(odb != NULL);
435 ASSERT(tile != NULL);
436 ASSERT(cc != NULL);
437
438 latlon2path(tile->lat, tile->lon, subpath);
439 path = mkpathname(odb->cache_dir, cc, subpath, NULL);
440 dirpath = strdup(path);
441 VERIFY(dirpath != NULL);
442
443 /* strip the last path component to create the directory */
444 p = strrchr(dirpath, DIRSEP);
445 ASSERT(p != NULL);
446 *p = 0;
447
448 if (!create_directory_recursive(dirpath))
449 goto errout;
450 fp = fopen(path, "wb");
451 if (fp == NULL) {
452 logMsg("Error writing obstacle database tile %s: %s",
453 path, strerror (errno));
454 goto errout;
455 }
456 for (obst_t *obst = list_head(&tile->obst); obst != NULL;
457 obst = list_next(&tile->obst, obst)) {
458 fprintf(fp, ",,US,,,%f,%f,,,%s,%d,%.0f,%.0f,%c,1A,,,,\n",
459 obst->pos.lat, obst->pos.lon, type2dof(obst->type),
460 obst->quant, MET2FEET(obst->agl),
461 MET2FEET(obst->pos.elev + obst->agl),
462 light2dof(obst->light));
463 }
464
465 res = B_TRUE;
466errout:
467 if (fp != NULL)
468 fclose(fp);
469 lacf_free(path);
470 free(dirpath);
471
472 return (res);
473}
474
475static void
476odb_write_tiles(odb_t *odb, const char *cc)
477{
478 ASSERT(odb != NULL);
479 ASSERT_MUTEX_HELD(&odb->tiles_lock);
480 ASSERT(cc != NULL);
481
482 for (odb_tile_t *tile = avl_first(&odb->tiles); tile != NULL;
483 tile = AVL_NEXT(&odb->tiles, tile)) {
484 if (!write_tile(odb, tile, cc))
485 break;
486 }
487}
488
489static void
490odb_flush_tiles(odb_t *odb)
491{
492 void *cookie = NULL;
493 odb_tile_t *tile;
494
495 ASSERT(odb != NULL);
496 ASSERT_MUTEX_HELD(&odb->tiles_lock);
497
498 while ((tile = avl_destroy_nodes(&odb->tiles, &cookie)) != NULL)
499 free_tile(tile);
500}
501
502static void
503write_odb_refresh_date(odb_t *odb, const char *cc)
504{
505 char *path;
506 FILE *fp;
507 time_t now = time(NULL);
508
509 ASSERT(odb != NULL);
510 ASSERT(odb->cache_dir != NULL);
511 ASSERT(cc != NULL);
512
513 path = mkpathname(odb->cache_dir, cc, "refresh.txt", NULL);
514 fp = fopen(path, "wb");
515 if (fp == NULL) {
516 logMsg("Error writing obstacle database refresh file %s: %s",
517 path, strerror(errno));
518 goto errout;
519 }
520 fprintf(fp, "%ld\n", (long)now);
521 fclose(fp);
522errout:
523 LACF_DESTROY(path);
524}
525
526static void
527odb_refresh_us_decompress(odb_t *odb, dl_info_t *dl_info)
528{
529 size_t len;
530 void *buf;
531
532 ASSERT(odb != NULL);
533 ASSERT(dl_info != NULL);
534
535 if (dl_info->bufsz == 0) {
536 logMsg("Error updating obstacle database from %s: "
537 "server didn't send any data", FAA_DOF_URL);
538 return;
539 }
540 buf = decompress_zip(dl_info->buf, dl_info->bufsz, &len);
541 if (buf != NULL) {
542 char *subpath = mkpathname(odb->cache_dir, "US", NULL);
543 /*
544 * The downloaded DOF is HUUUGE, so DON'T lock here.
545 * add_obst_to_odb will lock the database when it's
546 * ready to add a single obstacle instead.
547 */
548 odb_proc_us_dof_impl(buf, len, add_obst_to_odb, odb);
549
550 mutex_enter(&odb->tiles_lock);
551
552 if (file_exists(subpath, NULL))
553 remove_directory(subpath);
554 create_directory_recursive(odb->cache_dir);
555 odb_write_tiles(odb, "US");
556 odb_flush_tiles(odb);
557 write_odb_refresh_date(odb, "US");
558
559 lacf_free(subpath);
560 odb->refresh_times[ODB_REGION_US] = time(NULL);
561
562 mutex_exit(&odb->tiles_lock);
563 } else {
564 logMsg("Error updating obstacle database from %s: "
565 "failed to decompress downloaded ZIP file", FAA_DOF_URL);
566 odb->refresh_times[ODB_REGION_US] = -1u;
567 }
568 free(buf);
569}
570
571static void
572odb_refresh_us(odb_t *odb)
573{
574 CURL *curl;
575 CURLcode res;
576 dl_info_t dl_info = { .odb = odb };
577 time_t refresh_date;
578
579 thread_set_name("odb-refresh-us");
580
581 ASSERT(odb != NULL);
582
583 curl = curl_easy_init();
584 VERIFY(curl != NULL);
585
586 logMsg("Downloading new obstacle data from \"%s\" for region \"US\"",
587 FAA_DOF_URL);
588 curl_easy_setopt(curl, CURLOPT_URL, FAA_DOF_URL);
589 chart_setup_curl(curl, odb->cainfo);
590 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, dl_write);
591 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &dl_info);
592 curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, LOW_SPD_TIME);
593 curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, LOW_SPD_LIM);
594 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
595 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
596 curl_easy_setopt(curl, CURLOPT_TIMEOUT, DL_TIMEOUT);
597 mutex_enter(&odb->proxy_lock);
598 if (odb->proxy != NULL)
599 curl_easy_setopt(curl, CURLOPT_PROXY, odb->proxy);
600 mutex_exit(&odb->proxy_lock);
601
602 refresh_date = odb_get_cc_refresh_date(odb, "US");
603 if (refresh_date != 0) {
604 /* If-Modified-Since the above time stamp */
605 curl_easy_setopt(curl, CURLOPT_TIMEVALUE, (long)refresh_date);
606 curl_easy_setopt(curl, CURLOPT_TIMECONDITION,
607 (long)CURL_TIMECOND_IFMODSINCE);
608 }
609 res = curl_easy_perform(curl);
610
611 if (res == CURLE_OK) {
612 long code = 0;
613
614 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
615 if (code == 200) {
616 odb_refresh_us_decompress(odb, &dl_info);
617 } else if (code == 304) {
618 logMsg("Obstacle database %s unchanged", FAA_DOF_URL);
619 mutex_enter(&odb->tiles_lock);
620 odb->refresh_times[ODB_REGION_US] = time(NULL);
621 mutex_exit(&odb->tiles_lock);
622 } else {
623 logMsg("Error updating obstacle database from %s: "
624 "server responded with code %ld", FAA_DOF_URL,
625 code);
626 }
627 } else {
628 logMsg("Error updating obstacle database from %s: %s",
629 FAA_DOF_URL, curl_easy_strerror(res));
630 odb->refresh_times[ODB_REGION_US] = -1u;
631 }
632
633 curl_easy_cleanup(curl);
634 free(dl_info.buf);
635
636 mutex_enter(&odb->refresh_lock);
637 odb->refresh_run = B_FALSE;
638 mutex_exit(&odb->refresh_lock);
639}
640
641bool_t
642odb_refresh_cc(odb_t *odb, const char *cc)
643{
644 void (*refresh_op)(odb_t *odb) = NULL;
645
646 ASSERT(odb != NULL);
647 ASSERT(cc != NULL);
648
649 mutex_enter(&odb->refresh_lock);
650 if (!odb->refresh_run) {
651 if (strcmp(cc, "US") == 0)
652 refresh_op = odb_refresh_us;
653 if (refresh_op != NULL) {
654 odb->refresh_run = B_TRUE;
655 VERIFY(thread_create(&odb->refresh_thr,
656 (void (*)(void *))refresh_op, odb));
657 }
658 }
659 mutex_exit(&odb->refresh_lock);
660
661 return (B_TRUE);
662}
663
664static void
665add_tile_obst(obst_type_t type, geo_pos3_t pos, float agl,
666 obst_light_t light, unsigned quant, void *userinfo)
667{
668 odb_tile_t *tile;
669 obst_t *obst = safe_calloc(1, sizeof (*obst));
670
671 ASSERT(userinfo != NULL);
672 tile = userinfo;
673 ASSERT(tile->odb != NULL);
674 ASSERT_MUTEX_HELD(&tile->odb->tiles_lock);
675
676 obst->type = type;
677 obst->pos = pos;
678 obst->agl = agl;
679 obst->light = light;
680 obst->quant = quant;
681
682 list_insert_tail(&tile->obst, obst);
683}
684
685static void
686add_obst_to_odb(obst_type_t type, geo_pos3_t pos, float agl,
687 obst_light_t light, unsigned quant, void *userinfo)
688{
689 odb_t *odb;
690 odb_tile_t *tile;
691
692 ASSERT(userinfo != NULL);
693 odb = userinfo;
694
695 mutex_enter(&odb->tiles_lock);
696 tile = load_tile(odb, floor(pos.lat), floor(pos.lon), B_FALSE);
697 add_tile_obst(type, pos, agl, light, quant, tile);
698 mutex_exit(&odb->tiles_lock);
699}
700
701static void
702odb_populate_tile_us(odb_t *odb, odb_tile_t *tile)
703{
704 char tilepath[32];
705 char *path;
706
707 ASSERT(odb != NULL);
708 ASSERT(tile != NULL);
709
710 latlon2path(tile->lat, tile->lon, tilepath);
711 path = mkpathname(odb->cache_dir, "US", tilepath, NULL);
712 odb_proc_us_dof(path, add_tile_obst, tile);
713 lacf_free(path);
714}
715
716static void
717odb_populate_tile(odb_t *odb, odb_tile_t *tile)
718{
719 ASSERT(odb != NULL);
720 ASSERT(tile != NULL);
721 odb_populate_tile_us(odb, tile);
722}
723
724static odb_tile_t *
725load_tile(odb_t *odb, int lat, int lon, bool_t load_from_db)
726{
727 odb_tile_t *tile;
728 odb_tile_t srch = { .lat = lat, .lon = lon };
729 avl_index_t where;
730
731 ASSERT(odb != NULL);
732 ASSERT_MUTEX_HELD(&odb->tiles_lock);
733
734 tile = avl_find(&odb->tiles, &srch, &where);
735 if (tile == NULL) {
736 tile = safe_calloc(1, sizeof (*tile));
737 tile->odb = odb;
738 tile->lat = lat;
739 tile->lon = lon;
740 list_create(&tile->obst, sizeof (obst_t),
741 offsetof(obst_t, node));
742
743 if (load_from_db)
744 odb_populate_tile(odb, tile);
745 avl_insert(&odb->tiles, tile, where);
746 }
747 if (load_from_db)
748 tile->access_t = time(NULL);
749
750 return (tile);
751}
752
753bool_t
754odb_get_obstacles(odb_t *odb, int lat, int lon, add_obst_cb_t cb,
755 void *userinfo)
756{
757 odb_tile_t *tile;
758
759 ASSERT(odb != NULL);
760 ASSERT(cb != NULL);
761
762 mutex_enter(&odb->tiles_lock);
763
764 tile = load_tile(odb, lat, lon, B_TRUE);
765 for (obst_t *obst = list_head(&tile->obst); obst != NULL;
766 obst = list_next(&tile->obst, obst)) {
767 cb(obst->type, obst->pos, obst->agl, obst->light, obst->quant,
768 userinfo);
769 }
770
771 mutex_exit(&odb->tiles_lock);
772
773 return (B_TRUE);
774}
775
776void
777odb_set_proxy(odb_t *odb, const char *proxy)
778{
779 ASSERT(odb != NULL);
780
781 mutex_enter(&odb->proxy_lock);
782 LACF_DESTROY(odb->proxy);
783 if (proxy != NULL)
784 odb->proxy = safe_strdup(proxy);
785 mutex_exit(&odb->proxy_lock);
786}
787
788size_t
789odb_get_proxy(odb_t *odb, char *proxy, size_t cap)
790{
791 size_t len;
792
793 ASSERT(odb != NULL);
794 ASSERT(proxy != NULL || cap == 0);
795
796 mutex_enter(&odb->proxy_lock);
797 if (odb->proxy != NULL) {
798 lacf_strlcpy(proxy, odb->proxy, cap);
799 len = strlen(odb->proxy) + 1;
800 } else {
801 lacf_strlcpy(proxy, "", cap);
802 len = 0;
803 }
804 mutex_exit(&odb->proxy_lock);
805
806 return (len);
807}
#define ASSERT_MSG(x, fmt,...)
Definition assert.h:214
#define VERIFY(x)
Definition assert.h:78
#define ASSERT(x)
Definition assert.h:208
void * avl_destroy_nodes(avl_tree_t *tree, void **cookie)
Definition avl.c:938
void * avl_first(const avl_tree_t *tree)
Definition avl.c:172
uintptr_t avl_index_t
Definition avl.h:119
void avl_insert(avl_tree_t *tree, void *node, avl_index_t where)
Definition avl.c:471
void avl_create(avl_tree_t *tree, int(*compar)(const void *, const void *), size_t size, size_t offset)
Definition avl.c:867
#define AVL_NEXT(tree, node)
Definition avl.h:212
void avl_destroy(avl_tree_t *tree)
Definition avl.c:890
void * avl_find(const avl_tree_t *tree, const void *node, avl_index_t *where)
Definition avl.c:244
void lacf_free(void *buf)
Definition core.c:49
#define LACF_DESTROY(ptr)
Definition core.h:158
char ** strsplit(const char *input, const char *sep, bool_t skip_empty, size_t *num)
Definition helpers.c:650
char * mkpathname(const char *comp,...)
Definition helpers.c:833
char * file2str(const char *comp,...)
Definition helpers.c:1036
bool_t remove_directory(const char *dirname)
Definition helpers.c:1382
void lacf_strlcpy(char *dest, const char *src, size_t cap)
Definition helpers.c:1667
void free_strlist(char **comps, size_t num)
Definition helpers.c:703
bool_t create_directory_recursive(const char *dirname)
Definition helpers.c:1292
static bool_t is_valid_lat(double lat)
Definition helpers.h:93
static bool_t is_valid_alt_m(double alt_m)
Definition helpers.h:149
bool_t file_exists(const char *path, bool_t *isdir)
Definition helpers.c:1222
static bool_t is_valid_lon(double lon)
Definition helpers.h:111
void list_destroy(list_t *)
Definition list.c:136
void * list_head(const list_t *)
Definition list.c:292
void list_create(list_t *, size_t, size_t)
Definition list.c:113
void * list_next(const list_t *, const void *)
Definition list.c:344
void * list_remove_head(list_t *)
Definition list.c:251
void list_insert_tail(list_t *, void *)
Definition list.c:213
#define logMsg(...)
Definition log.h:112
static void strip_space(char *line)
static char * safe_strdup(const char *str2)
Definition safe_alloc.h:201
static void * safe_calloc(size_t nmemb, size_t size)
Definition safe_alloc.h:71
double lat
Definition geom.h:41
double elev
Definition geom.h:43
double lon
Definition geom.h:42
Definition odb.c:59
Definition odb.c:77
#define ASSERT_MUTEX_HELD(mtx)
Definition thread.h:1228
static void thread_set_name(const char *name)
Definition thread.h:852
static void mutex_destroy(mutex_t *mtx)
Definition thread.h:499
static void thread_join(thread_t *thrp)
Definition thread.h:836
static void mutex_enter(mutex_t *mtx)
Definition thread.h:530
static void mutex_exit(mutex_t *mtx)
Definition thread.h:556
HANDLE thread_t
Definition thread.h:393
static void mutex_init(mutex_t *mtx)
Definition thread.h:488
#define thread_create(thrp, start_proc, arg)
Definition thread.h:189