2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE 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 SOPE 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 SOPE; 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);
37 #include <sys/types.h>
42 static pid_t child = -1;
43 static NSString *pidFile = nil;
44 static time_t lastFailExit = 0;
45 static unsigned failExitCount = 0;
46 static BOOL killedChild = NO;
48 static void killChild(void) {
52 fprintf(stderr, "watchdog[%i]: terminating child %i ..\n", getpid(), child);
54 if (kill(child, SIGTERM) == 0) {
55 waitpid(child, &status, 0);
58 fprintf(stderr, " terminated child %i", child);
60 if (WIFEXITED(status))
61 fprintf(stderr, " exit=%i", WEXITSTATUS(status));
62 if (WIFSIGNALED(status))
63 fprintf(stderr, " signal=%i", WTERMSIG(status));
65 fprintf(stderr, ".\n");
71 else if (kill(child, SIGKILL)) {
72 waitpid(child, &status, 0);
75 fprintf(stderr, " killed child %i", child);
77 if (WIFEXITED(status))
78 fprintf(stderr, " exit=%i", WEXITSTATUS(status));
79 if (WIFSIGNALED(status))
80 fprintf(stderr, " signal=%i", WTERMSIG(status));
82 fprintf(stderr, ".\n");
91 static void _writePid(NSString *pidFile) {
92 if ([pidFile length] > 0) {
95 if ((pf = fopen([pidFile cString], "w"))) {
96 fprintf(pf, "%i\n", getpid());
102 static void _delPid(void) {
103 if ([pidFile length] > 0) {
104 if (unlink([pidFile cString]) == 0)
109 static void exitWatchdog(void) {
114 static void wsignalHandler(int _signal) {
118 fprintf(stderr, "[%i]: watchdog handling signal ctrl-c ..\n", getpid());
121 /* shouldn't get here */
127 "[%i]: watchdog handling segmentation fault "
128 "(SERIOUS PROBLEM) ..\n",
132 /* shouldn't get here */
136 /* TERM signal (kill 'pid') */
137 fprintf(stderr, "[%i]: watchdog handling SIGTERM ..\n", getpid());
140 /* shouldn't get here */
144 /* HUP signal (restart children) */
145 fprintf(stderr, "[%i]: watchdog handling SIGHUP ..\n", getpid());
148 signal(_signal, wsignalHandler);
155 fprintf(stderr, "[%i]: watchdog handling signal %i ..\n",
178 // NSLog(@"SIGNAL: SIGCHLD");
179 // fetch return state
182 result = waitpid(-1, &returnStatus, WNOHANG);
184 fprintf(stderr, "[%i]: process %i exited with code %i",
185 getpid(), (int)result, WEXITSTATUS(returnStatus));
187 if (WIFSIGNALED(returnStatus)) {
188 fprintf(stderr, " (terminated due to signal %i%s)",
189 WTERMSIG(returnStatus),
190 WCOREDUMP(returnStatus) ? ", coredump" : "");
192 if (WIFSTOPPED(returnStatus)) {
193 fprintf(stderr, " (stopped due to signal %i)",
194 WSTOPSIG(returnStatus));
197 fprintf(stderr, "\n");
207 fprintf(stderr, "watchdog[%i]: caught signal %i\n", getpid(), _signal);
210 signal(_signal, wsignalHandler);
213 static void signalHandler(int _signal) {
214 fprintf(stderr, "[%i]: handling signal %i ..\n",
220 fprintf(stderr, "[%i]: caught signal SIGPIPE\n", getpid());
224 fprintf(stderr, "[%i]: caught signal %i\n", getpid(), _signal);
227 signal(_signal, signalHandler);
230 int WOWatchDogApplicationMain
231 (NSString *appName, int argc, const char *argv[])
233 NSAutoreleasePool *pool;
236 pool = [[NSAutoreleasePool alloc] init];
237 #if LIB_FOUNDATION_LIBRARY || defined(GS_PASS_ARGUMENTS)
239 extern char **environ;
240 [NSProcessInfo initializeWithArguments:(void*)argv count:argc
241 environment:(void*)environ];
245 ud = [NSUserDefaults standardUserDefaults];
247 /* default is to use the watch dog! */
248 /* Note: the Defaults.plist is not yet loaded at this stage! */
249 if ([ud objectForKey:@"WOUseWatchDog"] != nil) {
250 if (![ud boolForKey:@"WOUseWatchDog"])
251 return WOApplicationMain(appName, argc, argv);
261 isVerbose = [[ud objectForKey:@"watchdog_verbose"] boolValue];
262 pidFile = [[[ud objectForKey:@"watchdog_pidfile"] stringValue] copy];
264 /* write current pid to pidfile */
267 /* register exit handler */
268 atexit(exitWatchdog);
270 /* register signal handlers of watch dog */
271 signal(SIGPIPE, wsignalHandler);
272 signal(SIGCHLD, wsignalHandler);
273 signal(SIGINT, wsignalHandler);
274 signal(SIGTERM, wsignalHandler);
275 signal(SIGKILL, wsignalHandler);
276 signal(SIGHUP, wsignalHandler);
281 time_t clientStartTime;
283 clientStartTime = time(NULL);
286 if ((child = fork()) == -1) {
287 fprintf(stderr, "[%i]: fork failed: %s\n", getpid(), strerror(errno));
291 fprintf(stderr, " fork failed %i times, sleeping 60 seconds ..\n",
302 signal(SIGPIPE, SIG_DFL);
303 signal(SIGCHLD, SIG_DFL);
304 signal(SIGINT, SIG_DFL);
305 signal(SIGTERM, SIG_DFL);
306 signal(SIGKILL, SIG_DFL);
309 fprintf(stderr, "starting child %i ..\n", getpid());
311 pidFile = [pidFile stringByAppendingPathExtension:@"child"];
316 exit(WOApplicationMain(appName, argc, argv));
318 /* shouldn't even get here ! */
319 fprintf(stderr, "internal server error !\n");
323 /* parent (watch dog) */
326 time_t clientStopTime;
332 fprintf(stderr, "forked child process %i (#%i) ..\n",
339 if ((result = waitpid(child, &status, 0)) == -1) {
346 "### waiting for child %i (#%i) failed: %s\n",
347 child, forkCount, strerror(errno));
351 clientStopTime = time(NULL);
352 uptime = clientStopTime - clientStartTime;
354 if (WIFSIGNALED(status)) {
356 "### child %i (#%i) was terminated by signal %i "
358 child, forkCount, WTERMSIG(status), uptime);
360 lastFailExit = time(NULL);
363 else if (WIFEXITED(status)) {
366 if ((exitCode = WEXITSTATUS(status)) != 0) {
372 if (failExitCount > 0) {
373 unsigned secsSinceLastFail;
375 secsSinceLastFail = (now - lastFailExit);
377 if (secsSinceLastFail > 120) {
378 /* reset fail count */
381 else if (failExitCount > 20) {
382 printf("### child %i (#%i) already failed %i times "
383 "in the last %i seconds, stopping watchdog !\n",
384 child, forkCount, failExitCount, secsSinceLastFail);
393 "### child %i (#%i) exited with status %i "
394 "(#fails=%i, uptime=%ds).\n",
395 child, forkCount, exitCode, failExitCount, uptime);
399 "### child %i (#%i) exited successfully (uptime=%ds).\n",
400 child, forkCount, uptime);
403 if (exitCode == 123) // ???
408 "### abnormal termination of child %i (#%i) status=%i"
409 "(was not signaled nor exited).",
410 child, forkCount, status);
420 /* main function which initializes server defaults (usually in /etc) */
422 @interface NSUserDefaults(ServerDefaults)
423 + (id)hackInServerDefaults:(NSUserDefaults *)_ud
424 withAppDomainPath:(NSString *)_appDomainPath
425 globalDomainPath:(NSString *)_globalDomainPath;
428 int WOWatchDogApplicationMainWithServerDefaults
429 (NSString *appName, int argc, const char *argv[],
430 NSString *globalDomainPath, NSString *appDomainPath)
432 NSAutoreleasePool *pool;
435 pool = [[NSAutoreleasePool alloc] init];
436 #if LIB_FOUNDATION_LIBRARY || defined(GS_PASS_ARGUMENTS)
438 extern char **environ;
439 [NSProcessInfo initializeWithArguments:(void*)argv count:argc
440 environment:(void*)environ];
444 if ((defClass = NSClassFromString(@"WOServerDefaults")) != nil) {
445 NSUserDefaults *ud, *sd;
447 ud = [NSUserDefaults standardUserDefaults];
448 sd = [defClass hackInServerDefaults:ud
449 withAppDomainPath:appDomainPath
450 globalDomainPath:globalDomainPath];
453 if (((sd == nil) || (sd == ud)) && (appDomainPath != nil)) {
454 NSLog(@"Note: not using server defaults: '%@' "
455 @"(not supported on this Foundation)", appDomainPath);
460 return WOWatchDogApplicationMain(appName, argc, argv);