Imported Upstream version 1.24
[platform/upstream/connman.git] / src / agent.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2013  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 #define agent_ref(agent) \
37         agent_ref_debug(agent, __FILE__, __LINE__, __func__)
38 #define agent_unref(agent) \
39         agent_unref_debug(agent, __FILE__, __LINE__, __func__)
40
41 static DBusConnection *connection = NULL;
42 static GHashTable *agent_hash = NULL;
43 static struct connman_agent *default_agent = NULL;
44
45 struct connman_agent {
46         int refcount;
47         char *owner;
48         char *path;
49         struct connman_agent_request *pending;
50         GList *queue;   /* queued requests for this agent */
51         guint watch;
52 };
53
54 struct connman_agent_request {
55         void *user_context;
56         void *user_data;
57         DBusMessage *msg;
58         DBusPendingCall *call;
59         int timeout;
60         agent_queue_cb callback;
61         struct connman_agent_driver *driver;
62 };
63
64 static GSList *driver_list = NULL;
65
66 void *connman_agent_get_info(const char *dbus_sender, const char **sender,
67                                                         const char **path)
68 {
69         struct connman_agent *agent;
70
71         if (!dbus_sender)
72                 agent = default_agent;
73         else {
74                 agent = g_hash_table_lookup(agent_hash, dbus_sender);
75                 if (!agent)
76                         agent = default_agent;
77         }
78
79         if (agent) {
80                 if (sender)
81                         *sender = agent->owner;
82                 if (path)
83                         *path = agent->path;
84         } else {
85                 if (sender)
86                         *sender = NULL;
87                 if (path)
88                         *path = NULL;
89         }
90
91         return agent;
92 }
93
94 static void agent_request_free(struct connman_agent_request *request)
95 {
96         if (!request)
97                 return;
98
99         if (request->user_context) {
100                 if (request->driver && request->driver->context_unref)
101                         request->driver->context_unref(request->user_context);
102         }
103
104         if (request->msg)
105                 dbus_message_unref(request->msg);
106
107         if (request->call) {
108                 dbus_pending_call_cancel(request->call);
109                 dbus_pending_call_unref(request->call);
110         }
111
112         g_free(request);
113 }
114
115 static void agent_finalize_pending(struct connman_agent *agent,
116                                 DBusMessage *reply)
117 {
118         struct connman_agent_request *pending = agent->pending;
119         if (pending) {
120                 agent->pending = NULL;
121                 pending->callback(reply, pending->user_data);
122                 agent_request_free(pending);
123         }
124 }
125
126 static void agent_receive_message(DBusPendingCall *call, void *user_data);
127
128 static int agent_send_next_request(struct connman_agent *agent)
129 {
130         if (agent->pending)
131                 return -EBUSY;
132
133         if (!agent->queue)
134                 return 0;
135
136         agent->pending = agent->queue->data;
137         agent->queue = g_list_remove(agent->queue, agent->pending);
138
139         if (!agent->pending->msg)
140                 goto fail;
141
142         if (!dbus_connection_send_with_reply(connection, agent->pending->msg,
143                                                 &agent->pending->call,
144                                                 agent->pending->timeout))
145                 goto fail;
146
147         if (!agent->pending->call)
148                 goto fail;
149
150         if (!dbus_pending_call_set_notify(agent->pending->call,
151                                                 agent_receive_message,
152                                                 agent, NULL))
153                 goto fail;
154
155         dbus_message_unref(agent->pending->msg);
156         agent->pending->msg = NULL;
157         return 0;
158
159 fail:
160         agent_finalize_pending(agent, NULL);
161         return -ESRCH;
162 }
163
164 static int send_cancel_request(struct connman_agent *agent,
165                         struct connman_agent_request *request)
166 {
167         DBusMessage *message;
168
169         DBG("send cancel req to %s %s", agent->owner, agent->path);
170
171         message = dbus_message_new_method_call(agent->owner,
172                                         agent->path,
173                                         request->driver->interface,
174                                         "Cancel");
175         if (!message) {
176                 connman_error("Couldn't allocate D-Bus message");
177                 return -ENOMEM;
178         }
179
180         g_dbus_send_message(connection, message);
181         return 0;
182 }
183
184 static void agent_receive_message(DBusPendingCall *call, void *user_data)
185 {
186         struct connman_agent *agent = user_data;
187         DBusMessage *reply;
188         int err;
189
190         DBG("agent %p req %p", agent, agent->pending);
191
192         reply = dbus_pending_call_steal_reply(call);
193         dbus_pending_call_unref(call);
194         agent->pending->call = NULL;
195
196         if (dbus_message_is_error(reply,
197                         "org.freedesktop.DBus.Error.Timeout") ||
198                         dbus_message_is_error(reply,
199                         "org.freedesktop.DBus.Error.TimedOut")) {
200                 send_cancel_request(agent, agent->pending);
201         }
202
203         agent_finalize_pending(agent, reply);
204         dbus_message_unref(reply);
205
206         err = agent_send_next_request(agent);
207         if (err < 0 && err != -EBUSY)
208                 DBG("send next request failed (%s/%d)", strerror(-err), -err);
209 }
210
211 static struct connman_agent_driver *get_driver(void)
212 {
213         return g_slist_nth_data(driver_list, 0);
214 }
215
216 int connman_agent_queue_message(void *user_context,
217                                 DBusMessage *msg, int timeout,
218                                 agent_queue_cb callback, void *user_data,
219                                 void *agent_data)
220 {
221         struct connman_agent_request *queue_data;
222         struct connman_agent_driver *driver;
223         struct connman_agent *agent = agent_data;
224         int err;
225
226         if (!user_context || !callback)
227                 return -EBADMSG;
228
229         queue_data = g_new0(struct connman_agent_request, 1);
230         if (!queue_data)
231                 return -ENOMEM;
232
233         driver = get_driver();
234         DBG("driver %p", driver);
235
236         if (driver && driver->context_ref) {
237                 queue_data->user_context = driver->context_ref(user_context);
238                 queue_data->driver = driver;
239         } else
240                 queue_data->user_context = user_context;
241
242         queue_data->msg = dbus_message_ref(msg);
243         queue_data->timeout = timeout;
244         queue_data->callback = callback;
245         queue_data->user_data = user_data;
246         agent->queue = g_list_append(agent->queue, queue_data);
247
248         err = agent_send_next_request(agent);
249         if (err < 0 && err != -EBUSY)
250                 DBG("send next request failed (%s/%d)", strerror(-err), -err);
251
252         return err;
253 }
254
255 static void set_default_agent(void)
256 {
257         struct connman_agent *agent = NULL;
258         GHashTableIter iter;
259         gpointer key, value;
260
261         if (default_agent)
262                 return;
263
264         g_hash_table_iter_init(&iter, agent_hash);
265         if (g_hash_table_iter_next(&iter, &key, &value))
266                 agent = value;
267
268         if (agent)
269                 DBG("default agent set to %s %s", agent->owner, agent->path);
270         else
271                 DBG("default agent cleared");
272
273         default_agent = agent;
274 }
275
276 static void agent_disconnect(DBusConnection *conn, void *user_data)
277 {
278         struct connman_agent *agent = user_data;
279
280         DBG("agent %s disconnected", agent->owner);
281
282         if (agent->watch > 0) {
283                 g_dbus_remove_watch(conn, agent->watch);
284                 agent->watch = 0;
285         }
286
287         g_hash_table_remove(agent_hash, agent->owner);
288 }
289
290 static struct connman_agent *agent_ref_debug(struct connman_agent *agent,
291                                 const char *file, int line, const char *caller)
292 {
293         DBG("%p ref %d by %s:%d:%s()", agent, agent->refcount + 1,
294                 file, line, caller);
295
296         __sync_fetch_and_add(&agent->refcount, 1);
297
298         return agent;
299 }
300
301 static struct connman_agent *agent_create(const char *name, const char *path)
302 {
303         struct connman_agent *agent;
304
305         agent = g_new0(struct connman_agent, 1);
306
307         agent->owner = g_strdup(name);
308         agent->path = g_strdup(path);
309
310         agent->watch = g_dbus_add_disconnect_watch(connection,
311                                                         name, agent_disconnect,
312                                                         agent, NULL);
313
314         return agent_ref(agent);
315 }
316
317 int connman_agent_register(const char *sender, const char *path)
318 {
319         struct connman_agent *agent;
320
321         DBG("sender %s path %s", sender, path);
322
323         agent = g_hash_table_lookup(agent_hash, sender);
324         if (agent)
325                 return -EEXIST;
326
327         agent = agent_create(sender, path);
328         if (!agent)
329                 return -EINVAL;
330
331         DBG("agent %s", agent->owner);
332
333         g_hash_table_replace(agent_hash, agent->owner, agent);
334
335         if (!default_agent)
336                 set_default_agent();
337
338         return 0;
339 }
340
341 struct report_error_data {
342         void *user_context;
343         report_error_cb_t callback;
344         void *user_data;
345 };
346
347 static void report_error_reply(DBusMessage *reply, void *user_data)
348 {
349         struct report_error_data *report_error = user_data;
350         bool retry = false;
351         const char *dbus_err;
352
353         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
354                 dbus_err = dbus_message_get_error_name(reply);
355                 if (dbus_err &&
356                         strcmp(dbus_err,
357                                 CONNMAN_AGENT_INTERFACE ".Error.Retry") == 0)
358                         retry = true;
359         }
360
361         report_error->callback(report_error->user_context, retry,
362                         report_error->user_data);
363         g_free(report_error);
364 }
365
366 int connman_agent_report_error(void *user_context, const char *path,
367                                 const char *error,
368                                 report_error_cb_t callback,
369                                 const char *dbus_sender, void *user_data)
370 {
371         DBusMessage *message;
372         DBusMessageIter iter;
373         struct report_error_data *report_error;
374         struct connman_agent *agent;
375         int err;
376
377         agent = connman_agent_get_info(dbus_sender, NULL, NULL);
378
379         DBG("agent %p sender %s context %p path %s", agent,
380                 dbus_sender, user_context, agent ? agent->path : "-");
381
382         if (!user_context || !agent || !agent->path || !error || !callback)
383                 return -ESRCH;
384
385         message = dbus_message_new_method_call(agent->owner, agent->path,
386                                         CONNMAN_AGENT_INTERFACE,
387                                         "ReportError");
388         if (!message)
389                 return -ENOMEM;
390
391         dbus_message_iter_init_append(message, &iter);
392
393         dbus_message_iter_append_basic(&iter,
394                                 DBUS_TYPE_OBJECT_PATH, &path);
395         dbus_message_iter_append_basic(&iter,
396                                 DBUS_TYPE_STRING, &error);
397
398         report_error = g_try_new0(struct report_error_data, 1);
399         if (!report_error) {
400                 dbus_message_unref(message);
401                 return -ENOMEM;
402         }
403
404         report_error->user_context = user_context;
405         report_error->callback = callback;
406         report_error->user_data = user_data;
407
408         err = connman_agent_queue_message(user_context, message,
409                                         connman_timeout_input_request(),
410                                         report_error_reply, report_error,
411                                         agent);
412         if (err < 0 && err != -EBUSY) {
413                 DBG("error %d sending error request", err);
414                 g_free(report_error);
415                 dbus_message_unref(message);
416                 return -ESRCH;
417         }
418
419         dbus_message_unref(message);
420
421         return -EINPROGRESS;
422 }
423
424 static gint compare_priority(gconstpointer a, gconstpointer b)
425 {
426         const struct connman_agent_driver *driver1 = a;
427         const struct connman_agent_driver *driver2 = b;
428
429         return driver2->priority - driver1->priority;
430 }
431
432 /**
433  * connman_agent_driver_register:
434  * @driver: Agent driver definition
435  *
436  * Register a new agent driver
437  *
438  * Returns: %0 on success
439  */
440 int connman_agent_driver_register(struct connman_agent_driver *driver)
441 {
442         DBG("Registering driver %p name %s", driver, driver->name);
443
444         driver_list = g_slist_insert_sorted(driver_list, driver,
445                                                         compare_priority);
446
447         return 0;
448 }
449
450 static void release_driver(void)
451 {
452         connman_agent_driver_unregister(get_driver());
453 }
454
455 static void cancel_all_requests(struct connman_agent *agent)
456 {
457         GList *list;
458
459         DBG("request %p pending %p", agent->pending, agent->queue);
460
461         if (agent->pending) {
462                 if (agent->pending->call)
463                         send_cancel_request(agent, agent->pending);
464
465                 agent_finalize_pending(agent, NULL);
466         }
467
468         for (list = agent->queue; list; list = list->next) {
469                 struct connman_agent_request *request = list->data;
470
471                 if (!request)
472                         continue;
473
474                 request->callback(NULL, request->user_data);
475                 agent_request_free(request);
476         }
477
478         g_list_free(agent->queue);
479         agent->queue = NULL;
480 }
481
482 void connman_agent_cancel(void *user_context)
483 {
484         GHashTableIter iter;
485         gpointer key, value;
486         int err;
487
488         DBG("context %p", user_context);
489
490         g_hash_table_iter_init(&iter, agent_hash);
491         while (g_hash_table_iter_next(&iter, &key, &value)) {
492                 GList *list;
493                 struct connman_agent *agent = value;
494
495                 /*
496                  * Cancel all the pending requests to a given agent and service
497                  */
498                 list = agent->queue;
499                 while (list) {
500                         struct connman_agent_request *request = list->data;
501
502                         if (request && request->user_context &&
503                                                 request->user_context ==
504                                                                 user_context) {
505                                 DBG("cancel pending %p", request);
506
507                                 request->callback(NULL, request->user_data);
508
509                                 agent_request_free(request);
510
511                                 agent->queue = list->next;
512                                 list = g_list_delete_link(list, list);
513                         } else
514                                 list = list->next;
515                 }
516
517                 /*
518                  * If there is a request from client to a given service,
519                  * we need to cancel it.
520                  */
521                 if (agent->pending && agent->pending->user_context &&
522                                 agent->pending->user_context == user_context) {
523                         DBG("cancel request %p", agent->pending);
524
525                         if (agent->pending->call)
526                                 send_cancel_request(agent, agent->pending);
527
528                         agent_finalize_pending(agent, NULL);
529
530                         err = agent_send_next_request(agent);
531                         if (err < 0 && err != -EBUSY)
532                                 DBG("send next request failed (%s/%d)",
533                                                 strerror(-err), -err);
534                 }
535         }
536 }
537
538 static void agent_unref_debug(struct connman_agent *agent,
539                         const char *file, int line, const char *caller)
540 {
541         DBG("%p ref %d by %s:%d:%s()", agent, agent->refcount - 1,
542                 file, line, caller);
543
544         if (__sync_fetch_and_sub(&agent->refcount, 1) != 1)
545                 return;
546
547         cancel_all_requests(agent);
548
549         g_free(agent->owner);
550         g_free(agent->path);
551
552         if (agent == default_agent) {
553                 default_agent = NULL;
554                 set_default_agent();
555         }
556
557         g_free(agent);
558 }
559
560 static void agent_release(struct connman_agent *agent, const char *interface)
561 {
562         DBusMessage *message;
563
564         DBG("release agent %s %s", agent->owner, agent->path);
565
566         message = dbus_message_new_method_call(agent->owner, agent->path,
567                                                 interface, "Release");
568         if (message == NULL) {
569                 connman_error("Couldn't allocate D-Bus message");
570                 return;
571         }
572
573         dbus_message_set_no_reply(message, TRUE);
574         g_dbus_send_message(connection, message);
575 }
576
577 static void release_agents(void)
578 {
579         GHashTableIter iter;
580         gpointer key, value;
581
582         g_hash_table_iter_init(&iter, agent_hash);
583         while (g_hash_table_iter_next(&iter, &key, &value))
584                 agent_release(value, get_driver()->interface);
585 }
586
587 /**
588  * connman_agent_driver_unregister:
589  * @driver: Agent driver definition
590  *
591  * Remove a previously registered agent driver
592  */
593 void connman_agent_driver_unregister(struct connman_agent_driver *driver)
594 {
595         GSList *list;
596
597         if (!driver)
598                 return;
599
600         DBG("Unregistering driver %p name %s", driver, driver->name);
601
602         release_agents();
603
604         for (list = driver_list; list; list = list->next) {
605                 if (driver != list->data)
606                         continue;
607
608                 g_hash_table_remove_all(agent_hash);
609                 break;
610         }
611
612         driver_list = g_slist_remove(driver_list, driver);
613 }
614
615 static void agent_destroy(gpointer data)
616 {
617         struct connman_agent *agent = data;
618
619         DBG("agent %s req %p", agent->owner, agent->pending);
620
621         if (agent->watch > 0) {
622                 g_dbus_remove_watch(connection, agent->watch);
623                 agent->watch = 0;
624         }
625
626         agent_unref(agent);
627 }
628
629 int connman_agent_unregister(const char *sender, const char *path)
630 {
631         DBG("sender %s path %s", sender, path);
632
633         if (!g_hash_table_remove(agent_hash, sender))
634                 return -ESRCH;
635
636         return 0;
637 }
638
639 int __connman_agent_init(void)
640 {
641         DBG("");
642
643         connection = connman_dbus_get_connection();
644         if (!connection)
645                 return -EINVAL;
646
647         agent_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
648                                                 NULL, agent_destroy);
649         if (!agent_hash)
650                 return -ENOMEM;
651
652         return 0;
653 }
654
655 void __connman_agent_cleanup(void)
656 {
657         DBG("");
658
659         if (!connection)
660                 return;
661
662         g_hash_table_destroy(agent_hash);
663
664         release_driver();
665
666         dbus_connection_unref(connection);
667         connection = NULL;
668 }