0799bfbae8
Sanitized version of red team infrastructure automation platform. Operational content (implant pipelines, lures, credential capture) replaced with documented stubs. Architecture and infrastructure automation code intact.
1738 lines
56 KiB
Bash
1738 lines
56 KiB
Bash
#!/usr/bin/env bash
|
|
# OpsLogger - Complete logging solution for red team operations
|
|
# Version 2.5.3 - Merged config and prompt fixes
|
|
|
|
# Configuration variables
|
|
CONFIG_FILE="${HOME}/.ops-logger.conf"
|
|
DEFAULT_TARGET="target-$(hostname | tr '.' '-')"
|
|
DEFAULT_LOG_DIR="${HOME}/OperationLogs"
|
|
PROMPT_NEW_SHELLS=true
|
|
RECORD_INTERVAL=0.5
|
|
DEBUG=false
|
|
|
|
# File markers
|
|
LOG_MARKER="/tmp/ops-logger-active"
|
|
RECORDING_MARKER="/tmp/ops-logger-recording"
|
|
CONFIG_IN_PROGRESS="/tmp/ops-logger-configuring"
|
|
VERBOSE_CMD_MARKER="/tmp/ops-logger-cmd"
|
|
|
|
# Helper functions
|
|
log_debug() {
|
|
if [[ "$DEBUG" == "true" ]]; then
|
|
local debug_dir="${LOG_DIR:-${DEFAULT_LOG_DIR}}"
|
|
mkdir -p "$debug_dir" 2>/dev/null
|
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] DEBUG: $*" >> "${debug_dir}/ops-logger-debug.log"
|
|
fi
|
|
}
|
|
|
|
ensure_dir() {
|
|
mkdir -p "$1" 2>/dev/null || return 1
|
|
return 0
|
|
}
|
|
|
|
load_config() {
|
|
if [[ -f "$CONFIG_FILE" ]]; then
|
|
source "$CONFIG_FILE"
|
|
else
|
|
TARGET_NAME="$DEFAULT_TARGET"
|
|
LOG_DIR="$DEFAULT_LOG_DIR"
|
|
fi
|
|
|
|
TARGET_NAME="${TARGET_NAME:-$DEFAULT_TARGET}"
|
|
LOG_DIR="${LOG_DIR:-$DEFAULT_LOG_DIR}"
|
|
}
|
|
|
|
save_config() {
|
|
ensure_dir "$(dirname "$CONFIG_FILE")"
|
|
cat > "$CONFIG_FILE" << EOF
|
|
# Ops Logger Configuration
|
|
TARGET_NAME="$TARGET_NAME"
|
|
LOG_DIR="$LOG_DIR"
|
|
PROMPT_NEW_SHELLS=$PROMPT_NEW_SHELLS
|
|
RECORD_INTERVAL=$RECORD_INTERVAL
|
|
DEBUG=$DEBUG
|
|
EOF
|
|
}
|
|
|
|
# Get public IP
|
|
get_public_ip() {
|
|
local ip
|
|
ip=$(curl -s --connect-timeout 3 ifconfig.me) || \
|
|
ip=$(curl -s --connect-timeout 3 ipinfo.io/ip) || \
|
|
ip=$(curl -s --connect-timeout 3 icanhazip.com) || \
|
|
ip="unknown"
|
|
echo "$ip"
|
|
}
|
|
|
|
# Detection functions
|
|
is_tmux() { [[ -n "$TMUX" ]]; }
|
|
|
|
# Get normalized pane ID (session-window-pane format)
|
|
get_pane_id() {
|
|
if is_tmux; then
|
|
# If TMUX_PANE is set (from hook), use that specific pane
|
|
if [[ -n "$TMUX_PANE" ]]; then
|
|
local session=$(tmux display -t "$TMUX_PANE" -p '#{session_name}')
|
|
local window=$(tmux display -t "$TMUX_PANE" -p '#{window_index}')
|
|
local pane=$(tmux display -t "$TMUX_PANE" -p '#{pane_index}')
|
|
echo "${session}-${window}-${pane}"
|
|
else
|
|
local session=$(tmux display -p '#{session_name}')
|
|
local window=$(tmux display -p '#{window_index}')
|
|
local pane=$(tmux display -p '#{pane_index}')
|
|
echo "${session}-${window}-${pane}"
|
|
fi
|
|
else
|
|
echo "$$"
|
|
fi
|
|
}
|
|
|
|
# Get tmux pane reference for tmux commands (% format for tmux commands)
|
|
get_tmux_pane_ref() {
|
|
if is_tmux; then
|
|
# If TMUX_PANE is set (from hook), use that specific pane
|
|
if [[ -n "$TMUX_PANE" ]]; then
|
|
echo "$TMUX_PANE"
|
|
else
|
|
tmux display -p '#{pane_id}'
|
|
fi
|
|
else
|
|
echo ""
|
|
fi
|
|
}
|
|
|
|
# Get the actual tmux prefix key instead of hardcoding C-b
|
|
get_tmux_prefix() {
|
|
# First check if we're using oh-my-tmux which often uses C-a
|
|
if tmux show-options -g | grep -q "TMUX_CONF"; then
|
|
# Check if prefix has been overridden in .local config
|
|
local prefix=$(tmux show-options -g prefix 2>/dev/null | awk '{print $2}')
|
|
if [[ -n "$prefix" ]]; then
|
|
case "$prefix" in
|
|
"C-q") echo "C-q" ;;
|
|
"C-a") echo "C-a" ;;
|
|
"C-b") echo "C-b" ;;
|
|
*) echo "$prefix" ;;
|
|
esac
|
|
else
|
|
echo "C-a" # oh-my-tmux default
|
|
fi
|
|
else
|
|
# Try to get the actual prefix from tmux config
|
|
local prefix=$(tmux show-options -g prefix 2>/dev/null | awk '{print $2}')
|
|
if [[ -n "$prefix" ]]; then
|
|
echo "$prefix"
|
|
else
|
|
# Default to C-b if we can't determine
|
|
echo "C-b"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
is_logging_active() { [[ -f "${LOG_MARKER}-$1" ]]; }
|
|
is_recording_active() { [[ -f "${RECORDING_MARKER}-$1" ]]; }
|
|
|
|
# ================================================================
|
|
# DEPENDENCY CHECKING
|
|
# ================================================================
|
|
|
|
# Check if TPM is installed
|
|
check_tpm_installed() {
|
|
[[ -d "$HOME/.tmux/plugins/tpm" ]]
|
|
}
|
|
|
|
# Check if tmux-logging plugin is installed
|
|
check_tmux_logging_installed() {
|
|
[[ -d "$HOME/.tmux/plugins/tmux-logging" ]]
|
|
}
|
|
|
|
# Print installation instructions for TMux Plugin Manager (TPM)
|
|
print_tpm_install_instructions() {
|
|
echo "========================================================================="
|
|
echo "Tmux Plugin Manager (TPM) is not installed. To install:"
|
|
echo "========================================================================="
|
|
echo "1. Run these commands:"
|
|
echo " mkdir -p ~/.tmux/plugins"
|
|
echo " git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm"
|
|
echo ""
|
|
echo "2. Add these lines to your ~/.tmux.conf:"
|
|
echo " # List of plugins"
|
|
echo " set -g @plugin 'tmux-plugins/tpm'"
|
|
echo " set -g @plugin 'tmux-plugins/tmux-sensible'"
|
|
echo " set -g @plugin 'tmux-plugins/tmux-logging'"
|
|
echo ""
|
|
echo " # Initialize TMUX plugin manager (keep this line at the bottom)"
|
|
echo " run '~/.tmux/plugins/tpm/tpm'"
|
|
echo ""
|
|
echo "3. Reload your tmux configuration:"
|
|
echo " tmux source-file ~/.tmux.conf"
|
|
echo ""
|
|
echo "4. Install plugins by pressing:"
|
|
echo " prefix + I (capital I)"
|
|
echo "========================================================================="
|
|
}
|
|
|
|
# Print installation instructions for tmux-logging plugin
|
|
print_tmux_logging_install_instructions() {
|
|
echo "========================================================================="
|
|
echo "tmux-logging plugin is not installed. To install:"
|
|
echo "========================================================================="
|
|
echo "1. Ensure TPM is installed (see previous instructions if needed)"
|
|
echo ""
|
|
echo "2. Add this line to your ~/.tmux.conf (before the tpm init line):"
|
|
echo " set -g @plugin 'tmux-plugins/tmux-logging'"
|
|
echo ""
|
|
echo "3. Reload your tmux configuration:"
|
|
echo " tmux source-file ~/.tmux.conf"
|
|
echo ""
|
|
echo "4. Install the plugin by pressing:"
|
|
echo " prefix + I (capital I)"
|
|
echo "========================================================================="
|
|
}
|
|
|
|
# Check for required dependencies
|
|
check_dependencies() {
|
|
local id="$1"
|
|
local missing=false
|
|
|
|
if ! is_tmux; then
|
|
echo "WARNING: Not running in tmux. Only basic CSV logging will be available."
|
|
echo " For full functionality, start tmux first."
|
|
return 0
|
|
fi
|
|
|
|
# Check TPM and tmux-logging plugin
|
|
if ! check_tpm_installed; then
|
|
missing=true
|
|
print_tpm_install_instructions
|
|
elif ! check_tmux_logging_installed; then
|
|
missing=true
|
|
print_tmux_logging_install_instructions
|
|
fi
|
|
|
|
if $missing; then
|
|
# Don't use display-message, just echo
|
|
echo "Plugin dependencies missing, see instructions above"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# ================================================================
|
|
# CSV LOGGING (our custom implementation)
|
|
# ================================================================
|
|
|
|
# Setup CSV logging (ORIGINAL FORMAT MAINTAINED)
|
|
setup_csv_log() {
|
|
local target="$1"
|
|
local log_dir="$2"
|
|
local csv_file="${log_dir}/${target}_commands.csv"
|
|
|
|
ensure_dir "$log_dir"
|
|
if [[ ! -f "$csv_file" ]]; then
|
|
echo '"StartTime","EndTime","SourceIP","User","Path","Command"' > "$csv_file"
|
|
fi
|
|
|
|
echo "$csv_file"
|
|
}
|
|
|
|
# Create command hook with better timing and error handling
|
|
create_command_hook() {
|
|
local hook_script="$1"
|
|
local csv_log="$2"
|
|
local pane_id="$3"
|
|
local verbose_log="$4"
|
|
|
|
cat > "$hook_script" << 'EOF'
|
|
#!/usr/bin/env bash
|
|
# Command hook - FIXED version with proper function timing
|
|
|
|
# IMMEDIATELY set all variables and disable errexit
|
|
set +e
|
|
|
|
# Set variables first before anything else
|
|
CSV_LOG="__CSV_LOG__"
|
|
VERBOSE_LOG="__VERBOSE_LOG__"
|
|
PANE_ID="__PANE_ID__"
|
|
PUBLIC_IP=$(timeout 5 curl -s ifconfig.me 2>/dev/null || echo "unknown")
|
|
RTL_INTERNAL_LOGGING=true
|
|
RTL_CMD_START_TIME=""
|
|
VERBOSE_CMD_MARKER="__VERBOSE_CMD_MARKER__"
|
|
|
|
# Function to check if command should be logged
|
|
should_log_command() {
|
|
local cmd="$1"
|
|
case "$cmd" in
|
|
*"RTL_"*|*"log_command"*|*"CSV_LOG"*|*"should_log_command"*) return 1 ;;
|
|
"history "*|*"PROMPT_COMMAND"*|*"source /tmp/ops"*) return 1 ;;
|
|
"") return 1 ;;
|
|
*) return 0 ;;
|
|
esac
|
|
}
|
|
|
|
# Function to write verbose command header
|
|
write_verbose_header() {
|
|
local cmd="$1"
|
|
local start_time="$2"
|
|
local user=$(whoami)
|
|
local path=$(pwd)
|
|
|
|
# Create a marker file with command info for the verbose processor
|
|
echo "$cmd|$start_time|$user|$path|$PUBLIC_IP|$PANE_ID" > "${VERBOSE_CMD_MARKER}-${PANE_ID}"
|
|
}
|
|
|
|
# Function to log command to CSV
|
|
RTL_log_command() {
|
|
local cmd="$1"
|
|
local start_time="$2"
|
|
local end_time="$3"
|
|
local user=$(whoami)
|
|
local path=$(pwd)
|
|
|
|
should_log_command "$cmd" || return 0
|
|
|
|
local old_flag="$RTL_INTERNAL_LOGGING"
|
|
RTL_INTERNAL_LOGGING=false
|
|
|
|
local escaped_cmd=${cmd//\"/\"\"}
|
|
|
|
printf '"%s","%s","%s","%s","%s","%s"\n' \
|
|
"$start_time" "$end_time" "$PUBLIC_IP" "$user" "$path" "$escaped_cmd" >> "$CSV_LOG" 2>/dev/null
|
|
|
|
RTL_INTERNAL_LOGGING="$old_flag"
|
|
}
|
|
|
|
# BASH-specific setup
|
|
if [[ -n "$BASH_VERSION" ]]; then
|
|
# Save original PROMPT_COMMAND
|
|
[[ -z "$RTL_ORIG_PROMPT_COMMAND" ]] && RTL_ORIG_PROMPT_COMMAND="$PROMPT_COMMAND"
|
|
|
|
RTL_LAST_COMMAND=""
|
|
RTL_LAST_HISTNUM=""
|
|
|
|
# FIXED: Define preexec function BEFORE setting trap
|
|
RTL_preexec() {
|
|
local cmd="$BASH_COMMAND"
|
|
if should_log_command "$cmd" 2>/dev/null; then
|
|
RTL_CMD_START_TIME="$(date '+%Y-%m-%d %H:%M:%S')"
|
|
write_verbose_header "$cmd" "$RTL_CMD_START_TIME"
|
|
fi
|
|
}
|
|
|
|
# FIXED: Define PROMPT_COMMAND function BEFORE using it
|
|
RTL_PROMPT_COMMAND() {
|
|
if [[ "$RTL_INTERNAL_LOGGING" == "false" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
local current_histnum=$(history 1 2>/dev/null | awk '{print $1}')
|
|
local current_cmd=$(history 1 2>/dev/null | sed 's/^[ ]*[0-9]*[ ]*//')
|
|
|
|
if [[ "$current_cmd" != "$RTL_LAST_COMMAND" && "$current_histnum" != "$RTL_LAST_HISTNUM" ]]; then
|
|
if should_log_command "$current_cmd" 2>/dev/null; then
|
|
local end_time="$(date '+%Y-%m-%d %H:%M:%S')"
|
|
local start_time="${RTL_CMD_START_TIME:-$end_time}"
|
|
|
|
RTL_log_command "$current_cmd" "$start_time" "$end_time"
|
|
|
|
RTL_LAST_COMMAND="$current_cmd"
|
|
RTL_LAST_HISTNUM="$current_histnum"
|
|
fi
|
|
fi
|
|
|
|
RTL_CMD_START_TIME=""
|
|
}
|
|
|
|
# NOW set the trap (function is already defined)
|
|
trap 'RTL_preexec 2>/dev/null || true' DEBUG 2>/dev/null
|
|
|
|
# Set PROMPT_COMMAND (function is already defined)
|
|
if [[ -n "$RTL_ORIG_PROMPT_COMMAND" ]]; then
|
|
PROMPT_COMMAND="RTL_PROMPT_COMMAND; $RTL_ORIG_PROMPT_COMMAND"
|
|
else
|
|
PROMPT_COMMAND="RTL_PROMPT_COMMAND"
|
|
fi
|
|
|
|
# ZSH-specific setup
|
|
elif [[ -n "$ZSH_VERSION" ]]; then
|
|
# Define functions first
|
|
RTL_zsh_preexec() {
|
|
local cmd="$1"
|
|
if should_log_command "$cmd" 2>/dev/null; then
|
|
RTL_CMD_START_TIME="$(date '+%Y-%m-%d %H:%M:%S')"
|
|
write_verbose_header "$cmd" "$RTL_CMD_START_TIME"
|
|
fi
|
|
}
|
|
|
|
RTL_zsh_precmd() {
|
|
if [[ "$RTL_INTERNAL_LOGGING" == "false" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
local cmd=$(fc -ln -1 2>/dev/null)
|
|
|
|
if should_log_command "$cmd" 2>/dev/null; then
|
|
local end_time="$(date '+%Y-%m-%d %H:%M:%S')"
|
|
local start_time="${RTL_CMD_START_TIME:-$end_time}"
|
|
|
|
RTL_log_command "$cmd" "$start_time" "$end_time"
|
|
fi
|
|
|
|
RTL_CMD_START_TIME=""
|
|
}
|
|
|
|
# NOW set the hooks (functions are already defined)
|
|
if autoload -Uz add-zsh-hook 2>/dev/null; then
|
|
add-zsh-hook preexec RTL_zsh_preexec 2>/dev/null || true
|
|
add-zsh-hook precmd RTL_zsh_precmd 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
# Mark as active
|
|
echo "$$" > "__LOG_MARKER__-${PANE_ID}"
|
|
echo "Ops Logger command hook installed for pane $PANE_ID" >&2
|
|
EOF
|
|
|
|
# Replace placeholders with actual values
|
|
sed -i "s|__CSV_LOG__|$csv_log|g" "$hook_script"
|
|
sed -i "s|__VERBOSE_LOG__|$verbose_log|g" "$hook_script"
|
|
sed -i "s|__PANE_ID__|$pane_id|g" "$hook_script"
|
|
sed -i "s|__LOG_MARKER__|$LOG_MARKER|g" "$hook_script"
|
|
sed -i "s|__VERBOSE_CMD_MARKER__|$VERBOSE_CMD_MARKER|g" "$hook_script"
|
|
|
|
chmod +x "$hook_script"
|
|
}
|
|
|
|
# ================================================================
|
|
# UNIFIED VERBOSE LOGGING WITH COMMAND HEADERS
|
|
# ================================================================
|
|
|
|
# Create single master verbose log file for all panes
|
|
create_master_verbose_log() {
|
|
local target="$1"
|
|
local log_dir="$2"
|
|
# Remove date from filename for continuous logging
|
|
local verbose_dir="${log_dir}/verbose"
|
|
ensure_dir "$verbose_dir"
|
|
# Single log file for entire target
|
|
local log_file="${verbose_dir}/${target}_master.log"
|
|
# Create the file with header if it doesn't exist
|
|
if [[ ! -f "$log_file" ]]; then
|
|
cat > "$log_file" << EOF
|
|
================================================================================
|
|
RED TEAM TERMINAL LOGGER
|
|
Master Verbose Log
|
|
================================================================================
|
|
Target: $target
|
|
Date: $(date +%Y-%m-%d)
|
|
Host: $(hostname)
|
|
Public IP: $(get_public_ip)
|
|
Started: $(date '+%Y-%m-%d %H:%M:%S')
|
|
================================================================================
|
|
|
|
EOF
|
|
fi
|
|
|
|
echo "$log_file"
|
|
}
|
|
|
|
# Create debug filter that captures EVERYTHING (no filtering)
|
|
create_debug_filter_script() {
|
|
local filter_script="/tmp/ops-debug-filter-$$.sh"
|
|
|
|
cat > "$filter_script" << 'EODEBUG'
|
|
#!/usr/bin/env bash
|
|
# DEBUG: Raw pipe-pane output capture
|
|
|
|
LOGFILE="$1"
|
|
PANE_ID="$2"
|
|
DEBUG_RAW_FILE="${LOGFILE}.debug-raw"
|
|
|
|
mkdir -p "$(dirname "$LOGFILE")" 2>/dev/null
|
|
|
|
echo "[$(date)] DEBUG FILTER STARTED for pane $PANE_ID" >> "$DEBUG_RAW_FILE"
|
|
echo "=================================================" >> "$DEBUG_RAW_FILE"
|
|
|
|
line_count=0
|
|
while IFS= read -r line; do
|
|
line_count=$((line_count + 1))
|
|
|
|
# Write EVERYTHING to debug file with line numbers
|
|
printf "[%04d][$(date +%H:%M:%S)] RAW: %s\n" "$line_count" "$line" >> "$DEBUG_RAW_FILE"
|
|
|
|
# Also write to main log (no filtering at all)
|
|
echo "$line" >> "$LOGFILE"
|
|
done
|
|
|
|
echo "[$(date)] DEBUG FILTER ENDED - Total lines: $line_count" >> "$DEBUG_RAW_FILE"
|
|
EODEBUG
|
|
|
|
chmod +x "$filter_script"
|
|
echo "$filter_script"
|
|
}
|
|
|
|
# Add debug start function
|
|
start_debug_verbose_logging() {
|
|
local id="$1"
|
|
local tmux_pane_ref="$2"
|
|
local target="$3"
|
|
local log_dir="$4"
|
|
|
|
local debug_log="${log_dir}/debug-${target}-${id}-$(date +%H%M%S).log"
|
|
local filter_script=$(create_debug_filter_script)
|
|
|
|
echo "Starting DEBUG verbose logging to: $debug_log"
|
|
|
|
# Use simple pipe-pane with debug filter
|
|
tmux pipe-pane -t "$tmux_pane_ref" "bash '$filter_script' '$debug_log' '$id'"
|
|
|
|
sleep 1
|
|
|
|
# Check if logging started
|
|
if tmux list-panes -F "#{pane_id} #{pane_pipe}" | grep -q "$tmux_pane_ref.*1"; then
|
|
echo "DEBUG logging started successfully"
|
|
echo "Raw output file: ${debug_log}.debug-raw"
|
|
echo "Filtered output file: $debug_log"
|
|
|
|
echo "$filter_script" > "/tmp/ops-debug-filter-script-${id}"
|
|
return 0
|
|
else
|
|
echo "ERROR: DEBUG logging failed to start"
|
|
rm -f "$filter_script"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Create the MINIMAL FIX verbose filter script
|
|
# Create the FIXED verbose filter script
|
|
create_verbose_filter_script() {
|
|
local filter_script="/tmp/ops-verbose-filter-$$.sh"
|
|
|
|
cat > "$filter_script" << 'EOFILTER'
|
|
#!/usr/bin/env bash
|
|
# FIXED: Proper command boundary detection
|
|
|
|
LOGFILE="$1"
|
|
PANE_ID="$2"
|
|
TARGET="$3"
|
|
VERBOSE_CMD_MARKER="$4"
|
|
|
|
mkdir -p "$(dirname "$LOGFILE")" 2>/dev/null
|
|
|
|
# Command tracking
|
|
CURRENT_COMMAND=""
|
|
IN_COMMAND=false
|
|
OUTPUT_LINE_COUNT=0
|
|
MAX_LINES_PER_COMMAND=22
|
|
LAST_MARKER_CHECK=""
|
|
|
|
# Function to properly close a command
|
|
close_current_command() {
|
|
if [[ "$IN_COMMAND" == "true" ]]; then
|
|
echo "==============================================================================" >> "$LOGFILE"
|
|
echo "" >> "$LOGFILE"
|
|
IN_COMMAND=false
|
|
OUTPUT_LINE_COUNT=0
|
|
CURRENT_COMMAND=""
|
|
fi
|
|
}
|
|
|
|
# Function to check for new command markers
|
|
check_command_marker() {
|
|
local marker_file="${VERBOSE_CMD_MARKER}-${PANE_ID}"
|
|
if [[ -f "$marker_file" ]]; then
|
|
# Read the marker content
|
|
local marker_content=$(cat "$marker_file" 2>/dev/null)
|
|
|
|
# Only process if this is a new marker (different from last check)
|
|
if [[ "$marker_content" != "$LAST_MARKER_CHECK" ]]; then
|
|
LAST_MARKER_CHECK="$marker_content"
|
|
|
|
# FIXED: Always close previous command first
|
|
close_current_command
|
|
|
|
# Read command info
|
|
IFS='|' read -r cmd start_time user path public_ip pane_marker <<< "$marker_content"
|
|
|
|
# Start new command if it's for this pane
|
|
if [[ "$pane_marker" == "$PANE_ID" && -n "$cmd" ]]; then
|
|
CURRENT_COMMAND="$cmd"
|
|
IN_COMMAND=true
|
|
OUTPUT_LINE_COUNT=0
|
|
|
|
# Write command header
|
|
cat >> "$LOGFILE" << EOCMD
|
|
==============================================================================
|
|
COMMAND EXECUTION - $start_time
|
|
==============================================================================
|
|
Command: $cmd
|
|
User: $user
|
|
Path: $path
|
|
Start: $start_time
|
|
Pane: $PANE_ID
|
|
Public IP: $public_ip
|
|
------------------------------------------------------------------------------
|
|
OUTPUT:
|
|
EOCMD
|
|
|
|
# Remove the marker file after processing
|
|
rm -f "$marker_file" 2>/dev/null
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Simple ANSI stripping
|
|
strip_ansi() {
|
|
local line="$1"
|
|
# Remove bracketed paste mode
|
|
line="${line//[?2004h/}"
|
|
line="${line//[?2004l/}"
|
|
# Remove ANSI escape sequences
|
|
line=$(echo "$line" | sed -E 's/\x1b\[[0-9;]*[mGKHF]//g; s/\x1b\[[?]?[0-9]*[hlc]//g')
|
|
echo "$line"
|
|
}
|
|
|
|
# Check if line looks like a shell prompt
|
|
is_prompt_line() {
|
|
local line="$1"
|
|
# More specific prompt detection patterns
|
|
if [[ "$line" =~ .*@.*:.*[\$#][[:space:]]*$ ]] || \
|
|
[[ "$line" =~ ^[[:space:]]*[\$#][[:space:]]*$ ]] || \
|
|
[[ "$line" =~ .*[\$#][[:space:]]+[a-zA-Z] ]]; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# Main processing loop
|
|
while IFS= read -r line; do
|
|
# ALWAYS check for new commands first, before any processing
|
|
check_command_marker
|
|
|
|
# Clean the line
|
|
clean_line=$(strip_ansi "$line")
|
|
|
|
# Skip completely empty lines
|
|
[[ -z "$clean_line" ]] && continue
|
|
|
|
# If we're in a command, log the output
|
|
if [[ "$IN_COMMAND" == "true" ]]; then
|
|
OUTPUT_LINE_COUNT=$((OUTPUT_LINE_COUNT + 1))
|
|
|
|
# Check if this looks like a prompt (indicating command end)
|
|
if is_prompt_line "$clean_line" && [[ $OUTPUT_LINE_COUNT -gt 1 ]]; then
|
|
# Don't log the prompt line, just close the command
|
|
close_current_command
|
|
continue
|
|
fi
|
|
|
|
# Log the output line with timestamp
|
|
if [[ $OUTPUT_LINE_COUNT -le $MAX_LINES_PER_COMMAND ]]; then
|
|
echo "$(date '+%H:%M:%S') $clean_line" >> "$LOGFILE"
|
|
elif [[ $OUTPUT_LINE_COUNT -eq $(($MAX_LINES_PER_COMMAND + 1)) ]]; then
|
|
echo "... [OUTPUT TRUNCATED - showing first $MAX_LINES_PER_COMMAND lines only] ..." >> "$LOGFILE"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# End any active command on exit
|
|
close_current_command
|
|
|
|
EOFILTER
|
|
|
|
chmod +x "$filter_script"
|
|
echo "$filter_script"
|
|
}
|
|
|
|
# Enhanced command hook that integrates with verbose logging
|
|
create_enhanced_command_hook() {
|
|
local hook_script="$1"
|
|
local csv_log="$2"
|
|
local pane_id="$3"
|
|
local verbose_log="$4"
|
|
|
|
cat > "$hook_script" << 'EOF'
|
|
#!/usr/bin/env bash
|
|
# Enhanced command hook with verbose integration
|
|
|
|
set +e
|
|
|
|
# Configuration
|
|
CSV_LOG="__CSV_LOG__"
|
|
VERBOSE_LOG="__VERBOSE_LOG__"
|
|
PANE_ID="__PANE_ID__"
|
|
PUBLIC_IP=$(timeout 5 curl -s ifconfig.me 2>/dev/null || echo "unknown")
|
|
RTL_INTERNAL_LOGGING=true
|
|
RTL_CMD_START_TIME=""
|
|
VERBOSE_CMD_MARKER="__VERBOSE_CMD_MARKER__"
|
|
|
|
# Function to check if command should be logged
|
|
should_log_command() {
|
|
local cmd="$1"
|
|
case "$cmd" in
|
|
*"RTL_"*|*"log_command"*|*"CSV_LOG"*|*"should_log_command"*) return 1 ;;
|
|
"history "*|*"PROMPT_COMMAND"*|*"source /tmp/ops"*) return 1 ;;
|
|
"") return 1 ;;
|
|
*) return 0 ;;
|
|
esac
|
|
}
|
|
|
|
# Enhanced function to write verbose command header
|
|
write_verbose_header() {
|
|
local cmd="$1"
|
|
local start_time="$2"
|
|
local user=$(whoami)
|
|
local path=$(pwd)
|
|
|
|
# Create marker for verbose filter
|
|
echo "$cmd|$start_time|$user|$path|$PUBLIC_IP|$PANE_ID" > "${VERBOSE_CMD_MARKER}-${PANE_ID}"
|
|
}
|
|
|
|
# Function to log command to CSV (unchanged)
|
|
RTL_log_command() {
|
|
local cmd="$1"
|
|
local start_time="$2"
|
|
local end_time="$3"
|
|
local user=$(whoami)
|
|
local path=$(pwd)
|
|
|
|
should_log_command "$cmd" || return 0
|
|
|
|
local old_flag="$RTL_INTERNAL_LOGGING"
|
|
RTL_INTERNAL_LOGGING=false
|
|
|
|
local escaped_cmd=${cmd//\"/\"\"}
|
|
|
|
printf '"%s","%s","%s","%s","%s","%s"\n' \
|
|
"$start_time" "$end_time" "$PUBLIC_IP" "$user" "$path" "$escaped_cmd" >> "$CSV_LOG" 2>/dev/null
|
|
|
|
RTL_INTERNAL_LOGGING="$old_flag"
|
|
}
|
|
|
|
# BASH-specific setup
|
|
if [[ -n "$BASH_VERSION" ]]; then
|
|
[[ -z "$RTL_ORIG_PROMPT_COMMAND" ]] && RTL_ORIG_PROMPT_COMMAND="$PROMPT_COMMAND"
|
|
|
|
RTL_LAST_COMMAND=""
|
|
RTL_LAST_HISTNUM=""
|
|
|
|
RTL_preexec() {
|
|
local cmd="$BASH_COMMAND"
|
|
if should_log_command "$cmd" 2>/dev/null; then
|
|
RTL_CMD_START_TIME="$(date '+%Y-%m-%d %H:%M:%S')"
|
|
write_verbose_header "$cmd" "$RTL_CMD_START_TIME"
|
|
fi
|
|
}
|
|
|
|
RTL_PROMPT_COMMAND() {
|
|
if [[ "$RTL_INTERNAL_LOGGING" == "false" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
local current_histnum=$(history 1 2>/dev/null | awk '{print $1}')
|
|
local current_cmd=$(history 1 2>/dev/null | sed 's/^[ ]*[0-9]*[ ]*//')
|
|
|
|
if [[ "$current_cmd" != "$RTL_LAST_COMMAND" && "$current_histnum" != "$RTL_LAST_HISTNUM" ]]; then
|
|
if should_log_command "$current_cmd" 2>/dev/null; then
|
|
local end_time="$(date '+%Y-%m-%d %H:%M:%S')"
|
|
local start_time="${RTL_CMD_START_TIME:-$end_time}"
|
|
|
|
RTL_log_command "$current_cmd" "$start_time" "$end_time"
|
|
|
|
RTL_LAST_COMMAND="$current_cmd"
|
|
RTL_LAST_HISTNUM="$current_histnum"
|
|
fi
|
|
fi
|
|
|
|
RTL_CMD_START_TIME=""
|
|
}
|
|
|
|
trap 'RTL_preexec 2>/dev/null || true' DEBUG 2>/dev/null
|
|
|
|
if [[ -n "$RTL_ORIG_PROMPT_COMMAND" ]]; then
|
|
PROMPT_COMMAND="RTL_PROMPT_COMMAND; $RTL_ORIG_PROMPT_COMMAND"
|
|
else
|
|
PROMPT_COMMAND="RTL_PROMPT_COMMAND"
|
|
fi
|
|
|
|
# ZSH-specific setup
|
|
elif [[ -n "$ZSH_VERSION" ]]; then
|
|
RTL_zsh_preexec() {
|
|
local cmd="$1"
|
|
if should_log_command "$cmd" 2>/dev/null; then
|
|
RTL_CMD_START_TIME="$(date '+%Y-%m-%d %H:%M:%S')"
|
|
write_verbose_header "$cmd" "$RTL_CMD_START_TIME"
|
|
fi
|
|
}
|
|
|
|
RTL_zsh_precmd() {
|
|
if [[ "$RTL_INTERNAL_LOGGING" == "false" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
local cmd=$(fc -ln -1 2>/dev/null)
|
|
|
|
if should_log_command "$cmd" 2>/dev/null; then
|
|
local end_time="$(date '+%Y-%m-%d %H:%M:%S')"
|
|
local start_time="${RTL_CMD_START_TIME:-$end_time}"
|
|
|
|
RTL_log_command "$cmd" "$start_time" "$end_time"
|
|
fi
|
|
|
|
RTL_CMD_START_TIME=""
|
|
}
|
|
|
|
if autoload -Uz add-zsh-hook 2>/dev/null; then
|
|
add-zsh-hook preexec RTL_zsh_preexec 2>/dev/null || true
|
|
add-zsh-hook precmd RTL_zsh_precmd 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
# Mark as active
|
|
echo "$$" > "__LOG_MARKER__-${PANE_ID}"
|
|
echo "Enhanced Ops Logger with verbose integration installed for pane $PANE_ID" >&2
|
|
EOF
|
|
|
|
# Replace placeholders
|
|
sed -i "s|__CSV_LOG__|$csv_log|g" "$hook_script"
|
|
sed -i "s|__VERBOSE_LOG__|$verbose_log|g" "$hook_script"
|
|
sed -i "s|__PANE_ID__|$pane_id|g" "$hook_script"
|
|
sed -i "s|__LOG_MARKER__|$LOG_MARKER|g" "$hook_script"
|
|
sed -i "s|__VERBOSE_CMD_MARKER__|$VERBOSE_CMD_MARKER|g" "$hook_script"
|
|
|
|
chmod +x "$hook_script"
|
|
}
|
|
|
|
# Start unified verbose logging
|
|
# Start unified verbose logging (add delay for stability)
|
|
start_unified_verbose_logging() {
|
|
local id="$1"
|
|
local tmux_pane_ref="$2"
|
|
local target="$3"
|
|
local log_dir="$4"
|
|
|
|
# Create master log file
|
|
local master_log=$(create_master_verbose_log "$target" "$log_dir")
|
|
|
|
# Create filter script
|
|
local filter_script=$(create_verbose_filter_script)
|
|
|
|
log_debug "Starting unified verbose logging to: $master_log"
|
|
|
|
# Use pipe-pane with our filter script
|
|
tmux pipe-pane -t "$tmux_pane_ref" "bash '$filter_script' '$master_log' '$id' '$target' '$VERBOSE_CMD_MARKER'"
|
|
|
|
# Give pipe-pane time to establish
|
|
sleep 0.5
|
|
|
|
# Check if logging started successfully
|
|
if tmux list-panes -F "#{pane_id} #{pane_pipe}" | grep -q "$tmux_pane_ref.*1"; then
|
|
log_debug "Unified verbose logging started successfully"
|
|
|
|
# Store filter script path for cleanup
|
|
echo "$filter_script" > "/tmp/ops-filter-script-${id}"
|
|
|
|
return 0
|
|
else
|
|
log_debug "ERROR: Unified verbose logging failed to start"
|
|
rm -f "$filter_script"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Stop unified verbose logging
|
|
stop_unified_verbose_logging() {
|
|
local tmux_pane_ref="$1"
|
|
local id="$2"
|
|
|
|
# Stop pipe-pane
|
|
tmux pipe-pane -t "$tmux_pane_ref" 2>/dev/null || true
|
|
|
|
# Clean up filter script
|
|
if [[ -f "/tmp/ops-filter-script-${id}" ]]; then
|
|
local filter_script=$(cat "/tmp/ops-filter-script-${id}")
|
|
rm -f "$filter_script" 2>/dev/null
|
|
rm -f "/tmp/ops-filter-script-${id}"
|
|
fi
|
|
|
|
# Clean up markers
|
|
rm -f "${VERBOSE_CMD_MARKER}-${id}" 2>/dev/null
|
|
rm -f "/tmp/ops-cmd-active-${id}" 2>/dev/null
|
|
|
|
log_debug "Stopped unified verbose logging for pane: $tmux_pane_ref"
|
|
}
|
|
|
|
# ================================================================
|
|
# OHMYTMUX-COMPATIBLE WINDOW NAME MANAGEMENT
|
|
# ================================================================
|
|
|
|
# Improved window name handling that works with ohmytmux
|
|
get_current_window_name() {
|
|
local tmux_pane_ref="$1"
|
|
local name=$(tmux display -t "$tmux_pane_ref" -p '#{window_name}')
|
|
|
|
# Remove our indicators as well as any ohmytmux status indicators
|
|
name="${name#🔴 }"
|
|
name="${name#🎥 }"
|
|
name="${name#● }"
|
|
name="${name#⚠ }"
|
|
name="${name#▶ }"
|
|
|
|
echo "$name"
|
|
}
|
|
|
|
set_window_logging_indicator() {
|
|
local tmux_pane_ref="$1"
|
|
local current_name=$(get_current_window_name "$tmux_pane_ref")
|
|
|
|
# Preserve ohmytmux automatic formats but add our indicator
|
|
if [[ "$current_name" == *Z ]]; then
|
|
# Zoomed window format in ohmytmux
|
|
tmux rename-window -t "$tmux_pane_ref" "🔴 ${current_name%Z}Z"
|
|
else
|
|
tmux rename-window -t "$tmux_pane_ref" "🔴 $current_name"
|
|
fi
|
|
}
|
|
|
|
set_window_recording_indicator() {
|
|
local tmux_pane_ref="$1"
|
|
local current_name=$(get_current_window_name "$tmux_pane_ref")
|
|
|
|
# Remove logging indicator if present and add recording
|
|
current_name="${current_name#🔴 }"
|
|
|
|
# Preserve ohmytmux automatic formats
|
|
if [[ "$current_name" == *Z ]]; then
|
|
# Zoomed window format in ohmytmux
|
|
tmux rename-window -t "$tmux_pane_ref" "🎥 ${current_name%Z}Z"
|
|
else
|
|
tmux rename-window -t "$tmux_pane_ref" "🎥 $current_name"
|
|
fi
|
|
}
|
|
|
|
clear_window_indicators() {
|
|
local tmux_pane_ref="$1"
|
|
local current_name=$(tmux display -t "$tmux_pane_ref" -p '#{window_name}')
|
|
local clean_name=$(get_current_window_name "$tmux_pane_ref")
|
|
|
|
# Preserve any ohmytmux indicators that might be present
|
|
if [[ "$current_name" == *Z ]]; then
|
|
# Zoomed window format in ohmytmux
|
|
tmux rename-window -t "$tmux_pane_ref" "${clean_name}Z"
|
|
else
|
|
tmux rename-window -t "$tmux_pane_ref" "$clean_name"
|
|
fi
|
|
}
|
|
|
|
# MAIN LOGGING FUNCTIONS
|
|
# ================================================================
|
|
|
|
# Enhanced logging installation with unified verbose logging
|
|
install_logging() {
|
|
local id="$1"
|
|
local csv_log="$2"
|
|
local target="$3"
|
|
local log_dir="$4"
|
|
|
|
log_debug "Installing enhanced logging for pane $id"
|
|
|
|
if is_tmux; then
|
|
local tmux_pane_ref=$(get_tmux_pane_ref)
|
|
|
|
# Get master verbose log path
|
|
local master_log=$(create_master_verbose_log "$target" "$log_dir")
|
|
|
|
# Start unified verbose logging
|
|
if start_unified_verbose_logging "$id" "$tmux_pane_ref" "$target" "$log_dir"; then
|
|
log_debug "Unified verbose logging started successfully"
|
|
else
|
|
log_debug "Verbose logging failed, continuing with CSV only"
|
|
fi
|
|
|
|
# Create enhanced command hook
|
|
local hook_script="/tmp/ops-hook-${id}.sh"
|
|
create_enhanced_command_hook "$hook_script" "$csv_log" "$id" "$master_log"
|
|
|
|
# Install the hook
|
|
tmux send-keys -t "$tmux_pane_ref" "source '$hook_script' 2>/dev/null && echo 'Enhanced logging hooks installed successfully' || echo 'Logging may have warnings but is active'" ENTER
|
|
|
|
sleep 2
|
|
|
|
# Set window indicator
|
|
set_window_logging_indicator "$tmux_pane_ref"
|
|
|
|
# Mark as active
|
|
touch "${LOG_MARKER}-${id}"
|
|
touch "${LOG_MARKER}-${id}.success"
|
|
|
|
else
|
|
# Direct shell logging (CSV only)
|
|
local hook_script="/tmp/ops-hook-${id}.sh"
|
|
create_enhanced_command_hook "$hook_script" "$csv_log" "$id" ""
|
|
source "$hook_script"
|
|
echo "Direct shell logging started (CSV only)"
|
|
|
|
touch "${LOG_MARKER}-${id}"
|
|
touch "${LOG_MARKER}-${id}.success"
|
|
fi
|
|
|
|
log_debug "Enhanced logging installation completed"
|
|
}
|
|
|
|
# Enhanced removal function
|
|
remove_logging() {
|
|
local id="$1"
|
|
|
|
log_debug "Removing enhanced logging for pane $id"
|
|
|
|
if is_tmux; then
|
|
local tmux_pane_ref=$(get_tmux_pane_ref)
|
|
|
|
# Stop unified verbose logging
|
|
stop_unified_verbose_logging "$tmux_pane_ref" "$id"
|
|
|
|
# Clean up command hooks (same as before)
|
|
local cleanup_script="/tmp/ops-cleanup-${id}.sh"
|
|
cat > "$cleanup_script" << 'EOF'
|
|
#!/usr/bin/env bash
|
|
set +e
|
|
|
|
if [[ -n "$BASH_VERSION" ]]; then
|
|
trap - DEBUG 2>/dev/null
|
|
if [[ -n "$RTL_ORIG_PROMPT_COMMAND" ]]; then
|
|
PROMPT_COMMAND="$RTL_ORIG_PROMPT_COMMAND"
|
|
unset RTL_ORIG_PROMPT_COMMAND
|
|
else
|
|
unset PROMPT_COMMAND
|
|
fi
|
|
unset -f RTL_PROMPT_COMMAND RTL_log_command should_log_command RTL_preexec write_verbose_header 2>/dev/null
|
|
elif [[ -n "$ZSH_VERSION" ]]; then
|
|
add-zsh-hook -d preexec RTL_zsh_preexec 2>/dev/null
|
|
add-zsh-hook -d precmd RTL_zsh_precmd 2>/dev/null
|
|
unset -f RTL_zsh_preexec RTL_zsh_precmd RTL_log_command should_log_command write_verbose_header 2>/dev/null
|
|
fi
|
|
|
|
unset RTL_CMD_START_TIME RTL_LAST_COMMAND RTL_LAST_HISTNUM RTL_INTERNAL_LOGGING 2>/dev/null
|
|
unset CSV_LOG VERBOSE_LOG PANE_ID PUBLIC_IP VERBOSE_CMD_MARKER 2>/dev/null
|
|
|
|
echo "Enhanced Ops Logger hooks removed"
|
|
EOF
|
|
chmod +x "$cleanup_script"
|
|
|
|
tmux send-keys -t "$tmux_pane_ref" "source '$cleanup_script' 2>/dev/null; rm -f '$cleanup_script'" ENTER
|
|
|
|
# Clear window indicators
|
|
clear_window_indicators "$tmux_pane_ref"
|
|
|
|
# Clean up temp files
|
|
rm -f "/tmp/ops-hook-${id}.sh"
|
|
|
|
else
|
|
# Direct shell cleanup (same as before)
|
|
if [[ -n "$BASH_VERSION" ]]; then
|
|
trap - DEBUG 2>/dev/null
|
|
if [[ -n "$RTL_ORIG_PROMPT_COMMAND" ]]; then
|
|
PROMPT_COMMAND="$RTL_ORIG_PROMPT_COMMAND"
|
|
unset RTL_ORIG_PROMPT_COMMAND
|
|
else
|
|
unset PROMPT_COMMAND
|
|
fi
|
|
unset -f RTL_PROMPT_COMMAND RTL_log_command should_log_command RTL_preexec write_verbose_header 2>/dev/null
|
|
elif [[ -n "$ZSH_VERSION" ]]; then
|
|
add-zsh-hook -d preexec RTL_zsh_preexec 2>/dev/null
|
|
add-zsh-hook -d precmd RTL_zsh_precmd 2>/dev/null
|
|
unset -f RTL_zsh_preexec RTL_zsh_precmd RTL_log_command should_log_command write_verbose_header 2>/dev/null
|
|
fi
|
|
|
|
echo "Direct shell logging stopped"
|
|
fi
|
|
|
|
# Remove markers
|
|
rm -f "${LOG_MARKER}-${id}" "${LOG_MARKER}-${id}.success"
|
|
rm -f "/tmp/ops-hook-${id}.sh"
|
|
|
|
log_debug "Enhanced logging removal completed"
|
|
}
|
|
|
|
# ================================================================
|
|
# RECORDING FUNCTIONS
|
|
# ================================================================
|
|
|
|
start_recording() {
|
|
local id="$1"
|
|
local target="$2"
|
|
local log_dir="$3"
|
|
local recordings_dir="${log_dir}/recordings"
|
|
local timestamp=$(date +%Y%m%d_%H%M%S)
|
|
|
|
ensure_dir "$recordings_dir"
|
|
|
|
# Check for asciinema
|
|
if ! command -v asciinema >/dev/null 2>&1; then
|
|
local error_msg="ERROR: asciinema not installed"
|
|
echo "$error_msg" >&2
|
|
echo "To install asciinema:"
|
|
echo " Ubuntu/Debian: sudo apt install asciinema"
|
|
echo " Fedora/RHEL: sudo dnf install asciinema"
|
|
echo " macOS: brew install asciinema"
|
|
echo " Pip: pip3 install asciinema"
|
|
return 1
|
|
fi
|
|
|
|
local cast_file="${recordings_dir}/${target}_${id}_${timestamp}.cast"
|
|
echo "asciinema:$cast_file" > "${RECORDING_MARKER}-${id}"
|
|
|
|
if is_tmux; then
|
|
local tmux_pane_ref=$(get_tmux_pane_ref)
|
|
|
|
# Set recording indicator (ohmytmux compatible)
|
|
set_window_recording_indicator "$tmux_pane_ref"
|
|
|
|
# Start recording cleanly
|
|
tmux send-keys -t "$tmux_pane_ref" "asciinema rec '$cast_file'" ENTER
|
|
|
|
else
|
|
echo "Starting asciinema recording. Use 'exit' or Ctrl+D to stop."
|
|
asciinema rec "$cast_file"
|
|
fi
|
|
}
|
|
|
|
stop_recording() {
|
|
local id="$1"
|
|
|
|
[[ ! -f "${RECORDING_MARKER}-${id}" ]] && {
|
|
echo "No active recording"
|
|
return 1
|
|
}
|
|
|
|
read -r rec_type rec_path < <(cat "${RECORDING_MARKER}-${id}" | tr ':' ' ')
|
|
|
|
if [[ "$rec_type" == "asciinema" ]]; then
|
|
if is_tmux; then
|
|
local tmux_pane_ref=$(get_tmux_pane_ref)
|
|
|
|
# Try sending Ctrl+D
|
|
tmux send-keys -t "$tmux_pane_ref" C-d
|
|
|
|
sleep 1
|
|
|
|
# If that didn't work, try killing the asciinema process
|
|
if ps aux | grep -v grep | grep -q "asciinema rec"; then
|
|
log_debug "Ctrl+D didn't stop recording, trying to kill asciinema process"
|
|
tmux send-keys -t "$tmux_pane_ref" "pkill -f 'asciinema rec'" ENTER
|
|
|
|
sleep 1
|
|
|
|
if ps aux | grep -v grep | grep -q "asciinema rec"; then
|
|
pkill -f "asciinema rec" || true
|
|
fi
|
|
fi
|
|
|
|
# Clear recording indicator but preserve logging indicator
|
|
local current_name=$(tmux display -t "$tmux_pane_ref" -p '#{window_name}')
|
|
if [[ "$current_name" == "🎥"* ]]; then
|
|
if is_logging_active "$id"; then
|
|
local clean_name="${current_name#🎥 }"
|
|
tmux rename-window -t "$tmux_pane_ref" "🔴 $clean_name"
|
|
else
|
|
clear_window_indicators "$tmux_pane_ref"
|
|
fi
|
|
fi
|
|
|
|
else
|
|
echo "Recording stopped: $rec_path"
|
|
fi
|
|
fi
|
|
|
|
rm -f "${RECORDING_MARKER}-${id}"
|
|
}
|
|
|
|
# ================================================================
|
|
# CONFIGURATION FUNCTIONS
|
|
# ================================================================
|
|
|
|
create_config() {
|
|
local is_first_run="${1:-false}"
|
|
|
|
# If this is a first run from ensure_config in tmux, use the local version's approach
|
|
if [[ "$is_first_run" == "true" ]] && is_tmux; then
|
|
# Mark configuration as in progress
|
|
touch "$CONFIG_IN_PROGRESS"
|
|
|
|
# Create a temporary script that will handle the config creation
|
|
local config_script="/tmp/ops-config-$$.sh"
|
|
cat > "$config_script" << 'EOSCRIPT'
|
|
#!/usr/bin/env bash
|
|
CONFIG_FILE="${HOME}/.ops-logger.conf"
|
|
DEFAULT_TARGET="target-$(hostname | tr '.' '-')"
|
|
DEFAULT_LOG_DIR="${HOME}/OperationLogs"
|
|
|
|
echo "Red Team Terminal Logger - First Time Setup"
|
|
echo "==========================================="
|
|
echo ""
|
|
|
|
read -p "Enter target name [$DEFAULT_TARGET]: " TARGET_NAME
|
|
TARGET_NAME="${TARGET_NAME:-$DEFAULT_TARGET}"
|
|
|
|
read -p "Enter log directory [$DEFAULT_LOG_DIR]: " LOG_DIR
|
|
LOG_DIR="${LOG_DIR:-$DEFAULT_LOG_DIR}"
|
|
|
|
read -p "Prompt for logging in new shells? [Y/n]: " PROMPT_NEW_SHELLS
|
|
[[ "${PROMPT_NEW_SHELLS,,}" == "n" ]] && PROMPT_NEW_SHELLS=false || PROMPT_NEW_SHELLS=true
|
|
|
|
read -p "Enable debug logging? [y/N]: " DEBUG
|
|
[[ "${DEBUG,,}" == "y" ]] && DEBUG=true || DEBUG=false
|
|
|
|
# Save config
|
|
mkdir -p "$(dirname "$CONFIG_FILE")"
|
|
cat > "$CONFIG_FILE" << EOF
|
|
# Ops Logger Configuration
|
|
TARGET_NAME="$TARGET_NAME"
|
|
LOG_DIR="$LOG_DIR"
|
|
PROMPT_NEW_SHELLS=$PROMPT_NEW_SHELLS
|
|
RECORD_INTERVAL=0.5
|
|
DEBUG=$DEBUG
|
|
EOF
|
|
|
|
# Create directories
|
|
mkdir -p "$LOG_DIR" "$LOG_DIR/verbose" "$LOG_DIR/recordings"
|
|
|
|
# Remove the in-progress marker
|
|
rm -f "/tmp/ops-logger-configuring"
|
|
|
|
echo ""
|
|
echo "Configuration saved!"
|
|
echo " Target name: $TARGET_NAME"
|
|
echo " Log directory: $LOG_DIR"
|
|
echo " Prompt new shells: $PROMPT_NEW_SHELLS"
|
|
echo " Debug mode: $DEBUG"
|
|
echo ""
|
|
echo "Now run: prefix+L to start logging (or ops-logger --start)"
|
|
echo "Press ENTER to close this window..."
|
|
read
|
|
EOSCRIPT
|
|
chmod +x "$config_script"
|
|
|
|
# Open new window for config
|
|
bash "$config_script"; rm -f "$config_script"
|
|
return 0
|
|
fi
|
|
|
|
# For manual --config or non-tmux environments (GitHub version approach)
|
|
echo "Red Team Terminal Logger - First Time Setup"
|
|
echo "==========================================="
|
|
echo ""
|
|
|
|
CONFIG_FILE="${HOME}/.ops-logger.conf"
|
|
DEFAULT_TARGET="target-$(hostname | tr '.' '-')"
|
|
DEFAULT_LOG_DIR="${HOME}/OperationLogs"
|
|
|
|
# Detect whether we're in a tty
|
|
if [[ ! -t 0 ]]; then
|
|
echo "Error: Cannot prompt user in non-interactive shell."
|
|
return 1
|
|
fi
|
|
|
|
read -p "Enter target name [$DEFAULT_TARGET]: " TARGET_NAME
|
|
TARGET_NAME="${TARGET_NAME:-$DEFAULT_TARGET}"
|
|
|
|
read -p "Enter log directory [$DEFAULT_LOG_DIR]: " LOG_DIR
|
|
LOG_DIR="${LOG_DIR:-$DEFAULT_LOG_DIR}"
|
|
|
|
read -p "Prompt for logging in new shells? [Y/n]: " PROMPT_NEW_SHELLS
|
|
[[ "${PROMPT_NEW_SHELLS,,}" == "n" ]] && PROMPT_NEW_SHELLS=false || PROMPT_NEW_SHELLS=true
|
|
|
|
read -p "Enable debug logging? [y/N]: " DEBUG
|
|
[[ "${DEBUG,,}" == "y" ]] && DEBUG=true || DEBUG=false
|
|
|
|
# Save config
|
|
mkdir -p "$(dirname "$CONFIG_FILE")"
|
|
cat > "$CONFIG_FILE" <<EOF
|
|
# Ops Logger Configuration
|
|
TARGET_NAME="$TARGET_NAME"
|
|
LOG_DIR="$LOG_DIR"
|
|
PROMPT_NEW_SHELLS=$PROMPT_NEW_SHELLS
|
|
RECORD_INTERVAL=0.5
|
|
DEBUG=$DEBUG
|
|
EOF
|
|
|
|
mkdir -p "$LOG_DIR" "$LOG_DIR/verbose" "$LOG_DIR/recordings"
|
|
|
|
echo ""
|
|
echo "Configuration saved!"
|
|
echo " Target name: $TARGET_NAME"
|
|
echo " Log directory: $LOG_DIR"
|
|
echo " Prompt new shells: $PROMPT_NEW_SHELLS"
|
|
echo " Debug mode: $DEBUG"
|
|
echo ""
|
|
echo "Now run: prefix+L to start logging (or ops-logger --start)"
|
|
}
|
|
|
|
# Keep the ensure_config from local version
|
|
ensure_config() {
|
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
create_config "true"
|
|
# If configuration is in progress, abort the current operation
|
|
if [[ -f "$CONFIG_IN_PROGRESS" ]]; then
|
|
return 1
|
|
fi
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
# ================================================================
|
|
# MAIN USER FUNCTIONS
|
|
# ================================================================
|
|
|
|
start_logging() {
|
|
# Ensure we output to terminal if run from tmux keybinding
|
|
if is_tmux && [[ -t 1 ]]; then
|
|
local tty=$(tty 2>/dev/null || echo "/dev/tty")
|
|
exec 1>$tty 2>$tty
|
|
fi
|
|
|
|
# Auto-setup config with prompts on first logging start
|
|
ensure_config || return 1
|
|
load_config
|
|
|
|
local id=$(get_pane_id)
|
|
is_logging_active "$id" && {
|
|
echo "Already logging"
|
|
return 0
|
|
}
|
|
|
|
local csv_log=$(setup_csv_log "$TARGET_NAME" "$LOG_DIR")
|
|
|
|
install_logging "$id" "$csv_log" "$TARGET_NAME" "$LOG_DIR"
|
|
}
|
|
|
|
stop_logging() {
|
|
# Ensure we output to terminal if run from tmux keybinding
|
|
if is_tmux && [[ -t 1 ]]; then
|
|
local tty=$(tty 2>/dev/null || echo "/dev/tty")
|
|
exec 1>$tty 2>$tty
|
|
fi
|
|
|
|
local id=$(get_pane_id)
|
|
is_logging_active "$id" || {
|
|
echo "Not logging"
|
|
return 0
|
|
}
|
|
|
|
remove_logging "$id"
|
|
}
|
|
|
|
toggle_logging() {
|
|
# Ensure we output to terminal if run from tmux keybinding
|
|
if is_tmux && [[ -t 1 ]]; then
|
|
local tty=$(tty 2>/dev/null || echo "/dev/tty")
|
|
exec 1>$tty 2>$tty
|
|
fi
|
|
|
|
local id=$(get_pane_id)
|
|
is_logging_active "$id" && stop_logging || start_logging
|
|
}
|
|
|
|
toggle_recording() {
|
|
# Ensure we output to terminal if run from tmux keybinding
|
|
if is_tmux && [[ -t 1 ]]; then
|
|
local tty=$(tty 2>/dev/null || echo "/dev/tty")
|
|
exec 1>$tty 2>$tty
|
|
fi
|
|
|
|
local id=$(get_pane_id)
|
|
# Load existing config or use defaults
|
|
[[ -f "$CONFIG_FILE" ]] && load_config || { TARGET_NAME="$DEFAULT_TARGET"; LOG_DIR="$DEFAULT_LOG_DIR"; }
|
|
|
|
is_recording_active "$id" && stop_recording "$id" || start_recording "$id" "$TARGET_NAME" "$LOG_DIR"
|
|
}
|
|
|
|
prompt_for_logging() {
|
|
local id
|
|
id=$(get_pane_id)
|
|
|
|
# ✅ Debug logging to see what's happening
|
|
echo "[$(date)] prompt_for_logging called for pane: $id" >> /tmp/ops-logger-prompt-debug.log
|
|
echo "[$(date)] TMUX_PANE: ${TMUX_PANE:-'not set'}" >> /tmp/ops-logger-prompt-debug.log
|
|
|
|
# ✅ Avoid recursion with a pane-specific lock - BUT with timeout
|
|
local lock_file="/tmp/ops-logger-prompted-${id}"
|
|
if [[ -f "$lock_file" ]]; then
|
|
# Check if lock file is stale (older than 30 seconds)
|
|
if [[ $(find "$lock_file" -mmin +0.5 2>/dev/null) ]]; then
|
|
echo "[$(date)] Removing stale lock file" >> /tmp/ops-logger-prompt-debug.log
|
|
rm -f "$lock_file"
|
|
else
|
|
echo "[$(date)] Recent lock file exists, returning" >> /tmp/ops-logger-prompt-debug.log
|
|
return 0
|
|
fi
|
|
fi
|
|
touch "$lock_file"
|
|
|
|
# ✅ Cleanup function to ensure lock file is always removed
|
|
cleanup_lock() {
|
|
rm -f "$lock_file"
|
|
echo "[$(date)] Lock file cleaned up" >> /tmp/ops-logger-prompt-debug.log
|
|
}
|
|
trap cleanup_lock EXIT
|
|
|
|
is_logging_active "$id" && {
|
|
echo "[$(date)] Already logging, returning" >> /tmp/ops-logger-prompt-debug.log
|
|
cleanup_lock
|
|
return 0
|
|
}
|
|
|
|
load_config
|
|
echo "[$(date)] Config loaded - PROMPT_NEW_SHELLS: $PROMPT_NEW_SHELLS" >> /tmp/ops-logger-prompt-debug.log
|
|
|
|
[[ "$PROMPT_NEW_SHELLS" != "true" ]] && {
|
|
echo "[$(date)] Prompting disabled, returning" >> /tmp/ops-logger-prompt-debug.log
|
|
cleanup_lock
|
|
return 0
|
|
}
|
|
|
|
# ✅ FIXED: Use tmux display-popup for proper interaction in tmux
|
|
if is_tmux; then
|
|
echo "[$(date)] In tmux, creating popup" >> /tmp/ops-logger-prompt-debug.log
|
|
|
|
# Create a temporary script for the popup
|
|
local popup_script="/tmp/ops-prompt-${id}.sh"
|
|
local script_path=$(readlink -f "$0")
|
|
|
|
cat > "$popup_script" << 'EOPOPUP'
|
|
#!/bin/bash
|
|
echo "Start logging this shell?"
|
|
echo ""
|
|
echo " [Y]es - Start logging"
|
|
echo " [N]o - Skip logging"
|
|
echo ""
|
|
read -n 1 -p "Choice [Y/n]: " response
|
|
echo ""
|
|
|
|
if [[ -z "$response" || "${response,,}" == "y" ]]; then
|
|
echo "Starting logging..."
|
|
__SCRIPT_PATH__ --start
|
|
echo "Logging started!"
|
|
sleep 2
|
|
else
|
|
echo "Logging skipped."
|
|
sleep 1
|
|
fi
|
|
EOPOPUP
|
|
|
|
# Replace placeholder with actual script path
|
|
sed -i "s|__SCRIPT_PATH__|$script_path|g" "$popup_script"
|
|
chmod +x "$popup_script"
|
|
|
|
echo "[$(date)] Popup script created, calling display-popup" >> /tmp/ops-logger-prompt-debug.log
|
|
|
|
# Use tmux display-popup for proper interaction
|
|
tmux display-popup -E -w 50 -h 10 -T "Ops Logger" "bash '$popup_script'; rm -f '$popup_script'"
|
|
|
|
echo "[$(date)] display-popup completed" >> /tmp/ops-logger-prompt-debug.log
|
|
|
|
else
|
|
echo "[$(date)] Not in tmux, using direct prompt" >> /tmp/ops-logger-prompt-debug.log
|
|
|
|
# Direct shell - standard approach
|
|
echo "Start logging this shell?"
|
|
echo ""
|
|
echo " [Y]es - Start logging"
|
|
echo " [N]o - Skip logging"
|
|
echo ""
|
|
|
|
if read -t 10 -n 1 -p "Choice [Y/n]: " response; then
|
|
echo ""
|
|
if [[ -z "$response" || "${response,,}" == "y" ]]; then
|
|
start_logging
|
|
echo "Logging started!"
|
|
else
|
|
echo "Logging skipped."
|
|
fi
|
|
else
|
|
echo ""
|
|
echo "Timeout - logging skipped."
|
|
fi
|
|
fi
|
|
|
|
# Clean up will be handled by trap
|
|
echo "[$(date)] prompt_for_logging completed" >> /tmp/ops-logger-prompt-debug.log
|
|
}
|
|
|
|
# ================================================================
|
|
# TMUX INTEGRATION
|
|
# ================================================================
|
|
|
|
install_tmux_keys() {
|
|
load_config
|
|
|
|
if is_tmux; then
|
|
local script_path
|
|
script_path=$(readlink -f "$0")
|
|
|
|
# Bind keys
|
|
tmux bind-key L run-shell "bash -c '[ -f ~/.ops-logger.conf ] && tmux run-shell \"$script_path --toggle\" || tmux display-popup -E -w 80% -h 60% -T \"Ops Logger Config\" \"bash $script_path --toggle\"'"
|
|
tmux bind-key R run-shell "'$script_path' --toggle-recording"
|
|
|
|
# ✅ FIXED: Create a wrapper script that exports the pane context
|
|
if [[ "$PROMPT_NEW_SHELLS" == "true" ]]; then
|
|
local hook_wrapper="/tmp/ops-logger-pane-wrapper.sh"
|
|
cat > "$hook_wrapper" << EOWRAPPER
|
|
#!/bin/bash
|
|
# Wrapper that sets pane context and calls the prompt
|
|
|
|
# Sleep to let pane initialize
|
|
sleep 1
|
|
|
|
# Check config
|
|
if [ -f ~/.ops-logger.conf ]; then
|
|
source ~/.ops-logger.conf
|
|
if [ "\$PROMPT_NEW_SHELLS" = "true" ]; then
|
|
# Get pane info in tmux context
|
|
PANE_ID=\$(tmux display-message -p "#{session_name}-#{window_index}-#{pane_index}")
|
|
|
|
# Check if already logging
|
|
if [ ! -f "/tmp/ops-logger-active-\$PANE_ID" ]; then
|
|
# Export the tmux pane reference for the script to use
|
|
export TMUX_PANE=\$(tmux display-message -p "#{pane_id}")
|
|
|
|
# Call the script with the pane context
|
|
$script_path --prompt
|
|
fi
|
|
fi
|
|
fi
|
|
EOWRAPPER
|
|
chmod +x "$hook_wrapper"
|
|
|
|
tmux set-hook -g after-new-window "run-shell '$hook_wrapper'"
|
|
tmux set-hook -g after-split-window "run-shell '$hook_wrapper'"
|
|
echo "Hooks installed for new windows/panes"
|
|
fi
|
|
|
|
echo "Keys installed: $(get_tmux_prefix)+L (logging), $(get_tmux_prefix)+R (recording)"
|
|
log_debug "Tmux key bindings installed"
|
|
else
|
|
echo "Not in tmux, no keys installed"
|
|
fi
|
|
}
|
|
|
|
uninstall_tmux_keys() {
|
|
if is_tmux; then
|
|
tmux unbind-key L 2>/dev/null
|
|
tmux unbind-key R 2>/dev/null
|
|
tmux set-hook -gu after-new-window 2>/dev/null
|
|
tmux set-hook -gu after-split-window 2>/dev/null
|
|
echo "Tmux keys removed"
|
|
log_debug "Tmux key bindings removed"
|
|
|
|
# Remove our hook scripts
|
|
rm -f /tmp/ops-logger-hook-wrapper.sh 2>/dev/null
|
|
rm -f /tmp/ops-logger-tmux-hook.sh 2>/dev/null
|
|
rm -f /tmp/ops-logger-pane-wrapper.sh 2>/dev/null
|
|
fi
|
|
|
|
# Also remove config file as user expected
|
|
load_config
|
|
local log_dir_for_cleanup="$LOG_DIR"
|
|
|
|
# Remove config
|
|
[[ -f "$CONFIG_FILE" ]] && rm -f "$CONFIG_FILE"
|
|
|
|
# Remove debug log
|
|
[[ -f "${log_dir_for_cleanup}/ops-logger-debug.log" ]] && rm -f "${log_dir_for_cleanup}/ops-logger-debug.log"
|
|
|
|
echo "Configuration removed"
|
|
}
|
|
|
|
uninstall_all() {
|
|
local id=$(get_pane_id)
|
|
|
|
log_debug "Starting complete uninstall"
|
|
|
|
# Stop active sessions
|
|
is_logging_active "$id" && stop_logging
|
|
is_recording_active "$id" && stop_recording "$id"
|
|
|
|
# Remove tmux integration and config
|
|
is_tmux && uninstall_tmux_keys
|
|
|
|
# Clean up temp files
|
|
rm -f /tmp/ops-* 2>/dev/null
|
|
|
|
echo "OpsLogger uninstalled completely"
|
|
}
|
|
|
|
show_status() {
|
|
# Ensure we output to terminal if run from tmux keybinding
|
|
if is_tmux && [[ -t 1 ]]; then
|
|
local tty=$(tty 2>/dev/null || echo "/dev/tty")
|
|
exec 1>$tty 2>$tty
|
|
fi
|
|
|
|
load_config
|
|
local id=$(get_pane_id)
|
|
|
|
echo "Red Team Terminal Logger Status:"
|
|
echo "==============================="
|
|
echo "Target: $TARGET_NAME"
|
|
echo "Log directory: $LOG_DIR"
|
|
echo "Debug mode: $DEBUG"
|
|
echo "Logging: $(is_logging_active "$id" && echo "ACTIVE" || echo "INACTIVE")"
|
|
echo "Recording: $(is_recording_active "$id" && echo "ACTIVE" || echo "INACTIVE")"
|
|
echo "Public IP: $(get_public_ip)"
|
|
echo "Asciinema: $(command -v asciinema >/dev/null 2>&1 && echo "INSTALLED" || echo "NOT INSTALLED")"
|
|
|
|
if is_tmux; then
|
|
echo "Environment: tmux ($(tmux display -p '#{session_name}'))"
|
|
echo "Tmux prefix: $(get_tmux_prefix)"
|
|
echo "ohmytmux: $(tmux show-options -g | grep -q "TMUX_CONF" && echo "DETECTED" || echo "NOT DETECTED")"
|
|
echo "Normalized Pane ID: $id"
|
|
|
|
# Check plugin installation status
|
|
echo "TPM: $(check_tpm_installed && echo "INSTALLED" || echo "NOT INSTALLED")"
|
|
echo "tmux-logging plugin: $(check_tmux_logging_installed && echo "INSTALLED" || echo "NOT INSTALLED")"
|
|
|
|
# Check if there's active pipe-pane logging
|
|
echo "Active logging pipe: $(tmux list-panes -F "#{pane_id} #{pane_pipe}" | grep -q "$(get_tmux_pane_ref).*1" && echo "YES" || echo "NO")"
|
|
else
|
|
echo "Environment: direct shell"
|
|
fi
|
|
|
|
# Show current log files
|
|
local csv_log="${LOG_DIR}/${TARGET_NAME}_commands_$(date +%Y-%m-%d).csv"
|
|
|
|
[[ -f "$csv_log" ]] && {
|
|
echo "Current CSV log: $csv_log"
|
|
echo "Commands logged: $(($(wc -l < "$csv_log") - 1))"
|
|
}
|
|
|
|
# Show verbose logs with headers
|
|
local verbose_dir="${LOG_DIR}/verbose"
|
|
if [[ -d "$verbose_dir" ]]; then
|
|
local verbose_files=$(find "$verbose_dir" -name "*${TARGET_NAME}*.log" -type f 2>/dev/null | wc -l)
|
|
echo "Verbose log files: $verbose_files in $verbose_dir"
|
|
if [[ "$verbose_files" -gt 0 ]]; then
|
|
local latest_verbose=$(find "$verbose_dir" -name "*${TARGET_NAME}*.log" -type f -printf '%T@ %p\n' 2>/dev/null | sort -n | tail -1 | cut -d' ' -f2-)
|
|
[[ -n "$latest_verbose" ]] && echo "Latest verbose log: $latest_verbose ($(du -h "$latest_verbose" | cut -f1))"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
show_help() {
|
|
echo "Red Team Terminal Logger - Professional Solution v2.5.2"
|
|
echo "========================================================"
|
|
echo "USAGE: $0 [OPTIONS]"
|
|
echo ""
|
|
echo "LOGGING CONTROLS:"
|
|
echo " --start Start command/verbose logging"
|
|
echo " --stop Stop logging"
|
|
echo " --toggle Toggle logging on/off"
|
|
echo ""
|
|
echo "RECORDING CONTROLS:"
|
|
echo " --start-recording Start terminal recording (requires asciinema)"
|
|
echo " --stop-recording Stop recording"
|
|
echo " --toggle-recording Toggle recording on/off"
|
|
echo ""
|
|
echo "SETUP & CONFIGURATION:"
|
|
echo " --prompt Show logging prompt (for new shells)"
|
|
echo " --install Install tmux keybindings"
|
|
echo " --uninstall Remove tmux keybindings and config"
|
|
echo " --uninstall-all Complete removal (same as --uninstall)"
|
|
echo " --config Configure settings"
|
|
echo " --save-config Save config (internal use)"
|
|
echo ""
|
|
echo "INFO:"
|
|
echo " --status Show current status"
|
|
echo " --help Show this help"
|
|
echo ""
|
|
echo "DEBUG:"
|
|
echo " --debug-on Enable debug logging"
|
|
echo " --debug-off Disable debug logging"
|
|
echo ""
|
|
echo "TMUX INTEGRATION:"
|
|
echo " Keys: prefix+L (toggle logging), prefix+R (recording)"
|
|
echo " Compatible with ohmytmux themes and window naming"
|
|
echo " Auto-prompts for new windows/panes (configurable)"
|
|
echo ""
|
|
echo "VERSION 2.5.2 FIXES:"
|
|
echo " - FIXED: Verbose logs now include formatted command headers"
|
|
echo " - FIXED: Configuration uses interactive tmux windows"
|
|
echo " - FIXED: Command metadata properly captured and formatted"
|
|
echo " - IMPROVED: Better separation of capture and formatting"
|
|
}
|
|
|
|
# Main command handler with output handling
|
|
main() {
|
|
# Always ensure direct terminal output for tmux operations
|
|
if is_tmux && [[ -t 1 ]]; then
|
|
local tty=$(tty 2>/dev/null || echo "/dev/tty")
|
|
exec 1>$tty 2>$tty
|
|
fi
|
|
|
|
case "$1" in
|
|
--start) start_logging ;;
|
|
--stop) stop_logging ;;
|
|
--toggle) toggle_logging ;;
|
|
--start-recording) [[ -f "$CONFIG_FILE" ]] && load_config || { TARGET_NAME="$DEFAULT_TARGET"; LOG_DIR="$DEFAULT_LOG_DIR"; }; start_recording "$(get_pane_id)" "$TARGET_NAME" "$LOG_DIR" ;;
|
|
--stop-recording) stop_recording "$(get_pane_id)" ;;
|
|
--toggle-recording) toggle_recording ;;
|
|
--prompt) prompt_for_logging ;;
|
|
--install) install_tmux_keys ;;
|
|
--uninstall) uninstall_tmux_keys ;;
|
|
--uninstall-all) uninstall_all ;;
|
|
--config) create_config ;;
|
|
--save-config) save_config_from_tmux ;;
|
|
--status) show_status ;;
|
|
--debug-pipe)
|
|
load_config || { TARGET_NAME="$DEFAULT_TARGET"; LOG_DIR="$DEFAULT_LOG_DIR"; }
|
|
local id=$(get_pane_id)
|
|
local tmux_pane_ref=$(get_tmux_pane_ref)
|
|
start_debug_verbose_logging "$id" "$tmux_pane_ref" "$TARGET_NAME" "$LOG_DIR"
|
|
;;
|
|
--debug-on) [[ -f "$CONFIG_FILE" ]] && { load_config; DEBUG=true; save_config; } || echo "Run --config first to set up configuration"; echo "Debug logging enabled" ;;
|
|
--debug-off) [[ -f "$CONFIG_FILE" ]] && { load_config; DEBUG=false; save_config; } || echo "Run --config first to set up configuration"; echo "Debug logging disabled" ;;
|
|
--help|'') show_help ;;
|
|
*) echo "Unknown option: $1. Use --help" ;;
|
|
esac
|
|
}
|
|
|
|
# ShellOpsLog compatibility layer
|
|
if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then
|
|
export REDTEAM_LOGGER_PATH="${BASH_SOURCE[0]}"
|
|
|
|
start_operation_log() {
|
|
local auto_start=0
|
|
local log_dir="$HOME/OperationLogs"
|
|
|
|
while [ "$#" -gt 0 ]; do
|
|
case "$1" in
|
|
-AutoStart|-autostart) auto_start=1; shift ;;
|
|
*) log_dir="$1"; shift ;;
|
|
esac
|
|
done
|
|
|
|
[[ -f "$CONFIG_FILE" ]] && source "$CONFIG_FILE"
|
|
|
|
LOG_DIR="$log_dir"
|
|
TARGET_NAME="${TARGET_NAME:-$DEFAULT_TARGET}"
|
|
save_config
|
|
|
|
if [[ "$auto_start" -eq 1 ]]; then
|
|
"$REDTEAM_LOGGER_PATH" --start
|
|
else
|
|
"$REDTEAM_LOGGER_PATH" --prompt
|
|
fi
|
|
}
|
|
|
|
stop_operation_log() {
|
|
"$REDTEAM_LOGGER_PATH" --stop
|
|
}
|
|
|
|
log_debug "ShellOpsLog compatibility layer loaded"
|
|
else
|
|
# Script is being executed directly
|
|
main "$@"
|
|
fi
|