agent.c 15.1 KB
Newer Older
1 2 3 4
/*
 *
 *  Connection Manager
 *
5
 *  Copyright (C) 2007-2013  Intel Corporation. All rights reserved.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include <gdbus.h>
#include <connman/agent.h>
#include <connman/setting.h>

34
#include "connman.h"
35

36 37 38 39 40
#define agent_ref(agent) \
	agent_ref_debug(agent, __FILE__, __LINE__, __func__)
#define agent_unref(agent) \
	agent_unref_debug(agent, __FILE__, __LINE__, __func__)

41
static DBusConnection *connection = NULL;
42 43
static GHashTable *agent_hash = NULL;
static struct connman_agent *default_agent = NULL;
44 45

struct connman_agent {
46 47 48 49 50 51 52 53 54
	int refcount;
	char *owner;
	char *path;
	struct connman_agent_request *pending;
	GList *queue;	/* queued requests for this agent */
	guint watch;
};

struct connman_agent_request {
55 56 57 58 59 60 61 62 63 64 65
	void *user_context;
	void *user_data;
	DBusMessage *msg;
	DBusPendingCall *call;
	int timeout;
	agent_queue_cb callback;
	struct connman_agent_driver *driver;
};

static GSList *driver_list = NULL;

66 67
void *connman_agent_get_info(const char *dbus_sender, const char **sender,
							const char **path)
68
{
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
	struct connman_agent *agent;

	if (!dbus_sender)
		agent = default_agent;
	else {
		agent = g_hash_table_lookup(agent_hash, dbus_sender);
		if (!agent)
			agent = default_agent;
	}

	if (agent) {
		if (sender)
			*sender = agent->owner;
		if (path)
			*path = agent->path;
	} else {
		if (sender)
			*sender = NULL;
		if (path)
			*path = NULL;
	}

	return agent;
92 93
}

94
static void agent_request_free(struct connman_agent_request *request)
95
{
96
	if (!request)
97
		return;
98 99 100 101 102 103 104 105 106 107 108 109

	if (request->user_context) {
		if (request->driver && request->driver->context_unref)
			request->driver->context_unref(request->user_context);
	}

	if (request->msg)
		dbus_message_unref(request->msg);

	if (request->call) {
		dbus_pending_call_cancel(request->call);
		dbus_pending_call_unref(request->call);
110 111
	}

112
	g_free(request);
113 114
}

115 116 117 118 119 120 121 122 123 124 125
static void agent_finalize_pending(struct connman_agent *agent,
				DBusMessage *reply)
{
	struct connman_agent_request *pending = agent->pending;
	if (pending) {
		agent->pending = NULL;
		pending->callback(reply, pending->user_data);
		agent_request_free(pending);
	}
}

126 127
static void agent_receive_message(DBusPendingCall *call, void *user_data);

128
static int agent_send_next_request(struct connman_agent *agent)
129
{
130
	if (agent->pending)
131 132
		return -EBUSY;

133
	if (!agent->queue)
134 135
		return 0;

136 137
	agent->pending = agent->queue->data;
	agent->queue = g_list_remove(agent->queue, agent->pending);
138

139
	if (!agent->pending->msg)
140 141
		goto fail;

142 143 144
	if (!dbus_connection_send_with_reply(connection, agent->pending->msg,
						&agent->pending->call,
						agent->pending->timeout))
145 146
		goto fail;

147
	if (!agent->pending->call)
148 149
		goto fail;

150 151 152 153 154 155 156
	if (!dbus_pending_call_set_notify(agent->pending->call,
						agent_receive_message,
						agent, NULL))
		goto fail;

	dbus_message_unref(agent->pending->msg);
	agent->pending->msg = NULL;
157 158 159
	return 0;

fail:
160
	agent_finalize_pending(agent, NULL);
161 162 163
	return -ESRCH;
}

164 165
static int send_cancel_request(struct connman_agent *agent,
			struct connman_agent_request *request)
166 167
{
	DBusMessage *message;
168
	const char *interface = NULL;
169

170 171
	if (request && request->driver)
		interface = request->driver->interface;
172

173 174
	DBG("send cancel req to %s %s iface %s", agent->owner, agent->path,
								interface);
175

176 177
	message = dbus_message_new_method_call(agent->owner,
					agent->path,
178
					interface,
179 180 181 182
					"Cancel");
	if (!message) {
		connman_error("Couldn't allocate D-Bus message");
		return -ENOMEM;
183 184
	}

185 186
	g_dbus_send_message(connection, message);
	return 0;
187 188 189 190
}

static void agent_receive_message(DBusPendingCall *call, void *user_data)
{
191
	struct connman_agent *agent = user_data;
192 193 194
	DBusMessage *reply;
	int err;

195
	DBG("agent %p req %p", agent, agent->pending);
196 197 198

	reply = dbus_pending_call_steal_reply(call);
	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 205
			"org.freedesktop.DBus.Error.TimedOut")) {
		send_cancel_request(agent, agent->pending);
206 207
	}

208
	agent_finalize_pending(agent, reply);
209 210
	dbus_message_unref(reply);

211 212
	err = agent_send_next_request(agent);
	if (err < 0 && err != -EBUSY)
213 214 215 216 217 218 219 220 221 222
		DBG("send next request failed (%s/%d)", strerror(-err), -err);
}

static struct connman_agent_driver *get_driver(void)
{
	return g_slist_nth_data(driver_list, 0);
}

int connman_agent_queue_message(void *user_context,
				DBusMessage *msg, int timeout,
223 224
				agent_queue_cb callback, void *user_data,
				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 230
	int err;

231
	if (!callback)
232 233
		return -EBADMSG;

234 235
	queue_data = g_new0(struct connman_agent_request, 1);
	if (!queue_data)
236 237 238 239 240
		return -ENOMEM;

	driver = get_driver();
	DBG("driver %p", driver);

241
	if (user_context && driver && driver->context_ref) {
242 243
		queue_data->user_context = driver->context_ref(user_context);
		queue_data->driver = driver;
244
	} else {
245
		queue_data->user_context = user_context;
246
      }
247

248 249 250 251
	queue_data->msg = dbus_message_ref(msg);
	queue_data->timeout = timeout;
	queue_data->callback = callback;
	queue_data->user_data = user_data;
252
	agent->queue = g_list_append(agent->queue, queue_data);
253

254 255
	err = agent_send_next_request(agent);
	if (err < 0 && err != -EBUSY)
256 257 258 259 260
		DBG("send next request failed (%s/%d)", strerror(-err), -err);

	return err;
}

261
static void set_default_agent(void)
262
{
263 264 265
	struct connman_agent *agent = NULL;
	GHashTableIter iter;
	gpointer key, value;
266

267
	if (default_agent)
268 269
		return;

270 271 272
	g_hash_table_iter_init(&iter, agent_hash);
	if (g_hash_table_iter_next(&iter, &key, &value))
		agent = value;
273

274 275 276 277
	if (agent)
		DBG("default agent set to %s %s", agent->owner, agent->path);
	else
		DBG("default agent cleared");
278

279
	default_agent = agent;
280 281
}

282
static void agent_disconnect(DBusConnection *conn, void *user_data)
283
{
284
	struct connman_agent *agent = user_data;
285

286
	DBG("agent %s disconnected", agent->owner);
287

288 289 290 291
	if (agent->watch > 0) {
		g_dbus_remove_watch(conn, agent->watch);
		agent->watch = 0;
	}
292

293
	g_hash_table_remove(agent_hash, agent->owner);
294 295
}

296 297
static struct connman_agent *agent_ref_debug(struct connman_agent *agent,
				const char *file, int line, const char *caller)
298
{
299 300 301 302 303 304
	DBG("%p ref %d by %s:%d:%s()", agent, agent->refcount + 1,
		file, line, caller);

	__sync_fetch_and_add(&agent->refcount, 1);

	return agent;
305 306
}

307
static struct connman_agent *agent_create(const char *name, const char *path)
308
{
309 310 311
	struct connman_agent *agent;

	agent = g_new0(struct connman_agent, 1);
312

313 314
	agent->owner = g_strdup(name);
	agent->path = g_strdup(path);
315

316 317 318
	agent->watch = g_dbus_add_disconnect_watch(connection,
							name, agent_disconnect,
							agent, NULL);
319

320
	return agent_ref(agent);
321 322
}

323
int connman_agent_register(const char *sender, const char *path)
324
{
325 326
	struct connman_agent *agent;

327 328
	DBG("sender %s path %s", sender, path);

329 330 331 332 333 334 335
	agent = g_hash_table_lookup(agent_hash, sender);
	if (agent)
		return -EEXIST;

	agent = agent_create(sender, path);
	if (!agent)
		return -EINVAL;
336

337
	DBG("agent %s", agent->owner);
338

339 340 341 342
	g_hash_table_replace(agent_hash, agent->owner, agent);

	if (!default_agent)
		set_default_agent();
343 344 345 346 347 348 349 350 351 352 353 354 355

	return 0;
}

struct report_error_data {
	void *user_context;
	report_error_cb_t callback;
	void *user_data;
};

static void report_error_reply(DBusMessage *reply, void *user_data)
{
	struct report_error_data *report_error = user_data;
356
	bool retry = false;
357 358
	const char *dbus_err;

359 360 361
	if (!reply)
		goto out;

362
	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
363
		dbus_err = dbus_message_get_error_name(reply);
364
		if (dbus_err &&
365 366
			strcmp(dbus_err,
				CONNMAN_AGENT_INTERFACE ".Error.Retry") == 0)
367
			retry = true;
368 369 370 371
	}

	report_error->callback(report_error->user_context, retry,
			report_error->user_data);
372
out:
373 374 375
	g_free(report_error);
}

376 377
int connman_agent_report_error_full(void *user_context, const char *path,
				const char *method, const char *error,
378 379
				report_error_cb_t callback,
				const char *dbus_sender, void *user_data)
380 381 382 383
{
	DBusMessage *message;
	DBusMessageIter iter;
	struct report_error_data *report_error;
384
	struct connman_agent *agent;
385 386
	int err;

387 388 389 390 391 392
	agent = connman_agent_get_info(dbus_sender, NULL, NULL);

	DBG("agent %p sender %s context %p path %s", agent,
		dbus_sender, user_context, agent ? agent->path : "-");

	if (!user_context || !agent || !agent->path || !error || !callback)
393 394
		return -ESRCH;

395
	message = dbus_message_new_method_call(agent->owner, agent->path,
396
					CONNMAN_AGENT_INTERFACE, method);
397
	if (!message)
398 399 400 401 402 403 404 405 406 407
		return -ENOMEM;

	dbus_message_iter_init_append(message, &iter);

	dbus_message_iter_append_basic(&iter,
				DBUS_TYPE_OBJECT_PATH, &path);
	dbus_message_iter_append_basic(&iter,
				DBUS_TYPE_STRING, &error);

	report_error = g_try_new0(struct report_error_data, 1);
408
	if (!report_error) {
409 410 411 412 413 414 415 416 417 418
		dbus_message_unref(message);
		return -ENOMEM;
	}

	report_error->user_context = user_context;
	report_error->callback = callback;
	report_error->user_data = user_data;

	err = connman_agent_queue_message(user_context, message,
					connman_timeout_input_request(),
419 420
					report_error_reply, report_error,
					agent);
421 422 423 424 425 426 427 428 429 430 431 432
	if (err < 0 && err != -EBUSY) {
		DBG("error %d sending error request", err);
		g_free(report_error);
		dbus_message_unref(message);
		return -ESRCH;
	}

	dbus_message_unref(message);

	return -EINPROGRESS;
}

433 434 435 436 437 438 439 440 441 442
int connman_agent_report_error(void *user_context, const char *path,
				const char *error,
				report_error_cb_t callback,
				const char *dbus_sender, void *user_data)
{
	return connman_agent_report_error_full(user_context, path,
				"ReportError", error, callback, dbus_sender,
				user_data);
}

443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
static gint compare_priority(gconstpointer a, gconstpointer b)
{
	const struct connman_agent_driver *driver1 = a;
	const struct connman_agent_driver *driver2 = b;

	return driver2->priority - driver1->priority;
}

/**
 * connman_agent_driver_register:
 * @driver: Agent driver definition
 *
 * Register a new agent driver
 *
 * Returns: %0 on success
 */
int connman_agent_driver_register(struct connman_agent_driver *driver)
{
	DBG("Registering driver %p name %s", driver, driver->name);

	driver_list = g_slist_insert_sorted(driver_list, driver,
							compare_priority);

	return 0;
}

469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
static void release_driver(void)
{
	connman_agent_driver_unregister(get_driver());
}

static void cancel_all_requests(struct connman_agent *agent)
{
	GList *list;

	DBG("request %p pending %p", agent->pending, agent->queue);

	if (agent->pending) {
		if (agent->pending->call)
			send_cancel_request(agent, agent->pending);

484
		agent_finalize_pending(agent, NULL);
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
	}

	for (list = agent->queue; list; list = list->next) {
		struct connman_agent_request *request = list->data;

		if (!request)
			continue;

		request->callback(NULL, request->user_data);
		agent_request_free(request);
	}

	g_list_free(agent->queue);
	agent->queue = NULL;
}

void connman_agent_cancel(void *user_context)
{
	GHashTableIter iter;
	gpointer key, value;
	int err;

	DBG("context %p", user_context);

	g_hash_table_iter_init(&iter, agent_hash);
	while (g_hash_table_iter_next(&iter, &key, &value)) {
511
		GList *list, *next;
512 513 514 515 516 517 518 519 520
		struct connman_agent *agent = value;

		/*
		 * Cancel all the pending requests to a given agent and service
		 */
		list = agent->queue;
		while (list) {
			struct connman_agent_request *request = list->data;

521 522
			next = list->next;

523 524 525 526 527
			if (request && request->user_context &&
						request->user_context ==
								user_context) {
				DBG("cancel pending %p", request);

528 529 530
				agent->queue = g_list_delete_link(agent->queue,
									list);

531 532 533
				request->callback(NULL, request->user_data);

				agent_request_free(request);
534 535 536
			}

			list = next;
537 538 539 540 541 542 543 544 545 546 547 548 549
		}

		/*
		 * If there is a request from client to a given service,
		 * we need to cancel it.
		 */
		if (agent->pending && agent->pending->user_context &&
				agent->pending->user_context == user_context) {
			DBG("cancel request %p", agent->pending);

			if (agent->pending->call)
				send_cancel_request(agent, agent->pending);

550
			agent_finalize_pending(agent, NULL);
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589

			err = agent_send_next_request(agent);
			if (err < 0 && err != -EBUSY)
				DBG("send next request failed (%s/%d)",
						strerror(-err), -err);
		}
	}
}

static void agent_unref_debug(struct connman_agent *agent,
			const char *file, int line, const char *caller)
{
	DBG("%p ref %d by %s:%d:%s()", agent, agent->refcount - 1,
		file, line, caller);

	if (__sync_fetch_and_sub(&agent->refcount, 1) != 1)
		return;

	cancel_all_requests(agent);

	g_free(agent->owner);
	g_free(agent->path);

	if (agent == default_agent) {
		default_agent = NULL;
		set_default_agent();
	}

	g_free(agent);
}

static void agent_release(struct connman_agent *agent, const char *interface)
{
	DBusMessage *message;

	DBG("release agent %s %s", agent->owner, agent->path);

	message = dbus_message_new_method_call(agent->owner, agent->path,
						interface, "Release");
590
	if (!message) {
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
		connman_error("Couldn't allocate D-Bus message");
		return;
	}

	dbus_message_set_no_reply(message, TRUE);
	g_dbus_send_message(connection, message);
}

static void release_agents(void)
{
	GHashTableIter iter;
	gpointer key, value;

	g_hash_table_iter_init(&iter, agent_hash);
	while (g_hash_table_iter_next(&iter, &key, &value))
		agent_release(value, get_driver()->interface);
}

609 610 611 612 613 614 615 616 617 618
/**
 * connman_agent_driver_unregister:
 * @driver: Agent driver definition
 *
 * Remove a previously registered agent driver
 */
void connman_agent_driver_unregister(struct connman_agent_driver *driver)
{
	GSList *list;

619
	if (!driver)
620 621 622 623
		return;

	DBG("Unregistering driver %p name %s", driver, driver->name);

624
	release_agents();
625 626 627 628 629

	for (list = driver_list; list; list = list->next) {
		if (driver != list->data)
			continue;

630 631 632
		g_hash_table_remove_all(agent_hash);
		break;
	}
633

634 635
	driver_list = g_slist_remove(driver_list, driver);
}
636

637 638 639
static void agent_destroy(gpointer data)
{
	struct connman_agent *agent = data;
640

641 642 643 644 645
	DBG("agent %s req %p", agent->owner, agent->pending);

	if (agent->watch > 0) {
		g_dbus_remove_watch(connection, agent->watch);
		agent->watch = 0;
646 647
	}

648
	agent_unref(agent);
649 650
}

651
int connman_agent_unregister(const char *sender, const char *path)
652
{
653 654 655 656 657 658
	DBG("sender %s path %s", sender, path);

	if (!g_hash_table_remove(agent_hash, sender))
		return -ESRCH;

	return 0;
659 660 661 662 663 664 665
}

int __connman_agent_init(void)
{
	DBG("");

	connection = connman_dbus_get_connection();
666 667 668 669 670 671 672
	if (!connection)
		return -EINVAL;

	agent_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
						NULL, agent_destroy);
	if (!agent_hash)
		return -ENOMEM;
673 674 675 676 677 678 679 680

	return 0;
}

void __connman_agent_cleanup(void)
{
	DBG("");

681
	if (!connection)
682 683
		return;

684
	g_hash_table_destroy(agent_hash);
685

686
	release_driver();
687 688 689 690

	dbus_connection_unref(connection);
	connection = NULL;
}