1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-21 02:50:18 +03:00

Merge gregkh@ehlo.org:/home/kay/public_html/pub/scm/linux/hotplug/udev-kay

This commit is contained in:
Greg KH 2005-06-21 16:36:29 -07:00 committed by Greg Kroah-Hartman
commit 972d318a31
58 changed files with 2252 additions and 854 deletions

View File

@ -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)

3
README
View File

@ -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

View File

@ -1,5 +1,4 @@
# udev.conf
#
# Where in the filesystem to place the device nodes
udev_root="@udevdir@"

View 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"

View 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"

View File

@ -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)

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View 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)

View File

@ -40,12 +40,26 @@
#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

View 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

View File

@ -0,0 +1 @@
#include <linux/route.h>

View File

@ -0,0 +1 @@
#include <linux/if_packet.h>

View 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);

View 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)

View 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}) ) {

149
klibc/klibc/Kbuild Normal file
View File

@ -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)

View 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 *)

View 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 \

View File

@ -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

View File

@ -15,8 +15,13 @@
# 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

View 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))

View File

@ -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

View 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

View File

@ -20,6 +20,7 @@ char *fgets(char *s, int n, FILE *f)
return NULL;
}
*p++ = ch;
n--;
if ( ch == '\n' )
break;
}

View 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;
}

View 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

View 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

View 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

View 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 ) {

0
klibc/makeklcc.pl Normal file → Executable file
View File

View File

@ -1 +1 @@
1.0.7
1.0.14

2
test/simple-build-check.sh Normal file → Executable file
View File

@ -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"

View File

@ -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" ,
@ -360,6 +360,51 @@ EOF
exp_name => "my-foo8" ,
rules => <<EOF
BUS=="scsi", PROGRAM=="/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9", KERNEL=="sda3", NAME="my-%c{6}"
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
},
{
@ -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
},
@ -1304,6 +1349,43 @@ EOF
KERNEL=="sda", NAME="ok2", RUN+="/bin/ln -s ok2 %r/testsymlink2"
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
},
);
@ -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);
}

View File

@ -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),

156
udev.c
View File

@ -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);
}
udev_init_device(&udev, devpath, subsystem, action);
udev_rules_init();
if (udev.type == DEV_BLOCK || udev.type == DEV_CLASS || udev.type == DEV_NET) {
udev_rules_init();
/* 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;
}

15
udev.h
View File

@ -39,12 +39,6 @@
#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

View File

@ -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

View File

@ -52,23 +52,6 @@ int udev_make_node(struct udevice *udev, const char *file, dev_t devt, mode_t mo
struct stat stats;
int retval = 0;
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)) {
info("preserve file '%s', cause it has correct dev_t", file);
selinux_setfilecon(file, udev->kernel_name, stats.st_mode);
goto perms;
}
if (unlink(file) != 0)
dbg("unlink(%s) failed with error '%s'", file, strerror(errno));
else
dbg("already present file '%s' unlinked", file);
create:
switch (udev->type) {
case DEV_BLOCK:
mode |= S_IFBLK;
@ -81,6 +64,22 @@ create:
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) == (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;
}
if (unlink(file) != 0)
dbg("unlink(%s) failed with error '%s'", file, strerror(errno));
else
dbg("already present file '%s' unlinked", file);
create:
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;
}

View 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));

View File

@ -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);
}
}

View 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);

View 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;
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;
pos = string;
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);
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;
/* add multiple symlinks separated by spaces */
pos = temp;
next = strchr(temp, ' ');
while (next) {
next[0] = '\0';
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);
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;
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);
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;
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_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 (!udev->run_final && rule->run_operation != KEY_OP_UNSET) {
char program[PATH_SIZE];
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;
}
dbg(KEY_SUBSYSTEM " key is true");
}
if (rule->run_operation == KEY_OP_ASSIGN || rule->run_operation == KEY_OP_ASSIGN_FINAL) {
struct name_entry *name_loop;
struct name_entry *temp_loop;
if (rule->env_pair_count) {
int i;
dbg("check for " KEY_ENV " pairs");
for (i = 0; i < rule->env_pair_count; i++) {
struct key_pair *pair;
const char *value;
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;
}
}
}

View 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

View 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;
}

View 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;

View 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:

View 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);

136
udevcontrol.c Normal file
View File

@ -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;
}

481
udevd.c
View File

@ -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"
@ -48,16 +49,17 @@
#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,67 +448,21 @@ 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;
ssize_t size;
struct msghdr smsg;
struct cmsghdr *cmsg;
struct iovec iov;
struct ucred *cred;
char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
int envbuf_size;
struct uevent_msg *msg;
memset(&usend_msg, 0x00, sizeof(struct udevsend_msg));
iov.iov_base = &usend_msg;
iov.iov_len = sizeof(struct udevsend_msg);
memset(&smsg, 0x00, sizeof(struct msghdr));
smsg.msg_iov = &iov;
smsg.msg_iovlen = 1;
smsg.msg_control = cred_msg;
smsg.msg_controllen = sizeof(cred_msg);
size = recvmsg(udevsendsock, &smsg, 0);
if (size < 0) {
if (errno != EINTR)
dbg("unable to receive udevsend message");
return NULL;
}
cmsg = CMSG_FIRSTHDR(&smsg);
cred = (struct ucred *) CMSG_DATA(cmsg);
if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
info("no sender credentials received, message ignored");
return NULL;
}
if (cred->uid != 0) {
info("sender uid=%i, message ignored", cred->uid);
return NULL;
}
if (strncmp(usend_msg.magic, UDEV_MAGIC, sizeof(UDEV_MAGIC)) != 0 ) {
info("message magic '%s' doesn't match, ignore it", usend_msg.magic);
return NULL;
}
envbuf_size = size - offsetof(struct udevsend_msg, envbuf);
dbg("envbuf_size=%i", envbuf_size);
msg = malloc(sizeof(struct hotplug_msg) + envbuf_size);
msg = malloc(sizeof(struct uevent_msg) + buf_size);
if (msg == NULL)
return NULL;
memset(msg, 0x00, sizeof(struct hotplug_msg) + envbuf_size);
memset(msg, 0x00, sizeof(struct uevent_msg) + buf_size);
/* copy environment buffer and reconstruct envp */
memcpy(msg->envbuf, usend_msg.envbuf, envbuf_size);
memcpy(msg->envbuf, buf, buf_size);
bufpos = 0;
for (i = 0; (bufpos < envbuf_size) && (i < HOTPLUG_NUM_ENVP-2); i++) {
for (i = 0; (bufpos < buf_size) && (i < UEVENT_NUM_ENVP-2); i++) {
int keylen;
char *key;
@ -511,6 +497,142 @@ static struct hotplug_msg *get_udevsend_msg(void)
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;
struct iovec iov;
struct ucred *cred;
char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
int envbuf_size;
int *intval;
memset(&usend_msg, 0x00, sizeof(struct udevd_msg));
iov.iov_base = &usend_msg;
iov.iov_len = sizeof(struct udevd_msg);
memset(&smsg, 0x00, sizeof(struct msghdr));
smsg.msg_iov = &iov;
smsg.msg_iovlen = 1;
smsg.msg_control = cred_msg;
smsg.msg_controllen = sizeof(cred_msg);
size = recvmsg(udevd_sock, &smsg, 0);
if (size < 0) {
if (errno != EINTR)
dbg("unable to receive udevd message");
return NULL;
}
cmsg = CMSG_FIRSTHDR(&smsg);
cred = (struct ucred *) CMSG_DATA(cmsg);
if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
info("no sender credentials received, message ignored");
return NULL;
}
if (cred->uid != 0) {
info("sender uid=%i, message ignored", cred->uid);
return NULL;
}
if (strncmp(usend_msg.magic, UDEV_MAGIC, sizeof(UDEV_MAGIC)) != 0 ) {
info("message magic '%s' doesn't match, ignore it", usend_msg.magic);
return NULL;
}
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;
}
/* 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;
size = recv(uevent_netlink_sock, &buffer, sizeof(buffer), 0);
if (size < 0) {
if (errno != EINTR)
dbg("unable to receive udevd message");
return NULL;
}
if ((size_t)size > sizeof(buffer)-1)
size = sizeof(buffer)-1;
buffer[size] = '\0';
dbg("uevent_size=%i", size);
/* 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;
/* validate message */
pos = strchr(buffer, '@');
if (pos == NULL) {
dbg("invalid uevent '%s'", buffer);
free(msg);
return NULL;
}
pos[0] = '\0';
if (msg->action == NULL) {
dbg("no ACTION in payload found, skip event '%s'", buffer);
free(msg);
return NULL;
}
if (strcmp(msg->action, buffer) != 0) {
dbg("ACTION in payload does not match uevent, skip event '%s'", buffer);
free(msg);
return NULL;
}
return msg;
}
static void asmlinkage sig_handler(int signum)
{
int rc;
@ -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();
}
}

42
udevd.h
View File

@ -26,32 +26,48 @@
#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[];
};

127
udeveventrecorder.c Normal file
View File

@ -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;
}

239
udevinitsend.c Normal file
View File

@ -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;
}

View 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);
}
}

View 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);

View 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;