releasing version 1.4-1
[debian/cpulimit.git] / cpulimit.c
index bd2ea0a..ba41986 100644 (file)
@@ -1,13 +1,7 @@
 /**
- * Copyright (c) 2005, by:      Angelo Marletta <marlonx80@hotmail.com>
- *
- * This file may be used subject to the terms and conditions of the
- * GNU Library General Public License Version 2, or any later version
- * at your option, as published by the Free Software Foundation.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Library General Public License for more details.
+ * This program is licensed under the GNU General Public License,
+ * version 2. A copy of the license can be found in the accompanying
+ * LICENSE file.
  *
  **********************************************************************
  *
  * Author:  Angelo Marletta
  * Date:    26/06/2005
  * Version: 1.1
- * Last version at: http://marlon80.interfree.it/cpulimit/index.html
  *
- * Changelog:
- *  - Fixed a segmentation fault if controlled process exited in particular circumstances
- *  - Better CPU usage estimate
- *  - Fixed a <0 %CPU usage reporting in rare cases
- *  - Replaced MAX_PATH_SIZE with PATH_MAX already defined in <limits.h>
- *  - Command line arguments now available
- *  - Now is possible to specify target process by pid
+ * Modifications and updates by: Jesse Smith
+ * Date: May 4, 2011
+ * Version 1.2
+ *
  */
 
 
@@ -42,6 +32,8 @@
 #include <dirent.h>
 #include <errno.h>
 #include <string.h>
+#include <limits.h>    // for compatibility
+
 
 //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!)
@@ -59,6 +51,8 @@ char *program_name;
 int verbose=0;
 //lazy mode
 int lazy=0;
+// is higher priority nice possible?
+int nice_lim;
 
 //reverse byte search
 void *memrchr(const void *s, int c, size_t n);
@@ -71,7 +65,9 @@ inline long timediff(const struct timespec *ta,const struct timespec *tb) {
 
 int waitforpid(int pid) {
        //switch to low priority
-       if (setpriority(PRIO_PROCESS,getpid(),19)!=0) {
+       // if (setpriority(PRIO_PROCESS,getpid(),19)!=0) {
+        if ( (nice_lim < INT_MAX) && 
+             (setpriority(PRIO_PROCESS, getpid(), 19) != 0) ) {
                printf("Warning: cannot renice\n");
        }
 
@@ -95,6 +91,10 @@ int waitforpid(int pid) {
                                //pid detected
                                if (kill(pid,SIGSTOP)==0 &&  kill(pid,SIGCONT)==0) {
                                        //process is ok!
+                                        if (closedir(dip) == -1) {
+                                           perror("closedir");
+                                           return -1;
+                                        }
                                        goto done;
                                }
                                else {
@@ -127,7 +127,9 @@ int waitforpid(int pid) {
 done:
        printf("Process %d detected\n",pid);
        //now set high priority, if possible
-       if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) {
+       // if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) {
+        if ( (nice_lim < INT_MAX) &&
+             (setpriority(PRIO_PROCESS, getpid(), nice_lim) != 0) ) {
                printf("Warning: cannot renice.\nTo work better you should run this program as root.\n");
        }
        return 0;
@@ -143,7 +145,9 @@ done:
 int getpidof(const char *process) {
 
        //set low priority
-       if (setpriority(PRIO_PROCESS,getpid(),19)!=0) {
+       // if (setpriority(PRIO_PROCESS,getpid(),19)!=0) {
+        if ( (nice_lim < INT_MAX) &&
+             (setpriority(PRIO_PROCESS, getpid(), 19) != 0) ) {
                printf("Warning: cannot renice\n");
        }
 
@@ -185,6 +189,10 @@ int getpidof(const char *process) {
                                        if (found==1) {
                                                if (kill(pid,SIGSTOP)==0 &&  kill(pid,SIGCONT)==0) {
                                                        //process is ok!
+                                                        if (closedir(dip) == -1) {
+                                                          perror("closedir");
+                                                          return -1;
+                                                        }
                                                        goto done;
                                                }
                                                else {
@@ -219,7 +227,9 @@ int getpidof(const char *process) {
 done:
        printf("Process %d detected\n",pid);
        //now set high priority, if possible
-       if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) {
+       // if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) {
+        if ( (nice_lim < INT_MAX) &&
+             (setpriority(PRIO_PROCESS, getpid(), nice_lim) != 0) ) {
                printf("Warning: cannot renice.\nTo work better you should run this program as root.\n");
        }
        return pid;
@@ -238,22 +248,28 @@ void quit(int sig) {
 int getjiffies(int pid) {
        static char stat[20];
        static char buffer[1024];
+        char *p;
        sprintf(stat,"/proc/%d/stat",pid);
        FILE *f=fopen(stat,"r");
        if (f==NULL) return -1;
-       fgets(buffer,sizeof(buffer),f);
+       p = fgets(buffer,sizeof(buffer),f);
        fclose(f);
-       char *p=buffer;
-       p=memchr(p+1,')',sizeof(buffer)-(p-buffer));
-       int sp=12;
-       while (sp--)
+       // char *p=buffer;
+        if (p)
+        {
+         p=memchr(p+1,')',sizeof(buffer)-(p-buffer));
+         int sp=12;
+         while (sp--)
                p=memchr(p+1,' ',sizeof(buffer)-(p-buffer));
-       //user mode jiffies
-       int utime=atoi(p+1);
-       p=memchr(p+1,' ',sizeof(buffer)-(p-buffer));
-       //kernel mode jiffies
-       int ktime=atoi(p+1);
-       return utime+ktime;
+         //user mode jiffies
+         int utime=atoi(p+1);
+         p=memchr(p+1,' ',sizeof(buffer)-(p-buffer));
+         //kernel mode jiffies
+         int ktime=atoi(p+1);
+         return utime+ktime;
+        }
+        // could not read info
+        return -1;
 }
 
 //process instant photo
@@ -339,6 +355,7 @@ void print_usage(FILE *stream,int exit_code) {
        fprintf(stream, "      -e, --exe=FILE     name of the executable program file\n");
        fprintf(stream, "      -P, --path=PATH    absolute path name of the executable program file\n");
        fprintf(stream, "   OPTIONS\n");
+        fprintf(stream, "      -b  --background   run in background\n");
        fprintf(stream, "      -l, --limit=N      percentage of cpu allowed from 0 to 100 (mandatory)\n");
        fprintf(stream, "      -v, --verbose      show control statistics\n");
        fprintf(stream, "      -z, --lazy         exit if there is no suitable target process, or if it dies\n");
@@ -351,19 +368,21 @@ 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;
        //parse arguments
        int next_option;
        /* A string listing valid short options letters. */
-       const char* short_options="p:e:P:l:vzh";
+       const char* short_options="p:e:P:l:bvzh";
        /* An array describing valid long options. */
        const struct option long_options[] = {
-               { "pid", 0, NULL, 'p' },
-               { "exe", 1, NULL, 'e' },
-               { "path", 0, NULL, 'P' },
-               { "limit", 0, NULL, 'l' },
-               { "verbose", 0, NULL, 'v' },
-               { "lazy", 0, NULL, 'z' },
-               { "help", 0, NULL, 'h' },
+               { "pid", required_argument, NULL, 'p' },
+               { "exe", required_argument, NULL, 'e' },
+               { "path", required_argument, NULL, 'P' },
+               { "limit", required_argument, NULL, 'l' },
+                { "background", no_argument, NULL, 'b' },
+               { "verbose", no_argument, NULL, 'v' },
+               { "lazy", no_argument, NULL, 'z' },
+               { "help", no_argument, NULL, 'h' },
                { NULL, 0, NULL, 0 }
        };
        //argument variables
@@ -373,13 +392,18 @@ int main(int argc, char **argv) {
        int pid_ok=0;
        int process_ok=0;
        int limit_ok=0;
+        struct rlimit maxlimit;
 
        do {
                next_option = getopt_long (argc, argv, short_options,long_options, NULL);
                switch(next_option) {
+                        case 'b':
+                                run_in_background = 1;
+                                break;
                        case 'p':
                                pid=atoi(optarg);
                                pid_ok=1;
+                                lazy = 1;
                                break;
                        case 'e':
                                exe=optarg;
@@ -428,15 +452,73 @@ int main(int argc, char **argv) {
                exit(1);
        }
        float limit=perclimit/100.0;
-       if (limit<0 || limit >1) {
-               fprintf(stderr,"Error: limit must be in the range 0-100\n");
+       if (limit <= 0.00) // || limit >1) {
+        {
+               fprintf(stderr,"Error: limit must be greater than 0\n");
                print_usage (stderr, 1);
                exit(1);
        }
+
+        // check to see if we should fork
+        if (run_in_background)
+        {
+             pid_t process_id;
+             process_id = fork();
+             if (! process_id)
+                exit(0);
+             else
+             {
+                setsid();
+                process_id = fork();
+                if (process_id)
+                  exit(0);
+             }
+        }
+
        //parameters are all ok!
        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
+
+
+
        //time quantum in microseconds. it's splitted in a working period and a sleeping one
        int period=100000;
        struct timespec twork,tsleep;   //working and sleeping intervals