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