The following code example is real code from one of our projects, so I'm fairly confident that it works. The only part I haven't fully tested is the user switching code, you have been warned. You may reuse this code, providing the copyright notices remain intact. Neither I nor Outer Reaches Studios accepts any responsibility if this code goes boom and causes problems. You use it at your own risk.
Code:
program sampledaemond;
(*
Sample Daemon
Replace the routines 'initialise' and 'deinitialise' to create and cleanup
your actual process object. If you need to periodically call a routine in
your object to make it operate, then you should do this in 'main' at the
point indicated.
The paths used by this example are fairly standard on RedHat systems, but will
undoubtedly need tweaking for other distributions.
Original Code - Copyright (C) 2001-2005 Outer Reaches Studios (http://www.outer-reaches.co.uk)
*)
{$APPTYPE CONSOLE}
uses
SysUtils,
Libc,
IniFiles;
const
// This constant is used to provide the name that will be used for the
// PID file. Ideally it should be the same as the compiled executable
_appName = 'sampledaemond';
// Specify the name of the file that will be used to hold our basic
// configuration
_configFileName = 'sampledaemon.ini';
var
// Daemon variables
// These are used to store base config information and also to handle state
terminateDaemon : boolean;
becomeDaemon : boolean;
becameDaemon : boolean;
abortingDaemon : boolean;
changeUID : boolean;
newUID : integer;
newGID : integer;
oldUID : integer;
oldGID : integer;
iniFile : TIniFile;
(*---Main Routines-----------------------------------------------------------*)
function initialise:boolean;
begin
// Initialise your process here.
// You should return TRUE if this completes successfully
end;
procedure deinitialise;
begin
// De-initialise your process and do any cleanup here
end;
procedure main;
begin
// Wait here in the main loop until terminated
repeat
// If you need to periodically call a method of your process
// object to make it work, you should do so here. A better way
// to handle that however would be to create a timer object
// inside your process object and use that to make the call
// You may need to adjust the sleep period if the process is
// chewing clock cycles
sleep(100);
until (terminateDaemon);
end;
(*---Daemon Support Routines-------------------------------------------------*)
(*---Pid File Routine--------------------------------------------------------*)
procedure pidFile(writeit:boolean);
var
pFile : textFile;
pFileName : string;
begin
pFileName:='/var/run/'+_appName+'.pid';
assignFile(pFile,pFileName);
if writeIt then
begin
try
rewrite(pFile);
write(pFile,format('%d',[libc.getPid]));
closeFile(pFile);
except
end;
end
else
begin
try
if fileExists(pFileName) then
erase(pFile);
except
end;
end;
end;
(*---SIGTERM/SIGINT/SIGKILL Handler------------------------------------------*)
procedure killDaemon;
begin
terminateDaemon:=true;
end;
(*---Daemon Start Up Code----------------------------------------------------*)
begin
// Initialise our globals
terminateDaemon:=false;
abortingDaemon:=false;
becameDaemon:=false;
becomeDaemon:=false;
changeUID:=false;
// Initialise any variables you need to here. Do NOT create any
// process specific objects here. That should be done using the
// 'Initialise' function above.
// Initialise these to keep the compiler happy, even though they are
// initialised then they are used
newUID:=99;
newGID:=99;
oldUID:=0;
oldGID:=0;
// Read a little about our configuration now (the stuff we need to know
// about switching to demon mode)
try
iniFile:=TIniFile.create('/etc/'+_configFileName);
// Are we to become a damone?
becomeDaemon:=(iniFile.readInteger('DAEMON','RUNASDAEMON',0)=1);
// Are we to run as someone else?
changeUID:=(iniFile.readInteger('DAEMON','DAEMONSWITCHUID',0)=1);
// If we are to run as someone else...
if changeUID then
begin
// Get our current UID and GID
oldUID:=libc.getUID;
oldGID:=libc.getGID;
// Read the UID and GID that we are to become (Default is
// 99... nobody)
newUID:=iniFile.readInteger('DAEMON','NEWDAEMONUID',99);
newGID:=iniFile.readInteger('DAEMON','NEWDAEMONGID',99);
end;
iniFile.free;
except
on e:exception do
begin
writeln('Exception occured accessing /etc/'+
_configFileName+'. The message was '+e.message);
abortingDaemon:=true;
end;
end;
// If we are to run as a daemon and we have managed to read our
if (becomeDaemon) and (not abortingDaemon) then
begin
// Call libc.daemon to fork the process and become
// a daemon
libc.daemon(0,0);
// Create our PID file
pidFile(true);
// Hook the signals we want to listen to
// to terminate
libc.signal(libc.SIGINT,@killDaemon);
libc.signal(libc.SIGTERM,@killDaemon);
libc.signal(libc.SIGKILL,@killDaemon);
// If we are to run as someone else, switch ID's
if changeUID then
begin
libc.setuid(newUID);
libc.setgid(newGID);
end;
becameDaemon:=true;
end;
// If all is well...
if (not abortingDaemon) then
begin
// If we initialise...
if initialise then
begin
// Enter the main loop until we are tol to stop
main;
// Deinitialise and cleanup
deinitialise;
end;
end;
// If we became a daemon...
if becameDaemon then
begin
// If we switched ID's, then switch back to our originals
if changeUID then
begin
libc.setgid(oldGID);
libc.setuid(oldUID);
end;
// Get rid of the PID file
pidFile(false);
end;
end.


Menu
Categories
Content Tags






