Using exec to relaunch crashing binary - bash

Sorry, the question is pretty vague, but I hope someone still can help.
As I understand exec bash command, it replaces the code segment with what is specified by an argument. Practically replace the running script with something else.
But I am pretty sure I saw people using exec(not fork) in a loop to relaunch executable if it crashes or just exits with non-zero exit code. Unfortunately I can't find that piece of code now. Is it at all possible or am I imagining things?

I don't know specifically what you saw, but there are conceivable ways of using exec in a loop to launch and relaunch a process, e.g.
while true
do
( unset DISPLAY && exec ./myfile )
done
The ( .. ) here is an explicit subshell, so there is a fork even if it's not obvious.
Other conceivable reasons for putting exec in a loop include trying to exec different files or different paths, until you find one that works or the file is created or becomes available.
However, there is no way to successfully exec a process without any kind of implicit or explicit fork, and then loop around to exec itself again (unless the script ends up execing itself in a recursive way).

This is actually a more common problem than you'd think.
In the past, I've always implement a bash script to monitor if the process is there, and if it's not, restart it.
Here are some solutions that could work for you:
https://serverfault.com/questions/52976/simple-way-of-restarting-crashed-processes

Related

Blocking a bash script running with &

I may have inadvertently launched a bash script containing an infinite cycle whose exit condition may be met next century, if ever. The fact is that I launched the script, as I would do with a nohup program, with
bash [scriptname].sh &
so that (as I get it, which is most probably wrong) I can close the terminal and still keep the script running, as was my intention in developing it. The script should run calculation programmes in my absence and let me gather the results after some time.
Now I want to stop it, but nothing seems to do the trick: I killed the programmes the script had launched, I removed the input file the script was getting orders from and - last and most perfect of accomplishments - I accidentally closed the terminal trying to "exit" the script, which was still giving me error messages.
How can I check whether the script is running (as it does not appear in "top")? Is the '&' relevant? Should I just ask permission to reboot the pc, if that will work and kill everything?
Thank you.
[I put a "Hi everyone" at the beginning but the editor won't let me show it. Oh, well. It's that kind of day.]
Ok, I'll put it right here to prove my stupidity, as I wandered the internet shortly (after a long wandering before writing this post) and found that the line:
kill -9 $(pgrep -f [SCRIPTNAME].sh)
does the trick from any terminal window.
I write this answer to help anyone in the same situation, but feel free to remove the thread if unnecessary (and excuse me for disturbing).
Good you found it, here is another way if you do not use bash -c and run it in current shell not a separate shell.
# put a job in background
sleep 100 &
# save the last PID of background job
MY_PID=$!
# later
kill $MY_PID

Are shell scripts read in their entirety when invoked?

I ask because I recently made a change to a KornShell (ksh) script that was executing. A short while after I saved my changes, the executing process failed. Judging from the error message, it looked as though the running process had seen some -- but not all -- of my changes. This strongly suggests that when a shell script is invoked, the entire script is not read into memory.
If this conclusion is correct, it suggests that one should avoid making changes to scripts that are running.
$ uname -a
SunOS blahblah 5.9 Generic_122300-61 sun4u sparc SUNW,Sun-Fire-15000
No. Shell scripts are read either line-by-line, or command-by-command followed by ;s, with the exception of blocks such as if ... fi blocks which are interpreted as a chunk:
A shell script is a text file containing shell commands. When such a
file is used as the first non-option argument when invoking Bash, and
neither the -c nor -s option is supplied (see Invoking Bash), Bash
reads and executes commands from the file, then exits. This mode of
operation creates a non-interactive shell.
You can demonstrate that the shell waits for the fi of an if block to execute commands by typing them manually on the command line.
http://www.gnu.org/software/bash/manual/bashref.html#Executing-Commands
http://www.gnu.org/software/bash/manual/bashref.html#Shell-Scripts
It's funny that most OS'es I know, do NOT read the entire content of any script in memory, and run it from disk. Doing otherwise would allow making changes to the script, while running. I don't understand why that is done, given the fact :
scripts are usually very small (and don't take many memory anyway)
at some point, and shown in this thread, people would start making changes to a script that is already running anyway
But, acknowledging this, here's something to think about: If you decided that a script is not running OK (because you are writing/changing/debugging), do you care on the rest of the running of that script ? you can go ahead making the changes, save them, and ignore all output and actions, done by the current run.
But .. Sometimes, and that depends on the script in question, a subsequent run of the same script (modified or not), can become a problem since the current/previous run is doing an abnormal run. It would typically skip some stuff, or sudenly jump to parts in the script, it shouldn't. And THAT may be a problem. It may leave "things" in a bad state; particularly if file manipulation/creation is involved.
So, as a general rule : even if the OS supports the feature or not, it's best to let the current run finish, and THEN save the updated script. You can change it already, but don't save it.
It's not like in the old days of DOS, where you actually have only one screen in front of you (one DOS screen), so you can't say you need to wait on run completion, before you can open a file again.
No they are not and there are many good reasons for that.
One of the things you should keep in mind is that a shell is not an interpreter even if there are some similarities. Shells are designed to work with a stream of commands. Either from the TTY ,a PIPE, FIFO or even a socket.
The shell reads from its resource line by line until a EOF is returned by the kernel.
The most shells have no extra support for interpreting files. they work with a file as they would work with a terminal.
In fact this is considered to be a nice feature because you can do interesting stuff like this How do Linux binary installers (.bin, .sh) work?
You can use a binary file and prepend shell scripts. You can't do this with an interpreter. because it parses the whole file or at least it would try it and fail. A shell would just interpret it line by line and doesnt care about the garbage at the end of the file. You just have to make sure the execution of the script gets terminated before it reaches the binary part.

Running a variable script with parameters through a script

I am sure there is an easier way to do this, but I have yet to figure out what to try next. We are running some jboss applications and I wish to be able to restart these with a input parameter. As I wish to restart more than one application at a time I figured a list would be good. This is comma seperated. This is how far I have gotten thus far.
IFS=',';
while read mLine
do
for i in $mLine
do
sh jboss-{$mLine} restart
done;
done < /tmp/apps
In general it works if I just write "sh jboss-abcdef restart", but not as long as I write "jboss-${mLine} restart". The latter will return a response from the script ( which is the right script according to the input values ) asking for the parameter which as you can see is in the sh command of this script. The former starts the correct script just like the latter, but unlike the latter, the first one actually restarts the server in question.
One could argue that I put one like for each applcation as well, but since not all applications needs to be restarted every time that would make me alot of if's this and if's that to find out which lines would have to be run, and thus defeating the purpose of neat and simple ...
Any ideas would be appreciated as I'm willing to try most to find a solution.
If you do this:
#!/bin/sh
for app in "$#"; do
sh "jboss-$app" restart
done
Then you can pass a space-separated list of app names to the script
./restart-apps app1 app2 app3 ...

Shell script that can check if it was backgrounded at invocation

I have written a script that relies on other server responses (uses wget to pull data), and I want it to always be run in the background unquestionably. I know one solution is to just write a wrapper script that will call my script with an & appended, but I want to avoid that clutter.
Is there a way for a bash (or zsh) script to determine if it was called with say ./foo.sh &, and if not, exit and re-launch itself as such?
The definition of a background process (I think) is that it has a controlling terminal but it is not part of that terminal's foreground process group. I don't think any shell, even zsh, gives you any access to that information through a builtin.
On Linux (and perhaps other unices), the STAT column of ps includes a + when the process is part of its terminal's foreground process group. So a literal answer to your question is that you could put your script's content in a main function and invoke it with:
case $(ps -o stat= -p $$) in
*+*) main "$#" &;;
*) main "$#";;
esac
But you might as well run main "$#" & anyway. On Unix, fork is cheap.
However, I strongly advise against doing what you propose. This makes it impossible for someone to run your script and do something else afterwards — one would expect to be able to write your_script; my_postprocessing or your_script && my_postprocessing, but forking the script's main task makes this impossible. Considering that the gain is occasionally saving one character when the script is invoked, it's not worth making your script markedly less useful in this way.
If you really mean for the script to run in the background so that the user can close his terminal, you'll need to do more work — you'll need to daemonize the script, which includes not just backgrounding but also closing all file descriptors that have the terminal open, making the process a session leader and more. I think that will require splitting your script into a daemonizing wrapper script and a main script. But daemonizing is normally done for programs that never terminate unless explicitly stopped, which is not the behavior you describe.
I do not know, how to do this, but you may set variable in parent script and check for it in child:
if [[ -z "$_BACKGROUNDED" ]] ; then
_BACKGROUNDED=1 exec "$0" "$#" & exit
fi
# Put code here
Works both in bash and zsh.
the "tty" command says "not a tty" if you're in the background, or gives the controlling terminal name (/dev/pts/1 for example) if you're in the foreground. A simple way to tell.
Remember that you can't (or, not recommended to) edit the running script. This question and the answers give workarounds.
I don't write shell scripts a long time ago, but I can give you a very good idea (I hope). You can check the value of $$ (this is the PID of the process) and compare with the output of the command "jobs -l". This last command will return the PID of all the backgrounded processes (jobs) and if the value of $$ is contained in the result of the "jobs -l", this means that the current script is running on background.

Determine if a ruby script is already running

Is there an easy way to tell if a ruby script is already running and then handle it appropriately? For example: I have a script called really_long_script.rb. I have it cronned to run every 5 minutes. When it runs, I want to see if the previous run is still running and then stop the execution of the second script. Any ideas?
The ps is a really poor way of doing that and probably open to race conditions.
The traditional Unix/Linux way would be to write the PID to a file (typically in /var/run) and check to see if that file exists on startup.
e.g. the pidfile being located at /var/run/myscript.pid then you'd check to see if that exists before running the program. There are a few tricks to avoid race conditions involving using O_EXCL (exclusing locking) to open the file and symbolic links.
However unlikely, you should try to code to avoid race conditions by using atomic operations on the filesystem.
To save re-inventing the wheel, you might want to look at http://rubyforge.org/projects/pidify/
Highlander
Description
A gem that ensures only one instance of your main script is running.
In short, there can be only one.
Installation
gem install highlander
Synopsis
require 'highlander' # This should be the -first- thing in your code.
# Your code here
Meanwhile, back on the command line...
# First attempt, works. Assume it's running in the background.
ruby your_script.rb
# Second attempt while the first instance is still running, fails.
ruby your_script.rb # => RuntimeError
Notes
Simply requiring the highlander gem ensures that only one instance
of that script cannot be started again. If you try to start it again
it will raise a RuntimeError.
You should probably also check that the process is actually running, so that if your script dies without cleaning itself up, it will run the next time rather than simply checking that
/var/run/foo.pid exists and exiting.
In bash:
if ps aux | grep really_long_script.rb | grep -vq grep
then
echo Script already running
else
ruby really_long_script.rb
fi

Resources