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   : omniIFR
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 omniIFR application.
00008 //
00009 //    omniIFR 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 //    omniIFR 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 Omniifr {
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   // Cut off the redundant packageNamePrefix.
00268   static const char*  packageNamePrefix    ="omniIFR: ";
00269   static const size_t packageNamePrefixLen =::strlen(packageNamePrefix);
00270   if(0==::strncmp(message,packageNamePrefix,packageNamePrefixLen))
00271       message+=packageNamePrefixLen;
00272   // Send the message.
00273   ::syslog(LOG_INFO,message);
00274 #ifndef LOG_PERROR
00275   // If we don't have LOG_PERROR, then we'll have to manually send
00276   // log messages to stderr.
00277   if(daemon._haveParent)
00278       cerr<<message<<flush;
00279 #endif
00280 }
00281 
00282 
00283 void DaemonImpl::checkPidfileOrShutdown()
00284 {
00285   if(!_pidfile)
00286       return;
00287 
00288   // Try to read pidfile.
00289   pid_t pidFromFile =0;
00290   struct stat buf;
00291   if(0==::stat(_pidfile,&buf))
00292   {
00293     if(!S_ISREG(buf.st_mode))
00294     {
00295       cerr<<"Pidfile '"<<_pidfile<<"' is not a regular file."<<endl;
00296       ::exit(-1);
00297     }
00298     try
00299     {
00300       ifstream infile(_pidfile);
00301       infile>>pidFromFile;
00302       infile.close();
00303     }
00304     catch(...)
00305     {
00306       cerr<<"Failed to read pidfile'"<<_pidfile<<"'."<<endl;
00307       ::exit(-1);
00308     }
00309   }
00310   else if(errno!=ENOENT)
00311   {
00312     cerr<<"Failed to stat pidfile '"<<_pidfile<<"': "
00313         <<STRERR_FILE_LINE<<endl;
00314     ::exit(-1);
00315   }
00316 
00317   // If process 'pidFromFile' is running then exit, else remove pidfile.
00318   if(pidFromFile>0)
00319   {
00320     if(0==::kill(pidFromFile,0)) // tests for running 'pidFromFile'.
00321     {
00322       cerr<<"Quitting because process "<<pidFromFile
00323           <<" defined in pidfile '"<<_pidfile<<"'"
00324           <<" is already running."<<endl;
00325       ::exit(-1);
00326     }
00327     else if(errno!=ESRCH)
00328     {
00329       cerr<<"Failed to test for process "<<pidFromFile
00330           <<" defined in pidfile '"<<_pidfile<<"': "
00331           <<STRERR_FILE_LINE<<endl;
00332       ::exit(-1);
00333     }
00334   }
00335 }
00336 
00337 
00338 void DaemonImpl::writePidfile()
00339 {
00340   if(_pidfile)
00341   {
00342     try
00343     {
00344 #ifdef FSTREAM_OPEN_PROT
00345       ofstream outfile(_pidfile,ios::out|ios::trunc,0644);
00346 #else
00347       ofstream outfile(_pidfile,ios::out|ios::trunc);
00348 #endif
00349       outfile<<::getpid()<<endl;
00350       outfile.close();
00351       // Tell shutdown() that the pidfile needs to be cleared away.
00352       _havePidfile=true;
00353     }
00354     catch(...)
00355     {
00356       cerr<<"Failed to write pidfile '"<<_pidfile<<"'."<<endl;
00357       ::exit(-1);
00358     }
00359   }
00360 }
00361 
00362 
00363 void DaemonImpl::fork()
00364 {
00365   if( ::pipe(_pipe) <0)
00366   {
00367     cerr<<"Failed to open pipe: "<<STRERR_FILE_LINE<<endl;
00368     ::exit(-1);
00369   }
00370 
00371   // Fork off from the parent process
00372   pid_t pid =::fork();
00373   if(pid<0)
00374   {
00375     cerr<<STRERR_FILE_LINE<<endl;
00376     ::exit(-1);
00377   }
00378   else if(pid>0)
00379   {
00380     //
00381     // Now in the PARENT
00382     //
00383 
00384     // Close the write end of the pipe.
00385     if( ::close(_pipe[PIPE_WRITE]) <0)
00386         cerr<<"Failed to close pipe: "<<STRERR_FILE_LINE<<endl;
00387 
00388     ::_exit(waitForChild()); // Exit without flushing buffers
00389   }
00390 
00391   //
00392   // ...now in the CHILD.
00393   //
00394 
00395   _haveParent=true;
00396 
00397   // Close the read end of the pipe
00398   if( ::close(_pipe[PIPE_READ]) <0)
00399       cerr<<"Failed to close pipe: "<<STRERR_FILE_LINE<<endl;
00400 
00401   // Create a new SID for the child process
00402   pid_t sid =::setsid();
00403   if(sid<0)
00404   {
00405     cerr<<STRERR_FILE_LINE<<endl;
00406     ::exit(-1);
00407   }
00408 }
00409 
00410 
00411 void DaemonImpl::redirectStreamsTo(const char* filename)
00412 {
00413   if(openFileFor(STDIN_FILENO,"/dev/null",O_RDONLY)<0)
00414   {
00415     cerr<<"Failed to open /dev/null for STDIN: "<<STRERR_FILE_LINE<<endl;
00416     ::exit(-1);
00417   }
00418   if(openFileFor(STDOUT_FILENO,filename,O_WRONLY|O_CREAT|O_APPEND)<0)
00419   {
00420     cerr<<"Failed to open "<<filename<<" for STDOUT: "<<STRERR_FILE_LINE<<endl;
00421     ::exit(-1);
00422   }
00423   if(openFileFor(STDERR_FILENO,filename,O_WRONLY|O_CREAT|O_APPEND)<0)
00424   {
00425     cerr<<"Failed to open "<<filename<<" for STDERR: "<<STRERR_FILE_LINE<<endl;
00426     ::exit(-1);
00427   }
00428 }
00429 
00430 
00431 int DaemonImpl::openFileFor(int fd, const char* filename, int flags)
00432 {
00433   int newfd =::open(filename,flags,0644);
00434   if(newfd<0)
00435       return -1;
00436   if(newfd==fd)
00437       return fd;
00438   if(::dup2(newfd,fd)<0) // replace fd with a copy of newfd
00439       return -1;
00440   ::close(newfd);
00441   return fd;
00442 }
00443 
00444 
00445 int DaemonImpl::waitForChild()
00446 {
00447   int status =-1;
00448   ssize_t bytes =::read(_pipe[PIPE_READ],&status,sizeof(status));
00449   if(bytes<sizeof(status))
00450   {
00451     status=-1;
00452     if(bytes<0)
00453        cerr<<"Parent failed to read result from pipe: "<<STRERR_FILE_LINE<<endl;
00454   }
00455   if( ::close(_pipe[PIPE_READ]) !=0)
00456       cerr<<"Failed to close pipe: "<<STRERR_FILE_LINE<<endl;
00457 
00458   return status;
00459 }
00460 
00461 
00462 void DaemonImpl::notifyParent(int status)
00463 {
00464   ssize_t r =::write(_pipe[PIPE_WRITE],&status,sizeof(status));
00465   if(r<sizeof(status))
00466   {
00467     if(r<0)
00468         cerr<<"read() failed while writing return value to pipe: "
00469             <<STRERR_FILE_LINE<<endl;
00470     else
00471         cerr<<"write() too short while writing return value from pipe: "
00472             <<STRERR_FILE_LINE<<endl;
00473   }
00474   if( ::close(_pipe[PIPE_WRITE]) !=0)
00475       cerr<<"Failed to close pipe: "<<STRERR_FILE_LINE<<endl;
00476 }
00477 
00478 } // end namespace Omniifr

Generated on Fri Mar 4 13:03:22 2005 for OmniIFR by  doxygen 1.4.1