element: Remove element.c
[platform/upstream/connman.git] / src / task.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
6  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27 #include <unistd.h>
28 #include <stdarg.h>
29 #include <sys/wait.h>
30
31 #include <glib.h>
32
33 #include "connman.h"
34
35 struct notify_data {
36         connman_task_notify_t func;
37         void *data;
38 };
39
40 struct connman_task {
41         char *path;
42         pid_t pid;
43         guint child_watch;
44         GPtrArray *argv;
45         GPtrArray *envp;
46         connman_task_exit_t exit_func;
47         void *exit_data;
48         GHashTable *notify;
49 };
50
51 static GHashTable *task_hash = NULL;
52
53 static volatile gint task_counter;
54
55 static DBusConnection *connection;
56
57 static void free_pointer(gpointer data, gpointer user_data)
58 {
59         g_free(data);
60 }
61
62 static void free_task(gpointer data)
63 {
64         struct connman_task *task = data;
65
66         DBG("task %p", task);
67
68         g_hash_table_destroy(task->notify);
69         task->notify = NULL;
70
71         if (task->pid > 0)
72                 kill(task->pid, SIGTERM);
73
74         if (task->child_watch > 0)
75                 g_source_remove(task->child_watch);
76
77         g_ptr_array_foreach(task->envp, free_pointer, NULL);
78         g_ptr_array_free(task->envp, TRUE);
79
80         g_ptr_array_foreach(task->argv, free_pointer, NULL);
81         g_ptr_array_free(task->argv, TRUE);
82
83         g_free(task->path);
84         g_free(task);
85 }
86
87 /**
88  * connman_task_create:
89  * @program: name of executable
90  *
91  * Allocate a new task of given #program
92  *
93  * Returns: a newly-allocated #connman_task structure
94  */
95 struct connman_task *connman_task_create(const char *program)
96 {
97         struct connman_task *task;
98         gint counter;
99         char *str;
100
101         DBG("");
102
103         task = g_try_new0(struct connman_task, 1);
104         if (task == NULL)
105                 return NULL;
106
107         counter = g_atomic_int_exchange_and_add(&task_counter, 1);
108
109         task->path = g_strdup_printf("/task/%d", counter);
110         task->pid = -1;
111
112         task->argv = g_ptr_array_new();
113         task->envp = g_ptr_array_new();
114
115         str = g_strdup(program);
116         g_ptr_array_add(task->argv, str);
117
118         task->notify = g_hash_table_new_full(g_str_hash, g_str_equal,
119                                                         g_free, g_free);
120
121         DBG("task %p", task);
122
123         g_hash_table_insert(task_hash, task->path, task);
124
125         return task;
126 }
127
128 /**
129  * connman_task_destory:
130  * @task: task structure
131  *
132  * Remove and destory #task
133  */
134 void connman_task_destroy(struct connman_task *task)
135 {
136         DBG("task %p", task);
137
138         g_hash_table_remove(task_hash, task->path);
139 }
140
141 /**
142  * connman_task_get_path:
143  * @task: task structure
144  *
145  * Get object path
146  */
147 const char *connman_task_get_path(struct connman_task *task)
148 {
149         return task->path;
150 }
151
152 /**
153  * connman_task_add_argument:
154  * @task: task structure
155  * @name: argument name
156  * @format: format string
157  * @Varargs: list of arguments
158  *
159  * Add a new command line argument
160  */
161 int connman_task_add_argument(struct connman_task *task,
162                                 const char *name, const char *format, ...)
163 {
164         va_list ap;
165         char *str;
166
167         DBG("task %p arg %s", task, name);
168
169         if (name == NULL)
170                 return -EINVAL;
171
172         str = g_strdup(name);
173         g_ptr_array_add(task->argv, str);
174
175         va_start(ap, format);
176
177         if (format != NULL) {
178                 str = g_strdup_vprintf(format, ap);
179                 g_ptr_array_add(task->argv, str);
180         }
181
182         va_end(ap);
183
184         return 0;
185 }
186
187 /**
188  * connman_task_add_variable:
189  * @task: task structure
190  * @key: variable name
191  * @format: format string
192  * @Varargs: list of arguments
193  *
194  * Add a new environment variable
195  */
196 int connman_task_add_variable(struct connman_task *task,
197                                 const char *key, const char *format, ...)
198 {
199         va_list ap;
200         char *str, *val;
201
202         DBG("task %p key %s", task, key);
203
204         if (key == NULL)
205                 return -EINVAL;
206
207         va_start(ap, format);
208
209         val = g_strdup_vprintf(format, ap);
210         str = g_strdup_printf("%s=%s", key, format ? format : "");
211         g_ptr_array_add(task->envp, str);
212         g_free(val);
213
214         va_end(ap);
215
216         return 0;
217 }
218
219 /**
220  * connman_task_set_notify:
221  * @task: task structure
222  * @member: notifcation method name
223  * @function: notification callback
224  * @user_data: optional notification user data
225  *
226  * Set notification handler for #member
227  */
228 int connman_task_set_notify(struct connman_task *task, const char *member,
229                         connman_task_notify_t function, void *user_data)
230 {
231         struct notify_data *notify;
232
233         DBG("task %p", task);
234
235         notify = g_try_new0(struct notify_data, 1);
236         if (notify == NULL)
237                 return -ENOMEM;
238
239         notify->func = function;
240         notify->data = user_data;
241
242         g_hash_table_insert(task->notify, g_strdup(member), notify);
243
244         return 0;
245 }
246
247 static void task_died(GPid pid, gint status, gpointer user_data)
248 {
249         struct connman_task *task = user_data;
250         int exit_code;
251
252         if (WIFEXITED(status)) {
253                 exit_code = WEXITSTATUS(status);
254                 DBG("task %p exit status %d", task, exit_code);
255         } else {
256                 exit_code = 0;
257                 DBG("task %p signal %d", task, WTERMSIG(status));
258         }
259
260         g_spawn_close_pid(pid);
261         task->pid = -1;
262
263         task->child_watch = 0;
264
265         if (task->exit_func)
266                 task->exit_func(task, exit_code, task->exit_data);
267 }
268
269 static void task_setup(gpointer user_data)
270 {
271         struct connman_task *task = user_data;
272
273         DBG("task %p", task);
274 }
275
276 /**
277  * connman_task_run:
278  * @task: task structure
279  * @function: exit callback
280  * @user_data: optional exit user data
281  * @fd: optional spawn with pipe
282  *
283  * Execute program specified by #task
284  */
285 int connman_task_run(struct connman_task *task,
286                         connman_task_exit_t function, void *user_data,
287                         int *stdin_fd, int *stdout_fd, int *stderr_fd)
288 {
289         GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD;
290         gboolean result;
291         char **argv, **envp;
292
293         DBG("task %p", task);
294
295         if (task->pid > 0)
296                 return -EALREADY;
297
298         if (stdout_fd == NULL)
299                 flags |= G_SPAWN_STDOUT_TO_DEV_NULL;
300
301         if (stderr_fd == NULL)
302                 flags |= G_SPAWN_STDERR_TO_DEV_NULL;
303
304         task->exit_func = function;
305         task->exit_data = user_data;
306
307         if (g_ptr_array_index(task->argv, task->argv->len - 1) != NULL)
308                 g_ptr_array_add(task->argv, NULL);
309
310         if (task->envp->len == 0 || g_ptr_array_index(task->envp,
311                                         task->envp->len - 1) != NULL) {
312                 if (g_hash_table_size(task->notify) > 0) {
313                         const char *busname;
314                         char *str;
315
316                         busname = dbus_bus_get_unique_name(connection);
317                         str = g_strdup_printf("CONNMAN_BUSNAME=%s", busname);
318                         g_ptr_array_add(task->envp, str);
319
320                         str = g_strdup_printf("CONNMAN_INTERFACE=%s",
321                                                 CONNMAN_TASK_INTERFACE);
322                         g_ptr_array_add(task->envp, str);
323
324                         str = g_strdup_printf("CONNMAN_PATH=%s", task->path);
325                         g_ptr_array_add(task->envp, str);
326                 }
327
328                 g_ptr_array_add(task->envp, NULL);
329         }
330
331         argv = (char **) task->argv->pdata;
332         envp = (char **) task->envp->pdata;
333
334         result = g_spawn_async_with_pipes(NULL, argv, envp, flags,
335                                         task_setup, task, &task->pid,
336                                         stdin_fd, stdout_fd, stderr_fd, NULL);
337         if (result == FALSE) {
338                 connman_error("Failed to spawn %s", argv[0]);
339                 return -EIO;
340         }
341
342         task->child_watch = g_child_watch_add(task->pid, task_died, task);
343
344         return 0;
345 }
346
347 /**
348  * connman_task_stop:
349  * @task: task structure
350  *
351  * Stop program specified by #task
352  */
353 int connman_task_stop(struct connman_task *task)
354 {
355         DBG("task %p", task);
356
357         if (task->pid > 0)
358                 kill(task->pid, SIGTERM);
359
360         return 0;
361 }
362
363 static DBusHandlerResult task_filter(DBusConnection *connection,
364                                         DBusMessage *message, void *user_data)
365 {
366         struct connman_task *task;
367         struct notify_data *notify;
368         const char *path, *member;
369
370         if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
371                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
372
373         if (dbus_message_has_interface(message,
374                                         CONNMAN_TASK_INTERFACE) == FALSE)
375                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
376
377         path = dbus_message_get_path(message);
378         if (path == NULL)
379                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
380
381         task = g_hash_table_lookup(task_hash, path);
382         if (task == NULL)
383                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
384
385         if (dbus_message_get_no_reply(message) == FALSE) {
386                 DBusMessage *reply;
387
388                 reply = dbus_message_new_method_return(message);
389                 if (reply == NULL)
390                         return DBUS_HANDLER_RESULT_NEED_MEMORY;
391
392                 dbus_connection_send(connection, reply, NULL);
393
394                 dbus_message_unref(reply);
395         }
396
397         member = dbus_message_get_member(message);
398         if (member == NULL)
399                 return DBUS_HANDLER_RESULT_HANDLED;
400
401         notify = g_hash_table_lookup(task->notify, member);
402         if (notify == NULL)
403                 return DBUS_HANDLER_RESULT_HANDLED;
404
405         if (notify->func)
406                 notify->func(task, message, notify->data);
407
408         return DBUS_HANDLER_RESULT_HANDLED;
409 }
410
411 static const char *task_rule = "type=method_call"
412                                         ",interface=" CONNMAN_TASK_INTERFACE;
413
414 int __connman_task_init(void)
415 {
416         DBG("");
417
418         connection = connman_dbus_get_connection();
419
420         dbus_connection_add_filter(connection, task_filter, NULL, NULL);
421
422         g_atomic_int_set(&task_counter, 0);
423
424         task_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
425                                                         NULL, free_task);
426
427         dbus_bus_add_match(connection, task_rule, NULL);
428         dbus_connection_flush(connection);
429
430         return 0;
431 }
432
433 void __connman_task_cleanup(void)
434 {
435         DBG("");
436
437         dbus_bus_remove_match(connection, task_rule, NULL);
438         dbus_connection_flush(connection);
439
440         g_hash_table_destroy(task_hash);
441         task_hash = NULL;
442
443         dbus_connection_remove_filter(connection, task_filter, NULL);
444
445         dbus_connection_unref(connection);
446 }