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.