🛠️ chore: update scripts and documentation for v1.1.3 release

This commit is contained in:
rE-Bo0t.bx1
2025-12-05 20:24:55 +08:00
parent ca517d1768
commit 49c9f69918
7 changed files with 9 additions and 125 deletions

View File

@@ -1,65 +1,40 @@
#!/bin/sh
# docker-entrypoint.sh - Tor Guard Relay initialization and process management
# 🆕 v1.1.2 - Ultra-optimized 16.8 MB build with multi-mode support
set -e
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# ENV Variable Compatibility Layer
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Supports BOTH naming conventions for maximum compatibility:
#
# 1. Our naming (TOR_* prefix):
# - TOR_NICKNAME, TOR_CONTACT_INFO, TOR_ORPORT, TOR_OBFS4_PORT, etc.
#
# 2. Official Tor Project bridge naming (for compatibility):
# - NICKNAME, EMAIL, OR_PORT, PT_PORT, OBFS4V_* (additional torrc options)
#
# Priority: Official names override TOR_* defaults (for drop-in compatibility)
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Map official bridge ENV vars to our internal TOR_* prefix
# Official naming takes precedence over TOR_* defaults from Dockerfile
[ -n "${NICKNAME:-}" ] && TOR_NICKNAME="$NICKNAME"
[ -n "${EMAIL:-}" ] && TOR_CONTACT_INFO="$EMAIL"
[ -n "${OR_PORT:-}" ] && TOR_ORPORT="$OR_PORT"
[ -n "${PT_PORT:-}" ] && TOR_OBFS4_PORT="$PT_PORT"
# Auto-detect bridge mode if PT_PORT is set (official bridge convention)
if [ -n "${PT_PORT:-}" ] && [ "${TOR_RELAY_MODE:-guard}" = "guard" ]; then
TOR_RELAY_MODE="bridge"
fi
# Configuration
readonly TOR_CONFIG="${TOR_CONFIG:-/etc/tor/torrc}"
readonly TOR_DATA_DIR="${TOR_DATA_DIR:-/var/lib/tor}"
readonly TOR_LOG_DIR="${TOR_LOG_DIR:-/var/log/tor}"
readonly TOR_RELAY_MODE="${TOR_RELAY_MODE:-guard}"
# Global PID tracking for cleanup
TOR_PID=""
TAIL_PID=""
# Emoji logging helpers (v1.1.0 style)
log() { printf "%s\n" "$1"; }
info() { printf " %s\n" "$1"; }
success() { printf "✅ %s\n" "$1"; }
warn() { printf "🛑 %s\n" "$1"; }
die() { printf "🛑 ERROR: %s\n" "$1"; exit 1; }
# Signal handler for graceful shutdown
trap 'cleanup_and_exit' SIGTERM SIGINT
cleanup_and_exit() {
log ""
warn "Shutdown signal received. Stopping Tor relay..."
# Stop log tail process first
if [ -n "$TAIL_PID" ] && kill -0 "$TAIL_PID" 2>/dev/null; then
kill -TERM "$TAIL_PID" 2>/dev/null || true
fi
# Stop Tor process
if [ -n "$TOR_PID" ] && kill -0 "$TOR_PID" 2>/dev/null; then
log " Sending SIGTERM to Tor (PID: $TOR_PID)..."
kill -TERM "$TOR_PID" 2>/dev/null || true
@@ -73,15 +48,13 @@ cleanup_and_exit() {
exit 0
}
# Startup banner
startup_banner() {
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
log "🧅 Tor Guard Relay v1.1.2 - Initialization"
log "🧅 Tor Guard Relay v1.1.3 - Initialization"
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
log ""
}
# Phase 1: Directory setup
phase_1_directories() {
log "🗂️ Phase 1: Directory Structure"
mkdir -p "$TOR_DATA_DIR" "$TOR_LOG_DIR" /run/tor /tmp
@@ -91,7 +64,6 @@ phase_1_directories() {
log " • Logs: $TOR_LOG_DIR"
log " • Run: /run/tor"
# Show disk space
if command -v df >/dev/null 2>&1; then
available=$(df -h "$TOR_DATA_DIR" 2>/dev/null | tail -n 1 | awk '{print $4}' || echo "unknown")
log " 💽 Available disk space: $available"
@@ -99,12 +71,9 @@ phase_1_directories() {
log ""
}
# Phase 2: Permissions
phase_2_permissions() {
log "🔐 Phase 2: Permission Hardening"
# Note: Ownership is set at build time by Dockerfile (chown requires root)
# Only set directory permissions (chmod doesn't require ownership)
chmod 700 "$TOR_DATA_DIR" 2>/dev/null || warn "Failed to set data directory permissions (may be read-only mount)"
chmod 755 "$TOR_LOG_DIR" 2>/dev/null || warn "Failed to set log directory permissions (may be read-only mount)"
@@ -112,13 +81,11 @@ phase_2_permissions() {
log ""
}
# Validate relay configuration parameters
validate_relay_config() {
# Validate relay mode
if [ -n "${TOR_RELAY_MODE:-}" ]; then
case "$TOR_RELAY_MODE" in
guard|middle|exit|bridge)
: # Valid
:
;;
*)
die "TOR_RELAY_MODE must be: guard, middle, exit, or bridge (got: $TOR_RELAY_MODE)"
@@ -126,17 +93,14 @@ validate_relay_config() {
esac
fi
# Validate nickname (1-19 alphanumeric characters)
if [ -n "${TOR_NICKNAME:-}" ]; then
nickname_len=$(printf "%s" "$TOR_NICKNAME" | wc -c)
if [ "$nickname_len" -lt 1 ] || [ "$nickname_len" -gt 19 ]; then
die "TOR_NICKNAME must be 1-19 characters (got: $nickname_len)"
fi
# Check for invalid characters (busybox-compatible)
if ! printf "%s" "$TOR_NICKNAME" | grep -qE '^[a-zA-Z0-9]+$'; then
die "TOR_NICKNAME must contain only alphanumeric characters"
fi
# Check for reserved names
case "$(printf "%s" "$TOR_NICKNAME" | tr '[:upper:]' '[:lower:]')" in
unnamed|noname|default|tor|relay|bridge|exit)
die "TOR_NICKNAME cannot use reserved name: $TOR_NICKNAME"
@@ -144,52 +108,39 @@ validate_relay_config() {
esac
fi
# Validate contact info is not empty if set
if [ -n "${TOR_CONTACT_INFO:-}" ]; then
# Trim leading/trailing whitespace (fixes Cosmos ENV variable padding)
TOR_CONTACT_INFO="$(printf "%s" "$TOR_CONTACT_INFO" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
contact_len=$(printf "%s" "$TOR_CONTACT_INFO" | wc -c)
if [ "$contact_len" -lt 3 ]; then
die "TOR_CONTACT_INFO must be at least 3 characters"
fi
# Check for embedded newlines using wc -l (more reliable than grep with escape sequences)
line_count=$(printf "%s" "$TOR_CONTACT_INFO" | wc -l)
if [ "$line_count" -gt 0 ]; then
die "TOR_CONTACT_INFO cannot contain newlines (got $line_count lines)"
fi
# Note: We don't check for null bytes as printf would truncate the string anyway
# Email addresses are safe to use in torrc without additional escaping
fi
# Validate port numbers (1024-65535)
for port_var in TOR_ORPORT TOR_DIRPORT TOR_OBFS4_PORT; do
eval "port_val=\${$port_var:-}"
if [ -n "$port_val" ]; then
# Check if it's a valid integer
if ! printf "%s" "$port_val" | grep -qE '^[0-9]+$'; then
die "$port_var must be a valid port number (got: $port_val)"
fi
# Check range (allow 0 for DirPort to disable it)
if [ "$port_var" = "TOR_DIRPORT" ] && [ "$port_val" -eq 0 ]; then
: # Allow 0 for DirPort
:
elif [ "$port_val" -lt 1 ] || [ "$port_val" -gt 65535 ]; then
die "$port_var must be between 1-65535 (got: $port_val)"
fi
# Warn about privileged ports
if [ "$port_val" -lt 1024 ] && [ "$port_val" -ne 0 ]; then
warn "$port_var using privileged port $port_val (may require CAP_NET_BIND_SERVICE)"
fi
fi
done
# Validate bandwidth values if set
for bw_var in TOR_BANDWIDTH_RATE TOR_BANDWIDTH_BURST; do
eval "bw_val=\${$bw_var:-}"
if [ -n "$bw_val" ]; then
# Check for valid Tor bandwidth format (e.g., "10 MB", "1 GB")
if ! printf "%s" "$bw_val" | grep -qE '^[0-9]+ ?(Bytes?|KBytes?|MBytes?|GBytes?|TBytes?|KB?|MB?|GB?|TB?)$'; then
die "$bw_var has invalid format (got: $bw_val, expected: '10 MB' or '1 GB')"
fi
@@ -197,15 +148,12 @@ validate_relay_config() {
done
}
# Phase 3: Configuration
phase_3_configuration() {
log "🔧 Phase 3: Configuration Setup"
# Priority 1: Mounted config file
if [ -f "$TOR_CONFIG" ] && [ -s "$TOR_CONFIG" ]; then
success "Using mounted configuration: $TOR_CONFIG"
CONFIG_SOURCE="mounted"
# Priority 2: Environment variables
elif [ -n "${TOR_NICKNAME:-}" ] && [ -n "${TOR_CONTACT_INFO:-}" ]; then
log " Generating configuration from environment variables..."
validate_relay_config
@@ -219,7 +167,6 @@ phase_3_configuration() {
log ""
}
# Generate torrc from environment variables
generate_config_from_env() {
cat > "$TOR_CONFIG" << EOF
# Generated Tor configuration for ${TOR_RELAY_MODE} relay
@@ -242,7 +189,6 @@ Log notice stdout
EOF
# Mode-specific configuration
case "$TOR_RELAY_MODE" in
guard|middle)
cat >> "$TOR_CONFIG" << EOF
@@ -289,52 +235,34 @@ EOF
[ -n "${TOR_BANDWIDTH_RATE:-}" ] && echo "RelayBandwidthRate ${TOR_BANDWIDTH_RATE}" >> "$TOR_CONFIG"
[ -n "${TOR_BANDWIDTH_BURST:-}" ] && echo "RelayBandwidthBurst ${TOR_BANDWIDTH_BURST}" >> "$TOR_CONFIG"
# Process OBFS4V_* additional variables (official Tor Project bridge compatibility)
# SECURITY: Strict validation to prevent injection attacks
if [ "${OBFS4_ENABLE_ADDITIONAL_VARIABLES:-0}" = "1" ]; then
echo "" >> "$TOR_CONFIG"
echo "# Additional torrc options from OBFS4V_* environment variables" >> "$TOR_CONFIG"
# Get OBFS4V_* variables safely
env | grep '^OBFS4V_' | sort | while IFS='=' read -r key value; do
# Strip OBFS4V_ prefix to get torrc option name
torrc_key="${key#OBFS4V_}"
# SECURITY: Validate torrc_key (must be alphanumeric with underscores)
if ! printf "%s" "$torrc_key" | grep -qE '^[a-zA-Z][a-zA-Z0-9_]*$'; then
warn "Skipping invalid OBFS4V variable name: $key (must be alphanumeric)"
continue
fi
# SECURITY: Validate value doesn't contain dangerous characters
# Reject actual newlines (not the literal \n), nulls, and control chars
# Use wc -l to detect real newlines (busybox-compatible)
line_count=$(printf "%s" "$value" | wc -l)
if [ "$line_count" -gt 0 ]; then
warn "Skipping $key: value contains newlines ($line_count lines)"
continue
fi
# Check for null bytes or other control characters (ASCII < 32, except space)
if printf "%s" "$value" | tr -d '[ -~]' | grep -q .; then
warn "Skipping $key: value contains control characters"
continue
fi
# SECURITY: Whitelist known safe torrc options for OBFS4V_*
# Only allow specific torrc directives that are safe for bridges
case "$torrc_key" in
# Safe torrc options for bridges
AccountingMax|AccountingStart|Address|AddressDisableIPv6|\
BandwidthBurst|BandwidthRate|RelayBandwidthBurst|RelayBandwidthRate|\
ContactInfo|DirPort|MaxMemInQueues|NumCPUs|ORPort|\
OutboundBindAddress|OutboundBindAddressOR|Nickname|\
ServerDNSAllowBrokenConfig|ServerDNSDetectHijacking)
# Safe - write to config
# Use printf to ensure proper escaping
printf "%s %s\n" "$torrc_key" "$value" >> "$TOR_CONFIG"
;;
*)
# Unknown/potentially dangerous option - reject
warn "Skipping $key: torrc option '$torrc_key' not in whitelist"
warn " If you need this option, mount a custom torrc file instead"
;;
@@ -349,11 +277,9 @@ EOF
esac
}
# Phase 4: Validation
phase_4_validation() {
log "🔎 Phase 4: Configuration Validation"
# Check Tor binary
if ! command -v tor >/dev/null 2>&1; then
die "Tor binary not found in PATH"
fi
@@ -361,17 +287,14 @@ phase_4_validation() {
TOR_VERSION=$(tor --version 2>/dev/null | head -n1 || echo "unknown")
log " 📦 Tor version: $TOR_VERSION"
# Validate config syntax
log " Validating torrc syntax..."
# SECURITY: Set trap BEFORE creating temp file to prevent leaks
VERIFY_TMP=""
cleanup_verify_tmp() {
[ -n "$VERIFY_TMP" ] && rm -f "$VERIFY_TMP"
}
trap cleanup_verify_tmp EXIT INT TERM
# Create temp file in secure location
VERIFY_TMP=$(mktemp -t tor-verify.XXXXXX)
if ! tor --verify-config -f "$TOR_CONFIG" >"$VERIFY_TMP" 2>&1; then
@@ -389,7 +312,6 @@ phase_4_validation() {
log ""
}
# Phase 5: Build info
phase_5_build_info() {
log "📊 Phase 5: Build Information"
@@ -406,7 +328,6 @@ phase_5_build_info() {
log ""
}
# Phase 6: Diagnostic tools
phase_6_diagnostics() {
log "🧩 Phase 6: Available Diagnostic Tools"
log ""
@@ -418,14 +339,12 @@ phase_6_diagnostics() {
log ""
}
# Launch Tor
launch_tor() {
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
success "Starting Tor relay..."
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
log ""
# Start Tor in background
tor -f "$TOR_CONFIG" &
TOR_PID=$!
@@ -434,14 +353,12 @@ launch_tor() {
log "📋 Tor bootstrap logs:"
log ""
# Wait for log file to be created (max 5 seconds)
log_wait=0
while [ ! -f "$TOR_LOG_DIR/notices.log" ] && [ $log_wait -lt 50 ]; do
sleep 0.1
log_wait=$((log_wait + 1))
done
# Tail Tor logs to stdout in background (if log file exists)
if [ -f "$TOR_LOG_DIR/notices.log" ]; then
tail -F "$TOR_LOG_DIR/notices.log" 2>/dev/null &
TAIL_PID=$!
@@ -449,11 +366,9 @@ launch_tor() {
warn "Log file not created yet, bootstrap messages will not be shown"
fi
# Wait for Tor process
wait "$TOR_PID"
TOR_EXIT_CODE=$?
# Stop tail process if still running
if [ -n "$TAIL_PID" ] && kill -0 "$TAIL_PID" 2>/dev/null; then
kill -TERM "$TAIL_PID" 2>/dev/null || true
fi
@@ -463,7 +378,6 @@ launch_tor() {
cleanup_and_exit
}
# Main execution
main() {
startup_banner
phase_1_directories

View File

@@ -1,27 +1,21 @@
#!/bin/sh
# healthcheck.sh - Docker HEALTHCHECK wrapper 🐋💚
# Validates Tor configuration regardless of source (mounted file or ENV vars)
# Validates Tor configuration regardless of source (mounted file or ENV vars) 🐋💚
set -e
TOR_CONFIG="${TOR_CONFIG:-/etc/tor/torrc}"
# Check if config file exists and is readable
if [ ! -f "$TOR_CONFIG" ]; then
echo "ERROR: Config file not found: $TOR_CONFIG"
exit 1
fi
if [ ! -r "$TOR_CONFIG" ]; then
echo "ERROR: Config file not readable: $TOR_CONFIG"
exit 1
fi
# Verify Tor configuration
if tor --verify-config -f "$TOR_CONFIG" >/dev/null 2>&1; then
# Config is valid
exit 0
else
echo "ERROR: Invalid Tor configuration"
exit 1
fi
fi

View File

@@ -44,6 +44,7 @@ You can configure Tor relays using **TWO methods**:
docker run -d \
--name tor-bridge \
--network host \
--security-opt no-new-privileges:true \
-e TOR_RELAY_MODE=bridge \
-e TOR_NICKNAME=MyBridge \
-e TOR_CONTACT_INFO=admin@example.com \
@@ -293,6 +294,6 @@ If you still see this error after updating to v1.1.1:
---
**Version:** 1.1.2
**Last Updated:** 2025-11-18
**Version:** 1.1.3
**Last Updated:** 2025-12-05
**Maintainer:** rE-Bo0t.bx1 <r3bo0tbx1@brokenbotnet.com>

View File

@@ -6,9 +6,8 @@ PT_STATE_DIR="${TOR_DATA_DIR}/pt_state"
BRIDGE_LINE_FILE="${PT_STATE_DIR}/obfs4_bridgeline.txt"
TORRC_FILE="${TOR_CONFIG:-/etc/tor/torrc}"
# Check if we're running as a bridge
if [ -f "$TORRC_FILE" ] && grep -q "^BridgeRelay 1" "$TORRC_FILE" 2>/dev/null; then
: # Bridge mode confirmed
:
elif [ -f "$TORRC_FILE" ]; then
echo ""
echo "❌ Not a bridge relay"
@@ -26,7 +25,6 @@ else
exit 1
fi
# Check for bridge line file
if [ -f "$BRIDGE_LINE_FILE" ]; then
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
@@ -53,7 +51,6 @@ if [ -f "$BRIDGE_LINE_FILE" ]; then
exit 0
fi
# Bridge line not available yet
echo ""
echo "⏳ Bridge line not available yet"
echo ""
@@ -74,7 +71,6 @@ echo " 4. Verify bridge config:"
echo " docker exec <container> grep BridgeRelay $TORRC_FILE"
echo ""
# Try to find bridge line in logs as fallback
if [ -f "/var/log/tor/notices.log" ]; then
if grep -q "bridge line" /var/log/tor/notices.log 2>/dev/null; then
echo "📝 Found in logs:"

View File

@@ -10,7 +10,6 @@ if [ -f "$FINGERPRINT_FILE" ]; then
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Read and format fingerprint
NICKNAME=$(cat "$FINGERPRINT_FILE" | awk '{print $1}')
FINGERPRINT=$(cat "$FINGERPRINT_FILE" | awk '{print $2}')
@@ -18,11 +17,9 @@ if [ -f "$FINGERPRINT_FILE" ]; then
echo " 🔐 Fingerprint: $FINGERPRINT"
echo ""
# Format for easy copying
echo " 📋 Copy/paste: $NICKNAME $FINGERPRINT"
echo ""
# Show search URL
echo " 🔍 Find on Tor Metrics:"
echo " https://metrics.torproject.org/rs.html#search/$FINGERPRINT"
echo ""

View File

@@ -4,25 +4,21 @@
TOR_LOG="${TOR_LOG_DIR:-/var/log/tor}/notices.log"
TOR_DATA="${TOR_DATA_DIR:-/var/lib/tor}"
# Sanitize numeric values to prevent "bad number" errors
sanitize_num() {
v=$(printf '%s' "$1" | tr -cd '0-9')
[ -z "$v" ] && v=0
printf '%s' "$v"
}
# Check if running
if ! pgrep -x tor >/dev/null 2>&1; then
echo '{"status":"down","bootstrap":0,"reachable":"unknown","errors":0}'
exit 1
fi
# Get PID and uptime
TOR_PID=$(pgrep -x tor)
TOR_PID=$(sanitize_num "$TOR_PID")
UPTIME=$(ps -o etime= -p "$TOR_PID" 2>/dev/null | tr -d ' ' || echo "unknown")
# Check bootstrap
BOOTSTRAP=0
if [ -f "$TOR_LOG" ]; then
if grep -q "Bootstrapped 100%" "$TOR_LOG"; then
@@ -33,7 +29,6 @@ if [ -f "$TOR_LOG" ]; then
fi
BOOTSTRAP=$(sanitize_num "$BOOTSTRAP")
# Check reachability
REACHABLE="unknown"
if [ -f "$TOR_LOG" ]; then
if grep -q "Self-testing indicates your ORPort is reachable" "$TOR_LOG"; then
@@ -43,11 +38,9 @@ if [ -f "$TOR_LOG" ]; then
fi
fi
# Count errors
ERRORS=$(grep -c "\[err\]" "$TOR_LOG" 2>/dev/null || echo "0")
ERRORS=$(sanitize_num "$ERRORS")
# Get nickname and fingerprint
NICKNAME="unknown"
FINGERPRINT="unknown"
if [ -f "$TOR_DATA/fingerprint" ]; then
@@ -55,7 +48,6 @@ if [ -f "$TOR_DATA/fingerprint" ]; then
FINGERPRINT=$(cat "$TOR_DATA/fingerprint" | awk '{print $2}')
fi
# Output comprehensive JSON
cat <<EOF
{
"status": "up",

View File

@@ -4,21 +4,18 @@
TOR_LOG="${TOR_LOG_DIR:-/var/log/tor}/notices.log"
TOR_DATA="${TOR_DATA_DIR:-/var/lib/tor}"
# Sanitize numeric values to prevent "bad number" errors
sanitize_num() {
v=$(printf '%s' "$1" | tr -cd '0-9')
[ -z "$v" ] && v=0
printf '%s' "$v"
}
# Header
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🧅 Tor Relay Status"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Check if Tor is running
if pgrep -x tor >/dev/null 2>&1; then
TOR_PID=$(pgrep -x tor)
echo "🚀 Status: RUNNING (PID: $TOR_PID)"
@@ -28,14 +25,12 @@ else
exit 1
fi
# Check log file
if [ ! -f "$TOR_LOG" ]; then
echo "⚠️ Log: NOT FOUND"
echo ""
exit 1
fi
# Bootstrap progress
if grep -q "Bootstrapped 100%" "$TOR_LOG" 2>/dev/null; then
echo "✅ Bootstrap: 100% COMPLETE"
else
@@ -44,7 +39,6 @@ else
echo "⏳ Bootstrap: ${PROGRESS}%"
fi
# Reachability
if grep -q "Self-testing indicates your ORPort is reachable" "$TOR_LOG"; then
echo "🌐 ORPort: REACHABLE"
elif grep -q "ORPort is not reachable" "$TOR_LOG"; then
@@ -53,19 +47,16 @@ else
echo "❓ ORPort: TESTING..."
fi
# Get relay info (POSIX-compliant fingerprint truncation)
if [ -f "$TOR_DATA/fingerprint" ] && [ -r "$TOR_DATA/fingerprint" ]; then
FINGERPRINT=$(cat "$TOR_DATA/fingerprint" | awk '{print $2}')
NICKNAME=$(cat "$TOR_DATA/fingerprint" | awk '{print $1}')
echo "🪪 Nickname: $NICKNAME"
# POSIX-compliant substring extraction using cut
FP_START=$(printf "%s" "$FINGERPRINT" | cut -c1-8)
FP_END=$(printf "%s" "$FINGERPRINT" | cut -c33-40)
echo "🔑 Fingerprint: ${FP_START}...${FP_END}"
fi
# Count errors and warnings
ERRORS=$(grep -c "\[err\]" "$TOR_LOG" 2>/dev/null || echo "0")
ERRORS=$(sanitize_num "$ERRORS")
WARNINGS=$(grep -c "\[warn\]" "$TOR_LOG" 2>/dev/null || echo "0")
@@ -81,7 +72,6 @@ if [ "$WARNINGS" -gt 5 ]; then
echo "⚠️ Warnings: $WARNINGS"
fi
# Uptime
UPTIME=$(ps -o etime= -p "$TOR_PID" 2>/dev/null | tr -d ' ' || echo "unknown")
[ -n "$UPTIME" ] && echo "⏱️ Uptime: $UPTIME"