chiark / gitweb /
Merge gregkh@ehlo.org:/home/kay/public_html/pub/scm/linux/hotplug/udev-kay
authorGreg KH <greg@press.(none)>
Tue, 21 Jun 2005 23:36:29 +0000 (16:36 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 21 Jun 2005 23:36:29 +0000 (16:36 -0700)
58 files changed:
Makefile
README
etc/udev/udev.conf.in
etc/udev/udev.rules
etc/udev/udev.rules.examples
extras/run_directory/Makefile [new file with mode: 0644]
extras/run_directory/run_directory.c [new file with mode: 0644]
extras/run_directory/udev_run_devd.c [new file with mode: 0644]
extras/run_directory/udev_run_hotplugd.c [new file with mode: 0644]
extras/volume_id/Makefile
extras/volume_id/udev_volume_id.c
klibc/Makefile
klibc/include/net/route.h [new file with mode: 0644]
klibc/include/netinet/if_ether.h [moved from klibc/include/net/if_ether.h with 100% similarity]
klibc/include/netpacket/packet.h [new file with mode: 0644]
klibc/include/signal.h
klibc/klcc.1
klibc/klcc.in
klibc/klibc/Kbuild [new file with mode: 0644]
klibc/klibc/SYSCALLS.def
klibc/klibc/arch/i386/Makefile.inc
klibc/klibc/arch/i386/sigreturn.S [new file with mode: 0644]
klibc/klibc/arch/x86_64/MCONFIG
klibc/klibc/arch/x86_64/Makefile.inc
klibc/klibc/arch/x86_64/sigreturn.S [new file with mode: 0644]
klibc/klibc/arch/x86_64/syscall.S
klibc/klibc/fgets.c
klibc/klibc/sigaction.c
klibc/klibc/sigpending.c
klibc/klibc/sigprocmask.c
klibc/klibc/sigsuspend.c
klibc/klibc/strntoumax.c
klibc/makeklcc.pl [changed mode: 0644->0755]
klibc/version
test/simple-build-check.sh [changed mode: 0644->0755]
test/udev-test.pl
udev.8.in
udev.c
udev.h
udev.spec
udev_add.c
udev_config.c
udev_multiplex.c [deleted file]
udev_remove.c
udev_rules.c
udev_rules.h
udev_rules_parse.c
udev_sysfs.c
udev_utils.c
udev_utils.h
udevcontrol.c [new file with mode: 0644]
udevd.c
udevd.h
udeveventrecorder.c [new file with mode: 0644]
udevinitsend.c [new file with mode: 0644]
udevsend.c
udevstart.c
udevtest.c

index dc3930b..8694b91 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -44,11 +44,13 @@ V=false
 ROOT =         udev
 DAEMON =       udevd
 SENDER =       udevsend
+INITSENDER =   udevinitsend
+RECORDER =     udeveventrecorder
+CONTROL =      udevcontrol
 INFO =         udevinfo
 TESTER =       udevtest
 STARTER =      udevstart
 VERSION =      058
-INSTALL_DIR =  /usr/local/bin
 RELEASE_NAME = $(ROOT)-$(VERSION)
 LOCAL_CFG_DIR =        etc/udev
 DESTDIR =
@@ -61,10 +63,7 @@ etcdir =     ${prefix}/etc
 sbindir =      ${exec_prefix}/sbin
 usrbindir =    ${exec_prefix}/usr/bin
 mandir =       ${prefix}/usr/share/man
-hotplugdir =   ${etcdir}/hotplug.d/default
 configdir =    ${etcdir}/udev
-initdir =      ${etcdir}/init.d
-dev_ddir =     ${etcdir}/dev.d
 srcdir = .
 
 INSTALL = /usr/bin/install -c
@@ -142,7 +141,6 @@ UDEV_OBJS = \
        udev_remove.o           \
        udev_sysfs.o            \
        udev_db.o               \
-       udev_multiplex.o        \
        udev_rules.o            \
        udev_rules_parse.o      \
        udev_libc_wrapper.o
@@ -178,7 +176,6 @@ ifeq ($(strip $(USE_KLIBC)),true)
        KLCC            = $(KLIBC_INSTALL)/bin/klcc
        CC              = $(KLCC)
        LD              = $(KLCC)
-       LDFLAGS         += -static
 else
        CFLAGS          += -Wshadow -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations
 endif
@@ -205,7 +202,7 @@ endif
 # config files automatically generated
 GEN_CONFIGS =  $(LOCAL_CFG_DIR)/udev.conf
 
-all: $(ROOT) $(SENDER) $(DAEMON) $(INFO) $(TESTER) $(STARTER) $(GEN_CONFIGS) $(KLCC)
+all: $(ROOT) $(SENDER) $(INITSENDER) $(RECORDER) $(CONTROL) $(DAEMON) $(INFO) $(TESTER) $(STARTER) $(GEN_CONFIGS) $(KLCC)
        @extras="$(EXTRAS)" ; for target in $$extras ; do \
                echo $$target ; \
                $(MAKE) prefix=$(prefix) \
@@ -250,8 +247,8 @@ udev_version.h:
        @echo \#define UDEV_CONFIG_DIR          \"$(configdir)\" >> $@
        @echo \#define UDEV_CONFIG_FILE         \"$(configdir)/udev.conf\" >> $@
        @echo \#define UDEV_RULES_FILE          \"$(configdir)/rules.d\" >> $@
-       @echo \#define UDEV_BIN                 \"$(DESTDIR)$(sbindir)/udev\" >> $@
-       @echo \#define UDEVD_BIN                \"$(DESTDIR)$(sbindir)/udevd\" >> $@
+       @echo \#define UDEV_BIN                 \"$(sbindir)/udev\" >> $@
+       @echo \#define UDEVD_BIN                \"$(sbindir)/udevd\" >> $@
 
 # Rules on how to create the generated config files
 $(LOCAL_CFG_DIR)/udev.conf:
@@ -263,15 +260,18 @@ GEN_MANPAGESIN = udev.8.in
 $(GEN_MANPAGES): $(GEN_MANPAGESIN)
        sed -e "s:@udevdir@:$(udevdir):" < $@.in > $@
 
-$(UDEV_OBJS): $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
-$(SYSFS_OBJS): $(HOST_PROGS) $(KLCC)
-$(OBJS): $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
-$(ROOT).o: $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
-$(TESTER).o: $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
-$(INFO).o: $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
-$(DAEMON).o: $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
-$(SENDER).o: $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
-$(STARTER).o: $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
+$(UDEV_OBJS): $(HEADERS) $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
+$(SYSFS_OBJS): $(HEADERS) $(HOST_PROGS) $(KLCC)
+$(OBJS): $(HEADERS) $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
+$(ROOT).o: $(HEADERS) $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
+$(TESTER).o: $(HEADERS) $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
+$(INFO).o: $(HEADERS) $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
+$(DAEMON).o: $(HEADERS) $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
+$(SENDER).o: $(HEADERS) $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
+$(INITSENDER).o: $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
+$(RECORDER).o: $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
+$(CONTROL).o: $(HEADERS) $( $(HEADERS)GEN_HEADERS) $(HOST_PROGS) $(KLCC)
+$(STARTER).o: $(HEADERS) $(GEN_HEADERS) $(HOST_PROGS) $(KLCC)
 
 $(ROOT): $(KLCC) $(ROOT).o $(OBJS) $(HEADERS) $(GEN_MANPAGES)
        $(QUIET) $(LD) $(LDFLAGS) -o $@ $(ROOT).o $(OBJS) $(LIB_OBJS)
@@ -293,6 +293,18 @@ $(SENDER): $(KLCC) $(SENDER).o $(OBJS) udevd.h
        $(QUIET) $(LD) $(LDFLAGS) -o $@ $(SENDER).o $(OBJS) $(LIB_OBJS)
        $(QUIET) $(STRIPCMD) $@
 
+$(INITSENDER): $(KLCC) $(INITSENDER).o $(OBJS) udevd.h
+       $(QUIET) $(LD) $(LDFLAGS) -o $@ $(INITSENDER).o $(OBJS) $(LIB_OBJS)
+       $(QUIET) $(STRIPCMD) $@
+
+$(RECORDER): $(KLCC) $(RECORDER).o $(OBJS) udevd.h
+       $(QUIET) $(LD) $(LDFLAGS) -o $@ $(RECORDER).o $(OBJS) $(LIB_OBJS)
+       $(QUIET) $(STRIPCMD) $@
+
+$(CONTROL): $(KLCC) $(CONTROL).o $(OBJS) udevd.h
+       $(QUIET) $(LD) $(LDFLAGS) -o $@ $(CONTROL).o $(OBJS) $(LIB_OBJS)
+       $(QUIET) $(STRIPCMD) $@
+
 $(STARTER): $(KLCC) $(STARTER).o $(OBJS)
        $(QUIET) $(LD) $(LDFLAGS) -o $@ $(STARTER).o $(OBJS) $(LIB_OBJS)
        $(QUIET) $(STRIPCMD) $@
@@ -304,7 +316,7 @@ clean:
        -find . \( -not -type d \) -and \( -name '*~' -o -name '*.[oas]' \) -type f -print \
         | xargs rm -f 
        -rm -f core $(ROOT) $(GEN_HEADERS) $(GEN_CONFIGS) $(GEN_MANPAGES) $(INFO) $(DAEMON) \
-        $(SENDER) $(TESTER) $(STARTER)
+        $(SENDER) $(INITSENDER) $(RECORDER) $(CONTROL) $(TESTER) $(STARTER)
        -rm -f ccdv
        $(MAKE) -C klibc SUBDIRS=klibc clean
        @extras="$(EXTRAS)" ; for target in $$extras ; do \
@@ -332,16 +344,6 @@ install-config:
                $(INSTALL_DATA) $(LOCAL_CFG_DIR)/udev.rules $(DESTDIR)$(configdir)/rules.d/50-udev.rules; \
        fi
 
-install-dev.d:
-       $(INSTALL) -d $(DESTDIR)$(dev_ddir)/default
-       $(INSTALL_PROGRAM) -D etc/dev.d/net/hotplug.dev $(DESTDIR)$(dev_ddir)/net/hotplug.dev
-
-uninstall-dev.d:
-       - rm $(dev_ddir)/net/hotplug.dev
-       - rmdir $(dev_ddir)/net
-       - rmdir $(dev_ddir)/default
-       - rmdir $(dev_ddir)
-
 install-man:
        $(INSTALL_DATA) -D udev.8 $(DESTDIR)$(mandir)/man8/udev.8
        $(INSTALL_DATA) -D udevinfo.8 $(DESTDIR)$(mandir)/man8/udevinfo.8
@@ -358,18 +360,18 @@ uninstall-man:
        - rm $(mandir)/man8/udevd.8
        - rm $(mandir)/man8/udevsend.8
 
-install: install-config install-man install-dev.d all
+install: install-config install-man all
        $(INSTALL) -d $(DESTDIR)$(udevdir)
-       $(INSTALL) -d $(DESTDIR)$(hotplugdir)
        $(INSTALL_PROGRAM) -D $(ROOT) $(DESTDIR)$(sbindir)/$(ROOT)
        $(INSTALL_PROGRAM) -D $(DAEMON) $(DESTDIR)$(sbindir)/$(DAEMON)
        $(INSTALL_PROGRAM) -D $(SENDER) $(DESTDIR)$(sbindir)/$(SENDER)
+       $(INSTALL_PROGRAM) -D $(CONTROL) $(DESTDIR)$(sbindir)/$(CONTROL)
        $(INSTALL_PROGRAM) -D $(INFO) $(DESTDIR)$(usrbindir)/$(INFO)
        $(INSTALL_PROGRAM) -D $(TESTER) $(DESTDIR)$(usrbindir)/$(TESTER)
        $(INSTALL_PROGRAM) -D $(STARTER) $(DESTDIR)$(sbindir)/$(STARTER)
-       - ln -f -s $(sbindir)/$(SENDER) $(DESTDIR)$(hotplugdir)/10-udev.hotplug
 ifndef DESTDIR
        - killall $(DAEMON)
+       - $(sbindir)/$(DAEMON) --daemon
        - rm -rf $(udevdb)
 endif
        @extras="$(EXTRAS)" ; for target in $$extras ; do \
@@ -378,8 +380,7 @@ endif
                        -C $$target $@ ; \
        done ; \
 
-uninstall: uninstall-man uninstall-dev.d
-       - rm $(hotplugdir)/10-udev.hotplug
+uninstall: uninstall-man
        - rm $(configdir)/rules.d/50-udev.rules
        - rm $(configdir)/udev.conf
        - rmdir $(configdir)/rules.d
@@ -387,10 +388,12 @@ uninstall: uninstall-man uninstall-dev.d
        - rm $(sbindir)/$(ROOT)
        - rm $(sbindir)/$(DAEMON)
        - rm $(sbindir)/$(SENDER)
+       - rm $(sbindir)/$(INITSENDER)
+       - rm $(sbindir)/$(RECORDER)
+       - rm $(sbindir)/$(CONTROL)
        - rm $(sbindir)/$(STARTER)
        - rm $(usrbindir)/$(INFO)
        - rm $(usrbindir)/$(TESTER)
-       - rmdir $(hotplugdir)
        - rm -rf $(udevdb)
        - rmdir $(udevdir)
        - killall $(DAEMON)
diff --git a/README b/README
index 54cee65..a2ce9c7 100644 (file)
--- a/README
+++ b/README
@@ -10,8 +10,7 @@ To use:
 
 - Your 2.6 kernel must have had CONFIG_HOTPLUG enabled when it was built.
 
-- Make sure sysfs is mounted.  udev will figure out where sysfs is mounted, but
-  the traditional place for it is at /sys.  You can mount it by hand by running:
+- Make sure sysfs is mounted at /sys. You can mount it by running:
        mount -t sysfs none /sys
 
 - Make sure you have the latest version of the linux-hotplug scripts.  They are
index 9d5300a..efdb1d1 100644 (file)
@@ -1,5 +1,4 @@
 # udev.conf
-#
 
 # Where in the filesystem to place the device nodes
 udev_root="@udevdir@"
index bc8c8bd..bebf748 100644 (file)
@@ -5,43 +5,42 @@
 #
 
 # if this is a ide cdrom, name it the default name, and create a symlink to cdrom
-BUS="ide", KERNEL="*[!0-9]", PROGRAM="/bin/cat /proc/ide/%k/media", RESULT="cdrom", NAME="%k", SYMLINK="cdrom"
-
-# create a symlink named after the device map name
-# note devmap_name comes with extras/multipath
-KERNEL="dm-[0-9]*", PROGRAM="/sbin/devmap_name %M %m", NAME="%k", SYMLINK="%c"
+BUS=="ide", KERNEL=="*[!0-9]", PROGRAM="/bin/cat /proc/ide/%k/media", RESULT="cdrom", NAME="%k", SYMLINK+="cdrom"
 
 # DRI devices always go into a subdirectory (as per the LSB spec)
-KERNEL="card*", NAME="dri/card%n"
+KERNEL=="card*",               NAME="dri/card%n"
 
 # alsa devices
-KERNEL="controlC[0-9]*", NAME="snd/%k"
-KERNEL="hw[CD0-9]*",     NAME="snd/%k"
-KERNEL="pcm[CD0-9cp]*",  NAME="snd/%k"
-KERNEL="midiC[D0-9]*",   NAME="snd/%k"
-KERNEL="timer",          NAME="snd/%k"
-KERNEL="seq",            NAME="snd/%k"
+KERNEL=="controlC[0-9]*",      NAME="snd/%k"
+KERNEL=="hw[CD0-9]*",          NAME="snd/%k"
+KERNEL=="pcm[CD0-9cp]*",       NAME="snd/%k"
+KERNEL=="midiC[D0-9]*",                NAME="snd/%k"
+KERNEL=="timer",               NAME="snd/%k"
+KERNEL=="seq",                 NAME="snd/%k"
 
 # input devices
-KERNEL="mice",         NAME="input/%k"
-KERNEL="mouse*",       NAME="input/%k"
-KERNEL="event*",       NAME="input/%k"
-KERNEL="js*",          NAME="input/%k"
-KERNEL="ts*",          NAME="input/%k"
+KERNEL=="mice",                        NAME="input/%k"
+KERNEL=="mouse*",              NAME="input/%k"
+KERNEL=="event*",              NAME="input/%k"
+KERNEL=="js*",                 NAME="input/%k"
+KERNEL=="ts*",                 NAME="input/%k"
 
 # USB devices
-KERNEL="hiddev*",      NAME="usb/%k"
-KERNEL="auer*",                NAME="usb/%k"
-KERNEL="legousbtower*",        NAME="usb/%k"
-KERNEL="dabusb*",      NAME="usb/%k"
-BUS="usb", KERNEL="lp[0-9]*",  NAME="usb/%k"
+KERNEL=="hiddev*",             NAME="usb/%k"
+KERNEL=="auer*",               NAME="usb/%k"
+KERNEL=="legousbtower*",       NAME="usb/%k"
+KERNEL=="dabusb*",             NAME="usb/%k"
+BUS=="usb", KERNEL=="lp[0-9]*",        NAME="usb/%k"
 
 # CAPI devices
-KERNEL="capi",         NAME="capi20", SYMLINK="isdn/capi20"
-KERNEL="capi*",                NAME="capi/%n"
+KERNEL=="capi",                        NAME="capi20", SYMLINK+="isdn/capi20"
+KERNEL=="capi*",               NAME="capi/%n"
 
 # Network devices
-KERNEL="tun",          NAME="net/%k"
+KERNEL=="tun",                 NAME="net/%k"
 
 # raw devices
-KERNEL="raw[0-9]*",    NAME="raw/%k" 
+KERNEL=="raw[0-9]*",           NAME="raw/%k"
+
+# emulate dev.d/
+RUN="/sbin/udev_run_devd"
index a464b6b..1c45278 100644 (file)
@@ -9,64 +9,55 @@
 #
 
 # Looking for scsi bus id 42:0:0:1
-BUS="scsi", PROGRAM="/bin/echo -n test-%b", RESULT="test-42:0:0:1", NAME="%c"
+BUS=="scsi", PROGRAM="/bin/echo -n test-%b", RESULT=="test-42:0:0:1", NAME="%c"
 
 # A usb camera.
-BUS="usb", SYSFS{vendor}="FUJIFILM", SYSFS{model}="M100", NAME="camera%n"
+BUS=="usb", SYSFS{vendor}=="FUJIFILM", SYSFS{model}=="M100", NAME="camera%n"
 
 # USB Epson printer to be called lp_epson
-BUS="usb", SYSFS_serial="HXOLL0012202323480", NAME="lp_epson"
+BUS=="usb", SYSFS_serial=="HXOLL0012202323480", NAME="lp_epson"
 
 # USB HP printer to be called lp_hp
-BUS="usb", SYSFS{serial}="W09090207101241330", NAME="lp_hp"
+BUS=="usb", SYSFS{serial}=="W09090207101241330", NAME="lp_hp"
 
 # sound card with PCI bus id 00:0b.0 to be the first sound card
-BUS="pci", ID="00:0b.0", NAME="dsp"
+BUS=="pci", ID=="00:0b.0", NAME="dsp"
 
 # sound card with PCI bus id 00:07.1 to be the second sound card
-BUS="pci", ID="00:07.1", NAME="dsp1" 
-
-# USB mouse plugged into the third port of the first hub to be called mouse0
-BUS="usb", PLACE="1.3", NAME="mouse0"
-
-# USB tablet plugged into the third port of the second hub to be called mouse1
-BUS="usb", PLACE="2.3", NAME="mouse1"
-BUS="usb", PLACE="2.4", NAME="mouse2"
+BUS=="pci", ID=="00:07.1", NAME="dsp1" 
 
 # ttyUSB1 should always be called visor
-KERNEL="ttyUSB1", NAME="visor"
-KERNEL="ttyUSB0", NAME="pl2303"
+KERNEL=="ttyUSB1", NAME="visor"
+KERNEL=="ttyUSB0", NAME="pl2303"
 
 # a devfs like way to name some tty devices
-KERNEL="ttyS*", NAME="tts/%n"
-KERNEL="tty*", NAME="vc/%n"
+KERNEL=="ttyS*", NAME="tts/%n"
+KERNEL=="tty*", NAME="vc/%n"
 
 # if this is a ide cdrom, name it the default name, and create a symlink to cdrom
-BUS="ide", KERNEL="*[!0-9]", PROGRAM="/bin/cat /proc/ide/%k/media", RESULT="cdrom", NAME="%k", SYMLINK="cdrom"
-
-# create a symlink named after the device map name
-# note devmap_name comes with extras/multipath
-KERNEL="dm-[0-9]*", PROGRAM="/sbin/devmap_name %M %m", NAME="%k", SYMLINK="%c"
+BUS=="ide", KERNEL=="*[!0-9]", PROGRAM="/bin/cat /proc/ide/%k/media", RESULT=="cdrom", NAME="%k", SYMLINK+="cdrom"
 
 # DRI devices always go into a subdirectory (as per the LSB spec)
-KERNEL="card*", NAME="dri/card%n"
+KERNEL=="card*", NAME="dri/card%n"
 
 # create all 15 partitions of a USB flash card reader
-# note the trailing spaces in the attribute, use udevinfo(8) to examine your device
-BUS="scsi", SYSFS{model}="CF/MD           ", NAME{all_partitions}="compactflash"
+BUS=="scsi", SYSFS{model}=="CF/MD", NAME{all_partitions}="compactflash"
 
 # alsa devices
-KERNEL="controlC[0-9]*", NAME="snd/%k"
-KERNEL="hw[CD0-9]*",     NAME="snd/%k"
-KERNEL="pcm[CD0-9cp]*",  NAME="snd/%k"
-KERNEL="midi[CD0-9]*",   NAME="snd/%k"
-KERNEL="timer",          NAME="snd/%k"
-KERNEL="seq",            NAME="snd/%k"
+KERNEL=="controlC[0-9]*",      NAME="snd/%k"
+KERNEL=="hw[CD0-9]*",          NAME="snd/%k"
+KERNEL=="pcm[CD0-9cp]*",       NAME="snd/%k"
+KERNEL=="midi[CD0-9]*",                NAME="snd/%k"
+KERNEL=="timer",               NAME="snd/%k"
+KERNEL=="seq",                 NAME="snd/%k"
 
 # input devices
-KERNEL="mice",         NAME="input/%k"
-KERNEL="mouse*",       NAME="input/%k"
-KERNEL="event*",       NAME="input/%k"
-KERNEL="js*",          NAME="input/%k"
-KERNEL="ts*",          NAME="input/%k"
+KERNEL=="mice",                        NAME="input/%k"
+KERNEL=="mouse*",              NAME="input/%k"
+KERNEL=="event*",              NAME="input/%k"
+KERNEL=="js*",                 NAME="input/%k"
+KERNEL=="ts*",                 NAME="input/%k"
+
+# emulate dev.d/
+RUN="/sbin/udev_run_devd"
 
diff --git a/extras/run_directory/Makefile b/extras/run_directory/Makefile
new file mode 100644 (file)
index 0000000..12dccf0
--- /dev/null
@@ -0,0 +1,55 @@
+# Makefile for udev_volume_id
+#
+# Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+
+DEVD = udev_run_devd
+HOTPLUGD = udev_run_hotplugd
+
+all:   $(DEVD) $(HOTPLUGD)
+
+prefix =
+exec_prefix =  ${prefix}
+etcdir =       ${prefix}/etc
+sbindir =      ${exec_prefix}/sbin
+usrbindir =    ${exec_prefix}/usr/bin
+usrsbindir =   ${exec_prefix}/usr/sbin
+mandir =       ${prefix}/usr/share/man
+devddir =      ${etcdir}/dev.d/default
+configdir =    ${etcdir}/udev/
+initdir =      ${etcdir}/init.d/
+srcdir = .
+
+INSTALL = /usr/bin/install -c
+INSTALL_PROGRAM = ${INSTALL}
+INSTALL_DATA  = ${INSTALL} -m 644
+INSTALL_SCRIPT = ${INSTALL_PROGRAM}
+
+override CFLAGS+=-D_FILE_OFFSET_BITS=64
+
+OBJS = ../../udev.a ../../libsysfs/sysfs.a
+
+.c.o:
+       $(QUIET) $(CC) $(CFLAGS) -c -o $@ $<
+
+$(DEVD): $(HEADERS) $(DEVD).o run_directory.o
+       $(QUIET) $(LD) $(LDFLAGS) -o $(DEVD) $(DEVD).o run_directory.o $(OBJS)
+
+$(HOTPLUGD): $(HEADERS) $(HOTPLUGD).o run_directory.o
+       $(QUIET) $(LD) $(LDFLAGS) -o $(HOTPLUGD) $(HOTPLUGD).o run_directory.o $(OBJS)
+
+clean:
+       rm -f $(DEVD) $(HOTPLUGD) run_directory.o
+
+spotless: clean
+
+install: all
+       $(INSTALL_PROGRAM) $(DEVD) $(DESTDIR)$(sbindir)/$(DEVD)
+       $(INSTALL_PROGRAM) $(HOTPLUGD) $(DESTDIR)$(sbindir)/$(HOTPLUGD)
+
+uninstall:
+       - rm $(DESTDIR)$(sbindir)/$(DEVD)
diff --git a/extras/run_directory/run_directory.c b/extras/run_directory/run_directory.c
new file mode 100644 (file)
index 0000000..e6c6173
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * udev_run_directory.c - directory multiplexer
+ *
+ * Copyright (C) 2005 Kay Sievers <kay@vrfy.org>
+ *
+ *     This program is free software; you can redistribute it and/or modify it
+ *     under the terms of the GNU General Public License as published by the
+ *     Free Software Foundation version 2 of the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include "../../udev_utils.h"
+#include "../../list.h"
+#include "../../logging.h"
+
+int run_directory(const char *dir, const char *suffix, const char *subsystem);
+
+static int run_program(const char *filename, const char *subsystem)
+{
+       pid_t pid;
+
+       dbg("running %s", filename);
+       pid = fork();
+       switch (pid) {
+       case 0:
+               /* child */
+               execl(filename, filename, subsystem, NULL);
+               dbg("exec of child failed");
+               _exit(1);
+       case -1:
+               dbg("fork of child failed");
+               break;
+               return -1;
+       default:
+               waitpid(pid, NULL, 0);
+       }
+
+       return 0;
+}
+
+int run_directory(const char *dir, const char *suffix, const char *subsystem)
+{
+       char dirname[NAME_SIZE];
+       struct name_entry *name_loop, *name_tmp;
+       LIST_HEAD(name_list);
+
+       if (subsystem) {
+               snprintf(dirname, sizeof(dirname), "%s/%s", dir, subsystem);
+               dirname[sizeof(dirname)-1] = '\0';
+               dbg("looking at '%s'", dirname);
+               add_matching_files(&name_list, dirname, suffix);
+       }
+
+       snprintf(dirname, sizeof(dirname), "%s/default", dir);
+       dirname[sizeof(dirname)-1] = '\0';
+       dbg("looking at '%s'", dirname);
+       add_matching_files(&name_list, dirname, suffix);
+
+       list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) {
+               run_program(name_loop->name, subsystem);
+               list_del(&name_loop->node);
+       }
+
+       logging_close();
+       return 0;
+}
diff --git a/extras/run_directory/udev_run_devd.c b/extras/run_directory/udev_run_devd.c
new file mode 100644 (file)
index 0000000..02bbc8c
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * udev_run_devd.c - directory multiplexer
+ *
+ * Copyright (C) 2005 Kay Sievers <kay@vrfy.org>
+ *
+ *     This program is free software; you can redistribute it and/or modify it
+ *     under the terms of the GNU General Public License as published by the
+ *     Free Software Foundation version 2 of the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include "../../udev_utils.h"
+#include "../../list.h"
+#include "../../logging.h"
+
+extern int run_directory(const char *dir, const char *suffix, const char *subsystem);
+
+#ifdef USE_LOG
+void log_message (int priority, const char *format, ...)
+{
+       va_list args;
+       static int udev_log = -1;
+
+       if (udev_log == -1) {
+               const char *value;
+
+               value = getenv("UDEV_LOG");
+               if (value)
+                       udev_log = log_priority(value);
+               else
+                       udev_log = LOG_ERR;
+       }
+
+       if (priority > udev_log)
+               return;
+
+       va_start(args, format);
+       vsyslog(priority, format, args);
+       va_end(args);
+}
+#endif
+
+int main(int argc, char *argv[], char *envp[])
+{
+       const char *subsystem;
+       int fd;
+
+       if (getenv("DEVNAME") == NULL)
+               exit(0);
+
+       subsystem = argv[1];
+       logging_init("udev_run_devd");
+
+       fd = open("/dev/null", O_RDWR);
+       if (fd >= 0) {
+               dup2(fd, STDOUT_FILENO);
+               dup2(fd, STDIN_FILENO);
+               dup2(fd, STDERR_FILENO);
+               close(fd);
+       }
+       dbg("running dev.d directory");
+
+       run_directory("/etc/dev.d", ".dev", subsystem);
+       exit(0);
+}
diff --git a/extras/run_directory/udev_run_hotplugd.c b/extras/run_directory/udev_run_hotplugd.c
new file mode 100644 (file)
index 0000000..54b6bf4
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * udev_run_hotplugd.c - directory multiplexer
+ *
+ * Copyright (C) 2005 Kay Sievers <kay@vrfy.org>
+ *
+ *     This program is free software; you can redistribute it and/or modify it
+ *     under the terms of the GNU General Public License as published by the
+ *     Free Software Foundation version 2 of the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include "../../udev_utils.h"
+#include "../../list.h"
+#include "../../logging.h"
+
+extern int run_directory(const char *dir, const char *suffix, const char *subsystem);
+
+#ifdef USE_LOG
+void log_message (int priority, const char *format, ...)
+{
+       va_list args;
+       static int udev_log = -1;
+
+       if (udev_log == -1) {
+               const char *value;
+
+               value = getenv("UDEV_LOG");
+               if (value)
+                       udev_log = log_priority(value);
+               else
+                       udev_log = LOG_ERR;
+       }
+
+       if (priority > udev_log)
+               return;
+
+       va_start(args, format);
+       vsyslog(priority, format, args);
+       va_end(args);
+}
+#endif
+
+int main(int argc, char *argv[], char *envp[])
+{
+       const char *subsystem;
+       int fd;
+
+       subsystem = argv[1];
+       logging_init("udev_run_hotplugd");
+
+       fd = open("/dev/null", O_RDWR);
+       if (fd >= 0) {
+               dup2(fd, STDOUT_FILENO);
+               dup2(fd, STDIN_FILENO);
+               dup2(fd, STDERR_FILENO);
+               close(fd);
+       }
+
+       dbg("running dev.d directory");
+
+       run_directory("/etc/hotplug.d", ".hotplug", subsystem);
+       exit(0);
+}
index 91527cc..56fdf27 100644 (file)
@@ -33,7 +33,7 @@ override CFLAGS+=-D_FILE_OFFSET_BITS=64
 VOLUME_ID_BASE=volume_id
 include $(VOLUME_ID_BASE)/Makefile.inc
 
-OBJS = udev_volume_id.o $(VOLUME_ID_OBJS) $(SYSFS)
+OBJS = udev_volume_id.o $(VOLUME_ID_OBJS) ../../udev.a
 HEADERS = $(VOLUME_ID_HEADERS)
 
 $(OBJS): $(HEADERS)
index a00b01a..7c6fc40 100644 (file)
 #define BLKGETSIZE64 _IOR(0x12,114,size_t)
 
 #ifdef USE_LOG
-void log_message(int level, const char *format, ...)
+void log_message(int priority, const char *format, ...)
 {
        va_list args;
+       static int udev_log = -1;
+
+       if (udev_log == -1) {
+               const char *value;
+
+               value = getenv("UDEV_LOG");
+               if (value)
+                       udev_log = log_priority(value);
+               else
+                       udev_log = LOG_ERR;
+       }
+
+       if (priority > udev_log)
+               return;
 
        va_start(args, format);
-       vsyslog(level, format, args);
+       vsyslog(priority, format, args);
        va_end(args);
 }
 #endif
index 5071e60..e634aac 100644 (file)
@@ -69,4 +69,9 @@ local-install: $(CROSS)klcc
        $(INSTALL_DATA) klcc.1 $(INSTALLROOT)$(mandir)/man1/$(KCROSS)klcc.1
        $(INSTALL_EXEC) $(KCROSS)klcc $(INSTALLROOT)$(bindir)
 
+# This does all the prep work needed to turn a freshly exported git repository
+# into a release tarball tree
+release: klibc.spec
+       rm -f maketar.sh
+
 -include MCONFIG
diff --git a/klibc/include/net/route.h b/klibc/include/net/route.h
new file mode 100644 (file)
index 0000000..a60df24
--- /dev/null
@@ -0,0 +1 @@
+#include <linux/route.h>
diff --git a/klibc/include/netpacket/packet.h b/klibc/include/netpacket/packet.h
new file mode 100644 (file)
index 0000000..b5e8e0e
--- /dev/null
@@ -0,0 +1 @@
+#include <linux/if_packet.h>
index ab3c98d..05930bd 100644 (file)
@@ -82,10 +82,6 @@ __extern int sigaction(int, const struct sigaction *, struct sigaction *);
 __extern int sigprocmask(int, const sigset_t *, sigset_t *);
 __extern int sigpending(sigset_t *);
 __extern int sigsuspend(const sigset_t *);
-__extern int rt_sigaction(int, const struct sigaction *, struct sigaction *, size_t);
-__extern int rt_sigprocmask(int, const sigset_t *, sigset_t *, size_t);
-__extern int rt_sigpending(sigset_t *, size_t);
-__extern int rt_sigsuspend(const sigset_t *, size_t);
 __extern int raise(int);
 __extern int kill(pid_t, int);
 
index a6f979c..76334a3 100644 (file)
@@ -1,4 +1,4 @@
-.\" $Id: klcc.1,v 1.2 2005/03/02 02:24:17 hpa Exp $
+.\" $Id: klcc.1,v 1.3 2005/04/19 23:27:46 hpa Exp $
 .\" -----------------------------------------------------------------------
 .\"   
 .\"   Copyright 2005 H. Peter Anvin - All Rights Reserved
@@ -39,7 +39,9 @@ klcc \- compile a program against klibc
 .B klcc
 is a wrapper around
 .BR gcc (1)
-to compile a program against the
+and
+.BR ld (1)
+which compiles and links a program against the
 .B klibc
 tiny C library.  It supports most
 .B gcc
@@ -63,7 +65,12 @@ or
 option to use the default optimization level; this will generally
 result in the smallest binaries.  You may want to use
 .B \-s
-when linking, however.
+when linking, however.  Use
+.B \-O0
+to compile without any optimization whatsoever; this may not work depending
+on the version of
+.B gcc
+used.
 .PP
 Use the
 .B \-shared
@@ -72,10 +79,38 @@ or
 option to compile for and link against shared or static klibc.  Note
 that shared klibc only supports running against the exact same klibc
 binary as the binary was linked with.
+.PP
+In addition to standard
+.B gcc
+options,
+.B klcc
+supports options of the form \fB\-print-klibc-\fP\fIoption\fP,
+which prints the corresponding klibc configuration option.
 .SH AUTHOR
 Written by H. Peter Anvin <hpa@zytor.com>.
 .SH COPYRIGHT
-Copyright \(co 2005 H. Peter Anvin.
+Copyright \(co 2005 H. Peter Anvin \- All Rights Reserved
+.PP
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom
+the Software is furnished to do so, subject to the following
+conditions:
+.PP
+The above copyright notice and this permission notice shall
+be included in all copies or substantial portions of the Software.
+.PP
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
 .SH "SEE ALSO"
 .BR gcc (1)
 
index 36c4d9b..5629f24 100644 (file)
@@ -1,5 +1,7 @@
 # -*- perl -*-
 
+use IPC::Open3;
+
 # Standard includes
 @includes = ("-I${prefix}/${KCROSS}include/arch/${ARCH}",
             "-I${prefix}/${KCROSS}include/bits${BITSIZE}",
@@ -57,7 +59,7 @@ sub files_with_lang($$) {
 
        # Skip object files
        if ( $need ne 'obj' ) {
-           unless ( $xopt eq $need ) {
+           unless ( $xopt eq $need || $need eq 'stdin') {
                push(@as, '-x', $need);
                $xopt = $need;
            }
@@ -79,7 +81,11 @@ sub syserr($) {
 # Run a program; printing out the command line if $verbose is set
 sub mysystem(@) {
     print STDERR join(' ', @_), "\n" if ( $verbose );
-    return system(@_);
+    my $cmd = shift;
+    open(INPUT, "<&STDIN");    # dup STDIN filehandle to INPUT
+    my $childpid = open3("<&INPUT", ">&STDOUT", ">&STDERR", $cmd, @_);
+    waitpid ($childpid, 0);
+    return $?;
 }
 
 #
@@ -117,6 +123,11 @@ while ( defined($a = shift(@ARGV)) ) {
        # Not an option.  Must be a filename then.
        push(@files, $a);
        $flang{$a} = $lang || filename2lang($a);
+    } elsif ( $a eq '-' ) {
+       # gcc gets its input from stdin
+       push(@files, $a);
+       # prevent setting -x
+       $flang{$a} = 'stdin'
     } elsif ( $a =~ /^-print-klibc-(.*)$/ ) {
        # This test must precede -print
        if ( defined($conf{$1}) ) {
diff --git a/klibc/klibc/Kbuild b/klibc/klibc/Kbuild
new file mode 100644 (file)
index 0000000..be239a6
--- /dev/null
@@ -0,0 +1,149 @@
+#
+# Kbuild file for klibc
+#
+
+libc-y := vsnprintf.o snprintf.o vsprintf.o sprintf.o \
+         asprintf.o vasprintf.o \
+         vsscanf.o sscanf.o ctypes.o \
+         strntoumax.o strntoimax.o \
+         atoi.o atol.o atoll.o \
+         strtol.o strtoll.o strtoul.o strtoull.o \
+         strtoimax.o strtoumax.o \
+         globals.o exitc.o atexit.o onexit.o \
+         execl.o execle.o execv.o execvpe.o execvp.o execlp.o execlpe.o \
+         fork.o wait.o wait3.o waitpid.o system.o setpgrp.o getpgrp.o \
+         daemon.o \
+         printf.o vprintf.o fprintf.o vfprintf.o perror.o \
+         statfs.o fstatfs.o umount.o \
+         open.o fopen.o fread.o fread2.o fgetc.o fgets.o \
+         fwrite.o fwrite2.o fputc.o fputs.o puts.o putchar.o \
+         sleep.o usleep.o strtotimespec.o strtotimeval.o \
+         raise.o abort.o assert.o alarm.o pause.o \
+         __signal.o sysv_signal.o bsd_signal.o siglist.o siglongjmp.o \
+         sigaction.o sigpending.o sigprocmask.o sigsuspend.o \
+         brk.o sbrk.o malloc.o realloc.o calloc.o mmap.o \
+         memcpy.o memcmp.o memset.o memccpy.o memmem.o memswap.o \
+         memmove.o memchr.o memrchr.o \
+         strcasecmp.o strncasecmp.o strndup.o strerror.o \
+         strcat.o strchr.o strcmp.o strcpy.o strdup.o strlen.o strnlen.o \
+         strncat.o strlcpy.o strlcat.o \
+         strstr.o strncmp.o strncpy.o strrchr.o \
+         strxspn.o strspn.o strcspn.o strpbrk.o strsep.o strtok.o \
+         gethostname.o getdomainname.o getcwd.o \
+         seteuid.o setegid.o \
+         getenv.o setenv.o putenv.o __put_env.o unsetenv.o \
+         getopt.o readdir.o \
+         syslog.o closelog.o pty.o getpt.o isatty.o reboot.o \
+         time.o utime.o llseek.o nice.o getpriority.o \
+         qsort.o \
+         lrand48.o jrand48.o mrand48.o nrand48.o srand48.o seed48.o \
+         inet/inet_ntoa.o inet/inet_aton.o inet/inet_addr.o \
+         inet/inet_ntop.o inet/inet_pton.o inet/bindresvport.o \
+         send.o recv.o
+
+libc-$(CONFIG_KLIBC_ERRLIST) += errlist.o
+
+libc-$(CONFIG_KLIBC_ZLIB)    += \
+       zlib/adler32.o zlib/compress.o zlib/crc32.o zlib/gzio.o \
+       zlib/uncompr.o zlib/deflate.o zlib/trees.o zlib/zutil.o \
+       zlib/inflate.o zlib/infback.o zlib/inftrees.o zlib/inffast.o
+
+#####
+# Add any architecture-specific rules
+include $(obj)/arch/$(ARCH)/Makefile.inc
+
+#####
+# Shared definitions
+LIB      := libc.a
+SOLIB    := libc.so
+SOHASH   := klibc.so
+CRT0     := arch/$(ARCH)/crt0.o
+INTERP_O := interp.o
+
+always   := $(CRT0) $(LIB) $(SOLIB) $(SOHASH) $(INTERP_O)
+LIB      := $(call objectify,$(LIB))
+SOLIB    := $(call objectify,$(SOLIB))
+SOHASH   := $(call objectify,$(SOHASH))
+CRT0     := $(call objectify,$(CRT0))
+INTERP_O := $(call objectify,$(INTERP_O))
+
+targets  := arch/$(ARCH)/crt0.o
+targets  += $(libc-y) $(ARCHOBJS)
+
+# Generate syscall stubs
+subdir-y += syscalls
+# Generate socket calls stubs
+subdir-y += socketcalls
+
+# Tell make to descend before building libs
+$(obj)/syscalls/syscalls.o: $(obj)/syscalls
+$(obj)/socketcalls/socketcalls.o: $(obj)/socketcalls
+
+#####
+# Readable errormessages extracted from src..
+targets += errlist.c
+quiet_cmd_errlist = GEN     $@
+      cmd_errlist = $(PERL) $< $(LINUXINCLUDE) -errlist > $@ || rm -f $@
+
+$(obj)/errlist.c: $(srctree)/$(src)/makeerrlist.pl
+       $(call cmd,errlist)
+
+# full list of dependencies for klibc
+libc-deps = $(call objectify, $(libc-y) $(ARCHOBJS)) \
+            $(call objectify, syscalls/syscalls.o socketcalls/socketcalls.o)
+
+######
+# Build static library: libc.a
+targets += libc.a __static_init.o
+quiet_cmd_libc = USERAR  $@
+      cmd_libc = rm -f $@; \
+                 $(USERAR) cq $@ $(filter-out FORCE,$^); \
+                 $(USERRANLIB) $@
+
+$(LIB): $(call objectify,__static_init.o) $(libc-deps) FORCE
+       $(call if_changed,libc)
+
+######
+# Build shared library
+targets += libc.so __shared_init.o
+
+quiet_cmd_libcso = LD      $@
+      cmd_libcso = $(USERLD) $(USERLDFLAGS) $(USERSHAREDFLAGS) \
+                             -o $@ $(filter-out FORCE,$^) $(USERLIBGCC)
+
+$(SOLIB): $(CRT0) $(call objectify,__shared_init.o) $(libc-deps) FORCE
+       $(call if_changed,libcso)
+
+
+#####
+# Build sha1 hash values
+targets     += klibc.so libc.so.hash
+hostprogs-y := sha1hash
+
+quiet_cmd_solibhash = HASH    $@
+      cmd_solibhash = $(USERNM) $< | egrep '^[0-9a-fA-F]+ [ADRTW] ' | \
+                                        sort | $(obj)/sha1hash > $@
+$(SOLIB).hash: $(SOLIB) $(obj)/sha1hash FORCE
+       $(call if_changed,solibhash)
+
+quiet_cmd_sohash = GEN     $@
+      cmd_sohash = cat $< > $@;                                           \
+                     $(USERSTRIP) $(USERSTRIPFLAGS) $@;                   \
+                     rm -f $(obj)/klibc-??????????????????????.so;        \
+                     ln -f $@ $(obj)/klibc-`cat $(SOLIB).hash`.so
+$(SOHASH): $(SOLIB) $(SOLIB).hash
+       $(call cmd,sohash)
+
+
+#####
+# build interp.o
+targets += interp.o
+
+quiet_cmd_interp = BUILD  $@
+      cmd_interp = $(USERCC) $(usercflags) -D__ASSEMBLY__     \
+                             -DLIBDIR=\"$(SHLIBDIR)\"         \
+                            -DSOHASH=\"`cat $(SOLIB).hash`\" \
+                            -c -o $@ $<
+
+$(INTERP_O): $(obj)/interp.S $(SOLIB).hash
+       $(call if_changed,interp)
index e8b9a7f..11d8c9a 100644 (file)
@@ -145,17 +145,29 @@ ssize_t pwrite64,pwrite::pwrite(int, void *, size_t, off_t)
 ;
 ; Signal operations
 ;
-int kill(pid_t, int)
 ; We really should get rid of the non-rt_* of these, but that takes
-; sanitizing <signal.h> for all architectures, sigh...
-<?> int sigaction(int, const struct sigaction *, struct sigaction *)
-<?> int sigsuspend(const sigset_t *)
-<?> int sigpending(sigset_t *)
-<?> int sigprocmask(int, const sigset_t *, sigset_t *)
-int rt_sigaction(int, const struct sigaction *, struct sigaction *, size_t)
-int rt_sigsuspend(const sigset_t *, size_t)
-int rt_sigpending(sigset_t *, size_t)
-int rt_sigprocmask(int, const sigset_t *, sigset_t *, size_t)
+; sanitizing <signal.h> for all architectures, sigh.
+#ifdef __NR_sigaction
+int sigaction::__sigaction(int, const struct sigaction *, struct sigaction *)
+#else
+int rt_sigaction::__rt_sigaction(int, const struct sigaction *, struct sigaction *, size_t)
+#endif
+#ifdef __NR_sigsuspend
+int sigsuspend(const sigset_t *)
+#else
+int rt_sigsuspend::__rt_sigsuspend(const sigset_t *, size_t)
+#endif
+#ifdef __NR_sigpending
+int sigpending(sigset_t *)
+#else
+int rt_sigpending::__rt_sigpending(sigset_t *, size_t)
+#endif
+#ifdef __NR_sigprocmask
+int sigprocmask(int, const sigset_t *, sigset_t *)
+#else
+int rt_sigprocmask::__rt_sigprocmask(int, const sigset_t *, sigset_t *, size_t)
+#endif
+int kill(pid_t, int)
 <?> unsigned int alarm(unsigned int)
 int getitimer(int, struct itimerval *)
 int setitimer(int, const struct itimerval *, struct itimerval *)
index 5c320cb..80344bd 100644 (file)
@@ -13,6 +13,7 @@ ARCHOBJS = \
        arch/$(ARCH)/setjmp.o \
        arch/$(ARCH)/syscall.o \
        arch/$(ARCH)/open.o \
+       arch/$(ARCH)/sigreturn.o \
        arch/$(ARCH)/libgcc/__ashldi3.o \
        arch/$(ARCH)/libgcc/__ashrdi3.o \
        arch/$(ARCH)/libgcc/__lshrdi3.o \
diff --git a/klibc/klibc/arch/i386/sigreturn.S b/klibc/klibc/arch/i386/sigreturn.S
new file mode 100644 (file)
index 0000000..f2a3241
--- /dev/null
@@ -0,0 +1,15 @@
+#
+# arch/i386/sigreturn.S
+#
+
+#include <asm/unistd.h>
+
+       .text
+       .align 4
+       .globl __sigreturn
+       .type __sigreturn,@function
+__sigreturn:
+       pop     %eax            # Have no idea why this is needed...
+       movl    $__NR_sigreturn,%eax
+       int     $0x80
+       .size __sigreturn,.-__sigreturn
index 13b6e39..c9b5da8 100644 (file)
 # debugging using gdb.
 #
 ARCHREQFLAGS = -m64
+ifeq ($(DEBUG),y)
+OPTFLAGS     = -Os -fomit-frame-pointer \
+               -falign-functions=0 -falign-jumps=0 -falign-loops=0
+else
 OPTFLAGS     = -Os -fno-asynchronous-unwind-tables -fomit-frame-pointer \
                -falign-functions=0 -falign-jumps=0 -falign-loops=0
+endif
 BITSIZE      = 64
 LDFLAGS      = -m elf_x86_64
 
index d6cc120..26d880d 100644 (file)
@@ -10,7 +10,8 @@
 ARCHOBJS = \
        arch/$(ARCH)/exits.o \
        arch/$(ARCH)/setjmp.o \
-       arch/$(ARCH)/syscall.o
+       arch/$(ARCH)/syscall.o \
+       arch/$(ARCH)/sigreturn.o
 
 ARCHSOOBJS = $(patsubst %.o,%.lo,$(ARCHOBJS))
 
diff --git a/klibc/klibc/arch/x86_64/sigreturn.S b/klibc/klibc/arch/x86_64/sigreturn.S
new file mode 100644 (file)
index 0000000..66e7152
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * arch/x86_64/sigreturn.S
+ */
+
+#include <asm/unistd.h>
+       
+       .text
+       .align  4
+       .globl  __sigreturn
+       .type   __sigreturn,@function
+__sigreturn:
+       movl    $__NR_rt_sigreturn,%eax
+       syscall
+
+       .size   __sigreturn,.-__sigreturn
index f2c74ae..1797797 100644 (file)
@@ -15,14 +15,14 @@ __syscall_common:
        syscall
 
        cmpq    $-4095,%rax
-       jb      1f
+       jnb     1f
+       ret
 
        # Error return, must set errno
+1:
        negl    %eax
        movl    %eax,errno(%rip)        # errno is type int, so 32 bits
        orq     $-1,%rax                # orq $-1 smaller than movq $-1
-
-1:
        ret
 
        .size   __syscall_common,.-__syscall_common
index 88a145a..72f8a13 100644 (file)
@@ -20,6 +20,7 @@ char *fgets(char *s, int n, FILE *f)
       return NULL;
     }
     *p++ = ch;
+    n--;
     if ( ch == '\n' )
       break;
   }
index 819ffd4..85f42a2 100644 (file)
@@ -5,11 +5,40 @@
 #include <signal.h>
 #include <sys/syscall.h>
 
-#ifndef __NR_sigaction
+__extern void __sigreturn(void);
+__extern int __sigaction(int, const struct sigaction *, struct sigaction *);
+__extern int __rt_sigaction(int, const struct sigaction *, struct sigaction *, size_t);
 
 int sigaction(int sig, const struct sigaction *act, struct sigaction *oact)
 {
-  return rt_sigaction(sig, act, oact, sizeof(sigset_t));
-}
+  int rv;
+
+#if defined(__i386__) || defined(__x86_64__)
+  /* x86-64, and the Fedora i386 kernel, are broken without SA_RESTORER */
+  struct sigaction sa;
+
+  if ( act && !(act->sa_flags & SA_RESTORER) ) {
+    sa = *act;
+    act = &sa;
+
+    /* The kernel can't be trusted to have a valid default restorer */
+    sa.sa_flags |= SA_RESTORER;
+    sa.sa_restorer = &__sigreturn;
+  }
+#endif
 
+#ifdef __NR_sigaction
+  rv = __sigaction(sig, act, oact);
+#else
+  rv = __rt_sigaction(sig, act, oact, sizeof(sigset_t));
 #endif
+
+
+#if defined(__i386__) || defined(__x86_64__)
+  if ( oact && (oact->sa_restorer == &__sigreturn) ) {
+    oact->sa_flags &= ~SA_RESTORER;
+  }
+#endif
+
+  return rv;
+}
index 76d2b1a..decfe32 100644 (file)
@@ -7,9 +7,11 @@
 
 #ifndef __NR_sigpending
 
+__extern __rt_sigpending(sigset_t *, size_t);
+
 int sigpending(sigset_t *set)
 {
-  return rt_sigpending(set, sizeof(sigset_t));
+  return __rt_sigpending(set, sizeof(sigset_t));
 }
 
 #endif
index b5e58b2..372e0fd 100644 (file)
@@ -7,9 +7,11 @@
 
 #ifndef __NR_sigprocmask
 
+__extern __rt_sigprocmask(int, const sigset_t *, sigset_t *, size_t);
+
 int sigprocmask(int how, const sigset_t *set, sigset_t *oset)
 {
-  return rt_sigprocmask(how, set, oset, sizeof(sigset_t));
+  return __rt_sigprocmask(how, set, oset, sizeof(sigset_t));
 }
 
 #endif
index a927999..22f9a46 100644 (file)
@@ -7,9 +7,11 @@
 
 #ifndef __NR_sigsuspend
 
+__extern int __rt_sigsuspend(const sigset_t *, size_t);
+
 int sigsuspend(const sigset_t *mask)
 {
-  return rt_sigsuspend(mask, sizeof *mask);
+  return __rt_sigsuspend(mask, sizeof *mask);
 }
 
 #endif
index 4e30637..4c47fe8 100644 (file)
@@ -33,12 +33,13 @@ uintmax_t strntoumax(const char *nptr, char **endptr, int base, size_t n)
   }
 
   /* Single optional + or - */
-  if ( n && *nptr == '-' ) {
-    minus = 1;
-    nptr++;
-    n--;
-  } else if ( n && *nptr == '+' ) {
-    nptr++;
+  if ( n ) {
+    char c = *nptr;
+    if ( c == '-' || c == '+' ) {
+      minus = (c == '-');
+      nptr++;
+      n--;
+    }
   }
 
   if ( base == 0 ) {
old mode 100644 (file)
new mode 100755 (executable)
index 238d6e8..5b09c67 100644 (file)
@@ -1 +1 @@
-1.0.7
+1.0.14
old mode 100644 (file)
new mode 100755 (executable)
index d1e13e6..b653185
@@ -1,6 +1,6 @@
 #/bin/sh
 
-EXTRAS="extras/chassis_id extras/scsi_id extras/volume_id"
+EXTRAS="extras/chassis_id extras/scsi_id extras/volume_id extras/run_directory"
 
 [ -z "$KERNEL_DIR" ] && KERNEL_DIR=/lib/modules/`uname -r`/build
 echo KERNEL_DIR: "$KERNEL_DIR"
index 3172d33..9225352 100755 (executable)
@@ -241,7 +241,7 @@ BUS=="scsi", ID=="0:0:0:0", NAME="first_disk%n"
 EOF
        },
        {
-               desc            => "test NAME substitution chars",
+               desc            => "test substitution chars",
                subsys          => "block",
                devpath         => "/block/sda/sda3",
                exp_name        => "Major:8:minor:3:kernelnumber:3:bus:0:0:0:0" ,
@@ -250,7 +250,7 @@ BUS=="scsi", ID=="0:0:0:0", NAME="Major:%M:minor:%m:kernelnumber:%n:bus:%b"
 EOF
        },
        {
-               desc            => "test NAME substitution chars (with length limit)",
+               desc            => "test substitution chars (with length limit)",
                subsys          => "block",
                devpath         => "/block/sda/sda3",
                exp_name        => "M8-m3-n3-b0:0-sIBM" ,
@@ -363,6 +363,51 @@ BUS=="scsi", PROGRAM=="/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9", KERNEL=
 EOF
        },
        {
+               desc            => "test substitution by variable name",
+               subsys          => "block",
+               devpath         => "/block/sda/sda3",
+               exp_name        => "Major:8-minor:3-kernelnumber:3-bus:0:0:0:0" ,
+               rules           => <<EOF
+BUS=="scsi", ID=="0:0:0:0", NAME="Major:\$major-minor:\$minor-kernelnumber:\$number-bus:\$id"
+EOF
+       },
+       {
+               desc            => "test substitution by variable name 2",
+               subsys          => "block",
+               devpath         => "/block/sda/sda3",
+               exp_name        => "Major:8-minor:3-kernelnumber:3-bus:0:0:0:0" ,
+               rules           => <<EOF
+BUS=="scsi", ID=="0:0:0:0", DEVPATH="*/sda/*", NAME="Major:\$major-minor:%m-kernelnumber:\$number-bus:%b"
+EOF
+       },
+       {
+               desc            => "test substitution by variable name 3",
+               subsys          => "block",
+               devpath         => "/block/sda/sda3",
+               exp_name        => "830:0:0:03" ,
+               rules           => <<EOF
+BUS=="scsi", ID=="0:0:0:0", DEVPATH="*/sda/*", NAME="%M%m%b%n"
+EOF
+       },
+       {
+               desc            => "test substitution by variable name 4",
+               subsys          => "block",
+               devpath         => "/block/sda/sda3",
+               exp_name        => "833" ,
+               rules           => <<EOF
+BUS=="scsi", ID=="0:0:0:0", DEVPATH="*/sda/*", NAME="\$major\$minor\$number"
+EOF
+       },
+       {
+               desc            => "test substitution by variable name 5",
+               subsys          => "block",
+               devpath         => "/block/sda/sda3",
+               exp_name        => "8330:0:0:0" ,
+               rules           => <<EOF
+BUS=="scsi", ID=="0:0:0:0", DEVPATH="*/sda/*", NAME="\$major%m%n\$id"
+EOF
+       },
+       {
                desc            => "invalid program for device with no bus",
                subsys          => "tty",
                devpath         => "/class/tty/console",
@@ -783,9 +828,9 @@ EOF
                exp_name        => "symlink-only2",
                exp_target      => "link",
                rules           => <<EOF
-BUS=="scsi", KERNEL=="sda", SYMLINK="symlink-only1"
-BUS=="scsi", KERNEL=="sda", SYMLINK="symlink-only2"
-BUS=="scsi", KERNEL=="sda", NAME="link", SYMLINK="symlink0"
+BUS=="scsi", KERNEL=="sda", SYMLINK+="symlink-only1"
+BUS=="scsi", KERNEL=="sda", SYMLINK+="symlink-only2"
+BUS=="scsi", KERNEL=="sda", NAME="link", SYMLINK+="symlink0"
 EOF
        },
        {
@@ -797,7 +842,7 @@ EOF
                exp_add_error   => "yes",
                exp_rem_error   => "yes",
                rules           => <<EOF
-BUS=="scsi", KERNEL=="sda", NAME="link", SYMLINK="."
+BUS=="scsi", KERNEL=="sda", NAME="link", SYMLINK+="."
 EOF
        },
        {
@@ -809,7 +854,7 @@ EOF
                exp_rem_error   => "yes",
                option          => "clean",
                rules           => <<EOF
-KERNEL=="tty0", NAME="link", SYMLINK="link"
+KERNEL=="tty0", NAME="link", SYMLINK+="link"
 EOF
        },
        {
@@ -819,7 +864,7 @@ EOF
                exp_name        => "symlink0",
                exp_target      => "ttyUSB0",
                rules           => <<EOF
-KERNEL=="ttyUSB[0-9]*", NAME="ttyUSB%n", SYMLINK="symlink%n"
+KERNEL=="ttyUSB[0-9]*", NAME="ttyUSB%n", SYMLINK+="symlink%n"
 EOF
        },
        {
@@ -829,7 +874,7 @@ EOF
                exp_name        => "symlink-ttyUSB0",
                exp_target      => "ttyUSB0",
                rules           => <<EOF
-KERNEL=="ttyUSB[0-9]*", NAME="ttyUSB%n", SYMLINK="symlink-%k"
+KERNEL=="ttyUSB[0-9]*", NAME="ttyUSB%n", SYMLINK+="symlink-%k"
 EOF
        },
        {
@@ -839,7 +884,7 @@ EOF
                exp_name        => "major-188:0",
                exp_target      => "ttyUSB0",
                rules           => <<EOF
-KERNEL=="ttyUSB[0-9]*", NAME="ttyUSB%n", SYMLINK="major-%M:%m"
+KERNEL=="ttyUSB[0-9]*", NAME="ttyUSB%n", SYMLINK+="major-%M:%m"
 EOF
        },
        {
@@ -849,7 +894,7 @@ EOF
                exp_name        => "symlink-0:0:0:0",
                exp_target      => "node",
                rules           => <<EOF
-BUS=="scsi", KERNEL=="sda", NAME="node", SYMLINK="symlink-%b"
+BUS=="scsi", KERNEL=="sda", NAME="node", SYMLINK+="symlink-%b"
 EOF
        },
        {
@@ -859,7 +904,7 @@ EOF
                exp_name        => "test",
                exp_target      => "ttyUSB0",
                rules           => <<EOF
-KERNEL=="ttyUSB[0-9]*", PROGRAM=="/bin/echo test" NAME="ttyUSB%n", SYMLINK="%c"
+KERNEL=="ttyUSB[0-9]*", PROGRAM=="/bin/echo test" NAME="ttyUSB%n", SYMLINK+="%c"
 EOF
        },
        {
@@ -869,7 +914,7 @@ EOF
                exp_name        => "test",
                exp_target      => "ttyUSB0",
                rules           => <<EOF
-KERNEL=="ttyUSB[0-9]*", PROGRAM=="/bin/echo symlink test this" NAME="ttyUSB%n", SYMLINK="%c{2}"
+KERNEL=="ttyUSB[0-9]*", PROGRAM=="/bin/echo symlink test this" NAME="ttyUSB%n", SYMLINK+="%c{2}"
 EOF
        },
        {
@@ -879,7 +924,7 @@ EOF
                exp_name        => "this",
                exp_target      => "ttyUSB0",
                rules           => <<EOF
-KERNEL=="ttyUSB[0-9]*", PROGRAM=="/bin/echo symlink test this" NAME="ttyUSB%n", SYMLINK="%c{2+}"
+KERNEL=="ttyUSB[0-9]*", PROGRAM=="/bin/echo symlink test this" NAME="ttyUSB%n", SYMLINK+="%c{2+}"
 EOF
        },
        {
@@ -889,8 +934,8 @@ EOF
                exp_name        => "test",
                exp_target      => "link",
                rules           => <<EOF
-BUS=="scsi", KERNEL=="sda", PROGRAM=="/bin/echo link test this" SYMLINK="%c{2+}"
-BUS=="scsi", KERNEL=="sda", NAME="link", SYMLINK="symlink0"
+BUS=="scsi", KERNEL=="sda", PROGRAM=="/bin/echo link test this" SYMLINK+="%c{2+}"
+BUS=="scsi", KERNEL=="sda", NAME="link", SYMLINK+="symlink0"
 EOF
        },
        {
@@ -900,7 +945,7 @@ EOF
                exp_name        => "188:0",
                exp_target      => "ttyUSB0",
                rules           => <<EOF
-KERNEL=="ttyUSB[0-9]*", NAME="ttyUSB%n", SYMLINK="%s{dev}"
+KERNEL=="ttyUSB[0-9]*", NAME="ttyUSB%n", SYMLINK+="%s{dev}"
 EOF
        },
        {
@@ -910,7 +955,7 @@ EOF
                exp_name        => "188",
                exp_target      => "ttyUSB0",
                rules           => <<EOF
-KERNEL=="ttyUSB[0-9]*", NAME="ttyUSB%n", SYMLINK="%3s{dev}"
+KERNEL=="ttyUSB[0-9]*", NAME="ttyUSB%n", SYMLINK+="%3s{dev}"
 EOF
        },
        {
@@ -920,7 +965,7 @@ EOF
                exp_name        => "percent%sign",
                exp_target      => "ttyUSB0",
                rules           => <<EOF
-KERNEL=="ttyUSB[0-9]*", NAME="ttyUSB%n", SYMLINK="percent%%sign"
+KERNEL=="ttyUSB[0-9]*", NAME="ttyUSB%n", SYMLINK+="percent%%sign"
 EOF
        },
        {
@@ -930,7 +975,7 @@ EOF
                exp_name        => "%ttyUSB0_name",
                exp_target      => "ttyUSB0",
                rules           => <<EOF
-KERNEL=="ttyUSB[0-9]*", NAME="ttyUSB%n", SYMLINK="%%%k_name"
+KERNEL=="ttyUSB[0-9]*", NAME="ttyUSB%n", SYMLINK+="%%%k_name"
 EOF
        },
        {
@@ -940,7 +985,7 @@ EOF
                exp_name        => "link1",
                exp_target      => "node",
                rules           => <<EOF
-BUS=="scsi", PROGRAM=="/bin/echo -n node link1 link2", RESULT=="node *", NAME="%c{1}", SYMLINK="%c{2} %c{3}"
+BUS=="scsi", PROGRAM=="/bin/echo -n node link1 link2", RESULT=="node *", NAME="%c{1}", SYMLINK+="%c{2} %c{3}"
 EOF
        },
        {
@@ -950,7 +995,7 @@ EOF
                exp_name        => "link4",
                exp_target      => "node",
                rules           => <<EOF
-BUS=="scsi", PROGRAM=="/bin/echo -n node link1 link2 link3 link4", RESULT=="node *", NAME="%c{1}", SYMLINK="%c{2+}"
+BUS=="scsi", PROGRAM=="/bin/echo -n node link1 link2 link3 link4", RESULT=="node *", NAME="%c{1}", SYMLINK+="%c{2+}"
 EOF
        },
        {
@@ -1147,7 +1192,7 @@ EOF
                devpath         => "/block/sda/sda1",
                exp_name        => "last",
                rules           => <<EOF
-BUS=="scsi", KERNEL=="sda1", SYMLINK="last", OPTIONS="last_rule"
+BUS=="scsi", KERNEL=="sda1", SYMLINK+="last", OPTIONS="last_rule"
 BUS=="scsi", KERNEL=="sda1", NAME="very-last"
 EOF
        },
@@ -1290,7 +1335,7 @@ EOF
                exp_rem_error   => "yes",
                option          => "clean",
                rules           => <<EOF
-KERNEL=="sda", NAME="ok", RUN+="/bin/sh -c 'ln -s `basename \$DEVNAME` %r/testsymlink'"
+KERNEL=="sda", NAME="ok", RUN+="/bin/sh -c 'ln -s `basename \$\$DEVNAME` %r/testsymlink'"
 KERNEL=="sda", NAME="not-ok"
 EOF
        },
@@ -1306,6 +1351,43 @@ KERNEL=="sda", ACTION=="remove", RUN+="/bin/rm -f %r/testsymlink2"
 KERNEL=="sda", NAME="not-ok2"
 EOF
        },
+       {
+               desc            => "final assignment",
+               subsys          => "block",
+               devpath         => "/block/sda",
+               exp_name        => "ok",
+               exp_perms       => "root:nobody:0640",
+               rules           => <<EOF
+KERNEL=="sda", GROUP:="nobody"
+KERNEL=="sda", GROUP="not-ok", MODE="0640", NAME="ok"
+EOF
+       },
+       {
+               desc            => "final assignment",
+               subsys          => "block",
+               devpath         => "/block/sda",
+               exp_name        => "ok",
+               exp_perms       => "root:nobody:0640",
+               rules           => <<EOF
+KERNEL=="sda", GROUP:="nobody"
+SUBSYSTEM=="block", MODE:="640"
+KERNEL=="sda", GROUP="not-ok", MODE="0666", NAME="ok"
+EOF
+       },
+       {
+               desc            => "reset list to current value",
+               subsys          => "tty",
+               devpath         => "/class/tty/ttyUSB0",
+               exp_name        => "three",
+               not_exp_name    => "two",
+               exp_target      => "node",
+               rules           => <<EOF
+KERNEL=="ttyUSB[0-9]*", SYMLINK+="one"
+KERNEL=="ttyUSB[0-9]*", SYMLINK+="two"
+KERNEL=="ttyUSB[0-9]*", SYMLINK="three"
+KERNEL=="ttyUSB[0-9]*", NAME="node"
+EOF
+       },
 );
 
 # set env
@@ -1443,6 +1525,13 @@ sub run_test {
                my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
                    $atime, $mtime, $ctime, $blksize, $blocks) = stat("$PWD/$udev_root$rules->{exp_name}");
 
+               if (defined($rules->{not_exp_name})) {
+                       if ((-e "$PWD/$udev_root$rules->{not_exp_name}") ||
+                           (-l "$PWD/$udev_root$rules->{not_exp_name}")) {
+                               print "nonexistent: error \'$rules->{not_exp_name}\' not expected to be there\n";
+                               $error++
+                       }
+               }
                if (defined($rules->{exp_perms})) {
                        permissions_test($rules, $uid, $gid, $mode);
                }
index 5be9308..b4edd5a 100644 (file)
--- a/udev.8.in
+++ b/udev.8.in
@@ -104,6 +104,36 @@ Every rule consists of a list of comma separated key value fields:
 .sp
 .IR "key " ,[ "key " ,...]
 .P
+Each key has the following format:
+.sp
+.IR "name op value"
+.P
+There are distinct key operation types, depending on the type of the key, it
+does a comparison or an assignment.
+.P
+Comparison operators are:
+.TP
+.B ==
+Compare for equality.
+.TP
+.B !=
+Compare for non-equality.
+.P
+Assignment operators are:
+.TP
+.B +=
+Add the value to a key that holds a list of entries.
+.TP
+.B :=
+Assign a value to a key finally; disallow any later changes, which
+is useful to prevent changes by any later rules.
+.TP
+.B =
+Asign a value to a key. Keys that represent a list, are reset and only this
+single value is assigned.  While this operator still works inplicitely as
+comparison on keys that can't get a value assigned, its usage as an comparison
+operator is deprecated.
+.P
 The following key names can be used to match against device properties:
 .TP
 .B BUS
@@ -158,7 +188,8 @@ The following keys can get values assigned:
 .TP
 .B NAME
 The name of the node to be created, or the name, the network interface
-should be renamed to.
+should be renamed to. Only one rule can set the a name, all later rules
+with a NAME key will be ignored.
 .TP
 .B SYMLINK
 The name of a symlink targeting the node. Every matching rule can add
@@ -305,11 +336,9 @@ following the '[' is a '!', any characters not enclosed are matched.
 .P
 After device node creation, removal, or network device renaming,
 .B udev
-executes the programs located in the directory tree under
-.IR /etc/dev.d/ .
-The name of a program must have the suffix
-.I .dev
-to be recognized.
+executes the programs specified by the
+.B RUN
+key.
 .br
 In addition to the kernel provided hotplug environment variables,
 .B UDEV_LOG
@@ -318,15 +347,7 @@ is set and contains the numerical priority value, if udev is configured to use
 Executed programs may want to follow that setting.
 .B DEVNAME
 is exported to make the name of the created node, or the name the network
-device is renamed to, available to the executed program. The programs in every
-directory are sorted in lexical order, while the directories are searched in
-the following order:
-.sp
-.nf
-/etc/dev.d/$(DEVNAME)/*.dev
-/etc/dev.d/$(SUBSYSTEM)/*.dev
-/etc/dev.d/default/*.dev
-.fi
+device is renamed to, available to the executed programs.
 .SH "ENVIRONMENT"
 .P
 The following variables are read from the environment:
@@ -352,20 +373,10 @@ Overrides the log priority specified in the config file.
 .TP
 .B UDEV_RUN
 If set to "0", it disables the execution of programs added by rules.
-.TP
-.B UDEV_NO_DEVD
-The default behavior of
-.B udev
-is to execute programs in the
-.I /etc/dev.d/
-directory after device handling. If set,
-.B udev
-will skip this step.
 .SH "FILES"
 .nf
 /sbin/udev                           udev program
 /etc/udev/*                          udev config files
-/etc/dev.d/*                         programs invoked by udev
 .fi
 .SH "SEE ALSO"
 .BR udevinfo (8),
diff --git a/udev.c b/udev.c
index ce9b42f..67d081d 100644 (file)
--- a/udev.c
+++ b/udev.c
@@ -54,35 +54,6 @@ void log_message(int priority, const char *format, ...)
 }
 #endif
 
-/* decide if we should manage the whole hotplug event
- * for now look if the kernel calls udevsend instead of /sbin/hotplug
- */
-static int manage_hotplug_event(void) {
-       char helper[256];
-       int fd;
-       int len;
-
-       /* don't handle hotplug.d if we are called directly */
-       if (!getenv("UDEVD_EVENT"))
-               return 0;
-
-       fd = open("/proc/sys/kernel/hotplug", O_RDONLY);
-       if (fd < 0)
-               return 0;
-
-       len = read(fd, helper, 256);
-       close(fd);
-
-       if (len < 0)
-               return 0;
-       helper[len] = '\0';
-
-       if (strstr(helper, "udevsend"))
-               return 1;
-
-       return 0;
-}
-
 static void asmlinkage sig_handler(int signum)
 {
        switch (signum) {
@@ -96,15 +67,12 @@ static void asmlinkage sig_handler(int signum)
 
 int main(int argc, char *argv[], char *envp[])
 {
-       struct sysfs_class_device *class_dev;
-       struct sysfs_device *devices_dev;
        struct udevice udev;
        char path[PATH_SIZE];
        const char *error;
        const char *action;
        const char *devpath;
        const char *subsystem;
-       int managed_event;
        struct sigaction act;
        int retval = -EINVAL;
 
@@ -129,11 +97,6 @@ int main(int argc, char *argv[], char *envp[])
        /* trigger timeout to prevent hanging processes */
        alarm(ALARM_TIMEOUT);
 
-       /* let the executed programs know if we handle the whole hotplug event */
-       managed_event = manage_hotplug_event();
-       if (managed_event)
-               setenv("MANAGED_EVENT", "1", 1);
-
        action = getenv("ACTION");
        devpath = getenv("DEVPATH");
        subsystem = getenv("SUBSYSTEM");
@@ -141,14 +104,12 @@ int main(int argc, char *argv[], char *envp[])
        if (!subsystem && argc == 2)
                subsystem = argv[1];
 
-       udev_init_device(&udev, devpath, subsystem, action);
-
        if (!action || !subsystem || !devpath) {
                err("action, subsystem or devpath missing");
-               goto hotplug;
+               goto exit;
        }
 
-       /* export logging flag, as called scripts may want to do the same as udev */
+       /* export log_priority , as called programs may want to do the same as udev */
        if (udev_log_priority) {
                char priority[32];
 
@@ -156,99 +117,112 @@ int main(int argc, char *argv[], char *envp[])
                setenv("UDEV_LOG", priority, 1);
        }
 
-       if (udev.type == DEV_BLOCK || udev.type == DEV_CLASS || udev.type == DEV_NET) {
-               udev_rules_init();
+       udev_init_device(&udev, devpath, subsystem, action);
+       udev_rules_init();
 
+       if (udev.type == DEV_BLOCK || udev.type == DEV_CLASS || udev.type == DEV_NET) {
+               /* handle device node */
                if (strcmp(action, "add") == 0) {
-                       /* wait for sysfs and possibly add node */
-                       dbg("udev add");
-
-                       /* skip subsystems without "dev", but handle net devices */
-                       if (udev.type != DEV_NET && subsystem_expect_no_dev(udev.subsystem)) {
-                               dbg("don't care about '%s' devices", udev.subsystem);
-                               goto hotplug;
-                       }
+                       struct sysfs_class_device *class_dev;
 
+                       /* wait for sysfs of /sys/class /sys/block */
+                       dbg("node add");
                        snprintf(path, sizeof(path), "%s%s", sysfs_path, udev.devpath);
                        path[sizeof(path)-1] = '\0';
                        class_dev = wait_class_device_open(path);
                        if (class_dev == NULL) {
                                dbg("open class device failed");
-                               goto hotplug;
+                               goto run;
                        }
                        dbg("opened class_dev->name='%s'", class_dev->name);
-
                        wait_for_class_device(class_dev, &error);
 
-                       /* name, create node, store in db */
-                       retval = udev_add_device(&udev, class_dev);
-
+                       /* get major/minor */
+                       if (udev.type == DEV_BLOCK || udev.type == DEV_CLASS)
+                               udev.devt = get_devt(class_dev);
+
+                       if (udev.type == DEV_NET || udev.devt) {
+                               /* name device */
+                               udev_rules_get_name(&udev, class_dev);
+                               if (udev.ignore_device) {
+                                       info("device event will be ignored");
+                                       goto cleanup;
+                               }
+                               if (udev.name[0] == '\0') {
+                                       info("device node creation supressed");
+                                       goto cleanup;
+                               }
+                               
+                               /* create node, store in db */
+                               retval = udev_add_device(&udev, class_dev);
+                       } else {
+                               dbg("no dev-file found");
+                               udev_rules_get_run(&udev, NULL);
+                               if (udev.ignore_device) {
+                                       info("device event will be ignored");
+                                       goto cleanup;
+                               }
+                       }
                        sysfs_close_class_device(class_dev);
                } else if (strcmp(action, "remove") == 0) {
-                       /* possibly remove a node */
-                       dbg("udev remove");
-
-                       /* skip subsystems without "dev" */
-                       if (subsystem_expect_no_dev(udev.subsystem)) {
-                               dbg("don't care about '%s' devices", udev.subsystem);
-                               goto hotplug;
-                       }
-
-                       udev_rules_get_run(&udev);
+                       dbg("node remove");
+                       udev_rules_get_run(&udev, NULL);
                        if (udev.ignore_device) {
                                dbg("device event will be ignored");
-                               goto hotplug;
+                               goto cleanup;
                        }
 
-                       /* get node from db, remove db-entry, delete created node */
+                       /* get name from db, remove db-entry, delete node */
                        retval = udev_remove_device(&udev);
                }
 
+               /* export name of device node or netif */
                if (udev.devname[0] != '\0')
                        setenv("DEVNAME", udev.devname, 1);
-
-               if (udev_run && !list_empty(&udev.run_list)) {
-                       struct name_entry *name_loop;
-
-                       dbg("executing run list");
-                       list_for_each_entry(name_loop, &udev.run_list, node)
-                               execute_command(name_loop->name, udev.subsystem);
-               }
-
-               /* run dev.d/ scripts if we created/deleted a node or changed a netif name */
-               if (udev_dev_d && udev.devname[0] != '\0')
-                       udev_multiplex_directory(&udev, DEVD_DIR, DEVD_SUFFIX);
-
        } else if (udev.type == DEV_DEVICE) {
                if (strcmp(action, "add") == 0) {
-                       /* wait for sysfs */
-                       dbg("devices add");
+                       struct sysfs_device *devices_dev;
 
+                       /* wait for sysfs of /sys/devices/ */
+                       dbg("devices add");
                        snprintf(path, sizeof(path), "%s%s", sysfs_path, devpath);
                        path[sizeof(path)-1] = '\0';
                        devices_dev = wait_devices_device_open(path);
                        if (!devices_dev) {
                                dbg("devices device unavailable (probably remove has beaten us)");
-                               goto hotplug;
+                               goto run;
                        }
                        dbg("devices device opened '%s'", path);
-
                        wait_for_devices_device(devices_dev, &error);
-
+                       udev_rules_get_run(&udev, devices_dev);
                        sysfs_close_device(devices_dev);
-               } else if (strcmp(action, "remove") == 0) {
-                       dbg("devices remove");
+                       if (udev.ignore_device) {
+                               info("device event will be ignored");
+                               goto cleanup;
+                       }
                }
        } else {
-               dbg("unhandled");
+               dbg("default handling");
+               udev_rules_get_run(&udev, NULL);
+               if (udev.ignore_device) {
+                       info("device event will be ignored");
+                       goto cleanup;
+               }
        }
 
-hotplug:
-       if (udev_hotplug_d && managed_event)
-               udev_multiplex_directory(&udev, HOTPLUGD_DIR, HOTPLUG_SUFFIX);
+run:
+       if (udev_run && !list_empty(&udev.run_list)) {
+               struct name_entry *name_loop;
+
+               dbg("executing run list");
+               list_for_each_entry(name_loop, &udev.run_list, node)
+                       execute_command(name_loop->name, udev.subsystem);
+       }
 
+cleanup:
        udev_cleanup_device(&udev);
 
+exit:
        logging_close();
        return retval;
 }
diff --git a/udev.h b/udev.h
index f7a65c6..11fbe02 100644 (file)
--- a/udev.h
+++ b/udev.h
 #define SEQNUM_SIZE                    32
 #define VALUE_SIZE                     128
 
-#define DEVD_DIR                       "/etc/dev.d"
-#define DEVD_SUFFIX                    ".dev"
-
-#define HOTPLUGD_DIR                   "/etc/hotplug.d"
-#define HOTPLUG_SUFFIX                 ".hotplug"
-
 #define DEFAULT_PARTITIONS_COUNT       15
 
 enum device_type {
@@ -62,13 +56,19 @@ struct udevice {
 
        enum device_type type;
        char name[PATH_SIZE];
+       int name_set;
        char devname[PATH_SIZE];
        struct list_head symlink_list;
+       int symlink_final;
        char owner[USER_SIZE];
+       int owner_final;
        char group[USER_SIZE];
+       int group_final;
        mode_t mode;
+       int mode_final;
        dev_t devt;
        struct list_head run_list;
+       int run_final;
 
        char tmp_node[PATH_SIZE];
        int partitions;
@@ -87,7 +87,6 @@ extern int udev_add_device(struct udevice *udev, struct sysfs_class_device *clas
 extern int udev_remove_device(struct udevice *udev);
 extern void udev_init_config(void);
 extern int udev_start(void);
-extern void udev_multiplex_directory(struct udevice *udev, const char *basedir, const char *suffix);
 extern int udev_make_node(struct udevice *udev, const char *file, dev_t devt, mode_t mode, uid_t uid, gid_t gid);
 
 extern char sysfs_path[PATH_SIZE];
@@ -97,7 +96,5 @@ extern char udev_config_filename[PATH_SIZE];
 extern char udev_rules_filename[PATH_SIZE];
 extern int udev_log_priority;
 extern int udev_run;
-extern int udev_dev_d;
-extern int udev_hotplug_d;
 
 #endif
index 28f1956..0430a12 100644 (file)
--- a/udev.spec
+++ b/udev.spec
@@ -112,9 +112,6 @@ rm -rf $RPM_BUILD_ROOT
 %attr(-,root,root) /etc/hotplug.d/default/udev.hotplug
 %attr(755,root,root) /etc/init.d/udev
 %attr(0644,root,root) %{_mandir}/man8/udev*.8*
-%attr(755,root,root) %dir /etc/dev.d/
-%attr(755,root,root) %dir /etc/dev.d/net/
-%attr(0755,root,root) /etc/dev.d/net/hotplug.dev
 
 %if %{scsi_id}
        %attr(755,root,root) /sbin/scsi_id
index 9764cb9..89af965 100644 (file)
@@ -52,12 +52,23 @@ int udev_make_node(struct udevice *udev, const char *file, dev_t devt, mode_t mo
        struct stat stats;
        int retval = 0;
 
+       switch (udev->type) {
+       case DEV_BLOCK:
+               mode |= S_IFBLK;
+               break;
+       case DEV_CLASS:
+               mode |= S_IFCHR;
+               break;
+       default:
+               dbg("unknown node type %c\n", udev->type);
+               return -EINVAL;
+       }
+
        if (stat(file, &stats) != 0)
                goto create;
 
        /* preserve node with already correct numbers, to not change the inode number */
-       if (((stats.st_mode & S_IFMT) == S_IFBLK || (stats.st_mode & S_IFMT) == S_IFCHR) &&
-           (stats.st_rdev == devt)) {
+       if ((stats.st_mode & S_IFMT) == (mode & S_IFMT) && (stats.st_rdev == devt)) {
                info("preserve file '%s', cause it has correct dev_t", file);
                selinux_setfilecon(file, udev->kernel_name, stats.st_mode);
                goto perms;
@@ -69,18 +80,6 @@ int udev_make_node(struct udevice *udev, const char *file, dev_t devt, mode_t mo
                dbg("already present file '%s' unlinked", file);
 
 create:
-       switch (udev->type) {
-       case DEV_BLOCK:
-               mode |= S_IFBLK;
-               break;
-       case DEV_CLASS:
-               mode |= S_IFCHR;
-               break;
-       default:
-               dbg("unknown node type %c\n", udev->type);
-               return -EINVAL;
-       }
-
        selinux_setfscreatecon(file, udev->kernel_name, mode);
        retval = mknod(file, mode, devt);
        selinux_resetfscreatecon();
@@ -268,22 +267,7 @@ int udev_add_device(struct udevice *udev, struct sysfs_class_device *class_dev)
        char *pos;
        int retval = 0;
 
-       if (udev->type == DEV_BLOCK || udev->type == DEV_CLASS) {
-               udev->devt = get_devt(class_dev);
-               if (!udev->devt) {
-                       dbg("no dev-file found, do nothing");
-                       return 0;
-               }
-       }
-
-       udev_rules_get_name(udev, class_dev);
-       if (udev->ignore_device) {
-               dbg("device event will be ignored");
-               return 0;
-       }
-
        dbg("adding name='%s'", udev->name);
-
        selinux_init();
 
        if (udev->type == DEV_BLOCK || udev->type == DEV_CLASS) {
@@ -325,6 +309,5 @@ int udev_add_device(struct udevice *udev, struct sysfs_class_device *class_dev)
 
 exit:
        selinux_exit();
-
        return retval;
 }
index 6bc070d..773ee67 100644 (file)
@@ -45,39 +45,6 @@ char udev_config_filename[PATH_SIZE];
 char udev_rules_filename[PATH_SIZE];
 int udev_log_priority;
 int udev_run;
-int udev_dev_d;
-int udev_hotplug_d;
-
-static 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;
-}
-
-static 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;
-}
 
 static int get_key(char **line, char **key, char **value)
 {
@@ -219,8 +186,6 @@ void udev_init_config(void)
        strcpy(udev_rules_filename, UDEV_RULES_FILE);
        udev_log_priority = LOG_ERR;
        udev_run = 1;
-       udev_dev_d = 1;
-       udev_hotplug_d = 1;
        sysfs_get_mnt_path(sysfs_path, sizeof(sysfs_path));
 
        /* disable RUN key execution */
@@ -228,14 +193,6 @@ void udev_init_config(void)
        if (env && !string_is_true(env))
                udev_run = 0;
 
-       env = getenv("UDEV_NO_DEVD");
-       if (env && string_is_true(env))
-               udev_dev_d = 0;
-
-       env = getenv("UDEV_NO_HOTPLUGD");
-       if (env && string_is_true(env))
-               udev_hotplug_d = 0;
-
        env = getenv("UDEV_CONFIG_FILE");
        if (env) {
                strlcpy(udev_config_filename, env, sizeof(udev_config_filename));
diff --git a/udev_multiplex.c b/udev_multiplex.c
deleted file mode 100644 (file)
index 22bbaf7..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * udev_multiplex.c directory multiplexer
- * 
- * Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
- *
- *     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 essentially emulates the following shell script logic in C:
- *     DIR="/etc/dev.d"
- *     export DEVNAME="whatever_dev_name_udev_just_gave"
- *     for I in "${DIR}/$DEVNAME/"*.dev "${DIR}/$1/"*.dev "${DIR}/default/"*.dev ; do
- *             if [ -f $I ]; then $I $1 ; fi
- *     done
- *     exit 1;
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include "udev.h"
-#include "udev_libc_wrapper.h"
-#include "udev_utils.h"
-#include "logging.h"
-
-
-/* 
- * runs files in these directories in order:
- *     <node name given by udev>/
- *     subsystem/
- *     default/
- */
-void udev_multiplex_directory(struct udevice *udev, const char *basedir, const char *suffix)
-{
-       char dirname[PATH_SIZE];
-       struct name_entry *name_loop, *name_tmp;
-       LIST_HEAD(name_list);
-
-       /* chop the device name up into pieces based on '/' */
-       if (udev->name[0] != '\0') {
-               char devname[PATH_SIZE];
-               char *temp;
-
-               strlcpy(devname, udev->name, sizeof(devname));
-               temp = strchr(devname, '/');
-               while (temp != NULL) {
-                       temp[0] = '\0';
-
-                       /* don't call the subsystem directory here */
-                       if (strcmp(devname, udev->subsystem) != 0) {
-                               snprintf(dirname, sizeof(dirname), "%s/%s", basedir, devname);
-                               dirname[sizeof(dirname)-1] = '\0';
-                               add_matching_files(&name_list, dirname, suffix);
-                       }
-
-                       temp[0] = '/';
-                       ++temp;
-                       temp = strchr(temp, '/');
-               }
-       }
-
-       if (udev->name[0] != '\0') {
-               snprintf(dirname, sizeof(dirname), "%s/%s", basedir, udev->name);
-               dirname[sizeof(dirname)-1] = '\0';
-               add_matching_files(&name_list, dirname, suffix);
-       }
-
-       if (udev->subsystem[0] != '\0') {
-               snprintf(dirname, sizeof(dirname), "%s/%s", basedir, udev->subsystem);
-               dirname[sizeof(dirname)-1] = '\0';
-               add_matching_files(&name_list, dirname, suffix);
-       }
-
-       snprintf(dirname, sizeof(dirname), "%s/default", basedir);
-       dirname[sizeof(dirname)-1] = '\0';
-       add_matching_files(&name_list, dirname, suffix);
-
-       list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) {
-               execute_command(name_loop->name, udev->subsystem);
-               list_del(&name_loop->node);
-       }
-
-}
index 2df5553..cf28ff3 100644 (file)
@@ -140,26 +140,20 @@ static int delete_node(struct udevice *udev)
  */
 int udev_remove_device(struct udevice *udev)
 {
-       const char *temp;
-
        if (udev->type != DEV_BLOCK && udev->type != DEV_CLASS)
                return 0;
 
-       if (udev_db_get_device(udev, udev->devpath) == 0) {
-               if (udev->ignore_remove) {
-                       dbg("remove event for '%s' requested to be ignored by rule", udev->name);
-                       return 0;
-               }
-               dbg("remove name='%s'", udev->name);
-               udev_db_delete_device(udev);
-       } else {
-               /* fall back to kernel name */
-               temp = strrchr(udev->devpath, '/');
-               if (temp == NULL)
-                       return -ENODEV;
-               strlcpy(udev->name, &temp[1], sizeof(udev->name));
-               info("'%s' not found in database, falling back on default name", udev->name);
+       /* remove node only if we can find it in our database */
+       if (udev_db_get_device(udev, udev->devpath) != 0) {
+               dbg("'%s' not found in database, ignore event", udev->name);
+               return -1;
+       }
+       if (udev->ignore_remove) {
+               dbg("remove event for '%s' requested to be ignored by rule", udev->name);
+               return 0;
        }
+       dbg("remove name='%s'", udev->name);
+       udev_db_delete_device(udev);
 
        /* use full path to the environment */
        snprintf(udev->devname, sizeof(udev->devname), "%s/%s", udev_root, udev->name);
index 6f82fac..52e0712 100644 (file)
@@ -172,16 +172,17 @@ static int find_sysfs_attribute(struct sysfs_class_device *class_dev, struct sys
 
        dbg("look for device attribute '%s'", name);
        if (class_dev) {
+               dbg("look for class attribute '%s/%s'", class_dev->path, name);
                tmpattr = sysfs_get_classdev_attr(class_dev, name);
                if (tmpattr)
                        goto attr_found;
        }
        if (sysfs_device) {
+               dbg("look for devices attribute '%s/%s'", sysfs_device->path, name);
                tmpattr = sysfs_get_device_attr(sysfs_device, name);
                if (tmpattr)
                        goto attr_found;
        }
-
        return -1;
 
 attr_found:
@@ -197,56 +198,133 @@ static void apply_format(struct udevice *udev, char *string, size_t maxsize,
 {
        char temp[PATH_SIZE];
        char temp2[PATH_SIZE];
-       char *tail, *pos, *cpos, *attr, *rest;
+       char *head, *tail, *cpos, *attr, *rest;
        int len;
        int i;
-       char c;
        unsigned int next_free_number;
        struct sysfs_class_device *class_dev_parent;
-
-       pos = string;
+       enum subst_type {
+               SUBST_UNKNOWN,
+               SUBST_DEVPATH,
+               SUBST_ID,
+               SUBST_KERNEL_NUMBER,
+               SUBST_KERNEL_NAME,
+               SUBST_MAJOR,
+               SUBST_MINOR,
+               SUBST_RESULT,
+               SUBST_SYSFS,
+               SUBST_ENUM,
+               SUBST_PARENT,
+               SUBST_TEMP_NODE,
+               SUBST_ROOT,
+               SUBST_MODALIAS,
+       };
+       static const struct subst_map {
+               char *name;
+               char fmt;
+               enum subst_type type;
+       } map[] = {
+               { .name = "devpath",            .fmt = 'p',     .type = SUBST_DEVPATH },
+               { .name = "id",                 .fmt = 'b',     .type = SUBST_ID },
+               { .name = "number",             .fmt = 'n',     .type = SUBST_KERNEL_NUMBER },
+               { .name = "kernel",             .fmt = 'k',     .type = SUBST_KERNEL_NAME },
+               { .name = "major",              .fmt = 'M',     .type = SUBST_MAJOR },
+               { .name = "minor",              .fmt = 'm',     .type = SUBST_MINOR },
+               { .name = "result",             .fmt = 'c',     .type = SUBST_RESULT },
+               { .name = "sysfs",              .fmt = 's',     .type = SUBST_SYSFS },
+               { .name = "enum",               .fmt = 'e',     .type = SUBST_ENUM },
+               { .name = "parent",             .fmt = 'P',     .type = SUBST_PARENT },
+               { .name = "tempnode",           .fmt = 'N',     .type = SUBST_TEMP_NODE },
+               { .name = "root",               .fmt = 'r',     .type = SUBST_ROOT },
+               { .name = "modalias",           .fmt = 'A',     .type = SUBST_MODALIAS },
+               {}
+       };
+       enum subst_type type;
+       const struct subst_map *subst;
+
+       head = string;
        while (1) {
-               pos = strchr(pos, '%');
-               if (pos == NULL)
-                       break;
-
-               pos[0] = '\0';
-               tail = pos+1;
-               len = get_format_len(&tail);
-               c = tail[0];
-               strlcpy(temp, tail+1, sizeof(temp));
-               tail = temp;
-               dbg("format=%c, string='%s', tail='%s'",c , string, tail);
+               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'", subst->name);
+                                               goto found;
+                                       }
+                               }
+                       }
+                       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'", subst->fmt);
+                                               goto found;
+                                       }
+                               }
+                       }
+                       head++;
+               }
+               break;
+found:
                attr = get_format_attribute(&tail);
+               strlcpy(temp, tail, sizeof(temp));
+               dbg("format=%i, string='%s', tail='%s', class_dev=%p, sysfs_dev=%p",
+                   type ,string, tail, class_dev, sysfs_device);
 
-               switch (c) {
-               case 'p':
+               switch (type) {
+               case SUBST_DEVPATH:
                        strlcat(string, udev->devpath, maxsize);
-                       dbg("substitute kernel name '%s'", udev->kernel_name);
+                       dbg("substitute devpath '%s'", udev->devpath);
                        break;
-               case 'b':
+               case SUBST_ID:
                        strlcat(string, udev->bus_id, maxsize);
                        dbg("substitute bus_id '%s'", udev->bus_id);
                        break;
-               case 'k':
+               case SUBST_KERNEL_NAME:
                        strlcat(string, udev->kernel_name, maxsize);
                        dbg("substitute kernel name '%s'", udev->kernel_name);
                        break;
-               case 'n':
+               case SUBST_KERNEL_NUMBER:
                        strlcat(string, udev->kernel_number, maxsize);
                        dbg("substitute kernel number '%s'", udev->kernel_number);
-                               break;
-               case 'm':
-                       sprintf(temp2, "%d", minor(udev->devt));
-                       strlcat(string, temp2, maxsize);
-                       dbg("substitute minor number '%s'", temp2);
                        break;
-               case 'M':
+               case SUBST_MAJOR:
                        sprintf(temp2, "%d", major(udev->devt));
                        strlcat(string, temp2, maxsize);
                        dbg("substitute major number '%s'", temp2);
                        break;
-               case 'c':
+               case SUBST_MINOR:
+                       sprintf(temp2, "%d", minor(udev->devt));
+                       strlcat(string, temp2, maxsize);
+                       dbg("substitute minor number '%s'", temp2);
+                       break;
+               case SUBST_RESULT:
                        if (udev->program_result[0] == '\0')
                                break;
                        /* get part part of the result string */
@@ -280,7 +358,7 @@ static void apply_format(struct udevice *udev, char *string, size_t maxsize,
                                dbg("substitute result string '%s'", udev->program_result);
                        }
                        break;
-               case 's':
+               case SUBST_SYSFS:
                        if (attr == NULL) {
                                dbg("missing attribute");
                                break;
@@ -307,18 +385,14 @@ static void apply_format(struct udevice *udev, char *string, size_t maxsize,
                        strlcat(string, temp2, maxsize);
                        dbg("substitute sysfs value '%s'", temp2);
                        break;
-               case '%':
-                       strlcat(string, "%", maxsize);
-                       pos++;
-                       break;
-               case 'e':
+               case SUBST_ENUM:
                        next_free_number = find_free_number(udev, string);
                        if (next_free_number > 0) {
                                sprintf(temp2, "%d", next_free_number);
                                strlcat(string, temp2, maxsize);
                        }
                        break;
-               case 'P':
+               case SUBST_PARENT:
                        if (!class_dev)
                                break;
                        class_dev_parent = sysfs_get_classdev_parent(class_dev);
@@ -336,7 +410,7 @@ static void apply_format(struct udevice *udev, char *string, size_t maxsize,
                                udev_cleanup_device(&udev_parent);
                        }
                        break;
-               case 'N':
+               case SUBST_TEMP_NODE:
                        if (udev->tmp_node[0] == '\0') {
                                dbg("create temporary device node for callout");
                                snprintf(udev->tmp_node, sizeof(udev->tmp_node), "%s/.tmp-%u-%u",
@@ -347,19 +421,27 @@ static void apply_format(struct udevice *udev, char *string, size_t maxsize,
                        strlcat(string, udev->tmp_node, maxsize);
                        dbg("substitute temporary device node name '%s'", udev->tmp_node);
                        break;
-               case 'r':
+               case SUBST_ROOT:
                        strlcat(string, udev_root, maxsize);
                        dbg("substitute udev_root '%s'", udev_root);
                        break;
+               case SUBST_MODALIAS:
+                       if (find_sysfs_attribute(NULL, sysfs_device, "modalias", temp2, sizeof(temp2)) != 0)
+                               break;
+                       strlcat(string, temp2, maxsize);
+                       dbg("substitute MODALIAS '%s'", temp2);
+                       break;
                default:
-                       err("unknown substitution type '%%%c'", c);
+                       err("unknown substitution type=%i", type);
                        break;
                }
-               /* truncate to specified length */
-               if (len > 0)
-                       pos[len] = '\0';
+               /* possibly truncate to format-char specified length */
+               if (len != -1) {
+                       head[len] = '\0';
+                       dbg("truncate to %i chars, subtitution string becomes '%s'", len, head);
+               }
 
-               strlcat(string, tail, maxsize);
+               strlcat(string, temp, maxsize);
        }
 }
 
@@ -519,6 +601,42 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule,
                dbg(KEY_SUBSYSTEM " key is true");
        }
 
+       if (rule->devpath_operation != KEY_OP_UNSET) {
+               dbg("check for " KEY_DEVPATH " rule->devpath='%s' udev->devpath='%s'",
+                   rule->devpath, udev->devpath);
+               if (strcmp_pattern(rule->devpath, udev->devpath) != 0) {
+                       dbg(KEY_DEVPATH " is not matching");
+                       if (rule->devpath_operation != KEY_OP_NOMATCH)
+                               goto exit;
+               } else {
+                       dbg(KEY_DEVPATH " matches");
+                       if (rule->devpath_operation == KEY_OP_NOMATCH)
+                               goto exit;
+               }
+               dbg(KEY_DEVPATH " key is true");
+       }
+
+       if (rule->modalias_operation != KEY_OP_UNSET) {
+               char value[NAME_SIZE];
+
+               if (find_sysfs_attribute(NULL, sysfs_device, "modalias", value, sizeof(value)) != 0) {
+                       dbg(KEY_MODALIAS " value not found");
+                       goto exit;
+               }
+               dbg("check for " KEY_MODALIAS " rule->modalias='%s' modalias='%s'",
+                   rule->modalias, value);
+               if (strcmp_pattern(rule->modalias, value) != 0) {
+                       dbg(KEY_MODALIAS " is not matching");
+                       if (rule->modalias_operation != KEY_OP_NOMATCH)
+                               goto exit;
+               } else {
+                       dbg(KEY_MODALIAS " matches");
+                       if (rule->modalias_operation == KEY_OP_NOMATCH)
+                               goto exit;
+               }
+               dbg(KEY_MODALIAS " key is true");
+       }
+
        if (rule->env_pair_count) {
                int i;
 
@@ -729,13 +847,13 @@ int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_d
 
        /* look for a matching rule to apply */
        list_for_each_entry(rule, &udev_rule_list, node) {
+               if (udev->name_set && rule->name_operation != KEY_OP_UNSET) {
+                       dbg("node name already set, rule ignored");
+                       continue;
+               }
+
                dbg("process rule");
                if (match_rule(udev, rule, class_dev, sysfs_device) == 0) {
-                       if (udev->name[0] != '\0' && rule->name[0] != '\0') {
-                               dbg("node name already set, rule ignored");
-                               continue;
-                       }
-
                        /* apply options */
                        if (rule->ignore_device) {
                                info("configured rule in '%s[%i]' applied, '%s' is ignored",
@@ -754,67 +872,106 @@ int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_d
                        }
 
                        /* apply permissions */
-                       if (rule->mode != 0000) {
+                       if (!udev->mode_final && rule->mode != 0000) {
+                               if (rule->mode_operation == KEY_OP_ASSIGN_FINAL)
+                                       udev->mode_final = 1;
                                udev->mode = rule->mode;
                                dbg("applied mode=%#o to '%s'", udev->mode, udev->kernel_name);
                        }
-                       if (rule->owner[0] != '\0') {
+                       if (!udev->owner_final && rule->owner[0] != '\0') {
+                               if (rule->owner_operation == KEY_OP_ASSIGN_FINAL)
+                                       udev->owner_final = 1;
                                strlcpy(udev->owner, rule->owner, sizeof(udev->owner));
                                apply_format(udev, udev->owner, sizeof(udev->owner), class_dev, sysfs_device);
                                dbg("applied owner='%s' to '%s'", udev->owner, udev->kernel_name);
                        }
-                       if (rule->group[0] != '\0') {
+                       if (!udev->group_final && rule->group[0] != '\0') {
+                               if (rule->group_operation == KEY_OP_ASSIGN_FINAL)
+                                       udev->group_final = 1;
                                strlcpy(udev->group, rule->group, sizeof(udev->group));
                                apply_format(udev, udev->group, sizeof(udev->group), class_dev, sysfs_device);
                                dbg("applied group='%s' to '%s'", udev->group, udev->kernel_name);
                        }
 
                        /* collect symlinks */
-                       if (rule->symlink[0] != '\0') {
+                       if (!udev->symlink_final && rule->symlink_operation != KEY_OP_UNSET) {
                                char temp[PATH_SIZE];
                                char *pos, *next;
 
-                               info("configured rule in '%s[%i]' applied, added symlink '%s'",
-                                    rule->config_file, rule->config_line, rule->symlink);
-                               strlcpy(temp, rule->symlink, sizeof(temp));
-                               apply_format(udev, temp, sizeof(temp), class_dev, sysfs_device);
-
-                               /* add multiple symlinks separated by spaces */
-                               pos = temp;
-                               next = strchr(temp, ' ');
-                               while (next) {
-                                       next[0] = '\0';
+                               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) {
+                                       struct name_entry *name_loop;
+                                       struct name_entry *temp_loop;
+
+                                       info("reset symlink list");
+                                       list_for_each_entry_safe(name_loop, temp_loop, &udev->symlink_list, node) {
+                                               list_del(&name_loop->node);
+                                               free(name_loop);
+                                       }
+                               }
+                               if (rule->symlink[0] != '\0') {
+                                       info("configured rule in '%s[%i]' applied, added symlink '%s'",
+                                            rule->config_file, rule->config_line, rule->symlink);
+                                       strlcpy(temp, rule->symlink, sizeof(temp));
+                                       apply_format(udev, temp, sizeof(temp), class_dev, sysfs_device);
+
+                                       /* add multiple symlinks separated by spaces */
+                                       pos = temp;
+                                       next = strchr(temp, ' ');
+                                       while (next) {
+                                               next[0] = '\0';
+                                               info("add symlink '%s'", pos);
+                                               name_list_add(&udev->symlink_list, pos, 0);
+                                               pos = &next[1];
+                                               next = strchr(pos, ' ');
+                                       }
                                        info("add symlink '%s'", pos);
                                        name_list_add(&udev->symlink_list, pos, 0);
-                                       pos = &next[1];
-                                       next = strchr(pos, ' ');
                                }
-                               info("add symlink '%s'", pos);
-                               name_list_add(&udev->symlink_list, pos, 0);
                        }
 
                        /* set name, later rules with name set will be ignored */
-                       if (rule->name[0] != '\0') {
-                               info("configured rule in '%s[%i]' applied, '%s' becomes '%s'",
-                                    rule->config_file, rule->config_line, udev->kernel_name, rule->name);
-
-                               strlcpy(udev->name, rule->name, sizeof(udev->name));
-                               apply_format(udev, udev->name, sizeof(udev->name), class_dev, sysfs_device);
-                               strlcpy(udev->config_file, rule->config_file, sizeof(udev->config_file));
-                               udev->config_line = rule->config_line;
-
-                               if (udev->type != DEV_NET)
-                                       dbg("name, '%s' is going to have owner='%s', group='%s', mode=%#o partitions=%i",
-                                           udev->name, udev->owner, udev->group, udev->mode, udev->partitions);
+                       if (rule->name_operation != KEY_OP_UNSET) {
+                               udev->name_set = 1;
+                               if (rule->name[0] == '\0') {
+                                       info("configured rule in '%s[%i]' applied, node handling for '%s' supressed",
+                                            rule->config_file, rule->config_line, udev->kernel_name);
+                               } else {
+                                       strlcpy(udev->name, rule->name, sizeof(udev->name));
+                                       apply_format(udev, udev->name, sizeof(udev->name), class_dev, sysfs_device);
+                                       strlcpy(udev->config_file, rule->config_file, sizeof(udev->config_file));
+                                       udev->config_line = rule->config_line;
+
+                                       info("configured rule in '%s:%i' applied, '%s' becomes '%s'",
+                                            rule->config_file, rule->config_line, udev->kernel_name, rule->name);
+                                       if (udev->type != DEV_NET)
+                                               dbg("name, '%s' is going to have owner='%s', group='%s', mode=%#o partitions=%i",
+                                                   udev->name, udev->owner, udev->group, udev->mode, udev->partitions);
+                               }
                        }
 
-                       if (rule->run[0] != '\0') {
+                       if (!udev->run_final && rule->run_operation != KEY_OP_UNSET) {
                                char program[PATH_SIZE];
 
-                               strlcpy(program, rule->run, sizeof(program));
-                               apply_format(udev, program, sizeof(program), class_dev, sysfs_device);
-                               dbg("add run '%s'", program);
-                               name_list_add(&udev->run_list, program, 0);
+                               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) {
+                                       struct name_entry *name_loop;
+                                       struct name_entry *temp_loop;
+
+                                       info("reset run list");
+                                       list_for_each_entry_safe(name_loop, temp_loop, &udev->run_list, node) {
+                                               list_del(&name_loop->node);
+                                               free(name_loop);
+                                       }
+                               }
+                               if (rule->run[0] != '\0') {
+                                       strlcpy(program, rule->run, sizeof(program));
+                                       apply_format(udev, program, sizeof(program), class_dev, sysfs_device);
+                                       dbg("add run '%s'", program);
+                                       name_list_add(&udev->run_list, program, 0);
+                               }
                        }
 
                        if (rule->last_rule) {
@@ -838,113 +995,58 @@ int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_d
        return 0;
 }
 
-int udev_rules_get_run(struct udevice *udev)
+int udev_rules_get_run(struct udevice *udev, struct sysfs_device *sysfs_device)
 {
        struct udev_rule *rule;
-       char program[PATH_SIZE];
 
        /* look for a matching rule to apply */
        list_for_each_entry(rule, &udev_rule_list, node) {
                dbg("process rule");
 
-               if (rule->run[0] == '\0')
+               if (rule->run_operation == KEY_OP_UNSET)
                        continue;
 
-               if (rule->name[0] != '\0' || rule->symlink[0] != '\0' ||
+               if (rule->name_operation != KEY_OP_UNSET || rule->symlink_operation != KEY_OP_UNSET ||
                    rule->mode != 0000 || rule->owner[0] != '\0' || rule->group[0] != '\0') {
                        dbg("skip rule that names a device");
                        continue;
                }
 
-               if (rule->action_operation != KEY_OP_UNSET) {
-                       dbg("check for " KEY_ACTION " rule->action='%s' udev->action='%s'",
-                           rule->action, udev->action);
-                       if (strcmp_pattern(rule->action, udev->action) != 0) {
-                               dbg(KEY_ACTION " is not matching");
-                               if (rule->action_operation != KEY_OP_NOMATCH)
-                                       continue;
-                       } else {
-                               dbg(KEY_ACTION " matches");
-                               if (rule->action_operation == KEY_OP_NOMATCH)
-                                       continue;
-                       }
-                       dbg(KEY_ACTION " key is true");
-               }
-
-               if (rule->kernel_operation != KEY_OP_UNSET) {
-                       dbg("check for " KEY_KERNEL " rule->kernel='%s' udev->kernel_name='%s'",
-                           rule->kernel, udev->kernel_name);
-                       if (strcmp_pattern(rule->kernel, udev->kernel_name) != 0) {
-                               dbg(KEY_KERNEL " is not matching");
-                               if (rule->kernel_operation != KEY_OP_NOMATCH)
-                                       continue;
-               } else {
-                               dbg(KEY_KERNEL " matches");
-                               if (rule->kernel_operation == KEY_OP_NOMATCH)
-                                       continue;
-                       }
-                       dbg(KEY_KERNEL " key is true");
-               }
-
-               if (rule->subsystem_operation != KEY_OP_UNSET) {
-                       dbg("check for " KEY_SUBSYSTEM " rule->subsystem='%s' udev->subsystem='%s'",
-                           rule->subsystem, udev->subsystem);
-                       if (strcmp_pattern(rule->subsystem, udev->subsystem) != 0) {
-                               dbg(KEY_SUBSYSTEM " is not matching");
-                               if (rule->subsystem_operation != KEY_OP_NOMATCH)
-                                       continue;
-                       } else {
-                               dbg(KEY_SUBSYSTEM " matches");
-                               if (rule->subsystem_operation == KEY_OP_NOMATCH)
-                                       continue;
+               if (match_rule(udev, rule, NULL, sysfs_device) == 0) {
+                       if (rule->ignore_device) {
+                               info("configured rule in '%s[%i]' applied, '%s' is ignored",
+                                    rule->config_file, rule->config_line, udev->kernel_name);
+                               udev->ignore_device = 1;
+                               return 0;
                        }
-                       dbg(KEY_SUBSYSTEM " key is true");
-               }
 
-               if (rule->env_pair_count) {
-                       int i;
+                       if (!udev->run_final && rule->run_operation != KEY_OP_UNSET) {
+                               char program[PATH_SIZE];
 
-                       dbg("check for " KEY_ENV " pairs");
-                       for (i = 0; i < rule->env_pair_count; i++) {
-                               struct key_pair *pair;
-                               const char *value;
+                               if (rule->run_operation == KEY_OP_ASSIGN || rule->run_operation == KEY_OP_ASSIGN_FINAL) {
+                                       struct name_entry *name_loop;
+                                       struct name_entry *temp_loop;
 
-                               pair = &rule->env_pair[i];
-                               value = getenv(pair->name);
-                               if (!value) {
-                                       dbg(KEY_ENV "{'%s'} is not found", pair->name);
-                                       continue;
+                                       info("reset run list");
+                                       list_for_each_entry_safe(name_loop, temp_loop, &udev->run_list, node) {
+                                               list_del(&name_loop->node);
+                                               free(name_loop);
+                                       }
                                }
-                               if (strcmp_pattern(pair->value, value) != 0) {
-                                       dbg(KEY_ENV "{'%s'} is not matching", pair->name);
-                                       if (pair->operation != KEY_OP_NOMATCH)
-                                               continue;
-                               } else {
-                                       dbg(KEY_ENV "{'%s'} matches", pair->name);
-                                       if (pair->operation == KEY_OP_NOMATCH)
-                                               continue;
+                               if (rule->run[0] != '\0') {
+                                       strlcpy(program, rule->run, sizeof(program));
+                                       apply_format(udev, program, sizeof(program), NULL, sysfs_device);
+                                       dbg("add run '%s'", program);
+                                       name_list_add(&udev->run_list, program, 0);
                                }
+                               if (rule->run_operation == KEY_OP_ASSIGN_FINAL)
+                                       break;
                        }
-                       dbg(KEY_ENV " key is true");
-               }
-
-               /* rule matches */
-
-               if (rule->ignore_device) {
-                       info("configured rule in '%s[%i]' applied, '%s' is ignored",
-                            rule->config_file, rule->config_line, udev->kernel_name);
-                       udev->ignore_device = 1;
-                       return 0;
-               }
 
-               strlcpy(program, rule->run, sizeof(program));
-               apply_format(udev, program, sizeof(program), NULL, NULL);
-               dbg("add run '%s'", program);
-               name_list_add(&udev->run_list, program, 0);
-
-               if (rule->last_rule) {
-                       dbg("last rule to be applied");
-                       break;
+                       if (rule->last_rule) {
+                               dbg("last rule to be applied");
+                               break;
+                       }
                }
        }
 
index 5fba2d5..2bf8107 100644 (file)
@@ -31,6 +31,7 @@
 #define KEY_KERNEL             "KERNEL"
 #define KEY_SUBSYSTEM          "SUBSYSTEM"
 #define KEY_ACTION             "ACTION"
+#define KEY_DEVPATH            "DEVPATH"
 #define KEY_BUS                        "BUS"
 #define KEY_ID                 "ID"
 #define KEY_PROGRAM            "PROGRAM"
@@ -38,6 +39,7 @@
 #define KEY_DRIVER             "DRIVER"
 #define KEY_SYSFS              "SYSFS"
 #define KEY_ENV                        "ENV"
+#define KEY_MODALIAS           "MODALIAS"
 #define KEY_NAME               "NAME"
 #define KEY_SYMLINK            "SYMLINK"
 #define KEY_OWNER              "OWNER"
@@ -62,6 +64,7 @@ enum key_operation {
        KEY_OP_NOMATCH,
        KEY_OP_ADD,
        KEY_OP_ASSIGN,
+       KEY_OP_ASSIGN_FINAL,
 };
 
 struct key_pair {
@@ -79,6 +82,8 @@ struct udev_rule {
        enum key_operation subsystem_operation;
        char action[NAME_SIZE];
        enum key_operation action_operation;
+       char devpath[PATH_SIZE];
+       enum key_operation devpath_operation;
        char bus[NAME_SIZE];
        enum key_operation bus_operation;
        char id[NAME_SIZE];
@@ -93,13 +98,21 @@ struct udev_rule {
        int sysfs_pair_count;
        struct key_pair env_pair[KEY_ENV_PAIRS_MAX];
        int env_pair_count;
+       enum key_operation modalias_operation;
+       char modalias[PATH_SIZE];
 
        char name[PATH_SIZE];
+       enum key_operation name_operation;
        char symlink[PATH_SIZE];
+       enum key_operation symlink_operation;
        char owner[USER_SIZE];
+       enum key_operation owner_operation;
        char group[USER_SIZE];
+       enum key_operation group_operation;
        mode_t mode;
+       enum key_operation mode_operation;
        char run[PATH_SIZE];
+       enum key_operation run_operation;
 
        int last_rule;
        int ignore_device;
@@ -114,7 +127,7 @@ extern struct list_head udev_rule_list;
 
 extern int udev_rules_init(void);
 extern int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_dev);
-extern int udev_rules_get_run(struct udevice *udev);
+extern int udev_rules_get_run(struct udevice *udev, struct sysfs_device *sysfs_device);
 extern void udev_rules_close(void);
 
 #endif
index e665957..89925a3 100644 (file)
@@ -89,6 +89,8 @@ static int get_key(char **line, char **key, enum key_operation *operation, char
                        break;
                if (linepos[0] == '!')
                        break;
+               if (linepos[0] == ':')
+                       break;
        }
 
        /* remember end of key */
@@ -115,6 +117,10 @@ static int get_key(char **line, char **key, enum key_operation *operation, char
                *operation = KEY_OP_ASSIGN;
                linepos++;
                dbg("operator=assign");
+       } else if (linepos[0] == ':' && linepos[1] == '=') {
+               *operation = KEY_OP_ASSIGN_FINAL;
+               linepos += 2;
+               dbg("operator=assign_final");
        } else
                return -1;
 
@@ -263,6 +269,13 @@ static int rules_parse(const char *filename)
                                continue;
                        }
 
+                       if (strcasecmp(key, KEY_DEVPATH) == 0) {
+                               strlcpy(rule.devpath, value, sizeof(rule.devpath));
+                               rule.devpath_operation = operation;
+                               valid = 1;
+                               continue;
+                       }
+
                        if (strcasecmp(key, KEY_BUS) == 0) {
                                strlcpy(rule.bus, value, sizeof(rule.bus));
                                rule.bus_operation = operation;
@@ -319,6 +332,13 @@ static int rules_parse(const char *filename)
                                continue;
                        }
 
+                       if (strcasecmp(key, KEY_MODALIAS) == 0) {
+                               strlcpy(rule.modalias, value, sizeof(rule.modalias));
+                               rule.modalias_operation = operation;
+                               valid = 1;
+                               continue;
+                       }
+
                        if (strcasecmp(key, KEY_DRIVER) == 0) {
                                strlcpy(rule.driver, value, sizeof(rule.driver));
                                rule.driver_operation = operation;
@@ -343,51 +363,54 @@ static int rules_parse(const char *filename)
 
                        if (strncasecmp(key, KEY_NAME, sizeof(KEY_NAME)-1) == 0) {
                                attr = get_key_attribute(key + sizeof(KEY_NAME)-1);
-                               /* FIXME: remove old style options and make OPTIONS= mandatory */
                                if (attr != NULL) {
                                        if (strstr(attr, OPTION_PARTITIONS) != NULL) {
                                                dbg("creation of partition nodes requested");
                                                rule.partitions = DEFAULT_PARTITIONS_COUNT;
                                        }
+                                       /* FIXME: remove old style option and make OPTIONS= mandatory */
                                        if (strstr(attr, OPTION_IGNORE_REMOVE) != NULL) {
                                                dbg("remove event should be ignored");
                                                rule.ignore_remove = 1;
                                        }
                                }
-                               if (value[0] != '\0')
-                                       strlcpy(rule.name, value, sizeof(rule.name));
-                               else
-                                       rule.ignore_device = 1;
+                               rule.name_operation = operation;
+                               strlcpy(rule.name, value, sizeof(rule.name));
                                valid = 1;
                                continue;
                        }
 
                        if (strcasecmp(key, KEY_SYMLINK) == 0) {
                                strlcpy(rule.symlink, value, sizeof(rule.symlink));
+                               rule.symlink_operation = operation;
                                valid = 1;
                                continue;
                        }
 
                        if (strcasecmp(key, KEY_OWNER) == 0) {
                                strlcpy(rule.owner, value, sizeof(rule.owner));
+                               rule.owner_operation = operation;
                                valid = 1;
                                continue;
                        }
 
                        if (strcasecmp(key, KEY_GROUP) == 0) {
                                strlcpy(rule.group, value, sizeof(rule.group));
+                               rule.group_operation = operation;
                                valid = 1;
                                continue;
                        }
 
                        if (strcasecmp(key, KEY_MODE) == 0) {
                                rule.mode = strtol(value, NULL, 8);
+                               rule.mode_operation = operation;
                                valid = 1;
                                continue;
                        }
 
                        if (strcasecmp(key, KEY_RUN) == 0) {
                                strlcpy(rule.run, value, sizeof(rule.run));
+                               rule.run_operation = operation;
                                valid = 1;
                                continue;
                        }
index f9ff1ed..5c43190 100644 (file)
@@ -297,38 +297,38 @@ int wait_for_devices_device(struct sysfs_device *devices_dev,
                { .bus = "usb",         .file = "idVendor" },
                { .bus = "usb",         .file = "iInterface" },
                { .bus = "usb",         .file = "bNumEndpoints" },
-               { .bus = "usb-serial",  .file = "detach_state" },
-               { .bus = "ide",         .file = "detach_state" },
+               { .bus = "usb-serial",  .file = "power" },
+               { .bus = "ide",         .file = "power" },
                { .bus = "pci",         .file = "vendor" },
-               { .bus = "platform",    .file = "detach_state" },
-               { .bus = "pcmcia",      .file = "detach_state" },
-               { .bus = "i2c",         .file = "detach_state" },
+               { .bus = "platform",    .file = "power" },
+               { .bus = "pcmcia",      .file = "power" },
+               { .bus = "i2c",         .file = "power" },
                { .bus = "ieee1394",    .file = "node_count" },
                { .bus = "ieee1394",    .file = "nodeid" },
                { .bus = "ieee1394",    .file = "address" },
                { .bus = "bttv-sub",    .file = NULL },
-               { .bus = "pnp",         .file = "detach_state" },
-               { .bus = "eisa",        .file = "detach_state" },
-               { .bus = "serio",       .file = "detach_state" },
-               { .bus = "pseudo",      .file = "detach_state" },
-               { .bus = "mmc",         .file = "detach_state" },
-               { .bus = "macio",       .file = "detach_state" },
-               { .bus = "of_platform", .file = "detach_state" },
-               { .bus = "vio",         .file = "detach_state" },
-               { .bus = "ecard",       .file = "detach_state" },
-               { .bus = "sa1111-rab",  .file = "detach_state" },
-               { .bus = "amba",        .file = "detach_state" },
-               { .bus = "locomo-bus",  .file = "detach_state" },
-               { .bus = "logicmodule", .file = "detach_state" },
-               { .bus = "parisc",      .file = "detach_state" },
-               { .bus = "ocp",         .file = "detach_state" },
-               { .bus = "dio",         .file = "detach_state" },
-               { .bus = "MCA",         .file = "detach_state" },
-               { .bus = "wl",          .file = "detach_state" },
-               { .bus = "ccwgroup",    .file = "detach_state" },
-               { .bus = "css",         .file = "detach_state" },
-               { .bus = "ccw",         .file = "detach_state" },
-               { .bus = "iucv",        .file = "detach_state" },
+               { .bus = "pnp",         .file = "power" },
+               { .bus = "eisa",        .file = "power" },
+               { .bus = "serio",       .file = "power" },
+               { .bus = "pseudo",      .file = "power" },
+               { .bus = "mmc",         .file = "power" },
+               { .bus = "macio",       .file = "power" },
+               { .bus = "of_platform", .file = "power" },
+               { .bus = "vio",         .file = "power" },
+               { .bus = "ecard",       .file = "power" },
+               { .bus = "sa1111-rab",  .file = "power" },
+               { .bus = "amba",        .file = "power" },
+               { .bus = "locomo-bus",  .file = "power" },
+               { .bus = "logicmodule", .file = "power" },
+               { .bus = "parisc",      .file = "power" },
+               { .bus = "ocp",         .file = "power" },
+               { .bus = "dio",         .file = "power" },
+               { .bus = "MCA",         .file = "power" },
+               { .bus = "wl",          .file = "power" },
+               { .bus = "ccwgroup",    .file = "power" },
+               { .bus = "css",         .file = "power" },
+               { .bus = "ccw",         .file = "power" },
+               { .bus = "iucv",        .file = "power" },
                { NULL, NULL }
        };
        const struct device_file *devicefile = NULL;
index 74b55ed..64a7ba9 100644 (file)
@@ -27,6 +27,7 @@
 #include <errno.h>
 #include <ctype.h>
 #include <dirent.h>
+#include <syslog.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
@@ -107,6 +108,41 @@ void udev_cleanup_device(struct udevice *udev)
                list_del(&name_loop->node);
                free(name_loop);
        }
+       list_for_each_entry_safe(name_loop, temp_loop, &udev->run_list, node) {
+               list_del(&name_loop->node);
+               free(name_loop);
+       }
+}
+
+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;
+}
+
+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;
 }
 
 int kernel_release_satisfactory(unsigned int version, unsigned int patchlevel, unsigned int sublevel)
@@ -373,10 +409,10 @@ int execute_command(const char *command, const char *subsystem)
                        close(devnull);
                }
                retval = execv(arg, argv);
-               err("exec of child failed");
+               err("exec of child '%s' failed", command);
                _exit(1);
        case -1:
-               dbg("fork of child failed");
+               dbg("fork of child '%s' failed", command);
                break;
                return -1;
        default:
index 9cdae6d..b3e604f 100644 (file)
@@ -34,6 +34,8 @@ extern void udev_cleanup_device(struct udevice *udev);
 
 extern int kernel_release_satisfactory(unsigned int version, unsigned int patchlevel, unsigned int sublevel);
 extern int create_path(const char *path);
+extern int log_priority(const char *priority);
+extern int string_is_true(const char *str);
 extern int parse_get_pair(char **orig_string, char **left, char **right);
 extern int unlink_secure(const char *filename);
 extern int file_map(const char *filename, char **buf, size_t *bufsize);
diff --git a/udevcontrol.c b/udevcontrol.c
new file mode 100644 (file)
index 0000000..f19ae98
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * udevcontrol.c
+ *
+ * Userspace devfs
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *
+ *     This program is free software; you can redistribute it and/or modify it
+ *     under the terms of the GNU General Public License as published by the
+ *     Free Software Foundation 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.,
+ *     675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <time.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/stddef.h>
+
+#include "udev.h"
+#include "udev_version.h"
+#include "udevd.h"
+#include "udev_utils.h"
+#include "logging.h"
+
+/* global variables */
+static int sock = -1;
+static int log = 0;
+
+#ifdef USE_LOG
+void log_message (int priority, const char *format, ...)
+{
+       va_list args;
+
+       if (priority > log)
+               return;
+
+       va_start(args, format);
+       vsyslog(priority, format, args);
+       va_end(args);
+}
+#endif
+
+
+int main(int argc, char *argv[], char *envp[])
+{
+       static struct udevd_msg usend_msg;
+       struct sockaddr_un saddr;
+       socklen_t addrlen;
+       const char *env;
+       const char *val;
+       int *intval;
+       int retval = 1;
+
+       env = getenv("UDEV_LOG");
+       if (env)
+               log = log_priority(env);
+
+       logging_init("udevcontrol");
+       dbg("version %s", UDEV_VERSION);
+
+       if (argc != 2) {
+               err("error finding comand");
+               goto exit;
+       }
+
+       memset(&usend_msg, 0x00, sizeof(struct udevd_msg));
+       strcpy(usend_msg.magic, UDEV_MAGIC);
+
+       if (!strcmp(argv[1], "stop_exec_queue"))
+               usend_msg.type = UDEVD_STOP_EXEC_QUEUE;
+       else if (!strcmp(argv[1], "start_exec_queue"))
+               usend_msg.type = UDEVD_START_EXEC_QUEUE;
+       else if (!strncmp(argv[1], "log_priority=", strlen("log_priority="))) {
+               intval = (int *) usend_msg.envbuf;
+               val = &argv[1][strlen("log_priority=")];
+               usend_msg.type = UDEVD_SET_LOG_LEVEL;
+               *intval = log_priority(val);
+               info("send log_priority=%i", *intval);
+       } else if (!strncmp(argv[1], "max_childs=", strlen("max_childs="))) {
+               intval = (int *) usend_msg.envbuf;
+               val = &argv[1][strlen("max_childs=")];
+               usend_msg.type = UDEVD_SET_MAX_CHILDS;
+               *intval = atoi(val);
+               info("send max_childs=%i", *intval);
+       } else {
+               err("error parsing command\n");
+               goto exit;
+       }
+
+       sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
+       if (sock == -1) {
+               err("error getting socket");
+               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_SOCK_PATH);
+       addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path+1) + 1;
+
+
+       retval = sendto(sock, &usend_msg, sizeof(usend_msg), 0, (struct sockaddr *)&saddr, addrlen);
+       if (retval == -1) {
+               info("error sending message (%s)", strerror(errno));
+               retval = 1;
+       } else {
+               dbg("sent message type=0x%02x, %u bytes sent", usend_msg.type, retval);
+               retval = 0;
+       }
+
+       close(sock);
+
+exit:
+       logging_close();
+
+       return retval;
+}
diff --git a/udevd.c b/udevd.c
index 91033b0..209b9fc 100644 (file)
--- a/udevd.c
+++ b/udevd.c
@@ -38,6 +38,7 @@
 #include <sys/un.h>
 #include <sys/sysinfo.h>
 #include <sys/stat.h>
+#include <linux/netlink.h>
 
 #include "list.h"
 #include "udev_libc_wrapper.h"
 #include "logging.h"
 
 /* global variables*/
-static int udevsendsock;
+static int udevd_sock;
+static int uevent_netlink_sock;
 static pid_t sid;
 
 static int pipefds[2];
-static long startup_time;
-static unsigned long long expected_seqnum = 0;
 static volatile int sigchilds_waiting;
 static volatile int run_msg_q;
 static volatile int sig_flag;
+static int init_phase = 1;
 static int run_exec_q;
+static int stop_exec_q;
 
 static LIST_HEAD(msg_list);
 static LIST_HEAD(exec_list);
@@ -67,7 +69,13 @@ static void exec_queue_manager(void);
 static void msg_queue_manager(void);
 static void user_sighandler(void);
 static void reap_sigchilds(void);
-char *udev_bin;
+
+static char *udev_bin;
+static unsigned long long expected_seqnum;
+static int event_timeout;
+static int max_childs;
+static int max_childs_running;
+
 
 #ifdef USE_LOG
 void log_message (int priority, const char *format, ...)
@@ -86,23 +94,23 @@ void log_message (int priority, const char *format, ...)
 static void msg_dump_queue(void)
 {
 #ifdef DEBUG
-       struct hotplug_msg *msg;
+       struct uevent_msg *msg;
 
        list_for_each_entry(msg, &msg_list, node)
                dbg("sequence %llu in queue", msg->seqnum);
 #endif
 }
 
-static void run_queue_delete(struct hotplug_msg *msg)
+static void run_queue_delete(struct uevent_msg *msg)
 {
        list_del(&msg->node);
        free(msg);
 }
 
 /* orders the message in the queue by sequence number */
-static void msg_queue_insert(struct hotplug_msg *msg)
+static void msg_queue_insert(struct uevent_msg *msg)
 {
-       struct hotplug_msg *loop_msg;
+       struct uevent_msg *loop_msg;
        struct sysinfo info;
 
        if (msg->seqnum == 0) {
@@ -112,6 +120,20 @@ static void msg_queue_insert(struct hotplug_msg *msg)
                return;
        }
 
+       /* store timestamp of queuing */
+       sysinfo(&info);
+       msg->queue_time = info.uptime;
+
+       /* with the first event we provide a phase of shorter timeout */
+       if (init_phase) {
+               static long init_time;
+
+               if (init_time == 0)
+                       init_time = info.uptime;
+               if (info.uptime - init_time >= UDEVD_INIT_TIME)
+                       init_phase = 0;
+       }
+
        /* don't delay messages with timeout set */
        if (msg->timeout) {
                dbg("move seq %llu with timeout %u to exec queue", msg->seqnum, msg->timeout);
@@ -126,17 +148,13 @@ static void msg_queue_insert(struct hotplug_msg *msg)
                        break;
 
                if (loop_msg->seqnum == msg->seqnum) {
-                       info("ignoring duplicate message seq %llu", msg->seqnum);
+                       dbg("ignoring duplicate message seq %llu", msg->seqnum);
+                       free(msg);
                        return;
                }
        }
-
-       /* store timestamp of queuing */
-       sysinfo(&info);
-       msg->queue_time = info.uptime;
-
        list_add(&msg->node, &loop_msg->node);
-       dbg("queued message seq %llu", msg->seqnum);
+       info("seq %llu queued, devpath '%s'", msg->seqnum, msg->devpath);
 
        /* run msg queue manager */
        run_msg_q = 1;
@@ -145,16 +163,19 @@ static void msg_queue_insert(struct hotplug_msg *msg)
 }
 
 /* forks event and removes event from run queue when finished */
-static void execute_udev(struct hotplug_msg *msg)
+static void execute_udev(struct uevent_msg *msg)
 {
        char *const argv[] = { "udev", msg->subsystem, NULL };
        pid_t pid;
+       struct sysinfo info;
 
        pid = fork();
        switch (pid) {
        case 0:
                /* child */
-               close(udevsendsock);
+               if (uevent_netlink_sock != -1)
+                       close(uevent_netlink_sock);
+               close(udevd_sock);
                logging_close();
 
                setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY);
@@ -168,7 +189,9 @@ static void execute_udev(struct hotplug_msg *msg)
                break;
        default:
                /* get SIGCHLD in main loop */
-               dbg("==> exec seq %llu [%d] working at '%s'", msg->seqnum, pid, msg->devpath);
+               sysinfo(&info);
+               info("seq %llu forked, pid %d, %ld seconds old",
+                    msg->seqnum, pid, info.uptime - msg->queue_time);
                msg->pid = pid;
        }
 }
@@ -293,73 +316,84 @@ static int compare_devpath(const char *running, const char *waiting)
 }
 
 /* returns still running task for the same device, its parent or its physical device */
-static struct hotplug_msg *running_with_devpath(struct hotplug_msg *msg)
+static int running_with_devpath(struct uevent_msg *msg, int limit)
 {
-       struct hotplug_msg *loop_msg;
+       struct uevent_msg *loop_msg;
+       int childs_count = 0;
 
        if (msg->devpath == NULL)
-               return NULL;
+               return 0;
 
        /* skip any events with a timeout set */
-       if (msg->timeout)
-               return NULL;
+       if (msg->timeout != 0)
+               return 0;
 
        list_for_each_entry(loop_msg, &running_list, node) {
+               if (limit && childs_count++ > limit) {
+                       dbg("%llu, maximum number (%i) of child reached", msg->seqnum, childs_count);
+                       return 1;
+               }
                if (loop_msg->devpath == NULL)
                        continue;
 
                /* return running parent/child device event */
-               if (compare_devpath(loop_msg->devpath, msg->devpath) != 0)
-                       return loop_msg;
+               if (compare_devpath(loop_msg->devpath, msg->devpath) != 0) {
+                       dbg("%llu, child device event still running %llu (%s)",
+                           msg->seqnum, loop_msg->seqnum, loop_msg->devpath);
+                       return 2;
+               }
 
                /* return running physical device event */
                if (msg->physdevpath && msg->action && strcmp(msg->action, "add") == 0)
-                       if (compare_devpath(loop_msg->devpath, msg->physdevpath) != 0)
-                               return loop_msg;
+                       if (compare_devpath(loop_msg->devpath, msg->physdevpath) != 0) {
+                               dbg("%llu, physical device event still running %llu (%s)",
+                                   msg->seqnum, loop_msg->seqnum, loop_msg->devpath);
+                               return 3;
+                       }
        }
 
-       return NULL;
+       return 0;
 }
 
 /* exec queue management routine executes the events and serializes events in the same sequence */
 static void exec_queue_manager(void)
 {
-       struct hotplug_msg *loop_msg;
-       struct hotplug_msg *tmp_msg;
-       struct hotplug_msg *msg;
+       struct uevent_msg *loop_msg;
+       struct uevent_msg *tmp_msg;
        int running;
 
+       if (list_empty(&exec_list))
+               return;
+
        running = running_processes();
        dbg("%d processes runnning on system", running);
        if (running < 0)
-               running = THROTTLE_MAX_RUNNING_CHILDS;
+               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 >= THROTTLE_MAX_RUNNING_CHILDS) {
-                       running = running_processes_in_session(sid, THROTTLE_MAX_RUNNING_CHILDS+10);
-                       dbg("%d processes running in session", running);
-                       if (running >= THROTTLE_MAX_RUNNING_CHILDS) {
-                               dbg("delay seq %llu, cause too many processes already running", loop_msg->seqnum);
+               if (running >= max_childs_running) {
+                       running = running_processes_in_session(sid, max_childs_running+10);
+                       dbg("at least %d processes running in session", running);
+                       if (running >= max_childs_running) {
+                               dbg("delay seq %llu, cause too many processes already running",
+                                   loop_msg->seqnum);
                                return;
                        }
                }
 
-               msg = running_with_devpath(loop_msg);
-               if (!msg) {
+               if (running_with_devpath(loop_msg, max_childs) == 0) {
                        /* move event to run list */
                        list_move_tail(&loop_msg->node, &running_list);
                        execute_udev(loop_msg);
                        running++;
                        dbg("moved seq %llu to running list", loop_msg->seqnum);
-               } else {
-                       dbg("delay seq %llu (%s), cause seq %llu (%s) is still running",
-                           loop_msg->seqnum, loop_msg->devpath, msg->seqnum, msg->devpath);
-               }
+               } else
+                       dbg("delay seq %llu (%s)", loop_msg->seqnum, loop_msg->devpath);
        }
 }
 
-static void msg_move_exec(struct hotplug_msg *msg)
+static void msg_move_exec(struct uevent_msg *msg)
 {
        list_move_tail(&msg->node, &exec_list);
        run_exec_q = 1;
@@ -371,15 +405,15 @@ static void msg_move_exec(struct hotplug_msg *msg)
 /* msg queue management routine handles the timeouts and dispatches the events */
 static void msg_queue_manager(void)
 {
-       struct hotplug_msg *loop_msg;
-       struct hotplug_msg *tmp_msg;
+       struct uevent_msg *loop_msg;
+       struct uevent_msg *tmp_msg;
        struct sysinfo info;
        long msg_age = 0;
-       static int timeout = EVENT_INIT_TIMEOUT_SEC;
-       static int init = 1;
+       int timeout = event_timeout;
 
        dbg("msg queue manager, next expected is %llu", expected_seqnum);
 recheck:
+       sysinfo(&info);
        list_for_each_entry_safe(loop_msg, tmp_msg, &msg_list, node) {
                /* move event with expected sequence to the exec list */
                if (loop_msg->seqnum == expected_seqnum) {
@@ -387,15 +421,13 @@ recheck:
                        continue;
                }
 
-               /* see if we are in the initialization phase and wait for the very first events */
-               if (init && (info.uptime - startup_time >= INIT_TIME_SEC)) {
-                       init = 0;
-                       timeout = EVENT_TIMEOUT_SEC;
-                       dbg("initialization phase passed, set timeout to %i seconds", EVENT_TIMEOUT_SEC);
+               /* limit timeout during initialization phase */
+               if (init_phase) {
+                       timeout = UDEVD_INIT_EVENT_TIMEOUT;
+                       dbg("initialization phase, limit timeout to %i seconds", UDEVD_INIT_EVENT_TIMEOUT);
                }
 
                /* move event with expired timeout to the exec list */
-               sysinfo(&info);
                msg_age = info.uptime - loop_msg->queue_time;
                dbg("seq %llu is %li seconds old", loop_msg->seqnum, msg_age);
                if (msg_age >= timeout) {
@@ -416,13 +448,60 @@ recheck:
        }
 }
 
-/* receive the udevsend message and do some sanity checks */
-static struct hotplug_msg *get_udevsend_msg(void)
+static struct uevent_msg *get_msg_from_envbuf(const char *buf, int buf_size)
 {
-       static struct udevsend_msg usend_msg;
-       struct hotplug_msg *msg;
        int bufpos;
        int i;
+       struct uevent_msg *msg;
+
+       msg = malloc(sizeof(struct uevent_msg) + buf_size);
+       if (msg == NULL)
+               return NULL;
+       memset(msg, 0x00, sizeof(struct 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]", msg->envp[i], i);
+
+               /* remember some keys for further processing */
+               if (strncmp(key, "ACTION=", 7) == 0)
+                       msg->action = &key[7];
+
+               if (strncmp(key, "DEVPATH=", 8) == 0)
+                       msg->devpath = &key[8];
+
+               if (strncmp(key, "SUBSYSTEM=", 10) == 0)
+                       msg->subsystem = &key[10];
+
+               if (strncmp(key, "SEQNUM=", 7) == 0)
+                       msg->seqnum = strtoull(&key[7], NULL, 10);
+
+               if (strncmp(key, "PHYSDEVPATH=", 12) == 0)
+                       msg->physdevpath = &key[12];
+
+               if (strncmp(key, "TIMEOUT=", 8) == 0)
+                       msg->timeout = strtoull(&key[8], NULL, 10);
+       }
+       msg->envp[i++] = "UDEVD_EVENT=1";
+       msg->envp[i] = NULL;
+
+       return msg;
+}
+
+/* receive the udevd message from userspace */
+static struct uevent_msg *get_udevd_msg(void)
+{
+       static struct udevd_msg usend_msg;
+       struct uevent_msg *msg;
        ssize_t size;
        struct msghdr smsg;
        struct cmsghdr *cmsg;
@@ -430,10 +509,11 @@ static struct hotplug_msg *get_udevsend_msg(void)
        struct ucred *cred;
        char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
        int envbuf_size;
+       int *intval;
 
-       memset(&usend_msg, 0x00, sizeof(struct udevsend_msg));
+       memset(&usend_msg, 0x00, sizeof(struct udevd_msg));
        iov.iov_base = &usend_msg;
-       iov.iov_len = sizeof(struct udevsend_msg);
+       iov.iov_len = sizeof(struct udevd_msg);
 
        memset(&smsg, 0x00, sizeof(struct msghdr));
        smsg.msg_iov = &iov;
@@ -441,10 +521,10 @@ static struct hotplug_msg *get_udevsend_msg(void)
        smsg.msg_control = cred_msg;
        smsg.msg_controllen = sizeof(cred_msg);
 
-       size = recvmsg(udevsendsock, &smsg, 0);
+       size = recvmsg(udevd_sock, &smsg, 0);
        if (size <  0) {
                if (errno != EINTR)
-                       dbg("unable to receive udevsend message");
+                       dbg("unable to receive udevd message");
                return NULL;
        }
        cmsg = CMSG_FIRSTHDR(&smsg);
@@ -465,48 +545,90 @@ static struct hotplug_msg *get_udevsend_msg(void)
                return NULL;
        }
 
-       envbuf_size = size - offsetof(struct udevsend_msg, envbuf);
-       dbg("envbuf_size=%i", envbuf_size);
-       msg = malloc(sizeof(struct hotplug_msg) + envbuf_size);
-       if (msg == NULL)
-               return NULL;
-
-       memset(msg, 0x00, sizeof(struct hotplug_msg) + envbuf_size);
-
-       /* copy environment buffer and reconstruct envp */
-       memcpy(msg->envbuf, usend_msg.envbuf, envbuf_size);
-       bufpos = 0;
-       for (i = 0; (bufpos < envbuf_size) && (i < HOTPLUG_NUM_ENVP-2); i++) {
-               int keylen;
-               char *key;
+       switch (usend_msg.type) {
+       case UDEVD_UEVENT_UDEVSEND:
+       case UDEVD_UEVENT_INITSEND:
+               info("udevd event message received");
+               envbuf_size = size - offsetof(struct udevd_msg, envbuf);
+               dbg("envbuf_size=%i", envbuf_size);
+               msg = get_msg_from_envbuf(usend_msg.envbuf, envbuf_size);
+               if (msg == NULL)
+                       return NULL;
+               msg->type = usend_msg.type;
+               return msg;
+       case UDEVD_STOP_EXEC_QUEUE:
+               info("udevd message (STOP_EXEC_QUEUE) received");
+               stop_exec_q = 1;
+               break;
+       case UDEVD_START_EXEC_QUEUE:
+               info("udevd message (START_EXEC_QUEUE) received");
+               stop_exec_q = 0;
+               exec_queue_manager();
+               break;
+       case UDEVD_SET_LOG_LEVEL:
+               intval = (int *) usend_msg.envbuf;
+               info("udevd message (SET_LOG_PRIORITY) received, udev_log_priority=%i", *intval);
+               udev_log_priority = *intval;
+               break;
+       case UDEVD_SET_MAX_CHILDS:
+               intval = (int *) usend_msg.envbuf;
+               info("udevd message (UDEVD_SET_MAX_CHILDS) received, max_childs=%i", *intval);
+               max_childs = *intval;
+               break;
+       default:
+               dbg("unknown message type");
+       }
+       return NULL;
+}
 
-               key = &msg->envbuf[bufpos];
-               keylen = strlen(key);
-               msg->envp[i] = key;
-               bufpos += keylen + 1;
-               dbg("add '%s' to msg.envp[%i]", msg->envp[i], i);
+/* receive the kernel user event message and do some sanity checks */
+static struct uevent_msg *get_netlink_msg(void)
+{
+       struct uevent_msg *msg;
+       int bufpos;
+       ssize_t size;
+       static char buffer[UEVENT_BUFFER_SIZE + 512];
+       char *pos;
 
-               /* remember some keys for further processing */
-               if (strncmp(key, "ACTION=", 7) == 0)
-                       msg->action = &key[7];
+       size = recv(uevent_netlink_sock, &buffer, sizeof(buffer), 0);
+       if (size <  0) {
+               if (errno != EINTR)
+                       dbg("unable to receive udevd message");
+               return NULL;
+       }
 
-               if (strncmp(key, "DEVPATH=", 8) == 0)
-                       msg->devpath = &key[8];
+       if ((size_t)size > sizeof(buffer)-1)
+               size = sizeof(buffer)-1;
+       buffer[size] = '\0';
+       dbg("uevent_size=%i", size);
 
-               if (strncmp(key, "SUBSYSTEM=", 10) == 0)
-                       msg->subsystem = &key[10];
+       /* start of event payload */
+       bufpos = strlen(buffer)+1;
+       msg = get_msg_from_envbuf(&buffer[bufpos], size-bufpos);
+       if (msg == NULL)
+               return NULL;
+       msg->type = UDEVD_UEVENT_NETLINK;
 
-               if (strncmp(key, "SEQNUM=", 7) == 0)
-                       msg->seqnum = strtoull(&key[7], NULL, 10);
+       /* validate message */
+       pos = strchr(buffer, '@');
+       if (pos == NULL) {
+               dbg("invalid uevent '%s'", buffer);
+               free(msg);
+               return NULL;
+       }
+       pos[0] = '\0';
 
-               if (strncmp(key, "PHYSDEVPATH=", 12) == 0)
-                       msg->physdevpath = &key[12];
+       if (msg->action == NULL) {
+               dbg("no ACTION in payload found, skip event '%s'", buffer);
+               free(msg);
+               return NULL;
+       }
 
-               if (strncmp(key, "TIMEOUT=", 8) == 0)
-                       msg->timeout = strtoull(&key[8], NULL, 10);
+       if (strcmp(msg->action, buffer) != 0) {
+               dbg("ACTION in payload does not match uevent, skip event '%s'", buffer);
+               free(msg);
+               return NULL;
        }
-       msg->envp[i++] = "UDEVD_EVENT=1";
-       msg->envp[i] = NULL;
 
        return msg;
 }
@@ -546,11 +668,13 @@ do_write:
 static void udev_done(int pid)
 {
        /* find msg associated with pid and delete it */
-       struct hotplug_msg *msg;
+       struct uevent_msg *msg;
+       struct sysinfo info;
 
        list_for_each_entry(msg, &running_list, node) {
                if (msg->pid == pid) {
-                       dbg("<== exec seq %llu came back", msg->seqnum);
+                       sysinfo(&info);
+                       info("seq %llu exit, %ld seconds old", msg->seqnum, info.uptime - msg->queue_time);
                        run_queue_delete(msg);
 
                        /* we want to run the exec queue manager since there may
@@ -589,7 +713,7 @@ static void user_sighandler(void)
        }
 }
 
-static int init_udevsend_socket(void)
+static int init_udevd_socket(void)
 {
        struct sockaddr_un saddr;
        socklen_t addrlen;
@@ -602,35 +726,65 @@ static int init_udevsend_socket(void)
        strcpy(&saddr.sun_path[1], UDEVD_SOCK_PATH);
        addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path+1) + 1;
 
-       udevsendsock = socket(AF_LOCAL, SOCK_DGRAM, 0);
-       if (udevsendsock == -1) {
+       udevd_sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
+       if (udevd_sock == -1) {
                err("error getting socket, %s", strerror(errno));
                return -1;
        }
 
        /* the bind takes care of ensuring only one copy running */
-       retval = bind(udevsendsock, (struct sockaddr *) &saddr, addrlen);
+       retval = bind(udevd_sock, (struct sockaddr *) &saddr, addrlen);
        if (retval < 0) {
                err("bind failed, %s", strerror(errno));
-               close(udevsendsock);
+               close(udevd_sock);
                return -1;
        }
 
        /* enable receiving of the sender credentials */
-       setsockopt(udevsendsock, SOL_SOCKET, SO_PASSCRED, &feature_on, sizeof(feature_on));
+       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;
+       int retval;
+
+       memset(&snl, 0x00, sizeof(struct sockaddr_nl));
+       snl.nl_family = AF_NETLINK;
+       snl.nl_pid = getpid();
+       snl.nl_groups = 0xffffffff;
+
+       uevent_netlink_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+       if (uevent_netlink_sock == -1) {
+               dbg("error getting socket, %s", strerror(errno));
+               return -1;
+       }
+
+       retval = bind(uevent_netlink_sock, (struct sockaddr *) &snl,
+                     sizeof(struct sockaddr_nl));
+       if (retval < 0) {
+               dbg("bind failed, %s", strerror(errno));
+               close(uevent_netlink_sock);
+               uevent_netlink_sock = -1;
+               return -1;
+       }
 
        return 0;
 }
 
 int main(int argc, char *argv[], char *envp[])
 {
-       struct sysinfo info;
        int maxsockplus;
        int retval;
        int fd;
        struct sigaction act;
        fd_set readfds;
-       const char *udevd_expected_seqnum;
+       const char *value;
+       int uevent_netlink_active = 0;
+       int daemonize = 0;
+       int i;
 
        logging_init("udevd");
        udev_init_config();
@@ -641,8 +795,18 @@ int main(int argc, char *argv[], char *envp[])
                goto exit;
        }
 
-       /* daemonize on request */
-       if (argc == 2 && strcmp(argv[1], "-d") == 0) {
+       for (i = 1 ; i < argc; i++) {
+               char *arg = argv[i];
+               if (strcmp(arg, "--daemon") == 0 || strcmp(arg, "-d") == 0) {
+                       info("will daemonize");
+                       daemonize = 1;
+               }
+               if (strcmp(arg, "--stop-exec-queue") == 0) {
+                       info("will not execute events until START_EXEC_QUEUE is received");
+                       stop_exec_q = 1;
+               }
+       }
+       if (daemonize) {
                pid_t pid;
 
                pid = fork();
@@ -663,7 +827,6 @@ int main(int argc, char *argv[], char *envp[])
        sid = setsid();
        dbg("our session is %d", sid);
 
-       /* make sure we don't lock any path */
        chdir("/");
        umask(umask(077) | 022);
 
@@ -716,39 +879,65 @@ int main(int argc, char *argv[], char *envp[])
        sigaction(SIGALRM, &act, NULL);
        sigaction(SIGCHLD, &act, NULL);
 
-       if (init_udevsend_socket() < 0) {
+       if (init_uevent_netlink_sock() < 0) {
+               dbg("uevent socket not available");
+       }
+
+       if (init_udevd_socket() < 0) {
                if (errno == EADDRINUSE)
                        dbg("another udevd running, exit");
                else
-                       dbg("error initialising udevsend socket: %s", strerror(errno));
+                       dbg("error initialising udevd socket: %s", strerror(errno));
 
                goto exit;
        }
 
-       /* possible override of udev binary, used for testing */
+       /* override of forked udev binary, used for testing */
        udev_bin = getenv("UDEV_BIN");
        if (udev_bin != NULL)
                info("udev binary is set to '%s'", udev_bin);
        else
                udev_bin = UDEV_BIN;
 
-       /* possible init of expected_seqnum value */
-       udevd_expected_seqnum = getenv("UDEVD_EXPECTED_SEQNUM");
-       if (udevd_expected_seqnum != NULL) {
-               expected_seqnum = strtoull(udevd_expected_seqnum, NULL, 10);
+       /* init of expected_seqnum value */
+       value = getenv("UDEVD_EXPECTED_SEQNUM");
+       if (value) {
+               expected_seqnum = strtoull(value, NULL, 10);
                info("initialize expected_seqnum to %llu", expected_seqnum);
        }
 
-       /* get current time to provide shorter timeout on startup */
-       sysinfo(&info);
-       startup_time = info.uptime;
+       /* timeout to wait for missing events */
+       value = getenv("UDEVD_EVENT_TIMEOUT");
+       if (value)
+               event_timeout = strtoul(value, NULL, 10);
+       else
+               event_timeout = UDEVD_EVENT_TIMEOUT;
+       info("initialize event_timeout to %u", event_timeout);
+
+       /* maximum limit of forked childs */
+       value = getenv("UDEVD_MAX_CHILDS");
+       if (value)
+               max_childs = strtoul(value, NULL, 10);
+       else
+               max_childs = UDEVD_MAX_CHILDS;
+       info("initialize max_childs to %u", 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
+               max_childs_running = UDEVD_MAX_CHILDS_RUNNING;
+       info("initialize max_childs_running to %u", max_childs_running);
 
        FD_ZERO(&readfds);
-       FD_SET(udevsendsock, &readfds);
+       FD_SET(udevd_sock, &readfds);
+       if (uevent_netlink_sock != -1)
+               FD_SET(uevent_netlink_sock, &readfds);
        FD_SET(pipefds[0], &readfds);
-       maxsockplus = udevsendsock+1;
+       maxsockplus = udevd_sock+1;
        while (1) {
-               struct hotplug_msg *msg;
+               struct uevent_msg *msg;
 
                fd_set workreadfds = readfds;
                retval = select(maxsockplus, &workreadfds, NULL, NULL, NULL);
@@ -759,10 +948,29 @@ int main(int argc, char *argv[], char *envp[])
                        continue;
                }
 
-               if (FD_ISSET(udevsendsock, &workreadfds)) {
-                       msg = get_udevsend_msg();
-                       if (msg)
+               if (FD_ISSET(udevd_sock, &workreadfds)) {
+                       msg = get_udevd_msg();
+                       if (msg) {
+                               /* discard kernel messages if netlink is active */
+                               if (uevent_netlink_active && msg->type == UDEVD_UEVENT_UDEVSEND && msg->seqnum != 0) {
+                                       dbg("skip uevent_helper message, netlink is active");
+                                       free(msg);
+                                       continue;
+                               }
                                msg_queue_insert(msg);
+                       }
+               }
+
+               if (FD_ISSET(uevent_netlink_sock, &workreadfds)) {
+                       msg = get_netlink_msg();
+                       if (msg) {
+                               msg_queue_insert(msg);
+                               /* disable udevsend with first netlink message */
+                               if (!uevent_netlink_active) {
+                                       info("uevent_nl message received, disable udevsend messages");
+                                       uevent_netlink_active = 1;
+                               }
+                       }
                }
 
                if (FD_ISSET(pipefds[0], &workreadfds))
@@ -786,7 +994,8 @@ int main(int argc, char *argv[], char *envp[])
                        }
 
                        run_exec_q = 0;
-                       exec_queue_manager();
+                       if (!stop_exec_q)
+                               exec_queue_manager();
                }
        }
 
diff --git a/udevd.h b/udevd.h
index b3e998b..34bfe93 100644 (file)
--- a/udevd.h
+++ b/udevd.h
 
 #define UDEV_MAGIC                     "udevd_" UDEV_VERSION
 #define UDEVD_SOCK_PATH                        "udevd"
-#define SEND_WAIT_MAX_SECONDS          3
-#define SEND_WAIT_LOOP_PER_SECOND      10
+#define UDEVSEND_WAIT_MAX_SECONDS      3
+#define UDEVSEND_WAIT_LOOP_PER_SECOND  10
 
 #define UDEVD_PRIORITY                 -4
 #define UDEV_PRIORITY                  -2
 
 /* duration of initialization phase with shorter timeout */
-#define INIT_TIME_SEC                  5
-#define EVENT_INIT_TIMEOUT_SEC         2
+#define UDEVD_INIT_TIME                        5
+#define UDEVD_INIT_EVENT_TIMEOUT       2
 
 /* timeout to wait for missing events */
-#define EVENT_TIMEOUT_SEC              10
+#define UDEVD_EVENT_TIMEOUT            5
 
+/* maximum limit of runnig childs */
+#define UDEVD_MAX_CHILDS               64
 /* start to throttle forking if maximum number of running childs in our session is reached */
-#define THROTTLE_MAX_RUNNING_CHILDS    10
+#define UDEVD_MAX_CHILDS_RUNNING       8
 
 /* environment buffer, should match the kernel's size in lib/kobject_uevent.h */
-#define HOTPLUG_BUFFER_SIZE            1024
-#define HOTPLUG_NUM_ENVP               32
+#define UEVENT_BUFFER_SIZE             1024
+#define UEVENT_NUM_ENVP                        32
 
-struct udevsend_msg {
-       char magic[20];
-       char envbuf[HOTPLUG_BUFFER_SIZE+256];
+enum udevd_msg_type {
+       UDEVD_UNKNOWN,
+       UDEVD_UEVENT_UDEVSEND,
+       UDEVD_UEVENT_INITSEND,
+       UDEVD_UEVENT_NETLINK,
+       UDEVD_STOP_EXEC_QUEUE,
+       UDEVD_START_EXEC_QUEUE,
+       UDEVD_SET_LOG_LEVEL,
+       UDEVD_SET_MAX_CHILDS,
 };
 
-struct hotplug_msg {
+
+struct udevd_msg {
+       char magic[32];
+       enum udevd_msg_type type;
+       char envbuf[UEVENT_BUFFER_SIZE+512];
+};
+
+struct uevent_msg {
+       enum udevd_msg_type type;
        struct list_head node;
        pid_t pid;
        long queue_time;
@@ -61,6 +77,6 @@ struct hotplug_msg {
        unsigned long long seqnum;
        char *physdevpath;
        unsigned int timeout;
-       char *envp[HOTPLUG_NUM_ENVP+1];
+       char *envp[UEVENT_NUM_ENVP+1];
        char envbuf[];
 };
diff --git a/udeveventrecorder.c b/udeveventrecorder.c
new file mode 100644 (file)
index 0000000..af7ea7c
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * udeveventrecorder.c
+ *
+ * Userspace devfs
+ *
+ * Copyright (C) 2004, 2005 Olaf Hering <olh@suse.de>
+ *
+ *     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.,
+ *     675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "udev.h"
+#include "udev_version.h"
+#include "udev_utils.h"
+#include "logging.h"
+
+#define BUFSIZE 12345
+#define FNSIZE  123
+
+static int log = 0;
+
+#ifdef USE_LOG
+void log_message (int priority, const char *format, ...)
+{
+       va_list args;
+
+       if (priority > log)
+               return;
+
+       va_start(args, format);
+       vsyslog(priority, format, args);
+       va_end(args);
+}
+#endif
+
+int main(int argc, char **argv, char **envp)
+{
+       int fd, i;
+       unsigned long seq;
+       char **ep = envp;
+       char *buf, *p, *a;
+       struct stat sb;
+       const char *env;
+
+       if (stat("/events", &sb) || !(S_ISDIR(sb.st_mode)))
+               return 1;
+
+       env = getenv("UDEV_LOG");
+       if (env)
+               log = log_priority(env);
+
+       logging_init("udeveventrecorder");
+       dbg("version %s", UDEV_VERSION);
+
+       p = getenv("SEQNUM");
+       a = getenv("ACTION");
+       buf = malloc(FNSIZE);
+       if (!(buf && a && argv[1]))
+               goto error;
+       if (p)
+               seq = strtoul(p, NULL, 0);
+       else
+               seq = 0;
+
+       snprintf(buf, FNSIZE, "/events/debug.%05lu.%s.%s.%u", seq, argv[1], a ? a : "", getpid());
+       if ((fd = open(buf, O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0) {
+               err("error creating '%s'", buf);
+               goto error;
+       }
+       free(buf);
+       p = malloc(BUFSIZE);
+       buf = p;
+       buf += snprintf(buf, p + BUFSIZE - buf, "set --");
+       for (i = 1; i < argc; ++i) {
+               buf += snprintf(buf, p + BUFSIZE - buf, " %s", argv[i]);
+               if (buf > p + BUFSIZE)
+                       goto full;
+       }
+       buf += snprintf(buf, p + BUFSIZE - buf, "\n");
+       if (buf > p + BUFSIZE)
+               goto full;
+       while (*ep) {
+               unsigned char *t;
+               t = memchr(*ep, '=', strlen(*ep));
+               if (t) {
+                       *t = '\0';
+                       t++;
+                       buf += snprintf(buf, p + BUFSIZE - buf, "%s='%s'\n", *ep, t);
+                       --t;
+                       *t = '=';
+               }
+               ep++;
+               if (buf > p + BUFSIZE)
+                       break;
+       }
+
+full:
+       buf = p;
+       write(fd, buf, strlen(buf));
+       close(fd);
+       free(buf);
+       return 0;
+
+error:
+       fprintf(stderr, "record enviroment to /events, to be called from udev context\n");
+       return 1;
+}
diff --git a/udevinitsend.c b/udevinitsend.c
new file mode 100644 (file)
index 0000000..26ae5c8
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * udevinitsend.c
+ *
+ * Userspace devfs
+ *
+ * Copyright (C) 2004, 2005 Hannes Reinecke <hare@suse.de>
+ *
+ *     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.,
+ *     675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <dirent.h>
+
+#include "udev.h"
+#include "udev_version.h"
+#include "udevd.h"
+#include "udev_utils.h"
+#include "logging.h"
+
+static int log = 0;
+
+#ifdef USE_LOG
+void log_message (int priority, const char *format, ...)
+{
+       va_list args;
+
+       if (priority > log)
+               return;
+
+       va_start(args, format);
+       vsyslog(priority, format, args);
+       va_end(args);
+}
+#endif
+
+/*
+ * udevsend
+ *
+ * Scan a file, write all variables into the msgbuf and
+ * fires the message to udevd.
+ */
+static int udevsend(char *filename, int sock, int disable_loop_detection)
+{
+       static struct udevd_msg usend_msg;
+       int usend_msg_len;
+       int bufpos = 0;
+       struct stat statbuf;
+       int fd;
+       char *fdmap, *ls, *le, *ch;
+       struct sockaddr_un saddr;
+       socklen_t addrlen;
+       int retval = 0;
+
+       if (stat(filename,&statbuf) < 0) {
+               dbg("cannot stat %s: %s\n", filename, strerror(errno));
+               return 1;
+       }
+       fd = open(filename,O_RDONLY);
+       if (fd < 0)
+               return 1;
+
+       fdmap = mmap(0, statbuf.st_size,
+                    PROT_READ, MAP_PRIVATE, fd, 0);
+       close(fd);
+       if (fdmap == MAP_FAILED) {
+               dbg("mmap failed, errno %d\n", errno);
+               return 1;
+       }
+
+       memset(&saddr, 0x00, sizeof(struct sockaddr_un));
+       saddr.sun_family = AF_LOCAL;
+       /* use abstract namespace for socket path */
+       strcpy(&saddr.sun_path[1], UDEVD_SOCK_PATH);
+       addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path+1) + 1;
+
+       memset(&usend_msg, 0x00, sizeof(struct udevd_msg));
+       strcpy(usend_msg.magic, UDEV_MAGIC);
+       usend_msg.type = UDEVD_UEVENT_INITSEND;
+
+       ls = fdmap;
+       ch = le = ls;
+       while (*ch && ch < fdmap + statbuf.st_size) {
+               le = strchr(ch, '\n');
+               if (!le)
+                       break;
+               ch = strchr(ch, '=');
+               if (!ch)
+                       break;
+
+               /* prevent loops in the scripts we execute */
+               if (strncmp(ls, "UDEVD_EVENT=", 12) == 0) {
+                       if (!disable_loop_detection) {
+                               dbg("event already handled by udev\n");
+                               retval = -1;
+                               break;
+                       } 
+                       goto loop_end;
+               }
+
+               /* omit shell-generated keys */
+               if (ls[0] == '_' && ls[1] == '=') {
+                       goto loop_end;
+               }
+
+               if (ch < le) {
+
+                       strncpy(&usend_msg.envbuf[bufpos],ls,(ch - ls) + 1);
+                       bufpos += (ch - ls) + 1;
+                       if (ch[1] == '\'' && le[-1] == '\'') {
+                               strncpy(&usend_msg.envbuf[bufpos],ch + 2, (le - ch) -3);
+                               bufpos += (le - ch) - 3;
+                       } else {
+                               strncpy(&usend_msg.envbuf[bufpos],ch, (le - ch));
+                               bufpos += (le - ch);
+                       }
+                       bufpos++;
+               }
+loop_end:
+               ch = le + 1;
+               ls = ch;
+       }
+       munmap(fdmap, statbuf.st_size);
+
+       usend_msg_len = offsetof(struct udevd_msg, envbuf) + bufpos;
+       dbg("usend_msg_len=%i", usend_msg_len);
+
+       if (!retval) {
+               retval = sendto(sock, &usend_msg, usend_msg_len, 0, (struct sockaddr *)&saddr, addrlen);
+               if (retval < 0) {
+                       dbg("error sending message (%s)", strerror(errno));
+               }
+       }
+               
+       return retval;
+}
+
+int main(int argc, char *argv[], char *envp[])
+{
+       static const char short_options[] = "d:f:lVh";
+       int option;
+       char *event_dir = NULL;
+       char *event_file = NULL;
+       DIR *dirstream;
+       struct dirent *direntry;
+       int retval = 1;
+       int disable_loop_detection = 0;
+       int sock;
+       const char *env;
+
+       env = getenv("UDEV_LOG");
+       if (env)
+               log = log_priority(env);
+
+       logging_init("udevinitsend");
+       dbg("version %s", UDEV_VERSION);
+
+       /* get command line options */
+       while (1) {
+               option = getopt(argc, argv, short_options);
+               if (option == -1)
+                       break;
+
+               dbg("option '%c': ", option);
+               switch (option) {
+               case 'd':
+                       dbg("scan directory %s\n", optarg);
+                       event_dir = optarg;
+                       break;
+
+               case 'f':
+                       dbg("use event file %s\n", optarg);
+                       event_file = optarg;
+                       break;
+
+               case 'l':
+                       dbg("disable loop detection, ignore UDEVD_EVENT\n");
+                       disable_loop_detection = 1;
+                       break;
+
+               case 'h':
+                       retval = 0;
+               }
+       }
+
+       sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
+       if (sock == -1) {
+               dbg("error getting socket");
+               return 1;
+       }
+
+       if (event_dir) {
+               dirstream = opendir(event_dir);
+               if (!dirstream) {
+                       info("error opening directory %s: %s\n",
+                            event_dir, strerror(errno));
+                       return 1;
+               }
+               chdir(event_dir);
+               while ((direntry = readdir(dirstream)) != NULL) {
+                       if (!strcmp(direntry->d_name,".") ||
+                           !strcmp(direntry->d_name,".."))
+                               continue;
+                       retval = udevsend(direntry->d_name, sock, disable_loop_detection);
+               }
+               closedir(dirstream);
+       } else if (event_file) {
+               retval = udevsend(event_file, sock, disable_loop_detection);
+       }
+
+       if (sock != -1)
+               close(sock);
+
+       return retval;
+}
index 8915eda..bdc69d0 100644 (file)
@@ -115,7 +115,7 @@ static void run_udev(const char *subsystem)
 
 int main(int argc, char *argv[], char *envp[])
 {
-       static struct udevsend_msg usend_msg;
+       static struct udevd_msg usend_msg;
        int usend_msg_len;
        int i;
        int loop;
@@ -144,8 +144,9 @@ int main(int argc, char *argv[], char *envp[])
        strcpy(&saddr.sun_path[1], UDEVD_SOCK_PATH);
        addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path+1) + 1;
 
-       memset(&usend_msg, 0x00, sizeof(struct udevsend_msg));
+       memset(&usend_msg, 0x00, sizeof(struct udevd_msg));
        strcpy(usend_msg.magic, UDEV_MAGIC);
+       usend_msg.type = UDEVD_UEVENT_UDEVSEND;
 
        /* copy all keys to send buffer */
        for (i = 0; envp[i]; i++) {
@@ -161,7 +162,7 @@ int main(int argc, char *argv[], char *envp[])
                        goto exit;
                }
 
-               if (bufpos + keylen >= HOTPLUG_BUFFER_SIZE-1) {
+               if (bufpos + keylen >= UEVENT_BUFFER_SIZE-1) {
                        err("environment buffer too small, probably not called by the kernel");
                        continue;
                }
@@ -180,11 +181,11 @@ int main(int argc, char *argv[], char *envp[])
                dbg("add 'SUBSYSTEM=%s' to env[%i] buffer from argv", argv[1], i);
        }
 
-       usend_msg_len = offsetof(struct udevsend_msg, envbuf) + bufpos;
+       usend_msg_len = offsetof(struct udevd_msg, envbuf) + bufpos;
        dbg("usend_msg_len=%i", usend_msg_len);
 
        /* If we can't send, try to start daemon and resend message */
-       loop = SEND_WAIT_MAX_SECONDS * SEND_WAIT_LOOP_PER_SECOND;
+       loop = UDEVSEND_WAIT_MAX_SECONDS * UDEVSEND_WAIT_LOOP_PER_SECOND;
        while (--loop) {
                retval = sendto(sock, &usend_msg, usend_msg_len, 0, (struct sockaddr *)&saddr, addrlen);
                if (retval != -1) {
@@ -207,8 +208,8 @@ int main(int argc, char *argv[], char *envp[])
                        dbg("udevd daemon started");
                        started_daemon = 1;
                } else {
-                       dbg("retry to connect %d", SEND_WAIT_MAX_SECONDS * SEND_WAIT_LOOP_PER_SECOND - loop);
-                       usleep(1000 * 1000 / SEND_WAIT_LOOP_PER_SECOND);
+                       dbg("retry to connect %d", UDEVSEND_WAIT_MAX_SECONDS * UDEVSEND_WAIT_LOOP_PER_SECOND - loop);
+                       usleep(1000 * 1000 / UDEVSEND_WAIT_LOOP_PER_SECOND);
                }
        }
 
index 60e63c5..bd91e96 100644 (file)
@@ -37,6 +37,7 @@
 
 #include "libsysfs/sysfs/libsysfs.h"
 #include "udev_libc_wrapper.h"
+#include "udev_sysfs.h"
 #include "udev.h"
 #include "udev_version.h"
 #include "logging.h"
@@ -110,25 +111,36 @@ static int add_device(const char *path, const char *subsystem)
        const char *devpath;
 
        devpath = &path[strlen(sysfs_path)];
-
-       /* set environment for callouts and dev.d/ */
        setenv("DEVPATH", devpath, 1);
        setenv("SUBSYSTEM", subsystem, 1);
-
        dbg("exec: '%s' (%s)\n", devpath, path);
 
        class_dev = sysfs_open_class_device_path(path);
        if (class_dev == NULL) {
-               dbg ("sysfs_open_class_device_path failed");
-               return -ENODEV;
+               dbg("sysfs_open_class_device_path failed");
+               return -1;
        }
 
        udev_init_device(&udev, devpath, subsystem, "add");
-       udev_add_device(&udev, class_dev);
+       udev.devt = get_devt(class_dev);
+       if (!udev.devt) {
+               dbg("sysfs_open_class_device_path failed");
+               return -1;
+       }
+       udev_rules_get_name(&udev, class_dev);
+       if (udev.ignore_device) {
+               dbg("device event will be ignored");
+               goto exit;
+       }
+       if (udev.name[0] == '\0') {
+               dbg("device node creation supressed");
+               goto run;
+       }
 
+       udev_add_device(&udev, class_dev);
        if (udev.devname[0] != '\0')
                setenv("DEVNAME", udev.devname, 1);
-
+run:
        if (udev_run && !list_empty(&udev.run_list)) {
                struct name_entry *name_loop;
 
@@ -136,11 +148,7 @@ static int add_device(const char *path, const char *subsystem)
                list_for_each_entry(name_loop, &udev.run_list, node)
                        execute_command(name_loop->name, udev.subsystem);
        }
-
-       /* run dev.d/ scripts if we created a node or changed a netif name */
-       if (udev_dev_d && udev.devname[0] != '\0')
-               udev_multiplex_directory(&udev, DEVD_DIR, DEVD_SUFFIX);
-
+exit:
        sysfs_close_class_device(class_dev);
        udev_cleanup_device(&udev);
 
index 93387f7..d3e4359 100644 (file)
@@ -98,12 +98,6 @@ int main(int argc, char *argv[], char *envp[])
        /* fill in values and test_run flag*/
        udev_init_device(&udev, devpath, subsystem, "add");
 
-       /* skip subsystems without "dev", but handle net devices */
-       if (udev.type != DEV_NET && subsystem_expect_no_dev(udev.subsystem)) {
-               info("don't care about '%s' devices", udev.subsystem);
-               return 2;
-       }
-
        /* open the device */
        snprintf(path, sizeof(path), "%s%s", sysfs_path, udev.devpath);
        path[sizeof(path)-1] = '\0';
@@ -112,13 +106,18 @@ int main(int argc, char *argv[], char *envp[])
                info("sysfs_open_class_device_path failed");
                return 1;
        }
-
        info("opened class_dev->name='%s'", class_dev->name);
 
+       if (udev.type == DEV_BLOCK || udev.type == DEV_CLASS)
+               udev.devt = get_devt(class_dev);
+
        /* simulate node creation with test flag */
        udev.test_run = 1;
-       udev_add_device(&udev, class_dev);
-
+       if (udev.type == DEV_NET || udev.devt) {
+               udev_rules_get_name(&udev, class_dev);
+               udev_add_device(&udev, class_dev);
+       } else
+               info("only char and block devices with a dev-file are supported by this test program");
        sysfs_close_class_device(class_dev);
 
        return 0;