5 * Copyright (C) 2007-2010 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
35 connman_task_notify_t func;
45 connman_task_exit_t exit_func;
50 static GHashTable *task_hash = NULL;
52 static volatile gint task_counter;
54 static DBusConnection *connection;
56 static void free_pointer(gpointer data, gpointer user_data)
61 static void free_task(gpointer data)
63 struct connman_task *task = data;
67 g_hash_table_destroy(task->notify);
71 kill(task->pid, SIGTERM);
73 if (task->child_watch > 0)
74 g_source_remove(task->child_watch);
76 g_ptr_array_foreach(task->envp, free_pointer, NULL);
77 g_ptr_array_free(task->envp, TRUE);
79 g_ptr_array_foreach(task->argv, free_pointer, NULL);
80 g_ptr_array_free(task->argv, TRUE);
87 * connman_task_create:
88 * @program: name of executable
90 * Allocate a new task of given #program
92 * Returns: a newly-allocated #connman_task structure
94 struct connman_task *connman_task_create(const char *program)
96 struct connman_task *task;
102 task = g_try_new0(struct connman_task, 1);
106 counter = g_atomic_int_exchange_and_add(&task_counter, 1);
108 task->path = g_strdup_printf("/task/%d", counter);
111 task->argv = g_ptr_array_new();
112 task->envp = g_ptr_array_new();
114 str = g_strdup(program);
115 g_ptr_array_add(task->argv, str);
117 task->notify = g_hash_table_new_full(g_str_hash, g_str_equal,
120 DBG("task %p", task);
122 g_hash_table_insert(task_hash, task->path, task);
128 * connman_task_destory:
129 * @task: task structure
131 * Remove and destory #task
133 void connman_task_destroy(struct connman_task *task)
135 DBG("task %p", task);
137 g_hash_table_remove(task_hash, task->path);
141 * connman_task_get_path:
142 * @task: task structure
146 const char *connman_task_get_path(struct connman_task *task)
152 * connman_task_add_argument:
153 * @task: task structure
154 * @name: argument name
155 * @format: format string
156 * @Varargs: list of arguments
158 * Add a new command line argument
160 int connman_task_add_argument(struct connman_task *task,
161 const char *name, const char *format, ...)
166 DBG("task %p arg %s", task, name);
171 str = g_strdup(name);
172 g_ptr_array_add(task->argv, str);
174 va_start(ap, format);
176 if (format != NULL) {
177 str = g_strdup_vprintf(format, ap);
178 g_ptr_array_add(task->argv, str);
187 * connman_task_add_variable:
188 * @task: task structure
189 * @key: variable name
190 * @format: format string
191 * @Varargs: list of arguments
193 * Add a new environment variable
195 int connman_task_add_variable(struct connman_task *task,
196 const char *key, const char *format, ...)
201 DBG("task %p key %s", task, key);
206 va_start(ap, format);
208 val = g_strdup_vprintf(format, ap);
209 str = g_strdup_printf("%s=%s", key, format ? format : "");
210 g_ptr_array_add(task->envp, str);
219 * connman_task_set_notify:
220 * @task: task structure
221 * @member: notifcation method name
222 * @function: notification callback
223 * @user_data: optional notification user data
225 * Set notification handler for #member
227 int connman_task_set_notify(struct connman_task *task, const char *member,
228 connman_task_notify_t function, void *user_data)
230 struct notify_data *notify;
232 DBG("task %p", task);
234 notify = g_try_new0(struct notify_data, 1);
238 notify->func = function;
239 notify->data = user_data;
241 g_hash_table_insert(task->notify, g_strdup(member), notify);
246 static void task_died(GPid pid, gint status, gpointer user_data)
248 struct connman_task *task = user_data;
251 if (WIFEXITED(status)) {
252 exit_code = WEXITSTATUS(status);
253 DBG("task %p exit status %d", task, exit_code);
256 DBG("task %p signal %d", task, WTERMSIG(status));
259 g_spawn_close_pid(pid);
262 task->child_watch = 0;
265 task->exit_func(task, exit_code, task->exit_data);
268 static void task_setup(gpointer user_data)
270 struct connman_task *task = user_data;
272 DBG("task %p", task);
277 * @task: task structure
278 * @function: exit callback
279 * @user_data: optional exit user data
280 * @fd: optional spawn with pipe
282 * Execute program specified by #task
284 int connman_task_run(struct connman_task *task,
285 connman_task_exit_t function, void *user_data,
286 int *stdin_fd, int *stdout_fd, int *stderr_fd)
288 GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD;
292 DBG("task %p", task);
297 if (stdout_fd == NULL)
298 flags |= G_SPAWN_STDOUT_TO_DEV_NULL;
300 if (stderr_fd == NULL)
301 flags |= G_SPAWN_STDERR_TO_DEV_NULL;
303 task->exit_func = function;
304 task->exit_data = user_data;
306 if (g_ptr_array_index(task->argv, task->argv->len - 1) != NULL)
307 g_ptr_array_add(task->argv, NULL);
309 if (task->envp->len == 0 || g_ptr_array_index(task->envp,
310 task->envp->len - 1) != NULL) {
311 if (g_hash_table_size(task->notify) > 0) {
315 busname = dbus_bus_get_unique_name(connection);
316 str = g_strdup_printf("CONNMAN_BUSNAME=%s", busname);
317 g_ptr_array_add(task->envp, str);
319 str = g_strdup_printf("CONNMAN_INTERFACE=%s",
320 CONNMAN_TASK_INTERFACE);
321 g_ptr_array_add(task->envp, str);
323 str = g_strdup_printf("CONNMAN_PATH=%s", task->path);
324 g_ptr_array_add(task->envp, str);
327 g_ptr_array_add(task->envp, NULL);
330 argv = (char **) task->argv->pdata;
331 envp = (char **) task->envp->pdata;
333 result = g_spawn_async_with_pipes(NULL, argv, envp, flags,
334 task_setup, task, &task->pid,
335 stdin_fd, stdout_fd, stderr_fd, NULL);
336 if (result == FALSE) {
337 connman_error("Failed to spawn %s", argv[0]);
341 task->child_watch = g_child_watch_add(task->pid, task_died, task);
348 * @task: task structure
350 * Stop program specified by #task
352 int connman_task_stop(struct connman_task *task)
354 DBG("task %p", task);
357 kill(task->pid, SIGTERM);
362 static DBusHandlerResult task_filter(DBusConnection *connection,
363 DBusMessage *message, void *user_data)
365 struct connman_task *task;
366 struct notify_data *notify;
367 const char *path, *member;
369 if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
370 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
372 if (dbus_message_has_interface(message,
373 CONNMAN_TASK_INTERFACE) == FALSE)
374 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
376 path = dbus_message_get_path(message);
378 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
380 task = g_hash_table_lookup(task_hash, path);
382 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
384 if (dbus_message_get_no_reply(message) == FALSE) {
388 reply = dbus_message_new_method_return(message);
390 return DBUS_HANDLER_RESULT_NEED_MEMORY;
392 result = dbus_connection_send(connection, reply, NULL);
394 dbus_message_unref(reply);
397 member = dbus_message_get_member(message);
399 return DBUS_HANDLER_RESULT_HANDLED;
401 notify = g_hash_table_lookup(task->notify, member);
403 return DBUS_HANDLER_RESULT_HANDLED;
406 notify->func(task, message, notify->data);
408 return DBUS_HANDLER_RESULT_HANDLED;
411 static const char *task_rule = "type=method_call"
412 ",interface=" CONNMAN_TASK_INTERFACE;
414 int __connman_task_init(void)
418 connection = connman_dbus_get_connection();
420 dbus_connection_add_filter(connection, task_filter, NULL, NULL);
422 g_atomic_int_set(&task_counter, 0);
424 task_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
427 dbus_bus_add_match(connection, task_rule, NULL);
428 dbus_connection_flush(connection);
433 void __connman_task_cleanup(void)
437 dbus_bus_remove_match(connection, task_rule, NULL);
438 dbus_connection_flush(connection);
440 g_hash_table_destroy(task_hash);
443 dbus_connection_remove_filter(connection, task_filter, NULL);
445 dbus_connection_unref(connection);