aboutsummaryrefslogtreecommitdiffstats
path: root/bin/pubip-sample.sh
blob: c5100f8769a922e934f36a38adaed922513d2e3a (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
#!/bin/bash
# Fetch the public IP address into a cache file, for widgets/network.lua.
#
# Run from conky's ${execi} every 30 minutes. The draw hook must never make a
# network call: a hung request would block the Cairo draw and freeze the whole
# dashboard, and a residential address is stable for days anyway.
#
# Exits non-zero with a message on stderr when it cannot fetch, leaving any
# existing cache untouched rather than replacing a good address with an error
# page.

set -u

CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/udt"
CACHE="$CACHE_DIR/pubip.txt"

umask 077
mkdir -p "$CACHE_DIR"

# The temp file sits in the same directory as the target: rename is only
# atomic within a filesystem, and the widget reads this on its own 2s cadence.
TMP="$CACHE.tmp.$$"
trap 'rm -f "$TMP"' EXIT

# --max-time, not just --connect-timeout: a server that accepts the connection
# and then stalls would otherwise hold a curl process for as long as it likes.
if ! IP=$(curl -fsS --max-time 10 https://ipinfo.io/ip 2>/dev/null); then
    echo "pubip-sample: fetch failed" >&2
    exit 1
fi

# Validate before writing. A captive portal, a rate-limit message and an error
# page all arrive with a 200 and would otherwise be written to the cache and
# drawn on the dashboard as though they were an address.
IP=$(echo "$IP" | tr -d '[:space:]')
if ! echo "$IP" | grep -qE '^([0-9]{1,3}\.){3}[0-9]{1,3}$'; then
    echo "pubip-sample: response is not an IPv4 address" >&2
    exit 1
fi

# The epoch travels with the address so the widget can refuse to show a stale
# one.
{
    echo "ip $IP"
    echo "fetched $(date +%s)"
} > "$TMP"

mv -f "$TMP" "$CACHE"