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