]> matita.cs.unibo.it Git - helm.git/blob - helm/software/components/binaries/matitaprover/TreeLimitedRun.c
Preparing for 0.5.9 release.
[helm.git] / helm / software / components / binaries / matitaprover / TreeLimitedRun.c
1 //---------------------------------------------------------------------------
2 //----Program to watch CPU usage of a process
3 //----TreeLimitedRun <CPU time limit> <WC time limit> <Memory limit> <Job>
4 //---------------------------------------------------------------------------
5 //----SUN or LINUX or OSX (OSX is very generic)
6 #if (!defined(SUN) && !defined(LINUX) && !defined(OSX))
7     #define LINUX
8 #endif
9 //---------------------------------------------------------------------------
10 #include <stdio.h>
11 #include <string.h>
12 #include <sys/time.h>
13 #include <sys/resource.h>
14 #include <sys/types.h>
15 #include <sys/wait.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <dirent.h>
19 #include <ctype.h>
20 #include <signal.h>
21 #include <errno.h>
22 #ifdef SUN
23 #include <procfs.h>
24 #endif
25 //---------------------------------------------------------------------------
26 #define STRING_LENGTH 80
27 #define MAX_PROCESSES 1000
28 #define DEFAULT_DELAY_BETWEEN_CHECKS 10
29 #define NANOSECONDS 1E9
30 #define MICROSECONDS 1E6
31 #define JIFFIES 100
32
33 #define STDOUT 1
34 #define STDERR 2
35
36 typedef char String[STRING_LENGTH];
37
38 typedef struct {
39     int Active;
40     pid_t PID;
41     pid_t PPID;
42     double CPUTime;
43     double AccumulatedCPUTime;
44 } ProcessData;
45
46 typedef ProcessData ProcessDataArray[MAX_PROCESSES];
47
48 int GlobalInterrupted;
49 int GlobalSignalReceived;
50 //---------------------------------------------------------------------------
51 void SIGCHLDHandler(int TheSignal) {
52
53 //DEBUG printf("Some child has died\n");
54 //----The child is reaped in WatchChildTree
55
56 }
57 //---------------------------------------------------------------------------
58 void SIGCHLDReaper(int TheSignal) {
59
60     int DeadPID;
61     int Status;
62
63     while ((DeadPID = waitpid(-1,&Status,WNOHANG)) > 0) {
64 //DEBUG printf("!!! Child %d of TreeLimitedRun %d has died\n",DeadPID,getpid());fflush(stdout);
65     }
66
67 }
68 //---------------------------------------------------------------------------
69 //----Controllers in the CASC/SystemOnTPTP/SSCPA/etc hierarchy may send
70 //----SIGQUIT to stop things
71 void SIGQUITHandler(int TheSignal) {
72
73 //DEBUG printf("!!! TreeLimitedRun %d got a signal %d\n",getpid(),TheSignal);fflush(stdout);
74     GlobalInterrupted = 1;
75     GlobalSignalReceived = TheSignal;
76
77 }
78 //---------------------------------------------------------------------------
79 void SetCPUTimeLimit(rlim_t CPUTimeLimit) {
80
81     struct rlimit ResourceLimits;
82
83 //----Set the signal handler
84 //----No point - signals are reset on exec
85 //----For some reason this is still needed, or the signal doesn't work
86     if (signal(SIGXCPU,SIGQUITHandler) == SIG_ERR) {
87         perror("Setting signal handler");
88         exit(EXIT_FAILURE);
89     }
90
91 //----Limit the CPU time. Need to get old one for hard limit field
92     if (getrlimit(RLIMIT_CPU,&ResourceLimits) == -1) {
93         perror("Getting CPU limit");
94         exit(EXIT_FAILURE);
95     }
96 //----Set new CPU limit in ms (sent in secs)
97     ResourceLimits.rlim_cur = CPUTimeLimit;
98     if (setrlimit(RLIMIT_CPU,&ResourceLimits) == -1) {
99         perror("Setting CPU limit");
100         exit(EXIT_FAILURE);
101     }
102 }
103 //-----------------------------------------------------------------------------
104 void SetMemoryLimit(rlim_t MemoryLimit) {
105
106     struct rlimit ResourceLimits;
107
108 #ifdef RLIMIT_AS
109 #define THE_LIMIT RLIMIT_AS
110 #else
111 #define THE_LIMIT RLIMIT_DATA
112 #endif
113
114 //----Limit the memory. Need to get old one for hard limit field
115     if (getrlimit(THE_LIMIT,&ResourceLimits) == -1) {
116         perror("Getting memory limit");
117         exit(EXIT_FAILURE);
118     }
119 //----Set new memory limit
120     ResourceLimits.rlim_max = MemoryLimit;
121     ResourceLimits.rlim_cur = MemoryLimit;
122     if (setrlimit(THE_LIMIT,&ResourceLimits) == -1) {
123         perror("Setting memory limit");
124         exit(EXIT_FAILURE);
125     }
126 }
127 //---------------------------------------------------------------------------
128 //----Prevent core dumps that occur on timeout
129 void SetNoCoreDump(void) {
130
131     struct rlimit ResourceLimits;
132
133 //----Get old resource limits
134     if (getrlimit(RLIMIT_CORE,&ResourceLimits) == -1) {
135         perror("Getting resource limit:");
136         exit(EXIT_FAILURE);
137     }
138 //----Set new core limit to 0
139     ResourceLimits.rlim_cur = 0;
140     if (setrlimit(RLIMIT_CORE,&ResourceLimits) == -1) {
141         perror("Setting resource limit:");
142         exit(EXIT_FAILURE);
143     }
144 }
145 //---------------------------------------------------------------------------
146 #ifdef LINUX
147 void GetProcessesOwnedByMe(uid_t MyRealUID,ProcessDataArray OwnedPIDs,
148 int *NumberOfOwnedPIDs) {
149
150     DIR *ProcDir;
151     struct dirent *ProcessDir;
152     pid_t UID,PID,PPID;
153     FILE *ProcFile;
154     String ProcFileName,Line;
155
156     if ((ProcDir = opendir("/proc")) == NULL) {
157         perror("ERROR: Cannot opendir /proc\n");
158         exit(EXIT_FAILURE);
159     }
160 //DEBUG printf("look for processes owned by %d\n",MyRealUID);
161
162     *NumberOfOwnedPIDs = 0;
163     while ((ProcessDir = readdir(ProcDir)) != NULL) {
164         if (isdigit(ProcessDir->d_name[0])) {
165             PID = (pid_t)atoi(ProcessDir->d_name);
166             sprintf(ProcFileName,"/proc/%d/status",PID);
167             if ((ProcFile = fopen(ProcFileName,"r")) != NULL) {
168                 PPID = -1;
169                 UID = -1;
170                 while ((PPID == -1 || UID == -1) &&
171 fgets(Line,STRING_LENGTH,ProcFile) != NULL) {
172                     sscanf(Line,"PPid: %d",&PPID);
173                     sscanf(Line,"Uid: %d",&UID);
174                 }
175                 fclose(ProcFile);
176 //----Check that data was found
177 //DEBUG printf("PID = %d PPID = %d UID = %d\n",PID,PPID,UID);
178                 if (PPID == -1 || UID == -1) {
179                     fprintf(stderr,"Could not get process information\n");
180                     exit(EXIT_FAILURE);
181                 }
182 //----Check if this process is owned by this user
183                 if (UID == MyRealUID) {
184 //----Record the PIDs as potentially relevant
185                     OwnedPIDs[*NumberOfOwnedPIDs].Active = 1;
186                     OwnedPIDs[*NumberOfOwnedPIDs].PID = PID;
187                     OwnedPIDs[*NumberOfOwnedPIDs].PPID = PPID;
188                     (*NumberOfOwnedPIDs)++;
189 //DEBUG printf("%d I own PID = %d PPID = %d UID = %d\n",*NumberOfOwnedPIDs,PID,PPID,UID);
190                     if (*NumberOfOwnedPIDs >= MAX_PROCESSES) {
191                         fprintf(stderr,"ERROR: Out of save process space\n");
192                         exit(EXIT_FAILURE);
193                     }
194                 }
195             } else {
196 //----Bloody child just died
197             }
198         }
199     }
200     closedir(ProcDir);
201 }
202 //---------------------------------------------------------------------------
203 float GetProcessTime(pid_t PID,int IncludeSelf,int IncludeChildren) {
204
205     FILE *ProcFile;
206     String ProcFileName;
207     float MyTime,ChildTime,ProcessTime;
208     int UserModeJiffies,SystemModeJiffies,ChildUserModeJiffies,
209 ChildSystemModeJiffies;
210
211     ProcessTime = 0;
212     sprintf(ProcFileName,"/proc/%d/stat",PID);
213     if ((ProcFile = fopen(ProcFileName,"r")) != NULL) {
214         fscanf(ProcFile,"%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %d %d %d %d",&UserModeJiffies,&SystemModeJiffies,&ChildUserModeJiffies,
215 &ChildSystemModeJiffies);
216         fclose(ProcFile);
217 //DEBUG printf("%d: my jiffies = %d, dead child jiffies = %d\n",PID,UserModeJiffies+SystemModeJiffies,ChildUserModeJiffies+ChildSystemModeJiffies);
218 //----Time used by this process
219         MyTime = ((float)(UserModeJiffies + SystemModeJiffies))/JIFFIES;
220 //----Time used by this process's dead children (man pages are wrong - does
221 //----not include my jiffies)
222         ChildTime = ((float)(ChildUserModeJiffies + ChildSystemModeJiffies))/
223 JIFFIES;
224         if (IncludeSelf) {
225             ProcessTime += MyTime;
226         }
227         if (IncludeChildren) {
228             ProcessTime += ChildTime;
229         }
230 //DEBUG printf("Process time for %d is %f\n",PID,ProcessTime);
231         return(ProcessTime);
232     } else {
233 //----Bloody process died, return 0 and catch it in the parent next time
234         return(0);
235     }
236 }
237 #endif
238 //---------------------------------------------------------------------------
239 #ifdef SUN
240 void GetProcessesOwnedByMe(uid_t MyRealUID,ProcessDataArray OwnedPIDs,
241 int *NumberOfOwnedPIDs) {
242
243     DIR *ProcDir;
244     struct dirent *ProcessDir;
245     pid_t PID;
246     struct psinfo ProcessRecord;
247     FILE *ProcFile;
248     String ProcFileName;
249
250     if ((ProcDir = opendir("/proc")) == NULL) {
251         perror("ERROR: Cannot opendir /proc\n");
252         exit(EXIT_FAILURE);
253     }
254
255 //DEBUG printf("look for processes owned by %d\n",MyRealUID);
256
257     *NumberOfOwnedPIDs = 0;
258     while ((ProcessDir = readdir(ProcDir)) != NULL) {
259         if (isdigit((int)ProcessDir->d_name[0])) {
260             PID = (pid_t)atoi(ProcessDir->d_name);
261             sprintf(ProcFileName,"/proc/%d/psinfo",(int)PID);
262             if ((ProcFile = fopen(ProcFileName,"r")) != NULL) {
263                 fread(&ProcessRecord,sizeof(ProcessRecord),1,ProcFile);
264                 fclose(ProcFile);
265 //----Check if this process is owned by this user
266                 if (ProcessRecord.pr_uid == MyRealUID) {
267 //----Record the PIDs as potentially relevant
268                     OwnedPIDs[*NumberOfOwnedPIDs].PID = PID;
269                     OwnedPIDs[*NumberOfOwnedPIDs].PPID = ProcessRecord.pr_ppid;
270                     (*NumberOfOwnedPIDs)++;
271                     if (*NumberOfOwnedPIDs >= MAX_PROCESSES) {
272                         fprintf(stderr,"ERROR: Out of save process space\n");
273                         exit(EXIT_FAILURE);
274                     }
275                 }
276             } else {
277 //----Bloody child just died
278             }
279         }
280     }
281     closedir(ProcDir);
282 }
283 //---------------------------------------------------------------------------
284 float GetProcessTime(pid_t PID,int IncludeSelf,int IncludeChildren) {
285
286     FILE *ProcFile;
287     String ProcFileName;
288     pstatus_t StatusRecord;
289     float ProcessTime;
290
291     ProcessTime = 0;
292     sprintf(ProcFileName,"/proc/%d/status",(int)PID);
293     if ((ProcFile = fopen(ProcFileName,"r")) != NULL) {
294         fread(&StatusRecord,sizeof(StatusRecord),1,ProcFile);
295         fclose(ProcFile);
296         if (IncludeSelf) {
297             ProcessTime += StatusRecord.pr_utime.tv_sec +
298 StatusRecord.pr_stime.tv_sec +
299 ((float)(StatusRecord.pr_utime.tv_nsec+StatusRecord.pr_stime.tv_nsec))/
300 NANOSECONDS;
301         }
302         if (IncludeChildren) {
303             ProcessTime += StatusRecord.pr_cutime.tv_sec +
304 StatusRecord.pr_cstime.tv_sec +
305 ((float)(StatusRecord.pr_cutime.tv_nsec+StatusRecord.pr_cstime.tv_nsec))/
306 NANOSECONDS;
307         }
308 //DEBUG printf("Process %d has used U %ld +n%ld + S %ld +n%ld + CU %ld +n%ld + CS %ld +n%ld = %.1f\n",
309 //DEBUG PID,StatusRecord.pr_utime.tv_sec,StatusRecord.pr_utime.tv_nsec,
310 //DEBUG StatusRecord.pr_stime.tv_sec,StatusRecord.pr_stime.tv_nsec,
311 //DEBUG StatusRecord.pr_cutime.tv_sec,StatusRecord.pr_cutime.tv_nsec,
312 //DEBUG StatusRecord.pr_cstime.tv_sec,StatusRecord.pr_cstime.tv_nsec,
313 //DEBUG ProcessTime);
314         return(ProcessTime);
315     } else {
316 //----Bloody child died, return 0 and catch it in the parent next time
317         return(0);
318     }
319 }
320 #endif //----SUN
321 //---------------------------------------------------------------------------
322 #ifdef OSX
323 //----No looking in /proc for OSX
324 #endif
325 //---------------------------------------------------------------------------
326 int PIDInArray(pid_t PID,ProcessDataArray PIDs,int NumberOfPIDs) {
327
328     int PIDIndex;
329
330     for (PIDIndex = 0;PIDIndex < NumberOfPIDs;PIDIndex++) {
331         if (PIDs[PIDIndex].PID == PID) {
332             return(1);
333         }
334     }
335     return(0);
336 }
337 //---------------------------------------------------------------------------
338 #if (defined(LINUX)||defined(SUN))
339 //----Only for systems that have /proc
340
341 int GetTreeTimes(uid_t MyRealUID,pid_t FirstBornPID,ProcessDataArray 
342 TreeTimes) {
343
344     ProcessDataArray OwnedPIDs;
345     int NumberOfOwnedPIDs;
346     int NumberOfTreeTimes;
347     int CurrentTreeIndex;
348     int OwnedIndex;
349
350 //----Get the list of processes owned by this user
351     GetProcessesOwnedByMe(MyRealUID,OwnedPIDs,&NumberOfOwnedPIDs);
352
353 //----Check that the root of the tree is still there
354 //DEBUG printf("Check if %d is alive\n",FirstBornPID);
355     if (!PIDInArray(FirstBornPID,OwnedPIDs,NumberOfOwnedPIDs)) {
356 //DEBUG printf("It is dead\n");
357         return(0);
358     }
359 //DEBUG printf("It is alive\n");
360
361 //----Find those in the process tree, and get their times
362     CurrentTreeIndex = 0;
363     TreeTimes[0].Active = 1;
364     TreeTimes[0].PID = FirstBornPID;
365     TreeTimes[0].PPID = MyRealUID;
366     TreeTimes[0].CPUTime = GetProcessTime(TreeTimes[0].PID,1,1);
367     NumberOfTreeTimes = 1;
368
369     while (CurrentTreeIndex < NumberOfTreeTimes) {
370 //DEBUG printf("%d %d is in the tree\n",TreeTimes[CurrentTreeIndex].PID,TreeTimes[CurrentTreeIndex].PPID);
371 //----Scan for offspring
372         TreeTimes[CurrentTreeIndex].CPUTime = 
373 GetProcessTime(TreeTimes[CurrentTreeIndex].PID,1,1);
374         for (OwnedIndex = 0; OwnedIndex < NumberOfOwnedPIDs; OwnedIndex++) {
375             if (OwnedPIDs[OwnedIndex].PPID == TreeTimes[CurrentTreeIndex].PID) {
376                 TreeTimes[NumberOfTreeTimes].Active = 1;
377                 TreeTimes[NumberOfTreeTimes].PID = OwnedPIDs[OwnedIndex].PID;
378                 TreeTimes[NumberOfTreeTimes].PPID = OwnedPIDs[OwnedIndex].PPID;
379                 NumberOfTreeTimes++;
380             }
381         }
382 //----Move on to the next process in the tree
383         CurrentTreeIndex++;
384     }
385
386     return(NumberOfTreeTimes);
387 }
388
389 #endif
390 #ifdef OSX
391 //----No /proc for OSX
392 #endif
393 //---------------------------------------------------------------------------
394 //----Send signals and reports if process is known to be gone
395 int SignalAndReport(int PID,int Signal,int RepeatUntilTerminated,
396 char * ProcessType) {
397
398     String ErrorMessage;
399     extern int errno;
400     int Terminated;
401     int NumberOfLoops;
402
403     Terminated = 0;
404     NumberOfLoops = 0;
405     do {
406         NumberOfLoops++;
407 //DEBUG printf("!!! TreeLimitedRun %d sends signal %d to %s %d\n",getpid(),Signal,ProcessType,PID);fflush(stdout);
408         if (kill(PID,Signal) != 0) {
409 //DEBUG printf("The kill errno is %d\n",errno);
410 //----If process no longer exists record that to avoid killing again
411             if (errno == ESRCH) {
412                 Terminated = 1;
413             } else {
414                 sprintf(ErrorMessage,
415 "!!! ERROR: TreeLimitedRun %d cannot signal %s %d with %d",getpid(),ProcessType,
416 PID,Signal);
417                 perror(ErrorMessage);
418             }
419         }
420 //----Must do perror after errno, as perror clears errno
421     } while (RepeatUntilTerminated && !Terminated && NumberOfLoops < 10);
422
423     return(Terminated);
424 }
425 //---------------------------------------------------------------------------
426 void ChildKillTree(int TargetIndex,ProcessDataArray TreeTimes,
427 int NumberInTree,int Signal) {
428
429     int ChildIndex;
430
431 //DEBUG printf("!!! TreeLimitedRun %d killing tree below PID %d with %d\n",getpid(),TreeTimes[TargetIndex].PID,Signal);fflush(stdout);
432     for (ChildIndex=0; ChildIndex < NumberInTree; ChildIndex++) {
433         if (TreeTimes[TargetIndex].PID == TreeTimes[ChildIndex].PPID) {
434             ChildKillTree(ChildIndex,TreeTimes,NumberInTree,Signal);
435         }
436 //TOO SLOW? usleep(100000);
437     }
438     if (TreeTimes[TargetIndex].Active) {
439         if (SignalAndReport(TreeTimes[TargetIndex].PID,Signal,
440 Signal == SIGKILL,"tree process")) {
441             TreeTimes[TargetIndex].Active = 0;
442         }
443     }
444 }
445 //---------------------------------------------------------------------------
446 int KillTree(uid_t MyRealUID,pid_t FirstBornPID,int Signal) {
447
448     int NumberOfTreeTimes;
449     extern int errno;
450
451 #if (defined(LINUX)||defined(SUN))
452     ProcessDataArray TreeTimes;
453
454     if ((NumberOfTreeTimes = GetTreeTimes(MyRealUID,FirstBornPID,TreeTimes)) > 
455 0) {
456
457 //----The first born gets it first so that it can curb its descendants nicely
458         if (TreeTimes[0].Active) {
459 //DEBUG printf("!!! TreeLimitedRun %d killing top process %d with %d\n",getpid(),TreeTimes[0].PID,Signal);fflush(stdout);
460 //----TreeTimes[0].PID is FirstBornPID
461             if (SignalAndReport(TreeTimes[0].PID,Signal,Signal == SIGKILL,
462 "top process")) {
463                 TreeTimes[0].Active = 0;
464             }
465         }
466
467 //----200000 is not enough - EP's eproof script gets killed before it can
468 //----kill eprover
469         usleep(500000);
470 //----Now gently from the bottom up
471         ChildKillTree(0,TreeTimes,NumberOfTreeTimes,Signal);
472         usleep(100000);
473 //----Now viciously from the bottom up
474         ChildKillTree(0,TreeTimes,NumberOfTreeTimes,SIGKILL);
475     }
476 #endif
477 #ifdef OSX
478     int Active;
479
480     Active = 1;
481 //----The first born gets it first so that it can curb its descendants nicely
482     if (SignalAndReport(FirstBornPID,Signal,Signal == SIGKILL,"top process")) {
483         Active = 0;
484     }
485     if (Active) {
486         usleep(100000);
487 //----Now viciously
488         SignalAndReport(FirstBornPID,SIGKILL,1,"only process");
489     }
490     NumberOfTreeTimes = 1;
491 #endif
492
493     return(NumberOfTreeTimes);
494 }
495 //---------------------------------------------------------------------------
496 void KillOrphans(uid_t MyRealUID,ProcessDataArray SavePIDs,
497 int NumberOfSavePIDs) {
498
499 #if (defined(LINUX)||defined(SUN))
500
501     ProcessDataArray OwnedPIDs;
502     int NumberOfOwnedPIDs;
503     int OwnedIndex;
504     int NumberOfOrphansKilled;
505     extern int errno;
506
507     do {
508 //----Get the list of processes owned by this user
509         GetProcessesOwnedByMe(MyRealUID,OwnedPIDs,&NumberOfOwnedPIDs);
510
511         NumberOfOrphansKilled = 0;
512         for (OwnedIndex = 0; OwnedIndex < NumberOfOwnedPIDs; OwnedIndex++) {
513 //DEBUG printf("!!! TreeLimitedRun %d considers %d with parent %d\n",getpid(),OwnedPIDs[OwnedIndex].PID, OwnedPIDs[OwnedIndex].PPID);fflush(stdout);
514             if (OwnedPIDs[OwnedIndex].PPID == 1 &&
515 !PIDInArray(OwnedPIDs[OwnedIndex].PID,SavePIDs,NumberOfSavePIDs)) {
516 //DEBUG printf("!!! TreeLimitedRun %d kills orphan %d\n",getpid(),OwnedPIDs[OwnedIndex].PID);fflush(stdout);
517                 if (SignalAndReport(OwnedPIDs[OwnedIndex].PID,SIGQUIT,0,
518 "orphan")) {
519                     OwnedPIDs[OwnedIndex].Active = 0;
520                 }
521                 if (OwnedPIDs[OwnedIndex].Active) {
522                     sleep(1);
523                     SignalAndReport(OwnedPIDs[OwnedIndex].PID,SIGKILL,1,
524 "orphan");
525                 }
526                 NumberOfOrphansKilled++;
527             }
528         }
529         if (NumberOfOrphansKilled > 0) {
530             printf("Killed %d orphans\n",NumberOfOrphansKilled);
531         }
532     } while (NumberOfOrphansKilled > 0);
533
534 #endif
535 #ifdef OSX
536 //----No orphans known for OSX
537 #endif
538 }
539 //---------------------------------------------------------------------------
540 void PrintTimes(char* Tag,float TreeCPUTime,float WCTime) {
541
542 //----You can print times with more accuracy here
543     printf("%s: %.1f CPU %.1f WC\n",Tag,TreeCPUTime,WCTime);
544     fflush(NULL);
545 }
546 //---------------------------------------------------------------------------
547 float WallClockSoFar(struct timeval WCStartTime) {
548
549     struct timeval WCEndTime;
550
551     gettimeofday(&WCEndTime,NULL);
552 //DEBUG printf("Started at %ld +%f and ended at %ld +%f\n",
553 //DEBUG WCStartTime.tv_sec,WCStartTime.tv_usec/MICROSECONDS,
554 //DEBUG WCEndTime.tv_sec,WCEndTime.tv_usec/MICROSECONDS);
555
556     return((WCEndTime.tv_sec - WCStartTime.tv_sec) +
557 (WCEndTime.tv_usec - WCStartTime.tv_usec)/MICROSECONDS);
558
559 }
560 //---------------------------------------------------------------------------
561 double AccumulateTreeTime(int TargetIndex,ProcessDataArray TreeTimes,
562 int NumberInTree) {
563
564     int ChildIndex;
565
566     TreeTimes[TargetIndex].AccumulatedCPUTime = TreeTimes[TargetIndex].CPUTime;
567     for (ChildIndex=0; ChildIndex < NumberInTree; ChildIndex++) {
568         if (TreeTimes[TargetIndex].PID == TreeTimes[ChildIndex].PPID) {
569             TreeTimes[TargetIndex].AccumulatedCPUTime += 
570 AccumulateTreeTime(ChildIndex,TreeTimes,NumberInTree);
571         }
572     }
573
574     return(TreeTimes[TargetIndex].AccumulatedCPUTime);
575 }
576 //---------------------------------------------------------------------------
577 float WatchChildTree(int MyPID,int ChildPID,int CPUTimeLimit,
578 int DelayBetweenChecks,struct timeval WCStartTime,int PrintEachCheck) {
579
580     double TreeTime,LastTreeTime,LostTime;
581     int NumberInTree;
582     int KilledInTree;
583     int Status;
584     int DeadPID;
585 #if (defined(LINUX)||defined(SUN))
586     ProcessDataArray TreeTimes;
587 #endif
588 #ifdef OSX
589     struct rusage ResourceUsage;
590 #endif
591
592     LastTreeTime = 0.0;
593     LostTime = 0.0;
594
595 //----Loop watching times taken. Order is important - get time before
596 //----checking for interrupt
597     do {
598 #if (defined(LINUX)||defined(SUN))
599 //----Look at the tree
600         NumberInTree = GetTreeTimes(getuid(),ChildPID,TreeTimes);
601         TreeTime = AccumulateTreeTime(0,TreeTimes,NumberInTree);
602 //DEBUG fprintf(stderr,"now %5.2f limit %d\n",TreeTime,CPUTimeLimit);
603 //----For those with /proc, reap the children. Need to reap late so /proc
604 //----entries do not disappear
605         while ((DeadPID = waitpid(-1,&Status,WNOHANG)) > 0) {
606             NumberInTree--;
607 //DEBUG fprintf(stderr,"The child %d has died\n",DeadPID);
608         }
609 #endif
610 #ifdef OSX
611 //----Check if child is gone (-1 if no child, 0 if not dead, PID if dead)
612         DeadPID = waitpid(-1,&Status,WNOHANG);
613         if (DeadPID == ChildPID || DeadPID == -1) {
614             TreeTime = LastTreeTime;
615             NumberInTree = 0;
616         } else {
617 //----Maybe more than 1, but we don't know in OSX version
618             NumberInTree = 1;
619         }
620         if (getrusage(RUSAGE_CHILDREN,&ResourceUsage) != -1) {
621             TreeTime = ResourceUsage.ru_utime.tv_sec +
622 ResourceUsage.ru_utime.tv_usec / 1000000 + ResourceUsage.ru_stime.tv_sec +
623 ResourceUsage.ru_stime.tv_usec / 1000000;
624         } else {
625             printf("TreeLimitedRun could not getrusage\n");
626             TreeTime = LastTreeTime;
627         }
628 #endif
629
630 //DEBUG fprintf(stderr,"acc time is %.2f\n",TreeTime);
631         if (TreeTime < LastTreeTime) {
632             LostTime += LastTreeTime - TreeTime;
633             printf("WARNING: TreeLimitedRun lost %.2fs, total lost is %.2fs\n",
634 LastTreeTime - TreeTime,LostTime);
635         }
636 //DEBUG printf("lost time is %.2f\n",LostTime);
637         LastTreeTime = TreeTime;
638         TreeTime += LostTime;
639
640 //----Print each loop if requested
641         if (PrintEachCheck) {
642             PrintTimes("WATCH",TreeTime,WallClockSoFar(WCStartTime));
643         }
644 //----If we're going to loop, wait a bit first
645 //----DANGER - if the last descedantprocess dies between GetTreeTimes() and 
646 //----here, it still waits.
647         if ((CPUTimeLimit == 0 || TreeTime <= CPUTimeLimit) &&
648 NumberInTree > 0 && !GlobalInterrupted) {
649             sleep(DelayBetweenChecks);
650         }
651     } while ((CPUTimeLimit == 0 || TreeTime <= CPUTimeLimit) && 
652 NumberInTree > 0 && !GlobalInterrupted);
653
654 //----From now on reap normally
655     if (signal(SIGCHLD,SIGCHLDReaper) == SIG_ERR) {
656         perror("ERROR: Could not set SIGCHLD handler");
657         exit(EXIT_FAILURE);
658     }
659 //----Reap anyway in case I missed some
660     SIGCHLDReaper(0);
661
662 //----If over time limit, stop them all (XCPU to top guy first)
663     if (NumberInTree > 0 && TreeTime > CPUTimeLimit) {
664         KilledInTree = KillTree(getuid(),ChildPID,SIGXCPU);
665 //DEBUG printf("Killed %d in tree\n",KilledInTree);
666     }
667
668 //----If global interrupted, then send it on
669     if (NumberInTree > 0 && GlobalInterrupted) {
670         KilledInTree = KillTree(getuid(),ChildPID,GlobalSignalReceived);
671 //DEBUG printf("Killed %d in tree\n",KilledInTree);
672     }
673
674     return(TreeTime);
675 }
676 //---------------------------------------------------------------------------
677 int main(int argc,char *argv[]) {
678
679     int CPUTimeLimit;
680     int WCTimeLimit;
681     int MemoryLimit;
682     int ArgNumber;
683     int QuietnessLevel;
684     int ArgOffset;
685     pid_t ChildPID;
686     float TreeCPUTime;
687     float WCTime;
688     struct timeval WCStartTime;
689     int DelayBetweenChecks;
690     int PrintEachCheck = 0;
691     ProcessDataArray SavePIDs;
692     int NumberOfSavePIDs;
693     int Status;
694
695 //----Check the quietness level
696     if (argc >= 2 && strstr(argv[1],"-q") == argv[1]) {
697         ArgOffset = 1;
698         QuietnessLevel = atoi(&argv[ArgOffset][2]);
699     } else {
700         QuietnessLevel = 1;
701         ArgOffset = 0;
702     }
703
704 //----Look for time and print flags
705     if (argc >= ArgOffset+2 &&
706 strstr(argv[ArgOffset+1],"-t") == argv[ArgOffset+1]) {
707         ArgOffset++;
708         DelayBetweenChecks = atoi(&argv[ArgOffset][2]);
709     } else {
710         if (argc >= ArgOffset+2 &&
711 strstr(argv[ArgOffset+1],"-p") == argv[ArgOffset+1]) {
712             PrintEachCheck = 1;
713             ArgOffset++;
714             DelayBetweenChecks = atoi(&argv[ArgOffset][2]);
715         } else {
716             DelayBetweenChecks = DEFAULT_DELAY_BETWEEN_CHECKS;
717         }
718     }
719
720     if (argc - ArgOffset >= 4) {
721 //----Redirect stderr to stdout
722         if (dup2(STDOUT,STDERR) == -1) {
723             perror("ERROR: Cannot dup STDERR to STDOUT");
724         }
725
726 //----Extract time limits
727         CPUTimeLimit = atoi(argv[ArgOffset+1]);
728         WCTimeLimit = atoi(argv[ArgOffset+2]);
729         if (isdigit((int)argv[ArgOffset+3][0])) {
730             MemoryLimit = atoi(argv[ArgOffset+3]);
731             ArgOffset++;
732         } else {
733             MemoryLimit = 0;
734         }
735
736         if (QuietnessLevel == 0) {
737             printf(
738 "TreeLimitedRun: ----------------------------------------------------------\n");
739             printf("TreeLimitedRun: %s ",argv[ArgOffset+3]);
740             for (ArgNumber=ArgOffset+4;ArgNumber<argc;ArgNumber++)
741                 printf("%s ",argv[ArgNumber]);
742             printf("\n");
743             printf("TreeLimitedRun: CPU time limit is %ds\n",CPUTimeLimit);
744             printf("TreeLimitedRun: WC  time limit is %ds\n",WCTimeLimit);
745             if (MemoryLimit > 0) {
746                 printf("TreeLimitedRun: Memory   limit is %dbytes\n",
747 MemoryLimit);
748             }
749 //----Output the PID for possible later use
750             printf("TreeLimitedRun: PID is %d\n",(int)getpid());
751             printf(
752 "TreeLimitedRun: ----------------------------------------------------------\n");
753             fflush(stdout);
754         }
755         SetNoCoreDump();
756
757 //----Set handler for when child dies
758         if (signal(SIGCHLD,SIGCHLDHandler) == SIG_ERR) {
759             perror("ERROR: Could not set SIGCHLD handler");
760             exit(EXIT_FAILURE);
761         }
762 //----Set handler for global interruptions and alarms
763         GlobalInterrupted = 0;
764         GlobalSignalReceived = 0;
765         if (signal(SIGQUIT,SIGQUITHandler) == SIG_ERR) {
766             perror("ERROR: Could not set SIGQUIT handler");
767             exit(EXIT_FAILURE);
768         }
769         if (signal(SIGALRM,SIGQUITHandler) == SIG_ERR) {
770             perror("ERROR: Could not set SIGALRM handler");
771             exit(EXIT_FAILURE);
772         }
773
774 #if (defined(LINUX)||defined(SUN))
775 //----Record running processes at start (xeyes, gnome, etc)
776         GetProcessesOwnedByMe(getuid(),SavePIDs,&NumberOfSavePIDs);
777 #endif
778 #ifdef OSX
779 //----No recording processes for OSX
780 #endif
781
782 //----Fork for ATP process
783         if ((ChildPID = fork()) == -1) {
784             perror("ERROR: Cannot fork for ATP system process");
785             exit(EXIT_FAILURE);
786         }
787
788 //----In child, set limits and execute the ATP system
789         if (ChildPID == 0) {
790             if (setvbuf(stdout,NULL,_IONBF,0) != 0) {
791                 perror("Setting unbuffered");
792             }
793 //DEBUG printf("The prover PID will be %d\n",getpid());
794 //----Set memory limit for child only
795             if (MemoryLimit > 0) {
796                 SetMemoryLimit(MemoryLimit);
797             }
798
799 #if (defined(LINUX)||defined(SUN))
800 //----Systems with /proc are stopped by this program with kill
801 #endif
802 #ifdef OSX
803 //----In OSX case, child must limit itself
804             if (CPUTimeLimit > 0) {
805                 SetCPUTimeLimit(CPUTimeLimit);
806             }
807 #endif
808
809             execvp(argv[ArgOffset+3],argv+ArgOffset+3);
810             perror("Cannot exec");
811             exit(EXIT_FAILURE);
812
813 //----In parent, set limits and watch the ATP system
814         } else {
815             if (WCTimeLimit > 0) {
816                 alarm(WCTimeLimit);
817             }
818 //----Record start time
819             gettimeofday(&WCStartTime,NULL);
820             TreeCPUTime = 0;
821             WCTime = 0;
822
823 //----Watch the tree of processes
824             TreeCPUTime = WatchChildTree(getpid(),ChildPID,CPUTimeLimit,
825 DelayBetweenChecks,WCStartTime,PrintEachCheck);
826
827 //----Record end WC time
828             WCTime = WallClockSoFar(WCStartTime);
829
830 //----See if the time is increased by looking at my children
831 //----Not sure what this was for
832 //            ChildTime = GetProcessTime(getpid(),0,1);
833 //            if (ChildTime > TreeCPUTime) {
834 //                TreeCPUTime = ChildTime;
835 //            }
836
837             PrintTimes("FINAL WATCH",TreeCPUTime,WCTime);
838
839 //----Sweep for orphans
840             KillOrphans(getuid(),SavePIDs,NumberOfSavePIDs);
841 //----Reap any remaining
842             while (wait(&Status) != -1) {
843                 sleep(1);
844 //DEBUG printf("!!! Waiting for children that havn't died\n");
845             }
846         }
847     } else {
848         printf("Usage: %s [-q<quietness>] [-t<check delay>|-p<print check delay>] <CPU limit> <WC limit> [<Memory limit>] <Job>\n",
849 argv[0]);
850     }
851
852     return(0);
853 }
854 //---------------------------------------------------------------------------