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