A daemon is nothing other than any normal program under linux. Systemd is simply another program that can be used to start and communicate with the process of that program.
So writing a program to run as a service is not magic, it's pretty much a normal program.
So let's look into systemd a little bit further. Take for example this service file for nginx:
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network.target remote-fs.target nss-lookup.target
[Service]
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx -g "daemon off;"
ExecReload=/bin/kill -s HUP $MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=mixed
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Here are a few interesting things to see. The first few lines in the service category are PIDFile, ExecStartPre and ExecStart. You can find more information about those in
the documentation but to make it short:
PIDFile references a file where your process writes it's pid into. This is than used by systemd later on to communicate with the process.
If you use such a pid file, your service of course needs to create that file. If you don't use it, systemd will simply use the pid it started. You need such a file if your process is simply a startup process that will start the main process, whose pid you want systemd to communicate with.
ExecStartPre: is a command executed before the Execution (there is also ExecStartPost for executing afterwards)
ExecStart is the command that will actually start your service. This is executed when systemctl start ... is called. This could for example simply be the (absolute) path of your program.
ExecReload is the command that will be executed when the daemon should reload it's config. In this case it uses
kill to send a
HUP signal to the main process.
There is also
ExecStop where you can enter a command which should be executed when the process is stopped. Nginx doesn't use it, it uses
KillSignal and
KillMode (
documented here), which basically tells systemd to send
SIGQUIT to the process.
If nothing of that kind is defined,
SIGTERM will be sent.
So at it's core, systemd service configurations are just a collection of commands executed. If I type in systemctl start nginx, first the command of ExecStartPre and after that the command in ExecStart will be executed. If i type systemctl reload nginx, the command in ExecReload will be executed and if I type in systemctl stop nginx the SIGQUIT will be sent to nginx (which is basically the same as the command "kill -s QUIT $MAINPID" in ExecStop).
So thats rather simple. Now on how to write a program with fpc that can be used with this.
This completely depends on how you want to do this. The usual way, such as it is used by nginx, is to use signals. Signals are simply a way for process to communicate asynchronous with each other. When a program sends a signal to another program, the program will be interrupted and the registered signal handler will be executed.
For example:
program simpleService;
{$Mode ObjFPC}{$H+}
uses baseunix;
var
Stopped: Boolean;
procedure handleSigTerm(signum: CInt); cdecl;
begin
Stopped := True;
end;
begin
Stopped := False;
fpSignal(SigTerm, SignalHandler(@handleSigTerm));
While not Stopped do;
end.
This program registers a handler for the signal SigTerm, and then starts a loop until stopped is True. When sigterm is sent to the program it should set stopped to true and stop the infinite loop.
If you compile this simple program and write a simple service file like:
[Unit]
Description=A program that will increase your cpu load
[Service]
ExecStart=/path/to/the/compiled/executable
[Install]
WantedBy=multi-user.target
and save this file as /etc/systemd/system/cpukiller.service, you can reload your daemons via: systemctl daemon-reload and then use it. With systemctl start cpukiller it would start your process (which should be notable by having a process taking 100% of your CPU on one core) and with systemctl stop cpukiller the sigterm should be sent to that process and it should stop pretty immediately.
But signals are only one way to communicate with your process. You could do anything you like. For example your process could start a TCP server, and your Reload and Stop commands start a simple client which sends commands to that TCP server. Another possibility is that you use these commands to write to special files, and you process reads those files to get it's commands.
You can also not do anything, and let systemd kill your program by sending SIGKILL (which does not have a handler, this force quits the process), which is the easiest if you don't have any graceful shut down actions to take (like closing connections or something similar). The usual way to configure systemd is to first try to send SigTerm (or SIGQUIT or SIGINT) and after a timeout of a few seconds force kill it with SIGKILL.
Your possibilities are endless, you basically need to write your process and than some commands with which you an communicate with that process. How you do that is compeletly up to you, there is no given protocol how this has to work, just do it as you see fit.
That said, signals are of course pretty neat, it's like they where developed for this kind of thing.
PS: these fp... functions in the unix and baseunix unit are Ansi-C and Posix compatible functions. For information about those you could: 1. Check out man pages (like
signal(2),
signal(7) and
sigaction(2)), which you can easiely find via google "man functionName" where functionName is for example signal for fpsignal, 2. Reading the
C standard (for more detailed information) or 3. reading the
posix standardA little bit more information about signals, which is more detailed than the man pages and easier to digest than the posix and c standard linked above, can be found here:
http://kirste.userpage.fu-berlin.de/chemnet/use/info/libc/libc_21.htmlAlso useful
https://en.cppreference.com/w/c/program/signalThe documentation for the clusterfuck that is systemd can be found here
https://www.freedesktop.org/software/systemd/man/index.html most importantly for you is the
systemd.service documentation but also
systemd.kill and
systemd.exec. I recommend you approaching this by examples (here i found this:
https://www.shellhacks.com/systemd-service-file-example/), and google for the different commands you see (for shell commands search the man pages, for the systemd configuration google for "systemd configName").