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