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 static char marker[1] = {'S'};
245
246 /* write a 'S' character to the other end to tell about
247 * the signal. Note that 'the other end' is a GIOChannel thingy
248 * that is only called from the mainloop - thus this is how we
249 * defer this since UNIX signal handlers are evil
250 *
251 * Oh, and write(2) is indeed reentrant */
252 (void) write (sigterm_unix_signal_pipe_fds[1], marker, 1);
253 }
254
255 static gboolean
sigterm_iochn_data(GIOChannel * source,GIOCondition condition,gpointer user_data)256 sigterm_iochn_data (GIOChannel *source,
257 GIOCondition condition,
258 gpointer user_data)
259 {
260 GError *err = NULL;
261 gchar data[1];
262 gsize bytes_read;
263
264 /* Empty the pipe */
265 if (G_IO_STATUS_NORMAL !=
266 g_io_channel_read_chars (source, data, 1, &bytes_read, &err)) {
267 HAL_ERROR (("Error emptying sigterm pipe: %s",
268 err->message));
269 g_error_free (err);
270 goto out;
271 }
272
273 HAL_INFO (("Caught SIGTERM, initiating shutdown"));
274 hald_runner_kill_all();
275 exit (0);
276
277 out:
278 return TRUE;
279 }
280
281
282 /** This is set to #TRUE if we are probing and #FALSE otherwise */
283 dbus_bool_t hald_is_initialising;
284
285 static int startup_daemonize_pipe[2];
286
287
288 /*--------------------------------------------------------------------------------------------------*/
289
290 static gboolean child_died = FALSE;
291
292 static void
handle_sigchld(int value)293 handle_sigchld (int value)
294 {
295 child_died = TRUE;
296 }
297
298 static int
parent_wait_for_child(int child_fd,pid_t child_pid)299 parent_wait_for_child (int child_fd, pid_t child_pid)
300 {
301 fd_set rfds;
302 fd_set efds;
303 struct timeval tv;
304 int retval;
305 int ret;
306
307 signal(SIGCHLD, handle_sigchld);
308
309 /* wait for either
310 *
311 * o Child writes something to the child_fd; means that device
312 * probing is completed and the parent should exit with success
313 *
314 * o Child is killed (segfault etc.); means that parent should exit
315 * with failure
316 *
317 * o Timeout; means that we should kill the child and exit with
318 * failure
319 *
320 */
321
322 FD_ZERO(&rfds);
323 FD_SET(child_fd, &rfds);
324 FD_ZERO(&efds);
325 FD_SET(child_fd, &efds);
326 /* Wait up to 250 seconds for device probing */
327 tv.tv_sec = 250;
328 tv.tv_usec = 0;
329
330 retval = select (child_fd + 1, &rfds, NULL, &efds, &tv);
331
332 if (child_died) {
333 /* written from handle_sigchld */
334 ret = 1;
335 goto out;
336 }
337
338 if (retval > 0) {
339 /* means child wrote to socket or closed it; all good */
340 ret = 0;
341 goto out;
342 }
343
344 /* assume timeout; kill child */
345 kill (child_pid, SIGTERM);
346 ret = 2;
347
348 out:
349 return ret;
350 }
351
352 /*--------------------------------------------------------------------------------------------------*/
353
354 /** Entry point for HAL daemon
355 *
356 * @param argc Number of arguments
357 * @param argv Array of arguments
358 * @return Exit code
359 */
360 int
main(int argc,char * argv[])361 main (int argc, char *argv[])
362 {
363 GMainLoop *loop;
364 char *path;
365 char newpath[512];
366
367 openlog ("hald", LOG_PID, LOG_DAEMON);
368 #if !GLIB_CHECK_VERSION(2,35,0)
369 g_type_init ();
370 #endif
371 if (getenv ("HALD_VERBOSE"))
372 hald_is_verbose = TRUE;
373 else
374 hald_is_verbose = FALSE;
375
376 /* our helpers are installed into libexec, so adjust out $PATH
377 * to include this at the end (since we want to overide in
378 * run-hald.sh and friends)
379 */
380 path = getenv ("PATH");
381 if (path != NULL) {
382 g_strlcpy (newpath, path, sizeof (newpath));
383 g_strlcat (newpath, ":", sizeof (newpath));
384 } else {
385 /* No PATH was set */
386 newpath[0] = '\0';
387 }
388
389 g_strlcat (newpath, PACKAGE_LIBEXEC_DIR, sizeof (newpath));
390 g_strlcat (newpath, ":", sizeof (newpath));
391 g_strlcat (newpath, PACKAGE_SCRIPT_DIR, sizeof (newpath));
392
393 setenv ("PATH", newpath, TRUE);
394
395 while (1) {
396 int c;
397 int option_index = 0;
398 const char *opt;
399 static struct option long_options[] = {
400 {"daemon", 1, NULL, 0},
401 {"verbose", 1, NULL, 0},
402 {"use-syslog", 0, NULL, 0},
403 {"help", 0, NULL, 0},
404 {"version", 0, NULL, 0},
405 {NULL, 0, NULL, 0}
406 };
407
408 c = getopt_long (argc, argv, "",
409 long_options, &option_index);
410 if (c == -1)
411 break;
412
413 switch (c) {
414 case 0:
415 opt = long_options[option_index].name;
416
417 if (strcmp (opt, "help") == 0) {
418 usage ();
419 return 0;
420 } else if (strcmp (opt, "version") == 0) {
421 fprintf (stderr, "HAL package version: " PACKAGE_VERSION "\n");
422 return 0;
423 } else if (strcmp (opt, "daemon") == 0) {
424 if (strcmp ("yes", optarg) == 0) {
425 opt_become_daemon = TRUE;
426 } else if (strcmp ("no", optarg) == 0) {
427 opt_become_daemon = FALSE;
428 } else {
429 usage ();
430 return 1;
431 }
432 } else if (strcmp (opt, "verbose") == 0) {
433 if (strcmp ("yes", optarg) == 0) {
434 hald_is_verbose = TRUE;
435 } else if (strcmp ("no", optarg) == 0) {
436 hald_is_verbose = FALSE;
437 } else {
438 usage ();
439 return 1;
440 }
441 } else if (strcmp (opt, "use-syslog") == 0) {
442 hald_use_syslog = TRUE;
443 }
444
445 break;
446
447 default:
448 usage ();
449 return 1;
450 break;
451 }
452 }
453
454 if (hald_is_verbose)
455 logger_enable ();
456 else
457 logger_disable ();
458
459 if (hald_use_syslog)
460 logger_enable_syslog ();
461 else
462 logger_disable_syslog ();
463
464 /* will fork into two; only the child will return here if we are successful */
465 /*master_slave_setup ();
466 sleep (100000000);*/
467
468 loop = g_main_loop_new (NULL, FALSE);
469
470 HAL_INFO ((PACKAGE_STRING));
471
472 if (opt_become_daemon) {
473 int child_pid;
474 int dev_null_fd;
475 int pf;
476 char pid[9];
477
478 HAL_INFO (("Will daemonize"));
479 HAL_INFO (("Becoming a daemon"));
480
481 if (pipe (startup_daemonize_pipe) != 0) {
482 fprintf (stderr, "Could not setup pipe: %s\n", strerror(errno));
483 exit (1);
484 }
485
486
487 if (chdir ("/") < 0) {
488 fprintf (stderr, "Could not chdir to /: %s\n", strerror(errno));
489 exit (1);
490 }
491
492 child_pid = fork ();
493 switch (child_pid) {
494 case -1:
495 fprintf (stderr, "Cannot fork(): %s\n", strerror(errno));
496 break;
497
498 case 0:
499 /* child */
500
501 dev_null_fd = open ("/dev/null", O_RDWR);
502 /* ignore if we can't open /dev/null */
503 if (dev_null_fd >= 0) {
504 /* attach /dev/null to stdout, stdin, stderr */
505 dup2 (dev_null_fd, 0);
506 dup2 (dev_null_fd, 1);
507 dup2 (dev_null_fd, 2);
508 close (dev_null_fd);
509 }
510
511 umask (022);
512 break;
513
514 default:
515 /* parent, block until child writes */
516 exit (parent_wait_for_child (startup_daemonize_pipe[0], child_pid));
517 break;
518 }
519
520 /* Create session */
521 setsid ();
522
523 /* remove old pid file */
524 unlink (HALD_PID_FILE);
525
526 /* Make a new one */
527 if ((pf= open (HALD_PID_FILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644)) > 0) {
528 snprintf (pid, sizeof(pid), "%lu\n", (long unsigned) getpid ());
529 (void) write (pf, pid, strlen(pid));
530 close (pf);
531 atexit (delete_pid);
532 }
533 } else {
534 HAL_INFO (("Will not daemonize"));
535 }
536
537
538 /* we need to do stuff when we are expected to terminate, thus
539 * this involves looking for SIGTERM; UNIX signal handlers are
540 * evil though, so set up a pipe to transmit the signal.
541 */
542
543 /* create pipe */
544 if (pipe (sigterm_unix_signal_pipe_fds) != 0) {
545 DIE (("Could not setup pipe, errno=%d", errno));
546 }
547
548 /* setup glib handler - 0 is for reading, 1 is for writing */
549 sigterm_iochn = g_io_channel_unix_new (sigterm_unix_signal_pipe_fds[0]);
550 if (sigterm_iochn == NULL)
551 DIE (("Could not create GIOChannel"));
552
553 /* get callback when there is data to read */
554 (void) g_io_add_watch (
555 sigterm_iochn, G_IO_IN, sigterm_iochn_data, NULL);
556
557 /* Finally, setup unix signal handler for TERM */
558 signal (SIGTERM, handle_sigterm);
559
560 /* set up the local dbus server */
561 if (!hald_dbus_local_server_init ())
562 return 1;
563 /* Start the runner helper daemon */
564 if (!hald_runner_start_runner ()) {
565 return 1;
566 }
567
568 drop_privileges(0);
569
570 /* initialize operating system specific parts */
571 osspec_init ();
572
573 hald_is_initialising = TRUE;
574
575 /* detect devices */
576 osspec_probe ();
577
578 /* run the main loop and serve clients */
579 g_main_loop_run (loop);
580
581 return 0;
582 }
583
584 #ifdef HALD_MEMLEAK_DBG
585 extern int dbg_hal_device_object_delta;
586
587 /* useful for valgrinding; see below */
588 static gboolean
my_shutdown(gpointer data)589 my_shutdown (gpointer data)
590 {
591 HalDeviceStore *gdl;
592
593 printf ("Num devices in TDL: %d\n", g_slist_length ((hald_get_tdl ())->devices));
594 printf ("Num devices in GDL: %d\n", g_slist_length ((hald_get_gdl ())->devices));
595
596 gdl = hald_get_gdl ();
597 next:
598 if (g_slist_length (gdl->devices) > 0) {
599 HalDevice *d = HAL_DEVICE(gdl->devices->data);
600 hal_device_store_remove (gdl, d);
601 g_object_unref (d);
602 goto next;
603 }
604
605 printf ("hal_device_object_delta = %d (should be zero)\n", dbg_hal_device_object_delta);
606 exit (1);
607 }
608 #endif
609
610 void
osspec_probe_done(void)611 osspec_probe_done (void)
612 {
613 char buf[1] = {0};
614
615 HAL_INFO (("Device probing completed"));
616
617 if (!hald_dbus_init ()) {
618 hald_runner_kill_all();
619 exit (1);
620 }
621
622 /* tell parent to exit */
623 (void) write (startup_daemonize_pipe[1], buf, sizeof (buf));
624 close (startup_daemonize_pipe[0]);
625 close (startup_daemonize_pipe[1]);
626
627 hald_is_initialising = FALSE;
628
629 #ifdef HALD_MEMLEAK_DBG
630 g_timeout_add ((HALD_MEMLEAK_DBG) * 1000,
631 my_shutdown,
632 NULL);
633 #endif
634 }
635
636
637 /** @} */
638