5c3bd28c2903fc3d6c61f3e671d3697bf719f034
[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                 queue_data->driver = driver;
193         } else
194                 queue_data->user_context = user_context;
195
196         queue_data->msg = dbus_message_ref(msg);
197         queue_data->timeout = timeout;
198         queue_data->callback = callback;
199         queue_data->user_data = user_data;
200         agent_queue = g_list_append(agent_queue, queue_data);
201
202         err = agent_send_next_request();
203         if (err < 0)
204                 DBG("send next request failed (%s/%d)", strerror(-err), -err);
205
206         return err;
207 }
208
209 void connman_agent_cancel(void *user_context)
210 {
211         GList *item, *next;
212         struct connman_agent *queued_req;
213         int err;
214
215         DBG("context %p", user_context);
216
217         item = agent_queue;
218
219         while (item != NULL) {
220                 next = g_list_next(item);
221                 queued_req = item->data;
222
223                 if (queued_req->user_context == user_context ||
224                                                         user_context == NULL) {
225                         agent_data_free(queued_req);
226                         agent_queue = g_list_delete_link(agent_queue, item);
227                 }
228
229                 item = next;
230         }
231
232         if (agent_request == NULL)
233                 return;
234
235         if (agent_request->user_context != user_context &&
236                                                 user_context != NULL)
237                 return;
238
239         agent_send_cancel(agent_request);
240
241         agent_data_free(agent_request);
242         agent_request = NULL;
243
244         err = agent_send_next_request();
245         if (err < 0)
246                 DBG("send next request failed (%s/%d)", strerror(-err), -err);
247 }
248
249 static void agent_free(void)
250 {
251         if (agent_watch > 0)
252                 g_dbus_remove_watch(connection, agent_watch);
253
254         agent_watch = 0;
255
256         g_free(agent_sender);
257         agent_sender = NULL;
258
259         g_free(agent_path);
260         agent_path = NULL;
261
262         connman_agent_cancel(NULL);
263 }
264
265 static void agent_disconnect(DBusConnection *conn, void *data)
266 {
267         DBG("data %p", data);
268         agent_free();
269 }
270
271 int connman_agent_register(const char *sender, const char *path)
272 {
273         DBG("sender %s path %s", sender, path);
274         if (agent_path != NULL)
275                 return -EEXIST;
276
277         agent_sender = g_strdup(sender);
278         agent_path = g_strdup(path);
279
280         agent_watch = g_dbus_add_disconnect_watch(connection, sender,
281                                                 agent_disconnect, NULL, NULL);
282
283         return 0;
284 }
285
286 int connman_agent_unregister(const char *sender, const char *path)
287 {
288         DBG("sender %s path %s", sender, path);
289
290         if (agent_path == NULL)
291                 return -ESRCH;
292
293         if (agent_watch > 0)
294                 g_dbus_remove_watch(connection, agent_watch);
295
296         agent_free();
297
298         return 0;
299 }
300
301 struct report_error_data {
302         void *user_context;
303         report_error_cb_t callback;
304         void *user_data;
305 };
306
307 static void report_error_reply(DBusMessage *reply, void *user_data)
308 {
309         struct report_error_data *report_error = user_data;
310         gboolean retry = FALSE;
311         const char *dbus_err;
312
313         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
314                 dbus_err = dbus_message_get_error_name(reply);
315                 if (dbus_err != NULL &&
316                         strcmp(dbus_err,
317                                 CONNMAN_AGENT_INTERFACE ".Error.Retry") == 0)
318                         retry = TRUE;
319         }
320
321         report_error->callback(report_error->user_context, retry,
322                         report_error->user_data);
323         g_free(report_error);
324 }
325
326 int connman_agent_report_error(void *user_context, const char *path,
327                                 const char *error,
328                                 report_error_cb_t callback, void *user_data)
329 {
330         DBusMessage *message;
331         DBusMessageIter iter;
332         struct report_error_data *report_error;
333         int err;
334
335         if (user_context == NULL || agent_path == NULL || error == NULL ||
336                                                         callback == NULL)
337                 return -ESRCH;
338
339         message = dbus_message_new_method_call(agent_sender, agent_path,
340                                         CONNMAN_AGENT_INTERFACE,
341                                         "ReportError");
342         if (message == NULL)
343                 return -ENOMEM;
344
345         dbus_message_iter_init_append(message, &iter);
346
347         dbus_message_iter_append_basic(&iter,
348                                 DBUS_TYPE_OBJECT_PATH, &path);
349         dbus_message_iter_append_basic(&iter,
350                                 DBUS_TYPE_STRING, &error);
351
352         report_error = g_try_new0(struct report_error_data, 1);
353         if (report_error == NULL) {
354                 dbus_message_unref(message);
355                 return -ENOMEM;
356         }
357
358         report_error->user_context = user_context;
359         report_error->callback = callback;
360         report_error->user_data = user_data;
361
362         err = connman_agent_queue_message(user_context, message,
363                                         connman_timeout_input_request(),
364                                         report_error_reply, report_error);
365         if (err < 0 && err != -EBUSY) {
366                 DBG("error %d sending error request", err);
367                 g_free(report_error);
368                 dbus_message_unref(message);
369                 return -ESRCH;
370         }
371
372         dbus_message_unref(message);
373
374         return -EINPROGRESS;
375 }
376
377 static gint compare_priority(gconstpointer a, gconstpointer b)
378 {
379         const struct connman_agent_driver *driver1 = a;
380         const struct connman_agent_driver *driver2 = b;
381
382         return driver2->priority - driver1->priority;
383 }
384
385 /**
386  * connman_agent_driver_register:
387  * @driver: Agent driver definition
388  *
389  * Register a new agent driver
390  *
391  * Returns: %0 on success
392  */
393 int connman_agent_driver_register(struct connman_agent_driver *driver)
394 {
395         DBG("Registering driver %p name %s", driver, driver->name);
396
397         driver_list = g_slist_insert_sorted(driver_list, driver,
398                                                         compare_priority);
399
400         return 0;
401 }
402
403 /**
404  * connman_agent_driver_unregister:
405  * @driver: Agent driver definition
406  *
407  * Remove a previously registered agent driver
408  */
409 void connman_agent_driver_unregister(struct connman_agent_driver *driver)
410 {
411         GSList *list;
412
413         if (driver == NULL)
414                 return;
415
416         DBG("Unregistering driver %p name %s", driver, driver->name);
417
418         if (agent_sender == NULL && agent_path == NULL)
419                 goto out;
420
421         for (list = driver_list; list; list = list->next) {
422                 DBusMessage *message;
423
424                 if (driver != list->data)
425                         continue;
426
427                 DBG("Sending release to %s path %s iface %s", agent_sender,
428                         agent_path, driver->interface);
429
430                 message = dbus_message_new_method_call(agent_sender, agent_path,
431                                 driver->interface, "Release");
432                 if (message != NULL) {
433                         dbus_message_set_no_reply(message, TRUE);
434                         g_dbus_send_message(connection, message);
435                 }
436
437                 agent_free();
438
439                 /*
440                  * ATM agent_free() unsets the agent_sender and agent_path
441                  * variables so we can unregister only once.
442                  * This needs proper fix later.
443                  */
444                 break;
445         }
446
447 out:
448         driver_list = g_slist_remove(driver_list, driver);
449 }
450
451 static void release_all_agents(void)
452 {
453         connman_agent_driver_unregister(get_driver());
454 }
455
456 int __connman_agent_init(void)
457 {
458         DBG("");
459
460         connection = connman_dbus_get_connection();
461         if (connection == NULL)
462                 return -1;
463
464         return 0;
465 }
466
467 void __connman_agent_cleanup(void)
468 {
469         DBG("");
470
471         if (connection == NULL)
472                 return;
473
474         if (agent_watch > 0)
475                 g_dbus_remove_watch(connection, agent_watch);
476
477         release_all_agents();
478
479         dbus_connection_unref(connection);
480         connection = NULL;
481 }