udev: remove seqnum API and all assumptions about seqnums

The way the kernel namespaces have been implemented breaks assumptions
udev made regarding uevent sequence numbers. Creating devices in a
namespace "steals" uevents and its sequence numbers from the host. It
confuses the "udevadmin settle" logic, which might block until util a
timeout is reached, even when no uevent is pending.

Remove any assumptions about sequence numbers and deprecate libudev's
API exposing these numbers; none of that can reliably be used anymore
when namespaces are involved.

Signed-off-by: Anthony G. Basile <blueness@gentoo.org>
This commit is contained in:
Kay Sievers 2014-04-24 15:40:08 -04:00 committed by Anthony G. Basile
parent dbc5e32fae
commit 2500dbc810
12 changed files with 235 additions and 953 deletions

View File

@ -1,6 +1,6 @@
AC_PREREQ([2.68])
AC_INIT([eudev],[1.6],[https://github.com/gentoo/eudev/issues])
AC_INIT([eudev],[1.7],[https://github.com/gentoo/eudev/issues])
AC_SUBST(UDEV_VERSION, 212)
AC_CONFIG_SRCDIR([src/udev/udevd.c])

View File

@ -61,9 +61,10 @@
</refsynopsisdiv>
<refsect1><title>Description</title>
<para>udevadm expects a command and command specific options. It
controls the runtime behavior of udev, requests kernel events,
manages the event queue, and provides simple debugging mechanisms.</para>
<para><command>udevadm</command> expects a command and command
specific options. It controls the runtime behavior of
<command>udev</command>, requests kernel events, manages
the event queue, and provides simple debugging mechanisms.</para>
</refsect1>
<refsect1><title>OPTIONS</title>
@ -71,7 +72,7 @@
<varlistentry>
<term><option>--debug</option></term>
<listitem>
<para>Print debug messages to stderr.</para>
<para>Print debug messages to standard error.</para>
</listitem>
</varlistentry>
<varlistentry>
@ -81,6 +82,7 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>-h</option></term>
<term><option>--help</option></term>
<listitem>
<para>Print help text.</para>
@ -88,35 +90,53 @@
</varlistentry>
</variablelist>
<refsect2><title>udevadm info <replaceable>options</replaceable></title>
<refsect2><title>udevadm info <optional><replaceable>OPTIONS</replaceable></optional> <optional><replaceable>DEVPATH</replaceable>|<replaceable>FILE</replaceable></optional></title>
<para>Queries the udev database for device information
stored in the udev database. It can also query the properties
of a device from its sysfs representation to help creating udev
rules that match this device.</para>
<variablelist>
<varlistentry>
<term><option>--query=<replaceable>type</replaceable></option></term>
<term><option>-q</option></term>
<term><option>--query=<replaceable>TYPE</replaceable></option></term>
<listitem>
<para>Query the database for specified type of device data. It needs the
<option>--path</option> or <option>--name</option> to identify the specified
device. Valid queries are:
<command>name</command>, <command>symlink</command>, <command>path</command>,
<command>property</command>, <command>all</command>.</para>
<para>Query the database for the specified type of device
data. It needs the <option>--path</option> or
<option>--name</option> to identify the specified device.
Valid <replaceable>TYPE</replaceable>s are:
<constant>name</constant>, <constant>symlink</constant>,
<constant>path</constant>, <constant>property</constant>,
<constant>all</constant>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--path=<replaceable>devpath</replaceable></option></term>
<term><option>-p</option></term>
<term><option>--path=<replaceable>DEVPATH</replaceable></option></term>
<listitem>
<para>The devpath of the device to query.</para>
<para>The <filename>/sys</filename> path of the device to
query, e.g.
<filename><optional>/sys</optional>/class/block/sda</filename>.
Note that this option usually is not very useful, since
<command>udev</command> can guess the type of the
argument, so <command>udevadm
--devpath=/class/block/sda</command> is equivalent to
<command>udevadm /sys/class/block/sda</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--name=<replaceable>file</replaceable></option></term>
<term><option>-n</option></term>
<term><option>--name=<replaceable>FILE</replaceable></option></term>
<listitem>
<para>The name of the device node or a symlink to query</para>
<para>The name of the device node or a symlink to query,
e.g. <filename><optional>/dev</optional>/sda</filename>.
Note that this option usually is not very useful, since
<command>udev</command> can guess the type of the
argument, so <command>udevadm --name=sda</command> is
equivalent to <command>udevadm /dev/sda</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-r</option></term>
<term><option>--root</option></term>
<listitem>
<para>Print absolute paths in <command>name</command> or <command>symlink</command>
@ -124,6 +144,7 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>-a</option></term>
<term><option>--attribute-walk</option></term>
<listitem>
<para>Print all sysfs properties of the specified device that can be used
@ -132,31 +153,36 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>-x</option></term>
<term><option>--export</option></term>
<listitem>
<para>Print output as key/value pairs. Values are enclosed in single quotes.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--export-prefix=<replaceable>name</replaceable></option></term>
<term><option>-P</option></term>
<term><option>--export-prefix=<replaceable>NAME</replaceable></option></term>
<listitem>
<para>Add a prefix to the key name of exported values.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--device-id-of-file=<replaceable>file</replaceable></option></term>
<term><option>-d</option></term>
<term><option>--device-id-of-file=<replaceable>FILE</replaceable></option></term>
<listitem>
<para>Print major/minor numbers of the underlying device, where the file
lives on.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-e</option></term>
<term><option>--export-db</option></term>
<listitem>
<para>Export the content of the udev database.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-c</option></term>
<term><option>--cleanup-db</option></term>
<listitem>
<para>Cleanup the udev database.</para>
@ -169,6 +195,7 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>-h</option></term>
<term><option>--help</option></term>
<listitem>
<para>Print help text.</para>
@ -181,19 +208,22 @@
<para>Request device events from the kernel. Primarily used to replay events at system coldplug time.</para>
<variablelist>
<varlistentry>
<term><option>-v</option></term>
<term><option>--verbose</option></term>
<listitem>
<para>Print the list of devices which will be triggered.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
<listitem>
<para>Do not actually trigger the event.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--type=<replaceable>type</replaceable></option></term>
<term><option>-t</option></term>
<term><option>--type=<replaceable>TYPE</replaceable></option></term>
<listitem>
<para>Trigger a specific type of devices. Valid types are:
<command>devices</command>, <command>subsystems</command>.
@ -201,68 +231,95 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--action=<replaceable>action</replaceable></option></term>
<term><option>-c</option></term>
<term><option>--action=<replaceable>ACTION</replaceable></option></term>
<listitem>
<para>Type of event to be triggered. The default value is <command>change</command>.</para>
<para>Type of event to be triggered. The default value is
<command>change</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--subsystem-match=<replaceable>subsystem</replaceable></option></term>
<term><option>-s</option></term>
<term><option>--subsystem-match=<replaceable>SUBSYSTEM</replaceable></option></term>
<listitem>
<para>Trigger events for devices which belong to a matching subsystem. This option
can be specified multiple times and supports shell style pattern matching.</para>
<para>Trigger events for devices which belong to a
matching subsystem. This option can be specified multiple
times and supports shell style pattern matching.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--subsystem-nomatch=<replaceable>subsystem</replaceable></option></term>
<term><option>-S</option></term>
<term><option>--subsystem-nomatch=<replaceable>SUBSYSTEM</replaceable></option></term>
<listitem>
<para>Do not trigger events for devices which belong to a matching subsystem. This option
can be specified multiple times and supports shell style pattern matching.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--attr-match=<replaceable>attribute</replaceable>=<replaceable>value</replaceable></option></term>
<term><option>-a</option></term>
<term><option>--attr-match=<replaceable>ATTRIBUTE</replaceable>=<replaceable>VALUE</replaceable></option></term>
<listitem>
<para>Trigger events for devices with a matching sysfs attribute. If a value is specified
along with the attribute name, the content of the attribute is matched against the given
value using shell style pattern matching. If no value is specified, the existence of the
sysfs attribute is checked. This option can be specified multiple times.</para>
<para>Trigger events for devices with a matching sysfs
attribute. If a value is specified along with the
attribute name, the content of the attribute is matched
against the given value using shell style pattern
matching. If no value is specified, the existence of the
sysfs attribute is checked. This option can be specified
multiple times.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--attr-nomatch=<replaceable>attribute</replaceable>=<replaceable>value</replaceable></option></term>
<term><option>-A</option></term>
<term><option>--attr-nomatch=<replaceable>ATTRIBUTE</replaceable>=<replaceable>VALUE</replaceable></option></term>
<listitem>
<para>Do not trigger events for devices with a matching sysfs attribute. If a value is
specified along with the attribute name, the content of the attribute is matched against
the given value using shell style pattern matching. If no value is specified, the existence
of the sysfs attribute is checked. This option can be specified multiple times.</para>
<para>Do not trigger events for devices with a matching
sysfs attribute. If a value is specified along with the
attribute name, the content of the attribute is matched
against the given value using shell style pattern
matching. If no value is specified, the existence of the
sysfs attribute is checked. This option can be specified
multiple times.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--property-match=<replaceable>property</replaceable>=<replaceable>value</replaceable></option></term>
<term><option>-p</option></term>
<term><option>--property-match=<replaceable>PROPERTY</replaceable>=<replaceable>VALUE</replaceable></option></term>
<listitem>
<para>Trigger events for devices with a matching property value. This option can be
specified multiple times and supports shell style pattern matching.</para>
<para>Trigger events for devices with a matching property
value. This option can be specified multiple times and
supports shell style pattern matching.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--tag-match=<replaceable>property</replaceable></option></term>
<term><option>-g</option></term>
<term><option>--tag-match=<replaceable>PROPERTY</replaceable></option></term>
<listitem>
<para>Trigger events for devices with a matching tag. This option can be
specified multiple times.</para>
<para>Trigger events for devices with a matching tag. This
option can be specified multiple times.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--sysname-match=<replaceable>name</replaceable></option></term>
<term><option>-y</option></term>
<term><option>--sysname-match=<replaceable>NAME</replaceable></option></term>
<listitem>
<para>Trigger events for devices with a matching sys device name. This option can be
specified multiple times and supports shell style pattern matching.</para>
<para>Trigger events for devices with a matching sys
device name. This option can be specified multiple times
and supports shell style pattern matching.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--parent-match=<replaceable>syspath</replaceable></option></term>
<term><option>-b</option></term>
<term><option>--parent-match=<replaceable>SYSPATH</replaceable></option></term>
<listitem>
<para>Trigger events for all children of a given device.</para>
<para>Trigger events for all children of a given
device.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-h</option></term>
<term><option>--help</option></term>
<listitem>
<para>Print help text.</para>
</listitem>
</varlistentry>
</variablelist>
@ -272,38 +329,24 @@
<para>Watches the udev event queue, and exits if all current events are handled.</para>
<variablelist>
<varlistentry>
<term><option>--timeout=<replaceable>seconds</replaceable></option></term>
<term><option>-t</option></term>
<term><option>--timeout=<replaceable>SECONDS</replaceable></option></term>
<listitem>
<para>Maximum number of seconds to wait for the event queue to become empty.
The default value is 120 seconds. A value of 0 will check if the queue is empty
and always return immediately.</para>
<para>Maximum number of seconds to wait for the event
queue to become empty. The default value is 120 seconds. A
value of 0 will check if the queue is empty and always
return immediately.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--seq-start=<replaceable>seqnum</replaceable></option></term>
<listitem>
<para>Wait only for events after the given sequence number.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--seq-end=<replaceable>seqnum</replaceable></option></term>
<listitem>
<para>Wait only for events before the given sequence number.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--exit-if-exists=<replaceable>file</replaceable></option></term>
<term><option>-E</option></term>
<term><option>--exit-if-exists=<replaceable>FILE</replaceable></option></term>
<listitem>
<para>Stop waiting if file exists.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--quiet</option></term>
<listitem>
<para>Do not print any output, like the remaining queue entries when reaching the timeout.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-h</option></term>
<term><option>--help</option></term>
<listitem>
<para>Print help text.</para>
@ -316,12 +359,14 @@
<para>Modify the internal state of the running udev daemon.</para>
<variablelist>
<varlistentry>
<term><option>-x</option></term>
<term><option>--exit</option></term>
<listitem>
<para>Signal and wait for udevd to exit.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-l</option></term>
<term><option>--log-priority=<replaceable>value</replaceable></option></term>
<listitem>
<para>Set the internal log level of udevd. Valid values are the numerical
@ -330,6 +375,7 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>-s</option></term>
<term><option>--stop-exec-queue</option></term>
<listitem>
<para>Signal udevd to stop executing new events. Incoming events
@ -337,12 +383,14 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>-S</option></term>
<term><option>--start-exec-queue</option></term>
<listitem>
<para>Signal udevd to enable the execution of events.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-R</option></term>
<term><option>--reload</option></term>
<listitem>
<para>Signal udevd to reload the rules files and other databases like the kernel
@ -351,12 +399,14 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>-p</option></term>
<term><option>--property=<replaceable>KEY</replaceable>=<replaceable>value</replaceable></option></term>
<listitem>
<para>Set a global property for all events.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-m</option></term>
<term><option>--children-max=</option><replaceable>value</replaceable></term>
<listitem>
<para>Set the maximum number of events, udevd will handle at the
@ -370,6 +420,7 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>-h</option></term>
<term><option>--help</option></term>
<listitem>
<para>Print help text.</para>
@ -385,36 +436,42 @@
</para>
<variablelist>
<varlistentry>
<term><option>-k</option></term>
<term><option>--kernel</option></term>
<listitem>
<para>Print the kernel uevents.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-u</option></term>
<term><option>--udev</option></term>
<listitem>
<para>Print the udev event after the rule processing.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-p</option></term>
<term><option>--property</option></term>
<listitem>
<para>Also print the properties of the event.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-s</option></term>
<term><option>--subsystem-match=<replaceable>string[/string]</replaceable></option></term>
<listitem>
<para>Filter events by subsystem[/devtype]. Only udev events with a matching subsystem value will pass.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-t</option></term>
<term><option>--tag-match=<replaceable>string</replaceable></option></term>
<listitem>
<para>Filter events by property. Only udev events with a given tag attached will pass.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-h</option></term>
<term><option>--help</option></term>
<listitem>
<para>Print help text.</para>
@ -427,6 +484,7 @@
<para>Maintain the hardware database index in <filename>/etc/udev/hwdb.bin</filename>.</para>
<variablelist>
<varlistentry>
<term><option>-u</option></term>
<term><option>--update</option></term>
<listitem>
<para>Compile the hardware database information located in /usr/lib/udev/hwdb.d/,
@ -437,6 +495,7 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>-t</option></term>
<term><option>--test=<replaceable>string</replaceable></option></term>
<listitem>
<para>Query the database with a modalias string, and print the
@ -444,9 +503,17 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>-r</option></term>
<term><option>--root=<replaceable>string</replaceable></option></term>
<listitem>
<para>Alternative root path in the filesystem for reading and writing files.</para>
<para>Alternative root path in the file system for reading and writing files.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-h</option></term>
<term><option>--help</option></term>
<listitem>
<para>Print help text.</para>
</listitem>
</varlistentry>
</variablelist>
@ -456,18 +523,27 @@
<para>Simulate a udev event run for the given device, and print debug output.</para>
<variablelist>
<varlistentry>
<term><option>-a</option></term>
<term><option>--action=<replaceable>string</replaceable></option></term>
<listitem>
<para>The action string.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--subsystem=<replaceable>string</replaceable></option></term>
<term><option>-N</option></term>
<term><option>--resolve-names=<constant>early</constant>|<constant>late</constant>|<constant>never</constant></option></term>
<listitem>
<para>The subsystem string.</para>
<para>Specify when udevadm should resolve names of users
and groups. When set to <constant>early</constant> (the
default), names will be resolved when the rules are
parsed. When set to <constant>late</constant>, names will
be resolved for every event. When set to
<constant>never</constant>, names will never be resolved
and all devices will be owned by root.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-h</option></term>
<term><option>--help</option></term>
<listitem>
<para>Print help text.</para>
@ -476,10 +552,13 @@
</variablelist>
</refsect2>
<refsect2><title>udevadm test-builtin <optional>options</optional> <replaceable>command</replaceable> <replaceable>devpath</replaceable></title>
<para>Run a built-in command for the given device, and print debug output.</para>
<refsect2><title>udevadm test-builtin <optional>options</optional> <replaceable>COMMAND</replaceable> <replaceable>DEVPATH</replaceable></title>
<para>Run a built-in command <replaceable>COMMAND</replaceable>
for device <replaceable>DEVPATH</replaceable>, and print debug
output.</para>
<variablelist>
<varlistentry>
<term><option>-h</option></term>
<term><option>--help</option></term>
<listitem>
<para>Print help text.</para>
@ -493,7 +572,7 @@
<title>See Also</title>
<para><citerefentry>
<refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum>
</citerefentry>
</citerefentry>,
<citerefentry>
<refentrytitle>udevd.service</refentrytitle><manvolnum>8</manvolnum>
</citerefentry></para>

View File

@ -88,8 +88,7 @@ libudev_la_LDFLAGS = \
libudev_private_la_SOURCES =\
libudev-device-private.c \
libudev-queue-private.c
libudev-device-private.c
libudev_private_la_LIBADD =\
libudev.la

View File

@ -146,21 +146,6 @@ static bool udev_has_devtmpfs(struct udev *udev) {
return false;
}
/* we consider udev running when we have running udev service */
static bool udev_has_service(struct udev *udev) {
struct udev_queue *queue;
bool active;
queue = udev_queue_new(udev);
if (!queue)
return false;
active = udev_queue_get_udev_is_active(queue);
udev_queue_unref(queue);
return active;
}
struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd)
{
struct udev_monitor *udev_monitor;
@ -184,7 +169,7 @@ struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const c
* We do not set a netlink multicast group here, so the socket
* will not receive any messages.
*/
if (!udev_has_service(udev) && !udev_has_devtmpfs(udev)) {
if (access("/run/udev/control", F_OK) < 0 && !udev_has_devtmpfs(udev)) {
udev_dbg(udev, "the udev service seems not to be active, disable the monitor\n");
group = UDEV_MONITOR_NONE;
} else

View File

@ -1,406 +0,0 @@
/***
This file is part of systemd.
Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
Copyright 2009 Alan Jenkins <alan-jenkins@tuffmail.co.uk>
systemd 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.
systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
***/
/*
* DISCLAIMER - The file format mentioned here is private to udev/libudev,
* and may be changed without notice.
*
* The udev event queue is exported as a binary log file.
* Each log record consists of a sequence number followed by the device path.
*
* When a new event is queued, its details are appended to the log.
* When the event finishes, a second record is appended to the log
* with the same sequence number but a devpath len of 0.
*
* Example:
* { 0x0000000000000001 }
* { 0x0000000000000001, 0x0019, "/devices/virtual/mem/null" },
* { 0x0000000000000002, 0x001b, "/devices/virtual/mem/random" },
* { 0x0000000000000001, 0x0000 },
* { 0x0000000000000003, 0x0019, "/devices/virtual/mem/zero" },
*
* Events 2 and 3 are still queued, but event 1 has finished.
*
* The queue does not grow indefinitely. It is periodically re-created
* to remove finished events. Atomic rename() makes this transparent to readers.
*
* The queue file starts with a single sequence number which specifies the
* minimum sequence number in the log that follows. Any events prior to this
* sequence number have already finished.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <limits.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "libudev.h"
#include "libudev-private.h"
static int rebuild_queue_file(struct udev_queue_export *udev_queue_export);
struct udev_queue_export {
struct udev *udev;
int queued_count; /* number of unfinished events exported in queue file */
FILE *queue_file;
unsigned long long int seqnum_max; /* earliest sequence number in queue file */
unsigned long long int seqnum_min; /* latest sequence number in queue file */
int waste_bytes; /* queue file bytes wasted on finished events */
};
struct udev_queue_export *udev_queue_export_new(struct udev *udev)
{
struct udev_queue_export *udev_queue_export;
unsigned long long int initial_seqnum;
if (udev == NULL)
return NULL;
udev_queue_export = new0(struct udev_queue_export, 1);
if (udev_queue_export == NULL)
return NULL;
udev_queue_export->udev = udev;
initial_seqnum = udev_get_kernel_seqnum(udev);
udev_queue_export->seqnum_min = initial_seqnum;
udev_queue_export->seqnum_max = initial_seqnum;
udev_queue_export_cleanup(udev_queue_export);
if (rebuild_queue_file(udev_queue_export) != 0) {
free(udev_queue_export);
return NULL;
}
return udev_queue_export;
}
struct udev_queue_export *udev_queue_export_unref(struct udev_queue_export *udev_queue_export)
{
if (udev_queue_export == NULL)
return NULL;
if (udev_queue_export->queue_file != NULL)
fclose(udev_queue_export->queue_file);
free(udev_queue_export);
return NULL;
}
void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export)
{
if (udev_queue_export == NULL)
return;
unlink("/run/udev/queue.tmp");
unlink("/run/udev/queue.bin");
}
static int skip_to(FILE *file, long offset)
{
long old_offset;
/* fseek may drop buffered data, avoid it for small seeks */
old_offset = ftell(file);
if (offset > old_offset && offset - old_offset <= BUFSIZ) {
size_t skip_bytes = offset - old_offset;
char *buf = alloca(skip_bytes);
if (fread(buf, skip_bytes, 1, file) != skip_bytes)
return -1;
}
return fseek(file, offset, SEEK_SET);
}
struct queue_devpaths {
unsigned int devpaths_first; /* index of first queued event */
unsigned int devpaths_size;
long devpaths[]; /* seqnum -> offset of devpath in queue file (or 0) */
};
/*
* Returns a table mapping seqnum to devpath file offset for currently queued events.
* devpaths[i] represents the event with seqnum = i + udev_queue_export->seqnum_min.
*/
static struct queue_devpaths *build_index(struct udev_queue_export *udev_queue_export)
{
struct queue_devpaths *devpaths;
unsigned long long int range;
long devpath_offset;
ssize_t devpath_len;
unsigned long long int seqnum;
unsigned long long int n;
unsigned int i;
/* seek to the first event in the file */
rewind(udev_queue_export->queue_file);
udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum);
/* allocate the table */
range = udev_queue_export->seqnum_min - udev_queue_export->seqnum_max;
if (range - 1 > INT_MAX) {
udev_err(udev_queue_export->udev, "queue file overflow\n");
return NULL;
}
devpaths = malloc0(sizeof(struct queue_devpaths) + (range + 1) * sizeof(long));
if (devpaths == NULL)
return NULL;
devpaths->devpaths_size = range + 1;
/* read all records and populate the table */
for (;;) {
if (udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum) < 0)
break;
n = seqnum - udev_queue_export->seqnum_max;
if (n >= devpaths->devpaths_size)
goto read_error;
devpath_offset = ftell(udev_queue_export->queue_file);
devpath_len = udev_queue_skip_devpath(udev_queue_export->queue_file);
if (devpath_len < 0)
goto read_error;
if (devpath_len > 0)
devpaths->devpaths[n] = devpath_offset;
else
devpaths->devpaths[n] = 0;
}
/* find first queued event */
for (i = 0; i < devpaths->devpaths_size; i++) {
if (devpaths->devpaths[i] != 0)
break;
}
devpaths->devpaths_first = i;
return devpaths;
read_error:
udev_err(udev_queue_export->udev, "queue file corrupted\n");
free(devpaths);
return NULL;
}
static int rebuild_queue_file(struct udev_queue_export *udev_queue_export)
{
unsigned long long int seqnum;
struct queue_devpaths *devpaths = NULL;
FILE *new_queue_file = NULL;
unsigned int i;
/* read old queue file */
if (udev_queue_export->queue_file != NULL) {
devpaths = build_index(udev_queue_export);
if (devpaths != NULL)
udev_queue_export->seqnum_max += devpaths->devpaths_first;
}
if (devpaths == NULL) {
udev_queue_export->queued_count = 0;
udev_queue_export->seqnum_max = udev_queue_export->seqnum_min;
}
/* create new queue file */
new_queue_file = fopen("/run/udev/queue.tmp", "w+e");
if (new_queue_file == NULL)
goto error;
seqnum = udev_queue_export->seqnum_max;
fwrite(&seqnum, 1, sizeof(unsigned long long int), new_queue_file);
/* copy unfinished events only to the new file */
if (devpaths != NULL) {
for (i = devpaths->devpaths_first; i < devpaths->devpaths_size; i++) {
char devpath[UTIL_PATH_SIZE];
int err;
unsigned short devpath_len;
if (devpaths->devpaths[i] != 0)
{
skip_to(udev_queue_export->queue_file, devpaths->devpaths[i]);
err = udev_queue_read_devpath(udev_queue_export->queue_file, devpath, sizeof(devpath));
devpath_len = err;
fwrite(&seqnum, sizeof(unsigned long long int), 1, new_queue_file);
fwrite(&devpath_len, sizeof(unsigned short), 1, new_queue_file);
fwrite(devpath, 1, devpath_len, new_queue_file);
}
seqnum++;
}
free(devpaths);
devpaths = NULL;
}
fflush(new_queue_file);
if (ferror(new_queue_file))
goto error;
/* rename the new file on top of the old one */
if (rename("/run/udev/queue.tmp", "/run/udev/queue.bin") != 0)
goto error;
if (udev_queue_export->queue_file != NULL)
fclose(udev_queue_export->queue_file);
udev_queue_export->queue_file = new_queue_file;
udev_queue_export->waste_bytes = 0;
return 0;
error:
udev_err(udev_queue_export->udev, "failed to create queue file: %m\n");
udev_queue_export_cleanup(udev_queue_export);
if (udev_queue_export->queue_file != NULL) {
fclose(udev_queue_export->queue_file);
udev_queue_export->queue_file = NULL;
}
if (new_queue_file != NULL)
fclose(new_queue_file);
if (devpaths != NULL)
free(devpaths);
udev_queue_export->queued_count = 0;
udev_queue_export->waste_bytes = 0;
udev_queue_export->seqnum_max = udev_queue_export->seqnum_min;
return -1;
}
static int write_queue_record(struct udev_queue_export *udev_queue_export,
unsigned long long int seqnum, const char *devpath, size_t devpath_len)
{
unsigned short len;
if (udev_queue_export->queue_file == NULL)
return -1;
if (fwrite(&seqnum, sizeof(unsigned long long int), 1, udev_queue_export->queue_file) != 1)
goto write_error;
len = (devpath_len < USHRT_MAX) ? devpath_len : USHRT_MAX;
if (fwrite(&len, sizeof(unsigned short), 1, udev_queue_export->queue_file) != 1)
goto write_error;
if (len > 0) {
if (fwrite(devpath, 1, len, udev_queue_export->queue_file) != len)
goto write_error;
}
/* *must* flush output; caller may fork */
if (fflush(udev_queue_export->queue_file) != 0)
goto write_error;
return 0;
write_error:
/* if we failed half way through writing a record to a file,
we should not try to write any further records to it. */
udev_err(udev_queue_export->udev, "error writing to queue file: %m\n");
fclose(udev_queue_export->queue_file);
udev_queue_export->queue_file = NULL;
return -1;
}
enum device_state {
DEVICE_QUEUED,
DEVICE_FINISHED,
};
static inline size_t queue_record_size(size_t devpath_len)
{
return sizeof(unsigned long long int) + sizeof(unsigned short int) + devpath_len;
}
static int update_queue(struct udev_queue_export *udev_queue_export,
struct udev_device *udev_device, enum device_state state)
{
unsigned long long int seqnum = udev_device_get_seqnum(udev_device);
const char *devpath = NULL;
size_t devpath_len = 0;
int bytes;
int err;
/* FINISHED records have a zero length devpath */
if (state == DEVICE_QUEUED) {
devpath = udev_device_get_devpath(udev_device);
devpath_len = strlen(devpath);
}
/* recover from an earlier failed rebuild */
if (udev_queue_export->queue_file == NULL) {
if (rebuild_queue_file(udev_queue_export) != 0)
return -1;
}
/* if we're removing the last event from the queue, that's the best time to rebuild it */
if (state != DEVICE_QUEUED && udev_queue_export->queued_count == 1) {
/* we don't need to read the old queue file */
fclose(udev_queue_export->queue_file);
udev_queue_export->queue_file = NULL;
rebuild_queue_file(udev_queue_export);
return 0;
}
/* try to rebuild the queue files before they grow larger than one page. */
bytes = ftell(udev_queue_export->queue_file) + queue_record_size(devpath_len);
if ((udev_queue_export->waste_bytes > bytes / 2) && bytes > 4096)
rebuild_queue_file(udev_queue_export);
/* don't record a finished event, if we already dropped the event in a failed rebuild */
if (seqnum < udev_queue_export->seqnum_max)
return 0;
/* now write to the queue */
if (state == DEVICE_QUEUED) {
udev_queue_export->queued_count++;
udev_queue_export->seqnum_min = seqnum;
} else {
udev_queue_export->waste_bytes += queue_record_size(devpath_len) + queue_record_size(0);
udev_queue_export->queued_count--;
}
err = write_queue_record(udev_queue_export, seqnum, devpath, devpath_len);
/* try to handle ENOSPC */
if (err != 0 && udev_queue_export->queued_count == 0) {
udev_queue_export_cleanup(udev_queue_export);
err = rebuild_queue_file(udev_queue_export);
}
return err;
}
static int update(struct udev_queue_export *udev_queue_export,
struct udev_device *udev_device, enum device_state state)
{
if (update_queue(udev_queue_export, udev_device, state) != 0)
return -1;
return 0;
}
int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
{
return update(udev_queue_export, udev_device, DEVICE_QUEUED);
}
int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
{
return update(udev_queue_export, udev_device, DEVICE_FINISHED);
}

View File

@ -24,8 +24,6 @@
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/stat.h>
@ -36,10 +34,7 @@
* SECTION:libudev-queue
* @short_description: access to currently active events
*
* The udev daemon processes events asynchronously. All events which do not have
* interdependencies run in parallel. This exports the current state of the
* event processing queue, and the current event sequence numbers from the kernel
* and the udev daemon.
* This exports the current state of the udev processing queue.
*/
/**
@ -50,7 +45,6 @@
struct udev_queue {
struct udev *udev;
int refcount;
struct udev_list queue_list;
};
/**
@ -72,9 +66,9 @@ _public_ struct udev_queue *udev_queue_new(struct udev *udev)
udev_queue = new0(struct udev_queue, 1);
if (udev_queue == NULL)
return NULL;
udev_queue->refcount = 1;
udev_queue->udev = udev;
udev_list_init(udev, &udev_queue->queue_list, false);
return udev_queue;
}
@ -90,6 +84,7 @@ _public_ struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue)
{
if (udev_queue == NULL)
return NULL;
udev_queue->refcount++;
return udev_queue;
}
@ -107,10 +102,11 @@ _public_ struct udev_queue *udev_queue_unref(struct udev_queue *udev_queue)
{
if (udev_queue == NULL)
return NULL;
udev_queue->refcount--;
if (udev_queue->refcount > 0)
return NULL;
udev_list_cleanup(&udev_queue->queue_list);
free(udev_queue);
return NULL;
}
@ -130,141 +126,30 @@ _public_ struct udev *udev_queue_get_udev(struct udev_queue *udev_queue)
return udev_queue->udev;
}
unsigned long long int udev_get_kernel_seqnum(struct udev *udev)
{
unsigned long long int seqnum;
int fd;
char buf[32];
ssize_t len;
fd = open("/sys/kernel/uevent_seqnum", O_RDONLY|O_CLOEXEC);
if (fd < 0)
return 0;
len = read(fd, buf, sizeof(buf));
close(fd);
if (len <= 2)
return 0;
buf[len-1] = '\0';
seqnum = strtoull(buf, NULL, 10);
return seqnum;
}
/**
* udev_queue_get_kernel_seqnum:
* @udev_queue: udev queue context
*
* Get the current kernel event sequence number.
* This function is deprecated.
*
* Returns: the sequence number.
* Returns: 0.
**/
_public_ unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue)
{
unsigned long long int seqnum;
if (udev_queue == NULL)
return -EINVAL;
seqnum = udev_get_kernel_seqnum(udev_queue->udev);
return seqnum;
}
int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum)
{
if (fread(seqnum, sizeof(unsigned long long int), 1, queue_file) != 1)
return -1;
return 0;
}
ssize_t udev_queue_skip_devpath(FILE *queue_file)
{
unsigned short int len;
if (fread(&len, sizeof(unsigned short int), 1, queue_file) == 1) {
char *devpath = alloca(len);
/* use fread to skip, fseek might drop buffered data */
if (fread(devpath, 1, len, queue_file) == len)
return len;
}
return -1;
}
ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size)
{
unsigned short int read_bytes = 0;
unsigned short int len;
if (fread(&len, sizeof(unsigned short int), 1, queue_file) != 1)
return -1;
read_bytes = (len < size - 1) ? len : size - 1;
if (fread(devpath, 1, read_bytes, queue_file) != read_bytes)
return -1;
devpath[read_bytes] = '\0';
/* if devpath was too long, skip unread characters */
if (read_bytes != len) {
unsigned short int skip_bytes = len - read_bytes;
char *buf = alloca(skip_bytes);
if (fread(buf, 1, skip_bytes, queue_file) != skip_bytes)
return -1;
}
return read_bytes;
}
static FILE *open_queue_file(struct udev_queue *udev_queue, unsigned long long int *seqnum_start)
{
FILE *queue_file;
queue_file = fopen("/run/udev/queue.bin", "re");
if (queue_file == NULL)
return NULL;
if (udev_queue_read_seqnum(queue_file, seqnum_start) < 0) {
udev_err(udev_queue->udev, "corrupt queue file\n");
fclose(queue_file);
return NULL;
}
return queue_file;
}
/**
* udev_queue_get_udev_seqnum:
* @udev_queue: udev queue context
*
* Get the last known udev event sequence number.
* This function is deprecated.
*
* Returns: the sequence number.
* Returns: 0.
**/
_public_ unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue)
{
unsigned long long int seqnum_udev;
FILE *queue_file;
queue_file = open_queue_file(udev_queue, &seqnum_udev);
if (queue_file == NULL)
return 0;
for (;;) {
unsigned long long int seqnum;
ssize_t devpath_len;
if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
break;
devpath_len = udev_queue_skip_devpath(queue_file);
if (devpath_len < 0)
break;
if (devpath_len > 0)
seqnum_udev = seqnum;
}
fclose(queue_file);
return seqnum_udev;
return 0;
}
/**
@ -277,15 +162,7 @@ _public_ unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *ud
**/
_public_ int udev_queue_get_udev_is_active(struct udev_queue *udev_queue)
{
unsigned long long int seqnum_start;
FILE *queue_file;
queue_file = open_queue_file(udev_queue, &seqnum_start);
if (queue_file == NULL)
return 0;
fclose(queue_file);
return 1;
return access("/run/udev/control", F_OK) >= 0;
}
/**
@ -298,48 +175,7 @@ _public_ int udev_queue_get_udev_is_active(struct udev_queue *udev_queue)
**/
_public_ int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue)
{
unsigned long long int seqnum_kernel;
unsigned long long int seqnum_udev = 0;
int queued = 0;
int is_empty = 0;
FILE *queue_file;
if (udev_queue == NULL)
return -EINVAL;
queue_file = open_queue_file(udev_queue, &seqnum_udev);
if (queue_file == NULL)
return 1;
for (;;) {
unsigned long long int seqnum;
ssize_t devpath_len;
if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
break;
devpath_len = udev_queue_skip_devpath(queue_file);
if (devpath_len < 0)
break;
if (devpath_len > 0) {
queued++;
seqnum_udev = seqnum;
} else {
queued--;
}
}
if (queued > 0)
goto out;
seqnum_kernel = udev_queue_get_kernel_seqnum(udev_queue);
if (seqnum_udev < seqnum_kernel)
goto out;
is_empty = 1;
out:
fclose(queue_file);
return is_empty;
return access("/run/udev/queue", F_OK) >= 0;
}
/**
@ -348,63 +184,15 @@ out:
* @start: first event sequence number
* @end: last event sequence number
*
* Check if udev is currently processing any events in a given sequence number range.
* This function is deprecated, it just returns the result of
* udev_queue_get_queue_is_empty().
*
* Returns: a flag indicating if any of the sequence numbers in the given range is currently active.
* Returns: a flag indicating if udev is currently handling events.
**/
_public_ int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
unsigned long long int start, unsigned long long int end)
{
unsigned long long int seqnum;
ssize_t devpath_len;
int unfinished;
FILE *queue_file;
if (udev_queue == NULL)
return -EINVAL;
queue_file = open_queue_file(udev_queue, &seqnum);
if (queue_file == NULL)
return 1;
if (start < seqnum)
start = seqnum;
if (start > end) {
fclose(queue_file);
return 1;
}
if (end - start > INT_MAX - 1) {
fclose(queue_file);
return -EOVERFLOW;
}
/*
* we might start with 0, and handle the initial seqnum
* only when we find an entry in the queue file
**/
unfinished = end - start;
do {
if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
break;
devpath_len = udev_queue_skip_devpath(queue_file);
if (devpath_len < 0)
break;
/*
* we might start with an empty or re-build queue file, where
* the initial seqnum is not recorded as finished
*/
if (start == seqnum && devpath_len > 0)
unfinished++;
if (devpath_len == 0) {
if (seqnum >= start && seqnum <= end)
unfinished--;
}
} while (unfinished > 0);
fclose(queue_file);
return (unfinished == 0);
return udev_queue_get_queue_is_empty(udev_queue);
}
/**
@ -412,69 +200,25 @@ _public_ int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_
* @udev_queue: udev queue context
* @seqnum: sequence number
*
* Check if udev is currently processing a given sequence number.
* This function is deprecated, it just returns the result of
* udev_queue_get_queue_is_empty().
*
* Returns: a flag indicating if the given sequence number is currently active.
* Returns: a flag indicating if udev is currently handling events.
**/
_public_ int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum)
{
if (!udev_queue_get_seqnum_sequence_is_finished(udev_queue, seqnum, seqnum))
return 0;
return 1;
return udev_queue_get_queue_is_empty(udev_queue);
}
/**
* udev_queue_get_queued_list_entry:
* @udev_queue: udev queue context
*
* Get the first entry of the list of queued events.
* This function is deprecated.
*
* Returns: a udev_list_entry.
* Returns: NULL.
**/
_public_ struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue)
{
unsigned long long int seqnum;
FILE *queue_file;
if (udev_queue == NULL)
return NULL;
udev_list_cleanup(&udev_queue->queue_list);
queue_file = open_queue_file(udev_queue, &seqnum);
if (queue_file == NULL)
return NULL;
for (;;) {
char syspath[UTIL_PATH_SIZE];
char *s;
size_t l;
ssize_t len;
char seqnum_str[32];
struct udev_list_entry *list_entry;
if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
break;
snprintf(seqnum_str, sizeof(seqnum_str), "%llu", seqnum);
s = syspath;
l = strpcpy(&s, sizeof(syspath), "/sys");
len = udev_queue_read_devpath(queue_file, s, l);
if (len < 0)
break;
if (len > 0) {
udev_list_entry_add(&udev_queue->queue_list, syspath, seqnum_str);
} else {
udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_queue->queue_list)) {
if (streq(seqnum_str, udev_list_entry_get_value(list_entry))) {
udev_list_entry_delete(list_entry);
break;
}
}
}
}
fclose(queue_file);
return udev_list_get_entry(&udev_queue->queue_list);
return NULL;
}

View File

@ -170,14 +170,14 @@ struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue);
struct udev_queue *udev_queue_unref(struct udev_queue *udev_queue);
struct udev *udev_queue_get_udev(struct udev_queue *udev_queue);
struct udev_queue *udev_queue_new(struct udev *udev);
unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue);
unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue);
unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue) __attribute__ ((deprecated));
unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue) __attribute__ ((deprecated));
int udev_queue_get_udev_is_active(struct udev_queue *udev_queue);
int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue);
int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum);
int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum) __attribute__ ((deprecated));
int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
unsigned long long int start, unsigned long long int end);
struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue);
unsigned long long int start, unsigned long long int end) __attribute__ ((deprecated));
struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue) __attribute__ ((deprecated));
/*
* udev_hwdb

View File

@ -297,7 +297,7 @@ static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int
pfd[0].fd = uctrl->sock;
pfd[0].events = POLLIN;
r = poll(pfd, 1, timeout * 1000);
r = poll(pfd, 1, timeout * MSEC_PER_SEC);
if (r < 0) {
if (errno == EINTR)
continue;

View File

@ -31,7 +31,6 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_event*, udev_event_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_rules*, udev_rules_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl*, udev_ctrl_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_monitor*, udev_monitor_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_queue*, udev_queue_unref);
#define _cleanup_udev_unref_ _cleanup_(udev_unrefp)
#define _cleanup_udev_device_unref_ _cleanup_(udev_device_unrefp)
@ -40,5 +39,4 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_queue*, udev_queue_unref);
#define _cleanup_udev_rules_unref_ _cleanup_(udev_rules_unrefp)
#define _cleanup_udev_ctrl_unref_ _cleanup_(udev_ctrl_unrefp)
#define _cleanup_udev_monitor_unref_ _cleanup_(udev_monitor_unrefp)
#define _cleanup_udev_queue_unref_ _cleanup_(udev_queue_unrefp)
#define _cleanup_udev_list_cleanup_ _cleanup_(udev_list_cleanup)

View File

@ -41,42 +41,28 @@
static void help(void) {
printf("Usage: udevadm settle OPTIONS\n"
" -t,--timeout=<seconds> maximum time to wait for events\n"
" -s,--seq-start=<seqnum> first seqnum to wait for\n"
" -e,--seq-end=<seqnum> last seqnum to wait for\n"
" -E,--exit-if-exists=<file> stop waiting if file exists\n"
" -q,--quiet do not print list after timeout\n"
" -h,--help\n\n");
}
static int adm_settle(struct udev *udev, int argc, char *argv[])
{
static const struct option options[] = {
{ "seq-start", required_argument, NULL, 's' },
{ "seq-end", required_argument, NULL, 'e' },
{ "seq-start", required_argument, NULL, '\0' }, /* removed */
{ "seq-end", required_argument, NULL, '\0' }, /* removed */
{ "timeout", required_argument, NULL, 't' },
{ "exit-if-exists", required_argument, NULL, 'E' },
{ "quiet", no_argument, NULL, 'q' },
{ "quiet", no_argument, NULL, 'q' }, /* removed */
{ "help", no_argument, NULL, 'h' },
{}
};
usec_t start_usec = now(CLOCK_MONOTONIC);
usec_t start = 0;
usec_t end = 0;
int quiet = 0;
const char *exists = NULL;
unsigned int timeout = 120;
struct pollfd pfd[1] = { {.fd = -1}, };
_cleanup_udev_queue_unref_ struct udev_queue *udev_queue = NULL;
int rc = EXIT_FAILURE, c;
while ((c = getopt_long(argc, argv, "s:e:t:E:qh", options, NULL)) >= 0)
while ((c = getopt_long(argc, argv, "s:e:t:E:qh", options, NULL)) >= 0) {
switch (c) {
case 's':
start = strtoull(optarg, NULL, 0);
break;
case 'e':
end = strtoull(optarg, NULL, 0);
break;
case 't': {
int r;
@ -91,9 +77,6 @@ static int adm_settle(struct udev *udev, int argc, char *argv[])
case 'E':
exists = optarg;
break;
case 'q':
quiet = 1;
break;
case 'h':
help();
exit(EXIT_SUCCESS);
@ -102,44 +85,13 @@ static int adm_settle(struct udev *udev, int argc, char *argv[])
default:
assert_not_reached("Unknown argument");
}
}
if (optind < argc) {
fprintf(stderr, "Extraneous argument: '%s'\n", argv[optind]);
exit(EXIT_FAILURE);
}
udev_queue = udev_queue_new(udev);
if (udev_queue == NULL)
exit(2);
if (start > 0) {
unsigned long long kernel_seq;
kernel_seq = udev_queue_get_kernel_seqnum(udev_queue);
/* unless specified, the last event is the current kernel seqnum */
if (end == 0)
end = udev_queue_get_kernel_seqnum(udev_queue);
if (start > end) {
log_error("seq-start larger than seq-end, ignoring");
start = 0;
end = 0;
}
if (start > kernel_seq || end > kernel_seq) {
log_error("seq-start or seq-end larger than current kernel value, ignoring");
start = 0;
end = 0;
}
log_debug("start=%llu end=%llu current=%llu", (unsigned long long)start, (unsigned long long)end, kernel_seq);
} else {
if (end > 0) {
log_error("seq-end needs seq-start parameter, ignoring");
end = 0;
}
}
/* guarantee that the udev daemon isn't pre-processing */
if (getuid() == 0) {
struct udev_ctrl *uctrl;
@ -160,76 +112,34 @@ static int adm_settle(struct udev *udev, int argc, char *argv[])
pfd[0].fd = inotify_init1(IN_CLOEXEC);
if (pfd[0].fd < 0) {
log_error("inotify_init failed: %m");
} else {
if (inotify_add_watch(pfd[0].fd, "/run/udev" , IN_MOVED_TO) < 0) {
log_error("watching /run/udev failed");
close(pfd[0].fd);
pfd[0].fd = -1;
}
goto out;
}
if (inotify_add_watch(pfd[0].fd, "/run/udev/queue" , IN_DELETE) < 0) {
log_debug("watching /run/udev failed");
goto out;
}
for (;;) {
struct stat statbuf;
if (exists != NULL && stat(exists, &statbuf) == 0) {
if (exists && access(exists, F_OK) >= 0) {
rc = EXIT_SUCCESS;
break;
}
if (start > 0) {
/* if asked for, wait for a specific sequence of events */
if (udev_queue_get_seqnum_sequence_is_finished(udev_queue, start, end) == 1) {
rc = EXIT_SUCCESS;
break;
}
} else {
/* exit if queue is empty */
if (udev_queue_get_queue_is_empty(udev_queue)) {
rc = EXIT_SUCCESS;
break;
}
/* exit if queue is empty */
if (access("/run/udev/queue", F_OK) < 0) {
rc = EXIT_SUCCESS;
break;
}
if (pfd[0].fd >= 0) {
int delay;
/* wake up when "queue" file is deleted */
if (poll(pfd, 1, 100) > 0 && pfd[0].revents & POLLIN) {
char buf[sizeof(struct inotify_event) + PATH_MAX];
if (exists != NULL || start > 0)
delay = 100;
else
delay = 1000;
/* wake up after delay, or immediately after the queue is rebuilt */
if (poll(pfd, 1, delay) > 0 && pfd[0].revents & POLLIN) {
char buf[sizeof(struct inotify_event) + PATH_MAX];
if(read(pfd[0].fd, buf, sizeof(buf)) < 0){
log_error("failed to read /run/udev");
goto out;
}
}
} else {
sleep(1);
}
if (timeout > 0) {
usec_t age_usec;
age_usec = now(CLOCK_MONOTONIC) - start_usec;
if (age_usec / (1000 * 1000) >= timeout) {
struct udev_list_entry *list_entry;
if (!quiet && udev_queue_get_queued_list_entry(udev_queue) != NULL) {
log_debug("timeout waiting for udev queue");
printf("\nudevadm settle - timeout of %i seconds reached, the event queue contains:\n", timeout);
udev_list_entry_foreach(list_entry, udev_queue_get_queued_list_entry(udev_queue))
printf(" %s (%s)\n",
udev_list_entry_get_name(list_entry),
udev_list_entry_get_value(list_entry));
}
break;
}
read(pfd[0].fd, buf, sizeof(buf));
}
}
out:
if (pfd[0].fd >= 0)
close(pfd[0].fd);
@ -239,5 +149,5 @@ out:
const struct udevadm_cmd udevadm_settle = {
.name = "settle",
.cmd = adm_settle,
.help = "wait for the event queue to finish",
.help = "wait for pending udev events",
};

View File

@ -60,7 +60,6 @@ void udev_main_log(struct udev *udev, int priority,
}
static struct udev_rules *rules;
static struct udev_queue_export *udev_queue_export;
static struct udev_ctrl *udev_ctrl;
static struct udev_monitor *monitor;
static int worker_watch[2] = { -1, -1 };
@ -139,14 +138,9 @@ static inline struct worker *node_to_worker(struct udev_list_node *node)
return container_of(node, struct worker, node);
}
static void event_queue_delete(struct event *event, bool export)
static void event_queue_delete(struct event *event)
{
udev_list_node_remove(&event->node);
if (export) {
udev_queue_export_device_finished(udev_queue_export, event->dev);
log_debug("seq %llu done with %i", udev_device_get_seqnum(event->dev), event->exitcode);
}
udev_device_unref(event->dev);
free(event);
}
@ -225,7 +219,6 @@ static void worker_new(struct event *event)
free(worker);
worker_list_cleanup(udev);
event_queue_cleanup(udev, EVENT_UNDEF);
udev_queue_export_unref(udev_queue_export);
udev_monitor_unref(monitor);
udev_ctrl_unref(udev_ctrl);
close(fd_signal);
@ -449,7 +442,6 @@ static int event_queue_insert(struct udev_device *dev)
event->nodelay = true;
#endif
udev_queue_export_device_queued(udev_queue_export, dev);
log_debug("seq %llu queued, '%s' '%s'", udev_device_get_seqnum(dev),
udev_device_get_action(dev), udev_device_get_subsystem(dev));
@ -580,7 +572,7 @@ static void event_queue_cleanup(struct udev *udev, enum event_state match_type)
if (match_type != EVENT_UNDEF && match_type != event->state)
continue;
event_queue_delete(event, false);
event_queue_delete(event);
}
}
@ -605,7 +597,7 @@ static void worker_returned(int fd_worker)
/* worker returned */
if (worker->event) {
worker->event->exitcode = msg.exitcode;
event_queue_delete(worker->event, true);
event_queue_delete(worker->event);
worker->event = NULL;
}
if (worker->state != WORKER_KILLED)
@ -797,7 +789,8 @@ static void handle_signal(struct udev *udev, int signo)
log_error("worker [%u] failed while handling '%s'",
pid, worker->event->devpath);
worker->event->exitcode = -32;
event_queue_delete(worker->event, true);
event_queue_delete(worker->event);
/* drop reference taken for state 'running' */
worker_unref(worker);
}
@ -1093,14 +1086,7 @@ int main(int argc, char *argv[])
goto exit;
}
udev_monitor_set_receive_buffer_size(monitor, 128*1024*1024);
/* create queue file before signalling 'ready', to make sure we block 'settle' */
udev_queue_export = udev_queue_export_new(udev);
if (udev_queue_export == NULL) {
log_error("error creating queue file");
goto exit;
}
udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024);
if (daemonize) {
pid_t pid;
@ -1268,12 +1254,12 @@ int main(int argc, char *argv[])
worker_kill(udev);
/* exit after all has cleaned up */
if (udev_list_node_is_empty(&event_list) && udev_list_node_is_empty(&worker_list))
if (udev_list_node_is_empty(&event_list) && children == 0)
break;
/* timeout at exit for workers to finish */
timeout = 30 * 1000;
} else if (udev_list_node_is_empty(&event_list) && !children) {
timeout = 30 * MSEC_PER_SEC;
} else if (udev_list_node_is_empty(&event_list) && children == 0) {
/* we are idle */
timeout = -1;
@ -1282,8 +1268,20 @@ int main(int argc, char *argv[])
cg_kill(SYSTEMD_CGROUP_CONTROLLER, udev_cgroup, SIGKILL, false, true, NULL);
} else {
/* kill idle or hanging workers */
timeout = 3 * 1000;
timeout = 3 * MSEC_PER_SEC;
}
/* tell settle that we are busy or idle */
if (!udev_list_node_is_empty(&event_list)) {
int fd;
fd = open("/run/udev/queue", O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
if (fd >= 0)
close(fd);
} else {
unlink("/run/udev/queue");
}
fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), timeout);
if (fdcount < 0)
continue;
@ -1310,18 +1308,18 @@ int main(int argc, char *argv[])
if (worker->state != WORKER_RUNNING)
continue;
if ((now(CLOCK_MONOTONIC) - worker->event_start_usec) > 30 * 1000 * 1000) {
if ((now(CLOCK_MONOTONIC) - worker->event_start_usec) > 30 * USEC_PER_SEC) {
log_error("worker [%u] %s timeout; kill it", worker->pid,
worker->event ? worker->event->devpath : "<idle>");
kill(worker->pid, SIGKILL);
worker->state = WORKER_KILLED;
/* drop reference taken for state 'running' */
worker_unref(worker);
if (worker && worker->event) {
log_error("seq %llu '%s' killed",
udev_device_get_seqnum(worker->event->dev), worker->event->devpath);
log_error("seq %llu '%s' killed", udev_device_get_seqnum(worker->event->dev), worker->event->devpath);
worker->event->exitcode = -64;
event_queue_delete(worker->event, true);
event_queue_delete(worker->event);
worker->event = NULL;
}
}
@ -1344,7 +1342,7 @@ int main(int argc, char *argv[])
}
/* check for changed config, every 3 seconds at most */
if ((now(CLOCK_MONOTONIC) - last_usec) > 3 * 1000 * 1000) {
if ((now(CLOCK_MONOTONIC) - last_usec) > 3 * USEC_PER_SEC) {
if (udev_rules_check_timestamp(rules))
reload = true;
if (udev_builtin_validate(udev))
@ -1417,8 +1415,8 @@ int main(int argc, char *argv[])
rc = EXIT_SUCCESS;
exit:
udev_queue_export_cleanup(udev_queue_export);
udev_ctrl_cleanup(udev_ctrl);
unlink("/run/udev/queue");
exit_daemonize:
if (fd_ep >= 0)
close(fd_ep);
@ -1433,7 +1431,6 @@ exit_daemonize:
if (worker_watch[WRITE_END] >= 0)
close(worker_watch[WRITE_END]);
udev_monitor_unref(monitor);
udev_queue_export_unref(udev_queue_export);
udev_ctrl_connection_unref(ctrl_conn);
udev_ctrl_unref(udev_ctrl);
label_finish();

View File

@ -303,38 +303,14 @@ out:
static int test_queue(struct udev *udev) {
struct udev_queue *udev_queue;
unsigned long long int seqnum;
struct udev_list_entry *list_entry;
udev_queue = udev_queue_new(udev);
if (udev_queue == NULL)
return -1;
seqnum = udev_queue_get_kernel_seqnum(udev_queue);
printf("seqnum kernel: %llu\n", seqnum);
seqnum = udev_queue_get_udev_seqnum(udev_queue);
printf("seqnum udev : %llu\n", seqnum);
if (udev_queue_get_queue_is_empty(udev_queue))
printf("queue is empty\n");
printf("get queue list\n");
udev_list_entry_foreach(list_entry, udev_queue_get_queued_list_entry(udev_queue))
printf("queued: '%s' [%s]\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
printf("\n");
printf("get queue list again\n");
udev_list_entry_foreach(list_entry, udev_queue_get_queued_list_entry(udev_queue))
printf("queued: '%s' [%s]\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry));
printf("\n");
list_entry = udev_queue_get_queued_list_entry(udev_queue);
if (list_entry != NULL) {
printf("event [%llu] is queued\n", seqnum);
seqnum = strtoull(udev_list_entry_get_value(list_entry), NULL, 10);
if (udev_queue_get_seqnum_is_finished(udev_queue, seqnum))
printf("event [%llu] is not finished\n", seqnum);
else
printf("event [%llu] is finished\n", seqnum);
}
printf("\n");
udev_queue_unref(udev_queue);
return 0;
}