#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <pwd.h>
#include <stdint.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <grp.h>
#include <syslog.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include "logger.h"
#include "hald.h"
#include "device_store.h"
#include "device_info.h"
#include "osspec.h"
#include "hald_dbus.h"
#include "util.h"
#include "hald_runner.h"
#include "util_helper.h"
static void delete_pid(void)
{
unlink(HALD_PID_FILE);
}
static HalDeviceStore *global_device_list = NULL;
static HalDeviceStore *temporary_device_list = NULL;
static void
addon_terminated (HalDevice *device, guint32 exit_type,
gint return_code, gchar **error,
gpointer data1, gpointer data2)
{
HAL_INFO (("in addon_terminated for udi=%s", device->udi));
if (hal_device_inc_num_ready_addons (device)) {
if (hal_device_are_all_addons_ready (device)) {
manager_send_signal_device_added (device);
}
}
}
static void
gdl_store_changed (HalDeviceStore *store, HalDevice *device,
gboolean is_added, gpointer user_data)
{
if (is_added) {
GSList *addons;
HAL_INFO (("Added device to GDL; udi=%s", hal_device_get_udi(device)));
if ((addons = hal_device_property_get_strlist (device, "info.addons")) != NULL) {
GSList *i;
for (i = addons; i != NULL; i = g_slist_next (i)) {
const gchar *command_line;
gchar *extra_env[2] = {"HALD_ACTION=addon", NULL};
command_line = (const gchar *) i->data;
if (hald_runner_start(device, command_line, extra_env, addon_terminated, NULL, NULL)) {
HAL_INFO (("Started addon %s for udi %s",
command_line, hal_device_get_udi(device)));
hal_device_inc_num_addons (device);
} else {
HAL_ERROR (("Cannot start addon %s for udi %s",
command_line, hal_device_get_udi(device)));
}
}
}
} else {
HAL_INFO (("Removed device from GDL; udi=%s", hal_device_get_udi(device)));
hald_runner_kill_device(device);
}
if (is_added) {
if (hal_device_are_all_addons_ready (device)) {
manager_send_signal_device_added (device);
}
} else {
if (hal_device_are_all_addons_ready (device)) {
manager_send_signal_device_removed (device);
}
}
}
static void
gdl_property_changed (HalDeviceStore *store, HalDevice *device,
const char *key, gboolean added, gboolean removed,
gpointer user_data)
{
if (hal_device_are_all_addons_ready (device)) {
device_send_signal_property_modified (device, key, removed, added);
}
if (added == FALSE && removed == FALSE)
;
}
static void
gdl_capability_added (HalDeviceStore *store, HalDevice *device,
const char *capability, gpointer user_data)
{
if (hal_device_are_all_addons_ready (device)) {
manager_send_signal_new_capability (device, capability);
}
;
}
HalDeviceStore *
hald_get_gdl (void)
{
if (global_device_list == NULL) {
global_device_list = hal_device_store_new ();
g_signal_connect (global_device_list,
"store_changed",
G_CALLBACK (gdl_store_changed), NULL);
g_signal_connect (global_device_list,
"device_property_changed",
G_CALLBACK (gdl_property_changed), NULL);
g_signal_connect (global_device_list,
"device_capability_added",
G_CALLBACK (gdl_capability_added), NULL);
}
return global_device_list;
}
HalDeviceStore *
hald_get_tdl (void)
{
if (temporary_device_list == NULL) {
temporary_device_list = hal_device_store_new ();
}
return temporary_device_list;
}
static void
usage ()
{
fprintf (stderr, "\n" "usage : hald [--daemon=yes|no] [--verbose=yes|no] [--help]\n");
fprintf (stderr,
"\n"
" --daemon=yes|no Become a daemon\n"
" --verbose=yes|no Print out debug (overrides HALD_VERBOSE)\n"
" --use-syslog Print out debug messages to syslog instead of stderr.\n"
" Use this option to get debug messages if HAL runs as\n"
" daemon.\n"
" --help Show this information and exit\n"
" --version Output version information and exit"
"\n"
"The HAL daemon detects devices present in the system and provides the\n"
"org.freedesktop.Hal service through the system-wide message bus provided\n"
"by D-BUS.\n"
"\n"
"For more information visit http://freedesktop.org/Software/hal\n"
"\n");
}
static dbus_bool_t opt_become_daemon = TRUE;
dbus_bool_t hald_is_verbose = FALSE;
dbus_bool_t hald_use_syslog = FALSE;
static int sigterm_unix_signal_pipe_fds[2];
static GIOChannel *sigterm_iochn;
static void
handle_sigterm (int value)
{
static char marker[1] = {'S'};
(void) write (sigterm_unix_signal_pipe_fds[1], marker, 1);
}
static gboolean
sigterm_iochn_data (GIOChannel *source,
GIOCondition condition,
gpointer user_data)
{
GError *err = NULL;
gchar data[1];
gsize bytes_read;
if (G_IO_STATUS_NORMAL !=
g_io_channel_read_chars (source, data, 1, &bytes_read, &err)) {
HAL_ERROR (("Error emptying sigterm pipe: %s",
err->message));
g_error_free (err);
goto out;
}
HAL_INFO (("Caught SIGTERM, initiating shutdown"));
hald_runner_kill_all();
exit (0);
out:
return TRUE;
}
dbus_bool_t hald_is_initialising;
static int startup_daemonize_pipe[2];
static gboolean child_died = FALSE;
static void
handle_sigchld (int value)
{
child_died = TRUE;
}
static int
parent_wait_for_child (int child_fd, pid_t child_pid)
{
fd_set rfds;
fd_set efds;
struct timeval tv;
int retval;
int ret;
signal(SIGCHLD, handle_sigchld);
FD_ZERO(&rfds);
FD_SET(child_fd, &rfds);
FD_ZERO(&efds);
FD_SET(child_fd, &efds);
tv.tv_sec = 250;
tv.tv_usec = 0;
retval = select (child_fd + 1, &rfds, NULL, &efds, &tv);
if (child_died) {
ret = 1;
goto out;
}
if (retval > 0) {
ret = 0;
goto out;
}
kill (child_pid, SIGTERM);
ret = 2;
out:
return ret;
}
int
main (int argc, char *argv[])
{
GMainLoop *loop;
char *path;
char newpath[512];
openlog ("hald", LOG_PID, LOG_DAEMON);
#if !GLIB_CHECK_VERSION(2,35,0)
g_type_init ();
#endif
if (getenv ("HALD_VERBOSE"))
hald_is_verbose = TRUE;
else
hald_is_verbose = FALSE;
path = getenv ("PATH");
if (path != NULL) {
g_strlcpy (newpath, path, sizeof (newpath));
g_strlcat (newpath, ":", sizeof (newpath));
} else {
newpath[0] = '\0';
}
g_strlcat (newpath, PACKAGE_LIBEXEC_DIR, sizeof (newpath));
g_strlcat (newpath, ":", sizeof (newpath));
g_strlcat (newpath, PACKAGE_SCRIPT_DIR, sizeof (newpath));
setenv ("PATH", newpath, TRUE);
while (1) {
int c;
int option_index = 0;
const char *opt;
static struct option long_options[] = {
{"daemon", 1, NULL, 0},
{"verbose", 1, NULL, 0},
{"use-syslog", 0, NULL, 0},
{"help", 0, NULL, 0},
{"version", 0, NULL, 0},
{NULL, 0, NULL, 0}
};
c = getopt_long (argc, argv, "",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 0:
opt = long_options[option_index].name;
if (strcmp (opt, "help") == 0) {
usage ();
return 0;
} else if (strcmp (opt, "version") == 0) {
fprintf (stderr, "HAL package version: " PACKAGE_VERSION "\n");
return 0;
} else if (strcmp (opt, "daemon") == 0) {
if (strcmp ("yes", optarg) == 0) {
opt_become_daemon = TRUE;
} else if (strcmp ("no", optarg) == 0) {
opt_become_daemon = FALSE;
} else {
usage ();
return 1;
}
} else if (strcmp (opt, "verbose") == 0) {
if (strcmp ("yes", optarg) == 0) {
hald_is_verbose = TRUE;
} else if (strcmp ("no", optarg) == 0) {
hald_is_verbose = FALSE;
} else {
usage ();
return 1;
}
} else if (strcmp (opt, "use-syslog") == 0) {
hald_use_syslog = TRUE;
}
break;
default:
usage ();
return 1;
break;
}
}
if (hald_is_verbose)
logger_enable ();
else
logger_disable ();
if (hald_use_syslog)
logger_enable_syslog ();
else
logger_disable_syslog ();
loop = g_main_loop_new (NULL, FALSE);
HAL_INFO ((PACKAGE_STRING));
if (opt_become_daemon) {
int child_pid;
int dev_null_fd;
int pf;
char pid[9];
HAL_INFO (("Will daemonize"));
HAL_INFO (("Becoming a daemon"));
if (pipe (startup_daemonize_pipe) != 0) {
fprintf (stderr, "Could not setup pipe: %s\n", strerror(errno));
exit (1);
}
if (chdir ("/") < 0) {
fprintf (stderr, "Could not chdir to /: %s\n", strerror(errno));
exit (1);
}
child_pid = fork ();
switch (child_pid) {
case -1:
fprintf (stderr, "Cannot fork(): %s\n", strerror(errno));
break;
case 0:
dev_null_fd = open ("/dev/null", O_RDWR);
if (dev_null_fd >= 0) {
dup2 (dev_null_fd, 0);
dup2 (dev_null_fd, 1);
dup2 (dev_null_fd, 2);
close (dev_null_fd);
}
umask (022);
break;
default:
exit (parent_wait_for_child (startup_daemonize_pipe[0], child_pid));
break;
}
setsid ();
unlink (HALD_PID_FILE);
if ((pf= open (HALD_PID_FILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644)) > 0) {
snprintf (pid, sizeof(pid), "%lu\n", (long unsigned) getpid ());
(void) write (pf, pid, strlen(pid));
close (pf);
atexit (delete_pid);
}
} else {
HAL_INFO (("Will not daemonize"));
}
if (pipe (sigterm_unix_signal_pipe_fds) != 0) {
DIE (("Could not setup pipe, errno=%d", errno));
}
sigterm_iochn = g_io_channel_unix_new (sigterm_unix_signal_pipe_fds[0]);
if (sigterm_iochn == NULL)
DIE (("Could not create GIOChannel"));
(void) g_io_add_watch (
sigterm_iochn, G_IO_IN, sigterm_iochn_data, NULL);
signal (SIGTERM, handle_sigterm);
if (!hald_dbus_local_server_init ())
return 1;
if (!hald_runner_start_runner ()) {
return 1;
}
drop_privileges(0);
osspec_init ();
hald_is_initialising = TRUE;
osspec_probe ();
g_main_loop_run (loop);
return 0;
}
#ifdef HALD_MEMLEAK_DBG
extern int dbg_hal_device_object_delta;
static gboolean
my_shutdown (gpointer data)
{
HalDeviceStore *gdl;
printf ("Num devices in TDL: %d\n", g_slist_length ((hald_get_tdl ())->devices));
printf ("Num devices in GDL: %d\n", g_slist_length ((hald_get_gdl ())->devices));
gdl = hald_get_gdl ();
next:
if (g_slist_length (gdl->devices) > 0) {
HalDevice *d = HAL_DEVICE(gdl->devices->data);
hal_device_store_remove (gdl, d);
g_object_unref (d);
goto next;
}
printf ("hal_device_object_delta = %d (should be zero)\n", dbg_hal_device_object_delta);
exit (1);
}
#endif
void
osspec_probe_done (void)
{
char buf[1] = {0};
HAL_INFO (("Device probing completed"));
if (!hald_dbus_init ()) {
hald_runner_kill_all();
exit (1);
}
(void) write (startup_daemonize_pipe[1], buf, sizeof (buf));
close (startup_daemonize_pipe[0]);
close (startup_daemonize_pipe[1]);
hald_is_initialising = FALSE;
#ifdef HALD_MEMLEAK_DBG
g_timeout_add ((HALD_MEMLEAK_DBG) * 1000,
my_shutdown,
NULL);
#endif
}