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