cmd=${1:-help} ct_name=${2:-} require_name() { if [ -z "$ct_name" ]; then echo "usage: ct $cmd " >&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 [container]\n\n' printf ' %-9s %-13s %s\n' \ "list" "" "list all containers with state (running/stopped/FAILED)" \ "start" "" "start a container" \ "stop" "" "stop a container" \ "restart" "" "restart a container" \ "status" "" "show container status" \ "logs" "" "view logs (extra args forwarded to journalctl)" \ "shell" "" "open a shell" \ "attach" "" "attach to Minecraft console (detach: Ctrl+\\)" ;; esac