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