]> git.meshlink.io Git - catta/blob - avahi-daemon/main.c
Fix error message when passing invalid command line arguments. I admit defeat, tedp...
[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 <stdio.h>
37 #include <fcntl.h>
38 #include <time.h>
39 #include <stdlib.h>
40 #include <sys/time.h>
41 #include <sys/resource.h>
42 #include <sys/socket.h>
43
44 #include <libdaemon/dfork.h>
45 #include <libdaemon/dsignal.h>
46 #include <libdaemon/dlog.h>
47 #include <libdaemon/dpid.h>
48
49 #include <avahi-common/malloc.h>
50 #include <avahi-common/simple-watch.h>
51 #include <avahi-common/error.h>
52 #include <avahi-common/alternative.h>
53 #include <avahi-common/domain.h>
54
55 #include <avahi-core/core.h>
56 #include <avahi-core/publish.h>
57 #include <avahi-core/dns-srv-rr.h>
58 #include <avahi-core/log.h>
59
60 #ifdef ENABLE_CHROOT
61 #include "chroot.h"
62 #include "caps.h"
63 #endif
64
65 #include "setproctitle.h"
66 #include "main.h"
67 #include "simple-protocol.h"
68 #include "static-services.h"
69 #include "static-hosts.h"
70 #include "ini-file-parser.h"
71
72 #ifdef HAVE_DBUS
73 #include "dbus-protocol.h"
74 #endif
75
76 AvahiServer *avahi_server = NULL;
77 AvahiSimplePoll *simple_poll_api = NULL;
78 static char *argv0 = NULL;
79 int nss_support = 0;
80
81 typedef enum {
82     DAEMON_RUN,
83     DAEMON_KILL,
84     DAEMON_VERSION,
85     DAEMON_HELP,
86     DAEMON_RELOAD,
87     DAEMON_CHECK
88 } DaemonCommand;
89
90 typedef struct {
91     AvahiServerConfig server_config;
92     DaemonCommand command;
93     int daemonize;
94     int use_syslog;
95     char *config_file;
96 #ifdef HAVE_DBUS
97     int enable_dbus;
98     int fail_on_missing_dbus;
99 #endif
100     int drop_root;
101     int set_rlimits;
102 #ifdef ENABLE_CHROOT
103     int use_chroot;
104 #endif
105     int modify_proc_title;
106
107     int disable_user_service_publishing;
108     int publish_resolv_conf;
109     char ** publish_dns_servers;
110     int debug;
111
112     int rlimit_as_set, rlimit_core_set, rlimit_data_set, rlimit_fsize_set, rlimit_nofile_set, rlimit_stack_set;
113     rlim_t rlimit_as, rlimit_core, rlimit_data, rlimit_fsize, rlimit_nofile, rlimit_stack;
114
115 #ifdef RLIMIT_NPROC
116     int rlimit_nproc_set;
117     rlim_t rlimit_nproc;
118 #endif
119 } DaemonConfig;
120
121 #define RESOLV_CONF "/etc/resolv.conf"
122
123 static AvahiSEntryGroup *dns_servers_entry_group = NULL;
124 static AvahiSEntryGroup *resolv_conf_entry_group = NULL;
125
126 static char **resolv_conf = NULL;
127
128 static DaemonConfig config;
129
130 static int has_prefix(const char *s, const char *prefix) {
131     size_t l;
132
133     l = strlen(prefix);
134     
135     return strlen(s) >= l && strncmp(s, prefix, l) == 0;
136 }
137
138 static int load_resolv_conf(void) {
139     int ret = -1;
140     FILE *f;
141     int i = 0;
142     
143     avahi_strfreev(resolv_conf);
144     resolv_conf = NULL;
145
146 #ifdef ENABLE_CHROOT
147     f = avahi_chroot_helper_get_file(RESOLV_CONF);
148 #else
149     f = fopen(RESOLV_CONF, "r");
150 #endif
151     
152     if (!f) {
153         avahi_log_warn("Failed to open "RESOLV_CONF": %s", strerror(errno));
154         goto finish;
155     }
156
157     resolv_conf = avahi_new0(char*, AVAHI_WIDE_AREA_SERVERS_MAX+1);
158
159     while (!feof(f) && i < AVAHI_WIDE_AREA_SERVERS_MAX) {
160         char ln[128];
161         char *p;
162
163         if (!(fgets(ln, sizeof(ln), f)))
164             break;
165
166         ln[strcspn(ln, "\r\n#")] = 0;
167         p = ln + strspn(ln, "\t ");
168
169         if (has_prefix(p, "nameserver")) {
170             p += 10;
171             p += strspn(p, "\t ");
172             p[strcspn(p, "\t ")] = 0;
173             resolv_conf[i++] = avahi_strdup(p);
174         }
175     }
176
177     ret = 0;
178
179 finish:
180
181     if (ret != 0) {
182         avahi_strfreev(resolv_conf);
183         resolv_conf = NULL;
184     }
185         
186     if (f)
187         fclose(f);
188
189     return ret;
190 }
191
192 static AvahiSEntryGroup* add_dns_servers(AvahiServer *s, AvahiSEntryGroup* g, char **l) {
193     char **p;
194
195     assert(s);
196     assert(l);
197
198     if (!g) 
199         g = avahi_s_entry_group_new(s, NULL, NULL);
200
201     assert(avahi_s_entry_group_is_empty(g));
202
203     for (p = l; *p; p++) {
204         AvahiAddress a;
205         
206         if (!avahi_address_parse(*p, AVAHI_PROTO_UNSPEC, &a))
207             avahi_log_warn("Failed to parse address '%s', ignoring.", *p);
208         else
209             if (avahi_server_add_dns_server_address(s, g, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, NULL, AVAHI_DNS_SERVER_RESOLVE, &a, 53) < 0) {
210                 avahi_s_entry_group_free(g);
211                 avahi_log_error("Failed to add DNS server address: %s", avahi_strerror(avahi_server_errno(s)));
212                 return NULL;
213             }
214     }
215
216     avahi_s_entry_group_commit(g);
217
218     return g;
219 }
220
221 static void remove_dns_server_entry_groups(void) {
222
223     if (resolv_conf_entry_group)
224         avahi_s_entry_group_reset(resolv_conf_entry_group);
225     
226     if (dns_servers_entry_group) 
227         avahi_s_entry_group_reset(dns_servers_entry_group);
228 }
229
230 static void update_wide_area_servers(void) {
231     AvahiAddress a[AVAHI_WIDE_AREA_SERVERS_MAX];
232     unsigned n = 0;
233     char **p;
234
235     if (!resolv_conf) {
236         avahi_server_set_wide_area_servers(avahi_server, NULL, 0);
237         return;
238     }
239
240     for (p = resolv_conf; *p && n < AVAHI_WIDE_AREA_SERVERS_MAX; p++) {
241         if (!avahi_address_parse(*p, AVAHI_PROTO_UNSPEC, &a[n]))
242             avahi_log_warn("Failed to parse address '%s', ignoring.", *p);
243         else
244             n++;
245     }
246
247     avahi_server_set_wide_area_servers(avahi_server, a, n);
248 }
249
250 static void server_callback(AvahiServer *s, AvahiServerState state, void *userdata) {
251     DaemonConfig *c = userdata;
252     
253     assert(s);
254     assert(c);
255
256     /* This function is possibly called before the global variable
257      * avahi_server has been set, therefore we do it explicitly */
258
259     avahi_server = s;
260     
261 #ifdef HAVE_DBUS
262     if (c->enable_dbus && state != AVAHI_SERVER_INVALID && state != AVAHI_SERVER_FAILURE)
263         dbus_protocol_server_state_changed(state);
264 #endif
265
266     switch (state) {
267         case AVAHI_SERVER_RUNNING:
268             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));
269             
270             avahi_set_proc_title(argv0, "%s: running [%s]", argv0, avahi_server_get_host_name_fqdn(s));
271             
272             static_service_add_to_server();
273             static_hosts_add_to_server();
274             
275             remove_dns_server_entry_groups();
276             
277             if (c->publish_resolv_conf && resolv_conf && resolv_conf[0])
278                 resolv_conf_entry_group = add_dns_servers(s, resolv_conf_entry_group, resolv_conf);
279             
280             if (c->publish_dns_servers && c->publish_dns_servers[0])
281                 dns_servers_entry_group = add_dns_servers(s, dns_servers_entry_group, c->publish_dns_servers);
282             
283             simple_protocol_restart_queries();
284             break;
285             
286         case AVAHI_SERVER_COLLISION: {
287             char *n;
288             
289             avahi_set_proc_title(argv0, "%s: collision", argv0);
290             
291             static_service_remove_from_server();
292             static_hosts_remove_from_server();
293             remove_dns_server_entry_groups();
294
295             n = avahi_alternative_host_name(avahi_server_get_host_name(s));
296             avahi_log_warn("Host name conflict, retrying with <%s>", n);
297             avahi_server_set_host_name(s, n);
298             avahi_free(n);
299
300             break;
301         }
302
303         case AVAHI_SERVER_FAILURE:
304
305             avahi_log_error("Server error: %s", avahi_strerror(avahi_server_errno(s)));
306             avahi_simple_poll_quit(simple_poll_api);
307             break;
308
309         case AVAHI_SERVER_REGISTERING:
310
311             avahi_set_proc_title(argv0, "%s: registering [%s]", argv0, avahi_server_get_host_name_fqdn(s));
312             
313             static_service_remove_from_server();
314             static_hosts_remove_from_server();
315             remove_dns_server_entry_groups();
316             
317             break;
318
319         case AVAHI_SERVER_INVALID:
320             break;
321             
322     }
323 }
324
325 static void help(FILE *f) {
326     fprintf(f,
327             "%s [options]\n"
328             "    -h --help          Show this help\n"
329             "    -D --daemonize     Daemonize after startup (implies -s)\n"
330             "    -s --syslog        Write log messages to syslog(3) instead of STDERR\n"
331             "    -k --kill          Kill a running daemon\n"
332             "    -r --reload        Request a running daemon to reload static services\n"
333             "    -c --check         Return 0 if a daemon is already running\n"
334             "    -V --version       Show version\n"
335             "    -f --file=FILE     Load the specified configuration file instead of\n"
336             "                       "AVAHI_CONFIG_FILE"\n"
337             "       --no-rlimits    Don't enforce resource limits\n"
338             "       --no-drop-root  Don't drop privileges\n"
339 #ifdef ENABLE_CHROOT            
340             "       --no-chroot     Don't chroot()\n"
341 #endif            
342             "       --no-proc-title Don't modify process title\n"
343             "       --debug         Increase verbosity\n",
344             argv0);
345 }
346
347
348 static int parse_command_line(DaemonConfig *c, int argc, char *argv[]) {
349     int o;
350
351     enum {
352         OPTION_NO_RLIMITS = 256,
353         OPTION_NO_DROP_ROOT,
354 #ifdef ENABLE_CHROOT        
355         OPTION_NO_CHROOT,
356 #endif
357         OPTION_NO_PROC_TITLE,
358         OPTION_DEBUG
359     };
360     
361     static const struct option long_options[] = {
362         { "help",           no_argument,       NULL, 'h' },
363         { "daemonize",      no_argument,       NULL, 'D' },
364         { "kill",           no_argument,       NULL, 'k' },
365         { "version",        no_argument,       NULL, 'V' },
366         { "file",           required_argument, NULL, 'f' },
367         { "reload",         no_argument,       NULL, 'r' },
368         { "check",          no_argument,       NULL, 'c' },
369         { "syslog",         no_argument,       NULL, 's' },
370         { "no-rlimits",     no_argument,       NULL, OPTION_NO_RLIMITS },
371         { "no-drop-root",   no_argument,       NULL, OPTION_NO_DROP_ROOT },
372 #ifdef ENABLE_CHROOT
373         { "no-chroot",      no_argument,       NULL, OPTION_NO_CHROOT },
374 #endif
375         { "no-proc-title",  no_argument,       NULL, OPTION_NO_PROC_TITLE },
376         { "debug",          no_argument,       NULL, OPTION_DEBUG },
377         { NULL, 0, NULL, 0 }
378     };
379
380     assert(c);
381
382     while ((o = getopt_long(argc, argv, "hDkVf:rcs", long_options, NULL)) >= 0) {
383
384         switch(o) {
385             case 's':
386                 c->use_syslog = 1;
387                 break;
388             case 'h':
389                 c->command = DAEMON_HELP;
390                 break;
391             case 'D':
392                 c->daemonize = 1;
393                 break;
394             case 'k':
395                 c->command = DAEMON_KILL;
396                 break;
397             case 'V':
398                 c->command = DAEMON_VERSION;
399                 break;
400             case 'f':
401                 avahi_free(c->config_file);
402                 c->config_file = avahi_strdup(optarg);
403                 break;
404             case 'r':
405                 c->command = DAEMON_RELOAD;
406                 break;
407             case 'c':
408                 c->command = DAEMON_CHECK;
409                 break;
410             case OPTION_NO_RLIMITS:
411                 c->set_rlimits = 0;
412                 break;
413             case OPTION_NO_DROP_ROOT:
414                 c->drop_root = 0;
415                 break;
416 #ifdef ENABLE_CHROOT
417             case OPTION_NO_CHROOT:
418                 c->use_chroot = 0;
419                 break;
420 #endif
421             case OPTION_NO_PROC_TITLE:
422                 c->modify_proc_title = 0;
423                 break;
424             case OPTION_DEBUG:
425                 c->debug = 1;
426                 break;
427             default:
428                 return -1;
429         }
430     }
431
432     if (optind < argc) {
433         fprintf(stderr, "Too many arguments\n");
434         return -1;
435     }
436         
437     return 0;
438 }
439
440 static int is_yes(const char *s) {
441     assert(s);
442     
443     return *s == 'y' || *s == 'Y' || *s == '1' || *s == 't' || *s == 'T';
444 }
445
446 static int load_config_file(DaemonConfig *c) {
447     int r = -1;
448     AvahiIniFile *f;
449     AvahiIniFileGroup *g;
450
451     assert(c);
452
453     if (!(f = avahi_ini_file_load(c->config_file ? c->config_file : AVAHI_CONFIG_FILE)))
454         goto finish;
455     
456     for (g = f->groups; g; g = g->groups_next) {
457         
458         if (strcasecmp(g->name, "server") == 0) {
459             AvahiIniFilePair *p;
460
461             for (p = g->pairs; p; p = p->pairs_next) {
462
463                 if (strcasecmp(p->key, "host-name") == 0) {
464                     avahi_free(c->server_config.host_name);
465                     c->server_config.host_name = avahi_strdup(p->value);
466                 } else if (strcasecmp(p->key, "domain-name") == 0) {
467                     avahi_free(c->server_config.domain_name);
468                     c->server_config.domain_name = avahi_strdup(p->value);
469                 } else if (strcasecmp(p->key, "browse-domains") == 0) {
470                     char **e, **t;
471
472                     e = avahi_split_csv(p->value);
473                     
474                     for (t = e; *t; t++) {
475                         char cleaned[AVAHI_DOMAIN_NAME_MAX];
476
477                         if (!avahi_normalize_name(*t, cleaned, sizeof(cleaned))) {
478                             avahi_log_error("Invalid domain name \"%s\" for key \"%s\" in group \"%s\"\n", *t, p->key, g->name);
479                             avahi_strfreev(e);
480                             goto finish;
481                         }
482
483                         c->server_config.browse_domains = avahi_string_list_add(c->server_config.browse_domains, cleaned);
484                     }
485                     
486                     avahi_strfreev(e);
487                 } else if (strcasecmp(p->key, "use-ipv4") == 0)
488                     c->server_config.use_ipv4 = is_yes(p->value);
489                 else if (strcasecmp(p->key, "use-ipv6") == 0)
490                     c->server_config.use_ipv6 = is_yes(p->value);
491                 else if (strcasecmp(p->key, "check-response-ttl") == 0)
492                     c->server_config.check_response_ttl = is_yes(p->value);
493                 else if (strcasecmp(p->key, "allow-point-to-point") == 0)
494                     c->server_config.allow_point_to_point = is_yes(p->value);
495                 else if (strcasecmp(p->key, "use-iff-running") == 0)
496                     c->server_config.use_iff_running = is_yes(p->value);
497                 else if (strcasecmp(p->key, "disallow-other-stacks") == 0)
498                     c->server_config.disallow_other_stacks = is_yes(p->value);
499 #ifdef HAVE_DBUS
500                 else if (strcasecmp(p->key, "enable-dbus") == 0) {
501
502                     if (*(p->value) == 'w' || *(p->value) == 'W') {
503                         c->fail_on_missing_dbus = 0;
504                         c->enable_dbus = 1;
505                     } else if (*(p->value) == 'y' || *(p->value) == 'Y') {
506                         c->fail_on_missing_dbus = 1;
507                         c->enable_dbus = 1;
508                     } else {
509                         c->enable_dbus = 0;
510                     }
511                 }
512 #endif
513                 else {
514                     avahi_log_error("Invalid configuration key \"%s\" in group \"%s\"\n", p->key, g->name);
515                     goto finish;
516                 }
517             }
518             
519         } else if (strcasecmp(g->name, "publish") == 0) {
520             AvahiIniFilePair *p;
521
522             for (p = g->pairs; p; p = p->pairs_next) {
523                 
524                 if (strcasecmp(p->key, "publish-addresses") == 0)
525                     c->server_config.publish_addresses = is_yes(p->value);
526                 else if (strcasecmp(p->key, "publish-hinfo") == 0)
527                     c->server_config.publish_hinfo = is_yes(p->value);
528                 else if (strcasecmp(p->key, "publish-workstation") == 0)
529                     c->server_config.publish_workstation = is_yes(p->value);
530                 else if (strcasecmp(p->key, "publish-domain") == 0)
531                     c->server_config.publish_domain = is_yes(p->value);
532                 else if (strcasecmp(p->key, "publish-resolv-conf-dns-servers") == 0)
533                     c->publish_resolv_conf = is_yes(p->value);
534                 else if (strcasecmp(p->key, "disable-publishing") == 0)
535                     c->server_config.disable_publishing = is_yes(p->value);
536                 else if (strcasecmp(p->key, "disable-user-service-publishing") == 0)
537                     c->disable_user_service_publishing = is_yes(p->value);
538                 else if (strcasecmp(p->key, "add-service-cookie") == 0)
539                     c->server_config.add_service_cookie = is_yes(p->value);
540                 else if (strcasecmp(p->key, "publish-dns-servers") == 0) {
541                     avahi_strfreev(c->publish_dns_servers);
542                     c->publish_dns_servers = avahi_split_csv(p->value);
543                 } else if (strcasecmp(p->key, "publish-a-on-ipv6") == 0)
544                     c->server_config.publish_a_on_ipv6 = is_yes(p->value);
545                 else if (strcasecmp(p->key, "publish-aaaa-on-ipv4") == 0)
546                     c->server_config.publish_aaaa_on_ipv4 = is_yes(p->value);
547                 else {
548                     avahi_log_error("Invalid configuration key \"%s\" in group \"%s\"\n", p->key, g->name);
549                     goto finish;
550                 }
551             }
552
553         } else if (strcasecmp(g->name, "wide-area") == 0) {
554             AvahiIniFilePair *p;
555
556             for (p = g->pairs; p; p = p->pairs_next) {
557                 
558                 if (strcasecmp(p->key, "enable-wide-area") == 0)
559                     c->server_config.enable_wide_area = is_yes(p->value);
560                 else {
561                     avahi_log_error("Invalid configuration key \"%s\" in group \"%s\"\n", p->key, g->name);
562                     goto finish;
563                 }
564             }
565             
566         } else if (strcasecmp(g->name, "reflector") == 0) {
567             AvahiIniFilePair *p;
568
569             for (p = g->pairs; p; p = p->pairs_next) {
570                 
571                 if (strcasecmp(p->key, "enable-reflector") == 0)
572                     c->server_config.enable_reflector = is_yes(p->value);
573                 else if (strcasecmp(p->key, "reflect-ipv") == 0)
574                     c->server_config.reflect_ipv = is_yes(p->value);
575                 else {
576                     avahi_log_error("Invalid configuration key \"%s\" in group \"%s\"\n", p->key, g->name);
577                     goto finish;
578                 }
579             }
580             
581         } else if (strcasecmp(g->name, "rlimits") == 0) {
582             AvahiIniFilePair *p;
583
584             for (p = g->pairs; p; p = p->pairs_next) {
585                 
586                 if (strcasecmp(p->key, "rlimit-as") == 0) {
587                     c->rlimit_as_set = 1;
588                     c->rlimit_as = atoi(p->value);
589                 } else if (strcasecmp(p->key, "rlimit-core") == 0) {
590                     c->rlimit_core_set = 1;
591                     c->rlimit_core = atoi(p->value);
592                 } else if (strcasecmp(p->key, "rlimit-data") == 0) {
593                     c->rlimit_data_set = 1;
594                     c->rlimit_data = atoi(p->value);
595                 } else if (strcasecmp(p->key, "rlimit-fsize") == 0) {
596                     c->rlimit_fsize_set = 1;
597                     c->rlimit_fsize = atoi(p->value);
598                 } else if (strcasecmp(p->key, "rlimit-nofile") == 0) {
599                     c->rlimit_nofile_set = 1;
600                     c->rlimit_nofile = atoi(p->value);
601                 } else if (strcasecmp(p->key, "rlimit-stack") == 0) {
602                     c->rlimit_stack_set = 1;
603                     c->rlimit_stack = atoi(p->value);
604                 } else if (strcasecmp(p->key, "rlimit-nproc") == 0) {
605 #ifdef RLIMIT_NPROC
606                     c->rlimit_nproc_set = 1;
607                     c->rlimit_nproc = atoi(p->value);
608 #else
609                     avahi_log_error("Ignoring configuration key \"%s\" in group \"%s\"\n", p->key, g->name);
610 #endif
611                 } else {
612                     avahi_log_error("Invalid configuration key \"%s\" in group \"%s\"\n", p->key, g->name);
613                     goto finish;
614                 }
615
616             }
617             
618         } else {
619             avahi_log_error("Invalid configuration file group \"%s\".\n", g->name);
620             goto finish;
621         }
622     }
623
624     r = 0;
625
626 finish:
627
628     if (f)
629         avahi_ini_file_free(f);
630     
631     return r;
632 }
633
634 static void log_function(AvahiLogLevel level, const char *txt) {
635
636     static const int log_level_map[] = {
637         LOG_ERR,
638         LOG_WARNING,
639         LOG_NOTICE,
640         LOG_INFO,
641         LOG_DEBUG
642     };
643     
644     assert(level < AVAHI_LOG_LEVEL_MAX);
645     assert(txt);
646
647     if (!config.debug && level == AVAHI_LOG_DEBUG)
648         return;
649
650     daemon_log(log_level_map[level], "%s", txt);
651 }
652
653 static void dump(const char *text, AVAHI_GCC_UNUSED void* userdata) {
654     avahi_log_info("%s", text);
655 }
656
657 static void signal_callback(AvahiWatch *watch, AVAHI_GCC_UNUSED int fd, AVAHI_GCC_UNUSED AvahiWatchEvent event, AVAHI_GCC_UNUSED void *userdata) {
658     int sig;
659     const AvahiPoll *poll_api;
660     
661     assert(watch);
662     assert(simple_poll_api);
663
664     poll_api = avahi_simple_poll_get(simple_poll_api);
665
666     if ((sig = daemon_signal_next()) <= 0) {
667         avahi_log_error("daemon_signal_next() failed");
668         poll_api->watch_free(watch);
669         return;
670     }
671
672     switch (sig) {
673         case SIGINT:
674         case SIGQUIT:
675         case SIGTERM:
676             avahi_log_info(
677                 "Got %s, quitting.",
678                 sig == SIGINT ? "SIGINT" :
679                 (sig == SIGQUIT ? "SIGQUIT" : "SIGTERM"));
680             avahi_simple_poll_quit(simple_poll_api);
681             break;
682
683         case SIGHUP:
684             avahi_log_info("Got SIGHUP, reloading.");
685 #ifdef ENABLE_CHROOT
686             static_service_load(config.use_chroot);
687             static_hosts_load(config.use_chroot);
688 #else
689             static_service_load(0);
690             static_hosts_load(0);
691 #endif            
692             static_service_add_to_server();
693             static_hosts_add_to_server();
694
695             if (resolv_conf_entry_group)
696                 avahi_s_entry_group_reset(resolv_conf_entry_group);
697
698             load_resolv_conf();
699
700             update_wide_area_servers();
701             
702             if (config.publish_resolv_conf && resolv_conf && resolv_conf[0])
703                 resolv_conf_entry_group = add_dns_servers(avahi_server, resolv_conf_entry_group, resolv_conf);
704
705             break;
706
707         case SIGUSR1:
708             avahi_log_info("Got SIGUSR1, dumping record data.");
709             avahi_server_dump(avahi_server, dump, NULL);
710             break;
711
712         default:
713             avahi_log_warn("Got spurious signal, ignoring.");
714             break;
715     }
716 }
717
718 /* Imported from ../avahi-client/nss-check.c */
719 int avahi_nss_support(void);
720
721 static int run_server(DaemonConfig *c) {
722     int r = -1;
723     int error;
724     const AvahiPoll *poll_api = NULL;
725     AvahiWatch *sig_watch = NULL;
726     int retval_is_sent = 0;
727
728     assert(c);
729
730     if (!(nss_support = avahi_nss_support()))
731         avahi_log_warn("WARNING: No NSS support for mDNS detected, consider installing nss-mdns!");
732
733     if (!(simple_poll_api = avahi_simple_poll_new())) {
734         avahi_log_error("Failed to create main loop object.");
735         goto finish;
736     }
737
738     poll_api = avahi_simple_poll_get(simple_poll_api);
739
740     if (daemon_signal_init(SIGINT, SIGQUIT, SIGHUP, SIGTERM, SIGUSR1, 0) < 0) {
741         avahi_log_error("Could not register signal handlers (%s).", strerror(errno));
742         goto finish;
743     }
744
745     if (!(sig_watch = poll_api->watch_new(poll_api, daemon_signal_fd(), AVAHI_WATCH_IN, signal_callback, simple_poll_api))) {
746         avahi_log_error( "Failed to create signal watcher");
747         goto finish;
748     }
749
750     if (simple_protocol_setup(poll_api) < 0)
751         goto finish;
752
753 #ifdef HAVE_DBUS
754     if (c->enable_dbus) {
755         if (dbus_protocol_setup(poll_api, config.disable_user_service_publishing, !c->fail_on_missing_dbus
756 #ifdef ENABLE_CHROOT
757                                 && !config.use_chroot
758 #endif
759             ) < 0) {
760
761             avahi_log_warn("WARNING: Failed to contact D-Bus daemon.");
762
763             if (c->fail_on_missing_dbus)
764                 goto finish;
765         }
766     }
767 #endif
768
769 #ifdef ENABLE_CHROOT
770
771     if (config.drop_root && config.use_chroot) {
772         if (chroot(AVAHI_CONFIG_DIR) < 0) {
773             avahi_log_error("Failed to chroot(): %s", strerror(errno));
774             goto finish;
775         }
776         
777         avahi_log_info("Successfully called chroot().");
778         chdir("/");
779         
780         if (avahi_caps_drop_all() < 0) {
781             avahi_log_error("Failed to drop capabilities.");
782             goto finish;
783         }
784         avahi_log_info("Successfully dropped remaining capabilities.");
785     }
786     
787 #endif
788
789     load_resolv_conf();
790 #ifdef ENABLE_CHROOT
791     static_service_load(config.use_chroot);
792     static_hosts_load(config.use_chroot);
793 #else
794     static_service_load(0);
795     static_hosts_load(0);
796 #endif
797
798     if (!(avahi_server = avahi_server_new(poll_api, &c->server_config, server_callback, c, &error))) {
799         avahi_log_error("Failed to create server: %s", avahi_strerror(error));
800         goto finish;
801     }
802
803     update_wide_area_servers();
804
805     if (c->daemonize) {
806         daemon_retval_send(0);
807         retval_is_sent = 1;
808     }
809
810     for (;;) {
811         if ((r = avahi_simple_poll_iterate(simple_poll_api, -1)) < 0) {
812
813             /* We handle signals through an FD, so let's continue */
814             if (errno == EINTR)
815                 continue;
816             
817             avahi_log_error("poll(): %s", strerror(errno));
818             goto finish;
819         } else if (r > 0)
820             /* Quit */
821             break;
822     }
823     
824
825 finish:
826     
827     static_service_remove_from_server();
828     static_service_free_all();
829
830     static_hosts_remove_from_server();
831     static_hosts_free_all();
832
833     remove_dns_server_entry_groups();
834     
835     simple_protocol_shutdown();
836
837 #ifdef HAVE_DBUS
838     if (c->enable_dbus)
839         dbus_protocol_shutdown();
840 #endif
841
842     if (avahi_server) {
843         avahi_server_free(avahi_server);
844         avahi_server = NULL;
845     }
846
847     daemon_signal_done();
848
849     if (sig_watch)
850         poll_api->watch_free(sig_watch);
851
852     if (simple_poll_api) {
853         avahi_simple_poll_free(simple_poll_api);
854         simple_poll_api = NULL;
855     }
856
857     if (!retval_is_sent && c->daemonize)
858         daemon_retval_send(1);
859     
860     return r;
861 }
862
863 #define set_env(key, value) putenv(avahi_strdup_printf("%s=%s", (key), (value)))
864
865 static int drop_root(void) {
866     struct passwd *pw;
867     struct group * gr;
868     int r;
869     
870     if (!(pw = getpwnam(AVAHI_USER))) {
871         avahi_log_error( "Failed to find user '"AVAHI_USER"'.");
872         return -1;
873     }
874
875     if (!(gr = getgrnam(AVAHI_GROUP))) {
876         avahi_log_error( "Failed to find group '"AVAHI_GROUP"'.");
877         return -1;
878     }
879
880     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);
881
882     if (initgroups(AVAHI_USER, gr->gr_gid) != 0) {
883         avahi_log_error("Failed to change group list: %s", strerror(errno));
884         return -1;
885     }
886
887 #if defined(HAVE_SETRESGID)
888     r = setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid);
889 #elif defined(HAVE_SETEGID)
890     if ((r = setgid(gr->gr_gid)) >= 0)
891         r = setegid(gr->gr_gid);
892 #elif defined(HAVE_SETREGID)
893     r = setregid(gr->gr_gid, gr->gr_gid);
894 #else
895 #error "No API to drop priviliges"
896 #endif
897
898     if (r < 0) {
899         avahi_log_error("Failed to change GID: %s", strerror(errno));
900         return -1;
901     }
902
903 #if defined(HAVE_SETRESUID)
904     r = setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid);
905 #elif defined(HAVE_SETEUID)
906     if ((r = setuid(pw->pw_uid)) >= 0)
907         r = seteuid(pw->pw_uid);
908 #elif defined(HAVE_SETREUID)
909     r = setreuid(pw->pw_uid, pw->pw_uid);
910 #else
911 #error "No API to drop priviliges"
912 #endif
913
914     if (r < 0) {
915         avahi_log_error("Failed to change UID: %s", strerror(errno));
916         return -1;
917     }
918
919     set_env("USER", pw->pw_name);
920     set_env("LOGNAME", pw->pw_name);
921     set_env("HOME", pw->pw_dir);
922
923     avahi_log_info("Successfully dropped root privileges.");
924
925     return 0;
926 }
927
928 static const char* pid_file_proc(void) {
929     return AVAHI_DAEMON_RUNTIME_DIR"/pid";
930 }
931
932 static int make_runtime_dir(void) {
933     int r = -1;
934     mode_t u;
935     int reset_umask = 0;
936     struct passwd *pw;
937     struct group * gr;
938     struct stat st;
939
940     if (!(pw = getpwnam(AVAHI_USER))) {
941         avahi_log_error( "Failed to find user '"AVAHI_USER"'.");
942         goto fail;
943     }
944
945     if (!(gr = getgrnam(AVAHI_GROUP))) {
946         avahi_log_error( "Failed to find group '"AVAHI_GROUP"'.");
947         goto fail;
948     }
949
950     u = umask(0000);
951     reset_umask = 1;
952     
953     if (mkdir(AVAHI_DAEMON_RUNTIME_DIR, 0755) < 0 && errno != EEXIST) {
954         avahi_log_error("mkdir(\""AVAHI_DAEMON_RUNTIME_DIR"\"): %s", strerror(errno));
955         goto fail;
956     }
957     
958     chown(AVAHI_DAEMON_RUNTIME_DIR, pw->pw_uid, gr->gr_gid);
959
960     if (stat(AVAHI_DAEMON_RUNTIME_DIR, &st) < 0) {
961         avahi_log_error("stat(): %s\n", strerror(errno));
962         goto fail;
963     }
964
965     if (!S_ISDIR(st.st_mode) || st.st_uid != pw->pw_uid || st.st_gid != gr->gr_gid) {
966         avahi_log_error("Failed to create runtime directory "AVAHI_DAEMON_RUNTIME_DIR".");
967         goto fail;
968     }
969
970     r = 0;
971
972 fail:
973     if (reset_umask)
974         umask(u);
975     return r;
976 }
977
978 static void set_one_rlimit(int resource, rlim_t limit, const char *name) {
979     struct rlimit rl;
980     rl.rlim_cur = rl.rlim_max = limit;
981
982     if (setrlimit(resource, &rl) < 0)
983         avahi_log_warn("setrlimit(%s, {%u, %u}) failed: %s", name, (unsigned) limit, (unsigned) limit, strerror(errno));
984 }
985
986 static void enforce_rlimits(void) {
987 #ifdef RLIMIT_AS
988     if (config.rlimit_as_set)
989         set_one_rlimit(RLIMIT_AS, config.rlimit_as, "RLIMIT_AS");
990 #endif
991     if (config.rlimit_core_set)
992         set_one_rlimit(RLIMIT_CORE, config.rlimit_core, "RLIMIT_CORE");
993     if (config.rlimit_data_set)
994         set_one_rlimit(RLIMIT_DATA, config.rlimit_data, "RLIMIT_DATA");
995     if (config.rlimit_fsize_set)
996         set_one_rlimit(RLIMIT_FSIZE, config.rlimit_fsize, "RLIMIT_FSIZE");
997     if (config.rlimit_nofile_set)
998         set_one_rlimit(RLIMIT_NOFILE, config.rlimit_nofile, "RLIMIT_NOFILE");
999     if (config.rlimit_stack_set)
1000         set_one_rlimit(RLIMIT_STACK, config.rlimit_stack, "RLIMIT_STACK");
1001 #ifdef RLIMIT_NPROC
1002     if (config.rlimit_nproc_set)
1003         set_one_rlimit(RLIMIT_NPROC, config.rlimit_nproc, "RLIMIT_NPROC");
1004 #endif
1005
1006     /* the sysctl() call from iface-pfroute.c needs locked memory on FreeBSD */
1007 #if defined(RLIMIT_MEMLOCK) && !defined(__FreeBSD__)
1008     /* We don't need locked memory */
1009     set_one_rlimit(RLIMIT_MEMLOCK, 0, "RLIMIT_MEMLOCK");
1010 #endif
1011 }
1012
1013 #define RANDOM_DEVICE "/dev/urandom"
1014
1015 static void init_rand_seed(void) {
1016     int fd;
1017     unsigned seed = 0;
1018
1019     /* Try to initialize seed from /dev/urandom, to make it a little
1020      * less predictable, and to make sure that multiple machines
1021      * booted at the same time choose different random seeds.  */
1022     if ((fd = open(RANDOM_DEVICE, O_RDONLY)) >= 0) {
1023         read(fd, &seed, sizeof(seed));
1024         close(fd);
1025     }
1026
1027     /* If the initialization failed by some reason, we add the time to the seed*/
1028     seed ^= (unsigned) time(NULL);
1029
1030     srand(seed);
1031 }
1032
1033 int main(int argc, char *argv[]) {
1034     int r = 255;
1035     int wrote_pid_file = 0;
1036
1037     avahi_set_log_function(log_function);
1038
1039     init_rand_seed();
1040     
1041     avahi_server_config_init(&config.server_config);
1042     config.command = DAEMON_RUN;
1043     config.daemonize = 0;
1044     config.config_file = NULL;
1045 #ifdef HAVE_DBUS
1046     config.enable_dbus = 1;
1047     config.fail_on_missing_dbus = 1;
1048 #endif
1049     
1050     config.drop_root = 1;
1051     config.set_rlimits = 1;
1052 #ifdef ENABLE_CHROOT
1053     config.use_chroot = 1;
1054 #endif
1055     config.modify_proc_title = 1;
1056
1057     config.disable_user_service_publishing = 0;
1058     config.publish_dns_servers = NULL;
1059     config.publish_resolv_conf = 0;
1060     config.use_syslog = 0;
1061     config.debug = 0;
1062     config.rlimit_as_set = 0;
1063     config.rlimit_core_set = 0;
1064     config.rlimit_data_set = 0;
1065     config.rlimit_fsize_set = 0;
1066     config.rlimit_nofile_set = 0;
1067     config.rlimit_stack_set = 0;
1068 #ifdef RLIMIT_NPROC
1069     config.rlimit_nproc_set = 0;
1070 #endif
1071     
1072     if ((argv0 = strrchr(argv[0], '/')))
1073         argv0 = avahi_strdup(argv0 + 1);
1074     else
1075         argv0 = avahi_strdup(argv[0]);
1076
1077     daemon_pid_file_ident = (const char *) argv0;
1078     daemon_log_ident = (char*) argv0;
1079     daemon_pid_file_proc = pid_file_proc;
1080     
1081     if (parse_command_line(&config, argc, argv) < 0)
1082         goto finish;
1083
1084     if (config.modify_proc_title)
1085         avahi_init_proc_title(argc, argv);
1086
1087 #ifdef ENABLE_CHROOT
1088     config.use_chroot = config.use_chroot && config.drop_root;
1089 #endif
1090     
1091     if (config.command == DAEMON_HELP) {
1092         help(stdout);
1093         r = 0;
1094     } else if (config.command == DAEMON_VERSION) {
1095         printf("%s "PACKAGE_VERSION"\n", argv0);
1096         r = 0;
1097     } else if (config.command == DAEMON_KILL) {
1098         if (daemon_pid_file_kill_wait(SIGTERM, 5) < 0) {
1099             avahi_log_warn("Failed to kill daemon: %s", strerror(errno));
1100             goto finish;
1101         }
1102
1103         r = 0;
1104
1105     } else if (config.command == DAEMON_RELOAD) {
1106         if (daemon_pid_file_kill(SIGHUP) < 0) {
1107             avahi_log_warn("Failed to kill daemon: %s", strerror(errno));
1108             goto finish;
1109         }
1110
1111         r = 0;
1112         
1113     } else if (config.command == DAEMON_CHECK)
1114         r = (daemon_pid_file_is_running() >= 0) ? 0 : 1;
1115     else if (config.command == DAEMON_RUN) {
1116         pid_t pid;
1117
1118         if (getuid() != 0 && config.drop_root) {
1119             avahi_log_error("This program is intended to be run as root.");
1120             goto finish;
1121         }
1122         
1123         if ((pid = daemon_pid_file_is_running()) >= 0) {
1124             avahi_log_error("Daemon already running on PID %u", pid);
1125             goto finish;
1126         }
1127
1128         if (load_config_file(&config) < 0)
1129             goto finish;
1130         
1131         if (config.daemonize) {
1132             daemon_retval_init();
1133             
1134             if ((pid = daemon_fork()) < 0)
1135                 goto finish;
1136             else if (pid != 0) {
1137                 int ret;
1138                 /** Parent **/
1139
1140                 if ((ret = daemon_retval_wait(20)) < 0) {
1141                     avahi_log_error("Could not receive return value from daemon process.");
1142                     goto finish;
1143                 }
1144
1145                 r = ret;
1146                 goto finish;
1147             }
1148
1149             /* Child */
1150         }
1151
1152         if (config.use_syslog || config.daemonize)
1153             daemon_log_use = DAEMON_LOG_SYSLOG;
1154
1155         if (make_runtime_dir() < 0)
1156             goto finish;
1157
1158         if (config.drop_root) {
1159 #ifdef ENABLE_CHROOT
1160             if (config.use_chroot)
1161                 if (avahi_caps_reduce() < 0)
1162                     goto finish;
1163 #endif
1164             
1165             if (drop_root() < 0)
1166                 goto finish;
1167
1168 #ifdef ENABLE_CHROOT
1169             if (config.use_chroot)
1170                 if (avahi_caps_reduce2() < 0)
1171                     goto finish;
1172 #endif
1173         }
1174
1175         if (daemon_pid_file_create() < 0) {
1176             avahi_log_error("Failed to create PID file: %s", strerror(errno));
1177
1178             if (config.daemonize)
1179                 daemon_retval_send(1);
1180             goto finish;
1181         } else
1182             wrote_pid_file = 1;
1183
1184         if (config.set_rlimits)
1185             enforce_rlimits();
1186
1187         chdir("/");
1188
1189 #ifdef ENABLE_CHROOT
1190         if (config.drop_root && config.use_chroot)
1191             if (avahi_chroot_helper_start(argv0) < 0) {
1192                 avahi_log_error("failed to start chroot() helper daemon.");
1193                 goto finish;
1194             }
1195 #endif
1196         avahi_log_info("%s "PACKAGE_VERSION" starting up.", argv0);
1197
1198         avahi_set_proc_title(argv0, "%s: starting up", argv0);
1199         
1200         if (run_server(&config) == 0)
1201             r = 0;
1202     }
1203         
1204 finish:
1205
1206     if (config.daemonize)
1207         daemon_retval_done();
1208
1209     avahi_server_config_free(&config.server_config);
1210     avahi_free(config.config_file);
1211     avahi_strfreev(config.publish_dns_servers);
1212     avahi_strfreev(resolv_conf);
1213
1214     if (wrote_pid_file) {
1215 #ifdef ENABLE_CHROOT
1216         avahi_chroot_helper_unlink(pid_file_proc());
1217 #else
1218         daemon_pid_file_remove();
1219 #endif
1220     }
1221
1222 #ifdef ENABLE_CHROOT
1223     avahi_chroot_helper_shutdown();
1224 #endif
1225
1226     avahi_free(argv0);
1227     
1228     return r;
1229 }