Skip to content

Release 3.3.0

Latest

Choose a tag to compare

@bbonev bbonev released this 03 Nov 10:06
v3.3.0
c440d95

eudev 3.3.0 (2026-11-03)

This release completes the sync with systemd-udev at systemd a446e8ff2c (v262+) that 3.2.15 started.
3.2.15 brought the hardware database, the rules, the builtins and the helper programs to that level;
this release does the same for udevd, udevadm, the rules engine and libudev. A few later systemd
changes are included too: hwdb data, the caller information of triggered uevents and the fixes of
systemd v262.1 that apply to eudev. The main additions are systemd's network interface naming
schemes and alternative interface names, sticky tags, the reworked device node symlink handling,
postponing the events of locked block devices, ID_PROCESSING, trace logging, udev.conf drop-ins,
systemd's log settings ($SYSTEMD_LOG_LEVEL and the like), the udevadm verify, cat, wait and
lock commands, the systemd-hwdb tool, the tpm2_id and factory_reset builtins and GPT root
partition discovery. The rules parser is now as strict as systemd's, so please read "Before
upgrading" first.

UDEV_VERSION is now 262. The libudev ABI is unchanged: no symbol was added or removed, and the
soname stays libudev.so.1. The library file becomes libudev.so.1.7.15, the same version as
systemd v262's libudev: 3.2.14 added udev_device_has_current_tag() and
udev_device_get_current_tags_list_entry() without raising the library version.

In the notes below, "(deviation: ...)" marks a place where eudev deliberately differs from
systemd a446e8ff2c.

The complete release notes are in NEWS.md, which is also in
the release tarball. This page has the introduction, "Changes that may need attention" and
"Before upgrading"; the sections that they refer to (udevd, Rules, udevadm, Logging,
Builtins and helper programs, hwdb, libudev, Build and configuration, Documentation and
Fixes) are in NEWS.md.

Changes that may need attention

The list names the changes that most often need an adjustment; "Before upgrading" and the sections
after it have the details.

  • Workers are named "(udev-worker)", the child that re-reads a partition table "(udev-synth)" and the
    processes of the programs that rules run "(spawn)", as in systemd: the messages of the workers and
    of that child in the kernel log and syslog are tagged "(udev-worker)" and "(udev-synth)" instead of
    "udevd", so log filters that match "udevd" miss them.
  • $<NAME>_PATH in udevd's environment, e.g. $SCSI_ID_PATH, replaces the program of the same name
    that a rule runs, as in systemd.
  • IMPORT{program}, IMPORT{file}, IMPORT{db}, IMPORT{cmdline}, IMPORT{parent} and hwdb
    entries no longer set the properties that ENV{} cannot set (DEVNAME, ACTION, DEVPATH,
    ...): e.g. IMPORT{db}=="DEVNAME" is now always false, and udevadm verify warns about such
    names and fails (deviation).
  • Device names, paths and the arguments of udevadm are escaped in messages (control characters and
    invalid UTF-8 as \xNN; deviation); the data that udevadm info, monitor and trigger --verbose print is unchanged.
  • udevadm trigger and info check their match options like systemd, and trigger, control,
    settle and monitor check all their options before they look for a chroot, so --help,
    --version and invalid options work in a chroot (deviation: systemd's trigger and control
    look for the chroot first).
  • --attr-match= and --attr-nomatch= of udevadm trigger and info take attribute paths
    relative to the device, also with .., e.g. queue/rotational (deviation).
  • udevadm test -D fails for a directory that does not exist; invalid tag names are refused by
    udevadm trigger/info --tag-match= and udev_enumerate_add_match_tag().
  • The release tarball no longer contains the generated rules/64-btrfs.rules and
    rule_generator/write_net_rules (GitHub #301), and make check does not change the timestamps
    of the build machine's device nodes.
  • New configure option --with-net-ifnames-default=yes|no for what happens without net.ifnames=
    on the kernel command line.
  • Builds with --enable-rule-generator no longer write rules for interfaces that the kernel did not
    name by enumeration, for the interfaces of more virtual machines, for User-mode Linux and mac80211
    monitor interfaces, or for an interface without a MAC address or bus id (also with
    INTERFACE_NAME), and udevd no longer moves an interface without a new name of its own to a
    temporary name such as eth126 (see "Rule generator").
  • /run/udev/queue is created once when events are queued and removed when the queue is empty, as in
    systemd: when something else removes it while events are queued, udevd does not create it again
    until the queue has emptied, so udevadm settle returns early then.
  • With the default hwdb_format=1, it is undefined which value a device gets when several hwdb
    records set the same key, as before; hwdb(7) now says so, and some entries, e.g. the new one for the
    lid keys of the HP Pavilion Gaming 15-dk0, work only with hwdb_format=2 in udev.conf (see "hwdb").
  • The library file is libudev.so.1.7.15 (the soname stays libudev.so.1).

Before upgrading

Packagers

  • Create the groups that the shipped rules use: audio, cdrom, clock, dialout, disk, input, kmem,
    kvm, lp, render, sgx, tape, tty and video. A GROUP= (or OWNER=) that names a group (or user)
    that does not exist is now ignored with an error such as "Failed to resolve group 'clock',
    ignoring: Unknown group" every time the rules are loaded, and the device node keeps its group; the
    rest of the rule line (MODE=, SYMLINK+=, ...) still applies. Before, eudev used root instead.

  • New clock group: rtc devices get GROUP="clock", MODE="0660" and ptp devices
    GROUP="clock", MODE="0664" (systemd af96ccfc24 and 1e6854e112), so that e.g. linuxptp can open
    them without root. Read access to /dev/ptp* for everybody relies on the PTP file mode checks of
    Linux b4e53b15c04e, which was backported to the maintained stable kernels. Nothing creates the
    clock group on systems without systemd-sysusers (e.g. Alpine, Gentoo, Void).

  • $(sbindir)/udevadm is gone: make install no longer creates the /sbin/udevadm (or
    /usr/sbin/udevadm) compatibility symlink that existed since 2013. Packages and scripts that call
    udevadm by that path must provide the link themselves.

  • The shipped hwdb files moved to $(udevlibexecdir)/hwdb.d (e.g. /usr/lib/udev/hwdb.d), as in
    systemd; $(sysconfdir)/udev/hwdb.d is left to the administrator, and its files override shipped
    files of the same name. Remove copies of the shipped hwdb files that an older package left in
    /etc/udev/hwdb.d
    , or they override the new data with stale data. hwdb.bin is still written to
    $(sysconfdir)/udev/hwdb.bin, which now really follows --sysconfdir (it was hard-coded to
    /etc/udev). Builds with --disable-programs install no hwdb files and no man pages. The hwdb
    README is installed next to the hwdb files, like in systemd.

  • udev.h is no longer installed; libudev.h is the only public header. udev.h was an internal
    header that included private ones. udev_device_read_db() is removed from libudev.h; it was never
    exported, so no program could have linked against it.

  • UDEV_VERSION is 262 in udevadm --version, udevd --version, the helpers' --version,
    udev.pc and libudev.pc (3.2.15 still reported 251). It says that the rules syntax, the hwdb, the
    builtins, the helpers and the libudev API match systemd-udev v262. It does not mean feature parity:
    eudev has, for example, no Varlink interface, no sd-device API, no CONST{} key, no
    dissect_image, net_setup_link or uaccess builtin and no udevadm control --load-credentials.
    Scripts should test for a feature (e.g. udevadm lock --help) rather than compare numbers.
    Distributions that hard-code the number must update it (Void: _UDEV_VERSION="251"). The package
    version is shown next to it, e.g. in udevd's start line "starting eudev-3.3.0 (udev 262)".

  • New configure options that packagers may want to set: --with-nobody-group= (Debian and Devuan:
    nogroup), --with-system-uid-max=/--with-system-gid-max=, --with-foreign-uid-base=,
    --with-net-ifnames-default= (see "Network interface names"), --disable-tpm2,
    --disable-systemd-hwdb (systemd-hwdb is installed by default and clashes with systemd's own
    /usr/bin/systemd-hwdb where both packages can be installed), --with-bashcompletiondir=,
    --with-zshcompletiondir= and --enable-smack. See "Build and configuration".

  • udev.conf is looked up like in systemd: the main file is the first udev.conf found in
    $(sysconfdir)/udev, /run/udev, /usr/local/lib/udev and $(udevlibexecdir) (with split-usr also
    /lib/udev and /usr/lib/udev), and udev.conf.d/*.conf drop-ins in the same directories are read
    after it (see "udevd"). An empty file or a symlink to /dev/null masks a file of the same name in
    the later directories. The shipped udev.conf lists the new keys as comments and writes
    #udev_log=info without quotes, so packages that patch it must be adjusted.

  • Release tarball and make check: the tarball no longer contains the generated
    rules/64-btrfs.rules and rule_generator/write_net_rules, only their .in sources, from which
    the build makes them for its own directories (GitHub #301); rule_generator.functions is made the
    same way, for --with-rootrundir=. The installed files do not change, apart from the paths in the
    rule generator's scripts (see "Fixes"). The tarball now also contains autogen.sh, man/make.sh,
    man/custom-man.xsl, BUILD, CONTRIBUTING, LEGACY and NOTES. The new tests of make check
    that use the build machine's /dev/null, /dev/zero, /dev/full, /dev/random, /dev/urandom,
    /dev/tty and /dev/ptmx bind them read-only, so they do not change their timestamps, also when
    they run as root (see "Build and configuration").

Init scripts and service supervisors

  • udevd keeps its standard streams in the foreground: it connects /dev/null to stdin, stdout
    and stderr only when it daemonizes and does not log at debug level (systemd 40e749b59b). Before, it
    did so unless --debug was given, also without --daemon.
  • Log level precedence of udevd is now, from lowest to highest: the default, systemd.log_level=
    on the kernel command line, $UDEV_LOG_LEVEL/$SYSTEMD_LOG_LEVEL, udev_log= in udev.conf, udevd
    options (--debug), udev.log_level= on the kernel command line, udevadm control, as in systemd;
    udevd(8) documents it. Before, --debug overrode udev.log-priority= on the kernel command line.
    --debug still logs to the console, now from the start (see "udevd").
  • Kernel command line: udevd accepts systemd's spellings udev.log_level= and
    udev.log_priority=, and - and _ are equivalent in all its options (udev.children_max= is
    udev.children-max=); the old spellings keep working. rd.* options are honoured only in an initrd
    (see "Rules"). Unknown udev.* options, and unknown rd.udev.* options in an initrd, are ignored
    with systemd's warning "Unknown udev kernel command line option "…", ignoring." (before, without a
    message).
  • systemd's log settings: udevd, udevadm and the helper programs read $SYSTEMD_LOG_LEVEL,
    $SYSTEMD_LOG_TARGET, $SYSTEMD_LOG_COLOR, $SYSTEMD_LOG_LOCATION, $SYSTEMD_LOG_TIME,
    $SYSTEMD_LOG_TID and $SYSTEMD_LOG_RATELIMIT_KMSG (each $UDEV_LOG_* variant first). Processes
    without a controlling terminal (udevd, the programs it runs, the commands of init scripts) also read
    systemd.log_level=, systemd.log_target=, systemd.log_color, systemd.log_location,
    systemd.log_time, systemd.log_tid and systemd.log_ratelimit_kmsg from the kernel command line;
    the environment overrides them. A bare debug on the kernel command line counts as
    systemd.log_level=debug, as in systemd (rd.debug only in an initrd). So
    systemd.log_level=debug or debug on the kernel command line now also makes
    udevadm trigger/settle in init scripts and every helper verbose on the boot console, and every
    helper run reads /proc/cmdline. Warnings about invalid log settings in the environment or on the
    kernel command line go to stderr, as in systemd. Like in systemd, a process closes the console once
    it logs to another target, e.g. /dev/kmsg. With systemd.log_target=console, a udevd started with
    --daemon writes to /dev/console, or to /dev/kmsg when that cannot be opened, e.g. in a
    container (deviation: systemd writes to /dev/null then). Messages to /dev/kmsg are limited to
    200 per 5 seconds per process, as in systemd; messages dropped by the limit are not written anywhere
    else.
  • Changed log messages: log monitors and scripts that match udevd's messages must be adjusted. The
    slow and timeout messages are now systemd's: "Spawned process '…' [PID] is taking longer than … to
    complete.", "Spawned process '…' [PID] timed out after …, killing.", "Worker [PID] processing
    SEQNUM=N is taking a long time." and "Worker [PID] processing SEQNUM=N killed." (before: "slow:",
    "timeout: killing", "timeout; kill it", "seq N '…' killed"); "Delaying execution of "…" for …." was
    "delay execution of"; socket failures read "Failed to initialize udev control socket: …", "Failed to
    initialize device monitor: …", "Failed to bind udev control socket: …" and "Failed to listen udev
    control socket: …". The messages of the workers and of the child that re-reads a partition table are
    tagged "(udev-worker)" and "(udev-synth)" (see below). Programs are reported like in systemd:
    "Process '…' succeeded." at debug level (was "'…' [PID] exit with return code 0"), "Process '…'
    failed with exit code N." as a warning for RUN and at debug level for PROGRAM and
    IMPORT{program} (was "exit with return code N" at debug level), "Process '…' terminated by signal
    NAME." (was "terminated by signal N (Name)"), and a program that cannot be found or is not
    executable is reported before it is started: "Failed to find and pin callout binary "…": …" (was
    "failed to execute '…' '…': …"). Device names, paths and other values in messages are escaped (see
    "Logging"). See "udevd" and "Logging".
  • Process names: workers rename themselves "(udev-worker)", the child that re-reads a partition
    table "(udev-synth)" and the processes of the programs that rules run "(spawn)", as in systemd, so
    they show under these names in ps, and the kernel log and syslog tag the messages of the workers
    "(udev-worker)" and those of the re-read child "(udev-synth)" instead of "udevd" (udevd's own
    messages keep its name): log filters that match "udevd" miss the messages of the workers and of
    the re-read child.
    The option errors of the builtins start with "(udev-worker):", and the uevents
    that a worker triggers carry SYNTH_ARG_Comm=udevworker (see "Caller information in triggered
    uevents"). The names are complete whatever the name udevd was started with.
  • $<NAME>_PATH: like systemd-udevd, udevd honours $<NAME>_PATH from its environment, the file
    name of a program in upper case with - replaced by _ and _PATH appended, e.g. $SCSI_ID_PATH
    for scsi_id: it names another file to run instead of that RUN, PROGRAM or IMPORT{program}
    program. Init scripts and supervisors that pass their environment to udevd should not set such
    variables by accident.

Local rules

Run udevadm verify on local rules after upgrading: the parser now follows systemd, and lines that
older versions accepted silently may be dropped with an error.

  • Rules parser strictness:

    • key names must be exact (NAMEX is not NAME, TESTX is not TEST);
    • a key that takes no attribute must not have one ("Invalid attribute for X."), and ATTR, ATTRS,
      ENV, IMPORT, SECLABEL and SYSCTL need a non-empty one; an attribute is everything up to
      }, so ATTR{a b} works, and text between } and the operator is an error;
    • == and != are errors for RUN, OWNER, GROUP, MODE and SECLABEL; LABEL and GOTO
      take only =; OPTIONS refuses ==, != and -=, and += acts as =;
    • a syntax error anywhere in a line drops the whole line (before, the keys parsed so far were kept);
      the message is "Invalid key/value pair, ignoring." without a character position; a line of a
      single character is such an error; a trailing comma is accepted;
    • an unknown builtin, an unknown IMPORT{} or RUN{} type, or IMPORT without a type drops the
      line (before, only that key); in builds without blkid or kmod, lines using them are dropped as in
      systemd; in --disable-tpm2 builds IMPORT{builtin}, IMPORT{program}, RUN{builtin},
      RUN{program} and PROGRAM naming tpm2_id are dropped with "... is not supported by this build
      of eudev (configured with --disable-tpm2), ignoring the rule.";
    • lines using what only systemd supports are dropped with a message saying so: the CONST{} key and
      the dissect_image, net_setup_link and uaccess builtins;
    • OPTIONS values must be exact known names (nowatch no longer also means watch); unknown
      values get a warning; an invalid link_priority= or log_level= value drops the line, and
      link_priority= takes systemd's number formats (before, "10x" meant 10 and "abc" meant 0);
      several values separated by commas in one OPTIONS key are still accepted (deviation: systemd
      takes one value per key; no systemd rule is affected);
    • OWNER/GROUP: an empty or invalid value (leading zeros, leading whitespace or a minus sign in
      a number, 65535, 4294967295, values that are too large) is an error and only that key is ignored,
      also with OPTIONS+="static_node=..." (deviation: systemd drops the line; before, eudev used
      root); udevadm verify reports a line that is left without effect by it; a value starting with
      + is looked up as a name; a numeric ID without an account is accepted only up to the highest
      system ID, for 0, for 65534 ("nobody") and in systemd's foreign range, otherwise it counts as an
      unknown user or group; see also "Packagers" above;
    • MODE: an invalid mode without substitutions (empty, 0999, a sign, more than 07777, also with
      MODE:=) is an error when the rules are read, and only that key is ignored (deviation: systemd
      keeps the key, which then fails at every event); a mode produced by a substitution that is invalid
      is logged and leaves the mode unchanged (before, an empty result set mode 0000);
    • NAME="%k" and NAME="" drop the line, with systemd's message;
    • TEST{mode} with an invalid mode drops the line (before, it meant "no mode"), and so does
      TEST{0} ("Mode mask of TEST must not be zero, ignoring.") (deviation: in systemd TEST{0} never
      matches); a mask matches when any of its bits is set, as documented now;
    • WAIT_FOR and WAIT_FOR_SYSFS are removed (systemd f2b8052fb6) and now drop the line as unknown
      keys;
    • only the first GOTO of a line counts; later ones get "Contains multiple GOTO keys, ignoring
      GOTO=..." (before, a later GOTO could jump);
    • like in systemd, a line that cannot do anything for the event is not evaluated at all: one without
      an assignment, GOTO, PROGRAM or IMPORT, or whose only assignments are NAME on a device
      that is not a network interface, or SYMLINK, OWNER, GROUP or MODE on a device without a
      node or on a remove event; so a failing TEST or SYSCTL{} in such a line no longer stops the
      event, and udevadm test does not show it; a line whose GOTO has no LABEL and that has
      nothing else to do is dropped with "The line has no effect any more, dropping.";
    • a rule may have any number of keys (before, more than 64 keys crashed udevd).
  • Line continuation is read like systemd: the leading whitespace of continuation lines is
    removed; comment lines inside a continued rule are skipped (before, they ended the rule, and the
    remaining lines became a separate rule without the earlier matches); a rule continued at the end of
    the file is ignored with "Unexpected EOF after line continuation, line ignored."; CRLF files work.
    A physical line longer than 16383 characters drops only that rule, with an error naming the line
    (deviation: systemd ignores the whole file).

  • Program arguments: RUN, PROGRAM and IMPORT{program} command lines are split like systemd
    does, with the same splitter as the builtins: any whitespace (also tabs) separates arguments;
    single and double quotes group characters anywhere in a word and are removed (a"b c"d is one
    argument ab cd, and 'a b'c is a bc; before: a b and c); a missing closing quote is
    accepted; backslashes are not special; a command given as "prog" runs prog. Builtin commands
    also skip leading whitespace and are no longer cut at 1024 bytes. None of the rules shipped by
    eudev, systemd, Devuan or Debian are affected; local rules that pass quotes inside a word, or tabs,
    must be adjusted.

  • Rules directory precedence is now /etc/udev/rules.d > /run/udev/rules.d >
    /usr/local/lib/udev/rules.d > /usr/lib/udev/rules.d (and /lib/udev/rules.d), as in systemd.
    Before, /lib came before /run, and /usr/local came last. Each directory is read once, also
    when it has several paths, e.g. /lib and /usr/lib on a merged /usr, or a bind mount. A rules
    file that is not a regular file (a dangling symlink, a symlink to a directory, a FIFO) no longer
    hides the file of the same name in a lower-priority directory; this holds for udevd, udevadm test,
    cat and verify.

  • Properties are validated wherever they come from (kernel events, rules, IMPORT, builtins, the
    database), as in systemd: names may contain only ASCII letters, digits, _ and .; values must be
    valid UTF-8 without control characters. Invalid ones are ignored with a warning (rules) or a debug
    message, and processing continues; so programs or IMPORT{cmdline} keys that use e.g. - in
    property names lose those properties. An empty value removes the property, also from builtins
    (e.g. an empty USB serial) and IMPORT (A="").

  • Errors stop the event as in systemd: an IMPORT{file} that exists but cannot be opened, an
    unreadable kernel command line, an unreadable SYSCTL{} (other than a missing one), an unreadable
    directory for a * in TEST, failing to remove a property (e.g. ENV{a-b}=""), and running out of
    memory (e.g. "Failed to clone sd_device object: …" for the copy of the device with its database
    entry, before which eudev went on without that copy). No further rules are applied, the node,
    symlinks and database are not updated, RUN is not executed, and the event is still sent to
    listeners. A missing IMPORT{file} or a failing program is still only a false key. A database or
    tag index that cannot be written also fails the event (see "udevd").

  • IMPORT matches like systemd: IMPORT{parent} is true only when at least one property was
    imported, and IMPORT{...}!= now matches only when the import failed (before, also when it
    succeeded). IMPORT{db} also finds the properties of the event itself, e.g. PRODUCT of a USB
    device whose database entry does not exist yet; when both have the property, the database value is
    imported, as in systemd.

  • rd.* options in IMPORT{cmdline} are honoured only in an initrd again, as in systemd: since
    3.2.15, IMPORT{cmdline}="rd.foo" also matched on the real root. The initrd detection also
    recognises initrds without /etc/initrd-release (e.g. initramfs-tools), see "udevd".

  • Substitutions that do not fit make a match fail (debug message) and an assignment be skipped
    (warning), instead of using a cut value. Invalid substitutions ($attr/$env without {...}, a
    missing }) stop the substitution with a warning. In $result{N}/%c{N} the parts are separated
    by any whitespace, leading whitespace is skipped, and a non-numeric N is an error, also in
    udevadm verify.

  • Private properties (names starting with .) are now passed to RUN, PROGRAM and
    IMPORT{program} programs and to udev monitor listeners, as systemd has done since v220; they are
    still not stored in the database. /bin/sh (dash) drops such variables from the environment of
    the commands it runs.

  • OWNER/GROUP resolved at event time (from substitutions) that cannot be resolved are logged
    and leave the owner or group unchanged (before, root). With OPTIONS+="static_node=..." an unknown
    user or group drops only that key, too.

  • /etc/scsi_id.config is read with systemd's parser: a line that cannot be parsed, e.g. with
    options= and nothing after it, or one that is too long, makes scsi_id fail (exit status 1, no
    output) with an error naming the file and the line, e.g. "Error parsing config file
    /etc/scsi_id.config line 3 '…'" (deviation: systemd does not name the file), also when the line
    comes after the global options= line and is only found while the options of the device are looked
    up, and so does an option that a vendor= line may not set (see "scsi_id" under "Builtins and
    helper programs"); an unterminated quote is accepted; options are split on runs of spaces and tabs;
    the last line needs no newline; a NUL or CR ends a line. White space after a comma
    (vendor="ATA", options=-g) is still accepted, as in 3.2.x (deviation: systemd refuses it).

  • Protected properties in IMPORT and hwdb: IMPORT{program}, IMPORT{file}, IMPORT{db},
    IMPORT{cmdline}, IMPORT{parent} and hwdb entries no longer set the properties that ENV{}
    cannot set: ACTION, SEQNUM, SYNTH_UUID, SYNTH_ARG_*, DEVPATH, DEVPATH_OLD, SUBSYSTEM,
    DEVTYPE, DRIVER, MODALIAS, DEVNAME, DEVMODE, DEVUID, DEVGID, MAJOR, MINOR,
    DISKSEQ, PARTN, IFINDEX, INTERFACE, INTERFACE_OLD, DEVLINKS, TAGS, CURRENT_TAGS,
    USEC_INITIALIZED and UDEV_DATABASE_VERSION. They are skipped with a debug message ("Property "…"
    cannot be set, ignoring."; IMPORT{parent} skips them silently). Before, a program or file could
    make udevd send the event as another device, action or sequence number, and the database kept such
    values. So IMPORT{db}=="DEVNAME" and IMPORT{cmdline} with such a name are always false now, and
    IMPORT{parent} is false for a parent that has only such properties. Loading the rules and
    udevadm verify warn about such a name in IMPORT{db} or IMPORT{cmdline} ("IMPORT{db} property
    'DEVNAME' cannot be set, it is never imported."), the line is kept, and udevadm verify fails
    (deviation: systemd a446e8ff2c imports these properties). udev(7) and hwdb(7) document it, and
    udev(7) now says that IMPORT{parent} imports all properties of the parent, from its uevent and its
    database.

  • IMPORT{program} output is split into lines like systemd: a line ends at every \n or \r,
    and backslashes and quotes are not special; only spaces, tabs and line breaks are removed around
    keys and values, also in IMPORT{file} (before, lines ended only at \n, and any white space, also
    \v and \f, was removed); of output that does not fit, everything after the last line break is
    dropped.

Network interface names

  • Naming schemes: the net_id builtin implements systemd's network interface naming schemes,
    v238 to v261, plus eudev, which generates exactly the names of eudev 3.2.15 and older, and
    latest, the default, which is v261 (systemd a446e8ff2c defines no v262 scheme). The scheme is
    exported as ID_NET_NAMING_SCHEME. With the default, interfaces that 80-net-name-slot.rules
    renames get different names than with 3.2.15
    , for example:

    • virtio-net NICs: ens<N>/enp0s<N> instead of enx<MAC>;
    • NICs that report a port name (e.g. Intel X710/X722/E810, Mellanox mlx5, Broadcom bnxt): an
      np<port> suffix, e.g. enp59s0f0np0, eno1np0;
    • SR-IOV virtual functions are named after their PF (eno1v0), representors get ...r<vf>;
    • firmware onboard indexes above 65535 (e.g. VMware's eno16777736) are ignored, so such NICs get
      slot or path names; an acpi_index of 0 gives eno0;
    • servers with ACPI _SUN get ens<slot>[f<fn>]; NPAR/ARI functions use the full function number;
    • IBM Z: enc<busid> without leading zeros instead of enccw0.0.xxxx, virtio-ccw gets
      enc<busid>, PCI slots are named by function_id;
    • newly named: IBM PowerVM VIO (env<N>), ARM64 ACPI platform NICs (ena...), DeviceTree boards
      (end<N>, WLAN wld<N>), USB NICs on SoC host controllers (enu...), InfiniBand (ib...),
      netdevsim, Xen netfront (enX<N>), MCTP (mc...) and mlx5 sub-functions (...S<sfnum>);
    • Azure MANA NICs drop the PCI domain; the hwdb properties ID_NET_NAME_ALLOW* and
      ID_NET_NAME_INCLUDE_DOMAIN are honoured.
  • Selecting the scheme: net.naming_scheme= on the kernel command line, the new
    net_naming_scheme= key in udev.conf, or $NET_NAMING_SCHEME, which takes precedence unless it
    starts with :, in which case it is used only when neither of the others selects a scheme. To keep
    the names of 3.2.15, set net_naming_scheme=eudev in udev.conf. An unknown name is skipped with
    systemd's warning "Unknown interface naming scheme '…' requested, ignoring." and the next setting in
    that order is used (deviation: systemd uses the default, so a typo on the kernel command line would
    override the scheme pinned in udev.conf).

  • Renaming policy of 80-net-name-slot.rules, for every scheme (the eudev scheme pins only the
    names net_id generates):

    • the name from the hwdb (ID_NET_NAME_FROM_DATABASE) comes first, as in systemd; today this
      renames only the Dell iDRAC virtual USB NIC, to idrac;
    • interfaces named by userspace, already renamed (e.g. in the initrd), or named predictably by the
      kernel (name_assign_type 2, 3 or 4) are not renamed, like systemd's "keep" and "kernel"
      policies, also on udevadm trigger --action=add; NAME= in earlier rules still applies;
    • a candidate that is not a valid interface name (systemd's ifname_valid(): longer than 15
      characters, only digits, ., .., all, default, :, /, %, whitespace or non-ASCII, or an
      interface index in another notation such as 0x10, +5, 0b1, 0o7) is skipped and the next
      one is used; a MAC based name of an all-zero address is never used. The rules file, like
      80-net-altnames.rules, is generated by tools/gen-net-naming-rules.py (with --help).
  • NAME= on network interfaces: a name longer than 15 characters is refused with an error
    (before, it was silently cut). With schemes v249 and later, invalid characters are replaced by _
    and a name that is still invalid is ignored with "Invalid network interface name, ignoring"; with
    older schemes, including eudev, such a rename fails. NAME= on any other device is ignored with
    "Only network interfaces can be renamed, ignoring." (see "Rules").

  • Alternative interface names: on Linux 5.5 and later, udevd assigns the predictable names
    (ID_NET_NAME_FROM_DATABASE, _ONBOARD, _SLOT, _PATH, _MAC, also those longer than 15
    characters) as alternative names to new interfaces, like systemd's default
    AlternativeNamesPolicy=; ip link shows them and accepts them instead of the interface name. This
    is done by OPTIONS+="net_altnames" in the new 80-net-altnames.rules, installed also with
    --enable-rule-generator, independent of net.ifnames=, unless eudev is built with
    --with-net-ifnames-default=no (see below). net_altnames=no in udev.conf disables it; an invalid
    or empty value is reported, with the value, by udevd and udevadm test. Existing interfaces get
    them at their next add event.

  • Default of net.ifnames=: the new configure option --with-net-ifnames-default=yes|no sets
    what happens without net.ifnames= on the kernel command line. With yes, the default, nothing
    changes: 80-net-name-slot.rules renames interfaces unless the kernel command line has
    net.ifnames=0, and alternative names are assigned either way. With no, interfaces keep the names
    the kernel gave them and get no alternative names unless the kernel command line has
    net.ifnames=1. The installed 80-net-name-slot.rules and 80-net-altnames.rules, the configure
    summary ("net.ifnames default:") and udev(7), udevd(8) and udev.conf(5) follow the option. With
    --enable-rule-generator and yes, the rule generator replaces 80-net-name-slot.rules, which is
    not installed, and net.ifnames= changes nothing; with no, 80-net-name-slot.rules is installed
    too, and with net.ifnames=1 the rule generator records no names (see "Rule generator" below).

  • Rule generator (--enable-rule-generator), with parts of Debian's generator, which Devuan
    ships: 75-persistent-net-generator.rules records only interfaces that the kernel named by
    enumeration (name_assign_type 1, or unreadable as before Linux 3.17), not those of more virtual
    machines (VirtualBox, Xen by its MAC prefix, more VMware prefixes, Microsoft Azure, Ravello, old
    libvirt, Parallels, Oracle VM, Nutanix AHV, bhyve and Proxmox VE, as before for KVM, VMware and
    Hyper-V; the Azure prefixes are Microsoft's, so Microsoft hardware gets no rule either), of
    User-mode Linux and mac80211 monitor interfaces; dev_id is matched for every MAC address, not for
    S/390 devices; the comment of a rule names the driver of the nearest device that has one, e.g.
    "(r8152)" instead of "(usb)". write_net_rules writes no rule for an interface without a MAC
    address or bus id, also with INTERFACE_NAME (before, a rule that matched every interface of its
    type; an interface of KVM, VMware or Hyper-V now gets the warning below too when it has
    INTERFACE_NAME, where its rules ended silently before), and logs "no MAC address or bus id to
    match … with, writing no rule" and "Cannot lock …!" also to the kernel log, as warnings, when
    /dev/kmsg can be written. udevd renames an interface to a temporary name only when it gets a new
    name of its own: one without, e.g. of a virtual machine, keeps its name also when a stale rule
    gives that name to another interface (before, it became e.g. eth126); the other interface then
    keeps its own name; it waits for the name, up to 90 s but not beyond the event timeout, only while
    udevd has not handled the add event of the interface that has it (the handled ones are recorded in
    /run/udev/net-added/). udev(7) describes the generator in a new section.

Persistent storage links

  • by-path names change on these systems; update fstab, crypttab, LVM filters and scripts that use
    them:
    • Hyper-V and Azure guests (VMBus as an ACPI device): SCSI disks and DVDs are named after the
      controller's instance GUID and the LUN, e.g. acpi-VMBUS:00-scsi-0:0:0:0 becomes
      acpi-VMBUS:00-vmbus-f8b3781b1e824818a1c363d806ec15bb-lun-0; before, LUNs on different
      controllers could share a name;
    • IBM Z: zfcp LUNs change from ccw-<dev>-zfcp-<WWPN>:<LUN> to ccw-<dev>-fc-<WWPN>-lun-<LUN>;
      SCSI devices below a CCW device (e.g. virtio-scsi) gain the SCSI address (ccw-0.0.0005 becomes
      ccw-0.0.0005-scsi-0:0:0:0); qeth, AP and IUCV devices get ccwgroup-..., ap-... and
      iucv-... names; DASD and virtio-blk paths do not change;
    • devices behind an SPI controller get the chip select appended, e.g. platform-fe204000.spi
      becomes platform-fe204000.spi-cs-1 (for ACPI-enumerated devices the ACPI instance number, e.g.
      -cs-00);
    • devices whose sysfs strings contain control characters or invalid UTF-8 get no ID_PATH instead
      of one with escape sequences in it;
    • the default SCSI path (...-scsi-H:B:T:L) prints numbers that do not fit a signed int as
      negative numbers, as systemd does (e.g. LUN 4294967295 gives scsi-0:0:0:-1);
    • SCSI LUNs of 2^32 and more (third and fourth addressing level) get their full 8-byte LUN,
      lun-0x<16 hex digits>, one link per LUN; 3.2.15 named them after their lower 32 bits (on 32-bit
      systems all of them lun-0xffffffff00000000), so different LUNs could share a link (deviation:
      systemd a446e8ff2c names them ...(null));
    • the old ...-ata-<port> link and the ID_PATH_ATA_COMPAT property are given only to the device
      that owns the name alone: the one on bus 0, target 0 of the ATA port (a single SATA disk, a PATA
      master, the disk on port 0 of a port multiplier) or the only device of the port (a lone PATA or
      ATAPI slave, a lone disk behind a port multiplier); a slave next to a master and port-multiplier
      disks next to others no longer get it, so the link no longer points to whichever came last
      (deviation: systemd sets the property for every device). When the bus 0/target 0 device appears
      after a lone device of its port got the link, that device and its partitions get a change event
      and drop it (this can add change events during coldplug); when any device of the port is
      removed (also a PATA slave or a disk behind a port multiplier) and one device is left on the
      port, that device and its partitions get a change event and take the link, unless the device
      has it already (a new rule in 60-persistent-storage.rules runs path_id for the remove event
      of every SCSI target).
  • by-id links: partitions of host-managed zoned (ZAC) ATA disks get their
    scsi-<serial>-part<N> links again (deviation: systemd creates the link only for the whole disk).
    Tapes whose serial number contains C1 control characters get their tape/by-id/scsi-* links back,
    and a serial with a TAB is now refused, exactly as in systemd.

Scripts that call udevadm

  • udevadm test no longer changes the system (systemd 089bef6631 and follow-ups): it renames no
    interface, creates or changes no device node, permission, label or symlink, writes no database,
    tag, ATTR{} or SYSCTL{} value, loads no module, remaps no keys and issues no btrfs ioctl (as
    udevadm test-builtin already did; the rules test runner of make check now also skips the
    keyboard and btrfs builtins). It runs no program except the helpers
    ata_id, cdrom_id, dmi_memory_id, fido_id, mtd_probe and scsi_id, given by exact name or
    by their path in udev's helper directory (deviation: systemd matches any command line that starts
    with a helper name). cdrom_id gets the new --dry-run option there and does not lock or eject the
    media (deviation). So properties from other programs are missing, PROGRAM results are empty, and
    RESULT==, %c and PROGRAM!= behave accordingly; a renamed interface keeps its INTERFACE=. A
    value that ATTR{}= would write is seen by a later ATTR{}==.

  • udevadm test output format: stdout carries only a result block of labelled sections (device,
    node owner/group/mode, symlinks, the would-be interface name and alternative names, ATTR/SYSCTL
    writes, tags, current tags, indented properties, queued commands as RUN{program} : ...); the
    banner and log messages go to stderr. Scripts that grep ^KEY= or run: break; use the new
    --json=.

  • chroot: udevadm trigger, control, settle and monitor do nothing in a chroot: they log
    "Running in chroot, ignoring request." and exit 0 (systemd c494b739a4). It counts as a chroot when
    the root directory is not the one of PID 1, or /proc is not mounted; $UDEV_IN_CHROOT, then
    $SYSTEMD_IN_CHROOT, override the detection, and the deprecated $SYSTEMD_IGNORE_CHROOT disables
    it. All four check their options, the option values and, for trigger, the match options first, so
    --help and --version work in a chroot, and invalid options, values and matches fail there as
    elsewhere; only a valid request is ignored (deviation: systemd's trigger and control look for a
    chroot before they parse their options; its settle and monitor parse them first, like eudev).
    Maintainer scripts that run in debootstrap or sbuild chroots and call these commands now do nothing.

  • Exit statuses: udevadm trigger exits 1 when writing a uevent fails (it always exited 0), for
    an invalid --action= ("Invalid action 'x'."; was "unknown action" and 2) and for a device that
    cannot be found ("Failed to open the device '…': No such device"; was "unable to open the device"
    and 2); udevadm test exits 1 on every error (was 2 to 5); udevadm test-builtin prints only the
    error and exits 1 (was the help text and 2 to 6); udevadm control exits 1 on every failure and
    stops at the first failing command (was 2, or the status of the last command); udevadm info with a
    missing or invalid device exits 1 ('Unknown device "…": No such device'; was 2); udevadm settle as
    root exits 1 when the control socket cannot be created (was 0); invalid --timeout= values of
    control and settle exit 1; udevadm without a command or with an unknown one exits 1 (was 2);
    udevadm version with arguments exits 1 ("Too many arguments."; was 0); udevadm info exits 1 for
    a query or -a without a device ("A device name or path is required"; was the help text and 2, or
    4), for --query=name on a device without a node ("No device node found: No such file or
    directory"; was 5) and for an unknown query type ("Unknown query type 'x'"; was 3); udevadm settle
    as root exits 1 when udevd cannot be pinged for another reason than not running, e.g. when it does
    not answer within the ping timeout of at least 5 s ("Failed to connect to udev daemon: …", "Failed
    to wait for daemon to reply: …"; was 0); udevadm trigger with an unknown --type= ("Unknown type
    --type=x"; was 2) or an invalid match option (see "Match options" below) exits 1; udevadm test -D
    exits 1 for a directory that does not exist ("Failed to access '…': No such file or directory") or a
    file ("'…' is not a directory."); udevadm monitor exits 1 when a filter cannot be applied.

  • Option parsing: invalid options are reported with systemd's texts, through the log at error
    level: "udevadm: unrecognized option '-Z'", "option '-n' requires an argument", "option '--hel'
    doesn't allow an argument" and "option '--exp' is ambiguous; possibilities: --export,
    --export-prefix, --export-db" (before, glibc's texts with the command name). An abbreviation shared
    by an option and its alias is ambiguous now, as in systemd and in musl builds:
    udevadm control --log… (--log-level/--log-priority) and --relo…
    (--reload/--reload-rules) must be spelled out. Without a command, udevadm says "Command verb
    required (one of help, info, …).", and for an unknown one "Unknown command verb 'x', did you mean
    'y'?". systemd-hwdb, udevd and the helper programs (ata_id, cdrom_id, collect,
    dmi_memory_id, fido_id, mtd_probe, scsi_id, v4l_id) report invalid options with systemd's
    texts too, through the log at error level (e.g. "ata_id: unrecognized option '-Z'"; before, glibc's
    texts on stderr). So do the blkid and hwdb builtins, whose IMPORT{builtin} then fails (before,
    invalid options were skipped silently). The helpers print their help in systemd's layout; ata_id
    and mtd_probe take exactly one device ("Need exactly one DEVICE argument."; ata_id accepted
    more), and dmi_memory_id no argument ("This program takes no arguments.").

  • Property order: properties are listed in the order they were added, which is systemd's order,
    instead of sorted by name: in udevadm info, its JSON output, program environments, monitor
    messages and udev_device_get_properties_list_entry(); the database E: lines are not sorted
    either, but their order can differ from systemd's. The values of DEVLINKS and TAGS stay sorted
    (deviation: systemd uses hash order, which changes between runs).

  • Device arguments of info, test, test-builtin, trigger and wait are resolved like
    systemd: a device ID (e.g. b8:0, n1, +net:lo), then the path (also relative, or through a
    symlink), then the path below /dev or /sys; a path outside /dev and /sys is an unknown
    device, and for info, test, test-builtin and trigger a path that is not a device gives "No
    such device", also e.g. /dev/null/x, as in systemd (wait warns about it, see "udevadm");
    /dev/block/MAJ:MIN and /dev/char/MAJ:MIN are looked up by the number, so they work without the
    symlinks. Several --parent-match=/--name-match= of trigger now all count (before, only the
    last).

  • Match options: udevadm trigger checks --attr-match=, --attr-nomatch=, --property-match=
    and --tag-match= like udevadm info --export-db and systemd: an invalid attribute or property
    name ("a//b is not a valid key name"), a --property-match= without = ("Missing '=' in key/value
    pair FOO.") and an invalid tag name ("Failed to add tag match '../data': Invalid argument") fail
    with exit status 1 (before, they were accepted, and arguments were cut at 4096 bytes), also in a
    chroot (see above); udevadm info checks them also without --export-db ("'../data' is not a valid
    tag name"). Both take attribute paths relative to the device, e.g. queue/rotational or ../size,
    and refuse an empty name, a leading /, empty components and . components (deviation: systemd
    refuses every name with a /). A tag name may contain only letters, digits, - and _ and be at
    most 255 bytes long, also in udev_enumerate_add_match_tag() (EINVAL); before, e.g.
    --tag-match=../data listed every device with a database entry.

  • Terminal output: colors need both stdout and stderr on a terminal and $TERM set, and use
    256/24-bit colors ($SYSTEMD_COLORS); log messages to a terminal are colored by level by default
    (see "Logging"); udevadm info output and all help texts go through a pager on a terminal
    (--no-pager, $SYSTEMD_PAGER). See "udevadm".

libudev users

  • Several public functions changed behaviour, as in systemd: property order and validation, sticky
    tags, syspath and attribute checks, device lookups and their error codes, parent devices, the device
    IDs of drivers and of devices without a subsystem, the devices that enumerations list (e.g. the
    /sys/bus/*/drivers directories, each device only under its own subsystem), the tag names that
    udev_enumerate_add_match_tag() accepts, devices received from a monitor keeping the values of
    their event, the errno of udev_hwdb_new(), and udev.conf being read once per process. See
    "libudev" for the list.

Since 3.2.14: changes that 3.2.15 made without a release note

  • SYMLINK= values are checked and normalized: a value with a .. component or an absolute path
    outside /dev/ is refused; a /dev/ prefix is accepted; a trailing slash is removed, so a value
    that substitutes to foo/ creates /dev/foo (in 3.2.14 that made the worker loop until the event
    timeout). New in this release: redundant slashes and . components are removed instead of refused
    (a//b creates /dev/a/b), components longer than NAME_MAX and control characters are refused (the
    latter unless OPTIONS+="string_escape=none"), invalid values are warnings, and devlinks read from
    the database are checked and normalized the same way.
  • Tags may contain only letters, digits, - and _. Invalid tags recorded in the database by
    3.2.14 are ignored when it is read, so they vanish from the device (udevadm info, TAGS); their
    /run/udev/tags/<tag>/ entries stay until reboot, so udevadm trigger --tag-match=<tag> still
    finds those devices.
  • TAG="" only clears the tags; an empty tag cannot be added. With sticky tags (see "Rules") it
    clears the current tags only.
  • Protected properties: an ENV{} assignment to a property owned by the kernel or udevd
    (DEVPATH, SEQNUM, MODALIAS, ...) drops the whole rule line, not only that key.
  • Host-managed zoned (ZAC) SATA drives on non-libata transports (mpt3sas, megaraid, smartpqi,
    USB SAT) are identified by ata_id: their ID_SERIAL, ID_BUS and ID_MODEL change, and they get
    disk/by-id/ata-* links. The whole-disk scsi-* link is kept by a rule, which also gives libata
    ZAC disks an additional scsi-* link; their partitions get their scsi-*-part<N> links again with
    this release (deviation: systemd's rule covers only whole disks).
  • ATA drives behind SCSI/ATA translation layers whose sense data has bit 7 of the response code
    set (e.g. some SAS HBAs) are identified by ata_id: they get ID_BUS=ata and
    disk/by-id/ata-<model>_<serial> links, and lose the disk/by-id/scsi-<serial> links (disk and
    partitions) that 3.2.14 created. eudev ships no compatibility rule for them.
  • Helper exit statuses and outputs: ata_id, cdrom_id, mtd_probe and v4l_id exit 0
    silently when the device node vanished (ENODEV, ENXIO, ENOENT; ata_id, mtd_probe and v4l_id
    also on ENOMEDIUM); scsi_id exits 1 when it cannot open the device; zoned SCSI disks get
    ID_TYPE=zbc.