1/***************************************************************************
2 * CVSID: $Id$
3 *
4 * main.c - Main dbus interface of the hald runner
5 *
6 * Copyright (C) 2006 Sjoerd Simons, <sjoerd@luon.net>
7 * Copyright (C) 2007 Codethink Ltd. Author Rob Taylor <rob.taylor@codethink.co.uk>
8 *
9 * Licensed under the Academic Free License version 2.1
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
24 *
25 **************************************************************************/
26#include <stdio.h>
27#include <stdlib.h>
28#define DBUS_API_SUBJECT_TO_CHANGE
29#include <dbus/dbus-glib-lowlevel.h>
30
31#include <glib.h>
32#include "utils.h"
33#include "runner.h"
34
35#ifndef __GNUC__
36#define __attribute__(x)
37#endif
38
39static gboolean
40parse_udi (run_request *r, DBusMessage *msg, DBusMessageIter *iter)
41{
42	char *tmpstr;
43
44	/* Should be the device UDI */
45	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
46		goto malformed;
47	dbus_message_iter_get_basic(iter, &tmpstr);
48	r->udi = g_strdup(tmpstr);
49
50	if (!dbus_message_iter_next(iter))
51		goto malformed;
52
53	return TRUE;
54
55malformed:
56	return FALSE;
57}
58
59static gboolean
60parse_environment(run_request *r, DBusMessage *msg, DBusMessageIter *iter)
61{
62	DBusMessageIter sub_iter;
63	char *tmpstr;
64
65	/* The environment array */
66	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
67		goto malformed;
68	dbus_message_iter_recurse(iter, &sub_iter);
69	/* Add default path for the programs we start */
70#if defined(__FreeBSD__)
71	tmpstr = g_strdup_printf("PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/X11R6/sbin:/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:%s", getenv("PATH"));
72#else
73	tmpstr = g_strdup_printf("PATH=/sbin:/usr/sbin:/bin:/usr/bin:%s", getenv("PATH"));
74#endif
75	r->environment = get_string_array(&sub_iter, tmpstr);
76
77	/* Then argv */
78	if (!dbus_message_iter_next(iter) || dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
79		goto malformed;
80	dbus_message_iter_recurse(iter, &sub_iter);
81	r->argv = get_string_array(&sub_iter, NULL);
82
83	return TRUE;
84
85malformed:
86	return FALSE;
87}
88
89static void
90handle_run(DBusConnection *con, DBusMessage *msg)
91{
92	DBusMessage *reply;
93	DBusMessageIter iter;
94	run_request *r;
95	char *tmpstr;
96
97	r = new_run_request();
98	g_assert(dbus_message_iter_init(msg, &iter));
99
100	if (!parse_udi(r, msg, &iter))
101		goto malformed;
102
103	if (!parse_environment(r, msg, &iter))
104		goto malformed;
105
106	/* Next a string of what should be written to stdin */
107	if (!dbus_message_iter_next(&iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
108		goto malformed;
109	dbus_message_iter_get_basic(&iter, &tmpstr);
110	r->input = g_strdup(tmpstr);
111
112	/* Then an bool to indicate if we should grab stderr */
113	if (!dbus_message_iter_next(&iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
114		goto malformed;
115	dbus_message_iter_get_basic(&iter, &(r->error_on_stderr));
116
117	/* Then an uint32 timeout for it */
118	if (!dbus_message_iter_next(&iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
119		goto malformed;
120	dbus_message_iter_get_basic(&iter, &(r->timeout));
121
122	/* let run_request_run handle the reply */
123	run_request_run(r, con, msg, NULL);
124	return;
125
126malformed:
127	del_run_request(r);
128	reply = dbus_message_new_error(msg, "org.freedesktop.HalRunner.Malformed",
129				       "Malformed run request");
130	dbus_connection_send(con, reply, NULL);
131	dbus_message_unref(reply);
132}
133
134static void
135handle_start(DBusConnection *con, DBusMessage *msg, gboolean is_singleton)
136{
137	DBusMessage *reply;
138	DBusMessageIter iter;
139	run_request *r;
140	GPid pid;
141
142	r = new_run_request();
143	r->is_singleton = is_singleton;
144
145	g_assert(dbus_message_iter_init(msg, &iter));
146
147	if (!dbus_message_iter_init(msg, &iter))
148		goto malformed;
149
150	if (!is_singleton && !parse_udi(r, msg, &iter)) {
151		fprintf(stderr, "error parsing udi");
152		goto malformed;
153	}
154
155	if (!parse_environment(r, msg, &iter)) {
156		fprintf(stderr, "error parsing environment");
157		goto malformed;
158	}
159
160	if (run_request_run(r, con, NULL, &pid)) {
161		gint64 ppid = pid;
162		reply = dbus_message_new_method_return(msg);
163		dbus_message_append_args (reply,
164					  DBUS_TYPE_INT64, &ppid,
165					  DBUS_TYPE_INVALID);
166
167	} else {
168		reply = dbus_message_new_error(msg, "org.freedesktop.HalRunner.Failed",
169					       "Start request failed");
170	}
171	dbus_connection_send(con, reply, NULL);
172	dbus_message_unref(reply);
173	return ;
174malformed:
175	del_run_request(r);
176	reply = dbus_message_new_error(msg, "org.freedesktop.HalRunner.Malformed",
177				       "Malformed start request");
178	dbus_connection_send(con, reply, NULL);
179	dbus_message_unref(reply);
180}
181
182static void
183handle_kill(DBusConnection *con, DBusMessage *msg)
184{
185	DBusError error;
186	DBusMessage *reply = NULL;
187	char *udi;
188
189	dbus_error_init (&error);
190	if (!dbus_message_get_args(msg, &error,
191				   DBUS_TYPE_STRING, &udi,
192				   DBUS_TYPE_INVALID)) {
193		reply = dbus_message_new_error (msg, "org.freedesktop.HalRunner.Malformed",
194						"Malformed kill message");
195		g_assert(reply);
196		dbus_connection_send (con, reply, NULL);
197		dbus_message_unref(reply);
198		return;
199	}
200	run_kill_udi(udi);
201
202	/* always successfull */
203	reply = dbus_message_new_method_return(msg);
204	dbus_connection_send(con, reply, NULL);
205	dbus_message_unref(reply);
206}
207
208static DBusHandlerResult
209filter(DBusConnection *con, DBusMessage *msg, void *user_data)
210{
211	DBusMessage *reply;
212
213	if (dbus_message_is_method_call(msg, "org.freedesktop.HalRunner", "Run")) {
214		handle_run(con, msg);
215		return DBUS_HANDLER_RESULT_HANDLED;
216	} else if (dbus_message_is_method_call(msg, "org.freedesktop.HalRunner", "Start")) {
217		handle_start(con, msg, FALSE);
218		return DBUS_HANDLER_RESULT_HANDLED;
219	} else if (dbus_message_is_method_call(msg, "org.freedesktop.HalRunner", "StartSingleton")) {
220		handle_start(con, msg, TRUE);
221		return DBUS_HANDLER_RESULT_HANDLED;
222	} else if (dbus_message_is_method_call(msg, "org.freedesktop.HalRunner", "Kill")) {
223		handle_kill(con, msg);
224		return DBUS_HANDLER_RESULT_HANDLED;
225	} else if (dbus_message_is_method_call(msg, "org.freedesktop.HalRunner", "Shutdown")) {
226		run_kill_all ();
227		exit (0);
228		return DBUS_HANDLER_RESULT_HANDLED;
229	} else if (dbus_message_is_method_call(msg, "org.freedesktop.HalRunner", "KillAll")) {
230		run_kill_all();
231		/* alwasy successfull */
232		reply = dbus_message_new_method_return(msg);
233		dbus_connection_send(con, reply, NULL);
234		dbus_message_unref(reply);
235		return DBUS_HANDLER_RESULT_HANDLED;
236	}
237	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
238}
239
240int
241main(int argc, char **argv)
242{
243	DBusConnection *c;
244	DBusError error;
245	GMainLoop *loop;
246	char *dbus_address;
247
248	run_init();
249	dbus_error_init(&error);
250	dbus_address = getenv("HALD_RUNNER_DBUS_ADDRESS");
251	g_assert(dbus_address != NULL);
252
253	fprintf(stderr, "Runner started - allowed paths are '%s'\n", getenv("PATH"));
254
255	c = dbus_connection_open(dbus_address, &error);
256	if (c == NULL)
257		goto error;
258
259	loop = g_main_loop_new(NULL, FALSE);
260
261	dbus_connection_setup_with_g_main(c, NULL);
262	dbus_connection_set_exit_on_disconnect(c, TRUE);
263	dbus_connection_add_filter(c, filter, NULL, NULL);
264
265	g_main_loop_run(loop);
266
267error:
268	fprintf(stderr,"An error has occured: %s\n", error.message);
269	return -1;
270}
271