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>
28 Added #include of crt_externs.h and #define of environ so that environ
29 symbol would be found for passing into execve()
31 #include <crt_externs.h>
32 #define environ (*_NSGetEnviron())
35 #include <sys/types.h>
36 #include <sys/unistd.h>
37 #include <sys/param.h>
40 static pid_t child = -1;
41 static NSString *pidFile = nil;
42 static time_t lastFailExit = 0;
43 static unsigned failExitCount = 0;
44 static BOOL killedChild = NO;
46 static void killChild(void) {
50 fprintf(stderr, "watchdog[%i]: terminating child %i ..\n", getpid(),child);
52 if (kill(child, SIGTERM) == 0) {
53 waitpid(child, &status, 0);
56 fprintf(stderr, " terminated child %i", child);
58 if (WIFEXITED(status))
59 fprintf(stderr, " exit=%i", WEXITSTATUS(status));
60 if (WIFSIGNALED(status))
61 fprintf(stderr, " signal=%i", WTERMSIG(status));
63 fprintf(stderr, ".\n");
69 else if (kill(child, SIGKILL)) {
70 waitpid(child, &status, 0);
73 fprintf(stderr, " killed child %i", child);
75 if (WIFEXITED(status))
76 fprintf(stderr, " exit=%i", WEXITSTATUS(status));
77 if (WIFSIGNALED(status))
78 fprintf(stderr, " signal=%i", WTERMSIG(status));
80 fprintf(stderr, ".\n");
89 static void _writePid(NSString *pidFile) {
90 if ([pidFile length] > 0) {
93 if ((pf = fopen([pidFile cString], "w"))) {
94 fprintf(pf, "%i\n", getpid());
100 static void _delPid(void) {
101 if ([pidFile length] > 0) {
102 if (unlink([pidFile cString]) == 0)
107 static void exitWatchdog(void) {
112 static void wsignalHandler(int _signal) {
116 fprintf(stderr, "[%i]: watchdog handling signal ctrl-c ..\n", getpid());
119 /* shouldn't get here */
125 "[%i]: watchdog handling segmentation fault "
126 "(SERIOUS PROBLEM) ..\n",
130 /* shouldn't get here */
134 /* TERM signal (kill 'pid') */
135 fprintf(stderr, "[%i]: watchdog handling SIGTERM ..\n", getpid());
138 /* shouldn't get here */
142 /* HUP signal (restart children) */
143 fprintf(stderr, "[%i]: watchdog handling SIGHUP ..\n", getpid());
146 signal(_signal, wsignalHandler);
153 fprintf(stderr, "[%i]: watchdog handling signal %i ..\n",
176 // NSLog(@"SIGNAL: SIGCHLD");
177 // fetch return state
180 result = waitpid(-1, &returnStatus, WNOHANG);
182 fprintf(stderr, "[%i]: process %i exited with code %i",
183 getpid(), (int)result, WEXITSTATUS(returnStatus));
185 if (WIFSIGNALED(returnStatus)) {
186 fprintf(stderr, " (terminated due to signal %i%s)",
187 WTERMSIG(returnStatus),
188 WCOREDUMP(returnStatus) ? ", coredump" : "");
190 if (WIFSTOPPED(returnStatus)) {
191 fprintf(stderr, " (stopped due to signal %i)",
192 WSTOPSIG(returnStatus));
195 fprintf(stderr, "\n");
205 fprintf(stderr, "watchdog[%i]: caught signal %i\n", getpid(), _signal);
208 signal(_signal, wsignalHandler);
211 static void signalHandler(int _signal) {
212 fprintf(stderr, "[%i]: handling signal %i ..\n",
218 fprintf(stderr, "[%i]: caught signal SIGPIPE\n", getpid());
222 fprintf(stderr, "[%i]: caught signal %i\n", getpid(), _signal);
225 signal(_signal, signalHandler);
228 int WOWatchDogApplicationMain
229 (NSString *appName, int argc, const char *argv[])
231 NSAutoreleasePool *pool;
234 pool = [[NSAutoreleasePool alloc] init];
237 We have to reset the user defaults because this could be a child process
238 where we would have modified the arguments so that it doesn't try and run
241 [NSUserDefaults resetStandardUserDefaults];
242 ud = [NSUserDefaults standardUserDefaults];
244 /* default is to use the watch dog! */
245 /* Note: the Defaults.plist is not yet loaded at this stage! */
246 if ([ud objectForKey:@"WOUseWatchDog"] != nil) {
247 if (![ud boolForKey:@"WOUseWatchDog"])
248 return WOApplicationMain(appName, argc, argv);
251 /* ensure we can actually do execve() */
252 if (argv[0][0] != '/') {
254 Note: we cannot use getcwd to make the path absolute, the
255 binary could have been found anywhere in $PATH
258 "program called with a non-absolute path, can't use execve(): "
260 " (start using `which %s`)\n",
262 exit(123); // TODO: better error code
272 isVerbose = [[ud objectForKey:@"watchdog_verbose"] boolValue];
273 pidFile = [[[ud objectForKey:@"watchdog_pidfile"] stringValue] copy];
275 /* write current pid to pidfile */
278 /* register exit handler */
279 atexit(exitWatchdog);
281 /* register signal handlers of watch dog */
282 signal(SIGPIPE, wsignalHandler);
283 signal(SIGCHLD, wsignalHandler);
284 signal(SIGINT, wsignalHandler);
285 signal(SIGTERM, wsignalHandler);
286 signal(SIGKILL, wsignalHandler);
287 signal(SIGHUP, wsignalHandler);
292 time_t clientStartTime;
294 clientStartTime = time(NULL);
297 if ((child = fork()) == -1) {
298 fprintf(stderr, "[%i]: fork failed: %s\n", getpid(), strerror(errno));
302 fprintf(stderr, " fork failed %i times, sleeping 60 seconds ..\n",
313 /* We need to modify the arguements passed into execve() */
317 extern char **environ;
320 fprintf(stderr, "starting child %i ..\n", getpid());
322 pidFile = [pidFile stringByAppendingPathExtension:@"child"];
328 We need to call execve() to transform this child process into a
330 Signals set to be caught wil be set to the default action by
335 Set the path (0) argument and set -WOUseWatchDog to NO so this
336 process won't execute a parent
338 childArgv = calloc(argc + 5, sizeof(char *));
339 childArgv[0] = strdup(argv[0]);
340 childArgv[1] = strdup("-WOUseWatchDog");
341 childArgv[2] = strdup("NO");
344 Copy any remaining arguments skipping a -WOUseWatchDog if it exists
346 for (index = 1, childIndex = 3; index < argc; index++) {
347 if (strncmp(argv[index], "-WOUseWatchDog", 14) == 0) {
348 /* Skip past the -WOUseWatchDog argument since we used our own */
352 break; /* We have reached the end of the arguments */
353 else if (argv[index][0] != '-') {
355 If the argument after -WOUseWatchDog is not another flag
356 (and it shouldn't be, it should be YES or NO), then skip
357 the YES/NO and go to the next argument
363 childArgv[childIndex] = strdup(argv[index]);
367 /* Mark the end of the array of arguments */
368 childArgv[childIndex] = NULL;
370 /* transform the child into a new process */
377 Note: we cannot use getcwd to make the path absolute, the
378 binary could have been found anywhere in $PATH
381 "program called with a non-absolute path: '%s'\n", p);
382 exit(123); // TODO: better error code
385 execve(p, childArgv, environ);
387 /* shouldn't even get here ! */
389 "execve of '%s' failed in child %i failed: %s\n",
390 p, getpid(), strerror(errno));
395 /* parent (watch dog) */
398 time_t clientStopTime;
404 fprintf(stderr, "forked child process %i (#%i) ..\n",
411 if ((result = waitpid(child, &status, 0)) == -1) {
418 "### waiting for child %i (#%i) failed: %s\n",
419 child, forkCount, strerror(errno));
423 clientStopTime = time(NULL);
424 uptime = clientStopTime - clientStartTime;
426 if (WIFSIGNALED(status)) {
428 "### child %i (#%i) was terminated by signal %i "
430 child, forkCount, WTERMSIG(status), uptime);
432 lastFailExit = time(NULL);
435 else if (WIFEXITED(status)) {
438 if ((exitCode = WEXITSTATUS(status)) != 0) {
444 if (failExitCount > 0) {
445 unsigned secsSinceLastFail;
447 secsSinceLastFail = (now - lastFailExit);
449 if (secsSinceLastFail > 120) {
450 /* reset fail count */
453 else if (failExitCount > 20) {
454 printf("### child %i (#%i) already failed %i times "
455 "in the last %i seconds, stopping watchdog !\n",
456 child, forkCount, failExitCount, secsSinceLastFail);
465 "### child %i (#%i) exited with status %i "
466 "(#fails=%i, uptime=%ds).\n",
467 child, forkCount, exitCode, failExitCount, uptime);
471 "### child %i (#%i) exited successfully (uptime=%ds).\n",
472 child, forkCount, uptime);
475 if (exitCode == 123) // ???
480 "### abnormal termination of child %i (#%i) status=%i"
481 "(was not signaled nor exited).",
482 child, forkCount, status);
491 /* main function which initializes server defaults (usually in /etc) */
493 @interface NSUserDefaults(ServerDefaults)
494 + (id)hackInServerDefaults:(NSUserDefaults *)_ud
495 withAppDomainPath:(NSString *)_appDomainPath
496 globalDomainPath:(NSString *)_globalDomainPath;
499 int WOWatchDogApplicationMainWithServerDefaults
500 (NSString *appName, int argc, const char *argv[],
501 NSString *globalDomainPath, NSString *appDomainPath)
503 NSAutoreleasePool *pool;
506 pool = [[NSAutoreleasePool alloc] init];
508 if ((defClass = NSClassFromString(@"WOServerDefaults")) != nil) {
509 NSUserDefaults *ud, *sd;
511 ud = [NSUserDefaults standardUserDefaults];
512 sd = [defClass hackInServerDefaults:ud
513 withAppDomainPath:appDomainPath
514 globalDomainPath:globalDomainPath];
517 return WOWatchDogApplicationMain(appName, argc, argv);