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
38 static GObjectClass *parent_class;
39
40 enum {
41 STORE_CHANGED,
42 DEVICE_PROPERTY_CHANGED,
43 DEVICE_CAPABILITY_ADDED,
44 LAST_SIGNAL
45 };
46
47 static guint signals[LAST_SIGNAL] = { 0 };
48
49 static void
hal_device_store_finalize(GObject * obj)50 hal_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
60 static void
hal_device_store_class_init(HalDeviceStoreClass * klass)61 hal_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_BOOLEAN,
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_BOOLEAN_BOOLEAN,
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
108 static void
hal_device_store_init(HalDeviceStore * device)109 hal_device_store_init (HalDeviceStore *device)
110 {
111 }
112
113 GType
hal_device_store_get_type(void)114 hal_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
138 HalDeviceStore *
hal_device_store_new(void)139 hal_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
148 static void
emit_device_property_changed(HalDevice * device,const char * key,gboolean added,gboolean removed,gpointer data)149 emit_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
161 static void
emit_device_capability_added(HalDevice * device,const char * capability,gpointer data)162 emit_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
172 void
hal_device_store_add(HalDeviceStore * store,HalDevice * device)173 hal_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
193 out:
194 ;
195 }
196
197 gboolean
hal_device_store_remove(HalDeviceStore * store,HalDevice * device)198 hal_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
219 HalDevice *
hal_device_store_find(HalDeviceStore * store,const char * udi)220 hal_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
234 void
hal_device_store_foreach(HalDeviceStore * store,HalDeviceStoreForeachFn callback,gpointer user_data)235 hal_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
255 static gboolean
hal_device_store_print_foreach_fn(HalDeviceStore * store,HalDevice * device,gpointer user_data)256 hal_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
266 void
hal_device_store_print(HalDeviceStore * store)267 hal_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
279 HalDevice *
hal_device_store_match_key_value_string(HalDeviceStore * store,const char * key,const char * value)280 hal_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
309 HalDevice *
hal_device_store_match_key_value_int(HalDeviceStore * store,const char * key,int value)310 hal_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
337 GSList *
hal_device_store_match_multiple_key_value_string(HalDeviceStore * store,const char * key,const char * value)338 hal_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
368 typedef 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
380 static void
destroy_async_match_info(AsyncMatchInfo * info)381 destroy_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
395 static void
match_device_async(HalDeviceStore * store,HalDevice * device,const char * key,gboolean removed,gboolean added,gpointer user_data)396 match_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
420 static void
store_changed(HalDeviceStore * store,HalDevice * device,gboolean added,gpointer user_data)421 store_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
441 static gboolean
match_device_async_timeout(gpointer user_data)442 match_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
453 void
hal_device_store_match_key_value_string_async(HalDeviceStore * store,const char * key,const char * value,HalDeviceStoreAsyncCallback callback,gpointer user_data,int timeout)454 hal_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