5 * Copyright (C) 2007-2012 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
37 connman_task_notify_t func;
47 connman_task_exit_t exit_func;
48 connman_task_setup_t setup_func;
54 static GHashTable *task_hash = NULL;
56 static volatile int task_counter;
58 static DBusConnection *connection;
60 static void free_pointer(gpointer data, gpointer user_data)
65 static void free_task(gpointer data)
67 struct connman_task *task = data;
71 g_hash_table_destroy(task->notify);
75 kill(task->pid, SIGTERM);
77 if (task->child_watch > 0)
78 g_source_remove(task->child_watch);
80 g_ptr_array_foreach(task->envp, free_pointer, NULL);
81 g_ptr_array_free(task->envp, TRUE);
83 g_ptr_array_foreach(task->argv, free_pointer, NULL);
84 g_ptr_array_free(task->argv, TRUE);
91 * connman_task_create:
92 * @program: name of executable
94 * Allocate a new task of given #program
96 * Returns: a newly-allocated #connman_task structure
98 struct connman_task *connman_task_create(const char *program,
99 connman_task_setup_t custom_task_setup,
102 struct connman_task *task;
108 task = g_try_new0(struct connman_task, 1);
112 counter = __sync_fetch_and_add(&task_counter, 1);
114 task->path = g_strdup_printf("/task/%d", counter);
117 task->argv = g_ptr_array_new();
118 task->envp = g_ptr_array_new();
120 str = g_strdup(program);
121 g_ptr_array_add(task->argv, str);
123 task->setup_func = custom_task_setup;
125 task->notify = g_hash_table_new_full(g_str_hash, g_str_equal,
128 task->setup_data = setup_data;
130 DBG("task %p", task);
132 g_hash_table_insert(task_hash, task->path, task);
138 * connman_task_destory:
139 * @task: task structure
141 * Remove and destroy #task
143 void connman_task_destroy(struct connman_task *task)
145 DBG("task %p", task);
147 g_hash_table_remove(task_hash, task->path);
151 * connman_task_get_path:
152 * @task: task structure
156 const char *connman_task_get_path(struct connman_task *task)
162 * connman_task_add_argument:
163 * @task: task structure
164 * @name: argument name
165 * @format: format string
166 * @Varargs: list of arguments
168 * Add a new command line argument
170 int connman_task_add_argument(struct connman_task *task,
171 const char *name, const char *format, ...)
176 DBG("task %p arg %s", task, name);
181 str = g_strdup(name);
182 g_ptr_array_add(task->argv, str);
184 va_start(ap, format);
187 str = g_strdup_vprintf(format, ap);
188 g_ptr_array_add(task->argv, str);
197 * connman_task_add_variable:
198 * @task: task structure
199 * @key: variable name
200 * @format: format string
201 * @Varargs: list of arguments
203 * Add a new environment variable
205 int connman_task_add_variable(struct connman_task *task,
206 const char *key, const char *format, ...)
211 DBG("task %p key %s", task, key);
216 va_start(ap, format);
218 val = g_strdup_vprintf(format, ap);
219 str = g_strdup_printf("%s=%s", key, format ? format : "");
220 g_ptr_array_add(task->envp, str);
229 * connman_task_set_notify:
230 * @task: task structure
231 * @member: notification method name
232 * @function: notification callback
233 * @user_data: optional notification user data
235 * Set notification handler for #member
237 int connman_task_set_notify(struct connman_task *task, const char *member,
238 connman_task_notify_t function, void *user_data)
240 struct notify_data *notify;
242 DBG("task %p", task);
244 notify = g_try_new0(struct notify_data, 1);
248 notify->func = function;
249 notify->data = user_data;
251 g_hash_table_replace(task->notify, g_strdup(member), notify);
256 static void task_died(GPid pid, gint status, gpointer user_data)
258 struct connman_task *task = user_data;
261 if (WIFEXITED(status)) {
262 exit_code = WEXITSTATUS(status);
263 DBG("task %p exit status %d", task, exit_code);
266 DBG("task %p signal %d", task, WTERMSIG(status));
269 g_spawn_close_pid(pid);
272 task->child_watch = 0;
275 task->exit_func(task, exit_code, task->exit_data);
278 static void task_setup(gpointer user_data)
281 struct connman_task *task = user_data;
283 DBG("task %p", task);
286 if (sigprocmask(SIG_SETMASK, &mask, NULL) < 0)
287 connman_error("Failed to clean signal mask");
289 if (task->setup_func)
290 task->setup_func(task->setup_data);
295 * @task: task structure
296 * @function: exit callback
297 * @user_data: optional exit user data
298 * @fd: optional spawn with pipe
300 * Execute program specified by #task
302 int connman_task_run(struct connman_task *task,
303 connman_task_exit_t function, void *user_data,
304 int *stdin_fd, int *stdout_fd, int *stderr_fd)
306 GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD;
310 DBG("task %p", task);
316 flags |= G_SPAWN_STDOUT_TO_DEV_NULL;
319 flags |= G_SPAWN_STDERR_TO_DEV_NULL;
321 task->exit_func = function;
322 task->exit_data = user_data;
324 if (g_ptr_array_index(task->argv, task->argv->len - 1))
325 g_ptr_array_add(task->argv, NULL);
327 if (task->envp->len == 0 ||
328 g_ptr_array_index(task->envp, task->envp->len - 1)) {
329 if (g_hash_table_size(task->notify) > 0) {
333 busname = dbus_bus_get_unique_name(connection);
334 str = g_strdup_printf("CONNMAN_BUSNAME=%s", busname);
335 g_ptr_array_add(task->envp, str);
337 str = g_strdup_printf("CONNMAN_INTERFACE=%s",
338 CONNMAN_TASK_INTERFACE);
339 g_ptr_array_add(task->envp, str);
341 str = g_strdup_printf("CONNMAN_PATH=%s", task->path);
342 g_ptr_array_add(task->envp, str);
345 g_ptr_array_add(task->envp, NULL);
348 argv = (char **) task->argv->pdata;
349 envp = (char **) task->envp->pdata;
351 result = g_spawn_async_with_pipes(NULL, argv, envp, flags,
352 task_setup, task, &task->pid,
353 stdin_fd, stdout_fd, stderr_fd, NULL);
355 connman_error("Failed to spawn %s", argv[0]);
359 task->child_watch = g_child_watch_add(task->pid, task_died, task);
364 static gboolean force_kill_timeout(gpointer user_data)
366 pid_t pid = GPOINTER_TO_INT(user_data);
368 if (kill(pid, SIGKILL) == 0)
369 connman_warn("killing pid %d by force", pid);
375 static gboolean kill_timeout(gpointer user_data)
377 pid_t pid = GPOINTER_TO_INT(user_data);
379 if (kill(pid, SIGINT) == 0)
380 g_timeout_add_seconds(1, force_kill_timeout,
381 GINT_TO_POINTER(pid));
387 static gboolean check_kill(gpointer user_data)
389 pid_t pid = GPOINTER_TO_INT(user_data);
391 if (kill(pid, 0) == 0) {
392 connman_info("pid %d was not killed, "
393 "retrying after 2 sec", pid);
394 g_timeout_add_seconds(2, kill_timeout,
395 GINT_TO_POINTER(pid));
404 * @task: task structure
406 * Stop program specified by #task
408 int connman_task_stop(struct connman_task *task)
410 DBG("task %p", task);
413 kill(task->pid, SIGTERM);
415 g_idle_add(check_kill, GINT_TO_POINTER(task->pid));
421 static DBusHandlerResult task_filter(DBusConnection *conn,
422 DBusMessage *message, void *user_data)
424 struct connman_task *task;
425 struct notify_data *notify;
426 const char *path, *member;
427 DBusMessage *reply = NULL;
429 if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
430 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
432 if (!dbus_message_has_interface(message, CONNMAN_TASK_INTERFACE))
433 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
435 path = dbus_message_get_path(message);
437 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
439 task = g_hash_table_lookup(task_hash, path);
441 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
443 member = dbus_message_get_member(message);
447 notify = g_hash_table_lookup(task->notify, member);
452 reply = notify->func(task, message, notify->data);
455 if (!dbus_message_get_no_reply(message) &&
458 reply = dbus_message_new_method_return(message);
460 return DBUS_HANDLER_RESULT_NEED_MEMORY;
464 dbus_connection_send(conn, reply, NULL);
466 dbus_message_unref(reply);
469 return DBUS_HANDLER_RESULT_HANDLED;
472 static const char *task_rule = "type=method_call"
473 ",interface=" CONNMAN_TASK_INTERFACE;
475 int __connman_task_init(void)
479 connection = connman_dbus_get_connection();
481 dbus_connection_add_filter(connection, task_filter, NULL, NULL);
484 __sync_synchronize();
486 task_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
489 dbus_bus_add_match(connection, task_rule, NULL);
490 dbus_connection_flush(connection);
495 void __connman_task_cleanup(void)
499 dbus_bus_remove_match(connection, task_rule, NULL);
500 dbus_connection_flush(connection);
502 g_hash_table_destroy(task_hash);
505 dbus_connection_remove_filter(connection, task_filter, NULL);
507 dbus_connection_unref(connection);