From: helge Date: Sun, 6 Mar 2005 17:27:05 +0000 (+0000) Subject: added special WOWatchDogApplicationMain for MacOSX (see OGo bug #1175) X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7dfbcc13575efac7e835d371d9e5aa8a7c020c3b;p=sope added special WOWatchDogApplicationMain for MacOSX (see OGo bug #1175) git-svn-id: http://svn.opengroupware.org/SOPE/trunk@623 e4a50df8-12e2-0310-a44c-efbce7f8a7e3 --- diff --git a/sope-appserver/NGObjWeb/ChangeLog b/sope-appserver/NGObjWeb/ChangeLog index d5acadd5..5a520088 100644 --- a/sope-appserver/NGObjWeb/ChangeLog +++ b/sope-appserver/NGObjWeb/ChangeLog @@ -1,3 +1,9 @@ +2005-03-06 Mont Rothstein + + * added an MacOSX specific WOWatchDogApplicationMain, this fixes some + issue when linking against the AJR libraries (see OGo bug #1175) + (v4.5.127) + 2005-03-04 Helge Hess * WOElementID.h (NGObjWeb_MAX_ELEMENT_ID_COUNT): bumped max element diff --git a/sope-appserver/NGObjWeb/GNUmakefile b/sope-appserver/NGObjWeb/GNUmakefile index ca13c991..5c2fc768 100644 --- a/sope-appserver/NGObjWeb/GNUmakefile +++ b/sope-appserver/NGObjWeb/GNUmakefile @@ -125,7 +125,12 @@ libNGObjWeb_OBJC_FILES = \ WOServerSessionStore.m \ WOSimpleHTTPParser.m \ WOStats.m \ - WOWatchDogApplicationMain.m \ + +ifeq ($(FOUNDATION_LIB),apple) +libNGObjWeb_OBJC_FILES += WOWatchDogApplicationMainOSX.m +else +libNGObjWeb_OBJC_FILES += WOWatchDogApplicationMain.m +endif ifeq ($(FOUNDATION_LIB),fd) NGObjWebCore_OBJC_FILES += WOServerDefaults.m diff --git a/sope-appserver/NGObjWeb/Version b/sope-appserver/NGObjWeb/Version index be66ce47..44ddb30e 100644 --- a/sope-appserver/NGObjWeb/Version +++ b/sope-appserver/NGObjWeb/Version @@ -1,6 +1,6 @@ # version file -SUBMINOR_VERSION:=126 +SUBMINOR_VERSION:=127 # v4.5.122 requires libNGExtensions v4.5.153 # v4.5.91 requires libNGExtensions v4.5.134 diff --git a/sope-appserver/NGObjWeb/WORequest.m b/sope-appserver/NGObjWeb/WORequest.m index 40572f21..c37f972e 100644 --- a/sope-appserver/NGObjWeb/WORequest.m +++ b/sope-appserver/NGObjWeb/WORequest.m @@ -402,10 +402,15 @@ static BOOL debugOn = NO; } - (NGHashMap *)_getFormParameters { - if (self->formContent) + if (self->formContent != nil) return self->formContent; - if (self->request == nil) { + if (self->request != nil) { + self->formContent = [[self->request formParameters] retain]; + return self->formContent; + } + + { /* TODO: add parsing of form values @@ -456,8 +461,6 @@ static BOOL debugOn = NO; else self->formContent = [[NGHashMap alloc] init]; } - else - self->formContent = [[self->request formParameters] retain]; return self->formContent; } diff --git a/sope-appserver/NGObjWeb/WOWatchDogApplicationMainOSX.m b/sope-appserver/NGObjWeb/WOWatchDogApplicationMainOSX.m new file mode 100644 index 00000000..6f265213 --- /dev/null +++ b/sope-appserver/NGObjWeb/WOWatchDogApplicationMainOSX.m @@ -0,0 +1,492 @@ +/* + Copyright (C) 2000-2005 SKYRIX Software AG + + This file is part of SOPE. + + SOPE is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + SOPE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with SOPE; see the file COPYING. If not, write to the + Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +#import +#include + +#include + +/* + Added #include of crt_externs.h and #define of environ so that environ + symbol would be found for passing into execve() +*/ +#include +#define environ (*_NSGetEnviron()) + +#include +#include +#include +#include + +static pid_t child = -1; +static NSString *pidFile = nil; +static time_t lastFailExit = 0; +static unsigned failExitCount = 0; +static BOOL killedChild = NO; + +static void killChild(void) { + if (child > 0) { + int status; + + fprintf(stderr, "watchdog[%i]: terminating child %i ..\n", getpid(),child); + + if (kill(child, SIGTERM) == 0) { + waitpid(child, &status, 0); + killedChild = YES; + + fprintf(stderr, " terminated child %i", child); + + if (WIFEXITED(status)) + fprintf(stderr, " exit=%i", WEXITSTATUS(status)); + if (WIFSIGNALED(status)) + fprintf(stderr, " signal=%i", WTERMSIG(status)); + + fprintf(stderr, ".\n"); + fflush(stderr); + + child = -1; + return; + } + else if (kill(child, SIGKILL)) { + waitpid(child, &status, 0); + killedChild = YES; + + fprintf(stderr, " killed child %i", child); + + if (WIFEXITED(status)) + fprintf(stderr, " exit=%i", WEXITSTATUS(status)); + if (WIFSIGNALED(status)) + fprintf(stderr, " signal=%i", WTERMSIG(status)); + + fprintf(stderr, ".\n"); + fflush(stderr); + + child = -1; + return; + } + } +} + +static void _writePid(NSString *pidFile) { + if ([pidFile length] > 0) { + FILE *pf; + + if ((pf = fopen([pidFile cString], "w"))) { + fprintf(pf, "%i\n", getpid()); + fflush(pf); + fclose(pf); + } + } +} +static void _delPid(void) { + if ([pidFile length] > 0) { + if (unlink([pidFile cString]) == 0) + pidFile = nil; + } +} + +static void exitWatchdog(void) { + killChild(); + _delPid(); +} + +static void wsignalHandler(int _signal) { + switch (_signal) { + case SIGINT: + /* Control-C */ + fprintf(stderr, "[%i]: watchdog handling signal ctrl-c ..\n", getpid()); + killChild(); + exit(0); + /* shouldn't get here */ + abort(); + + case SIGSEGV: + /* Coredump ! */ + fprintf(stderr, + "[%i]: watchdog handling segmentation fault " + "(SERIOUS PROBLEM) ..\n", + getpid()); + killChild(); + exit(123); + /* shouldn't get here */ + abort(); + + case SIGTERM: + /* TERM signal (kill 'pid') */ + fprintf(stderr, "[%i]: watchdog handling SIGTERM ..\n", getpid()); + killChild(); + exit(0); + /* shouldn't get here */ + abort(); + + case SIGHUP: + /* HUP signal (restart children) */ + fprintf(stderr, "[%i]: watchdog handling SIGHUP ..\n", getpid()); + killChild(); + killedChild = YES; + signal(_signal, wsignalHandler); + return; + + case SIGCHLD: + break; + + default: + fprintf(stderr, "[%i]: watchdog handling signal %i ..\n", + getpid(), _signal); + break; + } + fflush(stderr); + + switch (_signal) { + case SIGTERM: + case SIGINT: + case SIGKILL: + case SIGILL: + killChild(); + exit(0); + break; + + case SIGHUP: + killChild(); + break; + + case SIGCHLD: { + int returnStatus; + pid_t result; + + // NSLog(@"SIGNAL: SIGCHLD"); + // fetch return state + + do { + result = waitpid(-1, &returnStatus, WNOHANG); + if (result > 0) { + fprintf(stderr, "[%i]: process %i exited with code %i", + getpid(), (int)result, WEXITSTATUS(returnStatus)); + + if (WIFSIGNALED(returnStatus)) { + fprintf(stderr, " (terminated due to signal %i%s)", + WTERMSIG(returnStatus), + WCOREDUMP(returnStatus) ? ", coredump" : ""); + } + if (WIFSTOPPED(returnStatus)) { + fprintf(stderr, " (stopped due to signal %i)", + WSTOPSIG(returnStatus)); + } + + fprintf(stderr, "\n"); + fflush(stderr); + } + } + while (result > 0); + + break; + } + + default: + fprintf(stderr, "watchdog[%i]: caught signal %i\n", getpid(), _signal); + break; + } + signal(_signal, wsignalHandler); +} + +static void signalHandler(int _signal) { + fprintf(stderr, "[%i]: handling signal %i ..\n", + getpid(), _signal); + fflush(stderr); + + switch (_signal) { + case SIGPIPE: + fprintf(stderr, "[%i]: caught signal SIGPIPE\n", getpid()); + break; + + default: + fprintf(stderr, "[%i]: caught signal %i\n", getpid(), _signal); + break; + } + signal(_signal, signalHandler); +} + +int WOWatchDogApplicationMain +(NSString *appName, int argc, const char *argv[]) +{ + NSAutoreleasePool *pool; + NSUserDefaults *ud; + + pool = [[NSAutoreleasePool alloc] init]; + + /* + We have to reset the user defaults because this could be a child process + where we would have modified the arguments so that it doesn't try and run + as a parent again. + */ + [NSUserDefaults resetStandardUserDefaults]; + ud = [NSUserDefaults standardUserDefaults]; + + /* default is to use the watch dog! */ + /* Note: the Defaults.plist is not yet loaded at this stage! */ + if ([ud objectForKey:@"WOUseWatchDog"] != nil) { + if (![ud boolForKey:@"WOUseWatchDog"]) + return WOApplicationMain(appName, argc, argv); + } + + /* watch dog */ + { + int failCount = 0; + int forkCount = 0; + BOOL repeat = YES; + BOOL isVerbose = NO; + + isVerbose = [[ud objectForKey:@"watchdog_verbose"] boolValue]; + pidFile = [[[ud objectForKey:@"watchdog_pidfile"] stringValue] copy]; + + /* write current pid to pidfile */ + _writePid(pidFile); + + /* register exit handler */ + atexit(exitWatchdog); + + /* register signal handlers of watch dog */ + signal(SIGPIPE, wsignalHandler); + signal(SIGCHLD, wsignalHandler); + signal(SIGINT, wsignalHandler); + signal(SIGTERM, wsignalHandler); + signal(SIGKILL, wsignalHandler); + signal(SIGHUP, wsignalHandler); + + /* loop */ + + while (repeat) { + time_t clientStartTime; + + clientStartTime = time(NULL); + killedChild = NO; + + if ((child = fork()) == -1) { + fprintf(stderr, "[%i]: fork failed: %s\n", getpid(), strerror(errno)); + failCount++; + + if (failCount > 5) { + fprintf(stderr, " fork failed %i times, sleeping 60 seconds ..\n", + failCount); + sleep(60); + } + else { + sleep(1); + } + } + else { + if (child == 0) { + /* child process */ + /* We need to modify the arguements passed into execve() */ + char **childArgv; + int index; + int childIndex; + extern char **environ; + + if (isVerbose) + fprintf(stderr, "starting child %i ..\n", getpid()); + + pidFile = [pidFile stringByAppendingPathExtension:@"child"]; + _writePid(pidFile); + + atexit(_delPid); + + /* + We need to call execve() to transform this child process into a + new process. + Signals set to be caught wil be set to the default action by + execve(). + */ + + /* + Set the path (0) argument and set -WOUseWatchDog to NO so this + process won't execute a parent + */ + childArgv = malloc(argc + 3); + childArgv[0] = malloc(strlen(argv[0]) + 1); + strcpy(childArgv[0], argv[0]); + childArgv[1] = (char *)malloc(strlen("-WOUseWatchDog") + 1); + strcpy(childArgv[1], "-WOUseWatchDog"); + childArgv[2] = (char *)malloc(strlen("NO") + 1); + strcpy(childArgv[2], "NO"); + + /* + Copy any remaining arguments skipping a -WOUseWatchDog if it exists + */ + for (index = 1, childIndex = 3; index < argc; index++) { + if (strncmp(argv[index], "-WOUseWatchDog", 14) == 0) { + /* Skip past the -WOUseWatchDog argument since we used our own */ + index++; + + if (index >= argc) + break; /* We have reached the end of the arguments */ + else if (argv[index][0] != '-') { + /* + If the argument after -WOUseWatchDog is not another flag + (and it shouldn't be, it should be YES or NO), then skip + the YES/NO and go to the next argument + */ + continue; + } + } + + childArgv[childIndex] = (char *)malloc(strlen(argv[index]) + 1); + strcpy(childArgv[childIndex], argv[index]); + childIndex++; + } + + /* Mark the end of the array of arguments */ + childArgv[childIndex] = NULL; + + /* transform the child into a new process */ + execve(argv[0], childArgv, environ); + + /* shouldn't even get here ! */ + fprintf(stderr, + "exeve failed in child %i failed: %s\n", + getpid(), strerror(errno)); + abort(); + } + else { + /* parent (watch dog) */ + int status = 0; + pid_t result = 0; + time_t clientStopTime; + unsigned uptime; + + forkCount++; + + if (isVerbose) { + fprintf(stderr, "forked child process %i (#%i) ..\n", + child, forkCount); + } + + failCount = 0; + status = 0; + + if ((result = waitpid(child, &status, 0)) == -1) { + if (killedChild) { + killedChild = NO; + continue; + } + + fprintf(stderr, + "### waiting for child %i (#%i) failed: %s\n", + child, forkCount, strerror(errno)); + continue; + } + + clientStopTime = time(NULL); + uptime = clientStopTime - clientStartTime; + + if (WIFSIGNALED(status)) { + fprintf(stderr, + "### child %i (#%i) was terminated by signal %i " + "(uptime=%ds).\n", + child, forkCount, WTERMSIG(status), uptime); + + lastFailExit = time(NULL); + failExitCount++; + } + else if (WIFEXITED(status)) { + unsigned exitCode; + + if ((exitCode = WEXITSTATUS(status)) != 0) { + time_t now; + + now = time(NULL); + + if (uptime < 3) { + if (failExitCount > 0) { + unsigned secsSinceLastFail; + + secsSinceLastFail = (now - lastFailExit); + + if (secsSinceLastFail > 120) { + /* reset fail count */ + failExitCount = 0; + } + else if (failExitCount > 20) { + printf("### child %i (#%i) already failed %i times " + "in the last %i seconds, stopping watchdog !\n", + child, forkCount, failExitCount, secsSinceLastFail); + repeat = NO; + } + } + } + failExitCount++; + lastFailExit = now; + + fprintf(stderr, + "### child %i (#%i) exited with status %i " + "(#fails=%i, uptime=%ds).\n", + child, forkCount, exitCode, failExitCount, uptime); + } + else { + fprintf(stderr, + "### child %i (#%i) exited successfully (uptime=%ds).\n", + child, forkCount, uptime); + } + + if (exitCode == 123) // ??? + repeat = NO; + } + else { + fprintf(stderr, + "### abnormal termination of child %i (#%i) status=%i" + "(was not signaled nor exited).", + child, forkCount, status); + } + } + } + } + return 0; + } +} + +/* main function which initializes server defaults (usually in /etc) */ + +@interface NSUserDefaults(ServerDefaults) ++ (id)hackInServerDefaults:(NSUserDefaults *)_ud + withAppDomainPath:(NSString *)_appDomainPath + globalDomainPath:(NSString *)_globalDomainPath; +@end + +int WOWatchDogApplicationMainWithServerDefaults +(NSString *appName, int argc, const char *argv[], + NSString *globalDomainPath, NSString *appDomainPath) +{ + NSAutoreleasePool *pool; + Class defClass; + + pool = [[NSAutoreleasePool alloc] init]; + + if ((defClass = NSClassFromString(@"WOServerDefaults")) != nil) { + NSUserDefaults *ud, *sd; + + ud = [NSUserDefaults standardUserDefaults]; + sd = [defClass hackInServerDefaults:ud + withAppDomainPath:appDomainPath + globalDomainPath:globalDomainPath]; + } + + return WOWatchDogApplicationMain(appName, argc, argv); +}