Start script in /etc/service (runit) is not working with daemon
Don't use forever.
It's dead easy. As you've already observed, forever is unnecessary here as runit already is a service manager, and is already starting and restarting your program.
As you've also already observed, there are a few rules for what run
programs must do. They must not fork and exit the main program. The runit service manager, like most daemontools-family service managers (there being a whole family of softwares that all work like this), expects that the process running the run
program is the dæmon. Not its parent. Not a brief fly-by-night that forks and exits. But the actual dæmon itself.
a simple run
program
There are various scripting languages that make writing such run
programs a doddle. Laurent Bercot's execline
is one. My nosh
program is another. Presuming that bin/www
is the actual executable program for your dæmon, a nosh run
script would look something like:
#!/bin/nosh fdmove -c 2 1 chdir /src bin/www
An execline script is similarly brief. But the shell script isn't that much longer. If your run
program is a shell script, the thing to remember is to overlay the shell program with your dæmon program in the same process. The shell command for doing this is exec
and a shell script thus looks something like:
#!/bin/sh -e exec 2>&1 cd /src exec bin/www
I strongly recommend that, if your program does not require superuser privileges, you execute it via the chpst
program (and its -u
option), so that it starts as a non-privileged user — for best results, one that is dedicated to this service.
Several people have collected and published suites of run
programs, over the years, and most of the run
programs are that short and simple. Since you have runit, you can start with Gerrit Pape's own collection of run
programs.
starting and stopping dæmons
When it comes to starting and stopping the service, again most daemontools-family service managers need to be told to stop auto-restarting the service. They all come with a tool to do this. You just need to use it in your clean-shutdown
script.
- runit has the
sv
program:sv down /etc/service/MyApp
- s6 has the
s6-svc
program:s6-svc -d /etc/service/MyApp
- perp has the
perpctl
program:perpctl d /etc/service/MyApp
- daemontools has the
svc
program:svc -d /etc/service/MyApp
- daemontools-encore has the
svc
program:svc -d /etc/service/MyApp
- nosh has the
service-control
program:service-control --down /etc/service/MyApp
which is also aliased assvc
:svc -d /etc/service/MyApp
I said that it was a family of toolsets. In fact, under the covers all of these tools are speaking mostly the same protocol.
This brings me to a larger point. All of these toolsets are not exclusive. Just because you have runit, that doesn't stop you from using execline
if you want to. Or you can run a nosh
script under s6's service manager.
logging
You've tried to write log files with forever. Again, don't use forever. This isn't the right way to go about logging with runit. Redirecting standard output and error directly to files makes your logs impossible to rotate, size-cap, and otherwise control without drastic interference in your dæmon's operation.
daemontools-family service managers all do logging by having the output of one main service connected, through an ordinary pipe, to the input of another log service. This pipe is set up by the service manager. You don't do it yourself.
The log service is another service. It runs one of the many available tools that simply read from their standard input and write to a strictly size-capped, automatically cycled, on-demand rotateable, set of log files in a log directory.
These programs are multilog
,multilog
, s6-log
, tinylog
, cyclog
, and svlogd
which latter is the one that you'll find that comes in the toolkit with runit.
In fact, you might find that whoever set up /etc/service/MyApp
has already set up a log service in /etc/service/MyApp/log
. If not, a log service script is dead simple:
#!/bin/sh -e exec chpst -uMyApp-log svlogd -t ./main
Just create a user account named MyApp-log
, mkdir /etc/service/MyApp/log/main
, chown MyApp-log /etc/service/MyApp/log/main
and you are away. (Note that main
can be a symbolic link to somewhere else where you make the directory instead. You don't have to put logs under /etc
with runit. I put my log directories under /var/log/sv
.)
You do nothing at all to your main service to cycle and size-cap logs. The cycling and size-capping happens independently, in the log service process.
Further reading
- Jonathan de Boyne Pollard (2001). "Don't fork() in order to 'put the dæmon into the background'.". Mistakes to avoid when designing Unix dæmon programs. Frequently Given Answers.
- Jonathan de Boyne Pollard (2014). A side-by-side look at run scripts and service units.. Frequently Given Answers.
- https://stackoverflow.com/a/21554947/340790
- https://superuser.com/a/868519/38062
- Gerrit Pape. A collection of run scripts.
Related videos on Youtube
Eric Olson
Updated on September 18, 2022Comments
-
Eric Olson over 1 year
I am facing an issue with a script I have kicked off through
/etc/service
and is usingrunit
.My script at
/etc/service/myApp/run
looks like:#!/bin/bash cd /src forever -l forever.log -o out.log -e err.log -a start bin/www
What Forever does is runs my script as a daemon. But doing this seems to make runit think that my service has ended and runs
/etc/service/myApp/run
again, and again, and again...I have also tried running this not as a daemon and it works fine running in the foreground, but then I still have an issue. I have a clean-shutdown command to send to my server at some point which will eventually shut down the foreground process and I do not want it to restart. But to my dismay,
/etc/service/myApp/run
gets called immediately to restart my server :(I am no sys admin so most of this side of things is new to me. All I want is my script to run on boot and not auto-restart. Thanks for your help.
EDIT: I updated my question to include the fact that
runit
is being used here. I see that runit monitors processes to keep services up. My question still remains though.-
JdeBP almost 9 years
/etc/services
is not a directory. If you mean/etc/service
and you are using runit, then edit your question to correct it and to say so. (There's an easy answer for runit, but it rather presumes that you are using runit.) -
Eric Olson almost 9 yearsThanks @JdeBP. I found that I am using runit and updated my question accordingly. Thanks for the guidance. I look forward to your "easy answer" :)
-
-
Eric Olson almost 9 yearsWow! What a great answer @JdeBP. This was like a mini-lesson with multiple answers sprinkled in. I ran and tested with success without using
forever
and the magicalsv down /etc/service/MyApp
you suggested. Thank you so much for such detailed and awesome help. The knowledge shared is better than the answer.