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.
This commit is contained in:
Executable
+620
@@ -0,0 +1,620 @@
|
||||
#!/bin/bash
|
||||
################################################################################
|
||||
# Script Name: systemd-timer-exporter.sh
|
||||
# Version: 1.1
|
||||
# Description: Prometheus exporter for systemd timers providing comprehensive
|
||||
# metrics for monitoring timer status, scheduling, associated
|
||||
# service results, and health detection
|
||||
#
|
||||
# Author: Phil Connor
|
||||
# Contact: contact@mylinux.work
|
||||
# Website: https://mylinux.work
|
||||
# License: MIT
|
||||
#
|
||||
# Prerequisites:
|
||||
# - systemd-based system with systemctl
|
||||
# - netcat (nc) for HTTP mode
|
||||
# - Standard Unix tools (grep, awk, sort)
|
||||
#
|
||||
# Usage:
|
||||
# # Output to stdout
|
||||
# ./systemd-timer-exporter.sh
|
||||
#
|
||||
# # HTTP server mode
|
||||
# ./systemd-timer-exporter.sh --http -p 9199
|
||||
#
|
||||
# # Textfile collector mode
|
||||
# ./systemd-timer-exporter.sh --textfile
|
||||
#
|
||||
# Metrics Exported:
|
||||
# Core Status:
|
||||
# - systemd_timer_up - Exporter status (1=up, 0=down)
|
||||
# - systemd_timer_exporter_info{version} - Exporter info
|
||||
#
|
||||
# Timer Counts:
|
||||
# - systemd_timer_count_total - Total number of timers
|
||||
# - systemd_timer_count_by_state{state} - Count per state
|
||||
#
|
||||
# Per-Timer Metrics:
|
||||
# - systemd_timer_active{timer} - 1 if active, 0 if not
|
||||
# - systemd_timer_next_trigger_timestamp{timer} - Next trigger unix timestamp
|
||||
# - systemd_timer_last_trigger_timestamp{timer} - Last trigger unix timestamp
|
||||
# - systemd_timer_seconds_until_next{timer} - Seconds until next trigger
|
||||
# - systemd_timer_seconds_since_last{timer} - Seconds since last trigger
|
||||
#
|
||||
# Associated Service Metrics:
|
||||
# - systemd_timer_service_result{timer,service,result} - Service result
|
||||
# - systemd_timer_service_exit_code{timer,service} - Exit code of last run
|
||||
# - systemd_timer_service_duration_seconds{timer,service} - Last run duration
|
||||
#
|
||||
# Health:
|
||||
# - systemd_timer_overdue_count - Timers past their next trigger time
|
||||
# - systemd_timer_failed_service_count - Associated services in failed state
|
||||
#
|
||||
# Exporter:
|
||||
# - systemd_timer_exporter_duration_seconds - Script execution time
|
||||
# - systemd_timer_exporter_last_run_timestamp - Last run unix timestamp
|
||||
#
|
||||
# Configuration:
|
||||
# Default HTTP port: 9199
|
||||
# Textfile directory: /var/lib/node_exporter
|
||||
#
|
||||
################################################################################
|
||||
|
||||
# ============================================================================
|
||||
# CONFIGURATION VARIABLES
|
||||
# ============================================================================
|
||||
|
||||
TEXTFILE_DIR="/var/lib/node_exporter"
|
||||
OUTPUT_FILE=""
|
||||
HTTP_MODE=false
|
||||
HTTP_PORT=9199
|
||||
|
||||
# ============================================================================
|
||||
# HELPER FUNCTIONS
|
||||
# ============================================================================
|
||||
|
||||
show_usage() {
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTIONS]
|
||||
|
||||
Export systemd timer statistics as Prometheus metrics (v1.0).
|
||||
|
||||
MODES:
|
||||
--textfile Write to node_exporter textfile collector
|
||||
--http Run HTTP server on port $HTTP_PORT
|
||||
|
||||
OPTIONS:
|
||||
-p, --port HTTP port (default: 9199)
|
||||
-o, --output Output file path
|
||||
|
||||
EXAMPLES:
|
||||
$0 --textfile # Write to textfile collector
|
||||
$0 --http --port 9199 # Run HTTP server
|
||||
$0 -o /tmp/systemd_timers.prom # Write to custom file
|
||||
|
||||
METRICS:
|
||||
- Timer counts and states (active/inactive/failed)
|
||||
- Per-timer scheduling (next/last trigger timestamps)
|
||||
- Associated service results and exit codes
|
||||
- Service execution duration
|
||||
- Overdue and failed timer detection
|
||||
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-h|--help) show_usage ;;
|
||||
--textfile) OUTPUT_FILE="$TEXTFILE_DIR/systemd_timers.prom"; shift ;;
|
||||
--http) HTTP_MODE=true; shift ;;
|
||||
-p|--port) HTTP_PORT="$2"; shift 2 ;;
|
||||
-o|--output) OUTPUT_FILE="$2"; shift 2 ;;
|
||||
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Verify systemctl is available and systemd is running
|
||||
# Returns: 0 if systemd is available, 1 otherwise
|
||||
check_systemd() {
|
||||
if ! command -v systemctl >/dev/null 2>&1; then
|
||||
return 1
|
||||
fi
|
||||
# is-system-running returns "degraded" (exit 1) when any unit has failed,
|
||||
# which is normal on most production servers. Only fail if systemd is
|
||||
# genuinely unavailable (offline, unknown, etc.)
|
||||
local state
|
||||
state=$(systemctl is-system-running 2>/dev/null || true)
|
||||
case "$state" in
|
||||
running|degraded|starting|stopping) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Get list of all timer unit names
|
||||
# Returns: One timer unit name per line
|
||||
get_timer_list() {
|
||||
systemctl list-timers --all --no-pager --no-legend 2>/dev/null | \
|
||||
awk '{for(i=1;i<=NF;i++) if($i ~ /\.timer$/) {print $i; break}}'
|
||||
}
|
||||
|
||||
# Get the state of a timer unit (active/inactive/failed)
|
||||
# Args: $1 - timer unit name
|
||||
# Returns: State string
|
||||
get_timer_state() {
|
||||
local timer="$1"
|
||||
local state
|
||||
state=$(systemctl show "$timer" --property=ActiveState --value 2>/dev/null)
|
||||
echo "${state:-inactive}"
|
||||
}
|
||||
|
||||
# Get unix timestamp of next trigger for a timer
|
||||
# Args: $1 - timer unit name
|
||||
# Returns: Unix timestamp (0 if unavailable)
|
||||
get_timer_next_trigger() {
|
||||
local timer="$1"
|
||||
local usec
|
||||
usec=$(systemctl show "$timer" --property=NextElapseUSecRealtime --value 2>/dev/null)
|
||||
if [ -z "$usec" ] || [ "$usec" = "0" ]; then
|
||||
echo "0"
|
||||
return
|
||||
fi
|
||||
echo "$(( usec / 1000000 ))"
|
||||
}
|
||||
|
||||
# Get unix timestamp of last trigger for a timer
|
||||
# Args: $1 - timer unit name
|
||||
# Returns: Unix timestamp (0 if unavailable)
|
||||
get_timer_last_trigger() {
|
||||
local timer="$1"
|
||||
local usec
|
||||
usec=$(systemctl show "$timer" --property=LastTriggerUSec --value 2>/dev/null)
|
||||
if [ -z "$usec" ] || [ "$usec" = "0" ]; then
|
||||
echo "0"
|
||||
return
|
||||
fi
|
||||
echo "$(( usec / 1000000 ))"
|
||||
}
|
||||
|
||||
# Get the result of the associated service unit
|
||||
# Args: $1 - timer unit name
|
||||
# Returns: Result string (success/failed/exit-code/timeout)
|
||||
get_associated_service_result() {
|
||||
local timer="$1"
|
||||
local service="${timer%.timer}.service"
|
||||
local result
|
||||
result=$(systemctl show "$service" --property=Result --value 2>/dev/null)
|
||||
echo "${result:-success}"
|
||||
}
|
||||
|
||||
# Get the exit code of the associated service unit
|
||||
# Args: $1 - timer unit name
|
||||
# Returns: Exit code integer
|
||||
get_associated_service_exit_code() {
|
||||
local timer="$1"
|
||||
local service="${timer%.timer}.service"
|
||||
local code
|
||||
code=$(systemctl show "$service" --property=ExecMainStatus --value 2>/dev/null)
|
||||
echo "${code:-0}"
|
||||
}
|
||||
|
||||
# Get the last execution duration of the associated service in seconds
|
||||
# Args: $1 - timer unit name
|
||||
# Returns: Duration in seconds (0 if unavailable)
|
||||
get_associated_service_runtime() {
|
||||
local timer="$1"
|
||||
local service="${timer%.timer}.service"
|
||||
local start_usec
|
||||
local exit_usec
|
||||
start_usec=$(systemctl show "$service" --property=ExecMainStartTimestampMonotonic --value 2>/dev/null)
|
||||
exit_usec=$(systemctl show "$service" --property=ExecMainExitTimestampMonotonic --value 2>/dev/null)
|
||||
if [ -z "$start_usec" ] || [ "$start_usec" = "0" ] || [ -z "$exit_usec" ] || [ "$exit_usec" = "0" ]; then
|
||||
echo "0"
|
||||
return
|
||||
fi
|
||||
echo "$(( (exit_usec - start_usec) / 1000000 ))"
|
||||
}
|
||||
|
||||
# Get count of timers where NextElapseUSecRealtime is in the past
|
||||
# Returns: Number of overdue timers
|
||||
get_overdue_timers() {
|
||||
local now
|
||||
now=$(date +%s)
|
||||
local count=0
|
||||
local timer next_ts
|
||||
while read -r timer; do
|
||||
[ -z "$timer" ] && continue
|
||||
next_ts=$(get_timer_next_trigger "$timer")
|
||||
if [ "$next_ts" -gt 0 ] && [ "$next_ts" -lt "$now" ]; then
|
||||
count=$((count + 1))
|
||||
fi
|
||||
done < <(get_timer_list)
|
||||
echo "$count"
|
||||
}
|
||||
|
||||
# Count timers by state (active, inactive, failed)
|
||||
# Args: $1 - state to count
|
||||
# Returns: Count of timers in that state
|
||||
count_timers_by_state() {
|
||||
local target_state="$1"
|
||||
local count=0
|
||||
local timer state
|
||||
while read -r timer; do
|
||||
[ -z "$timer" ] && continue
|
||||
state=$(get_timer_state "$timer")
|
||||
if [ "$state" = "$target_state" ]; then
|
||||
count=$((count + 1))
|
||||
fi
|
||||
done < <(get_timer_list)
|
||||
echo "$count"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# METRIC GENERATION
|
||||
# ============================================================================
|
||||
|
||||
# Generate all Prometheus metrics
|
||||
# Returns: Prometheus text format metrics on stdout
|
||||
generate_metrics() {
|
||||
local script_start
|
||||
script_start=$(date +%s)
|
||||
|
||||
# Check systemd availability
|
||||
if ! check_systemd; then
|
||||
cat <<EOF
|
||||
# HELP systemd_timer_up Systemd timer exporter status
|
||||
# TYPE systemd_timer_up gauge
|
||||
systemd_timer_up 0
|
||||
EOF
|
||||
return
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
# HELP systemd_timer_up Systemd timer exporter status
|
||||
# TYPE systemd_timer_up gauge
|
||||
systemd_timer_up 1
|
||||
|
||||
# HELP systemd_timer_exporter_info Systemd timer exporter information
|
||||
# TYPE systemd_timer_exporter_info gauge
|
||||
systemd_timer_exporter_info{version="1.0"} 1
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
|
||||
# ========================================================================
|
||||
# Timer Counts
|
||||
# ========================================================================
|
||||
|
||||
# Cache timer list for reuse
|
||||
local timer_cache
|
||||
timer_cache=$(get_timer_list)
|
||||
|
||||
local total_timers
|
||||
total_timers=$(echo "$timer_cache" | grep -c '.' 2>/dev/null)
|
||||
total_timers=${total_timers:-0}
|
||||
|
||||
# Pre-compute states for all timers (avoids repeated systemctl calls)
|
||||
declare -A timer_states
|
||||
local timer state
|
||||
while read -r timer; do
|
||||
[ -z "$timer" ] && continue
|
||||
state=$(get_timer_state "$timer")
|
||||
timer_states["$timer"]="$state"
|
||||
done <<< "$timer_cache"
|
||||
|
||||
local active_count=0
|
||||
local inactive_count=0
|
||||
local failed_count=0
|
||||
for timer in "${!timer_states[@]}"; do
|
||||
case "${timer_states[$timer]}" in
|
||||
active) active_count=$((active_count + 1)) ;;
|
||||
inactive) inactive_count=$((inactive_count + 1)) ;;
|
||||
failed) failed_count=$((failed_count + 1)) ;;
|
||||
esac
|
||||
done
|
||||
|
||||
cat <<EOF
|
||||
# HELP systemd_timer_count_total Total number of systemd timers
|
||||
# TYPE systemd_timer_count_total gauge
|
||||
systemd_timer_count_total $total_timers
|
||||
|
||||
# HELP systemd_timer_count_by_state Number of timers per state
|
||||
# TYPE systemd_timer_count_by_state gauge
|
||||
systemd_timer_count_by_state{state="active"} $active_count
|
||||
systemd_timer_count_by_state{state="inactive"} $inactive_count
|
||||
systemd_timer_count_by_state{state="failed"} $failed_count
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
|
||||
# ========================================================================
|
||||
# Per-Timer Metrics
|
||||
# ========================================================================
|
||||
cat <<EOF
|
||||
# HELP systemd_timer_active Whether the timer is active (1=active, 0=not active)
|
||||
# TYPE systemd_timer_active gauge
|
||||
EOF
|
||||
|
||||
while read -r timer; do
|
||||
[ -z "$timer" ] && continue
|
||||
local is_active=0
|
||||
if [ "${timer_states[$timer]}" = "active" ]; then
|
||||
is_active=1
|
||||
fi
|
||||
echo "systemd_timer_active{timer=\"$timer\"} $is_active"
|
||||
done <<< "$timer_cache"
|
||||
|
||||
echo ""
|
||||
|
||||
local now
|
||||
now=$(date +%s)
|
||||
|
||||
cat <<EOF
|
||||
# HELP systemd_timer_next_trigger_timestamp Unix timestamp of next trigger
|
||||
# TYPE systemd_timer_next_trigger_timestamp gauge
|
||||
EOF
|
||||
|
||||
while read -r timer; do
|
||||
[ -z "$timer" ] && continue
|
||||
local next_ts
|
||||
next_ts=$(get_timer_next_trigger "$timer")
|
||||
echo "systemd_timer_next_trigger_timestamp{timer=\"$timer\"} ${next_ts:-0}"
|
||||
done <<< "$timer_cache"
|
||||
|
||||
echo ""
|
||||
|
||||
cat <<EOF
|
||||
# HELP systemd_timer_last_trigger_timestamp Unix timestamp of last trigger
|
||||
# TYPE systemd_timer_last_trigger_timestamp gauge
|
||||
EOF
|
||||
|
||||
while read -r timer; do
|
||||
[ -z "$timer" ] && continue
|
||||
local last_ts
|
||||
last_ts=$(get_timer_last_trigger "$timer")
|
||||
echo "systemd_timer_last_trigger_timestamp{timer=\"$timer\"} ${last_ts:-0}"
|
||||
done <<< "$timer_cache"
|
||||
|
||||
echo ""
|
||||
|
||||
cat <<EOF
|
||||
# HELP systemd_timer_seconds_until_next Seconds until next trigger (negative means overdue)
|
||||
# TYPE systemd_timer_seconds_until_next gauge
|
||||
EOF
|
||||
|
||||
while read -r timer; do
|
||||
[ -z "$timer" ] && continue
|
||||
local next_ts
|
||||
next_ts=$(get_timer_next_trigger "$timer")
|
||||
local until_next=0
|
||||
if [ "${next_ts:-0}" -gt 0 ]; then
|
||||
until_next=$((next_ts - now))
|
||||
fi
|
||||
echo "systemd_timer_seconds_until_next{timer=\"$timer\"} $until_next"
|
||||
done <<< "$timer_cache"
|
||||
|
||||
echo ""
|
||||
|
||||
cat <<EOF
|
||||
# HELP systemd_timer_seconds_since_last Seconds since last trigger
|
||||
# TYPE systemd_timer_seconds_since_last gauge
|
||||
EOF
|
||||
|
||||
while read -r timer; do
|
||||
[ -z "$timer" ] && continue
|
||||
local last_ts
|
||||
last_ts=$(get_timer_last_trigger "$timer")
|
||||
local since_last=0
|
||||
if [ "${last_ts:-0}" -gt 0 ]; then
|
||||
since_last=$((now - last_ts))
|
||||
fi
|
||||
echo "systemd_timer_seconds_since_last{timer=\"$timer\"} $since_last"
|
||||
done <<< "$timer_cache"
|
||||
|
||||
echo ""
|
||||
|
||||
# ========================================================================
|
||||
# Associated Service Metrics
|
||||
# ========================================================================
|
||||
cat <<EOF
|
||||
# HELP systemd_timer_service_result Result of the associated service (1 for current result)
|
||||
# TYPE systemd_timer_service_result gauge
|
||||
EOF
|
||||
|
||||
while read -r timer; do
|
||||
[ -z "$timer" ] && continue
|
||||
local service="${timer%.timer}.service"
|
||||
local result
|
||||
result=$(get_associated_service_result "$timer")
|
||||
result=${result:-success}
|
||||
echo "systemd_timer_service_result{timer=\"$timer\",service=\"$service\",result=\"$result\"} 1"
|
||||
done <<< "$timer_cache"
|
||||
|
||||
echo ""
|
||||
|
||||
cat <<EOF
|
||||
# HELP systemd_timer_service_exit_code Exit code of the last service run
|
||||
# TYPE systemd_timer_service_exit_code gauge
|
||||
EOF
|
||||
|
||||
while read -r timer; do
|
||||
[ -z "$timer" ] && continue
|
||||
local service="${timer%.timer}.service"
|
||||
local exit_code
|
||||
exit_code=$(get_associated_service_exit_code "$timer")
|
||||
echo "systemd_timer_service_exit_code{timer=\"$timer\",service=\"$service\"} ${exit_code:-0}"
|
||||
done <<< "$timer_cache"
|
||||
|
||||
echo ""
|
||||
|
||||
cat <<EOF
|
||||
# HELP systemd_timer_service_duration_seconds Duration of the last service execution in seconds
|
||||
# TYPE systemd_timer_service_duration_seconds gauge
|
||||
EOF
|
||||
|
||||
while read -r timer; do
|
||||
[ -z "$timer" ] && continue
|
||||
local service="${timer%.timer}.service"
|
||||
local duration
|
||||
duration=$(get_associated_service_runtime "$timer")
|
||||
echo "systemd_timer_service_duration_seconds{timer=\"$timer\",service=\"$service\"} ${duration:-0}"
|
||||
done <<< "$timer_cache"
|
||||
|
||||
echo ""
|
||||
|
||||
# ========================================================================
|
||||
# Health
|
||||
# ========================================================================
|
||||
|
||||
local overdue_count=0
|
||||
local failed_service_count=0
|
||||
while read -r timer; do
|
||||
[ -z "$timer" ] && continue
|
||||
local next_ts
|
||||
next_ts=$(get_timer_next_trigger "$timer")
|
||||
if [ "${next_ts:-0}" -gt 0 ] && [ "$next_ts" -lt "$now" ]; then
|
||||
overdue_count=$((overdue_count + 1))
|
||||
fi
|
||||
local result
|
||||
result=$(get_associated_service_result "$timer")
|
||||
if [ "$result" = "failed" ]; then
|
||||
failed_service_count=$((failed_service_count + 1))
|
||||
fi
|
||||
done <<< "$timer_cache"
|
||||
|
||||
cat <<EOF
|
||||
# HELP systemd_timer_overdue_count Number of timers past their next trigger time
|
||||
# TYPE systemd_timer_overdue_count gauge
|
||||
systemd_timer_overdue_count $overdue_count
|
||||
|
||||
# HELP systemd_timer_failed_service_count Number of associated services in failed state
|
||||
# TYPE systemd_timer_failed_service_count gauge
|
||||
systemd_timer_failed_service_count $failed_service_count
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
|
||||
# ========================================================================
|
||||
# Exporter
|
||||
# ========================================================================
|
||||
local script_end script_duration
|
||||
script_end=$(date +%s)
|
||||
script_duration=$((script_end - script_start))
|
||||
|
||||
cat <<EOF
|
||||
# HELP systemd_timer_exporter_duration_seconds Time to generate all metrics
|
||||
# TYPE systemd_timer_exporter_duration_seconds gauge
|
||||
systemd_timer_exporter_duration_seconds $script_duration
|
||||
|
||||
# HELP systemd_timer_exporter_last_run_timestamp Unix timestamp of last successful run
|
||||
# TYPE systemd_timer_exporter_last_run_timestamp gauge
|
||||
systemd_timer_exporter_last_run_timestamp $script_end
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# HTTP SERVER MODE
|
||||
# ============================================================================
|
||||
|
||||
# Run simple HTTP server using netcat
|
||||
# Serves metrics on /metrics endpoint
|
||||
run_http_server() {
|
||||
echo "Starting systemd timer exporter on port $HTTP_PORT..." >&2
|
||||
|
||||
if ! command -v nc >/dev/null 2>&1; then
|
||||
echo "ERROR: netcat (nc) required for HTTP mode" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Infinite loop accepting HTTP requests
|
||||
while true; do
|
||||
{
|
||||
read -r request
|
||||
# Check if request is for /metrics endpoint
|
||||
if [[ "$request" =~ ^GET\ /metrics ]]; then
|
||||
echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/plain; version=0.0.4\r\n\r"
|
||||
generate_metrics
|
||||
else # Serve HTML landing page for other requests
|
||||
echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r"
|
||||
cat <<EOF
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Systemd Timer Exporter v1.0</title></head>
|
||||
<body>
|
||||
<h1>Systemd Timer Exporter v1.0</h1>
|
||||
<p><a href="/metrics">Metrics</a></p>
|
||||
<h2>Metric Categories</h2>
|
||||
<ul>
|
||||
<li>Core Status: exporter up/down, version info</li>
|
||||
<li>Timer Counts: total timers, count by state (active/inactive/failed)</li>
|
||||
<li>Per-Timer Metrics: active state, next/last trigger timestamps, time until/since trigger</li>
|
||||
<li>Associated Service Metrics: service result, exit code, execution duration</li>
|
||||
<li>Health: overdue timers, failed service count</li>
|
||||
<li>Exporter: script duration, last run timestamp</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
fi
|
||||
} | nc -l -p "$HTTP_PORT" -q 1 2>/dev/null # -q 1: wait 1 second after EOF before closing
|
||||
done
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# MAIN EXECUTION
|
||||
# ============================================================================
|
||||
|
||||
# Main entry point - routes to appropriate output mode
|
||||
main() {
|
||||
parse_args "$@"
|
||||
|
||||
if [ "$HTTP_MODE" = true ]; then
|
||||
# Run HTTP server (blocks until killed)
|
||||
run_http_server
|
||||
elif [ -n "$OUTPUT_FILE" ]; then
|
||||
# Textfile collector mode: write atomically using temp file
|
||||
local output_dir
|
||||
output_dir="$(dirname "$OUTPUT_FILE")"
|
||||
mkdir -p "$output_dir"
|
||||
|
||||
# Create temp file in SAME directory for atomic rename (same filesystem)
|
||||
local temp_file
|
||||
temp_file=$(mktemp "${output_dir}/.systemd_timer_metrics.XXXXXX")
|
||||
|
||||
# Generate metrics to temp file
|
||||
if ! generate_metrics > "$temp_file" 2>/dev/null; then
|
||||
rm -f "$temp_file"
|
||||
echo "ERROR: Failed to generate metrics" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate: file must exist, have content, and contain enough metric lines
|
||||
local file_lines
|
||||
file_lines=$(wc -l < "$temp_file" 2>/dev/null || echo 0)
|
||||
|
||||
if [ "$file_lines" -lt 10 ]; then
|
||||
rm -f "$temp_file"
|
||||
echo "ERROR: Metrics file too small ($file_lines lines), keeping previous" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Set permissions before move
|
||||
chmod 644 "$temp_file"
|
||||
|
||||
# Atomic rename - no gap where file is missing
|
||||
mv -f "$temp_file" "$OUTPUT_FILE"
|
||||
|
||||
echo "Metrics written to $OUTPUT_FILE ($file_lines lines)" >&2
|
||||
else
|
||||
# Default: output to stdout
|
||||
generate_metrics
|
||||
fi
|
||||
}
|
||||
|
||||
# Execute main function with all script arguments
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user