Hacker News new | ask | show | jobs
by ww520 524 days ago
Looks nice.

One thing I found systemd really confusing was its treatment of ExecStop in a service script. ExecStart is the command to run when systemd starts the service at system boot up (or when a user tells systemd to start the service). However, ExecStop is run when the starting command has finished running. You have to set RemainAfterExit=yes to have the desired function of running the stop command on system shutdown or on user stopping the service. ExecStop is basically the "on-cleanup" event rather than "to-shutdown-the-service" event.

2 comments

I think about them as “on start” and “on stop”.

It is important to keep in mind that systemd is tailored towards daemons. So if your service just runs a command that eventually exits, you need to explicitly tell systemd to treat it differently than a daemon.

Edit: As others noted, you’re probably looking for oneshot + RemainAfterExit.

It is a little asymmetric, because 'ExecStart' is actually normally 'Executable that is the service', not just script that starts the service, but I think that's a hangover from the self-daemonizing approach to init scripts.
True, but it still makes sense to reason about them this way. Say you have an HTTP server:

- on start: start the server

- on stop: do nothing, because you are already terminating the server

But suppose you need to perform an additional task when the server is terminated. That is where you would add a ExecStop command or script.

Then ExecStop is basically on-cleanup, not to-stop. ExecStart really is to-start, not on-start. In the httpd server case, ExecStart runs the httpd command.
If that helps you understand it better, then sure.

All I am trying to say is that the name of the option makes sense as-is: it literally runs the provided command(s) on service stop (“execute on stop”).

Similarly, ExecStart literally means command(s) to execute on start.

If the command runs a blocking daemon/server (usually the case), then the server will be implicitly stopped by definition - because if you’re stopping the systemd service, you’re interrupting any commands that are still running/blocking.

I think part of the problem is with how one "naturally" or "habitually" thinks of a Service. From systemd's perspective/terminology, the Service is the thing that starts and stops. But whether because it is inherently more intuitive, or because of how daemons traditionally worked on *nix, the mind tends to think of the process which the Service starts as the thing which starts and stops. I'm not able to double check currently, but I also think that systemd isn't totally consistent with the mental model it bases the choice of keywords on when it comes to ExecStop, because with that mental model one would expect ExecStop to only run when the Service is stopped from systemd, but I'm fairly sure it runs in other cases when the process started with ExecStart exits, but the service isn't necessarily stopped. I could be wrong about that though.
ExecStop works the way you want for type=forking
Still has the same problem even with type=forking. Only way to get it working was RemainAfterExit=yes
I think you actually probably want type=oneshot (and also RemainAfterExit=yes) for the kind of service you're describing
This was ultimately what I needed to do when I wrote a systemd service that managed some firewall rules. It really was a footgun though, what with having essentially different meanings/purposes for ExecStop whether you’re doing a Type=forking, a Type=oneshot, or a Type=oneshot with RemainAfterExit=yes.

And relatedly, I honestly have no idea when I’d want to use ExecStartPre, or multiple ExecStarts, or ExecStartPost, and so on.

Having different semantics with different proprieties on the same command is really confusing.
I would argue the semantics of ExecStop are always the same. It's the command that's executed to stop the service. On the other hand, what it means for a service to be "running" or "stopping" naturally depends on what type of service it is (i.e., is it a daemon or not?)
Think of it as an enum, Type branches the logic.

    enum Service {
        Exec { ExecStart: String, ... }
        Forking { ExecStart: String, ... }
        OneShot { ExecStart: String, ... }
    }
You can argue that sometimes that ExecStart could be a different term, but it'd still end up being the same across multiple enum variants.
It's been enlightening to me to read through some of the distro-provided .service files to see what can be done, with services I'm more of less familiar with.