You are not logged in.

#1 2015-06-04 06:17:32

Fylwind
Member
Registered: 2015-06-04
Posts: 5

[script]Autorestart netctl-auto when Internet fails (feedback welcome)

Yesterday my laptop lost Internet connectivity while it was on WiFi and netctl-auto didn't restart the connection, so I couldn't SSH into it remotely :(  The next day I decided to write a script to make sure that wouldn't happen again.  After a few hours of debugging, I ended up with this little script.  It probably didn't need to be this complicated but I was very nitpicky about the requirements.  I hope may be of use to others as well :3

Any feedback is welcome! ^^

#!/bin/sh
#
# NAME
#        netctl-auto-monitor - Restart netctl-auto when connection fails
#
# SYNOPSIS
#        netctl-auto-monitor INTERFACE INTERVAL USER
#
# DESCRIPTION
#        Whenever the curl fails connect to all of the websites listed below
#        (in the script), the script will attempt to restart the corresponding
#        'netctl-auto@INTERFACE.service' via systemd.
#
#        The time between periodic checks is fixed at a minimum of INTERVAL
#        seconds.  In reality, it will probably be longer than that due to
#        latency.  There are also some additional delays between the
#        reconnects to allow the network interface to act, and also to prevent
#        frequent but futile reconnections when network is down.  These
#        timings can be adjusted via the 'init_pause' and 'max_pause'
#        parameters.
#
#        The script must be run as root as it operates on systemd.  To reduce
#        the danger of this, curl is executed under a separate USER, which
#        should have minimal privileges.  One may create such a user via:
#
#          useradd -r -s /bin/false USER
#
# OPTIONS
#        INTERFACE
#               Network interface to monitor.
#
#        INTERVAL
#               Time between periodic checks in seconds.
#
#        USER
#               An unprivileged user under which curl is run.
#
# EXAMPLE
#        The script itself may be managed via a systemd unit such as:
#
#          # /etc/systemd/system/netctl-auto-monitor@.service
#
#          [Unit]
#          Description=Restart netctl-auto when connection fails
#          Wants=netctl-auto@%i.service
#
#          [Service]
#          ExecStart=/usr/local/bin/netctl-auto-monitor %I 60 netctlautomonitor
#          Restart=always
#          RestartSec=600
#
#          [Install]
#          WantedBy=multi-user.target
#
set -e

if [ $# -ne 3 ]
then
    prog=`basename "$0"`
    printf >&2 "usage: %s INTERFACE INTERVAL USER\n" "$prog"
    exit 2
fi

interface=$1
interval=$2
user=$3

service=netctl-auto@$interface.service
timeout=30
init_pause=10
max_pause=1200
pause=$init_pause

# test connectivity using 'curl' as unprivileged $user for extra safety;
# return 37 on immediate failure and 0 on subsequent failures
test_connectivity() {
    cd "${TMP:-/tmp}"
    if sudo -n -u "$user" interface="$interface" interval="$interval" \
                          timeout="$timeout" sh <<"EOF"

    check_url() {
        curl -ISs --interface "$interface" -m "$timeout" "$1" >/dev/null
    }

    check() {
        check_url https://www.google.com    ||
        check_url https://www.microsoft.com ||
        check_url https://www.apple.com
    }

    exitcode=37
    while check
    do
        if [ "$exitcode" -eq 1 ]
        then echo "Connection active."
        fi
        exitcode=0
        sleep "$interval"
    done
    echo "Connection failed."
    exit "$exitcode"

EOF
    then return 0
    else return "$?"
    fi
}

# allow some time for netctl-auto to initialize
# since it may have been started in parallel
sleep "$pause"
while :
do

    # wait for failures; if it didn't fail immediately, reset the pause timer
    if test_connectivity
    then pause=$init_pause
    else
        exitcode=$?
        if [ "$exitcode" -ne 37 ]
        then exit "$exitcode"
        fi
    fi

    # restart and then pause for some time
    echo "Restarting ..."
    systemctl restart "$service" || :
    printf "Restarted %s.\n" "$service"
    sleep "$pause"

    # double the pause timer, up to some maximum
    pause=`expr "$pause" \* 2`
    if [ "$pause" -gt "$max_pause" ]
    then pause=$max_pause
    fi

done

Offline

#2 2015-06-06 07:11:52

Saint0fCloud
Member
Registered: 2009-03-31
Posts: 137

Re: [script]Autorestart netctl-auto when Internet fails (feedback welcome)

Actual feedback: Haven't used netctl in forever so forgive me if I am completely off base, but wouldn't netctl hooks be simpler to use?

Nitpick feedback: Any reason why you use `` instead of $(), [ instead of [[, and printf instead of echo? Also you're if/else statements are really hard to read, especially in the test_connectivity function.

Offline

#3 2015-06-06 07:22:53

jasonwryan
Anarchist
From: .nz
Registered: 2009-05-09
Posts: 30,424
Website

Re: [script]Autorestart netctl-auto when Internet fails (feedback welcome)

Saint0fCloud wrote:

Nitpick feedback: Any reason why you use `` instead of $(), [ instead of [[, and printf instead of echo? Also you're if/else statements are really hard to read, especially in the test_connectivity function.

Agree about using $(...) as it is POSIX, printf is a better option than echo and OP's shebang is /bin/sh and [[ is a bashism...


Arch + dwm   •   Mercurial repos  •   Surfraw

Registered Linux User #482438

Offline

#4 2015-06-06 07:47:03

Fylwind
Member
Registered: 2015-06-04
Posts: 5

Re: [script]Autorestart netctl-auto when Internet fails (feedback welcome)

Saint0fCloud wrote:

Actual feedback: Haven't used netctl in forever so forgive me if I am completely off base, but wouldn't netctl hooks be simpler to use?

I'm not sure how that would simplify things for this script.  The goal of the script is to verify Internet connectivity periodically and, if it drops or gets stuck, it will kick the netctl-auto unit so that hopefully it will fix itself.  Could you elaborate?

jasonwryan wrote:

Agree about using $(...) as it is POSIX, printf is a better option than echo and OP's shebang is /bin/sh and [[ is a bashism...

Because I'm a tad obsessed with portability.  I test them with the heirloom Bourne shell, which doesn't support $(…) because it predates POSIX sh.

Saint0fCloud wrote:

Nitpick feedback: Any reason why you use `` instead of $(), [ instead of [[, and printf instead of echo? Also you're if/else statements are really hard to read, especially in the test_connectivity function.

Yeah, the 'test_connectivity' one is annoying … I prefer having 'set -e' at the top but it messes up 'test_connectivity', so I had to add an 'if' to overcome that.  I could probably rewrite that part using '|| return $?' instead to make it less ugly.

Offline

#5 2015-06-06 10:43:58

tomk
Forum Fellow
From: Ireland
Registered: 2004-07-21
Posts: 9,839

Re: [script]Autorestart netctl-auto when Internet fails (feedback welcome)

I'm sure this is a useful thing, but did you look into why your connection dropped? There are all kinds of possible causes for "lost internet connectivity".

Offline

#6 2015-06-06 20:46:07

Fylwind
Member
Registered: 2015-06-04
Posts: 5

Re: [script]Autorestart netctl-auto when Internet fails (feedback welcome)

tomk wrote:

I'm sure this is a useful thing, but did you look into why your connection dropped? There are all kinds of possible causes for "lost internet connectivity".

No I haven't.  I'm not really familiar enough with netctl to diagnose why it would maintain a stale wireless connection, though if you have any potential leads I'd love to know!

Offline

Board footer

Powered by FluxBB