3

I have a small problem. I have one program (let's call it master) which, internally, starts several programs (client1, client2 and client3) and detaches them.

Now, I want the main program to always stay online, so I wrote a systemd service file like this:

[Unit]
Description=Run the master
Before=runlevel2.target runlevel3.target runlevel4.target runlevel5.target
After=remote-fs.target systemd-journald-dev-log.socket all.target

[Service]
Type=forking
Restart=always
TimeoutSec=5min
IgnoreSIGPIPE=no
KillMode=process
RemainAfterExit=no
SysVStartPriority=5
ExecStart=/home/user/start_master.sh start
ExecStop=/home/user/start_master.sh stop
PIDFile=/var/run/mymaster.pid

[Install]
WantedBy=multi-user.target

The starting script is like this one:

#! /bin/sh

PIDFILE=/var/run/mymaster.pid

do_start() {
    /home/user/master >/dev/null 2>&1 &
    echo $!
}

case "$1" in
    start)
        PID=$(do_start)
        if [ -z $PID ]; then
                echo "Failed starting master"
                exit 2
        else
                echo $PID > $PIDFILE
        fi
        ;;
    stop)
        killall master client1 client2 client3 2>/dev/null
        rm $PIDFILE
        ;;
    restart)
        killall master 2>/dev/null
        $0 start
        ;;
    *)
        echo "Usage: $0 start|stop|restart" >&2
        exit 3
        ;;
esac

My desired behavior is:

  • on start: just start the master (it will take care of starting the clients)
  • on restart (or when the master is shut down): kill the master program and restart it (since it will attach to the previous clients)
  • on stop: kill the master program and all the clients

Now, with this setup, everything works fine: when I start the service, this is what I see:

$ systemctl status master.service
● master.service - Run the master
   Loaded: loaded (/etc/systemd/system/master.service; enabled)
   Active: active (running) since Wed 2016-10-19 16:16:33 CEST; 5s ago
  Process: 4161 ExecStop=/home/user/start_master.sh stop (code=exited, status=0/SUCCESS)
  Process: 4198 ExecStart=/home/user/start_master.sh start (code=exited, status=0/SUCCESS)
 Main PID: 4200 (master)
   CGroup: /system.slice/master.service
           ├─4200 /home/user/master
           ├─4203 /home/user/client1
           ├─4205 /home/user/client2
           └─4209 /home/user/client3

Also the stop runs fine. BUT... when I want to restart it (either by calling restart or by manually killing the master program) systemd executes the ExecStop instead of the restart command, thus killing all the clients too.

Is there a way to achieve what I wanted?

Best regards

PS. I'm using Debian 8.6.0 x64

3

systemd does not have a notion of a restart that is not just stop-if-running+start. You might be able to use ExecReload= and then use systemctl reload instead of restart, but the question is why you need this?

Why do you restart the master without restarting the clients? For that matter, what does the master actually do? Your start script leads me to believe that you are trying to do process supervision again, which I would advise against - systemd can do that, so use it.

I would recommend that you instead try to figure out how you can start the clients via systemd.

In any case, your start script is completely superfluous. It uses PID files and all sorts of stuff that is absolutely unnecessary because systemd can already do all that stuff. I suggest removing it and instead just using "ExecStart=/home/user/master" in your .service file (if the master is necessary after all), then adjust the Type= from "forking" to "simple". Remove the "ExecStop" and "PIDFile".

I recommend https://jdebp.eu/FGA/systemd-house-of-horror/tomcat.html as further reading.

  • 1
    Thank you for answering. Anyway yes, I was trying to implement a supervisor, but using systemd was not a good idea because I needed to have more control on which clients should be started and when (so the master had to handle this). It's better if the clients are left running because this way they will be able to continue to receive data and then connect back to master. The script, however, was automatically generated when I switched the system from init.d to systemd (and that's why it was considered forking). – frarugi87 Nov 18 '16 at 16:00
  • The master can still kill the clients (and they'll stay down) if they're systemd services, provided they have "Restart=on-failure" (or on-abnormal). What you need here is to get the clients out of the master's systemd service, and the easiest way to do that is to make them systemd units that master starts. – faho Nov 18 '16 at 16:31
  • Ok, I'll think about it. BTW is there a way to control systemd from a C/C++ program instead of using system calls to launch systemctl? – frarugi87 Nov 19 '16 at 11:20
  • 1
    There's the dbus apis, and I'd imagine libsystemd also has functions for it. – faho Nov 20 '16 at 12:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.