Package: cpulimit
Version: 1.1-9
Severity: important
Tags: patch

Cpulimit drops priority, then can't get back up.

True, it recommends to run as root, but that's not always an option.

This patch fixes it. If it can't raise the priority, at least it won't
lower it.

But: even better, this patch supports the new RLIMIT_NICE option of
linux >= 2.6.13.

This way the admin can configure users to be allowed to raise the
priority, up to a specific limit! If that option is enabled, cpulimit
will make use of it.

(Personally I wish linux had programmed it that if you ask for too high (low)
of a nice value, it would take you as far as possible, but it didn't, so
I had to include code to test how far you can go, and then do it.)

I added code to detect if RLIMIT_NICE is valid on the build system, to
support older installations as well. I don't know what would happen if
it's build on a linux >= 2.6.13, but run on a lower one. I suspect libc
would not allow it.

        -Ariel

-- System Information:
Debian Release: 4.0
  APT prefers testing
  APT policy: (500, 'testing')
Architecture: i386 (i686)
Shell:  /bin/sh linked to /bin/bash
Kernel: Linux 2.6.18
Locale: LANG=C, LC_CTYPE=C (charmap=ANSI_X3.4-1968)

Versions of packages cpulimit depends on:
ii  libc6                        2.3.6.ds1-8 GNU C Library: Shared libraries

cpulimit recommends no packages.

-- no debconf information
--- cpulimit-1.1.orig/cpulimit.c
+++ cpulimit-1.1/cpulimit.c
@@ -42,6 +42,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <string.h>
+#include <limits.h> //for INT_MAX, could also hardcode some large number
 
 //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 +60,8 @@
 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 +74,7 @@
 
 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) {
 		printf("Warning: cannot renice\n");
 	}
 
@@ -127,8 +130,8 @@
 done:
 	printf("Process %d detected\n",pid);
 	//now set high priority, if possible
-	if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) {
-		printf("Warning: cannot renice.\nTo work better you should run this program as root.\n");
+	if (nice_lim < INT_MAX && setpriority(PRIO_PROCESS,getpid(),nice_lim)!=0) {
+		printf("Warning: cannot renice.\n");
 	}
 	return 0;
 
@@ -143,7 +146,7 @@
 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) {
 		printf("Warning: cannot renice\n");
 	}
 
@@ -219,8 +222,8 @@
 done:
 	printf("Process %d detected\n",pid);
 	//now set high priority, if possible
-	if (setpriority(PRIO_PROCESS,getpid(),-20)!=0) {
-		printf("Warning: cannot renice.\nTo work better you should run this program as root.\n");
+	if (nice_lim < INT_MAX && setpriority(PRIO_PROCESS,getpid(),nice_lim)!=0) {
+		printf("Warning: cannot renice.\n");
 	}
 	return pid;
 
@@ -373,6 +376,7 @@
 	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);
@@ -437,6 +441,35 @@
 	signal(SIGINT,quit);
 	signal(SIGTERM,quit);
 
+	//now test high priority
+	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( (20 - (signed)maxlimit.rlim_cur) < nice_lim &&  //if we can do better then current
+		    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));
+			}
+
+			nice_lim = 20 - (signed)maxlimit.rlim_cur; //our new limit
+
+		} 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

Reply via email to