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
42using 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()
79namespace omniORB {
80 void setLogFunction(void (*logFunction)(const char*));
81}
82
83namespace OmniEvents {
84
85#define STRERR_FILE_LINE strerror(errno)<<" "<<__FILE__<<":"<<__LINE__
86
87#define PIPE_READ 0
88#define PIPE_WRITE 1
90
95
96Daemon::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}
107void Daemon::tracefile(const char* val) { daemon.tracefile(val); }
108void Daemon::pidfile(const char* val) { daemon.pidfile(val); }
109void Daemon::foreground(bool val) { daemon.foreground(val); }
113
114void shutdown0(void) { daemon.shutdown(0); }
115void shutdown2(int s,void*){ daemon.shutdown(s); }
116
118
120
121
123{
124 delete[] _pidfile;
125 delete[] _tracefile;
126 _pidfile=NULL;
127 _tracefile=NULL;
128}
129
130
131void DaemonImpl::tracefile(const char* val)
132{
133 _tracefile=::strdup(val);
134}
135
136
138{
139 _foreground=val;
140}
141
142
143void 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
152void DaemonImpl::initialize(int&,char**&)
153{
154 // Does nothing on Unix
155}
156
157
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).
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 {
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;
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
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
236void 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
265void 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.
293 cerr<<message<<flush;
294#endif
295}
296
297
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
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
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
426void 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
446int 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
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
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
#define PIPE_READ
#define PIPE_WRITE
#define STDOUT_FILENO
Definition events.cc:75
#define STDIN_FILENO
Definition events.cc:74
DaemonImpl daemon
Singleton - only at file scope.
void shutdown2(int s, void *)
Param to on_exit().
void shutdown0(void)
Param to atexit().
void setLogFunction(void(*logFunction)(const char *))
void pidfile(const char *val)
Set _pidfile.
Daemon()
No implementation.
void foreground(bool val)
Set _foreground.
void runningOk()
Called to signal that all startup operations have completed OK.
void tracefile(const char *val)
Set _tracefile.
void daemonize()
Redirects output streams to tracefile.
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.
void foreground(bool val)
Set _foreground.
void tracefile(const char *val)
Set _tracefile.
int openFileFor(int fd, const char *filename, int flags)
Opens a (new?) file called 'filename' for writing, and uses it to hijack stream 'fd'.
int _pipe[2]
Unnamed pipe for child->parent comms.
Definition daemon_unix.h:45
void checkPidfileOrShutdown()
If pidfile exists & contains a running process then shutdown() (Unix).
void pidfile(const char *val)
Set _pidfile.
int waitForChild()
Called by the parent process (Unix).
void notifyParent(int status)
Tells the parent to exit with the given status (Unix).
static void log(const char *message)
Callback, used as a parameter to omniORB::setLogFunction().
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.
void initialize(int &, char **&)
Does nothing on Unix.
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 redirectStreamsTo(const char *filename)
Redirect stdout & stderr to filename.
void shutdown(int status)
Exit handler called (indirectly) by ::on_exit() - shuts down the daemon.
void fork()
Performs the actual fork.
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