Files
server-config/scripts/ct.sh
Floweynt 76fa38e34c
flake-check / flake-check (push) Canceled after 0s
lint / lint (push) Canceled after 0s
diff-tag / dix (push) Canceled after 0s
initial commit
2026-08-31 08:41:45 -05:00

137 lines
4.3 KiB
Bash

cmd=${1:-help}
ct_name=${2:-}
require_name() {
if [ -z "$ct_name" ]; then
echo "usage: ct $cmd <container>" >&2
exit 1
fi
}
case "$cmd" in
list)
sep() { printf '%0.s-' {1..60}; printf '\n'; }
uptime_str() {
awk '{s=int($1);d=int(s/86400);h=int((s%86400)/3600);m=int((s%3600)/60);
if(d>0)printf "%d days %d hours %d minutes",d,h,m;
else if(h>0)printf "%d hours %d minutes",h,m;
else printf "%d minutes",m}' /proc/uptime
}
printf 'HOST: %s UP: %s\n' "$(hostname)" "$(uptime_str)"
sep
printf 'CONTAINERS\n'
mapfile -t all_units < <({
systemctl list-unit-files --no-legend --no-pager --plain 2>/dev/null
systemctl list-units --all --no-legend --no-pager --plain 2>/dev/null
} | awk '$1 ~ /^container@.+\.service$/ {print $1}' | sort -u)
if [ ${#all_units[@]} -gt 0 ]; then
systemctl show "${all_units[@]}" \
--property=Id,UnitFileState,ActiveState --no-pager 2>/dev/null \
| awk '
/^Id=container@/ { n=$0; sub(/^Id=container@/,"",n); sub(/\.service$/,"",n) }
/^UnitFileState=/ { f=$0; sub(/^UnitFileState=/,"",f) }
/^ActiveState=/ { a=$0; sub(/^ActiveState=/,"",a) }
/^[[:space:]]*$/ { if (n!="") { _print(); n=f=a="" } }
END { if (n!="") _print() }
function _print() {
start = (f=="enabled") ? "auto" : "manual"
if (a=="active") st="running"
else if (a=="failed") st="FAILED"
else if (a=="inactive") st="stopped"
else st=(a!="") ? a : "not-loaded"
mid = "-"
if (a == "active") {
midcmd = "machinectl show " n " -p Id --value 2>/dev/null"
got = ""
midcmd | getline got
close(midcmd)
if (got != "") mid = substr(got,1,12) "..."
}
printf " %-35s %-8s %-10s %s\n", n, start, st, mid
}
'
else
printf ' none\n'
fi
sep
printf 'FAILED UNITS (host)\n'
failed=$(systemctl list-units --state=failed --no-legend --no-pager --plain 2>/dev/null \
| awk 'NF>0{print $1}')
if [ -z "$failed" ]; then printf ' none\n'
else printf '%s\n' "$failed" | sed 's/^/ /'; fi
sep
printf 'FAILED UNITS (containers)\n'
any_failed=0
machines=$(machinectl list --no-legend --no-pager 2>/dev/null | awk 'NF>0{print $1}')
for m in $machines; do
ct_failed=$(sudo systemctl -M "$m" list-units --state=failed \
--no-legend --no-pager --plain 2>/dev/null \
| awk 'NF>0 && $1!~/^container@/{print $1}' || true)
if [ -n "$ct_failed" ]; then
printf ' [%s]\n' "$m"
printf '%s\n' "$ct_failed" | sed 's/^/ /'
any_failed=1
fi
done
[ "$any_failed" -eq 0 ] && printf ' none\n'
sep
df -h /persist | awk 'NR==2{printf "DISK %s used / %s total (%s)\n",$3,$2,$5}'
free -h | awk '/^Mem:/{printf "MEM %s used / %s total\n",$3,$2}'
;;
start)
require_name
sudo systemctl start "container@$ct_name"
;;
stop)
require_name
sudo systemctl stop "container@$ct_name"
;;
restart)
require_name
sudo systemctl restart "container@$ct_name"
;;
status)
require_name
machinectl status "$ct_name"
;;
logs)
require_name
shift 2
sudo journalctl -M "$ct_name" --no-hostname "$@"
;;
shell)
require_name
exec sudo machinectl shell "$ct_name"
;;
attach)
require_name
leader=$(machinectl show "$ct_name" -p Leader --value 2>/dev/null)
if [ -z "$leader" ] || [ "$leader" = "0" ]; then
printf 'error: container %s is not running\n' "$ct_name" >&2
exit 1
fi
exec sudo nsenter -m -t "$leader" -- dtach -a -r none /run/ct-attach/attach.sock
;;
*)
printf 'usage: ct <cmd> [container]\n\n'
printf ' %-9s %-13s %s\n' \
"list" "" "list all containers with state (running/stopped/FAILED)" \
"start" "<container>" "start a container" \
"stop" "<container>" "stop a container" \
"restart" "<container>" "restart a container" \
"status" "<container>" "show container status" \
"logs" "<container>" "view logs (extra args forwarded to journalctl)" \
"shell" "<container>" "open a shell" \
"attach" "<container>" "attach to Minecraft console (detach: Ctrl+\\)"
;;
esac