diff options
62 files changed, 2281 insertions, 1932 deletions
@@ -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 ============================================ @@ -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 @@ -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. @@ -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 @@ -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(); |