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