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>
8
* Copyright (C) 2007 Codethink Ltd. Author Rob Taylor <rob.taylor@codethink.co.uk>
10
* Licensed under the Academic Free License version 2.1
12
* This program is free software; you can redistribute it and/or modify
13
* it under the terms of the GNU General Public License as published by
14
* the Free Software Foundation; either version 2 of the License, or
15
* (at your option) any later version.
17
* This program is distributed in the hope that it will be useful,
18
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
* GNU General Public License for more details.
22
* You should have received a copy of the GNU General Public License
23
* along with this program; if not, write to the Free Software
24
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26
**************************************************************************/
51
#include <sys/resource.h>
52
#include <dbus/dbus.h>
53
#include <dbus/dbus-glib.h>
54
#include <dbus/dbus-glib-lowlevel.h>
56
/*#include "master_slave.h"*/
60
#include "device_store.h"
61
#include "device_info.h"
63
#include "hald_dbus.h"
65
#include "hald_runner.h"
66
#include "util_helper.h"
67
#include "mmap_cache.h"
69
static void delete_pid(void)
71
unlink(HALD_PID_FILE);
74
static HalDeviceStore *global_device_list = NULL;
76
static HalDeviceStore *temporary_device_list = NULL;
80
addon_terminated (HalDevice *device, guint32 exit_type,
81
gint return_code, gchar **error,
82
gpointer data1, gpointer data2)
84
HAL_INFO (("in addon_terminated for udi=%s", hal_device_get_udi (device)));
86
/* TODO: log to syslog - addons shouldn't just terminate, this is a bug with the addon */
88
/* however, the world can stop, mark this addon as ready
89
* (TODO: potential bug if the addon crashed after calling libhal_device_addon_is_ready())
91
if (hal_device_inc_num_ready_addons (device)) {
92
if (hal_device_are_all_addons_ready (device)) {
93
manager_send_signal_device_added (device);
102
gdl_store_changed (HalDeviceStore *store, HalDevice *device,
103
gboolean is_added, gpointer user_data)
106
HalDeviceStrListIter iter;
108
HAL_INFO (("Added device to GDL; udi=%s", hal_device_get_udi(device)));
110
for (hal_device_property_strlist_iter_init (device, "info.addons", &iter);
111
hal_device_property_strlist_iter_is_valid (&iter);
112
hal_device_property_strlist_iter_next (&iter)) {
113
const gchar *command_line;
114
gchar *extra_env[2] = {"HALD_ACTION=addon", NULL};
116
command_line = hal_device_property_strlist_iter_get_value (&iter);
118
if (hald_runner_start(device, command_line, extra_env, addon_terminated, NULL, NULL)) {
119
HAL_INFO (("Started addon %s for udi %s",
120
command_line, hal_device_get_udi(device)));
121
hal_device_inc_num_addons (device);
123
HAL_ERROR (("Cannot start addon %s for udi %s",
124
command_line, hal_device_get_udi(device)));
127
for (hal_device_property_strlist_iter_init (device, "info.addons.singleton", &iter);
128
hal_device_property_strlist_iter_is_valid (&iter);
129
hal_device_property_strlist_iter_next (&iter)) {
130
const gchar *command_line;
132
command_line = hal_device_property_strlist_iter_get_value (&iter);
134
if (hald_singleton_device_added (command_line, device))
135
hal_device_inc_num_addons (device);
137
HAL_ERROR(("Couldn't add device to singleton"));
141
HalDeviceStrListIter iter;
143
HAL_INFO (("Removed device from GDL; udi=%s", hal_device_get_udi(device)));
144
for (hal_device_property_strlist_iter_init (device, "info.addons.singleton", &iter);
145
hal_device_property_strlist_iter_is_valid (&iter);
146
hal_device_property_strlist_iter_next (&iter)) {
147
const gchar *command_line;
149
command_line = hal_device_property_strlist_iter_get_value (&iter);
151
hald_singleton_device_removed (command_line, device);
154
hald_runner_kill_device(device);
157
/*hal_device_print (device);*/
160
if (hal_device_are_all_addons_ready (device)) {
161
manager_send_signal_device_added (device);
164
if (hal_device_are_all_addons_ready (device)) {
165
manager_send_signal_device_removed (device);
171
gdl_property_changed (HalDeviceStore *store, HalDevice *device,
172
const char *key, gboolean added, gboolean removed,
175
if (hal_device_are_all_addons_ready (device)) {
176
device_send_signal_property_modified (device, key, removed, added);
179
/* only execute the callouts if the property _changed_ */
180
if (added == FALSE && removed == FALSE)
181
/*hal_callout_property (device, key)*/;
185
gdl_capability_added (HalDeviceStore *store, HalDevice *device,
186
const char *capability, gpointer user_data)
188
if (hal_device_are_all_addons_ready (device)) {
189
manager_send_signal_new_capability (device, capability);
191
/*hal_callout_capability (device, capability, TRUE)*/;
195
gdl_lock_acquired (HalDeviceStore *store, HalDevice *device, const char *lock_name, const char *lock_owner)
197
if (hal_device_are_all_addons_ready (device)) {
198
if (strncmp (lock_name, "Global.", 7) == 0 &&
199
strcmp (hal_device_get_udi (device), "/org/freedesktop/Hal/devices/computer") == 0) {
200
manager_send_signal_interface_lock_acquired (lock_name + 7, lock_owner);
202
device_send_signal_interface_lock_acquired (device, lock_name, lock_owner);
208
gdl_lock_released (HalDeviceStore *store, HalDevice *device, const char *lock_name, const char *lock_owner)
210
if (hal_device_are_all_addons_ready (device)) {
211
if (strncmp (lock_name, "Global.", 7) == 0 &&
212
strcmp (hal_device_get_udi (device), "/org/freedesktop/Hal/devices/computer") == 0) {
213
manager_send_signal_interface_lock_released (lock_name + 7, lock_owner);
215
device_send_signal_interface_lock_released (device, lock_name, lock_owner);
223
if (global_device_list == NULL) {
224
global_device_list = hal_device_store_new ();
226
g_signal_connect (global_device_list,
228
G_CALLBACK (gdl_store_changed), NULL);
229
g_signal_connect (global_device_list,
230
"device_property_changed",
231
G_CALLBACK (gdl_property_changed), NULL);
232
g_signal_connect (global_device_list,
233
"device_capability_added",
234
G_CALLBACK (gdl_capability_added), NULL);
235
g_signal_connect (global_device_list,
236
"device_lock_acquired",
237
G_CALLBACK (gdl_lock_acquired), NULL);
238
g_signal_connect (global_device_list,
239
"device_lock_released",
240
G_CALLBACK (gdl_lock_released), NULL);
243
return global_device_list;
249
if (temporary_device_list == NULL) {
250
temporary_device_list = hal_device_store_new ();
254
return temporary_device_list;
258
hald_compute_udi (gchar *dst, gsize dstsize, const gchar *format, ...)
264
va_start (args, format);
265
hal_util_compute_udi_valist (hald_get_gdl (), dst, dstsize, format, args);
268
hal_util_validate_udi (dst, dstsize);
270
if (hal_device_store_find (hald_get_gdl (), dst) == NULL &&
271
hal_device_store_find (hald_get_tdl (), dst) == NULL)
275
g_snprintf (buf, sizeof(buf), "%s_%d", dst, i);
276
if (hal_device_store_find (hald_get_gdl (), buf) == NULL &&
277
hal_device_store_find (hald_get_tdl (), buf) == NULL) {
278
g_strlcpy (dst, buf, dstsize);
291
* Print out program usage.
296
fprintf (stderr, "\n" "usage : hald [--daemon=yes|no] [--verbose=yes|no] [--help]\n");
299
" --daemon=yes|no Become a daemon\n"
300
" --verbose=yes|no Print out debug (overrides HALD_VERBOSE)\n"
301
" --retain-privileges Retain privileges (for debugging)\n"
302
" --child-timeout=time Set this timout for the child prober. A larger\n"
303
" number than the default 250s is required for systems\n"
304
" with many resources to be probed at boot time\n"
305
" --use-syslog Print out debug messages to syslog instead of\n"
306
" stderr. Use this option to get debug messages\n"
307
" if hald runs as a daemon.\n"
308
" --help Show this information and exit\n"
309
" --version Output version information and exit\n"
310
" --exit-after-probing Exit when probing is complete. Useful only\n"
311
" when profiling hald.\n"
313
"The HAL daemon detects devices present in the system and provides the\n"
314
"org.freedesktop.Hal service through the system-wide message bus provided\n"
317
"For more information visit http://freedesktop.org/Software/hal\n"
321
/** If #TRUE, we will daemonize */
322
static dbus_bool_t opt_become_daemon = TRUE;
324
/** If #TRUE, we will retain privs */
325
static dbus_bool_t opt_retain_privileges = FALSE;
327
/** If #TRUE, we will spew out debug */
328
dbus_bool_t hald_is_verbose = FALSE;
329
dbus_bool_t hald_use_syslog = FALSE;
330
static dbus_bool_t hald_debug_exit_after_probing = FALSE;
333
PolKitContext *pk_context;
336
static int sigterm_unix_signal_pipe_fds[2];
337
static GIOChannel *sigterm_iochn;
340
handle_sigterm (int value)
343
static char marker[1] = {'S'};
345
/* write a 'S' character to the other end to tell about
346
* the signal. Note that 'the other end' is a GIOChannel thingy
347
* that is only called from the mainloop - thus this is how we
348
* defer this since UNIX signal handlers are evil
350
* Oh, and write(2) is indeed reentrant */
351
written = write (sigterm_unix_signal_pipe_fds[1], marker, 1);
355
sigterm_iochn_data (GIOChannel *source,
356
GIOCondition condition,
364
if (G_IO_STATUS_NORMAL !=
365
g_io_channel_read_chars (source, data, 1, &bytes_read, &err)) {
366
HAL_ERROR (("Error emptying sigterm pipe: %s",
372
HAL_INFO (("Caught SIGTERM, initiating shutdown"));
373
hald_runner_kill_all();
381
/** This is set to #TRUE if we are probing and #FALSE otherwise */
382
dbus_bool_t hald_is_initialising;
384
static int startup_daemonize_pipe[2];
387
/*--------------------------------------------------------------------------------------------------*/
389
static gboolean child_died = FALSE;
392
handle_sigchld (int value)
398
parent_wait_for_child (guint timeout, int child_fd, pid_t child_pid)
406
signal(SIGCHLD, handle_sigchld);
410
* o Child writes something to the child_fd; means that device
411
* probing is completed and the parent should exit with success
413
* o Child is killed (segfault etc.); means that parent should exit
416
* o Timeout; means that we should kill the child and exit with
422
FD_SET(child_fd, &rfds);
424
FD_SET(child_fd, &efds);
425
/* Wait up to a set time for device probing */
429
retval = select (child_fd + 1, &rfds, NULL, &efds, &tv);
432
/* written from handle_sigchld */
438
/* means child wrote to socket or closed it; all good */
443
/* assume timeout; kill child */
444
kill (child_pid, SIGTERM);
451
/*--------------------------------------------------------------------------------------------------*/
456
_polkit_io_watch_have_data (GIOChannel *channel, GIOCondition condition, gpointer user_data)
459
PolKitContext *pk_context = user_data;
460
fd = g_io_channel_unix_get_fd (channel);
461
polkit_context_io_func (pk_context, fd);
466
_polkit_io_add_watch (PolKitContext *pk_context, int fd)
470
channel = g_io_channel_unix_new (fd);
473
id = g_io_add_watch (channel, G_IO_IN, _polkit_io_watch_have_data, pk_context);
475
g_io_channel_unref (channel);
478
g_io_channel_unref (channel);
484
_polkit_io_remove_watch (PolKitContext *pk_context, int watch_id)
486
g_source_remove (watch_id);
489
static guint _polkit_cooloff_timer = 0;
492
_polkit_config_really_changed (gpointer user_data)
494
HAL_INFO (("Acting on PolicyKit config change"));
495
_polkit_cooloff_timer = 0;
496
reconfigure_all_policy ();
501
_polkit_config_changed_cb (PolKitContext *pk_context, gpointer user_data)
503
HAL_INFO (("PolicyKit reports that the config have changed; starting cool-off timer"));
505
/* Start a cool-off timer since we get a lot of events within
506
* a short time-frame...
509
if (_polkit_cooloff_timer > 0) {
510
/* cancel old cool-off timer */
511
g_source_remove (_polkit_cooloff_timer);
512
HAL_INFO (("restarting cool-off timer"));
514
#ifdef HAVE_GLIB_2_14
515
_polkit_cooloff_timer = g_timeout_add_seconds (1,
516
_polkit_config_really_changed,
519
_polkit_cooloff_timer = g_timeout_add (1000, /* one second... */
520
_polkit_config_really_changed,
525
#endif /* HAVE_POLKIT */
530
* @argc: Number of arguments
531
* @argv: Array of arguments
534
* Entry point for HAL daemon
537
main (int argc, char *argv[])
540
guint sigterm_iochn_listener_source_id;
541
guint opt_child_timeout;
543
PolKitError *p_error;
545
openlog ("hald", LOG_PID, LOG_DAEMON);
549
#define HAL_MMAP_THRESHOLD 100
550
#define HAL_TRIM_THRESHOLD 100
552
/* We use memory in small chunks, thus optimize
555
mallopt(M_MMAP_THRESHOLD, HAL_MMAP_THRESHOLD);
556
mallopt(M_TRIM_THRESHOLD, HAL_TRIM_THRESHOLD);
560
#ifdef HALD_MEMLEAK_DBG
561
/*g_mem_set_vtable (glib_mem_profiler_table);*/
566
if (getenv ("HALD_VERBOSE"))
567
hald_is_verbose = TRUE;
569
hald_is_verbose = FALSE;
571
/* our helpers are installed into libexec, so adjust out $PATH
572
* to include this at the end (since we want to overide in
573
* run-hald.sh and friends)
575
GString *path = g_string_new(getenv("PATH"));
576
if ( path->len > 0 ) {
577
g_string_append_c(path, ':');
580
g_string_append_printf(path, "%s:%s", PACKAGE_LIBEXEC_DIR, PACKAGE_SCRIPT_DIR);
582
setenv ("PATH", path->str, TRUE);
583
g_string_free(path, TRUE);
585
/* set the default child timeout to 250 seconds */
586
opt_child_timeout = 250;
590
int option_index = 0;
592
static struct option long_options[] = {
593
{"exit-after-probing", 0, NULL, 0},
594
{"daemon", 1, NULL, 0},
595
{"verbose", 1, NULL, 0},
596
{"retain-privileges", 0, NULL, 0},
597
{"child-timeout", 1, NULL, 0},
598
{"use-syslog", 0, NULL, 0},
599
{"help", 0, NULL, 0},
600
{"version", 0, NULL, 0},
604
c = getopt_long (argc, argv, "",
605
long_options, &option_index);
611
opt = long_options[option_index].name;
613
if (strcmp (opt, "help") == 0) {
616
} else if (strcmp (opt, "version") == 0) {
617
fprintf (stderr, "HAL package version: " PACKAGE_VERSION "\n");
619
} else if (strcmp (opt, "exit-after-probing") == 0) {
620
hald_debug_exit_after_probing = TRUE;
621
} else if (strcmp (opt, "child-timeout") == 0) {
622
opt_child_timeout = atoi (optarg);
623
} else if (strcmp (opt, "daemon") == 0) {
624
if (strcmp ("yes", optarg) == 0) {
625
opt_become_daemon = TRUE;
626
} else if (strcmp ("no", optarg) == 0) {
627
opt_become_daemon = FALSE;
632
} else if (strcmp (opt, "verbose") == 0) {
633
if (strcmp ("yes", optarg) == 0) {
634
hald_is_verbose = TRUE;
635
} else if (strcmp ("no", optarg) == 0) {
636
hald_is_verbose = FALSE;
641
} else if (strcmp (opt, "retain-privileges") == 0) {
642
opt_retain_privileges = TRUE;
643
} else if (strcmp (opt, "use-syslog") == 0) {
644
hald_use_syslog = TRUE;
662
logger_enable_syslog ();
664
logger_disable_syslog ();
666
/* will fork into two; only the child will return here if we are successful */
667
/*master_slave_setup ();
671
loop = g_main_loop_new (NULL, FALSE);
673
HAL_INFO ((PACKAGE_STRING));
674
HAL_INFO (("using child timeout %is", opt_child_timeout));
676
if (opt_become_daemon) {
683
HAL_INFO (("Will daemonize"));
684
HAL_INFO (("Becoming a daemon"));
686
if (pipe (startup_daemonize_pipe) != 0) {
687
fprintf (stderr, "Could not setup pipe: %s\n", strerror(errno));
692
if (chdir ("/") < 0) {
693
fprintf (stderr, "Could not chdir to /: %s\n", strerror(errno));
700
fprintf (stderr, "Cannot fork(): %s\n", strerror(errno));
706
dev_null_fd = open ("/dev/null", O_RDWR);
707
/* ignore if we can't open /dev/null */
708
if (dev_null_fd >= 0) {
709
/* attach /dev/null to stdout, stdin, stderr */
710
dup2 (dev_null_fd, 0);
711
dup2 (dev_null_fd, 1);
712
dup2 (dev_null_fd, 2);
720
/* parent, block until child writes */
721
exit (parent_wait_for_child (opt_child_timeout, startup_daemonize_pipe[0], child_pid));
728
/* remove old pid file */
729
unlink (HALD_PID_FILE);
732
if ((pf= open (HALD_PID_FILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644)) > 0) {
733
snprintf (pid, sizeof(pid), "%lu\n", (long unsigned) getpid ());
734
written = write (pf, pid, strlen(pid));
739
HAL_INFO (("Will not daemonize"));
742
/* we need to do stuff when we are expected to terminate, thus
743
* this involves looking for SIGTERM; UNIX signal handlers are
744
* evil though, so set up a pipe to transmit the signal.
748
if (pipe (sigterm_unix_signal_pipe_fds) != 0) {
749
DIE (("Could not setup pipe, errno=%d", errno));
752
/* setup glib handler - 0 is for reading, 1 is for writing */
753
sigterm_iochn = g_io_channel_unix_new (sigterm_unix_signal_pipe_fds[0]);
754
if (sigterm_iochn == NULL)
755
DIE (("Could not create GIOChannel"));
757
/* get callback when there is data to read */
758
sigterm_iochn_listener_source_id = g_io_add_watch (
759
sigterm_iochn, G_IO_IN, sigterm_iochn_data, NULL);
761
/* Finally, setup unix signal handler for TERM */
762
signal (SIGTERM, handle_sigterm);
764
/* set up the local dbus server */
765
if (!hald_dbus_local_server_init ())
768
if (!hald_dbus_init_preprobe ()) {
772
/* Start the runner helper daemon */
773
if (!hald_runner_start_runner ()) {
777
/* initialize privileged operating system specific parts */
778
osspec_privileged_init ();
782
pk_context = polkit_context_new ();
783
if (pk_context == NULL)
784
DIE (("Could not create PolicyKit context"));
785
polkit_context_set_config_changed (pk_context,
786
_polkit_config_changed_cb,
788
polkit_context_set_io_watch_functions (pk_context, _polkit_io_add_watch, _polkit_io_remove_watch);
789
if (!polkit_context_init (pk_context, &p_error))
790
DIE (("Could not init PolicyKit context: %s", polkit_error_get_error_message (p_error)));
793
/* sometimes we don't want to drop root privs, for instance
794
if we are profiling memory usage */
795
if (opt_retain_privileges == FALSE) {
799
hald_is_initialising = TRUE;
801
/* make sure our fdi rule cache is up to date and setup file monitoring */
802
di_cache_coherency_check(TRUE);
804
/* initialize operating system specific parts */
813
/* run the main loop and serve clients */
814
g_main_loop_run (loop);
819
#ifdef HALD_MEMLEAK_DBG
820
extern int dbg_hal_device_object_delta;
822
/* useful for valgrinding; see below */
824
my_shutdown2 (gpointer data)
826
/*g_mem_profile ();*/
833
my_shutdown (gpointer data)
837
HAL_INFO (("Num devices in TDL: %d", g_slist_length ((hald_get_tdl ())->devices)));
838
HAL_INFO (("Num devices in GDL: %d", g_slist_length ((hald_get_gdl ())->devices)));
840
gdl = hald_get_gdl ();
842
if (g_slist_length (gdl->devices) > 0) {
843
HalDevice *d = HAL_DEVICE(gdl->devices->data);
844
hal_device_store_remove (gdl, d);
849
HAL_INFO (("hal_device_object_delta = %d (should be zero)", dbg_hal_device_object_delta));
851
hald_dbus_local_server_shutdown ();
852
hald_runner_stop_runner ();
854
HAL_INFO (("Shutting down in five seconds"));
855
g_timeout_add (5 * 1000,
864
osspec_probe_done (void)
869
HAL_INFO (("Device probing completed"));
871
if (hald_debug_exit_after_probing) {
872
HAL_INFO (("Exiting on user request (--exit-after-probing)"));
873
hald_runner_kill_all();
878
if (!hald_dbus_init ()) {
879
hald_runner_kill_all();
883
/* tell parent to exit */
884
written = write (startup_daemonize_pipe[1], buf, sizeof (buf));
885
close (startup_daemonize_pipe[0]);
886
close (startup_daemonize_pipe[1]);
888
hald_is_initialising = FALSE;
890
#ifdef HALD_MEMLEAK_DBG
891
g_timeout_add ((HALD_MEMLEAK_DBG) * 1000,