summaryrefslogtreecommitdiff
path: root/opentracker.c
diff options
context:
space:
mode:
Diffstat (limited to 'opentracker.c')
-rw-r--r--opentracker.c242
1 files changed, 185 insertions, 57 deletions
diff --git a/opentracker.c b/opentracker.c
index 5b197d1..5f21ede 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -48,7 +48,22 @@ int g_self_pipe[2];
48 48
49static char *g_serverdir; 49static char *g_serverdir;
50static char *g_serveruser; 50static char *g_serveruser;
51static unsigned int g_udp_workers; 51
52typedef struct {
53 ot_ip6 ip;
54 uint16_t port;
55 PROTO_FLAG proto;
56 int shared;
57 int workers;
58 int64_t sock;
59} ot_listen;
60#define OT_LISTEN_INHERIT (-1)
61
62/* Config entries collected from command line and config files */
63static ot_listen *g_listen;
64static size_t g_listen_count;
65static int g_listen_shared;
66static int g_listen_workers;
52 67
53static void panic(const char *routine) __attribute__((noreturn)); 68static void panic(const char *routine) __attribute__((noreturn));
54static void panic(const char *routine) { 69static void panic(const char *routine) {
@@ -338,44 +353,156 @@ static void *server_mainloop(void *args) {
338 return 0; 353 return 0;
339} 354}
340 355
341static int64_t ot_try_bind(ot_ip6 ip, uint16_t port, PROTO_FLAG proto) { 356static void listen_append(const ot_ip6 ip, uint16_t port, PROTO_FLAG proto, int shared, int workers) {
342 int64_t sock; 357 ot_listen *grown = realloc(g_listen, (g_listen_count + 1) * sizeof(ot_listen));
358 ot_listen *entry;
359
360 if (!grown)
361 panic("listen_append");
362 g_listen = grown;
363
364 entry = g_listen + g_listen_count++;
365 memcpy(entry->ip, ip, sizeof(ot_ip6));
366 entry->port = port;
367 entry->proto = proto;
368 entry->shared = shared;
369 entry->workers = workers;
370 entry->sock = -1;
371}
372
373typedef enum { LISTEN_OPTION_UNKNOWN, LISTEN_OPTION_SHARED, LISTEN_OPTION_WORKERS } listen_option_kind;
374
375typedef struct {
376 const char *name;
377 size_t len;
378 listen_option_kind kind;
379 int takes_value;
380} listen_option_key;
381
382#define LISTEN_OPTION_KEY(s, k, v) {s, sizeof(s) - 1, k, v}
383
384static const listen_option_key listen_option_keys[] = {
385 LISTEN_OPTION_KEY("shared", LISTEN_OPTION_SHARED, 0),
386 LISTEN_OPTION_KEY("workers", LISTEN_OPTION_WORKERS, 1),
387};
388static const size_t listen_option_count = sizeof(listen_option_keys) / sizeof(listen_option_keys[0]);
389
390static listen_option_kind parse_listen_option(char **p) {
391 size_t i;
392
393 for (i = 0; i < listen_option_count; ++i) {
394 const listen_option_key *key = &listen_option_keys[i];
395 char delimiter = (*p)[key->len];
396
397 if (strncmp(*p, key->name, key->len))
398 continue;
399 if (key->takes_value ? delimiter != '=' : (delimiter && !isspace((unsigned char)delimiter)))
400 continue;
401
402 *p += key->len + (key->takes_value ? 1 : 0);
403 return key->kind;
404 }
405 return LISTEN_OPTION_UNKNOWN;
406}
407
408/* Options trailing an address in a listen statement, in any order */
409static int listen_scan_options(char *p, int *shared, int *workers) {
410 uint64_t value;
411
412 for (;;) {
413 while (isspace((unsigned char)*p))
414 ++p;
415 if (!*p)
416 return 1;
417
418 switch (parse_listen_option(&p)) {
419 case LISTEN_OPTION_SHARED:
420 *shared = 1;
421 break;
422 case LISTEN_OPTION_WORKERS: {
423 size_t off = scan_udec(p, &value, UINT_MAX);
424
425 if (!off)
426 return 0;
427 *workers = (int)value;
428 p += off;
429 break;
430 }
431 default:
432 return 0;
433 }
434 }
435}
436
437static int listen_option(int value, int fallback) { return value == OT_LISTEN_INHERIT ? fallback : value; }
438
439static void listen_bind_one(ot_listen *entry) {
440 socket_sharing sharing = listen_option(entry->shared, g_listen_shared) ? SOCKET_SHARED : SOCKET_EXCLUSIVE;
343 441
344#ifdef _DEBUG 442#ifdef _DEBUG
345 { 443 {
346 char *protos[] = {"TCP", "UDP", "UDP mcast"}; 444 char *protos[] = {"TCP", "UDP", "UDP mcast"};
347 char _debug[512]; 445 char _debug[512];
348 int off = snprintf(_debug, sizeof(_debug), "Binding socket type %s to address [", protos[proto]); 446 int off = snprintf(_debug, sizeof(_debug), "Binding socket type %s to address [", protos[entry->proto]);
349 off += ip_format(_debug + off, ip); 447 off += ip_format(_debug + off, entry->ip);
350 snprintf(_debug + off, sizeof(_debug) - off, "]:%d...", port); 448 snprintf(_debug + off, sizeof(_debug) - off, "]:%d...", entry->port);
351 fputs(_debug, stderr); 449 fputs(_debug, stderr);
352 } 450 }
353#endif 451#endif
354 452
355 if (proto == FLAG_TCP) { 453#ifdef WANT_SYNC_LIVE
356 if ((sock = socket_tcp_listen(ip, port)) == -1) 454 if (entry->proto == FLAG_MCA) {
455 livesync_bind_mcast(entry->ip, entry->port);
456#ifdef _DEBUG
457 fputs(" success.\n", stderr);
458#endif
459 return;
460 }
461#endif
462
463 if (entry->proto == FLAG_TCP) {
464 if ((entry->sock = socket_tcp_listen(entry->ip, entry->port, sharing)) == -1)
357 panic("socket_tcp_listen"); 465 panic("socket_tcp_listen");
358 } else { 466 } else {
359 if ((sock = socket_udp_bind(ip, port)) == -1) 467 if ((entry->sock = socket_udp_bind(entry->ip, entry->port, sharing)) == -1)
360 panic("socket_udp_bind"); 468 panic("socket_udp_bind");
361 } 469 }
362 470
363 if (!io_fd(sock)) 471 if (!io_fd(entry->sock))
364 panic("io_fd"); 472 panic("io_fd");
365 473
366 io_setcookie(sock, (void *)proto); 474 io_setcookie(entry->sock, (void *)entry->proto);
367 475 io_wantread(entry->sock);
368 if ((proto == FLAG_UDP) && g_udp_workers) {
369 io_block(sock);
370 udp_init(sock, g_udp_workers);
371 } else
372 io_wantread(sock);
373 476
374#ifdef _DEBUG 477#ifdef _DEBUG
375 fputs(" success.\n", stderr); 478 fputs(" success.\n", stderr);
376#endif 479#endif
480}
481
482/* Bound in the order the entries were given.
483 Note: On macOS SO_REUSEPORT delivers everything to the socket that bound last. */
484static void listen_bind_all(void) {
485 size_t i;
486
487 for (i = 0; i < g_listen_count; ++i)
488 listen_bind_one(g_listen + i);
489}
490
491static void listen_start_udp_workers(void) {
492 size_t i;
377 493
378 return sock; 494 udp_init();
495
496 for (i = 0; i < g_listen_count; ++i) {
497 ot_listen *entry = g_listen + i;
498 int workers = listen_option(entry->workers, g_listen_workers);
499
500 if (entry->proto != FLAG_UDP || workers <= 0)
501 continue;
502
503 io_block(entry->sock);
504 udp_workers_install(entry->sock, (unsigned int)workers);
505 }
379} 506}
380 507
381char *set_config_option(char **option, char *value) { 508char *set_config_option(char **option, char *value) {
@@ -452,7 +579,7 @@ typedef enum {
452 CONFIG_LISTEN_TCP, 579 CONFIG_LISTEN_TCP,
453 CONFIG_LISTEN_UDP, 580 CONFIG_LISTEN_UDP,
454 CONFIG_LISTEN_UDP_WORKERS, 581 CONFIG_LISTEN_UDP_WORKERS,
455 CONFIG_LISTEN_REUSEPORT, 582 CONFIG_LISTEN_SHARED,
456 CONFIG_ACCESS_WHITELIST, 583 CONFIG_ACCESS_WHITELIST,
457 CONFIG_ACCESS_BLACKLIST, 584 CONFIG_ACCESS_BLACKLIST,
458 CONFIG_ACCESS_FIFO_ADD, 585 CONFIG_ACCESS_FIFO_ADD,
@@ -480,7 +607,7 @@ static const config_key config_keys[] = {
480 CONFIG_KEY("listen.tcp", CONFIG_LISTEN_TCP), 607 CONFIG_KEY("listen.tcp", CONFIG_LISTEN_TCP),
481 CONFIG_KEY("listen.udp", CONFIG_LISTEN_UDP), 608 CONFIG_KEY("listen.udp", CONFIG_LISTEN_UDP),
482 CONFIG_KEY("listen.udp.workers", CONFIG_LISTEN_UDP_WORKERS), 609 CONFIG_KEY("listen.udp.workers", CONFIG_LISTEN_UDP_WORKERS),
483 CONFIG_KEY("listen.reuseport", CONFIG_LISTEN_REUSEPORT), 610 CONFIG_KEY("listen.shared", CONFIG_LISTEN_SHARED),
484#ifdef WANT_ACCESSLIST_WHITE 611#ifdef WANT_ACCESSLIST_WHITE
485 CONFIG_KEY("access.whitelist", CONFIG_ACCESS_WHITELIST), 612 CONFIG_KEY("access.whitelist", CONFIG_ACCESS_WHITELIST),
486#elif defined(WANT_ACCESSLIST_BLACK) 613#elif defined(WANT_ACCESSLIST_BLACK)
@@ -537,9 +664,10 @@ int parse_configfile(char *config_filename) {
537 } 664 }
538 665
539 while (fgets(inbuf, sizeof(inbuf), config_file_handle)) { 666 while (fgets(inbuf, sizeof(inbuf), config_file_handle)) {
540 char *p = inbuf, *end; 667 char *p = inbuf, *end;
541 uint64_t value; 668 uint64_t value;
542 uint16_t tmpport = 6969; 669 uint16_t tmpport = 6969;
670 config_kind kind;
543 671
544 /* Skip white spaces */ 672 /* Skip white spaces */
545 while (isspace((unsigned char)*p)) 673 while (isspace((unsigned char)*p))
@@ -555,7 +683,8 @@ int parse_configfile(char *config_filename) {
555 *--end = 0; 683 *--end = 0;
556 684
557 /* Scan for commands */ 685 /* Scan for commands */
558 switch (parse_config_key(&p)) { 686 kind = parse_config_key(&p);
687 switch (kind) {
559 case CONFIG_TRACKER_ROOTDIR: 688 case CONFIG_TRACKER_ROOTDIR:
560 set_config_option(&g_serverdir, p); 689 set_config_option(&g_serverdir, p);
561 break; 690 break;
@@ -563,35 +692,29 @@ int parse_configfile(char *config_filename) {
563 set_config_option(&g_serveruser, p); 692 set_config_option(&g_serveruser, p);
564 break; 693 break;
565 case CONFIG_LISTEN_TCP_UDP: 694 case CONFIG_LISTEN_TCP_UDP:
566 if (!scan_ip6_port(p, tmpip, &tmpport))
567 goto parse_error;
568 ot_try_bind(tmpip, tmpport, FLAG_TCP);
569 ++bound;
570 ot_try_bind(tmpip, tmpport, FLAG_UDP);
571 ++bound;
572 break;
573 case CONFIG_LISTEN_TCP: 695 case CONFIG_LISTEN_TCP:
574 if (!scan_ip6_port(p, tmpip, &tmpport)) 696 case CONFIG_LISTEN_UDP: {
575 goto parse_error; 697 int shared = OT_LISTEN_INHERIT
576 ot_try_bind(tmpip, tmpport, FLAG_TCP); 698 int workers = OT_LISTEN_INHERIT;
577 ++bound; 699 int off = scan_ip6_port(p, tmpip, &tmpport);
578 break; 700
579 case CONFIG_LISTEN_UDP: 701 if (!off || !listen_scan_options(p + off, &shared, &workers))
580 if (!scan_ip6_port(p, tmpip, &tmpport))
581 goto parse_error; 702 goto parse_error;
582 ot_try_bind(tmpip, tmpport, FLAG_UDP); 703 if (kind != CONFIG_LISTEN_UDP)
583 ++bound; 704 listen_append(tmpip, tmpport, FLAG_TCP, shared, OT_LISTEN_INHERIT), ++bound;
705 if (kind != CONFIG_LISTEN_TCP)
706 listen_append(tmpip, tmpport, FLAG_UDP, shared, workers), ++bound;
584 break; 707 break;
708 }
585 case CONFIG_LISTEN_UDP_WORKERS: 709 case CONFIG_LISTEN_UDP_WORKERS:
586 if (scan_udec(p, &value, UINT_MAX)) 710 if (!scan_udec(p, &value, UINT_MAX))
587 g_udp_workers = (unsigned int)value; 711 goto parse_error;
712 g_listen_workers = (int)value;
588 break; 713 break;
589 case CONFIG_LISTEN_REUSEPORT: 714 case CONFIG_LISTEN_SHARED:
590 /* Like listen.udp.workers this is read when a socket is bound, so it
591 has to come before the listen lines it should apply to. */
592 if (!scan_udec(p, &value, 1)) 715 if (!scan_udec(p, &value, 1))
593 goto parse_error; 716 goto parse_error;
594 g_socket_reuseport = (int)value; 717 g_listen_shared = (int)value;
595 break; 718 break;
596#ifdef WANT_ACCESSLIST_WHITE 719#ifdef WANT_ACCESSLIST_WHITE
597 case CONFIG_ACCESS_WHITELIST: 720 case CONFIG_ACCESS_WHITELIST:
@@ -638,9 +761,14 @@ int parse_configfile(char *config_filename) {
638 break; 761 break;
639 case CONFIG_LIVESYNC_CLUSTER_LISTEN: 762 case CONFIG_LIVESYNC_CLUSTER_LISTEN:
640 tmpport = LIVESYNC_PORT; 763 tmpport = LIVESYNC_PORT;
641 if (!scan_ip6_port(p, tmpip, &tmpport)) 764 {
642 goto parse_error; 765 int shared = OT_LISTEN_INHERIT, dummy = OT_LISTEN_INHERIT;
643 livesync_bind_mcast(tmpip, tmpport); 766 int off = scan_ip6_port(p, tmpip, &tmpport);
767
768 if (!off || !listen_scan_options(p + off, &shared, &dummy))
769 goto parse_error;
770 listen_append(tmpip, tmpport, FLAG_MCA, shared, OT_LISTEN_INHERIT);
771 }
644 break; 772 break;
645#endif 773#endif
646 default: 774 default:
@@ -791,7 +919,7 @@ int main(int argc, char **argv) {
791 usage(argv[0]); 919 usage(argv[0]);
792 exit(1); 920 exit(1);
793 } 921 }
794 ot_try_bind(serverip, (uint16_t)value, FLAG_TCP); 922 listen_append(serverip, (uint16_t)value, FLAG_TCP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT);
795 bound++; 923 bound++;
796 break; 924 break;
797 case 'P': 925 case 'P':
@@ -799,7 +927,7 @@ int main(int argc, char **argv) {
799 usage(argv[0]); 927 usage(argv[0]);
800 exit(1); 928 exit(1);
801 } 929 }
802 ot_try_bind(serverip, (uint16_t)value, FLAG_UDP); 930 listen_append(serverip, (uint16_t)value, FLAG_UDP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT);
803 bound++; 931 bound++;
804 break; 932 break;
805#ifdef WANT_SYNC_LIVE 933#ifdef WANT_SYNC_LIVE
@@ -808,7 +936,7 @@ int main(int argc, char **argv) {
808 usage(argv[0]); 936 usage(argv[0]);
809 exit(1); 937 exit(1);
810 } 938 }
811 livesync_bind_mcast(serverip, (uint16_t)value); 939 listen_append(serverip, (uint16_t)value, FLAG_MCA, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT);
812 break; 940 break;
813#endif 941#endif
814 case 'd': 942 case 'd':
@@ -849,12 +977,13 @@ int main(int argc, char **argv) {
849 } 977 }
850 } 978 }
851 979
852 /* Bind to our default tcp/udp ports */
853 if (!bound) { 980 if (!bound) {
854 ot_try_bind(serverip, 6969, FLAG_TCP); 981 listen_append(serverip, 6969, FLAG_TCP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT);
855 ot_try_bind(serverip, 6969, FLAG_UDP); 982 listen_append(serverip, 6969, FLAG_UDP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT);
856 } 983 }
857 984
985 listen_bind_all();
986
858 defaul_signal_handlers(); 987 defaul_signal_handlers();
859 988
860#ifdef WANT_SYSLOGS 989#ifdef WANT_SYSLOGS
@@ -893,8 +1022,7 @@ int main(int argc, char **argv) {
893 1022
894 install_signal_handlers(); 1023 install_signal_handlers();
895 1024
896 if (!g_udp_workers) 1025 listen_start_udp_workers();
897 udp_init(-1, 0);
898 1026
899 server_mainloop(0); 1027 server_mainloop(0);
900 1028