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 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 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 109 hal_device_store_init (HalDeviceStore *device) 110 { 111 } 112 113 GType 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 * 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 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 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 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 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 * 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 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 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 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 * 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 * 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 * 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 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 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 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 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 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