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
abort_on_error(GError * error)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
load_fake_devices_from_file(const gchar * device_descriptor_file)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
load_fake_devices(const gchar * device_descriptor_files)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
g_udev_preload_init(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 *
get_fake_g_udev_device(GUdevDevice * device)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))
fake_g_udev_init(void)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))
fake_g_udev_fini(void)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 *
g_udev_client_query_by_subsystem(GUdevClient * client,const gchar * subsystem)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 *
g_udev_client_query_by_device_file(GUdevClient * client,const gchar * device_file)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 *
g_udev_client_query_by_sysfs_path(GUdevClient * client,const gchar * sysfs_path)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 *
g_udev_client_query_by_subsystem_and_name(GUdevClient * client,const gchar * subsystem,const gchar * name)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 *
g_udev_device_get_device_file(GUdevDevice * device)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 *
g_udev_device_get_devtype(GUdevDevice * device)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 *
g_udev_device_get_driver(GUdevDevice * device)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 *
g_udev_device_get_name(GUdevDevice * device)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 *
g_udev_device_get_parent(GUdevDevice * device)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 *
g_udev_device_get_property(GUdevDevice * device,const gchar * key)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 *
g_udev_device_get_property_keys(GUdevDevice * device)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 *
g_udev_device_get_subsystem(GUdevDevice * device)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 *
g_udev_device_get_sysfs_attr(GUdevDevice * device,const gchar * name)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 *
g_udev_device_get_sysfs_path(GUdevDevice * device)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
fake_g_udev_device_init(FakeGUdevDevice * device)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
fake_g_udev_device_finalize(GObject * object)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
fake_g_udev_device_class_init(FakeGUdevDeviceClass * klass)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