summaryrefslogtreecommitdiffstats
path: root/modules.d/35network-legacy/dhcp-multi.sh
blob: 60e03741c6d5f753758b58a3e3095c14ed250817 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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