93

I'm trying to ensure a script remains running on a development server. It collates stats and provides a web service so it's supposed to persist, yet a few times a day, it dies off for unknown reasons. When we notice we just launch it again, but it's a pain in the rear and some users don't have permission (or the knowhow) to launch it up.

The programmer in me wants to spend a few hours getting to the bottom of the problem but the busy person in me thinks there must be an easy way to detect if an app is not running, and launch it again.

I know I could cron-script ps through grep:

ps -A | grep appname

But again, that's another hour of my life wasted on doing something that must already exist... Is there not a pre-made app that I can pass an executable (optionally with arguments) and that will keep a process running indefinitely?

In case it makes any difference, it's Ubuntu.

Oli
  • 235,628
  • 64
  • 220
  • 299
  • 1
    It depends on whether stats are lost when the script isn't running, but I think the programmer in you is right - you should find out more about why the script does not continue indefinitely. – Jonathan Leffler Nov 18 '08 at 14:36
  • 1
    It's more of an on-demand service utility than something that's constantly crunching numbers. If it crashes out before it saves data, nothing is lost; it will just complete the job the next time it runs. – Oli Nov 19 '08 at 11:33
  • Possible duplicate of [How do I write a bash script to restart a process if it dies?](http://stackoverflow.com/questions/696839/how-do-i-write-a-bash-script-to-restart-a-process-if-it-dies) – tripleee Sep 19 '16 at 11:18
  • The question was answered at [Standard or best way to keep alive process started by init.d](http://serverfault.com/q/611525/107832). The solutions include using systemd and `/etc/inittab`. – koppor Mar 08 '17 at 08:14

17 Answers17

94

I have used a simple script with cron to make sure that the program is running. If it is not, then it will start it up. This may not be the perfect solution you are looking for, but it is simple and works rather well.

#!/bin/bash
#make-run.sh
#make sure a process is always running.

export DISPLAY=:0 #needed if you are running a simple gui app.

process=YourProcessName
makerun="/usr/bin/program"

if ps ax | grep -v grep | grep $process > /dev/null
then
    exit
else
    $makerun &
fi

exit

Then add a cron job every minute, or every 5 minutes.

Chris Wendt
  • 300
  • 3
  • 10
47

Monit is perfect for this :)

You can write simple config files which tell monit to watch e.g. a TCP port, a PID file etc

monit will run a command you specify when the process it is monitoring is unavailable/using too much memory/is pegging the CPU for too long/etc. It will also pop out an email alert telling you what happened and whether it could do anything about it.

We use it to keep a load of our websites running while giving us early warning when something's going wrong.

-- Your faithful employee, Monit

Gareth
  • 133,157
  • 36
  • 148
  • 157
39

Notice: Upstart is in maintenance mode and was abandoned by Ubuntu which uses systemd. One should check the systemd' manual for details how to write service definition.

Since you're using Ubuntu, you may be interested in Upstart, which has replaced the traditional sysV init. One key feature is that it can restart a service if it dies unexpectedly. Fedora has moved to upstart, and Debian is in experimental, so it may be worth looking into.

This may be overkill for this situation though, as a cron script will take 2 minutes to implement.

#!/bin/bash
if [[ ! `pidof -s yourapp` ]]; then
    invoke-rc.d yourapp start
fi
Paweł Szczur
  • 5,484
  • 3
  • 29
  • 32
JimB
  • 104,193
  • 13
  • 262
  • 255
  • I must note that configuring upstart to manage your service and keep it running is quite simple (you write small config file). This solution is way cleaner, simpler and better than any other "simple" cron/shell scripts based monitoring. – thor Mar 14 '12 at 13:31
  • @thor - (note, this is from 2008, when upstart was new, and documentation was harder to come by). I agree, the "simple" cron script was just to illustrate that a script needn't be complex to do the job either. – JimB Mar 14 '12 at 14:07
  • I see. I have upvoted your answer anyway when placing my comment – thor Mar 19 '12 at 12:39
28

If you are using a systemd-based distro such as Fedora and recent Ubuntu releases, you can use systemd's "Restart" capability for services. It can be setup as a system service or as a user service if it needs to be managed by, and run as, a particular user, which is more likely the case in OP's particular situation.

The Restart option takes one of no, on-success, on-failure, on-abnormal, on-watchdog, on-abort, or always.

To run it as a user, simply place a file something.service (name it appropriately for your use case) into ~/.config/systemd/user/ (note here that "user" in the path is literal):

[Unit]
Description=Something

[Service]
ExecStart=/path/to/something
Restart=on-failure

[Install]
WantedBy=graphical.target

then:

systemctl --user daemon-reload

# to start it automatically on system start
systemctl --user enable something
systemctl --user start something

# other common commands are status, stop, restart, disable

No root privilege / modification of system files needed, no cron jobs needed, nothing to install, flexible as hell (see all the related service options in the documentation).

See also https://wiki.archlinux.org/index.php/Systemd/User for more information about using the per-user systemd instance.

Raman
  • 17,606
  • 5
  • 95
  • 112
  • 1
    Great answer, but ~/.config/systemd/user/something.service was, to me, a little ambiguous. Is the "user" part literally "user" or like "yourusername"? If it's "yourusername", that strikes me as redundant since you're making this file in your home directory. Edit: looks like it's literally "user" – fivedogit Oct 18 '19 at 22:14
  • @fivedogit Yep, I've updated the answer to make that explicit. – Raman May 30 '23 at 16:12
9

I have used from cron "killall -0 programname || /etc/init.d/programname start". kill will error if the process doesn't exist. If it does exist, it'll deliver a null signal to the process (which the kernel will ignore and not bother passing on.)

This idiom is simple to remember (IMHO). Generally I use this while I'm still trying to discover why the service itself is failing. IMHO a program shouldn't just disappear unexpectedly :)

8

Put your run in a loop- so when it exits, it runs again... while(true){ run my app.. }

In bash, for example you can do this:

while true
do
    echo "Hello"
    sleep 1
done

Just change the echo+sleep by your buggy process, like this

while true
do
    /path/to/long-running-process
done

If you want, you can do the while and do one-liner like this:

while true; do
    /path/to/long-running-process
done
Xavi Montero
  • 9,239
  • 7
  • 57
  • 79
Klathzazt
  • 2,415
  • 19
  • 27
  • 5
    If the script is dying for unknown reasons, it's likely that it will cancel the loop script too, no? – Gareth Nov 18 '08 at 14:38
  • 4
    Im with Garath, if its the system killing long running processes, then the suggested loop - which would start before the app - would be killed first. – ekerner Sep 16 '12 at 16:06
  • 2
    To answer your two comments: No, if the script is dieing for unknown reasons then the loop script is unaffected because it is run in a separate process- but yes, if the system is randomly killing long running processes then this would still be a problem- however I doubt that is the case. The rest of the system was not reported as affected and is full of other long-running processes that would have been restarted as well (only mentioned this one script having a problem). – Klathzazt Sep 19 '12 at 18:08
4

I couldn't get Chris Wendt solution to work for some reason, and it was hard to debug. This one is pretty much the same but easier to debug, excludes bash from the pattern matching. To debug just run: bash ./root/makerun-mysql.sh. In the following example with mysql-server just replace the value of the variables for process and makerun for your process.

  • Create a BASH-script like this (nano /root/makerun-mysql.sh):
#!/bin/bash
process="mysql"
makerun="/etc/init.d/mysql restart"
if ps ax | grep -v grep | grep -v bash | grep --quiet $process
then
    printf "Process '%s' is running.\n" "$process"
    exit
else
    printf "Starting process '%s' with command '%s'.\n" "$process" "$makerun"
    $makerun
fi
exit
  • Make sure it's executable by adding proper file permissions (i.e. chmod 700 /root/makerun-mysql.sh)

  • Then add this to your crontab (crontab -e):

# Keep processes running every 5 minutes
*/5 * * * * bash /root/makerun-mysql.sh
cjohansson
  • 1,058
  • 10
  • 13
  • 1
    Pay attention, in comparison to the Chris Wendt's solution, the script is not started as background task here. – Mason Jan 20 '17 at 10:44
2

The supervise tool from daemontools would be my preference - but then everything Dan J Bernstein writes is my preference :)

http://cr.yp.to/daemontools/supervise.html

You have to create a particular directory structure for your application startup script, but it's very simple to use.

Asfand Qazi
  • 6,586
  • 4
  • 32
  • 34
1

It's a job for a DMD (daemon monitoring daemon). there are a few around; but I usually just write a script that checks if the daemon is running, and run if not, and put it in cron to run every minute.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Javier
  • 60,510
  • 8
  • 78
  • 126
1

first of all, how do you start this app? Does it fork itself to the background? Is it started with nohup .. & etc? If it's the latter, check why it died in nohup.out, if it's the first, build logging.

As for your main question: you could cron it, or run another process on the background (not the best choice) and use pidof in a bashscript, easy enough:

if [ `pidof -s app` -eq 0 ]; then
    nohup app &
fi
gx.
  • 393
  • 1
  • 8
1

You could make it a service launched from inittab (although some Linuxes have moved on to something newer in /etc/event.d). These built in systems make sure your service keeps running without writing your own scripts or installing something new.

Paul Tomblin
  • 179,021
  • 58
  • 319
  • 408
1

Check out 'nanny' referenced in Chapter 9 (p197 or thereabouts) of "Unix Hater's Handbook" (one of several sources for the book in PDF).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
1

A nice, simple way to do this is as follows:

  1. Write your server to die if it can't listen on the port it expects
  2. Set a cronjob to try to launch your server every minute

If it isn't running it'll start, and if it is running it won't. In any case, your server will always be up.

Casey Rodarmor
  • 14,878
  • 5
  • 30
  • 33
0

I think a better solution is if you test the function, too. For example, if you had to test an apache, it is not enough only to test, if "apache" processes on the systems exist.

If you want to test if apache OK is, then try to download a simple web page, and test if your unique code is in the output.

If not, kill the apache with -9 and then do a restart. And send a mail to the root (which is a forwarded mail address to the roots of the company/server/project).

peterh
  • 11,875
  • 18
  • 85
  • 108
  • 1
    This is true. A lot of the monitoring solutions suggested in other answers do provide protocol-monitoring. – Oli Nov 25 '13 at 14:01
0

It's even simplier:

#!/bin/bash

export DISPLAY=:0

process=processname
makerun="/usr/bin/processname"

if ! pgrep $process > /dev/null
then
    $makerun &
fi

You have to remember though to make sure processname is unique.

Smooth Fire
  • 183
  • 5
0

One can install minutely monitoring cronjob like this:

crontab -l > crontab;echo -e '* * * * * export DISPLAY=":0.0" && for app in "eiskaltdcpp-qt" "transmission-gtk" "nicotine";do ps aux|grep -v grep|grep "$app";done||"$app" &' >> crontab;crontab crontab

disadvantage is that the app names you enter have to be found in ps aux|grep "appname" output and at same time being able to be launched using that name: "appname" &

16851556
  • 255
  • 3
  • 11
0

also you can use the pm2 library.

sudo apt-get pm2 

And if its a node app can install.

Sudo npm install pm2 -g

them can run the service.

linux service:

sudo pm2 start [service_name]

npm service app:

pm2 start index.js