chiark / gitweb /
fix spelling
[elogind.git] / extras / gudev / gudevclient.c
1 /* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2  *
3  * Copyright (C) 2008 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 information about a device, 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() or
55  * g_udev_client_query_by_subsystem_and_name().
56  *
57  * To listen to uevents, connect to the #GUdevClient::uevent signal.
58  */
59
60 struct _GUdevClientPrivate
61 {
62   guint watch_id;
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   udevice = udev_monitor_receive_device (client->priv->monitor);
97   if (udevice == NULL)
98     goto out;
99
100   device = _g_udev_device_new (udevice);
101   udev_device_unref (udevice);
102   g_signal_emit (client,
103                  signals[UEVENT_SIGNAL],
104                  0,
105                  g_udev_device_get_action (device),
106                  device);
107   g_object_unref (device);
108
109  out:
110   return TRUE;
111 }
112
113 static void
114 g_udev_client_finalize (GObject *object)
115 {
116   GUdevClient *client = G_UDEV_CLIENT (object);
117
118   if (client->priv->watch_id != 0)
119     {
120       g_source_remove (client->priv->watch_id);
121       client->priv->watch_id = 0;
122     }
123
124   if (client->priv->monitor != NULL)
125     {
126       udev_monitor_unref (client->priv->monitor);
127       client->priv->monitor = NULL;
128     }
129
130   if (client->priv->udev != NULL)
131     {
132       udev_unref (client->priv->udev);
133       client->priv->udev = NULL;
134     }
135
136   g_strfreev (client->priv->subsystems);
137
138   if (G_OBJECT_CLASS (g_udev_client_parent_class)->finalize != NULL)
139     G_OBJECT_CLASS (g_udev_client_parent_class)->finalize (object);
140 }
141
142 static void
143 g_udev_client_set_property (GObject      *object,
144                             guint         prop_id,
145                             const GValue *value,
146                             GParamSpec   *pspec)
147 {
148   GUdevClient *client = G_UDEV_CLIENT (object);
149
150   switch (prop_id)
151     {
152     case PROP_SUBSYSTEMS:
153       if (client->priv->subsystems != NULL)
154         g_strfreev (client->priv->subsystems);
155       client->priv->subsystems = g_strdupv (g_value_get_boxed (value));
156       break;
157
158     default:
159       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
160       break;
161     }
162 }
163
164 static void
165 g_udev_client_get_property (GObject     *object,
166                             guint        prop_id,
167                             GValue      *value,
168                             GParamSpec  *pspec)
169 {
170   GUdevClient *client = G_UDEV_CLIENT (object);
171
172   switch (prop_id)
173     {
174     case PROP_SUBSYSTEMS:
175       g_value_set_boxed (value, client->priv->subsystems);
176       break;
177
178     default:
179       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
180       break;
181     }
182 }
183
184 static void
185 g_udev_client_constructed (GObject *object)
186 {
187   GUdevClient *client = G_UDEV_CLIENT (object);
188   GIOChannel *channel;
189   guint n;
190
191   client->priv->udev = udev_new ();
192
193   /* connect to event source */
194   client->priv->monitor = udev_monitor_new_from_netlink (client->priv->udev, "udev");
195
196   //g_debug ("ss = %p", client->priv->subsystems);
197
198   if (client->priv->subsystems != NULL)
199     {
200       /* install subsystem filters to only wake up for certain events */
201       for (n = 0; client->priv->subsystems[n] != NULL; n++)
202         {
203           gchar *subsystem;
204           gchar *devtype;
205           gchar *s;
206
207           subsystem = g_strdup (client->priv->subsystems[n]);
208           devtype = NULL;
209
210           //g_debug ("s = '%s'", subsystem);
211
212           s = strstr (subsystem, "/");
213           if (s != NULL)
214             {
215               devtype = s + 1;
216               *s = '\0';
217             }
218
219           udev_monitor_filter_add_match_subsystem_devtype (client->priv->monitor, subsystem, devtype);
220
221           g_free (subsystem);
222         }
223
224       /* listen to events, and buffer them */
225       udev_monitor_enable_receiving (client->priv->monitor);
226
227       channel = g_io_channel_unix_new (udev_monitor_get_fd (client->priv->monitor));
228       client->priv->watch_id = g_io_add_watch (channel, G_IO_IN, monitor_event, client);
229       g_io_channel_unref (channel);
230     }
231
232   if (G_OBJECT_CLASS (g_udev_client_parent_class)->constructed != NULL)
233     G_OBJECT_CLASS (g_udev_client_parent_class)->constructed (object);
234 }
235
236
237 static void
238 g_udev_client_class_init (GUdevClientClass *klass)
239 {
240   GObjectClass *gobject_class = (GObjectClass *) klass;
241
242   gobject_class->constructed  = g_udev_client_constructed;
243   gobject_class->set_property = g_udev_client_set_property;
244   gobject_class->get_property = g_udev_client_get_property;
245   gobject_class->finalize     = g_udev_client_finalize;
246
247   /**
248    * GUdevClient:subsystems:
249    *
250    * The subsystems to listen for uevents on.
251    *
252    * To listen for only a specific DEVTYPE for a given SUBSYSTEM, use
253    * "subsystem/devtype". For example, to only listen for uevents
254    * where SUBSYSTEM is usb and DEVTYPE is usb_interface, use
255    * "usb/usb_interface".
256    *
257    * If this property is %NULL, then no events will be reported. If
258    * it's the empty array, events from all subsystems will be
259    * reported.
260    */
261   g_object_class_install_property (gobject_class,
262                                    PROP_SUBSYSTEMS,
263                                    g_param_spec_boxed ("subsystems",
264                                                        "The subsystems to listen for changes on",
265                                                        "The subsystems to listen for changes on",
266                                                        G_TYPE_STRV,
267                                                        G_PARAM_CONSTRUCT_ONLY |
268                                                        G_PARAM_READWRITE));
269
270   /**
271    * GUdevClient::uevent:
272    * @client: The #GUdevClient receiving the event.
273    * @action: The action for the uevent e.g. "add", "remove", "change", "move", etc.
274    * @device: Details about the #GUdevDevice the event is for.
275    *
276    * Emitted when @client receives an uevent.
277    */
278   signals[UEVENT_SIGNAL] = g_signal_new ("uevent",
279                                          G_TYPE_FROM_CLASS (klass),
280                                          G_SIGNAL_RUN_LAST,
281                                          G_STRUCT_OFFSET (GUdevClientClass, uevent),
282                                          NULL,
283                                          NULL,
284                                          g_udev_marshal_VOID__STRING_OBJECT,
285                                          G_TYPE_NONE,
286                                          2,
287                                          G_TYPE_STRING,
288                                          G_UDEV_TYPE_DEVICE);
289
290   g_type_class_add_private (klass, sizeof (GUdevClientPrivate));
291 }
292
293 static void
294 g_udev_client_init (GUdevClient *client)
295 {
296   client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client,
297                                               G_UDEV_TYPE_CLIENT,
298                                               GUdevClientPrivate);
299 }
300
301 /**
302  * g_udev_client_new:
303  * @subsystems: (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.
304  *
305  * Constructs a #GUdevClient object that can be used to query
306  * information about devices. Connect to the #GUdevClient::uevent
307  * signal to listen for uevents.
308  *
309  * Returns: A new #GUdevClient object. Free with g_object_unref().
310  */
311 GUdevClient *
312 g_udev_client_new (const gchar * const *subsystems)
313 {
314   return G_UDEV_CLIENT (g_object_new (G_UDEV_TYPE_CLIENT, "subsystems", subsystems, NULL));
315 }
316
317 /**
318  * g_udev_client_query_by_subsystem:
319  * @client: A #GUdevClient.
320  * @subsystem: (allow-none): The subsystem to get devices for or %NULL to get all devices.
321  *
322  * Gets all devices belonging to @subsystem.
323  *
324  * Returns: (element-type GUdevDevice): 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.
325  */
326 GList *
327 g_udev_client_query_by_subsystem (GUdevClient  *client,
328                                   const gchar  *subsystem)
329 {
330   struct udev_enumerate *enumerate;
331   struct udev_list_entry *l, *devices;
332   GList *ret;
333
334   g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL);
335
336   ret = NULL;
337
338   /* prepare a device scan */
339   enumerate = udev_enumerate_new (client->priv->udev);
340
341   /* filter for subsystem */
342   if (subsystem != NULL)
343     udev_enumerate_add_match_subsystem (enumerate, subsystem);
344   /* retrieve the list */
345   udev_enumerate_scan_devices (enumerate);
346
347   /* add devices to the list */
348   devices = udev_enumerate_get_list_entry (enumerate);
349   for (l = devices; l != NULL; l = udev_list_entry_get_next (l))
350     {
351       struct udev_device *udevice;
352       GUdevDevice *device;
353
354       udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerate),
355                                               udev_list_entry_get_name (l));
356       if (udevice == NULL)
357         continue;
358       device = _g_udev_device_new (udevice);
359       udev_device_unref (udevice);
360       ret = g_list_prepend (ret, device);
361     }
362   udev_enumerate_unref (enumerate);
363
364   ret = g_list_reverse (ret);
365
366   return ret;
367 }
368
369 /**
370  * g_udev_client_query_by_device_number:
371  * @client: A #GUdevClient.
372  * @type: A value from the #GUdevDeviceType enumeration.
373  * @number: A device number.
374  *
375  * Looks up a device for a type and device number.
376  *
377  * Returns: A #GUdevDevice object or %NULL if the device was not found. Free with g_object_unref().
378  */
379 GUdevDevice *
380 g_udev_client_query_by_device_number (GUdevClient      *client,
381                                       GUdevDeviceType   type,
382                                       GUdevDeviceNumber number)
383 {
384   struct udev_device *udevice;
385   GUdevDevice *device;
386
387   g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL);
388
389   device = NULL;
390   udevice = udev_device_new_from_devnum (client->priv->udev, type, number);
391
392   if (udevice == NULL)
393     goto out;
394
395   device = _g_udev_device_new (udevice);
396   udev_device_unref (udevice);
397
398  out:
399   return device;
400 }
401
402 /**
403  * g_udev_client_query_by_device_file:
404  * @client: A #GUdevClient.
405  * @device_file: A device file.
406  *
407  * Looks up a device for a device file.
408  *
409  * Returns: A #GUdevDevice object or %NULL if the device was not found. Free with g_object_unref().
410  */
411 GUdevDevice *
412 g_udev_client_query_by_device_file (GUdevClient  *client,
413                                     const gchar  *device_file)
414 {
415   struct stat stat_buf;
416   GUdevDevice *device;
417
418   g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL);
419   g_return_val_if_fail (device_file != NULL, NULL);
420
421   device = NULL;
422
423   if (stat (device_file, &stat_buf) != 0)
424     goto out;
425
426   if (stat_buf.st_rdev == 0)
427     goto out;
428
429   if (S_ISBLK (stat_buf.st_mode))
430     device = g_udev_client_query_by_device_number (client, G_UDEV_DEVICE_TYPE_BLOCK, stat_buf.st_rdev);
431   else if (S_ISCHR (stat_buf.st_mode))
432     device = g_udev_client_query_by_device_number (client, G_UDEV_DEVICE_TYPE_CHAR, stat_buf.st_rdev);
433
434  out:
435   return device;
436 }
437
438 /**
439  * g_udev_client_query_by_sysfs_path:
440  * @client: A #GUdevClient.
441  * @sysfs_path: A sysfs path.
442  *
443  * Looks up a device for a sysfs path.
444  *
445  * Returns: A #GUdevDevice object or %NULL if the device was not found. Free with g_object_unref().
446  */
447 GUdevDevice *
448 g_udev_client_query_by_sysfs_path (GUdevClient  *client,
449                                    const gchar  *sysfs_path)
450 {
451   struct udev_device *udevice;
452   GUdevDevice *device;
453
454   g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL);
455   g_return_val_if_fail (sysfs_path != NULL, NULL);
456
457   device = NULL;
458   udevice = udev_device_new_from_syspath (client->priv->udev, sysfs_path);
459   if (udevice == NULL)
460     goto out;
461
462   device = _g_udev_device_new (udevice);
463   udev_device_unref (udevice);
464
465  out:
466   return device;
467 }
468
469 /**
470  * g_udev_client_query_by_subsystem_and_name:
471  * @client: A #GUdevClient.
472  * @subsystem: A subsystem name.
473  * @name: The name of the device.
474  *
475  * Looks up a device for a subsystem and name.
476  *
477  * Returns: A #GUdevDevice object or %NULL if the device was not found. Free with g_object_unref().
478  */
479 GUdevDevice *
480 g_udev_client_query_by_subsystem_and_name (GUdevClient  *client,
481                                            const gchar  *subsystem,
482                                            const gchar  *name)
483 {
484   struct udev_device *udevice;
485   GUdevDevice *device;
486
487   g_return_val_if_fail (G_UDEV_IS_CLIENT (client), NULL);
488   g_return_val_if_fail (subsystem != NULL, NULL);
489   g_return_val_if_fail (name != NULL, NULL);
490
491   device = NULL;
492   udevice = udev_device_new_from_subsystem_sysname (client->priv->udev, subsystem, name);
493   if (udevice == NULL)
494     goto out;
495
496   device = _g_udev_device_new (udevice);
497   udev_device_unref (udevice);
498
499  out:
500   return device;
501 }
502