Home | History | Annotate | Download | only in avahi-daemon
      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