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