Please handle network device hotplug events (move from udev)

Martin Pitt mpitt at debian.org
Sun Dec 27 19:58:32 GMT 2015


Package: ifupdown
Severity: wishlist
Version: 0.8.4

Hello,

for historical reasons the udev package has shipped the functionality
for handling network device hotplug events. However, it's a bit weird
to ship the coldplugging parts (/etc/init.d/networking and
networking.service) in ifupdown, but not the hotplugging ones. It
would be more consistent to ship both in ifupdown and also make it
easier to do changes. Of course the systemd packagers are still happy
to discuss changes to the udev bits and provide guidance.

This involves three parts:

 * An udev rule which reacts to adding or removing network devices.
   This is currently shipped as /lib/udev/rules.d/80-networking.rules
   but I propose that ifupdown ships it as
   /lib/udev/rules.d/80-ifupdown.rules to avoid a package file
   conflict and also to make it clearer that this applies to ifupdown
   only. I attach this as 80-ifupdown.rules.

 * The above rule just calls an udev helper script "ifupdown" which
   needs to be put into /lib/udev/. This does the actual work of
   calling either ifup $IFACE directly (under SysV init or upstart) or
   a systemd unit "ifup at IFACE.service" under systemd. The latter is
   necessary as udev rules must not start long-running programs (ifup
   can take quite long), and it also provides much nicer and cleaner
   logging, a proper shutdown order, etc.

   I attach this as "ifupdown".

 * The ifup at .service helper unit. This should go into

     `pkg-config --variable=systemdsystemunitdir systemd`

   aka /lib/systemd/system/. It does not need to be enabled in any way
   as the above udev helper rule will start/stop it.

   Note that this will file-conflict with udev with this name, so
   if/once you accept this we'll need to remove the above bits from
   udev and add a Breaks:/Replaces: to ifupdown and a Breaks: to udev.

Please let us know if you have any questions about these!

Thanks for considering,

Martin

-- 
Martin Pitt                        | http://www.piware.de
Ubuntu Developer (www.ubuntu.com)  | Debian Developer  (www.debian.org)
-------------- next part --------------
#!/bin/sh -e
#
# run /sbin/{ifup,ifdown} with the --allow=hotplug option.
#

PATH='/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin'

if [ -x /usr/bin/logger ]; then
	LOGGER=/usr/bin/logger
elif [ -x /bin/logger ]; then
	LOGGER=/bin/logger
else
	unset LOGGER
fi

# for diagnostics
if [ -t 1 -a -z "$LOGGER" ] || [ ! -e '/dev/log' ]; then
	mesg() {
		echo "$@" >&2
	}
elif [ -t 1 ]; then
	mesg() {
		echo "$@"
		$LOGGER -t "${0##*/}[$$]" "$@"
	}
else
	mesg() {
		$LOGGER -t "${0##*/}[$$]" "$@"
	}
fi

if [ -z "$INTERFACE" ]; then
    mesg "Bad ifupdown udev helper invocation: \$INTERFACE is not set"
    exit 1
fi

check_program() {
    [ -x $1 ] && return 0

    mesg "ERROR: $1 not found. You need to install the ifupdown package."
    mesg "ifupdown udev helper $ACTION event for $INTERFACE not handled."
    exit 1
}

wait_for_interface() {
    local interface=$1
    local state

    while :; do
	read state /sys/class/net/$interface/operstate 2>/dev/null || true
	if [ "$state" != down ]; then
		return 0
	fi
	sleep 1
    done
}

net_ifup() {
    check_program /sbin/ifup

    # exit if the interface is not configured as allow-hotplug
    if ! ifquery --allow hotplug -l | grep -q "^${INTERFACE}\$"; then
        exit 0
    fi

    if [ -d /run/systemd/system ]; then
        exec systemctl --no-block start $(systemd-escape --template ifup at .service $INTERFACE)
    fi

    local out=$(ps -C ifup ho args)
    if [ "${out%$INTERFACE*}" != "$out" ]; then
        mesg "Already ifup-ing interface $INTERFACE"
        exit 0
    fi

    wait_for_interface lo

    exec ifup --allow=hotplug $INTERFACE
}

net_ifdown() {
    check_program /sbin/ifdown

    # systemd will automatically ifdown the interface on device
    # removal by binding the instanced service to the network device
    if [ -d /run/systemd/system ]; then
        exit 0
    fi

    local out=$(ps -C ifdown ho args)
    if [ "${out%$INTERFACE*}" != "$out" ]; then
	mesg "Already ifdown-ing interface $INTERFACE"
	exit 0
    fi

    exec ifdown --allow=hotplug $INTERFACE
}

do_everything() {

case "$ACTION" in
    add)
    # these interfaces generate hotplug events *after* they are brought up
    case $INTERFACE in
	ppp*|ippp*|isdn*|plip*|lo|irda*|ipsec*)
	exit 0 ;;
    esac

    net_ifup
    ;;

    remove)
    # the pppd persist option may have been used, so it should not be killed
    case $INTERFACE in
	ppp*)
	exit 0 ;;
    esac

    net_ifdown
    ;;

    *)
    mesg "NET $ACTION event not supported"
    exit 1
    ;;
esac

}

# under systemd we don't do synchronous operations, so we can run in the
# foreground; we also need to, as forked children get killed right away under
# systemd
if [ -d /run/systemd/system ]; then
    do_everything
else
    # under sysvinit/upstart we need to fork as we start the long-running
    # "ifup". but there, forked processes won't get killed.
    # When udev_log="debug" stdout and stderr are pipes connected to udevd.
    # They need to be closed or udevd will wait for this process which will
    # deadlock with udevsettle until the timeout.
    exec > /dev/null 2> /dev/null
    do_everything &
fi
-------------- next part --------------
[Unit]
Description=ifup for %I
After=local-fs.target network-pre.target apparmor.service
Before=network.target
BindsTo=sys-subsystem-net-devices-%i.device
DefaultDependencies=no
IgnoreOnIsolate=yes

[Service]
ExecStart=/bin/sh -ec 'ifup --allow=hotplug %I; ifquery --state %I'
ExecStop=/sbin/ifdown %I
RemainAfterExit=true
-------------- next part --------------
SUBSYSTEM=="net", ACTION=="add|remove", RUN+="ifupdown"


More information about the Pkg-systemd-maintainers mailing list