When trying to kill processes older than a specific time threshold, most existing solutions fall short. The common approaches either:
- Only match processes within fixed 24-hour windows (e.g., "7 days" actually means 7-8 days)
- Rely on
find
commands that don't work consistently across Linux distributions - Fail to properly handle process start time comparisons
Here's a Python script that accurately identifies and kills processes older than a specified threshold:
#!/usr/bin/env python2.6
import os
import sys
import time
import psutil
from datetime import datetime, timedelta
def kill_old_processes(process_name, hours_threshold):
threshold = datetime.now() - timedelta(hours=hours_threshold)
killed = 0
for proc in psutil.process_iter(['pid', 'name', 'create_time']):
try:
if process_name in proc.info['name']:
create_time = datetime.fromtimestamp(proc.info['create_time'])
if create_time < threshold:
proc.kill()
killed += 1
print(f"Killed PID {proc.pid} (started: {create_time})")
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
return killed
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: {} ".format(sys.argv[0]))
sys.exit(1)
process_name = sys.argv[1]
hours_threshold = float(sys.argv[2])
print(f"Scanning for '{process_name}' processes older than {hours_threshold} hours...")
killed = kill_old_processes(process_name, hours_threshold)
print(f"Done. Killed {killed} processes.")
For those preferring a bash solution, here's an improved version that handles time comparisons accurately:
#!/bin/bash
PROCESS_NAME="page.py"
THRESHOLD_HOURS=1
# Calculate threshold in seconds since epoch
NOW=$(date +%s)
THRESHOLD=$((NOW - THRESHOLD_HOURS*3600))
# Find and kill old processes
ps -eo pid,etime,args | grep "$PROCESS_NAME" | while read -r pid etime args; do
# Convert elapsed time to seconds
if [[ "$etime" =~ ^([0-9]+)-([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$ ]]; then
days=${BASH_REMATCH[1]}
hours=${BASH_REMATCH[2]}
minutes=${BASH_REMATCH[3]}
seconds=${BASH_REMATCH[4]}
total_seconds=$((days*86400 + hours*3600 + minutes*60 + seconds))
elif [[ "$etime" =~ ^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$ ]]; then
hours=${BASH_REMATCH[1]}
minutes=${BASH_REMATCH[2]}
seconds=${BASH_REMATCH[3]}
total_seconds=$((hours*3600 + minutes*60 + seconds))
elif [[ "$etime" =~ ^([0-9][0-9]):([0-9][0-9])$ ]]; then
minutes=${BASH_REMATCH[1]}
seconds=${BASH_REMATCH[2]}
total_seconds=$((minutes*60 + seconds))
else
continue
fi
# Compare against threshold
if [ "$total_seconds" -gt "$((THRESHOLD_HOURS*3600))" ]; then
echo "Killing PID $pid (running for $total_seconds seconds)"
kill -9 "$pid"
fi
done
To run this daily, add to your crontab:
0 * * * * /path/to/process_killer.py page.py 1 >> /var/log/process_killer.log 2>&1
Consider these additional safeguards:
- Add process whitelisting for critical services
- Implement logging to track killed processes
- Add email alerts when multiple processes are terminated
- Consider process dependencies before killing
When managing long-running Python processes like page.py
in Apache, we often need to terminate stale processes that exceed a certain age threshold. The standard approaches found online frequently fail because they either:
- Use imprecise time range matching (e.g., "7 days" actually means 7-8 days)
- Rely on
find
commands that don't work consistently across Linux distributions - Don't properly handle the "greater than X hours" use case
Here's a production-tested approach that works on CentOS, Gentoo, and most Linux distributions:
#!/bin/bash # Kill page.py processes older than 1 hour ps -eo pid,etime,args | \ awk '/page\.py/ { # Extract days if present if (index($2,"-")) { split($2,d,"-"); $2=d[2]; days=d[1] } # Convert elapsed time to seconds split($2,t,":"); secs = t[1]*3600 + t[2]*60 + t[3]; if (days) secs += days*86400; # Kill if older than 3600 seconds (1 hour) if (secs > 3600) { print $1; } }' | xargs -r kill
For systems where ps
output format varies, we can check process start time directly:
#!/bin/bash threshold=$(date -d "1 hour ago" +%s) for pid in $(pgrep -f "page\.py"); do start_time=$(stat -c %Y /proc/$pid) if [ -n "$start_time" ] && [ "$start_time" -lt "$threshold" ]; then kill $pid fi done
For daily execution via cron, save this as /usr/local/bin/cleanup_page_procs
:
#!/bin/bash LOG=/var/log/page_proc_cleanup.log echo "[$(date)] Starting cleanup" >> $LOG ps -eo pid,etime,args | \ awk '/page\.py/ { split($2,t,":"); secs = t[1]*3600 + t[2]*60 + t[3]; if (secs > 3600) { system("kill -9 " $1); print "[$(date)] Killed PID " $1 >> "'"$LOG"'"; } }'
Then add to crontab:
0 * * * * /usr/local/bin/cleanup_page_procs
- Always test with
echo
before actually killing processes - Consider using
kill -15
(SIGTERM) beforekill -9
- Log all actions for audit purposes
- Set appropriate permissions on the cleanup script
For processes that might restart themselves, add a cooldown check:
#!/bin/bash marker_file="/tmp/page_proc_cleanup.lock" # Exit if last run was less than 5 minutes ago find "$marker_file" -mmin +5 2>/dev/null | grep -q . || exit 0 touch "$marker_file" # Rest of cleanup script...