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 50static const char *k_tmp_logging_file_full_path = "/tmp/fakegudev.dbg"; 51static 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 79typedef struct _FakeGUdevDeviceClass FakeGUdevDeviceClass; 80typedef struct _FakeGUdevDevice FakeGUdevDevice; 81typedef 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 103struct _FakeGUdevDevice 104{ 105 GUdevDevice parent; 106 FakeGUdevDevicePrivate *priv; 107}; 108 109struct _FakeGUdevDeviceClass 110{ 111 GUdevDeviceClass parent_class; 112}; 113 114GType fake_g_udev_device_get_type (void) G_GNUC_CONST; 115 116/* end header */ 117 118struct _FakeGUdevDevicePrivate 119{ 120 GHashTable *properties; 121 GUdevClient *client; 122 const gchar **propkeys; 123}; 124 125G_DEFINE_TYPE (FakeGUdevDevice, fake_g_udev_device, G_UDEV_TYPE_DEVICE) 126 127 128/* Map from device paths (/dev/pts/1) to FakeGUdevDevice objects */ 129static GHashTable *devices_by_path; 130 131/* Map from sysfs paths (/sys/devices/blah) to FakeGUdevDevice objects */ 132static GHashTable *devices_by_syspath; 133 134/* Map which acts as a set of FakeGUdevDevice objects */ 135static GHashTable *devices_by_ptr; 136 137/* Prevent subsystem query from listing devices */ 138static gboolean block_real = FALSE; 139 140static const char *k_env_devices = "FAKEGUDEV_DEVICES"; 141static const char *k_env_block_real = "FAKEGUDEV_BLOCK_REAL"; 142static const char *k_prop_device_file = "device_file"; 143static const char *k_prop_devtype = "devtype"; 144static const char *k_prop_driver = "driver"; 145static const char *k_prop_name = "name"; 146static const char *k_prop_parent = "parent"; 147static const char *k_prop_subsystem = "subsystem"; 148static const char *k_prop_sysfs_path = "sysfs_path"; 149static const char *k_property_prefix = "property_"; 150static const char *k_sysfs_attr_prefix = "sysfs_attr_"; 151 152static const char *k_func_q_device_file = "g_udev_client_query_by_device_file"; 153static const char *k_func_q_sysfs_path = "g_udev_client_query_by_sysfs_path"; 154static const char *k_func_q_by_subsystem = "g_udev_client_query_by_subsystem"; 155static const char *k_func_q_by_subsystem_and_name = 156 "g_udev_client_query_by_subsystem_and_name"; 157static const char *k_func_get_device_file = "g_udev_device_get_device_file"; 158static const char *k_func_get_devtype = "g_udev_device_get_devtype"; 159static const char *k_func_get_driver = "g_udev_device_get_driver"; 160static const char *k_func_get_name = "g_udev_device_get_name"; 161static const char *k_func_get_parent = "g_udev_device_get_parent"; 162static const char *k_func_get_property = "g_udev_device_get_property"; 163static const char *k_func_get_property_keys = "g_udev_device_get_property_keys"; 164static const char *k_func_get_subsystem = "g_udev_device_get_subsystem"; 165static const char *k_func_get_sysfs_path = "g_udev_device_get_sysfs_path"; 166static const char *k_func_get_sysfs_attr = "g_udev_device_get_sysfs_attr"; 167 168static void 169abort_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 178static void 179load_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 282static void 283load_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 */ 305static void 306g_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 */ 325static FakeGUdevDevice * 326get_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 343void __attribute__ ((constructor)) 344fake_g_udev_init (void) 345{ 346 fake_g_udev_debug_init (); 347 fake_g_udev_debug ("Initialized FakeGUdev library.\n"); 348} 349 350void __attribute__ ((destructor)) 351fake_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 363GList * 364g_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 */ 399GUdevDevice * 400g_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 423GUdevDevice * 424g_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 446GUdevDevice * 447g_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 */ 492const gchar * 493g_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 508const gchar * 509g_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 524const gchar * 525g_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 540const gchar * 541g_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 556GUdevDevice * 557g_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 578const gchar * 579g_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 607gboolean 608g_udev_device_get_property_as_boolean (GUdevDevice *device, 609 const gchar *key); 610gint 611g_udev_device_get_property_as_int (GUdevDevice *device, 612 const gchar *key); 613guint64 g_udev_device_get_property_as_uint64 (FakeGUdevDevice *device, 614 const gchar *key); 615gdouble g_udev_device_get_property_as_double (FakeGUdevDevice *device, 616 const gchar *key); 617 618const gchar* const *g_udev_device_get_property_as_strv (FakeGUdevDevice *device, 619 const gchar *key); 620#endif 621 622const gchar * const * 623g_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 663const gchar * 664g_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 */ 683const gchar * 684g_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 705const gchar * 706g_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 */ 723const gchar *g_udev_device_get_number (FakeGUdevDevice *device); 724const gchar *g_udev_device_get_action (FakeGUdevDevice *device); 725guint64 g_udev_device_get_seqnum (FakeGUdevDevice *device); 726FakeGUdevDeviceType g_udev_device_get_device_type (FakeGUdevDevice *device); 727FakeGUdevDeviceNumber g_udev_device_get_device_number (FakeGUdevDevice *device); 728const gchar * const * 729g_udev_device_get_device_file_symlinks (FakeGUdevDevice *device); 730FakeGUdevDevice * 731g_udev_device_get_parent_with_subsystem (FakeGUdevDevice *device, 732 const gchar *subsystem, 733 const gchar *devtype); 734const gchar * const *g_udev_device_get_tags (FakeGUdevDevice *device); 735gboolean g_udev_device_get_is_initialized (FakeGUdevDevice *device); 736guint64 g_udev_device_get_usec_since_initialized (FakeGUdevDevice *device); 737gboolean g_udev_device_has_property (FakeGUdevDevice *device, const gchar *key); 738#endif 739 740static void 741fake_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 755static void 756fake_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 766static void 767fake_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