chiark / gitweb /
add persistent rules generator for net devices and optical drives
authorMarco d'Itri <md@Linux.IT>
Tue, 5 Sep 2006 13:20:28 +0000 (15:20 +0200)
committerKay Sievers <kay.sievers@suse.de>
Tue, 5 Sep 2006 13:20:28 +0000 (15:20 +0200)
extras/rule_generator/75-cd-aliases-generator.rules [new file with mode: 0644]
extras/rule_generator/75-persistent-net-generator.rules [new file with mode: 0644]
extras/rule_generator/Makefile [new file with mode: 0644]
extras/rule_generator/rule_generator.functions [new file with mode: 0644]
extras/rule_generator/write_cd_rules [new file with mode: 0644]
extras/rule_generator/write_net_rules [new file with mode: 0644]
test/simple-build-check.sh

diff --git a/extras/rule_generator/75-cd-aliases-generator.rules b/extras/rule_generator/75-cd-aliases-generator.rules
new file mode 100644 (file)
index 0000000..c3eb887
--- /dev/null
@@ -0,0 +1,3 @@
+# these rules generate rules for the /dev/{cdrom,dvd,...} symlinks
+
+ACTION=="add", SUBSYSTEM=="block", ENV{ID_CDROM}=="?*", ENV{GENERATED}!="?*", PROGRAM="write_cd_rules", SYMLINK+="%c"
diff --git a/extras/rule_generator/75-persistent-net-generator.rules b/extras/rule_generator/75-persistent-net-generator.rules
new file mode 100644 (file)
index 0000000..fdebb91
--- /dev/null
@@ -0,0 +1,17 @@
+# these rules generate rules for persistent network device naming
+
+ACTION=="add", SUBSYSTEM=="net", NAME!="?*", DRIVERS=="?*", GOTO="persistent_net_generator_do"
+GOTO="persistent_net_generator_end"
+
+LABEL="persistent_net_generator_do"
+# export device description to comment the generated rule
+SUBSYSTEMS=="pci", ENV{COMMENT}="PCI Device: $attr{vendor}:$attr{device} ($attr{driver})"
+SUBSYSTEMS=="usb", ENV{COMMENT}="USB Device: 0x$attr{idVendor}:0x$attr{idProduct} ($attr{driver})"
+SUBSYSTEMS=="ieee1394", ENV{COMMENT}="Firewire Device: $attr{host_id} ($attr{driver})"
+SUBSYSTEMS=="xen", ENV{COMMENT}="Xen virtual device"
+
+KERNEL=="eth*|ath*|wlan*|ra*|sta*", IMPORT{program}="write_net_rules $attr{address}"
+ENV{INTERFACE_NEW}=="?*", NAME="$env{INTERFACE_NEW}"
+
+LABEL="persistent_net_generator_end"
+
diff --git a/extras/rule_generator/Makefile b/extras/rule_generator/Makefile
new file mode 100644 (file)
index 0000000..810be24
--- /dev/null
@@ -0,0 +1,68 @@
+# Makefile for udev extra invoked from the udev main Makefile
+#
+# Copyright (C) 2004-2006 Kay Sievers <kay.sievers@vrfy.org>
+#
+# Released under the GNU General Public License, version 2.
+#
+
+PROG =
+MAN_PAGES =
+
+prefix =
+etcdir =       ${prefix}/etc
+sbindir =      ${prefix}/sbin
+usrbindir =    ${prefix}/usr/bin
+usrsbindir =   ${prefix}/usr/sbin
+libudevdir =   ${prefix}/lib/udev
+mandir =       ${prefix}/usr/share/man
+configdir =    ${etcdir}/udev
+
+INSTALL = /usr/bin/install -c
+INSTALL_PROGRAM = ${INSTALL}
+INSTALL_DATA  = ${INSTALL} -m 644
+INSTALL_SCRIPT = ${INSTALL_PROGRAM}
+
+all: $(PROG) $(MAN_PAGES)
+.PHONY: all
+.DEFAULT: all
+
+# man pages
+%.8: %.xml
+       $(E) "  XMLTO   " $@
+       $(Q) xmlto man $?
+.PRECIOUS: %.8
+
+clean:
+       $(E) "  CLEAN   "
+.PHONY: clean
+
+install-bin: all
+       $(INSTALL_PROGRAM) -D rule_generator.functions $(DESTDIR)$(libudevdir)/rule_generator.functions
+       $(INSTALL_PROGRAM) -D write_cd_rules $(DESTDIR)$(libudevdir)/write_cd_rules
+       $(INSTALL_PROGRAM) -D write_net_rules $(DESTDIR)$(libudevdir)/write_net_rules
+       $(INSTALL_DATA) -D 75-cd-aliases-generator.rules \
+               $(DESTDIR)$(configdir)/rules.d/75-cd-aliases-generator.rules
+       $(INSTALL_DATA) -D 75-persistent-net-generator.rules \
+               $(DESTDIR)$(configdir)/rules.d/75-persistent-net-generator.rules
+.PHONY: install-bin
+
+uninstall-bin:
+       - rm $(DESTDIR)$(libudevdir)/rule_generator.functions
+       - rm $(DESTDIR)$(libudevdir)/write_cd_rules
+       - rm $(DESTDIR)$(libudevdir)/write_net_rules
+       - rm $(DESTDIR)$(configdir)/rules.d/75-cd-aliases-generator.rules
+       - rm $(DESTDIR)$(configdir)/rules.d/75-persistent-net-generator.rules
+.PHONY: uninstall-bin
+
+install-man:
+       @echo "Please create a man page for this tool."
+.PHONY: install-man
+
+uninstall-man:
+       @echo "Please create a man page for this tool."
+.PHONY: uninstall-man
+
+install-config:
+       @echo "no config file to install"
+.PHONY: install-config
+
diff --git a/extras/rule_generator/rule_generator.functions b/extras/rule_generator/rule_generator.functions
new file mode 100644 (file)
index 0000000..14c585a
--- /dev/null
@@ -0,0 +1,98 @@
+# functions used by the udev rule generator
+#
+# 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.
+
+PATH='/sbin:/bin'
+
+# Read a single line from file $1 in the $DEVPATH directory.
+# The function must not return an error even if the file does not exist.
+sysread() {
+       local file="$1"
+       [ -e "/sys$DEVPATH/$file" ] || return 0
+       local value
+       read value < "/sys$DEVPATH/$file" || return 0
+       echo "$value"
+}
+
+sysreadlink() {
+       local file="$1"
+       [ -e "/sys$DEVPATH/$file" ] || return 0
+       readlink -f /sys$DEVPATH/$file 2> /dev/null || true
+}
+
+# Return true if a directory is writeable.
+writeable() {
+       if ln -s test-link $1/.is-writeable 2> /dev/null; then
+               rm -f $1/.is-writeable
+               return 0
+       else
+               return 1
+       fi
+}
+
+# Create a lock file for the current rules file.
+lock_rules_file() {
+       [ -e /dev/.udev/ ] || return 0
+
+       RULES_LOCK="/dev/.udev/.lock-${RULES_FILE##*/}"
+
+       retry=30
+       while ! mkdir $RULES_LOCK 2> /dev/null; do
+               if [ $retry -eq 0 ]; then
+                        echo "Cannot lock $RULES_FILE!" >&2
+                        exit 2
+               fi
+               sleep 1
+               retry=$(($retry - 1))
+       done
+}
+
+unlock_rules_file() {
+       [ "$RULES_LOCK" ] || return 0
+       rmdir $RULES_LOCK || true
+}
+
+# Choose the real rules file if it is writeable or a temporary file if not.
+# Both files should be checked later when looking for existing rules.
+choose_rules_file() {
+       local tmp_rules_file="/dev/.udev/tmp-rules--${RULES_FILE##*/}"
+       [ -e "$RULES_FILE" -o -e "$tmp_rules_file" ] || PRINT_HEADER=1
+
+       if writeable ${RULES_FILE%/*}; then
+               RO_RULES_FILE='/dev/null'
+       else
+               RO_RULES_FILE=$RULES_FILE
+               RULES_FILE=$tmp_rules_file
+       fi
+}
+
+# Return the name of the first free device.
+raw_find_next_available() {
+       local links="$1"
+
+       local basename=${links%%[ 0-9]*}
+       local max=-1
+       for name in $links; do
+               local num=${name#$basename}
+               [ "$num" ] || num=0
+               [ $num -gt $max ] && max=$num
+       done
+
+       local max=$(($max + 1))
+       # "name0" actually is just "name"
+       [ $max -eq 0 ] && return
+       echo "$max"
+}
+
+# Find all rules matching a key (with action) and a pattern.
+find_all_rules() {
+       local key="$1"
+       local linkre="$2"
+       local match="$3"
+
+       [ -e $RULES_FILE ] || return
+       local search='.*[[:space:],]'"$key"'"\('"$linkre"'\)"[[:space:]]*\(,.*\|\\\|\)$'
+       echo $(sed -n -e "${match}s/${search}/\1/p" $RO_RULES_FILE $RULES_FILE)
+}
diff --git a/extras/rule_generator/write_cd_rules b/extras/rule_generator/write_cd_rules
new file mode 100644 (file)
index 0000000..232daeb
--- /dev/null
@@ -0,0 +1,78 @@
+#!/bin/sh -e
+
+# This script is run if an optical drive lacks a rule for persistent naming.
+#
+# It adds symlinks for optical drives based on the device class determined
+# by cdrom_id and used ID_PATH to identify the device.
+#
+# (C) 2006 Marco d'Itri <md@Linux.IT>
+#
+# 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.
+
+RULES_FILE="/etc/udev/rules.d/70-persistent-cd.rules"
+
+. /lib/udev/rule_generator.functions
+
+find_next_available() {
+       raw_find_next_available "$(find_all_rules 'SYMLINK+=' $1)"
+}
+
+write_rule() {
+       local match="$1"
+       local link="$2"
+       local comment="$3"
+
+       {
+       if [ "$PRINT_HEADER" ]; then
+               PRINT_HEADER=
+               echo "# This file was automatically generated by the $0"
+               echo "# program, probably run by the cd-aliases-generator.rules rules file."
+               echo "#"
+               echo "# You can modify it, as long as you keep each rule on a single line"
+               echo "# and set the \$GENERATED variable."
+               echo ""
+       fi
+
+       [ "$comment" ] && echo "# $comment"
+       echo "$match, SYMLINK+=\"$link\", ENV{GENERATED}=\"1\""
+       } >> $RULES_FILE
+       SYMLINKS="$SYMLINKS $link"
+}
+
+if [ -z "$DEVPATH" ]; then
+       echo "Missing \$DEVPATH." >&2
+       exit 1
+fi
+if [ -z "$ID_CDROM" ]; then
+       echo "$DEVPATH is not a CD reader." >&2
+       exit 1
+fi
+
+# Prevent concurrent processes from modifying the file at the same time.
+lock_rules_file
+
+# Check if the rules file is writeable.
+choose_rules_file
+
+link_num=$(find_next_available 'cdrom[0-9]*')
+
+match="ENV{ID_CDROM}==\"?*\", ENV{ID_PATH}==\"$ID_PATH\""
+
+comment="$ID_MODEL ($ID_PATH)"
+
+       write_rule "$match" "cdrom$link_num" "$comment"
+[ "$ID_CDROM_CD_R" -o "$ID_CDROM_CD_RW" ] && \
+       write_rule "$match" "cdrw$link_num"
+[ "$ID_CDROM_DVD" ] && \
+       write_rule "$match" "dvd$link_num"
+[ "$ID_CDROM_DVD_R" -o "$ID_CDROM_DVD_RW" -o "$ID_CDROM_DVD_RAM" ] && \
+       write_rule "$match" "dvdrw$link_num"
+
+unlock_rules_file
+
+echo $SYMLINKS
+
+exit 0
+
diff --git a/extras/rule_generator/write_net_rules b/extras/rule_generator/write_net_rules
new file mode 100644 (file)
index 0000000..b709200
--- /dev/null
@@ -0,0 +1,130 @@
+#!/bin/sh -e
+#
+# This script is run if the interface (recognized by its MAC address) lacks
+# a rule for persistent naming.
+#
+# If there is already a persistent rule with that interface name then the
+# current interface needs to be renamed.
+#
+# If the interface needs to be renamed, a NAME=value pair will be printed
+# on stdout to allow udev to IMPORT it. Then a rule for the MAC address and
+# interface name is written.
+#
+# (C) 2006 Marco d'Itri <md@Linux.IT>
+#
+# 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.
+
+RULES_FILE='/etc/udev/rules.d/70-persistent-net.rules'
+
+. /lib/udev/rule_generator.functions
+
+interface_name_taken() {
+       local value="$(find_all_rules 'NAME=' $INTERFACE)"
+       if [ "$value" ]; then
+               return 0
+       else
+               return 1
+       fi
+}
+
+find_next_available() {
+       raw_find_next_available "$(find_all_rules 'NAME=' "$1")"
+}
+
+write_rule() {
+       local match="$1"
+       local name="$2"
+       local comment="$3"
+
+       {
+       if [ "$PRINT_HEADER" ]; then
+               PRINT_HEADER=
+               echo "# This file was automatically generated by the $0"
+               echo "# program, probably run by the persistent-net-generator.rules rules file."
+               echo "#"
+               echo "# You can modify it, as long as you keep each rule on a single line."
+       fi
+
+       echo ""
+       [ "$comment" ] && echo "# $comment"
+       echo "SUBSYSTEM==\"net\", $match, NAME=\"$name\""
+       } >> $RULES_FILE
+}
+
+# used only if $RULES_FILE is empty, like on installation
+if [ "$1" = "all_interfaces" ]; then
+       if [ -e $RULES_FILE ]; then
+               printf "$RULES_FILE exists, persistent interface names\nnot saved.\n" >&2
+               exit 0
+       fi
+
+       if [ ! -e /sys/class/net/ ]; then
+               echo "/sys/class/net/ is not available, persistent interface names not saved." >&2
+               exit 0
+       fi
+
+       cd /sys/class/net/ || return 0
+
+       for INTERFACE in *; do
+               case $INTERFACE in
+               eth*|ath*|wlan*|ra*|sta*) ;;
+               *) continue ;;
+               esac
+
+               INTERFACE="$INTERFACE" DEVPATH="/class/net/$INTERFACE" \
+                       /lib/udev/write_net_rules || true
+       done
+
+       exit 0
+fi
+
+if [ -z "$INTERFACE" ]; then
+       echo "Missing \$INTERFACE." >&2
+       exit 1
+fi
+
+if [ "$1" ]; then
+       MAC_ADDR="$1"
+else
+       MAC_ADDR=$(sysread address)
+fi
+
+if [ -z "$MAC_ADDR" ]; then
+       echo "No MAC address for $INTERFACE." >&2
+       exit 1
+fi
+if [ "$MAC_ADDR" = "00:00:00:00:00:00" ]; then
+       echo "NULL MAC address for $INTERFACE." >&2
+       exit 1
+fi
+
+# Prevent concurrent processes from modifying the file at the same time.
+lock_rules_file
+
+# Check if the rules file is writeable.
+choose_rules_file
+
+# If a rule using the current name already exists then find a new name and
+# report it to udev which will rename the interface.
+basename=${INTERFACE%%[0-9]*}
+if interface_name_taken; then
+       INTERFACE="$basename$(find_next_available "$basename[0-9]*")"
+       if [ ! -t 1 ]; then
+               echo "INTERFACE_NEW=$INTERFACE"
+       fi
+fi
+
+# the DRIVERS key is needed to not match bridges and VLAN sub-interfaces
+match="DRIVERS==\"?*\", ATTRS{address}==\"$MAC_ADDR\""
+if [ $basename = "ath" -o $basename = "wlan" ]; then
+       match="$match, ATTRS{type}==\"1\"" # do not match the wifi* interfaces
+fi
+
+write_rule "$match" "$INTERFACE" "$COMMENT"
+
+unlock_rules_file
+
+exit 0
+
index 6a03608..385abbd 100755 (executable)
@@ -11,7 +11,9 @@ EXTRAS="\
        extras/edd_id \
        extras/floppy \
        extras/run_directory \
-       extras/firmware"
+       extras/firmware \
+       extras/path_id \
+       extras/rule_generator"
 
 # with debug
 make clean EXTRAS="$EXTRAS" >/dev/null