summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien Nocera <hadess@hadess.net>2020-09-14 15:15:36 +0200
committerBastien Nocera <hadess@hadess.net>2020-09-15 10:26:44 +0200
commitb5363fe829c16b31a0c8250c2c8c9a17dd8e08bf (patch)
treed88ee359ef01793c870b322e5e602cfae10b3042
parent25730cdda1caa2b44fffbf0b8b28c4b1fb3d24c6 (diff)
downloadlibgudev-b5363fe829c16b31a0c8250c2c8c9a17dd8e08bf.tar.gz
gudev: Add helpers to get uncached sysfs attributeswip/hadess/add-uncached-helpers
We very often need to access the current value of sysfs attributes. Add functions that do I/O on the sysfs files and update the cache.
-rw-r--r--docs/gudev-sections.txt7
-rw-r--r--gudev/gudevdevice.c260
-rw-r--r--gudev/gudevdevice.h15
-rw-r--r--libgudev-1.0.sym7
-rw-r--r--tests/Makefile.am6
-rw-r--r--tests/test-sysfsattr.c80
6 files changed, 367 insertions, 8 deletions
diff --git a/docs/gudev-sections.txt b/docs/gudev-sections.txt
index 90765ee..86efd13 100644
--- a/docs/gudev-sections.txt
+++ b/docs/gudev-sections.txt
@@ -61,6 +61,13 @@ g_udev_device_get_sysfs_attr_as_uint64
g_udev_device_get_sysfs_attr_as_double
g_udev_device_get_sysfs_attr_as_boolean
g_udev_device_get_sysfs_attr_as_strv
+g_udev_device_has_sysfs_attr_uncached
+g_udev_device_get_sysfs_attr_uncached
+g_udev_device_get_sysfs_attr_as_int_uncached
+g_udev_device_get_sysfs_attr_as_uint64_uncached
+g_udev_device_get_sysfs_attr_as_double_uncached
+g_udev_device_get_sysfs_attr_as_boolean_uncached
+g_udev_device_get_sysfs_attr_as_strv_uncached
<SUBSECTION Standard>
G_UDEV_DEVICE
G_UDEV_IS_DEVICE
diff --git a/gudev/gudevdevice.c b/gudev/gudevdevice.c
index 631d126..df6ebd1 100644
--- a/gudev/gudevdevice.c
+++ b/gudev/gudevdevice.c
@@ -91,6 +91,7 @@ struct _GUdevDevicePrivate
gchar **tags;
GHashTable *prop_strvs;
GHashTable *sysfs_attr_strvs;
+ GHashTable *sysfs_attr;
};
G_DEFINE_TYPE_WITH_CODE (GUdevDevice, g_udev_device, G_TYPE_OBJECT, G_ADD_PRIVATE(GUdevDevice))
@@ -114,6 +115,9 @@ g_udev_device_finalize (GObject *object)
if (device->priv->sysfs_attr_strvs != NULL)
g_hash_table_unref (device->priv->sysfs_attr_strvs);
+ if (device->priv->sysfs_attr != NULL)
+ g_hash_table_unref (device->priv->sysfs_attr);
+
if (G_OBJECT_CLASS (g_udev_device_parent_class)->finalize != NULL)
(* G_OBJECT_CLASS (g_udev_device_parent_class)->finalize) (object);
}
@@ -140,6 +144,10 @@ _g_udev_device_new (struct udev_device *udevice)
device = G_UDEV_DEVICE (g_object_new (G_UDEV_TYPE_DEVICE, NULL));
device->priv->udevice = udev_device_ref (udevice);
+ device->priv->sysfs_attr = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_free);
return device;
}
@@ -746,7 +754,8 @@ g_udev_device_get_sysfs_attr_keys (GUdevDevice *device)
* Check if a the sysfs attribute with the given key exists. The
* retrieved value is cached in the device. Repeated calls will
* return the same result and not check for the presence of the
- * attribute again.
+ * attribute again, unless updated through one of the "uncached"
+ * functions.
*
* Returns: %TRUE only if the value for @key exist.
*/
@@ -766,7 +775,8 @@ g_udev_device_has_sysfs_attr (GUdevDevice *device,
*
* Look up the sysfs attribute with @name on @device. The retrieved value
* is cached in the device. Repeated calls will return the same value and
- * not open the attribute again.
+ * not open the attribute again, unless updated through one of the
+ * "uncached" functions.
*
* Returns: (nullable): The value of the sysfs attribute or %NULL if
* there is no such attribute. Do not free this string, it is owned by
@@ -776,8 +786,14 @@ const gchar *
g_udev_device_get_sysfs_attr (GUdevDevice *device,
const gchar *name)
{
+ const char *attr;
+
g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
g_return_val_if_fail (name != NULL, NULL);
+
+ attr = g_hash_table_lookup (device->priv->sysfs_attr, name);
+ if (attr)
+ return attr;
return udev_device_get_sysattr_value (device->priv->udevice, name);
}
@@ -788,7 +804,8 @@ g_udev_device_get_sysfs_attr (GUdevDevice *device,
*
* Look up the sysfs attribute with @name on @device and convert it to an integer
* using strtol(). The retrieved value is cached in the device. Repeated calls
- * will return the same value and not open the attribute again.
+ * will return the same value and not open the attribute again, unless updated
+ * through one of the "uncached" functions.
*
* Returns: The value of the sysfs attribute or 0 if there is no such
* attribute.
@@ -821,7 +838,7 @@ out:
* Look up the sysfs attribute with @name on @device and convert it to an unsigned
* 64-bit integer using g_ascii_strtoull(). The retrieved value is cached in the
* device. Repeated calls will return the same value and not open the attribute
- * again.
+ * again, unless updated through one of the "uncached" functions.
*
* Returns: The value of the sysfs attribute or 0 if there is no such
* attribute.
@@ -854,7 +871,7 @@ out:
* Look up the sysfs attribute with @name on @device and convert it to a double
* precision floating point number using strtod(). The retrieved value is cached
* in the device. Repeated calls will return the same value and not open the
- * attribute again.
+ * attribute again, unless updated through one of the "uncached" functions.
*
* Returns: The value of the sysfs attribute or 0.0 if there is no such
* attribute.
@@ -888,7 +905,8 @@ out:
* boolean. This is done by doing a case-insensitive string comparison
* on the string value against "1" and "true". The retrieved value is
* cached in the device. Repeated calls will return the same value and
- * not open the attribute again.
+ * not open the attribute again, unless updated through one of the
+ * "uncached" functions.
*
* Returns: The value of the sysfs attribute or %FALSE if there is no such
* attribute.
@@ -926,7 +944,8 @@ g_udev_device_get_sysfs_attr_as_boolean (GUdevDevice *device,
* not taken into account).
*
* The retrieved value is cached in the device. Repeated calls will return
- * the same value and not open the attribute again.
+ * the same value and not open the attribute again, unless updated through
+ * one of the "uncached" functions.
*
* Returns: (nullable) (transfer none) (array zero-terminated=1) (element-type utf8):
* The value of the sysfs attribute split into tokens or %NULL if
@@ -968,6 +987,233 @@ out:
}
/**
+ * g_udev_device_has_sysfs_attr_uncached:
+ * @device: A #GUdevDevice.
+ * @key: Name of sysfs attribute.
+ *
+ * Check if a the sysfs attribute with the given key exists. The
+ * retrieved value is cached in the device. Repeated calls will
+ * return the same result and not check for the presence of the
+ * attribute again, unless updated through one of the "uncached"
+ * functions.
+ *
+ * Returns: %TRUE only if the value for @key exist.
+ */
+gboolean
+g_udev_device_has_sysfs_attr_uncached (GUdevDevice *device,
+ const gchar *key)
+{
+ g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+ return g_udev_device_get_sysfs_attr_uncached (device, key) != NULL;
+}
+
+/**
+ * g_udev_device_get_sysfs_attr_uncached:
+ * @device: A #GUdevDevice.
+ * @name: Name of the sysfs attribute.
+ *
+ * Look up the sysfs attribute with @name on @device. This function does
+ * blocking I/O, and updates the sysfs attributes cache.
+ *
+ * Returns: (nullable): The value of the sysfs attribute or %NULL if
+ * there is no such attribute. Do not free this string, it is owned by
+ * @device.
+ */
+const gchar *
+g_udev_device_get_sysfs_attr_uncached (GUdevDevice *device,
+ const gchar *name)
+{
+ g_autofree char *path = NULL;
+ char *contents = NULL;
+
+ g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ path = g_build_filename (udev_device_get_syspath (device->priv->udevice), name, NULL);
+ if (!g_file_get_contents (path, &contents, NULL, NULL))
+ return NULL;
+ g_hash_table_insert (device->priv->sysfs_attr, g_strdup (name), contents);
+
+ return contents;
+}
+
+/**
+ * g_udev_device_get_sysfs_attr_as_int_uncached:
+ * @device: A #GUdevDevice.
+ * @name: Name of the sysfs attribute.
+ *
+ * Look up the sysfs attribute with @name on @device and convert it to an integer
+ * using strtol(). This function does blocking I/O, and updates the sysfs
+ * attributes cache.
+ *
+ * Returns: The value of the sysfs attribute or 0 if there is no such
+ * attribute.
+ */
+gint
+g_udev_device_get_sysfs_attr_as_int_uncached (GUdevDevice *device,
+ const gchar *name)
+{
+ gint result;
+ const gchar *s;
+
+ g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0);
+ g_return_val_if_fail (name != NULL, 0);
+
+ result = 0;
+ s = g_udev_device_get_sysfs_attr_uncached (device, name);
+ if (s == NULL)
+ goto out;
+
+ result = strtol (s, NULL, 0);
+out:
+ return result;
+}
+
+/**
+ * g_udev_device_get_sysfs_attr_as_uint64_uncached:
+ * @device: A #GUdevDevice.
+ * @name: Name of the sysfs attribute.
+ *
+ * Look up the sysfs attribute with @name on @device and convert it to an unsigned
+ * 64-bit integer using g_ascii_strtoull(). This function does blocking I/O, and
+ * updates the sysfs attributes cache.
+ *
+ * Returns: The value of the sysfs attribute or 0 if there is no such
+ * attribute.
+ */
+guint64
+g_udev_device_get_sysfs_attr_as_uint64_uncached (GUdevDevice *device,
+ const gchar *name)
+{
+ guint64 result;
+ const gchar *s;
+
+ g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0);
+ g_return_val_if_fail (name != NULL, 0);
+
+ result = 0;
+ s = g_udev_device_get_sysfs_attr_uncached (device, name);
+ if (s == NULL)
+ goto out;
+
+ result = g_ascii_strtoull (s, NULL, 0);
+out:
+ return result;
+}
+
+/**
+ * g_udev_device_get_sysfs_attr_as_double_uncached:
+ * @device: A #GUdevDevice.
+ * @name: Name of the sysfs attribute.
+ *
+ * Look up the sysfs attribute with @name on @device and convert it to a double
+ * precision floating point number using strtod(). This function does blocking
+ * I/O, and updates the sysfs attributes cache.
+ *
+ * Returns: The value of the sysfs attribute or 0.0 if there is no such
+ * attribute.
+ */
+gdouble
+g_udev_device_get_sysfs_attr_as_double_uncached (GUdevDevice *device,
+ const gchar *name)
+{
+ gdouble result;
+ const gchar *s;
+
+ g_return_val_if_fail (G_UDEV_IS_DEVICE (device), 0.0);
+ g_return_val_if_fail (name != NULL, 0.0);
+
+ result = 0.0;
+ s = g_udev_device_get_sysfs_attr_uncached (device, name);
+ if (s == NULL)
+ goto out;
+
+ result = strtod (s, NULL);
+out:
+ return result;
+}
+
+/**
+ * g_udev_device_get_sysfs_attr_as_boolean_uncached:
+ * @device: A #GUdevDevice.
+ * @name: Name of the sysfs attribute.
+ *
+ * Look up the sysfs attribute with @name on @device and convert it to an
+ * boolean. This is done by doing a case-insensitive string comparison
+ * on the string value against "1" and "true". This function does
+ * blocking I/O, and updates the sysfs attributes cache.
+ *
+ * Returns: The value of the sysfs attribute or %FALSE if there is no such
+ * attribute.
+ */
+gboolean
+g_udev_device_get_sysfs_attr_as_boolean_uncached (GUdevDevice *device,
+ const gchar *name)
+{
+ gboolean result;
+ const gchar *s;
+
+ g_return_val_if_fail (G_UDEV_IS_DEVICE (device), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ result = FALSE;
+ s = g_udev_device_get_sysfs_attr_uncached (device, name);
+ if (s == NULL)
+ goto out;
+
+ if (strcmp (s, "1") == 0 || g_ascii_strcasecmp (s, "true") == 0)
+ result = TRUE;
+ out:
+ return result;
+}
+
+/**
+ * g_udev_device_get_sysfs_attr_as_strv_uncached:
+ * @device: A #GUdevDevice.
+ * @name: Name of the sysfs attribute.
+ *
+ * Look up the sysfs attribute with @name on @device and return the result of
+ * splitting it into non-empty tokens split at white space (only space (' '),
+ * form-feed ('\f'), newline ('\n'), carriage return ('\r'), horizontal
+ * tab ('\t'), and vertical tab ('\v') are considered; the locale is
+ * not taken into account).
+ *
+ * This function does blocking I/O, and updates the sysfs attributes cache.
+ *
+ * Returns: (nullable) (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.
+ */
+const gchar * const *
+g_udev_device_get_sysfs_attr_as_strv_uncached (GUdevDevice *device,
+ const gchar *name)
+{
+ gchar **result;
+ const gchar *s;
+
+ g_return_val_if_fail (G_UDEV_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ result = NULL;
+ s = g_udev_device_get_sysfs_attr_uncached (device, name);
+ if (s == NULL)
+ goto out;
+
+ result = split_at_whitespace (s);
+ if (result == NULL)
+ goto out;
+
+ if (device->priv->sysfs_attr_strvs == NULL)
+ device->priv->sysfs_attr_strvs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_strfreev);
+ g_hash_table_insert (device->priv->sysfs_attr_strvs, g_strdup (name), result);
+
+out:
+ return (const gchar* const *) result;
+}
+
+/**
* g_udev_device_get_tags:
* @device: A #GUdevDevice.
*
diff --git a/gudev/gudevdevice.h b/gudev/gudevdevice.h
index 4691ce0..dfcecd6 100644
--- a/gudev/gudevdevice.h
+++ b/gudev/gudevdevice.h
@@ -129,6 +129,21 @@ const gchar* const *g_udev_device_get_sysfs_attr_as_strv (GUdevDevice *devic
const gchar *name);
const gchar* const *g_udev_device_get_tags (GUdevDevice *device);
+gboolean g_udev_device_has_sysfs_attr_uncached (GUdevDevice *device,
+ const gchar *key);
+const gchar *g_udev_device_get_sysfs_attr_uncached (GUdevDevice *device,
+ const gchar *name);
+gint g_udev_device_get_sysfs_attr_as_int_uncached (GUdevDevice *device,
+ const gchar *name);
+guint64 g_udev_device_get_sysfs_attr_as_uint64_uncached (GUdevDevice *device,
+ const gchar *name);
+gdouble g_udev_device_get_sysfs_attr_as_double_uncached (GUdevDevice *device,
+ const gchar *name);
+gboolean g_udev_device_get_sysfs_attr_as_boolean_uncached (GUdevDevice *device,
+ const gchar *name);
+const gchar* const *g_udev_device_get_sysfs_attr_as_strv_uncached (GUdevDevice *device,
+ const gchar *name);
+
G_END_DECLS
#endif /* __G_UDEV_DEVICE_H__ */
diff --git a/libgudev-1.0.sym b/libgudev-1.0.sym
index f4cd038..ab9cccf 100644
--- a/libgudev-1.0.sym
+++ b/libgudev-1.0.sym
@@ -34,6 +34,13 @@ global:
g_udev_device_get_sysfs_attr_as_int;
g_udev_device_get_sysfs_attr_as_strv;
g_udev_device_get_sysfs_attr_as_uint64;
+ g_udev_device_has_sysfs_attr_uncached;
+ g_udev_device_get_sysfs_attr_uncached;
+ g_udev_device_get_sysfs_attr_as_int_uncached;
+ g_udev_device_get_sysfs_attr_as_uint64_uncached;
+ g_udev_device_get_sysfs_attr_as_double_uncached;
+ g_udev_device_get_sysfs_attr_as_boolean_uncached;
+ g_udev_device_get_sysfs_attr_as_strv_uncached;
g_udev_device_get_sysfs_attr_keys;
g_udev_device_get_sysfs_path;
g_udev_device_get_tags;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 4352a81..4cb60ca 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -4,13 +4,17 @@ all-local: check-local
if HAVE_UMOCKDEV
-noinst_PROGRAMS = test-enumerator-filter
+noinst_PROGRAMS = test-enumerator-filter test-sysfsattr
TEST_PROGS += $(noinst_PROGRAMS)
test_enumerator_filter_SOURCES = test-enumerator-filter.c
test_enumerator_filter_CFLAGS = $(UMOCKDEV_CFLAGS) -I$(top_srcdir)
test_enumerator_filter_LDADD = $(UMOCKDEV_LIBS) $(top_builddir)/libgudev-1.0.la
+test_sysfsattr_SOURCES = test-sysfsattr.c
+test_sysfsattr_CFLAGS = $(UMOCKDEV_CFLAGS) -I$(top_srcdir)
+test_sysfsattr_LDADD = $(UMOCKDEV_LIBS) $(top_builddir)/libgudev-1.0.la
+
endif
EXTRA_DIST = test-enumerator-filter.c
diff --git a/tests/test-sysfsattr.c b/tests/test-sysfsattr.c
new file mode 100644
index 0000000..b8b2ec1
--- /dev/null
+++ b/tests/test-sysfsattr.c
@@ -0,0 +1,80 @@
+/* umockdev example: use libumockdev in C to fake a battery
+ * Build with:
+ * gcc battery.c -Wall `pkg-config --cflags --libs umockdev-1.0 gio-2.0` -o /tmp/battery
+ * Run with:
+ * umockdev-wrapper /tmp/battery
+ *
+ * Copyright (C) 2013 Canonical Ltd.
+ * Author: Martin Pitt <martin.pitt@ubuntu.com>
+ *
+ * umockdev is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * umockdev is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <locale.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/wait.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <umockdev.h>
+
+#include <gudev/gudev.h>
+
+static void
+test_uncached_sysfs_attr (void)
+{
+ /* create test bed */
+ UMockdevTestbed *testbed = umockdev_testbed_new ();
+
+ /* Relies on a test bed having been set up */
+ g_assert (umockdev_in_mock_environment ());
+
+ umockdev_testbed_add_device (testbed, "platform", "dev1", NULL,
+ "dytc_lapmode", "0", NULL,
+ "ID_MODEL", "KoolGadget", NULL);
+
+ /* Check the number of items in GUdevClient */
+ const gchar *subsystems[] = { "platform", NULL};
+ GUdevClient *client = g_udev_client_new (subsystems);
+ GUdevDevice *dev;
+ g_autofree char *lapmode_path = NULL;
+ FILE *sysfsfp;
+
+ GList *devices = g_udev_client_query_by_subsystem (client, NULL);
+ g_assert_cmpint (g_list_length (devices), ==, 1);
+ dev = devices->data;
+ lapmode_path = g_build_filename (g_udev_device_get_sysfs_path (dev), "dytc_lapmode", NULL);
+ /* First access */
+ g_assert_false (g_udev_device_get_sysfs_attr_as_boolean (dev, "dytc_lapmode"));
+ sysfsfp = fopen (lapmode_path, "w");
+ fprintf (sysfsfp, "%s", "1");
+ fclose (sysfsfp);
+ /* This is cached */
+ g_assert_false (g_udev_device_get_sysfs_attr_as_boolean (dev, "dytc_lapmode"));
+ /* This is uncached, and updates the cache */
+ g_assert_true (g_udev_device_get_sysfs_attr_as_boolean_uncached (dev, "dytc_lapmode"));
+ g_assert_true (g_udev_device_get_sysfs_attr_as_boolean (dev, "dytc_lapmode"));
+
+ g_list_free_full (devices, g_object_unref);
+}
+
+int main(int argc, char **argv)
+{
+ setlocale (LC_ALL, NULL);
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/gudev/uncached_sysfs_attr", test_uncached_sysfs_attr);
+
+ return g_test_run ();
+}