From 305f0eef4de67c09598271caf1d19c5436cd40d7 Mon Sep 17 00:00:00 2001 From: Ian Stakenvicius Date: Tue, 22 Sep 2015 13:53:41 -0400 Subject: Forward-ported network rule-generator code from eudev-1.10 --- Makefile.am | 4 + configure.ac | 15 +++ rule_generator/75-persistent-net-generator.rules | 100 ++++++++++++++++ rule_generator/Makefile.am | 13 +++ rule_generator/rule_generator.functions | 120 +++++++++++++++++++ rule_generator/write_net_rules.in | 141 +++++++++++++++++++++++ rules/Makefile.am | 6 +- src/udev/udev-event.c | 135 ++++++++++++++++++++-- src/udev/udev-rules.c | 66 +++++++++++ 9 files changed, 588 insertions(+), 12 deletions(-) create mode 100644 rule_generator/75-persistent-net-generator.rules create mode 100644 rule_generator/Makefile.am create mode 100644 rule_generator/rule_generator.functions create mode 100644 rule_generator/write_net_rules.in diff --git a/Makefile.am b/Makefile.am index 29326906a3..0c6429df60 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,3 +15,7 @@ SUBDIRS += \ hwdb endif +if ENABLE_RULE_GENERATOR +SUBDIRS += \ + rule_generator +endif diff --git a/configure.ac b/configure.ac index ccb1012d57..56d37fa113 100644 --- a/configure.ac +++ b/configure.ac @@ -268,11 +268,26 @@ AM_CONDITIONAL(HAVE_KMOD, [test "$have_kmod" = "yes"]) AC_ARG_ENABLE([hwdb], AS_HELP_STRING([--enable-hwdb],[install hwdb.d files]),[],[enable_hwdb=yes]) AM_CONDITIONAL(ENABLE_HWDB, [test "x$enable_hwdb" = "xyes"]) +# ------------------------------------------------------------------------------ +# rule-generator - persistent network and optical device rule generator +# ------------------------------------------------------------------------------ +AC_ARG_ENABLE([rule-generator], + AS_HELP_STRING([--enable-rule-generator], [enable legacy persistent network, cdrom support]), + [], [enable_rule_generator=no]) + +if test "x${enable_rule_generator}" != xno; then + AC_DEFINE([ENABLE_RULE_GENERATOR], [1], [Define if we are enabling rule generator]) +fi + +AM_CONDITIONAL([ENABLE_RULE_GENERATOR], [test "x$enable_rule_generator" = xyes]) + # ------------------------------------------------------------------------------ AC_CONFIG_FILES([Makefile hwdb/Makefile man/Makefile + rule_generator/Makefile + rule_generator/write_net_rules rules/Makefile src/Makefile src/ata_id/Makefile diff --git a/rule_generator/75-persistent-net-generator.rules b/rule_generator/75-persistent-net-generator.rules new file mode 100644 index 0000000000..ce59e5d82b --- /dev/null +++ b/rule_generator/75-persistent-net-generator.rules @@ -0,0 +1,100 @@ +# do not edit this file, it will be overwritten on update + +# these rules generate rules for persistent network device naming +# +# variables used to communicate: +# MATCHADDR MAC address used for the match +# MATCHID bus_id used for the match +# MATCHDRV driver name used for the match +# MATCHIFTYPE interface type match +# COMMENT comment to add to the generated rule +# INTERFACE_NAME requested name supplied by external tool +# INTERFACE_NEW new interface name returned by rule writer + +ACTION!="add", GOTO="persistent_net_generator_end" +SUBSYSTEM!="net", GOTO="persistent_net_generator_end" + +# ignore the interface if a name has already been set +NAME=="?*", GOTO="persistent_net_generator_end" + +# device name whitelist +KERNEL!="eth*|ath*|wlan*[0-9]|msh*|ra*|sta*|ctc*|lcs*|hsi*", GOTO="persistent_net_generator_end" + +# ignore Xen virtual interfaces +SUBSYSTEMS=="xen", GOTO="persistent_net_generator_end" + +# read MAC address +ENV{MATCHADDR}="$attr{address}" + +# match interface type +ENV{MATCHIFTYPE}="$attr{type}" + +# ignore KVM virtual interfaces +ENV{MATCHADDR}=="52:54:00:*", GOTO="persistent_net_generator_end" +# ignore VMWare virtual interfaces +ENV{MATCHADDR}=="00:0c:29:*|00:50:56:*", GOTO="persistent_net_generator_end" +# ignore Hyper-V virtual interfaces +ENV{MATCHADDR}=="00:15:5d:*", GOTO="persistent_net_generator_end" + +# These vendors are known to violate the local MAC address assignment scheme +# Interlan, DEC (UNIBUS or QBUS), Apollo, Cisco, Racal-Datacom +ENV{MATCHADDR}=="02:07:01:*", GOTO="globally_administered_whitelist" +# 3Com +ENV{MATCHADDR}=="02:60:60:*", GOTO="globally_administered_whitelist" +# 3Com IBM PC; Imagen; Valid; Cisco; Apple +ENV{MATCHADDR}=="02:60:8c:*", GOTO="globally_administered_whitelist" +# Intel +ENV{MATCHADDR}=="02:a0:c9:*", GOTO="globally_administered_whitelist" +# Olivetti +ENV{MATCHADDR}=="02:aa:3c:*", GOTO="globally_administered_whitelist" +# CMC Masscomp; Silicon Graphics; Prime EXL +ENV{MATCHADDR}=="02:cf:1f:*", GOTO="globally_administered_whitelist" +# Prominet Corporation Gigabit Ethernet Switch +ENV{MATCHADDR}=="02:e0:3b:*", GOTO="globally_administered_whitelist" +# BTI (Bus-Tech, Inc.) IBM Mainframes +ENV{MATCHADDR}=="02:e6:d3:*", GOTO="globally_administered_whitelist" +# Realtek +ENV{MATCHADDR}=="52:54:00:*", GOTO="globally_administered_whitelist" +# Novell 2000 +ENV{MATCHADDR}=="52:54:4c:*", GOTO="globally_administered_whitelist" +# Realtec +ENV{MATCHADDR}=="52:54:ab:*", GOTO="globally_administered_whitelist" +# Kingston Technologies +ENV{MATCHADDR}=="e2:0c:0f:*", GOTO="globally_administered_whitelist" + +# match interface dev_id +ATTR{dev_id}=="?*", ENV{MATCHDEVID}="$attr{dev_id}" + +# do not use "locally administered" MAC address +ENV{MATCHADDR}=="?[2367abef]:*", ENV{MATCHADDR}="" + +# do not use empty address +ENV{MATCHADDR}=="00:00:00:00:00:00", ENV{MATCHADDR}="" + +LABEL="globally_administered_whitelist" + +# build comment line for generated rule: +SUBSYSTEMS=="pci", ENV{COMMENT}="PCI device $attr{vendor}:$attr{device} ($driver)" +SUBSYSTEMS=="usb", ATTRS{idVendor}=="?*", ENV{COMMENT}="USB device 0x$attr{idVendor}:0x$attr{idProduct} ($driver)" +SUBSYSTEMS=="pcmcia", ENV{COMMENT}="PCMCIA device $attr{card_id}:$attr{manf_id} ($driver)" +SUBSYSTEMS=="ieee1394", ENV{COMMENT}="Firewire device $attr{host_id})" + +# ibmveth likes to use "locally administered" MAC addresses +DRIVERS=="ibmveth", ENV{MATCHADDR}="$attr{address}", ENV{COMMENT}="ibmveth ($id)" + +# S/390 uses id matches only, do not use MAC address match +SUBSYSTEMS=="ccwgroup", ENV{COMMENT}="S/390 $driver device at $id", ENV{MATCHID}="$id", ENV{MATCHDRV}="$driver", ENV{MATCHADDR}="" + +# see if we got enough data to create a rule +ENV{MATCHADDR}=="", ENV{MATCHID}=="", ENV{INTERFACE_NAME}=="", GOTO="persistent_net_generator_end" + +# default comment +ENV{COMMENT}=="", ENV{COMMENT}="net device ($attr{driver})" + +# write rule +DRIVERS=="?*", IMPORT{program}="write_net_rules" + +# rename interface if needed +ENV{INTERFACE_NEW}=="?*", NAME="$env{INTERFACE_NEW}" + +LABEL="persistent_net_generator_end" diff --git a/rule_generator/Makefile.am b/rule_generator/Makefile.am new file mode 100644 index 0000000000..8ed69f70eb --- /dev/null +++ b/rule_generator/Makefile.am @@ -0,0 +1,13 @@ +ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} + +# ------------------------------------------------------------------------------ +# rule_generator - persistent network rule generator +# ------------------------------------------------------------------------------ +dist_udevlibexec_SCRIPTS = \ + write_net_rules + +udevhomedir = $(udevlibexecdir) +dist_udevhome_DATA = rule_generator.functions + +dist_udevrules_DATA = \ + 75-persistent-net-generator.rules diff --git a/rule_generator/rule_generator.functions b/rule_generator/rule_generator.functions new file mode 100644 index 0000000000..ea02acc4c7 --- /dev/null +++ b/rule_generator/rule_generator.functions @@ -0,0 +1,120 @@ +# functions used by the udev rule generator + +# Copyright (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, either version 2 of the License, or +# (at your option) any later version. + +# 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, see . + +PATH='/sbin:/bin' +# + +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() { + RUNDIR="/run/udev/" + + RULES_LOCK="$RUNDIR/.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() { + RUNDIR="/run/udev/" + + local tmp_rules_file="$RUNDIR/tmp-rules--${RULES_FILE##*/}" + [ -e "$RULES_FILE" -o -e "$tmp_rules_file" ] || PRINT_HEADER=1 + + [ -d "${RULES_FILE%/*}" ] || if writeable ${RULES_FILE%/rules.d/*}; then + mkdir -p "${RULES_FILE%/*}" + fi + + 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" + + local search='.*[[:space:],]'"$key"'"('"$linkre"')".*' + echo $(sed -n -r -e 's/^#.*//' -e "${match}s/${search}/\1/p" \ + $RO_RULES_FILE \ + $([ -e $RULES_FILE ] && echo $RULES_FILE) \ + 2>/dev/null) +} diff --git a/rule_generator/write_net_rules.in b/rule_generator/write_net_rules.in new file mode 100644 index 0000000000..324e97821c --- /dev/null +++ b/rule_generator/write_net_rules.in @@ -0,0 +1,141 @@ +#!/bin/sh -e + +# This script is run to create persistent network device naming rules +# based on properties of the device. +# If the interface needs to be renamed, INTERFACE_NEW= will be printed +# on stdout to allow udev to IMPORT it. + +# variables used to communicate: +# MATCHADDR MAC address used for the match +# MATCHID bus_id used for the match +# MATCHDEVID dev_id used for the match +# MATCHDRV driver name used for the match +# MATCHIFTYPE interface type match +# COMMENT comment to add to the generated rule +# INTERFACE_NAME requested name supplied by external tool +# INTERFACE_NEW new interface name returned by rule writer + +# Copyright (C) 2006 Marco d'Itri +# Copyright (C) 2007 Kay Sievers +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# 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, see . + +# debug, if UDEV_LOG= +if [ -n "$UDEV_LOG" ]; then + if [ "$UDEV_LOG" -ge 7 ]; then + set -x + fi +fi + +RULES_FILE='@udevconfdir@/rules.d/70-persistent-net.rules' + +. @udevlibexecdir@/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, 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" + echo "# line, and change only the value of the NAME= key." + fi + + echo "" + [ "$comment" ] && echo "# $comment" + echo "SUBSYSTEM==\"net\", ACTION==\"add\"$match, NAME=\"$name\"" + } >> $RULES_FILE +} + +if [ -z "$INTERFACE" ]; then + echo "missing \$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 + +# the DRIVERS key is needed to not match bridges and VLAN sub-interfaces +if [ "$MATCHADDR" ]; then + match="$match, DRIVERS==\"?*\", ATTR{address}==\"$MATCHADDR\"" +fi + +if [ "$MATCHDRV" ]; then + match="$match, DRIVERS==\"$MATCHDRV\"" +fi + +if [ "$MATCHDEVID" ]; then + match="$match, ATTR{dev_id}==\"$MATCHDEVID\"" +fi + +if [ "$MATCHID" ]; then + match="$match, KERNELS==\"$MATCHID\"" +fi + +if [ "$MATCHIFTYPE" ]; then + match="$match, ATTR{type}==\"$MATCHIFTYPE\"" +fi + +if [ -z "$match" ]; then + echo "missing valid match" >&2 + unlock_rules_file + exit 1 +fi + +basename=${INTERFACE%%[0-9]*} +match="$match, KERNEL==\"$basename*\"" + +if [ "$INTERFACE_NAME" ]; then + # external tools may request a custom name + COMMENT="$COMMENT (custom name provided by external tool)" + if [ "$INTERFACE_NAME" != "$INTERFACE" ]; then + INTERFACE=$INTERFACE_NAME; + echo "INTERFACE_NEW=$INTERFACE" + fi +else + # if a rule using the current name already exists, find a new name + if interface_name_taken; then + INTERFACE="$basename$(find_next_available "$basename[0-9]*")" + # prevent INTERFACE from being "eth" instead of "eth0" + [ "$INTERFACE" = "${INTERFACE%%[ \[\]0-9]*}" ] && INTERFACE=${INTERFACE}0 + echo "INTERFACE_NEW=$INTERFACE" + fi +fi + +write_rule "$match" "$INTERFACE" "$COMMENT" + +unlock_rules_file + +exit 0 diff --git a/rules/Makefile.am b/rules/Makefile.am index 2c8624fff1..24c099ca72 100644 --- a/rules/Makefile.am +++ b/rules/Makefile.am @@ -15,8 +15,12 @@ dist_udevrules_DATA = \ 70-mouse.rules \ 75-net-description.rules \ 75-probe_mtd.rules \ - 78-sound-card.rules \ + 78-sound-card.rules + +if !ENABLE_RULE_GENERATOR +dist_udevrules_DATA += \ 80-net-name-slot.rules +endif if HAVE_BLKID dist_udevrules_DATA += \ diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index ed6f203ce7..897f98b321 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -771,17 +771,17 @@ out: return err; } -static int rename_netif(struct udev_event *event) { - struct udev_device *dev = event->dev; - char name[IFNAMSIZ]; - const char *oldname; +#ifdef ENABLE_RULE_GENERATOR +/* function to return the count of rules that assign NAME= to a value matching arg#2 , defined in udev-rules.c */ +int udev_rules_assigning_name_to(struct udev_rules *rules,const char *match_name); +#endif + +static int rename_netif_dev_fromname_toname(struct udev_device *dev,const char *oldname,const char *name) { int r; int sk; struct ifreq ifr; - oldname = udev_device_get_sysname(dev); - - strscpy(name, IFNAMSIZ, event->name); + log_debug("changing net interface name from '%s' to '%s'\n",oldname,name); sk = socket(PF_INET, SOCK_DGRAM, 0); if (sk < 0) @@ -791,13 +791,52 @@ static int rename_netif(struct udev_event *event) { strscpy(ifr.ifr_name, IFNAMSIZ, oldname); strscpy(ifr.ifr_newname, IFNAMSIZ, name); r = ioctl(sk, SIOCSIFNAME, &ifr); - if (r < 0) - return log_error_errno(-errno, "Error changing net interface name '%s' to '%s': %m", oldname, name); - log_debug("renamed network interface '%s' to '%s'", oldname, name); +#ifdef ENABLE_RULE_GENERATOR + int loop; + + if (r == 0) { + log_info("renamed network interface %s to %s\n", ifr.ifr_name, ifr.ifr_newname); + goto out; + } + /* keep trying if the destination interface name already exists */ + log_debug("collision on rename of network interface %s to %s , retrying until timeout\n", + ifr.ifr_name, ifr.ifr_newname); + + r = -errno; + if (r != -EEXIST) + goto out; + + /* wait a maximum of 90 seconds for our target to become available */ + loop = 90 * 20; + while (loop--) { + const struct timespec duration = { 0, 1000 * 1000 * 1000 / 20 }; + + nanosleep(&duration, NULL); + + r = ioctl(sk, SIOCSIFNAME, &ifr); + if (r == 0) { + log_info("renamed network interface %s to %s\n", ifr.ifr_name, ifr.ifr_newname); + break; + } + r = -errno; + if (r != -EEXIST) + break; + } + +out: +#endif + if (r < 0) + log_error_errno(-errno, "Error changing net interface name %s to %s: %m\n", ifr.ifr_name, ifr.ifr_newname); + else + log_debug("renamed network interface '%s' to '%s'", oldname, name); close(sk); - return 0; + return r; +} + +static int rename_netif(struct udev_event *event) { + return rename_netif_dev_fromname_toname(event->dev,udev_device_get_sysname(event->dev),event->name); } void udev_event_execute_rules(struct udev_event *event, @@ -843,6 +882,79 @@ void udev_event_execute_rules(struct udev_event *event, sigmask); /* rename a new network interface, if needed */ + + /* ENABLE_RULE_GENERATOR conditional: + * if this is a net iface, and it is an add event, + * and as long as all of the following are FALSE: + * - no NAME target and the current name is not being used + * - there is a NAME target and it is the same as the current name + * - the rules can successfully be searched for the current name (not really part of the conditional) + * the run the rename. + * + * note - udev_rules_assigning_name_to is run when event->name is NULL to ensure renames happen, + * but also on its own to check if a temp-rename is necessary when event->name exists. + * + * A temp-rename is necessary when: + * - there is no rule renaming the current iface but the current name IS used in some other rule + * - there is a rule renaming the current iface, + * the current name IS used AND the target name != the current name + */ + +#ifdef ENABLE_RULE_GENERATOR + int r; + if (udev_device_get_ifindex(dev) > 0 && streq(udev_device_get_action(dev), "add") && + (event->name == NULL && (r=udev_rules_assigning_name_to(rules,udev_device_get_sysname(dev))) > 0 || + event->name != NULL && !streq(event->name, udev_device_get_sysname(dev)))) { + char syspath[UTIL_PATH_SIZE]; + char *pos; + char *finalifname = event->name; + char newifname[IFNAMSIZ]; + + /* r is the number of rules that assign a device with NAME= this sysname */ + if (r > 0 || (r=udev_rules_assigning_name_to(rules,udev_device_get_sysname(dev))) > 0) { + /* have a conflict, rename to a temp name */ + char *newpos; + int ifidnum; + + /* build the temporary iface name */ + strscpy(newifname, IFNAMSIZ, udev_device_get_sysname(dev)); + newpos=pos=&newifname[strcspn(newifname,"0123456789")]; + ifidnum=(int)strtol(pos,&newpos,10); + *pos='\0'; + if (newpos > pos && *newpos == '\0') /* append new iface num to name */ + /* use udev_device_get_ifindex(dev) as it is unique to every iface */ + snprintf(pos,IFNAMSIZ+(newifname-pos), "%d", 128 - udev_device_get_ifindex(dev)); + + /* note, r > 0, which will skip the post-rename stuff if no rename occurs */ + + /* if sysname isn't already the tmpname (ie there is no numeric component), do the rename */ + if (!streq(newifname,udev_device_get_sysname(dev))) { + r = rename_netif_dev_fromname_toname(dev,udev_device_get_sysname(dev),newifname); + if (r == 0) { + finalifname = newifname; + log_debug("renamed netif to '%s' for collision avoidance\n", newifname); + } else { + log_error("could not rename netif to '%s' for collision avoidance\n",newifname); + } + } + /* rename it now to its final target if its not already there */ + if (event->name != NULL && !streq(event->name, newifname)) { + r = rename_netif_dev_fromname_toname(dev,newifname,event->name); + if (r == 0) + finalifname = event->name; + } + + } else { /* no need to rename to a tempname first, do a regular direct rename to event->name */ + + r = 1; /* skip the post-rename stuff if no rename occurs */ + if (!streq(event->name, udev_device_get_sysname(dev))) + r = rename_netif(event); + } + + if (r == 0) { + log_debug("renamed netif to '%s'\n", finalifname); + r = udev_device_rename(dev, finalifname); +#else if (udev_device_get_ifindex(dev) > 0 && streq(udev_device_get_action(dev), "add") && event->name != NULL && !streq(event->name, udev_device_get_sysname(dev))) { int r; @@ -853,6 +965,7 @@ void udev_event_execute_rules(struct udev_event *event, udev_device_get_sysname(dev), event->name); else { r = udev_device_rename(dev, event->name); +#endif if (r < 0) log_warning_errno(r, "renamed interface '%d' from '%s' to '%s', but could not update udev_device: %m", udev_device_get_ifindex(dev), udev_device_get_sysname(dev), event->name); diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index c5e85faaaf..e2bb99ca7d 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -2755,3 +2755,69 @@ finish: return r; } + +#ifdef ENABLE_RULE_GENERATOR +/* function to return the count of rules that assign NAME= to a value matching arg#2 - returns 0,1 */ +int udev_rules_assigning_name_to(struct udev_rules *rules, const char *match_name) +{ + struct token *cur; + struct token *rule; + enum escape_type esc = ESCAPE_UNSET; + bool name_final = false; + + if (rules->tokens == NULL) + return 0; + + /* loop through token list, match, run actions or forward to next rule */ + cur = &rules->tokens[0]; + rule = cur; + for (;;) { + dump_token(rules, cur); + switch (cur->type) { + case TK_RULE: + /* current rule */ + rule = cur; + if (!rule->rule.can_set_name) + goto nomatch; + break; + case TK_M_SUBSYSTEM: + if (match_key(rules, cur, "net") != 0) + goto nomatch; + break; + case TK_M_ACTION: + if (match_key(rules, cur, "add") != 0) + goto nomatch; + break; + case TK_A_NAME: { + const char *name = rules_str(rules, cur->key.value_off); + char name_str[UTIL_PATH_SIZE]; + int count; + + strscpy(name_str,UTIL_PATH_SIZE,name); + count = util_replace_chars(name_str, "/"); + if (count > 0) + log_debug("%i character(s) replaced\n", count); + if (streq(name_str,match_name)) { + log_debug("found a match! NAME assigns %s in: %s:%u\n", + name, + rules_str(rules, rule->rule.filename_off), + rule->rule.filename_line); + return 1; /* no need to find more than one */ + } + + /* skip to next rule */ + goto nomatch; + } + case TK_END: + return 0; + } + + cur++; + continue; + nomatch: + /* fast-forward to next rule */ + cur = rule + rule->rule.token_count; + } +} +#endif + -- cgit v1.2.3-54-g00ecf