Merge "Imported Upstream connman version 1.38" into tizen
[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         const char *interface = NULL;
169
170         if (request && request->driver)
171                 interface = request->driver->interface;
172
173         DBG("send cancel req to %s %s iface %s", agent->owner, agent->path,
174                                                                 interface);
175
176         message = dbus_message_new_method_call(agent->owner,
177                                         agent->path,
178                                         interface,
179                                         "Cancel");
180         if (!message) {
181                 connman_error("Couldn't allocate D-Bus message");
182                 return -ENOMEM;
183         }
184
185         g_dbus_send_message(connection, message);
186         return 0;
187 }
188
189 static void agent_receive_message(DBusPendingCall *call, void *user_data)
190 {
191         struct connman_agent *agent = user_data;
192         DBusMessage *reply;
193         int err;
194
195         DBG("agent %p req %p", agent, agent->pending);
196
197         reply = dbus_pending_call_steal_reply(call);
198         dbus_pending_call_unref(call);
199         agent->pending->call = NULL;
200
201         if (dbus_message_is_error(reply,
202                         "org.freedesktop.DBus.Error.Timeout") ||
203                         dbus_message_is_error(reply,
204                         "org.freedesktop.DBus.Error.TimedOut")) {
205                 send_cancel_request(agent, agent->pending);
206         }
207
208         agent_finalize_pending(agent, reply);
209         dbus_message_unref(reply);
210
211         err = agent_send_next_request(agent);
212         if (err < 0 && err != -EBUSY)
213                 DBG("send next request failed (%s/%d)", strerror(-err), -err);
214 }
215
216 static struct connman_agent_driver *get_driver(void)
217 {
218         return g_slist_nth_data(driver_list, 0);
219 }
220
221 int connman_agent_queue_message(void *user_context,
222                                 DBusMessage *msg, int timeout,
223                                 agent_queue_cb callback, void *user_data,
224                                 void *agent_data)
225 {
226         struct connman_agent_request *queue_data;
227         struct connman_agent_driver *driver;
228         struct connman_agent *agent = agent_data;
229         int err;
230
231         if (!user_context || !callback)
232                 return -EBADMSG;
233
234         queue_data = g_new0(struct connman_agent_request, 1);
235         if (!queue_data)
236                 return -ENOMEM;
237
238         driver = get_driver();
239         DBG("driver %p", driver);
240
241         if (driver && driver->context_ref)
242                 queue_data->user_context = driver->context_ref(user_context);
243         else
244                 queue_data->user_context = user_context;
245
246         queue_data->driver = driver;
247         queue_data->msg = dbus_message_ref(msg);
248         queue_data->timeout = timeout;
249         queue_data->callback = callback;
250         queue_data->user_data = user_data;
251         agent->queue = g_list_append(agent->queue, queue_data);
252
253         err = agent_send_next_request(agent);
254         if (err < 0 && err != -EBUSY)
255                 DBG("send next request failed (%s/%d)", strerror(-err), -err);
256
257         return err;
258 }
259
260 static void set_default_agent(void)
261 {
262         struct connman_agent *agent = NULL;
263         GHashTableIter iter;
264         gpointer key, value;
265
266         if (default_agent)
267                 return;
268
269         g_hash_table_iter_init(&iter, agent_hash);
270         if (g_hash_table_iter_next(&iter, &key, &value))
271                 agent = value;
272
273         if (agent)
274                 DBG("default agent set to %s %s", agent->owner, agent->path);
275         else
276                 DBG("default agent cleared");
277
278         default_agent = agent;
279 }
280
281 static void agent_disconnect(DBusConnection *conn, void *user_data)
282 {
283         struct connman_agent *agent = user_data;
284
285         DBG("agent %s disconnected", agent->owner);
286
287         if (agent->watch > 0) {
288                 g_dbus_remove_watch(conn, agent->watch);
289                 agent->watch = 0;
290         }
291
292         g_hash_table_remove(agent_hash, agent->owner);
293 }
294
295 static struct connman_agent *agent_ref_debug(struct connman_agent *agent,
296                                 const char *file, int line, const char *caller)
297 {
298         DBG("%p ref %d by %s:%d:%s()", agent, agent->refcount + 1,
299                 file, line, caller);
300
301         __sync_fetch_and_add(&agent->refcount, 1);
302
303         return agent;
304 }
305
306 static struct connman_agent *agent_create(const char *name, const char *path)
307 {
308         struct connman_agent *agent;
309
310         agent = g_new0(struct connman_agent, 1);
311
312         agent->owner = g_strdup(name);
313         agent->path = g_strdup(path);
314
315         agent->watch = g_dbus_add_disconnect_watch(connection,
316                                                         name, agent_disconnect,
317                                                         agent, NULL);
318
319         return agent_ref(agent);
320 }
321
322 int connman_agent_register(const char *sender, const char *path)
323 {
324         struct connman_agent *agent;
325
326         DBG("sender %s path %s", sender, path);
327
328         agent = g_hash_table_lookup(agent_hash, sender);
329         if (agent)
330                 return -EEXIST;
331
332         agent = agent_create(sender, path);
333         if (!agent)
334                 return -EINVAL;
335
336         DBG("agent %s", agent->owner);
337
338         g_hash_table_replace(agent_hash, agent->owner, agent);
339
340         if (!default_agent)
341                 set_default_agent();
342
343         return 0;
344 }
345
346 struct report_error_data {
347         void *user_context;
348         report_error_cb_t callback;
349         void *user_data;
350 };
351
352 static void report_error_reply(DBusMessage *reply, void *user_data)
353 {
354         struct report_error_data *report_error = user_data;
355         bool retry = false;
356         const char *dbus_err;
357
358         if (!reply)
359                 goto out;
360
361         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
362                 dbus_err = dbus_message_get_error_name(reply);
363                 if (dbus_err &&
364                         strcmp(dbus_err,
365                                 CONNMAN_AGENT_INTERFACE ".Error.Retry") == 0)
366                         retry = true;
367         }
368
369         report_error->callback(report_error->user_context, retry,
370                         report_error->user_data);
371 out:
372         g_free(report_error);
373 }
374
375 int connman_agent_report_error_full(void *user_context, const char *path,
376                                 const char *method, const char *error,
377                                 report_error_cb_t callback,
378                                 const char *dbus_sender, void *user_data)
379 {
380         DBusMessage *message;
381         DBusMessageIter iter;
382         struct report_error_data *report_error;
383         struct connman_agent *agent;
384         int err;
385
386         agent = connman_agent_get_info(dbus_sender, NULL, NULL);
387
388         DBG("agent %p sender %s context %p path %s", agent,
389                 dbus_sender, user_context, agent ? agent->path : "-");
390
391         if (!user_context || !agent || !agent->path || !error || !callback)
392                 return -ESRCH;
393
394         message = dbus_message_new_method_call(agent->owner, agent->path,
395                                         CONNMAN_AGENT_INTERFACE, method);
396         if (!message)
397                 return -ENOMEM;
398
399         dbus_message_iter_init_append(message, &iter);
400
401         dbus_message_iter_append_basic(&iter,
402                                 DBUS_TYPE_OBJECT_PATH, &path);
403         dbus_message_iter_append_basic(&iter,
404                                 DBUS_TYPE_STRING, &error);
405
406         report_error = g_try_new0(struct report_error_data, 1);
407         if (!report_error) {
408                 dbus_message_unref(message);
409                 return -ENOMEM;
410         }
411
412         report_error->user_context = user_context;
413         report_error->callback = callback;
414         report_error->user_data = user_data;
415
416         err = connman_agent_queue_message(user_context, message,
417                                         connman_timeout_input_request(),
418                                         report_error_reply, report_error,
419                                         agent);
420         if (err < 0 && err != -EBUSY) {
421                 DBG("error %d sending error request", err);
422                 g_free(report_error);
423                 dbus_message_unref(message);
424                 return -ESRCH;
425         }
426
427         dbus_message_unref(message);
428
429         return -EINPROGRESS;
430 }
431
432 int connman_agent_report_error(void *user_context, const char *path,
433                                 const char *error,
434                                 report_error_cb_t callback,
435                                 const char *dbus_sender, void *user_data)
436 {
437         return connman_agent_report_error_full(user_context, path,
438                                 "ReportError", error, callback, dbus_sender,
439                                 user_data);
440 }
441
442 static gint compare_priority(gconstpointer a, gconstpointer b)
443 {
444         const struct connman_agent_driver *driver1 = a;
445         const struct connman_agent_driver *driver2 = b;
446
447         return driver2->priority - driver1->priority;
448 }
449
450 /**
451  * connman_agent_driver_register:
452  * @driver: Agent driver definition
453  *
454  * Register a new agent driver
455  *
456  * Returns: %0 on success
457  */
458 int connman_agent_driver_register(struct connman_agent_driver *driver)
459 {
460         DBG("Registering driver %p name %s", driver, driver->name);
461
462         driver_list = g_slist_insert_sorted(driver_list, driver,
463                                                         compare_priority);
464
465         return 0;
466 }
467
468 static void release_driver(void)
469 {
470         connman_agent_driver_unregister(get_driver());
471 }
472
473 static void cancel_all_requests(struct connman_agent *agent)
474 {
475         GList *list;
476
477         DBG("request %p pending %p", agent->pending, agent->queue);
478
479         if (agent->pending) {
480                 if (agent->pending->call)
481                         send_cancel_request(agent, agent->pending);
482
483                 agent_finalize_pending(agent, NULL);
484         }
485
486         for (list = agent->queue; list; list = list->next) {
487                 struct connman_agent_request *request = list->data;
488
489                 if (!request)
490                         continue;
491
492                 request->callback(NULL, request->user_data);
493                 agent_request_free(request);
494         }
495
496         g_list_free(agent->queue);
497         agent->queue = NULL;
498 }
499
500 void connman_agent_cancel(void *user_context)
501 {
502         GHashTableIter iter;
503         gpointer key, value;
504         int err;
505
506         DBG("context %p", user_context);
507
508         g_hash_table_iter_init(&iter, agent_hash);
509         while (g_hash_table_iter_next(&iter, &key, &value)) {
510                 GList *list, *next;
511                 struct connman_agent *agent = value;
512
513                 /*
514                  * Cancel all the pending requests to a given agent and service
515                  */
516                 list = agent->queue;
517                 while (list) {
518                         struct connman_agent_request *request = list->data;
519
520                         next = list->next;
521
522                         if (request && request->user_context &&
523                                                 request->user_context ==
524                                                                 user_context) {
525                                 DBG("cancel pending %p", request);
526
527                                 agent->queue = g_list_delete_link(agent->queue,
528                                                                         list);
529
530                                 request->callback(NULL, request->user_data);
531
532                                 agent_request_free(request);
533                         }
534
535                         list = next;
536                 }
537
538                 /*
539                  * If there is a request from client to a given service,
540                  * we need to cancel it.
541                  */
542                 if (agent->pending && agent->pending->user_context &&
543                                 agent->pending->user_context == user_context) {
544                         DBG("cancel request %p", agent->pending);
545
546                         if (agent->pending->call)
547                                 send_cancel_request(agent, agent->pending);
548
549                         agent_finalize_pending(agent, NULL);
550
551                         err = agent_send_next_request(agent);
552                         if (err < 0 && err != -EBUSY)
553                                 DBG("send next request failed (%s/%d)",
554                                                 strerror(-err), -err);
555                 }
556         }
557 }
558
559 static void agent_unref_debug(struct connman_agent *agent,
560                         const char *file, int line, const char *caller)
561 {
562         DBG("%p ref %d by %s:%d:%s()", agent, agent->refcount - 1,
563                 file, line, caller);
564
565         if (__sync_fetch_and_sub(&agent->refcount, 1) != 1)
566                 return;
567
568         cancel_all_requests(agent);
569
570         g_free(agent->owner);
571         g_free(agent->path);
572
573         if (agent == default_agent) {
574                 default_agent = NULL;
575                 set_default_agent();
576         }
577
578         g_free(agent);
579 }
580
581 static void agent_release(struct connman_agent *agent, const char *interface)
582 {
583         DBusMessage *message;
584
585         DBG("release agent %s %s", agent->owner, agent->path);
586
587         message = dbus_message_new_method_call(agent->owner, agent->path,
588                                                 interface, "Release");
589         if (!message) {
590                 connman_error("Couldn't allocate D-Bus message");
591                 return;
592         }
593
594         dbus_message_set_no_reply(message, TRUE);
595         g_dbus_send_message(connection, message);
596 }
597
598 static void release_agents(void)
599 {
600         GHashTableIter iter;
601         gpointer key, value;
602
603         g_hash_table_iter_init(&iter, agent_hash);
604         while (g_hash_table_iter_next(&iter, &key, &value))
605                 agent_release(value, get_driver()->interface);
606 }
607
608 /**
609  * connman_agent_driver_unregister:
610  * @driver: Agent driver definition
611  *
612  * Remove a previously registered agent driver
613  */
614 void connman_agent_driver_unregister(struct connman_agent_driver *driver)
615 {
616         GSList *list;
617
618         if (!driver)
619                 return;
620
621         DBG("Unregistering driver %p name %s", driver, driver->name);
622
623         release_agents();
624
625         for (list = driver_list; list; list = list->next) {
626                 if (driver != list->data)
627                         continue;
628
629                 g_hash_table_remove_all(agent_hash);
630                 break;
631         }
632
633         driver_list = g_slist_remove(driver_list, driver);
634 }
635
636 static void agent_destroy(gpointer data)
637 {
638         struct connman_agent *agent = data;
639
640         DBG("agent %s req %p", agent->owner, agent->pending);
641
642         if (agent->watch > 0) {
643                 g_dbus_remove_watch(connection, agent->watch);
644                 agent->watch = 0;
645         }
646
647         agent_unref(agent);
648 }
649
650 int connman_agent_unregister(const char *sender, const char *path)
651 {
652         DBG("sender %s path %s", sender, path);
653
654         if (!g_hash_table_remove(agent_hash, sender))
655                 return -ESRCH;
656
657         return 0;
658 }
659
660 int __connman_agent_init(void)
661 {
662         DBG("");
663
664         connection = connman_dbus_get_connection();
665         if (!connection)
666                 return -EINVAL;
667
668         agent_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
669                                                 NULL, agent_destroy);
670         if (!agent_hash)
671                 return -ENOMEM;
672
673         return 0;
674 }
675
676 void __connman_agent_cleanup(void)
677 {
678         DBG("");
679
680         if (!connection)
681                 return;
682
683         g_hash_table_destroy(agent_hash);
684
685         release_driver();
686
687         dbus_connection_unref(connection);
688         connection = NULL;
689 }