1 /* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 *
3 * Copyright (C) 2008-2010 David Zeuthen <davidz@redhat.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/stat.h>
27
28 #include "gudevclient.h"
29 #include "gudevdevice.h"
30 #include "gudevprivate.h"
31
32 /**
33 * SECTION:gudevclient
34 * @short_description: Query devices and listen to uevents
35 *
36 * #GUdevClient is used to query information about devices on a Linux
37 * system from the Linux kernel and the udev device
38 * manager.
39 *
40 * Device information is retrieved from the kernel (through the
41 * <literal>sysfs</literal> filesystem) and the udev daemon (through a
42 * <literal>tmpfs</literal> filesystem) and presented through
43 * #GUdevDevice objects. This means that no blocking IO ever happens
44 * (in both cases, we are essentially just reading data from kernel
45 * memory) and as such there are no asynchronous versions of the
46 * provided methods.
47 *
48 * To get #GUdevDevice objects, use
49 * g_udev_client_query_by_subsystem(),
50 * g_udev_client_query_by_device_number(),
51 * g_udev_client_query_by_device_file(),
52 * g_udev_client_query_by_sysfs_path(),
53 * g_udev_client_query_by_subsystem_and_name()
54 * or the #GUdevEnumerator type.
55 *
56 * To listen to uevents, connect to the #GUdevClient::uevent signal.
57 */
58
59 struct _GUdevClientPrivate
60 {
61 GSource *watch_source;
62 struct udev *udev;
63 struct udev_monitor *monitor;
64
65 gchar **subsystems;
66 };
67
68 enum
69 {
70 PROP_0,
71 PROP_SUBSYSTEMS,
72 };
73
74 enum
75 {
76 UEVENT_SIGNAL,
77 LAST_SIGNAL,
78 };
79
80 static guint signals[LAST_SIGNAL] = { 0 };
81
G_DEFINE_TYPE_WITH_CODE(GUdevClient,g_udev_client,G_TYPE_OBJECT,G_ADD_PRIVATE (GUdevClient))82 G_DEFINE_TYPE_WITH_CODE (GUdevClient, g_udev_client, G_TYPE_OBJECT, G_ADD_PRIVATE(GUdevClient))
83
84 /* ---------------------------------------------------------------------------------------------------- */
85
86 static gboolean
87 monitor_event (GIOChannel *source,
88 GIOCondition condition,
89 gpointer data)
90 {
91 GUdevClient *client = (GUdevClient *) data;
92 GUdevDevice *device;
93 struct udev_device *udevice;
94
95 if (client->priv->monitor == NULL)
96 goto out;
97 udevice = udev_monitor_receive_device (client->priv->monitor);
98 if (udevice == NULL)
99 goto out;
100
101 device = _g_udev_device_new (udevice);
102 udev_device_unref (udevice);
103 g_signal_emit (client,
104 signals[UEVENT_SIGNAL],
105 0,
106 g_udev_device_get_action (device),
107 device);
108 g_object_unref (device);
109
110 out:
111 return TRUE;
112 }
113
114 static void
g_udev_client_finalize(GObject * object)115 g_udev_client_finalize (GObject *object)
116 {
117 GUdevClient *client = G_UDEV_CLIENT (object);
118
119 if (client->priv->watch_source != NULL)
120 {
121 g_source_destroy (client->priv->watch_source);
122 client->priv->watch_source = NULL;
123 }
124
125 if (client->priv->monitor != NULL)
126 {
127 udev_monitor_unref (client->priv->monitor);
128 client->priv->monitor = NULL;
129 }
130
131 if (client->priv->udev != NULL)
132 {
133 udev_unref (client->priv->udev);
134 client->priv->udev = NULL;
135 }
136
137 g_strfreev (client->priv->subsystems);
138
139 if (G_OBJECT_CLASS (g_udev_client_parent_class)->finalize != NULL)
140 G_OBJECT_CLASS (g_udev_client_parent_class)->finalize (object);
141 }
142
143 static void
g_udev_client_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)144 g_udev_client_set_property (GObject *object,
145 guint prop_id,
146 const GValue *value,
147 GParamSpec *pspec)
148 {
149 GUdevClient *client = G_UDEV_CLIENT (object);
150
151 switch (prop_id)
152 {
153 case PROP_SUBSYSTEMS:
154 if (client->priv->subsystems != NULL)
155 g_strfreev (client->priv->subsystems);
156 client->priv->subsystems = g_strdupv (g_value_get_boxed (value));
157 break;
158
159 default:
160 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
161 break;
162 }
163 }
164
165 static void
g_udev_client_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)166 g_udev_client_get_property (GObject *object,
167 guint prop_id,
168 GValue *value,
169 GParamSpec *pspec)
170 {
171 GUdevClient *client = G_UDEV_CLIENT (object);
172
173 switch (prop_id)
174 {
175 case PROP_SUBSYSTEMS:
176 g_value_set_boxed (value, client->priv->subsystems);
177 break;
178
179 default:
180 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
181 break;
182 }
183 }
184
185 static void
g_udev_client_constructed(GObject * object)186 g_udev_client_constructed (GObject *object)
187 {
188 GUdevClient *client = G_UDEV_CLIENT (object);
189 GIOChannel *channel;
190 guint n;
191
192 client->priv->udev = udev_new ();
193
194 /* connect to event source */
195 client->priv->monitor = udev_monitor_new_from_netlink (client->priv->udev, "udev");
196
197 //g_debug ("ss = %p", client->priv->subsystems);
198
199 if (client->priv->subsystems != NULL)
200 {
201 /* install subsystem filters to only wake up for certain events */
202 for (n = 0; client->priv->subsystems[n] != NULL; n++)
203 {
204 gchar *subsystem;
205 gchar *devtype;
206 gchar *s;
207
208 subsystem = g_strdup (client->priv->subsystems[n]);
209 devtype = NULL;
210
211 //g_debug ("s = '%s'", subsystem);
212
213 s = strstr (subsystem, "/");
214 if (s != NULL)
215 {
216 devtype = s + 1;
217 *s = '\0';
218 }
219
220 if (client->priv->monitor != NULL)
221 udev_monitor_filter_add_match_subsystem_devtype (client->priv->monitor, subsystem, devtype);
222
223 g_free (subsystem);
224 }
225
226 /* listen to events, and buffer them */
227 if (client->priv->monitor != NULL)
228 {
229 udev_monitor_enable_receiving (client->priv->monitor);
230 channel = g_io_channel_unix_new (udev_monitor_get_fd (client->priv->monitor));
231 client->priv->watch_source = g_io_create_watch (channel, G_IO_IN);
232 g_io_channel_unref (channel);
233 g_source_set_callback (client->priv->watch_source, (GSourceFunc) monitor_event, client, NULL);
234 g_source_attach (client->priv->watch_source, g_main_context_get_thread_default ());
235 g_source_unref (client->priv->watch_source);
236 }
237 else
238 {
239 client->priv->watch_source = NULL;
240 }
241 }
242
243 if (G_OBJECT_CLASS (g_udev_client_parent_class)->constructed != NULL)
244 G_OBJECT_CLASS (g_udev_client_parent_class)->constructed (object);
245 }
246
247
248 static void
g_udev_client_class_init(GUdevClientClass * klass)249 g_udev_client_class_init (GUdevClientClass *klass)
250 {
251 GObjectClass *gobject_class = (GObjectClass *) klass;
252
253 gobject_class->constructed = g_udev_client_constructed;
254 gobject_class->set_property = g_udev_client_set_property;
255 gobject_class->get_property = g_udev_client_get_property;
256 gobject_class->finalize = g_udev_client_finalize;
257
258 /**
259 * GUdevClient:subsystems:
260 *
261 * The subsystems to listen for uevents on.
262 *
263 * To listen for only a specific DEVTYPE for a given SUBSYSTEM, use
264 * "subsystem/devtype". For example, to only listen for uevents
265 * where SUBSYSTEM is usb and DEVTYPE is usb_interface, use
266 * "usb/usb_interface".
267 *
268 * If this property is %NULL, then no events will be reported. If
269 * it's the empty array, events from all subsystems will be
270 * reported.
271 */
272 g_object_class_install_property (gobject_class,
273 PROP_SUBSYSTEMS,
274 g_param_spec_boxed ("subsystems",
275 "The subsystems to listen for changes on",
276 "The subsystems to listen for changes on",
277 G_TYPE_STRV,
278 G_PARAM_CONSTRUCT_ONLY |
279 G_PARAM_READWRITE));
280
281 /**
282 * GUdevClient::uevent:
283 * @client: The #GUdevClient receiving the event.
284 * @action: The action for the uevent e.g. "add", "remove", "change", "move",
285 * "online" or "offline"
286 * @device: Details about the #GUdevDevice the event is for.
287 *
288 * Emitted when @client receives an uevent.
289 *
290 * Note that while you'll have access to all the device's properties and attributes
291 * for the majority of actions, only the sysfs path will be available when the device
292 * is removed.
293 *
294 * Also note that the action is an arbitrary string, controlled by device drivers. Other
295 * values than those listed is possible, but unlikely.
296 *
297 * This signal is emitted in the
298 * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
299 * of the thread that @client was created in.
300 */
301 signals[UEVENT_SIGNAL] = g_signal_new ("uevent",
302 G_TYPE_FROM_CLASS (klass),
303 G_SIGNAL_RUN_LAST,
304 G_STRUCT_OFFSET (GUdevClientClass, uevent),
305 NULL,
306 NULL,
307 g_cclosure_marshal_generic,
308 G_TYPE_NONE,
309 2,
310 G_TYPE_STRING,
311 G_UDEV_TYPE_DEVICE);
312 }
313
314 static void
g_udev_client_init(GUdevClient * client)315 g_udev_client_init (GUdevClient *client)
316 {
317 client->priv = g_udev_client_get_instance_private (client);
318 }
319
320 /**
321 * g_udev_client_new:
322 * @subsystems: (array zero-terminated=1) (element-type utf8) (transfer none) (allow-none): A %NULL terminated string array of subsystems to listen for uevents on, %NULL to not listen on uevents at all, or an empty array to listen to uevents on all subsystems. See the documentation for the #GUdevClient:subsystems property for details on this parameter.
323 *
324 * Constructs a #GUdevClient object that can be used to query
325 * information about devices. Connect to the #GUdevClient::uevent
326 * signal to listen for uevents. Note that signals are emitted in the
327 * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
328 * of the thread that you call this constructor from.
329 *
330 * Returns: A new #GUdevClient object. Free with g_object_unref().
331 */
332 GUdevClient *
g_udev_client_new(const gchar * const * subsystems)333 g_udev_client_new (const gchar * const *subsystems)
334 {
335 return G_UDEV_CLIENT (g_object_new (G_UDEV_TYPE_CLIENT, "subsystems", subsystems, NULL));
336 }
337
338 /**
339 * g_udev_client_query_by_subsystem:
340 * @client: A #GUdevClient.
341 * @subsystem: (allow-none): The subsystem to get devices for or %NULL to get all devices.
342 *
343 * Gets all devices belonging to @subsystem.
344 *
345 * Returns: (nullable) (element-type GUdevDevice) (transfer full): A
346 * list of #GUdevDevice objects. The caller should free the result by
347 * using g_object_unref() on each element in the list and then
348 * g_list_free() on the list.
349 */
350 GList *
g_udev_client_query_by_subsystem(GUdevClient * client,const gchar * subsystem)351 g_udev_client_query_by_subsystem (GUdevClient *client,
352 const gchar *subsystem)
353 {
354 struct udev_enumerate *enumerate;
355 struct udev_list_entry *l, *devices;
356 GList *ret;
357
358 g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL);
359
360 ret = NULL;
361
362 /* prepare a device scan */
363 enumerate = udev_enumerate_new (client->priv->udev);
364
365 /* filter for subsystem */
366 if (subsystem != NULL)
367 udev_enumerate_add_match_subsystem (enumerate, subsystem);
368 /* retrieve the list */
369 udev_enumerate_scan_devices (enumerate);
370
371 /* add devices to the list */
372 devices = udev_enumerate_get_list_entry (enumerate);
373 for (l = devices; l != NULL; l = udev_list_entry_get_next (l))
374 {
375 struct udev_device *udevice;
376 GUdevDevice *device;
377
378 udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerate),
379 udev_list_entry_get_name (l));
380 if (udevice == NULL)
381 continue;
382 device = _g_udev_device_new (udevice);
383 udev_device_unref (udevice);
384 ret = g_list_prepend (ret, device);
385 }
386 udev_enumerate_unref (enumerate);
387
388 ret = g_list_reverse (ret);
389
390 return ret;
391 }
392
393 /**
394 * g_udev_client_query_by_device_number:
395 * @client: A #GUdevClient.
396 * @type: A value from the #GUdevDeviceType enumeration.
397 * @number: A device number.
398 *
399 * Looks up a device for a type and device number.
400 *
401 * Returns: (nullable) (transfer full): A #GUdevDevice object or %NULL
402 * if the device was not found. Free with g_object_unref().
403 */
404 GUdevDevice *
g_udev_client_query_by_device_number(GUdevClient * client,GUdevDeviceType type,GUdevDeviceNumber number)405 g_udev_client_query_by_device_number (GUdevClient *client,
406 GUdevDeviceType type,
407 GUdevDeviceNumber number)
408 {
409 struct udev_device *udevice;
410 GUdevDevice *device;
411
412 g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL);
413
414 device = NULL;
415 udevice = udev_device_new_from_devnum (client->priv->udev, type, number);
416
417 if (udevice == NULL)
418 goto out;
419
420 device = _g_udev_device_new (udevice);
421 udev_device_unref (udevice);
422
423 out:
424 return device;
425 }
426
427 /**
428 * g_udev_client_query_by_device_file:
429 * @client: A #GUdevClient.
430 * @device_file: A device file.
431 *
432 * Looks up a device for a device file.
433 *
434 * Returns: (nullable) (transfer full): A #GUdevDevice object or %NULL
435 * if the device was not found. Free with g_object_unref().
436 */
437 GUdevDevice *
g_udev_client_query_by_device_file(GUdevClient * client,const gchar * device_file)438 g_udev_client_query_by_device_file (GUdevClient *client,
439 const gchar *device_file)
440 {
441 struct stat stat_buf;
442 GUdevDevice *device;
443
444 g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL);
445 g_return_val_if_fail (device_file != NULL, NULL);
446
447 device = NULL;
448
449 if (stat (device_file, &stat_buf) != 0)
450 goto out;
451
452 if (stat_buf.st_rdev == 0)
453 goto out;
454
455 if (S_ISBLK (stat_buf.st_mode))
456 device = g_udev_client_query_by_device_number (client, G_UDEV_DEVICE_TYPE_BLOCK, stat_buf.st_rdev);
457 else if (S_ISCHR (stat_buf.st_mode))
458 device = g_udev_client_query_by_device_number (client, G_UDEV_DEVICE_TYPE_CHAR, stat_buf.st_rdev);
459
460 out:
461 return device;
462 }
463
464 /**
465 * g_udev_client_query_by_sysfs_path:
466 * @client: A #GUdevClient.
467 * @sysfs_path: A sysfs path.
468 *
469 * Looks up a device for a sysfs path.
470 *
471 * Returns: (nullable) (transfer full): A #GUdevDevice object or %NULL
472 * if the device was not found. Free with g_object_unref().
473 */
474 GUdevDevice *
g_udev_client_query_by_sysfs_path(GUdevClient * client,const gchar * sysfs_path)475 g_udev_client_query_by_sysfs_path (GUdevClient *client,
476 const gchar *sysfs_path)
477 {
478 struct udev_device *udevice;
479 GUdevDevice *device;
480
481 g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL);
482 g_return_val_if_fail (sysfs_path != NULL, NULL);
483
484 device = NULL;
485 udevice = udev_device_new_from_syspath (client->priv->udev, sysfs_path);
486 if (udevice == NULL)
487 goto out;
488
489 device = _g_udev_device_new (udevice);
490 udev_device_unref (udevice);
491
492 out:
493 return device;
494 }
495
496 /**
497 * g_udev_client_query_by_subsystem_and_name:
498 * @client: A #GUdevClient.
499 * @subsystem: A subsystem name.
500 * @name: The name of the device.
501 *
502 * Looks up a device for a subsystem and name.
503 *
504 * Returns: (nullable) (transfer full): A #GUdevDevice object or %NULL
505 * if the device was not found. Free with g_object_unref().
506 */
507 GUdevDevice *
g_udev_client_query_by_subsystem_and_name(GUdevClient * client,const gchar * subsystem,const gchar * name)508 g_udev_client_query_by_subsystem_and_name (GUdevClient *client,
509 const gchar *subsystem,
510 const gchar *name)
511 {
512 struct udev_device *udevice;
513 GUdevDevice *device;
514
515 g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL);
516 g_return_val_if_fail (subsystem != NULL, NULL);
517 g_return_val_if_fail (name != NULL, NULL);
518
519 device = NULL;
520 udevice = udev_device_new_from_subsystem_sysname (client->priv->udev, subsystem, name);
521 if (udevice == NULL)
522 goto out;
523
524 device = _g_udev_device_new (udevice);
525 udev_device_unref (udevice);
526
527 out:
528 return device;
529 }
530
531 struct udev_enumerate *
_g_udev_client_new_enumerate(GUdevClient * client)532 _g_udev_client_new_enumerate (GUdevClient *client)
533 {
534 struct udev_enumerate *enumerate;
535
536 enumerate = udev_enumerate_new (client->priv->udev);
537
538 if (client->priv->subsystems != NULL)
539 {
540 guint n;
541 for (n = 0; client->priv->subsystems[n] != NULL; n++)
542 {
543 gchar *subsystem;
544 gchar *devtype;
545 gchar *s;
546
547 subsystem = g_strdup (client->priv->subsystems[n]);
548 devtype = NULL;
549
550 s = strstr (subsystem, "/");
551 if (s != NULL)
552 {
553 devtype = s + 1;
554 *s = '\0';
555 }
556
557 udev_enumerate_add_match_subsystem (enumerate, subsystem);
558
559 if (devtype != NULL)
560 udev_enumerate_add_match_property (enumerate, "DEVTYPE", devtype);
561
562 g_free (subsystem);
563 }
564 }
565
566 return enumerate;
567 }
568