debian/copyright: update GPL-2 stanza.
[debian/cpulimit.git] / cpulimit.c
index 709bda17755fdd91868349757c56396e5fa8b41e..e248eec00c4b33daba6e48f690f4962886354668 100644 (file)
  *
  * Modifications and updates by: Jesse Smith
  * Date: May 4, 2011
- * Version 1.2
+ * Version 1.2 
+ * Date: Jan 29, 2013
+ * Version 1.2 and newer
  *
+ * Modifications and updates by: Hasnain Lakhani
+ * Date: Mar 26, 2014
+ * Version 2.1
  */
 
 
@@ -24,6 +29,8 @@
 #include <stdlib.h>
 #include <time.h>
 #include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <signal.h>
 #include <string.h>
 #include <limits.h>    // for compatibility
 
+#ifdef __APPLE__
+#include <mach/clock.h>
+#include <mach/mach.h>
+#endif
+
+
+#ifdef FREEBSD
+#include <fcntl.h>
+#include <kvm.h>
+#include <paths.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#endif
+
 
 //kernel time resolution (inverse of one jiffy interval) in Hertz
 //i don't know how to detect it, then define to the default (not very clean!)
 #define min(a,b) (a<b?a:b)
 #define max(a,b) (a>b?a:b)
 
+// For platforms without PATH_MAX
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+#define BEST_PRIORITY -10
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef VERSION
+#define VERSION 2.3
+#endif
+
 //pid of the controlled process
-int pid=0;
+pid_t pid = 0;
+pid_t my_pid;     // this process's PID
+
 //executable file name
 char *program_name;
 //verbose mode
-int verbose=0;
+int verbose = FALSE;
 //lazy mode
-int lazy=0;
+int lazy = FALSE;
 // is higher priority nice possible?
 int nice_lim;
 
+// number of CPUs we detected
+int NCPU;
+
+// quiet mode
+int quiet = FALSE;
+
+// What signal should we send to the watched process
+// when cpulimit exits?
+int send_signal = SIGCONT;
+
 //reverse byte search
-void *memrchr(const void *s, int c, size_t n);
+// void *memrchr(const void *s, int c, size_t n);
+
+#define MAX_SIGNAL 7
+const char *SIGNAL_NAME[MAX_SIGNAL] = { "SIGHUP", "SIGINT", "SIGQUIT", 
+                                 "SIGKILL", "SIGTERM", "SIGSTOP", "SIGCONT" };
+const int SIGNAL_VALUE[MAX_SIGNAL] = { SIGHUP, SIGINT, SIGQUIT,
+                                  SIGKILL, SIGTERM, SIGSTOP, SIGCONT };
+
 
 //return ta-tb in microseconds (no overflow checks!)
 inline long timediff(const struct timespec *ta,const struct timespec *tb) {
@@ -63,14 +122,31 @@ inline long timediff(const struct timespec *ta,const struct timespec *tb) {
     return us;
 }
 
+
+
+int Check_Us(pid_t target_pid)
+{
+   pid_t this_pid;
+
+   this_pid = getpid();
+   if (this_pid == target_pid)
+   {
+      fprintf(stderr, "We cannot throttle ourselves.\n");
+      exit(7);
+   }
+   return TRUE;
+}
+
+
 int waitforpid(int pid) {
        //switch to low priority
        // if (setpriority(PRIO_PROCESS,getpid(),19)!=0) {
+        /*
         if ( (nice_lim < INT_MAX) && 
-             (setpriority(PRIO_PROCESS, getpid(), 19) != 0) ) {
+             (setpriority(PRIO_PROCESS, my_pid, 19) != 0) ) {
                printf("Warning: cannot renice\n");
        }
-
+        */
        int i=0;
 
        while(1) {
@@ -89,6 +165,7 @@ int waitforpid(int pid) {
                        //get pid
                        if (pid==atoi(dit->d_name)) {
                                //pid detected
+                                Check_Us(pid);
                                if (kill(pid,SIGSTOP)==0 &&  kill(pid,SIGCONT)==0) {
                                        //process is ok!
                                         if (closedir(dip) == -1) {
@@ -116,7 +193,7 @@ int waitforpid(int pid) {
                                exit(2);
                        }
                        else {
-                               printf("Warning: no target process found. Waiting for it...\n");
+                               fprintf(stderr, "Warning: no target process found. Waiting for it...\n");
                        }
                }
 
@@ -125,13 +202,16 @@ int waitforpid(int pid) {
        }
 
 done:
-       printf("Process %d detected\n",pid);
+    if (!quiet)
+       printf("Process %d detected\n",pid);
        //now set high priority, if possible
        // if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) {
+        /*
         if ( (nice_lim < INT_MAX) &&
-             (setpriority(PRIO_PROCESS, getpid(), nice_lim) != 0) ) {
+             (setpriority(PRIO_PROCESS, my_pid, nice_lim) != 0) ) {
                printf("Warning: cannot renice.\nTo work better you should run this program as root.\n");
        }
+        */
        return 0;
 
 }
@@ -146,11 +226,12 @@ int getpidof(const char *process) {
 
        //set low priority
        // if (setpriority(PRIO_PROCESS,getpid(),19)!=0) {
+        /*
         if ( (nice_lim < INT_MAX) &&
-             (setpriority(PRIO_PROCESS, getpid(), 19) != 0) ) {
+             (setpriority(PRIO_PROCESS, my_pid, 19) != 0) ) {
                printf("Warning: cannot renice\n");
        }
-
+        */
        char exelink[20];
        char exepath[PATH_MAX+1];
        int pid=0;
@@ -187,6 +268,7 @@ int getpidof(const char *process) {
                                                }
                                        }
                                        if (found==1) {
+                                        Check_Us(pid);
                                                if (kill(pid,SIGSTOP)==0 &&  kill(pid,SIGCONT)==0) {
                                                        //process is ok!
                                                         if (closedir(dip) == -1) {
@@ -216,7 +298,7 @@ int getpidof(const char *process) {
                                exit(2);
                        }
                        else {
-                               printf("Warning: no target process found. Waiting for it...\n");
+                               fprintf(stderr, "Warning: no target process found. Waiting for it...\n");
                        }
                }
 
@@ -225,26 +307,72 @@ int getpidof(const char *process) {
        }
 
 done:
-       printf("Process %d detected\n",pid);
+    if (!quiet)
+       printf("Process %d detected\n",pid);
        //now set high priority, if possible
        // if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) {
+        /*
         if ( (nice_lim < INT_MAX) &&
-             (setpriority(PRIO_PROCESS, getpid(), nice_lim) != 0) ) {
+             (setpriority(PRIO_PROCESS, my_pid, nice_lim) != 0) ) {
                printf("Warning: cannot renice.\nTo work better you should run this program as root.\n");
        }
+        */
        return pid;
 
 }
 
 //SIGINT and SIGTERM signal handler
 void quit(int sig) {
-       //let the process continue if it's stopped
-       kill(pid,SIGCONT);
+       //let the process continue if we are stopped
+       kill(pid, send_signal);
        printf("Exiting...\n");
        exit(0);
 }
 
+// Handle a child process quitting
+void Child_Done(int sig)
+{
+   pid_t caught_child;
+   caught_child = waitpid(-1, NULL, WNOHANG);
+   printf("Caught child process: %d\n", (int) caught_child);
+   printf("%d\n", errno);
+   if (caught_child == pid)
+   {
+      printf("Child process is finished, exiting...\n");
+      exit(0);
+   }
+}
+
+
+#ifdef FREEBSD
 //get jiffies count from /proc filesystem
+int getjiffies(int pid)
+{
+   kvm_t *my_kernel = NULL;
+   struct kinfo_proc *process_data = NULL;
+   int processes;
+   int my_jiffies = -1;
+
+   my_kernel = kvm_open(0, 0, 0, O_RDONLY, "kvm_open");
+   if (! my_kernel)
+   {
+      fprintf(stderr, "Error opening kernel vm. You should be running as root.\n");
+      return -1;
+   }
+
+   process_data = kvm_getprocs(my_kernel, KERN_PROC_PID, pid, &processes);
+   if ( (process_data) && (processes >= 1) )
+       my_jiffies = process_data->ki_runtime;
+   
+   kvm_close(my_kernel);
+   if (my_jiffies >= 0)
+     my_jiffies /= 1000;
+   return my_jiffies;
+}
+
+#endif
+
+#ifdef LINUX
 int getjiffies(int pid) {
        static char stat[20];
        static char buffer[1024];
@@ -271,6 +399,8 @@ int getjiffies(int pid) {
         // could not read info
         return -1;
 }
+#endif
+
 
 //process instant photo
 struct process_screenshot {
@@ -311,9 +441,22 @@ int compute_cpu_usage(int pid,int last_working_quantum,struct cpu_usage *pusage)
        int j=getjiffies(pid);
        if (j>=0) ps[front].jiffies=j;
        else return -1; //error: pid does not exist
+
+        #ifdef __APPLE__
+        // OS X does not have clock_gettime, use clock_get_time
+        clock_serv_t cclock;
+        mach_timespec_t mts;
+        host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+        clock_get_time(cclock, &mts);
+        mach_port_deallocate(mach_task_self(), cclock);
+        ps[front].when.tv_sec = mts.tv_sec;
+        ps[front].when.tv_nsec = mts.tv_nsec;
+
+        #else
+        // Linux and BSD can use real time
        clock_gettime(CLOCK_REALTIME,&(ps[front].when));
        ps[front].cputime=last_working_quantum;
-
+        #endif
        //buffer actual size is: (front-tail+MEM_ORDER)%MEM_ORDER+1
        int size=(front-tail+MEM_ORDER)%MEM_ORDER+1;
 
@@ -348,8 +491,32 @@ void print_caption() {
        printf("\n%%CPU\twork quantum\tsleep quantum\tactive rate\n");
 }
 
+
+void increase_priority()
+{
+        //find the best available nice value
+        int old_priority = getpriority(PRIO_PROCESS, 0);
+        int priority = old_priority;
+        while ( (setpriority(PRIO_PROCESS, 0, priority-1) == 0) &&
+                (priority > BEST_PRIORITY) )
+        {
+                priority--;
+        }
+        if (priority != old_priority) {
+                if (verbose) printf("Priority changed to %d\n", priority);
+        }
+        else {
+                if (verbose) printf("Warning: Cannot change priority. Run as root or renice for best results.\n");
+        }
+
+
+}
+
+
+
 void print_usage(FILE *stream,int exit_code) {
-       fprintf(stream, "Usage: %s TARGET [OPTIONS...]\n",program_name);
+        fprintf(stream, "CPUlimit version %1.1f\n", VERSION);
+       fprintf(stream, "Usage: %s TARGET [OPTIONS...] [-- PROGRAM]\n",program_name);
        fprintf(stream, "   TARGET must be exactly one of these:\n");
        fprintf(stream, "      -p, --pid=N        pid of the process\n");
        fprintf(stream, "      -e, --exe=FILE     name of the executable program file\n");
@@ -359,26 +526,81 @@ void print_usage(FILE *stream,int exit_code) {
         fprintf(stream, "                         executable program file\n");
        fprintf(stream, "   OPTIONS\n");
         fprintf(stream, "      -b  --background   run in background\n");
+        fprintf(stream, "      -f  --foreground   launch target process in foreground and wait for it to exit\n");
+        fprintf(stream, "      -c  --cpu=N        override the detection of CPUs on the machine.\n");
        fprintf(stream, "      -l, --limit=N      percentage of cpu allowed from 1 up.\n");
-        fprintf(stream, "                         Usually 1 - 100, but can be higher\n");
+        fprintf(stream, "                         Usually 1 - %d00, but can be higher\n", NCPU);
         fprintf(stream, "                         on multi-core CPUs (mandatory)\n");
+        fprintf(stream, "      -q, --quiet        run in quiet mode (only print errors).\n");
+        fprintf(stream, "      -k, --kill         kill processes going over their limit\n");
+        fprintf(stream, "                         instead of just throttling them.\n");
+        fprintf(stream, "      -r, --restore      Restore processes after they have\n");
+        fprintf(stream, "                         been killed. Works with the -k flag.\n");
+        fprintf(stream, "      -s, --signal=SIG   Send this signal to the watched process when cpulimit exits.\n");
+        fprintf(stream, "                         Signal should be specificed as a number or \n");
+        fprintf(stream, "                         SIGTERM, SIGCONT, SIGSTOP, etc. SIGCONT is the default.\n");
        fprintf(stream, "      -v, --verbose      show control statistics\n");
        fprintf(stream, "      -z, --lazy         exit if there is no suitable target process,\n");
         fprintf(stream, "                         or if it dies\n");
+        fprintf(stream, "          --             This is the final CPUlimit option. All following\n");
+        fprintf(stream, "                         options are for another program we will launch.\n");
        fprintf(stream, "      -h, --help         display this help and exit\n");
        exit(exit_code);
 }
 
+
+
+// Get the number of CPU cores on this machine.
+int get_ncpu()
+{
+        int ncpu = 1;
+#ifdef _SC_NPROCESSORS_ONLN
+        ncpu = sysconf(_SC_NPROCESSORS_ONLN);
+#endif
+        return ncpu;
+}
+
+
+// This function attempts to figure out what signal we should send
+// target processes based on a command line paramter. First we check
+// for text such as SIGINT, SIGCONT, SIGSTOP, etc. If no match is found
+// then we assume the value given is a number and use that.
+int Translate_Signal(char *my_signal)
+{
+    int signal_value;
+    int index = 0, found = FALSE;
+    // first check to see if we were passed a string
+    while ( (index < MAX_SIGNAL) && (! found) )
+    {
+        if (! strcmp(my_signal, SIGNAL_NAME[index]) )
+        {
+            found = TRUE;
+            signal_value = SIGNAL_VALUE[index];
+        }
+        else
+           index++;
+    }
+
+    // no value found, try a number
+    if (! found)
+       signal_value = atoi(my_signal);
+
+    return signal_value;
+}
+
+
+
 int main(int argc, char **argv) {
 
        //get program name
-       char *p=(char*)memrchr(argv[0],(unsigned int)'/',strlen(argv[0]));
-       program_name = p==NULL?argv[0]:(p+1);
-        int run_in_background = 0;
+       // char *p=(char*)memrchr(argv[0],(unsigned int)'/',strlen(argv[0]));
+       // program_name = p==NULL?argv[0]:(p+1);
+        program_name = argv[0];
+        int run_in_background = FALSE;
        //parse arguments
        int next_option;
        /* A string listing valid short options letters. */
-       const char* short_options="p:e:P:l:bvzh";
+       const char* short_options="p:e:P:l:c:s:bfqkrvzh";
        /* An array describing valid long options. */
        const struct option long_options[] = {
                { "pid", required_argument, NULL, 'p' },
@@ -386,62 +608,189 @@ int main(int argc, char **argv) {
                { "path", required_argument, NULL, 'P' },
                { "limit", required_argument, NULL, 'l' },
                 { "background", no_argument, NULL, 'b' },
+                { "foreground", no_argument, NULL, 'f' },
+                { "quiet", no_argument, NULL, 'q' },
                { "verbose", no_argument, NULL, 'v' },
                { "lazy", no_argument, NULL, 'z' },
                { "help", no_argument, NULL, 'h' },
+                { "cpu", required_argument, NULL, 'c'},
+                { "signal", required_argument, NULL, 's'},
                { NULL, 0, NULL, 0 }
        };
        //argument variables
        const char *exe=NULL;
-       const char *path=NULL;
+        const char *path=NULL;
        int perclimit=0;
-       int pid_ok=0;
-       int process_ok=0;
-       int limit_ok=0;
-        struct rlimit maxlimit;
-
+       int pid_ok = FALSE;
+       int process_ok = FALSE;
+       int limit_ok = FALSE;
+        int last_known_argument = 0;
+        int kill_process = FALSE;   // kill process instead of stopping it
+        int restore_process = FALSE;  // restore killed process
+        int run_child_in_background = TRUE;  // run cpulimit in background when 
+                                             //  we launch new process
+        // struct rlimit maxlimit;
+
+        NCPU = get_ncpu();
+
+        opterr = 0;      // avoid unwanted error messages for unknown parameters
        do {
                next_option = getopt_long (argc, argv, short_options,long_options, NULL);
                switch(next_option) {
                         case 'b':
-                                run_in_background = 1;
+                                run_in_background = TRUE;
+                                last_known_argument++;
+                                break;
+                        case 'f':
+                                run_child_in_background = FALSE;
+                                run_in_background = FALSE;
+                                last_known_argument++;
                                 break;
                        case 'p':
                                pid=atoi(optarg);
-                               pid_ok=1;
-                                lazy = 1;
+                                if (pid)   // valid PID
+                                {
+                                 pid_ok = TRUE;
+                                  lazy = TRUE;
+                                }
+                                last_known_argument += 2;
                                break;
                        case 'e':
                                exe=optarg;
-                               process_ok=1;
+                               process_ok = TRUE;
+                                last_known_argument += 2;
                                break;
                        case 'P':
                                path=optarg;
-                               process_ok=1;
+                               process_ok = TRUE;
+                                last_known_argument += 2;
                                break;
                        case 'l':
                                perclimit=atoi(optarg);
-                               limit_ok=1;
+                               limit_ok = TRUE;
+                                last_known_argument += 2;
                                break;
+                        case 'c':
+                                NCPU = atoi(optarg);
+                                last_known_argument += 2;
+                                break;
+                        case 's':
+                                send_signal = Translate_Signal(optarg);
+                                if ( (send_signal < 1) || (send_signal > 35) )
+                                {
+                                    fprintf(stderr, "Specified exit signal is not recognized or not within bounds (1-35). Using SIGCONT.\n");
+                                    send_signal = SIGCONT;
+                                }
+                                last_known_argument += 2;
+                        case 'k':
+                                kill_process = TRUE;
+                                last_known_argument++;
+                                break;
+                        case 'r':
+                                restore_process = TRUE;
+                                last_known_argument++;
+                                break;
+
                        case 'v':
-                               verbose=1;
+                               verbose = TRUE;
+                                last_known_argument++;
                                break;
+                        case 'q':
+                                quiet = TRUE;
+                                last_known_argument++;
+                                break;
                        case 'z':
-                               lazy=1;
+                               lazy = TRUE;
+                                last_known_argument++;
                                break;
                        case 'h':
                                print_usage (stdout, 1);
+                                last_known_argument++;
                                break;
+                        case 'o':
+                                last_known_argument++;
+                                next_option = -1;
+                                break;
                        case '?':
                                print_usage (stderr, 1);
+                                last_known_argument++;
                                break;
                        case -1:
                                break;
-                       default:
-                               abort();
+                       // default:
+                       //      abort();
                }
        } while(next_option != -1);
 
+        signal(SIGCHLD, Child_Done);
+
+        // try to launch a program passed on the command line
+        // But only if we do not already have a PID to watch
+        if ( (last_known_argument + 1 < argc) && (pid_ok == FALSE) )
+        {
+           last_known_argument++;
+           // if we stopped on "--" jump to the next parameter
+           if ( (last_known_argument + 1 < argc) && (! strcmp(argv[last_known_argument], "--") ) )
+               last_known_argument++;
+           pid_t forked_pid;
+           // try to launch remaining arguments
+           if (verbose)
+           {
+               int index = last_known_argument;
+               printf("Launching %s", argv[index]);
+               for (index = last_known_argument + 1; index < argc; index++)
+                    printf(" %s", argv[index]);
+               printf(" with limit %d\n", perclimit);
+           }
+           forked_pid = fork();
+           if (forked_pid == -1)  // error
+           {
+               printf("Failed to launch specified process.\n");
+               exit(1);
+           }
+           else if (forked_pid == 0)   // target child
+           {
+              execvp(argv[last_known_argument],
+                     &(argv[last_known_argument]) );
+              exit(2);
+           }
+           else     // parent who will now fork the throttler
+           {
+              pid_t limit_pid;
+              // if we are planning to kill a process, give it
+              // a running head start to avoid death at start-up
+              if (kill_process)
+                 sleep(5);
+    
+              /* The following block assumes we want to run cpulimit in the
+                 background. This is the default behaviour.
+              */ 
+              if (run_child_in_background)
+              {
+                 limit_pid = fork();
+                 if (limit_pid == 0)   // child cpulimit process running in background
+                 {
+                    pid = forked_pid;    // the first child, target process
+                    lazy = TRUE;
+                    pid_ok = TRUE;
+                    if (verbose)
+                      printf("Throttling process %d\n", (int) pid);
+                 }
+                 else    // parent cpulimit process which can quit
+                   exit(0);
+              }  // end of running in background
+              else
+              {
+                  pid = forked_pid;
+                  lazy = TRUE;
+                  pid_ok = TRUE;
+                  run_in_background = FALSE;
+              }  // end of running in foreground
+
+           }  // end of parent that launched target
+
+        }      // end of launching child process
+
        if (!process_ok && !pid_ok) {
                fprintf(stderr,"Error: You must specify a target process\n");
                print_usage (stderr, 1);
@@ -458,9 +807,9 @@ int main(int argc, char **argv) {
                exit(1);
        }
        float limit=perclimit/100.0;
-       if (limit <= 0.00) // || limit >1) {
+       if ( (limit <= 0.00) || (limit > NCPU) )
         {
-               fprintf(stderr,"Error: limit must be greater than 0\n");
+               fprintf(stderr,"Error: limit must be in the range of 1 to %d00\n", NCPU);
                print_usage (stderr, 1);
                exit(1);
        }
@@ -485,45 +834,11 @@ int main(int argc, char **argv) {
        signal(SIGINT,quit);
        signal(SIGTERM,quit);
 
-       if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) {
-       //if that failed, check if we have a limit 
-        // by how much we can raise the priority
-#ifdef RLIMIT_NICE 
-//check if non-root can even make changes 
-// (ifdef because it's only available in linux >= 2.6.13)
-               nice_lim=getpriority(PRIO_PROCESS,getpid());
-               getrlimit(RLIMIT_NICE, &maxlimit);
-
-//if we can do better then current
-               if( (20 - (signed)maxlimit.rlim_cur) < nice_lim &&  
-                   setpriority(PRIO_PROCESS,getpid(),
-                    20 - (signed)maxlimit.rlim_cur)==0 //and it actually works
-                 ) {
-
-                       //if we can do better, but not by much, warn about it
-                       if( (nice_lim - (20 - (signed)maxlimit.rlim_cur)) < 9) 
-                        {
-                       printf("Warning, can only increase priority by %d.\n",                                nice_lim - (20 - (signed)maxlimit.rlim_cur));
-                       }
-                        //our new limit
-                       nice_lim = 20 - (signed)maxlimit.rlim_cur; 
-
-               } else 
-// otherwise don't try to change priority. 
-// The below will also run if it's not possible 
-// for non-root to change priority
-#endif
-               {
-                       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");
-                       nice_lim=INT_MAX;
-               }
-       } else {
-               nice_lim=-20;
-       }
-       //don't bother putting setpriority back down, 
-        // since getpidof and waitforpid twiddle it anyway
-
+        my_pid = getpid();
+        if (verbose)
+           printf("%d CPUs detected.\n", NCPU);
 
+        increase_priority();
 
        //time quantum in microseconds. it's splitted in a working period and a sleeping one
        int period=100000;
@@ -534,13 +849,14 @@ int main(int argc, char **argv) {
 wait_for_process:
 
        //look for the target process..or wait for it
-       if (exe!=NULL)
+       if (exe != NULL)
                pid=getpidof(exe);
-       else if (path!=NULL)
+       else if (path != NULL)
                pid=getpidof(path);
-       else {
+       else 
                waitforpid(pid);
-       }
+
+       
        //process detected...let's play
 
        //init compute_cpu_usage internal stuff
@@ -561,7 +877,8 @@ wait_for_process:
                //estimate how much the controlled process is using the cpu in its working interval
                struct cpu_usage cu;
                if (compute_cpu_usage(pid,workingtime,&cu)==-1) {
-                       fprintf(stderr,"Process %d dead!\n",pid);
+            if (!quiet)
+                       fprintf(stderr,"Process %d dead!\n",pid);
                        if (lazy) exit(2);
                        //wait until our process appears
                        goto wait_for_process;          
@@ -592,34 +909,114 @@ wait_for_process:
 
                if (verbose && i%10==0 && i>0) {
                        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);
+                        if (i%200 == 0)
+                           print_caption();
                }
 
-               if (limit<1 && limit>0) {
+               // if (limit<1 && limit>0) {
+                // printf("Comparing %f to %f\n", pcpu, limit);
+                if (pcpu < limit)
+                {
+                        // printf("Continue\n");
                        //resume process
                        if (kill(pid,SIGCONT)!=0) {
-                               fprintf(stderr,"Process %d dead!\n",pid);
-                               if (lazy) exit(2);
+                             if (!quiet)
+                               fprintf(stderr,"Process %d dead!\n",pid);
+                            if (lazy) exit(2);
                                //wait until our process appears
                                goto wait_for_process;
                        }
                }
 
+                #ifdef __APPLE_
+                // OS X does not have clock_gettime, use clock_get_time
+                clock_serv_t cclock;
+                mach_timespec_t mts;
+                host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+                clock_get_time(cclock, &mts);
+                mach_port_deallocate(mach_task_self(), cclock);
+                startwork.tv_sec = mts.tv_sec;
+                startwork.tv_nsec = mts.tv_nsec;
+
+                #else
                clock_gettime(CLOCK_REALTIME,&startwork);
-               nanosleep(&twork,NULL);         //now process is working        
+                #endif
+
+               nanosleep(&twork,NULL);         //now process is working
+                #ifdef __APPLE__
+                // OS X does not have clock_gettime, use clock_get_time
+                // clock_serv_t cclock;
+                // mach_timespec_t mts;
+                host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+                clock_get_time(cclock, &mts);
+                mach_port_deallocate(mach_task_self(), cclock);
+                endwork.tv_sec = mts.tv_sec;
+                endwork.tv_nsec = mts.tv_nsec;
+
+                #else
                clock_gettime(CLOCK_REALTIME,&endwork);
+                #endif
                workingtime=timediff(&endwork,&startwork);
 
-               if (limit<1) {
+               // if (limit<1) {
+                // printf("Checking %f vs %f\n", pcpu, limit);
+                if (pcpu > limit)
+                {
+                     // When over our limit we may run into
+                     // situations where we want to kill
+                     // the offending process, then restart it
+                     if (kill_process)
+                     {
+                         kill(pid, SIGKILL);
+                         if (!quiet)
+                             fprintf(stderr, "Process %d killed.\n", pid);
+                         if ( (lazy) && (! restore_process) ) 
+                              exit(2);
+                         // restart killed process
+                         if (restore_process)
+                         {
+                             pid_t new_process;
+                             new_process = fork();
+                             if (new_process == -1)
+                             {
+                              fprintf(stderr, "Failed to restore killed process.\n");
+                             }
+                             else if (new_process == 0)
+                             {
+                                // child which becomes new process
+                                if (verbose)
+                                   printf("Relaunching %s\n",
+                                          argv[last_known_argument]);
+                                execvp(argv[last_known_argument],
+                                       &(argv[last_known_argument]) ); 
+                             }
+                             else // parent
+                             {
+                                // we need to track new process
+                                pid = new_process;
+                                // avoid killing child process
+                                sleep(5);
+                             }
+                         }
+                     }
+                     // do not kill process, just throttle it
+                     else
+                     {
+
+                        // printf("Stop\n");
                        //stop process, it has worked enough
                        if (kill(pid,SIGSTOP)!=0) {
-                               fprintf(stderr,"Process %d dead!\n",pid);
-                               if (lazy) exit(2);
+                            if (!quiet)
+                               fprintf(stderr,"Process %d dead!\n", pid);
+                           if (lazy) exit(2);
                                //wait until our process appears
                                goto wait_for_process;
                        }
                        nanosleep(&tsleep,NULL);        //now process is sleeping
-               }
+                      }   // end of throttle process
+               }         // end of process using too much CPU
                i++;
        }
 
+   return 0;
 }