agent: Split agent code into generic and service specific parts
[platform/upstream/connman.git] / src / agent.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2012  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 <stdlib.h>
28 #include <string.h>
29
30 #include <gdbus.h>
31 #include <connman/agent.h>
32 #include <connman/setting.h>
33
34 #include "connman.h"
35
36 static DBusConnection *connection = NULL;
37 static guint agent_watch = 0;
38 static gchar *agent_path = NULL;
39 static gchar *agent_sender = NULL;
40
41 struct connman_agent {
42         void *user_context;
43         void *user_data;
44         DBusMessage *msg;
45         DBusPendingCall *call;
46         int timeout;
47         agent_queue_cb callback;
48         struct connman_agent_driver *driver;
49 };
50
51 static GList *agent_queue = NULL;
52 static struct connman_agent *agent_request = NULL;
53 static GSList *driver_list = NULL;
54
55 void connman_agent_get_info(const char **sender, const char **path)
56 {
57         *sender = agent_sender;
58         *path = agent_path;
59 }
60
61 static void agent_data_free(struct connman_agent *data)
62 {
63         if (data == NULL)
64                 return;
65         if (data->user_context != NULL) {
66                 if (data->driver != NULL && data->driver->context_unref != NULL)
67                         data->driver->context_unref(data->user_context);
68         }
69         if (data->msg != NULL)
70                 dbus_message_unref(data->msg);
71         if (data->call != NULL)
72                 dbus_pending_call_cancel(data->call);
73
74         g_free(data);
75 }
76
77 static void agent_receive_message(DBusPendingCall *call, void *user_data);
78
79 static int agent_send_next_request(void)
80 {
81         if (agent_request != NULL)
82                 return -EBUSY;
83
84         if (agent_queue == NULL)
85                 return 0;
86
87         agent_request = agent_queue->data;
88         agent_queue = g_list_remove(agent_queue, agent_request);
89
90         if (dbus_connection_send_with_reply(connection, agent_request->msg,
91                                         &agent_request->call,
92                                         agent_request->timeout) == FALSE)
93                 goto fail;
94
95         if (agent_request->call == NULL)
96                 goto fail;
97
98         if (dbus_pending_call_set_notify(agent_request->call,
99                         agent_receive_message, agent_request, NULL) == FALSE)
100                 goto fail;
101
102         dbus_message_unref(agent_request->msg);
103         agent_request->msg = NULL;
104         return 0;
105
106 fail:
107         agent_data_free(agent_request);
108         agent_request = NULL;
109         return -ESRCH;
110 }
111
112 static int agent_send_cancel(struct connman_agent *agent)
113 {
114         DBusMessage *message;
115
116         if (agent_sender == NULL || agent == NULL || agent->driver == NULL)
117                 return 0;
118
119         message = dbus_message_new_method_call(agent_sender, agent_path,
120                         agent->driver->interface, "Cancel");
121         if (message != NULL) {
122                 dbus_message_set_no_reply(message, TRUE);
123                 g_dbus_send_message(connection, message);
124                 return 0;
125         }
126
127         connman_warn("Failed to send Cancel message to agent");
128         return -ESRCH;
129 }
130
131 static void agent_receive_message(DBusPendingCall *call, void *user_data)
132 {
133         struct connman_agent *queue_data = user_data;
134         DBusMessage *reply;
135         int err;
136
137         DBG("waiting for %p received %p", agent_request, queue_data);
138
139         if (agent_request != queue_data) {
140                 connman_error("Agent callback expected %p got %p",
141                                 agent_request, queue_data);
142                 return;
143         }
144
145         reply = dbus_pending_call_steal_reply(call);
146         dbus_pending_call_unref(call);
147         queue_data->call = NULL;
148
149         if (dbus_message_is_error(reply,
150                         "org.freedesktop.DBus.Error.Timeout") == TRUE ||
151                         dbus_message_is_error(reply,
152                         "org.freedesktop.DBus.Error.TimedOut") == TRUE) {
153                 agent_send_cancel(queue_data->user_context);
154         }
155
156         queue_data->callback(reply, queue_data->user_data);
157         dbus_message_unref(reply);
158
159         agent_data_free(queue_data);
160         agent_request = NULL;
161
162         err = agent_send_next_request();
163         if (err < 0)
164                 DBG("send next request failed (%s/%d)", strerror(-err), -err);
165 }
166
167 static struct connman_agent_driver *get_driver(void)
168 {
169         return g_slist_nth_data(driver_list, 0);
170 }
171
172 int connman_agent_queue_message(void *user_context,
173                                 DBusMessage *msg, int timeout,
174                                 agent_queue_cb callback, void *user_data)
175 {
176         struct connman_agent *queue_data;
177         struct connman_agent_driver *driver;
178         int err;
179
180         if (user_context == NULL || callback == NULL)
181                 return -EBADMSG;
182
183         queue_data = g_new0(struct connman_agent, 1);
184         if (queue_data == NULL)
185                 return -ENOMEM;
186
187         driver = get_driver();
188         DBG("driver %p", driver);
189
190         if (driver != NULL && driver->context_ref != NULL)
191                 queue_data->user_context = driver->context_ref(user_context);
192         else
193                 queue_data->user_context = user_context;
194
195         queue_data->msg = dbus_message_ref(msg);
196         queue_data->timeout = timeout;
197         queue_data->callback = callback;
198         queue_data->user_data = user_data;
199         agent_queue = g_list_append(agent_queue, queue_data);
200
201         err = agent_send_next_request();
202         if (err < 0)
203                 DBG("send next request failed (%s/%d)", strerror(-err), -err);
204
205         return err;
206 }
207
208 void connman_agent_cancel(void *user_context)
209 {
210         GList *item, *next;
211         struct connman_agent *queued_req;
212         int err;
213
214         DBG("context %p", user_context);
215
216         item = agent_queue;
217
218         while (item != NULL) {
219                 next = g_list_next(item);
220                 queued_req = item->data;
221
222                 if (queued_req->user_context == user_context ||
223                                                         user_context == NULL) {
224                         agent_data_free(queued_req);
225                         agent_queue = g_list_delete_link(agent_queue, item);
226                 }
227
228                 item = next;
229         }
230
231         if (agent_request == NULL)
232                 return;
233
234         if (agent_request->user_context != user_context &&
235                                                 user_context != NULL)
236                 return;
237
238         agent_send_cancel(agent_request);
239
240         agent_data_free(agent_request);
241         agent_request = NULL;
242
243         err = agent_send_next_request();
244         if (err < 0)
245                 DBG("send next request failed (%s/%d)", strerror(-err), -err);
246 }
247
248 static void agent_free(void)
249 {
250         if (agent_watch > 0)
251                 g_dbus_remove_watch(connection, agent_watch);
252
253         agent_watch = 0;
254
255         g_free(agent_sender);
256         agent_sender = NULL;
257
258         g_free(agent_path);
259         agent_path = NULL;
260
261         connman_agent_cancel(NULL);
262 }
263
264 static void agent_disconnect(DBusConnection *conn, void *data)
265 {
266         DBG("data %p", data);
267         agent_free();
268 }
269
270 int connman_agent_register(const char *sender, const char *path)
271 {
272         DBG("sender %s path %s", sender, path);
273         if (agent_path != NULL)
274                 return -EEXIST;
275
276         agent_sender = g_strdup(sender);
277         agent_path = g_strdup(path);
278
279         agent_watch = g_dbus_add_disconnect_watch(connection, sender,
280                                                 agent_disconnect, NULL, NULL);
281
282         return 0;
283 }
284
285 int connman_agent_unregister(const char *sender, const char *path)
286 {
287         DBG("sender %s path %s", sender, path);
288
289         if (agent_path == NULL)
290                 return -ESRCH;
291
292         if (agent_watch > 0)
293                 g_dbus_remove_watch(connection, agent_watch);
294
295         agent_free();
296
297         return 0;
298 }
299
300 struct report_error_data {
301         void *user_context;
302         report_error_cb_t callback;
303         void *user_data;
304 };
305
306 static void report_error_reply(DBusMessage *reply, void *user_data)
307 {
308         struct report_error_data *report_error = user_data;
309         gboolean retry = FALSE;
310         const char *dbus_err;
311
312         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
313                 dbus_err = dbus_message_get_error_name(reply);
314                 if (dbus_err != NULL &&
315                         strcmp(dbus_err,
316                                 CONNMAN_AGENT_INTERFACE ".Error.Retry") == 0)
317                         retry = TRUE;
318         }
319
320         report_error->callback(report_error->user_context, retry,
321                         report_error->user_data);
322         g_free(report_error);
323 }
324
325 int connman_agent_report_error(void *user_context, const char *path,
326                                 const char *error,
327                                 report_error_cb_t callback, void *user_data)
328 {
329         DBusMessage *message;
330         DBusMessageIter iter;
331         struct report_error_data *report_error;
332         int err;
333
334         if (user_context == NULL || agent_path == NULL || error == NULL ||
335                                                         callback == NULL)
336                 return -ESRCH;
337
338         message = dbus_message_new_method_call(agent_sender, agent_path,
339                                         CONNMAN_AGENT_INTERFACE,
340                                         "ReportError");
341         if (message == NULL)
342                 return -ENOMEM;
343
344         dbus_message_iter_init_append(message, &iter);
345
346         dbus_message_iter_append_basic(&iter,
347                                 DBUS_TYPE_OBJECT_PATH, &path);
348         dbus_message_iter_append_basic(&iter,
349                                 DBUS_TYPE_STRING, &error);
350
351         report_error = g_try_new0(struct report_error_data, 1);
352         if (report_error == NULL) {
353                 dbus_message_unref(message);
354                 return -ENOMEM;
355         }
356
357         report_error->user_context = user_context;
358         report_error->callback = callback;
359         report_error->user_data = user_data;
360
361         err = connman_agent_queue_message(user_context, message,
362                                         connman_timeout_input_request(),
363                                         report_error_reply, report_error);
364         if (err < 0 && err != -EBUSY) {
365                 DBG("error %d sending error request", err);
366                 g_free(report_error);
367                 dbus_message_unref(message);
368                 return -ESRCH;
369         }
370
371         dbus_message_unref(message);
372
373         return -EINPROGRESS;
374 }
375
376 static gint compare_priority(gconstpointer a, gconstpointer b)
377 {
378         const struct connman_agent_driver *driver1 = a;
379         const struct connman_agent_driver *driver2 = b;
380
381         return driver2->priority - driver1->priority;
382 }
383
384 /**
385  * connman_agent_driver_register:
386  * @driver: Agent driver definition
387  *
388  * Register a new agent driver
389  *
390  * Returns: %0 on success
391  */
392 int connman_agent_driver_register(struct connman_agent_driver *driver)
393 {
394         DBG("Registering driver %p name %s", driver, driver->name);
395
396         driver_list = g_slist_insert_sorted(driver_list, driver,
397                                                         compare_priority);
398
399         return 0;
400 }
401
402 /**
403  * connman_agent_driver_unregister:
404  * @driver: Agent driver definition
405  *
406  * Remove a previously registered agent driver
407  */
408 void connman_agent_driver_unregister(struct connman_agent_driver *driver)
409 {
410         GSList *list;
411
412         DBG("Unregistering driver %p name %s", driver, driver->name);
413
414         for (list = driver_list; list; list = list->next) {
415                 DBusMessage *message;
416
417                 if (driver == list->data)
418                         continue;
419
420                 message = dbus_message_new_method_call(agent_sender, agent_path,
421                                 driver->interface, "Release");
422                 if (message != NULL) {
423                         dbus_message_set_no_reply(message, TRUE);
424                         g_dbus_send_message(connection, message);
425                 }
426
427                 agent_free();
428         }
429
430         driver_list = g_slist_remove(driver_list, driver);
431 }
432
433 int __connman_agent_init(void)
434 {
435         DBG("");
436
437         connection = connman_dbus_get_connection();
438         if (connection == NULL)
439                 return -1;
440
441         return 0;
442 }
443
444 void __connman_agent_cleanup(void)
445 {
446         DBG("");
447
448         if (connection == NULL)
449                 return;
450
451         if (agent_watch > 0)
452                 g_dbus_remove_watch(connection, agent_watch);
453
454         dbus_connection_unref(connection);
455         connection = NULL;
456 }