* Modifications and updates by: Jesse Smith
* Date: May 4, 2011
* Version 1.2
- * Date: Jam 29, 2013
+ * Date: Jan 29, 2013
* Version 1.2 and newer
*
+ * Modifications and updates by: Hasnain Lakhani
+ * Date: Mar 26, 2014
+ * Version 2.1
*/
#endif
#ifndef VERSION
-#define VERSION 1.9
+#define VERSION 2.3
#endif
//pid of the controlled process
// 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);
+#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!)
this_pid = getpid();
if (this_pid == target_pid)
{
- printf("We cannot throttle ourselves.\n");
+ fprintf(stderr, "We cannot throttle ourselves.\n");
exit(7);
}
return TRUE;
exit(2);
}
else {
- printf("Warning: no target process found. Waiting for it...\n");
+ fprintf(stderr, "Warning: no target process found. Waiting for it...\n");
}
}
}
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) {
/*
exit(2);
}
else {
- printf("Warning: no target process found. Waiting for it...\n");
+ fprintf(stderr, "Warning: no target process found. Waiting for it...\n");
}
}
}
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) {
/*
//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
my_kernel = kvm_open(0, 0, 0, O_RDONLY, "kvm_open");
if (! my_kernel)
{
- printf("Error opening kernel vm. You should be running as root.\n");
+ fprintf(stderr, "Error opening kernel vm. You should be running as root.\n");
return -1;
}
void print_usage(FILE *stream,int exit_code) {
fprintf(stream, "CPUlimit version %1.1f\n", VERSION);
- fprintf(stream, "Usage: %s TARGET [OPTIONS...] [PROGRAM]\n",program_name);
+ 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");
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 - %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);
}
}
+// 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) {
//parse arguments
int next_option;
/* A string listing valid short options letters. */
- const char* short_options="p:e:P:l:c:bkrvzh";
+ 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' },
{ "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 = FALSE;
int process_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) {
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 = TRUE;
- lazy = TRUE;
+ if (pid) // valid PID
+ {
+ pid_ok = TRUE;
+ lazy = TRUE;
+ }
last_known_argument += 2;
break;
case 'e':
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++;
verbose = TRUE;
last_known_argument++;
break;
+ case 'q':
+ quiet = TRUE;
+ last_known_argument++;
+ break;
case 'z':
lazy = TRUE;
last_known_argument++;
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++;
}
} while(next_option != -1);
+ signal(SIGCHLD, Child_Done);
- if (last_known_argument + 1 < argc)
+ // 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]);
+ printf("Launching %s", argv[index]);
for (index = last_known_argument + 1; index < argc; index++)
- printf("%s", argv[index]);
+ printf(" %s", argv[index]);
printf(" with limit %d\n", perclimit);
}
forked_pid = fork();
// a running head start to avoid death at start-up
if (kill_process)
sleep(5);
-
- limit_pid = fork();
- if (limit_pid == 0) // child
+
+ /* The following block assumes we want to run cpulimit in the
+ background. This is the default behaviour.
+ */
+ if (run_child_in_background)
{
- pid = forked_pid; // the first child
- lazy = TRUE;
- pid_ok = TRUE;
- if (verbose)
- printf("Throttling process %d\n", (int) pid);
- }
- else // parent
- exit(0);
- }
+ 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
- /*
- else if (forked_pid == 0) // child
- {
- lazy = TRUE;
- pid_ok = TRUE;
- pid = getppid();
- if (verbose)
- printf("Throttling process %d\n", (int) pid);
- }
- else // parent
- {
- execvp(argv[last_known_argument],
- &(argv[last_known_argument]));
-
- // we should never return
- exit(2);
- }
- */
-
} // end of launching child process
if (!process_ok && !pid_ok) {
printf("%d CPUs detected.\n", NCPU);
increase_priority();
-/*
-Instead of all this big block of code to detect and change
-priority settings, let us just use the increase_priority()
-function. It is a little more simple and takes a more
-gradual approach, rather than "all or nothing".
--- Jesse
-
- if (setpriority(PRIO_PROCESS, my_pid,-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, my_pid);
- getrlimit(RLIMIT_NICE, &maxlimit);
-
-//if we can do better then current
- if( (20 - (signed)maxlimit.rlim_cur) < nice_lim &&
- setpriority(PRIO_PROCESS, my_pid,
- 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;
- }
-*/
-
//time quantum in microseconds. it's splitted in a working period and a sleeping one
int period=100000;
//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;
// 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;
}
if (kill_process)
{
kill(pid, SIGKILL);
- fprintf(stderr, "Process %d killed.\n", pid);
+ if (!quiet)
+ fprintf(stderr, "Process %d killed.\n", pid);
if ( (lazy) && (! restore_process) )
exit(2);
// restart killed process
}
}
}
- // do not kll process, just throttle it
+ // 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;
}