Recent

Author Topic: Need example on creating a daemon to run as Linux service  (Read 3381 times)

BosseB

  • Sr. Member
  • ****
  • Posts: 484
Need example on creating a daemon to run as Linux service
« on: June 21, 2020, 11:06:06 pm »
I have been trying on/off for the last 4 months to port a Windows service application from Delphi 2007 to FPC 3.0.4 using Lazarus 2.0.8.
The problem is that TService in Delphi is the base of the application and it interfaces to the operating system (Windows) in ways I have no knowledge about. It "just worked" on Windows at the time.

I struggled a lot including trying my hand at Lazarus TDaemon but without much success. I have converted most of the active application code and "all" that is needed is to plug it into the framework of the service concept.

So now I need to start fresh by making a simple program that can interface to Linux systemd running as a Linux service.
Once that works the plan is to plug the main application code into it.
This program thus needs to react to the control signals from systemd on Debian Buster (Actually Raspbian Buster since I am on Raspberry Pi4).
So it needs to respond to the control signals sent to it when the system wants to shut it down or restart. I have no good idea on how this can be done.
FpSigAction and FpSignal have been mentioned in Google search results, but examples are hard to find (at least such that I understand them)...

While googling I found this webpage where a similar project is discussed back in 2008.
But I do not really understand that example either.

Can someone advice on a good example for a Linux service app framework?
« Last Edit: June 22, 2020, 12:33:55 am by BosseB »
--
Bo Berglund
Sweden

Warfley

  • Hero Member
  • *****
  • Posts: 2051
Re: Need example on creating a daemon to run as Linux service
« Reply #1 on: June 22, 2020, 01:36:20 am »
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:
Quote
[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:
Code: Pascal  [Select][+][-]
  1. program simpleService;
  2.  
  3. {$Mode ObjFPC}{$H+}
  4.  
  5. uses baseunix;
  6. var
  7.   Stopped: Boolean;
  8.  
  9. procedure handleSigTerm(signum: CInt); cdecl;
  10. begin
  11.   Stopped := True;
  12. end;
  13.  
  14. begin
  15.   Stopped := False;
  16.   fpSignal(SigTerm, SignalHandler(@handleSigTerm));
  17.   While not Stopped do;
  18. 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:
Quote
[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 standard

A 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.html
Also useful https://en.cppreference.com/w/c/program/signal

The 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").
« Last Edit: June 22, 2020, 01:55:06 am by Warfley »

BosseB

  • Sr. Member
  • ****
  • Posts: 484
Re: Need example on creating a daemon to run as Linux service
« Reply #2 on: June 22, 2020, 07:58:02 am »
Amazing!
What a great answer!  :D :D :D
Thank you so much!

As I understand it:
This really means that I can convert my Delphi TService program to a regular non-service Fpc/Lazarus program that needs to be started anytime it is used and debug the Linux porting that way.

Then I only need to add the signal handler so it can be stopped sensibly when running as a systemd service, automatically started with the system (which is what is needed since it is a data monitoring system on a remote location).

Whenever it is shut down it needs to power off external equipment via relay controls, so a sensible way to bring it down is needed.
--
Bo Berglund
Sweden

Thaddy

  • Hero Member
  • *****
  • Posts: 19017
  • Glad to be alive.
Re: Need example on creating a daemon to run as Linux service
« Reply #3 on: June 22, 2020, 01:56:45 pm »
It is not amazing.
There is a good example as standard: under /packages/fcl-extra/examples
Works for both linux and windows.

Again, explore the code....that is already provided..... ;D :P
Recovered from removal of tumor in tongue following tongue reconstruction with a part from my leg.

Warfley

  • Hero Member
  • *****
  • Posts: 2051
Re: Need example on creating a daemon to run as Linux service
« Reply #4 on: June 22, 2020, 02:30:44 pm »
There is a good example as standard: under /packages/fcl-extra/examples

The fpcs daemonapp is nice if you need a windows service, because microsoft decided to make such things really complicated. But if you need a daemon for linux only, this is a complete overkill because linux is pretty simple.

No need for threading, of the 8 methods the daemon can have (start, stop, pause, continue, execute, shutdown, install, uninstall) only 2 work for linux (start and stop) and just the boilerplate code required for a daemon app to run is longer than the complete daemon I've written in in my previous post. But the main advantage about using standard applications as linux services is simple, you can develop and debug them like any normal program, you can even use then as a normal program if you don't have admin rights to register them as a service, and to run them as a service you simply need a 5-10 line systemd, systemV or BSD init script.

For a Linux only daemon I highly recommend to not use the daemonapp, because this massively overcomplicates things. It is very complicated because windows is. But if you are not constraint by the quirks of windows, no need to deal with such a mess.

BosseB

  • Sr. Member
  • ****
  • Posts: 484
Re: Need example on creating a daemon to run as Linux service
« Reply #5 on: June 22, 2020, 02:46:33 pm »
There is a good example as standard: under /packages/fcl-extra/examples

The fpcs daemonapp is nice if you need a windows service, because microsoft decided to make such things really complicated. But if you need a daemon for linux only, this is a complete overkill because linux is pretty simple.

..cut..

For a Linux only daemon I highly recommend to not use the daemonapp, because this massively overcomplicates things. It is very complicated because windows is. But if you are not constraint by the quirks of windows, no need to deal with such a mess.
In my last attempt at porting the Windows service application I was steered towards using TDaemon, but then I drowned in the mess and had to give up completely...
I simply could not understand how it actually worked.

With the suggestion from you and the detailed explanation I am on much firmer ground.
As you write I realized that I could just create my server as a regular simple program and test it out normally, then add the signal handling and run it as a systemd service on Linux.

That is why I used the word Amazing, for both the solution itself and the detailed explanation which I could in fact understand.

Thanks again!
--
Bo Berglund
Sweden

Warfley

  • Hero Member
  • *****
  • Posts: 2051
Re: Need example on creating a daemon to run as Linux service
« Reply #6 on: June 22, 2020, 03:19:09 pm »
Some suggestions about developing a linux program that can be used as a service:
1. Use a logger component that depending on a parameter given will log via writeln or to a file (or both), this way you can simply debug it with console ouptut while running normal
2. you can register multiple signals for the same handler, so you can register SigInt and SigTerm for the same closing handler, this way, when executing your program in the console, hitting ctrl+c (which sends sigint) will stop the program exactly the same as SigTerm by systemd (as it will trigger the same handler)
3. Use the command line tool kill to test your signal handlers, kill -s INT pid will send SigINT, kill -s TERM pid will send SigTerm and so on (see manpage for kill)
4. use different file paths for running as daemon and local (i.e. relative paths when running local, absolute paths to /etc/myconfigdirectory when running as daemon)

You can then use a command line switch to enable daemon mode (like -d), this will then choose the correct logger, choose the correct file paths and such stuff. So when deploying your program all you need to do is deploy your systemd config with the ExecStart command of "/absolute/path/to/your/progrm -d" and it will work fine, while you can use the exact same program without daemon mode

 

TinyPortal © 2005-2018