Building a (real) Linux daemon with Delphi - Part 1
Building a (real) Linux daemon with Delphi Serie
- Part 1: Process management in Linux
- Part 2: Build a Linux daemon with Delphi
This will be a multipart article, in this first part I explore the options of launching a (console) Linux program in background, in the next part I will focus on building a proper Linux Daemon with Delphi. So let's start!
Introduction
In the past weeks I've been busy converting some REST services, built with the WiRL REST library, to Linux. Compiling the projects to Linux was the easy part, thanks to the great job Embarcadero did on the Linux compiler and the RTL.
For debugging and testing purposes, a console Linux application was sufficient but to deploy the service (actually on a AWS Linux instance) a simple console was not an option, so I've decided to build a Linux Daemon for that purpose.
Problem is that Delphi (at least in 10.2 Tokyo) doesn't support the creation of a Linux Daemon with an application template, so you have to build one for yourself using directly Linux system calls.
My setup: tools & applications
In order to be able to run, control and run application under Linux you must obviously have some (additional) software installed in your machine, my setup:
- Windows 10 as host OS
- VirtualBox as virtualization software (https://www.virtualbox.org)
- Debian 9 Stretch as Linux OS (https://www.debian.org) or Ubuntu Server 16.04.2 LTS (https://www.ubuntu.com/server)
- Putty as terminal emulator (https://www.chiark.greenend.org.uk/~sgtatham/putty) or the Ubuntu Bash Shell if you have Windows 10 Anniversary Edition (https://msdn.microsoft.com/en-us/commandline/wsl/install_guide)
- WinSCP as SCP client for Windows (https://winscp.net) or the Ubuntu Bash Shell if you have Windows 10 Anniversary Edition (https://msdn.microsoft.com/en-us/commandline/wsl/install_guide)
- Delphi 10.2 Tokyo to create applications for Linux, what else? :-) (https://www.embarcadero.com/products/delphi)
Even if you are not an expert on Linux I encourage you to try to install it on a virtual machine, you'll find that the process is very easy and fast.
There are already some articles that explain in detail the process, and I'm actually writing an article on installing Debian 9 from scratch and configuring it for Delphi development, so.. stay tuned.
Launching a process in Linux
First of all if you have the Linux machine already configured for Delphi development, you can create a simple console application in Delphi, this one would be fine for our "launching program" exercise:
program DemoConsole;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
begin
try
Writeln('Hello World');
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Now if you want to be able to launch your program under Linux independently (not via Delphi IDE) you must deploy your application (the "Deploy DemoConsole" menu command in the Delphi IDE). Now you have to launch your application in Linux, how to do it?
Remember that you have already opened several programs by typing ls, apt-get, wget, etc...
so, to launch a Linux command (program) you simply have to write the command name in the shell, so let's try with our newly created Delphi program DemoConsole
:
paolor@debian-9:˜$ DemoConsole
and...... doesn't work! :-(
.
Introducing the ./
(dot-slash)
The previous command failed because in Linux (Unix) the current directory is not in the current path (for security reasons) so you have to specify yourself the path:
paolor@debian-9:˜$ ./DemoConsole
where ./
is the current directory
Great, now you program is (finally) running, but you notice that you can't use that console (for other commands and so on) until your process is terminated.
And yes, of course you can always use another console to log into the system
but this is far less than ideal. This is what happens when you start the process with ./DemoConsole
:
- The process
DemoConsole
is created - The process inherits
stdin
,stdout
, andstderr
from the shell - The process receives
SIGHUP
signals (possibly) sent to the shell - The shell is blocked until the process terminates
As you can see the 3rd and 4th points are bad things and the 2nd is not very useful if it's not an interactive console application (like services)
The &
option (starting a background job)
Another option is to launch your process appending a &
after the process name:
paolor@debian-9:˜$ ./DemoConsole &
so the console will be immediately available to you! Great! it seems to be the perfect way to launch your service but.. it's not! So here what happens:
- The process
DemoConsole
is created. - The process inherits
stdout/stderr
from the shell (not thestdin
) - As soon as the process tries to read from
stdin
, the process halts! - The process receives
SIGHUP
signals sent to the shell.
The 3rd item is quite dangerous and killing the shell still kills your process.
nohup
and disown
on the rescue
So even with &
the background process in not detached from the console, to do so we have 2 options: the commands disown
and nohup
. Basically they do the same thing, they detach the process from the shell isolating it from SIGHUP
signals, but they are slightly different though:
to use nohup
:
paolor@debian-9:˜$ nohup ./ConsoleDemo &
to use disown
:
paolor@debian-9:˜$ ./ConsoleDemo &
paolor@debian-9:˜$ disown
or if you forget to put your program in background:
paolor@debian-9:˜$ ./ConsoleDemo
paolor@debian-9:˜$ <Ctrl-Z>
paolor@debian-9:˜$ bg
paolor@debian-9:˜$ disown
other differences:
disown
doesn't redirectstdout/stderr
so you have no way to see messages possibly sent to the (original) shellnohup
redirectstdout/stderr
tonohup.out
file, so you can typetail nohup.out
to see your messagesnohup
is defined by POSIX whiledisown
is not, this means that some shell have it, some have not.
I largely prefer nohup
to disown
because it's easier to script, and the nohup.out file is can be useful (although it doesn't replace a proper logging system)
Conclusions
In this article we explored the options for launching our (standard) console application (that must behave as a service) in Linux and the preferred way seems to be the nohup
command. This solution works but a real Linux daemon is by far the better way to deploy "services" on the Linux platform and even more so is the "official" way in UNIX OSes but to be able to build a daemon in Delphi you have to wait the next article, stay tuned!