2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #import <Foundation/Foundation.h>
23 #include <NGObjWeb/NGObjWeb.h>
25 #if defined(__CYGWIN32__) || defined(__MINGW32__)
27 int WOWatchDogApplicationMain
28 (NSString *appName, int argc, const char *argv[])
30 /* no watchdog support on Win* */
31 return WOApplicationMain(appName, argc, argv);
40 #include <sys/types.h>
41 #include <sys/unistd.h>
44 static pid_t child = -1;
45 static NSString *pidFile = nil;
46 static time_t lastFailExit = 0;
47 static unsigned failExitCount = 0;
48 static BOOL killedChild = NO;
50 static void killChild(void) {
54 fprintf(stderr, "watchdog[%i]: terminating child %i ..\n", getpid(), child);
56 if (kill(child, SIGTERM) == 0) {
57 waitpid(child, &status, 0);
60 fprintf(stderr, " terminated child %i", child);
62 if (WIFEXITED(status))
63 fprintf(stderr, " exit=%i", WEXITSTATUS(status));
64 if (WIFSIGNALED(status))
65 fprintf(stderr, " signal=%i", WTERMSIG(status));
67 fprintf(stderr, ".\n");
73 else if (kill(child, SIGKILL)) {
74 waitpid(child, &status, 0);
77 fprintf(stderr, " killed child %i", child);
79 if (WIFEXITED(status))
80 fprintf(stderr, " exit=%i", WEXITSTATUS(status));
81 if (WIFSIGNALED(status))
82 fprintf(stderr, " signal=%i", WTERMSIG(status));
84 fprintf(stderr, ".\n");
93 static void _writePid(NSString *pidFile) {
94 if ([pidFile length] > 0) {
97 if ((pf = fopen([pidFile cString], "w"))) {
98 fprintf(pf, "%i\n", getpid());
104 static void _delPid(void) {
105 if ([pidFile length] > 0) {
106 if (unlink([pidFile cString]) == 0)
111 static void exitWatchdog(void) {
116 static void wsignalHandler(int _signal) {
120 fprintf(stderr, "[%i]: watchdog handling signal ctrl-c ..\n", getpid());
123 /* shouldn't get here */
129 "[%i]: watchdog handling segmentation fault "
130 "(SERIOUS PROBLEM) ..\n",
134 /* shouldn't get here */
138 /* TERM signal (kill 'pid') */
139 fprintf(stderr, "[%i]: watchdog handling SIGTERM ..\n", getpid());
142 /* shouldn't get here */
146 /* HUP signal (restart children) */
147 fprintf(stderr, "[%i]: watchdog handling SIGHUP ..\n", getpid());
150 signal(_signal, wsignalHandler);
157 fprintf(stderr, "[%i]: watchdog handling signal %i ..\n",
180 // NSLog(@"SIGNAL: SIGCHLD");
181 // fetch return state
184 result = waitpid(-1, &returnStatus, WNOHANG);
186 fprintf(stderr, "[%i]: process %i exited with code %i",
187 getpid(), (int)result, WEXITSTATUS(returnStatus));
189 if (WIFSIGNALED(returnStatus)) {
190 fprintf(stderr, " (terminated due to signal %i%s)",
191 WTERMSIG(returnStatus),
192 WCOREDUMP(returnStatus) ? ", coredump" : "");
194 if (WIFSTOPPED(returnStatus)) {
195 fprintf(stderr, " (stopped due to signal %i)",
196 WSTOPSIG(returnStatus));
199 fprintf(stderr, "\n");
209 fprintf(stderr, "watchdog[%i]: caught signal %i\n", getpid(), _signal);
212 signal(_signal, wsignalHandler);
215 static void signalHandler(int _signal) {
216 fprintf(stderr, "[%i]: handling signal %i ..\n",
222 fprintf(stderr, "[%i]: caught signal SIGPIPE\n", getpid());
226 fprintf(stderr, "[%i]: caught signal %i\n", getpid(), _signal);
229 signal(_signal, signalHandler);
232 int WOWatchDogApplicationMain
233 (NSString *appName, int argc, const char *argv[])
235 NSAutoreleasePool *pool;
238 pool = [[NSAutoreleasePool alloc] init];
239 #if LIB_FOUNDATION_LIBRARY || defined(GS_PASS_ARGUMENTS)
241 extern char **environ;
242 [NSProcessInfo initializeWithArguments:(void*)argv count:argc
243 environment:(void*)environ];
247 ud = [NSUserDefaults standardUserDefaults];
249 /* default is to use the watch dog! */
250 /* Note: the Defaults.plist is not yet loaded at this stage! */
251 if ([ud objectForKey:@"WOUseWatchDog"] != nil) {
252 if (![ud boolForKey:@"WOUseWatchDog"])
253 return WOApplicationMain(appName, argc, argv);
263 isVerbose = [[ud objectForKey:@"watchdog_verbose"] boolValue];
264 pidFile = [[[ud objectForKey:@"watchdog_pidfile"] stringValue] copy];
266 /* write current pid to pidfile */
269 /* register exit handler */
270 atexit(exitWatchdog);
272 /* register signal handlers of watch dog */
273 signal(SIGPIPE, wsignalHandler);
274 signal(SIGCHLD, wsignalHandler);
275 signal(SIGINT, wsignalHandler);
276 signal(SIGTERM, wsignalHandler);
277 signal(SIGKILL, wsignalHandler);
278 signal(SIGHUP, wsignalHandler);
283 time_t clientStartTime;
285 clientStartTime = time(NULL);
288 if ((child = fork()) == -1) {
289 fprintf(stderr, "[%i]: fork failed: %s\n", getpid(), strerror(errno));
293 fprintf(stderr, " fork failed %i times, sleeping 60 seconds ..\n",
304 signal(SIGPIPE, SIG_DFL);
305 signal(SIGCHLD, SIG_DFL);
306 signal(SIGINT, SIG_DFL);
307 signal(SIGTERM, SIG_DFL);
308 signal(SIGKILL, SIG_DFL);
311 fprintf(stderr, "starting child %i ..\n", getpid());
313 pidFile = [pidFile stringByAppendingPathExtension:@"child"];
318 exit(WOApplicationMain(appName, argc, argv));
320 /* shouldn't even get here ! */
321 fprintf(stderr, "internal server error !\n");
325 /* parent (watch dog) */
328 time_t clientStopTime;
334 fprintf(stderr, "forked child process %i (#%i) ..\n",
341 if ((result = waitpid(child, &status, 0)) == -1) {
348 "### waiting for child %i (#%i) failed: %s\n",
349 child, forkCount, strerror(errno));
353 clientStopTime = time(NULL);
354 uptime = clientStopTime - clientStartTime;
356 if (WIFSIGNALED(status)) {
358 "### child %i (#%i) was terminated by signal %i "
360 child, forkCount, WTERMSIG(status), uptime);
362 lastFailExit = time(NULL);
365 else if (WIFEXITED(status)) {
368 if ((exitCode = WEXITSTATUS(status)) != 0) {
374 if (failExitCount > 0) {
375 unsigned secsSinceLastFail;
377 secsSinceLastFail = (now - lastFailExit);
379 if (secsSinceLastFail > 120) {
380 /* reset fail count */
383 else if (failExitCount > 20) {
384 printf("### child %i (#%i) already failed %i times "
385 "in the last %i seconds, stopping watchdog !\n",
386 child, forkCount, failExitCount, secsSinceLastFail);
395 "### child %i (#%i) exited with status %i "
396 "(#fails=%i, uptime=%ds).\n",
397 child, forkCount, exitCode, failExitCount, uptime);
401 "### child %i (#%i) exited successfully (uptime=%ds).\n",
402 child, forkCount, uptime);
405 if (exitCode == 123) // ???
410 "### abnormal termination of child %i (#%i) status=%i"
411 "(was not signaled nor exited).",
412 child, forkCount, status);
422 /* main function which initializes server defaults (usually in /etc) */
424 @interface NSUserDefaults(ServerDefaults)
425 + (id)hackInServerDefaults:(NSUserDefaults *)_ud
426 withAppDomainPath:(NSString *)_appDomainPath
427 globalDomainPath:(NSString *)_globalDomainPath;
430 int WOWatchDogApplicationMainWithServerDefaults
431 (NSString *appName, int argc, const char *argv[],
432 NSString *globalDomainPath, NSString *appDomainPath)
434 NSAutoreleasePool *pool;
437 pool = [[NSAutoreleasePool alloc] init];
438 #if LIB_FOUNDATION_LIBRARY || defined(GS_PASS_ARGUMENTS)
440 extern char **environ;
441 [NSProcessInfo initializeWithArguments:(void*)argv count:argc
442 environment:(void*)environ];
446 if ((defClass = NSClassFromString(@"WOServerDefaults")) != nil) {
447 NSUserDefaults *ud, *sd;
449 ud = [NSUserDefaults standardUserDefaults];
450 sd = [defClass hackInServerDefaults:ud
451 withAppDomainPath:appDomainPath
452 globalDomainPath:globalDomainPath];
454 if (((sd == nil) || (sd == ud)) && (appDomainPath != nil)) {
455 NSLog(@"Note: not using server defaults: '%@' "
456 @"(not supported on this Foundation)", appDomainPath);
460 return WOWatchDogApplicationMain(appName, argc, argv);