Home | History | Annotate | Download | only in src
      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