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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
|
#!/bin/bash
# Wake-on-LAN and shutdown script for f3s hosts (f0, f1, f2, f3)
# and optional shutdown for Raspberry Pi nodes (pi0–pi3)
#
# Before any shutdown of f3s Beelink hosts (f0/f1/f2/f3), this script will
# attempt to umount all NFS filesystems currently mounted on this machine
# (earth). NFS mounts here are tunnelled via stunnel (127.0.0.1:2323 →
# 192.168.1.138:2323 CARP VIP on f0/f1). If any NFS mount is active and
# cannot be umounted, the shutdown is aborted to prevent data loss or a
# hung filesystem.
#
# Usage:
# wol-f3s # Wake f0, f1, and f2
# wol-f3s f0 # Wake only f0
# wol-f3s f1 # Wake only f1
# wol-f3s f2 # Wake only f2
# wol-f3s f3 # Wake only f3
# wol-f3s shutdown # Shutdown f0, f1, and f2
# wol-f3s shutdown-f3 # Shutdown only f3
# wol-f3s shutdown-pis # Shutdown all four Raspberry Pis
# wol-f3s shutdown-all # Shutdown f0, f1, f2, and Pis
# MAC addresses
F0_MAC="e8:ff:1e:d7:1c:ac" # f0 (192.168.1.130)
F1_MAC="e8:ff:1e:d7:1e:44" # f1 (192.168.1.131)
F2_MAC="e8:ff:1e:d7:1c:a0" # f2 (192.168.1.132)
F3_MAC="e8:ff:1e:d7:f3:d7" # f3 (192.168.1.133)
# IP addresses
F0_IP="192.168.1.130"
F1_IP="192.168.1.131"
F2_IP="192.168.1.132"
F3_IP="192.168.1.133"
# Raspberry Pi IP addresses (no WoL support, shutdown only)
PI0_IP="192.168.1.125"
PI1_IP="192.168.1.126"
PI2_IP="192.168.1.127"
PI3_IP="192.168.1.128"
# SSH user
SSH_USER="paul"
# Broadcast address for your LAN
BROADCAST="192.168.1.255"
wake() {
local name=$1
local mac=$2
if [[ "$mac" == "XX:XX:XX:XX:XX:XX" ]]; then
echo "⚠️ $name MAC address not configured yet"
return 1
fi
echo "Sending WoL packet to $name ($mac)..."
wol -i "$BROADCAST" "$mac"
}
# umount_nfs_mounts tries to umount all currently mounted NFS filesystems on
# this local machine (earth). It reads /proc/mounts to find active nfs/nfs4
# mounts, attempts to umount each one, and returns 1 if any mount could not
# be umounted. This prevents shutting down f3s servers while NFS is still in
# use, which would leave the filesystem in a hung/stale state.
umount_nfs_mounts() {
# Collect all active NFS mount points from /proc/mounts (covers nfs and nfs4)
local nfs_mounts=()
while IFS= read -r mountpoint; do
nfs_mounts+=("$mountpoint")
done < <(awk '$3 ~ /^nfs/ { print $2 }' /proc/mounts)
if [[ ${#nfs_mounts[@]} -eq 0 ]]; then
echo " No NFS filesystems currently mounted — nothing to umount."
return 0
fi
local failed=0
for mp in "${nfs_mounts[@]}"; do
echo " Umounting NFS filesystem: $mp ..."
if umount "$mp" 2>/dev/null; then
echo " ✓ Umounted $mp"
else
# If it's no longer listed in /proc/mounts, it's already gone.
if ! grep -q "^[^ ]* $mp nfs" /proc/mounts; then
echo " ✓ $mp was already unmounted"
else
echo " ✗ Failed to umount $mp (in use or already unmounted?)"
failed=1
fi
fi
done
if [[ $failed -ne 0 ]]; then
echo ""
echo "✗ One or more NFS filesystems could not be umounted."
echo " Aborting shutdown to prevent data loss or a hung filesystem."
echo " Please check running processes that may have open files on NFS,"
echo " then retry."
return 1
fi
return 0
}
shutdown_host() {
local name=$1
local ip=$2
echo "Shutting down $name ($ip)..."
ssh -o ConnectTimeout=5 "$SSH_USER@$ip" "doas poweroff" 2>/dev/null && \
echo " ✓ Shutdown command sent to $name" || \
echo " ✗ Failed to reach $name (already down?)"
}
ACTION="${1:-all}"
case "$ACTION" in
f0)
wake "f0" "$F0_MAC"
;;
f1)
wake "f1" "$F1_MAC"
;;
f2)
wake "f2" "$F2_MAC"
;;
f3)
wake "f3" "$F3_MAC"
;;
all|"")
wake "f0" "$F0_MAC"
wake "f1" "$F1_MAC"
wake "f2" "$F2_MAC"
;;
shutdown|poweroff|down)
# Umount local NFS filesystems first; abort if any are stuck.
# The NFS mounts on earth tunnel through stunnel to the f3s CARP VIP,
# so they will hang or corrupt if the servers go down while mounted.
echo "Checking for locally mounted NFS filesystems..."
umount_nfs_mounts || exit 1
# Mute Gogios monitoring alerts for a day on both OpenBSD gateways
ssh rex@blowfish.buetow.org touch /tmp/f3s_taken_down
ssh rex@fishfinger.buetow.org touch /tmp/f3s_taken_down
shutdown_host "f0" "$F0_IP"
shutdown_host "f1" "$F1_IP"
shutdown_host "f2" "$F2_IP"
echo ""
echo "✓ Shutdown commands sent to all Beelinks."
exit 0
;;
shutdown-f3|poweroff-f3|down-f3)
# Umount local NFS filesystems first; abort if any are stuck.
# f3 does not host the NFS CARP VIP (that is f0/f1), but umounting
# first is still the safe default to avoid stale state.
echo "Checking for locally mounted NFS filesystems..."
umount_nfs_mounts || exit 1
shutdown_host "f3" "$F3_IP"
echo ""
echo "✓ Shutdown command sent to f3."
exit 0
;;
shutdown-pis)
# Raspberry Pis do not serve NFS, so no umount check is needed here.
shutdown_host "pi0" "$PI0_IP"
shutdown_host "pi1" "$PI1_IP"
shutdown_host "pi2" "$PI2_IP"
shutdown_host "pi3" "$PI3_IP"
echo ""
echo "✓ Shutdown commands sent to all Pis."
exit 0
;;
shutdown-all)
# Umount local NFS filesystems first; abort if any are stuck.
# The NFS mounts on earth tunnel through stunnel to the f3s CARP VIP,
# so they will hang or corrupt if the servers go down while mounted.
echo "Checking for locally mounted NFS filesystems..."
umount_nfs_mounts || exit 1
# Mute Gogios monitoring alerts for a day on both OpenBSD gateways
ssh rex@blowfish.buetow.org touch /tmp/f3s_taken_down
ssh rex@fishfinger.buetow.org touch /tmp/f3s_taken_down
shutdown_host "f0" "$F0_IP"
shutdown_host "f1" "$F1_IP"
shutdown_host "f2" "$F2_IP"
shutdown_host "pi0" "$PI0_IP"
shutdown_host "pi1" "$PI1_IP"
shutdown_host "pi2" "$PI2_IP"
shutdown_host "pi3" "$PI3_IP"
echo ""
echo "✓ Shutdown commands sent to all machines."
exit 0
;;
*)
echo "Usage: $0 [f0|f1|f2|f3|all|shutdown|shutdown-f3|shutdown-pis|shutdown-all]"
exit 1
;;
esac
echo ""
echo "✓ WoL packets sent. Machines should boot in a few seconds."
echo " Wait ~30 seconds, then try: ssh paul@192.168.1.130"
|