1 /***************************************************************************
4 * hald_runner.c - Interface to the hal runner helper daemon
6 * Copyright (C) 2006 Sjoerd Simons, <sjoerd@luon.net>
8 * Licensed under the Academic Free License version 2.1
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 **************************************************************************/
30 #include <sys/utsname.h>
34 #include <dbus/dbus.h>
35 #include <dbus/dbus-glib-lowlevel.h>
40 #include "hald_dbus.h"
41 #include "hald_runner.h"
45 HalRunTerminatedCB cb
;
50 #define DBUS_SERVER_ADDRESS "unix:tmpdir=" HALD_SOCKET_DIR
52 static DBusConnection
*runner_connection
= NULL
;
58 HalRunTerminatedCB cb
;
63 /* mapping from PID to RunningProcess */
64 static GHashTable
*running_processes
;
67 rprd_foreach (gpointer key
,
71 gboolean remove
= FALSE
;
72 RunningProcess
*rp
= value
;
73 HalDevice
*device
= user_data
;
75 if (rp
->device
== device
) {
84 running_processes_remove_device (HalDevice
*device
)
86 g_hash_table_foreach_remove (running_processes
, rprd_foreach
, device
);
90 runner_device_finalized (HalDevice
*device
)
92 running_processes_remove_device (device
);
96 static DBusHandlerResult
97 runner_server_message_handler (DBusConnection
*connection
,
102 /*HAL_INFO (("runner_server_message_handler: destination=%s obj_path=%s interface=%s method=%s",
103 dbus_message_get_destination (message),
104 dbus_message_get_path (message),
105 dbus_message_get_interface (message),
106 dbus_message_get_member (message)));*/
107 if (dbus_message_is_signal (message
,
108 "org.freedesktop.HalRunner",
109 "StartedProcessExited")) {
112 dbus_error_init (&error
);
113 if (dbus_message_get_args (message
, &error
,
114 DBUS_TYPE_INT64
, &dpid
,
115 DBUS_TYPE_INVALID
)) {
121 /*HAL_INFO (("Previously started process with pid %d exited", pid));*/
122 rp
= g_hash_table_lookup (running_processes
, (gpointer
) (intptr_t) pid
);
124 rp
->cb (rp
->device
, 0, 0, NULL
, rp
->data1
, rp
->data2
);
125 g_hash_table_remove (running_processes
, (gpointer
) (intptr_t) pid
);
130 return DBUS_HANDLER_RESULT_HANDLED
;
134 runner_server_unregister_handler (DBusConnection
*connection
, void *user_data
)
136 HAL_INFO (("unregistered"));
141 handle_connection(DBusServer
*server
,
142 DBusConnection
*new_connection
,
146 if (runner_connection
== NULL
) {
147 DBusObjectPathVTable vtable
= { &runner_server_unregister_handler
,
148 &runner_server_message_handler
,
149 NULL
, NULL
, NULL
, NULL
};
151 runner_connection
= new_connection
;
152 dbus_connection_ref (new_connection
);
153 dbus_connection_setup_with_g_main (new_connection
, NULL
);
155 dbus_connection_register_fallback (new_connection
,
160 /* dbus_server_unref(server); */
166 runner_died(GPid pid
, gint status
, gpointer data
) {
167 g_spawn_close_pid (pid
);
168 DIE (("Runner died"));
172 hald_runner_start_runner(void)
174 DBusServer
*server
= NULL
;
176 GError
*error
= NULL
;
178 char *argv
[] = { NULL
, NULL
};
179 char *env
[] = { NULL
, NULL
, NULL
, NULL
};
180 const char *hald_runner_path
;
183 running_processes
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
185 dbus_error_init(&err
);
186 server
= dbus_server_listen(DBUS_SERVER_ADDRESS
, &err
);
187 if (server
== NULL
) {
188 HAL_ERROR (("Cannot create D-BUS server for the runner"));
192 dbus_server_setup_with_g_main(server
, NULL
);
193 dbus_server_set_new_connection_function(server
, handle_connection
,
197 argv
[0] = "hald-runner";
198 server_addr
= dbus_server_get_address (server
);
199 env
[0] = g_strdup_printf("HALD_RUNNER_DBUS_ADDRESS=%s", server_addr
);
200 dbus_free (server_addr
);
201 hald_runner_path
= g_getenv("HALD_RUNNER_PATH");
202 if (hald_runner_path
!= NULL
) {
203 env
[1] = g_strdup_printf ("PATH=%s:" PACKAGE_LIBEXEC_DIR
":" PACKAGE_SCRIPT_DIR
":" PACKAGE_BIN_DIR
, hald_runner_path
);
205 env
[1] = g_strdup_printf ("PATH=" PACKAGE_LIBEXEC_DIR
":" PACKAGE_SCRIPT_DIR
":" PACKAGE_BIN_DIR
);
208 /*env[2] = "DBUS_VERBOSE=1";*/
211 if (!g_spawn_async(NULL
, argv
, env
, G_SPAWN_DO_NOT_REAP_CHILD
|G_SPAWN_SEARCH_PATH
,
212 NULL
, NULL
, &pid
, &error
)) {
213 HAL_ERROR (("Could not spawn runner : '%s'", error
->message
));
214 g_error_free (error
);
220 HAL_INFO (("Runner has pid %d", pid
));
222 g_child_watch_add(pid
, runner_died
, NULL
);
223 while (runner_connection
== NULL
) {
224 /* Wait for the runner */
225 g_main_context_iteration(NULL
, TRUE
);
231 dbus_server_unref(server
);
236 add_property_to_msg (HalDevice
*device
, HalProperty
*property
,
239 char *prop_upper
, *value
;
242 DBusMessageIter
*iter
= (DBusMessageIter
*)user_data
;
244 prop_upper
= g_ascii_strup (hal_property_get_key (property
), -1);
246 /* periods aren't valid in the environment, so replace them with
248 for (c
= prop_upper
; *c
; c
++) {
253 value
= hal_property_to_string (property
);
254 env
= g_strdup_printf ("HAL_PROP_%s=%s", prop_upper
, value
);
255 dbus_message_iter_append_basic(iter
, DBUS_TYPE_STRING
, &env
);
265 add_env(DBusMessageIter
*iter
, const gchar
*key
, const gchar
*value
) {
267 env
= g_strdup_printf ("%s=%s", key
, value
);
268 dbus_message_iter_append_basic(iter
, DBUS_TYPE_STRING
, &env
);
273 add_basic_env(DBusMessageIter
*iter
, const gchar
*udi
) {
277 if (hald_is_verbose
) {
278 add_env(iter
, "HALD_VERBOSE", "1");
280 if (hald_is_initialising
) {
281 add_env(iter
, "HALD_STARTUP", "1");
283 if (hald_use_syslog
) {
284 add_env(iter
, "HALD_USE_SYSLOG", "1");
286 add_env(iter
, "UDI", udi
);
287 server_addr
= hald_dbus_local_server_addr();
288 add_env(iter
, "HALD_DIRECT_ADDR", server_addr
);
289 dbus_free (server_addr
);
291 add_env(iter
, "HAVE_POLKIT", "1");
294 if (uname(&un
) >= 0) {
297 sysname
= g_ascii_strdown(un
.sysname
, -1);
298 add_env(iter
, "HALD_UNAME_S", sysname
);
304 add_extra_env(DBusMessageIter
*iter
, gchar
**env
) {
306 if (env
!= NULL
) for (i
= 0; env
[i
] != NULL
; i
++) {
307 dbus_message_iter_append_basic(iter
, DBUS_TYPE_STRING
, &env
[i
]);
312 add_command(DBusMessageIter
*iter
, const gchar
*command_line
) {
317 DBusMessageIter array_iter
;
319 if (!g_shell_parse_argv(command_line
, &argc
, &argv
, &err
)) {
320 HAL_ERROR (("Error parsing commandline '%s': %s",
321 command_line
, err
->message
));
325 if (!dbus_message_iter_open_container(iter
,
327 DBUS_TYPE_STRING_AS_STRING
,
330 for (x
= 0 ; argv
[x
] != NULL
; x
++) {
331 dbus_message_iter_append_basic(&array_iter
, DBUS_TYPE_STRING
, &argv
[x
]);
333 dbus_message_iter_close_container(iter
, &array_iter
);
340 add_first_part(DBusMessageIter
*iter
, HalDevice
*device
,
341 const gchar
*command_line
, char **extra_env
) {
342 DBusMessageIter array_iter
;
345 udi
= hal_device_get_udi(device
);
346 dbus_message_iter_append_basic(iter
, DBUS_TYPE_STRING
, &udi
);
348 dbus_message_iter_open_container(iter
,
350 DBUS_TYPE_STRING_AS_STRING
,
352 hal_device_property_foreach (device
, add_property_to_msg
, &array_iter
);
353 add_basic_env(&array_iter
, udi
);
354 add_extra_env(&array_iter
, extra_env
);
355 dbus_message_iter_close_container(iter
, &array_iter
);
357 if (!add_command(iter
, command_line
)) {
363 /* Start a helper, returns true on a successfull start */
365 hald_runner_start (HalDevice
*device
, const gchar
*command_line
, char **extra_env
,
366 HalRunTerminatedCB cb
, gpointer data1
, gpointer data2
)
368 DBusMessage
*msg
, *reply
;
370 DBusMessageIter iter
;
372 dbus_error_init(&err
);
373 msg
= dbus_message_new_method_call("org.freedesktop.HalRunner",
374 "/org/freedesktop/HalRunner",
375 "org.freedesktop.HalRunner",
379 dbus_message_iter_init_append(msg
, &iter
);
381 if (!add_first_part(&iter
, device
, command_line
, extra_env
))
384 /* Wait for the reply, should be almost instantanious */
386 dbus_connection_send_with_reply_and_block(runner_connection
,
390 (dbus_message_get_type(reply
) == DBUS_MESSAGE_TYPE_METHOD_RETURN
);
393 dbus_int64_t pid_from_runner
;
394 if (dbus_message_get_args (reply
, &err
,
395 DBUS_TYPE_INT64
, &pid_from_runner
,
396 DBUS_TYPE_INVALID
)) {
399 rp
= g_new0 (RunningProcess
, 1);
400 rp
->pid
= (GPid
) pid_from_runner
;
406 g_hash_table_insert (running_processes
, (gpointer
) (intptr_t) rp
->pid
, rp
);
409 HAL_ERROR (("Error extracting out_pid from runner's Start()"));
413 dbus_message_unref(reply
);
414 dbus_message_unref(msg
);
419 dbus_message_unref(msg
);
424 call_notify(DBusPendingCall
*pending
, void *user_data
)
426 HelperData
*hb
= (HelperData
*)user_data
;
427 dbus_uint32_t exitt
= HALD_RUN_SUCCESS
;
428 dbus_int32_t return_code
= 0;
430 GArray
*error
= NULL
;
431 DBusMessageIter iter
;
433 error
= g_array_new(TRUE
, FALSE
, sizeof(char *));
435 m
= dbus_pending_call_steal_reply(pending
);
436 if (dbus_message_get_type(m
) != DBUS_MESSAGE_TYPE_METHOD_RETURN
)
439 if (!dbus_message_iter_init(m
, &iter
) ||
440 dbus_message_iter_get_arg_type(&iter
) != DBUS_TYPE_UINT32
)
442 dbus_message_iter_get_basic(&iter
, &exitt
);
444 if (!dbus_message_iter_next(&iter
) ||
445 dbus_message_iter_get_arg_type(&iter
) != DBUS_TYPE_INT32
)
447 dbus_message_iter_get_basic(&iter
, &return_code
);
449 while (dbus_message_iter_next(&iter
) &&
450 dbus_message_iter_get_arg_type(&iter
) == DBUS_TYPE_STRING
) {
452 dbus_message_iter_get_basic(&iter
, &value
);
453 g_array_append_vals(error
, &value
, 1);
456 hb
->cb(hb
->d
, exitt
, return_code
,
457 (gchar
**)error
->data
, hb
->data1
, hb
->data2
);
459 g_object_unref (hb
->d
);
461 dbus_message_unref(m
);
462 dbus_pending_call_unref (pending
);
463 g_array_free(error
, TRUE
);
467 /* Send a Fail callback on malformed messages */
468 HAL_ERROR (("Malformed or unexpected reply message"));
469 hb
->cb(hb
->d
, HALD_RUN_FAILED
, return_code
, NULL
, hb
->data1
, hb
->data2
);
471 g_object_unref (hb
->d
);
473 dbus_message_unref(m
);
474 dbus_pending_call_unref (pending
);
475 g_array_free(error
, TRUE
);
478 /* Run a helper program using the commandline, with input as infomation on
481 hald_runner_run_method(HalDevice
*device
,
482 const gchar
*command_line
, char **extra_env
,
483 gchar
*input
, gboolean error_on_stderr
,
485 HalRunTerminatedCB cb
,
486 gpointer data1
, gpointer data2
) {
488 DBusMessageIter iter
;
489 DBusPendingCall
*call
;
490 HelperData
*hd
= NULL
;
491 msg
= dbus_message_new_method_call("org.freedesktop.HalRunner",
492 "/org/freedesktop/HalRunner",
493 "org.freedesktop.HalRunner",
497 dbus_message_iter_init_append(msg
, &iter
);
499 if (!add_first_part(&iter
, device
, command_line
, extra_env
))
502 dbus_message_iter_append_basic(&iter
, DBUS_TYPE_STRING
, &input
);
503 dbus_message_iter_append_basic(&iter
, DBUS_TYPE_BOOLEAN
, &error_on_stderr
);
504 dbus_message_iter_append_basic(&iter
, DBUS_TYPE_UINT32
, &timeout
);
506 if (!dbus_connection_send_with_reply(runner_connection
,
507 msg
, &call
, INT_MAX
))
510 /* the connection was disconnected */
514 hd
= malloc(sizeof(HelperData
));
520 g_object_ref (device
);
522 dbus_pending_call_set_notify(call
, call_notify
, hd
, free
);
523 dbus_message_unref(msg
);
526 dbus_message_unref(msg
);
528 cb(device
, HALD_RUN_FAILED
, 0, NULL
, data1
, data2
);
532 hald_runner_run(HalDevice
*device
,
533 const gchar
*command_line
, char **extra_env
,
535 HalRunTerminatedCB cb
,
536 gpointer data1
, gpointer data2
) {
537 hald_runner_run_method(device
, command_line
, extra_env
,
538 "", FALSE
, timeout
, cb
, data1
, data2
);
544 hald_runner_kill_device(HalDevice
*device
) {
545 DBusMessage
*msg
, *reply
;
547 DBusMessageIter iter
;
550 running_processes_remove_device (device
);
552 msg
= dbus_message_new_method_call("org.freedesktop.HalRunner",
553 "/org/freedesktop/HalRunner",
554 "org.freedesktop.HalRunner",
558 dbus_message_iter_init_append(msg
, &iter
);
559 udi
= hal_device_get_udi(device
);
560 dbus_message_iter_append_basic(&iter
, DBUS_TYPE_STRING
, &udi
);
562 /* Wait for the reply, should be almost instantanious */
563 dbus_error_init(&err
);
565 dbus_connection_send_with_reply_and_block(runner_connection
, msg
, -1, &err
);
567 dbus_message_unref(reply
);
570 dbus_message_unref(msg
);
574 hald_runner_kill_all(HalDevice
*device
) {
575 DBusMessage
*msg
, *reply
;
578 /* hald_runner has not yet started, just return */
579 if (runner_connection
== NULL
) {
583 running_processes_remove_device (device
);
585 msg
= dbus_message_new_method_call("org.freedesktop.HalRunner",
586 "/org/freedesktop/HalRunner",
587 "org.freedesktop.HalRunner",
592 /* Wait for the reply, should be almost instantanious */
593 dbus_error_init(&err
);
595 dbus_connection_send_with_reply_and_block(runner_connection
,
598 dbus_message_unref(reply
);
601 dbus_message_unref(msg
);