From fbcbf70bb21a618024ce8c1ea0b4f6718c2a8dd2 Mon Sep 17 00:00:00 2001 From: Marco d'Itri Date: Tue, 5 Sep 2006 15:20:28 +0200 Subject: [PATCH] add persistent rules generator for net devices and optical drives --- .../75-cd-aliases-generator.rules | 3 + .../75-persistent-net-generator.rules | 17 +++ extras/rule_generator/Makefile | 68 +++++++++ .../rule_generator/rule_generator.functions | 98 +++++++++++++ extras/rule_generator/write_cd_rules | 78 +++++++++++ extras/rule_generator/write_net_rules | 130 ++++++++++++++++++ test/simple-build-check.sh | 4 +- 7 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 extras/rule_generator/75-cd-aliases-generator.rules create mode 100644 extras/rule_generator/75-persistent-net-generator.rules create mode 100644 extras/rule_generator/Makefile create mode 100644 extras/rule_generator/rule_generator.functions create mode 100644 extras/rule_generator/write_cd_rules create mode 100644 extras/rule_generator/write_net_rules diff --git a/extras/rule_generator/75-cd-aliases-generator.rules b/extras/rule_generator/75-cd-aliases-generator.rules new file mode 100644 index 000000000..c3eb887cb --- /dev/null +++ b/extras/rule_generator/75-cd-aliases-generator.rules @@ -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 index 000000000..fdebb919b --- /dev/null +++ b/extras/rule_generator/75-persistent-net-generator.rules @@ -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 index 000000000..810be24d2 --- /dev/null +++ b/extras/rule_generator/Makefile @@ -0,0 +1,68 @@ +# Makefile for udev extra invoked from the udev main Makefile +# +# Copyright (C) 2004-2006 Kay Sievers +# +# Released under the GNU General Public License, version 2. +# + +PROG = +MAN_PAGES = + +prefix = +etcdir = ${prefix}/etc +sbindir = ${prefix}/sbin +usrbindir = ${prefix}/usr/bin +usrsbindir = ${prefix}/usr/sbin +libudevdir = ${prefix}/lib/udev +mandir = ${prefix}/usr/share/man +configdir = ${etcdir}/udev + +INSTALL = /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 index 000000000..14c585a86 --- /dev/null +++ b/extras/rule_generator/rule_generator.functions @@ -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 index 000000000..232daeb86 --- /dev/null +++ b/extras/rule_generator/write_cd_rules @@ -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 +# +# 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 index 000000000..b709200a3 --- /dev/null +++ b/extras/rule_generator/write_net_rules @@ -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 +# +# 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 + diff --git a/test/simple-build-check.sh b/test/simple-build-check.sh index 6a0360847..385abbd09 100755 --- a/test/simple-build-check.sh +++ b/test/simple-build-check.sh @@ -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 -- 2.30.2