Drop hardening.patch, applied upstream.
[debian/cpulimit.git] / cpulimit.c
1 /**
2  * This program is licensed under the GNU General Public License,
3  * version 2. A copy of the license can be found in the accompanying
4  * LICENSE file.
5  *
6  **********************************************************************
7  *
8  * Simple program to limit the cpu usage of a process
9  * If you modify this code, send me a copy please
10  *
11  * Author:  Angelo Marletta
12  * Date:    26/06/2005
13  * Version: 1.1
14  *
15  * Modifications and updates by: Jesse Smith
16  * Date: May 4, 2011
17  * Version 1.2
18  *
19  */
20
21
22 #include <getopt.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <time.h>
26 #include <sys/time.h>
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #include <unistd.h>
30 #include <sys/types.h>
31 #include <signal.h>
32 #include <sys/resource.h>
33 #include <string.h>
34 #include <dirent.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <limits.h>    // for compatibility
38
39 #ifdef __MACH__
40 #include <mach/clock.h>
41 #include <mach/mach.h>
42 #endif
43
44
45 #ifdef FREEBSD
46 #include <fcntl.h>
47 #include <kvm.h>
48 #include <paths.h>
49 #include <sys/param.h>
50 #include <sys/sysctl.h>
51 #include <sys/user.h>
52 #endif
53
54
55 //kernel time resolution (inverse of one jiffy interval) in Hertz
56 //i don't know how to detect it, then define to the default (not very clean!)
57 #define HZ 100
58
59 //some useful macro
60 #define min(a,b) (a<b?a:b)
61 #define max(a,b) (a>b?a:b)
62
63 // For platforms without PATH_MAX
64 #ifndef PATH_MAX
65 #define PATH_MAX 4096
66 #endif
67
68 #define BEST_PRIORITY -10
69
70 #ifndef TRUE
71 #define TRUE 1
72 #endif
73 #ifndef FALSE
74 #define FALSE 0
75 #endif
76
77 #ifndef VERSION
78 #define VERSION 1.7
79 #endif
80
81 //pid of the controlled process
82 pid_t pid=0;
83 pid_t my_pid;     // this process's PID
84
85 //executable file name
86 char *program_name;
87 //verbose mode
88 int verbose = FALSE;
89 //lazy mode
90 int lazy = FALSE;
91 // is higher priority nice possible?
92 int nice_lim;
93
94 // number of CPUs we detected
95 int NCPU;
96
97 //reverse byte search
98 // void *memrchr(const void *s, int c, size_t n);
99
100
101
102 //return ta-tb in microseconds (no overflow checks!)
103 inline long timediff(const struct timespec *ta,const struct timespec *tb) {
104     unsigned long us = (ta->tv_sec-tb->tv_sec)*1000000 + (ta->tv_nsec/1000 - tb->tv_nsec/1000);
105     return us;
106 }
107
108
109
110 int Check_Us(pid_t target_pid)
111 {
112    pid_t this_pid;
113
114    this_pid = getpid();
115    if (this_pid == target_pid)
116    {
117       printf("We cannot throttle ourselves.\n");
118       exit(7);
119    }
120    return TRUE;
121 }
122
123
124 int waitforpid(int pid) {
125         //switch to low priority
126         // if (setpriority(PRIO_PROCESS,getpid(),19)!=0) {
127         /*
128         if ( (nice_lim < INT_MAX) && 
129              (setpriority(PRIO_PROCESS, my_pid, 19) != 0) ) {
130                 printf("Warning: cannot renice\n");
131         }
132         */
133         int i=0;
134
135         while(1) {
136
137                 DIR *dip;
138                 struct dirent *dit;
139
140                 //open a directory stream to /proc directory
141                 if ((dip = opendir("/proc")) == NULL) {
142                         perror("opendir");
143                         return -1;
144                 }
145
146                 //read in from /proc and seek for process dirs
147                 while ((dit = readdir(dip)) != NULL) {
148                         //get pid
149                         if (pid==atoi(dit->d_name)) {
150                                 //pid detected
151                                 Check_Us(pid);
152                                 if (kill(pid,SIGSTOP)==0 &&  kill(pid,SIGCONT)==0) {
153                                         //process is ok!
154                                         if (closedir(dip) == -1) {
155                                            perror("closedir");
156                                            return -1;
157                                         }
158                                         goto done;
159                                 }
160                                 else {
161                                         fprintf(stderr,"Error: Process %d detected, but you don't have permission to control it\n",pid);
162                                 }
163                         }
164                 }
165
166                 //close the dir stream and check for errors
167                 if (closedir(dip) == -1) {
168                         perror("closedir");
169                         return -1;
170                 }
171
172                 //no suitable target found
173                 if (i++==0) {
174                         if (lazy) {
175                                 fprintf(stderr,"No process found\n");
176                                 exit(2);
177                         }
178                         else {
179                                 printf("Warning: no target process found. Waiting for it...\n");
180                         }
181                 }
182
183                 //sleep for a while
184                 sleep(2);
185         }
186
187 done:
188         printf("Process %d detected\n",pid);
189         //now set high priority, if possible
190         // if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) {
191         /*
192         if ( (nice_lim < INT_MAX) &&
193              (setpriority(PRIO_PROCESS, my_pid, nice_lim) != 0) ) {
194                 printf("Warning: cannot renice.\nTo work better you should run this program as root.\n");
195         }
196         */
197         return 0;
198
199 }
200
201 //this function periodically scans process list and looks for executable path names
202 //it should be executed in a low priority context, since precise timing does not matter
203 //if a process is found then its pid is returned
204 //process: the name of the wanted process, can be an absolute path name to the executable file
205 //         or simply its name
206 //return: pid of the found process
207 int getpidof(const char *process) {
208
209         //set low priority
210         // if (setpriority(PRIO_PROCESS,getpid(),19)!=0) {
211         /*
212         if ( (nice_lim < INT_MAX) &&
213              (setpriority(PRIO_PROCESS, my_pid, 19) != 0) ) {
214                 printf("Warning: cannot renice\n");
215         }
216         */
217         char exelink[20];
218         char exepath[PATH_MAX+1];
219         int pid=0;
220         int i=0;
221
222         while(1) {
223
224                 DIR *dip;
225                 struct dirent *dit;
226
227                 //open a directory stream to /proc directory
228                 if ((dip = opendir("/proc")) == NULL) {
229                         perror("opendir");
230                         return -1;
231                 }
232
233                 //read in from /proc and seek for process dirs
234                 while ((dit = readdir(dip)) != NULL) {
235                         //get pid
236                         pid=atoi(dit->d_name);
237                         if (pid>0) {
238                                 sprintf(exelink,"/proc/%d/exe",pid);
239                                 int size=readlink(exelink,exepath,sizeof(exepath));
240                                 if (size>0) {
241                                         int found=0;
242                                         if (process[0]=='/' && strncmp(exepath,process,size)==0 && size==strlen(process)) {
243                                                 //process starts with / then it's an absolute path
244                                                 found=1;
245                                         }
246                                         else {
247                                                 //process is the name of the executable file
248                                                 if (strncmp(exepath+size-strlen(process),process,strlen(process))==0) {
249                                                         found=1;
250                                                 }
251                                         }
252                                         if (found==1) {
253                                         Check_Us(pid);
254                                                 if (kill(pid,SIGSTOP)==0 &&  kill(pid,SIGCONT)==0) {
255                                                         //process is ok!
256                                                         if (closedir(dip) == -1) {
257                                                           perror("closedir");
258                                                           return -1;
259                                                         }
260                                                         goto done;
261                                                 }
262                                                 else {
263                                                         fprintf(stderr,"Error: Process %d detected, but you don't have permission to control it\n",pid);
264                                                 }
265                                         }
266                                 }
267                         }
268                 }
269
270                 //close the dir stream and check for errors
271                 if (closedir(dip) == -1) {
272                         perror("closedir");
273                         return -1;
274                 }
275
276                 //no suitable target found
277                 if (i++==0) {
278                         if (lazy) {
279                                 fprintf(stderr,"No process found\n");
280                                 exit(2);
281                         }
282                         else {
283                                 printf("Warning: no target process found. Waiting for it...\n");
284                         }
285                 }
286
287                 //sleep for a while
288                 sleep(2);
289         }
290
291 done:
292         printf("Process %d detected\n",pid);
293         //now set high priority, if possible
294         // if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) {
295         /*
296         if ( (nice_lim < INT_MAX) &&
297              (setpriority(PRIO_PROCESS, my_pid, nice_lim) != 0) ) {
298                 printf("Warning: cannot renice.\nTo work better you should run this program as root.\n");
299         }
300         */
301         return pid;
302
303 }
304
305 //SIGINT and SIGTERM signal handler
306 void quit(int sig) {
307         //let the process continue if it's stopped
308         kill(pid,SIGCONT);
309         printf("Exiting...\n");
310         exit(0);
311 }
312
313
314 #ifdef FREEBSD
315 //get jiffies count from /proc filesystem
316 int getjiffies(int pid)
317 {
318    kvm_t *my_kernel = NULL;
319    struct kinfo_proc *process_data = NULL;
320    int processes;
321    int my_jiffies = -1;
322
323    my_kernel = kvm_open(0, 0, 0, O_RDONLY, "kvm_open");
324    if (! my_kernel)
325    {
326       printf("Error opening kernel vm. You should be running as root.\n");
327       return -1;
328    }
329
330    process_data = kvm_getprocs(my_kernel, KERN_PROC_PID, pid, &processes);
331    if ( (process_data) && (processes >= 1) )
332        my_jiffies = process_data->ki_runtime;
333    
334    kvm_close(my_kernel);
335    if (my_jiffies >= 0)
336      my_jiffies /= 1000;
337    return my_jiffies;
338 }
339
340 #endif
341
342 #ifdef LINUX
343 int getjiffies(int pid) {
344         static char stat[20];
345         static char buffer[1024];
346         char *p;
347         sprintf(stat,"/proc/%d/stat",pid);
348         FILE *f=fopen(stat,"r");
349         if (f==NULL) return -1;
350         p = fgets(buffer,sizeof(buffer),f);
351         fclose(f);
352         // char *p=buffer;
353         if (p)
354         {
355           p=memchr(p+1,')',sizeof(buffer)-(p-buffer));
356           int sp=12;
357           while (sp--)
358                 p=memchr(p+1,' ',sizeof(buffer)-(p-buffer));
359           //user mode jiffies
360           int utime=atoi(p+1);
361           p=memchr(p+1,' ',sizeof(buffer)-(p-buffer));
362           //kernel mode jiffies
363           int ktime=atoi(p+1);
364           return utime+ktime;
365         }
366         // could not read info
367         return -1;
368 }
369 #endif
370
371
372 //process instant photo
373 struct process_screenshot {
374         struct timespec when;   //timestamp
375         int jiffies;    //jiffies count of the process
376         int cputime;    //microseconds of work from previous screenshot to current
377 };
378
379 //extracted process statistics
380 struct cpu_usage {
381         float pcpu;
382         float workingrate;
383 };
384
385 //this function is an autonomous dynamic system
386 //it works with static variables (state variables of the system), that keep memory of recent past
387 //its aim is to estimate the cpu usage of the process
388 //to work properly it should be called in a fixed periodic way
389 //perhaps i will put it in a separate thread...
390 int compute_cpu_usage(int pid,int last_working_quantum,struct cpu_usage *pusage) {
391         #define MEM_ORDER 10
392         //circular buffer containing last MEM_ORDER process screenshots
393         static struct process_screenshot ps[MEM_ORDER];
394         //the last screenshot recorded in the buffer
395         static int front=-1;
396         //the oldest screenshot recorded in the buffer
397         static int tail=0;
398
399         if (pusage==NULL) {
400                 //reinit static variables
401                 front=-1;
402                 tail=0;
403                 return 0;
404         }
405
406         //let's advance front index and save the screenshot
407         front=(front+1)%MEM_ORDER;
408         int j=getjiffies(pid);
409         if (j>=0) ps[front].jiffies=j;
410         else return -1; //error: pid does not exist
411
412         #ifdef __MACH__ 
413         // OS X does not have clock_gettime, use clock_get_time
414         clock_serv_t cclock;
415         mach_timespec_t mts;
416         host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
417         clock_get_time(cclock, &mts);
418         mach_port_deallocate(mach_task_self(), cclock);
419         ps[front].when.tv_sec = mts.tv_sec;
420         ps[front].when.tv_nsec = mts.tv_nsec;
421
422         #else
423         // Linux and BSD can use real time
424         clock_gettime(CLOCK_REALTIME,&(ps[front].when));
425         ps[front].cputime=last_working_quantum;
426         #endif
427         //buffer actual size is: (front-tail+MEM_ORDER)%MEM_ORDER+1
428         int size=(front-tail+MEM_ORDER)%MEM_ORDER+1;
429
430         if (size==1) {
431                 //not enough samples taken (it's the first one!), return -1
432                 pusage->pcpu=-1;
433                 pusage->workingrate=1;
434                 return 0;
435         }
436         else {
437                 //now we can calculate cpu usage, interval dt and dtwork are expressed in microseconds
438                 long dt=timediff(&(ps[front].when),&(ps[tail].when));
439                 long dtwork=0;
440                 int i=(tail+1)%MEM_ORDER;
441                 int max=(front+1)%MEM_ORDER;
442                 do {
443                         dtwork+=ps[i].cputime;
444                         i=(i+1)%MEM_ORDER;
445                 } while (i!=max);
446                 int used=ps[front].jiffies-ps[tail].jiffies;
447                 float usage=(used*1000000.0/HZ)/dtwork;
448                 pusage->workingrate=1.0*dtwork/dt;
449                 pusage->pcpu=usage*pusage->workingrate;
450                 if (size==MEM_ORDER)
451                         tail=(tail+1)%MEM_ORDER;
452                 return 0;
453         }
454         #undef MEM_ORDER
455 }
456
457 void print_caption() {
458         printf("\n%%CPU\twork quantum\tsleep quantum\tactive rate\n");
459 }
460
461
462 void increase_priority()
463 {
464         //find the best available nice value
465         int old_priority = getpriority(PRIO_PROCESS, 0);
466         int priority = old_priority;
467         while ( (setpriority(PRIO_PROCESS, 0, priority-1) == 0) &&
468                 (priority > BEST_PRIORITY) )
469         {
470                 priority--;
471         }
472         if (priority != old_priority) {
473                 if (verbose) printf("Priority changed to %d\n", priority);
474         }
475         else {
476                 if (verbose) printf("Warning: Cannot change priority. Run as root or renice for best results.\n");
477         }
478
479
480 }
481
482
483
484 void print_usage(FILE *stream,int exit_code) {
485         fprintf(stream, "CPUlimit version %1.1f\n", VERSION);
486         fprintf(stream, "Usage: %s TARGET [OPTIONS...]\n",program_name);
487         fprintf(stream, "   TARGET must be exactly one of these:\n");
488         fprintf(stream, "      -p, --pid=N        pid of the process\n");
489         fprintf(stream, "      -e, --exe=FILE     name of the executable program file\n");
490         fprintf(stream, "                         The -e option only works when\n");
491         fprintf(stream, "                         cpulimit is run with admin rights.\n");
492         fprintf(stream, "      -P, --path=PATH    absolute path name of the\n");
493         fprintf(stream, "                         executable program file\n");
494         fprintf(stream, "   OPTIONS\n");
495         fprintf(stream, "      -b  --background   run in background\n");
496         fprintf(stream, "      -c  --cpu=N        override the detection of CPUs on the machine.\n");
497         fprintf(stream, "      -l, --limit=N      percentage of cpu allowed from 1 up.\n");
498         fprintf(stream, "                         Usually 1 - %d00, but can be higher\n", NCPU);
499         fprintf(stream, "                         on multi-core CPUs (mandatory)\n");
500         fprintf(stream, "      -v, --verbose      show control statistics\n");
501         fprintf(stream, "      -z, --lazy         exit if there is no suitable target process,\n");
502         fprintf(stream, "                         or if it dies\n");
503         fprintf(stream, "      -h, --help         display this help and exit\n");
504         exit(exit_code);
505 }
506
507
508
509 // Get the number of CPU cores on this machine.
510 int get_ncpu()
511 {
512         int ncpu = 1;
513 #ifdef _SC_NPROCESSORS_ONLN
514         ncpu = sysconf(_SC_NPROCESSORS_ONLN);
515 #endif
516         return ncpu;
517 }
518
519
520
521
522 int main(int argc, char **argv) {
523
524         //get program name
525         // char *p=(char*)memrchr(argv[0],(unsigned int)'/',strlen(argv[0]));
526         // program_name = p==NULL?argv[0]:(p+1);
527         program_name = argv[0];
528         int run_in_background = FALSE;
529         //parse arguments
530         int next_option;
531         /* A string listing valid short options letters. */
532         const char* short_options="p:e:P:l:c:bvzh";
533         /* An array describing valid long options. */
534         const struct option long_options[] = {
535                 { "pid", required_argument, NULL, 'p' },
536                 { "exe", required_argument, NULL, 'e' },
537                 { "path", required_argument, NULL, 'P' },
538                 { "limit", required_argument, NULL, 'l' },
539                 { "background", no_argument, NULL, 'b' },
540                 { "verbose", no_argument, NULL, 'v' },
541                 { "lazy", no_argument, NULL, 'z' },
542                 { "help", no_argument, NULL, 'h' },
543                 { "cpu", required_argument, NULL, 'c'},
544                 { NULL, 0, NULL, 0 }
545         };
546         //argument variables
547         const char *exe=NULL;
548         const char *path=NULL;
549         int perclimit=0;
550         int pid_ok = FALSE;
551         int process_ok = FALSE;
552         int limit_ok = FALSE;
553         int last_known_argument = 0;
554         // struct rlimit maxlimit;
555
556         NCPU = get_ncpu();
557
558         do {
559                 next_option = getopt_long (argc, argv, short_options,long_options, NULL);
560                 switch(next_option) {
561                         case 'b':
562                                 run_in_background = TRUE;
563                                 last_known_argument++;
564                                 break;
565                         case 'p':
566                                 pid=atoi(optarg);
567                                 pid_ok = TRUE;
568                                 lazy = TRUE;
569                                 last_known_argument += 2;
570                                 break;
571                         case 'e':
572                                 exe=optarg;
573                                 process_ok = TRUE;
574                                 last_known_argument += 2;
575                                 break;
576                         case 'P':
577                                 path=optarg;
578                                 process_ok = TRUE;
579                                 last_known_argument += 2;
580                                 break;
581                         case 'l':
582                                 perclimit=atoi(optarg);
583                                 limit_ok = TRUE;
584                                 last_known_argument += 2;
585                                 break;
586                         case 'c':
587                                 NCPU = atoi(optarg);
588                                 last_known_argument += 2;
589                                 break;
590                         case 'v':
591                                 verbose = TRUE;
592                                 last_known_argument++;
593                                 break;
594                         case 'z':
595                                 lazy = TRUE;
596                                 last_known_argument++;
597                                 break;
598                         case 'h':
599                                 print_usage (stdout, 1);
600                                 last_known_argument++;
601                                 break;
602                         case '?':
603                                 print_usage (stderr, 1);
604                                 last_known_argument++;
605                                 break;
606                         case -1:
607                                 break;
608                         // default:
609                         //      abort();
610                 }
611         } while(next_option != -1);
612
613
614         if (last_known_argument + 1 < argc)
615         {
616            last_known_argument++;
617            pid_t forked_pid;
618            // try to launch remaining arguments
619            if (verbose)
620            {
621                int index = last_known_argument;
622                printf("Launching %s ", argv[index]);
623                for (index = last_known_argument + 1; index < argc; index++)
624                     printf("%s", argv[index]);
625                printf(" with limit %d\n", perclimit);
626            }
627            forked_pid = fork();
628            if (forked_pid == -1)  // error
629            {
630                printf("Failed to launch specified process.\n");
631                exit(1);
632            }
633            else if (forked_pid == 0)   // target child
634            {
635               execvp(argv[last_known_argument],
636                      &(argv[last_known_argument]) );
637               exit(2);
638            }
639            else     // parent who will now fork the throttler
640            {
641               pid_t limit_pid;
642               limit_pid = fork();
643               if (limit_pid == 0)   // child
644               {
645                  pid = forked_pid;    // the first child
646                  lazy = TRUE;
647                  pid_ok = TRUE;
648                  if (verbose)
649                    printf("Throttling process %d\n", (int) pid);
650               }
651               else    // parent
652                 exit(0);
653            }
654
655            /*
656            else if (forked_pid == 0)   // child
657            {
658                lazy = TRUE;
659                pid_ok = TRUE;
660                pid = getppid();
661                if (verbose)
662                   printf("Throttling process %d\n", (int) pid);
663            }
664            else // parent
665            {
666                execvp(argv[last_known_argument], 
667                       &(argv[last_known_argument]));
668                
669                // we should never return  
670                exit(2);
671            }
672            */
673            
674         }      // end of launching child process
675
676         if (!process_ok && !pid_ok) {
677                 fprintf(stderr,"Error: You must specify a target process\n");
678                 print_usage (stderr, 1);
679                 exit(1);
680         }
681         if ((exe!=NULL && path!=NULL) || (pid_ok && (exe!=NULL || path!=NULL))) {
682                 fprintf(stderr,"Error: You must specify exactly one target process\n");
683                 print_usage (stderr, 1);
684                 exit(1);
685         }
686         if (!limit_ok) {
687                 fprintf(stderr,"Error: You must specify a cpu limit\n");
688                 print_usage (stderr, 1);
689                 exit(1);
690         }
691         float limit=perclimit/100.0;
692         if ( (limit <= 0.00) || (limit > NCPU) )
693         {
694                 fprintf(stderr,"Error: limit must be in the range of 1 to %d00\n", NCPU);
695                 print_usage (stderr, 1);
696                 exit(1);
697         }
698
699         // check to see if we should fork
700         if (run_in_background)
701         {
702              pid_t process_id;
703              process_id = fork();
704              if (! process_id)
705                 exit(0);
706              else
707              {
708                 setsid();
709                 process_id = fork();
710                 if (process_id)
711                   exit(0);
712              }
713         }
714
715         //parameters are all ok!
716         signal(SIGINT,quit);
717         signal(SIGTERM,quit);
718
719         my_pid = getpid();
720         if (verbose)
721            printf("%d CPUs detected.\n", NCPU);
722
723         increase_priority();
724 /*
725 Instead of all this big block of code to detect and change
726 priority settings, let us just use the increase_priority()
727 function. It is a little more simple and takes a more
728 gradual approach, rather than "all or nothing".
729 -- Jesse
730
731         if (setpriority(PRIO_PROCESS, my_pid,-20)!=0) {
732         //if that failed, check if we have a limit 
733         // by how much we can raise the priority
734 #ifdef RLIMIT_NICE 
735 //check if non-root can even make changes 
736 // (ifdef because it's only available in linux >= 2.6.13)
737                 nice_lim=getpriority(PRIO_PROCESS, my_pid);
738                 getrlimit(RLIMIT_NICE, &maxlimit);
739
740 //if we can do better then current
741                 if( (20 - (signed)maxlimit.rlim_cur) < nice_lim &&  
742                     setpriority(PRIO_PROCESS, my_pid,
743                     20 - (signed)maxlimit.rlim_cur)==0 //and it actually works
744                   ) {
745
746                         //if we can do better, but not by much, warn about it
747                         if( (nice_lim - (20 - (signed)maxlimit.rlim_cur)) < 9) 
748                         {
749                         printf("Warning, can only increase priority by %d.\n",                                nice_lim - (20 - (signed)maxlimit.rlim_cur));
750                         }
751                         //our new limit
752                         nice_lim = 20 - (signed)maxlimit.rlim_cur; 
753
754                 } else 
755 // otherwise don't try to change priority. 
756 // The below will also run if it's not possible 
757 // for non-root to change priority
758 #endif
759                 {
760                         printf("Warning: cannot renice.\nTo work better you should run this program as root, or adjust RLIMIT_NICE.\nFor example in /etc/security/limits.conf add a line with: * - nice -10\n\n");
761                         nice_lim=INT_MAX;
762                 }
763         } else {
764                 nice_lim=-20;
765         }
766 */
767
768
769         //time quantum in microseconds. it's splitted in a working period and a sleeping one
770         int period=100000;
771         struct timespec twork,tsleep;   //working and sleeping intervals
772         memset(&twork,0,sizeof(struct timespec));
773         memset(&tsleep,0,sizeof(struct timespec));
774
775 wait_for_process:
776
777         //look for the target process..or wait for it
778         if (exe!=NULL)
779                 pid=getpidof(exe);
780         else if (path!=NULL)
781                 pid=getpidof(path);
782         else 
783                 waitforpid(pid);
784
785         
786         //process detected...let's play
787
788         //init compute_cpu_usage internal stuff
789         compute_cpu_usage(0,0,NULL);
790         //main loop counter
791         int i=0;
792
793         struct timespec startwork,endwork;
794         long workingtime=0;             //last working time in microseconds
795
796         if (verbose) print_caption();
797
798         float pcpu_avg=0;
799
800         //here we should already have high priority, for time precision
801         while(1) {
802
803                 //estimate how much the controlled process is using the cpu in its working interval
804                 struct cpu_usage cu;
805                 if (compute_cpu_usage(pid,workingtime,&cu)==-1) {
806                         fprintf(stderr,"Process %d dead!\n",pid);
807                         if (lazy) exit(2);
808                         //wait until our process appears
809                         goto wait_for_process;          
810                 }
811
812                 //cpu actual usage of process (range 0-1)
813                 float pcpu=cu.pcpu;
814                 //rate at which we are keeping active the process (range 0-1)
815                 float workingrate=cu.workingrate;
816
817                 //adjust work and sleep time slices
818                 if (pcpu>0) {
819                         twork.tv_nsec=min(period*limit*1000/pcpu*workingrate,period*1000);
820                 }
821                 else if (pcpu==0) {
822                         twork.tv_nsec=period*1000;
823                 }
824                 else if (pcpu==-1) {
825                         //not yet a valid idea of cpu usage
826                         pcpu=limit;
827                         workingrate=limit;
828                         twork.tv_nsec=min(period*limit*1000,period*1000);
829                 }
830                 tsleep.tv_nsec=period*1000-twork.tv_nsec;
831
832                 //update average usage
833                 pcpu_avg=(pcpu_avg*i+pcpu)/(i+1);
834
835                 if (verbose && i%10==0 && i>0) {
836                         printf("%0.2f%%\t%6ld us\t%6ld us\t%0.2f%%\n",pcpu*100,twork.tv_nsec/1000,tsleep.tv_nsec/1000,workingrate*100);
837                         if (i%200 == 0)
838                            print_caption();
839                 }
840
841                 // if (limit<1 && limit>0) {
842                 // printf("Comparing %f to %f\n", pcpu, limit);
843                 if (pcpu < limit)
844                 {
845                         // printf("Continue\n");
846                         //resume process
847                         if (kill(pid,SIGCONT)!=0) {
848                                 fprintf(stderr,"Process %d dead!\n",pid);
849                                 if (lazy) exit(2);
850                                 //wait until our process appears
851                                 goto wait_for_process;
852                         }
853                 }
854
855                 #ifdef __MACH__ 
856                 // OS X does not have clock_gettime, use clock_get_time
857                 clock_serv_t cclock;
858                 mach_timespec_t mts;
859                 host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
860                 clock_get_time(cclock, &mts);
861                 mach_port_deallocate(mach_task_self(), cclock);
862                 startwork.tv_sec = mts.tv_sec;
863                 startwork.tv_nsec = mts.tv_nsec;
864
865                 #else
866                 clock_gettime(CLOCK_REALTIME,&startwork);
867                 #endif
868
869                 nanosleep(&twork,NULL);         //now process is working
870                 #ifdef __MACH__ 
871                 // OS X does not have clock_gettime, use clock_get_time
872                 // clock_serv_t cclock;
873                 // mach_timespec_t mts;
874                 host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
875                 clock_get_time(cclock, &mts);
876                 mach_port_deallocate(mach_task_self(), cclock);
877                 endwork.tv_sec = mts.tv_sec;
878                 endwork.tv_nsec = mts.tv_nsec;
879
880                 #else
881                 clock_gettime(CLOCK_REALTIME,&endwork);
882                 #endif
883                 workingtime=timediff(&endwork,&startwork);
884
885                 // if (limit<1) {
886                 // printf("Checking %f vs %f\n", pcpu, limit);
887                 if (pcpu > limit)
888                 {
889                         // printf("Stop\n");
890                         //stop process, it has worked enough
891                         if (kill(pid,SIGSTOP)!=0) {
892                                 fprintf(stderr,"Process %d dead!\n",pid);
893                                 if (lazy) exit(2);
894                                 //wait until our process appears
895                                 goto wait_for_process;
896                         }
897                         nanosleep(&tsleep,NULL);        //now process is sleeping
898                 }
899                 i++;
900         }
901
902    return 0;
903 }