xref: /onnv-gate/usr/src/cmd/hal/hald-runner/runner.c (revision 3502:2bf49c526f07)
12912Sartem /***************************************************************************
22912Sartem  * CVSID: $Id$
32912Sartem  *
42912Sartem  * runner.c - Process running code
52912Sartem  *
62912Sartem  * Copyright (C) 2006 Sjoerd Simons, <sjoerd@luon.net>
72912Sartem  *
82912Sartem  * Licensed under the Academic Free License version 2.1
92912Sartem  *
102912Sartem  * This program is free software; you can redistribute it and/or modify
112912Sartem  * it under the terms of the GNU General Public License as published by
122912Sartem  * the Free Software Foundation; either version 2 of the License, or
132912Sartem  * (at your option) any later version.
142912Sartem  *
152912Sartem  * This program is distributed in the hope that it will be useful,
162912Sartem  * but WITHOUT ANY WARRANTY; without even the implied warranty of
172912Sartem  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
182912Sartem  * GNU General Public License for more details.
192912Sartem  *
202912Sartem  * You should have received a copy of the GNU General Public License
212912Sartem  * along with this program; if not, write to the Free Software
222912Sartem  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
232912Sartem  *
242912Sartem  **************************************************************************/
252912Sartem #include <stdio.h>
262912Sartem #include <unistd.h>
272912Sartem #include <stdlib.h>
282912Sartem #include <sys/types.h>
292912Sartem #include <sys/stat.h>
302912Sartem #include <sys/wait.h>
312912Sartem #include <signal.h>
322912Sartem #include <string.h>
332912Sartem 
342912Sartem #define DBUS_API_SUBJECT_TO_CHANGE
352912Sartem #include <dbus/dbus-glib-lowlevel.h>
362912Sartem 
372912Sartem #include <glib.h>
382912Sartem #include "utils.h"
392912Sartem #include "runner.h"
402912Sartem 
412912Sartem /* Successful run of the program */
422912Sartem #define HALD_RUN_SUCCESS 0x0
432912Sartem /* Process was killed because of running too long */
442912Sartem #define  HALD_RUN_TIMEOUT 0x1
452912Sartem /* Failed to start for some reason */
462912Sartem #define HALD_RUN_FAILED 0x2
472912Sartem /* Killed on purpose, e.g. hal_util_kill_device_helpers */
482912Sartem #define HALD_RUN_KILLED 0x4
492912Sartem 
502912Sartem GHashTable *udi_hash = NULL;
512912Sartem 
522912Sartem typedef struct {
532912Sartem 	run_request *r;
542912Sartem 	DBusMessage *msg;
552912Sartem 	DBusConnection *con;
562912Sartem 	GPid pid;
572912Sartem 	gint stderr_v;
582912Sartem 	guint watch;
592912Sartem 	guint timeout;
602912Sartem 	gboolean sent_kill;
612912Sartem 	gboolean emit_pid_exited;
622912Sartem } run_data;
632912Sartem 
642912Sartem static void
del_run_data(run_data * rd)652912Sartem del_run_data(run_data *rd)
662912Sartem {
672912Sartem 	if (rd == NULL)
682912Sartem 		return;
692912Sartem 
702912Sartem 	del_run_request(rd->r);
712912Sartem 	if (rd->msg)
722912Sartem 		dbus_message_unref(rd->msg);
732912Sartem 
742912Sartem 	g_spawn_close_pid(rd->pid);
752912Sartem 
762912Sartem 	if (rd->stderr_v >= 0)
772912Sartem 		close(rd->stderr_v);
782912Sartem 
792912Sartem 	if (rd->timeout != 0)
802912Sartem 		g_source_remove(rd->timeout);
812912Sartem 
822912Sartem 	g_free(rd);
832912Sartem }
842912Sartem 
852912Sartem run_request *
new_run_request(void)862912Sartem new_run_request(void)
872912Sartem {
882912Sartem 	run_request *result;
892912Sartem 	result = g_new0(run_request, 1);
902912Sartem 	g_assert(result != NULL);
912912Sartem 	return result;
922912Sartem }
932912Sartem 
942912Sartem void
del_run_request(run_request * r)952912Sartem del_run_request(run_request *r)
962912Sartem {
972912Sartem 	if (r == NULL)
982912Sartem 		return;
992912Sartem 	g_free(r->udi);
1002912Sartem 	free_string_array(r->environment);
1012912Sartem 	free_string_array(r->argv);
1022912Sartem 	g_free(r->input);
1032912Sartem 	g_free(r);
1042912Sartem }
1052912Sartem 
1062912Sartem static void
send_reply(DBusConnection * con,DBusMessage * msg,guint32 exit_type,gint32 return_code,gchar ** error)1072912Sartem send_reply(DBusConnection *con, DBusMessage *msg, guint32 exit_type, gint32 return_code, gchar **error)
1082912Sartem {
1092912Sartem 	DBusMessage *reply;
1102912Sartem 	DBusMessageIter iter;
1112912Sartem 	int i;
1122912Sartem 
1132912Sartem 	if (con == NULL || msg == NULL)
1142912Sartem 		return;
1152912Sartem 
1162912Sartem 	reply = dbus_message_new_method_return(msg);
1172912Sartem 	g_assert(reply != NULL);
1182912Sartem 
1192912Sartem 	dbus_message_iter_init_append(reply, &iter);
1202912Sartem 	dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &exit_type);
1212912Sartem 	dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &return_code);
1222912Sartem 	if (error != NULL) for (i = 0; error[i] != NULL; i++) {
1232912Sartem 		dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &error[i]);
1242912Sartem 	}
1252912Sartem 
1262912Sartem 	dbus_connection_send(con, reply, NULL);
1272912Sartem 	dbus_message_unref(reply);
1282912Sartem }
1292912Sartem 
1302912Sartem static void
remove_from_hash_table(run_data * rd)1312912Sartem remove_from_hash_table(run_data *rd)
1322912Sartem {
1332912Sartem 	GList *list;
1342912Sartem 
1352912Sartem 	/* Remove to the hashtable */
1362912Sartem 	list = (GList *)g_hash_table_lookup(udi_hash, rd->r->udi);
1372912Sartem 	list = g_list_remove(list, rd);
1382912Sartem 	/* The hash table will take care to not leak the dupped string */
1392912Sartem 	g_hash_table_insert(udi_hash, g_strdup(rd->r->udi), list);
1402912Sartem }
1412912Sartem 
1422912Sartem static void
run_exited(GPid pid,gint status,gpointer data)1432912Sartem run_exited(GPid pid, gint status, gpointer data)
1442912Sartem {
1452912Sartem 	run_data *rd = (run_data *)data;
1462912Sartem 	char **error = NULL;
1472912Sartem 
1482912Sartem 	printf("%s exited\n", rd->r->argv[0]);
1492912Sartem 	rd->watch = 0;
1502912Sartem 	if (rd->sent_kill == TRUE) {
1512912Sartem 		/* We send it a kill, so ignore */
1522912Sartem 		del_run_data(rd);
1532912Sartem 		return;
1542912Sartem 	}
1552912Sartem 	/* Check if it was a normal exit */
1562912Sartem 	if (!WIFEXITED(status)) {
1572912Sartem 		/* No not normal termination ? crash ? */
1582912Sartem 		send_reply(rd->con, rd->msg, HALD_RUN_FAILED, 0, NULL);
1592912Sartem 		goto out;
1602912Sartem 	}
1612912Sartem 	/* normal exit */
1622912Sartem 	if (rd->stderr_v >= 0) {
1632912Sartem 		/* Need to read stderr */
1642912Sartem 		error = get_string_array_from_fd(rd->stderr_v);
1652912Sartem 		close(rd->stderr_v);
1662912Sartem 		rd->stderr_v = -1;
1672912Sartem 	}
1682912Sartem 	if (rd->msg != NULL)
1692912Sartem 		send_reply(rd->con, rd->msg, HALD_RUN_SUCCESS, WEXITSTATUS(status), error);
1702912Sartem 	free_string_array(error);
1712912Sartem 
1722912Sartem out:
1732912Sartem 	remove_from_hash_table(rd);
1742912Sartem 
1752912Sartem 	/* emit a signal that this PID exited */
1762912Sartem 	if(rd->con != NULL && rd->emit_pid_exited) {
1772912Sartem 		DBusMessage *signal;
1783168Sartem 		dbus_int64_t pid64;
1792912Sartem 		signal = dbus_message_new_signal ("/org/freedesktop/HalRunner",
1802912Sartem 						  "org.freedesktop.HalRunner",
1812912Sartem 						  "StartedProcessExited");
1823168Sartem 		pid64 = rd->pid;
1832912Sartem 		dbus_message_append_args (signal,
1843168Sartem 					  DBUS_TYPE_INT64, &pid64,
1852912Sartem 					  DBUS_TYPE_INVALID);
1862912Sartem 		dbus_connection_send(rd->con, signal, NULL);
1872912Sartem 	}
1882912Sartem 
1892912Sartem 	del_run_data(rd);
1902912Sartem }
1912912Sartem 
1922912Sartem static gboolean
run_timedout(gpointer data)1932912Sartem run_timedout(gpointer data) {
1942912Sartem 	run_data *rd = (run_data *)data;
1952912Sartem 	/* Time is up, kill the process, send reply that it was killed!
1962912Sartem 	 * Don't wait for exit, because it could hang in state D
1972912Sartem 	 */
1982912Sartem 	kill(rd->pid, SIGTERM);
1992912Sartem 	/* Ensure the timeout is not removed in the delete */
2002912Sartem 	rd->timeout = 0;
2012912Sartem 	/* So the exit watch will know it's killed  in case it runs*/
2022912Sartem 	rd->sent_kill = TRUE;
2032912Sartem 
2042912Sartem 	send_reply(rd->con, rd->msg, HALD_RUN_TIMEOUT, 0, NULL);
2052912Sartem 	remove_from_hash_table(rd);
2062912Sartem 	return FALSE;
2072912Sartem }
2082912Sartem 
2092912Sartem static gboolean
find_program(char ** argv)2102912Sartem find_program(char **argv)
2112912Sartem {
2122912Sartem 	/* Search for the program in the dirs where it's allowed to be */
2132912Sartem 	char *program;
2142912Sartem 	char *path = NULL;
2152912Sartem 
2162912Sartem 	if (argv[0] == NULL)
2172912Sartem 		return FALSE;
2182912Sartem 
2192912Sartem 	program = g_path_get_basename(argv[0]);
2202912Sartem 
2212912Sartem 	/* first search $PATH to make e.g. run-hald.sh work */
2222912Sartem 	path = g_find_program_in_path (program);
2232912Sartem 	g_free(program);
2242912Sartem 	if (path == NULL)
2252912Sartem 		return FALSE;
2262912Sartem 	else {
2272912Sartem 		/* Replace program in argv[0] with the full path */
2282912Sartem 		g_free(argv[0]);
2292912Sartem 		argv[0] = path;
2302912Sartem 	}
2312912Sartem 	return TRUE;
2322912Sartem }
2332912Sartem 
2342912Sartem /* Run the given request and reply it's result on msg */
2352912Sartem gboolean
run_request_run(run_request * r,DBusConnection * con,DBusMessage * msg,GPid * out_pid)2362912Sartem run_request_run (run_request *r, DBusConnection *con, DBusMessage *msg, GPid *out_pid)
2372912Sartem {
2382912Sartem 	GPid pid;
2392912Sartem 	GError *error = NULL;
2402912Sartem 	gint *stdin_p = NULL;
2412912Sartem 	gint *stderr_p = NULL;
2422912Sartem 	gint stdin_v;
2432912Sartem 	gint stderr_v = -1;
2442912Sartem 	run_data *rd = NULL;
2452912Sartem 	gboolean program_exists = FALSE;
2462912Sartem 	char *program_dir = NULL;
2472912Sartem 	GList *list;
2482912Sartem 
2492912Sartem 	printf("Run started %s (%d) (%d) \n!", r->argv[0], r->timeout,
2502912Sartem 		r->error_on_stderr);
2512912Sartem 	if (r->input != NULL) {
2522912Sartem 		stdin_p = &stdin_v;
2532912Sartem 	}
2542912Sartem 	if (r->error_on_stderr) {
2552912Sartem 		stderr_p = &stderr_v;
2562912Sartem 	}
2572912Sartem 
2582912Sartem 	program_exists = find_program(r->argv);
2592912Sartem 
2603020Sartem 	if (program_exists) {
2612912Sartem 		program_dir = g_path_get_dirname (r->argv[0]);
2623020Sartem 		printf("  full path is '%s', program_dir is '%s'\n", r->argv[0], program_dir);
2633020Sartem 	}
2642912Sartem 
2652912Sartem 	if (!program_exists ||
2662912Sartem 		!g_spawn_async_with_pipes(program_dir, r->argv, r->environment,
2672912Sartem 		                          G_SPAWN_DO_NOT_REAP_CHILD,
2682912Sartem 		                          NULL, NULL, &pid,
2692912Sartem 		                          stdin_p, NULL, stderr_p, &error)) {
2702912Sartem 		g_free (program_dir);
2712912Sartem 		del_run_request(r);
2722912Sartem 		if (con && msg)
2732912Sartem 			send_reply(con, msg, HALD_RUN_FAILED, 0, NULL);
2742912Sartem 		return FALSE;
2752912Sartem 	}
2762912Sartem 	g_free (program_dir);
2772912Sartem 
2782912Sartem 	if (r->input) {
279*3502Sartem 		if (write(stdin_v, r->input, strlen(r->input)) != (ssize_t) strlen(r->input))
2802912Sartem 			printf("Warning: Error while wite r->input (%s) to stdin_v.\n", r->input);
2812912Sartem 		close(stdin_v);
2822912Sartem 	}
2832912Sartem 
2842912Sartem 	rd = g_new0(run_data,1);
2852912Sartem 	g_assert(rd != NULL);
2862912Sartem 	rd->r = r;
2872912Sartem 	rd->msg = msg;
2882912Sartem 	if (msg != NULL)
2892912Sartem 		dbus_message_ref(msg);
2902912Sartem 
2912912Sartem 	rd->con = con;
2922912Sartem 	rd->pid = pid;
2932912Sartem 	rd->stderr_v = stderr_v;
2942912Sartem 	rd->sent_kill = FALSE;
2952912Sartem 
2962912Sartem 	/* Add watch for exit of the program */
2972912Sartem 	rd->watch = g_child_watch_add(pid, run_exited, rd);
2982912Sartem 
2992912Sartem 	/* Add timeout if needed */
3002912Sartem 	if (r->timeout > 0)
3012912Sartem 		rd->timeout = g_timeout_add(r->timeout, run_timedout, rd);
3022912Sartem 	else
3032912Sartem 		rd->timeout = 0;
3042912Sartem 
3052912Sartem 	/* Add to the hashtable */
3062912Sartem 	list = (GList *)g_hash_table_lookup(udi_hash, r->udi);
3072912Sartem 	list = g_list_prepend(list, rd);
3082912Sartem 
3092912Sartem 	/* The hash table will take care to not leak the dupped string */
3102912Sartem 	g_hash_table_insert(udi_hash, g_strdup(r->udi), list);
3112912Sartem 
3122912Sartem 	/* send back PID if requested.. and only emit StartedProcessExited in this case */
3132912Sartem 	if (out_pid != NULL) {
3142912Sartem 		*out_pid = pid;
3152912Sartem 		rd->emit_pid_exited = TRUE;
3162912Sartem 	}
3172912Sartem 	return TRUE;
3182912Sartem }
3192912Sartem 
3202912Sartem static void
kill_rd(gpointer data,gpointer user_data)3212912Sartem kill_rd(gpointer data, gpointer user_data)
3222912Sartem {
3232912Sartem 	run_data *rd = (run_data *)data;
3242912Sartem 
3252912Sartem 	kill(rd->pid, SIGTERM);
3262912Sartem 	printf("Sent kill to %d\n", rd->pid);
3272912Sartem 	if (rd->timeout != 0) {
3282912Sartem 		/* Remove the timeout watch */
3292912Sartem 		g_source_remove(rd->timeout);
3302912Sartem 		rd->timeout = 0;
3312912Sartem 	}
3322912Sartem 
3332912Sartem 	/* So the exit watch will know it's killed  in case it runs */
3342912Sartem 	rd->sent_kill = TRUE;
3352912Sartem 
3362912Sartem 	if (rd->msg != NULL)
3372912Sartem 		send_reply(rd->con, rd->msg, HALD_RUN_KILLED, 0, NULL);
3382912Sartem }
3392912Sartem 
3402912Sartem static void
do_kill_udi(gchar * udi)3412912Sartem do_kill_udi(gchar *udi)
3422912Sartem {
3432912Sartem 	GList *list;
3442912Sartem 	list = (GList *)g_hash_table_lookup(udi_hash, udi);
3452912Sartem 	g_list_foreach(list, kill_rd, NULL);
3462912Sartem 	g_list_free(list);
3472912Sartem }
3482912Sartem 
3492912Sartem /* Kill all running request for a udi */
3502912Sartem void
run_kill_udi(gchar * udi)3512912Sartem run_kill_udi(gchar *udi)
3522912Sartem {
3532912Sartem 	do_kill_udi(udi);
3542912Sartem 	g_hash_table_remove(udi_hash, udi);
3552912Sartem }
3562912Sartem 
3572912Sartem static gboolean
hash_kill_udi(gpointer key,gpointer value,gpointer user_data)3582912Sartem hash_kill_udi(gpointer key, gpointer value, gpointer user_data) {
3592912Sartem 	do_kill_udi(key);
3602912Sartem 	return TRUE;
3612912Sartem }
3622912Sartem 
3632912Sartem /* Kill all running request*/
3642912Sartem void
run_kill_all()3652912Sartem run_kill_all()
3662912Sartem {
3672912Sartem 	g_hash_table_foreach_remove(udi_hash, hash_kill_udi, NULL);
3682912Sartem }
3692912Sartem 
3702912Sartem void
run_init()3712912Sartem run_init()
3722912Sartem {
3732912Sartem 	udi_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
3742912Sartem }
375