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