device_store.c revision 2912:85ea316d9c18
1/***************************************************************************
2 * CVSID: $Id$
3 *
4 * device_store.c : HalDeviceStore methods
5 *
6 * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
7 * Copyright (C) 2004 Novell, Inc.
8 *
9 * Licensed under the Academic Free License version 2.1
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
24 *
25 **************************************************************************/
26
27#ifdef HAVE_CONFIG_H
28#  include <config.h>
29#endif
30
31#include <stdio.h>
32#include <string.h>
33
34#include "device_store.h"
35#include "hald_marshal.h"
36#include "logger.h"
37
38static GObjectClass *parent_class;
39
40enum {
41	STORE_CHANGED,
42	DEVICE_PROPERTY_CHANGED,
43	DEVICE_CAPABILITY_ADDED,
44	LAST_SIGNAL
45};
46
47static guint signals[LAST_SIGNAL] = { 0 };
48
49static void
50hal_device_store_finalize (GObject *obj)
51{
52	HalDeviceStore *store = HAL_DEVICE_STORE (obj);
53
54	g_slist_foreach (store->devices, (GFunc) g_object_unref, NULL);
55
56	if (parent_class->finalize)
57		parent_class->finalize (obj);
58}
59
60static void
61hal_device_store_class_init (HalDeviceStoreClass *klass)
62{
63	GObjectClass *obj_class = (GObjectClass *) klass;
64
65	parent_class = g_type_class_peek_parent (klass);
66
67	obj_class->finalize = hal_device_store_finalize;
68
69	signals[STORE_CHANGED] =
70		g_signal_new ("store_changed",
71			      G_TYPE_FROM_CLASS (klass),
72			      G_SIGNAL_RUN_LAST,
73			      G_STRUCT_OFFSET (HalDeviceStoreClass,
74					       store_changed),
75			      NULL, NULL,
76			      hald_marshal_VOID__OBJECT_BOOL,
77			      G_TYPE_NONE, 2,
78			      G_TYPE_OBJECT,
79			      G_TYPE_BOOLEAN);
80
81	signals[DEVICE_PROPERTY_CHANGED] =
82		g_signal_new ("device_property_changed",
83			      G_TYPE_FROM_CLASS (klass),
84			      G_SIGNAL_RUN_LAST,
85			      G_STRUCT_OFFSET (HalDeviceStoreClass,
86					       device_property_changed),
87			      NULL, NULL,
88			      hald_marshal_VOID__OBJECT_STRING_BOOL_BOOL,
89			      G_TYPE_NONE, 4,
90			      G_TYPE_OBJECT,
91			      G_TYPE_STRING,
92			      G_TYPE_BOOLEAN,
93			      G_TYPE_BOOLEAN);
94
95	signals[DEVICE_CAPABILITY_ADDED] =
96		g_signal_new ("device_capability_added",
97			      G_TYPE_FROM_CLASS (klass),
98			      G_SIGNAL_RUN_LAST,
99			      G_STRUCT_OFFSET (HalDeviceStoreClass,
100					       device_capability_added),
101			      NULL, NULL,
102			      hald_marshal_VOID__OBJECT_STRING,
103			      G_TYPE_NONE, 2,
104			      G_TYPE_OBJECT,
105			      G_TYPE_STRING);
106}
107
108static void
109hal_device_store_init (HalDeviceStore *device)
110{
111}
112
113GType
114hal_device_store_get_type (void)
115{
116	static GType type = 0;
117
118	if (!type) {
119		static GTypeInfo type_info = {
120			sizeof (HalDeviceStoreClass),
121			NULL, NULL,
122			(GClassInitFunc) hal_device_store_class_init,
123			NULL, NULL,
124			sizeof (HalDeviceStore),
125			0,
126			(GInstanceInitFunc) hal_device_store_init
127		};
128
129		type = g_type_register_static (G_TYPE_OBJECT,
130					       "HalDeviceStore",
131					       &type_info,
132					       0);
133	}
134
135	return type;
136}
137
138HalDeviceStore *
139hal_device_store_new (void)
140{
141	HalDeviceStore *store;
142
143	store = g_object_new (HAL_TYPE_DEVICE_STORE, NULL, NULL);
144
145	return store;
146}
147
148static void
149emit_device_property_changed (HalDevice *device,
150			      const char *key,
151			      gboolean added,
152			      gboolean removed,
153			      gpointer data)
154{
155	HalDeviceStore *store = HAL_DEVICE_STORE (data);
156
157	g_signal_emit (store, signals[DEVICE_PROPERTY_CHANGED], 0,
158		       device, key, added, removed);
159}
160
161static void
162emit_device_capability_added (HalDevice *device,
163			      const char *capability,
164			      gpointer data)
165{
166	HalDeviceStore *store = HAL_DEVICE_STORE (data);
167
168	g_signal_emit (store, signals[DEVICE_CAPABILITY_ADDED], 0,
169		       device, capability);
170}
171
172void
173hal_device_store_add (HalDeviceStore *store, HalDevice *device)
174{
175	const char buf[] = "/org/freedesktop/Hal/devices/";
176
177	if (strncmp(device->udi, buf, sizeof (buf) - 1) != 0) {
178
179		HAL_ERROR(("Can't add HalDevice with incorrect UDI. Valid "
180			   "UDI must start with '/org/freedesktop/Hal/devices/'"));
181		goto out;
182	}
183	store->devices = g_slist_prepend (store->devices,
184					  g_object_ref (device));
185
186	g_signal_connect (device, "property_changed",
187			  G_CALLBACK (emit_device_property_changed), store);
188	g_signal_connect (device, "capability_added",
189			  G_CALLBACK (emit_device_capability_added), store);
190
191	g_signal_emit (store, signals[STORE_CHANGED], 0, device, TRUE);
192
193out:
194	;
195}
196
197gboolean
198hal_device_store_remove (HalDeviceStore *store, HalDevice *device)
199{
200	if (!g_slist_find (store->devices, device))
201		return FALSE;
202
203	store->devices = g_slist_remove (store->devices, device);
204
205	g_signal_handlers_disconnect_by_func (device,
206					      (gpointer)emit_device_property_changed,
207					      store);
208	g_signal_handlers_disconnect_by_func (device,
209					      (gpointer)emit_device_capability_added,
210					      store);
211
212	g_signal_emit (store, signals[STORE_CHANGED], 0, device, FALSE);
213
214	g_object_unref (device);
215
216	return TRUE;
217}
218
219HalDevice *
220hal_device_store_find (HalDeviceStore *store, const char *udi)
221{
222	GSList *iter;
223
224	for (iter = store->devices; iter != NULL; iter = iter->next) {
225		HalDevice *d = iter->data;
226
227		if (strcmp (hal_device_get_udi (d), udi) == 0)
228			return d;
229	}
230
231	return NULL;
232}
233
234void
235hal_device_store_foreach (HalDeviceStore *store,
236			  HalDeviceStoreForeachFn callback,
237			  gpointer user_data)
238{
239	GSList *iter;
240
241	g_return_if_fail (store != NULL);
242	g_return_if_fail (callback != NULL);
243
244	for (iter = store->devices; iter != NULL; iter = iter->next) {
245		HalDevice *d = HAL_DEVICE (iter->data);
246		gboolean cont;
247
248		cont = callback (store, d, user_data);
249
250		if (cont == FALSE)
251			return;
252	}
253}
254
255static gboolean
256hal_device_store_print_foreach_fn (HalDeviceStore *store,
257				   HalDevice *device,
258				   gpointer user_data)
259{
260	fprintf (stderr, "----\n");
261	hal_device_print (device);
262	fprintf (stderr, "----\n");
263	return TRUE;
264}
265
266void
267hal_device_store_print (HalDeviceStore *store)
268{
269	fprintf (stderr, "===============================================\n");
270        fprintf (stderr, "Dumping %d devices\n",
271		 g_slist_length (store->devices));
272	fprintf (stderr, "===============================================\n");
273	hal_device_store_foreach (store,
274				  hal_device_store_print_foreach_fn,
275				  NULL);
276	fprintf (stderr, "===============================================\n");
277}
278
279HalDevice *
280hal_device_store_match_key_value_string (HalDeviceStore *store,
281					 const char *key,
282					 const char *value)
283{
284	GSList *iter;
285
286	g_return_val_if_fail (store != NULL, NULL);
287	g_return_val_if_fail (key != NULL, NULL);
288	g_return_val_if_fail (value != NULL, NULL);
289
290	for (iter = store->devices; iter != NULL; iter = iter->next) {
291		HalDevice *d = HAL_DEVICE (iter->data);
292		int type;
293
294		if (!hal_device_has_property (d, key))
295			continue;
296
297		type = hal_device_property_get_type (d, key);
298		if (type != HAL_PROPERTY_TYPE_STRING)
299			continue;
300
301		if (strcmp (hal_device_property_get_string (d, key),
302			    value) == 0)
303			return d;
304	}
305
306	return NULL;
307}
308
309HalDevice *
310hal_device_store_match_key_value_int (HalDeviceStore *store,
311				      const char *key,
312				      int value)
313{
314	GSList *iter;
315
316	g_return_val_if_fail (store != NULL, NULL);
317	g_return_val_if_fail (key != NULL, NULL);
318
319	for (iter = store->devices; iter != NULL; iter = iter->next) {
320		HalDevice *d = HAL_DEVICE (iter->data);
321		int type;
322
323		if (!hal_device_has_property (d, key))
324			continue;
325
326		type = hal_device_property_get_type (d, key);
327		if (type != HAL_PROPERTY_TYPE_INT32)
328			continue;
329
330		if (hal_device_property_get_int (d, key) == value)
331			return d;
332	}
333
334	return NULL;
335}
336
337GSList *
338hal_device_store_match_multiple_key_value_string (HalDeviceStore *store,
339						  const char *key,
340						  const char *value)
341{
342	GSList *iter;
343	GSList *matches = NULL;
344
345	g_return_val_if_fail (store != NULL, NULL);
346	g_return_val_if_fail (key != NULL, NULL);
347	g_return_val_if_fail (value != NULL, NULL);
348
349	for (iter = store->devices; iter != NULL; iter = iter->next) {
350		HalDevice *d = HAL_DEVICE (iter->data);
351		int type;
352
353		if (!hal_device_has_property (d, key))
354			continue;
355
356		type = hal_device_property_get_type (d, key);
357		if (type != HAL_PROPERTY_TYPE_STRING)
358			continue;
359
360		if (strcmp (hal_device_property_get_string (d, key),
361			    value) == 0)
362			matches = g_slist_prepend (matches, d);
363	}
364
365	return matches;
366}
367
368typedef struct {
369	HalDeviceStore *store;
370	char *key;
371	char *value;
372	HalDeviceStoreAsyncCallback callback;
373	gpointer user_data;
374
375	guint prop_signal_id;
376	guint store_signal_id;
377	guint timeout_id;
378} AsyncMatchInfo;
379
380static void
381destroy_async_match_info (AsyncMatchInfo *info)
382{
383	g_object_unref (info->store);
384
385	g_free (info->key);
386	g_free (info->value);
387
388	g_signal_handler_disconnect (info->store, info->prop_signal_id);
389	g_signal_handler_disconnect (info->store, info->store_signal_id);
390	g_source_remove (info->timeout_id);
391
392	g_free (info);
393}
394
395static void
396match_device_async (HalDeviceStore *store, HalDevice *device,
397		    const char *key, gboolean removed, gboolean added,
398		    gpointer user_data)
399{
400	AsyncMatchInfo *info = (AsyncMatchInfo *) user_data;
401
402	/* Only want to do it for added or changed properties */
403	if (removed)
404		return;
405
406	/* Keys have to match */
407	if (strcmp (info->key, key) != 0)
408		return;
409
410	/* Values have to match */
411	if (strcmp (hal_device_property_get_string (device, key),
412		    info->value) != 0)
413		return;
414
415	info->callback (store, device, info->user_data);
416
417	destroy_async_match_info (info);
418}
419
420static void
421store_changed (HalDeviceStore *store, HalDevice *device,
422	       gboolean added, gpointer user_data)
423{
424	AsyncMatchInfo *info = (AsyncMatchInfo *) user_data;
425
426	if (!added)
427		return;
428
429	if (!hal_device_has_property (device, info->key))
430		return;
431
432	if (strcmp (hal_device_property_get_string (device, info->key),
433		    info->value) != 0)
434		return;
435
436	info->callback (store, device, info->user_data);
437
438	destroy_async_match_info (info);
439}
440
441static gboolean
442match_device_async_timeout (gpointer user_data)
443{
444	AsyncMatchInfo *info = (AsyncMatchInfo *) user_data;
445
446	info->callback (info->store, NULL, info->user_data);
447
448	destroy_async_match_info (info);
449
450	return FALSE;
451}
452
453void
454hal_device_store_match_key_value_string_async (HalDeviceStore *store,
455					       const char *key,
456					       const char *value,
457					       HalDeviceStoreAsyncCallback callback,
458					       gpointer user_data,
459					       int timeout)
460{
461	HalDevice *device;
462	AsyncMatchInfo *info;
463
464	/* First check to see if it's already there */
465	device = hal_device_store_match_key_value_string (store, key, value);
466
467	if (device != NULL || timeout == 0) {
468		callback (store, device, user_data);
469
470		return;
471	}
472
473	info = g_new0 (AsyncMatchInfo, 1);
474
475	info->store = g_object_ref (store);
476	info->key = g_strdup (key);
477	info->value = g_strdup (value);
478	info->callback = callback;
479	info->user_data = user_data;
480
481	info->prop_signal_id = g_signal_connect (store,
482						 "device_property_changed",
483						 G_CALLBACK (match_device_async),
484						 info);
485	info->store_signal_id = g_signal_connect (store,
486						  "store_changed",
487						  G_CALLBACK (store_changed),
488						  info);
489
490	info->timeout_id = g_timeout_add (timeout,
491					  match_device_async_timeout,
492					  info);
493}
494