1

Okay, first post! So I have this situation where I need to check for a process older than 5 minutes by a particular process name. The problem is that the system in question is a customized version of CentOS 4, so it has pmisc 21.4 and killall didn't incorporate -o/--older-than until 22.9. I have no option of upgrading the system whatsoever, so please don't suggest that as an option. It's an internal system without outside facing access, so the potential security issues are not a big concern for this customer.

Engineers are working on a solution to figure out why this process is occasionally getting hung up, but in the mean time, I just need to cron a script to check for the process to kill it off if it's been running for more than 5 min, because it should normally only take 30-45 seconds to complete.

I'm pretty sure this can be accomplished with ps output, some egrep and regex syntax, but I don't know how to put it together.

The process is called "synch". Someone gave me the following suggested code, but it seems to not work correctly.

#!/bin/bash

for i in $(pgrep -f synch)
do
TIME=$(ps --no-headers -o etime $i | cut -d":" -f 2)
if [ "$TIME" -gt 4 ] ; then
kill $i
fi
done
  • 1
    Can you wrap the naughty command with timeout 5m thatnaughtycommand ? – thrig Jul 12 '16 at 21:13
2

Try -o etimes instead, which gives the time in seconds instead of a human-friendly representation. Then you can get rid of the cut and check that the time is at least 300 seconds:

#!/bin/bash

for i in $(pgrep -f synch)
do
    TIME=$(ps --no-headers -o etimes $i)
    if [ "$TIME" -ge 300 ] ; then
        kill $i
    fi
done
  • Thank you for the suggestion. Apparently, the version of ps included does not have "etimes" either. I updated as you suggested and got: ERROR: Unknown user-defined format specifier "etimes". – user179421 Jul 12 '16 at 19:09
2

According to the man page, the format for etime is [[DD-]hh:]mm:ss, which is a bit annoying to parse because some fields may be missing.

If we assume the process has only been running for a short while, then the first field should be minutes. So try with cut -d: -f1 instead of -f2. That will give you hours, though, if the process has been running for a while...

(etimes would be much nicer, but it doesn't seem to be present even in psmisc 22.2-5, so you probably can't use it.)

  • Ooo. I think the f1 instead of f2 may have fixed it. I just tested locally and it seems to have done the trick. I will continue to monitor. Thanks for the pointer! – user179421 Jul 12 '16 at 20:04
2

If crusty old CentOS 4 has find, this may be worth trying.

find /proc -maxdepth 1 -regex '/proc/[0-9]*' -type d -mmin +5 -exec kill {} \;
  • That will kill everything older than 5 mins. including sshd. and your current shell. and init. It needs something to limit the kill to processes called synch - e.g. find /proc/ -mindepth 2 -maxdepth 2 -type f -name 'comm' -mmin +5 -exec grep -l '^synch$' {} + | awk -F'/' '{ print $3 }' | xargs kill – cas Jul 13 '16 at 6:22
  • that won't work, need to use timestamp of /proc/PID directory, not /proc/PID/comm file. working version in my answer. – cas Jul 13 '16 at 6:37
1
find /proc/ -maxdepth 1 -type d -name '[0-9]*' -mmin +5 | 
  sed -e 's:$:/comm:' | 
  xargs -r grep -l '^synch$' |
  cut -d/ -f3 |
  xargs -r kill

find all processes older than 5 minutes with process name (/proc/PID/comm) matching the regexp ^synch$ and kill them.

  • it's not important here (because we know that PID directories in /proc/ can't have annoying characters in the name) but in more general variants of this oneliner, I would have used -print0 - except that I suspect that cut and sed in Centos4 are too old to have -z options for NUL-separated input. – cas Jul 13 '16 at 6:43
0

If you don't mind C++, and the machine has the resources to spare you could write a recursive program like the following:

int main()
{
 int Started;
 int Passed;
RUNAGAIN:
 system("time=$(date); echo $time > ~/timeStarted");
CHECKTIME:
 system("passed0=$(date); echo $passed0 > ~/timePassed; passed 1=$(cut -c15,16 ~/timePassed); echo $passed1 > ~/timePassedCut; started0=$(cut -c15,16 ~/timeStarted; echo $started0 > ~/timeStartedCut");
 ifstream STARTED("~/timeStartedCut");
 STARTED>>Started;
 STARTED.close();
 ifstream PASSED("~/timePassedCut");
 PASSED>>Passed;
 PASSED.close();
 int TimePassed
 TimePassed = Started - Passed;
 if(TimePassed>4)
   {
    system("killall *process name here*; ~/*process name here*//to start it again, omit if uneeded//");
    goto RUNAGAIN;
   }
 else if(TimePassed<5)
   {
    goto CHECKTIME;
   }
}

It will pull the date of when the program starts, cut out the minute of the hour, write it to a file, load the file as a variable, do the same after some time has passed, and if the difference between the start time's minute count and check time's minute count is 5 or greater kills the process. Launch the process in question with a basic shell script containing the following to ensure that both the process and timer start simultaneously:

#!/bin/sh

~/*process name here* & ~/CheckTime
  • If you want to wait 5 minutes in C++ or C on Unix, just sleep(300);. That also works if the process starts in the last 5 minutes of an hour, which your code doesn't. But you don't need C; just do sleep 300; killall xxx in shell. But that will kill any process running 5 minutes after you start, not a process that has been running 5 minutes; e.g. if the program starts at 01:01:00 and completes in 30 seconds then starts again at 01:05:50 you'll kill it after it ran only 10 seconds. – dave_thompson_085 Jul 13 '16 at 8:20
  • This seems wrong on so many levels. If you're going to mostly fork out shells, you might as well write a shell script. Otherwise there are probably better ways to get the time in C(++) than forking out date. Maybe consider using proper loops instead of gotos too. – ilkkachu Jul 13 '16 at 10:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.