* Re: [PATCH] udev: sync udev rules directories with systemd units directories
From: Marco d'Itri @ 2012-07-14 3:46 UTC (permalink / raw)
To: linux-hotplug
In-Reply-To: <1342214503-7931-1-git-send-email-w.d.hubbs@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 519 bytes --]
On Jul 14, William Hubbs <w.d.hubbs@gmail.com> wrote:
> /usr/lib/udev/rules.d. The reason is not because of udev, but other
> packages which install their rules in /lib/udev/rules.d need to be fixed
> to install them in /usr/lib/udev/rules.d.
As the maintainer of udev for a distribution which still supports a
standalone /usr I fail to see how this can be an issue.
Why should an upstream package install something in
/usr/lib/udev/rules.d/ if no distribution supports this directory?
--
ciao,
Marco
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* Re: [PATCH] udev: sync udev rules directories with systemd units directories
From: William Hubbs @ 2012-07-14 0:33 UTC (permalink / raw)
To: linux-hotplug
In-Reply-To: <1342214503-7931-1-git-send-email-w.d.hubbs@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 618 bytes --]
On Sat, Jul 14, 2012 at 01:06:42AM +0200, Kay Sievers wrote:
> It's a mess you are creating here, by not just merging /lib and
> /usr/lib right away.
I'm not interested in the binaries being installed in /lib/udev. We just
need to be able to read rules from /lib/udev/rules.d along with
/usr/lib/udev/rules.d. The reason is not because of udev, but other
packages which install their rules in /lib/udev/rules.d need to be fixed
to install them in /usr/lib/udev/rules.d.
We are working on fixing things so we can merge /lib into /usr/lib, but
that is a process for us and can't happen over night.
Thanks,
William
[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* Re: [PATCH] udev: sync udev rules directories with systemd units directories
From: Kay Sievers @ 2012-07-13 23:06 UTC (permalink / raw)
To: linux-hotplug
In-Reply-To: <1342214503-7931-1-git-send-email-w.d.hubbs@gmail.com>
On Sat, Jul 14, 2012 at 12:48 AM, William Hubbs <w.d.hubbs@gmail.com> wrote:
> On Fri, Jul 13, 2012 at 11:38:24PM +0200, Kay Sievers wrote:
>> > TEST_PREFIX UDEVLIBEXECDIR "/rules.d",
>> > + TEST_PREFIX "/usr/lib/udev/rules.d",
>> > +#ifdef HAVE_SPLIT_USR
>> > + TEST_PREFIX "/lib/udev/rules.d",
>> > +#endif
Now we have 3 instead of one directory here, that really needs to be
sorted out. It's not cool to just add every random thing here. :)
>> I have no problem with that, even when I think it's the wrong way to
>> do these things. It just creates mess, that is much bigger than the
>> split-usr ever was. But we have it in systemd, and there is no real
>> argument for me to make against it.
>
> I'm personally not concerned with /usr/local, but the portion above is
> the more important to me. For split /usr systems, there is a lot of
> software that installs rules in /lib/udev, so I think we need to support
> that directory.
You are aware that this will not work with the installed binaries in
/lib/udev/ referenced from rules and whatever? The code is really not
made to have this I-read-stuff-from-all-possible locations.
It's a mess you are creating here, by not just merging /lib and
/usr/lib right away.
> Should I resubmit the patch?
Nah, we need to decide what to do first, I'll let you know.
Kay
^ permalink raw reply
* Re: [PATCH] udev: sync udev rules directories with systemd units directories
From: William Hubbs @ 2012-07-13 22:48 UTC (permalink / raw)
To: linux-hotplug
In-Reply-To: <1342214503-7931-1-git-send-email-w.d.hubbs@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1430 bytes --]
On Fri, Jul 13, 2012 at 11:38:24PM +0200, Kay Sievers wrote:
> Udev will *never* look in /usr/local, that is no place for
> system-level software. My take on this is: early boot has absolutely
> zero business in /usr/local.
>
> I see that you just want to sync this, but I will not allow /usr/local
> happen to udev. It makes no sense, does not solve any problem, creates
> nothing but needless issues with custom setups and custom mounts
> (which are not uncommon) on /usr/local that are better avoided.
>
> I tried several times to talk Lennart out of that nonsense, but wasn't
> successful so far. :)
>
> > TEST_PREFIX UDEVLIBEXECDIR "/rules.d",
> > + TEST_PREFIX "/usr/lib/udev/rules.d",
> > +#ifdef HAVE_SPLIT_USR
> > + TEST_PREFIX "/lib/udev/rules.d",
> > +#endif
>
> I have no problem with that, even when I think it's the wrong way to
> do these things. It just creates mess, that is much bigger than the
> split-usr ever was. But we have it in systemd, and there is no real
> argument for me to make against it.
I'm personally not concerned with /usr/local, but the portion above is
the more important to me. For split /usr systems, there is a lot of
software that installs rules in /lib/udev, so I think we need to support
that directory.
Should I resubmit the patch?
Thanks,
William
[-- Attachment #2: Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* Re: [PATCH] udev: sync udev rules directories with systemd units directories
From: Kay Sievers @ 2012-07-13 21:38 UTC (permalink / raw)
To: linux-hotplug
In-Reply-To: <1342214503-7931-1-git-send-email-w.d.hubbs@gmail.com>
On Fri, Jul 13, 2012 at 11:21 PM, William Hubbs <w.d.hubbs@gmail.com> wrote:
> From: Michał Górny <mgorny@gentoo.org>
>
> This adds /usr/lib, /usr/local/lib, and /lib (if split-usr is enabled)
> to the directories where udev searches for rules.d. This brings udev in
> line with how systemd searches for units and it is also needed if
> split-usr is enabled because other software packages currently install
> rules in /lib/udev/rules.d.
> ---
> src/udev/udev-rules.c | 5 +++++
> 1 files changed, 5 insertions(+), 0 deletions(-)
>
> diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
> index b5b54dd..784d25e 100644
> --- a/src/udev/udev-rules.c
> +++ b/src/udev/udev-rules.c
> @@ -1759,7 +1759,12 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names)
>
> rules->dirs = strv_new(TEST_PREFIX SYSCONFDIR "/udev/rules.d",
> TEST_PREFIX "/run/udev/rules.d",
> + TEST_PREFIX "/usr/local/lib/udev/rules.d",
Udev will *never* look in /usr/local, that is no place for
system-level software. My take on this is: early boot has absolutely
zero business in /usr/local.
I see that you just want to sync this, but I will not allow /usr/local
happen to udev. It makes no sense, does not solve any problem, creates
nothing but needless issues with custom setups and custom mounts
(which are not uncommon) on /usr/local that are better avoided.
I tried several times to talk Lennart out of that nonsense, but wasn't
successful so far. :)
> TEST_PREFIX UDEVLIBEXECDIR "/rules.d",
> + TEST_PREFIX "/usr/lib/udev/rules.d",
> +#ifdef HAVE_SPLIT_USR
> + TEST_PREFIX "/lib/udev/rules.d",
> +#endif
I have no problem with that, even when I think it's the wrong way to
do these things. It just creates mess, that is much bigger than the
split-usr ever was. But we have it in systemd, and there is no real
argument for me to make against it.
Thanks,
Kay
^ permalink raw reply
* [PATCH] udev: sync udev rules directories with systemd units directories
From: William Hubbs @ 2012-07-13 21:21 UTC (permalink / raw)
To: linux-hotplug
From: Michał Górny <mgorny@gentoo.org>
This adds /usr/lib, /usr/local/lib, and /lib (if split-usr is enabled)
to the directories where udev searches for rules.d. This brings udev in
line with how systemd searches for units and it is also needed if
split-usr is enabled because other software packages currently install
rules in /lib/udev/rules.d.
---
src/udev/udev-rules.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index b5b54dd..784d25e 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -1759,7 +1759,12 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names)
rules->dirs = strv_new(TEST_PREFIX SYSCONFDIR "/udev/rules.d",
TEST_PREFIX "/run/udev/rules.d",
+ TEST_PREFIX "/usr/local/lib/udev/rules.d",
TEST_PREFIX UDEVLIBEXECDIR "/rules.d",
+ TEST_PREFIX "/usr/lib/udev/rules.d",
+#ifdef HAVE_SPLIT_USR
+ TEST_PREFIX "/lib/udev/rules.d",
+#endif
NULL);
if (!rules->dirs) {
log_error("failed to build config directory array");
--
1.7.8.6
^ permalink raw reply related
* [PATCH v2] Testbeds for libudev/gudev clients
From: Martin Pitt @ 2012-07-10 9:11 UTC (permalink / raw)
To: linux-hotplug
[-- Attachment #1.1: Type: text/plain, Size: 2508 bytes --]
Hello again,
Martin Pitt [2012-07-06 7:01 +0200]:
> I have a patch attached to generalize this to use
> $UDEV_TEST_PREFIX, replacing the current compile-time TEST_PREFIX
> macro. This will also allow us in the future to create mock dev and
> run/rules.d files without having to change the environment variable
> and temporary directory structure again. Once that (or a variant of
> it) is in, I'll adapt upower's test suite to also set
> UDEV_TEST_PREFIX so that it works with both old and new libudev
> versions.
As the original patch that changed libudev to respect
$UDEV_TEST_PREFIX was not very palatable, I now changed the approach
to use an external LD_PRELOAD wrapper, as discussed with Lucas. This
is the "no privileges" and "do not do any permanent changes to the
system" variant of Kay's namespacing proposal, and does not touch the
existing code at all. I based libudev-testbed.so on kmod's path.c,
simplified it a bit, added some missing functions, and integrated it
into the build system together with a simple "libudev-testbed" wrapper
script that you need to run test suites under.
I actually like this approach, as it allows us to extend the
functionality later on. E. g. this can easily intercept access to
/dev/ and sysctls as well.
> With that patch you can now e. g. call
>
> UDEV_TEST_PREFIX=test ./udevadm info --export-db
> UDEV_TEST_PREFIX=test ./udevadm info --query=all --path=/devices/virtual/block/md0
This now becomes
LD_LIBRARY_PATH=.libs UDEV_TEST_PREFIX=test src/test/libudev-testbed ./udevadm info --export-db
The LD_LIBRARY_PATH is just for the build tree; when using the
installed version (e. g. upower's test suite), you would use
UDEV_TEST_PREFIX=/path/to/testbed libudev-testbed /udevadm info --export-db
I also adjusted the GUdevTestbed patch a bit for the new approach.
TODOs that I am aware of which need to be fixed before committing:
- libudev-testbed needs a manpage
- GUdevTestbed needs to remove its temporary directory in the
destructor
- Write some documentation how to use this stuff (in
libudev-testbed's manpage, and some general "how to write tests for
hardware handling" wiki page on fd.o)
I'll do these once we have a general agreement about the approach in
these patches.
Thanks,
Martin
--
Martin Pitt | http://www.piware.de
Ubuntu Developer (www.ubuntu.com) | Debian Developer (www.debian.org)
[-- Attachment #1.2: 0001-Add-libudev-test-bed-wrapper.patch --]
[-- Type: text/x-diff, Size: 8578 bytes --]
From 695119a0a324de98d340ca956e92c60fba949048 Mon Sep 17 00:00:00 2001
From: Martin Pitt <martinpitt@gnome.org>
Date: Tue, 10 Jul 2012 08:48:34 +0200
Subject: [PATCH 1/2] Add libudev test bed wrapper
The removal of $SYSFS_PATH and configurable paths made it impossible for test
suites to set up a local sysfs tree and point libudev to that. Restore and
generalize the functionality with checking the $UDEV_TEST_PREFIX environment
variable, so that test suites can continue to run without root privileges and
the danger of breaking the system.
Add a small LD_PRELOAD library (based on path.c from kmod) to wrap the libc
functions that libudev needs for prefixing the paths with $UDEV_TEST_PREFIX.
Cover sysfs for now (which mostly needs open(), readlink(), and stat()), but
this approach is extensible for intercepting sysctls, device node access, and
similar things in the future as well.
Also add a small shell wrapper "libudev-testbed" which runs the argument under
the appropriate LD_PRELOAD. This is more robust for third party test suites and
also allows us to change the test bed implementation in the future. Run all
tests under this wrapper; it does not change behaviour if $UDEV_TEST_PREFIX is
not set, and will make future selftests of libudev/gudev using the new test bed
just work.
---
Makefile.am | 27 ++++++
src/test/libudev-testbed | 5 ++
src/test/libudev-testbed.c | 214 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 246 insertions(+)
create mode 100755 src/test/libudev-testbed
create mode 100644 src/test/libudev-testbed.c
diff --git a/Makefile.am b/Makefile.am
index 70b8b09..e4b8166 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -104,6 +104,7 @@ check_PROGRAMS =
check_DATA =
noinst_PROGRAMS =
TESTS =
+TESTS_ENVIRONMENT = LD_LIBRARY_PATH=.libs:$$LD_LIBRARY_PATH src/test/libudev-testbed
udevlibexec_PROGRAMS =
AM_CPPFLAGS = \
@@ -1508,6 +1509,32 @@ libudev_private_la_LIBADD = \
libsystemd-shared.la
# ------------------------------------------------------------------------------
+
+lib_LTLIBRARIES += \
+ libudev-testbed.la
+
+libudev_testbed_la_SOURCES = \
+ src/test/libudev-testbed.c
+
+libudev_testbed_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ -fvisibility=default
+
+libudev_testbed_la_LDFLAGS = \
+ $(AM_LDFLAGS)
+
+# this is an LD_PRELOAD library, so remove static library and libtool wrappers
+libudev-testbed-install-hook:
+ rm $(DESTDIR)$(libdir)/libudev-testbed.a
+ rm $(DESTDIR)$(libdir)/libudev-testbed.la
+
+INSTALL_EXEC_HOOKS += \
+ libudev-testbed-install-hook
+
+bin_SCRIPTS = \
+ src/test/libudev-testbed
+
+# ------------------------------------------------------------------------------
MANPAGES += \
man/udev.7 \
man/udevadm.8 \
diff --git a/src/test/libudev-testbed b/src/test/libudev-testbed
new file mode 100755
index 0000000..44b8258
--- /dev/null
+++ b/src/test/libudev-testbed
@@ -0,0 +1,5 @@
+#!/bin/sh
+# Wrapper program to preload the libudev-testbed shared library, so that test
+# programs can set $UDEV_TEST_PREFIX for redirecting sysfs and other queries to
+# a test bed.
+env LD_PRELOAD=libudev-testbed.so.0:$LD_PRELOAD "$@"
diff --git a/src/test/libudev-testbed.c b/src/test/libudev-testbed.c
new file mode 100644
index 0000000..0ecd211
--- /dev/null
+++ b/src/test/libudev-testbed.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2012 ProFUSION embedded systems
+ * Portions Copyright (C) 2012 Canonical Ltd.
+ * Authors:
+ * Lucas De Marchi <lucas.demarchi@profusion.mobi>
+ * Martin Pitt <martin.pitt@ubuntu.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+static void *nextlib;
+
+static inline int need_trap(const char *path)
+{
+ return path != NULL && strncmp(path, "/sys/", 5) == 0;
+}
+
+static const char *trap_path(const char *path)
+{
+ static char buf[PATH_MAX * 2];
+ const char *prefix;
+ size_t path_len, prefix_len;
+
+ if (!need_trap(path))
+ return path;
+
+ prefix = getenv("UDEV_TEST_PREFIX");
+ if (prefix == NULL)
+ return path;
+
+ path_len = strlen(path);
+ prefix_len = strlen(prefix);
+
+ if (path_len + prefix_len >= sizeof(buf)) {
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+
+ strcpy(buf, prefix);
+ strcpy(buf + prefix_len, path);
+ return buf;
+}
+
+static void *get_libc_func(const char *f)
+{
+ void *fp;
+
+ if (nextlib == NULL) {
+#ifdef RTLD_NEXT
+ nextlib = RTLD_NEXT;
+#else
+ nextlib = dlopen("libc.so.6", RTLD_LAZY);
+#endif
+ }
+
+ fp = dlsym(nextlib, f);
+ assert(fp);
+
+ return fp;
+}
+
+FILE *fopen(const char *path, const char *mode)
+{
+ const char *p;
+ static FILE* (*_fopen)(const char *path, const char *mode);
+
+ _fopen = get_libc_func("fopen");
+
+ p = trap_path(path);
+ if (p == NULL)
+ return NULL;
+
+ return (*_fopen)(p, mode);
+}
+
+int open(const char *path, int flags, ...)
+{
+ const char *p;
+ static int (*_open)(const char *path, int flags, ...);
+
+ _open = get_libc_func("open");
+ p = trap_path(path);
+ if (p == NULL)
+ return -1;
+
+ if (flags & O_CREAT) {
+ mode_t mode;
+ va_list ap;
+
+ va_start(ap, flags);
+ mode = va_arg(ap, mode_t);
+ va_end(ap);
+ return _open(p, flags, mode);
+ }
+
+ return _open(p, flags);
+}
+
+int mkdir(const char *path, mode_t mode)
+{
+ const char *p;
+ static int (*_mkdir)(const char *path, mode_t mode);
+
+ _mkdir = get_libc_func("mkdir");
+ p = trap_path(path);
+ if (p == NULL)
+ return -1;
+
+ return _mkdir(p, mode);
+}
+
+/* stat() comes in umpteen different flavours, so define them with templates */
+#define WRAP_STAT(prefix, suffix) \
+int prefix ## stat ## suffix (const char *path, struct stat ## suffix *st) \
+{ \
+ const char *p; \
+ static int (*_fn)(const char *path, struct stat ## suffix *buf);\
+ _fn = get_libc_func(#prefix "stat" #suffix); \
+ p = trap_path(path); \
+ /* printf("testbed wrapped " #prefix "stat" #suffix "(%s) -> %s\n", path, p);*/ \
+ if (p == NULL) \
+ return -1; \
+ return _fn(p, st); \
+}
+
+WRAP_STAT(,);
+WRAP_STAT(,64);
+WRAP_STAT(l,);
+WRAP_STAT(l,64);
+
+#define WRAP_VERSTAT(prefix, suffix) \
+int prefix ## stat ## suffix (int ver, const char *path, struct stat ## suffix *st) \
+{ \
+ const char *p; \
+ static int (*_fn)(int ver, const char *path, struct stat ## suffix *buf); \
+ _fn = get_libc_func(#prefix "stat" #suffix); \
+ p = trap_path(path); \
+ /* printf("testbed wrapped " #prefix "stat" #suffix "(%s) -> %s\n", path, p);*/ \
+ if (p == NULL) \
+ return -1; \
+ return _fn(ver, p, st); \
+}
+
+WRAP_VERSTAT(__x,);
+WRAP_VERSTAT(__x,64);
+WRAP_VERSTAT(__lx,);
+WRAP_VERSTAT(__lx,64);
+
+int access(const char *path, int mode)
+{
+ const char *p;
+ static int (*_access)(const char *path, int mode);
+
+ _access = get_libc_func("access");
+
+ p = trap_path(path);
+ if (p == NULL)
+ return -1;
+
+ return _access(p, mode);
+}
+
+DIR *opendir(const char *path)
+{
+ const char *p;
+ static DIR* (*_opendir)(const char *path);
+
+ _opendir = get_libc_func("opendir");
+
+ p = trap_path(path);
+ if (p == NULL)
+ return NULL;
+
+ return (*_opendir)(p);
+}
+
+ssize_t readlink(const char *path, char *buf, size_t bufsiz)
+{
+ const char *p;
+ static int (*_readlink)(const char *path, char *buf, size_t bufsiz);
+
+ _readlink = get_libc_func("readlink");
+
+ p = trap_path(path);
+ if (p == NULL)
+ return -1;
+
+ return _readlink(p, buf, bufsiz);
+}
--
1.7.10.4
[-- Attachment #1.3: 0002-gudev-Add-GUdevTestbed.patch --]
[-- Type: text/x-diff, Size: 34273 bytes --]
From 2b76a9b27c730864eafd7e83848e04e39af4fa2a Mon Sep 17 00:00:00 2001
From: Martin Pitt <martinpitt@gnome.org>
Date: Fri, 6 Jul 2012 13:27:51 +0200
Subject: [PATCH 2/2] gudev: Add GUdevTestbed
The GUdevTestbed class is used to build a temporary sysfs file system. You can
add a number of devices including arbitrary sysfs attributes and udev
properties, and then run a gudev client in that test bed that is independent of
the actual hardware it is running on. With this you can simulate particular
hardware in virtual environments up to some degree (e. g. sysctls will fail).
Also add a test-gudev check which tests both GUDev itself and GUdevTestbed.
---
.gitignore | 1 +
Makefile.am | 25 ++-
docs/gudev/gudev-docs.xml | 1 +
docs/gudev/gudev-sections.txt | 24 +++
src/gudev/gudev.h | 1 +
src/gudev/gudevtestbed.c | 456 +++++++++++++++++++++++++++++++++++++++++
src/gudev/gudevtestbed.h | 103 ++++++++++
src/gudev/gudevtypes.h | 1 +
src/test/test-gudev.c | 248 ++++++++++++++++++++++
9 files changed, 859 insertions(+), 1 deletion(-)
create mode 100644 src/gudev/gudevtestbed.c
create mode 100644 src/gudev/gudevtestbed.h
create mode 100644 src/test/test-gudev.c
diff --git a/.gitignore b/.gitignore
index 0732176..1109778 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,3 +123,4 @@ stamp-*
/v4l_id
/test-libudev
/test-udev
+/test-gudev
diff --git a/Makefile.am b/Makefile.am
index e4b8166..8435592 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1829,6 +1829,7 @@ libgudev_include_HEADERS = \
src/gudev/gudevtypes.h \
src/gudev/gudevclient.h \
src/gudev/gudevdevice.h \
+ src/gudev/gudevtestbed.h \
src/gudev/gudevenumerator.h
lib_LTLIBRARIES += libgudev-1.0.la
@@ -1853,6 +1854,8 @@ libgudev_1_0_la_SOURCES = \
src/gudev/gudevdevice.c \
src/gudev/gudevenumerator.h \
src/gudev/gudevenumerator.c \
+ src/gudev/gudevtestbed.h \
+ src/gudev/gudevtestbed.c \
src/gudev/gudevprivate.h
nodist_libgudev_1_0_la_SOURCES = \
@@ -1947,7 +1950,10 @@ src_gudev_GUdev_1_0_gir_FILES = \
$(top_srcdir)/src/gudev/gudevenumerator.h \
$(top_srcdir)/src/gudev/gudevclient.c \
$(top_srcdir)/src/gudev/gudevdevice.c \
- $(top_srcdir)/src/gudev/gudevenumerator.c
+ $(top_srcdir)/src/gudev/gudevenumerator.c \
+ $(top_srcdir)/src/gudev/gudevtestbed.h \
+ $(top_srcdir)/src/gudev/gudevtestbed.c \
+ $(NULL)
INTROSPECTION_GIRS = src/gudev/GUdev-1.0.gir
INTROSPECTION_SCANNER_ARGS = --c-include=gudev/gudev.h
@@ -1976,6 +1982,23 @@ libgudev-install-move-hook:
libgudev-uninstall-move-hook:
rm -f $(DESTDIR)$(rootlibdir)/libgudev-1.0.so*
+noinst_PROGRAMS += \
+ test-gudev
+
+test_gudev_SOURCES = \
+ src/test/test-gudev.c
+
+test_gudev_CFLAGS = \
+ $(GLIB_CFLAGS)
+
+test_gudev_LDADD = \
+ libgudev-1.0.la \
+ $(GLIB_LIBS)
+
+TESTS += \
+ test-gudev
+
+
INSTALL_EXEC_HOOKS += libgudev-install-move-hook
UNINSTALL_EXEC_HOOKS += libgudev-uninstall-move-hook
endif
diff --git a/docs/gudev/gudev-docs.xml b/docs/gudev/gudev-docs.xml
index 3e7e50a..43cc9cb 100644
--- a/docs/gudev/gudev-docs.xml
+++ b/docs/gudev/gudev-docs.xml
@@ -26,6 +26,7 @@
<xi:include href="xml/gudevclient.xml"/>
<xi:include href="xml/gudevdevice.xml"/>
<xi:include href="xml/gudevenumerator.xml"/>
+ <xi:include href="xml/gudevtestbed.xml"/>
</chapter>
<chapter id="gudev-hierarchy">
diff --git a/docs/gudev/gudev-sections.txt b/docs/gudev/gudev-sections.txt
index b25c13b..ab2ccfb 100644
--- a/docs/gudev/gudev-sections.txt
+++ b/docs/gudev/gudev-sections.txt
@@ -98,3 +98,27 @@ G_UDEV_ENUMERATOR_GET_CLASS
<SUBSECTION Private>
GUdevEnumeratorPrivate
</SECTION>
+
+<SECTION>
+<FILE>gudevtestbed</FILE>
+<TITLE>GUdevTestbed</TITLE>
+GUdevTestbed
+GUdevTestbedClass
+g_udev_testbed_new
+g_udev_testbed_add_device
+g_udev_testbed_add_devicev
+g_udev_testbed_get_root_dir
+g_udev_testbed_get_sys_dir
+g_udev_testbed_set_attribute
+g_udev_testbed_set_property
+<SUBSECTION Standard>
+g_udev_testbed_get_type
+G_UDEV_IS_TESTBED
+G_UDEV_IS_TESTBED_CLASS
+G_UDEV_TESTBED
+G_UDEV_TESTBED_CLASS
+G_UDEV_TESTBED_GET_CLASS
+G_UDEV_TYPE_TESTBED
+<SUBSECTION Private>
+GUdevTestbedPrivate
+</SECTION>
diff --git a/src/gudev/gudev.h b/src/gudev/gudev.h
index 6ae01f2..a8f9102 100644
--- a/src/gudev/gudev.h
+++ b/src/gudev/gudev.h
@@ -28,6 +28,7 @@
#include <gudev/gudevclient.h>
#include <gudev/gudevdevice.h>
#include <gudev/gudevenumerator.h>
+#include <gudev/gudevtestbed.h>
#undef _GUDEV_INSIDE_GUDEV_H
#endif /* __G_UDEV_H__ */
diff --git a/src/gudev/gudevtestbed.c b/src/gudev/gudevtestbed.c
new file mode 100644
index 0000000..6d732de
--- /dev/null
+++ b/src/gudev/gudevtestbed.c
@@ -0,0 +1,456 @@
+/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012 Canonical Ltd.
+ * Author: Martin Pitt <martin.pitt@ubuntu.com>
+ *
+ * This library 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 of the License, or (at your option) any later version.
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "gudevtestbed.h"
+
+/**
+ * SECTION:gudevtestbed
+ * @short_description: Build an udev test bed for testing gudev based programs
+ *
+ * The #GUdevTestbed class is used to build a temporary sysfs file
+ * system. You can add a number of devices including arbitrary sysfs
+ * attributes and udev properties, and then run a libudev or gudev client in
+ * that test bed that is independent of the actual hardware it is running on.
+ * With this you can simulate particular hardware in virtual environments up to
+ * some degree (e. g. accessing the nodes in /dev/ and sysctls will fail).
+ *
+ * Instantiating a #GUdevTestbed object creates a temporary directory with a
+ * sysfs tree and sets the $UDEV_TEST_PREFIX environment variable so that
+ * subsequently started programs that use libudev will use the test bed instead
+ * of the system's real sysfs.
+ */
+
+struct _GUdevTestbedPrivate
+{
+ gchar *root_dir;
+ gchar *sys_dir;
+};
+
+G_DEFINE_TYPE (GUdevTestbed, g_udev_testbed, G_TYPE_OBJECT)
+
+static void
+g_udev_testbed_finalize (GObject *object)
+{
+ GUdevTestbed *testbed = G_UDEV_TESTBED (object);
+
+ /* TODO: rm -r root_dir */
+
+ g_debug ("Removing udev test bed %s", testbed->priv->root_dir);
+ g_unsetenv ("UDEV_TEST_PREFIX");
+
+ g_free (testbed->priv->root_dir);
+ g_free (testbed->priv->sys_dir);
+
+ if (G_OBJECT_CLASS (g_udev_testbed_parent_class)->finalize != NULL)
+ (* G_OBJECT_CLASS (g_udev_testbed_parent_class)->finalize) (object);
+}
+
+static void
+g_udev_testbed_class_init (GUdevTestbedClass *klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ gobject_class->finalize = g_udev_testbed_finalize;
+
+ g_type_class_add_private (klass, sizeof (GUdevTestbedPrivate));
+}
+
+static void
+g_udev_testbed_init (GUdevTestbed *testbed)
+{
+ GError *error = NULL;
+
+ testbed->priv = G_TYPE_INSTANCE_GET_PRIVATE (testbed,
+ G_UDEV_TYPE_TESTBED,
+ GUdevTestbedPrivate);
+
+ testbed->priv->root_dir = g_dir_make_tmp ("udevtestbed.XXXXXX", &error);
+ g_assert_no_error (error);
+
+ testbed->priv->sys_dir = g_build_filename (testbed->priv->root_dir, "sys", NULL);
+ g_assert (g_mkdir (testbed->priv->sys_dir, 0755) == 0);
+
+ g_assert (g_setenv ("UDEV_TEST_PREFIX", testbed->priv->root_dir, TRUE));
+
+ g_debug ("Created udev test bed %s", testbed->priv->root_dir);
+}
+
+/**
+ * g_udev_testbed_new:
+ *
+ * Construct a #GUdevTestbed object with no devices. Use
+ * #g_udev_testbed_add_device to populate it. This automatically sets the
+ * UDEV_TEST_PREFIX environment variable so that subsequently started gudev
+ * clients will use the test bed.
+ *
+ * Returns: A new #GUdevTestbed object. Free with g_object_unref().
+ */
+GUdevTestbed *
+g_udev_testbed_new (void)
+{
+ return G_UDEV_TESTBED (g_object_new (G_UDEV_TYPE_TESTBED, NULL));
+}
+
+/**
+ * g_udev_testbed_get_root_dir:
+ * @testbed: A #GUdevTestbed.
+ *
+ * Gets the root directory for @testbed.
+ *
+ * Returns: (transfer none): The root directory for @testbed. Do not free or
+ * modify.
+ */
+const gchar *
+g_udev_testbed_get_root_dir (GUdevTestbed *testbed)
+{
+ g_return_val_if_fail (G_UDEV_IS_TESTBED (testbed), NULL);
+ return testbed->priv->root_dir;
+}
+
+/**
+ * g_udev_testbed_get_sys_dir:
+ * @testbed: A #GUdevTestbed.
+ *
+ * Gets the sysfs directory for @testbed.
+ *
+ * Returns: (transfer none): The sysfs directory for @testbed. Do not free or
+ * modify.
+ */
+const gchar *
+g_udev_testbed_get_sys_dir (GUdevTestbed *testbed)
+{
+ g_return_val_if_fail (G_UDEV_IS_TESTBED (testbed), NULL);
+ return testbed->priv->sys_dir;
+}
+
+/**
+ * uevent_from_property_list:
+ *
+ * Build the contents of an uevent file (with udev properties) from a property
+ * list.
+ */
+static gchar*
+uevent_from_property_list (const gchar** properties)
+{
+ GString *result;
+ const gchar *key, *value;
+
+ result = g_string_sized_new (1024);
+
+ while (*properties != NULL)
+ {
+ key = *properties;
+ ++properties;
+ if (*properties == NULL)
+ {
+ g_warning ("uevent_from_property_list: Ignoring key '%s' without value", key);
+ break;
+ }
+ value = *properties;
+ ++properties;
+ g_string_append (result, key);
+ g_string_append_c (result, '=');
+ g_string_append (result, value);
+ g_string_append_c (result, '\n');
+ }
+
+ return g_string_free (result, FALSE);
+}
+
+/**
+ * g_udev_testbed_add_devicev:
+ * @testbed: A #GUdevTestbed.
+ * @subsystem: The subsystem name, e. g. "usb"
+ * @name: The device name; arbitrary, but needs to be unique within the testbed
+ * @attributes: (transfer none): A list of device sysfs attributes, alternating
+ * names and values, terminated with NULL:
+ * { "key1", "value1", "key2", "value2", ..., NULL }
+ * @properties: (transfer none): A list of device udev properties; same format
+ * as @attributes
+ *
+ * This method is mostly meant for language bindings (where it is named
+ * #g_udev_testbed_add_device). For C programs it is usually more convenient to
+ * use #g_udev_testbed_add_device.
+ *
+ * Add a new device to the @testbed. A Linux kernel device always has a
+ * subsystem (such as "usb" or "pci"), and a device name. The test bed only
+ * builds a very simple sysfs structure without nested namespaces, so it
+ * requires device names to be unique. Some gudev client programs might make
+ * assumptions about the name (e. g. a SCSI disk block device should be called
+ * sdaN). A device also has an arbitrary number of sysfs attributes and udev
+ * properties; usually you should specify them upon creation, but it is also
+ * possible to change them later on with #g_udev_testbed_set_attribute and
+ * #g_udev_testbed_set_property.
+ *
+ * Returns: (transfer full): The sysfs path for the newly created device. Free
+ * with g_free().
+ *
+ * Rename to: g_udev_testbed_add_device
+ */
+gchar*
+g_udev_testbed_add_devicev (GUdevTestbed *testbed,
+ const gchar *subsystem,
+ const gchar *name,
+ const gchar **attributes,
+ const gchar **properties)
+{
+ gchar *dev_path;
+ gchar *dev_dir;
+ gchar *class_dir;
+ gchar *target, *link;
+ const gchar *key, *value;
+ gchar *prop_str;
+
+ dev_path = g_build_filename ("/sys/devices", name, NULL);
+ dev_dir = g_build_filename (testbed->priv->root_dir, dev_path, NULL);
+
+ /* must not exist yet */
+ g_return_val_if_fail (!g_file_test (dev_dir, G_FILE_TEST_EXISTS), NULL);
+
+ /* create device and corresponding subsystem dir */
+ g_assert (g_mkdir_with_parents (dev_dir, 0755) == 0);
+ class_dir = g_build_filename (testbed->priv->sys_dir, "class", subsystem, NULL);
+ g_assert (g_mkdir_with_parents (class_dir, 0755) == 0);
+
+ /* subsystem symlink */
+ target = g_build_filename ("..", "..", "class", subsystem, NULL);
+ link = g_build_filename (dev_dir, "subsystem", NULL);
+ g_assert (symlink (target, link) == 0);
+ g_free (target);
+ g_free (link);
+
+ /* device symlink from class/ */
+ target = g_build_filename ("..", "..", "devices", name, NULL);
+ link = g_build_filename (class_dir, name, NULL);
+ g_assert (symlink (target, link) == 0);
+ g_free (target);
+ g_free (link);
+
+ g_free (class_dir);
+ g_free (dev_dir);
+
+ /* attributes */
+ while (*attributes != NULL)
+ {
+ key = *attributes;
+ ++attributes;
+ if (*attributes == NULL)
+ {
+ g_warning ("g_udev_testbed_add_devicev: Ignoring attribute key '%s' without value", key);
+ break;
+ }
+ value = *attributes;
+ ++attributes;
+ g_udev_testbed_set_attribute (testbed, dev_path, key, value);
+ }
+
+ /* properties; they go into the "uevent" sysfs attribute */
+ prop_str = uevent_from_property_list (properties);
+ g_udev_testbed_set_attribute (testbed, dev_path, "uevent", prop_str);
+ g_free (prop_str);
+
+ /* we want to return a realistic device path, not one starting with the the
+ * testbed prefix */
+ return dev_path;
+}
+
+/**
+ * g_udev_testbed_add_device: (skip)
+ * @testbed: A #GUdevTestbed.
+ * @subsystem: The subsystem name, e. g. "usb"
+ * @name: The device name; arbitrary, but needs to be unique within the testbed
+ * @...: Arbitrarily many pairs of sysfs attributes (alternating names and
+ * values), terminated by NULL, followed by arbitrarily many pairs of udev
+ * properties, terminated by another NULL.
+ *
+ * Add a new device to the @testbed. A Linux kernel device always has a
+ * subsystem (such as "usb" or "pci"), and a device name. The test bed only
+ * builds a very simple sysfs structure without nested namespaces, so it
+ * requires device names to be unique. Some gudev client programs might make
+ * assumptions about the name (e. g. a SCSI disk block device should be called
+ * sdaN). A device also has an arbitrary number of sysfs attributes and udev
+ * properties; usually you should specify them upon creation, but it is also
+ * possible to change them later on with #g_udev_testbed_set_attribute and
+ * #g_udev_testbed_set_property.
+ *
+ * Example:
+ * |[
+ * g_udev_testbed_add_device (testbed, "usb", "dev1",
+ * "idVendor", "0815", "idProduct", "AFFE", NULL,
+ * "ID_MODEL", "KoolGadget", NULL);
+ * ]|
+ *
+ * Returns: (transfer full): The sysfs path for the newly created device. Free
+ * with g_free().
+ */
+gchar*
+g_udev_testbed_add_device (GUdevTestbed *testbed,
+ const gchar *subsystem,
+ const gchar *name,
+ ...)
+{
+ va_list args;
+ int arg_set = 0; /* 0 -> attributes, 1 -> properties */
+ gchar *syspath;
+ const gchar *arg;
+ GArray *attributes;
+ GArray *properties;
+
+ attributes = g_array_new (TRUE, FALSE, sizeof (gchar*));
+ properties = g_array_new (TRUE, FALSE, sizeof (gchar*));
+
+ va_start (args, name);
+
+ for (;;) {
+ arg = va_arg (args, const gchar*);
+ /* we iterate arguments until NULL twice; first for the attributes, then
+ * for the properties */
+ if (arg == NULL)
+ {
+ if (++arg_set > 1)
+ break;
+ else
+ continue;
+ }
+
+ if (arg_set == 0)
+ g_array_append_val (attributes, arg);
+ else
+ g_array_append_val (properties, arg);
+ }
+
+ syspath = g_udev_testbed_add_devicev (testbed,
+ subsystem,
+ name,
+ (const gchar**) attributes->data,
+ (const gchar**) properties->data);
+
+ g_array_free (attributes, FALSE);
+ g_array_free (properties, FALSE);
+
+ va_end (args);
+
+ return syspath;
+}
+
+
+/**
+ * g_udev_testbed_set_attribute:
+ * @testbed: A #GUdevTestbed.
+ * @devpath: The full device path, as returned by #g_udev_testbed_add_device
+ * @name: The attribute name
+ * @value: The attribute value
+ *
+ * Set a sysfs attribute of a device.
+ */
+void
+g_udev_testbed_set_attribute (GUdevTestbed *testbed,
+ const gchar *devpath,
+ const gchar *name,
+ const gchar *value)
+{
+ gchar *attr_path;
+ GError *error = NULL;
+
+ attr_path = g_build_filename (testbed->priv->root_dir, devpath, name, NULL);
+ g_file_set_contents (attr_path, value, -1, &error);
+ g_assert_no_error (error);
+ g_free (attr_path);
+}
+
+/**
+ * g_udev_testbed_set_property:
+ * @testbed: A #GUdevTestbed.
+ * @devpath: The full device path, as returned by #g_udev_testbed_add_device
+ * @name: The property name
+ * @value: The property value
+ *
+ * Set an udev property of a device.
+ */
+void
+g_udev_testbed_set_property (GUdevTestbed *testbed,
+ const gchar *devpath,
+ const gchar *name,
+ const gchar *value)
+{
+ size_t name_len;
+ gchar *uevent_path;
+ gboolean existing = FALSE;
+ GString *props;
+ FILE *f;
+ char line[4096];
+
+ name_len = strlen (name);
+
+ /* read current properties from the uevent file; if name is already set,
+ * replace its value with the new one */
+ uevent_path = g_build_filename (testbed->priv->root_dir, devpath, "uevent", NULL);
+ f = fopen (uevent_path, "r");
+ g_assert (f != NULL);
+
+ props = g_string_sized_new (1024);
+ while (fgets (line, sizeof (line), f) != NULL)
+ {
+ if (g_str_has_prefix (line, name) && line[name_len] == '=')
+ {
+ existing = TRUE;
+ g_string_append (props, name);
+ g_string_append_c (props, '=');
+ g_string_append (props, value);
+ g_string_append_c (props, '\n');
+ }
+ else
+ {
+ g_string_append (props, line);
+ }
+ }
+ fclose (f);
+
+ /* if property name does not yet exist, append it */
+ if (!existing)
+ {
+ g_string_append (props, name);
+ g_string_append_c (props, '=');
+ g_string_append (props, value);
+ g_string_append_c (props, '\n');
+ }
+
+
+ /* write it back */
+ f = fopen (uevent_path, "w");
+ g_assert (f != NULL);
+ g_assert_cmpint (fwrite (props->str, sizeof (gchar), props->len, f), ==, props->len);
+ fclose (f);
+
+ g_string_free (props, TRUE);
+ g_free (uevent_path);
+}
diff --git a/src/gudev/gudevtestbed.h b/src/gudev/gudevtestbed.h
new file mode 100644
index 0000000..dc934dd
--- /dev/null
+++ b/src/gudev/gudevtestbed.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012 Canonical Ltd.
+ * Author: Martin Pitt <martin.pitt@ubuntu.com>
+ *
+ * This library 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 of the License, or (at your option) any later version.
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H)
+#error "Only <gudev/gudev.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef __G_UDEV_TESTBED_H__
+#define __G_UDEV_TESTBED_H__
+
+#include <gudev/gudevtypes.h>
+
+G_BEGIN_DECLS
+
+#define G_UDEV_TYPE_TESTBED (g_udev_testbed_get_type ())
+#define G_UDEV_TESTBED(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_UDEV_TYPE_TESTBED, GUdevTestbed))
+#define G_UDEV_TESTBED_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_UDEV_TYPE_TESTBED, GUdevTestbedClass))
+#define G_UDEV_IS_TESTBED(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_UDEV_TYPE_TESTBED))
+#define G_UDEV_IS_TESTBED_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_UDEV_TYPE_TESTBED))
+#define G_UDEV_TESTBED_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_UDEV_TYPE_TESTBED, GUdevTestbedClass))
+
+typedef struct _GUdevTestbedClass GUdevTestbedClass;
+typedef struct _GUdevTestbedPrivate GUdevTestbedPrivate;
+
+/**
+ * GUdevTestbed:
+ * @parent: Parent object
+ *
+ * The #GUdevTestbed struct is opaque and should not be accessed directly.
+ */
+struct _GUdevTestbed
+{
+ GObject parent;
+
+ /*< private >*/
+ GUdevTestbedPrivate *priv;
+};
+
+/**
+ * GUdevTestbedClass:
+ * @parent_class: Parent class.
+ *
+ * Class structure for #GUdevTestbed.
+ */
+struct _GUdevTestbedClass
+{
+ GObjectClass parent_class;
+
+ /*< private >*/
+ /* Padding for future expansion */
+ void (*reserved1) (void);
+ void (*reserved2) (void);
+ void (*reserved3) (void);
+ void (*reserved4) (void);
+ void (*reserved5) (void);
+ void (*reserved6) (void);
+ void (*reserved7) (void);
+ void (*reserved8) (void);
+};
+
+GType g_udev_testbed_get_type (void) G_GNUC_CONST;
+GUdevTestbed *g_udev_testbed_new (void);
+const gchar *g_udev_testbed_get_root_dir (GUdevTestbed *testbed);
+const gchar *g_udev_testbed_get_sys_dir (GUdevTestbed *testbed);
+gchar *g_udev_testbed_add_devicev (GUdevTestbed *testbed,
+ const gchar *subsystem,
+ const gchar *name,
+ const gchar **attributes,
+ const gchar **properties);
+gchar *g_udev_testbed_add_device (GUdevTestbed *testbed,
+ const gchar *subsystem,
+ const gchar *name,
+ ...);
+void g_udev_testbed_set_attribute (GUdevTestbed *testbed,
+ const gchar *devpath,
+ const gchar *name,
+ const gchar *value);
+void g_udev_testbed_set_property (GUdevTestbed *testbed,
+ const gchar *devpath,
+ const gchar *name,
+ const gchar *value);
+
+G_END_DECLS
+
+#endif /* __G_UDEV_TESTBED_H__ */
diff --git a/src/gudev/gudevtypes.h b/src/gudev/gudevtypes.h
index 8884827..f97751e 100644
--- a/src/gudev/gudevtypes.h
+++ b/src/gudev/gudevtypes.h
@@ -33,6 +33,7 @@ G_BEGIN_DECLS
typedef struct _GUdevClient GUdevClient;
typedef struct _GUdevDevice GUdevDevice;
typedef struct _GUdevEnumerator GUdevEnumerator;
+typedef struct _GUdevTestbed GUdevTestbed;
/**
* GUdevDeviceNumber:
diff --git a/src/test/test-gudev.c b/src/test/test-gudev.c
new file mode 100644
index 0000000..b38eaef
--- /dev/null
+++ b/src/test/test-gudev.c
@@ -0,0 +1,248 @@
+/*
+ * test-gudev
+ *
+ * Copyright (C) 2012 Canonical Ltd.
+ * Author: Martin Pitt <martin.pitt@ubuntu.com>
+ *
+ * This library 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.
+ */
+
+#include <glib.h>
+#include <string.h>
+#include <gudev/gudev.h>
+
+typedef struct {
+ GUdevTestbed *testbed;
+} GUdevTestbedFixture;
+
+static void
+gudev_testbed_fixture_setup (GUdevTestbedFixture *fixture, gconstpointer data)
+{
+ fixture->testbed = g_udev_testbed_new();
+ g_assert (fixture->testbed != NULL);
+}
+
+static void
+gudev_testbed_fixture_teardown (GUdevTestbedFixture *fixture, gconstpointer data)
+{
+ g_object_unref (fixture->testbed);
+}
+
+/* enumeration on the system picks up some devices */
+static void
+gudev_system_enumerate (void)
+{
+ GUdevClient *client;
+ GUdevEnumerator *enumerator;
+ GList *result;
+ GUdevDevice *device;
+
+ client = g_udev_client_new (NULL);
+ g_assert (client);
+
+ /* there ought to be at least one device on every system; e. g. a CPU is
+ * always handy to have */
+ enumerator = g_udev_enumerator_new (client);
+ g_assert (enumerator);
+ result = g_udev_enumerator_execute (enumerator);
+ g_assert_cmpuint (g_list_length (result), >, 0);
+
+ /* check that the entry is an useful GUdevDevice */
+ device = G_UDEV_DEVICE (result->data);
+ g_assert (device);
+ g_assert_cmpstr (g_udev_device_get_name (device), !=, "");
+ g_assert (strstr (g_udev_device_get_sysfs_path (device), "/sys/") != NULL);
+
+ g_list_free_full (result, g_object_unref);
+ g_object_unref (enumerator);
+ g_object_unref (client);
+}
+
+/* Empty GUdevTestbed without any devices */
+static void
+gudev_testbed_empty (GUdevTestbedFixture *fixture, gconstpointer data)
+{
+ GUdevClient *client;
+ GUdevEnumerator *enumerator;
+ GList *result;
+
+ client = g_udev_client_new (NULL);
+ g_assert (client);
+
+ enumerator = g_udev_enumerator_new (client);
+ g_assert (enumerator);
+ result = g_udev_enumerator_execute (enumerator);
+ g_assert_cmpuint (g_list_length (result), ==, 0);
+
+ g_object_unref (enumerator);
+ g_object_unref (client);
+}
+
+/* common checks for gudev_testbed_add_device{,v}() */
+static void
+_gudev_testbed_check_extkeyboard1 (const gchar* syspath)
+{
+ GUdevClient *client;
+ GUdevEnumerator *enumerator;
+ GList *result;
+ GUdevDevice *device;
+ client = g_udev_client_new (NULL);
+ g_assert (client);
+
+ enumerator = g_udev_enumerator_new (client);
+ g_assert (enumerator);
+ result = g_udev_enumerator_execute (enumerator);
+ g_assert_cmpuint (g_list_length (result), ==, 1);
+
+ /* check that the entry matches what we put into our test bed */
+ device = G_UDEV_DEVICE (result->data);
+ g_assert (device);
+ g_assert_cmpstr (g_udev_device_get_name (device), ==, "extkeyboard1");
+ g_assert_cmpstr (g_udev_device_get_sysfs_path (device), ==, syspath);
+
+ g_assert_cmpstr (g_udev_device_get_sysfs_attr (device, "idVendor"), ==, "0815");
+ g_assert_cmpstr (g_udev_device_get_sysfs_attr (device, "idProduct"), ==, "AFFE");
+ g_assert_cmpstr (g_udev_device_get_sysfs_attr (device, "noSuchAttr"), ==, NULL);
+
+ g_assert_cmpstr (g_udev_device_get_property (device, "DEVPATH"), ==, "/devices/extkeyboard1");
+ g_assert_cmpstr (g_udev_device_get_property (device, "SUBSYSTEM"), ==, "usb");
+ g_assert_cmpstr (g_udev_device_get_property (device, "ID_INPUT"), ==, "1");
+ g_assert_cmpstr (g_udev_device_get_property (device, "ID_INPUT_KEYBOARD"), ==, "1");
+ g_assert_cmpstr (g_udev_device_get_property (device, "NO_SUCH_PROP"), ==, NULL);
+
+ g_list_free_full (result, g_object_unref);
+ g_object_unref (enumerator);
+ g_object_unref (client);
+}
+
+/* GUdevTestbed add_devicev() with adding one device */
+static void
+gudev_testbed_add_devicev (GUdevTestbedFixture *fixture, gconstpointer data)
+{
+ gchar *syspath;
+ const gchar *attributes[] = { "idVendor", "0815", "idProduct", "AFFE", NULL };
+ const gchar *properties[] = { "ID_INPUT", "1", "ID_INPUT_KEYBOARD", "1", NULL };
+
+ syspath = g_udev_testbed_add_devicev (fixture->testbed,
+ "usb",
+ "extkeyboard1",
+ attributes,
+ properties);
+ g_assert (syspath);
+ g_assert (g_str_has_suffix (syspath, "/sys/devices/extkeyboard1"));
+
+ _gudev_testbed_check_extkeyboard1(syspath);
+ g_free (syspath);
+}
+
+/* GUdevTestbed add_device() with adding one device */
+static void
+gudev_testbed_add_device (GUdevTestbedFixture *fixture, gconstpointer data)
+{
+ gchar *syspath;
+
+ syspath = g_udev_testbed_add_device (fixture->testbed,
+ "usb",
+ "extkeyboard1",
+ /* attributes */
+ "idVendor", "0815", "idProduct", "AFFE", NULL,
+ /* properties */
+ "ID_INPUT", "1", "ID_INPUT_KEYBOARD", "1", NULL);
+ g_assert (syspath);
+ g_assert (g_str_has_suffix (syspath, "/sys/devices/extkeyboard1"));
+
+ _gudev_testbed_check_extkeyboard1(syspath);
+ g_free (syspath);
+}
+
+static void
+gudev_testbed_set_attribute (GUdevTestbedFixture *fixture, gconstpointer data)
+{
+ GUdevClient *client;
+ GUdevDevice *device;
+ gchar *syspath;
+
+ client = g_udev_client_new (NULL);
+
+ syspath = g_udev_testbed_add_device (fixture->testbed,
+ "usb",
+ "extkeyboard1",
+ /* attributes */
+ "idVendor", "0815", "idProduct", "AFFE", NULL,
+ /* properties */
+ NULL);
+
+ /* change an existing attribute */
+ g_udev_testbed_set_attribute (fixture->testbed, syspath, "idProduct", "BEEF");
+ /* add a new one */
+ g_udev_testbed_set_attribute (fixture->testbed, syspath, "color", "yellow");
+
+ device = g_udev_client_query_by_sysfs_path (client, syspath);
+ g_assert (device);
+ g_assert_cmpstr (g_udev_device_get_sysfs_attr (device, "idVendor"), ==, "0815");
+ g_assert_cmpstr (g_udev_device_get_sysfs_attr (device, "idProduct"), ==, "BEEF");
+ g_assert_cmpstr (g_udev_device_get_sysfs_attr (device, "color"), ==, "yellow");
+ g_object_unref (device);
+
+ g_object_unref (client);
+ g_free (syspath);
+}
+
+static void
+gudev_testbed_set_property (GUdevTestbedFixture *fixture, gconstpointer data)
+{
+ GUdevClient *client;
+ GUdevDevice *device;
+ gchar *syspath;
+
+ client = g_udev_client_new (NULL);
+
+ syspath = g_udev_testbed_add_device (fixture->testbed,
+ "usb",
+ "extkeyboard1",
+ /* attributes */
+ NULL,
+ /* properties */
+ "ID_INPUT", "1", NULL);
+
+ /* change an existing property */
+ g_udev_testbed_set_property (fixture->testbed, syspath, "ID_INPUT", "0");
+ /* add a new one */
+ g_udev_testbed_set_property (fixture->testbed, syspath, "ID_COLOR", "green");
+
+ device = g_udev_client_query_by_sysfs_path (client, syspath);
+ g_assert (device);
+ g_assert_cmpstr (g_udev_device_get_property (device, "ID_INPUT"), ==, "0");
+ g_assert_cmpstr (g_udev_device_get_property (device, "ID_COLOR"), ==, "green");
+ g_object_unref (device);
+
+ g_object_unref (client);
+ g_free (syspath);
+}
+
+int
+main (int argc, char **argv)
+{
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ /* tests on system */
+ g_test_add_func ("/gudev-system/enumerate", gudev_system_enumerate);
+
+ /* tests with GUdevTestbed */
+ g_test_add ("/gudev-testbed/empty", GUdevTestbedFixture, NULL, gudev_testbed_fixture_setup,
+ gudev_testbed_empty, gudev_testbed_fixture_teardown);
+ g_test_add ("/gudev-testbed/add_devicev", GUdevTestbedFixture, NULL, gudev_testbed_fixture_setup,
+ gudev_testbed_add_devicev, gudev_testbed_fixture_teardown);
+ g_test_add ("/gudev-testbed/add_device", GUdevTestbedFixture, NULL, gudev_testbed_fixture_setup,
+ gudev_testbed_add_device, gudev_testbed_fixture_teardown);
+ g_test_add ("/gudev-testbed/set_attribute", GUdevTestbedFixture, NULL, gudev_testbed_fixture_setup,
+ gudev_testbed_set_attribute, gudev_testbed_fixture_teardown);
+ g_test_add ("/gudev-testbed/set_property", GUdevTestbedFixture, NULL, gudev_testbed_fixture_setup,
+ gudev_testbed_set_property, gudev_testbed_fixture_teardown);
+
+ return g_test_run ();
+}
--
1.7.10.4
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply related
* Re: [PATCH] Testbeds for libudev/gudev clients
From: Martin Pitt @ 2012-07-10 3:40 UTC (permalink / raw)
To: linux-hotplug
In-Reply-To: <20120706050131.GC3109@piware.de>
Lucas De Marchi [2012-07-09 17:21 -0300]:
> > So the trade is "add the get_prefix() calls to the path name build
> > calls" vs. "maintain/build/install a preload library".
>
> I never ever install that, and doing so is just wrong IMO. Just make
> it part of the "make check". The final user is not supposed to be
> running a testsuite.
This is not (primarily) for testing libudev/gudev itself. The whole
point of this is to make the functionality available to users of
libudev/gudev so that you can write tests for _those_ easily. I
already pointed out two existing examples (upower and
ubuntu-drivers-common) in my initial mail. This is also why I wrote
that entire GUdevTestbed; testing gudev itself would be a lot simpler,
but that's not the reason why we need it.
So we do have to ship it, and document how upower and friends have to
use it (presumably an "udevtestbed" wrapper script which finds and
preloads the test library).
Thanks,
Martin
--
Martin Pitt | http://www.piware.de
Ubuntu Developer (www.ubuntu.com) | Debian Developer (www.debian.org)
^ permalink raw reply
* Re: [systemd-devel] [PATCH] Always populate EXTRA_DIST
From: Lennart Poettering @ 2012-07-10 0:06 UTC (permalink / raw)
To: linux-hotplug
In-Reply-To: <4FD195C7.6010808@kadzban.is-a-geek.net>
On Thu, 07.06.12 23:24, Bryan Kadzban (bryan@kadzban.is-a-geek.net) wrote:
> Bryan Kadzban wrote:
> > "make dist" can build a different tarball depending on the flags passed
> > to ./configure and the (optional) dependencies found on the system.
> > Move all append-to-EXTRA_DIST operations out of automake conditionals to
> > fix this.
> >
> > Introduce a polkitpolicy_files so that the policy files built still
> > correctly depend on the automake conditionals, but the .in files that
> > get distributed do not.
> >
> > Signed-Off-By: Bryan Kadzban <bryan.kadzban.is-a-geek.net>
>
> Arg, always compile-check. Tested, this time.
>
> v2, fixing warnings about polkitpolicy_files not being defined.
Looks good, but could you rebase this to current master? Will apply then.
Thanks!
Lennart
--
Lennart Poettering - Red Hat, Inc.
^ permalink raw reply
* Re: [PATCH] Testbeds for libudev/gudev clients
From: Lucas De Marchi @ 2012-07-09 20:21 UTC (permalink / raw)
To: linux-hotplug
In-Reply-To: <20120706050131.GC3109@piware.de>
On Mon, Jul 9, 2012 at 5:09 PM, Martin Pitt <martin.pitt@ubuntu.com> wrote:
> Hello Lucas,
>
> Lucas De Marchi [2012-07-09 16:54 -0300]:
>> You might want to look into kmod's testsuite. We do exactly that and
>> until there's a better alternative I plan to support this for newer
>> glibcs.
>
> Thanks for pointing out! I'll do that. When it fails with a newer
> glibc, we should get test case failures and thus it should be rather
> obvious where things need fixing.
>
> I guess that still means we'd either need a libudev-test.so or a shell
> wrapper and the libpath.so thing around all tests, and thus
> build/ship the two as part of a libudev install. That's something
> which I considered to be more ugly, but if Kay prefers that, I'll look
> into this.
It's part of my "make check" running the testsuite.
>
>> When I implemented that I tried what you are trying now and it didn't
>> look right and it's ugly to touch all the calls with path strings and
>> also very error prone.
>
> libudev already has something like that, it has the TEST_PREFIX macro
> everywhere. So I don't think it's actually getting much worse, but
> with a preloaded library we wouldn't need the TEST_PREFIX thing any
> more either.
>
> So the trade is "add the get_prefix() calls to the path name build
> calls" vs. "maintain/build/install a preload library".
I never ever install that, and doing so is just wrong IMO. Just make
it part of the "make check". The final user is not supposed to be
running a testsuite.
Lucas De Marchi
^ permalink raw reply
* Re: [PATCH] Testbeds for libudev/gudev clients
From: Martin Pitt @ 2012-07-09 20:09 UTC (permalink / raw)
To: linux-hotplug
In-Reply-To: <20120706050131.GC3109@piware.de>
Hello Lucas,
Lucas De Marchi [2012-07-09 16:54 -0300]:
> You might want to look into kmod's testsuite. We do exactly that and
> until there's a better alternative I plan to support this for newer
> glibcs.
Thanks for pointing out! I'll do that. When it fails with a newer
glibc, we should get test case failures and thus it should be rather
obvious where things need fixing.
I guess that still means we'd either need a libudev-test.so or a shell
wrapper and the libpath.so thing around all tests, and thus
build/ship the two as part of a libudev install. That's something
which I considered to be more ugly, but if Kay prefers that, I'll look
into this.
> When I implemented that I tried what you are trying now and it didn't
> look right and it's ugly to touch all the calls with path strings and
> also very error prone.
libudev already has something like that, it has the TEST_PREFIX macro
everywhere. So I don't think it's actually getting much worse, but
with a preloaded library we wouldn't need the TEST_PREFIX thing any
more either.
So the trade is "add the get_prefix() calls to the path name build
calls" vs. "maintain/build/install a preload library".
Thanks,
Martin
--
Martin Pitt | http://www.piware.de
Ubuntu Developer (www.ubuntu.com) | Debian Developer (www.debian.org)
^ permalink raw reply
* Re: [PATCH] Testbeds for libudev/gudev clients
From: Lucas De Marchi @ 2012-07-09 19:54 UTC (permalink / raw)
To: linux-hotplug
In-Reply-To: <20120706050131.GC3109@piware.de>
On Mon, Jul 9, 2012 at 11:22 AM, Martin Pitt <martin.pitt@ubuntu.com> wrote:
> Kay Sievers [2012-07-09 15:50 +0200]:
>> Can't the tests just use a fs namespace and run the test in it with a
>> bind-mount of /sys in it? Maybe check the unshare(1) tool to get the
>> idea, or it might already be able to do that.
>
> Most alternatives would require root privileges, which make tests a
> lot less useful: You cannot run them in environments like jhbuild or
> "make distcheck", and are also both inconvenient and potentially
> ruining your system when you run them during development.
>
> Another alternative I can think of that avoids root privs is a
> fakechroot like LD_PRELOAD wrapper which intercepts all gazillion
> variants of open and stat-like calls and redirects them to the
> testbed. But given how fakechroot breaks with every other new glibc
> release this is not something I'm very keen to do.
You might want to look into kmod's testsuite. We do exactly that and
until there's a better alternative I plan to support this for newer
glibcs. There's a path.so that you can copy and paste to your project
- there are even other traps like the ones to uname(), init_module(),
delete_module() etc.
When I implemented that I tried what you are trying now and it didn't
look right and it's ugly to touch all the calls with path strings and
also very error prone.
It's not perfect, it could be simplified I think, but it doesn't touch
any libkmod/tools code like your approach, it doesn't require root
privileges and it's working quite well for all needs of kmod until
now, which I think are more than the ones for testing udev.
Lucas De Marchi
^ permalink raw reply
* Re: [PATCH] Testbeds for libudev/gudev clients
From: Martin Pitt @ 2012-07-09 14:22 UTC (permalink / raw)
To: linux-hotplug
In-Reply-To: <20120706050131.GC3109@piware.de>
Kay Sievers [2012-07-09 15:50 +0200]:
> Can't the tests just use a fs namespace and run the test in it with a
> bind-mount of /sys in it? Maybe check the unshare(1) tool to get the
> idea, or it might already be able to do that.
Most alternatives would require root privileges, which make tests a
lot less useful: You cannot run them in environments like jhbuild or
"make distcheck", and are also both inconvenient and potentially
ruining your system when you run them during development.
Another alternative I can think of that avoids root privs is a
fakechroot like LD_PRELOAD wrapper which intercepts all gazillion
variants of open and stat-like calls and redirects them to the
testbed. But given how fakechroot breaks with every other new glibc
release this is not something I'm very keen to do.
The third option that comes to my mind is to change the build system
to build a libudev-test.so with a hardcoded TEST_PREFIX of '.', so
that upower and friends can preload/link to that instead of the real
udev, and run the daemon in the directory of the test bed. Daemons
must not chdir() then (chdir('/') actually used to be best practice
for daemons), but that would not be a too hard limitation.
Other ideas greatly appreciated.
If you veto all those, we'll need to live with tests needing root
privileges. It still allows continuous integration test servers to run
stuff in VMs, but that's a lot harder for developers to set up.
Thanks,
Martin
--
Martin Pitt | http://www.piware.de
Ubuntu Developer (www.ubuntu.com) | Debian Developer (www.debian.org)
^ permalink raw reply
* Re: [PATCH] Testbeds for libudev/gudev clients
From: Kay Sievers @ 2012-07-09 13:50 UTC (permalink / raw)
To: linux-hotplug
In-Reply-To: <20120706050131.GC3109@piware.de>
On Mon, Jul 9, 2012 at 9:25 AM, Martin Pitt <martin.pitt@ubuntu.com> wrote:
> Martin Pitt [2012-07-06 7:01 +0200]:
>> * Copy&paste code is obviously bad. It would be much better if the
>> convenience API to set up a libudev/gudev test bed would be
>> provided by libudev/gudev itself. Also, it should be in C and
>> available through introspection, so that you can use it from a
>> variety of languages; this means it should be in gudev.
>>
>> I am currently working on a first patch for this, and will post it
>> for discussion here when I have something working.
>
> I have that working now. The remaining TODO is to remove the temporary
> tree again in the destructor (if only GLib had a function for that..)
> This provides a simple to use API for building a sandbox with mock
> devices, including a new automatic test (covering the sandbox as well
> as parts of gudev itself), and gtk-doc.
>
> What do you think about that?
Urks, the patch isn't really pretty. It was actually nice to get rid
of all these otherwise totally useless knobs.
Can't the tests just use a fs namespace and run the test in it with a
bind-mount of /sys in it? Maybe check the unshare(1) tool to get the
idea, or it might already be able to do that.
If that works, it would not require any patching of tools and could
possibly offer even more useful options for testing in general.
Thanks,
Kay
^ permalink raw reply
* Re: [PATCH] Testbeds for libudev/gudev clients
From: Martin Pitt @ 2012-07-09 7:25 UTC (permalink / raw)
To: linux-hotplug
In-Reply-To: <20120706050131.GC3109@piware.de>
[-- Attachment #1.1: Type: text/plain, Size: 1027 bytes --]
Martin Pitt [2012-07-06 7:01 +0200]:
> * Copy&paste code is obviously bad. It would be much better if the
> convenience API to set up a libudev/gudev test bed would be
> provided by libudev/gudev itself. Also, it should be in C and
> available through introspection, so that you can use it from a
> variety of languages; this means it should be in gudev.
>
> I am currently working on a first patch for this, and will post it
> for discussion here when I have something working.
I have that working now. The remaining TODO is to remove the temporary
tree again in the destructor (if only GLib had a function for that..)
This provides a simple to use API for building a sandbox with mock
devices, including a new automatic test (covering the sandbox as well
as parts of gudev itself), and gtk-doc.
What do you think about that?
Thanks,
Martin
--
Martin Pitt | http://www.piware.de
Ubuntu Developer (www.ubuntu.com) | Debian Developer (www.debian.org)
[-- Attachment #1.2: 0002-gudev-Add-GUdevTestbed.patch --]
[-- Type: text/x-diff, Size: 33942 bytes --]
From d8e3de2529b956ac3fc78640ca179fde6e909150 Mon Sep 17 00:00:00 2001
From: Martin Pitt <martinpitt@gnome.org>
Date: Fri, 6 Jul 2012 13:27:51 +0200
Subject: [PATCH 2/2] gudev: Add GUdevTestbed
The GUdevTestbed class is used to build a temporary sysfs file system. You can
add a number of devices including arbitrary sysfs attributes and udev
properties, and then run a gudev client in that test bed that is independent of
the actual hardware it is running on. With this you can simulate particular
hardware in virtual environments up to some degree (e. g. sysctls will fail).
Also add a test-gudev check which tests both GUDev itself and GUdevTestbed.
---
.gitignore | 1 +
Makefile.am | 25 ++-
docs/gudev/gudev-docs.xml | 1 +
docs/gudev/gudev-sections.txt | 24 +++
src/gudev/gudev.h | 1 +
src/gudev/gudevtestbed.c | 448 +++++++++++++++++++++++++++++++++++++++++
src/gudev/gudevtestbed.h | 103 ++++++++++
src/gudev/gudevtypes.h | 1 +
src/test/test-gudev.c | 248 +++++++++++++++++++++++
9 files changed, 851 insertions(+), 1 deletion(-)
create mode 100644 src/gudev/gudevtestbed.c
create mode 100644 src/gudev/gudevtestbed.h
create mode 100644 src/test/test-gudev.c
diff --git a/.gitignore b/.gitignore
index 0732176..1109778 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,3 +123,4 @@ stamp-*
/v4l_id
/test-libudev
/test-udev
+/test-gudev
diff --git a/Makefile.am b/Makefile.am
index f05c945..3bb74b1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1802,6 +1802,7 @@ libgudev_include_HEADERS = \
src/gudev/gudevtypes.h \
src/gudev/gudevclient.h \
src/gudev/gudevdevice.h \
+ src/gudev/gudevtestbed.h \
src/gudev/gudevenumerator.h
lib_LTLIBRARIES += libgudev-1.0.la
@@ -1826,6 +1827,8 @@ libgudev_1_0_la_SOURCES = \
src/gudev/gudevdevice.c \
src/gudev/gudevenumerator.h \
src/gudev/gudevenumerator.c \
+ src/gudev/gudevtestbed.h \
+ src/gudev/gudevtestbed.c \
src/gudev/gudevprivate.h
nodist_libgudev_1_0_la_SOURCES = \
@@ -1920,7 +1923,10 @@ src_gudev_GUdev_1_0_gir_FILES = \
$(top_srcdir)/src/gudev/gudevenumerator.h \
$(top_srcdir)/src/gudev/gudevclient.c \
$(top_srcdir)/src/gudev/gudevdevice.c \
- $(top_srcdir)/src/gudev/gudevenumerator.c
+ $(top_srcdir)/src/gudev/gudevenumerator.c \
+ $(top_srcdir)/src/gudev/gudevtestbed.h \
+ $(top_srcdir)/src/gudev/gudevtestbed.c \
+ $(NULL)
INTROSPECTION_GIRS = src/gudev/GUdev-1.0.gir
INTROSPECTION_SCANNER_ARGS = --c-include=gudev/gudev.h
@@ -1949,6 +1955,23 @@ libgudev-install-move-hook:
libgudev-uninstall-move-hook:
rm -f $(DESTDIR)$(rootlibdir)/libgudev-1.0.so*
+noinst_PROGRAMS += \
+ test-gudev
+
+test_gudev_SOURCES = \
+ src/test/test-gudev.c
+
+test_gudev_CFLAGS = \
+ $(GLIB_CFLAGS)
+
+test_gudev_LDADD = \
+ libgudev-1.0.la \
+ $(GLIB_LIBS)
+
+TESTS += \
+ test-gudev
+
+
INSTALL_EXEC_HOOKS += libgudev-install-move-hook
UNINSTALL_EXEC_HOOKS += libgudev-uninstall-move-hook
endif
diff --git a/docs/gudev/gudev-docs.xml b/docs/gudev/gudev-docs.xml
index 3e7e50a..43cc9cb 100644
--- a/docs/gudev/gudev-docs.xml
+++ b/docs/gudev/gudev-docs.xml
@@ -26,6 +26,7 @@
<xi:include href="xml/gudevclient.xml"/>
<xi:include href="xml/gudevdevice.xml"/>
<xi:include href="xml/gudevenumerator.xml"/>
+ <xi:include href="xml/gudevtestbed.xml"/>
</chapter>
<chapter id="gudev-hierarchy">
diff --git a/docs/gudev/gudev-sections.txt b/docs/gudev/gudev-sections.txt
index b25c13b..ab2ccfb 100644
--- a/docs/gudev/gudev-sections.txt
+++ b/docs/gudev/gudev-sections.txt
@@ -98,3 +98,27 @@ G_UDEV_ENUMERATOR_GET_CLASS
<SUBSECTION Private>
GUdevEnumeratorPrivate
</SECTION>
+
+<SECTION>
+<FILE>gudevtestbed</FILE>
+<TITLE>GUdevTestbed</TITLE>
+GUdevTestbed
+GUdevTestbedClass
+g_udev_testbed_new
+g_udev_testbed_add_device
+g_udev_testbed_add_devicev
+g_udev_testbed_get_root_dir
+g_udev_testbed_get_sys_dir
+g_udev_testbed_set_attribute
+g_udev_testbed_set_property
+<SUBSECTION Standard>
+g_udev_testbed_get_type
+G_UDEV_IS_TESTBED
+G_UDEV_IS_TESTBED_CLASS
+G_UDEV_TESTBED
+G_UDEV_TESTBED_CLASS
+G_UDEV_TESTBED_GET_CLASS
+G_UDEV_TYPE_TESTBED
+<SUBSECTION Private>
+GUdevTestbedPrivate
+</SECTION>
diff --git a/src/gudev/gudev.h b/src/gudev/gudev.h
index 6ae01f2..a8f9102 100644
--- a/src/gudev/gudev.h
+++ b/src/gudev/gudev.h
@@ -28,6 +28,7 @@
#include <gudev/gudevclient.h>
#include <gudev/gudevdevice.h>
#include <gudev/gudevenumerator.h>
+#include <gudev/gudevtestbed.h>
#undef _GUDEV_INSIDE_GUDEV_H
#endif /* __G_UDEV_H__ */
diff --git a/src/gudev/gudevtestbed.c b/src/gudev/gudevtestbed.c
new file mode 100644
index 0000000..04cdbbb
--- /dev/null
+++ b/src/gudev/gudevtestbed.c
@@ -0,0 +1,448 @@
+/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012 Canonical Ltd.
+ * Author: Martin Pitt <martin.pitt@ubuntu.com>
+ *
+ * This library 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 of the License, or (at your option) any later version.
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "gudevtestbed.h"
+
+/**
+ * SECTION:gudevtestbed
+ * @short_description: Build an udev test bed for testing gudev based programs
+ *
+ * The #GUdevTestbed class is used to build a temporary sysfs file
+ * system. You can add a number of devices including arbitrary sysfs
+ * attributes and udev properties, and then run a libudev or gudev client in
+ * that test bed that is independent of the actual hardware it is running on.
+ * With this you can simulate particular hardware in virtual environments up to
+ * some degree (e. g. accessing the nodes in /dev/ and sysctls will fail).
+ *
+ * Instantiating a #GUdevTestbed object creates a temporary directory with a
+ * sysfs tree and sets the $UDEV_TEST_PREFIX environment variable so that
+ * subsequently started programs that use libudev will use the test bed instead
+ * of the system's real sysfs.
+ */
+
+struct _GUdevTestbedPrivate
+{
+ gchar *root_dir;
+ gchar *sys_dir;
+};
+
+G_DEFINE_TYPE (GUdevTestbed, g_udev_testbed, G_TYPE_OBJECT)
+
+static void
+g_udev_testbed_finalize (GObject *object)
+{
+ GUdevTestbed *testbed = G_UDEV_TESTBED (object);
+
+ /* TODO: rm -r root_dir */
+
+ g_debug ("Removing udev test bed %s", testbed->priv->root_dir);
+ g_unsetenv ("UDEV_TEST_PREFIX");
+
+ g_free (testbed->priv->root_dir);
+ g_free (testbed->priv->sys_dir);
+
+ if (G_OBJECT_CLASS (g_udev_testbed_parent_class)->finalize != NULL)
+ (* G_OBJECT_CLASS (g_udev_testbed_parent_class)->finalize) (object);
+}
+
+static void
+g_udev_testbed_class_init (GUdevTestbedClass *klass)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+
+ gobject_class->finalize = g_udev_testbed_finalize;
+
+ g_type_class_add_private (klass, sizeof (GUdevTestbedPrivate));
+}
+
+static void
+g_udev_testbed_init (GUdevTestbed *testbed)
+{
+ GError *error = NULL;
+
+ testbed->priv = G_TYPE_INSTANCE_GET_PRIVATE (testbed,
+ G_UDEV_TYPE_TESTBED,
+ GUdevTestbedPrivate);
+
+ testbed->priv->root_dir = g_dir_make_tmp ("udevtestbed.XXXXXX", &error);
+ g_assert_no_error (error);
+
+ testbed->priv->sys_dir = g_build_filename (testbed->priv->root_dir, "sys", NULL);
+ g_assert (g_mkdir (testbed->priv->sys_dir, 0755) == 0);
+
+ g_assert (g_setenv ("UDEV_TEST_PREFIX", testbed->priv->root_dir, TRUE));
+
+ g_debug ("Created udev test bed %s", testbed->priv->root_dir);
+}
+
+/**
+ * g_udev_testbed_new:
+ *
+ * Construct a #GUdevTestbed object with no devices. Use
+ * #g_udev_testbed_add_device to populate it. This automatically sets the
+ * UDEV_TEST_PREFIX environment variable so that subsequently started gudev
+ * clients will use the test bed.
+ *
+ * Returns: A new #GUdevTestbed object. Free with g_object_unref().
+ */
+GUdevTestbed *
+g_udev_testbed_new (void)
+{
+ return G_UDEV_TESTBED (g_object_new (G_UDEV_TYPE_TESTBED, NULL));
+}
+
+/**
+ * g_udev_testbed_get_root_dir:
+ * @testbed: A #GUdevTestbed.
+ *
+ * Gets the root directory for @testbed.
+ *
+ * Returns: (transfer none): The root directory for @testbed. Do not free or
+ * modify.
+ */
+const gchar *
+g_udev_testbed_get_root_dir (GUdevTestbed *testbed)
+{
+ g_return_val_if_fail (G_UDEV_IS_TESTBED (testbed), NULL);
+ return testbed->priv->root_dir;
+}
+
+/**
+ * g_udev_testbed_get_sys_dir:
+ * @testbed: A #GUdevTestbed.
+ *
+ * Gets the sysfs directory for @testbed.
+ *
+ * Returns: (transfer none): The sysfs directory for @testbed. Do not free or
+ * modify.
+ */
+const gchar *
+g_udev_testbed_get_sys_dir (GUdevTestbed *testbed)
+{
+ g_return_val_if_fail (G_UDEV_IS_TESTBED (testbed), NULL);
+ return testbed->priv->sys_dir;
+}
+
+/**
+ * uevent_from_property_list:
+ *
+ * Build the contents of an uevent file (with udev properties) from a property
+ * list.
+ */
+static gchar*
+uevent_from_property_list (const gchar** properties)
+{
+ GString *result;
+ const gchar *key, *value;
+
+ result = g_string_sized_new (1024);
+
+ while (*properties != NULL)
+ {
+ key = *properties;
+ ++properties;
+ if (*properties == NULL)
+ {
+ g_warning ("uevent_from_property_list: Ignoring key '%s' without value", key);
+ break;
+ }
+ value = *properties;
+ ++properties;
+ g_string_append (result, key);
+ g_string_append_c (result, '=');
+ g_string_append (result, value);
+ g_string_append_c (result, '\n');
+ }
+
+ return g_string_free (result, FALSE);
+}
+
+/**
+ * g_udev_testbed_add_devicev:
+ * @testbed: A #GUdevTestbed.
+ * @subsystem: The subsystem name, e. g. "usb"
+ * @name: The device name; arbitrary, but needs to be unique within the testbed
+ * @attributes: (transfer none): A list of device sysfs attributes, alternating
+ * names and values, terminated with NULL:
+ * { "key1", "value1", "key2", "value2", ..., NULL }
+ * @properties: (transfer none): A list of device udev properties; same format
+ * as @attributes
+ *
+ * This method is mostly meant for language bindings (where it is named
+ * #g_udev_testbed_add_device). For C programs it is usually more convenient to
+ * use #g_udev_testbed_add_device.
+ *
+ * Add a new device to the @testbed. A Linux kernel device always has a
+ * subsystem (such as "usb" or "pci"), and a device name. The test bed only
+ * builds a very simple sysfs structure without nested namespaces, so it
+ * requires device names to be unique. Some gudev client programs might make
+ * assumptions about the name (e. g. a SCSI disk block device should be called
+ * sdaN). A device also has an arbitrary number of sysfs attributes and udev
+ * properties; usually you should specify them upon creation, but it is also
+ * possible to change them later on with #g_udev_testbed_set_attribute and
+ * #g_udev_testbed_set_property.
+ *
+ * Returns: (transfer full): The sysfs path for the newly created device. Free
+ * with g_free().
+ *
+ * Rename to: g_udev_testbed_add_device
+ */
+gchar*
+g_udev_testbed_add_devicev (GUdevTestbed *testbed,
+ const gchar *subsystem,
+ const gchar *name,
+ const gchar **attributes,
+ const gchar **properties)
+{
+ gchar *dev_dir;
+ gchar *class_dir;
+ gchar *target, *link;
+ const gchar *key, *value;
+ gchar *prop_str;
+
+ dev_dir = g_build_filename (testbed->priv->sys_dir, "devices", name, NULL);
+ /* must not exist yet */
+ g_return_val_if_fail (!g_file_test (dev_dir, G_FILE_TEST_EXISTS), NULL);
+
+ /* create device and corresponding subsystem dir */
+ g_assert (g_mkdir_with_parents (dev_dir, 0755) == 0);
+ class_dir = g_build_filename (testbed->priv->sys_dir, "class", subsystem, NULL);
+ g_assert (g_mkdir_with_parents (class_dir, 0755) == 0);
+
+ /* subsystem symlink */
+ target = g_build_filename ("..", "..", "class", subsystem, NULL);
+ link = g_build_filename (dev_dir, "subsystem", NULL);
+ g_assert (symlink (target, link) == 0);
+ g_free (target);
+ g_free (link);
+
+ /* device symlink from class/ */
+ target = g_build_filename ("..", "..", "devices", name, NULL);
+ link = g_build_filename (class_dir, name, NULL);
+ g_assert (symlink (target, link) == 0);
+ g_free (target);
+ g_free (link);
+
+ g_free (class_dir);
+
+ /* attributes */
+ while (*attributes != NULL)
+ {
+ key = *attributes;
+ ++attributes;
+ if (*attributes == NULL)
+ {
+ g_warning ("g_udev_testbed_add_devicev: Ignoring attribute key '%s' without value", key);
+ break;
+ }
+ value = *attributes;
+ ++attributes;
+ g_udev_testbed_set_attribute (testbed, dev_dir, key, value);
+ }
+
+ /* properties; they go into the "uevent" sysfs attribute */
+ prop_str = uevent_from_property_list (properties);
+ g_udev_testbed_set_attribute (testbed, dev_dir, "uevent", prop_str);
+ g_free (prop_str);
+
+ return dev_dir;
+}
+
+/**
+ * g_udev_testbed_add_device: (skip)
+ * @testbed: A #GUdevTestbed.
+ * @subsystem: The subsystem name, e. g. "usb"
+ * @name: The device name; arbitrary, but needs to be unique within the testbed
+ * @...: Arbitrarily many pairs of sysfs attributes (alternating names and
+ * values), terminated by NULL, followed by arbitrarily many pairs of udev
+ * properties, terminated by another NULL.
+ *
+ * Add a new device to the @testbed. A Linux kernel device always has a
+ * subsystem (such as "usb" or "pci"), and a device name. The test bed only
+ * builds a very simple sysfs structure without nested namespaces, so it
+ * requires device names to be unique. Some gudev client programs might make
+ * assumptions about the name (e. g. a SCSI disk block device should be called
+ * sdaN). A device also has an arbitrary number of sysfs attributes and udev
+ * properties; usually you should specify them upon creation, but it is also
+ * possible to change them later on with #g_udev_testbed_set_attribute and
+ * #g_udev_testbed_set_property.
+ *
+ * Example:
+ * |[
+ * g_udev_testbed_add_device (testbed, "usb", "dev1",
+ * "idVendor", "0815", "idProduct", "AFFE", NULL,
+ * "ID_MODEL", "KoolGadget", NULL);
+ * ]|
+ *
+ * Returns: (transfer full): The sysfs path for the newly created device. Free
+ * with g_free().
+ */
+gchar*
+g_udev_testbed_add_device (GUdevTestbed *testbed,
+ const gchar *subsystem,
+ const gchar *name,
+ ...)
+{
+ va_list args;
+ int arg_set = 0; /* 0 -> attributes, 1 -> properties */
+ gchar *syspath;
+ const gchar *arg;
+ GArray *attributes;
+ GArray *properties;
+
+ attributes = g_array_new (TRUE, FALSE, sizeof (gchar*));
+ properties = g_array_new (TRUE, FALSE, sizeof (gchar*));
+
+ va_start (args, name);
+
+ for (;;) {
+ arg = va_arg (args, const gchar*);
+ /* we iterate arguments until NULL twice; first for the attributes, then
+ * for the properties */
+ if (arg == NULL)
+ {
+ if (++arg_set > 1)
+ break;
+ else
+ continue;
+ }
+
+ if (arg_set == 0)
+ g_array_append_val (attributes, arg);
+ else
+ g_array_append_val (properties, arg);
+ }
+
+ syspath = g_udev_testbed_add_devicev (testbed,
+ subsystem,
+ name,
+ (const gchar**) attributes->data,
+ (const gchar**) properties->data);
+
+ g_array_free (attributes, FALSE);
+ g_array_free (properties, FALSE);
+
+ va_end (args);
+
+ return syspath;
+}
+
+
+/**
+ * g_udev_testbed_set_attribute:
+ * @testbed: A #GUdevTestbed.
+ * @devpath: The full device path (including the sys dir itself)
+ * @name: The attribute name
+ * @value: The attribute value
+ *
+ * Set a sysfs attribute of a device.
+ */
+void
+g_udev_testbed_set_attribute (GUdevTestbed *testbed,
+ const gchar *devpath,
+ const gchar *name,
+ const gchar *value)
+{
+ gchar *attr_path;
+
+ attr_path = g_build_filename (devpath, name, NULL);
+ g_assert (g_file_set_contents (attr_path, value, -1, NULL));
+ g_free (attr_path);
+}
+
+/**
+ * g_udev_testbed_set_property:
+ * @testbed: A #GUdevTestbed.
+ * @devpath: The full device path (including the sys dir itself)
+ * @name: The property name
+ * @value: The property value
+ *
+ * Set an udev property of a device.
+ */
+void
+g_udev_testbed_set_property (GUdevTestbed *testbed,
+ const gchar *devpath,
+ const gchar *name,
+ const gchar *value)
+{
+ size_t name_len;
+ gchar *uevent_path;
+ gboolean existing = FALSE;
+ GString *props;
+ FILE *f;
+ char line[4096];
+
+ name_len = strlen (name);
+
+ /* read current properties from the uevent file; if name is already set,
+ * replace its value with the new one */
+ uevent_path = g_build_filename (devpath, "uevent", NULL);
+ f = fopen (uevent_path, "r");
+ g_assert (f != NULL);
+
+ props = g_string_sized_new (1024);
+ while (fgets (line, sizeof (line), f) != NULL)
+ {
+ if (g_str_has_prefix (line, name) && line[name_len] == '=')
+ {
+ existing = TRUE;
+ g_string_append (props, name);
+ g_string_append_c (props, '=');
+ g_string_append (props, value);
+ g_string_append_c (props, '\n');
+ }
+ else
+ {
+ g_string_append (props, line);
+ }
+ }
+ fclose (f);
+
+ /* if property name does not yet exist, append it */
+ if (!existing)
+ {
+ g_string_append (props, name);
+ g_string_append_c (props, '=');
+ g_string_append (props, value);
+ g_string_append_c (props, '\n');
+ }
+
+
+ /* write it back */
+ f = fopen (uevent_path, "w");
+ g_assert (f != NULL);
+ g_assert_cmpint (fwrite (props->str, sizeof (gchar), props->len, f), ==, props->len);
+ fclose (f);
+
+ g_string_free (props, TRUE);
+ g_free (uevent_path);
+}
diff --git a/src/gudev/gudevtestbed.h b/src/gudev/gudevtestbed.h
new file mode 100644
index 0000000..dc934dd
--- /dev/null
+++ b/src/gudev/gudevtestbed.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012 Canonical Ltd.
+ * Author: Martin Pitt <martin.pitt@ubuntu.com>
+ *
+ * This library 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 of the License, or (at your option) any later version.
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (_GUDEV_COMPILATION) && !defined(_GUDEV_INSIDE_GUDEV_H)
+#error "Only <gudev/gudev.h> can be included directly, this file may disappear or change contents."
+#endif
+
+#ifndef __G_UDEV_TESTBED_H__
+#define __G_UDEV_TESTBED_H__
+
+#include <gudev/gudevtypes.h>
+
+G_BEGIN_DECLS
+
+#define G_UDEV_TYPE_TESTBED (g_udev_testbed_get_type ())
+#define G_UDEV_TESTBED(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_UDEV_TYPE_TESTBED, GUdevTestbed))
+#define G_UDEV_TESTBED_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_UDEV_TYPE_TESTBED, GUdevTestbedClass))
+#define G_UDEV_IS_TESTBED(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_UDEV_TYPE_TESTBED))
+#define G_UDEV_IS_TESTBED_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_UDEV_TYPE_TESTBED))
+#define G_UDEV_TESTBED_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_UDEV_TYPE_TESTBED, GUdevTestbedClass))
+
+typedef struct _GUdevTestbedClass GUdevTestbedClass;
+typedef struct _GUdevTestbedPrivate GUdevTestbedPrivate;
+
+/**
+ * GUdevTestbed:
+ * @parent: Parent object
+ *
+ * The #GUdevTestbed struct is opaque and should not be accessed directly.
+ */
+struct _GUdevTestbed
+{
+ GObject parent;
+
+ /*< private >*/
+ GUdevTestbedPrivate *priv;
+};
+
+/**
+ * GUdevTestbedClass:
+ * @parent_class: Parent class.
+ *
+ * Class structure for #GUdevTestbed.
+ */
+struct _GUdevTestbedClass
+{
+ GObjectClass parent_class;
+
+ /*< private >*/
+ /* Padding for future expansion */
+ void (*reserved1) (void);
+ void (*reserved2) (void);
+ void (*reserved3) (void);
+ void (*reserved4) (void);
+ void (*reserved5) (void);
+ void (*reserved6) (void);
+ void (*reserved7) (void);
+ void (*reserved8) (void);
+};
+
+GType g_udev_testbed_get_type (void) G_GNUC_CONST;
+GUdevTestbed *g_udev_testbed_new (void);
+const gchar *g_udev_testbed_get_root_dir (GUdevTestbed *testbed);
+const gchar *g_udev_testbed_get_sys_dir (GUdevTestbed *testbed);
+gchar *g_udev_testbed_add_devicev (GUdevTestbed *testbed,
+ const gchar *subsystem,
+ const gchar *name,
+ const gchar **attributes,
+ const gchar **properties);
+gchar *g_udev_testbed_add_device (GUdevTestbed *testbed,
+ const gchar *subsystem,
+ const gchar *name,
+ ...);
+void g_udev_testbed_set_attribute (GUdevTestbed *testbed,
+ const gchar *devpath,
+ const gchar *name,
+ const gchar *value);
+void g_udev_testbed_set_property (GUdevTestbed *testbed,
+ const gchar *devpath,
+ const gchar *name,
+ const gchar *value);
+
+G_END_DECLS
+
+#endif /* __G_UDEV_TESTBED_H__ */
diff --git a/src/gudev/gudevtypes.h b/src/gudev/gudevtypes.h
index 8884827..f97751e 100644
--- a/src/gudev/gudevtypes.h
+++ b/src/gudev/gudevtypes.h
@@ -33,6 +33,7 @@ G_BEGIN_DECLS
typedef struct _GUdevClient GUdevClient;
typedef struct _GUdevDevice GUdevDevice;
typedef struct _GUdevEnumerator GUdevEnumerator;
+typedef struct _GUdevTestbed GUdevTestbed;
/**
* GUdevDeviceNumber:
diff --git a/src/test/test-gudev.c b/src/test/test-gudev.c
new file mode 100644
index 0000000..b38eaef
--- /dev/null
+++ b/src/test/test-gudev.c
@@ -0,0 +1,248 @@
+/*
+ * test-gudev
+ *
+ * Copyright (C) 2012 Canonical Ltd.
+ * Author: Martin Pitt <martin.pitt@ubuntu.com>
+ *
+ * This library 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.
+ */
+
+#include <glib.h>
+#include <string.h>
+#include <gudev/gudev.h>
+
+typedef struct {
+ GUdevTestbed *testbed;
+} GUdevTestbedFixture;
+
+static void
+gudev_testbed_fixture_setup (GUdevTestbedFixture *fixture, gconstpointer data)
+{
+ fixture->testbed = g_udev_testbed_new();
+ g_assert (fixture->testbed != NULL);
+}
+
+static void
+gudev_testbed_fixture_teardown (GUdevTestbedFixture *fixture, gconstpointer data)
+{
+ g_object_unref (fixture->testbed);
+}
+
+/* enumeration on the system picks up some devices */
+static void
+gudev_system_enumerate (void)
+{
+ GUdevClient *client;
+ GUdevEnumerator *enumerator;
+ GList *result;
+ GUdevDevice *device;
+
+ client = g_udev_client_new (NULL);
+ g_assert (client);
+
+ /* there ought to be at least one device on every system; e. g. a CPU is
+ * always handy to have */
+ enumerator = g_udev_enumerator_new (client);
+ g_assert (enumerator);
+ result = g_udev_enumerator_execute (enumerator);
+ g_assert_cmpuint (g_list_length (result), >, 0);
+
+ /* check that the entry is an useful GUdevDevice */
+ device = G_UDEV_DEVICE (result->data);
+ g_assert (device);
+ g_assert_cmpstr (g_udev_device_get_name (device), !=, "");
+ g_assert (strstr (g_udev_device_get_sysfs_path (device), "/sys/") != NULL);
+
+ g_list_free_full (result, g_object_unref);
+ g_object_unref (enumerator);
+ g_object_unref (client);
+}
+
+/* Empty GUdevTestbed without any devices */
+static void
+gudev_testbed_empty (GUdevTestbedFixture *fixture, gconstpointer data)
+{
+ GUdevClient *client;
+ GUdevEnumerator *enumerator;
+ GList *result;
+
+ client = g_udev_client_new (NULL);
+ g_assert (client);
+
+ enumerator = g_udev_enumerator_new (client);
+ g_assert (enumerator);
+ result = g_udev_enumerator_execute (enumerator);
+ g_assert_cmpuint (g_list_length (result), ==, 0);
+
+ g_object_unref (enumerator);
+ g_object_unref (client);
+}
+
+/* common checks for gudev_testbed_add_device{,v}() */
+static void
+_gudev_testbed_check_extkeyboard1 (const gchar* syspath)
+{
+ GUdevClient *client;
+ GUdevEnumerator *enumerator;
+ GList *result;
+ GUdevDevice *device;
+ client = g_udev_client_new (NULL);
+ g_assert (client);
+
+ enumerator = g_udev_enumerator_new (client);
+ g_assert (enumerator);
+ result = g_udev_enumerator_execute (enumerator);
+ g_assert_cmpuint (g_list_length (result), ==, 1);
+
+ /* check that the entry matches what we put into our test bed */
+ device = G_UDEV_DEVICE (result->data);
+ g_assert (device);
+ g_assert_cmpstr (g_udev_device_get_name (device), ==, "extkeyboard1");
+ g_assert_cmpstr (g_udev_device_get_sysfs_path (device), ==, syspath);
+
+ g_assert_cmpstr (g_udev_device_get_sysfs_attr (device, "idVendor"), ==, "0815");
+ g_assert_cmpstr (g_udev_device_get_sysfs_attr (device, "idProduct"), ==, "AFFE");
+ g_assert_cmpstr (g_udev_device_get_sysfs_attr (device, "noSuchAttr"), ==, NULL);
+
+ g_assert_cmpstr (g_udev_device_get_property (device, "DEVPATH"), ==, "/devices/extkeyboard1");
+ g_assert_cmpstr (g_udev_device_get_property (device, "SUBSYSTEM"), ==, "usb");
+ g_assert_cmpstr (g_udev_device_get_property (device, "ID_INPUT"), ==, "1");
+ g_assert_cmpstr (g_udev_device_get_property (device, "ID_INPUT_KEYBOARD"), ==, "1");
+ g_assert_cmpstr (g_udev_device_get_property (device, "NO_SUCH_PROP"), ==, NULL);
+
+ g_list_free_full (result, g_object_unref);
+ g_object_unref (enumerator);
+ g_object_unref (client);
+}
+
+/* GUdevTestbed add_devicev() with adding one device */
+static void
+gudev_testbed_add_devicev (GUdevTestbedFixture *fixture, gconstpointer data)
+{
+ gchar *syspath;
+ const gchar *attributes[] = { "idVendor", "0815", "idProduct", "AFFE", NULL };
+ const gchar *properties[] = { "ID_INPUT", "1", "ID_INPUT_KEYBOARD", "1", NULL };
+
+ syspath = g_udev_testbed_add_devicev (fixture->testbed,
+ "usb",
+ "extkeyboard1",
+ attributes,
+ properties);
+ g_assert (syspath);
+ g_assert (g_str_has_suffix (syspath, "/sys/devices/extkeyboard1"));
+
+ _gudev_testbed_check_extkeyboard1(syspath);
+ g_free (syspath);
+}
+
+/* GUdevTestbed add_device() with adding one device */
+static void
+gudev_testbed_add_device (GUdevTestbedFixture *fixture, gconstpointer data)
+{
+ gchar *syspath;
+
+ syspath = g_udev_testbed_add_device (fixture->testbed,
+ "usb",
+ "extkeyboard1",
+ /* attributes */
+ "idVendor", "0815", "idProduct", "AFFE", NULL,
+ /* properties */
+ "ID_INPUT", "1", "ID_INPUT_KEYBOARD", "1", NULL);
+ g_assert (syspath);
+ g_assert (g_str_has_suffix (syspath, "/sys/devices/extkeyboard1"));
+
+ _gudev_testbed_check_extkeyboard1(syspath);
+ g_free (syspath);
+}
+
+static void
+gudev_testbed_set_attribute (GUdevTestbedFixture *fixture, gconstpointer data)
+{
+ GUdevClient *client;
+ GUdevDevice *device;
+ gchar *syspath;
+
+ client = g_udev_client_new (NULL);
+
+ syspath = g_udev_testbed_add_device (fixture->testbed,
+ "usb",
+ "extkeyboard1",
+ /* attributes */
+ "idVendor", "0815", "idProduct", "AFFE", NULL,
+ /* properties */
+ NULL);
+
+ /* change an existing attribute */
+ g_udev_testbed_set_attribute (fixture->testbed, syspath, "idProduct", "BEEF");
+ /* add a new one */
+ g_udev_testbed_set_attribute (fixture->testbed, syspath, "color", "yellow");
+
+ device = g_udev_client_query_by_sysfs_path (client, syspath);
+ g_assert (device);
+ g_assert_cmpstr (g_udev_device_get_sysfs_attr (device, "idVendor"), ==, "0815");
+ g_assert_cmpstr (g_udev_device_get_sysfs_attr (device, "idProduct"), ==, "BEEF");
+ g_assert_cmpstr (g_udev_device_get_sysfs_attr (device, "color"), ==, "yellow");
+ g_object_unref (device);
+
+ g_object_unref (client);
+ g_free (syspath);
+}
+
+static void
+gudev_testbed_set_property (GUdevTestbedFixture *fixture, gconstpointer data)
+{
+ GUdevClient *client;
+ GUdevDevice *device;
+ gchar *syspath;
+
+ client = g_udev_client_new (NULL);
+
+ syspath = g_udev_testbed_add_device (fixture->testbed,
+ "usb",
+ "extkeyboard1",
+ /* attributes */
+ NULL,
+ /* properties */
+ "ID_INPUT", "1", NULL);
+
+ /* change an existing property */
+ g_udev_testbed_set_property (fixture->testbed, syspath, "ID_INPUT", "0");
+ /* add a new one */
+ g_udev_testbed_set_property (fixture->testbed, syspath, "ID_COLOR", "green");
+
+ device = g_udev_client_query_by_sysfs_path (client, syspath);
+ g_assert (device);
+ g_assert_cmpstr (g_udev_device_get_property (device, "ID_INPUT"), ==, "0");
+ g_assert_cmpstr (g_udev_device_get_property (device, "ID_COLOR"), ==, "green");
+ g_object_unref (device);
+
+ g_object_unref (client);
+ g_free (syspath);
+}
+
+int
+main (int argc, char **argv)
+{
+ g_type_init ();
+ g_test_init (&argc, &argv, NULL);
+
+ /* tests on system */
+ g_test_add_func ("/gudev-system/enumerate", gudev_system_enumerate);
+
+ /* tests with GUdevTestbed */
+ g_test_add ("/gudev-testbed/empty", GUdevTestbedFixture, NULL, gudev_testbed_fixture_setup,
+ gudev_testbed_empty, gudev_testbed_fixture_teardown);
+ g_test_add ("/gudev-testbed/add_devicev", GUdevTestbedFixture, NULL, gudev_testbed_fixture_setup,
+ gudev_testbed_add_devicev, gudev_testbed_fixture_teardown);
+ g_test_add ("/gudev-testbed/add_device", GUdevTestbedFixture, NULL, gudev_testbed_fixture_setup,
+ gudev_testbed_add_device, gudev_testbed_fixture_teardown);
+ g_test_add ("/gudev-testbed/set_attribute", GUdevTestbedFixture, NULL, gudev_testbed_fixture_setup,
+ gudev_testbed_set_attribute, gudev_testbed_fixture_teardown);
+ g_test_add ("/gudev-testbed/set_property", GUdevTestbedFixture, NULL, gudev_testbed_fixture_setup,
+ gudev_testbed_set_property, gudev_testbed_fixture_teardown);
+
+ return g_test_run ();
+}
--
1.7.10.4
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply related
* [PATCH] Testbeds for libudev/gudev clients
From: Martin Pitt @ 2012-07-06 5:01 UTC (permalink / raw)
To: linux-hotplug
[-- Attachment #1.1: Type: text/plain, Size: 3789 bytes --]
Hello friends of udev,
I would like to make it vastly easier, and for some aspects possible
at all, to write automatic tests for libudev/gudev client software.
For example, upower's test suite builds a temporary mock sysfs tree:
http://cgit.freedesktop.org/upower/tree/src/linux/integration-test
(line 106 ff.) so that we can test its logic with having various
combinations of empty or full batteries, UPSes, ACs, power supplies
that power the whole platform vs. only particular devices, and so on.
Often developers do not have that hardware, or it's not easy to set up
a situation to reproduce a bug (waiting a couple of hours for your
battery to drain, etc.), so it is much more convenient to reproduce
the sysfs attributes in a small test bed.
Another example is the test suite for Ubuntu's driver package
detection which implements the corresponding PackageKit API; it also
uses a "fake sys" module largely derived from upower's:
https://github.com/tseliot/ubuntu-drivers-common/blob/master/tests/fakesysfs.py
There are several problems with these approaches:
* Copy&paste code is obviously bad. It would be much better if the
convenience API to set up a libudev/gudev test bed would be
provided by libudev/gudev itself. Also, it should be in C and
available through introspection, so that you can use it from a
variety of languages; this means it should be in gudev.
I am currently working on a first patch for this, and will post it
for discussion here when I have something working.
* upower's test suite is not working any more with current
systemd/udev versions because the support for $SYSFS_PATH was
removed.
I have a patch attached to generalize this to use
$UDEV_TEST_PREFIX, replacing the current compile-time TEST_PREFIX
macro. This will also allow us in the future to create mock dev and
run/rules.d files without having to change the environment variable
and temporary directory structure again. Once that (or a variant of
it) is in, I'll adapt upower's test suite to also set
UDEV_TEST_PREFIX so that it works with both old and new libudev
versions.
With that patch you can now e. g. call
UDEV_TEST_PREFIX=test ./udevadm info --export-db
UDEV_TEST_PREFIX=test ./udevadm info --query=all --path=/devices/virtual/block/md0
in the built systemd tree and it will DTRT. I tested with
./test-libudev, which also works fine.
* You can currently only control the coldplug part. That's why
upower's test suite starts the daemon again in each test case. But
you cannot test the dynamic parts with that, such as updating the
properties correctly when changing power sources. What's missing is
to emulate corresponding uevents for the $UDEV_TEST_PREFIX tree.
My initial idea about this was to make udev_monitor_send_device()
public API and relax the uid/pid checks to allow non-root messages
when the corresponding sysfs dir is writable for the calling user
(or something like that). But as non-root users are not allowed to
send NETLINK_KOBJECT_UEVENT messages, this is moot anyway as we do
not want to require root privileges for tests, at least not in all
cases.
So my current idea about this to have gudev read events from a
simple GIO socket in $UDEV_TEST_PREFIX/uevent instead from
libudev_monitor_* if $UDEV_TEST_PREFIX is given, as in that case
the real uevents won't coincide with the sysfs anyway. The "uevent"
signal would then be raised on messages from that socket. Do you
think that's too crazy?
Thanks,
Martin
--
Martin Pitt | http://www.piware.de
Ubuntu Developer (www.ubuntu.com) | Debian Developer (www.debian.org)
[-- Attachment #1.2: 0001-udev-Replace-compile-time-TEST_PREFIX-with-runtime-U.patch --]
[-- Type: text/x-diff, Size: 32938 bytes --]
From eebd02e708cc73e08f6ff13b49ac4f99716c6777 Mon Sep 17 00:00:00 2001
From: Martin Pitt <martinpitt@gnome.org>
Date: Thu, 5 Jul 2012 13:34:23 +0200
Subject: [PATCH] udev: Replace compile-time TEST_PREFIX with runtime $UDEV_TEST_PREFIX
The removal of $SYSFS_PATH and configurable paths made it impossible for test
suites to set up a local sysfs tree and point libudev to that. Restore and
generalize the functionality with checking the $UDEV_TEST_PREFIX environment
variable which replaces the compile-time TEST_PREFIX macro.
Also adjust udevadm to respect the test prefix.
---
Makefile.am | 7 +++--
src/libudev/libudev-device-private.c | 11 +++++---
src/libudev/libudev-device.c | 50 ++++++++++++++++++++--------------
src/libudev/libudev-enumerate.c | 19 +++++++++----
src/libudev/libudev-private.h | 4 ---
src/libudev/libudev.c | 23 ++++++++++++++++
src/libudev/libudev.h | 1 +
src/test/test-udev.c | 2 +-
src/udev/udev-event.c | 14 ++++++----
src/udev/udev-node.c | 11 +++++---
src/udev/udev-rules.c | 26 ++++++++++++------
src/udev/udevadm-info.c | 17 +++++++++---
12 files changed, 125 insertions(+), 60 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index 3b7ec0b..6d941a0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1630,6 +1630,10 @@ TESTS += \
test/udev-test.pl \
test/rules-test.sh
+# The test-udev program should run with relative path names for /sys, /dev,
+# /run prefixed, pointing to our test/ directory.
+TESTS_ENVIRONMENT = UDEV_TEST_PREFIX=test
+
noinst_PROGRAMS += \
test-libudev \
test-udev
@@ -1647,10 +1651,7 @@ test_udev_SOURCES = \
$(libudev_core_la_SOURCES) \
$(libudev_private_la_SOURCES)
-# The test-udev program needs everything compiled with relative path
-# names for /sys, /dev, /run prefixed, pointing to our test/ directory.
test_udev_CFLAGS = \
- -DTEST_PREFIX=\"test\" \
$(libudev_core_la_CFLAGS) \
$(libudev_private_la_CFLAGS)
diff --git a/src/libudev/libudev-device-private.c b/src/libudev/libudev-device-private.c
index 2347736..f6cbb46 100644
--- a/src/libudev/libudev-device-private.c
+++ b/src/libudev/libudev-device-private.c
@@ -30,7 +30,8 @@ static void udev_device_tag(struct udev_device *dev, const char *tag, bool add)
id = udev_device_get_id_filename(dev);
if (id == NULL)
return;
- util_strscpyl(filename, sizeof(filename), TEST_PREFIX "/run/udev/tags/", tag, "/", id, NULL);
+ util_strscpyl(filename, sizeof(filename), udev_get_test_prefix(udev_device_get_udev(dev)),
+ "/run/udev/tags/", tag, "/", id, NULL);
if (add) {
int fd;
@@ -107,7 +108,8 @@ int udev_device_update_db(struct udev_device *udev_device)
return -1;
has_info = device_has_info(udev_device);
- util_strscpyl(filename, sizeof(filename), TEST_PREFIX "/run/udev/data/", id, NULL);
+ util_strscpyl(filename, sizeof(filename), udev_get_test_prefix(udev_device_get_udev(udev_device)),
+ "/run/udev/data/", id, NULL);
/* do not store anything for otherwise empty devices */
if (!has_info &&
@@ -138,7 +140,7 @@ int udev_device_update_db(struct udev_device *udev_device)
if (major(udev_device_get_devnum(udev_device)) > 0) {
udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(udev_device))
- fprintf(f, "S:%s\n", udev_list_entry_get_name(list_entry) + strlen(TEST_PREFIX "/dev/"));
+ fprintf(f, "S:%s\n", udev_list_entry_get_name(list_entry) + strlen(udev_get_test_prefix(udev)) + strlen("/dev/"));
if (udev_device_get_devlink_priority(udev_device) != 0)
fprintf(f, "L:%i\n", udev_device_get_devlink_priority(udev_device));
if (udev_device_get_watch_handle(udev_device) >= 0)
@@ -175,7 +177,8 @@ int udev_device_delete_db(struct udev_device *udev_device)
id = udev_device_get_id_filename(udev_device);
if (id == NULL)
return -1;
- util_strscpyl(filename, sizeof(filename), TEST_PREFIX "/run/udev/data/", id, NULL);
+ util_strscpyl(filename, sizeof(filename), udev_get_test_prefix(udev_device_get_udev(udev_device)),
+ "/run/udev/data/", id, NULL);
unlink(filename);
return 0;
}
diff --git a/src/libudev/libudev-device.c b/src/libudev/libudev-device.c
index a8277d1..665619a 100644
--- a/src/libudev/libudev-device.c
+++ b/src/libudev/libudev-device.c
@@ -360,7 +360,8 @@ void udev_device_add_property_from_string_parse(struct udev_device *udev_device,
if (startswith(property, "DEVPATH=")) {
char path[UTIL_PATH_SIZE];
- util_strscpyl(path, sizeof(path), TEST_PREFIX "/sys", &property[8], NULL);
+ util_strscpyl(path, sizeof(path), udev_get_test_prefix(udev_device_get_udev(udev_device)),
+ "/sys", &property[8], NULL);
udev_device_set_syspath(udev_device, path);
} else if (startswith(property, "SUBSYSTEM=")) {
udev_device_set_subsystem(udev_device, &property[10]);
@@ -467,6 +468,9 @@ int udev_device_read_db(struct udev_device *udev_device, const char *dbfile)
char filename[UTIL_PATH_SIZE];
char line[UTIL_LINE_SIZE];
FILE *f;
+ const char *prefix;
+
+ prefix = udev_get_test_prefix(udev_device_get_udev(udev_device));
/* providing a database file will always force-load it */
if (dbfile == NULL) {
@@ -479,7 +483,7 @@ int udev_device_read_db(struct udev_device *udev_device, const char *dbfile)
id = udev_device_get_id_filename(udev_device);
if (id == NULL)
return -1;
- util_strscpyl(filename, sizeof(filename), TEST_PREFIX "/run/udev/data/", id, NULL);
+ util_strscpyl(filename, sizeof(filename), prefix, "/run/udev/data/", id, NULL);
dbfile = filename;
}
@@ -502,7 +506,7 @@ int udev_device_read_db(struct udev_device *udev_device, const char *dbfile)
val = &line[2];
switch(line[0]) {
case 'S':
- util_strscpyl(filename, sizeof(filename), TEST_PREFIX "/dev/", val, NULL);
+ util_strscpyl(filename, sizeof(filename), prefix, "/dev/", val, NULL);
udev_device_add_devlink(udev_device, filename, 0);
break;
case 'L':
@@ -635,6 +639,11 @@ _public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, con
char *pos;
struct stat statbuf;
struct udev_device *udev_device;
+ const char *prefix;
+ int prefix_sys_len;
+
+ prefix = udev_get_test_prefix(udev);
+ prefix_sys_len = strlen(prefix) + strlen("/sys");
if (udev == NULL)
return NULL;
@@ -642,13 +651,14 @@ _public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, con
return NULL;
/* path starts in sys */
- if (!startswith(syspath, TEST_PREFIX "/sys")) {
+ if (!startswith(syspath, udev_get_test_prefix(udev)) ||
+ !startswith(syspath + strlen(udev_get_test_prefix(udev)), "/sys")) {
udev_dbg(udev, "not in sys :%s\n", syspath);
return NULL;
}
/* path is not a root directory */
- subdir = syspath + strlen(TEST_PREFIX "/sys");
+ subdir = syspath + prefix_sys_len;
pos = strrchr(subdir, '/');
if (pos == NULL || pos[1] == '\0' || pos < &subdir[2])
return NULL;
@@ -657,7 +667,7 @@ _public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, con
util_strscpy(path, sizeof(path), syspath);
util_resolve_sys_link(udev, path, sizeof(path));
- if (startswith(path + strlen(TEST_PREFIX "/sys"), "/devices/")) {
+ if (startswith(path + prefix_sys_len, "/devices/")) {
char file[UTIL_PATH_SIZE];
/* all "devices" require a "uevent" file */
@@ -709,8 +719,8 @@ _public_ struct udev_device *udev_device_new_from_devnum(struct udev *udev, char
return NULL;
/* use /sys/dev/{block,char}/<maj>:<min> link */
- snprintf(path, sizeof(path), TEST_PREFIX "/sys/dev/%s/%u:%u",
- type_str, major(devnum), minor(devnum));
+ snprintf(path, sizeof(path), "%s/sys/dev/%s/%u:%u",
+ udev_get_test_prefix(udev), type_str, major(devnum), minor(devnum));
return udev_device_new_from_syspath(udev, path);
}
@@ -790,22 +800,22 @@ _public_ struct udev_device *udev_device_new_from_subsystem_sysname(struct udev
struct stat statbuf;
if (streq(subsystem, "subsystem")) {
- util_strscpyl(path, sizeof(path), TEST_PREFIX "/sys/subsystem/", sysname, NULL);
+ util_strscpyl(path, sizeof(path), udev_get_test_prefix(udev), "/sys/subsystem/", sysname, NULL);
if (stat(path, &statbuf) == 0)
goto found;
- util_strscpyl(path, sizeof(path), TEST_PREFIX "/sys/bus/", sysname, NULL);
+ util_strscpyl(path, sizeof(path), udev_get_test_prefix(udev), "/sys/bus/", sysname, NULL);
if (stat(path, &statbuf) == 0)
goto found;
- util_strscpyl(path, sizeof(path), TEST_PREFIX "/sys/class/", sysname, NULL);
+ util_strscpyl(path, sizeof(path), udev_get_test_prefix(udev), "/sys/class/", sysname, NULL);
if (stat(path, &statbuf) == 0)
goto found;
goto out;
}
if (streq(subsystem, "module")) {
- util_strscpyl(path, sizeof(path), TEST_PREFIX "/sys/module/", sysname, NULL);
+ util_strscpyl(path, sizeof(path), udev_get_test_prefix(udev), "/sys/module/", sysname, NULL);
if (stat(path, &statbuf) == 0)
goto found;
goto out;
@@ -821,26 +831,26 @@ _public_ struct udev_device *udev_device_new_from_subsystem_sysname(struct udev
driver[0] = '\0';
driver = &driver[1];
- util_strscpyl(path, sizeof(path), TEST_PREFIX "/sys/subsystem/", subsys, "/drivers/", driver, NULL);
+ util_strscpyl(path, sizeof(path), udev_get_test_prefix(udev), "/sys/subsystem/", subsys, "/drivers/", driver, NULL);
if (stat(path, &statbuf) == 0)
goto found;
- util_strscpyl(path, sizeof(path), TEST_PREFIX "/sys/bus/", subsys, "/drivers/", driver, NULL);
+ util_strscpyl(path, sizeof(path), udev_get_test_prefix(udev), "/sys/bus/", subsys, "/drivers/", driver, NULL);
if (stat(path, &statbuf) == 0)
goto found;
}
goto out;
}
- util_strscpyl(path, sizeof(path), TEST_PREFIX "/sys/subsystem/", subsystem, "/devices/", sysname, NULL);
+ util_strscpyl(path, sizeof(path), udev_get_test_prefix(udev), "/sys/subsystem/", subsystem, "/devices/", sysname, NULL);
if (stat(path, &statbuf) == 0)
goto found;
- util_strscpyl(path, sizeof(path), TEST_PREFIX "/sys/bus/", subsystem, "/devices/", sysname, NULL);
+ util_strscpyl(path, sizeof(path), udev_get_test_prefix(udev), "/sys/bus/", subsystem, "/devices/", sysname, NULL);
if (stat(path, &statbuf) == 0)
goto found;
- util_strscpyl(path, sizeof(path), TEST_PREFIX "/sys/class/", subsystem, "/", sysname, NULL);
+ util_strscpyl(path, sizeof(path), udev_get_test_prefix(udev), "/sys/class/", subsystem, "/", sysname, NULL);
if (stat(path, &statbuf) == 0)
goto found;
out:
@@ -892,7 +902,7 @@ static struct udev_device *device_new_from_parent(struct udev_device *udev_devic
const char *subdir;
util_strscpy(path, sizeof(path), udev_device->syspath);
- subdir = path + strlen(TEST_PREFIX "/sys/");
+ subdir = path + strlen(udev_get_test_prefix(udev_device_get_udev(udev_device))) + strlen("/sys/");
for (;;) {
char *pos;
@@ -1443,7 +1453,7 @@ int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath
udev_device->syspath = strdup(syspath);
if (udev_device->syspath == NULL)
return -ENOMEM;
- udev_device->devpath = udev_device->syspath + strlen(TEST_PREFIX "/sys");
+ udev_device->devpath = udev_device->syspath + strlen(udev_get_test_prefix(udev_device_get_udev(udev_device))) + strlen("/sys");
udev_device_add_property(udev_device, "DEVPATH", udev_device->devpath);
pos = strrchr(udev_device->syspath, '/');
@@ -1476,7 +1486,7 @@ int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode
{
free(udev_device->devnode);
if (devnode[0] != '/') {
- if (asprintf(&udev_device->devnode, TEST_PREFIX "/dev/%s", devnode) < 0)
+ if (asprintf(&udev_device->devnode, "%s/dev/%s", udev_get_test_prefix(udev_device_get_udev(udev_device)), devnode) < 0)
udev_device->devnode = NULL;
} else {
udev_device->devnode = strdup(devnode);
diff --git a/src/libudev/libudev-enumerate.c b/src/libudev/libudev-enumerate.c
index a945758..1fb9208 100644
--- a/src/libudev/libudev-enumerate.c
+++ b/src/libudev/libudev-enumerate.c
@@ -214,7 +214,7 @@ static bool devices_delay_end(struct udev *udev, const char *syspath)
int i;
for (i = 0; delay_device_list[i] != NULL; i++) {
- if (strstr(syspath + strlen("/sys"), delay_device_list[i]) != NULL)
+ if (strstr(syspath + strlen(udev_get_test_prefix(udev)) + strlen("/sys"), delay_device_list[i]) != NULL)
return true;
}
return false;
@@ -649,7 +649,8 @@ static int scan_dir_and_add_devices(struct udev_enumerate *udev_enumerate,
struct dirent *dent;
s = path;
- l = util_strpcpyl(&s, sizeof(path), "/sys/", basedir, NULL);
+ l = util_strpcpyl(&s, sizeof(path), udev_get_test_prefix(udev_enumerate_get_udev(udev_enumerate)),
+ "/sys/", basedir, NULL);
if (subdir1 != NULL)
l = util_strpcpyl(&s, l, "/", subdir1, NULL);
if (subdir2 != NULL)
@@ -728,7 +729,8 @@ static int scan_dir(struct udev_enumerate *udev_enumerate, const char *basedir,
DIR *dir;
struct dirent *dent;
- util_strscpyl(path, sizeof(path), "/sys/", basedir, NULL);
+ util_strscpyl(path, sizeof(path), udev_get_test_prefix(udev_enumerate_get_udev(udev_enumerate)),
+ "/sys/", basedir, NULL);
dir = opendir(path);
if (dir == NULL)
return -1;
@@ -875,8 +877,12 @@ static int scan_devices_children(struct udev_enumerate *enumerate)
static int scan_devices_all(struct udev_enumerate *udev_enumerate)
{
struct stat statbuf;
+ char path[UTIL_PATH_SIZE];
+
+ util_strscpyl(path, sizeof(path), udev_get_test_prefix(udev_enumerate_get_udev(udev_enumerate)),
+ "/sys/subsystem", NULL);
- if (stat("/sys/subsystem", &statbuf) == 0) {
+ if (stat(path, &statbuf) == 0) {
/* we have /subsystem/, forget all the old stuff */
scan_dir(udev_enumerate, "subsystem", "devices", NULL);
} else {
@@ -924,6 +930,7 @@ _public_ int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerat
{
struct stat statbuf;
const char *subsysdir;
+ char path[UTIL_PATH_SIZE];
if (udev_enumerate == NULL)
return -EINVAL;
@@ -932,7 +939,9 @@ _public_ int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerat
if (match_subsystem(udev_enumerate, "module"))
scan_dir_and_add_devices(udev_enumerate, "module", NULL, NULL);
- if (stat("/sys/subsystem", &statbuf) == 0)
+ util_strscpyl(path, sizeof(path), udev_get_test_prefix(udev_enumerate_get_udev(udev_enumerate)),
+ "/sys/subsystem", NULL);
+ if (stat(path, &statbuf) == 0)
subsysdir = "subsystem";
else
subsysdir = "bus";
diff --git a/src/libudev/libudev-private.h b/src/libudev/libudev-private.h
index 4eb4a59..af3338f 100644
--- a/src/libudev/libudev-private.h
+++ b/src/libudev/libudev-private.h
@@ -25,10 +25,6 @@
#define READ_END 0
#define WRITE_END 1
-#ifndef TEST_PREFIX
-#define TEST_PREFIX ""
-#endif
-
/* avoid (sometimes expensive) calculations of parameters for debug output */
#define udev_log_cond(udev, prio, arg...) \
do { \
diff --git a/src/libudev/libudev.c b/src/libudev/libudev.c
index 07a24d5..7be9c89 100644
--- a/src/libudev/libudev.c
+++ b/src/libudev/libudev.c
@@ -43,6 +43,7 @@ struct udev {
void *userdata;
struct udev_list properties_list;
int log_priority;
+ char *test_prefix;
};
void udev_log(struct udev *udev,
@@ -95,6 +96,23 @@ _public_ void udev_set_userdata(struct udev *udev, void *userdata)
}
/**
+ * udev_get_test_prefix:
+ * @udev: udev library context
+ *
+ * Retrieve the file system root prefix for accessing /sys, /dev, etc. The
+ * default is empty, meaning the normal file system root. For testing purposes,
+ * it can be overridden with the environment variable $UDEV_TEST_PREFIX.
+ *
+ * Returns: the test prefix
+ **/
+_public_ const char *udev_get_test_prefix(struct udev *udev)
+{
+ if (udev == NULL)
+ return NULL;
+ return udev->test_prefix;
+}
+
+/**
* udev_new:
*
* Create udev library context. This reads the udev configuration
@@ -190,6 +208,10 @@ _public_ struct udev *udev_new(void)
fclose(f);
}
+ /* test prefix in environment? */
+ env = getenv("UDEV_TEST_PREFIX");
+ udev->test_prefix = strdup(env ? env : "");
+
/* environment overrides config */
env = getenv("UDEV_LOG");
if (env != NULL)
@@ -231,6 +253,7 @@ _public_ struct udev *udev_unref(struct udev *udev)
if (udev->refcount > 0)
return udev;
udev_list_cleanup(&udev->properties_list);
+ free(udev->test_prefix);
free(udev);
return NULL;
}
diff --git a/src/libudev/libudev.h b/src/libudev/libudev.h
index 8643984..5dd3f98 100644
--- a/src/libudev/libudev.h
+++ b/src/libudev/libudev.h
@@ -38,6 +38,7 @@ int udev_get_log_priority(struct udev *udev);
void udev_set_log_priority(struct udev *udev, int priority);
void *udev_get_userdata(struct udev *udev);
void udev_set_userdata(struct udev *udev, void *userdata);
+const char *udev_get_test_prefix(struct udev *udev);
/*
* udev_list
diff --git a/src/test/test-udev.c b/src/test/test-udev.c
index 414eabc..46e8ce6 100644
--- a/src/test/test-udev.c
+++ b/src/test/test-udev.c
@@ -68,7 +68,7 @@ int main(int argc, char *argv[])
rules = udev_rules_new(udev, 1);
- util_strscpyl(syspath, sizeof(syspath), TEST_PREFIX "/sys", devpath, NULL);
+ util_strscpyl(syspath, sizeof(syspath), udev_get_test_prefix(udev) , "/sys", devpath, NULL);
dev = udev_device_new_from_syspath(udev, syspath);
if (dev == NULL) {
log_debug("unknown device '%s'\n", devpath);
diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index e6f405b..228b16c 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -112,6 +112,8 @@ size_t udev_event_apply_format(struct udev_event *event, const char *src, char *
const char *from;
char *s;
size_t l;
+ const char *prefix = udev_get_test_prefix(udev_device_get_udev(dev));
+ const int devpath_len = strlen(prefix) + strlen("/dev/");
from = src;
s = dest;
@@ -319,7 +321,7 @@ subst:
break;
devnode = udev_device_get_devnode(dev_parent);
if (devnode != NULL)
- l = util_strpcpy(&s, l, devnode + strlen(TEST_PREFIX "/dev/"));
+ l = util_strpcpy(&s, l, devnode + devpath_len);
break;
}
case SUBST_DEVNODE:
@@ -330,7 +332,7 @@ subst:
if (event->name != NULL)
l = util_strpcpy(&s, l, event->name);
else if (udev_device_get_devnode(dev) != NULL)
- l = util_strpcpy(&s, l, udev_device_get_devnode(dev) + strlen(TEST_PREFIX "/dev/"));
+ l = util_strpcpy(&s, l, udev_device_get_devnode(dev) + devpath_len);
else
l = util_strpcpy(&s, l, udev_device_get_sysname(dev));
break;
@@ -340,16 +342,16 @@ subst:
list_entry = udev_device_get_devlinks_list_entry(dev);
if (list_entry == NULL)
break;
- l = util_strpcpy(&s, l, udev_list_entry_get_name(list_entry) + strlen(TEST_PREFIX "/dev/"));
+ l = util_strpcpy(&s, l, udev_list_entry_get_name(list_entry) + devpath_len);
udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry))
- l = util_strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry) + strlen(TEST_PREFIX "/dev/"), NULL);
+ l = util_strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry) + devpath_len, NULL);
break;
}
case SUBST_ROOT:
- l = util_strpcpy(&s, l, TEST_PREFIX "/dev");
+ l = util_strpcpyl(&s, l, prefix, "/dev", NULL);
break;
case SUBST_SYS:
- l = util_strpcpy(&s, l, TEST_PREFIX "/sys");
+ l = util_strpcpyl(&s, l, prefix, "/sys", NULL);
break;
case SUBST_ENV:
if (attr == NULL) {
diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
index 1bef521..18a75c3 100644
--- a/src/udev/udev-node.c
+++ b/src/udev/udev-node.c
@@ -202,9 +202,10 @@ static void link_update(struct udev_device *dev, const char *slink, bool add)
char dirname[UTIL_PATH_SIZE];
const char *target;
char buf[UTIL_PATH_SIZE];
+ const char *prefix = udev_get_test_prefix(udev_device_get_udev(dev));
- util_path_encode(slink + strlen(TEST_PREFIX "/dev"), name_enc, sizeof(name_enc));
- util_strscpyl(dirname, sizeof(dirname), TEST_PREFIX "/run/udev/links/", name_enc, NULL);
+ util_path_encode(slink + strlen(prefix) + strlen("/dev"), name_enc, sizeof(name_enc));
+ util_strscpyl(dirname, sizeof(dirname), prefix, "/run/udev/links/", name_enc, NULL);
util_strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL);
if (!add && unlink(filename) == 0)
@@ -328,7 +329,8 @@ void udev_node_add(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid)
return;
/* always add /dev/{block,char}/$major:$minor */
- snprintf(filename, sizeof(filename), TEST_PREFIX "/dev/%s/%u:%u",
+ snprintf(filename, sizeof(filename), "%s/dev/%s/%u:%u",
+ udev_get_test_prefix(udev),
strcmp(udev_device_get_subsystem(dev), "block") == 0 ? "block" : "char",
major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)));
node_symlink(udev, udev_device_get_devnode(dev), filename);
@@ -353,7 +355,8 @@ void udev_node_remove(struct udev_device *dev)
link_update(dev, udev_list_entry_get_name(list_entry), 0);
/* remove /dev/{block,char}/$major:$minor */
- snprintf(filename, sizeof(filename), TEST_PREFIX "/dev/%s/%u:%u",
+ snprintf(filename, sizeof(filename), "%s/dev/%s/%u:%u",
+ udev_get_test_prefix(udev_device_get_udev(dev)),
strcmp(udev_device_get_subsystem(dev), "block") == 0 ? "block" : "char",
major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)));
unlink(filename);
diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c
index b5b54dd..d098ced 100644
--- a/src/udev/udev-rules.c
+++ b/src/udev/udev-rules.c
@@ -1718,6 +1718,9 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names)
struct token end_token;
char **files, **f;
int r;
+ char sysconf_path[UTIL_PATH_SIZE];
+ char run_path[UTIL_PATH_SIZE];
+ char libexec_path[UTIL_PATH_SIZE];
rules = calloc(1, sizeof(struct udev_rules));
if (rules == NULL)
@@ -1757,10 +1760,14 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names)
memset(rules->trie_nodes, 0x00, sizeof(struct trie_node));
rules->trie_nodes_cur = 1;
- rules->dirs = strv_new(TEST_PREFIX SYSCONFDIR "/udev/rules.d",
- TEST_PREFIX "/run/udev/rules.d",
- TEST_PREFIX UDEVLIBEXECDIR "/rules.d",
- NULL);
+ util_strscpyl(sysconf_path, sizeof(sysconf_path), udev_get_test_prefix(udev),
+ SYSCONFDIR "/udev/rules.d", NULL);
+ util_strscpyl(run_path, sizeof(run_path), udev_get_test_prefix(udev),
+ "/run/udev/rules.d", NULL);
+ util_strscpyl(libexec_path, sizeof(libexec_path), udev_get_test_prefix(udev),
+ UDEVLIBEXECDIR "/rules.d", NULL);
+ rules->dirs = strv_new(sysconf_path, run_path, libexec_path, NULL);
+
if (!rules->dirs) {
log_error("failed to build config directory array");
return NULL;
@@ -2018,6 +2025,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
struct token *rule;
enum escape_type esc = ESCAPE_UNSET;
bool can_set_name;
+ const char *prefix = udev_get_test_prefix (event->udev);
if (rules->tokens == NULL)
return -1;
@@ -2059,7 +2067,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(event->dev)) {
const char *devlink;
- devlink = udev_list_entry_get_name(list_entry) + strlen(TEST_PREFIX "/dev/");
+ devlink = udev_list_entry_get_name(list_entry) + strlen(prefix) + strlen("/dev/");
if (match_key(rules, cur, devlink) == 0) {
match = true;
break;
@@ -2533,7 +2541,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
log_debug("%i character(s) replaced\n", count);
}
if (major(udev_device_get_devnum(event->dev)) &&
- (!streq(name_str, udev_device_get_devnode(event->dev) + strlen(TEST_PREFIX "/dev/")))) {
+ (!streq(name_str, udev_device_get_devnode(event->dev) + strlen(prefix) + strlen("/dev/")))) {
log_error("NAME=\"%s\" ignored, kernel device nodes "
"can not be renamed; please fix it in %s:%u\n", name,
&rules->buf[rule->rule.filename_off], rule->rule.filename_line);
@@ -2578,7 +2586,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
next[0] = '\0';
log_debug("LINK '%s' %s:%u\n", pos,
&rules->buf[rule->rule.filename_off], rule->rule.filename_line);
- util_strscpyl(filename, sizeof(filename), TEST_PREFIX "/dev/", pos, NULL);
+ util_strscpyl(filename, sizeof(filename), prefix, "/dev/", pos, NULL);
udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique);
while (isspace(next[1]))
next++;
@@ -2588,7 +2596,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
if (pos[0] != '\0') {
log_debug("LINK '%s' %s:%u\n", pos,
&rules->buf[rule->rule.filename_off], rule->rule.filename_line);
- util_strscpyl(filename, sizeof(filename), TEST_PREFIX "/dev/", pos, NULL);
+ util_strscpyl(filename, sizeof(filename), prefix, "/dev/", pos, NULL);
udev_device_add_devlink(event->dev, filename, cur->key.devlink_unique);
}
break;
@@ -2698,7 +2706,7 @@ void udev_rules_apply_static_dev_perms(struct udev_rules *rules)
/* we assure, that the permissions tokens are sorted before the static token */
if (mode == 0 && uid == 0 && gid == 0)
goto next;
- util_strscpyl(filename, sizeof(filename), TEST_PREFIX "/dev/",
+ util_strscpyl(filename, sizeof(filename), udev_get_test_prefix(rules->udev), "/dev/",
&rules->buf[cur->key.value_off], NULL);
if (stat(filename, &stats) != 0)
goto next;
diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c
index 907e961..81a9ce7 100644
--- a/src/udev/udevadm-info.c
+++ b/src/udev/udevadm-info.c
@@ -267,13 +267,22 @@ static void cleanup_db(struct udev *udev)
static struct udev_device *find_device(struct udev *udev, const char *id, const char *prefix)
{
char name[UTIL_PATH_SIZE];
+ char fullprefix[UTIL_PATH_SIZE];
+ char devdir[UTIL_PATH_SIZE];
+ char sysdir[UTIL_PATH_SIZE];
- if (prefix && !startswith(id, prefix)) {
- util_strscpyl(name, sizeof(name), prefix, id, NULL);
+ if (prefix)
+ util_strscpyl(fullprefix, sizeof(fullprefix), udev_get_test_prefix(udev), prefix, NULL);
+
+ if (prefix && !startswith(id, fullprefix)) {
+ util_strscpyl(name, sizeof(name), fullprefix, id, NULL);
id = name;
}
- if (startswith(id, "/dev/")) {
+ util_strscpyl(devdir, sizeof(devdir), udev_get_test_prefix(udev), "/dev/", NULL);
+ util_strscpyl(sysdir, sizeof(sysdir), udev_get_test_prefix(udev), "/sys/", NULL);
+
+ if (startswith(id, devdir)) {
struct stat statbuf;
char type;
@@ -288,7 +297,7 @@ static struct udev_device *find_device(struct udev *udev, const char *id, const
return NULL;
return udev_device_new_from_devnum(udev, type, statbuf.st_rdev);
- } else if (startswith(id, "/sys/"))
+ } else if (startswith(id, sysdir))
return udev_device_new_from_syspath(udev, id);
else
return NULL;
--
1.7.10.4
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply related
* Re: [PATCH] udev: persistent FireWire device names
From: Clemens Ladisch @ 2012-07-04 19:48 UTC (permalink / raw)
To: linux-hotplug
In-Reply-To: <4FF47870.3020509@ladisch.de>
Kay Sievers wrote:
> On Wed, Jul 4, 2012 at 7:08 PM, Clemens Ladisch <clemens@ladisch.de> wrote:
>> Makefile.am | 1 +
>> rules/60-persistent-firewire.rules | 26 ++++++++++++++++++++++++++
>> 2 files changed, 27 insertions(+), 0 deletions(-)
>
> What is this useful for? Firewire is by definiton a hotplug bus, and
> application dealing with raw firewire nodes, need to manage that
> anyway.
Most normal applications indeed do this.
> what kind of problem would that solve, that isn't better solved at
> the application level?
There are several testing and debugging tools where devices are
selected not by their capabilities but by their identity.
It would certainly be possible to add code to lookup devices by
their GUID or name, but having udev create a bunch of symlinks
is much simpler.
Regards,
Clemens
^ permalink raw reply
* Re: [PATCH] udev: persistent FireWire device names
From: Kay Sievers @ 2012-07-04 17:22 UTC (permalink / raw)
To: linux-hotplug
In-Reply-To: <4FF47870.3020509@ladisch.de>
On Wed, Jul 4, 2012 at 7:08 PM, Clemens Ladisch <clemens@ladisch.de> wrote:
> ---
> Makefile.am | 1 +
> rules/60-persistent-firewire.rules | 26 ++++++++++++++++++++++++++
> 2 files changed, 27 insertions(+), 0 deletions(-)
What is this useful for? Firewire is by definiton a hotplug bus, and
application dealing with raw firewire nodes, need to manage that
anyway. It's very much like USB, we don't do anything like that for
USB either, because we require the apps to cope with the nature of the
bus, and don't want them to expect predictable device nodes for the
raw access.
There is no legacy to support for firewire, like we need to do for
most other of the persistent stuff. So, what kind of problem would
that solve, that isn't better solved at the application level?
Kay
^ permalink raw reply
* [PATCH] udev: persistent FireWire device names
From: Clemens Ladisch @ 2012-07-04 17:08 UTC (permalink / raw)
To: linux-hotplug
---
Makefile.am | 1 +
rules/60-persistent-firewire.rules | 26 ++++++++++++++++++++++++++
2 files changed, 27 insertions(+), 0 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index 3b7ec0b..415fae6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1510,6 +1510,7 @@ dist_udevrules_DATA += \
rules/60-persistent-input.rules \
rules/60-persistent-alsa.rules \
rules/60-persistent-storage.rules \
+ rules/60-persistent-firewire.rules \
rules/75-net-description.rules \
rules/75-tty-description.rules \
rules/78-sound-card.rules \
diff --git a/rules/60-persistent-firewire.rules b/rules/60-persistent-firewire.rules
new file mode 100644
index 0000000..2eb8cfe
--- /dev/null
+++ b/rules/60-persistent-firewire.rules
@@ -0,0 +1,26 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION="remove", GOTO="persistent_firewire_end"
+SUBSYSTEM!="firewire", GOTO="persistent_firewire_end"
+
+ATTR{is_local}="1", OPTIONS="link_priority=1"
+
+TEST="guid", SYMLINK+="firewire/by-id/guid-$attr{guid}"
+
+# Most devices have the model name in the root directory.
+TEST="vendor_name", TEST="model_name", \
+ OPTIONS="string_escape=replace", \
+ SYMLINK+="firewire/by-name/$attr{vendor_name} $attr{model_name}"
+# Some devices have the model name only in the first unit directory, so we have
+# to get the name from the corresponding subdevice. (The detour through cat is
+# necessary because $attr does not allow substitutions in its file name.)
+TEST="vendor_name", TEST!="model_name", TEST="$kernel.0/model_name", \
+ PROGRAM="/bin/cat $sys$devpath/$kernel.0/model_name", \
+ OPTIONS="string_escape=replace", \
+ SYMLINK+="firewire/by-name/$attr{vendor_name} $result"
+# To make the preceding rule work when the subdevice is added after the parent
+# device, force a 'change' event to rerun that rule in this case.
+ACTION="add", DEVPATH="*/fw[0-9]*/fw[0-9]*.0", TEST="model_name", TEST!="../model_name", \
+ ATTR{../uevent}="change"
+
+LABEL="persistent_firewire_end"
^ permalink raw reply related
* Re: UDEV identical devices. How to?
From: Kay Sievers @ 2012-07-03 9:36 UTC (permalink / raw)
To: linux-hotplug
In-Reply-To: <4FF2B967.9060607@gmail.com>
On Tue, Jul 3, 2012 at 11:20 AM, Budnev Vladimir
<vladimir.budnev@gmail.com> wrote:
> We have Debian 2.6.32 and DVB cards with two/four tunners on the each card.
> We want to assign specific number in /dev/dvb/ tree for each tuner.
>
> The problems is that from udev point of view those devices(tuners) are
> absolutely equal(udefinfo gives absolutely identical output), because tuners
> are placed in one dvb card.
>
> We'v googled and found that theoretically we can assign numbers in specific
> order with using ENV options in udev rules.
>
> Here is a working example which creates symlinks:
> # Create a symlinks for both tuners of Kworld device
> SUBSYSTEM="dvb", ATTRS{idVendor}="1b80", ATTRS{idProduct}="e399",
> ENV{kworld}!="two", ENV{kworld}="two", PROGRAM="/bin/sh -c 'K=%k;
> K=$${K#dvb}; printf dvb/adapter_kw1/%%s $${K#*.}'", SYMLINK+="%c"
> SUBSYSTEM="dvb", ATTRS{idVendor}="1b80", ATTRS{idProduct}="e399",
> ENV{kworld}="two", ENV{kworld}="one", PROGRAM="/bin/sh -c 'K=%k;
> K=$${K#dvb}; printf dvb/adapter_kw2/%%s $${K#*.}'", SYMLINK+="%c"
>
> But we do want devices instead of symlinks, like e.g. /dev/dvb/adapterX
>
> We'v tried such rules, but has no luck:
>
> SUBSYSTEM="dvb", KERNELS="0000:04:00.0", ENV{kworld}!="two",
> ENV{kworld}="one", PROGRAM="/bin/sh -c 'K=%k; K=$${K#dvb}; printf
> dvb/adapter1/%%s $${K#*.}'", NAME="%c", GROUP="video"
> SUBSYSTEM="dvb", KERNELS="0000:04:00.0", ENV{kworld}="two",
> ENV{kworld}="one", PROGRAM="/bin/sh -c 'K=%k; K=$${K#dvb}; printf
> dvb/adapter2/%%s $${K#*.}'", NAME="%c", GROUP="video"
>
> Can someone give an advice how to properly construct rules to assign
> devices. It woul be great with an example string
> Mb we missing some global udev option to enable such constructions?
You can't. You have to use symlinks.
http://thread.gmane.org/gmane.comp.sysutils.systemd.devel/5503/focusU04
Kay
^ permalink raw reply
* UDEV identical devices. How to?
From: Budnev Vladimir @ 2012-07-03 9:20 UTC (permalink / raw)
To: linux-hotplug
Good day to all
I am not sure which list this quistions should be send to so some
overkill maybe.
We have Debian 2.6.32 and DVB cards with two/four tunners on the each
card. We want to assign specific number in /dev/dvb/ tree for each tuner.
The problems is that from udev point of view those devices(tuners) are
absolutely equal(udefinfo gives absolutely identical output), because
tuners are placed in one dvb card.
We'v googled and found that theoretically we can assign numbers in
specific order with using ENV options in udev rules.
Here is a working example which creates symlinks:
# Create a symlinks for both tuners of Kworld device
SUBSYSTEM="dvb", ATTRS{idVendor}="1b80", ATTRS{idProduct}="e399",
ENV{kworld}!="two", ENV{kworld}="two", PROGRAM="/bin/sh -c 'K=%k;
K=$${K#dvb}; printf dvb/adapter_kw1/%%s $${K#*.}'", SYMLINK+="%c"
SUBSYSTEM="dvb", ATTRS{idVendor}="1b80", ATTRS{idProduct}="e399",
ENV{kworld}="two", ENV{kworld}="one", PROGRAM="/bin/sh -c 'K=%k;
K=$${K#dvb}; printf dvb/adapter_kw2/%%s $${K#*.}'", SYMLINK+="%c"
But we do want devices instead of symlinks, like e.g. /dev/dvb/adapterX
We'v tried such rules, but has no luck:
SUBSYSTEM="dvb", KERNELS="0000:04:00.0", ENV{kworld}!="two",
ENV{kworld}="one", PROGRAM="/bin/sh -c 'K=%k; K=$${K#dvb}; printf
dvb/adapter1/%%s $${K#*.}'", NAME="%c", GROUP="video"
SUBSYSTEM="dvb", KERNELS="0000:04:00.0", ENV{kworld}="two",
ENV{kworld}="one", PROGRAM="/bin/sh -c 'K=%k; K=$${K#dvb}; printf
dvb/adapter2/%%s $${K#*.}'", NAME="%c", GROUP="video"
Can someone give an advice how to properly construct rules to assign
devices. It woul be great with an example string
Mb we missing some global udev option to enable such constructions?
Tnx in advance.
^ permalink raw reply
* udev (udevadm trigger) causes "Text File Busy"
From: Ulf Samuelsson @ 2012-07-02 18:10 UTC (permalink / raw)
To: linux-hotplug
I am building a rootfs using OpenEmbedded Classic 2011.03
(maintenance release). It is using an older version of udev (165).
Have been mounting the rootfs on a AT91SAM9N12EK development board
using NFS without problems. The kernel is 2.6.39 with Atmel patches.
When I tried to program the real SAM9N12 based target from a JFFS2 file
system in NAND, the initial boot fails in the "/etc/rcS.d/S03udev"
script generated by OE. After that has been run, I cannot execute any
applications since I get the error message "Text file busy" when I try-
I.E:
# cat AAAA
/bin/sh: cat: Text file busy
# ls
/bin/sh: ls: Text file busy
The S03udev is the early part of the init, and only
mount -t proc proc /proc
mount sysfs /sys -t sysfs
has been executed before S03udev.
The problem seems to occur in the statment:
/sbin/udevadm trigger
Have added "init=/bin/sh" to the bootargs, and run the boot manually.
"/sbin/udevadm --debug trigger --verbose"
to get more info, but this is not helpful at all.
I get some occasional JFFS2 notices
# JFFS2 notice: (39) check_node_data: wrong data CRC in data node at
0x007f2e5c: read 0x5a469344, calculated 0x8f5964d0.
This seems to be close to the end or after the fs.
Any clues?
BR
Ulf Samuelsson
=====================
S03udev
#!/bin/sh -e
### BEGIN INIT INFO
# Provides: udev
# Required-Start: mountvirtfs
# Required-Stop:
# Default-Start: S
# Default-Stop:
# Short-Description: Start udevd, populate /dev and load drivers.
### END INIT INFO
export TZ=/etc/localtime
[ -d /sys/class ] || exit 1
[ -r /proc/mounts ] || exit 1
[ -x /sbin/udevd ] || exit 1
[ -f /etc/default/udev ] && . /etc/default/udev
[ -f /etc/udev/udev.conf ] && . /etc/udev/udev.conf
kill_udevd() {
if [ -x /sbin/pidof ]; then
pid=`/sbin/pidof -x udevd`
[ -n "$pid" ] && kill $pid
fi
}
export ACTIONd
# propagate /dev from /sys
echo "Starting udev"
# mount the tmpfs on /dev, if not already done
LANG=C awk "\$2 = \"/dev\" && \$3 = \"tmpfs\" { exit 1 }" /proc/mounts
&& {
mount -n -o mode\a55 -t tmpfs none "/dev"
mkdir -m 0755 /dev/pts
mkdir -m 1777 /dev/shm
}
if [ "$DEVCACHE" != "" ]; then
# Invalidate udev cache if the kernel or its bootargs/cmdline have changed
[ -x /bin/uname ] && /bin/uname -mrspv > /tmp/uname || touch /tmp/uname
[ -r /proc/cmdline ] && cat /proc/cmdline > /tmp/cmdline || touch
/tmp/cmdline
[ -r /proc/devices ] && cat /proc/devices > /tmp/devices || touch
/tmp/devices
[ -r /proc/atags ] && cat /proc/atags > /tmp/atags || touch /tmp/atags
if [ -e $DEVCACHE ] && \
cmp -s /tmp/uname /etc/udev/saved.uname && \
cmp -s /tmp/cmdline /etc/udev/saved.cmdline && \
cmp -s /tmp/devices /etc/udev/saved.devices && \
cmp -s /tmp/atags /etc/udev/saved.atags; then
(cd /; tar xf $DEVCACHE > /dev/null 2>&1)
not_first_boot=1
fi
fi
if [ ! -e "/lib/modules/$(uname -r)"/modules.dep ] ; then
mkdir -p /lib/modules/$(uname -r)
depmod -ae
fi
# make_extra_nodes
kill_udevd > "/dev/null" 2>&1
# trigger the sorted events
echo -e '\000\000\000\000' > /proc/sys/kernel/hotplug
/sbin/udevd -d
/sbin/udevadm control --env STARTUP=1
if [ "$not_first_boot" != "" ];then
/sbin/udevadm trigger --subsystem-nomatch=tty --subsystem-nomatch=mem
--subsystem-nomatch=vc --subsystem-nomatch=vtconsole
--subsystem-nomatch=misc --subsystem-nomatchÜon
--subsystem-nomatch=pci_bus --subsystem-nomatch=graphics
--subsystem-nomatchºcklight --subsystem-nomatch=video4linux
--subsystem-nomatch=platform
(/sbin/udevadm settle --timeout=8; /sbin/udevadm control --env STARTUP=)&
else
/sbin/udevadm trigger
/sbin/udevadm settle
fi
exit 0
======================BOOTLOG
Uncompressing Linux... done, booting the kernel.
Initializing cgroup subsys cpuset
Linux version 2.6.39 (ulf@grond) (gcc version 4.5.3 20110311
(prerelease) (GCC) ) #1 Mon Jul 2 15:24:45 CEST 2012
CPU: ARM926EJ-S [41069265] revision 5 (ARMv5TEJ), cr\0053177
CPU: VIVT data cache, VIVT instruction cache
Machine: SAM9N12EK2
Memory policy: ECC disabled, Data cache writeback
Clocks: CPU 399 MHz, master 133 MHz, main 16.000 MHz
Built 1 zonelists in Zone order, mobility grouping on. Total pages: 16256
Kernel command line: memdM console=ttyS0,115200 root=/dev/mtdblock7
mtdparts=atmel_nand:128k(bootstrap)ro,256k(uboot)ro,128k(env1)ro,128k(env2)ro,1408k(dummy),2M(linux),28M(root),32M(root2),-(spare)
rw rootfstype=jffs2 init=/binh
PID hash table entries: 256 (order: -2, 1024 bytes)
Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
allocated 262144 bytes of page_cgroup
please try 'cgroup_disable=memory' option if you don't want memory cgroups
Memory: 64MB = 64MB total
Memory: 60564k/60564k available, 4972k reserved, 0K highmem
Virtual kernel memory layout:
vector : 0xffff0000 - 0xffff1000 ( 4 kB)
fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB)
DMA : 0xff000000 - 0xffe00000 ( 14 MB)
vmalloc : 0xc4800000 - 0xfee00000 ( 934 MB)
lowmem : 0xc0000000 - 0xc4000000 ( 64 MB)
modules : 0xbf000000 - 0xc0000000 ( 16 MB)
.init : 0xc0008000 - 0xc0029000 ( 132 kB)
.text : 0xc0029000 - 0xc03b5000 (3632 kB)
.data : 0xc03b6000 - 0xc03daa80 ( 147 kB)
NR_IRQS:192
AT91: 128 gpio irqs in 4 banks
Console: colour dummy device 80x30
console [ttyS0] enabled
Calibrating delay loop... 199.06 BogoMIPS (lpj™5328)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 512
Initializing cgroup subsys debug
Initializing cgroup subsys ns
ns_cgroup deprecated: consider using the 'clone_children' flag without
the ns_cgroup.
Initializing cgroup subsys cpuacct
Initializing cgroup subsys memory
Initializing cgroup subsys devices
Initializing cgroup subsys freezer
Initializing cgroup subsys blkio
CPU: Testing write buffer coherency: ok
devtmpfs: initialized
NET: Registered protocol family 16
AT91: Power Management (with slow clock mode)
AT91: Starting after user reset
bio: create slab <bio-0> at 0
SCSI subsystem initialized
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
at_hdmac at_hdmac.0: Atmel AHB DMA Controller ( cpy slave ), 8 channels
Bluetooth: Core ver 2.16
NET: Registered protocol family 31
Bluetooth: HCI device and connection manager initialized
Bluetooth: HCI socket layer initialized
Bluetooth: L2CAP socket layer initialized
Bluetooth: SCO socket layer initialized
Switching to clocksource tcb_clksrc
NET: Registered protocol family 2
IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
TCP established hash table entries: 2048 (order: 2, 16384 bytes)
TCP bind hash table entries: 2048 (order: 1, 8192 bytes)
TCP: Hash tables configured (established 2048 bind 2048)
TCP reno registered
UDP hash table entries: 256 (order: 0, 4096 bytes)
UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
NET: Registered protocol family 1
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
RPC: Registered tcp NFSv4.1 backchannel transport module.
JFFS2 version 2.2. (NAND) (SUMMARY) �© 2001-2006 Red Hat, Inc.
fuse init (API version 7.16)
msgmni has been set to 118
Block layer SCSI generic (bsg) driver version 0.4 loaded (major 253)
io scheduler noop registered (default)
atmel_usart.0: ttyS0 at MMIO 0xfefff200 (irq = 1) is a ATMEL_SERIAL
brd: module loaded
loop: module loaded
ssc ssc.0: Atmel SSC device at 0xc4870000 (irq 28)
atmel_nand atmel_nand: Using dma0chan0 for DMA transfers.
ONFI flash detected
ONFI param page 0 valid
NAND device: Manufacturer ID: 0xad, Chip ID: 0xa1 (Hynix H27S1G8F2BFR-BC)
Scanning device for bad blocks
Bad eraseblock 82 at 0x000000a40000
9 cmdlinepart partitions found on MTD device atmel_nand
Creating 9 MTD partitions on "atmel_nand":
0x000000000000-0x000000020000 : "bootstrap"
0x000000020000-0x000000060000 : "uboot"
0x000000060000-0x000000080000 : "env1"
0x000000080000-0x0000000a0000 : "env2"
0x0000000a0000-0x000000200000 : "dummy"
0x000000200000-0x000000400000 : "linux"
0x000000400000-0x000002000000 : "root"
0x000002000000-0x000004000000 : "root2"
0x000004000000-0x000008000000 : "spare"
atmel_spi atmel_spi.0: Using dma0chan1 (tx) and dma0chan2 (rx) for DMA
transfers
atmel_spi atmel_spi.0: Atmel SPI Controller at 0xf0000000 (irq 13)
ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
at91_ohci at91_ohci: AT91 OHCI
at91_ohci at91_ohci: new USB bus registered, assigned bus number 1
at91_ohci at91_ohci: irq 22, io mem 0x00500000
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 1 port detected
Initializing USB Mass Storage driver...
usbcore: registered new interface driver usb-storage
USB Mass Storage support registered.
usbcore: registered new interface driver libusual
mousedev: PS/2 mouse device common for all mice
input: gpio-keys as /devices/platform/gpio-keys/input/input0
at91_rtc at91_rtc: rtc core: registered at91_rtc as rtc0
AT91 Real Time Clock driver.
Bluetooth: Generic Bluetooth SDIO driver ver 0.1
usbcore: registered new interface driver usbhid
usbhid: USB HID core driver
TCP cubic registered
NET: Registered protocol family 17
lib80211: common routines for IEEE802.11 drivers
Registering the dns_resolver key type
registered taskstats version 1
at91_rtc at91_rtc: setting system clock to 2012-07-02 18:11:26 UTC
(1341252686)
atmel_mci atmel_mci.0: Using dma0chan3 for DMA transfers
atmel_mci atmel_mci.0: Atmel MCI controller at 0xf0008000 irq 12, 1 slots
Empty flash at 0x007e3820 ends at 0x007e4000
Empty flash at 0x007e7818 ends at 0x007e8000
VFS: Mounted root (jffs2 filesystem) on device 31:7.
devtmpfs: mounted
Freeing init memory: 132K
/bin/sh: can't access tty; job control turned off
======================OUTPUT from /sbin/udevadm --debug trigger --verbose
run_command: calling: trigger
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/atmel_usart.0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/at91_ohci'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/atmel_spi.0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/atmel_mci.0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/atmel_nand'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/gpio-keys'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/leds-gpio'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/ssc.0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/at_hdmac.0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/at91_rtc'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/atmel_tcb.0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/atmel_tcb.1'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/atmel_spi.0/spi0.0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/at91_ohci/usb1'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/at91_ohci/usb1/1-0:1.0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/default'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/1:0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/1:1'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/7:0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/7:1'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/7:2'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/7:3'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/7:4'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/7:5'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/7:6'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/7:7'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/mtd-unmap'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/mtd-romap'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/mtd-rwmap'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/31:0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/31:1'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/31:2'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/31:3'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/31:4'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/31:5'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/31:6'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/31:7'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/bdi/31:8'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/gpio/gpiochip32'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/gpio/gpiochip64'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/gpio/gpiochip96'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/gpio/gpiochip128'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/console'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty1'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty2'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty3'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty4'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty5'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty6'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty7'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty8'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty9'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty10'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty11'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty12'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty13'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty14'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty15'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty16'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty17'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty18'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty19'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty20'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty21'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty22'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty23'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty24'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty25'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty26'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty27'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty28'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty29'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty30'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty31'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty32'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty33'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty34'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty35'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty36'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty37'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty38'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty39'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty40'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty41'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty42'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty43'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty44'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty45'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty46'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty47'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty48'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty49'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty50'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty51'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty52'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty53'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty54'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty55'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty56'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty57'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty58'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty59'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty60'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty61'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty62'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/tty63'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/ptyp0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/ptyp1'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/ptyp2'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/ptyp3'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/ptyp4'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/ptyp5'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/ptyp6'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/ptyp7'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/ttyp0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/ttyp1'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/ttyp2'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/ttyp3'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/ttyp4'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/ttyp5'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/ttyp6'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/ttyp7'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/tty/ptmx'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/atmel_usart.0/tty/ttyS0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/vtconsole/vtcon0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/atmel_spi.0/spi_master/spi0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/at_hdmac.0/dma/dma0chan0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/at_hdmac.0/dma/dma0chan1'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/at_hdmac.0/dma/dma0chan2'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/at_hdmac.0/dma/dma0chan3'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/at_hdmac.0/dma/dma0chan4'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/at_hdmac.0/dma/dma0chan5'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/at_hdmac.0/dma/dma0chan6'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/at_hdmac.0/dma/dma0chan7'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/block/ram0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/block/ram1'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/block/loop0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/block/loop1'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/block/loop2'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/block/loop3'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/block/loop4'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/block/loop5'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/block/loop6'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/block/loop7'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd0/mtdblock0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd1/mtdblock1'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd2/mtdblock2'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd3/mtdblock3'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd4/mtdblock4'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd5/mtdblock5'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd6/mtdblock6'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd7/mtdblock7'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd8/mtdblock8'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/misc/autofs'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/misc/fuse'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/misc/cuse'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/misc/ubi_ctrl'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/misc/psaux'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/misc/cpu_dma_latency'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/misc/network_latency'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/misc/network_throughput'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/input/mice'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/gpio-keys/input/input0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/gpio-keys/input/input0/event0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/at91_rtc/rtc/rtc0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/atmel_mci.0/mmc_host/mmc0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/platform/leds-gpio/leds/d8'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/net/lo'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mem/mem'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mem/kmem'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mem/null'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mem/zero'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mem/full'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mem/random'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mem/urandom'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mem/kmsg'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/vc/vcs'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/vc/vcsa'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/vc/vcs1'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/vc/vcsa1'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd0'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd0ro'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd1'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd1ro'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd2'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd2ro'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd3'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd3ro'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd4'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd4ro'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd5'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd5ro'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd6'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd6ro'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd7'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd7ro'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd8'
udev_device_new_from_syspath: device 0x2b0c8 has devpath
'/devices/virtual/mtd/mtd8ro'
/sys/devices/platform/at91_ohci
/sys/devices/platform/at91_ohci/usb1
/sys/devices/platform/at91_ohci/usb1/1-0:1.0
/sys/devices/platform/at91_rtc
/sys/devices/platform/at91_rtc/rtc/rtc0
/sys/devices/platform/at_hdmac.0
/sys/devices/platform/at_hdmac.0/dma/dma0chan0
/sys/devices/platform/at_hdmac.0/dma/dma0chan1
/sys/devices/platform/at_hdmac.0/dma/dma0chan2
/sys/devices/platform/at_hdmac.0/dma/dma0chan3
/sys/devices/platform/at_hdmac.0/dma/dma0chan4
/sys/devices/platform/at_hdmac.0/dma/dma0chan5
/sys/devices/platform/at_hdmac.0/dma/dma0chan6
/sys/devices/platform/at_hdmac.0/dma/dma0chan7
/sys/devices/platform/atmel_mci.0
/sys/devices/platform/atmel_mci.0/mmc_host/mmc0
/sys/devices/platform/atmel_nand
/sys/devices/platform/atmel_spi.0
/sys/devices/platform/atmel_spi.0/spi0.0
/sys/devices/platform/atmel_spi.0/spi_master/spi0
/sys/devices/platform/atmel_tcb.0
/sys/devices/platform/atmel_tcb.1
/sys/devices/platform/atmel_usart.0
/sys/devices/platform/atmel_usart.0/tty/ttyS0
/sys/devices/platform/gpio-keys
/sys/devices/platform/gpio-keys/input/input0
/sys/devices/platform/gpio-keys/input/input0/event0
/sys/devices/platform/leds-gpio
/sys/devices/platform/leds-gpio/leds/d8
/sys/devices/platform/ssc.0
/sys/devices/virtual/bdi/1:0
/sys/devices/virtual/bdi/1:1
/sys/devices/virtual/bdi/31:0
/sys/devices/virtual/bdi/31:1
/sys/devices/virtual/bdi/31:2
/sys/devices/virtual/bdi/31:3
/sys/devices/virtual/bdi/31:4
/sys/devices/virtual/bdi/31:5
/sys/devices/virtual/bdi/31:6
/sys/devices/virtual/bdi/31:7
/sys/devices/virtual/bdi/31:8
/sys/devices/virtual/bdi/7:0
/sys/devices/virtual/bdi/7:1
/sys/devices/virtual/bdi/7:2
/sys/devices/virtual/bdi/7:3
/sys/devices/virtual/bdi/7:4
/sys/devices/virtual/bdi/7:5
/sys/devices/virtual/bdi/7:6
/sys/devices/virtual/bdi/7:7
/sys/devices/virtual/bdi/default
/sys/devices/virtual/bdi/mtd-romap
/sys/devices/virtual/bdi/mtd-rwmap
/sys/devices/virtual/bdi/mtd-unmap
/sys/devices/virtual/block/loop0
/sys/devices/virtual/block/loop1
/sys/devices/virtual/block/loop2
/sys/devices/virtual/block/loop3
/sys/devices/virtual/block/loop4
/sys/devices/virtual/block/loop5
/sys/devices/virtual/block/loop6
/sys/devices/virtual/block/loop7
/sys/devices/virtual/block/ram0
/sys/devices/virtual/block/ram1
/sys/devices/virtual/gpio/gpiochip128
/sys/devices/virtual/gpio/gpiochip32
/sys/devices/virtual/gpio/gpiochip64
/sys/devices/virtual/gpio/gpiochip96
/sys/devices/virtual/input/mice
/sys/devices/virtual/mem/full
/sys/devices/virtual/mem/kmem
/sys/devices/virtual/mem/kmsg
/sys/devices/virtual/mem/mem
/sys/devices/virtual/mem/null
/sys/devices/virtual/mem/random
/sys/devices/virtual/mem/urandom
/sys/devices/virtual/mem/zero
/sys/devices/virtual/misc/autofs
/sys/devices/virtual/misc/cpu_dma_latency
/sys/devices/virtual/misc/cuse
/sys/devices/virtual/misc/fuse
/sys/devices/virtual/misc/network_latency
/sys/devices/virtual/misc/network_throughput
/sys/devices/virtual/misc/psaux
/sys/devices/virtual/misc/ubi_ctrl
/sys/devices/virtual/mtd/mtd0
/sys/devices/virtual/mtd/mtd0/mtdblock0
/sys/devices/virtual/mtd/mtd0ro
/sys/devices/virtual/mtd/mtd1
/sys/devices/virtual/mtd/mtd1/mtdblock1
/sys/devices/virtual/mtd/mtd1ro
/sys/devices/virtual/mtd/mtd2
/sys/devices/virtual/mtd/mtd2/mtdblock2
/sys/devices/virtual/mtd/mtd2ro
/sys/devices/virtual/mtd/mtd3
/sys/devices/virtual/mtd/mtd3/mtdblock3
/sys/devices/virtual/mtd/mtd3ro
/sys/devices/virtual/mtd/mtd4
/sys/devices/virtual/mtd/mtd4/mtdblock4
/sys/devices/virtual/mtd/mtd4ro
/sys/devices/virtual/mtd/mtd5
/sys/devices/virtual/mtd/mtd5/mtdblock5
/sys/devices/virtual/mtd/mtd5ro
/sys/devices/virtual/mtd/mtd6
/sys/devices/virtual/mtd/mtd6/mtdblock6
/sys/devices/virtual/mtd/mtd6ro
/sys/devices/virtual/mtd/mtd7
/sys/devices/virtual/mtd/mtd7/mtdblock7
/sys/devices/virtual/mtd/mtd7ro
/sys/devices/virtual/mtd/mtd8
/sys/devices/virtual/mtd/mtd8/mtdblock8
/sys/devices/virtual/mtd/mtd8ro
/sys/devices/virtual/net/lo
/sys/devices/virtual/tty/console
/sys/devices/virtual/tty/ptmx
/sys/devices/virtual/tty/ptyp0
/sys/devices/virtual/tty/ptyp1
/sys/devices/virtual/tty/ptyp2
/sys/devices/virtual/tty/ptyp3
/sys/devices/virtual/tty/ptyp4
/sys/devices/virtual/tty/ptyp5
/sys/devices/virtual/tty/ptyp6
/sys/devices/virtual/tty/ptyp7
/sys/devices/virtual/tty/tty
/sys/devices/virtual/tty/tty0
/sys/devices/virtual/tty/tty1
/sys/devices/virtual/tty/tty10
/sys/devices/virtual/tty/tty11
/sys/devices/virtual/tty/tty12
/sys/devices/virtual/tty/tty13
/sys/devices/virtual/tty/tty14
/sys/devices/virtual/tty/tty15
/sys/devices/virtual/tty/tty16
/sys/devices/virtual/tty/tty17
/sys/devices/virtual/tty/tty18
/sys/devices/virtual/tty/tty19
/sys/devices/virtual/tty/tty2
/sys/devices/virtual/tty/tty20
/sys/devices/virtual/tty/tty21
/sys/devices/virtual/tty/tty22
/sys/devices/virtual/tty/tty23
/sys/devices/virtual/tty/tty24
/sys/devices/virtual/tty/tty25
/sys/devices/virtual/tty/tty26
/sys/devices/virtual/tty/tty27
/sys/devices/virtual/tty/tty28
/sys/devices/virtual/tty/tty29
/sys/devices/virtual/tty/tty3
/sys/devices/virtual/tty/tty30
/sys/devices/virtual/tty/tty31
/sys/devices/virtual/tty/tty32
/sys/devices/virtual/tty/tty33
/sys/devices/virtual/tty/tty34
/sys/devices/virtual/tty/tty35
/sys/devices/virtual/tty/tty36
/sys/devices/virtual/tty/tty37
/sys/devices/virtual/tty/tty38
/sys/devices/virtual/tty/tty39
/sys/devices/virtual/tty/tty4
/sys/devices/virtual/tty/tty40
/sys/devices/virtual/tty/tty41
/sys/devices/virtual/tty/tty42
/sys/devices/virtual/tty/tty43
/sys/devices/virtual/tty/tty44
/sys/devices/virtual/tty/tty45
/sys/devices/virtual/tty/tty46
/sys/devices/virtual/tty/tty47
/sys/devices/virtual/tty/tty48
/sys/devices/virtual/tty/tty49
/sys/devices/virtual/tty/tty5
/sys/devices/virtual/tty/tty50
/sys/devices/virtual/tty/tty51
/sys/devices/virtual/tty/tty52
/sys/devices/virtual/tty/tty53
/sys/devices/virtual/tty/tty54
/sys/devices/virtual/tty/tty55
/sys/devices/virtual/tty/tty56
/sys/devices/virtual/tty/tty57
/sys/devices/virtual/tty/tty58
/sys/devices/virtual/tty/tty59
/sys/devices/virtual/tty/tty6
/sys/devices/virtual/tty/tty60
/sys/devices/virtual/tty/tty61
/sys/devices/virtual/tty/tty62
/sys/devices/virtual/tty/tty63
/sys/devices/virtual/tty/tty7
/sys/devices/virtual/tty/tty8
/sys/devices/virtual/tty/tty9
/sys/devices/virtual/tty/ttyp0
/sys/devices/virtual/tty/ttyp1
/sys/devices/virtual/tty/ttyp2
/sys/devices/virtual/tty/ttyp3
/sys/devices/virtual/tty/ttyp4
/sys/devices/virtual/tty/ttyp5
/sys/devices/virtual/tty/ttyp6
/sys/devices/virtual/tty/ttyp7
/sys/devices/virtual/vc/vcs
/sys/devices/virtual/vc/vcs1
/sys/devices/virtual/vc/vcsa
/sys/devices/virtual/vc/vcsa1
/sys/devices/virtual/vtconsole/vtcon0
/ #
/ #
/ #
/ # cat AAAA
/bin/sh: cat: Text file busy
^ permalink raw reply
* Re: udev: getting attributes from subdevice?
From: Kay Sievers @ 2012-07-02 17:03 UTC (permalink / raw)
To: linux-hotplug
On Sat, Jun 30, 2012 at 7:15 PM, Clemens Ladisch <clemens@ladisch.de> wrote:
> TEST="vendor_name", TEST!="model_name", WAIT_FOR="$kernel.0/model_name"
> TEST="vendor_name", TEST!="model_name", TEST="$kernel.0/model_name", \
> OPTIONS="string_escape=replace", \
> SYMLINK+="firewire/by-name/$attr{vendor_name} $attr{$kernel.0/model_name}"
>
> But this doesn't work, probably because $kernel is not substituted
> before $attr:
>
> $ ls -l /dev/firewire/by-name/
> lrwxrwxrwx 1 root root 9 2012-06-30 18:13 LaCie_ -> ../../fw2
>
> (It works if I use $attr{fw2.0/model_name}, but that isn't generic enough.)
>
> Do I have to write a separate tool for extracting the name?
There is no direct support for accessing child devices, because it is
too racy and the child devices usually do not even exist at the time
the event for the parent device is handled.
Special care in the kernel need to be taken to make all that possible,
like we do for example for disks and partitions.
Aside from that, why this usually does not work well, the format char
replacement is just one pass from the left to the right, so that order
would not work.
Kay
^ permalink raw reply
* udev: getting attributes from subdevice?
From: Clemens Ladisch @ 2012-06-30 17:15 UTC (permalink / raw)
To: linux-hotplug
Hi,
I thought it would be useful to have persistent names for FireWire
devices:
########################################################################
ACTION="remove", GOTO="persistent_firewire_end"
SUBSYSTEM!="firewire", GOTO="persistent_firewire_end"
TEST="guid", SYMLINK+="firewire/by-id/guid-$attr{guid}"
TEST="vendor_name", TEST="model_name", \
OPTIONS="string_escape=replace", \
SYMLINK+="firewire/by-name/$attr{vendor_name} $attr{model_name}"
LABEL="persistent_firewire_end"
########################################################################
And these rules appear to work:
$ cat /sys/bus/firewire/devices/fw0/{vendor_name,model_name}
Linux Firewire
Juju
$ ls -l /dev/firewire/by-name/
lrwxrwxrwx 1 root root 9 2012-06-30 18:13 Linux_Firewire_Juju -> ../../fw0
However, there are devices where, due to some strange rules in the
FireWire specifications, some attributes end up belonging to a subdevice:
$ cat /sys/bus/firewire/devices/fw2/{vendor_name,model_name}
LaCie
cat: /sys/bus/firewire/devices/fw2/model_name: No such file or directory
$ cat /sys/bus/firewire/devices/fw2/fw2.0/model_name
Hard Drive Quadra
Okay, let's add some rules to handle this:
TEST="vendor_name", TEST!="model_name", WAIT_FOR="$kernel.0/model_name"
TEST="vendor_name", TEST!="model_name", TEST="$kernel.0/model_name", \
OPTIONS="string_escape=replace", \
SYMLINK+="firewire/by-name/$attr{vendor_name} $attr{$kernel.0/model_name}"
But this doesn't work, probably because $kernel is not substituted
before $attr:
$ ls -l /dev/firewire/by-name/
lrwxrwxrwx 1 root root 9 2012-06-30 18:13 LaCie_ -> ../../fw2
(It works if I use $attr{fw2.0/model_name}, but that isn't generic enough.)
Do I have to write a separate tool for extracting the name?
Regards,
Clemens
^ permalink raw reply
* Re: add loop device backing file in udev database
From: quidame @ 2012-06-28 14:38 UTC (permalink / raw)
To: linux-hotplug
In-Reply-To: <5deaf9514b7dafac35d4cdef91260ba1@poivron.org>
Hi,
Le 2012-06-28 13:11, Kay Sievers a écrit :
> On Thu, Jun 28, 2012 at 12:39 PM, <quidame@poivron.org> wrote:
>> Since kernel 2.6.37, some infos are available in /sys for associated
>> loop
>> devices, especially the backing file. Is it poosible to introduce
>> the
>> BACKING_FILE variable for associated loop devices ?
>
> Why would you *ever* want to mirror easily in /sys accessible kernel
> variables?
Because it reflects the status of a block device. If I run
# losetup /dev/loop0 /dev/sda1
# udevadm info --attribute-walk --name loop0
[...]
ATTR{removable}="0"
ATTR{ro}="0"
ATTR{size}="4096512"
[...]
Nothing about the backing file, except we know the loop device is
associated because its size is not "0". Its a poor and unusable
information. And yes, the backing file name is easily accessible in
/sys; but it is also the case for "removable", "ro" and "size". (OK,
this is not about "ENV{BACKING_FILE}", but "ATTR{backing_file}". The
first one was a suggestion, the second one is another.)
> That is usually the wrong thing to do, because it can get
> out-of-sync,
> and udev would pretend something that is not true anymore.
Not sure:
# losetup -d /dev/loop0
# losetup /dev/loop0 /dev/sdc1 # sdc is USB stick
# udevadm info --export --query property --name loop0
BACKING_FILE='/dev/sdc1'
DEVLINKS='/dev/disk/by-label/mini /dev/disk/by-uuid/7B55-DC7E'
DEVNAME='/dev/loop0'
DEVPATH='/devices/virtual/block/loop0'
DEVTYPE='disk'
ID_FS_LABEL='mini'
ID_FS_LABEL_ENC='mini'
ID_FS_TYPE='vfat'
ID_FS_USAGE='filesystem'
ID_FS_UUID='7B55-DC7E'
ID_FS_UUID_ENC='7B55-DC7E'
ID_FS_VERSION='FAT32'
MAJOR='7'
MINOR='0'
SUBSYSTEM='block'
UDEV_LOG='3'
UDISKS_PRESENTATION_NOPOLICY='1'
USEC_INITIALIZED='96227881'
I wildly remove the usb stick, and the same command leads to the same
results. This means BACKING_FILE, DEVLINKS and ID_FS_* variables are
'out-of-sync'.
But:
# udevadm trigger --sysname-match=loop0
# udevadm info --export --query=property --name=loop0
BACKING_FILE='/dev/sdc1 _deleted_'
DEVNAME='/dev/loop0'
DEVPATH='/devices/virtual/block/loop0'
DEVTYPE='disk'
MAJOR='7'
MINOR='0'
SUBSYSTEM='block'
UDEV_LOG='3'
UDISKS_PRESENTATION_NOPOLICY='1'
USEC_INITIALIZED='96227881'
And:
# losetup /dev/loop0
/dev/loop0: [0005]:39712 (/dev/sdc1)
Here you can see that the only one relevant information is
BACKING_FILE='/dev/sdc1 _deleted_'
But, really I don't care: if 'udevadm info --attribute-walk --name
loop0' can provide something as
[...]
ATTR{removable}="0"
ATTR{ro}="0"
ATTR{size}="7813120"
ATTR{backing_file}="/dev/sdc1 (deleted)"
[...]
it' s the same for me. What do you think about that ?
Cheers,
quidame
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox