a34002628f6f0f866db1020f98147b34d0904968
[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 (!reply)
354                 goto out;
355
356         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
357                 dbus_err = dbus_message_get_error_name(reply);
358                 if (dbus_err &&
359                         strcmp(dbus_err,
360                                 CONNMAN_AGENT_INTERFACE ".Error.Retry") == 0)
361                         retry = true;
362         }
363
364         report_error->callback(report_error->user_context, retry,
365                         report_error->user_data);
366 out:
367         g_free(report_error);
368 }
369
370 int connman_agent_report_error_full(void *user_context, const char *path,
371                                 const char *method, const char *error,
372                                 report_error_cb_t callback,
373                                 const char *dbus_sender, void *user_data)
374 {
375         DBusMessage *message;
376         DBusMessageIter iter;
377         struct report_error_data *report_error;
378         struct connman_agent *agent;
379         int err;
380
381         agent = connman_agent_get_info(dbus_sender, NULL, NULL);
382
383         DBG("agent %p sender %s context %p path %s", agent,
384                 dbus_sender, user_context, agent ? agent->path : "-");
385
386         if (!user_context || !agent || !agent->path || !error || !callback)
387                 return -ESRCH;
388
389         message = dbus_message_new_method_call(agent->owner, agent->path,
390                                         CONNMAN_AGENT_INTERFACE, method);
391         if (!message)
392                 return -ENOMEM;
393
394         dbus_message_iter_init_append(message, &iter);
395
396         dbus_message_iter_append_basic(&iter,
397                                 DBUS_TYPE_OBJECT_PATH, &path);
398         dbus_message_iter_append_basic(&iter,
399                                 DBUS_TYPE_STRING, &error);
400
401         report_error = g_try_new0(struct report_error_data, 1);
402         if (!report_error) {
403                 dbus_message_unref(message);
404                 return -ENOMEM;
405         }
406
407         report_error->user_context = user_context;
408         report_error->callback = callback;
409         report_error->user_data = user_data;
410
411         err = connman_agent_queue_message(user_context, message,
412                                         connman_timeout_input_request(),
413                                         report_error_reply, report_error,
414                                         agent);
415         if (err < 0 && err != -EBUSY) {
416                 DBG("error %d sending error request", err);
417                 g_free(report_error);
418                 dbus_message_unref(message);
419                 return -ESRCH;
420         }
421
422         dbus_message_unref(message);
423
424         return -EINPROGRESS;
425 }
426
427 int connman_agent_report_error(void *user_context, const char *path,
428                                 const char *error,
429                                 report_error_cb_t callback,
430                                 const char *dbus_sender, void *user_data)
431 {
432         return connman_agent_report_error_full(user_context, path,
433                                 "ReportError", error, callback, dbus_sender,
434                                 user_data);
435 }
436
437 static gint compare_priority(gconstpointer a, gconstpointer b)
438 {
439         const struct connman_agent_driver *driver1 = a;
440         const struct connman_agent_driver *driver2 = b;
441
442         return driver2->priority - driver1->priority;
443 }
444
445 /**
446  * connman_agent_driver_register:
447  * @driver: Agent driver definition
448  *
449  * Register a new agent driver
450  *
451  * Returns: %0 on success
452  */
453 int connman_agent_driver_register(struct connman_agent_driver *driver)
454 {
455         DBG("Registering driver %p name %s", driver, driver->name);
456
457         driver_list = g_slist_insert_sorted(driver_list, driver,
458                                                         compare_priority);
459
460         return 0;
461 }
462
463 static void release_driver(void)
464 {
465         connman_agent_driver_unregister(get_driver());
466 }
467
468 static void cancel_all_requests(struct connman_agent *agent)
469 {
470         GList *list;
471
472         DBG("request %p pending %p", agent->pending, agent->queue);
473
474         if (agent->pending) {
475                 if (agent->pending->call)
476                         send_cancel_request(agent, agent->pending);
477
478                 agent_finalize_pending(agent, NULL);
479         }
480
481         for (list = agent->queue; list; list = list->next) {
482                 struct connman_agent_request *request = list->data;
483
484                 if (!request)
485                         continue;
486
487                 request->callback(NULL, request->user_data);
488                 agent_request_free(request);
489         }
490
491         g_list_free(agent->queue);
492         agent->queue = NULL;
493 }
494
495 void connman_agent_cancel(void *user_context)
496 {
497         GHashTableIter iter;
498         gpointer key, value;
499         int err;
500
501         DBG("context %p", user_context);
502
503         g_hash_table_iter_init(&iter, agent_hash);
504         while (g_hash_table_iter_next(&iter, &key, &value)) {
505                 GList *list;
506                 struct connman_agent *agent = value;
507
508                 /*
509                  * Cancel all the pending requests to a given agent and service
510                  */
511                 list = agent->queue;
512                 while (list) {
513                         struct connman_agent_request *request = list->data;
514
515                         if (request && request->user_context &&
516                                                 request->user_context ==
517                                                                 user_context) {
518                                 DBG("cancel pending %p", request);
519
520                                 request->callback(NULL, request->user_data);
521
522                                 agent_request_free(request);
523
524                                 agent->queue = list->next;
525                                 list = g_list_delete_link(list, list);
526                         } else
527                                 list = list->next;
528                 }
529
530                 /*
531                  * If there is a request from client to a given service,
532                  * we need to cancel it.
533                  */
534                 if (agent->pending && agent->pending->user_context &&
535                                 agent->pending->user_context == user_context) {
536                         DBG("cancel request %p", agent->pending);
537
538                         if (agent->pending->call)
539                                 send_cancel_request(agent, agent->pending);
540
541                         agent_finalize_pending(agent, NULL);
542
543                         err = agent_send_next_request(agent);
544                         if (err < 0 && err != -EBUSY)
545                                 DBG("send next request failed (%s/%d)",
546                                                 strerror(-err), -err);
547                 }
548         }
549 }
550
551 static void agent_unref_debug(struct connman_agent *agent,
552                         const char *file, int line, const char *caller)
553 {
554         DBG("%p ref %d by %s:%d:%s()", agent, agent->refcount - 1,
555                 file, line, caller);
556
557         if (__sync_fetch_and_sub(&agent->refcount, 1) != 1)
558                 return;
559
560         cancel_all_requests(agent);
561
562         g_free(agent->owner);
563         g_free(agent->path);
564
565         if (agent == default_agent) {
566                 default_agent = NULL;
567                 set_default_agent();
568         }
569
570         g_free(agent);
571 }
572
573 static void agent_release(struct connman_agent *agent, const char *interface)
574 {
575         DBusMessage *message;
576
577         DBG("release agent %s %s", agent->owner, agent->path);
578
579         message = dbus_message_new_method_call(agent->owner, agent->path,
580                                                 interface, "Release");
581         if (message == NULL) {
582                 connman_error("Couldn't allocate D-Bus message");
583                 return;
584         }
585
586         dbus_message_set_no_reply(message, TRUE);
587         g_dbus_send_message(connection, message);
588 }
589
590 static void release_agents(void)
591 {
592         GHashTableIter iter;
593         gpointer key, value;
594
595         g_hash_table_iter_init(&iter, agent_hash);
596         while (g_hash_table_iter_next(&iter, &key, &value))
597                 agent_release(value, get_driver()->interface);
598 }
599
600 /**
601  * connman_agent_driver_unregister:
602  * @driver: Agent driver definition
603  *
604  * Remove a previously registered agent driver
605  */
606 void connman_agent_driver_unregister(struct connman_agent_driver *driver)
607 {
608         GSList *list;
609
610         if (!driver)
611                 return;
612
613         DBG("Unregistering driver %p name %s", driver, driver->name);
614
615         release_agents();
616
617         for (list = driver_list; list; list = list->next) {
618                 if (driver != list->data)
619                         continue;
620
621                 g_hash_table_remove_all(agent_hash);
622                 break;
623         }
624
625         driver_list = g_slist_remove(driver_list, driver);
626 }
627
628 static void agent_destroy(gpointer data)
629 {
630         struct connman_agent *agent = data;
631
632         DBG("agent %s req %p", agent->owner, agent->pending);
633
634         if (agent->watch > 0) {
635                 g_dbus_remove_watch(connection, agent->watch);
636                 agent->watch = 0;
637         }
638
639         agent_unref(agent);
640 }
641
642 int connman_agent_unregister(const char *sender, const char *path)
643 {
644         DBG("sender %s path %s", sender, path);
645
646         if (!g_hash_table_remove(agent_hash, sender))
647                 return -ESRCH;
648
649         return 0;
650 }
651
652 int __connman_agent_init(void)
653 {
654         DBG("");
655
656         connection = connman_dbus_get_connection();
657         if (!connection)
658                 return -EINVAL;
659
660         agent_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
661                                                 NULL, agent_destroy);
662         if (!agent_hash)
663                 return -ENOMEM;
664
665         return 0;
666 }
667
668 void __connman_agent_cleanup(void)
669 {
670         DBG("");
671
672         if (!connection)
673                 return;
674
675         g_hash_table_destroy(agent_hash);
676
677         release_driver();
678
679         dbus_connection_unref(connection);
680         connection = NULL;
681 }