]> err.no Git - sope/blob - sope-appserver/NGObjWeb/WOWatchDogApplicationMain.m
started support for non-userspecific server defaults
[sope] / sope-appserver / NGObjWeb / WOWatchDogApplicationMain.m
1 /*
2   Copyright (C) 2000-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #import <Foundation/Foundation.h>
23 #include <NGObjWeb/NGObjWeb.h>
24
25 #if defined(__CYGWIN32__) || defined(__MINGW32__)
26
27 int WOWatchDogApplicationMain
28 (NSString *appName, int argc, const char *argv[])
29 {
30   /* no watchdog support on Win* */
31   return WOApplicationMain(appName, argc, argv);
32 }
33
34 #else
35
36 #ifdef __APPLE__
37 #  include <unistd.h>
38 #endif
39 #include <sys/wait.h>
40 #include <sys/types.h>
41 #include <sys/unistd.h>
42 #include <time.h>
43
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;
49
50 static void killChild(void) {
51   if (child > 0) {
52     int status;
53     
54     fprintf(stderr, "watchdog[%i]: terminating child %i ..\n", getpid(), child);
55     
56     if (kill(child, SIGTERM) == 0) {
57       waitpid(child, &status, 0);
58       killedChild = YES;
59       
60       fprintf(stderr, "  terminated child %i",  child);
61       
62       if (WIFEXITED(status))
63         fprintf(stderr, " exit=%i", WEXITSTATUS(status));
64       if (WIFSIGNALED(status))
65         fprintf(stderr, " signal=%i", WTERMSIG(status));
66       
67       fprintf(stderr, ".\n");
68       fflush(stderr);
69       
70       child = -1;
71       return;
72     }
73     else if (kill(child, SIGKILL)) {
74       waitpid(child, &status, 0);
75       killedChild = YES;
76       
77       fprintf(stderr, "  killed child %i",  child);
78       
79       if (WIFEXITED(status))
80         fprintf(stderr, " exit=%i", WEXITSTATUS(status));
81       if (WIFSIGNALED(status))
82         fprintf(stderr, " signal=%i", WTERMSIG(status));
83       
84       fprintf(stderr, ".\n");
85       fflush(stderr);
86       
87       child = -1;
88       return;
89     }
90   }
91 }
92
93 static void _writePid(NSString *pidFile) {
94   if ([pidFile length] > 0) {
95     FILE *pf;
96       
97     if ((pf = fopen([pidFile cString], "w"))) {
98       fprintf(pf, "%i\n", getpid());
99       fflush(pf);
100       fclose(pf);
101     }
102   }
103 }
104 static void _delPid(void) {
105   if ([pidFile length] > 0) {
106     if (unlink([pidFile cString]) == 0)
107       pidFile = nil;
108   }
109 }
110
111 static void exitWatchdog(void) {
112   killChild();
113   _delPid();
114 }
115
116 static void wsignalHandler(int _signal) {
117   switch (_signal) {
118   case SIGINT:
119     /* Control-C */
120     fprintf(stderr, "[%i]: watchdog handling signal ctrl-c ..\n", getpid());
121     killChild();
122     exit(0);
123     /* shouldn't get here */
124     abort();
125
126   case SIGSEGV:
127     /* Coredump ! */
128     fprintf(stderr,
129             "[%i]: watchdog handling segmentation fault "
130             "(SERIOUS PROBLEM) ..\n",
131             getpid());
132     killChild();
133     exit(123);
134     /* shouldn't get here */
135     abort();
136
137   case SIGTERM:
138     /* TERM signal (kill 'pid') */
139     fprintf(stderr, "[%i]: watchdog handling SIGTERM ..\n", getpid());
140     killChild();
141     exit(0);
142     /* shouldn't get here */
143     abort();
144     
145   case SIGHUP:
146     /* HUP signal (restart children) */
147     fprintf(stderr, "[%i]: watchdog handling SIGHUP ..\n", getpid());
148     killChild();
149     killedChild = YES;
150     signal(_signal, wsignalHandler);
151     return;
152     
153   case SIGCHLD:
154     break;
155     
156   default:
157     fprintf(stderr, "[%i]: watchdog handling signal %i ..\n",
158             getpid(), _signal);
159     break;
160   }
161   fflush(stderr);
162   
163   switch (_signal) {
164     case SIGTERM:
165     case SIGINT:
166     case SIGKILL:
167     case SIGILL:
168       killChild();
169       exit(0);
170       break;
171       
172     case SIGHUP:
173       killChild();
174       break;
175       
176     case SIGCHLD: {
177       int   returnStatus;
178       pid_t result;
179       
180       //      NSLog(@"SIGNAL: SIGCHLD");
181       // fetch return state
182       
183       do {
184         result = waitpid(-1, &returnStatus, WNOHANG);
185         if (result > 0) {
186           fprintf(stderr, "[%i]: process %i exited with code %i",
187                   getpid(), (int)result, WEXITSTATUS(returnStatus));
188
189           if (WIFSIGNALED(returnStatus)) {
190             fprintf(stderr, " (terminated due to signal %i%s)",
191                     WTERMSIG(returnStatus),
192                     WCOREDUMP(returnStatus) ? ", coredump" : "");
193           }
194           if (WIFSTOPPED(returnStatus)) {
195             fprintf(stderr, " (stopped due to signal %i)",
196                     WSTOPSIG(returnStatus));
197           }
198           
199           fprintf(stderr, "\n");
200           fflush(stderr);
201         }
202       }
203       while (result > 0);
204       
205       break;
206     }
207     
208     default:
209       fprintf(stderr, "watchdog[%i]: caught signal %i\n", getpid(), _signal);
210       break;
211   }
212   signal(_signal, wsignalHandler);
213 }
214
215 static void signalHandler(int _signal) {
216   fprintf(stderr, "[%i]: handling signal %i ..\n",
217           getpid(), _signal);
218   fflush(stderr);
219   
220   switch (_signal) {
221     case SIGPIPE:
222       fprintf(stderr, "[%i]: caught signal SIGPIPE\n", getpid());
223       break;
224       
225     default:
226       fprintf(stderr, "[%i]: caught signal %i\n", getpid(), _signal);
227       break;
228   }
229   signal(_signal, signalHandler);
230 }
231
232 int WOWatchDogApplicationMain
233 (NSString *appName, int argc, const char *argv[])
234 {
235   NSAutoreleasePool *pool;
236   NSUserDefaults *ud;
237
238   pool = [[NSAutoreleasePool alloc] init];
239 #if LIB_FOUNDATION_LIBRARY || defined(GS_PASS_ARGUMENTS)
240   {
241     extern char **environ;
242     [NSProcessInfo initializeWithArguments:(void*)argv count:argc
243                    environment:(void*)environ];
244   }
245 #endif
246   
247   ud = [NSUserDefaults standardUserDefaults];
248   
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);
254   }
255   
256   /* watch dog */
257   {
258     int      failCount = 0;
259     int      forkCount = 0;
260     BOOL     repeat    = YES;
261     BOOL     isVerbose = NO;  
262     
263     isVerbose = [[ud objectForKey:@"watchdog_verbose"] boolValue];
264     pidFile   = [[[ud objectForKey:@"watchdog_pidfile"] stringValue] copy];
265     
266     /* write current pid to pidfile */
267     _writePid(pidFile);
268     
269     /* register exit handler */
270     atexit(exitWatchdog);
271     
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);
279     
280     /* loop */
281     
282     while (repeat) {
283       time_t clientStartTime;
284       
285       clientStartTime = time(NULL);
286       killedChild = NO;
287       
288       if ((child = fork()) == -1) {
289         fprintf(stderr, "[%i]: fork failed: %s\n", getpid(), strerror(errno));
290         failCount++;
291         
292         if (failCount > 5) {
293           fprintf(stderr, "  fork failed %i times, sleeping 60 seconds ..\n",
294                   failCount);
295           sleep(60);
296         }
297         else {
298           sleep(1);
299         }
300       }
301       else {
302         if (child == 0) {
303           /* child process */
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);
309           
310           if (isVerbose)
311             fprintf(stderr, "starting child %i ..\n", getpid());
312
313           pidFile = [pidFile stringByAppendingPathExtension:@"child"];
314           _writePid(pidFile);
315           
316           atexit(_delPid);
317           
318           exit(WOApplicationMain(appName, argc, argv));
319           
320           /* shouldn't even get here ! */
321           fprintf(stderr, "internal server error !\n");
322           abort();
323         }
324         else {
325           /* parent (watch dog) */
326           int      status = 0;
327           pid_t    result = 0;
328           time_t   clientStopTime;
329           unsigned uptime;
330           
331           forkCount++;
332           
333           if (isVerbose) {
334             fprintf(stderr, "forked child process %i (#%i) ..\n",
335                     child, forkCount);
336           }
337           
338           failCount = 0;
339           status    = 0;
340           
341           if ((result = waitpid(child, &status, 0)) == -1) {
342             if (killedChild) {
343               killedChild = NO;
344               continue;
345             }
346             
347             fprintf(stderr,
348                     "### waiting for child %i (#%i) failed: %s\n",
349                     child, forkCount, strerror(errno));
350             continue;
351           }
352
353           clientStopTime = time(NULL);
354           uptime = clientStopTime - clientStartTime;
355           
356           if (WIFSIGNALED(status)) {
357             fprintf(stderr,
358                     "### child %i (#%i) was terminated by signal %i "
359                     "(uptime=%ds).\n",
360                     child, forkCount, WTERMSIG(status), uptime);
361             
362             lastFailExit  = time(NULL);
363             failExitCount++;
364           }
365           else if (WIFEXITED(status)) {
366             unsigned exitCode;
367             
368             if ((exitCode = WEXITSTATUS(status)) != 0) {
369               time_t now;
370               
371               now = time(NULL);
372
373               if (uptime < 3) {
374                 if (failExitCount > 0) {
375                   unsigned secsSinceLastFail;
376                 
377                   secsSinceLastFail = (now - lastFailExit);
378                 
379                   if (secsSinceLastFail > 120) {
380                     /* reset fail count */
381                     failExitCount = 0;
382                   }
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);
387                     repeat = NO;
388                   }
389                 }
390               }              
391               failExitCount++;
392               lastFailExit  = now;
393               
394               fprintf(stderr,
395                       "### child %i (#%i) exited with status %i "
396                       "(#fails=%i, uptime=%ds).\n",
397                       child, forkCount, exitCode, failExitCount, uptime);
398             }
399             else {
400               fprintf(stderr,
401                       "### child %i (#%i) exited successfully (uptime=%ds).\n",
402                       child, forkCount, uptime);
403             }
404             
405             if (exitCode == 123) // ???
406               repeat = NO;
407           }
408           else {
409             fprintf(stderr,
410                     "### abnormal termination of child %i (#%i) status=%i"
411                     "(was not signaled nor exited).",
412                     child, forkCount, status);
413           }
414         }
415       }
416     }
417     return 0;
418   }
419 }
420 #endif
421
422 /* main function which initializes server defaults (usually in /etc) */
423
424 @interface NSUserDefaults(ServerDefaults)
425 + (id)hackInServerDefaults:(NSUserDefaults *)_ud
426   withAppDomainPath:(NSString *)_appDomainPath
427   globalDomainPath:(NSString *)_globalDomainPath;
428 @end
429
430 int WOWatchDogApplicationMainWithServerDefaults
431 (NSString *appName, int argc, const char *argv[],
432  NSString *globalDomainPath, NSString *appDomainPath)
433 {
434   NSAutoreleasePool *pool;
435   Class defClass;
436   
437   pool = [[NSAutoreleasePool alloc] init];
438 #if LIB_FOUNDATION_LIBRARY || defined(GS_PASS_ARGUMENTS)
439   {
440     extern char **environ;
441     [NSProcessInfo initializeWithArguments:(void*)argv count:argc
442                    environment:(void*)environ];
443   }
444 #endif
445   
446   if ((defClass = NSClassFromString(@"WOServerDefaults")) != nil) {
447     NSUserDefaults *ud, *sd;
448     
449     ud = [NSUserDefaults standardUserDefaults];
450     sd = [defClass hackInServerDefaults:ud
451                    withAppDomainPath:appDomainPath
452                    globalDomainPath:globalDomainPath];
453     
454     if (((sd == nil) || (sd == ud)) && (appDomainPath != nil)) {
455       NSLog(@"Note: not using server defaults: '%@' "
456             @"(not supported on this Foundation)", appDomainPath);
457     }
458   }
459   
460   return WOWatchDogApplicationMain(appName, argc, argv);
461 }