[Debian-med-packaging] Help with systemd to start shiny-server needed
Christian Seiler
christian at iwakd.de
Fri Mar 24 16:43:36 UTC 2017
On 03/24/2017 04:21 PM, Andreas Tille wrote:
> I intend to package shiny-server and have prepared some preliminary
> packaging in Debian Med Git[1]. When trying to install the resulting
> package I get: [...]
>
> Mar 24 16:20:23 sputnik systemd[1]: Starting ShinyServer...
> Mar 24 16:20:26 sputnik systemd[1]: shiny-server.service: PID file /var/run/shiny-server.pid not readable (yet?) after start-post: No such file or directory
> Mar 24 16:20:31 sputnik systemd[1]: Failed to start ShinyServer.
Well, your service doesn't appear to write out a PID file
(or delete it before it exits again), hence systemd can't
find that PID file and will consider the service to be
failed.
Your logs indicate that the process seems to have exited,
but whether that's because of SIGTERM sent to it or not
is unclear at this point.
First thing is you need to figure out why your service
doesn't appear to write a PID file at all. From the logs
you posted, it could also be that the service exits
prematurely.
Why are you using Type=simple + PIDFile= anyway?
sleep 3 / 5 in ExecStartPost=/ExecStopPost= also seems to
be completely wrong to me.
>From my perspective, when writing a systemd unit file for a
service, I'd like to follow the following guidelines:
- Ideally the service supports systemd's notification
protocol, in which case I'd use Type=notify and no PID
file. Doesn't appear to be the case here.
- If the service is just a program you start and it does
not fork (so running it on the command line will have
it be active until you press Ctrl+C or similar), then
the best thing one can do is Type=simple, but no PID
file, the process started by systemd is considered to be
the main PID file of that service.
This is not 100% ideal, as there will be no notification
of whether the service has been started or not (systemd
will just assume the service is up if fork+exec works),
but without a notification protocol, that's the best
one can do.
- If a service forks, then I'd use Type=forking. If the
service supports writing out a PID file [1], then I'd
tell the service to write a PID file in /run [2], and
tell systemd to look for it (via PIDFile=).
I don't know much about your specific code, so you'd have to
test this on the command line first (to see how the process
reacts), but you'd either just want a Type=simple OR you'd
want Type=forking with PIDFile=... But you wouldn't want to
have Type=simple with PIDFile=, that just doesn't make any
sense.
And the sleep stuff in *StartPost seems completely wrong to
me. Especially ExecStopPost: please use KillMode=control-group
or KillMode=mixed instead (see docs) if you want to ensure
that all processes of a service have exited.
Regards,
Christian
[1] Most services actually get PID file handling and forking wrong,
which is why many init system developers have developed their own
startup notification mechanisms for services that are way easier
to get right.
What should happen for forking services is (in this order):
- process forks (possibly twice)
- parent process stays alive and waits for child process to
signal it that initialization is complete (there are various
ways of doing this, easiest is just using a pipe(2) and
closing it from the child when that's done)
- child process initializes (opens all sockets, log files,
etc.)
- - child process signals parent process that it's done
- parent process writes out the PID file (with the PID of the
child) that the child is now up and running
- parent process exits to signal the caller that the daemon
has now been initialized successfully
(Alternatively you can already initialize in the parent process
and fork only afterwards when you know you're all set.)
Basically, from the outside, the proper interface should be:
- process that was started exits with code 0 and a PID file
has been written at that time, plus the PID in the PID file
exists: service is considered started successfully
- process that was started exits with non-zero code: failure
- process that was started exits with code 0 but PID file
does not exist: shouldn't happen
I see way too many programs out there that don't get this
completely right. Common mistakes:
- PID file is written in child process, but possibly at a
point after parent process has exited
- PID file is written unconditionally after fork, even if
initialization in child process has failed
- parent process exits before child is properly initialized
[2] Btw. in Debian you should use /run instead of /var/run, which
nowadays is just a symlink to /run anyway. For early-boot
services this is required (because /var could be a remote file
system), but even if it's not required, unless there's a fixed
value set by upstream that you don't want to patch out for no
good reason, please use /run directly anyway.
More information about the Debian-med-packaging
mailing list