1 /*** 2 This file is part of avahi. 3 4 avahi is free software; you can redistribute it and/or modify it 5 under the terms of the GNU Lesser General Public License as 6 published by the Free Software Foundation; either version 2.1 of the 7 License, or (at your option) any later version. 8 9 avahi is distributed in the hope that it will be useful, but WITHOUT 10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General 12 Public License for more details. 13 14 You should have received a copy of the GNU Lesser General Public 15 License along with avahi; if not, write to the Free Software 16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 17 USA. 18 ***/ 19 20 #ifdef HAVE_CONFIG_H 21 #include <config.h> 22 #endif 23 24 #include <assert.h> 25 #include <getopt.h> 26 #include <string.h> 27 #include <signal.h> 28 #include <errno.h> 29 #include <string.h> 30 #include <unistd.h> 31 #include <grp.h> 32 #include <pwd.h> 33 #include <sys/stat.h> 34 #include <sys/ioctl.h> 35 #include <stdio.h> 36 #include <fcntl.h> 37 #include <time.h> 38 #include <stdlib.h> 39 #include <sys/time.h> 40 #include <sys/resource.h> 41 #include <sys/socket.h> 42 43 #ifdef HAVE_INOTIFY 44 #include <sys/inotify.h> 45 #endif 46 47 #ifdef HAVE_KQUEUE 48 #include <sys/types.h> 49 #include <sys/event.h> 50 #include <unistd.h> 51 #endif 52 53 #include <libdaemon/dfork.h> 54 #include <libdaemon/dsignal.h> 55 #include <libdaemon/dlog.h> 56 #include <libdaemon/dpid.h> 57 58 #include "avahi-common/avahi-malloc.h" 59 #include <avahi-common/simple-watch.h> 60 #include <avahi-common/error.h> 61 #include <avahi-common/alternative.h> 62 #include <avahi-common/domain.h> 63 64 #include <avahi-core/core.h> 65 #include <avahi-core/publish.h> 66 #include <avahi-core/dns-srv-rr.h> 67 #include <avahi-core/log.h> 68 #include <avahi-core/util.h> 69 70 #ifdef ENABLE_CHROOT 71 #include "chroot.h" 72 #include "caps.h" 73 #endif 74 75 #include "setproctitle.h" 76 #include "main.h" 77 #include "static-services.h" 78 #include "static-hosts.h" 79 #include "ini-file-parser.h" 80 #include "sd-daemon.h" 81 82 #ifdef AVAHI_SOCKET 83 #include "simple-protocol.h" 84 #endif 85 86 #ifdef HAVE_DBUS 87 #include "dbus-protocol.h" 88 #endif 89 90 AvahiServer *avahi_server = NULL; 91 AvahiSimplePoll *simple_poll_api = NULL; 92 static char *argv0 = NULL; 93 int nss_support = 0; 94 95 typedef enum { 96 DAEMON_RUN, 97 DAEMON_KILL, 98 DAEMON_VERSION, 99 DAEMON_HELP, 100 DAEMON_RELOAD, 101 DAEMON_CHECK 102 } DaemonCommand; 103 104 typedef struct { 105 AvahiServerConfig server_config; 106 DaemonCommand command; 107 int daemonize; 108 int use_syslog; 109 char *config_file; 110 #ifdef HAVE_DBUS 111 int enable_dbus; 112 int fail_on_missing_dbus; 113 unsigned n_clients_max; 114 unsigned n_objects_per_client_max; 115 unsigned n_entries_per_entry_group_max; 116 #endif 117 int drop_root; 118 int set_rlimits; 119 #ifdef ENABLE_CHROOT 120 int use_chroot; 121 #endif 122 int modify_proc_title; 123 124 int disable_user_service_publishing; 125 int publish_resolv_conf; 126 char ** publish_dns_servers; 127 int debug; 128 129 int rlimit_as_set, rlimit_core_set, rlimit_data_set, rlimit_fsize_set, rlimit_nofile_set, rlimit_stack_set; 130 rlim_t rlimit_as, rlimit_core, rlimit_data, rlimit_fsize, rlimit_nofile, rlimit_stack; 131 132 #ifdef RLIMIT_NPROC 133 int rlimit_nproc_set; 134 rlim_t rlimit_nproc; 135 #endif 136 } DaemonConfig; 137 138 #define RESOLV_CONF "/etc/resolv.conf" 139 #define BROWSE_DOMAINS_MAX 16 140 141 static AvahiSEntryGroup *dns_servers_entry_group = NULL; 142 static AvahiSEntryGroup *resolv_conf_entry_group = NULL; 143 144 static char **resolv_conf_name_servers = NULL; 145 static char **resolv_conf_search_domains = NULL; 146 147 static DaemonConfig config; 148 149 static int has_prefix(const char *s, const char *prefix) { 150 size_t l; 151 152 l = strlen(prefix); 153 154 return strlen(s) >= l && strncmp(s, prefix, l) == 0; 155 } 156 157 static int load_resolv_conf(void) { 158 int ret = -1; 159 FILE *f; 160 int i = 0, j = 0; 161 162 avahi_strfreev(resolv_conf_name_servers); 163 resolv_conf_name_servers = NULL; 164 165 avahi_strfreev(resolv_conf_search_domains); 166 resolv_conf_search_domains = NULL; 167 168 #ifdef ENABLE_CHROOT 169 f = avahi_chroot_helper_get_file(RESOLV_CONF); 170 #else 171 f = fopen(RESOLV_CONF, "r"); 172 #endif 173 174 if (!f) { 175 avahi_log_warn("Failed to open "RESOLV_CONF": %s", strerror(errno)); 176 goto finish; 177 } 178 179 resolv_conf_name_servers = avahi_new0(char*, AVAHI_WIDE_AREA_SERVERS_MAX+1); 180 resolv_conf_search_domains = avahi_new0(char*, BROWSE_DOMAINS_MAX+1); 181 182 while (!feof(f)) { 183 char ln[128]; 184 char *p; 185 186 if (!(fgets(ln, sizeof(ln), f))) 187 break; 188 189 ln[strcspn(ln, "\r\n#")] = 0; 190 p = ln + strspn(ln, "\t "); 191 192 if ((has_prefix(p, "nameserver ") || has_prefix(p, "nameserver\t")) && i < AVAHI_WIDE_AREA_SERVERS_MAX) { 193 p += 10; 194 p += strspn(p, "\t "); 195 p[strcspn(p, "\t ")] = 0; 196 resolv_conf_name_servers[i++] = avahi_strdup(p); 197 } 198 199 if ((has_prefix(p, "search ") || has_prefix(p, "search\t") || 200 has_prefix(p, "domain ") || has_prefix(p, "domain\t"))) { 201 202 p += 6; 203 204 while (j < BROWSE_DOMAINS_MAX) { 205 size_t k; 206 207 p += strspn(p, "\t "); 208 k = strcspn(p, "\t "); 209 210 if (k > 0) { 211 resolv_conf_search_domains[j++] = avahi_strndup(p, k); 212 p += k; 213 } 214 215 if (!*p) 216 break; 217 } 218 } 219 } 220 221 ret = 0; 222 223 finish: 224 225 if (ret != 0) { 226 avahi_strfreev(resolv_conf_name_servers); 227 resolv_conf_name_servers = NULL; 228 229 avahi_strfreev(resolv_conf_search_domains); 230 resolv_conf_search_domains = NULL; 231 } 232 233 if (f) 234 fclose(f); 235 236 return ret; 237 } 238 239 static AvahiSEntryGroup* add_dns_servers(AvahiServer *s, AvahiSEntryGroup* g, char **l) { 240 char **p; 241 242 assert(s); 243 assert(l); 244 245 if (!g) 246 g = avahi_s_entry_group_new(s, NULL, NULL); 247 248 assert(avahi_s_entry_group_is_empty(g)); 249 250 for (p = l; *p; p++) { 251 AvahiAddress a; 252 253 if (!avahi_address_parse(*p, AVAHI_PROTO_UNSPEC, &a)) 254 avahi_log_warn("Failed to parse address '%s', ignoring.", *p); 255 else 256 if (avahi_server_add_dns_server_address(s, g, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, NULL, AVAHI_DNS_SERVER_RESOLVE, &a, 53) < 0) { 257 avahi_s_entry_group_free(g); 258 avahi_log_error("Failed to add DNS server address: %s", avahi_strerror(avahi_server_errno(s))); 259 return NULL; 260 } 261 } 262 263 avahi_s_entry_group_commit(g); 264 265 return g; 266 } 267 268 static void remove_dns_server_entry_groups(void) { 269 270 if (resolv_conf_entry_group) 271 avahi_s_entry_group_reset(resolv_conf_entry_group); 272 273 if (dns_servers_entry_group) 274 avahi_s_entry_group_reset(dns_servers_entry_group); 275 } 276 277 static void update_wide_area_servers(void) { 278 AvahiAddress a[AVAHI_WIDE_AREA_SERVERS_MAX]; 279 unsigned n = 0; 280 char **p; 281 282 if (!resolv_conf_name_servers) { 283 avahi_server_set_wide_area_servers(avahi_server, NULL, 0); 284 return; 285 } 286 287 for (p = resolv_conf_name_servers; *p && n < AVAHI_WIDE_AREA_SERVERS_MAX; p++) { 288 if (!avahi_address_parse(*p, AVAHI_PROTO_UNSPEC, &a[n])) 289 avahi_log_warn("Failed to parse address '%s', ignoring.", *p); 290 else 291 n++; 292 } 293 294 avahi_server_set_wide_area_servers(avahi_server, a, n); 295 } 296 297 static AvahiStringList *filter_duplicate_domains(AvahiStringList *l) { 298 AvahiStringList *e, *n, *p; 299 300 if (!l) 301 return l; 302 303 for (p = l, e = l->next; e; e = n) { 304 n = e->next; 305 306 if (avahi_domain_equal((char*) e->text, (char*) l->text)) { 307 p->next = e->next; 308 avahi_free(e); 309 } else 310 p = e; 311 } 312 313 l->next = filter_duplicate_domains(l->next); 314 return l; 315 } 316 317 static void update_browse_domains(void) { 318 AvahiStringList *l; 319 int n; 320 char **p; 321 322 if (!resolv_conf_search_domains) { 323 avahi_server_set_browse_domains(avahi_server, NULL); 324 return; 325 } 326 327 l = avahi_string_list_copy(config.server_config.browse_domains); 328 329 for (p = resolv_conf_search_domains, n = 0; *p && n < BROWSE_DOMAINS_MAX; p++, n++) { 330 if (!avahi_is_valid_domain_name(*p)) 331 avahi_log_warn("'%s' is no valid domain name, ignoring.", *p); 332 else 333 l = avahi_string_list_add(l, *p); 334 } 335 336 l = filter_duplicate_domains(l); 337 338 avahi_server_set_browse_domains(avahi_server, l); 339 avahi_string_list_free(l); 340 } 341 342 static void server_callback(AvahiServer *s, AvahiServerState state, void *userdata) { 343 DaemonConfig *c = userdata; 344 345 assert(s); 346 assert(c); 347 348 /* This function is possibly called before the global variable 349 * avahi_server has been set, therefore we do it explicitly */ 350 351 avahi_server = s; 352 353 #ifdef HAVE_DBUS 354 if (c->enable_dbus && state != AVAHI_SERVER_INVALID && state != AVAHI_SERVER_FAILURE) 355 dbus_protocol_server_state_changed(state); 356 #endif 357 358 switch (state) { 359 case AVAHI_SERVER_RUNNING: 360 avahi_log_info("Server startup complete. Host name is %s. Local service cookie is %u.", avahi_server_get_host_name_fqdn(s), avahi_server_get_local_service_cookie(s)); 361 sd_notifyf(0, "STATUS=Server startup complete. Host name is %s. Local service cookie is %u.", avahi_server_get_host_name_fqdn(s), avahi_server_get_local_service_cookie(s)); 362 avahi_set_proc_title(argv0, "%s: running [%s]", argv0, avahi_server_get_host_name_fqdn(s)); 363 364 static_service_add_to_server(); 365 static_hosts_add_to_server(); 366 367 remove_dns_server_entry_groups(); 368 369 if (c->publish_resolv_conf && resolv_conf_name_servers && resolv_conf_name_servers[0]) 370 resolv_conf_entry_group = add_dns_servers(s, resolv_conf_entry_group, resolv_conf_name_servers); 371 372 if (c->publish_dns_servers && c->publish_dns_servers[0]) 373 dns_servers_entry_group = add_dns_servers(s, dns_servers_entry_group, c->publish_dns_servers); 374 375 #ifdef AVAHI_SOCKET 376 simple_protocol_restart_queries(); 377 #endif 378 break; 379 380 case AVAHI_SERVER_COLLISION: { 381 char *n; 382 383 static_service_remove_from_server(); 384 static_hosts_remove_from_server(); 385 remove_dns_server_entry_groups(); 386 387 n = avahi_alternative_host_name(avahi_server_get_host_name(s)); 388 389 avahi_log_warn("Host name conflict, retrying with %s", n); 390 sd_notifyf(0, "STATUS=Host name conflict, retrying with %s", n); 391 avahi_set_proc_title(argv0, "%s: collision [%s]", argv0, n); 392 393 avahi_server_set_host_name(s, n); 394 avahi_free(n); 395 396 break; 397 } 398 399 case AVAHI_SERVER_FAILURE: 400 401 avahi_log_error("Server error: %s", avahi_strerror(avahi_server_errno(s))); 402 sd_notifyf(0, "STATUS=Server error: %s", avahi_strerror(avahi_server_errno(s))); 403 404 avahi_simple_poll_quit(simple_poll_api); 405 break; 406 407 case AVAHI_SERVER_REGISTERING: 408 409 sd_notifyf(0, "STATUS=Registering host name %s", avahi_server_get_host_name_fqdn(s)); 410 avahi_set_proc_title(argv0, "%s: registering [%s]", argv0, avahi_server_get_host_name_fqdn(s)); 411 412 static_service_remove_from_server(); 413 static_hosts_remove_from_server(); 414 remove_dns_server_entry_groups(); 415 416 break; 417 418 case AVAHI_SERVER_INVALID: 419 break; 420 421 } 422 } 423 424 static void help(FILE *f) { 425 fprintf(f, 426 "%s [options]\n" 427 " -h --help Show this help\n" 428 " -D --daemonize Daemonize after startup (implies -s)\n" 429 " -s --syslog Write log messages to syslog(3) instead of STDERR\n" 430 " -k --kill Kill a running daemon\n" 431 " -r --reload Request a running daemon to reload static services\n" 432 " -c --check Return 0 if a daemon is already running\n" 433 " -V --version Show version\n" 434 " -f --file=FILE Load the specified configuration file instead of\n" 435 " "AVAHI_CONFIG_FILE"\n" 436 " --no-rlimits Don't enforce resource limits\n" 437 " --no-drop-root Don't drop privileges\n" 438 #ifdef ENABLE_CHROOT 439 " --no-chroot Don't chroot()\n" 440 #endif 441 " --no-proc-title Don't modify process title\n" 442 " --debug Increase verbosity\n", 443 argv0); 444 } 445 446 447 static int parse_command_line(DaemonConfig *c, int argc, char *argv[]) { 448 int o; 449 450 enum { 451 OPTION_NO_RLIMITS = 256, 452 OPTION_NO_DROP_ROOT, 453 #ifdef ENABLE_CHROOT 454 OPTION_NO_CHROOT, 455 #endif 456 OPTION_NO_PROC_TITLE, 457 OPTION_DEBUG 458 }; 459 460 static const struct option long_options[] = { 461 { "help", no_argument, NULL, 'h' }, 462 { "daemonize", no_argument, NULL, 'D' }, 463 { "kill", no_argument, NULL, 'k' }, 464 { "version", no_argument, NULL, 'V' }, 465 { "file", required_argument, NULL, 'f' }, 466 { "reload", no_argument, NULL, 'r' }, 467 { "check", no_argument, NULL, 'c' }, 468 { "syslog", no_argument, NULL, 's' }, 469 { "no-rlimits", no_argument, NULL, OPTION_NO_RLIMITS }, 470 { "no-drop-root", no_argument, NULL, OPTION_NO_DROP_ROOT }, 471 #ifdef ENABLE_CHROOT 472 { "no-chroot", no_argument, NULL, OPTION_NO_CHROOT }, 473 #endif 474 { "no-proc-title", no_argument, NULL, OPTION_NO_PROC_TITLE }, 475 { "debug", no_argument, NULL, OPTION_DEBUG }, 476 { NULL, 0, NULL, 0 } 477 }; 478 479 assert(c); 480 481 while ((o = getopt_long(argc, argv, "hDkVf:rcs", long_options, NULL)) >= 0) { 482 483 switch(o) { 484 case 's': 485 c->use_syslog = 1; 486 break; 487 case 'h': 488 c->command = DAEMON_HELP; 489 break; 490 case 'D': 491 c->daemonize = 1; 492 break; 493 case 'k': 494 c->command = DAEMON_KILL; 495 break; 496 case 'V': 497 c->command = DAEMON_VERSION; 498 break; 499 case 'f': 500 avahi_free(c->config_file); 501 c->config_file = avahi_strdup(optarg); 502 break; 503 case 'r': 504 c->command = DAEMON_RELOAD; 505 break; 506 case 'c': 507 c->command = DAEMON_CHECK; 508 break; 509 case OPTION_NO_RLIMITS: 510 c->set_rlimits = 0; 511 break; 512 case OPTION_NO_DROP_ROOT: 513 c->drop_root = 0; 514 break; 515 #ifdef ENABLE_CHROOT 516 case OPTION_NO_CHROOT: 517 c->use_chroot = 0; 518 break; 519 #endif 520 case OPTION_NO_PROC_TITLE: 521 c->modify_proc_title = 0; 522 break; 523 case OPTION_DEBUG: 524 c->debug = 1; 525 break; 526 default: 527 return -1; 528 } 529 } 530 531 if (optind < argc) { 532 fprintf(stderr, "Too many arguments\n"); 533 return -1; 534 } 535 536 return 0; 537 } 538 539 static int is_yes(const char *s) { 540 assert(s); 541 542 return *s == 'y' || *s == 'Y' || *s == '1' || *s == 't' || *s == 'T'; 543 } 544 545 static int parse_unsigned(const char *s, unsigned *u) { 546 char *e = NULL; 547 unsigned long ul; 548 unsigned k; 549 550 errno = 0; 551 ul = strtoul(s, &e, 0); 552 553 if (!e || *e || errno != 0) 554 return -1; 555 556 k = (unsigned) ul; 557 558 if ((unsigned long) k != ul) 559 return -1; 560 561 *u = k; 562 return 0; 563 } 564 565 static int parse_usec(const char *s, AvahiUsec *u) { 566 char *e = NULL; 567 unsigned long long ull; 568 AvahiUsec k; 569 570 errno = 0; 571 ull = strtoull(s, &e, 0); 572 573 if (!e || *e || errno != 0) 574 return -1; 575 576 k = (AvahiUsec) ull; 577 578 if ((unsigned long long) k != ull) 579 return -1; 580 581 *u = k; 582 return 0; 583 } 584 585 static char *get_machine_id(void) { 586 int fd; 587 char buf[32]; 588 589 fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY); 590 if (fd == -1 && errno == ENOENT) 591 fd = open("/var/lib/dbus/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY); 592 if (fd == -1) 593 return NULL; 594 595 /* File is on a filesystem so we never get EINTR or partial reads */ 596 if (read(fd, buf, sizeof buf) != sizeof buf) { 597 close(fd); 598 return NULL; 599 } 600 close(fd); 601 602 /* Contents can be lower, upper and even mixed case so normalize */ 603 avahi_strdown(buf); 604 605 return avahi_strndup(buf, sizeof buf); 606 } 607 608 static int load_config_file(DaemonConfig *c) { 609 int r = -1; 610 AvahiIniFile *f; 611 AvahiIniFileGroup *g; 612 613 assert(c); 614 615 if (!(f = avahi_ini_file_load(c->config_file ? c->config_file : AVAHI_CONFIG_FILE))) 616 goto finish; 617 618 for (g = f->groups; g; g = g->groups_next) { 619 620 if (strcasecmp(g->name, "server") == 0) { 621 AvahiIniFilePair *p; 622 623 for (p = g->pairs; p; p = p->pairs_next) { 624 625 if (strcasecmp(p->key, "host-name") == 0) { 626 avahi_free(c->server_config.host_name); 627 c->server_config.host_name = avahi_strdup(p->value); 628 } else if (strcasecmp(p->key, "domain-name") == 0) { 629 avahi_free(c->server_config.domain_name); 630 c->server_config.domain_name = avahi_strdup(p->value); 631 } else if (strcasecmp(p->key, "browse-domains") == 0) { 632 char **e, **t; 633 634 e = avahi_split_csv(p->value); 635 636 for (t = e; *t; t++) { 637 char cleaned[AVAHI_DOMAIN_NAME_MAX]; 638 639 if (!avahi_normalize_name(*t, cleaned, sizeof(cleaned))) { 640 avahi_log_error("Invalid domain name \"%s\" for key \"%s\" in group \"%s\"\n", *t, p->key, g->name); 641 avahi_strfreev(e); 642 goto finish; 643 } 644 645 c->server_config.browse_domains = avahi_string_list_add(c->server_config.browse_domains, cleaned); 646 } 647 648 avahi_strfreev(e); 649 650 c->server_config.browse_domains = filter_duplicate_domains(c->server_config.browse_domains); 651 } else if (strcasecmp(p->key, "use-ipv4") == 0) 652 c->server_config.use_ipv4 = is_yes(p->value); 653 else if (strcasecmp(p->key, "use-ipv6") == 0) 654 c->server_config.use_ipv6 = is_yes(p->value); 655 else if (strcasecmp(p->key, "check-response-ttl") == 0) 656 c->server_config.check_response_ttl = is_yes(p->value); 657 else if (strcasecmp(p->key, "allow-point-to-point") == 0) 658 c->server_config.allow_point_to_point = is_yes(p->value); 659 else if (strcasecmp(p->key, "use-iff-running") == 0) 660 c->server_config.use_iff_running = is_yes(p->value); 661 else if (strcasecmp(p->key, "disallow-other-stacks") == 0) 662 c->server_config.disallow_other_stacks = is_yes(p->value); 663 else if (strcasecmp(p->key, "host-name-from-machine-id") == 0) { 664 if (*(p->value) == 'y' || *(p->value) == 'Y') { 665 char *machine_id = get_machine_id(); 666 if (machine_id != NULL) { 667 avahi_free(c->server_config.host_name); 668 c->server_config.host_name = machine_id; 669 } 670 } 671 } 672 #ifdef HAVE_DBUS 673 else if (strcasecmp(p->key, "enable-dbus") == 0) { 674 675 if (*(p->value) == 'w' || *(p->value) == 'W') { 676 c->fail_on_missing_dbus = 0; 677 c->enable_dbus = 1; 678 } else if (*(p->value) == 'y' || *(p->value) == 'Y') { 679 c->fail_on_missing_dbus = 1; 680 c->enable_dbus = 1; 681 } else { 682 c->enable_dbus = 0; 683 } 684 } 685 #endif 686 else if (strcasecmp(p->key, "allow-interfaces") == 0) { 687 char **e, **t; 688 689 avahi_string_list_free(c->server_config.allow_interfaces); 690 c->server_config.allow_interfaces = NULL; 691 e = avahi_split_csv(p->value); 692 693 for (t = e; *t; t++) 694 c->server_config.allow_interfaces = avahi_string_list_add(c->server_config.allow_interfaces, *t); 695 696 avahi_strfreev(e); 697 } else if (strcasecmp(p->key, "deny-interfaces") == 0) { 698 char **e, **t; 699 700 avahi_string_list_free(c->server_config.deny_interfaces); 701 c->server_config.deny_interfaces = NULL; 702 e = avahi_split_csv(p->value); 703 704 for (t = e; *t; t++) 705 c->server_config.deny_interfaces = avahi_string_list_add(c->server_config.deny_interfaces, *t); 706 707 avahi_strfreev(e); 708 } else if (strcasecmp(p->key, "ratelimit-interval-usec") == 0) { 709 AvahiUsec k; 710 711 if (parse_usec(p->value, &k) < 0) { 712 avahi_log_error("Invalid ratelimit-interval-usec setting %s", p->value); 713 goto finish; 714 } 715 716 c->server_config.ratelimit_interval = k; 717 718 } else if (strcasecmp(p->key, "ratelimit-burst") == 0) { 719 unsigned k; 720 721 if (parse_unsigned(p->value, &k) < 0) { 722 avahi_log_error("Invalid ratelimit-burst setting %s", p->value); 723 goto finish; 724 } 725 726 c->server_config.ratelimit_burst = k; 727 728 } else if (strcasecmp(p->key, "cache-entries-max") == 0) { 729 unsigned k; 730 731 if (parse_unsigned(p->value, &k) < 0) { 732 avahi_log_error("Invalid cache-entries-max setting %s", p->value); 733 goto finish; 734 } 735 736 c->server_config.n_cache_entries_max = k; 737 #ifdef HAVE_DBUS 738 } else if (strcasecmp(p->key, "clients-max") == 0) { 739 unsigned k; 740 741 if (parse_unsigned(p->value, &k) < 0) { 742 avahi_log_error("Invalid clients-max setting %s", p->value); 743 goto finish; 744 } 745 746 c->n_clients_max = k; 747 } else if (strcasecmp(p->key, "objects-per-client-max") == 0) { 748 unsigned k; 749 750 if (parse_unsigned(p->value, &k) < 0) { 751 avahi_log_error("Invalid objects-per-client-max setting %s", p->value); 752 goto finish; 753 } 754 755 c->n_objects_per_client_max = k; 756 } else if (strcasecmp(p->key, "entries-per-entry-group-max") == 0) { 757 unsigned k; 758 759 if (parse_unsigned(p->value, &k) < 0) { 760 avahi_log_error("Invalid entries-per-entry-group-max setting %s", p->value); 761 goto finish; 762 } 763 764 c->n_entries_per_entry_group_max = k; 765 #endif 766 } else { 767 avahi_log_error("Invalid configuration key \"%s\" in group \"%s\"\n", p->key, g->name); 768 goto finish; 769 } 770 } 771 772 } else if (strcasecmp(g->name, "publish") == 0) { 773 AvahiIniFilePair *p; 774 775 for (p = g->pairs; p; p = p->pairs_next) { 776 777 if (strcasecmp(p->key, "publish-addresses") == 0) 778 c->server_config.publish_addresses = is_yes(p->value); 779 else if (strcasecmp(p->key, "publish-hinfo") == 0) 780 c->server_config.publish_hinfo = is_yes(p->value); 781 else if (strcasecmp(p->key, "publish-workstation") == 0) 782 c->server_config.publish_workstation = is_yes(p->value); 783 else if (strcasecmp(p->key, "publish-domain") == 0) 784 c->server_config.publish_domain = is_yes(p->value); 785 else if (strcasecmp(p->key, "publish-resolv-conf-dns-servers") == 0) 786 c->publish_resolv_conf = is_yes(p->value); 787 else if (strcasecmp(p->key, "disable-publishing") == 0) 788 c->server_config.disable_publishing = is_yes(p->value); 789 else if (strcasecmp(p->key, "disable-user-service-publishing") == 0) 790 c->disable_user_service_publishing = is_yes(p->value); 791 else if (strcasecmp(p->key, "add-service-cookie") == 0) 792 c->server_config.add_service_cookie = is_yes(p->value); 793 else if (strcasecmp(p->key, "publish-dns-servers") == 0) { 794 avahi_strfreev(c->publish_dns_servers); 795 c->publish_dns_servers = avahi_split_csv(p->value); 796 } else if (strcasecmp(p->key, "publish-a-on-ipv6") == 0) 797 c->server_config.publish_a_on_ipv6 = is_yes(p->value); 798 else if (strcasecmp(p->key, "publish-aaaa-on-ipv4") == 0) 799 c->server_config.publish_aaaa_on_ipv4 = is_yes(p->value); 800 else { 801 avahi_log_error("Invalid configuration key \"%s\" in group \"%s\"\n", p->key, g->name); 802 goto finish; 803 } 804 } 805 806 } else if (strcasecmp(g->name, "wide-area") == 0) { 807 AvahiIniFilePair *p; 808 809 for (p = g->pairs; p; p = p->pairs_next) { 810 811 if (strcasecmp(p->key, "enable-wide-area") == 0) 812 c->server_config.enable_wide_area = is_yes(p->value); 813 else { 814 avahi_log_error("Invalid configuration key \"%s\" in group \"%s\"\n", p->key, g->name); 815 goto finish; 816 } 817 } 818 819 } else if (strcasecmp(g->name, "reflector") == 0) { 820 AvahiIniFilePair *p; 821 822 for (p = g->pairs; p; p = p->pairs_next) { 823 824 if (strcasecmp(p->key, "enable-reflector") == 0) 825 c->server_config.enable_reflector = is_yes(p->value); 826 else if (strcasecmp(p->key, "reflect-ipv") == 0) 827 c->server_config.reflect_ipv = is_yes(p->value); 828 else { 829 avahi_log_error("Invalid configuration key \"%s\" in group \"%s\"\n", p->key, g->name); 830 goto finish; 831 } 832 } 833 834 } else if (strcasecmp(g->name, "rlimits") == 0) { 835 AvahiIniFilePair *p; 836 837 for (p = g->pairs; p; p = p->pairs_next) { 838 839 if (strcasecmp(p->key, "rlimit-as") == 0) { 840 c->rlimit_as_set = 1; 841 c->rlimit_as = atoi(p->value); 842 } else if (strcasecmp(p->key, "rlimit-core") == 0) { 843 c->rlimit_core_set = 1; 844 c->rlimit_core = atoi(p->value); 845 } else if (strcasecmp(p->key, "rlimit-data") == 0) { 846 c->rlimit_data_set = 1; 847 c->rlimit_data = atoi(p->value); 848 } else if (strcasecmp(p->key, "rlimit-fsize") == 0) { 849 c->rlimit_fsize_set = 1; 850 c->rlimit_fsize = atoi(p->value); 851 } else if (strcasecmp(p->key, "rlimit-nofile") == 0) { 852 c->rlimit_nofile_set = 1; 853 c->rlimit_nofile = atoi(p->value); 854 } else if (strcasecmp(p->key, "rlimit-stack") == 0) { 855 c->rlimit_stack_set = 1; 856 c->rlimit_stack = atoi(p->value); 857 } else if (strcasecmp(p->key, "rlimit-nproc") == 0) { 858 #ifdef RLIMIT_NPROC 859 c->rlimit_nproc_set = 1; 860 c->rlimit_nproc = atoi(p->value); 861 #else 862 avahi_log_error("Ignoring configuration key \"%s\" in group \"%s\"\n", p->key, g->name); 863 #endif 864 } else { 865 avahi_log_error("Invalid configuration key \"%s\" in group \"%s\"\n", p->key, g->name); 866 goto finish; 867 } 868 869 } 870 871 } else { 872 avahi_log_error("Invalid configuration file group \"%s\".\n", g->name); 873 goto finish; 874 } 875 } 876 877 r = 0; 878 879 finish: 880 881 if (f) 882 avahi_ini_file_free(f); 883 884 return r; 885 } 886 887 static void log_function(AvahiLogLevel level, const char *txt) { 888 889 static const int log_level_map[] = { 890 LOG_ERR, 891 LOG_WARNING, 892 LOG_NOTICE, 893 LOG_INFO, 894 LOG_DEBUG 895 }; 896 897 assert(level < AVAHI_LOG_LEVEL_MAX); 898 assert(txt); 899 900 if (!config.debug && level == AVAHI_LOG_DEBUG) 901 return; 902 903 daemon_log(log_level_map[level], "%s", txt); 904 } 905 906 static void dump(const char *text, AVAHI_GCC_UNUSED void* userdata) { 907 avahi_log_info("%s", text); 908 } 909 910 #ifdef HAVE_INOTIFY 911 912 static int inotify_fd = -1; 913 914 static void add_inotify_watches(void) { 915 int c = 0; 916 /* We ignore the return values, because one or more of these files 917 * might not exist and we're OK with that. In addition we never 918 * want to remove these watches, hence we keep their ids? */ 919 920 #ifdef ENABLE_CHROOT 921 c = config.use_chroot; 922 #endif 923 924 inotify_add_watch(inotify_fd, c ? "/services" : AVAHI_SERVICE_DIR, IN_CLOSE_WRITE|IN_DELETE|IN_DELETE_SELF|IN_MOVED_FROM|IN_MOVED_TO|IN_MOVE_SELF 925 #ifdef IN_ONLYDIR 926 |IN_ONLYDIR 927 #endif 928 ); 929 930 #ifdef AVAHI_CONFIG_DIR 931 inotify_add_watch(inotify_fd, c ? "/" : AVAHI_CONFIG_DIR, IN_CLOSE_WRITE|IN_DELETE|IN_DELETE_SELF|IN_MOVED_FROM|IN_MOVED_TO|IN_MOVE_SELF 932 #ifdef IN_ONLYDIR 933 |IN_ONLYDIR 934 #endif 935 ); 936 #endif 937 938 } 939 940 #endif 941 942 #ifdef HAVE_KQUEUE 943 944 #define NUM_WATCHES 2 945 946 static int kq = -1; 947 static int kfds[NUM_WATCHES]; 948 static int num_kfds = 0; 949 950 static void add_kqueue_watch(const char *dir); 951 952 static void add_kqueue_watches(void) { 953 int c = 0; 954 955 #ifdef ENABLE_CHROOT 956 c = config.use_chroot; 957 #endif 958 959 #ifdef AVAHI_CONFIG_DIR 960 add_kqueue_watch(c ? "/" : AVAHI_CONFIG_DIR); 961 #endif 962 963 add_kqueue_watch(c ? "/services" : AVAHI_SERVICE_DIR); 964 } 965 966 static void add_kqueue_watch(const char *dir) { 967 int fd; 968 struct kevent ev; 969 970 if (kq < 0) 971 return; 972 973 if (num_kfds >= NUM_WATCHES) 974 return; 975 976 fd = open(dir, O_RDONLY); 977 if (fd < 0) 978 return; 979 EV_SET(&ev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, 980 NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_RENAME, 981 0, 0); 982 if (kevent(kq, &ev, 1, NULL, 0, NULL) == -1) { 983 close(fd); 984 return; 985 } 986 987 kfds[num_kfds++] = fd; 988 } 989 990 #endif 991 992 static void reload_config(void) { 993 994 #ifdef HAVE_INOTIFY 995 /* Refresh in case the config dirs have been removed */ 996 add_inotify_watches(); 997 #endif 998 999 #ifdef HAVE_KQUEUE 1000 add_kqueue_watches(); 1001 #endif 1002 1003 #ifdef ENABLE_CHROOT 1004 static_service_load(config.use_chroot); 1005 static_hosts_load(config.use_chroot); 1006 #else 1007 static_service_load(0); 1008 static_hosts_load(0); 1009 #endif 1010 static_service_add_to_server(); 1011 static_hosts_add_to_server(); 1012 1013 if (resolv_conf_entry_group) 1014 avahi_s_entry_group_reset(resolv_conf_entry_group); 1015 1016 load_resolv_conf(); 1017 1018 update_wide_area_servers(); 1019 update_browse_domains(); 1020 1021 if (config.publish_resolv_conf && resolv_conf_name_servers && resolv_conf_name_servers[0]) 1022 resolv_conf_entry_group = add_dns_servers(avahi_server, resolv_conf_entry_group, resolv_conf_name_servers); 1023 } 1024 1025 #ifdef HAVE_INOTIFY 1026 1027 static void inotify_callback(AvahiWatch *watch, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent event, AVAHI_GCC_UNUSED void *userdata) { 1028 char* buffer; 1029 int n = 0; 1030 1031 assert(fd == inotify_fd); 1032 assert(watch); 1033 1034 ioctl(inotify_fd, FIONREAD, &n); 1035 if (n <= 0) 1036 n = 128; 1037 1038 buffer = avahi_malloc(n); 1039 if (read(inotify_fd, buffer, n) < 0 ) { 1040 avahi_free(buffer); 1041 avahi_log_error("Failed to read inotify event: %s", avahi_strerror(errno)); 1042 return; 1043 } 1044 avahi_free(buffer); 1045 1046 avahi_log_info("Files changed, reloading."); 1047 reload_config(); 1048 } 1049 1050 #endif 1051 1052 #ifdef HAVE_KQUEUE 1053 1054 static void kqueue_callback(AvahiWatch *watch, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent event, AVAHI_GCC_UNUSED void *userdata) { 1055 struct kevent ev; 1056 struct timespec nullts = { 0, 0 }; 1057 int res; 1058 1059 assert(fd == kq); 1060 assert(watch); 1061 1062 res = kevent(kq, NULL, 0, &ev, 1, &nullts); 1063 1064 if (res > 0) { 1065 /* Sleep for a half-second to avoid potential races 1066 * during install/uninstall. */ 1067 usleep(500000); 1068 avahi_log_info("Files changed, reloading."); 1069 reload_config(); 1070 } else { 1071 avahi_log_error("Failed to read kqueue event: %s", avahi_strerror(errno)); 1072 } 1073 } 1074 1075 #endif 1076 1077 static void signal_callback(AvahiWatch *watch, AVAHI_GCC_UNUSED int fd, AVAHI_GCC_UNUSED AvahiWatchEvent event, AVAHI_GCC_UNUSED void *userdata) { 1078 int sig; 1079 const AvahiPoll *poll_api; 1080 1081 assert(watch); 1082 assert(simple_poll_api); 1083 1084 poll_api = avahi_simple_poll_get(simple_poll_api); 1085 1086 if ((sig = daemon_signal_next()) <= 0) { 1087 avahi_log_error("daemon_signal_next() failed"); 1088 poll_api->watch_free(watch); 1089 return; 1090 } 1091 1092 switch (sig) { 1093 case SIGINT: 1094 case SIGTERM: 1095 avahi_log_info( 1096 "Got %s, quitting.", 1097 sig == SIGINT ? "SIGINT" : "SIGTERM"); 1098 avahi_simple_poll_quit(simple_poll_api); 1099 break; 1100 1101 case SIGHUP: 1102 avahi_log_info("Got SIGHUP, reloading."); 1103 1104 reload_config(); 1105 break; 1106 1107 case SIGUSR1: 1108 avahi_log_info("Got SIGUSR1, dumping record data."); 1109 avahi_server_dump(avahi_server, dump, NULL); 1110 break; 1111 1112 default: 1113 avahi_log_warn("Got spurious signal, ignoring."); 1114 break; 1115 } 1116 } 1117 1118 /* Imported from ../avahi-client/nss-check.c */ 1119 int avahi_nss_support(void); 1120 1121 static void ignore_signal(int sig) { 1122 struct sigaction sa; 1123 1124 memset(&sa, 0, sizeof(sa)); 1125 sa.sa_handler = SIG_IGN; 1126 sa.sa_flags = SA_RESTART; 1127 1128 sigaction(sig, &sa, NULL); 1129 } 1130 1131 static int run_server(DaemonConfig *c) { 1132 int r = -1; 1133 int error; 1134 const AvahiPoll *poll_api = NULL; 1135 AvahiWatch *sig_watch = NULL; 1136 int retval_is_sent = 0; 1137 #ifdef HAVE_INOTIFY 1138 AvahiWatch *inotify_watch = NULL; 1139 #endif 1140 #ifdef HAVE_KQUEUE 1141 int i; 1142 AvahiWatch *kqueue_watch = NULL; 1143 #endif 1144 1145 assert(c); 1146 1147 ignore_signal(SIGPIPE); 1148 1149 if (!(nss_support = avahi_nss_support())) 1150 avahi_log_warn("WARNING: No NSS support for mDNS detected, consider installing nss-mdns!"); 1151 1152 if (!(simple_poll_api = avahi_simple_poll_new())) { 1153 avahi_log_error("Failed to create main loop object."); 1154 goto finish; 1155 } 1156 1157 poll_api = avahi_simple_poll_get(simple_poll_api); 1158 1159 if (daemon_signal_init(SIGINT, SIGHUP, SIGTERM, SIGUSR1, 0) < 0) { 1160 avahi_log_error("Could not register signal handlers (%s).", strerror(errno)); 1161 goto finish; 1162 } 1163 1164 if (!(sig_watch = poll_api->watch_new(poll_api, daemon_signal_fd(), AVAHI_WATCH_IN, signal_callback, simple_poll_api))) { 1165 avahi_log_error( "Failed to create signal watcher"); 1166 goto finish; 1167 } 1168 1169 #ifdef AVAHI_SOCKET 1170 if (simple_protocol_setup(poll_api) < 0) 1171 goto finish; 1172 #endif 1173 1174 #ifdef HAVE_DBUS 1175 if (c->enable_dbus) { 1176 if (dbus_protocol_setup(poll_api, 1177 config.disable_user_service_publishing, 1178 config.n_clients_max, 1179 config.n_objects_per_client_max, 1180 config.n_entries_per_entry_group_max, 1181 !c->fail_on_missing_dbus 1182 #ifdef ENABLE_CHROOT 1183 && !config.use_chroot 1184 #endif 1185 ) < 0) { 1186 1187 avahi_log_warn("WARNING: Failed to contact D-Bus daemon."); 1188 1189 if (c->fail_on_missing_dbus) 1190 goto finish; 1191 } 1192 } 1193 #endif 1194 1195 #ifdef ENABLE_CHROOT 1196 1197 if (config.drop_root && config.use_chroot) { 1198 if (chroot(AVAHI_CONFIG_DIR) < 0) { 1199 avahi_log_error("Failed to chroot(): %s", strerror(errno)); 1200 goto finish; 1201 } 1202 1203 avahi_log_info("Successfully called chroot()."); 1204 chdir("/"); 1205 1206 if (avahi_caps_drop_all() < 0) { 1207 avahi_log_error("Failed to drop capabilities."); 1208 goto finish; 1209 } 1210 avahi_log_info("Successfully dropped remaining capabilities."); 1211 } 1212 1213 #endif 1214 1215 #ifdef HAVE_INOTIFY 1216 if ((inotify_fd = inotify_init()) < 0) 1217 avahi_log_warn( "Failed to initialize inotify: %s", strerror(errno)); 1218 else { 1219 add_inotify_watches(); 1220 1221 if (!(inotify_watch = poll_api->watch_new(poll_api, inotify_fd, AVAHI_WATCH_IN, inotify_callback, NULL))) { 1222 avahi_log_error( "Failed to create inotify watcher"); 1223 goto finish; 1224 } 1225 } 1226 #endif 1227 1228 #ifdef HAVE_KQUEUE 1229 if ((kq = kqueue()) < 0) 1230 avahi_log_warn( "Failed to initialize kqueue: %s", strerror(errno)); 1231 else { 1232 add_kqueue_watches(); 1233 1234 if (!(kqueue_watch = poll_api->watch_new(poll_api, kq, AVAHI_WATCH_IN, kqueue_callback, NULL))) { 1235 avahi_log_error( "Failed to create kqueue watcher"); 1236 goto finish; 1237 } 1238 } 1239 #endif 1240 1241 load_resolv_conf(); 1242 #ifdef ENABLE_CHROOT 1243 static_service_load(config.use_chroot); 1244 static_hosts_load(config.use_chroot); 1245 #else 1246 static_service_load(0); 1247 static_hosts_load(0); 1248 #endif 1249 1250 if (!(avahi_server = avahi_server_new(poll_api, &c->server_config, server_callback, c, &error))) { 1251 avahi_log_error("Failed to create server: %s", avahi_strerror(error)); 1252 goto finish; 1253 } 1254 1255 update_wide_area_servers(); 1256 update_browse_domains(); 1257 1258 if (c->daemonize) { 1259 daemon_retval_send(0); 1260 retval_is_sent = 1; 1261 } 1262 1263 for (;;) { 1264 if ((r = avahi_simple_poll_iterate(simple_poll_api, -1)) < 0) { 1265 1266 /* We handle signals through an FD, so let's continue */ 1267 if (errno == EINTR) 1268 continue; 1269 1270 avahi_log_error("poll(): %s", strerror(errno)); 1271 goto finish; 1272 } else if (r > 0) 1273 /* Quit */ 1274 break; 1275 } 1276 1277 r = 0; 1278 1279 finish: 1280 1281 static_service_remove_from_server(); 1282 static_service_free_all(); 1283 1284 static_hosts_remove_from_server(); 1285 static_hosts_free_all(); 1286 1287 remove_dns_server_entry_groups(); 1288 1289 #ifdef AVAHI_SOCKET 1290 simple_protocol_shutdown(); 1291 #endif 1292 1293 #ifdef HAVE_DBUS 1294 if (c->enable_dbus) 1295 dbus_protocol_shutdown(); 1296 #endif 1297 1298 if (avahi_server) { 1299 avahi_server_free(avahi_server); 1300 avahi_server = NULL; 1301 } 1302 1303 daemon_signal_done(); 1304 1305 if (sig_watch) 1306 poll_api->watch_free(sig_watch); 1307 1308 #ifdef HAVE_INOTIFY 1309 if (inotify_watch) 1310 poll_api->watch_free(inotify_watch); 1311 if (inotify_fd >= 0) 1312 close(inotify_fd); 1313 #endif 1314 1315 #ifdef HAVE_KQUEUE 1316 if (kqueue_watch) 1317 poll_api->watch_free(kqueue_watch); 1318 if (kq >= 0) 1319 close(kq); 1320 for (i = 0; i < num_kfds; i++) { 1321 if (kfds[i] >= 0) 1322 close(kfds[i]); 1323 } 1324 #endif 1325 1326 if (simple_poll_api) { 1327 avahi_simple_poll_free(simple_poll_api); 1328 simple_poll_api = NULL; 1329 } 1330 1331 if (!retval_is_sent && c->daemonize) 1332 daemon_retval_send(1); 1333 1334 return r; 1335 } 1336 1337 #define set_env(key, value) putenv(avahi_strdup_printf("%s=%s", (key), (value))) 1338 1339 static int drop_root(void) { 1340 struct passwd *pw; 1341 struct group * gr; 1342 int r; 1343 1344 if (!(pw = getpwnam(AVAHI_USER))) { 1345 avahi_log_error( "Failed to find user '"AVAHI_USER"'."); 1346 return -1; 1347 } 1348 1349 if (!(gr = getgrnam(AVAHI_GROUP))) { 1350 avahi_log_error( "Failed to find group '"AVAHI_GROUP"'."); 1351 return -1; 1352 } 1353 1354 avahi_log_info("Found user '"AVAHI_USER"' (UID %lu) and group '"AVAHI_GROUP"' (GID %lu).", (unsigned long) pw->pw_uid, (unsigned long) gr->gr_gid); 1355 1356 if (initgroups(AVAHI_USER, gr->gr_gid) != 0) { 1357 avahi_log_error("Failed to change group list: %s", strerror(errno)); 1358 return -1; 1359 } 1360 1361 #if defined(HAVE_SETRESGID) 1362 r = setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid); 1363 #elif defined(HAVE_SETEGID) 1364 if ((r = setgid(gr->gr_gid)) >= 0) 1365 r = setegid(gr->gr_gid); 1366 #elif defined(HAVE_SETREGID) 1367 r = setregid(gr->gr_gid, gr->gr_gid); 1368 #else 1369 #error "No API to drop privileges" 1370 #endif 1371 1372 if (r < 0) { 1373 avahi_log_error("Failed to change GID: %s", strerror(errno)); 1374 return -1; 1375 } 1376 1377 #if defined(HAVE_SETRESUID) 1378 r = setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid); 1379 #elif defined(HAVE_SETEUID) 1380 if ((r = setuid(pw->pw_uid)) >= 0) 1381 r = seteuid(pw->pw_uid); 1382 #elif defined(HAVE_SETREUID) 1383 r = setreuid(pw->pw_uid, pw->pw_uid); 1384 #else 1385 #error "No API to drop privileges" 1386 #endif 1387 1388 if (r < 0) { 1389 avahi_log_error("Failed to change UID: %s", strerror(errno)); 1390 return -1; 1391 } 1392 1393 set_env("USER", pw->pw_name); 1394 set_env("LOGNAME", pw->pw_name); 1395 set_env("HOME", pw->pw_dir); 1396 1397 avahi_log_info("Successfully dropped root privileges."); 1398 1399 return 0; 1400 } 1401 1402 static const char* pid_file_proc(void) { 1403 #ifdef AVAHI_DAEMON_RUNTIME_DIR 1404 return AVAHI_DAEMON_RUNTIME_DIR"/pid"; 1405 #else 1406 return NULL; 1407 #endif 1408 } 1409 1410 #ifdef AVAHI_DAEMON_RUNTIME_DIR 1411 static int make_runtime_dir(void) { 1412 int r = -1; 1413 mode_t u; 1414 int reset_umask = 0; 1415 struct passwd *pw; 1416 struct group * gr; 1417 struct stat st; 1418 1419 #ifndef __BIONIC__ 1420 if (!(pw = getpwnam(AVAHI_USER))) { 1421 avahi_log_error( "Failed to find user '"AVAHI_USER"'."); 1422 goto fail; 1423 } 1424 1425 if (!(gr = getgrnam(AVAHI_GROUP))) { 1426 avahi_log_error( "Failed to find group '"AVAHI_GROUP"'."); 1427 goto fail; 1428 } 1429 #endif 1430 1431 u = umask(0000); 1432 reset_umask = 1; 1433 1434 if (mkdir(AVAHI_DAEMON_RUNTIME_DIR, 0755) < 0 && errno != EEXIST) { 1435 avahi_log_error("mkdir(\""AVAHI_DAEMON_RUNTIME_DIR"\"): %s", strerror(errno)); 1436 goto fail; 1437 } 1438 #ifndef __BIONIC__ 1439 chown(AVAHI_DAEMON_RUNTIME_DIR, pw->pw_uid, gr->gr_gid); 1440 1441 if (stat(AVAHI_DAEMON_RUNTIME_DIR, &st) < 0) { 1442 avahi_log_error("stat(): %s\n", strerror(errno)); 1443 goto fail; 1444 } 1445 1446 if (!S_ISDIR(st.st_mode) || st.st_uid != pw->pw_uid || st.st_gid != gr->gr_gid) { 1447 avahi_log_error("Failed to create runtime directory "AVAHI_DAEMON_RUNTIME_DIR"."); 1448 goto fail; 1449 } 1450 #endif 1451 r = 0; 1452 1453 fail: 1454 if (reset_umask) 1455 umask(u); 1456 return r; 1457 } 1458 #endif 1459 1460 static void set_one_rlimit(int resource, rlim_t limit, const char *name) { 1461 struct rlimit rl; 1462 rl.rlim_cur = rl.rlim_max = limit; 1463 1464 if (setrlimit(resource, &rl) < 0) 1465 avahi_log_warn("setrlimit(%s, {%u, %u}) failed: %s", name, (unsigned) limit, (unsigned) limit, strerror(errno)); 1466 } 1467 1468 static void enforce_rlimits(void) { 1469 #ifdef RLIMIT_AS 1470 if (config.rlimit_as_set) 1471 set_one_rlimit(RLIMIT_AS, config.rlimit_as, "RLIMIT_AS"); 1472 #endif 1473 if (config.rlimit_core_set) 1474 set_one_rlimit(RLIMIT_CORE, config.rlimit_core, "RLIMIT_CORE"); 1475 if (config.rlimit_data_set) 1476 set_one_rlimit(RLIMIT_DATA, config.rlimit_data, "RLIMIT_DATA"); 1477 if (config.rlimit_fsize_set) 1478 set_one_rlimit(RLIMIT_FSIZE, config.rlimit_fsize, "RLIMIT_FSIZE"); 1479 if (config.rlimit_nofile_set) 1480 set_one_rlimit(RLIMIT_NOFILE, config.rlimit_nofile, "RLIMIT_NOFILE"); 1481 if (config.rlimit_stack_set) 1482 set_one_rlimit(RLIMIT_STACK, config.rlimit_stack, "RLIMIT_STACK"); 1483 #ifdef RLIMIT_NPROC 1484 if (config.rlimit_nproc_set) 1485 set_one_rlimit(RLIMIT_NPROC, config.rlimit_nproc, "RLIMIT_NPROC"); 1486 #endif 1487 1488 /* the sysctl() call from iface-pfroute.c needs locked memory on FreeBSD */ 1489 #if defined(RLIMIT_MEMLOCK) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) 1490 /* We don't need locked memory */ 1491 set_one_rlimit(RLIMIT_MEMLOCK, 0, "RLIMIT_MEMLOCK"); 1492 #endif 1493 } 1494 1495 #define RANDOM_DEVICE "/dev/urandom" 1496 1497 static void init_rand_seed(void) { 1498 int fd; 1499 unsigned seed = 0; 1500 1501 /* Try to initialize seed from /dev/urandom, to make it a little 1502 * less predictable, and to make sure that multiple machines 1503 * booted at the same time choose different random seeds. */ 1504 if ((fd = open(RANDOM_DEVICE, O_RDONLY)) >= 0) { 1505 read(fd, &seed, sizeof(seed)); 1506 close(fd); 1507 } 1508 1509 /* If the initialization failed by some reason, we add the time to the seed*/ 1510 seed ^= (unsigned) time(NULL); 1511 1512 srand(seed); 1513 } 1514 1515 #ifdef BUILD_AS_ANDROID_SERVICE 1516 int avahi_main(int argc, char *argv[]) { 1517 #else 1518 int main(int argc, char *argv[]) { 1519 #endif 1520 int r = 255; 1521 int wrote_pid_file = 0; 1522 1523 avahi_set_log_function(log_function); 1524 1525 init_rand_seed(); 1526 1527 avahi_server_config_init(&config.server_config); 1528 config.command = DAEMON_RUN; 1529 config.daemonize = 0; 1530 config.config_file = NULL; 1531 #ifdef HAVE_DBUS 1532 config.enable_dbus = 1; 1533 config.fail_on_missing_dbus = 1; 1534 config.n_clients_max = 0; 1535 config.n_objects_per_client_max = 0; 1536 config.n_entries_per_entry_group_max = 0; 1537 #endif 1538 1539 config.drop_root = 1; 1540 config.set_rlimits = 1; 1541 #ifdef ENABLE_CHROOT 1542 config.use_chroot = 1; 1543 #endif 1544 config.modify_proc_title = 1; 1545 1546 config.disable_user_service_publishing = 0; 1547 config.publish_dns_servers = NULL; 1548 config.publish_resolv_conf = 0; 1549 config.use_syslog = 0; 1550 config.debug = 0; 1551 config.rlimit_as_set = 0; 1552 config.rlimit_core_set = 0; 1553 config.rlimit_data_set = 0; 1554 config.rlimit_fsize_set = 0; 1555 config.rlimit_nofile_set = 0; 1556 config.rlimit_stack_set = 0; 1557 #ifdef RLIMIT_NPROC 1558 config.rlimit_nproc_set = 0; 1559 #endif 1560 1561 if ((argv0 = strrchr(argv[0], '/'))) 1562 argv0 = avahi_strdup(argv0 + 1); 1563 else 1564 argv0 = avahi_strdup(argv[0]); 1565 1566 daemon_pid_file_ident = (const char *) argv0; 1567 daemon_log_ident = (char*) argv0; 1568 daemon_pid_file_proc = pid_file_proc; 1569 1570 if (parse_command_line(&config, argc, argv) < 0) 1571 goto finish; 1572 1573 if (config.modify_proc_title) 1574 avahi_init_proc_title(argc, argv); 1575 1576 #ifdef ENABLE_CHROOT 1577 config.use_chroot = config.use_chroot && config.drop_root; 1578 #endif 1579 1580 if (config.command == DAEMON_HELP) { 1581 help(stdout); 1582 r = 0; 1583 } else if (config.command == DAEMON_VERSION) { 1584 printf("%s "PACKAGE_VERSION"\n", argv0); 1585 r = 0; 1586 } else if (config.command == DAEMON_KILL) { 1587 if (daemon_pid_file_kill_wait(SIGTERM, 5) < 0) { 1588 avahi_log_warn("Failed to kill daemon: %s", strerror(errno)); 1589 goto finish; 1590 } 1591 1592 r = 0; 1593 1594 } else if (config.command == DAEMON_RELOAD) { 1595 if (daemon_pid_file_kill(SIGHUP) < 0) { 1596 avahi_log_warn("Failed to kill daemon: %s", strerror(errno)); 1597 goto finish; 1598 } 1599 1600 r = 0; 1601 1602 } else if (config.command == DAEMON_CHECK) 1603 r = (daemon_pid_file_is_running() >= 0) ? 0 : 1; 1604 else if (config.command == DAEMON_RUN) { 1605 pid_t pid; 1606 1607 if (getuid() != 0 && config.drop_root) { 1608 avahi_log_error("This program is intended to be run as root."); 1609 goto finish; 1610 } 1611 1612 if ((pid = daemon_pid_file_is_running()) >= 0) { 1613 avahi_log_error("Daemon already running on PID %u", pid); 1614 goto finish; 1615 } 1616 1617 if (load_config_file(&config) < 0) 1618 goto finish; 1619 1620 if (config.daemonize) { 1621 daemon_retval_init(); 1622 1623 if ((pid = daemon_fork()) < 0) 1624 goto finish; 1625 else if (pid != 0) { 1626 int ret; 1627 /** Parent **/ 1628 1629 if ((ret = daemon_retval_wait(20)) < 0) { 1630 avahi_log_error("Could not receive return value from daemon process."); 1631 goto finish; 1632 } 1633 1634 r = ret; 1635 goto finish; 1636 } 1637 1638 /* Child */ 1639 } 1640 1641 if (config.use_syslog || config.daemonize) 1642 daemon_log_use = DAEMON_LOG_SYSLOG; 1643 1644 #ifndef BUILD_AS_ANDROID_SERVICE 1645 if (sd_listen_fds(0) <= 0) 1646 if (daemon_close_all(-1) < 0) 1647 avahi_log_warn("Failed to close all remaining file descriptors: %s", strerror(errno)); 1648 1649 daemon_reset_sigs(-1); 1650 daemon_unblock_sigs(-1); 1651 #endif 1652 1653 #ifdef AVAHI_DAEMON_RUNTIME_DIR 1654 if (make_runtime_dir() < 0) 1655 goto finish; 1656 #endif 1657 1658 if (config.drop_root) { 1659 #ifdef ENABLE_CHROOT 1660 if (config.use_chroot) 1661 if (avahi_caps_reduce() < 0) 1662 goto finish; 1663 #endif 1664 1665 if (drop_root() < 0) 1666 goto finish; 1667 1668 #ifdef ENABLE_CHROOT 1669 if (config.use_chroot) 1670 if (avahi_caps_reduce2() < 0) 1671 goto finish; 1672 #endif 1673 } 1674 1675 if (daemon_pid_file_create() < 0) { 1676 if (config.daemonize) 1677 daemon_retval_send(1); 1678 1679 if (pid_file_proc() != NULL) { 1680 avahi_log_error("Failed to create PID file: %s", 1681 strerror(errno)); 1682 goto finish; 1683 } 1684 } else 1685 wrote_pid_file = 1; 1686 1687 if (config.set_rlimits) 1688 enforce_rlimits(); 1689 1690 chdir("/"); 1691 1692 #ifdef ENABLE_CHROOT 1693 if (config.drop_root && config.use_chroot) 1694 if (avahi_chroot_helper_start(argv0) < 0) { 1695 avahi_log_error("failed to start chroot() helper daemon."); 1696 goto finish; 1697 } 1698 #endif 1699 avahi_log_info("%s "PACKAGE_VERSION" starting up.", argv0); 1700 sd_notifyf(0, "STATUS=%s "PACKAGE_VERSION" starting up.", argv0); 1701 avahi_set_proc_title(argv0, "%s: starting up", argv0); 1702 1703 if (run_server(&config) == 0) 1704 r = 0; 1705 1706 avahi_log_info("%s "PACKAGE_VERSION" exiting.", argv0); 1707 sd_notifyf(0, "STATUS=%s "PACKAGE_VERSION" exiting.", argv0); 1708 } 1709 1710 finish: 1711 1712 if (config.daemonize) 1713 daemon_retval_done(); 1714 1715 avahi_server_config_free(&config.server_config); 1716 avahi_free(config.config_file); 1717 avahi_strfreev(config.publish_dns_servers); 1718 avahi_strfreev(resolv_conf_name_servers); 1719 avahi_strfreev(resolv_conf_search_domains); 1720 1721 if (wrote_pid_file) { 1722 #ifdef ENABLE_CHROOT 1723 avahi_chroot_helper_unlink(pid_file_proc()); 1724 #else 1725 daemon_pid_file_remove(); 1726 #endif 1727 } 1728 1729 #ifdef ENABLE_CHROOT 1730 avahi_chroot_helper_shutdown(); 1731 #endif 1732 1733 avahi_free(argv0); 1734 1735 return r; 1736 } 1737