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