Comprehensive Script Analysis: Process Monitoring and Management System
#!/bin/bash
# ------------------------------------------------------------
# Optimized script to check processes
# ------------------------------------------------------------
# RETURN CODES:
# 0: No processes running
# 1: One process running (CPU time < threshold)
# 2: One process running (CPU time >= threshold)
# 3: Multiple processes running
# ------------------------------------------------------------
# Default configuration
PS_NAME="<process_name>"
CPU_MAX="000100" # HHMMSS format
CONFIG_FILE="/etc/processcheck.conf"
VERBOSE=0
LOG_FILE="/var/log/processcheck.log"
# Process command line arguments
while getopts "vc:l:" opt; do
case $opt in
v) VERBOSE=1 ;;
c) CONFIG_FILE="$OPTARG" ;;
l) LOG_FILE="$OPTARG" ;;
?) echo "Usage: $0 [-v] [-c config_file] [-l log_file]" >&2; exit 1 ;;
esac
done
# Load configuration file if it exists
if [ -f "$CONFIG_FILE" ]; then
debug "Loading configuration from $CONFIG_FILE"
source "$CONFIG_FILE"
fi
# Make constants readonly after config load
readonly PS_NAME CPU_MAX GREP_OPTS
# Signal handling
trap cleanup INT TERM EXIT
# Logging function
log() {
local message="[$(date +'%Y-%m-%d %H:%M:%S')] $*"
echo "$message" | tee -a "$LOG_FILE" >&2
}
# Debug logging function
debug() {
if [ "$VERBOSE" -eq 1 ]; then
log "[DEBUG] $*"
fi
}
# Cleanup function
cleanup() {
local exit_code=$?
debug "Cleaning up and exiting with code $exit_code"
# Add any cleanup tasks here
exit "$exit_code"
}
# More efficient process counting using pgrep
get_process_count() {
local count
count=$(pgrep -c -f "$PS_NAME" 2>/dev/null || echo 0)
debug "Process count: $count"
echo "$count"
}
# Get CPU time of single process more efficiently
get_cpu_time() {
local cpu_time
cpu_time=$(ps -o time= -p "$(pgrep -f "$PS_NAME")" 2>/dev/null | \
awk '{ split($1,a,":"); print a[1]*3600 + a[2]*60 + a[3] }')
debug "CPU time: $cpu_time seconds"
echo "$cpu_time"
}
# Convert HHMMSS to seconds for comparison
convert_hhmmss_to_seconds() {
local time=$1
local seconds
seconds=$((
10#${time:0:2} * 3600 + # Hours
10#${time:2:2} * 60 + # Minutes
10#${time:4:2} # Seconds
))
debug "Converted $time to $seconds seconds"
echo "$seconds"
}
# Check if log directory exists and is writable
check_log_directory() {
local log_dir
log_dir=$(dirname "$LOG_FILE")
if [ ! -d "$log_dir" ]; then
mkdir -p "$log_dir" || {
echo "Error: Cannot create log directory $log_dir" >&2
exit 1
}
fi
if [ ! -w "$log_dir" ]; then
echo "Error: Log directory $log_dir is not writable" >&2
exit 1
}
}
main() {
debug "Starting process check for $PS_NAME"
check_log_directory
local count
count=$(get_process_count)
case $count in
0)
log "[RET-CODE:0] processes are not running."
exit 0
;;
1)
local cpu_time max_seconds
cpu_time=$(get_cpu_time)
max_seconds=$(convert_hhmmss_to_seconds "$CPU_MAX")
debug "Comparing CPU time $cpu_time with max allowed $max_seconds"
if [ "$cpu_time" -lt "$max_seconds" ]; then
log "[RET-CODE:1] one process is running. (CPU time < configured value)"
exit 1
else
log "[RET-CODE:2] one process is running. (CPU time >= configured value)"
exit 2
fi
;;
*)
log "[RET-CODE:3] more than two processes are running."
exit 3
;;
esac
}
# Example configuration file format (/etc/processcheck.conf):
# PS_NAME="myprocess"
# CPU_MAX="001500" # 15 minutes
# LOG_FILE="/var/log/custom/processcheck.log"
# Run main function
main "$@"
Let me break down each major component and explain its purpose and functionality:
1. Script Header and Configuration Management
# Default configuration
PS_NAME="<process_name>"
CPU_MAX="000100" # HHMMSS format
CONFIG_FILE="/etc/processcheck.conf"
VERBOSE=0
LOG_FILE="/var/log/processcheck.log"
- Sets default values for essential parameters
- Allows for easy configuration changes
- Defines paths for config and log files
2. Command Line Argument Processing
while getopts "vc:l:" opt; do
case $opt in
v) VERBOSE=1 ;;
c) CONFIG_FILE="$OPTARG" ;;
l) LOG_FILE="$OPTARG" ;;
?) echo "Usage: $0 [-v] [-c config_file] [-l log_file]" >&2; exit 1 ;;
esac
done
- Processes command-line flags:
-v: Enables verbose mode-c: Specifies custom config file-l: Specifies custom log file
- Provides usage information on invalid input
3. Configuration File Handling
if [ -f "$CONFIG_FILE" ]; then
debug "Loading configuration from $CONFIG_FILE"
source "$CONFIG_FILE"
fi
- Checks for existence of config file
- Loads custom configurations if available
- Uses default values if no config file exists
4. Signal Management
trap cleanup INT TERM EXIT
cleanup() {
local exit_code=$?
debug "Cleaning up and exiting with code $exit_code"
exit "$exit_code"
}
- Handles script interruption gracefully
- Captures system signals (SIGINT, SIGTERM)
- Performs cleanup before exit
- Preserves original exit codes
5. Logging System
log() {
local message="[$(date +'%Y-%m-%d %H:%M:%S')] $*"
echo "$message" | tee -a "$LOG_FILE" >&2
}
debug() {
if [ "$VERBOSE" -eq 1 ]; then
log "[DEBUG] $*"
fi
}
- Two-tier logging system:
- Standard logging for important events
- Debug logging for detailed information
- Timestamps all log entries
- Writes to both console and log file
- Conditional debug output based on verbose mode
6. Process Management Functions
get_process_count() {
local count
count=$(pgrep -c -f "$PS_NAME" 2>/dev/null || echo 0)
debug "Process count: $count"
echo "$count"
}
get_cpu_time() {
local cpu_time
cpu_time=$(ps -o time= -p "$(pgrep -f "$PS_NAME")" 2>/dev/null | \
awk '{ split($1,a,":"); print a[1]*3600 + a[2]*60 + a[3] }')
debug "CPU time: $cpu_time seconds"
echo "$cpu_time"
}
- Efficient process counting using
pgrep - Accurate CPU time calculation
- Error handling for process operations
- Debug logging of process information
7. Time Conversion Utility
convert_hhmmss_to_seconds() {
local time=$1
local seconds
seconds=$((
10#${time:0:2} * 3600 + # Hours
10#${time:2:2} * 60 + # Minutes
10#${time:4:2} # Seconds
))
debug "Converted $time to $seconds seconds"
echo "$seconds"
}
- Converts HHMMSS format to seconds
- Handles base-10 conversion properly
- Supports time comparison operations
- Logs conversion for debugging
8. Log Directory Management
check_log_directory() {
local log_dir
log_dir=$(dirname "$LOG_FILE")
if [ ! -d "$log_dir" ]; then
mkdir -p "$log_dir" || {
echo "Error: Cannot create log directory $log_dir" >&2
exit 1
}
fi
if [ ! -w "$log_dir" ]; then
echo "Error: Log directory $log_dir is not writable" >&2
exit 1
}
}
- Ensures log directory exists
- Creates directory if missing
- Checks write permissions
- Fails gracefully with error messages
9. Main Process Logic
main() {
debug "Starting process check for $PS_NAME"
check_log_directory
local count
count=$(get_process_count)
case $count in
0) # No processes
1) # Single process with CPU time check
*) # Multiple processes
esac
}
- Orchestrates overall script execution
- Implements core business logic
- Handles different process scenarios
- Provides appropriate exit codes
Usage Examples
# Basic usage
./script.sh
# Verbose mode with custom config
./script.sh -v -c /path/to/config.conf
# Custom log file
./script.sh -l /path/to/custom.log
# All options
./script.sh -v -c /path/to/config.conf -l /path/to/custom.log
Benefits of This Implementation
-
Reliability:
- Robust error handling
- Graceful exit management
- Permission checking
- Signal handling
-
Maintainability:
- Modular design
- Well-documented functions
- Consistent coding style
- Clear variable naming
-
Flexibility:
- Configurable through files
- Command-line options
- Customizable logging
- Adaptable to different environments
-
Debugging:
- Comprehensive logging
- Verbose mode option
- Clear error messages
- Debug information available