• Recent Tutorials

  • Tripping The Class Fantastic: Introductory Quickie

    Example Daemon

    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.