summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog81
-rw-r--r--INSTALL28
-rw-r--r--Makefile.am209
-rw-r--r--NEWS43
-rw-r--r--README62
-rw-r--r--TODO14
-rw-r--r--configure.ac137
-rw-r--r--extras/ata_id/ata_id.c2
-rw-r--r--extras/cdrom_id/60-cdrom_id.rules2
-rw-r--r--extras/cdrom_id/cdrom_id.c8
-rw-r--r--extras/firmware/firmware.c4
-rw-r--r--extras/fstab_import/.gitignore1
-rw-r--r--extras/fstab_import/79-fstab_import.rules2
-rw-r--r--extras/fstab_import/fstab_import.c203
-rw-r--r--extras/gudev/docs/gudev-docs.xml2
-rw-r--r--extras/gudev/docs/gudev-sections.txt19
-rw-r--r--extras/gudev/gudevclient.c2
-rw-r--r--extras/gudev/gudevclient.h1
-rw-r--r--extras/gudev/gudevdevice.h1
-rw-r--r--extras/gudev/gudevenumerator.h1
-rw-r--r--extras/hid2hci/.gitignore1
-rw-r--r--extras/hid2hci/70-hid2hci.rules30
-rw-r--r--extras/hid2hci/hid2hci.c350
-rw-r--r--extras/keymap/95-keymap.rules2
-rw-r--r--extras/keymap/keymap.c4
-rw-r--r--extras/path_id/path_id.c69
-rw-r--r--extras/rule_generator/75-persistent-net-generator.rules2
-rw-r--r--extras/scsi_id/scsi_id.c1
-rw-r--r--extras/scsi_id/scsi_serial.c12
-rw-r--r--extras/udev-acl/udev-acl.c6
-rw-r--r--extras/usb_id/usb_id.c2
-rw-r--r--init/udev-settle.service.in1
-rw-r--r--init/udev.service.in4
-rw-r--r--init/udev.socket5
-rw-r--r--libudev/libudev-ctrl.c235
-rw-r--r--libudev/libudev-device-private.c11
-rw-r--r--libudev/libudev-device.c72
-rw-r--r--libudev/libudev-enumerate.c22
-rw-r--r--libudev/libudev-list.c23
-rw-r--r--libudev/libudev-monitor.c97
-rw-r--r--libudev/libudev-private.h58
-rw-r--r--libudev/libudev-queue-private.c25
-rw-r--r--libudev/libudev-queue.c8
-rw-r--r--libudev/libudev-util-private.c228
-rw-r--r--libudev/libudev-util.c2
-rw-r--r--libudev/libudev.c2
-rw-r--r--libudev/test-libudev.c84
-rw-r--r--rules/misc/30-kernel-compat.rules92
-rwxr-xr-xtest/udev-test.pl2
-rw-r--r--udev/test-udev.c61
-rw-r--r--udev/udev-event.c422
-rw-r--r--udev/udev-node.c2
-rw-r--r--udev/udev-rules.c199
-rw-r--r--udev/udev.h17
-rw-r--r--udev/udevadm-control.c59
-rw-r--r--udev/udevadm-info.c82
-rw-r--r--udev/udevadm-monitor.c109
-rw-r--r--udev/udevadm-settle.c122
-rw-r--r--udev/udevadm-test.c67
-rw-r--r--udev/udevadm.c8
-rw-r--r--udev/udevadm.xml20
-rw-r--r--udev/udevd.c773
62 files changed, 2281 insertions, 1932 deletions
diff --git a/ChangeLog b/ChangeLog
index d62ecbd76e..ed80c4e43c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,84 @@
+Summary of changes from v167 to v168
+============================================
+
+David Zeuthen (1):
+ Run ata_id on non-removable USB devices
+
+Harald Hoyer (1):
+ udevd: clarify worker exit status
+
+Kay Sievers (35):
+ version bump
+ systemd: let settle depend on trigger, do not block basic with trigger
+ selinux: do not label files in runtime dir
+ selinux: firmware - do not label files in runtime dir
+ udevadm: control - add --exit
+ trivial cleanups
+ udevd: log warning if /run is not writable
+ libudev: ctrl - fix refcounting in connection handling
+ udevadm: settle - watch queue file
+ libudev: bump revision
+ udevadm: info --cleanup-db
+ udevd: do not nice processes
+ "db_persist=" -> "db_persist"
+ udevd: move OOM disable into --daemon option
+ systemd: add OOMScoreAdjust=-1000
+ require explicit "db_persist" to exclude device info from --db-cleanup
+ udevd: get netlink socket from systemd
+ fix more warnings
+ libudev: ctrl, monitor - use SOCK_NONBLOCK
+ systemd: socket -> sockets
+ udevadm: monitor - use epoll
+ libudev: test - use epoll
+ udevadm: test - use printf() instead of info() for non-debug output
+ use 'else if' in epoll event array loop
+ libudev: run_program() - select() -> epoll
+ udevd: ppoll() -> epoll + signalfd
+ Merge branch 'docs/README' of git://github.com/mfwitten/udev
+ timeout handling without alarm()
+ udevadm: settle - kill alarm()
+ udevd: netif rename - use ifindex for temporary name
+ udevd: always use udevd[] log prefix
+ udevd: rules files - accept empty or /dev/null links
+ udevd: log signal number when spawned processes fail
+ systemd: Reqires= -> Wants=udev.socket
+ udevd, udev-event: sync waitpid() error handling
+
+Lee, Chun-Yi (1):
+ Add rule for Acer Aspire One ZG8 to use acer-aspire_5720 keymap
+
+Leonid Antonenkov (1):
+ rule-generator: net - ignore Hyper-V virtual interfaces
+
+Martin Pitt (3):
+ Revert "Do not build extras with --disable-extras"
+ Avoid spinning up CD on pressing eject button
+ keymap: Another ID for Logitech Wave keyboard
+
+Michael Reed (1):
+ path_id: rework SAS device handling
+
+Michael Witten (12):
+ Docs: README: `to replace' -> `replacing'
+ Docs: README: `,' -> `;'
+ Docs: README: Clean up a sentence
+ Docs: README: Use present tense
+ Docs: README: Add missing `and'
+ Docs: README: Remove commas and use subjective mood
+ Docs: README: Clean up `udev extras' requirements
+ Docs: README: Clarify configuration of existing devices
+ Docs: README: `does never apply' -> `never applies'
+ Docs: README: Flip sentence structure to improve wording
+ Docs: README: `set up' is the verb; `setup' is a noun
+ Docs: README: Add a comma to offset the modifier
+
+Seth Forshee (1):
+ keymap: Support Dell Latitude XT2 tablet-mode navigation keys
+
+Thomas Egerer (1):
+ udevd: add 'N:' to optstring in getopt_long
+
+
Summary of changes from v166 to v167
============================================
diff --git a/INSTALL b/INSTALL
index 02ad55fe47..7341272e68 100644
--- a/INSTALL
+++ b/INSTALL
@@ -27,9 +27,24 @@ All options:
--with-selinux
Link against SELinux libraries to set the expected context
for created files.
- --disable-extras
- Disable the build of all extras which have larger external
- dependencies like glib, libacl, libusb, ...
+ --disable-rule_generator
+ Disable persistent network, cdrom naming support.
+ --disable-hwdb
+ Disable hardware database support
+ --disable-udev_acl
+ Disable local user acl permissions support.
+ --disable-gudev
+ Disable Gobject libudev support.
+ --disable-introspection
+ Disable Gobject introspection support.
+ --disable-keymap
+ Disable keymap fixup support.
+ --enable-floppy
+ Enable legacy floppy support.
+ --enable-edd
+ Enable disk edd support.
+ --enable-action_modeswitch
+ Enable action modeswitch support.
The options used in a RPM spec file usually look like:
%configure \
@@ -47,13 +62,12 @@ other location will break other packages, who rightfully expect
the /lib/udev/ directory, to install their rule helper and udev
rule files.
-It is recommended to use the /lib/udev/devices/ directory to place
+It is possible to use the /lib/udev/devices/ directory to place
device nodes, directories and symlinks, which are copied to /dev/
at every bootup. That way, nodes for devices which can not be
detected automatically, or are activated on-demand by opening the
pre-existing device node, will be available.
-Default udev rules and persistent device naming rules are required
+Default udev rules and persistent device naming rules may be required
by other software that depends on the data udev collects from the
-devices, and should therefore be installed by default with every udev
-installation.
+devices.
diff --git a/Makefile.am b/Makefile.am
index 4244089822..0a340aa391 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -25,17 +25,18 @@ DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
CLEANFILES =
-
+BUILT_SOURCES =
INSTALL_EXEC_HOOKS =
INSTALL_DATA_HOOKS =
UNINSTALL_EXEC_HOOKS =
DISTCHECK_HOOKS =
+dist_libexec_SCRIPTS =
# ------------------------------------------------------------------------------
# libudev
# ------------------------------------------------------------------------------
LIBUDEV_CURRENT=11
-LIBUDEV_REVISION=1
+LIBUDEV_REVISION=4
LIBUDEV_AGE=11
SUBDIRS += libudev/docs
@@ -212,20 +213,6 @@ udev_test_udev_SOURCES = \
udev_test_udev_LDADD = libudev/libudev-private.la
# ------------------------------------------------------------------------------
-# rule_generator - persistent network and optical device rule generator
-# ------------------------------------------------------------------------------
-dist_libexec_SCRIPTS = \
- extras/rule_generator/write_cd_rules \
- extras/rule_generator/write_net_rules
-
-udevhomedir = $(libexecdir)
-dist_udevhome_DATA = extras/rule_generator/rule_generator.functions
-
-dist_udevrules_DATA += \
- extras/rule_generator/75-cd-aliases-generator.rules \
- extras/rule_generator/75-persistent-net-generator.rules
-
-# ------------------------------------------------------------------------------
# firmware - firmware loading
# ------------------------------------------------------------------------------
extras_firmware_firmware_SOURCES = extras/firmware/firmware.c
@@ -257,22 +244,6 @@ extras_collect_collect_LDADD = libudev/libudev-private.la
libexec_PROGRAMS += extras/collect/collect
# ------------------------------------------------------------------------------
-# edd_id - BIOS EDD block device match
-# ------------------------------------------------------------------------------
-extras_edd_id_edd_id_SOURCES = extras/edd_id/edd_id.c
-extras_edd_id_edd_id_LDADD = libudev/libudev-private.la
-libexec_PROGRAMS += extras/edd_id/edd_id
-dist_udevrules_DATA += extras/edd_id/61-persistent-storage-edd.rules
-
-# ------------------------------------------------------------------------------
-# create_floppy_devices - historical floppy kernel device nodes (/dev/fd0h1440, ...)
-# ------------------------------------------------------------------------------
-extras_floppy_create_floppy_devices_SOURCES = extras/floppy/create_floppy_devices.c
-extras_floppy_create_floppy_devices_LDADD = libudev/libudev-private.la
-libexec_PROGRAMS += extras/floppy/create_floppy_devices
-dist_udevrules_DATA += extras/floppy/60-floppy.rules
-
-# ------------------------------------------------------------------------------
# input_id - import input device class
# ------------------------------------------------------------------------------
extras_input_id_input_id_SOURCES = extras/input_id/input_id.c
@@ -314,19 +285,74 @@ libexec_PROGRAMS += extras/v4l_id/v4l_id
dist_udevrules_DATA += extras/v4l_id/60-persistent-v4l.rules
# ------------------------------------------------------------------------------
+# mtd_probe - autoloads FTL module for mtd devices
+# ------------------------------------------------------------------------------
+extras_mtd_probe_mtd_probe_SOURCES = \
+ extras/mtd_probe/mtd_probe.c \
+ extras/mtd_probe/mtd_probe.h \
+ extras/mtd_probe/probe_smartmedia.c
+extras_mtd_probe_mtd_probe_CPPFLAGS = $(AM_CPPFLAGS)
+dist_udevrules_DATA += extras/mtd_probe/75-probe_mtd.rules
+libexec_PROGRAMS += extras/mtd_probe/mtd_probe
+
+# ------------------------------------------------------------------------------
# qemu -- qemu/kvm guest tweaks
# ------------------------------------------------------------------------------
dist_udevrules_DATA += extras/qemu/42-qemu-usb.rules
-if ENABLE_EXTRAS
+if ENABLE_RULE_GENERATOR
+# ------------------------------------------------------------------------------
+# rule_generator - persistent network and optical device rule generator
+# ------------------------------------------------------------------------------
+dist_libexec_SCRIPTS += \
+ extras/rule_generator/write_cd_rules \
+ extras/rule_generator/write_net_rules
+
+udevhomedir = $(libexecdir)
+dist_udevhome_DATA = extras/rule_generator/rule_generator.functions
+
+dist_udevrules_DATA += \
+ extras/rule_generator/75-cd-aliases-generator.rules \
+ extras/rule_generator/75-persistent-net-generator.rules
+endif
+
+if ENABLE_HWDB
# ------------------------------------------------------------------------------
-# conditional extras (need glib, libusb, libacl, ...)
+# usb/pci-db - read vendor/device string database
# ------------------------------------------------------------------------------
+extras_usb_db_usb_db_SOURCES = extras/usb-db/usb-db.c
+extras_usb_db_usb_db_CPPFLAGS = $(AM_CPPFLAGS) -DUSB_DATABASE=\"$(USB_DATABASE)\" -DBUILD_FOR_USB
+extras_usb_db_usb_db_LDADD = libudev/libudev-private.la
+libexec_PROGRAMS += extras/usb-db/usb-db
dist_udevrules_DATA += \
rules/rules.d/75-net-description.rules \
rules/rules.d/75-tty-description.rules \
rules/rules.d/78-sound-card.rules
+extras_usb_db_pci_db_SOURCES = extras/usb-db/usb-db.c
+extras_usb_db_pci_db_CPPFLAGS = $(AM_CPPFLAGS) -DPCI_DATABASE=\"$(PCI_DATABASE)\" -DBUILD_FOR_PCI
+extras_usb_db_pci_db_LDADD = libudev/libudev-private.la
+libexec_PROGRAMS += extras/usb-db/pci-db
+endif
+
+if ENABLE_UDEV_ACL
+# ------------------------------------------------------------------------------
+# udev_acl - apply ACLs for users with local forground sessions
+# ------------------------------------------------------------------------------
+extras_udev_acl_udev_acl_SOURCES = extras/udev-acl/udev-acl.c
+extras_udev_acl_udev_acl_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS)
+extras_udev_acl_udev_acl_LDADD = libudev/libudev-private.la -lacl $(GLIB_LIBS)
+dist_udevrules_DATA += extras/udev-acl/70-acl.rules
+libexec_PROGRAMS += extras/udev-acl/udev-acl
+
+udevacl-install-hook:
+ mkdir -p $(DESTDIR)$(prefix)/lib/ConsoleKit/run-seat.d
+ ln -sf $(libexecdir)/udev-acl $(DESTDIR)$(prefix)/lib/ConsoleKit/run-seat.d/udev-acl.ck
+
+INSTALL_EXEC_HOOKS += udevacl-install-hook
+endif
+
+if ENABLE_GUDEV
# ------------------------------------------------------------------------------
# GUdev - libudev gobject interface
# ------------------------------------------------------------------------------
@@ -367,7 +393,7 @@ dist_extras_gudev_libgudev_1_0_la_SOURCES = \
extras/gudev/gudevmarshal.c \
extras/gudev/gudevenumtypes.h \
extras/gudev/gudevenumtypes.c
-BUILT_SOURCES = $(dist_extras_gudev_libgudev_1_0_la_SOURCES)
+BUILT_SOURCES += $(dist_extras_gudev_libgudev_1_0_la_SOURCES)
extras_gudev_libgudev_1_0_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
@@ -462,79 +488,9 @@ libgudev-uninstall-move-hook:
INSTALL_EXEC_HOOKS += libgudev-install-move-hook
UNINSTALL_EXEC_HOOKS += libgudev-uninstall-move-hook
+endif
-# ------------------------------------------------------------------------------
-# Bluetooth HID devices with special magic to switch the device
-# ------------------------------------------------------------------------------
-extras_hid2hci_hid2hci_SOURCES = extras/hid2hci/hid2hci.c
-extras_hid2hci_hid2hci_CPPFLAGS = $(AM_CPPFLAGS) $(LIBUSB_CFLAGS)
-extras_hid2hci_hid2hci_LDADD = libudev/libudev-private.la $(LIBUSB_LIBS)
-dist_udevrules_DATA += extras/hid2hci/70-hid2hci.rules
-libexec_PROGRAMS += extras/hid2hci/hid2hci
-
-# ------------------------------------------------------------------------------
-# udev_acl - apply ACLs for users with local forground sessions
-# ------------------------------------------------------------------------------
-extras_udev_acl_udev_acl_SOURCES = extras/udev-acl/udev-acl.c
-extras_udev_acl_udev_acl_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS)
-extras_udev_acl_udev_acl_LDADD = libudev/libudev-private.la -lacl $(GLIB_LIBS)
-dist_udevrules_DATA += extras/udev-acl/70-acl.rules
-libexec_PROGRAMS += extras/udev-acl/udev-acl
-
-udevacl-install-hook:
- mkdir -p $(DESTDIR)$(prefix)/lib/ConsoleKit/run-seat.d
- ln -sf $(libexecdir)/udev-acl $(DESTDIR)$(prefix)/lib/ConsoleKit/run-seat.d/udev-acl.ck
-
-INSTALL_EXEC_HOOKS += udevacl-install-hook
-
-# ------------------------------------------------------------------------------
-# usb-db - read USB vendor/device string database
-# ------------------------------------------------------------------------------
-extras_usb_db_usb_db_SOURCES = extras/usb-db/usb-db.c
-extras_usb_db_usb_db_CPPFLAGS = $(AM_CPPFLAGS) -DUSB_DATABASE=\"$(USB_DATABASE)\" -DBUILD_FOR_USB
-extras_usb_db_usb_db_LDADD = libudev/libudev-private.la
-libexec_PROGRAMS += extras/usb-db/usb-db
-
-# ------------------------------------------------------------------------------
-# pci-db - read PCI vendor/device string database
-# ------------------------------------------------------------------------------
-extras_usb_db_pci_db_SOURCES = extras/usb-db/usb-db.c
-extras_usb_db_pci_db_CPPFLAGS = $(AM_CPPFLAGS) -DPCI_DATABASE=\"$(PCI_DATABASE)\" -DBUILD_FOR_PCI
-extras_usb_db_pci_db_LDADD = libudev/libudev-private.la
-libexec_PROGRAMS += extras/usb-db/pci-db
-
-# ------------------------------------------------------------------------------
-# mobile-action-modeswitch - switch Mobile Action cables into serial mode
-# ------------------------------------------------------------------------------
-extras_mobile_action_modeswitch_mobile_action_modeswitch_SOURCES =\
- extras/mobile-action-modeswitch/mobile-action-modeswitch.c \
- extras/mobile-action-modeswitch/utils.c \
- extras/mobile-action-modeswitch/utils.h \
- extras/mobile-action-modeswitch/ma8280p_us.c \
- extras/mobile-action-modeswitch/ma8280p_us.h
-extras_mobile_action_modeswitch_mobile_action_modeswitch_CPPFLAGS = $(AM_CPPFLAGS) $(LIBUSB_CFLAGS)
-extras_mobile_action_modeswitch_mobile_action_modeswitch_LDADD = libudev/libudev-private.la $(LIBUSB_LIBS)
-
-dist_udevrules_DATA += \
- extras/mobile-action-modeswitch/61-mobile-action.rules
-
-libexec_PROGRAMS += extras/mobile-action-modeswitch/mobile-action-modeswitch
-
-# ------------------------------------------------------------------------------
-# mtd_probe - autoloads FTL module for mtd devices
-# ------------------------------------------------------------------------------
-extras_mtd_probe_mtd_probe_SOURCES = \
- extras/mtd_probe/mtd_probe.c \
- extras/mtd_probe/mtd_probe.h \
- extras/mtd_probe/probe_smartmedia.c
-
-extras_mtd_probe_mtd_probe_CPPFLAGS = $(AM_CPPFLAGS)
-
-dist_udevrules_DATA += extras/mtd_probe/75-probe_mtd.rules
-
-libexec_PROGRAMS += extras/mtd_probe/mtd_probe
-
-
+if ENABLE_KEYMAP
# ------------------------------------------------------------------------------
# keymap - map custom hardware's multimedia keys
# ------------------------------------------------------------------------------
@@ -552,7 +508,6 @@ libexec_PROGRAMS += extras/keymap/keymap
dist_libexec_SCRIPTS += extras/keymap/findkeyboards
dist_libexec_SCRIPTS += extras/keymap/keyboard-force-release.sh
dist_doc_DATA = extras/keymap/README.keymap.txt
-
CLEANFILES += \
extras/keymap/keys.txt \
extras/keymap/keys-from-name.gperf
@@ -638,8 +593,44 @@ extras/keymap/keys-to-name.h: extras/keymap/keys.txt Makefile
keymaps-distcheck-hook: extras/keymap/keys.txt
$(top_srcdir)/extras/keymap/check-keymaps.sh $(top_srcdir) $^
DISTCHECK_HOOKS += keymaps-distcheck-hook
+endif
-endif # ENABLE_EXTRAS
+if ENABLE_FLOPPY
+# ------------------------------------------------------------------------------
+# create_floppy_devices - historical floppy kernel device nodes (/dev/fd0h1440, ...)
+# ------------------------------------------------------------------------------
+extras_floppy_create_floppy_devices_SOURCES = extras/floppy/create_floppy_devices.c
+extras_floppy_create_floppy_devices_LDADD = libudev/libudev-private.la
+libexec_PROGRAMS += extras/floppy/create_floppy_devices
+dist_udevrules_DATA += extras/floppy/60-floppy.rules
+endif
+
+if ENABLE_EDD
+# ------------------------------------------------------------------------------
+# edd_id - create /dev/disk/by-id/edd-* links for BIOS EDD data
+# ------------------------------------------------------------------------------
+extras_edd_id_edd_id_SOURCES = extras/edd_id/edd_id.c
+extras_edd_id_edd_id_LDADD = libudev/libudev-private.la
+libexec_PROGRAMS += extras/edd_id/edd_id
+dist_udevrules_DATA += extras/edd_id/61-persistent-storage-edd.rules
+endif
+
+if ENABLE_ACTION_MODESWITCH
+# ------------------------------------------------------------------------------
+# mobile-action-modeswitch - switch Mobile Action cables into serial mode
+# ------------------------------------------------------------------------------
+extras_mobile_action_modeswitch_mobile_action_modeswitch_SOURCES =\
+ extras/mobile-action-modeswitch/mobile-action-modeswitch.c \
+ extras/mobile-action-modeswitch/utils.c \
+ extras/mobile-action-modeswitch/utils.h \
+ extras/mobile-action-modeswitch/ma8280p_us.c \
+ extras/mobile-action-modeswitch/ma8280p_us.h
+extras_mobile_action_modeswitch_mobile_action_modeswitch_CPPFLAGS = $(AM_CPPFLAGS) $(LIBUSB_CFLAGS)
+extras_mobile_action_modeswitch_mobile_action_modeswitch_LDADD = libudev/libudev-private.la $(LIBUSB_LIBS)
+dist_udevrules_DATA += \
+ extras/mobile-action-modeswitch/61-mobile-action.rules
+libexec_PROGRAMS += extras/mobile-action-modeswitch/mobile-action-modeswitch
+endif
# ------------------------------------------------------------------------------
# install, uninstall, clean hooks
diff --git a/NEWS b/NEWS
index f45957b62d..16a41a9bbb 100644
--- a/NEWS
+++ b/NEWS
@@ -1,7 +1,50 @@
+udev 169
+========
+Bugfixes.
+
+We require at least Linux kernel 2.6.32 now. Some platforms might
+require a later kernel that supports accept4(), or need to backport
+the accept4() syscall wiring.
+
+The hid2hci tool move to the bluez package and was removed.
+
+Many of the extras can be --enabled/--disabled during configure
+now. The --disable-extras option was removed. To check the
+current options, the usual './configure --help' prints them.
+
udev 168
========
Bugfixes.
+Udev logs a warning now if /run is not writable at udevd
+startup. It will still fall back to /dev/.udev, but this is
+now considered a bug.
+
+The running udev daemon can now cleanly shut down with:
+ udevadm control --exit
+
+Udev in initramfs should clean the state of the udev database
+with: udevadm info --cleanup-db which will remove all state left
+behind from events/rules in initramfs. If initramfs uses
+--cleanup-db and device-mapper/LVM, the rules in initramfs need
+to add OPTIONS+="db_persist" for all dm devices. This will
+prevent removal of the udev database for these devices.
+
+Spawned programs by PROGRAM/IMPORT/RUN now have a hard timeout of
+120 seconds per process. If that timeout is reached the spawned
+process will be killed. The event timeout can be overwritten with
+udev rules.
+
+If systemd is used, udev gets now activated by netlink data.
+Systemd will bind the netlink socket which will buffer all data.
+If needed, such setup allows a seemless update of the udev daemon,
+where no event can be lost during a udevd update/restart.
+Packages need to make sure to: systemctl stop udev.socket udev.service
+or 'mask' udev.service during the upgrade to prevent any unwanted
+auto-spawning of udevd.
+This version of udev conflicts with systemd version below 25. The
+unchanged service files will not wirk correctly.
+
udev 167
========
Bugfixes.
diff --git a/README b/README
index 78787469cc..f856370685 100644
--- a/README
+++ b/README
@@ -3,21 +3,26 @@ udev - Linux userspace device management
Integrating udev in the system has complex dependencies and may differ from
distribution to distribution. A system may not be able to boot up or work
reliably without a properly installed udev version. The upstream udev project
-does not recommend to replace a distro's udev installation with the upstream
+does not recommend replacing a distro's udev installation with the upstream
version.
The upstream udev project's set of default rules may require a most recent
-kernel release to work properly. This is currently version 2.6.31.
+kernel release to work properly. This is currently version 2.6.32.
Tools and rules shipped by udev are not public API and may change at any time.
-Never call any private tool in /lib/udev from any external application, it might
+Never call any private tool in /lib/udev from any external application; it might
just go away in the next release. Access to udev information is only offered
-by udevadm and libudev. Tools and rules in /lib/udev, and the entire content of
-the /dev/.udev directory is private to udev and does change whenever needed.
+by udevadm and libudev. Tools and rules in /lib/udev and the entire contents of
+the /dev/.udev directory are private to udev and do change whenever needed.
Requirements:
- - Version 2.6.27 of the Linux kernel with sysfs, procfs, signalfd, inotify,
- unix domain sockets, networking and hotplug enabled:
+ - Version 2.6.32 of the Linux kernel with sysfs, procfs, signalfd, inotify,
+ unix domain sockets, networking and hotplug enabled
+
+ - Some architectures might need a later kernel, that supports accept4(),
+ or need to backport the accept4() syscall wiring in the kernel.
+
+ - These options are needed:
CONFIG_HOTPLUG=y
CONFIG_UEVENT_HELPER_PATH=""
CONFIG_NET=y
@@ -25,13 +30,15 @@ Requirements:
CONFIG_SYSFS=y
CONFIG_SYSFS_DEPRECATED*=n
CONFIG_PROC_FS=y
- CONFIG_TMPFS=y
CONFIG_INOTIFY_USER=y
CONFIG_SIGNALFD=y
+
+ - These options might be needed:
+ CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y (user ACLs for device nodes)
CONFIG_BLK_DEV_BSG=y (SCSI devices)
- - Udev will not work with the CONFIG_SYSFS_DEPRECATED* option.
+ - Udev does not work with the CONFIG_SYSFS_DEPRECATED* option.
- Unix domain sockets (CONFIG_UNIX) as a loadable kernel module may work,
but it is not supported.
@@ -41,46 +48,46 @@ Requirements:
unusable because the kernel may create too many processes in parallel
so that the system runs out-of-memory.
- - The proc filesystem must be mounted on /proc, the sysfs filesystem must
+ - The proc filesystem must be mounted on /proc, and the sysfs filesystem must
be mounted at /sys. No other locations are supported by a standard
udev installation.
- - The system must have the following group names resolvable at udev startup:
- disk, cdrom, floppy, tape, audio, video, lp, tty, dialout, kmem.
- Especially in LDAP setups, it is required, that getgrnam() is able to resolve
- these group names with only the rootfs mounted, and while no network is
+ - The default rule sset requires the following group names resolvable at udev startup:
+ disk, cdrom, floppy, tape, audio, video, lp, tty, dialout, and kmem.
+ Especially in LDAP setups, it is required that getgrnam() be able to resolve
+ these group names with only the rootfs mounted and while no network is
available.
- - To build all 'udev extras', libacl, libglib2, libusb, usbutils, pciutils,
- gperf are needed. These dependencies can be disabled with the
- --disable-extras configure option.
+ - Some udev extras have external dependencies like:
+ libacl, libglib2, libusb, usbutils, pciutils, and gperf.
+ All these extras can be disabled with configure options.
Setup:
- At bootup, the /dev directory should get the 'devtmpfs' filesystem
- mounted. Udev will manage permissions and ownership of the kernel-created
- device nodes, and possibly create additional symlinks. If needed, udev also
+ mounted. Udev manages the permissions and ownership of the kernel-created
+ device nodes, and udev possibly creates additional symlinks. If needed, udev also
works on an empty 'tmpfs' filesystem, but some static device nodes like
/dev/null, /dev/console, /dev/kmsg are needed to be able to start udev itself.
- The udev daemon should be started to handle device events sent by the kernel.
During bootup, the kernel can be asked to send events for all already existing
- devices, to apply the configuration to these devices. This is usually done by:
+ devices so that they too can be configured by udev. This is usually done by:
/sbin/udevadm trigger --type=subsystems
/sbin/udevadm trigger --type=devices
- - Restarting the daemon does never apply any rules to existing devices.
+ - Restarting the daemon never applies any rules to existing devices.
- - New/changed rule files are picked up automatically, there is no daemon
+ - New/changed rule files are picked up automatically; there is no daemon
restart or signal needed.
Operation:
- - Udev creates/removes device nodes in /dev, based on events the kernel
- sends out on device creation/removal.
+ - Based on events the kernel sends out on device creation/removal, udev
+ creates/removes device nodes in the /dev directory.
- All kernel events are matched against a set of specified rules, which
possibly hook into the event processing and load required kernel
- modules to setup devices. For all devices the kernel exports a major/minor
- number, if needed, udev will create a device node with the default kernel
+ modules to set up devices. For all devices, the kernel exports a major/minor
+ number; if needed, udev creates a device node with the default kernel
name. If specified, udev applies permissions/ownership to the device
node, creates additional symlinks pointing to the node, and executes
programs to handle the device.
@@ -90,7 +97,8 @@ Operation:
http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/
http://www.kernel.org/pub/linux/utils/kernel/hotplug/gudev/
-For more details about udev and udev rules see the udev(7) man page.
+For more details about udev and udev rules, see the udev man pages:
+ http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev/
Please direct any comment/question to the linux-hotplug mailing list at:
linux-hotplug@vger.kernel.org
diff --git a/TODO b/TODO
index 964ffe9cbb..0ccdcc11d7 100644
--- a/TODO
+++ b/TODO
@@ -1,5 +1,13 @@
- - bind control socket in systemd
+ - /run/udev/control socket
- - remove deprecated trigger --type=failed logic
+ - rules_generator:
+ - no longer rename netif in kernel namespace
+ - do not create rules on-the-fly, require explicit
+ configuration
+ - drop persistent optical names
- - remove deprecated BUS=, SYSFS{}=, ID= keys
+ - kill tabs?
+
+ - remove deprecated trigger --type=failed logic
+
+ - remove deprecated BUS=, SYSFS{}=, ID= keys
diff --git a/configure.ac b/configure.ac
index cb7585e58b..22e0835919 100644
--- a/configure.ac
+++ b/configure.ac
@@ -54,8 +54,7 @@ AC_ARG_WITH(firmware-path,
[], [with_firmware_path="/lib/firmware/updates:/lib/firmware"])
OLD_IFS=$IFS
IFS=:
-for i in $with_firmware_path
-do
+for i in $with_firmware_path; do
if test "x${FIRMWARE_PATH}" = "x"; then
FIRMWARE_PATH="\\\"${i}/\\\""
else
@@ -69,38 +68,34 @@ AC_ARG_WITH([systemdsystemunitdir],
AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
[], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)])
if test "x$with_systemdsystemunitdir" != xno; then
- AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])
+ AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])
fi
AM_CONDITIONAL(WITH_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ])
-AC_ARG_ENABLE([extras],
- AS_HELP_STRING([--disable-extras], [disable extras with external dependencies]),
- [], [enable_extras=yes])
-if test "x$enable_extras" = xyes; then
- AC_PATH_PROG([GPERF], [gperf])
- if test -z "$GPERF"; then
- AC_MSG_ERROR([gperf is needed])
- fi
-
- PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.22.0 gobject-2.0 >= 2.22.0])
- AC_SUBST([GLIB_CFLAGS])
- AC_SUBST([GLIB_LIBS])
-
- AC_CHECK_LIB([acl], [acl_init], [:], AC_MSG_ERROR([libacl not found]))
- AC_CHECK_HEADER([acl/libacl.h], [:], AC_MSG_ERROR([libacl header not found]))
-
- PKG_CHECK_MODULES(LIBUSB, libusb >= 0.1.12)
- AC_SUBST(LIBUSB_CFLAGS)
- AC_SUBST(LIBUSB_LIBS)
-
- PKG_CHECK_MODULES(USBUTILS, usbutils >= 0.82)
- AC_SUBST([USB_DATABASE], [$($PKG_CONFIG --variable=usbids usbutils)])
+# ------------------------------------------------------------------------------
+# rule_generator - persistent network and optical device rule generator
+# ------------------------------------------------------------------------------
+AC_ARG_ENABLE([hwdb],
+ AS_HELP_STRING([--disable-rule_generator], [disable persistent network, cdrom support]),
+ [], [enable_rule_generator=yes])
+AM_CONDITIONAL([ENABLE_RULE_GENERATOR], [test "x$enable_rule_genarator" = xyes])
+# ------------------------------------------------------------------------------
+# usb/pci-db - read vendor/device string database
+# ------------------------------------------------------------------------------
+AC_ARG_ENABLE([hwdb],
+ AS_HELP_STRING([--disable-hwdb], [disable hardware database support]),
+ [], [enable_hwdb=yes])
+if test "x$enable_hwdb" = xyes; then
if test "x$cross_compiling" = "xno" ; then
AC_CHECK_FILES([/usr/share/pci.ids], [pciids=/usr/share/pci.ids])
AC_CHECK_FILES([/usr/share/hwdata/pci.ids], [pciids=/usr/share/hwdata/pci.ids])
AC_CHECK_FILES([/usr/share/misc/pci.ids], [pciids=/usr/share/misc/pci.ids])
fi
+
+ PKG_CHECK_MODULES(USBUTILS, usbutils >= 0.82)
+ AC_SUBST([USB_DATABASE], [$($PKG_CONFIG --variable=usbids usbutils)])
+
AC_ARG_WITH(pci-ids-path,
[AS_HELP_STRING([--with-pci-ids-path=DIR], [Path to pci.ids file])],
[PCI_DATABASE=${withval}],
@@ -112,15 +107,40 @@ if test "x$enable_extras" = xyes; then
AC_MSG_CHECKING([for PCI database location])
AC_MSG_RESULT([$PCI_DATABASE])
AC_SUBST(PCI_DATABASE)
+fi
+AM_CONDITIONAL([ENABLE_HWDB], [test "x$enable_hwdb" = xyes])
- AC_CHECK_HEADER([linux/input.h], [:], AC_MSG_ERROR([kernel headers not found]))
- AC_SUBST([INCLUDE_PREFIX], [$(echo '#include <linux/input.h>' | eval $ac_cpp -E - | sed -n '/linux\/input.h/ {s:.*"\(.*\)/linux/input.h".*:\1:; p; q}')])
+# ------------------------------------------------------------------------------
+# udev_acl - apply ACLs for users with local forground sessions
+# ------------------------------------------------------------------------------
+AC_ARG_ENABLE([udev_acl],
+ AS_HELP_STRING([--disable-udev_acl], [disable local user acl permissions support]),
+ [], [enable_udev_acl=yes])
+if test "x$enable_udev_acl" = xyes; then
+ AC_CHECK_LIB([acl], [acl_init], [:], AC_MSG_ERROR([libacl not found]))
+ AC_CHECK_HEADER([acl/libacl.h], [:], AC_MSG_ERROR([libacl header not found]))
+
+ PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.22.0 gobject-2.0 >= 2.22.0])
+ AC_SUBST([GLIB_CFLAGS])
+ AC_SUBST([GLIB_LIBS])
+fi
+AM_CONDITIONAL([ENABLE_UDEV_ACL], [test "x$enable_udev_acl" = xyes])
+
+# ------------------------------------------------------------------------------
+# GUdev - libudev gobject interface
+# ------------------------------------------------------------------------------
+AC_ARG_ENABLE([gudev],
+ AS_HELP_STRING([--disable-gudev], [disable Gobject libudev support]),
+ [], [enable_gudev=yes])
+if test "x$enable_gudev" = xyes; then
+ PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.22.0 gobject-2.0 >= 2.22.0])
+ AC_SUBST([GLIB_CFLAGS])
+ AC_SUBST([GLIB_LIBS])
fi
-AM_CONDITIONAL([ENABLE_EXTRAS], [test "x$enable_extras" = xyes])
AC_ARG_ENABLE([introspection],
- AS_HELP_STRING([--disable-introspection], [disable GObject introspection]),
- [], [enable_introspection=yes])
+ AS_HELP_STRING([--disable-introspection], [disable GObject introspection]),
+ [], [enable_introspection=yes])
if test "x$enable_introspection" = xyes; then
PKG_CHECK_MODULES([INTROSPECTION], [gobject-introspection-1.0 >= 0.6.2])
AC_DEFINE([ENABLE_INTROSPECTION], [1], [enable GObject introspection support])
@@ -132,6 +152,54 @@ if test "x$enable_introspection" = xyes; then
fi
AM_CONDITIONAL([ENABLE_INTROSPECTION], [test "x$enable_introspection" = xyes])
+AM_CONDITIONAL([ENABLE_GUDEV], [test "x$enable_gudev" = xyes])
+
+# ------------------------------------------------------------------------------
+# keymap - map custom hardware's multimedia keys
+# ------------------------------------------------------------------------------
+AC_ARG_ENABLE([keymap],
+ AS_HELP_STRING([--disable-keymap], [disable keymap fixup support]),
+ [], [enable_keymap=yes])
+if test "x$enable_keymap" = xyes; then
+ AC_PATH_PROG([GPERF], [gperf])
+ if test -z "$GPERF"; then
+ AC_MSG_ERROR([gperf is needed])
+ fi
+
+ AC_CHECK_HEADER([linux/input.h], [:], AC_MSG_ERROR([kernel headers not found]))
+ AC_SUBST([INCLUDE_PREFIX], [$(echo '#include <linux/input.h>' | eval $ac_cpp -E - | sed -n '/linux\/input.h/ {s:.*"\(.*\)/linux/input.h".*:\1:; p; q}')])
+fi
+AM_CONDITIONAL([ENABLE_KEYMAP], [test "x$enable_keymap" = xyes])
+
+# ------------------------------------------------------------------------------
+# create_floppy_devices - historical floppy kernel device nodes (/dev/fd0h1440, ...)
+# ------------------------------------------------------------------------------
+AC_ARG_ENABLE([floppy],
+ AS_HELP_STRING([--enable-floppy], [enable legacy floppy support]),
+ [], [enable_floppy=no])
+AM_CONDITIONAL([ENABLE_FLOPPY], [test "x$enable_floppy" = xyes])
+
+# ------------------------------------------------------------------------------
+# edd_id - create /dev/disk/by-id/edd-* links for BIOS EDD data
+# ------------------------------------------------------------------------------
+AC_ARG_ENABLE([edd],
+ AS_HELP_STRING([--enable-edd], [enable disk edd support]),
+ [], [enable_edd=no])
+AM_CONDITIONAL([ENABLE_EDD], [test "x$enable_edd" = xyes])
+
+# ------------------------------------------------------------------------------
+# mobile-action-modeswitch - switch Mobile Action cables into serial mode
+# ------------------------------------------------------------------------------
+AC_ARG_ENABLE([ACTION_MODESWITCH],
+ AS_HELP_STRING([--enable-action_modeswitch], [enable action modeswitch support]),
+ [], [enable_action_modeswitch=no])
+if test "x$enable_action_modeswitch" = xyes; then
+ PKG_CHECK_MODULES(LIBUSB, libusb >= 0.1.12)
+ AC_SUBST(LIBUSB_CFLAGS)
+ AC_SUBST(LIBUSB_LIBS)
+fi
+AM_CONDITIONAL([ENABLE_ACTION_MODESWITCH], [test "x$enable_action_modeswitch" = xyes])
+
AC_CONFIG_HEADERS(config.h)
AC_CONFIG_FILES([
Makefile
@@ -175,8 +243,15 @@ AC_MSG_RESULT([
cflags: ${CFLAGS}
ldflags: ${LDFLAGS}
- extras: ${enable_extras}
+ rule_generator: ${enable_rule_generator}
+ hwdb: ${enable_hwdb}
+ udev_acl: ${enable_udev_acl}
+ gudev: ${enable_gudev}
gintrospection: ${enable_introspection}
+ keymap: ${enable_keymap}
+ floppy: ${enable_floppy}
+ edd: ${enable_edd}
+ action_modeswitch: ${enable_action_modeswitch}
usb.ids: ${USB_DATABASE}
pci.ids: ${PCI_DATABASE}
diff --git a/extras/ata_id/ata_id.c b/extras/ata_id/ata_id.c
index 594cfb2017..cb4a1a1f07 100644
--- a/extras/ata_id/ata_id.c
+++ b/extras/ata_id/ata_id.c
@@ -486,8 +486,6 @@ int main(int argc, char *argv[])
printf("Usage: ata_id [--export] [--help] <device>\n"
" --export print values as environment keys\n"
" --help print this help text\n\n");
- default:
- rc = 1;
goto exit;
}
}
diff --git a/extras/cdrom_id/60-cdrom_id.rules b/extras/cdrom_id/60-cdrom_id.rules
index aedf6f0558..1ad1d0cc23 100644
--- a/extras/cdrom_id/60-cdrom_id.rules
+++ b/extras/cdrom_id/60-cdrom_id.rules
@@ -9,6 +9,6 @@ ENV{DEVTYPE}!="disk", GOTO="cdrom_end"
ENV{DISK_EJECT_REQUEST}=="?*", GOTO="cdrom_end"
KERNEL=="sr[0-9]*", ENV{ID_CDROM}="1"
-IMPORT{program}="cdrom_id --export $tempnode"
+IMPORT{program}="cdrom_id $tempnode"
LABEL="cdrom_end"
diff --git a/extras/cdrom_id/cdrom_id.c b/extras/cdrom_id/cdrom_id.c
index b2f897e3b5..7908f6d19a 100644
--- a/extras/cdrom_id/cdrom_id.c
+++ b/extras/cdrom_id/cdrom_id.c
@@ -825,13 +825,11 @@ int main(int argc, char *argv[])
{
struct udev *udev;
static const struct option options[] = {
- { "export", no_argument, NULL, 'x' },
{ "debug", no_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
{}
};
const char *node = NULL;
- int export = 0;
int fd = -1;
int cnt;
int rc = 0;
@@ -846,7 +844,7 @@ int main(int argc, char *argv[])
while (1) {
int option;
- option = getopt_long(argc, argv, "dxh", options, NULL);
+ option = getopt_long(argc, argv, "dh", options, NULL);
if (option == -1)
break;
@@ -856,12 +854,8 @@ int main(int argc, char *argv[])
if (udev_get_log_priority(udev) < LOG_INFO)
udev_set_log_priority(udev, LOG_INFO);
break;
- case 'x':
- export = 1;
- break;
case 'h':
printf("Usage: cdrom_id [options] <device>\n"
- " --export export key/value pairs\n"
" --debug debug to stderr\n"
" --help print this help text\n\n");
goto exit;
diff --git a/extras/firmware/firmware.c b/extras/firmware/firmware.c
index f2df76c8c5..36ce79054c 100644
--- a/extras/firmware/firmware.c
+++ b/extras/firmware/firmware.c
@@ -43,7 +43,7 @@ static bool set_loading(struct udev *udev, char *loadpath, const char *state)
static bool copy_firmware(struct udev *udev, const char *source, const char *target, size_t size)
{
char *buf;
- FILE *fsource, *ftarget;
+ FILE *fsource = NULL, *ftarget = NULL;
bool ret = false;
buf = malloc(size);
@@ -112,8 +112,6 @@ int main(int argc, char **argv)
break;
case 'h':
printf("Usage: firmware --firmware=<fwfile> --devpath=<path> [--help]\n\n");
- default:
- rc = 1;
goto exit;
}
}
diff --git a/extras/fstab_import/.gitignore b/extras/fstab_import/.gitignore
deleted file mode 100644
index c9ec5b801c..0000000000
--- a/extras/fstab_import/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-fstab_import
diff --git a/extras/fstab_import/79-fstab_import.rules b/extras/fstab_import/79-fstab_import.rules
deleted file mode 100644
index 2ded7d3d9f..0000000000
--- a/extras/fstab_import/79-fstab_import.rules
+++ /dev/null
@@ -1,2 +0,0 @@
-ACTION!="remove", SUBSYSTEM=="block", ENV{ID_FS_USAGE}=="filesystem|other", IMPORT="fstab_import $name $links mapper/$env{DM_NAME}"
-
diff --git a/extras/fstab_import/fstab_import.c b/extras/fstab_import/fstab_import.c
deleted file mode 100644
index c66bffb25c..0000000000
--- a/extras/fstab_import/fstab_import.c
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * find matching entry in fstab and export it
- *
- * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE 1
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <string.h>
-#include <errno.h>
-#include <getopt.h>
-#include <mntent.h>
-#include <sys/stat.h>
-
-#include "libudev.h"
-#include "libudev-private.h"
-
-static int debug;
-
-static void log_fn(struct udev *udev, int priority,
- const char *file, int line, const char *fn,
- const char *format, va_list args)
-{
- if (debug) {
- fprintf(stderr, "%s: ", fn);
- vfprintf(stderr, format, args);
- } else {
- vsyslog(priority, format, args);
- }
-}
-
-static int matches_device_list(struct udev *udev, char **devices, const char *name)
-{
- int i;
-
- for (i = 0; devices[i] != NULL; i++) {
- info(udev, "compare '%s' == '%s'\n", name, devices[i]);
- if (strcmp(devices[i], name) == 0)
- return 1;
- }
- return 0;
-}
-
-static void print_fstab_entry(struct udev *udev, struct mntent *mnt)
-{
- printf("FSTAB_NAME=%s\n", mnt->mnt_fsname);
- printf("FSTAB_DIR=%s\n", mnt->mnt_dir);
- printf("FSTAB_TYPE=%s\n", mnt->mnt_type);
- printf("FSTAB_OPTS=%s\n", mnt->mnt_opts);
- printf("FSTAB_FREQ=%d\n", mnt->mnt_freq);
- printf("FSTAB_PASSNO=%d\n", mnt->mnt_passno);
-}
-
-int main(int argc, char *argv[])
-{
- struct udev *udev;
- static const struct option options[] = {
- { "export", no_argument, NULL, 'x' },
- { "debug", no_argument, NULL, 'd' },
- { "help", no_argument, NULL, 'h' },
- {}
- };
- char **devices;
- FILE *fp;
- struct mntent *mnt;
- int rc = 1;
-
- udev = udev_new();
- if (udev == NULL)
- goto exit;
-
- udev_log_init("fstab_id");
- udev_set_log_fn(udev, log_fn);
-
- while (1) {
- int option;
-
- option = getopt_long(argc, argv, "dxh", options, NULL);
- if (option == -1)
- break;
-
- switch (option) {
- case 'd':
- debug = 1;
- if (udev_get_log_priority(udev) < LOG_INFO)
- udev_set_log_priority(udev, LOG_INFO);
- break;
- case 'h':
- printf("Usage: fstab_id [OPTIONS] name [...]\n"
- " --export print environment keys\n"
- " --debug debug to stderr\n"
- " --help print this help text\n\n");
- goto exit;
- case 'x':
- break;
- default:
- rc = 2;
- goto exit;
- }
- }
-
- devices = &argv[optind];
- if (devices[0] == NULL) {
- fprintf(stderr, "error: missing device(s) to match\n");
- rc = 3;
- goto exit;
- }
-
- fp = setmntent ("/etc/fstab", "r");
- if (fp == NULL) {
- fprintf(stderr, "error: opening fstab: %s\n", strerror(errno));
- rc = 4;
- goto exit;
- }
-
- while (1) {
- mnt = getmntent(fp);
- if (mnt == NULL)
- break;
-
- info(udev, "found '%s'@'%s'\n", mnt->mnt_fsname, mnt->mnt_dir);
-
- /* skip root device */
- if (strcmp(mnt->mnt_dir, "/") == 0)
- continue;
-
- /* match LABEL */
- if (strncmp(mnt->mnt_fsname, "LABEL=", 6) == 0) {
- const char *label;
- char str[256];
-
- label = &mnt->mnt_fsname[6];
- if (label[0] == '"' || label[0] == '\'') {
- char *pos;
-
- util_strscpy(str, sizeof(str), &label[1]);
- pos = strrchr(str, label[0]);
- if (pos == NULL)
- continue;
- pos[0] = '\0';
- label = str;
- }
- if (matches_device_list(udev, devices, label)) {
- print_fstab_entry(udev, mnt);
- rc = 0;
- break;
- }
- continue;
- }
-
- /* match UUID */
- if (strncmp(mnt->mnt_fsname, "UUID=", 5) == 0) {
- const char *uuid;
- char str[256];
-
- uuid = &mnt->mnt_fsname[5];
- if (uuid[0] == '"' || uuid[0] == '\'') {
- char *pos;
-
- util_strscpy(str, sizeof(str), &uuid[1]);
- pos = strrchr(str, uuid[0]);
- if (pos == NULL)
- continue;
- pos[0] = '\0';
- uuid = str;
- }
- if (matches_device_list(udev, devices, uuid)) {
- print_fstab_entry(udev, mnt);
- rc = 0;
- break;
- }
- continue;
- }
-
- /* only devices */
- if (strncmp(mnt->mnt_fsname, udev_get_dev_path(udev), strlen(udev_get_dev_path(udev))) != 0)
- continue;
-
- if (matches_device_list(udev, devices, &mnt->mnt_fsname[strlen(udev_get_dev_path(udev))+1])) {
- print_fstab_entry(udev, mnt);
- rc = 0;
- break;
- }
- }
- endmntent(fp);
-
-exit:
- udev_unref(udev);
- udev_log_close();
- return rc;
-}
diff --git a/extras/gudev/docs/gudev-docs.xml b/extras/gudev/docs/gudev-docs.xml
index 3a93b21720..65fdfff8e5 100644
--- a/extras/gudev/docs/gudev-docs.xml
+++ b/extras/gudev/docs/gudev-docs.xml
@@ -29,7 +29,7 @@
</authorgroup>
<copyright>
- <year>2009</year>
+ <year>2011</year>
<holder>The GUDev Authors</holder>
</copyright>
diff --git a/extras/gudev/docs/gudev-sections.txt b/extras/gudev/docs/gudev-sections.txt
index 181870fb5a..213e1a7465 100644
--- a/extras/gudev/docs/gudev-sections.txt
+++ b/extras/gudev/docs/gudev-sections.txt
@@ -19,6 +19,8 @@ g_udev_client_get_type
G_UDEV_CLIENT_CLASS
G_UDEV_IS_CLIENT_CLASS
G_UDEV_CLIENT_GET_CLASS
+<SUBSECTION Private>
+GUdevClientPrivate
</SECTION>
<SECTION>
@@ -65,6 +67,8 @@ g_udev_device_get_type
G_UDEV_DEVICE_CLASS
G_UDEV_IS_DEVICE_CLASS
G_UDEV_DEVICE_GET_CLASS
+<SUBSECTION Private>
+GUdevDevicePrivate
</SECTION>
<SECTION>
@@ -91,4 +95,19 @@ g_udev_enumerator_get_type
G_UDEV_ENUMERATOR_CLASS
G_UDEV_IS_ENUMERATOR_CLASS
G_UDEV_ENUMERATOR_GET_CLASS
+<SUBSECTION Private>
+GUdevEnumeratorPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gudevmarshal</FILE>
+<SUBSECTION Private>
+g_udev_marshal_VOID__STRING_OBJECT
+</SECTION>
+
+<SECTION>
+<FILE>gudevenumtypes</FILE>
+<SUBSECTION Private>
+G_TYPE_UDEV_DEVICE_TYPE
+g_udev_device_type_get_type
</SECTION>
diff --git a/extras/gudev/gudevclient.c b/extras/gudev/gudevclient.c
index 853454b6f0..97b951adcd 100644
--- a/extras/gudev/gudevclient.c
+++ b/extras/gudev/gudevclient.c
@@ -460,6 +460,8 @@ g_udev_client_query_by_device_file (GUdevClient *client,
* @sysfs_path: A sysfs path.
*
* Looks up a device for a sysfs path.
+ *
+ * Returns: (transfer full): A #GUdevDevice object or %NULL if the device was not found. Free with g_object_unref().
*/
GUdevDevice *
g_udev_client_query_by_sysfs_path (GUdevClient *client,
diff --git a/extras/gudev/gudevclient.h b/extras/gudev/gudevclient.h
index 6e365c6d35..b425d03d48 100644
--- a/extras/gudev/gudevclient.h
+++ b/extras/gudev/gudevclient.h
@@ -41,7 +41,6 @@ typedef struct _GUdevClientPrivate GUdevClientPrivate;
/**
* GUdevClient:
- * @parent: Parent instance.
*
* The #GUdevClient struct is opaque and should not be accessed directly.
*/
diff --git a/extras/gudev/gudevdevice.h b/extras/gudev/gudevdevice.h
index ab22371ded..d4873bad0f 100644
--- a/extras/gudev/gudevdevice.h
+++ b/extras/gudev/gudevdevice.h
@@ -41,7 +41,6 @@ typedef struct _GUdevDevicePrivate GUdevDevicePrivate;
/**
* GUdevDevice:
- * @parent: Parent instance.
*
* The #GUdevDevice struct is opaque and should not be accessed directly.
*/
diff --git a/extras/gudev/gudevenumerator.h b/extras/gudev/gudevenumerator.h
index ed54dbfc66..3fddccf573 100644
--- a/extras/gudev/gudevenumerator.h
+++ b/extras/gudev/gudevenumerator.h
@@ -41,7 +41,6 @@ typedef struct _GUdevEnumeratorPrivate GUdevEnumeratorPrivate;
/**
* GUdevEnumerator:
- * @parent: Parent instance.
*
* The #GUdevEnumerator struct is opaque and should not be accessed directly.
*
diff --git a/extras/hid2hci/.gitignore b/extras/hid2hci/.gitignore
deleted file mode 100644
index efd972905a..0000000000
--- a/extras/hid2hci/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-hid2hci
diff --git a/extras/hid2hci/70-hid2hci.rules b/extras/hid2hci/70-hid2hci.rules
deleted file mode 100644
index 3b36629d4b..0000000000
--- a/extras/hid2hci/70-hid2hci.rules
+++ /dev/null
@@ -1,30 +0,0 @@
-# do not edit this file, it will be overwritten on update
-
-ACTION=="remove", GOTO="hid2hci_end"
-SUBSYSTEM!="usb", GOTO="hid2hci_end"
-
-# Variety of Dell Bluetooth devices - match on a mouse device that is
-# self powered and where a HID report needs to be sent to switch modes
-# Known supported devices: 413c:8154, 413c:8158, 413c:8162
-ATTR{bInterfaceClass}=="03", ATTR{bInterfaceSubClass}=="01", ATTR{bInterfaceProtocol}=="02", \
- ATTRS{bDeviceClass}=="00", ATTRS{idVendor}=="413c", ATTRS{bmAttributes}=="e0", \
- RUN+="hid2hci --method=dell --devpath=%p", ENV{HID2HCI_SWITCH}="1"
-
-# Logitech devices
-KERNEL=="hiddev*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c70[35e]", \
- RUN+="hid2hci --method=logitech-hid --devpath=%p"
-KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c70[4abc]|c71[34bc]", \
- RUN+="hid2hci --method=logitech-hid --devpath=%p"
-
-ENV{DEVTYPE}!="usb_device", GOTO="hid2hci_end"
-
-# When a Dell device recovers from S3, the mouse child needs to be repoked
-# Unfortunately the only event seen is the BT device disappearing, so the mouse
-# device needs to be chased down on the USB bus.
-ATTR{bDeviceClass}=="e0", ATTR{bDeviceSubClass}=="01", ATTR{bDeviceProtocol}=="01", ATTR{idVendor}=="413c", \
- ENV{REMOVE_CMD}="/sbin/udevadm trigger --action=change --subsystem-match=usb --property-match=HID2HCI_SWITCH=1"
-
-# CSR devices
-ATTR{idVendor}=="0a12|0458|05ac", ATTR{idProduct}=="1000", RUN+="hid2hci --method=csr --devpath=%p"
-
-LABEL="hid2hci_end"
diff --git a/extras/hid2hci/hid2hci.c b/extras/hid2hci/hid2hci.c
deleted file mode 100644
index 839c4fbce7..0000000000
--- a/extras/hid2hci/hid2hci.c
+++ /dev/null
@@ -1,350 +0,0 @@
-/*
- * hid2hci : switch the radio on devices that support
- * it from HID to HCI and back
- *
- * Copyright (C) 2003-2009 Marcel Holtmann <marcel@holtmann.org>
- * Copyright (C) 2008-2009 Mario Limonciello <mario_limonciello@dell.com>
- * Copyright (C) 2009 Kay Sievers <kay.sievers@vrfy.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <stdio.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdint.h>
-#include <string.h>
-#include <getopt.h>
-#include <sys/ioctl.h>
-#include <linux/types.h>
-#include <linux/hiddev.h>
-#include <usb.h>
-
-#include "libudev.h"
-#include "libudev-private.h"
-
-enum mode {
- HCI = 0,
- HID = 1,
-};
-
-static int usb_switch_csr(struct usb_dev_handle *dev, enum mode mode)
-{
- int err;
-
- err = usb_control_msg(dev,
- USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, mode, 0, NULL, 0, 10000);
- if (err == 0) {
- err = -1;
- errno = EALREADY;
- } else {
- if (errno == ETIMEDOUT)
- err = 0;
- }
- return err;
-}
-
-static int hid_logitech_send_report(int fd, const char *buf, size_t size)
-{
- struct hiddev_report_info rinfo;
- struct hiddev_usage_ref uref;
- unsigned int i;
- int err;
-
- for (i = 0; i < size; i++) {
- memset(&uref, 0, sizeof(uref));
- uref.report_type = HID_REPORT_TYPE_OUTPUT;
- uref.report_id = 0x10;
- uref.field_index = 0;
- uref.usage_index = i;
- uref.usage_code = 0xff000001;
- uref.value = buf[i] & 0x000000ff;
- err = ioctl(fd, HIDIOCSUSAGE, &uref);
- if (err < 0)
- return err;
- }
-
- memset(&rinfo, 0, sizeof(rinfo));
- rinfo.report_type = HID_REPORT_TYPE_OUTPUT;
- rinfo.report_id = 0x10;
- rinfo.num_fields = 1;
- err = ioctl(fd, HIDIOCSREPORT, &rinfo);
-
- return err;
-}
-
-static int hid_switch_logitech(const char *filename)
-{
- char rep1[] = { 0xff, 0x80, 0x80, 0x01, 0x00, 0x00 };
- char rep2[] = { 0xff, 0x80, 0x00, 0x00, 0x30, 0x00 };
- char rep3[] = { 0xff, 0x81, 0x80, 0x00, 0x00, 0x00 };
- int fd;
- int err = -1;
-
- fd = open(filename, O_RDWR);
- if (fd < 0)
- return err;
-
- err = ioctl(fd, HIDIOCINITREPORT, 0);
- if (err < 0)
- goto out;
-
- err = hid_logitech_send_report(fd, rep1, sizeof(rep1));
- if (err < 0)
- goto out;
-
- err = hid_logitech_send_report(fd, rep2, sizeof(rep2));
- if (err < 0)
- goto out;
-
- err = hid_logitech_send_report(fd, rep3, sizeof(rep3));
-out:
- close(fd);
- return err;
-}
-
-static int usb_switch_dell(struct usb_dev_handle *dev, enum mode mode)
-{
- char report[] = { 0x7f, 0x00, 0x00, 0x00 };
- int err;
-
- switch (mode) {
- case HCI:
- report[1] = 0x13;
- break;
- case HID:
- report[1] = 0x14;
- break;
- }
-
- /* Don't need to check return, as might not be in use */
- usb_detach_kernel_driver_np(dev, 0);
-
- if (usb_claim_interface(dev, 0) < 0)
- return -EIO;
-
- err = usb_control_msg(dev,
- USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- USB_REQ_SET_CONFIGURATION, 0x7f | (0x03 << 8), 0,
- report, sizeof(report), 5000);
-
- if (err == 0) {
- err = -1;
- errno = EALREADY;
- } else {
- if (errno == ETIMEDOUT)
- err = 0;
- }
- return err;
-}
-
-/*
- * The braindead libusb needs to scan and open all devices, just to
- * to find the device we already have. This needs to be fixed in libusb
- * or it will be ripped out and we carry our own code.
- */
-static struct usb_device *usb_device_open_from_udev(struct udev_device *usb_dev)
-{
- struct usb_bus *bus;
- const char *str;
- int busnum;
- int devnum;
-
- str = udev_device_get_sysattr_value(usb_dev, "busnum");
- if (str == NULL)
- return NULL;
- busnum = strtol(str, NULL, 0);
-
- str = udev_device_get_sysattr_value(usb_dev, "devnum");
- if (str == NULL)
- return NULL;
- devnum = strtol(str, NULL, 0);
-
- usb_init();
- usb_find_busses();
- usb_find_devices();
-
- for (bus = usb_get_busses(); bus; bus = bus->next) {
- struct usb_device *dev;
-
- if (strtol(bus->dirname, NULL, 10) != busnum)
- continue;
-
- for (dev = bus->devices; dev; dev = dev->next) {
- if (dev->devnum == devnum)
- return dev;
- }
- }
-
- return NULL;
-}
-
-static struct usb_dev_handle *find_device(struct udev_device *udev_dev)
-{
- struct usb_device *dev;
-
- dev = usb_device_open_from_udev(udev_dev);
- if (dev == NULL)
- return NULL;
- return usb_open(dev);
-}
-
-static void usage(const char *error)
-{
- if (error)
- fprintf(stderr,"\n%s\n", error);
- else
- printf("hid2hci - Bluetooth HID to HCI mode switching utility\n\n");
-
- printf("Usage: hid2hci [options]\n"
- " --mode= mode to switch to [hid|hci] (default hci)\n"
- " --devpath= sys device path\n"
- " --method= method to use to switch [csr|logitech-hid|dell]\n"
- " --help\n\n");
-}
-
-int main(int argc, char *argv[])
-{
- static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "mode", required_argument, NULL, 'm' },
- { "devpath", required_argument, NULL, 'p' },
- { "method", required_argument, NULL, 'M' },
- { }
- };
- enum method {
- METHOD_UNDEF,
- METHOD_CSR,
- METHOD_LOGITECH_HID,
- METHOD_DELL,
- } method = METHOD_UNDEF;
- struct udev *udev;
- struct udev_device *udev_dev = NULL;
- char syspath[UTIL_PATH_SIZE];
- int (*usb_switch)(struct usb_dev_handle *dev, enum mode mode) = NULL;
- enum mode mode = HCI;
- const char *devpath = NULL;
- int err = -1;
- int rc = 1;
-
- for (;;) {
- int option;
-
- option = getopt_long(argc, argv, "m:p:M:qh", options, NULL);
- if (option == -1)
- break;
-
- switch (option) {
- case 'm':
- if (!strcmp(optarg, "hid")) {
- mode = HID;
- } else if (!strcmp(optarg, "hci")) {
- mode = HCI;
- } else {
- usage("error: undefined radio mode\n");
- exit(1);
- }
- break;
- case 'p':
- devpath = optarg;
- break;
- case 'M':
- if (!strcmp(optarg, "csr")) {
- method = METHOD_CSR;
- usb_switch = usb_switch_csr;
- } else if (!strcmp(optarg, "logitech-hid")) {
- method = METHOD_LOGITECH_HID;
- } else if (!strcmp(optarg, "dell")) {
- method = METHOD_DELL;
- usb_switch = usb_switch_dell;
- } else {
- usage("error: undefined switching method\n");
- exit(1);
- }
- break;
- case 'h':
- usage(NULL);
- default:
- exit(1);
- }
- }
-
- if (!devpath || method == METHOD_UNDEF) {
- usage("error: --devpath= and --method= must be defined\n");
- exit(1);
- }
-
- udev = udev_new();
- if (udev == NULL)
- goto exit;
-
- util_strscpyl(syspath, sizeof(syspath), udev_get_sys_path(udev), devpath, NULL);
- udev_dev = udev_device_new_from_syspath(udev, syspath);
- if (udev_dev == NULL) {
- fprintf(stderr, "error: could not find '%s'\n", devpath);
- goto exit;
- }
-
- switch (method) {
- case METHOD_CSR:
- case METHOD_DELL: {
- struct udev_device *dev;
- struct usb_dev_handle *handle;
- const char *type;
-
- /* get the parent usb_device if needed */
- dev = udev_dev;
- type = udev_device_get_devtype(dev);
- if (type == NULL || strcmp(type, "usb_device") != 0) {
- dev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device");
- if (dev == NULL) {
- fprintf(stderr, "error: could not find usb_device for '%s'\n", devpath);
- goto exit;
- }
- }
-
- handle = find_device(dev);
- if (handle == NULL) {
- fprintf(stderr, "error: unable to handle '%s'\n",
- udev_device_get_syspath(dev));
- goto exit;
- }
- err = usb_switch(handle, mode);
- break;
- }
- case METHOD_LOGITECH_HID: {
- const char *device;
-
- device = udev_device_get_devnode(udev_dev);
- if (device == NULL) {
- fprintf(stderr, "error: could not find hiddev device node\n");
- goto exit;
- }
- err = hid_switch_logitech(device);
- break;
- }
- default:
- break;
- }
-
- if (err < 0)
- fprintf(stderr, "error: switching device '%s' failed.\n",
- udev_device_get_syspath(udev_dev));
-exit:
- udev_device_unref(udev_dev);
- udev_unref(udev);
- return rc;
-}
diff --git a/extras/keymap/95-keymap.rules b/extras/keymap/95-keymap.rules
index 350f111504..9fb90048e8 100644
--- a/extras/keymap/95-keymap.rules
+++ b/extras/keymap/95-keymap.rules
@@ -22,7 +22,7 @@ LABEL="keyboard_usbcheck"
ENV{ID_VENDOR}=="Logitech*", ATTRS{name}=="Logitech USB Multimedia Keyboard", RUN+="keymap $name logitech-wave"
ENV{ID_VENDOR}=="Logitech*", ATTRS{name}=="Logitech USB Receiver", RUN+="keymap $name logitech-wave-cordless"
# Logitech Cordless Wave Pro looks slightly weird; some hotkeys are coming through the mouse interface
-ENV{ID_VENDOR_ID}=="046d", ENV{ID_MODEL_ID}=="c529", ATTRS{name}=="Logitech USB Receiver", RUN+="keymap $name logitech-wave-pro-cordless"
+ENV{ID_VENDOR_ID}=="046d", ENV{ID_MODEL_ID}=="c52[9b]", ATTRS{name}=="Logitech USB Receiver", RUN+="keymap $name logitech-wave-pro-cordless"
ENV{ID_VENDOR}=="Lite-On_Technology_Corp*", ATTRS{name}=="Lite-On Technology Corp. ThinkPad USB Keyboard with TrackPoint", RUN+="keymap $name lenovo-thinkpad-usb-keyboard-trackpoint"
ENV{ID_VENDOR_ID}=="04b3", ENV{ID_MODEL_ID}=="301[89]", RUN+="keymap $name ibm-thinkpad-usb-keyboard-trackpoint"
diff --git a/extras/keymap/keymap.c b/extras/keymap/keymap.c
index eabd1e44d8..597a53b467 100644
--- a/extras/keymap/keymap.c
+++ b/extras/keymap/keymap.c
@@ -192,8 +192,7 @@ static int merge_table(int fd, const char *filename) {
f = fopen(filename, "r");
if (!f) {
perror(filename);
- r = -1;
- goto fail;
+ return -1;
}
while (!feof(f)) {
@@ -243,6 +242,7 @@ static int merge_table(int fd, const char *filename) {
scancode, new_keycode, old_keycode);
}
fail:
+ fclose(f);
return r;
}
diff --git a/extras/path_id/path_id.c b/extras/path_id/path_id.c
index c19bfd0f8d..62d26d8c4d 100644
--- a/extras/path_id/path_id.c
+++ b/extras/path_id/path_id.c
@@ -75,6 +75,21 @@ static int path_prepend(char **path, const char *fmt, ...)
return 0;
}
+/*
+** Linux only supports 32 bit luns.
+** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details.
+*/
+static int format_lun_number(struct udev_device *dev, char **path)
+{
+ unsigned long lun = strtoul(udev_device_get_sysnum(dev), NULL, 10);
+
+ /* address method 0, peripheral device addressing with bus id of zero */
+ if (lun < 256)
+ return path_prepend(path, "lun-%d", lun);
+ /* handle all other lun addressing methods by using a variant of the original lun format */
+ return path_prepend(path, "lun-0x%04x%04x00000000", (lun & 0xffff), (lun >> 16) & 0xffff);
+}
+
static struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys)
{
struct udev_device *parent = dev;
@@ -97,7 +112,7 @@ static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent,
struct udev_device *targetdev;
struct udev_device *fcdev = NULL;
const char *port;
- unsigned int lun;
+ char *lun = NULL;;
targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
if (targetdev == NULL)
@@ -112,8 +127,10 @@ static struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent,
goto out;
}
- lun = strtoul(udev_device_get_sysnum(parent), NULL, 10);
- path_prepend(path, "fc-%s:0x%04x%04x00000000", port, lun & 0xffff, (lun >> 16) & 0xffff);
+ format_lun_number(parent, &lun);
+ path_prepend(path, "fc-%s-%s", port, lun);
+ if (lun)
+ free(lun);
out:
udev_device_unref(fcdev);
return parent;
@@ -121,7 +138,39 @@ out:
static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path)
{
- return NULL;
+ struct udev *udev = udev_device_get_udev(parent);
+ struct udev_device *targetdev;
+ struct udev_device *target_parent;
+ struct udev_device *sasdev;
+ const char *sas_address;
+ char *lun = NULL;
+
+ targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target");
+ if (targetdev == NULL)
+ return NULL;
+
+ target_parent = udev_device_get_parent(targetdev);
+ if (target_parent == NULL)
+ return NULL;
+
+ sasdev = udev_device_new_from_subsystem_sysname(udev, "sas_device",
+ udev_device_get_sysname(target_parent));
+ if (sasdev == NULL)
+ return NULL;
+
+ sas_address = udev_device_get_sysattr_value(sasdev, "sas_address");
+ if (sas_address == NULL) {
+ parent = NULL;
+ goto out;
+ }
+
+ format_lun_number(parent, &lun);
+ path_prepend(path, "sas-%s-%s", sas_address, lun);
+ if (lun)
+ free(lun);
+out:
+ udev_device_unref(sasdev);
+ return parent;
}
static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path)
@@ -134,18 +183,17 @@ static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **
struct udev_device *conndev = NULL;
const char *addr;
const char *port;
+ char *lun = NULL;
/* find iscsi session */
transportdev = parent;
- while (1) {
+ for (;;) {
transportdev = udev_device_get_parent(transportdev);
if (transportdev == NULL)
return NULL;
if (strncmp(udev_device_get_sysname(transportdev), "session", 7) == 0)
break;
}
- if (transportdev == NULL)
- return NULL;
/* find iscsi session device */
sessiondev = udev_device_new_from_subsystem_sysname(udev, "iscsi_session", udev_device_get_sysname(transportdev));
@@ -174,7 +222,10 @@ static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **
goto out;
}
- path_prepend(path, "ip-%s:%s-iscsi-%s-lun-%s", addr, port, target, udev_device_get_sysnum(parent));
+ format_lun_number(parent, &lun);
+ path_prepend(path, "ip-%s:%s-iscsi-%s-%s", addr, port, target, lun);
+ if (lun)
+ free(lun);
out:
udev_device_unref(sessiondev);
udev_device_unref(conndev);
@@ -388,8 +439,6 @@ int main(int argc, char **argv)
printf("Usage: path_id [--debug] [--help] <devpath>\n"
" --debug print debug information\n"
" --help print this help text\n\n");
- default:
- rc = 1;
goto exit;
}
}
diff --git a/extras/rule_generator/75-persistent-net-generator.rules b/extras/rule_generator/75-persistent-net-generator.rules
index f150e55208..539807c861 100644
--- a/extras/rule_generator/75-persistent-net-generator.rules
+++ b/extras/rule_generator/75-persistent-net-generator.rules
@@ -33,6 +33,8 @@ ENV{MATCHIFTYPE}="$attr{type}"
ENV{MATCHADDR}=="52:54:00:*", GOTO="persistent_net_generator_end"
# ignore VMWare virtual interfaces
ENV{MATCHADDR}=="00:0c:29:*|00:50:56:*", GOTO="persistent_net_generator_end"
+# ignore Hyper-V virtual interfaces
+ENV{MATCHADDR}=="00:15:5d:*", GOTO="persistent_net_generator_end"
# These vendors are known to violate the local MAC address assignment scheme
# Interlan, DEC (UNIBUS or QBUS), Apollo, Cisco, Racal-Datacom
diff --git a/extras/scsi_id/scsi_id.c b/extras/scsi_id/scsi_id.c
index 6c8826568f..c14d411841 100644
--- a/extras/scsi_id/scsi_id.c
+++ b/extras/scsi_id/scsi_id.c
@@ -199,6 +199,7 @@ static int get_file_options(struct udev *udev,
*/
buffer = malloc(MAX_BUFFER_LEN);
if (!buffer) {
+ fclose(fd);
err(udev, "can't allocate memory\n");
return -1;
}
diff --git a/extras/scsi_id/scsi_serial.c b/extras/scsi_id/scsi_serial.c
index 516a6f16c5..5cde9a54a0 100644
--- a/extras/scsi_id/scsi_serial.c
+++ b/extras/scsi_id/scsi_serial.c
@@ -141,13 +141,13 @@ static int sg_err_category_new(struct udev *udev,
}
return SG_ERR_CAT_SENSE;
}
- if (!host_status) {
+ if (host_status) {
if ((host_status == DID_NO_CONNECT) ||
(host_status == DID_BUS_BUSY) ||
(host_status == DID_TIME_OUT))
return SG_ERR_CAT_TIMEOUT;
}
- if (!driver_status) {
+ if (driver_status) {
if (driver_status == DRIVER_TIMEOUT)
return SG_ERR_CAT_TIMEOUT;
}
@@ -322,8 +322,10 @@ static int scsi_inquiry(struct udev *udev,
{ INQUIRY_CMD, evpd, page, 0, buflen, 0 };
unsigned char sense[SENSE_BUFF_LEN];
void *io_buf;
- int retval;
+ struct sg_io_v4 io_v4;
+ struct sg_io_hdr io_hdr;
int retry = 3; /* rather random */
+ int retval;
if (buflen > SCSI_INQ_BUFF_LEN) {
info(udev, "buflen %d too long\n", buflen);
@@ -334,8 +336,6 @@ resend:
dbg(udev, "%s evpd %d, page 0x%x\n", dev_scsi->kernel, evpd, page);
if (dev_scsi->use_sg == 4) {
- struct sg_io_v4 io_v4;
-
memset(&io_v4, 0, sizeof(struct sg_io_v4));
io_v4.guard = 'Q';
io_v4.protocol = BSG_PROTOCOL_SCSI;
@@ -348,8 +348,6 @@ resend:
io_v4.din_xferp = (uintptr_t)buf;
io_buf = (void *)&io_v4;
} else {
- struct sg_io_hdr io_hdr;
-
memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
io_hdr.interface_id = 'S';
io_hdr.cmd_len = sizeof(inq_cmd);
diff --git a/extras/udev-acl/udev-acl.c b/extras/udev-acl/udev-acl.c
index 31e9991a51..35bad76034 100644
--- a/extras/udev-acl/udev-acl.c
+++ b/extras/udev-acl/udev-acl.c
@@ -258,11 +258,6 @@ static int consolekit_called(const char *ck_action, uid_t *uid, uid_t *uid2, con
u = u2;
}
break;
- case ACTION_NONE:
- break;
- default:
- g_assert_not_reached();
- break;
}
*remove_session_id = old_session;
@@ -364,7 +359,6 @@ int main (int argc, char* argv[])
break;
case 'h':
printf("Usage: udev-acl --action=ACTION [--device=DEVICEFILE] [--user=UID]\n\n");
- default:
goto out;
}
}
diff --git a/extras/usb_id/usb_id.c b/extras/usb_id/usb_id.c
index fabd09283d..3a7a0ffd49 100644
--- a/extras/usb_id/usb_id.c
+++ b/extras/usb_id/usb_id.c
@@ -503,8 +503,6 @@ int main(int argc, char **argv)
" --num-info use numerical values\n"
" --export print values as environment keys\n"
" --help print this help text\n\n");
- default:
- retval = 1;
goto exit;
}
}
diff --git a/init/udev-settle.service.in b/init/udev-settle.service.in
index bda8626b2d..d7d6f782cb 100644
--- a/init/udev-settle.service.in
+++ b/init/udev-settle.service.in
@@ -17,6 +17,7 @@ Before=basic.target
[Service]
Type=oneshot
+TimeoutSec=180
RemainAfterExit=yes
ExecStart=@sbindir@/udevadm settle
diff --git a/init/udev.service.in b/init/udev.service.in
index c02a4b4d85..e85974a575 100644
--- a/init/udev.service.in
+++ b/init/udev.service.in
@@ -1,10 +1,12 @@
[Unit]
Description=udev Kernel Device Manager
-Requires=udev.socket
+Wants=udev.socket
After=udev.socket
Before=basic.target
DefaultDependencies=no
[Service]
Type=notify
+OOMScoreAdjust=-1000
+Restart=on-failure
ExecStart=@sbindir@/udevd
diff --git a/init/udev.socket b/init/udev.socket
index 18a154fc82..f45a1a6fc9 100644
--- a/init/udev.socket
+++ b/init/udev.socket
@@ -1,6 +1,7 @@
[Unit]
-Description=udev Kernel Device Manager Socket
+Description=udev Kernel Device Manager Sockets
DefaultDependencies=no
[Socket]
-ListenDatagram=@/org/kernel/udev/udevd
+ListenSequentialPacket=@/org/kernel/udev/udevd
+ListenNetlink=kobject-uevent 1
diff --git a/libudev/libudev-ctrl.c b/libudev/libudev-ctrl.c
index 63bf539197..61e0243383 100644
--- a/libudev/libudev-ctrl.c
+++ b/libudev/libudev-ctrl.c
@@ -16,6 +16,7 @@
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
+#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -33,7 +34,8 @@ enum udev_ctrl_msg_type {
UDEV_CTRL_RELOAD_RULES,
UDEV_CTRL_SET_ENV,
UDEV_CTRL_SET_CHILDREN_MAX,
- UDEV_CTRL_SETTLE,
+ UDEV_CTRL_PING,
+ UDEV_CTRL_EXIT,
};
struct udev_ctrl_msg_wire {
@@ -48,9 +50,8 @@ struct udev_ctrl_msg_wire {
struct udev_ctrl_msg {
int refcount;
- struct udev_ctrl *uctrl;
+ struct udev_ctrl_connection *conn;
struct udev_ctrl_msg_wire ctrl_msg_wire;
- pid_t pid;
};
struct udev_ctrl {
@@ -59,6 +60,14 @@ struct udev_ctrl {
int sock;
struct sockaddr_un saddr;
socklen_t addrlen;
+ bool bound;
+ bool connected;
+};
+
+struct udev_ctrl_connection {
+ int refcount;
+ struct udev_ctrl *uctrl;
+ int sock;
};
static struct udev_ctrl *udev_ctrl_new(struct udev *udev)
@@ -73,7 +82,7 @@ static struct udev_ctrl *udev_ctrl_new(struct udev *udev)
return uctrl;
}
-struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socket_path)
+struct udev_ctrl *udev_ctrl_new_from_socket_fd(struct udev *udev, const char *socket_path, int fd)
{
struct udev_ctrl *uctrl;
@@ -81,11 +90,16 @@ struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socke
if (uctrl == NULL)
return NULL;
- uctrl->sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
- if (uctrl->sock < 0) {
- err(udev, "error getting socket: %m\n");
- udev_ctrl_unref(uctrl);
- return NULL;
+ if (fd < 0) {
+ uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
+ if (uctrl->sock < 0) {
+ err(udev, "error getting socket: %m\n");
+ udev_ctrl_unref(uctrl);
+ return NULL;
+ }
+ } else {
+ uctrl->bound = true;
+ uctrl->sock = fd;
}
uctrl->saddr.sun_family = AF_LOCAL;
@@ -97,33 +111,32 @@ struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socke
return uctrl;
}
-struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd)
+struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socket_path)
{
- struct udev_ctrl *uctrl;
-
- uctrl = udev_ctrl_new(udev);
- if (uctrl == NULL)
- return NULL;
- uctrl->sock = fd;
-
- return uctrl;
+ return udev_ctrl_new_from_socket_fd(udev, socket_path, -1);
}
int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl)
{
int err;
- const int on = 1;
- if (uctrl->addrlen > 0) {
+ if (!uctrl->bound) {
err = bind(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen);
if (err < 0) {
+ err = -errno;
err(uctrl->udev, "bind failed: %m\n");
return err;
}
- }
- /* enable receiving of the sender credentials */
- setsockopt(uctrl->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+ err = listen(uctrl->sock, 0);
+ if (err < 0) {
+ err = -errno;
+ err(uctrl->udev, "listen failed: %m\n");
+ return err;
+ }
+
+ uctrl->bound = true;
+ }
return 0;
}
@@ -140,16 +153,17 @@ struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl)
return uctrl;
}
-void udev_ctrl_unref(struct udev_ctrl *uctrl)
+struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl)
{
if (uctrl == NULL)
- return;
+ return NULL;
uctrl->refcount--;
if (uctrl->refcount > 0)
- return;
+ return uctrl;
if (uctrl->sock >= 0)
close(uctrl->sock);
free(uctrl);
+ return NULL;
}
int udev_ctrl_get_fd(struct udev_ctrl *uctrl)
@@ -159,10 +173,57 @@ int udev_ctrl_get_fd(struct udev_ctrl *uctrl)
return uctrl->sock;
}
-static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf)
+struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl)
+{
+ struct udev_ctrl_connection *conn;
+ const int on = 1;
+
+ conn = calloc(1, sizeof(struct udev_ctrl_connection));
+ if (conn == NULL)
+ return NULL;
+ conn->refcount = 1;
+ conn->uctrl = uctrl;
+
+ conn->sock = accept4(uctrl->sock, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK);
+ if (conn->sock < 0) {
+ if (errno != EINTR)
+ err(uctrl->udev, "unable to receive ctrl connection: %m\n");
+ free(conn);
+ return NULL;
+ }
+
+ /* enable receiving of the sender credentials */
+ setsockopt(conn->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+ udev_ctrl_ref(uctrl);
+ return conn;
+}
+
+struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn)
+{
+ if (conn == NULL)
+ return NULL;
+ conn->refcount++;
+ return conn;
+}
+
+struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn)
+{
+ if (conn == NULL)
+ return NULL;
+ conn->refcount--;
+ if (conn->refcount > 0)
+ return conn;
+ if (conn->sock >= 0)
+ close(conn->sock);
+ udev_ctrl_unref(conn->uctrl);
+ free(conn);
+ return NULL;
+}
+
+static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf, int timeout)
{
struct udev_ctrl_msg_wire ctrl_msg_wire;
- int err;
+ int err = 0;
memset(&ctrl_msg_wire, 0x00, sizeof(struct udev_ctrl_msg_wire));
strcpy(ctrl_msg_wire.version, "udev-" VERSION);
@@ -174,51 +235,89 @@ static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int
else
ctrl_msg_wire.intval = intval;
- err = sendto(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0,
- (struct sockaddr *)&uctrl->saddr, uctrl->addrlen);
- if (err == -1) {
- err(uctrl->udev, "error sending message: %m\n");
+ if (!uctrl->connected) {
+ if (connect(uctrl->sock, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen) < 0) {
+ err = -errno;
+ goto out;
+ }
+ uctrl->connected = true;
+ }
+ if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0) {
+ err = -errno;
+ goto out;
+ }
+
+ /* wait for peer message handling or disconnect */
+ for (;;) {
+ struct pollfd pfd[1];
+ int r;
+
+ pfd[0].fd = uctrl->sock;
+ pfd[0].events = POLLIN;
+ r = poll(pfd, 1, timeout * 1000);
+ if (r < 0) {
+ if (errno == EINTR)
+ continue;
+ err = -errno;
+ break;
+ }
+
+ if (r > 0 && pfd[0].revents & POLLERR) {
+ err = -EIO;
+ break;
+ }
+
+ if (r == 0)
+ err = -ETIMEDOUT;
+ break;
}
+out:
return err;
}
-int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority)
+int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout)
{
- return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL);
+ return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL, timeout);
}
-int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl)
+int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout)
{
- return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL);
+ return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL, timeout);
}
-int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl)
+int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout)
{
- return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL);
+ return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL, timeout);
}
-int udev_ctrl_send_reload_rules(struct udev_ctrl *uctrl)
+int udev_ctrl_send_reload_rules(struct udev_ctrl *uctrl, int timeout)
{
- return ctrl_send(uctrl, UDEV_CTRL_RELOAD_RULES, 0, NULL);
+ return ctrl_send(uctrl, UDEV_CTRL_RELOAD_RULES, 0, NULL, timeout);
}
-int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key)
+int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout)
{
- return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key);
+ return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key, timeout);
}
-int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count)
+int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout)
{
- return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL);
+ return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL, timeout);
}
-int udev_ctrl_send_settle(struct udev_ctrl *uctrl)
+int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout)
{
- return ctrl_send(uctrl, UDEV_CTRL_SETTLE, 0, NULL);
+ return ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL, timeout);
}
-struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl *uctrl)
+int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout)
{
+ return ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL, timeout);
+}
+
+struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn)
+{
+ struct udev *udev = conn->uctrl->udev;
struct udev_ctrl_msg *uctrl_msg;
ssize_t size;
struct msghdr smsg;
@@ -231,43 +330,40 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl *uctrl)
if (uctrl_msg == NULL)
return NULL;
uctrl_msg->refcount = 1;
- uctrl_msg->uctrl = uctrl;
+ uctrl_msg->conn = conn;
+ udev_ctrl_connection_ref(conn);
iov.iov_base = &uctrl_msg->ctrl_msg_wire;
iov.iov_len = sizeof(struct udev_ctrl_msg_wire);
-
memset(&smsg, 0x00, sizeof(struct msghdr));
smsg.msg_iov = &iov;
smsg.msg_iovlen = 1;
smsg.msg_control = cred_msg;
smsg.msg_controllen = sizeof(cred_msg);
-
- size = recvmsg(uctrl->sock, &smsg, 0);
+ size = recvmsg(conn->sock, &smsg, 0);
if (size < 0) {
- err(uctrl->udev, "unable to receive user udevd message: %m\n");
+ err(udev, "unable to receive user udevd message: %m\n");
goto err;
}
cmsg = CMSG_FIRSTHDR(&smsg);
cred = (struct ucred *) CMSG_DATA(cmsg);
if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
- err(uctrl->udev, "no sender credentials received, message ignored\n");
+ err(udev, "no sender credentials received, message ignored\n");
goto err;
}
if (cred->uid != 0) {
- err(uctrl->udev, "sender uid=%i, message ignored\n", cred->uid);
+ err(udev, "sender uid=%i, message ignored\n", cred->uid);
goto err;
}
- uctrl_msg->pid = cred->pid;
-
if (uctrl_msg->ctrl_msg_wire.magic != UDEV_CTRL_MAGIC) {
- err(uctrl->udev, "message magic 0x%08x doesn't match, ignore it\n", uctrl_msg->ctrl_msg_wire.magic);
+ err(udev, "message magic 0x%08x doesn't match, ignore it\n", uctrl_msg->ctrl_msg_wire.magic);
goto err;
}
- dbg(uctrl->udev, "created ctrl_msg %p (%i)\n", uctrl_msg, uctrl_msg->ctrl_msg_wire.type);
+ dbg(udev, "created ctrl_msg %p (%i)\n", uctrl_msg, uctrl_msg->ctrl_msg_wire.type);
return uctrl_msg;
err:
udev_ctrl_msg_unref(uctrl_msg);
@@ -282,15 +378,17 @@ struct udev_ctrl_msg *udev_ctrl_msg_ref(struct udev_ctrl_msg *ctrl_msg)
return ctrl_msg;
}
-void udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg)
+struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg)
{
if (ctrl_msg == NULL)
- return;
+ return NULL;
ctrl_msg->refcount--;
if (ctrl_msg->refcount > 0)
- return;
- dbg(ctrl_msg->uctrl->udev, "release ctrl_msg %p\n", ctrl_msg);
+ return ctrl_msg;
+ dbg(ctrl_msg->conn->uctrl->udev, "release ctrl_msg %p\n", ctrl_msg);
+ udev_ctrl_connection_unref(ctrl_msg->conn);
free(ctrl_msg);
+ return NULL;
}
int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg)
@@ -335,9 +433,16 @@ int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg)
return -1;
}
-pid_t udev_ctrl_get_settle(struct udev_ctrl_msg *ctrl_msg)
+int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg)
+{
+ if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_PING)
+ return 1;
+ return -1;
+}
+
+int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg)
{
- if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SETTLE)
- return ctrl_msg->pid;
+ if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_EXIT)
+ return 1;
return -1;
}
diff --git a/libudev/libudev-device-private.c b/libudev/libudev-device-private.c
index 6d72d328bd..b78b6c9553 100644
--- a/libudev/libudev-device-private.c
+++ b/libudev/libudev-device-private.c
@@ -86,7 +86,7 @@ static bool device_has_info(struct udev_device *udev_device)
if (udev_device_get_devlink_priority(udev_device) != 0)
return true;
udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device))
- if (udev_list_entry_get_flags(list_entry))
+ if (udev_list_entry_get_num(list_entry))
return true;
if (udev_device_get_tags_list_entry(udev_device) != NULL)
return true;
@@ -134,6 +134,13 @@ int udev_device_update_db(struct udev_device *udev_device)
return -1;
}
+ /*
+ * set 'sticky' bit to indicate that we should not clean the
+ * database when we transition from initramfs to the real root
+ */
+ if (udev_device_get_db_persist(udev_device))
+ fchmod(fileno(f), 01644);
+
if (has_info) {
size_t devlen = strlen(udev_get_dev_path(udev))+1;
struct udev_list_entry *list_entry;
@@ -150,7 +157,7 @@ int udev_device_update_db(struct udev_device *udev_device)
if (udev_device_get_usec_initialized(udev_device) > 0)
fprintf(f, "I:%llu\n", udev_device_get_usec_initialized(udev_device));
udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) {
- if (!udev_list_entry_get_flags(list_entry))
+ if (!udev_list_entry_get_num(list_entry))
continue;
fprintf(f, "E:%s=%s\n",
udev_list_entry_get_name(list_entry),
diff --git a/libudev/libudev-device.c b/libudev/libudev-device.c
index ccd4a70677..d57a9eb597 100644
--- a/libudev/libudev-device.c
+++ b/libudev/libudev-device.c
@@ -51,6 +51,7 @@ struct udev_device {
char *sysname;
const char *sysnum;
char *devnode;
+ mode_t devnode_mode;
char *subsystem;
char *devtype;
char *driver;
@@ -69,7 +70,6 @@ struct udev_device {
struct udev_list_node tags_list;
unsigned long long int seqnum;
unsigned long long int usec_initialized;
- int event_timeout;
int timeout;
int devlink_priority;
int refcount;
@@ -89,6 +89,7 @@ struct udev_device {
bool uevent_loaded;
bool is_initialized;
bool sysattr_list_read;
+ bool db_persist;
};
struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value)
@@ -103,7 +104,7 @@ struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device
udev_list_entry_delete(list_entry);
return NULL;
}
- return udev_list_entry_add(udev_device->udev, &udev_device->properties_list, key, value, 1, 0);
+ return udev_list_entry_add(udev_device->udev, &udev_device->properties_list, key, value, UDEV_LIST_UNIQUE);
}
static struct udev_list_entry *udev_device_add_property_from_string(struct udev_device *udev_device, const char *property)
@@ -199,6 +200,8 @@ void udev_device_add_property_from_string_parse(struct udev_device *udev_device,
udev_device_set_timeout(udev_device, strtoull(&property[8], NULL, 10));
} else if (strncmp(property, "IFINDEX=", 8) == 0) {
udev_device_set_ifindex(udev_device, strtoull(&property[8], NULL, 10));
+ } else if (strncmp(property, "DEVMODE=", 8) == 0) {
+ udev_device_set_devnode_mode(udev_device, strtoul(&property[8], NULL, 8));
} else {
udev_device_add_property_from_string(udev_device, property);
}
@@ -289,7 +292,7 @@ int udev_device_read_db(struct udev_device *udev_device, const char *dbfile)
break;
case 'E':
entry = udev_device_add_property_from_string(udev_device, val);
- udev_list_entry_set_flags(entry, 1);
+ udev_list_entry_set_num(entry, true);
break;
case 'G':
udev_device_add_tag(udev_device, val);
@@ -343,6 +346,8 @@ int udev_device_read_uevent_file(struct udev_device *udev_device)
udev_device_set_ifindex(udev_device, strtoull(&line[8], NULL, 10));
else if (strncmp(line, "DEVNAME=", 8) == 0)
udev_device_set_knodename(udev_device, &line[8]);
+ else if (strncmp(line, "DEVMODE=", 8) == 0)
+ udev_device->devnode_mode = strtoul(&line[8], NULL, 8);
udev_device_add_property_from_string(udev_device, line);
}
@@ -375,7 +380,7 @@ struct udev_device *udev_device_new(struct udev *udev)
udev_list_init(&udev_device->sysattr_value_list);
udev_list_init(&udev_device->sysattr_list);
udev_list_init(&udev_device->tags_list);
- udev_device->event_timeout = -1;
+ udev_device->timeout = -1;
udev_device->watch_handle = -1;
/* copy global properties */
udev_list_entry_foreach(list_entry, udev_get_properties_list_entry(udev))
@@ -930,6 +935,13 @@ const char *udev_device_get_devnode(struct udev_device *udev_device)
return udev_device->devnode;
}
+mode_t udev_device_get_devnode_mode(struct udev_device *udev_device)
+{
+ if (!udev_device->info_loaded)
+ udev_device_read_uevent_file(udev_device);
+ return udev_device->devnode_mode;
+}
+
/**
* udev_device_get_subsystem:
* @udev_device: udev device
@@ -1163,7 +1175,7 @@ unsigned long long int udev_device_get_usec_since_initialized(struct udev_device
udev_device_read_db(udev_device, NULL);
if (udev_device->usec_initialized == 0)
return 0;
- now = usec_monotonic();
+ now = now_usec();
if (now == 0)
return 0;
return now - udev_device->usec_initialized;
@@ -1216,7 +1228,7 @@ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const
util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", sysattr, NULL);
if (lstat(path, &statbuf) != 0) {
dbg(udev_device->udev, "no attribute '%s', keep negative entry\n", path);
- udev_list_entry_add(udev_device->udev, &udev_device->sysattr_value_list, sysattr, NULL, 0, 0);
+ udev_list_entry_add(udev_device->udev, &udev_device->sysattr_value_list, sysattr, NULL, 0);
goto out;
}
@@ -1240,7 +1252,7 @@ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const
if (pos != NULL) {
pos = &pos[1];
dbg(udev_device->udev, "cache '%s' with link value '%s'\n", sysattr, pos);
- list_entry = udev_list_entry_add(udev_device->udev, &udev_device->sysattr_value_list, sysattr, pos, 0, 0);
+ list_entry = udev_list_entry_add(udev_device->udev, &udev_device->sysattr_value_list, sysattr, pos, 0);
val = udev_list_entry_get_value(list_entry);
}
@@ -1272,7 +1284,7 @@ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const
value[size] = '\0';
util_remove_trailing_chars(value, '\n');
dbg(udev_device->udev, "'%s' has attribute value '%s'\n", path, value);
- list_entry = udev_list_entry_add(udev_device->udev, &udev_device->sysattr_value_list, sysattr, value, 0, 0);
+ list_entry = udev_list_entry_add(udev_device->udev, &udev_device->sysattr_value_list, sysattr, value, 0);
val = udev_list_entry_get_value(list_entry);
out:
return val;
@@ -1311,7 +1323,7 @@ static int udev_device_sysattr_list_read(struct udev_device *udev_device)
continue;
udev_list_entry_add(udev_device->udev, &udev_device->sysattr_list,
- dent->d_name, NULL, 0, 0);
+ dent->d_name, NULL, 0);
num++;
}
@@ -1414,16 +1426,26 @@ int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode
return 0;
}
+int udev_device_set_devnode_mode(struct udev_device *udev_device, mode_t mode)
+{
+ char num[32];
+
+ udev_device->devnode_mode = mode;
+ snprintf(num, sizeof(num), "%#o", mode);
+ udev_device_add_property(udev_device, "DEVMODE", num);
+ return 0;
+}
+
int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink, int unique)
{
struct udev_list_entry *list_entry;
udev_device->devlinks_uptodate = false;
- list_entry = udev_list_entry_add(udev_device->udev, &udev_device->devlinks_list, devlink, NULL, 1, 0);
+ list_entry = udev_list_entry_add(udev_device->udev, &udev_device->devlinks_list, devlink, NULL, UDEV_LIST_UNIQUE);
if (list_entry == NULL)
return -ENOMEM;
if (unique)
- udev_list_entry_set_flags(list_entry, 1);
+ udev_list_entry_set_num(list_entry, true);
return 0;
}
@@ -1491,7 +1513,7 @@ int udev_device_add_tag(struct udev_device *udev_device, const char *tag)
if (strchr(tag, ':') != NULL || strchr(tag, ' ') != NULL)
return -EINVAL;
udev_device->tags_uptodate = false;
- if (udev_list_entry_add(udev_device->udev, &udev_device->tags_list, tag, NULL, 1, 0) != NULL)
+ if (udev_list_entry_add(udev_device->udev, &udev_device->tags_list, tag, NULL, UDEV_LIST_UNIQUE) != NULL)
return 0;
return -ENOMEM;
}
@@ -1689,22 +1711,10 @@ int udev_device_get_timeout(struct udev_device *udev_device)
int udev_device_set_timeout(struct udev_device *udev_device, int timeout)
{
- udev_device->timeout = timeout;
- return 0;
-}
-int udev_device_get_event_timeout(struct udev_device *udev_device)
-{
- if (!udev_device->info_loaded)
- udev_device_read_db(udev_device, NULL);
- return udev_device->event_timeout;
-}
-
-int udev_device_set_event_timeout(struct udev_device *udev_device, int event_timeout)
-{
char num[32];
- udev_device->event_timeout = event_timeout;
- snprintf(num, sizeof(num), "%u", event_timeout);
+ udev_device->timeout = timeout;
+ snprintf(num, sizeof(num), "%u", timeout);
udev_device_add_property(udev_device, "TIMEOUT", num);
return 0;
}
@@ -1774,3 +1784,13 @@ int udev_device_set_ifindex(struct udev_device *udev_device, int ifindex)
udev_device_add_property(udev_device, "IFINDEX", num);
return 0;
}
+
+bool udev_device_get_db_persist(struct udev_device *udev_device)
+{
+ return udev_device->db_persist;
+}
+
+void udev_device_set_db_persist(struct udev_device *udev_device)
+{
+ udev_device->db_persist = true;
+}
diff --git a/libudev/libudev-enumerate.c b/libudev/libudev-enumerate.c
index 6870bb6115..018d89cc04 100644
--- a/libudev/libudev-enumerate.c
+++ b/libudev/libudev-enumerate.c
@@ -295,24 +295,24 @@ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *ude
strncmp(entry->syspath, move_later->syspath, move_later_prefix) != 0) {
udev_list_entry_add(udev_enumerate->udev, &udev_enumerate->devices_list,
- move_later->syspath, NULL, 0, 0);
+ move_later->syspath, NULL, 0);
move_later = NULL;
}
udev_list_entry_add(udev_enumerate->udev, &udev_enumerate->devices_list,
- entry->syspath, NULL, 0, 0);
+ entry->syspath, NULL, 0);
}
if (move_later)
udev_list_entry_add(udev_enumerate->udev, &udev_enumerate->devices_list,
- move_later->syspath, NULL, 0, 0);
+ move_later->syspath, NULL, 0);
/* add and cleanup delayed devices from end of list */
for (i = max; i < udev_enumerate->devices_cur; i++) {
struct syspath *entry = &udev_enumerate->devices[i];
udev_list_entry_add(udev_enumerate->udev, &udev_enumerate->devices_list,
- entry->syspath, NULL, 0, 0);
+ entry->syspath, NULL, 0);
free(entry->syspath);
}
udev_enumerate->devices_cur = max;
@@ -336,7 +336,7 @@ int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, co
if (subsystem == NULL)
return 0;
if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
- &udev_enumerate->subsystem_match_list, subsystem, NULL, 1, 0) == NULL)
+ &udev_enumerate->subsystem_match_list, subsystem, NULL, UDEV_LIST_UNIQUE) == NULL)
return -ENOMEM;
return 0;
}
@@ -355,7 +355,7 @@ int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate,
if (subsystem == NULL)
return 0;
if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
- &udev_enumerate->subsystem_nomatch_list, subsystem, NULL, 1, 0) == NULL)
+ &udev_enumerate->subsystem_nomatch_list, subsystem, NULL, UDEV_LIST_UNIQUE) == NULL)
return -ENOMEM;
return 0;
}
@@ -375,7 +375,7 @@ int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, cons
if (sysattr == NULL)
return 0;
if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
- &udev_enumerate->sysattr_match_list, sysattr, value, 0, 0) == NULL)
+ &udev_enumerate->sysattr_match_list, sysattr, value, 0) == NULL)
return -ENOMEM;
return 0;
}
@@ -395,7 +395,7 @@ int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, co
if (sysattr == NULL)
return 0;
if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
- &udev_enumerate->sysattr_nomatch_list, sysattr, value, 0, 0) == NULL)
+ &udev_enumerate->sysattr_nomatch_list, sysattr, value, 0) == NULL)
return -ENOMEM;
return 0;
}
@@ -435,7 +435,7 @@ int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, con
if (property == NULL)
return 0;
if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
- &udev_enumerate->properties_match_list, property, value, 0, 0) == NULL)
+ &udev_enumerate->properties_match_list, property, value, 0) == NULL)
return -ENOMEM;
return 0;
}
@@ -454,7 +454,7 @@ int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const ch
if (tag == NULL)
return 0;
if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
- &udev_enumerate->tags_match_list, tag, NULL, 1, 0) == NULL)
+ &udev_enumerate->tags_match_list, tag, NULL, UDEV_LIST_UNIQUE) == NULL)
return -ENOMEM;
return 0;
}
@@ -499,7 +499,7 @@ int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, cons
if (sysname == NULL)
return 0;
if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
- &udev_enumerate->sysname_match_list, sysname, NULL, 1, 0) == NULL)
+ &udev_enumerate->sysname_match_list, sysname, NULL, UDEV_LIST_UNIQUE) == NULL)
return -ENOMEM;
return 0;
}
diff --git a/libudev/libudev-list.c b/libudev/libudev-list.c
index b5e96458cc..29453ac251 100644
--- a/libudev/libudev-list.c
+++ b/libudev/libudev-list.c
@@ -38,10 +38,10 @@ struct udev_list_entry {
struct udev_list_node *list;
char *name;
char *value;
- unsigned int flags;
+ int num;
};
-/* list head point to itself if empty */
+/* the list's head points to itself if empty */
void udev_list_init(struct udev_list_node *list)
{
list->next = list;
@@ -114,12 +114,12 @@ void udev_list_entry_insert_before(struct udev_list_entry *new, struct udev_list
struct udev_list_entry *udev_list_entry_add(struct udev *udev, struct udev_list_node *list,
const char *name, const char *value,
- int unique, int sort)
+ unsigned int flags)
{
struct udev_list_entry *entry_loop = NULL;
struct udev_list_entry *entry_new;
- if (unique)
+ if (flags & UDEV_LIST_UNIQUE) {
udev_list_entry_foreach(entry_loop, udev_list_get_entry(list)) {
if (strcmp(entry_loop->name, name) == 0) {
dbg(udev, "'%s' is already in the list\n", name);
@@ -136,12 +136,14 @@ struct udev_list_entry *udev_list_entry_add(struct udev *udev, struct udev_list_
return entry_loop;
}
}
+ }
- if (sort)
+ if (flags & UDEV_LIST_SORT) {
udev_list_entry_foreach(entry_loop, udev_list_get_entry(list)) {
if (strcmp(entry_loop->name, name) > 0)
break;
}
+ }
entry_new = malloc(sizeof(struct udev_list_entry));
if (entry_new == NULL)
@@ -153,6 +155,7 @@ struct udev_list_entry *udev_list_entry_add(struct udev *udev, struct udev_list_
free(entry_new);
return NULL;
}
+
if (value != NULL) {
entry_new->value = strdup(value);
if (entry_new->value == NULL) {
@@ -161,10 +164,12 @@ struct udev_list_entry *udev_list_entry_add(struct udev *udev, struct udev_list_
return NULL;
}
}
+
if (entry_loop != NULL)
udev_list_entry_insert_before(entry_new, entry_loop);
else
udev_list_entry_append(entry_new, list);
+
dbg(udev, "'%s=%s' added\n", entry_new->name, entry_new->value);
return entry_new;
}
@@ -258,16 +263,16 @@ const char *udev_list_entry_get_value(struct udev_list_entry *list_entry)
return list_entry->value;
}
-unsigned int udev_list_entry_get_flags(struct udev_list_entry *list_entry)
+int udev_list_entry_get_num(struct udev_list_entry *list_entry)
{
if (list_entry == NULL)
return -EINVAL;
- return list_entry->flags;
+ return list_entry->num;
}
-void udev_list_entry_set_flags(struct udev_list_entry *list_entry, unsigned int flags)
+void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num)
{
if (list_entry == NULL)
return;
- list_entry->flags = flags;
+ list_entry->num = num;
}
diff --git a/libudev/libudev-monitor.c b/libudev/libudev-monitor.c
index c97f6faa65..5917b9e497 100644
--- a/libudev/libudev-monitor.c
+++ b/libudev/libudev-monitor.c
@@ -50,6 +50,7 @@ struct udev_monitor {
socklen_t addrlen;
struct udev_list_node filter_subsystem_list;
struct udev_list_node filter_tag_list;
+ bool bound;
};
enum udev_monitor_netlink_group {
@@ -144,7 +145,7 @@ struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char
util_strscpy(&udev_monitor->sun.sun_path[1], sizeof(udev_monitor->sun.sun_path)-1, socket_path);
udev_monitor->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path)+1;
}
- udev_monitor->sock = socket(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+ udev_monitor->sock = socket(AF_LOCAL, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
if (udev_monitor->sock == -1) {
err(udev, "error getting socket: %m\n");
free(udev_monitor);
@@ -155,32 +156,7 @@ struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char
return udev_monitor;
}
-/**
- * udev_monitor_new_from_netlink:
- * @udev: udev library context
- * @name: name of event source
- *
- * Create new udev monitor and connect to a specified event
- * source. Valid sources identifiers are "udev" and "kernel".
- *
- * Applications should usually not connect directly to the
- * "kernel" events, because the devices might not be useable
- * at that time, before udev has configured them, and created
- * device nodes.
- *
- * Accessing devices at the same time as udev, might result
- * in unpredictable behavior.
- *
- * The "udev" events are sent out after udev has finished its
- * event processing, all rules have been processed, and needed
- * device nodes are created.
- *
- * The initial refcount is 1, and needs to be decremented to
- * release the resources of the udev monitor.
- *
- * Returns: a new udev monitor, or #NULL, in case of an error
- **/
-struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name)
+struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd)
{
struct udev_monitor *udev_monitor;
unsigned int group;
@@ -201,11 +177,16 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char
if (udev_monitor == NULL)
return NULL;
- udev_monitor->sock = socket(PF_NETLINK, SOCK_DGRAM|SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
- if (udev_monitor->sock == -1) {
- err(udev, "error getting socket: %m\n");
- free(udev_monitor);
- return NULL;
+ if (fd < 0) {
+ udev_monitor->sock = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
+ if (udev_monitor->sock == -1) {
+ err(udev, "error getting socket: %m\n");
+ free(udev_monitor);
+ return NULL;
+ }
+ } else {
+ udev_monitor->bound = true;
+ udev_monitor->sock = fd;
}
udev_monitor->snl.nl_family = AF_NETLINK;
@@ -219,6 +200,36 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char
return udev_monitor;
}
+/**
+ * udev_monitor_new_from_netlink:
+ * @udev: udev library context
+ * @name: name of event source
+ *
+ * Create new udev monitor and connect to a specified event
+ * source. Valid sources identifiers are "udev" and "kernel".
+ *
+ * Applications should usually not connect directly to the
+ * "kernel" events, because the devices might not be useable
+ * at that time, before udev has configured them, and created
+ * device nodes.
+ *
+ * Accessing devices at the same time as udev, might result
+ * in unpredictable behavior.
+ *
+ * The "udev" events are sent out after udev has finished its
+ * event processing, all rules have been processed, and needed
+ * device nodes are created.
+ *
+ * The initial refcount is 1, and needs to be decremented to
+ * release the resources of the udev monitor.
+ *
+ * Returns: a new udev monitor, or #NULL, in case of an error
+ **/
+struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name)
+{
+ return udev_monitor_new_from_netlink_fd(udev, name, -1);
+}
+
static inline void bpf_stmt(struct sock_filter *inss, unsigned int *i,
unsigned short code, unsigned int data)
{
@@ -364,16 +375,24 @@ int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct
*/
int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
{
- int err;
+ int err = 0;
const int on = 1;
if (udev_monitor->sun.sun_family != 0) {
- err = bind(udev_monitor->sock,
- (struct sockaddr *)&udev_monitor->sun, udev_monitor->addrlen);
+ if (!udev_monitor->bound) {
+ err = bind(udev_monitor->sock,
+ (struct sockaddr *)&udev_monitor->sun, udev_monitor->addrlen);
+ if (err == 0)
+ udev_monitor->bound = true;
+ }
} else if (udev_monitor->snl.nl_family != 0) {
udev_monitor_filter_update(udev_monitor);
- err = bind(udev_monitor->sock,
- (struct sockaddr *)&udev_monitor->snl, sizeof(struct sockaddr_nl));
+ if (!udev_monitor->bound) {
+ err = bind(udev_monitor->sock,
+ (struct sockaddr *)&udev_monitor->snl, sizeof(struct sockaddr_nl));
+ if (err == 0)
+ udev_monitor->bound = true;
+ }
if (err == 0) {
struct sockaddr_nl snl;
socklen_t addrlen;
@@ -811,7 +830,7 @@ int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_mo
if (subsystem == NULL)
return -EINVAL;
if (udev_list_entry_add(udev_monitor->udev,
- &udev_monitor->filter_subsystem_list, subsystem, devtype, 0, 0) == NULL)
+ &udev_monitor->filter_subsystem_list, subsystem, devtype, 0) == NULL)
return -ENOMEM;
return 0;
}
@@ -835,7 +854,7 @@ int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const c
if (tag == NULL)
return -EINVAL;
if (udev_list_entry_add(udev_monitor->udev,
- &udev_monitor->filter_tag_list, tag, NULL, 0, 0) == NULL)
+ &udev_monitor->filter_tag_list, tag, NULL, 0) == NULL)
return -ENOMEM;
return 0;
}
diff --git a/libudev/libudev-private.h b/libudev/libudev-private.h
index 8495f9aaee..39b46dde31 100644
--- a/libudev/libudev-private.h
+++ b/libudev/libudev-private.h
@@ -69,6 +69,8 @@ struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev);
/* libudev-device.c */
struct udev_device *udev_device_new(struct udev *udev);
struct udev_device *udev_device_new_from_id_filename(struct udev *udev, char *id);
+mode_t udev_device_get_devnode_mode(struct udev_device *udev_device);
+int udev_device_set_devnode_mode(struct udev_device *udev_device, mode_t mode);
int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath);
int udev_device_set_subsystem(struct udev_device *udev_device, const char *subsystem);
int udev_device_set_devtype(struct udev_device *udev_device, const char *devtype);
@@ -96,8 +98,6 @@ int udev_device_has_tag(struct udev_device *udev_device, const char *tag);
int udev_device_set_knodename(struct udev_device *udev_device, const char *knodename);
int udev_device_get_timeout(struct udev_device *udev_device);
int udev_device_set_timeout(struct udev_device *udev_device, int timeout);
-int udev_device_get_event_timeout(struct udev_device *udev_device);
-int udev_device_set_event_timeout(struct udev_device *udev_device, int event_timeout);
int udev_device_set_devnum(struct udev_device *udev_device, dev_t devnum);
int udev_device_set_seqnum(struct udev_device *udev_device, unsigned long long int seqnum);
unsigned long long udev_device_get_usec_initialized(struct udev_device *udev_device);
@@ -109,6 +109,8 @@ int udev_device_set_watch_handle(struct udev_device *udev_device, int handle);
int udev_device_get_ifindex(struct udev_device *udev_device);
int udev_device_set_ifindex(struct udev_device *udev_device, int ifindex);
void udev_device_set_info_loaded(struct udev_device *device);
+bool udev_device_get_db_persist(struct udev_device *udev_device);
+void udev_device_set_db_persist(struct udev_device *udev_device);
/* libudev-device-private.c */
int udev_device_update_db(struct udev_device *udev_device);
@@ -120,40 +122,52 @@ int udev_monitor_disconnect(struct udev_monitor *udev_monitor);
int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender);
int udev_monitor_send_device(struct udev_monitor *udev_monitor,
struct udev_monitor *destination, struct udev_device *udev_device);
+struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd);
/* libudev-ctrl.c - daemon runtime setup */
struct udev_ctrl;
struct udev_ctrl *udev_ctrl_new_from_socket(struct udev *udev, const char *socket_path);
-struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd);
+struct udev_ctrl *udev_ctrl_new_from_socket_fd(struct udev *udev, const char *socket_path, int fd);
int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl);
struct udev_ctrl *udev_ctrl_ref(struct udev_ctrl *uctrl);
-void udev_ctrl_unref(struct udev_ctrl *uctrl);
+struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl);
struct udev *udev_ctrl_get_udev(struct udev_ctrl *uctrl);
int udev_ctrl_get_fd(struct udev_ctrl *uctrl);
-int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority);
-int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl);
-int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl);
-int udev_ctrl_send_reload_rules(struct udev_ctrl *uctrl);
-int udev_ctrl_send_settle(struct udev_ctrl *uctrl);
-int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key);
-int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count);
+int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout);
+int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_reload_rules(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout);
+int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout);
+int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout);
+struct udev_ctrl_connection;
+struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl);
+struct udev_ctrl_connection *udev_ctrl_connection_ref(struct udev_ctrl_connection *conn);
+struct udev_ctrl_connection *udev_ctrl_connection_unref(struct udev_ctrl_connection *conn);
struct udev_ctrl_msg;
-struct udev_ctrl_msg *udev_ctrl_msg(struct udev_ctrl *uctrl);
-struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl *uctrl);
+struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn);
struct udev_ctrl_msg *udev_ctrl_msg_ref(struct udev_ctrl_msg *ctrl_msg);
-void udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg);
+struct udev_ctrl_msg *udev_ctrl_msg_unref(struct udev_ctrl_msg *ctrl_msg);
int udev_ctrl_get_set_log_level(struct udev_ctrl_msg *ctrl_msg);
int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg);
int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg);
int udev_ctrl_get_reload_rules(struct udev_ctrl_msg *ctrl_msg);
-pid_t udev_ctrl_get_settle(struct udev_ctrl_msg *ctrl_msg);
+int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg);
+int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg);
const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg);
int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg);
/* libudev-list.c */
+enum udev_list_flags {
+ UDEV_LIST_NONE = 0,
+ UDEV_LIST_UNIQUE = 1,
+ UDEV_LIST_SORT = 1 << 1,
+};
struct udev_list_node {
struct udev_list_node *next, *prev;
};
+#define UDEV_LIST(list) struct udev_list_node list = { &(list), &(list) }
void udev_list_init(struct udev_list_node *list);
int udev_list_is_empty(struct udev_list_node *list);
void udev_list_node_append(struct udev_list_node *new, struct udev_list_node *list);
@@ -167,16 +181,15 @@ void udev_list_node_remove(struct udev_list_node *entry);
node != list; \
node = tmp, tmp = (tmp)->next)
struct udev_list_entry *udev_list_entry_add(struct udev *udev, struct udev_list_node *list,
- const char *name, const char *value,
- int unique, int sort);
+ const char *name, const char *value, unsigned int flags);
void udev_list_entry_delete(struct udev_list_entry *entry);
void udev_list_entry_remove(struct udev_list_entry *entry);
void udev_list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry);
void udev_list_entry_append(struct udev_list_entry *new, struct udev_list_node *list);
void udev_list_cleanup_entries(struct udev *udev, struct udev_list_node *name_list);
struct udev_list_entry *udev_list_get_entry(struct udev_list_node *list);
-unsigned int udev_list_entry_get_flags(struct udev_list_entry *list_entry);
-void udev_list_entry_set_flags(struct udev_list_entry *list_entry, unsigned int flags);
+int udev_list_entry_get_num(struct udev_list_entry *list_entry);
+void udev_list_entry_set_num(struct udev_list_entry *list_entry, int num);
#define udev_list_entry_foreach_safe(entry, tmp, first) \
for (entry = first, tmp = udev_list_entry_get_next(entry); \
entry != NULL; \
@@ -190,7 +203,7 @@ ssize_t udev_queue_skip_devpath(FILE *queue_file);
/* libudev-queue-private.c */
struct udev_queue_export *udev_queue_export_new(struct udev *udev);
-void udev_queue_export_unref(struct udev_queue_export *udev_queue_export);
+struct udev_queue_export *udev_queue_export_unref(struct udev_queue_export *udev_queue_export);
void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export);
int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device);
int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device);
@@ -225,12 +238,9 @@ int util_delete_path(struct udev *udev, const char *path);
int util_unlink_secure(struct udev *udev, const char *filename);
uid_t util_lookup_user(struct udev *udev, const char *user);
gid_t util_lookup_group(struct udev *udev, const char *group);
-int util_run_program(struct udev *udev, const char *command, char **envp,
- char *result, size_t ressize, size_t *reslen,
- const sigset_t *sigmask, bool reset_prio);
int util_resolve_subsys_kernel(struct udev *udev, const char *string,
char *result, size_t maxsize, int read_value);
-unsigned long long usec_monotonic(void);
+unsigned long long now_usec(void);
/* libudev-selinux-private.c */
#ifndef WITH_SELINUX
diff --git a/libudev/libudev-queue-private.c b/libudev/libudev-queue-private.c
index 2f1afecb29..6e13d8a3d9 100644
--- a/libudev/libudev-queue-private.c
+++ b/libudev/libudev-queue-private.c
@@ -90,22 +90,24 @@ struct udev_queue_export *udev_queue_export_new(struct udev *udev)
return udev_queue_export;
}
-void udev_queue_export_unref(struct udev_queue_export *udev_queue_export)
+struct udev_queue_export *udev_queue_export_unref(struct udev_queue_export *udev_queue_export)
{
if (udev_queue_export == NULL)
- return;
+ 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)
{
char filename[UTIL_PATH_SIZE];
+ if (udev_queue_export == NULL)
+ return;
util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue_export->udev), "/queue.tmp", NULL);
unlink(filename);
-
util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue_export->udev), "/queue.bin", NULL);
unlink(filename);
}
@@ -163,7 +165,7 @@ static struct queue_devpaths *build_index(struct udev_queue_export *udev_queue_e
devpaths->devpaths_size = range + 1;
/* read all records and populate the table */
- while(1) {
+ for (;;) {
if (udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum) < 0)
break;
n = seqnum - udev_queue_export->seqnum_max;
@@ -302,8 +304,10 @@ static int write_queue_record(struct udev_queue_export *udev_queue_export,
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 (fwrite(devpath, 1, len, udev_queue_export->queue_file) != len)
- 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)
@@ -341,6 +345,7 @@ static int update_queue(struct udev_queue_export *udev_queue_export,
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);
@@ -352,12 +357,9 @@ static int update_queue(struct udev_queue_export *udev_queue_export,
return -1;
}
- /* when the queue file grows too large, garbage-collect and rebuild it */
- bytes = ftell(udev_queue_export->queue_file) + queue_record_size(devpath_len);
-
/* 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 && bytes > 2048) {
- /* because we don't need to read the old queue file */
+ 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);
@@ -365,6 +367,7 @@ static int update_queue(struct udev_queue_export *udev_queue_export,
}
/* 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);
diff --git a/libudev/libudev-queue.c b/libudev/libudev-queue.c
index 75c5b2425e..be6409d02a 100644
--- a/libudev/libudev-queue.c
+++ b/libudev/libudev-queue.c
@@ -343,7 +343,7 @@ out:
* @start: first event sequence number
* @end: last event sequence number
*
- * Returns: if any of the sequence numbers in the given range is currently active.
+ * Returns: a flag indicating if any of the sequence numbers in the given range is currently active.
**/
int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
unsigned long long int start, unsigned long long int end)
@@ -405,7 +405,7 @@ int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
* @udev_queue: udev queue context
* @seqnum: sequence number
*
- * Returns: a flag indicating if the given sequence number is handled.
+ * Returns: a flag indicating if the given sequence number is currently active.
**/
int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum)
{
@@ -454,7 +454,7 @@ struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev
break;
if (len > 0) {
- udev_list_entry_add(udev_queue->udev, &udev_queue->queue_list, syspath, seqnum_str, 0, 0);
+ udev_list_entry_add(udev_queue->udev, &udev_queue->queue_list, syspath, seqnum_str, 0);
} else {
udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_queue->queue_list)) {
if (strcmp(seqnum_str, udev_list_entry_get_value(list_entry)) == 0) {
@@ -508,7 +508,7 @@ struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev
util_strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
if (stat(filename, &statbuf) != 0)
continue;
- udev_list_entry_add(udev_queue->udev, &udev_queue->failed_list, syspath, NULL, 0, 0);
+ udev_list_entry_add(udev_queue->udev, &udev_queue->failed_list, syspath, NULL, 0);
}
closedir(dir);
return udev_list_get_entry(&udev_queue->failed_list);
diff --git a/libudev/libudev-util-private.c b/libudev/libudev-util-private.c
index 19f979eeab..2d7f8dc758 100644
--- a/libudev/libudev-util-private.c
+++ b/libudev/libudev-util-private.c
@@ -19,7 +19,6 @@
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
-#include <sys/wait.h>
#include <sys/param.h>
#include "libudev.h"
@@ -134,7 +133,7 @@ int util_unlink_secure(struct udev *udev, const char *filename)
uid_t util_lookup_user(struct udev *udev, const char *user)
{
char *endptr;
- int buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
char buf[buflen];
struct passwd pwbuf;
struct passwd *pw;
@@ -159,7 +158,7 @@ uid_t util_lookup_user(struct udev *udev, const char *user)
gid_t util_lookup_group(struct udev *udev, const char *group)
{
char *endptr;
- int buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
char *buf;
struct group grbuf;
struct group *gr;
@@ -258,226 +257,3 @@ int util_resolve_subsys_kernel(struct udev *udev, const char *string,
udev_device_unref(dev);
return 0;
}
-
-int util_run_program(struct udev *udev, const char *command, char **envp,
- char *result, size_t ressize, size_t *reslen,
- const sigset_t *sigmask, bool reset_prio)
-{
- int status;
- int outpipe[2] = {-1, -1};
- int errpipe[2] = {-1, -1};
- pid_t pid;
- char arg[UTIL_PATH_SIZE];
- char program[UTIL_PATH_SIZE];
- char *argv[((sizeof(arg) + 1) / 2) + 1];
- int devnull;
- int i;
- int err = 0;
-
- info(udev, "'%s' started\n", command);
-
- /* build argv from command */
- util_strscpy(arg, sizeof(arg), command);
- i = 0;
- if (strchr(arg, ' ') != NULL) {
- char *pos = arg;
-
- while (pos != NULL && pos[0] != '\0') {
- if (pos[0] == '\'') {
- /* do not separate quotes */
- pos++;
- argv[i] = strsep(&pos, "\'");
- if (pos != NULL)
- while (pos[0] == ' ')
- pos++;
- } else {
- argv[i] = strsep(&pos, " ");
- if (pos != NULL)
- while (pos[0] == ' ')
- pos++;
- }
- dbg(udev, "arg[%i] '%s'\n", i, argv[i]);
- i++;
- }
- argv[i] = NULL;
- } else {
- argv[0] = arg;
- argv[1] = NULL;
- }
-
- /* prepare pipes from child to parent */
- if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) {
- if (pipe(outpipe) != 0) {
- err(udev, "pipe failed: %m\n");
- return -1;
- }
- }
- if (udev_get_log_priority(udev) >= LOG_INFO) {
- if (pipe(errpipe) != 0) {
- err(udev, "pipe failed: %m\n");
- return -1;
- }
- }
-
- /* allow programs in /lib/udev/ to be called without the path */
- if (argv[0][0] != '/') {
- util_strscpyl(program, sizeof(program), LIBEXECDIR "/", argv[0], NULL);
- argv[0] = program;
- }
-
- pid = fork();
- switch(pid) {
- case 0:
- /* child closes parent ends of pipes */
- if (outpipe[READ_END] > 0)
- close(outpipe[READ_END]);
- if (errpipe[READ_END] > 0)
- close(errpipe[READ_END]);
-
- /* discard child output or connect to pipe */
- devnull = open("/dev/null", O_RDWR);
- if (devnull > 0) {
- dup2(devnull, STDIN_FILENO);
- if (outpipe[WRITE_END] < 0)
- dup2(devnull, STDOUT_FILENO);
- if (errpipe[WRITE_END] < 0)
- dup2(devnull, STDERR_FILENO);
- close(devnull);
- } else
- err(udev, "open /dev/null failed: %m\n");
- if (outpipe[WRITE_END] > 0) {
- dup2(outpipe[WRITE_END], STDOUT_FILENO);
- close(outpipe[WRITE_END]);
- }
- if (errpipe[WRITE_END] > 0) {
- dup2(errpipe[WRITE_END], STDERR_FILENO);
- close(errpipe[WRITE_END]);
- }
-
- if (sigmask)
- sigprocmask(SIG_SETMASK, sigmask, NULL);
- if (reset_prio)
- setpriority(PRIO_PROCESS, 0, 0);
-
- execve(argv[0], argv, envp);
- if (errno == ENOENT || errno == ENOTDIR) {
- /* may be on a filesystem which is not mounted right now */
- info(udev, "program '%s' not found\n", argv[0]);
- } else {
- /* other problems */
- err(udev, "exec of program '%s' failed\n", argv[0]);
- }
- _exit(1);
- case -1:
- err(udev, "fork of '%s' failed: %m\n", argv[0]);
- return -1;
- default:
- /* read from child if requested */
- if (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) {
- ssize_t count;
- size_t respos = 0;
-
- /* parent closes child ends of pipes */
- if (outpipe[WRITE_END] > 0)
- close(outpipe[WRITE_END]);
- if (errpipe[WRITE_END] > 0)
- close(errpipe[WRITE_END]);
-
- /* read child output */
- while (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) {
- int fdcount;
- fd_set readfds;
-
- FD_ZERO(&readfds);
- if (outpipe[READ_END] > 0)
- FD_SET(outpipe[READ_END], &readfds);
- if (errpipe[READ_END] > 0)
- FD_SET(errpipe[READ_END], &readfds);
- fdcount = select(MAX(outpipe[READ_END], errpipe[READ_END])+1, &readfds, NULL, NULL, NULL);
- if (fdcount < 0) {
- if (errno == EINTR)
- continue;
- err = -1;
- break;
- }
-
- /* get stdout */
- if (outpipe[READ_END] > 0 && FD_ISSET(outpipe[READ_END], &readfds)) {
- char inbuf[1024];
- char *pos;
- char *line;
-
- count = read(outpipe[READ_END], inbuf, sizeof(inbuf)-1);
- if (count <= 0) {
- close(outpipe[READ_END]);
- outpipe[READ_END] = -1;
- if (count < 0) {
- err(udev, "stdin read failed: %m\n");
- err = -1;
- }
- continue;
- }
- inbuf[count] = '\0';
-
- /* store result for rule processing */
- if (result) {
- if (respos + count < ressize) {
- memcpy(&result[respos], inbuf, count);
- respos += count;
- } else {
- err(udev, "ressize %ld too short\n", (long)ressize);
- err = -1;
- }
- }
- pos = inbuf;
- while ((line = strsep(&pos, "\n")))
- if (pos || line[0] != '\0')
- info(udev, "'%s' (stdout) '%s'\n", argv[0], line);
- }
-
- /* get stderr */
- if (errpipe[READ_END] > 0 && FD_ISSET(errpipe[READ_END], &readfds)) {
- char errbuf[1024];
- char *pos;
- char *line;
-
- count = read(errpipe[READ_END], errbuf, sizeof(errbuf)-1);
- if (count <= 0) {
- close(errpipe[READ_END]);
- errpipe[READ_END] = -1;
- if (count < 0)
- err(udev, "stderr read failed: %m\n");
- continue;
- }
- errbuf[count] = '\0';
- pos = errbuf;
- while ((line = strsep(&pos, "\n")))
- if (pos || line[0] != '\0')
- info(udev, "'%s' (stderr) '%s'\n", argv[0], line);
- }
- }
- if (outpipe[READ_END] > 0)
- close(outpipe[READ_END]);
- if (errpipe[READ_END] > 0)
- close(errpipe[READ_END]);
-
- /* return the child's stdout string */
- if (result) {
- result[respos] = '\0';
- dbg(udev, "result='%s'\n", result);
- if (reslen)
- *reslen = respos;
- }
- }
- waitpid(pid, &status, 0);
- if (WIFEXITED(status)) {
- info(udev, "'%s' returned with exitcode %i\n", command, WEXITSTATUS(status));
- if (WEXITSTATUS(status) != 0)
- err = -1;
- } else {
- err(udev, "'%s' unexpected exit with status 0x%04x\n", command, status);
- err = -1;
- }
- }
- return err;
-}
diff --git a/libudev/libudev-util.c b/libudev/libudev-util.c
index 51dd017467..48eea0b898 100644
--- a/libudev/libudev-util.c
+++ b/libudev/libudev-util.c
@@ -557,7 +557,7 @@ uint64_t util_string_bloom64(const char *str)
#define USEC_PER_SEC 1000000ULL
#define NSEC_PER_USEC 1000ULL
-unsigned long long usec_monotonic(void)
+unsigned long long now_usec(void)
{
struct timespec ts;
diff --git a/libudev/libudev.c b/libudev/libudev.c
index edc24e2fca..6b5c5e9f84 100644
--- a/libudev/libudev.c
+++ b/libudev/libudev.c
@@ -458,7 +458,7 @@ struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, co
udev_list_entry_delete(list_entry);
return NULL;
}
- return udev_list_entry_add(udev, &udev->properties_list, key, value, 1, 0);
+ return udev_list_entry_add(udev, &udev->properties_list, key, value, UDEV_LIST_UNIQUE);
}
struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev)
diff --git a/libudev/test-libudev.c b/libudev/test-libudev.c
index 5ff8663b90..966a406507 100644
--- a/libudev/test-libudev.c
+++ b/libudev/test-libudev.c
@@ -18,10 +18,12 @@
#include <getopt.h>
#include <syslog.h>
#include <fcntl.h>
-#include <sys/select.h>
+#include <sys/epoll.h>
#include "libudev.h"
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
static void log_fn(struct udev *udev,
int priority, const char *file, int line, const char *fn,
const char *format, va_list args)
@@ -219,56 +221,80 @@ static int test_enumerate_print_list(struct udev_enumerate *enumerate)
static int test_monitor(struct udev *udev)
{
- struct udev_monitor *udev_monitor;
- fd_set readfds;
- int fd;
+ struct udev_monitor *udev_monitor = NULL;
+ int fd_ep;
+ int fd_udev = -1;
+ struct epoll_event ep_udev, ep_stdin;
+
+ fd_ep = epoll_create1(EPOLL_CLOEXEC);
+ if (fd_ep < 0) {
+ printf("error creating epoll fd: %m\n");
+ goto out;
+ }
udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
if (udev_monitor == NULL) {
printf("no socket\n");
- return -1;
+ goto out;
}
+ fd_udev = udev_monitor_get_fd(udev_monitor);
+
if (udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "block", NULL) < 0 ||
udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "tty", NULL) < 0 ||
udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "usb", "usb_device") < 0) {
printf("filter failed\n");
- return -1;
+ goto out;
}
+
if (udev_monitor_enable_receiving(udev_monitor) < 0) {
printf("bind failed\n");
- return -1;
+ goto out;
}
- fd = udev_monitor_get_fd(udev_monitor);
- FD_ZERO(&readfds);
+ memset(&ep_udev, 0, sizeof(struct epoll_event));
+ ep_udev.events = EPOLLIN;
+ ep_udev.data.fd = fd_udev;
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) {
+ printf("fail to add fd to epoll: %m\n");
+ goto out;
+ }
+
+ memset(&ep_stdin, 0, sizeof(struct epoll_event));
+ ep_stdin.events = EPOLLIN;
+ ep_stdin.data.fd = STDIN_FILENO;
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, STDIN_FILENO, &ep_stdin) < 0) {
+ printf("fail to add fd to epoll: %m\n");
+ goto out;
+ }
for (;;) {
- struct udev_device *device;
int fdcount;
-
- FD_SET(STDIN_FILENO, &readfds);
- FD_SET(fd, &readfds);
+ struct epoll_event ev[4];
+ struct udev_device *device;
+ int i;
printf("waiting for events from udev, press ENTER to exit\n");
- fdcount = select(fd+1, &readfds, NULL, NULL, NULL);
- printf("select fd count: %i\n", fdcount);
-
- if (FD_ISSET(fd, &readfds)) {
- device = udev_monitor_receive_device(udev_monitor);
- if (device == NULL) {
- printf("no device from socket\n");
- continue;
+ fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1);
+ printf("epoll fd count: %i\n", fdcount);
+
+ for (i = 0; i < fdcount; i++) {
+ if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) {
+ device = udev_monitor_receive_device(udev_monitor);
+ if (device == NULL) {
+ printf("no device from socket\n");
+ continue;
+ }
+ print_device(device);
+ udev_device_unref(device);
+ } else if (ev[i].data.fd == STDIN_FILENO && ev[i].events & EPOLLIN) {
+ printf("exiting loop\n");
+ goto out;
}
- print_device(device);
- udev_device_unref(device);
- }
-
- if (FD_ISSET(STDIN_FILENO, &readfds)) {
- printf("exiting loop\n");
- break;
}
}
-
+out:
+ if (fd_ep >= 0)
+ close(fd_ep);
udev_monitor_unref(udev_monitor);
return 0;
}
diff --git a/rules/misc/30-kernel-compat.rules b/rules/misc/30-kernel-compat.rules
index 2a6a97c5f3..7240f9b234 100644
--- a/rules/misc/30-kernel-compat.rules
+++ b/rules/misc/30-kernel-compat.rules
@@ -1,102 +1,12 @@
# do not edit this file, it will be overwritten on update
# Rules to support older kernels, which migth miss functionality needed
-# for the recent udev version.
+# for the recent udev rules.
#
# This file is used and maintained by Gentoo.
-ACTION=="remove", GOTO="kernel_compat_end"
-
-#
-# sysfs timing rules
-#
-
-# needed for kernels <2.6.30
-SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", WAIT_FOR="descriptors"
-
-#
-# naming device rules
-#
-
-# dvb device naming
-# needed for kernels <2.6.29-rc1
-SUBSYSTEM=="dvb", ENV{DVB_ADAPTER_NUM}=="", PROGRAM="/bin/sh -c 'K=%k; K=$${K#dvb}; printf dvb/adapter%%i/%%s $${K%%%%.*} $${K#*.}'", NAME="%c", GROUP="video"
-
-# naming rules for kernels <2.6.31
-
-# alsa names
-KERNEL=="controlC[0-9]*", NAME="snd/%k"
-KERNEL=="hwC[D0-9]*", NAME="snd/%k"
-KERNEL=="pcmC[D0-9cp]*", NAME="snd/%k"
-KERNEL=="midiC[D0-9]*", NAME="snd/%k"
-KERNEL=="timer", NAME="snd/%k"
-KERNEL=="seq", NAME="snd/%k"
-
-KERNEL=="mwave", NAME="modems/mwave"
-
-KERNEL=="mouse*|mice|event*", NAME="input/%k"
-KERNEL=="ts[0-9]*|uinput", NAME="input/%k"
-KERNEL=="js[0-9]*", NAME="input/%k"
-
-KERNEL=="card[0-9]*", NAME="dri/%k"
-
-SUBSYSTEM=="dvb", ENV{DVB_ADAPTER_NUM}=="?*", NAME="dvb/adapter$env{DVB_ADAPTER_NUM}/$env{DVB_DEVICE_TYPE}$env{DVB_DEVICE_NUM}"
-
-# libusb device nodes
-SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", NAME="bus/usb/$env{BUSNUM}/$env{DEVNUM}"
-
-# printer
-SUBSYSTEM=="usb", KERNEL=="lp*", NAME="usb/%k"
-
-# block
-KERNEL=="pktcdvd[0-9]*", NAME="pktcdvd/%k"
-KERNEL=="pktcdvd", NAME="pktcdvd/control"
-
-KERNEL=="rawctl", NAME="raw/rawctl"
-SUBSYSTEM=="raw", KERNEL=="raw[0-9]*", NAME="raw/%k"
-SUBSYSTEM=="bsg", NAME="bsg/%k"
-SUBSYSTEM=="aoe", NAME="etherd/%k"
-
-# network
-KERNEL=="tun", NAME="net/%k"
-
-# CPU
-KERNEL=="cpu[0-9]*", NAME="cpu/%n/cpuid"
-KERNEL=="msr[0-9]*", NAME="cpu/%n/msr"
-KERNEL=="microcode", NAME="cpu/microcode", MODE="0600"
-
-# miscellaneous
-KERNEL=="auer[0-9]*", NAME="usb/%k"
-KERNEL=="hw_random", NAME="hwrng"
-KERNEL=="sxctl", NAME="specialix_sxctl"
-KERNEL=="rioctl", NAME="specialix_rioctl"
-KERNEL=="iowarrior[0-9]*", NAME="usb/%k"
-KERNEL=="hiddev[0-9]*", NAME="usb/%k"
-KERNEL=="legousbtower[0-9]*", NAME="usb/%k"
-KERNEL=="dabusb[0-9]*", NAME="usb/%k"
-KERNEL=="usbdpfp[0-9]*", NAME="usb/%k"
-KERNEL=="cpad[0-9]*", NAME="usb/%k"
-
-# end of naming rules for kernel <2.6.31
-
-
-#
-# module loading rules
-#
ACTION!="add", GOTO="kernel_compat_end"
-# No need for more code, as MODALIAS is present
-ENV{MODALIAS}=="?*", GOTO="kernel_compat_end"
-
-# needed for kernel <2.6.30-rc1
-SUBSYSTEM=="scsi", ATTR{type}=="8", RUN+="/sbin/modprobe -b ch"
-SUBSYSTEM=="mmc", RUN+="/sbin/modprobe -b mmc_block"
-
-# needed for kernel <2.6.27-rc5
-# acpi will do on newer kernels
-SUBSYSTEM=="pnp", DRIVER!="?*", \
- RUN{ignore_error}+="/bin/sh -c '/sbin/modprobe -a $$(while read id; do echo pnp:d$$id; done < /sys$devpath/id)'"
-
# see extras/qemu/42-qemu-usb.rules, version for 2.6.32 + older.
ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Mouse", ATTR{serial}=="42", TEST=="power/level", ATTR{power/level}="auto"
ACTION=="add", SUBSYSTEM=="usb", ATTR{product}=="QEMU USB Tablet", ATTR{serial}=="42", TEST=="power/level", ATTR{power/level}="auto"
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 4103cf0bff..4322da5ecd 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1487,7 +1487,7 @@ EOF
subsys => "block",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
exp_name => "sda",
- exp_perms => "0:0:0600",
+ exp_perms => "0:0:0440",
exp_rem_error => "yes",
rules => <<EOF
KERNEL=="sda", MODE="440"
diff --git a/udev/test-udev.c b/udev/test-udev.c
index 28c833a9e5..07716897b4 100644
--- a/udev/test-udev.c
+++ b/udev/test-udev.c
@@ -23,36 +23,24 @@
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
-#include <signal.h>
#include <unistd.h>
#include <syslog.h>
#include <grp.h>
+#include <sys/signalfd.h>
#include "udev.h"
-static void sig_handler(int signum)
-{
- switch (signum) {
- case SIGALRM:
- _exit(1);
- case SIGINT:
- case SIGTERM:
- _exit(20 + signum);
- }
-}
-
int main(int argc, char *argv[])
{
struct udev *udev;
- struct udev_event *event;
- struct udev_device *dev;
- struct udev_rules *rules;
+ struct udev_event *event = NULL;
+ struct udev_device *dev = NULL;
+ struct udev_rules *rules = NULL;
char syspath[UTIL_PATH_SIZE];
const char *devpath;
const char *action;
const char *subsystem;
- struct sigaction act;
- sigset_t mask;
+ sigset_t mask, sigmask_orig;
int err = -EINVAL;
udev = udev_new();
@@ -61,22 +49,7 @@ int main(int argc, char *argv[])
info(udev, "version %s\n", VERSION);
udev_selinux_init(udev);
- /* set signal handlers */
- memset(&act, 0x00, sizeof(act));
- act.sa_handler = sig_handler;
- sigemptyset (&act.sa_mask);
- act.sa_flags = 0;
- sigaction(SIGALRM, &act, NULL);
- sigaction(SIGINT, &act, NULL);
- sigaction(SIGTERM, &act, NULL);
- sigemptyset(&mask);
- sigaddset(&mask, SIGALRM);
- sigaddset(&mask, SIGINT);
- sigaddset(&mask, SIGTERM);
- sigprocmask(SIG_UNBLOCK, &mask, NULL);
-
- /* trigger timeout to prevent hanging processes */
- alarm(UDEV_EVENT_TIMEOUT);
+ sigprocmask(SIG_SETMASK, NULL, &sigmask_orig);
action = getenv("ACTION");
devpath = getenv("DEVPATH");
@@ -84,7 +57,7 @@ int main(int argc, char *argv[])
if (action == NULL || subsystem == NULL || devpath == NULL) {
err(udev, "action, subsystem or devpath missing\n");
- goto exit;
+ goto out;
}
rules = udev_rules_new(udev, 1);
@@ -93,7 +66,7 @@ int main(int argc, char *argv[])
dev = udev_device_new_from_syspath(udev, syspath);
if (dev == NULL) {
info(udev, "unknown device '%s'\n", devpath);
- goto fail;
+ goto out;
}
/* skip reading of db, but read kernel parameters */
@@ -102,20 +75,24 @@ int main(int argc, char *argv[])
udev_device_set_action(dev, action);
event = udev_event_new(dev);
- err = udev_event_execute_rules(event, rules);
- /* rules may change/disable the timeout */
- if (udev_device_get_event_timeout(dev) >= 0)
- alarm(udev_device_get_event_timeout(dev));
+ sigfillset(&mask);
+ sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
+ event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
+ if (event->fd_signal < 0) {
+ fprintf(stderr, "error creating signalfd\n");
+ goto out;
+ }
+ err = udev_event_execute_rules(event, rules, &sigmask_orig);
if (err == 0)
udev_event_execute_run(event, NULL);
-
+out:
+ if (event != NULL && event->fd_signal >= 0)
+ close(event->fd_signal);
udev_event_unref(event);
udev_device_unref(dev);
-fail:
udev_rules_unref(rules);
-exit:
udev_selinux_exit(udev);
udev_unref(udev);
if (err != 0)
diff --git a/udev/udev-event.c b/udev/udev-event.c
index 63a8b3aa83..832abbbfce 100644
--- a/udev/udev-event.c
+++ b/udev/udev-event.c
@@ -26,7 +26,12 @@
#include <time.h>
#include <net/if.h>
#include <sys/ioctl.h>
+#include <sys/prctl.h>
+#include <sys/poll.h>
+#include <sys/epoll.h>
+#include <sys/wait.h>
#include <sys/socket.h>
+#include <sys/signalfd.h>
#include <linux/sockios.h>
#include "udev.h"
@@ -38,10 +43,12 @@ struct udev_event *udev_event_new(struct udev_device *dev)
event = calloc(1, sizeof(struct udev_event));
if (event == NULL)
return NULL;
- event->mode = 0600;
event->dev = dev;
event->udev = udev_device_get_udev(dev);
udev_list_init(&event->run_list);
+ event->fd_signal = -1;
+ event->birth_usec = now_usec();
+ event->timeout_usec = 60 * 1000 * 1000;
dbg(event->udev, "allocated event %p\n", event);
return event;
}
@@ -439,6 +446,383 @@ out:
return l;
}
+static int spawn_exec(struct udev_event *event,
+ const char *cmd, char *const argv[], char **envp, const sigset_t *sigmask,
+ int fd_stdout, int fd_stderr)
+{
+ struct udev *udev = event->udev;
+ int err;
+ int fd;
+
+ /* discard child output or connect to pipe */
+ fd = open("/dev/null", O_RDWR);
+ if (fd >= 0) {
+ dup2(fd, STDIN_FILENO);
+ if (fd_stdout < 0)
+ dup2(fd, STDOUT_FILENO);
+ if (fd_stderr < 0)
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+ } else {
+ err(udev, "open /dev/null failed: %m\n");
+ }
+
+ /* connect pipes to std{out,err} */
+ if (fd_stdout >= 0) {
+ dup2(fd_stdout, STDOUT_FILENO);
+ close(fd_stdout);
+ }
+ if (fd_stderr >= 0) {
+ dup2(fd_stderr, STDERR_FILENO);
+ close(fd_stderr);
+ }
+
+ /* terminate child in case parent goes away */
+ prctl(PR_SET_PDEATHSIG, SIGTERM);
+
+ /* restore original udev sigmask before exec */
+ if (sigmask)
+ sigprocmask(SIG_SETMASK, sigmask, NULL);
+
+ execve(argv[0], argv, envp);
+
+ /* exec failed */
+ err = -errno;
+ err(udev, "failed to execute '%s' '%s': %m\n", argv[0], cmd);
+ return err;
+}
+
+static int spawn_read(struct udev_event *event,
+ const char *cmd,
+ int fd_stdout, int fd_stderr,
+ char *result, size_t ressize)
+{
+ struct udev *udev = event->udev;
+ size_t respos = 0;
+ int fd_ep = -1;
+ struct epoll_event ep_outpipe, ep_errpipe;
+ int err = 0;
+
+ /* read from child if requested */
+ if (fd_stdout < 0 && fd_stderr < 0)
+ return 0;
+
+ fd_ep = epoll_create1(EPOLL_CLOEXEC);
+ if (fd_ep < 0) {
+ err = -errno;
+ err(udev, "error creating epoll fd: %m\n");
+ goto out;
+ }
+
+ if (fd_stdout >= 0) {
+ memset(&ep_outpipe, 0, sizeof(struct epoll_event));
+ ep_outpipe.events = EPOLLIN;
+ ep_outpipe.data.ptr = &fd_stdout;
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe) < 0) {
+ err(udev, "fail to add fd to epoll: %m\n");
+ goto out;
+ }
+ }
+
+ if (fd_stderr >= 0) {
+ memset(&ep_errpipe, 0, sizeof(struct epoll_event));
+ ep_errpipe.events = EPOLLIN;
+ ep_errpipe.data.ptr = &fd_stderr;
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe) < 0) {
+ err(udev, "fail to add fd to epoll: %m\n");
+ goto out;
+ }
+ }
+
+ /* read child output */
+ while (fd_stdout >= 0 || fd_stderr >= 0) {
+ int timeout;
+ int fdcount;
+ struct epoll_event ev[4];
+ int i;
+
+ if (event->timeout_usec > 0) {
+ unsigned long long age_usec;
+
+ age_usec = now_usec() - event->birth_usec;
+ if (age_usec >= event->timeout_usec) {
+ err = -ETIMEDOUT;
+ err(udev, "timeout '%s'\n", cmd);
+ goto out;
+ }
+ timeout = ((event->timeout_usec - age_usec) / 1000) + 1000;
+ } else {
+ timeout = -1;
+ }
+
+ fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), timeout);
+ if (fdcount < 0) {
+ if (errno == EINTR)
+ continue;
+ err = -errno;
+ err(udev, "failed to poll: %m\n");
+ goto out;
+ }
+ if (fdcount == 0) {
+ err = -ETIMEDOUT;
+ err(udev, "timeout '%s'\n", cmd);
+ goto out;
+ }
+
+ for (i = 0; i < fdcount; i++) {
+ int *fd = (int *)ev[i].data.ptr;
+
+ if (ev[i].events & EPOLLIN) {
+ ssize_t count;
+ char buf[4096];
+
+ count = read(*fd, buf, sizeof(buf)-1);
+ if (count <= 0)
+ continue;
+ buf[count] = '\0';
+
+ /* store stdout result */
+ if (result != NULL && *fd == fd_stdout) {
+ if (respos + count < ressize) {
+ memcpy(&result[respos], buf, count);
+ respos += count;
+ } else {
+ err(udev, "'%s' ressize %zd too short\n", cmd, ressize);
+ err = -ENOBUFS;
+ }
+ }
+
+ /* log debug output only if we watch stderr */
+ if (fd_stderr >= 0) {
+ char *pos;
+ char *line;
+
+ pos = buf;
+ while ((line = strsep(&pos, "\n"))) {
+ if (pos != NULL || line[0] != '\0')
+ info(udev, "'%s'(%s) '%s'\n", cmd, *fd == fd_stdout ? "out" : "err" , line);
+ }
+ }
+ } else if (ev[i].events & EPOLLHUP) {
+ if (epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL) < 0) {
+ err = -errno;
+ err(udev, "failed to remove fd from epoll: %m\n");
+ goto out;
+ }
+ *fd = -1;
+ }
+ }
+ }
+
+ /* return the child's stdout string */
+ if (result != NULL) {
+ result[respos] = '\0';
+ dbg(udev, "result='%s'\n", result);
+ }
+out:
+ if (fd_ep >= 0)
+ close(fd_ep);
+ return err;
+}
+
+static int spawn_wait(struct udev_event *event, const char *cmd, pid_t pid)
+{
+ struct udev *udev = event->udev;
+ struct pollfd pfd[1];
+ int err = 0;
+
+ pfd[0].events = POLLIN;
+ pfd[0].fd = event->fd_signal;
+
+ while (pid > 0) {
+ int timeout;
+ int fdcount;
+
+ if (event->timeout_usec > 0) {
+ unsigned long long age_usec;
+
+ age_usec = now_usec() - event->birth_usec;
+ if (age_usec >= event->timeout_usec)
+ timeout = 1000;
+ else
+ timeout = ((event->timeout_usec - age_usec) / 1000) + 1000;
+ } else {
+ timeout = -1;
+ }
+
+ fdcount = poll(pfd, 1, timeout);
+ if (fdcount < 0) {
+ if (errno == EINTR)
+ continue;
+ err = -errno;
+ err(udev, "failed to poll: %m\n");
+ goto out;
+ }
+ if (fdcount == 0) {
+ err(udev, "timeout: killing '%s' [%u]\n", cmd, pid);
+ kill(pid, SIGKILL);
+ }
+
+ if (pfd[0].revents & POLLIN) {
+ struct signalfd_siginfo fdsi;
+ int status;
+ ssize_t size;
+
+ size = read(event->fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
+ if (size != sizeof(struct signalfd_siginfo))
+ continue;
+
+ switch (fdsi.ssi_signo) {
+ case SIGTERM:
+ event->sigterm = true;
+ break;
+ case SIGCHLD:
+ if (waitpid(pid, &status, WNOHANG) < 0)
+ break;
+ if (WIFEXITED(status)) {
+ info(udev, "'%s' [%u] exit with return code %i\n", cmd, pid, WEXITSTATUS(status));
+ if (WEXITSTATUS(status) != 0)
+ err = -1;
+ } else if (WIFSIGNALED(status)) {
+ err(udev, "'%s' [%u] terminated by signal %i (%s)\n", cmd, pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
+ err = -1;
+ } else if (WIFSTOPPED(status)) {
+ err(udev, "'%s' [%u] stopped\n", cmd, pid);
+ err = -1;
+ } else if (WIFCONTINUED(status)) {
+ err(udev, "'%s' [%u] continued\n", cmd, pid);
+ err = -1;
+ } else {
+ err(udev, "'%s' [%u] exit with status 0x%04x\n", cmd, pid, status);
+ err = -1;
+ }
+ pid = 0;
+ break;
+ }
+ }
+ }
+out:
+ return err;
+}
+
+int udev_event_spawn(struct udev_event *event,
+ const char *cmd, char **envp, const sigset_t *sigmask,
+ char *result, size_t ressize)
+{
+ struct udev *udev = event->udev;
+ int outpipe[2] = {-1, -1};
+ int errpipe[2] = {-1, -1};
+ pid_t pid;
+ char arg[UTIL_PATH_SIZE];
+ char program[UTIL_PATH_SIZE];
+ char *argv[((sizeof(arg) + 1) / 2) + 1];
+ int i;
+ int err = 0;
+
+ /* build argv from command */
+ util_strscpy(arg, sizeof(arg), cmd);
+ i = 0;
+ if (strchr(arg, ' ') != NULL) {
+ char *pos = arg;
+
+ while (pos != NULL && pos[0] != '\0') {
+ if (pos[0] == '\'') {
+ /* do not separate quotes */
+ pos++;
+ argv[i] = strsep(&pos, "\'");
+ if (pos != NULL)
+ while (pos[0] == ' ')
+ pos++;
+ } else {
+ argv[i] = strsep(&pos, " ");
+ if (pos != NULL)
+ while (pos[0] == ' ')
+ pos++;
+ }
+ dbg(udev, "arg[%i] '%s'\n", i, argv[i]);
+ i++;
+ }
+ argv[i] = NULL;
+ } else {
+ argv[0] = arg;
+ argv[1] = NULL;
+ }
+
+ /* pipes from child to parent */
+ if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) {
+ if (pipe2(outpipe, O_NONBLOCK) != 0) {
+ err = -errno;
+ err(udev, "pipe failed: %m\n");
+ goto out;
+ }
+ }
+ if (udev_get_log_priority(udev) >= LOG_INFO) {
+ if (pipe2(errpipe, O_NONBLOCK) != 0) {
+ err = -errno;
+ err(udev, "pipe failed: %m\n");
+ goto out;
+ }
+ }
+
+ /* allow programs in /lib/udev/ to be called without the path */
+ if (argv[0][0] != '/') {
+ util_strscpyl(program, sizeof(program), LIBEXECDIR "/", argv[0], NULL);
+ argv[0] = program;
+ }
+
+ pid = fork();
+ switch(pid) {
+ case 0:
+ /* child closes parent's ends of pipes */
+ if (outpipe[READ_END] >= 0) {
+ close(outpipe[READ_END]);
+ outpipe[READ_END] = -1;
+ }
+ if (errpipe[READ_END] >= 0) {
+ close(errpipe[READ_END]);
+ errpipe[READ_END] = -1;
+ }
+
+ info(udev, "starting '%s'\n", cmd);
+
+ err = spawn_exec(event, cmd, argv, envp, sigmask,
+ outpipe[WRITE_END], errpipe[WRITE_END]);
+
+ _exit(2 );
+ case -1:
+ err(udev, "fork of '%s' failed: %m\n", cmd);
+ err = -1;
+ goto out;
+ default:
+ /* parent closed child's ends of pipes */
+ if (outpipe[WRITE_END] >= 0) {
+ close(outpipe[WRITE_END]);
+ outpipe[WRITE_END] = -1;
+ }
+ if (errpipe[WRITE_END] >= 0) {
+ close(errpipe[WRITE_END]);
+ errpipe[WRITE_END] = -1;
+ }
+
+ err = spawn_read(event, cmd,
+ outpipe[READ_END], errpipe[READ_END],
+ result, ressize);
+
+ err = spawn_wait(event, cmd, pid);
+ }
+
+out:
+ if (outpipe[READ_END] >= 0)
+ close(outpipe[READ_END]);
+ if (outpipe[WRITE_END] >= 0)
+ close(outpipe[WRITE_END]);
+ if (errpipe[READ_END] >= 0)
+ close(errpipe[READ_END]);
+ if (errpipe[WRITE_END] >= 0)
+ close(errpipe[WRITE_END]);
+ return err;
+}
+
static void rename_netif_kernel_log(struct ifreq ifr)
{
int klog;
@@ -454,7 +838,7 @@ static void rename_netif_kernel_log(struct ifreq ifr)
return;
}
- fprintf(f, "<30>udev[%u]: renamed network interface %s to %s\n",
+ fprintf(f, "<30>udevd[%u]: renamed network interface %s to %s\n",
getpid(), ifr.ifr_name, ifr.ifr_newname);
fclose(f);
}
@@ -492,7 +876,7 @@ static int rename_netif(struct udev_event *event)
goto out;
/* free our own name, another process may wait for us */
- util_strscpyl(ifr.ifr_newname, IFNAMSIZ, udev_device_get_sysname(dev), "-", event->name, NULL);
+ snprintf(ifr.ifr_newname, IFNAMSIZ, "rename%u", udev_device_get_ifindex(dev));
err = ioctl(sk, SIOCSIFNAME, &ifr);
if (err < 0) {
err = -errno;
@@ -530,7 +914,7 @@ out:
return err;
}
-int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules)
+int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, const sigset_t *sigmask)
{
struct udev_device *dev = event->dev;
int err = 0;
@@ -546,7 +930,7 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules)
if (major(udev_device_get_devnum(dev)) != 0)
udev_watch_end(event->udev, dev);
- udev_rules_apply_to_event(rules, event);
+ udev_rules_apply_to_event(rules, event, sigmask);
if (major(udev_device_get_devnum(dev)) != 0)
err = udev_node_remove(dev);
@@ -561,7 +945,7 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules)
udev_watch_end(event->udev, event->dev_db);
}
- udev_rules_apply_to_event(rules, event);
+ udev_rules_apply_to_event(rules, event, sigmask);
/* rename a new network interface, if needed */
if (udev_device_get_ifindex(dev) > 0 && strcmp(udev_device_get_action(dev), "add") == 0 &&
@@ -636,9 +1020,18 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules)
if (event->dev_db != NULL)
udev_node_update_old_links(dev, event->dev_db);
- /* change default 0600 to 0660 if a group is assigned */
- if (event->mode == 0600 && event->gid > 0)
- event->mode = 0660;
+ if (!event->mode_set) {
+ if (udev_device_get_devnode_mode(dev) > 0) {
+ /* kernel supplied value */
+ event->mode = udev_device_get_devnode_mode(dev);
+ } else if (event->gid > 0) {
+ /* default 0660 if a group is assigned */
+ event->mode = 0660;
+ } else {
+ /* default 0600 */
+ event->mode = 0600;
+ }
+ }
err = udev_node_add(dev, event->mode, event->uid, event->gid);
}
@@ -647,7 +1040,7 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules)
if (event->dev_db != NULL && udev_device_get_usec_initialized(event->dev_db) > 0)
udev_device_set_usec_initialized(event->dev, udev_device_get_usec_initialized(event->dev_db));
else
- udev_device_set_usec_initialized(event->dev, usec_monotonic());
+ udev_device_set_usec_initialized(event->dev, now_usec());
/* (re)write database file */
udev_device_update_db(dev);
@@ -682,14 +1075,15 @@ int udev_event_execute_run(struct udev_event *event, const sigset_t *sigmask)
char program[UTIL_PATH_SIZE];
char **envp;
- udev_event_apply_format(event, cmd, program, sizeof(program));
- envp = udev_device_get_properties_envp(event->dev);
if (event->exec_delay > 0) {
info(event->udev, "delay execution of '%s'\n", program);
sleep(event->exec_delay);
}
- if (util_run_program(event->udev, program, envp, NULL, 0, NULL, sigmask, true) != 0) {
- if (udev_list_entry_get_flags(list_entry))
+
+ udev_event_apply_format(event, cmd, program, sizeof(program));
+ envp = udev_device_get_properties_envp(event->dev);
+ if (udev_event_spawn(event, program, envp, sigmask, NULL, 0) < 0) {
+ if (udev_list_entry_get_num(list_entry))
err = -1;
}
}
diff --git a/udev/udev-node.c b/udev/udev-node.c
index 5e791ffb6c..dc7d9c365a 100644
--- a/udev/udev-node.c
+++ b/udev/udev-node.c
@@ -386,7 +386,7 @@ int udev_node_add(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid)
/* create/update symlinks, add symlinks to name index */
udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) {
- if (udev_list_entry_get_flags(list_entry))
+ if (udev_list_entry_get_num(list_entry))
/* simple unmanaged link name */
node_symlink(udev, udev_device_get_devnode(dev), udev_list_entry_get_name(list_entry));
else
diff --git a/udev/udev-rules.c b/udev/udev-rules.c
index 221865c9dc..63123e083a 100644
--- a/udev/udev-rules.c
+++ b/udev/udev-rules.c
@@ -139,6 +139,7 @@ enum token_type {
TK_M_PARENTS_MAX,
TK_M_TEST, /* val, mode_t */
+ TK_M_EVENT_TIMEOUT, /* int */
TK_M_PROGRAM, /* val */
TK_M_IMPORT_FILE, /* val */
TK_M_IMPORT_PROG, /* val */
@@ -150,6 +151,7 @@ enum token_type {
TK_A_STRING_ESCAPE_NONE,
TK_A_STRING_ESCAPE_REPLACE,
+ TK_A_DB_PERSIST,
TK_A_INOTIFY_WATCH, /* int */
TK_A_DEVLINK_PRIO, /* int */
TK_A_OWNER, /* val */
@@ -163,7 +165,6 @@ enum token_type {
TK_A_TAG, /* val */
TK_A_NAME, /* val */
TK_A_DEVLINK, /* val */
- TK_A_EVENT_TIMEOUT, /* int */
TK_A_ATTR, /* val, attr */
TK_A_RUN, /* val, bool */
TK_A_GOTO, /* size_t */
@@ -273,6 +274,7 @@ static const char *token_str(enum token_type type)
[TK_M_PARENTS_MAX] = "M PARENTS_MAX",
[TK_M_TEST] = "M TEST",
+ [TK_M_EVENT_TIMEOUT] = "M EVENT_TIMEOUT",
[TK_M_PROGRAM] = "M PROGRAM",
[TK_M_IMPORT_FILE] = "M IMPORT_FILE",
[TK_M_IMPORT_PROG] = "M IMPORT_PROG",
@@ -284,6 +286,7 @@ static const char *token_str(enum token_type type)
[TK_A_STRING_ESCAPE_NONE] = "A STRING_ESCAPE_NONE",
[TK_A_STRING_ESCAPE_REPLACE] = "A STRING_ESCAPE_REPLACE",
+ [TK_A_DB_PERSIST] = "A DB_PERSIST",
[TK_A_INOTIFY_WATCH] = "A INOTIFY_WATCH",
[TK_A_DEVLINK_PRIO] = "A DEVLINK_PRIO",
[TK_A_OWNER] = "A OWNER",
@@ -297,7 +300,6 @@ static const char *token_str(enum token_type type)
[TK_A_TAG] = "A ENV",
[TK_A_NAME] = "A NAME",
[TK_A_DEVLINK] = "A DEVLINK",
- [TK_A_EVENT_TIMEOUT] = "A EVENT_TIMEOUT",
[TK_A_ATTR] = "A ATTR",
[TK_A_RUN] = "A RUN",
[TK_A_GOTO] = "A GOTO",
@@ -370,6 +372,7 @@ static void dump_token(struct udev_rules *rules, struct token *token)
break;
case TK_A_STRING_ESCAPE_NONE:
case TK_A_STRING_ESCAPE_REPLACE:
+ case TK_A_DB_PERSIST:
dbg(rules->udev, "%s\n", token_str(type));
break;
case TK_M_TEST:
@@ -394,7 +397,7 @@ static void dump_token(struct udev_rules *rules, struct token *token)
case TK_A_STATIC_NODE:
dbg(rules->udev, "%s '%s'\n", token_str(type), value);
break;
- case TK_A_EVENT_TIMEOUT:
+ case TK_M_EVENT_TIMEOUT:
dbg(rules->udev, "%s %u\n", token_str(type), token->key.event_timeout);
break;
case TK_A_GOTO:
@@ -728,7 +731,7 @@ static int import_property_from_string(struct udev_device *dev, char *line)
entry = udev_device_add_property(dev, key, val);
/* store in db, skip private keys */
if (key[0] != '.')
- udev_list_entry_set_flags(entry, 1);
+ udev_list_entry_set_num(entry, true);
}
return 0;
}
@@ -747,17 +750,18 @@ static int import_file_into_properties(struct udev_device *dev, const char *file
return 0;
}
-static int import_program_into_properties(struct udev_device *dev, const char *program)
+static int import_program_into_properties(struct udev_event *event, const char *program, const sigset_t *sigmask)
{
- struct udev *udev = udev_device_get_udev(dev);
+ struct udev_device *dev = event->dev;
char **envp;
char result[UTIL_LINE_SIZE];
- size_t reslen;
char *line;
+ int err;
envp = udev_device_get_properties_envp(dev);
- if (util_run_program(udev, program, envp, result, sizeof(result), &reslen, NULL, false) != 0)
- return -1;
+ err = udev_event_spawn(event, program, envp, sigmask, result, sizeof(result));
+ if (err < 0)
+ return err;
line = result;
while (line != NULL) {
@@ -796,7 +800,7 @@ static int import_parent_into_properties(struct udev_device *dev, const char *fi
entry = udev_device_add_property(dev, key, val);
/* store in db, skip private keys */
if (key[0] != '.')
- udev_list_entry_set_flags(entry, 1);
+ udev_list_entry_set_num(entry, true);
}
}
return 0;
@@ -1045,6 +1049,7 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
break;
case TK_A_STRING_ESCAPE_NONE:
case TK_A_STRING_ESCAPE_REPLACE:
+ case TK_A_DB_PERSIST:
break;
case TK_A_RUN:
token->key.value_off = add_string(rule_tmp->rules, value);
@@ -1066,7 +1071,7 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
case TK_A_STATIC_NODE:
token->key.value_off = add_string(rule_tmp->rules, value);
break;
- case TK_A_EVENT_TIMEOUT:
+ case TK_M_EVENT_TIMEOUT:
token->key.event_timeout = *(int *)data;
break;
case TK_RULE:
@@ -1572,13 +1577,15 @@ static int add_rule(struct udev_rules *rules, char *line,
rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio);
dbg(rules->udev, "link priority=%i\n", prio);
}
+
pos = strstr(value, "event_timeout=");
if (pos != NULL) {
int tout = atoi(&pos[strlen("event_timeout=")]);
- rule_add_key(&rule_tmp, TK_A_EVENT_TIMEOUT, op, NULL, &tout);
+ rule_add_key(&rule_tmp, TK_M_EVENT_TIMEOUT, op, NULL, &tout);
dbg(rules->udev, "event timeout=%i\n", tout);
}
+
pos = strstr(value, "string_escape=");
if (pos != NULL) {
pos = &pos[strlen("string_escape=")];
@@ -1587,6 +1594,11 @@ static int add_rule(struct udev_rules *rules, char *line,
else if (strncmp(pos, "replace", strlen("replace")) == 0)
rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, op, NULL, NULL);
}
+
+ pos = strstr(value, "db_persist");
+ if (pos != NULL)
+ rule_add_key(&rule_tmp, TK_A_DB_PERSIST, op, NULL, NULL);
+
pos = strstr(value, "nowatch");
if (pos != NULL) {
const int off = 0;
@@ -1602,11 +1614,13 @@ static int add_rule(struct udev_rules *rules, char *line,
dbg(rules->udev, "inotify watch of device requested\n");
}
}
+
pos = strstr(value, "static_node=");
if (pos != NULL) {
rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, &pos[strlen("static_node=")], NULL);
rule_tmp.rule.rule.has_static_node = true;
}
+
continue;
}
err(rules->udev, "unknown key '%s' in %s:%u\n", key, filename, lineno);
@@ -1705,6 +1719,7 @@ static int parse_file(struct udev_rules *rules, const char *filename, unsigned s
static int add_matching_files(struct udev *udev, struct udev_list_node *file_list, const char *dirname, const char *suffix)
{
DIR *dir;
+ struct dirent *dent;
char filename[UTIL_PATH_SIZE];
dbg(udev, "open directory '%s'\n", dirname);
@@ -1714,13 +1729,7 @@ static int add_matching_files(struct udev *udev, struct udev_list_node *file_lis
return -1;
}
- for (;;) {
- struct dirent *dent;
-
- dent = readdir(dir);
- if (dent == NULL || dent->d_name[0] == '\0')
- break;
-
+ for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
if (dent->d_name[0] == '.')
continue;
@@ -1736,7 +1745,12 @@ static int add_matching_files(struct udev *udev, struct udev_list_node *file_lis
}
util_strscpyl(filename, sizeof(filename), dirname, "/", dent->d_name, NULL);
dbg(udev, "put file '%s' into list\n", filename);
- udev_list_entry_add(udev, file_list, filename, NULL, 1, 1);
+ /*
+ * the basename is the key, the filename the value
+ * identical basenames from different directories overwrite each other
+ * entries are sorted after basename
+ */
+ udev_list_entry_add(udev, file_list, dent->d_name, filename, UDEV_LIST_UNIQUE|UDEV_LIST_SORT);
}
closedir(dir);
@@ -1746,9 +1760,8 @@ static int add_matching_files(struct udev *udev, struct udev_list_node *file_lis
struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names)
{
struct udev_rules *rules;
- struct stat statbuf;
struct udev_list_node file_list;
- struct udev_list_entry *file_loop, *file_tmp;
+ struct udev_list_entry *file_loop;
struct token end_token;
rules = calloc(1, sizeof(struct udev_rules));
@@ -1791,85 +1804,55 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names)
memset(rules->trie_nodes, 0x00, sizeof(struct trie_node));
rules->trie_nodes_cur = 1;
- if (udev_get_rules_path(udev) != NULL) {
- /* custom rules location for testing */
- add_matching_files(udev, &file_list, udev_get_rules_path(udev), ".rules");
- } else {
- char filename[PATH_MAX];
- struct udev_list_node sort_list;
- struct udev_list_entry *sort_loop, *sort_tmp;
+ if (udev_get_rules_path(udev) == NULL) {
+ char filename[UTIL_PATH_SIZE];
+
+ /* /lib/udev -- default/package rules */
+ add_matching_files(udev, &file_list, LIBEXECDIR "/rules.d", ".rules");
- /* read user/custom rules */
+ /* /etc/udev -- system-specific/user/admin rules */
add_matching_files(udev, &file_list, SYSCONFDIR "/udev/rules.d", ".rules");
- /* read dynamic/temporary rules */
+ /* /run/udev -- throw-away/temporary rules */
util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/rules.d", NULL);
- udev_list_init(&sort_list);
- add_matching_files(udev, &sort_list, filename, ".rules");
-
- /* read default rules */
- add_matching_files(udev, &sort_list, LIBEXECDIR "/rules.d", ".rules");
-
- /* sort all rules files by basename into list of files */
- udev_list_entry_foreach_safe(sort_loop, sort_tmp, udev_list_get_entry(&sort_list)) {
- const char *sort_name = udev_list_entry_get_name(sort_loop);
- const char *sort_base = strrchr(sort_name, '/');
-
- if (sort_base == NULL)
- continue;
- /* sort entry into existing list */
- udev_list_entry_foreach_safe(file_loop, file_tmp, udev_list_get_entry(&file_list)) {
- const char *file_name = udev_list_entry_get_name(file_loop);
- const char *file_base = strrchr(file_name, '/');
-
- if (file_base == NULL)
- continue;
- if (strcmp(file_base, sort_base) == 0) {
- info(udev, "rule file basename '%s' already added, ignoring '%s'\n",
- file_name, sort_name);
- udev_list_entry_delete(sort_loop);
- sort_loop = NULL;
- break;
- }
- if (strcmp(file_base, sort_base) > 0) {
- /* found later file, insert before */
- udev_list_entry_remove(sort_loop);
- udev_list_entry_insert_before(sort_loop, file_loop);
- sort_loop = NULL;
- break;
- }
- }
- /* current file already handled */
- if (sort_loop == NULL)
- continue;
- /* no later file, append to end of list */
- udev_list_entry_remove(sort_loop);
- udev_list_entry_append(sort_loop, &file_list);
- }
+ add_matching_files(udev, &file_list, filename, ".rules");
+ } else {
+ /* custom rules files location for testing */
+ add_matching_files(udev, &file_list, udev_get_rules_path(udev), ".rules");
}
/* add all filenames to the string buffer */
udev_list_entry_foreach(file_loop, udev_list_get_entry(&file_list)) {
- const char *filename = udev_list_entry_get_name(file_loop);
+ const char *filename = udev_list_entry_get_value(file_loop);
unsigned int filename_off;
filename_off = add_string(rules, filename);
/* the offset in the rule is limited to unsigned short */
if (filename_off < USHRT_MAX)
- udev_list_entry_set_flags(file_loop, filename_off);
+ udev_list_entry_set_num(file_loop, filename_off);
}
- /* parse list of files */
- udev_list_entry_foreach_safe(file_loop, file_tmp, udev_list_get_entry(&file_list)) {
- const char *filename = udev_list_entry_get_name(file_loop);
- unsigned int filename_off = udev_list_entry_get_flags(file_loop);
+ /* parse all rules files */
+ udev_list_entry_foreach(file_loop, udev_list_get_entry(&file_list)) {
+ const char *filename = udev_list_entry_get_value(file_loop);
+ unsigned int filename_off = udev_list_entry_get_num(file_loop);
+ struct stat st;
- if (stat(filename, &statbuf) == 0 && statbuf.st_size > 0)
- parse_file(rules, filename, filename_off);
- else
- err(udev, "can not read '%s'\n", filename);
- udev_list_entry_delete(file_loop);
+ if (stat(filename, &st) != 0) {
+ err(udev, "can not find '%s': %m\n", filename);
+ continue;
+ }
+ if (S_ISREG(st.st_mode) && st.st_size <= 0) {
+ info(udev, "ignore empty '%s'\n", filename);
+ continue;
+ }
+ if (S_ISCHR(st.st_mode)) {
+ info(udev, "ignore masked '%s'\n", filename);
+ continue;
+ }
+ parse_file(rules, filename, filename_off);
}
+ udev_list_cleanup_entries(udev, &file_list);
memset(&end_token, 0x00, sizeof(struct token));
end_token.type = TK_END;
@@ -2068,7 +2051,7 @@ enum escape_type {
ESCAPE_REPLACE,
};
-int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event)
+int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask)
{
struct token *cur;
struct token *rule;
@@ -2270,6 +2253,10 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
goto nomatch;
break;
}
+ case TK_M_EVENT_TIMEOUT:
+ info(event->udev, "OPTIONS event_timeout=%u\n", cur->key.event_timeout);
+ event->timeout_usec = cur->key.event_timeout * 1000 * 1000;
+ break;
case TK_M_PROGRAM:
{
char program[UTIL_PATH_SIZE];
@@ -2284,7 +2271,8 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
program,
&rules->buf[rule->rule.filename_off],
rule->rule.filename_line);
- if (util_run_program(event->udev, program, envp, result, sizeof(result), NULL, NULL, false) != 0) {
+
+ if (udev_event_spawn(event, program, envp, sigmask, result, sizeof(result)) < 0) {
if (cur->key.op != OP_NOMATCH)
goto nomatch;
} else {
@@ -2322,7 +2310,8 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
import,
&rules->buf[rule->rule.filename_off],
rule->rule.filename_line);
- if (import_program_into_properties(event->dev, import) != 0)
+
+ if (import_program_into_properties(event, import, sigmask) != 0)
if (cur->key.op != OP_NOMATCH)
goto nomatch;
break;
@@ -2337,7 +2326,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
struct udev_list_entry *entry;
entry = udev_device_add_property(event->dev, key, value);
- udev_list_entry_set_flags(entry, 1);
+ udev_list_entry_set_num(entry, true);
} else {
if (cur->key.op != OP_NOMATCH)
goto nomatch;
@@ -2365,7 +2354,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
if (pos[0] == '\0' || isspace(pos[0])) {
/* we import simple flags as 'FLAG=1' */
entry = udev_device_add_property(event->dev, key, "1");
- udev_list_entry_set_flags(entry, 1);
+ udev_list_entry_set_num(entry, true);
imported = true;
} else if (pos[0] == '=') {
const char *value;
@@ -2376,7 +2365,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
pos++;
pos[0] = '\0';
entry = udev_device_add_property(event->dev, key, value);
- udev_list_entry_set_flags(entry, 1);
+ udev_list_entry_set_num(entry, true);
imported = true;
}
}
@@ -2407,6 +2396,9 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
case TK_A_STRING_ESCAPE_REPLACE:
esc = ESCAPE_REPLACE;
break;
+ case TK_A_DB_PERSIST:
+ udev_device_set_db_persist(event->dev);
+ break;
case TK_A_INOTIFY_WATCH:
if (event->inotify_watch_final)
break;
@@ -2451,19 +2443,22 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
}
case TK_A_MODE:
{
- char mode[UTIL_NAME_SIZE];
+ char mode_str[UTIL_NAME_SIZE];
+ mode_t mode;
char *endptr;
if (event->mode_final)
break;
- if (cur->key.op == OP_ASSIGN_FINAL)
- event->mode_final = true;
- udev_event_apply_format(event, &rules->buf[cur->key.value_off], mode, sizeof(mode));
- event->mode = strtol(mode, &endptr, 8);
+ udev_event_apply_format(event, &rules->buf[cur->key.value_off], mode_str, sizeof(mode_str));
+ mode = strtol(mode_str, &endptr, 8);
if (endptr[0] != '\0') {
- err(event->udev, "invalide mode '%s' set default mode 0600\n", mode);
- event->mode = 0600;
+ err(event->udev, "ignoring invalid mode '%s'\n", mode_str);
+ break;
}
+ if (cur->key.op == OP_ASSIGN_FINAL)
+ event->mode_final = true;
+ event->mode_set = true;
+ event->mode = mode;
info(event->udev, "MODE %#o %s:%u\n",
event->mode,
&rules->buf[rule->rule.filename_off],
@@ -2497,6 +2492,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
break;
if (cur->key.op == OP_ASSIGN_FINAL)
event->mode_final = true;
+ event->mode_set = true;
event->mode = cur->key.mode;
info(event->udev, "MODE %#o %s:%u\n",
event->mode,
@@ -2518,7 +2514,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
entry = udev_device_add_property(event->dev, name, temp_value);
/* store in db, skip private keys */
if (name[0] != '.')
- udev_list_entry_set_flags(entry, 1);
+ udev_list_entry_set_num(entry, true);
} else {
udev_device_add_property(event->dev, name, NULL);
}
@@ -2601,9 +2597,6 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
}
}
break;
- case TK_A_EVENT_TIMEOUT:
- udev_device_set_event_timeout(event->dev, cur->key.event_timeout);
- break;
case TK_A_ATTR:
{
const char *key_name = &rules->buf[cur->key.attr_off];
@@ -2640,9 +2633,9 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
&rules->buf[rule->rule.filename_off],
rule->rule.filename_line);
list_entry = udev_list_entry_add(event->udev, &event->run_list,
- &rules->buf[cur->key.value_off], NULL, 1, 0);
+ &rules->buf[cur->key.value_off], NULL, UDEV_LIST_UNIQUE);
if (cur->key.fail_on_error)
- udev_list_entry_set_flags(list_entry, 1);
+ udev_list_entry_set_num(list_entry, true);
break;
}
case TK_A_GOTO:
diff --git a/udev/udev.h b/udev/udev.h
index 6833e60d61..88c32ec203 100644
--- a/udev/udev.h
+++ b/udev/udev.h
@@ -26,9 +26,6 @@
#include "libudev.h"
#include "libudev-private.h"
-#define DEFAULT_FAKE_PARTITIONS_COUNT 15
-#define UDEV_EVENT_TIMEOUT 180
-
#define UDEV_CTRL_SOCK_PATH "@/org/kernel/udev/udevd"
struct udev_event {
@@ -44,10 +41,15 @@ struct udev_event {
gid_t gid;
struct udev_list_node run_list;
int exec_delay;
+ unsigned long long birth_usec;
+ unsigned long long timeout_usec;
+ int fd_signal;
+ bool sigterm;
bool inotify_watch;
bool inotify_watch_final;
bool group_final;
bool owner_final;
+ bool mode_set;
bool mode_final;
bool name_final;
bool devlink_final;
@@ -64,17 +66,20 @@ struct udev_watch {
struct udev_rules;
struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names);
void udev_rules_unref(struct udev_rules *rules);
-int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event);
+int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask);
void udev_rules_apply_static_dev_perms(struct udev_rules *rules);
/* udev-event.c */
struct udev_event *udev_event_new(struct udev_device *dev);
void udev_event_unref(struct udev_event *event);
-int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules);
-int udev_event_execute_run(struct udev_event *event, const sigset_t *sigset);
size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size);
int udev_event_apply_subsys_kernel(struct udev_event *event, const char *string,
char *result, size_t maxsize, int read_value);
+int udev_event_spawn(struct udev_event *event,
+ const char *cmd, char **envp, const sigset_t *sigmask,
+ char *result, size_t ressize);
+int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules, const sigset_t *sigset);
+int udev_event_execute_run(struct udev_event *event, const sigset_t *sigset);
/* udev-watch.c */
int udev_watch_init(struct udev *udev);
diff --git a/udev/udevadm-control.c b/udev/udevadm-control.c
index 0447804c95..69da86563d 100644
--- a/udev/udevadm-control.c
+++ b/udev/udevadm-control.c
@@ -30,21 +30,25 @@
static void print_help(void)
{
printf("Usage: udevadm control COMMAND\n"
+ " --exit instruct the daemon to cleanup and exit\n"
" --log-priority=<level> set the udev log level for the daemon\n"
" --stop-exec-queue keep udevd from executing events, queue only\n"
" --start-exec-queue execute events, flush queue\n"
" --reload-rules reloads the rules files\n"
" --property=<KEY>=<value> set a global property for all events\n"
" --children-max=<N> maximum number of children\n"
+ " --timeout=<seconds> maximum time to block for a reply\n"
" --help print this help text\n\n");
}
int udevadm_control(struct udev *udev, int argc, char *argv[])
{
struct udev_ctrl *uctrl = NULL;
+ int timeout = 60;
int rc = 1;
static const struct option options[] = {
+ { "exit", no_argument, NULL, 'e' },
{ "log-priority", required_argument, NULL, 'l' },
{ "stop-exec-queue", no_argument, NULL, 's' },
{ "start-exec-queue", no_argument, NULL, 'S' },
@@ -52,6 +56,7 @@ int udevadm_control(struct udev *udev, int argc, char *argv[])
{ "property", required_argument, NULL, 'p' },
{ "env", required_argument, NULL, 'p' },
{ "children-max", required_argument, NULL, 'm' },
+ { "timeout", required_argument, NULL, 't' },
{ "help", no_argument, NULL, 'h' },
{}
};
@@ -67,39 +72,46 @@ int udevadm_control(struct udev *udev, int argc, char *argv[])
for (;;) {
int option;
- int i;
- char *endp;
- option = getopt_long(argc, argv, "l:sSRp:m:h", options, NULL);
+ option = getopt_long(argc, argv, "el:sSRp:m:h", options, NULL);
if (option == -1)
break;
switch (option) {
- case 'l':
+ case 'e':
+ if (udev_ctrl_send_exit(uctrl, timeout) < 0)
+ rc = 2;
+ else
+ rc = 0;
+ break;
+ case 'l': {
+ int i;
+
i = util_log_priority(optarg);
if (i < 0) {
fprintf(stderr, "invalid number '%s'\n", optarg);
- goto exit;
+ goto out;
}
- if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg)) < 0)
+ if (udev_ctrl_send_set_log_level(uctrl, util_log_priority(optarg), timeout) < 0)
rc = 2;
else
rc = 0;
break;
+ }
case 's':
- if (udev_ctrl_send_stop_exec_queue(uctrl) < 0)
+ if (udev_ctrl_send_stop_exec_queue(uctrl, timeout) < 0)
rc = 2;
else
rc = 0;
break;
case 'S':
- if (udev_ctrl_send_start_exec_queue(uctrl) < 0)
+ if (udev_ctrl_send_start_exec_queue(uctrl, timeout) < 0)
rc = 2;
else
rc = 0;
break;
case 'R':
- if (udev_ctrl_send_reload_rules(uctrl) < 0)
+ if (udev_ctrl_send_reload_rules(uctrl, timeout) < 0)
rc = 2;
else
rc = 0;
@@ -107,34 +119,45 @@ int udevadm_control(struct udev *udev, int argc, char *argv[])
case 'p':
if (strchr(optarg, '=') == NULL) {
fprintf(stderr, "expect <KEY>=<value> instead of '%s'\n", optarg);
- goto exit;
+ goto out;
}
- if (udev_ctrl_send_set_env(uctrl, optarg) < 0)
+ if (udev_ctrl_send_set_env(uctrl, optarg, timeout) < 0)
rc = 2;
else
rc = 0;
break;
- case 'm':
+ case 'm': {
+ char *endp;
+ int i;
+
i = strtoul(optarg, &endp, 0);
if (endp[0] != '\0' || i < 1) {
fprintf(stderr, "invalid number '%s'\n", optarg);
- goto exit;
+ goto out;
}
- if (udev_ctrl_send_set_children_max(uctrl, i) < 0)
+ if (udev_ctrl_send_set_children_max(uctrl, i, timeout) < 0)
rc = 2;
else
rc = 0;
break;
+ }
+ case 't': {
+ int seconds;
+
+ seconds = atoi(optarg);
+ if (seconds >= 0)
+ timeout = seconds;
+ else
+ fprintf(stderr, "invalid timeout value\n");
+ break;
+ }
case 'h':
print_help();
rc = 0;
break;
}
}
-
- if (rc == 1)
- err(udev, "unrecognized command\n");
-exit:
+out:
udev_ctrl_unref(uctrl);
return rc;
}
diff --git a/udev/udevadm-info.c b/udev/udevadm-info.c
index 9357f67208..87c1c32314 100644
--- a/udev/udevadm-info.c
+++ b/udev/udevadm-info.c
@@ -200,6 +200,81 @@ static int export_devices(struct udev *udev)
return 0;
}
+static void cleanup_dir(DIR *dir, mode_t mask, int depth)
+{
+ struct dirent *dent;
+
+ if (depth <= 0)
+ return;
+
+ for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+ struct stat stats;
+
+ if (dent->d_name[0] == '.')
+ continue;
+ if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0)
+ continue;
+ if ((stats.st_mode & mask) != 0)
+ continue;
+ if (S_ISDIR(stats.st_mode)) {
+ DIR *dir2;
+
+ dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
+ if (dir2 != NULL) {
+ cleanup_dir(dir2, mask, depth-1);
+ closedir(dir2);
+ }
+ unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
+ } else {
+ unlinkat(dirfd(dir), dent->d_name, 0);
+ }
+ }
+}
+
+static void cleanup_db(struct udev *udev)
+{
+ char filename[UTIL_PATH_SIZE];
+ DIR *dir;
+
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/queue.bin", NULL);
+ unlink(filename);
+
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/data", NULL);
+ dir = opendir(filename);
+ if (dir != NULL) {
+ cleanup_dir(dir, S_ISVTX, 1);
+ closedir(dir);
+ }
+
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/links", NULL);
+ dir = opendir(filename);
+ if (dir != NULL) {
+ cleanup_dir(dir, 0, 2);
+ closedir(dir);
+ }
+
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/tags", NULL);
+ dir = opendir(filename);
+ if (dir != NULL) {
+ cleanup_dir(dir, 0, 2);
+ closedir(dir);
+ }
+
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/watch", NULL);
+ dir = opendir(filename);
+ if (dir != NULL) {
+ cleanup_dir(dir, 0, 1);
+ closedir(dir);
+ }
+
+ util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev), "/firmware-missing", NULL);
+ dir = opendir(filename);
+ if (dir != NULL) {
+ cleanup_dir(dir, 0, 1);
+ closedir(dir);
+ }
+}
+
int udevadm_info(struct udev *udev, int argc, char *argv[])
{
struct udev_device *device = NULL;
@@ -216,6 +291,7 @@ int udevadm_info(struct udev *udev, int argc, char *argv[])
{ "path", required_argument, NULL, 'p' },
{ "query", required_argument, NULL, 'q' },
{ "attribute-walk", no_argument, NULL, 'a' },
+ { "cleanup-db", no_argument, NULL, 'c' },
{ "export-db", no_argument, NULL, 'e' },
{ "root", no_argument, NULL, 'r' },
{ "run", no_argument, NULL, 'R' },
@@ -248,7 +324,7 @@ int udevadm_info(struct udev *udev, int argc, char *argv[])
int option;
struct stat statbuf;
- option = getopt_long(argc, argv, "aed:n:p:q:rxP:RVh", options, NULL);
+ option = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL);
if (option == -1)
break;
@@ -345,6 +421,9 @@ int udevadm_info(struct udev *udev, int argc, char *argv[])
case 'e':
export_devices(udev);
goto exit;
+ case 'c':
+ cleanup_db(udev);
+ goto exit;
case 'x':
export = true;
break;
@@ -371,6 +450,7 @@ int udevadm_info(struct udev *udev, int argc, char *argv[])
" --export export key/value pairs\n"
" --export-prefix export the key name with a prefix\n"
" --export-db export the content of the udev database\n"
+ " --cleanup-db cleanup the udev database\n"
" --help\n\n");
goto exit;
default:
diff --git a/udev/udevadm-monitor.c b/udev/udevadm-monitor.c
index 85252bb7d9..f3a72cf17e 100644
--- a/udev/udevadm-monitor.c
+++ b/udev/udevadm-monitor.c
@@ -27,18 +27,18 @@
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/un.h>
-#include <sys/select.h>
+#include <sys/epoll.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include "udev.h"
-static int udev_exit;
+static bool udev_exit;
static void sig_handler(int signum)
{
if (signum == SIGINT || signum == SIGTERM)
- udev_exit = 1;
+ udev_exit = true;
}
static void print_device(struct udev_device *device, const char *source, int prop)
@@ -69,14 +69,16 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
struct sigaction act;
sigset_t mask;
int option;
- int prop = 0;
- int print_kernel = 0;
- int print_udev = 0;
+ bool prop = false;
+ bool print_kernel = false;
+ bool print_udev = false;
struct udev_list_node subsystem_match_list;
struct udev_list_node tag_match_list;
struct udev_monitor *udev_monitor = NULL;
struct udev_monitor *kernel_monitor = NULL;
- fd_set readfds;
+ int fd_ep = -1;
+ int fd_kernel = -1, fd_udev = -1;
+ struct epoll_event ep_kernel, ep_udev;
int rc = 0;
static const struct option options[] = {
@@ -92,6 +94,7 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
udev_list_init(&subsystem_match_list);
udev_list_init(&tag_match_list);
+
for (;;) {
option = getopt_long(argc, argv, "pekus:t:h", options, NULL);
if (option == -1)
@@ -100,13 +103,13 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
switch (option) {
case 'p':
case 'e':
- prop = 1;
+ prop = true;
break;
case 'k':
- print_kernel = 1;
+ print_kernel = true;
break;
case 'u':
- print_udev = 1;
+ print_udev = true;
break;
case 's':
{
@@ -119,11 +122,11 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
devtype[0] = '\0';
devtype++;
}
- udev_list_entry_add(udev, &subsystem_match_list, subsys, devtype, 0, 0);
+ udev_list_entry_add(udev, &subsystem_match_list, subsys, devtype, 0);
break;
}
case 't':
- udev_list_entry_add(udev, &tag_match_list, optarg, NULL, 0, 0);
+ udev_list_entry_add(udev, &tag_match_list, optarg, NULL, 0);
break;
case 'h':
printf("Usage: udevadm monitor [--property] [--kernel] [--udev] [--help]\n"
@@ -133,14 +136,13 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
" --subsystem-match=<subsystem[/devtype]> filter events by subsystem\n"
" --tag-match=<tag> filter events by tag\n"
" --help\n\n");
- default:
goto out;
}
}
if (!print_kernel && !print_udev) {
- print_kernel = 1;
- print_udev =1;
+ print_kernel = true;
+ print_udev = true;
}
/* set signal handlers */
@@ -155,6 +157,12 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
sigaddset(&mask, SIGTERM);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ fd_ep = epoll_create1(EPOLL_CLOEXEC);
+ if (fd_ep < 0) {
+ err(udev, "error creating epoll fd: %m\n");
+ goto out;
+ }
+
printf("monitor will print the received events for:\n");
if (print_udev) {
struct udev_list_entry *entry;
@@ -166,6 +174,7 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
goto out;
}
udev_monitor_set_receive_buffer_size(udev_monitor, 128*1024*1024);
+ fd_udev = udev_monitor_get_fd(udev_monitor);
udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) {
const char *subsys = udev_list_entry_get_name(entry);
@@ -187,8 +196,18 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
rc = 2;
goto out;
}
+
+ memset(&ep_udev, 0, sizeof(struct epoll_event));
+ ep_udev.events = EPOLLIN;
+ ep_udev.data.fd = fd_udev;
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) {
+ err(udev, "fail to add fd to epoll: %m\n");
+ goto out;
+ }
+
printf("UDEV - the event which udev sends out after rule processing\n");
}
+
if (print_kernel) {
struct udev_list_entry *entry;
@@ -199,6 +218,7 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
goto out;
}
udev_monitor_set_receive_buffer_size(kernel_monitor, 128*1024*1024);
+ fd_kernel = udev_monitor_get_fd(kernel_monitor);
udev_list_entry_foreach(entry, udev_list_get_entry(&subsystem_match_list)) {
const char *subsys = udev_list_entry_get_name(entry);
@@ -212,49 +232,54 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
rc = 4;
goto out;
}
+
+ memset(&ep_kernel, 0, sizeof(struct epoll_event));
+ ep_kernel.events = EPOLLIN;
+ ep_kernel.data.fd = fd_kernel;
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_kernel, &ep_kernel) < 0) {
+ err(udev, "fail to add fd to epoll: %m\n");
+ goto out;
+ }
+
printf("KERNEL - the kernel uevent\n");
}
printf("\n");
while (!udev_exit) {
int fdcount;
+ struct epoll_event ev[4];
+ int i;
- FD_ZERO(&readfds);
- if (kernel_monitor != NULL)
- FD_SET(udev_monitor_get_fd(kernel_monitor), &readfds);
- if (udev_monitor != NULL)
- FD_SET(udev_monitor_get_fd(udev_monitor), &readfds);
-
- fdcount = select(MAX(udev_monitor_get_fd(kernel_monitor), udev_monitor_get_fd(udev_monitor))+1,
- &readfds, NULL, NULL, NULL);
+ fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1);
if (fdcount < 0) {
if (errno != EINTR)
fprintf(stderr, "error receiving uevent message: %m\n");
continue;
}
- if ((kernel_monitor != NULL) && FD_ISSET(udev_monitor_get_fd(kernel_monitor), &readfds)) {
- struct udev_device *device;
-
- device = udev_monitor_receive_device(kernel_monitor);
- if (device == NULL)
- continue;
- print_device(device, "KERNEL", prop);
- udev_device_unref(device);
- }
-
- if ((udev_monitor != NULL) && FD_ISSET(udev_monitor_get_fd(udev_monitor), &readfds)) {
- struct udev_device *device;
-
- device = udev_monitor_receive_device(udev_monitor);
- if (device == NULL)
- continue;
- print_device(device, "UDEV", prop);
- udev_device_unref(device);
+ for (i = 0; i < fdcount; i++) {
+ if (ev[i].data.fd == fd_kernel && ev[i].events & EPOLLIN) {
+ struct udev_device *device;
+
+ device = udev_monitor_receive_device(kernel_monitor);
+ if (device == NULL)
+ continue;
+ print_device(device, "KERNEL", prop);
+ udev_device_unref(device);
+ } else if (ev[i].data.fd == fd_udev && ev[i].events & EPOLLIN) {
+ struct udev_device *device;
+
+ device = udev_monitor_receive_device(udev_monitor);
+ if (device == NULL)
+ continue;
+ print_device(device, "UDEV", prop);
+ udev_device_unref(device);
+ }
}
}
-
out:
+ if (fd_ep >= 0)
+ close(fd_ep);
udev_monitor_unref(udev_monitor);
udev_monitor_unref(kernel_monitor);
udev_list_cleanup_entries(udev, &subsystem_match_list);
diff --git a/udev/udevadm-settle.c b/udev/udevadm-settle.c
index 1423cec6dc..b717ae60de 100644
--- a/udev/udevadm-settle.c
+++ b/udev/udevadm-settle.c
@@ -29,26 +29,13 @@
#include <getopt.h>
#include <signal.h>
#include <time.h>
+#include <sys/inotify.h>
+#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "udev.h"
-#define DEFAULT_TIMEOUT 180
-#define LOOP_PER_SECOND 20
-
-static volatile sig_atomic_t is_timeout;
-
-static void sig_handler(int signum)
-{
- switch (signum) {
- case SIGALRM:
- is_timeout = 1;
- case SIGUSR1:
- ;
- }
-}
-
int udevadm_settle(struct udev *udev, int argc, char *argv[])
{
static const struct option options[] = {
@@ -60,30 +47,18 @@ int udevadm_settle(struct udev *udev, int argc, char *argv[])
{ "help", no_argument, NULL, 'h' },
{}
};
+ unsigned long long start_usec = now_usec();
unsigned long long start = 0;
unsigned long long end = 0;
int quiet = 0;
const char *exists = NULL;
- int timeout = DEFAULT_TIMEOUT;
- struct sigaction act;
- sigset_t mask;
+ unsigned int timeout = 120;
+ struct pollfd pfd[1];
struct udev_queue *udev_queue = NULL;
- int rc = 1;
+ int rc = EXIT_FAILURE;
dbg(udev, "version %s\n", VERSION);
- /* set signal handlers */
- memset(&act, 0x00, sizeof(act));
- act.sa_handler = sig_handler;
- sigemptyset (&act.sa_mask);
- act.sa_flags = 0;
- sigaction(SIGALRM, &act, NULL);
- sigaction(SIGUSR1, &act, NULL);
- sigemptyset(&mask);
- sigaddset(&mask, SIGUSR1);
- sigaddset(&mask, SIGALRM);
- sigprocmask(SIG_UNBLOCK, &mask, NULL);
-
for (;;) {
int option;
int seconds;
@@ -125,11 +100,6 @@ int udevadm_settle(struct udev *udev, int argc, char *argv[])
}
}
- if (timeout > 0)
- alarm(timeout);
- else
- is_timeout = 1;
-
udev_queue = udev_queue_new(udev);
if (udev_queue == NULL)
exit(2);
@@ -168,62 +138,84 @@ int udevadm_settle(struct udev *udev, int argc, char *argv[])
uctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH);
if (uctrl != NULL) {
- sigset_t oldmask;
-
- sigemptyset(&mask);
- sigaddset(&mask, SIGUSR1);
- sigaddset(&mask, SIGALRM);
- sigprocmask(SIG_BLOCK, &mask, &oldmask);
- if (udev_ctrl_send_settle(uctrl) > 0)
- sigsuspend(&oldmask);
- sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ if (udev_ctrl_send_ping(uctrl, timeout) < 0) {
+ info(udev, "no connection to daemon\n");
+ udev_ctrl_unref(uctrl);
+ rc = EXIT_SUCCESS;
+ goto out;
+ }
udev_ctrl_unref(uctrl);
}
}
+ pfd[0].events = POLLIN;
+ pfd[0].fd = inotify_init1(IN_CLOEXEC);
+ if (pfd[0].fd < 0) {
+ err(udev, "inotify_init failed: %m\n");
+ } else {
+ if (inotify_add_watch(pfd[0].fd, udev_get_run_path(udev), IN_MOVED_TO) < 0) {
+ err(udev, "watching '%s' failed\n", udev_get_run_path(udev));
+ close(pfd[0].fd);
+ pfd[0].fd = -1;
+ }
+ }
+
for (;;) {
struct stat statbuf;
- const struct timespec duration = { 0 , 1000 * 1000 * 1000 / LOOP_PER_SECOND };
if (exists != NULL && stat(exists, &statbuf) == 0) {
- rc = 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 = 0;
+ rc = EXIT_SUCCESS;
break;
}
} else {
/* exit if queue is empty */
if (udev_queue_get_queue_is_empty(udev_queue)) {
- rc = 0;
+ rc = EXIT_SUCCESS;
break;
}
}
- if (is_timeout)
- break;
+ if (pfd[0].fd >= 0) {
+ /* wake up once every second, or whenever the queue file gets gets closed */
+ if (poll(pfd, 1, 1000) > 0 && pfd[0].revents & POLLIN) {
+ char buf[sizeof(struct inotify_event) + PATH_MAX];
- nanosleep(&duration, NULL);
- }
+ read(pfd[0].fd, buf, sizeof(buf));
+ }
+ } else {
+ sleep(1);
+ }
- /* if we reached the timeout, print the list of remaining events */
- if (is_timeout) {
- struct udev_list_entry *list_entry;
-
- if (!quiet && udev_queue_get_queued_list_entry(udev_queue) != NULL) {
- info(udev, "timeout waiting for udev queue\n");
- 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));
+ if (timeout > 0) {
+ unsigned long long age_usec;
+
+ age_usec = now_usec() - 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) {
+ info(udev, "timeout waiting for udev queue\n");
+ 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;
+ }
}
}
-
+out:
+ if (pfd[0].fd >= 0)
+ close(pfd[0].fd);
udev_queue_unref(udev_queue);
return rc;
}
diff --git a/udev/udevadm-test.c b/udev/udevadm-test.c
index 4db70c49dd..bb7a0fd8a1 100644
--- a/udev/udevadm-test.c
+++ b/udev/udevadm-test.c
@@ -27,23 +27,27 @@
#include <signal.h>
#include <syslog.h>
#include <getopt.h>
+#include <sys/signalfd.h>
#include "udev.h"
int udevadm_test(struct udev *udev, int argc, char *argv[])
{
+ int resolve_names = 1;
char filename[UTIL_PATH_SIZE];
const char *action = "add";
const char *syspath = NULL;
- struct udev_event *event;
- struct udev_device *dev;
+ struct udev_event *event = NULL;
+ struct udev_device *dev = NULL;
struct udev_rules *rules = NULL;
struct udev_list_entry *entry;
+ sigset_t mask, sigmask_orig;
int err;
int rc = 0;
static const struct option options[] = {
{ "action", required_argument, NULL, 'a' },
+ { "resolve-names", required_argument, NULL, 'N' },
{ "help", no_argument, NULL, 'h' },
{}
};
@@ -53,7 +57,7 @@ int udevadm_test(struct udev *udev, int argc, char *argv[])
for (;;) {
int option;
- option = getopt_long(argc, argv, "a:s:fh", options, NULL);
+ option = getopt_long(argc, argv, "a:s:N:fh", options, NULL);
if (option == -1)
break;
@@ -62,21 +66,34 @@ int udevadm_test(struct udev *udev, int argc, char *argv[])
case 'a':
action = optarg;
break;
+ case 'N':
+ if (strcmp (optarg, "early") == 0) {
+ resolve_names = 1;
+ } else if (strcmp (optarg, "late") == 0) {
+ resolve_names = 0;
+ } else if (strcmp (optarg, "never") == 0) {
+ resolve_names = -1;
+ } else {
+ fprintf(stderr, "resolve-names must be early, late or never\n");
+ err(udev, "resolve-names must be early, late or never\n");
+ exit(EXIT_FAILURE);
+ }
+ break;
case 'h':
printf("Usage: udevadm test OPTIONS <syspath>\n"
" --action=<string> set action string\n"
" --help\n\n");
- exit(0);
+ exit(EXIT_SUCCESS);
default:
- exit(1);
+ exit(EXIT_FAILURE);
}
}
syspath = argv[optind];
if (syspath == NULL) {
fprintf(stderr, "syspath parameter missing\n");
- rc = 1;
- goto exit;
+ rc = 2;
+ goto out;
}
printf("This program is for debugging only, it does not run any program,\n"
@@ -84,11 +101,13 @@ int udevadm_test(struct udev *udev, int argc, char *argv[])
"some values may be different, or not available at a simulation run.\n"
"\n");
- rules = udev_rules_new(udev, 1);
+ sigprocmask(SIG_SETMASK, NULL, &sigmask_orig);
+
+ rules = udev_rules_new(udev, resolve_names);
if (rules == NULL) {
fprintf(stderr, "error reading rules\n");
- rc = 1;
- goto exit;
+ rc = 3;
+ goto out;
}
/* add /sys if needed */
@@ -101,8 +120,8 @@ int udevadm_test(struct udev *udev, int argc, char *argv[])
dev = udev_device_new_from_syspath(udev, filename);
if (dev == NULL) {
fprintf(stderr, "unable to open device '%s'\n", filename);
- rc = 2;
- goto exit;
+ rc = 4;
+ goto out;
}
/* skip reading of db, but read kernel parameters */
@@ -111,24 +130,34 @@ int udevadm_test(struct udev *udev, int argc, char *argv[])
udev_device_set_action(dev, action);
event = udev_event_new(dev);
- err = udev_event_execute_rules(event, rules);
- if (udev_device_get_event_timeout(dev) >= 0)
- info(udev, "custom event timeout: %i\n", udev_device_get_event_timeout(dev));
+ sigfillset(&mask);
+ sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
+ event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
+ if (event->fd_signal < 0) {
+ fprintf(stderr, "error creating signalfd\n");
+ rc = 5;
+ goto out;
+ }
+
+ err = udev_event_execute_rules(event, rules, &sigmask_orig);
udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev))
- info(udev, "%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry));
+ printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry));
- if (err == 0)
+ if (err == 0) {
udev_list_entry_foreach(entry, udev_list_get_entry(&event->run_list)) {
char program[UTIL_PATH_SIZE];
udev_event_apply_format(event, udev_list_entry_get_name(entry), program, sizeof(program));
- info(udev, "run: '%s'\n", program);
+ printf("run: '%s'\n", program);
}
+ }
+out:
+ if (event != NULL && event->fd_signal >= 0)
+ close(event->fd_signal);
udev_event_unref(event);
udev_device_unref(dev);
-exit:
udev_rules_unref(rules);
return rc;
}
diff --git a/udev/udevadm.c b/udev/udevadm.c
index 336d723705..2a29158917 100644
--- a/udev/udevadm.c
+++ b/udev/udevadm.c
@@ -25,7 +25,7 @@
#include "udev.h"
-static int debug;
+static bool debug;
static void log_fn(struct udev *udev, int priority,
const char *file, int line, const char *fn,
@@ -101,7 +101,7 @@ static const struct command cmds[] = {
.name = "test",
.cmd = udevadm_test,
.help = "simulation run",
- .debug = 1,
+ .debug = true,
},
{
.name = "version",
@@ -117,7 +117,7 @@ static const struct command cmds[] = {
static int run_command(struct udev *udev, const struct command *cmd, int argc, char *argv[])
{
if (cmd->debug) {
- debug = 1;
+ debug = true;
if (udev_get_log_priority(udev) < LOG_INFO)
udev_set_log_priority(udev, LOG_INFO);
}
@@ -155,7 +155,7 @@ int main(int argc, char *argv[])
switch (option) {
case 'd':
- debug = 1;
+ debug = true;
if (udev_get_log_priority(udev) < LOG_INFO)
udev_set_log_priority(udev, LOG_INFO);
break;
diff --git a/udev/udevadm.xml b/udev/udevadm.xml
index 7a682a68ef..b5fb971f3b 100644
--- a/udev/udevadm.xml
+++ b/udev/udevadm.xml
@@ -150,6 +150,12 @@
</listitem>
</varlistentry>
<varlistentry>
+ <term><option>--cleanup-db</option></term>
+ <listitem>
+ <para>Cleanup the udev database.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><option>--version</option></term>
<listitem>
<para>Print version.</para>
@@ -256,7 +262,7 @@
<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 180 seconds. A value of 0 will check if the queue is 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>
@@ -297,6 +303,12 @@
<para>Modify the internal state of the running udev daemon.</para>
<variablelist>
<varlistentry>
+ <term><option>--exit</option></term>
+ <listitem>
+ <para>Signal and wait for udevd to exit.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><option>--log-priority=<replaceable>value</replaceable></option></term>
<listitem>
<para>Set the internal log level of udevd. Valid values are the numerical
@@ -340,6 +352,12 @@
</listitem>
</varlistentry>
<varlistentry>
+ <term><option>--timeout=</option><replaceable>seconds</replaceable></term>
+ <listitem>
+ <para>The maximum number seonds to wait for a reply from udevd.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><option>--help</option></term>
<listitem>
<para>Print help text.</para>
diff --git a/udev/udevd.c b/udev/udevd.c
index df5c1995bc..e7384e19a2 100644
--- a/udev/udevd.c
+++ b/udev/udevd.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004-2009 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2004-2011 Kay Sievers <kay.sievers@vrfy.org>
* Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca>
* Copyright (C) 2009 Canonical Ltd.
* Copyright (C) 2009 Scott James Remnant <scott@netsplit.com>
@@ -36,7 +36,7 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/signalfd.h>
-#include <sys/select.h>
+#include <sys/epoll.h>
#include <sys/poll.h>
#include <sys/wait.h>
#include <sys/stat.h>
@@ -47,9 +47,6 @@
#include "udev.h"
#include "sd-daemon.h"
-#define UDEVD_PRIORITY -4
-#define UDEV_PRIORITY -2
-
static bool debug;
static void log_fn(struct udev *udev, int priority,
@@ -76,33 +73,18 @@ 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 };
-static pid_t settle_pid;
+static int fd_signal = -1;
+static int fd_ep = -1;
+static int fd_inotify = -1;
static bool stop_exec_queue;
static bool reload_config;
static int children;
static int children_max;
static int exec_delay;
-static sigset_t orig_sigmask;
-static struct udev_list_node event_list;
-static struct udev_list_node worker_list;
+static sigset_t sigmask_orig;
+static UDEV_LIST(event_list);
+static UDEV_LIST(worker_list);
static bool udev_exit;
-static volatile sig_atomic_t worker_exit;
-
-enum poll_fd {
- FD_CONTROL,
- FD_NETLINK,
- FD_INOTIFY,
- FD_SIGNAL,
- FD_WORKER,
-};
-
-static struct pollfd pfd[] = {
- [FD_NETLINK] = { .events = POLLIN, .fd = -1 },
- [FD_WORKER] = { .events = POLLIN, .fd = -1 },
- [FD_SIGNAL] = { .events = POLLIN, .fd = -1 },
- [FD_INOTIFY] = { .events = POLLIN, .fd = -1 },
- [FD_CONTROL] = { .events = POLLIN, .fd = -1 },
-};
enum event_state {
EVENT_UNDEF,
@@ -135,6 +117,8 @@ static struct event *node_to_event(struct udev_list_node *node)
return (struct event *)event;
}
+static void event_queue_cleanup(struct udev *udev, enum event_state type);
+
enum worker_state {
WORKER_UNDEF,
WORKER_RUNNING,
@@ -167,61 +151,65 @@ static struct worker *node_to_worker(struct udev_list_node *node)
return (struct worker *)worker;
}
-static void event_queue_delete(struct event *event)
+static void event_queue_delete(struct event *event, bool export)
{
udev_list_node_remove(&event->node);
- /* mark as failed, if "add" event returns non-zero */
- if (event->exitcode != 0 && strcmp(udev_device_get_action(event->dev), "remove") != 0)
- udev_queue_export_device_failed(udev_queue_export, event->dev);
- else
- udev_queue_export_device_finished(udev_queue_export, event->dev);
-
- info(event->udev, "seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode);
+ if (export) {
+ /* mark as failed, if "add" event returns non-zero */
+ if (event->exitcode != 0 && strcmp(udev_device_get_action(event->dev), "remove") != 0)
+ udev_queue_export_device_failed(udev_queue_export, event->dev);
+ else
+ udev_queue_export_device_finished(udev_queue_export, event->dev);
+ info(event->udev, "seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode);
+ }
udev_device_unref(event->dev);
free(event);
}
-static void event_sig_handler(int signum)
-{
- switch (signum) {
- case SIGALRM:
- _exit(1);
- break;
- case SIGTERM:
- worker_exit = true;
- break;
- }
-}
-
static struct worker *worker_ref(struct worker *worker)
{
worker->refcount++;
return worker;
}
+static void worker_cleanup(struct worker *worker)
+{
+ udev_list_node_remove(&worker->node);
+ udev_monitor_unref(worker->monitor);
+ children--;
+ free(worker);
+}
+
static void worker_unref(struct worker *worker)
{
worker->refcount--;
if (worker->refcount > 0)
return;
-
- udev_list_node_remove(&worker->node);
- udev_monitor_unref(worker->monitor);
- children--;
info(worker->udev, "worker [%u] cleaned up\n", worker->pid);
- free(worker);
+ worker_cleanup(worker);
+}
+
+static void worker_list_cleanup(struct udev *udev)
+{
+ struct udev_list_node *loop, *tmp;
+
+ udev_list_node_foreach_safe(loop, tmp, &worker_list) {
+ struct worker *worker = node_to_worker(loop);
+
+ worker_cleanup(worker);
+ }
}
static void worker_new(struct event *event)
{
+ struct udev *udev = event->udev;
struct worker *worker;
struct udev_monitor *worker_monitor;
pid_t pid;
- struct sigaction act;
/* listen for new events */
- worker_monitor = udev_monitor_new_from_netlink(event->udev, NULL);
+ worker_monitor = udev_monitor_new_from_netlink(udev, NULL);
if (worker_monitor == NULL)
return;
/* allow the main daemon netlink address to send devices to the worker */
@@ -235,80 +223,93 @@ static void worker_new(struct event *event)
}
/* worker + event reference */
worker->refcount = 2;
- worker->udev = event->udev;
+ worker->udev = udev;
pid = fork();
switch (pid) {
case 0: {
- sigset_t sigmask;
- struct udev_device *dev;
- struct pollfd pmon = {
- .fd = udev_monitor_get_fd(worker_monitor),
- .events = POLLIN,
- };
+ struct udev_device *dev = NULL;
+ int fd_monitor;
+ struct epoll_event ep_signal, ep_monitor;
+ sigset_t mask;
+ int rc = EXIT_SUCCESS;
+
+ /* move initial device from queue */
+ dev = event->dev;
+ event->dev = NULL;
+ 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(pfd[FD_SIGNAL].fd);
+ close(fd_signal);
+ close(fd_ep);
close(worker_watch[READ_END]);
- udev_log_close();
- udev_log_init("udevd-work");
- setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY);
-
- /* set signal handlers */
- memset(&act, 0x00, sizeof(act));
- act.sa_handler = event_sig_handler;
- sigemptyset (&act.sa_mask);
- act.sa_flags = 0;
- sigaction(SIGTERM, &act, NULL);
- sigaction(SIGALRM, &act, NULL);
-
- /* unblock SIGALRM */
- sigfillset(&sigmask);
- sigdelset(&sigmask, SIGALRM);
- sigprocmask(SIG_SETMASK, &sigmask, NULL);
- /* SIGTERM is unblocked in ppoll() */
- sigdelset(&sigmask, SIGTERM);
+
+ sigfillset(&mask);
+ fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
+ if (fd_signal < 0) {
+ err(udev, "error creating signalfd %m\n");
+ rc = 2;
+ goto out;
+ }
+
+ fd_ep = epoll_create1(EPOLL_CLOEXEC);
+ if (fd_ep < 0) {
+ err(udev, "error creating epoll fd: %m\n");
+ rc = 3;
+ goto out;
+ }
+
+ memset(&ep_signal, 0, sizeof(struct epoll_event));
+ ep_signal.events = EPOLLIN;
+ ep_signal.data.fd = fd_signal;
+
+ fd_monitor = udev_monitor_get_fd(worker_monitor);
+ memset(&ep_monitor, 0, sizeof(struct epoll_event));
+ ep_monitor.events = EPOLLIN;
+ ep_monitor.data.fd = fd_monitor;
+
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
+ epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) {
+ err(udev, "fail to add fds to epoll: %m\n");
+ rc = 4;
+ goto out;
+ }
/* request TERM signal if parent exits */
prctl(PR_SET_PDEATHSIG, SIGTERM);
- /* initial device */
- dev = event->dev;
-
- do {
+ for (;;) {
struct udev_event *udev_event;
- struct worker_message msg = {};
- int err;
+ struct worker_message msg;
int failed = 0;
+ int err;
- info(event->udev, "seq %llu running\n", udev_device_get_seqnum(dev));
+ info(udev, "seq %llu running\n", udev_device_get_seqnum(dev));
udev_event = udev_event_new(dev);
- if (udev_event == NULL)
- _exit(3);
+ if (udev_event == NULL) {
+ rc = 5;
+ goto out;
+ }
- /* set timeout to prevent hanging processes */
- alarm(UDEV_EVENT_TIMEOUT);
+ /* needed for SIGCHLD/SIGTERM in spawn() */
+ udev_event->fd_signal = fd_signal;
if (exec_delay > 0)
udev_event->exec_delay = exec_delay;
/* apply rules, create node, symlinks */
- err = udev_event_execute_rules(udev_event, rules);
-
- /* rules may change/disable the timeout */
- if (udev_device_get_event_timeout(dev) >= 0)
- alarm(udev_device_get_event_timeout(dev));
+ err = udev_event_execute_rules(udev_event, rules, &sigmask_orig);
if (err == 0)
- failed = udev_event_execute_run(udev_event, &orig_sigmask);
-
- alarm(0);
+ failed = udev_event_execute_run(udev_event, &sigmask_orig);
/* apply/restore inotify watch */
if (err == 0 && udev_event->inotify_watch) {
- udev_watch_begin(udev_event->udev, dev);
+ udev_watch_begin(udev, dev);
udev_device_update_db(dev);
}
@@ -316,6 +317,7 @@ static void worker_new(struct event *event)
udev_monitor_send_device(worker_monitor, NULL, dev);
/* send udevd the result of the event execution */
+ memset(&msg, 0, sizeof(struct worker_message));
if (err != 0)
msg.exitcode = err;
else if (failed != 0)
@@ -323,36 +325,70 @@ static void worker_new(struct event *event)
msg.pid = getpid();
send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0);
- info(event->udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err);
- udev_event_unref(udev_event);
+ info(udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err);
+
udev_device_unref(dev);
dev = NULL;
- /* wait for more device messages or signal from udevd */
- while (!worker_exit) {
- int fdcount;
+ if (udev_event->sigterm) {
+ udev_event_unref(udev_event);
+ goto out;
+ }
- fdcount = ppoll(&pmon, 1, NULL, &sigmask);
- if (fdcount < 0)
- continue;
+ udev_event_unref(udev_event);
- if (pmon.revents & POLLIN) {
- dev = udev_monitor_receive_device(worker_monitor);
- if (dev != NULL)
- break;
+ /* wait for more device messages from main udevd, or term signal */
+ while (dev == NULL) {
+ struct epoll_event ev[4];
+ int fdcount;
+ int i;
+
+ fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1);
+ if (fdcount < 0) {
+ if (errno == EINTR)
+ continue;
+ err = -errno;
+ err(udev, "failed to poll: %m\n");
+ goto out;
}
- }
- } while (dev != NULL);
+ for (i = 0; i < fdcount; i++) {
+ if (ev[i].data.fd == fd_monitor && ev[i].events & EPOLLIN) {
+ dev = udev_monitor_receive_device(worker_monitor);
+ } else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN) {
+ struct signalfd_siginfo fdsi;
+ ssize_t size;
+
+ size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
+ if (size != sizeof(struct signalfd_siginfo))
+ continue;
+ switch (fdsi.ssi_signo) {
+ case SIGTERM:
+ goto out;
+ }
+ }
+ }
+ }
+ }
+out:
+ udev_device_unref(dev);
+ if (fd_signal >= 0)
+ close(fd_signal);
+ if (fd_ep >= 0)
+ close(fd_ep);
+ close(fd_inotify);
+ close(worker_watch[WRITE_END]);
+ udev_rules_unref(rules);
udev_monitor_unref(worker_monitor);
+ udev_unref(udev);
udev_log_close();
- exit(0);
+ exit(rc);
}
case -1:
udev_monitor_unref(worker_monitor);
event->state = EVENT_QUEUED;
free(worker);
- err(event->udev, "fork of child failed: %m\n");
+ err(udev, "fork of child failed: %m\n");
break;
default:
/* close monitor, but keep address around */
@@ -364,7 +400,7 @@ static void worker_new(struct event *event)
event->state = EVENT_RUNNING;
udev_list_node_append(&worker->node, &worker_list);
children++;
- info(event->udev, "seq %llu forked new worker [%u]\n", udev_device_get_seqnum(event->dev), pid);
+ info(udev, "seq %llu forked new worker [%u]\n", udev_device_get_seqnum(event->dev), pid);
break;
}
}
@@ -535,7 +571,7 @@ static bool is_devpath_busy(struct event *event)
return false;
}
-static void events_start(struct udev *udev)
+static void event_queue_start(struct udev *udev)
{
struct udev_list_node *loop;
@@ -555,14 +591,28 @@ static void events_start(struct udev *udev)
}
}
-static void worker_returned(void)
+static void event_queue_cleanup(struct udev *udev, enum event_state match_type)
+{
+ struct udev_list_node *loop, *tmp;
+
+ udev_list_node_foreach_safe(loop, tmp, &event_list) {
+ struct event *event = node_to_event(loop);
+
+ if (match_type != EVENT_UNDEF && match_type != event->state)
+ continue;
+
+ event_queue_delete(event, false);
+ }
+}
+
+static void worker_returned(int fd_worker)
{
for (;;) {
struct worker_message msg;
ssize_t size;
struct udev_list_node *loop;
- size = recv(pfd[FD_WORKER].fd, &msg, sizeof(struct worker_message), MSG_DONTWAIT);
+ size = recv(fd_worker, &msg, sizeof(struct worker_message), MSG_DONTWAIT);
if (size != sizeof(struct worker_message))
break;
@@ -575,7 +625,7 @@ static void worker_returned(void)
/* worker returned */
worker->event->exitcode = msg.exitcode;
- event_queue_delete(worker->event);
+ event_queue_delete(worker->event, true);
worker->event = NULL;
if (worker->state != WORKER_KILLED)
worker->state = WORKER_IDLE;
@@ -586,16 +636,21 @@ static void worker_returned(void)
}
/* receive the udevd message from userspace */
-static void handle_ctrl_msg(struct udev_ctrl *uctrl)
+static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl)
{
struct udev *udev = udev_ctrl_get_udev(uctrl);
- struct udev_ctrl_msg *ctrl_msg;
+ struct udev_ctrl_connection *ctrl_conn;
+ struct udev_ctrl_msg *ctrl_msg = NULL;
const char *str;
int i;
- ctrl_msg = udev_ctrl_receive_msg(uctrl);
+ ctrl_conn = udev_ctrl_get_connection(uctrl);
+ if (ctrl_conn == NULL)
+ goto out;
+
+ ctrl_msg = udev_ctrl_receive_msg(ctrl_conn);
if (ctrl_msg == NULL)
- return;
+ goto out;
i = udev_ctrl_get_set_log_level(ctrl_msg);
if (i >= 0) {
@@ -652,13 +707,18 @@ static void handle_ctrl_msg(struct udev_ctrl *uctrl)
children_max = i;
}
- settle_pid = udev_ctrl_get_settle(ctrl_msg);
- if (settle_pid > 0) {
- info(udev, "udevd message (SETTLE) received\n");
- kill(settle_pid, SIGUSR1);
- settle_pid = 0;
+ if (udev_ctrl_get_ping(ctrl_msg) > 0)
+ info(udev, "udevd message (SYNC) received\n");
+
+ if (udev_ctrl_get_exit(ctrl_msg) > 0) {
+ info(udev, "udevd message (EXIT) received\n");
+ udev_exit = true;
+ /* keep reference to block the client until we exit */
+ udev_ctrl_connection_ref(ctrl_conn);
}
+out:
udev_ctrl_msg_unref(ctrl_msg);
+ return udev_ctrl_connection_unref(ctrl_conn);
}
/* read inotify messages */
@@ -668,7 +728,7 @@ static int handle_inotify(struct udev *udev)
char *buf;
struct inotify_event *ev;
- if ((ioctl(pfd[FD_INOTIFY].fd, FIONREAD, &nbytes) < 0) || (nbytes <= 0))
+ if ((ioctl(fd_inotify, FIONREAD, &nbytes) < 0) || (nbytes <= 0))
return 0;
buf = malloc(nbytes);
@@ -677,7 +737,7 @@ static int handle_inotify(struct udev *udev)
return -1;
}
- nbytes = read(pfd[FD_INOTIFY].fd, buf, nbytes);
+ nbytes = read(fd_inotify, buf, nbytes);
for (pos = 0; pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) {
struct udev_device *dev;
@@ -706,9 +766,11 @@ static int handle_inotify(struct udev *udev)
info(udev, "device %s closed, synthesising 'change'\n", udev_device_get_devnode(dev));
util_strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL);
fd = open(filename, O_WRONLY);
- if (fd < 0 || write(fd, "change", 6) < 0)
- info(udev, "error writing uevent: %m\n");
- close(fd);
+ if (fd >= 0) {
+ if (write(fd, "change", 6) < 0)
+ info(udev, "error writing uevent: %m\n");
+ close(fd);
+ }
}
if (ev->mask & IN_IGNORED)
udev_watch_end(udev, dev);
@@ -744,15 +806,29 @@ static void handle_signal(struct udev *udev, int signo)
if (worker->pid != pid)
continue;
-
info(udev, "worker [%u] exit\n", pid);
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0)
+ err(udev, "worker [%u] exit with return code %i\n", pid, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ err(udev, "worker [%u] terminated by signal %i (%s)\n",
+ pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
+ } else if (WIFSTOPPED(status)) {
+ err(udev, "worker [%u] stopped\n", pid);
+ } else if (WIFCONTINUED(status)) {
+ err(udev, "worker [%u] continued\n", pid);
+ } else {
+ err(udev, "worker [%u] exit with status 0x%04x\n", pid, status);
+ }
+
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- err(udev, "worker [%u] unexpectedly returned with status 0x%04x\n", pid, status);
if (worker->event != NULL) {
- err(udev, "worker [%u] failed while handling '%s'\n", pid, worker->event->devpath);
+ err(udev, "worker [%u] failed while handling '%s'\n",
+ pid, worker->event->devpath);
worker->event->exitcode = -32;
- event_queue_delete(worker->event);
- /* drop reference from running event */
+ event_queue_delete(worker->event, true);
+ /* drop reference taken for state 'running' */
worker_unref(worker);
}
}
@@ -994,7 +1070,7 @@ static int convert_db(struct udev *udev)
f = fopen("/dev/kmsg", "w");
if (f != NULL) {
- fprintf(f, "<30>udev[%u]: converting old udev database\n", getpid());
+ fprintf(f, "<30>udevd[%u]: converting old udev database\n", getpid());
fclose(f);
}
@@ -1063,10 +1139,45 @@ static int convert_db(struct udev *udev)
return 0;
}
+static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink)
+{
+ int ctrl = -1, netlink = -1;
+ int fd, n;
+
+ n = sd_listen_fds(true);
+ if (n <= 0)
+ return -1;
+
+ for (fd = SD_LISTEN_FDS_START; fd < n + SD_LISTEN_FDS_START; fd++) {
+ if (sd_is_socket(fd, AF_LOCAL, SOCK_SEQPACKET, -1)) {
+ if (ctrl >= 0)
+ return -1;
+ ctrl = fd;
+ continue;
+ }
+
+ if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1)) {
+ if (netlink >= 0)
+ return -1;
+ netlink = fd;
+ continue;
+ }
+
+ return -1;
+ }
+
+ if (ctrl < 0 || netlink < 0)
+ return -1;
+
+ info(udev, "ctrl=%i netlink=%i\n", ctrl, netlink);
+ *rctrl = ctrl;
+ *rnetlink = netlink;
+ return 0;
+}
+
int main(int argc, char *argv[])
{
struct udev *udev;
- int fd;
FILE *f;
sigset_t mask;
int daemonize = false;
@@ -1081,6 +1192,11 @@ int main(int argc, char *argv[])
{ "version", no_argument, NULL, 'V' },
{}
};
+ int fd_ctrl = -1;
+ int fd_netlink = -1;
+ int fd_worker = -1;
+ struct epoll_event ep_ctrl, ep_inotify, ep_signal, ep_netlink, ep_worker;
+ struct udev_ctrl_connection *ctrl_conn = NULL;
int rc = 1;
udev = udev_new();
@@ -1108,6 +1224,8 @@ int main(int argc, char *argv[])
if (udev_set_run_path(udev, filename) == NULL)
goto exit;
mkdir(udev_get_run_path(udev), 0755);
+ err(udev, "error: runtime directory '%s' not writable, for now falling back to '%s'",
+ udev_get_run_config_path(udev), udev_get_run_path(udev));
}
}
/* relabel runtime dir only if it resides below /dev */
@@ -1218,47 +1336,140 @@ int main(int argc, char *argv[])
static_dev_create_from_modules(udev);
/* before opening new files, make sure std{in,out,err} fds are in a sane state */
- fd = open("/dev/null", O_RDWR);
- if (fd < 0) {
- fprintf(stderr, "cannot open /dev/null\n");
- err(udev, "cannot open /dev/null\n");
+ if (daemonize) {
+ int fd;
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd >= 0) {
+ if (write(STDOUT_FILENO, 0, 0) < 0)
+ dup2(fd, STDOUT_FILENO);
+ if (write(STDERR_FILENO, 0, 0) < 0)
+ dup2(fd, STDERR_FILENO);
+ if (fd > STDERR_FILENO)
+ close(fd);
+ } else {
+ fprintf(stderr, "cannot open /dev/null\n");
+ err(udev, "cannot open /dev/null\n");
+ }
}
- if (write(STDOUT_FILENO, 0, 0) < 0)
- dup2(fd, STDOUT_FILENO);
- if (write(STDERR_FILENO, 0, 0) < 0)
- dup2(fd, STDERR_FILENO);
-
- /* udevadm control socket */
- if (sd_listen_fds(true) == 1 && sd_is_socket(SD_LISTEN_FDS_START, AF_LOCAL, SOCK_DGRAM, -1))
- udev_ctrl = udev_ctrl_new_from_fd(udev, SD_LISTEN_FDS_START);
- else
+
+ if (systemd_fds(udev, &fd_ctrl, &fd_netlink) >= 0) {
+ /* get control and netlink socket from from systemd */
+ udev_ctrl = udev_ctrl_new_from_socket_fd(udev, UDEV_CTRL_SOCK_PATH, fd_ctrl);
+ if (udev_ctrl == NULL) {
+ err(udev, "error taking over udev control socket");
+ rc = 1;
+ goto exit;
+ }
+
+ monitor = udev_monitor_new_from_netlink_fd(udev, "kernel", fd_netlink);
+ if (monitor == NULL) {
+ err(udev, "error taking over netlink socket\n");
+ rc = 3;
+ goto exit;
+ }
+ } else {
+ /* open control and netlink socket */
udev_ctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH);
- if (udev_ctrl == NULL) {
- fprintf(stderr, "error initializing udev control socket");
- err(udev, "error initializing udev control socket");
- rc = 1;
+ if (udev_ctrl == NULL) {
+ fprintf(stderr, "error initializing udev control socket");
+ err(udev, "error initializing udev control socket");
+ rc = 1;
+ goto exit;
+ }
+ fd_ctrl = udev_ctrl_get_fd(udev_ctrl);
+
+ monitor = udev_monitor_new_from_netlink(udev, "kernel");
+ if (monitor == NULL) {
+ fprintf(stderr, "error initializing netlink socket\n");
+ err(udev, "error initializing netlink socket\n");
+ rc = 3;
+ goto exit;
+ }
+ fd_netlink = udev_monitor_get_fd(monitor);
+ }
+
+ if (udev_monitor_enable_receiving(monitor) < 0) {
+ fprintf(stderr, "error binding netlink socket\n");
+ err(udev, "error binding netlink socket\n");
+ rc = 3;
goto exit;
}
+
if (udev_ctrl_enable_receiving(udev_ctrl) < 0) {
fprintf(stderr, "error binding udev control socket\n");
err(udev, "error binding udev control socket\n");
rc = 1;
goto exit;
}
- pfd[FD_CONTROL].fd = udev_ctrl_get_fd(udev_ctrl);
- monitor = udev_monitor_new_from_netlink(udev, "kernel");
- if (monitor == NULL || udev_monitor_enable_receiving(monitor) < 0) {
- fprintf(stderr, "error initializing netlink socket\n");
- err(udev, "error initializing netlink socket\n");
- rc = 3;
+ 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) {
+ err(udev, "error creating queue file\n");
goto exit;
}
- udev_monitor_set_receive_buffer_size(monitor, 128*1024*1024);
- pfd[FD_NETLINK].fd = udev_monitor_get_fd(monitor);
- pfd[FD_INOTIFY].fd = udev_watch_init(udev);
- if (pfd[FD_INOTIFY].fd < 0) {
+ if (daemonize) {
+ pid_t pid;
+ int fd;
+
+ pid = fork();
+ switch (pid) {
+ case 0:
+ break;
+ case -1:
+ err(udev, "fork of daemon failed: %m\n");
+ rc = 4;
+ goto exit;
+ default:
+ rc = EXIT_SUCCESS;
+ goto exit_keep_queue;
+ }
+
+ setsid();
+
+ fd = open("/proc/self/oom_score_adj", O_RDWR);
+ if (fd < 0) {
+ /* Fallback to old interface */
+ fd = open("/proc/self/oom_adj", O_RDWR);
+ if (fd < 0) {
+ err(udev, "error disabling OOM: %m\n");
+ } else {
+ /* OOM_DISABLE == -17 */
+ write(fd, "-17", 3);
+ close(fd);
+ }
+ } else {
+ write(fd, "-1000", 5);
+ close(fd);
+ }
+ } else {
+ sd_notify(1, "READY=1");
+ }
+
+ f = fopen("/dev/kmsg", "w");
+ if (f != NULL) {
+ fprintf(f, "<30>udevd[%u]: starting version " VERSION "\n", getpid());
+ fclose(f);
+ }
+
+ if (!debug) {
+ int fd;
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd >= 0) {
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+ }
+ }
+
+ fd_inotify = udev_watch_init(udev);
+ if (fd_inotify < 0) {
fprintf(stderr, "error initializing inotify\n");
err(udev, "error initializing inotify\n");
rc = 4;
@@ -1266,15 +1477,15 @@ int main(int argc, char *argv[])
}
if (udev_get_rules_path(udev) != NULL) {
- inotify_add_watch(pfd[FD_INOTIFY].fd, udev_get_rules_path(udev),
+ inotify_add_watch(fd_inotify, udev_get_rules_path(udev),
IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
} else {
char filename[UTIL_PATH_SIZE];
struct stat statbuf;
- inotify_add_watch(pfd[FD_INOTIFY].fd, LIBEXECDIR "/rules.d",
+ inotify_add_watch(fd_inotify, LIBEXECDIR "/rules.d",
IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
- inotify_add_watch(pfd[FD_INOTIFY].fd, SYSCONFDIR "/udev/rules.d",
+ inotify_add_watch(fd_inotify, SYSCONFDIR "/udev/rules.d",
IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
/* watch dynamic rules directory */
@@ -1283,30 +1494,30 @@ int main(int argc, char *argv[])
util_create_path(udev, filename);
mkdir(filename, 0755);
}
- inotify_add_watch(pfd[FD_INOTIFY].fd, filename,
+ inotify_add_watch(fd_inotify, filename,
IN_DELETE | IN_MOVE | IN_CLOSE_WRITE);
}
udev_watch_restore(udev);
/* block and listen to all signals on signalfd */
sigfillset(&mask);
- sigprocmask(SIG_SETMASK, &mask, &orig_sigmask);
- pfd[FD_SIGNAL].fd = signalfd(-1, &mask, 0);
- if (pfd[FD_SIGNAL].fd < 0) {
- fprintf(stderr, "error getting signalfd\n");
- err(udev, "error getting signalfd\n");
+ sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
+ fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
+ if (fd_signal < 0) {
+ fprintf(stderr, "error creating signalfd\n");
+ err(udev, "error creating signalfd\n");
rc = 5;
goto exit;
}
/* unnamed socket from workers to the main daemon */
if (socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, worker_watch) < 0) {
- fprintf(stderr, "error getting socketpair\n");
- err(udev, "error getting socketpair\n");
+ fprintf(stderr, "error creating socketpair\n");
+ err(udev, "error creating socketpair\n");
rc = 6;
goto exit;
}
- pfd[FD_WORKER].fd = worker_watch[READ_END];
+ fd_worker = worker_watch[READ_END];
rules = udev_rules_new(udev, resolve_names);
if (rules == NULL) {
@@ -1314,69 +1525,43 @@ int main(int argc, char *argv[])
goto exit;
}
- udev_queue_export = udev_queue_export_new(udev);
- if (udev_queue_export == NULL) {
- err(udev, "error creating queue file\n");
- goto exit;
- }
-
- /* if needed, convert old database from earlier udev version */
- convert_db(udev);
-
- if (!debug) {
- dup2(fd, STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- }
- if (fd > STDERR_FILENO)
- close(fd);
+ memset(&ep_ctrl, 0, sizeof(struct epoll_event));
+ ep_ctrl.events = EPOLLIN;
+ ep_ctrl.data.fd = fd_ctrl;
- if (daemonize) {
- pid_t pid;
+ memset(&ep_inotify, 0, sizeof(struct epoll_event));
+ ep_inotify.events = EPOLLIN;
+ ep_inotify.data.fd = fd_inotify;
- pid = fork();
- switch (pid) {
- case 0:
- break;
- case -1:
- err(udev, "fork of daemon failed: %m\n");
- rc = 4;
- goto exit;
- default:
- rc = 0;
- goto exit;
- }
- } else {
- sd_notify(1, "READY=1");
- }
+ memset(&ep_signal, 0, sizeof(struct epoll_event));
+ ep_signal.events = EPOLLIN;
+ ep_signal.data.fd = fd_signal;
- /* set scheduling priority for the main daemon process */
- setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY);
+ memset(&ep_netlink, 0, sizeof(struct epoll_event));
+ ep_netlink.events = EPOLLIN;
+ ep_netlink.data.fd = fd_netlink;
- setsid();
+ memset(&ep_worker, 0, sizeof(struct epoll_event));
+ ep_worker.events = EPOLLIN;
+ ep_worker.data.fd = fd_worker;
- f = fopen("/dev/kmsg", "w");
- if (f != NULL) {
- fprintf(f, "<30>udev[%u]: starting version " VERSION "\n", getpid());
- fclose(f);
+ fd_ep = epoll_create1(EPOLL_CLOEXEC);
+ if (fd_ep < 0) {
+ err(udev, "error creating epoll fd: %m\n");
+ goto exit;
}
-
- fd = open("/proc/self/oom_score_adj", O_RDWR);
- if (fd < 0) {
- /* Fallback to old interface */
- fd = open("/proc/self/oom_adj", O_RDWR);
- if (fd < 0) {
- err(udev, "error disabling OOM: %m\n");
- } else {
- /* OOM_DISABLE == -17 */
- write(fd, "-17", 3);
- close(fd);
- }
- } else {
- write(fd, "-1000", 5);
- close(fd);
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_ctrl, &ep_ctrl) < 0 ||
+ epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_inotify, &ep_inotify) < 0 ||
+ epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 ||
+ epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_netlink, &ep_netlink) < 0 ||
+ epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_worker, &ep_worker) < 0) {
+ err(udev, "fail to add fds to epoll: %m\n");
+ goto exit;
}
+ /* if needed, convert old database from earlier udev version */
+ convert_db(udev);
+
if (children_max <= 0) {
int memsize = mem_size_mb();
@@ -1393,30 +1578,79 @@ int main(int argc, char *argv[])
udev_list_init(&event_list);
udev_list_init(&worker_list);
- while (!udev_exit) {
+ for (;;) {
+ struct epoll_event ev[8];
int fdcount;
int timeout;
+ bool is_worker, is_signal, is_inotify, is_netlink, is_ctrl;
+ int i;
+
+ if (udev_exit) {
+ /* close sources of new events and discard buffered events */
+ if (fd_ctrl >= 0) {
+ epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_ctrl, NULL);
+ fd_ctrl = -1;
+ }
+ if (monitor != NULL) {
+ epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_netlink, NULL);
+ udev_monitor_unref(monitor);
+ monitor = NULL;
+ }
+ if (fd_inotify >= 0) {
+ epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_inotify, NULL);
+ close(fd_inotify);
+ fd_inotify = -1;
+ }
- /* set timeout to kill idle workers */
- if (udev_list_is_empty(&event_list) && children > 2)
+ /* discard queued events and kill workers */
+ event_queue_cleanup(udev, EVENT_QUEUED);
+ worker_kill(udev, 0);
+
+ /* exit after all has cleaned up */
+ if (udev_list_is_empty(&event_list) && udev_list_is_empty(&worker_list))
+ break;
+
+ /* timeout at exit for workers to finish */
+ timeout = 60 * 1000;
+ } else if (udev_list_is_empty(&event_list) && children > 2) {
+ /* set timeout to kill idle workers */
timeout = 3 * 1000;
- else
+ } else {
timeout = -1;
- /* wait for events */
- fdcount = poll(pfd, ARRAY_SIZE(pfd), timeout);
+ }
+ fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), timeout);
if (fdcount < 0)
continue;
- /* timeout - kill idle workers */
- if (fdcount == 0)
+ if (fdcount == 0) {
+ if (udev_exit) {
+ info(udev, "timeout, giving up waiting for workers to finish\n");
+ break;
+ }
+
+ /* timeout - kill idle workers */
worker_kill(udev, 2);
+ }
+
+ is_worker = is_signal = is_inotify = is_netlink = is_ctrl = false;
+ for (i = 0; i < fdcount; i++) {
+ if (ev[i].data.fd == fd_worker && ev[i].events & EPOLLIN)
+ is_worker = true;
+ else if (ev[i].data.fd == fd_netlink && ev[i].events & EPOLLIN)
+ is_netlink = true;
+ else if (ev[i].data.fd == fd_signal && ev[i].events & EPOLLIN)
+ is_signal = true;
+ else if (ev[i].data.fd == fd_inotify && ev[i].events & EPOLLIN)
+ is_inotify = true;
+ else if (ev[i].data.fd == fd_ctrl && ev[i].events & EPOLLIN)
+ is_ctrl = true;
+ }
/* event has finished */
- if (pfd[FD_WORKER].revents & POLLIN)
- worker_returned();
+ if (is_worker)
+ worker_returned(fd_worker);
- /* get kernel uevent */
- if (pfd[FD_NETLINK].revents & POLLIN) {
+ if (is_netlink) {
struct udev_device *dev;
dev = udev_monitor_receive_device(monitor);
@@ -1426,32 +1660,37 @@ int main(int argc, char *argv[])
}
/* start new events */
- if (!udev_list_is_empty(&event_list) && !stop_exec_queue)
- events_start(udev);
+ if (!udev_list_is_empty(&event_list) && !udev_exit && !stop_exec_queue)
+ event_queue_start(udev);
- /* get signal */
- if (pfd[FD_SIGNAL].revents & POLLIN) {
+ if (is_signal) {
struct signalfd_siginfo fdsi;
ssize_t size;
- size = read(pfd[FD_SIGNAL].fd, &fdsi, sizeof(struct signalfd_siginfo));
+ size = read(fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
if (size == sizeof(struct signalfd_siginfo))
handle_signal(udev, fdsi.ssi_signo);
}
+ /* we are shutting down, the events below are not handled anymore */
+ if (udev_exit)
+ continue;
+
/* device node and rules directory inotify watch */
- if (pfd[FD_INOTIFY].revents & POLLIN)
+ if (is_inotify)
handle_inotify(udev);
/*
- * get control message
- *
* This needs to be after the inotify handling, to make sure,
- * that the settle signal is send back after the possibly generated
+ * that the ping is send back after the possibly generated
* "change" events by the inotify device node watch.
+ *
+ * A single time we may receive a client connection which we need to
+ * keep open to block the client. It will be closed right before we
+ * exit.
*/
- if (pfd[FD_CONTROL].revents & POLLIN)
- handle_ctrl_msg(udev_ctrl);
+ if (is_ctrl)
+ ctrl_conn = handle_ctrl_msg(udev_ctrl);
/* rules changed, set by inotify or a HUP signal */
if (reload_config) {
@@ -1467,19 +1706,25 @@ int main(int argc, char *argv[])
}
}
- udev_queue_export_cleanup(udev_queue_export);
- rc = 0;
+ rc = EXIT_SUCCESS;
exit:
- udev_queue_export_unref(udev_queue_export);
+ udev_queue_export_cleanup(udev_queue_export);
+exit_keep_queue:
+ if (fd_ep >= 0)
+ close(fd_ep);
+ worker_list_cleanup(udev);
+ event_queue_cleanup(udev, EVENT_UNDEF);
udev_rules_unref(rules);
- udev_ctrl_unref(udev_ctrl);
- if (pfd[FD_SIGNAL].fd >= 0)
- close(pfd[FD_SIGNAL].fd);
+ if (fd_signal >= 0)
+ close(fd_signal);
if (worker_watch[READ_END] >= 0)
close(worker_watch[READ_END]);
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);
udev_selinux_exit(udev);
udev_unref(udev);
udev_log_close();