]> git.meshlink.io Git - catta/blob - avahi-daemon/main.c
* detect other running mDNS stacks
[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
33 #include <libdaemon/dfork.h>
34 #include <libdaemon/dsignal.h>
35 #include <libdaemon/dlog.h>
36 #include <libdaemon/dpid.h>
37
38 #include <avahi-core/core.h>
39 #include <avahi-core/log.h>
40
41 #include "main.h"
42 #include "simple-protocol.h"
43 #include "dbus-protocol.h"
44 #include "static-services.h"
45
46 AvahiServer *avahi_server = NULL;
47
48 typedef enum {
49     DAEMON_RUN,
50     DAEMON_KILL,
51     DAEMON_VERSION,
52     DAEMON_HELP,
53     DAEMON_RELOAD
54 } DaemonCommand;
55
56 typedef struct {
57     AvahiServerConfig server_config;
58     DaemonCommand command;
59     gboolean daemonize;
60     gchar *config_file;
61     gboolean enable_dbus;
62 } DaemonConfig;
63
64 static void server_callback(AvahiServer *s, AvahiServerState state, gpointer userdata) {
65     g_assert(s);
66
67     if (state == AVAHI_SERVER_RUNNING) {
68         avahi_log_info("Server startup complete.  Host name is <%s>", avahi_server_get_host_name_fqdn(s));
69         static_service_add_to_server();
70     } else if (state == AVAHI_SERVER_COLLISION) {
71         gchar *n;
72
73         static_service_remove_from_server();
74         
75         n = avahi_alternative_host_name(avahi_server_get_host_name(s));
76         avahi_log_warn("Host name conflict, retrying with <%s>", n);
77         avahi_server_set_host_name(s, n);
78         g_free(n);
79     }
80 }
81
82 static void help(FILE *f, const gchar *argv0) {
83     fprintf(f,
84             "%s [options]\n"
85             "    -h --help        Show this help\n"
86             "    -D --daemonize   Daemonize after startup\n"
87             "    -k --kill        Kill a running daemon\n"
88             "    -r --reload      Request a running daemon to reload static services\n"
89             "    -V --version     Show version\n"
90             "    -f --file=FILE   Load the specified configuration file instead of\n"
91             "                     "AVAHI_CONFIG_FILE"\n",
92             argv0);
93 }
94
95 static gint parse_command_line(DaemonConfig *config, int argc, char *argv[]) {
96     gint c;
97     
98     static const struct option const long_options[] = {
99         { "help",      no_argument,       NULL, 'h' },
100         { "daemonize", no_argument,       NULL, 'D' },
101         { "kill",      no_argument,       NULL, 'k' },
102         { "version",   no_argument,       NULL, 'V' },
103         { "file",      required_argument, NULL, 'f' },
104         { "reload",    no_argument,       NULL, 'r' },
105     };
106
107     g_assert(config);
108
109     opterr = 0;
110     while ((c = getopt_long(argc, argv, "hDkVf:r", long_options, NULL)) >= 0) {
111
112         switch(c) {
113             case 'h':
114                 config->command = DAEMON_HELP;
115                 break;
116             case 'D':
117                 config->daemonize = TRUE;
118                 break;
119             case 'k':
120                 config->command = DAEMON_KILL;
121                 break;
122             case 'V':
123                 config->command = DAEMON_VERSION;
124                 break;
125             case 'f':
126                 g_free(config->config_file);
127                 config->config_file = g_strdup(optarg);
128                 break;
129             case 'r':
130                 config->command = DAEMON_RELOAD;
131                 break;
132             default:
133                 fprintf(stderr, "Invalid command line argument: %c\n", c);
134                 return -1;
135         }
136     }
137
138     if (optind < argc) {
139         fprintf(stderr, "Too many arguments\n");
140         return -1;
141     }
142         
143     return 0;
144 }
145
146 static gboolean is_yes(const gchar *s) {
147     g_assert(s);
148     
149     return *s == 'y' || *s == 'Y';
150 }
151
152 static gint load_config_file(DaemonConfig *config) {
153     int r = -1;
154     GKeyFile *f = NULL;
155     GError *err = NULL;
156     gchar **groups = NULL, **g, **keys = NULL, *v = NULL;
157
158     g_assert(config);
159     
160     f = g_key_file_new();
161     
162     if (!g_key_file_load_from_file(f, config->config_file ? config->config_file : AVAHI_CONFIG_FILE, G_KEY_FILE_NONE, &err)) {
163         fprintf(stderr, "Unable to read config file: %s\n", err->message);
164         goto finish;
165     }
166
167     groups = g_key_file_get_groups(f, NULL);
168
169     for (g = groups; *g; g++) {
170         if (g_strcasecmp(*g, "server") == 0) {
171             gchar **k;
172             
173             keys = g_key_file_get_keys(f, *g, NULL, NULL);
174
175             for (k = keys; *k; k++) {
176
177                 v = g_key_file_get_value(f, *g, *k, NULL);
178                 
179                 if (g_strcasecmp(*k, "host-name") == 0) {
180                     g_free(config->server_config.host_name);
181                     config->server_config.host_name = v;
182                     v = NULL;
183                 } else if (g_strcasecmp(*k, "domain-name") == 0) {
184                     g_free(config->server_config.domain_name);
185                     config->server_config.domain_name = v;
186                     v = NULL;
187                 } else if (g_strcasecmp(*k, "use-ipv4") == 0)
188                     config->server_config.use_ipv4 = is_yes(v);
189                 else if (g_strcasecmp(*k, "use-ipv6") == 0)
190                     config->server_config.use_ipv6 = is_yes(v);
191                 else if (g_strcasecmp(*k, "check-response-ttl") == 0)
192                     config->server_config.check_response_ttl = is_yes(v);
193                 else if (g_strcasecmp(*k, "use-iff-running") == 0)
194                     config->server_config.use_iff_running = is_yes(v);
195                 else if (g_strcasecmp(*k, "enable-dbus") == 0)
196                     config->enable_dbus = is_yes(v);
197                 else {
198                     fprintf(stderr, "Invalid configuration key \"%s\" in group \"%s\"\n", *k, *g);
199                     goto finish;
200                 }
201
202                 g_free(v);
203                 v = NULL;
204             }
205         
206             g_strfreev(keys);
207             keys = NULL;
208             
209         } else if (g_strcasecmp(*g, "publish") == 0) {
210             gchar **k;
211             
212             keys = g_key_file_get_keys(f, *g, NULL, NULL);
213
214             for (k = keys; *k; k++) {
215
216                 v = g_key_file_get_string(f, *g, *k, NULL);
217                 
218                 if (g_strcasecmp(*k, "publish-addresses") == 0)
219                     config->server_config.publish_addresses = is_yes(v);
220                 else if (g_strcasecmp(*k, "publish-hinfo") == 0)
221                     config->server_config.publish_hinfo = is_yes(v);
222                 else if (g_strcasecmp(*k, "publish-workstation") == 0)
223                     config->server_config.publish_workstation = is_yes(v);
224                 else if (g_strcasecmp(*k, "publish-domain") == 0)
225                     config->server_config.publish_domain = is_yes(v);
226                 else {
227                     fprintf(stderr, "Invalid configuration key \"%s\" in group \"%s\"\n", *k, *g);
228                     goto finish;
229                 }
230
231                 g_free(v);
232                 v = NULL;
233             }
234
235             g_strfreev(keys);
236             keys = NULL;
237
238         } else if (g_strcasecmp(*g, "reflector") == 0) {
239             gchar **k;
240             
241             keys = g_key_file_get_keys(f, *g, NULL, NULL);
242
243             for (k = keys; *k; k++) {
244
245                 v = g_key_file_get_string(f, *g, *k, NULL);
246                 
247                 if (g_strcasecmp(*k, "enable-reflector") == 0)
248                     config->server_config.enable_reflector = is_yes(v);
249                 else if (g_strcasecmp(*k, "reflect-ipv") == 0)
250                     config->server_config.reflect_ipv = is_yes(v);
251                 else {
252                     fprintf(stderr, "Invalid configuration key \"%s\" in group \"%s\"\n", *k, *g);
253                     goto finish;
254                 }
255
256                 g_free(v);
257                 v = NULL;
258             }
259     
260             g_strfreev(keys);
261             keys = NULL;
262             
263         } else {
264             fprintf(stderr, "Invalid configuration file group \"%s\".\n", *g);
265             goto finish;
266         }
267     }
268
269     r = 0;
270
271 finish:
272
273     g_strfreev(groups);
274     g_strfreev(keys);
275     g_free(v);
276
277     if (err)
278         g_error_free (err);
279
280     if (f)
281         g_key_file_free(f);
282     
283     return r;
284 }
285
286 static void log_function(AvahiLogLevel level, const gchar *txt) {
287
288     static const int const log_level_map[] = {
289         LOG_ERR,
290         LOG_WARNING,
291         LOG_NOTICE,
292         LOG_INFO,
293         LOG_DEBUG
294     };
295     
296     g_assert(level < AVAHI_LOG_LEVEL_MAX);
297     g_assert(txt);
298
299     daemon_log(log_level_map[level], "%s", txt);
300 }
301
302 static gboolean signal_callback(GIOChannel *source, GIOCondition condition, gpointer data) {
303     gint sig;
304     g_assert(source);
305
306     if ((sig = daemon_signal_next()) <= 0) {
307         avahi_log_error("daemon_signal_next() failed");
308         return FALSE;
309     }
310
311     switch (sig) {
312         case SIGINT:
313         case SIGQUIT:
314         case SIGTERM:
315             avahi_log_info(
316                 "Got %s, quitting.",
317                 sig == SIGINT ? "SIGINT" :
318                 (sig == SIGQUIT ? "SIGQUIT" : "SIGTERM"));
319             g_main_loop_quit((GMainLoop*) data);
320             break;
321
322         case SIGHUP:
323             avahi_log_info("Got SIGHUP, reloading.");
324             static_service_load();
325             static_service_add_to_server();
326             break;
327
328         default:
329             avahi_log_warn("Got spurious signal, ignoring.");
330             break;
331     }
332
333     return TRUE;
334 }
335
336 static gint run_server(DaemonConfig *config) {
337     GMainLoop *loop = NULL;
338     gint r = -1;
339     GIOChannel *io = NULL;
340     guint watch_id = (guint) -1;
341
342     g_assert(config);
343     
344     loop = g_main_loop_new(NULL, FALSE);
345
346     if (daemon_signal_init(SIGINT, SIGQUIT, SIGHUP, SIGTERM, 0) < 0) {
347         avahi_log_error("Could not register signal handlers (%s).", strerror(errno));
348         goto finish;
349     }
350
351     if (!(io = g_io_channel_unix_new(daemon_signal_fd()))) {
352         avahi_log_error( "Failed to create signal io channel.");
353         goto finish;
354     }
355
356     g_io_channel_set_close_on_unref(io, FALSE);
357     g_io_add_watch(io, G_IO_IN, signal_callback, loop);
358     
359     if (simple_protocol_setup(NULL) < 0)
360         goto finish;
361     
362 #ifdef ENABLE_DBUS
363     if (config->enable_dbus)
364         if (dbus_protocol_setup(loop) < 0)
365             goto finish;
366 #endif
367     
368     if (!(avahi_server = avahi_server_new(NULL, &config->server_config, server_callback, NULL)))
369         goto finish;
370     
371     static_service_load();
372
373     if (config->daemonize) {
374         daemon_retval_send(0);
375         r = 0;
376     }
377
378     g_main_loop_run(loop);
379
380 finish:
381     
382     static_service_remove_from_server();
383     static_service_free_all();
384     
385     simple_protocol_shutdown();
386
387 #ifdef ENABLE_DBUS
388     if (config->enable_dbus)
389         dbus_protocol_shutdown();
390 #endif
391
392     if (avahi_server)
393         avahi_server_free(avahi_server);
394
395     daemon_signal_done();
396
397     if (watch_id != (guint) -1)
398         g_source_remove(watch_id);
399     
400     if (io)
401         g_io_channel_unref(io);
402
403         
404     if (loop)
405         g_main_loop_unref(loop);
406
407     if (r != 0 && config->daemonize)
408         daemon_retval_send(1);
409     
410     return r;
411 }
412
413 int main(int argc, char *argv[]) {
414     gint r = 255;
415     DaemonConfig config;
416     const gchar *argv0;
417     gboolean wrote_pid_file = FALSE;
418
419     avahi_set_log_function(log_function);
420     
421     avahi_server_config_init(&config.server_config);
422     config.command = DAEMON_RUN;
423     config.daemonize = FALSE;
424     config.config_file = NULL;
425     config.enable_dbus = TRUE;
426
427     if ((argv0 = strrchr(argv[0], '/')))
428         argv0++;
429     else
430         argv0 = argv[0];
431
432     daemon_pid_file_ident = daemon_log_ident = (char *) argv0;
433     
434     if (parse_command_line(&config, argc, argv) < 0)
435         goto finish;
436
437     if (config.command == DAEMON_HELP) {
438         help(stdout, argv0);
439         r = 0;
440     } else if (config.command == DAEMON_VERSION) {
441         printf("%s "PACKAGE_VERSION"\n", argv0);
442
443     } else if (config.command == DAEMON_KILL) {
444         if (daemon_pid_file_kill_wait(SIGTERM, 5) < 0) {
445             avahi_log_warn("Failed to kill daemon: %s", strerror(errno));
446             goto finish;
447         }
448
449         r = 0;
450         
451     } else if (config.command == DAEMON_RELOAD) {
452         if (daemon_pid_file_kill(SIGHUP) < 0) {
453             avahi_log_warn("Failed to kill daemon: %s", strerror(errno));
454             goto finish;
455         }
456
457         r = 0;
458         
459     } else if (config.command == DAEMON_RUN) {
460         pid_t pid;
461
462         if (getuid() != 0) {
463             avahi_log_error("This program is intended to be run as root.");
464             goto finish;
465         }
466         
467         if ((pid = daemon_pid_file_is_running()) >= 0) {
468             avahi_log_error("Daemon already running on PID %u", pid);
469             goto finish;
470         }
471
472         if (load_config_file(&config) < 0)
473             goto finish;
474         
475         if (config.daemonize) {
476
477             daemon_retval_init();
478             
479             if ((pid = daemon_fork()) < 0)
480                 goto finish;
481             else if (pid != 0) {
482                 int ret;
483                 /** Parent **/
484
485                 if ((ret = daemon_retval_wait(20)) < 0) {
486                     avahi_log_error("Could not recieve return value from daemon process.");
487                     goto finish;
488                 }
489
490                 r = ret;
491                 goto finish;
492             }
493
494             /* Child */
495         }
496
497         if (daemon_pid_file_create() < 0) {
498             avahi_log_error("Failed to create PID file: %s", strerror(errno));
499
500             if (config.daemonize)
501                 daemon_retval_send(1);
502             goto finish;
503         } else
504             wrote_pid_file = TRUE;
505
506         if (run_server(&config) == 0)
507             r = 0;
508     }
509         
510 finish:
511
512     if (config.daemonize)
513         daemon_retval_done();
514
515     avahi_server_config_free(&config.server_config);
516     g_free(config.config_file);
517
518     if (wrote_pid_file)
519         daemon_pid_file_remove();
520     
521     return r;
522 }