aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAngelo Marletta <angelo.marletta@gmail.com>2012-06-23 05:02:54 -0700
committerAngelo Marletta <angelo.marletta@gmail.com>2012-06-23 05:02:54 -0700
commit157117a985b0ba13b8df6ac164c57027e4374216 (patch)
treeccc7f862b705a8049bd6fcc74a9c959b6784dda2
parent0ba6a181d84c196c9d9854ded7528baac66af461 (diff)
parentb8bc055ee5131c333af3422569bde24bb760ac65 (diff)
downloadcpulimit-157117a985b0ba13b8df6ac164c57027e4374216.tar.gz
Merge pull request #3 from opsengine/develop
Major refactoring of cpulimit
-rw-r--r--.gitignore2
-rw-r--r--src/Makefile22
-rw-r--r--src/cpulimit.c166
-rw-r--r--src/list.c2
-rw-r--r--src/list.h2
-rw-r--r--src/process.c233
-rw-r--r--src/process_group.c203
-rw-r--r--src/process_group.h53
-rw-r--r--src/process_iterator.c26
-rw-r--r--src/process_iterator.h (renamed from src/process.h)61
-rw-r--r--src/process_iterator_apple.c77
-rw-r--r--src/process_iterator_freebsd.c95
-rw-r--r--src/process_iterator_linux.c159
-rw-r--r--src/procutils.c463
-rw-r--r--src/procutils.h2
-rw-r--r--tests/Makefile20
-rwxr-xr-xtests/busybin8672 -> 0 bytes
-rw-r--r--tests/busy.c8
-rw-r--r--tests/process_iterator_test.c196
19 files changed, 926 insertions, 864 deletions
diff --git a/.gitignore b/.gitignore
index 1d390bb..70e4875 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
*.o
*~
src/cpulimit
+busy
+process_iterator_test
diff --git a/src/Makefile b/src/Makefile
index 33c632d..86fbfbd 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,22 +1,28 @@
CC?=gcc
-CFLAGS?=-Wall -O2
+CFLAGS?=-Wall -g -D_GNU_SOURCE
TARGETS=cpulimit
-LIBS=process.o procutils.o list.o
+LIBS=list.o process_iterator.o process_group.o
-all:: $(TARGETS)
+UNAME := $(shell uname)
+
+ifeq ($(UNAME), FreeBSD)
+LIBS+=-lkvm
+endif
+
+all:: $(TARGETS) $(LIBS)
cpulimit: cpulimit.c $(LIBS)
$(CC) -o cpulimit cpulimit.c $(LIBS) $(CFLAGS)
-process.o: process.c process.h
- $(CC) -c process.c $(CFLAGS)
-
-procutils.o: procutils.c procutils.h
- $(CC) -c procutils.c $(CFLAGS)
+process_iterator.o: process_iterator.c process_iterator.h
+ $(CC) -c process_iterator.c $(CFLAGS)
list.o: list.c list.h
$(CC) -c list.c $(CFLAGS)
+process_group.o: process_group.c process_group.h process_iterator.o list.o
+ $(CC) -c process_group.c $(CFLAGS)
+
clean:
rm -f *~ *.o $(TARGETS)
diff --git a/src/cpulimit.c b/src/cpulimit.c
index 300d320..394b132 100644
--- a/src/cpulimit.c
+++ b/src/cpulimit.c
@@ -61,8 +61,7 @@
#include <sys/resource.h>
#include <sys/wait.h>
-#include "process.h"
-#include "procutils.h"
+#include "process_group.h"
#include "list.h"
//some useful macro
@@ -83,7 +82,7 @@
/* GLOBAL VARIABLES */
//the "family"
-struct process_family pf;
+struct process_group pgroup;
//pid of cpulimit
pid_t cpulimit_pid;
//name of this program (maybe cpulimit...)
@@ -99,35 +98,16 @@ int verbose = 0;
//lazy mode (exits if there is no process)
int lazy = 0;
-static void *memrchr(const void *s, int c, size_t n)
-{
- const unsigned char *start = (const unsigned char*)s;
- const unsigned char *end = (const unsigned char*)s;
-
- end+=n-1;
-
- while(end>=start) {
- if(*end==c)
- return (void *)end;
- else
- end--;
- }
-
- return NULL;
-}
-
//SIGINT and SIGTERM signal handler
static void quit(int sig)
{
//let all the processes continue if stopped
struct list_node *node = NULL;
- for (node=pf.members.first; node!= NULL; node=node->next) {
+ for (node=pgroup.proclist->first; node!= NULL; node=node->next) {
struct process *p = (struct process*)(node->data);
kill(p->pid, SIGCONT);
- process_close(p);
}
- //free all the memory
- cleanup_process_family(&pf);
+ close_process_group(&pgroup);
//fix ^C little problem
printf("\r");
fflush(stdout);
@@ -185,22 +165,7 @@ static int get_ncpu() {
return ncpu;
}
-#ifdef __linux__
-
-#include <sys/vfs.h>
-
-static int check_proc()
-{
- struct statfs mnt;
- if (statfs("/proc", &mnt) < 0)
- return 0;
- if (mnt.f_type!=0x9fa0)
- return 0;
- return 1;
-}
-#endif
-
-void limit_process(pid_t pid, double limit, int ignore_children)
+void limit_process(pid_t pid, double limit, int include_children)
{
//slice of the slot in which the process is allowed to run
struct timespec twork;
@@ -226,39 +191,17 @@ void limit_process(pid_t pid, double limit, int ignore_children)
increase_priority();
//build the family
- create_process_family(&pf, pid);
- if (ignore_children) {
- //delete any process with a different pid than the father
- for (node=pf.members.first; node!=NULL; node=node->next) {
- struct process *proc = (struct process*)(node->data);
- if (proc->pid != pid)
- remove_process_from_family(&pf, proc->pid);
- }
- }
-
- if (!ignore_children && verbose) printf("Members in the family owned by %d: %d\n", pf.father, pf.members.count);
+ init_process_group(&pgroup, pid, include_children);
+
+ if (verbose) printf("Members in the process group owned by %d: %d\n", pgroup.target_pid, pgroup.proclist->count);
//rate at which we are keeping active the processes (range 0-1)
//1 means that the process are using all the twork slice
double workingrate = -1;
while(1) {
- if (!ignore_children && c%10==0) {
- //update the process family (checks only for new members)
- int new_children = update_process_family(&pf);
- if (verbose && new_children) {
- printf("%d new children processes detected (", new_children);
- int j;
- node = pf.members.last;
- for (j=0; j<new_children; j++) {
- printf("%d", ((struct process*)(node->data))->pid);
- if (j<new_children-1) printf(" ");
- node = node->previous;
- }
- printf(")\n");
- }
- }
+ update_process_group(&pgroup);
- if (pf.members.count==0) {
+ if (pgroup.proclist->count==0) {
if (verbose) printf("No more processes.\n");
break;
}
@@ -268,24 +211,12 @@ void limit_process(pid_t pid, double limit, int ignore_children)
double pcpu = -1;
//estimate how much the controlled processes are using the cpu in the working interval
- for (node=pf.members.first; node!=NULL; node=node->next) {
+ for (node = pgroup.proclist->first; node != NULL; node = node->next) {
struct process *proc = (struct process*)(node->data);
- if (proc->is_zombie) {
- //process is zombie, remove it from family
- fprintf(stderr,"Process %d is zombie!\n", proc->pid);
- remove_process_from_family(&pf, proc->pid);
+ if (proc->cpu_usage < 0) {
continue;
}
- if (process_monitor(proc) != 0) {
- //process is dead, remove it from family
- if (verbose) fprintf(stderr,"Process %d dead!\n", proc->pid);
- remove_process_from_family(&pf, proc->pid);
- continue;
- }
- if (proc->cpu_usage<0) {
- continue;
- }
- if (pcpu<0) pcpu = 0;
+ if (pcpu < 0) pcpu = 0;
pcpu += proc->cpu_usage;
}
@@ -294,52 +225,52 @@ void limit_process(pid_t pid, double limit, int ignore_children)
//it's the 1st cycle, initialize workingrate
pcpu = limit;
workingrate = limit;
- twork.tv_nsec = TIME_SLOT*limit*1000;
+ twork.tv_nsec = TIME_SLOT * limit * 1000;
}
else {
//adjust workingrate
workingrate = MIN(workingrate / pcpu * limit, 1);
- twork.tv_nsec = TIME_SLOT*1000*workingrate;
+ twork.tv_nsec = TIME_SLOT * 1000 * workingrate;
}
- tsleep.tv_nsec = TIME_SLOT*1000-twork.tv_nsec;
+ tsleep.tv_nsec = TIME_SLOT * 1000 - twork.tv_nsec;
if (verbose) {
if (c%200==0)
printf("\n%%CPU\twork quantum\tsleep quantum\tactive rate\n");
if (c%10==0 && c>0)
- printf("%0.2lf%%\t%6ld us\t%6ld us\t%0.2lf%%\n",pcpu*100,twork.tv_nsec/1000,tsleep.tv_nsec/1000,workingrate*100);
+ printf("%0.2lf%%\t%6ld us\t%6ld us\t%0.2lf%%\n", pcpu*100, twork.tv_nsec/1000, tsleep.tv_nsec/1000, workingrate*100);
}
//resume processes
- for (node=pf.members.first; node!=NULL; node=node->next) {
+ for (node = pgroup.proclist->first; node != NULL; node = node->next) {
struct process *proc = (struct process*)(node->data);
if (kill(proc->pid,SIGCONT)!=0) {
//process is dead, remove it from family
- if (verbose) fprintf(stderr,"Process %d dead!\n", proc->pid);
- remove_process_from_family(&pf, proc->pid);
+ if (verbose) fprintf(stderr, "Process %d dead!\n", proc->pid);
+ //remove_process_from_family(&pf, proc->pid);
}
}
//now processes are free to run (same working slice for all)
gettimeofday(&startwork, NULL);
- nanosleep(&twork,NULL);
+ nanosleep(&twork, NULL);
gettimeofday(&endwork, NULL);
- workingtime = timediff(&endwork,&startwork);
+ workingtime = timediff(&endwork, &startwork);
- long delay = workingtime-twork.tv_nsec/1000;
+ long delay = workingtime - twork.tv_nsec/1000;
if (c>0 && delay>10000) {
//delay is too much! signal to user?
//fprintf(stderr, "%d %ld us\n", c, delay);
}
if (tsleep.tv_nsec>0) {
- //stop only if tsleep>0, instead it's useless
- for (node=pf.members.first; node!=NULL; node=node->next) {
+ //stop only if tsleep>0
+ for (node = pgroup.proclist->first; node != NULL; node = node->next) {
struct process *proc = (struct process*)(node->data);
if (kill(proc->pid,SIGSTOP)!=0) {
//process is dead, remove it from family
if (verbose) fprintf(stderr,"Process %d dead!\n", proc->pid);
- remove_process_from_family(&pf, proc->pid);
+ //remove_process_from_family(&pf, proc->pid);
}
}
//now the processes are sleeping
@@ -347,7 +278,7 @@ void limit_process(pid_t pid, double limit, int ignore_children)
}
c++;
}
- cleanup_process_family(&pf);
+ close_process_group(&pgroup);
}
int main(int argc, char **argv) {
@@ -361,8 +292,8 @@ int main(int argc, char **argv) {
int ignore_children = 0;
//get program name
- char *p=(char*)memrchr(argv[0],(unsigned int)'/',strlen(argv[0]));
- program_name = p==NULL?argv[0]:(p+1);
+ char *p = (char*)memrchr(argv[0], (unsigned int)'/', strlen(argv[0]));
+ program_name = p==NULL ? argv[0] : (p+1);
//get current pid
cpulimit_pid = getpid();
//get cpu count
@@ -422,12 +353,12 @@ int main(int argc, char **argv) {
}
} while(next_option != -1);
- if (pid_ok && (pid<=1 || pid>=65536)) {
+ if (pid_ok && (pid <= 1 || pid >= 65536)) {
fprintf(stderr,"Error: Invalid value for argument PID\n");
print_usage(stderr, 1);
exit(1);
}
- if (pid!=0) {
+ if (pid != 0) {
lazy = 1;
}
@@ -436,14 +367,14 @@ int main(int argc, char **argv) {
print_usage(stderr, 1);
exit(1);
}
- double limit = perclimit/100.0;
+ double limit = perclimit / 100.0;
if (limit<0 || limit >NCPU) {
fprintf(stderr,"Error: limit must be in the range 0-%d00\n", NCPU);
print_usage(stderr, 1);
exit(1);
}
- int command_mode = optind<argc;
+ int command_mode = optind < argc;
if (exe_ok + pid_ok + command_mode == 0) {
fprintf(stderr,"Error: You must specify one target process, either by name, pid, or command line\n");
print_usage(stderr, 1);
@@ -463,19 +394,12 @@ int main(int argc, char **argv) {
//print the number of available cpu
if (verbose) printf("%d cpu detected\n", NCPU);
-#ifdef __linux__
- if (!check_proc()) {
- fprintf(stderr, "procfs is not mounted!\nAborting\n");
- exit(-2);
- }
-#endif
-
if (command_mode) {
int i;
//executable file
const char *cmd = argv[optind];
//command line arguments
- char **cmd_args = (char**)malloc((argc-optind+1)*sizeof(char*));
+ char **cmd_args = (char**)malloc((argc-optind + 1) * sizeof(char*));
if (cmd_args==NULL) exit(2);
for (i=0; i<argc-optind; i++) {
cmd_args[i] = argv[i+optind];
@@ -494,7 +418,14 @@ int main(int argc, char **argv) {
if (child < 0) {
exit(EXIT_FAILURE);
}
- else if (child > 0) {
+ else if (child == 0) {
+ //target process code
+ int ret = execvp(cmd, cmd_args);
+ //if we are here there was an error, show it
+ perror("Error");
+ exit(ret);
+ }
+ else {
//parent code
free(cmd_args);
int limiter = fork();
@@ -521,13 +452,6 @@ int main(int argc, char **argv) {
exit(0);
}
}
- else {
- //target process code
- int ret = execvp(cmd, cmd_args);
- //if we are here there was an error, show it
- perror("Error");
- exit(ret);
- }
}
while(1) {
@@ -535,7 +459,7 @@ int main(int argc, char **argv) {
pid_t ret = 0;
if (pid_ok) {
//search by pid
- ret = look_for_process_by_pid(pid);
+ ret = find_process_by_pid(pid);
if (ret == 0) {
printf("No process found\n");
}
@@ -545,7 +469,7 @@ int main(int argc, char **argv) {
}
else {
//search by file or path name
- ret = look_for_process_by_name(exe);
+ ret = find_process_by_name(exe);
if (ret == 0) {
printf("No process found\n");
}
@@ -558,7 +482,7 @@ int main(int argc, char **argv) {
}
if (ret > 0) {
if (ret == cpulimit_pid) {
- printf("Process %d is cpulimit itself! Aborting to avoid deadlock\n", ret);
+ printf("Target process %d is cpulimit itself! Aborting because it makes no sense\n", ret);
exit(1);
}
printf("Process %d found\n", pid);
diff --git a/src/list.c b/src/list.c
index 49f84ce..6fe1e06 100644
--- a/src/list.c
+++ b/src/list.c
@@ -127,7 +127,7 @@ void *locate_elem(struct list *l,void *elem) {
return(xlocate_elem(l,elem,0,0));
}
-void flush_list(struct list *l) {
+void clear_list(struct list *l) {
struct list_node *tmp;
while(l->first!=EMPTYLIST) {
tmp=l->first;
diff --git a/src/list.h b/src/list.h
index 387f79c..478635f 100644
--- a/src/list.h
+++ b/src/list.h
@@ -133,7 +133,7 @@ void *locate_elem(struct list *l,void *elem);
/*
* Delete all the elements in the list
*/
-void flush_list(struct list *l);
+void clear_list(struct list *l);
/*
* Delete every element in the list, and free the memory pointed by all the node data
diff --git a/src/process.c b/src/process.c
deleted file mode 100644
index 398d9e9..0000000
--- a/src/process.c
+++ /dev/null
@@ -1,233 +0,0 @@
-/**
- *
- * cpulimit - a cpu limiter for Linux
- *
- * Copyright (C) 2005-2008, by: Angelo Marletta <marlonx80@hotmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-//TODO: add documentation to public functions
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <signal.h>
-#include <time.h>
-#include <sys/utsname.h>
-
-#include "process.h"
-
-#ifdef __APPLE__
-#include <sys/sysctl.h>
-#include <errno.h>
-#endif
-
-#ifdef __linux__
-int get_proc_info(struct process *p, pid_t pid) {
-/* static char statfile[20];
- static char buffer[64];
- int ret;
- sprintf(statfile, "/proc/%d/stat", pid);
- FILE *fd = fopen(statfile, "r");
- if (fd==NULL) return -1;
- fgets(buffer, sizeof(buffer), fd);
- fclose(fd);
-
- char state;
-
- int n = sscanf(buffer, "%d %s %c %d %d %d %d %d "
- "%lu %lu %lu %lu %lu %lu %lu "
- "%ld %ld %ld %ld %ld %ld "
- "%lu ",
- &p->pid,
- &p->command,
- &state,
- NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
- &utime,&stime,&cutime,&cstime,
- NULL,NULL,NULL,NULL,
- &starttime,
- );*/
- return 0;
-}
-#elif defined __APPLE__
-int get_proc_info(struct process *p, pid_t pid) {
- int err;
- struct kinfo_proc *result = NULL;
- size_t length;
- int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
-
- /* We start by calling sysctl with result == NULL and length == 0.
- That will succeed, and set length to the appropriate length.
- We then allocate a buffer of that size and call sysctl again
- with that buffer.
- */
- length = 0;
- err = sysctl(mib, 4, NULL, &length, NULL, 0);
- if (err == -1) {
- err = errno;
- }
- if (err == 0) {
- result = malloc(length);
- err = sysctl(mib, 4, result, &length, NULL, 0);
- if (err == -1)
- err = errno;
- if (err == ENOMEM) {
- free(result); /* clean up */
- result = NULL;
- }
- }
-
- p->pid = result->kp_proc.p_pid;
- p->ppid = result->kp_eproc.e_ppid;
- p->starttime = result->kp_proc.p_starttime.tv_sec;
- p->last_jiffies = result->kp_proc.p_cpticks;
- //p_pctcpu
-
- return 0;
-}
-#endif
-
-// returns the start time of a process (used with pid to identify a process)
-static int get_starttime(pid_t pid)
-{
-#ifdef __linux__
- char file[20];
- char buffer[1024];
- sprintf(file, "/proc/%d/stat", pid);
- FILE *fd = fopen(file, "r");
- if (fd==NULL) return -1;
- if (fgets(buffer, sizeof(buffer), fd) == NULL) return -1;
- fclose(fd);
- char *p = buffer;
- p = memchr(p+1,')', sizeof(buffer) - (p-buffer));
- int sp = 20;
- while (sp--)
- p = memchr(p+1,' ',sizeof(buffer) - (p-buffer));
- //start time of the process
- int time = atoi(p+1);
- return time;
-#elif defined __APPLE__
- struct process proc;
- get_proc_info(&proc, pid);
- return proc.starttime;
-#endif
-}
-
-static int get_jiffies(struct process *proc) {
-#ifdef __linux__
- FILE *f = fopen(proc->stat_file, "r");
- if (f==NULL) return -1;
- if (fgets(proc->buffer, sizeof(proc->buffer),f) == NULL) return -1;
- fclose(f);
- char *p = proc->buffer;
- p = memchr(p+1,')', sizeof(proc->buffer) - (p-proc->buffer));
- int sp = 12;
- while (sp--)
- p = memchr(p+1,' ',sizeof(proc->buffer) - (p-proc->buffer));
- //user mode jiffies
- int utime = atoi(p+1);
- p = memchr(p+1,' ',sizeof(proc->buffer) - (p-proc->buffer));
- //kernel mode jiffies
- int ktime = atoi(p+1);
- return utime+ktime;
-#elif defined __APPLE__
- struct process proc2;
- get_proc_info(&proc2, proc->pid);
- return proc2.last_jiffies;
-#endif
-}
-
-//return t1-t2 in microseconds (no overflow checks, so better watch out!)
-static inline unsigned long timediff(const struct timeval *t1,const struct timeval *t2)
-{
- return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_usec - t2->tv_usec);
-}
-
-/*static int*/ int process_update(struct process *proc) {
- //TODO: get any process statistic here
- //check that starttime is not changed(?), update jiffies, parent, zombie status
- return 0;
-}
-
-int process_init(struct process *proc, int pid)
-{
- //general members
- proc->pid = pid;
- proc->starttime = get_starttime(pid);
- proc->cpu_usage = 0;
- memset(&(proc->last_sample), 0, sizeof(struct timeval));
- proc->last_jiffies = -1;
- //system dependent members
-#ifdef __linux__
-//TODO: delete these members for the sake of portability?
- //test /proc file descriptor for reading
- sprintf(proc->stat_file, "/proc/%d/stat", pid);
- FILE *fd = fopen(proc->stat_file, "r");
- if (fd == NULL) return 1;
- fclose(fd);
-#endif
- return 0;
-}
-
-//parameter in range 0-1
-#define ALFA 0.08
-
-int process_monitor(struct process *proc)
-{
- int j = get_jiffies(proc);
- if (j<0) return -1; //error retrieving jiffies count (maybe the process is dead)
- struct timeval now;
- gettimeofday(&now, NULL);
- if (proc->last_jiffies==-1) {
- //store current time
- proc->last_sample = now;
- //store current jiffies
- proc->last_jiffies = j;
- //it's the first sample, cannot figure out the cpu usage
- proc->cpu_usage = -1;
- return 0;
- }
- //time from previous sample (in ns)
- long dt = timediff(&now, &(proc->last_sample));
- //how many jiffies in dt?
- double max_jiffies = dt * HZ / 1000000.0;
- double sample = (j - proc->last_jiffies) / max_jiffies;
- if (proc->cpu_usage == -1) {
- //initialization
- proc->cpu_usage = sample;
- }
- else {
- //usage adjustment
- proc->cpu_usage = (1-ALFA) * proc->cpu_usage + ALFA * sample;
- }
-
- //store current time
- proc->last_sample = now;
- //store current jiffies
- proc->last_jiffies = j;
-
- return 0;
-}
-
-int process_close(struct process *proc)
-{
- if (kill(proc->pid,SIGCONT)!=0) {
- fprintf(stderr,"Process %d is already dead!\n", proc->pid);
- }
- proc->pid = 0;
- return 0;
-}
diff --git a/src/process_group.c b/src/process_group.c
new file mode 100644
index 0000000..71f180b
--- /dev/null
+++ b/src/process_group.c
@@ -0,0 +1,203 @@
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <signal.h>
+
+#include <assert.h>
+
+#include "process_iterator.h"
+#include "process_group.h"
+#include "list.h"
+
+// look for a process by pid
+// search_pid : pid of the wanted process
+// return: pid of the found process, if successful
+// negative pid, if the process does not exist or if the signal fails
+int find_process_by_pid(pid_t pid)
+{
+ return (kill(pid,0)==0) ? pid : -pid;
+}
+
+// look for a process with a given name
+// process: the name of the wanted process. it can be an absolute path name to the executable file
+// or just the file name
+// return: pid of the found process, if it is found
+// 0, if it's not found
+// negative pid, if it is found but it's not possible to control it
+int find_process_by_name(const char *process_name)
+{
+ //whether the variable process_name is the absolute path or not
+ int is_absolute_path = process_name[0] == '/';
+ //flag indicating if the a process with given name was found
+ int found = 0;
+ pid_t pid = -1;
+
+ //process iterator
+ struct process_iterator it;
+ struct process proc;
+ struct process_filter filter;
+ filter.pid = 0;
+ filter.include_children = 0;
+ init_process_iterator(&it, &filter);
+ while (get_next_process(&it, &proc) != -1)
+ {
+ pid = proc.pid;
+ int size = strlen(proc.command);
+
+ found = 0;
+ if (is_absolute_path && strncmp(proc.command, process_name, size)==0 && size==strlen(process_name)) {
+ //process found
+ found = 1;
+ }
+ else {
+ //process found
+ if (strncmp(proc.command + size - strlen(process_name), process_name, strlen(process_name))==0) {
+ found = 1;
+ }
+ }
+ if (found==1) {
+ if (kill(pid,SIGCONT)==0) {
+ //process is ok!
+ break;
+ }
+ else {
+ //we don't have permission to send signal to that process
+ //so, don't exit from the loop and look for another one with the same name
+ found = -1;
+ }
+ }
+ }
+ if (close_process_iterator(&it) != 1) exit(1);
+ if (found == 1) {
+ //ok, the process was found
+ return pid;
+ }
+ else if (found == 0) {
+ //no process found
+ return 0;
+ }
+ else if (found == -1) {
+ //the process was found, but we haven't permission to control it
+ return -pid;
+ }
+ //this MUST NOT happen
+ exit(2);
+}
+
+int init_process_group(struct process_group *pgroup, int target_pid, int include_children)
+{
+ //hashtable initialization
+ memset(&pgroup->proctable, 0, sizeof(pgroup->proctable));
+ pgroup->target_pid = target_pid;
+ pgroup->include_children = include_children;
+ pgroup->proclist = (struct list*)malloc(sizeof(struct list));
+ init_list(pgroup->proclist, 4);
+ memset(&pgroup->last_update, 0, sizeof(pgroup->last_update));
+ update_process_group(pgroup);
+ return 0;
+}
+
+int close_process_group(struct process_group *pgroup)
+{
+ int i;
+ int size = sizeof(pgroup->proctable) / sizeof(struct process*);
+ for (i=0; i<size; i++) {
+ if (pgroup->proctable[i] != NULL) {
+ //free() history for each process
+ destroy_list(pgroup->proctable[i]);
+ free(pgroup->proctable[i]);
+ pgroup->proctable[i] = NULL;
+ }
+ }
+ clear_list(pgroup->proclist);
+ free(pgroup->proclist);
+ pgroup->proclist = NULL;
+ return 0;
+}
+
+void remove_terminated_processes(struct process_group *pgroup)
+{
+ //TODO
+}
+
+//return t1-t2 in microseconds (no overflow checks, so better watch out!)
+static inline unsigned long timediff(const struct timeval *t1,const struct timeval *t2)
+{
+ return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_usec - t2->tv_usec);
+}
+
+//parameter in range 0-1
+#define ALFA 0.08
+#define MIN_DT 20
+
+void update_process_group(struct process_group *pgroup)
+{
+ struct process_iterator it;
+ struct process tmp_process;
+ struct process_filter filter;
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ //time elapsed from previous sample (in ms)
+ long dt = timediff(&now, &pgroup->last_update) / 1000;
+ filter.pid = pgroup->target_pid;
+ filter.include_children = pgroup->include_children;
+ init_process_iterator(&it, &filter);
+ clear_list(pgroup->proclist);
+ init_list(pgroup->proclist, 4);
+
+ while (get_next_process(&it, &tmp_process) != -1)
+ {
+// struct timeval t;
+// gettimeofday(&t, NULL);
+// printf("T=%ld.%ld PID=%d PPID=%d START=%d CPUTIME=%d\n", t.tv_sec, t.tv_usec, tmp_process.pid, tmp_process.ppid, tmp_process.starttime, tmp_process.cputime);
+ int hashkey = pid_hashfn(tmp_process.pid + tmp_process.starttime);
+ if (pgroup->proctable[hashkey] == NULL)
+ {
+ //empty bucket
+ pgroup->proctable[hashkey] = malloc(sizeof(struct list));
+ struct process *new_process = malloc(sizeof(struct process));
+ tmp_process.cpu_usage = -1;
+ memcpy(new_process, &tmp_process, sizeof(struct process));
+ init_list(pgroup->proctable[hashkey], 4);
+ add_elem(pgroup->proctable[hashkey], new_process);
+ add_elem(pgroup->proclist, new_process);
+ }
+ else
+ {
+ //existing bucket
+ struct process *p = (struct process*)locate_elem(pgroup->proctable[hashkey], &tmp_process);
+ if (p == NULL)
+ {
+ //process is new. add it
+ struct process *new_process = malloc(sizeof(struct process));
+ tmp_process.cpu_usage = -1;
+ memcpy(new_process, &tmp_process, sizeof(struct process));
+ add_elem(pgroup->proctable[hashkey], new_process);
+ add_elem(pgroup->proclist, new_process);
+ }
+ else
+ {
+ assert(tmp_process.pid == p->pid);
+ assert(tmp_process.ppid == p->ppid);
+ assert(tmp_process.starttime == p->starttime);
+ add_elem(pgroup->proclist, p);
+ if (dt < MIN_DT) continue;
+ //process exists. update CPU usage
+ double sample = 1.0 * (tmp_process.cputime - p->cputime) / dt;
+ if (p->cpu_usage == -1) {
+ //initialization
+ p->cpu_usage = sample;
+ }
+ else {
+ //usage adjustment
+ p->cpu_usage = (1.0-ALFA) * p->cpu_usage + ALFA * sample;
+ }
+ p->cputime = tmp_process.cputime;
+ }
+ }
+ }
+ close_process_iterator(&it);
+ if (dt < MIN_DT) return;
+ pgroup->last_update = now;
+}
diff --git a/src/process_group.h b/src/process_group.h
new file mode 100644
index 0000000..100711d
--- /dev/null
+++ b/src/process_group.h
@@ -0,0 +1,53 @@
+/**
+ *
+ * cpulimit - a cpu limiter for Linux
+ *
+ * Copyright (C) 2005-2012, by: Angelo Marletta <marlonx80@hotmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __PROCESS_GROUP_H
+
+#define __PROCESS_GROUP_H
+
+#include "process_iterator.h"
+
+#include "list.h"
+
+#define PIDHASH_SZ 1024
+#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
+
+struct process_group
+{
+ //hashtable with all the processes (array of struct list of struct process)
+ struct list *proctable[PIDHASH_SZ];
+ struct list *proclist;
+ pid_t target_pid;
+ int include_children;
+ struct timeval last_update;
+};
+
+int init_process_group(struct process_group *pgroup, int target_pid, int include_children);
+
+void update_process_group(struct process_group *pgroup);
+
+int close_process_group(struct process_group *pgroup);
+
+int find_process_by_pid(pid_t pid);
+
+int find_process_by_name(const char *process_name);
+
+#endif
diff --git a/src/process_iterator.c b/src/process_iterator.c
new file mode 100644
index 0000000..0e31c81
--- /dev/null
+++ b/src/process_iterator.c
@@ -0,0 +1,26 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/procfs.h>
+#include <time.h>
+#include "process_iterator.h"
+
+//See this link to port to other systems: http://www.steve.org.uk/Reference/Unix/faq_8.html#SEC85
+
+#ifdef __linux__
+
+#include "process_iterator_linux.c"
+
+#elif defined __FreeBSD__
+
+#include "process_iterator_freebsd.c"
+
+#elif defined __APPLE__
+
+#include "process_iterator_apple.c"
+
+#else
+
+#error Platform not supported
+
+#endif
diff --git a/src/process.h b/src/process_iterator.h
index 0280ad6..82fadac 100644
--- a/src/process.h
+++ b/src/process_iterator.h
@@ -2,7 +2,7 @@
*
* cpulimit - a cpu limiter for Linux
*
- * Copyright (C) 2005-2008, by: Angelo Marletta <marlonx80@hotmail.com>
+ * Copyright (C) 2005-2012, by: Angelo Marletta <marlonx80@hotmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -19,13 +19,13 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#ifndef __PROCESS_H
+#ifndef __PROCESS_ITERATOR_H
-#define __PROCESS_H
+#define __PROCESS_ITERATOR_H
-#include <sys/time.h>
#include <unistd.h>
#include <limits.h>
+#include <dirent.h>
//USER_HZ detection, from openssl code
#ifndef HZ
@@ -45,44 +45,51 @@
# endif
#endif
+#ifdef __FreeBSD__
+#include <kvm.h>
+#endif
+
// process descriptor
struct process {
//pid of the process
pid_t pid;
- //pid of the process
+ //ppid of the process
pid_t ppid;
- //start time
+ //start time (unix timestamp)
int starttime;
- //is member of the family?
- int member; //TODO: delete this field
- //total number of jiffies used by the process at time last_sample
- int last_jiffies;
- //timestamp when last_jiffies and cpu_usage was calculated
- struct timeval last_sample;
+ //cputime used by the process (in milliseconds)
+ int cputime;
//actual cpu usage estimation (value in range 0-1)
double cpu_usage;
- //1 if the process is zombie
- int is_zombie;
//absolute path of the executable file
char command[PATH_MAX+1];
+};
- //system-dependent members
-//TODO: delete these members for the sake of portability?
-#ifdef __linux__
- //preallocate buffers for performance
- //name of /proc/PID/stat file
- char stat_file[20];
- //read buffer for /proc filesystem
- char buffer[1024];
-#endif
+struct process_filter {
+ int pid;
+ int include_children;
+ char program_name[PATH_MAX+1];
};
-int get_proc_info(struct process *p, pid_t pid);
+struct process_iterator {
+#ifdef __linux__
+ DIR *dip;
+ int boot_time;
+#elif defined __FreeBSD__
+ kvm_t *kd;
+ struct kinfo_proc *procs;
+ int count;
+ int i;
+#elif defined __APPLE__
+ struct kinfo_proc *proclist;
+#endif
+ struct process_filter *filter;
+};
-int process_init(struct process *proc, pid_t pid);
+int init_process_iterator(struct process_iterator *i, struct process_filter *filter);
-int process_monitor(struct process *proc);
+int get_next_process(struct process_iterator *i, struct process *p);
-int process_close(struct process *proc);
+int close_process_iterator(struct process_iterator *i);
#endif
diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c
new file mode 100644
index 0000000..ee574e7
--- /dev/null
+++ b/src/process_iterator_apple.c
@@ -0,0 +1,77 @@
+int init_process_iterator(struct process_iterator *it) {
+ return 0;
+}
+
+int get_next_process(struct process_iterator *it, struct process *p) {
+ return -1;
+}
+
+int close_process_iterator(struct process_iterator *it) {
+ return 0;
+}
+
+ // int err;
+ // struct kinfo_proc *result = NULL;
+ // size_t length;
+ // int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
+
+ // /* We start by calling sysctl with result == NULL and length == 0.
+ // That will succeed, and set length to the appropriate length.
+ // We then allocate a buffer of that size and call sysctl again
+ // with that buffer.
+ // */
+ // length = 0;
+ // err = sysctl(mib, 4, NULL, &length, NULL, 0);
+ // if (err == -1) {
+ // err = errno;
+ // }
+ // if (err == 0) {
+ // result = malloc(length);
+ // err = sysctl(mib, 4, result, &length, NULL, 0);
+ // if (err == -1)
+ // err = errno;
+ // if (err == ENOMEM) {
+ // free(result); /* clean up */
+ // result = NULL;
+ // }
+ // }
+
+ // i->proclist = result;
+ // i->count = err == 0 ? length / sizeof *result : 0;
+ // i->c = 0;
+
+// int get_proc_info(struct process *p, pid_t pid) {
+// int err;
+// struct kinfo_proc *result = NULL;
+// size_t length;
+// int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+
+// /* We start by calling sysctl with result == NULL and length == 0.
+// That will succeed, and set length to the appropriate length.
+// We then allocate a buffer of that size and call sysctl again
+// with that buffer.
+// */
+// length = 0;
+// err = sysctl(mib, 4, NULL, &length, NULL, 0);
+// if (err == -1) {
+// err = errno;
+// }
+// if (err == 0) {
+// result = malloc(length);
+// err = sysctl(mib, 4, result, &length, NULL, 0);
+// if (err == -1)
+// err = errno;
+// if (err == ENOMEM) {
+// free(result); /* clean up */
+// result = NULL;
+// }
+// }
+
+// p->pid = result->kp_proc.p_pid;
+// p->ppid = result->kp_eproc.e_ppid;
+// p->starttime = result->kp_proc.p_starttime.tv_sec;
+// p->last_jiffies = result->kp_proc.p_cpticks;
+// //p_pctcpu
+
+// return 0;
+// }
diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c
new file mode 100644
index 0000000..eb6cfaa
--- /dev/null
+++ b/src/process_iterator_freebsd.c
@@ -0,0 +1,95 @@
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <fcntl.h>
+#include <paths.h>
+
+int init_process_iterator(struct process_iterator *it, struct process_filter *filter) {
+ char errbuf[_POSIX2_LINE_MAX];
+ it->i = 0;
+ /* Open the kvm interface, get a descriptor */
+ if ((it->kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf)) == NULL) {
+ fprintf(stderr, "kvm_open: %s\n", errbuf);
+ return -1;
+ }
+ /* Get the list of processes. */
+ if ((it->procs = kvm_getprocs(it->kd, KERN_PROC_PROC, 0, &it->count)) == NULL) {
+ kvm_close(it->kd);
+ fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(it->kd));
+ return -1;
+ }
+ it->filter = filter;
+ return 0;
+}
+
+static void kproc2proc(kvm_t *kd, struct kinfo_proc *kproc, struct process *proc)
+{
+ proc->pid = kproc->ki_pid;
+ proc->ppid = kproc->ki_ppid;
+ proc->cputime = kproc->ki_runtime / 1000;
+ proc->starttime = kproc->ki_start.tv_sec;
+ char **args = kvm_getargv(kd, kproc, sizeof(proc->command));
+ if (args != NULL)
+ {
+ memcpy(proc->command, args[0], strlen(args[0]) + 1);
+ }
+}
+
+static int get_single_process(kvm_t *kd, pid_t pid, struct process *process)
+{
+ int count;
+ struct kinfo_proc *kproc = kvm_getprocs(kd, KERN_PROC_PID, pid, &count);
+ if (count == 0 || kproc == NULL)
+ {
+ fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(kd));
+ return -1;
+ }
+ kproc2proc(kd, kproc, process);
+ return 0;
+}
+
+int get_next_process(struct process_iterator *it, struct process *p) {
+ if (it->i == it->count)
+ {
+ return -1;
+ }
+ if (it->filter->pid > 0 && !it->filter->include_children)
+ {
+ get_single_process(it->kd, it->filter->pid, p);
+ it->i = it->count = 1;
+ return 0;
+ }
+ while (it->i < it->count)
+ {
+ struct kinfo_proc *kproc = &(it->procs[it->i]);
+ if (kproc->ki_flag & P_SYSTEM)
+ {
+ // skip system processes
+ it->i++;
+ continue;
+ }
+ if (it->filter->pid > 0 && it->filter->include_children)
+ {
+ kproc2proc(it->kd, kproc, p);
+ it->i++;
+ if (p->pid != it->filter->pid && p->ppid != it->filter->pid)
+ continue;
+ return 0;
+ }
+ else if (it->filter->pid == 0)
+ {
+ kproc2proc(it->kd, kproc, p);
+ it->i++;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int close_process_iterator(struct process_iterator *it) {
+ if (kvm_close(it->kd) == -1) {
+ fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(it->kd));
+ return -1;
+ }
+ return 0;
+}
+
diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c
new file mode 100644
index 0000000..a15be45
--- /dev/null
+++ b/src/process_iterator_linux.c
@@ -0,0 +1,159 @@
+#include <sys/vfs.h>
+
+static int get_boot_time()
+{
+ int uptime;
+ FILE *fp = fopen ("/proc/uptime", "r");
+ if (fp != NULL)
+ {
+ char buf[BUFSIZ];
+ char *b = fgets(buf, BUFSIZ, fp);
+ if (b == buf)
+ {
+ char *end_ptr;
+ double upsecs = strtod(buf, &end_ptr);
+ uptime = (int)upsecs;
+ }
+ fclose (fp);
+ }
+ time_t now = time(NULL);
+ return now - uptime;
+}
+
+static int check_proc()
+{
+ struct statfs mnt;
+ if (statfs("/proc", &mnt) < 0)
+ return 0;
+ if (mnt.f_type!=0x9fa0)
+ return 0;
+ return 1;
+}
+
+int init_process_iterator(struct process_iterator *it, struct process_filter *filter)
+{
+ if (!check_proc()) {
+ fprintf(stderr, "procfs is not mounted!\nAborting\n");
+ exit(-2);
+ }
+ //open a directory stream to /proc directory
+ if ((it->dip = opendir("/proc")) == NULL)
+ {
+ perror("opendir");
+ return -1;
+ }
+ it->filter = filter;
+ it->boot_time = get_boot_time();
+ return 0;
+}
+
+static int read_process_info(pid_t pid, struct process *p)
+{
+ static char buffer[1024];
+ static char statfile[32];
+ static char exefile[1024];
+ p->pid = pid;
+ //read stat file
+ sprintf(statfile, "/proc/%d/stat", p->pid);
+ FILE *fd = fopen(statfile, "r");
+ if (fd==NULL) return -1;
+ if (fgets(buffer, sizeof(buffer), fd)==NULL) {
+ fclose(fd);
+ return -1;
+ }
+ fclose(fd);
+ char *token = strtok(buffer, " ");
+ int i;
+ for (i=0; i<3; i++) token = strtok(NULL, " ");
+ p->ppid = atoi(token);
+ for (i=0; i<10; i++)
+ token = strtok(NULL, " ");
+ p->cputime = atoi(token) * 1000 / HZ;
+ token = strtok(NULL, " ");
+ p->cputime += atoi(token) * 1000 / HZ;
+ for (i=0; i<7; i++)
+ token = strtok(NULL, " ");
+ p->starttime = atoi(token) / sysconf(_SC_CLK_TCK);
+ //read command line
+ sprintf(exefile,"/proc/%d/cmdline", p->pid);
+ fd = fopen(exefile, "r");
+ if (fgets(buffer, sizeof(buffer), fd)==NULL) {
+ fclose(fd);
+ return -1;
+ }
+ fclose(fd);
+ sscanf(buffer, "%s", p->command);
+ return 0;
+}
+
+static pid_t getppid_of(pid_t pid)
+{
+ char statfile[20];
+ char buffer[1024];
+ sprintf(statfile, "/proc/%d/stat", pid);
+ FILE *fd = fopen(statfile, "r");
+ if (fd==NULL) return -1;
+ if (fgets(buffer, sizeof(buffer), fd)==NULL) {
+ fclose(fd);
+ return -1;
+ }
+ fclose(fd);
+ char *token = strtok(buffer, " ");
+ int i;
+ for (i=0; i<3; i++) token = strtok(NULL, " ");
+ return atoi(token);
+}
+
+static int is_child_of(pid_t child_pid, pid_t parent_pid)
+{
+ int ppid = child_pid;
+ while(ppid > 1 && ppid != parent_pid) {
+ ppid = getppid_of(ppid);
+ }
+ return ppid == parent_pid;
+}
+
+int get_next_process(struct process_iterator *it, struct process *p)
+{
+ if (it->dip == NULL)
+ {
+ //end of processes
+ return -1;
+ }
+ if (it->filter->pid > 0 && !it->filter->include_children)
+ {
+ read_process_info(it->filter->pid, p);
+ //p->starttime += it->boot_time;
+ closedir(it->dip);
+ it->dip = NULL;
+ return 0;
+ }
+ struct dirent *dit;
+ //read in from /proc and seek for process dirs
+ while ((dit = readdir(it->dip)) != NULL) {
+ if(strtok(dit->d_name, "0123456789") != NULL)
+ continue;
+ p->pid = atoi(dit->d_name);
+ if (it->filter->pid > 0 && it->filter->pid != p->pid && !is_child_of(p->pid, it->filter->pid)) continue;
+ read_process_info(p->pid, p);
+ //p->starttime += it->boot_time;
+ break;
+ }
+ if (dit == NULL)
+ {
+ //end of processes
+ closedir(it->dip);
+ it->dip = NULL;
+ return -1;
+ }
+ return 0;
+}
+
+int close_process_iterator(struct process_iterator *it) {
+ if (it->dip != NULL && closedir(it->dip) == -1) {
+ perror("closedir");
+ return 1;
+ }
+ it->dip = NULL;
+ return 0;
+}
diff --git a/src/procutils.c b/src/procutils.c
deleted file mode 100644
index fb7aaae..0000000
--- a/src/procutils.c
+++ /dev/null
@@ -1,463 +0,0 @@
-/**
- *
- * cpulimit - a cpu limiter for Linux
- *
- * Copyright (C) 2005-2008, by: Angelo Marletta <marlonx80@hotmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <signal.h>
-#include <string.h>
-#include <limits.h>
-#include <fcntl.h>
-#include <sys/utsname.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include "procutils.h"
-
-#ifdef __APPLE__
-#include <sys/sysctl.h>
-#include <errno.h>
-#endif
-
-/* PROCESS STATISTICS FUNCTIONS */
-
-//deprecated
-// returns pid of the parent process
-static pid_t getppid_of(pid_t pid)
-{
-#ifdef __linux__
- char file[20];
- char buffer[1024];
- sprintf(file, "/proc/%d/stat", pid);
- FILE *fd = fopen(file, "r");
- if (fd==NULL) return -1;
- if (fgets(buffer, sizeof(buffer), fd)==NULL) return -1;
- fclose(fd);
- char *p = buffer;
- p = memchr(p+1,')', sizeof(buffer) - (p-buffer));
- int sp = 2;
- while (sp--)
- p = memchr(p+1,' ',sizeof(buffer) - (p-buffer));
- //pid of the parent process
- pid_t ppid = atoi(p+1);
- return ppid;
-#elif defined __APPLE__
- struct process p;
- get_proc_info(&p, pid);
- return p.ppid;
-#endif
-}
-
-#ifdef __linux__
-// detects whether a process is a kernel thread or not
-static int is_kernel_thread(pid_t pid)
-{
- static char statfile[20];
- static char buffer[64];
- int ret;
- sprintf(statfile, "/proc/%d/statm", pid);
- FILE *fd = fopen(statfile, "r");
- if (fd==NULL) return -1;
- if (fgets(buffer, sizeof(buffer), fd)==NULL) return -1;
- ret = strncmp(buffer,"0 0 0",3)==0;
- fclose(fd);
- return ret;
-}
-#endif
-
-//deprecated
-// returns 1 if pid is an existing pid, 0 otherwise
-static int process_exists(pid_t pid) {
-#ifdef __linux__
- static char procdir[20];
- struct stat procstat;
- sprintf(procdir, "/proc/%d", pid);
- return stat(procdir, &procstat)==0;
-#elif defined __APPLE__
- struct process p;
- return get_proc_info(&p, pid)==0;
-#endif
-}
-
-/* PID HASH FUNCTIONS */
-
-static int hash_process(struct process_family *f, struct process *p)
-{
- int ret;
- struct list **l = &(f->proctable[pid_hashfn(p->pid)]);
- if (*l == NULL) {
- //there is no process in this hashtable item
- //allocate the list
- *l = (struct list*)malloc(sizeof(struct list));
- init_list(*l, 4);
- add_elem(*l, p);
- ret = 0;
- f->count++;
- }
- else {
- //list already exists
- struct process *tmp = (struct process*)locate_elem(*l, p);
- if (tmp != NULL) {
- //update process info
- memcpy(tmp, p, sizeof(struct process));
- free(p);
- p = NULL;
- ret = 1;
- }
- else {
- //add new process
- add_elem(*l, p);
- ret = 0;
- f->count++;
- }
- }
- return ret;
-}
-
-static void unhash_process(struct process_family *f, pid_t pid) {
- //remove process from hashtable
- struct list **l = &(f->proctable[pid_hashfn(pid)]);
- if (*l == NULL)
- return; //nothing done
- struct list_node *node = locate_node(*l, &pid);
- if (node != NULL)
- destroy_node(*l, node);
- f->count--;
-}
-
-static struct process *seek_process(struct process_family *f, pid_t pid)
-{
- struct list **l = &(f->proctable[pid_hashfn(pid)]);
- return (*l != NULL) ? (struct process*)locate_elem(*l, &pid) : NULL;
-}
-
-/* PROCESS ITERATOR STUFF */
-
-// creates an object that browse all running processes
-int init_process_iterator(struct process_iterator *i) {
-#ifdef __linux__
- //open a directory stream to /proc directory
- if ((i->dip = opendir("/proc")) == NULL) {
- perror("opendir");
- return -1;
- }
-#elif defined __APPLE__
-
- int err;
- struct kinfo_proc *result = NULL;
- size_t length;
- int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
-
- /* We start by calling sysctl with result == NULL and length == 0.
- That will succeed, and set length to the appropriate length.
- We then allocate a buffer of that size and call sysctl again
- with that buffer.
- */
- length = 0;
- err = sysctl(mib, 4, NULL, &length, NULL, 0);
- if (err == -1) {
- err = errno;
- }
- if (err == 0) {
- result = malloc(length);
- err = sysctl(mib, 4, result, &length, NULL, 0);
- if (err == -1)
- err = errno;
- if (err == ENOMEM) {
- free(result); /* clean up */
- result = NULL;
- }
- }
-
- i->procList = result;
- i->count = err == 0 ? length / sizeof *result : 0;
- i->c = 0;
-
-#endif
- i->current = (struct process*)calloc(1, sizeof(struct process));
- return 0;
-}
-
-int close_process_iterator(struct process_iterator *i) {
-#ifdef __linux__
- if (closedir(i->dip) == -1) {
- perror("closedir");
- return 1;
- }
-#elif defined __APPLE__
- //TODO
-#endif
- return 0;
-}
-
-// reads the next user process from /process
-// automatic closing if the end of the list is reached
-int read_next_process(struct process_iterator *i) {
-#ifdef __linux__
- pid_t pid = 0;
-//TODO read this to port to other systems: http://www.steve.org.uk/Reference/Unix/faq_8.html#SEC85
- //read in from /proc and seek for process dirs
- while ((i->dit = readdir(i->dip)) != NULL) {
- if(strtok(i->dit->d_name, "0123456789") != NULL)
- continue;
- pid = atoi(i->dit->d_name);
- if (is_kernel_thread(pid))
- continue;
- //return the first found process
- break;
- }
- if (pid == 0) {
- //no more processes
- closedir(i->dip);
- free(i->current);
- i->current = NULL;
- return -1;
- }
- //read the executable link
- char statfile[20];
- sprintf(statfile,"/proc/%d/cmdline",pid);
- FILE *fd = fopen(statfile, "r");
- if (fd == NULL) return -1;
- char buffer[1024];
- if (fgets(buffer, sizeof(buffer), fd)==NULL) return -2;
- fclose(fd);
- sscanf(buffer, "%s", (char*)&i->current->command);
- i->current->pid = pid;
-
-#elif defined __APPLE__
- if (i->c >= i->count) {
- //no more processes
- free(i->procList);
- i->procList = NULL;
- free(i->current);
- i->current = NULL;
- return -1;
- }
- i->current->pid = i->procList[i->c].kp_proc.p_pid;
- strncpy(i->current->command, i->procList[i->c].kp_proc.p_comm, MAXCOMLEN);
-printf("%d %d %s\n", i->c, i->current->pid, i->current->command);//i->procList[i->c].kp_proc.p_comm);
-//printf("%d %d %s\n", i->c, i->current->pid, i->proc[i->c].kp_proc.p_comm);
- i->c++;
-#endif
- return 0;
-}
-
-/* PUBLIC FUNCTIONS */
-
-// search for all the processes derived from father and stores them
-// in the process family struct
-int create_process_family(struct process_family *f, pid_t father)
-{
- //process list initialization (4 bytes key)
- init_list(&(f->members), 4);
- //hashtable initialization
- memset(&(f->proctable), 0, sizeof(f->proctable));
- f->count = 0;
- f->father = father;
- //process iterator
- struct process_iterator iter;
- init_process_iterator(&iter);
- int pid = 0;
- while (read_next_process(&iter)==0) {
- pid = iter.current->pid;
- //check if process belongs to the family
- int ppid = pid;
- //TODO: optimize adding also these parents, and continue if process is already present
- while(ppid!=1 && ppid!=father) {
- ppid = getppid_of(ppid);
- }
- //allocate process descriptor
- struct process *p = (struct process*)calloc(1, sizeof(struct process));
- //init process
- process_init(p, pid);
- if (ppid==1) {
- //the init process
- p->member = 0;
- }
- else if (pid != getpid()) {
- //add to members (but exclude the current cpulimit process!)
- p->member = 1;
- add_elem(&(f->members), p);
- }
- //add to hashtable
- hash_process(f, p);
- }
- return 0;
-}
-
-// checks if there are new processes born in the specified family
-// if any they are added to the members list
-// the number of new born processes is returned
-int update_process_family(struct process_family *f)
-{
- int ret = 0;
- //process iterator
- struct process_iterator iter;
- init_process_iterator(&iter);
- int pid = 0;
- while (read_next_process(&iter)==0) {
- pid = iter.current->pid;
- struct process *newp = seek_process(f, pid);
- if (newp != NULL) continue; //already known //TODO: what if newp is a new process with the same PID??
- //the process is new, check if it belongs to the family
- int ppid = getppid_of(pid);
- //search the youngest known ancestor of the process
- struct process *ancestor = NULL;
- while((ancestor=seek_process(f, ppid))==NULL) {
- ppid = getppid_of(ppid);
- }
- if (ancestor == NULL) {
- //this should never happen! if does, find and correct the bug
- fprintf(stderr, "Fatal bug! Process %d is without parent\n", pid);
- exit(1);
- }
- //allocate and insert the process
- struct process *p = (struct process*)calloc(1, sizeof(struct process));
- //init process
- process_init(p, pid);
- if (ancestor->member) {
- //add to members
- p->member = 1;
- add_elem(&(f->members), p);
- ret++;
- }
- else {
- //not a member
- p->member = 0;
- }
- //add to hashtable
- hash_process(f, p);
- }
- return ret;
-}
-
-// removes a process from the family by its pid
-void remove_process_from_family(struct process_family *f, pid_t pid)
-{
- struct list_node *node = locate_node(&(f->members), &pid);
- if (node != NULL) {
-// struct process *p = (struct process*)(node->data);
-// free(p->history);
-// p->history = NULL;
- delete_node(&(f->members), node);
- }
- unhash_process(f, pid);
-}
-
-// free the heap memory used by a process family
-void cleanup_process_family(struct process_family *f)
-{
- int i;
- int size = sizeof(f->proctable) / sizeof(struct process*);
- for (i=0; i<size; i++) {
- if (f->proctable[i] != NULL) {
- //free() history for each process
- struct list_node *node = NULL;
- for (node=f->proctable[i]->first; node!=NULL; node=node->next) {
-// struct process *p = (struct process*)(node->data);
-// free(p->history);
-// p->history = NULL;
- }
- destroy_list(f->proctable[i]);
- free(f->proctable[i]);
- f->proctable[i] = NULL;
- }
- }
- flush_list(&(f->members));
- f->count = 0;
- f->father = 0;
-}
-
-// look for a process by pid
-// search_pid : pid of the wanted process
-// return: pid of the found process, if it is found
-// 0, if it's not found
-// negative pid, if it is found but it's not possible to control it
-int look_for_process_by_pid(pid_t pid)
-{
- if (process_exists(pid))
- return (kill(pid,0)==0) ? pid : -pid;
- return 0;
-}
-
-// look for a process with a given name
-// process: the name of the wanted process. it can be an absolute path name to the executable file
-// or just the file name
-// return: pid of the found process, if it is found
-// 0, if it's not found
-// negative pid, if it is found but it's not possible to control it
-int look_for_process_by_name(const char *process_name)
-{
- //whether the variable process_name is the absolute path or not
- int is_absolute_path = process_name[0] == '/';
- //flag indicating if the a process with given name was found
- int found = 0;
-
- //process iterator
- struct process_iterator iter;
- init_process_iterator(&iter);
- pid_t pid = 0;
-
- while (read_next_process(&iter)==0) {
- pid = iter.current->pid;
-
- int size = strlen(iter.current->command);
-
- found = 0;
- if (is_absolute_path && strncmp(iter.current->command, process_name, size)==0 && size==strlen(process_name)) {
- //process found
- found = 1;
- }
- else {
- //process found
- if (strncmp(iter.current->command + size - strlen(process_name), process_name, strlen(process_name))==0) {
- found = 1;
- }
- }
- if (found==1) {
- if (kill(pid,SIGCONT)==0) {
- //process is ok!
- break;
- }
- else {
- //we don't have permission to send signal to that process
- //so, don't exit from the loop and look for another one with the same name
- found = -1;
- }
- }
- }
- if (close_process_iterator(&iter) != 1) exit(1);
- if (found == 1) {
- //ok, the process was found
- return pid;
- }
- else if (found == 0) {
- //no process found
- return 0;
- }
- else if (found == -1) {
- //the process was found, but we haven't permission to control it
- return -pid;
- }
- //this MUST NOT happen
- exit(2);
-}
-
diff --git a/src/procutils.h b/src/procutils.h
index b5a91a3..c39a239 100644
--- a/src/procutils.h
+++ b/src/procutils.h
@@ -62,7 +62,7 @@ struct process_iterator {
struct kinfo_proc *procList;
int count;
int c;
-#elif defined __hpux
+#elif defined __FreeBSD__
int count;
#endif
struct process *current;
diff --git a/tests/Makefile b/tests/Makefile
index 11f7e2a..ffc80fa 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -1,12 +1,22 @@
CC?=gcc
-CFLAGS?=-Wall -O2
-TARGETS=busy
-LIBS?=-lpthread
+CFLAGS?=-Wall -g
+TARGETS=busy process_iterator_test
+SRC=../src
+SYSLIBS?=-lpthread
+LIBS=$(SRC)/list.o $(SRC)/process_iterator.o $(SRC)/process_group.o
+UNAME := $(shell uname)
+
+ifeq ($(UNAME), FreeBSD)
+LIBS+=-lkvm
+endif
all:: $(TARGETS)
-busy: busy.c $(LIBS)
- $(CC) -o busy busy.c $(LIBS) $(CFLAGS)
+busy: busy.c $(SYSLIBS)
+ $(CC) -o busy busy.c $(SYSLIBS) $(CFLAGS)
+
+process_iterator_test: process_iterator_test.c $(LIBS)
+ $(CC) -I$(SRC) -o process_iterator_test process_iterator_test.c $(LIBS) $(SYSLIBS) $(CFLAGS)
clean:
rm -f *~ *.o $(TARGETS)
diff --git a/tests/busy b/tests/busy
deleted file mode 100755
index 1a8a589..0000000
--- a/tests/busy
+++ /dev/null
Binary files differ
diff --git a/tests/busy.c b/tests/busy.c
index bb2e2ed..b3afb7c 100644
--- a/tests/busy.c
+++ b/tests/busy.c
@@ -13,7 +13,7 @@ int main(int argc, char **argv) {
int i = 0;
int num_threads = 1;
if (argc == 2) num_threads = atoi(argv[1]);
- for (i=0; i<num_threads; i++)
+ for (i=0; i<num_threads-1; i++)
{
pthread_t thread;
int ret;
@@ -23,7 +23,7 @@ int main(int argc, char **argv) {
exit(1);
}
}
- printf("Press ENTER to exit...");
- getchar();
+ loop();
return 0;
-} \ No newline at end of file
+}
+
diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c
new file mode 100644
index 0000000..f5b7586
--- /dev/null
+++ b/tests/process_iterator_test.c
@@ -0,0 +1,196 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+#include <time.h>
+#include <signal.h>
+#include <string.h>
+
+#include <process_iterator.h>
+#include <process_group.h>
+
+volatile sig_atomic_t child;
+
+void kill_child(int sig)
+{
+ kill(child, SIGINT);
+}
+
+void test_single_process()
+{
+ struct process_iterator it;
+ struct process process;
+ struct process_filter filter;
+ int count;
+ //don't iterate children
+ filter.pid = getpid();
+ filter.include_children = 0;
+ count = 0;
+// time_t now = time(NULL);
+ init_process_iterator(&it, &filter);
+ while (get_next_process(&it, &process) == 0)
+ {
+ assert(process.pid == getpid());
+ assert(process.ppid == getppid());
+ assert(process.cputime < 100);
+// assert(process.starttime == now || process.starttime == now - 1);
+ count++;
+ }
+ assert(count == 1);
+ close_process_iterator(&it);
+ //iterate children
+ filter.pid = getpid();
+ filter.include_children = 0;
+ count = 0;
+// now = time(NULL);
+ init_process_iterator(&it, &filter);
+ while (get_next_process(&it, &process) == 0)
+ {
+ assert(process.pid == getpid());
+ assert(process.ppid == getppid());
+ assert(process.cputime < 100);
+// assert(process.starttime == now || process.starttime == now - 1);
+ count++;
+ }
+ assert(count == 1);
+ close_process_iterator(&it);
+}
+
+void test_multiple_process()
+{
+ struct process_iterator it;
+ struct process process;
+ struct process_filter filter;
+ pid_t child = fork();
+ if (child == 0)
+ {
+ //child is supposed to be killed by the parent :/
+ sleep(1);
+ exit(1);
+ }
+ filter.pid = getpid();
+ filter.include_children = 1;
+ init_process_iterator(&it, &filter);
+ int count = 0;
+// time_t now = time(NULL);
+ while (get_next_process(&it, &process) == 0)
+ {
+ if (process.pid == getpid()) assert(process.ppid == getppid());
+ else if (process.pid == child) assert(process.ppid == getpid());
+ else assert(0);
+ assert(process.cputime < 100);
+// assert(process.starttime == now || process.starttime == now - 1);
+ count++;
+ }
+ assert(count == 2);
+ close_process_iterator(&it);
+ kill(child, SIGINT);
+}
+
+void test_all_processes()
+{
+ struct process_iterator it;
+ struct process process;
+ struct process_filter filter;
+ filter.pid = 0;
+ filter.include_children = 0;
+ init_process_iterator(&it, &filter);
+ int count = 0;
+// time_t now = time(NULL);
+ while (get_next_process(&it, &process) == 0)
+ {
+ if (process.pid == getpid())
+ {
+ assert(process.ppid == getppid());
+ assert(process.cputime < 100);
+// assert(process.starttime == now || process.starttime == now - 1);
+ }
+ count++;
+ }
+ assert(count >= 10);
+ close_process_iterator(&it);
+}
+
+void test_process_group_all()
+{
+ struct process_group pgroup;
+ assert(init_process_group(&pgroup, 0, 0) == 0);
+ update_process_group(&pgroup);
+ struct list_node *node = NULL;
+ int count = 0;
+ for (node=pgroup.proclist->first; node!= NULL; node=node->next) {
+ count++;
+ }
+ assert(count > 10);
+ update_process_group(&pgroup);
+ assert(close_process_group(&pgroup) == 0);
+}
+
+void test_process_group_single(int include_children)
+{
+ struct process_group pgroup;
+ child = fork();
+ if (child == 0)
+ {
+ //child is supposed to be killed by the parent :/
+ while(1);
+ exit(1);
+ }
+ signal(SIGABRT, &kill_child);
+ signal(SIGTERM, &kill_child);
+ assert(init_process_group(&pgroup, child, include_children) == 0);
+ int i;
+ double tot_usage = 0;
+ for (i=0; i<100; i++)
+ {
+ update_process_group(&pgroup);
+ struct list_node *node = NULL;
+ int count = 0;
+ for (node=pgroup.proclist->first; node!= NULL; node=node->next) {
+ struct process *p = (struct process*)(node->data);
+ assert(p->pid == child);
+ assert(p->ppid == getpid());
+ assert(p->cpu_usage <= 1.2);
+ tot_usage += p->cpu_usage;
+ count++;
+ }
+ assert(count == 1);
+ struct timespec interval;
+ interval.tv_sec = 0;
+ interval.tv_nsec = 50000000;
+ nanosleep(&interval, NULL);
+ }
+ assert(tot_usage / i < 1.1 && tot_usage / i > 0.8);
+ assert(close_process_group(&pgroup) == 0);
+ kill(child, SIGINT);
+}
+
+void test_process_name(const char * command)
+{
+ struct process_iterator it;
+ struct process process;
+ struct process_filter filter;
+ filter.pid = getpid();
+ filter.include_children = 0;
+ init_process_iterator(&it, &filter);
+ assert(get_next_process(&it, &process) == 0);
+ assert(process.pid == getpid());
+ assert(process.ppid == getppid());
+ assert(strncmp(command, process.command, strlen(process.command)) == 0);
+ assert(get_next_process(&it, &process) != 0);
+ close_process_iterator(&it);
+}
+
+int main(int argc, char **argv)
+{
+// printf("Pid %d\n", getpid());
+ test_single_process();
+ test_multiple_process();
+ test_all_processes();
+ test_process_group_all();
+ test_process_group_single(0);
+ test_process_group_single(1);
+ test_process_name(argv[0]);
+ return 0;
+}
Un proyecto texto-plano.xyz