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 @@
# Process this file with autoconf to produce a configure script.
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
AC_CONFIG_SRCDIR([cputool.h])
......
......@@ -49,7 +49,8 @@
int verbose = 0;
/* 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;
/* Use with the SIGUSR1 and SIGUSR2 to stop/resume suspending/unsuspending */
int canStopProcesses = 1;
......@@ -68,6 +69,8 @@ static void printUsage(char **argv) {
fprintf(stderr,"\n");
fprintf(stderr,"Options:\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," -l, --load-limit=<LOAD> Load to limit process to. Decimals allowed\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)
fclose(fd);
/* Check result */
if (i < 42)
return -1;
if (i < 42) {
return -1;
}
return 0;
}
......@@ -222,16 +226,25 @@ static void sigusr2 () {
/* And the handler itself */
static void signal_handler(int signum) {
/* If we have a child we can handle it here */
if(child_pgid > 0) {
/* Resume so the child can handle the signal */
killpg(child_pgid, SIGCONT);
/* Resume so the child can handle the signal */
if (child_pgid) {
killpg(child_pgid,SIGCONT);
} else if (child_pid) {
kill(child_pid,SIGCONT);
}
/* Only kill if its ours */
if (!child_external) {
killpg(child_pgid, signum);
waitpid(-1, NULL, 0);
/* Make sure its not an external process */
if (!child_external) {
/* 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 */
......@@ -281,6 +294,7 @@ int main (int argc, char *argv[]) {
/* Our long options */
struct option long_options[] = {
{"pid",0,0,'p'},
{"pid-pgrp",0,0,'P'},
{"cpu-limit",0,0,'c'},
{"load-limit",0,0,'l'},
{"verbose",0,0,'v'},
......@@ -309,16 +323,21 @@ int main (int argc, char *argv[]) {
char c;
/* 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;
}
/* Check... */
switch (c) {
case 'p':
pid = atoi(optarg);
break;
case 'P':
pid = atoi(optarg);
pgid = pid;
break;
case 'c':
cpuLimit = atoi(optarg);
break;
......@@ -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 (!pid && optind == argc) {
fprintf(stderr,"%s: Nothing to manage, either --pid/-p or a command must be given\n\n",argv[0]);
if (!pid && !pgid && optind == argc) {
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");
return 1;
}
/* If we STILL have params left over, its bad */
if (pid && optind < argc) {
if (pid && !pgid && optind < argc) {
while (optind < argc)
fprintf(stderr,"%s: Invalid option -- %s\n",argv[0],argv[optind++]);
fprintf(stderr,"Try --help for more info\n");
......@@ -392,12 +411,18 @@ int main (int argc, char *argv[]) {
logmsg("ERROR: Failed to execute command '%s': %s\n",argv[0],strerror(errno));
return 1;
}
/* Set process group if we were specified on the commandline */
} 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 */
child_pid = pid;
child_pgid = pgid;
/* Continue process */
killpg(pgid, SIGCONT);
......@@ -406,8 +431,9 @@ int main (int argc, char *argv[]) {
/* Last update is right now */
gettimeofday(&lastUpdate,NULL);
if (verbose > 1)
logmsg("Child PID/PGID => %lu/%lu\n",pid,pgid);
if (verbose > 1) {
logmsg("Child PID/PGID => %lu/%lu\n",pid,pgid);
}
/* This is the main program loop */
while (loop) {
......@@ -419,22 +445,24 @@ int main (int argc, char *argv[]) {
/* Check if we have dead children */
if (haveChild) {
if (waitpid(pid, &waitStatus, WNOHANG) < 0)
if (waitpid(pid, &waitStatus, WNOHANG) < 0) {
logmsg("Dead child\n");
break;
}
/* Check process is still alive */
} else {
if (kill(pid,0) == -1 && errno == ESRCH)
break;
} else if (kill(pid,0) == -1 && errno == ESRCH) {
logmsg("Process not alive\n");
break;
}
/* We need to grab "now" so we can calculate below */
gettimeofday(&now,NULL);
elapsed_us = timediff_us(&now,&lastUpdate);
/* Statistics */
if (!wasRunning)
statsTimeDelayed += elapsed_us;
if (!wasRunning) {
statsTimeDelayed += elapsed_us;
}
/* Are we processing load limits? */
if (loadLimit > 0.00) {
......@@ -449,8 +477,9 @@ int main (int argc, char *argv[]) {
exceededLoad = 0;
}
if (verbose > 1)
if (verbose > 1) {
logmsg("LOAD LIMIT => load %.2f/%.2f\n",load,loadLimit);
}
}
/* Are we processing cpu limits? */
......@@ -499,15 +528,17 @@ int main (int argc, char *argv[]) {
/* If our value is higher than the clock precision we MUST adjust it lower */
/* or we will get a error returned */
else if (sleep_us > CLOCK_PRECISION - DEFAULT_SLEEP)
sleep_us = CLOCK_PRECISION - DEFAULT_SLEEP;
else if (sleep_us > CLOCK_PRECISION - DEFAULT_SLEEP) {
sleep_us = CLOCK_PRECISION - DEFAULT_SLEEP;
}
/* Set last values */
cpuLast = cpuNow;
/* Print out info if we're running in verbose mode */
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);
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);
}
}
/* Set last time we were updated */
......@@ -522,20 +553,40 @@ int main (int argc, char *argv[]) {
/* If we running and we should not be, then stop */
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;
statsSlowdowns++;
/* If we not running and should be then continue */
} 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;
}
......@@ -544,8 +595,9 @@ int main (int argc, char *argv[]) {
nanosleep(&sleepTime,NULL);
}
if (verbose)
logmsg("STATISTICS: Slowdowns = %lu, Total Time Delayed = %.2fs\n",statsSlowdowns,( (double) statsTimeDelayed / (double) CLOCK_PRECISION ));
if (verbose) {
logmsg("STATISTICS: Slowdowns = %lu, Total Time Delayed = %.2fs\n",statsSlowdowns,( (double) statsTimeDelayed / (double) CLOCK_PRECISION ));
}
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