OmniEvents
|
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,"%s",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