--- /dev/null
+#!/bin/sh
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+#
+PATH=/usr/sbin:/usr/bin:/sbin:/bin
+
+# File to start dhclient requests on different interfaces in parallel
+
+. /lib/dracut-lib.sh
+. /lib/net-lib.sh
+
+netif=$1
+do_vlan=$2
+arg=$3
+
+# Run dhclient in parallel
+do_dhclient() {
+ local _COUNT=0
+ local _timeout=$(getargs rd.net.timeout.dhcp=)
+ local _DHCPRETRY=$(getargs rd.net.dhcp.retry=)
+ _DHCPRETRY=${_DHCPRETRY:-1}
+
+ while [ $_COUNT -lt $_DHCPRETRY ]; do
+ info "Starting dhcp for interface $netif"
+ dhclient $arg \
+ ${_timeout:+--timeout $_timeout} \
+ -q \
+ -1 \
+ -cf /etc/dhclient.conf \
+ -pf /tmp/dhclient.$netif.pid \
+ -lf /tmp/dhclient.$netif.lease \
+ $netif &
+ wait $! 2>/dev/null
+
+ # wait will return the return value of dhclient
+ retv=$?
+
+ # dhclient and hence wait returned success, 0.
+ if [ $retv -eq 0 ]; then
+ return 0
+ fi
+
+ # If dhclient exited before wait was called, or it was killed by
+ # another thread for interface whose DHCP succeeded, then it will not
+ # find the process with that pid and return error code 127. In that
+ # case we need to check if /tmp/dhclient.$netif.lease exists. If it
+ # does, it means dhclient finished executing before wait was called,
+ # and it was successful (return 0). If /tmp/dhclient.$netif.lease
+ # does not exist, then it means dhclient was killed by another thread
+ # or it finished execution but failed dhcp on that interface.
+
+ if [ $retv -eq 127 ]; then
+ pid=$(cat /tmp/dhclient.$netif.pid)
+ info "PID $pid was not found by wait for $netif"
+ if [ -e /tmp/dhclient.$netif.lease ]; then
+ info "PID $pid not found but DHCP successful on $netif"
+ return 0
+ fi
+ fi
+
+ _COUNT=$(($_COUNT+1))
+ [ $_COUNT -lt $_DHCPRETRY ] && sleep 1
+ done
+ warn "dhcp for interface $netif failed"
+ # nuke those files since we failed; we might retry dhcp again if it's e.g.
+ # `ip=dhcp,dhcp6` and we check for the PID file earlier
+ rm -f /tmp/dhclient.$netif.{pid,lease}
+ return 1
+}
+
+do_dhclient
+ret=$?
+
+# setup nameserver
+for s in "$dns1" "$dns2" $(getargs nameserver); do
+ [ -n "$s" ] || continue
+ echo nameserver $s >> /tmp/net.$netif.resolv.conf
+done
+
+if [ $ret -eq 0 ]; then
+ > /tmp/net.${netif}.up
+
+ if [ -z "$do_vlan" ] && [ -e /sys/class/net/${netif}/address ]; then
+ > /tmp/net.$(cat /sys/class/net/${netif}/address).up
+ fi
+
+ # Check if DHCP also suceeded on another interface before this one.
+ # We will always use the first one on which DHCP succeeded, by using
+ # a commom file $IFNETFILE, to synchronize between threads.
+ # Consider the race condition in which multiple threads
+ # corresponding to different interfaces may try to read $IFNETFILE
+ # and find it does not exist; they may all end up thinking they are the
+ # first to succeed (hence more than one thread may end up writing to
+ # $IFNETFILE). To take care of this, instead of checking if $IFNETFILE
+ # exists to determine if we are the first, we create a symbolic link
+ # in $IFNETFILE, pointing to the interface name ($netif), thus storing
+ # the interface name in the link pointer.
+ # Creating a link will fail, if the link already exists, hence kernel
+ # will take care of allowing only first thread to create link, which
+ # takes care of the race condition for us. Subsequent threads will fail.
+ # Also, the link points to the interface name, which will tell us which
+ # interface succeeded.
+
+ if ln -s $netif $IFNETFILE 2>/dev/null; then
+ intf=$(readlink $IFNETFILE)
+ if [ -e /tmp/dhclient.$intf.lease ]; then
+ info "DHCP successful on interface $intf"
+ # Kill all existing dhclient calls for other interfaces, since we
+ # already got one successful interface
+
+ npid=$(cat /tmp/dhclient.$netif.pid)
+ pidlist=$(pgrep dhclient)
+ for pid in $pidlist; do
+ [ "$pid" -eq "$npid" ] && continue
+ kill -9 $pid >/dev/null 2>&1
+ done
+ else
+ echo "ERROR! $IFNETFILE exists but /tmp/dhclient.$intf.lease does not exist!!!"
+ fi
+ else
+ info "DHCP success on $netif, and also on $intf"
+ exit 0
+ fi
+ exit $ret
+fi
exit 0
fi
+do_dhcp_parallel() {
+ # dhclient-script will mark the netif up and generate the online
+ # event for nfsroot
+ # XXX add -V vendor class and option parsing per kernel
+
+ [ -e /tmp/dhclient.$netif.pid ] && return 0
+
+ if ! iface_has_carrier $netif; then
+ warn "No carrier detected on interface $netif"
+ return 1
+ fi
+
+ bootintf=$(readlink $IFNETFILE)
+ if [ ! -z $bootintf ] && [ -e /tmp/dhclient.$bootintf.lease ]; then
+ info "DHCP already succeeded for $bootintf, exiting for $netif"
+ return 1;
+ fi
+
+ if [ ! -e /run/NetworkManager/conf.d/10-dracut-dhclient.conf ]; then
+ mkdir -p /run/NetworkManager/conf.d
+ echo '[main]' > /run/NetworkManager/conf.d/10-dracut-dhclient.conf
+ echo 'dhcp=dhclient' >>/run/NetworkManager/conf.d/10-dracut-dhclient.conf
+ fi
+
+ chmod +x /sbin/dhcp-multi.sh
+ /sbin/dhcp-multi.sh "$netif" "$DO_VLAN" "$@" &
+ return 0
+}
+
# Run dhclient
do_dhcp() {
# dhclient-script will mark the netif up and generate the online
case $autoopt in
dhcp|on|any)
do_dhcp -4 ;;
+ single-dhcp)
+ do_dhcp_parallel -4
+ exit 0 ;;
dhcp6)
load_ipv6
do_dhcp -6 ;;