Added current thread id routine to BeOS code
[pwlib.git] / src / ptlib / unix / svcproc.cxx
blob66841ca3556d27efe9fe3354d6c30979cd872888
1 /*
2 * svcproc.cxx
4 * Service process (daemon) implementation.
6 * Portable Windows Library
8 * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
25 * All Rights Reserved.
27 * Contributor(s): ______________________________________.
29 * $Log$
30 * Revision 1.84 2004/02/22 03:31:50 ykiryanov
31 * Added current thread id routine to BeOS code
33 * Revision 1.83 2003/09/17 09:02:14 csoutheren
34 * Removed memory leak detection code
36 * Revision 1.82 2003/01/14 04:43:21 robertj
37 * Improved output on error in getting service status.
39 * Revision 1.81 2002/12/12 00:54:29 robertj
40 * Fixed issue with setting max file handles causing a log file to be created
41 * as root (real uid) rather than as the user determined by the -u arg.
42 * Utilised new PProcess functions for setting user and group.
44 * Revision 1.80 2002/12/02 08:27:43 robertj
45 * Fixed incorrectly set #if statement from RTEMS patch.
47 * Revision 1.79 2002/12/02 03:57:18 robertj
48 * More RTEMS support patches, thank you Vladimir Nesic.
50 * Revision 1.78 2002/11/02 00:32:21 robertj
51 * Further fixes to VxWorks (Tornado) port, thanks Andreas Sikkema.
53 * Revision 1.77 2002/10/30 03:14:15 robertj
54 * Added tab character between time and thread in system log (for db import).
56 * Revision 1.76 2002/10/23 04:26:27 robertj
57 * Fixed extra debug levels in system log dump.
59 * Revision 1.75 2002/10/17 13:44:27 robertj
60 * Port to RTEMS, thanks Vladimir Nesic.
62 * Revision 1.74 2002/10/17 12:57:24 robertj
63 * Added ability to increase maximum file handles on a process.
65 * Revision 1.73 2002/10/10 04:43:44 robertj
66 * VxWorks port, thanks Martijn Roest
68 * Revision 1.72 2002/10/03 01:27:51 robertj
69 * Added fail safe _exit() as it appears raise(SIGQUIT) does not always dump
70 * core and exit the app causing endless SEGV's to appear the log file!
72 * Revision 1.71 2002/10/01 06:03:44 robertj
73 * Attempt to prevent recursive signals for SEGV etc
75 * Revision 1.70 2002/09/06 13:29:06 robertj
76 * Added missing help line for status command.
78 * Revision 1.69 2002/07/15 06:39:23 craigs
79 * Added function to allow raising of per-process file handle limit
81 * Revision 1.68 2002/06/13 08:50:11 rogerh
82 * GCC 3.1 uses slightly different #includes
84 * Revision 1.67 2002/06/05 12:29:16 craigs
85 * Changes for gcc 3.1
87 * Revision 1.66 2002/04/17 03:31:48 robertj
88 * Renamed system log file member variable to be common with Windows version.
90 * Revision 1.65 2002/03/18 08:03:30 robertj
91 * Fixed hex output of thread pid.
92 * Added ability to have -tk option wich attempt to terminate a daemon and
93 * if no response in 10 seconds kills it.
94 * Fixed leaving .pid file behind when using -k option.
96 * Revision 1.64 2002/02/19 07:12:36 rogerh
97 * Mac Carbon fix
99 * Revision 1.63 2001/12/16 23:40:16 robertj
100 * Fixed system log so does not crash if current thread not created by PWLib.
102 * Revision 1.62 2001/12/09 23:45:20 craigs
103 * Set debugMode flag when in .... debug mode!
105 * Revision 1.61 2001/09/20 05:35:47 robertj
106 * Fixed crash (race condition) if shutdown service via signal and exit main.
108 * Revision 1.60 2001/08/11 15:38:43 rogerh
109 * Add Mac OS Carbon changes from John Woods <jfw@jfwhome.funhouse.com>
111 * Revision 1.59 2001/08/07 03:05:54 robertj
112 * Expanded thread name field width in system log.
114 * Revision 1.58 2001/07/09 04:26:08 yurik
115 * Fixed lack of pthread_self function on BeOS
117 * Revision 1.57 2001/07/04 08:54:23 robertj
118 * Added dump of thread in SEGV signal handler, this one seems to work.
120 * Revision 1.56 2001/07/03 04:41:25 yurik
121 * Corrections to Jac's submission from 6/28
123 * Revision 1.55 2001/06/30 06:59:07 yurik
124 * Jac Goudsmit from Be submit these changes 6/28. Implemented by Yuri Kiryanov
126 * Revision 1.54 2001/05/03 01:13:10 robertj
127 * Closed stdin if in background, should never block in tty I/O if daemon!
129 * Revision 1.53 2001/04/20 05:41:35 craigs
130 * Added ability to set core dump size from command line under Linux
132 * Revision 1.52 2001/04/20 05:08:42 robertj
133 * Removed dump of thread in SEGV signal, it does not work.
135 * Revision 1.51 2001/04/17 03:13:21 robertj
136 * Added dump of thread address in SEGV etc log output.
138 * Revision 1.50 2001/04/05 03:09:29 robertj
139 * Changed so output to PError goes to system log. Useful for asserts.
141 * Revision 1.49 2001/03/29 03:25:03 robertj
142 * Added dumping to log file of SEGV etc if running as daemon.
144 * Revision 1.48 2001/03/28 23:47:26 robertj
145 * Added start log message and version numbers to start and stop log messages
147 * Revision 1.47 2001/03/28 05:36:14 robertj
148 * Added milliseconds to system log time output.
150 * Revision 1.46 2001/03/24 00:49:02 robertj
151 * Added status indication command for services
153 * Revision 1.45 2001/03/23 01:05:32 robertj
154 * Added check that log file is writable after setuid but before fork.
156 * Revision 1.44 2001/03/22 22:48:25 robertj
157 * Fixed errors in usage help text.
159 * Revision 1.43 2001/03/20 06:44:25 robertj
160 * Lots of changes to fix the problems with terminating threads that are I/O
161 * blocked, especially when doing orderly shutdown of service via SIGTERM.
163 * Revision 1.42 2001/03/20 01:04:46 robertj
164 * Fixed some difficulties with terminating a service process from signals or
165 * from simply dropping out of Main().
167 * Revision 1.41 2001/03/19 02:41:13 robertj
168 * Extra trace output on exiting service.
170 * Revision 1.40 2001/03/19 00:20:55 robertj
171 * Added test for if deamon actually stops
173 * Revision 1.39 2001/03/19 00:11:03 robertj
174 * Added information message when killing service.
176 * Revision 1.38 2001/03/14 01:30:35 robertj
177 * Do setgid before so setuid, ie when still root.
179 * Revision 1.37 2001/03/14 01:16:11 robertj
180 * Fixed signals processing, now uses housekeeping thread to handle signals
181 * synchronously. This also fixes issues with stopping PServiceProcess.
183 * Revision 1.36 2001/03/13 03:47:18 robertj
184 * Added ability to set pid file from command line.
186 * Revision 1.35 2001/03/09 06:31:22 robertj
187 * Added ability to set default PConfig file or path to find it.
189 * Revision 1.34 2000/05/02 03:17:13 robertj
190 * Added display of thread name in SystemLog, thanks Ashley Unitt.
192 * Revision 1.33 2000/04/03 18:36:50 robertj
193 * Fix for BeOS support (stupid prototype in system header files).
195 * Revision 1.32 1999/09/14 13:02:53 robertj
196 * Fixed PTRACE to PSYSTEMLOG conversion problem under Unix.
198 * Revision 1.31 1999/08/17 09:29:22 robertj
199 * Added long name versions of parameters.
201 * Revision 1.30 1999/08/12 12:12:47 robertj
202 * GCC 2.95 compatibility.
204 * Revision 1.29 1999/06/23 14:19:46 robertj
205 * Fixed core dump problem with SIGINT/SIGTERM terminating process.
207 * Revision 1.28 1999/05/13 04:44:18 robertj
208 * Added SIGHUP and SIGWINCH handlers to increase and decrease the log levels.
210 * Revision 1.27 1999/03/02 05:41:59 robertj
211 * More BeOS changes
213 * Revision 1.26 1999/01/11 12:10:32 robertj
214 * Improved operating system version display.
216 * Revision 1.26 1999/01/11 05:20:12 robertj
217 * Added OS to the -v display text.
219 * Revision 1.25 1998/12/21 06:37:14 robertj
220 * Fixed GNu warning on solaris x86
222 * Revision 1.24 1998/12/16 12:41:25 robertj
223 * Fixed bug where .ini file is not written when service run as a daemon.
225 * Revision 1.23 1998/11/30 21:52:00 robertj
226 * New directory structure.
228 * Revision 1.22 1998/11/06 03:44:55 robertj
229 * Fixed bug in argument list parsing, not doing it to member variable.
230 * Added check for daemon already running before starting a new daemon.
232 * Revision 1.21 1998/10/11 02:26:46 craigs
233 * Added thread ID to output messages
235 * Revision 1.20 1998/09/24 04:12:20 robertj
236 * Added open software license.
240 #include <ptlib.h>
242 #pragma implementation "svcproc.h"
243 #include <ptlib/svcproc.h>
245 #ifdef P_VXWORKS
246 #include <logLib.h>
247 #define LOG_EMERG 0
248 #define LOG_ALERT 1
249 #define LOG_CRIT 2
250 #define LOG_ERR 3
251 #define LOG_WARNING 4
252 #define LOG_NOTICE 5
253 #define LOG_INFO 6
254 #define LOG_DEBUG 7
255 #else
256 #include <syslog.h>
257 #include <pwd.h>
258 #include <grp.h>
259 #endif
261 #include <stdarg.h>
262 #if (__GNUC__ >= 3)
263 #include <fstream>
264 #else
265 #include <fstream.h>
266 #endif
267 #include <signal.h>
269 #include "uerror.h"
271 #ifdef P_LINUX
272 #include <sys/resource.h>
273 #endif
275 #define new PNEW
278 #define MAX_LOG_LINE_LEN 1024
280 #ifndef P_VXWORKS
281 static int PwlibLogToUnixLog[PSystemLog::NumLogLevels] = {
282 LOG_CRIT, // LogFatal,
283 LOG_ERR, // LogError,
284 LOG_WARNING, // LogWarning,
285 LOG_INFO, // LogInfo,
286 LOG_DEBUG, // LogDebug
287 LOG_DEBUG,
288 LOG_DEBUG,
289 LOG_DEBUG,
290 LOG_DEBUG,
291 LOG_DEBUG
293 #endif // !P_VXWORKS
295 static const char * const PLevelName[PSystemLog::NumLogLevels+1] = {
296 "Message",
297 "Fatal error",
298 "Error",
299 "Warning",
300 "Info",
301 "Debug",
302 "Debug2",
303 "Debug3",
304 "Debug4",
305 "Debug5",
306 "Debug6",
309 #ifdef P_MAC_MPTHREADS
310 // alas, this can't be statically initialized
311 // XXX This ought to be an MPCriticalRegionID, but they're broken in
312 // XXX Mac OS X 10.0.x!
313 static MPSemaphoreID logMutex;
315 // yuck.
316 static void SetUpLogMutex()
318 if (logMutex == 0) {
319 MPSemaphoreID tempCrit;
320 long err = MPCreateSemaphore(1, 1, &tempCrit);
321 PAssertOS(err == 0);
322 if (!OTCompareAndSwap32(0, (UInt32)tempCrit, (UInt32*)&logMutex)) {
323 // lost the race
324 MPDeleteSemaphore(tempCrit);
328 #endif
329 #ifdef P_PTHREADS
331 static pthread_mutex_t logMutex = PTHREAD_MUTEX_INITIALIZER;
333 #endif
335 void PSystemLog::Output(Level level, const char * cmsg)
337 PString systemLogFileName = PServiceProcess::Current().systemLogFileName;
338 if (systemLogFileName.IsEmpty()) {
339 #ifdef P_VXWORKS
340 printf("%s\n",cmsg);
341 logMsg((char *)(const char)cmsg,0,0,0,0,0,0);
342 #else
343 syslog(PwlibLogToUnixLog[level], "%s", cmsg);
344 #endif
346 else {
347 #ifdef P_PTHREADS
348 pthread_mutex_lock(&logMutex);
349 #endif
350 #ifdef P_MAC_MPTHREADS
351 SetUpLogMutex();
352 (void)MPWaitOnSemaphore(logMutex, kDurationForever);
353 #endif
355 ostream * out;
356 if (systemLogFileName == "-")
357 out = &cerr;
358 else
359 out = new ofstream(systemLogFileName, ios::app);
361 PTime now;
362 *out << now.AsString("yyyy/MM/dd hh:mm:ss.uuu\t");
364 PThread * thread = PThread::Current();
365 if (thread == NULL) {
366 #ifdef P_MAC_MPTHREADS
367 unsigned tid = (unsigned)MPCurrentTaskID();
368 #elif defined(P_VXWORKS)
369 unsigned tid = ::taskIdSelf();
370 #elif defined(BE_THREADS)
371 thread_id tid = ::find_thread(NULL);
372 #else
373 unsigned tid = (unsigned) pthread_self();
374 #endif
375 *out << "ThreadID=0x"
376 << setfill('0') << ::hex
377 << setw(8) << tid
378 << setfill(' ') << ::dec;
379 } else {
380 PString threadName = thread->GetThreadName();
381 if (threadName.GetLength() <= 23)
382 *out << setw(23) << threadName;
383 else
384 *out << threadName.Left(10) << "..." << threadName.Right(10);
387 *out << '\t'
388 << PLevelName[level+1]
389 << '\t'
390 << cmsg << endl;
392 if (out != &cerr)
393 delete out;
395 #ifdef P_PTHREADS
396 pthread_mutex_unlock(&logMutex);
397 #endif
398 #ifdef P_MAC_MPTHREADS
399 MPSignalSemaphore(logMutex);
400 #endif
405 int PSystemLog::Buffer::overflow(int c)
407 if (pptr() >= epptr()) {
408 int ppos = pptr() - pbase();
409 char * newptr = string.GetPointer(string.GetSize() + 10);
410 setp(newptr, newptr + string.GetSize() - 1);
411 pbump(ppos);
413 if (c != EOF) {
414 *pptr() = (char)c;
415 pbump(1);
417 return 0;
421 int PSystemLog::Buffer::underflow()
423 return EOF;
427 int PSystemLog::Buffer::sync()
429 PSystemLog::Output(log->logLevel, string);
431 string = PString();
432 char * base = string.GetPointer(10);
433 setp(base, base + string.GetSize() - 1);
434 return 0;
438 PServiceProcess::PServiceProcess(const char * manuf,
439 const char * name,
440 WORD majorVersion,
441 WORD minorVersion,
442 CodeStatus status,
443 WORD buildNumber)
444 : PProcess(manuf, name, majorVersion, minorVersion, status, buildNumber)
446 currentLogLevel = PSystemLog::Warning;
447 isTerminating = FALSE;
451 PServiceProcess::~PServiceProcess()
453 if (!pidFileToRemove)
454 PFile::Remove(pidFileToRemove);
456 #ifndef P_VXWORKS
457 // close the system log
458 if (systemLogFileName.IsEmpty())
459 closelog();
460 #endif // !P_VXWORKS
464 PServiceProcess & PServiceProcess::Current()
466 PProcess & process = PProcess::Current();
467 PAssert(process.IsDescendant(PServiceProcess::Class()), "Not a service process!");
468 return (PServiceProcess &)process;
472 #ifndef P_VXWORKS
473 static int KillProcess(int pid, int sig)
475 if (kill(pid, sig) != 0)
476 return -1;
478 cout << "Sent SIG";
479 if (sig == SIGTERM)
480 cout << "TERM";
481 else
482 cout << "KILL";
483 cout << " to daemon at pid " << pid << ' ' << flush;
485 for (PINDEX retry = 1; retry <= 10; retry++) {
486 PThread::Sleep(1000);
487 if (kill(pid, 0) != 0) {
488 cout << "\nDaemon stopped." << endl;
489 return 0;
491 cout << '.' << flush;
493 cout << "\nDaemon has not stopped." << endl;
495 return 1;
497 #endif // !P_VXWORKS
500 void PServiceProcess::_PXShowSystemWarning(PINDEX code, const PString & str)
502 PSYSTEMLOG(Warning, "PWLib\t" << GetOSClass() << " error #" << code << '-' << str);
506 int PServiceProcess::InitialiseService()
508 #ifndef P_VXWORKS
509 PSetErrorStream(new PSystemLog(PSystemLog::StdError));
510 PTrace::SetStream(new PSystemLog(PSystemLog::Debug3));
511 PTrace::ClearOptions(PTrace::FileAndLine);
512 PTrace::SetOptions(PTrace::SystemLogStream);
513 PTrace::SetLevel(4);
514 debugMode = FALSE;
516 // parse arguments so we can grab what we want
517 PArgList & args = GetArguments();
519 args.Parse("v-version."
520 "d-daemon."
521 "c-console."
522 "h-help."
523 "x-execute."
524 "p-pid-file:"
525 "H-handlemax:"
526 "i-ini-file:"
527 "k-kill."
528 "t-terminate."
529 "s-status."
530 "l-log-file:"
531 "u-uid:"
532 "g-gid:"
533 "C-core-size:");
535 // if only displaying version information, do it and finish
536 if (args.HasOption('v')) {
537 cout << "Product Name: " << productName << endl
538 << "Manufacturer: " << manufacturer << endl
539 << "Version : " << GetVersion(TRUE) << endl
540 << "System : " << GetOSName() << '-'
541 << GetOSHardware() << ' '
542 << GetOSVersion() << endl;
543 return 0;
546 PString pidfilename;
547 if (args.HasOption('p'))
548 pidfilename = args.GetOptionString('p');
549 #ifdef _PATH_VARRUN
550 else
551 pidfilename = _PATH_VARRUN;
552 #endif
554 if (!pidfilename && PDirectory::Exists(pidfilename))
555 pidfilename = PDirectory(pidfilename) + PProcess::Current().GetFile().GetFileName() + ".pid";
557 if (args.HasOption('k') || args.HasOption('t') || args.HasOption('s')) {
558 pid_t pid;
561 ifstream pidfile(pidfilename);
562 if (!pidfile.is_open()) {
563 cout << "Could not open pid file: \"" << pidfilename << "\""
564 " - " << strerror(errno) << endl;
565 return 1;
568 pidfile >> pid;
569 if (pid == 0) {
570 cout << "Illegal format pid file \"" << pidfilename << '"' << endl;
571 return 1;
575 if (args.HasOption('s')) {
576 cout << "Process at " << pid << ' ';
577 if (kill(pid, 0) == 0)
578 cout << "is running.";
579 else if (errno == ESRCH)
580 cout << "does not exist.";
581 else
582 cout << " status could not be determined, error: " << strerror(errno);
583 cout << endl;
584 return 0;
587 int sig = args.HasOption('t') ? SIGTERM : SIGKILL;
588 switch (KillProcess(pid, sig)) {
589 case -1 :
590 break;
591 case 0 :
592 PFile::Remove(pidfilename);
593 return 0;
594 case 1 :
595 if (args.HasOption('t') && args.HasOption('k')) {
596 switch (KillProcess(pid, SIGKILL)) {
597 case -1 :
598 break;
599 case 0 :
600 PFile::Remove(pidfilename);
601 return 0;
602 case 1 :
603 return 2;
606 else
607 return 2;
610 cout << "Could not stop process " << pid <<
611 " - " << strerror(errno) << endl;
612 return 1;
615 // Set the gid we are running under
616 if (args.HasOption('g')) {
617 PString gidstr = args.GetOptionString('g');
618 if (!SetGroupName(gidstr)) {
619 cout << "Could not set GID to \"" << gidstr << "\" - " << strerror(errno) << endl;
620 return 1;
624 // Set the uid we are running under
625 if (args.HasOption('u')) {
626 PString uidstr = args.GetOptionString('u');
627 if (!SetUserName(uidstr)) {
628 cout << "Could not set UID to \"" << uidstr << "\" - " << strerror(errno) << endl;
629 return 1;
633 BOOL helpAndExit = FALSE;
635 // if displaying help, then do it
636 if (args.HasOption('h'))
637 helpAndExit = TRUE;
638 else if (!args.HasOption('d') && !args.HasOption('x')) {
639 cout << "error: must specify one of -v, -h, -t, -k, -d or -x" << endl;
640 helpAndExit = TRUE;
643 // set flag for console messages
644 if (args.HasOption('c')) {
645 systemLogFileName = '-';
646 debugMode = TRUE;
649 if (args.HasOption('l')) {
650 systemLogFileName = args.GetOptionString('l');
651 if (systemLogFileName.IsEmpty()) {
652 cout << "error: must specify file name for -l" << endl;
653 helpAndExit = TRUE;
655 else if (PDirectory::Exists(systemLogFileName))
656 systemLogFileName = PDirectory(systemLogFileName) + PProcess::Current().GetFile().GetFileName() + ".log";
659 if (helpAndExit) {
660 cout << "usage: [-c] -v|-d|-h|-x\n"
661 " -h --help output this help message and exit\n"
662 " -v --version display version information and exit\n"
663 #if !defined(BE_THREADS) && !defined(P_RTEMS)
664 " -d --daemon run as a daemon\n"
665 #endif
666 " -u --uid uid set user id to run as\n"
667 " -g --gid gid set group id to run as\n"
668 " -p --pid-file name or directory for pid file\n"
669 " -t --terminate orderly terminate process in pid file\n"
670 " -k --kill preemptively kill process in pid file\n"
671 " -s --status check to see if daemon is running\n"
672 " -c --console output messages to stdout rather than syslog\n"
673 " -l --log-file file output messages to file or directory instead of syslog\n"
674 " -x --execute execute as a normal program\n"
675 " -i --ini-file set the ini file to use, may be explicit file or\n"
676 " a ':' separated set of directories to search.\n"
677 " -H --handlemax n set maximum number of file handles (set before uid/gid)\n"
678 #ifdef P_LINUX
679 " -C --core-size set the maximum core file size\n"
680 #endif
681 << endl;
682 return 0;
685 // open the system logger for this program
686 if (systemLogFileName.IsEmpty())
687 openlog((char *)(const char *)GetName(), LOG_PID, LOG_DAEMON);
688 else if (systemLogFileName == "-")
689 cout << "All output for " << GetName() << " is to console." << endl;
690 else {
691 ofstream logfile(systemLogFileName, ios::app);
692 if (!logfile.is_open()) {
693 cout << "Could not open log file \"" << systemLogFileName << "\""
694 " - " << strerror(errno) << endl;
695 return 1;
698 PSYSTEMLOG(StdError, "Starting service process \"" << GetName() << "\" v" << GetVersion(TRUE));
700 if (args.HasOption('i'))
701 SetConfigurationPath(args.GetOptionString('i'));
703 if (args.HasOption('H')) {
704 int uid = geteuid();
705 seteuid(getuid()); // Switch back to starting uid for next call
706 SetMaxHandles(args.GetOptionString('H').AsInteger());
707 seteuid(uid);
710 // set the core file size
711 if (args.HasOption('C')) {
712 #ifdef P_LINUX
713 struct rlimit rlim;
714 if (getrlimit(RLIMIT_CORE, &rlim) != 0)
715 cout << "Could not get current core file size : error = " << errno << endl;
716 else {
717 int uid = geteuid();
718 seteuid(getuid()); // Switch back to starting uid for next call
719 int v = args.GetOptionString('C').AsInteger();
720 rlim.rlim_cur = v;
721 if (setrlimit(RLIMIT_CORE, &rlim) != 0)
722 cout << "Could not set current core file size to " << v << " : error = " << errno << endl;
723 else {
724 getrlimit(RLIMIT_CORE, &rlim);
725 cout << "Core file size set to " << rlim.rlim_cur << "/" << rlim.rlim_max << endl;
727 seteuid(uid);
729 #endif
732 #if !defined(BE_THREADS) && !defined(P_RTEMS)
733 if (!args.HasOption('d'))
734 return -1;
736 // Run as a daemon, ie fork
738 if (!pidfilename) {
739 ifstream pidfile(pidfilename);
740 if (pidfile.is_open()) {
741 pid_t pid;
742 pidfile >> pid;
743 if (pid != 0 && kill(pid, 0) == 0) {
744 cout << "Already have daemon running with pid " << pid << endl;
745 return 2;
750 // Need to get rid of the config write thread before fork, as on
751 // pthreads platforms the forked process does not have the thread
752 CommonDestruct();
754 pid_t pid = fork();
755 switch (pid) {
756 case 0 : // The forked process
757 break;
759 case -1 : // Failed
760 cout << "Fork failed creating daemon process." << endl;
761 return 1;
763 default : // Parent process
764 cout << "Daemon started with pid " << pid << endl;
765 if (!pidfilename) {
766 // Write out the child pid to magic file in /var/run (at least for linux)
767 ofstream pidfile(pidfilename);
768 if (pidfile.is_open())
769 pidfile << pid;
770 else
771 cout << "Could not write pid to file \"" << pidfilename << "\""
772 " - " << strerror(errno) << endl;
774 return 0;
777 // Set ourselves as out own process group so we don't get signals
778 // from our parent's terminal (hopefully!)
779 PSETPGRP();
781 CommonConstruct();
783 pidFileToRemove = pidfilename;
785 // Only if we are running in the background as a daemon, we intercept
786 // the core dumping signals so get a message in the log file.
787 signal(SIGSEGV, PXSignalHandler);
788 signal(SIGFPE, PXSignalHandler);
789 signal(SIGBUS, PXSignalHandler);
791 // Also if in background, don't want to get blocked on input from stdin
792 ::close(STDIN_FILENO);
794 #endif // !BE_THREADS && !P_RTEMS
795 #endif // !P_VXWORKS
796 return -1;
799 int PServiceProcess::_main(void *)
801 if ((terminationValue = InitialiseService()) < 0) {
802 // Make sure housekeeping thread is going so signals are handled.
803 SignalTimerChange();
805 terminationValue = 1;
806 if (OnStart()) {
807 terminationValue = 0;
808 Main();
809 Terminate();
813 return terminationValue;
817 BOOL PServiceProcess::OnPause()
819 return TRUE;
822 void PServiceProcess::OnContinue()
826 void PServiceProcess::OnStop()
831 void PServiceProcess::Terminate()
833 if (isTerminating) {
834 // If we are the process itself and another thread is terminating us,
835 // just stop and wait forever for us to go away
836 if (PThread::Current() == this)
837 Sleep(PMaxTimeInterval);
838 PSYSTEMLOG(Error, "Nested call to process termination!");
839 return;
842 isTerminating = TRUE;
844 PSYSTEMLOG(Warning, "Stopping service process \"" << GetName() << "\" v" << GetVersion(TRUE));
846 // Avoid strange errors caused by threads (and the process itself!) being destoyed
847 // before they have EVER been scheduled
848 Yield();
850 // Do the services stop code
851 OnStop();
853 #ifndef P_VXWORKS
854 // close the system log
855 if (systemLogFileName.IsEmpty())
856 closelog();
857 #endif // !P_VXWORKS
859 // Now end the program
860 exit(terminationValue);
863 void PServiceProcess::PXOnAsyncSignal(int sig)
865 const char * sigmsg;
867 // Override the default behavious for these signals as that just
868 // summarily exits the program. Allow PXOnSignal() to do orderly exit.
870 switch (sig) {
871 case SIGINT :
872 case SIGTERM :
873 case SIGHUP :
874 return;
876 case SIGSEGV :
877 sigmsg = "segmentation fault (SIGSEGV)";
878 break;
880 case SIGFPE :
881 sigmsg = "floating point exception (SIGFPE)";
882 break;
884 #ifndef __BEOS__ // In BeOS, SIGBUS is the same value as SIGSEGV
885 case SIGBUS :
886 sigmsg = "bus error (SIGBUS)";
887 break;
888 #endif
889 default :
890 PProcess::PXOnAsyncSignal(sig);
891 return;
894 signal(SIGSEGV, SIG_DFL);
895 signal(SIGFPE, SIG_DFL);
896 signal(SIGBUS, SIG_DFL);
898 static BOOL inHandler = FALSE;
899 if (inHandler) {
900 raise(SIGQUIT); // Dump core
901 _exit(-1); // Fail safe if raise() didn't dump core and exit
904 inHandler = TRUE;
906 #ifdef P_MAC_MPTHREADS
907 unsigned tid = (unsigned)MPCurrentTaskID();
908 #elif defined(P_VXWORKS)
909 unsigned tid = ::taskIdSelf();
910 #elif defined(BE_THREADS)
911 thread_id tid = ::find_thread(NULL);
912 #else
913 unsigned tid = (unsigned) pthread_self();
914 #endif
915 PThread * thread_ptr = activeThreads.GetAt(tid);
917 char msg[200];
918 sprintf(msg, "\nCaught %s, thread_id=%u", sigmsg, tid);
920 if (thread_ptr != NULL) {
921 PString thread_name = thread_ptr->GetThreadName();
922 if (thread_name.IsEmpty())
923 sprintf(&msg[strlen(msg)], " obj_ptr=%p", thread_ptr);
924 else {
925 strcat(msg, " name=");
926 strcat(msg, thread_name);
930 strcat(msg, ", aborting.\n");
932 if (systemLogFileName.IsEmpty()) {
933 #ifdef P_VXWORKS
934 logMsg((char *)msg,0,0,0,0,0,0);
935 #else
936 syslog(LOG_CRIT, msg);
937 closelog();
938 #endif // !P_VXWORKS
940 else {
941 #ifdef P_VXWORKS
942 int fd = open(systemLogFileName, O_WRONLY|O_APPEND, FWRITE|FAPPEND);
943 #else
944 int fd = open(systemLogFileName, O_WRONLY|O_APPEND);
945 #endif // !P_VXWORKS
946 if (fd >= 0) {
947 write(fd, msg, strlen(msg));
948 close(fd);
952 raise(SIGQUIT); // Dump core
953 _exit(-1); // Fail safe if raise() didn't dump core and exit
957 void PServiceProcess::PXOnSignal(int sig)
959 switch (sig) {
960 case SIGINT :
961 case SIGTERM :
962 Terminate();
963 break;
965 case SIGUSR1 :
966 OnPause();
967 break;
969 case SIGUSR2 :
970 OnContinue();
971 break;
973 case SIGHUP :
974 if (currentLogLevel < PSystemLog::NumLogLevels-1) {
975 currentLogLevel = (PSystemLog::Level)(currentLogLevel+1);
976 PSystemLog s(PSystemLog::StdError);
977 s << "Log level increased to " << PLevelName[currentLogLevel+1];
979 break;
981 #ifdef SIGWINCH
982 case SIGWINCH :
983 if (currentLogLevel > PSystemLog::Fatal) {
984 currentLogLevel = (PSystemLog::Level)(currentLogLevel-1);
985 PSystemLog s(PSystemLog::StdError);
986 s << "Log level decreased to " << PLevelName[currentLogLevel+1];
988 break;
989 #endif