1 /***************************************************************************
4 * hald.c : main startup for HAL daemon
6 * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
7 * Copyright (C) 2005 Danny Kukawka, <danny.kukawka@web.de>
9 * Licensed under the Academic Free License version 2.1
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.
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.
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
25 **************************************************************************/
45 #include <dbus/dbus.h>
46 #include <dbus/dbus-glib.h>
47 #include <dbus/dbus-glib-lowlevel.h>
49 /*#include "master_slave.h"*/
53 #include "device_store.h"
54 #include "device_info.h"
56 #include "hald_dbus.h"
58 #include "hald_runner.h"
59 #include "util_helper.h"
61 static void delete_pid(void)
63 unlink(HALD_PID_FILE
);
67 * @defgroup HalDaemon HAL daemon
68 * @brief The HAL daemon manages persistent device objects available through
72 static HalDeviceStore
*global_device_list
= NULL
;
74 static HalDeviceStore
*temporary_device_list
= NULL
;
78 addon_terminated (HalDevice
*device
, guint32 exit_type
,
79 gint return_code
, gchar
**error
,
80 gpointer data1
, gpointer data2
)
82 HAL_INFO (("in addon_terminated for udi=%s", device
->udi
));
84 /* TODO: log to syslog - addons shouldn't just terminate, this is a bug with the addon */
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())
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
);
100 gdl_store_changed (HalDeviceStore
*store
, HalDevice
*device
,
101 gboolean is_added
, gpointer user_data
)
106 HAL_INFO (("Added device to GDL; udi=%s", hal_device_get_udi(device
)));
108 if ((addons
= hal_device_property_get_strlist (device
, "info.addons")) != NULL
) {
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
};
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
);
121 HAL_ERROR (("Cannot start addon %s for udi %s",
122 command_line
, hal_device_get_udi(device
)));
127 HAL_INFO (("Removed device from GDL; udi=%s", hal_device_get_udi(device
)));
128 hald_runner_kill_device(device
);
131 /*hal_device_print (device);*/
134 if (hal_device_are_all_addons_ready (device
)) {
135 manager_send_signal_device_added (device
);
138 if (hal_device_are_all_addons_ready (device
)) {
139 manager_send_signal_device_removed (device
);
145 gdl_property_changed (HalDeviceStore
*store
, HalDevice
*device
,
146 const char *key
, gboolean added
, gboolean removed
,
149 if (hal_device_are_all_addons_ready (device
)) {
150 device_send_signal_property_modified (device
, key
, removed
, added
);
153 /* only execute the callouts if the property _changed_ */
154 if (added
== FALSE
&& removed
== FALSE
)
155 /*hal_callout_property (device, key)*/;
159 gdl_capability_added (HalDeviceStore
*store
, HalDevice
*device
,
160 const char *capability
, gpointer user_data
)
162 if (hal_device_are_all_addons_ready (device
)) {
163 manager_send_signal_new_capability (device
, capability
);
165 /*hal_callout_capability (device, capability, TRUE)*/;
171 if (global_device_list
== NULL
) {
172 global_device_list
= hal_device_store_new ();
174 g_signal_connect (global_device_list
,
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
);
185 return global_device_list
;
191 if (temporary_device_list
== NULL
) {
192 temporary_device_list
= hal_device_store_new ();
196 return temporary_device_list
;
200 * @defgroup MainDaemon Basic functions
202 * @brief Basic functions in the HAL daemon
206 /** Print out program usage.
212 fprintf (stderr
, "\n" "usage : hald [--daemon=yes|no] [--verbose=yes|no] [--help]\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"
220 " --help Show this information and exit\n"
221 " --version Output version information and exit"
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"
227 "For more information visit http://freedesktop.org/Software/hal\n"
231 /** If #TRUE, we will daemonize */
232 static dbus_bool_t opt_become_daemon
= TRUE
;
234 /** If #TRUE, we will spew out debug */
235 dbus_bool_t hald_is_verbose
= FALSE
;
236 dbus_bool_t hald_use_syslog
= FALSE
;
238 static int sigterm_unix_signal_pipe_fds
[2];
239 static GIOChannel
*sigterm_iochn
;
242 handle_sigterm (int value
)
245 static char marker
[1] = {'S'};
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
252 * Oh, and write(2) is indeed reentrant */
253 written
= write (sigterm_unix_signal_pipe_fds
[1], marker
, 1);
257 sigterm_iochn_data (GIOChannel
*source
,
258 GIOCondition condition
,
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",
274 HAL_INFO (("Caught SIGTERM, initiating shutdown"));
275 hald_runner_kill_all();
283 /** This is set to #TRUE if we are probing and #FALSE otherwise */
284 dbus_bool_t hald_is_initialising
;
286 static int startup_daemonize_pipe
[2];
289 /*--------------------------------------------------------------------------------------------------*/
291 static gboolean child_died
= FALSE
;
294 handle_sigchld (int value
)
300 parent_wait_for_child (int child_fd
, pid_t child_pid
)
308 signal(SIGCHLD
, handle_sigchld
);
312 * o Child writes something to the child_fd; means that device
313 * probing is completed and the parent should exit with success
315 * o Child is killed (segfault etc.); means that parent should exit
318 * o Timeout; means that we should kill the child and exit with
324 FD_SET(child_fd
, &rfds
);
326 FD_SET(child_fd
, &efds
);
327 /* Wait up to 250 seconds for device probing */
331 retval
= select (child_fd
+ 1, &rfds
, NULL
, &efds
, &tv
);
334 /* written from handle_sigchld */
340 /* means child wrote to socket or closed it; all good */
345 /* assume timeout; kill child */
346 kill (child_pid
, SIGTERM
);
353 /*--------------------------------------------------------------------------------------------------*/
355 /** Entry point for HAL daemon
357 * @param argc Number of arguments
358 * @param argv Array of arguments
362 main (int argc
, char *argv
[])
365 guint sigterm_iochn_listener_source_id
;
369 openlog ("hald", LOG_PID
, LOG_DAEMON
);
370 #if !GLIB_CHECK_VERSION(2,35,0)
373 if (getenv ("HALD_VERBOSE"))
374 hald_is_verbose
= TRUE
;
376 hald_is_verbose
= FALSE
;
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)
382 path
= getenv ("PATH");
384 g_strlcpy (newpath
, path
, sizeof (newpath
));
385 g_strlcat (newpath
, ":", sizeof (newpath
));
387 /* No PATH was set */
391 g_strlcat (newpath
, PACKAGE_LIBEXEC_DIR
, sizeof (newpath
));
392 g_strlcat (newpath
, ":", sizeof (newpath
));
393 g_strlcat (newpath
, PACKAGE_SCRIPT_DIR
, sizeof (newpath
));
395 setenv ("PATH", newpath
, TRUE
);
399 int option_index
= 0;
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},
410 c
= getopt_long (argc
, argv
, "",
411 long_options
, &option_index
);
417 opt
= long_options
[option_index
].name
;
419 if (strcmp (opt
, "help") == 0) {
422 } else if (strcmp (opt
, "version") == 0) {
423 fprintf (stderr
, "HAL package version: " PACKAGE_VERSION
"\n");
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
;
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
;
443 } else if (strcmp (opt
, "use-syslog") == 0) {
444 hald_use_syslog
= TRUE
;
462 logger_enable_syslog ();
464 logger_disable_syslog ();
466 /* will fork into two; only the child will return here if we are successful */
467 /*master_slave_setup ();
470 loop
= g_main_loop_new (NULL
, FALSE
);
472 HAL_INFO ((PACKAGE_STRING
));
474 if (opt_become_daemon
) {
481 HAL_INFO (("Will daemonize"));
482 HAL_INFO (("Becoming a daemon"));
484 if (pipe (startup_daemonize_pipe
) != 0) {
485 fprintf (stderr
, "Could not setup pipe: %s\n", strerror(errno
));
490 if (chdir ("/") < 0) {
491 fprintf (stderr
, "Could not chdir to /: %s\n", strerror(errno
));
498 fprintf (stderr
, "Cannot fork(): %s\n", strerror(errno
));
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);
518 /* parent, block until child writes */
519 exit (parent_wait_for_child (startup_daemonize_pipe
[0], child_pid
));
526 /* remove old pid file */
527 unlink (HALD_PID_FILE
);
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
));
537 HAL_INFO (("Will not daemonize"));
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.
547 if (pipe (sigterm_unix_signal_pipe_fds
) != 0) {
548 DIE (("Could not setup pipe, errno=%d", errno
));
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"));
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
);
560 /* Finally, setup unix signal handler for TERM */
561 signal (SIGTERM
, handle_sigterm
);
563 /* set up the local dbus server */
564 if (!hald_dbus_local_server_init ())
566 /* Start the runner helper daemon */
567 if (!hald_runner_start_runner ()) {
573 /* initialize operating system specific parts */
576 hald_is_initialising
= TRUE
;
581 /* run the main loop and serve clients */
582 g_main_loop_run (loop
);
587 #ifdef HALD_MEMLEAK_DBG
588 extern int dbg_hal_device_object_delta
;
590 /* useful for valgrinding; see below */
592 my_shutdown (gpointer data
)
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
));
599 gdl
= hald_get_gdl ();
601 if (g_slist_length (gdl
->devices
) > 0) {
602 HalDevice
*d
= HAL_DEVICE(gdl
->devices
->data
);
603 hal_device_store_remove (gdl
, d
);
608 printf ("hal_device_object_delta = %d (should be zero)\n", dbg_hal_device_object_delta
);
614 osspec_probe_done (void)
619 HAL_INFO (("Device probing completed"));
621 if (!hald_dbus_init ()) {
622 hald_runner_kill_all();
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]);
631 hald_is_initialising
= FALSE
;
633 #ifdef HALD_MEMLEAK_DBG
634 g_timeout_add ((HALD_MEMLEAK_DBG
) * 1000,