From 726687ad48bdececed1e7e44387c50e009e28208 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Wed, 30 Jul 2008 00:39:15 +0200 Subject: delete all Makefiles and move udev source to udev/ --- Makefile | 354 --------- NEWS | 861 +++++++++++++++++++++ RELEASE-NOTES | 861 --------------------- extras/ata_id/Makefile | 72 -- extras/cdrom_id/Makefile | 74 -- extras/collect/Makefile | 65 -- extras/edd_id/Makefile | 75 -- extras/firmware/Makefile | 59 -- extras/floppy/Makefile | 71 -- extras/fstab_import/.gitignore | 1 + extras/fstab_import/Makefile | 72 -- extras/path_id/Makefile | 60 -- extras/rule_generator/Makefile | 70 -- extras/scsi_id/Makefile | 91 --- extras/usb_id/Makefile | 70 -- extras/volume_id/Makefile | 84 --- extras/volume_id/lib/Makefile | 138 ---- extras/volume_id/vol_id.8 | 79 -- list.h | 289 ------- logging.h | 73 -- test-udev.c | 175 ----- udev.7 | 432 ----------- udev.h | 184 ----- udev.xml | 639 ---------------- udev/list.h | 289 +++++++ udev/logging.h | 73 ++ udev/test-udev.c | 175 +++++ udev/udev.h | 184 +++++ udev/udev.xml | 639 ++++++++++++++++ udev/udev_config.c | 200 +++++ udev/udev_db.c | 331 ++++++++ udev/udev_device.c | 305 ++++++++ udev/udev_node.c | 448 +++++++++++ udev/udev_rules.c | 1618 ++++++++++++++++++++++++++++++++++++++++ udev/udev_rules.h | 131 ++++ udev/udev_rules_parse.c | 804 ++++++++++++++++++++ udev/udev_selinux.c | 173 +++++ udev/udev_selinux.h | 38 + udev/udev_sysdeps.c | 73 ++ udev/udev_sysdeps.h | 172 +++++ udev/udev_sysfs.c | 509 +++++++++++++ udev/udev_utils.c | 222 ++++++ udev/udev_utils_file.c | 178 +++++ udev/udev_utils_string.c | 270 +++++++ udev/udevadm.c | 168 +++++ udev/udevadm.xml | 379 ++++++++++ udev/udevcontrol.c | 165 ++++ udev/udevd.c | 1304 ++++++++++++++++++++++++++++++++ udev/udevd.h | 74 ++ udev/udevd.xml | 108 +++ udev/udevinfo.c | 500 +++++++++++++ udev/udevmonitor.c | 289 +++++++ udev/udevsettle.c | 178 +++++ udev/udevtest.c | 207 +++++ udev/udevtrigger.c | 712 ++++++++++++++++++ udev_config.c | 200 ----- udev_db.c | 331 -------- udev_device.c | 305 -------- udev_node.c | 448 ----------- udev_rules.c | 1618 ---------------------------------------- udev_rules.h | 131 ---- udev_rules_parse.c | 804 -------------------- udev_selinux.c | 173 ----- udev_selinux.h | 38 - udev_sysdeps.c | 73 -- udev_sysdeps.h | 172 ----- udev_sysfs.c | 509 ------------- udev_utils.c | 222 ------ udev_utils_file.c | 178 ----- udev_utils_string.c | 270 ------- udevadm.8 | 272 ------- udevadm.c | 168 ----- udevadm.xml | 379 ---------- udevcontrol.c | 165 ---- udevd.8 | 61 -- udevd.c | 1304 -------------------------------- udevd.h | 74 -- udevd.xml | 108 --- udevinfo.c | 500 ------------- udevmonitor.c | 289 ------- udevsettle.c | 178 ----- udevtest.c | 207 ----- udevtrigger.c | 712 ------------------ 83 files changed, 11778 insertions(+), 13976 deletions(-) delete mode 100644 Makefile create mode 100644 NEWS delete mode 100644 RELEASE-NOTES delete mode 100644 extras/ata_id/Makefile delete mode 100644 extras/cdrom_id/Makefile delete mode 100644 extras/collect/Makefile delete mode 100644 extras/edd_id/Makefile delete mode 100644 extras/firmware/Makefile delete mode 100644 extras/floppy/Makefile create mode 100644 extras/fstab_import/.gitignore delete mode 100644 extras/fstab_import/Makefile delete mode 100644 extras/path_id/Makefile delete mode 100644 extras/rule_generator/Makefile delete mode 100644 extras/scsi_id/Makefile delete mode 100644 extras/usb_id/Makefile delete mode 100644 extras/volume_id/Makefile delete mode 100644 extras/volume_id/lib/Makefile delete mode 100644 extras/volume_id/vol_id.8 delete mode 100644 list.h delete mode 100644 logging.h delete mode 100644 test-udev.c delete mode 100644 udev.7 delete mode 100644 udev.h delete mode 100644 udev.xml create mode 100644 udev/list.h create mode 100644 udev/logging.h create mode 100644 udev/test-udev.c create mode 100644 udev/udev.h create mode 100644 udev/udev.xml create mode 100644 udev/udev_config.c create mode 100644 udev/udev_db.c create mode 100644 udev/udev_device.c create mode 100644 udev/udev_node.c create mode 100644 udev/udev_rules.c create mode 100644 udev/udev_rules.h create mode 100644 udev/udev_rules_parse.c create mode 100644 udev/udev_selinux.c create mode 100644 udev/udev_selinux.h create mode 100644 udev/udev_sysdeps.c create mode 100644 udev/udev_sysdeps.h create mode 100644 udev/udev_sysfs.c create mode 100644 udev/udev_utils.c create mode 100644 udev/udev_utils_file.c create mode 100644 udev/udev_utils_string.c create mode 100644 udev/udevadm.c create mode 100644 udev/udevadm.xml create mode 100644 udev/udevcontrol.c create mode 100644 udev/udevd.c create mode 100644 udev/udevd.h create mode 100644 udev/udevd.xml create mode 100644 udev/udevinfo.c create mode 100644 udev/udevmonitor.c create mode 100644 udev/udevsettle.c create mode 100644 udev/udevtest.c create mode 100644 udev/udevtrigger.c delete mode 100644 udev_config.c delete mode 100644 udev_db.c delete mode 100644 udev_device.c delete mode 100644 udev_node.c delete mode 100644 udev_rules.c delete mode 100644 udev_rules.h delete mode 100644 udev_rules_parse.c delete mode 100644 udev_selinux.c delete mode 100644 udev_selinux.h delete mode 100644 udev_sysdeps.c delete mode 100644 udev_sysdeps.h delete mode 100644 udev_sysfs.c delete mode 100644 udev_utils.c delete mode 100644 udev_utils_file.c delete mode 100644 udev_utils_string.c delete mode 100644 udevadm.8 delete mode 100644 udevadm.c delete mode 100644 udevadm.xml delete mode 100644 udevcontrol.c delete mode 100644 udevd.8 delete mode 100644 udevd.c delete mode 100644 udevd.h delete mode 100644 udevd.xml delete mode 100644 udevinfo.c delete mode 100644 udevmonitor.c delete mode 100644 udevsettle.c delete mode 100644 udevtest.c delete mode 100644 udevtrigger.c diff --git a/Makefile b/Makefile deleted file mode 100644 index 8b391a01fc..0000000000 --- a/Makefile +++ /dev/null @@ -1,354 +0,0 @@ -# -# Copyright (C) 2003-2004 Greg Kroah-Hartman -# Copyright (C) 2004-2006 Kay Sievers -# -# 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; version 2 of the License. -# -# 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 -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# - -VERSION = 125 - -# set this to make use of syslog -USE_LOG = true - -# compile-in development debug messages -# (export UDEV_LOG="debug" or set udev_log="debug" in udev.conf -# to print the debug messages to syslog) -DEBUG = false - -# compile with gcc's code coverage option -USE_GCOV = false - -# include Security-Enhanced Linux support -USE_SELINUX = false - -# set this to create statically linked binaries -USE_STATIC = false - -# to build any of the extras programs pass: -# make EXTRAS="extras/ extras/" -EXTRAS = - -# make the build silent -V = - -PROGRAMS = \ - udevd \ - udevadm \ - test-udev - -HEADERS = \ - udev.h \ - udevd.h \ - udev_rules.h \ - logging.h \ - udev_sysdeps.h \ - udev_selinux.h \ - list.h - -UDEV_OBJS = \ - udev_device.o \ - udev_config.o \ - udev_node.o \ - udev_db.o \ - udev_sysfs.o \ - udev_rules.o \ - udev_rules_parse.o \ - udev_utils.o \ - udev_utils_string.o \ - udev_utils_file.o \ - udevmonitor.o \ - udevinfo.o \ - udevcontrol.o \ - udevtrigger.o \ - udevsettle.o \ - udevtest.o \ - udev_sysdeps.o -LIBUDEV = libudev.a - -MAN_PAGES = \ - udev.7 \ - udevd.8 \ - udevadm.8 - -GEN_HEADERS = \ - udev_version.h - -prefix ?= -etcdir = ${prefix}/etc -sbindir = ${prefix}/sbin -usrbindir = ${prefix}/usr/bin -libudevdir = ${prefix}/lib/udev -mandir = ${prefix}/usr/share/man -configdir = ${etcdir}/udev -udevdir = /dev -DESTDIR = - -INSTALL = install -c -INSTALL_PROGRAM = ${INSTALL} -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_SCRIPT = ${INSTALL} -PWD = $(shell pwd) - -CROSS_COMPILE ?= -CC = $(CROSS_COMPILE)gcc -LD = $(CROSS_COMPILE)gcc -AR = $(CROSS_COMPILE)ar -RANLIB = $(CROSS_COMPILE)ranlib - -CFLAGS += -g -Wall -pipe -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -WARNINGS = -Wstrict-prototypes -Wsign-compare -Wshadow \ - -Wchar-subscripts -Wmissing-declarations -Wnested-externs \ - -Wpointer-arith -Wcast-align -Wsign-compare -Wmissing-prototypes -CFLAGS += $(WARNINGS) - -LDFLAGS += -Wl,-warn-common,--as-needed - -OPTFLAGS = -Os -CFLAGS += $(OPTFLAGS) - -ifeq ($(strip $(USE_LOG)),true) - CFLAGS += -DUSE_LOG -endif - -# if DEBUG is enabled, then we do not strip -ifeq ($(strip $(DEBUG)),true) - CFLAGS += -DDEBUG -endif - -ifeq ($(strip $(USE_GCOV)),true) - CFLAGS += -fprofile-arcs -ftest-coverage - LDFLAGS += -fprofile-arcs -endif - -ifeq ($(strip $(USE_SELINUX)),true) - UDEV_OBJS += udev_selinux.o - LIB_OBJS += -lselinux -lsepol - CFLAGS += -DUSE_SELINUX -endif - -ifeq ($(strip $(USE_STATIC)),true) - CFLAGS += -DUSE_STATIC - LDFLAGS += -static -endif - -ifeq ($(strip $(V)),) - E = @echo - Q = @ -else - E = @\# - Q = -endif -export E Q - -all: $(PROGRAMS) $(MAN_PAGES) - $(Q) extras="$(EXTRAS)"; for target in $$extras; do \ - $(MAKE) CC="$(CC)" \ - CFLAGS="$(CFLAGS)" \ - LD="$(LD)" \ - LDFLAGS="$(LDFLAGS)" \ - AR="$(AR)" \ - RANLIB="$(RANLIB)" \ - LIB_OBJS="$(LIB_OBJS)" \ - LIBUDEV="$(PWD)/$(LIBUDEV)" \ - -C $$target $@ || exit 1; \ - done; -.PHONY: all -.DEFAULT: all - -# clear implicit rules -.SUFFIXES: - -# build the objects -%.o: %.c $(HEADERS) $(GEN_HEADERS) - $(E) " CC " $@ - $(Q) $(CC) -c $(CFLAGS) $< -o $@ - -# "Static Pattern Rule" to build all programs -$(PROGRAMS): %: $(HEADERS) $(GEN_HEADERS) $(LIBUDEV) %.o - $(E) " LD " $@ - $(Q) $(LD) $(LDFLAGS) $@.o -o $@ $(LIBUDEV) $(LIB_OBJS) - -$(LIBUDEV): $(HEADERS) $(GEN_HEADERS) $(UDEV_OBJS) - $(Q) rm -f $@ - $(E) " AR " $@ - $(Q) $(AR) cq $@ $(UDEV_OBJS) - $(E) " RANLIB " $@ - $(Q) $(RANLIB) $@ - -udev_version.h: - $(E) " GENHDR " $@ - $(Q) echo "/* Generated by make. */" > $@ - $(Q) echo \#define UDEV_VERSION \"$(VERSION)\" >> $@ - $(Q) echo \#define UDEV_CONFIG_FILE \"$(configdir)/udev.conf\" >> $@ - -# man pages -%.8 %.7: %.xml - $(E) " XMLTO " $@ - $(Q) xmlto man $? -.PRECIOUS: %.8 - -clean: - $(E) " CLEAN " - $(Q) - find . -type f -name '*.orig' -print0 | xargs -0r rm -f - $(Q) - find . -type f -name '*.rej' -print0 | xargs -0r rm -f - $(Q) - find . -type f -name '*~' -print0 | xargs -0r rm -f - $(Q) - find . -type f -name '*.[oas]' -print0 | xargs -0r rm -f - $(Q) - find . -type f -name "*.gcno" -print0 | xargs -0r rm -f - $(Q) - find . -type f -name "*.gcda" -print0 | xargs -0r rm -f - $(Q) - find . -type f -name "*.gcov" -print0 | xargs -0r rm -f - $(Q) - rm -f udev_gcov.txt - $(Q) - rm -f core $(PROGRAMS) $(GEN_HEADERS) - $(Q) - rm -f udev-$(VERSION).tar.gz - $(Q) - rm -f udev-$(VERSION).tar.bz2 - $(Q) - rm -f udev-git-HEAD.patch - @ extras="$(EXTRAS)"; for target in $$extras; do \ - $(MAKE) -C $$target $@ || exit 1; \ - done; -.PHONY: clean - -install-config: - $(INSTALL) -d $(DESTDIR)$(libudevdir)/rules.d - $(INSTALL) -d $(DESTDIR)$(configdir)/rules.d - @ if [ ! -r $(DESTDIR)$(configdir)/udev.conf ]; then \ - $(INSTALL_DATA) udev.conf $(DESTDIR)$(configdir); \ - fi - cp rules/rules.d/* $(DESTDIR)$(libudevdir)/rules.d - @ extras="$(EXTRAS)"; for target in $$extras; do \ - $(MAKE) -C $$target $@ || exit 1; \ - done; -.PHONY: install-config - -install-man: - $(INSTALL) -d $(DESTDIR)$(mandir)/man7 - $(INSTALL_DATA) udev.7 $(DESTDIR)$(mandir)/man7/udev.7 - $(INSTALL) -d $(DESTDIR)$(mandir)/man8 - $(INSTALL_DATA) udevd.8 $(DESTDIR)$(mandir)/man8/udevd.8 - $(INSTALL_DATA) udevadm.8 $(DESTDIR)$(mandir)/man8/udevadm.8 - @extras="$(EXTRAS)"; for target in $$extras; do \ - $(MAKE) -C $$target $@ || exit 1; \ - done; -.PHONY: install-man - -uninstall-man: - - rm -f $(DESTDIR)$(mandir)/man7/udev.7 - - rm -f $(DESTDIR)$(mandir)/man8/udevadm.8 - - rm -f $(DESTDIR)$(mandir)/man8/udevd.8 - @ extras="$(EXTRAS)"; for target in $$extras; do \ - $(MAKE) -C $$target $@ || exit 1; \ - done; -.PHONY: uninstall-man - -install-bin: - $(INSTALL) -d $(DESTDIR)$(sbindir) - $(INSTALL_PROGRAM) udevd $(DESTDIR)$(sbindir)/udevd - $(INSTALL_PROGRAM) udevadm $(DESTDIR)$(sbindir)/udevadm - ln -f -s udevadm $(DESTDIR)$(sbindir)/udevsettle - $(INSTALL) -d $(DESTDIR)$(usrbindir) - ln -f -s $(sbindir)/udevadm $(DESTDIR)$(usrbindir)/udevinfo - @extras="$(EXTRAS)"; for target in $$extras; do \ - $(MAKE) -C $$target $@ || exit 1; \ - done; -ifndef DESTDIR - - killall udevd - - rm -rf $(udevdir)/.udev - - $(sbindir)/udevd --daemon -endif -.PHONY: install-bin - -uninstall-bin: - - rm -f $(DESTDIR)$(sbindir)/udevd - - rm -f $(DESTDIR)$(sbindir)/udevadm - - rm -f $(DESTDIR)$(sbindir)/udevsettle - - rm -f $(DESTDIR)$(usrbindir)/udevinfo -ifndef DESTDIR - - killall udevd - - rm -rf $(udevdir)/.udev -endif - @extras="$(EXTRAS)"; for target in $$extras; do \ - $(MAKE) -C $$target $@ || exit 1; \ - done; -.PHONY: uninstall-bin - -install: all install-bin install-config install-man -.PHONY: install - -uninstall: uninstall-bin uninstall-man -.PHONY: uninstall - -test tests: all - @ cd test && ./udev-test.pl -.PHONY: test tests - -buildtest: - test/simple-build-check.sh -.PHONY: buildtest - -ChangeLog: - head -1 $@ | grep -q "to v$(shell echo $$(($(VERSION) - 1)))" - @ mv $@ $@.tmp - @ echo "Summary of changes from v$(shell echo $$(($(VERSION) - 1))) to v$(VERSION)" >> $@ - @ echo "============================================" >> $@ - @ echo >> $@ - @ git log --pretty=short $(shell echo $$(($(VERSION) - 1)))..HEAD | git shortlog >> $@ - @ echo >> $@ - @ cat $@ - @ cat $@.tmp >> $@ - @ rm $@.tmp - head -1 $@ | grep -q "to v$(VERSION)" -.PHONY: ChangeLog -.PRECIOUS: ChangeLog - -release: - $(Q) - rm -f udev-$(VERSION).tar.gz - $(Q) - rm -f udev-$(VERSION).tar.bz2 - head -1 ChangeLog | grep -q "to v$(VERSION)" - head -1 RELEASE-NOTES | grep -q "udev $(VERSION)" - git commit -a -m "release $(VERSION)" - cat .git/refs/heads/master > .git/refs/tags/$(VERSION) - @ echo - git-archive --format=tar --prefix=udev-$(VERSION)/ HEAD | gzip -9v > udev-$(VERSION).tar.gz - git-archive --format=tar --prefix=udev-$(VERSION)/ HEAD | bzip2 -9v > udev-$(VERSION).tar.bz2 -.PHONY: release - -dist: - git-archive --format=tar --prefix=udev-$(VERSION)/ HEAD | gzip -9v > udev-$(VERSION).tar.gz - git-archive --format=tar --prefix=udev-$(VERSION)/ HEAD | bzip2 -9v > udev-$(VERSION).tar.bz2 -.PHONY: dist - -patch: - git diff $(shell echo $$(($(VERSION) - 1))) HEAD > udev-git-HEAD.patch -.PHONY: patch - -gcov-all: - $(MAKE) clean all USE_GCOV=true - @ echo - @ echo "binaries built with gcov support." - @ echo "run the tests and analyze with 'make udev_gcov.txt'" -.PHONY: gcov-all - -# see docs/README-gcov_for_udev -udev_gcov.txt: $(wildcard *.gcda) $(wildcard *.gcno) - for file in `find -maxdepth 1 -name "*.gcno"`; do \ - name=`basename $$file .gcno`; \ - echo "################" >> $@; \ - echo "$$name.c" >> $@; \ - echo "################" >> $@; \ - if [ -e "$$name.gcda" ]; then \ - gcov -l "$$name.c" >> $@ 2>&1; \ - else \ - echo "code for $$name.c was never executed" >> $@ 2>&1; \ - fi; \ - echo >> $@; \ - done; \ - echo "view $@ for the result" diff --git a/NEWS b/NEWS new file mode 100644 index 0000000000..fea7db0d2b --- /dev/null +++ b/NEWS @@ -0,0 +1,861 @@ +udev 125 +======== +Bugfixes. + +Default udev rules, which are not supposed to be edited by the user, should +be placed in /lib/udev/rules.d/ now, to make it clear that they are private to +the udev package and will be replaced with an update. Udev will pick up rule +files from: + /lib/udev/rules.d/ - default installed rules + /etc/udev/rules.d/ - user rules + on-the-fly generated rules + /dev/.udev/rules.d/ - temporary non-persistent rules created after bootup +It does not matter in which directory a rule file lives, all files are sorted +in lexical order. + +To help creating /dev/root, we have now: + $ udevadm info --export --export-prefix="ROOT_" --device-id-of-file=/ + ROOT_MAJOR=8 + ROOT_MINOR=5 +In case the current --device-id-of-file is already used, please switch to +the --export format version, it saves the output parsing and the old +format will be changed to use ':' as a separator, like the format in the +sysfs 'dev' file. + +udev 124 +======== +Fix cdrom_id to properly recognize blank media. + +udev 123 +======== +Bugfixes. + +Tape drive id-data is queried from /dev/bsg/* instead of the tape +nodes. This avoids rewinding tapes on open(). + +udev 122 +======== +Bugfixes. + +The symlinks udevcontrol and udevtrigger are no longer installed by +the Makefile. + +The scsi_id program does not depend on sysfs anymore. It can speak +SGv4 now, so /dev/bsg/* device nodes can be used, to query SCSI device +data, which should solve some old problems with tape devices, where +we better do not open all tape device nodes to identify the device. + +udev 121 +======== +Many bugfixes. + +The cdrom_id program is replaced by an advanced version, which can +detect most common device types, and also properties of the inserted +media. This is part of moving some basic functionality from HAL into +udev (and the kernel). + +udev 120 +======== +Bugfixes. + +The last WAIT_FOR_SYSFS rule is removed from the default rules. + +The symlinks to udevadm for the debugging tools: udevmonitor and +udevtest are no longer created. + +The symlinks to the udevadm man page for the old tool names are +no longer created. + +Abstract namespace sockets paths in RUN+="socket:@" rules, +should be prefixed with '@' to indicate that the path is not a +real file. + +udev 119 +======== +Bugfixes. + +udev 118 +======== +Bugfixes. + +Udevstart is removed from the tree, it did not get installed for +a long time now, and is long replaced by trigger and settle. + +udev 117 +======== +Bugfixes. + +All udev tools are merged into a single binary called udevadm. +The old names of the tools are built-in commands in udevadm now. +Symlinks to udevadm, with the names of the old tools, provide +the same functionality as the standalone tools. There is also +only a single udevadm.8 man page left for all tools. + +Tools like mkinitramfs should be checked, if they need to include +udevadm in the list of files. + +udev 116 +======== +Bugfixes. + +udev 115 +======== +Bugfixes. + +The etc/udev/rules.d/ directory now contains a default set of basic +udev rules. This initial version is the result of a rules file merge +of Fedora and openSUSE. For these both distros only a few specific +rules are left in their own file, named after the distro. Rules which +are optionally installed, because they are only valid for a specific +architecture, or rules for subsystems which are not always used are +in etc/udev/packages/. + +udev 114 +======== +Bugfixes. + +Dynamic rules can be created in /dev/.udev/rules.d/ to trigger +actions by dynamically created rules. + +SYMLINK=="" matches agains the entries in the list of +currently defined symlinks. The links are not created in the +filesystem at that point in time, but the values can be matched. + +RUN{ignore_error}+="" will ignore any exit code from the +program and not record as a failed event. + +udev 113 +======== +Bugfixes. + +Final merge of patches/features from the Ubuntu package. + +udev 112 +======== +Bugfixes. + +Control characters in filesystem label strings are no longer silenty +removed, but hex-encoded, to be able to uniquely identify the device +by its symlink in /dev/disk/by-label/. +If libvolume_id is used by mount(8), LABEL= will work as expected, +if slashes or other characters are used in the label string. + +To test the existence of a file, TEST=="" and TEST!="" +can be specified now. The TEST key accepts an optional mode mask +TEST{0100}=="". + +Scsi_id now supports a mode without expecting scsi-specific sysfs +entries to allow the extraction of cciss-device persistent properties. + +udev 111 +======== +Bugfixes. + +In the future, we may see uuid's which are just simple character +strings (see the DDF Raid Specification). For that reason vol_id now +exports ID_FS_UUID_SAFE, just like ID_FS_LABEL_SAFE. For things like +the creation of symlinks, the *_SAFE values ensure, that no control +or whitespace characters are used in the filename. + +Possible users of libvolume_id, please use the volume_id_get_* functions. +The public struct will go away in a future release of the library. + +udev 110 +======== +Bugfixes. + +Removal of useless extras/eventrecorder.sh. + +udev 109 +======== +Bugfixes. + +udev 108 +======== +Bugfixes. + +The directory multiplexer for dev.d/ and hotplug.d are finally removed +from the udev package. + +udev 107 +======== +Bugfixes. + +Symlinks can have priorities now, the priority is assigned to the device +and specified with OPTIONS="link_priority=100". Devices with higher +priorities overwrite the symlinks of devices with lower priorities. +If the device that currently owns the link, goes away, the symlink +will be removed, and recreated, pointing to the next device with the +highest actual priority. This should make /dev/disk/by-{label,uuid,id} +more reliable, if multiple devices contain the same metadata and overwrite +these symlinks. + +The dasd_id program is removed from the udev tree, and dasdinfo, with the +needed rules, are part of the s390-tools now. + +Please add KERNEL=="[0-9]*:[0-9]*" to the scsi wait-for-sysfs rule, +we may get the scsi sysfs mess fixed some day, and this will only catch +the devices we are looking for. + +USB serial numbers for storage devices have the target:lun now appended, +to make it possibble to distinguish broken multi-lun devices with all +the same SCSI identifiers. + +Note: The extra "run_directory" which searches and executes stuff in +/etc/hotplug.d/ and /etc/dev.d/ is long deprecated, and will be removed +with the next release. Make sure, that you don't use it anymore, or +provides your own implementation of that inefficient stuff. +We are tired of reports about a "slow udev", because these directories +contain stuff, that runs with _every_ event, instead of using rules, +that run programs only for the matching events. + +udev 106 +======== +Bugfixes. + +udev 105 +======== +Bugfixes. + +DRIVER== will match only for devices that actually have a real +driver. DRIVERS== must be used, if parent devices should be +included in the match. + +Libvolume_id's "linux_raid" detection needed another fix. + +udev 104 +======== +Bugfixes. + +udev 103 +======== +Add additional check to volume_id detection of via_raid, cause +some company decided to put a matching pattern all over the empty +storage area of their music players. + +udev 102 +======== +Fix path_id for SAS devices. + +udev 101 +======== +The udev daemon can be started with --debug-trace now, which will +execute all events serialized to get a chance to catch a possible +action that crashes the box. + +A warning is logged, if PHYSDEV* keys, the "device" link, or a parent +device attribute like $attr{../file} is used, only WAIT_FOR_SYSFS rules +are excluded from the warning. Referencing parent attributes directly +may break when something in the kernel driver model changes. Udev will +just find the attribute by walking up the parent chain. + +Udevtrigger now sorts the list of devices depending on the device +dependency, so a "usb" device is triggered after the parent "pci" +device. + +udev 100 +======== +Revert persistent-storage ata-serial '_' '-' replacement. + +udev 099 +======== +Bugfixes. + +Udevtrigger can now filter the list of devices to be triggered. Matches +for subsystems or sysfs attributes can be specified. + +The entries in /dev/.udev/queue and /dev/.udev/failed have changed to +zero-sized files to avoid pointing to /sys and confuse broken tools which +scan the /dev directory. To retry failed events, udevtrigger --retry-failed +should be used now. + +The rules and scripts to create udev rules for persistent network +devices and optical drives are in the extras/rules_generator directory +now. If you use something similar, please consider replacing your own +version with this, to share the support effort. The rule_generator +installs its own rules into /etc/udev/rules.d. + +The cdrom_id tool installs its own rule now in /etc/udev/rules.d, cause +the rule_generator depends on cdrom_id to be called in an earlier rule. + +udev 098 +======== +Bugfixes. + +Renaming of some key names (the old names still work): +BUS -> SUBSYSTEMS, ID -> KERNELS, SYSFS -> ATTRS, DRIVER -> DRIVERS. +(The behavior of the key DRIVER will change soon in one of the next +releases, to match only the event device, please switch to DRIVERS +instead. If DRIVER is used, it will behave like DRIVERS, but an error +is logged. +With the new key names, we have a more consistent and simpler scheme. +We can match the properties of the event device only, with: KERNEL, +SUBSYSTEM, ATTR, DRIVER. Or include all the parent devices in the match, +with: KERNELS, SUBSYSTEMS, ATTRS, DRIVERS. ID, BUS, SYSFS, DRIVER are no +longer mentioned in the man page and should be switched in the rule +files. + +ATTR{file}="value" can be used now, to write to a sysfs file of the +event device. Instead of: + ..., SYSFS{type}=="0|7|14", RUN+="/bin/sh -c 'echo 60 > /sys$$DEVPATH/timeout'" +we now can do: + ..., ATTR{type}=="0|7|14", ATTR{timeout}="60" + +All the PHYSDEV* keys are deprecated and will be removed from a +future kernel: + PHYDEVPATH - is the path of a parent device and should not be + needed at all. + PHYSDEVBUS - is just a SUBSYSTEM value of a parent, and can be + matched with SUBSYSTEMS== + PHYSDEVDRIVER - for bus devices it is available as ENV{DRIVER}. + Newer kernels will have DRIVER in the environment, + for older kernels udev puts in. Class device will + no longer carry this property of a parent and + DRIVERS== can be used to match such a parent value. +Note that ENV{DRIVER} is only available for a few bus devices, where +the driver is already bound at device event time. On coldplug, the +events for a lot devices are already bound to a driver, and they will have +that value set. But on hotplug, at the time the kernel creates the device, +it can't know what driver may claim the device after that, therefore +in most cases it will be empty. + +Failed events should now be re-triggered with: + udevtrigger --retry-failed. +Please switch to this command, so we keep the details of the /dev/.udev/failed/ +files private to the udev tools. We may need to switch the current symlink +target, cause some obviously broken tools try to scan all files in /dev +including /dev/.udev/, find the links to /sys and end up stat()'ing sysfs files +million times. This takes ages on slow boxes. + +The udevinfo attribute walk (-a) now works with giving a device node +name (-n) instead of a devpath (-p). The query now always works, also when +no database file was created by udev. + +The built-in /etc/passwd /etc/group parser is removed, we always depend on +getpwnam() and getgrnam() now. One of the next releases will depend on +fnmatch() and may use getopt_long(). + +udev 097 +======== +Bugfixes and small improvements. + +udev 096 +======== +Fix path_id for recent kernels. + +udev 095 +======== +%e is finally gone. + +Added support for swapping network interface names, by temporarily +renaming the device and wait for the target name to become free. + +udev 094 +======== +The built-in MODALIAS key and substitution is removed. + +udev 093 +======== +The binary firmware helper is replaced by the usual simple +shell script. Udevsend is removed from the tree. + +udev 092 +======== +Bugfix release. + +udev 091 +======== +Some more keys require the correct use of '==' and '=' depending +on the kind of operation beeing an assignment or a match. Rules +with invalid operations are skipped and logged to syslog. Please +test with udevtest if the parsing of your rules throws errors and +fix possibly broken rules. + +udev 090 +======== +Provide "udevsettle" to wait for all current udev events to finish. +It also watches the current kernel netlink queue by comparing the +even sequence number to make sure that there are no current pending +events that have not already arrived in the daemon. + +udev 089 +======== +Fix rule to skip persistent rules for removable IDE devices, which +also skipped optical IDE drives. + +All *_id program are installed in /lib/udev/ by default now. + +No binary is stripped anymore as this should be done in the +packaging process and not at build time. + +libvolume_id is provided as a shared library now and vol_id is +linked against it. Also one of the next HAL versions will require +this library, and the HAL build process will also require the +header file to be installed. The copy of the same code in HAL will +be removed to have only a single copy left on the system. + +udev 088 +======== +Add persistent links for SCSI tapes. The rules file is renamed +to 60-persistent-storage.rules. + +Create persistent path for usb devices. Can be used for all sorts +of devices that can't be distinguished by other properties like +multiple identical keyboards and mice connected to the same box. + +Provide "udevtrigger" program to request events on coldplug. The +shell script is much too slow with thousends of devices. + +udev 087 +======== +Fix persistent disk rules to exclude removable IDE drives. + +Warn if %e, $modalias or MODALIAS is used. + +udev 086 +======== +Fix queue export, which wasn't correct for subsequent add/remove +events for the same device. + +udev 085 +======== +Fix cramfs detection on big endian. + +Make WAIT_FOR_SYSFS usable in "normal" rules and silent if the whole +device goes away. + +udev 084 +======== +If BUS== and SYSFS{}== have been used in the same rule, the sysfs +attributes were only checked at the parent device that matched the +by BUS requested subsystem. Fix it to also look at the device we +received the event for. + +Build variable CROSS has changed to CROSS_COMPILE to match the kernel +build name. + +udev 083 +======== +Fix a bug where NAME="" would prevent RUN from beeing executed. + +RUN="/bin/program" does not longer automatically add the subsystem +as the first parameter. This is from the days of /sbin/hotplug +which is dead now and it's just confusing to need to add a space at +the end of the program name to prevent this. +If you use rules that need the subsystem as the first parameter, +like the old "udev_run_hotlugd" and "udev_run_devd", add the subsystem +to the key like RUN+="/bin/program $env{SUBSYSTEM}". + +udev 082 +======== +The udev man page has moved to udev(7) as it does not describe a command +anymore. The programs udev, udevstart and udevsend are no longer installed +by default and must be copied manually, if they should be installed or +included in a package. + +Fix a bug where "ignore_device" could run earlier collected RUN keys before +the ignore rule was applied. + +More preparation for future sysfs changes. usb_id and scsi_id no longer +depend on a magic order of devices in the /devices chain. Specific devices +should be requested by their subsytem. + +This will always find the scsi parent device without depending on a specific +path position: + dev = sysfs_device_get(devpath); + dev_usb = sysfs_device_get_parent_with_subsystem(dev, "scsi"); + +The "device" link in the current sysfs layout will be automatically +_resolved_ as a parent and in the new sysfs layout it will just _be_ the +parent in the devpath. If a device is requested by it's symlink, like all +class devices in the new sysfs layout will look like, it gets automatically +resolved and substituted with the real devpath and not the symlink path. + +Note: +A similar logic must be applied to _all_ sysfs users, including +scripts, that search along parent devices in sysfs. The explicit use of +the "device" link must be avoided. With the future sysfs layout all +DEVPATH's will start with /devices/ and have a "subsystem" symlink poiting +back to the "class" or the "bus". The layout of the parent devices in +/devices is not necessarily expected to be stable across kernel releases and +searching for parents by their subsystem should make sysfs users tolerant +for changed parent chains. + +udev 081 +======== +Prepare udev to work with the experimental kernel patch, that moves +/sys/class devices to /sys/devices and /sys/block to /sys/class/block. + +Clarify BUS, ID, $id usage and fix $id behavior. This prepares for +moving the class devices to /sys/devices. + +Thanks again to Marco for help finding a hopefully nice compromise +to make %b simpler and working again. + +udev 080 +======== +Complete removal of libsysfs, replaced by simple helper functions +which are much simpler and a bit faster. The udev daemon operatesentirely +on event parameters and does not use sysfs for simple rules anymore. +Please report any new bugs/problems, that may be caused by this big +change. They will be fixed immediately. + +The enumeration format character '%e' is deprecated and will be +removed sometimes from a future udev version. It never worked correctly +outside of udevstart, so we can't use it with the new parallel +coldplug. A simple enumeration is as useless as the devfs naming +scheme, just get rid of both if you still use it. + +MODALIAS and $modalias is not needed and will be removed from one of +the next udev versions, replace it in all rules with ENV{MODALIAS} or +the sysfs "modalias" value. + +Thanks a lot to Marco for all his help on finding and fixing bugs. + +udev 079 +======== +Let scsi_id request libata drive serial numbers from page 0x80. + +Renamed etc/udev/persistent.rules to persistent-disk.rules and +added /dev/disk/by-name/* for device mapper device names. + +Removed %e from the man page. It never worked reliably outside +of udevstart and udevstart is no longer recommended to use. + +udev 078 +======== +Symlinks are now exported to the event environment. Hopefully it's no +longer needed to run udevinfo from an event process, like it was +mentioned on the hotplug list: + UDEV [1134776873.702967] add@/block/sdb + ... + DEVNAME=/dev/sdb + DEVLINKS=/dev/disk/by-id/usb-IBM_Memory_Key_0218B301030027E8 /dev/disk/by-path/usb-0218B301030027E8:0:0:0 + +udev 077 +======== +Fix a problem if udevsend is used as the hotplug handler and tries to use +syslog, which causes a "vc" event loop. 2.6.15 will make udevsend obsolete +and this kind of problems will hopefully go away soon. + +udev 076 +======== +All built-in logic to work around bad sysfs timing is removed with this +version. The need to wait for sysfs files is almost fixed with a kernel +version that doesn't work with this udev version anyway. Until we fix +the timing of the "bus" link creation, the former integrated logic should +be emulated by a rule placed before all other rules: + ACTION=="add", DEVPATH=="/devices/*", ENV{PHYSDEVBUS}=="?*", WAIT_FOR_SYSFS="bus" + +The option "udev_db" does no longer exist. All udev state will be in +/$udev_root/.udev/ now, there is no longer an option to set this +to anything else. +If the init script or something else used this value, just depend on +this hardcoded path. But remember _all_content_ of this directory is +still private to udev and can change at any time. + +Default location for rule sripts and helper programs is now: /lib/udev/. +Everything that is not useful on the commandline should go into this +directory. Some of the helpers in the extras folder are installed there +now. The rules need to be changed, to find the helpers there. + +Also /lib/udev/devices is recommended as a directory where packages or +the user can place real device nodes, which get copied over to /dev at +every boot. This should replace the various solutions with custom config +files. + +Udevsend does no longer start the udev daemon. This must be done with +the init script that prepares /dev on tmpfs and creates the initial nodes, +before starting the daemon. + +udev 075 +======== +Silent a too verbose error logging for the old hotplug.d/ dev.d/ +emulation. + +The copy of klibc is removed. A systemwide installed version of klibc +should be used to build a klibc udev now. + +udev 074 +======== +NAME="" will not create any nodes, but execute RUN keys. To completely +ignore an event the OPTION "ignore_device" should be used. + +After removal of the reorder queue, events with a TIMEOUT can be executed +without any queuing now. + +udev 073 +======== +Fixed bug in udevd, if inotify is not available. We depend on netlink +uevents now, kernels without that event source will not work with that +version of udev anymore. + +udev 072 +======== +The rule parsing happens now in the daemon once at startup, all udev +event processes inherit the already parsed rules from the daemon. +It is shipped with SUSE10.0 and reduces heavily the system load at +startup. The option to save precompiled rules and let the udev process +pick the them up is removed, as it's no longer needed. + +Kernel 2.6.15 will have symlinks at /class/input pointing to the real +device. Libsysfs is changed to "translate" the requested link into the +real device path, as it would happen with the hotplug event. Otherwise +device removal and the udev database will not work. + +Using 'make STRIPCMD=' will leave the binaries unstripped for debugging +and packaging. + +A few improvements for vol_id, the filesytem probing code. + +udev 071 +======== +Fix a stupid typo in extras/run_directory for "make install". + +scsi_id creates the temporary devnode now in /dev for usage with a +non-writable /tmp directory. + +The uevent kernel socket buffer can carry app. 50.000 events now, +let's see who can break this again. :) + +The upcoming kernel will have a new input driver core integration. +Some class devices are now symlinks to the real device. libsysfs +needs a fix for this to work correctly. Udevstart of older udev +versions will _not_ create these devices! + +udev 070 +======== +Fix a 'install' target in the Makefile, that prevents EXTRAS from +beeing installed. + +udev 069 +======== +A bunch of mostly trivial bugfixes. From now on no node name or +symlink name can contain any character than plain whitelisted ascii +characters or validated utf8 byte-streams. This is needed for the +/dev/disk/by-label/* links, because we import untrusted data and +export it to the filesystem. + +udev 068 +======== +More bugfixes. If udevd was started from the kernel, we don't +have stdin/stdout/stderr, which broke the forked tools in some +situations. + +udev 067 +======== +Bugfix. udevstart event ordering was broken for a long time. +The new run_program() uncovered it, because /dev/null was not +available while we try to run external programs. +Now udevstart should create it before we run anything. + +udev 066 +======== +Minor bugfixes and some distro rules updates. If you don't have the +persistent disk rules in /dev/disk/by-*/* on your distro, just +grab it from here. :) + +udev 065 +======== +We can use socket communication now to pass events from udev to +other programs: + RUN+="socket:/org/freedesktop/hal/udev_event" +will pass the whole udev event to the HAL daemon without the need +for a forked helper. (See ChangeLog for udevmonitor, as an example) + +udev 064 +======== +Mostly bugfixes and see ChangeLog. + +The test for the existence of an environment value should be +switched from: + ENV{KEY}=="*" to ENV{KEY}=="?*" +because "*" will not fail anymore, if the key does not exist or +is empty. + +udev 063 +======== +Bugfixes and a few tweaks described in the ChangeLog. + +udev 062 +======== +Mostly a Bugfix release. + +Added WAIT_FOR_SYSFS="" to be able to fight against the sysfs +timing with custom rules. + +udev 061 +======== +We changed the internal rule storage format. Our large rule files took +2 MB of RAM, with the change we are down to 99kB. + +If the device-node has been created with default name and no symlink or +options are to remenber, it is not longer stored in the udevdb. HAL will +need to be updated to work correctly with that change. + +To overrride optimization flags, OPTFLAGS may be used now. + +udev 060 +======== +Bugfix release. + +udev 059 +======== +Major changes happened with this release. The goal is to take over the +complete kernel-event handling and provide a more efficient way to dispatch +kernel events. Replacing most of the current shell script logic and the +kernel forked helper with a netlink-daemon and a rule-based event handling. + +o udevd listens to netlink events now. The first valid netlink event + will make udevd ignore any message from udevsend that contains a + SEQNUM, to avoid duplicate events. The forked events can be disabled + with: + echo "" > /proc/sys/kernel/hotplug + For full support, the broken input-subsytem needs to be fixed, not to + bypass the driver core. + +o /etc/dev.d/ + /etc/hotplug.d/ directory multiplexing is completely + removed from udev itself and must be emulated by calling small + helper binaries provided in the extras folder: + make EXTRAS=extras/run_directory/ + will build udev_run_devd and udev_run_hotplugd, which can be called + from a rule if needed: + RUN+="/sbin/udev_run_hotplugd" + The recommended way to handle this is to convert all the calls from + the directories to explicit udev rules and get completely rid of the + multiplexing. (To catch a ttyUSB event, you now no longer need to + fork and exit 300 tty script instances you are not interested in, it + is just one rule that matches exactly the device.) + +o udev handles now _all_ events not just events for class and block + devices, this way it is possible to control the complete event + behavior with udev rules. Especially useful for rules like: + ACTION="add", DEVPATH="/devices/*", MODALIAS=="?*", RUN+="/sbin/modprobe $modalias" + +o As used in the modalias rule, udev supports now textual + substitution placeholder along with the usual format chars. This + needs to be documented, for now it's only visible in udev_rules_parse.c. + +o The rule keys support now more operations. This is documented in the + man page. It is possible to add values to list-keys like the SYMLINK + and RUN list with KEY+="value" and to clear the list by assigning KEY="". + Also "final"-assignments are supported by using KEY:="value", which will + prevent changing the key by any later rule. + +o kernel 2.6.12 has the "detached_state" attribute removed from + sysfs, which was used to recognize sysfs population. We switched that + to wait for the "bus" link, which is only available in kernels after 2.6.11. + Running this udev version on older kernels may cause a short delay for + some events. + +o To provide infrastructure for persistent device naming, the id programs: + scsi_id, vol_id (former udev_volume_id), and ata_id (new) are able now + to export the probed data in environment key format: + pim:~ # /sbin/ata_id --export /dev/hda + ID_MODEL=HTS726060M9AT00 + ID_SERIAL=MRH401M4G6UM9B + ID_REVISION=MH4OA6BA + + The following rules: + KERNEL="hd*[!0-9]", IMPORT="/sbin/ata_id --export $tempnode" + KERNEL="hd*[!0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_MODEL}_$env{ID_SERIAL}" + + Will create: + kay@pim:~> tree /dev/disk + /dev/disk + |-- by-id + | |-- HTS726060M9AT00_MRH401M4G6UM9B -> ../../hda + | `-- IBM-Memory_Key -> ../../sda + |-- by-label + | |-- swap -> ../../hda1 + | |-- date -> ../../sda1 + | `-- home -> ../../hda3 + `-- by-uuid + |-- 2E08712B0870F2E7 -> ../../hda3 + |-- 9352cfef-7687-47bc-a2a3-34cf136f72e1 -> ../../hda1 + |-- E845-7A89 -> ../../sda1 + `-- b2a61681-3812-4f13-a4ff-920d70604299 -> ../../hda2 + + The IMPORT= operation will import these keys in the environment and make + it available for later PROGRAM= and RUN= executed programs. The keys are + also stored in the udevdb and can be queried from there with one of the + next udev versions. + +o A few binaries are silently added to the repository, which can be used + to replay kernel events from initramfs instead of using coldplug. udevd + can be instructed now to queue-up events while the stored events from + initramfs are filled into the udevd-queue. This code is still under + development and there is no documentation now besides the code itself. + The additional binaries get compiled, but are not installed by default. + +o There is also a temporary fix for a performance problem where too many + events happen in parallel and every event needs to parse the rules. + udev can now read precompiled rules stored on disk. This is likely to be + replaced by a more elegant solution in a future udev version. + +udev 058 +======== +With kernel version 2.6.12, the sysfs file "detached_state" was removed. +Fix for libsysfs not to expect this file was added. + +udev 057 +======== +All rules are applied now, but only the first matching rule with a NAME-key +will be applied. All later rules with NAME-key are completely ignored. This +way system supplied symlinks or permissions gets applied to user-defined +naming rules. + +Note: +Please check your rules setup, if you may need to add OPTIONS="last_rule" +to some rules, to keep the old behavior. + +The rules are read on "remove"-events too. That makes is possible to match +with keys that are available on remove (KERNEL, SUBSYSTEM, ID, ENV, ...) to +instruct udev to ignore an event (OPTIONS="ignore_device"). +The new ACTION-key may be used to let a rule act only at a "remove"-event. + +The new RUN-key supports rule-based execution of programs after device-node +handling. This is meant as a general replacement for the dev.d/-directories +to give fine grained control over the execution of programs. + +The %s{}-sysfs format char replacement values are searched at any of the +devices in the device chain now, not only at the class-device. + +We support log priority levels now. The value udev_log in udev.conf is used +to determine what is printed to syslog. This makes it possible to +run a version with compiled-in debug messages in a production environment +which is sometimes needed to find a bug. +It is still possible to supress the inclusion of _any_ syslog usage with +USE_LOG=false to create the smallest possible binaries if needed. +The configured udev_log value can be overridden with the environment variable +UDEV_LOG. + +udev 056 +======== +Possible use of a system-wide klibc: + make USE_KLIBC=true KLCC=/usr/bin/klcc all +will link against an external klibc and our own version will be ignored. + +udev 055 +======== +We support an unlimited count of symlinks now. + +If USE_STATIC=true is passed to a glibc build, we link statically and use +a built-in userdb parser to resolve user and group names. + +The PLACE= key is gone. It can be replaced by an ID= for a long time, because +we walk up the chain of physical devices to find a match. + +The KEY="" format supports '=', '==', '!=,' , '+=' now. This makes it +easy to skip certain attribute matches without composing rules with weird +character class negations like: + KERNEL="[!s][!c][!d]*" +this can now be replaced with: + KERNEL!="scd*" +The current simple '=' is still supported, and should work as it does today, +but existing rules should be converted if possible, to be better readable. + +We have new ENV{}== key now, to match against a maximum of 5 environment +variables. + +udevstart is its own binary again, because we don't need co carry this araound +with every forked event. diff --git a/RELEASE-NOTES b/RELEASE-NOTES deleted file mode 100644 index fea7db0d2b..0000000000 --- a/RELEASE-NOTES +++ /dev/null @@ -1,861 +0,0 @@ -udev 125 -======== -Bugfixes. - -Default udev rules, which are not supposed to be edited by the user, should -be placed in /lib/udev/rules.d/ now, to make it clear that they are private to -the udev package and will be replaced with an update. Udev will pick up rule -files from: - /lib/udev/rules.d/ - default installed rules - /etc/udev/rules.d/ - user rules + on-the-fly generated rules - /dev/.udev/rules.d/ - temporary non-persistent rules created after bootup -It does not matter in which directory a rule file lives, all files are sorted -in lexical order. - -To help creating /dev/root, we have now: - $ udevadm info --export --export-prefix="ROOT_" --device-id-of-file=/ - ROOT_MAJOR=8 - ROOT_MINOR=5 -In case the current --device-id-of-file is already used, please switch to -the --export format version, it saves the output parsing and the old -format will be changed to use ':' as a separator, like the format in the -sysfs 'dev' file. - -udev 124 -======== -Fix cdrom_id to properly recognize blank media. - -udev 123 -======== -Bugfixes. - -Tape drive id-data is queried from /dev/bsg/* instead of the tape -nodes. This avoids rewinding tapes on open(). - -udev 122 -======== -Bugfixes. - -The symlinks udevcontrol and udevtrigger are no longer installed by -the Makefile. - -The scsi_id program does not depend on sysfs anymore. It can speak -SGv4 now, so /dev/bsg/* device nodes can be used, to query SCSI device -data, which should solve some old problems with tape devices, where -we better do not open all tape device nodes to identify the device. - -udev 121 -======== -Many bugfixes. - -The cdrom_id program is replaced by an advanced version, which can -detect most common device types, and also properties of the inserted -media. This is part of moving some basic functionality from HAL into -udev (and the kernel). - -udev 120 -======== -Bugfixes. - -The last WAIT_FOR_SYSFS rule is removed from the default rules. - -The symlinks to udevadm for the debugging tools: udevmonitor and -udevtest are no longer created. - -The symlinks to the udevadm man page for the old tool names are -no longer created. - -Abstract namespace sockets paths in RUN+="socket:@" rules, -should be prefixed with '@' to indicate that the path is not a -real file. - -udev 119 -======== -Bugfixes. - -udev 118 -======== -Bugfixes. - -Udevstart is removed from the tree, it did not get installed for -a long time now, and is long replaced by trigger and settle. - -udev 117 -======== -Bugfixes. - -All udev tools are merged into a single binary called udevadm. -The old names of the tools are built-in commands in udevadm now. -Symlinks to udevadm, with the names of the old tools, provide -the same functionality as the standalone tools. There is also -only a single udevadm.8 man page left for all tools. - -Tools like mkinitramfs should be checked, if they need to include -udevadm in the list of files. - -udev 116 -======== -Bugfixes. - -udev 115 -======== -Bugfixes. - -The etc/udev/rules.d/ directory now contains a default set of basic -udev rules. This initial version is the result of a rules file merge -of Fedora and openSUSE. For these both distros only a few specific -rules are left in their own file, named after the distro. Rules which -are optionally installed, because they are only valid for a specific -architecture, or rules for subsystems which are not always used are -in etc/udev/packages/. - -udev 114 -======== -Bugfixes. - -Dynamic rules can be created in /dev/.udev/rules.d/ to trigger -actions by dynamically created rules. - -SYMLINK=="" matches agains the entries in the list of -currently defined symlinks. The links are not created in the -filesystem at that point in time, but the values can be matched. - -RUN{ignore_error}+="" will ignore any exit code from the -program and not record as a failed event. - -udev 113 -======== -Bugfixes. - -Final merge of patches/features from the Ubuntu package. - -udev 112 -======== -Bugfixes. - -Control characters in filesystem label strings are no longer silenty -removed, but hex-encoded, to be able to uniquely identify the device -by its symlink in /dev/disk/by-label/. -If libvolume_id is used by mount(8), LABEL= will work as expected, -if slashes or other characters are used in the label string. - -To test the existence of a file, TEST=="" and TEST!="" -can be specified now. The TEST key accepts an optional mode mask -TEST{0100}=="". - -Scsi_id now supports a mode without expecting scsi-specific sysfs -entries to allow the extraction of cciss-device persistent properties. - -udev 111 -======== -Bugfixes. - -In the future, we may see uuid's which are just simple character -strings (see the DDF Raid Specification). For that reason vol_id now -exports ID_FS_UUID_SAFE, just like ID_FS_LABEL_SAFE. For things like -the creation of symlinks, the *_SAFE values ensure, that no control -or whitespace characters are used in the filename. - -Possible users of libvolume_id, please use the volume_id_get_* functions. -The public struct will go away in a future release of the library. - -udev 110 -======== -Bugfixes. - -Removal of useless extras/eventrecorder.sh. - -udev 109 -======== -Bugfixes. - -udev 108 -======== -Bugfixes. - -The directory multiplexer for dev.d/ and hotplug.d are finally removed -from the udev package. - -udev 107 -======== -Bugfixes. - -Symlinks can have priorities now, the priority is assigned to the device -and specified with OPTIONS="link_priority=100". Devices with higher -priorities overwrite the symlinks of devices with lower priorities. -If the device that currently owns the link, goes away, the symlink -will be removed, and recreated, pointing to the next device with the -highest actual priority. This should make /dev/disk/by-{label,uuid,id} -more reliable, if multiple devices contain the same metadata and overwrite -these symlinks. - -The dasd_id program is removed from the udev tree, and dasdinfo, with the -needed rules, are part of the s390-tools now. - -Please add KERNEL=="[0-9]*:[0-9]*" to the scsi wait-for-sysfs rule, -we may get the scsi sysfs mess fixed some day, and this will only catch -the devices we are looking for. - -USB serial numbers for storage devices have the target:lun now appended, -to make it possibble to distinguish broken multi-lun devices with all -the same SCSI identifiers. - -Note: The extra "run_directory" which searches and executes stuff in -/etc/hotplug.d/ and /etc/dev.d/ is long deprecated, and will be removed -with the next release. Make sure, that you don't use it anymore, or -provides your own implementation of that inefficient stuff. -We are tired of reports about a "slow udev", because these directories -contain stuff, that runs with _every_ event, instead of using rules, -that run programs only for the matching events. - -udev 106 -======== -Bugfixes. - -udev 105 -======== -Bugfixes. - -DRIVER== will match only for devices that actually have a real -driver. DRIVERS== must be used, if parent devices should be -included in the match. - -Libvolume_id's "linux_raid" detection needed another fix. - -udev 104 -======== -Bugfixes. - -udev 103 -======== -Add additional check to volume_id detection of via_raid, cause -some company decided to put a matching pattern all over the empty -storage area of their music players. - -udev 102 -======== -Fix path_id for SAS devices. - -udev 101 -======== -The udev daemon can be started with --debug-trace now, which will -execute all events serialized to get a chance to catch a possible -action that crashes the box. - -A warning is logged, if PHYSDEV* keys, the "device" link, or a parent -device attribute like $attr{../file} is used, only WAIT_FOR_SYSFS rules -are excluded from the warning. Referencing parent attributes directly -may break when something in the kernel driver model changes. Udev will -just find the attribute by walking up the parent chain. - -Udevtrigger now sorts the list of devices depending on the device -dependency, so a "usb" device is triggered after the parent "pci" -device. - -udev 100 -======== -Revert persistent-storage ata-serial '_' '-' replacement. - -udev 099 -======== -Bugfixes. - -Udevtrigger can now filter the list of devices to be triggered. Matches -for subsystems or sysfs attributes can be specified. - -The entries in /dev/.udev/queue and /dev/.udev/failed have changed to -zero-sized files to avoid pointing to /sys and confuse broken tools which -scan the /dev directory. To retry failed events, udevtrigger --retry-failed -should be used now. - -The rules and scripts to create udev rules for persistent network -devices and optical drives are in the extras/rules_generator directory -now. If you use something similar, please consider replacing your own -version with this, to share the support effort. The rule_generator -installs its own rules into /etc/udev/rules.d. - -The cdrom_id tool installs its own rule now in /etc/udev/rules.d, cause -the rule_generator depends on cdrom_id to be called in an earlier rule. - -udev 098 -======== -Bugfixes. - -Renaming of some key names (the old names still work): -BUS -> SUBSYSTEMS, ID -> KERNELS, SYSFS -> ATTRS, DRIVER -> DRIVERS. -(The behavior of the key DRIVER will change soon in one of the next -releases, to match only the event device, please switch to DRIVERS -instead. If DRIVER is used, it will behave like DRIVERS, but an error -is logged. -With the new key names, we have a more consistent and simpler scheme. -We can match the properties of the event device only, with: KERNEL, -SUBSYSTEM, ATTR, DRIVER. Or include all the parent devices in the match, -with: KERNELS, SUBSYSTEMS, ATTRS, DRIVERS. ID, BUS, SYSFS, DRIVER are no -longer mentioned in the man page and should be switched in the rule -files. - -ATTR{file}="value" can be used now, to write to a sysfs file of the -event device. Instead of: - ..., SYSFS{type}=="0|7|14", RUN+="/bin/sh -c 'echo 60 > /sys$$DEVPATH/timeout'" -we now can do: - ..., ATTR{type}=="0|7|14", ATTR{timeout}="60" - -All the PHYSDEV* keys are deprecated and will be removed from a -future kernel: - PHYDEVPATH - is the path of a parent device and should not be - needed at all. - PHYSDEVBUS - is just a SUBSYSTEM value of a parent, and can be - matched with SUBSYSTEMS== - PHYSDEVDRIVER - for bus devices it is available as ENV{DRIVER}. - Newer kernels will have DRIVER in the environment, - for older kernels udev puts in. Class device will - no longer carry this property of a parent and - DRIVERS== can be used to match such a parent value. -Note that ENV{DRIVER} is only available for a few bus devices, where -the driver is already bound at device event time. On coldplug, the -events for a lot devices are already bound to a driver, and they will have -that value set. But on hotplug, at the time the kernel creates the device, -it can't know what driver may claim the device after that, therefore -in most cases it will be empty. - -Failed events should now be re-triggered with: - udevtrigger --retry-failed. -Please switch to this command, so we keep the details of the /dev/.udev/failed/ -files private to the udev tools. We may need to switch the current symlink -target, cause some obviously broken tools try to scan all files in /dev -including /dev/.udev/, find the links to /sys and end up stat()'ing sysfs files -million times. This takes ages on slow boxes. - -The udevinfo attribute walk (-a) now works with giving a device node -name (-n) instead of a devpath (-p). The query now always works, also when -no database file was created by udev. - -The built-in /etc/passwd /etc/group parser is removed, we always depend on -getpwnam() and getgrnam() now. One of the next releases will depend on -fnmatch() and may use getopt_long(). - -udev 097 -======== -Bugfixes and small improvements. - -udev 096 -======== -Fix path_id for recent kernels. - -udev 095 -======== -%e is finally gone. - -Added support for swapping network interface names, by temporarily -renaming the device and wait for the target name to become free. - -udev 094 -======== -The built-in MODALIAS key and substitution is removed. - -udev 093 -======== -The binary firmware helper is replaced by the usual simple -shell script. Udevsend is removed from the tree. - -udev 092 -======== -Bugfix release. - -udev 091 -======== -Some more keys require the correct use of '==' and '=' depending -on the kind of operation beeing an assignment or a match. Rules -with invalid operations are skipped and logged to syslog. Please -test with udevtest if the parsing of your rules throws errors and -fix possibly broken rules. - -udev 090 -======== -Provide "udevsettle" to wait for all current udev events to finish. -It also watches the current kernel netlink queue by comparing the -even sequence number to make sure that there are no current pending -events that have not already arrived in the daemon. - -udev 089 -======== -Fix rule to skip persistent rules for removable IDE devices, which -also skipped optical IDE drives. - -All *_id program are installed in /lib/udev/ by default now. - -No binary is stripped anymore as this should be done in the -packaging process and not at build time. - -libvolume_id is provided as a shared library now and vol_id is -linked against it. Also one of the next HAL versions will require -this library, and the HAL build process will also require the -header file to be installed. The copy of the same code in HAL will -be removed to have only a single copy left on the system. - -udev 088 -======== -Add persistent links for SCSI tapes. The rules file is renamed -to 60-persistent-storage.rules. - -Create persistent path for usb devices. Can be used for all sorts -of devices that can't be distinguished by other properties like -multiple identical keyboards and mice connected to the same box. - -Provide "udevtrigger" program to request events on coldplug. The -shell script is much too slow with thousends of devices. - -udev 087 -======== -Fix persistent disk rules to exclude removable IDE drives. - -Warn if %e, $modalias or MODALIAS is used. - -udev 086 -======== -Fix queue export, which wasn't correct for subsequent add/remove -events for the same device. - -udev 085 -======== -Fix cramfs detection on big endian. - -Make WAIT_FOR_SYSFS usable in "normal" rules and silent if the whole -device goes away. - -udev 084 -======== -If BUS== and SYSFS{}== have been used in the same rule, the sysfs -attributes were only checked at the parent device that matched the -by BUS requested subsystem. Fix it to also look at the device we -received the event for. - -Build variable CROSS has changed to CROSS_COMPILE to match the kernel -build name. - -udev 083 -======== -Fix a bug where NAME="" would prevent RUN from beeing executed. - -RUN="/bin/program" does not longer automatically add the subsystem -as the first parameter. This is from the days of /sbin/hotplug -which is dead now and it's just confusing to need to add a space at -the end of the program name to prevent this. -If you use rules that need the subsystem as the first parameter, -like the old "udev_run_hotlugd" and "udev_run_devd", add the subsystem -to the key like RUN+="/bin/program $env{SUBSYSTEM}". - -udev 082 -======== -The udev man page has moved to udev(7) as it does not describe a command -anymore. The programs udev, udevstart and udevsend are no longer installed -by default and must be copied manually, if they should be installed or -included in a package. - -Fix a bug where "ignore_device" could run earlier collected RUN keys before -the ignore rule was applied. - -More preparation for future sysfs changes. usb_id and scsi_id no longer -depend on a magic order of devices in the /devices chain. Specific devices -should be requested by their subsytem. - -This will always find the scsi parent device without depending on a specific -path position: - dev = sysfs_device_get(devpath); - dev_usb = sysfs_device_get_parent_with_subsystem(dev, "scsi"); - -The "device" link in the current sysfs layout will be automatically -_resolved_ as a parent and in the new sysfs layout it will just _be_ the -parent in the devpath. If a device is requested by it's symlink, like all -class devices in the new sysfs layout will look like, it gets automatically -resolved and substituted with the real devpath and not the symlink path. - -Note: -A similar logic must be applied to _all_ sysfs users, including -scripts, that search along parent devices in sysfs. The explicit use of -the "device" link must be avoided. With the future sysfs layout all -DEVPATH's will start with /devices/ and have a "subsystem" symlink poiting -back to the "class" or the "bus". The layout of the parent devices in -/devices is not necessarily expected to be stable across kernel releases and -searching for parents by their subsystem should make sysfs users tolerant -for changed parent chains. - -udev 081 -======== -Prepare udev to work with the experimental kernel patch, that moves -/sys/class devices to /sys/devices and /sys/block to /sys/class/block. - -Clarify BUS, ID, $id usage and fix $id behavior. This prepares for -moving the class devices to /sys/devices. - -Thanks again to Marco for help finding a hopefully nice compromise -to make %b simpler and working again. - -udev 080 -======== -Complete removal of libsysfs, replaced by simple helper functions -which are much simpler and a bit faster. The udev daemon operatesentirely -on event parameters and does not use sysfs for simple rules anymore. -Please report any new bugs/problems, that may be caused by this big -change. They will be fixed immediately. - -The enumeration format character '%e' is deprecated and will be -removed sometimes from a future udev version. It never worked correctly -outside of udevstart, so we can't use it with the new parallel -coldplug. A simple enumeration is as useless as the devfs naming -scheme, just get rid of both if you still use it. - -MODALIAS and $modalias is not needed and will be removed from one of -the next udev versions, replace it in all rules with ENV{MODALIAS} or -the sysfs "modalias" value. - -Thanks a lot to Marco for all his help on finding and fixing bugs. - -udev 079 -======== -Let scsi_id request libata drive serial numbers from page 0x80. - -Renamed etc/udev/persistent.rules to persistent-disk.rules and -added /dev/disk/by-name/* for device mapper device names. - -Removed %e from the man page. It never worked reliably outside -of udevstart and udevstart is no longer recommended to use. - -udev 078 -======== -Symlinks are now exported to the event environment. Hopefully it's no -longer needed to run udevinfo from an event process, like it was -mentioned on the hotplug list: - UDEV [1134776873.702967] add@/block/sdb - ... - DEVNAME=/dev/sdb - DEVLINKS=/dev/disk/by-id/usb-IBM_Memory_Key_0218B301030027E8 /dev/disk/by-path/usb-0218B301030027E8:0:0:0 - -udev 077 -======== -Fix a problem if udevsend is used as the hotplug handler and tries to use -syslog, which causes a "vc" event loop. 2.6.15 will make udevsend obsolete -and this kind of problems will hopefully go away soon. - -udev 076 -======== -All built-in logic to work around bad sysfs timing is removed with this -version. The need to wait for sysfs files is almost fixed with a kernel -version that doesn't work with this udev version anyway. Until we fix -the timing of the "bus" link creation, the former integrated logic should -be emulated by a rule placed before all other rules: - ACTION=="add", DEVPATH=="/devices/*", ENV{PHYSDEVBUS}=="?*", WAIT_FOR_SYSFS="bus" - -The option "udev_db" does no longer exist. All udev state will be in -/$udev_root/.udev/ now, there is no longer an option to set this -to anything else. -If the init script or something else used this value, just depend on -this hardcoded path. But remember _all_content_ of this directory is -still private to udev and can change at any time. - -Default location for rule sripts and helper programs is now: /lib/udev/. -Everything that is not useful on the commandline should go into this -directory. Some of the helpers in the extras folder are installed there -now. The rules need to be changed, to find the helpers there. - -Also /lib/udev/devices is recommended as a directory where packages or -the user can place real device nodes, which get copied over to /dev at -every boot. This should replace the various solutions with custom config -files. - -Udevsend does no longer start the udev daemon. This must be done with -the init script that prepares /dev on tmpfs and creates the initial nodes, -before starting the daemon. - -udev 075 -======== -Silent a too verbose error logging for the old hotplug.d/ dev.d/ -emulation. - -The copy of klibc is removed. A systemwide installed version of klibc -should be used to build a klibc udev now. - -udev 074 -======== -NAME="" will not create any nodes, but execute RUN keys. To completely -ignore an event the OPTION "ignore_device" should be used. - -After removal of the reorder queue, events with a TIMEOUT can be executed -without any queuing now. - -udev 073 -======== -Fixed bug in udevd, if inotify is not available. We depend on netlink -uevents now, kernels without that event source will not work with that -version of udev anymore. - -udev 072 -======== -The rule parsing happens now in the daemon once at startup, all udev -event processes inherit the already parsed rules from the daemon. -It is shipped with SUSE10.0 and reduces heavily the system load at -startup. The option to save precompiled rules and let the udev process -pick the them up is removed, as it's no longer needed. - -Kernel 2.6.15 will have symlinks at /class/input pointing to the real -device. Libsysfs is changed to "translate" the requested link into the -real device path, as it would happen with the hotplug event. Otherwise -device removal and the udev database will not work. - -Using 'make STRIPCMD=' will leave the binaries unstripped for debugging -and packaging. - -A few improvements for vol_id, the filesytem probing code. - -udev 071 -======== -Fix a stupid typo in extras/run_directory for "make install". - -scsi_id creates the temporary devnode now in /dev for usage with a -non-writable /tmp directory. - -The uevent kernel socket buffer can carry app. 50.000 events now, -let's see who can break this again. :) - -The upcoming kernel will have a new input driver core integration. -Some class devices are now symlinks to the real device. libsysfs -needs a fix for this to work correctly. Udevstart of older udev -versions will _not_ create these devices! - -udev 070 -======== -Fix a 'install' target in the Makefile, that prevents EXTRAS from -beeing installed. - -udev 069 -======== -A bunch of mostly trivial bugfixes. From now on no node name or -symlink name can contain any character than plain whitelisted ascii -characters or validated utf8 byte-streams. This is needed for the -/dev/disk/by-label/* links, because we import untrusted data and -export it to the filesystem. - -udev 068 -======== -More bugfixes. If udevd was started from the kernel, we don't -have stdin/stdout/stderr, which broke the forked tools in some -situations. - -udev 067 -======== -Bugfix. udevstart event ordering was broken for a long time. -The new run_program() uncovered it, because /dev/null was not -available while we try to run external programs. -Now udevstart should create it before we run anything. - -udev 066 -======== -Minor bugfixes and some distro rules updates. If you don't have the -persistent disk rules in /dev/disk/by-*/* on your distro, just -grab it from here. :) - -udev 065 -======== -We can use socket communication now to pass events from udev to -other programs: - RUN+="socket:/org/freedesktop/hal/udev_event" -will pass the whole udev event to the HAL daemon without the need -for a forked helper. (See ChangeLog for udevmonitor, as an example) - -udev 064 -======== -Mostly bugfixes and see ChangeLog. - -The test for the existence of an environment value should be -switched from: - ENV{KEY}=="*" to ENV{KEY}=="?*" -because "*" will not fail anymore, if the key does not exist or -is empty. - -udev 063 -======== -Bugfixes and a few tweaks described in the ChangeLog. - -udev 062 -======== -Mostly a Bugfix release. - -Added WAIT_FOR_SYSFS="" to be able to fight against the sysfs -timing with custom rules. - -udev 061 -======== -We changed the internal rule storage format. Our large rule files took -2 MB of RAM, with the change we are down to 99kB. - -If the device-node has been created with default name and no symlink or -options are to remenber, it is not longer stored in the udevdb. HAL will -need to be updated to work correctly with that change. - -To overrride optimization flags, OPTFLAGS may be used now. - -udev 060 -======== -Bugfix release. - -udev 059 -======== -Major changes happened with this release. The goal is to take over the -complete kernel-event handling and provide a more efficient way to dispatch -kernel events. Replacing most of the current shell script logic and the -kernel forked helper with a netlink-daemon and a rule-based event handling. - -o udevd listens to netlink events now. The first valid netlink event - will make udevd ignore any message from udevsend that contains a - SEQNUM, to avoid duplicate events. The forked events can be disabled - with: - echo "" > /proc/sys/kernel/hotplug - For full support, the broken input-subsytem needs to be fixed, not to - bypass the driver core. - -o /etc/dev.d/ + /etc/hotplug.d/ directory multiplexing is completely - removed from udev itself and must be emulated by calling small - helper binaries provided in the extras folder: - make EXTRAS=extras/run_directory/ - will build udev_run_devd and udev_run_hotplugd, which can be called - from a rule if needed: - RUN+="/sbin/udev_run_hotplugd" - The recommended way to handle this is to convert all the calls from - the directories to explicit udev rules and get completely rid of the - multiplexing. (To catch a ttyUSB event, you now no longer need to - fork and exit 300 tty script instances you are not interested in, it - is just one rule that matches exactly the device.) - -o udev handles now _all_ events not just events for class and block - devices, this way it is possible to control the complete event - behavior with udev rules. Especially useful for rules like: - ACTION="add", DEVPATH="/devices/*", MODALIAS=="?*", RUN+="/sbin/modprobe $modalias" - -o As used in the modalias rule, udev supports now textual - substitution placeholder along with the usual format chars. This - needs to be documented, for now it's only visible in udev_rules_parse.c. - -o The rule keys support now more operations. This is documented in the - man page. It is possible to add values to list-keys like the SYMLINK - and RUN list with KEY+="value" and to clear the list by assigning KEY="". - Also "final"-assignments are supported by using KEY:="value", which will - prevent changing the key by any later rule. - -o kernel 2.6.12 has the "detached_state" attribute removed from - sysfs, which was used to recognize sysfs population. We switched that - to wait for the "bus" link, which is only available in kernels after 2.6.11. - Running this udev version on older kernels may cause a short delay for - some events. - -o To provide infrastructure for persistent device naming, the id programs: - scsi_id, vol_id (former udev_volume_id), and ata_id (new) are able now - to export the probed data in environment key format: - pim:~ # /sbin/ata_id --export /dev/hda - ID_MODEL=HTS726060M9AT00 - ID_SERIAL=MRH401M4G6UM9B - ID_REVISION=MH4OA6BA - - The following rules: - KERNEL="hd*[!0-9]", IMPORT="/sbin/ata_id --export $tempnode" - KERNEL="hd*[!0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_MODEL}_$env{ID_SERIAL}" - - Will create: - kay@pim:~> tree /dev/disk - /dev/disk - |-- by-id - | |-- HTS726060M9AT00_MRH401M4G6UM9B -> ../../hda - | `-- IBM-Memory_Key -> ../../sda - |-- by-label - | |-- swap -> ../../hda1 - | |-- date -> ../../sda1 - | `-- home -> ../../hda3 - `-- by-uuid - |-- 2E08712B0870F2E7 -> ../../hda3 - |-- 9352cfef-7687-47bc-a2a3-34cf136f72e1 -> ../../hda1 - |-- E845-7A89 -> ../../sda1 - `-- b2a61681-3812-4f13-a4ff-920d70604299 -> ../../hda2 - - The IMPORT= operation will import these keys in the environment and make - it available for later PROGRAM= and RUN= executed programs. The keys are - also stored in the udevdb and can be queried from there with one of the - next udev versions. - -o A few binaries are silently added to the repository, which can be used - to replay kernel events from initramfs instead of using coldplug. udevd - can be instructed now to queue-up events while the stored events from - initramfs are filled into the udevd-queue. This code is still under - development and there is no documentation now besides the code itself. - The additional binaries get compiled, but are not installed by default. - -o There is also a temporary fix for a performance problem where too many - events happen in parallel and every event needs to parse the rules. - udev can now read precompiled rules stored on disk. This is likely to be - replaced by a more elegant solution in a future udev version. - -udev 058 -======== -With kernel version 2.6.12, the sysfs file "detached_state" was removed. -Fix for libsysfs not to expect this file was added. - -udev 057 -======== -All rules are applied now, but only the first matching rule with a NAME-key -will be applied. All later rules with NAME-key are completely ignored. This -way system supplied symlinks or permissions gets applied to user-defined -naming rules. - -Note: -Please check your rules setup, if you may need to add OPTIONS="last_rule" -to some rules, to keep the old behavior. - -The rules are read on "remove"-events too. That makes is possible to match -with keys that are available on remove (KERNEL, SUBSYSTEM, ID, ENV, ...) to -instruct udev to ignore an event (OPTIONS="ignore_device"). -The new ACTION-key may be used to let a rule act only at a "remove"-event. - -The new RUN-key supports rule-based execution of programs after device-node -handling. This is meant as a general replacement for the dev.d/-directories -to give fine grained control over the execution of programs. - -The %s{}-sysfs format char replacement values are searched at any of the -devices in the device chain now, not only at the class-device. - -We support log priority levels now. The value udev_log in udev.conf is used -to determine what is printed to syslog. This makes it possible to -run a version with compiled-in debug messages in a production environment -which is sometimes needed to find a bug. -It is still possible to supress the inclusion of _any_ syslog usage with -USE_LOG=false to create the smallest possible binaries if needed. -The configured udev_log value can be overridden with the environment variable -UDEV_LOG. - -udev 056 -======== -Possible use of a system-wide klibc: - make USE_KLIBC=true KLCC=/usr/bin/klcc all -will link against an external klibc and our own version will be ignored. - -udev 055 -======== -We support an unlimited count of symlinks now. - -If USE_STATIC=true is passed to a glibc build, we link statically and use -a built-in userdb parser to resolve user and group names. - -The PLACE= key is gone. It can be replaced by an ID= for a long time, because -we walk up the chain of physical devices to find a match. - -The KEY="" format supports '=', '==', '!=,' , '+=' now. This makes it -easy to skip certain attribute matches without composing rules with weird -character class negations like: - KERNEL="[!s][!c][!d]*" -this can now be replaced with: - KERNEL!="scd*" -The current simple '=' is still supported, and should work as it does today, -but existing rules should be converted if possible, to be better readable. - -We have new ENV{}== key now, to match against a maximum of 5 environment -variables. - -udevstart is its own binary again, because we don't need co carry this araound -with every forked event. diff --git a/extras/ata_id/Makefile b/extras/ata_id/Makefile deleted file mode 100644 index bab49a250d..0000000000 --- a/extras/ata_id/Makefile +++ /dev/null @@ -1,72 +0,0 @@ -# Makefile for udev extra invoked from the udev main Makefile -# -# Copyright (C) 2004-2005 Kay Sievers -# -# Released under the GNU General Public License, version 2. -# - -PROG = ata_id -OBJ = -HEADERS = -GEN_HEADERS = -MAN_PAGES = - -prefix = -etcdir = ${prefix}/etc -sbindir = ${prefix}/sbin -usrbindir = ${prefix}/usr/bin -usrsbindir = ${prefix}/usr/sbin -libudevdir = ${prefix}/lib/udev -mandir = ${prefix}/usr/share/man -configdir = ${etcdir}/udev/ - -INSTALL = install -c -INSTALL_PROGRAM = ${INSTALL} -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_SCRIPT = ${INSTALL} - -all: $(PROG) $(MAN_PAGES) -.PHONY: all -.DEFAULT: all - -%.o: %.c $(GEN_HEADERS) - $(E) " CC " $@ - $(Q) $(CC) -c $(CFLAGS) $< -o $@ - -$(PROG): %: $(HEADERS) %.o $(OBJS) - $(E) " LD " $@ - $(Q) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIB_OBJS) - -# man pages -%.8: %.xml - $(E) " XMLTO " $@ - $(Q) xmlto man $? -.PRECIOUS: %.8 - -clean: - $(E) " CLEAN " - $(Q) rm -f $(PROG) $(OBJS) $(GEN_HEADERS) -.PHONY: clean - -install-bin: all - $(INSTALL) -d $(DESTDIR)$(libudevdir) - $(INSTALL_PROGRAM) $(PROG) $(DESTDIR)$(libudevdir)/$(PROG) -.PHONY: install-bin - -uninstall-bin: - - rm $(DESTDIR)$(libudevdir)/$(PROG) -.PHONY: uninstall-bin - -install-man: - $(INSTALL) -d $(DESTDIR)$(mandir)/man8 - $(INSTALL_DATA) -D $(PROG).8 $(DESTDIR)$(mandir)/man8/$(PROG).8 -.PHONY: install-man - -uninstall-man: - -rm -f $(DESTDIR)$(mandir)/man8/$(PROG).8 -.PHONY: uninstall-man - -install-config: - @echo "no config file to install" -.PHONY: install-config - diff --git a/extras/cdrom_id/Makefile b/extras/cdrom_id/Makefile deleted file mode 100644 index ec03d1fce2..0000000000 --- a/extras/cdrom_id/Makefile +++ /dev/null @@ -1,74 +0,0 @@ -# Makefile for udev extra invoked from the udev main Makefile -# -# Copyright (C) 2004-2005 Kay Sievers -# -# Released under the GNU General Public License, version 2. -# - -PROG = cdrom_id -OBJ = -HEADERS = -GEN_HEADERS = -MAN_PAGES = - -prefix = -etcdir = ${prefix}/etc -sbindir = ${prefix}/sbin -usrbindir = ${prefix}/usr/bin -usrsbindir = ${prefix}/usr/sbin -libudevdir = ${prefix}/lib/udev -mandir = ${prefix}/usr/share/man -configdir = ${etcdir}/udev - -INSTALL = install -c -INSTALL_PROGRAM = ${INSTALL} -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_SCRIPT = ${INSTALL} - -all: $(PROG) $(MAN_PAGES) -.PHONY: all -.DEFAULT: all - -%.o: %.c $(GEN_HEADERS) - $(E) " CC " $@ - $(Q) $(CC) -c $(CFLAGS) $< -o $@ - -$(PROG): %: $(HEADERS) %.o $(OBJS) - $(E) " LD " $@ - $(Q) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIB_OBJS) - -# man pages -%.8: %.xml - $(E) " XMLTO " $@ - $(Q) xmlto man $? -.PRECIOUS: %.8 - -clean: - $(E) " CLEAN " - $(Q) rm -f $(PROG) $(OBJS) $(GEN_HEADERS) -.PHONY: clean - -install-bin: all - $(INSTALL) -d $(DESTDIR)$(libudevdir) - $(INSTALL_PROGRAM) $(PROG) $(DESTDIR)$(libudevdir)/$(PROG) - $(INSTALL) -d $(DESTDIR)$(libudevdir)/rules.d - $(INSTALL_DATA) 60-cdrom_id.rules $(DESTDIR)$(libudevdir)/rules.d/60-cdrom_id.rules -.PHONY: install-bin - -uninstall-bin: - - rm $(DESTDIR)$(libudevdir)/$(PROG) - - rm $(DESTDIR)$(libudevdir)/rules.d/60-cdrom_id.rules -.PHONY: uninstall-bin - -install-man: - $(INSTALL) -d $(DESTDIR)$(mandir)/man8 - $(INSTALL_DATA) $(PROG).8 $(DESTDIR)$(mandir)/man8/$(PROG).8 -.PHONY: install-man - -uninstall-man: - -rm -f $(DESTDIR)$(mandir)/man8/$(PROG).8 -.PHONY: uninstall-man - -install-config: - @echo "no config file to install" -.PHONY: install-config diff --git a/extras/collect/Makefile b/extras/collect/Makefile deleted file mode 100644 index 76487017dd..0000000000 --- a/extras/collect/Makefile +++ /dev/null @@ -1,65 +0,0 @@ -# Makefile for udev extra invoked by the udev main Makefile - -PROG = collect -OBJ = -HEADERS = -GEN_HEADERS = -MAN_PAGES = - -prefix = -etcdir = ${prefix}/etc -sbindir = ${prefix}/sbin -usrbindir = ${prefix}/usr/bin -usrsbindir = ${prefix}/usr/sbin -libudevdir = ${prefix}/lib/udev -mandir = ${prefix}/usr/share/man -configdir = ${etcdir}/udev/ - -INSTALL = install -c -INSTALL_PROGRAM = ${INSTALL} -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_SCRIPT = ${INSTALL} - -all: $(PROG) $(MAN_PAGES) -.PHONY: all -.DEFAULT: all - -%.o: %.c $(GEN_HEADERS) - $(E) " CC " $@ - $(Q) $(CC) -c $(CFLAGS) $< -o $@ - -$(PROG): %: $(HEADERS) %.o $(OBJS) - $(E) " LD " $@ - $(Q) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIB_OBJS) - -# man pages -%.8: %.xml - $(E) " XMLTO " $@ - $(Q) xmlto man $? -.PRECIOUS: %.8 - -clean: - $(E) " CLEAN " - $(Q) rm -f $(PROG) $(OBJS) $(GEN_HEADERS) -.PHONY: clean - -install-bin: all - $(INSTALL) -d $(DESTDIR)$(libudevdir) - $(INSTALL_PROGRAM) $(PROG) $(DESTDIR)$(libudevdir)/$(PROG) -.PHONY: install-bin - -uninstall-bin: - - rm $(DESTDIR)$(libudevdir)/$(PROG) -.PHONY: uninstall-bin - -install-man: - @echo "Please create a man page for this tool." -.PHONY: install-man - -uninstall-man: - @echo "Please create a man page for this tool." -.PHONY: uninstall-man - -install-config: - @echo "no config file to install" -.PHONY: install-config diff --git a/extras/edd_id/Makefile b/extras/edd_id/Makefile deleted file mode 100644 index 0126b859ee..0000000000 --- a/extras/edd_id/Makefile +++ /dev/null @@ -1,75 +0,0 @@ -# Makefile for udev extra invoked from the udev main Makefile -# -# Copyright (C) 2004-2005 Kay Sievers -# -# Released under the GNU General Public License, version 2. -# - -PROG = edd_id -OBJ = -HEADERS = -GEN_HEADERS = -MAN_PAGES = - -prefix = -etcdir = ${prefix}/etc -sbindir = ${prefix}/sbin -usrbindir = ${prefix}/usr/bin -usrsbindir = ${prefix}/usr/sbin -libudevdir = ${prefix}/lib/udev -mandir = ${prefix}/usr/share/man -configdir = ${etcdir}/udev/ - -INSTALL = install -c -INSTALL_PROGRAM = ${INSTALL} -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_SCRIPT = ${INSTALL} - -all: $(PROG) $(MAN_PAGES) -.PHONY: all -.DEFAULT: all - -%.o: %.c $(GEN_HEADERS) - $(E) " CC " $@ - $(Q) $(CC) -c $(CFLAGS) $< -o $@ - -$(PROG): %: $(HEADERS) %.o $(OBJS) - $(E) " LD " $@ - $(Q) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIB_OBJS) - -# man pages -%.8: %.xml - $(E) " XMLTO " $@ - $(Q) xmlto man $? -.PRECIOUS: %.8 - -clean: - $(E) " CLEAN " - $(Q) rm -f $(PROG) $(OBJS) $(GEN_HEADERS) -.PHONY: clean - -install-bin: all - $(INSTALL) -d $(DESTDIR)$(libudevdir) - $(INSTALL_PROGRAM) $(PROG) $(DESTDIR)$(libudevdir)/$(PROG) - $(INSTALL) -d $(DESTDIR)$(libudevdir)/rules.d/ - $(INSTALL_DATA) 61-persistent-storage-edd.rules $(DESTDIR)$(libudevdir)/rules.d/61-persistent-storage-edd.rules -.PHONY: install-bin - -uninstall-bin: - - rm $(DESTDIR)$(libudevdir)/$(PROG) - - rm $(DESTDIR)$(libudevdir)/rules.d/61-persistent-storage-edd.rules -.PHONY: uninstall-bin - -install-man: - $(INSTALL) -d $(DESTDIR)$(mandir)/man8 - $(INSTALL_DATA) $(PROG).8 $(DESTDIR)$(mandir)/man8/$(PROG).8 -.PHONY: install-man - -uninstall-man: - -rm -f $(DESTDIR)$(mandir)/man8/$(PROG).8 -.PHONY: uninstall-man - -install-config: - @echo "no config file to install" -.PHONY: install-config - diff --git a/extras/firmware/Makefile b/extras/firmware/Makefile deleted file mode 100644 index 5e84acb156..0000000000 --- a/extras/firmware/Makefile +++ /dev/null @@ -1,59 +0,0 @@ -# Makefile for udev extra invoked from the udev main Makefile -# -# Copyright (C) 2004-2006 Kay Sievers -# -# Released under the GNU General Public License, version 2. -# - -PROG = firmware.sh -MAN_PAGES = - -prefix = -etcdir = ${prefix}/etc -sbindir = ${prefix}/sbin -usrbindir = ${prefix}/usr/bin -usrsbindir = ${prefix}/usr/sbin -libudevdir = ${prefix}/lib/udev -mandir = ${prefix}/usr/share/man -configdir = ${etcdir}/udev/ - -INSTALL = install -c -INSTALL_PROGRAM = ${INSTALL} -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_SCRIPT = ${INSTALL} - -all: $(PROG) $(MAN_PAGES) -.PHONY: all -.DEFAULT: all - -# man pages -%.8: %.xml - $(E) " XMLTO " $@ - $(Q) xmlto man $? -.PRECIOUS: %.8 - -clean: - $(E) " CLEAN " -.PHONY: clean - -install-bin: all - $(INSTALL) -d $(DESTDIR)$(libudevdir) - $(INSTALL_SCRIPT) $(PROG) $(DESTDIR)$(libudevdir)/$(PROG) -.PHONY: install-bin - -uninstall-bin: - - rm $(DESTDIR)$(libudevdir)/$(PROG) -.PHONY: uninstall-bin - -install-man: - @echo "Please create a man page for this tool." -.PHONY: install-man - -uninstall-man: - @echo "Please create a man page for this tool." -.PHONY: uninstall-man - -install-config: - @echo "no config file to install" -.PHONY: install-config - diff --git a/extras/floppy/Makefile b/extras/floppy/Makefile deleted file mode 100644 index 0adc373c94..0000000000 --- a/extras/floppy/Makefile +++ /dev/null @@ -1,71 +0,0 @@ -# Makefile for udev extra invoked from the udev main Makefile -# -# Copyright (C) 2004-2005 Kay Sievers -# -# Released under the GNU General Public License, version 2. -# - -PROG = create_floppy_devices -OBJ = -HEADERS = -GEN_HEADERS = -MAN_PAGES = - -prefix = -etcdir = ${prefix}/etc -sbindir = ${prefix}/sbin -usrbindir = ${prefix}/usr/bin -usrsbindir = ${prefix}/usr/sbin -libudevdir = ${prefix}/lib/udev -mandir = ${prefix}/usr/share/man -configdir = ${etcdir}/udev/ - -INSTALL = install -c -INSTALL_PROGRAM = ${INSTALL} -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_SCRIPT = ${INSTALL} - -all: $(PROG) $(MAN_PAGES) -.PHONY: all -.DEFAULT: all - -%.o: %.c $(GEN_HEADERS) - $(E) " CC " $@ - $(Q) $(CC) -c $(CFLAGS) $< -o $@ - -$(PROG): %: $(HEADERS) %.o $(OBJS) - $(E) " LD " $@ - $(Q) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIB_OBJS) - -# man pages -%.8: %.xml - $(E) " XMLTO " $@ - $(Q) xmlto man $? -.PRECIOUS: %.8 - -clean: - $(E) " CLEAN " - $(Q) rm -f $(PROG) $(OBJS) $(GEN_HEADERS) -.PHONY: clean - -install-bin: all - $(INSTALL) -d $(DESTDIR)$(libudevdir) - $(INSTALL_PROGRAM) $(PROG) $(DESTDIR)$(libudevdir)/$(PROG) -.PHONY: install-bin - -uninstall-bin: - - rm $(DESTDIR)$(libudevdir)/$(PROG) -.PHONY: uninstall-bin - -install-man: - $(INSTALL) -d $(DESTDIR)$(mandir)/man8/ - $(INSTALL_DATA) $(PROG).8 $(DESTDIR)$(mandir)/man8/$(PROG).8 -.PHONY: install-man - -uninstall-man: - -rm -f $(DESTDIR)$(mandir)/man8/$(PROG).8 -.PHONY: uninstall-man - -install-config: - @echo "no config file to install" -.PHONY: install-config diff --git a/extras/fstab_import/.gitignore b/extras/fstab_import/.gitignore new file mode 100644 index 0000000000..c9ec5b801c --- /dev/null +++ b/extras/fstab_import/.gitignore @@ -0,0 +1 @@ +fstab_import diff --git a/extras/fstab_import/Makefile b/extras/fstab_import/Makefile deleted file mode 100644 index 3f65aa3f64..0000000000 --- a/extras/fstab_import/Makefile +++ /dev/null @@ -1,72 +0,0 @@ -# Makefile for udev extra invoked from the udev main Makefile -# -# Copyright (C) 2008 Kay Sievers -# -# Released under the GNU General Public License, version 2. -# - -PROG = fstab_import -OBJ = -HEADERS = -GEN_HEADERS = -MAN_PAGES = - -prefix = -etcdir = ${prefix}/etc -sbindir = ${prefix}/sbin -usrbindir = ${prefix}/usr/bin -usrsbindir = ${prefix}/usr/sbin -libudevdir = ${prefix}/lib/udev -mandir = ${prefix}/usr/share/man -configdir = ${etcdir}/udev/ - -INSTALL = install -c -INSTALL_PROGRAM = ${INSTALL} -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_SCRIPT = ${INSTALL} - -all: $(PROG) $(MAN_PAGES) -.PHONY: all -.DEFAULT: all - -%.o: %.c $(GEN_HEADERS) - $(E) " CC " $@ - $(Q) $(CC) -c $(CFLAGS) $< -o $@ - -$(PROG): %: $(HEADERS) %.o $(OBJS) - $(E) " LD " $@ - $(Q) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIB_OBJS) - -# man pages -%.8: %.xml - $(E) " XMLTO " $@ - $(Q) xmlto man $? -.PRECIOUS: %.8 - -clean: - $(E) " CLEAN " - $(Q) rm -f $(PROG) $(OBJS) $(GEN_HEADERS) -.PHONY: clean - -install-bin: all - $(INSTALL_PROGRAM) -D $(PROG) $(DESTDIR)$(libudevdir)/$(PROG) - $(INSTALL_DATA) 79-fstab_import.rules $(DESTDIR)$(libudevdir)/rules.d/79-fstab_import.rules -.PHONY: install-bin - -uninstall-bin: - - rm $(DESTDIR)$(libudevdir)/$(PROG) - - rm $(DESTDIR)$(libudevdir)/rules.d/79-fstab_import.rules -.PHONY: uninstall-bin - -install-man: - @echo "Please create a man page for this tool." -.PHONY: install-man - -uninstall-man: - @echo "Please create a man page for this tool." -.PHONY: uninstall-man - -install-config: - @echo "no config file to install" -.PHONY: install-config - diff --git a/extras/path_id/Makefile b/extras/path_id/Makefile deleted file mode 100644 index 2f0b882e65..0000000000 --- a/extras/path_id/Makefile +++ /dev/null @@ -1,60 +0,0 @@ -# Makefile for udev extra invoked from the udev main Makefile -# -# Copyright (C) 2004-2006 Kay Sievers -# -# Released under the GNU General Public License, version 2. -# - -PROG = path_id -MAN_PAGES = - -prefix = -etcdir = ${prefix}/etc -sbindir = ${prefix}/sbin -usrbindir = ${prefix}/usr/bin -usrsbindir = ${prefix}/usr/sbin -libudevdir = ${prefix}/lib/udev -mandir = ${prefix}/usr/share/man -configdir = ${etcdir}/udev/ - -INSTALL = install -c -INSTALL_PROGRAM = ${INSTALL} -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_SCRIPT = ${INSTALL} - -all: $(PROG) $(MAN_PAGES) -.PHONY: all -.DEFAULT: all - -# man pages -%.8: %.xml - $(E) " XMLTO " $@ - $(Q) xmlto man $? -.PRECIOUS: %.8 - -clean: - $(E) " CLEAN " -.PHONY: clean - -install-bin: all - $(INSTALL) -d $(DESTDIR)$(libudevdir) - $(INSTALL_SCRIPT) $(PROG) $(DESTDIR)$(libudevdir)/$(PROG) -.PHONY: install-bin - -uninstall-bin: - - rm $(DESTDIR)$(libudevdir)/$(PROG) -.PHONY: uninstall-bin - -install-man: - $(INSTALL) -d $(DESTDIR)$(mandir)/man8/ - $(INSTALL_DATA) $(PROG).8 $(DESTDIR)$(mandir)/man8/$(PROG).8 -.PHONY: install-man - -uninstall-man: - -rm -f $(DESTDIR)$(mandir)/man8/$(PROG).8 -.PHONY: uninstall-man - -install-config: - @echo "no config file to install" -.PHONY: install-config - diff --git a/extras/rule_generator/Makefile b/extras/rule_generator/Makefile deleted file mode 100644 index e236ca77f0..0000000000 --- a/extras/rule_generator/Makefile +++ /dev/null @@ -1,70 +0,0 @@ -# Makefile for udev extra invoked from the udev main Makefile -# -# Copyright (C) 2004-2006 Kay Sievers -# -# Released under the GNU General Public License, version 2. -# - -PROG = -MAN_PAGES = - -prefix = -etcdir = ${prefix}/etc -sbindir = ${prefix}/sbin -usrbindir = ${prefix}/usr/bin -usrsbindir = ${prefix}/usr/sbin -libudevdir = ${prefix}/lib/udev -mandir = ${prefix}/usr/share/man -configdir = ${etcdir}/udev - -INSTALL = install -c -INSTALL_PROGRAM = ${INSTALL} -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_SCRIPT = ${INSTALL} - -all: $(PROG) $(MAN_PAGES) -.PHONY: all -.DEFAULT: all - -# man pages -%.8: %.xml - $(E) " XMLTO " $@ - $(Q) xmlto man $? -.PRECIOUS: %.8 - -clean: - $(E) " CLEAN " -.PHONY: clean - -install-bin: all - $(INSTALL) -d $(DESTDIR)$(libudevdir) - $(INSTALL_DATA) rule_generator.functions $(DESTDIR)$(libudevdir)/rule_generator.functions - $(INSTALL_SCRIPT) write_cd_rules $(DESTDIR)$(libudevdir)/write_cd_rules - $(INSTALL_SCRIPT) write_net_rules $(DESTDIR)$(libudevdir)/write_net_rules - $(INSTALL) -d $(DESTDIR)$(libudevdir)/rules.d - $(INSTALL_DATA) 75-cd-aliases-generator.rules \ - $(DESTDIR)$(libudevdir)/rules.d/75-cd-aliases-generator.rules - $(INSTALL_DATA) 75-persistent-net-generator.rules \ - $(DESTDIR)$(libudevdir)/rules.d/75-persistent-net-generator.rules -.PHONY: install-bin - -uninstall-bin: - - rm $(DESTDIR)$(libudevdir)/rule_generator.functions - - rm $(DESTDIR)$(libudevdir)/write_cd_rules - - rm $(DESTDIR)$(libudevdir)/write_net_rules - - rm $(DESTDIR)$(configdir)/rules.d/75-cd-aliases-generator.rules - - rm $(DESTDIR)$(configdir)/rules.d/75-persistent-net-generator.rules -.PHONY: uninstall-bin - -install-man: - @echo "Please create a man page for this tool." -.PHONY: install-man - -uninstall-man: - @echo "Please create a man page for this tool." -.PHONY: uninstall-man - -install-config: - @echo "no config file to install" -.PHONY: install-config - diff --git a/extras/scsi_id/Makefile b/extras/scsi_id/Makefile deleted file mode 100644 index ca067d0cb9..0000000000 --- a/extras/scsi_id/Makefile +++ /dev/null @@ -1,91 +0,0 @@ -# Makefile for udev extra invoked from the udev main Makefile -# -# Copyright (C) 2003 IBM -# Copyright (C) 2004-2005 Kay Sievers -# -# Released under the GNU General Public License, version 2. -# - -SCSI_ID_VERSION = 2.0 - -PROG = scsi_id -OBJS= scsi_serial.o -HEADERS = scsi_id.h scsi.h scsi_id_version.h bsg.h -GEN_HEADERS = scsi_id_version.h -MAN_PAGES = scsi_id.8 - -prefix = -etcdir = ${prefix}/etc -sbindir = ${prefix}/sbin -usrbindir = ${prefix}/usr/bin -usrsbindir = ${prefix}/usr/sbin -libudevdir = ${prefix}/lib/udev -mandir = ${prefix}/usr/share/man -configdir = ${etcdir}/udev/ - -INSTALL = install -c -INSTALL_PROGRAM = ${INSTALL} -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_SCRIPT = ${INSTALL} - -all: $(PROG) $(MAN_PAGES) -.PHONY: all -.DEFAULT: all - -%.o: %.c $(GEN_HEADERS) - $(E) " CC " $@ - $(Q) $(CC) -c $(CFLAGS) $< -o $@ - -$(PROG): %: $(HEADERS) %.o $(OBJS) - $(E) " LD " $@ - $(Q) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIB_OBJS) - -scsi_id_version.h: - $(E) " GENHDR " $@ - $(Q) echo "/* Generated by make. */" > $@ - $(Q) echo \#define SCSI_ID_VERSION \"$(SCSI_ID_VERSION)\" >> $@ - $(Q) echo \#define SCSI_ID_CONFIG_FILE \"$(etcdir)/scsi_id.config\" >> $@ - -# man pages -%.8: %.xml - $(E) " XMLTO " $@ - $(Q) xmlto man $? -.PRECIOUS: %.8 - -clean: - $(E) " CLEAN " - $(Q) rm -f $(PROG) $(OBJS) $(GEN_HEADERS) -.PHONY: clean - -install-bin: all - $(INSTALL) -d $(DESTDIR)$(libudevdir) - $(INSTALL_PROGRAM) $(PROG) $(DESTDIR)$(libudevdir)/$(PROG) -.PHONY: install-bin - -uninstall-bin: - - rm $(DESTDIR)$(libudevdir)/$(PROG) -.PHONY: uninstall-bin - -install-man: - $(INSTALL) -d $(DESTDIR)$(mandir)/man8 - $(INSTALL_DATA) scsi_id.8 $(DESTDIR)$(mandir)/man8/scsi_id.8 -.PHONY: install-man - -uninstall-man: - -rm -f $(DESTDIR)$(mandir)/man8/scsi_id.8 -.PHONY: uninstall-man - -install-config: - @ if [ ! -r $(DESTDIR)$(etcdir)/scsi_id.config ]; then \ - echo $(INSTALL) -d $(DESTDIR)$(etcdir); \ - echo $(INSTALL_DATA) ./scsi_id.config $(DESTDIR)$(etcdir); \ - $(INSTALL) -d $(DESTDIR)$(etcdir); \ - $(INSTALL_DATA) ./scsi_id.config $(DESTDIR)$(etcdir)/scsi_id.config; \ - fi -.PHONY: install-config - -install: all install-bin install-config install-man -.PHONY: install - -uninstall: uninstall-bin uninstall-man -.PHONY: uninstall diff --git a/extras/usb_id/Makefile b/extras/usb_id/Makefile deleted file mode 100644 index 09496049a5..0000000000 --- a/extras/usb_id/Makefile +++ /dev/null @@ -1,70 +0,0 @@ -# Makefile for udev extra invoked from the udev main Makefile -# -# Copyright (C) 2004-2005 Kay Sievers -# -# Released under the GNU General Public License, version 2. -# - -PROG = usb_id -OBJ = -HEADERS = -GEN_HEADERS = -MAN_PAGES = - -prefix = -etcdir = ${prefix}/etc -sbindir = ${prefix}/sbin -usrbindir = ${prefix}/usr/bin -usrsbindir = ${prefix}/usr/sbin -libudevdir = ${prefix}/lib/udev -mandir = ${prefix}/usr/share/man -configdir = ${etcdir}/udev/ - -INSTALL = install -c -INSTALL_PROGRAM = ${INSTALL} -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_SCRIPT = ${INSTALL} - -all: $(PROG) $(MAN_PAGES) -.PHONY: all -.DEFAULT: all - -%.o: %.c $(GEN_HEADERS) - $(E) " CC " $@ - $(Q) $(CC) -c $(CFLAGS) $< -o $@ - -$(PROG): %: $(HEADERS) %.o $(OBJS) - $(E) " LD " $@ - $(Q) $(LD) $(LDFLAGS) $@.o $(OBJS) -o $@ $(LIBUDEV) $(LIB_OBJS) - -# man pages -%.8: %.xml - $(E) " XMLTO " $@ - $(Q) xmlto man $? -.PRECIOUS: %.8 - -clean: - $(E) " CLEAN " - $(Q) rm -f $(PROG) $(OBJS) $(GEN_HEADERS) -.PHONY: clean - -install-bin: all - $(INSTALL) -d $(DESTDIR)$(libudevdir) - $(INSTALL_PROGRAM) $(PROG) $(DESTDIR)$(libudevdir)/$(PROG) -.PHONY: install-bin - -uninstall-bin: - - rm $(DESTDIR)$(libudevdir)/$(PROG) -.PHONY: uninstall-bin - -install-man: - @echo "Please create a man page for this tool." -.PHONY: install-man - -uninstall-man: - @echo "Please create a man page for this tool." -.PHONY: uninstall-man - -install-config: - @echo "no config file to install" -.PHONY: install-config diff --git a/extras/volume_id/Makefile b/extras/volume_id/Makefile deleted file mode 100644 index 50d5e338c2..0000000000 --- a/extras/volume_id/Makefile +++ /dev/null @@ -1,84 +0,0 @@ -# Makefile for udev extra invoked from the udev main Makefile -# -# Copyright (C) 2004-2006 Kay Sievers -# -# Released under the GNU General Public License, version 2. -# - -PROG = vol_id -GEN_HEADERS = -MAN_PAGES = vol_id.8 - -prefix = -etcdir = ${prefix}/etc -sbindir = ${prefix}/sbin -usrbindir = ${prefix}/usr/bin -usrsbindir = ${prefix}/usr/sbin -libudevdir = ${prefix}/lib/udev -mandir = ${prefix}/usr/share/man -configdir = ${etcdir}/udev/ - -INSTALL = install -c -INSTALL_PROGRAM = ${INSTALL} -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_SCRIPT = ${INSTALL} - -all: lib $(PROG) $(MAN_PAGES) -.PHONY: all -.DEFAULT: all - -.SUFFIXES: - -%.o: %.c $(GEN_HEADERS) - $(E) " CC " $@ - $(Q) $(CC) -c $(CFLAGS) $< -o $@ - -lib: - $(Q) $(MAKE) -C $@ -.PHONY: lib - -lib/libvolume_id.a: lib - -$(PROG): %: $(HEADERS) %.o lib/libvolume_id.a - $(E) " LD " $@ -ifeq ($(strip $(VOLUME_ID_STATIC)),true) - $(Q) $(LD) $(LDFLAGS) -o $@ $@.o $(LIBUDEV) lib/libvolume_id.a $(LIB_OBJS) -else - $(Q) $(LD) $(LDFLAGS) -o $@ $@.o $(LIBUDEV) -Llib -lvolume_id $(LIB_OBJS) -endif - -# man pages -%.8: %.xml - $(E) " XMLTO " $@ - $(Q) xmlto man $? -.PRECIOUS: %.8 - -clean: - $(E) " CLEAN " - $(Q) rm -f $(PROG) $(OBJS) $(GEN_HEADERS) - $(Q) $(MAKE) -C lib clean -.PHONY: clean - -install-bin: all - $(INSTALL) -d $(DESTDIR)$(libudevdir) - $(INSTALL_PROGRAM) $(PROG) $(DESTDIR)$(libudevdir)/$(PROG) - $(MAKE) -C lib install -.PHONY: install-bin - -uninstall-bin: - - rm $(DESTDIR)$(libudevdir)/$(PROG) - $(MAKE) -C lib uninstall -.PHONY: uninstall-bin - -install-man: - $(INSTALL) -d $(DESTDIR)$(mandir)/man8 - $(INSTALL_DATA) $(PROG).8 $(DESTDIR)$(mandir)/man8/$(PROG).8 -.PHONY: install-man - -uninstall-man: - -rm -f $(DESTDIR)$(mandir)/man8/$(PROG).8 -.PHONY: uninstall-man - -install-config: - @echo "no config file to install" -.PHONY: install-config diff --git a/extras/volume_id/lib/Makefile b/extras/volume_id/lib/Makefile deleted file mode 100644 index 95bb2ed394..0000000000 --- a/extras/volume_id/lib/Makefile +++ /dev/null @@ -1,138 +0,0 @@ -# libvolume_id - read filesystem label/uuid -# -# Copyright (C) 2004-2006 Kay Sievers -# -# Released under the GNU General Public License, version 2. -# -includedir = ${prefix}/usr/include -oslibdir = $(shell $(CC) $(CFLAGS) -print-multi-os-directory) -libdir = $(realpath ${prefix}/lib/$(oslibdir)) -usrlibdir = $(realpath ${prefix}/usr/lib/$(oslibdir)) - -INSTALL = install -c -INSTALL_DATA = ${INSTALL} -m 644 -INSTALL_LIB = ${INSTALL} -m 755 - -SHLIB_CUR = 0 -SHLIB_REV = 85 -SHLIB_AGE = 0 -SHLIB = libvolume_id.so.$(SHLIB_CUR).$(SHLIB_REV).$(SHLIB_AGE) - -OBJS= \ - ext.o \ - fat.o \ - hfs.o \ - highpoint.o \ - isw_raid.o \ - lsi_raid.o \ - via_raid.o \ - silicon_raid.o \ - nvidia_raid.o \ - promise_raid.o \ - adaptec_raid.o \ - jmicron_raid.o \ - ddf_raid.o \ - iso9660.o \ - jfs.o \ - linux_raid.o \ - linux_swap.o \ - lvm.o \ - ntfs.o \ - reiserfs.o \ - udf.o \ - ufs.o \ - xfs.o \ - cramfs.o \ - hpfs.o \ - romfs.o \ - sysv.o \ - minix.o \ - gfs.o \ - luks.o \ - ocfs.o \ - vxfs.o \ - squashfs.o \ - netware.o \ - oracleasm.o \ - volume_id.o \ - util.o - -HEADERS= \ - libvolume_id.h \ - util.h - -all: libvolume_id.a $(SHLIB) libvolume_id.pc -.PHONY: all -.DEFAULT: all - -.SUFFIXES: - -%.o: %.c - $(E) " CC " $@ - $(Q) $(CC) -c $(CFLAGS) $< -o $@ - -.shlib/%.o: %.c - $(E) " CC " $@ - $(Q) mkdir -p $(dir $@) - $(Q) $(CC) -c $(CFLAGS) -fPIC $< -o $@ - -libvolume_id.a: $(HEADERS) $(OBJS) - $(Q) rm -f $@ - $(E) " AR " $@ - $(Q) $(AR) cq $@ $(OBJS) - $(E) " RANLIB " $@ - $(Q) $(RANLIB) $@ - -$(SHLIB): $(HEADERS) exported_symbols $(addprefix .shlib/,$(OBJS)) - $(E) " CC " $@ - $(Q) $(CC) -shared $(CFLAGS) $(LDFLAGS) -o $@ \ - -Wl,-soname,libvolume_id.so.$(SHLIB_CUR),--version-script,exported_symbols \ - $(addprefix .shlib/,$(OBJS)) - $(Q) ln -sf $@ libvolume_id.so.$(SHLIB_CUR) - $(Q) ln -sf $@ libvolume_id.so - -libvolume_id.pc: - $(E) " GENPC " $@ - $(Q) echo "prefix=${prefix}/usr" >> $@ - $(Q) echo "exec_prefix=\$${prefix}" >> $@ - $(Q) echo "libdir=${usrlibdir}" >> $@ - $(Q) echo "includedir=${includedir}" >> $@ - $(Q) echo "" >> $@ - $(Q) echo "Name: libvolume_id" >> $@ - $(Q) echo "Description: Filesystem label and uuid access" >> $@ - $(Q) echo "Version: $(SHLIB_CUR).$(SHLIB_REV).$(SHLIB_AGE)" >> $@ - $(Q) echo "Libs: -L\$${libdir} -lvolume_id" >> $@ - $(Q) echo "Cflags: -I\$${includedir}" >> $@ - -install: all - $(INSTALL) -d $(DESTDIR)$(includedir) - $(INSTALL_DATA) libvolume_id.h $(DESTDIR)$(includedir)/libvolume_id.h - $(INSTALL) -d $(DESTDIR)$(libdir) - $(INSTALL_LIB) $(SHLIB) $(DESTDIR)$(libdir)/$(SHLIB) - $(INSTALL) -d $(DESTDIR)$(usrlibdir) - ln -sf $(SHLIB) $(DESTDIR)$(libdir)/libvolume_id.so.$(SHLIB_CUR) -ifeq ($(libdir),$(usrlibdir)) - ln -sf $(SHLIB) $(DESTDIR)$(usrlibdir)/libvolume_id.so -else - ln -sf $(libdir)/$(SHLIB) $(DESTDIR)$(usrlibdir)/libvolume_id.so -endif - $(INSTALL) -d $(DESTDIR)$(usrlibdir)/pkgconfig - $(INSTALL_DATA) libvolume_id.pc $(DESTDIR)$(usrlibdir)/pkgconfig/libvolume_id.pc -.PHONY: install - -uninstall: - rm -f $(DESTDIR)$(includedir)/libvolume_id.h - rm -f $(DESTDIR)$(usrlibdir)/libvolume_id.a - rm -f $(DESTDIR)$(libdir)/$(SHLIB) - rm -f $(DESTDIR)$(libdir)/libvolume_id.so.$(SHLIB_CUR) - rm -f $(DESTDIR)$(libdir)/libvolume_id.so -.PHONY: uninstall - -clean: - $(E) " CLEAN " - $(Q) rm -f libvolume_id.a $(OBJS) - $(Q) rm -f $(SHLIB) libvolume_id.so.$(SHLIB_CUR) libvolume_id.so - $(Q) rm -rf .shlib - $(Q) rm -f libvolume_id.pc -.PHONY: clean - diff --git a/extras/volume_id/vol_id.8 b/extras/volume_id/vol_id.8 deleted file mode 100644 index 2e4a8c3811..0000000000 --- a/extras/volume_id/vol_id.8 +++ /dev/null @@ -1,79 +0,0 @@ -.\" Title: vol_id -.\" Author: -.\" Generator: DocBook XSL Stylesheets v1.73.2 -.\" Date: March 2006 -.\" Manual: vol_id -.\" Source: volume_id -.\" -.TH "VOL_ID" "8" "March 2006" "volume_id" "vol_id" -.\" disable hyphenation -.nh -.\" disable justification (adjust text to left margin only) -.ad l -.SH "NAME" -vol_id - probe filesystem type and read label and uuid -.SH "SYNOPSIS" -.HP 7 -\fBvol_id\fR [\fB\-\-export\fR] [\fB\-\-type\fR] [\fB\-\-label\fR] [\fB\-\-label\-raw\fR] [\fB\-\-uuid\fR] [\fB\-\-skip\-raid\fR] [\fB\-\-probe\-all\fR] [\fB\-\-help\fR] [\fIdevice\fR] -.SH "DESCRIPTION" -.PP -\fBvol_id\fR -is usually called from a udev rule, to provide udev with the filesystem type, the label and the uuid of a volume\. It supports most of the common filesystem formats and detects various raid setups to prevent the recognition of raid members as a volume with a filesystem\. -.SH "OPTIONS" -.PP -\fB\-\-export\fR -.RS 4 -Print all values in key/value format to import them into the environment\. -.RE -.PP -\fB\-\-type\fR -.RS 4 -Print the filesystem type\. -.RE -.PP -\fB\-\-label\fR -.RS 4 -Print the safe version of volume label suitable for use as filename\. -.RE -.PP -\fB\-\-label\-raw\fR -.RS 4 -Print the raw volume label\. -.RE -.PP -\fB\-\-uuid\fR -.RS 4 -Print the uuid of a volume\. -.RE -.PP -\fB\-\-skip\-raid\fR -.RS 4 -Skip detection of raid metadata\. -.RE -.PP -\fB\-\-probe\-all\fR -.RS 4 -Probe for all types and print all matches\. -.RE -.PP -\fB\-\-help\fR -.RS 4 -Print usage\. -.RE -.SH "ENVIRONMENT" -.PP -\fBUDEV_LOG\fR -.RS 4 -Set the syslog priority\. -.RE -.SH "EXIT STATUS" -.PP -\fBvol_id\fR -will only return successful if the value asked for is not empty\. All trailing whitespace will be removed, spaces replaced by underscore and slashes ignored\. -.SH "AUTHOR" -.PP -Written by Kay Sievers - -.SH "SEE ALSO" -.PP -\fBudev\fR(7) diff --git a/list.h b/list.h deleted file mode 100644 index 8626630f6b..0000000000 --- a/list.h +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copied from the Linux kernel source tree, version 2.6.0-test1. - * - * Licensed under the GPL v2 as per the whole kernel source tree. - * - */ - -#ifndef _LIST_H -#define _LIST_H - -/** - * container_of - cast a member of a structure out to the containing structure - * - * @ptr: the pointer to the member. - * @type: the type of the container struct this is embedded in. - * @member: the name of the member within the struct. - * - */ -#define container_of(ptr, type, member) ({ \ - const typeof( ((type *)0)->member ) *__mptr = (ptr); \ - (type *)( (char *)__mptr - offsetof(type,member) );}) - -/* - * These are non-NULL pointers that will result in page faults - * under normal circumstances, used to verify that nobody uses - * non-initialized list entries. - */ -#define LIST_POISON1 ((void *) 0x00100100) -#define LIST_POISON2 ((void *) 0x00200200) - -/* - * Simple doubly linked list implementation. - * - * Some of the internal functions ("__xxx") are useful when - * manipulating whole lists rather than single entries, as - * sometimes we already know the next/prev entries and we can - * generate better code by using them directly rather than - * using the generic single-entry routines. - */ - -struct list_head { - struct list_head *next, *prev; -}; - -#define LIST_HEAD_INIT(name) { &(name), &(name) } - -#define LIST_HEAD(name) \ - struct list_head name = LIST_HEAD_INIT(name) - -#define INIT_LIST_HEAD(ptr) do { \ - (ptr)->next = (ptr); (ptr)->prev = (ptr); \ -} while (0) - -/* - * Insert a new entry between two known consecutive entries. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static inline void __list_add(struct list_head *new, - struct list_head *prev, - struct list_head *next) -{ - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; -} - -/** - * list_add - add a new entry - * @new: new entry to be added - * @head: list head to add it after - * - * Insert a new entry after the specified head. - * This is good for implementing stacks. - */ -static inline void list_add(struct list_head *new, struct list_head *head) -{ - __list_add(new, head, head->next); -} - -/** - * list_add_tail - add a new entry - * @new: new entry to be added - * @head: list head to add it before - * - * Insert a new entry before the specified head. - * This is useful for implementing queues. - */ -static inline void list_add_tail(struct list_head *new, struct list_head *head) -{ - __list_add(new, head->prev, head); -} - -/* - * Delete a list entry by making the prev/next entries - * point to each other. - * - * This is only for internal list manipulation where we know - * the prev/next entries already! - */ -static inline void __list_del(struct list_head * prev, struct list_head * next) -{ - next->prev = prev; - prev->next = next; -} - -/** - * list_del - deletes entry from list. - * @entry: the element to delete from the list. - * Note: list_empty on entry does not return true after this, the entry is - * in an undefined state. - */ -static inline void list_del(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); - entry->next = LIST_POISON1; - entry->prev = LIST_POISON2; -} - -/** - * list_del_init - deletes entry from list and reinitialize it. - * @entry: the element to delete from the list. - */ -static inline void list_del_init(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); - INIT_LIST_HEAD(entry); -} - -/** - * list_move - delete from one list and add as another's head - * @list: the entry to move - * @head: the head that will precede our entry - */ -static inline void list_move(struct list_head *list, struct list_head *head) -{ - __list_del(list->prev, list->next); - list_add(list, head); -} - -/** - * list_move_tail - delete from one list and add as another's tail - * @list: the entry to move - * @head: the head that will follow our entry - */ -static inline void list_move_tail(struct list_head *list, - struct list_head *head) -{ - __list_del(list->prev, list->next); - list_add_tail(list, head); -} - -/** - * list_empty - tests whether a list is empty - * @head: the list to test. - */ -static inline int list_empty(struct list_head *head) -{ - return head->next == head; -} - -static inline void __list_splice(struct list_head *list, - struct list_head *head) -{ - struct list_head *first = list->next; - struct list_head *last = list->prev; - struct list_head *at = head->next; - - first->prev = head; - head->next = first; - - last->next = at; - at->prev = last; -} - -/** - * list_splice - join two lists - * @list: the new list to add. - * @head: the place to add it in the first list. - */ -static inline void list_splice(struct list_head *list, struct list_head *head) -{ - if (!list_empty(list)) - __list_splice(list, head); -} - -/** - * list_splice_init - join two lists and reinitialise the emptied list. - * @list: the new list to add. - * @head: the place to add it in the first list. - * - * The list at @list is reinitialised - */ -static inline void list_splice_init(struct list_head *list, - struct list_head *head) -{ - if (!list_empty(list)) { - __list_splice(list, head); - INIT_LIST_HEAD(list); - } -} - -/** - * list_entry - get the struct for this entry - * @ptr: the &struct list_head pointer. - * @type: the type of the struct this is embedded in. - * @member: the name of the list_struct within the struct. - */ -#define list_entry(ptr, type, member) \ - container_of(ptr, type, member) - -/** - * list_for_each - iterate over a list - * @pos: the &struct list_head to use as a loop counter. - * @head: the head for your list. - */ -#define list_for_each(pos, head) \ - for (pos = (head)->next; pos != (head); \ - pos = pos->next) - -/** - * __list_for_each - iterate over a list - * @pos: the &struct list_head to use as a loop counter. - * @head: the head for your list. - * - * This variant differs from list_for_each() in that it's the - * simplest possible list iteration code. - * Use this for code that knows the list to be very short (empty - * or 1 entry) most of the time. - */ -#define __list_for_each(pos, head) \ - for (pos = (head)->next; pos != (head); pos = pos->next) - -/** - * list_for_each_prev - iterate over a list backwards - * @pos: the &struct list_head to use as a loop counter. - * @head: the head for your list. - */ -#define list_for_each_prev(pos, head) \ - for (pos = (head)->prev; pos != (head); pos = pos->prev) - -/** - * list_for_each_safe - iterate over a list safe against removal of list entry - * @pos: the &struct list_head to use as a loop counter. - * @n: another &struct list_head to use as temporary storage - * @head: the head for your list. - */ -#define list_for_each_safe(pos, n, head) \ - for (pos = (head)->next, n = pos->next; pos != (head); \ - pos = n, n = pos->next) - -/** - * list_for_each_entry - iterate over list of given type - * @pos: the type * to use as a loop counter. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - */ -#define list_for_each_entry(pos, head, member) \ - for (pos = list_entry((head)->next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = list_entry(pos->member.next, typeof(*pos), member)) - -/** - * list_for_each_entry_reverse - iterate backwards over list of given type. - * @pos: the type * to use as a loop counter. - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - */ -#define list_for_each_entry_reverse(pos, head, member) \ - for (pos = list_entry((head)->prev, typeof(*pos), member); \ - &pos->member != (head); \ - pos = list_entry(pos->member.prev, typeof(*pos), member)) - -/** - * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry - * @pos: the type * to use as a loop counter. - * @n: another type * to use as temporary storage - * @head: the head for your list. - * @member: the name of the list_struct within the struct. - */ -#define list_for_each_entry_safe(pos, n, head, member) \ - for (pos = list_entry((head)->next, typeof(*pos), member), \ - n = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = n, n = list_entry(n->member.next, typeof(*n), member)) - -#endif /* _LIST_H */ diff --git a/logging.h b/logging.h deleted file mode 100644 index 6e1d5a20ef..0000000000 --- a/logging.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * simple logging functions that can be expanded into nothing - * - * Copyright (C) 2003-2004 Greg Kroah-Hartman - * Copyright (C) 2004-2006 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef LOGGING_H -#define LOGGING_H - -#define err(format, arg...) do { } while (0) -#define info(format, arg...) do { } while (0) -#define dbg(format, arg...) do { } while (0) -#define logging_init(foo) do { } while (0) -#define logging_close(foo) do { } while (0) - -#ifdef USE_LOG -#include -#include -#include - -#undef err -#define err(format, arg...) \ - do { \ - log_message(LOG_ERR ,"%s: " format ,__FUNCTION__ ,## arg); \ - } while (0) - -#undef info -#define info(format, arg...) \ - do { \ - log_message(LOG_INFO ,"%s: " format ,__FUNCTION__ ,## arg); \ - } while (0) - -#ifdef DEBUG -#undef dbg -#define dbg(format, arg...) \ - do { \ - log_message(LOG_DEBUG ,"%s: " format ,__FUNCTION__ ,## arg); \ - } while (0) -#endif - -extern void log_message(int priority, const char *format, ...) - __attribute__ ((format (printf, 2, 3))); - -#undef logging_init -static inline void logging_init(const char *program_name) -{ - openlog(program_name, LOG_PID | LOG_CONS, LOG_DAEMON); -} - -#undef logging_close -static inline void logging_close(void) -{ - closelog(); -} - -#endif /* USE_LOG */ - -#endif diff --git a/test-udev.c b/test-udev.c deleted file mode 100644 index 4ac2d5a889..0000000000 --- a/test-udev.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2003-2004 Greg Kroah-Hartman - * Copyright (C) 2004-2006 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udev_rules.h" -#include "udev_selinux.h" - -#ifdef USE_LOG -void log_message(int priority, const char *format, ...) -{ - va_list args; - - if (priority > udev_log_priority) - return; - - va_start(args, format); - vsyslog(priority, format, args); - va_end(args); -} -#endif - -static void asmlinkage sig_handler(int signum) -{ - switch (signum) { - case SIGALRM: - exit(1); - case SIGINT: - case SIGTERM: - exit(20 + signum); - } -} - -int main(int argc, char *argv[], char *envp[]) -{ - struct sysfs_device *dev; - struct udevice *udev; - const char *maj, *min; - struct udev_rules rules; - const char *action; - const char *devpath; - const char *subsystem; - struct sigaction act; - int devnull; - int retval = -EINVAL; - - if (argc == 2 && strcmp(argv[1], "-V") == 0) { - printf("%s\n", UDEV_VERSION); - exit(0); - } - - /* set std fd's to /dev/null, /sbin/hotplug forks us, we don't have them at all */ - devnull = open("/dev/null", O_RDWR); - if (devnull >= 0) { - if (devnull != STDIN_FILENO) - dup2(devnull, STDIN_FILENO); - if (devnull != STDOUT_FILENO) - dup2(devnull, STDOUT_FILENO); - if (devnull != STDERR_FILENO) - dup2(devnull, STDERR_FILENO); - if (devnull > STDERR_FILENO) - close(devnull); - } - - logging_init("udev"); - if (devnull < 0) - err("open /dev/null failed: %s\n", strerror(errno)); - udev_config_init(); - selinux_init(); - dbg("version %s\n", UDEV_VERSION); - - /* set signal handlers */ - memset(&act, 0x00, sizeof(act)); - act.sa_handler = (void (*)(int)) sig_handler; - sigemptyset (&act.sa_mask); - act.sa_flags = 0; - sigaction(SIGALRM, &act, NULL); - sigaction(SIGINT, &act, NULL); - sigaction(SIGTERM, &act, NULL); - - /* trigger timeout to prevent hanging processes */ - alarm(UDEV_EVENT_TIMEOUT); - - action = getenv("ACTION"); - devpath = getenv("DEVPATH"); - subsystem = getenv("SUBSYSTEM"); - /* older kernels passed the SUBSYSTEM only as argument */ - if (subsystem == NULL && argc == 2) - subsystem = argv[1]; - - if (action == NULL || subsystem == NULL || devpath == NULL) { - err("action, subsystem or devpath missing\n"); - goto exit; - } - - /* export log_priority , as called programs may want to do the same as udev */ - if (udev_log_priority) { - char priority[32]; - - sprintf(priority, "%i", udev_log_priority); - setenv("UDEV_LOG", priority, 1); - } - - sysfs_init(); - udev_rules_init(&rules, 0); - - dev = sysfs_device_get(devpath); - if (dev == NULL) { - info("unable to open '%s'\n", devpath); - goto fail; - } - - udev = udev_device_init(NULL); - if (udev == NULL) - goto fail; - - /* override built-in sysfs device */ - udev->dev = dev; - strlcpy(udev->action, action, sizeof(udev->action)); - - /* get dev_t from environment, which is needed for "remove" to work, "add" works also from sysfs */ - maj = getenv("MAJOR"); - min = getenv("MINOR"); - if (maj != NULL && min != NULL) - udev->devt = makedev(atoi(maj), atoi(min)); - else - udev->devt = udev_device_get_devt(udev); - - retval = udev_device_event(&rules, udev); - - /* rules may change/disable the timeout */ - if (udev->event_timeout >= 0) - alarm(udev->event_timeout); - - if (retval == 0 && !udev->ignore_device && udev_run) - udev_rules_run(udev); - - udev_device_cleanup(udev); -fail: - udev_rules_cleanup(&rules); - sysfs_cleanup(); - selinux_exit(); - -exit: - logging_close(); - if (retval != 0) - return 1; - return 0; -} diff --git a/udev.7 b/udev.7 deleted file mode 100644 index c4ca28603d..0000000000 --- a/udev.7 +++ /dev/null @@ -1,432 +0,0 @@ -.\" Title: udev -.\" Author: -.\" Generator: DocBook XSL Stylesheets v1.73.2 -.\" Date: August 2005 -.\" Manual: udev -.\" Source: udev -.\" -.TH "UDEV" "7" "August 2005" "udev" "udev" -.\" disable hyphenation -.nh -.\" disable justification (adjust text to left margin only) -.ad l -.SH "NAME" -udev - dynamic device management -.SH "DESCRIPTION" -.PP -udev provides a dynamic device directory containing only the files for actually present devices\. It creates or removes device node files in the -\fI/dev\fR -directory, or it renames network interfaces\. -.PP -Usually udev runs as -\fBudevd\fR(8) -and receives uevents directly from the kernel if a device is added or removed from the system\. -.PP -If udev receives a device event, it matches its configured rules against the available device attributes provided in sysfs to identify the device\. Rules that match may provide additional device information or specify a device node name and multiple symlink names and instruct udev to run additional programs as part of the device event handling\. -.SH "CONFIGURATION" -.PP -udev configuration files are placed in -\fI/etc/udev/\fR -and -\fI/lib/udev/\fR\. All empty lines, or lines beginning with \'#\' will be ignored\. -.SS "Configuration file" -.PP -udev expects its main configuration file at -\fI/etc/udev/udev\.conf\fR\. It consists of a set of variables allowing the user to override default udev values\. The following variables can be set: -.PP -\fBudev_root\fR -.RS 4 -Specifies where to place the device nodes in the filesystem\. The default value is -\fI/dev\fR\. -.RE -.PP -\fBudev_log\fR -.RS 4 -The logging priority\. Valid values are the numerical syslog priorities or their textual representations: -\fBerr\fR, -\fBinfo\fR -and -\fBdebug\fR\. -.RE -.SS "Rules files" -.PP -The udev rules are read from the files located in the default rules directory -\fI/lib/udev/rules\.d/\fR, the custom rules directory -\fI/etc/udev/rules\.d/\fR -and the temporary rules directory -\fI/dev/\.udev/rules\.d/\fR\. All rule files are sorted and processed in lexical order, regardless in which of these directories they live\. Every line in the rules file contains at least one key value pair\. There are two kind of keys, match and assignment keys\. If all match keys are matching against its value, the rule gets applied and the assign keys get the specified value assigned\. -.PP -A matching rule may specify the name of the device node, add a symlink pointing to the node, or run a specified program as part of the event handling\. If no matching rule is found, the default device node name is used\. -.PP -A rule may consist of a list of one or more key value pairs separated by a comma\. Each key has a distinct operation, depending on the used operator\. Valid operators are: -.PP -\fB==\fR -.RS 4 -Compare for equality\. -.RE -.PP -\fB!=\fR -.RS 4 -Compare for non\-equality\. -.RE -.PP -\fB=\fR -.RS 4 -Assign a value to a key\. Keys that represent a list, are reset and only this single value is assigned\. -.RE -.PP -\fB+=\fR -.RS 4 -Add the value to a key that holds a list of entries\. -.RE -.PP -\fB:=\fR -.RS 4 -Assign a value to a key finally; disallow any later changes, which may be used to prevent changes by any later rules\. -.RE -.PP -The following key names can be used to match against device properties\. Some of the keys also match against properties of the parent devices in sysfs, not only the device that has generated the event\. If multiple keys that match a parent device are specified in a single rule, all these keys must match at one and the same parent device\. -.PP -\fBACTION\fR -.RS 4 -Match the name of the event action\. -.RE -.PP -\fBDEVPATH\fR -.RS 4 -Match the devpath of the event device\. -.RE -.PP -\fBKERNEL\fR -.RS 4 -Match the name of the event device\. -.RE -.PP -\fBNAME\fR -.RS 4 -Match the name of the node or network interface\. It can be used once the NAME key has been set in one of the preceding rules\. -.RE -.PP -\fBSUBSYSTEM\fR -.RS 4 -Match the subsystem of the event device\. -.RE -.PP -\fBDRIVER\fR -.RS 4 -Match the driver name of the event device\. Only set for devices which are bound to a driver at the time the event is generated\. -.RE -.PP -\fBATTR{\fR\fB\fIfilename\fR\fR\fB}\fR -.RS 4 -Match sysfs attribute values of the event device\. Up to five -\fBATTR\fR -keys can be specified per rule\. Trailing whitespace in the attribute values is ignored, if the specified match value does not contain trailing whitespace itself\. Depending on the type of operator, this key is also used to set the value of a sysfs attribute\. -.RE -.PP -\fBKERNELS\fR -.RS 4 -Search the devpath upwards for a matching device name\. -.RE -.PP -\fBSUBSYSTEMS\fR -.RS 4 -Search the devpath upwards for a matching device subsystem name\. -.RE -.PP -\fBDRIVERS\fR -.RS 4 -Search the devpath upwards for a matching device driver name\. -.RE -.PP -\fBATTRS{\fR\fB\fIfilename\fR\fR\fB}\fR -.RS 4 -Search the devpath upwards for a device with matching sysfs attribute values\. Up to five -\fBATTRS\fR -keys can be specified per rule, but all of them must match on the same device\. Trailing whitespace in the attribute values is ignored, if the specified match value does not contain trailing whitespace itself\. -.RE -.PP -\fBENV{\fR\fB\fIkey\fR\fR\fB}\fR -.RS 4 -Match against the value of an environment variable\. Up to five -\fBENV\fR -keys can be specified per rule\. Depending on the type of operator, this key is also used to export a variable to the environment\. -.RE -.PP -\fBTEST{\fR\fB\fIoctal mode mask\fR\fR\fB}\fR -.RS 4 -Test the existence of a file\. An octal mode mask can be specified if needed\. -.RE -.PP -\fBPROGRAM\fR -.RS 4 -Execute external program\. The key is true, if the program returns with exit code zero\. The whole event environment is available to the executed program\. The program\'s output printed to stdout, is available in the RESULT key\. -.RE -.PP -\fBRESULT\fR -.RS 4 -Match the returned string of the last PROGRAM call\. This key can be used in the same or in any later rule after a PROGRAM call\. -.RE -.PP -Most of the fields support a shell style pattern matching\. The following pattern characters are supported: -.PP -\fB*\fR -.RS 4 -Matches zero, or any number of characters\. -.RE -.PP -\fB?\fR -.RS 4 -Matches any single character\. -.RE -.PP -\fB[]\fR -.RS 4 -Matches any single character specified within the brackets\. For example, the pattern string \'tty[SR]\' would match either \'ttyS\' or \'ttyR\'\. Ranges are also supported within this match with the \'\-\' character\. For example, to match on the range of all digits, the pattern [0\-9] would be used\. If the first character following the \'[\' is a \'!\', any characters not enclosed are matched\. -.RE -.PP -The following keys can get values assigned: -.PP -\fBNAME\fR -.RS 4 -The name of the node to be created, or the name the network interface should be renamed to\. Only one rule can set the node name, all later rules with a NAME key will be ignored\. -.RE -.PP -\fBSYMLINK\fR -.RS 4 -The name of a symlink targeting the node\. Every matching rule can add this value to the list of symlinks to be created along with the device node\. Multiple symlinks may be specified by separating the names by the space character\. -.RE -.PP -\fBOWNER, GROUP, MODE\fR -.RS 4 -The permissions for the device node\. Every specified value overwrites the compiled\-in default value\. -.RE -.PP -\fBATTR{\fR\fB\fIkey\fR\fR\fB}\fR -.RS 4 -The value that should be written to a sysfs attribute of the event device\. Depending on the type of operator, this key is also used to match against the value of a sysfs attribute\. -.RE -.PP -\fBENV{\fR\fB\fIkey\fR\fR\fB}\fR -.RS 4 -Export a variable to the environment\. Depending on the type of operator, this key is also to match against an environment variable\. -.RE -.PP -\fBRUN\fR -.RS 4 -Add a program to the list of programs to be executed for a specific device\. This can only be used for very short running tasks\. Running an event process for a long period of time may block all further events for this or a dependent device\. Long running tasks need to be immediately detached from the event process itself\. -.sp -If the specifiefd string starts with -\fBsocket:\fR\fB\fIpath\fR\fR, all current event values will be passed to the specified socket, as a message in the same format the kernel sends an uevent\. If the first character of the specified path is an @ character, an abstract namespace socket is used, instead of an existing socket file\. -.RE -.PP -\fBLABEL\fR -.RS 4 -Named label where a GOTO can jump to\. -.RE -.PP -\fBGOTO\fR -.RS 4 -Jumps to the next LABEL with a matching name -.RE -.PP -\fBIMPORT{\fR\fB\fItype\fR\fR\fB}\fR -.RS 4 -Import a set of variables into the event environment, depending on -\fItype\fR: -.PP -\fBprogram\fR -.RS 4 -Execute an external program specified as the assigned value and import its output, which must be in environment key format\. -.RE -.PP -\fBfile\fR -.RS 4 -Import a text file specified as the assigned value, which must be in environment key format\. -.RE -.PP -\fBparent\fR -.RS 4 -Import the stored keys from the parent device by reading the database entry of the parent device\. The value assigned to -\fBIMPORT{parent}\fR -is used as a filter of key names to import (with the same shell\-style pattern matching used for comparisons)\. -.RE -.sp -If no option is given, udev will choose between -\fBprogram\fR -and -\fBfile\fR -based on the executable bit of the file permissions\. -.RE -.PP -\fBWAIT_FOR\fR -.RS 4 -Wait for a file to become available\. -.RE -.PP -\fBOPTIONS\fR -.RS 4 -Rule and device options: -.PP -\fBlast_rule\fR -.RS 4 -Stops further rules application\. No later rules will have any effect\. -.RE -.PP -\fBignore_device\fR -.RS 4 -Ignore this event completely\. -.RE -.PP -\fBignore_remove\fR -.RS 4 -Do not remove the device node when the device goes away\. This may be useful as a workaround for broken device drivers\. -.RE -.PP -\fBlink_priority=\fR\fB\fIvalue\fR\fR -.RS 4 -Specify the priority of the created symlinks\. Devices with higher priorities overwrite existing symlinks of other devices\. The default is 0\. -.RE -.PP -\fBall_partitions\fR -.RS 4 -Create the device nodes for all available partitions of a block device\. This may be useful for removable media devices where media changes are not detected\. -.RE -.PP -\fBevent_timeout=\fR -.RS 4 -Number of seconds an event will wait for operations to finish, before it will terminate itself\. -.RE -.PP -\fBstring_escape=\fR\fB\fInone|replace\fR\fR -.RS 4 -Usually control and other possibly unsafe characters are replaced in strings used for device naming\. The mode of replacement can be specified with this option\. -.RE -.RE -.PP -The -\fBNAME\fR, -\fBSYMLINK\fR, -\fBPROGRAM\fR, -\fBOWNER\fR, -\fBGROUP\fR, -\fBMODE\fR -and -\fBRUN\fR -fields support simple printf\-like string substitutions\. The -\fBRUN\fR -format chars gets applied after all rules have been processed, right before the program is executed\. It allows the use of the complete environment set by earlier matching rules\. For all other fields, substitutions are applied while the individual rule is being processed\. The available substitutions are: -.PP -\fB$kernel\fR, \fB%k\fR -.RS 4 -The kernel name for this device\. -.RE -.PP -\fB$number\fR, \fB%n\fR -.RS 4 -The kernel number for this device\. For example, \'sda3\' has kernel number of \'3\' -.RE -.PP -\fB$devpath\fR, \fB%p\fR -.RS 4 -The devpath of the device\. -.RE -.PP -\fB$id\fR, \fB%b\fR -.RS 4 -The name of the device matched while searching the devpath upwards for -\fBSUBSYSTEMS\fR, -\fBKERNELS\fR, -\fBDRIVERS\fR -and -\fBATTRS\fR\. -.RE -.PP -\fB$driver\fR -.RS 4 -The driver name of the device matched while searching the devpath upwards for -\fBSUBSYSTEMS\fR, -\fBKERNELS\fR, -\fBDRIVERS\fR -and -\fBATTRS\fR\. -.RE -.PP -\fB$attr{\fR\fB\fIfile\fR\fR\fB}\fR, \fB%s{\fR\fB\fIfile\fR\fR\fB}\fR -.RS 4 -The value of a sysfs attribute found at the device, where all keys of the rule have matched\. If the matching device does not have such an attribute, follow the chain of parent devices and use the value of the first attribute that matches\. If the attribute is a symlink, the last element of the symlink target is returned as the value\. -.RE -.PP -\fB$env{\fR\fB\fIkey\fR\fR\fB}\fR, \fB%E{\fR\fB\fIkey\fR\fR\fB}\fR -.RS 4 -The value of an environment variable\. -.RE -.PP -\fB$major\fR, \fB%M\fR -.RS 4 -The kernel major number for the device\. -.RE -.PP -\fB$minor\fR, \fB%m\fR -.RS 4 -The kernel minor number for the device\. -.RE -.PP -\fB$result\fR, \fB%c\fR -.RS 4 -The string returned by the external program requested with PROGRAM\. A single part of the string, separated by a space character may be selected by specifying the part number as an attribute: -\fB%c{N}\fR\. If the number is followed by the \'+\' char this part plus all remaining parts of the result string are substituted: -\fB%c{N+}\fR -.RE -.PP -\fB$parent\fR, \fB%P\fR -.RS 4 -The node name of the parent device\. -.RE -.PP -\fB$name\fR -.RS 4 -The current name of the device node\. If not changed by a rule, it is the name of the kernel device\. -.RE -.PP -\fB$links\fR -.RS 4 -The current list of symlinks, separated by a space character\. The value is only set if an earlier rule assigned a value, or during a remove events\. -.RE -.PP -\fB$root\fR, \fB%r\fR -.RS 4 -The udev_root value\. -.RE -.PP -\fB$sys\fR, \fB%S\fR -.RS 4 -The sysfs mount point\. -.RE -.PP -\fB$tempnode\fR, \fB%N\fR -.RS 4 -The name of a created temporary device node to provide access to the device from a external program before the real node is created\. -.RE -.PP -\fB%%\fR -.RS 4 -The \'%\' character itself\. -.RE -.PP -\fB$$\fR -.RS 4 -The \'$\' character itself\. -.RE -.PP -The count of characters to be substituted may be limited by specifying the format length value\. For example, \'%3s{file}\' will only insert the first three characters of the sysfs attribute -.SH "AUTHOR" -.PP -Written by Greg Kroah\-Hartman - -and Kay Sievers -\. With much help from Dan Stekloff and many others\. -.SH "SEE ALSO" -.PP -\fBudevd\fR(8), -\fBudevadm\fR(8) diff --git a/udev.h b/udev.h deleted file mode 100644 index 5ecef4abbd..0000000000 --- a/udev.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2003 Greg Kroah-Hartman - * Copyright (C) 2003-2006 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef _UDEV_H_ -#define _UDEV_H_ - -#include -#include - -#include "list.h" -#include "logging.h" -#include "udev_sysdeps.h" -#include "udev_version.h" - -#define COMMENT_CHARACTER '#' -#define LINE_SIZE 512 -#define PATH_SIZE 512 -#define NAME_SIZE 256 -#define VALUE_SIZE 128 - -#define ALLOWED_CHARS "#+-.:=@_" -#define ALLOWED_CHARS_FILE ALLOWED_CHARS "/" -#define ALLOWED_CHARS_INPUT ALLOWED_CHARS_FILE " $%?," - -#define DEFAULT_PARTITIONS_COUNT 15 -#define UDEV_EVENT_TIMEOUT 180 - -#define UDEV_MAX(a,b) ((a) > (b) ? (a) : (b)) - -/* pipes */ -#define READ_END 0 -#define WRITE_END 1 - -#define UDEV_ROOT "/dev" -#define DB_DIR ".udev/db" -#define DB_NAME_INDEX_DIR ".udev/names" -#define RULES_LIB_DIR "/lib/udev/rules.d" -#define RULES_DYN_DIR ".udev/rules.d" -#define RULES_ETC_DIR "/etc/udev/rules.d" - -struct udev_rules; - -struct sysfs_device { - struct list_head node; /* for device cache */ - struct sysfs_device *parent; /* already cached parent*/ - char devpath[PATH_SIZE]; - char subsystem[NAME_SIZE]; /* $class, $bus, drivers, module */ - char kernel[NAME_SIZE]; /* device instance name */ - char kernel_number[NAME_SIZE]; - char driver[NAME_SIZE]; /* device driver name */ -}; - -struct udevice { - /* device event */ - struct sysfs_device *dev; /* points to dev_local by default */ - struct sysfs_device dev_local; - struct sysfs_device *dev_parent; /* current parent device used for matching */ - char action[NAME_SIZE]; - char *devpath_old; - - /* node */ - char name[PATH_SIZE]; - struct list_head symlink_list; - int symlink_final; - char owner[NAME_SIZE]; - int owner_final; - char group[NAME_SIZE]; - int group_final; - mode_t mode; - int mode_final; - dev_t devt; - - /* event processing */ - struct list_head run_list; - int run_final; - struct list_head env_list; - char tmp_node[PATH_SIZE]; - int partitions; - int ignore_device; - int ignore_remove; - char program_result[PATH_SIZE]; - int link_priority; - int event_timeout; - int test_run; -}; - -/* udev_config.c */ -extern char udev_root[PATH_SIZE]; -extern char udev_config_filename[PATH_SIZE]; -extern char udev_rules_dir[PATH_SIZE]; -extern int udev_log_priority; -extern int udev_run; -extern void udev_config_init(void); - -/* udev_device.c */ -extern struct udevice *udev_device_init(struct udevice *udev); -extern void udev_device_cleanup(struct udevice *udev); -extern int udev_device_event(struct udev_rules *rules, struct udevice *udev); -extern dev_t udev_device_get_devt(struct udevice *udev); - -/* udev_sysfs.c */ -extern char sysfs_path[PATH_SIZE]; -extern int sysfs_init(void); -extern void sysfs_cleanup(void); -extern void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, - const char *subsystem, const char *driver); -extern struct sysfs_device *sysfs_device_get(const char *devpath); -extern struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev); -extern struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem); -extern char *sysfs_attr_get_value(const char *devpath, const char *attr_name); -extern int sysfs_resolve_link(char *path, size_t size); -extern int sysfs_lookup_devpath_by_subsys_id(char *devpath, size_t len, const char *subsystem, const char *id); - -/* udev_node.c */ -extern int udev_node_mknod(struct udevice *udev, const char *file, dev_t devt, mode_t mode, uid_t uid, gid_t gid); -extern void udev_node_update_symlinks(struct udevice *udev, struct udevice *udev_old); -extern int udev_node_add(struct udevice *udev); -extern int udev_node_remove(struct udevice *udev); - -/* udev_db.c */ -extern int udev_db_add_device(struct udevice *dev); -extern int udev_db_delete_device(struct udevice *dev); -extern int udev_db_rename(const char *devpath_old, const char *devpath); -extern int udev_db_get_device(struct udevice *udev, const char *devpath); -extern int udev_db_get_devices_by_name(const char *name, struct list_head *name_list); -extern int udev_db_get_all_entries(struct list_head *name_list); - -/* udev_utils.c */ -struct name_entry { - struct list_head node; - char name[PATH_SIZE]; - unsigned int ignore_error:1; -}; - -extern int log_priority(const char *priority); -extern struct name_entry *name_list_add(struct list_head *name_list, const char *name, int sort); -extern struct name_entry *name_list_key_add(struct list_head *name_list, const char *key, const char *value); -extern int name_list_key_remove(struct list_head *name_list, const char *key); -extern void name_list_cleanup(struct list_head *name_list); -extern int add_matching_files(struct list_head *name_list, const char *dirname, const char *suffix); -extern uid_t lookup_user(const char *user); -extern gid_t lookup_group(const char *group); - -/* udev_utils_string.c */ -extern int string_is_true(const char *str); -extern void remove_trailing_chars(char *path, char c); -extern size_t path_encode(char *s, size_t len); -extern size_t path_decode(char *s); -extern int utf8_encoded_valid_unichar(const char *str); -extern int replace_chars(char *str, const char *white); - -/* udev_utils_file.c */ -extern int create_path(const char *path); -extern int delete_path(const char *path); -extern int file_map(const char *filename, char **buf, size_t *bufsize); -extern void file_unmap(void *buf, size_t bufsize); -extern int unlink_secure(const char *filename); -extern size_t buf_get_line(const char *buf, size_t buflen, size_t cur); - -/* udev commands */ -extern int udevmonitor(int argc, char *argv[], char *envp[]); -extern int udevinfo(int argc, char *argv[], char *envp[]); -extern int udevcontrol(int argc, char *argv[], char *envp[]); -extern int udevtrigger(int argc, char *argv[], char *envp[]); -extern int udevsettle(int argc, char *argv[], char *envp[]); -extern int udevtest(int argc, char *argv[], char *envp[]); - -#endif diff --git a/udev.xml b/udev.xml deleted file mode 100644 index c740c0d807..0000000000 --- a/udev.xml +++ /dev/null @@ -1,639 +0,0 @@ - - - -
-
- udev - - - udev - August 2005 - udev - - - - udev - 7 - - - - - udev - dynamic device management - - - DESCRIPTION - udev provides a dynamic device directory containing only the files for - actually present devices. It creates or removes device node files in the - /dev directory, or it renames network interfaces. - - Usually udev runs as udevd - 8 and receives uevents directly from the - kernel if a device is added or removed from the system. - - If udev receives a device event, it matches its configured rules - against the available device attributes provided in sysfs to identify the device. - Rules that match may provide additional device information or specify a device - node name and multiple symlink names and instruct udev to run additional programs - as part of the device event handling. - - - CONFIGURATION - udev configuration files are placed in /etc/udev/ - and /lib/udev/. All empty lines, or lines beginning with - '#' will be ignored. - - Configuration file - udev expects its main configuration file at /etc/udev/udev.conf. - It consists of a set of variables allowing the user to override default udev values. - The following variables can be set: - - - - - Specifies where to place the device nodes in the filesystem. - The default value is /dev. - - - - - - - The logging priority. Valid values are the numerical syslog priorities - or their textual representations: , - and . - - - - - - Rules files - The udev rules are read from the files located in the - default rules directory /lib/udev/rules.d/, - the custom rules directory /etc/udev/rules.d/ - and the temporary rules directory /dev/.udev/rules.d/. - All rule files are sorted and processed in lexical order, regardless - in which of these directories they live. Every line in the rules file contains at least - one key value pair. There are two kind of keys, match and assignment keys. - If all match keys are matching against its value, the rule gets applied and the - assign keys get the specified value assigned. - - A matching rule may specify the name of the device node, add a symlink - pointing to the node, or run a specified program as part of the event handling. - If no matching rule is found, the default device node name is used. - - A rule may consist of a list of one or more key value pairs separated by - a comma. Each key has a distinct operation, depending on the used operator. Valid - operators are: - - - - - Compare for equality. - - - - - - - Compare for non-equality. - - - - - - - Assign a value to a key. Keys that represent a list, are reset - and only this single value is assigned. - - - - - - - Add the value to a key that holds a list of entries. - - - - - - - Assign a value to a key finally; disallow any later changes, - which may be used to prevent changes by any later rules. - - - - - The following key names can be used to match against device properties. - Some of the keys also match against properties of the parent devices in sysfs, - not only the device that has generated the event. If multiple keys that match - a parent device are specified in a single rule, all these keys must match at - one and the same parent device. - - - - - Match the name of the event action. - - - - - - - Match the devpath of the event device. - - - - - - - Match the name of the event device. - - - - - - - Match the name of the node or network interface. It can - be used once the NAME key has been set in one of the preceding - rules. - - - - - - - Match the subsystem of the event device. - - - - - - Match the driver name of the event device. Only set for devices - which are bound to a driver at the time the event is generated. - - - - - - Match sysfs attribute values of the event device. Up to five - keys can be specified per rule. Trailing - whitespace in the attribute values is ignored, if the specified match - value does not contain trailing whitespace itself. Depending on the type - of operator, this key is also used to set the value of a sysfs attribute. - - - - - - - - Search the devpath upwards for a matching device name. - - - - - - - Search the devpath upwards for a matching device subsystem name. - - - - - - - Search the devpath upwards for a matching device driver name. - - - - - - - Search the devpath upwards for a device with matching sysfs attribute values. - Up to five keys can be specified per rule, but all of them - must match on the same device. Trailing whitespace in the attribute values is ignored, - if the specified match value does not contain trailing whitespace itself. - - - - - - - Match against the value of an environment variable. Up to five - keys can be specified per rule. Depending on the type of operator, this key is also used - to export a variable to the environment. - - - - - - - Test the existence of a file. An octal mode mask can be specified - if needed. - - - - - - - Execute external program. The key is true, if the program returns - with exit code zero. The whole event environment is available to the - executed program. The program's output printed to stdout, is available in - the RESULT key. - - - - - - - Match the returned string of the last PROGRAM call. This key can - be used in the same or in any later rule after a PROGRAM call. - - - - - Most of the fields support a shell style pattern matching. The following - pattern characters are supported: - - - - - Matches zero, or any number of characters. - - - - - - Matches any single character. - - - - - - Matches any single character specified within the brackets. For - example, the pattern string 'tty[SR]' would match either 'ttyS' or 'ttyR'. - Ranges are also supported within this match with the '-' character. - For example, to match on the range of all digits, the pattern [0-9] would - be used. If the first character following the '[' is a '!', any characters - not enclosed are matched. - - - - - The following keys can get values assigned: - - - - - The name of the node to be created, or the name the network interface - should be renamed to. Only one rule can set the node name, all later rules with - a NAME key will be ignored. - - - - - - - The name of a symlink targeting the node. Every matching rule can add - this value to the list of symlinks to be created along with the device node. - Multiple symlinks may be specified by separating the names by the space - character. - - - - - - - The permissions for the device node. Every specified value overwrites - the compiled-in default value. - - - - - - - The value that should be written to a sysfs attribute of the - event device. Depending on the type of operator, this key is also - used to match against the value of a sysfs attribute. - - - - - - - Export a variable to the environment. Depending on the type of operator, - this key is also to match against an environment variable. - - - - - - - Add a program to the list of programs to be executed for a specific - device. This can only be used for very short running tasks. Running an - event process for a long period of time may block all further events for - this or a dependent device. Long running tasks need to be immediately - detached from the event process itself. - If the specifiefd string starts with - , all current event - values will be passed to the specified socket, as a message in the same - format the kernel sends an uevent. If the first character of the specified path - is an @ character, an abstract namespace socket is used, instead of an existing - socket file. - - - - - - - Named label where a GOTO can jump to. - - - - - - - Jumps to the next LABEL with a matching name - - - - - - - Import a set of variables into the event environment, - depending on type: - - - - - Execute an external program specified as the assigned value and - import its output, which must be in environment key format. - - - - - - Import a text file specified as the assigned value, which must be in - environment key format. - - - - - - Import the stored keys from the parent device by reading - the database entry of the parent device. The value assigned to - is used as a filter of key names - to import (with the same shell-style pattern matching used for - comparisons). - - - - If no option is given, udev will choose between - and based on the executable bit of the file - permissions. - - - - - - - Wait for a file to become available. - - - - - - - Rule and device options: - - - - - Stops further rules application. No later rules will have - any effect. - - - - - - Ignore this event completely. - - - - - - Do not remove the device node when the device goes away. This may be - useful as a workaround for broken device drivers. - - - - - - Specify the priority of the created symlinks. Devices with higher - priorities overwrite existing symlinks of other devices. The default is 0. - - - - - - Create the device nodes for all available partitions of a block device. - This may be useful for removable media devices where media changes are not - detected. - - - - - - Number of seconds an event will wait for operations to finish, before it - will terminate itself. - - - - - - Usually control and other possibly unsafe characters are replaced - in strings used for device naming. The mode of replacement can be specified - with this option. - - - - - - - - The , , , - , , and - fields support simple printf-like string substitutions. The - format chars gets applied after all rules have been processed, right before the program - is executed. It allows the use of the complete environment set by earlier matching - rules. For all other fields, substitutions are applied while the individual rule is - being processed. The available substitutions are: - - - , - - The kernel name for this device. - - - - - , - - The kernel number for this device. For example, 'sda3' has - kernel number of '3' - - - - - , - - The devpath of the device. - - - - - , - - The name of the device matched while searching the devpath upwards for - , , and . - - - - - - - - The driver name of the device matched while searching the devpath upwards for - , , and . - - - - - - , - - The value of a sysfs attribute found at the device, where - all keys of the rule have matched. If the matching device does not have - such an attribute, follow the chain of parent devices and use the value - of the first attribute that matches. - If the attribute is a symlink, the last element of the symlink target is - returned as the value. - - - - - , - - The value of an environment variable. - - - - - , - - The kernel major number for the device. - - - - - , - - The kernel minor number for the device. - - - - - , - - The string returned by the external program requested with PROGRAM. - A single part of the string, separated by a space character may be selected - by specifying the part number as an attribute: . - If the number is followed by the '+' char this part plus all remaining parts - of the result string are substituted: - - - - - , - - The node name of the parent device. - - - - - - - The current name of the device node. If not changed by a rule, it is the - name of the kernel device. - - - - - - - The current list of symlinks, separated by a space character. The value is - only set if an earlier rule assigned a value, or during a remove events. - - - - - , - - The udev_root value. - - - - - , - - The sysfs mount point. - - - - - , - - The name of a created temporary device node to provide access to - the device from a external program before the real node is created. - - - - - - - The '%' character itself. - - - - - - - The '$' character itself. - - - - The count of characters to be substituted may be limited by specifying - the format length value. For example, '%3s{file}' will only - insert the first three characters of the sysfs attribute - - - - AUTHOR - Written by Greg Kroah-Hartman greg@kroah.com and - Kay Sievers kay.sievers@vrfy.org. With much help from - Dan Stekloff and many others. - - - - SEE ALSO - - udevd8 - , - - udevadm8 - - - -
-
diff --git a/udev/list.h b/udev/list.h new file mode 100644 index 0000000000..8626630f6b --- /dev/null +++ b/udev/list.h @@ -0,0 +1,289 @@ +/* + * Copied from the Linux kernel source tree, version 2.6.0-test1. + * + * Licensed under the GPL v2 as per the whole kernel source tree. + * + */ + +#ifndef _LIST_H +#define _LIST_H + +/** + * container_of - cast a member of a structure out to the containing structure + * + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(struct list_head *head) +{ + return head->next == head; +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +#endif /* _LIST_H */ diff --git a/udev/logging.h b/udev/logging.h new file mode 100644 index 0000000000..6e1d5a20ef --- /dev/null +++ b/udev/logging.h @@ -0,0 +1,73 @@ +/* + * simple logging functions that can be expanded into nothing + * + * Copyright (C) 2003-2004 Greg Kroah-Hartman + * Copyright (C) 2004-2006 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef LOGGING_H +#define LOGGING_H + +#define err(format, arg...) do { } while (0) +#define info(format, arg...) do { } while (0) +#define dbg(format, arg...) do { } while (0) +#define logging_init(foo) do { } while (0) +#define logging_close(foo) do { } while (0) + +#ifdef USE_LOG +#include +#include +#include + +#undef err +#define err(format, arg...) \ + do { \ + log_message(LOG_ERR ,"%s: " format ,__FUNCTION__ ,## arg); \ + } while (0) + +#undef info +#define info(format, arg...) \ + do { \ + log_message(LOG_INFO ,"%s: " format ,__FUNCTION__ ,## arg); \ + } while (0) + +#ifdef DEBUG +#undef dbg +#define dbg(format, arg...) \ + do { \ + log_message(LOG_DEBUG ,"%s: " format ,__FUNCTION__ ,## arg); \ + } while (0) +#endif + +extern void log_message(int priority, const char *format, ...) + __attribute__ ((format (printf, 2, 3))); + +#undef logging_init +static inline void logging_init(const char *program_name) +{ + openlog(program_name, LOG_PID | LOG_CONS, LOG_DAEMON); +} + +#undef logging_close +static inline void logging_close(void) +{ + closelog(); +} + +#endif /* USE_LOG */ + +#endif diff --git a/udev/test-udev.c b/udev/test-udev.c new file mode 100644 index 0000000000..4ac2d5a889 --- /dev/null +++ b/udev/test-udev.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2003-2004 Greg Kroah-Hartman + * Copyright (C) 2004-2006 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udev_rules.h" +#include "udev_selinux.h" + +#ifdef USE_LOG +void log_message(int priority, const char *format, ...) +{ + va_list args; + + if (priority > udev_log_priority) + return; + + va_start(args, format); + vsyslog(priority, format, args); + va_end(args); +} +#endif + +static void asmlinkage sig_handler(int signum) +{ + switch (signum) { + case SIGALRM: + exit(1); + case SIGINT: + case SIGTERM: + exit(20 + signum); + } +} + +int main(int argc, char *argv[], char *envp[]) +{ + struct sysfs_device *dev; + struct udevice *udev; + const char *maj, *min; + struct udev_rules rules; + const char *action; + const char *devpath; + const char *subsystem; + struct sigaction act; + int devnull; + int retval = -EINVAL; + + if (argc == 2 && strcmp(argv[1], "-V") == 0) { + printf("%s\n", UDEV_VERSION); + exit(0); + } + + /* set std fd's to /dev/null, /sbin/hotplug forks us, we don't have them at all */ + devnull = open("/dev/null", O_RDWR); + if (devnull >= 0) { + if (devnull != STDIN_FILENO) + dup2(devnull, STDIN_FILENO); + if (devnull != STDOUT_FILENO) + dup2(devnull, STDOUT_FILENO); + if (devnull != STDERR_FILENO) + dup2(devnull, STDERR_FILENO); + if (devnull > STDERR_FILENO) + close(devnull); + } + + logging_init("udev"); + if (devnull < 0) + err("open /dev/null failed: %s\n", strerror(errno)); + udev_config_init(); + selinux_init(); + dbg("version %s\n", UDEV_VERSION); + + /* set signal handlers */ + memset(&act, 0x00, sizeof(act)); + act.sa_handler = (void (*)(int)) sig_handler; + sigemptyset (&act.sa_mask); + act.sa_flags = 0; + sigaction(SIGALRM, &act, NULL); + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + + /* trigger timeout to prevent hanging processes */ + alarm(UDEV_EVENT_TIMEOUT); + + action = getenv("ACTION"); + devpath = getenv("DEVPATH"); + subsystem = getenv("SUBSYSTEM"); + /* older kernels passed the SUBSYSTEM only as argument */ + if (subsystem == NULL && argc == 2) + subsystem = argv[1]; + + if (action == NULL || subsystem == NULL || devpath == NULL) { + err("action, subsystem or devpath missing\n"); + goto exit; + } + + /* export log_priority , as called programs may want to do the same as udev */ + if (udev_log_priority) { + char priority[32]; + + sprintf(priority, "%i", udev_log_priority); + setenv("UDEV_LOG", priority, 1); + } + + sysfs_init(); + udev_rules_init(&rules, 0); + + dev = sysfs_device_get(devpath); + if (dev == NULL) { + info("unable to open '%s'\n", devpath); + goto fail; + } + + udev = udev_device_init(NULL); + if (udev == NULL) + goto fail; + + /* override built-in sysfs device */ + udev->dev = dev; + strlcpy(udev->action, action, sizeof(udev->action)); + + /* get dev_t from environment, which is needed for "remove" to work, "add" works also from sysfs */ + maj = getenv("MAJOR"); + min = getenv("MINOR"); + if (maj != NULL && min != NULL) + udev->devt = makedev(atoi(maj), atoi(min)); + else + udev->devt = udev_device_get_devt(udev); + + retval = udev_device_event(&rules, udev); + + /* rules may change/disable the timeout */ + if (udev->event_timeout >= 0) + alarm(udev->event_timeout); + + if (retval == 0 && !udev->ignore_device && udev_run) + udev_rules_run(udev); + + udev_device_cleanup(udev); +fail: + udev_rules_cleanup(&rules); + sysfs_cleanup(); + selinux_exit(); + +exit: + logging_close(); + if (retval != 0) + return 1; + return 0; +} diff --git a/udev/udev.h b/udev/udev.h new file mode 100644 index 0000000000..5ecef4abbd --- /dev/null +++ b/udev/udev.h @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2003 Greg Kroah-Hartman + * Copyright (C) 2003-2006 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _UDEV_H_ +#define _UDEV_H_ + +#include +#include + +#include "list.h" +#include "logging.h" +#include "udev_sysdeps.h" +#include "udev_version.h" + +#define COMMENT_CHARACTER '#' +#define LINE_SIZE 512 +#define PATH_SIZE 512 +#define NAME_SIZE 256 +#define VALUE_SIZE 128 + +#define ALLOWED_CHARS "#+-.:=@_" +#define ALLOWED_CHARS_FILE ALLOWED_CHARS "/" +#define ALLOWED_CHARS_INPUT ALLOWED_CHARS_FILE " $%?," + +#define DEFAULT_PARTITIONS_COUNT 15 +#define UDEV_EVENT_TIMEOUT 180 + +#define UDEV_MAX(a,b) ((a) > (b) ? (a) : (b)) + +/* pipes */ +#define READ_END 0 +#define WRITE_END 1 + +#define UDEV_ROOT "/dev" +#define DB_DIR ".udev/db" +#define DB_NAME_INDEX_DIR ".udev/names" +#define RULES_LIB_DIR "/lib/udev/rules.d" +#define RULES_DYN_DIR ".udev/rules.d" +#define RULES_ETC_DIR "/etc/udev/rules.d" + +struct udev_rules; + +struct sysfs_device { + struct list_head node; /* for device cache */ + struct sysfs_device *parent; /* already cached parent*/ + char devpath[PATH_SIZE]; + char subsystem[NAME_SIZE]; /* $class, $bus, drivers, module */ + char kernel[NAME_SIZE]; /* device instance name */ + char kernel_number[NAME_SIZE]; + char driver[NAME_SIZE]; /* device driver name */ +}; + +struct udevice { + /* device event */ + struct sysfs_device *dev; /* points to dev_local by default */ + struct sysfs_device dev_local; + struct sysfs_device *dev_parent; /* current parent device used for matching */ + char action[NAME_SIZE]; + char *devpath_old; + + /* node */ + char name[PATH_SIZE]; + struct list_head symlink_list; + int symlink_final; + char owner[NAME_SIZE]; + int owner_final; + char group[NAME_SIZE]; + int group_final; + mode_t mode; + int mode_final; + dev_t devt; + + /* event processing */ + struct list_head run_list; + int run_final; + struct list_head env_list; + char tmp_node[PATH_SIZE]; + int partitions; + int ignore_device; + int ignore_remove; + char program_result[PATH_SIZE]; + int link_priority; + int event_timeout; + int test_run; +}; + +/* udev_config.c */ +extern char udev_root[PATH_SIZE]; +extern char udev_config_filename[PATH_SIZE]; +extern char udev_rules_dir[PATH_SIZE]; +extern int udev_log_priority; +extern int udev_run; +extern void udev_config_init(void); + +/* udev_device.c */ +extern struct udevice *udev_device_init(struct udevice *udev); +extern void udev_device_cleanup(struct udevice *udev); +extern int udev_device_event(struct udev_rules *rules, struct udevice *udev); +extern dev_t udev_device_get_devt(struct udevice *udev); + +/* udev_sysfs.c */ +extern char sysfs_path[PATH_SIZE]; +extern int sysfs_init(void); +extern void sysfs_cleanup(void); +extern void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, + const char *subsystem, const char *driver); +extern struct sysfs_device *sysfs_device_get(const char *devpath); +extern struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev); +extern struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem); +extern char *sysfs_attr_get_value(const char *devpath, const char *attr_name); +extern int sysfs_resolve_link(char *path, size_t size); +extern int sysfs_lookup_devpath_by_subsys_id(char *devpath, size_t len, const char *subsystem, const char *id); + +/* udev_node.c */ +extern int udev_node_mknod(struct udevice *udev, const char *file, dev_t devt, mode_t mode, uid_t uid, gid_t gid); +extern void udev_node_update_symlinks(struct udevice *udev, struct udevice *udev_old); +extern int udev_node_add(struct udevice *udev); +extern int udev_node_remove(struct udevice *udev); + +/* udev_db.c */ +extern int udev_db_add_device(struct udevice *dev); +extern int udev_db_delete_device(struct udevice *dev); +extern int udev_db_rename(const char *devpath_old, const char *devpath); +extern int udev_db_get_device(struct udevice *udev, const char *devpath); +extern int udev_db_get_devices_by_name(const char *name, struct list_head *name_list); +extern int udev_db_get_all_entries(struct list_head *name_list); + +/* udev_utils.c */ +struct name_entry { + struct list_head node; + char name[PATH_SIZE]; + unsigned int ignore_error:1; +}; + +extern int log_priority(const char *priority); +extern struct name_entry *name_list_add(struct list_head *name_list, const char *name, int sort); +extern struct name_entry *name_list_key_add(struct list_head *name_list, const char *key, const char *value); +extern int name_list_key_remove(struct list_head *name_list, const char *key); +extern void name_list_cleanup(struct list_head *name_list); +extern int add_matching_files(struct list_head *name_list, const char *dirname, const char *suffix); +extern uid_t lookup_user(const char *user); +extern gid_t lookup_group(const char *group); + +/* udev_utils_string.c */ +extern int string_is_true(const char *str); +extern void remove_trailing_chars(char *path, char c); +extern size_t path_encode(char *s, size_t len); +extern size_t path_decode(char *s); +extern int utf8_encoded_valid_unichar(const char *str); +extern int replace_chars(char *str, const char *white); + +/* udev_utils_file.c */ +extern int create_path(const char *path); +extern int delete_path(const char *path); +extern int file_map(const char *filename, char **buf, size_t *bufsize); +extern void file_unmap(void *buf, size_t bufsize); +extern int unlink_secure(const char *filename); +extern size_t buf_get_line(const char *buf, size_t buflen, size_t cur); + +/* udev commands */ +extern int udevmonitor(int argc, char *argv[], char *envp[]); +extern int udevinfo(int argc, char *argv[], char *envp[]); +extern int udevcontrol(int argc, char *argv[], char *envp[]); +extern int udevtrigger(int argc, char *argv[], char *envp[]); +extern int udevsettle(int argc, char *argv[], char *envp[]); +extern int udevtest(int argc, char *argv[], char *envp[]); + +#endif diff --git a/udev/udev.xml b/udev/udev.xml new file mode 100644 index 0000000000..c740c0d807 --- /dev/null +++ b/udev/udev.xml @@ -0,0 +1,639 @@ + + + +
+
+ udev + + + udev + August 2005 + udev + + + + udev + 7 + + + + + udev + dynamic device management + + + DESCRIPTION + udev provides a dynamic device directory containing only the files for + actually present devices. It creates or removes device node files in the + /dev directory, or it renames network interfaces. + + Usually udev runs as udevd + 8 and receives uevents directly from the + kernel if a device is added or removed from the system. + + If udev receives a device event, it matches its configured rules + against the available device attributes provided in sysfs to identify the device. + Rules that match may provide additional device information or specify a device + node name and multiple symlink names and instruct udev to run additional programs + as part of the device event handling. + + + CONFIGURATION + udev configuration files are placed in /etc/udev/ + and /lib/udev/. All empty lines, or lines beginning with + '#' will be ignored. + + Configuration file + udev expects its main configuration file at /etc/udev/udev.conf. + It consists of a set of variables allowing the user to override default udev values. + The following variables can be set: + + + + + Specifies where to place the device nodes in the filesystem. + The default value is /dev. + + + + + + + The logging priority. Valid values are the numerical syslog priorities + or their textual representations: , + and . + + + + + + Rules files + The udev rules are read from the files located in the + default rules directory /lib/udev/rules.d/, + the custom rules directory /etc/udev/rules.d/ + and the temporary rules directory /dev/.udev/rules.d/. + All rule files are sorted and processed in lexical order, regardless + in which of these directories they live. Every line in the rules file contains at least + one key value pair. There are two kind of keys, match and assignment keys. + If all match keys are matching against its value, the rule gets applied and the + assign keys get the specified value assigned. + + A matching rule may specify the name of the device node, add a symlink + pointing to the node, or run a specified program as part of the event handling. + If no matching rule is found, the default device node name is used. + + A rule may consist of a list of one or more key value pairs separated by + a comma. Each key has a distinct operation, depending on the used operator. Valid + operators are: + + + + + Compare for equality. + + + + + + + Compare for non-equality. + + + + + + + Assign a value to a key. Keys that represent a list, are reset + and only this single value is assigned. + + + + + + + Add the value to a key that holds a list of entries. + + + + + + + Assign a value to a key finally; disallow any later changes, + which may be used to prevent changes by any later rules. + + + + + The following key names can be used to match against device properties. + Some of the keys also match against properties of the parent devices in sysfs, + not only the device that has generated the event. If multiple keys that match + a parent device are specified in a single rule, all these keys must match at + one and the same parent device. + + + + + Match the name of the event action. + + + + + + + Match the devpath of the event device. + + + + + + + Match the name of the event device. + + + + + + + Match the name of the node or network interface. It can + be used once the NAME key has been set in one of the preceding + rules. + + + + + + + Match the subsystem of the event device. + + + + + + Match the driver name of the event device. Only set for devices + which are bound to a driver at the time the event is generated. + + + + + + Match sysfs attribute values of the event device. Up to five + keys can be specified per rule. Trailing + whitespace in the attribute values is ignored, if the specified match + value does not contain trailing whitespace itself. Depending on the type + of operator, this key is also used to set the value of a sysfs attribute. + + + + + + + + Search the devpath upwards for a matching device name. + + + + + + + Search the devpath upwards for a matching device subsystem name. + + + + + + + Search the devpath upwards for a matching device driver name. + + + + + + + Search the devpath upwards for a device with matching sysfs attribute values. + Up to five keys can be specified per rule, but all of them + must match on the same device. Trailing whitespace in the attribute values is ignored, + if the specified match value does not contain trailing whitespace itself. + + + + + + + Match against the value of an environment variable. Up to five + keys can be specified per rule. Depending on the type of operator, this key is also used + to export a variable to the environment. + + + + + + + Test the existence of a file. An octal mode mask can be specified + if needed. + + + + + + + Execute external program. The key is true, if the program returns + with exit code zero. The whole event environment is available to the + executed program. The program's output printed to stdout, is available in + the RESULT key. + + + + + + + Match the returned string of the last PROGRAM call. This key can + be used in the same or in any later rule after a PROGRAM call. + + + + + Most of the fields support a shell style pattern matching. The following + pattern characters are supported: + + + + + Matches zero, or any number of characters. + + + + + + Matches any single character. + + + + + + Matches any single character specified within the brackets. For + example, the pattern string 'tty[SR]' would match either 'ttyS' or 'ttyR'. + Ranges are also supported within this match with the '-' character. + For example, to match on the range of all digits, the pattern [0-9] would + be used. If the first character following the '[' is a '!', any characters + not enclosed are matched. + + + + + The following keys can get values assigned: + + + + + The name of the node to be created, or the name the network interface + should be renamed to. Only one rule can set the node name, all later rules with + a NAME key will be ignored. + + + + + + + The name of a symlink targeting the node. Every matching rule can add + this value to the list of symlinks to be created along with the device node. + Multiple symlinks may be specified by separating the names by the space + character. + + + + + + + The permissions for the device node. Every specified value overwrites + the compiled-in default value. + + + + + + + The value that should be written to a sysfs attribute of the + event device. Depending on the type of operator, this key is also + used to match against the value of a sysfs attribute. + + + + + + + Export a variable to the environment. Depending on the type of operator, + this key is also to match against an environment variable. + + + + + + + Add a program to the list of programs to be executed for a specific + device. This can only be used for very short running tasks. Running an + event process for a long period of time may block all further events for + this or a dependent device. Long running tasks need to be immediately + detached from the event process itself. + If the specifiefd string starts with + , all current event + values will be passed to the specified socket, as a message in the same + format the kernel sends an uevent. If the first character of the specified path + is an @ character, an abstract namespace socket is used, instead of an existing + socket file. + + + + + + + Named label where a GOTO can jump to. + + + + + + + Jumps to the next LABEL with a matching name + + + + + + + Import a set of variables into the event environment, + depending on type: + + + + + Execute an external program specified as the assigned value and + import its output, which must be in environment key format. + + + + + + Import a text file specified as the assigned value, which must be in + environment key format. + + + + + + Import the stored keys from the parent device by reading + the database entry of the parent device. The value assigned to + is used as a filter of key names + to import (with the same shell-style pattern matching used for + comparisons). + + + + If no option is given, udev will choose between + and based on the executable bit of the file + permissions. + + + + + + + Wait for a file to become available. + + + + + + + Rule and device options: + + + + + Stops further rules application. No later rules will have + any effect. + + + + + + Ignore this event completely. + + + + + + Do not remove the device node when the device goes away. This may be + useful as a workaround for broken device drivers. + + + + + + Specify the priority of the created symlinks. Devices with higher + priorities overwrite existing symlinks of other devices. The default is 0. + + + + + + Create the device nodes for all available partitions of a block device. + This may be useful for removable media devices where media changes are not + detected. + + + + + + Number of seconds an event will wait for operations to finish, before it + will terminate itself. + + + + + + Usually control and other possibly unsafe characters are replaced + in strings used for device naming. The mode of replacement can be specified + with this option. + + + + + + + + The , , , + , , and + fields support simple printf-like string substitutions. The + format chars gets applied after all rules have been processed, right before the program + is executed. It allows the use of the complete environment set by earlier matching + rules. For all other fields, substitutions are applied while the individual rule is + being processed. The available substitutions are: + + + , + + The kernel name for this device. + + + + + , + + The kernel number for this device. For example, 'sda3' has + kernel number of '3' + + + + + , + + The devpath of the device. + + + + + , + + The name of the device matched while searching the devpath upwards for + , , and . + + + + + + + + The driver name of the device matched while searching the devpath upwards for + , , and . + + + + + + , + + The value of a sysfs attribute found at the device, where + all keys of the rule have matched. If the matching device does not have + such an attribute, follow the chain of parent devices and use the value + of the first attribute that matches. + If the attribute is a symlink, the last element of the symlink target is + returned as the value. + + + + + , + + The value of an environment variable. + + + + + , + + The kernel major number for the device. + + + + + , + + The kernel minor number for the device. + + + + + , + + The string returned by the external program requested with PROGRAM. + A single part of the string, separated by a space character may be selected + by specifying the part number as an attribute: . + If the number is followed by the '+' char this part plus all remaining parts + of the result string are substituted: + + + + + , + + The node name of the parent device. + + + + + + + The current name of the device node. If not changed by a rule, it is the + name of the kernel device. + + + + + + + The current list of symlinks, separated by a space character. The value is + only set if an earlier rule assigned a value, or during a remove events. + + + + + , + + The udev_root value. + + + + + , + + The sysfs mount point. + + + + + , + + The name of a created temporary device node to provide access to + the device from a external program before the real node is created. + + + + + + + The '%' character itself. + + + + + + + The '$' character itself. + + + + The count of characters to be substituted may be limited by specifying + the format length value. For example, '%3s{file}' will only + insert the first three characters of the sysfs attribute + + + + AUTHOR + Written by Greg Kroah-Hartman greg@kroah.com and + Kay Sievers kay.sievers@vrfy.org. With much help from + Dan Stekloff and many others. + + + + SEE ALSO + + udevd8 + , + + udevadm8 + + + +
+
diff --git a/udev/udev_config.c b/udev/udev_config.c new file mode 100644 index 0000000000..55f0361dd2 --- /dev/null +++ b/udev/udev_config.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2003-2004 Greg Kroah-Hartman + * Copyright (C) 2004-2005 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +/* global variables */ +char udev_root[PATH_SIZE]; +char udev_config_filename[PATH_SIZE]; +char udev_rules_dir[PATH_SIZE]; +int udev_log_priority; +int udev_run; + +static int get_key(char **line, char **key, char **value) +{ + char *linepos; + char *temp; + + linepos = *line; + if (!linepos) + return -1; + + /* skip whitespace */ + while (isspace(linepos[0])) + linepos++; + + /* get the key */ + *key = linepos; + while (1) { + linepos++; + if (linepos[0] == '\0') + return -1; + if (isspace(linepos[0])) + break; + if (linepos[0] == '=') + break; + } + + /* terminate key */ + linepos[0] = '\0'; + linepos++; + + /* skip whitespace */ + while (isspace(linepos[0])) + linepos++; + + /* get the value*/ + if (linepos[0] == '"') + linepos++; + else + return -1; + *value = linepos; + + temp = strchr(linepos, '"'); + if (!temp) + return -1; + temp[0] = '\0'; + + return 0; +} + +static int parse_config_file(void) +{ + char line[LINE_SIZE]; + char *bufline; + char *linepos; + char *variable; + char *value; + char *buf; + size_t bufsize; + size_t cur; + size_t count; + int lineno; + int retval = 0; + + if (file_map(udev_config_filename, &buf, &bufsize) != 0) { + err("can't open '%s' as config file: %s\n", udev_config_filename, strerror(errno)); + return -ENODEV; + } + + /* loop through the whole file */ + lineno = 0; + cur = 0; + while (cur < bufsize) { + count = buf_get_line(buf, bufsize, cur); + bufline = &buf[cur]; + cur += count+1; + lineno++; + + /* eat the whitespace */ + while ((count > 0) && isspace(bufline[0])) { + bufline++; + count--; + } + if (count == 0) + continue; + + /* see if this is a comment */ + if (bufline[0] == COMMENT_CHARACTER) + continue; + + if (count >= sizeof(line)) { + err("line too long, conf line skipped %s, line %d\n", udev_config_filename, lineno); + continue; + } + + memcpy(line, bufline, count); + line[count] = '\0'; + + linepos = line; + retval = get_key(&linepos, &variable, &value); + if (retval != 0) { + err("error parsing %s, line %d:%d\n", udev_config_filename, lineno, (int)(linepos-line)); + continue; + } + + if (strcasecmp(variable, "udev_root") == 0) { + strlcpy(udev_root, value, sizeof(udev_root)); + remove_trailing_chars(udev_root, '/'); + continue; + } + + if (strcasecmp(variable, "udev_rules") == 0) { + strlcpy(udev_rules_dir, value, sizeof(udev_rules_dir)); + remove_trailing_chars(udev_rules_dir, '/'); + continue; + } + + if (strcasecmp(variable, "udev_log") == 0) { + udev_log_priority = log_priority(value); + continue; + } + } + + file_unmap(buf, bufsize); + return retval; +} + +void udev_config_init(void) +{ + const char *env; + + strcpy(udev_config_filename, UDEV_CONFIG_FILE); + strcpy(udev_root, UDEV_ROOT); + udev_rules_dir[0] = '\0'; + udev_log_priority = LOG_ERR; + udev_run = 1; + + /* disable RUN key execution */ + env = getenv("UDEV_RUN"); + if (env && !string_is_true(env)) + udev_run = 0; + + env = getenv("UDEV_CONFIG_FILE"); + if (env) { + strlcpy(udev_config_filename, env, sizeof(udev_config_filename)); + remove_trailing_chars(udev_config_filename, '/'); + } + + parse_config_file(); + + env = getenv("UDEV_ROOT"); + if (env) { + strlcpy(udev_root, env, sizeof(udev_root)); + remove_trailing_chars(udev_root, '/'); + } + + env = getenv("UDEV_LOG"); + if (env) + udev_log_priority = log_priority(env); + + dbg("UDEV_CONFIG_FILE='%s'\n", udev_config_filename); + dbg("udev_root='%s'\n", udev_root); + dbg("udev_rules_dir='%s'\n", udev_rules_dir); + dbg("udev_log=%d\n", udev_log_priority); +} diff --git a/udev/udev_db.c b/udev/udev_db.c new file mode 100644 index 0000000000..3348c9a02c --- /dev/null +++ b/udev/udev_db.c @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2003 Greg Kroah-Hartman + * Copyright (C) 2004-2005 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udev_selinux.h" + + +static size_t devpath_to_db_path(const char *devpath, char *filename, size_t len) +{ + size_t start; + + /* translate to location of db file */ + strlcpy(filename, udev_root, len); + start = strlcat(filename, "/"DB_DIR"/", len); + strlcat(filename, devpath, len); + return path_encode(&filename[start], len - start); +} + +/* reverse mapping from the device file name to the devpath */ +static int name_index(const char *devpath, const char *name, int add) +{ + char device[PATH_SIZE]; + char filename[PATH_SIZE * 2]; + size_t start; + int fd; + + /* directory with device name */ + strlcpy(filename, udev_root, sizeof(filename)); + start = strlcat(filename, "/"DB_NAME_INDEX_DIR"/", sizeof(filename)); + strlcat(filename, name, sizeof(filename)); + path_encode(&filename[start], sizeof(filename) - start); + /* entry with the devpath */ + strlcpy(device, devpath, sizeof(device)); + path_encode(device, sizeof(device)); + strlcat(filename, "/", sizeof(filename)); + strlcat(filename, device, sizeof(filename)); + + if (add) { + info("creating index: '%s'\n", filename); + create_path(filename); + fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644); + if (fd > 0) + close(fd); + } else { + info("removing index: '%s'\n", filename); + unlink(filename); + delete_path(filename); + } + return 0; +} + +int udev_db_get_devices_by_name(const char *name, struct list_head *name_list) +{ + char dirname[PATH_MAX]; + size_t start; + DIR *dir; + int rc = 0; + + strlcpy(dirname, udev_root, sizeof(dirname)); + start = strlcat(dirname, "/"DB_NAME_INDEX_DIR"/", sizeof(dirname)); + strlcat(dirname, name, sizeof(dirname)); + path_encode(&dirname[start], sizeof(dirname) - start); + + dir = opendir(dirname); + if (dir == NULL) { + info("no index directory '%s': %s\n", dirname, strerror(errno)); + rc = -1; + goto out; + } + + info("found index directory '%s'\n", dirname); + while (1) { + struct dirent *ent; + char device[PATH_SIZE]; + + ent = readdir(dir); + if (ent == NULL || ent->d_name[0] == '\0') + break; + if (ent->d_name[0] == '.') + continue; + + strlcpy(device, ent->d_name, sizeof(device)); + path_decode(device); + name_list_add(name_list, device, 0); + rc++; + } + closedir(dir); +out: + return rc; +} + +int udev_db_rename(const char *devpath_old, const char *devpath) +{ + char filename[PATH_SIZE]; + char filename_old[PATH_SIZE]; + + devpath_to_db_path(devpath_old, filename_old, sizeof(filename_old)); + devpath_to_db_path(devpath, filename, sizeof(filename)); + return rename(filename_old, filename); +} + +int udev_db_add_device(struct udevice *udev) +{ + char filename[PATH_SIZE]; + + if (udev->test_run) + return 0; + + devpath_to_db_path(udev->dev->devpath, filename, sizeof(filename)); + create_path(filename); + unlink(filename); + + /* + * don't waste tmpfs memory pages, if we don't have any data to store + * create fake db-file; store the node-name in a symlink target + */ + if (list_empty(&udev->symlink_list) && list_empty(&udev->env_list) && + !udev->partitions && !udev->ignore_remove) { + int ret; + dbg("nothing interesting to store, create symlink\n"); + selinux_setfscreatecon(filename, NULL, S_IFLNK); + ret = symlink(udev->name, filename); + selinux_resetfscreatecon(); + if (ret != 0) { + err("unable to create db link '%s': %s\n", filename, strerror(errno)); + return -1; + } + } else { + FILE *f; + struct name_entry *name_loop; + + f = fopen(filename, "w"); + if (f == NULL) { + err("unable to create db file '%s': %s\n", filename, strerror(errno)); + return -1; + } + dbg("storing data for device '%s' in '%s'\n", udev->dev->devpath, filename); + + fprintf(f, "N:%s\n", udev->name); + list_for_each_entry(name_loop, &udev->symlink_list, node) { + fprintf(f, "S:%s\n", name_loop->name); + /* add symlink-name to index */ + name_index(udev->dev->devpath, name_loop->name, 1); + } + fprintf(f, "M:%u:%u\n", major(udev->devt), minor(udev->devt)); + if (udev->link_priority != 0) + fprintf(f, "L:%u\n", udev->link_priority); + if (udev->event_timeout >= 0) + fprintf(f, "T:%u\n", udev->event_timeout); + if (udev->partitions != 0) + fprintf(f, "A:%u\n", udev->partitions); + if (udev->ignore_remove) + fprintf(f, "R:%u\n", udev->ignore_remove); + list_for_each_entry(name_loop, &udev->env_list, node) + fprintf(f, "E:%s\n", name_loop->name); + fclose(f); + } + + /* add name to index */ + name_index(udev->dev->devpath, udev->name, 1); + + return 0; +} + +int udev_db_get_device(struct udevice *udev, const char *devpath) +{ + struct stat stats; + char filename[PATH_SIZE]; + char line[PATH_SIZE]; + unsigned int maj, min; + char *bufline; + char *buf; + size_t bufsize; + size_t cur; + size_t count; + + sysfs_device_set_values(udev->dev, devpath, NULL, NULL); + devpath_to_db_path(devpath, filename, sizeof(filename)); + + if (lstat(filename, &stats) != 0) { + info("no db file to read %s: %s\n", filename, strerror(errno)); + return -1; + } + if ((stats.st_mode & S_IFMT) == S_IFLNK) { + char target[NAME_SIZE]; + int target_len; + + info("found a symlink as db file\n"); + target_len = readlink(filename, target, sizeof(target)); + if (target_len > 0) + target[target_len] = '\0'; + else { + info("error reading db link %s: %s\n", filename, strerror(errno)); + return -1; + } + dbg("db link points to '%s'\n", target); + strlcpy(udev->name, target, sizeof(udev->name)); + return 0; + } + + if (file_map(filename, &buf, &bufsize) != 0) { + info("error reading db file %s: %s\n", filename, strerror(errno)); + return -1; + } + + cur = 0; + while (cur < bufsize) { + count = buf_get_line(buf, bufsize, cur); + bufline = &buf[cur]; + cur += count+1; + + if (count > sizeof(line)) + count = sizeof(line); + memcpy(line, &bufline[2], count-2); + line[count-2] = '\0'; + + switch(bufline[0]) { + case 'N': + strlcpy(udev->name, line, sizeof(udev->name)); + break; + case 'M': + sscanf(line, "%u:%u", &maj, &min); + udev->devt = makedev(maj, min); + break; + case 'S': + name_list_add(&udev->symlink_list, line, 0); + break; + case 'L': + udev->link_priority = atoi(line); + break; + case 'T': + udev->event_timeout = atoi(line); + break; + case 'A': + udev->partitions = atoi(line); + break; + case 'R': + udev->ignore_remove = atoi(line); + break; + case 'E': + name_list_add(&udev->env_list, line, 0); + break; + } + } + file_unmap(buf, bufsize); + + if (udev->name[0] == '\0') + return -1; + + return 0; +} + +int udev_db_delete_device(struct udevice *udev) +{ + char filename[PATH_SIZE]; + struct name_entry *name_loop; + + if (udev->test_run) + return 0; + + devpath_to_db_path(udev->dev->devpath, filename, sizeof(filename)); + unlink(filename); + + name_index(udev->dev->devpath, udev->name, 0); + list_for_each_entry(name_loop, &udev->symlink_list, node) + name_index(udev->dev->devpath, name_loop->name, 0); + + return 0; +} + +int udev_db_get_all_entries(struct list_head *name_list) +{ + char dbpath[PATH_MAX]; + DIR *dir; + + strlcpy(dbpath, udev_root, sizeof(dbpath)); + strlcat(dbpath, "/"DB_DIR, sizeof(dbpath)); + dir = opendir(dbpath); + if (dir == NULL) { + info("no udev_db available '%s': %s\n", dbpath, strerror(errno)); + return -1; + } + + while (1) { + struct dirent *ent; + char device[PATH_SIZE]; + + ent = readdir(dir); + if (ent == NULL || ent->d_name[0] == '\0') + break; + if (ent->d_name[0] == '.') + continue; + + strlcpy(device, ent->d_name, sizeof(device)); + path_decode(device); + name_list_add(name_list, device, 1); + dbg("added '%s'\n", device); + } + + closedir(dir); + return 0; +} diff --git a/udev/udev_device.c b/udev/udev_device.c new file mode 100644 index 0000000000..cf21191ca0 --- /dev/null +++ b/udev/udev_device.c @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2004-2006 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udev_rules.h" + + +struct udevice *udev_device_init(struct udevice *udev) +{ + if (udev == NULL) + udev = malloc(sizeof(struct udevice)); + if (udev == NULL) + return NULL; + memset(udev, 0x00, sizeof(struct udevice)); + + INIT_LIST_HEAD(&udev->symlink_list); + INIT_LIST_HEAD(&udev->run_list); + INIT_LIST_HEAD(&udev->env_list); + + /* set sysfs device to local storage, can be overridden if needed */ + udev->dev = &udev->dev_local; + + /* default node permissions */ + udev->mode = 0660; + strcpy(udev->owner, "root"); + strcpy(udev->group, "root"); + + udev->event_timeout = -1; + + return udev; +} + +void udev_device_cleanup(struct udevice *udev) +{ + name_list_cleanup(&udev->symlink_list); + name_list_cleanup(&udev->run_list); + name_list_cleanup(&udev->env_list); + free(udev); +} + +dev_t udev_device_get_devt(struct udevice *udev) +{ + const char *attr; + unsigned int maj, min; + + /* read it from sysfs */ + attr = sysfs_attr_get_value(udev->dev->devpath, "dev"); + if (attr != NULL) { + if (sscanf(attr, "%u:%u", &maj, &min) == 2) + return makedev(maj, min); + } + return makedev(0, 0); +} + +static void kernel_log(struct ifreq ifr) +{ + int klog; + FILE *f; + + klog = open("/dev/kmsg", O_WRONLY); + if (klog < 0) + return; + + f = fdopen(klog, "w"); + if (f == NULL) { + close(klog); + return; + } + + fprintf(f, "<6>udev: renamed network interface %s to %s\n", + ifr.ifr_name, ifr.ifr_newname); + fclose(f); +} + +static int rename_netif(struct udevice *udev) +{ + int sk; + struct ifreq ifr; + int retval; + + info("changing net interface name from '%s' to '%s'\n", udev->dev->kernel, udev->name); + if (udev->test_run) + return 0; + + sk = socket(PF_INET, SOCK_DGRAM, 0); + if (sk < 0) { + err("error opening socket: %s\n", strerror(errno)); + return -1; + } + + memset(&ifr, 0x00, sizeof(struct ifreq)); + strlcpy(ifr.ifr_name, udev->dev->kernel, IFNAMSIZ); + strlcpy(ifr.ifr_newname, udev->name, IFNAMSIZ); + retval = ioctl(sk, SIOCSIFNAME, &ifr); + if (retval == 0) + kernel_log(ifr); + else { + int loop; + + /* see if the destination interface name already exists */ + if (errno != EEXIST) { + err("error changing netif name %s to %s: %s\n", ifr.ifr_name, ifr.ifr_newname, strerror(errno)); + goto exit; + } + + /* free our own name, another process may wait for us */ + strlcpy(ifr.ifr_newname, udev->dev->kernel, IFNAMSIZ); + strlcat(ifr.ifr_newname, "_rename", IFNAMSIZ); + retval = ioctl(sk, SIOCSIFNAME, &ifr); + if (retval != 0) { + err("error changing netif name %s to %s: %s\n", ifr.ifr_name, ifr.ifr_newname, strerror(errno)); + goto exit; + } + + /* wait 30 seconds for our target to become available */ + strlcpy(ifr.ifr_name, ifr.ifr_newname, IFNAMSIZ); + strlcpy(ifr.ifr_newname, udev->name, IFNAMSIZ); + loop = 30 * 20; + while (loop--) { + retval = ioctl(sk, SIOCSIFNAME, &ifr); + if (retval == 0) { + kernel_log(ifr); + break; + } + + if (errno != EEXIST) { + err("error changing net interface name %s to %s: %s\n", + ifr.ifr_name, ifr.ifr_newname, strerror(errno)); + break; + } + dbg("wait for netif '%s' to become free, loop=%i\n", udev->name, (30 * 20) - loop); + usleep(1000 * 1000 / 20); + } + } + +exit: + close(sk); + return retval; +} + +int udev_device_event(struct udev_rules *rules, struct udevice *udev) +{ + int retval = 0; + + if (udev->devpath_old != NULL) + if (udev_db_rename(udev->devpath_old, udev->dev->devpath) == 0) + info("moved database from '%s' to '%s'\n", udev->devpath_old, udev->dev->devpath); + + /* add device node */ + if (major(udev->devt) != 0 && + (strcmp(udev->action, "add") == 0 || strcmp(udev->action, "change") == 0)) { + struct udevice *udev_old; + + dbg("device node add '%s'\n", udev->dev->devpath); + + udev_rules_get_name(rules, udev); + if (udev->ignore_device) { + info("device event will be ignored\n"); + goto exit; + } + if (udev->name[0] == '\0') { + info("device node creation supressed\n"); + goto exit; + } + + /* read current database entry; cleanup, if it is known device */ + udev_old = udev_device_init(NULL); + if (udev_old != NULL) { + udev_old->test_run = udev->test_run; + if (udev_db_get_device(udev_old, udev->dev->devpath) == 0) { + info("device '%s' already in database, cleanup\n", udev->dev->devpath); + udev_db_delete_device(udev_old); + } else { + udev_device_cleanup(udev_old); + udev_old = NULL; + } + } + + /* create node */ + retval = udev_node_add(udev); + if (retval != 0) + goto exit; + + /* store in database */ + udev_db_add_device(udev); + + /* create, replace, delete symlinks according to priority */ + udev_node_update_symlinks(udev, udev_old); + + if (udev_old != NULL) + udev_device_cleanup(udev_old); + goto exit; + } + + /* add netif */ + if (strcmp(udev->dev->subsystem, "net") == 0 && strcmp(udev->action, "add") == 0) { + dbg("netif add '%s'\n", udev->dev->devpath); + udev_rules_get_name(rules, udev); + if (udev->ignore_device) { + info("device event will be ignored\n"); + goto exit; + } + if (udev->name[0] == '\0') { + info("device renaming supressed\n"); + goto exit; + } + + /* look if we want to change the name of the netif */ + if (strcmp(udev->name, udev->dev->kernel) != 0) { + char devpath[PATH_MAX]; + char *pos; + + retval = rename_netif(udev); + if (retval != 0) + goto exit; + info("renamed netif to '%s'\n", udev->name); + + /* export old name */ + setenv("INTERFACE_OLD", udev->dev->kernel, 1); + + /* now change the devpath, because the kernel device name has changed */ + strlcpy(devpath, udev->dev->devpath, sizeof(devpath)); + pos = strrchr(devpath, '/'); + if (pos != NULL) { + pos[1] = '\0'; + strlcat(devpath, udev->name, sizeof(devpath)); + sysfs_device_set_values(udev->dev, devpath, NULL, NULL); + setenv("DEVPATH", udev->dev->devpath, 1); + setenv("INTERFACE", udev->name, 1); + info("changed devpath to '%s'\n", udev->dev->devpath); + } + } + goto exit; + } + + /* remove device node */ + if (major(udev->devt) != 0 && strcmp(udev->action, "remove") == 0) { + struct name_entry *name_loop; + + /* import database entry, and delete it */ + if (udev_db_get_device(udev, udev->dev->devpath) == 0) { + udev_db_delete_device(udev); + /* restore stored persistent data */ + list_for_each_entry(name_loop, &udev->env_list, node) + putenv(name_loop->name); + } else { + dbg("'%s' not found in database, using kernel name '%s'\n", + udev->dev->devpath, udev->dev->kernel); + strlcpy(udev->name, udev->dev->kernel, sizeof(udev->name)); + } + + udev_rules_get_run(rules, udev); + if (udev->ignore_device) { + info("device event will be ignored\n"); + goto exit; + } + + if (udev->ignore_remove) { + info("ignore_remove for '%s'\n", udev->name); + goto exit; + } + /* remove the node */ + retval = udev_node_remove(udev); + + /* delete or restore symlinks according to priority */ + udev_node_update_symlinks(udev, NULL); + goto exit; + } + + /* default devices */ + udev_rules_get_run(rules, udev); + if (udev->ignore_device) + info("device event will be ignored\n"); + +exit: + return retval; +} diff --git a/udev/udev_node.c b/udev/udev_node.c new file mode 100644 index 0000000000..78b6747043 --- /dev/null +++ b/udev/udev_node.c @@ -0,0 +1,448 @@ +/* + * Copyright (C) 2003 Greg Kroah-Hartman + * Copyright (C) 2004-2006 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udev_rules.h" +#include "udev_selinux.h" + +#define TMP_FILE_EXT ".udev-tmp" + +int udev_node_mknod(struct udevice *udev, const char *file, dev_t devt, mode_t mode, uid_t uid, gid_t gid) +{ + char file_tmp[PATH_SIZE + sizeof(TMP_FILE_EXT)]; + struct stat stats; + int preserve = 0; + int err = 0; + + if (major(devt) != 0 && strcmp(udev->dev->subsystem, "block") == 0) + mode |= S_IFBLK; + else + mode |= S_IFCHR; + + if (lstat(file, &stats) == 0) { + if (((stats.st_mode & S_IFMT) == (mode & S_IFMT)) && (stats.st_rdev == devt)) { + info("preserve file '%s', because it has correct dev_t\n", file); + preserve = 1; + selinux_setfilecon(file, udev->dev->kernel, mode); + } else { + info("atomically replace existing file '%s'\n", file); + strlcpy(file_tmp, file, sizeof(file_tmp)); + strlcat(file_tmp, TMP_FILE_EXT, sizeof(file_tmp)); + unlink(file_tmp); + selinux_setfscreatecon(file_tmp, udev->dev->kernel, mode); + err = mknod(file_tmp, mode, devt); + selinux_resetfscreatecon(); + if (err != 0) { + err("mknod(%s, %#o, %u, %u) failed: %s\n", + file_tmp, mode, major(devt), minor(devt), strerror(errno)); + goto exit; + } + err = rename(file_tmp, file); + if (err != 0) { + err("rename(%s, %s) failed: %s\n", + file_tmp, file, strerror(errno)); + unlink(file_tmp); + } + } + } else { + info("mknod(%s, %#o, (%u,%u))\n", file, mode, major(devt), minor(devt)); + selinux_setfscreatecon(file, udev->dev->kernel, mode); + err = mknod(file, mode, devt); + selinux_resetfscreatecon(); + if (err != 0) { + err("mknod(%s, %#o, (%u,%u) failed: %s\n", + file, mode, major(devt), minor(devt), strerror(errno)); + goto exit; + } + } + + if (!preserve || stats.st_mode != mode) { + info("chmod(%s, %#o)\n", file, mode); + err = chmod(file, mode); + if (err != 0) { + err("chmod(%s, %#o) failed: %s\n", file, mode, strerror(errno)); + goto exit; + } + } + + if (!preserve || stats.st_uid != uid || stats.st_gid != gid) { + info("chown(%s, %u, %u)\n", file, uid, gid); + err = chown(file, uid, gid); + if (err != 0) { + err("chown(%s, %u, %u) failed: %s\n", file, uid, gid, strerror(errno)); + goto exit; + } + } +exit: + return err; +} + +static int node_symlink(const char *node, const char *slink) +{ + struct stat stats; + char target[PATH_SIZE] = ""; + char slink_tmp[PATH_SIZE + sizeof(TMP_FILE_EXT)]; + int i = 0; + int tail = 0; + int len; + int retval = 0; + + /* use relative link */ + while (node[i] && (node[i] == slink[i])) { + if (node[i] == '/') + tail = i+1; + i++; + } + while (slink[i] != '\0') { + if (slink[i] == '/') + strlcat(target, "../", sizeof(target)); + i++; + } + strlcat(target, &node[tail], sizeof(target)); + + /* preserve link with correct target, do not replace node of other device */ + if (lstat(slink, &stats) == 0) { + if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { + struct stat stats2; + + info("found existing node instead of symlink '%s'\n", slink); + if (lstat(node, &stats2) == 0) { + if ((stats.st_mode & S_IFMT) == (stats2.st_mode & S_IFMT) && + stats.st_rdev == stats2.st_rdev) { + info("replace device node '%s' with symlink to our node '%s'\n", slink, node); + } else { + err("device node '%s' already exists, link to '%s' will not overwrite it\n", slink, node); + goto exit; + } + } + } else if (S_ISLNK(stats.st_mode)) { + char buf[PATH_SIZE]; + + info("found existing symlink '%s'\n", slink); + len = readlink(slink, buf, sizeof(buf)); + if (len > 0) { + buf[len] = '\0'; + if (strcmp(target, buf) == 0) { + info("preserve already existing symlink '%s' to '%s'\n", slink, target); + selinux_setfilecon(slink, NULL, S_IFLNK); + goto exit; + } + } + } + } else { + info("creating symlink '%s' to '%s'\n", slink, target); + selinux_setfscreatecon(slink, NULL, S_IFLNK); + retval = symlink(target, slink); + selinux_resetfscreatecon(); + if (retval == 0) + goto exit; + } + + info("atomically replace '%s'\n", slink); + strlcpy(slink_tmp, slink, sizeof(slink_tmp)); + strlcat(slink_tmp, TMP_FILE_EXT, sizeof(slink_tmp)); + unlink(slink_tmp); + selinux_setfscreatecon(slink, NULL, S_IFLNK); + retval = symlink(target, slink_tmp); + selinux_resetfscreatecon(); + if (retval != 0) { + err("symlink(%s, %s) failed: %s\n", target, slink_tmp, strerror(errno)); + goto exit; + } + retval = rename(slink_tmp, slink); + if (retval != 0) { + err("rename(%s, %s) failed: %s\n", slink_tmp, slink, strerror(errno)); + unlink(slink_tmp); + goto exit; + } +exit: + return retval; +} + +static int update_link(struct udevice *udev, const char *name) +{ + LIST_HEAD(name_list); + char slink[PATH_SIZE]; + char node[PATH_SIZE]; + struct udevice *udev_db; + struct name_entry *device; + char target[PATH_MAX] = ""; + int count; + int priority = 0; + int rc = 0; + + strlcpy(slink, udev_root, sizeof(slink)); + strlcat(slink, "/", sizeof(slink)); + strlcat(slink, name, sizeof(slink)); + + count = udev_db_get_devices_by_name(name, &name_list); + info("found %i devices with name '%s'\n", count, name); + + /* if we don't have a reference, delete it */ + if (count <= 0) { + info("no reference left, remove '%s'\n", name); + if (!udev->test_run) { + unlink(slink); + delete_path(slink); + } + goto out; + } + + /* find the device with the highest priority */ + list_for_each_entry(device, &name_list, node) { + info("found '%s' for '%s'\n", device->name, name); + + /* did we find ourself? we win, if we have the same priority */ + if (strcmp(udev->dev->devpath, device->name) == 0) { + info("compare (our own) priority of '%s' %i >= %i\n", + udev->dev->devpath, udev->link_priority, priority); + if (strcmp(udev->name, name) == 0) { + info("'%s' is our device node, database inconsistent, skip link update\n", udev->name); + } else if (target[0] == '\0' || udev->link_priority >= priority) { + priority = udev->link_priority; + strlcpy(target, udev->name, sizeof(target)); + } + continue; + } + + /* another device, read priority from database */ + udev_db = udev_device_init(NULL); + if (udev_db == NULL) + continue; + if (udev_db_get_device(udev_db, device->name) == 0) { + if (strcmp(udev_db->name, name) == 0) { + info("'%s' is a device node of '%s', skip link update\n", udev_db->name, device->name); + } else { + info("compare priority of '%s' %i > %i\n", + udev_db->dev->devpath, udev_db->link_priority, priority); + if (target[0] == '\0' || udev_db->link_priority > priority) { + priority = udev_db->link_priority; + strlcpy(target, udev_db->name, sizeof(target)); + } + } + } + udev_device_cleanup(udev_db); + } + name_list_cleanup(&name_list); + + if (target[0] == '\0') { + info("no current target for '%s' found\n", name); + rc = 1; + goto out; + } + + /* create symlink to the target with the highest priority */ + strlcpy(node, udev_root, sizeof(node)); + strlcat(node, "/", sizeof(node)); + strlcat(node, target, sizeof(node)); + info("'%s' with target '%s' has the highest priority %i, create it\n", name, target, priority); + if (!udev->test_run) { + create_path(slink); + node_symlink(node, slink); + } +out: + return rc; +} + +void udev_node_update_symlinks(struct udevice *udev, struct udevice *udev_old) +{ + struct name_entry *name_loop; + char symlinks[PATH_SIZE] = ""; + + list_for_each_entry(name_loop, &udev->symlink_list, node) { + info("update symlink '%s' of '%s'\n", name_loop->name, udev->dev->devpath); + update_link(udev, name_loop->name); + strlcat(symlinks, udev_root, sizeof(symlinks)); + strlcat(symlinks, "/", sizeof(symlinks)); + strlcat(symlinks, name_loop->name, sizeof(symlinks)); + strlcat(symlinks, " ", sizeof(symlinks)); + } + + /* export symlinks to environment */ + remove_trailing_chars(symlinks, ' '); + if (symlinks[0] != '\0') + setenv("DEVLINKS", symlinks, 1); + + /* update possible left-over symlinks (device metadata changed) */ + if (udev_old != NULL) { + struct name_entry *link_loop; + struct name_entry *link_old_loop; + int found; + + /* remove current symlinks from old list */ + list_for_each_entry(link_old_loop, &udev_old->symlink_list, node) { + found = 0; + list_for_each_entry(link_loop, &udev->symlink_list, node) { + if (strcmp(link_old_loop->name, link_loop->name) == 0) { + found = 1; + break; + } + } + if (!found) { + /* link does no longer belong to this device */ + info("update old symlink '%s' no longer belonging to '%s'\n", + link_old_loop->name, udev->dev->devpath); + update_link(udev, link_old_loop->name); + } + } + + /* + * if the node name has changed, delete the node, + * or possibly restore a symlink of another device + */ + if (strcmp(udev->name, udev_old->name) != 0) + update_link(udev, udev_old->name); + } +} + +int udev_node_add(struct udevice *udev) +{ + char filename[PATH_SIZE]; + uid_t uid; + gid_t gid; + int i; + int retval = 0; + + strlcpy(filename, udev_root, sizeof(filename)); + strlcat(filename, "/", sizeof(filename)); + strlcat(filename, udev->name, sizeof(filename)); + create_path(filename); + + if (strcmp(udev->owner, "root") == 0) + uid = 0; + else { + char *endptr; + unsigned long id; + + id = strtoul(udev->owner, &endptr, 10); + if (endptr[0] == '\0') + uid = (uid_t) id; + else + uid = lookup_user(udev->owner); + } + + if (strcmp(udev->group, "root") == 0) + gid = 0; + else { + char *endptr; + unsigned long id; + + id = strtoul(udev->group, &endptr, 10); + if (endptr[0] == '\0') + gid = (gid_t) id; + else + gid = lookup_group(udev->group); + } + + info("creating device node '%s', major=%d, minor=%d, mode=%#o, uid=%d, gid=%d\n", + filename, major(udev->devt), minor(udev->devt), udev->mode, uid, gid); + + if (!udev->test_run) + if (udev_node_mknod(udev, filename, udev->devt, udev->mode, uid, gid) != 0) { + retval = -1; + goto exit; + } + + setenv("DEVNAME", filename, 1); + + /* create all_partitions if requested */ + if (udev->partitions) { + char partitionname[PATH_SIZE]; + char *attr; + int range; + + /* take the maximum registered minor range */ + attr = sysfs_attr_get_value(udev->dev->devpath, "range"); + if (attr != NULL) { + range = atoi(attr); + if (range > 1) + udev->partitions = range-1; + } + info("creating device partition nodes '%s[1-%i]'\n", filename, udev->partitions); + if (!udev->test_run) { + for (i = 1; i <= udev->partitions; i++) { + dev_t part_devt; + + snprintf(partitionname, sizeof(partitionname), "%s%d", filename, i); + partitionname[sizeof(partitionname)-1] = '\0'; + part_devt = makedev(major(udev->devt), minor(udev->devt) + i); + udev_node_mknod(udev, partitionname, part_devt, udev->mode, uid, gid); + } + } + } +exit: + return retval; +} + +int udev_node_remove(struct udevice *udev) +{ + char filename[PATH_SIZE]; + char partitionname[PATH_SIZE]; + struct stat stats; + int retval = 0; + int num; + + strlcpy(filename, udev_root, sizeof(filename)); + strlcat(filename, "/", sizeof(filename)); + strlcat(filename, udev->name, sizeof(filename)); + if (stat(filename, &stats) != 0) { + info("device node '%s' not found\n", filename); + return 0; + } + if (udev->devt && stats.st_rdev != udev->devt) { + info("device node '%s' points to a different device, skip removal\n", filename); + return -1; + } + + info("removing device node '%s'\n", filename); + if (!udev->test_run) + retval = unlink_secure(filename); + if (retval) + return retval; + + setenv("DEVNAME", filename, 1); + num = udev->partitions; + if (num > 0) { + int i; + + info("removing all_partitions '%s[1-%i]'\n", filename, num); + if (num > 255) + return -1; + for (i = 1; i <= num; i++) { + snprintf(partitionname, sizeof(partitionname), "%s%d", filename, i); + partitionname[sizeof(partitionname)-1] = '\0'; + if (!udev->test_run) + unlink_secure(partitionname); + } + } + delete_path(filename); + return retval; +} diff --git a/udev/udev_rules.c b/udev/udev_rules.c new file mode 100644 index 0000000000..ea850a84c3 --- /dev/null +++ b/udev/udev_rules.c @@ -0,0 +1,1618 @@ +/* + * Copyright (C) 2003 Greg Kroah-Hartman + * Copyright (C) 2003-2006 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udev_rules.h" +#include "udev_selinux.h" + +extern char **environ; + +/* extract possible {attr} and move str behind it */ +static char *get_format_attribute(char **str) +{ + char *pos; + char *attr = NULL; + + if (*str[0] == '{') { + pos = strchr(*str, '}'); + if (pos == NULL) { + err("missing closing brace for format\n"); + return NULL; + } + pos[0] = '\0'; + attr = *str+1; + *str = pos+1; + dbg("attribute='%s', str='%s'\n", attr, *str); + } + return attr; +} + +/* extract possible format length and move str behind it*/ +static int get_format_len(char **str) +{ + int num; + char *tail; + + if (isdigit(*str[0])) { + num = (int) strtoul(*str, &tail, 10); + if (num > 0) { + *str = tail; + dbg("format length=%i\n", num); + return num; + } else { + err("format parsing error '%s'\n", *str); + } + } + return -1; +} + +static int get_key(char **line, char **key, char **value) +{ + char *linepos; + char *temp; + + linepos = *line; + if (linepos == NULL) + return -1; + + /* skip whitespace */ + while (isspace(linepos[0])) + linepos++; + + /* get the key */ + temp = strchr(linepos, '='); + if (temp == NULL || temp == linepos) + return -1; + temp[0] = '\0'; + *key = linepos; + linepos = &temp[1]; + + /* get a quoted value */ + if (linepos[0] == '"' || linepos[0] == '\'') { + temp = strchr(&linepos[1], linepos[0]); + if (temp != NULL) { + temp[0] = '\0'; + *value = &linepos[1]; + goto out; + } + } + + /* get the value*/ + temp = strchr(linepos, '\n'); + if (temp != NULL) + temp[0] = '\0'; + *value = linepos; +out: + return 0; +} + +static int run_program(const char *command, const char *subsystem, + char *result, size_t ressize, size_t *reslen) +{ + int status; + int outpipe[2] = {-1, -1}; + int errpipe[2] = {-1, -1}; + pid_t pid; + char arg[PATH_SIZE]; + char program[PATH_SIZE]; + char *argv[(sizeof(arg) / 2) + 1]; + int devnull; + int i; + int retval = 0; + + /* build argv from comand */ + strlcpy(arg, command, sizeof(arg)); + i = 0; + if (strchr(arg, ' ') != NULL) { + char *pos = arg; + + while (pos != NULL) { + if (pos[0] == '\'') { + /* don't separate if in apostrophes */ + pos++; + argv[i] = strsep(&pos, "\'"); + while (pos != NULL && pos[0] == ' ') + pos++; + } else { + argv[i] = strsep(&pos, " "); + } + dbg("arg[%i] '%s'\n", i, argv[i]); + i++; + } + argv[i] = NULL; + } else { + argv[0] = arg; + argv[1] = NULL; + } + info("'%s'\n", command); + + /* prepare pipes from child to parent */ + if (result != NULL || udev_log_priority >= LOG_INFO) { + if (pipe(outpipe) != 0) { + err("pipe failed: %s\n", strerror(errno)); + return -1; + } + } + if (udev_log_priority >= LOG_INFO) { + if (pipe(errpipe) != 0) { + err("pipe failed: %s\n", strerror(errno)); + return -1; + } + } + + /* allow programs in /lib/udev called without the path */ + if (strchr(argv[0], '/') == NULL) { + strlcpy(program, "/lib/udev/", sizeof(program)); + strlcat(program, argv[0], sizeof(program)); + 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("open /dev/null failed: %s\n", strerror(errno)); + 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]); + } + execv(argv[0], argv); + if (errno == ENOENT || errno == ENOTDIR) { + /* may be on a filesytem which is not mounted right now */ + info("program '%s' not found\n", argv[0]); + } else { + /* other problems */ + err("exec of program '%s' failed\n", argv[0]); + } + _exit(1); + case -1: + err("fork of '%s' failed: %s\n", argv[0], strerror(errno)); + 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(UDEV_MAX(outpipe[READ_END], errpipe[READ_END])+1, &readfds, NULL, NULL, NULL); + if (fdcount < 0) { + if (errno == EINTR) + continue; + retval = -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("stdin read failed: %s\n", strerror(errno)); + retval = -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("ressize %ld too short\n", (long)ressize); + retval = -1; + } + } + pos = inbuf; + while ((line = strsep(&pos, "\n"))) + if (pos || line[0] != '\0') + info("'%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("stderr read failed: %s\n", strerror(errno)); + continue; + } + errbuf[count] = '\0'; + pos = errbuf; + while ((line = strsep(&pos, "\n"))) + if (pos || line[0] != '\0') + info("'%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 childs stdout string */ + if (result) { + result[respos] = '\0'; + dbg("result='%s'\n", result); + if (reslen) + *reslen = respos; + } + } + waitpid(pid, &status, 0); + if (WIFEXITED(status)) { + info("'%s' returned with status %i\n", argv[0], WEXITSTATUS(status)); + if (WEXITSTATUS(status) != 0) + retval = -1; + } else { + err("'%s' abnormal exit\n", argv[0]); + retval = -1; + } + } + + return retval; +} + +static int import_keys_into_env(struct udevice *udev, const char *buf, size_t bufsize) +{ + char line[LINE_SIZE]; + const char *bufline; + char *linepos; + char *variable; + char *value; + size_t cur; + size_t count; + int lineno; + + /* loop through the whole buffer */ + lineno = 0; + cur = 0; + while (cur < bufsize) { + count = buf_get_line(buf, bufsize, cur); + bufline = &buf[cur]; + cur += count+1; + lineno++; + + /* eat the whitespace */ + while ((count > 0) && isspace(bufline[0])) { + bufline++; + count--; + } + if (count == 0) + continue; + + /* see if this is a comment */ + if (bufline[0] == COMMENT_CHARACTER) + continue; + + if (count >= sizeof(line)) { + err("line too long, conf line skipped %s, line %d\n", udev_config_filename, lineno); + continue; + } + + memcpy(line, bufline, count); + line[count] = '\0'; + + linepos = line; + if (get_key(&linepos, &variable, &value) == 0) { + dbg("import '%s=%s'\n", variable, value); + + /* handle device, renamed by external tool, returning new path */ + if (strcmp(variable, "DEVPATH") == 0) { + info("updating devpath from '%s' to '%s'\n", udev->dev->devpath, value); + sysfs_device_set_values(udev->dev, value, NULL, NULL); + } else + name_list_key_add(&udev->env_list, variable, value); + setenv(variable, value, 1); + } + } + + return 0; +} + +static int import_file_into_env(struct udevice *udev, const char *filename) +{ + char *buf; + size_t bufsize; + + if (file_map(filename, &buf, &bufsize) != 0) { + err("can't open '%s': %s\n", filename, strerror(errno)); + return -1; + } + import_keys_into_env(udev, buf, bufsize); + file_unmap(buf, bufsize); + + return 0; +} + +static int import_program_into_env(struct udevice *udev, const char *program) +{ + char result[2048]; + size_t reslen; + + if (run_program(program, udev->dev->subsystem, result, sizeof(result), &reslen) != 0) + return -1; + return import_keys_into_env(udev, result, reslen); +} + +static int import_parent_into_env(struct udevice *udev, const char *filter) +{ + struct sysfs_device *dev_parent; + int rc = -1; + + dev_parent = sysfs_device_get_parent(udev->dev); + if (dev_parent != NULL) { + struct udevice *udev_parent; + struct name_entry *name_loop; + + dbg("found parent '%s', get the node name\n", dev_parent->devpath); + udev_parent = udev_device_init(NULL); + if (udev_parent == NULL) + return -1; + /* import the udev_db of the parent */ + if (udev_db_get_device(udev_parent, dev_parent->devpath) == 0) { + dbg("import stored parent env '%s'\n", udev_parent->name); + list_for_each_entry(name_loop, &udev_parent->env_list, node) { + char name[NAME_SIZE]; + char *pos; + + strlcpy(name, name_loop->name, sizeof(name)); + pos = strchr(name, '='); + if (pos) { + pos[0] = '\0'; + pos++; + if (fnmatch(filter, name, 0) == 0) { + dbg("import key '%s'\n", name_loop->name); + name_list_add(&udev->env_list, name_loop->name, 0); + setenv(name, pos, 1); + } else + dbg("skip key '%s'\n", name_loop->name); + } + } + rc = 0; + } else + dbg("parent not found in database\n"); + udev_device_cleanup(udev_parent); + } + + return rc; +} + +static int pass_env_to_socket(const char *sockpath, const char *devpath, const char *action) +{ + int sock; + struct sockaddr_un saddr; + socklen_t saddrlen; + struct stat stats; + char buf[2048]; + size_t bufpos = 0; + int i; + ssize_t count; + int retval = 0; + + dbg("pass environment to socket '%s'\n", sockpath); + sock = socket(AF_LOCAL, SOCK_DGRAM, 0); + memset(&saddr, 0x00, sizeof(struct sockaddr_un)); + saddr.sun_family = AF_LOCAL; + if (sockpath[0] == '@') { + /* abstract namespace socket requested */ + strlcpy(&saddr.sun_path[1], &sockpath[1], sizeof(saddr.sun_path)-1); + saddrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); + } else if (stat(sockpath, &stats) == 0 && S_ISSOCK(stats.st_mode)) { + /* existing socket file */ + strlcpy(saddr.sun_path, sockpath, sizeof(saddr.sun_path)); + saddrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path); + } else { + /* no socket file, assume abstract namespace socket */ + strlcpy(&saddr.sun_path[1], sockpath, sizeof(saddr.sun_path)-1); + saddrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); + } + + bufpos = snprintf(buf, sizeof(buf)-1, "%s@%s", action, devpath); + bufpos++; + for (i = 0; environ[i] != NULL && bufpos < (sizeof(buf)-1); i++) { + bufpos += strlcpy(&buf[bufpos], environ[i], sizeof(buf) - bufpos-1); + bufpos++; + } + if (bufpos > sizeof(buf)) + bufpos = sizeof(buf); + + count = sendto(sock, &buf, bufpos, 0, (struct sockaddr *)&saddr, saddrlen); + if (count < 0) + retval = -1; + info("passed %zi bytes to socket '%s', \n", count, sockpath); + + close(sock); + return retval; +} + +int udev_rules_run(struct udevice *udev) +{ + struct name_entry *name_loop; + int retval = 0; + + dbg("executing run list\n"); + list_for_each_entry(name_loop, &udev->run_list, node) { + if (strncmp(name_loop->name, "socket:", strlen("socket:")) == 0) { + pass_env_to_socket(&name_loop->name[strlen("socket:")], udev->dev->devpath, udev->action); + } else { + char program[PATH_SIZE]; + + strlcpy(program, name_loop->name, sizeof(program)); + udev_rules_apply_format(udev, program, sizeof(program)); + if (run_program(program, udev->dev->subsystem, NULL, 0, NULL) != 0) + if (!name_loop->ignore_error) + retval = -1; + } + } + + return retval; +} + +#define WAIT_LOOP_PER_SECOND 50 +static int wait_for_file(struct udevice *udev, const char *file, int timeout) +{ + char filepath[PATH_SIZE]; + char devicepath[PATH_SIZE] = ""; + struct stat stats; + int loop = timeout * WAIT_LOOP_PER_SECOND; + + /* a relative path is a device attribute */ + if (file[0] != '/') { + strlcpy(devicepath, sysfs_path, sizeof(devicepath)); + strlcat(devicepath, udev->dev->devpath, sizeof(devicepath)); + + strlcpy(filepath, devicepath, sizeof(filepath)); + strlcat(filepath, "/", sizeof(filepath)); + strlcat(filepath, file, sizeof(filepath)); + file = filepath; + } + + dbg("will wait %i sec for '%s'\n", timeout, file); + while (--loop) { + /* lookup file */ + if (stat(file, &stats) == 0) { + info("file '%s' appeared after %i loops\n", file, (timeout * WAIT_LOOP_PER_SECOND) - loop-1); + return 0; + } + /* make sure, the device did not disappear in the meantime */ + if (devicepath[0] != '\0' && stat(devicepath, &stats) != 0) { + info("device disappeared while waiting for '%s'\n", file); + return -2; + } + info("wait for '%s' for %i mseconds\n", file, 1000 / WAIT_LOOP_PER_SECOND); + usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND); + } + info("waiting for '%s' failed\n", file); + return -1; +} + +/* handle "[$SUBSYSTEM/$KERNEL]" lookup */ +static int attr_get_by_subsys_id(const char *attrstr, char *devpath, size_t len, char **attr) +{ + char subsys[NAME_SIZE]; + char *attrib; + char *id; + int found = 0; + + if (attrstr[0] != '[') + goto out; + + strlcpy(subsys, &attrstr[1], sizeof(subsys)); + + attrib = strchr(subsys, ']'); + if (attrib == NULL) + goto out; + attrib[0] = '\0'; + attrib = &attrib[1]; + + id = strchr(subsys, '/'); + if (id == NULL) + goto out; + id[0] = '\0'; + id = &id[1]; + + if (sysfs_lookup_devpath_by_subsys_id(devpath, len, subsys, id)) { + if (attr != NULL) { + if (attrib[0] != '\0') + *attr = attrib; + else + *attr = NULL; + } + found = 1; + } +out: + return found; +} + +static int attr_subst_subdir(char *attr, size_t len) +{ + char *pos; + int found = 0; + + pos = strstr(attr, "/*/"); + if (pos != NULL) { + char str[PATH_SIZE]; + DIR *dir; + + pos[1] = '\0'; + strlcpy(str, &pos[2], sizeof(str)); + dir = opendir(attr); + if (dir != NULL) { + struct dirent *dent; + + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + struct stat stats; + + if (dent->d_name[0] == '.') + continue; + strlcat(attr, dent->d_name, len); + strlcat(attr, str, len); + if (stat(attr, &stats) == 0) { + found = 1; + break; + } + pos[1] = '\0'; + } + closedir(dir); + } + if (!found) + strlcat(attr, str, len); + } + + return found; +} + +void udev_rules_apply_format(struct udevice *udev, char *string, size_t maxsize) +{ + char temp[PATH_SIZE]; + char temp2[PATH_SIZE]; + char *head, *tail, *pos, *cpos, *attr, *rest; + int len; + int i; + int count; + enum subst_type { + SUBST_UNKNOWN, + SUBST_DEVPATH, + SUBST_KERNEL, + SUBST_KERNEL_NUMBER, + SUBST_ID, + SUBST_DRIVER, + SUBST_MAJOR, + SUBST_MINOR, + SUBST_RESULT, + SUBST_ATTR, + SUBST_PARENT, + SUBST_TEMP_NODE, + SUBST_NAME, + SUBST_LINKS, + SUBST_ROOT, + SUBST_SYS, + SUBST_ENV, + }; + static const struct subst_map { + char *name; + char fmt; + enum subst_type type; + } map[] = { + { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH }, + { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER }, + { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL }, + { .name = "id", .fmt = 'b', .type = SUBST_ID }, + { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER }, + { .name = "major", .fmt = 'M', .type = SUBST_MAJOR }, + { .name = "minor", .fmt = 'm', .type = SUBST_MINOR }, + { .name = "result", .fmt = 'c', .type = SUBST_RESULT }, + { .name = "attr", .fmt = 's', .type = SUBST_ATTR }, + { .name = "sysfs", .fmt = 's', .type = SUBST_ATTR }, + { .name = "parent", .fmt = 'P', .type = SUBST_PARENT }, + { .name = "tempnode", .fmt = 'N', .type = SUBST_TEMP_NODE }, + { .name = "name", .fmt = 'D', .type = SUBST_NAME }, + { .name = "links", .fmt = 'L', .type = SUBST_LINKS }, + { .name = "root", .fmt = 'r', .type = SUBST_ROOT }, + { .name = "sys", .fmt = 'S', .type = SUBST_SYS }, + { .name = "env", .fmt = 'E', .type = SUBST_ENV }, + { NULL, '\0', 0 } + }; + enum subst_type type; + const struct subst_map *subst; + + head = string; + while (1) { + len = -1; + while (head[0] != '\0') { + if (head[0] == '$') { + /* substitute named variable */ + if (head[1] == '\0') + break; + if (head[1] == '$') { + strlcpy(temp, head+2, sizeof(temp)); + strlcpy(head+1, temp, maxsize); + head++; + continue; + } + head[0] = '\0'; + for (subst = map; subst->name; subst++) { + if (strncasecmp(&head[1], subst->name, strlen(subst->name)) == 0) { + type = subst->type; + tail = head + strlen(subst->name)+1; + dbg("will substitute format name '%s'\n", subst->name); + goto found; + } + } + head[0] = '$'; + err("unknown format variable '%s'\n", head); + } else if (head[0] == '%') { + /* substitute format char */ + if (head[1] == '\0') + break; + if (head[1] == '%') { + strlcpy(temp, head+2, sizeof(temp)); + strlcpy(head+1, temp, maxsize); + head++; + continue; + } + head[0] = '\0'; + tail = head+1; + len = get_format_len(&tail); + for (subst = map; subst->name; subst++) { + if (tail[0] == subst->fmt) { + type = subst->type; + tail++; + dbg("will substitute format char '%c'\n", subst->fmt); + goto found; + } + } + head[0] = '%'; + err("unknown format char '%c'\n", tail[0]); + } + head++; + } + break; +found: + attr = get_format_attribute(&tail); + strlcpy(temp, tail, sizeof(temp)); + dbg("format=%i, string='%s', tail='%s'\n", type ,string, tail); + + switch (type) { + case SUBST_DEVPATH: + strlcat(string, udev->dev->devpath, maxsize); + dbg("substitute devpath '%s'\n", udev->dev->devpath); + break; + case SUBST_KERNEL: + strlcat(string, udev->dev->kernel, maxsize); + dbg("substitute kernel name '%s'\n", udev->dev->kernel); + break; + case SUBST_KERNEL_NUMBER: + strlcat(string, udev->dev->kernel_number, maxsize); + dbg("substitute kernel number '%s'\n", udev->dev->kernel_number); + break; + case SUBST_ID: + if (udev->dev_parent != NULL) { + strlcat(string, udev->dev_parent->kernel, maxsize); + dbg("substitute id '%s'\n", udev->dev_parent->kernel); + } + break; + case SUBST_DRIVER: + if (udev->dev_parent != NULL) { + strlcat(string, udev->dev_parent->driver, maxsize); + dbg("substitute driver '%s'\n", udev->dev_parent->driver); + } + break; + case SUBST_MAJOR: + sprintf(temp2, "%d", major(udev->devt)); + strlcat(string, temp2, maxsize); + dbg("substitute major number '%s'\n", temp2); + break; + case SUBST_MINOR: + sprintf(temp2, "%d", minor(udev->devt)); + strlcat(string, temp2, maxsize); + dbg("substitute minor number '%s'\n", temp2); + break; + case SUBST_RESULT: + if (udev->program_result[0] == '\0') + break; + /* get part part of the result string */ + i = 0; + if (attr != NULL) + i = strtoul(attr, &rest, 10); + if (i > 0) { + dbg("request part #%d of result string\n", i); + cpos = udev->program_result; + while (--i) { + while (cpos[0] != '\0' && !isspace(cpos[0])) + cpos++; + while (isspace(cpos[0])) + cpos++; + } + if (i > 0) { + err("requested part of result string not found\n"); + break; + } + strlcpy(temp2, cpos, sizeof(temp2)); + /* %{2+}c copies the whole string from the second part on */ + if (rest[0] != '+') { + cpos = strchr(temp2, ' '); + if (cpos) + cpos[0] = '\0'; + } + strlcat(string, temp2, maxsize); + dbg("substitute part of result string '%s'\n", temp2); + } else { + strlcat(string, udev->program_result, maxsize); + dbg("substitute result string '%s'\n", udev->program_result); + } + break; + case SUBST_ATTR: + if (attr == NULL) + err("missing file parameter for attr\n"); + else { + char devpath[PATH_SIZE]; + char *attrib; + const char *value = NULL; + size_t size; + + if (attr_get_by_subsys_id(attr, devpath, sizeof(devpath), &attrib)) { + if (attrib != NULL) + value = sysfs_attr_get_value(devpath, attrib); + else + break; + } + + /* try the current device, other matches may have selected */ + if (value == NULL && udev->dev_parent != NULL && udev->dev_parent != udev->dev) + value = sysfs_attr_get_value(udev->dev_parent->devpath, attr); + + /* look at all devices along the chain of parents */ + if (value == NULL) { + struct sysfs_device *dev_parent = udev->dev; + + do { + dbg("looking at '%s'\n", dev_parent->devpath); + value = sysfs_attr_get_value(dev_parent->devpath, attr); + if (value != NULL) { + strlcpy(temp2, value, sizeof(temp2)); + break; + } + dev_parent = sysfs_device_get_parent(dev_parent); + } while (dev_parent != NULL); + } + + if (value == NULL) + break; + + /* strip trailing whitespace, and replace unwanted characters */ + size = strlcpy(temp2, value, sizeof(temp2)); + if (size >= sizeof(temp2)) + size = sizeof(temp2)-1; + while (size > 0 && isspace(temp2[size-1])) + temp2[--size] = '\0'; + count = replace_chars(temp2, ALLOWED_CHARS_INPUT); + if (count > 0) + info("%i character(s) replaced\n" , count); + strlcat(string, temp2, maxsize); + dbg("substitute sysfs value '%s'\n", temp2); + } + break; + case SUBST_PARENT: + { + struct sysfs_device *dev_parent; + + dev_parent = sysfs_device_get_parent(udev->dev); + if (dev_parent != NULL) { + struct udevice *udev_parent; + + dbg("found parent '%s', get the node name\n", dev_parent->devpath); + udev_parent = udev_device_init(NULL); + if (udev_parent != NULL) { + /* lookup the name in the udev_db with the DEVPATH of the parent */ + if (udev_db_get_device(udev_parent, dev_parent->devpath) == 0) { + strlcat(string, udev_parent->name, maxsize); + dbg("substitute parent node name'%s'\n", udev_parent->name); + } else + dbg("parent not found in database\n"); + udev_device_cleanup(udev_parent); + } + } + } + break; + case SUBST_TEMP_NODE: + if (udev->tmp_node[0] == '\0' && major(udev->devt) > 0) { + dbg("create temporary device node for callout\n"); + snprintf(udev->tmp_node, sizeof(udev->tmp_node), "%s/.tmp-%u-%u", + udev_root, major(udev->devt), minor(udev->devt)); + udev->tmp_node[sizeof(udev->tmp_node)-1] = '\0'; + udev_node_mknod(udev, udev->tmp_node, udev->devt, 0600, 0, 0); + } + strlcat(string, udev->tmp_node, maxsize); + dbg("substitute temporary device node name '%s'\n", udev->tmp_node); + break; + case SUBST_NAME: + if (udev->name[0] == '\0') { + strlcat(string, udev->dev->kernel, maxsize); + dbg("substitute udev->kernel '%s'\n", udev->name); + } else { + strlcat(string, udev->name, maxsize); + dbg("substitute udev->name '%s'\n", udev->name); + } + break; + case SUBST_LINKS: + if (!list_empty(&udev->symlink_list)) { + struct name_entry *name_loop; + char symlinks[PATH_SIZE] = ""; + + list_for_each_entry(name_loop, &udev->symlink_list, node) { + strlcat(symlinks, name_loop->name, sizeof(symlinks)); + strlcat(symlinks, " ", sizeof(symlinks)); + } + remove_trailing_chars(symlinks, ' '); + strlcat(string, symlinks, maxsize); + } + break; + case SUBST_ROOT: + strlcat(string, udev_root, maxsize); + dbg("substitute udev_root '%s'\n", udev_root); + break; + case SUBST_SYS: + strlcat(string, sysfs_path, maxsize); + dbg("substitute sysfs_path '%s'\n", sysfs_path); + break; + case SUBST_ENV: + if (attr == NULL) { + dbg("missing attribute\n"); + break; + } + pos = getenv(attr); + if (pos == NULL) { + dbg("env '%s' not available\n", attr); + break; + } + dbg("substitute env '%s=%s'\n", attr, pos); + strlcat(string, pos, maxsize); + break; + default: + err("unknown substitution type=%i\n", type); + break; + } + /* possibly truncate to format-char specified length */ + if (len >= 0 && len < (int)strlen(head)) { + head[len] = '\0'; + dbg("truncate to %i chars, subtitution string becomes '%s'\n", len, head); + } + strlcat(string, temp, maxsize); + } +} + +static char *key_val(struct udev_rule *rule, struct key *key) +{ + return rule->buf + key->val_off; +} + +static char *key_pair_name(struct udev_rule *rule, struct key_pair *pair) +{ + return rule->buf + pair->key_name_off; +} + +static int match_key(const char *key_name, struct udev_rule *rule, struct key *key, const char *val) +{ + char value[PATH_SIZE]; + char *key_value; + char *pos; + int match = 0; + + if (key->operation != KEY_OP_MATCH && + key->operation != KEY_OP_NOMATCH) + return 0; + + /* look for a matching string, parts are separated by '|' */ + strlcpy(value, rule->buf + key->val_off, sizeof(value)); + key_value = value; + dbg("key %s value='%s'\n", key_name, key_value); + while (key_value) { + pos = strchr(key_value, '|'); + if (pos) { + pos[0] = '\0'; + pos++; + } + + dbg("match %s '%s' <-> '%s'\n", key_name, key_value, val); + match = (fnmatch(key_value, val, 0) == 0); + if (match) + break; + + key_value = pos; + } + + if (match && (key->operation == KEY_OP_MATCH)) { + dbg("%s is true (matching value)\n", key_name); + return 0; + } + if (!match && (key->operation == KEY_OP_NOMATCH)) { + dbg("%s is true (non-matching value)\n", key_name); + return 0; + } + return -1; +} + +/* match a single rule against a given device and possibly its parent devices */ +static int match_rule(struct udevice *udev, struct udev_rule *rule) +{ + int i; + + if (match_key("ACTION", rule, &rule->action, udev->action)) + goto nomatch; + + if (match_key("KERNEL", rule, &rule->kernel, udev->dev->kernel)) + goto nomatch; + + if (match_key("SUBSYSTEM", rule, &rule->subsystem, udev->dev->subsystem)) + goto nomatch; + + if (match_key("DEVPATH", rule, &rule->devpath, udev->dev->devpath)) + goto nomatch; + + if (match_key("DRIVER", rule, &rule->driver, udev->dev->driver)) + goto nomatch; + + /* match NAME against a value assigned by an earlier rule */ + if (match_key("NAME", rule, &rule->name, udev->name)) + goto nomatch; + + /* match against current list of symlinks */ + if (rule->symlink_match.operation == KEY_OP_MATCH || + rule->symlink_match.operation == KEY_OP_NOMATCH) { + struct name_entry *name_loop; + int match = 0; + + list_for_each_entry(name_loop, &udev->symlink_list, node) { + if (match_key("SYMLINK", rule, &rule->symlink_match, name_loop->name) == 0) { + match = 1; + break; + } + } + if (!match) + goto nomatch; + } + + for (i = 0; i < rule->env.count; i++) { + struct key_pair *pair = &rule->env.keys[i]; + + /* we only check for matches, assignments will be handled later */ + if (pair->key.operation == KEY_OP_MATCH || + pair->key.operation == KEY_OP_NOMATCH) { + const char *key_name = key_pair_name(rule, pair); + const char *value = getenv(key_name); + + if (!value) { + dbg("ENV{'%s'} is not set, treat as empty\n", key_name); + value = ""; + } + if (match_key("ENV", rule, &pair->key, value)) + goto nomatch; + } + } + + if (rule->test.operation == KEY_OP_MATCH || + rule->test.operation == KEY_OP_NOMATCH) { + char filename[PATH_SIZE]; + char devpath[PATH_SIZE]; + char *attr; + struct stat statbuf; + int match; + + strlcpy(filename, key_val(rule, &rule->test), sizeof(filename)); + udev_rules_apply_format(udev, filename, sizeof(filename)); + + if (attr_get_by_subsys_id(filename, devpath, sizeof(devpath), &attr)) { + strlcpy(filename, sysfs_path, sizeof(filename)); + strlcat(filename, devpath, sizeof(filename)); + if (attr != NULL) { + strlcat(filename, "/", sizeof(filename)); + strlcat(filename, attr, sizeof(filename)); + } + } else if (filename[0] != '/') { + char tmp[PATH_SIZE]; + + strlcpy(tmp, sysfs_path, sizeof(tmp)); + strlcat(tmp, udev->dev->devpath, sizeof(tmp)); + strlcat(tmp, "/", sizeof(tmp)); + strlcat(tmp, filename, sizeof(tmp)); + strlcpy(filename, tmp, sizeof(filename)); + } + + attr_subst_subdir(filename, sizeof(filename)); + + match = (stat(filename, &statbuf) == 0); + info("'%s' %s", filename, match ? "exists\n" : "does not exist\n"); + if (match && rule->test_mode_mask > 0) { + match = ((statbuf.st_mode & rule->test_mode_mask) > 0); + info("'%s' has mode=%#o and %s %#o\n", filename, statbuf.st_mode, + match ? "matches" : "does not match", + rule->test_mode_mask); + } + if (match && rule->test.operation == KEY_OP_NOMATCH) + goto nomatch; + if (!match && rule->test.operation == KEY_OP_MATCH) + goto nomatch; + dbg("TEST key is true\n"); + } + + if (rule->wait_for.operation != KEY_OP_UNSET) { + char filename[PATH_SIZE]; + int found; + + strlcpy(filename, key_val(rule, &rule->wait_for), sizeof(filename)); + udev_rules_apply_format(udev, filename, sizeof(filename)); + found = (wait_for_file(udev, filename, 10) == 0); + if (!found && (rule->wait_for.operation != KEY_OP_NOMATCH)) + goto nomatch; + } + + /* check for matching sysfs attribute pairs */ + for (i = 0; i < rule->attr.count; i++) { + struct key_pair *pair = &rule->attr.keys[i]; + + if (pair->key.operation == KEY_OP_MATCH || + pair->key.operation == KEY_OP_NOMATCH) { + const char *key_name = key_pair_name(rule, pair); + const char *key_value = key_val(rule, &pair->key); + char devpath[PATH_SIZE]; + char *attrib; + const char *value = NULL; + char val[VALUE_SIZE]; + size_t len; + + if (attr_get_by_subsys_id(key_name, devpath, sizeof(devpath), &attrib)) { + if (attrib != NULL) + value = sysfs_attr_get_value(devpath, attrib); + else + goto nomatch; + } + if (value == NULL) + value = sysfs_attr_get_value(udev->dev->devpath, key_name); + if (value == NULL) + goto nomatch; + strlcpy(val, value, sizeof(val)); + + /* strip trailing whitespace of value, if not asked to match for it */ + len = strlen(key_value); + if (len > 0 && !isspace(key_value[len-1])) { + len = strlen(val); + while (len > 0 && isspace(val[len-1])) + val[--len] = '\0'; + dbg("removed %zi trailing whitespace chars from '%s'\n", strlen(val)-len, val); + } + + if (match_key("ATTR", rule, &pair->key, val)) + goto nomatch; + } + } + + /* walk up the chain of parent devices and find a match */ + udev->dev_parent = udev->dev; + while (1) { + /* check for matching kernel device name */ + if (match_key("KERNELS", rule, &rule->kernels, udev->dev_parent->kernel)) + goto try_parent; + + /* check for matching subsystem value */ + if (match_key("SUBSYSTEMS", rule, &rule->subsystems, udev->dev_parent->subsystem)) + goto try_parent; + + /* check for matching driver */ + if (match_key("DRIVERS", rule, &rule->drivers, udev->dev_parent->driver)) + goto try_parent; + + /* check for matching sysfs attribute pairs */ + for (i = 0; i < rule->attrs.count; i++) { + struct key_pair *pair = &rule->attrs.keys[i]; + + if (pair->key.operation == KEY_OP_MATCH || + pair->key.operation == KEY_OP_NOMATCH) { + const char *key_name = key_pair_name(rule, pair); + const char *key_value = key_val(rule, &pair->key); + const char *value; + char val[VALUE_SIZE]; + size_t len; + + value = sysfs_attr_get_value(udev->dev_parent->devpath, key_name); + if (value == NULL) + value = sysfs_attr_get_value(udev->dev->devpath, key_name); + if (value == NULL) + goto try_parent; + strlcpy(val, value, sizeof(val)); + + /* strip trailing whitespace of value, if not asked to match for it */ + len = strlen(key_value); + if (len > 0 && !isspace(key_value[len-1])) { + len = strlen(val); + while (len > 0 && isspace(val[len-1])) + val[--len] = '\0'; + dbg("removed %zi trailing whitespace chars from '%s'\n", strlen(val)-len, val); + } + + if (match_key("ATTRS", rule, &pair->key, val)) + goto try_parent; + } + } + + /* found matching device */ + break; +try_parent: + /* move to parent device */ + dbg("try parent sysfs device\n"); + udev->dev_parent = sysfs_device_get_parent(udev->dev_parent); + if (udev->dev_parent == NULL) + goto nomatch; + dbg("looking at dev_parent->devpath='%s'\n", udev->dev_parent->devpath); + dbg("looking at dev_parent->kernel='%s'\n", udev->dev_parent->kernel); + } + + /* execute external program */ + if (rule->program.operation != KEY_OP_UNSET) { + char program[PATH_SIZE]; + char result[PATH_SIZE]; + + strlcpy(program, key_val(rule, &rule->program), sizeof(program)); + udev_rules_apply_format(udev, program, sizeof(program)); + if (run_program(program, udev->dev->subsystem, result, sizeof(result), NULL) != 0) { + dbg("PROGRAM is false\n"); + udev->program_result[0] = '\0'; + if (rule->program.operation != KEY_OP_NOMATCH) + goto nomatch; + } else { + int count; + + dbg("PROGRAM matches\n"); + remove_trailing_chars(result, '\n'); + if (rule->string_escape == ESCAPE_UNSET || + rule->string_escape == ESCAPE_REPLACE) { + count = replace_chars(result, ALLOWED_CHARS_INPUT); + if (count > 0) + info("%i character(s) replaced\n" , count); + } + dbg("result is '%s'\n", result); + strlcpy(udev->program_result, result, sizeof(udev->program_result)); + dbg("PROGRAM returned successful\n"); + if (rule->program.operation == KEY_OP_NOMATCH) + goto nomatch; + } + dbg("PROGRAM key is true\n"); + } + + /* check for matching result of external program */ + if (match_key("RESULT", rule, &rule->result, udev->program_result)) + goto nomatch; + + /* import variables returned from program or or file into environment */ + if (rule->import.operation != KEY_OP_UNSET) { + char import[PATH_SIZE]; + int rc = -1; + + strlcpy(import, key_val(rule, &rule->import), sizeof(import)); + udev_rules_apply_format(udev, import, sizeof(import)); + dbg("check for IMPORT import='%s'\n", import); + if (rule->import_type == IMPORT_PROGRAM) { + rc = import_program_into_env(udev, import); + } else if (rule->import_type == IMPORT_FILE) { + dbg("import file import='%s'\n", import); + rc = import_file_into_env(udev, import); + } else if (rule->import_type == IMPORT_PARENT) { + dbg("import parent import='%s'\n", import); + rc = import_parent_into_env(udev, import); + } + if (rc != 0) { + dbg("IMPORT failed\n"); + if (rule->import.operation != KEY_OP_NOMATCH) + goto nomatch; + } else + dbg("IMPORT '%s' imported\n", key_val(rule, &rule->import)); + dbg("IMPORT key is true\n"); + } + + /* rule matches, if we have ENV assignments export it */ + for (i = 0; i < rule->env.count; i++) { + struct key_pair *pair = &rule->env.keys[i]; + + if (pair->key.operation == KEY_OP_ASSIGN) { + char temp_value[NAME_SIZE]; + const char *key_name = key_pair_name(rule, pair); + const char *value = key_val(rule, &pair->key); + + /* make sure we don't write to the same string we possibly read from */ + strlcpy(temp_value, value, sizeof(temp_value)); + udev_rules_apply_format(udev, temp_value, NAME_SIZE); + + if (temp_value[0] == '\0') { + name_list_key_remove(&udev->env_list, key_name); + unsetenv(key_name); + info("unset ENV '%s'\n", key_name); + } else { + struct name_entry *entry; + + entry = name_list_key_add(&udev->env_list, key_name, temp_value); + if (entry == NULL) + break; + putenv(entry->name); + info("set ENV '%s'\n", entry->name); + } + } + } + + /* if we have ATTR assignments, write value to sysfs file */ + for (i = 0; i < rule->attr.count; i++) { + struct key_pair *pair = &rule->attr.keys[i]; + + if (pair->key.operation == KEY_OP_ASSIGN) { + const char *key_name = key_pair_name(rule, pair); + char devpath[PATH_SIZE]; + char *attrib; + char attr[PATH_SIZE] = ""; + char value[NAME_SIZE]; + FILE *f; + + if (attr_get_by_subsys_id(key_name, devpath, sizeof(devpath), &attrib)) { + if (attrib != NULL) { + strlcpy(attr, sysfs_path, sizeof(attr)); + strlcat(attr, devpath, sizeof(attr)); + strlcat(attr, "/", sizeof(attr)); + strlcat(attr, attrib, sizeof(attr)); + } + } + + if (attr[0] == '\0') { + strlcpy(attr, sysfs_path, sizeof(attr)); + strlcat(attr, udev->dev->devpath, sizeof(attr)); + strlcat(attr, "/", sizeof(attr)); + strlcat(attr, key_name, sizeof(attr)); + } + + attr_subst_subdir(attr, sizeof(attr)); + + strlcpy(value, key_val(rule, &pair->key), sizeof(value)); + udev_rules_apply_format(udev, value, sizeof(value)); + info("writing '%s' to sysfs file '%s'\n", value, attr); + f = fopen(attr, "w"); + if (f != NULL) { + if (!udev->test_run) + if (fprintf(f, "%s", value) <= 0) + err("error writing ATTR{%s}: %s\n", attr, strerror(errno)); + fclose(f); + } else + err("error opening ATTR{%s} for writing: %s\n", attr, strerror(errno)); + } + } + return 0; + +nomatch: + return -1; +} + +int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev) +{ + struct udev_rule *rule; + int name_set = 0; + + dbg("udev->dev->devpath='%s'\n", udev->dev->devpath); + dbg("udev->dev->kernel='%s'\n", udev->dev->kernel); + + /* look for a matching rule to apply */ + udev_rules_iter_init(rules); + while (1) { + rule = udev_rules_iter_next(rules); + if (rule == NULL) + break; + + if (name_set && + (rule->name.operation == KEY_OP_ASSIGN || + rule->name.operation == KEY_OP_ASSIGN_FINAL || + rule->name.operation == KEY_OP_ADD)) { + dbg("node name already set, rule ignored\n"); + continue; + } + + dbg("process rule\n"); + if (match_rule(udev, rule) == 0) { + /* apply options */ + if (rule->ignore_device) { + info("rule applied, '%s' is ignored\n", udev->dev->kernel); + udev->ignore_device = 1; + return 0; + } + if (rule->ignore_remove) { + udev->ignore_remove = 1; + dbg("remove event should be ignored\n"); + } + if (rule->link_priority != 0) { + udev->link_priority = rule->link_priority; + info("link_priority=%i\n", udev->link_priority); + } + if (rule->event_timeout >= 0) { + udev->event_timeout = rule->event_timeout; + info("event_timeout=%i\n", udev->event_timeout); + } + /* apply all_partitions option only at a main block device */ + if (rule->partitions && + strcmp(udev->dev->subsystem, "block") == 0 && udev->dev->kernel_number[0] == '\0') { + udev->partitions = rule->partitions; + dbg("creation of partition nodes requested\n"); + } + + /* apply permissions */ + if (!udev->mode_final && rule->mode.operation != KEY_OP_UNSET) { + if (rule->mode.operation == KEY_OP_ASSIGN_FINAL) + udev->mode_final = 1; + char buf[20]; + strlcpy(buf, key_val(rule, &rule->mode), sizeof(buf)); + udev_rules_apply_format(udev, buf, sizeof(buf)); + udev->mode = strtol(buf, NULL, 8); + dbg("applied mode=%#o to '%s'\n", udev->mode, udev->dev->kernel); + } + if (!udev->owner_final && rule->owner.operation != KEY_OP_UNSET) { + if (rule->owner.operation == KEY_OP_ASSIGN_FINAL) + udev->owner_final = 1; + strlcpy(udev->owner, key_val(rule, &rule->owner), sizeof(udev->owner)); + udev_rules_apply_format(udev, udev->owner, sizeof(udev->owner)); + dbg("applied owner='%s' to '%s'\n", udev->owner, udev->dev->kernel); + } + if (!udev->group_final && rule->group.operation != KEY_OP_UNSET) { + if (rule->group.operation == KEY_OP_ASSIGN_FINAL) + udev->group_final = 1; + strlcpy(udev->group, key_val(rule, &rule->group), sizeof(udev->group)); + udev_rules_apply_format(udev, udev->group, sizeof(udev->group)); + dbg("applied group='%s' to '%s'\n", udev->group, udev->dev->kernel); + } + + /* collect symlinks */ + if (!udev->symlink_final && + (rule->symlink.operation == KEY_OP_ASSIGN || + rule->symlink.operation == KEY_OP_ASSIGN_FINAL || + rule->symlink.operation == KEY_OP_ADD)) { + char temp[PATH_SIZE]; + char *pos, *next; + int count; + + if (rule->symlink.operation == KEY_OP_ASSIGN_FINAL) + udev->symlink_final = 1; + if (rule->symlink.operation == KEY_OP_ASSIGN || + rule->symlink.operation == KEY_OP_ASSIGN_FINAL) { + info("reset symlink list\n"); + name_list_cleanup(&udev->symlink_list); + } + /* allow multiple symlinks separated by spaces */ + strlcpy(temp, key_val(rule, &rule->symlink), sizeof(temp)); + udev_rules_apply_format(udev, temp, sizeof(temp)); + if (rule->string_escape == ESCAPE_UNSET || + rule->string_escape == ESCAPE_REPLACE) { + count = replace_chars(temp, ALLOWED_CHARS_FILE " "); + if (count > 0) + info("%i character(s) replaced\n" , count); + } + dbg("rule applied, added symlink(s) '%s'\n", temp); + pos = temp; + while (isspace(pos[0])) + pos++; + next = strchr(pos, ' '); + while (next) { + next[0] = '\0'; + info("add symlink '%s'\n", pos); + name_list_add(&udev->symlink_list, pos, 0); + while (isspace(next[1])) + next++; + pos = &next[1]; + next = strchr(pos, ' '); + } + if (pos[0] != '\0') { + info("add symlink '%s'\n", pos); + name_list_add(&udev->symlink_list, pos, 0); + } + } + + /* set name, later rules with name set will be ignored */ + if (rule->name.operation == KEY_OP_ASSIGN || + rule->name.operation == KEY_OP_ASSIGN_FINAL || + rule->name.operation == KEY_OP_ADD) { + int count; + + name_set = 1; + strlcpy(udev->name, key_val(rule, &rule->name), sizeof(udev->name)); + udev_rules_apply_format(udev, udev->name, sizeof(udev->name)); + if (rule->string_escape == ESCAPE_UNSET || + rule->string_escape == ESCAPE_REPLACE) { + count = replace_chars(udev->name, ALLOWED_CHARS_FILE); + if (count > 0) + info("%i character(s) replaced\n", count); + } + + info("rule applied, '%s' becomes '%s'\n", udev->dev->kernel, udev->name); + if (strcmp(udev->dev->subsystem, "net") != 0) + dbg("name, '%s' is going to have owner='%s', group='%s', mode=%#o partitions=%i\n", + udev->name, udev->owner, udev->group, udev->mode, udev->partitions); + } + + if (!udev->run_final && rule->run.operation != KEY_OP_UNSET) { + struct name_entry *entry; + + if (rule->run.operation == KEY_OP_ASSIGN_FINAL) + udev->run_final = 1; + if (rule->run.operation == KEY_OP_ASSIGN || rule->run.operation == KEY_OP_ASSIGN_FINAL) { + info("reset run list\n"); + name_list_cleanup(&udev->run_list); + } + dbg("add run '%s'\n", key_val(rule, &rule->run)); + entry = name_list_add(&udev->run_list, key_val(rule, &rule->run), 0); + if (rule->run_ignore_error) + entry->ignore_error = 1; + } + + if (rule->last_rule) { + dbg("last rule to be applied\n"); + break; + } + + if (rule->goto_label.operation != KEY_OP_UNSET) { + dbg("moving forward to label '%s'\n", key_val(rule, &rule->goto_label)); + udev_rules_iter_label(rules, key_val(rule, &rule->goto_label)); + } + } + } + + if (!name_set) { + info("no node name set, will use kernel name '%s'\n", udev->dev->kernel); + strlcpy(udev->name, udev->dev->kernel, sizeof(udev->name)); + } + + if (udev->tmp_node[0] != '\0') { + dbg("removing temporary device node\n"); + unlink_secure(udev->tmp_node); + udev->tmp_node[0] = '\0'; + } + + return 0; +} + +int udev_rules_get_run(struct udev_rules *rules, struct udevice *udev) +{ + struct udev_rule *rule; + + dbg("udev->kernel='%s'\n", udev->dev->kernel); + + /* look for a matching rule to apply */ + udev_rules_iter_init(rules); + while (1) { + rule = udev_rules_iter_next(rules); + if (rule == NULL) + break; + + dbg("process rule\n"); + if (rule->name.operation == KEY_OP_ASSIGN || + rule->name.operation == KEY_OP_ASSIGN_FINAL || + rule->name.operation == KEY_OP_ADD || + rule->symlink.operation == KEY_OP_ASSIGN || + rule->symlink.operation == KEY_OP_ASSIGN_FINAL || + rule->symlink.operation == KEY_OP_ADD || + rule->mode.operation != KEY_OP_UNSET || + rule->owner.operation != KEY_OP_UNSET || rule->group.operation != KEY_OP_UNSET) { + dbg("skip rule that names a device\n"); + continue; + } + + if (match_rule(udev, rule) == 0) { + if (rule->ignore_device) { + info("rule applied, '%s' is ignored\n", udev->dev->kernel); + udev->ignore_device = 1; + return 0; + } + if (rule->ignore_remove) { + udev->ignore_remove = 1; + dbg("remove event should be ignored\n"); + } + + if (!udev->run_final && rule->run.operation != KEY_OP_UNSET) { + struct name_entry *entry; + + if (rule->run.operation == KEY_OP_ASSIGN || + rule->run.operation == KEY_OP_ASSIGN_FINAL) { + info("reset run list\n"); + name_list_cleanup(&udev->run_list); + } + dbg("add run '%s'\n", key_val(rule, &rule->run)); + entry = name_list_add(&udev->run_list, key_val(rule, &rule->run), 0); + if (rule->run_ignore_error) + entry->ignore_error = 1; + if (rule->run.operation == KEY_OP_ASSIGN_FINAL) + break; + } + + if (rule->last_rule) { + dbg("last rule to be applied\n"); + break; + } + + if (rule->goto_label.operation != KEY_OP_UNSET) { + dbg("moving forward to label '%s'\n", key_val(rule, &rule->goto_label)); + udev_rules_iter_label(rules, key_val(rule, &rule->goto_label)); + } + } + } + + return 0; +} diff --git a/udev/udev_rules.h b/udev/udev_rules.h new file mode 100644 index 0000000000..fe0f9dfbb5 --- /dev/null +++ b/udev/udev_rules.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2003-2004 Greg Kroah-Hartman + * Copyright (C) 2004-2006 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef UDEV_RULES_H +#define UDEV_RULES_H + +#include "udev.h" +#include "list.h" + +#define PAIRS_MAX 5 +#define RULESFILE_SUFFIX ".rules" + +enum key_operation { + KEY_OP_UNSET, + KEY_OP_MATCH, + KEY_OP_NOMATCH, + KEY_OP_ADD, + KEY_OP_ASSIGN, + KEY_OP_ASSIGN_FINAL, +}; + +struct key { + enum key_operation operation; + size_t val_off; +}; + +struct key_pair { + struct key key; + size_t key_name_off; +}; + +struct key_pairs { + int count; + struct key_pair keys[PAIRS_MAX]; +}; + +enum import_type { + IMPORT_UNSET, + IMPORT_PROGRAM, + IMPORT_FILE, + IMPORT_PARENT, +}; + +enum escape_type { + ESCAPE_UNSET, + ESCAPE_NONE, + ESCAPE_REPLACE, +}; + +struct udev_rule { + struct key action; + struct key devpath; + struct key kernel; + struct key subsystem; + struct key driver; + struct key_pairs attr; + + struct key kernels; + struct key subsystems; + struct key drivers; + struct key_pairs attrs; + + struct key_pairs env; + struct key program; + struct key result; + struct key import; + enum import_type import_type; + struct key test; + mode_t test_mode_mask; + struct key run; + struct key wait_for; + struct key label; + struct key goto_label; + + struct key name; + struct key symlink; + struct key symlink_match; + struct key owner; + struct key group; + struct key mode; + enum escape_type string_escape; + + unsigned int link_priority; + int event_timeout; + unsigned int partitions; + unsigned int last_rule:1, + run_ignore_error:1, + ignore_device:1, + ignore_remove:1; + + size_t bufsize; + char buf[]; +}; + +struct udev_rules { + char *buf; + size_t bufsize; + size_t current; + int resolve_names; +}; + +extern int udev_rules_init(struct udev_rules *rules, int resolve_names); +extern void udev_rules_cleanup(struct udev_rules *rules); + +extern void udev_rules_iter_init(struct udev_rules *rules); +extern struct udev_rule *udev_rules_iter_next(struct udev_rules *rules); +extern struct udev_rule *udev_rules_iter_label(struct udev_rules *rules, const char *label); + +extern int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev); +extern int udev_rules_get_run(struct udev_rules *rules, struct udevice *udev); +extern int udev_rules_run(struct udevice *udev); + +extern void udev_rules_apply_format(struct udevice *udev, char *string, size_t maxsize); + +#endif diff --git a/udev/udev_rules_parse.c b/udev/udev_rules_parse.c new file mode 100644 index 0000000000..3ada8b1c7d --- /dev/null +++ b/udev/udev_rules_parse.c @@ -0,0 +1,804 @@ +/* + * Copyright (C) 2003,2004 Greg Kroah-Hartman + * Copyright (C) 2003-2006 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udev_rules.h" +#include "udev_selinux.h" + + +void udev_rules_iter_init(struct udev_rules *rules) +{ + dbg("bufsize=%zi\n", rules->bufsize); + rules->current = 0; +} + +struct udev_rule *udev_rules_iter_next(struct udev_rules *rules) +{ + static struct udev_rule *rule; + + if (!rules) + return NULL; + + dbg("current=%zi\n", rules->current); + if (rules->current >= rules->bufsize) { + dbg("no more rules\n"); + return NULL; + } + + /* get next rule */ + rule = (struct udev_rule *) (rules->buf + rules->current); + rules->current += sizeof(struct udev_rule) + rule->bufsize; + + return rule; +} + +struct udev_rule *udev_rules_iter_label(struct udev_rules *rules, const char *label) +{ + static struct udev_rule *rule; + size_t start = rules->current; + +next: + dbg("current=%zi\n", rules->current); + if (rules->current >= rules->bufsize) { + err("LABEL='%s' not found, GOTO will be ignored\n", label); + rules->current = start; + return NULL; + } + rule = (struct udev_rule *) (rules->buf + rules->current); + + if (strcmp(&rule->buf[rule->label.val_off], label) != 0) { + dbg("moving forward, looking for label '%s'\n", label); + rules->current += sizeof(struct udev_rule) + rule->bufsize; + goto next; + } + + dbg("found label '%s'\n", label); + return rule; +} + +static int get_key(char **line, char **key, enum key_operation *operation, char **value) +{ + char *linepos; + char *temp; + + linepos = *line; + if (linepos == NULL && linepos[0] == '\0') + return -1; + + /* skip whitespace */ + while (isspace(linepos[0]) || linepos[0] == ',') + linepos++; + + /* get the key */ + if (linepos[0] == '\0') + return -1; + *key = linepos; + + while (1) { + linepos++; + if (linepos[0] == '\0') + return -1; + if (isspace(linepos[0])) + break; + if (linepos[0] == '=') + break; + if ((linepos[0] == '+') || (linepos[0] == '!') || (linepos[0] == ':')) + if (linepos[1] == '=') + break; + } + + /* remember end of key */ + temp = linepos; + + /* skip whitespace after key */ + while (isspace(linepos[0])) + linepos++; + if (linepos[0] == '\0') + return -1; + + /* get operation type */ + if (linepos[0] == '=' && linepos[1] == '=') { + *operation = KEY_OP_MATCH; + linepos += 2; + dbg("operator=match\n"); + } else if (linepos[0] == '!' && linepos[1] == '=') { + *operation = KEY_OP_NOMATCH; + linepos += 2; + dbg("operator=nomatch\n"); + } else if (linepos[0] == '+' && linepos[1] == '=') { + *operation = KEY_OP_ADD; + linepos += 2; + dbg("operator=add\n"); + } else if (linepos[0] == '=') { + *operation = KEY_OP_ASSIGN; + linepos++; + dbg("operator=assign\n"); + } else if (linepos[0] == ':' && linepos[1] == '=') { + *operation = KEY_OP_ASSIGN_FINAL; + linepos += 2; + dbg("operator=assign_final\n"); + } else + return -1; + + /* terminate key */ + temp[0] = '\0'; + dbg("key='%s'\n", *key); + + /* skip whitespace after operator */ + while (isspace(linepos[0])) + linepos++; + if (linepos[0] == '\0') + return -1; + + /* get the value*/ + if (linepos[0] == '"') + linepos++; + else + return -1; + *value = linepos; + + temp = strchr(linepos, '"'); + if (!temp) + return -1; + temp[0] = '\0'; + temp++; + dbg("value='%s'\n", *value); + + /* move line to next key */ + *line = temp; + + return 0; +} + +/* extract possible KEY{attr} */ +static char *get_key_attribute(char *str) +{ + char *pos; + char *attr; + + attr = strchr(str, '{'); + if (attr != NULL) { + attr++; + pos = strchr(attr, '}'); + if (pos == NULL) { + err("missing closing brace for format\n"); + return NULL; + } + pos[0] = '\0'; + dbg("attribute='%s'\n", attr); + return attr; + } + + return NULL; +} + +static int add_rule_key(struct udev_rule *rule, struct key *key, + enum key_operation operation, const char *value) +{ + size_t val_len = strnlen(value, PATH_SIZE); + + key->operation = operation; + + key->val_off = rule->bufsize; + strlcpy(rule->buf + rule->bufsize, value, val_len+1); + rule->bufsize += val_len+1; + + return 0; +} + +static int add_rule_key_pair(struct udev_rule *rule, struct key_pairs *pairs, + enum key_operation operation, const char *key, const char *value) +{ + size_t key_len = strnlen(key, PATH_SIZE); + + if (pairs->count >= PAIRS_MAX) { + err("skip, too many keys of the same type in a single rule\n"); + return -1; + } + + add_rule_key(rule, &pairs->keys[pairs->count].key, operation, value); + + /* add the key-name of the pair */ + pairs->keys[pairs->count].key_name_off = rule->bufsize; + strlcpy(rule->buf + rule->bufsize, key, key_len+1); + rule->bufsize += key_len+1; + + pairs->count++; + + return 0; +} + +static int add_to_rules(struct udev_rules *rules, char *line, const char *filename, unsigned int lineno) +{ + char buf[sizeof(struct udev_rule) + LINE_SIZE]; + struct udev_rule *rule; + size_t rule_size; + int valid; + char *linepos; + char *attr; + size_t padding; + int physdev = 0; + int retval; + + memset(buf, 0x00, sizeof(buf)); + rule = (struct udev_rule *) buf; + rule->event_timeout = -1; + linepos = line; + valid = 0; + + /* get all the keys */ + while (1) { + char *key; + char *value; + enum key_operation operation = KEY_OP_UNSET; + + retval = get_key(&linepos, &key, &operation, &value); + if (retval) + break; + + if (strcasecmp(key, "ACTION") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err("invalid ACTION operation\n"); + goto invalid; + } + add_rule_key(rule, &rule->action, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "DEVPATH") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err("invalid DEVPATH operation\n"); + goto invalid; + } + add_rule_key(rule, &rule->devpath, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "KERNEL") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err("invalid KERNEL operation\n"); + goto invalid; + } + add_rule_key(rule, &rule->kernel, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "SUBSYSTEM") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err("invalid SUBSYSTEM operation\n"); + goto invalid; + } + /* bus, class, subsystem events should all be the same */ + if (strcmp(value, "subsystem") == 0 || + strcmp(value, "bus") == 0 || + strcmp(value, "class") == 0) { + if (strcmp(value, "bus") == 0 || strcmp(value, "class") == 0) + err("'%s' must be specified as 'subsystem' \n" + "please fix it in %s:%u", value, filename, lineno); + add_rule_key(rule, &rule->subsystem, operation, "subsystem|class|bus"); + } else + add_rule_key(rule, &rule->subsystem, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "DRIVER") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err("invalid DRIVER operation\n"); + goto invalid; + } + add_rule_key(rule, &rule->driver, operation, value); + valid = 1; + continue; + } + + if (strncasecmp(key, "ATTR{", sizeof("ATTR{")-1) == 0) { + attr = get_key_attribute(key + sizeof("ATTR")-1); + if (attr == NULL) { + err("error parsing ATTR attribute\n"); + goto invalid; + } + if (add_rule_key_pair(rule, &rule->attr, operation, attr, value) != 0) + goto invalid; + valid = 1; + continue; + } + + if (strcasecmp(key, "KERNELS") == 0 || + strcasecmp(key, "ID") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err("invalid KERNELS operation\n"); + goto invalid; + } + add_rule_key(rule, &rule->kernels, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "SUBSYSTEMS") == 0 || + strcasecmp(key, "BUS") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err("invalid SUBSYSTEMS operation\n"); + goto invalid; + } + add_rule_key(rule, &rule->subsystems, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "DRIVERS") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err("invalid DRIVERS operation\n"); + goto invalid; + } + add_rule_key(rule, &rule->drivers, operation, value); + valid = 1; + continue; + } + + if (strncasecmp(key, "ATTRS{", sizeof("ATTRS{")-1) == 0 || + strncasecmp(key, "SYSFS{", sizeof("SYSFS{")-1) == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err("invalid ATTRS operation\n"); + goto invalid; + } + attr = get_key_attribute(key + sizeof("ATTRS")-1); + if (attr == NULL) { + err("error parsing ATTRS attribute\n"); + goto invalid; + } + if (strncmp(attr, "device/", 7) == 0) + err("the 'device' link is deprecated and will be removed from a future kernel, \n" + "please fix it in %s:%u", filename, lineno); + else if (strstr(attr, "../") != NULL) + err("do not reference parent sysfs directories directly, that may break with a future kernel, \n" + "please fix it in %s:%u", filename, lineno); + if (add_rule_key_pair(rule, &rule->attrs, operation, attr, value) != 0) + goto invalid; + valid = 1; + continue; + } + + if (strncasecmp(key, "ENV{", sizeof("ENV{")-1) == 0) { + attr = get_key_attribute(key + sizeof("ENV")-1); + if (attr == NULL) { + err("error parsing ENV attribute\n"); + goto invalid; + } + if (strncmp(attr, "PHYSDEV", 7) == 0) + physdev = 1; + if (add_rule_key_pair(rule, &rule->env, operation, attr, value) != 0) + goto invalid; + valid = 1; + continue; + } + + if (strcasecmp(key, "PROGRAM") == 0) { + add_rule_key(rule, &rule->program, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "RESULT") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err("invalid RESULT operation\n"); + goto invalid; + } + add_rule_key(rule, &rule->result, operation, value); + valid = 1; + continue; + } + + if (strncasecmp(key, "IMPORT", sizeof("IMPORT")-1) == 0) { + attr = get_key_attribute(key + sizeof("IMPORT")-1); + if (attr != NULL && strstr(attr, "program")) { + dbg("IMPORT will be executed\n"); + rule->import_type = IMPORT_PROGRAM; + } else if (attr != NULL && strstr(attr, "file")) { + dbg("IMPORT will be included as file\n"); + rule->import_type = IMPORT_FILE; + } else if (attr != NULL && strstr(attr, "parent")) { + dbg("IMPORT will include the parent values\n"); + rule->import_type = IMPORT_PARENT; + } else { + /* figure it out if it is executable */ + char file[PATH_SIZE]; + char *pos; + struct stat statbuf; + + strlcpy(file, value, sizeof(file)); + pos = strchr(file, ' '); + if (pos) + pos[0] = '\0'; + + /* allow programs in /lib/udev called without the path */ + if (strchr(file, '/') == NULL) { + strlcpy(file, "/lib/udev/", sizeof(file)); + strlcat(file, value, sizeof(file)); + pos = strchr(file, ' '); + if (pos) + pos[0] = '\0'; + } + + dbg("IMPORT auto mode for '%s'\n", file); + if (!lstat(file, &statbuf) && (statbuf.st_mode & S_IXUSR)) { + dbg("IMPORT is executable, will be executed (autotype)\n"); + rule->import_type = IMPORT_PROGRAM; + } else { + dbg("IMPORT is not executable, will be included as file (autotype)\n"); + rule->import_type = IMPORT_FILE; + } + } + add_rule_key(rule, &rule->import, operation, value); + valid = 1; + continue; + } + + if (strncasecmp(key, "TEST", sizeof("TEST")-1) == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err("invalid TEST operation\n"); + goto invalid; + } + attr = get_key_attribute(key + sizeof("TEST")-1); + if (attr != NULL) + rule->test_mode_mask = strtol(attr, NULL, 8); + add_rule_key(rule, &rule->test, operation, value); + valid = 1; + continue; + } + + if (strncasecmp(key, "RUN", sizeof("RUN")-1) == 0) { + attr = get_key_attribute(key + sizeof("RUN")-1); + if (attr != NULL) { + if (strstr(attr, "ignore_error")) + rule->run_ignore_error = 1; + } + add_rule_key(rule, &rule->run, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "WAIT_FOR") == 0 || strcasecmp(key, "WAIT_FOR_SYSFS") == 0) { + add_rule_key(rule, &rule->wait_for, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "LABEL") == 0) { + add_rule_key(rule, &rule->label, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "GOTO") == 0) { + add_rule_key(rule, &rule->goto_label, operation, value); + valid = 1; + continue; + } + + if (strncasecmp(key, "NAME", sizeof("NAME")-1) == 0) { + attr = get_key_attribute(key + sizeof("NAME")-1); + if (attr != NULL) { + if (strstr(attr, "all_partitions") != NULL) { + dbg("creation of partition nodes requested\n"); + rule->partitions = DEFAULT_PARTITIONS_COUNT; + } + if (strstr(attr, "ignore_remove") != NULL) { + dbg("remove event should be ignored\n"); + rule->ignore_remove = 1; + } + } + if (value[0] == '\0') + dbg("name empty, node creation supressed\n"); + add_rule_key(rule, &rule->name, operation, value); + continue; + } + + if (strcasecmp(key, "SYMLINK") == 0) { + if (operation == KEY_OP_MATCH || + operation == KEY_OP_NOMATCH) + add_rule_key(rule, &rule->symlink_match, operation, value); + else + add_rule_key(rule, &rule->symlink, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "OWNER") == 0) { + valid = 1; + if (rules->resolve_names && (!strchr(value, '$') && !strchr(value, '%'))) { + char *endptr; + strtoul(value, &endptr, 10); + if (endptr[0] != '\0') { + char owner[32]; + uid_t uid = lookup_user(value); + dbg("replacing username='%s' by id=%i\n", value, uid); + sprintf(owner, "%u", (unsigned int) uid); + add_rule_key(rule, &rule->owner, operation, owner); + continue; + } + } + + add_rule_key(rule, &rule->owner, operation, value); + continue; + } + + if (strcasecmp(key, "GROUP") == 0) { + valid = 1; + if (rules->resolve_names && (!strchr(value, '$') && !strchr(value, '%'))) { + char *endptr; + strtoul(value, &endptr, 10); + if (endptr[0] != '\0') { + char group[32]; + gid_t gid = lookup_group(value); + dbg("replacing groupname='%s' by id=%i\n", value, gid); + sprintf(group, "%u", (unsigned int) gid); + add_rule_key(rule, &rule->group, operation, group); + continue; + } + } + + add_rule_key(rule, &rule->group, operation, value); + continue; + } + + if (strcasecmp(key, "MODE") == 0) { + add_rule_key(rule, &rule->mode, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "OPTIONS") == 0) { + const char *pos; + + if (strstr(value, "last_rule") != NULL) { + dbg("last rule to be applied\n"); + rule->last_rule = 1; + } + if (strstr(value, "ignore_device") != NULL) { + dbg("device should be ignored\n"); + rule->ignore_device = 1; + } + if (strstr(value, "ignore_remove") != NULL) { + dbg("remove event should be ignored\n"); + rule->ignore_remove = 1; + } + pos = strstr(value, "link_priority="); + if (pos != NULL) { + rule->link_priority = atoi(&pos[strlen("link_priority=")]); + dbg("link priority=%i\n", rule->link_priority); + } + pos = strstr(value, "event_timeout="); + if (pos != NULL) { + rule->event_timeout = atoi(&pos[strlen("event_timeout=")]); + dbg("event timout=%i\n", rule->event_timeout); + } + pos = strstr(value, "string_escape="); + if (pos != NULL) { + pos = &pos[strlen("string_escape=")]; + if (strncmp(pos, "none", strlen("none")) == 0) + rule->string_escape = ESCAPE_NONE; + else if (strncmp(pos, "replace", strlen("replace")) == 0) + rule->string_escape = ESCAPE_REPLACE; + } + if (strstr(value, "all_partitions") != NULL) { + dbg("creation of partition nodes requested\n"); + rule->partitions = DEFAULT_PARTITIONS_COUNT; + } + valid = 1; + continue; + } + + err("unknown key '%s' in %s:%u\n", key, filename, lineno); + } + + if (physdev && rule->wait_for.operation == KEY_OP_UNSET) + err("PHYSDEV* values are deprecated and will be removed from a future kernel, \n" + "please fix it in %s:%u", filename, lineno); + + /* skip line if not any valid key was found */ + if (!valid) + goto invalid; + + /* grow buffer and add rule */ + rule_size = sizeof(struct udev_rule) + rule->bufsize; + padding = (sizeof(size_t) - rule_size % sizeof(size_t)) % sizeof(size_t); + dbg("add %zi padding bytes\n", padding); + rule_size += padding; + rule->bufsize += padding; + + rules->buf = realloc(rules->buf, rules->bufsize + rule_size); + if (!rules->buf) { + err("realloc failed\n"); + goto exit; + } + dbg("adding rule to offset %zi\n", rules->bufsize); + memcpy(rules->buf + rules->bufsize, rule, rule_size); + rules->bufsize += rule_size; +exit: + return 0; + +invalid: + err("invalid rule '%s:%u'\n", filename, lineno); + return -1; +} + +static int parse_file(struct udev_rules *rules, const char *filename) +{ + char line[LINE_SIZE]; + char *bufline; + unsigned int lineno; + char *buf; + size_t bufsize; + size_t cur; + size_t count; + int retval = 0; + + if (file_map(filename, &buf, &bufsize) != 0) { + err("can't open '%s' as rules file: %s\n", filename, strerror(errno)); + return -1; + } + info("reading '%s' as rules file\n", filename); + + /* loop through the whole file */ + cur = 0; + lineno = 0; + while (cur < bufsize) { + unsigned int i, j; + + count = buf_get_line(buf, bufsize, cur); + bufline = &buf[cur]; + cur += count+1; + lineno++; + + /* eat the whitespace */ + while ((count > 0) && isspace(bufline[0])) { + bufline++; + count--; + } + if (count == 0) + continue; + + /* see if this is a comment */ + if (bufline[0] == COMMENT_CHARACTER) + continue; + + if (count >= sizeof(line)) { + err("line too long, rule skipped '%s:%u'\n", filename, lineno); + continue; + } + + /* skip backslash and newline from multiline rules */ + for (i = j = 0; i < count; i++) { + if (bufline[i] == '\\' && bufline[i+1] == '\n') + continue; + + line[j++] = bufline[i]; + } + line[j] = '\0'; + + dbg("read '%s'\n", line); + add_to_rules(rules, line, filename, lineno); + } + + file_unmap(buf, bufsize); + return retval; +} + +int udev_rules_init(struct udev_rules *rules, int resolve_names) +{ + struct stat statbuf; + char filename[PATH_MAX]; + LIST_HEAD(name_list); + LIST_HEAD(sort_list); + struct name_entry *name_loop, *name_tmp; + struct name_entry *sort_loop, *sort_tmp; + int retval = 0; + + memset(rules, 0x00, sizeof(struct udev_rules)); + rules->resolve_names = resolve_names; + + if (udev_rules_dir[0] != '\0') { + /* custom rules location for testing */ + add_matching_files(&name_list, udev_rules_dir, RULESFILE_SUFFIX); + } else { + /* read default rules */ + add_matching_files(&name_list, RULES_LIB_DIR, RULESFILE_SUFFIX); + + /* read user/custom rules */ + add_matching_files(&sort_list, RULES_ETC_DIR, RULESFILE_SUFFIX); + + /* read dynamic/temporary rules */ + strlcpy(filename, udev_root, sizeof(filename)); + strlcat(filename, "/"RULES_DYN_DIR, sizeof(filename)); + if (stat(filename, &statbuf) != 0) { + create_path(filename); + selinux_setfscreatecon(filename, NULL, S_IFDIR|0755); + mkdir(filename, 0755); + selinux_resetfscreatecon(); + } + add_matching_files(&sort_list, filename, RULESFILE_SUFFIX); + + /* sort all rules files by basename into list of files */ + list_for_each_entry_safe(sort_loop, sort_tmp, &sort_list, node) { + const char *sort_base = strrchr(sort_loop->name, '/'); + + if (sort_base == NULL) + continue; + + list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) { + const char *name_base = strrchr(name_loop->name, '/'); + + if (name_base == NULL) + continue; + + if (strcmp(name_base, sort_base) > 0) + break; + } + list_move_tail(&sort_loop->node, &name_loop->node); + } + } + + /* parse list of files */ + list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) { + if (stat(name_loop->name, &statbuf) == 0) { + if (statbuf.st_size) + parse_file(rules, name_loop->name); + else + dbg("empty rules file '%s'\n", name_loop->name); + } else + err("could not read '%s': %s\n", name_loop->name, strerror(errno)); + list_del(&name_loop->node); + free(name_loop); + } + + return retval; +} + +void udev_rules_cleanup(struct udev_rules *rules) +{ + if (rules->buf) { + free(rules->buf); + rules->buf = NULL; + } +} + diff --git a/udev/udev_selinux.c b/udev/udev_selinux.c new file mode 100644 index 0000000000..eec950194c --- /dev/null +++ b/udev/udev_selinux.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2004 Daniel Walsh + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udev_selinux.h" + +static security_context_t prev_scontext = NULL; + +static int is_selinux_running(void) +{ + static int selinux_enabled = -1; + + if (selinux_enabled == -1) + selinux_enabled = (is_selinux_enabled() > 0); + + dbg("selinux=%i\n", selinux_enabled); + return selinux_enabled; +} + +static char *get_media(const char *devname, int mode) +{ + FILE *fp; + char procfile[PATH_MAX]; + char mediabuf[256]; + int size; + char *media = NULL; + + if (!(mode & S_IFBLK)) + return NULL; + + snprintf(procfile, PATH_MAX, "/proc/ide/%s/media", devname); + procfile[PATH_MAX-1] = '\0'; + + fp = fopen(procfile, "r"); + if (!fp) + goto out; + + if (fgets(mediabuf, sizeof(mediabuf), fp) == NULL) + goto close_out; + + size = strlen(mediabuf); + while (size-- > 0) { + if (isspace(mediabuf[size])) { + mediabuf[size] = '\0'; + } else { + break; + } + } + + media = strdup(mediabuf); + info("selinux_get_media(%s)='%s'\n", devname, media); + +close_out: + fclose(fp); +out: + return media; +} + +void selinux_setfilecon(const char *file, const char *devname, unsigned int mode) +{ + if (is_selinux_running()) { + security_context_t scontext = NULL; + char *media; + int ret = -1; + + if (devname) { + media = get_media(devname, mode); + if (media) { + ret = matchmediacon(media, &scontext); + free(media); + } + } + + if (ret < 0) + if (matchpathcon(file, mode, &scontext) < 0) { + err("matchpathcon(%s) failed\n", file); + return; + } + + if (lsetfilecon(file, scontext) < 0) + err("setfilecon %s failed: %s\n", file, strerror(errno)); + + freecon(scontext); + } +} + +void selinux_setfscreatecon(const char *file, const char *devname, unsigned int mode) +{ + if (is_selinux_running()) { + security_context_t scontext = NULL; + char *media; + int ret = -1; + + if (devname) { + media = get_media(devname, mode); + if (media) { + ret = matchmediacon(media, &scontext); + free(media); + } + } + + if (ret < 0) + if (matchpathcon(file, mode, &scontext) < 0) { + err("matchpathcon(%s) failed\n", file); + return; + } + + if (setfscreatecon(scontext) < 0) + err("setfscreatecon %s failed: %s\n", file, strerror(errno)); + + freecon(scontext); + } +} + +void selinux_resetfscreatecon(void) +{ + if (is_selinux_running()) { + if (setfscreatecon(prev_scontext) < 0) + err("setfscreatecon failed: %s\n", strerror(errno)); + } +} + +void selinux_init(void) +{ + /* + * record the present security context, for file-creation + * restoration creation purposes. + */ + if (is_selinux_running()) { + if (!udev_root[0]) + err("selinux_init: udev_root not set\n"); + matchpathcon_init_prefix(NULL, udev_root); + if (getfscreatecon(&prev_scontext) < 0) { + err("getfscreatecon failed\n"); + prev_scontext = NULL; + } + } +} + +void selinux_exit(void) +{ + if (is_selinux_running() && prev_scontext) { + freecon(prev_scontext); + prev_scontext = NULL; + } +} diff --git a/udev/udev_selinux.h b/udev/udev_selinux.h new file mode 100644 index 0000000000..73567d6cfe --- /dev/null +++ b/udev/udev_selinux.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2004 Daniel Walsh + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef _UDEV_SELINUX_H +#define _UDEV_SELINUX_H + +#ifdef USE_SELINUX + +extern void selinux_setfilecon(const char *file, const char *devname, unsigned int mode); +extern void selinux_setfscreatecon(const char *file, const char *devname, unsigned int mode); +extern void selinux_resetfscreatecon(void); +extern void selinux_init(void); +extern void selinux_exit(void); + +#else + +static inline void selinux_setfilecon(const char *file, const char *devname, unsigned int mode) {} +static inline void selinux_setfscreatecon(const char *file, const char *devname, unsigned int mode) {} +static inline void selinux_resetfscreatecon(void) {} +static inline void selinux_init(void) {} +static inline void selinux_exit(void) {} + +#endif /* USE_SELINUX */ +#endif /* _UDEV_USE_SELINUX */ diff --git a/udev/udev_sysdeps.c b/udev/udev_sysdeps.c new file mode 100644 index 0000000000..9447cca936 --- /dev/null +++ b/udev/udev_sysdeps.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2003 Greg Kroah-Hartman + * Copyright (C) 2005-2006 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +#ifdef __GLIBC__ +size_t strlcpy(char *dst, const char *src, size_t size) +{ + size_t bytes = 0; + char *q = dst; + const char *p = src; + char ch; + + while ((ch = *p++)) { + if (bytes+1 < size) + *q++ = ch; + bytes++; + } + + /* If size == 0 there is no space for a final null... */ + if (size) + *q = '\0'; + return bytes; +} + +size_t strlcat(char *dst, const char *src, size_t size) +{ + size_t bytes = 0; + char *q = dst; + const char *p = src; + char ch; + + while (bytes < size && *q) { + q++; + bytes++; + } + if (bytes == size) + return (bytes + strlen(src)); + + while ((ch = *p++)) { + if (bytes+1 < size) + *q++ = ch; + bytes++; + } + + *q = '\0'; + return bytes; +} +#endif /* __GLIBC__ */ diff --git a/udev/udev_sysdeps.h b/udev/udev_sysdeps.h new file mode 100644 index 0000000000..d4f03686af --- /dev/null +++ b/udev/udev_sysdeps.h @@ -0,0 +1,172 @@ +/* + * wrapping of libc features and kernel interfaces + * + * Copyright (C) 2005-2006 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _UDEV_SYSDEPS_H_ +#define _UDEV_SYSDEPS_H_ + +#include +#include +#include + +/* needed until Inotify! syscalls reach glibc */ +#include +#ifndef __NR_inotify_init +#if defined(__i386__) +# define __NR_inotify_init 291 +# define __NR_inotify_add_watch 292 +# define __NR_inotify_rm_watch 293 +#elif defined(__x86_64__) +# define __NR_inotify_init 253 +# define __NR_inotify_add_watch 254 +# define __NR_inotify_rm_watch 255 +#elif defined(__powerpc__) || defined(__powerpc64__) +# define __NR_inotify_init 275 +# define __NR_inotify_add_watch 276 +# define __NR_inotify_rm_watch 277 +#elif defined (__ia64__) +# define __NR_inotify_init 1277 +# define __NR_inotify_add_watch 1278 +# define __NR_inotify_rm_watch 1279 +#elif defined (__s390__) +# define __NR_inotify_init 284 +# define __NR_inotify_add_watch 285 +# define __NR_inotify_rm_watch 286 +#elif defined (__alpha__) +# define __NR_inotify_init 444 +# define __NR_inotify_add_watch 445 +# define __NR_inotify_rm_watch 446 +#elif defined (__sparc__) || defined (__sparc64__) +# define __NR_inotify_init 151 +# define __NR_inotify_add_watch 152 +# define __NR_inotify_rm_watch 156 +#elif defined (__arm__) +# define __NR_inotify_init __NR_SYSCALL_BASE+316 +# define __NR_inotify_add_watch __NR_SYSCALL_BASE+317 +# define __NR_inotify_rm_watch __NR_SYSCALL_BASE+318 +#elif defined (__sh__) +# define __NR_inotify_init 290 +# define __NR_inotify_add_watch 291 +# define __NR_inotify_rm_watch 292 +#elif defined (__m32r__) +# define __NR_inotify_init 290 +# define __NR_inotify_add_watch 291 +# define __NR_inotify_rm_watch 292 +#elif defined (__hppa__) +# define __NR_inotify_init 269 +# define __NR_inotify_add_watch 270 +# define __NR_inotify_rm_watch 271 +#elif defined (__mips__) +# include +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define __NR_Linux 4000 +# define __NR_inotify_init (__NR_Linux + 284) +# define __NR_inotify_add_watch (__NR_Linux + 285) +# define __NR_inotify_rm_watch (__NR_Linux + 286) +# elif _MIPS_SIM == _MIPS_SIM_ABI64 +# define __NR_Linux 5000 +# define __NR_inotify_init (__NR_Linux + 243) +# define __NR_inotify_add_watch (__NR_Linux + 244) +# define __NR_inotify_rm_watch (__NR_Linux + 245) +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# define __NR_Linux 6000 +# define __NR_inotify_init (__NR_Linux + 247) +# define __NR_inotify_add_watch (__NR_Linux + 248) +# define __NR_inotify_rm_watch (__NR_Linux + 249) +# endif +#else +#warning "inotify unsupported on this architecture!" +#endif +#endif /* __NR_inotify_init */ + +/* dummy if we don't have the syscalls defined */ +#ifndef __NR_inotify_init +static inline int inotify_init(void) +{ + return -1; +} + +static inline int inotify_add_watch(int fd, const char *name, uint32_t mask) +{ + return -1; +} +#else +/* needed until /usr/include/sys/inotify.h is working */ +#ifndef __GLIBC__ +#include +#else +static inline int inotify_init(void) +{ + return syscall(__NR_inotify_init); +} + +static inline int inotify_add_watch(int fd, const char *name, uint32_t mask) +{ + return syscall(__NR_inotify_add_watch, fd, name, mask); +} +#endif /* __GLIBC__ */ +#endif /* __NR_inotify_init */ + +#ifndef IN_CREATE +#define IN_CREATE 0x00000100 /* Subfile was created */ +#define IN_MOVED_FROM 0x00000040 /* File was moved from X */ +#define IN_MOVED_TO 0x00000080 /* File was moved to Y */ +#define IN_DELETE 0x00000200 /* Subfile was deleted */ +#define IN_CLOSE_WRITE 0x00000008 /* Writtable file was closed */ +#define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO) /* moves */ +#endif /* IN_CREATE */ + +/* needed for our signal handlers to work */ +#undef asmlinkage +#ifdef __i386__ +#define asmlinkage __attribute__((regparm(0))) +#else +#define asmlinkage +#endif /* __i386__ */ + +/* headers are broken on some architectures */ +#ifndef __FD_SET +#define __FD_SET(d, set) ((set)->fds_bits[__FDELT(d)] |= __FDMASK(d)) +#endif +#ifndef __FD_CLR +#define __FD_CLR(d, set) ((set)->fds_bits[__FDELT(d)] &= ~__FDMASK(d)) +#endif +#ifndef __FD_ISSET +#define __FD_ISSET(d, set) (((set)->fds_bits[__FDELT(d)] & __FDMASK(d)) != 0) +#endif +#ifndef __FD_ZERO +#define __FD_ZERO(set) ((void) memset ((void*) (set), 0, sizeof (fd_set))) +#endif + +#ifndef NETLINK_KOBJECT_UEVENT +#define NETLINK_KOBJECT_UEVENT 15 +#endif + +#ifndef SO_RCVBUFFORCE +#if defined(__alpha__) || defined(__hppa__) || defined(__sparc__) || defined(__sparc_v9__) +#define SO_RCVBUFFORCE 0x100b +#else +#define SO_RCVBUFFORCE 33 +#endif +#endif + +extern size_t strlcpy(char *dst, const char *src, size_t size); +extern size_t strlcat(char *dst, const char *src, size_t size); + +#endif diff --git a/udev/udev_sysfs.c b/udev/udev_sysfs.c new file mode 100644 index 0000000000..c4cd4ab75d --- /dev/null +++ b/udev/udev_sysfs.c @@ -0,0 +1,509 @@ +/* + * Copyright (C) 2005-2006 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +char sysfs_path[PATH_SIZE]; + +/* device cache */ +static LIST_HEAD(dev_list); + +/* attribute value cache */ +static LIST_HEAD(attr_list); +struct sysfs_attr { + struct list_head node; + char path[PATH_SIZE]; + char *value; /* points to value_local if value is cached */ + char value_local[NAME_SIZE]; +}; + +int sysfs_init(void) +{ + const char *env; + + env = getenv("SYSFS_PATH"); + if (env) { + strlcpy(sysfs_path, env, sizeof(sysfs_path)); + remove_trailing_chars(sysfs_path, '/'); + } else + strlcpy(sysfs_path, "/sys", sizeof(sysfs_path)); + dbg("sysfs_path='%s'\n", sysfs_path); + + INIT_LIST_HEAD(&dev_list); + INIT_LIST_HEAD(&attr_list); + return 0; +} + +void sysfs_cleanup(void) +{ + struct sysfs_attr *attr_loop; + struct sysfs_attr *attr_temp; + struct sysfs_device *dev_loop; + struct sysfs_device *dev_temp; + + list_for_each_entry_safe(attr_loop, attr_temp, &attr_list, node) { + list_del(&attr_loop->node); + free(attr_loop); + } + + list_for_each_entry_safe(dev_loop, dev_temp, &dev_list, node) { + list_del(&dev_loop->node); + free(dev_loop); + } +} + +void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, + const char *subsystem, const char *driver) +{ + char *pos; + + strlcpy(dev->devpath, devpath, sizeof(dev->devpath)); + if (subsystem != NULL) + strlcpy(dev->subsystem, subsystem, sizeof(dev->subsystem)); + if (driver != NULL) + strlcpy(dev->driver, driver, sizeof(dev->driver)); + + /* set kernel name */ + pos = strrchr(dev->devpath, '/'); + if (pos == NULL) + return; + strlcpy(dev->kernel, &pos[1], sizeof(dev->kernel)); + dbg("kernel='%s'\n", dev->kernel); + + /* some devices have '!' in their name, change that to '/' */ + pos = dev->kernel; + while (pos[0] != '\0') { + if (pos[0] == '!') + pos[0] = '/'; + pos++; + } + + /* get kernel number */ + pos = &dev->kernel[strlen(dev->kernel)]; + while (isdigit(pos[-1])) + pos--; + strlcpy(dev->kernel_number, pos, sizeof(dev->kernel_number)); + dbg("kernel_number='%s'\n", dev->kernel_number); +} + +int sysfs_resolve_link(char *devpath, size_t size) +{ + char link_path[PATH_SIZE]; + char link_target[PATH_SIZE]; + int len; + int i; + int back; + + strlcpy(link_path, sysfs_path, sizeof(link_path)); + strlcat(link_path, devpath, sizeof(link_path)); + len = readlink(link_path, link_target, sizeof(link_target)); + if (len <= 0) + return -1; + link_target[len] = '\0'; + dbg("path link '%s' points to '%s'\n", devpath, link_target); + + for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++) + ; + dbg("base '%s', tail '%s', back %i\n", devpath, &link_target[back * 3], back); + for (i = 0; i <= back; i++) { + char *pos = strrchr(devpath, '/'); + + if (pos == NULL) + return -1; + pos[0] = '\0'; + } + dbg("after moving back '%s'\n", devpath); + strlcat(devpath, "/", size); + strlcat(devpath, &link_target[back * 3], size); + return 0; +} + +struct sysfs_device *sysfs_device_get(const char *devpath) +{ + char path[PATH_SIZE]; + char devpath_real[PATH_SIZE]; + struct sysfs_device *dev; + struct sysfs_device *dev_loop; + struct stat statbuf; + char link_path[PATH_SIZE]; + char link_target[PATH_SIZE]; + int len; + char *pos; + + /* we handle only these devpathes */ + if (devpath != NULL && + strncmp(devpath, "/devices/", 9) != 0 && + strncmp(devpath, "/subsystem/", 11) != 0 && + strncmp(devpath, "/module/", 8) != 0 && + strncmp(devpath, "/bus/", 5) != 0 && + strncmp(devpath, "/class/", 7) != 0 && + strncmp(devpath, "/block/", 7) != 0) + return NULL; + + dbg("open '%s'\n", devpath); + strlcpy(devpath_real, devpath, sizeof(devpath_real)); + remove_trailing_chars(devpath_real, '/'); + if (devpath[0] == '\0' ) + return NULL; + + /* look for device already in cache (we never put an untranslated path in the cache) */ + list_for_each_entry(dev_loop, &dev_list, node) { + if (strcmp(dev_loop->devpath, devpath_real) == 0) { + dbg("found in cache '%s'\n", dev_loop->devpath); + return dev_loop; + } + } + + /* if we got a link, resolve it to the real device */ + strlcpy(path, sysfs_path, sizeof(path)); + strlcat(path, devpath_real, sizeof(path)); + if (lstat(path, &statbuf) != 0) { + dbg("stat '%s' failed: %s\n", path, strerror(errno)); + return NULL; + } + if (S_ISLNK(statbuf.st_mode)) { + if (sysfs_resolve_link(devpath_real, sizeof(devpath_real)) != 0) + return NULL; + + /* now look for device in cache after path translation */ + list_for_each_entry(dev_loop, &dev_list, node) { + if (strcmp(dev_loop->devpath, devpath_real) == 0) { + dbg("found in cache '%s'\n", dev_loop->devpath); + return dev_loop; + } + } + } + + /* it is a new device */ + dbg("new uncached device '%s'\n", devpath_real); + dev = malloc(sizeof(struct sysfs_device)); + if (dev == NULL) + return NULL; + memset(dev, 0x00, sizeof(struct sysfs_device)); + + sysfs_device_set_values(dev, devpath_real, NULL, NULL); + + /* get subsystem name */ + strlcpy(link_path, sysfs_path, sizeof(link_path)); + strlcat(link_path, dev->devpath, sizeof(link_path)); + strlcat(link_path, "/subsystem", sizeof(link_path)); + len = readlink(link_path, link_target, sizeof(link_target)); + if (len > 0) { + /* get subsystem from "subsystem" link */ + link_target[len] = '\0'; + dbg("subsystem link '%s' points to '%s'\n", link_path, link_target); + pos = strrchr(link_target, '/'); + if (pos != NULL) + strlcpy(dev->subsystem, &pos[1], sizeof(dev->subsystem)); + } else if (strstr(dev->devpath, "/drivers/") != NULL) { + strlcpy(dev->subsystem, "drivers", sizeof(dev->subsystem)); + } else if (strncmp(dev->devpath, "/module/", 8) == 0) { + strlcpy(dev->subsystem, "module", sizeof(dev->subsystem)); + } else if (strncmp(dev->devpath, "/subsystem/", 11) == 0) { + pos = strrchr(dev->devpath, '/'); + if (pos == &dev->devpath[10]) + strlcpy(dev->subsystem, "subsystem", sizeof(dev->subsystem)); + } else if (strncmp(dev->devpath, "/class/", 7) == 0) { + pos = strrchr(dev->devpath, '/'); + if (pos == &dev->devpath[6]) + strlcpy(dev->subsystem, "subsystem", sizeof(dev->subsystem)); + } else if (strncmp(dev->devpath, "/bus/", 5) == 0) { + pos = strrchr(dev->devpath, '/'); + if (pos == &dev->devpath[4]) + strlcpy(dev->subsystem, "subsystem", sizeof(dev->subsystem)); + } + + /* get driver name */ + strlcpy(link_path, sysfs_path, sizeof(link_path)); + strlcat(link_path, dev->devpath, sizeof(link_path)); + strlcat(link_path, "/driver", sizeof(link_path)); + len = readlink(link_path, link_target, sizeof(link_target)); + if (len > 0) { + link_target[len] = '\0'; + dbg("driver link '%s' points to '%s'\n", link_path, link_target); + pos = strrchr(link_target, '/'); + if (pos != NULL) + strlcpy(dev->driver, &pos[1], sizeof(dev->driver)); + } + + dbg("add to cache 'devpath=%s', subsystem='%s', driver='%s'\n", dev->devpath, dev->subsystem, dev->driver); + list_add(&dev->node, &dev_list); + + return dev; +} + +struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev) +{ + char parent_devpath[PATH_SIZE]; + char *pos; + + dbg("open '%s'\n", dev->devpath); + + /* look if we already know the parent */ + if (dev->parent != NULL) + return dev->parent; + + strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath)); + dbg("'%s'\n", parent_devpath); + + /* strip last element */ + pos = strrchr(parent_devpath, '/'); + if (pos == NULL || pos == parent_devpath) + return NULL; + pos[0] = '\0'; + + if (strncmp(parent_devpath, "/class", 6) == 0) { + pos = strrchr(parent_devpath, '/'); + if (pos == &parent_devpath[6] || pos == parent_devpath) { + dbg("/class top level, look for device link\n"); + goto device_link; + } + } + if (strcmp(parent_devpath, "/block") == 0) { + dbg("/block top level, look for device link\n"); + goto device_link; + } + + /* are we at the top level? */ + pos = strrchr(parent_devpath, '/'); + if (pos == NULL || pos == parent_devpath) + return NULL; + + /* get parent and remember it */ + dev->parent = sysfs_device_get(parent_devpath); + return dev->parent; + +device_link: + strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath)); + strlcat(parent_devpath, "/device", sizeof(parent_devpath)); + if (sysfs_resolve_link(parent_devpath, sizeof(parent_devpath)) != 0) + return NULL; + + /* get parent and remember it */ + dev->parent = sysfs_device_get(parent_devpath); + return dev->parent; +} + +struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem) +{ + struct sysfs_device *dev_parent; + + dev_parent = sysfs_device_get_parent(dev); + while (dev_parent != NULL) { + if (strcmp(dev_parent->subsystem, subsystem) == 0) + return dev_parent; + dev_parent = sysfs_device_get_parent(dev_parent); + } + return NULL; +} + +char *sysfs_attr_get_value(const char *devpath, const char *attr_name) +{ + char path_full[PATH_SIZE]; + const char *path; + char value[NAME_SIZE]; + struct sysfs_attr *attr_loop; + struct sysfs_attr *attr; + struct stat statbuf; + int fd; + ssize_t size; + size_t sysfs_len; + + dbg("open '%s'/'%s'\n", devpath, attr_name); + sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full)); + if(sysfs_len >= sizeof(path_full)) + sysfs_len = sizeof(path_full) - 1; + path = &path_full[sysfs_len]; + strlcat(path_full, devpath, sizeof(path_full)); + strlcat(path_full, "/", sizeof(path_full)); + strlcat(path_full, attr_name, sizeof(path_full)); + + /* look for attribute in cache */ + list_for_each_entry(attr_loop, &attr_list, node) { + if (strcmp(attr_loop->path, path) == 0) { + dbg("found in cache '%s'\n", attr_loop->path); + return attr_loop->value; + } + } + + /* store attribute in cache (also negatives are kept in cache) */ + dbg("new uncached attribute '%s'\n", path_full); + attr = malloc(sizeof(struct sysfs_attr)); + if (attr == NULL) + return NULL; + memset(attr, 0x00, sizeof(struct sysfs_attr)); + strlcpy(attr->path, path, sizeof(attr->path)); + dbg("add to cache '%s'\n", path_full); + list_add(&attr->node, &attr_list); + + if (lstat(path_full, &statbuf) != 0) { + dbg("stat '%s' failed: %s\n", path_full, strerror(errno)); + goto out; + } + + if (S_ISLNK(statbuf.st_mode)) { + /* links return the last element of the target path */ + char link_target[PATH_SIZE]; + int len; + const char *pos; + + len = readlink(path_full, link_target, sizeof(link_target)); + if (len > 0) { + link_target[len] = '\0'; + pos = strrchr(link_target, '/'); + if (pos != NULL) { + dbg("cache '%s' with link value '%s'\n", path_full, value); + strlcpy(attr->value_local, &pos[1], sizeof(attr->value_local)); + attr->value = attr->value_local; + } + } + goto out; + } + + /* skip directories */ + if (S_ISDIR(statbuf.st_mode)) + goto out; + + /* skip non-readable files */ + if ((statbuf.st_mode & S_IRUSR) == 0) + goto out; + + /* read attribute value */ + fd = open(path_full, O_RDONLY); + if (fd < 0) { + dbg("attribute '%s' can not be opened\n", path_full); + goto out; + } + size = read(fd, value, sizeof(value)); + close(fd); + if (size < 0) + goto out; + if (size == sizeof(value)) + goto out; + + /* got a valid value, store and return it */ + value[size] = '\0'; + remove_trailing_chars(value, '\n'); + dbg("cache '%s' with attribute value '%s'\n", path_full, value); + strlcpy(attr->value_local, value, sizeof(attr->value_local)); + attr->value = attr->value_local; + +out: + return attr->value; +} + +int sysfs_lookup_devpath_by_subsys_id(char *devpath_full, size_t len, const char *subsystem, const char *id) +{ + size_t sysfs_len; + char path_full[PATH_SIZE]; + char *path; + struct stat statbuf; + + sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full)); + path = &path_full[sysfs_len]; + + if (strcmp(subsystem, "subsystem") == 0) { + strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len); + strlcat(path, id, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; + + strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len); + strlcat(path, id, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; + goto out; + + strlcpy(path, "/class/", sizeof(path_full) - sysfs_len); + strlcat(path, id, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; + } + + if (strcmp(subsystem, "module") == 0) { + strlcpy(path, "/module/", sizeof(path_full) - sysfs_len); + strlcat(path, id, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; + goto out; + } + + if (strcmp(subsystem, "drivers") == 0) { + char subsys[NAME_SIZE]; + char *driver; + + strlcpy(subsys, id, sizeof(subsys)); + driver = strchr(subsys, ':'); + if (driver != NULL) { + driver[0] = '\0'; + driver = &driver[1]; + strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len); + strlcat(path, subsys, sizeof(path_full) - sysfs_len); + strlcat(path, "/drivers/", sizeof(path_full) - sysfs_len); + strlcat(path, driver, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; + + strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len); + strlcat(path, subsys, sizeof(path_full) - sysfs_len); + strlcat(path, "/drivers/", sizeof(path_full) - sysfs_len); + strlcat(path, driver, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; + } + goto out; + } + + strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len); + strlcat(path, subsystem, sizeof(path_full) - sysfs_len); + strlcat(path, "/devices/", sizeof(path_full) - sysfs_len); + strlcat(path, id, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; + + strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len); + strlcat(path, subsystem, sizeof(path_full) - sysfs_len); + strlcat(path, "/devices/", sizeof(path_full) - sysfs_len); + strlcat(path, id, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; + + strlcpy(path, "/class/", sizeof(path_full) - sysfs_len); + strlcat(path, subsystem, sizeof(path_full) - sysfs_len); + strlcat(path, "/", sizeof(path_full) - sysfs_len); + strlcat(path, id, sizeof(path_full) - sysfs_len); + if (stat(path_full, &statbuf) == 0) + goto found; +out: + return 0; +found: + if (S_ISLNK(statbuf.st_mode)) + sysfs_resolve_link(path, sizeof(path_full) - sysfs_len); + strlcpy(devpath_full, path, len); + return 1; +} diff --git a/udev/udev_utils.c b/udev/udev_utils.c new file mode 100644 index 0000000000..00b67dadc6 --- /dev/null +++ b/udev/udev_utils.c @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2004-2005 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + + +int log_priority(const char *priority) +{ + char *endptr; + int prio; + + prio = strtol(priority, &endptr, 10); + if (endptr[0] == '\0') + return prio; + if (strncasecmp(priority, "err", 3) == 0) + return LOG_ERR; + if (strcasecmp(priority, "info") == 0) + return LOG_INFO; + if (strcasecmp(priority, "debug") == 0) + return LOG_DEBUG; + if (string_is_true(priority)) + return LOG_ERR; + + return 0; +} + +struct name_entry *name_list_add(struct list_head *name_list, const char *name, int sort) +{ + struct name_entry *name_loop; + struct name_entry *name_new; + + /* avoid duplicate entries */ + list_for_each_entry(name_loop, name_list, node) { + if (strcmp(name_loop->name, name) == 0) { + dbg("'%s' is already in the list\n", name); + return name_loop; + } + } + + if (sort) + list_for_each_entry(name_loop, name_list, node) { + if (strcmp(name_loop->name, name) > 0) + break; + } + + name_new = malloc(sizeof(struct name_entry)); + if (name_new == NULL) + return NULL; + + strlcpy(name_new->name, name, sizeof(name_new->name)); + dbg("adding '%s'\n", name_new->name); + list_add_tail(&name_new->node, &name_loop->node); + + return name_new; +} + +struct name_entry *name_list_key_add(struct list_head *name_list, const char *key, const char *value) +{ + struct name_entry *name_loop; + struct name_entry *name_new; + + list_for_each_entry(name_loop, name_list, node) { + if (strncmp(name_loop->name, key, strlen(key)) == 0) { + dbg("key already present '%s', replace it\n", name_loop->name); + snprintf(name_loop->name, sizeof(name_loop->name), "%s=%s", key, value); + name_loop->name[sizeof(name_loop->name)-1] = '\0'; + return name_loop; + } + } + + name_new = malloc(sizeof(struct name_entry)); + if (name_new == NULL) + return NULL; + + snprintf(name_new->name, sizeof(name_new->name), "%s=%s", key, value); + name_new->name[sizeof(name_new->name)-1] = '\0'; + dbg("adding '%s'\n", name_new->name); + list_add_tail(&name_new->node, &name_loop->node); + + return name_new; +} + +int name_list_key_remove(struct list_head *name_list, const char *key) +{ + struct name_entry *name_loop; + struct name_entry *name_tmp; + size_t keylen = strlen(key); + int retval = 0; + + list_for_each_entry_safe(name_loop, name_tmp, name_list, node) { + if (strncmp(name_loop->name, key, keylen) != 0) + continue; + if (name_loop->name[keylen] != '=') + continue; + list_del(&name_loop->node); + free(name_loop); + retval = 1; + break; + } + return retval; +} + +void name_list_cleanup(struct list_head *name_list) +{ + struct name_entry *name_loop; + struct name_entry *name_tmp; + + list_for_each_entry_safe(name_loop, name_tmp, name_list, node) { + list_del(&name_loop->node); + free(name_loop); + } +} + +/* calls function for every file found in specified directory */ +int add_matching_files(struct list_head *name_list, const char *dirname, const char *suffix) +{ + struct dirent *ent; + DIR *dir; + char filename[PATH_SIZE]; + + dbg("open directory '%s'\n", dirname); + dir = opendir(dirname); + if (dir == NULL) { + err("unable to open '%s': %s\n", dirname, strerror(errno)); + return -1; + } + + while (1) { + ent = readdir(dir); + if (ent == NULL || ent->d_name[0] == '\0') + break; + + if ((ent->d_name[0] == '.') || (ent->d_name[0] == COMMENT_CHARACTER)) + continue; + + /* look for file matching with specified suffix */ + if (suffix != NULL) { + const char *ext; + + ext = strrchr(ent->d_name, '.'); + if (ext == NULL) + continue; + if (strcmp(ext, suffix) != 0) + continue; + } + dbg("put file '%s/%s' into list\n", dirname, ent->d_name); + + snprintf(filename, sizeof(filename), "%s/%s", dirname, ent->d_name); + filename[sizeof(filename)-1] = '\0'; + name_list_add(name_list, filename, 1); + } + + closedir(dir); + return 0; +} + +uid_t lookup_user(const char *user) +{ + struct passwd *pw; + uid_t uid = 0; + + errno = 0; + pw = getpwnam(user); + if (pw == NULL) { + if (errno == 0 || errno == ENOENT || errno == ESRCH) + err("specified user '%s' unknown\n", user); + else + err("error resolving user '%s': %s\n", user, strerror(errno)); + } else + uid = pw->pw_uid; + + return uid; +} + +extern gid_t lookup_group(const char *group) +{ + struct group *gr; + gid_t gid = 0; + + errno = 0; + gr = getgrnam(group); + if (gr == NULL) { + if (errno == 0 || errno == ENOENT || errno == ESRCH) + err("specified group '%s' unknown\n", group); + else + err("error resolving group '%s': %s\n", group, strerror(errno)); + } else + gid = gr->gr_gid; + + return gid; +} + diff --git a/udev/udev_utils_file.c b/udev/udev_utils_file.c new file mode 100644 index 0000000000..a492785af9 --- /dev/null +++ b/udev/udev_utils_file.c @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2004-2005 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udev_selinux.h" + +int create_path(const char *path) +{ + char p[PATH_SIZE]; + char *pos; + struct stat stats; + int ret; + + strlcpy(p, path, sizeof(p)); + pos = strrchr(p, '/'); + if (pos == p || pos == NULL) + return 0; + + while (pos[-1] == '/') + pos--; + pos[0] = '\0'; + + dbg("stat '%s'\n", p); + if (stat(p, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR) + return 0; + + if (create_path(p) != 0) + return -1; + + dbg("mkdir '%s'\n", p); + selinux_setfscreatecon(p, NULL, S_IFDIR|0755); + ret = mkdir(p, 0755); + selinux_resetfscreatecon(); + if (ret == 0) + return 0; + + if (errno == EEXIST) + if (stat(p, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR) + return 0; + return -1; +} + +int delete_path(const char *path) +{ + char p[PATH_SIZE]; + char *pos; + int retval; + + strcpy (p, path); + pos = strrchr(p, '/'); + if (pos == p || pos == NULL) + return 0; + + while (1) { + *pos = '\0'; + pos = strrchr(p, '/'); + + /* don't remove the last one */ + if ((pos == p) || (pos == NULL)) + break; + + /* remove if empty */ + retval = rmdir(p); + if (errno == ENOENT) + retval = 0; + if (retval) { + if (errno == ENOTEMPTY) + return 0; + err("rmdir(%s) failed: %s\n", p, strerror(errno)); + break; + } + dbg("removed '%s'\n", p); + } + return 0; +} + +/* Reset permissions on the device node, before unlinking it to make sure, + * that permisions of possible hard links will be removed too. + */ +int unlink_secure(const char *filename) +{ + int retval; + + retval = chown(filename, 0, 0); + if (retval) + err("chown(%s, 0, 0) failed: %s\n", filename, strerror(errno)); + + retval = chmod(filename, 0000); + if (retval) + err("chmod(%s, 0000) failed: %s\n", filename, strerror(errno)); + + retval = unlink(filename); + if (errno == ENOENT) + retval = 0; + + if (retval) + err("unlink(%s) failed: %s\n", filename, strerror(errno)); + + return retval; +} + +int file_map(const char *filename, char **buf, size_t *bufsize) +{ + struct stat stats; + int fd; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + return -1; + } + + if (fstat(fd, &stats) < 0) { + close(fd); + return -1; + } + + *buf = mmap(NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (*buf == MAP_FAILED) { + close(fd); + return -1; + } + *bufsize = stats.st_size; + + close(fd); + + return 0; +} + +void file_unmap(void *buf, size_t bufsize) +{ + munmap(buf, bufsize); +} + +/* return number of chars until the next newline, skip escaped newline */ +size_t buf_get_line(const char *buf, size_t buflen, size_t cur) +{ + int escape = 0; + size_t count; + + for (count = cur; count < buflen; count++) { + if (!escape && buf[count] == '\n') + break; + + if (buf[count] == '\\') + escape = 1; + else + escape = 0; + } + + return count - cur; +} diff --git a/udev/udev_utils_string.c b/udev/udev_utils_string.c new file mode 100644 index 0000000000..e3dc137e63 --- /dev/null +++ b/udev/udev_utils_string.c @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2004-2005 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +int string_is_true(const char *str) +{ + if (strcasecmp(str, "true") == 0) + return 1; + if (strcasecmp(str, "yes") == 0) + return 1; + if (strcasecmp(str, "1") == 0) + return 1; + return 0; +} + +void remove_trailing_chars(char *path, char c) +{ + size_t len; + + len = strlen(path); + while (len > 0 && path[len-1] == c) + path[--len] = '\0'; +} + +size_t path_encode(char *s, size_t len) +{ + char t[(len * 3)+1]; + size_t i, j; + + t[0] = '\0'; + for (i = 0, j = 0; s[i] != '\0'; i++) { + if (s[i] == '/') { + memcpy(&t[j], "\\x2f", 4); + j += 4; + } else if (s[i] == '\\') { + memcpy(&t[j], "\\x5c", 4); + j += 4; + } else { + t[j] = s[i]; + j++; + } + } + t[j] = '\0'; + strncpy(s, t, len); + return j; +} + +size_t path_decode(char *s) +{ + size_t i, j; + + for (i = 0, j = 0; s[i] != '\0'; j++) { + if (memcmp(&s[i], "\\x2f", 4) == 0) { + s[j] = '/'; + i += 4; + }else if (memcmp(&s[i], "\\x5c", 4) == 0) { + s[j] = '\\'; + i += 4; + } else { + s[j] = s[i]; + i++; + } + } + s[j] = '\0'; + return j; +} + +/* count of characters used to encode one unicode char */ +static int utf8_encoded_expected_len(const char *str) +{ + unsigned char c = (unsigned char)str[0]; + + if (c < 0x80) + return 1; + if ((c & 0xe0) == 0xc0) + return 2; + if ((c & 0xf0) == 0xe0) + return 3; + if ((c & 0xf8) == 0xf0) + return 4; + if ((c & 0xfc) == 0xf8) + return 5; + if ((c & 0xfe) == 0xfc) + return 6; + return 0; +} + +/* decode one unicode char */ +static int utf8_encoded_to_unichar(const char *str) +{ + int unichar; + int len; + int i; + + len = utf8_encoded_expected_len(str); + switch (len) { + case 1: + return (int)str[0]; + case 2: + unichar = str[0] & 0x1f; + break; + case 3: + unichar = (int)str[0] & 0x0f; + break; + case 4: + unichar = (int)str[0] & 0x07; + break; + case 5: + unichar = (int)str[0] & 0x03; + break; + case 6: + unichar = (int)str[0] & 0x01; + break; + default: + return -1; + } + + for (i = 1; i < len; i++) { + if (((int)str[i] & 0xc0) != 0x80) + return -1; + unichar <<= 6; + unichar |= (int)str[i] & 0x3f; + } + + return unichar; +} + +/* expected size used to encode one unicode char */ +static int utf8_unichar_to_encoded_len(int unichar) +{ + if (unichar < 0x80) + return 1; + if (unichar < 0x800) + return 2; + if (unichar < 0x10000) + return 3; + if (unichar < 0x200000) + return 4; + if (unichar < 0x4000000) + return 5; + return 6; +} + +/* check if unicode char has a valid numeric range */ +static int utf8_unichar_valid_range(int unichar) +{ + if (unichar > 0x10ffff) + return 0; + if ((unichar & 0xfffff800) == 0xd800) + return 0; + if ((unichar > 0xfdcf) && (unichar < 0xfdf0)) + return 0; + if ((unichar & 0xffff) == 0xffff) + return 0; + return 1; +} + +/* validate one encoded unicode char and return its length */ +int utf8_encoded_valid_unichar(const char *str) +{ + int len; + int unichar; + int i; + + len = utf8_encoded_expected_len(str); + if (len == 0) + return -1; + + /* ascii is valid */ + if (len == 1) + return 1; + + /* check if expected encoded chars are available */ + for (i = 0; i < len; i++) + if ((str[i] & 0x80) != 0x80) + return -1; + + unichar = utf8_encoded_to_unichar(str); + + /* check if encoded length matches encoded value */ + if (utf8_unichar_to_encoded_len(unichar) != len) + return -1; + + /* check if value has valid range */ + if (!utf8_unichar_valid_range(unichar)) + return -1; + + return len; +} + +/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */ +int replace_chars(char *str, const char *white) +{ + size_t i = 0; + int replaced = 0; + + while (str[i] != '\0') { + int len; + + /* accept whitelist */ + if (white != NULL && strchr(white, str[i]) != NULL) { + i++; + continue; + } + + /* accept plain ascii char */ + if ((str[i] >= '0' && str[i] <= '9') || + (str[i] >= 'A' && str[i] <= 'Z') || + (str[i] >= 'a' && str[i] <= 'z')) { + i++; + continue; + } + + /* accept hex encoding */ + if (str[i] == '\\' && str[i+1] == 'x') { + i += 2; + continue; + } + + /* accept valid utf8 */ + len = utf8_encoded_valid_unichar(&str[i]); + if (len > 1) { + i += len; + continue; + } + + /* if space is allowed, replace whitespace with ordinary space */ + if (isspace(str[i]) && strchr(white, ' ') != NULL) { + str[i] = ' '; + i++; + replaced++; + continue; + } + + /* everything else is replaced with '_' */ + str[i] = '_'; + i++; + replaced++; + } + + return replaced; +} diff --git a/udev/udevadm.c b/udev/udevadm.c new file mode 100644 index 0000000000..6e7d7734d2 --- /dev/null +++ b/udev/udevadm.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2007 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static int debug; + +#ifdef USE_LOG +void log_message(int priority, const char *format, ...) +{ + va_list args; + + if (priority > udev_log_priority) + return; + + va_start(args, format); + if (debug) { + vprintf(format, args); + } else + vsyslog(priority, format, args); + va_end(args); +} +#endif + +struct command { + const char *name; + int (*cmd)(int argc, char *argv[], char *envp[]); + const char *help; + int debug; +}; + +static const struct command cmds[]; + +static int version(int argc, char *argv[], char *envp[]) +{ + printf("%s\n", UDEV_VERSION); + return 0; +} + +static int help(int argc, char *argv[], char *envp[]) +{ + const struct command *cmd; + + printf("Usage: udevadm COMMAND [OPTIONS]\n"); + for (cmd = cmds; cmd->name != NULL; cmd++) + printf(" %-12s %s\n", cmd->name, cmd->help); + printf("\n"); + return 0; +} + +static const struct command cmds[] = { + { + .name = "info", + .cmd = udevinfo, + .help = "query sysfs or the udev database", + }, + { + .name = "trigger", + .cmd = udevtrigger, + .help = "request events from the kernel", + }, + { + .name = "settle", + .cmd = udevsettle, "", + .help = "wait for the event queue to finish", + }, + { + .name = "control", + .cmd = udevcontrol, + .help = "control the udev daemon", + }, + { + .name = "monitor", + .cmd = udevmonitor, + .help = "listen to kernel and udev events", + }, + { + .name = "test", + .cmd = udevtest, + .help = "simulation run", + .debug = 1, + }, + { + .name = "version", + .cmd = version, + .help = "print the version number", + }, + { + .name = "help", + .cmd = help, + .help = "print this help text", + }, + {} +}; + +int main(int argc, char *argv[], char *envp[]) +{ + const char *command; + const char *pos; + const struct command *cmd; + int rc; + + /* get binary or symlink name */ + pos = strrchr(argv[0], '/'); + if (pos != NULL) + command = &pos[1]; + else + command = argv[0]; + + /* the trailing part of the binary or symlink name is the command */ + if (strncmp(command, "udev", 4) == 0) + command = &command[4]; + + if (command == NULL || command[0] == '\0') + goto err_unknown; + + /* udevadm itself needs to strip its name from the passed options */ + if (strcmp(command, "adm") == 0) { + command = argv[1]; + argv++; + argc--; + } + + if (command == NULL) + goto err_unknown; + + /* allow command to be specified as an option */ + if (strncmp(command, "--", 2) == 0) + command += 2; + + /* find and execute command */ + for (cmd = cmds; cmd->name != NULL; cmd++) { + if (strcmp(cmd->name, command) == 0) { + debug = cmd->debug; + rc = cmd->cmd(argc, argv, envp); + goto out; + } + } + +err_unknown: + fprintf(stderr, "unknown command, try help\n\n"); + rc = 2; +out: + return rc; +} diff --git a/udev/udevadm.xml b/udev/udevadm.xml new file mode 100644 index 0000000000..670c991457 --- /dev/null +++ b/udev/udevadm.xml @@ -0,0 +1,379 @@ + + + +
+
+ udevadm + + + udevd + November 2007 + udev + + + + udevadm + 8 + + + + + udevadmudev management tool + + + + + udevadm info options + + + udevadm trigger options + + + udevadm settle options + + + udevadm control options instruction + + + udevadm monitor options + + + udevadm test options devpath + + + udevadm version + + + udevadm help + + + + DESCRIPTION + udevadm expects a command and command specific options. It + controls the runtime behavior of udev, requests kernel events, + manages the event queue, and provides simple debugging mechanisms. + + + OPTIONS + + udevadm info <replaceable>options</replaceable> + Queries the udev database for device information + stored in the udev database. It can also query the properties + of a device from its sysfs representation to help creating udev + rules that match this device. + + + + + Query the database for specified type of device data. It needs the + or to identify the specified + device. Valid queries are: + name, symlink, path, + env, all. + + + + + + The devpath of the device to query. + + + + + + The name of the device node or a symlink to query + + + + + + The udev root directory: /dev. If used in conjunction + with a name or symlink query, the + query returns the absolute path including the root directory. + + + + + + Print all sysfs properties of the specified device that can be used + in udev rules to match the specified device. It prints all devices + along the chain, up to the root of sysfs that can be used in udev rules. + + + + + + Print major/minor numbers of the underlying device, where the file + lives on. + + + + + + Export the content of the udev database. + + + + + + Print version. + + + + + + Print help text. + + + + + + udevadm trigger <optional>options</optional> + Request device uevents, usually used to replay events at system coldplug. + + + + + Print the list of devices which will be triggered. + + + + + + Do not actually trigger the event. + + + + + + Trigger only the events which are failed during a previous run. + + + + + + Type of event to be triggered. The default value is "add". + + + + + + Trigger events for devices which belong to a matching subsystem. This option + can be specified multiple times and supports shell style pattern matching. + + + + + + Do not trigger events for devices which belong to a matching subsystem. This option + can be specified multiple times and supports shell style pattern matching. + + + + + + Trigger events for devices with a matching sysfs attribute. If a value is specified + along with the attribute name, the content of the attribute is matched against the given + value using shell style pattern matching. If no value is specified, the existence of the + sysfs attribute is checked. This option can be specified multiple times. + + + + + + Do not trigger events for devices with a matching sysfs attribute. If a value is + specified along with the attribute name, the content of the attribute is matched against + the given value using shell style pattern matching. If no value is specified, the existence + of the sysfs attribute is checked. This option can be specified multiple times. + + + + + + Pass the synthesized events to the specified socket, instead of triggering + a global kernel event. All available event values will be send in the same format + the kernel sends an uevent, or + sends a message. If the first character of the specified path is an @ character, + an abstract namespace socket is used, instead of an existing socket file. + + + + + + Pass an additional environemt key to the event. This works only with the + --socket option. + + + + + + udevadm settle <optional>options</optional> + Watches the udev event queue, and exits if all current events are handled. + + + + + Maximum number of seconds to wait for the event queue to become empty. + The default value is 180 seconds. + + + + + + Print help text. + + + + + + udevadm control <replaceable>command</replaceable> + Modify the internal state of the running udev daemon. + + + + + Set the internal log level of udevd. Valid values are the numerical + syslog priorities or their textual representations: , + and . + + + + + + Signal udevd to stop executing new events. Incoming events + will be queued. + + + + + + Signal udevd to enable the execution of events. + + + + + + Signal udevd to reload the rules from the config. + + + + + + Set global variable. + + + + value + + Set the maximum number of events, udevd will handle at the + same time. + + + + + + Set the maximum number of events, which are allowed to run at the + same time. + + + + + + Print help text. + + + + + + udevadm monitor <optional>options</optional> + Listens to the kernel uevents and events sent out by a udev rule + and prints the devpath of the event to the console. It can be used to analyze the + event timing, by comparing the timestamps of the kernel uevent and the udev event. + + + + + + Print the complete environment for all events. Can be used to compare the + kernel supplied and the udev added environment values. + + + + + + Print the kernel uevents. + + + + + + Print the udev event after the rule processing. + + + + + + Print help text. + + + + + + udevadm test <optional>options</optional> <replaceable>devpath</replaceable> + Simulate a udev event run for the given device, and print out debug + output. Unless forced to, no device node or symlink will be created. + + + + + The action string. + + + + + + The subsystem string. + + + + + + Force the creation of a device node or symlink. Usually the test run + prints only debug output. + + + + + + Print help text. + + + + + + udevadm version + Print version number. + + + udevadm help + Print help text. + + + + AUTHOR + Written by Kay Sievers kay.sievers@vrfy.org. + + + + SEE ALSO + + udev7 + + + udevd8 + + + +
+
diff --git a/udev/udevcontrol.c b/udev/udevcontrol.c new file mode 100644 index 0000000000..4c93b8f464 --- /dev/null +++ b/udev/udevcontrol.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2005-2006 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udevd.h" + +static int sock = -1; +static int udev_log = 0; + +int udevcontrol(int argc, char *argv[], char *envp[]) +{ + static struct udevd_ctrl_msg ctrl_msg; + struct sockaddr_un saddr; + socklen_t addrlen; + const char *env; + const char *arg; + const char *val; + int *intval; + int retval = 1; + + env = getenv("UDEV_LOG"); + if (env) + udev_log = log_priority(env); + + logging_init("udevcontrol"); + dbg("version %s\n", UDEV_VERSION); + + if (argc < 2) { + fprintf(stderr, "missing command\n\n"); + goto exit; + } + memset(&ctrl_msg, 0x00, sizeof(struct udevd_ctrl_msg)); + strcpy(ctrl_msg.magic, UDEVD_CTRL_MAGIC); + arg = argv[1]; + + /* allow instructions passed as options */ + if (strncmp(arg, "--", 2) == 0) + arg += 2; + + if (!strcmp(arg, "stop_exec_queue")) + ctrl_msg.type = UDEVD_CTRL_STOP_EXEC_QUEUE; + else if (!strcmp(arg, "start_exec_queue")) + ctrl_msg.type = UDEVD_CTRL_START_EXEC_QUEUE; + else if (!strcmp(arg, "reload_rules")) + ctrl_msg.type = UDEVD_CTRL_RELOAD_RULES; + else if (!strncmp(arg, "log_priority=", strlen("log_priority="))) { + intval = (int *) ctrl_msg.buf; + val = &arg[strlen("log_priority=")]; + ctrl_msg.type = UDEVD_CTRL_SET_LOG_LEVEL; + *intval = log_priority(val); + info("send log_priority=%i\n", *intval); + } else if (!strncmp(arg, "max_childs=", strlen("max_childs="))) { + char *endp; + int count; + + intval = (int *) ctrl_msg.buf; + val = &arg[strlen("max_childs=")]; + ctrl_msg.type = UDEVD_CTRL_SET_MAX_CHILDS; + count = strtoul(val, &endp, 0); + if (endp[0] != '\0' || count < 1) { + fprintf(stderr, "invalid number\n"); + goto exit; + } + *intval = count; + info("send max_childs=%i\n", *intval); + } else if (!strncmp(arg, "max_childs_running=", strlen("max_childs_running="))) { + char *endp; + int count; + + intval = (int *) ctrl_msg.buf; + val = &arg[strlen("max_childs_running=")]; + ctrl_msg.type = UDEVD_CTRL_SET_MAX_CHILDS_RUNNING; + count = strtoul(val, &endp, 0); + if (endp[0] != '\0' || count < 1) { + fprintf(stderr, "invalid number\n"); + goto exit; + } + *intval = count; + info("send max_childs_running=%i\n", *intval); + } else if (!strncmp(arg, "env", strlen("env"))) { + if (!strncmp(arg, "env=", strlen("env="))) + val = &arg[strlen("env=")]; + else + val = argv[2]; + if (val == NULL) { + fprintf(stderr, "missing key\n"); + goto exit; + } + ctrl_msg.type = UDEVD_CTRL_ENV; + strlcpy(ctrl_msg.buf, val, sizeof(ctrl_msg.buf)); + info("send env '%s'\n", val); + } else if (strcmp(arg, "help") == 0 || strcmp(arg, "-h") == 0) { + printf("Usage: udevadm control COMMAND\n" + " --log_priority= 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" + " --env== set a global environment variable\n" + " --max_childs= maximum number of childs\n" + " --max_childs_running= maximum number of childs running at the same time\n" + " --help print this help text\n\n"); + goto exit; + } else { + fprintf(stderr, "unrecognized command '%s'\n", arg); + goto exit; + } + + if (getuid() != 0) { + fprintf(stderr, "root privileges required\n"); + goto exit; + } + + sock = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (sock == -1) { + err("error getting socket: %s\n", strerror(errno)); + goto exit; + } + + memset(&saddr, 0x00, sizeof(struct sockaddr_un)); + saddr.sun_family = AF_LOCAL; + /* use abstract namespace for socket path */ + strcpy(&saddr.sun_path[1], UDEVD_CTRL_SOCK_PATH); + addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); + + retval = sendto(sock, &ctrl_msg, sizeof(ctrl_msg), 0, (struct sockaddr *)&saddr, addrlen); + if (retval == -1) { + err("error sending message: %s\n", strerror(errno)); + retval = 1; + } else { + dbg("sent message type=0x%02x, %u bytes sent\n", ctrl_msg.type, retval); + retval = 0; + } + + close(sock); +exit: + logging_close(); + return retval; +} diff --git a/udev/udevd.c b/udev/udevd.c new file mode 100644 index 0000000000..0827a5ceb3 --- /dev/null +++ b/udev/udevd.c @@ -0,0 +1,1304 @@ +/* + * Copyright (C) 2004-2006 Kay Sievers + * Copyright (C) 2004 Chris Friesen + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udev_rules.h" +#include "udevd.h" +#include "udev_selinux.h" + +static int debug_trace; +static int debug; + +static struct udev_rules rules; +static int udevd_sock = -1; +static int uevent_netlink_sock = -1; +static int inotify_fd = -1; +static pid_t sid; + +static int signal_pipe[2] = {-1, -1}; +static volatile int sigchilds_waiting; +static volatile int udev_exit; +static volatile int reload_config; +static int run_exec_q; +static int stop_exec_q; +static int max_childs; +static int max_childs_running; +static char udev_log[32]; + +static LIST_HEAD(exec_list); +static LIST_HEAD(running_list); + + +#ifdef USE_LOG +void log_message(int priority, const char *format, ...) +{ + va_list args; + + if (priority > udev_log_priority) + return; + + va_start(args, format); + if (debug) { + printf("[%d] ", (int) getpid()); + vprintf(format, args); + } else + vsyslog(priority, format, args); + va_end(args); +} + +#endif + +static void asmlinkage udev_event_sig_handler(int signum) +{ + if (signum == SIGALRM) + exit(1); +} + +static int udev_event_process(struct udevd_uevent_msg *msg) +{ + struct sigaction act; + struct udevice *udev; + int i; + int retval; + + /* set signal handlers */ + memset(&act, 0x00, sizeof(act)); + act.sa_handler = (void (*)(int)) udev_event_sig_handler; + sigemptyset (&act.sa_mask); + act.sa_flags = 0; + sigaction(SIGALRM, &act, NULL); + + /* reset to default */ + act.sa_handler = SIG_DFL; + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + sigaction(SIGCHLD, &act, NULL); + sigaction(SIGHUP, &act, NULL); + + /* trigger timeout to prevent hanging processes */ + alarm(UDEV_EVENT_TIMEOUT); + + /* reconstruct event environment from message */ + for (i = 0; msg->envp[i]; i++) + putenv(msg->envp[i]); + + udev = udev_device_init(NULL); + if (udev == NULL) + return -1; + strlcpy(udev->action, msg->action, sizeof(udev->action)); + sysfs_device_set_values(udev->dev, msg->devpath, msg->subsystem, msg->driver); + udev->devpath_old = msg->devpath_old; + udev->devt = msg->devt; + + retval = udev_device_event(&rules, udev); + + /* rules may change/disable the timeout */ + if (udev->event_timeout >= 0) + alarm(udev->event_timeout); + + /* run programs collected by RUN-key*/ + if (retval == 0 && !udev->ignore_device && udev_run) + retval = udev_rules_run(udev); + + udev_device_cleanup(udev); + return retval; +} + +enum event_state { + EVENT_QUEUED, + EVENT_FINISHED, + EVENT_FAILED, +}; + +static void export_event_state(struct udevd_uevent_msg *msg, enum event_state state) +{ + char filename[PATH_SIZE]; + char filename_failed[PATH_SIZE]; + size_t start; + + /* location of queue file */ + snprintf(filename, sizeof(filename), "%s/"EVENT_QUEUE_DIR"/%llu", udev_root, msg->seqnum); + + /* location of failed file */ + strlcpy(filename_failed, udev_root, sizeof(filename_failed)); + strlcat(filename_failed, "/", sizeof(filename_failed)); + start = strlcat(filename_failed, EVENT_FAILED_DIR"/", sizeof(filename_failed)); + strlcat(filename_failed, msg->devpath, sizeof(filename_failed)); + path_encode(&filename_failed[start], sizeof(filename_failed) - start); + + switch (state) { + case EVENT_QUEUED: + unlink(filename_failed); + delete_path(filename_failed); + + create_path(filename); + selinux_setfscreatecon(filename, NULL, S_IFLNK); + symlink(msg->devpath, filename); + selinux_resetfscreatecon(); + break; + case EVENT_FINISHED: + if (msg->devpath_old != NULL) { + /* "move" event - rename failed file to current name, do not delete failed */ + char filename_failed_old[PATH_SIZE]; + + strlcpy(filename_failed_old, udev_root, sizeof(filename_failed_old)); + strlcat(filename_failed_old, "/", sizeof(filename_failed_old)); + start = strlcat(filename_failed_old, EVENT_FAILED_DIR"/", sizeof(filename_failed_old)); + strlcat(filename_failed_old, msg->devpath_old, sizeof(filename_failed_old)); + path_encode(&filename_failed_old[start], sizeof(filename) - start); + + if (rename(filename_failed_old, filename_failed) == 0) + info("renamed devpath, moved failed state of '%s' to %s'\n", + msg->devpath_old, msg->devpath); + } else { + unlink(filename_failed); + delete_path(filename_failed); + } + + unlink(filename); + delete_path(filename); + break; + case EVENT_FAILED: + /* move failed event to the failed directory */ + create_path(filename_failed); + rename(filename, filename_failed); + + /* clean up possibly empty queue directory */ + delete_path(filename); + break; + } + + return; +} + +static void msg_queue_delete(struct udevd_uevent_msg *msg) +{ + list_del(&msg->node); + + /* mark as failed, if "add" event returns non-zero */ + if (msg->exitstatus && strcmp(msg->action, "add") == 0) + export_event_state(msg, EVENT_FAILED); + else + export_event_state(msg, EVENT_FINISHED); + + free(msg); +} + +static void udev_event_run(struct udevd_uevent_msg *msg) +{ + pid_t pid; + int retval; + + pid = fork(); + switch (pid) { + case 0: + /* child */ + close(uevent_netlink_sock); + close(udevd_sock); + if (inotify_fd >= 0) + close(inotify_fd); + close(signal_pipe[READ_END]); + close(signal_pipe[WRITE_END]); + logging_close(); + + logging_init("udevd-event"); + setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY); + + retval = udev_event_process(msg); + info("seq %llu finished with %i\n", msg->seqnum, retval); + + logging_close(); + if (retval) + exit(1); + exit(0); + case -1: + err("fork of child failed: %s\n", strerror(errno)); + msg_queue_delete(msg); + break; + default: + /* get SIGCHLD in main loop */ + info("seq %llu forked, pid [%d], '%s' '%s', %ld seconds old\n", + msg->seqnum, pid, msg->action, msg->subsystem, time(NULL) - msg->queue_time); + msg->pid = pid; + } +} + +static void msg_queue_insert(struct udevd_uevent_msg *msg) +{ + char filename[PATH_SIZE]; + int fd; + + msg->queue_time = time(NULL); + + export_event_state(msg, EVENT_QUEUED); + info("seq %llu queued, '%s' '%s'\n", msg->seqnum, msg->action, msg->subsystem); + + strlcpy(filename, udev_root, sizeof(filename)); + strlcat(filename, "/" EVENT_SEQNUM, sizeof(filename)); + fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644); + if (fd >= 0) { + char str[32]; + int len; + + len = sprintf(str, "%llu\n", msg->seqnum); + write(fd, str, len); + close(fd); + } + + /* run one event after the other in debug mode */ + if (debug_trace) { + list_add_tail(&msg->node, &running_list); + udev_event_run(msg); + waitpid(msg->pid, NULL, 0); + msg_queue_delete(msg); + return; + } + + /* run all events with a timeout set immediately */ + if (msg->timeout != 0) { + list_add_tail(&msg->node, &running_list); + udev_event_run(msg); + return; + } + + list_add_tail(&msg->node, &exec_list); + run_exec_q = 1; +} + +static int mem_size_mb(void) +{ + FILE* f; + char buf[4096]; + long int memsize = -1; + + f = fopen("/proc/meminfo", "r"); + if (f == NULL) + return -1; + + while (fgets(buf, sizeof(buf), f) != NULL) { + long int value; + + if (sscanf(buf, "MemTotal: %ld kB", &value) == 1) { + memsize = value / 1024; + break; + } + } + + fclose(f); + return memsize; +} + +static int cpu_count(void) +{ + FILE* f; + char buf[4096]; + int count = 0; + + f = fopen("/proc/stat", "r"); + if (f == NULL) + return -1; + + while (fgets(buf, sizeof(buf), f) != NULL) { + if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) + count++; + } + + fclose(f); + if (count == 0) + return -1; + return count; +} + +static int running_processes(void) +{ + FILE* f; + char buf[4096]; + int running = -1; + + f = fopen("/proc/stat", "r"); + if (f == NULL) + return -1; + + while (fgets(buf, sizeof(buf), f) != NULL) { + int value; + + if (sscanf(buf, "procs_running %u", &value) == 1) { + running = value; + break; + } + } + + fclose(f); + return running; +} + +/* return the number of process es in our session, count only until limit */ +static int running_processes_in_session(pid_t session, int limit) +{ + DIR *dir; + struct dirent *dent; + int running = 0; + + dir = opendir("/proc"); + if (!dir) + return -1; + + /* read process info from /proc */ + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + int f; + char procdir[64]; + char line[256]; + const char *pos; + char state; + pid_t ppid, pgrp, sess; + int len; + + if (!isdigit(dent->d_name[0])) + continue; + + snprintf(procdir, sizeof(procdir), "/proc/%s/stat", dent->d_name); + procdir[sizeof(procdir)-1] = '\0'; + + f = open(procdir, O_RDONLY); + if (f == -1) + continue; + + len = read(f, line, sizeof(line)-1); + close(f); + + if (len <= 0) + continue; + else + line[len] = '\0'; + + /* skip ugly program name */ + pos = strrchr(line, ')') + 2; + if (pos == NULL) + continue; + + if (sscanf(pos, "%c %d %d %d ", &state, &ppid, &pgrp, &sess) != 4) + continue; + + /* count only processes in our session */ + if (sess != session) + continue; + + /* count only running, no sleeping processes */ + if (state != 'R') + continue; + + running++; + if (limit > 0 && running >= limit) + break; + } + closedir(dir); + + return running; +} + +static int compare_devpath(const char *running, const char *waiting) +{ + int i; + + for (i = 0; i < PATH_SIZE; i++) { + /* identical device event found */ + if (running[i] == '\0' && waiting[i] == '\0') + return 1; + + /* parent device event found */ + if (running[i] == '\0' && waiting[i] == '/') + return 2; + + /* child device event found */ + if (running[i] == '/' && waiting[i] == '\0') + return 3; + + /* no matching event */ + if (running[i] != waiting[i]) + break; + } + + return 0; +} + +/* lookup event for identical, parent, child, or physical device */ +static int devpath_busy(struct udevd_uevent_msg *msg, int limit) +{ + struct udevd_uevent_msg *loop_msg; + int childs_count = 0; + + /* check exec-queue which may still contain delayed events we depend on */ + list_for_each_entry(loop_msg, &exec_list, node) { + /* skip ourself and all later events */ + if (loop_msg->seqnum >= msg->seqnum) + break; + + /* check our old name */ + if (msg->devpath_old != NULL) + if (strcmp(loop_msg->devpath , msg->devpath_old) == 0) + return 2; + + /* check identical, parent, or child device event */ + if (compare_devpath(loop_msg->devpath, msg->devpath) != 0) { + dbg("%llu, device event still pending %llu (%s)\n", + msg->seqnum, loop_msg->seqnum, loop_msg->devpath); + return 3; + } + + /* check for our major:minor number */ + if (msg->devt && loop_msg->devt == msg->devt && + strcmp(msg->subsystem, loop_msg->subsystem) == 0) { + dbg("%llu, device event still pending %llu (%d:%d)\n", msg->seqnum, + loop_msg->seqnum, major(loop_msg->devt), minor(loop_msg->devt)); + return 4; + } + + /* check physical device event (special case of parent) */ + if (msg->physdevpath && msg->action && strcmp(msg->action, "add") == 0) + if (compare_devpath(loop_msg->devpath, msg->physdevpath) != 0) { + dbg("%llu, physical device event still pending %llu (%s)\n", + msg->seqnum, loop_msg->seqnum, loop_msg->devpath); + return 5; + } + } + + /* check run queue for still running events */ + list_for_each_entry(loop_msg, &running_list, node) { + if (limit && childs_count++ > limit) { + dbg("%llu, maximum number (%i) of childs reached\n", msg->seqnum, childs_count); + return 1; + } + + /* check our old name */ + if (msg->devpath_old != NULL) + if (strcmp(loop_msg->devpath , msg->devpath_old) == 0) + return 2; + + /* check identical, parent, or child device event */ + if (compare_devpath(loop_msg->devpath, msg->devpath) != 0) { + dbg("%llu, device event still running %llu (%s)\n", + msg->seqnum, loop_msg->seqnum, loop_msg->devpath); + return 3; + } + + /* check for our major:minor number */ + if (msg->devt && loop_msg->devt == msg->devt && + strcmp(msg->subsystem, loop_msg->subsystem) == 0) { + dbg("%llu, device event still running %llu (%d:%d)\n", msg->seqnum, + loop_msg->seqnum, major(loop_msg->devt), minor(loop_msg->devt)); + return 4; + } + + /* check physical device event (special case of parent) */ + if (msg->physdevpath && msg->action && strcmp(msg->action, "add") == 0) + if (compare_devpath(loop_msg->devpath, msg->physdevpath) != 0) { + dbg("%llu, physical device event still running %llu (%s)\n", + msg->seqnum, loop_msg->seqnum, loop_msg->devpath); + return 5; + } + } + return 0; +} + +/* serializes events for the identical and parent and child devices */ +static void msg_queue_manager(void) +{ + struct udevd_uevent_msg *loop_msg; + struct udevd_uevent_msg *tmp_msg; + int running; + + if (list_empty(&exec_list)) + return; + + running = running_processes(); + dbg("%d processes runnning on system\n", running); + if (running < 0) + running = max_childs_running; + + list_for_each_entry_safe(loop_msg, tmp_msg, &exec_list, node) { + /* check running processes in our session and possibly throttle */ + if (running >= max_childs_running) { + running = running_processes_in_session(sid, max_childs_running+10); + dbg("at least %d processes running in session\n", running); + if (running >= max_childs_running) { + dbg("delay seq %llu, too many processes already running\n", loop_msg->seqnum); + return; + } + } + + /* serialize and wait for parent or child events */ + if (devpath_busy(loop_msg, max_childs) != 0) { + dbg("delay seq %llu (%s)\n", loop_msg->seqnum, loop_msg->devpath); + continue; + } + + /* move event to run list */ + list_move_tail(&loop_msg->node, &running_list); + udev_event_run(loop_msg); + running++; + dbg("moved seq %llu to running list\n", loop_msg->seqnum); + } +} + +static struct udevd_uevent_msg *get_msg_from_envbuf(const char *buf, int buf_size) +{ + int bufpos; + int i; + struct udevd_uevent_msg *msg; + char *physdevdriver_key = NULL; + int maj = 0; + int min = 0; + + msg = malloc(sizeof(struct udevd_uevent_msg) + buf_size); + if (msg == NULL) + return NULL; + memset(msg, 0x00, sizeof(struct udevd_uevent_msg) + buf_size); + + /* copy environment buffer and reconstruct envp */ + memcpy(msg->envbuf, buf, buf_size); + bufpos = 0; + for (i = 0; (bufpos < buf_size) && (i < UEVENT_NUM_ENVP-2); i++) { + int keylen; + char *key; + + key = &msg->envbuf[bufpos]; + keylen = strlen(key); + msg->envp[i] = key; + bufpos += keylen + 1; + dbg("add '%s' to msg.envp[%i]\n", msg->envp[i], i); + + /* remember some keys for further processing */ + if (strncmp(key, "ACTION=", 7) == 0) + msg->action = &key[7]; + else if (strncmp(key, "DEVPATH=", 8) == 0) + msg->devpath = &key[8]; + else if (strncmp(key, "SUBSYSTEM=", 10) == 0) + msg->subsystem = &key[10]; + else if (strncmp(key, "DRIVER=", 7) == 0) + msg->driver = &key[7]; + else if (strncmp(key, "SEQNUM=", 7) == 0) + msg->seqnum = strtoull(&key[7], NULL, 10); + else if (strncmp(key, "DEVPATH_OLD=", 12) == 0) + msg->devpath_old = &key[12]; + else if (strncmp(key, "PHYSDEVPATH=", 12) == 0) + msg->physdevpath = &key[12]; + else if (strncmp(key, "PHYSDEVDRIVER=", 14) == 0) + physdevdriver_key = key; + else if (strncmp(key, "MAJOR=", 6) == 0) + maj = strtoull(&key[6], NULL, 10); + else if (strncmp(key, "MINOR=", 6) == 0) + min = strtoull(&key[6], NULL, 10); + else if (strncmp(key, "TIMEOUT=", 8) == 0) + msg->timeout = strtoull(&key[8], NULL, 10); + } + msg->devt = makedev(maj, min); + msg->envp[i++] = "UDEVD_EVENT=1"; + + if (msg->driver == NULL && msg->physdevpath == NULL && physdevdriver_key != NULL) { + /* for older kernels DRIVER is empty for a bus device, export PHYSDEVDRIVER as DRIVER */ + msg->envp[i++] = &physdevdriver_key[7]; + msg->driver = &physdevdriver_key[14]; + } + + msg->envp[i] = NULL; + + if (msg->devpath == NULL || msg->action == NULL) { + info("DEVPATH or ACTION missing, ignore message\n"); + free(msg); + return NULL; + } + return msg; +} + +/* receive the udevd message from userspace */ +static void get_ctrl_msg(void) +{ + struct udevd_ctrl_msg ctrl_msg; + ssize_t size; + struct msghdr smsg; + struct cmsghdr *cmsg; + struct iovec iov; + struct ucred *cred; + char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; + int *intval; + char *pos; + + memset(&ctrl_msg, 0x00, sizeof(struct udevd_ctrl_msg)); + iov.iov_base = &ctrl_msg; + iov.iov_len = sizeof(struct udevd_ctrl_msg); + + 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(udevd_sock, &smsg, 0); + if (size < 0) { + if (errno != EINTR) + err("unable to receive user udevd message: %s\n", strerror(errno)); + return; + } + cmsg = CMSG_FIRSTHDR(&smsg); + cred = (struct ucred *) CMSG_DATA(cmsg); + + if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { + err("no sender credentials received, message ignored\n"); + return; + } + + if (cred->uid != 0) { + err("sender uid=%i, message ignored\n", cred->uid); + return; + } + + if (strncmp(ctrl_msg.magic, UDEVD_CTRL_MAGIC, sizeof(UDEVD_CTRL_MAGIC)) != 0 ) { + err("message magic '%s' doesn't match, ignore it\n", ctrl_msg.magic); + return; + } + + switch (ctrl_msg.type) { + case UDEVD_CTRL_ENV: + pos = strchr(ctrl_msg.buf, '='); + if (pos == NULL) { + err("wrong key format '%s'\n", ctrl_msg.buf); + break; + } + pos[0] = '\0'; + if (pos[1] == '\0') { + info("udevd message (ENV) received, unset '%s'\n", ctrl_msg.buf); + unsetenv(ctrl_msg.buf); + } else { + info("udevd message (ENV) received, set '%s=%s'\n", ctrl_msg.buf, &pos[1]); + setenv(ctrl_msg.buf, &pos[1], 1); + } + break; + case UDEVD_CTRL_STOP_EXEC_QUEUE: + info("udevd message (STOP_EXEC_QUEUE) received\n"); + stop_exec_q = 1; + break; + case UDEVD_CTRL_START_EXEC_QUEUE: + info("udevd message (START_EXEC_QUEUE) received\n"); + stop_exec_q = 0; + msg_queue_manager(); + break; + case UDEVD_CTRL_SET_LOG_LEVEL: + intval = (int *) ctrl_msg.buf; + info("udevd message (SET_LOG_PRIORITY) received, udev_log_priority=%i\n", *intval); + udev_log_priority = *intval; + sprintf(udev_log, "UDEV_LOG=%i", udev_log_priority); + putenv(udev_log); + break; + case UDEVD_CTRL_SET_MAX_CHILDS: + intval = (int *) ctrl_msg.buf; + info("udevd message (UDEVD_SET_MAX_CHILDS) received, max_childs=%i\n", *intval); + max_childs = *intval; + break; + case UDEVD_CTRL_SET_MAX_CHILDS_RUNNING: + intval = (int *) ctrl_msg.buf; + info("udevd message (UDEVD_SET_MAX_CHILDS_RUNNING) received, max_childs=%i\n", *intval); + max_childs_running = *intval; + break; + case UDEVD_CTRL_RELOAD_RULES: + info("udevd message (RELOAD_RULES) received\n"); + reload_config = 1; + break; + default: + err("unknown control message type\n"); + } +} + +/* receive the kernel user event message and do some sanity checks */ +static struct udevd_uevent_msg *get_netlink_msg(void) +{ + struct udevd_uevent_msg *msg; + int bufpos; + ssize_t size; + static char buffer[UEVENT_BUFFER_SIZE+512]; + char *pos; + + size = recv(uevent_netlink_sock, &buffer, sizeof(buffer), 0); + if (size < 0) { + if (errno != EINTR) + err("unable to receive kernel netlink message: %s\n", strerror(errno)); + return NULL; + } + + if ((size_t)size > sizeof(buffer)-1) + size = sizeof(buffer)-1; + buffer[size] = '\0'; + dbg("uevent_size=%zi\n", size); + + /* start of event payload */ + bufpos = strlen(buffer)+1; + msg = get_msg_from_envbuf(&buffer[bufpos], size-bufpos); + if (msg == NULL) + return NULL; + + /* validate message */ + pos = strchr(buffer, '@'); + if (pos == NULL) { + err("invalid uevent '%s'\n", buffer); + free(msg); + return NULL; + } + pos[0] = '\0'; + + if (msg->action == NULL) { + info("no ACTION in payload found, skip event '%s'\n", buffer); + free(msg); + return NULL; + } + + if (strcmp(msg->action, buffer) != 0) { + err("ACTION in payload does not match uevent, skip event '%s'\n", buffer); + free(msg); + return NULL; + } + + return msg; +} + +static void asmlinkage sig_handler(int signum) +{ + switch (signum) { + case SIGINT: + case SIGTERM: + udev_exit = 1; + break; + case SIGCHLD: + /* set flag, then write to pipe if needed */ + sigchilds_waiting = 1; + break; + case SIGHUP: + reload_config = 1; + break; + } + + /* write to pipe, which will wakeup select() in our mainloop */ + write(signal_pipe[WRITE_END], "", 1); +} + +static void udev_done(int pid, int exitstatus) +{ + /* find msg associated with pid and delete it */ + struct udevd_uevent_msg *msg; + + list_for_each_entry(msg, &running_list, node) { + if (msg->pid == pid) { + info("seq %llu, pid [%d] exit with %i, %ld seconds old\n", msg->seqnum, msg->pid, + exitstatus, time(NULL) - msg->queue_time); + msg->exitstatus = exitstatus; + msg_queue_delete(msg); + + /* there may be events waiting with the same devpath */ + run_exec_q = 1; + return; + } + } +} + +static void reap_sigchilds(void) +{ + pid_t pid; + int status; + + while (1) { + pid = waitpid(-1, &status, WNOHANG); + if (pid <= 0) + break; + if (WIFEXITED(status)) + status = WEXITSTATUS(status); + else if (WIFSIGNALED(status)) + status = WTERMSIG(status) + 128; + else + status = 0; + udev_done(pid, status); + } +} + +static int init_udevd_socket(void) +{ + struct sockaddr_un saddr; + socklen_t addrlen; + const int feature_on = 1; + int retval; + + memset(&saddr, 0x00, sizeof(saddr)); + saddr.sun_family = AF_LOCAL; + /* use abstract namespace for socket path */ + strcpy(&saddr.sun_path[1], UDEVD_CTRL_SOCK_PATH); + addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); + + udevd_sock = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (udevd_sock == -1) { + err("error getting socket: %s\n", strerror(errno)); + return -1; + } + + /* the bind takes care of ensuring only one copy running */ + retval = bind(udevd_sock, (struct sockaddr *) &saddr, addrlen); + if (retval < 0) { + err("bind failed: %s\n", strerror(errno)); + close(udevd_sock); + udevd_sock = -1; + return -1; + } + + /* enable receiving of the sender credentials */ + setsockopt(udevd_sock, SOL_SOCKET, SO_PASSCRED, &feature_on, sizeof(feature_on)); + + return 0; +} + +static int init_uevent_netlink_sock(void) +{ + struct sockaddr_nl snl; + const int buffersize = 16 * 1024 * 1024; + int retval; + + memset(&snl, 0x00, sizeof(struct sockaddr_nl)); + snl.nl_family = AF_NETLINK; + snl.nl_pid = getpid(); + snl.nl_groups = 1; + + uevent_netlink_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + if (uevent_netlink_sock == -1) { + err("error getting socket: %s\n", strerror(errno)); + return -1; + } + + /* set receive buffersize */ + setsockopt(uevent_netlink_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize)); + + retval = bind(uevent_netlink_sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl)); + if (retval < 0) { + err("bind failed: %s\n", strerror(errno)); + close(uevent_netlink_sock); + uevent_netlink_sock = -1; + return -1; + } + return 0; +} + +static void export_initial_seqnum(void) +{ + char filename[PATH_SIZE]; + int fd; + char seqnum[32]; + ssize_t len = 0; + + strlcpy(filename, sysfs_path, sizeof(filename)); + strlcat(filename, "/kernel/uevent_seqnum", sizeof(filename)); + fd = open(filename, O_RDONLY); + if (fd >= 0) { + len = read(fd, seqnum, sizeof(seqnum)-1); + close(fd); + } + if (len <= 0) { + strcpy(seqnum, "0\n"); + len = 3; + } + strlcpy(filename, udev_root, sizeof(filename)); + strlcat(filename, "/" EVENT_SEQNUM, sizeof(filename)); + create_path(filename); + fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644); + if (fd >= 0) { + write(fd, seqnum, len); + close(fd); + } +} + +int main(int argc, char *argv[], char *envp[]) +{ + int retval; + int fd; + struct sigaction act; + fd_set readfds; + const char *value; + int daemonize = 0; + int option; + static const struct option options[] = { + { "daemon", 0, NULL, 'd' }, + { "debug-trace", 0, NULL, 't' }, + { "debug", 0, NULL, 'D' }, + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + {} + }; + int rc = 1; + int maxfd; + + logging_init("udevd"); + udev_config_init(); + selinux_init(); + dbg("version %s\n", UDEV_VERSION); + + while (1) { + option = getopt_long(argc, argv, "dDthV", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'd': + daemonize = 1; + break; + case 't': + debug_trace = 1; + break; + case 'D': + debug = 1; + if (udev_log_priority < LOG_INFO) + udev_log_priority = LOG_INFO; + break; + case 'h': + printf("Usage: udevd [--help] [--daemon] [--debug-trace] [--debug] [--version]\n"); + goto exit; + case 'V': + printf("%s\n", UDEV_VERSION); + goto exit; + default: + goto exit; + } + } + + if (getuid() != 0) { + fprintf(stderr, "root privileges required\n"); + err("root privileges required\n"); + goto exit; + } + + /* make sure std{in,out,err} fd's are in a sane state */ + fd = open("/dev/null", O_RDWR); + if (fd < 0) { + fprintf(stderr, "cannot open /dev/null\n"); + err("cannot open /dev/null\n"); + } + if (fd > STDIN_FILENO) + dup2(fd, STDIN_FILENO); + if (write(STDOUT_FILENO, 0, 0) < 0) + dup2(fd, STDOUT_FILENO); + if (write(STDERR_FILENO, 0, 0) < 0) + dup2(fd, STDERR_FILENO); + + /* init sockets to receive events */ + if (init_udevd_socket() < 0) { + if (errno == EADDRINUSE) { + fprintf(stderr, "another udev daemon already running\n"); + err("another udev daemon already running\n"); + rc = 1; + } else { + fprintf(stderr, "error initializing udevd socket\n"); + err("error initializing udevd socket\n"); + rc = 2; + } + goto exit; + } + + if (init_uevent_netlink_sock() < 0) { + fprintf(stderr, "error initializing netlink socket\n"); + err("error initializing netlink socket\n"); + rc = 3; + goto exit; + } + + /* setup signal handler pipe */ + retval = pipe(signal_pipe); + if (retval < 0) { + err("error getting pipes: %s\n", strerror(errno)); + goto exit; + } + + retval = fcntl(signal_pipe[READ_END], F_GETFL, 0); + if (retval < 0) { + err("error fcntl on read pipe: %s\n", strerror(errno)); + goto exit; + } + retval = fcntl(signal_pipe[READ_END], F_SETFL, retval | O_NONBLOCK); + if (retval < 0) { + err("error fcntl on read pipe: %s\n", strerror(errno)); + goto exit; + } + + retval = fcntl(signal_pipe[WRITE_END], F_GETFL, 0); + if (retval < 0) { + err("error fcntl on write pipe: %s\n", strerror(errno)); + goto exit; + } + retval = fcntl(signal_pipe[WRITE_END], F_SETFL, retval | O_NONBLOCK); + if (retval < 0) { + err("error fcntl on write pipe: %s\n", strerror(errno)); + goto exit; + } + + /* parse the rules and keep them in memory */ + sysfs_init(); + udev_rules_init(&rules, 1); + + export_initial_seqnum(); + + if (daemonize) { + pid_t pid; + + pid = fork(); + switch (pid) { + case 0: + dbg("daemonized fork running\n"); + break; + case -1: + err("fork of daemon failed: %s\n", strerror(errno)); + rc = 4; + goto exit; + default: + dbg("child [%u] running, parent exits\n", pid); + rc = 0; + goto exit; + } + } + + /* redirect std{out,err} fd's */ + if (!debug) + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) + close(fd); + + /* set scheduling priority for the daemon */ + setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY); + + chdir("/"); + umask(022); + + /* become session leader */ + sid = setsid(); + dbg("our session is %d\n", sid); + + /* OOM_DISABLE == -17 */ + fd = open("/proc/self/oom_adj", O_RDWR); + if (fd < 0) + err("error disabling OOM: %s\n", strerror(errno)); + else { + write(fd, "-17", 3); + close(fd); + } + + fd = open("/dev/kmsg", O_WRONLY); + if (fd > 0) { + const char *str = "<6>udevd version " UDEV_VERSION " started\n"; + + write(fd, str, strlen(str)); + close(fd); + } + + /* set signal handlers */ + memset(&act, 0x00, sizeof(struct sigaction)); + act.sa_handler = (void (*)(int)) sig_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + sigaction(SIGCHLD, &act, NULL); + sigaction(SIGHUP, &act, NULL); + + /* watch rules directory */ + inotify_fd = inotify_init(); + if (inotify_fd >= 0) { + if (udev_rules_dir[0] != '\0') { + inotify_add_watch(inotify_fd, udev_rules_dir, + IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); + } else { + char filename[PATH_MAX]; + + inotify_add_watch(inotify_fd, RULES_LIB_DIR, + IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); + inotify_add_watch(inotify_fd, RULES_ETC_DIR, + IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); + + /* watch dynamic rules directory */ + strlcpy(filename, udev_root, sizeof(filename)); + strlcat(filename, "/"RULES_DYN_DIR, sizeof(filename)); + inotify_add_watch(inotify_fd, filename, + IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); + } + } else if (errno == ENOSYS) + err("the kernel does not support inotify, udevd can't monitor rules file changes\n"); + else + err("inotify_init failed: %s\n", strerror(errno)); + + /* maximum limit of forked childs */ + value = getenv("UDEVD_MAX_CHILDS"); + if (value) + max_childs = strtoul(value, NULL, 10); + else { + int memsize = mem_size_mb(); + if (memsize > 0) + max_childs = 128 + (memsize / 4); + else + max_childs = UDEVD_MAX_CHILDS; + } + info("initialize max_childs to %u\n", max_childs); + + /* start to throttle forking if maximum number of _running_ childs is reached */ + value = getenv("UDEVD_MAX_CHILDS_RUNNING"); + if (value) + max_childs_running = strtoull(value, NULL, 10); + else { + int cpus = cpu_count(); + if (cpus > 0) + max_childs_running = 8 + (8 * cpus); + else + max_childs_running = UDEVD_MAX_CHILDS_RUNNING; + } + info("initialize max_childs_running to %u\n", max_childs_running); + + /* clear environment for forked event processes */ + clearenv(); + + /* export log_priority , as called programs may want to follow that setting */ + sprintf(udev_log, "UDEV_LOG=%i", udev_log_priority); + putenv(udev_log); + if (debug_trace) + putenv("DEBUG=1"); + + maxfd = udevd_sock; + maxfd = UDEV_MAX(maxfd, uevent_netlink_sock); + maxfd = UDEV_MAX(maxfd, signal_pipe[READ_END]); + maxfd = UDEV_MAX(maxfd, inotify_fd); + + while (!udev_exit) { + struct udevd_uevent_msg *msg; + int fdcount; + + FD_ZERO(&readfds); + FD_SET(signal_pipe[READ_END], &readfds); + FD_SET(udevd_sock, &readfds); + FD_SET(uevent_netlink_sock, &readfds); + if (inotify_fd >= 0) + FD_SET(inotify_fd, &readfds); + + fdcount = select(maxfd+1, &readfds, NULL, NULL, NULL); + if (fdcount < 0) { + if (errno != EINTR) + err("error in select: %s\n", strerror(errno)); + continue; + } + + /* get control message */ + if (FD_ISSET(udevd_sock, &readfds)) + get_ctrl_msg(); + + /* get netlink message */ + if (FD_ISSET(uevent_netlink_sock, &readfds)) { + msg = get_netlink_msg(); + if (msg) + msg_queue_insert(msg); + } + + /* received a signal, clear our notification pipe */ + if (FD_ISSET(signal_pipe[READ_END], &readfds)) { + char buf[256]; + + read(signal_pipe[READ_END], &buf, sizeof(buf)); + } + + /* rules directory inotify watch */ + if ((inotify_fd >= 0) && FD_ISSET(inotify_fd, &readfds)) { + int nbytes; + + /* discard all possible events, we can just reload the config */ + if ((ioctl(inotify_fd, FIONREAD, &nbytes) == 0) && nbytes > 0) { + char *buf; + + reload_config = 1; + buf = malloc(nbytes); + if (buf == NULL) { + err("error getting buffer for inotify, disable watching\n"); + close(inotify_fd); + inotify_fd = -1; + } + read(inotify_fd, buf, nbytes); + free(buf); + } + } + + /* rules changed, set by inotify or a HUP signal */ + if (reload_config) { + reload_config = 0; + udev_rules_cleanup(&rules); + udev_rules_init(&rules, 1); + } + + /* forked child has returned */ + if (sigchilds_waiting) { + sigchilds_waiting = 0; + reap_sigchilds(); + } + + if (run_exec_q) { + run_exec_q = 0; + if (!stop_exec_q) + msg_queue_manager(); + } + } + rc = 0; + +exit: + udev_rules_cleanup(&rules); + sysfs_cleanup(); + selinux_exit(); + + if (signal_pipe[READ_END] >= 0) + close(signal_pipe[READ_END]); + if (signal_pipe[WRITE_END] >= 0) + close(signal_pipe[WRITE_END]); + + if (udevd_sock >= 0) + close(udevd_sock); + if (inotify_fd >= 0) + close(inotify_fd); + if (uevent_netlink_sock >= 0) + close(uevent_netlink_sock); + + logging_close(); + + return rc; +} diff --git a/udev/udevd.h b/udev/udevd.h new file mode 100644 index 0000000000..9be34cb0cc --- /dev/null +++ b/udev/udevd.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2004 Ling, Xiaofeng + * Copyright (C) 2004-2006 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "list.h" + +#define UDEVD_PRIORITY -4 +#define UDEV_PRIORITY -2 + +#define EVENT_QUEUE_DIR ".udev/queue" +#define EVENT_FAILED_DIR ".udev/failed" +#define EVENT_SEQNUM ".udev/uevent_seqnum" + +/* maximum limit of forked childs */ +#define UDEVD_MAX_CHILDS 256 +/* start to throttle forking if maximum number of running childs in our session is reached */ +#define UDEVD_MAX_CHILDS_RUNNING 16 + +/* linux/include/linux/kobject.h */ +#define UEVENT_BUFFER_SIZE 2048 +#define UEVENT_NUM_ENVP 32 + +#define UDEVD_CTRL_SOCK_PATH "/org/kernel/udev/udevd" +#define UDEVD_CTRL_MAGIC "udevd_" UDEV_VERSION + +enum udevd_ctrl_msg_type { + UDEVD_CTRL_UNKNOWN, + UDEVD_CTRL_STOP_EXEC_QUEUE, + UDEVD_CTRL_START_EXEC_QUEUE, + UDEVD_CTRL_SET_LOG_LEVEL, + UDEVD_CTRL_SET_MAX_CHILDS, + UDEVD_CTRL_SET_MAX_CHILDS_RUNNING, + UDEVD_CTRL_RELOAD_RULES, + UDEVD_CTRL_ENV, +}; + +struct udevd_ctrl_msg { + char magic[32]; + enum udevd_ctrl_msg_type type; + char buf[256]; +}; + +struct udevd_uevent_msg { + struct list_head node; + pid_t pid; + int exitstatus; + time_t queue_time; + char *action; + char *devpath; + char *subsystem; + char *driver; + dev_t devt; + unsigned long long seqnum; + char *devpath_old; + char *physdevpath; + unsigned int timeout; + char *envp[UEVENT_NUM_ENVP+1]; + char envbuf[]; +}; diff --git a/udev/udevd.xml b/udev/udevd.xml new file mode 100644 index 0000000000..8d22a0c14d --- /dev/null +++ b/udev/udevd.xml @@ -0,0 +1,108 @@ + + + +
+
+ udevd + + + udevd + August 2005 + udev + + + + udevd + 8 + + + + + udevdevent managing daemon + + + + + udevd + + + + + + + + + DESCRIPTION + udevd listens to kernel uevents and passes the incoming events to + udev. It ensures the correct event order and takes care, that events for child + devices are delayed until the parent event has finished the device handling. + The behavior of the running daemon can be changed with + udevadm control. + + + OPTIONS + + + + + Detach and run in the background. + + + + + + Run all events completely serialized. This may be useful if udev triggers + actions or loads kernel modules which cause problems and a slow but continuous + operation is needed, where no events are processed in parallel. + + + + + + + Print log messages to stdout. + + + + + + Print version number. + + + + + + Print help text. + + + + + + ENVIRONMENT + + + + + Overrides the syslog priority specified in the config file. + + + + + + AUTHOR + Written by Kay Sievers kay.sievers@vrfy.org. + + + + SEE ALSO + + udev7 + , + + udevadm8 + + + +
+
diff --git a/udev/udevinfo.c b/udev/udevinfo.c new file mode 100644 index 0000000000..b9ee17c4f1 --- /dev/null +++ b/udev/udevinfo.c @@ -0,0 +1,500 @@ +/* + * Copyright (C) 2004-2006 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static void print_all_attributes(const char *devpath, const char *key) +{ + char path[PATH_SIZE]; + DIR *dir; + struct dirent *dent; + + strlcpy(path, sysfs_path, sizeof(path)); + strlcat(path, devpath, sizeof(path)); + + dir = opendir(path); + if (dir != NULL) { + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + struct stat statbuf; + char filename[PATH_SIZE]; + char *attr_value; + char value[NAME_SIZE]; + size_t len; + + if (dent->d_name[0] == '.') + continue; + + if (strcmp(dent->d_name, "uevent") == 0) + continue; + if (strcmp(dent->d_name, "dev") == 0) + continue; + + strlcpy(filename, path, sizeof(filename)); + strlcat(filename, "/", sizeof(filename)); + strlcat(filename, dent->d_name, sizeof(filename)); + if (lstat(filename, &statbuf) != 0) + continue; + if (S_ISLNK(statbuf.st_mode)) + continue; + + attr_value = sysfs_attr_get_value(devpath, dent->d_name); + if (attr_value == NULL) + continue; + len = strlcpy(value, attr_value, sizeof(value)); + if(len >= sizeof(value)) + len = sizeof(value) - 1; + dbg("attr '%s'='%s'(%zi)\n", dent->d_name, value, len); + + /* remove trailing newlines */ + while (len && value[len-1] == '\n') + value[--len] = '\0'; + + /* skip nonprintable attributes */ + while (len && isprint(value[len-1])) + len--; + if (len) { + dbg("attribute value of '%s' non-printable, skip\n", dent->d_name); + continue; + } + + printf(" %s{%s}==\"%s\"\n", key, dent->d_name, value); + } + } + printf("\n"); +} + +static int print_device_chain(const char *devpath) +{ + struct sysfs_device *dev; + + dev = sysfs_device_get(devpath); + if (dev == NULL) + return -1; + + printf("\n" + "Udevinfo starts with the device specified by the devpath and then\n" + "walks up the chain of parent devices. It prints for every device\n" + "found, all possible attributes in the udev rules key format.\n" + "A rule to match, can be composed by the attributes of the device\n" + "and the attributes from one single parent device.\n" + "\n"); + + printf(" looking at device '%s':\n", dev->devpath); + printf(" KERNEL==\"%s\"\n", dev->kernel); + printf(" SUBSYSTEM==\"%s\"\n", dev->subsystem); + printf(" DRIVER==\"%s\"\n", dev->driver); + print_all_attributes(dev->devpath, "ATTR"); + + /* walk up the chain of devices */ + while (1) { + dev = sysfs_device_get_parent(dev); + if (dev == NULL) + break; + printf(" looking at parent device '%s':\n", dev->devpath); + printf(" KERNELS==\"%s\"\n", dev->kernel); + printf(" SUBSYSTEMS==\"%s\"\n", dev->subsystem); + printf(" DRIVERS==\"%s\"\n", dev->driver); + + print_all_attributes(dev->devpath, "ATTRS"); + } + + return 0; +} + +static void print_record(struct udevice *udev) +{ + struct name_entry *name_loop; + + printf("P: %s\n", udev->dev->devpath); + printf("N: %s\n", udev->name); + list_for_each_entry(name_loop, &udev->symlink_list, node) + printf("S: %s\n", name_loop->name); + if (udev->link_priority != 0) + printf("L: %i\n", udev->link_priority); + if (udev->partitions != 0) + printf("A:%u\n", udev->partitions); + if (udev->ignore_remove) + printf("R:%u\n", udev->ignore_remove); + list_for_each_entry(name_loop, &udev->env_list, node) + printf("E: %s\n", name_loop->name); +} + +static void export_db(void) { + LIST_HEAD(name_list); + struct name_entry *name_loop; + + udev_db_get_all_entries(&name_list); + list_for_each_entry(name_loop, &name_list, node) { + struct udevice *udev_db; + + udev_db = udev_device_init(NULL); + if (udev_db == NULL) + continue; + if (udev_db_get_device(udev_db, name_loop->name) == 0) + print_record(udev_db); + printf("\n"); + udev_device_cleanup(udev_db); + } + name_list_cleanup(&name_list); +} + +static int lookup_device_by_name(struct udevice *udev, const char *name) +{ + LIST_HEAD(name_list); + int count; + struct name_entry *device; + int rc = -1; + + count = udev_db_get_devices_by_name(name, &name_list); + if (count <= 0) + goto out; + + info("found %i devices for '%s'\n", count, name); + + /* select the device that seems to match */ + list_for_each_entry(device, &name_list, node) { + char filename[PATH_SIZE]; + struct stat statbuf; + + udev_device_init(udev); + if (udev_db_get_device(udev, device->name) != 0) + continue; + info("found db entry '%s'\n", device->name); + + /* make sure, we don't get a link of a differnt device */ + strlcpy(filename, udev_root, sizeof(filename)); + strlcat(filename, "/", sizeof(filename)); + strlcat(filename, name, sizeof(filename)); + if (stat(filename, &statbuf) != 0) + continue; + if (major(udev->devt) > 0 && udev->devt != statbuf.st_rdev) { + info("skip '%s', dev_t doesn't match\n", udev->name); + continue; + } + rc = 0; + break; + } +out: + name_list_cleanup(&name_list); + return rc; +} + +static int stat_device(const char *name, int export, const char *prefix) +{ + struct stat statbuf; + + if (stat(name, &statbuf) != 0) + return -1; + + if (export) { + if (prefix == NULL) + prefix = "INFO_"; + printf("%sMAJOR=%d\n" + "%sMINOR=%d\n", + prefix, major(statbuf.st_dev), + prefix, minor(statbuf.st_dev)); + } else + printf("%d %d\n", major(statbuf.st_dev), minor(statbuf.st_dev)); + return 0; +} + +int udevinfo(int argc, char *argv[], char *envp[]) +{ + int option; + struct udevice *udev; + int root = 0; + int export = 0; + const char *export_prefix = NULL; + + static const struct option options[] = { + { "name", 1, NULL, 'n' }, + { "path", 1, NULL, 'p' }, + { "query", 1, NULL, 'q' }, + { "attribute-walk", 0, NULL, 'a' }, + { "export-db", 0, NULL, 'e' }, + { "root", 0, NULL, 'r' }, + { "device-id-of-file", 1, NULL, 'd' }, + { "export", 0, NULL, 'x' }, + { "export-prefix", 1, NULL, 'P' }, + { "version", 0, NULL, 1 }, /* -V outputs braindead format */ + { "help", 0, NULL, 'h' }, + {} + }; + + enum action_type { + ACTION_NONE, + ACTION_QUERY, + ACTION_ATTRIBUTE_WALK, + ACTION_ROOT, + ACTION_DEVICE_ID_FILE, + } action = ACTION_NONE; + + enum query_type { + QUERY_NONE, + QUERY_NAME, + QUERY_PATH, + QUERY_SYMLINK, + QUERY_ENV, + QUERY_ALL, + } query = QUERY_NONE; + + char path[PATH_SIZE] = ""; + char name[PATH_SIZE] = ""; + struct name_entry *name_loop; + int rc = 0; + + logging_init("udevinfo"); + udev_config_init(); + sysfs_init(); + + udev = udev_device_init(NULL); + if (udev == NULL) { + rc = 1; + goto exit; + } + + while (1) { + option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL); + if (option == -1) + break; + + dbg("option '%c'\n", option); + switch (option) { + case 'n': + /* remove /dev if given */ + if (strncmp(optarg, udev_root, strlen(udev_root)) == 0) + strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name)); + else + strlcpy(name, optarg, sizeof(name)); + remove_trailing_chars(name, '/'); + dbg("name: %s\n", name); + break; + case 'p': + /* remove /sys if given */ + if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0) + strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path)); + else + strlcpy(path, optarg, sizeof(path)); + remove_trailing_chars(path, '/'); + + /* possibly resolve to real devpath */ + if (sysfs_resolve_link(path, sizeof(path)) != 0) { + char temp[PATH_SIZE]; + char *pos; + + /* also check if the parent is a link */ + strlcpy(temp, path, sizeof(temp)); + pos = strrchr(temp, '/'); + if (pos != 0) { + char tail[PATH_SIZE]; + + strlcpy(tail, pos, sizeof(tail)); + pos[0] = '\0'; + if (sysfs_resolve_link(temp, sizeof(temp)) == 0) { + strlcpy(path, temp, sizeof(path)); + strlcat(path, tail, sizeof(path)); + } + } + } + dbg("path: %s\n", path); + break; + case 'q': + action = ACTION_QUERY; + if (strcmp(optarg, "name") == 0) { + query = QUERY_NAME; + break; + } + if (strcmp(optarg, "symlink") == 0) { + query = QUERY_SYMLINK; + break; + } + if (strcmp(optarg, "path") == 0) { + query = QUERY_PATH; + break; + } + if (strcmp(optarg, "env") == 0) { + query = QUERY_ENV; + break; + } + if (strcmp(optarg, "all") == 0) { + query = QUERY_ALL; + break; + } + fprintf(stderr, "unknown query type\n"); + rc = 2; + goto exit; + case 'r': + if (action == ACTION_NONE) + action = ACTION_ROOT; + root = 1; + break; + case 'd': + action = ACTION_DEVICE_ID_FILE; + strlcpy(name, optarg, sizeof(name)); + break; + case 'a': + action = ACTION_ATTRIBUTE_WALK; + break; + case 'e': + export_db(); + goto exit; + case 'x': + export = 1; + break; + case 'P': + export_prefix = optarg; + break; + case 1: + printf("%s\n", UDEV_VERSION); + goto exit; + case 'V': + printf("udevinfo, version %s\n", UDEV_VERSION); + goto exit; + case 'h': + printf("Usage: udevadm info OPTIONS\n" + " --query= query database for the specified value:\n" + " name name of device node\n" + " symlink pointing to node\n" + " path sysfs device path\n" + " env the device related imported environment\n" + " all all values\n" + " --path= sysfs device path used for query or chain\n" + " --name= node or symlink name used for query\n" + " --root prepend to query result or print udev_root\n" + " --attribute-walk print all key matches while walking along chain\n" + " of parent devices\n" + " --device-id-of-file= print major/minor of underlying device\n" + " --export-db export the content of the udev database\n" + " --help print this text\n" + "\n"); + goto exit; + default: + goto exit; + } + } + + /* run action */ + switch (action) { + case ACTION_QUERY: + /* needs devpath or node/symlink name for query */ + if (path[0] != '\0') { + if (udev_db_get_device(udev, path) != 0) { + fprintf(stderr, "no record for '%s' in database\n", path); + rc = 3; + goto exit; + } + } else if (name[0] != '\0') { + if (lookup_device_by_name(udev, name) != 0) { + fprintf(stderr, "node name not found\n"); + rc = 4; + goto exit; + } + } else { + fprintf(stderr, "query needs --path or node --name specified\n"); + rc = 4; + goto exit; + } + + switch(query) { + case QUERY_NAME: + if (root) + printf("%s/%s\n", udev_root, udev->name); + else + printf("%s\n", udev->name); + break; + case QUERY_SYMLINK: + list_for_each_entry(name_loop, &udev->symlink_list, node) { + char c = name_loop->node.next != &udev->symlink_list ? ' ' : '\n'; + + if (root) + printf("%s/%s%c", udev_root, name_loop->name, c); + else + printf("%s%c", name_loop->name, c); + } + break; + case QUERY_PATH: + printf("%s\n", udev->dev->devpath); + goto exit; + case QUERY_ENV: + list_for_each_entry(name_loop, &udev->env_list, node) + printf("%s\n", name_loop->name); + break; + case QUERY_ALL: + print_record(udev); + break; + default: + fprintf(stderr, "unknown query type\n"); + break; + } + break; + case ACTION_ATTRIBUTE_WALK: + if (path[0] != '\0') { + if (print_device_chain(path) != 0) { + fprintf(stderr, "no valid sysfs device found\n"); + rc = 4; + goto exit; + } + } else if (name[0] != '\0') { + if (lookup_device_by_name(udev, name) != 0) { + fprintf(stderr, "node name not found\n"); + rc = 4; + goto exit; + } + if (print_device_chain(udev->dev->devpath) != 0) { + fprintf(stderr, "no valid sysfs device found\n"); + rc = 4; + goto exit; + } + } else { + fprintf(stderr, "attribute walk needs --path or node --name specified\n"); + rc = 5; + goto exit; + } + break; + case ACTION_DEVICE_ID_FILE: + if (stat_device(name, export, export_prefix) != 0) + rc = 6; + break; + case ACTION_ROOT: + printf("%s\n", udev_root); + break; + default: + fprintf(stderr, "missing option\n"); + rc = 1; + break; + } + +exit: + udev_device_cleanup(udev); + sysfs_cleanup(); + logging_close(); + return rc; +} diff --git a/udev/udevmonitor.c b/udev/udevmonitor.c new file mode 100644 index 0000000000..2430dd39a5 --- /dev/null +++ b/udev/udevmonitor.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2004-2006 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udevd.h" + +static int uevent_netlink_sock = -1; +static int udev_monitor_sock = -1; +static volatile int udev_exit; + +static int init_udev_monitor_socket(void) +{ + struct sockaddr_un saddr; + socklen_t addrlen; + int retval; + + memset(&saddr, 0x00, sizeof(saddr)); + saddr.sun_family = AF_LOCAL; + /* use abstract namespace for socket path */ + strcpy(&saddr.sun_path[1], "/org/kernel/udev/monitor"); + addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); + + udev_monitor_sock = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (udev_monitor_sock == -1) { + fprintf(stderr, "error getting socket: %s\n", strerror(errno)); + return -1; + } + + /* the bind takes care of ensuring only one copy running */ + retval = bind(udev_monitor_sock, (struct sockaddr *) &saddr, addrlen); + if (retval < 0) { + fprintf(stderr, "bind failed: %s\n", strerror(errno)); + close(udev_monitor_sock); + udev_monitor_sock = -1; + return -1; + } + + return 0; +} + +static int init_uevent_netlink_sock(void) +{ + struct sockaddr_nl snl; + int retval; + + memset(&snl, 0x00, sizeof(struct sockaddr_nl)); + snl.nl_family = AF_NETLINK; + snl.nl_pid = getpid(); + snl.nl_groups = 1; + + uevent_netlink_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + if (uevent_netlink_sock == -1) { + fprintf(stderr, "error getting socket: %s\n", strerror(errno)); + return -1; + } + + retval = bind(uevent_netlink_sock, (struct sockaddr *) &snl, + sizeof(struct sockaddr_nl)); + if (retval < 0) { + fprintf(stderr, "bind failed: %s\n", strerror(errno)); + close(uevent_netlink_sock); + uevent_netlink_sock = -1; + return -1; + } + + return 0; +} + +static void asmlinkage sig_handler(int signum) +{ + if (signum == SIGINT || signum == SIGTERM) + udev_exit = 1; +} + +static const char *search_key(const char *searchkey, const char *buf, size_t buflen) +{ + size_t bufpos = 0; + size_t searchkeylen = strlen(searchkey); + + while (bufpos < buflen) { + const char *key; + int keylen; + + key = &buf[bufpos]; + keylen = strlen(key); + if (keylen == 0) + break; + if ((strncmp(searchkey, key, searchkeylen) == 0) && key[searchkeylen] == '=') + return &key[searchkeylen + 1]; + bufpos += keylen + 1; + } + return NULL; +} + +int udevmonitor(int argc, char *argv[], char *envp[]) +{ + struct sigaction act; + int option; + int env = 0; + int kernel = 0; + int udev = 0; + fd_set readfds; + int retval = 0; + + static const struct option options[] = { + { "environment", 0, NULL, 'e' }, + { "kernel", 0, NULL, 'k' }, + { "udev", 0, NULL, 'u' }, + { "help", 0, NULL, 'h' }, + {} + }; + + while (1) { + option = getopt_long(argc, argv, "ekuh", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'e': + env = 1; + break; + case 'k': + kernel = 1; + break; + case 'u': + udev = 1; + break; + case 'h': + printf("Usage: udevadm monitor [--environment] [--kernel] [--udev] [--help]\n" + " --env print the whole event environment\n" + " --kernel print kernel uevents\n" + " --udev print udev events\n" + " --help print this help text\n\n"); + default: + goto out; + } + } + + if (!kernel && !udev) { + kernel = 1; + udev =1; + } + + if (getuid() != 0 && kernel) { + fprintf(stderr, "root privileges needed to subscribe to kernel events\n"); + goto out; + } + + /* set signal handlers */ + memset(&act, 0x00, sizeof(struct sigaction)); + act.sa_handler = (void (*)(int)) sig_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + + printf("udevmonitor will print the received events for:\n"); + if (udev) { + retval = init_udev_monitor_socket(); + if (retval) + goto out; + printf("UDEV the event which udev sends out after rule processing\n"); + } + if (kernel) { + retval = init_uevent_netlink_sock(); + if (retval) + goto out; + printf("UEVENT the kernel uevent\n"); + } + printf("\n"); + + while (!udev_exit) { + char buf[UEVENT_BUFFER_SIZE*2]; + ssize_t buflen; + ssize_t bufpos; + ssize_t keys; + int fdcount; + struct timeval tv; + struct timezone tz; + char timestr[64]; + const char *source = NULL; + const char *devpath, *action, *subsys; + + buflen = 0; + FD_ZERO(&readfds); + if (uevent_netlink_sock >= 0) + FD_SET(uevent_netlink_sock, &readfds); + if (udev_monitor_sock >= 0) + FD_SET(udev_monitor_sock, &readfds); + + fdcount = select(UDEV_MAX(uevent_netlink_sock, udev_monitor_sock)+1, &readfds, NULL, NULL, NULL); + if (fdcount < 0) { + if (errno != EINTR) + fprintf(stderr, "error receiving uevent message: %s\n", strerror(errno)); + continue; + } + + if (gettimeofday(&tv, &tz) == 0) { + snprintf(timestr, sizeof(timestr), "%llu.%06u", + (unsigned long long) tv.tv_sec, (unsigned int) tv.tv_usec); + } else + timestr[0] = '\0'; + + if ((uevent_netlink_sock >= 0) && FD_ISSET(uevent_netlink_sock, &readfds)) { + buflen = recv(uevent_netlink_sock, &buf, sizeof(buf), 0); + if (buflen <= 0) { + fprintf(stderr, "error receiving uevent message: %s\n", strerror(errno)); + continue; + } + source = "UEVENT"; + } + + if ((udev_monitor_sock >= 0) && FD_ISSET(udev_monitor_sock, &readfds)) { + buflen = recv(udev_monitor_sock, &buf, sizeof(buf), 0); + if (buflen <= 0) { + fprintf(stderr, "error receiving udev message: %s\n", strerror(errno)); + continue; + } + source = "UDEV "; + } + + if (buflen == 0) + continue; + + keys = strlen(buf) + 1; /* start of payload */ + devpath = search_key("DEVPATH", &buf[keys], buflen); + action = search_key("ACTION", &buf[keys], buflen); + subsys = search_key("SUBSYSTEM", &buf[keys], buflen); + printf("%s[%s] %-8s %s (%s)\n", source, timestr, action, devpath, subsys); + + /* print environment */ + bufpos = keys; + if (env) { + while (bufpos < buflen) { + int keylen; + char *key; + + key = &buf[bufpos]; + keylen = strlen(key); + if (keylen == 0) + break; + printf("%s\n", key); + bufpos += keylen + 1; + } + printf("\n"); + } + } + +out: + if (uevent_netlink_sock >= 0) + close(uevent_netlink_sock); + if (udev_monitor_sock >= 0) + close(udev_monitor_sock); + + if (retval) + return 1; + return 0; +} diff --git a/udev/udevsettle.c b/udev/udevsettle.c new file mode 100644 index 0000000000..cb63a66bba --- /dev/null +++ b/udev/udevsettle.c @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2006 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udevd.h" + +#define DEFAULT_TIMEOUT 180 +#define LOOP_PER_SECOND 20 + +static void print_queue(const char *dir) +{ + LIST_HEAD(files); + struct name_entry *item; + + if (add_matching_files(&files, dir, NULL) < 0) + return; + + printf("\n\nAfter the udevadm settle timeout, the events queue contains:\n\n"); + + list_for_each_entry(item, &files, node) { + char target[NAME_SIZE]; + size_t len; + const char *filename = strrchr(item->name, '/'); + + if (filename == NULL) + continue; + filename++; + if (*filename == '\0') + continue; + + len = readlink(item->name, target, sizeof(target)); + if (len < 0) + continue; + target[len] = '\0'; + + printf("%s: %s\n", filename, target); + } + + printf("\n\n"); +} + +int udevsettle(int argc, char *argv[], char *envp[]) +{ + char queuename[PATH_SIZE]; + char filename[PATH_SIZE]; + unsigned long long seq_kernel; + unsigned long long seq_udev; + char seqnum[32]; + int fd; + ssize_t len; + int timeout = DEFAULT_TIMEOUT; + int loop; + static const struct option options[] = { + { "timeout", 1, NULL, 't' }, + { "help", 0, NULL, 'h' }, + {} + }; + int option; + int rc = 1; + int seconds; + + logging_init("udevsettle"); + udev_config_init(); + dbg("version %s\n", UDEV_VERSION); + sysfs_init(); + + while (1) { + option = getopt_long(argc, argv, "t:h", options, NULL); + if (option == -1) + break; + + switch (option) { + case 't': + seconds = atoi(optarg); + if (seconds > 0) + timeout = seconds; + else + fprintf(stderr, "invalid timeout value\n"); + dbg("timeout=%i\n", timeout); + break; + case 'h': + printf("Usage: udevadm settle [--help] [--timeout=]\n\n"); + goto exit; + } + } + + strlcpy(queuename, udev_root, sizeof(queuename)); + strlcat(queuename, "/" EVENT_QUEUE_DIR, sizeof(queuename)); + + loop = timeout * LOOP_PER_SECOND; + while (loop--) { + /* wait for events in queue to finish */ + while (loop--) { + struct stat statbuf; + + if (stat(queuename, &statbuf) < 0) { + info("queue is empty\n"); + break; + } + usleep(1000 * 1000 / LOOP_PER_SECOND); + } + if (loop <= 0) { + info("timeout waiting for queue\n"); + print_queue(queuename); + goto exit; + } + + /* read current udev seqnum */ + strlcpy(filename, udev_root, sizeof(filename)); + strlcat(filename, "/" EVENT_SEQNUM, sizeof(filename)); + fd = open(filename, O_RDONLY); + if (fd < 0) + goto exit; + len = read(fd, seqnum, sizeof(seqnum)-1); + close(fd); + if (len <= 0) + goto exit; + seqnum[len] = '\0'; + seq_udev = strtoull(seqnum, NULL, 10); + info("udev seqnum = %llu\n", seq_udev); + + /* read current kernel seqnum */ + strlcpy(filename, sysfs_path, sizeof(filename)); + strlcat(filename, "/kernel/uevent_seqnum", sizeof(filename)); + fd = open(filename, O_RDONLY); + if (fd < 0) + goto exit; + len = read(fd, seqnum, sizeof(seqnum)-1); + close(fd); + if (len <= 0) + goto exit; + seqnum[len] = '\0'; + seq_kernel = strtoull(seqnum, NULL, 10); + info("kernel seqnum = %llu\n", seq_kernel); + + /* make sure all kernel events have arrived in the queue */ + if (seq_udev >= seq_kernel) { + info("queue is empty and no pending events left\n"); + rc = 0; + goto exit; + } + usleep(1000 * 1000 / LOOP_PER_SECOND); + info("queue is empty, but events still pending\n"); + } + +exit: + sysfs_cleanup(); + logging_close(); + return rc; +} diff --git a/udev/udevtest.c b/udev/udevtest.c new file mode 100644 index 0000000000..d5e90b02c6 --- /dev/null +++ b/udev/udevtest.c @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2003-2004 Greg Kroah-Hartman + * Copyright (C) 2004-2006 Kay Sievers + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udev_rules.h" + +static int import_uevent_var(const char *devpath) +{ + char path[PATH_SIZE]; + static char value[4096]; /* must stay, used with putenv */ + ssize_t size; + int fd; + char *key; + char *next; + int rc = -1; + + /* read uevent file */ + strlcpy(path, sysfs_path, sizeof(path)); + strlcat(path, devpath, sizeof(path)); + strlcat(path, "/uevent", sizeof(path)); + fd = open(path, O_RDONLY); + if (fd < 0) + goto out; + size = read(fd, value, sizeof(value)); + close(fd); + if (size < 0) + goto out; + value[size] = '\0'; + + /* import keys into environment */ + key = value; + while (key[0] != '\0') { + next = strchr(key, '\n'); + if (next == NULL) + goto out; + next[0] = '\0'; + info("import into environment: '%s'\n", key); + putenv(key); + key = &next[1]; + } + rc = 0; +out: + return rc; +} + +int udevtest(int argc, char *argv[], char *envp[]) +{ + int force = 0; + const char *action = "add"; + const char *subsystem = NULL; + const char *devpath = NULL; + struct udevice *udev; + struct sysfs_device *dev; + struct udev_rules rules = {}; + int retval; + int rc = 0; + + static const struct option options[] = { + { "action", 1, NULL, 'a' }, + { "subsystem", 1, NULL, 's' }, + { "force", 0, NULL, 'f' }, + { "help", 0, NULL, 'h' }, + {} + }; + + info("version %s\n", UDEV_VERSION); + udev_config_init(); + if (udev_log_priority < LOG_INFO) { + char priority[32]; + + udev_log_priority = LOG_INFO; + sprintf(priority, "%i", udev_log_priority); + setenv("UDEV_LOG", priority, 1); + } + + while (1) { + int option; + + option = getopt_long(argc, argv, "a:s:fh", options, NULL); + if (option == -1) + break; + + dbg("option '%c'\n", option); + switch (option) { + case 'a': + action = optarg; + break; + case 's': + subsystem = optarg; + break; + case 'f': + force = 1; + break; + case 'h': + printf("Usage: udevadm test OPTIONS \n" + " --action= set action string\n" + " --subsystem= set subsystem string\n" + " --force don't skip node/link creation\n" + " --help print this help text\n\n"); + exit(0); + default: + exit(1); + } + } + devpath = argv[optind]; + + if (devpath == NULL) { + fprintf(stderr, "devpath parameter missing\n"); + rc = 1; + goto exit; + } + + printf("This program is for debugging only, it does not run any program,\n" + "specified by a RUN key. It may show incorrect results, because\n" + "some values may be different, or not available at a simulation run.\n" + "\n"); + + sysfs_init(); + udev_rules_init(&rules, 0); + + /* remove /sys if given */ + if (strncmp(devpath, sysfs_path, strlen(sysfs_path)) == 0) + devpath = &devpath[strlen(sysfs_path)]; + + dev = sysfs_device_get(devpath); + if (dev == NULL) { + fprintf(stderr, "unable to open device '%s'\n", devpath); + rc = 2; + goto exit; + } + + udev = udev_device_init(NULL); + if (udev == NULL) { + fprintf(stderr, "error initializing device\n"); + rc = 3; + goto exit; + } + + if (subsystem != NULL) + strlcpy(dev->subsystem, subsystem, sizeof(dev->subsystem)); + + /* override built-in sysfs device */ + udev->dev = dev; + strlcpy(udev->action, action, sizeof(udev->action)); + udev->devt = udev_device_get_devt(udev); + + /* simulate node creation with test flag */ + if (!force) + udev->test_run = 1; + + setenv("DEVPATH", udev->dev->devpath, 1); + setenv("SUBSYSTEM", udev->dev->subsystem, 1); + setenv("ACTION", udev->action, 1); + import_uevent_var(udev->dev->devpath); + + info("looking at device '%s' from subsystem '%s'\n", udev->dev->devpath, udev->dev->subsystem); + retval = udev_device_event(&rules, udev); + + if (udev->event_timeout >= 0) + info("custom event timeout: %i\n", udev->event_timeout); + + if (retval == 0 && !udev->ignore_device && udev_run) { + struct name_entry *name_loop; + + list_for_each_entry(name_loop, &udev->run_list, node) { + char program[PATH_SIZE]; + + strlcpy(program, name_loop->name, sizeof(program)); + udev_rules_apply_format(udev, program, sizeof(program)); + info("run: '%s'\n", program); + } + } + udev_device_cleanup(udev); + +exit: + udev_rules_cleanup(&rules); + sysfs_cleanup(); + return rc; +} diff --git a/udev/udevtrigger.c b/udev/udevtrigger.c new file mode 100644 index 0000000000..d4b10d06ab --- /dev/null +++ b/udev/udevtrigger.c @@ -0,0 +1,712 @@ +/* + * Copyright (C) 2004-2006 Kay Sievers + * Copyright (C) 2006 Hannes Reinecke + * + * 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 version 2 of the License. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udevd.h" +#include "udev_rules.h" + +static int verbose; +static int dry_run; +LIST_HEAD(device_list); +LIST_HEAD(filter_subsystem_match_list); +LIST_HEAD(filter_subsystem_nomatch_list); +LIST_HEAD(filter_attr_match_list); +LIST_HEAD(filter_attr_nomatch_list); +static int sock = -1; +static struct sockaddr_un saddr; +static socklen_t saddrlen; + +/* devices that should run last cause of their dependencies */ +static int delay_device(const char *devpath) +{ + static const char *delay_device_list[] = { + "*/md*", + "*/dm-*", + NULL + }; + int i; + + for (i = 0; delay_device_list[i] != NULL; i++) + if (fnmatch(delay_device_list[i], devpath, 0) == 0) + return 1; + return 0; +} + +static int device_list_insert(const char *path) +{ + char filename[PATH_SIZE]; + char devpath[PATH_SIZE]; + struct stat statbuf; + + dbg("add '%s'\n" , path); + + /* we only have a device, if we have an uevent file */ + strlcpy(filename, path, sizeof(filename)); + strlcat(filename, "/uevent", sizeof(filename)); + if (stat(filename, &statbuf) < 0) + return -1; + if (!(statbuf.st_mode & S_IWUSR)) + return -1; + + strlcpy(devpath, &path[strlen(sysfs_path)], sizeof(devpath)); + + /* resolve possible link to real target */ + if (lstat(path, &statbuf) < 0) + return -1; + if (S_ISLNK(statbuf.st_mode)) + if (sysfs_resolve_link(devpath, sizeof(devpath)) != 0) + return -1; + + name_list_add(&device_list, devpath, 1); + return 0; +} + +static void trigger_uevent(const char *devpath, const char *action) +{ + char filename[PATH_SIZE]; + int fd; + + strlcpy(filename, sysfs_path, sizeof(filename)); + strlcat(filename, devpath, sizeof(filename)); + strlcat(filename, "/uevent", sizeof(filename)); + + if (verbose) + printf("%s\n", devpath); + + if (dry_run) + return; + + fd = open(filename, O_WRONLY); + if (fd < 0) { + dbg("error on opening %s: %s\n", filename, strerror(errno)); + return; + } + + if (write(fd, action, strlen(action)) < 0) + info("error writing '%s' to '%s': %s\n", action, filename, strerror(errno)); + + close(fd); +} + +static int pass_to_socket(const char *devpath, const char *action, const char *env) +{ + struct udevice udev; + struct name_entry *name_loop; + char buf[4096]; + size_t bufpos = 0; + ssize_t count; + char path[PATH_SIZE]; + int fd; + char link_target[PATH_SIZE]; + int len; + int err = 0; + + if (verbose) + printf("%s\n", devpath); + + udev_device_init(&udev); + udev_db_get_device(&udev, devpath); + + /* add header */ + bufpos = snprintf(buf, sizeof(buf)-1, "%s@%s", action, devpath); + bufpos++; + + /* add cookie */ + if (env != NULL) { + bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "%s", env); + bufpos++; + } + + /* add standard keys */ + bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "DEVPATH=%s", devpath); + bufpos++; + bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "ACTION=%s", action); + bufpos++; + + /* add subsystem */ + strlcpy(path, sysfs_path, sizeof(path)); + strlcat(path, devpath, sizeof(path)); + strlcat(path, "/subsystem", sizeof(path)); + len = readlink(path, link_target, sizeof(link_target)); + if (len > 0) { + char *pos; + + link_target[len] = '\0'; + pos = strrchr(link_target, '/'); + if (pos != NULL) { + bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "SUBSYSTEM=%s", &pos[1]); + bufpos++; + } + } + + /* add symlinks and node name */ + path[0] = '\0'; + list_for_each_entry(name_loop, &udev.symlink_list, node) { + strlcat(path, udev_root, sizeof(path)); + strlcat(path, "/", sizeof(path)); + strlcat(path, name_loop->name, sizeof(path)); + strlcat(path, " ", sizeof(path)); + } + remove_trailing_chars(path, ' '); + if (path[0] != '\0') { + bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "DEVLINKS=%s", path); + bufpos++; + } + if (udev.name[0] != '\0') { + strlcpy(path, udev_root, sizeof(path)); + strlcat(path, "/", sizeof(path)); + strlcat(path, udev.name, sizeof(path)); + bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "DEVNAME=%s", path); + bufpos++; + } + + /* add keys from device "uevent" file */ + strlcpy(path, sysfs_path, sizeof(path)); + strlcat(path, devpath, sizeof(path)); + strlcat(path, "/uevent", sizeof(path)); + fd = open(path, O_RDONLY); + if (fd >= 0) { + char value[4096]; + + count = read(fd, value, sizeof(value)); + close(fd); + if (count > 0) { + char *key; + + value[count] = '\0'; + key = value; + while (key[0] != '\0') { + char *next; + + next = strchr(key, '\n'); + if (next == NULL) + break; + next[0] = '\0'; + bufpos += strlcpy(&buf[bufpos], key, sizeof(buf) - bufpos-1); + bufpos++; + key = &next[1]; + } + } + } + + /* add keys from database */ + list_for_each_entry(name_loop, &udev.env_list, node) { + bufpos += strlcpy(&buf[bufpos], name_loop->name, sizeof(buf) - bufpos-1); + bufpos++; + } + if (bufpos > sizeof(buf)) + bufpos = sizeof(buf); + + count = sendto(sock, &buf, bufpos, 0, (struct sockaddr *)&saddr, saddrlen); + if (count < 0) + err = -1; + + return err; +} + +static void exec_list(const char *action, const char *env) +{ + struct name_entry *loop_device; + struct name_entry *tmp_device; + + list_for_each_entry_safe(loop_device, tmp_device, &device_list, node) { + if (delay_device(loop_device->name)) + continue; + if (sock >= 0) + pass_to_socket(loop_device->name, action, env); + else + trigger_uevent(loop_device->name, action); + list_del(&loop_device->node); + free(loop_device); + } + + /* trigger remaining delayed devices */ + list_for_each_entry_safe(loop_device, tmp_device, &device_list, node) { + if (sock >= 0) + pass_to_socket(loop_device->name, action, env); + else + trigger_uevent(loop_device->name, action); + list_del(&loop_device->node); + free(loop_device); + } +} + +static int subsystem_filtered(const char *subsystem) +{ + struct name_entry *loop_name; + + /* skip devices matching the listed subsystems */ + list_for_each_entry(loop_name, &filter_subsystem_nomatch_list, node) + if (fnmatch(loop_name->name, subsystem, 0) == 0) + return 1; + + /* skip devices not matching the listed subsystems */ + if (!list_empty(&filter_subsystem_match_list)) { + list_for_each_entry(loop_name, &filter_subsystem_match_list, node) + if (fnmatch(loop_name->name, subsystem, 0) == 0) + return 0; + return 1; + } + + return 0; +} + +static int attr_match(const char *path, const char *attr_value) +{ + char attr[NAME_SIZE]; + char file[PATH_SIZE]; + char *match_value; + + strlcpy(attr, attr_value, sizeof(attr)); + + /* separate attr and match value */ + match_value = strchr(attr, '='); + if (match_value != NULL) { + match_value[0] = '\0'; + match_value = &match_value[1]; + } + + strlcpy(file, path, sizeof(file)); + strlcat(file, "/", sizeof(file)); + strlcat(file, attr, sizeof(file)); + + if (match_value != NULL) { + /* match file content */ + char value[NAME_SIZE]; + int fd; + ssize_t size; + + fd = open(file, O_RDONLY); + if (fd < 0) + return 0; + size = read(fd, value, sizeof(value)); + close(fd); + if (size < 0) + return 0; + value[size] = '\0'; + remove_trailing_chars(value, '\n'); + + /* match if attribute value matches */ + if (fnmatch(match_value, value, 0) == 0) + return 1; + } else { + /* match if attribute exists */ + struct stat statbuf; + + if (stat(file, &statbuf) == 0) + return 1; + } + return 0; +} + +static int attr_filtered(const char *path) +{ + struct name_entry *loop_name; + + /* skip devices matching the listed sysfs attributes */ + list_for_each_entry(loop_name, &filter_attr_nomatch_list, node) + if (attr_match(path, loop_name->name)) + return 1; + + /* skip devices not matching the listed sysfs attributes */ + if (!list_empty(&filter_attr_match_list)) { + list_for_each_entry(loop_name, &filter_attr_match_list, node) + if (attr_match(path, loop_name->name)) + return 0; + return 1; + } + return 0; +} + +enum scan_type { + SCAN_DEVICES, + SCAN_SUBSYSTEM, +}; + +static void scan_subsystem(const char *subsys, enum scan_type scan) +{ + char base[PATH_SIZE]; + DIR *dir; + struct dirent *dent; + const char *subdir; + + if (scan == SCAN_DEVICES) + subdir = "/devices"; + else if (scan == SCAN_SUBSYSTEM) + subdir = "/drivers"; + else + return; + + strlcpy(base, sysfs_path, sizeof(base)); + strlcat(base, "/", sizeof(base)); + strlcat(base, subsys, sizeof(base)); + + dir = opendir(base); + if (dir != NULL) { + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char dirname[PATH_SIZE]; + DIR *dir2; + struct dirent *dent2; + + if (dent->d_name[0] == '.') + continue; + + if (scan == SCAN_DEVICES) + if (subsystem_filtered(dent->d_name)) + continue; + + strlcpy(dirname, base, sizeof(dirname)); + strlcat(dirname, "/", sizeof(dirname)); + strlcat(dirname, dent->d_name, sizeof(dirname)); + + if (scan == SCAN_SUBSYSTEM) { + if (!subsystem_filtered("subsystem")) + device_list_insert(dirname); + if (subsystem_filtered("drivers")) + continue; + } + + strlcat(dirname, subdir, sizeof(dirname)); + + /* look for devices/drivers */ + dir2 = opendir(dirname); + if (dir2 != NULL) { + for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { + char dirname2[PATH_SIZE]; + + if (dent2->d_name[0] == '.') + continue; + + strlcpy(dirname2, dirname, sizeof(dirname2)); + strlcat(dirname2, "/", sizeof(dirname2)); + strlcat(dirname2, dent2->d_name, sizeof(dirname2)); + if (attr_filtered(dirname2)) + continue; + device_list_insert(dirname2); + } + closedir(dir2); + } + } + closedir(dir); + } +} + +static void scan_block(void) +{ + char base[PATH_SIZE]; + DIR *dir; + struct dirent *dent; + + if (subsystem_filtered("block")) + return; + + strlcpy(base, sysfs_path, sizeof(base)); + strlcat(base, "/block", sizeof(base)); + + dir = opendir(base); + if (dir != NULL) { + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char dirname[PATH_SIZE]; + DIR *dir2; + struct dirent *dent2; + + if (dent->d_name[0] == '.') + continue; + + strlcpy(dirname, base, sizeof(dirname)); + strlcat(dirname, "/", sizeof(dirname)); + strlcat(dirname, dent->d_name, sizeof(dirname)); + if (attr_filtered(dirname)) + continue; + if (device_list_insert(dirname) != 0) + continue; + + /* look for partitions */ + dir2 = opendir(dirname); + if (dir2 != NULL) { + for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { + char dirname2[PATH_SIZE]; + + if (dent2->d_name[0] == '.') + continue; + + if (!strcmp(dent2->d_name,"device")) + continue; + + strlcpy(dirname2, dirname, sizeof(dirname2)); + strlcat(dirname2, "/", sizeof(dirname2)); + strlcat(dirname2, dent2->d_name, sizeof(dirname2)); + if (attr_filtered(dirname2)) + continue; + device_list_insert(dirname2); + } + closedir(dir2); + } + } + closedir(dir); + } +} + +static void scan_class(void) +{ + char base[PATH_SIZE]; + DIR *dir; + struct dirent *dent; + + strlcpy(base, sysfs_path, sizeof(base)); + strlcat(base, "/class", sizeof(base)); + + dir = opendir(base); + if (dir != NULL) { + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char dirname[PATH_SIZE]; + DIR *dir2; + struct dirent *dent2; + + if (dent->d_name[0] == '.') + continue; + + if (subsystem_filtered(dent->d_name)) + continue; + + strlcpy(dirname, base, sizeof(dirname)); + strlcat(dirname, "/", sizeof(dirname)); + strlcat(dirname, dent->d_name, sizeof(dirname)); + dir2 = opendir(dirname); + if (dir2 != NULL) { + for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { + char dirname2[PATH_SIZE]; + + if (dent2->d_name[0] == '.') + continue; + + if (!strcmp(dent2->d_name, "device")) + continue; + + strlcpy(dirname2, dirname, sizeof(dirname2)); + strlcat(dirname2, "/", sizeof(dirname2)); + strlcat(dirname2, dent2->d_name, sizeof(dirname2)); + if (attr_filtered(dirname2)) + continue; + device_list_insert(dirname2); + } + closedir(dir2); + } + } + closedir(dir); + } +} + +static void scan_failed(void) +{ + char base[PATH_SIZE]; + DIR *dir; + struct dirent *dent; + + strlcpy(base, udev_root, sizeof(base)); + strlcat(base, "/" EVENT_FAILED_DIR, sizeof(base)); + + dir = opendir(base); + if (dir != NULL) { + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char device[PATH_SIZE]; + size_t start; + + if (dent->d_name[0] == '.') + continue; + + start = strlcpy(device, sysfs_path, sizeof(device)); + if(start >= sizeof(device)) + start = sizeof(device) - 1; + strlcat(device, dent->d_name, sizeof(device)); + path_decode(&device[start]); + device_list_insert(device); + } + closedir(dir); + } +} + +int udevtrigger(int argc, char *argv[], char *envp[]) +{ + int failed = 0; + const char *sockpath = NULL; + int option; + const char *action = "add"; + const char *env = NULL; + static const struct option options[] = { + { "verbose", 0, NULL, 'v' }, + { "dry-run", 0, NULL, 'n' }, + { "retry-failed", 0, NULL, 'F' }, + { "socket", 1, NULL, 'o' }, + { "help", 0, NULL, 'h' }, + { "action", 1, NULL, 'c' }, + { "subsystem-match", 1, NULL, 's' }, + { "subsystem-nomatch", 1, NULL, 'S' }, + { "attr-match", 1, NULL, 'a' }, + { "attr-nomatch", 1, NULL, 'A' }, + { "env", 1, NULL, 'e' }, + {} + }; + + logging_init("udevtrigger"); + udev_config_init(); + dbg("version %s\n", UDEV_VERSION); + sysfs_init(); + + while (1) { + option = getopt_long(argc, argv, "vnFo:hce::s:S:a:A:", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'v': + verbose = 1; + break; + case 'n': + dry_run = 1; + break; + case 'F': + failed = 1; + break; + case 'o': + sockpath = optarg; + break; + case 'c': + action = optarg; + break; + case 'e': + if (strchr(optarg, '=') != NULL) + env = optarg; + break; + case 's': + name_list_add(&filter_subsystem_match_list, optarg, 0); + break; + case 'S': + name_list_add(&filter_subsystem_nomatch_list, optarg, 0); + break; + case 'a': + name_list_add(&filter_attr_match_list, optarg, 0); + break; + case 'A': + name_list_add(&filter_attr_nomatch_list, optarg, 0); + break; + case 'h': + printf("Usage: udevadm trigger OPTIONS\n" + " --verbose print the list of devices while running\n" + " --dry-run do not actually trigger the events\n" + " --retry-failed trigger only the events which have been\n" + " marked as failed during a previous run\n" + " --socket= pass events to socket instead of triggering kernel events\n" + " --env== pass an additional key (works only with --socket=)\n" + " --subsystem-match= trigger devices from a matching subystem\n" + " --subsystem-nomatch= exclude devices from a matching subystem\n" + " --attr-match=]> trigger devices with a matching sysfs\n" + " attribute\n" + " --attr-nomatch=]> exclude devices with a matching sysfs\n" + " attribute\n" + " --help print this text\n" + "\n"); + goto exit; + default: + goto exit; + } + } + + if (sockpath != NULL) { + struct stat stats; + + sock = socket(AF_LOCAL, SOCK_DGRAM, 0); + memset(&saddr, 0x00, sizeof(struct sockaddr_un)); + saddr.sun_family = AF_LOCAL; + if (sockpath[0] == '@') { + /* abstract namespace socket requested */ + strlcpy(&saddr.sun_path[1], &sockpath[1], sizeof(saddr.sun_path)-1); + saddrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); + } else if (stat(sockpath, &stats) == 0 && S_ISSOCK(stats.st_mode)) { + /* existing socket file */ + strlcpy(saddr.sun_path, sockpath, sizeof(saddr.sun_path)); + saddrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path); + } else { + /* no socket file, assume abstract namespace socket */ + strlcpy(&saddr.sun_path[1], sockpath, sizeof(saddr.sun_path)-1); + saddrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); + } + } else if (env != NULL) { + fprintf(stderr, "error: --env= only valid with --socket= option\n"); + goto exit; + } + + if (failed) { + scan_failed(); + exec_list(action, env); + } else { + char base[PATH_SIZE]; + struct stat statbuf; + + /* if we have /sys/subsystem, forget all the old stuff */ + strlcpy(base, sysfs_path, sizeof(base)); + strlcat(base, "/subsystem", sizeof(base)); + if (stat(base, &statbuf) == 0) { + scan_subsystem("subsystem", SCAN_SUBSYSTEM); + exec_list(action, env); + scan_subsystem("subsystem", SCAN_DEVICES); + exec_list(action, env); + } else { + scan_subsystem("bus", SCAN_SUBSYSTEM); + exec_list(action, env); + scan_subsystem("bus", SCAN_DEVICES); + scan_class(); + + /* scan "block" if it isn't a "class" */ + strlcpy(base, sysfs_path, sizeof(base)); + strlcat(base, "/class/block", sizeof(base)); + if (stat(base, &statbuf) != 0) + scan_block(); + exec_list(action, env); + } + } + +exit: + name_list_cleanup(&filter_subsystem_match_list); + name_list_cleanup(&filter_subsystem_nomatch_list); + name_list_cleanup(&filter_attr_match_list); + name_list_cleanup(&filter_attr_nomatch_list); + + if (sock >= 0) + close(sock); + sysfs_cleanup(); + logging_close(); + return 0; +} diff --git a/udev_config.c b/udev_config.c deleted file mode 100644 index 55f0361dd2..0000000000 --- a/udev_config.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2003-2004 Greg Kroah-Hartman - * Copyright (C) 2004-2005 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -/* global variables */ -char udev_root[PATH_SIZE]; -char udev_config_filename[PATH_SIZE]; -char udev_rules_dir[PATH_SIZE]; -int udev_log_priority; -int udev_run; - -static int get_key(char **line, char **key, char **value) -{ - char *linepos; - char *temp; - - linepos = *line; - if (!linepos) - return -1; - - /* skip whitespace */ - while (isspace(linepos[0])) - linepos++; - - /* get the key */ - *key = linepos; - while (1) { - linepos++; - if (linepos[0] == '\0') - return -1; - if (isspace(linepos[0])) - break; - if (linepos[0] == '=') - break; - } - - /* terminate key */ - linepos[0] = '\0'; - linepos++; - - /* skip whitespace */ - while (isspace(linepos[0])) - linepos++; - - /* get the value*/ - if (linepos[0] == '"') - linepos++; - else - return -1; - *value = linepos; - - temp = strchr(linepos, '"'); - if (!temp) - return -1; - temp[0] = '\0'; - - return 0; -} - -static int parse_config_file(void) -{ - char line[LINE_SIZE]; - char *bufline; - char *linepos; - char *variable; - char *value; - char *buf; - size_t bufsize; - size_t cur; - size_t count; - int lineno; - int retval = 0; - - if (file_map(udev_config_filename, &buf, &bufsize) != 0) { - err("can't open '%s' as config file: %s\n", udev_config_filename, strerror(errno)); - return -ENODEV; - } - - /* loop through the whole file */ - lineno = 0; - cur = 0; - while (cur < bufsize) { - count = buf_get_line(buf, bufsize, cur); - bufline = &buf[cur]; - cur += count+1; - lineno++; - - /* eat the whitespace */ - while ((count > 0) && isspace(bufline[0])) { - bufline++; - count--; - } - if (count == 0) - continue; - - /* see if this is a comment */ - if (bufline[0] == COMMENT_CHARACTER) - continue; - - if (count >= sizeof(line)) { - err("line too long, conf line skipped %s, line %d\n", udev_config_filename, lineno); - continue; - } - - memcpy(line, bufline, count); - line[count] = '\0'; - - linepos = line; - retval = get_key(&linepos, &variable, &value); - if (retval != 0) { - err("error parsing %s, line %d:%d\n", udev_config_filename, lineno, (int)(linepos-line)); - continue; - } - - if (strcasecmp(variable, "udev_root") == 0) { - strlcpy(udev_root, value, sizeof(udev_root)); - remove_trailing_chars(udev_root, '/'); - continue; - } - - if (strcasecmp(variable, "udev_rules") == 0) { - strlcpy(udev_rules_dir, value, sizeof(udev_rules_dir)); - remove_trailing_chars(udev_rules_dir, '/'); - continue; - } - - if (strcasecmp(variable, "udev_log") == 0) { - udev_log_priority = log_priority(value); - continue; - } - } - - file_unmap(buf, bufsize); - return retval; -} - -void udev_config_init(void) -{ - const char *env; - - strcpy(udev_config_filename, UDEV_CONFIG_FILE); - strcpy(udev_root, UDEV_ROOT); - udev_rules_dir[0] = '\0'; - udev_log_priority = LOG_ERR; - udev_run = 1; - - /* disable RUN key execution */ - env = getenv("UDEV_RUN"); - if (env && !string_is_true(env)) - udev_run = 0; - - env = getenv("UDEV_CONFIG_FILE"); - if (env) { - strlcpy(udev_config_filename, env, sizeof(udev_config_filename)); - remove_trailing_chars(udev_config_filename, '/'); - } - - parse_config_file(); - - env = getenv("UDEV_ROOT"); - if (env) { - strlcpy(udev_root, env, sizeof(udev_root)); - remove_trailing_chars(udev_root, '/'); - } - - env = getenv("UDEV_LOG"); - if (env) - udev_log_priority = log_priority(env); - - dbg("UDEV_CONFIG_FILE='%s'\n", udev_config_filename); - dbg("udev_root='%s'\n", udev_root); - dbg("udev_rules_dir='%s'\n", udev_rules_dir); - dbg("udev_log=%d\n", udev_log_priority); -} diff --git a/udev_db.c b/udev_db.c deleted file mode 100644 index 3348c9a02c..0000000000 --- a/udev_db.c +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright (C) 2003 Greg Kroah-Hartman - * Copyright (C) 2004-2005 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udev_selinux.h" - - -static size_t devpath_to_db_path(const char *devpath, char *filename, size_t len) -{ - size_t start; - - /* translate to location of db file */ - strlcpy(filename, udev_root, len); - start = strlcat(filename, "/"DB_DIR"/", len); - strlcat(filename, devpath, len); - return path_encode(&filename[start], len - start); -} - -/* reverse mapping from the device file name to the devpath */ -static int name_index(const char *devpath, const char *name, int add) -{ - char device[PATH_SIZE]; - char filename[PATH_SIZE * 2]; - size_t start; - int fd; - - /* directory with device name */ - strlcpy(filename, udev_root, sizeof(filename)); - start = strlcat(filename, "/"DB_NAME_INDEX_DIR"/", sizeof(filename)); - strlcat(filename, name, sizeof(filename)); - path_encode(&filename[start], sizeof(filename) - start); - /* entry with the devpath */ - strlcpy(device, devpath, sizeof(device)); - path_encode(device, sizeof(device)); - strlcat(filename, "/", sizeof(filename)); - strlcat(filename, device, sizeof(filename)); - - if (add) { - info("creating index: '%s'\n", filename); - create_path(filename); - fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644); - if (fd > 0) - close(fd); - } else { - info("removing index: '%s'\n", filename); - unlink(filename); - delete_path(filename); - } - return 0; -} - -int udev_db_get_devices_by_name(const char *name, struct list_head *name_list) -{ - char dirname[PATH_MAX]; - size_t start; - DIR *dir; - int rc = 0; - - strlcpy(dirname, udev_root, sizeof(dirname)); - start = strlcat(dirname, "/"DB_NAME_INDEX_DIR"/", sizeof(dirname)); - strlcat(dirname, name, sizeof(dirname)); - path_encode(&dirname[start], sizeof(dirname) - start); - - dir = opendir(dirname); - if (dir == NULL) { - info("no index directory '%s': %s\n", dirname, strerror(errno)); - rc = -1; - goto out; - } - - info("found index directory '%s'\n", dirname); - while (1) { - struct dirent *ent; - char device[PATH_SIZE]; - - ent = readdir(dir); - if (ent == NULL || ent->d_name[0] == '\0') - break; - if (ent->d_name[0] == '.') - continue; - - strlcpy(device, ent->d_name, sizeof(device)); - path_decode(device); - name_list_add(name_list, device, 0); - rc++; - } - closedir(dir); -out: - return rc; -} - -int udev_db_rename(const char *devpath_old, const char *devpath) -{ - char filename[PATH_SIZE]; - char filename_old[PATH_SIZE]; - - devpath_to_db_path(devpath_old, filename_old, sizeof(filename_old)); - devpath_to_db_path(devpath, filename, sizeof(filename)); - return rename(filename_old, filename); -} - -int udev_db_add_device(struct udevice *udev) -{ - char filename[PATH_SIZE]; - - if (udev->test_run) - return 0; - - devpath_to_db_path(udev->dev->devpath, filename, sizeof(filename)); - create_path(filename); - unlink(filename); - - /* - * don't waste tmpfs memory pages, if we don't have any data to store - * create fake db-file; store the node-name in a symlink target - */ - if (list_empty(&udev->symlink_list) && list_empty(&udev->env_list) && - !udev->partitions && !udev->ignore_remove) { - int ret; - dbg("nothing interesting to store, create symlink\n"); - selinux_setfscreatecon(filename, NULL, S_IFLNK); - ret = symlink(udev->name, filename); - selinux_resetfscreatecon(); - if (ret != 0) { - err("unable to create db link '%s': %s\n", filename, strerror(errno)); - return -1; - } - } else { - FILE *f; - struct name_entry *name_loop; - - f = fopen(filename, "w"); - if (f == NULL) { - err("unable to create db file '%s': %s\n", filename, strerror(errno)); - return -1; - } - dbg("storing data for device '%s' in '%s'\n", udev->dev->devpath, filename); - - fprintf(f, "N:%s\n", udev->name); - list_for_each_entry(name_loop, &udev->symlink_list, node) { - fprintf(f, "S:%s\n", name_loop->name); - /* add symlink-name to index */ - name_index(udev->dev->devpath, name_loop->name, 1); - } - fprintf(f, "M:%u:%u\n", major(udev->devt), minor(udev->devt)); - if (udev->link_priority != 0) - fprintf(f, "L:%u\n", udev->link_priority); - if (udev->event_timeout >= 0) - fprintf(f, "T:%u\n", udev->event_timeout); - if (udev->partitions != 0) - fprintf(f, "A:%u\n", udev->partitions); - if (udev->ignore_remove) - fprintf(f, "R:%u\n", udev->ignore_remove); - list_for_each_entry(name_loop, &udev->env_list, node) - fprintf(f, "E:%s\n", name_loop->name); - fclose(f); - } - - /* add name to index */ - name_index(udev->dev->devpath, udev->name, 1); - - return 0; -} - -int udev_db_get_device(struct udevice *udev, const char *devpath) -{ - struct stat stats; - char filename[PATH_SIZE]; - char line[PATH_SIZE]; - unsigned int maj, min; - char *bufline; - char *buf; - size_t bufsize; - size_t cur; - size_t count; - - sysfs_device_set_values(udev->dev, devpath, NULL, NULL); - devpath_to_db_path(devpath, filename, sizeof(filename)); - - if (lstat(filename, &stats) != 0) { - info("no db file to read %s: %s\n", filename, strerror(errno)); - return -1; - } - if ((stats.st_mode & S_IFMT) == S_IFLNK) { - char target[NAME_SIZE]; - int target_len; - - info("found a symlink as db file\n"); - target_len = readlink(filename, target, sizeof(target)); - if (target_len > 0) - target[target_len] = '\0'; - else { - info("error reading db link %s: %s\n", filename, strerror(errno)); - return -1; - } - dbg("db link points to '%s'\n", target); - strlcpy(udev->name, target, sizeof(udev->name)); - return 0; - } - - if (file_map(filename, &buf, &bufsize) != 0) { - info("error reading db file %s: %s\n", filename, strerror(errno)); - return -1; - } - - cur = 0; - while (cur < bufsize) { - count = buf_get_line(buf, bufsize, cur); - bufline = &buf[cur]; - cur += count+1; - - if (count > sizeof(line)) - count = sizeof(line); - memcpy(line, &bufline[2], count-2); - line[count-2] = '\0'; - - switch(bufline[0]) { - case 'N': - strlcpy(udev->name, line, sizeof(udev->name)); - break; - case 'M': - sscanf(line, "%u:%u", &maj, &min); - udev->devt = makedev(maj, min); - break; - case 'S': - name_list_add(&udev->symlink_list, line, 0); - break; - case 'L': - udev->link_priority = atoi(line); - break; - case 'T': - udev->event_timeout = atoi(line); - break; - case 'A': - udev->partitions = atoi(line); - break; - case 'R': - udev->ignore_remove = atoi(line); - break; - case 'E': - name_list_add(&udev->env_list, line, 0); - break; - } - } - file_unmap(buf, bufsize); - - if (udev->name[0] == '\0') - return -1; - - return 0; -} - -int udev_db_delete_device(struct udevice *udev) -{ - char filename[PATH_SIZE]; - struct name_entry *name_loop; - - if (udev->test_run) - return 0; - - devpath_to_db_path(udev->dev->devpath, filename, sizeof(filename)); - unlink(filename); - - name_index(udev->dev->devpath, udev->name, 0); - list_for_each_entry(name_loop, &udev->symlink_list, node) - name_index(udev->dev->devpath, name_loop->name, 0); - - return 0; -} - -int udev_db_get_all_entries(struct list_head *name_list) -{ - char dbpath[PATH_MAX]; - DIR *dir; - - strlcpy(dbpath, udev_root, sizeof(dbpath)); - strlcat(dbpath, "/"DB_DIR, sizeof(dbpath)); - dir = opendir(dbpath); - if (dir == NULL) { - info("no udev_db available '%s': %s\n", dbpath, strerror(errno)); - return -1; - } - - while (1) { - struct dirent *ent; - char device[PATH_SIZE]; - - ent = readdir(dir); - if (ent == NULL || ent->d_name[0] == '\0') - break; - if (ent->d_name[0] == '.') - continue; - - strlcpy(device, ent->d_name, sizeof(device)); - path_decode(device); - name_list_add(name_list, device, 1); - dbg("added '%s'\n", device); - } - - closedir(dir); - return 0; -} diff --git a/udev_device.c b/udev_device.c deleted file mode 100644 index cf21191ca0..0000000000 --- a/udev_device.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright (C) 2004-2006 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udev_rules.h" - - -struct udevice *udev_device_init(struct udevice *udev) -{ - if (udev == NULL) - udev = malloc(sizeof(struct udevice)); - if (udev == NULL) - return NULL; - memset(udev, 0x00, sizeof(struct udevice)); - - INIT_LIST_HEAD(&udev->symlink_list); - INIT_LIST_HEAD(&udev->run_list); - INIT_LIST_HEAD(&udev->env_list); - - /* set sysfs device to local storage, can be overridden if needed */ - udev->dev = &udev->dev_local; - - /* default node permissions */ - udev->mode = 0660; - strcpy(udev->owner, "root"); - strcpy(udev->group, "root"); - - udev->event_timeout = -1; - - return udev; -} - -void udev_device_cleanup(struct udevice *udev) -{ - name_list_cleanup(&udev->symlink_list); - name_list_cleanup(&udev->run_list); - name_list_cleanup(&udev->env_list); - free(udev); -} - -dev_t udev_device_get_devt(struct udevice *udev) -{ - const char *attr; - unsigned int maj, min; - - /* read it from sysfs */ - attr = sysfs_attr_get_value(udev->dev->devpath, "dev"); - if (attr != NULL) { - if (sscanf(attr, "%u:%u", &maj, &min) == 2) - return makedev(maj, min); - } - return makedev(0, 0); -} - -static void kernel_log(struct ifreq ifr) -{ - int klog; - FILE *f; - - klog = open("/dev/kmsg", O_WRONLY); - if (klog < 0) - return; - - f = fdopen(klog, "w"); - if (f == NULL) { - close(klog); - return; - } - - fprintf(f, "<6>udev: renamed network interface %s to %s\n", - ifr.ifr_name, ifr.ifr_newname); - fclose(f); -} - -static int rename_netif(struct udevice *udev) -{ - int sk; - struct ifreq ifr; - int retval; - - info("changing net interface name from '%s' to '%s'\n", udev->dev->kernel, udev->name); - if (udev->test_run) - return 0; - - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) { - err("error opening socket: %s\n", strerror(errno)); - return -1; - } - - memset(&ifr, 0x00, sizeof(struct ifreq)); - strlcpy(ifr.ifr_name, udev->dev->kernel, IFNAMSIZ); - strlcpy(ifr.ifr_newname, udev->name, IFNAMSIZ); - retval = ioctl(sk, SIOCSIFNAME, &ifr); - if (retval == 0) - kernel_log(ifr); - else { - int loop; - - /* see if the destination interface name already exists */ - if (errno != EEXIST) { - err("error changing netif name %s to %s: %s\n", ifr.ifr_name, ifr.ifr_newname, strerror(errno)); - goto exit; - } - - /* free our own name, another process may wait for us */ - strlcpy(ifr.ifr_newname, udev->dev->kernel, IFNAMSIZ); - strlcat(ifr.ifr_newname, "_rename", IFNAMSIZ); - retval = ioctl(sk, SIOCSIFNAME, &ifr); - if (retval != 0) { - err("error changing netif name %s to %s: %s\n", ifr.ifr_name, ifr.ifr_newname, strerror(errno)); - goto exit; - } - - /* wait 30 seconds for our target to become available */ - strlcpy(ifr.ifr_name, ifr.ifr_newname, IFNAMSIZ); - strlcpy(ifr.ifr_newname, udev->name, IFNAMSIZ); - loop = 30 * 20; - while (loop--) { - retval = ioctl(sk, SIOCSIFNAME, &ifr); - if (retval == 0) { - kernel_log(ifr); - break; - } - - if (errno != EEXIST) { - err("error changing net interface name %s to %s: %s\n", - ifr.ifr_name, ifr.ifr_newname, strerror(errno)); - break; - } - dbg("wait for netif '%s' to become free, loop=%i\n", udev->name, (30 * 20) - loop); - usleep(1000 * 1000 / 20); - } - } - -exit: - close(sk); - return retval; -} - -int udev_device_event(struct udev_rules *rules, struct udevice *udev) -{ - int retval = 0; - - if (udev->devpath_old != NULL) - if (udev_db_rename(udev->devpath_old, udev->dev->devpath) == 0) - info("moved database from '%s' to '%s'\n", udev->devpath_old, udev->dev->devpath); - - /* add device node */ - if (major(udev->devt) != 0 && - (strcmp(udev->action, "add") == 0 || strcmp(udev->action, "change") == 0)) { - struct udevice *udev_old; - - dbg("device node add '%s'\n", udev->dev->devpath); - - udev_rules_get_name(rules, udev); - if (udev->ignore_device) { - info("device event will be ignored\n"); - goto exit; - } - if (udev->name[0] == '\0') { - info("device node creation supressed\n"); - goto exit; - } - - /* read current database entry; cleanup, if it is known device */ - udev_old = udev_device_init(NULL); - if (udev_old != NULL) { - udev_old->test_run = udev->test_run; - if (udev_db_get_device(udev_old, udev->dev->devpath) == 0) { - info("device '%s' already in database, cleanup\n", udev->dev->devpath); - udev_db_delete_device(udev_old); - } else { - udev_device_cleanup(udev_old); - udev_old = NULL; - } - } - - /* create node */ - retval = udev_node_add(udev); - if (retval != 0) - goto exit; - - /* store in database */ - udev_db_add_device(udev); - - /* create, replace, delete symlinks according to priority */ - udev_node_update_symlinks(udev, udev_old); - - if (udev_old != NULL) - udev_device_cleanup(udev_old); - goto exit; - } - - /* add netif */ - if (strcmp(udev->dev->subsystem, "net") == 0 && strcmp(udev->action, "add") == 0) { - dbg("netif add '%s'\n", udev->dev->devpath); - udev_rules_get_name(rules, udev); - if (udev->ignore_device) { - info("device event will be ignored\n"); - goto exit; - } - if (udev->name[0] == '\0') { - info("device renaming supressed\n"); - goto exit; - } - - /* look if we want to change the name of the netif */ - if (strcmp(udev->name, udev->dev->kernel) != 0) { - char devpath[PATH_MAX]; - char *pos; - - retval = rename_netif(udev); - if (retval != 0) - goto exit; - info("renamed netif to '%s'\n", udev->name); - - /* export old name */ - setenv("INTERFACE_OLD", udev->dev->kernel, 1); - - /* now change the devpath, because the kernel device name has changed */ - strlcpy(devpath, udev->dev->devpath, sizeof(devpath)); - pos = strrchr(devpath, '/'); - if (pos != NULL) { - pos[1] = '\0'; - strlcat(devpath, udev->name, sizeof(devpath)); - sysfs_device_set_values(udev->dev, devpath, NULL, NULL); - setenv("DEVPATH", udev->dev->devpath, 1); - setenv("INTERFACE", udev->name, 1); - info("changed devpath to '%s'\n", udev->dev->devpath); - } - } - goto exit; - } - - /* remove device node */ - if (major(udev->devt) != 0 && strcmp(udev->action, "remove") == 0) { - struct name_entry *name_loop; - - /* import database entry, and delete it */ - if (udev_db_get_device(udev, udev->dev->devpath) == 0) { - udev_db_delete_device(udev); - /* restore stored persistent data */ - list_for_each_entry(name_loop, &udev->env_list, node) - putenv(name_loop->name); - } else { - dbg("'%s' not found in database, using kernel name '%s'\n", - udev->dev->devpath, udev->dev->kernel); - strlcpy(udev->name, udev->dev->kernel, sizeof(udev->name)); - } - - udev_rules_get_run(rules, udev); - if (udev->ignore_device) { - info("device event will be ignored\n"); - goto exit; - } - - if (udev->ignore_remove) { - info("ignore_remove for '%s'\n", udev->name); - goto exit; - } - /* remove the node */ - retval = udev_node_remove(udev); - - /* delete or restore symlinks according to priority */ - udev_node_update_symlinks(udev, NULL); - goto exit; - } - - /* default devices */ - udev_rules_get_run(rules, udev); - if (udev->ignore_device) - info("device event will be ignored\n"); - -exit: - return retval; -} diff --git a/udev_node.c b/udev_node.c deleted file mode 100644 index 78b6747043..0000000000 --- a/udev_node.c +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright (C) 2003 Greg Kroah-Hartman - * Copyright (C) 2004-2006 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udev_rules.h" -#include "udev_selinux.h" - -#define TMP_FILE_EXT ".udev-tmp" - -int udev_node_mknod(struct udevice *udev, const char *file, dev_t devt, mode_t mode, uid_t uid, gid_t gid) -{ - char file_tmp[PATH_SIZE + sizeof(TMP_FILE_EXT)]; - struct stat stats; - int preserve = 0; - int err = 0; - - if (major(devt) != 0 && strcmp(udev->dev->subsystem, "block") == 0) - mode |= S_IFBLK; - else - mode |= S_IFCHR; - - if (lstat(file, &stats) == 0) { - if (((stats.st_mode & S_IFMT) == (mode & S_IFMT)) && (stats.st_rdev == devt)) { - info("preserve file '%s', because it has correct dev_t\n", file); - preserve = 1; - selinux_setfilecon(file, udev->dev->kernel, mode); - } else { - info("atomically replace existing file '%s'\n", file); - strlcpy(file_tmp, file, sizeof(file_tmp)); - strlcat(file_tmp, TMP_FILE_EXT, sizeof(file_tmp)); - unlink(file_tmp); - selinux_setfscreatecon(file_tmp, udev->dev->kernel, mode); - err = mknod(file_tmp, mode, devt); - selinux_resetfscreatecon(); - if (err != 0) { - err("mknod(%s, %#o, %u, %u) failed: %s\n", - file_tmp, mode, major(devt), minor(devt), strerror(errno)); - goto exit; - } - err = rename(file_tmp, file); - if (err != 0) { - err("rename(%s, %s) failed: %s\n", - file_tmp, file, strerror(errno)); - unlink(file_tmp); - } - } - } else { - info("mknod(%s, %#o, (%u,%u))\n", file, mode, major(devt), minor(devt)); - selinux_setfscreatecon(file, udev->dev->kernel, mode); - err = mknod(file, mode, devt); - selinux_resetfscreatecon(); - if (err != 0) { - err("mknod(%s, %#o, (%u,%u) failed: %s\n", - file, mode, major(devt), minor(devt), strerror(errno)); - goto exit; - } - } - - if (!preserve || stats.st_mode != mode) { - info("chmod(%s, %#o)\n", file, mode); - err = chmod(file, mode); - if (err != 0) { - err("chmod(%s, %#o) failed: %s\n", file, mode, strerror(errno)); - goto exit; - } - } - - if (!preserve || stats.st_uid != uid || stats.st_gid != gid) { - info("chown(%s, %u, %u)\n", file, uid, gid); - err = chown(file, uid, gid); - if (err != 0) { - err("chown(%s, %u, %u) failed: %s\n", file, uid, gid, strerror(errno)); - goto exit; - } - } -exit: - return err; -} - -static int node_symlink(const char *node, const char *slink) -{ - struct stat stats; - char target[PATH_SIZE] = ""; - char slink_tmp[PATH_SIZE + sizeof(TMP_FILE_EXT)]; - int i = 0; - int tail = 0; - int len; - int retval = 0; - - /* use relative link */ - while (node[i] && (node[i] == slink[i])) { - if (node[i] == '/') - tail = i+1; - i++; - } - while (slink[i] != '\0') { - if (slink[i] == '/') - strlcat(target, "../", sizeof(target)); - i++; - } - strlcat(target, &node[tail], sizeof(target)); - - /* preserve link with correct target, do not replace node of other device */ - if (lstat(slink, &stats) == 0) { - if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { - struct stat stats2; - - info("found existing node instead of symlink '%s'\n", slink); - if (lstat(node, &stats2) == 0) { - if ((stats.st_mode & S_IFMT) == (stats2.st_mode & S_IFMT) && - stats.st_rdev == stats2.st_rdev) { - info("replace device node '%s' with symlink to our node '%s'\n", slink, node); - } else { - err("device node '%s' already exists, link to '%s' will not overwrite it\n", slink, node); - goto exit; - } - } - } else if (S_ISLNK(stats.st_mode)) { - char buf[PATH_SIZE]; - - info("found existing symlink '%s'\n", slink); - len = readlink(slink, buf, sizeof(buf)); - if (len > 0) { - buf[len] = '\0'; - if (strcmp(target, buf) == 0) { - info("preserve already existing symlink '%s' to '%s'\n", slink, target); - selinux_setfilecon(slink, NULL, S_IFLNK); - goto exit; - } - } - } - } else { - info("creating symlink '%s' to '%s'\n", slink, target); - selinux_setfscreatecon(slink, NULL, S_IFLNK); - retval = symlink(target, slink); - selinux_resetfscreatecon(); - if (retval == 0) - goto exit; - } - - info("atomically replace '%s'\n", slink); - strlcpy(slink_tmp, slink, sizeof(slink_tmp)); - strlcat(slink_tmp, TMP_FILE_EXT, sizeof(slink_tmp)); - unlink(slink_tmp); - selinux_setfscreatecon(slink, NULL, S_IFLNK); - retval = symlink(target, slink_tmp); - selinux_resetfscreatecon(); - if (retval != 0) { - err("symlink(%s, %s) failed: %s\n", target, slink_tmp, strerror(errno)); - goto exit; - } - retval = rename(slink_tmp, slink); - if (retval != 0) { - err("rename(%s, %s) failed: %s\n", slink_tmp, slink, strerror(errno)); - unlink(slink_tmp); - goto exit; - } -exit: - return retval; -} - -static int update_link(struct udevice *udev, const char *name) -{ - LIST_HEAD(name_list); - char slink[PATH_SIZE]; - char node[PATH_SIZE]; - struct udevice *udev_db; - struct name_entry *device; - char target[PATH_MAX] = ""; - int count; - int priority = 0; - int rc = 0; - - strlcpy(slink, udev_root, sizeof(slink)); - strlcat(slink, "/", sizeof(slink)); - strlcat(slink, name, sizeof(slink)); - - count = udev_db_get_devices_by_name(name, &name_list); - info("found %i devices with name '%s'\n", count, name); - - /* if we don't have a reference, delete it */ - if (count <= 0) { - info("no reference left, remove '%s'\n", name); - if (!udev->test_run) { - unlink(slink); - delete_path(slink); - } - goto out; - } - - /* find the device with the highest priority */ - list_for_each_entry(device, &name_list, node) { - info("found '%s' for '%s'\n", device->name, name); - - /* did we find ourself? we win, if we have the same priority */ - if (strcmp(udev->dev->devpath, device->name) == 0) { - info("compare (our own) priority of '%s' %i >= %i\n", - udev->dev->devpath, udev->link_priority, priority); - if (strcmp(udev->name, name) == 0) { - info("'%s' is our device node, database inconsistent, skip link update\n", udev->name); - } else if (target[0] == '\0' || udev->link_priority >= priority) { - priority = udev->link_priority; - strlcpy(target, udev->name, sizeof(target)); - } - continue; - } - - /* another device, read priority from database */ - udev_db = udev_device_init(NULL); - if (udev_db == NULL) - continue; - if (udev_db_get_device(udev_db, device->name) == 0) { - if (strcmp(udev_db->name, name) == 0) { - info("'%s' is a device node of '%s', skip link update\n", udev_db->name, device->name); - } else { - info("compare priority of '%s' %i > %i\n", - udev_db->dev->devpath, udev_db->link_priority, priority); - if (target[0] == '\0' || udev_db->link_priority > priority) { - priority = udev_db->link_priority; - strlcpy(target, udev_db->name, sizeof(target)); - } - } - } - udev_device_cleanup(udev_db); - } - name_list_cleanup(&name_list); - - if (target[0] == '\0') { - info("no current target for '%s' found\n", name); - rc = 1; - goto out; - } - - /* create symlink to the target with the highest priority */ - strlcpy(node, udev_root, sizeof(node)); - strlcat(node, "/", sizeof(node)); - strlcat(node, target, sizeof(node)); - info("'%s' with target '%s' has the highest priority %i, create it\n", name, target, priority); - if (!udev->test_run) { - create_path(slink); - node_symlink(node, slink); - } -out: - return rc; -} - -void udev_node_update_symlinks(struct udevice *udev, struct udevice *udev_old) -{ - struct name_entry *name_loop; - char symlinks[PATH_SIZE] = ""; - - list_for_each_entry(name_loop, &udev->symlink_list, node) { - info("update symlink '%s' of '%s'\n", name_loop->name, udev->dev->devpath); - update_link(udev, name_loop->name); - strlcat(symlinks, udev_root, sizeof(symlinks)); - strlcat(symlinks, "/", sizeof(symlinks)); - strlcat(symlinks, name_loop->name, sizeof(symlinks)); - strlcat(symlinks, " ", sizeof(symlinks)); - } - - /* export symlinks to environment */ - remove_trailing_chars(symlinks, ' '); - if (symlinks[0] != '\0') - setenv("DEVLINKS", symlinks, 1); - - /* update possible left-over symlinks (device metadata changed) */ - if (udev_old != NULL) { - struct name_entry *link_loop; - struct name_entry *link_old_loop; - int found; - - /* remove current symlinks from old list */ - list_for_each_entry(link_old_loop, &udev_old->symlink_list, node) { - found = 0; - list_for_each_entry(link_loop, &udev->symlink_list, node) { - if (strcmp(link_old_loop->name, link_loop->name) == 0) { - found = 1; - break; - } - } - if (!found) { - /* link does no longer belong to this device */ - info("update old symlink '%s' no longer belonging to '%s'\n", - link_old_loop->name, udev->dev->devpath); - update_link(udev, link_old_loop->name); - } - } - - /* - * if the node name has changed, delete the node, - * or possibly restore a symlink of another device - */ - if (strcmp(udev->name, udev_old->name) != 0) - update_link(udev, udev_old->name); - } -} - -int udev_node_add(struct udevice *udev) -{ - char filename[PATH_SIZE]; - uid_t uid; - gid_t gid; - int i; - int retval = 0; - - strlcpy(filename, udev_root, sizeof(filename)); - strlcat(filename, "/", sizeof(filename)); - strlcat(filename, udev->name, sizeof(filename)); - create_path(filename); - - if (strcmp(udev->owner, "root") == 0) - uid = 0; - else { - char *endptr; - unsigned long id; - - id = strtoul(udev->owner, &endptr, 10); - if (endptr[0] == '\0') - uid = (uid_t) id; - else - uid = lookup_user(udev->owner); - } - - if (strcmp(udev->group, "root") == 0) - gid = 0; - else { - char *endptr; - unsigned long id; - - id = strtoul(udev->group, &endptr, 10); - if (endptr[0] == '\0') - gid = (gid_t) id; - else - gid = lookup_group(udev->group); - } - - info("creating device node '%s', major=%d, minor=%d, mode=%#o, uid=%d, gid=%d\n", - filename, major(udev->devt), minor(udev->devt), udev->mode, uid, gid); - - if (!udev->test_run) - if (udev_node_mknod(udev, filename, udev->devt, udev->mode, uid, gid) != 0) { - retval = -1; - goto exit; - } - - setenv("DEVNAME", filename, 1); - - /* create all_partitions if requested */ - if (udev->partitions) { - char partitionname[PATH_SIZE]; - char *attr; - int range; - - /* take the maximum registered minor range */ - attr = sysfs_attr_get_value(udev->dev->devpath, "range"); - if (attr != NULL) { - range = atoi(attr); - if (range > 1) - udev->partitions = range-1; - } - info("creating device partition nodes '%s[1-%i]'\n", filename, udev->partitions); - if (!udev->test_run) { - for (i = 1; i <= udev->partitions; i++) { - dev_t part_devt; - - snprintf(partitionname, sizeof(partitionname), "%s%d", filename, i); - partitionname[sizeof(partitionname)-1] = '\0'; - part_devt = makedev(major(udev->devt), minor(udev->devt) + i); - udev_node_mknod(udev, partitionname, part_devt, udev->mode, uid, gid); - } - } - } -exit: - return retval; -} - -int udev_node_remove(struct udevice *udev) -{ - char filename[PATH_SIZE]; - char partitionname[PATH_SIZE]; - struct stat stats; - int retval = 0; - int num; - - strlcpy(filename, udev_root, sizeof(filename)); - strlcat(filename, "/", sizeof(filename)); - strlcat(filename, udev->name, sizeof(filename)); - if (stat(filename, &stats) != 0) { - info("device node '%s' not found\n", filename); - return 0; - } - if (udev->devt && stats.st_rdev != udev->devt) { - info("device node '%s' points to a different device, skip removal\n", filename); - return -1; - } - - info("removing device node '%s'\n", filename); - if (!udev->test_run) - retval = unlink_secure(filename); - if (retval) - return retval; - - setenv("DEVNAME", filename, 1); - num = udev->partitions; - if (num > 0) { - int i; - - info("removing all_partitions '%s[1-%i]'\n", filename, num); - if (num > 255) - return -1; - for (i = 1; i <= num; i++) { - snprintf(partitionname, sizeof(partitionname), "%s%d", filename, i); - partitionname[sizeof(partitionname)-1] = '\0'; - if (!udev->test_run) - unlink_secure(partitionname); - } - } - delete_path(filename); - return retval; -} diff --git a/udev_rules.c b/udev_rules.c deleted file mode 100644 index ea850a84c3..0000000000 --- a/udev_rules.c +++ /dev/null @@ -1,1618 +0,0 @@ -/* - * Copyright (C) 2003 Greg Kroah-Hartman - * Copyright (C) 2003-2006 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udev_rules.h" -#include "udev_selinux.h" - -extern char **environ; - -/* extract possible {attr} and move str behind it */ -static char *get_format_attribute(char **str) -{ - char *pos; - char *attr = NULL; - - if (*str[0] == '{') { - pos = strchr(*str, '}'); - if (pos == NULL) { - err("missing closing brace for format\n"); - return NULL; - } - pos[0] = '\0'; - attr = *str+1; - *str = pos+1; - dbg("attribute='%s', str='%s'\n", attr, *str); - } - return attr; -} - -/* extract possible format length and move str behind it*/ -static int get_format_len(char **str) -{ - int num; - char *tail; - - if (isdigit(*str[0])) { - num = (int) strtoul(*str, &tail, 10); - if (num > 0) { - *str = tail; - dbg("format length=%i\n", num); - return num; - } else { - err("format parsing error '%s'\n", *str); - } - } - return -1; -} - -static int get_key(char **line, char **key, char **value) -{ - char *linepos; - char *temp; - - linepos = *line; - if (linepos == NULL) - return -1; - - /* skip whitespace */ - while (isspace(linepos[0])) - linepos++; - - /* get the key */ - temp = strchr(linepos, '='); - if (temp == NULL || temp == linepos) - return -1; - temp[0] = '\0'; - *key = linepos; - linepos = &temp[1]; - - /* get a quoted value */ - if (linepos[0] == '"' || linepos[0] == '\'') { - temp = strchr(&linepos[1], linepos[0]); - if (temp != NULL) { - temp[0] = '\0'; - *value = &linepos[1]; - goto out; - } - } - - /* get the value*/ - temp = strchr(linepos, '\n'); - if (temp != NULL) - temp[0] = '\0'; - *value = linepos; -out: - return 0; -} - -static int run_program(const char *command, const char *subsystem, - char *result, size_t ressize, size_t *reslen) -{ - int status; - int outpipe[2] = {-1, -1}; - int errpipe[2] = {-1, -1}; - pid_t pid; - char arg[PATH_SIZE]; - char program[PATH_SIZE]; - char *argv[(sizeof(arg) / 2) + 1]; - int devnull; - int i; - int retval = 0; - - /* build argv from comand */ - strlcpy(arg, command, sizeof(arg)); - i = 0; - if (strchr(arg, ' ') != NULL) { - char *pos = arg; - - while (pos != NULL) { - if (pos[0] == '\'') { - /* don't separate if in apostrophes */ - pos++; - argv[i] = strsep(&pos, "\'"); - while (pos != NULL && pos[0] == ' ') - pos++; - } else { - argv[i] = strsep(&pos, " "); - } - dbg("arg[%i] '%s'\n", i, argv[i]); - i++; - } - argv[i] = NULL; - } else { - argv[0] = arg; - argv[1] = NULL; - } - info("'%s'\n", command); - - /* prepare pipes from child to parent */ - if (result != NULL || udev_log_priority >= LOG_INFO) { - if (pipe(outpipe) != 0) { - err("pipe failed: %s\n", strerror(errno)); - return -1; - } - } - if (udev_log_priority >= LOG_INFO) { - if (pipe(errpipe) != 0) { - err("pipe failed: %s\n", strerror(errno)); - return -1; - } - } - - /* allow programs in /lib/udev called without the path */ - if (strchr(argv[0], '/') == NULL) { - strlcpy(program, "/lib/udev/", sizeof(program)); - strlcat(program, argv[0], sizeof(program)); - 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("open /dev/null failed: %s\n", strerror(errno)); - 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]); - } - execv(argv[0], argv); - if (errno == ENOENT || errno == ENOTDIR) { - /* may be on a filesytem which is not mounted right now */ - info("program '%s' not found\n", argv[0]); - } else { - /* other problems */ - err("exec of program '%s' failed\n", argv[0]); - } - _exit(1); - case -1: - err("fork of '%s' failed: %s\n", argv[0], strerror(errno)); - 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(UDEV_MAX(outpipe[READ_END], errpipe[READ_END])+1, &readfds, NULL, NULL, NULL); - if (fdcount < 0) { - if (errno == EINTR) - continue; - retval = -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("stdin read failed: %s\n", strerror(errno)); - retval = -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("ressize %ld too short\n", (long)ressize); - retval = -1; - } - } - pos = inbuf; - while ((line = strsep(&pos, "\n"))) - if (pos || line[0] != '\0') - info("'%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("stderr read failed: %s\n", strerror(errno)); - continue; - } - errbuf[count] = '\0'; - pos = errbuf; - while ((line = strsep(&pos, "\n"))) - if (pos || line[0] != '\0') - info("'%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 childs stdout string */ - if (result) { - result[respos] = '\0'; - dbg("result='%s'\n", result); - if (reslen) - *reslen = respos; - } - } - waitpid(pid, &status, 0); - if (WIFEXITED(status)) { - info("'%s' returned with status %i\n", argv[0], WEXITSTATUS(status)); - if (WEXITSTATUS(status) != 0) - retval = -1; - } else { - err("'%s' abnormal exit\n", argv[0]); - retval = -1; - } - } - - return retval; -} - -static int import_keys_into_env(struct udevice *udev, const char *buf, size_t bufsize) -{ - char line[LINE_SIZE]; - const char *bufline; - char *linepos; - char *variable; - char *value; - size_t cur; - size_t count; - int lineno; - - /* loop through the whole buffer */ - lineno = 0; - cur = 0; - while (cur < bufsize) { - count = buf_get_line(buf, bufsize, cur); - bufline = &buf[cur]; - cur += count+1; - lineno++; - - /* eat the whitespace */ - while ((count > 0) && isspace(bufline[0])) { - bufline++; - count--; - } - if (count == 0) - continue; - - /* see if this is a comment */ - if (bufline[0] == COMMENT_CHARACTER) - continue; - - if (count >= sizeof(line)) { - err("line too long, conf line skipped %s, line %d\n", udev_config_filename, lineno); - continue; - } - - memcpy(line, bufline, count); - line[count] = '\0'; - - linepos = line; - if (get_key(&linepos, &variable, &value) == 0) { - dbg("import '%s=%s'\n", variable, value); - - /* handle device, renamed by external tool, returning new path */ - if (strcmp(variable, "DEVPATH") == 0) { - info("updating devpath from '%s' to '%s'\n", udev->dev->devpath, value); - sysfs_device_set_values(udev->dev, value, NULL, NULL); - } else - name_list_key_add(&udev->env_list, variable, value); - setenv(variable, value, 1); - } - } - - return 0; -} - -static int import_file_into_env(struct udevice *udev, const char *filename) -{ - char *buf; - size_t bufsize; - - if (file_map(filename, &buf, &bufsize) != 0) { - err("can't open '%s': %s\n", filename, strerror(errno)); - return -1; - } - import_keys_into_env(udev, buf, bufsize); - file_unmap(buf, bufsize); - - return 0; -} - -static int import_program_into_env(struct udevice *udev, const char *program) -{ - char result[2048]; - size_t reslen; - - if (run_program(program, udev->dev->subsystem, result, sizeof(result), &reslen) != 0) - return -1; - return import_keys_into_env(udev, result, reslen); -} - -static int import_parent_into_env(struct udevice *udev, const char *filter) -{ - struct sysfs_device *dev_parent; - int rc = -1; - - dev_parent = sysfs_device_get_parent(udev->dev); - if (dev_parent != NULL) { - struct udevice *udev_parent; - struct name_entry *name_loop; - - dbg("found parent '%s', get the node name\n", dev_parent->devpath); - udev_parent = udev_device_init(NULL); - if (udev_parent == NULL) - return -1; - /* import the udev_db of the parent */ - if (udev_db_get_device(udev_parent, dev_parent->devpath) == 0) { - dbg("import stored parent env '%s'\n", udev_parent->name); - list_for_each_entry(name_loop, &udev_parent->env_list, node) { - char name[NAME_SIZE]; - char *pos; - - strlcpy(name, name_loop->name, sizeof(name)); - pos = strchr(name, '='); - if (pos) { - pos[0] = '\0'; - pos++; - if (fnmatch(filter, name, 0) == 0) { - dbg("import key '%s'\n", name_loop->name); - name_list_add(&udev->env_list, name_loop->name, 0); - setenv(name, pos, 1); - } else - dbg("skip key '%s'\n", name_loop->name); - } - } - rc = 0; - } else - dbg("parent not found in database\n"); - udev_device_cleanup(udev_parent); - } - - return rc; -} - -static int pass_env_to_socket(const char *sockpath, const char *devpath, const char *action) -{ - int sock; - struct sockaddr_un saddr; - socklen_t saddrlen; - struct stat stats; - char buf[2048]; - size_t bufpos = 0; - int i; - ssize_t count; - int retval = 0; - - dbg("pass environment to socket '%s'\n", sockpath); - sock = socket(AF_LOCAL, SOCK_DGRAM, 0); - memset(&saddr, 0x00, sizeof(struct sockaddr_un)); - saddr.sun_family = AF_LOCAL; - if (sockpath[0] == '@') { - /* abstract namespace socket requested */ - strlcpy(&saddr.sun_path[1], &sockpath[1], sizeof(saddr.sun_path)-1); - saddrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); - } else if (stat(sockpath, &stats) == 0 && S_ISSOCK(stats.st_mode)) { - /* existing socket file */ - strlcpy(saddr.sun_path, sockpath, sizeof(saddr.sun_path)); - saddrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path); - } else { - /* no socket file, assume abstract namespace socket */ - strlcpy(&saddr.sun_path[1], sockpath, sizeof(saddr.sun_path)-1); - saddrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); - } - - bufpos = snprintf(buf, sizeof(buf)-1, "%s@%s", action, devpath); - bufpos++; - for (i = 0; environ[i] != NULL && bufpos < (sizeof(buf)-1); i++) { - bufpos += strlcpy(&buf[bufpos], environ[i], sizeof(buf) - bufpos-1); - bufpos++; - } - if (bufpos > sizeof(buf)) - bufpos = sizeof(buf); - - count = sendto(sock, &buf, bufpos, 0, (struct sockaddr *)&saddr, saddrlen); - if (count < 0) - retval = -1; - info("passed %zi bytes to socket '%s', \n", count, sockpath); - - close(sock); - return retval; -} - -int udev_rules_run(struct udevice *udev) -{ - struct name_entry *name_loop; - int retval = 0; - - dbg("executing run list\n"); - list_for_each_entry(name_loop, &udev->run_list, node) { - if (strncmp(name_loop->name, "socket:", strlen("socket:")) == 0) { - pass_env_to_socket(&name_loop->name[strlen("socket:")], udev->dev->devpath, udev->action); - } else { - char program[PATH_SIZE]; - - strlcpy(program, name_loop->name, sizeof(program)); - udev_rules_apply_format(udev, program, sizeof(program)); - if (run_program(program, udev->dev->subsystem, NULL, 0, NULL) != 0) - if (!name_loop->ignore_error) - retval = -1; - } - } - - return retval; -} - -#define WAIT_LOOP_PER_SECOND 50 -static int wait_for_file(struct udevice *udev, const char *file, int timeout) -{ - char filepath[PATH_SIZE]; - char devicepath[PATH_SIZE] = ""; - struct stat stats; - int loop = timeout * WAIT_LOOP_PER_SECOND; - - /* a relative path is a device attribute */ - if (file[0] != '/') { - strlcpy(devicepath, sysfs_path, sizeof(devicepath)); - strlcat(devicepath, udev->dev->devpath, sizeof(devicepath)); - - strlcpy(filepath, devicepath, sizeof(filepath)); - strlcat(filepath, "/", sizeof(filepath)); - strlcat(filepath, file, sizeof(filepath)); - file = filepath; - } - - dbg("will wait %i sec for '%s'\n", timeout, file); - while (--loop) { - /* lookup file */ - if (stat(file, &stats) == 0) { - info("file '%s' appeared after %i loops\n", file, (timeout * WAIT_LOOP_PER_SECOND) - loop-1); - return 0; - } - /* make sure, the device did not disappear in the meantime */ - if (devicepath[0] != '\0' && stat(devicepath, &stats) != 0) { - info("device disappeared while waiting for '%s'\n", file); - return -2; - } - info("wait for '%s' for %i mseconds\n", file, 1000 / WAIT_LOOP_PER_SECOND); - usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND); - } - info("waiting for '%s' failed\n", file); - return -1; -} - -/* handle "[$SUBSYSTEM/$KERNEL]" lookup */ -static int attr_get_by_subsys_id(const char *attrstr, char *devpath, size_t len, char **attr) -{ - char subsys[NAME_SIZE]; - char *attrib; - char *id; - int found = 0; - - if (attrstr[0] != '[') - goto out; - - strlcpy(subsys, &attrstr[1], sizeof(subsys)); - - attrib = strchr(subsys, ']'); - if (attrib == NULL) - goto out; - attrib[0] = '\0'; - attrib = &attrib[1]; - - id = strchr(subsys, '/'); - if (id == NULL) - goto out; - id[0] = '\0'; - id = &id[1]; - - if (sysfs_lookup_devpath_by_subsys_id(devpath, len, subsys, id)) { - if (attr != NULL) { - if (attrib[0] != '\0') - *attr = attrib; - else - *attr = NULL; - } - found = 1; - } -out: - return found; -} - -static int attr_subst_subdir(char *attr, size_t len) -{ - char *pos; - int found = 0; - - pos = strstr(attr, "/*/"); - if (pos != NULL) { - char str[PATH_SIZE]; - DIR *dir; - - pos[1] = '\0'; - strlcpy(str, &pos[2], sizeof(str)); - dir = opendir(attr); - if (dir != NULL) { - struct dirent *dent; - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - struct stat stats; - - if (dent->d_name[0] == '.') - continue; - strlcat(attr, dent->d_name, len); - strlcat(attr, str, len); - if (stat(attr, &stats) == 0) { - found = 1; - break; - } - pos[1] = '\0'; - } - closedir(dir); - } - if (!found) - strlcat(attr, str, len); - } - - return found; -} - -void udev_rules_apply_format(struct udevice *udev, char *string, size_t maxsize) -{ - char temp[PATH_SIZE]; - char temp2[PATH_SIZE]; - char *head, *tail, *pos, *cpos, *attr, *rest; - int len; - int i; - int count; - enum subst_type { - SUBST_UNKNOWN, - SUBST_DEVPATH, - SUBST_KERNEL, - SUBST_KERNEL_NUMBER, - SUBST_ID, - SUBST_DRIVER, - SUBST_MAJOR, - SUBST_MINOR, - SUBST_RESULT, - SUBST_ATTR, - SUBST_PARENT, - SUBST_TEMP_NODE, - SUBST_NAME, - SUBST_LINKS, - SUBST_ROOT, - SUBST_SYS, - SUBST_ENV, - }; - static const struct subst_map { - char *name; - char fmt; - enum subst_type type; - } map[] = { - { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH }, - { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER }, - { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL }, - { .name = "id", .fmt = 'b', .type = SUBST_ID }, - { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER }, - { .name = "major", .fmt = 'M', .type = SUBST_MAJOR }, - { .name = "minor", .fmt = 'm', .type = SUBST_MINOR }, - { .name = "result", .fmt = 'c', .type = SUBST_RESULT }, - { .name = "attr", .fmt = 's', .type = SUBST_ATTR }, - { .name = "sysfs", .fmt = 's', .type = SUBST_ATTR }, - { .name = "parent", .fmt = 'P', .type = SUBST_PARENT }, - { .name = "tempnode", .fmt = 'N', .type = SUBST_TEMP_NODE }, - { .name = "name", .fmt = 'D', .type = SUBST_NAME }, - { .name = "links", .fmt = 'L', .type = SUBST_LINKS }, - { .name = "root", .fmt = 'r', .type = SUBST_ROOT }, - { .name = "sys", .fmt = 'S', .type = SUBST_SYS }, - { .name = "env", .fmt = 'E', .type = SUBST_ENV }, - { NULL, '\0', 0 } - }; - enum subst_type type; - const struct subst_map *subst; - - head = string; - while (1) { - len = -1; - while (head[0] != '\0') { - if (head[0] == '$') { - /* substitute named variable */ - if (head[1] == '\0') - break; - if (head[1] == '$') { - strlcpy(temp, head+2, sizeof(temp)); - strlcpy(head+1, temp, maxsize); - head++; - continue; - } - head[0] = '\0'; - for (subst = map; subst->name; subst++) { - if (strncasecmp(&head[1], subst->name, strlen(subst->name)) == 0) { - type = subst->type; - tail = head + strlen(subst->name)+1; - dbg("will substitute format name '%s'\n", subst->name); - goto found; - } - } - head[0] = '$'; - err("unknown format variable '%s'\n", head); - } else if (head[0] == '%') { - /* substitute format char */ - if (head[1] == '\0') - break; - if (head[1] == '%') { - strlcpy(temp, head+2, sizeof(temp)); - strlcpy(head+1, temp, maxsize); - head++; - continue; - } - head[0] = '\0'; - tail = head+1; - len = get_format_len(&tail); - for (subst = map; subst->name; subst++) { - if (tail[0] == subst->fmt) { - type = subst->type; - tail++; - dbg("will substitute format char '%c'\n", subst->fmt); - goto found; - } - } - head[0] = '%'; - err("unknown format char '%c'\n", tail[0]); - } - head++; - } - break; -found: - attr = get_format_attribute(&tail); - strlcpy(temp, tail, sizeof(temp)); - dbg("format=%i, string='%s', tail='%s'\n", type ,string, tail); - - switch (type) { - case SUBST_DEVPATH: - strlcat(string, udev->dev->devpath, maxsize); - dbg("substitute devpath '%s'\n", udev->dev->devpath); - break; - case SUBST_KERNEL: - strlcat(string, udev->dev->kernel, maxsize); - dbg("substitute kernel name '%s'\n", udev->dev->kernel); - break; - case SUBST_KERNEL_NUMBER: - strlcat(string, udev->dev->kernel_number, maxsize); - dbg("substitute kernel number '%s'\n", udev->dev->kernel_number); - break; - case SUBST_ID: - if (udev->dev_parent != NULL) { - strlcat(string, udev->dev_parent->kernel, maxsize); - dbg("substitute id '%s'\n", udev->dev_parent->kernel); - } - break; - case SUBST_DRIVER: - if (udev->dev_parent != NULL) { - strlcat(string, udev->dev_parent->driver, maxsize); - dbg("substitute driver '%s'\n", udev->dev_parent->driver); - } - break; - case SUBST_MAJOR: - sprintf(temp2, "%d", major(udev->devt)); - strlcat(string, temp2, maxsize); - dbg("substitute major number '%s'\n", temp2); - break; - case SUBST_MINOR: - sprintf(temp2, "%d", minor(udev->devt)); - strlcat(string, temp2, maxsize); - dbg("substitute minor number '%s'\n", temp2); - break; - case SUBST_RESULT: - if (udev->program_result[0] == '\0') - break; - /* get part part of the result string */ - i = 0; - if (attr != NULL) - i = strtoul(attr, &rest, 10); - if (i > 0) { - dbg("request part #%d of result string\n", i); - cpos = udev->program_result; - while (--i) { - while (cpos[0] != '\0' && !isspace(cpos[0])) - cpos++; - while (isspace(cpos[0])) - cpos++; - } - if (i > 0) { - err("requested part of result string not found\n"); - break; - } - strlcpy(temp2, cpos, sizeof(temp2)); - /* %{2+}c copies the whole string from the second part on */ - if (rest[0] != '+') { - cpos = strchr(temp2, ' '); - if (cpos) - cpos[0] = '\0'; - } - strlcat(string, temp2, maxsize); - dbg("substitute part of result string '%s'\n", temp2); - } else { - strlcat(string, udev->program_result, maxsize); - dbg("substitute result string '%s'\n", udev->program_result); - } - break; - case SUBST_ATTR: - if (attr == NULL) - err("missing file parameter for attr\n"); - else { - char devpath[PATH_SIZE]; - char *attrib; - const char *value = NULL; - size_t size; - - if (attr_get_by_subsys_id(attr, devpath, sizeof(devpath), &attrib)) { - if (attrib != NULL) - value = sysfs_attr_get_value(devpath, attrib); - else - break; - } - - /* try the current device, other matches may have selected */ - if (value == NULL && udev->dev_parent != NULL && udev->dev_parent != udev->dev) - value = sysfs_attr_get_value(udev->dev_parent->devpath, attr); - - /* look at all devices along the chain of parents */ - if (value == NULL) { - struct sysfs_device *dev_parent = udev->dev; - - do { - dbg("looking at '%s'\n", dev_parent->devpath); - value = sysfs_attr_get_value(dev_parent->devpath, attr); - if (value != NULL) { - strlcpy(temp2, value, sizeof(temp2)); - break; - } - dev_parent = sysfs_device_get_parent(dev_parent); - } while (dev_parent != NULL); - } - - if (value == NULL) - break; - - /* strip trailing whitespace, and replace unwanted characters */ - size = strlcpy(temp2, value, sizeof(temp2)); - if (size >= sizeof(temp2)) - size = sizeof(temp2)-1; - while (size > 0 && isspace(temp2[size-1])) - temp2[--size] = '\0'; - count = replace_chars(temp2, ALLOWED_CHARS_INPUT); - if (count > 0) - info("%i character(s) replaced\n" , count); - strlcat(string, temp2, maxsize); - dbg("substitute sysfs value '%s'\n", temp2); - } - break; - case SUBST_PARENT: - { - struct sysfs_device *dev_parent; - - dev_parent = sysfs_device_get_parent(udev->dev); - if (dev_parent != NULL) { - struct udevice *udev_parent; - - dbg("found parent '%s', get the node name\n", dev_parent->devpath); - udev_parent = udev_device_init(NULL); - if (udev_parent != NULL) { - /* lookup the name in the udev_db with the DEVPATH of the parent */ - if (udev_db_get_device(udev_parent, dev_parent->devpath) == 0) { - strlcat(string, udev_parent->name, maxsize); - dbg("substitute parent node name'%s'\n", udev_parent->name); - } else - dbg("parent not found in database\n"); - udev_device_cleanup(udev_parent); - } - } - } - break; - case SUBST_TEMP_NODE: - if (udev->tmp_node[0] == '\0' && major(udev->devt) > 0) { - dbg("create temporary device node for callout\n"); - snprintf(udev->tmp_node, sizeof(udev->tmp_node), "%s/.tmp-%u-%u", - udev_root, major(udev->devt), minor(udev->devt)); - udev->tmp_node[sizeof(udev->tmp_node)-1] = '\0'; - udev_node_mknod(udev, udev->tmp_node, udev->devt, 0600, 0, 0); - } - strlcat(string, udev->tmp_node, maxsize); - dbg("substitute temporary device node name '%s'\n", udev->tmp_node); - break; - case SUBST_NAME: - if (udev->name[0] == '\0') { - strlcat(string, udev->dev->kernel, maxsize); - dbg("substitute udev->kernel '%s'\n", udev->name); - } else { - strlcat(string, udev->name, maxsize); - dbg("substitute udev->name '%s'\n", udev->name); - } - break; - case SUBST_LINKS: - if (!list_empty(&udev->symlink_list)) { - struct name_entry *name_loop; - char symlinks[PATH_SIZE] = ""; - - list_for_each_entry(name_loop, &udev->symlink_list, node) { - strlcat(symlinks, name_loop->name, sizeof(symlinks)); - strlcat(symlinks, " ", sizeof(symlinks)); - } - remove_trailing_chars(symlinks, ' '); - strlcat(string, symlinks, maxsize); - } - break; - case SUBST_ROOT: - strlcat(string, udev_root, maxsize); - dbg("substitute udev_root '%s'\n", udev_root); - break; - case SUBST_SYS: - strlcat(string, sysfs_path, maxsize); - dbg("substitute sysfs_path '%s'\n", sysfs_path); - break; - case SUBST_ENV: - if (attr == NULL) { - dbg("missing attribute\n"); - break; - } - pos = getenv(attr); - if (pos == NULL) { - dbg("env '%s' not available\n", attr); - break; - } - dbg("substitute env '%s=%s'\n", attr, pos); - strlcat(string, pos, maxsize); - break; - default: - err("unknown substitution type=%i\n", type); - break; - } - /* possibly truncate to format-char specified length */ - if (len >= 0 && len < (int)strlen(head)) { - head[len] = '\0'; - dbg("truncate to %i chars, subtitution string becomes '%s'\n", len, head); - } - strlcat(string, temp, maxsize); - } -} - -static char *key_val(struct udev_rule *rule, struct key *key) -{ - return rule->buf + key->val_off; -} - -static char *key_pair_name(struct udev_rule *rule, struct key_pair *pair) -{ - return rule->buf + pair->key_name_off; -} - -static int match_key(const char *key_name, struct udev_rule *rule, struct key *key, const char *val) -{ - char value[PATH_SIZE]; - char *key_value; - char *pos; - int match = 0; - - if (key->operation != KEY_OP_MATCH && - key->operation != KEY_OP_NOMATCH) - return 0; - - /* look for a matching string, parts are separated by '|' */ - strlcpy(value, rule->buf + key->val_off, sizeof(value)); - key_value = value; - dbg("key %s value='%s'\n", key_name, key_value); - while (key_value) { - pos = strchr(key_value, '|'); - if (pos) { - pos[0] = '\0'; - pos++; - } - - dbg("match %s '%s' <-> '%s'\n", key_name, key_value, val); - match = (fnmatch(key_value, val, 0) == 0); - if (match) - break; - - key_value = pos; - } - - if (match && (key->operation == KEY_OP_MATCH)) { - dbg("%s is true (matching value)\n", key_name); - return 0; - } - if (!match && (key->operation == KEY_OP_NOMATCH)) { - dbg("%s is true (non-matching value)\n", key_name); - return 0; - } - return -1; -} - -/* match a single rule against a given device and possibly its parent devices */ -static int match_rule(struct udevice *udev, struct udev_rule *rule) -{ - int i; - - if (match_key("ACTION", rule, &rule->action, udev->action)) - goto nomatch; - - if (match_key("KERNEL", rule, &rule->kernel, udev->dev->kernel)) - goto nomatch; - - if (match_key("SUBSYSTEM", rule, &rule->subsystem, udev->dev->subsystem)) - goto nomatch; - - if (match_key("DEVPATH", rule, &rule->devpath, udev->dev->devpath)) - goto nomatch; - - if (match_key("DRIVER", rule, &rule->driver, udev->dev->driver)) - goto nomatch; - - /* match NAME against a value assigned by an earlier rule */ - if (match_key("NAME", rule, &rule->name, udev->name)) - goto nomatch; - - /* match against current list of symlinks */ - if (rule->symlink_match.operation == KEY_OP_MATCH || - rule->symlink_match.operation == KEY_OP_NOMATCH) { - struct name_entry *name_loop; - int match = 0; - - list_for_each_entry(name_loop, &udev->symlink_list, node) { - if (match_key("SYMLINK", rule, &rule->symlink_match, name_loop->name) == 0) { - match = 1; - break; - } - } - if (!match) - goto nomatch; - } - - for (i = 0; i < rule->env.count; i++) { - struct key_pair *pair = &rule->env.keys[i]; - - /* we only check for matches, assignments will be handled later */ - if (pair->key.operation == KEY_OP_MATCH || - pair->key.operation == KEY_OP_NOMATCH) { - const char *key_name = key_pair_name(rule, pair); - const char *value = getenv(key_name); - - if (!value) { - dbg("ENV{'%s'} is not set, treat as empty\n", key_name); - value = ""; - } - if (match_key("ENV", rule, &pair->key, value)) - goto nomatch; - } - } - - if (rule->test.operation == KEY_OP_MATCH || - rule->test.operation == KEY_OP_NOMATCH) { - char filename[PATH_SIZE]; - char devpath[PATH_SIZE]; - char *attr; - struct stat statbuf; - int match; - - strlcpy(filename, key_val(rule, &rule->test), sizeof(filename)); - udev_rules_apply_format(udev, filename, sizeof(filename)); - - if (attr_get_by_subsys_id(filename, devpath, sizeof(devpath), &attr)) { - strlcpy(filename, sysfs_path, sizeof(filename)); - strlcat(filename, devpath, sizeof(filename)); - if (attr != NULL) { - strlcat(filename, "/", sizeof(filename)); - strlcat(filename, attr, sizeof(filename)); - } - } else if (filename[0] != '/') { - char tmp[PATH_SIZE]; - - strlcpy(tmp, sysfs_path, sizeof(tmp)); - strlcat(tmp, udev->dev->devpath, sizeof(tmp)); - strlcat(tmp, "/", sizeof(tmp)); - strlcat(tmp, filename, sizeof(tmp)); - strlcpy(filename, tmp, sizeof(filename)); - } - - attr_subst_subdir(filename, sizeof(filename)); - - match = (stat(filename, &statbuf) == 0); - info("'%s' %s", filename, match ? "exists\n" : "does not exist\n"); - if (match && rule->test_mode_mask > 0) { - match = ((statbuf.st_mode & rule->test_mode_mask) > 0); - info("'%s' has mode=%#o and %s %#o\n", filename, statbuf.st_mode, - match ? "matches" : "does not match", - rule->test_mode_mask); - } - if (match && rule->test.operation == KEY_OP_NOMATCH) - goto nomatch; - if (!match && rule->test.operation == KEY_OP_MATCH) - goto nomatch; - dbg("TEST key is true\n"); - } - - if (rule->wait_for.operation != KEY_OP_UNSET) { - char filename[PATH_SIZE]; - int found; - - strlcpy(filename, key_val(rule, &rule->wait_for), sizeof(filename)); - udev_rules_apply_format(udev, filename, sizeof(filename)); - found = (wait_for_file(udev, filename, 10) == 0); - if (!found && (rule->wait_for.operation != KEY_OP_NOMATCH)) - goto nomatch; - } - - /* check for matching sysfs attribute pairs */ - for (i = 0; i < rule->attr.count; i++) { - struct key_pair *pair = &rule->attr.keys[i]; - - if (pair->key.operation == KEY_OP_MATCH || - pair->key.operation == KEY_OP_NOMATCH) { - const char *key_name = key_pair_name(rule, pair); - const char *key_value = key_val(rule, &pair->key); - char devpath[PATH_SIZE]; - char *attrib; - const char *value = NULL; - char val[VALUE_SIZE]; - size_t len; - - if (attr_get_by_subsys_id(key_name, devpath, sizeof(devpath), &attrib)) { - if (attrib != NULL) - value = sysfs_attr_get_value(devpath, attrib); - else - goto nomatch; - } - if (value == NULL) - value = sysfs_attr_get_value(udev->dev->devpath, key_name); - if (value == NULL) - goto nomatch; - strlcpy(val, value, sizeof(val)); - - /* strip trailing whitespace of value, if not asked to match for it */ - len = strlen(key_value); - if (len > 0 && !isspace(key_value[len-1])) { - len = strlen(val); - while (len > 0 && isspace(val[len-1])) - val[--len] = '\0'; - dbg("removed %zi trailing whitespace chars from '%s'\n", strlen(val)-len, val); - } - - if (match_key("ATTR", rule, &pair->key, val)) - goto nomatch; - } - } - - /* walk up the chain of parent devices and find a match */ - udev->dev_parent = udev->dev; - while (1) { - /* check for matching kernel device name */ - if (match_key("KERNELS", rule, &rule->kernels, udev->dev_parent->kernel)) - goto try_parent; - - /* check for matching subsystem value */ - if (match_key("SUBSYSTEMS", rule, &rule->subsystems, udev->dev_parent->subsystem)) - goto try_parent; - - /* check for matching driver */ - if (match_key("DRIVERS", rule, &rule->drivers, udev->dev_parent->driver)) - goto try_parent; - - /* check for matching sysfs attribute pairs */ - for (i = 0; i < rule->attrs.count; i++) { - struct key_pair *pair = &rule->attrs.keys[i]; - - if (pair->key.operation == KEY_OP_MATCH || - pair->key.operation == KEY_OP_NOMATCH) { - const char *key_name = key_pair_name(rule, pair); - const char *key_value = key_val(rule, &pair->key); - const char *value; - char val[VALUE_SIZE]; - size_t len; - - value = sysfs_attr_get_value(udev->dev_parent->devpath, key_name); - if (value == NULL) - value = sysfs_attr_get_value(udev->dev->devpath, key_name); - if (value == NULL) - goto try_parent; - strlcpy(val, value, sizeof(val)); - - /* strip trailing whitespace of value, if not asked to match for it */ - len = strlen(key_value); - if (len > 0 && !isspace(key_value[len-1])) { - len = strlen(val); - while (len > 0 && isspace(val[len-1])) - val[--len] = '\0'; - dbg("removed %zi trailing whitespace chars from '%s'\n", strlen(val)-len, val); - } - - if (match_key("ATTRS", rule, &pair->key, val)) - goto try_parent; - } - } - - /* found matching device */ - break; -try_parent: - /* move to parent device */ - dbg("try parent sysfs device\n"); - udev->dev_parent = sysfs_device_get_parent(udev->dev_parent); - if (udev->dev_parent == NULL) - goto nomatch; - dbg("looking at dev_parent->devpath='%s'\n", udev->dev_parent->devpath); - dbg("looking at dev_parent->kernel='%s'\n", udev->dev_parent->kernel); - } - - /* execute external program */ - if (rule->program.operation != KEY_OP_UNSET) { - char program[PATH_SIZE]; - char result[PATH_SIZE]; - - strlcpy(program, key_val(rule, &rule->program), sizeof(program)); - udev_rules_apply_format(udev, program, sizeof(program)); - if (run_program(program, udev->dev->subsystem, result, sizeof(result), NULL) != 0) { - dbg("PROGRAM is false\n"); - udev->program_result[0] = '\0'; - if (rule->program.operation != KEY_OP_NOMATCH) - goto nomatch; - } else { - int count; - - dbg("PROGRAM matches\n"); - remove_trailing_chars(result, '\n'); - if (rule->string_escape == ESCAPE_UNSET || - rule->string_escape == ESCAPE_REPLACE) { - count = replace_chars(result, ALLOWED_CHARS_INPUT); - if (count > 0) - info("%i character(s) replaced\n" , count); - } - dbg("result is '%s'\n", result); - strlcpy(udev->program_result, result, sizeof(udev->program_result)); - dbg("PROGRAM returned successful\n"); - if (rule->program.operation == KEY_OP_NOMATCH) - goto nomatch; - } - dbg("PROGRAM key is true\n"); - } - - /* check for matching result of external program */ - if (match_key("RESULT", rule, &rule->result, udev->program_result)) - goto nomatch; - - /* import variables returned from program or or file into environment */ - if (rule->import.operation != KEY_OP_UNSET) { - char import[PATH_SIZE]; - int rc = -1; - - strlcpy(import, key_val(rule, &rule->import), sizeof(import)); - udev_rules_apply_format(udev, import, sizeof(import)); - dbg("check for IMPORT import='%s'\n", import); - if (rule->import_type == IMPORT_PROGRAM) { - rc = import_program_into_env(udev, import); - } else if (rule->import_type == IMPORT_FILE) { - dbg("import file import='%s'\n", import); - rc = import_file_into_env(udev, import); - } else if (rule->import_type == IMPORT_PARENT) { - dbg("import parent import='%s'\n", import); - rc = import_parent_into_env(udev, import); - } - if (rc != 0) { - dbg("IMPORT failed\n"); - if (rule->import.operation != KEY_OP_NOMATCH) - goto nomatch; - } else - dbg("IMPORT '%s' imported\n", key_val(rule, &rule->import)); - dbg("IMPORT key is true\n"); - } - - /* rule matches, if we have ENV assignments export it */ - for (i = 0; i < rule->env.count; i++) { - struct key_pair *pair = &rule->env.keys[i]; - - if (pair->key.operation == KEY_OP_ASSIGN) { - char temp_value[NAME_SIZE]; - const char *key_name = key_pair_name(rule, pair); - const char *value = key_val(rule, &pair->key); - - /* make sure we don't write to the same string we possibly read from */ - strlcpy(temp_value, value, sizeof(temp_value)); - udev_rules_apply_format(udev, temp_value, NAME_SIZE); - - if (temp_value[0] == '\0') { - name_list_key_remove(&udev->env_list, key_name); - unsetenv(key_name); - info("unset ENV '%s'\n", key_name); - } else { - struct name_entry *entry; - - entry = name_list_key_add(&udev->env_list, key_name, temp_value); - if (entry == NULL) - break; - putenv(entry->name); - info("set ENV '%s'\n", entry->name); - } - } - } - - /* if we have ATTR assignments, write value to sysfs file */ - for (i = 0; i < rule->attr.count; i++) { - struct key_pair *pair = &rule->attr.keys[i]; - - if (pair->key.operation == KEY_OP_ASSIGN) { - const char *key_name = key_pair_name(rule, pair); - char devpath[PATH_SIZE]; - char *attrib; - char attr[PATH_SIZE] = ""; - char value[NAME_SIZE]; - FILE *f; - - if (attr_get_by_subsys_id(key_name, devpath, sizeof(devpath), &attrib)) { - if (attrib != NULL) { - strlcpy(attr, sysfs_path, sizeof(attr)); - strlcat(attr, devpath, sizeof(attr)); - strlcat(attr, "/", sizeof(attr)); - strlcat(attr, attrib, sizeof(attr)); - } - } - - if (attr[0] == '\0') { - strlcpy(attr, sysfs_path, sizeof(attr)); - strlcat(attr, udev->dev->devpath, sizeof(attr)); - strlcat(attr, "/", sizeof(attr)); - strlcat(attr, key_name, sizeof(attr)); - } - - attr_subst_subdir(attr, sizeof(attr)); - - strlcpy(value, key_val(rule, &pair->key), sizeof(value)); - udev_rules_apply_format(udev, value, sizeof(value)); - info("writing '%s' to sysfs file '%s'\n", value, attr); - f = fopen(attr, "w"); - if (f != NULL) { - if (!udev->test_run) - if (fprintf(f, "%s", value) <= 0) - err("error writing ATTR{%s}: %s\n", attr, strerror(errno)); - fclose(f); - } else - err("error opening ATTR{%s} for writing: %s\n", attr, strerror(errno)); - } - } - return 0; - -nomatch: - return -1; -} - -int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev) -{ - struct udev_rule *rule; - int name_set = 0; - - dbg("udev->dev->devpath='%s'\n", udev->dev->devpath); - dbg("udev->dev->kernel='%s'\n", udev->dev->kernel); - - /* look for a matching rule to apply */ - udev_rules_iter_init(rules); - while (1) { - rule = udev_rules_iter_next(rules); - if (rule == NULL) - break; - - if (name_set && - (rule->name.operation == KEY_OP_ASSIGN || - rule->name.operation == KEY_OP_ASSIGN_FINAL || - rule->name.operation == KEY_OP_ADD)) { - dbg("node name already set, rule ignored\n"); - continue; - } - - dbg("process rule\n"); - if (match_rule(udev, rule) == 0) { - /* apply options */ - if (rule->ignore_device) { - info("rule applied, '%s' is ignored\n", udev->dev->kernel); - udev->ignore_device = 1; - return 0; - } - if (rule->ignore_remove) { - udev->ignore_remove = 1; - dbg("remove event should be ignored\n"); - } - if (rule->link_priority != 0) { - udev->link_priority = rule->link_priority; - info("link_priority=%i\n", udev->link_priority); - } - if (rule->event_timeout >= 0) { - udev->event_timeout = rule->event_timeout; - info("event_timeout=%i\n", udev->event_timeout); - } - /* apply all_partitions option only at a main block device */ - if (rule->partitions && - strcmp(udev->dev->subsystem, "block") == 0 && udev->dev->kernel_number[0] == '\0') { - udev->partitions = rule->partitions; - dbg("creation of partition nodes requested\n"); - } - - /* apply permissions */ - if (!udev->mode_final && rule->mode.operation != KEY_OP_UNSET) { - if (rule->mode.operation == KEY_OP_ASSIGN_FINAL) - udev->mode_final = 1; - char buf[20]; - strlcpy(buf, key_val(rule, &rule->mode), sizeof(buf)); - udev_rules_apply_format(udev, buf, sizeof(buf)); - udev->mode = strtol(buf, NULL, 8); - dbg("applied mode=%#o to '%s'\n", udev->mode, udev->dev->kernel); - } - if (!udev->owner_final && rule->owner.operation != KEY_OP_UNSET) { - if (rule->owner.operation == KEY_OP_ASSIGN_FINAL) - udev->owner_final = 1; - strlcpy(udev->owner, key_val(rule, &rule->owner), sizeof(udev->owner)); - udev_rules_apply_format(udev, udev->owner, sizeof(udev->owner)); - dbg("applied owner='%s' to '%s'\n", udev->owner, udev->dev->kernel); - } - if (!udev->group_final && rule->group.operation != KEY_OP_UNSET) { - if (rule->group.operation == KEY_OP_ASSIGN_FINAL) - udev->group_final = 1; - strlcpy(udev->group, key_val(rule, &rule->group), sizeof(udev->group)); - udev_rules_apply_format(udev, udev->group, sizeof(udev->group)); - dbg("applied group='%s' to '%s'\n", udev->group, udev->dev->kernel); - } - - /* collect symlinks */ - if (!udev->symlink_final && - (rule->symlink.operation == KEY_OP_ASSIGN || - rule->symlink.operation == KEY_OP_ASSIGN_FINAL || - rule->symlink.operation == KEY_OP_ADD)) { - char temp[PATH_SIZE]; - char *pos, *next; - int count; - - if (rule->symlink.operation == KEY_OP_ASSIGN_FINAL) - udev->symlink_final = 1; - if (rule->symlink.operation == KEY_OP_ASSIGN || - rule->symlink.operation == KEY_OP_ASSIGN_FINAL) { - info("reset symlink list\n"); - name_list_cleanup(&udev->symlink_list); - } - /* allow multiple symlinks separated by spaces */ - strlcpy(temp, key_val(rule, &rule->symlink), sizeof(temp)); - udev_rules_apply_format(udev, temp, sizeof(temp)); - if (rule->string_escape == ESCAPE_UNSET || - rule->string_escape == ESCAPE_REPLACE) { - count = replace_chars(temp, ALLOWED_CHARS_FILE " "); - if (count > 0) - info("%i character(s) replaced\n" , count); - } - dbg("rule applied, added symlink(s) '%s'\n", temp); - pos = temp; - while (isspace(pos[0])) - pos++; - next = strchr(pos, ' '); - while (next) { - next[0] = '\0'; - info("add symlink '%s'\n", pos); - name_list_add(&udev->symlink_list, pos, 0); - while (isspace(next[1])) - next++; - pos = &next[1]; - next = strchr(pos, ' '); - } - if (pos[0] != '\0') { - info("add symlink '%s'\n", pos); - name_list_add(&udev->symlink_list, pos, 0); - } - } - - /* set name, later rules with name set will be ignored */ - if (rule->name.operation == KEY_OP_ASSIGN || - rule->name.operation == KEY_OP_ASSIGN_FINAL || - rule->name.operation == KEY_OP_ADD) { - int count; - - name_set = 1; - strlcpy(udev->name, key_val(rule, &rule->name), sizeof(udev->name)); - udev_rules_apply_format(udev, udev->name, sizeof(udev->name)); - if (rule->string_escape == ESCAPE_UNSET || - rule->string_escape == ESCAPE_REPLACE) { - count = replace_chars(udev->name, ALLOWED_CHARS_FILE); - if (count > 0) - info("%i character(s) replaced\n", count); - } - - info("rule applied, '%s' becomes '%s'\n", udev->dev->kernel, udev->name); - if (strcmp(udev->dev->subsystem, "net") != 0) - dbg("name, '%s' is going to have owner='%s', group='%s', mode=%#o partitions=%i\n", - udev->name, udev->owner, udev->group, udev->mode, udev->partitions); - } - - if (!udev->run_final && rule->run.operation != KEY_OP_UNSET) { - struct name_entry *entry; - - if (rule->run.operation == KEY_OP_ASSIGN_FINAL) - udev->run_final = 1; - if (rule->run.operation == KEY_OP_ASSIGN || rule->run.operation == KEY_OP_ASSIGN_FINAL) { - info("reset run list\n"); - name_list_cleanup(&udev->run_list); - } - dbg("add run '%s'\n", key_val(rule, &rule->run)); - entry = name_list_add(&udev->run_list, key_val(rule, &rule->run), 0); - if (rule->run_ignore_error) - entry->ignore_error = 1; - } - - if (rule->last_rule) { - dbg("last rule to be applied\n"); - break; - } - - if (rule->goto_label.operation != KEY_OP_UNSET) { - dbg("moving forward to label '%s'\n", key_val(rule, &rule->goto_label)); - udev_rules_iter_label(rules, key_val(rule, &rule->goto_label)); - } - } - } - - if (!name_set) { - info("no node name set, will use kernel name '%s'\n", udev->dev->kernel); - strlcpy(udev->name, udev->dev->kernel, sizeof(udev->name)); - } - - if (udev->tmp_node[0] != '\0') { - dbg("removing temporary device node\n"); - unlink_secure(udev->tmp_node); - udev->tmp_node[0] = '\0'; - } - - return 0; -} - -int udev_rules_get_run(struct udev_rules *rules, struct udevice *udev) -{ - struct udev_rule *rule; - - dbg("udev->kernel='%s'\n", udev->dev->kernel); - - /* look for a matching rule to apply */ - udev_rules_iter_init(rules); - while (1) { - rule = udev_rules_iter_next(rules); - if (rule == NULL) - break; - - dbg("process rule\n"); - if (rule->name.operation == KEY_OP_ASSIGN || - rule->name.operation == KEY_OP_ASSIGN_FINAL || - rule->name.operation == KEY_OP_ADD || - rule->symlink.operation == KEY_OP_ASSIGN || - rule->symlink.operation == KEY_OP_ASSIGN_FINAL || - rule->symlink.operation == KEY_OP_ADD || - rule->mode.operation != KEY_OP_UNSET || - rule->owner.operation != KEY_OP_UNSET || rule->group.operation != KEY_OP_UNSET) { - dbg("skip rule that names a device\n"); - continue; - } - - if (match_rule(udev, rule) == 0) { - if (rule->ignore_device) { - info("rule applied, '%s' is ignored\n", udev->dev->kernel); - udev->ignore_device = 1; - return 0; - } - if (rule->ignore_remove) { - udev->ignore_remove = 1; - dbg("remove event should be ignored\n"); - } - - if (!udev->run_final && rule->run.operation != KEY_OP_UNSET) { - struct name_entry *entry; - - if (rule->run.operation == KEY_OP_ASSIGN || - rule->run.operation == KEY_OP_ASSIGN_FINAL) { - info("reset run list\n"); - name_list_cleanup(&udev->run_list); - } - dbg("add run '%s'\n", key_val(rule, &rule->run)); - entry = name_list_add(&udev->run_list, key_val(rule, &rule->run), 0); - if (rule->run_ignore_error) - entry->ignore_error = 1; - if (rule->run.operation == KEY_OP_ASSIGN_FINAL) - break; - } - - if (rule->last_rule) { - dbg("last rule to be applied\n"); - break; - } - - if (rule->goto_label.operation != KEY_OP_UNSET) { - dbg("moving forward to label '%s'\n", key_val(rule, &rule->goto_label)); - udev_rules_iter_label(rules, key_val(rule, &rule->goto_label)); - } - } - } - - return 0; -} diff --git a/udev_rules.h b/udev_rules.h deleted file mode 100644 index fe0f9dfbb5..0000000000 --- a/udev_rules.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2003-2004 Greg Kroah-Hartman - * Copyright (C) 2004-2006 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef UDEV_RULES_H -#define UDEV_RULES_H - -#include "udev.h" -#include "list.h" - -#define PAIRS_MAX 5 -#define RULESFILE_SUFFIX ".rules" - -enum key_operation { - KEY_OP_UNSET, - KEY_OP_MATCH, - KEY_OP_NOMATCH, - KEY_OP_ADD, - KEY_OP_ASSIGN, - KEY_OP_ASSIGN_FINAL, -}; - -struct key { - enum key_operation operation; - size_t val_off; -}; - -struct key_pair { - struct key key; - size_t key_name_off; -}; - -struct key_pairs { - int count; - struct key_pair keys[PAIRS_MAX]; -}; - -enum import_type { - IMPORT_UNSET, - IMPORT_PROGRAM, - IMPORT_FILE, - IMPORT_PARENT, -}; - -enum escape_type { - ESCAPE_UNSET, - ESCAPE_NONE, - ESCAPE_REPLACE, -}; - -struct udev_rule { - struct key action; - struct key devpath; - struct key kernel; - struct key subsystem; - struct key driver; - struct key_pairs attr; - - struct key kernels; - struct key subsystems; - struct key drivers; - struct key_pairs attrs; - - struct key_pairs env; - struct key program; - struct key result; - struct key import; - enum import_type import_type; - struct key test; - mode_t test_mode_mask; - struct key run; - struct key wait_for; - struct key label; - struct key goto_label; - - struct key name; - struct key symlink; - struct key symlink_match; - struct key owner; - struct key group; - struct key mode; - enum escape_type string_escape; - - unsigned int link_priority; - int event_timeout; - unsigned int partitions; - unsigned int last_rule:1, - run_ignore_error:1, - ignore_device:1, - ignore_remove:1; - - size_t bufsize; - char buf[]; -}; - -struct udev_rules { - char *buf; - size_t bufsize; - size_t current; - int resolve_names; -}; - -extern int udev_rules_init(struct udev_rules *rules, int resolve_names); -extern void udev_rules_cleanup(struct udev_rules *rules); - -extern void udev_rules_iter_init(struct udev_rules *rules); -extern struct udev_rule *udev_rules_iter_next(struct udev_rules *rules); -extern struct udev_rule *udev_rules_iter_label(struct udev_rules *rules, const char *label); - -extern int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev); -extern int udev_rules_get_run(struct udev_rules *rules, struct udevice *udev); -extern int udev_rules_run(struct udevice *udev); - -extern void udev_rules_apply_format(struct udevice *udev, char *string, size_t maxsize); - -#endif diff --git a/udev_rules_parse.c b/udev_rules_parse.c deleted file mode 100644 index 3ada8b1c7d..0000000000 --- a/udev_rules_parse.c +++ /dev/null @@ -1,804 +0,0 @@ -/* - * Copyright (C) 2003,2004 Greg Kroah-Hartman - * Copyright (C) 2003-2006 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udev_rules.h" -#include "udev_selinux.h" - - -void udev_rules_iter_init(struct udev_rules *rules) -{ - dbg("bufsize=%zi\n", rules->bufsize); - rules->current = 0; -} - -struct udev_rule *udev_rules_iter_next(struct udev_rules *rules) -{ - static struct udev_rule *rule; - - if (!rules) - return NULL; - - dbg("current=%zi\n", rules->current); - if (rules->current >= rules->bufsize) { - dbg("no more rules\n"); - return NULL; - } - - /* get next rule */ - rule = (struct udev_rule *) (rules->buf + rules->current); - rules->current += sizeof(struct udev_rule) + rule->bufsize; - - return rule; -} - -struct udev_rule *udev_rules_iter_label(struct udev_rules *rules, const char *label) -{ - static struct udev_rule *rule; - size_t start = rules->current; - -next: - dbg("current=%zi\n", rules->current); - if (rules->current >= rules->bufsize) { - err("LABEL='%s' not found, GOTO will be ignored\n", label); - rules->current = start; - return NULL; - } - rule = (struct udev_rule *) (rules->buf + rules->current); - - if (strcmp(&rule->buf[rule->label.val_off], label) != 0) { - dbg("moving forward, looking for label '%s'\n", label); - rules->current += sizeof(struct udev_rule) + rule->bufsize; - goto next; - } - - dbg("found label '%s'\n", label); - return rule; -} - -static int get_key(char **line, char **key, enum key_operation *operation, char **value) -{ - char *linepos; - char *temp; - - linepos = *line; - if (linepos == NULL && linepos[0] == '\0') - return -1; - - /* skip whitespace */ - while (isspace(linepos[0]) || linepos[0] == ',') - linepos++; - - /* get the key */ - if (linepos[0] == '\0') - return -1; - *key = linepos; - - while (1) { - linepos++; - if (linepos[0] == '\0') - return -1; - if (isspace(linepos[0])) - break; - if (linepos[0] == '=') - break; - if ((linepos[0] == '+') || (linepos[0] == '!') || (linepos[0] == ':')) - if (linepos[1] == '=') - break; - } - - /* remember end of key */ - temp = linepos; - - /* skip whitespace after key */ - while (isspace(linepos[0])) - linepos++; - if (linepos[0] == '\0') - return -1; - - /* get operation type */ - if (linepos[0] == '=' && linepos[1] == '=') { - *operation = KEY_OP_MATCH; - linepos += 2; - dbg("operator=match\n"); - } else if (linepos[0] == '!' && linepos[1] == '=') { - *operation = KEY_OP_NOMATCH; - linepos += 2; - dbg("operator=nomatch\n"); - } else if (linepos[0] == '+' && linepos[1] == '=') { - *operation = KEY_OP_ADD; - linepos += 2; - dbg("operator=add\n"); - } else if (linepos[0] == '=') { - *operation = KEY_OP_ASSIGN; - linepos++; - dbg("operator=assign\n"); - } else if (linepos[0] == ':' && linepos[1] == '=') { - *operation = KEY_OP_ASSIGN_FINAL; - linepos += 2; - dbg("operator=assign_final\n"); - } else - return -1; - - /* terminate key */ - temp[0] = '\0'; - dbg("key='%s'\n", *key); - - /* skip whitespace after operator */ - while (isspace(linepos[0])) - linepos++; - if (linepos[0] == '\0') - return -1; - - /* get the value*/ - if (linepos[0] == '"') - linepos++; - else - return -1; - *value = linepos; - - temp = strchr(linepos, '"'); - if (!temp) - return -1; - temp[0] = '\0'; - temp++; - dbg("value='%s'\n", *value); - - /* move line to next key */ - *line = temp; - - return 0; -} - -/* extract possible KEY{attr} */ -static char *get_key_attribute(char *str) -{ - char *pos; - char *attr; - - attr = strchr(str, '{'); - if (attr != NULL) { - attr++; - pos = strchr(attr, '}'); - if (pos == NULL) { - err("missing closing brace for format\n"); - return NULL; - } - pos[0] = '\0'; - dbg("attribute='%s'\n", attr); - return attr; - } - - return NULL; -} - -static int add_rule_key(struct udev_rule *rule, struct key *key, - enum key_operation operation, const char *value) -{ - size_t val_len = strnlen(value, PATH_SIZE); - - key->operation = operation; - - key->val_off = rule->bufsize; - strlcpy(rule->buf + rule->bufsize, value, val_len+1); - rule->bufsize += val_len+1; - - return 0; -} - -static int add_rule_key_pair(struct udev_rule *rule, struct key_pairs *pairs, - enum key_operation operation, const char *key, const char *value) -{ - size_t key_len = strnlen(key, PATH_SIZE); - - if (pairs->count >= PAIRS_MAX) { - err("skip, too many keys of the same type in a single rule\n"); - return -1; - } - - add_rule_key(rule, &pairs->keys[pairs->count].key, operation, value); - - /* add the key-name of the pair */ - pairs->keys[pairs->count].key_name_off = rule->bufsize; - strlcpy(rule->buf + rule->bufsize, key, key_len+1); - rule->bufsize += key_len+1; - - pairs->count++; - - return 0; -} - -static int add_to_rules(struct udev_rules *rules, char *line, const char *filename, unsigned int lineno) -{ - char buf[sizeof(struct udev_rule) + LINE_SIZE]; - struct udev_rule *rule; - size_t rule_size; - int valid; - char *linepos; - char *attr; - size_t padding; - int physdev = 0; - int retval; - - memset(buf, 0x00, sizeof(buf)); - rule = (struct udev_rule *) buf; - rule->event_timeout = -1; - linepos = line; - valid = 0; - - /* get all the keys */ - while (1) { - char *key; - char *value; - enum key_operation operation = KEY_OP_UNSET; - - retval = get_key(&linepos, &key, &operation, &value); - if (retval) - break; - - if (strcasecmp(key, "ACTION") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err("invalid ACTION operation\n"); - goto invalid; - } - add_rule_key(rule, &rule->action, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "DEVPATH") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err("invalid DEVPATH operation\n"); - goto invalid; - } - add_rule_key(rule, &rule->devpath, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "KERNEL") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err("invalid KERNEL operation\n"); - goto invalid; - } - add_rule_key(rule, &rule->kernel, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "SUBSYSTEM") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err("invalid SUBSYSTEM operation\n"); - goto invalid; - } - /* bus, class, subsystem events should all be the same */ - if (strcmp(value, "subsystem") == 0 || - strcmp(value, "bus") == 0 || - strcmp(value, "class") == 0) { - if (strcmp(value, "bus") == 0 || strcmp(value, "class") == 0) - err("'%s' must be specified as 'subsystem' \n" - "please fix it in %s:%u", value, filename, lineno); - add_rule_key(rule, &rule->subsystem, operation, "subsystem|class|bus"); - } else - add_rule_key(rule, &rule->subsystem, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "DRIVER") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err("invalid DRIVER operation\n"); - goto invalid; - } - add_rule_key(rule, &rule->driver, operation, value); - valid = 1; - continue; - } - - if (strncasecmp(key, "ATTR{", sizeof("ATTR{")-1) == 0) { - attr = get_key_attribute(key + sizeof("ATTR")-1); - if (attr == NULL) { - err("error parsing ATTR attribute\n"); - goto invalid; - } - if (add_rule_key_pair(rule, &rule->attr, operation, attr, value) != 0) - goto invalid; - valid = 1; - continue; - } - - if (strcasecmp(key, "KERNELS") == 0 || - strcasecmp(key, "ID") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err("invalid KERNELS operation\n"); - goto invalid; - } - add_rule_key(rule, &rule->kernels, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "SUBSYSTEMS") == 0 || - strcasecmp(key, "BUS") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err("invalid SUBSYSTEMS operation\n"); - goto invalid; - } - add_rule_key(rule, &rule->subsystems, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "DRIVERS") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err("invalid DRIVERS operation\n"); - goto invalid; - } - add_rule_key(rule, &rule->drivers, operation, value); - valid = 1; - continue; - } - - if (strncasecmp(key, "ATTRS{", sizeof("ATTRS{")-1) == 0 || - strncasecmp(key, "SYSFS{", sizeof("SYSFS{")-1) == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err("invalid ATTRS operation\n"); - goto invalid; - } - attr = get_key_attribute(key + sizeof("ATTRS")-1); - if (attr == NULL) { - err("error parsing ATTRS attribute\n"); - goto invalid; - } - if (strncmp(attr, "device/", 7) == 0) - err("the 'device' link is deprecated and will be removed from a future kernel, \n" - "please fix it in %s:%u", filename, lineno); - else if (strstr(attr, "../") != NULL) - err("do not reference parent sysfs directories directly, that may break with a future kernel, \n" - "please fix it in %s:%u", filename, lineno); - if (add_rule_key_pair(rule, &rule->attrs, operation, attr, value) != 0) - goto invalid; - valid = 1; - continue; - } - - if (strncasecmp(key, "ENV{", sizeof("ENV{")-1) == 0) { - attr = get_key_attribute(key + sizeof("ENV")-1); - if (attr == NULL) { - err("error parsing ENV attribute\n"); - goto invalid; - } - if (strncmp(attr, "PHYSDEV", 7) == 0) - physdev = 1; - if (add_rule_key_pair(rule, &rule->env, operation, attr, value) != 0) - goto invalid; - valid = 1; - continue; - } - - if (strcasecmp(key, "PROGRAM") == 0) { - add_rule_key(rule, &rule->program, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "RESULT") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err("invalid RESULT operation\n"); - goto invalid; - } - add_rule_key(rule, &rule->result, operation, value); - valid = 1; - continue; - } - - if (strncasecmp(key, "IMPORT", sizeof("IMPORT")-1) == 0) { - attr = get_key_attribute(key + sizeof("IMPORT")-1); - if (attr != NULL && strstr(attr, "program")) { - dbg("IMPORT will be executed\n"); - rule->import_type = IMPORT_PROGRAM; - } else if (attr != NULL && strstr(attr, "file")) { - dbg("IMPORT will be included as file\n"); - rule->import_type = IMPORT_FILE; - } else if (attr != NULL && strstr(attr, "parent")) { - dbg("IMPORT will include the parent values\n"); - rule->import_type = IMPORT_PARENT; - } else { - /* figure it out if it is executable */ - char file[PATH_SIZE]; - char *pos; - struct stat statbuf; - - strlcpy(file, value, sizeof(file)); - pos = strchr(file, ' '); - if (pos) - pos[0] = '\0'; - - /* allow programs in /lib/udev called without the path */ - if (strchr(file, '/') == NULL) { - strlcpy(file, "/lib/udev/", sizeof(file)); - strlcat(file, value, sizeof(file)); - pos = strchr(file, ' '); - if (pos) - pos[0] = '\0'; - } - - dbg("IMPORT auto mode for '%s'\n", file); - if (!lstat(file, &statbuf) && (statbuf.st_mode & S_IXUSR)) { - dbg("IMPORT is executable, will be executed (autotype)\n"); - rule->import_type = IMPORT_PROGRAM; - } else { - dbg("IMPORT is not executable, will be included as file (autotype)\n"); - rule->import_type = IMPORT_FILE; - } - } - add_rule_key(rule, &rule->import, operation, value); - valid = 1; - continue; - } - - if (strncasecmp(key, "TEST", sizeof("TEST")-1) == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err("invalid TEST operation\n"); - goto invalid; - } - attr = get_key_attribute(key + sizeof("TEST")-1); - if (attr != NULL) - rule->test_mode_mask = strtol(attr, NULL, 8); - add_rule_key(rule, &rule->test, operation, value); - valid = 1; - continue; - } - - if (strncasecmp(key, "RUN", sizeof("RUN")-1) == 0) { - attr = get_key_attribute(key + sizeof("RUN")-1); - if (attr != NULL) { - if (strstr(attr, "ignore_error")) - rule->run_ignore_error = 1; - } - add_rule_key(rule, &rule->run, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "WAIT_FOR") == 0 || strcasecmp(key, "WAIT_FOR_SYSFS") == 0) { - add_rule_key(rule, &rule->wait_for, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "LABEL") == 0) { - add_rule_key(rule, &rule->label, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "GOTO") == 0) { - add_rule_key(rule, &rule->goto_label, operation, value); - valid = 1; - continue; - } - - if (strncasecmp(key, "NAME", sizeof("NAME")-1) == 0) { - attr = get_key_attribute(key + sizeof("NAME")-1); - if (attr != NULL) { - if (strstr(attr, "all_partitions") != NULL) { - dbg("creation of partition nodes requested\n"); - rule->partitions = DEFAULT_PARTITIONS_COUNT; - } - if (strstr(attr, "ignore_remove") != NULL) { - dbg("remove event should be ignored\n"); - rule->ignore_remove = 1; - } - } - if (value[0] == '\0') - dbg("name empty, node creation supressed\n"); - add_rule_key(rule, &rule->name, operation, value); - continue; - } - - if (strcasecmp(key, "SYMLINK") == 0) { - if (operation == KEY_OP_MATCH || - operation == KEY_OP_NOMATCH) - add_rule_key(rule, &rule->symlink_match, operation, value); - else - add_rule_key(rule, &rule->symlink, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "OWNER") == 0) { - valid = 1; - if (rules->resolve_names && (!strchr(value, '$') && !strchr(value, '%'))) { - char *endptr; - strtoul(value, &endptr, 10); - if (endptr[0] != '\0') { - char owner[32]; - uid_t uid = lookup_user(value); - dbg("replacing username='%s' by id=%i\n", value, uid); - sprintf(owner, "%u", (unsigned int) uid); - add_rule_key(rule, &rule->owner, operation, owner); - continue; - } - } - - add_rule_key(rule, &rule->owner, operation, value); - continue; - } - - if (strcasecmp(key, "GROUP") == 0) { - valid = 1; - if (rules->resolve_names && (!strchr(value, '$') && !strchr(value, '%'))) { - char *endptr; - strtoul(value, &endptr, 10); - if (endptr[0] != '\0') { - char group[32]; - gid_t gid = lookup_group(value); - dbg("replacing groupname='%s' by id=%i\n", value, gid); - sprintf(group, "%u", (unsigned int) gid); - add_rule_key(rule, &rule->group, operation, group); - continue; - } - } - - add_rule_key(rule, &rule->group, operation, value); - continue; - } - - if (strcasecmp(key, "MODE") == 0) { - add_rule_key(rule, &rule->mode, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "OPTIONS") == 0) { - const char *pos; - - if (strstr(value, "last_rule") != NULL) { - dbg("last rule to be applied\n"); - rule->last_rule = 1; - } - if (strstr(value, "ignore_device") != NULL) { - dbg("device should be ignored\n"); - rule->ignore_device = 1; - } - if (strstr(value, "ignore_remove") != NULL) { - dbg("remove event should be ignored\n"); - rule->ignore_remove = 1; - } - pos = strstr(value, "link_priority="); - if (pos != NULL) { - rule->link_priority = atoi(&pos[strlen("link_priority=")]); - dbg("link priority=%i\n", rule->link_priority); - } - pos = strstr(value, "event_timeout="); - if (pos != NULL) { - rule->event_timeout = atoi(&pos[strlen("event_timeout=")]); - dbg("event timout=%i\n", rule->event_timeout); - } - pos = strstr(value, "string_escape="); - if (pos != NULL) { - pos = &pos[strlen("string_escape=")]; - if (strncmp(pos, "none", strlen("none")) == 0) - rule->string_escape = ESCAPE_NONE; - else if (strncmp(pos, "replace", strlen("replace")) == 0) - rule->string_escape = ESCAPE_REPLACE; - } - if (strstr(value, "all_partitions") != NULL) { - dbg("creation of partition nodes requested\n"); - rule->partitions = DEFAULT_PARTITIONS_COUNT; - } - valid = 1; - continue; - } - - err("unknown key '%s' in %s:%u\n", key, filename, lineno); - } - - if (physdev && rule->wait_for.operation == KEY_OP_UNSET) - err("PHYSDEV* values are deprecated and will be removed from a future kernel, \n" - "please fix it in %s:%u", filename, lineno); - - /* skip line if not any valid key was found */ - if (!valid) - goto invalid; - - /* grow buffer and add rule */ - rule_size = sizeof(struct udev_rule) + rule->bufsize; - padding = (sizeof(size_t) - rule_size % sizeof(size_t)) % sizeof(size_t); - dbg("add %zi padding bytes\n", padding); - rule_size += padding; - rule->bufsize += padding; - - rules->buf = realloc(rules->buf, rules->bufsize + rule_size); - if (!rules->buf) { - err("realloc failed\n"); - goto exit; - } - dbg("adding rule to offset %zi\n", rules->bufsize); - memcpy(rules->buf + rules->bufsize, rule, rule_size); - rules->bufsize += rule_size; -exit: - return 0; - -invalid: - err("invalid rule '%s:%u'\n", filename, lineno); - return -1; -} - -static int parse_file(struct udev_rules *rules, const char *filename) -{ - char line[LINE_SIZE]; - char *bufline; - unsigned int lineno; - char *buf; - size_t bufsize; - size_t cur; - size_t count; - int retval = 0; - - if (file_map(filename, &buf, &bufsize) != 0) { - err("can't open '%s' as rules file: %s\n", filename, strerror(errno)); - return -1; - } - info("reading '%s' as rules file\n", filename); - - /* loop through the whole file */ - cur = 0; - lineno = 0; - while (cur < bufsize) { - unsigned int i, j; - - count = buf_get_line(buf, bufsize, cur); - bufline = &buf[cur]; - cur += count+1; - lineno++; - - /* eat the whitespace */ - while ((count > 0) && isspace(bufline[0])) { - bufline++; - count--; - } - if (count == 0) - continue; - - /* see if this is a comment */ - if (bufline[0] == COMMENT_CHARACTER) - continue; - - if (count >= sizeof(line)) { - err("line too long, rule skipped '%s:%u'\n", filename, lineno); - continue; - } - - /* skip backslash and newline from multiline rules */ - for (i = j = 0; i < count; i++) { - if (bufline[i] == '\\' && bufline[i+1] == '\n') - continue; - - line[j++] = bufline[i]; - } - line[j] = '\0'; - - dbg("read '%s'\n", line); - add_to_rules(rules, line, filename, lineno); - } - - file_unmap(buf, bufsize); - return retval; -} - -int udev_rules_init(struct udev_rules *rules, int resolve_names) -{ - struct stat statbuf; - char filename[PATH_MAX]; - LIST_HEAD(name_list); - LIST_HEAD(sort_list); - struct name_entry *name_loop, *name_tmp; - struct name_entry *sort_loop, *sort_tmp; - int retval = 0; - - memset(rules, 0x00, sizeof(struct udev_rules)); - rules->resolve_names = resolve_names; - - if (udev_rules_dir[0] != '\0') { - /* custom rules location for testing */ - add_matching_files(&name_list, udev_rules_dir, RULESFILE_SUFFIX); - } else { - /* read default rules */ - add_matching_files(&name_list, RULES_LIB_DIR, RULESFILE_SUFFIX); - - /* read user/custom rules */ - add_matching_files(&sort_list, RULES_ETC_DIR, RULESFILE_SUFFIX); - - /* read dynamic/temporary rules */ - strlcpy(filename, udev_root, sizeof(filename)); - strlcat(filename, "/"RULES_DYN_DIR, sizeof(filename)); - if (stat(filename, &statbuf) != 0) { - create_path(filename); - selinux_setfscreatecon(filename, NULL, S_IFDIR|0755); - mkdir(filename, 0755); - selinux_resetfscreatecon(); - } - add_matching_files(&sort_list, filename, RULESFILE_SUFFIX); - - /* sort all rules files by basename into list of files */ - list_for_each_entry_safe(sort_loop, sort_tmp, &sort_list, node) { - const char *sort_base = strrchr(sort_loop->name, '/'); - - if (sort_base == NULL) - continue; - - list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) { - const char *name_base = strrchr(name_loop->name, '/'); - - if (name_base == NULL) - continue; - - if (strcmp(name_base, sort_base) > 0) - break; - } - list_move_tail(&sort_loop->node, &name_loop->node); - } - } - - /* parse list of files */ - list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) { - if (stat(name_loop->name, &statbuf) == 0) { - if (statbuf.st_size) - parse_file(rules, name_loop->name); - else - dbg("empty rules file '%s'\n", name_loop->name); - } else - err("could not read '%s': %s\n", name_loop->name, strerror(errno)); - list_del(&name_loop->node); - free(name_loop); - } - - return retval; -} - -void udev_rules_cleanup(struct udev_rules *rules) -{ - if (rules->buf) { - free(rules->buf); - rules->buf = NULL; - } -} - diff --git a/udev_selinux.c b/udev_selinux.c deleted file mode 100644 index eec950194c..0000000000 --- a/udev_selinux.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2004 Daniel Walsh - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udev_selinux.h" - -static security_context_t prev_scontext = NULL; - -static int is_selinux_running(void) -{ - static int selinux_enabled = -1; - - if (selinux_enabled == -1) - selinux_enabled = (is_selinux_enabled() > 0); - - dbg("selinux=%i\n", selinux_enabled); - return selinux_enabled; -} - -static char *get_media(const char *devname, int mode) -{ - FILE *fp; - char procfile[PATH_MAX]; - char mediabuf[256]; - int size; - char *media = NULL; - - if (!(mode & S_IFBLK)) - return NULL; - - snprintf(procfile, PATH_MAX, "/proc/ide/%s/media", devname); - procfile[PATH_MAX-1] = '\0'; - - fp = fopen(procfile, "r"); - if (!fp) - goto out; - - if (fgets(mediabuf, sizeof(mediabuf), fp) == NULL) - goto close_out; - - size = strlen(mediabuf); - while (size-- > 0) { - if (isspace(mediabuf[size])) { - mediabuf[size] = '\0'; - } else { - break; - } - } - - media = strdup(mediabuf); - info("selinux_get_media(%s)='%s'\n", devname, media); - -close_out: - fclose(fp); -out: - return media; -} - -void selinux_setfilecon(const char *file, const char *devname, unsigned int mode) -{ - if (is_selinux_running()) { - security_context_t scontext = NULL; - char *media; - int ret = -1; - - if (devname) { - media = get_media(devname, mode); - if (media) { - ret = matchmediacon(media, &scontext); - free(media); - } - } - - if (ret < 0) - if (matchpathcon(file, mode, &scontext) < 0) { - err("matchpathcon(%s) failed\n", file); - return; - } - - if (lsetfilecon(file, scontext) < 0) - err("setfilecon %s failed: %s\n", file, strerror(errno)); - - freecon(scontext); - } -} - -void selinux_setfscreatecon(const char *file, const char *devname, unsigned int mode) -{ - if (is_selinux_running()) { - security_context_t scontext = NULL; - char *media; - int ret = -1; - - if (devname) { - media = get_media(devname, mode); - if (media) { - ret = matchmediacon(media, &scontext); - free(media); - } - } - - if (ret < 0) - if (matchpathcon(file, mode, &scontext) < 0) { - err("matchpathcon(%s) failed\n", file); - return; - } - - if (setfscreatecon(scontext) < 0) - err("setfscreatecon %s failed: %s\n", file, strerror(errno)); - - freecon(scontext); - } -} - -void selinux_resetfscreatecon(void) -{ - if (is_selinux_running()) { - if (setfscreatecon(prev_scontext) < 0) - err("setfscreatecon failed: %s\n", strerror(errno)); - } -} - -void selinux_init(void) -{ - /* - * record the present security context, for file-creation - * restoration creation purposes. - */ - if (is_selinux_running()) { - if (!udev_root[0]) - err("selinux_init: udev_root not set\n"); - matchpathcon_init_prefix(NULL, udev_root); - if (getfscreatecon(&prev_scontext) < 0) { - err("getfscreatecon failed\n"); - prev_scontext = NULL; - } - } -} - -void selinux_exit(void) -{ - if (is_selinux_running() && prev_scontext) { - freecon(prev_scontext); - prev_scontext = NULL; - } -} diff --git a/udev_selinux.h b/udev_selinux.h deleted file mode 100644 index 73567d6cfe..0000000000 --- a/udev_selinux.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2004 Daniel Walsh - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ -#ifndef _UDEV_SELINUX_H -#define _UDEV_SELINUX_H - -#ifdef USE_SELINUX - -extern void selinux_setfilecon(const char *file, const char *devname, unsigned int mode); -extern void selinux_setfscreatecon(const char *file, const char *devname, unsigned int mode); -extern void selinux_resetfscreatecon(void); -extern void selinux_init(void); -extern void selinux_exit(void); - -#else - -static inline void selinux_setfilecon(const char *file, const char *devname, unsigned int mode) {} -static inline void selinux_setfscreatecon(const char *file, const char *devname, unsigned int mode) {} -static inline void selinux_resetfscreatecon(void) {} -static inline void selinux_init(void) {} -static inline void selinux_exit(void) {} - -#endif /* USE_SELINUX */ -#endif /* _UDEV_USE_SELINUX */ diff --git a/udev_sysdeps.c b/udev_sysdeps.c deleted file mode 100644 index 9447cca936..0000000000 --- a/udev_sysdeps.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2003 Greg Kroah-Hartman - * Copyright (C) 2005-2006 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -#ifdef __GLIBC__ -size_t strlcpy(char *dst, const char *src, size_t size) -{ - size_t bytes = 0; - char *q = dst; - const char *p = src; - char ch; - - while ((ch = *p++)) { - if (bytes+1 < size) - *q++ = ch; - bytes++; - } - - /* If size == 0 there is no space for a final null... */ - if (size) - *q = '\0'; - return bytes; -} - -size_t strlcat(char *dst, const char *src, size_t size) -{ - size_t bytes = 0; - char *q = dst; - const char *p = src; - char ch; - - while (bytes < size && *q) { - q++; - bytes++; - } - if (bytes == size) - return (bytes + strlen(src)); - - while ((ch = *p++)) { - if (bytes+1 < size) - *q++ = ch; - bytes++; - } - - *q = '\0'; - return bytes; -} -#endif /* __GLIBC__ */ diff --git a/udev_sysdeps.h b/udev_sysdeps.h deleted file mode 100644 index d4f03686af..0000000000 --- a/udev_sysdeps.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * wrapping of libc features and kernel interfaces - * - * Copyright (C) 2005-2006 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#ifndef _UDEV_SYSDEPS_H_ -#define _UDEV_SYSDEPS_H_ - -#include -#include -#include - -/* needed until Inotify! syscalls reach glibc */ -#include -#ifndef __NR_inotify_init -#if defined(__i386__) -# define __NR_inotify_init 291 -# define __NR_inotify_add_watch 292 -# define __NR_inotify_rm_watch 293 -#elif defined(__x86_64__) -# define __NR_inotify_init 253 -# define __NR_inotify_add_watch 254 -# define __NR_inotify_rm_watch 255 -#elif defined(__powerpc__) || defined(__powerpc64__) -# define __NR_inotify_init 275 -# define __NR_inotify_add_watch 276 -# define __NR_inotify_rm_watch 277 -#elif defined (__ia64__) -# define __NR_inotify_init 1277 -# define __NR_inotify_add_watch 1278 -# define __NR_inotify_rm_watch 1279 -#elif defined (__s390__) -# define __NR_inotify_init 284 -# define __NR_inotify_add_watch 285 -# define __NR_inotify_rm_watch 286 -#elif defined (__alpha__) -# define __NR_inotify_init 444 -# define __NR_inotify_add_watch 445 -# define __NR_inotify_rm_watch 446 -#elif defined (__sparc__) || defined (__sparc64__) -# define __NR_inotify_init 151 -# define __NR_inotify_add_watch 152 -# define __NR_inotify_rm_watch 156 -#elif defined (__arm__) -# define __NR_inotify_init __NR_SYSCALL_BASE+316 -# define __NR_inotify_add_watch __NR_SYSCALL_BASE+317 -# define __NR_inotify_rm_watch __NR_SYSCALL_BASE+318 -#elif defined (__sh__) -# define __NR_inotify_init 290 -# define __NR_inotify_add_watch 291 -# define __NR_inotify_rm_watch 292 -#elif defined (__m32r__) -# define __NR_inotify_init 290 -# define __NR_inotify_add_watch 291 -# define __NR_inotify_rm_watch 292 -#elif defined (__hppa__) -# define __NR_inotify_init 269 -# define __NR_inotify_add_watch 270 -# define __NR_inotify_rm_watch 271 -#elif defined (__mips__) -# include -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define __NR_Linux 4000 -# define __NR_inotify_init (__NR_Linux + 284) -# define __NR_inotify_add_watch (__NR_Linux + 285) -# define __NR_inotify_rm_watch (__NR_Linux + 286) -# elif _MIPS_SIM == _MIPS_SIM_ABI64 -# define __NR_Linux 5000 -# define __NR_inotify_init (__NR_Linux + 243) -# define __NR_inotify_add_watch (__NR_Linux + 244) -# define __NR_inotify_rm_watch (__NR_Linux + 245) -# elif _MIPS_SIM == _MIPS_SIM_NABI32 -# define __NR_Linux 6000 -# define __NR_inotify_init (__NR_Linux + 247) -# define __NR_inotify_add_watch (__NR_Linux + 248) -# define __NR_inotify_rm_watch (__NR_Linux + 249) -# endif -#else -#warning "inotify unsupported on this architecture!" -#endif -#endif /* __NR_inotify_init */ - -/* dummy if we don't have the syscalls defined */ -#ifndef __NR_inotify_init -static inline int inotify_init(void) -{ - return -1; -} - -static inline int inotify_add_watch(int fd, const char *name, uint32_t mask) -{ - return -1; -} -#else -/* needed until /usr/include/sys/inotify.h is working */ -#ifndef __GLIBC__ -#include -#else -static inline int inotify_init(void) -{ - return syscall(__NR_inotify_init); -} - -static inline int inotify_add_watch(int fd, const char *name, uint32_t mask) -{ - return syscall(__NR_inotify_add_watch, fd, name, mask); -} -#endif /* __GLIBC__ */ -#endif /* __NR_inotify_init */ - -#ifndef IN_CREATE -#define IN_CREATE 0x00000100 /* Subfile was created */ -#define IN_MOVED_FROM 0x00000040 /* File was moved from X */ -#define IN_MOVED_TO 0x00000080 /* File was moved to Y */ -#define IN_DELETE 0x00000200 /* Subfile was deleted */ -#define IN_CLOSE_WRITE 0x00000008 /* Writtable file was closed */ -#define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO) /* moves */ -#endif /* IN_CREATE */ - -/* needed for our signal handlers to work */ -#undef asmlinkage -#ifdef __i386__ -#define asmlinkage __attribute__((regparm(0))) -#else -#define asmlinkage -#endif /* __i386__ */ - -/* headers are broken on some architectures */ -#ifndef __FD_SET -#define __FD_SET(d, set) ((set)->fds_bits[__FDELT(d)] |= __FDMASK(d)) -#endif -#ifndef __FD_CLR -#define __FD_CLR(d, set) ((set)->fds_bits[__FDELT(d)] &= ~__FDMASK(d)) -#endif -#ifndef __FD_ISSET -#define __FD_ISSET(d, set) (((set)->fds_bits[__FDELT(d)] & __FDMASK(d)) != 0) -#endif -#ifndef __FD_ZERO -#define __FD_ZERO(set) ((void) memset ((void*) (set), 0, sizeof (fd_set))) -#endif - -#ifndef NETLINK_KOBJECT_UEVENT -#define NETLINK_KOBJECT_UEVENT 15 -#endif - -#ifndef SO_RCVBUFFORCE -#if defined(__alpha__) || defined(__hppa__) || defined(__sparc__) || defined(__sparc_v9__) -#define SO_RCVBUFFORCE 0x100b -#else -#define SO_RCVBUFFORCE 33 -#endif -#endif - -extern size_t strlcpy(char *dst, const char *src, size_t size); -extern size_t strlcat(char *dst, const char *src, size_t size); - -#endif diff --git a/udev_sysfs.c b/udev_sysfs.c deleted file mode 100644 index c4cd4ab75d..0000000000 --- a/udev_sysfs.c +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Copyright (C) 2005-2006 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -char sysfs_path[PATH_SIZE]; - -/* device cache */ -static LIST_HEAD(dev_list); - -/* attribute value cache */ -static LIST_HEAD(attr_list); -struct sysfs_attr { - struct list_head node; - char path[PATH_SIZE]; - char *value; /* points to value_local if value is cached */ - char value_local[NAME_SIZE]; -}; - -int sysfs_init(void) -{ - const char *env; - - env = getenv("SYSFS_PATH"); - if (env) { - strlcpy(sysfs_path, env, sizeof(sysfs_path)); - remove_trailing_chars(sysfs_path, '/'); - } else - strlcpy(sysfs_path, "/sys", sizeof(sysfs_path)); - dbg("sysfs_path='%s'\n", sysfs_path); - - INIT_LIST_HEAD(&dev_list); - INIT_LIST_HEAD(&attr_list); - return 0; -} - -void sysfs_cleanup(void) -{ - struct sysfs_attr *attr_loop; - struct sysfs_attr *attr_temp; - struct sysfs_device *dev_loop; - struct sysfs_device *dev_temp; - - list_for_each_entry_safe(attr_loop, attr_temp, &attr_list, node) { - list_del(&attr_loop->node); - free(attr_loop); - } - - list_for_each_entry_safe(dev_loop, dev_temp, &dev_list, node) { - list_del(&dev_loop->node); - free(dev_loop); - } -} - -void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, - const char *subsystem, const char *driver) -{ - char *pos; - - strlcpy(dev->devpath, devpath, sizeof(dev->devpath)); - if (subsystem != NULL) - strlcpy(dev->subsystem, subsystem, sizeof(dev->subsystem)); - if (driver != NULL) - strlcpy(dev->driver, driver, sizeof(dev->driver)); - - /* set kernel name */ - pos = strrchr(dev->devpath, '/'); - if (pos == NULL) - return; - strlcpy(dev->kernel, &pos[1], sizeof(dev->kernel)); - dbg("kernel='%s'\n", dev->kernel); - - /* some devices have '!' in their name, change that to '/' */ - pos = dev->kernel; - while (pos[0] != '\0') { - if (pos[0] == '!') - pos[0] = '/'; - pos++; - } - - /* get kernel number */ - pos = &dev->kernel[strlen(dev->kernel)]; - while (isdigit(pos[-1])) - pos--; - strlcpy(dev->kernel_number, pos, sizeof(dev->kernel_number)); - dbg("kernel_number='%s'\n", dev->kernel_number); -} - -int sysfs_resolve_link(char *devpath, size_t size) -{ - char link_path[PATH_SIZE]; - char link_target[PATH_SIZE]; - int len; - int i; - int back; - - strlcpy(link_path, sysfs_path, sizeof(link_path)); - strlcat(link_path, devpath, sizeof(link_path)); - len = readlink(link_path, link_target, sizeof(link_target)); - if (len <= 0) - return -1; - link_target[len] = '\0'; - dbg("path link '%s' points to '%s'\n", devpath, link_target); - - for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++) - ; - dbg("base '%s', tail '%s', back %i\n", devpath, &link_target[back * 3], back); - for (i = 0; i <= back; i++) { - char *pos = strrchr(devpath, '/'); - - if (pos == NULL) - return -1; - pos[0] = '\0'; - } - dbg("after moving back '%s'\n", devpath); - strlcat(devpath, "/", size); - strlcat(devpath, &link_target[back * 3], size); - return 0; -} - -struct sysfs_device *sysfs_device_get(const char *devpath) -{ - char path[PATH_SIZE]; - char devpath_real[PATH_SIZE]; - struct sysfs_device *dev; - struct sysfs_device *dev_loop; - struct stat statbuf; - char link_path[PATH_SIZE]; - char link_target[PATH_SIZE]; - int len; - char *pos; - - /* we handle only these devpathes */ - if (devpath != NULL && - strncmp(devpath, "/devices/", 9) != 0 && - strncmp(devpath, "/subsystem/", 11) != 0 && - strncmp(devpath, "/module/", 8) != 0 && - strncmp(devpath, "/bus/", 5) != 0 && - strncmp(devpath, "/class/", 7) != 0 && - strncmp(devpath, "/block/", 7) != 0) - return NULL; - - dbg("open '%s'\n", devpath); - strlcpy(devpath_real, devpath, sizeof(devpath_real)); - remove_trailing_chars(devpath_real, '/'); - if (devpath[0] == '\0' ) - return NULL; - - /* look for device already in cache (we never put an untranslated path in the cache) */ - list_for_each_entry(dev_loop, &dev_list, node) { - if (strcmp(dev_loop->devpath, devpath_real) == 0) { - dbg("found in cache '%s'\n", dev_loop->devpath); - return dev_loop; - } - } - - /* if we got a link, resolve it to the real device */ - strlcpy(path, sysfs_path, sizeof(path)); - strlcat(path, devpath_real, sizeof(path)); - if (lstat(path, &statbuf) != 0) { - dbg("stat '%s' failed: %s\n", path, strerror(errno)); - return NULL; - } - if (S_ISLNK(statbuf.st_mode)) { - if (sysfs_resolve_link(devpath_real, sizeof(devpath_real)) != 0) - return NULL; - - /* now look for device in cache after path translation */ - list_for_each_entry(dev_loop, &dev_list, node) { - if (strcmp(dev_loop->devpath, devpath_real) == 0) { - dbg("found in cache '%s'\n", dev_loop->devpath); - return dev_loop; - } - } - } - - /* it is a new device */ - dbg("new uncached device '%s'\n", devpath_real); - dev = malloc(sizeof(struct sysfs_device)); - if (dev == NULL) - return NULL; - memset(dev, 0x00, sizeof(struct sysfs_device)); - - sysfs_device_set_values(dev, devpath_real, NULL, NULL); - - /* get subsystem name */ - strlcpy(link_path, sysfs_path, sizeof(link_path)); - strlcat(link_path, dev->devpath, sizeof(link_path)); - strlcat(link_path, "/subsystem", sizeof(link_path)); - len = readlink(link_path, link_target, sizeof(link_target)); - if (len > 0) { - /* get subsystem from "subsystem" link */ - link_target[len] = '\0'; - dbg("subsystem link '%s' points to '%s'\n", link_path, link_target); - pos = strrchr(link_target, '/'); - if (pos != NULL) - strlcpy(dev->subsystem, &pos[1], sizeof(dev->subsystem)); - } else if (strstr(dev->devpath, "/drivers/") != NULL) { - strlcpy(dev->subsystem, "drivers", sizeof(dev->subsystem)); - } else if (strncmp(dev->devpath, "/module/", 8) == 0) { - strlcpy(dev->subsystem, "module", sizeof(dev->subsystem)); - } else if (strncmp(dev->devpath, "/subsystem/", 11) == 0) { - pos = strrchr(dev->devpath, '/'); - if (pos == &dev->devpath[10]) - strlcpy(dev->subsystem, "subsystem", sizeof(dev->subsystem)); - } else if (strncmp(dev->devpath, "/class/", 7) == 0) { - pos = strrchr(dev->devpath, '/'); - if (pos == &dev->devpath[6]) - strlcpy(dev->subsystem, "subsystem", sizeof(dev->subsystem)); - } else if (strncmp(dev->devpath, "/bus/", 5) == 0) { - pos = strrchr(dev->devpath, '/'); - if (pos == &dev->devpath[4]) - strlcpy(dev->subsystem, "subsystem", sizeof(dev->subsystem)); - } - - /* get driver name */ - strlcpy(link_path, sysfs_path, sizeof(link_path)); - strlcat(link_path, dev->devpath, sizeof(link_path)); - strlcat(link_path, "/driver", sizeof(link_path)); - len = readlink(link_path, link_target, sizeof(link_target)); - if (len > 0) { - link_target[len] = '\0'; - dbg("driver link '%s' points to '%s'\n", link_path, link_target); - pos = strrchr(link_target, '/'); - if (pos != NULL) - strlcpy(dev->driver, &pos[1], sizeof(dev->driver)); - } - - dbg("add to cache 'devpath=%s', subsystem='%s', driver='%s'\n", dev->devpath, dev->subsystem, dev->driver); - list_add(&dev->node, &dev_list); - - return dev; -} - -struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev) -{ - char parent_devpath[PATH_SIZE]; - char *pos; - - dbg("open '%s'\n", dev->devpath); - - /* look if we already know the parent */ - if (dev->parent != NULL) - return dev->parent; - - strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath)); - dbg("'%s'\n", parent_devpath); - - /* strip last element */ - pos = strrchr(parent_devpath, '/'); - if (pos == NULL || pos == parent_devpath) - return NULL; - pos[0] = '\0'; - - if (strncmp(parent_devpath, "/class", 6) == 0) { - pos = strrchr(parent_devpath, '/'); - if (pos == &parent_devpath[6] || pos == parent_devpath) { - dbg("/class top level, look for device link\n"); - goto device_link; - } - } - if (strcmp(parent_devpath, "/block") == 0) { - dbg("/block top level, look for device link\n"); - goto device_link; - } - - /* are we at the top level? */ - pos = strrchr(parent_devpath, '/'); - if (pos == NULL || pos == parent_devpath) - return NULL; - - /* get parent and remember it */ - dev->parent = sysfs_device_get(parent_devpath); - return dev->parent; - -device_link: - strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath)); - strlcat(parent_devpath, "/device", sizeof(parent_devpath)); - if (sysfs_resolve_link(parent_devpath, sizeof(parent_devpath)) != 0) - return NULL; - - /* get parent and remember it */ - dev->parent = sysfs_device_get(parent_devpath); - return dev->parent; -} - -struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem) -{ - struct sysfs_device *dev_parent; - - dev_parent = sysfs_device_get_parent(dev); - while (dev_parent != NULL) { - if (strcmp(dev_parent->subsystem, subsystem) == 0) - return dev_parent; - dev_parent = sysfs_device_get_parent(dev_parent); - } - return NULL; -} - -char *sysfs_attr_get_value(const char *devpath, const char *attr_name) -{ - char path_full[PATH_SIZE]; - const char *path; - char value[NAME_SIZE]; - struct sysfs_attr *attr_loop; - struct sysfs_attr *attr; - struct stat statbuf; - int fd; - ssize_t size; - size_t sysfs_len; - - dbg("open '%s'/'%s'\n", devpath, attr_name); - sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full)); - if(sysfs_len >= sizeof(path_full)) - sysfs_len = sizeof(path_full) - 1; - path = &path_full[sysfs_len]; - strlcat(path_full, devpath, sizeof(path_full)); - strlcat(path_full, "/", sizeof(path_full)); - strlcat(path_full, attr_name, sizeof(path_full)); - - /* look for attribute in cache */ - list_for_each_entry(attr_loop, &attr_list, node) { - if (strcmp(attr_loop->path, path) == 0) { - dbg("found in cache '%s'\n", attr_loop->path); - return attr_loop->value; - } - } - - /* store attribute in cache (also negatives are kept in cache) */ - dbg("new uncached attribute '%s'\n", path_full); - attr = malloc(sizeof(struct sysfs_attr)); - if (attr == NULL) - return NULL; - memset(attr, 0x00, sizeof(struct sysfs_attr)); - strlcpy(attr->path, path, sizeof(attr->path)); - dbg("add to cache '%s'\n", path_full); - list_add(&attr->node, &attr_list); - - if (lstat(path_full, &statbuf) != 0) { - dbg("stat '%s' failed: %s\n", path_full, strerror(errno)); - goto out; - } - - if (S_ISLNK(statbuf.st_mode)) { - /* links return the last element of the target path */ - char link_target[PATH_SIZE]; - int len; - const char *pos; - - len = readlink(path_full, link_target, sizeof(link_target)); - if (len > 0) { - link_target[len] = '\0'; - pos = strrchr(link_target, '/'); - if (pos != NULL) { - dbg("cache '%s' with link value '%s'\n", path_full, value); - strlcpy(attr->value_local, &pos[1], sizeof(attr->value_local)); - attr->value = attr->value_local; - } - } - goto out; - } - - /* skip directories */ - if (S_ISDIR(statbuf.st_mode)) - goto out; - - /* skip non-readable files */ - if ((statbuf.st_mode & S_IRUSR) == 0) - goto out; - - /* read attribute value */ - fd = open(path_full, O_RDONLY); - if (fd < 0) { - dbg("attribute '%s' can not be opened\n", path_full); - goto out; - } - size = read(fd, value, sizeof(value)); - close(fd); - if (size < 0) - goto out; - if (size == sizeof(value)) - goto out; - - /* got a valid value, store and return it */ - value[size] = '\0'; - remove_trailing_chars(value, '\n'); - dbg("cache '%s' with attribute value '%s'\n", path_full, value); - strlcpy(attr->value_local, value, sizeof(attr->value_local)); - attr->value = attr->value_local; - -out: - return attr->value; -} - -int sysfs_lookup_devpath_by_subsys_id(char *devpath_full, size_t len, const char *subsystem, const char *id) -{ - size_t sysfs_len; - char path_full[PATH_SIZE]; - char *path; - struct stat statbuf; - - sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full)); - path = &path_full[sysfs_len]; - - if (strcmp(subsystem, "subsystem") == 0) { - strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len); - strlcat(path, id, sizeof(path_full) - sysfs_len); - if (stat(path_full, &statbuf) == 0) - goto found; - - strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len); - strlcat(path, id, sizeof(path_full) - sysfs_len); - if (stat(path_full, &statbuf) == 0) - goto found; - goto out; - - strlcpy(path, "/class/", sizeof(path_full) - sysfs_len); - strlcat(path, id, sizeof(path_full) - sysfs_len); - if (stat(path_full, &statbuf) == 0) - goto found; - } - - if (strcmp(subsystem, "module") == 0) { - strlcpy(path, "/module/", sizeof(path_full) - sysfs_len); - strlcat(path, id, sizeof(path_full) - sysfs_len); - if (stat(path_full, &statbuf) == 0) - goto found; - goto out; - } - - if (strcmp(subsystem, "drivers") == 0) { - char subsys[NAME_SIZE]; - char *driver; - - strlcpy(subsys, id, sizeof(subsys)); - driver = strchr(subsys, ':'); - if (driver != NULL) { - driver[0] = '\0'; - driver = &driver[1]; - strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len); - strlcat(path, subsys, sizeof(path_full) - sysfs_len); - strlcat(path, "/drivers/", sizeof(path_full) - sysfs_len); - strlcat(path, driver, sizeof(path_full) - sysfs_len); - if (stat(path_full, &statbuf) == 0) - goto found; - - strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len); - strlcat(path, subsys, sizeof(path_full) - sysfs_len); - strlcat(path, "/drivers/", sizeof(path_full) - sysfs_len); - strlcat(path, driver, sizeof(path_full) - sysfs_len); - if (stat(path_full, &statbuf) == 0) - goto found; - } - goto out; - } - - strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len); - strlcat(path, subsystem, sizeof(path_full) - sysfs_len); - strlcat(path, "/devices/", sizeof(path_full) - sysfs_len); - strlcat(path, id, sizeof(path_full) - sysfs_len); - if (stat(path_full, &statbuf) == 0) - goto found; - - strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len); - strlcat(path, subsystem, sizeof(path_full) - sysfs_len); - strlcat(path, "/devices/", sizeof(path_full) - sysfs_len); - strlcat(path, id, sizeof(path_full) - sysfs_len); - if (stat(path_full, &statbuf) == 0) - goto found; - - strlcpy(path, "/class/", sizeof(path_full) - sysfs_len); - strlcat(path, subsystem, sizeof(path_full) - sysfs_len); - strlcat(path, "/", sizeof(path_full) - sysfs_len); - strlcat(path, id, sizeof(path_full) - sysfs_len); - if (stat(path_full, &statbuf) == 0) - goto found; -out: - return 0; -found: - if (S_ISLNK(statbuf.st_mode)) - sysfs_resolve_link(path, sizeof(path_full) - sysfs_len); - strlcpy(devpath_full, path, len); - return 1; -} diff --git a/udev_utils.c b/udev_utils.c deleted file mode 100644 index 00b67dadc6..0000000000 --- a/udev_utils.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (C) 2004-2005 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - - -int log_priority(const char *priority) -{ - char *endptr; - int prio; - - prio = strtol(priority, &endptr, 10); - if (endptr[0] == '\0') - return prio; - if (strncasecmp(priority, "err", 3) == 0) - return LOG_ERR; - if (strcasecmp(priority, "info") == 0) - return LOG_INFO; - if (strcasecmp(priority, "debug") == 0) - return LOG_DEBUG; - if (string_is_true(priority)) - return LOG_ERR; - - return 0; -} - -struct name_entry *name_list_add(struct list_head *name_list, const char *name, int sort) -{ - struct name_entry *name_loop; - struct name_entry *name_new; - - /* avoid duplicate entries */ - list_for_each_entry(name_loop, name_list, node) { - if (strcmp(name_loop->name, name) == 0) { - dbg("'%s' is already in the list\n", name); - return name_loop; - } - } - - if (sort) - list_for_each_entry(name_loop, name_list, node) { - if (strcmp(name_loop->name, name) > 0) - break; - } - - name_new = malloc(sizeof(struct name_entry)); - if (name_new == NULL) - return NULL; - - strlcpy(name_new->name, name, sizeof(name_new->name)); - dbg("adding '%s'\n", name_new->name); - list_add_tail(&name_new->node, &name_loop->node); - - return name_new; -} - -struct name_entry *name_list_key_add(struct list_head *name_list, const char *key, const char *value) -{ - struct name_entry *name_loop; - struct name_entry *name_new; - - list_for_each_entry(name_loop, name_list, node) { - if (strncmp(name_loop->name, key, strlen(key)) == 0) { - dbg("key already present '%s', replace it\n", name_loop->name); - snprintf(name_loop->name, sizeof(name_loop->name), "%s=%s", key, value); - name_loop->name[sizeof(name_loop->name)-1] = '\0'; - return name_loop; - } - } - - name_new = malloc(sizeof(struct name_entry)); - if (name_new == NULL) - return NULL; - - snprintf(name_new->name, sizeof(name_new->name), "%s=%s", key, value); - name_new->name[sizeof(name_new->name)-1] = '\0'; - dbg("adding '%s'\n", name_new->name); - list_add_tail(&name_new->node, &name_loop->node); - - return name_new; -} - -int name_list_key_remove(struct list_head *name_list, const char *key) -{ - struct name_entry *name_loop; - struct name_entry *name_tmp; - size_t keylen = strlen(key); - int retval = 0; - - list_for_each_entry_safe(name_loop, name_tmp, name_list, node) { - if (strncmp(name_loop->name, key, keylen) != 0) - continue; - if (name_loop->name[keylen] != '=') - continue; - list_del(&name_loop->node); - free(name_loop); - retval = 1; - break; - } - return retval; -} - -void name_list_cleanup(struct list_head *name_list) -{ - struct name_entry *name_loop; - struct name_entry *name_tmp; - - list_for_each_entry_safe(name_loop, name_tmp, name_list, node) { - list_del(&name_loop->node); - free(name_loop); - } -} - -/* calls function for every file found in specified directory */ -int add_matching_files(struct list_head *name_list, const char *dirname, const char *suffix) -{ - struct dirent *ent; - DIR *dir; - char filename[PATH_SIZE]; - - dbg("open directory '%s'\n", dirname); - dir = opendir(dirname); - if (dir == NULL) { - err("unable to open '%s': %s\n", dirname, strerror(errno)); - return -1; - } - - while (1) { - ent = readdir(dir); - if (ent == NULL || ent->d_name[0] == '\0') - break; - - if ((ent->d_name[0] == '.') || (ent->d_name[0] == COMMENT_CHARACTER)) - continue; - - /* look for file matching with specified suffix */ - if (suffix != NULL) { - const char *ext; - - ext = strrchr(ent->d_name, '.'); - if (ext == NULL) - continue; - if (strcmp(ext, suffix) != 0) - continue; - } - dbg("put file '%s/%s' into list\n", dirname, ent->d_name); - - snprintf(filename, sizeof(filename), "%s/%s", dirname, ent->d_name); - filename[sizeof(filename)-1] = '\0'; - name_list_add(name_list, filename, 1); - } - - closedir(dir); - return 0; -} - -uid_t lookup_user(const char *user) -{ - struct passwd *pw; - uid_t uid = 0; - - errno = 0; - pw = getpwnam(user); - if (pw == NULL) { - if (errno == 0 || errno == ENOENT || errno == ESRCH) - err("specified user '%s' unknown\n", user); - else - err("error resolving user '%s': %s\n", user, strerror(errno)); - } else - uid = pw->pw_uid; - - return uid; -} - -extern gid_t lookup_group(const char *group) -{ - struct group *gr; - gid_t gid = 0; - - errno = 0; - gr = getgrnam(group); - if (gr == NULL) { - if (errno == 0 || errno == ENOENT || errno == ESRCH) - err("specified group '%s' unknown\n", group); - else - err("error resolving group '%s': %s\n", group, strerror(errno)); - } else - gid = gr->gr_gid; - - return gid; -} - diff --git a/udev_utils_file.c b/udev_utils_file.c deleted file mode 100644 index a492785af9..0000000000 --- a/udev_utils_file.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2004-2005 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udev_selinux.h" - -int create_path(const char *path) -{ - char p[PATH_SIZE]; - char *pos; - struct stat stats; - int ret; - - strlcpy(p, path, sizeof(p)); - pos = strrchr(p, '/'); - if (pos == p || pos == NULL) - return 0; - - while (pos[-1] == '/') - pos--; - pos[0] = '\0'; - - dbg("stat '%s'\n", p); - if (stat(p, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR) - return 0; - - if (create_path(p) != 0) - return -1; - - dbg("mkdir '%s'\n", p); - selinux_setfscreatecon(p, NULL, S_IFDIR|0755); - ret = mkdir(p, 0755); - selinux_resetfscreatecon(); - if (ret == 0) - return 0; - - if (errno == EEXIST) - if (stat(p, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR) - return 0; - return -1; -} - -int delete_path(const char *path) -{ - char p[PATH_SIZE]; - char *pos; - int retval; - - strcpy (p, path); - pos = strrchr(p, '/'); - if (pos == p || pos == NULL) - return 0; - - while (1) { - *pos = '\0'; - pos = strrchr(p, '/'); - - /* don't remove the last one */ - if ((pos == p) || (pos == NULL)) - break; - - /* remove if empty */ - retval = rmdir(p); - if (errno == ENOENT) - retval = 0; - if (retval) { - if (errno == ENOTEMPTY) - return 0; - err("rmdir(%s) failed: %s\n", p, strerror(errno)); - break; - } - dbg("removed '%s'\n", p); - } - return 0; -} - -/* Reset permissions on the device node, before unlinking it to make sure, - * that permisions of possible hard links will be removed too. - */ -int unlink_secure(const char *filename) -{ - int retval; - - retval = chown(filename, 0, 0); - if (retval) - err("chown(%s, 0, 0) failed: %s\n", filename, strerror(errno)); - - retval = chmod(filename, 0000); - if (retval) - err("chmod(%s, 0000) failed: %s\n", filename, strerror(errno)); - - retval = unlink(filename); - if (errno == ENOENT) - retval = 0; - - if (retval) - err("unlink(%s) failed: %s\n", filename, strerror(errno)); - - return retval; -} - -int file_map(const char *filename, char **buf, size_t *bufsize) -{ - struct stat stats; - int fd; - - fd = open(filename, O_RDONLY); - if (fd < 0) { - return -1; - } - - if (fstat(fd, &stats) < 0) { - close(fd); - return -1; - } - - *buf = mmap(NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0); - if (*buf == MAP_FAILED) { - close(fd); - return -1; - } - *bufsize = stats.st_size; - - close(fd); - - return 0; -} - -void file_unmap(void *buf, size_t bufsize) -{ - munmap(buf, bufsize); -} - -/* return number of chars until the next newline, skip escaped newline */ -size_t buf_get_line(const char *buf, size_t buflen, size_t cur) -{ - int escape = 0; - size_t count; - - for (count = cur; count < buflen; count++) { - if (!escape && buf[count] == '\n') - break; - - if (buf[count] == '\\') - escape = 1; - else - escape = 0; - } - - return count - cur; -} diff --git a/udev_utils_string.c b/udev_utils_string.c deleted file mode 100644 index e3dc137e63..0000000000 --- a/udev_utils_string.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (C) 2004-2005 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -int string_is_true(const char *str) -{ - if (strcasecmp(str, "true") == 0) - return 1; - if (strcasecmp(str, "yes") == 0) - return 1; - if (strcasecmp(str, "1") == 0) - return 1; - return 0; -} - -void remove_trailing_chars(char *path, char c) -{ - size_t len; - - len = strlen(path); - while (len > 0 && path[len-1] == c) - path[--len] = '\0'; -} - -size_t path_encode(char *s, size_t len) -{ - char t[(len * 3)+1]; - size_t i, j; - - t[0] = '\0'; - for (i = 0, j = 0; s[i] != '\0'; i++) { - if (s[i] == '/') { - memcpy(&t[j], "\\x2f", 4); - j += 4; - } else if (s[i] == '\\') { - memcpy(&t[j], "\\x5c", 4); - j += 4; - } else { - t[j] = s[i]; - j++; - } - } - t[j] = '\0'; - strncpy(s, t, len); - return j; -} - -size_t path_decode(char *s) -{ - size_t i, j; - - for (i = 0, j = 0; s[i] != '\0'; j++) { - if (memcmp(&s[i], "\\x2f", 4) == 0) { - s[j] = '/'; - i += 4; - }else if (memcmp(&s[i], "\\x5c", 4) == 0) { - s[j] = '\\'; - i += 4; - } else { - s[j] = s[i]; - i++; - } - } - s[j] = '\0'; - return j; -} - -/* count of characters used to encode one unicode char */ -static int utf8_encoded_expected_len(const char *str) -{ - unsigned char c = (unsigned char)str[0]; - - if (c < 0x80) - return 1; - if ((c & 0xe0) == 0xc0) - return 2; - if ((c & 0xf0) == 0xe0) - return 3; - if ((c & 0xf8) == 0xf0) - return 4; - if ((c & 0xfc) == 0xf8) - return 5; - if ((c & 0xfe) == 0xfc) - return 6; - return 0; -} - -/* decode one unicode char */ -static int utf8_encoded_to_unichar(const char *str) -{ - int unichar; - int len; - int i; - - len = utf8_encoded_expected_len(str); - switch (len) { - case 1: - return (int)str[0]; - case 2: - unichar = str[0] & 0x1f; - break; - case 3: - unichar = (int)str[0] & 0x0f; - break; - case 4: - unichar = (int)str[0] & 0x07; - break; - case 5: - unichar = (int)str[0] & 0x03; - break; - case 6: - unichar = (int)str[0] & 0x01; - break; - default: - return -1; - } - - for (i = 1; i < len; i++) { - if (((int)str[i] & 0xc0) != 0x80) - return -1; - unichar <<= 6; - unichar |= (int)str[i] & 0x3f; - } - - return unichar; -} - -/* expected size used to encode one unicode char */ -static int utf8_unichar_to_encoded_len(int unichar) -{ - if (unichar < 0x80) - return 1; - if (unichar < 0x800) - return 2; - if (unichar < 0x10000) - return 3; - if (unichar < 0x200000) - return 4; - if (unichar < 0x4000000) - return 5; - return 6; -} - -/* check if unicode char has a valid numeric range */ -static int utf8_unichar_valid_range(int unichar) -{ - if (unichar > 0x10ffff) - return 0; - if ((unichar & 0xfffff800) == 0xd800) - return 0; - if ((unichar > 0xfdcf) && (unichar < 0xfdf0)) - return 0; - if ((unichar & 0xffff) == 0xffff) - return 0; - return 1; -} - -/* validate one encoded unicode char and return its length */ -int utf8_encoded_valid_unichar(const char *str) -{ - int len; - int unichar; - int i; - - len = utf8_encoded_expected_len(str); - if (len == 0) - return -1; - - /* ascii is valid */ - if (len == 1) - return 1; - - /* check if expected encoded chars are available */ - for (i = 0; i < len; i++) - if ((str[i] & 0x80) != 0x80) - return -1; - - unichar = utf8_encoded_to_unichar(str); - - /* check if encoded length matches encoded value */ - if (utf8_unichar_to_encoded_len(unichar) != len) - return -1; - - /* check if value has valid range */ - if (!utf8_unichar_valid_range(unichar)) - return -1; - - return len; -} - -/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */ -int replace_chars(char *str, const char *white) -{ - size_t i = 0; - int replaced = 0; - - while (str[i] != '\0') { - int len; - - /* accept whitelist */ - if (white != NULL && strchr(white, str[i]) != NULL) { - i++; - continue; - } - - /* accept plain ascii char */ - if ((str[i] >= '0' && str[i] <= '9') || - (str[i] >= 'A' && str[i] <= 'Z') || - (str[i] >= 'a' && str[i] <= 'z')) { - i++; - continue; - } - - /* accept hex encoding */ - if (str[i] == '\\' && str[i+1] == 'x') { - i += 2; - continue; - } - - /* accept valid utf8 */ - len = utf8_encoded_valid_unichar(&str[i]); - if (len > 1) { - i += len; - continue; - } - - /* if space is allowed, replace whitespace with ordinary space */ - if (isspace(str[i]) && strchr(white, ' ') != NULL) { - str[i] = ' '; - i++; - replaced++; - continue; - } - - /* everything else is replaced with '_' */ - str[i] = '_'; - i++; - replaced++; - } - - return replaced; -} diff --git a/udevadm.8 b/udevadm.8 deleted file mode 100644 index 65e85e6bc0..0000000000 --- a/udevadm.8 +++ /dev/null @@ -1,272 +0,0 @@ -.\" Title: udevadm -.\" Author: -.\" Generator: DocBook XSL Stylesheets v1.73.2 -.\" Date: November 2007 -.\" Manual: udevadm -.\" Source: udev -.\" -.TH "UDEVADM" "8" "November 2007" "udev" "udevadm" -.\" disable hyphenation -.nh -.\" disable justification (adjust text to left margin only) -.ad l -.SH "NAME" -udevadm - udev management tool -.SH "SYNOPSIS" -.HP 21 -\fBudevadm info \fR\fB[options]\fR -.HP 24 -\fBudevadm trigger \fR\fB[options]\fR -.HP 23 -\fBudevadm settle \fR\fB[options]\fR -.HP 36 -\fBudevadm control \fR\fB[options]\fR\fB \fR\fB\fIinstruction\fR\fR -.HP 24 -\fBudevadm monitor \fR\fB[options]\fR -.HP 29 -\fBudevadm test \fR\fB[options]\fR\fB \fR\fB\fIdevpath\fR\fR -.HP 16 -\fBudevadm version\fR -.HP 13 -\fBudevadm help\fR -.SH "DESCRIPTION" -.PP -udevadm expects a command and command specific options\. It controls the runtime behavior of udev, requests kernel events, manages the event queue, and provides simple debugging mechanisms\. -.SH "OPTIONS" -.SS "udevadm info \fIoptions\fR" -.PP -Queries the udev database for device information stored in the udev database\. It can also query the properties of a device from its sysfs representation to help creating udev rules that match this device\. -.PP -\fB\-\-query=\fR\fB\fItype\fR\fR -.RS 4 -Query the database for specified type of device data\. It needs the -\fB\-\-path\fR -or -\fB\-\-name\fR -to identify the specified device\. Valid queries are: -\fBname\fR, -\fBsymlink\fR, -\fBpath\fR, -\fBenv\fR, -\fBall\fR\. -.RE -.PP -\fB\-\-path=\fR\fB\fIdevpath\fR\fR -.RS 4 -The devpath of the device to query\. -.RE -.PP -\fB\-\-name=\fR\fB\fIfile\fR\fR -.RS 4 -The name of the device node or a symlink to query -.RE -.PP -\fB\-\-root\fR -.RS 4 -The udev root directory: -\fI/dev\fR\. If used in conjunction with a -\fBname\fR -or -\fBsymlink\fR -query, the query returns the absolute path including the root directory\. -.RE -.PP -\fB\-\-attribute\-walk\fR -.RS 4 -Print all sysfs properties of the specified device that can be used in udev rules to match the specified device\. It prints all devices along the chain, up to the root of sysfs that can be used in udev rules\. -.RE -.PP -\fB\-\-device\-id\-of\-file=\fR\fB\fIfile\fR\fR -.RS 4 -Print major/minor numbers of the underlying device, where the file lives on\. -.RE -.PP -\fB\-\-export\-db\fR -.RS 4 -Export the content of the udev database\. -.RE -.PP -\fB\-\-version\fR -.RS 4 -Print version\. -.RE -.PP -\fB\-\-help\fR -.RS 4 -Print help text\. -.RE -.SS "udevadm trigger [options]" -.PP -Request device uevents, usually used to replay events at system coldplug\. -.PP -\fB\-\-verbose\fR -.RS 4 -Print the list of devices which will be triggered\. -.RE -.PP -\fB\-\-dry\-run\fR -.RS 4 -Do not actually trigger the event\. -.RE -.PP -\fB\-\-retry\-failed\fR -.RS 4 -Trigger only the events which are failed during a previous run\. -.RE -.PP -\fB\-\-action=\fR\fB\fIaction\fR\fR -.RS 4 -Type of event to be triggered\. The default value is "add"\. -.RE -.PP -\fB\-\-subsystem\-match=\fR\fB\fIsubsystem\fR\fR -.RS 4 -Trigger events for devices which belong to a matching subsystem\. This option can be specified multiple times and supports shell style pattern matching\. -.RE -.PP -\fB\-\-subsystem\-nomatch=\fR\fB\fIsubsystem\fR\fR -.RS 4 -Do not trigger events for devices which belong to a matching subsystem\. This option can be specified multiple times and supports shell style pattern matching\. -.RE -.PP -\fB\-\-attr\-match=\fR\fB\fIattribute\fR\fR\fB=\fR\fB\fIvalue\fR\fR -.RS 4 -Trigger events for devices with a matching sysfs attribute\. If a value is specified along with the attribute name, the content of the attribute is matched against the given value using shell style pattern matching\. If no value is specified, the existence of the sysfs attribute is checked\. This option can be specified multiple times\. -.RE -.PP -\fB\-\-attr\-nomatch=\fR\fB\fIattribute\fR\fR\fB=\fR\fB\fIvalue\fR\fR -.RS 4 -Do not trigger events for devices with a matching sysfs attribute\. If a value is specified along with the attribute name, the content of the attribute is matched against the given value using shell style pattern matching\. If no value is specified, the existence of the sysfs attribute is checked\. This option can be specified multiple times\. -.RE -.PP -\fB\-\-socket=\fR\fB\fIpath\fR\fR -.RS 4 -Pass the synthesized events to the specified socket, instead of triggering a global kernel event\. All available event values will be send in the same format the kernel sends an uevent, or -\fBRUN+="socket:\fR\fB\fIpath\fR\fR\fB"\fR -sends a message\. If the first character of the specified path is an @ character, an abstract namespace socket is used, instead of an existing socket file\. -.RE -.PP -\fB\-\-env=\fR\fB\fIKEY\fR\fR\fB=\fR\fB\fIvalue\fR\fR -.RS 4 -Pass an additional environemt key to the event\. This works only with the \-\-socket option\. -.RE -.SS "udevadm settle [options]" -.PP -Watches the udev event queue, and exits if all current events are handled\. -.PP -\fB\-\-timeout=\fR\fB\fIseconds\fR\fR -.RS 4 -Maximum number of seconds to wait for the event queue to become empty\. The default value is 180 seconds\. -.RE -.PP -\fB\-\-help\fR -.RS 4 -Print help text\. -.RE -.SS "udevadm control \fIcommand\fR" -.PP -Modify the internal state of the running udev daemon\. -.PP -\fB\-\-log_priority=\fR\fB\fIvalue\fR\fR -.RS 4 -Set the internal log level of udevd\. Valid values are the numerical syslog priorities or their textual representations: -\fBerr\fR, -\fBinfo\fR -and -\fBdebug\fR\. -.RE -.PP -\fB\-\-stop_exec_queue\fR -.RS 4 -Signal udevd to stop executing new events\. Incoming events will be queued\. -.RE -.PP -\fB\-\-start_exec_queue\fR -.RS 4 -Signal udevd to enable the execution of events\. -.RE -.PP -\fB\-\-reload_rules\fR -.RS 4 -Signal udevd to reload the rules from the config\. -.RE -.PP -\fB\-\-env=\fR\fB\fIKEY\fR\fR\fB=\fR\fB\fIvalue\fR\fR -.RS 4 -Set global variable\. -.RE -.PP -\fB\-\-max_childs=\fR\fIvalue\fR -.RS 4 -Set the maximum number of events, udevd will handle at the same time\. -.RE -.PP -\fB\-\-max_childs_running=\fR\fB\fIvalue\fR\fR -.RS 4 -Set the maximum number of events, which are allowed to run at the same time\. -.RE -.PP -\fB\-\-help\fR -.RS 4 -Print help text\. -.RE -.SS "udevadm monitor [options]" -.PP -Listens to the kernel uevents and events sent out by a udev rule and prints the devpath of the event to the console\. It can be used to analyze the event timing, by comparing the timestamps of the kernel uevent and the udev event\. -.PP -\fB\-\-environment\fR -.RS 4 -Print the complete environment for all events\. Can be used to compare the kernel supplied and the udev added environment values\. -.RE -.PP -\fB\-\-kernel\fR -.RS 4 -Print the kernel uevents\. -.RE -.PP -\fB\-\-udev\fR -.RS 4 -Print the udev event after the rule processing\. -.RE -.PP -\fB\-\-help\fR -.RS 4 -Print help text\. -.RE -.SS "udevadm test [options] \fIdevpath\fR" -.PP -Simulate a udev event run for the given device, and print out debug output\. Unless forced to, no device node or symlink will be created\. -.PP -\fB\-\-action=\fR\fB\fIstring\fR\fR -.RS 4 -The action string\. -.RE -.PP -\fB\-\-subsystem=\fR\fB\fIstring\fR\fR -.RS 4 -The subsystem string\. -.RE -.PP -\fB\-\-force\fR -.RS 4 -Force the creation of a device node or symlink\. Usually the test run prints only debug output\. -.RE -.PP -\fB\-\-help\fR -.RS 4 -Print help text\. -.RE -.SS "udevadm version" -.PP -Print version number\. -.SS "udevadm help" -.PP -Print help text\. -.SH "AUTHOR" -.PP -Written by Kay Sievers -\. -.SH "SEE ALSO" -.PP -\fBudev\fR(7) -\fBudevd\fR(8) diff --git a/udevadm.c b/udevadm.c deleted file mode 100644 index 6e7d7734d2..0000000000 --- a/udevadm.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2007 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static int debug; - -#ifdef USE_LOG -void log_message(int priority, const char *format, ...) -{ - va_list args; - - if (priority > udev_log_priority) - return; - - va_start(args, format); - if (debug) { - vprintf(format, args); - } else - vsyslog(priority, format, args); - va_end(args); -} -#endif - -struct command { - const char *name; - int (*cmd)(int argc, char *argv[], char *envp[]); - const char *help; - int debug; -}; - -static const struct command cmds[]; - -static int version(int argc, char *argv[], char *envp[]) -{ - printf("%s\n", UDEV_VERSION); - return 0; -} - -static int help(int argc, char *argv[], char *envp[]) -{ - const struct command *cmd; - - printf("Usage: udevadm COMMAND [OPTIONS]\n"); - for (cmd = cmds; cmd->name != NULL; cmd++) - printf(" %-12s %s\n", cmd->name, cmd->help); - printf("\n"); - return 0; -} - -static const struct command cmds[] = { - { - .name = "info", - .cmd = udevinfo, - .help = "query sysfs or the udev database", - }, - { - .name = "trigger", - .cmd = udevtrigger, - .help = "request events from the kernel", - }, - { - .name = "settle", - .cmd = udevsettle, "", - .help = "wait for the event queue to finish", - }, - { - .name = "control", - .cmd = udevcontrol, - .help = "control the udev daemon", - }, - { - .name = "monitor", - .cmd = udevmonitor, - .help = "listen to kernel and udev events", - }, - { - .name = "test", - .cmd = udevtest, - .help = "simulation run", - .debug = 1, - }, - { - .name = "version", - .cmd = version, - .help = "print the version number", - }, - { - .name = "help", - .cmd = help, - .help = "print this help text", - }, - {} -}; - -int main(int argc, char *argv[], char *envp[]) -{ - const char *command; - const char *pos; - const struct command *cmd; - int rc; - - /* get binary or symlink name */ - pos = strrchr(argv[0], '/'); - if (pos != NULL) - command = &pos[1]; - else - command = argv[0]; - - /* the trailing part of the binary or symlink name is the command */ - if (strncmp(command, "udev", 4) == 0) - command = &command[4]; - - if (command == NULL || command[0] == '\0') - goto err_unknown; - - /* udevadm itself needs to strip its name from the passed options */ - if (strcmp(command, "adm") == 0) { - command = argv[1]; - argv++; - argc--; - } - - if (command == NULL) - goto err_unknown; - - /* allow command to be specified as an option */ - if (strncmp(command, "--", 2) == 0) - command += 2; - - /* find and execute command */ - for (cmd = cmds; cmd->name != NULL; cmd++) { - if (strcmp(cmd->name, command) == 0) { - debug = cmd->debug; - rc = cmd->cmd(argc, argv, envp); - goto out; - } - } - -err_unknown: - fprintf(stderr, "unknown command, try help\n\n"); - rc = 2; -out: - return rc; -} diff --git a/udevadm.xml b/udevadm.xml deleted file mode 100644 index 670c991457..0000000000 --- a/udevadm.xml +++ /dev/null @@ -1,379 +0,0 @@ - - - -
-
- udevadm - - - udevd - November 2007 - udev - - - - udevadm - 8 - - - - - udevadmudev management tool - - - - - udevadm info options - - - udevadm trigger options - - - udevadm settle options - - - udevadm control options instruction - - - udevadm monitor options - - - udevadm test options devpath - - - udevadm version - - - udevadm help - - - - DESCRIPTION - udevadm expects a command and command specific options. It - controls the runtime behavior of udev, requests kernel events, - manages the event queue, and provides simple debugging mechanisms. - - - OPTIONS - - udevadm info <replaceable>options</replaceable> - Queries the udev database for device information - stored in the udev database. It can also query the properties - of a device from its sysfs representation to help creating udev - rules that match this device. - - - - - Query the database for specified type of device data. It needs the - or to identify the specified - device. Valid queries are: - name, symlink, path, - env, all. - - - - - - The devpath of the device to query. - - - - - - The name of the device node or a symlink to query - - - - - - The udev root directory: /dev. If used in conjunction - with a name or symlink query, the - query returns the absolute path including the root directory. - - - - - - Print all sysfs properties of the specified device that can be used - in udev rules to match the specified device. It prints all devices - along the chain, up to the root of sysfs that can be used in udev rules. - - - - - - Print major/minor numbers of the underlying device, where the file - lives on. - - - - - - Export the content of the udev database. - - - - - - Print version. - - - - - - Print help text. - - - - - - udevadm trigger <optional>options</optional> - Request device uevents, usually used to replay events at system coldplug. - - - - - Print the list of devices which will be triggered. - - - - - - Do not actually trigger the event. - - - - - - Trigger only the events which are failed during a previous run. - - - - - - Type of event to be triggered. The default value is "add". - - - - - - Trigger events for devices which belong to a matching subsystem. This option - can be specified multiple times and supports shell style pattern matching. - - - - - - Do not trigger events for devices which belong to a matching subsystem. This option - can be specified multiple times and supports shell style pattern matching. - - - - - - Trigger events for devices with a matching sysfs attribute. If a value is specified - along with the attribute name, the content of the attribute is matched against the given - value using shell style pattern matching. If no value is specified, the existence of the - sysfs attribute is checked. This option can be specified multiple times. - - - - - - Do not trigger events for devices with a matching sysfs attribute. If a value is - specified along with the attribute name, the content of the attribute is matched against - the given value using shell style pattern matching. If no value is specified, the existence - of the sysfs attribute is checked. This option can be specified multiple times. - - - - - - Pass the synthesized events to the specified socket, instead of triggering - a global kernel event. All available event values will be send in the same format - the kernel sends an uevent, or - sends a message. If the first character of the specified path is an @ character, - an abstract namespace socket is used, instead of an existing socket file. - - - - - - Pass an additional environemt key to the event. This works only with the - --socket option. - - - - - - udevadm settle <optional>options</optional> - Watches the udev event queue, and exits if all current events are handled. - - - - - Maximum number of seconds to wait for the event queue to become empty. - The default value is 180 seconds. - - - - - - Print help text. - - - - - - udevadm control <replaceable>command</replaceable> - Modify the internal state of the running udev daemon. - - - - - Set the internal log level of udevd. Valid values are the numerical - syslog priorities or their textual representations: , - and . - - - - - - Signal udevd to stop executing new events. Incoming events - will be queued. - - - - - - Signal udevd to enable the execution of events. - - - - - - Signal udevd to reload the rules from the config. - - - - - - Set global variable. - - - - value - - Set the maximum number of events, udevd will handle at the - same time. - - - - - - Set the maximum number of events, which are allowed to run at the - same time. - - - - - - Print help text. - - - - - - udevadm monitor <optional>options</optional> - Listens to the kernel uevents and events sent out by a udev rule - and prints the devpath of the event to the console. It can be used to analyze the - event timing, by comparing the timestamps of the kernel uevent and the udev event. - - - - - - Print the complete environment for all events. Can be used to compare the - kernel supplied and the udev added environment values. - - - - - - Print the kernel uevents. - - - - - - Print the udev event after the rule processing. - - - - - - Print help text. - - - - - - udevadm test <optional>options</optional> <replaceable>devpath</replaceable> - Simulate a udev event run for the given device, and print out debug - output. Unless forced to, no device node or symlink will be created. - - - - - The action string. - - - - - - The subsystem string. - - - - - - Force the creation of a device node or symlink. Usually the test run - prints only debug output. - - - - - - Print help text. - - - - - - udevadm version - Print version number. - - - udevadm help - Print help text. - - - - AUTHOR - Written by Kay Sievers kay.sievers@vrfy.org. - - - - SEE ALSO - - udev7 - - - udevd8 - - - -
-
diff --git a/udevcontrol.c b/udevcontrol.c deleted file mode 100644 index 4c93b8f464..0000000000 --- a/udevcontrol.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2005-2006 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udevd.h" - -static int sock = -1; -static int udev_log = 0; - -int udevcontrol(int argc, char *argv[], char *envp[]) -{ - static struct udevd_ctrl_msg ctrl_msg; - struct sockaddr_un saddr; - socklen_t addrlen; - const char *env; - const char *arg; - const char *val; - int *intval; - int retval = 1; - - env = getenv("UDEV_LOG"); - if (env) - udev_log = log_priority(env); - - logging_init("udevcontrol"); - dbg("version %s\n", UDEV_VERSION); - - if (argc < 2) { - fprintf(stderr, "missing command\n\n"); - goto exit; - } - memset(&ctrl_msg, 0x00, sizeof(struct udevd_ctrl_msg)); - strcpy(ctrl_msg.magic, UDEVD_CTRL_MAGIC); - arg = argv[1]; - - /* allow instructions passed as options */ - if (strncmp(arg, "--", 2) == 0) - arg += 2; - - if (!strcmp(arg, "stop_exec_queue")) - ctrl_msg.type = UDEVD_CTRL_STOP_EXEC_QUEUE; - else if (!strcmp(arg, "start_exec_queue")) - ctrl_msg.type = UDEVD_CTRL_START_EXEC_QUEUE; - else if (!strcmp(arg, "reload_rules")) - ctrl_msg.type = UDEVD_CTRL_RELOAD_RULES; - else if (!strncmp(arg, "log_priority=", strlen("log_priority="))) { - intval = (int *) ctrl_msg.buf; - val = &arg[strlen("log_priority=")]; - ctrl_msg.type = UDEVD_CTRL_SET_LOG_LEVEL; - *intval = log_priority(val); - info("send log_priority=%i\n", *intval); - } else if (!strncmp(arg, "max_childs=", strlen("max_childs="))) { - char *endp; - int count; - - intval = (int *) ctrl_msg.buf; - val = &arg[strlen("max_childs=")]; - ctrl_msg.type = UDEVD_CTRL_SET_MAX_CHILDS; - count = strtoul(val, &endp, 0); - if (endp[0] != '\0' || count < 1) { - fprintf(stderr, "invalid number\n"); - goto exit; - } - *intval = count; - info("send max_childs=%i\n", *intval); - } else if (!strncmp(arg, "max_childs_running=", strlen("max_childs_running="))) { - char *endp; - int count; - - intval = (int *) ctrl_msg.buf; - val = &arg[strlen("max_childs_running=")]; - ctrl_msg.type = UDEVD_CTRL_SET_MAX_CHILDS_RUNNING; - count = strtoul(val, &endp, 0); - if (endp[0] != '\0' || count < 1) { - fprintf(stderr, "invalid number\n"); - goto exit; - } - *intval = count; - info("send max_childs_running=%i\n", *intval); - } else if (!strncmp(arg, "env", strlen("env"))) { - if (!strncmp(arg, "env=", strlen("env="))) - val = &arg[strlen("env=")]; - else - val = argv[2]; - if (val == NULL) { - fprintf(stderr, "missing key\n"); - goto exit; - } - ctrl_msg.type = UDEVD_CTRL_ENV; - strlcpy(ctrl_msg.buf, val, sizeof(ctrl_msg.buf)); - info("send env '%s'\n", val); - } else if (strcmp(arg, "help") == 0 || strcmp(arg, "-h") == 0) { - printf("Usage: udevadm control COMMAND\n" - " --log_priority= 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" - " --env== set a global environment variable\n" - " --max_childs= maximum number of childs\n" - " --max_childs_running= maximum number of childs running at the same time\n" - " --help print this help text\n\n"); - goto exit; - } else { - fprintf(stderr, "unrecognized command '%s'\n", arg); - goto exit; - } - - if (getuid() != 0) { - fprintf(stderr, "root privileges required\n"); - goto exit; - } - - sock = socket(AF_LOCAL, SOCK_DGRAM, 0); - if (sock == -1) { - err("error getting socket: %s\n", strerror(errno)); - goto exit; - } - - memset(&saddr, 0x00, sizeof(struct sockaddr_un)); - saddr.sun_family = AF_LOCAL; - /* use abstract namespace for socket path */ - strcpy(&saddr.sun_path[1], UDEVD_CTRL_SOCK_PATH); - addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); - - retval = sendto(sock, &ctrl_msg, sizeof(ctrl_msg), 0, (struct sockaddr *)&saddr, addrlen); - if (retval == -1) { - err("error sending message: %s\n", strerror(errno)); - retval = 1; - } else { - dbg("sent message type=0x%02x, %u bytes sent\n", ctrl_msg.type, retval); - retval = 0; - } - - close(sock); -exit: - logging_close(); - return retval; -} diff --git a/udevd.8 b/udevd.8 deleted file mode 100644 index c6b481134d..0000000000 --- a/udevd.8 +++ /dev/null @@ -1,61 +0,0 @@ -.\" Title: udevd -.\" Author: -.\" Generator: DocBook XSL Stylesheets v1.73.2 -.\" Date: August 2005 -.\" Manual: udevd -.\" Source: udev -.\" -.TH "UDEVD" "8" "August 2005" "udev" "udevd" -.\" disable hyphenation -.nh -.\" disable justification (adjust text to left margin only) -.ad l -.SH "NAME" -udevd - event managing daemon -.SH "SYNOPSIS" -.HP 6 -\fBudevd\fR [\fB\-\-daemon\fR] [\fB\-\-debug\-trace\fR] [\fB\-\-debug\fR] [\fB\-\-version\fR] [\fB\-\-help\fR] -.SH "DESCRIPTION" -.PP -udevd listens to kernel uevents and passes the incoming events to udev\. It ensures the correct event order and takes care, that events for child devices are delayed until the parent event has finished the device handling\. The behavior of the running daemon can be changed with -\fBudevadm control\fR\. -.SH "OPTIONS" -.PP -\fB\-\-daemon\fR -.RS 4 -Detach and run in the background\. -.RE -.PP -\fB\-\-debug\-trace\fR -.RS 4 -Run all events completely serialized\. This may be useful if udev triggers actions or loads kernel modules which cause problems and a slow but continuous operation is needed, where no events are processed in parallel\. -.RE -.PP -\fB\-\-debug\fR -.RS 4 -Print log messages to stdout\. -.RE -.PP -\fB\-\-version\fR -.RS 4 -Print version number\. -.RE -.PP -\fBhelp\fR -.RS 4 -Print help text\. -.RE -.SH "ENVIRONMENT" -.PP -\fBUDEV_LOG\fR -.RS 4 -Overrides the syslog priority specified in the config file\. -.RE -.SH "AUTHOR" -.PP -Written by Kay Sievers -\. -.SH "SEE ALSO" -.PP -\fBudev\fR(7), -\fBudevadm\fR(8) diff --git a/udevd.c b/udevd.c deleted file mode 100644 index 0827a5ceb3..0000000000 --- a/udevd.c +++ /dev/null @@ -1,1304 +0,0 @@ -/* - * Copyright (C) 2004-2006 Kay Sievers - * Copyright (C) 2004 Chris Friesen - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udev_rules.h" -#include "udevd.h" -#include "udev_selinux.h" - -static int debug_trace; -static int debug; - -static struct udev_rules rules; -static int udevd_sock = -1; -static int uevent_netlink_sock = -1; -static int inotify_fd = -1; -static pid_t sid; - -static int signal_pipe[2] = {-1, -1}; -static volatile int sigchilds_waiting; -static volatile int udev_exit; -static volatile int reload_config; -static int run_exec_q; -static int stop_exec_q; -static int max_childs; -static int max_childs_running; -static char udev_log[32]; - -static LIST_HEAD(exec_list); -static LIST_HEAD(running_list); - - -#ifdef USE_LOG -void log_message(int priority, const char *format, ...) -{ - va_list args; - - if (priority > udev_log_priority) - return; - - va_start(args, format); - if (debug) { - printf("[%d] ", (int) getpid()); - vprintf(format, args); - } else - vsyslog(priority, format, args); - va_end(args); -} - -#endif - -static void asmlinkage udev_event_sig_handler(int signum) -{ - if (signum == SIGALRM) - exit(1); -} - -static int udev_event_process(struct udevd_uevent_msg *msg) -{ - struct sigaction act; - struct udevice *udev; - int i; - int retval; - - /* set signal handlers */ - memset(&act, 0x00, sizeof(act)); - act.sa_handler = (void (*)(int)) udev_event_sig_handler; - sigemptyset (&act.sa_mask); - act.sa_flags = 0; - sigaction(SIGALRM, &act, NULL); - - /* reset to default */ - act.sa_handler = SIG_DFL; - sigaction(SIGINT, &act, NULL); - sigaction(SIGTERM, &act, NULL); - sigaction(SIGCHLD, &act, NULL); - sigaction(SIGHUP, &act, NULL); - - /* trigger timeout to prevent hanging processes */ - alarm(UDEV_EVENT_TIMEOUT); - - /* reconstruct event environment from message */ - for (i = 0; msg->envp[i]; i++) - putenv(msg->envp[i]); - - udev = udev_device_init(NULL); - if (udev == NULL) - return -1; - strlcpy(udev->action, msg->action, sizeof(udev->action)); - sysfs_device_set_values(udev->dev, msg->devpath, msg->subsystem, msg->driver); - udev->devpath_old = msg->devpath_old; - udev->devt = msg->devt; - - retval = udev_device_event(&rules, udev); - - /* rules may change/disable the timeout */ - if (udev->event_timeout >= 0) - alarm(udev->event_timeout); - - /* run programs collected by RUN-key*/ - if (retval == 0 && !udev->ignore_device && udev_run) - retval = udev_rules_run(udev); - - udev_device_cleanup(udev); - return retval; -} - -enum event_state { - EVENT_QUEUED, - EVENT_FINISHED, - EVENT_FAILED, -}; - -static void export_event_state(struct udevd_uevent_msg *msg, enum event_state state) -{ - char filename[PATH_SIZE]; - char filename_failed[PATH_SIZE]; - size_t start; - - /* location of queue file */ - snprintf(filename, sizeof(filename), "%s/"EVENT_QUEUE_DIR"/%llu", udev_root, msg->seqnum); - - /* location of failed file */ - strlcpy(filename_failed, udev_root, sizeof(filename_failed)); - strlcat(filename_failed, "/", sizeof(filename_failed)); - start = strlcat(filename_failed, EVENT_FAILED_DIR"/", sizeof(filename_failed)); - strlcat(filename_failed, msg->devpath, sizeof(filename_failed)); - path_encode(&filename_failed[start], sizeof(filename_failed) - start); - - switch (state) { - case EVENT_QUEUED: - unlink(filename_failed); - delete_path(filename_failed); - - create_path(filename); - selinux_setfscreatecon(filename, NULL, S_IFLNK); - symlink(msg->devpath, filename); - selinux_resetfscreatecon(); - break; - case EVENT_FINISHED: - if (msg->devpath_old != NULL) { - /* "move" event - rename failed file to current name, do not delete failed */ - char filename_failed_old[PATH_SIZE]; - - strlcpy(filename_failed_old, udev_root, sizeof(filename_failed_old)); - strlcat(filename_failed_old, "/", sizeof(filename_failed_old)); - start = strlcat(filename_failed_old, EVENT_FAILED_DIR"/", sizeof(filename_failed_old)); - strlcat(filename_failed_old, msg->devpath_old, sizeof(filename_failed_old)); - path_encode(&filename_failed_old[start], sizeof(filename) - start); - - if (rename(filename_failed_old, filename_failed) == 0) - info("renamed devpath, moved failed state of '%s' to %s'\n", - msg->devpath_old, msg->devpath); - } else { - unlink(filename_failed); - delete_path(filename_failed); - } - - unlink(filename); - delete_path(filename); - break; - case EVENT_FAILED: - /* move failed event to the failed directory */ - create_path(filename_failed); - rename(filename, filename_failed); - - /* clean up possibly empty queue directory */ - delete_path(filename); - break; - } - - return; -} - -static void msg_queue_delete(struct udevd_uevent_msg *msg) -{ - list_del(&msg->node); - - /* mark as failed, if "add" event returns non-zero */ - if (msg->exitstatus && strcmp(msg->action, "add") == 0) - export_event_state(msg, EVENT_FAILED); - else - export_event_state(msg, EVENT_FINISHED); - - free(msg); -} - -static void udev_event_run(struct udevd_uevent_msg *msg) -{ - pid_t pid; - int retval; - - pid = fork(); - switch (pid) { - case 0: - /* child */ - close(uevent_netlink_sock); - close(udevd_sock); - if (inotify_fd >= 0) - close(inotify_fd); - close(signal_pipe[READ_END]); - close(signal_pipe[WRITE_END]); - logging_close(); - - logging_init("udevd-event"); - setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY); - - retval = udev_event_process(msg); - info("seq %llu finished with %i\n", msg->seqnum, retval); - - logging_close(); - if (retval) - exit(1); - exit(0); - case -1: - err("fork of child failed: %s\n", strerror(errno)); - msg_queue_delete(msg); - break; - default: - /* get SIGCHLD in main loop */ - info("seq %llu forked, pid [%d], '%s' '%s', %ld seconds old\n", - msg->seqnum, pid, msg->action, msg->subsystem, time(NULL) - msg->queue_time); - msg->pid = pid; - } -} - -static void msg_queue_insert(struct udevd_uevent_msg *msg) -{ - char filename[PATH_SIZE]; - int fd; - - msg->queue_time = time(NULL); - - export_event_state(msg, EVENT_QUEUED); - info("seq %llu queued, '%s' '%s'\n", msg->seqnum, msg->action, msg->subsystem); - - strlcpy(filename, udev_root, sizeof(filename)); - strlcat(filename, "/" EVENT_SEQNUM, sizeof(filename)); - fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644); - if (fd >= 0) { - char str[32]; - int len; - - len = sprintf(str, "%llu\n", msg->seqnum); - write(fd, str, len); - close(fd); - } - - /* run one event after the other in debug mode */ - if (debug_trace) { - list_add_tail(&msg->node, &running_list); - udev_event_run(msg); - waitpid(msg->pid, NULL, 0); - msg_queue_delete(msg); - return; - } - - /* run all events with a timeout set immediately */ - if (msg->timeout != 0) { - list_add_tail(&msg->node, &running_list); - udev_event_run(msg); - return; - } - - list_add_tail(&msg->node, &exec_list); - run_exec_q = 1; -} - -static int mem_size_mb(void) -{ - FILE* f; - char buf[4096]; - long int memsize = -1; - - f = fopen("/proc/meminfo", "r"); - if (f == NULL) - return -1; - - while (fgets(buf, sizeof(buf), f) != NULL) { - long int value; - - if (sscanf(buf, "MemTotal: %ld kB", &value) == 1) { - memsize = value / 1024; - break; - } - } - - fclose(f); - return memsize; -} - -static int cpu_count(void) -{ - FILE* f; - char buf[4096]; - int count = 0; - - f = fopen("/proc/stat", "r"); - if (f == NULL) - return -1; - - while (fgets(buf, sizeof(buf), f) != NULL) { - if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) - count++; - } - - fclose(f); - if (count == 0) - return -1; - return count; -} - -static int running_processes(void) -{ - FILE* f; - char buf[4096]; - int running = -1; - - f = fopen("/proc/stat", "r"); - if (f == NULL) - return -1; - - while (fgets(buf, sizeof(buf), f) != NULL) { - int value; - - if (sscanf(buf, "procs_running %u", &value) == 1) { - running = value; - break; - } - } - - fclose(f); - return running; -} - -/* return the number of process es in our session, count only until limit */ -static int running_processes_in_session(pid_t session, int limit) -{ - DIR *dir; - struct dirent *dent; - int running = 0; - - dir = opendir("/proc"); - if (!dir) - return -1; - - /* read process info from /proc */ - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - int f; - char procdir[64]; - char line[256]; - const char *pos; - char state; - pid_t ppid, pgrp, sess; - int len; - - if (!isdigit(dent->d_name[0])) - continue; - - snprintf(procdir, sizeof(procdir), "/proc/%s/stat", dent->d_name); - procdir[sizeof(procdir)-1] = '\0'; - - f = open(procdir, O_RDONLY); - if (f == -1) - continue; - - len = read(f, line, sizeof(line)-1); - close(f); - - if (len <= 0) - continue; - else - line[len] = '\0'; - - /* skip ugly program name */ - pos = strrchr(line, ')') + 2; - if (pos == NULL) - continue; - - if (sscanf(pos, "%c %d %d %d ", &state, &ppid, &pgrp, &sess) != 4) - continue; - - /* count only processes in our session */ - if (sess != session) - continue; - - /* count only running, no sleeping processes */ - if (state != 'R') - continue; - - running++; - if (limit > 0 && running >= limit) - break; - } - closedir(dir); - - return running; -} - -static int compare_devpath(const char *running, const char *waiting) -{ - int i; - - for (i = 0; i < PATH_SIZE; i++) { - /* identical device event found */ - if (running[i] == '\0' && waiting[i] == '\0') - return 1; - - /* parent device event found */ - if (running[i] == '\0' && waiting[i] == '/') - return 2; - - /* child device event found */ - if (running[i] == '/' && waiting[i] == '\0') - return 3; - - /* no matching event */ - if (running[i] != waiting[i]) - break; - } - - return 0; -} - -/* lookup event for identical, parent, child, or physical device */ -static int devpath_busy(struct udevd_uevent_msg *msg, int limit) -{ - struct udevd_uevent_msg *loop_msg; - int childs_count = 0; - - /* check exec-queue which may still contain delayed events we depend on */ - list_for_each_entry(loop_msg, &exec_list, node) { - /* skip ourself and all later events */ - if (loop_msg->seqnum >= msg->seqnum) - break; - - /* check our old name */ - if (msg->devpath_old != NULL) - if (strcmp(loop_msg->devpath , msg->devpath_old) == 0) - return 2; - - /* check identical, parent, or child device event */ - if (compare_devpath(loop_msg->devpath, msg->devpath) != 0) { - dbg("%llu, device event still pending %llu (%s)\n", - msg->seqnum, loop_msg->seqnum, loop_msg->devpath); - return 3; - } - - /* check for our major:minor number */ - if (msg->devt && loop_msg->devt == msg->devt && - strcmp(msg->subsystem, loop_msg->subsystem) == 0) { - dbg("%llu, device event still pending %llu (%d:%d)\n", msg->seqnum, - loop_msg->seqnum, major(loop_msg->devt), minor(loop_msg->devt)); - return 4; - } - - /* check physical device event (special case of parent) */ - if (msg->physdevpath && msg->action && strcmp(msg->action, "add") == 0) - if (compare_devpath(loop_msg->devpath, msg->physdevpath) != 0) { - dbg("%llu, physical device event still pending %llu (%s)\n", - msg->seqnum, loop_msg->seqnum, loop_msg->devpath); - return 5; - } - } - - /* check run queue for still running events */ - list_for_each_entry(loop_msg, &running_list, node) { - if (limit && childs_count++ > limit) { - dbg("%llu, maximum number (%i) of childs reached\n", msg->seqnum, childs_count); - return 1; - } - - /* check our old name */ - if (msg->devpath_old != NULL) - if (strcmp(loop_msg->devpath , msg->devpath_old) == 0) - return 2; - - /* check identical, parent, or child device event */ - if (compare_devpath(loop_msg->devpath, msg->devpath) != 0) { - dbg("%llu, device event still running %llu (%s)\n", - msg->seqnum, loop_msg->seqnum, loop_msg->devpath); - return 3; - } - - /* check for our major:minor number */ - if (msg->devt && loop_msg->devt == msg->devt && - strcmp(msg->subsystem, loop_msg->subsystem) == 0) { - dbg("%llu, device event still running %llu (%d:%d)\n", msg->seqnum, - loop_msg->seqnum, major(loop_msg->devt), minor(loop_msg->devt)); - return 4; - } - - /* check physical device event (special case of parent) */ - if (msg->physdevpath && msg->action && strcmp(msg->action, "add") == 0) - if (compare_devpath(loop_msg->devpath, msg->physdevpath) != 0) { - dbg("%llu, physical device event still running %llu (%s)\n", - msg->seqnum, loop_msg->seqnum, loop_msg->devpath); - return 5; - } - } - return 0; -} - -/* serializes events for the identical and parent and child devices */ -static void msg_queue_manager(void) -{ - struct udevd_uevent_msg *loop_msg; - struct udevd_uevent_msg *tmp_msg; - int running; - - if (list_empty(&exec_list)) - return; - - running = running_processes(); - dbg("%d processes runnning on system\n", running); - if (running < 0) - running = max_childs_running; - - list_for_each_entry_safe(loop_msg, tmp_msg, &exec_list, node) { - /* check running processes in our session and possibly throttle */ - if (running >= max_childs_running) { - running = running_processes_in_session(sid, max_childs_running+10); - dbg("at least %d processes running in session\n", running); - if (running >= max_childs_running) { - dbg("delay seq %llu, too many processes already running\n", loop_msg->seqnum); - return; - } - } - - /* serialize and wait for parent or child events */ - if (devpath_busy(loop_msg, max_childs) != 0) { - dbg("delay seq %llu (%s)\n", loop_msg->seqnum, loop_msg->devpath); - continue; - } - - /* move event to run list */ - list_move_tail(&loop_msg->node, &running_list); - udev_event_run(loop_msg); - running++; - dbg("moved seq %llu to running list\n", loop_msg->seqnum); - } -} - -static struct udevd_uevent_msg *get_msg_from_envbuf(const char *buf, int buf_size) -{ - int bufpos; - int i; - struct udevd_uevent_msg *msg; - char *physdevdriver_key = NULL; - int maj = 0; - int min = 0; - - msg = malloc(sizeof(struct udevd_uevent_msg) + buf_size); - if (msg == NULL) - return NULL; - memset(msg, 0x00, sizeof(struct udevd_uevent_msg) + buf_size); - - /* copy environment buffer and reconstruct envp */ - memcpy(msg->envbuf, buf, buf_size); - bufpos = 0; - for (i = 0; (bufpos < buf_size) && (i < UEVENT_NUM_ENVP-2); i++) { - int keylen; - char *key; - - key = &msg->envbuf[bufpos]; - keylen = strlen(key); - msg->envp[i] = key; - bufpos += keylen + 1; - dbg("add '%s' to msg.envp[%i]\n", msg->envp[i], i); - - /* remember some keys for further processing */ - if (strncmp(key, "ACTION=", 7) == 0) - msg->action = &key[7]; - else if (strncmp(key, "DEVPATH=", 8) == 0) - msg->devpath = &key[8]; - else if (strncmp(key, "SUBSYSTEM=", 10) == 0) - msg->subsystem = &key[10]; - else if (strncmp(key, "DRIVER=", 7) == 0) - msg->driver = &key[7]; - else if (strncmp(key, "SEQNUM=", 7) == 0) - msg->seqnum = strtoull(&key[7], NULL, 10); - else if (strncmp(key, "DEVPATH_OLD=", 12) == 0) - msg->devpath_old = &key[12]; - else if (strncmp(key, "PHYSDEVPATH=", 12) == 0) - msg->physdevpath = &key[12]; - else if (strncmp(key, "PHYSDEVDRIVER=", 14) == 0) - physdevdriver_key = key; - else if (strncmp(key, "MAJOR=", 6) == 0) - maj = strtoull(&key[6], NULL, 10); - else if (strncmp(key, "MINOR=", 6) == 0) - min = strtoull(&key[6], NULL, 10); - else if (strncmp(key, "TIMEOUT=", 8) == 0) - msg->timeout = strtoull(&key[8], NULL, 10); - } - msg->devt = makedev(maj, min); - msg->envp[i++] = "UDEVD_EVENT=1"; - - if (msg->driver == NULL && msg->physdevpath == NULL && physdevdriver_key != NULL) { - /* for older kernels DRIVER is empty for a bus device, export PHYSDEVDRIVER as DRIVER */ - msg->envp[i++] = &physdevdriver_key[7]; - msg->driver = &physdevdriver_key[14]; - } - - msg->envp[i] = NULL; - - if (msg->devpath == NULL || msg->action == NULL) { - info("DEVPATH or ACTION missing, ignore message\n"); - free(msg); - return NULL; - } - return msg; -} - -/* receive the udevd message from userspace */ -static void get_ctrl_msg(void) -{ - struct udevd_ctrl_msg ctrl_msg; - ssize_t size; - struct msghdr smsg; - struct cmsghdr *cmsg; - struct iovec iov; - struct ucred *cred; - char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; - int *intval; - char *pos; - - memset(&ctrl_msg, 0x00, sizeof(struct udevd_ctrl_msg)); - iov.iov_base = &ctrl_msg; - iov.iov_len = sizeof(struct udevd_ctrl_msg); - - 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(udevd_sock, &smsg, 0); - if (size < 0) { - if (errno != EINTR) - err("unable to receive user udevd message: %s\n", strerror(errno)); - return; - } - cmsg = CMSG_FIRSTHDR(&smsg); - cred = (struct ucred *) CMSG_DATA(cmsg); - - if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { - err("no sender credentials received, message ignored\n"); - return; - } - - if (cred->uid != 0) { - err("sender uid=%i, message ignored\n", cred->uid); - return; - } - - if (strncmp(ctrl_msg.magic, UDEVD_CTRL_MAGIC, sizeof(UDEVD_CTRL_MAGIC)) != 0 ) { - err("message magic '%s' doesn't match, ignore it\n", ctrl_msg.magic); - return; - } - - switch (ctrl_msg.type) { - case UDEVD_CTRL_ENV: - pos = strchr(ctrl_msg.buf, '='); - if (pos == NULL) { - err("wrong key format '%s'\n", ctrl_msg.buf); - break; - } - pos[0] = '\0'; - if (pos[1] == '\0') { - info("udevd message (ENV) received, unset '%s'\n", ctrl_msg.buf); - unsetenv(ctrl_msg.buf); - } else { - info("udevd message (ENV) received, set '%s=%s'\n", ctrl_msg.buf, &pos[1]); - setenv(ctrl_msg.buf, &pos[1], 1); - } - break; - case UDEVD_CTRL_STOP_EXEC_QUEUE: - info("udevd message (STOP_EXEC_QUEUE) received\n"); - stop_exec_q = 1; - break; - case UDEVD_CTRL_START_EXEC_QUEUE: - info("udevd message (START_EXEC_QUEUE) received\n"); - stop_exec_q = 0; - msg_queue_manager(); - break; - case UDEVD_CTRL_SET_LOG_LEVEL: - intval = (int *) ctrl_msg.buf; - info("udevd message (SET_LOG_PRIORITY) received, udev_log_priority=%i\n", *intval); - udev_log_priority = *intval; - sprintf(udev_log, "UDEV_LOG=%i", udev_log_priority); - putenv(udev_log); - break; - case UDEVD_CTRL_SET_MAX_CHILDS: - intval = (int *) ctrl_msg.buf; - info("udevd message (UDEVD_SET_MAX_CHILDS) received, max_childs=%i\n", *intval); - max_childs = *intval; - break; - case UDEVD_CTRL_SET_MAX_CHILDS_RUNNING: - intval = (int *) ctrl_msg.buf; - info("udevd message (UDEVD_SET_MAX_CHILDS_RUNNING) received, max_childs=%i\n", *intval); - max_childs_running = *intval; - break; - case UDEVD_CTRL_RELOAD_RULES: - info("udevd message (RELOAD_RULES) received\n"); - reload_config = 1; - break; - default: - err("unknown control message type\n"); - } -} - -/* receive the kernel user event message and do some sanity checks */ -static struct udevd_uevent_msg *get_netlink_msg(void) -{ - struct udevd_uevent_msg *msg; - int bufpos; - ssize_t size; - static char buffer[UEVENT_BUFFER_SIZE+512]; - char *pos; - - size = recv(uevent_netlink_sock, &buffer, sizeof(buffer), 0); - if (size < 0) { - if (errno != EINTR) - err("unable to receive kernel netlink message: %s\n", strerror(errno)); - return NULL; - } - - if ((size_t)size > sizeof(buffer)-1) - size = sizeof(buffer)-1; - buffer[size] = '\0'; - dbg("uevent_size=%zi\n", size); - - /* start of event payload */ - bufpos = strlen(buffer)+1; - msg = get_msg_from_envbuf(&buffer[bufpos], size-bufpos); - if (msg == NULL) - return NULL; - - /* validate message */ - pos = strchr(buffer, '@'); - if (pos == NULL) { - err("invalid uevent '%s'\n", buffer); - free(msg); - return NULL; - } - pos[0] = '\0'; - - if (msg->action == NULL) { - info("no ACTION in payload found, skip event '%s'\n", buffer); - free(msg); - return NULL; - } - - if (strcmp(msg->action, buffer) != 0) { - err("ACTION in payload does not match uevent, skip event '%s'\n", buffer); - free(msg); - return NULL; - } - - return msg; -} - -static void asmlinkage sig_handler(int signum) -{ - switch (signum) { - case SIGINT: - case SIGTERM: - udev_exit = 1; - break; - case SIGCHLD: - /* set flag, then write to pipe if needed */ - sigchilds_waiting = 1; - break; - case SIGHUP: - reload_config = 1; - break; - } - - /* write to pipe, which will wakeup select() in our mainloop */ - write(signal_pipe[WRITE_END], "", 1); -} - -static void udev_done(int pid, int exitstatus) -{ - /* find msg associated with pid and delete it */ - struct udevd_uevent_msg *msg; - - list_for_each_entry(msg, &running_list, node) { - if (msg->pid == pid) { - info("seq %llu, pid [%d] exit with %i, %ld seconds old\n", msg->seqnum, msg->pid, - exitstatus, time(NULL) - msg->queue_time); - msg->exitstatus = exitstatus; - msg_queue_delete(msg); - - /* there may be events waiting with the same devpath */ - run_exec_q = 1; - return; - } - } -} - -static void reap_sigchilds(void) -{ - pid_t pid; - int status; - - while (1) { - pid = waitpid(-1, &status, WNOHANG); - if (pid <= 0) - break; - if (WIFEXITED(status)) - status = WEXITSTATUS(status); - else if (WIFSIGNALED(status)) - status = WTERMSIG(status) + 128; - else - status = 0; - udev_done(pid, status); - } -} - -static int init_udevd_socket(void) -{ - struct sockaddr_un saddr; - socklen_t addrlen; - const int feature_on = 1; - int retval; - - memset(&saddr, 0x00, sizeof(saddr)); - saddr.sun_family = AF_LOCAL; - /* use abstract namespace for socket path */ - strcpy(&saddr.sun_path[1], UDEVD_CTRL_SOCK_PATH); - addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); - - udevd_sock = socket(AF_LOCAL, SOCK_DGRAM, 0); - if (udevd_sock == -1) { - err("error getting socket: %s\n", strerror(errno)); - return -1; - } - - /* the bind takes care of ensuring only one copy running */ - retval = bind(udevd_sock, (struct sockaddr *) &saddr, addrlen); - if (retval < 0) { - err("bind failed: %s\n", strerror(errno)); - close(udevd_sock); - udevd_sock = -1; - return -1; - } - - /* enable receiving of the sender credentials */ - setsockopt(udevd_sock, SOL_SOCKET, SO_PASSCRED, &feature_on, sizeof(feature_on)); - - return 0; -} - -static int init_uevent_netlink_sock(void) -{ - struct sockaddr_nl snl; - const int buffersize = 16 * 1024 * 1024; - int retval; - - memset(&snl, 0x00, sizeof(struct sockaddr_nl)); - snl.nl_family = AF_NETLINK; - snl.nl_pid = getpid(); - snl.nl_groups = 1; - - uevent_netlink_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); - if (uevent_netlink_sock == -1) { - err("error getting socket: %s\n", strerror(errno)); - return -1; - } - - /* set receive buffersize */ - setsockopt(uevent_netlink_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize)); - - retval = bind(uevent_netlink_sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl)); - if (retval < 0) { - err("bind failed: %s\n", strerror(errno)); - close(uevent_netlink_sock); - uevent_netlink_sock = -1; - return -1; - } - return 0; -} - -static void export_initial_seqnum(void) -{ - char filename[PATH_SIZE]; - int fd; - char seqnum[32]; - ssize_t len = 0; - - strlcpy(filename, sysfs_path, sizeof(filename)); - strlcat(filename, "/kernel/uevent_seqnum", sizeof(filename)); - fd = open(filename, O_RDONLY); - if (fd >= 0) { - len = read(fd, seqnum, sizeof(seqnum)-1); - close(fd); - } - if (len <= 0) { - strcpy(seqnum, "0\n"); - len = 3; - } - strlcpy(filename, udev_root, sizeof(filename)); - strlcat(filename, "/" EVENT_SEQNUM, sizeof(filename)); - create_path(filename); - fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644); - if (fd >= 0) { - write(fd, seqnum, len); - close(fd); - } -} - -int main(int argc, char *argv[], char *envp[]) -{ - int retval; - int fd; - struct sigaction act; - fd_set readfds; - const char *value; - int daemonize = 0; - int option; - static const struct option options[] = { - { "daemon", 0, NULL, 'd' }, - { "debug-trace", 0, NULL, 't' }, - { "debug", 0, NULL, 'D' }, - { "help", 0, NULL, 'h' }, - { "version", 0, NULL, 'V' }, - {} - }; - int rc = 1; - int maxfd; - - logging_init("udevd"); - udev_config_init(); - selinux_init(); - dbg("version %s\n", UDEV_VERSION); - - while (1) { - option = getopt_long(argc, argv, "dDthV", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'd': - daemonize = 1; - break; - case 't': - debug_trace = 1; - break; - case 'D': - debug = 1; - if (udev_log_priority < LOG_INFO) - udev_log_priority = LOG_INFO; - break; - case 'h': - printf("Usage: udevd [--help] [--daemon] [--debug-trace] [--debug] [--version]\n"); - goto exit; - case 'V': - printf("%s\n", UDEV_VERSION); - goto exit; - default: - goto exit; - } - } - - if (getuid() != 0) { - fprintf(stderr, "root privileges required\n"); - err("root privileges required\n"); - goto exit; - } - - /* make sure std{in,out,err} fd's are in a sane state */ - fd = open("/dev/null", O_RDWR); - if (fd < 0) { - fprintf(stderr, "cannot open /dev/null\n"); - err("cannot open /dev/null\n"); - } - if (fd > STDIN_FILENO) - dup2(fd, STDIN_FILENO); - if (write(STDOUT_FILENO, 0, 0) < 0) - dup2(fd, STDOUT_FILENO); - if (write(STDERR_FILENO, 0, 0) < 0) - dup2(fd, STDERR_FILENO); - - /* init sockets to receive events */ - if (init_udevd_socket() < 0) { - if (errno == EADDRINUSE) { - fprintf(stderr, "another udev daemon already running\n"); - err("another udev daemon already running\n"); - rc = 1; - } else { - fprintf(stderr, "error initializing udevd socket\n"); - err("error initializing udevd socket\n"); - rc = 2; - } - goto exit; - } - - if (init_uevent_netlink_sock() < 0) { - fprintf(stderr, "error initializing netlink socket\n"); - err("error initializing netlink socket\n"); - rc = 3; - goto exit; - } - - /* setup signal handler pipe */ - retval = pipe(signal_pipe); - if (retval < 0) { - err("error getting pipes: %s\n", strerror(errno)); - goto exit; - } - - retval = fcntl(signal_pipe[READ_END], F_GETFL, 0); - if (retval < 0) { - err("error fcntl on read pipe: %s\n", strerror(errno)); - goto exit; - } - retval = fcntl(signal_pipe[READ_END], F_SETFL, retval | O_NONBLOCK); - if (retval < 0) { - err("error fcntl on read pipe: %s\n", strerror(errno)); - goto exit; - } - - retval = fcntl(signal_pipe[WRITE_END], F_GETFL, 0); - if (retval < 0) { - err("error fcntl on write pipe: %s\n", strerror(errno)); - goto exit; - } - retval = fcntl(signal_pipe[WRITE_END], F_SETFL, retval | O_NONBLOCK); - if (retval < 0) { - err("error fcntl on write pipe: %s\n", strerror(errno)); - goto exit; - } - - /* parse the rules and keep them in memory */ - sysfs_init(); - udev_rules_init(&rules, 1); - - export_initial_seqnum(); - - if (daemonize) { - pid_t pid; - - pid = fork(); - switch (pid) { - case 0: - dbg("daemonized fork running\n"); - break; - case -1: - err("fork of daemon failed: %s\n", strerror(errno)); - rc = 4; - goto exit; - default: - dbg("child [%u] running, parent exits\n", pid); - rc = 0; - goto exit; - } - } - - /* redirect std{out,err} fd's */ - if (!debug) - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); - if (fd > STDERR_FILENO) - close(fd); - - /* set scheduling priority for the daemon */ - setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY); - - chdir("/"); - umask(022); - - /* become session leader */ - sid = setsid(); - dbg("our session is %d\n", sid); - - /* OOM_DISABLE == -17 */ - fd = open("/proc/self/oom_adj", O_RDWR); - if (fd < 0) - err("error disabling OOM: %s\n", strerror(errno)); - else { - write(fd, "-17", 3); - close(fd); - } - - fd = open("/dev/kmsg", O_WRONLY); - if (fd > 0) { - const char *str = "<6>udevd version " UDEV_VERSION " started\n"; - - write(fd, str, strlen(str)); - close(fd); - } - - /* set signal handlers */ - memset(&act, 0x00, sizeof(struct sigaction)); - act.sa_handler = (void (*)(int)) sig_handler; - sigemptyset(&act.sa_mask); - act.sa_flags = SA_RESTART; - sigaction(SIGINT, &act, NULL); - sigaction(SIGTERM, &act, NULL); - sigaction(SIGCHLD, &act, NULL); - sigaction(SIGHUP, &act, NULL); - - /* watch rules directory */ - inotify_fd = inotify_init(); - if (inotify_fd >= 0) { - if (udev_rules_dir[0] != '\0') { - inotify_add_watch(inotify_fd, udev_rules_dir, - IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); - } else { - char filename[PATH_MAX]; - - inotify_add_watch(inotify_fd, RULES_LIB_DIR, - IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); - inotify_add_watch(inotify_fd, RULES_ETC_DIR, - IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); - - /* watch dynamic rules directory */ - strlcpy(filename, udev_root, sizeof(filename)); - strlcat(filename, "/"RULES_DYN_DIR, sizeof(filename)); - inotify_add_watch(inotify_fd, filename, - IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); - } - } else if (errno == ENOSYS) - err("the kernel does not support inotify, udevd can't monitor rules file changes\n"); - else - err("inotify_init failed: %s\n", strerror(errno)); - - /* maximum limit of forked childs */ - value = getenv("UDEVD_MAX_CHILDS"); - if (value) - max_childs = strtoul(value, NULL, 10); - else { - int memsize = mem_size_mb(); - if (memsize > 0) - max_childs = 128 + (memsize / 4); - else - max_childs = UDEVD_MAX_CHILDS; - } - info("initialize max_childs to %u\n", max_childs); - - /* start to throttle forking if maximum number of _running_ childs is reached */ - value = getenv("UDEVD_MAX_CHILDS_RUNNING"); - if (value) - max_childs_running = strtoull(value, NULL, 10); - else { - int cpus = cpu_count(); - if (cpus > 0) - max_childs_running = 8 + (8 * cpus); - else - max_childs_running = UDEVD_MAX_CHILDS_RUNNING; - } - info("initialize max_childs_running to %u\n", max_childs_running); - - /* clear environment for forked event processes */ - clearenv(); - - /* export log_priority , as called programs may want to follow that setting */ - sprintf(udev_log, "UDEV_LOG=%i", udev_log_priority); - putenv(udev_log); - if (debug_trace) - putenv("DEBUG=1"); - - maxfd = udevd_sock; - maxfd = UDEV_MAX(maxfd, uevent_netlink_sock); - maxfd = UDEV_MAX(maxfd, signal_pipe[READ_END]); - maxfd = UDEV_MAX(maxfd, inotify_fd); - - while (!udev_exit) { - struct udevd_uevent_msg *msg; - int fdcount; - - FD_ZERO(&readfds); - FD_SET(signal_pipe[READ_END], &readfds); - FD_SET(udevd_sock, &readfds); - FD_SET(uevent_netlink_sock, &readfds); - if (inotify_fd >= 0) - FD_SET(inotify_fd, &readfds); - - fdcount = select(maxfd+1, &readfds, NULL, NULL, NULL); - if (fdcount < 0) { - if (errno != EINTR) - err("error in select: %s\n", strerror(errno)); - continue; - } - - /* get control message */ - if (FD_ISSET(udevd_sock, &readfds)) - get_ctrl_msg(); - - /* get netlink message */ - if (FD_ISSET(uevent_netlink_sock, &readfds)) { - msg = get_netlink_msg(); - if (msg) - msg_queue_insert(msg); - } - - /* received a signal, clear our notification pipe */ - if (FD_ISSET(signal_pipe[READ_END], &readfds)) { - char buf[256]; - - read(signal_pipe[READ_END], &buf, sizeof(buf)); - } - - /* rules directory inotify watch */ - if ((inotify_fd >= 0) && FD_ISSET(inotify_fd, &readfds)) { - int nbytes; - - /* discard all possible events, we can just reload the config */ - if ((ioctl(inotify_fd, FIONREAD, &nbytes) == 0) && nbytes > 0) { - char *buf; - - reload_config = 1; - buf = malloc(nbytes); - if (buf == NULL) { - err("error getting buffer for inotify, disable watching\n"); - close(inotify_fd); - inotify_fd = -1; - } - read(inotify_fd, buf, nbytes); - free(buf); - } - } - - /* rules changed, set by inotify or a HUP signal */ - if (reload_config) { - reload_config = 0; - udev_rules_cleanup(&rules); - udev_rules_init(&rules, 1); - } - - /* forked child has returned */ - if (sigchilds_waiting) { - sigchilds_waiting = 0; - reap_sigchilds(); - } - - if (run_exec_q) { - run_exec_q = 0; - if (!stop_exec_q) - msg_queue_manager(); - } - } - rc = 0; - -exit: - udev_rules_cleanup(&rules); - sysfs_cleanup(); - selinux_exit(); - - if (signal_pipe[READ_END] >= 0) - close(signal_pipe[READ_END]); - if (signal_pipe[WRITE_END] >= 0) - close(signal_pipe[WRITE_END]); - - if (udevd_sock >= 0) - close(udevd_sock); - if (inotify_fd >= 0) - close(inotify_fd); - if (uevent_netlink_sock >= 0) - close(uevent_netlink_sock); - - logging_close(); - - return rc; -} diff --git a/udevd.h b/udevd.h deleted file mode 100644 index 9be34cb0cc..0000000000 --- a/udevd.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2004 Ling, Xiaofeng - * Copyright (C) 2004-2006 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "list.h" - -#define UDEVD_PRIORITY -4 -#define UDEV_PRIORITY -2 - -#define EVENT_QUEUE_DIR ".udev/queue" -#define EVENT_FAILED_DIR ".udev/failed" -#define EVENT_SEQNUM ".udev/uevent_seqnum" - -/* maximum limit of forked childs */ -#define UDEVD_MAX_CHILDS 256 -/* start to throttle forking if maximum number of running childs in our session is reached */ -#define UDEVD_MAX_CHILDS_RUNNING 16 - -/* linux/include/linux/kobject.h */ -#define UEVENT_BUFFER_SIZE 2048 -#define UEVENT_NUM_ENVP 32 - -#define UDEVD_CTRL_SOCK_PATH "/org/kernel/udev/udevd" -#define UDEVD_CTRL_MAGIC "udevd_" UDEV_VERSION - -enum udevd_ctrl_msg_type { - UDEVD_CTRL_UNKNOWN, - UDEVD_CTRL_STOP_EXEC_QUEUE, - UDEVD_CTRL_START_EXEC_QUEUE, - UDEVD_CTRL_SET_LOG_LEVEL, - UDEVD_CTRL_SET_MAX_CHILDS, - UDEVD_CTRL_SET_MAX_CHILDS_RUNNING, - UDEVD_CTRL_RELOAD_RULES, - UDEVD_CTRL_ENV, -}; - -struct udevd_ctrl_msg { - char magic[32]; - enum udevd_ctrl_msg_type type; - char buf[256]; -}; - -struct udevd_uevent_msg { - struct list_head node; - pid_t pid; - int exitstatus; - time_t queue_time; - char *action; - char *devpath; - char *subsystem; - char *driver; - dev_t devt; - unsigned long long seqnum; - char *devpath_old; - char *physdevpath; - unsigned int timeout; - char *envp[UEVENT_NUM_ENVP+1]; - char envbuf[]; -}; diff --git a/udevd.xml b/udevd.xml deleted file mode 100644 index 8d22a0c14d..0000000000 --- a/udevd.xml +++ /dev/null @@ -1,108 +0,0 @@ - - - -
-
- udevd - - - udevd - August 2005 - udev - - - - udevd - 8 - - - - - udevdevent managing daemon - - - - - udevd - - - - - - - - - DESCRIPTION - udevd listens to kernel uevents and passes the incoming events to - udev. It ensures the correct event order and takes care, that events for child - devices are delayed until the parent event has finished the device handling. - The behavior of the running daemon can be changed with - udevadm control. - - - OPTIONS - - - - - Detach and run in the background. - - - - - - Run all events completely serialized. This may be useful if udev triggers - actions or loads kernel modules which cause problems and a slow but continuous - operation is needed, where no events are processed in parallel. - - - - - - - Print log messages to stdout. - - - - - - Print version number. - - - - - - Print help text. - - - - - - ENVIRONMENT - - - - - Overrides the syslog priority specified in the config file. - - - - - - AUTHOR - Written by Kay Sievers kay.sievers@vrfy.org. - - - - SEE ALSO - - udev7 - , - - udevadm8 - - - -
-
diff --git a/udevinfo.c b/udevinfo.c deleted file mode 100644 index b9ee17c4f1..0000000000 --- a/udevinfo.c +++ /dev/null @@ -1,500 +0,0 @@ -/* - * Copyright (C) 2004-2006 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static void print_all_attributes(const char *devpath, const char *key) -{ - char path[PATH_SIZE]; - DIR *dir; - struct dirent *dent; - - strlcpy(path, sysfs_path, sizeof(path)); - strlcat(path, devpath, sizeof(path)); - - dir = opendir(path); - if (dir != NULL) { - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - struct stat statbuf; - char filename[PATH_SIZE]; - char *attr_value; - char value[NAME_SIZE]; - size_t len; - - if (dent->d_name[0] == '.') - continue; - - if (strcmp(dent->d_name, "uevent") == 0) - continue; - if (strcmp(dent->d_name, "dev") == 0) - continue; - - strlcpy(filename, path, sizeof(filename)); - strlcat(filename, "/", sizeof(filename)); - strlcat(filename, dent->d_name, sizeof(filename)); - if (lstat(filename, &statbuf) != 0) - continue; - if (S_ISLNK(statbuf.st_mode)) - continue; - - attr_value = sysfs_attr_get_value(devpath, dent->d_name); - if (attr_value == NULL) - continue; - len = strlcpy(value, attr_value, sizeof(value)); - if(len >= sizeof(value)) - len = sizeof(value) - 1; - dbg("attr '%s'='%s'(%zi)\n", dent->d_name, value, len); - - /* remove trailing newlines */ - while (len && value[len-1] == '\n') - value[--len] = '\0'; - - /* skip nonprintable attributes */ - while (len && isprint(value[len-1])) - len--; - if (len) { - dbg("attribute value of '%s' non-printable, skip\n", dent->d_name); - continue; - } - - printf(" %s{%s}==\"%s\"\n", key, dent->d_name, value); - } - } - printf("\n"); -} - -static int print_device_chain(const char *devpath) -{ - struct sysfs_device *dev; - - dev = sysfs_device_get(devpath); - if (dev == NULL) - return -1; - - printf("\n" - "Udevinfo starts with the device specified by the devpath and then\n" - "walks up the chain of parent devices. It prints for every device\n" - "found, all possible attributes in the udev rules key format.\n" - "A rule to match, can be composed by the attributes of the device\n" - "and the attributes from one single parent device.\n" - "\n"); - - printf(" looking at device '%s':\n", dev->devpath); - printf(" KERNEL==\"%s\"\n", dev->kernel); - printf(" SUBSYSTEM==\"%s\"\n", dev->subsystem); - printf(" DRIVER==\"%s\"\n", dev->driver); - print_all_attributes(dev->devpath, "ATTR"); - - /* walk up the chain of devices */ - while (1) { - dev = sysfs_device_get_parent(dev); - if (dev == NULL) - break; - printf(" looking at parent device '%s':\n", dev->devpath); - printf(" KERNELS==\"%s\"\n", dev->kernel); - printf(" SUBSYSTEMS==\"%s\"\n", dev->subsystem); - printf(" DRIVERS==\"%s\"\n", dev->driver); - - print_all_attributes(dev->devpath, "ATTRS"); - } - - return 0; -} - -static void print_record(struct udevice *udev) -{ - struct name_entry *name_loop; - - printf("P: %s\n", udev->dev->devpath); - printf("N: %s\n", udev->name); - list_for_each_entry(name_loop, &udev->symlink_list, node) - printf("S: %s\n", name_loop->name); - if (udev->link_priority != 0) - printf("L: %i\n", udev->link_priority); - if (udev->partitions != 0) - printf("A:%u\n", udev->partitions); - if (udev->ignore_remove) - printf("R:%u\n", udev->ignore_remove); - list_for_each_entry(name_loop, &udev->env_list, node) - printf("E: %s\n", name_loop->name); -} - -static void export_db(void) { - LIST_HEAD(name_list); - struct name_entry *name_loop; - - udev_db_get_all_entries(&name_list); - list_for_each_entry(name_loop, &name_list, node) { - struct udevice *udev_db; - - udev_db = udev_device_init(NULL); - if (udev_db == NULL) - continue; - if (udev_db_get_device(udev_db, name_loop->name) == 0) - print_record(udev_db); - printf("\n"); - udev_device_cleanup(udev_db); - } - name_list_cleanup(&name_list); -} - -static int lookup_device_by_name(struct udevice *udev, const char *name) -{ - LIST_HEAD(name_list); - int count; - struct name_entry *device; - int rc = -1; - - count = udev_db_get_devices_by_name(name, &name_list); - if (count <= 0) - goto out; - - info("found %i devices for '%s'\n", count, name); - - /* select the device that seems to match */ - list_for_each_entry(device, &name_list, node) { - char filename[PATH_SIZE]; - struct stat statbuf; - - udev_device_init(udev); - if (udev_db_get_device(udev, device->name) != 0) - continue; - info("found db entry '%s'\n", device->name); - - /* make sure, we don't get a link of a differnt device */ - strlcpy(filename, udev_root, sizeof(filename)); - strlcat(filename, "/", sizeof(filename)); - strlcat(filename, name, sizeof(filename)); - if (stat(filename, &statbuf) != 0) - continue; - if (major(udev->devt) > 0 && udev->devt != statbuf.st_rdev) { - info("skip '%s', dev_t doesn't match\n", udev->name); - continue; - } - rc = 0; - break; - } -out: - name_list_cleanup(&name_list); - return rc; -} - -static int stat_device(const char *name, int export, const char *prefix) -{ - struct stat statbuf; - - if (stat(name, &statbuf) != 0) - return -1; - - if (export) { - if (prefix == NULL) - prefix = "INFO_"; - printf("%sMAJOR=%d\n" - "%sMINOR=%d\n", - prefix, major(statbuf.st_dev), - prefix, minor(statbuf.st_dev)); - } else - printf("%d %d\n", major(statbuf.st_dev), minor(statbuf.st_dev)); - return 0; -} - -int udevinfo(int argc, char *argv[], char *envp[]) -{ - int option; - struct udevice *udev; - int root = 0; - int export = 0; - const char *export_prefix = NULL; - - static const struct option options[] = { - { "name", 1, NULL, 'n' }, - { "path", 1, NULL, 'p' }, - { "query", 1, NULL, 'q' }, - { "attribute-walk", 0, NULL, 'a' }, - { "export-db", 0, NULL, 'e' }, - { "root", 0, NULL, 'r' }, - { "device-id-of-file", 1, NULL, 'd' }, - { "export", 0, NULL, 'x' }, - { "export-prefix", 1, NULL, 'P' }, - { "version", 0, NULL, 1 }, /* -V outputs braindead format */ - { "help", 0, NULL, 'h' }, - {} - }; - - enum action_type { - ACTION_NONE, - ACTION_QUERY, - ACTION_ATTRIBUTE_WALK, - ACTION_ROOT, - ACTION_DEVICE_ID_FILE, - } action = ACTION_NONE; - - enum query_type { - QUERY_NONE, - QUERY_NAME, - QUERY_PATH, - QUERY_SYMLINK, - QUERY_ENV, - QUERY_ALL, - } query = QUERY_NONE; - - char path[PATH_SIZE] = ""; - char name[PATH_SIZE] = ""; - struct name_entry *name_loop; - int rc = 0; - - logging_init("udevinfo"); - udev_config_init(); - sysfs_init(); - - udev = udev_device_init(NULL); - if (udev == NULL) { - rc = 1; - goto exit; - } - - while (1) { - option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL); - if (option == -1) - break; - - dbg("option '%c'\n", option); - switch (option) { - case 'n': - /* remove /dev if given */ - if (strncmp(optarg, udev_root, strlen(udev_root)) == 0) - strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name)); - else - strlcpy(name, optarg, sizeof(name)); - remove_trailing_chars(name, '/'); - dbg("name: %s\n", name); - break; - case 'p': - /* remove /sys if given */ - if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0) - strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path)); - else - strlcpy(path, optarg, sizeof(path)); - remove_trailing_chars(path, '/'); - - /* possibly resolve to real devpath */ - if (sysfs_resolve_link(path, sizeof(path)) != 0) { - char temp[PATH_SIZE]; - char *pos; - - /* also check if the parent is a link */ - strlcpy(temp, path, sizeof(temp)); - pos = strrchr(temp, '/'); - if (pos != 0) { - char tail[PATH_SIZE]; - - strlcpy(tail, pos, sizeof(tail)); - pos[0] = '\0'; - if (sysfs_resolve_link(temp, sizeof(temp)) == 0) { - strlcpy(path, temp, sizeof(path)); - strlcat(path, tail, sizeof(path)); - } - } - } - dbg("path: %s\n", path); - break; - case 'q': - action = ACTION_QUERY; - if (strcmp(optarg, "name") == 0) { - query = QUERY_NAME; - break; - } - if (strcmp(optarg, "symlink") == 0) { - query = QUERY_SYMLINK; - break; - } - if (strcmp(optarg, "path") == 0) { - query = QUERY_PATH; - break; - } - if (strcmp(optarg, "env") == 0) { - query = QUERY_ENV; - break; - } - if (strcmp(optarg, "all") == 0) { - query = QUERY_ALL; - break; - } - fprintf(stderr, "unknown query type\n"); - rc = 2; - goto exit; - case 'r': - if (action == ACTION_NONE) - action = ACTION_ROOT; - root = 1; - break; - case 'd': - action = ACTION_DEVICE_ID_FILE; - strlcpy(name, optarg, sizeof(name)); - break; - case 'a': - action = ACTION_ATTRIBUTE_WALK; - break; - case 'e': - export_db(); - goto exit; - case 'x': - export = 1; - break; - case 'P': - export_prefix = optarg; - break; - case 1: - printf("%s\n", UDEV_VERSION); - goto exit; - case 'V': - printf("udevinfo, version %s\n", UDEV_VERSION); - goto exit; - case 'h': - printf("Usage: udevadm info OPTIONS\n" - " --query= query database for the specified value:\n" - " name name of device node\n" - " symlink pointing to node\n" - " path sysfs device path\n" - " env the device related imported environment\n" - " all all values\n" - " --path= sysfs device path used for query or chain\n" - " --name= node or symlink name used for query\n" - " --root prepend to query result or print udev_root\n" - " --attribute-walk print all key matches while walking along chain\n" - " of parent devices\n" - " --device-id-of-file= print major/minor of underlying device\n" - " --export-db export the content of the udev database\n" - " --help print this text\n" - "\n"); - goto exit; - default: - goto exit; - } - } - - /* run action */ - switch (action) { - case ACTION_QUERY: - /* needs devpath or node/symlink name for query */ - if (path[0] != '\0') { - if (udev_db_get_device(udev, path) != 0) { - fprintf(stderr, "no record for '%s' in database\n", path); - rc = 3; - goto exit; - } - } else if (name[0] != '\0') { - if (lookup_device_by_name(udev, name) != 0) { - fprintf(stderr, "node name not found\n"); - rc = 4; - goto exit; - } - } else { - fprintf(stderr, "query needs --path or node --name specified\n"); - rc = 4; - goto exit; - } - - switch(query) { - case QUERY_NAME: - if (root) - printf("%s/%s\n", udev_root, udev->name); - else - printf("%s\n", udev->name); - break; - case QUERY_SYMLINK: - list_for_each_entry(name_loop, &udev->symlink_list, node) { - char c = name_loop->node.next != &udev->symlink_list ? ' ' : '\n'; - - if (root) - printf("%s/%s%c", udev_root, name_loop->name, c); - else - printf("%s%c", name_loop->name, c); - } - break; - case QUERY_PATH: - printf("%s\n", udev->dev->devpath); - goto exit; - case QUERY_ENV: - list_for_each_entry(name_loop, &udev->env_list, node) - printf("%s\n", name_loop->name); - break; - case QUERY_ALL: - print_record(udev); - break; - default: - fprintf(stderr, "unknown query type\n"); - break; - } - break; - case ACTION_ATTRIBUTE_WALK: - if (path[0] != '\0') { - if (print_device_chain(path) != 0) { - fprintf(stderr, "no valid sysfs device found\n"); - rc = 4; - goto exit; - } - } else if (name[0] != '\0') { - if (lookup_device_by_name(udev, name) != 0) { - fprintf(stderr, "node name not found\n"); - rc = 4; - goto exit; - } - if (print_device_chain(udev->dev->devpath) != 0) { - fprintf(stderr, "no valid sysfs device found\n"); - rc = 4; - goto exit; - } - } else { - fprintf(stderr, "attribute walk needs --path or node --name specified\n"); - rc = 5; - goto exit; - } - break; - case ACTION_DEVICE_ID_FILE: - if (stat_device(name, export, export_prefix) != 0) - rc = 6; - break; - case ACTION_ROOT: - printf("%s\n", udev_root); - break; - default: - fprintf(stderr, "missing option\n"); - rc = 1; - break; - } - -exit: - udev_device_cleanup(udev); - sysfs_cleanup(); - logging_close(); - return rc; -} diff --git a/udevmonitor.c b/udevmonitor.c deleted file mode 100644 index 2430dd39a5..0000000000 --- a/udevmonitor.c +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright (C) 2004-2006 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udevd.h" - -static int uevent_netlink_sock = -1; -static int udev_monitor_sock = -1; -static volatile int udev_exit; - -static int init_udev_monitor_socket(void) -{ - struct sockaddr_un saddr; - socklen_t addrlen; - int retval; - - memset(&saddr, 0x00, sizeof(saddr)); - saddr.sun_family = AF_LOCAL; - /* use abstract namespace for socket path */ - strcpy(&saddr.sun_path[1], "/org/kernel/udev/monitor"); - addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); - - udev_monitor_sock = socket(AF_LOCAL, SOCK_DGRAM, 0); - if (udev_monitor_sock == -1) { - fprintf(stderr, "error getting socket: %s\n", strerror(errno)); - return -1; - } - - /* the bind takes care of ensuring only one copy running */ - retval = bind(udev_monitor_sock, (struct sockaddr *) &saddr, addrlen); - if (retval < 0) { - fprintf(stderr, "bind failed: %s\n", strerror(errno)); - close(udev_monitor_sock); - udev_monitor_sock = -1; - return -1; - } - - return 0; -} - -static int init_uevent_netlink_sock(void) -{ - struct sockaddr_nl snl; - int retval; - - memset(&snl, 0x00, sizeof(struct sockaddr_nl)); - snl.nl_family = AF_NETLINK; - snl.nl_pid = getpid(); - snl.nl_groups = 1; - - uevent_netlink_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); - if (uevent_netlink_sock == -1) { - fprintf(stderr, "error getting socket: %s\n", strerror(errno)); - return -1; - } - - retval = bind(uevent_netlink_sock, (struct sockaddr *) &snl, - sizeof(struct sockaddr_nl)); - if (retval < 0) { - fprintf(stderr, "bind failed: %s\n", strerror(errno)); - close(uevent_netlink_sock); - uevent_netlink_sock = -1; - return -1; - } - - return 0; -} - -static void asmlinkage sig_handler(int signum) -{ - if (signum == SIGINT || signum == SIGTERM) - udev_exit = 1; -} - -static const char *search_key(const char *searchkey, const char *buf, size_t buflen) -{ - size_t bufpos = 0; - size_t searchkeylen = strlen(searchkey); - - while (bufpos < buflen) { - const char *key; - int keylen; - - key = &buf[bufpos]; - keylen = strlen(key); - if (keylen == 0) - break; - if ((strncmp(searchkey, key, searchkeylen) == 0) && key[searchkeylen] == '=') - return &key[searchkeylen + 1]; - bufpos += keylen + 1; - } - return NULL; -} - -int udevmonitor(int argc, char *argv[], char *envp[]) -{ - struct sigaction act; - int option; - int env = 0; - int kernel = 0; - int udev = 0; - fd_set readfds; - int retval = 0; - - static const struct option options[] = { - { "environment", 0, NULL, 'e' }, - { "kernel", 0, NULL, 'k' }, - { "udev", 0, NULL, 'u' }, - { "help", 0, NULL, 'h' }, - {} - }; - - while (1) { - option = getopt_long(argc, argv, "ekuh", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'e': - env = 1; - break; - case 'k': - kernel = 1; - break; - case 'u': - udev = 1; - break; - case 'h': - printf("Usage: udevadm monitor [--environment] [--kernel] [--udev] [--help]\n" - " --env print the whole event environment\n" - " --kernel print kernel uevents\n" - " --udev print udev events\n" - " --help print this help text\n\n"); - default: - goto out; - } - } - - if (!kernel && !udev) { - kernel = 1; - udev =1; - } - - if (getuid() != 0 && kernel) { - fprintf(stderr, "root privileges needed to subscribe to kernel events\n"); - goto out; - } - - /* set signal handlers */ - memset(&act, 0x00, sizeof(struct sigaction)); - act.sa_handler = (void (*)(int)) sig_handler; - sigemptyset(&act.sa_mask); - act.sa_flags = SA_RESTART; - sigaction(SIGINT, &act, NULL); - sigaction(SIGTERM, &act, NULL); - - printf("udevmonitor will print the received events for:\n"); - if (udev) { - retval = init_udev_monitor_socket(); - if (retval) - goto out; - printf("UDEV the event which udev sends out after rule processing\n"); - } - if (kernel) { - retval = init_uevent_netlink_sock(); - if (retval) - goto out; - printf("UEVENT the kernel uevent\n"); - } - printf("\n"); - - while (!udev_exit) { - char buf[UEVENT_BUFFER_SIZE*2]; - ssize_t buflen; - ssize_t bufpos; - ssize_t keys; - int fdcount; - struct timeval tv; - struct timezone tz; - char timestr[64]; - const char *source = NULL; - const char *devpath, *action, *subsys; - - buflen = 0; - FD_ZERO(&readfds); - if (uevent_netlink_sock >= 0) - FD_SET(uevent_netlink_sock, &readfds); - if (udev_monitor_sock >= 0) - FD_SET(udev_monitor_sock, &readfds); - - fdcount = select(UDEV_MAX(uevent_netlink_sock, udev_monitor_sock)+1, &readfds, NULL, NULL, NULL); - if (fdcount < 0) { - if (errno != EINTR) - fprintf(stderr, "error receiving uevent message: %s\n", strerror(errno)); - continue; - } - - if (gettimeofday(&tv, &tz) == 0) { - snprintf(timestr, sizeof(timestr), "%llu.%06u", - (unsigned long long) tv.tv_sec, (unsigned int) tv.tv_usec); - } else - timestr[0] = '\0'; - - if ((uevent_netlink_sock >= 0) && FD_ISSET(uevent_netlink_sock, &readfds)) { - buflen = recv(uevent_netlink_sock, &buf, sizeof(buf), 0); - if (buflen <= 0) { - fprintf(stderr, "error receiving uevent message: %s\n", strerror(errno)); - continue; - } - source = "UEVENT"; - } - - if ((udev_monitor_sock >= 0) && FD_ISSET(udev_monitor_sock, &readfds)) { - buflen = recv(udev_monitor_sock, &buf, sizeof(buf), 0); - if (buflen <= 0) { - fprintf(stderr, "error receiving udev message: %s\n", strerror(errno)); - continue; - } - source = "UDEV "; - } - - if (buflen == 0) - continue; - - keys = strlen(buf) + 1; /* start of payload */ - devpath = search_key("DEVPATH", &buf[keys], buflen); - action = search_key("ACTION", &buf[keys], buflen); - subsys = search_key("SUBSYSTEM", &buf[keys], buflen); - printf("%s[%s] %-8s %s (%s)\n", source, timestr, action, devpath, subsys); - - /* print environment */ - bufpos = keys; - if (env) { - while (bufpos < buflen) { - int keylen; - char *key; - - key = &buf[bufpos]; - keylen = strlen(key); - if (keylen == 0) - break; - printf("%s\n", key); - bufpos += keylen + 1; - } - printf("\n"); - } - } - -out: - if (uevent_netlink_sock >= 0) - close(uevent_netlink_sock); - if (udev_monitor_sock >= 0) - close(udev_monitor_sock); - - if (retval) - return 1; - return 0; -} diff --git a/udevsettle.c b/udevsettle.c deleted file mode 100644 index cb63a66bba..0000000000 --- a/udevsettle.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2006 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udevd.h" - -#define DEFAULT_TIMEOUT 180 -#define LOOP_PER_SECOND 20 - -static void print_queue(const char *dir) -{ - LIST_HEAD(files); - struct name_entry *item; - - if (add_matching_files(&files, dir, NULL) < 0) - return; - - printf("\n\nAfter the udevadm settle timeout, the events queue contains:\n\n"); - - list_for_each_entry(item, &files, node) { - char target[NAME_SIZE]; - size_t len; - const char *filename = strrchr(item->name, '/'); - - if (filename == NULL) - continue; - filename++; - if (*filename == '\0') - continue; - - len = readlink(item->name, target, sizeof(target)); - if (len < 0) - continue; - target[len] = '\0'; - - printf("%s: %s\n", filename, target); - } - - printf("\n\n"); -} - -int udevsettle(int argc, char *argv[], char *envp[]) -{ - char queuename[PATH_SIZE]; - char filename[PATH_SIZE]; - unsigned long long seq_kernel; - unsigned long long seq_udev; - char seqnum[32]; - int fd; - ssize_t len; - int timeout = DEFAULT_TIMEOUT; - int loop; - static const struct option options[] = { - { "timeout", 1, NULL, 't' }, - { "help", 0, NULL, 'h' }, - {} - }; - int option; - int rc = 1; - int seconds; - - logging_init("udevsettle"); - udev_config_init(); - dbg("version %s\n", UDEV_VERSION); - sysfs_init(); - - while (1) { - option = getopt_long(argc, argv, "t:h", options, NULL); - if (option == -1) - break; - - switch (option) { - case 't': - seconds = atoi(optarg); - if (seconds > 0) - timeout = seconds; - else - fprintf(stderr, "invalid timeout value\n"); - dbg("timeout=%i\n", timeout); - break; - case 'h': - printf("Usage: udevadm settle [--help] [--timeout=]\n\n"); - goto exit; - } - } - - strlcpy(queuename, udev_root, sizeof(queuename)); - strlcat(queuename, "/" EVENT_QUEUE_DIR, sizeof(queuename)); - - loop = timeout * LOOP_PER_SECOND; - while (loop--) { - /* wait for events in queue to finish */ - while (loop--) { - struct stat statbuf; - - if (stat(queuename, &statbuf) < 0) { - info("queue is empty\n"); - break; - } - usleep(1000 * 1000 / LOOP_PER_SECOND); - } - if (loop <= 0) { - info("timeout waiting for queue\n"); - print_queue(queuename); - goto exit; - } - - /* read current udev seqnum */ - strlcpy(filename, udev_root, sizeof(filename)); - strlcat(filename, "/" EVENT_SEQNUM, sizeof(filename)); - fd = open(filename, O_RDONLY); - if (fd < 0) - goto exit; - len = read(fd, seqnum, sizeof(seqnum)-1); - close(fd); - if (len <= 0) - goto exit; - seqnum[len] = '\0'; - seq_udev = strtoull(seqnum, NULL, 10); - info("udev seqnum = %llu\n", seq_udev); - - /* read current kernel seqnum */ - strlcpy(filename, sysfs_path, sizeof(filename)); - strlcat(filename, "/kernel/uevent_seqnum", sizeof(filename)); - fd = open(filename, O_RDONLY); - if (fd < 0) - goto exit; - len = read(fd, seqnum, sizeof(seqnum)-1); - close(fd); - if (len <= 0) - goto exit; - seqnum[len] = '\0'; - seq_kernel = strtoull(seqnum, NULL, 10); - info("kernel seqnum = %llu\n", seq_kernel); - - /* make sure all kernel events have arrived in the queue */ - if (seq_udev >= seq_kernel) { - info("queue is empty and no pending events left\n"); - rc = 0; - goto exit; - } - usleep(1000 * 1000 / LOOP_PER_SECOND); - info("queue is empty, but events still pending\n"); - } - -exit: - sysfs_cleanup(); - logging_close(); - return rc; -} diff --git a/udevtest.c b/udevtest.c deleted file mode 100644 index d5e90b02c6..0000000000 --- a/udevtest.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2003-2004 Greg Kroah-Hartman - * Copyright (C) 2004-2006 Kay Sievers - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udev_rules.h" - -static int import_uevent_var(const char *devpath) -{ - char path[PATH_SIZE]; - static char value[4096]; /* must stay, used with putenv */ - ssize_t size; - int fd; - char *key; - char *next; - int rc = -1; - - /* read uevent file */ - strlcpy(path, sysfs_path, sizeof(path)); - strlcat(path, devpath, sizeof(path)); - strlcat(path, "/uevent", sizeof(path)); - fd = open(path, O_RDONLY); - if (fd < 0) - goto out; - size = read(fd, value, sizeof(value)); - close(fd); - if (size < 0) - goto out; - value[size] = '\0'; - - /* import keys into environment */ - key = value; - while (key[0] != '\0') { - next = strchr(key, '\n'); - if (next == NULL) - goto out; - next[0] = '\0'; - info("import into environment: '%s'\n", key); - putenv(key); - key = &next[1]; - } - rc = 0; -out: - return rc; -} - -int udevtest(int argc, char *argv[], char *envp[]) -{ - int force = 0; - const char *action = "add"; - const char *subsystem = NULL; - const char *devpath = NULL; - struct udevice *udev; - struct sysfs_device *dev; - struct udev_rules rules = {}; - int retval; - int rc = 0; - - static const struct option options[] = { - { "action", 1, NULL, 'a' }, - { "subsystem", 1, NULL, 's' }, - { "force", 0, NULL, 'f' }, - { "help", 0, NULL, 'h' }, - {} - }; - - info("version %s\n", UDEV_VERSION); - udev_config_init(); - if (udev_log_priority < LOG_INFO) { - char priority[32]; - - udev_log_priority = LOG_INFO; - sprintf(priority, "%i", udev_log_priority); - setenv("UDEV_LOG", priority, 1); - } - - while (1) { - int option; - - option = getopt_long(argc, argv, "a:s:fh", options, NULL); - if (option == -1) - break; - - dbg("option '%c'\n", option); - switch (option) { - case 'a': - action = optarg; - break; - case 's': - subsystem = optarg; - break; - case 'f': - force = 1; - break; - case 'h': - printf("Usage: udevadm test OPTIONS \n" - " --action= set action string\n" - " --subsystem= set subsystem string\n" - " --force don't skip node/link creation\n" - " --help print this help text\n\n"); - exit(0); - default: - exit(1); - } - } - devpath = argv[optind]; - - if (devpath == NULL) { - fprintf(stderr, "devpath parameter missing\n"); - rc = 1; - goto exit; - } - - printf("This program is for debugging only, it does not run any program,\n" - "specified by a RUN key. It may show incorrect results, because\n" - "some values may be different, or not available at a simulation run.\n" - "\n"); - - sysfs_init(); - udev_rules_init(&rules, 0); - - /* remove /sys if given */ - if (strncmp(devpath, sysfs_path, strlen(sysfs_path)) == 0) - devpath = &devpath[strlen(sysfs_path)]; - - dev = sysfs_device_get(devpath); - if (dev == NULL) { - fprintf(stderr, "unable to open device '%s'\n", devpath); - rc = 2; - goto exit; - } - - udev = udev_device_init(NULL); - if (udev == NULL) { - fprintf(stderr, "error initializing device\n"); - rc = 3; - goto exit; - } - - if (subsystem != NULL) - strlcpy(dev->subsystem, subsystem, sizeof(dev->subsystem)); - - /* override built-in sysfs device */ - udev->dev = dev; - strlcpy(udev->action, action, sizeof(udev->action)); - udev->devt = udev_device_get_devt(udev); - - /* simulate node creation with test flag */ - if (!force) - udev->test_run = 1; - - setenv("DEVPATH", udev->dev->devpath, 1); - setenv("SUBSYSTEM", udev->dev->subsystem, 1); - setenv("ACTION", udev->action, 1); - import_uevent_var(udev->dev->devpath); - - info("looking at device '%s' from subsystem '%s'\n", udev->dev->devpath, udev->dev->subsystem); - retval = udev_device_event(&rules, udev); - - if (udev->event_timeout >= 0) - info("custom event timeout: %i\n", udev->event_timeout); - - if (retval == 0 && !udev->ignore_device && udev_run) { - struct name_entry *name_loop; - - list_for_each_entry(name_loop, &udev->run_list, node) { - char program[PATH_SIZE]; - - strlcpy(program, name_loop->name, sizeof(program)); - udev_rules_apply_format(udev, program, sizeof(program)); - info("run: '%s'\n", program); - } - } - udev_device_cleanup(udev); - -exit: - udev_rules_cleanup(&rules); - sysfs_cleanup(); - return rc; -} diff --git a/udevtrigger.c b/udevtrigger.c deleted file mode 100644 index d4b10d06ab..0000000000 --- a/udevtrigger.c +++ /dev/null @@ -1,712 +0,0 @@ -/* - * Copyright (C) 2004-2006 Kay Sievers - * Copyright (C) 2006 Hannes Reinecke - * - * 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 version 2 of the License. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udevd.h" -#include "udev_rules.h" - -static int verbose; -static int dry_run; -LIST_HEAD(device_list); -LIST_HEAD(filter_subsystem_match_list); -LIST_HEAD(filter_subsystem_nomatch_list); -LIST_HEAD(filter_attr_match_list); -LIST_HEAD(filter_attr_nomatch_list); -static int sock = -1; -static struct sockaddr_un saddr; -static socklen_t saddrlen; - -/* devices that should run last cause of their dependencies */ -static int delay_device(const char *devpath) -{ - static const char *delay_device_list[] = { - "*/md*", - "*/dm-*", - NULL - }; - int i; - - for (i = 0; delay_device_list[i] != NULL; i++) - if (fnmatch(delay_device_list[i], devpath, 0) == 0) - return 1; - return 0; -} - -static int device_list_insert(const char *path) -{ - char filename[PATH_SIZE]; - char devpath[PATH_SIZE]; - struct stat statbuf; - - dbg("add '%s'\n" , path); - - /* we only have a device, if we have an uevent file */ - strlcpy(filename, path, sizeof(filename)); - strlcat(filename, "/uevent", sizeof(filename)); - if (stat(filename, &statbuf) < 0) - return -1; - if (!(statbuf.st_mode & S_IWUSR)) - return -1; - - strlcpy(devpath, &path[strlen(sysfs_path)], sizeof(devpath)); - - /* resolve possible link to real target */ - if (lstat(path, &statbuf) < 0) - return -1; - if (S_ISLNK(statbuf.st_mode)) - if (sysfs_resolve_link(devpath, sizeof(devpath)) != 0) - return -1; - - name_list_add(&device_list, devpath, 1); - return 0; -} - -static void trigger_uevent(const char *devpath, const char *action) -{ - char filename[PATH_SIZE]; - int fd; - - strlcpy(filename, sysfs_path, sizeof(filename)); - strlcat(filename, devpath, sizeof(filename)); - strlcat(filename, "/uevent", sizeof(filename)); - - if (verbose) - printf("%s\n", devpath); - - if (dry_run) - return; - - fd = open(filename, O_WRONLY); - if (fd < 0) { - dbg("error on opening %s: %s\n", filename, strerror(errno)); - return; - } - - if (write(fd, action, strlen(action)) < 0) - info("error writing '%s' to '%s': %s\n", action, filename, strerror(errno)); - - close(fd); -} - -static int pass_to_socket(const char *devpath, const char *action, const char *env) -{ - struct udevice udev; - struct name_entry *name_loop; - char buf[4096]; - size_t bufpos = 0; - ssize_t count; - char path[PATH_SIZE]; - int fd; - char link_target[PATH_SIZE]; - int len; - int err = 0; - - if (verbose) - printf("%s\n", devpath); - - udev_device_init(&udev); - udev_db_get_device(&udev, devpath); - - /* add header */ - bufpos = snprintf(buf, sizeof(buf)-1, "%s@%s", action, devpath); - bufpos++; - - /* add cookie */ - if (env != NULL) { - bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "%s", env); - bufpos++; - } - - /* add standard keys */ - bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "DEVPATH=%s", devpath); - bufpos++; - bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "ACTION=%s", action); - bufpos++; - - /* add subsystem */ - strlcpy(path, sysfs_path, sizeof(path)); - strlcat(path, devpath, sizeof(path)); - strlcat(path, "/subsystem", sizeof(path)); - len = readlink(path, link_target, sizeof(link_target)); - if (len > 0) { - char *pos; - - link_target[len] = '\0'; - pos = strrchr(link_target, '/'); - if (pos != NULL) { - bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "SUBSYSTEM=%s", &pos[1]); - bufpos++; - } - } - - /* add symlinks and node name */ - path[0] = '\0'; - list_for_each_entry(name_loop, &udev.symlink_list, node) { - strlcat(path, udev_root, sizeof(path)); - strlcat(path, "/", sizeof(path)); - strlcat(path, name_loop->name, sizeof(path)); - strlcat(path, " ", sizeof(path)); - } - remove_trailing_chars(path, ' '); - if (path[0] != '\0') { - bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "DEVLINKS=%s", path); - bufpos++; - } - if (udev.name[0] != '\0') { - strlcpy(path, udev_root, sizeof(path)); - strlcat(path, "/", sizeof(path)); - strlcat(path, udev.name, sizeof(path)); - bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "DEVNAME=%s", path); - bufpos++; - } - - /* add keys from device "uevent" file */ - strlcpy(path, sysfs_path, sizeof(path)); - strlcat(path, devpath, sizeof(path)); - strlcat(path, "/uevent", sizeof(path)); - fd = open(path, O_RDONLY); - if (fd >= 0) { - char value[4096]; - - count = read(fd, value, sizeof(value)); - close(fd); - if (count > 0) { - char *key; - - value[count] = '\0'; - key = value; - while (key[0] != '\0') { - char *next; - - next = strchr(key, '\n'); - if (next == NULL) - break; - next[0] = '\0'; - bufpos += strlcpy(&buf[bufpos], key, sizeof(buf) - bufpos-1); - bufpos++; - key = &next[1]; - } - } - } - - /* add keys from database */ - list_for_each_entry(name_loop, &udev.env_list, node) { - bufpos += strlcpy(&buf[bufpos], name_loop->name, sizeof(buf) - bufpos-1); - bufpos++; - } - if (bufpos > sizeof(buf)) - bufpos = sizeof(buf); - - count = sendto(sock, &buf, bufpos, 0, (struct sockaddr *)&saddr, saddrlen); - if (count < 0) - err = -1; - - return err; -} - -static void exec_list(const char *action, const char *env) -{ - struct name_entry *loop_device; - struct name_entry *tmp_device; - - list_for_each_entry_safe(loop_device, tmp_device, &device_list, node) { - if (delay_device(loop_device->name)) - continue; - if (sock >= 0) - pass_to_socket(loop_device->name, action, env); - else - trigger_uevent(loop_device->name, action); - list_del(&loop_device->node); - free(loop_device); - } - - /* trigger remaining delayed devices */ - list_for_each_entry_safe(loop_device, tmp_device, &device_list, node) { - if (sock >= 0) - pass_to_socket(loop_device->name, action, env); - else - trigger_uevent(loop_device->name, action); - list_del(&loop_device->node); - free(loop_device); - } -} - -static int subsystem_filtered(const char *subsystem) -{ - struct name_entry *loop_name; - - /* skip devices matching the listed subsystems */ - list_for_each_entry(loop_name, &filter_subsystem_nomatch_list, node) - if (fnmatch(loop_name->name, subsystem, 0) == 0) - return 1; - - /* skip devices not matching the listed subsystems */ - if (!list_empty(&filter_subsystem_match_list)) { - list_for_each_entry(loop_name, &filter_subsystem_match_list, node) - if (fnmatch(loop_name->name, subsystem, 0) == 0) - return 0; - return 1; - } - - return 0; -} - -static int attr_match(const char *path, const char *attr_value) -{ - char attr[NAME_SIZE]; - char file[PATH_SIZE]; - char *match_value; - - strlcpy(attr, attr_value, sizeof(attr)); - - /* separate attr and match value */ - match_value = strchr(attr, '='); - if (match_value != NULL) { - match_value[0] = '\0'; - match_value = &match_value[1]; - } - - strlcpy(file, path, sizeof(file)); - strlcat(file, "/", sizeof(file)); - strlcat(file, attr, sizeof(file)); - - if (match_value != NULL) { - /* match file content */ - char value[NAME_SIZE]; - int fd; - ssize_t size; - - fd = open(file, O_RDONLY); - if (fd < 0) - return 0; - size = read(fd, value, sizeof(value)); - close(fd); - if (size < 0) - return 0; - value[size] = '\0'; - remove_trailing_chars(value, '\n'); - - /* match if attribute value matches */ - if (fnmatch(match_value, value, 0) == 0) - return 1; - } else { - /* match if attribute exists */ - struct stat statbuf; - - if (stat(file, &statbuf) == 0) - return 1; - } - return 0; -} - -static int attr_filtered(const char *path) -{ - struct name_entry *loop_name; - - /* skip devices matching the listed sysfs attributes */ - list_for_each_entry(loop_name, &filter_attr_nomatch_list, node) - if (attr_match(path, loop_name->name)) - return 1; - - /* skip devices not matching the listed sysfs attributes */ - if (!list_empty(&filter_attr_match_list)) { - list_for_each_entry(loop_name, &filter_attr_match_list, node) - if (attr_match(path, loop_name->name)) - return 0; - return 1; - } - return 0; -} - -enum scan_type { - SCAN_DEVICES, - SCAN_SUBSYSTEM, -}; - -static void scan_subsystem(const char *subsys, enum scan_type scan) -{ - char base[PATH_SIZE]; - DIR *dir; - struct dirent *dent; - const char *subdir; - - if (scan == SCAN_DEVICES) - subdir = "/devices"; - else if (scan == SCAN_SUBSYSTEM) - subdir = "/drivers"; - else - return; - - strlcpy(base, sysfs_path, sizeof(base)); - strlcat(base, "/", sizeof(base)); - strlcat(base, subsys, sizeof(base)); - - dir = opendir(base); - if (dir != NULL) { - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - char dirname[PATH_SIZE]; - DIR *dir2; - struct dirent *dent2; - - if (dent->d_name[0] == '.') - continue; - - if (scan == SCAN_DEVICES) - if (subsystem_filtered(dent->d_name)) - continue; - - strlcpy(dirname, base, sizeof(dirname)); - strlcat(dirname, "/", sizeof(dirname)); - strlcat(dirname, dent->d_name, sizeof(dirname)); - - if (scan == SCAN_SUBSYSTEM) { - if (!subsystem_filtered("subsystem")) - device_list_insert(dirname); - if (subsystem_filtered("drivers")) - continue; - } - - strlcat(dirname, subdir, sizeof(dirname)); - - /* look for devices/drivers */ - dir2 = opendir(dirname); - if (dir2 != NULL) { - for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { - char dirname2[PATH_SIZE]; - - if (dent2->d_name[0] == '.') - continue; - - strlcpy(dirname2, dirname, sizeof(dirname2)); - strlcat(dirname2, "/", sizeof(dirname2)); - strlcat(dirname2, dent2->d_name, sizeof(dirname2)); - if (attr_filtered(dirname2)) - continue; - device_list_insert(dirname2); - } - closedir(dir2); - } - } - closedir(dir); - } -} - -static void scan_block(void) -{ - char base[PATH_SIZE]; - DIR *dir; - struct dirent *dent; - - if (subsystem_filtered("block")) - return; - - strlcpy(base, sysfs_path, sizeof(base)); - strlcat(base, "/block", sizeof(base)); - - dir = opendir(base); - if (dir != NULL) { - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - char dirname[PATH_SIZE]; - DIR *dir2; - struct dirent *dent2; - - if (dent->d_name[0] == '.') - continue; - - strlcpy(dirname, base, sizeof(dirname)); - strlcat(dirname, "/", sizeof(dirname)); - strlcat(dirname, dent->d_name, sizeof(dirname)); - if (attr_filtered(dirname)) - continue; - if (device_list_insert(dirname) != 0) - continue; - - /* look for partitions */ - dir2 = opendir(dirname); - if (dir2 != NULL) { - for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { - char dirname2[PATH_SIZE]; - - if (dent2->d_name[0] == '.') - continue; - - if (!strcmp(dent2->d_name,"device")) - continue; - - strlcpy(dirname2, dirname, sizeof(dirname2)); - strlcat(dirname2, "/", sizeof(dirname2)); - strlcat(dirname2, dent2->d_name, sizeof(dirname2)); - if (attr_filtered(dirname2)) - continue; - device_list_insert(dirname2); - } - closedir(dir2); - } - } - closedir(dir); - } -} - -static void scan_class(void) -{ - char base[PATH_SIZE]; - DIR *dir; - struct dirent *dent; - - strlcpy(base, sysfs_path, sizeof(base)); - strlcat(base, "/class", sizeof(base)); - - dir = opendir(base); - if (dir != NULL) { - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - char dirname[PATH_SIZE]; - DIR *dir2; - struct dirent *dent2; - - if (dent->d_name[0] == '.') - continue; - - if (subsystem_filtered(dent->d_name)) - continue; - - strlcpy(dirname, base, sizeof(dirname)); - strlcat(dirname, "/", sizeof(dirname)); - strlcat(dirname, dent->d_name, sizeof(dirname)); - dir2 = opendir(dirname); - if (dir2 != NULL) { - for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { - char dirname2[PATH_SIZE]; - - if (dent2->d_name[0] == '.') - continue; - - if (!strcmp(dent2->d_name, "device")) - continue; - - strlcpy(dirname2, dirname, sizeof(dirname2)); - strlcat(dirname2, "/", sizeof(dirname2)); - strlcat(dirname2, dent2->d_name, sizeof(dirname2)); - if (attr_filtered(dirname2)) - continue; - device_list_insert(dirname2); - } - closedir(dir2); - } - } - closedir(dir); - } -} - -static void scan_failed(void) -{ - char base[PATH_SIZE]; - DIR *dir; - struct dirent *dent; - - strlcpy(base, udev_root, sizeof(base)); - strlcat(base, "/" EVENT_FAILED_DIR, sizeof(base)); - - dir = opendir(base); - if (dir != NULL) { - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - char device[PATH_SIZE]; - size_t start; - - if (dent->d_name[0] == '.') - continue; - - start = strlcpy(device, sysfs_path, sizeof(device)); - if(start >= sizeof(device)) - start = sizeof(device) - 1; - strlcat(device, dent->d_name, sizeof(device)); - path_decode(&device[start]); - device_list_insert(device); - } - closedir(dir); - } -} - -int udevtrigger(int argc, char *argv[], char *envp[]) -{ - int failed = 0; - const char *sockpath = NULL; - int option; - const char *action = "add"; - const char *env = NULL; - static const struct option options[] = { - { "verbose", 0, NULL, 'v' }, - { "dry-run", 0, NULL, 'n' }, - { "retry-failed", 0, NULL, 'F' }, - { "socket", 1, NULL, 'o' }, - { "help", 0, NULL, 'h' }, - { "action", 1, NULL, 'c' }, - { "subsystem-match", 1, NULL, 's' }, - { "subsystem-nomatch", 1, NULL, 'S' }, - { "attr-match", 1, NULL, 'a' }, - { "attr-nomatch", 1, NULL, 'A' }, - { "env", 1, NULL, 'e' }, - {} - }; - - logging_init("udevtrigger"); - udev_config_init(); - dbg("version %s\n", UDEV_VERSION); - sysfs_init(); - - while (1) { - option = getopt_long(argc, argv, "vnFo:hce::s:S:a:A:", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'v': - verbose = 1; - break; - case 'n': - dry_run = 1; - break; - case 'F': - failed = 1; - break; - case 'o': - sockpath = optarg; - break; - case 'c': - action = optarg; - break; - case 'e': - if (strchr(optarg, '=') != NULL) - env = optarg; - break; - case 's': - name_list_add(&filter_subsystem_match_list, optarg, 0); - break; - case 'S': - name_list_add(&filter_subsystem_nomatch_list, optarg, 0); - break; - case 'a': - name_list_add(&filter_attr_match_list, optarg, 0); - break; - case 'A': - name_list_add(&filter_attr_nomatch_list, optarg, 0); - break; - case 'h': - printf("Usage: udevadm trigger OPTIONS\n" - " --verbose print the list of devices while running\n" - " --dry-run do not actually trigger the events\n" - " --retry-failed trigger only the events which have been\n" - " marked as failed during a previous run\n" - " --socket= pass events to socket instead of triggering kernel events\n" - " --env== pass an additional key (works only with --socket=)\n" - " --subsystem-match= trigger devices from a matching subystem\n" - " --subsystem-nomatch= exclude devices from a matching subystem\n" - " --attr-match=]> trigger devices with a matching sysfs\n" - " attribute\n" - " --attr-nomatch=]> exclude devices with a matching sysfs\n" - " attribute\n" - " --help print this text\n" - "\n"); - goto exit; - default: - goto exit; - } - } - - if (sockpath != NULL) { - struct stat stats; - - sock = socket(AF_LOCAL, SOCK_DGRAM, 0); - memset(&saddr, 0x00, sizeof(struct sockaddr_un)); - saddr.sun_family = AF_LOCAL; - if (sockpath[0] == '@') { - /* abstract namespace socket requested */ - strlcpy(&saddr.sun_path[1], &sockpath[1], sizeof(saddr.sun_path)-1); - saddrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); - } else if (stat(sockpath, &stats) == 0 && S_ISSOCK(stats.st_mode)) { - /* existing socket file */ - strlcpy(saddr.sun_path, sockpath, sizeof(saddr.sun_path)); - saddrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path); - } else { - /* no socket file, assume abstract namespace socket */ - strlcpy(&saddr.sun_path[1], sockpath, sizeof(saddr.sun_path)-1); - saddrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); - } - } else if (env != NULL) { - fprintf(stderr, "error: --env= only valid with --socket= option\n"); - goto exit; - } - - if (failed) { - scan_failed(); - exec_list(action, env); - } else { - char base[PATH_SIZE]; - struct stat statbuf; - - /* if we have /sys/subsystem, forget all the old stuff */ - strlcpy(base, sysfs_path, sizeof(base)); - strlcat(base, "/subsystem", sizeof(base)); - if (stat(base, &statbuf) == 0) { - scan_subsystem("subsystem", SCAN_SUBSYSTEM); - exec_list(action, env); - scan_subsystem("subsystem", SCAN_DEVICES); - exec_list(action, env); - } else { - scan_subsystem("bus", SCAN_SUBSYSTEM); - exec_list(action, env); - scan_subsystem("bus", SCAN_DEVICES); - scan_class(); - - /* scan "block" if it isn't a "class" */ - strlcpy(base, sysfs_path, sizeof(base)); - strlcat(base, "/class/block", sizeof(base)); - if (stat(base, &statbuf) != 0) - scan_block(); - exec_list(action, env); - } - } - -exit: - name_list_cleanup(&filter_subsystem_match_list); - name_list_cleanup(&filter_subsystem_nomatch_list); - name_list_cleanup(&filter_attr_match_list); - name_list_cleanup(&filter_attr_nomatch_list); - - if (sock >= 0) - close(sock); - sysfs_cleanup(); - logging_close(); - return 0; -} -- cgit v1.2.3-54-g00ecf