I have a shell script which I need to start frequently. I can set the shortcut to start (which I use to start) but not to terminate. I need to Ctrl+C to terminate it. Resultantly sometimes end up opening many processes.
So what I want to do is to add some command in the script which checks if older process from the script exists and kill it then start the new one.
Just to make my requirement more clear, I tried following
Say the running script is /home/user/runscript.sh.
ps aux | grep runscript.sh gives me
user+ 6135 0.0 0.0 16620 1492 ? S 18:28 0:00 /home/user/runscript.sh
user+ 6208 0.0 0.0 15936 952 pts/6 R+ 18:28 0:00 grep --color=auto runscript.sh
I prepended following in script
pkill -f runscript.sh
to kill the process if it is already running. It solved the purpose of killing the older process but didn't let new one start. The reason was obvious which I understood later.
What is the correct way to do this?
The typical approach to this is to use a file system based locking strategy:
The script creates a lock file (/var/lock/subsystem/...) with its own process number as content. When being started, it first checks if such file already exists. If not, all fine. But if so, then it reads the process number from the file and uses it to check the process table. If such process still exists, then it can either exit (usual behavior), or it can terminate that process (what you ask for) by sending a SIG-TERM signal.
You could just use killall instead. The trouble is that you want to avoid killing your current process, but that's easily done with the -o flag:
-o, --older-than
Match only processes that are older (started before) the time
specified. The time is specified as a float then a unit. The
units are s,m,h,d,w,M,y for seconds, minutes, hours, days,
weeks, Months and years respectively.
Therefore this ought to work:
killall -o 5s runscript.sh
The other answer is fundamentally better as it's the resilient 'professional' approach, but this approach should work if it's just for your own script and you want to take the easy way out.
Related
I have a bash script which runs in a loop. Occasionally, the script gets into a state where it is using 100% of the CPU. Looking at pidtree, when that happens, the process has launched a child process to call date.
my_script(401)---my_script(463)---date(15804)
That PID of that child process is never changing. Moreover, that child process is somehow a zombie.
1 R root 463 401 51 80 0 - 4997 - Mar22 ? 6-17:43:01 /bin/bash -eu /usr/sbin/my_script
0 Z root 15804 463 0 80 0 - 0 - Mar28 ? 00:00:00 [date] <defunct>
Luckily, my code has exactly one place where date is called. That line, in a simplified version of the script, looks like (updated to include flock)
LOG="/tmp/foo"
(
flock -e -n 200 || exit 1
while true; do
do_something_that_includes_sleep
vals=("$(date --iso-8601=seconds --utc)")
echo ${vals} >> ${LOG}
done
) 200>>${LOG}
How can this possibly cause date to become a Zombie? Even if it did somehow become a zombie, why would the main script be in Running state consuming 100% of CPU instead of blocking on a pipe read from the child?
After much hair pulling, and several additional failure instances which were investigated with GDB, I now have an answer. There is nothing wrong with the Bash script itself. The problem is the Bash version. What I didn't mention originally is that this problem was only seen when running on Debian Stretch which has Bash version 4.4.11 (from 4.4-5 DEB package). This version has a known bug that was reported back in 2017 and has since been fixed, which explains why my other test systems that are running newer OS's didn't see the same failure.
Original bug report - https://lists.gnu.org/archive/html/bug-bash/2017-02/msg00025.html
There was also a second bug which explicitly documents the 100% CPU utilization waiting for a zombie child at https://lists.gnu.org/archive/html/bug-bash/2017-03/msg00141.html but that ultimately ties back to the original bug report.
Let's say I have a silly script:
while true;do
touch ~/test_file
sleep 3
done
And I start the script into the background and leave the terminal:
chmod u+x silly_script.sh
./silly_script.sh &
exit
Is there a way for me to identify and stop that script now? The way I see it is, that every command is started in it's own process and I might be able to catch and kill one command like the 'sleep 3' but not the execution of the entire script, am I mistaken? I expected a process to appear with the scripts name, but it does not. If I start the script with 'source silly_script.sh' I can't find a process by the name of 'source'. Do I need to identify the instance of bash, that is executing the script? How would I do that?
EDIT: There have been a few creative solutions, but so far they require the PID of the script execution to be stored right away, or the bash session to not be left with ^D or exit. I understand, that this way of running scripts should maybe be avoided, but I find it hard to believe, that any low privilege user could, even by accident, start an annoying script into the background, that is for instance filling the drive with garbage files or repeatedly starting new instances of some software and even the admin has no other option, than to restart the server, because a simple script can hide it's identifier without even trying.
With the help of the fine people here I was able to derive the answer I needed:
It is true, that the script runs every command in it's own process, so for instance killing the sleep 3 command won't do anything to the script being run, but through a command like the sleep 3 you can find the bash instance running the script, by looking for the parent process:
So after doing the above, you can run ps axf to show all processes in a tree form. You will then find this section:
18660 ? S 0:00 /bin/bash
18696 ? S 0:00 \_ sleep 3
Now you have found the bash instance, that is running the script and can stop it: kill 18660
(Of course your PID will be different from mine)
The jobs command will show you all running background jobs.
You can kill background jobs by id using kill, e.g.:
$ sleep 9999 &
[1] 58730
$ jobs
[1]+ Running sleep 9999 &
$ kill %1
[1]+ Terminated sleep 9999
$ jobs
$
58730 is the PID of the backgrounded task, and 1 is the task id of it. In this case kill 58730 and kill %1` would have the same effect.
See the JOB CONTROL section of man bash for more info.
When you exit, the backgrounded job will get a kill signal and die (assuming that's how it handles the signal - in your simple example it is), unless you disown it first.
That kill will propogate to the sleep process, which may well ignore it and continue sleeping. If this is the case you'll still see it in ps -e output, but with a parent pid of 1 indicating its original parent no longer exists.
You can use ps -o ppid= <pid> to find the parent of a process, or pstree -ap to visualise the job hierarchy and find the parent visually.
I am using Peppermint distro. I'm new to linux, however I need to display system processes, then create a new process to run in the background for 2 minutes, I need to prove its running and then terminate it before the 2 minutes is up.
So far i'm using xlogo to test my process is working. I have
ps
xlogo &
TASK_PID=$!
if pgrep -x xlogo>/dev/null 2>&1
then
ps
sleep 15
kill $TASK_PID
ps
fi
I can't seem to figure out a way to give it an initial time of 2 minutes but then kill it after 15 seconds anyway.
any help appreciated!
If you want the command to originally have a time limit of 2 minutes you could do
timeout 2m xlogo &
of course, then your $! will be of the timeout command. If you're using pgrep and satisfied it's only finding the process you care about though, you could use pkill instead of the PID to kill the xlogo
Of course, killing the timeout PID will also kill xlogo, so you might be able to keep things as-is for the rest if you're happy with how that works.
I'm writing a pretty simple music server for my Raspberry Pi that will play MP#s on-demand. I can successfully launch an instance of mpg123 using child_process.exec(), but the PID of this new process differs from the process actually decoding the music. This is important to me because I want to track the process in the event of a user wanting to stop the job (child.kill()). I'm executing as follows:
var child = child_process.exec('mpg123 "' + filename + '"');
console.log(JSON.stringify({pid: child.pid});
This outputs {"pid":9444}, and music starts playing. However, when I check ps aux | grep mpg123, I see two different processes:
pi 9444 0.0 0.1 1760 504 pts/1 S+ 18:55 0:00 /bin/sh -c mpg123 "/home/pi/test.mp3"
pi 9445 11.0 0.6 14840 3112 pts/1 S+ 18:55 0:00 mpg123 /home/pi/test.mp3
This latter process, 9445, is what's actually playing my mp3. If I hang onto child and .kill() upon request, the first job is killed but the second lingers, making the kill ineffective.
Is there a different way to create a process that avoids this, or some better way to kill it? I'd rather not have to track everything (eg, the command I used to create it) and try to track down the 'correct' one based on that (eg, ps aux | grep <filename>).
According to this page, child_process.execFile(cmd, [args]) will spawn a new subshell, which is the pid of the .exec'd process. The subshell then created my instance of mpg123, which is the process I wanted to kill. Instead, I use:
var child = child_process.execFile('mpg123', [filename]); // note filename isn't enquoted
And when I want to kill it, I can simply:
child.kill(); // works
I have been bangin my head on this problem.
I want to send a kill(pid,SIGUSR1) signal to a process running in root user with a process
running in tom user.However everytime,I do this Operation not permitted comes up.
I searched up the net for any programmatical solution but to no avail.All responses are its impossible.But i am a bit skeptical and think it can be done programatically using c.
I need a sample program or lines which can explain how this can be acheived.
i tried using execl also.
To be more specific this kill signal is generated from mysql user to a process running in root and tried running in mysql aswell returned the same result operation not permitted.
Tom
Have you considered creating a process with an setuid() setting ?
The following is what you'd do from a unix/linux command line. Haven't used c in a while, but I'm pretty sure there's some "system" or "shell" function you can pass a shell command to.
If you can use sudo from your, that should do it:
sudo kill -9
Normally, you'd just need
kill -9
but some processes need more authority to kill.
You can get the process id with
ps -aux | grep
I'm afraid I don't know any more than that, hope this helps!
kyle