OmniEvents
daemon_unix.cc
Go to the documentation of this file.
1 // Package : omniEvents
2 // daemon_unix.h Created : 2004/06/29
3 // Author : Alex Tingle
4 //
5 // Copyright (C) 2004 Alex Tingle.
6 //
7 // This file is part of the omniEvents application.
8 //
9 // omniEvents is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // omniEvents is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 //
23 
24 #include "daemon.h"
25 #include "main.h"
26 #include "daemon_unix.h"
27 
28 #define NEED_PACKAGE_INFO
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32 
33 #ifdef HAVE_IOSTREAM
34 # include <iostream>
35 # include <fstream>
36 #else
37 # include <iostream.h>
38 # include <fstream.h>
39 #endif
40 
41 #ifdef HAVE_STD_IOSTREAM
42 using namespace std;
43 #endif
44 
45 #include <stdlib.h> // exit, on_exit
46 #include <errno.h> // errno
47 
48 #ifdef HAVE_UNISTD_H
49 # include <unistd.h> // fork, umask, setsid, dup2, chdir, close
50 #endif
51 
52 #ifdef HAVE_SYS_TYPES_H
53 # include <sys/types.h> // fork, umask, open
54 #endif
55 
56 #ifdef HAVE_SYS_STAT_H
57 # include <sys/stat.h> //open
58 #endif
59 
60 #ifdef HAVE_FCNTL_H
61 # include <fcntl.h> // open
62 #endif
63 
64 #ifdef HAVE_SYSLOG_H
65 # include <syslog.h> // openlog, syslog
66 #endif
67 
68 #ifdef HAVE_STRING_H
69 # include <string.h> // strerror
70 #endif
71 
72 #ifdef HAVE_SIGNAL_H
73 #include <signal.h> // kill
74 #endif
75 
76 #include <string>
77 
78 // Forward declaration of omniORB::setLogFunction()
79 namespace omniORB {
80  void setLogFunction(void (*logFunction)(const char*));
81 }
82 
83 namespace OmniEvents {
84 
85 #define STRERR_FILE_LINE strerror(errno)<<" "<<__FILE__<<":"<<__LINE__
86 
87 #define PIPE_READ 0
88 #define PIPE_WRITE 1
90 
95 
96 Daemon::Daemon(int&,char**&)
97 {
98  // Initialise the DaemonImpl singleton.
99  daemon._tracefile=NULL;
100  daemon._foreground=false;
101  daemon._pidfile=NULL;
102  daemon._pipe[0]=daemon._pipe[1]=-1;
103  daemon._havePidfile=false;
104  daemon._haveParent=false;
105  daemon._haveSyslog=false;
106 }
107 void Daemon::tracefile(const char* val) { daemon.tracefile(val); }
108 void Daemon::pidfile(const char* val) { daemon.pidfile(val); }
109 void Daemon::foreground(bool val) { daemon.foreground(val); }
110 void Daemon::daemonize() { daemon.daemonize(); }
111 void Daemon::runningOk() { daemon.runningOk(); }
112 Daemon::~Daemon() { daemon.shutdown(0); }
113 
114 void shutdown0(void) { daemon.shutdown(0); }
115 void shutdown2(int s,void*){ daemon.shutdown(s); }
116 
118 
119 DaemonImpl::DaemonImpl(){}
120 
121 
122 DaemonImpl::~DaemonImpl()
123 {
124  delete[] _pidfile;
125  delete[] _tracefile;
126  _pidfile=NULL;
127  _tracefile=NULL;
128 }
129 
130 
131 void DaemonImpl::tracefile(const char* val)
132 {
133  _tracefile=::strdup(val);
134 }
135 
136 
137 void DaemonImpl::foreground(bool val)
138 {
139  _foreground=val;
140 }
141 
142 
143 void DaemonImpl::pidfile(const char* val)
144 {
145  string pidfileStr =val;
146  if(pidfileStr[0]!='/')
147  pidfileStr=string("/var/run/")+pidfileStr;
148  DaemonImpl::_pidfile=::strdup(pidfileStr.c_str());
149 }
150 
151 
152 void DaemonImpl::initialize(int&,char**&)
153 {
154  // Does nothing on Unix
155 }
156 
157 
158 void DaemonImpl::daemonize()
159 {
160  // Register the shutdown function.
161 #ifdef HAVE_ON_EXIT
162  if( ::on_exit(shutdown2,NULL) <0)
163 #else
164  if( ::atexit(shutdown0) <0)
165 #endif
166  {
167  cerr<<"Failed to set exit handler."<<endl;
168  ::exit(-1);
169  }
170 
171  if(!_foreground)
172  {
173  this->fork();
174  // ...now in the CHILD.
175  }
176 
177  // Check & write the pidfile (if _pidfile is set).
178  checkPidfileOrShutdown();
179  writePidfile();
180 
181  // Change the file mode mask
182  ::umask(0);
183 
184  // Change the current working directory
185  if(::chdir("/")!=0)
186  {
187  cerr<<STRERR_FILE_LINE<<endl;
188  ::exit(-1);
189  }
190 
191  // If _tracefile is not set, then use syslog.
192  if(_tracefile && _tracefile[0]!='\0')
193  {
194  redirectStreamsTo(_tracefile);
195  }
196  else
197  {
198 #ifndef HAVE_OMNIORB3
199 # ifdef LOG_PERROR
200  ::openlog(PACKAGE_NAME ": ",LOG_PID|LOG_PERROR,LOG_DAEMON);
201 # else
202  ::openlog(PACKAGE_NAME ": ",LOG_PID,LOG_DAEMON);
203 # endif
204  _haveSyslog=true;
205  omniORB::setLogFunction(DaemonImpl::log);
206 #else
207  cerr<<"You must use option -t to set the file for trace messages."
208  "\n(This is because omniORB3 cannot redirect messages to syslog.)"<<endl;
209  ::exit(-1);
210 #endif
211  }
212 } // end daemonize()
213 
214 
215 void DaemonImpl::runningOk()
216 {
217  if(_haveParent)
218  {
219  _haveParent=false;
220  notifyParent(0);
221  }
222 
223  // No longer send syslog messages to stderr.
224  if(_haveSyslog)
225  {
226 #ifdef LOG_PERROR
227  ::closelog();
228  // FIXME: Possible race here? If a log message is sent right now.
229  ::openlog(PACKAGE_NAME ": ",LOG_PID,LOG_DAEMON);
230 #endif
231  redirectStreamsTo("/dev/null");
232  }
233 }
234 
235 
236 void DaemonImpl::shutdown(int status)
237 {
238  // Remove the pidfile.
239  if(_havePidfile && _pidfile && 0!=::unlink(_pidfile))
240  {
241  cerr<<"Failed to remove pidfile '"<<_pidfile<<"': "
242  <<STRERR_FILE_LINE<<endl;
243  status=-1;
244  }
245  _havePidfile=false;
246 
247  // Close syslog.
248  if(_haveSyslog)
249  {
250  _haveSyslog=false;
251  ::closelog();
252  }
253 
254  // Notify the parent.
255  if(_haveParent)
256  {
257  _haveParent=false;
258  notifyParent(status);
259  }
260 
261  // outtahere...
262 }
263 
264 
265 void DaemonImpl::log(const char* message)
266 {
267  int priority =LOG_INFO;
268  // Cut off the redundant package name prefix.
269  // "omniEvents: " --> stripped off
270  // "omniEvents! " --> stripped off and sets priority to LOG_ERR
271  const char* mPos( message );
272  const char* pPos( "omniEvents: " );
273  while(*mPos && (*mPos==*pPos || *pPos==':'))
274  {
275  ++mPos;
276  ++pPos;
277  if(!*pPos)
278  {
279  switch(message[10])
280  {
281  case '!': priority=LOG_ERR; // ...AND DROPS THROUGH...
282  case ':': message=mPos;
283  }
284  break; // loop exit
285  }
286  }
287  // Send the message.
288  ::syslog(priority,"%s",message);
289 #ifndef LOG_PERROR
290  // If we don't have LOG_PERROR, then we'll have to manually send
291  // log messages to stderr.
292  if(daemon._haveParent)
293  cerr<<message<<flush;
294 #endif
295 }
296 
297 
298 void DaemonImpl::checkPidfileOrShutdown()
299 {
300  if(!_pidfile)
301  return;
302 
303  // Try to read pidfile.
304  pid_t pidFromFile =0;
305  struct stat buf;
306  if(0==::stat(_pidfile,&buf))
307  {
308  if(!S_ISREG(buf.st_mode))
309  {
310  cerr<<"Pidfile '"<<_pidfile<<"' is not a regular file."<<endl;
311  ::exit(-1);
312  }
313  try
314  {
315  ifstream infile(_pidfile);
316  infile>>pidFromFile;
317  infile.close();
318  }
319  catch(...)
320  {
321  cerr<<"Failed to read pidfile'"<<_pidfile<<"'."<<endl;
322  ::exit(-1);
323  }
324  }
325  else if(errno!=ENOENT)
326  {
327  cerr<<"Failed to stat pidfile '"<<_pidfile<<"': "
328  <<STRERR_FILE_LINE<<endl;
329  ::exit(-1);
330  }
331 
332  // If process 'pidFromFile' is running then exit, else remove pidfile.
333  if(pidFromFile>0)
334  {
335  if(0==::kill(pidFromFile,0)) // tests for running 'pidFromFile'.
336  {
337  cerr<<"Quitting because process "<<pidFromFile
338  <<" defined in pidfile '"<<_pidfile<<"'"
339  <<" is already running."<<endl;
340  ::exit(-1);
341  }
342  else if(errno!=ESRCH)
343  {
344  cerr<<"Failed to test for process "<<pidFromFile
345  <<" defined in pidfile '"<<_pidfile<<"': "
346  <<STRERR_FILE_LINE<<endl;
347  ::exit(-1);
348  }
349  }
350 }
351 
352 
353 void DaemonImpl::writePidfile()
354 {
355  if(_pidfile)
356  {
357  try
358  {
359 #ifdef FSTREAM_OPEN_PROT
360  ofstream outfile(_pidfile,ios::out|ios::trunc,0644);
361 #else
362  ofstream outfile(_pidfile,ios::out|ios::trunc);
363 #endif
364  outfile<<::getpid()<<endl;
365  outfile.close();
366  // Tell shutdown() that the pidfile needs to be cleared away.
367  _havePidfile=true;
368  }
369  catch(...)
370  {
371  cerr<<"Failed to write pidfile '"<<_pidfile<<"'."<<endl;
372  ::exit(-1);
373  }
374  }
375 }
376 
377 
378 void DaemonImpl::fork()
379 {
380  if( ::pipe(_pipe) <0)
381  {
382  cerr<<"Failed to open pipe: "<<STRERR_FILE_LINE<<endl;
383  ::exit(-1);
384  }
385 
386  // Fork off from the parent process
387  pid_t pid =::fork();
388  if(pid<0)
389  {
390  cerr<<STRERR_FILE_LINE<<endl;
391  ::exit(-1);
392  }
393  else if(pid>0)
394  {
395  //
396  // Now in the PARENT
397  //
398 
399  // Close the write end of the pipe.
400  if( ::close(_pipe[PIPE_WRITE]) <0)
401  cerr<<"Failed to close pipe: "<<STRERR_FILE_LINE<<endl;
402 
403  ::_exit(waitForChild()); // Exit without flushing buffers
404  }
405 
406  //
407  // ...now in the CHILD.
408  //
409 
410  _haveParent=true;
411 
412  // Close the read end of the pipe
413  if( ::close(_pipe[PIPE_READ]) <0)
414  cerr<<"Failed to close pipe: "<<STRERR_FILE_LINE<<endl;
415 
416  // Create a new SID for the child process
417  pid_t sid =::setsid();
418  if(sid<0)
419  {
420  cerr<<STRERR_FILE_LINE<<endl;
421  ::exit(-1);
422  }
423 }
424 
425 
426 void DaemonImpl::redirectStreamsTo(const char* filename)
427 {
428  if(openFileFor(STDIN_FILENO,"/dev/null",O_RDONLY)<0)
429  {
430  cerr<<"Failed to open /dev/null for STDIN: "<<STRERR_FILE_LINE<<endl;
431  ::exit(-1);
432  }
433  if(openFileFor(STDOUT_FILENO,filename,O_WRONLY|O_CREAT|O_APPEND)<0)
434  {
435  cerr<<"Failed to open "<<filename<<" for STDOUT: "<<STRERR_FILE_LINE<<endl;
436  ::exit(-1);
437  }
438  if(openFileFor(STDERR_FILENO,filename,O_WRONLY|O_CREAT|O_APPEND)<0)
439  {
440  cerr<<"Failed to open "<<filename<<" for STDERR: "<<STRERR_FILE_LINE<<endl;
441  ::exit(-1);
442  }
443 }
444 
445 
446 int DaemonImpl::openFileFor(int fd, const char* filename, int flags)
447 {
448  int newfd =::open(filename,flags,0644);
449  if(newfd<0)
450  return -1;
451  if(newfd==fd)
452  return fd;
453  if(::dup2(newfd,fd)<0) // replace fd with a copy of newfd
454  return -1;
455  ::close(newfd);
456  return fd;
457 }
458 
459 
460 int DaemonImpl::waitForChild()
461 {
462  int status =-1;
463  ssize_t bytes =::read(_pipe[PIPE_READ],&status,sizeof(status));
464  if(bytes<sizeof(status))
465  {
466  status=-1;
467  if(bytes<0)
468  cerr<<"Parent failed to read result from pipe: "<<STRERR_FILE_LINE<<endl;
469  }
470  if( ::close(_pipe[PIPE_READ]) !=0)
471  cerr<<"Failed to close pipe: "<<STRERR_FILE_LINE<<endl;
472 
473  return status;
474 }
475 
476 
477 void DaemonImpl::notifyParent(int status)
478 {
479  ssize_t r =::write(_pipe[PIPE_WRITE],&status,sizeof(status));
480  if(r<sizeof(status))
481  {
482  if(r<0)
483  cerr<<"read() failed while writing return value to pipe: "
484  <<STRERR_FILE_LINE<<endl;
485  else
486  cerr<<"write() too short while writing return value from pipe: "
487  <<STRERR_FILE_LINE<<endl;
488  }
489  if( ::close(_pipe[PIPE_WRITE]) !=0)
490  cerr<<"Failed to close pipe: "<<STRERR_FILE_LINE<<endl;
491 }
492 
493 } // end namespace OmniEvents
#define PACKAGE_NAME
Definition: config.h:158
#define STRERR_FILE_LINE
Definition: daemon_unix.cc:85
#define PIPE_READ
Definition: daemon_unix.cc:87
#define PIPE_WRITE
Definition: daemon_unix.cc:88
#define STDOUT_FILENO
Definition: events.cc:75
#define STDIN_FILENO
Definition: events.cc:74
DaemonImpl daemon
Singleton - only at file scope.
Definition: daemon_unix.cc:94
void shutdown2(int s, void *)
Param to on_exit().
Definition: daemon_unix.cc:115
void shutdown0(void)
Param to atexit().
Definition: daemon_unix.cc:114
void setLogFunction(void(*logFunction)(const char *))
Utility class that contains various methods for running omniEvents as a Unix daemon.
Definition: daemon_unix.h:38
void runningOk()
Called to signal that all startup operations have completed OK.
Definition: daemon_unix.cc:215
void foreground(bool val)
Set _foreground.
Definition: daemon_unix.cc:137
void tracefile(const char *val)
Set _tracefile.
Definition: daemon_unix.cc:131
int _pipe[2]
Unnamed pipe for child->parent comms.
Definition: daemon_unix.h:45
void pidfile(const char *val)
Set _pidfile.
Definition: daemon_unix.cc:143
char * _tracefile
The tracefile name (if any).
Definition: daemon_unix.h:42
bool _foreground
TRUE for debug mode (run in foreground)
Definition: daemon_unix.h:43
void daemonize()
Puts the current process into the background.
Definition: daemon_unix.cc:158
bool _haveSyslog
Should we close syslog before quitting?
Definition: daemon_unix.h:48
char * _pidfile
The pidfile name (if any).
Definition: daemon_unix.h:44
void shutdown(int status)
Exit handler called (indirectly) by ::on_exit() - shuts down the daemon.
Definition: daemon_unix.cc:236
bool _haveParent
Is there a parent for us to clean up?
Definition: daemon_unix.h:47
bool _havePidfile
Is there a pidfile for us to clean up?
Definition: daemon_unix.h:46