X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Ftask.c;h=97de7e351c9c1948321e451095395fa782eb45ef;hb=46d7e52dcabccff5e010a6aa7232fbbfea1abecb;hp=550027bb4464d863fae268df1f07932432c9a471;hpb=c78db61246eee132abf421b095baab5b996dd066;p=framework%2Fconnectivity%2Fconnman.git diff --git a/src/task.c b/src/task.c index 550027b..97de7e3 100644 --- a/src/task.c +++ b/src/task.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007-2009 Intel Corporation. All rights reserved. + * Copyright (C) 2007-2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -23,8 +23,11 @@ #include #endif +#include #include +#include #include +#include #include @@ -38,6 +41,7 @@ struct notify_data { struct connman_task { char *path; pid_t pid; + guint child_watch; GPtrArray *argv; GPtrArray *envp; connman_task_exit_t exit_func; @@ -47,7 +51,7 @@ struct connman_task { static GHashTable *task_hash = NULL; -static volatile gint task_counter; +static volatile int task_counter; static DBusConnection *connection; @@ -68,6 +72,9 @@ static void free_task(gpointer data) if (task->pid > 0) kill(task->pid, SIGTERM); + if (task->child_watch > 0) + g_source_remove(task->child_watch); + g_ptr_array_foreach(task->envp, free_pointer, NULL); g_ptr_array_free(task->envp, TRUE); @@ -98,7 +105,7 @@ struct connman_task *connman_task_create(const char *program) if (task == NULL) return NULL; - counter = g_atomic_int_exchange_and_add(&task_counter, 1); + counter = __sync_fetch_and_add(&task_counter, 1); task->path = g_strdup_printf("/task/%d", counter); task->pid = -1; @@ -133,31 +140,48 @@ void connman_task_destroy(struct connman_task *task) } /** + * connman_task_get_path: + * @task: task structure + * + * Get object path + */ +const char *connman_task_get_path(struct connman_task *task) +{ + return task->path; +} + +/** * connman_task_add_argument: * @task: task structure - * @argument: argument name - * @value: optional argument value + * @name: argument name + * @format: format string + * @Varargs: list of arguments * * Add a new command line argument */ int connman_task_add_argument(struct connman_task *task, - const char *argument, const char *value) + const char *name, const char *format, ...) { + va_list ap; char *str; - DBG("task %p arg %s val %s", task, argument, value); + DBG("task %p arg %s", task, name); - if (argument == NULL) + if (name == NULL) return -EINVAL; - str = g_strdup(argument); + str = g_strdup(name); g_ptr_array_add(task->argv, str); - if (value != NULL) { - str = g_strdup(value); + va_start(ap, format); + + if (format != NULL) { + str = g_strdup_vprintf(format, ap); g_ptr_array_add(task->argv, str); } + va_end(ap); + return 0; } @@ -165,22 +189,30 @@ int connman_task_add_argument(struct connman_task *task, * connman_task_add_variable: * @task: task structure * @key: variable name - * @value: optional variable value + * @format: format string + * @Varargs: list of arguments * * Add a new environment variable */ int connman_task_add_variable(struct connman_task *task, - const char *key, const char *value) + const char *key, const char *format, ...) { - char *str; + va_list ap; + char *str, *val; - DBG("task %p key %s val %s", task, key, value); + DBG("task %p key %s", task, key); if (key == NULL) return -EINVAL; - str = g_strdup_printf("%s=%s", key, value ? value : ""); + va_start(ap, format); + + val = g_strdup_vprintf(format, ap); + str = g_strdup_printf("%s=%s", key, format ? format : ""); g_ptr_array_add(task->envp, str); + g_free(val); + + va_end(ap); return 0; } @@ -208,7 +240,7 @@ int connman_task_set_notify(struct connman_task *task, const char *member, notify->func = function; notify->data = user_data; - g_hash_table_insert(task->notify, g_strdup(member), notify); + g_hash_table_replace(task->notify, g_strdup(member), notify); return 0; } @@ -216,24 +248,35 @@ int connman_task_set_notify(struct connman_task *task, const char *member, static void task_died(GPid pid, gint status, gpointer user_data) { struct connman_task *task = user_data; + int exit_code; - if (WIFEXITED(status)) - DBG("task %p exit status %d", task, WEXITSTATUS(status)); - else + if (WIFEXITED(status)) { + exit_code = WEXITSTATUS(status); + DBG("task %p exit status %d", task, exit_code); + } else { + exit_code = 0; DBG("task %p signal %d", task, WTERMSIG(status)); + } g_spawn_close_pid(pid); task->pid = -1; + task->child_watch = 0; + if (task->exit_func) - task->exit_func(task, task->exit_data); + task->exit_func(task, exit_code, task->exit_data); } static void task_setup(gpointer user_data) { + sigset_t mask; struct connman_task *task = user_data; DBG("task %p", task); + + sigemptyset(&mask); + if (sigprocmask(SIG_SETMASK, &mask, NULL) < 0) + connman_error("Failed to clean signal mask"); } /** @@ -241,14 +284,16 @@ static void task_setup(gpointer user_data) * @task: task structure * @function: exit callback * @user_data: optional exit user data + * @fd: optional spawn with pipe * * Execute program specified by #task */ int connman_task_run(struct connman_task *task, - connman_task_exit_t function, void *user_data) + connman_task_exit_t function, void *user_data, + int *stdin_fd, int *stdout_fd, int *stderr_fd) { - GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD | - G_SPAWN_STDOUT_TO_DEV_NULL; + GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD; + gboolean result; char **argv, **envp; DBG("task %p", task); @@ -256,6 +301,12 @@ int connman_task_run(struct connman_task *task, if (task->pid > 0) return -EALREADY; + if (stdout_fd == NULL) + flags |= G_SPAWN_STDOUT_TO_DEV_NULL; + + if (stderr_fd == NULL) + flags |= G_SPAWN_STDERR_TO_DEV_NULL; + task->exit_func = function; task->exit_data = user_data; @@ -286,17 +337,57 @@ int connman_task_run(struct connman_task *task, argv = (char **) task->argv->pdata; envp = (char **) task->envp->pdata; - if (g_spawn_async(NULL, argv, envp, flags, - task_setup, task, &task->pid, NULL) == FALSE) { + result = g_spawn_async_with_pipes(NULL, argv, envp, flags, + task_setup, task, &task->pid, + stdin_fd, stdout_fd, stderr_fd, NULL); + if (result == FALSE) { connman_error("Failed to spawn %s", argv[0]); return -EIO; } - g_child_watch_add(task->pid, task_died, task); + task->child_watch = g_child_watch_add(task->pid, task_died, task); return 0; } +static gboolean force_kill_timeout(gpointer user_data) +{ + pid_t pid = GPOINTER_TO_INT(user_data); + if (pid > 0) { + if (kill(pid, SIGKILL) == 0) + connman_warn("killing pid %d by force", pid); + } + + return FALSE; +} + +static gboolean kill_timeout(gpointer user_data) +{ + pid_t pid = GPOINTER_TO_INT(user_data); + if (pid > 0) { + if (kill(pid, SIGINT) == 0) + g_timeout_add_seconds(1, force_kill_timeout, + GINT_TO_POINTER(pid)); + } + + return FALSE; +} + +static gboolean check_kill(gpointer user_data) +{ + pid_t pid = GPOINTER_TO_INT(user_data); + if (pid > 0) { + if (kill(pid, 0) == 0) { + connman_info("pid %d was not killed, " + "retrying after 2 sec", pid); + g_timeout_add_seconds(2, kill_timeout, + GINT_TO_POINTER(pid)); + } + } + + return FALSE; +} + /** * connman_task_stop: * @task: task structure @@ -307,9 +398,13 @@ int connman_task_stop(struct connman_task *task) { DBG("task %p", task); - if (task->pid > 0) + if (task->pid > 0) { kill(task->pid, SIGTERM); + g_timeout_add_seconds(0, check_kill, + GINT_TO_POINTER(task->pid)); + } + return 0; } @@ -319,6 +414,7 @@ static DBusHandlerResult task_filter(DBusConnection *connection, struct connman_task *task; struct notify_data *notify; const char *path, *member; + DBusMessage *reply = NULL; if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -335,30 +431,32 @@ static DBusHandlerResult task_filter(DBusConnection *connection, if (task == NULL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - if (dbus_message_get_no_reply(message) == FALSE) { - DBusMessage *reply; - dbus_bool_t result; + member = dbus_message_get_member(message); + if (member == NULL) + goto send_reply; + + notify = g_hash_table_lookup(task->notify, member); + if (notify == NULL) + goto send_reply; + + if (notify->func) + reply = notify->func(task, message, notify->data); + +send_reply: + if (dbus_message_get_no_reply(message) == FALSE && + reply == NULL) { reply = dbus_message_new_method_return(message); if (reply == NULL) return DBUS_HANDLER_RESULT_NEED_MEMORY; + } - result = dbus_connection_send(connection, reply, NULL); + if (reply != NULL) { + dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); } - member = dbus_message_get_member(message); - if (member == NULL) - return DBUS_HANDLER_RESULT_HANDLED; - - notify = g_hash_table_lookup(task->notify, member); - if (notify == NULL) - return DBUS_HANDLER_RESULT_HANDLED; - - if (notify->func) - notify->func(task, message, notify->data); - return DBUS_HANDLER_RESULT_HANDLED; } @@ -373,7 +471,8 @@ int __connman_task_init(void) dbus_connection_add_filter(connection, task_filter, NULL, NULL); - g_atomic_int_set(&task_counter, 0); + task_counter = 0; + __sync_synchronize(); task_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free_task);