Add optional dtop - Docker container monitoring TUI

This commit is contained in:
buildplan 2025-11-06 21:20:56 +00:00
parent 103abb8d1d
commit ff6bcc97ec

View File

@ -1,8 +1,10 @@
#!/bin/bash #!/bin/bash
# Debian and Ubuntu Server Hardening Interactive Script # Debian and Ubuntu Server Hardening Interactive Script
# Version: 0.73 | 2025-10-22 # Version: 0.74 | 2025-11-06
# Changelog: # Changelog:
# - v0.74: Add optional dtop (https://github.com/amir20/dtop) after docker installation.
#. Update .bashrc
# - v0.73: Revised/improved logic in .bashrc for memory and system updates. # - v0.73: Revised/improved logic in .bashrc for memory and system updates.
# - v0.72: Added configure_custom_bashrc() function that creates and installs a feature-rich .bashrc file during user creation. # - v0.72: Added configure_custom_bashrc() function that creates and installs a feature-rich .bashrc file during user creation.
# - v0.71: Simplify test backup function to work reliably with Hetzner storagebox # - v0.71: Simplify test backup function to work reliably with Hetzner storagebox
@ -77,7 +79,7 @@
set -euo pipefail set -euo pipefail
# --- Update Configuration --- # --- Update Configuration ---
CURRENT_VERSION="0.73" CURRENT_VERSION="0.74"
SCRIPT_URL="https://raw.githubusercontent.com/buildplan/du_setup/refs/heads/main/du_setup.sh" SCRIPT_URL="https://raw.githubusercontent.com/buildplan/du_setup/refs/heads/main/du_setup.sh"
CHECKSUM_URL="${SCRIPT_URL}.sha256" CHECKSUM_URL="${SCRIPT_URL}.sha256"
@ -228,7 +230,7 @@ print_header() {
printf '%s\n' "${CYAN}╔═════════════════════════════════════════════════════════════════╗${NC}" printf '%s\n' "${CYAN}╔═════════════════════════════════════════════════════════════════╗${NC}"
printf '%s\n' "${CYAN}║ ║${NC}" printf '%s\n' "${CYAN}║ ║${NC}"
printf '%s\n' "${CYAN}║ DEBIAN/UBUNTU SERVER SETUP AND HARDENING SCRIPT ║${NC}" printf '%s\n' "${CYAN}║ DEBIAN/UBUNTU SERVER SETUP AND HARDENING SCRIPT ║${NC}"
printf '%s\n' "${CYAN}║ v0.73 | 2025-10-22${NC}" printf '%s\n' "${CYAN}║ v0.74 | 2025-11-06${NC}"
printf '%s\n' "${CYAN}║ ║${NC}" printf '%s\n' "${CYAN}║ ║${NC}"
printf '%s\n' "${CYAN}╚═════════════════════════════════════════════════════════════════╝${NC}" printf '%s\n' "${CYAN}╚═════════════════════════════════════════════════════════════════╝${NC}"
printf '\n' printf '\n'
@ -1238,16 +1240,15 @@ __bash_prompt_command() {
PROMPT_COMMAND=__bash_prompt_command PROMPT_COMMAND=__bash_prompt_command
# --- Editor Configuration --- # --- Editor Configuration ---
# Set default editor with fallback chain.
if command -v vim &>/dev/null; then if command -v vim &>/dev/null; then
export EDITOR=vim export EDITOR=vim
export VISUAL=vim export VISUAL=vim
elif command -v vi &>/dev/null; then elif command -v nano &>/dev/null; then
export EDITOR=vi
export VISUAL=vi
else
export EDITOR=nano export EDITOR=nano
export VISUAL=nano export VISUAL=nano
else
export EDITOR=vi
export VISUAL=vi
fi fi
# --- Additional Environment Variables --- # --- Additional Environment Variables ---
@ -1265,7 +1266,7 @@ mkcd() {
# Create a backup of a file with timestamp. # Create a backup of a file with timestamp.
backup() { backup() {
if [ -f "$1" ]; then if [ -f "$1" ]; then
local backup_file="$1.backup-$(date +%Y%m%d-%H%M%S)" local backup_file; backup_file="$1.backup-$(date +%Y%m%d-%H%M%S)"
cp "$1" "$backup_file" cp "$1" "$backup_file"
echo "Backup created: $backup_file" echo "Backup created: $backup_file"
else else
@ -1300,7 +1301,7 @@ extract() {
;; ;;
*) *)
echo "'$1' cannot be extracted via extract()" >&2 echo "'$1' cannot be extracted via extract()" >&2
return 1 return 1 # Add return 1 for consistency
;; ;;
esac esac
else else
@ -1334,6 +1335,9 @@ ftext() {
grep -rnw . -e "$1" 2>/dev/null grep -rnw . -e "$1" 2>/dev/null
} }
# Search history easily
hgrep() { history | grep -i --color=auto "$@"; }
# Create a tarball of a directory. # Create a tarball of a directory.
targz() { targz() {
if [ -d "$1" ]; then if [ -d "$1" ]; then
@ -1357,7 +1361,36 @@ sizeof() {
# Show most used commands from history. # Show most used commands from history.
histop() { histop() {
history | awk '{CMD[$2]++;count++;}END { for (a in CMD)print CMD[a] " " CMD[a]/count*100 "% " a;}' | grep -v "./" | column -c3 -s " " -t | sort -nr | nl | head -n20 history | awk -v ig="$HISTIGNORE" 'BEGIN{OFS="\t";gsub(/:/,"|",ig);ir="^("ig")($| )";sr="(^|\\s)\\./"}
{cmd=$4;for(i=5;i<=NF;i++)cmd=cmd" "$i}
(cmd==""||cmd~ir||cmd~sr){next}
{C[cmd]++;t++}
END{if(t>0)for(a in C)printf"%d\t%.2f%%\t%s\n",C[a],(C[a]/t*100),a}' |
sort -nr | head -n20 |
awk 'BEGIN{
FS="\t";
maxc=length("COUNT");
maxp=length("PERCENT");
}
{
data[NR]=$0;
len1=length($1);
len2=length($2);
if(len1>maxc)maxc=len1;
if(len2>maxp)maxp=len2;
}
END{
fmt=" %-4s %-*s %-*s %s\n";
printf fmt,"RANK",maxc,"COUNT",maxp,"PERCENT","COMMAND";
sep_c=sep_p="";
for(i=1;i<=maxc;i++)sep_c=sep_c"-";
for(i=1;i<=maxp;i++)sep_p=sep_p"-";
printf fmt,"----",maxc,sep_c,maxp,sep_p,"-------";
for(i=1;i<=NR;i++){
split(data[i],f,"\t");
printf fmt,i".",maxc,f[1],maxp,f[2],f[3]
}
}'
} }
# Quick server info display # Quick server info display
@ -1392,18 +1425,45 @@ sysinfo() {
cpu_info=$(lscpu | awk -F: '/Model name/ {print $2; exit}' | xargs || grep -m1 'model name' /proc/cpuinfo | cut -d ':' -f2 | xargs) cpu_info=$(lscpu | awk -F: '/Model name/ {print $2; exit}' | xargs || grep -m1 'model name' /proc/cpuinfo | cut -d ':' -f2 | xargs)
[ -z "$cpu_info" ] && cpu_info="Unknown" [ -z "$cpu_info" ] && cpu_info="Unknown"
# --- IP Detection (preferred interfaces first) --- # --- IP Detection ---
local ip_addr local ip_addr public_ipv4 public_ipv6
for iface in eth0 wlan0 ens33 eno1 enp0s3 enp3s0; do
# Try to get public IPv4 first
public_ipv4=$(curl -4 -s -m 2 --connect-timeout 1 https://checkip.amazonaws.com 2>/dev/null || \
curl -4 -s -m 2 --connect-timeout 1 https://ipconfig.io 2>/dev/null || \
curl -4 -s -m 2 --connect-timeout 1 https://api.ipify.org 2>/dev/null)
# If no IPv4, try IPv6
if [ -z "$public_ipv4" ]; then
public_ipv6=$(curl -6 -s -m 2 --connect-timeout 1 https://ipconfig.io 2>/dev/null || \
curl -6 -s -m 2 --connect-timeout 1 https://icanhazip.co 2>/dev/null || \
curl -6 -s -m 2 --connect-timeout 1 https://api64.ipify.org 2>/dev/null)
fi
# Get local/internal IP as fallback
for iface in eth0 ens3 enp0s3 enp0s6 wlan0 ens33 eno1; do
ip_addr=$(ip -4 addr show "$iface" 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1) ip_addr=$(ip -4 addr show "$iface" 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1)
[ -n "$ip_addr" ] && break [ -n "$ip_addr" ] && break
done done
[ -z "$ip_addr" ] && ip_addr=$(ip -4 addr show scope global | awk '/inet/ {print $2}' | cut -d/ -f1 | head -n1) [ -z "$ip_addr" ] && ip_addr=$(ip -4 addr show scope global 2>/dev/null | awk '/inet/ {print $2}' | cut -d/ -f1 | head -n1)
# --- System Info --- # --- System Info ---
if [ -n "$ip_addr" ]; then if [ -n "$public_ipv4" ]; then
# Show public IPv4 (preferred)
printf "${CYAN}%-15s${RESET} %s ${YELLOW}[%s]${RESET}" "Hostname:" "$(hostname)" "$public_ipv4"
# Show local IP if different from public
if [ -n "$ip_addr" ] && [ "$ip_addr" != "$public_ipv4" ]; then
printf " ${DIM}(local: %s)${RESET}\n" "$ip_addr"
else
printf "\n"
fi
elif [ -n "$public_ipv6" ]; then
# Show public IPv6 if no IPv4
printf "${CYAN}%-15s${RESET} %s ${YELLOW}[%s]${RESET}" "Hostname:" "$(hostname)" "$public_ipv6"
[ -n "$ip_addr" ] && printf " ${DIM}(local: %s)${RESET}\n" "$ip_addr" || printf "\n"
elif [ -n "$ip_addr" ]; then
# Show local IP only
printf "${CYAN}%-15s${RESET} %s ${YELLOW}[%s]${RESET}\n" "Hostname:" "$(hostname)" "$ip_addr" printf "${CYAN}%-15s${RESET} %s ${YELLOW}[%s]${RESET}\n" "Hostname:" "$(hostname)" "$ip_addr"
else else
# No IP detected
printf "${CYAN}%-15s${RESET} %s\n" "Hostname:" "$(hostname)" printf "${CYAN}%-15s${RESET} %s\n" "Hostname:" "$(hostname)"
fi fi
printf "${CYAN}%-15s${RESET} %s\n" "OS:" "$(grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d'"' -f2 || echo 'Unknown')" printf "${CYAN}%-15s${RESET} %s\n" "OS:" "$(grep PRETTY_NAME /etc/os-release 2>/dev/null | cut -d'"' -f2 || echo 'Unknown')"
@ -1439,19 +1499,23 @@ sysinfo() {
security="${apt_check_output##*;}" security="${apt_check_output##*;}"
fi fi
fi fi
# Fallback if apt-check didn't provide values # Fallback if apt-check didn't provide values
if [ -z "$total" ] && [ -r /var/lib/update-notifier/updates-available ]; then if [ -z "$total" ] && [ -r /var/lib/update-notifier/updates-available ]; then
total=$(awk '/[0-9]+ (update|package)s? can be (updated|applied|installed)/ {print $1; exit}' /var/lib/update-notifier/updates-available 2>/dev/null) total=$(awk '/[0-9]+ (update|package)s? can be (updated|applied|installed)/ {print $1; exit}' /var/lib/update-notifier/updates-available 2>/dev/null)
security=$(awk '/[0-9]+ (update|package)s? .*security/ {print $1; exit}' /var/lib/update-notifier/updates-available 2>/dev/null) security=$(awk '/[0-9]+ (update|package)s? .*security/ {print $1; exit}' /var/lib/update-notifier/updates-available 2>/dev/null)
fi fi
# Final fallback # Final fallback
if [ -z "$total" ]; then if [ -z "$total" ]; then
total=$(apt list --upgradable 2>/dev/null | grep -c upgradable) total=$(apt list --upgradable 2>/dev/null | grep -c upgradable)
security=$(apt list --upgradable 2>/dev/null | grep -ci security) security=$(apt list --upgradable 2>/dev/null | grep -ci security)
fi fi
total="${total:-0}" total="${total:-0}"
security="${security:-0}" security="${security:-0}"
# Display updates if available
if [ -n "$total" ] && [ "$total" -gt 0 ] 2>/dev/null; then if [ -n "$total" ] && [ "$total" -gt 0 ] 2>/dev/null; then
printf "${CYAN}%-15s${RESET} " "Updates:" printf "${CYAN}%-15s${RESET} " "Updates:"
if [ -n "$security" ] && [ "$security" -gt 0 ] 2>/dev/null; then if [ -n "$security" ] && [ "$security" -gt 0 ] 2>/dev/null; then
@ -1485,6 +1549,28 @@ sysinfo() {
fi fi
fi fi
# --- Tailscale Info (if installed and connected) ---
if command -v tailscale &>/dev/null; then
local ts_ipv4 ts_ipv6 ts_hostname
# Get Tailscale IPs
ts_ipv4=$(tailscale ip -4 2>/dev/null)
ts_ipv6=$(tailscale ip -6 2>/dev/null)
# Only show if connected
if [ -n "$ts_ipv4" ] || [ -n "$ts_ipv6" ]; then
# Get hostname from status (FIXED: use head -n1 to get only first line)
ts_hostname=$(tailscale status --self --peers=false 2>/dev/null | head -n1 | awk '{print $2}')
printf "${CYAN}%-15s${RESET} " "Tailscale:"
printf "${GREEN}Connected${RESET}"
[ -n "$ts_ipv4" ] && printf " - %s" "$ts_ipv4"
[ -n "$ts_hostname" ] && printf " ${DIM}(%s)${RESET}" "$ts_hostname"
printf "\n"
# Optional: Show IPv6 on second line if available
if [ -n "$ts_ipv6" ]; then
printf " ${DIM}IPv6: %s${RESET}\n" "$ts_ipv6"
fi
fi
fi
printf "\n" printf "\n"
} }
@ -1501,6 +1587,47 @@ checkupdates() {
fi fi
} }
# Disk space alert (warns if any partition > 80%)
diskcheck() {
df -h | awk '
NR > 1 {
usage = $5
gsub(/%/, "", usage)
if (usage > 80) {
printf "⚠️ %s\n", $0
found = 1
}
}
END {
if (!found) print "✓ All disks below 80%"
}
'
}
# Directory bookmarks
export MARKPATH=$HOME/.marks
[ -d "$MARKPATH" ] || mkdir -p "$MARKPATH"
mark() { ln -sfn "$(pwd)" "$MARKPATH/${1:-$(basename "$PWD")}"; }
jump() { cd -P "$MARKPATH/$1" 2>/dev/null || ls -l "$MARKPATH"; }
# Service status shortcut (cleaner output)
svc() { sudo systemctl status "$1" --no-pager -l | head -20; }
alias failed='systemctl --failed --no-pager'
# Show top 10 processes by CPU
topcpu() { ps aux --sort=-%cpu | head -11; }
# Show top 10 processes by memory
topmem() { ps aux --sort=-%mem | head -11; }
# Network connections summary
netsum() {
echo "=== Active Connections ==="
ss -s
echo -e "\n=== Listening Ports ==="
sudo ss -tulnp | grep LISTEN | awk '{print $5, $7}' | sort -u
}
# --- Aliases --- # --- Aliases ---
# Enable color support for common commands. # Enable color support for common commands.
if [ -x /usr/bin/dircolors ]; then if [ -x /usr/bin/dircolors ]; then
@ -1523,6 +1650,9 @@ alias lt='ls -alFht' # Sort by modification time, newest first
alias ltr='ls -alFhtr' # Sort by modification time, oldest first alias ltr='ls -alFhtr' # Sort by modification time, oldest first
alias lS='ls -alFhS' # Sort by size, largest first alias lS='ls -alFhS' # Sort by size, largest first
# Last command with sudo
alias please='sudo $(history -p !!)'
# Safety aliases to prompt before overwriting. # Safety aliases to prompt before overwriting.
alias rm='rm -i' alias rm='rm -i'
alias cp='cp -i' alias cp='cp -i'
@ -1582,6 +1712,7 @@ alias myip='curl -s ifconfig.me || curl -s icanhazip.com' # Alternatives: api.ip
localip() { localip() {
ip -4 addr | awk '/inet/ {print $2}' | cut -d/ -f1 | grep -v '127.0.0.1' ip -4 addr | awk '/inet/ {print $2}' | cut -d/ -f1 | grep -v '127.0.0.1'
} }
alias netstat='ss' alias netstat='ss'
alias ping='ping -c 5' alias ping='ping -c 5'
alias fastping='ping -c 100 -i 0.2' alias fastping='ping -c 100 -i 0.2'
@ -1635,12 +1766,14 @@ if command -v docker &>/dev/null; then
# Docker stats # Docker stats
alias dstats='docker stats --no-stream' alias dstats='docker stats --no-stream'
alias dstatsa='docker stats' alias dstatsa='docker stats'
dtop() { dst() {
docker stats --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}' docker stats --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}'
} }
# Safe stop all (shows command instead of executing) # Safe stop all (shows command instead of executing)
alias dstopall='echo "To stop all containers, run: docker stop \$(docker ps -q)"' alias dstopa='echo "To stop all containers, run: docker stop \$(docker ps -q)"'
# Start all stopped containers
alias dstarta='docker start $(docker ps -aq)'
# Docker Compose v2 aliases (check if the compose plugin exists) # Docker Compose v2 aliases (check if the compose plugin exists)
if docker compose version &>/dev/null; then if docker compose version &>/dev/null; then
@ -1880,6 +2013,8 @@ Categories: navigation, files, system, docker, git, network
mkcd <dir> Create directory and cd into it mkcd <dir> Create directory and cd into it
up <n> Go up N directories (e.g., up 3) up <n> Go up N directories (e.g., up 3)
path Display PATH variable (one per line) path Display PATH variable (one per line)
mark <name> Bookmark current directory
jump <name> Jump to a bookmarked directory
═══════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════
📄 FILE OPERATIONS 📄 FILE OPERATIONS
@ -1911,18 +2046,22 @@ Categories: navigation, files, system, docker, git, network
═══════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════
sysinfo Display comprehensive system information sysinfo Display comprehensive system information
checkupdates Check for available system updates checkupdates Check for available system updates
diskcheck Check for disk partitions over 80%
psgrep <pat> Search for process by name psgrep <pat> Search for process by name
psmem Show top 10 processes by memory usage topcpu Show top 10 processes by CPU
pscpu Show top 10 processes by CPU usage topmem Show top 10 processes by Memory
top10 Show top 10 memory-consuming processes pscpu Show top 10 processes by CPU (tree view)
psmem Show top 10 processes by Memory (tree view)
ports Show all listening ports (TCP/UDP) ports Show all listening ports (TCP/UDP)
listening Show listening ports with process info listening Show listening ports with process info
meminfo Display detailed memory information meminfo Display detailed memory information
h Show command history h Show command history
hgrep <pat> Search command history
histop Show most used commands histop Show most used commands
c, cls Clear the screen
reload Reload bashrc configuration reload Reload bashrc configuration
═══════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════
@ -1947,7 +2086,7 @@ Docker Commands:
dstats Container stats snapshot dstats Container stats snapshot
dstatsa Container stats live dstatsa Container stats live
dtop Container stats formatted table dst Container stats formatted table
dprune Prune system (remove unused data) dprune Prune system (remove unused data)
dprunea Prune all (including images) dprunea Prune all (including images)
@ -1996,6 +2135,8 @@ Docker Compose:
═══════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════
myip Show external IP address myip Show external IP address
localip Show local IP address(es) localip Show local IP address(es)
netsum Network connections summary
kssh SSH wrapper for kitty terminal
ping Ping with 5 packets (default) ping Ping with 5 packets (default)
fastping Fast ping (100 packets, 0.2s interval) fastping Fast ping (100 packets, 0.2s interval)
netstat Show network connections (ss) netstat Show network connections (ss)
@ -2004,6 +2145,8 @@ Docker Compose:
⚙️ SYSTEM ADMINISTRATION ⚙️ SYSTEM ADMINISTRATION
═══════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════
Systemd: Systemd:
svc <srv> Show service status (brief)
failed List failed systemd services
sysstart <srv> Start service sysstart <srv> Start service
sysstop <srv> Stop service sysstop <srv> Stop service
sysrestart <srv> Restart service sysrestart <srv> Restart service
@ -2021,6 +2164,9 @@ APT (Debian/Ubuntu):
aptclean Remove unused packages aptclean Remove unused packages
aptlist List installed packages aptlist List installed packages
Sudo:
please Run last command with sudo
═══════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════
🕒 DATE & TIME 🕒 DATE & TIME
═══════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════
@ -2032,6 +2178,8 @@ APT (Debian/Ubuntu):
HELP & INFORMATION HELP & INFORMATION
═══════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════
bashhelp Show this help (all categories) bashhelp Show this help (all categories)
bh Alias for bashhelp
commands List all custom functions and aliases
bashhelp navigation Show navigation commands only bashhelp navigation Show navigation commands only
bashhelp files Show file operation commands bashhelp files Show file operation commands
bashhelp system Show system monitoring commands bashhelp system Show system monitoring commands
@ -2042,7 +2190,7 @@ APT (Debian/Ubuntu):
═══════════════════════════════════════════════════════════════════ ═══════════════════════════════════════════════════════════════════
💡 TIP: Most commands support --help or -h for more information 💡 TIP: Most commands support --help or -h for more information
The prompt shows: ✗ for failed commands, git branch when in repo The prompt shows: ✗ for failed commands, (git branch) when in repo
HELPTEXT HELPTEXT
;; ;;
@ -2062,11 +2210,14 @@ HELPTEXT
mkcd <dir> Create directory and cd into it mkcd <dir> Create directory and cd into it
up <n> Go up N directories up <n> Go up N directories
path Display PATH variable path Display PATH variable
mark <name> Bookmark current directory
jump <name> Jump to a bookmarked directory
Examples: Examples:
mkcd ~/projects/newapp # Create and enter directory mkcd ~/projects/newapp # Create and enter directory
up 3 # Go up 3 levels up 3 # Go up 3 levels
cd - # Return to previous directory mark proj1 # Bookmark current dir as 'proj1'
jump proj1 # Jump back to 'proj1'
HELPTEXT HELPTEXT
;; ;;
@ -2111,12 +2262,14 @@ HELPTEXT
Overview: Overview:
sysinfo Comprehensive system info sysinfo Comprehensive system info
checkupdates Check for package updates checkupdates Check for package updates
diskcheck Check for disks > 80%
Processes: Processes:
psgrep <pat> Search processes psgrep <pat> Search processes
psmem Top 10 by memory topcpu Top 10 by CPU
pscpu Top 10 by CPU topmem Top 10 by Memory
top10 Top memory consumers pscpu Top 10 by CPU (tree view)
psmem Top 10 by Memory (tree view)
Network: Network:
ports Listening ports ports Listening ports
@ -2126,9 +2279,11 @@ Memory:
meminfo Detailed memory info meminfo Detailed memory info
free Free memory (human-readable) free Free memory (human-readable)
History: Shell:
h Show history h Show history
hgrep <pat> Search history
histop Most used commands histop Most used commands
c, cls Clear screen
reload Reload bashrc reload Reload bashrc
Examples: Examples:
@ -2155,7 +2310,7 @@ Management:
dfollow <id> Follow logs dfollow <id> Follow logs
Stats & Cleanup: Stats & Cleanup:
dstats, dstatsa, dtop dstats, dstatsa, dst
dprune, dprunea, dvprune, diprune dprune, dprunea, dvprune, diprune
drmall Remove stopped containers drmall Remove stopped containers
@ -2165,7 +2320,7 @@ Docker Compose:
dcstatus Status & resource usage dcstatus Status & resource usage
dcreload <srv> Restart & follow logs dcreload <srv> Restart & follow logs
dcupdate <srv> Pull & update service dcupdate <srv> Pull & update service
dcgrep <srv> <p> Filter logs dcgrep <s> <p> Filter logs
dcvalidate Validate compose file dcvalidate Validate compose file
Examples: Examples:
@ -2207,11 +2362,13 @@ HELPTEXT
myip Show external IP myip Show external IP
localip Show local IP(s) localip Show local IP(s)
netsum Network connection summary
kssh SSH wrapper for kitty
ports Show listening ports ports Show listening ports
listening Ports with process info listening Ports with process info
ping Ping (5 packets) ping Ping (5 packets)
fastping Fast ping (100 packets) fastping Fast ping (100 packets)
netstat Network connections netstat Network connections (ss)
Examples: Examples:
myip # Get public IP myip # Get public IP
@ -2231,6 +2388,7 @@ HELPTEXT
} }
# Preserve Bash's builtin `help` while integrating bashhelp # Preserve Bash's builtin `help` while integrating bashhelp
# This wrapper routes custom help to bashhelp, bash builtins to builtin help
help() { help() {
case "${1:-}" in case "${1:-}" in
""|all|navigation|files|system|docker|git|network) ""|all|navigation|files|system|docker|git|network)
@ -3680,6 +3838,68 @@ EOF
fi fi
print_warning "NOTE: '$USERNAME' must log out and back in to use Docker without sudo." print_warning "NOTE: '$USERNAME' must log out and back in to use Docker without sudo."
log "Docker installation completed." log "Docker installation completed."
# Offer dtop installation
install_dtop_optional
}
install_dtop_optional() {
if sudo sh -c 'command -v dtop' >/dev/null 2>&1 || command -v dtop >/dev/null 2>&1; then
print_info "dtop is already installed."
return 0
fi
if ! confirm "Install 'dtop' (Docker container monitoring TUI)?"; then
print_info "Skipping dtop installation."
return 0
fi
print_info "Installing dtop for user '$USERNAME'..."
local DTOP_INSTALLER="/tmp/dtop-installer.sh"
if ! curl -fsSL "https://github.com/amir20/dtop/releases/latest/download/dtop-installer.sh" -o "$DTOP_INSTALLER"; then
print_warning "Failed to download dtop installer. Continuing setup..."
log "Failed to download dtop installer."
return 0
fi
chmod +x "$DTOP_INSTALLER"
trap 'rm -f "$DTOP_INSTALLER"' RETURN
local USER_HOME
USER_HOME=$(getent passwd "$USERNAME" | cut -d: -f6)
local USER_LOCAL_BIN="$USER_HOME/.local/bin"
if [[ ! -d "$USER_LOCAL_BIN" ]]; then
print_info "Creating $USER_LOCAL_BIN..."
if ! sudo -u "$USERNAME" mkdir -p "$USER_LOCAL_BIN"; then
print_warning "Failed to create $USER_LOCAL_BIN. Skipping dtop."
return 0
fi
fi
if sudo -u "$USERNAME" bash "$DTOP_INSTALLER" < /dev/null >> "$LOG_FILE" 2>&1; then
# Verify installation
if [[ -f "$USER_LOCAL_BIN/dtop" ]]; then
sudo -u "$USERNAME" chmod +x "$USER_LOCAL_BIN/dtop"
local BASHRC="$USER_HOME/.bashrc"
if [[ -f "$BASHRC" ]] && ! grep -q "\.local/bin" "$BASHRC"; then
print_info "Adding ~/.local/bin to PATH in $BASHRC..."
{
echo ''
echo '# Add local bin to PATH'
# shellcheck disable=SC2016
echo 'if [ -d "$HOME/.local/bin" ]; then PATH="$HOME/.local/bin:$PATH"; fi'
} >> "$BASHRC"
chown "$USERNAME:$USERNAME" "$BASHRC"
if grep -q "\.local/bin" "$BASHRC"; then
print_info "PATH configuration updated successfully."
else
print_warning "Failed to update PATH, but dtop is still installed."
fi
fi
print_success "dtop installed successfully to $USER_LOCAL_BIN."
log "dtop installed to $USER_LOCAL_BIN for user $USERNAME"
else
print_warning "dtop installer finished, but binary not found at $USER_LOCAL_BIN/dtop"
log "dtop binary missing after user installation attempt."
fi
else
print_warning "dtop installation script failed. Continuing setup..."
log "dtop installation script failed."
fi
} }
install_tailscale() { install_tailscale() {