1/*
2 *
3 *  BlueZ - Bluetooth protocol stack for Linux
4 *
5 *  Copyright (C) 2006-2010  Nokia Corporation
6 *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
7 *
8 *
9 *  This program is free software; you can redistribute it and/or modify
10 *  it under the terms of the GNU General Public License as published by
11 *  the Free Software Foundation; either version 2 of the License, or
12 *  (at your option) any later version.
13 *
14 *  This program is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this program; if not, write to the Free Software
21 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 */
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#include <stdio.h>
30#include <errno.h>
31#include <stdlib.h>
32#include <sys/socket.h>
33#include <sys/ioctl.h>
34
35#include <bluetooth/bluetooth.h>
36#include <bluetooth/sdp.h>
37
38#include <glib.h>
39#include <dbus/dbus.h>
40#include <gdbus.h>
41
42#include "log.h"
43
44#include "hcid.h"
45#include "adapter.h"
46#include "device.h"
47#include "agent.h"
48
49#define REQUEST_TIMEOUT (60 * 1000)		/* 60 seconds */
50
51typedef enum {
52	AGENT_REQUEST_PASSKEY,
53	AGENT_REQUEST_CONFIRMATION,
54	AGENT_REQUEST_PINCODE,
55	AGENT_REQUEST_AUTHORIZE,
56	AGENT_REQUEST_CONFIRM_MODE,
57	AGENT_REQUEST_PAIRING_CONSENT,
58} agent_request_type_t;
59
60struct agent {
61	struct btd_adapter *adapter;
62	char *name;
63	char *path;
64	uint8_t capability;
65	struct agent_request *request;
66	int exited;
67	agent_remove_cb remove_cb;
68	void *remove_cb_data;
69	guint listener_id;
70};
71
72struct agent_request {
73	agent_request_type_t type;
74	struct agent *agent;
75	DBusMessage *msg;
76	DBusPendingCall *call;
77	void *cb;
78	void *user_data;
79	GDestroyNotify destroy;
80};
81
82static DBusConnection *connection = NULL;
83
84static int request_fallback(struct agent_request *req,
85				DBusPendingCallNotifyFunction function);
86
87static void agent_release(struct agent *agent)
88{
89	DBusMessage *message;
90
91	DBG("Releasing agent %s, %s", agent->name, agent->path);
92
93	if (agent->request)
94		agent_cancel(agent);
95
96	message = dbus_message_new_method_call(agent->name, agent->path,
97			"org.bluez.Agent", "Release");
98	if (message == NULL) {
99		error("Couldn't allocate D-Bus message");
100		return;
101	}
102
103	g_dbus_send_message(connection, message);
104}
105
106static int send_cancel_request(struct agent_request *req)
107{
108	DBusMessage *message;
109
110	DBG("Sending Cancel request to %s, %s", req->agent->name,
111							req->agent->path);
112
113	message = dbus_message_new_method_call(req->agent->name, req->agent->path,
114						"org.bluez.Agent", "Cancel");
115	if (message == NULL) {
116		error("Couldn't allocate D-Bus message");
117		return -ENOMEM;
118	}
119
120	g_dbus_send_message(connection, message);
121
122	return 0;
123}
124
125static void agent_request_free(struct agent_request *req, gboolean destroy)
126{
127	if (req->msg)
128		dbus_message_unref(req->msg);
129	if (req->call)
130		dbus_pending_call_unref(req->call);
131	if (req->agent && req->agent->request)
132		req->agent->request = NULL;
133	if (destroy && req->destroy)
134		req->destroy(req->user_data);
135	g_free(req);
136}
137
138static void agent_exited(DBusConnection *conn, void *user_data)
139{
140	struct agent *agent = user_data;
141
142	DBG("Agent exited without calling Unregister");
143
144	agent->exited = TRUE;
145
146	agent_free(agent);
147}
148
149void agent_free(struct agent *agent)
150{
151	if (!agent)
152		return;
153
154	if (agent->remove_cb)
155		agent->remove_cb(agent, agent->remove_cb_data);
156
157	if (agent->request) {
158		DBusError err;
159		agent_pincode_cb pincode_cb;
160		agent_cb cb;
161
162		dbus_error_init(&err);
163		dbus_set_error_const(&err, "org.bluez.Error.Failed", "Canceled");
164
165		switch (agent->request->type) {
166		case AGENT_REQUEST_PINCODE:
167			pincode_cb = agent->request->cb;
168			pincode_cb(agent, &err, NULL, agent->request->user_data);
169			break;
170		default:
171			cb = agent->request->cb;
172			cb(agent, &err, agent->request->user_data);
173		}
174
175		dbus_error_free(&err);
176
177		agent_cancel(agent);
178	}
179
180	if (!agent->exited) {
181		g_dbus_remove_watch(connection, agent->listener_id);
182		agent_release(agent);
183	}
184
185	g_free(agent->name);
186	g_free(agent->path);
187
188	g_free(agent);
189}
190
191struct agent *agent_create(struct btd_adapter *adapter, const char *name,
192				const char *path, uint8_t capability,
193				agent_remove_cb cb, void *remove_cb_data)
194{
195	struct agent *agent;
196
197	agent = g_new0(struct agent, 1);
198
199	agent->adapter = adapter;
200	agent->name = g_strdup(name);
201	agent->path = g_strdup(path);
202	agent->capability = capability;
203	agent->remove_cb = cb;
204	agent->remove_cb_data = remove_cb_data;
205
206	agent->listener_id = g_dbus_add_disconnect_watch(connection, name,
207							agent_exited, agent,
208							NULL);
209
210	return agent;
211}
212
213static struct agent_request *agent_request_new(struct agent *agent,
214						agent_request_type_t type,
215						void *cb,
216						void *user_data,
217						GDestroyNotify destroy)
218{
219	struct agent_request *req;
220
221	req = g_new0(struct agent_request, 1);
222
223	req->agent = agent;
224	req->type = type;
225	req->cb = cb;
226	req->user_data = user_data;
227	req->destroy = destroy;
228
229	return req;
230}
231
232int agent_cancel(struct agent *agent)
233{
234	if (!agent->request)
235		return -EINVAL;
236
237	if (agent->request->call)
238		dbus_pending_call_cancel(agent->request->call);
239
240	if (!agent->exited)
241		send_cancel_request(agent->request);
242
243	agent_request_free(agent->request, TRUE);
244	agent->request = NULL;
245
246	return 0;
247}
248
249static void simple_agent_reply(DBusPendingCall *call, void *user_data)
250{
251	struct agent_request *req = user_data;
252	struct agent *agent = req->agent;
253	DBusMessage *message;
254	DBusError err;
255	agent_cb cb = req->cb;
256
257	/* steal_reply will always return non-NULL since the callback
258	 * is only called after a reply has been received */
259	message = dbus_pending_call_steal_reply(call);
260
261	dbus_error_init(&err);
262	if (dbus_set_error_from_message(&err, message)) {
263		if ((g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err.name) ||
264				g_str_equal(DBUS_ERROR_NO_REPLY, err.name)) &&
265				request_fallback(req, simple_agent_reply) == 0) {
266			dbus_error_free(&err);
267			return;
268		}
269
270		error("Agent replied with an error: %s, %s",
271				err.name, err.message);
272
273		cb(agent, &err, req->user_data);
274
275		if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
276			agent_cancel(agent);
277			dbus_message_unref(message);
278			dbus_error_free(&err);
279			return;
280		}
281
282		dbus_error_free(&err);
283		goto done;
284	}
285
286	dbus_error_init(&err);
287	if (!dbus_message_get_args(message, &err, DBUS_TYPE_INVALID)) {
288		error("Wrong reply signature: %s", err.message);
289		cb(agent, &err, req->user_data);
290		dbus_error_free(&err);
291		goto done;
292	}
293
294	cb(agent, NULL, req->user_data);
295done:
296	dbus_message_unref(message);
297
298	agent->request = NULL;
299	agent_request_free(req, TRUE);
300}
301
302static int agent_call_authorize(struct agent_request *req,
303				const char *device_path,
304				const char *uuid)
305{
306	struct agent *agent = req->agent;
307
308	req->msg = dbus_message_new_method_call(agent->name, agent->path,
309				"org.bluez.Agent", "Authorize");
310	if (!req->msg) {
311		error("Couldn't allocate D-Bus message");
312		return -ENOMEM;
313	}
314
315	dbus_message_append_args(req->msg,
316				DBUS_TYPE_OBJECT_PATH, &device_path,
317				DBUS_TYPE_STRING, &uuid,
318				DBUS_TYPE_INVALID);
319
320	if (dbus_connection_send_with_reply(connection, req->msg,
321					&req->call, REQUEST_TIMEOUT) == FALSE) {
322		error("D-Bus send failed");
323		return -EIO;
324	}
325
326	dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
327	return 0;
328}
329
330int agent_authorize(struct agent *agent,
331			const char *path,
332			const char *uuid,
333			agent_cb cb,
334			void *user_data,
335			GDestroyNotify destroy)
336{
337	struct agent_request *req;
338	int err;
339
340	if (agent->request)
341		return -EBUSY;
342
343	req = agent_request_new(agent, AGENT_REQUEST_AUTHORIZE, cb,
344							user_data, destroy);
345
346	err = agent_call_authorize(req, path, uuid);
347	if (err < 0) {
348		agent_request_free(req, FALSE);
349		return -ENOMEM;
350	}
351
352	agent->request = req;
353
354	DBG("authorize request was sent for %s", path);
355
356	return 0;
357}
358
359static void pincode_reply(DBusPendingCall *call, void *user_data)
360{
361	struct agent_request *req = user_data;
362	struct agent *agent = req->agent;
363	struct btd_adapter *adapter = agent->adapter;
364	agent_pincode_cb cb = req->cb;
365	DBusMessage *message;
366	DBusError err;
367	bdaddr_t sba;
368	size_t len;
369	char *pin;
370
371	adapter_get_address(adapter, &sba);
372
373	/* steal_reply will always return non-NULL since the callback
374	 * is only called after a reply has been received */
375	message = dbus_pending_call_steal_reply(call);
376
377	dbus_error_init(&err);
378	if (dbus_set_error_from_message(&err, message)) {
379		if ((g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err.name) ||
380				g_str_equal(DBUS_ERROR_NO_REPLY, err.name)) &&
381				request_fallback(req, pincode_reply) == 0) {
382			dbus_error_free(&err);
383			return;
384		}
385
386		error("Agent replied with an error: %s, %s",
387				err.name, err.message);
388
389		cb(agent, &err, NULL, req->user_data);
390		dbus_error_free(&err);
391		goto done;
392	}
393
394	dbus_error_init(&err);
395	if (!dbus_message_get_args(message, &err,
396				DBUS_TYPE_STRING, &pin,
397				DBUS_TYPE_INVALID)) {
398		error("Wrong passkey reply signature: %s", err.message);
399		cb(agent, &err, NULL, req->user_data);
400		dbus_error_free(&err);
401		goto done;
402	}
403
404	len = strlen(pin);
405
406	dbus_error_init(&err);
407	if (len < 1) {
408		error("Invalid PIN length (%zu) from agent", len);
409		dbus_set_error_const(&err, "org.bluez.Error.InvalidArgs",
410					"Invalid passkey length");
411		cb(agent, &err, NULL, req->user_data);
412		dbus_error_free(&err);
413		goto done;
414	}
415
416	cb(agent, NULL, pin, req->user_data);
417
418done:
419	if (message)
420		dbus_message_unref(message);
421
422	dbus_pending_call_cancel(req->call);
423	agent->request = NULL;
424	agent_request_free(req, TRUE);
425}
426
427static int pincode_request_new(struct agent_request *req, const char *device_path,
428				dbus_bool_t numeric)
429{
430	struct agent *agent = req->agent;
431
432	req->msg = dbus_message_new_method_call(agent->name, agent->path,
433					"org.bluez.Agent", "RequestPinCode");
434	if (req->msg == NULL) {
435		error("Couldn't allocate D-Bus message");
436		return -ENOMEM;
437	}
438
439	dbus_message_append_args(req->msg, DBUS_TYPE_OBJECT_PATH, &device_path,
440					DBUS_TYPE_INVALID);
441
442	if (dbus_connection_send_with_reply(connection, req->msg,
443					&req->call, REQUEST_TIMEOUT) == FALSE) {
444		error("D-Bus send failed");
445		return -EIO;
446	}
447
448	dbus_pending_call_set_notify(req->call, pincode_reply, req, NULL);
449	return 0;
450}
451
452int agent_request_pincode(struct agent *agent, struct btd_device *device,
453				agent_pincode_cb cb, void *user_data,
454				GDestroyNotify destroy)
455{
456	struct agent_request *req;
457	const gchar *dev_path = device_get_path(device);
458	int err;
459
460	if (agent->request)
461		return -EBUSY;
462
463	req = agent_request_new(agent, AGENT_REQUEST_PINCODE, cb,
464							user_data, destroy);
465
466	err = pincode_request_new(req, dev_path, FALSE);
467	if (err < 0)
468		goto failed;
469
470	agent->request = req;
471
472	return 0;
473
474failed:
475	g_free(req);
476	return err;
477}
478
479static int confirm_mode_change_request_new(struct agent_request *req,
480						const char *mode)
481{
482	struct agent *agent = req->agent;
483
484	req->msg = dbus_message_new_method_call(agent->name, agent->path,
485				"org.bluez.Agent", "ConfirmModeChange");
486	if (req->msg == NULL) {
487		error("Couldn't allocate D-Bus message");
488		return -ENOMEM;
489	}
490
491	dbus_message_append_args(req->msg,
492				DBUS_TYPE_STRING, &mode,
493				DBUS_TYPE_INVALID);
494
495	if (dbus_connection_send_with_reply(connection, req->msg,
496					&req->call, REQUEST_TIMEOUT) == FALSE) {
497		error("D-Bus send failed");
498		return -EIO;
499	}
500
501	dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
502	return 0;
503}
504
505int agent_confirm_mode_change(struct agent *agent, const char *new_mode,
506				agent_cb cb, void *user_data,
507				GDestroyNotify destroy)
508{
509	struct agent_request *req;
510	int err;
511
512	if (agent->request)
513		return -EBUSY;
514
515	DBG("Calling Agent.ConfirmModeChange: name=%s, path=%s, mode=%s",
516			agent->name, agent->path, new_mode);
517
518	req = agent_request_new(agent, AGENT_REQUEST_CONFIRM_MODE,
519				cb, user_data, destroy);
520
521	err = confirm_mode_change_request_new(req, new_mode);
522	if (err < 0)
523		goto failed;
524
525	agent->request = req;
526
527	return 0;
528
529failed:
530	agent_request_free(req, FALSE);
531	return err;
532}
533
534static void passkey_reply(DBusPendingCall *call, void *user_data)
535{
536	struct agent_request *req = user_data;
537	struct agent *agent = req->agent;
538	agent_passkey_cb cb = req->cb;
539	DBusMessage *message;
540	DBusError err;
541	uint32_t passkey;
542
543	/* steal_reply will always return non-NULL since the callback
544	 * is only called after a reply has been received */
545	message = dbus_pending_call_steal_reply(call);
546
547	dbus_error_init(&err);
548	if (dbus_set_error_from_message(&err, message)) {
549		if ((g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err.name) ||
550				g_str_equal(DBUS_ERROR_NO_REPLY, err.name)) &&
551				request_fallback(req, passkey_reply) == 0) {
552			dbus_error_free(&err);
553			return;
554		}
555
556		error("Agent replied with an error: %s, %s",
557				err.name, err.message);
558		cb(agent, &err, 0, req->user_data);
559		dbus_error_free(&err);
560		goto done;
561	}
562
563	dbus_error_init(&err);
564	if (!dbus_message_get_args(message, &err,
565				DBUS_TYPE_UINT32, &passkey,
566				DBUS_TYPE_INVALID)) {
567		error("Wrong passkey reply signature: %s", err.message);
568		cb(agent, &err, 0, req->user_data);
569		dbus_error_free(&err);
570		goto done;
571	}
572
573	cb(agent, NULL, passkey, req->user_data);
574
575done:
576	if (message)
577		dbus_message_unref(message);
578
579	dbus_pending_call_cancel(req->call);
580	agent->request = NULL;
581	agent_request_free(req, TRUE);
582}
583
584static int passkey_request_new(struct agent_request *req,
585				const char *device_path)
586{
587	struct agent *agent = req->agent;
588
589	req->msg = dbus_message_new_method_call(agent->name, agent->path,
590					"org.bluez.Agent", "RequestPasskey");
591	if (req->msg == NULL) {
592		error("Couldn't allocate D-Bus message");
593		return -ENOMEM;
594	}
595
596	dbus_message_append_args(req->msg, DBUS_TYPE_OBJECT_PATH, &device_path,
597					DBUS_TYPE_INVALID);
598
599	if (dbus_connection_send_with_reply(connection, req->msg,
600					&req->call, REQUEST_TIMEOUT) == FALSE) {
601		error("D-Bus send failed");
602		return -EIO;
603	}
604
605	dbus_pending_call_set_notify(req->call, passkey_reply, req, NULL);
606	return 0;
607}
608
609int agent_request_passkey(struct agent *agent, struct btd_device *device,
610				agent_passkey_cb cb, void *user_data,
611				GDestroyNotify destroy)
612{
613	struct agent_request *req;
614	const gchar *dev_path = device_get_path(device);
615	int err;
616
617	if (agent->request)
618		return -EBUSY;
619
620	DBG("Calling Agent.RequestPasskey: name=%s, path=%s",
621			agent->name, agent->path);
622
623	req = agent_request_new(agent, AGENT_REQUEST_PASSKEY, cb,
624							user_data, destroy);
625
626	err = passkey_request_new(req, dev_path);
627	if (err < 0)
628		goto failed;
629
630	agent->request = req;
631
632	return 0;
633
634failed:
635	agent_request_free(req, FALSE);
636	return err;
637}
638
639static int confirmation_request_new(struct agent_request *req,
640					const char *device_path,
641					uint32_t passkey)
642{
643	struct agent *agent = req->agent;
644
645	req->msg = dbus_message_new_method_call(agent->name, agent->path,
646				"org.bluez.Agent", "RequestConfirmation");
647	if (req->msg == NULL) {
648		error("Couldn't allocate D-Bus message");
649		return -ENOMEM;
650	}
651
652	dbus_message_append_args(req->msg,
653				DBUS_TYPE_OBJECT_PATH, &device_path,
654				DBUS_TYPE_UINT32, &passkey,
655				DBUS_TYPE_INVALID);
656
657	if (dbus_connection_send_with_reply(connection, req->msg,
658				&req->call, REQUEST_TIMEOUT) == FALSE) {
659		error("D-Bus send failed");
660		return -EIO;
661	}
662
663	dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
664
665	return 0;
666}
667
668int agent_request_confirmation(struct agent *agent, struct btd_device *device,
669				uint32_t passkey, agent_cb cb,
670				void *user_data, GDestroyNotify destroy)
671{
672	struct agent_request *req;
673	const gchar *dev_path = device_get_path(device);
674	int err;
675
676	if (agent->request)
677		return -EBUSY;
678
679	DBG("Calling Agent.RequestConfirmation: name=%s, path=%s, passkey=%06u",
680			agent->name, agent->path, passkey);
681
682	req = agent_request_new(agent, AGENT_REQUEST_CONFIRMATION, cb,
683				user_data, destroy);
684
685	err = confirmation_request_new(req, dev_path, passkey);
686	if (err < 0)
687		goto failed;
688
689	agent->request = req;
690
691	return 0;
692
693failed:
694	agent_request_free(req, FALSE);
695	return err;
696}
697
698static int pairing_consent_request_new(struct agent_request *req,
699						const char *device_path)
700{
701	struct agent *agent = req->agent;
702
703	req->msg = dbus_message_new_method_call(agent->name, agent->path,
704				"org.bluez.Agent", "RequestPairingConsent");
705	if (req->msg == NULL) {
706		error("Couldn't allocate D-Bus message");
707		return -ENOMEM;
708	}
709
710	dbus_message_append_args(req->msg,
711				DBUS_TYPE_OBJECT_PATH, &device_path,
712				DBUS_TYPE_INVALID);
713
714	if (dbus_connection_send_with_reply(connection, req->msg,
715				&req->call, REQUEST_TIMEOUT) == FALSE) {
716		error("D-Bus send failed");
717		return -EIO;
718	}
719
720	dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
721
722	return 0;
723}
724
725int agent_request_pairing_consent(struct agent *agent, struct btd_device *device,
726				agent_cb cb, void *user_data,
727				GDestroyNotify destroy)
728{
729	struct agent_request *req;
730	const gchar *dev_path = device_get_path(device);
731	int err;
732
733	if (agent->request)
734		return -EBUSY;
735
736	DBG("Calling Agent.RequestPairingConsent: name=%s, path=%s",
737			agent->name, agent->path);
738
739	req = agent_request_new(agent, AGENT_REQUEST_PAIRING_CONSENT, cb,
740				user_data, destroy);
741
742	err = pairing_consent_request_new(req, dev_path);
743	if (err < 0)
744		goto failed;
745
746	agent->request = req;
747
748	return 0;
749
750failed:
751	agent_request_free(req, FALSE);
752	return err;
753}
754
755static int request_fallback(struct agent_request *req,
756				DBusPendingCallNotifyFunction function)
757{
758	struct btd_adapter *adapter = req->agent->adapter;
759	struct agent *adapter_agent = adapter_get_agent(adapter);
760	DBusMessage *msg;
761
762	if (req->agent == adapter_agent || adapter_agent == NULL)
763		return -EINVAL;
764
765	dbus_pending_call_cancel(req->call);
766	dbus_pending_call_unref(req->call);
767
768	msg = dbus_message_copy(req->msg);
769
770	dbus_message_set_destination(msg, adapter_agent->name);
771	dbus_message_set_path(msg, adapter_agent->path);
772
773	if (dbus_connection_send_with_reply(connection, msg,
774					&req->call, REQUEST_TIMEOUT) == FALSE) {
775		error("D-Bus send failed");
776		dbus_message_unref(msg);
777		return -EIO;
778	}
779
780	req->agent->request = NULL;
781	req->agent = adapter_agent;
782	req->agent->request = req;
783
784	dbus_message_unref(req->msg);
785	req->msg = msg;
786
787	dbus_pending_call_set_notify(req->call, function, req, NULL);
788
789	return 0;
790}
791
792int agent_display_passkey(struct agent *agent, struct btd_device *device,
793				uint32_t passkey)
794{
795	DBusMessage *message;
796	const gchar *dev_path = device_get_path(device);
797
798	message = dbus_message_new_method_call(agent->name, agent->path,
799				"org.bluez.Agent", "DisplayPasskey");
800	if (!message) {
801		error("Couldn't allocate D-Bus message");
802		return -1;
803	}
804
805	dbus_message_append_args(message,
806				DBUS_TYPE_OBJECT_PATH, &dev_path,
807				DBUS_TYPE_UINT32, &passkey,
808				DBUS_TYPE_INVALID);
809
810	if (!g_dbus_send_message(connection, message)) {
811		error("D-Bus send failed");
812		dbus_message_unref(message);
813		return -1;
814	}
815
816	return 0;
817}
818
819uint8_t agent_get_io_capability(struct agent *agent)
820{
821	return agent->capability;
822}
823
824gboolean agent_matches(struct agent *agent, const char *name, const char *path)
825{
826	if (g_str_equal(agent->name, name) && g_str_equal(agent->path, path))
827		return TRUE;
828
829	return FALSE;
830}
831
832gboolean agent_is_busy(struct agent *agent, void *user_data)
833{
834	if (!agent->request)
835		return FALSE;
836
837	if (user_data && user_data != agent->request->user_data)
838		return FALSE;
839
840	return TRUE;
841}
842
843void agent_exit(void)
844{
845	dbus_connection_unref(connection);
846	connection = NULL;
847}
848
849void agent_init(void)
850{
851	connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
852}
853