diff options
Diffstat (limited to 'modules.d/35network-legacy/dhcp-multi.sh')
-rwxr-xr-x | modules.d/35network-legacy/dhcp-multi.sh | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/modules.d/35network-legacy/dhcp-multi.sh b/modules.d/35network-legacy/dhcp-multi.sh new file mode 100755 index 0000000..60e0374 --- /dev/null +++ b/modules.d/35network-legacy/dhcp-multi.sh @@ -0,0 +1,133 @@ +#!/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 + local _DHCPRETRY + _timeout=$(getarg rd.net.timeout.dhcp=) + _DHCPRETRY=$(getargnum 1 1 1000000000 rd.net.dhcp.retry=) + + if [ -n "$_timeout" ]; then + if ! (dhclient --help 2>&1 | grep -q -F -- '--timeout' 2> /dev/null); then + warn "rd.net.timeout.dhcp has no effect because dhclient does not implement the --timeout option" + unset _timeout + fi + fi + + 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 + read -r pid < /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 /tmp/dhclient."$netif".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 succeeded on another interface before this one. + # We will always use the first one on which DHCP succeeded, by using + # a common 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 + + read -r npid < /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 |