chiark / gitweb /
7106719111d3c0557282399255c9f70f4d7dfb91
[elogind.git] / src / gudev / gudevdevice.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 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
27 #include "gudevdevice.h"
28 #include "gudevprivate.h"
29
30 /**
31  * SECTION:gudevdevice
32  * @short_description: Get information about a device
33  *
34  * The #GUdevDevice class is used to get information about a specific
35  * device. Note that you cannot instantiate a #GUdevDevice object
36  * yourself. Instead you must use #GUdevClient to obtain #GUdevDevice
37  * objects.
38  *
39  * To get basic information about a device, use
40  * g_udev_device_get_subsystem(), g_udev_device_get_devtype(),
41  * g_udev_device_get_name(), g_udev_device_get_number(),
42  * g_udev_device_get_sysfs_path(), g_udev_device_get_driver(),
43  * g_udev_device_get_action(), g_udev_device_get_seqnum(),
44  * g_udev_device_get_device_type(), g_udev_device_get_device_number(),
45  * g_udev_device_get_device_file(),
46  * g_udev_device_get_device_file_symlinks().
47  *
48  * To navigate the device tree, use g_udev_device_get_parent() and
49  * g_udev_device_get_parent_with_subsystem().
50  *
51  * To access udev properties for the device, use
52  * g_udev_device_get_property_keys(),
53  * g_udev_device_has_property(),
54  * g_udev_device_get_property(),
55  * g_udev_device_get_property_as_int(),
56  * g_udev_device_get_property_as_uint64(),
57  * g_udev_device_get_property_as_double(),
58  * g_udev_device_get_property_as_boolean() and
59  * g_udev_device_get_property_as_strv().
60  *
61  * To access sysfs attributes for the device, use
62  * g_udev_device_get_sysfs_attr_keys(),
63  * g_udev_device_has_sysfs_attr(),
64  * g_udev_device_get_sysfs_attr(),
65  * g_udev_device_get_sysfs_attr_as_int(),
66  * g_udev_device_get_sysfs_attr_as_uint64(),
67  * g_udev_device_get_sysfs_attr_as_double(),
68  * g_udev_device_get_sysfs_attr_as_boolean() and
69  * g_udev_device_get_sysfs_attr_as_strv().
70  *
71  * Note that all getters on #GUdevDevice are non-reffing – returned
72  * values are owned by the object, should not be freed and are only
73  * valid as long as the object is alive.
74  *
75  * By design, #GUdevDevice will not react to changes for a device – it
76  * only contains a snapshot of information when the #GUdevDevice
77  * object was created. To work with changes, you typically connect to
78  * the #GUdevClient::uevent signal on a #GUdevClient and get a new
79  * #GUdevDevice whenever an event happens.
80  */
81
82 struct _GUdevDevicePrivate
83 {
84   struct udev_device *udevice;
85
86   /* computed ondemand and cached */
87   gchar **device_file_symlinks;
88   gchar **property_keys;
89   gchar **sysfs_attr_keys;
90   gchar **tags;
91   GHashTable *prop_strvs;
92   GHashTable *sysfs_attr_strvs;
93 };
94
95 G_DEFINE_TYPE (GUdevDevice, g_udev_device, G_TYPE_OBJECT)
96
97 static void
98 g_udev_device_finalize (GObject *object)
99 {
100   GUdevDevice *device = G_UDEV_DEVICE (object);
101
102   g_strfreev (device->priv->device_file_symlinks);
103   g_strfreev (device->priv->property_keys);
104   g_strfreev (device->priv->sysfs_attr_keys);
105   g_strfreev (device->priv->tags);
106
107   if (device->priv->udevice != NULL)
108     udev_device_unref (device->priv->udevice);
109
110   if (device->priv->prop_strvs != NULL)
111     g_hash_table_unref (device->priv->prop_strvs);
112
113   if (device->priv->sysfs_attr_strvs != NULL)
114     g_hash_table_unref (device->priv->sysfs_attr_strvs);
115
116   if (G_OBJECT_CLASS (g_udev_device_parent_class)->finalize != NULL)
117     (* G_OBJECT_CLASS (g_udev_device_parent_class)->finalize) (object);
118 }
119
120 static void
121 g_udev_device_class_init (GUdevDeviceClass *klass)
122 {
123   GObjectClass *gobject_class = (GObjectClass *) klass;
124
125   gobject_class->finalize = g_udev_device_finalize;
126
127   g_type_class_add_private (klass, sizeof (GUdevDevicePrivate));
128 }
129
130 static void
131 g_udev_device_init (GUdevDevice *device)
132 {
133   device->priv = G_TYPE_INSTANCE_GET_PRIVATE (device,
134                                               G_UDEV_TYPE_DEVICE,
135                                               GUdevDevicePrivate);
136 }
137
138
139 GUdevDevice *
140 _g_udev_device_new (struct udev_device *udevice)
141 {
142   GUdevDevice *device;
143
144   device =  G_UDEV_DEVICE (g_object_new (G_UDEV_TYPE_DEVICE, NULL));
145   device->priv->udevice = udev_device_ref (udevice);
146
147   return device;
148 }
149
150 /**
151  * g_udev_device_get_subsystem:
152  * @device: A #GUdevDevice.
153  *
154  * Gets the subsystem for @device.
155  *
156  * Returns: The subsystem for @device.
157  */
158 const gchar *
159 g_udev_device_get_subsystem (GUdevDevice *device)
160 {
161   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
162   return udev_device_get_subsystem (device->priv->udevice);
163 }
164
165 /**
166  * g_udev_device_get_devtype:
167  * @device: A #GUdevDevice.
168  *
169  * Gets the device type for @device.
170  *
171  * Returns: The devtype for @device.
172  */
173 const gchar *
174 g_udev_device_get_devtype (GUdevDevice *device)
175 {
176   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
177   return udev_device_get_devtype (device->priv->udevice);
178 }
179
180 /**
181  * g_udev_device_get_name:
182  * @device: A #GUdevDevice.
183  *
184  * Gets the name of @device, e.g. "sda3".
185  *
186  * Returns: The name of @device.
187  */
188 const gchar *
189 g_udev_device_get_name (GUdevDevice *device)
190 {
191   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
192   return udev_device_get_sysname (device->priv->udevice);
193 }
194
195 /**
196  * g_udev_device_get_number:
197  * @device: A #GUdevDevice.
198  *
199  * Gets the number of @device, e.g. "3" if g_udev_device_get_name() returns "sda3".
200  *
201  * Returns: The number of @device.
202  */
203 const gchar *
204 g_udev_device_get_number (GUdevDevice *device)
205 {
206   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
207   return udev_device_get_sysnum (device->priv->udevice);
208 }
209
210 /**
211  * g_udev_device_get_sysfs_path:
212  * @device: A #GUdevDevice.
213  *
214  * Gets the sysfs path for @device.
215  *
216  * Returns: The sysfs path for @device.
217  */
218 const gchar *
219 g_udev_device_get_sysfs_path (GUdevDevice *device)
220 {
221   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
222   return udev_device_get_syspath (device->priv->udevice);
223 }
224
225 /**
226  * g_udev_device_get_driver:
227  * @device: A #GUdevDevice.
228  *
229  * Gets the name of the driver used for @device.
230  *
231  * Returns: (nullable): The name of the driver for @device or %NULL if
232  * unknown.
233  */
234 const gchar *
235 g_udev_device_get_driver (GUdevDevice *device)
236 {
237   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
238   return udev_device_get_driver (device->priv->udevice);
239 }
240
241 /**
242  * g_udev_device_get_action:
243  * @device: A #GUdevDevice.
244  *
245  * Gets the most recent action (e.g. "add", "remove", "change", etc.) for @device.
246  *
247  * Returns: An action string.
248  */
249 const gchar *
250 g_udev_device_get_action (GUdevDevice *device)
251 {
252   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
253   return udev_device_get_action (device->priv->udevice);
254 }
255
256 /**
257  * g_udev_device_get_seqnum:
258  * @device: A #GUdevDevice.
259  *
260  * Gets the most recent sequence number for @device.
261  *
262  * Returns: A sequence number.
263  */
264 guint64
265 g_udev_device_get_seqnum (GUdevDevice *device)
266 {
267   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0);
268   return udev_device_get_seqnum (device->priv->udevice);
269 }
270
271 /**
272  * g_udev_device_get_device_type:
273  * @device: A #GUdevDevice.
274  *
275  * Gets the type of the device file, if any, for @device.
276  *
277  * Returns: The device number for @device or #G_UDEV_DEVICE_TYPE_NONE if the device does not have a device file.
278  */
279 GUdevDeviceType
280 g_udev_device_get_device_type (GUdevDevice *device)
281 {
282   struct stat stat_buf;
283   const gchar *device_file;
284   GUdevDeviceType type;
285
286   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), G_UDEV_DEVICE_TYPE_NONE);
287
288   type = G_UDEV_DEVICE_TYPE_NONE;
289
290   /* TODO: would be better to have support for this in libudev... */
291
292   device_file = g_udev_device_get_device_file (device);
293   if (device_file == NULL)
294     goto out;
295
296   if (stat (device_file, &stat_buf) != 0)
297     goto out;
298
299   if (S_ISBLK (stat_buf.st_mode))
300     type = G_UDEV_DEVICE_TYPE_BLOCK;
301   else if (S_ISCHR (stat_buf.st_mode))
302     type = G_UDEV_DEVICE_TYPE_CHAR;
303
304  out:
305   return type;
306 }
307
308 /**
309  * g_udev_device_get_device_number:
310  * @device: A #GUdevDevice.
311  *
312  * Gets the device number, if any, for @device.
313  *
314  * Returns: The device number for @device or 0 if unknown.
315  */
316 GUdevDeviceNumber
317 g_udev_device_get_device_number (GUdevDevice *device)
318 {
319   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0);
320   return udev_device_get_devnum (device->priv->udevice);
321 }
322
323 /**
324  * g_udev_device_get_device_file:
325  * @device: A #GUdevDevice.
326  *
327  * Gets the device file for @device.
328  *
329  * Returns: (nullable): The device file for @device or %NULL if no
330  * device file exists.
331  */
332 const gchar *
333 g_udev_device_get_device_file (GUdevDevice *device)
334 {
335   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
336   return udev_device_get_devnode (device->priv->udevice);
337 }
338
339 /**
340  * g_udev_device_get_device_file_symlinks:
341  * @device: A #GUdevDevice.
342  *
343  * Gets a list of symlinks (in <literal>/dev</literal>) that points to
344  * the device file for @device.
345  *
346  * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): A %NULL terminated string array of symlinks. This array is owned by @device and should not be freed by the caller.
347  */
348 const gchar * const *
349 g_udev_device_get_device_file_symlinks (GUdevDevice *device)
350 {
351   struct udev_list_entry *l;
352   GPtrArray *p;
353
354   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
355
356   if (device->priv->device_file_symlinks != NULL)
357     goto out;
358
359   p = g_ptr_array_new ();
360   for (l = udev_device_get_devlinks_list_entry (device->priv->udevice); l != NULL; l = udev_list_entry_get_next (l))
361     {
362       g_ptr_array_add (p, g_strdup (udev_list_entry_get_name (l)));
363     }
364   g_ptr_array_add (p, NULL);
365   device->priv->device_file_symlinks = (gchar **) g_ptr_array_free (p, FALSE);
366
367  out:
368   return (const gchar * const *) device->priv->device_file_symlinks;
369 }
370
371 /* ---------------------------------------------------------------------------------------------------- */
372
373 /**
374  * g_udev_device_get_parent:
375  * @device: A #GUdevDevice.
376  *
377  * Gets the immediate parent of @device, if any.
378  *
379  * Returns: (nullable) (transfer full): A #GUdevDevice or %NULL if
380  * @device has no parent. Free with g_object_unref().
381  */
382 GUdevDevice *
383 g_udev_device_get_parent (GUdevDevice  *device)
384 {
385   GUdevDevice *ret;
386   struct udev_device *udevice;
387
388   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
389
390   ret = NULL;
391
392   udevice = udev_device_get_parent (device->priv->udevice);
393   if (udevice == NULL)
394     goto out;
395
396   ret = _g_udev_device_new (udevice);
397
398  out:
399   return ret;
400 }
401
402 /**
403  * g_udev_device_get_parent_with_subsystem:
404  * @device: A #GUdevDevice.
405  * @subsystem: The subsystem of the parent to get.
406  * @devtype: (allow-none): The devtype of the parent to get or %NULL.
407  *
408  * Walks up the chain of parents of @device and returns the first
409  * device encountered where @subsystem and @devtype matches, if any.
410  *
411  * Returns: (nullable) (transfer full): A #GUdevDevice or %NULL if
412  * @device has no parent with @subsystem and @devtype. Free with
413  * g_object_unref().
414  */
415 GUdevDevice *
416 g_udev_device_get_parent_with_subsystem (GUdevDevice  *device,
417                                          const gchar  *subsystem,
418                                          const gchar  *devtype)
419 {
420   GUdevDevice *ret;
421   struct udev_device *udevice;
422
423   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
424   g_return_val_if_fail (subsystem != NULL, NULL);
425
426   ret = NULL;
427
428   udevice = udev_device_get_parent_with_subsystem_devtype (device->priv->udevice,
429                                                            subsystem,
430                                                            devtype);
431   if (udevice == NULL)
432     goto out;
433
434   ret = _g_udev_device_new (udevice);
435
436  out:
437   return ret;
438 }
439
440 /* ---------------------------------------------------------------------------------------------------- */
441
442 /**
443  * g_udev_device_get_property_keys:
444  * @device: A #GUdevDevice.
445  *
446  * Gets all keys for properties on @device.
447  *
448  * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): A %NULL terminated string array of property keys. This array is owned by @device and should not be freed by the caller.
449  */
450 const gchar* const *
451 g_udev_device_get_property_keys (GUdevDevice *device)
452 {
453   struct udev_list_entry *l;
454   GPtrArray *p;
455
456   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
457
458   if (device->priv->property_keys != NULL)
459     goto out;
460
461   p = g_ptr_array_new ();
462   for (l = udev_device_get_properties_list_entry (device->priv->udevice); l != NULL; l = udev_list_entry_get_next (l))
463     {
464       g_ptr_array_add (p, g_strdup (udev_list_entry_get_name (l)));
465     }
466   g_ptr_array_add (p, NULL);
467   device->priv->property_keys = (gchar **) g_ptr_array_free (p, FALSE);
468
469  out:
470   return (const gchar * const *) device->priv->property_keys;
471 }
472
473
474 /**
475  * g_udev_device_has_property:
476  * @device: A #GUdevDevice.
477  * @key: Name of property.
478  *
479  * Check if a the property with the given key exists.
480  *
481  * Returns: %TRUE only if the value for @key exist.
482  */
483 gboolean
484 g_udev_device_has_property (GUdevDevice  *device,
485                             const gchar  *key)
486 {
487   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE);
488   g_return_val_if_fail (key != NULL, FALSE);
489   return udev_device_get_property_value (device->priv->udevice, key) != NULL;
490 }
491
492 /**
493  * g_udev_device_get_property:
494  * @device: A #GUdevDevice.
495  * @key: Name of property.
496  *
497  * Look up the value for @key on @device.
498  *
499  * Returns: (nullable): The value for @key or %NULL if @key doesn't
500  * exist on @device. Do not free this string, it is owned by @device.
501  */
502 const gchar *
503 g_udev_device_get_property (GUdevDevice  *device,
504                             const gchar  *key)
505 {
506   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
507   g_return_val_if_fail (key != NULL, NULL);
508   return udev_device_get_property_value (device->priv->udevice, key);
509 }
510
511 /**
512  * g_udev_device_get_property_as_int:
513  * @device: A #GUdevDevice.
514  * @key: Name of property.
515  *
516  * Look up the value for @key on @device and convert it to an integer
517  * using strtol().
518  *
519  * Returns: The value for @key or 0 if @key doesn't exist or
520  * isn't an integer.
521  */
522 gint
523 g_udev_device_get_property_as_int (GUdevDevice  *device,
524                                    const gchar  *key)
525 {
526   gint result;
527   const gchar *s;
528
529   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0);
530   g_return_val_if_fail (key != NULL, 0);
531
532   result = 0;
533   s = g_udev_device_get_property (device, key);
534   if (s == NULL)
535     goto out;
536
537   result = strtol (s, NULL, 0);
538 out:
539   return result;
540 }
541
542 /**
543  * g_udev_device_get_property_as_uint64:
544  * @device: A #GUdevDevice.
545  * @key: Name of property.
546  *
547  * Look up the value for @key on @device and convert it to an unsigned
548  * 64-bit integer using g_ascii_strtoull().
549  *
550  * Returns: The value  for @key or 0 if @key doesn't  exist or isn't a
551  * #guint64.
552  */
553 guint64
554 g_udev_device_get_property_as_uint64 (GUdevDevice  *device,
555                                       const gchar  *key)
556 {
557   guint64 result;
558   const gchar *s;
559
560   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0);
561   g_return_val_if_fail (key != NULL, 0);
562
563   result = 0;
564   s = g_udev_device_get_property (device, key);
565   if (s == NULL)
566     goto out;
567
568   result = g_ascii_strtoull (s, NULL, 0);
569 out:
570   return result;
571 }
572
573 /**
574  * g_udev_device_get_property_as_double:
575  * @device: A #GUdevDevice.
576  * @key: Name of property.
577  *
578  * Look up the value for @key on @device and convert it to a double
579  * precision floating point number using strtod().
580  *
581  * Returns: The value for @key or 0.0 if @key doesn't exist or isn't a
582  * #gdouble.
583  */
584 gdouble
585 g_udev_device_get_property_as_double (GUdevDevice  *device,
586                                       const gchar  *key)
587 {
588   gdouble result;
589   const gchar *s;
590
591   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0.0);
592   g_return_val_if_fail (key != NULL, 0.0);
593
594   result = 0.0;
595   s = g_udev_device_get_property (device, key);
596   if (s == NULL)
597     goto out;
598
599   result = strtod (s, NULL);
600 out:
601   return result;
602 }
603
604 /**
605  * g_udev_device_get_property_as_boolean:
606  * @device: A #GUdevDevice.
607  * @key: Name of property.
608  *
609  * Look up the value for @key on @device and convert it to an
610  * boolean. This is done by doing a case-insensitive string comparison
611  * on the string value against "1" and "true".
612  *
613  * Returns: The value for @key or %FALSE if @key doesn't exist or
614  * isn't a #gboolean.
615  */
616 gboolean
617 g_udev_device_get_property_as_boolean (GUdevDevice  *device,
618                                        const gchar  *key)
619 {
620   gboolean result;
621   const gchar *s;
622
623   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE);
624   g_return_val_if_fail (key != NULL, FALSE);
625
626   result = FALSE;
627   s = g_udev_device_get_property (device, key);
628   if (s == NULL)
629     goto out;
630
631   if (strcmp (s, "1") == 0 || g_ascii_strcasecmp (s, "true") == 0)
632     result = TRUE;
633  out:
634   return result;
635 }
636
637 static gchar **
638 split_at_whitespace (const gchar *s)
639 {
640   gchar **result;
641   guint n;
642   guint m;
643
644   result = g_strsplit_set (s, " \v\t\r\n", 0);
645
646   /* remove empty strings, thanks GLib */
647   for (n = 0; result[n] != NULL; n++)
648     {
649       if (strlen (result[n]) == 0)
650         {
651           g_free (result[n]);
652           for (m = n; result[m] != NULL; m++)
653             result[m] = result[m + 1];
654           n--;
655         }
656     }
657
658   return result;
659 }
660
661 /**
662  * g_udev_device_get_property_as_strv:
663  * @device: A #GUdevDevice.
664  * @key: Name of property.
665  *
666  * Look up the value for @key on @device and return the result of
667  * splitting it into non-empty tokens split at white space (only space
668  * (' '), form-feed ('\f'), newline ('\n'), carriage return ('\r'),
669  * horizontal tab ('\t'), and vertical tab ('\v') are considered; the
670  * locale is not taken into account).
671  *
672  * Returns: (nullable) (transfer none) (array zero-terminated=1) (element-type utf8):
673  * The value of @key on @device split into tokens or %NULL if @key
674  * doesn't exist. This array is owned by @device and should not be
675  * freed by the caller.
676  */
677 const gchar* const *
678 g_udev_device_get_property_as_strv (GUdevDevice  *device,
679                                     const gchar  *key)
680 {
681   gchar **result;
682   const gchar *s;
683
684   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
685   g_return_val_if_fail (key != NULL, NULL);
686
687   if (device->priv->prop_strvs != NULL)
688     {
689       result = g_hash_table_lookup (device->priv->prop_strvs, key);
690       if (result != NULL)
691         goto out;
692     }
693
694   result = NULL;
695   s = g_udev_device_get_property (device, key);
696   if (s == NULL)
697     goto out;
698
699   result = split_at_whitespace (s);
700   if (result == NULL)
701     goto out;
702
703   if (device->priv->prop_strvs == NULL)
704     device->priv->prop_strvs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_strfreev);
705   g_hash_table_insert (device->priv->prop_strvs, g_strdup (key), result);
706
707 out:
708   return (const gchar* const *) result;
709 }
710
711 /* ---------------------------------------------------------------------------------------------------- */
712
713 /**
714  * g_udev_device_get_sysfs_attr_keys:
715  * @device: A #GUdevDevice.
716  *
717  * Gets all keys for sysfs attributes on @device.
718  *
719  * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): A %NULL terminated string array of sysfs attribute keys. This array is owned by @device and should not be freed by the caller.
720  */
721 const gchar * const *
722 g_udev_device_get_sysfs_attr_keys (GUdevDevice *device)
723 {
724   struct udev_list_entry *l;
725   GPtrArray *p;
726
727   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
728
729   if (device->priv->sysfs_attr_keys != NULL)
730     goto out;
731
732   p = g_ptr_array_new ();
733   for (l = udev_device_get_sysattr_list_entry (device->priv->udevice); l != NULL; l = udev_list_entry_get_next (l))
734     {
735       g_ptr_array_add (p, g_strdup (udev_list_entry_get_name (l)));
736     }
737   g_ptr_array_add (p, NULL);
738   device->priv->sysfs_attr_keys = (gchar **) g_ptr_array_free (p, FALSE);
739
740  out:
741   return (const gchar * const *) device->priv->sysfs_attr_keys;
742 }
743
744 /**
745  * g_udev_device_has_sysfs_attr:
746  * @device: A #GUdevDevice.
747  * @key: Name of sysfs attribute.
748  *
749  * Check if a the sysfs attribute with the given key exists.
750  *
751  * Returns: %TRUE only if the value for @key exist.
752  */
753 gboolean
754 g_udev_device_has_sysfs_attr (GUdevDevice  *device,
755                             const gchar  *key)
756 {
757   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE);
758   g_return_val_if_fail (key != NULL, FALSE);
759   return udev_device_get_sysattr_value (device->priv->udevice, key) != NULL;
760 }
761
762 /**
763  * g_udev_device_get_sysfs_attr:
764  * @device: A #GUdevDevice.
765  * @name: Name of the sysfs attribute.
766  *
767  * Look up the sysfs attribute with @name on @device.
768  *
769  * Returns: (nullable): The value of the sysfs attribute or %NULL if
770  * there is no such attribute. Do not free this string, it is owned by
771  * @device.
772  */
773 const gchar *
774 g_udev_device_get_sysfs_attr (GUdevDevice  *device,
775                               const gchar  *name)
776 {
777   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
778   g_return_val_if_fail (name != NULL, NULL);
779   return udev_device_get_sysattr_value (device->priv->udevice, name);
780 }
781
782 /**
783  * g_udev_device_get_sysfs_attr_as_int:
784  * @device: A #GUdevDevice.
785  * @name: Name of the sysfs attribute.
786  *
787  * Look up the sysfs attribute with @name on @device and convert it to an integer
788  * using strtol().
789  *
790  * Returns: The value of the sysfs attribute or 0 if there is no such
791  * attribute.
792  */
793 gint
794 g_udev_device_get_sysfs_attr_as_int (GUdevDevice  *device,
795                                      const gchar  *name)
796 {
797   gint result;
798   const gchar *s;
799
800   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0);
801   g_return_val_if_fail (name != NULL, 0);
802
803   result = 0;
804   s = g_udev_device_get_sysfs_attr (device, name);
805   if (s == NULL)
806     goto out;
807
808   result = strtol (s, NULL, 0);
809 out:
810   return result;
811 }
812
813 /**
814  * g_udev_device_get_sysfs_attr_as_uint64:
815  * @device: A #GUdevDevice.
816  * @name: Name of the sysfs attribute.
817  *
818  * Look up the sysfs attribute with @name on @device and convert it to an unsigned
819  * 64-bit integer using g_ascii_strtoull().
820  *
821  * Returns: The value of the sysfs attribute or 0 if there is no such
822  * attribute.
823  */
824 guint64
825 g_udev_device_get_sysfs_attr_as_uint64 (GUdevDevice  *device,
826                                         const gchar  *name)
827 {
828   guint64 result;
829   const gchar *s;
830
831   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0);
832   g_return_val_if_fail (name != NULL, 0);
833
834   result = 0;
835   s = g_udev_device_get_sysfs_attr (device, name);
836   if (s == NULL)
837     goto out;
838
839   result = g_ascii_strtoull (s, NULL, 0);
840 out:
841   return result;
842 }
843
844 /**
845  * g_udev_device_get_sysfs_attr_as_double:
846  * @device: A #GUdevDevice.
847  * @name: Name of the sysfs attribute.
848  *
849  * Look up the sysfs attribute with @name on @device and convert it to a double
850  * precision floating point number using strtod().
851  *
852  * Returns: The value of the sysfs attribute or 0.0 if there is no such
853  * attribute.
854  */
855 gdouble
856 g_udev_device_get_sysfs_attr_as_double (GUdevDevice  *device,
857                                         const gchar  *name)
858 {
859   gdouble result;
860   const gchar *s;
861
862   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0.0);
863   g_return_val_if_fail (name != NULL, 0.0);
864
865   result = 0.0;
866   s = g_udev_device_get_sysfs_attr (device, name);
867   if (s == NULL)
868     goto out;
869
870   result = strtod (s, NULL);
871 out:
872   return result;
873 }
874
875 /**
876  * g_udev_device_get_sysfs_attr_as_boolean:
877  * @device: A #GUdevDevice.
878  * @name: Name of the sysfs attribute.
879  *
880  * Look up the sysfs attribute with @name on @device and convert it to an
881  * boolean. This is done by doing a case-insensitive string comparison
882  * on the string value against "1" and "true".
883  *
884  * Returns: The value of the sysfs attribute or %FALSE if there is no such
885  * attribute.
886  */
887 gboolean
888 g_udev_device_get_sysfs_attr_as_boolean (GUdevDevice  *device,
889                                          const gchar  *name)
890 {
891   gboolean result;
892   const gchar *s;
893
894   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE);
895   g_return_val_if_fail (name != NULL, FALSE);
896
897   result = FALSE;
898   s = g_udev_device_get_sysfs_attr (device, name);
899   if (s == NULL)
900     goto out;
901
902   if (strcmp (s, "1") == 0 || g_ascii_strcasecmp (s, "true") == 0)
903     result = TRUE;
904  out:
905   return result;
906 }
907
908 /**
909  * g_udev_device_get_sysfs_attr_as_strv:
910  * @device: A #GUdevDevice.
911  * @name: Name of the sysfs attribute.
912  *
913  * Look up the sysfs attribute with @name on @device and return the result of
914  * splitting it into non-empty tokens split at white space (only space (' '),
915  * form-feed ('\f'), newline ('\n'), carriage return ('\r'), horizontal
916  * tab ('\t'), and vertical tab ('\v') are considered; the locale is
917  * not taken into account).
918  *
919  * Returns: (nullable) (transfer none) (array zero-terminated=1) (element-type utf8):
920  * The value of the sysfs attribute split into tokens or %NULL if
921  * there is no such attribute. This array is owned by @device and
922  * should not be freed by the caller.
923  */
924 const gchar * const *
925 g_udev_device_get_sysfs_attr_as_strv (GUdevDevice  *device,
926                                       const gchar  *name)
927 {
928   gchar **result;
929   const gchar *s;
930
931   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
932   g_return_val_if_fail (name != NULL, NULL);
933
934   if (device->priv->sysfs_attr_strvs != NULL)
935     {
936       result = g_hash_table_lookup (device->priv->sysfs_attr_strvs, name);
937       if (result != NULL)
938         goto out;
939     }
940
941   result = NULL;
942   s = g_udev_device_get_sysfs_attr (device, name);
943   if (s == NULL)
944     goto out;
945
946   result = split_at_whitespace (s);
947   if (result == NULL)
948     goto out;
949
950   if (device->priv->sysfs_attr_strvs == NULL)
951     device->priv->sysfs_attr_strvs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_strfreev);
952   g_hash_table_insert (device->priv->sysfs_attr_strvs, g_strdup (name), result);
953
954 out:
955   return (const gchar* const *) result;
956 }
957
958 /**
959  * g_udev_device_get_tags:
960  * @device: A #GUdevDevice.
961  *
962  * Gets all tags for @device.
963  *
964  * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): A %NULL terminated string array of tags. This array is owned by @device and should not be freed by the caller.
965  *
966  * Since: 165
967  */
968 const gchar* const *
969 g_udev_device_get_tags (GUdevDevice  *device)
970 {
971   struct udev_list_entry *l;
972   GPtrArray *p;
973
974   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
975
976   if (device->priv->tags != NULL)
977     goto out;
978
979   p = g_ptr_array_new ();
980   for (l = udev_device_get_tags_list_entry (device->priv->udevice); l != NULL; l = udev_list_entry_get_next (l))
981     {
982       g_ptr_array_add (p, g_strdup (udev_list_entry_get_name (l)));
983     }
984   g_ptr_array_add (p, NULL);
985   device->priv->tags = (gchar **) g_ptr_array_free (p, FALSE);
986
987  out:
988   return (const gchar * const *) device->priv->tags;
989 }
990
991 /**
992  * g_udev_device_get_is_initialized:
993  * @device: A #GUdevDevice.
994  *
995  * Gets whether @device has been initalized.
996  *
997  * Returns: Whether @device has been initialized.
998  *
999  * Since: 165
1000  */
1001 gboolean
1002 g_udev_device_get_is_initialized (GUdevDevice  *device)
1003 {
1004   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE);
1005   return udev_device_get_is_initialized (device->priv->udevice);
1006 }
1007
1008 /**
1009  * g_udev_device_get_usec_since_initialized:
1010  * @device: A #GUdevDevice.
1011  *
1012  * Gets number of micro-seconds since @device was initialized.
1013  *
1014  * This only works for devices with properties in the udev
1015  * database. All other devices return 0.
1016  *
1017  * Returns: Number of micro-seconds since @device was initialized or 0 if unknown.
1018  *
1019  * Since: 165
1020  */
1021 guint64
1022 g_udev_device_get_usec_since_initialized (GUdevDevice *device)
1023 {
1024   g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0);
1025   return udev_device_get_usec_since_initialized (device->priv->udevice);
1026 }