--- /dev/null
+# Copyright 1999-2015 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+EAPI="5"
+
+KV_min=2.6.39
+WANT_AUTOMAKE=1.13
+
+inherit autotools eutils linux-info multilib multilib-minimal user
+
+if [[ ${PV} = 9999* ]]; then
+ EGIT_REPO_URI="git://github.com/gentoo/eudev.git"
+ inherit git-2
+else
+ SRC_URI="https://dev.gentoo.org/~blueness/${PN}/${P}.tar.gz"
+ KEYWORDS="~alpha ~amd64 ~arm ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sparc ~x86"
+fi
+
+DESCRIPTION="Linux dynamic and persistent device naming support (aka userspace devfs)"
+HOMEPAGE="https://github.com/gentoo/eudev"
+
+LICENSE="LGPL-2.1 MIT GPL-2"
+SLOT="0"
+IUSE="doc gudev +hwdb +kmod introspection rule-generator selinux static-libs test"
+
+COMMON_DEPEND=">=sys-apps/util-linux-2.20
+ gudev? ( >=dev-libs/glib-2.34.3:2[${MULTILIB_USEDEP}] )
+ introspection? ( >=dev-libs/gobject-introspection-1.38 )
+ kmod? ( >=sys-apps/kmod-16 )
+ selinux? ( >=sys-libs/libselinux-2.1.9 )
+ !<sys-libs/glibc-2.11
+ !sys-apps/gentoo-systemd-integration
+ !sys-apps/systemd
+ abi_x86_32? (
+ !<=app-emulation/emul-linux-x86-baselibs-20130224-r7
+ !app-emulation/emul-linux-x86-baselibs[-abi_x86_32(-)]
+ )"
+DEPEND="${COMMON_DEPEND}
+ dev-util/gperf
+ virtual/os-headers
+ virtual/pkgconfig
+ >=sys-devel/make-3.82-r4
+ >=sys-kernel/linux-headers-${KV_min}
+ doc? ( >=dev-util/gtk-doc-1.18
+ app-text/docbook-xml-dtd:4.2
+ app-text/docbook-xml-dtd:4.5
+ app-text/docbook-xsl-stylesheets
+ dev-libs/libxslt
+ )
+ >=dev-util/intltool-0.50
+ test? ( app-text/tree dev-lang/perl )"
+
+RDEPEND="${COMMON_DEPEND}
+ !<sys-fs/lvm2-2.02.103
+ !<sec-policy/selinux-base-2.20120725-r10
+ !sys-fs/udev
+ !sys-apps/systemd
+ gudev? ( !dev-libs/libgudev )"
+
+PDEPEND=">=sys-fs/udev-init-scripts-26
+ hwdb? ( >=sys-apps/hwids-20140304[udev] )"
+
+# The multilib-build.eclass doesn't handle situation where the installed headers
+# are different in ABIs. In this case, we install libgudev headers in native
+# ABI but not for non-native ABI.
+multilib_check_headers() { :; }
+
+pkg_pretend() {
+ ewarn
+ ewarn "As of 2013-01-29, ${P} provides the new interface renaming functionality,"
+ ewarn "as described in the URL below:"
+ ewarn "http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames"
+ ewarn
+ ewarn "This functionality is enabled BY DEFAULT because eudev has no means of synchronizing"
+ ewarn "between the default or user-modified choice of sys-fs/udev. If you wish to disable"
+ ewarn "this new iface naming, please be sure that /etc/udev/rules.d/80-net-name-slot.rules"
+ ewarn "exists: touch /etc/udev/rules.d/80-net-name-slot.rules"
+ ewarn
+}
+
+pkg_setup() {
+ CONFIG_CHECK="~BLK_DEV_BSG ~DEVTMPFS ~!IDE ~INOTIFY_USER ~!SYSFS_DEPRECATED ~!SYSFS_DEPRECATED_V2 ~SIGNALFD ~EPOLL ~FHANDLE ~NET"
+ linux-info_pkg_setup
+ get_running_version
+
+ # These are required kernel options, but we don't error out on them
+ # because you can build under one kernel and run under another.
+ if kernel_is lt ${KV_min//./ }; then
+ ewarn
+ ewarn "Your current running kernel version ${KV_FULL} is too old to run ${P}."
+ ewarn "Make sure to run udev under kernel version ${KV_min} or above."
+ ewarn
+ fi
+}
+
+src_prepare() {
+ epatch "${FILESDIR}"/${P}-pre-rule-generator.patch
+ epatch "${FILESDIR}"/${PN}-3-rule-generator.patch
+
+ # change rules back to group uucp instead of dialout for now
+ sed -e 's/GROUP="dialout"/GROUP="uucp"/' -i rules/*.rules \
+ || die "failed to change group dialout to uucp"
+
+ epatch_user
+
+ if use doc; then
+ gtkdocize --docdir docs || die "gtkdocize failed"
+ else
+ echo 'EXTRA_DIST =' > docs/gtk-doc.make
+ fi
+ eautoreconf
+}
+
+multilib_src_configure() {
+ tc-export CC #463846
+ export cc_cv_CFLAGS__flto=no #502950
+
+ # Keep sorted by ./configure --help and only pass --disable flags
+ # when *required* to avoid external deps or unnecessary compile
+ local econf_args
+ econf_args=(
+ ac_cv_search_cap_init=
+ ac_cv_header_sys_capability_h=yes
+ DBUS_CFLAGS=' '
+ DBUS_LIBS=' '
+ --with-rootprefix=
+ --with-rootrundir=/run
+ --docdir=/usr/share/doc/${PF}
+ --libdir=/usr/$(get_libdir)
+ --with-rootlibexecdir=/lib/udev
+ --with-html-dir="/usr/share/doc/${PF}/html"
+ --enable-split-usr
+ --enable-manpages
+ --disable-hwdb
+ --exec-prefix=/
+
+ $(use_enable gudev)
+ )
+
+ # Only build libudev for non-native_abi, and only install it to libdir,
+ # that means all options only apply to native_abi
+ if multilib_is_native_abi; then
+ econf_args+=(
+ --with-rootlibdir=/$(get_libdir)
+ $(use_enable doc gtk-doc)
+ $(use_enable introspection)
+ $(use_enable kmod)
+ $(use_enable static-libs static)
+ $(use_enable selinux)
+ $(use_enable rule-generator)
+ )
+ else
+ econf_args+=(
+ --disable-static
+ --disable-gtk-doc
+ --disable-introspection
+ --disable-kmod
+ --disable-selinux
+ --disable-rule-generator
+ )
+ fi
+ ECONF_SOURCE="${S}" econf "${econf_args[@]}"
+}
+
+multilib_src_compile() {
+ if multilib_is_native_abi; then
+ emake
+ else
+ emake -C src/shared
+ emake -C src/libudev
+ use gudev && emake -C src/gudev
+ fi
+}
+
+multilib_src_install() {
+ if multilib_is_native_abi; then
+ emake DESTDIR="${D}" install
+ else
+ emake -C src/libudev DESTDIR="${D}" install
+ use gudev && emake -C src/gudev DESTDIR="${D}" install
+ fi
+}
+
+multilib_src_test() {
+ # make sandbox get out of the way
+ # these are safe because there is a fake root filesystem put in place,
+ # but sandbox seems to evaluate the paths of the test i/o instead of the
+ # paths of the actual i/o that results.
+ # also only test for native abi
+ if multilib_is_native_abi; then
+ addread /sys
+ addwrite /dev
+ addwrite /run
+ default_src_test
+ fi
+}
+
+multilib_src_install_all() {
+ prune_libtool_files --all
+ rm -rf "${ED}"/usr/share/doc/${PF}/LICENSE.*
+
+ insinto /lib/udev/rules.d
+ doins "${FILESDIR}"/40-gentoo.rules
+
+ use rule-generator && doinitd "${FILESDIR}"/udev-postmount
+
+ if ! [[ ${PV} = 9999* ]]; then
+ insinto /usr/share/doc/${PF}/html/gudev
+ doins "${S}"/docs/gudev/html/*
+
+ insinto /usr/share/doc/${PF}/html/libudev
+ doins "${S}"/docs/libudev/html/*
+ fi
+}
+
+pkg_preinst() {
+ local htmldir
+ for htmldir in gudev libudev; do
+ if [[ -d ${EROOT}usr/share/gtk-doc/html/${htmldir} ]]; then
+ rm -rf "${EROOT}"usr/share/gtk-doc/html/${htmldir}
+ fi
+ if [[ -d ${ED}/usr/share/doc/${PF}/html/${htmldir} ]]; then
+ dosym ../../doc/${PF}/html/${htmldir} \
+ /usr/share/gtk-doc/html/${htmldir}
+ fi
+ done
+}
+
+pkg_postinst() {
+ mkdir -p "${EROOT}"run
+
+ # "losetup -f" is confused if there is an empty /dev/loop/, Bug #338766
+ # So try to remove it here (will only work if empty).
+ rmdir "${EROOT}"dev/loop 2>/dev/null
+ if [[ -d ${EROOT}dev/loop ]]; then
+ ewarn "Please make sure your remove /dev/loop,"
+ ewarn "else losetup may be confused when looking for unused devices."
+ fi
+
+ if use hwdb && has_version 'sys-apps/hwids[udev]'; then
+ udevadm hwdb --update --root="${ROOT%/}"
+
+ # http://cgit.freedesktop.org/systemd/systemd/commit/?id=1fab57c209035f7e66198343074e9cee06718bda
+ # reload database after it has be rebuilt, but only if we are not upgrading
+ # also pass if we are -9999 since who knows what hwdb related changes there might be
+ if [[ ${REPLACING_VERSIONS%-r*} == ${PV} || -z ${REPLACING_VERSIONS} ]] && \
+ [[ ${ROOT%/} == "" ]] && [[ ${PV} != "9999" ]]; then
+ udevadm control --reload
+ fi
+ fi
+
+ ewarn
+ ewarn "You need to restart eudev as soon as possible to make the"
+ ewarn "upgrade go into effect:"
+ ewarn "\t/etc/init.d/udev --nodeps restart"
+
+ if use rule-generator && \
+ [[ -x $(type -P rc-update) ]] && rc-update show | grep udev-postmount | grep -qsv 'boot\|default\|sysinit'; then
+ ewarn
+ ewarn "Please add the udev-postmount init script to your default runlevel"
+ ewarn "to ensure the legacy rule-generator functionality works as reliably"
+ ewarn "as possible."
+ ewarn "\trc-update add udev-postmount default"
+ fi
+
+ elog
+ elog "For more information on eudev on Gentoo, writing udev rules, and"
+ elog "fixing known issues visit:"
+ elog " https://www.gentoo.org/doc/en/udev-guide.xml"
+ elog
+
+ # http://cgit.freedesktop.org/systemd/systemd/commit/rules/50-udev-default.rules?id=3dff3e00e044e2d53c76fa842b9a4759d4a50e69
+ # https://bugs.gentoo.org/246847
+ # https://bugs.gentoo.org/514174
+ enewgroup input
+
+ # Update hwdb database in case the format is changed by udev version.
+ if has_version 'sys-apps/hwids[udev]'; then
+ udevadm hwdb --update --root="${ROOT%/}"
+ # Only reload when we are not upgrading to avoid potential race w/ incompatible hwdb.bin and the running udevd
+ if [[ -z ${REPLACING_VERSIONS} ]]; then
+ # http://cgit.freedesktop.org/systemd/systemd/commit/?id=1fab57c209035f7e66198343074e9cee06718bda
+ if [[ ${ROOT} != "" ]] && [[ ${ROOT} != "/" ]]; then
+ return 0
+ fi
+ udevadm control --reload
+ fi
+ fi
+}
--- /dev/null
+commit c3e2fc597a6ba92ab9fcfd4cd7db10bc6fb5b34b
+Author: Ian Stakenvicius <axs@gentoo.org>
+Date: Tue Sep 22 13:53:41 2015 -0400
+
+ Forward-ported network rule-generator code from eudev-1.10
+
+diff --git a/Makefile.am b/Makefile.am
+index 2932690..0c6429d 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 ccb1012..56d37fa 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -269,10 +269,25 @@ AC_ARG_ENABLE([hwdb], AS_HELP_STRING([--enable-hwdb],[install hwdb.d files]),[],
+ 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 0000000..ce59e5d
+--- /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 0000000..8ed69f7
+--- /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 0000000..ea02acc
+--- /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 <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, 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 <http://www.gnu.org/licenses/>.
++
++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 0000000..324e978
+--- /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=<name> 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 <md@Linux.IT>
++# Copyright (C) 2007 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, 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 <http://www.gnu.org/licenses/>.
++
++# debug, if UDEV_LOG=<debug>
++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 2c8624f..24c099c 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 ed6f203..897f98b 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 c5e85fa..e2bb99c 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
++