Commit dc3501d8 authored by Nigel Kukard's avatar Nigel Kukard
Browse files

Process & process group support

Added support to work on a process or process gorup. We are going to default
to a single PID if specified on the commandline. Not an entire process group.
parent b62058ea
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
AC_PREREQ([2.67]) AC_PREREQ([2.67])
AC_INIT([cputool], [0.0.1], [nkukard@lbsd.net]) AC_INIT([cputool], [0.0.2], [nkukard@lbsd.net])
AM_INIT_AUTOMAKE AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([cputool.h]) AC_CONFIG_SRCDIR([cputool.h])
......
...@@ -49,7 +49,8 @@ ...@@ -49,7 +49,8 @@
int verbose = 0; int verbose = 0;
/* These two child variable are used for the signalling function */ /* These two child variable are used for the signalling function */
int child_pgid = 0; pid_t child_pid = 0;
pid_t child_pgid = 0;
int child_external = 0; int child_external = 0;
/* Use with the SIGUSR1 and SIGUSR2 to stop/resume suspending/unsuspending */ /* Use with the SIGUSR1 and SIGUSR2 to stop/resume suspending/unsuspending */
int canStopProcesses = 1; int canStopProcesses = 1;
...@@ -68,6 +69,8 @@ static void printUsage(char **argv) { ...@@ -68,6 +69,8 @@ static void printUsage(char **argv) {
fprintf(stderr,"\n"); fprintf(stderr,"\n");
fprintf(stderr,"Options:\n"); fprintf(stderr,"Options:\n");
fprintf(stderr," -p, --pid=<PID> Manage the CPU usage of a specific PID\n"); fprintf(stderr," -p, --pid=<PID> Manage the CPU usage of a specific PID\n");
fprintf(stderr," -P, --pid-pgrp=<PID> Manage the CPU usage of a specific PID's entire\n");
fprintf(stderr," process group.\n");
fprintf(stderr," -c, --cpu-limit=<PCNT> Percentage of CPU to limit process to\n"); fprintf(stderr," -c, --cpu-limit=<PCNT> Percentage of CPU to limit process to\n");
fprintf(stderr," -l, --load-limit=<LOAD> Load to limit process to. Decimals allowed\n"); fprintf(stderr," -l, --load-limit=<LOAD> Load to limit process to. Decimals allowed\n");
fprintf(stderr," -v, --verbose Be verbose in what we do\n"); fprintf(stderr," -v, --verbose Be verbose in what we do\n");
...@@ -159,8 +162,9 @@ static int getProcessStat(pid_t pid, struct cputool_stat *pstat) ...@@ -159,8 +162,9 @@ static int getProcessStat(pid_t pid, struct cputool_stat *pstat)
fclose(fd); fclose(fd);
/* Check result */ /* Check result */
if (i < 42) if (i < 42) {
return -1; return -1;
}
return 0; return 0;
} }
...@@ -222,16 +226,25 @@ static void sigusr2 () { ...@@ -222,16 +226,25 @@ static void sigusr2 () {
/* And the handler itself */ /* And the handler itself */
static void signal_handler(int signum) { static void signal_handler(int signum) {
/* If we have a child we can handle it here */ /* Resume so the child can handle the signal */
if(child_pgid > 0) { if (child_pgid) {
/* Resume so the child can handle the signal */ killpg(child_pgid,SIGCONT);
killpg(child_pgid, SIGCONT); } else if (child_pid) {
kill(child_pid,SIGCONT);
}
/* Only kill if its ours */ /* Make sure its not an external process */
if (!child_external) { if (!child_external) {
killpg(child_pgid, signum);
waitpid(-1, NULL, 0); /* Kill it with the signal we got */
if (child_pid) {
killpg(child_pgid,signum);
} else if (child_pid) {
kill(child_pid,signum);
} }
/* And wait... */
waitpid(-1, NULL, 0);
} }
/* We should not continue looping */ /* We should not continue looping */
...@@ -281,6 +294,7 @@ int main (int argc, char *argv[]) { ...@@ -281,6 +294,7 @@ int main (int argc, char *argv[]) {
/* Our long options */ /* Our long options */
struct option long_options[] = { struct option long_options[] = {
{"pid",0,0,'p'}, {"pid",0,0,'p'},
{"pid-pgrp",0,0,'P'},
{"cpu-limit",0,0,'c'}, {"cpu-limit",0,0,'c'},
{"load-limit",0,0,'l'}, {"load-limit",0,0,'l'},
{"verbose",0,0,'v'}, {"verbose",0,0,'v'},
...@@ -309,16 +323,21 @@ int main (int argc, char *argv[]) { ...@@ -309,16 +323,21 @@ int main (int argc, char *argv[]) {
char c; char c;
/* Process */ /* Process */
c = getopt_long(argc,argv,"p:c:l:vh",long_options,&option_index); c = getopt_long(argc,argv,"p:P:c:l:vh",long_options,&option_index);
if (c == -1) if (c == -1) {
break; break;
}
/* Check... */ /* Check... */
switch (c) { switch (c) {
case 'p': case 'p':
pid = atoi(optarg); pid = atoi(optarg);
break; break;
case 'P':
pid = atoi(optarg);
pgid = pid;
break;
case 'c': case 'c':
cpuLimit = atoi(optarg); cpuLimit = atoi(optarg);
break; break;
...@@ -338,14 +357,14 @@ int main (int argc, char *argv[]) { ...@@ -338,14 +357,14 @@ int main (int argc, char *argv[]) {
} }
/* If we don't have a PID we should have a command to run */ /* If we don't have a PID we should have a command to run */
if (!pid && optind == argc) { if (!pid && !pgid && optind == argc) {
fprintf(stderr,"%s: Nothing to manage, either --pid/-p or a command must be given\n\n",argv[0]); fprintf(stderr,"%s: Nothing to manage. You must specify --pid/-p, --pid-pgid/-P or a command\n\n",argv[0]);
fprintf(stderr,"Try --help for more info\n"); fprintf(stderr,"Try --help for more info\n");
return 1; return 1;
} }
/* If we STILL have params left over, its bad */ /* If we STILL have params left over, its bad */
if (pid && optind < argc) { if (pid && !pgid && optind < argc) {
while (optind < argc) while (optind < argc)
fprintf(stderr,"%s: Invalid option -- %s\n",argv[0],argv[optind++]); fprintf(stderr,"%s: Invalid option -- %s\n",argv[0],argv[optind++]);
fprintf(stderr,"Try --help for more info\n"); fprintf(stderr,"Try --help for more info\n");
...@@ -392,12 +411,18 @@ int main (int argc, char *argv[]) { ...@@ -392,12 +411,18 @@ int main (int argc, char *argv[]) {
logmsg("ERROR: Failed to execute command '%s': %s\n",argv[0],strerror(errno)); logmsg("ERROR: Failed to execute command '%s': %s\n",argv[0],strerror(errno));
return 1; return 1;
} }
/* Set process group if we were specified on the commandline */ /* Set process group if we were specified on the commandline */
} else { } else {
pgid = getpgid(pid); child_external = 1;
/* Setup the process group properly, we just set it to pid earlier */
if (pgid) {
pgid = getpgid(pid);
}
} }
/* Setup child parent group for signalling */ /* Setup child parent group for signalling */
child_pid = pid;
child_pgid = pgid; child_pgid = pgid;
/* Continue process */ /* Continue process */
killpg(pgid, SIGCONT); killpg(pgid, SIGCONT);
...@@ -406,8 +431,9 @@ int main (int argc, char *argv[]) { ...@@ -406,8 +431,9 @@ int main (int argc, char *argv[]) {
/* Last update is right now */ /* Last update is right now */
gettimeofday(&lastUpdate,NULL); gettimeofday(&lastUpdate,NULL);
if (verbose > 1) if (verbose > 1) {
logmsg("Child PID/PGID => %lu/%lu\n",pid,pgid); logmsg("Child PID/PGID => %lu/%lu\n",pid,pgid);
}
/* This is the main program loop */ /* This is the main program loop */
while (loop) { while (loop) {
...@@ -419,22 +445,24 @@ int main (int argc, char *argv[]) { ...@@ -419,22 +445,24 @@ int main (int argc, char *argv[]) {
/* Check if we have dead children */ /* Check if we have dead children */
if (haveChild) { if (haveChild) {
if (waitpid(pid, &waitStatus, WNOHANG) < 0) if (waitpid(pid, &waitStatus, WNOHANG) < 0) {
logmsg("Dead child\n");
break; break;
}
/* Check process is still alive */ /* Check process is still alive */
} else { } else if (kill(pid,0) == -1 && errno == ESRCH) {
if (kill(pid,0) == -1 && errno == ESRCH) logmsg("Process not alive\n");
break; break;
} }
/* We need to grab "now" so we can calculate below */ /* We need to grab "now" so we can calculate below */
gettimeofday(&now,NULL); gettimeofday(&now,NULL);
elapsed_us = timediff_us(&now,&lastUpdate); elapsed_us = timediff_us(&now,&lastUpdate);
/* Statistics */ /* Statistics */
if (!wasRunning) if (!wasRunning) {
statsTimeDelayed += elapsed_us; statsTimeDelayed += elapsed_us;
}
/* Are we processing load limits? */ /* Are we processing load limits? */
if (loadLimit > 0.00) { if (loadLimit > 0.00) {
...@@ -449,8 +477,9 @@ int main (int argc, char *argv[]) { ...@@ -449,8 +477,9 @@ int main (int argc, char *argv[]) {
exceededLoad = 0; exceededLoad = 0;
} }
if (verbose > 1) if (verbose > 1) {
logmsg("LOAD LIMIT => load %.2f/%.2f\n",load,loadLimit); logmsg("LOAD LIMIT => load %.2f/%.2f\n",load,loadLimit);
}
} }
/* Are we processing cpu limits? */ /* Are we processing cpu limits? */
...@@ -499,15 +528,17 @@ int main (int argc, char *argv[]) { ...@@ -499,15 +528,17 @@ int main (int argc, char *argv[]) {
/* If our value is higher than the clock precision we MUST adjust it lower */ /* If our value is higher than the clock precision we MUST adjust it lower */
/* or we will get a error returned */ /* or we will get a error returned */
else if (sleep_us > CLOCK_PRECISION - DEFAULT_SLEEP) else if (sleep_us > CLOCK_PRECISION - DEFAULT_SLEEP) {
sleep_us = CLOCK_PRECISION - DEFAULT_SLEEP; sleep_us = CLOCK_PRECISION - DEFAULT_SLEEP;
}
/* Set last values */ /* Set last values */
cpuLast = cpuNow; cpuLast = cpuNow;
/* Print out info if we're running in verbose mode */ /* Print out info if we're running in verbose mode */
if (verbose > 1) if (verbose > 1) {
logmsg("CPU LIMIT => tickBucket = %.2f (allowed += %.2f, consumed -= %lu), elapsed us = %lu, sleep_us = %lu\n", tickBucket, ticks_allowed, ticks_delta, elapsed_us, sleep_us); logmsg("CPU LIMIT => tickBucket = %.2f (allowed += %.2f, consumed -= %lu), elapsed us = %lu, sleep_us = %lu\n", tickBucket, ticks_allowed, ticks_delta, elapsed_us, sleep_us);
}
} }
/* Set last time we were updated */ /* Set last time we were updated */
...@@ -522,20 +553,40 @@ int main (int argc, char *argv[]) { ...@@ -522,20 +553,40 @@ int main (int argc, char *argv[]) {
/* If we running and we should not be, then stop */ /* If we running and we should not be, then stop */
if ((exceededLoad || exceededCPU) && wasRunning) { if ((exceededLoad || exceededCPU) && wasRunning) {
if (verbose > 2)
logmsg("KILLPG: SIGSTOP sent to %lu\n",pgid);
killpg(pgid,SIGSTOP); /* Check if we signalling the process group or process */
if (pgid) {
killpg(pgid,SIGSTOP);
if (verbose > 2) {
logmsg("KILLPG: SIGSTOP sent to process group %lu (%lu)\n",pgid,pid);
}
} else if (pid) {
kill(pid,SIGSTOP);
if (verbose > 2) {
logmsg("KILLPG: SIGSTOP sent to process %lu\n",pid);
}
}
isRunning = 0; isRunning = 0;
statsSlowdowns++; statsSlowdowns++;
/* If we not running and should be then continue */ /* If we not running and should be then continue */
} else if (!(exceededLoad || exceededCPU) && !wasRunning) { } else if (!(exceededLoad || exceededCPU) && !wasRunning) {
if (verbose > 2)
logmsg("KILLPG: SIGCONT sent to %lu\n",pgid);
killpg(pgid,SIGCONT); /* Check if we signalling the process group or process */
if (pgid) {
killpg(pgid,SIGCONT);
if (verbose > 2) {
logmsg("KILLPG: SIGCONT sent to process group %lu (%lu)\n",pgid,pid);
}
} else if (pid) {
kill(pid,SIGCONT);
if (verbose > 2) {
logmsg("KILLPG: SIGCONT sent to process %lu\n",pid);
}
}
isRunning = 1; isRunning = 1;
} }
...@@ -544,8 +595,9 @@ int main (int argc, char *argv[]) { ...@@ -544,8 +595,9 @@ int main (int argc, char *argv[]) {
nanosleep(&sleepTime,NULL); nanosleep(&sleepTime,NULL);
} }
if (verbose) if (verbose) {
logmsg("STATISTICS: Slowdowns = %lu, Total Time Delayed = %.2fs\n",statsSlowdowns,( (double) statsTimeDelayed / (double) CLOCK_PRECISION )); logmsg("STATISTICS: Slowdowns = %lu, Total Time Delayed = %.2fs\n",statsSlowdowns,( (double) statsTimeDelayed / (double) CLOCK_PRECISION ));
}
return WEXITSTATUS(waitStatus); return WEXITSTATUS(waitStatus);
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment