Files
chiefgeek a1a17e81a1 Sync all scripts from website downloads — 352 scripts total
Includes updated JS challenge scripts with Claude-User whitelist,
same-site referer bypass, Blackbox-Exporter allowed bot, and all
new exporters, cheat sheets, and automation scripts.
2026-05-25 03:31:08 +02:00

434 lines
18 KiB
Bash

#!/usr/bin/env bash
#########################################################################################
#### cron-lister.sh — List all cron jobs across users, system cron, and timers ####
#### Scans user crontabs, /etc/cron.*, systemd timers, and anacron ####
#### ####
#### Author: Phil Connor ####
#### Contact: contact@mylinux.work ####
#### License: MIT ####
#### Version 1.00 ####
#### ####
#### Usage: ####
#### ./cron-lister.sh ####
#### ./cron-lister.sh --format raw ####
#### ####
#### See --help for all options. ####
#########################################################################################
set -euo pipefail
# ── Defaults ──────────────────────────────────────────────────────────
FORMAT="${FORMAT:-table}"
VERBOSE="${VERBOSE:-false}"
COLOR="${COLOR:-auto}"
# ── State ─────────────────────────────────────────────────────────────
SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_NAME
COUNT_USER_CRONTAB=0
COUNT_SYSTEM_CRONTAB=0
COUNT_CRON_D=0
COUNT_CRON_DIRS=0
COUNT_SYSTEMD_TIMER=0
COUNT_ANACRON=0
# ── Colors ────────────────────────────────────────────────────────────
setup_colors() {
if [[ "$COLOR" == "never" ]]; then
GREEN="" YELLOW="" BLUE="" MAGENTA="" CYAN="" BOLD="" DIM="" RESET=""
return
fi
if [[ "$COLOR" == "always" ]] || [[ -t 1 ]]; then
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
MAGENTA='\033[0;35m'
CYAN='\033[0;36m'
BOLD='\033[1m'
DIM='\033[2m'
RESET='\033[0m'
else
GREEN="" YELLOW="" BLUE="" MAGENTA="" CYAN="" BOLD="" DIM="" RESET=""
fi
}
# ── Logging ───────────────────────────────────────────────────────────
log() { echo -e "${DIM}[INFO]${RESET} $*"; }
warn() { echo -e "${YELLOW}[WARN]${RESET} $*" >&2; }
verbose() { if [[ "$VERBOSE" == "true" ]]; then echo -e "${DIM}[DEBUG]${RESET} $*"; fi; }
# ── Helpers ───────────────────────────────────────────────────────────
section_header() {
echo ""
echo -e " ${BOLD}${CYAN}── $1 ──${RESET}"
echo ""
}
print_table_header() {
printf " ${BOLD}%-18s %-14s %-22s %s${RESET}\n" "SOURCE" "USER/UNIT" "SCHEDULE" "COMMAND"
printf " %s\n" "$(printf '%.0s─' {1..80})"
}
print_job() {
local source="$1"
local user="$2"
local schedule="$3"
local command="$4"
# Truncate long commands
if [[ ${#command} -gt 60 ]]; then
command="${command:0:57}..."
fi
if [[ "$FORMAT" == "raw" ]]; then
printf "%s\t%s\t%s\t%s\n" "$source" "$user" "$schedule" "$command"
return
fi
local color
case "$source" in
user-crontab) color="$GREEN" ;;
/etc/crontab) color="$BLUE" ;;
/etc/cron.d/*) color="$CYAN" ;;
cron.hourly|cron.daily|cron.weekly|cron.monthly) color="$MAGENTA" ;;
systemd-timer) color="$YELLOW" ;;
anacron) color="$DIM" ;;
*) color="" ;;
esac
printf " %b%-18s%b %-14s %-22s %s\n" "$color" "$source" "$RESET" "$user" "$schedule" "$command"
}
# ══════════════════════════════════════════════════════════════════════
# USER CRONTABS
# ══════════════════════════════════════════════════════════════════════
scan_user_crontabs() {
section_header "User Crontabs"
local crontab_dir="/var/spool/cron/crontabs"
local found=false
if [[ -d "$crontab_dir" ]] && [[ -r "$crontab_dir" ]]; then
while IFS= read -r crontab_file; do
[[ -z "$crontab_file" ]] && continue
found=true
local username
username=$(basename "$crontab_file")
verbose "Reading crontab for user: $username"
while IFS= read -r line; do
# Skip comments and empty lines
[[ -z "$line" || "$line" == "#"* || "$line" == "SHELL="* || "$line" == "PATH="* || "$line" == "MAILTO="* ]] && continue
local schedule cmd
schedule=$(echo "$line" | awk '{print $1, $2, $3, $4, $5}')
cmd=$(echo "$line" | awk '{for(i=6;i<=NF;i++) printf "%s ", $i; print ""}' | sed 's/ *$//')
print_job "user-crontab" "$username" "$schedule" "$cmd"
COUNT_USER_CRONTAB=$((COUNT_USER_CRONTAB + 1))
done < "$crontab_file"
done < <(find "$crontab_dir" -type f 2>/dev/null)
fi
if [[ "$found" == "false" ]]; then
verbose "No user crontabs found in $crontab_dir"
fi
}
# ══════════════════════════════════════════════════════════════════════
# SYSTEM CRONTAB
# ══════════════════════════════════════════════════════════════════════
scan_system_crontab() {
section_header "/etc/crontab"
if [[ ! -f /etc/crontab ]]; then
verbose "/etc/crontab not found"
return
fi
while IFS= read -r line; do
[[ -z "$line" || "$line" == "#"* || "$line" == "SHELL="* || "$line" == "PATH="* || "$line" == "MAILTO="* || "$line" == "HOME="* ]] && continue
local schedule user cmd
schedule=$(echo "$line" | awk '{print $1, $2, $3, $4, $5}')
user=$(echo "$line" | awk '{print $6}')
cmd=$(echo "$line" | awk '{for(i=7;i<=NF;i++) printf "%s ", $i; print ""}' | sed 's/ *$//')
if [[ -n "$cmd" ]]; then
print_job "/etc/crontab" "$user" "$schedule" "$cmd"
COUNT_SYSTEM_CRONTAB=$((COUNT_SYSTEM_CRONTAB + 1))
fi
done < /etc/crontab
}
# ══════════════════════════════════════════════════════════════════════
# /etc/cron.d
# ══════════════════════════════════════════════════════════════════════
scan_cron_d() {
section_header "/etc/cron.d"
if [[ ! -d /etc/cron.d ]]; then
verbose "/etc/cron.d not found"
return
fi
while IFS= read -r cron_file; do
[[ -z "$cron_file" ]] && continue
local filename
filename=$(basename "$cron_file")
# Skip dpkg and package manager files
[[ "$filename" == *.dpkg-* || "$filename" == *.ucf-* || "$filename" == "." || "$filename" == ".." ]] && continue
verbose "Reading /etc/cron.d/$filename"
while IFS= read -r line; do
[[ -z "$line" || "$line" == "#"* || "$line" == "SHELL="* || "$line" == "PATH="* || "$line" == "MAILTO="* || "$line" == "HOME="* ]] && continue
local schedule user cmd
schedule=$(echo "$line" | awk '{print $1, $2, $3, $4, $5}')
user=$(echo "$line" | awk '{print $6}')
cmd=$(echo "$line" | awk '{for(i=7;i<=NF;i++) printf "%s ", $i; print ""}' | sed 's/ *$//')
if [[ -n "$cmd" ]]; then
print_job "/etc/cron.d/$filename" "$user" "$schedule" "$cmd"
COUNT_CRON_D=$((COUNT_CRON_D + 1))
fi
done < "$cron_file"
done < <(find /etc/cron.d -maxdepth 1 -type f 2>/dev/null)
}
# ══════════════════════════════════════════════════════════════════════
# CRON DIRECTORIES
# ══════════════════════════════════════════════════════════════════════
scan_cron_dirs() {
section_header "Cron Directories"
local period
for period in hourly daily weekly monthly; do
local dir="/etc/cron.${period}"
if [[ ! -d "$dir" ]]; then
continue
fi
while IFS= read -r script; do
[[ -z "$script" ]] && continue
local script_name
script_name=$(basename "$script")
# Skip non-executable and package manager leftovers
[[ "$script_name" == *.dpkg-* || "$script_name" == *.ucf-* || "$script_name" == "." || "$script_name" == ".." ]] && continue
if [[ -x "$script" ]]; then
print_job "cron.${period}" "root" "$period" "$script_name"
COUNT_CRON_DIRS=$((COUNT_CRON_DIRS + 1))
else
verbose "Skipping non-executable: $script"
fi
done < <(find "$dir" -maxdepth 1 -type f 2>/dev/null)
done
}
# ══════════════════════════════════════════════════════════════════════
# SYSTEMD TIMERS
# ══════════════════════════════════════════════════════════════════════
scan_systemd_timers() {
section_header "Systemd Timers"
if ! command -v systemctl &>/dev/null; then
verbose "systemd not available"
return
fi
systemctl list-timers --all --no-legend --no-pager 2>/dev/null | while IFS= read -r line; do
[[ -z "$line" ]] && continue
local unit_name schedule_info
# Timer unit is the second-to-last field, schedule is NEXT + LEFT
unit_name=$(echo "$line" | awk '{print $(NF-1)}')
schedule_info=$(echo "$line" | awk '{print $1, $2, $3}')
if [[ -n "$unit_name" && "$unit_name" != "UNIT" ]]; then
# Get the trigger schedule from the timer unit
local on_calendar
on_calendar=$(systemctl show "$unit_name" --property=TimersCalendar 2>/dev/null | sed 's/TimersCalendar=//' | head -1)
if [[ -z "$on_calendar" || "$on_calendar" == "" ]]; then
on_calendar=$(systemctl show "$unit_name" --property=TimersMonotonic 2>/dev/null | sed 's/TimersMonotonic=//' | head -1)
fi
on_calendar="${on_calendar:-$schedule_info}"
# Truncate schedule if too long
if [[ ${#on_calendar} -gt 20 ]]; then
on_calendar="${on_calendar:0:17}..."
fi
print_job "systemd-timer" "$unit_name" "$on_calendar" "$(echo "$line" | awk '{print $NF}')"
COUNT_SYSTEMD_TIMER=$((COUNT_SYSTEMD_TIMER + 1))
fi
done
}
# ══════════════════════════════════════════════════════════════════════
# ANACRON
# ══════════════════════════════════════════════════════════════════════
scan_anacron() {
section_header "Anacron"
if [[ ! -f /etc/anacrontab ]]; then
verbose "/etc/anacrontab not found"
return
fi
while IFS= read -r line; do
[[ -z "$line" || "$line" == "#"* || "$line" == "SHELL="* || "$line" == "PATH="* || "$line" == "MAILTO="* || "$line" == "HOME="* || "$line" == "START_HOURS_RANGE="* || "$line" == "RANDOM_DELAY="* ]] && continue
local period delay ident cmd
period=$(echo "$line" | awk '{print $1}')
delay=$(echo "$line" | awk '{print $2}')
ident=$(echo "$line" | awk '{print $3}')
cmd=$(echo "$line" | awk '{for(i=4;i<=NF;i++) printf "%s ", $i; print ""}' | sed 's/ *$//')
if [[ -n "$cmd" ]]; then
print_job "anacron" "$ident" "every ${period}d +${delay}m" "$cmd"
COUNT_ANACRON=$((COUNT_ANACRON + 1))
fi
done < /etc/anacrontab
}
# ══════════════════════════════════════════════════════════════════════
# SUMMARY
# ══════════════════════════════════════════════════════════════════════
print_summary() {
local total=$((COUNT_USER_CRONTAB + COUNT_SYSTEM_CRONTAB + COUNT_CRON_D + COUNT_CRON_DIRS + COUNT_SYSTEMD_TIMER + COUNT_ANACRON))
echo ""
echo -e " ${BOLD}══════════════════════════════════════════${RESET}"
echo -e " ${BOLD}Summary${RESET}"
echo -e " ${BOLD}══════════════════════════════════════════${RESET}"
printf " %-22s %d\n" "User crontabs:" "$COUNT_USER_CRONTAB"
printf " %-22s %d\n" "/etc/crontab:" "$COUNT_SYSTEM_CRONTAB"
printf " %-22s %d\n" "/etc/cron.d:" "$COUNT_CRON_D"
printf " %-22s %d\n" "cron.{h,d,w,m}:" "$COUNT_CRON_DIRS"
printf " %-22s %d\n" "Systemd timers:" "$COUNT_SYSTEMD_TIMER"
printf " %-22s %d\n" "Anacron:" "$COUNT_ANACRON"
printf " %s\n" "$(printf '%.0s─' {1..30})"
printf " ${BOLD}%-22s %d${RESET}\n" "Total:" "$total"
echo ""
}
# ══════════════════════════════════════════════════════════════════════
# USAGE
# ══════════════════════════════════════════════════════════════════════
usage() {
cat <<EOF
${SCRIPT_NAME} — List all cron jobs across users, system cron, and systemd timers
USAGE:
${SCRIPT_NAME} [OPTIONS]
OPTIONS:
--format FORMAT Output format: table (default) or raw (tab-separated)
--verbose Enable debug output
--no-color Disable colored output
--help Show this help
SOURCES SCANNED:
/var/spool/cron/crontabs/ Per-user crontabs
/etc/crontab System crontab
/etc/cron.d/* Drop-in cron files
/etc/cron.{hourly,daily,weekly,monthly}/ Periodic scripts
systemd timers Active systemd timer units
/etc/anacrontab Anacron jobs
ENVIRONMENT VARIABLES:
FORMAT Output format (default: table)
COLOR Color mode: auto, always, never (default: auto)
EXAMPLES:
# List all cron jobs
sudo ./cron-lister.sh
# Raw output for parsing
sudo ./cron-lister.sh --format raw
# Pipe-friendly
sudo ./cron-lister.sh --no-color | tee /tmp/cron-audit.txt
EOF
}
# ══════════════════════════════════════════════════════════════════════
# ARGUMENT PARSING
# ══════════════════════════════════════════════════════════════════════
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--format)
FORMAT="$2"; shift 2 ;;
--verbose)
VERBOSE="true"; shift ;;
--no-color)
COLOR="never"; shift ;;
--help|-h)
setup_colors
usage
exit 0 ;;
*)
echo "Unknown option: $1" >&2
echo "Run ${SCRIPT_NAME} --help for usage" >&2
exit 1 ;;
esac
done
if [[ "$FORMAT" != "table" && "$FORMAT" != "raw" ]]; then
echo "Invalid format: $FORMAT (must be 'table' or 'raw')" >&2
exit 1
fi
}
# ══════════════════════════════════════════════════════════════════════
# MAIN
# ══════════════════════════════════════════════════════════════════════
main() {
parse_args "$@"
setup_colors
if [[ "$FORMAT" != "raw" ]]; then
echo ""
echo -e "${BOLD}Cron Job Lister — $(hostname -f 2>/dev/null || hostname)${RESET}"
echo -e "${DIM}$(date '+%Y-%m-%d %H:%M:%S %Z')${RESET}"
fi
if [[ "$FORMAT" == "table" ]]; then
echo ""
print_table_header
fi
scan_user_crontabs
scan_system_crontab
scan_cron_d
scan_cron_dirs
scan_systemd_timers
scan_anacron
if [[ "$FORMAT" != "raw" ]]; then
print_summary
fi
}
main "$@"