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