1/***************************************************************************
2 * CVSID: $Id$
3 *
4 * dbus.c : D-BUS interface of HAL daemon
5 *
6 * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
7 *
8 * Licensed under the Academic Free License version 2.1
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23 *
24 **************************************************************************/
25
26#ifdef HAVE_CONFIG_H
27#  include <config.h>
28#endif
29
30#include <stdlib.h>
31#include <stdio.h>
32#include <string.h>
33#include <unistd.h>
34#include <stdarg.h>
35#include <stdint.h>
36#include <sys/time.h>
37
38#include <dbus/dbus.h>
39#include <dbus/dbus-glib-lowlevel.h>
40
41#include "hald.h"
42#include "hald_dbus.h"
43#include "device.h"
44#include "device_store.h"
45#include "device_info.h"
46#include "logger.h"
47#include "osspec.h"
48#include "util.h"
49#include "hald_runner.h"
50
51#define HALD_DBUS_ADDRESS "unix:tmpdir=" HALD_SOCKET_DIR
52
53static DBusConnection *dbus_connection = NULL;
54
55static void
56raise_error (DBusConnection *connection,
57	     DBusMessage *in_reply_to,
58	     const char *error_name,
59	     char *format, ...) __attribute__((format (printf, 4, 5)));
60
61/**
62 * @defgroup DaemonErrors Error conditions
63 * @ingroup HalDaemon
64 * @brief Various error messages the HAL daemon can raise
65 * @{
66 */
67
68/** Raise HAL error
69 *
70 *  @param  connection          D-Bus connection
71 *  @param  in_reply_to         message to report error on
72 *  @param  error_name          D-Bus error name
73 *  @param  format              printf-style format for error message
74 */
75static void
76raise_error (DBusConnection *connection,
77	     DBusMessage *in_reply_to,
78	     const char *error_name,
79	     char *format, ...)
80{
81	char buf[512];
82	DBusMessage *reply;
83
84	va_list args;
85	va_start(args, format);
86	vsnprintf(buf, sizeof buf, format, args);
87	va_end(args);
88
89	HAL_WARNING ((buf));
90	reply = dbus_message_new_error (in_reply_to, error_name, buf);
91	if (reply == NULL)
92		DIE (("No memory"));
93	if (!dbus_connection_send (connection, reply, NULL))
94		DIE (("No memory"));
95	dbus_message_unref (reply);
96}
97
98/** Raise the org.freedesktop.Hal.NoSuchDevice error
99 *
100 *  @param  connection          D-Bus connection
101 *  @param  in_reply_to         message to report error on
102 *  @param  udi                 Unique device id given
103 */
104static void
105raise_no_such_device (DBusConnection *connection,
106		      DBusMessage *in_reply_to, const char *udi)
107{
108	raise_error (
109		connection, in_reply_to,
110		"org.freedesktop.Hal.NoSuchDevice",
111		"No device with id %s",
112		udi
113	);
114}
115
116/** Raise the org.freedesktop.Hal.NoSuchProperty error
117 *
118 *  @param  connection          D-Bus connection
119 *  @param  in_reply_to         message to report error on
120 *  @param  device_id           Id of the device
121 *  @param  key                 Key of the property that didn't exist
122 */
123static void
124raise_no_such_property (DBusConnection *connection,
125			DBusMessage *in_reply_to,
126			const char *device_id, const char *key)
127{
128	raise_error (
129		connection, in_reply_to,
130		"org.freedesktop.Hal.NoSuchProperty",
131		"No property %s on device with id %s",
132		key, device_id
133	);
134}
135
136/** Raise the org.freedesktop.Hal.TypeMismatch error
137 *
138 *  @param  connection          D-Bus connection
139 *  @param  in_reply_to         message to report error on
140 *  @param  device_id           Id of the device
141 *  @param  key                 Key of the property
142 */
143static void
144raise_property_type_error (DBusConnection *connection,
145			   DBusMessage *in_reply_to,
146			   const char *device_id, const char *key)
147{
148	raise_error (
149		connection, in_reply_to,
150		"org.freedesktop.Hal.TypeMismatch",
151		"Type mismatch setting property %s on device with id %s",
152		key, device_id
153	);
154}
155
156/** Raise the org.freedesktop.Hal.SyntaxError error
157 *
158 *  @param  connection          D-Bus connection
159 *  @param  in_reply_to         message to report error on
160 *  @param  method_name         Name of the method that was invoked with
161 *                              the wrong signature
162 */
163static void
164raise_syntax (DBusConnection *connection,
165	      DBusMessage *in_reply_to, const char *method_name)
166{
167	raise_error (
168		connection, in_reply_to,
169		"org.freedesktop.Hal.SyntaxError",
170		"There is a syntax error in the invocation of the method %s",
171		method_name
172	);
173}
174
175/** Raise the org.freedesktop.Hal.DeviceNotLocked error
176 *
177 *  @param  connection          D-Bus connection
178 *  @param  in_reply_to         message to report error on
179 *  @param  device              device which isn't locked
180 */
181static void
182raise_device_not_locked (DBusConnection *connection,
183			 DBusMessage    *in_reply_to,
184			 HalDevice      *device)
185{
186	raise_error (
187		connection, in_reply_to,
188		"org.freedesktop.Hal.DeviceNotLocked",
189		"The device %s is not locked",
190		 hal_device_get_udi (device)
191	);
192}
193
194/** Raise the org.freedesktop.Hal.DeviceAlreadyLocked error
195 *
196 *  @param  connection          D-Bus connection
197 *  @param  in_reply_to         message to report error on
198 *  @param  device              device which isn't locked
199 */
200static void
201raise_device_already_locked (DBusConnection *connection,
202			     DBusMessage    *in_reply_to,
203			     HalDevice      *device)
204{
205	DBusMessage *reply;
206	const char *reason;
207
208	reason = hal_device_property_get_string (device, "info.locked.reason");
209	HAL_WARNING (("Device %s is already locked: %s",
210		      hal_device_get_udi (device), reason));
211
212
213	reply = dbus_message_new_error (in_reply_to,
214					"org.freedesktop.Hal.DeviceAlreadyLocked",
215					reason);
216
217	if (reply == NULL || !dbus_connection_send (connection, reply, NULL))
218		DIE (("No memory"));
219
220	dbus_message_unref (reply);
221}
222
223/** Raise the org.freedesktop.Hal.BranchAlreadyClaimed error
224 *
225 *  @param  connection          D-Bus connection
226 *  @param  in_reply_to         message to report error on
227 *  @param  udi                 branch which isn't claimed
228 */
229static void
230raise_branch_already_claimed (DBusConnection *connection,
231			     DBusMessage    *in_reply_to,
232			     HalDevice      *device)
233{
234	DBusMessage *reply;
235	const char *claim_service;
236
237	claim_service = hal_device_property_get_string (device, "info.claimed.service");
238	HAL_WARNING (("Branch %s is already claimed by: %s",
239		      hal_device_get_udi (device), claim_service));
240
241
242	reply = dbus_message_new_error (in_reply_to,
243					"org.freedesktop.Hal.BranchAlreadyClaimed",
244					claim_service);
245
246	if (reply == NULL || !dbus_connection_send (connection, reply, NULL))
247		DIE (("No memory"));
248
249	dbus_message_unref (reply);
250}
251
252/** Raise the org.freedesktop.Hal.BranchNotClaimed error
253 *
254 *  @param  connection          D-Bus connection
255 *  @param  in_reply_to         message to report error on
256 *  @param  udi                 branch which isn't claimed
257 */
258static void
259raise_branch_not_claimed (DBusConnection *connection,
260			     DBusMessage    *in_reply_to,
261			     HalDevice      *device)
262{
263	raise_error (
264		connection, in_reply_to,
265		"org.freedesktop.Hal.BranchNotClaimed",
266		"The branch %s is not claimed",
267		 hal_device_get_udi (device)
268	);
269}
270
271/** Raise the org.freedesktop.Hal.PermissionDenied error
272 *
273 *  @param  connection          D-Bus connection
274 *  @param  in_reply_to         message to report error on
275 *  @param  message             what you're not allowed to do
276 */
277static void
278raise_permission_denied (DBusConnection *connection,
279			 DBusMessage    *in_reply_to,
280			 const char     *reason)
281{
282	raise_error (
283		connection, in_reply_to,
284		"org.freedesktop.Hal.PermissionDenied",
285		"Permission denied: %s",
286		 reason
287	);
288}
289
290/** @} */
291
292/**
293 * @defgroup ManagerInterface D-BUS interface org.freedesktop.Hal.Manager
294 * @ingroup HalDaemon
295 * @brief D-BUS interface for querying device objects
296 *
297 * @{
298 */
299
300static gboolean
301foreach_device_get_udi (HalDeviceStore *store, HalDevice *device,
302			gpointer user_data)
303{
304	DBusMessageIter *iter = user_data;
305	const char *udi;
306
307	udi = hal_device_get_udi (device);
308	dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &udi);
309
310	return TRUE;
311}
312
313/** Get all devices.
314 *
315 *  <pre>
316 *  array{object_reference} Manager.GetAllDevices()
317 *  </pre>
318 *
319 *  @param  connection          D-BUS connection
320 *  @param  message             Message
321 *  @return                     What to do with the message
322 */
323DBusHandlerResult
324manager_get_all_devices (DBusConnection * connection,
325			 DBusMessage * message)
326{
327	DBusMessage *reply;
328	DBusMessageIter iter;
329	DBusMessageIter iter_array;
330
331	reply = dbus_message_new_method_return (message);
332	if (reply == NULL)
333		DIE (("No memory"));
334
335	dbus_message_iter_init_append (reply, &iter);
336	dbus_message_iter_open_container (&iter,
337					  DBUS_TYPE_ARRAY,
338					  DBUS_TYPE_STRING_AS_STRING,
339					  &iter_array);
340
341	hal_device_store_foreach (hald_get_gdl (),
342				  foreach_device_get_udi,
343				  &iter_array);
344
345	dbus_message_iter_close_container (&iter, &iter_array);
346
347	if (!dbus_connection_send (connection, reply, NULL))
348		DIE (("No memory"));
349
350	dbus_message_unref (reply);
351
352	return DBUS_HANDLER_RESULT_HANDLED;
353}
354
355typedef struct {
356	const char *key;
357	const char *value;
358	DBusMessageIter *iter;
359} DeviceMatchInfo;
360
361static gboolean
362foreach_device_match_get_udi (HalDeviceStore *store, HalDevice *device,
363			      gpointer user_data)
364{
365	DeviceMatchInfo *info = user_data;
366	const char *dev_value;
367
368	if (hal_device_property_get_type (device,
369					  info->key) != DBUS_TYPE_STRING)
370		return TRUE;
371
372	dev_value = hal_device_property_get_string (device, info->key);
373
374	if (dev_value != NULL && strcmp (dev_value, info->value) == 0) {
375		const char *udi;
376		udi =  hal_device_get_udi (device);
377		dbus_message_iter_append_basic  (info->iter,
378						 DBUS_TYPE_STRING,
379						 &udi);
380	}
381
382	return TRUE;
383}
384
385static gboolean
386foreach_device_match_get_udi_tdl (HalDeviceStore *store, HalDevice *device,
387				  gpointer user_data)
388{
389	DeviceMatchInfo *info = user_data;
390	const char *dev_value;
391
392	/* skip devices in the TDL that hasn't got a real UDI yet */
393	if (strncmp (device->udi, "/org/freedesktop/Hal/devices/temp",
394		     sizeof ("/org/freedesktop/Hal/devices/temp")) == 0)
395		return TRUE;
396
397	if (hal_device_property_get_type (device,
398					  info->key) != DBUS_TYPE_STRING)
399		return TRUE;
400
401	dev_value = hal_device_property_get_string (device, info->key);
402
403	if (dev_value != NULL && strcmp (dev_value, info->value) == 0) {
404		const char *udi;
405		udi = hal_device_get_udi (device);
406
407		dbus_message_iter_append_basic (info->iter,
408						DBUS_TYPE_STRING,
409						&udi);
410	}
411
412	return TRUE;
413}
414
415/** Find devices in the GDL where a single string property matches a given
416 *  value. Also returns devices in the TDL that has a non-tmp UDI.
417 *
418 *  <pre>
419 *  array{object_reference} Manager.FindDeviceStringMatch(string key,
420 *                                                        string value)
421 *  </pre>
422 *
423 *  @param  connection          D-BUS connection
424 *  @param  message             Message
425 *  @return                     What to do with the message
426 */
427DBusHandlerResult
428manager_find_device_string_match (DBusConnection * connection,
429				  DBusMessage * message)
430{
431	DBusMessage *reply;
432	DBusMessageIter iter;
433	DBusMessageIter iter_array;
434	DBusError error;
435	const char *key;
436	const char *value;
437	DeviceMatchInfo info;
438
439	HAL_TRACE (("entering"));
440
441	dbus_error_init (&error);
442	if (!dbus_message_get_args (message, &error,
443				    DBUS_TYPE_STRING, &key,
444				    DBUS_TYPE_STRING, &value,
445				    DBUS_TYPE_INVALID)) {
446		raise_syntax (connection, message,
447			      "Manager.FindDeviceStringMatch");
448		return DBUS_HANDLER_RESULT_HANDLED;
449	}
450
451	reply = dbus_message_new_method_return (message);
452	if (reply == NULL)
453		DIE (("No memory"));
454
455	dbus_message_iter_init_append (reply, &iter);
456	dbus_message_iter_open_container (&iter,
457					  DBUS_TYPE_ARRAY,
458					  DBUS_TYPE_STRING_AS_STRING,
459					  &iter_array);
460
461	info.key = key;
462	info.value = value;
463	info.iter = &iter_array;
464
465	hal_device_store_foreach (hald_get_gdl (),
466				  foreach_device_match_get_udi,
467				  &info);
468
469	/* Also returns devices in the TDL that has a non-tmp UDI */
470	hal_device_store_foreach (hald_get_tdl (),
471				  foreach_device_match_get_udi_tdl,
472				  &info);
473
474	dbus_message_iter_close_container (&iter, &iter_array);
475
476	if (!dbus_connection_send (connection, reply, NULL))
477		DIE (("No memory"));
478
479	dbus_message_unref (reply);
480
481	return DBUS_HANDLER_RESULT_HANDLED;
482}
483
484typedef struct {
485	const char *capability;
486	DBusMessageIter *iter;
487} DeviceCapabilityInfo;
488
489static gboolean
490foreach_device_by_capability (HalDeviceStore *store, HalDevice *device, gpointer user_data)
491{
492	DeviceCapabilityInfo *info = (DeviceCapabilityInfo *) user_data;
493
494	if (hal_device_has_capability (device, info->capability)) {
495		dbus_message_iter_append_basic (info->iter,
496						DBUS_TYPE_STRING,
497						&(device->udi));
498	}
499
500	return TRUE;
501}
502
503/** Find devices in the GDL with a given capability.
504 *
505 *  <pre>
506 *  array{object_reference} Manager.FindDeviceByCapability(string capability)
507 *  </pre>
508 *
509 *  @param  connection          D-BUS connection
510 *  @param  message             Message
511 *  @return                     What to do with the message
512 */
513DBusHandlerResult
514manager_find_device_by_capability (DBusConnection * connection,
515				   DBusMessage * message)
516{
517	DBusMessage *reply;
518	DBusMessageIter iter;
519	DBusMessageIter iter_array;
520	DBusError error;
521	const char *capability;
522	DeviceCapabilityInfo info;
523
524	HAL_TRACE (("entering"));
525
526	dbus_error_init (&error);
527	if (!dbus_message_get_args (message, &error,
528				    DBUS_TYPE_STRING, &capability,
529				    DBUS_TYPE_INVALID)) {
530		raise_syntax (connection, message,
531			      "Manager.FindDeviceByCapability");
532		return DBUS_HANDLER_RESULT_HANDLED;
533	}
534
535	reply = dbus_message_new_method_return (message);
536	if (reply == NULL)
537		DIE (("No memory"));
538
539	dbus_message_iter_init_append (reply, &iter);
540	dbus_message_iter_open_container (&iter,
541					  DBUS_TYPE_ARRAY,
542					  DBUS_TYPE_STRING_AS_STRING,
543					  &iter_array);
544
545	info.capability = capability;
546	info.iter = &iter_array;
547
548	hal_device_store_foreach (hald_get_gdl (),
549				  foreach_device_by_capability,
550				  &info);
551
552	dbus_message_iter_close_container (&iter, &iter_array);
553
554	if (!dbus_connection_send (connection, reply, NULL))
555		DIE (("No memory"));
556
557	dbus_message_unref (reply);
558
559	return DBUS_HANDLER_RESULT_HANDLED;
560}
561
562
563/** Determine if a device exists.
564 *
565 *  <pre>
566 *  bool Manager.DeviceExists(string udi)
567 *  </pre>
568 *
569 *  @param  connection          D-BUS connection
570 *  @param  message             Message
571 *  @return                     What to do with the message
572 */
573DBusHandlerResult
574manager_device_exists (DBusConnection * connection, DBusMessage * message)
575{
576	DBusMessage *reply;
577	DBusMessageIter iter;
578	DBusError error;
579	HalDevice *d;
580	const char *udi;
581	dbus_bool_t b;
582
583	dbus_error_init (&error);
584	if (!dbus_message_get_args (message, &error,
585				    DBUS_TYPE_STRING, &udi,
586				    DBUS_TYPE_INVALID)) {
587		raise_syntax (connection, message, "Manager.DeviceExists");
588		return DBUS_HANDLER_RESULT_HANDLED;
589	}
590
591	HAL_TRACE (("entering, udi=%s", udi));
592
593	d = hal_device_store_find (hald_get_gdl (), udi);
594
595	if (d == NULL)
596		d = hal_device_store_find (hald_get_tdl (), udi);
597
598	reply = dbus_message_new_method_return (message);
599	dbus_message_iter_init_append (reply, &iter);
600	b = d != NULL;
601	dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &b);
602
603	if (reply == NULL)
604		DIE (("No memory"));
605
606	if (!dbus_connection_send (connection, reply, NULL))
607		DIE (("No memory"));
608
609	dbus_message_unref (reply);
610	return DBUS_HANDLER_RESULT_HANDLED;
611}
612
613/** Send signal DeviceAdded(string udi) on the org.freedesktop.Hal.Manager
614 *  interface on the object /org/freedesktop/Hal/Manager.
615 *
616 *  @param  device              The HalDevice added
617 */
618void
619manager_send_signal_device_added (HalDevice *device)
620{
621	const char *udi = hal_device_get_udi (device);
622	DBusMessage *message;
623	DBusMessageIter iter;
624
625	if (dbus_connection == NULL)
626		goto out;
627
628	HAL_TRACE (("entering, udi=%s", udi));
629
630	message = dbus_message_new_signal ("/org/freedesktop/Hal/Manager",
631					   "org.freedesktop.Hal.Manager",
632					   "DeviceAdded");
633
634	dbus_message_iter_init_append (message, &iter);
635	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &udi);
636
637	if (!dbus_connection_send (dbus_connection, message, NULL))
638		DIE (("error broadcasting message"));
639
640	dbus_message_unref (message);
641
642out:
643	;
644}
645
646/** Send signal DeviceRemoved(string udi) on the org.freedesktop.Hal.Manager
647 *  interface on the object /org/freedesktop/Hal/Manager.
648 *
649 *  @param  device              The HalDevice removed
650 */
651void
652manager_send_signal_device_removed (HalDevice *device)
653{
654	const char *udi = hal_device_get_udi (device);
655	DBusMessage *message;
656	DBusMessageIter iter;
657
658	if (dbus_connection == NULL)
659		goto out;
660
661	HAL_TRACE (("entering, udi=%s", udi));
662
663	message = dbus_message_new_signal ("/org/freedesktop/Hal/Manager",
664					   "org.freedesktop.Hal.Manager",
665					   "DeviceRemoved");
666
667	dbus_message_iter_init_append (message, &iter);
668	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &udi);
669
670	if (!dbus_connection_send (dbus_connection, message, NULL))
671		DIE (("error broadcasting message"));
672
673	dbus_message_unref (message);
674out:
675	;
676}
677
678/** Send signal NewCapability(string udi, string capability) on the
679 *  org.freedesktop.Hal.Manager interface on the object
680 *  /org/freedesktop/Hal/Manager.
681 *
682 *  @param  udi                 Unique Device Id
683 *  @param  capability          Capability
684 */
685void
686manager_send_signal_new_capability (HalDevice *device,
687				    const char *capability)
688{
689	const char *udi = hal_device_get_udi (device);
690	DBusMessage *message;
691	DBusMessageIter iter;
692
693	if (dbus_connection == NULL)
694		goto out;
695
696	HAL_TRACE (("entering, udi=%s, cap=%s", udi, capability));
697
698	message = dbus_message_new_signal ("/org/freedesktop/Hal/Manager",
699					   "org.freedesktop.Hal.Manager",
700					   "NewCapability");
701
702	dbus_message_iter_init_append (message, &iter);
703	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &udi);
704	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &capability);
705
706	if (!dbus_connection_send (dbus_connection, message, NULL))
707		DIE (("error broadcasting message"));
708
709	dbus_message_unref (message);
710out:
711	;
712}
713
714/** @} */
715
716/**
717 * @defgroup DeviceInterface D-BUS interface org.freedesktop.Hal.Device
718 * @ingroup HalDaemon
719 * @brief D-BUS interface for generic device operations
720 * @{
721 */
722
723static gboolean
724foreach_property_append (HalDevice *device, HalProperty *p,
725			 gpointer user_data)
726{
727	DBusMessageIter *iter;
728	DBusMessageIter iter_dict_entry;
729	const char *key;
730	int type;
731
732	iter = (DBusMessageIter *)user_data;
733
734	dbus_message_iter_open_container (iter,
735					  DBUS_TYPE_DICT_ENTRY,
736					  NULL,
737					  &iter_dict_entry);
738
739	key = hal_property_get_key (p);
740	type = hal_property_get_type (p);
741
742	dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &key);
743
744	switch (type) {
745	case HAL_PROPERTY_TYPE_STRING:
746	{
747		DBusMessageIter iter_var;
748		const char *v;
749
750		v = hal_property_get_string (p);
751
752		dbus_message_iter_open_container (&iter_dict_entry,
753						  DBUS_TYPE_VARIANT,
754						  DBUS_TYPE_STRING_AS_STRING,
755						  &iter_var);
756
757		dbus_message_iter_append_basic (&iter_var,
758						DBUS_TYPE_STRING,
759						&v);
760
761		dbus_message_iter_close_container (&iter_dict_entry,
762						   &iter_var);
763		break;
764	}
765	case HAL_PROPERTY_TYPE_INT32:
766	{
767		DBusMessageIter iter_var;
768		dbus_int32_t v;
769
770		v = hal_property_get_int (p);
771
772		dbus_message_iter_open_container (&iter_dict_entry,
773						  DBUS_TYPE_VARIANT,
774						  DBUS_TYPE_INT32_AS_STRING,
775						  &iter_var);
776
777		dbus_message_iter_append_basic (&iter_var,
778						DBUS_TYPE_INT32,
779						&v);
780
781		dbus_message_iter_close_container (&iter_dict_entry,
782						   &iter_var);
783		break;
784	}
785	case HAL_PROPERTY_TYPE_UINT64:
786	{
787		DBusMessageIter iter_var;
788		dbus_uint64_t v;
789
790		v = hal_property_get_uint64 (p);
791
792		dbus_message_iter_open_container (&iter_dict_entry,
793						  DBUS_TYPE_VARIANT,
794						  DBUS_TYPE_UINT64_AS_STRING,
795						  &iter_var);
796
797		dbus_message_iter_append_basic (&iter_var,
798						DBUS_TYPE_UINT64,
799						&v);
800
801		dbus_message_iter_close_container (&iter_dict_entry,
802						   &iter_var);
803		break;
804	}
805	case HAL_PROPERTY_TYPE_DOUBLE:
806	{
807		DBusMessageIter iter_var;
808		double v;
809
810		v = hal_property_get_double (p);
811
812		dbus_message_iter_open_container (&iter_dict_entry,
813						  DBUS_TYPE_VARIANT,
814						  DBUS_TYPE_DOUBLE_AS_STRING,
815						  &iter_var);
816
817		dbus_message_iter_append_basic (&iter_var,
818						DBUS_TYPE_DOUBLE,
819						&v);
820
821		dbus_message_iter_close_container (&iter_dict_entry,
822						   &iter_var);
823		break;
824	}
825	case HAL_PROPERTY_TYPE_BOOLEAN:
826	{
827		DBusMessageIter iter_var;
828		dbus_bool_t v;
829
830		v = hal_property_get_bool (p);
831
832		dbus_message_iter_open_container (&iter_dict_entry,
833						  DBUS_TYPE_VARIANT,
834						  DBUS_TYPE_BOOLEAN_AS_STRING,
835						  &iter_var);
836
837		dbus_message_iter_append_basic (&iter_var,
838						DBUS_TYPE_BOOLEAN,
839						&v);
840
841		dbus_message_iter_close_container (&iter_dict_entry,
842						   &iter_var);
843		break;
844	}
845	case HAL_PROPERTY_TYPE_STRLIST:
846	{
847		DBusMessageIter iter_var, iter_array;
848		GSList *iter;
849
850		dbus_message_iter_open_container (&iter_dict_entry,
851						  DBUS_TYPE_VARIANT,
852						  DBUS_TYPE_ARRAY_AS_STRING
853						  DBUS_TYPE_STRING_AS_STRING,
854						  &iter_var);
855
856		dbus_message_iter_open_container (&iter_var,
857						  DBUS_TYPE_ARRAY,
858						  DBUS_TYPE_STRING_AS_STRING,
859						  &iter_array);
860
861		for (iter = hal_property_get_strlist (p); iter != NULL; iter = iter->next) {
862
863			const char *v;
864			v = (const char *) iter->data;
865
866			dbus_message_iter_append_basic (&iter_array,
867							DBUS_TYPE_STRING,
868							&v);
869		}
870
871		dbus_message_iter_close_container (&iter_var,
872						   &iter_array);
873
874		dbus_message_iter_close_container (&iter_dict_entry,
875						   &iter_var);
876		break;
877	}
878
879	default:
880		HAL_WARNING (("Unknown property type 0x%04x", type));
881		break;
882	}
883
884	dbus_message_iter_close_container (iter, &iter_dict_entry);
885
886
887	return TRUE;
888}
889
890
891
892/** Get all properties on a device.
893 *
894 *  <pre>
895 *  map{string, any} Device.GetAllProperties()
896 *
897 *    raises org.freedesktop.Hal.NoSuchDevice
898 *  </pre>
899 *
900 *  @param  connection          D-BUS connection
901 *  @param  message             Message
902 *  @return                     What to do with the message
903 */
904DBusHandlerResult
905device_get_all_properties (DBusConnection * connection,
906			   DBusMessage * message)
907{
908	DBusMessage *reply;
909	DBusMessageIter iter;
910	DBusMessageIter iter_dict;
911	HalDevice *d;
912	const char *udi;
913
914	udi = dbus_message_get_path (message);
915
916	HAL_TRACE (("entering, udi=%s", udi));
917
918	d = hal_device_store_find (hald_get_gdl (), udi);
919	if (d == NULL)
920		d = hal_device_store_find (hald_get_tdl (), udi);
921
922	if (d == NULL) {
923		raise_no_such_device (connection, message, udi);
924		return DBUS_HANDLER_RESULT_HANDLED;
925	}
926
927	reply = dbus_message_new_method_return (message);
928	if (reply == NULL)
929		DIE (("No memory"));
930
931	dbus_message_iter_init_append (reply, &iter);
932
933	dbus_message_iter_open_container (&iter,
934					  DBUS_TYPE_ARRAY,
935					  DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
936					  DBUS_TYPE_STRING_AS_STRING
937					  DBUS_TYPE_VARIANT_AS_STRING
938					  DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
939					  &iter_dict);
940
941	hal_device_property_foreach (d,
942				     foreach_property_append,
943				     &iter_dict);
944
945	dbus_message_iter_close_container (&iter, &iter_dict);
946
947	if (!dbus_connection_send (connection, reply, NULL))
948		DIE (("No memory"));
949
950	dbus_message_unref (reply);
951
952	return DBUS_HANDLER_RESULT_HANDLED;
953}
954
955#ifdef sun
956#include <sys/stat.h>
957static dbus_bool_t
958user_at_console(unsigned long uid)
959{
960	struct stat st;
961
962	return ((stat("/dev/vt/console_user", &st) == 0) && (st.st_uid == uid));
963}
964#endif /* sun */
965
966static dbus_bool_t
967sender_has_privileges (DBusConnection *connection, DBusMessage *message)
968{
969	DBusError error;
970	unsigned long user_uid;
971	const char *user_base_svc;
972	dbus_bool_t ret;
973
974	ret = FALSE;
975
976	user_base_svc = dbus_message_get_sender (message);
977	if (user_base_svc == NULL) {
978		HAL_WARNING (("Cannot determine base service of caller"));
979		goto out;
980	}
981
982	HAL_DEBUG (("base_svc = %s", user_base_svc));
983
984	dbus_error_init (&error);
985	user_uid = dbus_bus_get_unix_user (connection, user_base_svc, &error);
986	if (user_uid == (unsigned long) -1 || dbus_error_is_set (&error)) {
987		HAL_WARNING (("Could not get uid for connection: %s %s", error.name, error.message));
988		dbus_error_free (&error);
989		goto out;
990	}
991
992	HAL_INFO (("uid for caller is %ld", user_uid));
993
994	if (user_uid != 0 && user_uid != geteuid()) {
995#ifdef sun
996		if (dbus_message_is_method_call (message,
997						"org.freedesktop.Hal.Device",
998						"Rescan")) {
999			if (user_at_console(user_uid)) {
1000				ret = TRUE;
1001				goto out;
1002			}
1003		}
1004#endif
1005		HAL_WARNING (("uid %d is not privileged", user_uid));
1006		goto out;
1007	}
1008
1009	ret = TRUE;
1010
1011out:
1012	return ret;
1013}
1014
1015
1016/** Set multiple properties on a device in an atomic fashion.
1017 *
1018 *  <pre>
1019 *  Device.GetAllProperties(map{string, any} properties)
1020 *
1021 *    raises org.freedesktop.Hal.NoSuchDevice
1022 *  </pre>
1023 *
1024 *  @param  connection          D-BUS connection
1025 *  @param  message             Message
1026 *  @return                     What to do with the message
1027 */
1028static DBusHandlerResult
1029device_set_multiple_properties (DBusConnection *connection, DBusMessage *message, dbus_bool_t local_interface)
1030{
1031	DBusMessage *reply;
1032	DBusMessageIter iter;
1033	DBusMessageIter dict_iter;
1034	HalDevice *d;
1035	const char *udi;
1036
1037	udi = dbus_message_get_path (message);
1038
1039	HAL_TRACE (("entering, udi=%s", udi));
1040
1041	d = hal_device_store_find (hald_get_gdl (), udi);
1042	if (d == NULL)
1043		d = hal_device_store_find (hald_get_tdl (), udi);
1044
1045	if (d == NULL) {
1046		raise_no_such_device (connection, message, udi);
1047		return DBUS_HANDLER_RESULT_HANDLED;
1048	}
1049
1050	if (!local_interface && !sender_has_privileges (connection, message)) {
1051		raise_permission_denied (connection, message, "SetProperty: not privileged");
1052		return DBUS_HANDLER_RESULT_HANDLED;
1053	}
1054
1055	dbus_message_iter_init (message, &iter);
1056
1057	if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY  &&
1058	    dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_DICT_ENTRY) {
1059		HAL_ERROR (("error, expecting an array of dict entries", __FILE__, __LINE__));
1060		raise_syntax (connection, message, udi);
1061		return DBUS_HANDLER_RESULT_HANDLED;
1062	}
1063
1064	dbus_message_iter_recurse (&iter, &dict_iter);
1065
1066	/* update atomically */
1067	device_property_atomic_update_begin ();
1068
1069	while (dbus_message_iter_get_arg_type (&dict_iter) == DBUS_TYPE_DICT_ENTRY)
1070	{
1071		DBusMessageIter dict_entry_iter, var_iter, array_iter;
1072		const char *key;
1073		int change_type;
1074		dbus_bool_t rc __unused;
1075
1076		dbus_message_iter_recurse (&dict_iter, &dict_entry_iter);
1077		dbus_message_iter_get_basic (&dict_entry_iter, &key);
1078
1079		dbus_message_iter_next (&dict_entry_iter);
1080		dbus_message_iter_recurse (&dict_entry_iter, &var_iter);
1081		change_type = dbus_message_iter_get_arg_type (&var_iter);
1082
1083		rc = FALSE;
1084
1085		switch (change_type) {
1086		case DBUS_TYPE_ARRAY:
1087			if (dbus_message_iter_get_element_type (&var_iter) != DBUS_TYPE_STRING) {
1088				/* TODO: error */
1089			}
1090			dbus_message_iter_recurse (&var_iter, &array_iter);
1091
1092			hal_device_property_strlist_clear (d, key);
1093
1094			while (dbus_message_iter_get_arg_type (&array_iter) == DBUS_TYPE_STRING) {
1095				const char *v;
1096				dbus_message_iter_get_basic (&array_iter, &v);
1097				HAL_INFO ((" strlist elem %s -> %s", key, v));
1098				rc = hal_device_property_strlist_append (d, key, v);
1099				dbus_message_iter_next (&array_iter);
1100			}
1101
1102			break;
1103		case DBUS_TYPE_STRING:
1104		{
1105			const char *v;
1106			dbus_message_iter_get_basic (&var_iter, &v);
1107			HAL_INFO (("%s -> %s", key, v));
1108			rc = hal_device_property_set_string (d, key, v);
1109			break;
1110		}
1111		case DBUS_TYPE_INT32:
1112		{
1113			dbus_int32_t v;
1114			dbus_message_iter_get_basic (&var_iter, &v);
1115			HAL_INFO (("%s -> %d", key, v));
1116			rc = hal_device_property_set_int (d, key, v);
1117			break;
1118		}
1119		case DBUS_TYPE_UINT64:
1120		{
1121			dbus_uint64_t v;
1122			dbus_message_iter_get_basic (&var_iter, &v);
1123			HAL_INFO (("%s -> %lld", key, v));
1124			rc = hal_device_property_set_uint64 (d, key, v);
1125			break;
1126		}
1127		case DBUS_TYPE_DOUBLE:
1128		{
1129			double v;
1130			dbus_message_iter_get_basic (&var_iter, &v);
1131			HAL_INFO (("%s -> %g", key, v));
1132			rc = hal_device_property_set_double (d, key, v);
1133			break;
1134		}
1135		case DBUS_TYPE_BOOLEAN:
1136		{
1137			gboolean v;
1138			dbus_message_iter_get_basic (&var_iter, &v);
1139			HAL_INFO (("%s -> %s", key, v ? "True" : "False"));
1140			rc = hal_device_property_set_bool (d, key, v);
1141			break;
1142		}
1143		default:
1144			/* TODO: error */
1145			break;
1146		}
1147
1148		/* TODO: error out on rc==FALSE? */
1149
1150		dbus_message_iter_next (&dict_iter);
1151	}
1152
1153	device_property_atomic_update_end ();
1154
1155
1156	reply = dbus_message_new_method_return (message);
1157	if (reply == NULL)
1158		DIE (("No memory"));
1159
1160	if (!dbus_connection_send (connection, reply, NULL))
1161		DIE (("No memory"));
1162
1163	dbus_message_unref (reply);
1164	return DBUS_HANDLER_RESULT_HANDLED;
1165}
1166
1167
1168/** Get a property on a device.
1169 *
1170 *  <pre>
1171 *  any Device.GetProperty(string key)
1172 *  string Device.GetPropertyString(string key)
1173 *  int Device.GetPropertyInteger(string key)
1174 *  bool Device.GetPropertyBoolean(string key)
1175 *  double Device.GetPropertyDouble(string key)
1176 *
1177 *    raises org.freedesktop.Hal.NoSuchDevice,
1178 *           org.freedesktop.Hal.NoSuchProperty
1179 *  </pre>
1180 *
1181 *  @param  connection          D-BUS connection
1182 *  @param  message             Message
1183 *  @return                     What to do with the message
1184 */
1185DBusHandlerResult
1186device_get_property (DBusConnection * connection, DBusMessage * message)
1187{
1188	DBusMessage *reply;
1189	DBusMessageIter iter;
1190	DBusError error;
1191	HalDevice *d;
1192	const char *udi;
1193	char *key;
1194	int type;
1195	HalProperty *p;
1196
1197	udi = dbus_message_get_path (message);
1198
1199	HAL_TRACE (("entering, udi=%s", udi));
1200
1201	d = hal_device_store_find (hald_get_gdl (), udi);
1202	if (d == NULL)
1203		d = hal_device_store_find (hald_get_tdl (), udi);
1204
1205	if (d == NULL) {
1206		raise_no_such_device (connection, message, udi);
1207		return DBUS_HANDLER_RESULT_HANDLED;
1208	}
1209
1210	dbus_error_init (&error);
1211	if (!dbus_message_get_args (message, &error,
1212				    DBUS_TYPE_STRING, &key,
1213				    DBUS_TYPE_INVALID)) {
1214		raise_syntax (connection, message, "GetProperty");
1215		return DBUS_HANDLER_RESULT_HANDLED;
1216	}
1217
1218	p = hal_device_property_find (d, key);
1219	if (p == NULL) {
1220		raise_no_such_property (connection, message, udi, key);
1221		return DBUS_HANDLER_RESULT_HANDLED;
1222	}
1223
1224	reply = dbus_message_new_method_return (message);
1225	if (reply == NULL)
1226		DIE (("No memory"));
1227
1228	dbus_message_iter_init_append (reply, &iter);
1229
1230	type = hal_property_get_type (p);
1231	switch (type) {
1232	case HAL_PROPERTY_TYPE_STRING:
1233	{
1234		const char *s;
1235		s = hal_property_get_string (p);
1236		dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &s);
1237		break;
1238	}
1239	case HAL_PROPERTY_TYPE_INT32:
1240	{
1241		dbus_int32_t i;
1242		i = hal_property_get_int (p);
1243		dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &i);
1244		break;
1245	}
1246	case HAL_PROPERTY_TYPE_UINT64:
1247	{
1248		dbus_uint64_t ul;
1249		ul = hal_property_get_uint64 (p);
1250		dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT64, &ul);
1251		break;
1252	}
1253	case HAL_PROPERTY_TYPE_DOUBLE:
1254	{
1255		double d;
1256		d = hal_property_get_double (p);
1257		dbus_message_iter_append_basic (&iter, DBUS_TYPE_DOUBLE, &d);
1258		break;
1259	}
1260	case HAL_PROPERTY_TYPE_BOOLEAN:
1261	{
1262		dbus_bool_t b;
1263		b = hal_property_get_bool (p);
1264		dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &b);
1265		break;
1266	}
1267	case HAL_PROPERTY_TYPE_STRLIST:
1268	{
1269		GSList *l;
1270		DBusMessageIter iter_array;
1271
1272		dbus_message_iter_open_container (&iter,
1273						  DBUS_TYPE_ARRAY,
1274						  DBUS_TYPE_STRING_AS_STRING,
1275						  &iter_array);
1276
1277		for (l = hal_property_get_strlist (p); l != NULL; l = g_slist_next (l)) {
1278			dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_STRING, &(l->data));
1279		}
1280
1281		dbus_message_iter_close_container (&iter, &iter_array);
1282	}
1283		break;
1284
1285	default:
1286		HAL_WARNING (("Unknown property type %d", type));
1287		break;
1288	}
1289
1290	if (!dbus_connection_send (connection, reply, NULL))
1291		DIE (("No memory"));
1292
1293	dbus_message_unref (reply);
1294
1295	return DBUS_HANDLER_RESULT_HANDLED;
1296}
1297
1298
1299/** Get the type of a property on a device.
1300 *
1301 *  <pre>
1302 *  int Device.GetPropertyType(string key)
1303 *
1304 *    raises org.freedesktop.Hal.NoSuchDevice,
1305 *           org.freedesktop.Hal.NoSuchProperty
1306 *  </pre>
1307 *
1308 *  @param  connection          D-BUS connection
1309 *  @param  message             Message
1310 *  @return                     What to do with the message
1311 */
1312DBusHandlerResult
1313device_get_property_type (DBusConnection * connection,
1314			  DBusMessage * message)
1315{
1316	DBusMessage *reply;
1317	DBusMessageIter iter;
1318	DBusError error;
1319	HalDevice *d;
1320	const char *udi;
1321	char *key;
1322	HalProperty *p;
1323	dbus_int32_t i;
1324
1325	udi = dbus_message_get_path (message);
1326
1327	HAL_TRACE (("entering, udi=%s", udi));
1328
1329	d = hal_device_store_find (hald_get_gdl (), udi);
1330	if (d == NULL)
1331		d = hal_device_store_find (hald_get_tdl (), udi);
1332
1333	if (d == NULL) {
1334		raise_no_such_device (connection, message, udi);
1335		return DBUS_HANDLER_RESULT_HANDLED;
1336	}
1337
1338	dbus_error_init (&error);
1339	if (!dbus_message_get_args (message, &error,
1340				    DBUS_TYPE_STRING, &key,
1341				    DBUS_TYPE_INVALID)) {
1342		raise_syntax (connection, message, "GetPropertyType");
1343		return DBUS_HANDLER_RESULT_HANDLED;
1344	}
1345
1346	p = hal_device_property_find (d, key);
1347	if (p == NULL) {
1348		raise_no_such_property (connection, message, udi, key);
1349		return DBUS_HANDLER_RESULT_HANDLED;
1350	}
1351
1352	reply = dbus_message_new_method_return (message);
1353	if (reply == NULL)
1354		DIE (("No memory"));
1355
1356	i = hal_property_get_type (p);
1357	dbus_message_iter_init_append (reply, &iter);
1358	dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &i);
1359
1360	if (!dbus_connection_send (connection, reply, NULL))
1361		DIE (("No memory"));
1362
1363	dbus_message_unref (reply);
1364
1365	return DBUS_HANDLER_RESULT_HANDLED;
1366}
1367
1368/** Set a property on a device.
1369 *
1370 *  <pre>
1371 *  void Device.SetProperty(string key, any value)
1372 *  void Device.SetPropertyString(string key, string value)
1373 *  void Device.SetPropertyInteger(string key, int value)
1374 *  void Device.SetPropertyBoolean(string key, bool value)
1375 *  void Device.SetPropertyDouble(string key, double value)
1376 *
1377 *    raises org.freedesktop.Hal.NoSuchDevice,
1378 *           org.freedesktop.Hal.NoSuchProperty
1379 *           org.freedesktop.Hal.TypeMismatch
1380 *  </pre>
1381 *
1382 *  @param  connection          D-BUS connection
1383 *  @param  message             Message
1384 *  @return                     What to do with the message
1385 */
1386DBusHandlerResult
1387device_set_property (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
1388{
1389	const char *udi;
1390	char *key;
1391	int type;
1392	dbus_bool_t rc;
1393	HalDevice *device;
1394	DBusMessageIter iter;
1395	DBusMessage *reply;
1396
1397	HAL_TRACE (("entering"));
1398
1399	udi = dbus_message_get_path (message);
1400
1401	dbus_message_iter_init (message, &iter);
1402	type = dbus_message_iter_get_arg_type (&iter);
1403	if (type != DBUS_TYPE_STRING) {
1404		raise_syntax (connection, message, "SetProperty");
1405		return DBUS_HANDLER_RESULT_HANDLED;
1406	}
1407	dbus_message_iter_get_basic (&iter, &key);
1408
1409	if (!local_interface && !sender_has_privileges (connection, message)) {
1410		raise_permission_denied (connection, message, "SetProperty: not privileged");
1411		return DBUS_HANDLER_RESULT_HANDLED;
1412	}
1413
1414	HAL_DEBUG (("udi=%s, key=%s", udi, key));
1415
1416	device = hal_device_store_find (hald_get_gdl (), udi);
1417	if (device == NULL)
1418		device = hal_device_store_find (hald_get_tdl (), udi);
1419
1420	if (device == NULL) {
1421		raise_no_such_device (connection, message, udi);
1422		return DBUS_HANDLER_RESULT_HANDLED;
1423	}
1424	dbus_message_iter_next (&iter);
1425
1426	/** @todo check permissions of the sender vs property to be modified */
1427
1428	type = dbus_message_iter_get_arg_type (&iter);
1429	rc = FALSE;
1430
1431	switch (type) {
1432	case DBUS_TYPE_STRING:
1433	{
1434		const char *v;
1435		dbus_message_iter_get_basic (&iter, &v);
1436		rc = hal_device_property_set_string (device, key, v);
1437		break;
1438	}
1439	case DBUS_TYPE_INT32:
1440	{
1441		dbus_int32_t v;
1442		dbus_message_iter_get_basic (&iter, &v);
1443		rc = hal_device_property_set_int (device, key, v);
1444		break;
1445	}
1446	case DBUS_TYPE_UINT64:
1447	{
1448		dbus_uint64_t v;
1449		dbus_message_iter_get_basic (&iter, &v);
1450		rc = hal_device_property_set_uint64 (device, key, v);
1451		break;
1452	}
1453	case DBUS_TYPE_DOUBLE:
1454	{
1455		double v;
1456		dbus_message_iter_get_basic (&iter, &v);
1457		rc = hal_device_property_set_double (device, key, v);
1458		break;
1459	}
1460	case DBUS_TYPE_BOOLEAN:
1461	{
1462		dbus_bool_t v;
1463		dbus_message_iter_get_basic (&iter, &v);
1464		rc = hal_device_property_set_bool (device, key, v);
1465		break;
1466	}
1467	default:
1468		HAL_WARNING (("Unsupported property type %d", type));
1469		break;
1470	}
1471
1472	if (!rc) {
1473		raise_property_type_error (connection, message, udi, key);
1474		return DBUS_HANDLER_RESULT_HANDLED;
1475	}
1476
1477	reply = dbus_message_new_method_return (message);
1478	if (reply == NULL)
1479		DIE (("No memory"));
1480
1481	if (!dbus_connection_send (connection, reply, NULL))
1482		DIE (("No memory"));
1483
1484	dbus_message_unref (reply);
1485	return DBUS_HANDLER_RESULT_HANDLED;
1486}
1487
1488/** This function is used to modify the Capabilities property. The reason
1489 *  for having a dedicated function is that the HAL daemon will broadcast
1490 *  a signal on the Manager interface to tell applications that the device
1491 *  have got a new capability.
1492 *
1493 *  This is useful as capabilities can be merged after the device is created.
1494 *  One example of this is networking cards under Linux 2.6; the net.ethernet
1495 *  capability is not merged when the device is initially found by looking in
1496 *  /sys/devices; it is merged when the /sys/classes tree is searched.
1497 *
1498 *  Note that the signal is emitted every time this method is invoked even
1499 *  though the capability already existed. This is useful in the above
1500 *  scenario when the PCI class says ethernet networking card but we yet
1501 *  don't have enough information to fill in the net.* and net.ethernet.*
1502 *  fields since this only happens when we visit the /sys/classes tree.
1503 *
1504 *  <pre>
1505 *  void Device.AddCapability(string capability)
1506 *
1507 *    raises org.freedesktop.Hal.NoSuchDevice,
1508 *    raises org.freedesktop.Hal.PermissionDenied,
1509 *  </pre>
1510 *
1511 *  @param  connection          D-BUS connection
1512 *  @param  message             Message
1513 *  @return                     What to do with the message
1514 */
1515DBusHandlerResult
1516device_add_capability (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
1517{
1518	const char *udi;
1519	const char *capability;
1520	HalDevice *d;
1521	DBusMessage *reply;
1522	DBusError error;
1523
1524	HAL_TRACE (("entering"));
1525
1526	if (!local_interface && !sender_has_privileges (connection, message)) {
1527		raise_permission_denied (connection, message, "AddCapability: not privileged");
1528		return DBUS_HANDLER_RESULT_HANDLED;
1529	}
1530
1531	udi = dbus_message_get_path (message);
1532
1533	d = hal_device_store_find (hald_get_gdl (), udi);
1534	if (d == NULL)
1535		d = hal_device_store_find (hald_get_tdl (), udi);
1536
1537	if (d == NULL) {
1538		raise_no_such_device (connection, message, udi);
1539		return DBUS_HANDLER_RESULT_HANDLED;
1540	}
1541
1542	dbus_error_init (&error);
1543	if (!dbus_message_get_args (message, &error,
1544				    DBUS_TYPE_STRING, &capability,
1545				    DBUS_TYPE_INVALID)) {
1546		raise_syntax (connection, message, "AddCapability");
1547		return DBUS_HANDLER_RESULT_HANDLED;
1548	}
1549
1550
1551	hal_device_add_capability (d, capability);
1552
1553	reply = dbus_message_new_method_return (message);
1554	if (reply == NULL)
1555		DIE (("No memory"));
1556
1557	if (!dbus_connection_send (connection, reply, NULL))
1558		DIE (("No memory"));
1559
1560	dbus_message_unref (reply);
1561	return DBUS_HANDLER_RESULT_HANDLED;
1562}
1563
1564
1565/* TODO: docs */
1566static DBusHandlerResult
1567device_string_list_append_prepend (DBusConnection * connection, DBusMessage * message, dbus_bool_t do_prepend)
1568{
1569	const char *udi;
1570	const char *key;
1571	const char *value;
1572	HalDevice *d;
1573	DBusMessage *reply;
1574	DBusError error;
1575	gboolean ret;
1576
1577	HAL_TRACE (("entering"));
1578
1579	udi = dbus_message_get_path (message);
1580
1581	d = hal_device_store_find (hald_get_gdl (), udi);
1582	if (d == NULL)
1583		d = hal_device_store_find (hald_get_tdl (), udi);
1584
1585	if (d == NULL) {
1586		raise_no_such_device (connection, message, udi);
1587		return DBUS_HANDLER_RESULT_HANDLED;
1588	}
1589
1590	dbus_error_init (&error);
1591	if (!dbus_message_get_args (message, &error,
1592				    DBUS_TYPE_STRING, &key,
1593				    DBUS_TYPE_STRING, &value,
1594				    DBUS_TYPE_INVALID)) {
1595		raise_syntax (connection, message, do_prepend ? "StringListPrepend" : "StringListAppend");
1596		return DBUS_HANDLER_RESULT_HANDLED;
1597	}
1598
1599	if (do_prepend)
1600		ret = hal_device_property_strlist_prepend (d, key, value);
1601	else
1602		ret = hal_device_property_strlist_append (d, key, value);
1603	if (!ret) {
1604		raise_property_type_error (connection, message, udi, key);
1605		return DBUS_HANDLER_RESULT_HANDLED;
1606	}
1607
1608	reply = dbus_message_new_method_return (message);
1609	if (reply == NULL)
1610		DIE (("No memory"));
1611
1612	if (!dbus_connection_send (connection, reply, NULL))
1613		DIE (("No memory"));
1614
1615	dbus_message_unref (reply);
1616	return DBUS_HANDLER_RESULT_HANDLED;
1617}
1618
1619/* TODO: docs */
1620static DBusHandlerResult
1621device_string_list_remove (DBusConnection * connection, DBusMessage * message)
1622{
1623	const char *udi;
1624	const char *key;
1625	const char *value;
1626	HalDevice *d;
1627	DBusMessage *reply;
1628	DBusError error;
1629	gboolean ret;
1630
1631	HAL_TRACE (("entering"));
1632
1633	udi = dbus_message_get_path (message);
1634
1635	d = hal_device_store_find (hald_get_gdl (), udi);
1636	if (d == NULL)
1637		d = hal_device_store_find (hald_get_tdl (), udi);
1638
1639	if (d == NULL) {
1640		raise_no_such_device (connection, message, udi);
1641		return DBUS_HANDLER_RESULT_HANDLED;
1642	}
1643
1644	dbus_error_init (&error);
1645	if (!dbus_message_get_args (message, &error,
1646				    DBUS_TYPE_STRING, &key,
1647				    DBUS_TYPE_STRING, &value,
1648				    DBUS_TYPE_INVALID)) {
1649		raise_syntax (connection, message, "StringListRemove");
1650		return DBUS_HANDLER_RESULT_HANDLED;
1651	}
1652
1653	ret = hal_device_property_strlist_remove (d, key, value);
1654	if (!ret) {
1655		raise_property_type_error (connection, message, udi, key);
1656		return DBUS_HANDLER_RESULT_HANDLED;
1657	}
1658
1659	reply = dbus_message_new_method_return (message);
1660	if (reply == NULL)
1661		DIE (("No memory"));
1662
1663	if (!dbus_connection_send (connection, reply, NULL))
1664		DIE (("No memory"));
1665
1666	dbus_message_unref (reply);
1667	return DBUS_HANDLER_RESULT_HANDLED;
1668}
1669
1670
1671
1672/** Remove a property on a device.
1673 *
1674 *  <pre>
1675 *  void Device.RemoveProperty(string key)
1676 *
1677 *    raises org.freedesktop.Hal.NoSuchDevice,
1678 *           org.freedesktop.Hal.NoSuchProperty
1679 *           org.freedesktop.Hal.PermissionDenied
1680 *  </pre>
1681 *
1682 *  @param  connection          D-BUS connection
1683 *  @param  message             Message
1684 *  @return                     What to do with the message
1685 */
1686DBusHandlerResult
1687device_remove_property (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
1688{
1689	const char *udi;
1690	char *key;
1691	HalDevice *d;
1692	DBusMessage *reply;
1693	DBusError error;
1694
1695	HAL_TRACE (("entering"));
1696
1697	udi = dbus_message_get_path (message);
1698
1699	if (!local_interface && !sender_has_privileges (connection, message)) {
1700		raise_permission_denied (connection, message, "RemoveProperty: not privileged");
1701		return DBUS_HANDLER_RESULT_HANDLED;
1702	}
1703
1704	d = hal_device_store_find (hald_get_gdl (), udi);
1705	if (d == NULL)
1706		d = hal_device_store_find (hald_get_tdl (), udi);
1707
1708	if (d == NULL) {
1709		raise_no_such_device (connection, message, udi);
1710		return DBUS_HANDLER_RESULT_HANDLED;
1711	}
1712
1713	dbus_error_init (&error);
1714	if (!dbus_message_get_args (message, &error,
1715				    DBUS_TYPE_STRING, &key,
1716				    DBUS_TYPE_INVALID)) {
1717		raise_syntax (connection, message, "RemoveProperty");
1718		return DBUS_HANDLER_RESULT_HANDLED;
1719	}
1720
1721	if (!hal_device_property_remove (d, key)) {
1722		raise_no_such_property (connection, message, udi, key);
1723		return DBUS_HANDLER_RESULT_HANDLED;
1724	}
1725
1726
1727	reply = dbus_message_new_method_return (message);
1728	if (reply == NULL)
1729		DIE (("No memory"));
1730
1731	if (!dbus_connection_send (connection, reply, NULL))
1732		DIE (("No memory"));
1733
1734	dbus_message_unref (reply);
1735	return DBUS_HANDLER_RESULT_HANDLED;
1736}
1737
1738
1739/** Determine if a property exists
1740 *
1741 *  <pre>
1742 *  bool Device.PropertyExists(string key)
1743 *
1744 *    raises org.freedesktop.Hal.NoSuchDevice,
1745 *  </pre>
1746 *
1747 *  @param  connection          D-BUS connection
1748 *  @param  message             Message
1749 *  @return                     What to do with the message
1750 */
1751DBusHandlerResult
1752device_property_exists (DBusConnection * connection, DBusMessage * message)
1753{
1754	const char *udi;
1755	char *key;
1756	HalDevice *d;
1757	DBusMessage *reply;
1758	DBusError error;
1759	DBusMessageIter iter;
1760	dbus_bool_t b;
1761
1762	HAL_TRACE (("entering"));
1763
1764	udi = dbus_message_get_path (message);
1765
1766	d = hal_device_store_find (hald_get_gdl (), udi);
1767	if (d == NULL)
1768		d = hal_device_store_find (hald_get_tdl (), udi);
1769
1770	if (d == NULL) {
1771		raise_no_such_device (connection, message, udi);
1772		return DBUS_HANDLER_RESULT_HANDLED;
1773	}
1774
1775	dbus_error_init (&error);
1776	if (!dbus_message_get_args (message, &error,
1777				    DBUS_TYPE_STRING, &key,
1778				    DBUS_TYPE_INVALID)) {
1779		raise_syntax (connection, message, "RemoveProperty");
1780		return DBUS_HANDLER_RESULT_HANDLED;
1781	}
1782
1783	reply = dbus_message_new_method_return (message);
1784	if (reply == NULL)
1785		DIE (("No memory"));
1786
1787	b =  hal_device_has_property (d, key);
1788	dbus_message_iter_init_append (reply, &iter);
1789	dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &b);
1790
1791	if (!dbus_connection_send (connection, reply, NULL))
1792		DIE (("No memory"));
1793
1794	dbus_message_unref (reply);
1795	return DBUS_HANDLER_RESULT_HANDLED;
1796}
1797
1798
1799/** Determine if a device has a capability
1800 *
1801 *  <pre>
1802 *  bool Device.QueryCapability(string capability_name)
1803 *
1804 *    raises org.freedesktop.Hal.NoSuchDevice,
1805 *  </pre>
1806 *
1807 *  @param  connection          D-BUS connection
1808 *  @param  message             Message
1809 *  @return                     What to do with the message
1810 */
1811DBusHandlerResult
1812device_query_capability (DBusConnection * connection,
1813			 DBusMessage * message)
1814{
1815	dbus_bool_t rc;
1816	const char *udi;
1817	GSList *caps;
1818	char *capability;
1819	HalDevice *d;
1820	DBusMessage *reply;
1821	DBusError error;
1822	DBusMessageIter iter;
1823
1824	HAL_TRACE (("entering"));
1825
1826	udi = dbus_message_get_path (message);
1827
1828	d = hal_device_store_find (hald_get_gdl (), udi);
1829	if (d == NULL)
1830		d = hal_device_store_find (hald_get_tdl (), udi);
1831
1832	if (d == NULL) {
1833		raise_no_such_device (connection, message, udi);
1834		return DBUS_HANDLER_RESULT_HANDLED;
1835	}
1836
1837	dbus_error_init (&error);
1838	if (!dbus_message_get_args (message, &error,
1839				    DBUS_TYPE_STRING, &capability,
1840				    DBUS_TYPE_INVALID)) {
1841		raise_syntax (connection, message, "QueryCapability");
1842		return DBUS_HANDLER_RESULT_HANDLED;
1843	}
1844
1845	reply = dbus_message_new_method_return (message);
1846	if (reply == NULL)
1847		DIE (("No memory"));
1848
1849	rc = FALSE;
1850	caps = hal_device_property_get_strlist (d, "info.capabilities");
1851	if (caps != NULL) {
1852		GSList *iter;
1853
1854		for (iter = caps; iter != NULL; iter=g_slist_next(iter)) {
1855			if (strcmp (iter->data, capability) == 0) {
1856				rc = TRUE;
1857				break;
1858			}
1859		}
1860	}
1861
1862	dbus_message_iter_init_append (reply, &iter);
1863	dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &rc);
1864
1865	if (!dbus_connection_send (connection, reply, NULL))
1866		DIE (("No memory"));
1867
1868	dbus_message_unref (reply);
1869	return DBUS_HANDLER_RESULT_HANDLED;
1870}
1871
1872static GHashTable *services_with_locks = NULL;
1873
1874/** Grab an advisory lock on a device.
1875 *
1876 *  <pre>
1877 *  bool Device.Lock(string reason)
1878 *
1879 *    raises org.freedesktop.Hal.NoSuchDevice,
1880 *           org.freedesktop.Hal.DeviceAlreadyLocked
1881 *  </pre>
1882 *
1883 *  @param  connection          D-BUS connection
1884 *  @param  message             Message
1885 *  @return                     What to do with the message
1886 */
1887DBusHandlerResult
1888device_lock (DBusConnection * connection,
1889	     DBusMessage * message)
1890{
1891	const char *udi;
1892	HalDevice *d;
1893	DBusMessage *reply;
1894	dbus_bool_t already_locked;
1895	DBusError error;
1896	char *reason;
1897	const char *sender;
1898
1899	HAL_TRACE (("entering"));
1900
1901	udi = dbus_message_get_path (message);
1902
1903	d = hal_device_store_find (hald_get_gdl (), udi);
1904	if (d == NULL)
1905		d = hal_device_store_find (hald_get_tdl (), udi);
1906
1907	if (d == NULL) {
1908		raise_no_such_device (connection, message, udi);
1909		return DBUS_HANDLER_RESULT_HANDLED;
1910	}
1911
1912	already_locked = hal_device_property_get_bool (d, "info.locked");
1913
1914	if (already_locked) {
1915		raise_device_already_locked (connection, message, d);
1916		return DBUS_HANDLER_RESULT_HANDLED;
1917	}
1918
1919	dbus_error_init (&error);
1920	if (!dbus_message_get_args (message, &error,
1921				    DBUS_TYPE_STRING, &reason,
1922				    DBUS_TYPE_INVALID)) {
1923		raise_syntax (connection, message, "Lock");
1924		return DBUS_HANDLER_RESULT_HANDLED;
1925	}
1926
1927	reply = dbus_message_new_method_return (message);
1928	if (reply == NULL)
1929		DIE (("No memory"));
1930
1931	sender = dbus_message_get_sender (message);
1932
1933	hal_device_property_set_bool (d, "info.locked", TRUE);
1934	hal_device_property_set_string (d, "info.locked.reason", reason);
1935	hal_device_property_set_string (d, "info.locked.dbus_name",
1936					sender);
1937
1938	if (services_with_locks == NULL) {
1939		services_with_locks =
1940			g_hash_table_new_full (g_str_hash,
1941					       g_str_equal,
1942					       g_free,
1943					       g_object_unref);
1944	}
1945
1946	g_hash_table_insert (services_with_locks, g_strdup (sender),
1947			     g_object_ref (d));
1948
1949	if (!dbus_connection_send (connection, reply, NULL))
1950		DIE (("No memory"));
1951
1952	dbus_message_unref (reply);
1953	return DBUS_HANDLER_RESULT_HANDLED;
1954}
1955
1956/** Release an advisory lock on a device.
1957 *
1958 *  <pre>
1959 *  bool Device.Unlock()
1960 *
1961 *    raises org.freedesktop.Hal.NoSuchDevice,
1962 *           org.freedesktop.Hal.DeviceNotLocked,
1963 *           org.freedesktop.Hal.PermissionDenied
1964 *  </pre>
1965 *
1966 *  @param  connection          D-BUS connection
1967 *  @param  message             Message
1968 *  @return                     What to do with the message
1969 */
1970DBusHandlerResult
1971device_unlock (DBusConnection * connection,
1972	       DBusMessage * message)
1973{
1974	dbus_bool_t rc;
1975	const char *udi;
1976	HalDevice *d;
1977	DBusMessage *reply;
1978	DBusError error;
1979	const char *sender;
1980
1981	HAL_TRACE (("entering"));
1982
1983	udi = dbus_message_get_path (message);
1984
1985	d = hal_device_store_find (hald_get_gdl (), udi);
1986	if (d == NULL)
1987		d = hal_device_store_find (hald_get_tdl (), udi);
1988
1989	if (d == NULL) {
1990		raise_no_such_device (connection, message, udi);
1991		return DBUS_HANDLER_RESULT_HANDLED;
1992	}
1993
1994	dbus_error_init (&error);
1995	if (!dbus_message_get_args (message, &error,
1996				    DBUS_TYPE_INVALID)) {
1997		raise_syntax (connection, message, "Unlock");
1998		return DBUS_HANDLER_RESULT_HANDLED;
1999	}
2000
2001	reply = dbus_message_new_method_return (message);
2002	if (reply == NULL)
2003		DIE (("No memory"));
2004
2005	rc = hal_device_property_get_bool (d, "info.locked");
2006
2007	if (!rc) {
2008		raise_device_not_locked (connection, message, d);
2009		return DBUS_HANDLER_RESULT_HANDLED;
2010	}
2011
2012	sender = dbus_message_get_sender (message);
2013
2014	if (strcmp (sender, hal_device_property_get_string (
2015			    d, "info.locked.dbus_name")) != 0) {
2016		char *reason;
2017
2018		reason = g_strdup_printf ("Service '%s' does not own the "
2019					  "lock on %s", sender,
2020					  hal_device_get_udi (d));
2021
2022		raise_permission_denied (connection, message, reason);
2023
2024		g_free (reason);
2025
2026		return DBUS_HANDLER_RESULT_HANDLED;
2027	}
2028
2029	if (g_hash_table_lookup (services_with_locks, sender))
2030		g_hash_table_remove (services_with_locks, sender);
2031	else {
2032		HAL_WARNING (("Service '%s' was not in the list of services "
2033			      "with locks!", sender));
2034	}
2035
2036	hal_device_property_remove (d, "info.locked");
2037	hal_device_property_remove (d, "info.locked.reason");
2038	hal_device_property_remove (d, "info.locked.dbus_name");
2039
2040	/* FIXME?  Pointless? */
2041	if (!dbus_connection_send (connection, reply, NULL))
2042		DIE (("No memory"));
2043
2044	dbus_message_unref (reply);
2045	return DBUS_HANDLER_RESULT_HANDLED;
2046}
2047
2048static GHashTable *services_with_claims = NULL;
2049
2050/** Claim a branch.
2051 *
2052 *  <pre>
2053 *  bool Manager.ClaimBranch(string udi, string claim_service)
2054 *  </pre>
2055 *
2056 *  @param  connection          D-BUS connection
2057 *  @param  message             Message
2058 *  @return                     What to do with the message
2059 */
2060DBusHandlerResult
2061manager_claim_branch (DBusConnection * connection, DBusMessage * message)
2062{
2063	DBusMessage *reply;
2064	DBusMessageIter iter;
2065	DBusError error;
2066	HalDevice *d;
2067	const char *udi;
2068	const char *claim_service;
2069	const char *sender;
2070	dbus_bool_t already_claimed;
2071	unsigned long uid;
2072
2073	dbus_error_init (&error);
2074	if (!dbus_message_get_args (message, &error,
2075				    DBUS_TYPE_STRING, &udi,
2076				    DBUS_TYPE_STRING, &claim_service,
2077				    DBUS_TYPE_INVALID)) {
2078		raise_syntax (connection, message, "Manager.ClaimBranch");
2079		return DBUS_HANDLER_RESULT_HANDLED;
2080	}
2081
2082	HAL_TRACE (("entering, udi=%s", udi));
2083
2084	d = hal_device_store_find (hald_get_gdl (), udi);
2085
2086	if (d == NULL) {
2087		raise_no_such_device (connection, message, udi);
2088		return DBUS_HANDLER_RESULT_HANDLED;
2089	}
2090
2091	already_claimed = hal_device_property_get_bool (d, "info.claimed");
2092	if (already_claimed) {
2093		raise_branch_already_claimed (connection, message, d);
2094		return DBUS_HANDLER_RESULT_HANDLED;
2095	}
2096
2097	reply = dbus_message_new_method_return (message);
2098	if (reply == NULL)
2099		DIE (("No memory"));
2100
2101	sender = dbus_message_get_sender (message);
2102
2103	dbus_error_init (&error);
2104	uid = dbus_bus_get_unix_user (connection, sender, &error);
2105	if (uid == (unsigned long) -1 || dbus_error_is_set (&error)) {
2106		HAL_WARNING (("Could not get uid for connection: %s %s", error.name, error.message));
2107		dbus_error_free (&error);
2108		dbus_message_unref (reply);
2109		raise_no_such_device (connection, message, udi);
2110		return DBUS_HANDLER_RESULT_HANDLED;
2111	}
2112
2113	hal_util_branch_claim (hald_get_gdl (), d, TRUE, claim_service, uid);
2114	hal_device_property_set_string (d, "info.claimed.dbus_name", sender);
2115
2116	if (services_with_claims == NULL) {
2117		services_with_claims =
2118			g_hash_table_new_full (g_str_hash,
2119					       g_str_equal,
2120					       g_free,
2121					       g_object_unref);
2122	}
2123
2124	g_hash_table_insert (services_with_claims, g_strdup (sender),
2125			     g_object_ref (d));
2126
2127	if (!dbus_connection_send (connection, reply, NULL))
2128		DIE (("No memory"));
2129
2130	dbus_message_unref (reply);
2131	return DBUS_HANDLER_RESULT_HANDLED;
2132}
2133
2134/** Unclaim a branch.
2135 *
2136 *  <pre>
2137 *  bool Manager.UnclaimBranch(string udi)
2138 *  </pre>
2139 *
2140 *  @param  connection          D-BUS connection
2141 *  @param  message             Message
2142 *  @return                     What to do with the message
2143 */
2144DBusHandlerResult
2145manager_unclaim_branch (DBusConnection * connection, DBusMessage * message)
2146{
2147	DBusMessage *reply;
2148	DBusMessageIter iter;
2149	DBusError error;
2150	HalDevice *d;
2151	const char *udi;
2152	const char *claim_service;
2153	const char *sender;
2154	dbus_bool_t already_claimed;
2155
2156	dbus_error_init (&error);
2157	if (!dbus_message_get_args (message, &error,
2158				    DBUS_TYPE_STRING, &udi,
2159				    DBUS_TYPE_INVALID)) {
2160		raise_syntax (connection, message, "Manager.UnclaimBranch");
2161		return DBUS_HANDLER_RESULT_HANDLED;
2162	}
2163
2164	HAL_TRACE (("entering, udi=%s", udi));
2165
2166	d = hal_device_store_find (hald_get_gdl (), udi);
2167
2168	if (d == NULL) {
2169		raise_no_such_device (connection, message, udi);
2170		return DBUS_HANDLER_RESULT_HANDLED;
2171	}
2172
2173	already_claimed = hal_device_property_get_bool (d, "info.claimed");
2174	if (!already_claimed) {
2175		raise_branch_not_claimed (connection, message, d);
2176		return DBUS_HANDLER_RESULT_HANDLED;
2177	}
2178
2179	reply = dbus_message_new_method_return (message);
2180	if (reply == NULL)
2181		DIE (("No memory"));
2182
2183	if (strcmp (sender, hal_device_property_get_string (
2184			    d, "info.claimed.dbus_name")) != 0) {
2185		char *reason;
2186
2187		reason = g_strdup_printf ("Service '%s' does not own the "
2188					  "claim on %s", sender,
2189					  hal_device_get_udi (d));
2190
2191		raise_permission_denied (connection, message, reason);
2192
2193		g_free (reason);
2194
2195		return DBUS_HANDLER_RESULT_HANDLED;
2196	}
2197
2198	if (g_hash_table_lookup (services_with_claims, sender))
2199		g_hash_table_remove (services_with_claims, sender);
2200	else {
2201		HAL_WARNING (("Service '%s' was not in the list of services "
2202			      "with claims!", sender));
2203	}
2204
2205	hal_util_branch_claim (hald_get_gdl (), d, FALSE, NULL, 0);
2206	hal_device_property_remove (d, "info.claimed.dbus_name");
2207
2208	if (!dbus_connection_send (connection, reply, NULL))
2209		DIE (("No memory"));
2210
2211	dbus_message_unref (reply);
2212	return DBUS_HANDLER_RESULT_HANDLED;
2213}
2214
2215
2216/** Counter for atomic updating */
2217static int atomic_count = 0;
2218
2219/** Number of updates pending */
2220static int num_pending_updates = 0;
2221
2222/** Structure for queing updates */
2223typedef struct PendingUpdate_s {
2224	char *udi;                    /**< udi of device */
2225	char *key;                    /**< key of property; free when done */
2226	dbus_bool_t removed;          /**< true iff property was removed */
2227	dbus_bool_t added;            /**< true iff property was added */
2228	struct PendingUpdate_s *next; /**< next update or #NULL */
2229} PendingUpdate;
2230
2231static PendingUpdate *pending_updates_head = NULL;
2232
2233/** Begin an atomic update - this is useful for updating several properties
2234 *  in one go.
2235 *
2236 *  Note that an atomic update is recursive - use with caution!
2237 */
2238void
2239device_property_atomic_update_begin (void)
2240{
2241	atomic_count++;
2242}
2243
2244/** End an atomic update.
2245 *
2246 *  Note that an atomic update is recursive - use with caution!
2247 */
2248void
2249device_property_atomic_update_end (void)
2250{
2251	PendingUpdate *pu_iter = NULL;
2252	PendingUpdate *pu_iter_next = NULL;
2253	PendingUpdate *pu_iter2 = NULL;
2254
2255	--atomic_count;
2256
2257	if (atomic_count < 0) {
2258		HAL_WARNING (("*** atomic_count = %d < 0 !!", atomic_count));
2259		atomic_count = 0;
2260	}
2261
2262	if (atomic_count == 0 && num_pending_updates > 0) {
2263		DBusMessage *message;
2264		DBusMessageIter iter;
2265		DBusMessageIter iter_array;
2266
2267		for (pu_iter = pending_updates_head;
2268		     pu_iter != NULL; pu_iter = pu_iter_next) {
2269			int num_updates_this;
2270
2271			pu_iter_next = pu_iter->next;
2272
2273			/* see if we've already processed this */
2274			if (pu_iter->udi == NULL)
2275				goto already_processed;
2276
2277			/* count number of updates for this device */
2278			num_updates_this = 0;
2279			for (pu_iter2 = pu_iter; pu_iter2 != NULL; pu_iter2 = pu_iter2->next) {
2280				if (strcmp (pu_iter2->udi, pu_iter->udi) == 0)
2281					num_updates_this++;
2282			}
2283
2284			/* prepare message */
2285			message = dbus_message_new_signal (pu_iter->udi,
2286							   "org.freedesktop.Hal.Device",
2287							   "PropertyModified");
2288			dbus_message_iter_init_append (message, &iter);
2289			dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32,
2290							&num_updates_this);
2291
2292			dbus_message_iter_open_container (&iter,
2293							  DBUS_TYPE_ARRAY,
2294							  DBUS_STRUCT_BEGIN_CHAR_AS_STRING
2295							  DBUS_TYPE_STRING_AS_STRING
2296							  DBUS_TYPE_BOOLEAN_AS_STRING
2297							  DBUS_TYPE_BOOLEAN_AS_STRING
2298							  DBUS_STRUCT_END_CHAR_AS_STRING,
2299							  &iter_array);
2300
2301			for (pu_iter2 = pu_iter; pu_iter2 != NULL;
2302			     pu_iter2 = pu_iter2->next) {
2303				if (strcmp (pu_iter2->udi, pu_iter->udi) == 0) {
2304					DBusMessageIter iter_struct;
2305					dbus_message_iter_open_container (&iter_array,
2306									  DBUS_TYPE_STRUCT,
2307									  NULL,
2308									  &iter_struct);
2309					dbus_message_iter_append_basic
2310					    (&iter_struct,
2311					     DBUS_TYPE_STRING,
2312					     &(pu_iter2->key));
2313					dbus_message_iter_append_basic
2314					    (&iter_struct,
2315					     DBUS_TYPE_BOOLEAN,
2316					     &(pu_iter2->removed));
2317					dbus_message_iter_append_basic
2318					    (&iter_struct,
2319					     DBUS_TYPE_BOOLEAN,
2320					     &(pu_iter2->added));
2321
2322					dbus_message_iter_close_container (&iter_array, &iter_struct);
2323
2324					/* signal this is already processed */
2325					g_free (pu_iter2->key);
2326					if (pu_iter2 != pu_iter) {
2327						g_free (pu_iter2->udi);
2328						pu_iter2->udi = NULL;
2329					}
2330				}
2331			}
2332
2333			g_free (pu_iter->udi);
2334			dbus_message_iter_close_container (&iter, &iter_array);
2335
2336			if (dbus_connection != NULL) {
2337				if (!dbus_connection_send (dbus_connection, message, NULL))
2338					DIE (("error broadcasting message"));
2339			}
2340
2341			dbus_message_unref (message);
2342
2343		already_processed:
2344			g_free (pu_iter);
2345
2346		} /* for all updates */
2347
2348		num_pending_updates = 0;
2349		pending_updates_head = NULL;
2350	}
2351}
2352
2353
2354
2355void
2356device_send_signal_property_modified (HalDevice *device, const char *key,
2357				      dbus_bool_t added, dbus_bool_t removed)
2358{
2359	const char *udi = hal_device_get_udi (device);
2360	DBusMessage *message;
2361	DBusMessageIter iter;
2362
2363/*
2364    HAL_INFO(("Entering, udi=%s, key=%s, in_gdl=%s, removed=%s added=%s",
2365              device->udi, key,
2366              in_gdl ? "true" : "false",
2367              removed ? "true" : "false",
2368              added ? "true" : "false"));
2369*/
2370
2371	if (atomic_count > 0) {
2372		PendingUpdate *pu;
2373
2374		pu = g_new0 (PendingUpdate, 1);
2375		pu->udi = g_strdup (udi);
2376		pu->key = g_strdup (key);
2377		pu->removed = removed;
2378		pu->added = added;
2379		pu->next = pending_updates_head;
2380
2381		pending_updates_head = pu;
2382		num_pending_updates++;
2383	} else {
2384		dbus_int32_t i;
2385		DBusMessageIter iter_struct;
2386		DBusMessageIter iter_array;
2387
2388		if (dbus_connection == NULL)
2389			goto out;
2390
2391		message = dbus_message_new_signal (udi,
2392						  "org.freedesktop.Hal.Device",
2393						   "PropertyModified");
2394
2395		dbus_message_iter_init_append (message, &iter);
2396		i = 1;
2397		dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &i);
2398
2399		dbus_message_iter_open_container (&iter,
2400						  DBUS_TYPE_ARRAY,
2401						  DBUS_STRUCT_BEGIN_CHAR_AS_STRING
2402						  DBUS_TYPE_STRING_AS_STRING
2403						  DBUS_TYPE_BOOLEAN_AS_STRING
2404						  DBUS_TYPE_BOOLEAN_AS_STRING
2405						  DBUS_STRUCT_END_CHAR_AS_STRING,
2406						  &iter_array);
2407
2408		dbus_message_iter_open_container (&iter_array,
2409						  DBUS_TYPE_STRUCT,
2410						  NULL,
2411						  &iter_struct);
2412
2413		dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &key);
2414		dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &removed);
2415		dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &added);
2416
2417		dbus_message_iter_close_container (&iter_array, &iter_struct);
2418		dbus_message_iter_close_container (&iter, &iter_array);
2419
2420		if (!dbus_connection_send (dbus_connection, message, NULL))
2421			DIE (("error broadcasting message"));
2422
2423		dbus_message_unref (message);
2424	}
2425out:
2426	;
2427}
2428
2429/** Emits a condition on a device; the device has to be in the GDL for
2430 *  this function to have effect.
2431 *
2432 *  Is intended for non-continuous events on the device like
2433 *  ProcesserOverheating, BlockDeviceGotDevice, e.g. conditions that
2434 *  are exceptional and may not be inferred by looking at properties
2435 *  (though some may).
2436 *
2437 *  This function accepts a number of parameters that are passed along
2438 *  in the D-BUS message. The recipient is supposed to extract the parameters
2439 *  himself, by looking at the HAL specification.
2440 *
2441 * @param  udi                  The UDI for this device
2442 * @param  condition_name       Name of condition
2443 * @param  first_arg_type       Type of the first argument
2444 * @param  ...                  value of first argument, list of additional
2445 *                              type-value pairs. Must be terminated with
2446 *                              DBUS_TYPE_INVALID
2447 */
2448void
2449device_send_signal_condition (HalDevice *device, const char *condition_name, const char *condition_details)
2450{
2451	const char *udi = hal_device_get_udi (device);
2452	DBusMessage *message;
2453	DBusMessageIter iter;
2454
2455	if (dbus_connection == NULL)
2456		goto out;
2457
2458	message = dbus_message_new_signal (udi,
2459					   "org.freedesktop.Hal.Device",
2460					   "Condition");
2461	dbus_message_iter_init_append (message, &iter);
2462	dbus_message_iter_append_basic (&iter,
2463					DBUS_TYPE_STRING,
2464					&condition_name);
2465	dbus_message_iter_append_basic (&iter,
2466					DBUS_TYPE_STRING,
2467					&condition_details);
2468
2469	if (!dbus_connection_send (dbus_connection, message, NULL))
2470		DIE (("error broadcasting message"));
2471
2472	dbus_message_unref (message);
2473out:
2474	return;
2475}
2476
2477
2478
2479static gboolean
2480reinit_dbus (gpointer user_data)
2481{
2482	HAL_INFO (("entering!"));
2483	if (hald_dbus_init ())
2484		return FALSE;
2485	else
2486		return TRUE;
2487}
2488
2489static void
2490service_deleted (DBusMessage *message)
2491{
2492	char *old_service_name;
2493	char *new_service_name;
2494	HalDevice *d;
2495
2496	if (!dbus_message_get_args (message, NULL,
2497				    DBUS_TYPE_STRING, &old_service_name,
2498				    DBUS_TYPE_STRING, &new_service_name,
2499				    DBUS_TYPE_INVALID)) {
2500		HAL_ERROR (("Invalid NameOwnerChanged signal from bus!"));
2501		return;
2502	}
2503
2504	if (services_with_locks != NULL) {
2505		d = g_hash_table_lookup (services_with_locks, new_service_name);
2506
2507		if (d != NULL) {
2508			hal_device_property_remove (d, "info.locked");
2509			hal_device_property_remove (d, "info.locked.reason");
2510			hal_device_property_remove (d, "info.locked.dbus_name");
2511
2512			g_hash_table_remove (services_with_locks, new_service_name);
2513		}
2514	}
2515
2516	if (services_with_claims != NULL) {
2517		d = g_hash_table_lookup (services_with_claims, new_service_name);
2518
2519		if (d != NULL) {
2520			hal_util_branch_claim (hald_get_gdl (), d, FALSE, NULL, 0);
2521			hal_device_property_remove (d, "info.claimed.dbus_name");
2522
2523			g_hash_table_remove (services_with_claims, new_service_name);
2524		}
2525	}
2526}
2527
2528static DBusHandlerResult
2529device_rescan (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
2530{
2531	const char *udi;
2532	HalDevice *device;
2533	DBusMessage *reply;
2534	DBusMessageIter iter;
2535	gboolean res;
2536
2537	HAL_INFO (("entering, local_interface=%d", local_interface));
2538
2539	udi = dbus_message_get_path (message);
2540
2541	if (!local_interface && !sender_has_privileges (connection, message)) {
2542		raise_permission_denied (connection, message, "Rescan: not privileged");
2543		return DBUS_HANDLER_RESULT_HANDLED;
2544	}
2545
2546	HAL_DEBUG (("udi=%s", udi));
2547
2548	device = hal_device_store_find (hald_get_gdl (), udi);
2549	if (device == NULL)
2550		device = hal_device_store_find (hald_get_tdl (), udi);
2551
2552	if (device == NULL) {
2553		raise_no_such_device (connection, message, udi);
2554		return DBUS_HANDLER_RESULT_HANDLED;
2555	}
2556
2557	res = osspec_device_rescan (device);
2558
2559	reply = dbus_message_new_method_return (message);
2560	if (reply == NULL)
2561		DIE (("No memory"));
2562	dbus_message_iter_init_append (reply, &iter);
2563	dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &res);
2564
2565	if (!dbus_connection_send (connection, reply, NULL))
2566		DIE (("No memory"));
2567
2568	dbus_message_unref (reply);
2569	return DBUS_HANDLER_RESULT_HANDLED;
2570}
2571
2572static DBusHandlerResult
2573device_reprobe (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
2574{
2575	const char *udi;
2576	HalDevice *device;
2577	DBusMessageIter iter;
2578	DBusMessage *reply;
2579	gboolean res;
2580
2581	HAL_TRACE (("entering"));
2582
2583	udi = dbus_message_get_path (message);
2584
2585	if (!local_interface && !sender_has_privileges (connection, message)) {
2586		raise_permission_denied (connection, message, "Reprobe: not privileged");
2587		return DBUS_HANDLER_RESULT_HANDLED;
2588	}
2589
2590	HAL_DEBUG (("udi=%s", udi));
2591
2592	device = hal_device_store_find (hald_get_gdl (), udi);
2593	if (device == NULL)
2594		device = hal_device_store_find (hald_get_tdl (), udi);
2595
2596	if (device == NULL) {
2597		raise_no_such_device (connection, message, udi);
2598		return DBUS_HANDLER_RESULT_HANDLED;
2599	}
2600
2601	res = osspec_device_reprobe (device);
2602
2603	reply = dbus_message_new_method_return (message);
2604	if (reply == NULL)
2605		DIE (("No memory"));
2606	dbus_message_iter_init_append (reply, &iter);
2607	dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &res);
2608
2609	if (!dbus_connection_send (connection, reply, NULL))
2610		DIE (("No memory"));
2611
2612	dbus_message_unref (reply);
2613	return DBUS_HANDLER_RESULT_HANDLED;
2614}
2615
2616
2617static DBusHandlerResult
2618device_emit_condition (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
2619{
2620	const char *udi;
2621	HalDevice *device;
2622	DBusMessageIter iter;
2623	DBusMessage *reply;
2624	DBusError error;
2625	const char *condition_name;
2626	const char *condition_details;
2627	dbus_bool_t res;
2628
2629	HAL_TRACE (("entering"));
2630
2631	udi = dbus_message_get_path (message);
2632
2633	if (!local_interface) {
2634		raise_permission_denied (connection, message, "EmitCondition: only allowed for helpers");
2635		return DBUS_HANDLER_RESULT_HANDLED;
2636	}
2637
2638	HAL_DEBUG (("udi=%s", udi));
2639
2640	dbus_error_init (&error);
2641	if (!dbus_message_get_args (message, &error,
2642				    DBUS_TYPE_STRING, &condition_name,
2643				    DBUS_TYPE_STRING, &condition_details,
2644				    DBUS_TYPE_INVALID)) {
2645		raise_syntax (connection, message, "EmitCondition");
2646		return DBUS_HANDLER_RESULT_HANDLED;
2647	}
2648
2649
2650	device = hal_device_store_find (hald_get_gdl (), udi);
2651	if (device == NULL)
2652		device = hal_device_store_find (hald_get_tdl (), udi);
2653
2654	if (device == NULL) {
2655		raise_no_such_device (connection, message, udi);
2656		return DBUS_HANDLER_RESULT_HANDLED;
2657	}
2658
2659	device_send_signal_condition (device, condition_name, condition_details);
2660
2661	res = TRUE;
2662
2663	reply = dbus_message_new_method_return (message);
2664	if (reply == NULL)
2665		DIE (("No memory"));
2666	dbus_message_iter_init_append (reply, &iter);
2667	dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &res);
2668
2669	if (!dbus_connection_send (connection, reply, NULL))
2670		DIE (("No memory"));
2671
2672	dbus_message_unref (reply);
2673	return DBUS_HANDLER_RESULT_HANDLED;
2674}
2675
2676typedef struct
2677{
2678	DBusConnection  *connection;
2679	char            *interface_name;
2680	char            *introspection_xml;
2681	char            *udi;
2682} HelperInterfaceHandler;
2683
2684static GSList *helper_interface_handlers = NULL;
2685
2686static DBusHandlerResult
2687device_claim_interface (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
2688{
2689	const char *udi;
2690	HalDevice *device;
2691	DBusMessageIter iter;
2692	DBusMessage *reply;
2693	DBusError error;
2694	const char *interface_name;
2695	const char *introspection_xml;
2696	dbus_bool_t res;
2697
2698	HAL_TRACE (("entering"));
2699
2700	udi = dbus_message_get_path (message);
2701
2702	if (!local_interface) {
2703		raise_permission_denied (connection, message, "ClaimInterface: only allowed for helpers");
2704		return DBUS_HANDLER_RESULT_HANDLED;
2705	}
2706
2707	HAL_DEBUG (("udi=%s", udi));
2708
2709	dbus_error_init (&error);
2710	if (!dbus_message_get_args (message, &error,
2711				    DBUS_TYPE_STRING, &interface_name,
2712				    DBUS_TYPE_STRING, &introspection_xml,
2713				    DBUS_TYPE_INVALID)) {
2714		raise_syntax (connection, message, "ClaimInterface");
2715		return DBUS_HANDLER_RESULT_HANDLED;
2716	}
2717
2718	device = hal_device_store_find (hald_get_gdl (), udi);
2719	if (device == NULL)
2720		device = hal_device_store_find (hald_get_tdl (), udi);
2721
2722	if (device == NULL) {
2723		raise_no_such_device (connection, message, udi);
2724		return DBUS_HANDLER_RESULT_HANDLED;
2725	}
2726
2727	res = TRUE;
2728
2729	HAL_INFO (("Local connection 0x%x to handle interface '%s' on udi '%s'",
2730		   connection,
2731		   interface_name,
2732		   udi));
2733
2734	hal_device_property_strlist_add (device, "info.interfaces", interface_name);
2735
2736	HelperInterfaceHandler *hih = g_new0 (HelperInterfaceHandler, 1);
2737	hih->connection = connection;
2738	hih->interface_name = g_strdup (interface_name);
2739	hih->introspection_xml = g_strdup (introspection_xml);
2740	hih->udi = g_strdup (udi);
2741	helper_interface_handlers = g_slist_append (helper_interface_handlers, hih);
2742
2743
2744	reply = dbus_message_new_method_return (message);
2745	if (reply == NULL)
2746		DIE (("No memory"));
2747	dbus_message_iter_init_append (reply, &iter);
2748	dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &res);
2749
2750	if (!dbus_connection_send (connection, reply, NULL))
2751		DIE (("No memory"));
2752
2753	dbus_message_unref (reply);
2754	return DBUS_HANDLER_RESULT_HANDLED;
2755}
2756
2757
2758
2759static DBusHandlerResult
2760addon_is_ready (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
2761{
2762	const char *udi;
2763	HalDevice *device;
2764	DBusMessageIter iter;
2765	DBusMessage *reply;
2766	DBusError error;
2767	dbus_bool_t res;
2768
2769	HAL_TRACE (("entering"));
2770
2771	udi = dbus_message_get_path (message);
2772
2773	if (!local_interface) {
2774		raise_permission_denied (connection, message, "AddonIsReady: only allowed for helpers");
2775		return DBUS_HANDLER_RESULT_HANDLED;
2776	}
2777
2778	HAL_DEBUG (("udi=%s", udi));
2779
2780	dbus_error_init (&error);
2781	if (!dbus_message_get_args (message, &error,
2782				    DBUS_TYPE_INVALID)) {
2783		raise_syntax (connection, message, "AddonIsReady");
2784		return DBUS_HANDLER_RESULT_HANDLED;
2785	}
2786
2787	device = hal_device_store_find (hald_get_gdl (), udi);
2788	if (device == NULL)
2789		device = hal_device_store_find (hald_get_tdl (), udi);
2790
2791	if (device == NULL) {
2792		raise_no_such_device (connection, message, udi);
2793		return DBUS_HANDLER_RESULT_HANDLED;
2794	}
2795
2796	if (hal_device_inc_num_ready_addons (device)) {
2797		if (hal_device_are_all_addons_ready (device)) {
2798			manager_send_signal_device_added (device);
2799		}
2800	}
2801
2802	res = TRUE;
2803
2804	HAL_INFO (("AddonIsReady on udi '%s'", udi));
2805
2806	reply = dbus_message_new_method_return (message);
2807	if (reply == NULL)
2808		DIE (("No memory"));
2809	dbus_message_iter_init_append (reply, &iter);
2810	dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &res);
2811
2812	if (!dbus_connection_send (connection, reply, NULL))
2813		DIE (("No memory"));
2814
2815	dbus_message_unref (reply);
2816	return DBUS_HANDLER_RESULT_HANDLED;
2817}
2818
2819
2820/*
2821 * Create new device in tdl. Return temporary udi.
2822 */
2823DBusHandlerResult
2824manager_new_device (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
2825{
2826	DBusMessage *reply;
2827	DBusMessageIter iter;
2828	DBusError error;
2829	HalDevice *d;
2830	gchar *udi;
2831	int i;
2832	struct timeval tv;
2833
2834	dbus_error_init (&error);
2835
2836	if (!local_interface && !sender_has_privileges (connection, message)) {
2837		raise_permission_denied (connection, message, "NewDevice: not privileged");
2838		return DBUS_HANDLER_RESULT_HANDLED;
2839	}
2840
2841	reply = dbus_message_new_method_return (message);
2842	if (reply == NULL)
2843		DIE (("No memory"));
2844
2845	dbus_message_iter_init_append (reply, &iter);
2846	d = hal_device_new ();
2847
2848	gettimeofday(&tv, NULL);
2849	for (i = 0; i < 1000000 ; i++) {
2850		udi = g_strdup_printf ("/org/freedesktop/Hal/devices/tmp%05x", ((unsigned) tv.tv_usec & 0xfffff)) + i;
2851		if (!hal_device_store_find (hald_get_tdl (), udi)) break;
2852		g_free (udi);
2853		udi = NULL;
2854	}
2855
2856	if (!udi) {
2857		raise_error (connection, message, "org.freedesktop.Hal.NoSpace", "NewDevice: no space for device");
2858		return DBUS_HANDLER_RESULT_HANDLED;
2859	}
2860
2861	hal_device_set_udi (d, udi);
2862	hal_device_property_set_string (d, "info.udi", udi);
2863	hal_device_store_add (hald_get_tdl (), d);
2864	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &udi);
2865	g_free (udi);
2866
2867	if (!dbus_connection_send (connection, reply, NULL))
2868		DIE (("No memory"));
2869
2870	dbus_message_unref (reply);
2871
2872	return DBUS_HANDLER_RESULT_HANDLED;
2873}
2874
2875
2876/*
2877 * Callout helper.
2878 */
2879static void
2880manager_remove_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
2881{
2882	HAL_INFO (("Remove callouts completed udi=%s", d->udi));
2883
2884	if (!hal_device_store_remove (hald_get_gdl (), d)) {
2885		HAL_WARNING (("Error removing device"));
2886	}
2887}
2888
2889
2890/*
2891 * Remove device. Looks in gdl and tdl.
2892 */
2893DBusHandlerResult
2894manager_remove (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
2895{
2896	DBusMessage *reply;
2897	DBusMessageIter iter;
2898	DBusError error;
2899	HalDevice *d;
2900	char *udi;
2901	int in_tdl = 0;
2902
2903	dbus_error_init (&error);
2904
2905	if (!local_interface && !sender_has_privileges (connection, message)) {
2906		raise_permission_denied (connection, message, "Remove: not privileged");
2907		return DBUS_HANDLER_RESULT_HANDLED;
2908	}
2909
2910	if (!dbus_message_get_args (message, &error,
2911				    DBUS_TYPE_STRING, &udi,
2912				    DBUS_TYPE_INVALID)) {
2913		raise_syntax (connection, message, "Remove");
2914		return DBUS_HANDLER_RESULT_HANDLED;
2915	}
2916
2917	reply = dbus_message_new_method_return (message);
2918	if (reply == NULL)
2919		DIE (("No memory"));
2920
2921	dbus_message_iter_init_append (reply, &iter);
2922
2923	d = hal_device_store_find (hald_get_gdl (), udi);
2924	if (d == NULL) {
2925		hal_device_store_find (hald_get_tdl (), udi);
2926		in_tdl = 1;
2927	}
2928	if (d == NULL) {
2929		raise_no_such_device (connection, message, udi);
2930		return DBUS_HANDLER_RESULT_HANDLED;
2931	}
2932
2933	/* FIXME:
2934	 * run "info.callouts.remove" ?
2935	 * delete in gdl ?
2936	 * (auto) stop "info.addons" ?
2937	 */
2938
2939	if (!in_tdl) {
2940		hal_util_callout_device_remove (d, manager_remove_done, NULL, NULL);
2941	}
2942
2943	hal_device_store_remove (in_tdl ? hald_get_tdl () : hald_get_gdl (), d);
2944	g_object_unref (d);
2945
2946	if (!dbus_connection_send (connection, reply, NULL))
2947		DIE (("No memory"));
2948
2949	dbus_message_unref (reply);
2950
2951	return DBUS_HANDLER_RESULT_HANDLED;
2952}
2953
2954
2955/*
2956 * Callout helper.
2957 */
2958static void
2959manager_commit_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
2960{
2961	HAL_INFO (("Add callouts completed udi=%s", d->udi));
2962}
2963
2964/*
2965 * Preprobing helper.
2966 */
2967static void
2968manager_commit_preprobing_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
2969{
2970	if (hal_device_property_get_bool (d, "info.ignore")) {
2971		/* Leave the device here with info.ignore==TRUE so we won't pick up children
2972		 * Also remove category and all capabilities
2973		 */
2974		hal_device_property_remove (d, "info.category");
2975		hal_device_property_remove (d, "info.capabilities");
2976		hal_device_property_set_string (d, "info.udi", "/org/freedesktop/Hal/devices/ignored-device");
2977		hal_device_property_set_string (d, "info.product", "Ignored Device");
2978
2979		HAL_INFO (("Preprobing merged info.ignore==TRUE"));
2980
2981		return;
2982	}
2983
2984	/* Merge properties from .fdi files */
2985	di_search_and_merge (d, DEVICE_INFO_TYPE_INFORMATION);
2986	di_search_and_merge (d, DEVICE_INFO_TYPE_POLICY);
2987
2988	hal_util_callout_device_add (d, manager_commit_done, NULL, NULL);
2989}
2990
2991
2992/*
2993 * Move device from tdl to gdl. Runs helpers and callouts.
2994 */
2995DBusHandlerResult
2996manager_commit_to_gdl (DBusConnection * connection, DBusMessage * message, dbus_bool_t local_interface)
2997{
2998	DBusMessage *reply;
2999	DBusMessageIter iter;
3000	DBusError error;
3001	HalDevice *d;
3002	char udi[256], *udi0, *tmp_udi;
3003
3004	dbus_error_init (&error);
3005
3006	if (!local_interface && !sender_has_privileges (connection, message)) {
3007		raise_permission_denied (connection, message, "CommitToGdl: not privileged");
3008		return DBUS_HANDLER_RESULT_HANDLED;
3009	}
3010
3011	if (!dbus_message_get_args (message, &error,
3012				    DBUS_TYPE_STRING, &tmp_udi,
3013				    DBUS_TYPE_STRING, &udi0,
3014				    DBUS_TYPE_INVALID)) {
3015		raise_syntax (connection, message, "CommitToGdl");
3016		return DBUS_HANDLER_RESULT_HANDLED;
3017	}
3018
3019	reply = dbus_message_new_method_return (message);
3020	if (reply == NULL)
3021		DIE (("No memory"));
3022
3023	dbus_message_iter_init_append (reply, &iter);
3024
3025	/* look it up in tdl */
3026
3027	d = hal_device_store_find (hald_get_tdl (), tmp_udi);
3028	if (d == NULL) {
3029		raise_no_such_device (connection, message, tmp_udi);
3030		return DBUS_HANDLER_RESULT_HANDLED;
3031	}
3032
3033	/* sanity check & avoid races */
3034	hal_util_compute_udi (hald_get_gdl (), udi, sizeof udi, "%s", udi0);
3035
3036	if (hal_device_store_find (hald_get_gdl (), udi)) {
3037		/* loose it */
3038		hal_device_store_remove (hald_get_tdl (), d);
3039		g_object_unref (d);
3040		raise_error (connection, message, "org.freedesktop.Hal.DeviceExists", "CommitToGdl: Device exists: %s", udi);
3041		return DBUS_HANDLER_RESULT_HANDLED;
3042	}
3043
3044	/* set new udi */
3045	hal_device_property_remove (d, "info.udi");
3046	hal_device_set_udi (d, udi);
3047	hal_device_property_set_string (d, "info.udi", udi);
3048
3049	/* FIXME:
3050	 * 'RequireEnable' property?
3051	 * fdi "preprobe"?
3052	 * run "info.callouts.preprobe"?
3053	 * remove "info.ignore" devices?
3054	 * fdi "information"?
3055	 * fdi "policy"?
3056	 * run "info.callouts.add"?
3057	 * tdl -> gdl?
3058	 * (auto) start "info.addons"?
3059	 */
3060
3061	/* Process preprobe fdi files */
3062	di_search_and_merge (d, DEVICE_INFO_TYPE_PREPROBE);
3063	hal_util_callout_device_preprobe (d, manager_commit_preprobing_done, NULL, NULL);
3064
3065	/* move from tdl to gdl */
3066	hal_device_store_remove (hald_get_tdl (), d);
3067	hal_device_store_add (hald_get_gdl (), d);
3068
3069	if (!dbus_connection_send (connection, reply, NULL))
3070		DIE (("No memory"));
3071
3072	dbus_message_unref (reply);
3073
3074	return DBUS_HANDLER_RESULT_HANDLED;
3075}
3076
3077typedef struct {
3078	char *udi;
3079	char *execpath;
3080	char **extra_env;
3081	char *mstdin;
3082	char *member;
3083	char *interface;
3084	DBusMessage *message;
3085	DBusConnection *connection;
3086} MethodInvocation;
3087
3088static void
3089hald_exec_method_cb (HalDevice *d, guint32 exit_type,
3090		     gint return_code, gchar **error,
3091		     gpointer data1, gpointer data2);
3092
3093
3094static void
3095hald_exec_method_free_mi (MethodInvocation *mi)
3096{
3097	/* hald_runner_run_method() assumes ownership of mi->message.. so we don't free it here */
3098	g_free (mi->udi);
3099	g_free (mi->execpath);
3100	g_strfreev (mi->extra_env);
3101	g_free (mi->mstdin);
3102	g_free (mi->member);
3103	g_free (mi->interface);
3104	g_free (mi);
3105}
3106
3107/* returns FALSE if we don't actually invoke anything */
3108static gboolean
3109hald_exec_method_do_invocation (MethodInvocation *mi)
3110{
3111	gboolean ret;
3112	HalDevice *d;
3113
3114	ret = FALSE;
3115
3116	d = hal_device_store_find (hald_get_gdl (), mi->udi);
3117	if (d == NULL)
3118		d = hal_device_store_find (hald_get_tdl (), mi->udi);
3119
3120	if (d != NULL) {
3121		/* no timeout */
3122		hald_runner_run_method(d,
3123				       mi->execpath,
3124				       mi->extra_env,
3125				       mi->mstdin,
3126				       TRUE,
3127				       0,
3128				       hald_exec_method_cb,
3129				       (gpointer) mi->message,
3130				       (gpointer) mi->connection);
3131
3132		ret = TRUE;
3133	} else {
3134		HAL_WARNING (("In-queue method call on non-existant device"));
3135
3136		raise_no_such_device (mi->connection, mi->message, mi->udi);
3137	}
3138
3139	return ret;
3140}
3141
3142
3143static GHashTable *udi_to_method_queue = NULL;
3144
3145
3146gboolean
3147device_is_executing_method (HalDevice *d, const char *interface_name, const char *method_name)
3148{
3149	gpointer origkey;
3150	gboolean ret;
3151	GList *queue;
3152
3153	ret = FALSE;
3154
3155	if (g_hash_table_lookup_extended (udi_to_method_queue, d->udi, &origkey, (gpointer) &queue)) {
3156
3157		if (queue != NULL) {
3158			MethodInvocation *mi;
3159			mi = (MethodInvocation *) queue->data;
3160			if ((strcmp (mi->interface, interface_name) == 0) &&
3161			    (strcmp (mi->member, method_name) == 0)) {
3162				ret = TRUE;
3163			}
3164		}
3165
3166		ret = TRUE;
3167	}
3168	return ret;
3169}
3170
3171static void
3172hald_exec_method_process_queue (const char *udi);
3173
3174static void
3175hald_exec_method_enqueue (MethodInvocation *mi)
3176{
3177	gpointer origkey;
3178	GList *queue;
3179
3180	if (udi_to_method_queue == NULL) {
3181		udi_to_method_queue = g_hash_table_new_full (g_str_hash,
3182							     g_str_equal,
3183							     g_free,
3184							     NULL);
3185	}
3186
3187	if (g_hash_table_lookup_extended (udi_to_method_queue, mi->udi, &origkey, (gpointer) &queue)) {
3188		HAL_INFO (("enqueue"));
3189		queue = g_list_append (queue, mi);
3190		g_hash_table_replace (udi_to_method_queue, g_strdup (mi->udi), queue);
3191	} else {
3192		HAL_INFO (("no need to enqueue"));
3193		queue = g_list_append (NULL, mi);
3194		g_hash_table_insert (udi_to_method_queue, g_strdup (mi->udi), queue);
3195
3196		hald_exec_method_do_invocation (mi);
3197	}
3198}
3199
3200
3201static void
3202hald_exec_method_process_queue (const char *udi)
3203{
3204	gpointer origkey;
3205	GList *queue;
3206	MethodInvocation *mi;
3207
3208	if (g_hash_table_lookup_extended (udi_to_method_queue, udi, &origkey, (gpointer) &queue)) {
3209
3210		/* clean the top of the list */
3211		if (queue != NULL) {
3212			mi = (MethodInvocation *) queue->data;
3213			queue = g_list_delete_link (queue, queue);
3214			if (queue == NULL) {
3215				g_hash_table_remove (udi_to_method_queue, udi);
3216				HAL_INFO (("No more methods in queue"));
3217			}
3218
3219			/* if method was Volume.Unmount() then refresh mount state */
3220			if (strcmp (mi->interface, "org.freedesktop.Hal.Device.Volume") == 0 &&
3221			    strcmp (mi->member, "Unmount") == 0) {
3222				HalDevice *d;
3223
3224				HAL_INFO (("Refreshing mount state for %s since Unmount() completed", mi->udi));
3225
3226				d = hal_device_store_find (hald_get_gdl (), mi->udi);
3227				if (d == NULL) {
3228					d = hal_device_store_find (hald_get_tdl (), mi->udi);
3229				}
3230
3231				if (d != NULL) {
3232					osspec_refresh_mount_state_for_block_device (d);
3233				} else {
3234					HAL_WARNING ((" Cannot find device object for %s", mi->udi));
3235				}
3236			}
3237
3238			hald_exec_method_free_mi (mi);
3239		}
3240
3241		/* process the rest of the list */
3242		if (queue != NULL) {
3243			HAL_INFO (("Execing next method in queue"));
3244			g_hash_table_replace (udi_to_method_queue, g_strdup (udi), queue);
3245
3246			mi = (MethodInvocation *) queue->data;
3247			if (!hald_exec_method_do_invocation (mi)) {
3248				/* the device went away before we got to it... */
3249				hald_exec_method_process_queue (mi->udi);
3250			}
3251		}
3252	}
3253}
3254
3255static void
3256hald_exec_method_cb (HalDevice *d, guint32 exit_type,
3257		     gint return_code, gchar **error,
3258		     gpointer data1, gpointer data2)
3259{
3260	dbus_int32_t result;
3261	DBusMessage *reply = NULL;
3262	DBusMessage *message;
3263	DBusMessageIter iter;
3264	DBusConnection *conn;
3265	gchar *exp_name = NULL;
3266	gchar *exp_detail = NULL;
3267	gboolean invalid_name = FALSE;
3268
3269	hald_exec_method_process_queue (d->udi);
3270
3271	message = (DBusMessage *) data1;
3272	conn = (DBusConnection *) data2;
3273
3274	if (exit_type == HALD_RUN_SUCCESS && error != NULL &&
3275	    error[0] != NULL && error[1] != NULL) {
3276		exp_name = error[0];
3277		if (error[0] != NULL) {
3278			exp_detail = error[1];
3279		}
3280		HAL_INFO (("failed with '%s' '%s'", exp_name, exp_detail));
3281	}
3282
3283	if (exit_type != HALD_RUN_SUCCESS) {
3284		reply = dbus_message_new_error (message, "org.freedesktop.Hal.Device.UnknownError", "An unknown error occured");
3285		if (conn != NULL) {
3286			if (!dbus_connection_send (conn, reply, NULL))
3287				DIE (("No memory"));
3288		}
3289		dbus_message_unref (reply);
3290	} else if (exp_name != NULL && exp_detail != NULL) {
3291		if (!is_valid_interface_name (exp_name)) {
3292			/*
3293			 * error name may be invalid,
3294			 * if so we need a generic HAL error name;
3295			 * otherwise, dbus will be messed up.
3296			 */
3297			invalid_name = TRUE;
3298			exp_detail = g_strconcat (exp_name, " \n ", exp_detail, (void *)NULL);
3299			exp_name = "org.freedesktop.Hal.Device.UnknownError";
3300		}
3301		reply = dbus_message_new_error (message, exp_name, exp_detail);
3302		if (reply == NULL) {
3303			/* error name may be invalid - assume caller fucked up and use a generic HAL error name */
3304			reply = dbus_message_new_error (message, "org.freedesktop.Hal.Device.UnknownError", "An unknown error occured");
3305			if (reply == NULL) {
3306				DIE (("No memory"));
3307			}
3308		}
3309		if (conn != NULL) {
3310			if (!dbus_connection_send (conn, reply, NULL))
3311				DIE (("No memory"));
3312		}
3313		dbus_message_unref (reply);
3314
3315	} else {
3316		result = (dbus_int32_t) return_code;
3317
3318		reply = dbus_message_new_method_return (message);
3319		if (reply == NULL)
3320			DIE (("No memory"));
3321
3322		dbus_message_iter_init_append (reply, &iter);
3323		dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &result);
3324
3325		if (conn != NULL) {
3326			if (!dbus_connection_send (conn, reply, NULL))
3327				DIE (("No memory"));
3328		}
3329
3330		dbus_message_unref (reply);
3331	}
3332
3333	if (invalid_name)
3334		g_free (exp_detail);
3335	dbus_message_unref (message);
3336}
3337
3338static DBusHandlerResult
3339hald_exec_method (HalDevice *d, DBusConnection *connection, dbus_bool_t local_interface,
3340		  DBusMessage *message, const char *execpath)
3341{
3342	int type;
3343	GString *stdin_str;
3344	DBusMessageIter iter;
3345	char *extra_env[3];
3346	char uid_export[128];
3347	char sender_export[128];
3348	MethodInvocation *mi;
3349
3350	/* add calling uid */
3351	extra_env[0] = NULL;
3352	extra_env[1] = NULL;
3353	if (local_interface) {
3354		extra_env[0] = "HAL_METHOD_INVOKED_BY_UID=0";
3355		extra_env[1] = "HAL_METHOD_INVOKED_BY_SYSTEMBUS_CONNECTION_NAME=0";
3356	} else {
3357		const char *sender;
3358
3359		sender = dbus_message_get_sender (message);
3360		if (sender != NULL) {
3361			DBusError error;
3362			unsigned long uid;
3363
3364			dbus_error_init (&error);
3365			uid = dbus_bus_get_unix_user (connection, sender, &error);
3366			if (!dbus_error_is_set (&error)) {
3367				sprintf (uid_export, "HAL_METHOD_INVOKED_BY_UID=%lu", uid);
3368				extra_env[0] = uid_export;
3369			}
3370			snprintf (sender_export, sizeof (sender_export),
3371				  "HAL_METHOD_INVOKED_BY_SYSTEMBUS_CONNECTION_NAME=%s", sender);
3372			extra_env[1] = sender_export;
3373		}
3374	}
3375
3376	if (extra_env[0] == NULL)
3377		extra_env[0] = "HAL_METHOD_INVOKED_BY_UID=nobody";
3378	if (extra_env[1] == NULL)
3379		extra_env[1] = "HAL_METHOD_INVOKED_BY_SYSTEMBUS_CONNECTION_NAME=0";
3380
3381
3382	extra_env[2] = NULL;
3383
3384	/* prepare stdin with parameters */
3385	stdin_str = g_string_sized_new (256); /* default size for passing params; can grow */
3386	dbus_message_iter_init (message, &iter);
3387	while ((type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID) {
3388		switch (type) {
3389		case DBUS_TYPE_BYTE:
3390		{
3391			unsigned char value;
3392			dbus_message_iter_get_basic (&iter, &value);
3393			g_string_append_printf (stdin_str, "%u", value);
3394			break;
3395		}
3396		case DBUS_TYPE_INT16:
3397		{
3398			dbus_int16_t value;
3399			dbus_message_iter_get_basic (&iter, &value);
3400			g_string_append_printf (stdin_str, "%d", value);
3401			break;
3402		}
3403		case DBUS_TYPE_UINT16:
3404		{
3405			dbus_uint16_t value;
3406			dbus_message_iter_get_basic (&iter, &value);
3407			g_string_append_printf (stdin_str, "%u", value);
3408			break;
3409		}
3410		case DBUS_TYPE_INT32:
3411		{
3412			dbus_int32_t value;
3413			dbus_message_iter_get_basic (&iter, &value);
3414			g_string_append_printf (stdin_str, "%d", value);
3415			break;
3416		}
3417		case DBUS_TYPE_UINT32:
3418		{
3419			dbus_uint32_t value;
3420			dbus_message_iter_get_basic (&iter, &value);
3421			g_string_append_printf (stdin_str, "%u", value);
3422			break;
3423		}
3424		case DBUS_TYPE_INT64:
3425		{
3426			dbus_int64_t value;
3427			dbus_message_iter_get_basic (&iter, &value);
3428			g_string_append_printf (stdin_str, "%lld", (long long int) value);
3429			break;
3430		}
3431		case DBUS_TYPE_UINT64:
3432		{
3433			dbus_uint64_t value;
3434			dbus_message_iter_get_basic (&iter, &value);
3435			g_string_append_printf (stdin_str, "%llu", (long long unsigned int) value);
3436			break;
3437		}
3438		case DBUS_TYPE_DOUBLE:
3439		{
3440			double value;
3441			dbus_message_iter_get_basic (&iter, &value);
3442			g_string_append_printf (stdin_str, "%g", value);
3443			break;
3444		}
3445		case DBUS_TYPE_BOOLEAN:
3446		{
3447			dbus_bool_t value;
3448			dbus_message_iter_get_basic (&iter, &value);
3449			g_string_append (stdin_str, value ? "true" : "false");
3450			break;
3451		}
3452		case DBUS_TYPE_STRING:
3453		{
3454			char *value;
3455			dbus_message_iter_get_basic (&iter, &value);
3456			g_string_append (stdin_str, value);
3457			break;
3458		}
3459
3460		case DBUS_TYPE_ARRAY:
3461		{
3462			DBusMessageIter iter_strlist;
3463			if (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_STRING)
3464				goto error;
3465
3466			dbus_message_iter_recurse (&iter, &iter_strlist);
3467			while (dbus_message_iter_get_arg_type (&iter_strlist) == DBUS_TYPE_STRING) {
3468				const char *value;
3469				dbus_message_iter_get_basic (&iter_strlist, &value);
3470				g_string_append (stdin_str, value);
3471				g_string_append (stdin_str, "\t");
3472				dbus_message_iter_next(&iter_strlist);
3473			}
3474			break;
3475		}
3476
3477		default:
3478			goto error;
3479		}
3480
3481		g_string_append_c (stdin_str, '\n');
3482		dbus_message_iter_next (&iter);
3483	}
3484
3485	mi = g_new0 (MethodInvocation, 1);
3486	mi->udi = g_strdup (d->udi);
3487	mi->execpath = g_strdup (execpath);
3488	mi->extra_env = g_strdupv (extra_env);
3489	mi->mstdin = g_strdup (stdin_str->str);
3490	mi->message = message;
3491	mi->connection = connection;
3492	mi->member = g_strdup (dbus_message_get_member (message));
3493	mi->interface = g_strdup (dbus_message_get_interface (message));
3494	hald_exec_method_enqueue (mi);
3495
3496	dbus_message_ref (message);
3497	g_string_free (stdin_str, TRUE);
3498
3499	return DBUS_HANDLER_RESULT_HANDLED;
3500
3501error:
3502	g_string_free (stdin_str, TRUE);
3503	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
3504}
3505
3506static gboolean
3507foreach_device_get_xml_node (HalDeviceStore *store, HalDevice *device,
3508			     gpointer user_data)
3509{
3510	GString *xml = user_data;
3511	const char *udi, *name;
3512
3513	udi = hal_device_get_udi (device);
3514	name = strrchr(udi, '/')+1;
3515
3516	xml = g_string_append(xml, "  <node name=\"");
3517	xml = g_string_append(xml, name);
3518	xml = g_string_append(xml, "\"/>\n");
3519
3520	return TRUE;
3521}
3522
3523static DBusHandlerResult
3524do_introspect (DBusConnection  *connection,
3525	       DBusMessage     *message,
3526	       dbus_bool_t      local_interface)
3527{
3528	const char *path;
3529	DBusMessage *reply;
3530	GString *xml;
3531	char *xml_string;
3532
3533	HAL_TRACE (("entering do_introspect"));
3534
3535	path = dbus_message_get_path (message);
3536
3537	xml = g_string_new ("<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
3538			    "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
3539			    "<node>\n"
3540			    "  <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
3541			    "    <method name=\"Introspect\">\n"
3542			    "      <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
3543			    "    </method>\n"
3544			    "  </interface>\n");
3545
3546	if (strcmp (path, "/") == 0) {
3547
3548		xml = g_string_append (xml,
3549				       "  <node name=\"org\"/>\n");
3550
3551	} else if (strcmp (path, "/org") == 0) {
3552
3553		xml = g_string_append (xml,
3554				       "  <node name=\"freedesktop\"/>\n");
3555
3556	} else if (strcmp (path, "/org/freedesktop") == 0) {
3557
3558		xml = g_string_append (xml,
3559				       "  <node name=\"Hal\"/>\n");
3560
3561	} else if (strcmp (path, "/org/freedesktop/Hal") == 0) {
3562
3563		xml = g_string_append (xml,
3564				       "  <node name=\"Manager\"/>\n"
3565				       "  <node name=\"devices\"/>\n");
3566
3567	} else if (strcmp (path, "/org/freedesktop/Hal/devices") == 0) {
3568
3569		hal_device_store_foreach (hald_get_gdl (),
3570					  foreach_device_get_xml_node,
3571					  xml);
3572
3573	} else if (strcmp (path, "/org/freedesktop/Hal/Manager") == 0) {
3574
3575		xml = g_string_append (xml,
3576				       "  <interface name=\"org.freedesktop.Hal.Manager\">\n"
3577				       "    <method name=\"GetAllDevices\">\n"
3578				       "      <arg name=\"devices\" direction=\"out\" type=\"ao\"/>\n"
3579				       "    </method>\n"
3580				       "    <method name=\"DeviceExists\">\n"
3581				       "      <arg name=\"does_it_exist\" direction=\"out\" type=\"b\"/>\n"
3582				       "      <arg name=\"udi\" direction=\"in\" type=\"o\"/>\n"
3583				       "    </method>\n"
3584				       "    <method name=\"FindDeviceStringMatch\">\n"
3585				       "      <arg name=\"devices\" direction=\"out\" type=\"ao\"/>\n"
3586				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3587				       "      <arg name=\"value\" direction=\"in\" type=\"s\"/>\n"
3588				       "    </method>\n"
3589				       "    <method name=\"FindDeviceByCapability\">\n"
3590				       "      <arg name=\"devices\" direction=\"out\" type=\"ao\"/>\n"
3591				       "      <arg name=\"capability\" direction=\"in\" type=\"s\"/>\n"
3592				       "    </method>\n"
3593				       "    <method name=\"ClaimBranch\">\n"
3594				       "      <arg name=\"udi\" direction=\"in\" type=\"o\"/>\n"
3595				       "      <arg name=\"claim_service\" direction=\"in\" type=\"s\"/>\n"
3596				       "      <arg name=\"result\" direction=\"out\" type=\"b\"/>\n"
3597				       "    </method>\n"
3598				       "    <method name=\"UnclaimBranch\">\n"
3599				       "      <arg name=\"udi\" direction=\"in\" type=\"o\"/>\n"
3600				       "      <arg name=\"result\" direction=\"out\" type=\"b\"/>\n"
3601				       "    </method>\n"
3602				       "    <method name=\"NewDevice\">\n"
3603				       "      <arg name=\"temporary_udi\" direction=\"out\" type=\"s\"/>\n"
3604				       "    </method>\n"
3605				       "    <method name=\"Remove\">\n"
3606				       "      <arg name=\"udi\" direction=\"in\" type=\"s\"/>\n"
3607				       "    </method>\n"
3608				       "    <method name=\"CommitToGdl\">\n"
3609				       "      <arg name=\"temporary_udi\" direction=\"in\" type=\"s\"/>\n"
3610				       "      <arg name=\"global_udi\" direction=\"in\" type=\"s\"/>\n"
3611				       "    </method>\n"
3612				       "  </interface>\n");
3613	} else {
3614		HalDevice *d;
3615
3616		d = hal_device_store_find (hald_get_gdl (), path);
3617		if (d == NULL)
3618			d = hal_device_store_find (hald_get_tdl (), path);
3619
3620		if (d == NULL) {
3621			raise_no_such_device (connection, message, path);
3622			return DBUS_HANDLER_RESULT_HANDLED;
3623		}
3624
3625		xml = g_string_append (xml,
3626				       "  <interface name=\"org.freedesktop.Hal.Device\">\n"
3627				       "    <method name=\"GetAllProperties\">\n"
3628				       "      <arg name=\"properties\" direction=\"out\" type=\"a{sv}\"/>\n"
3629				       "    </method>\n"
3630				       "    <method name=\"SetMultipleProperties\">\n"
3631				       "      <arg name=\"properties\" direction=\"in\" type=\"a{sv}\"/>\n"
3632				       "    </method>\n"
3633				       "    <method name=\"GetProperty\">\n"
3634				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3635				       "      <arg name=\"value\" direction=\"out\" type=\"v\"/>\n"
3636				       "    </method>\n"
3637				       "    <method name=\"GetPropertyString\">\n"
3638				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3639				       "      <arg name=\"value\" direction=\"out\" type=\"s\"/>\n"
3640				       "    </method>\n"
3641				       "    <method name=\"GetPropertyStringList\">\n"
3642				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3643				       "      <arg name=\"value\" direction=\"out\" type=\"as\"/>\n"
3644				       "    </method>\n"
3645				       "    <method name=\"GetPropertyInteger\">\n"
3646				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3647				       "      <arg name=\"value\" direction=\"out\" type=\"i\"/>\n"
3648				       "    </method>\n"
3649				       "    <method name=\"GetPropertyBoolean\">\n"
3650				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3651				       "      <arg name=\"value\" direction=\"out\" type=\"b\"/>\n"
3652				       "    </method>\n"
3653				       "    <method name=\"GetPropertyDouble\">\n"
3654				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3655				       "      <arg name=\"value\" direction=\"out\" type=\"d\"/>\n"
3656				       "    </method>\n"
3657				       "    <method name=\"SetProperty\">\n"
3658				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3659				       "      <arg name=\"value\" direction=\"in\" type=\"v\"/>\n"
3660				       "    </method>\n"
3661				       "    <method name=\"SetPropertyString\">\n"
3662				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3663				       "      <arg name=\"value\" direction=\"in\" type=\"s\"/>\n"
3664				       "    </method>\n"
3665				       "    <method name=\"SetPropertyStringList\">\n"
3666				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3667				       "      <arg name=\"value\" direction=\"in\" type=\"as\"/>\n"
3668				       "    </method>\n"
3669				       "    <method name=\"SetPropertyInteger\">\n"
3670				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3671				       "      <arg name=\"value\" direction=\"in\" type=\"i\"/>\n"
3672				       "    </method>\n"
3673				       "    <method name=\"SetPropertyBoolean\">\n"
3674				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3675				       "      <arg name=\"value\" direction=\"in\" type=\"b\"/>\n"
3676				       "    </method>\n"
3677				       "    <method name=\"SetPropertyDouble\">\n"
3678				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3679				       "      <arg name=\"value\" direction=\"in\" type=\"d\"/>\n"
3680				       "    </method>\n"
3681
3682				       "    <method name=\"RemoveProperty\">\n"
3683				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3684				       "    </method>\n"
3685				       "    <method name=\"GetPropertyType\">\n"
3686				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3687				       "      <arg name=\"type\" direction=\"out\" type=\"i\"/>\n"
3688				       "    </method>\n"
3689				       "    <method name=\"PropertyExists\">\n"
3690				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3691				       "      <arg name=\"does_it_exist\" direction=\"out\" type=\"b\"/>\n"
3692				       "    </method>\n"
3693				       "    <method name=\"AddCapability\">\n"
3694				       "      <arg name=\"capability\" direction=\"in\" type=\"s\"/>\n"
3695				       "    </method>\n"
3696				       "    <method name=\"QueryCapability\">\n"
3697				       "      <arg name=\"capability\" direction=\"in\" type=\"s\"/>\n"
3698				       "      <arg name=\"does_it_have_capability\" direction=\"out\" type=\"b\"/>\n"
3699				       "    </method>\n"
3700				       "    <method name=\"Lock\">\n"
3701				       "      <arg name=\"reason\" direction=\"in\" type=\"s\"/>\n"
3702				       "      <arg name=\"acquired_lock\" direction=\"out\" type=\"b\"/>\n"
3703				       "    </method>\n"
3704				       "    <method name=\"Unlock\">\n"
3705				       "      <arg name=\"released_lock\" direction=\"out\" type=\"b\"/>\n"
3706				       "    </method>\n"
3707
3708				       "    <method name=\"StringListAppend\">\n"
3709				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3710				       "      <arg name=\"value\" direction=\"in\" type=\"s\"/>\n"
3711				       "    </method>\n"
3712				       "    <method name=\"StringListPrepend\">\n"
3713				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3714				       "      <arg name=\"value\" direction=\"in\" type=\"s\"/>\n"
3715				       "    </method>\n"
3716				       "    <method name=\"StringListRemove\">\n"
3717				       "      <arg name=\"key\" direction=\"in\" type=\"s\"/>\n"
3718				       "      <arg name=\"value\" direction=\"in\" type=\"s\"/>\n"
3719				       "    </method>\n"
3720				       "    <method name=\"EmitCondition\">\n"
3721				       "      <arg name=\"condition_name\" direction=\"in\" type=\"s\"/>\n"
3722				       "      <arg name=\"condition_details\" direction=\"in\" type=\"s\"/>\n"
3723				       "      <arg name=\"rc\" direction=\"out\" type=\"b\"/>\n"
3724				       "    </method>\n"
3725
3726				       "    <method name=\"Rescan\">\n"
3727				       "      <arg name=\"call_had_sideeffect\" direction=\"out\" type=\"b\"/>\n"
3728				       "    </method>\n"
3729				       "    <method name=\"Reprobe\">\n"
3730				       "      <arg name=\"call_had_sideeffect\" direction=\"out\" type=\"b\"/>\n"
3731				       "    </method>\n"
3732
3733				       "    <method name=\"ClaimInterface\">\n"
3734				       "      <arg name=\"interface_name\" direction=\"in\" type=\"s\"/>\n"
3735				       "      <arg name=\"introspection_xml\" direction=\"in\" type=\"s\"/>\n"
3736				       "      <arg name=\"rc\" direction=\"out\" type=\"b\"/>\n"
3737				       "    </method>\n"
3738
3739				       "    <method name=\"AddonIsReady\">\n"
3740				       "      <arg name=\"rc\" direction=\"out\" type=\"b\"/>\n"
3741				       "    </method>\n"
3742
3743				       "  </interface>\n");
3744
3745			GSList *interfaces;
3746			GSList *i;
3747
3748			interfaces = hal_device_property_get_strlist (d, "info.interfaces");
3749			for (i = interfaces; i != NULL; i = g_slist_next (i)) {
3750				const char *ifname = (const char *) i->data;
3751				char *method_names_prop;
3752				char *method_signatures_prop;
3753				char *method_argnames_prop;
3754				GSList *method_names;
3755				GSList *method_signatures;
3756				GSList *method_argnames;
3757				GSList *j;
3758				GSList *k;
3759				GSList *l;
3760
3761				g_string_append_printf (xml, "  <interface name=\"%s\">\n", ifname);
3762
3763				method_names_prop = g_strdup_printf ("%s.method_names", ifname);
3764				method_signatures_prop = g_strdup_printf ("%s.method_signatures", ifname);
3765				method_argnames_prop = g_strdup_printf ("%s.method_argnames", ifname);
3766
3767				method_names = hal_device_property_get_strlist (d, method_names_prop);
3768				method_signatures = hal_device_property_get_strlist (d, method_signatures_prop);
3769				method_argnames = hal_device_property_get_strlist (d, method_argnames_prop);
3770
3771				/* consult local list */
3772				if (method_names == NULL) {
3773					GSList *i;
3774
3775					for (i = helper_interface_handlers; i != NULL; i = g_slist_next (i)) {
3776						HelperInterfaceHandler *hih = i->data;
3777						if (strcmp (hih->udi, path) == 0) {
3778							xml = g_string_append (xml, hih->introspection_xml);
3779						}
3780					}
3781
3782				}
3783
3784				for (j = method_names, k = method_signatures, l = method_argnames;
3785				     j != NULL && k != NULL && l != NULL;
3786				     j = g_slist_next (j), k = g_slist_next (k), l = g_slist_next (l)) {
3787					const char *name;
3788					const char *sig;
3789					const char *argnames;
3790					char **args;
3791					unsigned int n;
3792					unsigned int m;
3793
3794					name = j->data;
3795					sig = k->data;
3796					argnames = l->data;
3797
3798					args = g_strsplit (argnames, " ", 0);
3799
3800					g_string_append_printf (xml, "    <method name=\"%s\">\n", name);
3801
3802					for (n = 0, m = 0; n < strlen (sig) && args[m] != NULL; n++, m++) {
3803						switch (sig[n]) {
3804						case 'a':
3805							if (n == strlen (sig) - 1) {
3806								HAL_WARNING (("Broken signature for method %s "
3807									      "on interface %s for object %s",
3808									      name, ifname, path));
3809								continue;
3810							}
3811							g_string_append_printf (
3812							  xml,
3813							  "      <arg name=\"%s\" direction=\"in\" type=\"a%c\"/>\n",
3814							  args[m], sig[n + 1]);
3815							n++;
3816							break;
3817
3818						default:
3819							g_string_append_printf (
3820							  xml,
3821							  "      <arg name=\"%s\" direction=\"in\" type=\"%c\"/>\n",
3822							  args[m], sig[n]);
3823							break;
3824						}
3825					}
3826					xml = g_string_append (
3827						xml,
3828						"      <arg name=\"return_code\" direction=\"out\" type=\"i\"/>\n");
3829					xml = g_string_append  (
3830						xml,
3831						"    </method>\n");
3832
3833				}
3834
3835
3836				xml = g_string_append (xml, "  </interface>\n");
3837
3838				g_free (method_names_prop);
3839				g_free (method_signatures_prop);
3840				g_free (method_argnames_prop);
3841			}
3842
3843	}
3844
3845	reply = dbus_message_new_method_return (message);
3846
3847	xml = g_string_append (xml, "</node>\n");
3848	xml_string = g_string_free (xml, FALSE);
3849
3850	dbus_message_append_args (reply,
3851				  DBUS_TYPE_STRING, &xml_string,
3852				  DBUS_TYPE_INVALID);
3853
3854	g_free (xml_string);
3855
3856	if (reply == NULL)
3857		DIE (("No memory"));
3858
3859	if (!dbus_connection_send (connection, reply, NULL))
3860		DIE (("No memory"));
3861
3862	dbus_message_unref (reply);
3863	return DBUS_HANDLER_RESULT_HANDLED;
3864}
3865
3866static void
3867reply_from_fwd_message (DBusPendingCall *pending_call,
3868			void            *user_data)
3869{
3870	DBusMessage *reply_from_addon;
3871	DBusMessage *method_from_caller;
3872	DBusMessage *reply;
3873
3874	/*HAL_INFO (("in reply_from_fwd_message : user_data = 0x%x", user_data));*/
3875
3876	method_from_caller = (DBusMessage *) user_data;
3877	reply_from_addon = dbus_pending_call_steal_reply (pending_call);
3878
3879	reply = dbus_message_copy (reply_from_addon);
3880	dbus_message_set_destination (reply, dbus_message_get_sender (method_from_caller));
3881	dbus_message_set_reply_serial (reply, dbus_message_get_serial (method_from_caller));
3882
3883	if (dbus_connection != NULL)
3884		dbus_connection_send (dbus_connection, reply, NULL);
3885
3886	dbus_message_unref (reply_from_addon);
3887	dbus_message_unref (reply);
3888	dbus_message_unref (method_from_caller);
3889	dbus_pending_call_unref (pending_call);
3890}
3891
3892static DBusHandlerResult
3893hald_dbus_filter_handle_methods (DBusConnection *connection, DBusMessage *message,
3894				 void *user_data, dbus_bool_t local_interface)
3895{
3896	/*HAL_INFO (("connection=0x%x obj_path=%s interface=%s method=%s local_interface=%d",
3897		   connection,
3898		   dbus_message_get_path (message),
3899		   dbus_message_get_interface (message),
3900		   dbus_message_get_member (message),
3901		   local_interface));*/
3902
3903	if (dbus_message_is_method_call (message,
3904					 "org.freedesktop.Hal.Manager",
3905					 "GetAllDevices") &&
3906		   strcmp (dbus_message_get_path (message),
3907			   "/org/freedesktop/Hal/Manager") == 0) {
3908		return manager_get_all_devices (connection, message);
3909	} else if (dbus_message_is_method_call (message,
3910						"org.freedesktop.Hal.Manager",
3911						"DeviceExists") &&
3912		   strcmp (dbus_message_get_path (message),
3913			   "/org/freedesktop/Hal/Manager") == 0) {
3914		return manager_device_exists (connection, message);
3915	} else if (dbus_message_is_method_call (message,
3916						"org.freedesktop.Hal.Manager",
3917						"FindDeviceStringMatch") &&
3918		   strcmp (dbus_message_get_path (message),
3919			   "/org/freedesktop/Hal/Manager") == 0) {
3920		return manager_find_device_string_match (connection,
3921							 message);
3922	} else if (dbus_message_is_method_call
3923		   (message, "org.freedesktop.Hal.Manager",
3924		    "FindDeviceByCapability")
3925		   && strcmp (dbus_message_get_path (message),
3926			      "/org/freedesktop/Hal/Manager") == 0) {
3927		return manager_find_device_by_capability (connection,
3928							  message);
3929	} else if (dbus_message_is_method_call (message,
3930						"org.freedesktop.Hal.Manager",
3931						"ClaimBranch") &&
3932		   strcmp (dbus_message_get_path (message),
3933			   "/org/freedesktop/Hal/Manager") == 0) {
3934		return manager_claim_branch (connection, message);
3935	} else if (dbus_message_is_method_call (message,
3936						"org.freedesktop.Hal.Manager",
3937						"UnclaimBranch") &&
3938		   strcmp (dbus_message_get_path (message),
3939			   "/org/freedesktop/Hal/Manager") == 0) {
3940		return manager_unclaim_branch (connection, message);
3941	} else if (dbus_message_is_method_call (message,
3942						"org.freedesktop.Hal.Manager",
3943						"NewDevice") &&
3944		   strcmp (dbus_message_get_path (message),
3945			    "/org/freedesktop/Hal/Manager") == 0) {
3946		return manager_new_device (connection, message, local_interface);
3947	} else if (dbus_message_is_method_call (message,
3948						"org.freedesktop.Hal.Manager",
3949						"Remove") &&
3950		   strcmp (dbus_message_get_path (message),
3951			    "/org/freedesktop/Hal/Manager") == 0) {
3952		return manager_remove (connection, message, local_interface);
3953	} else if (dbus_message_is_method_call (message,
3954						"org.freedesktop.Hal.Manager",
3955						"CommitToGdl") &&
3956		   strcmp (dbus_message_get_path (message),
3957			    "/org/freedesktop/Hal/Manager") == 0) {
3958		return manager_commit_to_gdl (connection, message, local_interface);
3959	} else if (dbus_message_is_method_call (message,
3960					      "org.freedesktop.Hal.Device",
3961					      "GetAllProperties")) {
3962		return device_get_all_properties (connection, message);
3963	} else if (dbus_message_is_method_call (message,
3964					      "org.freedesktop.Hal.Device",
3965					      "SetMultipleProperties")) {
3966		return device_set_multiple_properties (connection, message, local_interface);
3967	} else if (dbus_message_is_method_call (message,
3968						"org.freedesktop.Hal.Device",
3969						"GetProperty")) {
3970		return device_get_property (connection, message);
3971	} else if (dbus_message_is_method_call (message,
3972						"org.freedesktop.Hal.Device",
3973						"GetPropertyString")) {
3974		return device_get_property (connection, message);
3975	} else if (dbus_message_is_method_call (message,
3976						"org.freedesktop.Hal.Device",
3977						"GetPropertyStringList")) {
3978		return device_get_property (connection, message);
3979	} else if (dbus_message_is_method_call (message,
3980						"org.freedesktop.Hal.Device",
3981						"GetPropertyInteger")) {
3982		return device_get_property (connection, message);
3983	} else if (dbus_message_is_method_call (message,
3984						"org.freedesktop.Hal.Device",
3985						"GetPropertyBoolean")) {
3986		return device_get_property (connection, message);
3987	} else if (dbus_message_is_method_call (message,
3988						"org.freedesktop.Hal.Device",
3989						"GetPropertyDouble")) {
3990		return device_get_property (connection, message);
3991	} else if (dbus_message_is_method_call (message,
3992						"org.freedesktop.Hal.Device",
3993						"SetProperty")) {
3994		return device_set_property (connection, message, local_interface);
3995	} else if (dbus_message_is_method_call (message,
3996						"org.freedesktop.Hal.Device",
3997						"SetPropertyString")) {
3998		return device_set_property (connection, message, local_interface);
3999	} else if (dbus_message_is_method_call (message,
4000						"org.freedesktop.Hal.Device",
4001						"SetPropertyInteger")) {
4002		return device_set_property (connection, message, local_interface);
4003	} else if (dbus_message_is_method_call (message,
4004						"org.freedesktop.Hal.Device",
4005						"SetPropertyBoolean")) {
4006		return device_set_property (connection, message, local_interface);
4007	} else if (dbus_message_is_method_call (message,
4008						"org.freedesktop.Hal.Device",
4009						"SetPropertyDouble")) {
4010		return device_set_property (connection, message, local_interface);
4011	} else if (dbus_message_is_method_call (message,
4012						"org.freedesktop.Hal.Device",
4013						"RemoveProperty")) {
4014		return device_remove_property (connection, message, local_interface);
4015	} else if (dbus_message_is_method_call (message,
4016						"org.freedesktop.Hal.Device",
4017						"GetPropertyType")) {
4018		return device_get_property_type (connection, message);
4019	} else if (dbus_message_is_method_call (message,
4020						"org.freedesktop.Hal.Device",
4021						"PropertyExists")) {
4022		return device_property_exists (connection, message);
4023	} else if (dbus_message_is_method_call (message,
4024						"org.freedesktop.Hal.Device",
4025						"AddCapability")) {
4026		return device_add_capability (connection, message, local_interface);
4027	} else if (dbus_message_is_method_call (message,
4028						"org.freedesktop.Hal.Device",
4029						"QueryCapability")) {
4030		return device_query_capability (connection, message);
4031	} else if (dbus_message_is_method_call (message,
4032						"org.freedesktop.Hal.Device",
4033						"Lock")) {
4034		return device_lock (connection, message);
4035	} else if (dbus_message_is_method_call (message,
4036						"org.freedesktop.Hal.Device",
4037						"Unlock")) {
4038		return device_unlock (connection, message);
4039	} else if (dbus_message_is_method_call (message,
4040						"org.freedesktop.Hal.Device",
4041						"StringListAppend")) {
4042		return device_string_list_append_prepend (connection, message, FALSE);
4043	} else if (dbus_message_is_method_call (message,
4044						"org.freedesktop.Hal.Device",
4045						"StringListPrepend")) {
4046		return device_string_list_append_prepend (connection, message, TRUE);
4047	} else if (dbus_message_is_method_call (message,
4048						"org.freedesktop.Hal.Device",
4049						"StringListRemove")) {
4050		return device_string_list_remove (connection, message);
4051	} else if (dbus_message_is_method_call (message,
4052						"org.freedesktop.Hal.Device",
4053						"Rescan")) {
4054		return device_rescan (connection, message, local_interface);
4055	} else if (dbus_message_is_method_call (message,
4056						"org.freedesktop.Hal.Device",
4057						"Reprobe")) {
4058		return device_reprobe (connection, message, local_interface);
4059	} else if (dbus_message_is_method_call (message,
4060						"org.freedesktop.Hal.Device",
4061						"EmitCondition")) {
4062		return device_emit_condition (connection, message, local_interface);
4063	} else if (dbus_message_is_method_call (message,
4064						"org.freedesktop.Hal.Device",
4065						"ClaimInterface")) {
4066		return device_claim_interface (connection, message, local_interface);
4067#if 0
4068	} else if (dbus_message_is_method_call (message,
4069						"org.freedesktop.Hal.Device",
4070						"ReleaseInterface")) {
4071		return device_release_interface (connection, message, local_interface);
4072#endif
4073	} else if (dbus_message_is_method_call (message,
4074						"org.freedesktop.Hal.Device",
4075						"AddonIsReady")) {
4076		return addon_is_ready (connection, message, local_interface);
4077	} else if (dbus_message_is_method_call (message,
4078						"org.freedesktop.DBus.Introspectable",
4079						"Introspect")) {
4080		return do_introspect (connection, message, local_interface);
4081	} else {
4082		const char *interface;
4083		const char *udi;
4084		const char *method;
4085		const char *signature;
4086		HalDevice *d;
4087
4088		/* check for device-specific interfaces that individual objects may support */
4089
4090		udi = dbus_message_get_path (message);
4091		interface = dbus_message_get_interface (message);
4092		method = dbus_message_get_member (message);
4093		signature = dbus_message_get_signature (message);
4094
4095		d = NULL;
4096
4097		if (udi != NULL) {
4098			d = hal_device_store_find (hald_get_gdl (), udi);
4099			if (d == NULL)
4100				d = hal_device_store_find (hald_get_tdl (), udi);
4101		}
4102
4103		if (d != NULL && interface != NULL) {
4104			GSList *i;
4105
4106			for (i = helper_interface_handlers; i != NULL; i = g_slist_next (i)) {
4107				HelperInterfaceHandler *hih = i->data;
4108				if (strcmp (hih->udi, udi) == 0 &&
4109				    strcmp (hih->interface_name, interface) == 0) {
4110					DBusPendingCall *pending_call;
4111					DBusMessage *copy;
4112
4113					/*HAL_INFO (("forwarding method to connection 0x%x", hih->connection));*/
4114
4115					dbus_message_ref (message);
4116
4117					/* send a copy of the message */
4118					copy = dbus_message_copy (message);
4119					if (!dbus_connection_send_with_reply (hih->connection,
4120									      copy,
4121									      &pending_call,
4122									      /*-1*/ 8000)) {
4123						/* TODO: handle error */
4124					} else {
4125						/*HAL_INFO (("connection=%x message=%x", connection, message));*/
4126						dbus_pending_call_set_notify (pending_call,
4127									      reply_from_fwd_message,
4128									      (void *) message,
4129									      NULL);
4130					}
4131
4132					dbus_message_unref (copy);
4133
4134					return DBUS_HANDLER_RESULT_HANDLED;
4135				}
4136			}
4137		}
4138
4139		if (d != NULL && interface != NULL && method != NULL && signature != NULL) {
4140			GSList *interfaces;
4141			GSList *i;
4142
4143			interfaces = hal_device_property_get_strlist (d, "info.interfaces");
4144			for (i = interfaces; i != NULL; i = g_slist_next (i)) {
4145				const char *ifname = (const char *) i->data;
4146
4147				if (strcmp (ifname, interface) == 0) {
4148					guint num;
4149					GSList *method_names;
4150					char *s;
4151
4152					s = g_strdup_printf ("%s.method_names", interface);
4153					method_names = hal_device_property_get_strlist (d, s);
4154					g_free (s);
4155					for (i = method_names, num = 0; i != NULL; i = g_slist_next (i), num++) {
4156						const char *methodname = (const char *) i->data;
4157						if (strcmp (methodname, method) == 0) {
4158							const char *execpath;
4159							const char *sig;
4160
4161							s = g_strdup_printf ("%s.method_execpaths", interface);
4162							execpath = hal_device_property_get_strlist_elem (d, s, num);
4163							g_free (s);
4164							s = g_strdup_printf ("%s.method_signatures", interface);
4165							sig = hal_device_property_get_strlist_elem (d, s, num);
4166							g_free (s);
4167
4168							if (execpath != NULL && sig != NULL &&
4169							    strcmp (sig, signature) == 0) {
4170
4171								HAL_INFO (("OK for method '%s' with signature '%s' on interface '%s' for UDI '%s' and execpath '%s'", method, signature, interface, udi, execpath));
4172
4173								return hald_exec_method (d, connection, local_interface,
4174											 message, execpath);
4175							}
4176
4177						}
4178					}
4179				}
4180			}
4181
4182		}
4183	}
4184
4185	return osspec_filter_function (connection, message, user_data);
4186}
4187
4188
4189/** Message handler for method invocations. All invocations on any object
4190 *  or interface is routed through this function.
4191 *
4192 *  @param  connection          D-BUS connection
4193 *  @param  message             Message
4194 *  @param  user_data           User data
4195 *  @return                     What to do with the message
4196 */
4197DBusHandlerResult
4198hald_dbus_filter_function (DBusConnection * connection,
4199			   DBusMessage * message, void *user_data)
4200{
4201	if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected") &&
4202	    strcmp (dbus_message_get_path (message), DBUS_PATH_LOCAL) == 0) {
4203
4204		/* this is a local message; e.g. from libdbus in this process */
4205
4206		HAL_INFO (("Got disconnected from the system message bus; "
4207			   "retrying to reconnect every 3000 ms"));
4208		dbus_connection_unref (dbus_connection);
4209		dbus_connection = NULL;
4210
4211		g_timeout_add (3000, reinit_dbus, NULL);
4212
4213	} else if (dbus_message_is_signal (message,
4214					   DBUS_INTERFACE_DBUS,
4215					   "NameOwnerChanged")) {
4216
4217		if (services_with_locks != NULL || services_with_claims != NULL)
4218			service_deleted (message);
4219	} else
4220		return hald_dbus_filter_handle_methods (connection, message, user_data, FALSE);
4221
4222	return DBUS_HANDLER_RESULT_HANDLED;
4223}
4224
4225
4226
4227static DBusHandlerResult
4228local_server_message_handler (DBusConnection *connection,
4229			      DBusMessage *message,
4230			      void *user_data)
4231{
4232	/*HAL_INFO (("local_server_message_handler: destination=%s obj_path=%s interface=%s method=%s",
4233		   dbus_message_get_destination (message),
4234		   dbus_message_get_path (message),
4235		   dbus_message_get_interface (message),
4236		   dbus_message_get_member (message)));*/
4237
4238	if (dbus_message_is_method_call (message, "org.freedesktop.DBus", "AddMatch")) {
4239		DBusMessage *reply;
4240
4241		/* cheat, and handle AddMatch since libhal will try to invoke this method */
4242		reply = dbus_message_new_method_return (message);
4243		if (reply == NULL)
4244			DIE (("No memory"));
4245		if (!dbus_connection_send (connection, reply, NULL))
4246			DIE (("No memory"));
4247		dbus_message_unref (reply);
4248		return DBUS_HANDLER_RESULT_HANDLED;
4249	} else if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected") &&
4250		   strcmp (dbus_message_get_path (message), DBUS_PATH_LOCAL) == 0) {
4251		GSList *i;
4252		GSList *j;
4253
4254		HAL_INFO (("Client to local_server was disconnected"));
4255
4256		for (i = helper_interface_handlers; i != NULL; i = j) {
4257			HelperInterfaceHandler *hih = i->data;
4258
4259			j = g_slist_next (i);
4260
4261			if (hih->connection == connection) {
4262				g_free (hih->interface_name);
4263				g_free (hih->introspection_xml);
4264				g_free (hih->udi);
4265				g_free (hih);
4266				helper_interface_handlers = g_slist_remove_link (helper_interface_handlers, i);
4267			}
4268		}
4269
4270		dbus_connection_unref (connection);
4271		return DBUS_HANDLER_RESULT_HANDLED;
4272	} else if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_SIGNAL) {
4273		DBusMessage *copy;
4274
4275		/* it's a signal, just forward it onto the system message bus */
4276		copy = dbus_message_copy (message);
4277		if (dbus_connection != NULL) {
4278			dbus_connection_send (dbus_connection, copy, NULL);
4279		}
4280		dbus_message_unref (copy);
4281	} else {
4282		return hald_dbus_filter_handle_methods (connection, message, user_data, TRUE);
4283	}
4284
4285	return DBUS_HANDLER_RESULT_HANDLED;
4286}
4287
4288static void
4289local_server_unregister_handler (DBusConnection *connection, void *user_data)
4290{
4291	HAL_INFO (("unregistered"));
4292}
4293
4294static void
4295local_server_handle_connection (DBusServer *server,
4296			  DBusConnection *new_connection,
4297			  void *data)
4298{
4299	DBusObjectPathVTable vtable = { &local_server_unregister_handler,
4300					&local_server_message_handler,
4301					NULL, NULL, NULL, NULL};
4302
4303	HAL_INFO (("%d: Got a connection", getpid ()));
4304	HAL_INFO (("dbus_connection_get_is_connected = %d", dbus_connection_get_is_connected (new_connection)));
4305
4306	/*dbus_connection_add_filter (new_connection, server_filter_function, NULL, NULL);*/
4307
4308	dbus_connection_register_fallback (new_connection,
4309					   "/org/freedesktop",
4310					   &vtable,
4311					   NULL);
4312	dbus_connection_ref (new_connection);
4313	dbus_connection_setup_with_g_main (new_connection, NULL);
4314}
4315
4316
4317static DBusServer *local_server = NULL;
4318
4319char *
4320hald_dbus_local_server_addr (void)
4321{
4322	if (local_server == NULL)
4323		return NULL;
4324
4325	return dbus_server_get_address (local_server);
4326}
4327
4328gboolean
4329hald_dbus_local_server_init (void)
4330{
4331	gboolean ret;
4332	DBusError error;
4333	char *server_addr;
4334
4335	ret = FALSE;
4336
4337	/* setup a server listening on a socket so we can do point to point
4338	 * connections for programs spawned by hald
4339	 */
4340	dbus_error_init (&error);
4341	if ((local_server = dbus_server_listen (HALD_DBUS_ADDRESS, &error)) == NULL) {
4342		HAL_ERROR (("Cannot create D-BUS server"));
4343		goto out;
4344	}
4345	server_addr = dbus_server_get_address (local_server);
4346	HAL_INFO (("local server is listening at %s", server_addr));
4347	dbus_free (server_addr);
4348	dbus_server_setup_with_g_main (local_server, NULL);
4349	dbus_server_set_new_connection_function (local_server, local_server_handle_connection, NULL, NULL);
4350
4351	ret = TRUE;
4352
4353out:
4354	return ret;
4355}
4356
4357gboolean
4358hald_dbus_init (void)
4359{
4360	DBusError dbus_error;
4361
4362	HAL_INFO (("entering"));
4363
4364	dbus_connection_set_change_sigpipe (TRUE);
4365
4366	dbus_error_init (&dbus_error);
4367	dbus_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &dbus_error);
4368	if (dbus_connection == NULL) {
4369		HAL_ERROR (("dbus_bus_get(): %s", dbus_error.message));
4370		return FALSE;
4371	}
4372
4373	dbus_connection_setup_with_g_main (dbus_connection, NULL);
4374	dbus_connection_set_exit_on_disconnect (dbus_connection, FALSE);
4375
4376	dbus_bus_request_name (dbus_connection, "org.freedesktop.Hal",
4377				  0, &dbus_error);
4378	if (dbus_error_is_set (&dbus_error)) {
4379		HAL_ERROR (("dbus_bus_request_name(): %s",
4380			    dbus_error.message));
4381		return FALSE;
4382	}
4383
4384	dbus_connection_add_filter (dbus_connection, hald_dbus_filter_function, NULL, NULL);
4385
4386	dbus_bus_add_match (dbus_connection,
4387			    "type='signal'"
4388			    ",interface='"DBUS_INTERFACE_DBUS"'"
4389			    ",sender='"DBUS_SERVICE_DBUS"'"
4390			    ",member='NameOwnerChanged'",
4391			    NULL);
4392
4393	return TRUE;
4394}
4395
4396/** @} */
4397