1 /***************************************************************************
2 * CVSID: $Id$
3 *
4 * hald_runner.c - Interface to the hal runner helper daemon
5 *
6 * Copyright (C) 2006 Sjoerd Simons, <sjoerd@luon.net>
7 *
8 * Licensed under the Academic Free License version 2.1
9 *
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.
14 *
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.
19 *
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
23 *
24 **************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29
30 #include <sys/utsname.h>
31 #include <stdio.h>
32
33 #include <glib.h>
34 #include <dbus/dbus.h>
35 #include <dbus/dbus-glib-lowlevel.h>
36
37 #include "hald.h"
38 #include "util.h"
39 #include "logger.h"
40 #include "hald_dbus.h"
41 #include "hald_runner.h"
42
43 typedef struct {
44 HalDevice *d;
45 HalRunTerminatedCB cb;
46 gpointer data1;
47 gpointer data2;
48 } HelperData;
49
50 #define DBUS_SERVER_ADDRESS "unix:tmpdir=" HALD_SOCKET_DIR
51
52 static DBusConnection *runner_connection = NULL;
53
54 typedef struct
55 {
56 GPid pid;
57 HalDevice *device;
58 HalRunTerminatedCB cb;
59 gpointer data1;
60 gpointer data2;
61 } RunningProcess;
62
63 /* mapping from PID to RunningProcess */
64 static GHashTable *running_processes;
65
66 static gboolean
rprd_foreach(gpointer key,gpointer value,gpointer user_data)67 rprd_foreach (gpointer key,
68 gpointer value,
69 gpointer user_data)
70 {
71 gboolean remove = FALSE;
72 RunningProcess *rp = value;
73 HalDevice *device = user_data;
74
75 if (rp->device == device) {
76 remove = TRUE;
77 g_free (rp);
78 }
79
80 return remove;
81 }
82
83 static void
running_processes_remove_device(HalDevice * device)84 running_processes_remove_device (HalDevice *device)
85 {
86 g_hash_table_foreach_remove (running_processes, rprd_foreach, device);
87 }
88
89 void
runner_device_finalized(HalDevice * device)90 runner_device_finalized (HalDevice *device)
91 {
92 running_processes_remove_device (device);
93 }
94
95
96 static DBusHandlerResult
runner_server_message_handler(DBusConnection * connection,DBusMessage * message,void * user_data)97 runner_server_message_handler (DBusConnection *connection,
98 DBusMessage *message,
99 void *user_data)
100 {
101
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")) {
110 dbus_uint64_t dpid;
111 DBusError error;
112 dbus_error_init (&error);
113 if (dbus_message_get_args (message, &error,
114 DBUS_TYPE_INT64, &dpid,
115 DBUS_TYPE_INVALID)) {
116 RunningProcess *rp;
117 GPid pid;
118
119 pid = (GPid) dpid;
120
121 /*HAL_INFO (("Previously started process with pid %d exited", pid));*/
122 rp = g_hash_table_lookup (running_processes, (gpointer) pid);
123 if (rp != NULL) {
124 rp->cb (rp->device, 0, 0, NULL, rp->data1, rp->data2);
125 g_hash_table_remove (running_processes, (gpointer) pid);
126 g_free (rp);
127 }
128 }
129 }
130 return DBUS_HANDLER_RESULT_HANDLED;
131 }
132
133 static void
runner_server_unregister_handler(DBusConnection * connection,void * user_data)134 runner_server_unregister_handler (DBusConnection *connection, void *user_data)
135 {
136 HAL_INFO (("unregistered"));
137 }
138
139
140 static void
handle_connection(DBusServer * server,DBusConnection * new_connection,void * data)141 handle_connection(DBusServer *server,
142 DBusConnection *new_connection,
143 void *data)
144 {
145
146 if (runner_connection == NULL) {
147 DBusObjectPathVTable vtable = { &runner_server_unregister_handler,
148 &runner_server_message_handler,
149 NULL, NULL, NULL, NULL};
150
151 runner_connection = new_connection;
152 dbus_connection_ref (new_connection);
153 dbus_connection_setup_with_g_main (new_connection, NULL);
154
155 dbus_connection_register_fallback (new_connection,
156 "/org/freedesktop",
157 &vtable,
158 NULL);
159
160 /* dbus_server_unref(server); */
161
162 }
163 }
164
165 static void
runner_died(GPid pid,gint status,gpointer data)166 runner_died(GPid pid, gint status, gpointer data) {
167 g_spawn_close_pid (pid);
168 DIE (("Runner died"));
169 }
170
171 gboolean
hald_runner_start_runner(void)172 hald_runner_start_runner(void)
173 {
174 DBusServer *server = NULL;
175 DBusError err;
176 GError *error = NULL;
177 GPid pid;
178 char *argv[] = { NULL, NULL};
179 char *env[] = { NULL, NULL, NULL, NULL};
180 const char *hald_runner_path;
181 char *server_addr;
182
183 running_processes = g_hash_table_new (g_direct_hash, g_direct_equal);
184
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"));
189 goto error;
190 }
191
192 dbus_server_setup_with_g_main(server, NULL);
193 dbus_server_set_new_connection_function(server, handle_connection,
194 NULL, NULL);
195
196
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);
204 } else {
205 env[1] = g_strdup_printf ("PATH=" PACKAGE_LIBEXEC_DIR ":" PACKAGE_SCRIPT_DIR ":" PACKAGE_BIN_DIR);
206 }
207
208 /*env[2] = "DBUS_VERBOSE=1";*/
209
210
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);
215 goto error;
216 }
217 g_free(env[0]);
218 g_free(env[1]);
219
220 HAL_INFO (("Runner has pid %d", pid));
221
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);
226 }
227 return TRUE;
228
229 error:
230 if (server != NULL)
231 dbus_server_unref(server);
232 return FALSE;
233 }
234
235 static gboolean
add_property_to_msg(HalDevice * device,HalProperty * property,gpointer user_data)236 add_property_to_msg (HalDevice *device, HalProperty *property,
237 gpointer user_data)
238 {
239 char *prop_upper, *value;
240 char *c;
241 gchar *env;
242 DBusMessageIter *iter = (DBusMessageIter *)user_data;
243
244 prop_upper = g_ascii_strup (hal_property_get_key (property), -1);
245
246 /* periods aren't valid in the environment, so replace them with
247 * underscores. */
248 for (c = prop_upper; *c; c++) {
249 if (*c == '.')
250 *c = '_';
251 }
252
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);
256
257 g_free (env);
258 g_free (value);
259 g_free (prop_upper);
260
261 return TRUE;
262 }
263
264 static void
add_env(DBusMessageIter * iter,const gchar * key,const gchar * value)265 add_env(DBusMessageIter *iter, const gchar *key, const gchar *value) {
266 gchar *env;
267 env = g_strdup_printf ("%s=%s", key, value);
268 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &env);
269 g_free(env);
270 }
271
272 static void
add_basic_env(DBusMessageIter * iter,const gchar * udi)273 add_basic_env(DBusMessageIter *iter, const gchar *udi) {
274 struct utsname un;
275 char *server_addr;
276
277 if (hald_is_verbose) {
278 add_env(iter, "HALD_VERBOSE", "1");
279 }
280 if (hald_is_initialising) {
281 add_env(iter, "HALD_STARTUP", "1");
282 }
283 if (hald_use_syslog) {
284 add_env(iter, "HALD_USE_SYSLOG", "1");
285 }
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);
290 #ifdef HAVE_POLKIT
291 add_env(iter, "HAVE_POLKIT", "1");
292 #endif
293
294 if (uname(&un) >= 0) {
295 char *sysname;
296
297 sysname = g_ascii_strdown(un.sysname, -1);
298 add_env(iter, "HALD_UNAME_S", sysname);
299 g_free(sysname);
300 }
301 }
302
303 static void
add_extra_env(DBusMessageIter * iter,gchar ** env)304 add_extra_env(DBusMessageIter *iter, gchar **env) {
305 int i;
306 if (env != NULL) for (i = 0; env[i] != NULL; i++) {
307 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &env[i]);
308 }
309 }
310
311 static gboolean
add_command(DBusMessageIter * iter,const gchar * command_line)312 add_command(DBusMessageIter *iter, const gchar *command_line) {
313 gint argc;
314 gint x;
315 char **argv;
316 GError *err = NULL;
317 DBusMessageIter array_iter;
318
319 if (!g_shell_parse_argv(command_line, &argc, &argv, &err)) {
320 HAL_ERROR (("Error parsing commandline '%s': %s",
321 command_line, err->message));
322 g_error_free (err);
323 return FALSE;
324 }
325 if (!dbus_message_iter_open_container(iter,
326 DBUS_TYPE_ARRAY,
327 DBUS_TYPE_STRING_AS_STRING,
328 &array_iter))
329 DIE (("No memory"));
330 for (x = 0 ; argv[x] != NULL; x++) {
331 dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &argv[x]);
332 }
333 dbus_message_iter_close_container(iter, &array_iter);
334
335 g_strfreev(argv);
336 return TRUE;
337 }
338
339 static gboolean
add_first_part(DBusMessageIter * iter,HalDevice * device,const gchar * command_line,char ** extra_env)340 add_first_part(DBusMessageIter *iter, HalDevice *device,
341 const gchar *command_line, char **extra_env) {
342 DBusMessageIter array_iter;
343 const char *udi;
344
345 udi = hal_device_get_udi(device);
346 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &udi);
347
348 dbus_message_iter_open_container(iter,
349 DBUS_TYPE_ARRAY,
350 DBUS_TYPE_STRING_AS_STRING,
351 &array_iter);
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);
356
357 if (!add_command(iter, command_line)) {
358 return FALSE;
359 }
360 return TRUE;
361 }
362
363 /* Start a helper, returns true on a successfull start */
364 gboolean
hald_runner_start(HalDevice * device,const gchar * command_line,char ** extra_env,HalRunTerminatedCB cb,gpointer data1,gpointer data2)365 hald_runner_start (HalDevice *device, const gchar *command_line, char **extra_env,
366 HalRunTerminatedCB cb, gpointer data1, gpointer data2)
367 {
368 DBusMessage *msg, *reply;
369 DBusError err;
370 DBusMessageIter iter;
371
372 dbus_error_init(&err);
373 msg = dbus_message_new_method_call("org.freedesktop.HalRunner",
374 "/org/freedesktop/HalRunner",
375 "org.freedesktop.HalRunner",
376 "Start");
377 if (msg == NULL)
378 DIE(("No memory"));
379 dbus_message_iter_init_append(msg, &iter);
380
381 if (!add_first_part(&iter, device, command_line, extra_env))
382 goto error;
383
384 /* Wait for the reply, should be almost instantanious */
385 reply =
386 dbus_connection_send_with_reply_and_block(runner_connection,
387 msg, -1, &err);
388 if (reply) {
389 gboolean ret =
390 (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_METHOD_RETURN);
391
392 if (ret) {
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)) {
397 if (cb != NULL) {
398 RunningProcess *rp;
399 rp = g_new0 (RunningProcess, 1);
400 rp->pid = (GPid) pid_from_runner;
401 rp->cb = cb;
402 rp->device = device;
403 rp->data1 = data1;
404 rp->data2 = data2;
405
406 g_hash_table_insert (running_processes, (gpointer) rp->pid, rp);
407 }
408 } else {
409 HAL_ERROR (("Error extracting out_pid from runner's Start()"));
410 }
411 }
412
413 dbus_message_unref(reply);
414 dbus_message_unref(msg);
415 return ret;
416 }
417
418 error:
419 dbus_message_unref(msg);
420 return FALSE;
421 }
422
423 static void
call_notify(DBusPendingCall * pending,void * user_data)424 call_notify(DBusPendingCall *pending, void *user_data)
425 {
426 HelperData *hb = (HelperData *)user_data;
427 dbus_uint32_t exitt = HALD_RUN_SUCCESS;
428 dbus_int32_t return_code = 0;
429 DBusMessage *m;
430 GArray *error = NULL;
431 DBusMessageIter iter;
432
433 error = g_array_new(TRUE, FALSE, sizeof(char *));
434
435 m = dbus_pending_call_steal_reply(pending);
436 if (dbus_message_get_type(m) != DBUS_MESSAGE_TYPE_METHOD_RETURN)
437 goto malformed;
438
439 if (!dbus_message_iter_init(m, &iter) ||
440 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
441 goto malformed;
442 dbus_message_iter_get_basic(&iter, &exitt);
443
444 if (!dbus_message_iter_next(&iter) ||
445 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32)
446 goto malformed;
447 dbus_message_iter_get_basic(&iter, &return_code);
448
449 while (dbus_message_iter_next(&iter) &&
450 dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
451 const char *value;
452 dbus_message_iter_get_basic(&iter, &value);
453 g_array_append_vals(error, &value, 1);
454 }
455
456 hb->cb(hb->d, exitt, return_code,
457 (gchar **)error->data, hb->data1, hb->data2);
458
459 g_object_unref (hb->d);
460
461 dbus_message_unref(m);
462 dbus_pending_call_unref (pending);
463 g_array_free(error, TRUE);
464
465 return;
466 malformed:
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);
470
471 g_object_unref (hb->d);
472
473 dbus_message_unref(m);
474 dbus_pending_call_unref (pending);
475 g_array_free(error, TRUE);
476 }
477
478 /* Run a helper program using the commandline, with input as infomation on
479 * stdin */
480 void
hald_runner_run_method(HalDevice * device,const gchar * command_line,char ** extra_env,gchar * input,gboolean error_on_stderr,guint32 timeout,HalRunTerminatedCB cb,gpointer data1,gpointer data2)481 hald_runner_run_method(HalDevice *device,
482 const gchar *command_line, char **extra_env,
483 gchar *input, gboolean error_on_stderr,
484 guint32 timeout,
485 HalRunTerminatedCB cb,
486 gpointer data1, gpointer data2) {
487 DBusMessage *msg;
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",
494 "Run");
495 if (msg == NULL)
496 DIE(("No memory"));
497 dbus_message_iter_init_append(msg, &iter);
498
499 if (!add_first_part(&iter, device, command_line, extra_env))
500 goto error;
501
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);
505
506 if (!dbus_connection_send_with_reply(runner_connection,
507 msg, &call, INT_MAX))
508 DIE (("No memory"));
509
510 /* the connection was disconnected */
511 if (call == NULL)
512 goto error;
513
514 hd = malloc(sizeof(HelperData));
515 hd->d = device;
516 hd->cb = cb;
517 hd->data1 = data1;
518 hd->data2 = data2;
519
520 g_object_ref (device);
521
522 dbus_pending_call_set_notify(call, call_notify, hd, free);
523 dbus_message_unref(msg);
524 return;
525 error:
526 dbus_message_unref(msg);
527 free(hd);
528 cb(device, HALD_RUN_FAILED, 0, NULL, data1, data2);
529 }
530
531 void
hald_runner_run(HalDevice * device,const gchar * command_line,char ** extra_env,guint timeout,HalRunTerminatedCB cb,gpointer data1,gpointer data2)532 hald_runner_run(HalDevice *device,
533 const gchar *command_line, char **extra_env,
534 guint timeout,
535 HalRunTerminatedCB cb,
536 gpointer data1, gpointer data2) {
537 hald_runner_run_method(device, command_line, extra_env,
538 "", FALSE, timeout, cb, data1, data2);
539 }
540
541
542
543 void
hald_runner_kill_device(HalDevice * device)544 hald_runner_kill_device(HalDevice *device) {
545 DBusMessage *msg, *reply;
546 DBusError err;
547 DBusMessageIter iter;
548 const char *udi;
549
550 running_processes_remove_device (device);
551
552 msg = dbus_message_new_method_call("org.freedesktop.HalRunner",
553 "/org/freedesktop/HalRunner",
554 "org.freedesktop.HalRunner",
555 "Kill");
556 if (msg == NULL)
557 DIE(("No memory"));
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);
561
562 /* Wait for the reply, should be almost instantanious */
563 dbus_error_init(&err);
564 reply =
565 dbus_connection_send_with_reply_and_block(runner_connection, msg, -1, &err);
566 if (reply) {
567 dbus_message_unref(reply);
568 }
569
570 dbus_message_unref(msg);
571 }
572
573 void
hald_runner_kill_all(HalDevice * device)574 hald_runner_kill_all(HalDevice *device) {
575 DBusMessage *msg, *reply;
576 DBusError err;
577
578 /* hald_runner has not yet started, just return */
579 if (runner_connection == NULL) {
580 return;
581 }
582
583 running_processes_remove_device (device);
584
585 msg = dbus_message_new_method_call("org.freedesktop.HalRunner",
586 "/org/freedesktop/HalRunner",
587 "org.freedesktop.HalRunner",
588 "KillAll");
589 if (msg == NULL)
590 DIE(("No memory"));
591
592 /* Wait for the reply, should be almost instantanious */
593 dbus_error_init(&err);
594 reply =
595 dbus_connection_send_with_reply_and_block(runner_connection,
596 msg, -1, &err);
597 if (reply) {
598 dbus_message_unref(reply);
599 }
600
601 dbus_message_unref(msg);
602 }
603