]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/gudev/gudevclient.c
fix double semicolon typo
[thirdparty/systemd.git] / src / gudev / gudevclient.c
CommitLineData
bf05675a
DZ
1/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 *
cbdf255e 3 * Copyright (C) 2008-2010 David Zeuthen <davidz@redhat.com>
bf05675a
DZ
4 *
5 * This library is free software; you can redistribute it and/or
23757887 6 * modify it under the terms of the GNU Library General Public
bf05675a
DZ
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
23757887 13 * Library General Public License for more details.
bf05675a 14 *
23757887
SK
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
bf05675a
DZ
18 */
19
20#ifdef HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#include <stdlib.h>
25#include <string.h>
2eec67ac 26#include <sys/stat.h>
bf05675a
DZ
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 *
0976fd06 49 * To get #GUdevDevice objects, use
bf05675a
DZ
50 * g_udev_client_query_by_subsystem(),
51 * g_udev_client_query_by_device_number(),
52 * g_udev_client_query_by_device_file(),
0976fd06
DZ
53 * g_udev_client_query_by_sysfs_path(),
54 * g_udev_client_query_by_subsystem_and_name()
55 * or the #GUdevEnumerator type.
bf05675a
DZ
56 *
57 * To listen to uevents, connect to the #GUdevClient::uevent signal.
58 */
59
60struct _GUdevClientPrivate
61{
cbdf255e 62 GSource *watch_source;
bf05675a
DZ
63 struct udev *udev;
64 struct udev_monitor *monitor;
65
66 gchar **subsystems;
67};
68
69enum
70{
71 PROP_0,
72 PROP_SUBSYSTEMS,
73};
74
75enum
76{
77 UEVENT_SIGNAL,
78 LAST_SIGNAL,
79};
80
81static guint signals[LAST_SIGNAL] = { 0 };
82
83G_DEFINE_TYPE (GUdevClient, g_udev_client, G_TYPE_OBJECT)
84
85/* ---------------------------------------------------------------------------------------------------- */
86
87static gboolean
88monitor_event (GIOChannel *source,
912541b0
KS
89 GIOCondition condition,
90 gpointer data)
bf05675a
DZ
91{
92 GUdevClient *client = (GUdevClient *) data;
93 GUdevDevice *device;
94 struct udev_device *udevice;
95
e4dcdc4a
MP
96 if (client->priv->monitor == NULL)
97 goto out;
bf05675a
DZ
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
115static void
116g_udev_client_finalize (GObject *object)
117{
118 GUdevClient *client = G_UDEV_CLIENT (object);
119
cbdf255e 120 if (client->priv->watch_source != NULL)
bf05675a 121 {
cbdf255e
DZ
122 g_source_destroy (client->priv->watch_source);
123 client->priv->watch_source = NULL;
bf05675a
DZ
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
144static void
145g_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
166static void
167g_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
186static void
187g_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 {
214a6c79 202 /* install subsystem filters to only wake up for certain events */
bf05675a
DZ
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
e4dcdc4a
MP
221 if (client->priv->monitor != NULL)
222 udev_monitor_filter_add_match_subsystem_devtype (client->priv->monitor, subsystem, devtype);
bf05675a
DZ
223
224 g_free (subsystem);
225 }
226
227 /* listen to events, and buffer them */
e4dcdc4a
MP
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));
cbdf255e 232 client->priv->watch_source = g_io_create_watch (channel, G_IO_IN);
e4dcdc4a 233 g_io_channel_unref (channel);
cbdf255e
DZ
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 }
bf05675a
DZ
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
249static void
250g_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.
cbdf255e
DZ
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.
bf05675a
DZ
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
309static void
310g_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:
daef8ae3 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.
bf05675a
DZ
320 *
321 * Constructs a #GUdevClient object that can be used to query
322 * information about devices. Connect to the #GUdevClient::uevent
cbdf255e
DZ
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.
bf05675a
DZ
326 *
327 * Returns: A new #GUdevClient object. Free with g_object_unref().
328 */
329GUdevClient *
330g_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 *
2be61072
EN
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.
bf05675a
DZ
346 */
347GList *
348g_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 *
2be61072
EN
398 * Returns: (nullable) (transfer full): A #GUdevDevice object or %NULL
399 * if the device was not found. Free with g_object_unref().
bf05675a
DZ
400 */
401GUdevDevice *
402g_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 *
2be61072
EN
431 * Returns: (nullable) (transfer full): A #GUdevDevice object or %NULL
432 * if the device was not found. Free with g_object_unref().
bf05675a
DZ
433 */
434GUdevDevice *
435g_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.
079de714 467 *
2be61072
EN
468 * Returns: (nullable) (transfer full): A #GUdevDevice object or %NULL
469 * if the device was not found. Free with g_object_unref().
bf05675a
DZ
470 */
471GUdevDevice *
472g_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 *
2be61072
EN
501 * Returns: (nullable) (transfer full): A #GUdevDevice object or %NULL
502 * if the device was not found. Free with g_object_unref().
bf05675a
DZ
503 */
504GUdevDevice *
505g_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
0976fd06
DZ
528struct 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}