Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

daemon_unix.cc

Go to the documentation of this file.
00001 //                            Package   : omniEvents
00002 // daemon_unix.h              Created   : 2004/06/29
00003 //                            Author    : Alex Tingle
00004 //
00005 //    Copyright (C) 2004 Alex Tingle.
00006 //
00007 //    This file is part of the omniEvents application.
00008 //
00009 //    omniEvents is free software; you can redistribute it and/or
00010 //    modify it under the terms of the GNU Lesser General Public
00011 //    License as published by the Free Software Foundation; either
00012 //    version 2.1 of the License, or (at your option) any later version.
00013 //
00014 //    omniEvents is distributed in the hope that it will be useful,
00015 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 //    Lesser General Public License for more details.
00018 //
00019 //    You should have received a copy of the GNU Lesser General Public
00020 //    License along with this library; if not, write to the Free Software
00021 //    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 //
00023 
00024 #include "daemon.h"
00025 #include "main.h"
00026 #include "daemon_unix.h"
00027 
00028 #define NEED_PACKAGE_INFO
00029 #ifdef HAVE_CONFIG_H
00030 #  include "config.h"
00031 #endif
00032 
00033 #ifdef HAVE_IOSTREAM
00034 #  include <iostream>
00035 #  include <fstream>
00036 #else
00037 #  include <iostream.h>
00038 #  include <fstream.h>
00039 #endif
00040 
00041 #ifdef HAVE_STD_IOSTREAM
00042 using namespace std;
00043 #endif
00044 
00045 #include <stdlib.h> // exit, on_exit
00046 #include <errno.h> // errno
00047 
00048 #ifdef HAVE_UNISTD_H
00049 #  include <unistd.h> // fork, umask, setsid, dup2, chdir, close
00050 #endif
00051 
00052 #ifdef HAVE_SYS_TYPES_H
00053 #  include <sys/types.h> // fork, umask, open
00054 #endif
00055 
00056 #ifdef HAVE_SYS_STAT_H
00057 #  include <sys/stat.h> //open
00058 #endif
00059 
00060 #ifdef HAVE_FCNTL_H
00061 #  include <fcntl.h> // open
00062 #endif
00063 
00064 #ifdef HAVE_SYSLOG_H
00065 #  include <syslog.h> // openlog, syslog
00066 #endif
00067 
00068 #ifdef HAVE_STRING_H
00069 #  include <string.h> // strerror
00070 #endif
00071 
00072 #ifdef HAVE_SIGNAL_H
00073 #include <signal.h> // kill
00074 #endif
00075 
00076 #include <string>
00077 
00078 // Forward declaration of omniORB::setLogFunction()
00079 namespace omniORB {
00080   void setLogFunction(void (*logFunction)(const char*));
00081 }
00082 
00083 namespace OmniEvents {
00084 
00085 #define STRERR_FILE_LINE strerror(errno)<<" "<<__FILE__<<":"<<__LINE__
00086 
00087 #define PIPE_READ  0
00088 #define PIPE_WRITE 1
00089 
00090 
00094 DaemonImpl daemon;
00095 
00096 Daemon::Daemon(int&,char**&)
00097 {
00098   // Initialise the DaemonImpl singleton.
00099   daemon._tracefile=NULL;
00100   daemon._foreground=false;
00101   daemon._pidfile=NULL;
00102   daemon._pipe[0]=daemon._pipe[1]=-1;
00103   daemon._havePidfile=false;
00104   daemon._haveParent=false;
00105   daemon._haveSyslog=false;
00106 }
00107 void Daemon::tracefile(const char* val) { daemon.tracefile(val); }
00108 void Daemon::pidfile(const char* val)   { daemon.pidfile(val); }
00109 void Daemon::foreground(bool val)       { daemon.foreground(val); }
00110 void Daemon::daemonize()                { daemon.daemonize(); }
00111 void Daemon::runningOk()                { daemon.runningOk(); }
00112 Daemon::~Daemon()                       { daemon.shutdown(0); }
00113 
00114 void shutdown0(void)       { daemon.shutdown(0); } 
00115 void shutdown2(int s,void*){ daemon.shutdown(s); } 
00116 
00118 
00119 DaemonImpl::DaemonImpl(){}
00120 
00121 
00122 DaemonImpl::~DaemonImpl()
00123 {
00124   delete[] _pidfile;
00125   delete[] _tracefile;
00126   _pidfile=NULL;
00127   _tracefile=NULL;
00128 }
00129 
00130 
00131 void DaemonImpl::tracefile(const char* val)
00132 {
00133   _tracefile=::strdup(val);
00134 }
00135 
00136 
00137 void DaemonImpl::foreground(bool val)
00138 {
00139   _foreground=val;
00140 }
00141 
00142 
00143 void DaemonImpl::pidfile(const char* val)
00144 {
00145   string pidfileStr =val;
00146   if(pidfileStr[0]!='/')
00147       pidfileStr=string("/var/run/")+pidfileStr;
00148   DaemonImpl::_pidfile=::strdup(pidfileStr.c_str());
00149 }
00150 
00151 
00152 void DaemonImpl::initialize(int&,char**&)
00153 {
00154   // Does nothing on Unix
00155 }
00156 
00157 
00158 void DaemonImpl::daemonize()
00159 {
00160   // Register the shutdown function.
00161 #ifdef HAVE_ON_EXIT
00162   if( ::on_exit(shutdown2,NULL) <0)
00163 #else
00164   if( ::atexit(shutdown0) <0)
00165 #endif
00166   {
00167     cerr<<"Failed to set exit handler."<<endl;
00168     ::exit(-1);
00169   }
00170 
00171   if(!_foreground)
00172   {
00173     this->fork();
00174     // ...now in the CHILD.
00175   }
00176 
00177   // Check & write the pidfile (if _pidfile is set).
00178   checkPidfileOrShutdown();
00179   writePidfile();
00180 
00181   // Change the file mode mask
00182   ::umask(0);
00183   
00184   // Change the current working directory
00185   if(::chdir("/")!=0)
00186   {
00187     cerr<<STRERR_FILE_LINE<<endl;
00188     ::exit(-1);
00189   }
00190 
00191   // If _tracefile is not set, then use syslog.
00192   if(_tracefile && _tracefile[0]!='\0')
00193   {
00194     redirectStreamsTo(_tracefile);
00195   }
00196   else
00197   {
00198 #ifndef HAVE_OMNIORB3
00199 #  ifdef LOG_PERROR
00200     ::openlog(PACKAGE_NAME ": ",LOG_PID|LOG_PERROR,LOG_DAEMON);
00201 #  else
00202     ::openlog(PACKAGE_NAME ": ",LOG_PID,LOG_DAEMON);
00203 #  endif
00204     _haveSyslog=true;
00205     omniORB::setLogFunction(DaemonImpl::log);
00206 #else
00207     cerr<<"You must use option -t to set the file for trace messages."
00208       "\n(This is because omniORB3 cannot redirect messages to syslog.)"<<endl;
00209     ::exit(-1);
00210 #endif
00211   }
00212 } // end daemonize()
00213 
00214 
00215 void DaemonImpl::runningOk()
00216 {
00217   if(_haveParent)
00218   {
00219     _haveParent=false;
00220     notifyParent(0);
00221   }
00222 
00223   // No longer send syslog messages to stderr.
00224   if(_haveSyslog)
00225   {
00226 #ifdef LOG_PERROR
00227     ::closelog();
00228     // FIXME: Possible race here? If a log message is sent right now.
00229     ::openlog(PACKAGE_NAME ": ",LOG_PID,LOG_DAEMON);
00230 #endif
00231     redirectStreamsTo("/dev/null");
00232   }
00233 }
00234 
00235 
00236 void DaemonImpl::shutdown(int status)
00237 {
00238   // Remove the pidfile.
00239   if(_havePidfile && _pidfile && 0!=::unlink(_pidfile))
00240   {
00241     cerr<<"Failed to remove pidfile '"<<_pidfile<<"': "
00242         <<STRERR_FILE_LINE<<endl;
00243     status=-1;
00244   }
00245   _havePidfile=false;
00246 
00247   // Close syslog.
00248   if(_haveSyslog)
00249   {
00250     _haveSyslog=false;
00251     ::closelog();
00252   }
00253   
00254   // Notify the parent.
00255   if(_haveParent)
00256   {
00257     _haveParent=false;
00258     notifyParent(status);
00259   }
00260 
00261   // outtahere...
00262 }
00263 
00264 
00265 void DaemonImpl::log(const char* message)
00266 {
00267   int priority =LOG_INFO;
00268   // Cut off the redundant package name prefix.
00269   //  "omniEvents: " --> stripped off
00270   //  "omniEvents! " --> stripped off and sets priority to LOG_ERR
00271   const char* mPos( message );
00272   const char* pPos( "omniEvents: " );
00273   while(*mPos && (*mPos==*pPos || *pPos==':'))
00274   {
00275     ++mPos;
00276     ++pPos;
00277     if(!*pPos)
00278     {
00279       switch(message[10])
00280       {
00281         case '!':  priority=LOG_ERR; // ...AND DROPS THROUGH...
00282         case ':':  message=mPos;
00283       }
00284       break; // loop exit
00285     }
00286   }
00287   // Send the message.
00288   ::syslog(priority,message);
00289 #ifndef LOG_PERROR
00290   // If we don't have LOG_PERROR, then we'll have to manually send
00291   // log messages to stderr.
00292   if(daemon._haveParent)
00293       cerr<<message<<flush;
00294 #endif
00295 }
00296 
00297 
00298 void DaemonImpl::checkPidfileOrShutdown()
00299 {
00300   if(!_pidfile)
00301       return;
00302 
00303   // Try to read pidfile.
00304   pid_t pidFromFile =0;
00305   struct stat buf;
00306   if(0==::stat(_pidfile,&buf))
00307   {
00308     if(!S_ISREG(buf.st_mode))
00309     {
00310       cerr<<"Pidfile '"<<_pidfile<<"' is not a regular file."<<endl;
00311       ::exit(-1);
00312     }
00313     try
00314     {
00315       ifstream infile(_pidfile);
00316       infile>>pidFromFile;
00317       infile.close();
00318     }
00319     catch(...)
00320     {
00321       cerr<<"Failed to read pidfile'"<<_pidfile<<"'."<<endl;
00322       ::exit(-1);
00323     }
00324   }
00325   else if(errno!=ENOENT)
00326   {
00327     cerr<<"Failed to stat pidfile '"<<_pidfile<<"': "
00328         <<STRERR_FILE_LINE<<endl;
00329     ::exit(-1);
00330   }
00331 
00332   // If process 'pidFromFile' is running then exit, else remove pidfile.
00333   if(pidFromFile>0)
00334   {
00335     if(0==::kill(pidFromFile,0)) // tests for running 'pidFromFile'.
00336     {
00337       cerr<<"Quitting because process "<<pidFromFile
00338           <<" defined in pidfile '"<<_pidfile<<"'"
00339           <<" is already running."<<endl;
00340       ::exit(-1);
00341     }
00342     else if(errno!=ESRCH)
00343     {
00344       cerr<<"Failed to test for process "<<pidFromFile
00345           <<" defined in pidfile '"<<_pidfile<<"': "
00346           <<STRERR_FILE_LINE<<endl;
00347       ::exit(-1);
00348     }
00349   }
00350 }
00351 
00352 
00353 void DaemonImpl::writePidfile()
00354 {
00355   if(_pidfile)
00356   {
00357     try
00358     {
00359 #ifdef FSTREAM_OPEN_PROT
00360       ofstream outfile(_pidfile,ios::out|ios::trunc,0644);
00361 #else
00362       ofstream outfile(_pidfile,ios::out|ios::trunc);
00363 #endif
00364       outfile<<::getpid()<<endl;
00365       outfile.close();
00366       // Tell shutdown() that the pidfile needs to be cleared away.
00367       _havePidfile=true;
00368     }
00369     catch(...)
00370     {
00371       cerr<<"Failed to write pidfile '"<<_pidfile<<"'."<<endl;
00372       ::exit(-1);
00373     }
00374   }
00375 }
00376 
00377 
00378 void DaemonImpl::fork()
00379 {
00380   if( ::pipe(_pipe) <0)
00381   {
00382     cerr<<"Failed to open pipe: "<<STRERR_FILE_LINE<<endl;
00383     ::exit(-1);
00384   }
00385 
00386   // Fork off from the parent process
00387   pid_t pid =::fork();
00388   if(pid<0)
00389   {
00390     cerr<<STRERR_FILE_LINE<<endl;
00391     ::exit(-1);
00392   }
00393   else if(pid>0)
00394   {
00395     //
00396     // Now in the PARENT
00397     //
00398 
00399     // Close the write end of the pipe.
00400     if( ::close(_pipe[PIPE_WRITE]) <0)
00401         cerr<<"Failed to close pipe: "<<STRERR_FILE_LINE<<endl;
00402 
00403     ::_exit(waitForChild()); // Exit without flushing buffers
00404   }
00405 
00406   //
00407   // ...now in the CHILD.
00408   //
00409 
00410   _haveParent=true;
00411 
00412   // Close the read end of the pipe
00413   if( ::close(_pipe[PIPE_READ]) <0)
00414       cerr<<"Failed to close pipe: "<<STRERR_FILE_LINE<<endl;
00415 
00416   // Create a new SID for the child process
00417   pid_t sid =::setsid();
00418   if(sid<0)
00419   {
00420     cerr<<STRERR_FILE_LINE<<endl;
00421     ::exit(-1);
00422   }
00423 }
00424 
00425 
00426 void DaemonImpl::redirectStreamsTo(const char* filename)
00427 {
00428   if(openFileFor(STDIN_FILENO,"/dev/null",O_RDONLY)<0)
00429   {
00430     cerr<<"Failed to open /dev/null for STDIN: "<<STRERR_FILE_LINE<<endl;
00431     ::exit(-1);
00432   }
00433   if(openFileFor(STDOUT_FILENO,filename,O_WRONLY|O_CREAT|O_APPEND)<0)
00434   {
00435     cerr<<"Failed to open "<<filename<<" for STDOUT: "<<STRERR_FILE_LINE<<endl;
00436     ::exit(-1);
00437   }
00438   if(openFileFor(STDERR_FILENO,filename,O_WRONLY|O_CREAT|O_APPEND)<0)
00439   {
00440     cerr<<"Failed to open "<<filename<<" for STDERR: "<<STRERR_FILE_LINE<<endl;
00441     ::exit(-1);
00442   }
00443 }
00444 
00445 
00446 int DaemonImpl::openFileFor(int fd, const char* filename, int flags)
00447 {
00448   int newfd =::open(filename,flags,0644);
00449   if(newfd<0)
00450       return -1;
00451   if(newfd==fd)
00452       return fd;
00453   if(::dup2(newfd,fd)<0) // replace fd with a copy of newfd
00454       return -1;
00455   ::close(newfd);
00456   return fd;
00457 }
00458 
00459 
00460 int DaemonImpl::waitForChild()
00461 {
00462   int status =-1;
00463   ssize_t bytes =::read(_pipe[PIPE_READ],&status,sizeof(status));
00464   if(bytes<sizeof(status))
00465   {
00466     status=-1;
00467     if(bytes<0)
00468        cerr<<"Parent failed to read result from pipe: "<<STRERR_FILE_LINE<<endl;
00469   }
00470   if( ::close(_pipe[PIPE_READ]) !=0)
00471       cerr<<"Failed to close pipe: "<<STRERR_FILE_LINE<<endl;
00472 
00473   return status;
00474 }
00475 
00476 
00477 void DaemonImpl::notifyParent(int status)
00478 {
00479   ssize_t r =::write(_pipe[PIPE_WRITE],&status,sizeof(status));
00480   if(r<sizeof(status))
00481   {
00482     if(r<0)
00483         cerr<<"read() failed while writing return value to pipe: "
00484             <<STRERR_FILE_LINE<<endl;
00485     else
00486         cerr<<"write() too short while writing return value from pipe: "
00487             <<STRERR_FILE_LINE<<endl;
00488   }
00489   if( ::close(_pipe[PIPE_WRITE]) !=0)
00490       cerr<<"Failed to close pipe: "<<STRERR_FILE_LINE<<endl;
00491 }
00492 
00493 } // end namespace OmniEvents

Generated on Fri Aug 26 20:56:13 2005 for OmniEvents by  doxygen 1.4.3-20050530