]> git.meshlink.io Git - catta/blob - avahi-daemon/main.c
7d83c46858830eac293dbc5e782510e47087feec
[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 <getopt.h>
27 #include <string.h>
28 #include <signal.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <grp.h>
33 #include <pwd.h>
34 #include <sys/stat.h>
35 #include <stdio.h>
36
37 #include <libdaemon/dfork.h>
38 #include <libdaemon/dsignal.h>
39 #include <libdaemon/dlog.h>
40 #include <libdaemon/dpid.h>
41
42 #include <avahi-core/core.h>
43 #include <avahi-core/log.h>
44
45 #include "main.h"
46 #include "simple-protocol.h"
47 #include "dbus-protocol.h"
48 #include "static-services.h"
49
50 AvahiServer *avahi_server = NULL;
51
52 typedef enum {
53     DAEMON_RUN,
54     DAEMON_KILL,
55     DAEMON_VERSION,
56     DAEMON_HELP,
57     DAEMON_RELOAD,
58     DAEMON_CHECK
59 } DaemonCommand;
60
61 typedef struct {
62     AvahiServerConfig server_config;
63     DaemonCommand command;
64     gboolean daemonize;
65     gchar *config_file;
66     gboolean enable_dbus;
67     gboolean drop_root;
68     gboolean publish_resolv_conf;
69     gchar ** publish_dns_servers;
70 } DaemonConfig;
71
72 #define RESOLV_CONF "/etc/resolv.conf"
73
74 static AvahiEntryGroup *dns_servers_entry_group = NULL;
75 static AvahiEntryGroup *resolv_conf_entry_group = NULL;
76
77 static gchar **resolv_conf = NULL;
78
79 static DaemonConfig config;
80
81 #define MAX_NAME_SERVERS 10
82
83 static gint load_resolv_conf(const DaemonConfig *c) {
84     gint ret = -1;
85     FILE *f;
86     gint i = 0;
87     
88     g_strfreev(resolv_conf);
89     resolv_conf = NULL;
90
91     if (!c->publish_resolv_conf)
92         return 0;
93
94     if (!(f = fopen(RESOLV_CONF, "r"))) {
95         avahi_log_warn("Failed to open "RESOLV_CONF".");
96         goto finish;
97     }
98
99     resolv_conf = g_new0(gchar*, MAX_NAME_SERVERS+1);
100
101     while (!feof(f) && i < MAX_NAME_SERVERS) {
102         char ln[128];
103         gchar *p;
104
105         if (!(fgets(ln, sizeof(ln), f)))
106             break;
107
108         ln[strcspn(ln, "\r\n#")] = 0;
109         p = ln + strspn(ln, "\t ");
110
111         if (g_str_has_prefix(p, "nameserver")) {
112             p += 10;
113             p += strspn(p, "\t ");
114             p[strcspn(p, "\t ")] = 0;
115             resolv_conf[i++] = strdup(p);
116         }
117     }
118
119     ret = 0;
120
121 finish:
122
123     if (ret != 0) {
124         g_strfreev(resolv_conf);
125         resolv_conf = NULL;
126     }
127         
128     if (f)
129         fclose(f);
130
131     return ret;
132 }
133
134 static AvahiEntryGroup* add_dns_servers(AvahiServer *s, gchar **l) {
135     gchar **p;
136     AvahiEntryGroup *g;
137
138     g_assert(s);
139     g_assert(l);
140
141     g = avahi_entry_group_new(s, NULL, NULL);
142
143     for (p = l; *p; p++) {
144         AvahiAddress a;
145         
146         if (!avahi_address_parse(*p, AF_UNSPEC, &a))
147             avahi_log_warn("Failed to parse address '%s', ignoring.", *p);
148         else
149             if (avahi_server_add_dns_server_address(s, g, -1, AF_UNSPEC, NULL, AVAHI_DNS_SERVER_RESOLVE, &a, 53) < 0) {
150                 avahi_entry_group_free(g);
151                 return NULL;
152             }
153     }
154
155     avahi_entry_group_commit(g);
156
157     return g;
158     
159 }
160
161 static void remove_dns_server_entry_groups(void) {
162     if (resolv_conf_entry_group) {
163         avahi_entry_group_free(resolv_conf_entry_group);
164         resolv_conf_entry_group = NULL;
165     }
166     
167     if (dns_servers_entry_group) {
168         avahi_entry_group_free(dns_servers_entry_group);
169         dns_servers_entry_group = NULL;
170     }
171 }
172
173 static void server_callback(AvahiServer *s, AvahiServerState state, gpointer userdata) {
174     DaemonConfig *c = userdata;
175     
176     g_assert(s);
177     g_assert(c);
178
179 #ifdef ENABLE_DBUS
180     if (c->enable_dbus)
181         dbus_protocol_server_state_changed(state);
182 #endif
183
184     if (state == AVAHI_SERVER_RUNNING) {
185         avahi_log_info("Server startup complete.  Host name is <%s>", avahi_server_get_host_name_fqdn(s));
186         static_service_add_to_server();
187
188         remove_dns_server_entry_groups();
189
190         if (resolv_conf && resolv_conf[0])
191             resolv_conf_entry_group = add_dns_servers(s, resolv_conf);
192
193         if (c->publish_dns_servers && c->publish_dns_servers[0])
194             dns_servers_entry_group = add_dns_servers(s, c->publish_dns_servers);
195
196         simple_protocol_restart_queries();
197         
198     } else if (state == AVAHI_SERVER_COLLISION) {
199         gchar *n;
200
201         static_service_remove_from_server();
202
203         remove_dns_server_entry_groups();
204
205         n = avahi_alternative_host_name(avahi_server_get_host_name(s));
206         avahi_log_warn("Host name conflict, retrying with <%s>", n);
207         avahi_server_set_host_name(s, n);
208         g_free(n);
209     }
210
211     
212 }
213
214 static void help(FILE *f, const gchar *argv0) {
215     fprintf(f,
216             "%s [options]\n"
217             "    -h --help        Show this help\n"
218             "    -D --daemonize   Daemonize after startup\n"
219             "    -k --kill        Kill a running daemon\n"
220             "    -r --reload      Request a running daemon to reload static services\n"
221             "    -c --check       Return 0 if a daemon is already running\n"
222             "    -V --version     Show version\n"
223             "    -f --file=FILE   Load the specified configuration file instead of\n"
224             "                     "AVAHI_CONFIG_FILE"\n",
225             argv0);
226 }
227
228 static gint parse_command_line(DaemonConfig *c, int argc, char *argv[]) {
229     gint o;
230     
231     static const struct option const long_options[] = {
232         { "help",      no_argument,       NULL, 'h' },
233         { "daemonize", no_argument,       NULL, 'D' },
234         { "kill",      no_argument,       NULL, 'k' },
235         { "version",   no_argument,       NULL, 'V' },
236         { "file",      required_argument, NULL, 'f' },
237         { "reload",    no_argument,       NULL, 'r' },
238         { "check",     no_argument,       NULL, 'c' },
239     };
240
241     g_assert(c);
242
243     opterr = 0;
244     while ((o = getopt_long(argc, argv, "hDkVf:rc", long_options, NULL)) >= 0) {
245
246         switch(o) {
247             case 'h':
248                 c->command = DAEMON_HELP;
249                 break;
250             case 'D':
251                 c->daemonize = TRUE;
252                 break;
253             case 'k':
254                 c->command = DAEMON_KILL;
255                 break;
256             case 'V':
257                 c->command = DAEMON_VERSION;
258                 break;
259             case 'f':
260                 g_free(c->config_file);
261                 c->config_file = g_strdup(optarg);
262                 break;
263             case 'r':
264                 c->command = DAEMON_RELOAD;
265                 break;
266             case 'c':
267                 c->command = DAEMON_CHECK;
268                 break;
269             default:
270                 fprintf(stderr, "Invalid command line argument: %c\n", o);
271                 return -1;
272         }
273     }
274
275     if (optind < argc) {
276         fprintf(stderr, "Too many arguments\n");
277         return -1;
278     }
279         
280     return 0;
281 }
282
283 static gboolean is_yes(const gchar *s) {
284     g_assert(s);
285     
286     return *s == 'y' || *s == 'Y';
287 }
288
289 static gint load_config_file(DaemonConfig *c) {
290     int r = -1;
291     GKeyFile *f = NULL;
292     GError *err = NULL;
293     gchar **groups = NULL, **g, **keys = NULL, *v = NULL;
294
295     g_assert(c);
296     
297     f = g_key_file_new();
298     g_key_file_set_list_separator(f, ',');
299     
300     if (!g_key_file_load_from_file(f, c->config_file ? c->config_file : AVAHI_CONFIG_FILE, G_KEY_FILE_NONE, &err)) {
301         fprintf(stderr, "Unable to read config file: %s\n", err->message);
302         goto finish;
303     }
304
305     groups = g_key_file_get_groups(f, NULL);
306
307     for (g = groups; *g; g++) {
308         if (g_strcasecmp(*g, "server") == 0) {
309             gchar **k;
310             
311             keys = g_key_file_get_keys(f, *g, NULL, NULL);
312
313             for (k = keys; *k; k++) {
314
315                 v = g_key_file_get_value(f, *g, *k, NULL);
316                 
317                 if (g_strcasecmp(*k, "host-name") == 0) {
318                     g_free(c->server_config.host_name);
319                     c->server_config.host_name = v;
320                     v = NULL;
321                 } else if (g_strcasecmp(*k, "domain-name") == 0) {
322                     g_free(c->server_config.domain_name);
323                     c->server_config.domain_name = v;
324                     v = NULL;
325                 } else if (g_strcasecmp(*k, "use-ipv4") == 0)
326                     c->server_config.use_ipv4 = is_yes(v);
327                 else if (g_strcasecmp(*k, "use-ipv6") == 0)
328                     c->server_config.use_ipv6 = is_yes(v);
329                 else if (g_strcasecmp(*k, "check-response-ttl") == 0)
330                     c->server_config.check_response_ttl = is_yes(v);
331                 else if (g_strcasecmp(*k, "use-iff-running") == 0)
332                     c->server_config.use_iff_running = is_yes(v);
333                 else if (g_strcasecmp(*k, "enable-dbus") == 0)
334                     c->enable_dbus = is_yes(v);
335                 else if (g_strcasecmp(*k, "drop-root") == 0)
336                     c->drop_root = is_yes(v);
337                 else {
338                     fprintf(stderr, "Invalid configuration key \"%s\" in group \"%s\"\n", *k, *g);
339                     goto finish;
340                 }
341
342                 g_free(v);
343                 v = NULL;
344             }
345         
346             g_strfreev(keys);
347             keys = NULL;
348             
349         } else if (g_strcasecmp(*g, "publish") == 0) {
350             gchar **k;
351             
352             keys = g_key_file_get_keys(f, *g, NULL, NULL);
353
354             for (k = keys; *k; k++) {
355
356                 v = g_key_file_get_string(f, *g, *k, NULL);
357                 
358                 if (g_strcasecmp(*k, "publish-addresses") == 0)
359                     c->server_config.publish_addresses = is_yes(v);
360                 else if (g_strcasecmp(*k, "publish-hinfo") == 0)
361                     c->server_config.publish_hinfo = is_yes(v);
362                 else if (g_strcasecmp(*k, "publish-workstation") == 0)
363                     c->server_config.publish_workstation = is_yes(v);
364                 else if (g_strcasecmp(*k, "publish-domain") == 0)
365                     c->server_config.publish_domain = is_yes(v);
366                 else if (g_strcasecmp(*k, "publish-resolv-conf-dns-servers") == 0)
367                     c->publish_resolv_conf = is_yes(v);
368                 else if (g_strcasecmp(*k, "publish-dns-servers") == 0) {
369                     g_strfreev(c->publish_dns_servers);
370                     c->publish_dns_servers = g_key_file_get_string_list(f, *g, *k, NULL, NULL);
371                 } else {
372                     fprintf(stderr, "Invalid configuration key \"%s\" in group \"%s\"\n", *k, *g);
373                     goto finish;
374                 }
375
376                 g_free(v);
377                 v = NULL;
378             }
379
380             g_strfreev(keys);
381             keys = NULL;
382
383         } else if (g_strcasecmp(*g, "reflector") == 0) {
384             gchar **k;
385             
386             keys = g_key_file_get_keys(f, *g, NULL, NULL);
387
388             for (k = keys; *k; k++) {
389
390                 v = g_key_file_get_string(f, *g, *k, NULL);
391                 
392                 if (g_strcasecmp(*k, "enable-reflector") == 0)
393                     c->server_config.enable_reflector = is_yes(v);
394                 else if (g_strcasecmp(*k, "reflect-ipv") == 0)
395                     c->server_config.reflect_ipv = is_yes(v);
396                 else {
397                     fprintf(stderr, "Invalid configuration key \"%s\" in group \"%s\"\n", *k, *g);
398                     goto finish;
399                 }
400
401                 g_free(v);
402                 v = NULL;
403             }
404     
405             g_strfreev(keys);
406             keys = NULL;
407             
408         } else {
409             fprintf(stderr, "Invalid configuration file group \"%s\".\n", *g);
410             goto finish;
411         }
412     }
413
414     r = 0;
415
416 finish:
417
418     g_strfreev(groups);
419     g_strfreev(keys);
420     g_free(v);
421
422     if (err)
423         g_error_free (err);
424
425     if (f)
426         g_key_file_free(f);
427     
428     return r;
429 }
430
431 static void log_function(AvahiLogLevel level, const gchar *txt) {
432
433     static const int const log_level_map[] = {
434         LOG_ERR,
435         LOG_WARNING,
436         LOG_NOTICE,
437         LOG_INFO,
438         LOG_DEBUG
439     };
440     
441     g_assert(level < AVAHI_LOG_LEVEL_MAX);
442     g_assert(txt);
443
444     daemon_log(log_level_map[level], "%s", txt);
445 }
446
447 static void dump(const gchar *text, gpointer userdata) {
448     avahi_log_info("%s", text);
449 }
450
451 static gboolean signal_callback(GIOChannel *source, GIOCondition condition, gpointer data) {
452     gint sig;
453     GMainLoop *loop = data;
454     
455     g_assert(source);
456     g_assert(loop);
457
458     if ((sig = daemon_signal_next()) <= 0) {
459         avahi_log_error("daemon_signal_next() failed");
460         return FALSE;
461     }
462
463     switch (sig) {
464         case SIGINT:
465         case SIGQUIT:
466         case SIGTERM:
467             avahi_log_info(
468                 "Got %s, quitting.",
469                 sig == SIGINT ? "SIGINT" :
470                 (sig == SIGQUIT ? "SIGQUIT" : "SIGTERM"));
471             g_main_loop_quit(loop);
472             break;
473
474         case SIGHUP:
475             avahi_log_info("Got SIGHUP, reloading.");
476             static_service_load();
477             static_service_add_to_server();
478
479             if (resolv_conf_entry_group) {
480                 avahi_entry_group_free(resolv_conf_entry_group);
481                 resolv_conf_entry_group = NULL;
482             }
483
484             load_resolv_conf(&config);
485             
486             if (resolv_conf && resolv_conf[0])
487                 resolv_conf_entry_group = add_dns_servers(avahi_server, resolv_conf);
488
489             break;
490
491         case SIGUSR1:
492             avahi_log_info("Got SIGUSR1, dumping record data.");
493             avahi_server_dump(avahi_server, dump, NULL);
494             break;
495
496         default:
497             avahi_log_warn("Got spurious signal, ignoring.");
498             break;
499     }
500
501     return TRUE;
502 }
503
504 static gint run_server(DaemonConfig *c) {
505     GMainLoop *loop = NULL;
506     gint r = -1;
507     GIOChannel *io = NULL;
508     guint watch_id = (guint) -1;
509
510     g_assert(c);
511     
512     loop = g_main_loop_new(NULL, FALSE);
513
514     if (daemon_signal_init(SIGINT, SIGQUIT, SIGHUP, SIGTERM, SIGUSR1, 0) < 0) {
515         avahi_log_error("Could not register signal handlers (%s).", strerror(errno));
516         goto finish;
517     }
518
519     if (!(io = g_io_channel_unix_new(daemon_signal_fd()))) {
520         avahi_log_error( "Failed to create signal io channel.");
521         goto finish;
522     }
523
524     g_io_channel_set_close_on_unref(io, FALSE);
525     g_io_add_watch(io, G_IO_IN, signal_callback, loop);
526     
527     if (simple_protocol_setup(NULL) < 0)
528         goto finish;
529     
530 #ifdef ENABLE_DBUS
531     if (c->enable_dbus)
532         if (dbus_protocol_setup(loop) < 0)
533             goto finish;
534 #endif
535     
536     if (!(avahi_server = avahi_server_new(NULL, &c->server_config, server_callback, c)))
537         goto finish;
538
539     load_resolv_conf(c);
540     
541     static_service_load();
542
543     if (c->daemonize) {
544         daemon_retval_send(0);
545         r = 0;
546     }
547
548     g_main_loop_run(loop);
549
550 finish:
551     
552     static_service_remove_from_server();
553     static_service_free_all();
554     remove_dns_server_entry_groups();
555     
556     simple_protocol_shutdown();
557
558 #ifdef ENABLE_DBUS
559     if (c->enable_dbus)
560         dbus_protocol_shutdown();
561 #endif
562
563
564     if (avahi_server)
565         avahi_server_free(avahi_server);
566
567     daemon_signal_done();
568
569     if (watch_id != (guint) -1)
570         g_source_remove(watch_id);
571     
572     if (io)
573         g_io_channel_unref(io);
574
575         
576     if (loop)
577         g_main_loop_unref(loop);
578
579     if (r != 0 && c->daemonize)
580         daemon_retval_send(1);
581     
582     return r;
583 }
584
585 static gint drop_root(void) {
586     struct passwd *pw;
587     struct group * gr;
588     gint r;
589     
590     if (!(pw = getpwnam(AVAHI_USER))) {
591         avahi_log_error( "Failed to find user '"AVAHI_USER"'.");
592         return -1;
593     }
594
595     if (!(gr = getgrnam(AVAHI_GROUP))) {
596         avahi_log_error( "Failed to find group '"AVAHI_GROUP"'.");
597         return -1;
598     }
599
600     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);
601
602     if (initgroups(AVAHI_USER, gr->gr_gid) != 0) {
603         avahi_log_error("Failed to change group list: %s", strerror(errno));
604         return -1;
605     }
606
607 #if defined(HAVE_SETRESGID)
608     r = setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid);
609 #elif defined(HAVE_SETREGID)
610     r = setregid(gr->gr_gid, gr->gr_gid);
611 #else
612     if ((r = setgid(gr->gr_gid)) >= 0)
613         r = setegid(gr->gr_gid);
614 #endif
615
616     if (r < 0) {
617         avahi_log_error("Failed to change GID: %s", strerror(errno));
618         return -1;
619     }
620
621 #if defined(HAVE_SETRESUID)
622     r = setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid);
623 #elif defined(HAVE_SETREUID)
624     r = setreuid(pw->pw_uid, pw->pw_uid);
625 #else
626     if ((r = setuid(pw->pw_uid)) >= 0)
627         r = seteuid(pw->pw_uid);
628 #endif
629
630     if (r < 0) {
631         avahi_log_error("Failed to change UID: %s", strerror(errno));
632         return -1;
633     }
634
635     g_setenv("USER", pw->pw_name, 1);
636     g_setenv("LOGNAME", pw->pw_name, 1);
637     g_setenv("HOME", pw->pw_dir, 1);
638     
639     avahi_log_info("Successfully dropped root privileges.");
640
641     return 0;
642 }
643
644 static const char* pid_file_proc(void) {
645     return AVAHI_DAEMON_RUNTIME_DIR"/pid";
646 }
647
648 static gint make_runtime_dir(void) {
649     gint r = -1;
650     mode_t u;
651     gboolean reset_umask = FALSE;
652     struct passwd *pw;
653     struct group * gr;
654     struct stat st;
655
656     if (!(pw = getpwnam(AVAHI_USER))) {
657         avahi_log_error( "Failed to find user '"AVAHI_USER"'.");
658         goto fail;
659     }
660
661     if (!(gr = getgrnam(AVAHI_GROUP))) {
662         avahi_log_error( "Failed to find group '"AVAHI_GROUP"'.");
663         goto fail;
664     }
665
666     u = umask(0000);
667     reset_umask = TRUE;
668     
669     if (mkdir(AVAHI_DAEMON_RUNTIME_DIR, 0755) < 0 && errno != EEXIST) {
670         avahi_log_error("mkdir(\""AVAHI_DAEMON_RUNTIME_DIR"\"): %s", strerror(errno));
671         goto fail;
672     }
673     
674     chown(AVAHI_DAEMON_RUNTIME_DIR, pw->pw_uid, gr->gr_gid);
675
676     if (stat(AVAHI_DAEMON_RUNTIME_DIR, &st) < 0) {
677         avahi_log_error("stat(): %s\n", strerror(errno));
678         goto fail;
679     }
680
681     if (!S_ISDIR(st.st_mode) || st.st_uid != pw->pw_uid || st.st_gid != gr->gr_gid) {
682         avahi_log_error("Failed to create runtime directory "AVAHI_DAEMON_RUNTIME_DIR".");
683         goto fail;
684     }
685
686     r = 0;
687
688 fail:
689     if (reset_umask)
690         umask(u);
691     return r;
692
693 }
694
695 int main(int argc, char *argv[]) {
696     gint r = 255;
697     const gchar *argv0;
698     gboolean wrote_pid_file = FALSE;
699
700     avahi_set_log_function(log_function);
701     
702     avahi_server_config_init(&config.server_config);
703     config.command = DAEMON_RUN;
704     config.daemonize = FALSE;
705     config.config_file = NULL;
706     config.enable_dbus = TRUE;
707     config.drop_root = TRUE;
708     config.publish_dns_servers = NULL;
709     config.publish_resolv_conf = FALSE;
710
711     if ((argv0 = strrchr(argv[0], '/')))
712         argv0++;
713     else
714         argv0 = argv[0];
715
716     daemon_pid_file_ident = (const char *) argv0;
717     daemon_log_ident = (char*) argv0;
718     daemon_pid_file_proc = pid_file_proc;
719     
720     if (parse_command_line(&config, argc, argv) < 0)
721         goto finish;
722
723     if (config.command == DAEMON_HELP) {
724         help(stdout, argv0);
725         r = 0;
726     } else if (config.command == DAEMON_VERSION) {
727         printf("%s "PACKAGE_VERSION"\n", argv0);
728         r = 0;
729     } else if (config.command == DAEMON_KILL) {
730         if (daemon_pid_file_kill_wait(SIGTERM, 5) < 0) {
731             avahi_log_warn("Failed to kill daemon: %s", strerror(errno));
732             goto finish;
733         }
734
735         r = 0;
736
737     } else if (config.command == DAEMON_RELOAD) {
738         if (daemon_pid_file_kill(SIGHUP) < 0) {
739             avahi_log_warn("Failed to kill daemon: %s", strerror(errno));
740             goto finish;
741         }
742
743         r = 0;
744         
745     } else if (config.command == DAEMON_CHECK)
746         r = (daemon_pid_file_is_running() >= 0) ? 0 : 1;
747     else if (config.command == DAEMON_RUN) {
748         pid_t pid;
749
750         if (getuid() != 0) {
751             avahi_log_error("This program is intended to be run as root.");
752             goto finish;
753         }
754         
755         if ((pid = daemon_pid_file_is_running()) >= 0) {
756             avahi_log_error("Daemon already running on PID %u", pid);
757             goto finish;
758         }
759
760         if (load_config_file(&config) < 0)
761             goto finish;
762         
763         if (config.daemonize) {
764             daemon_retval_init();
765             
766             if ((pid = daemon_fork()) < 0)
767                 goto finish;
768             else if (pid != 0) {
769                 int ret;
770                 /** Parent **/
771
772                 if ((ret = daemon_retval_wait(20)) < 0) {
773                     avahi_log_error("Could not recieve return value from daemon process.");
774                     goto finish;
775                 }
776
777                 r = ret;
778                 goto finish;
779             }
780
781             /* Child */
782         }
783
784
785         printf("%s "PACKAGE_VERSION" starting up.\n", argv0);
786         
787         chdir("/");
788
789         if (make_runtime_dir() < 0)
790             goto finish;
791
792         if (config.drop_root) {
793             if (drop_root() < 0)
794                 goto finish;
795         }
796
797         if (daemon_pid_file_create() < 0) {
798             avahi_log_error("Failed to create PID file: %s", strerror(errno));
799
800             if (config.daemonize)
801                 daemon_retval_send(1);
802             goto finish;
803         } else
804             wrote_pid_file = TRUE;
805
806         if (run_server(&config) == 0)
807             r = 0;
808     }
809         
810 finish:
811
812     if (config.daemonize)
813         daemon_retval_done();
814
815     avahi_server_config_free(&config.server_config);
816     g_free(config.config_file);
817     g_strfreev(config.publish_dns_servers);
818     g_strfreev(resolv_conf);
819
820     if (wrote_pid_file)
821         daemon_pid_file_remove();
822     
823     return r;
824 }