One doesn't necessarily need this.
C services need to wait for
S to be ready so that they can open a socket connection to it, then one doesn't necessarily need to do this at all. Rather, one can take advantage of early listening socket opening by service managers.
Several systems, including Laurent Bercot's s6, my nosh toolset, and systemd, have ways in which a listening socket can be opened early on, the very first thing in setting up the service. They all involve something other than the service program opening the listening socket(s), and the service program, when invoked, receiving the listening sockets(s) as already-open file descriptors.
With systemd, specifically, one creates a socket unit that defines the listening socket. systemd opens the socket unit and sets it up so that the kernel networking subsystem is listening for connections; and passes it to the actual service as an open file descriptor when it comes to spawn the process(es) that handle(s) connections to the socket. (It can do this in two ways, just like
inetd could, but a discussion of the details of
Accept=false services is beyond the scope of this answer.)
The important point is that one does not necessarily need more ordering than that. The kernel batches up client connections in a queue until the service program is initialized, and ready to accept them and talk to clients.
When one does, readiness protocols are the thing.
systemd has a set of readiness protocols that it understands, specified service by service with the
Type= setting in the service unit. The particular readiness protocol of interest here is the
notify readiness protocol. With it, systemd is told to expect messages from the service, and when the service is ready it sends a message that flags readiness. systemd delays the activation of the other services until readiness is flagged.
Making use of this involves two things:
- Modifying the code of
S so that it calls something like Pierre-Yves Ritschard's
notify_systemd() function or Cameron T Norman's
- Setting up the service unit for the service with
NotifyAccess=main restriction (which is the default) is because systemd needs to know to ignore messages from mischievous (or just plain faulty) programs, because any process on the system can send messages to systemd's notification socket.
One uses Pierre-Yves Ritschard's or Cameron T Norman's code for preference because it does not exclude the possibility of having this mechanism on UbuntuBSD, Debian FreeBSD, actual FreeBSD, TrueOS, OpenBSD, and so forth; which the code supplied by the systemd authors does exclude.
One trap to avoid is the
systemd-notify program. It has several major problems, not the least of which is that messages sent with it can end up being thrown away unprocessed by systemd. The most major problem in this case is that it doesn't run as the "main" process of the service, so one has to open up the readiness notifications for the service
S to every process on the system with
Another trap to avoid is thinking that the
forking protocol is simpler. It is not. Doing it correctly involves not forking and exiting the parent until (for one thing) all of the program's worker threads are running. This does not match how the overwhelming majority of dæmons that fork actually fork.