Building a (real) Linux daemon with Delphi - Part 1

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:

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! :-(

DemoConsole not working

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

DemoConsole working

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.

WiRL Application console

And yes, of course you can always use another console to log into the system

Multiple consoles

but this is far less than ideal. This is what happens when you start the process with ./DemoConsole:

  1. The process DemoConsole is created
  2. The process inherits stdin, stdout, and stderr from the shell
  3. The process receives SIGHUP signals (possibly) sent to the shell
  4. 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:

  1. The process DemoConsole is created.
  2. The process inherits stdout/stderr from the shell (not the stdin)
  3. As soon as the process tries to read from stdin, the process halts!
  4. 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 redirect stdout/stderr so you have no way to see messages possibly sent to the (original) shell
  • nohup redirect stdout/stderr to nohup.out file, so you can type tail nohup.out to see your messages
  • nohup is defined by POSIX while disown 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!