1 /***************************************************************************
2 * CVSID: $Id$
3 *
4 * hald.c : main startup for HAL daemon
5 *
6 * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
7 * Copyright (C) 2005 Danny Kukawka, <danny.kukawka@web.de>
8 *
9 * Licensed under the Academic Free License version 2.1
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 *
25 **************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <getopt.h>
36 #include <pwd.h>
37 #include <stdint.h>
38 #include <sys/stat.h>
39 #include <fcntl.h>
40 #include <errno.h>
41 #include <signal.h>
42 #include <grp.h>
43 #include <syslog.h>
44
45 #include <dbus/dbus.h>
46 #include <dbus/dbus-glib.h>
47 #include <dbus/dbus-glib-lowlevel.h>
48
49 /*#include "master_slave.h"*/
50
51 #include "logger.h"
52 #include "hald.h"
53 #include "device_store.h"
54 #include "device_info.h"
55 #include "osspec.h"
56 #include "hald_dbus.h"
57 #include "util.h"
58 #include "hald_runner.h"
59 #include "util_helper.h"
60
delete_pid(void)61 static void delete_pid(void)
62 {
63 unlink(HALD_PID_FILE);
64 }
65
66 /**
67 * @defgroup HalDaemon HAL daemon
68 * @brief The HAL daemon manages persistent device objects available through
69 * a D-BUS network API
70 */
71
72 static HalDeviceStore *global_device_list = NULL;
73
74 static HalDeviceStore *temporary_device_list = NULL;
75
76
77 static void
addon_terminated(HalDevice * device,guint32 exit_type,gint return_code,gchar ** error,gpointer data1,gpointer data2)78 addon_terminated (HalDevice *device, guint32 exit_type,
79 gint return_code, gchar **error,
80 gpointer data1, gpointer data2)
81 {
82 HAL_INFO (("in addon_terminated for udi=%s", device->udi));
83
84 /* TODO: log to syslog - addons shouldn't just terminate, this is a bug with the addon */
85
86 /* however, the world can stop, mark this addon as ready
87 * (TODO: potential bug if the addon crashed after calling libhal_device_addon_is_ready())
88 */
89 if (hal_device_inc_num_ready_addons (device)) {
90 if (hal_device_are_all_addons_ready (device)) {
91 manager_send_signal_device_added (device);
92 }
93 }
94 }
95
96
97
98
99 static void
gdl_store_changed(HalDeviceStore * store,HalDevice * device,gboolean is_added,gpointer user_data)100 gdl_store_changed (HalDeviceStore *store, HalDevice *device,
101 gboolean is_added, gpointer user_data)
102 {
103 if (is_added) {
104 GSList *addons;
105
106 HAL_INFO (("Added device to GDL; udi=%s", hal_device_get_udi(device)));
107
108 if ((addons = hal_device_property_get_strlist (device, "info.addons")) != NULL) {
109 GSList *i;
110
111 for (i = addons; i != NULL; i = g_slist_next (i)) {
112 const gchar *command_line;
113 gchar *extra_env[2] = {"HALD_ACTION=addon", NULL};
114
115 command_line = (const gchar *) i->data;
116 if (hald_runner_start(device, command_line, extra_env, addon_terminated, NULL, NULL)) {
117 HAL_INFO (("Started addon %s for udi %s",
118 command_line, hal_device_get_udi(device)));
119 hal_device_inc_num_addons (device);
120 } else {
121 HAL_ERROR (("Cannot start addon %s for udi %s",
122 command_line, hal_device_get_udi(device)));
123 }
124 }
125 }
126 } else {
127 HAL_INFO (("Removed device from GDL; udi=%s", hal_device_get_udi(device)));
128 hald_runner_kill_device(device);
129 }
130
131 /*hal_device_print (device);*/
132
133 if (is_added) {
134 if (hal_device_are_all_addons_ready (device)) {
135 manager_send_signal_device_added (device);
136 }
137 } else {
138 if (hal_device_are_all_addons_ready (device)) {
139 manager_send_signal_device_removed (device);
140 }
141 }
142 }
143
144 static void
gdl_property_changed(HalDeviceStore * store,HalDevice * device,const char * key,gboolean added,gboolean removed,gpointer user_data)145 gdl_property_changed (HalDeviceStore *store, HalDevice *device,
146 const char *key, gboolean added, gboolean removed,
147 gpointer user_data)
148 {
149 if (hal_device_are_all_addons_ready (device)) {
150 device_send_signal_property_modified (device, key, removed, added);
151 }
152
153 /* only execute the callouts if the property _changed_ */
154 if (added == FALSE && removed == FALSE)
155 /*hal_callout_property (device, key)*/;
156 }
157
158 static void
gdl_capability_added(HalDeviceStore * store,HalDevice * device,const char * capability,gpointer user_data)159 gdl_capability_added (HalDeviceStore *store, HalDevice *device,
160 const char *capability, gpointer user_data)
161 {
162 if (hal_device_are_all_addons_ready (device)) {
163 manager_send_signal_new_capability (device, capability);
164 }
165 /*hal_callout_capability (device, capability, TRUE)*/;
166 }
167
168 HalDeviceStore *
hald_get_gdl(void)169 hald_get_gdl (void)
170 {
171 if (global_device_list == NULL) {
172 global_device_list = hal_device_store_new ();
173
174 g_signal_connect (global_device_list,
175 "store_changed",
176 G_CALLBACK (gdl_store_changed), NULL);
177 g_signal_connect (global_device_list,
178 "device_property_changed",
179 G_CALLBACK (gdl_property_changed), NULL);
180 g_signal_connect (global_device_list,
181 "device_capability_added",
182 G_CALLBACK (gdl_capability_added), NULL);
183 }
184
185 return global_device_list;
186 }
187
188 HalDeviceStore *
hald_get_tdl(void)189 hald_get_tdl (void)
190 {
191 if (temporary_device_list == NULL) {
192 temporary_device_list = hal_device_store_new ();
193
194 }
195
196 return temporary_device_list;
197 }
198
199 /**
200 * @defgroup MainDaemon Basic functions
201 * @ingroup HalDaemon
202 * @brief Basic functions in the HAL daemon
203 * @{
204 */
205
206 /** Print out program usage.
207 *
208 */
209 static void
usage()210 usage ()
211 {
212 fprintf (stderr, "\n" "usage : hald [--daemon=yes|no] [--verbose=yes|no] [--help]\n");
213 fprintf (stderr,
214 "\n"
215 " --daemon=yes|no Become a daemon\n"
216 " --verbose=yes|no Print out debug (overrides HALD_VERBOSE)\n"
217 " --use-syslog Print out debug messages to syslog instead of stderr.\n"
218 " Use this option to get debug messages if HAL runs as\n"
219 " daemon.\n"
220 " --help Show this information and exit\n"
221 " --version Output version information and exit"
222 "\n"
223 "The HAL daemon detects devices present in the system and provides the\n"
224 "org.freedesktop.Hal service through the system-wide message bus provided\n"
225 "by D-BUS.\n"
226 "\n"
227 "For more information visit http://freedesktop.org/Software/hal\n"
228 "\n");
229 }
230
231 /** If #TRUE, we will daemonize */
232 static dbus_bool_t opt_become_daemon = TRUE;
233
234 /** If #TRUE, we will spew out debug */
235 dbus_bool_t hald_is_verbose = FALSE;
236 dbus_bool_t hald_use_syslog = FALSE;
237
238 static int sigterm_unix_signal_pipe_fds[2];
239 static GIOChannel *sigterm_iochn;
240
241 static void
handle_sigterm(int value)242 handle_sigterm (int value)
243 {
244 ssize_t written;
245 static char marker[1] = {'S'};
246
247 /* write a 'S' character to the other end to tell about
248 * the signal. Note that 'the other end' is a GIOChannel thingy
249 * that is only called from the mainloop - thus this is how we
250 * defer this since UNIX signal handlers are evil
251 *
252 * Oh, and write(2) is indeed reentrant */
253 written = write (sigterm_unix_signal_pipe_fds[1], marker, 1);
254 }
255
256 static gboolean
sigterm_iochn_data(GIOChannel * source,GIOCondition condition,gpointer user_data)257 sigterm_iochn_data (GIOChannel *source,
258 GIOCondition condition,
259 gpointer user_data)
260 {
261 GError *err = NULL;
262 gchar data[1];
263 gsize bytes_read;
264
265 /* Empty the pipe */
266 if (G_IO_STATUS_NORMAL !=
267 g_io_channel_read_chars (source, data, 1, &bytes_read, &err)) {
268 HAL_ERROR (("Error emptying sigterm pipe: %s",
269 err->message));
270 g_error_free (err);
271 goto out;
272 }
273
274 HAL_INFO (("Caught SIGTERM, initiating shutdown"));
275 hald_runner_kill_all();
276 exit (0);
277
278 out:
279 return TRUE;
280 }
281
282
283 /** This is set to #TRUE if we are probing and #FALSE otherwise */
284 dbus_bool_t hald_is_initialising;
285
286 static int startup_daemonize_pipe[2];
287
288
289 /*--------------------------------------------------------------------------------------------------*/
290
291 static gboolean child_died = FALSE;
292
293 static void
handle_sigchld(int value)294 handle_sigchld (int value)
295 {
296 child_died = TRUE;
297 }
298
299 static int
parent_wait_for_child(int child_fd,pid_t child_pid)300 parent_wait_for_child (int child_fd, pid_t child_pid)
301 {
302 fd_set rfds;
303 fd_set efds;
304 struct timeval tv;
305 int retval;
306 int ret;
307
308 signal(SIGCHLD, handle_sigchld);
309
310 /* wait for either
311 *
312 * o Child writes something to the child_fd; means that device
313 * probing is completed and the parent should exit with success
314 *
315 * o Child is killed (segfault etc.); means that parent should exit
316 * with failure
317 *
318 * o Timeout; means that we should kill the child and exit with
319 * failure
320 *
321 */
322
323 FD_ZERO(&rfds);
324 FD_SET(child_fd, &rfds);
325 FD_ZERO(&efds);
326 FD_SET(child_fd, &efds);
327 /* Wait up to 250 seconds for device probing */
328 tv.tv_sec = 250;
329 tv.tv_usec = 0;
330
331 retval = select (child_fd + 1, &rfds, NULL, &efds, &tv);
332
333 if (child_died) {
334 /* written from handle_sigchld */
335 ret = 1;
336 goto out;
337 }
338
339 if (retval > 0) {
340 /* means child wrote to socket or closed it; all good */
341 ret = 0;
342 goto out;
343 }
344
345 /* assume timeout; kill child */
346 kill (child_pid, SIGTERM);
347 ret = 2;
348
349 out:
350 return ret;
351 }
352
353 /*--------------------------------------------------------------------------------------------------*/
354
355 /** Entry point for HAL daemon
356 *
357 * @param argc Number of arguments
358 * @param argv Array of arguments
359 * @return Exit code
360 */
361 int
main(int argc,char * argv[])362 main (int argc, char *argv[])
363 {
364 GMainLoop *loop;
365 guint sigterm_iochn_listener_source_id;
366 char *path;
367 char newpath[512];
368
369 openlog ("hald", LOG_PID, LOG_DAEMON);
370 #if !GLIB_CHECK_VERSION(2,35,0)
371 g_type_init ();
372 #endif
373 if (getenv ("HALD_VERBOSE"))
374 hald_is_verbose = TRUE;
375 else
376 hald_is_verbose = FALSE;
377
378 /* our helpers are installed into libexec, so adjust out $PATH
379 * to include this at the end (since we want to overide in
380 * run-hald.sh and friends)
381 */
382 path = getenv ("PATH");
383 if (path != NULL) {
384 g_strlcpy (newpath, path, sizeof (newpath));
385 g_strlcat (newpath, ":", sizeof (newpath));
386 } else {
387 /* No PATH was set */
388 newpath[0] = '\0';
389 }
390
391 g_strlcat (newpath, PACKAGE_LIBEXEC_DIR, sizeof (newpath));
392 g_strlcat (newpath, ":", sizeof (newpath));
393 g_strlcat (newpath, PACKAGE_SCRIPT_DIR, sizeof (newpath));
394
395 setenv ("PATH", newpath, TRUE);
396
397 while (1) {
398 int c;
399 int option_index = 0;
400 const char *opt;
401 static struct option long_options[] = {
402 {"daemon", 1, NULL, 0},
403 {"verbose", 1, NULL, 0},
404 {"use-syslog", 0, NULL, 0},
405 {"help", 0, NULL, 0},
406 {"version", 0, NULL, 0},
407 {NULL, 0, NULL, 0}
408 };
409
410 c = getopt_long (argc, argv, "",
411 long_options, &option_index);
412 if (c == -1)
413 break;
414
415 switch (c) {
416 case 0:
417 opt = long_options[option_index].name;
418
419 if (strcmp (opt, "help") == 0) {
420 usage ();
421 return 0;
422 } else if (strcmp (opt, "version") == 0) {
423 fprintf (stderr, "HAL package version: " PACKAGE_VERSION "\n");
424 return 0;
425 } else if (strcmp (opt, "daemon") == 0) {
426 if (strcmp ("yes", optarg) == 0) {
427 opt_become_daemon = TRUE;
428 } else if (strcmp ("no", optarg) == 0) {
429 opt_become_daemon = FALSE;
430 } else {
431 usage ();
432 return 1;
433 }
434 } else if (strcmp (opt, "verbose") == 0) {
435 if (strcmp ("yes", optarg) == 0) {
436 hald_is_verbose = TRUE;
437 } else if (strcmp ("no", optarg) == 0) {
438 hald_is_verbose = FALSE;
439 } else {
440 usage ();
441 return 1;
442 }
443 } else if (strcmp (opt, "use-syslog") == 0) {
444 hald_use_syslog = TRUE;
445 }
446
447 break;
448
449 default:
450 usage ();
451 return 1;
452 break;
453 }
454 }
455
456 if (hald_is_verbose)
457 logger_enable ();
458 else
459 logger_disable ();
460
461 if (hald_use_syslog)
462 logger_enable_syslog ();
463 else
464 logger_disable_syslog ();
465
466 /* will fork into two; only the child will return here if we are successful */
467 /*master_slave_setup ();
468 sleep (100000000);*/
469
470 loop = g_main_loop_new (NULL, FALSE);
471
472 HAL_INFO ((PACKAGE_STRING));
473
474 if (opt_become_daemon) {
475 int child_pid;
476 int dev_null_fd;
477 int pf;
478 ssize_t written;
479 char pid[9];
480
481 HAL_INFO (("Will daemonize"));
482 HAL_INFO (("Becoming a daemon"));
483
484 if (pipe (startup_daemonize_pipe) != 0) {
485 fprintf (stderr, "Could not setup pipe: %s\n", strerror(errno));
486 exit (1);
487 }
488
489
490 if (chdir ("/") < 0) {
491 fprintf (stderr, "Could not chdir to /: %s\n", strerror(errno));
492 exit (1);
493 }
494
495 child_pid = fork ();
496 switch (child_pid) {
497 case -1:
498 fprintf (stderr, "Cannot fork(): %s\n", strerror(errno));
499 break;
500
501 case 0:
502 /* child */
503
504 dev_null_fd = open ("/dev/null", O_RDWR);
505 /* ignore if we can't open /dev/null */
506 if (dev_null_fd >= 0) {
507 /* attach /dev/null to stdout, stdin, stderr */
508 dup2 (dev_null_fd, 0);
509 dup2 (dev_null_fd, 1);
510 dup2 (dev_null_fd, 2);
511 close (dev_null_fd);
512 }
513
514 umask (022);
515 break;
516
517 default:
518 /* parent, block until child writes */
519 exit (parent_wait_for_child (startup_daemonize_pipe[0], child_pid));
520 break;
521 }
522
523 /* Create session */
524 setsid ();
525
526 /* remove old pid file */
527 unlink (HALD_PID_FILE);
528
529 /* Make a new one */
530 if ((pf= open (HALD_PID_FILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644)) > 0) {
531 snprintf (pid, sizeof(pid), "%lu\n", (long unsigned) getpid ());
532 written = write (pf, pid, strlen(pid));
533 close (pf);
534 atexit (delete_pid);
535 }
536 } else {
537 HAL_INFO (("Will not daemonize"));
538 }
539
540
541 /* we need to do stuff when we are expected to terminate, thus
542 * this involves looking for SIGTERM; UNIX signal handlers are
543 * evil though, so set up a pipe to transmit the signal.
544 */
545
546 /* create pipe */
547 if (pipe (sigterm_unix_signal_pipe_fds) != 0) {
548 DIE (("Could not setup pipe, errno=%d", errno));
549 }
550
551 /* setup glib handler - 0 is for reading, 1 is for writing */
552 sigterm_iochn = g_io_channel_unix_new (sigterm_unix_signal_pipe_fds[0]);
553 if (sigterm_iochn == NULL)
554 DIE (("Could not create GIOChannel"));
555
556 /* get callback when there is data to read */
557 sigterm_iochn_listener_source_id = g_io_add_watch (
558 sigterm_iochn, G_IO_IN, sigterm_iochn_data, NULL);
559
560 /* Finally, setup unix signal handler for TERM */
561 signal (SIGTERM, handle_sigterm);
562
563 /* set up the local dbus server */
564 if (!hald_dbus_local_server_init ())
565 return 1;
566 /* Start the runner helper daemon */
567 if (!hald_runner_start_runner ()) {
568 return 1;
569 }
570
571 drop_privileges(0);
572
573 /* initialize operating system specific parts */
574 osspec_init ();
575
576 hald_is_initialising = TRUE;
577
578 /* detect devices */
579 osspec_probe ();
580
581 /* run the main loop and serve clients */
582 g_main_loop_run (loop);
583
584 return 0;
585 }
586
587 #ifdef HALD_MEMLEAK_DBG
588 extern int dbg_hal_device_object_delta;
589
590 /* useful for valgrinding; see below */
591 static gboolean
my_shutdown(gpointer data)592 my_shutdown (gpointer data)
593 {
594 HalDeviceStore *gdl;
595
596 printf ("Num devices in TDL: %d\n", g_slist_length ((hald_get_tdl ())->devices));
597 printf ("Num devices in GDL: %d\n", g_slist_length ((hald_get_gdl ())->devices));
598
599 gdl = hald_get_gdl ();
600 next:
601 if (g_slist_length (gdl->devices) > 0) {
602 HalDevice *d = HAL_DEVICE(gdl->devices->data);
603 hal_device_store_remove (gdl, d);
604 g_object_unref (d);
605 goto next;
606 }
607
608 printf ("hal_device_object_delta = %d (should be zero)\n", dbg_hal_device_object_delta);
609 exit (1);
610 }
611 #endif
612
613 void
osspec_probe_done(void)614 osspec_probe_done (void)
615 {
616 ssize_t written;
617 char buf[1] = {0};
618
619 HAL_INFO (("Device probing completed"));
620
621 if (!hald_dbus_init ()) {
622 hald_runner_kill_all();
623 exit (1);
624 }
625
626 /* tell parent to exit */
627 written = write (startup_daemonize_pipe[1], buf, sizeof (buf));
628 close (startup_daemonize_pipe[0]);
629 close (startup_daemonize_pipe[1]);
630
631 hald_is_initialising = FALSE;
632
633 #ifdef HALD_MEMLEAK_DBG
634 g_timeout_add ((HALD_MEMLEAK_DBG) * 1000,
635 my_shutdown,
636 NULL);
637 #endif
638 }
639
640
641 /** @} */
642