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_WITH_CODE (FakeGUdevDevice, fake_g_udev_device,
126 G_UDEV_TYPE_DEVICE, G_ADD_PRIVATE (FakeGUdevDevice))
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 = fake_g_udev_device_get_instance_private(device);
744
745 device->priv->properties = g_hash_table_new_full (g_str_hash,
746 g_str_equal,
747 g_free,
748 g_free);
749 device->priv->propkeys = NULL;
750 device->priv->client = NULL;
751 }
752
753 static void
fake_g_udev_device_finalize(GObject * object)754 fake_g_udev_device_finalize (GObject *object)
755 {
756 FakeGUdevDevice *device = FAKE_G_UDEV_DEVICE (object);
757
758 if (device->priv->client)
759 g_object_unref (device->priv->client);
760 g_free (device->priv->propkeys);
761 g_hash_table_unref (device->priv->properties);
762 }
763
764 static void
fake_g_udev_device_class_init(FakeGUdevDeviceClass * klass)765 fake_g_udev_device_class_init (FakeGUdevDeviceClass *klass)
766 {
767 GObjectClass *gobject_class = (GObjectClass *) klass;
768
769 gobject_class->finalize = fake_g_udev_device_finalize;
770 }
771