1 /* 2 * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 3 * Use of this source code is governed by a BSD-style license that can be 4 * found in the LICENSE file. 5 */ 6 7 #define _GNU_SOURCE /* for RTLD_NEXT in dlfcn.h */ 8 9 #include <glib.h> 10 #include <glib-object.h> 11 #include <gudev/gudev.h> 12 13 #include <dlfcn.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 18 /* 19 * The purpose of this library is to override libgudev to return 20 * arbitrary results for selected devices, generally for the purposes 21 * of testing. Adding the library file to LD_PRELOAD is the general 22 * way to accomplish this. The arbitrary results to return are 23 * specified using environment variable GUDEV_PRELOAD. GUDEV_PRELOAD is a ':' 24 * separated list of absolute paths to file that contain device descriptions for 25 * fake devices. 26 * 27 * Device description files are standard GKeyFile's. Each device is a group. By 28 * convention, we use the device name as the group name. A device description 29 * looks so 30 * 31 * [device] 32 * name=device 33 * property_FOO=BAR 34 * 35 * property_<name> are the special GUdevDevice properties that can be obtain 36 * with a call to g_udev_get_property. 37 * The "parent" property on a device specifies a device path that will be looked 38 * up with g_udev_client_query_by_device_file() to find a parent device. This 39 * may be a real device that the real libgudev will return a device for, or it 40 * may be another fake device handled by this library. 41 * Unspecified properties/attributes will be returned as NULL. 42 * For examples, see test_files directory. 43 * 44 * Setting the environment variable FAKEGUDEV_BLOCK_REAL causes this 45 * library to prevent real devices from being iterated over with 46 * g_udev_query_by_subsystem(). 47 */ 48 49 #ifdef FAKE_G_UDEV_DEBUG 50 static const char *k_tmp_logging_file_full_path = "/tmp/fakegudev.dbg"; 51 static FILE *debug_file; 52 53 #define fake_g_udev_debug_init() \ 54 debug_file = fopen (k_tmp_logging_file_full_path, "w") 55 56 #define fake_g_udev_debug(...) \ 57 do { \ 58 if (debug_file) { \ 59 fprintf (debug_file, __VA_ARGS__); \ 60 fprintf (debug_file, "\n"); \ 61 } \ 62 } while (0) 63 64 #define fake_g_udev_debug_finish() \ 65 do { \ 66 if (debug_file) { \ 67 fclose (debug_file); \ 68 debug_file = NULL; \ 69 } \ 70 } while (0) 71 72 #else /* FAKE_G_UDEV_DEBUG */ 73 #define fake_g_udev_debug_init() 74 #define fake_g_udev_debug(...) 75 #define fake_g_udev_debug_finish() 76 #endif /* FAKE_G_UDEV_DEBUG */ 77 78 79 typedef struct _FakeGUdevDeviceClass FakeGUdevDeviceClass; 80 typedef struct _FakeGUdevDevice FakeGUdevDevice; 81 typedef struct _FakeGUdevDevicePrivate FakeGUdevDevicePrivate; 82 83 #define FAKE_G_UDEV_TYPE_DEVICE (fake_g_udev_device_get_type ()) 84 #define FAKE_G_UDEV_DEVICE(obj) \ 85 (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ 86 FAKE_G_UDEV_TYPE_DEVICE, \ 87 FakeGUdevDevice)) 88 #define FAKE_G_UDEV_IS_DEVICE(obj) \ 89 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ 90 FAKE_G_UDEV_TYPE_DEVICE)) 91 #define FAKE_G_UDEV_DEVICE_CLASS(klass) \ 92 (G_TYPE_CHECK_CLASS_CAST ((klass), \ 93 FAKE_G_UDEV_TYPE_DEVICE, \ 94 FakeGUdevDeviceClass)) 95 #define FAKE_G_UDEV_IS_DEVICE_CLASS(klass) \ 96 (G_TYPE_CHECK_CLASS_TYPE ((klass), \ 97 FAKE_G_UDEV_TYPE_DEVICE)) 98 #define FAKE_G_UDEV_DEVICE_GET_CLASS(obj) \ 99 (G_TYPE_INSTANCE_GET_CLASS ((obj), \ 100 FAKE_G_UDEV_TYPE_DEVICE, \ 101 FakeGUdevDeviceClass)) 102 103 struct _FakeGUdevDevice 104 { 105 GUdevDevice parent; 106 FakeGUdevDevicePrivate *priv; 107 }; 108 109 struct _FakeGUdevDeviceClass 110 { 111 GUdevDeviceClass parent_class; 112 }; 113 114 GType fake_g_udev_device_get_type (void) G_GNUC_CONST; 115 116 /* end header */ 117 118 struct _FakeGUdevDevicePrivate 119 { 120 GHashTable *properties; 121 GUdevClient *client; 122 const gchar **propkeys; 123 }; 124 125 G_DEFINE_TYPE (FakeGUdevDevice, fake_g_udev_device, G_UDEV_TYPE_DEVICE) 126 127 128 /* Map from device paths (/dev/pts/1) to FakeGUdevDevice objects */ 129 static GHashTable *devices_by_path; 130 131 /* Map from sysfs paths (/sys/devices/blah) to FakeGUdevDevice objects */ 132 static GHashTable *devices_by_syspath; 133 134 /* Map which acts as a set of FakeGUdevDevice objects */ 135 static GHashTable *devices_by_ptr; 136 137 /* Prevent subsystem query from listing devices */ 138 static gboolean block_real = FALSE; 139 140 static const char *k_env_devices = "FAKEGUDEV_DEVICES"; 141 static const char *k_env_block_real = "FAKEGUDEV_BLOCK_REAL"; 142 static const char *k_prop_device_file = "device_file"; 143 static const char *k_prop_devtype = "devtype"; 144 static const char *k_prop_driver = "driver"; 145 static const char *k_prop_name = "name"; 146 static const char *k_prop_parent = "parent"; 147 static const char *k_prop_subsystem = "subsystem"; 148 static const char *k_prop_sysfs_path = "sysfs_path"; 149 static const char *k_property_prefix = "property_"; 150 static const char *k_sysfs_attr_prefix = "sysfs_attr_"; 151 152 static const char *k_func_q_device_file = "g_udev_client_query_by_device_file"; 153 static const char *k_func_q_sysfs_path = "g_udev_client_query_by_sysfs_path"; 154 static const char *k_func_q_by_subsystem = "g_udev_client_query_by_subsystem"; 155 static const char *k_func_q_by_subsystem_and_name = 156 "g_udev_client_query_by_subsystem_and_name"; 157 static const char *k_func_get_device_file = "g_udev_device_get_device_file"; 158 static const char *k_func_get_devtype = "g_udev_device_get_devtype"; 159 static const char *k_func_get_driver = "g_udev_device_get_driver"; 160 static const char *k_func_get_name = "g_udev_device_get_name"; 161 static const char *k_func_get_parent = "g_udev_device_get_parent"; 162 static const char *k_func_get_property = "g_udev_device_get_property"; 163 static const char *k_func_get_property_keys = "g_udev_device_get_property_keys"; 164 static const char *k_func_get_subsystem = "g_udev_device_get_subsystem"; 165 static const char *k_func_get_sysfs_path = "g_udev_device_get_sysfs_path"; 166 static const char *k_func_get_sysfs_attr = "g_udev_device_get_sysfs_attr"; 167 168 static void 169 abort_on_error (GError *error) { 170 if (!error) 171 return; 172 173 fake_g_udev_debug ("Aborting on error: |%s|", error->message); 174 fake_g_udev_debug_finish (); 175 g_assert (0); 176 } 177 178 static void 179 load_fake_devices_from_file (const gchar *device_descriptor_file) 180 { 181 GKeyFile *key_file; 182 gchar **groups; 183 gsize num_groups, group_iter; 184 FakeGUdevDevice *fake_device; 185 GError *error = NULL; 186 187 key_file = g_key_file_new(); 188 if (!g_key_file_load_from_file (key_file, 189 device_descriptor_file, 190 G_KEY_FILE_NONE, 191 &error)) 192 abort_on_error (error); 193 194 groups = g_key_file_get_groups(key_file, &num_groups); 195 196 for (group_iter = 0; group_iter < num_groups; ++group_iter) { 197 gchar *group; 198 gchar **keys; 199 gsize num_keys, key_iter; 200 gchar *id; 201 202 group = groups[group_iter]; 203 fake_g_udev_debug ("Loading fake device %s", group); 204 205 /* Ensure some basic properties exist. */ 206 if (!g_key_file_has_key (key_file, group, k_prop_device_file, &error)) { 207 fake_g_udev_debug ("Warning: Device %s does not have a |%s|.", 208 group, k_prop_device_file); 209 if (error) { 210 g_error_free (error); 211 error = NULL; 212 } 213 } 214 if (!g_key_file_has_key (key_file, group, k_prop_sysfs_path, &error)) { 215 fake_g_udev_debug ("Warning: Device %s does not have a |%s|.", 216 group, k_prop_sysfs_path); 217 if (error) { 218 g_error_free (error); 219 error = NULL; 220 } 221 } 222 223 /* Ensure this device has not been seen before. */ 224 id = g_key_file_get_string (key_file, group, k_prop_device_file, &error); 225 abort_on_error (error); 226 if (g_hash_table_lookup_extended (devices_by_path, id, NULL, NULL)) { 227 fake_g_udev_debug ("Multiple devices with |%s| = |%s|. Skipping latest.", 228 k_prop_device_file, id); 229 g_free (id); 230 continue; 231 } 232 g_free (id); 233 234 id = g_key_file_get_string (key_file, group, k_prop_sysfs_path, &error); 235 abort_on_error (error); 236 if (g_hash_table_lookup_extended (devices_by_syspath, id, NULL, NULL)) { 237 fake_g_udev_debug ("Multiple devices with |%s| = |%s|. Skipping latest.", 238 k_prop_sysfs_path, id); 239 g_free (id); 240 continue; 241 } 242 g_free (id); 243 244 245 /* Now add the fake device with all its properties. */ 246 fake_device = FAKE_G_UDEV_DEVICE (g_object_new (FAKE_G_UDEV_TYPE_DEVICE, 247 NULL)); 248 g_hash_table_insert (devices_by_ptr, g_object_ref (fake_device), NULL); 249 250 keys = g_key_file_get_keys (key_file, group, &num_keys, &error); 251 abort_on_error (error); 252 for (key_iter = 0; key_iter < num_keys; ++key_iter) { 253 gchar *key, *value; 254 255 key = keys[key_iter]; 256 value = g_key_file_get_string (key_file, group, key, &error); 257 abort_on_error (error); 258 259 g_hash_table_insert (fake_device->priv->properties, g_strdup (key), 260 g_strdup (value)); 261 if (g_strcmp0 (key, k_prop_device_file) == 0) { 262 g_hash_table_insert (devices_by_path, 263 g_strdup (value), 264 g_object_ref (fake_device)); 265 } 266 if (g_strcmp0 (key, k_prop_sysfs_path) == 0) { 267 g_hash_table_insert (devices_by_syspath, 268 g_strdup (value), 269 g_object_ref (fake_device)); 270 } 271 272 g_free (value); 273 } 274 275 g_strfreev (keys); 276 } 277 278 g_strfreev (groups); 279 g_key_file_free (key_file); 280 } 281 282 static void 283 load_fake_devices (const gchar *device_descriptor_files) 284 { 285 gchar **files, **file_iter; 286 287 if (!device_descriptor_files) { 288 fake_g_udev_debug ("No device descriptor file given!"); 289 return; 290 } 291 292 files = g_strsplit(device_descriptor_files, ":", 0); 293 for (file_iter = files; *file_iter; ++file_iter) { 294 fake_g_udev_debug ("Reading devices from |%s|", *file_iter); 295 load_fake_devices_from_file (*file_iter); 296 } 297 298 g_strfreev (files); 299 } 300 301 /* 302 * Don't initialize the global data in this library using the library 303 * constructor. GLib may not be setup when this library is loaded. 304 */ 305 static void 306 g_udev_preload_init (void) 307 { 308 309 /* global tables */ 310 devices_by_path = g_hash_table_new_full (g_str_hash, g_str_equal, 311 g_free, g_object_unref); 312 devices_by_syspath = g_hash_table_new_full (g_str_hash, g_str_equal, 313 g_free, g_object_unref); 314 devices_by_ptr = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL); 315 316 load_fake_devices (getenv (k_env_devices)); 317 318 if (getenv (k_env_block_real)) 319 block_real = TRUE; 320 } 321 322 /* If |device| is a FakeGUdevDevice registered earlier with the libarary, cast 323 * |device| into a FakeGUdevDevice, otherwise return NULL 324 */ 325 static FakeGUdevDevice * 326 get_fake_g_udev_device (GUdevDevice *device) 327 { 328 FakeGUdevDevice *fake_device; 329 330 if (devices_by_ptr == NULL) 331 g_udev_preload_init (); 332 333 if (!FAKE_G_UDEV_IS_DEVICE (device)) 334 return NULL; 335 fake_device = FAKE_G_UDEV_DEVICE (device); 336 337 g_return_val_if_fail ( 338 g_hash_table_lookup_extended (devices_by_ptr, fake_device, NULL, NULL), 339 NULL); 340 return fake_device; 341 } 342 343 void __attribute__ ((constructor)) 344 fake_g_udev_init (void) 345 { 346 fake_g_udev_debug_init (); 347 fake_g_udev_debug ("Initialized FakeGUdev library.\n"); 348 } 349 350 void __attribute__ ((destructor)) 351 fake_g_udev_fini (void) 352 { 353 if (devices_by_path) 354 g_hash_table_unref (devices_by_path); 355 if (devices_by_syspath) 356 g_hash_table_unref (devices_by_syspath); 357 if (devices_by_ptr) 358 g_hash_table_unref (devices_by_ptr); 359 fake_g_udev_debug ("Quit FakeGUdev library.\n"); 360 fake_g_udev_debug_finish (); 361 } 362 363 GList * 364 g_udev_client_query_by_subsystem (GUdevClient *client, const gchar *subsystem) 365 { 366 static GList* (*realfunc)(); 367 GHashTableIter iter; 368 gpointer key, value; 369 GList *list, *reallist; 370 371 if (devices_by_path == NULL) 372 g_udev_preload_init (); 373 374 list = NULL; 375 g_hash_table_iter_init (&iter, devices_by_path); 376 while (g_hash_table_iter_next (&iter, &key, &value)) { 377 FakeGUdevDevice *fake_device = value; 378 const gchar *dev_subsystem = 379 (const gchar *)g_hash_table_lookup (fake_device->priv->properties, 380 k_prop_subsystem); 381 if (strcmp (subsystem, dev_subsystem) == 0) 382 list = g_list_append (list, G_UDEV_DEVICE (fake_device)); 383 } 384 385 if (!block_real) { 386 if (realfunc == NULL) 387 realfunc = (GList *(*)()) dlsym (RTLD_NEXT, k_func_q_by_subsystem); 388 reallist = realfunc (client, subsystem); 389 list = g_list_concat (list, reallist); 390 } 391 392 return list; 393 } 394 395 /* 396 * This is our hook. We look for a particular device path 397 * and return a special pointer. 398 */ 399 GUdevDevice * 400 g_udev_client_query_by_device_file (GUdevClient *client, 401 const gchar *device_file) 402 { 403 static GUdevDevice* (*realfunc)(); 404 FakeGUdevDevice *fake_device; 405 406 if (devices_by_path == NULL) 407 g_udev_preload_init (); 408 409 if (g_hash_table_lookup_extended (devices_by_path, 410 device_file, 411 NULL, 412 (gpointer *)&fake_device)) { 413 /* Stash the client pointer for later use in _get_parent() */ 414 fake_device->priv->client = client; 415 return g_object_ref (G_UDEV_DEVICE (fake_device)); 416 } 417 418 if (realfunc == NULL) 419 realfunc = (GUdevDevice *(*)()) dlsym (RTLD_NEXT, k_func_q_device_file); 420 return realfunc (client, device_file); 421 } 422 423 GUdevDevice * 424 g_udev_client_query_by_sysfs_path (GUdevClient *client, 425 const gchar *sysfs_path) 426 { 427 static GUdevDevice* (*realfunc)(); 428 FakeGUdevDevice *fake_device; 429 430 if (devices_by_path == NULL) 431 g_udev_preload_init (); 432 433 if (g_hash_table_lookup_extended (devices_by_syspath, sysfs_path, NULL, 434 (gpointer *)&fake_device)) { 435 /* Stash the client pointer for later use in _get_parent() */ 436 fake_device->priv->client = client; 437 return g_object_ref (G_UDEV_DEVICE (fake_device)); 438 } 439 440 if (realfunc == NULL) 441 realfunc = (GUdevDevice *(*)()) dlsym (RTLD_NEXT, k_func_q_sysfs_path); 442 return realfunc (client, sysfs_path); 443 } 444 445 446 GUdevDevice * 447 g_udev_client_query_by_subsystem_and_name (GUdevClient *client, 448 const gchar *subsystem, 449 const gchar *name) 450 { 451 static GUdevDevice* (*realfunc)(); 452 GHashTableIter iter; 453 gpointer key, value; 454 455 if (devices_by_path == NULL) 456 g_udev_preload_init (); 457 458 g_hash_table_iter_init (&iter, devices_by_path); 459 while (g_hash_table_iter_next (&iter, &key, &value)) { 460 FakeGUdevDevice *fake_device = value; 461 const gchar *dev_subsystem = 462 (const gchar *)g_hash_table_lookup (fake_device->priv->properties, 463 k_prop_subsystem); 464 const gchar *dev_name = 465 (const gchar *)g_hash_table_lookup (fake_device->priv->properties, 466 k_prop_name); 467 if (dev_subsystem && dev_name && 468 (strcmp (subsystem, dev_subsystem) == 0) && 469 (strcmp (name, dev_name) == 0)) { 470 fake_device->priv->client = client; 471 return g_object_ref (G_UDEV_DEVICE (fake_device)); 472 } 473 } 474 475 if (realfunc == NULL) 476 realfunc = (GUdevDevice *(*)()) dlsym (RTLD_NEXT, 477 k_func_q_by_subsystem_and_name); 478 return realfunc (client, subsystem, name); 479 } 480 481 482 /* 483 * Our device data is a glib hash table with string keys and values; 484 * the keys and values are owned by the hash table. 485 */ 486 487 /* 488 * For g_udev_device_*() functions, the general drill is to check if 489 * the device is "ours", and if not, delegate to the real library 490 * method. 491 */ 492 const gchar * 493 g_udev_device_get_device_file (GUdevDevice *device) 494 { 495 static const gchar* (*realfunc)(); 496 FakeGUdevDevice * fake_device; 497 498 fake_device = get_fake_g_udev_device (device); 499 if (fake_device) 500 return (const gchar *)g_hash_table_lookup (fake_device->priv->properties, 501 k_prop_device_file); 502 503 if (realfunc == NULL) 504 realfunc = (const gchar *(*)()) dlsym (RTLD_NEXT, k_func_get_device_file); 505 return realfunc (device); 506 } 507 508 const gchar * 509 g_udev_device_get_devtype (GUdevDevice *device) 510 { 511 static const gchar* (*realfunc)(); 512 FakeGUdevDevice * fake_device; 513 514 fake_device = get_fake_g_udev_device (device); 515 if (fake_device) 516 return (const gchar *)g_hash_table_lookup (fake_device->priv->properties, 517 k_prop_devtype); 518 519 if (realfunc == NULL) 520 realfunc = (const gchar *(*)()) dlsym (RTLD_NEXT, k_func_get_devtype); 521 return realfunc (device); 522 } 523 524 const gchar * 525 g_udev_device_get_driver (GUdevDevice *device) 526 { 527 static const gchar* (*realfunc)(); 528 FakeGUdevDevice * fake_device; 529 530 fake_device = get_fake_g_udev_device (device); 531 if (fake_device) 532 return (const gchar *)g_hash_table_lookup (fake_device->priv->properties, 533 k_prop_driver); 534 535 if (realfunc == NULL) 536 realfunc = (const gchar *(*)()) dlsym (RTLD_NEXT, k_func_get_driver); 537 return realfunc (device); 538 } 539 540 const gchar * 541 g_udev_device_get_name (GUdevDevice *device) 542 { 543 static const gchar* (*realfunc)(); 544 FakeGUdevDevice * fake_device; 545 546 fake_device = get_fake_g_udev_device (device); 547 if (fake_device) 548 return (const gchar *)g_hash_table_lookup (fake_device->priv->properties, 549 k_prop_name); 550 551 if (realfunc == NULL) 552 realfunc = (const gchar *(*)()) dlsym (RTLD_NEXT, k_func_get_name); 553 return realfunc (device); 554 } 555 556 GUdevDevice * 557 g_udev_device_get_parent (GUdevDevice *device) 558 { 559 static GUdevDevice* (*realfunc)(); 560 FakeGUdevDevice * fake_device; 561 562 fake_device = get_fake_g_udev_device (device); 563 if (fake_device) { 564 const gchar *parent = 565 (const gchar *)g_hash_table_lookup (fake_device->priv->properties, 566 k_prop_parent); 567 if (parent == NULL) 568 return NULL; 569 return g_udev_client_query_by_device_file (fake_device->priv->client, 570 parent); 571 } 572 573 if (realfunc == NULL) 574 realfunc = (GUdevDevice *(*)()) dlsym (RTLD_NEXT, k_func_get_parent); 575 return realfunc (device); 576 } 577 578 const gchar * 579 g_udev_device_get_property (GUdevDevice *device, 580 const gchar *key) 581 { 582 static const gchar* (*realfunc)(); 583 FakeGUdevDevice * fake_device; 584 585 fake_device = get_fake_g_udev_device (device); 586 if (fake_device) { 587 gchar *propkey = g_strconcat (k_property_prefix, key, NULL); 588 const gchar *result = 589 (const gchar *)g_hash_table_lookup (fake_device->priv->properties, 590 propkey); 591 g_free (propkey); 592 return result; 593 } 594 595 if (realfunc == NULL) 596 realfunc = (const gchar *(*)()) dlsym (RTLD_NEXT, k_func_get_property); 597 return realfunc (device, key); 598 } 599 600 /* 601 * All of the g_udev_device_get_property_as_SOMETYPE () functions call 602 * g_udev_device_get_property() and then operate on the result, so we 603 * don't need to implement them ourselves, as the real udev will start by 604 * calling into our version of g_udev_device_get_property(). 605 */ 606 #if 0 607 gboolean 608 g_udev_device_get_property_as_boolean (GUdevDevice *device, 609 const gchar *key); 610 gint 611 g_udev_device_get_property_as_int (GUdevDevice *device, 612 const gchar *key); 613 guint64 g_udev_device_get_property_as_uint64 (FakeGUdevDevice *device, 614 const gchar *key); 615 gdouble g_udev_device_get_property_as_double (FakeGUdevDevice *device, 616 const gchar *key); 617 618 const gchar* const *g_udev_device_get_property_as_strv (FakeGUdevDevice *device, 619 const gchar *key); 620 #endif 621 622 const gchar * const * 623 g_udev_device_get_property_keys (GUdevDevice *device) 624 { 625 static const gchar* const* (*realfunc)(); 626 FakeGUdevDevice * fake_device; 627 628 fake_device = get_fake_g_udev_device (device); 629 if (fake_device) { 630 const gchar **keys; 631 if (fake_device->priv->propkeys) 632 return fake_device->priv->propkeys; 633 634 GList *keylist = g_hash_table_get_keys (fake_device->priv->properties); 635 GList *key, *prop, *proplist = NULL; 636 guint propcount = 0; 637 for (key = keylist; key != NULL; key = key->next) { 638 if (strncmp ((char *)key->data, 639 k_property_prefix, 640 strlen (k_property_prefix)) == 0) { 641 proplist = g_list_prepend (proplist, 642 key->data + strlen (k_property_prefix)); 643 propcount++; 644 } 645 } 646 keys = g_malloc ((propcount + 1) * sizeof(*keys)); 647 keys[propcount] = NULL; 648 for (prop = proplist; prop != NULL; prop = prop->next) 649 keys[--propcount] = prop->data; 650 g_list_free (proplist); 651 fake_device->priv->propkeys = keys; 652 653 return keys; 654 } 655 656 if (realfunc == NULL) 657 realfunc = (const gchar * const*(*)()) dlsym (RTLD_NEXT, 658 k_func_get_property_keys); 659 return realfunc (device); 660 } 661 662 663 const gchar * 664 g_udev_device_get_subsystem (GUdevDevice *device) 665 { 666 static const gchar* (*realfunc)(); 667 FakeGUdevDevice * fake_device; 668 669 fake_device = get_fake_g_udev_device (device); 670 if (fake_device) 671 return (const gchar *)g_hash_table_lookup (fake_device->priv->properties, 672 k_prop_subsystem); 673 674 if (realfunc == NULL) 675 realfunc = (const gchar *(*)()) dlsym (RTLD_NEXT, k_func_get_subsystem); 676 return realfunc (device); 677 } 678 679 /* 680 * The get_sysfs_attr_as_SOMETYPE() functions are also handled magically, as are 681 * the get_property_as_SOMETYPE() functions described above. 682 */ 683 const gchar * 684 g_udev_device_get_sysfs_attr (GUdevDevice *device, const gchar *name) 685 { 686 static const gchar* (*realfunc)(); 687 FakeGUdevDevice * fake_device; 688 689 fake_device = get_fake_g_udev_device (device); 690 if (fake_device) { 691 gchar *attrkey = g_strconcat (k_sysfs_attr_prefix, name, NULL); 692 const gchar *result = 693 (const gchar *)g_hash_table_lookup (fake_device->priv->properties, 694 attrkey); 695 g_free (attrkey); 696 return result; 697 } 698 699 if (realfunc == NULL) 700 realfunc = (const gchar *(*)()) dlsym (RTLD_NEXT, k_func_get_sysfs_attr); 701 return realfunc (device, name); 702 } 703 704 705 const gchar * 706 g_udev_device_get_sysfs_path (GUdevDevice *device) 707 { 708 static const gchar* (*realfunc)(); 709 FakeGUdevDevice * fake_device; 710 711 fake_device = get_fake_g_udev_device (device); 712 if (fake_device) 713 return (const gchar *)g_hash_table_lookup (fake_device->priv->properties, 714 k_prop_sysfs_path); 715 716 if (realfunc == NULL) 717 realfunc = (const gchar *(*)()) dlsym (RTLD_NEXT, k_func_get_sysfs_path); 718 return realfunc (device); 719 } 720 721 #if 0 722 /* Not implemented yet */ 723 const gchar *g_udev_device_get_number (FakeGUdevDevice *device); 724 const gchar *g_udev_device_get_action (FakeGUdevDevice *device); 725 guint64 g_udev_device_get_seqnum (FakeGUdevDevice *device); 726 FakeGUdevDeviceType g_udev_device_get_device_type (FakeGUdevDevice *device); 727 FakeGUdevDeviceNumber g_udev_device_get_device_number (FakeGUdevDevice *device); 728 const gchar * const * 729 g_udev_device_get_device_file_symlinks (FakeGUdevDevice *device); 730 FakeGUdevDevice * 731 g_udev_device_get_parent_with_subsystem (FakeGUdevDevice *device, 732 const gchar *subsystem, 733 const gchar *devtype); 734 const gchar * const *g_udev_device_get_tags (FakeGUdevDevice *device); 735 gboolean g_udev_device_get_is_initialized (FakeGUdevDevice *device); 736 guint64 g_udev_device_get_usec_since_initialized (FakeGUdevDevice *device); 737 gboolean g_udev_device_has_property (FakeGUdevDevice *device, const gchar *key); 738 #endif 739 740 static void 741 fake_g_udev_device_init (FakeGUdevDevice *device) 742 { 743 device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device, 744 FAKE_G_UDEV_TYPE_DEVICE, 745 FakeGUdevDevicePrivate); 746 747 device->priv->properties = g_hash_table_new_full (g_str_hash, 748 g_str_equal, 749 g_free, 750 g_free); 751 device->priv->propkeys = NULL; 752 device->priv->client = NULL; 753 } 754 755 static void 756 fake_g_udev_device_finalize (GObject *object) 757 { 758 FakeGUdevDevice *device = FAKE_G_UDEV_DEVICE (object); 759 760 if (device->priv->client) 761 g_object_unref (device->priv->client); 762 g_free (device->priv->propkeys); 763 g_hash_table_unref (device->priv->properties); 764 } 765 766 static void 767 fake_g_udev_device_class_init (FakeGUdevDeviceClass *klass) 768 { 769 GObjectClass *gobject_class = (GObjectClass *) klass; 770 771 gobject_class->finalize = fake_g_udev_device_finalize; 772 773 g_type_class_add_private (klass, sizeof (FakeGUdevDevicePrivate)); 774 } 775