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 }