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