From ef0d2e6eef63670d505ba6dd41ff7414395031bd Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 17 Apr 2022 22:44:51 +0800 Subject: [PATCH 001/209] update code --- Makefile | 4 +- src/.gitignore | 3 + src/Makefile | 36 +-- src/cpulimit.c | 486 ++++++++++++++++++--------------- src/list.c | 158 ++++++----- src/list.h | 44 +-- src/memrchr.c | 38 --- src/process_group.c | 107 +++++--- src/process_group.h | 4 +- src/process_iterator.c | 10 +- src/process_iterator.h | 65 ++--- src/process_iterator_apple.c | 95 ++++--- src/process_iterator_freebsd.c | 30 +- src/process_iterator_linux.c | 173 ++++++++---- .gitignore => tests/.gitignore | 1 - tests/Makefile | 25 +- tests/process_iterator_test.c | 3 - 17 files changed, 720 insertions(+), 562 deletions(-) create mode 100644 src/.gitignore delete mode 100644 src/memrchr.c rename .gitignore => tests/.gitignore (72%) diff --git a/Makefile b/Makefile index 7a467380..bc552e74 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ default: all .DEFAULT: - cd src && $(MAKE) $@ - cd tests && $(MAKE) $@ + $(MAKE) -C src $@ + $(MAKE) -C tests $@ diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 00000000..465c469b --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,3 @@ +*.o +*~ +cpulimit diff --git a/src/Makefile b/src/Makefile index 86fbfbd3..e8107a74 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,28 +1,32 @@ -CC?=gcc -CFLAGS?=-Wall -g -D_GNU_SOURCE -TARGETS=cpulimit -LIBS=list.o process_iterator.o process_group.o +CC ?= gcc +CFLAGS ?= -Wall +TARGET := cpulimit +OBJS := cpulimit.o list.o process_iterator.o process_group.o -UNAME := $(shell uname) +UNAME ?= $(shell uname) ifeq ($(UNAME), FreeBSD) -LIBS+=-lkvm + LDFLAGS += -lkvm endif -all:: $(TARGETS) $(LIBS) +.PHONY: all clean -cpulimit: cpulimit.c $(LIBS) - $(CC) -o cpulimit cpulimit.c $(LIBS) $(CFLAGS) +all: $(TARGET) -process_iterator.o: process_iterator.c process_iterator.h - $(CC) -c process_iterator.c $(CFLAGS) +$(TARGET): $(OBJS) + $(CC) $(LDFLAGS) $^ -o $@ + +cpulimit.o: cpulimit.c process_group.h list.h + $(CC) $(CFLAGS) -c $< -o $@ + +process_iterator.o: process_iterator.c process_iterator.h process_iterator_linux.c process_iterator_freebsd.c process_iterator_apple.c + $(CC) $(CFLAGS) -c $< -o $@ list.o: list.c list.h - $(CC) -c list.c $(CFLAGS) + $(CC) $(CFLAGS) -c $< -o $@ -process_group.o: process_group.c process_group.h process_iterator.o list.o - $(CC) -c process_group.c $(CFLAGS) +process_group.o: process_group.c process_group.h process_iterator.h list.h + $(CC) $(CFLAGS) -c $< -o $@ clean: - rm -f *~ *.o $(TARGETS) - + rm -f *~ $(OBJS) $(TARGET) diff --git a/src/cpulimit.c b/src/cpulimit.c index 50eabeac..75e81f98 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -2,7 +2,7 @@ * * cpulimit - a CPU limiter for Linux * - * Copyright (C) 2005-2012, by: Angelo Marletta + * Copyright (C) 2005-2012, by: Angelo Marletta * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -38,15 +38,11 @@ #include #include #include -#include +#include #include #include #include -#ifdef __APPLE__ || __FREEBSD__ -#include -#endif - #include "process_group.h" #include "list.h" @@ -54,65 +50,62 @@ #include #endif -#ifdef __APPLE__ -#include "memrchr.c" -#endif - -//some useful macro +// some useful macro #ifndef MIN -#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif #ifndef MAX -#define MAX(a,b) (((a)>(b))?(a):(b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif -//control time slot in microseconds -//each slot is splitted in a working slice and a sleeping slice -//TODO: make it adaptive, based on the actual system load +// control time slot in microseconds +// each slot is splitted in a working slice and a sleeping slice +// TODO: make it adaptive, based on the actual system load #define TIME_SLOT 100000 -#define MAX_PRIORITY -10 +#define MAX_PRIORITY -20 /* GLOBAL VARIABLES */ -//the "family" +// the "family" struct process_group pgroup; -//pid of cpulimit +// pid of cpulimit pid_t cpulimit_pid; -//name of this program (maybe cpulimit...) +// name of this program (maybe cpulimit...) char *program_name; -//number of cpu +// number of cpu int NCPU; /* CONFIGURATION VARIABLES */ -//verbose mode +// verbose mode int verbose = 0; -//lazy mode (exits if there is no process) +// lazy mode (exits if there is no process) int lazy = 0; -//SIGINT and SIGTERM signal handler +// SIGINT and SIGTERM signal handler static void quit(int sig) { - //let all the processes continue if stopped + // let all the processes continue if stopped struct list_node *node = NULL; if (pgroup.proclist != NULL) { - for (node = pgroup.proclist->first; node != NULL; node = node->next) { - struct process *p = (struct process*)(node->data); + for (node = pgroup.proclist->first; node != NULL; node = node->next) + { + struct process *p = (struct process *)(node->data); kill(p->pid, SIGCONT); } close_process_group(&pgroup); } - //fix ^C little problem + // fix ^C little problem printf("\r"); fflush(stdout); exit(0); } -//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-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); } @@ -121,7 +114,7 @@ static void print_usage(FILE *stream, int exit_code) { fprintf(stream, "Usage: %s [OPTIONS...] TARGET\n", program_name); fprintf(stream, " OPTIONS\n"); - fprintf(stream, " -l, --limit=N percentage of cpu allowed from 0 to %d (required)\n", 100*NCPU); + fprintf(stream, " -l, --limit=N percentage of cpu allowed from 0 to %d (required)\n", 100 * NCPU); fprintf(stream, " -v, --verbose show control statistics\n"); fprintf(stream, " -z, --lazy exit if there is no target process, or if it dies\n"); fprintf(stream, " -i, --include-children limit also the children processes\n"); @@ -134,31 +127,38 @@ static void print_usage(FILE *stream, int exit_code) exit(exit_code); } -static void increase_priority() { - //find the best available nice value +static void increase_priority() +{ + // find the best available nice value int old_priority = getpriority(PRIO_PROCESS, 0); int priority = old_priority; - while (setpriority(PRIO_PROCESS, 0, priority-1) == 0 && priority>MAX_PRIORITY) { - priority--; + while (setpriority(PRIO_PROCESS, 0, priority - 1) == 0 && priority > MAX_PRIORITY) + { + priority--; } - if (priority != old_priority) { - if (verbose) printf("Priority changed to %d\n", priority); + if (priority != old_priority) + { + if (verbose) + printf("Priority changed to %d\n", priority); } - else { - if (verbose) printf("Warning: Cannot change priority. Run as root or renice for best results.\n"); + else if (priority > MAX_PRIORITY) + { + if (verbose) + printf("Warning: Cannot change priority. Run as root or renice for best results.\n"); } } /* Get the number of CPUs */ -static int get_ncpu() { +static int get_ncpu() +{ int ncpu; -#ifdef _SC_NPROCESSORS_ONLN +#if defined(_SC_NPROCESSORS_ONLN) ncpu = sysconf(_SC_NPROCESSORS_ONLN); -#elif defined __APPLE__ +#elif defined(__APPLE__) int mib[2] = {CTL_HW, HW_NCPU}; size_t len = sizeof(ncpu); sysctl(mib, 2, &ncpu, &len, NULL, 0); -#elif defined _GNU_SOURCE +#elif defined(_GNU_SOURCE) ncpu = get_nprocs(); #else ncpu = -1; @@ -168,154 +168,173 @@ static int get_ncpu() { int get_pid_max() { -#ifdef __linux__ - //read /proc/sys/kernel/pid_max +#if defined(__linux__) + // read /proc/sys/kernel/pid_max static char buffer[1024]; FILE *fd = fopen("/proc/sys/kernel/pid_max", "r"); - if (fd==NULL) return -1; - if (fgets(buffer, sizeof(buffer), fd)==NULL) { + if (fd == NULL) + return -1; + if (fgets(buffer, sizeof(buffer), fd) == NULL) + { fclose(fd); return -1; } fclose(fd); return atoi(buffer); -#elif defined __FreeBSD__ +#elif defined(__FreeBSD__) return 99998; -#elif defined __APPLE__ +#elif defined(__APPLE__) return 99998; #endif } void limit_process(pid_t pid, double limit, int include_children) { - //slice of the slot in which the process is allowed to run + // slice of the slot in which the process is allowed to run struct timespec twork; - //slice of the slot in which the process is stopped + // slice of the slot in which the process is stopped struct timespec tsleep; - //when the last twork has started + // when the last twork has started struct timeval startwork; - //when the last twork has finished + // when the last twork has finished struct timeval endwork; - //initialization + // initialization memset(&twork, 0, sizeof(struct timespec)); memset(&tsleep, 0, sizeof(struct timespec)); memset(&startwork, 0, sizeof(struct timeval)); - memset(&endwork, 0, sizeof(struct timeval)); - //last working time in microseconds + memset(&endwork, 0, sizeof(struct timeval)); + // last working time in microseconds unsigned long workingtime = 0; - //generic list item + // generic list item struct list_node *node; - //counter + // counter int c = 0; - //get a better priority + // get a better priority increase_priority(); - - //build the family + + // build the family 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); + 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 + // 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) { + while (1) + { update_process_group(&pgroup); - if (pgroup.proclist->count==0) { - if (verbose) printf("No more processes.\n"); + if (pgroup.proclist->count == 0) + { + if (verbose) + printf("No more processes.\n"); break; } - - //total cpu actual usage (range 0-1) - //1 means that the processes are using 100% cpu + + // total cpu actual usage (range 0-1) + // 1 means that the processes are using 100% cpu double pcpu = -1; - //estimate how much the controlled processes are using the cpu in the working interval - for (node = pgroup.proclist->first; node != NULL; node = node->next) { - struct process *proc = (struct process*)(node->data); - if (proc->cpu_usage < 0) { + // estimate how much the controlled processes are using the cpu in the working interval + for (node = pgroup.proclist->first; node != NULL; node = node->next) + { + struct process *proc = (struct process *)(node->data); + if (proc->cpu_usage < 0) + { continue; } - if (pcpu < 0) pcpu = 0; + if (pcpu < 0) + pcpu = 0; pcpu += proc->cpu_usage; } - //adjust work and sleep time slices - if (pcpu < 0) { - //it's the 1st cycle, initialize workingrate + // adjust work and sleep time slices + if (pcpu < 0) + { + // it's the 1st cycle, initialize workingrate pcpu = limit; workingrate = limit; twork.tv_nsec = TIME_SLOT * limit * 1000; } - else { - //adjust workingrate + else + { + // adjust workingrate workingrate = MIN(workingrate / pcpu * limit, 1); twork.tv_nsec = TIME_SLOT * 1000 * workingrate; } tsleep.tv_nsec = TIME_SLOT * 1000 - twork.tv_nsec; - if (verbose) { - if (c%200==0) + 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); + 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); } - //resume processes + // resume processes node = pgroup.proclist->first; while (node != NULL) { struct list_node *next_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, "SIGCONT failed. Process %d dead!\n", proc->pid); - //remove process from group + struct process *proc = (struct process *)(node->data); + if (kill(proc->pid, SIGCONT) != 0) + { + // process is dead, remove it from family + if (verbose) + fprintf(stderr, "SIGCONT failed. Process %d dead!\n", proc->pid); + // remove process from group delete_node(pgroup.proclist, node); remove_process(&pgroup, proc->pid); } node = next_node; } - //now processes are free to run (same working slice for all) + // now processes are free to run (same working slice for all) gettimeofday(&startwork, NULL); nanosleep(&twork, NULL); gettimeofday(&endwork, NULL); workingtime = timediff(&endwork, &startwork); - - 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); + + 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 processes only if tsleep>0 + if (tsleep.tv_nsec > 0) + { + // stop processes only if tsleep>0 node = pgroup.proclist->first; while (node != NULL) { struct list_node *next_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, "SIGSTOP failed. Process %d dead!\n", proc->pid); - //remove process from group + struct process *proc = (struct process *)(node->data); + if (kill(proc->pid, SIGSTOP) != 0) + { + // process is dead, remove it from family + if (verbose) + fprintf(stderr, "SIGSTOP failed. Process %d dead!\n", proc->pid); + // remove process from group delete_node(pgroup.proclist, node); remove_process(&pgroup, proc->pid); } node = next_node; } - //now the processes are sleeping - nanosleep(&tsleep,NULL); + // now the processes are sleeping + nanosleep(&tsleep, NULL); } c++; } close_process_group(&pgroup); } -int main(int argc, char **argv) { - //argument variables +int main(int argc, char **argv) +{ + // argument variables const char *exe = NULL; int perclimit = 0; int exe_ok = 0; @@ -324,207 +343,240 @@ int main(int argc, char **argv) { pid_t pid = 0; int include_children = 0; - //get program name - char *p = (char*)memrchr(argv[0], (unsigned int)'/', strlen(argv[0])); - program_name = p==NULL ? argv[0] : (p+1); - //get current pid + // get program name + char *p = strrchr(argv[0], '/'); + program_name = p == NULL ? argv[0] : (p + 1); + // get current pid cpulimit_pid = getpid(); - //get cpu count + // get cpu count NCPU = get_ncpu(); - //parse arguments + // parse arguments int next_option; - int option_index = 0; - //A string listing valid short options letters - const char* short_options = "+p:e:l:vzih"; - //An array describing valid long options + int option_index = 0; + // A string listing valid short options letters + const char *short_options = "+p:e:l:vzih"; + // An array describing valid long options const struct option long_options[] = { - { "pid", required_argument, NULL, 'p' }, - { "exe", required_argument, NULL, 'e' }, - { "limit", required_argument, NULL, 'l' }, - { "verbose", no_argument, NULL, 'v' }, - { "lazy", no_argument, NULL, 'z' }, - { "include-children", no_argument, NULL, 'i' }, - { "help", no_argument, NULL, 'h' }, - { 0, 0, 0, 0 } - }; - - do { - next_option = getopt_long(argc, argv, short_options,long_options, &option_index); - switch(next_option) { - case 'p': - pid = atoi(optarg); - pid_ok = 1; - break; - case 'e': - exe = optarg; - exe_ok = 1; - break; - case 'l': - perclimit = atoi(optarg); - limit_ok = 1; - break; - case 'v': - verbose = 1; - break; - case 'z': - lazy = 1; - break; - case 'i': - include_children = 1; - break; - case 'h': - print_usage(stdout, 1); - break; - case '?': - print_usage(stderr, 1); - break; - case -1: - break; - default: - abort(); + {"pid", required_argument, NULL, 'p'}, + {"exe", required_argument, NULL, 'e'}, + {"limit", required_argument, NULL, 'l'}, + {"verbose", no_argument, NULL, 'v'}, + {"lazy", no_argument, NULL, 'z'}, + {"include-children", no_argument, NULL, 'i'}, + {"help", no_argument, NULL, 'h'}, + {0, 0, 0, 0}}; + + do + { + next_option = getopt_long(argc, argv, short_options, long_options, &option_index); + switch (next_option) + { + case 'p': + pid = atoi(optarg); + pid_ok = 1; + break; + case 'e': + exe = optarg; + exe_ok = 1; + break; + case 'l': + perclimit = atoi(optarg); + limit_ok = 1; + break; + case 'v': + verbose = 1; + break; + case 'z': + lazy = 1; + break; + case 'i': + include_children = 1; + break; + case 'h': + print_usage(stdout, 1); + break; + case '?': + print_usage(stderr, 1); + break; + case -1: + break; + default: + abort(); } - } while(next_option != -1); + } while (next_option != -1); - if (pid_ok && (pid <= 1 || pid >= get_pid_max())) { - fprintf(stderr,"Error: Invalid value for argument PID\n"); + if (pid_ok && (pid <= 1 || pid >= get_pid_max())) + { + fprintf(stderr, "Error: Invalid value for argument PID\n"); print_usage(stderr, 1); exit(1); } - if (pid != 0) { + if (pid != 0) + { lazy = 1; } - if (!limit_ok) { - fprintf(stderr,"Error: You must specify a cpu limit percentage\n"); + if (!limit_ok) + { + fprintf(stderr, "Error: You must specify a cpu limit percentage\n"); print_usage(stderr, 1); exit(1); } double limit = perclimit / 100.0; - if (limit<0 || limit >NCPU) { - fprintf(stderr,"Error: limit must be in the range 0-%d00\n", NCPU); + 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; - 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"); + 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); exit(1); } - - if (exe_ok + pid_ok + command_mode > 1) { - fprintf(stderr,"Error: You must specify exactly one target process, either by name, pid, or command line\n"); + + if (exe_ok + pid_ok + command_mode > 1) + { + fprintf(stderr, "Error: You must specify exactly one target process, either by name, pid, or command line\n"); print_usage(stderr, 1); exit(1); } - //all arguments are ok! + // all arguments are ok! signal(SIGINT, quit); signal(SIGTERM, quit); - //print the number of available cpu - if (verbose) printf("%d cpu detected\n", NCPU); + // print the number of available cpu + if (verbose) + printf("%d cpu detected\n", NCPU); - if (command_mode) { + if (command_mode) + { int i; - //executable file + // executable file const char *cmd = argv[optind]; - //command line arguments - char **cmd_args = (char**)malloc((argc-optind + 1) * sizeof(char*)); - if (cmd_args==NULL) exit(2); - for (i=0; i 0) { - //parent + else if (limiter > 0) + { + // parent int status_process; int status_limiter; waitpid(child, &status_process, 0); waitpid(limiter, &status_limiter, 0); - if (WIFEXITED(status_process)) { - if (verbose) printf("Process %d terminated with exit status %d\n", child, (int)WEXITSTATUS(status_process)); + if (WIFEXITED(status_process)) + { + if (verbose) + printf("Process %d terminated with exit status %d\n", child, (int)WEXITSTATUS(status_process)); exit(WEXITSTATUS(status_process)); } printf("Process %d terminated abnormally\n", child); exit(status_process); } - else { - //limiter code - if (verbose) printf("Limiting process %d\n",child); + else + { + // limiter code + if (verbose) + printf("Limiting process %d\n", child); limit_process(child, limit, include_children); exit(0); } } } - while(1) { - //look for the target process..or wait for it + while (1) + { + // look for the target process..or wait for it pid_t ret = 0; - if (pid_ok) { - //search by pid + if (pid_ok) + { + // search by pid ret = find_process_by_pid(pid); - if (ret == 0) { + if (ret == 0) + { printf("No process found\n"); } - else if (ret < 0) { + else if (ret < 0) + { printf("Process found but you aren't allowed to control it\n"); } } - else { - //search by file or path name + else + { + // search by file or path name ret = find_process_by_name(exe); - if (ret == 0) { + if (ret == 0) + { printf("No process found\n"); } - else if (ret < 0) { + else if (ret < 0) + { printf("Process found but you aren't allowed to control it\n"); } - else { + else + { pid = ret; } } - if (ret > 0) { - if (ret == cpulimit_pid) { + if (ret > 0) + { + if (ret == cpulimit_pid) + { printf("Target process %d is cpulimit itself! Aborting because it makes no sense\n", ret); exit(1); } printf("Process %d found\n", pid); - //control + // control limit_process(pid, limit, include_children); } - if (lazy) break; + if (lazy) + break; sleep(2); }; - + exit(0); } diff --git a/src/list.c b/src/list.c index 2a78358a..ad316b77 100644 --- a/src/list.c +++ b/src/list.c @@ -2,7 +2,7 @@ * * cpulimit - a CPU limiter for Linux * - * Copyright (C) 2005-2012, by: Angelo Marletta + * Copyright (C) 2005-2012, by: Angelo Marletta * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -26,123 +26,149 @@ #define EMPTYLIST NULL -void init_list(struct list *l,int keysize) { - l->first=l->last=NULL; - l->keysize=keysize; - l->count=0; -} - -struct list_node *add_elem(struct list *l,void *elem) { - struct list_node *newnode=(struct list_node*)malloc(sizeof(struct list_node)); - newnode->data=elem; - newnode->previous=l->last; - newnode->next=NULL; - if (l->count==0) { - l->first=l->last=newnode; +void init_list(struct list *l, int keysize) +{ + l->first = l->last = NULL; + l->keysize = keysize; + l->count = 0; +} + +struct list_node *add_elem(struct list *l, void *elem) +{ + struct list_node *newnode = (struct list_node *)malloc(sizeof(struct list_node)); + newnode->data = elem; + newnode->previous = l->last; + newnode->next = NULL; + if (l->count == 0) + { + l->first = l->last = newnode; } - else { - l->last->next=newnode; - l->last=newnode; + else + { + l->last->next = newnode; + l->last = newnode; } l->count++; return newnode; } -void delete_node(struct list *l,struct list_node *node) { - if (l->count==1) { - l->first=l->last=NULL; +void delete_node(struct list *l, struct list_node *node) +{ + if (l->count == 1) + { + l->first = l->last = NULL; } - else if (node==l->first) { - node->next->previous=NULL; - l->first=node->next; + else if (node == l->first) + { + node->next->previous = NULL; + l->first = node->next; } - else if (node==l->last) { - node->previous->next=NULL; - l->last=node->previous; + else if (node == l->last) + { + node->previous->next = NULL; + l->last = node->previous; } - else { - node->previous->next=node->next; - node->next->previous=node->previous; + else + { + node->previous->next = node->next; + node->next->previous = node->previous; } l->count--; free(node); } -void destroy_node(struct list *l,struct list_node *node) { +void destroy_node(struct list *l, struct list_node *node) +{ free(node->data); - node->data=NULL; - delete_node(l,node); + node->data = NULL; + delete_node(l, node); } -int is_empty_list(struct list *l) { - return (l->count==0?TRUE:FALSE); +int is_empty_list(struct list *l) +{ + return (l->count == 0 ? TRUE : FALSE); } -int get_list_count(struct list *l) { +int get_list_count(struct list *l) +{ return l->count; } -void *first_elem(struct list *l) { +void *first_elem(struct list *l) +{ return l->first->data; } -struct list_node *first_node(struct list *l) { +struct list_node *first_node(struct list *l) +{ return l->first; } -void *last_elem(struct list *l) { +void *last_elem(struct list *l) +{ return l->last->data; } -struct list_node *last_node(struct list *l) { +struct list_node *last_node(struct list *l) +{ return l->last; } -struct list_node *xlocate_node(struct list *l,void *elem,int offset,int length) { +struct list_node *xlocate_node(struct list *l, void *elem, int offset, int length) +{ struct list_node *tmp; - tmp=l->first; - while(tmp!=NULL) { - if(!memcmp((char*)tmp->data+offset,elem,length==0?l->keysize:length)) return (tmp); - tmp=tmp->next; + tmp = l->first; + while (tmp != NULL) + { + if (!memcmp((char *)tmp->data + offset, elem, length == 0 ? l->keysize : length)) + return (tmp); + tmp = tmp->next; } return EMPTYLIST; } -struct list_node *locate_node(struct list *l,void *elem) { - return(xlocate_node(l,elem,0,0)); +struct list_node *locate_node(struct list *l, void *elem) +{ + return (xlocate_node(l, elem, 0, 0)); } -void *xlocate_elem(struct list *l,void *elem,int offset,int length) { - struct list_node *node=xlocate_node(l,elem,offset,length); - return(node==NULL?NULL:node->data); +void *xlocate_elem(struct list *l, void *elem, int offset, int length) +{ + struct list_node *node = xlocate_node(l, elem, offset, length); + return (node == NULL ? NULL : node->data); } -void *locate_elem(struct list *l,void *elem) { - return(xlocate_elem(l,elem,0,0)); +void *locate_elem(struct list *l, void *elem) +{ + return (xlocate_elem(l, elem, 0, 0)); } -void clear_list(struct list *l) { - while(l->first!=EMPTYLIST) { +void clear_list(struct list *l) +{ + while (l->first != EMPTYLIST) + { struct list_node *tmp; - tmp=l->first; - l->first=l->first->next; + tmp = l->first; + l->first = l->first->next; free(tmp); - tmp=NULL; + tmp = NULL; } - l->last=EMPTYLIST; - l->count=0; + l->last = EMPTYLIST; + l->count = 0; } -void destroy_list(struct list *l) { - while(l->first!=EMPTYLIST) { +void destroy_list(struct list *l) +{ + while (l->first != EMPTYLIST) + { struct list_node *tmp; - tmp=l->first; - l->first=l->first->next; + tmp = l->first; + l->first = l->first->next; free(tmp->data); - tmp->data=NULL; + tmp->data = NULL; free(tmp); - tmp=NULL; + tmp = NULL; } - l->last=EMPTYLIST; - l->count=0; + l->last = EMPTYLIST; + l->count = 0; } diff --git a/src/list.h b/src/list.h index 0b43a2b3..bd169127 100644 --- a/src/list.h +++ b/src/list.h @@ -2,7 +2,7 @@ * * cpulimit - a CPU limiter for Linux * - * Copyright (C) 2005-2012, by: Angelo Marletta + * Copyright (C) 2005-2012, by: Angelo Marletta * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -23,52 +23,54 @@ #define __LIST__ -#ifndef TRUE - #define TRUE 1 - #define FALSE 0 +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 #endif -struct list_node { - //pointer to the content of the node +struct list_node +{ + // pointer to the content of the node void *data; - //pointer to previous node + // pointer to previous node struct list_node *previous; - //pointer to next node + // pointer to next node struct list_node *next; }; -struct list { - //first node +struct list +{ + // first node struct list_node *first; - //last node + // last node struct list_node *last; - //size of the search key in bytes + // size of the search key in bytes int keysize; - //element count + // element count int count; }; /* * Initialize a list, with a specified key size */ -void init_list(struct list *l,int keysize); +void init_list(struct list *l, int keysize); /* * Add a new element at the end of the list * return the pointer to the new node */ -struct list_node *add_elem(struct list *l,void *elem); +struct list_node *add_elem(struct list *l, void *elem); /* * Delete a node */ -void delete_node(struct list *l,struct list_node *node); +void delete_node(struct list *l, struct list_node *node); /* * Delete a node from the list, even the content pointed by it * Use only when the content is a dynamically allocated pointer */ -void destroy_node(struct list *l,struct list_node *node); +void destroy_node(struct list *l, struct list_node *node); /* * Check whether a list is empty or not @@ -108,22 +110,22 @@ struct list_node *last_node(struct list *l); * if the element is found, return the node address * else return NULL */ -struct list_node *xlocate_node(struct list *l,void *elem,int offset,int length); +struct list_node *xlocate_node(struct list *l, void *elem, int offset, int length); /* * The same of xlocate_node(), but return the content of the node */ -void *xlocate_elem(struct list *l,void *elem,int offset,int length); +void *xlocate_elem(struct list *l, void *elem, int offset, int length); /* * The same of calling xlocate_node() with offset=0 and length=0 */ -struct list_node *locate_node(struct list *l,void *elem); +struct list_node *locate_node(struct list *l, void *elem); /* * The same of locate_node, but return the content of the node */ -void *locate_elem(struct list *l,void *elem); +void *locate_elem(struct list *l, void *elem); /* * Delete all the elements in the list diff --git a/src/memrchr.c b/src/memrchr.c deleted file mode 100644 index 1f378702..00000000 --- a/src/memrchr.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2007 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -/* - * Reverse memchr() - * Find the last occurrence of 'c' in the buffer 's' of size 'n'. - */ -void * -memrchr(s, c, n) - const void *s; - int c; - size_t n; -{ - if (n != 0) { - const unsigned char *cp; - cp = (unsigned char *)s + n; - do { - if (*(--cp) == (unsigned char)c) - return((void *)cp); - } while (--n != 0); - } - return((void *)0); -} diff --git a/src/process_group.c b/src/process_group.c index 06d73a6f..d78f1460 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -2,7 +2,7 @@ * * cpulimit - a CPU limiter for Linux * - * Copyright (C) 2005-2012, by: Angelo Marletta + * Copyright (C) 2005-2012, by: Angelo Marletta * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -24,7 +24,7 @@ #include #include #include - +#include #include #include "process_iterator.h" @@ -37,7 +37,13 @@ // 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; + return (kill(pid, 0) == 0) ? pid : -pid; +} + +static inline char *basename(const char *filename) +{ + char *p = strrchr(filename, '/'); + return p ? p + 1 : (char *)filename; } // look for a process with a given name @@ -48,10 +54,10 @@ int find_process_by_pid(pid_t pid) // negative pid, if it is found but it's not possible to control it int find_process_by_name(const char *process_name) { - //pid of the target process + // pid of the target process pid_t pid = -1; - //process iterator + // process iterator struct process_iterator it; struct process proc; struct process_filter filter; @@ -60,31 +66,40 @@ int find_process_by_name(const char *process_name) init_process_iterator(&it, &filter); while (get_next_process(&it, &proc) != -1) { - //process found - if (strncmp(basename(proc.command), process_name, strlen(process_name))==0 && kill(pid,SIGCONT)==0) { - //process is ok! + // process found + if (strcmp(basename(proc.command), process_name) == 0) + { + if (kill(proc.pid, 0) == -1 && errno == EPERM) + { + // do not have permission + return -1; + } + // process is ok! pid = proc.pid; break; } } - if (close_process_iterator(&it) != 0) exit(1); - if (pid >= 0) { - //ok, the process was found + if (close_process_iterator(&it) != 0) + exit(1); + if (pid >= 0) + { + // ok, the process was found return pid; } - else { - //process not found + else + { + // process not found return 0; } } int init_process_group(struct process_group *pgroup, int target_pid, int include_children) { - //hashtable initialization + // 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)); + 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); @@ -94,10 +109,12 @@ int init_process_group(struct process_group *pgroup, int target_pid, int include int close_process_group(struct process_group *pgroup) { int i; - int size = sizeof(pgroup->proctable) / sizeof(struct process*); - for (i=0; iproctable[i] != NULL) { - //free() history for each process + 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; @@ -111,16 +128,16 @@ int close_process_group(struct process_group *pgroup) void remove_terminated_processes(struct process_group *pgroup) { - //TODO + // 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-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 +// parameter in range 0-1 #define ALFA 0.08 #define MIN_DT 20 @@ -131,7 +148,7 @@ void update_process_group(struct process_group *pgroup) struct process_filter filter; struct timeval now; gettimeofday(&now, NULL); - //time elapsed from previous sample (in ms) + // 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; @@ -141,13 +158,13 @@ void update_process_group(struct process_group *pgroup) 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); + // 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); if (pgroup->proctable[hashkey] == NULL) { - //empty bucket + // empty bucket pgroup->proctable[hashkey] = malloc(sizeof(struct list)); struct process *new_process = malloc(sizeof(struct process)); tmp_process.cpu_usage = -1; @@ -158,11 +175,11 @@ void update_process_group(struct process_group *pgroup) } else { - //existing bucket - struct process *p = (struct process*)locate_elem(pgroup->proctable[hashkey], &tmp_process); + // existing bucket + struct process *p = (struct process *)locate_elem(pgroup->proctable[hashkey], &tmp_process); if (p == NULL) { - //process is new. add it + // 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)); @@ -174,32 +191,38 @@ void update_process_group(struct process_group *pgroup) assert(tmp_process.pid == p->pid); assert(tmp_process.starttime == p->starttime); add_elem(pgroup->proclist, p); - if (dt < MIN_DT) continue; - //process exists. update CPU usage + 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 + if (p->cpu_usage == -1) + { + // initialization p->cpu_usage = sample; } - else { - //usage adjustment - p->cpu_usage = (1.0-ALFA) * p->cpu_usage + ALFA * 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; + if (dt < MIN_DT) + return; pgroup->last_update = now; } int remove_process(struct process_group *pgroup, int pid) { int hashkey = pid_hashfn(pid); - if (pgroup->proctable[hashkey] == NULL) return 1; //nothing to delete - struct list_node *node = (struct list_node*)locate_node(pgroup->proctable[hashkey], &pid); - if (node == NULL) return 2; + if (pgroup->proctable[hashkey] == NULL) + return 1; // nothing to delete + struct list_node *node = (struct list_node *)locate_node(pgroup->proctable[hashkey], &pid); + if (node == NULL) + return 2; delete_node(pgroup->proctable[hashkey], node); return 0; } diff --git a/src/process_group.h b/src/process_group.h index 5a5b5815..f5b3936f 100644 --- a/src/process_group.h +++ b/src/process_group.h @@ -2,7 +2,7 @@ * * cpulimit - a CPU limiter for Linux * - * Copyright (C) 2005-2012, by: Angelo Marletta + * Copyright (C) 2005-2012, by: Angelo Marletta * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,7 +32,7 @@ struct process_group { - //hashtable with all the processes (array of struct list of struct process) + // hashtable with all the processes (array of struct list of struct process) struct list *proctable[PIDHASH_SZ]; struct list *proclist; pid_t target_pid; diff --git a/src/process_iterator.c b/src/process_iterator.c index 8b4019d2..e4c89fdd 100644 --- a/src/process_iterator.c +++ b/src/process_iterator.c @@ -2,7 +2,7 @@ * * cpulimit - a CPU limiter for Linux * - * Copyright (C) 2005-2012, by: Angelo Marletta + * Copyright (C) 2005-2012, by: Angelo Marletta * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -28,17 +28,17 @@ #include #include "process_iterator.h" -//See this link to port to other systems: http://www.steve.org.uk/Reference/Unix/faq_8.html#SEC85 +// See this link to port to other systems: http://www.steve.org.uk/Reference/Unix/faq_8.html#SEC85 -#ifdef __linux__ +#if defined(__linux__) #include "process_iterator_linux.c" -#elif defined __FreeBSD__ +#elif defined(__FreeBSD__) #include "process_iterator_freebsd.c" -#elif defined __APPLE__ +#elif defined(__APPLE__) #include "process_iterator_apple.c" diff --git a/src/process_iterator.h b/src/process_iterator.h index 70520b68..392ed2df 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -2,7 +2,7 @@ * * cpulimit - a CPU limiter for Linux * - * Copyright (C) 2005-2012, by: Angelo Marletta + * Copyright (C) 2005-2012, by: Angelo Marletta * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -27,22 +27,21 @@ #include #include -//USER_HZ detection, from openssl code +// USER_HZ detection, from openssl code #ifndef HZ -# if defined(_SC_CLK_TCK) \ - && (!defined(OPENSSL_SYS_VMS) || __CTRL_VER >= 70000000) -# define HZ ((double)sysconf(_SC_CLK_TCK)) -# else -# ifndef CLK_TCK -# ifndef _BSD_CLK_TCK_ /* FreeBSD hack */ -# define HZ 100.0 -# else /* _BSD_CLK_TCK_ */ -# define HZ ((double)_BSD_CLK_TCK_) -# endif -# else /* CLK_TCK */ -# define HZ ((double)CLK_TCK) -# endif -# endif +#if defined(_SC_CLK_TCK) && (!defined(OPENSSL_SYS_VMS) || __CTRL_VER >= 70000000) +#define HZ ((double)sysconf(_SC_CLK_TCK)) +#else +#ifndef CLK_TCK +#ifndef _BSD_CLK_TCK_ /* FreeBSD hack */ +#define HZ 100.0 +#else /* _BSD_CLK_TCK_ */ +#define HZ ((double)_BSD_CLK_TCK_) +#endif +#else /* CLK_TCK */ +#define HZ ((double)CLK_TCK) +#endif +#endif #endif #ifdef __FreeBSD__ @@ -50,37 +49,39 @@ #endif // process descriptor -struct process { - //pid of the process +struct process +{ + // pid of the process pid_t pid; - //ppid of the process + // ppid of the process pid_t ppid; - //start time (unix timestamp) - int starttime; - //cputime used by the process (in milliseconds) + // start time (unix timestamp) + time_t starttime; + // cputime used by the process (in milliseconds) int cputime; - //actual cpu usage estimation (value in range 0-1) + // actual cpu usage estimation (value in range 0-1) double cpu_usage; - //absolute path of the executable file - char command[PATH_MAX+1]; + // absolute path of the executable file + char command[PATH_MAX + 1]; }; -struct process_filter { +struct process_filter +{ int pid; int include_children; - char program_name[PATH_MAX+1]; + char program_name[PATH_MAX + 1]; }; -struct process_iterator { -#ifdef __linux__ +struct process_iterator +{ +#if defined(__linux__) DIR *dip; - int boot_time; -#elif defined __FreeBSD__ +#elif defined(__FreeBSD__) kvm_t *kd; struct kinfo_proc *procs; int count; int i; -#elif defined __APPLE__ +#elif defined(__APPLE__) int i; int count; int *pidlist; diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index b878ed8c..9b7c218c 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -2,7 +2,7 @@ * * cpulimit - a CPU limiter for Linux * - * Copyright (C) 2005-2012, by: Angelo Marletta + * Copyright (C) 2005-2012, by: Angelo Marletta * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -17,7 +17,7 @@ * 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. - * + * * Author: Simon Sigurdhsson * */ @@ -26,44 +26,56 @@ #include #include -int unique_nonzero_ints(int* arr_in, int len_in, int* arr_out) { - int* source = arr_in; - if (arr_out == NULL) return -1; - if (arr_in == arr_out) { - source = malloc(sizeof(int)*len_in); - memcpy(source, arr_in, sizeof(int)*len_in); - memset(arr_out, -1, sizeof(int)*len_in); +int unique_nonzero_ints(int *arr_in, int len_in, int *arr_out) +{ + int *source = arr_in; + if (arr_out == NULL) + return -1; + if (arr_in == arr_out) + { + source = malloc(sizeof(int) * len_in); + memcpy(source, arr_in, sizeof(int) * len_in); + memset(arr_out, -1, sizeof(int) * len_in); } int len_out = 0; - int i, j; - for (i=0; ii = 0; /* Find out how much to allocate for it->pidlist */ - if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0)) <= 0) { + if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0)) <= 0) + { fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); return -1; } /* Allocate and populate it->pidlist */ - if ((it->pidlist = (int *)malloc((it->count)*sizeof(int))) == NULL) { + if ((it->pidlist = (int *)malloc((it->count) * sizeof(int))) == NULL) + { fprintf(stderr, "malloc: %s\n", strerror(errno)); } - if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, it->pidlist, it->count)) <= 0) { + if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, it->pidlist, it->count)) <= 0) + { fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); return -1; } @@ -72,7 +84,8 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi return 0; } -static int pti2proc(struct proc_taskallinfo *ti, struct process *process) { +static int pti2proc(struct proc_taskallinfo *ti, struct process *process) +{ int bytes; process->pid = ti->pbsd.pbi_pid; process->ppid = ti->pbsd.pbi_ppid; @@ -83,43 +96,56 @@ static int pti2proc(struct proc_taskallinfo *ti, struct process *process) { return 0; } -static int get_process_pti(pid_t pid, struct proc_taskallinfo *ti) { +static int get_process_pti(pid_t pid, struct proc_taskallinfo *ti) +{ int bytes; bytes = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, ti, sizeof(*ti)); - if (bytes <= 0) { - if (!(errno & (EPERM | ESRCH))) { + if (bytes <= 0) + { + if (!(errno & (EPERM | ESRCH))) + { fprintf(stderr, "proc_pidinfo: %s\n", strerror(errno)); } return -1; - } else if (bytes < sizeof(ti)) { + } + else if (bytes < sizeof(ti)) + { fprintf(stderr, "proc_pidinfo: too few bytes; expected %ld, got %d\n", sizeof(ti), bytes); return -1; } 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) { +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) + { struct proc_taskallinfo ti; - if (get_process_pti(it->filter->pid, &ti) != 0) { + if (get_process_pti(it->filter->pid, &ti) != 0) + { it->i = it->count = 0; return -1; } it->i = it->count = 1; return pti2proc(&ti, p); } - while (it->i < it->count) { + while (it->i < it->count) + { struct proc_taskallinfo ti; - if (get_process_pti(it->pidlist[it->i], &ti) != 0) { + if (get_process_pti(it->pidlist[it->i], &ti) != 0) + { it->i++; continue; } - if (ti.pbsd.pbi_flags & PROC_FLAG_SYSTEM) { + if (ti.pbsd.pbi_flags & PROC_FLAG_SYSTEM) + { it->i++; continue; } - if (it->filter->pid != 0 && it->filter->include_children) { + if (it->filter->pid != 0 && it->filter->include_children) + { pti2proc(&ti, p); it->i++; if (p->pid != it->pidlist[it->i - 1]) // I don't know why this can happen @@ -138,7 +164,8 @@ int get_next_process(struct process_iterator *it, struct process *p) { return -1; } -int close_process_iterator(struct process_iterator *it) { +int close_process_iterator(struct process_iterator *it) +{ free(it->pidlist); it->pidlist = NULL; it->filter = NULL; diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index a6381123..b3f56706 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -2,7 +2,7 @@ * * cpulimit - a CPU limiter for Linux * - * Copyright (C) 2005-2012, by: Angelo Marletta + * Copyright (C) 2005-2012, by: Angelo Marletta * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,23 +19,26 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include +#include #include #include #include -int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { +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) { + 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) { + 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)); + // fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(it->kd)); return -1; } it->filter = filter; @@ -49,7 +52,8 @@ static int kproc2proc(kvm_t *kd, struct kinfo_proc *kproc, struct process *proc) 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) return -1; + if (args == NULL) + return -1; memcpy(proc->command, args[0], strlen(args[0]) + 1); return 0; } @@ -60,14 +64,15 @@ static int get_single_process(kvm_t *kd, pid_t pid, struct process *process) 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)); + // 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) { +int get_next_process(struct process_iterator *it, struct process *p) +{ if (it->i == it->count) { return -1; @@ -109,11 +114,12 @@ int get_next_process(struct process_iterator *it, struct process *p) { return -1; } -int close_process_iterator(struct process_iterator *it) { - if (kvm_close(it->kd) == -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 index c8cdd07a..2e45f014 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -2,7 +2,7 @@ * * cpulimit - a CPU limiter for Linux * - * Copyright (C) 2005-2012, by: Angelo Marletta + * Copyright (C) 2005-2012, by: Angelo Marletta * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -21,50 +21,30 @@ #include -static int get_boot_time() -{ - int uptime = 0; - 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) + if (mnt.f_type != 0x9fa0) return 0; return 1; } int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { - if (!check_proc()) { + if (!check_proc()) + { fprintf(stderr, "procfs is not mounted!\nAborting\n"); exit(-2); } - //open a directory stream to /proc directory + // 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; } @@ -74,37 +54,84 @@ static int read_process_info(pid_t pid, struct process *p) static char statfile[32]; static char exefile[1024]; p->pid = pid; - //read stat file + FILE *fd; + // read command line + sprintf(exefile, "/proc/%d/cmdline", p->pid); + fd = fopen(exefile, "r"); + if (fd == NULL) + goto error_out1; + if (fgets(buffer, sizeof(buffer), fd) == NULL) + { + fclose(fd); + goto error_out1; + } + fclose(fd); + strcpy(p->command, buffer); + + // 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) { + fd = fopen(statfile, "r"); + if (fd == NULL) + goto error_out2; + if (fgets(buffer, sizeof(buffer), fd) == NULL) + { fclose(fd); - return -1; + goto error_out2; } fclose(fd); + // pid char *token = strtok(buffer, " "); - int i; - for (i=0; i<3; i++) token = strtok(NULL, " "); + if (token == NULL) + goto error_out2; + // comm + token = strtok(NULL, " "); + if (token == NULL) + goto error_out2; + if (token[0] == '(') + { + while (token[strlen(token) - 1] != ')') + { + token = strtok(NULL, " "); + if (token == NULL) + goto error_out2; + } + } + // state + token = strtok(NULL, " "); + if (token == NULL) + goto error_out2; + if (strcmp(token, "Z") == 0 || strcmp(token, "X") == 0) + goto error_out2; + // ppid + token = strtok(NULL, " "); + if (token == NULL) + goto error_out2; p->ppid = atoi(token); - for (i=0; i<10; i++) + int i; + for (i = 0; i < 10; i++) + { token = strtok(NULL, " "); + if (token == NULL) + goto error_out2; + } p->cputime = atoi(token) * 1000 / HZ; token = strtok(NULL, " "); + if (token == NULL) + goto error_out2; p->cputime += atoi(token) * 1000 / HZ; - for (i=0; i<7; i++) + 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; + if (token == NULL) + goto error_out2; } - fclose(fd); - strcpy(p->command, buffer); + p->starttime = atoi(token) / sysconf(_SC_CLK_TCK); return 0; + +error_out1: + p->command[0] = '\0'; +error_out2: + return -1; } static pid_t getppid_of(pid_t pid) @@ -113,22 +140,47 @@ static pid_t getppid_of(pid_t pid) 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) { + if (fd == NULL) + return -1; + if (fgets(buffer, sizeof(buffer), fd) == NULL) + { fclose(fd); return -1; } fclose(fd); + // pid char *token = strtok(buffer, " "); - int i; - for (i=0; i<3; i++) token = strtok(NULL, " "); + if (token == NULL) + return -1; + // comm + token = strtok(NULL, " "); + if (token == NULL) + return -1; + if (token[0] == '(') + { + while (token[strlen(token) - 1] != ')') + { + token = strtok(NULL, " "); + if (token == NULL) + return -1; + } + } + // state + token = strtok(NULL, " "); + if (token == NULL) + return -1; + // ppid + token = strtok(NULL, " "); + if (token == NULL) + return -1; 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) { + while (ppid > 1 && ppid != parent_pid) + { ppid = getppid_of(ppid); } return ppid == parent_pid; @@ -138,32 +190,33 @@ int get_next_process(struct process_iterator *it, struct process *p) { if (it->dip == NULL) { - //end of processes + // end of processes return -1; } if (it->filter->pid != 0 && !it->filter->include_children) { int ret = read_process_info(it->filter->pid, p); - //p->starttime += it->boot_time; closedir(it->dip); it->dip = NULL; - if (ret != 0) return -1; + if (ret != 0) + return -1; return 0; } struct dirent *dit = NULL; - //read in from /proc and seek for process dirs - while ((dit = readdir(it->dip)) != NULL) { - if(strtok(dit->d_name, "0123456789") != NULL) + // 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; + 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 + // end of processes closedir(it->dip); it->dip = NULL; return -1; @@ -171,8 +224,10 @@ int get_next_process(struct process_iterator *it, struct process *p) return 0; } -int close_process_iterator(struct process_iterator *it) { - if (it->dip != NULL && closedir(it->dip) == -1) { +int close_process_iterator(struct process_iterator *it) +{ + if (it->dip != NULL && closedir(it->dip) == -1) + { perror("closedir"); return 1; } diff --git a/.gitignore b/tests/.gitignore similarity index 72% rename from .gitignore rename to tests/.gitignore index 70e48752..0e9d6bed 100644 --- a/.gitignore +++ b/tests/.gitignore @@ -1,5 +1,4 @@ *.o *~ -src/cpulimit busy process_iterator_test diff --git a/tests/Makefile b/tests/Makefile index 764db878..2dd3eb59 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,23 +1,24 @@ -CC?=gcc -CFLAGS?=-Wall -g -TARGETS=busy process_iterator_test -SRC=../src -SYSLIBS?=-lpthread -LIBS=$(SRC)/list.o $(SRC)/process_iterator.o $(SRC)/process_group.o +CC ?= gcc +CFLAGS ?= -Wall +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 + LDFLAGS += -lkvm endif -all:: $(TARGETS) +.PHONY: all clean -busy: busy.c - $(CC) -o busy busy.c $(SYSLIBS) $(CFLAGS) +all: $(TARGETS) + +busy: busy.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(SYSLIBS) process_iterator_test: process_iterator_test.c $(LIBS) - $(CC) -I$(SRC) -o process_iterator_test process_iterator_test.c $(LIBS) $(SYSLIBS) $(CFLAGS) + $(CC) $(CFLAGS) $(LDFLAGS) -I$(SRC) -o $@ $< $(LIBS) $(SYSLIBS) clean: rm -f *~ *.o $(TARGETS) - diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index 16151967..a8163065 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -27,10 +27,7 @@ #include #include #include - -#ifdef __APPLE__ || __FREEBSD__ #include -#endif #include #include From c3047aa151c092e4f70dc7d52f983dd6f352f632 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 3 May 2022 03:12:00 +0800 Subject: [PATCH 002/209] avoid overflow --- src/cpulimit.c | 50 ++++++++++++++++++++++++++++----------------- src/process_group.c | 13 +++++++++--- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 75e81f98..524a5273 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -58,6 +58,10 @@ #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif +#ifndef EPSILON +#define EPSILON 1e-12 +#endif + // control time slot in microseconds // each slot is splitted in a working slice and a sleeping slice // TODO: make it adaptive, based on the actual system load @@ -105,10 +109,10 @@ static void quit(int sig) } // 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 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 void print_usage(FILE *stream, int exit_code) { @@ -204,7 +208,7 @@ void limit_process(pid_t pid, double limit, int include_children) memset(&startwork, 0, sizeof(struct timeval)); memset(&endwork, 0, sizeof(struct timeval)); // last working time in microseconds - unsigned long workingtime = 0; + // unsigned long workingtime = 0; // generic list item struct list_node *node; // counter @@ -256,22 +260,30 @@ void limit_process(pid_t pid, double limit, int include_children) // it's the 1st cycle, initialize workingrate pcpu = limit; workingrate = limit; - twork.tv_nsec = TIME_SLOT * limit * 1000; } else { // adjust workingrate - workingrate = MIN(workingrate / pcpu * limit, 1); - twork.tv_nsec = TIME_SLOT * 1000 * workingrate; + workingrate = (workingrate + EPSILON) / + (pcpu + EPSILON) * + (limit + EPSILON); } - tsleep.tv_nsec = TIME_SLOT * 1000 - twork.tv_nsec; + workingrate = MAX(MIN(workingrate, 1 - EPSILON), EPSILON); + + double twork_total_nsec = (double)TIME_SLOT * 1000 * workingrate; + twork.tv_sec = (time_t)(twork_total_nsec / 1e9); + twork.tv_nsec = (long)(twork_total_nsec - twork.tv_sec * 1e9); + + double tsleep_total_nsec = (double)TIME_SLOT * 1000 - twork_total_nsec; + tsleep.tv_sec = (time_t)(tsleep_total_nsec / 1e9); + tsleep.tv_nsec = (long)(tsleep_total_nsec - tsleep.tv_sec * 1e9); if (verbose) { if (c % 200 == 0) - printf("\n%%CPU\twork quantum\tsleep quantum\tactive rate\n"); + printf("\n %%CPU work quantum sleep quantum active 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("%7.2lf%% %9.0lf us %10.0lf us %10.2lf%%\n", pcpu * 100, twork_total_nsec / 1000, tsleep_total_nsec / 1000, workingrate * 100); } // resume processes @@ -296,16 +308,16 @@ void limit_process(pid_t pid, double limit, int include_children) gettimeofday(&startwork, NULL); nanosleep(&twork, NULL); gettimeofday(&endwork, NULL); - workingtime = timediff(&endwork, &startwork); + // workingtime = timediff(&endwork, &startwork); - 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); - } + // 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) + if (tsleep.tv_nsec > 0 || tsleep.tv_sec > 0) { // stop processes only if tsleep>0 node = pgroup.proclist->first; diff --git a/src/process_group.c b/src/process_group.c index d78f1460..724c4c32 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -132,9 +132,15 @@ void remove_terminated_processes(struct process_group *pgroup) } // 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) +// 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); +// } + +// returns t1-t2 in millisecond +static inline double timediff_in_ms(const struct timeval *t1, const struct timeval *t2) { - return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_usec - t2->tv_usec); + return (t1->tv_sec - t2->tv_sec) * 1e3 + (t1->tv_usec - t2->tv_usec) / 1e3; } // parameter in range 0-1 @@ -149,7 +155,8 @@ void update_process_group(struct process_group *pgroup) struct timeval now; gettimeofday(&now, NULL); // time elapsed from previous sample (in ms) - long dt = timediff(&now, &pgroup->last_update) / 1000; + // long dt = timediff(&now, &pgroup->last_update) / 1000; + double dt = timediff_in_ms(&now, &pgroup->last_update); filter.pid = pgroup->target_pid; filter.include_children = pgroup->include_children; init_process_iterator(&it, &filter); From 682796947f65f3b946f2cd61d231489f8d53d085 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 4 Sep 2022 11:55:51 +0800 Subject: [PATCH 003/209] add includes --- src/process_group.c | 7 +------ src/process_iterator.c | 1 + 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index 724c4c32..8e9dc8a4 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -40,12 +41,6 @@ int find_process_by_pid(pid_t pid) return (kill(pid, 0) == 0) ? pid : -pid; } -static inline char *basename(const char *filename) -{ - char *p = strrchr(filename, '/'); - return p ? p + 1 : (char *)filename; -} - // 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 diff --git a/src/process_iterator.c b/src/process_iterator.c index e4c89fdd..1c771699 100644 --- a/src/process_iterator.c +++ b/src/process_iterator.c @@ -23,6 +23,7 @@ #include #include #ifndef __APPLE__ +#include #include #endif #include From 25724a41f82ae7aeeaeb1c649f2be1fffd2e2ab7 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 5 Sep 2022 02:12:32 +0800 Subject: [PATCH 004/209] fix buffer size --- src/process_iterator_linux.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 2e45f014..812f0fd8 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -50,9 +50,9 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi static int read_process_info(pid_t pid, struct process *p) { - static char buffer[1024]; - static char statfile[32]; - static char exefile[1024]; + static char buffer[PATH_MAX + 1]; + static char statfile[64]; + static char exefile[64]; p->pid = pid; FILE *fd; // read command line @@ -136,8 +136,8 @@ static int read_process_info(pid_t pid, struct process *p) static pid_t getppid_of(pid_t pid) { - char statfile[20]; - char buffer[1024]; + static char statfile[64]; + static char buffer[2048]; sprintf(statfile, "/proc/%d/stat", pid); FILE *fd = fopen(statfile, "r"); if (fd == NULL) From 01a6873d953dc0d55c420e84a5a42291423471f5 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 5 Sep 2022 03:43:57 +0800 Subject: [PATCH 005/209] use basename of the executable program file --- src/cpulimit.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 524a5273..9d31b721 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -42,6 +42,7 @@ #include #include #include +#include #include "process_group.h" #include "list.h" @@ -348,6 +349,7 @@ int main(int argc, char **argv) { // argument variables const char *exe = NULL; + char exe_name[PATH_MAX + 1]; int perclimit = 0; int exe_ok = 0; int pid_ok = 0; @@ -389,7 +391,10 @@ int main(int argc, char **argv) pid_ok = 1; break; case 'e': - exe = optarg; + // exe = optarg; + *exe_name = '\0'; + strncat(exe_name, optarg, PATH_MAX); + exe = basename(exe_name); exe_ok = 1; break; case 'l': From 20801f93b6b0bdc21af9d2a4b45d704828ddf52a Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 22 Sep 2022 02:46:30 +0800 Subject: [PATCH 006/209] make large local arrays static --- src/cpulimit.c | 2 +- src/process_iterator_freebsd.c | 2 +- src/process_iterator_linux.c | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 9d31b721..f5c39a93 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -349,7 +349,7 @@ int main(int argc, char **argv) { // argument variables const char *exe = NULL; - char exe_name[PATH_MAX + 1]; + static char exe_name[PATH_MAX + 1]; int perclimit = 0; int exe_ok = 0; int pid_ok = 0; diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index b3f56706..7c9c4a46 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -26,7 +26,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { - char errbuf[_POSIX2_LINE_MAX]; + static 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) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 812f0fd8..77168e5b 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -51,8 +51,8 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi static int read_process_info(pid_t pid, struct process *p) { static char buffer[PATH_MAX + 1]; - static char statfile[64]; - static char exefile[64]; + char statfile[32]; + char exefile[32]; p->pid = pid; FILE *fd; // read command line @@ -136,8 +136,8 @@ static int read_process_info(pid_t pid, struct process *p) static pid_t getppid_of(pid_t pid) { - static char statfile[64]; - static char buffer[2048]; + char statfile[32]; + static char buffer[1024]; sprintf(statfile, "/proc/%d/stat", pid); FILE *fd = fopen(statfile, "r"); if (fd == NULL) From aa82a4ee5cbbbad6ecf3083bfbd4a4e0e250d249 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 22 Sep 2022 12:47:34 +0800 Subject: [PATCH 007/209] simplify getppid_of --- src/process_iterator_linux.c | 38 +++++------------------------------- 1 file changed, 5 insertions(+), 33 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 77168e5b..76a1d863 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -137,43 +137,15 @@ static int read_process_info(pid_t pid, struct process *p) static pid_t getppid_of(pid_t pid) { char statfile[32]; - static char buffer[1024]; + FILE *fd; + pid_t parent_pid = -1; sprintf(statfile, "/proc/%d/stat", pid); - FILE *fd = fopen(statfile, "r"); - if (fd == NULL) - return -1; - if (fgets(buffer, sizeof(buffer), fd) == NULL) + if ((fd = fopen(statfile, "r")) != NULL) { + fscanf(fd, "%*d (%*[^)]) %*c %d", &parent_pid); fclose(fd); - return -1; - } - fclose(fd); - // pid - char *token = strtok(buffer, " "); - if (token == NULL) - return -1; - // comm - token = strtok(NULL, " "); - if (token == NULL) - return -1; - if (token[0] == '(') - { - while (token[strlen(token) - 1] != ')') - { - token = strtok(NULL, " "); - if (token == NULL) - return -1; - } } - // state - token = strtok(NULL, " "); - if (token == NULL) - return -1; - // ppid - token = strtok(NULL, " "); - if (token == NULL) - return -1; - return atoi(token); + return parent_pid; } static int is_child_of(pid_t child_pid, pid_t parent_pid) From 9dc6182d823998622f92f605335b6a0863103434 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 23 Sep 2022 06:09:11 +0800 Subject: [PATCH 008/209] simplify read_process_info --- src/process_iterator_linux.c | 66 +++++++----------------------------- 1 file changed, 12 insertions(+), 54 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 76a1d863..b4be7415 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -50,82 +50,40 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi static int read_process_info(pid_t pid, struct process *p) { - static char buffer[PATH_MAX + 1]; - char statfile[32]; - char exefile[32]; - p->pid = pid; + char statfile[32], exefile[32], state; + unsigned long long utime, stime, start_time; FILE *fd; + + p->pid = pid; + // read command line sprintf(exefile, "/proc/%d/cmdline", p->pid); fd = fopen(exefile, "r"); if (fd == NULL) goto error_out1; - if (fgets(buffer, sizeof(buffer), fd) == NULL) + if (fgets(p->command, sizeof(p->command), fd) == NULL) { fclose(fd); goto error_out1; } fclose(fd); - strcpy(p->command, buffer); // read stat file sprintf(statfile, "/proc/%d/stat", p->pid); fd = fopen(statfile, "r"); if (fd == NULL) goto error_out2; - if (fgets(buffer, sizeof(buffer), fd) == NULL) + + if (fscanf(fd, "%*d (%*[^)]) %c %d %*d %*d %*d %*d %*d %*d %*d %*d %*d %llu %llu %*d %*d %*d %*d %*d %*d %llu", + &state, &p->ppid, &utime, &stime, &start_time) != 5 || + state == 'Z' || state == 'X') { fclose(fd); goto error_out2; } fclose(fd); - // pid - char *token = strtok(buffer, " "); - if (token == NULL) - goto error_out2; - // comm - token = strtok(NULL, " "); - if (token == NULL) - goto error_out2; - if (token[0] == '(') - { - while (token[strlen(token) - 1] != ')') - { - token = strtok(NULL, " "); - if (token == NULL) - goto error_out2; - } - } - // state - token = strtok(NULL, " "); - if (token == NULL) - goto error_out2; - if (strcmp(token, "Z") == 0 || strcmp(token, "X") == 0) - goto error_out2; - // ppid - token = strtok(NULL, " "); - if (token == NULL) - goto error_out2; - p->ppid = atoi(token); - int i; - for (i = 0; i < 10; i++) - { - token = strtok(NULL, " "); - if (token == NULL) - goto error_out2; - } - p->cputime = atoi(token) * 1000 / HZ; - token = strtok(NULL, " "); - if (token == NULL) - goto error_out2; - p->cputime += atoi(token) * 1000 / HZ; - for (i = 0; i < 7; i++) - { - token = strtok(NULL, " "); - if (token == NULL) - goto error_out2; - } - p->starttime = atoi(token) / sysconf(_SC_CLK_TCK); + p->cputime = (int)((utime + stime) * 1000 / HZ); + p->starttime = (long)(start_time / sysconf(_SC_CLK_TCK)); return 0; error_out1: From 26ad6c0c93ae1bc9ba2e63a5fac6598e7b831575 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 23 Sep 2022 15:13:59 +0800 Subject: [PATCH 009/209] simplify get_pid_max --- src/cpulimit.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index f5c39a93..894c5175 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -175,17 +175,14 @@ int get_pid_max() { #if defined(__linux__) // read /proc/sys/kernel/pid_max - static char buffer[1024]; - FILE *fd = fopen("/proc/sys/kernel/pid_max", "r"); - if (fd == NULL) - return -1; - if (fgets(buffer, sizeof(buffer), fd) == NULL) + int pid_max = -1; + FILE *fd; + if ((fd = fopen("/proc/sys/kernel/pid_max", "r")) != NULL) { + fscanf(fd, "%d", &pid_max); fclose(fd); - return -1; } - fclose(fd); - return atoi(buffer); + return pid_max; #elif defined(__FreeBSD__) return 99998; #elif defined(__APPLE__) From f363902665a1dac2cc883444c7c218175d956a70 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 23 Sep 2022 14:40:50 +0800 Subject: [PATCH 010/209] clean up code and fix build for macOS and FreeBSD --- src/Makefile | 23 +--- src/cpulimit.c | 193 +++++++++++++++------------------ src/list.h | 14 +-- src/process_group.c | 92 +++++++--------- src/process_group.h | 2 +- src/process_iterator.c | 6 +- src/process_iterator.h | 19 ++-- src/process_iterator_apple.c | 8 +- src/process_iterator_freebsd.c | 10 +- src/process_iterator_linux.c | 20 ++-- tests/Makefile | 10 +- tests/busy.c | 12 +- tests/process_iterator_test.c | 74 ++++++------- tests/run_tests.sh | 1 + 14 files changed, 220 insertions(+), 264 deletions(-) diff --git a/src/Makefile b/src/Makefile index e8107a74..8f9ce8c6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,32 +1,19 @@ CC ?= gcc -CFLAGS ?= -Wall +CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) -Wall -std=gnu89 TARGET := cpulimit -OBJS := cpulimit.o list.o process_iterator.o process_group.o UNAME ?= $(shell uname) ifeq ($(UNAME), FreeBSD) - LDFLAGS += -lkvm +LDFLAGS += -lkvm endif .PHONY: all clean all: $(TARGET) -$(TARGET): $(OBJS) - $(CC) $(LDFLAGS) $^ -o $@ - -cpulimit.o: cpulimit.c process_group.h list.h - $(CC) $(CFLAGS) -c $< -o $@ - -process_iterator.o: process_iterator.c process_iterator.h process_iterator_linux.c process_iterator_freebsd.c process_iterator_apple.c - $(CC) $(CFLAGS) -c $< -o $@ - -list.o: list.c list.h - $(CC) $(CFLAGS) -c $< -o $@ - -process_group.o: process_group.c process_group.h process_iterator.h list.h - $(CC) $(CFLAGS) -c $< -o $@ +$(TARGET): $(wildcard *.c *.h) + $(CC) $(CFLAGS) $(LDFLAGS) $(filter-out process_iterator_%.c, $(filter %.c, $^)) -o $@ clean: - rm -f *~ $(OBJS) $(TARGET) + rm -f *~ $(TARGET) diff --git a/src/cpulimit.c b/src/cpulimit.c index 894c5175..be7b4118 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -29,29 +29,19 @@ #include #include -#include #include #include -#include -#include -#include #include -#include +#include #include -#include #include -#include #include #include #include "process_group.h" #include "list.h" -#ifdef HAVE_SYS_SYSINFO_H -#include -#endif - -// some useful macro +/* some useful macro */ #ifndef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif @@ -63,36 +53,36 @@ #define EPSILON 1e-12 #endif -// control time slot in microseconds -// each slot is splitted in a working slice and a sleeping slice -// TODO: make it adaptive, based on the actual system load +/* control time slot in microseconds */ +/* each slot is splitted in a working slice and a sleeping slice */ +/* TODO: make it adaptive, based on the actual system load */ #define TIME_SLOT 100000 #define MAX_PRIORITY -20 /* GLOBAL VARIABLES */ -// the "family" +/* the "family" */ struct process_group pgroup; -// pid of cpulimit +/* pid of cpulimit */ pid_t cpulimit_pid; -// name of this program (maybe cpulimit...) +/* name of this program (maybe cpulimit...) */ char *program_name; -// number of cpu +/* number of cpu */ int NCPU; /* CONFIGURATION VARIABLES */ -// verbose mode +/* verbose mode */ int verbose = 0; -// lazy mode (exits if there is no process) +/* lazy mode (exits if there is no process) */ int lazy = 0; -// SIGINT and SIGTERM signal handler +/* SIGINT and SIGTERM signal handler */ static void quit(int sig) { - // let all the processes continue if stopped + /* let all the processes continue if stopped */ struct list_node *node = NULL; if (pgroup.proclist != NULL) { @@ -103,18 +93,12 @@ static void quit(int sig) } close_process_group(&pgroup); } - // fix ^C little problem + /* fix ^C little problem */ printf("\r"); fflush(stdout); exit(0); } -// 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 void print_usage(FILE *stream, int exit_code) { fprintf(stream, "Usage: %s [OPTIONS...] TARGET\n", program_name); @@ -134,7 +118,7 @@ static void print_usage(FILE *stream, int exit_code) static void increase_priority() { - // find the best available nice value + /* find the best available nice value */ int old_priority = getpriority(PRIO_PROCESS, 0); int priority = old_priority; while (setpriority(PRIO_PROCESS, 0, priority - 1) == 0 && priority > MAX_PRIORITY) @@ -174,7 +158,7 @@ static int get_ncpu() int get_pid_max() { #if defined(__linux__) - // read /proc/sys/kernel/pid_max + /* read /proc/sys/kernel/pid_max */ int pid_max = -1; FILE *fd; if ((fd = fopen("/proc/sys/kernel/pid_max", "r")) != NULL) @@ -192,40 +176,46 @@ int get_pid_max() void limit_process(pid_t pid, double limit, int include_children) { - // slice of the slot in which the process is allowed to run + /* slice of the slot in which the process is allowed to run */ struct timespec twork; - // slice of the slot in which the process is stopped + /* slice of the slot in which the process is stopped */ struct timespec tsleep; - // when the last twork has started + /* when the last twork has started */ struct timeval startwork; - // when the last twork has finished + /* when the last twork has finished */ struct timeval endwork; - // initialization + /* generic list item */ + struct list_node *node; + /* counter */ + int c = 0; + + /* 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; + + /* initialization */ memset(&twork, 0, sizeof(struct timespec)); memset(&tsleep, 0, sizeof(struct timespec)); memset(&startwork, 0, sizeof(struct timeval)); memset(&endwork, 0, sizeof(struct timeval)); - // last working time in microseconds - // unsigned long workingtime = 0; - // generic list item - struct list_node *node; - // counter - int c = 0; - // get a better priority + /* get a better priority */ increase_priority(); - // build the family + /* build the family */ 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) { + /* total cpu actual usage (range 0-1) */ + /* 1 means that the processes are using 100% cpu */ + double pcpu = -1; + + double twork_total_nsec, tsleep_total_nsec; + update_process_group(&pgroup); if (pgroup.proclist->count == 0) @@ -235,11 +225,7 @@ void limit_process(pid_t pid, double limit, int include_children) break; } - // total cpu actual usage (range 0-1) - // 1 means that the processes are using 100% cpu - double pcpu = -1; - - // estimate how much the controlled processes are using the cpu in the working interval + /* estimate how much the controlled processes are using the cpu in the working interval */ for (node = pgroup.proclist->first; node != NULL; node = node->next) { struct process *proc = (struct process *)(node->data); @@ -252,27 +238,27 @@ void limit_process(pid_t pid, double limit, int include_children) pcpu += proc->cpu_usage; } - // adjust work and sleep time slices + /* adjust work and sleep time slices */ if (pcpu < 0) { - // it's the 1st cycle, initialize workingrate + /* it's the 1st cycle, initialize workingrate */ pcpu = limit; workingrate = limit; } else { - // adjust workingrate + /* adjust workingrate */ workingrate = (workingrate + EPSILON) / (pcpu + EPSILON) * (limit + EPSILON); } workingrate = MAX(MIN(workingrate, 1 - EPSILON), EPSILON); - double twork_total_nsec = (double)TIME_SLOT * 1000 * workingrate; + twork_total_nsec = (double)TIME_SLOT * 1000 * workingrate; twork.tv_sec = (time_t)(twork_total_nsec / 1e9); twork.tv_nsec = (long)(twork_total_nsec - twork.tv_sec * 1e9); - double tsleep_total_nsec = (double)TIME_SLOT * 1000 - twork_total_nsec; + tsleep_total_nsec = (double)TIME_SLOT * 1000 - twork_total_nsec; tsleep.tv_sec = (time_t)(tsleep_total_nsec / 1e9); tsleep.tv_nsec = (long)(tsleep_total_nsec - tsleep.tv_sec * 1e9); @@ -281,10 +267,10 @@ void limit_process(pid_t pid, double limit, int include_children) if (c % 200 == 0) printf("\n %%CPU work quantum sleep quantum active rate\n"); if (c % 10 == 0 && c > 0) - printf("%7.2lf%% %9.0lf us %10.0lf us %10.2lf%%\n", pcpu * 100, twork_total_nsec / 1000, tsleep_total_nsec / 1000, workingrate * 100); + printf("%7.2f%% %9.0f us %10.0f us %10.2f%%\n", pcpu * 100, twork_total_nsec / 1000, tsleep_total_nsec / 1000, workingrate * 100); } - // resume processes + /* resume processes */ node = pgroup.proclist->first; while (node != NULL) { @@ -292,32 +278,24 @@ void limit_process(pid_t pid, double limit, int include_children) struct process *proc = (struct process *)(node->data); if (kill(proc->pid, SIGCONT) != 0) { - // process is dead, remove it from family + /* process is dead, remove it from family */ if (verbose) fprintf(stderr, "SIGCONT failed. Process %d dead!\n", proc->pid); - // remove process from group + /* remove process from group */ delete_node(pgroup.proclist, node); remove_process(&pgroup, proc->pid); } node = next_node; } - // now processes are free to run (same working slice for all) + /* now processes are free to run (same working slice for all) */ gettimeofday(&startwork, NULL); nanosleep(&twork, NULL); gettimeofday(&endwork, NULL); - // workingtime = timediff(&endwork, &startwork); - - // 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 || tsleep.tv_sec > 0) { - // stop processes only if tsleep>0 + /* stop processes only if tsleep>0 */ node = pgroup.proclist->first; while (node != NULL) { @@ -325,16 +303,16 @@ void limit_process(pid_t pid, double limit, int include_children) struct process *proc = (struct process *)(node->data); if (kill(proc->pid, SIGSTOP) != 0) { - // process is dead, remove it from family + /* process is dead, remove it from family */ if (verbose) fprintf(stderr, "SIGSTOP failed. Process %d dead!\n", proc->pid); - // remove process from group + /* remove process from group */ delete_node(pgroup.proclist, node); remove_process(&pgroup, proc->pid); } node = next_node; } - // now the processes are sleeping + /* now the processes are sleeping */ nanosleep(&tsleep, NULL); } c++; @@ -344,7 +322,7 @@ void limit_process(pid_t pid, double limit, int include_children) int main(int argc, char **argv) { - // argument variables + /* argument variables */ const char *exe = NULL; static char exe_name[PATH_MAX + 1]; int perclimit = 0; @@ -353,21 +331,14 @@ int main(int argc, char **argv) int limit_ok = 0; pid_t pid = 0; int include_children = 0; + int command_mode; - // get program name - char *p = strrchr(argv[0], '/'); - program_name = p == NULL ? argv[0] : (p + 1); - // get current pid - cpulimit_pid = getpid(); - // get cpu count - NCPU = get_ncpu(); - - // parse arguments + /* parse arguments */ int next_option; int option_index = 0; - // A string listing valid short options letters + /* A string listing valid short options letters */ const char *short_options = "+p:e:l:vzih"; - // An array describing valid long options + /* An array describing valid long options */ const struct option long_options[] = { {"pid", required_argument, NULL, 'p'}, {"exe", required_argument, NULL, 'e'}, @@ -378,6 +349,16 @@ int main(int argc, char **argv) {"help", no_argument, NULL, 'h'}, {0, 0, 0, 0}}; + double limit; + + /* get program name */ + char *p = strrchr(argv[0], '/'); + program_name = p == NULL ? argv[0] : (p + 1); + /* get current pid */ + cpulimit_pid = getpid(); + /* get cpu count */ + NCPU = get_ncpu(); + do { next_option = getopt_long(argc, argv, short_options, long_options, &option_index); @@ -388,7 +369,7 @@ int main(int argc, char **argv) pid_ok = 1; break; case 'e': - // exe = optarg; + /* exe = optarg; */ *exe_name = '\0'; strncat(exe_name, optarg, PATH_MAX); exe = basename(exe_name); @@ -437,7 +418,7 @@ int main(int argc, char **argv) print_usage(stderr, 1); exit(1); } - double limit = perclimit / 100.0; + limit = perclimit / 100.0; if (limit < 0 || limit > NCPU) { fprintf(stderr, "Error: limit must be in the range 0-%d00\n", NCPU); @@ -445,7 +426,7 @@ int main(int argc, char **argv) exit(1); } - int command_mode = optind < argc; + 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"); @@ -460,20 +441,21 @@ int main(int argc, char **argv) exit(1); } - // all arguments are ok! + /* all arguments are ok! */ signal(SIGINT, quit); signal(SIGTERM, quit); - // print the number of available cpu + /* print the number of available cpu */ if (verbose) printf("%d cpu detected\n", NCPU); if (command_mode) { int i; - // executable file + pid_t child; + /* executable file */ const char *cmd = argv[optind]; - // command line arguments + /* command line arguments */ char **cmd_args = (char **)malloc((argc - optind + 1) * sizeof(char *)); if (cmd_args == NULL) exit(2); @@ -493,31 +475,32 @@ int main(int argc, char **argv) printf("'\n"); } - int child = fork(); + child = fork(); if (child < 0) { exit(EXIT_FAILURE); } else if (child == 0) { - // target process code + /* target process code */ int ret = execvp(cmd, cmd_args); - // if we are here there was an error, show it + /* if we are here there was an error, show it */ perror("Error"); exit(ret); } else { - // parent code + pid_t limiter; + /* parent code */ free(cmd_args); - int limiter = fork(); + limiter = fork(); if (limiter < 0) { exit(EXIT_FAILURE); } else if (limiter > 0) { - // parent + /* parent */ int status_process; int status_limiter; waitpid(child, &status_process, 0); @@ -533,7 +516,7 @@ int main(int argc, char **argv) } else { - // limiter code + /* limiter code */ if (verbose) printf("Limiting process %d\n", child); limit_process(child, limit, include_children); @@ -544,11 +527,11 @@ int main(int argc, char **argv) while (1) { - // look for the target process..or wait for it + /* look for the target process..or wait for it */ pid_t ret = 0; if (pid_ok) { - // search by pid + /* search by pid */ ret = find_process_by_pid(pid); if (ret == 0) { @@ -561,7 +544,7 @@ int main(int argc, char **argv) } else { - // search by file or path name + /* search by file or path name */ ret = find_process_by_name(exe); if (ret == 0) { @@ -584,7 +567,7 @@ int main(int argc, char **argv) exit(1); } printf("Process %d found\n", pid); - // control + /* control */ limit_process(pid, limit, include_children); } if (lazy) diff --git a/src/list.h b/src/list.h index bd169127..39ee90bc 100644 --- a/src/list.h +++ b/src/list.h @@ -30,23 +30,23 @@ struct list_node { - // pointer to the content of the node + /* pointer to the content of the node */ void *data; - // pointer to previous node + /* pointer to previous node */ struct list_node *previous; - // pointer to next node + /* pointer to next node */ struct list_node *next; }; struct list { - // first node + /* first node */ struct list_node *first; - // last node + /* last node */ struct list_node *last; - // size of the search key in bytes + /* size of the search key in bytes */ int keysize; - // element count + /* element count */ int count; }; diff --git a/src/process_group.c b/src/process_group.c index 8e9dc8a4..35f0083c 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -21,7 +21,6 @@ #include #include -#include #include #include #include @@ -32,27 +31,27 @@ #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 +/* 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 +/* 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) { - // pid of the target process + /* pid of the target process */ pid_t pid = -1; - // process iterator + /* process iterator */ struct process_iterator it; struct process proc; struct process_filter filter; @@ -61,15 +60,15 @@ int find_process_by_name(const char *process_name) init_process_iterator(&it, &filter); while (get_next_process(&it, &proc) != -1) { - // process found + /* process found */ if (strcmp(basename(proc.command), process_name) == 0) { if (kill(proc.pid, 0) == -1 && errno == EPERM) { - // do not have permission + /* do not have permission */ return -1; } - // process is ok! + /* process is ok! */ pid = proc.pid; break; } @@ -78,19 +77,19 @@ int find_process_by_name(const char *process_name) exit(1); if (pid >= 0) { - // ok, the process was found + /* ok, the process was found */ return pid; } else { - // process not found + /* process not found */ return 0; } } int init_process_group(struct process_group *pgroup, int target_pid, int include_children) { - // hashtable initialization + /* hashtable initialization */ memset(&pgroup->proctable, 0, sizeof(pgroup->proctable)); pgroup->target_pid = target_pid; pgroup->include_children = include_children; @@ -109,7 +108,7 @@ int close_process_group(struct process_group *pgroup) { if (pgroup->proctable[i] != NULL) { - // free() history for each process + /* free() history for each process */ destroy_list(pgroup->proctable[i]); free(pgroup->proctable[i]); pgroup->proctable[i] = NULL; @@ -121,24 +120,12 @@ int close_process_group(struct process_group *pgroup) 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); -// } - -// returns t1-t2 in millisecond -static inline double timediff_in_ms(const struct timeval *t1, const struct timeval *t2) -{ - return (t1->tv_sec - t2->tv_sec) * 1e3 + (t1->tv_usec - t2->tv_usec) / 1e3; -} +/* returns t1-t2 in millisecond */ +/* static inline double timediff_in_ms(const struct timeval *t1, const struct timeval *t2) */ +#define timediff_in_ms(t1, t2) \ + (((t1)->tv_sec - (t2)->tv_sec) * 1e3 + ((t1)->tv_usec - (t2)->tv_usec) / 1e3) -// parameter in range 0-1 +/* parameter in range 0-1 */ #define ALFA 0.08 #define MIN_DT 20 @@ -148,10 +135,10 @@ void update_process_group(struct process_group *pgroup) struct process tmp_process; struct process_filter filter; struct timeval now; + double dt; gettimeofday(&now, NULL); - // time elapsed from previous sample (in ms) - // long dt = timediff(&now, &pgroup->last_update) / 1000; - double dt = timediff_in_ms(&now, &pgroup->last_update); + /* time elapsed from previous sample (in ms) */ + dt = timediff_in_ms(&now, &pgroup->last_update); filter.pid = pgroup->target_pid; filter.include_children = pgroup->include_children; init_process_iterator(&it, &filter); @@ -160,15 +147,12 @@ void update_process_group(struct process_group *pgroup) 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); if (pgroup->proctable[hashkey] == NULL) { - // empty bucket - pgroup->proctable[hashkey] = malloc(sizeof(struct list)); + /* empty bucket */ struct process *new_process = malloc(sizeof(struct process)); + pgroup->proctable[hashkey] = malloc(sizeof(struct list)); tmp_process.cpu_usage = -1; memcpy(new_process, &tmp_process, sizeof(struct process)); init_list(pgroup->proctable[hashkey], 4); @@ -177,11 +161,11 @@ void update_process_group(struct process_group *pgroup) } else { - // existing bucket + /* existing bucket */ struct process *p = (struct process *)locate_elem(pgroup->proctable[hashkey], &tmp_process); if (p == NULL) { - // process is new. add it + /* 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)); @@ -190,21 +174,22 @@ void update_process_group(struct process_group *pgroup) } else { + double sample; assert(tmp_process.pid == p->pid); 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; + /* process exists. update CPU usage */ + sample = (tmp_process.cputime - p->cputime) / dt; if (p->cpu_usage == -1) { - // initialization + /* initialization */ p->cpu_usage = sample; } else { - // usage adjustment + /* usage adjustment */ p->cpu_usage = (1.0 - ALFA) * p->cpu_usage + ALFA * sample; } p->cputime = tmp_process.cputime; @@ -220,9 +205,10 @@ void update_process_group(struct process_group *pgroup) int remove_process(struct process_group *pgroup, int pid) { int hashkey = pid_hashfn(pid); + struct list_node *node; if (pgroup->proctable[hashkey] == NULL) - return 1; // nothing to delete - struct list_node *node = (struct list_node *)locate_node(pgroup->proctable[hashkey], &pid); + return 1; /* nothing to delete */ + node = (struct list_node *)locate_node(pgroup->proctable[hashkey], &pid); if (node == NULL) return 2; delete_node(pgroup->proctable[hashkey], node); diff --git a/src/process_group.h b/src/process_group.h index f5b3936f..4bf04f8d 100644 --- a/src/process_group.h +++ b/src/process_group.h @@ -32,7 +32,7 @@ struct process_group { - // hashtable with all the processes (array of struct list of struct process) + /* hashtable with all the processes (array of struct list of struct process) */ struct list *proctable[PIDHASH_SZ]; struct list *proclist; pid_t target_pid; diff --git a/src/process_iterator.c b/src/process_iterator.c index 1c771699..c23e065c 100644 --- a/src/process_iterator.c +++ b/src/process_iterator.c @@ -22,14 +22,10 @@ #include #include #include -#ifndef __APPLE__ -#include -#include -#endif #include #include "process_iterator.h" -// See this link to port to other systems: http://www.steve.org.uk/Reference/Unix/faq_8.html#SEC85 +/* See this link to port to other systems: http://www.steve.org.uk/Reference/Unix/faq_8.html#SEC85 */ #if defined(__linux__) diff --git a/src/process_iterator.h b/src/process_iterator.h index 392ed2df..4dcc0a57 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -25,9 +25,12 @@ #include #include +#if defined(__linux__) +#include +#endif #include -// USER_HZ detection, from openssl code +/* USER_HZ detection, from openssl code */ #ifndef HZ #if defined(_SC_CLK_TCK) && (!defined(OPENSSL_SYS_VMS) || __CTRL_VER >= 70000000) #define HZ ((double)sysconf(_SC_CLK_TCK)) @@ -48,20 +51,20 @@ #include #endif -// process descriptor +/* process descriptor */ struct process { - // pid of the process + /* pid of the process */ pid_t pid; - // ppid of the process + /* ppid of the process */ pid_t ppid; - // start time (unix timestamp) + /* start time (unix timestamp) */ time_t starttime; - // cputime used by the process (in milliseconds) + /* cputime used by the process (in milliseconds) */ int cputime; - // actual cpu usage estimation (value in range 0-1) + /* actual cpu usage estimation (value in range 0-1) */ double cpu_usage; - // absolute path of the executable file + /* absolute path of the executable file */ char command[PATH_MAX + 1]; }; diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 9b7c218c..08ddebe9 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -29,6 +29,8 @@ int unique_nonzero_ints(int *arr_in, int len_in, int *arr_out) { int *source = arr_in; + int len_out = 0; + int i, j; if (arr_out == NULL) return -1; if (arr_in == arr_out) @@ -37,8 +39,6 @@ int unique_nonzero_ints(int *arr_in, int len_in, int *arr_out) memcpy(source, arr_in, sizeof(int) * len_in); memset(arr_out, -1, sizeof(int) * len_in); } - int len_out = 0; - int i, j; for (i = 0; i < len_in; i++) { int found = 0; @@ -108,7 +108,7 @@ static int get_process_pti(pid_t pid, struct proc_taskallinfo *ti) } return -1; } - else if (bytes < sizeof(ti)) + else if (bytes < (int)sizeof(ti)) { fprintf(stderr, "proc_pidinfo: too few bytes; expected %ld, got %d\n", sizeof(ti), bytes); return -1; @@ -148,7 +148,7 @@ int get_next_process(struct process_iterator *it, struct process *p) { pti2proc(&ti, p); it->i++; - if (p->pid != it->pidlist[it->i - 1]) // I don't know why this can happen + if (p->pid != it->pidlist[it->i - 1]) /* I don't know why this can happen */ continue; if (p->pid != it->filter->pid && p->ppid != it->filter->pid) continue; diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index 7c9c4a46..05b237da 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -19,7 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include +#include #include #include #include @@ -38,7 +38,6 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi 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; @@ -47,12 +46,12 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi static int kproc2proc(kvm_t *kd, struct kinfo_proc *kproc, struct process *proc) { + char **args; 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) + if ((args = kvm_getargv(kd, kproc, sizeof(proc->command))) == NULL) return -1; memcpy(proc->command, args[0], strlen(args[0]) + 1); return 0; @@ -64,7 +63,6 @@ static int get_single_process(kvm_t *kd, pid_t pid, struct process *process) 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); @@ -92,7 +90,7 @@ int get_next_process(struct process_iterator *it, struct process *p) struct kinfo_proc *kproc = &(it->procs[it->i]); if (kproc->ki_flag & P_SYSTEM) { - // skip system processes + /* skip system processes */ it->i++; continue; } diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index b4be7415..681fce32 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -38,7 +38,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi fprintf(stderr, "procfs is not mounted!\nAborting\n"); exit(-2); } - // open a directory stream to /proc directory + /* open a directory stream to /proc directory */ if ((it->dip = opendir("/proc")) == NULL) { perror("opendir"); @@ -51,12 +51,12 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi static int read_process_info(pid_t pid, struct process *p) { char statfile[32], exefile[32], state; - unsigned long long utime, stime, start_time; + long utime, stime, start_time; FILE *fd; p->pid = pid; - // read command line + /* read command line */ sprintf(exefile, "/proc/%d/cmdline", p->pid); fd = fopen(exefile, "r"); if (fd == NULL) @@ -68,13 +68,13 @@ static int read_process_info(pid_t pid, struct process *p) } fclose(fd); - // read stat file + /* read stat file */ sprintf(statfile, "/proc/%d/stat", p->pid); fd = fopen(statfile, "r"); if (fd == NULL) goto error_out2; - if (fscanf(fd, "%*d (%*[^)]) %c %d %*d %*d %*d %*d %*d %*d %*d %*d %*d %llu %llu %*d %*d %*d %*d %*d %*d %llu", + if (fscanf(fd, "%*d (%*[^)]) %c %d %*d %*d %*d %*d %*d %*d %*d %*d %*d %ld %ld %*d %*d %*d %*d %*d %*d %ld", &state, &p->ppid, &utime, &stime, &start_time) != 5 || state == 'Z' || state == 'X') { @@ -118,9 +118,11 @@ static int is_child_of(pid_t child_pid, pid_t parent_pid) int get_next_process(struct process_iterator *it, struct process *p) { + struct dirent *dit = NULL; + if (it->dip == NULL) { - // end of processes + /* end of processes */ return -1; } if (it->filter->pid != 0 && !it->filter->include_children) @@ -132,8 +134,8 @@ int get_next_process(struct process_iterator *it, struct process *p) return -1; return 0; } - struct dirent *dit = NULL; - // read in from /proc and seek for process dirs + + /* read in from /proc and seek for process dirs */ while ((dit = readdir(it->dip)) != NULL) { if (strtok(dit->d_name, "0123456789") != NULL) @@ -146,7 +148,7 @@ int get_next_process(struct process_iterator *it, struct process *p) } if (dit == NULL) { - // end of processes + /* end of processes */ closedir(it->dip); it->dip = NULL; return -1; diff --git a/tests/Makefile b/tests/Makefile index 2dd3eb59..7ddb2eb6 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,9 +1,9 @@ CC ?= gcc -CFLAGS ?= -Wall +CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) -Wall -std=gnu89 TARGETS = busy process_iterator_test SRC = ../src SYSLIBS ?= -lpthread -LIBS := $(SRC)/list.o $(SRC)/process_iterator.o $(SRC)/process_group.o +LIBS := $(SRC)/list.c $(SRC)/process_iterator.c $(SRC)/process_group.c UNAME := $(shell uname) ifeq ($(UNAME), FreeBSD) @@ -15,10 +15,10 @@ endif all: $(TARGETS) busy: busy.c - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(SYSLIBS) + $(CC) $(CFLAGS) $(LDFLAGS) $^ $(SYSLIBS) -o $@ process_iterator_test: process_iterator_test.c $(LIBS) - $(CC) $(CFLAGS) $(LDFLAGS) -I$(SRC) -o $@ $< $(LIBS) $(SYSLIBS) + $(CC) $(CFLAGS) $(LDFLAGS) -I$(SRC) $^ $(SYSLIBS) -o $@ clean: - rm -f *~ *.o $(TARGETS) + rm -f *~ $(TARGETS) diff --git a/tests/busy.c b/tests/busy.c index b3afb7cd..90c6fb59 100644 --- a/tests/busy.c +++ b/tests/busy.c @@ -5,15 +5,18 @@ void *loop() { - while(1); + while (1) + ; } -int main(int argc, char **argv) { +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 + * Copyright (C) 2005-2012, by: Angelo Marletta * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -45,34 +45,30 @@ void test_single_process() struct process process; struct process_filter filter; int count; - //don't iterate children + /* 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 + /* 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); @@ -84,25 +80,26 @@ void test_multiple_process() struct process_iterator it; struct process process; struct process_filter filter; + int count = 0; pid_t child = fork(); if (child == 0) { - //child is supposed to be killed by the parent :/ + /* 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); + 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); @@ -115,18 +112,17 @@ void test_all_processes() struct process_iterator it; struct process process; struct process_filter filter; + int count = 0; 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++; } @@ -137,11 +133,12 @@ void test_all_processes() 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) { + assert(init_process_group(&pgroup, 0, 0) == 0); + update_process_group(&pgroup); + for (node = pgroup.proclist->first; node != NULL; node = node->next) + { count++; } assert(count > 10); @@ -152,25 +149,28 @@ void test_process_group_all() void test_process_group_single(int include_children) { struct process_group pgroup; + int i; + double tot_usage = 0; child = fork(); if (child == 0) { - //child is supposed to be killed by the parent :/ - while(1); + /* child is supposed to be killed by the parent :/ */ + while (1) + ; exit(1); } signal(SIGABRT, &kill_child); - signal(SIGTERM, &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++) + 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); + struct timespec interval; + update_process_group(&pgroup); + 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); @@ -178,7 +178,6 @@ void test_process_group_single(int include_children) count++; } assert(count == 1); - struct timespec interval; interval.tv_sec = 0; interval.tv_nsec = 50000000; nanosleep(&interval, NULL); @@ -188,7 +187,7 @@ void test_process_group_single(int include_children) kill(child, SIGINT); } -void test_process_name(const char * command) +void test_process_name(const char *command) { struct process_iterator it; struct process process; @@ -199,13 +198,13 @@ void test_process_name(const char * command) assert(get_next_process(&it, &process) == 0); assert(process.pid == getpid()); assert(process.ppid == getppid()); - #ifdef __APPLE__ - // proc_pidinfo only gives us the first 15 chars - // of the basename of the command on OSX. - assert(strncmp(basename((char*)command), process.command, 15) == 0); - #else +#ifdef __APPLE__ + /* proc_pidinfo only gives us the first 15 chars */ + /* of the basename of the command on OSX. */ + assert(strncmp(basename((char *)command), process.command, 15) == 0); +#else assert(strncmp(command, process.command, strlen(process.command)) == 0); - #endif +#endif assert(get_next_process(&it, &process) != 0); close_process_iterator(&it); } @@ -226,7 +225,6 @@ void test_process_group_wrong_pid() int main(int argc, char **argv) { -// printf("Pid %d\n", getpid()); test_single_process(); test_multiple_process(); test_all_processes(); diff --git a/tests/run_tests.sh b/tests/run_tests.sh index e69de29b..8b137891 100644 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -0,0 +1 @@ + From 2cb93d317b3fc7b1b4254b86f71bbf14a08b805f Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 25 Sep 2022 05:08:18 +0800 Subject: [PATCH 011/209] add CI --- .github/workflows/CI.yml | 83 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 .github/workflows/CI.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 00000000..819b6bff --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,83 @@ +name: CI + +on: + push: + branches: [ master ] + workflow_dispatch: + +permissions: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: build + permissions: + contents: read + strategy: + max-parallel: 20 + matrix: + os: [ubuntu-22.04, ubuntu-20.04, ubuntu-18.04, macos-10.15, macos-11, macos-12] + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: master + fetch-depth: 0 + + - name: Compile dynamic + if: ${{ startsWith( matrix.os, 'macos-' ) }} + run: | + make + + - name: Compile static + if: ${{ matrix.os == 'ubuntu-22.04' }} + run: | + sudo -E apt -y -qq update &> /dev/null + sudo -E apt -y -qq install gcc-12 &> /dev/null + sudo -E update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 &> /dev/null + make CFLAGS="-Werror=use-after-free" LDFLAGS="-static" + + - name: Compile static + if: ${{ matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-18.04' }} + run: | + make LDFLAGS="-static" + + - name: Upload + uses: actions/upload-artifact@v3 + with: + name: cpulimit-${{ matrix.os }} + path: src/cpulimit + + build-FreeBSD: + name: build-FreeBSD + permissions: + contents: read + strategy: + max-parallel: 20 + matrix: + osver: ['12.3', '13.0', '13.1'] + runs-on: macos-12 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Build in FreeBSD + uses: vmactions/freebsd-vm@v0 + with: + release: ${{ matrix.osver }} + usesh: true + prepare: | + pkg install -y lang/gcc gmake + run: | + gmake + + - name: Upload + uses: actions/upload-artifact@v3 + with: + name: cpulimit-FreeBSD-${{ matrix.osver }} + path: src/cpulimit From 86120a8ac1ef1ee9d2ed0859a75b0b90ef609dc3 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 25 Dec 2022 01:13:58 +0800 Subject: [PATCH 012/209] simplify Makefile --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 8f9ce8c6..09592a7e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -13,7 +13,7 @@ endif all: $(TARGET) $(TARGET): $(wildcard *.c *.h) - $(CC) $(CFLAGS) $(LDFLAGS) $(filter-out process_iterator_%.c, $(filter %.c, $^)) -o $@ + $(CC) $(CFLAGS) $(LDFLAGS) $(filter-out process_iterator_%.c %.h, $^) -o $@ clean: rm -f *~ $(TARGET) From c2bbf5fdf54c1f2e4cf995ed834928ec1d669fab Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 25 Dec 2022 02:08:08 +0800 Subject: [PATCH 013/209] optimize code --- src/cpulimit.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index be7b4118..711cee06 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -180,10 +180,6 @@ void limit_process(pid_t pid, double limit, int include_children) struct timespec twork; /* slice of the slot in which the process is stopped */ struct timespec tsleep; - /* when the last twork has started */ - struct timeval startwork; - /* when the last twork has finished */ - struct timeval endwork; /* generic list item */ struct list_node *node; /* counter */ @@ -194,10 +190,8 @@ void limit_process(pid_t pid, double limit, int include_children) double workingrate = -1; /* initialization */ - memset(&twork, 0, sizeof(struct timespec)); - memset(&tsleep, 0, sizeof(struct timespec)); - memset(&startwork, 0, sizeof(struct timeval)); - memset(&endwork, 0, sizeof(struct timeval)); + memset(&twork, 0, sizeof(twork)); + memset(&tsleep, 0, sizeof(tsleep)); /* get a better priority */ increase_priority(); @@ -248,9 +242,9 @@ void limit_process(pid_t pid, double limit, int include_children) else { /* adjust workingrate */ - workingrate = (workingrate + EPSILON) / - (pcpu + EPSILON) * - (limit + EPSILON); + workingrate = limit * + workingrate / + (pcpu + EPSILON); } workingrate = MAX(MIN(workingrate, 1 - EPSILON), EPSILON); @@ -289,9 +283,7 @@ void limit_process(pid_t pid, double limit, int include_children) } /* now processes are free to run (same working slice for all) */ - gettimeofday(&startwork, NULL); nanosleep(&twork, NULL); - gettimeofday(&endwork, NULL); if (tsleep.tv_nsec > 0 || tsleep.tv_sec > 0) { From 8d95343103845e9a3c3f4f65cbb759960008b8c5 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 26 Dec 2022 01:16:02 +0800 Subject: [PATCH 014/209] drop starttime attribute of a process --- src/process_group.c | 1 - src/process_iterator.h | 2 -- src/process_iterator_apple.c | 1 - src/process_iterator_freebsd.c | 1 - src/process_iterator_linux.c | 7 +++---- 5 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index 35f0083c..e2121d6a 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -176,7 +176,6 @@ void update_process_group(struct process_group *pgroup) { double sample; assert(tmp_process.pid == p->pid); - assert(tmp_process.starttime == p->starttime); add_elem(pgroup->proclist, p); if (dt < MIN_DT) continue; diff --git a/src/process_iterator.h b/src/process_iterator.h index 4dcc0a57..e7a61157 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -58,8 +58,6 @@ struct process pid_t pid; /* ppid of the process */ pid_t ppid; - /* start time (unix timestamp) */ - time_t starttime; /* cputime used by the process (in milliseconds) */ int cputime; /* actual cpu usage estimation (value in range 0-1) */ diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 08ddebe9..5f8c1e01 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -89,7 +89,6 @@ static int pti2proc(struct proc_taskallinfo *ti, struct process *process) int bytes; process->pid = ti->pbsd.pbi_pid; process->ppid = ti->pbsd.pbi_ppid; - process->starttime = ti->pbsd.pbi_start_tvsec; process->cputime = (ti->ptinfo.pti_total_user + ti->ptinfo.pti_total_system) / 1000000; bytes = strlen(ti->pbsd.pbi_comm); memcpy(process->command, ti->pbsd.pbi_comm, (bytes < PATH_MAX ? bytes : PATH_MAX) + 1); diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index 05b237da..7a536a8c 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -50,7 +50,6 @@ static int 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; if ((args = kvm_getargv(kd, kproc, sizeof(proc->command))) == NULL) return -1; memcpy(proc->command, args[0], strlen(args[0]) + 1); diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 681fce32..28930993 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -51,7 +51,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi static int read_process_info(pid_t pid, struct process *p) { char statfile[32], exefile[32], state; - long utime, stime, start_time; + long utime, stime; FILE *fd; p->pid = pid; @@ -74,8 +74,8 @@ static int read_process_info(pid_t pid, struct process *p) if (fd == NULL) goto error_out2; - if (fscanf(fd, "%*d (%*[^)]) %c %d %*d %*d %*d %*d %*d %*d %*d %*d %*d %ld %ld %*d %*d %*d %*d %*d %*d %ld", - &state, &p->ppid, &utime, &stime, &start_time) != 5 || + if (fscanf(fd, "%*d (%*[^)]) %c %d %*d %*d %*d %*d %*d %*d %*d %*d %*d %ld %ld", + &state, &p->ppid, &utime, &stime) != 4 || state == 'Z' || state == 'X') { fclose(fd); @@ -83,7 +83,6 @@ static int read_process_info(pid_t pid, struct process *p) } fclose(fd); p->cputime = (int)((utime + stime) * 1000 / HZ); - p->starttime = (long)(start_time / sysconf(_SC_CLK_TCK)); return 0; error_out1: From 0499bd91092e5cc6c9820987dd4ca2ecab566dd8 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 27 Dec 2022 23:45:31 +0800 Subject: [PATCH 015/209] optimize pid_t --- src/Makefile | 2 +- src/cpulimit.c | 37 +++++++++++++++++++----------------- src/process_group.c | 12 +++++++----- src/process_group.h | 4 ++-- src/process_iterator.h | 3 ++- src/process_iterator_apple.c | 2 +- src/process_iterator_linux.c | 29 +++++++++++++++------------- 7 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/Makefile b/src/Makefile index 09592a7e..69ab2637 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,5 +1,5 @@ CC ?= gcc -CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) -Wall -std=gnu89 +CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) -Wall -Wextra -std=gnu89 TARGET := cpulimit UNAME ?= $(shell uname) diff --git a/src/cpulimit.c b/src/cpulimit.c index 711cee06..e2bff1ac 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -116,7 +116,7 @@ static void print_usage(FILE *stream, int exit_code) exit(exit_code); } -static void increase_priority() +static void increase_priority(void) { /* find the best available nice value */ int old_priority = getpriority(PRIO_PROCESS, 0); @@ -138,7 +138,7 @@ static void increase_priority() } /* Get the number of CPUs */ -static int get_ncpu() +static int get_ncpu(void) { int ncpu; #if defined(_SC_NPROCESSORS_ONLN) @@ -155,22 +155,22 @@ static int get_ncpu() return ncpu; } -int get_pid_max() +pid_t get_pid_max(void) { #if defined(__linux__) /* read /proc/sys/kernel/pid_max */ - int pid_max = -1; + long pid_max = -1; FILE *fd; if ((fd = fopen("/proc/sys/kernel/pid_max", "r")) != NULL) { - fscanf(fd, "%d", &pid_max); + fscanf(fd, "%ld", &pid_max); fclose(fd); } - return pid_max; + return (pid_t)pid_max; #elif defined(__FreeBSD__) - return 99998; + return (pid_t)99999; #elif defined(__APPLE__) - return 99998; + return (pid_t)99998; #endif } @@ -200,7 +200,8 @@ void limit_process(pid_t pid, double limit, int include_children) 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); + printf("Members in the process group owned by %ld: %ld\n", + (long)pgroup.target_pid, (long)pgroup.proclist->count); while (1) { @@ -274,7 +275,7 @@ void limit_process(pid_t pid, double limit, int include_children) { /* process is dead, remove it from family */ if (verbose) - fprintf(stderr, "SIGCONT failed. Process %d dead!\n", proc->pid); + fprintf(stderr, "SIGCONT failed. Process %ld dead!\n", (long)proc->pid); /* remove process from group */ delete_node(pgroup.proclist, node); remove_process(&pgroup, proc->pid); @@ -297,7 +298,7 @@ void limit_process(pid_t pid, double limit, int include_children) { /* process is dead, remove it from family */ if (verbose) - fprintf(stderr, "SIGSTOP failed. Process %d dead!\n", proc->pid); + fprintf(stderr, "SIGSTOP failed. Process %ld dead!\n", (long)proc->pid); /* remove process from group */ delete_node(pgroup.proclist, node); remove_process(&pgroup, proc->pid); @@ -357,7 +358,7 @@ int main(int argc, char **argv) switch (next_option) { case 'p': - pid = atoi(optarg); + pid = (pid_t)atol(optarg); pid_ok = 1; break; case 'e': @@ -500,17 +501,18 @@ int main(int argc, char **argv) if (WIFEXITED(status_process)) { if (verbose) - printf("Process %d terminated with exit status %d\n", child, (int)WEXITSTATUS(status_process)); + printf("Process %ld terminated with exit status %d\n", + (long)child, (int)WEXITSTATUS(status_process)); exit(WEXITSTATUS(status_process)); } - printf("Process %d terminated abnormally\n", child); + printf("Process %ld terminated abnormally\n", (long)child); exit(status_process); } else { /* limiter code */ if (verbose) - printf("Limiting process %d\n", child); + printf("Limiting process %ld\n", (long)child); limit_process(child, limit, include_children); exit(0); } @@ -555,10 +557,11 @@ int main(int argc, char **argv) { if (ret == cpulimit_pid) { - printf("Target process %d is cpulimit itself! Aborting because it makes no sense\n", ret); + printf("Target process %ld is cpulimit itself! Aborting because it makes no sense\n", + (long)ret); exit(1); } - printf("Process %d found\n", pid); + printf("Process %ld found\n", (long)pid); /* control */ limit_process(pid, limit, include_children); } diff --git a/src/process_group.c b/src/process_group.c index e2121d6a..9291640c 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "process_iterator.h" #include "process_group.h" @@ -35,7 +37,7 @@ 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) +pid_t find_process_by_pid(pid_t pid) { return (kill(pid, 0) == 0) ? pid : -pid; } @@ -46,7 +48,7 @@ process: the name of the wanted process. it can be an absolute path name to the 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) +pid_t find_process_by_name(const char *process_name) { /* pid of the target process */ pid_t pid = -1; @@ -94,7 +96,7 @@ int init_process_group(struct process_group *pgroup, int target_pid, int include pgroup->target_pid = target_pid; pgroup->include_children = include_children; pgroup->proclist = (struct list *)malloc(sizeof(struct list)); - init_list(pgroup->proclist, 4); + init_list(pgroup->proclist, sizeof(pid_t)); memset(&pgroup->last_update, 0, sizeof(pgroup->last_update)); update_process_group(pgroup); return 0; @@ -143,7 +145,7 @@ void update_process_group(struct process_group *pgroup) filter.include_children = pgroup->include_children; init_process_iterator(&it, &filter); clear_list(pgroup->proclist); - init_list(pgroup->proclist, 4); + init_list(pgroup->proclist, sizeof(pid_t)); while (get_next_process(&it, &tmp_process) != -1) { @@ -155,7 +157,7 @@ void update_process_group(struct process_group *pgroup) pgroup->proctable[hashkey] = malloc(sizeof(struct list)); tmp_process.cpu_usage = -1; memcpy(new_process, &tmp_process, sizeof(struct process)); - init_list(pgroup->proctable[hashkey], 4); + init_list(pgroup->proctable[hashkey], sizeof(pid_t)); add_elem(pgroup->proctable[hashkey], new_process); add_elem(pgroup->proclist, new_process); } diff --git a/src/process_group.h b/src/process_group.h index 4bf04f8d..0ce968dc 100644 --- a/src/process_group.h +++ b/src/process_group.h @@ -46,9 +46,9 @@ void update_process_group(struct process_group *pgroup); int close_process_group(struct process_group *pgroup); -int find_process_by_pid(pid_t pid); +pid_t find_process_by_pid(pid_t pid); -int find_process_by_name(const char *process_name); +pid_t find_process_by_name(const char *process_name); int remove_process(struct process_group *pgroup, int pid); diff --git a/src/process_iterator.h b/src/process_iterator.h index e7a61157..d4722fb6 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -23,6 +23,7 @@ #define __PROCESS_ITERATOR_H +#include #include #include #if defined(__linux__) @@ -68,7 +69,7 @@ struct process struct process_filter { - int pid; + pid_t pid; int include_children; char program_name[PATH_MAX + 1]; }; diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 5f8c1e01..f72164c2 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -26,7 +26,7 @@ #include #include -int unique_nonzero_ints(int *arr_in, int len_in, int *arr_out) +static int unique_nonzero_ints(int *arr_in, int len_in, int *arr_out) { int *source = arr_in; int len_out = 0; diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 28930993..0021f567 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -21,7 +21,7 @@ #include -static int check_proc() +static int check_proc(void) { struct statfs mnt; if (statfs("/proc", &mnt) < 0) @@ -51,13 +51,13 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi static int read_process_info(pid_t pid, struct process *p) { char statfile[32], exefile[32], state; - long utime, stime; + long utime, stime, ppid; FILE *fd; p->pid = pid; /* read command line */ - sprintf(exefile, "/proc/%d/cmdline", p->pid); + sprintf(exefile, "/proc/%ld/cmdline", (long)p->pid); fd = fopen(exefile, "r"); if (fd == NULL) goto error_out1; @@ -69,19 +69,20 @@ static int read_process_info(pid_t pid, struct process *p) fclose(fd); /* read stat file */ - sprintf(statfile, "/proc/%d/stat", p->pid); + sprintf(statfile, "/proc/%ld/stat", (long)p->pid); fd = fopen(statfile, "r"); if (fd == NULL) goto error_out2; - if (fscanf(fd, "%*d (%*[^)]) %c %d %*d %*d %*d %*d %*d %*d %*d %*d %*d %ld %ld", - &state, &p->ppid, &utime, &stime) != 4 || + if (fscanf(fd, "%*d (%*[^)]) %c %ld %*d %*d %*d %*d %*d %*d %*d %*d %*d %ld %ld", + &state, &ppid, &utime, &stime) != 4 || state == 'Z' || state == 'X') { fclose(fd); goto error_out2; } fclose(fd); + p->ppid = (pid_t)ppid; p->cputime = (int)((utime + stime) * 1000 / HZ); return 0; @@ -95,19 +96,19 @@ static pid_t getppid_of(pid_t pid) { char statfile[32]; FILE *fd; - pid_t parent_pid = -1; - sprintf(statfile, "/proc/%d/stat", pid); + long ppid = -1; + sprintf(statfile, "/proc/%ld/stat", (long)pid); if ((fd = fopen(statfile, "r")) != NULL) { - fscanf(fd, "%*d (%*[^)]) %*c %d", &parent_pid); + fscanf(fd, "%*d (%*[^)]) %*c %ld", &ppid); fclose(fd); } - return parent_pid; + return (pid_t)ppid; } static int is_child_of(pid_t child_pid, pid_t parent_pid) { - int ppid = child_pid; + pid_t ppid = child_pid; while (ppid > 1 && ppid != parent_pid) { ppid = getppid_of(ppid); @@ -139,8 +140,10 @@ int get_next_process(struct process_iterator *it, struct process *p) { 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)) + p->pid = (pid_t)atol(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); break; From 83911daed16d378a3dc7a5c4438184619483e262 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 2 Jan 2023 02:43:25 +0800 Subject: [PATCH 016/209] make code more readable --- src/cpulimit.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index e2bff1ac..5564254a 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -49,6 +49,14 @@ #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif +/* inline void nsec2timespec(double nsec, struct timespec *t); */ +#define nsec2timespec(nsec, t) \ + do \ + { \ + (t)->tv_sec = (time_t)((nsec) / 1e9); \ + (t)->tv_nsec = (long)((nsec) - (t)->tv_sec * 1e9); \ + } while (0) + #ifndef EPSILON #define EPSILON 1e-12 #endif @@ -250,12 +258,10 @@ void limit_process(pid_t pid, double limit, int include_children) workingrate = MAX(MIN(workingrate, 1 - EPSILON), EPSILON); twork_total_nsec = (double)TIME_SLOT * 1000 * workingrate; - twork.tv_sec = (time_t)(twork_total_nsec / 1e9); - twork.tv_nsec = (long)(twork_total_nsec - twork.tv_sec * 1e9); + nsec2timespec(twork_total_nsec, &twork); tsleep_total_nsec = (double)TIME_SLOT * 1000 - twork_total_nsec; - tsleep.tv_sec = (time_t)(tsleep_total_nsec / 1e9); - tsleep.tv_nsec = (long)(tsleep_total_nsec - tsleep.tv_sec * 1e9); + nsec2timespec(tsleep_total_nsec, &tsleep); if (verbose) { From ae27ff8af61a4f74c9684ee033b8da23e0081d64 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 2 Jan 2023 04:33:11 +0800 Subject: [PATCH 017/209] supress unused parameter warning --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 69ab2637..d9bbab54 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,5 +1,5 @@ CC ?= gcc -CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) -Wall -Wextra -std=gnu89 +CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) -Wall -Wextra -Wno-unused-parameter -std=gnu89 TARGET := cpulimit UNAME ?= $(shell uname) From 17edfd2b9df58ce4c57071ed5fbc16a2729e4e7c Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 2 Jan 2023 05:37:48 +0800 Subject: [PATCH 018/209] use clock_gettime to get time --- src/process_group.c | 8 ++++---- src/process_group.h | 4 +++- tests/Makefile | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index 9291640c..fbf952fa 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -123,9 +123,9 @@ int close_process_group(struct process_group *pgroup) } /* returns t1-t2 in millisecond */ -/* static inline double timediff_in_ms(const struct timeval *t1, const struct timeval *t2) */ +/* static inline double timediff_in_ms(const struct timespec *t1, const struct timespec *t2) */ #define timediff_in_ms(t1, t2) \ - (((t1)->tv_sec - (t2)->tv_sec) * 1e3 + ((t1)->tv_usec - (t2)->tv_usec) / 1e3) + (((t1)->tv_sec - (t2)->tv_sec) * 1e3 + ((t1)->tv_nsec - (t2)->tv_nsec) / 1e6) /* parameter in range 0-1 */ #define ALFA 0.08 @@ -136,9 +136,9 @@ void update_process_group(struct process_group *pgroup) struct process_iterator it; struct process tmp_process; struct process_filter filter; - struct timeval now; + struct timespec now; double dt; - gettimeofday(&now, NULL); + clock_gettime(CLOCK_MONOTONIC, &now); /* time elapsed from previous sample (in ms) */ dt = timediff_in_ms(&now, &pgroup->last_update); filter.pid = pgroup->target_pid; diff --git a/src/process_group.h b/src/process_group.h index 0ce968dc..a0a2c50a 100644 --- a/src/process_group.h +++ b/src/process_group.h @@ -23,6 +23,8 @@ #define __PROCESS_GROUP_H +#include + #include "process_iterator.h" #include "list.h" @@ -37,7 +39,7 @@ struct process_group struct list *proclist; pid_t target_pid; int include_children; - struct timeval last_update; + struct timespec last_update; }; int init_process_group(struct process_group *pgroup, int target_pid, int include_children); diff --git a/tests/Makefile b/tests/Makefile index 7ddb2eb6..fbaacd46 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,5 +1,5 @@ CC ?= gcc -CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) -Wall -std=gnu89 +CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) -Wall -Wextra -std=gnu89 TARGETS = busy process_iterator_test SRC = ../src SYSLIBS ?= -lpthread From f8a3235247fc72bd30c0175ffec3baf25584413a Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 2 Jan 2023 06:13:21 +0800 Subject: [PATCH 019/209] fix int overflow --- src/cpulimit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 5564254a..a2fc04a2 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -314,7 +314,7 @@ void limit_process(pid_t pid, double limit, int include_children) /* now the processes are sleeping */ nanosleep(&tsleep, NULL); } - c++; + c = (c + 1) % 200; } close_process_group(&pgroup); } From b18a236cd6db9b562a7105d0c266881a5ddb9338 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 2 Jan 2023 06:14:39 +0800 Subject: [PATCH 020/209] drop unnecessary cast --- src/cpulimit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index a2fc04a2..59420f0a 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -208,8 +208,8 @@ void limit_process(pid_t pid, double limit, int include_children) init_process_group(&pgroup, pid, include_children); if (verbose) - printf("Members in the process group owned by %ld: %ld\n", - (long)pgroup.target_pid, (long)pgroup.proclist->count); + printf("Members in the process group owned by %ld: %d\n", + (long)pgroup.target_pid, pgroup.proclist->count); while (1) { From 0dcf4d2317dde34c08f910626bc3e27d35695fa0 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 2 Jan 2023 06:26:51 +0800 Subject: [PATCH 021/209] drop unnecessary memset --- src/cpulimit.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 59420f0a..172379fa 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -197,10 +197,6 @@ void limit_process(pid_t pid, double limit, int include_children) /* 1 means that the process are using all the twork slice */ double workingrate = -1; - /* initialization */ - memset(&twork, 0, sizeof(twork)); - memset(&tsleep, 0, sizeof(tsleep)); - /* get a better priority */ increase_priority(); From 30ab492f7cc6c35d8b7733e4f46b2fece10b489a Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 2 Jan 2023 23:50:26 +0800 Subject: [PATCH 022/209] use macro PROC_SUPER_MAGIC --- src/process_iterator_linux.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 0021f567..a075d7f7 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -20,13 +20,14 @@ */ #include +#include static int check_proc(void) { struct statfs mnt; if (statfs("/proc", &mnt) < 0) return 0; - if (mnt.f_type != 0x9fa0) + if (mnt.f_type != PROC_SUPER_MAGIC) return 0; return 1; } From 23aea966bf03aaee51cd4442f6e2d282f6928b4f Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 2 Jan 2023 23:51:23 +0800 Subject: [PATCH 023/209] fix potential int overflow --- src/process_iterator_linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index a075d7f7..8162f8af 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -84,7 +84,7 @@ static int read_process_info(pid_t pid, struct process *p) } fclose(fd); p->ppid = (pid_t)ppid; - p->cputime = (int)((utime + stime) * 1000 / HZ); + p->cputime = (int)((utime + stime) * 1000.0 / HZ); return 0; error_out1: From 95c20b7830720f5c355e50a96f9868c7749396b5 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 3 Jan 2023 02:30:24 +0800 Subject: [PATCH 024/209] fix compiler warnings --- src/Makefile | 7 +++++-- src/cpulimit.c | 9 ++++++--- src/process_group.c | 1 - src/process_group.h | 4 ++++ tests/Makefile | 3 ++- tests/process_iterator_test.c | 1 - 6 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Makefile b/src/Makefile index d9bbab54..9e9921dd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,9 +1,12 @@ CC ?= gcc -CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) -Wall -Wextra -Wno-unused-parameter -std=gnu89 +CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ + -D_GNU_SOURCE -std=gnu89 \ + -Wall -Wextra -pedantic \ + -Wmissing-prototypes -Wstrict-prototypes \ + -Wold-style-definition TARGET := cpulimit UNAME ?= $(shell uname) - ifeq ($(UNAME), FreeBSD) LDFLAGS += -lkvm endif diff --git a/src/cpulimit.c b/src/cpulimit.c index 172379fa..00790442 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -36,7 +36,6 @@ #include #include #include -#include #include "process_group.h" #include "list.h" @@ -88,7 +87,11 @@ int verbose = 0; int lazy = 0; /* SIGINT and SIGTERM signal handler */ +#ifdef __GNUC__ +static void quit(__attribute__((__unused__)) int sig) +#else static void quit(int sig) +#endif { /* let all the processes continue if stopped */ struct list_node *node = NULL; @@ -163,7 +166,7 @@ static int get_ncpu(void) return ncpu; } -pid_t get_pid_max(void) +static pid_t get_pid_max(void) { #if defined(__linux__) /* read /proc/sys/kernel/pid_max */ @@ -182,7 +185,7 @@ pid_t get_pid_max(void) #endif } -void limit_process(pid_t pid, double limit, int include_children) +static 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; diff --git a/src/process_group.c b/src/process_group.c index fbf952fa..157bdf30 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff --git a/src/process_group.h b/src/process_group.h index a0a2c50a..8436a667 100644 --- a/src/process_group.h +++ b/src/process_group.h @@ -24,6 +24,7 @@ #define __PROCESS_GROUP_H #include +#include #include "process_iterator.h" @@ -32,6 +33,9 @@ #define PIDHASH_SZ 1024 #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1)) +#define basename(x) \ + ((strrchr((x), '/') != NULL) ? (strrchr((x), '/') + 1) : (x)) + struct process_group { /* hashtable with all the processes (array of struct list of struct process) */ diff --git a/tests/Makefile b/tests/Makefile index fbaacd46..fef16e83 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,5 +1,6 @@ CC ?= gcc -CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) -Wall -Wextra -std=gnu89 +CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ + -D_GNU_SOURCE -std=gnu89 TARGETS = busy process_iterator_test SRC = ../src SYSLIBS ?= -lpthread diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index 7a23446f..7591dade 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include From 13645d0b513a9b3ed5949dd0fa2998a972ebef7a Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 4 Jan 2023 02:49:05 +0800 Subject: [PATCH 025/209] improve Makefiles --- src/Makefile | 6 +++++- tests/Makefile | 8 ++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index 9e9921dd..dc737873 100644 --- a/src/Makefile +++ b/src/Makefile @@ -6,9 +6,13 @@ CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ -Wold-style-definition TARGET := cpulimit +ifeq ($(shell echo "int main(void){return 0;}" | $(CC) -x c - -lrt -o /dev/null 2>&1),) + LDFLAGS += -lrt +endif + UNAME ?= $(shell uname) ifeq ($(UNAME), FreeBSD) -LDFLAGS += -lkvm + LDFLAGS += -lkvm endif .PHONY: all clean diff --git a/tests/Makefile b/tests/Makefile index fef16e83..979bef60 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -5,10 +5,14 @@ TARGETS = busy process_iterator_test SRC = ../src SYSLIBS ?= -lpthread LIBS := $(SRC)/list.c $(SRC)/process_iterator.c $(SRC)/process_group.c -UNAME := $(shell uname) +ifeq ($(shell echo "int main(void){return 0;}" | $(CC) -x c - -lrt -o /dev/null 2>&1),) + LDFLAGS += -lrt +endif + +UNAME ?= $(shell uname) ifeq ($(UNAME), FreeBSD) - LDFLAGS += -lkvm + LDFLAGS += -lkvm endif .PHONY: all clean From 44cb66d4f9a8121553d3afe60be2f0a2fc33739c Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 16 Jan 2023 02:14:55 +0800 Subject: [PATCH 026/209] simplify getting program_name --- src/cpulimit.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 00790442..672a4f0f 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -318,7 +318,7 @@ static void limit_process(pid_t pid, double limit, int include_children) close_process_group(&pgroup); } -int main(int argc, char **argv) +int main(int argc, char *argv[]) { /* argument variables */ const char *exe = NULL; @@ -350,8 +350,7 @@ int main(int argc, char **argv) double limit; /* get program name */ - char *p = strrchr(argv[0], '/'); - program_name = p == NULL ? argv[0] : (p + 1); + program_name = basename(argv[0]); /* get current pid */ cpulimit_pid = getpid(); /* get cpu count */ From c44a78053223e4ebeba97721632142ab0cd0ec6a Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 16 Jan 2023 11:44:07 +0800 Subject: [PATCH 027/209] fix pid_t type --- src/process_group.c | 4 ++-- src/process_group.h | 4 ++-- src/process_iterator.h | 2 +- src/process_iterator_apple.c | 14 +++++++------- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index 157bdf30..946d799c 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -88,7 +88,7 @@ pid_t find_process_by_name(const char *process_name) } } -int init_process_group(struct process_group *pgroup, int target_pid, int include_children) +int init_process_group(struct process_group *pgroup, pid_t target_pid, int include_children) { /* hashtable initialization */ memset(&pgroup->proctable, 0, sizeof(pgroup->proctable)); @@ -202,7 +202,7 @@ void update_process_group(struct process_group *pgroup) pgroup->last_update = now; } -int remove_process(struct process_group *pgroup, int pid) +int remove_process(struct process_group *pgroup, pid_t pid) { int hashkey = pid_hashfn(pid); struct list_node *node; diff --git a/src/process_group.h b/src/process_group.h index 8436a667..fec71db9 100644 --- a/src/process_group.h +++ b/src/process_group.h @@ -46,7 +46,7 @@ struct process_group struct timespec last_update; }; -int init_process_group(struct process_group *pgroup, int target_pid, int include_children); +int init_process_group(struct process_group *pgroup, pid_t target_pid, int include_children); void update_process_group(struct process_group *pgroup); @@ -56,6 +56,6 @@ pid_t find_process_by_pid(pid_t pid); pid_t find_process_by_name(const char *process_name); -int remove_process(struct process_group *pgroup, int pid); +int remove_process(struct process_group *pgroup, pid_t pid); #endif diff --git a/src/process_iterator.h b/src/process_iterator.h index d4722fb6..efa620c1 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -86,7 +86,7 @@ struct process_iterator #elif defined(__APPLE__) int i; int count; - int *pidlist; + pid_t *pidlist; #endif struct process_filter *filter; }; diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index f72164c2..b70e67da 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -26,18 +26,18 @@ #include #include -static int unique_nonzero_ints(int *arr_in, int len_in, int *arr_out) +static int unique_nonzero_pids(pid_t *arr_in, int len_in, pid_t *arr_out) { - int *source = arr_in; + pid_t *source = arr_in; int len_out = 0; int i, j; if (arr_out == NULL) return -1; if (arr_in == arr_out) { - source = malloc(sizeof(int) * len_in); - memcpy(source, arr_in, sizeof(int) * len_in); - memset(arr_out, -1, sizeof(int) * len_in); + source = malloc(sizeof(pid_t) * len_in); + memcpy(source, arr_in, sizeof(pid_t) * len_in); + memset(arr_out, -1, sizeof(pid_t) * len_in); } for (i = 0; i < len_in; i++) { @@ -70,7 +70,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi return -1; } /* Allocate and populate it->pidlist */ - if ((it->pidlist = (int *)malloc((it->count) * sizeof(int))) == NULL) + if ((it->pidlist = malloc((it->count) * sizeof(pid_t))) == NULL) { fprintf(stderr, "malloc: %s\n", strerror(errno)); } @@ -79,7 +79,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); return -1; } - it->count = unique_nonzero_ints(it->pidlist, it->count, it->pidlist); + it->count = unique_nonzero_pids(it->pidlist, it->count, it->pidlist); it->filter = filter; return 0; } From ece694122426e3ac235955ba8b7867cea2137ec7 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 16 Jan 2023 11:49:59 +0800 Subject: [PATCH 028/209] remove unnecessary cast for malloc --- src/cpulimit.c | 2 +- src/list.c | 2 +- src/process_group.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 672a4f0f..793185fa 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -453,7 +453,7 @@ int main(int argc, char *argv[]) /* executable file */ const char *cmd = argv[optind]; /* command line arguments */ - char **cmd_args = (char **)malloc((argc - optind + 1) * sizeof(char *)); + char **cmd_args = malloc((argc - optind + 1) * sizeof(char *)); if (cmd_args == NULL) exit(2); for (i = 0; i < argc - optind; i++) diff --git a/src/list.c b/src/list.c index ad316b77..04ed275a 100644 --- a/src/list.c +++ b/src/list.c @@ -35,7 +35,7 @@ void init_list(struct list *l, int keysize) struct list_node *add_elem(struct list *l, void *elem) { - struct list_node *newnode = (struct list_node *)malloc(sizeof(struct list_node)); + struct list_node *newnode = malloc(sizeof(struct list_node)); newnode->data = elem; newnode->previous = l->last; newnode->next = NULL; diff --git a/src/process_group.c b/src/process_group.c index 946d799c..3cb40843 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -94,7 +94,7 @@ int init_process_group(struct process_group *pgroup, pid_t target_pid, int inclu 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)); + pgroup->proclist = malloc(sizeof(struct list)); init_list(pgroup->proclist, sizeof(pid_t)); memset(&pgroup->last_update, 0, sizeof(pgroup->last_update)); update_process_group(pgroup); From 1e4b863325add5589956f7c886d339bf69dd9dc8 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 16 Jan 2023 13:10:11 +0800 Subject: [PATCH 029/209] copy string using strncat --- src/process_iterator_apple.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index b70e67da..35006677 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -25,6 +25,7 @@ #include #include #include +#include static int unique_nonzero_pids(pid_t *arr_in, int len_in, pid_t *arr_out) { @@ -86,12 +87,11 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi static int pti2proc(struct proc_taskallinfo *ti, struct process *process) { - int bytes; process->pid = ti->pbsd.pbi_pid; process->ppid = ti->pbsd.pbi_ppid; process->cputime = (ti->ptinfo.pti_total_user + ti->ptinfo.pti_total_system) / 1000000; - bytes = strlen(ti->pbsd.pbi_comm); - memcpy(process->command, ti->pbsd.pbi_comm, (bytes < PATH_MAX ? bytes : PATH_MAX) + 1); + process->command[0] = '\0'; + strncat(process->command, ti->pbsd.pbi_comm, MIN(PATH_MAX, MAXCOMLEN)); return 0; } From 35e2f6067b3df60d6536cacdb04a1d927ab1cba5 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 16 Jan 2023 13:33:43 +0800 Subject: [PATCH 030/209] remove deprecated ubuntu-18.04 environment in workflow --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 819b6bff..349d8c0e 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -19,7 +19,7 @@ jobs: strategy: max-parallel: 20 matrix: - os: [ubuntu-22.04, ubuntu-20.04, ubuntu-18.04, macos-10.15, macos-11, macos-12] + os: [ubuntu-22.04, ubuntu-20.04, macos-10.15, macos-11, macos-12] runs-on: ${{ matrix.os }} steps: @@ -43,7 +43,7 @@ jobs: make CFLAGS="-Werror=use-after-free" LDFLAGS="-static" - name: Compile static - if: ${{ matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-18.04' }} + if: ${{ matrix.os == 'ubuntu-20.04' }} run: | make LDFLAGS="-static" From 0c0402907f5685ae4be86852285f918cac573764 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 17 Jan 2023 02:00:20 +0800 Subject: [PATCH 031/209] simplify handling exe name --- src/cpulimit.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 793185fa..a37d1c4b 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -322,7 +322,6 @@ int main(int argc, char *argv[]) { /* argument variables */ const char *exe = NULL; - static char exe_name[PATH_MAX + 1]; int perclimit = 0; int exe_ok = 0; int pid_ok = 0; @@ -366,10 +365,7 @@ int main(int argc, char *argv[]) pid_ok = 1; break; case 'e': - /* exe = optarg; */ - *exe_name = '\0'; - strncat(exe_name, optarg, PATH_MAX); - exe = basename(exe_name); + exe = basename(optarg); exe_ok = 1; break; case 'l': From d14781f85ca599a261314258b97007558c004d4d Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 17 Jan 2023 15:56:06 +0800 Subject: [PATCH 032/209] use return 0 instead of exit(0) at the end of main() --- src/cpulimit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index a37d1c4b..1b3d5887 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -570,5 +570,5 @@ int main(int argc, char *argv[]) sleep(2); }; - exit(0); + return 0; } From faf5fa22d16eb313f0e66015a9d0042525583132 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 17 Jan 2023 15:11:03 +0800 Subject: [PATCH 033/209] use CLOCK_MONOTONIC for sleep function calls if available --- src/cpulimit.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 1b3d5887..ef426cd7 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -56,6 +56,15 @@ (t)->tv_nsec = (long)((nsec) - (t)->tv_sec * 1e9); \ } while (0) +/* inline int sleep_timespec(struct timespec *t); */ +#if _POSIX_C_SOURCE >= 200809L +#define sleep_timespec(t) \ + (clock_nanosleep(CLOCK_MONOTONIC, 0, (t), NULL)) +#else +#define sleep_timespec(t) \ + (nanosleep((t), NULL)) +#endif + #ifndef EPSILON #define EPSILON 1e-12 #endif @@ -289,7 +298,7 @@ static void limit_process(pid_t pid, double limit, int include_children) } /* now processes are free to run (same working slice for all) */ - nanosleep(&twork, NULL); + sleep_timespec(&twork); if (tsleep.tv_nsec > 0 || tsleep.tv_sec > 0) { @@ -311,7 +320,7 @@ static void limit_process(pid_t pid, double limit, int include_children) node = next_node; } /* now the processes are sleeping */ - nanosleep(&tsleep, NULL); + sleep_timespec(&tsleep); } c = (c + 1) % 200; } @@ -348,6 +357,8 @@ int main(int argc, char *argv[]) double limit; + struct timespec wait_time = {2, 0}; + /* get program name */ program_name = basename(argv[0]); /* get current pid */ @@ -567,7 +578,8 @@ int main(int argc, char *argv[]) } if (lazy) break; - sleep(2); + /* wait for 2 seconds before next search */ + sleep_timespec(&wait_time); }; return 0; From 7a0e57ea21aa9bd681f611fa64ac7355f042d342 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 17 Jan 2023 20:02:27 +0800 Subject: [PATCH 034/209] use clock_gettime only when available --- src/process_group.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/process_group.c b/src/process_group.c index 3cb40843..2ccda5ee 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -32,6 +32,24 @@ #include "process_group.h" #include "list.h" +#if _POSIX_C_SOURCE >= 199309L +#define get_time(ts) \ + (clock_gettime(CLOCK_MONOTONIC, (ts))) +#else +static int get_time(struct timespec *ts) +{ + struct timeval tv; + memset(ts, 0, sizeof(*ts)); + if (gettimeofday(&tv, NULL)) + { + return -1; + } + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000; + return 0; +} +#endif + /* look for a process by pid search_pid : pid of the wanted process return: pid of the found process, if successful @@ -137,7 +155,10 @@ void update_process_group(struct process_group *pgroup) struct process_filter filter; struct timespec now; double dt; - clock_gettime(CLOCK_MONOTONIC, &now); + if (get_time(&now)) + { + exit(1); + } /* time elapsed from previous sample (in ms) */ dt = timediff_in_ms(&now, &pgroup->last_update); filter.pid = pgroup->target_pid; From 0ca578d6470a6f1d7e6fe620f3c53f8174a65b2a Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 17 Jan 2023 21:11:19 +0800 Subject: [PATCH 035/209] Makefile: allow overriding CFLAGSand LDFLAGS on the command line --- src/Makefile | 8 ++++---- tests/Makefile | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Makefile b/src/Makefile index dc737873..d0c09e97 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,5 +1,5 @@ CC ?= gcc -CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ +override CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ -D_GNU_SOURCE -std=gnu89 \ -Wall -Wextra -pedantic \ -Wmissing-prototypes -Wstrict-prototypes \ @@ -7,12 +7,12 @@ CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ TARGET := cpulimit ifeq ($(shell echo "int main(void){return 0;}" | $(CC) -x c - -lrt -o /dev/null 2>&1),) - LDFLAGS += -lrt + override LDFLAGS += -lrt endif -UNAME ?= $(shell uname) +override UNAME ?= $(shell uname) ifeq ($(UNAME), FreeBSD) - LDFLAGS += -lkvm + override LDFLAGS += -lkvm endif .PHONY: all clean diff --git a/tests/Makefile b/tests/Makefile index 979bef60..e13d704a 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,5 +1,5 @@ CC ?= gcc -CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ +override CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ -D_GNU_SOURCE -std=gnu89 TARGETS = busy process_iterator_test SRC = ../src @@ -7,12 +7,12 @@ SYSLIBS ?= -lpthread LIBS := $(SRC)/list.c $(SRC)/process_iterator.c $(SRC)/process_group.c ifeq ($(shell echo "int main(void){return 0;}" | $(CC) -x c - -lrt -o /dev/null 2>&1),) - LDFLAGS += -lrt + override LDFLAGS += -lrt endif -UNAME ?= $(shell uname) +override UNAME ?= $(shell uname) ifeq ($(UNAME), FreeBSD) - LDFLAGS += -lkvm + override LDFLAGS += -lkvm endif .PHONY: all clean From 86df4235b3dc6137dfec535f2a242874f2130225 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 18 Jan 2023 02:34:31 +0800 Subject: [PATCH 036/209] fix compiler warning in test code --- tests/Makefile | 5 ++++- tests/busy.c | 31 +++++++++++++++++++++++++++---- tests/process_iterator_test.c | 30 +++++++++++++++++++----------- 3 files changed, 50 insertions(+), 16 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index e13d704a..03e28494 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,6 +1,9 @@ CC ?= gcc override CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ - -D_GNU_SOURCE -std=gnu89 + -D_GNU_SOURCE -std=gnu89 \ + -Wall -Wextra -pedantic \ + -Wmissing-prototypes -Wstrict-prototypes \ + -Wold-style-definition TARGETS = busy process_iterator_test SRC = ../src SYSLIBS ?= -lpthread diff --git a/tests/busy.c b/tests/busy.c index 90c6fb59..05708b28 100644 --- a/tests/busy.c +++ b/tests/busy.c @@ -3,17 +3,40 @@ #include #include -void *loop() +/* Get the number of CPUs */ +static int get_ncpu(void) +{ + int ncpu; +#if defined(_SC_NPROCESSORS_ONLN) + ncpu = sysconf(_SC_NPROCESSORS_ONLN); +#elif defined(__APPLE__) + int mib[2] = {CTL_HW, HW_NCPU}; + size_t len = sizeof(ncpu); + sysctl(mib, 2, &ncpu, &len, NULL, 0); +#elif defined(_GNU_SOURCE) + ncpu = get_nprocs(); +#else + ncpu = -1; +#endif + return ncpu; +} + +#ifdef __GNUC__ +static void *loop(__attribute__((__unused__)) void *param) +#else +static void *loop(void *param) +#endif { while (1) ; + return NULL; } -int main(int argc, char **argv) +int main(int argc, char *argv[]) { int i = 0; - int num_threads = 1; + int num_threads = get_ncpu(); if (argc == 2) num_threads = atoi(argv[1]); for (i = 0; i < num_threads - 1; i++) @@ -26,6 +49,6 @@ int main(int argc, char **argv) exit(1); } } - loop(); + loop(NULL); return 0; } diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index 7591dade..f9c84aad 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -28,17 +28,21 @@ #include #include -#include -#include +#include "process_iterator.h" +#include "process_group.h" volatile sig_atomic_t child; -void kill_child(int sig) +#ifdef __GNUC__ +static void kill_child(__attribute__((__unused__)) int sig) +#else +static void kill_child(int sig) +#endif { kill(child, SIGINT); } -void test_single_process() +static void test_single_process(void) { struct process_iterator it; struct process process; @@ -74,7 +78,7 @@ void test_single_process() close_process_iterator(&it); } -void test_multiple_process() +static void test_multiple_process(void) { struct process_iterator it; struct process process; @@ -106,7 +110,7 @@ void test_multiple_process() kill(child, SIGINT); } -void test_all_processes() +static void test_all_processes(void) { struct process_iterator it; struct process process; @@ -129,7 +133,7 @@ void test_all_processes() close_process_iterator(&it); } -void test_process_group_all() +static void test_process_group_all(void) { struct process_group pgroup; struct list_node *node = NULL; @@ -145,7 +149,7 @@ void test_process_group_all() assert(close_process_group(&pgroup) == 0); } -void test_process_group_single(int include_children) +static void test_process_group_single(int include_children) { struct process_group pgroup; int i; @@ -186,7 +190,7 @@ void test_process_group_single(int include_children) kill(child, SIGINT); } -void test_process_name(const char *command) +static void test_process_name(const char *command) { struct process_iterator it; struct process process; @@ -208,7 +212,7 @@ void test_process_name(const char *command) close_process_iterator(&it); } -void test_process_group_wrong_pid() +static void test_process_group_wrong_pid(void) { struct process_group pgroup; assert(init_process_group(&pgroup, -1, 0) == 0); @@ -222,7 +226,11 @@ void test_process_group_wrong_pid() assert(close_process_group(&pgroup) == 0); } -int main(int argc, char **argv) +#ifdef __GNUC__ +int main(__attribute__((__unused__)) int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif { test_single_process(); test_multiple_process(); From 94a9519ac17f927766c98051d034a0ee7077f5f4 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 18 Jan 2023 13:45:42 +0800 Subject: [PATCH 037/209] fix unit test code --- tests/process_iterator_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index f9c84aad..cc9e968b 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -202,11 +202,11 @@ static void test_process_name(const char *command) assert(process.pid == getpid()); assert(process.ppid == getppid()); #ifdef __APPLE__ - /* proc_pidinfo only gives us the first 15 chars */ - /* of the basename of the command on OSX. */ + /* proc_pidinfo only gives us the first 15 chars + of the basename of the command on OSX. */ assert(strncmp(basename((char *)command), process.command, 15) == 0); #else - assert(strncmp(command, process.command, strlen(process.command)) == 0); + assert(strcmp(command, process.command) == 0); #endif assert(get_next_process(&it, &process) != 0); close_process_iterator(&it); From 1b6e598e0b8f12a37e87a528edd72c87b2cbe314 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 18 Jan 2023 13:49:55 +0800 Subject: [PATCH 038/209] fix a comment --- src/cpulimit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index ef426cd7..03948477 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -494,8 +494,8 @@ int main(int argc, char *argv[]) } else { - pid_t limiter; /* parent code */ + pid_t limiter; free(cmd_args); limiter = fork(); if (limiter < 0) From b337a4e221ea03a1a83abfa121910459288ed26e Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 18 Jan 2023 13:54:09 +0800 Subject: [PATCH 039/209] simplify CI.yml --- .github/workflows/CI.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 349d8c0e..3e52a4ae 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -33,17 +33,9 @@ jobs: if: ${{ startsWith( matrix.os, 'macos-' ) }} run: | make - - - name: Compile static - if: ${{ matrix.os == 'ubuntu-22.04' }} - run: | - sudo -E apt -y -qq update &> /dev/null - sudo -E apt -y -qq install gcc-12 &> /dev/null - sudo -E update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 &> /dev/null - make CFLAGS="-Werror=use-after-free" LDFLAGS="-static" - name: Compile static - if: ${{ matrix.os == 'ubuntu-20.04' }} + if: ${{ startsWith( matrix.os, 'ubuntu-' ) }} run: | make LDFLAGS="-static" From e3a1901c4461f7199707a4cdf256b8edd6207e06 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 18 Jan 2023 13:59:36 +0800 Subject: [PATCH 040/209] CI: run unit test after build --- .github/workflows/CI.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 3e52a4ae..338b8ddf 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -33,11 +33,13 @@ jobs: if: ${{ startsWith( matrix.os, 'macos-' ) }} run: | make + ./tests/process_iterator_test - name: Compile static if: ${{ startsWith( matrix.os, 'ubuntu-' ) }} run: | make LDFLAGS="-static" + ./tests/process_iterator_test - name: Upload uses: actions/upload-artifact@v3 @@ -67,6 +69,7 @@ jobs: pkg install -y lang/gcc gmake run: | gmake + ./tests/process_iterator_test - name: Upload uses: actions/upload-artifact@v3 From 988c5f13ea6352a9b8c3ebd7144f2e0eb52cf738 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 18 Jan 2023 14:53:01 +0800 Subject: [PATCH 041/209] update Makefiles --- src/Makefile | 12 ++++++------ tests/Makefile | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Makefile b/src/Makefile index d0c09e97..c10af797 100644 --- a/src/Makefile +++ b/src/Makefile @@ -6,21 +6,21 @@ override CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ -Wold-style-definition TARGET := cpulimit -ifeq ($(shell echo "int main(void){return 0;}" | $(CC) -x c - -lrt -o /dev/null 2>&1),) - override LDFLAGS += -lrt -endif - -override UNAME ?= $(shell uname) +UNAME ?= $(shell uname) ifeq ($(UNAME), FreeBSD) override LDFLAGS += -lkvm endif +ifeq ($(shell echo "int main(void){return 0;}" | $(CC) -x c - -lrt -o /dev/null 2>&1),) + override LDFLAGS += -lrt +endif + .PHONY: all clean all: $(TARGET) $(TARGET): $(wildcard *.c *.h) - $(CC) $(CFLAGS) $(LDFLAGS) $(filter-out process_iterator_%.c %.h, $^) -o $@ + $(CC) $(CFLAGS) $(filter-out process_iterator_%.c %.h, $^) $(LDFLAGS) -o $@ clean: rm -f *~ $(TARGET) diff --git a/tests/Makefile b/tests/Makefile index 03e28494..8c87784c 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -9,24 +9,24 @@ SRC = ../src SYSLIBS ?= -lpthread LIBS := $(SRC)/list.c $(SRC)/process_iterator.c $(SRC)/process_group.c -ifeq ($(shell echo "int main(void){return 0;}" | $(CC) -x c - -lrt -o /dev/null 2>&1),) - override LDFLAGS += -lrt -endif - -override UNAME ?= $(shell uname) +UNAME ?= $(shell uname) ifeq ($(UNAME), FreeBSD) override LDFLAGS += -lkvm endif +ifeq ($(shell echo "int main(void){return 0;}" | $(CC) -x c - -lrt -o /dev/null 2>&1),) + override LDFLAGS += -lrt +endif + .PHONY: all clean all: $(TARGETS) busy: busy.c - $(CC) $(CFLAGS) $(LDFLAGS) $^ $(SYSLIBS) -o $@ + $(CC) $(CFLAGS) $^ $(SYSLIBS) $(LDFLAGS) -o $@ process_iterator_test: process_iterator_test.c $(LIBS) - $(CC) $(CFLAGS) $(LDFLAGS) -I$(SRC) $^ $(SYSLIBS) -o $@ + $(CC) $(CFLAGS) -I$(SRC) $^ $(LDFLAGS) -o $@ clean: rm -f *~ $(TARGETS) From e9bba7a5007577abf5230cb072610f194d897460 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 18 Jan 2023 15:18:35 +0800 Subject: [PATCH 042/209] remove unnecessary assertion --- src/process_group.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index 2ccda5ee..dac7bb4c 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include @@ -197,7 +196,6 @@ void update_process_group(struct process_group *pgroup) else { double sample; - assert(tmp_process.pid == p->pid); add_elem(pgroup->proclist, p); if (dt < MIN_DT) continue; From 5813c163b1e9722abf778e351cf1ad16cdac889e Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 19 Jan 2023 01:48:45 +0800 Subject: [PATCH 043/209] fix unused-result warning --- src/cpulimit.c | 3 ++- src/process_iterator_linux.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 03948477..5b533689 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -183,7 +183,8 @@ static pid_t get_pid_max(void) FILE *fd; if ((fd = fopen("/proc/sys/kernel/pid_max", "r")) != NULL) { - fscanf(fd, "%ld", &pid_max); + if (fscanf(fd, "%ld", &pid_max) != 1) + pid_max = -1; fclose(fd); } return (pid_t)pid_max; diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 8162f8af..032bbf72 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -101,7 +101,8 @@ static pid_t getppid_of(pid_t pid) sprintf(statfile, "/proc/%ld/stat", (long)pid); if ((fd = fopen(statfile, "r")) != NULL) { - fscanf(fd, "%*d (%*[^)]) %*c %ld", &ppid); + if (fscanf(fd, "%*d (%*[^)]) %*c %ld", &ppid) != 1) + ppid = -1; fclose(fd); } return (pid_t)ppid; From c301f21c673fda1b7e866b7bfb4da43c0172b513 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 19 Jan 2023 02:22:36 +0800 Subject: [PATCH 044/209] remove unnecessary memset --- src/process_group.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/process_group.c b/src/process_group.c index dac7bb4c..9ac157ad 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -38,7 +38,6 @@ static int get_time(struct timespec *ts) { struct timeval tv; - memset(ts, 0, sizeof(*ts)); if (gettimeofday(&tv, NULL)) { return -1; From 5618db310ee0f17a1e8e94785332f9176e504dd0 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 19 Jan 2023 03:54:29 +0800 Subject: [PATCH 045/209] simplify find_process_by_name --- src/process_group.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index 9ac157ad..f6659af3 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -80,22 +80,16 @@ pid_t find_process_by_name(const char *process_name) /* process found */ if (strcmp(basename(proc.command), process_name) == 0) { - if (kill(proc.pid, 0) == -1 && errno == EPERM) - { - /* do not have permission */ - return -1; - } - /* process is ok! */ pid = proc.pid; break; } } if (close_process_iterator(&it) != 0) exit(1); - if (pid >= 0) + if (pid > 0) { - /* ok, the process was found */ - return pid; + /* the process was found */ + return find_process_by_pid(pid); } else { From daf38d90593967005245d3def042c7b3fec549d0 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 19 Jan 2023 04:15:43 +0800 Subject: [PATCH 046/209] fix process name comparison on Mac OS X --- src/process_group.c | 12 ++++++++++++ src/process_iterator_apple.c | 10 ++++++++-- tests/process_iterator_test.c | 12 ++++++++++-- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index f6659af3..d23b36c3 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -26,11 +26,18 @@ #include #include #include +#ifdef __APPLE__ +#include +#endif #include "process_iterator.h" #include "process_group.h" #include "list.h" +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + #if _POSIX_C_SOURCE >= 199309L #define get_time(ts) \ (clock_gettime(CLOCK_MONOTONIC, (ts))) @@ -78,7 +85,12 @@ pid_t find_process_by_name(const char *process_name) while (get_next_process(&it, &proc) != -1) { /* process found */ +#ifdef __APPLE__ + if (strncmp(proc.command, process_name, + MAX(strlen(proc.command) + 1, MAXCOMLEN)) == 0) +#else if (strcmp(basename(proc.command), process_name) == 0) +#endif { pid = proc.pid; break; diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 35006677..6ee45e6c 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -90,8 +90,14 @@ static int pti2proc(struct proc_taskallinfo *ti, struct process *process) process->pid = ti->pbsd.pbi_pid; process->ppid = ti->pbsd.pbi_ppid; process->cputime = (ti->ptinfo.pti_total_user + ti->ptinfo.pti_total_system) / 1000000; - process->command[0] = '\0'; - strncat(process->command, ti->pbsd.pbi_comm, MIN(PATH_MAX, MAXCOMLEN)); + if (ti->pbsd.pbi_name[0] != '\0') + { + memcpy(process->command, ti->pbsd.pbi_name, sizeof(ti->pbsd.pbi_name)); + } + else + { + memcpy(process->command, ti->pbsd.pbi_comm, sizeof(ti->pbsd.pbi_comm)); + } return 0; } diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index cc9e968b..1af14de9 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -27,10 +27,17 @@ #include #include #include +#ifdef __APPLE__ +#include +#endif #include "process_iterator.h" #include "process_group.h" +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + volatile sig_atomic_t child; #ifdef __GNUC__ @@ -202,9 +209,10 @@ static void test_process_name(const char *command) assert(process.pid == getpid()); assert(process.ppid == getppid()); #ifdef __APPLE__ - /* proc_pidinfo only gives us the first 15 chars + /* proc_pidinfo only gives us the first MAXCOMLEN-1 (15) chars of the basename of the command on OSX. */ - assert(strncmp(basename((char *)command), process.command, 15) == 0); + assert(strncmp(basename(command), process.command, + MAX(strlen(process.command) + 1, MAXCOMLEN)) == 0); #else assert(strcmp(command, process.command) == 0); #endif From 06ade1ebb4efda8b9b418974642ee0493ebf02ea Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 19 Jan 2023 15:53:21 +0800 Subject: [PATCH 047/209] improve increase_priority() --- src/cpulimit.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 5b533689..b081fe50 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -139,9 +139,11 @@ static void print_usage(FILE *stream, int exit_code) static void increase_priority(void) { /* find the best available nice value */ - int old_priority = getpriority(PRIO_PROCESS, 0); - int priority = old_priority; - while (setpriority(PRIO_PROCESS, 0, priority - 1) == 0 && priority > MAX_PRIORITY) + int old_priority, priority; + old_priority = getpriority(PRIO_PROCESS, 0); + setpriority(PRIO_PROCESS, 0, MAX_PRIORITY); + priority = getpriority(PRIO_PROCESS, 0); + while (priority > MAX_PRIORITY && setpriority(PRIO_PROCESS, 0, priority - 1) == 0) { priority--; } From 044113c53087ff44b9728d39dcb194ddb91fe808 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 19 Jan 2023 14:29:32 +0800 Subject: [PATCH 048/209] improve tests --- tests/busy.c | 17 ++++++++++++++ tests/process_iterator_test.c | 44 ++++++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/tests/busy.c b/tests/busy.c index 05708b28..4143a65a 100644 --- a/tests/busy.c +++ b/tests/busy.c @@ -2,6 +2,22 @@ #include #include #include +#include +#include + +#define MAX_PRIORITY -20 + +static void increase_priority(void) +{ + /* find the best available nice value */ + int priority; + setpriority(PRIO_PROCESS, 0, MAX_PRIORITY); + priority = getpriority(PRIO_PROCESS, 0); + while (priority > MAX_PRIORITY && setpriority(PRIO_PROCESS, 0, priority - 1) == 0) + { + priority--; + } +} /* Get the number of CPUs */ static int get_ncpu(void) @@ -37,6 +53,7 @@ int main(int argc, char *argv[]) int i = 0; int num_threads = get_ncpu(); + increase_priority(); if (argc == 2) num_threads = atoi(argv[1]); for (i = 0; i < num_threads - 1; i++) diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index 1af14de9..652974b0 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #ifdef __APPLE__ #include #endif @@ -38,6 +40,29 @@ #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif +#define MAX_PRIORITY -20 + +static void increase_priority(void) +{ + /* find the best available nice value */ + int priority; + setpriority(PRIO_PROCESS, 0, MAX_PRIORITY); + priority = getpriority(PRIO_PROCESS, 0); + while (priority > MAX_PRIORITY && setpriority(PRIO_PROCESS, 0, priority - 1) == 0) + { + priority--; + } +} + +/* inline int sleep_timespec(struct timespec *t); */ +#if _POSIX_C_SOURCE >= 200809L +#define sleep_timespec(t) \ + (clock_nanosleep(CLOCK_MONOTONIC, 0, (t), NULL)) +#else +#define sleep_timespec(t) \ + (nanosleep((t), NULL)) +#endif + volatile sig_atomic_t child; #ifdef __GNUC__ @@ -64,7 +89,7 @@ static void test_single_process(void) { assert(process.pid == getpid()); assert(process.ppid == getppid()); - assert(process.cputime < 100); + assert(process.cputime <= 100); count++; } assert(count == 1); @@ -78,7 +103,7 @@ static void test_single_process(void) { assert(process.pid == getpid()); assert(process.ppid == getppid()); - assert(process.cputime < 100); + assert(process.cputime <= 100); count++; } assert(count == 1); @@ -95,7 +120,8 @@ static void test_multiple_process(void) if (child == 0) { /* child is supposed to be killed by the parent :/ */ - sleep(1); + while (1) + sleep(5); exit(1); } filter.pid = getpid(); @@ -109,7 +135,7 @@ static void test_multiple_process(void) assert(process.ppid == getpid()); else assert(0); - assert(process.cputime < 100); + assert(process.cputime <= 100); count++; } assert(count == 2); @@ -132,7 +158,7 @@ static void test_all_processes(void) if (process.pid == getpid()) { assert(process.ppid == getppid()); - assert(process.cputime < 100); + assert(process.cputime <= 100); } count++; } @@ -165,6 +191,7 @@ static void test_process_group_single(int include_children) if (child == 0) { /* child is supposed to be killed by the parent :/ */ + increase_priority(); while (1) ; exit(1); @@ -172,7 +199,7 @@ static void test_process_group_single(int include_children) signal(SIGABRT, &kill_child); signal(SIGTERM, &kill_child); assert(init_process_group(&pgroup, child, include_children) == 0); - for (i = 0; i < 100; i++) + for (i = 0; i < 200; i++) { struct list_node *node = NULL; int count = 0; @@ -190,9 +217,9 @@ static void test_process_group_single(int include_children) assert(count == 1); interval.tv_sec = 0; interval.tv_nsec = 50000000; - nanosleep(&interval, NULL); + sleep_timespec(&interval); } - assert(tot_usage / i < 1.1 && tot_usage / i > 0.8); + assert(tot_usage / i < 1.1 && tot_usage / i > 0.7); assert(close_process_group(&pgroup) == 0); kill(child, SIGINT); } @@ -240,6 +267,7 @@ int main(__attribute__((__unused__)) int argc, char *argv[]) int main(int argc, char *argv[]) #endif { + increase_priority(); test_single_process(); test_multiple_process(); test_all_processes(); From 70f3a6ddf5be2ec5614f77d62729a649d56e60cd Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 19 Jan 2023 16:28:26 +0800 Subject: [PATCH 049/209] CI: run unit test using root --- .github/workflows/CI.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 338b8ddf..23ad23c5 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -33,13 +33,13 @@ jobs: if: ${{ startsWith( matrix.os, 'macos-' ) }} run: | make - ./tests/process_iterator_test + sudo ./tests/process_iterator_test - name: Compile static if: ${{ startsWith( matrix.os, 'ubuntu-' ) }} run: | make LDFLAGS="-static" - ./tests/process_iterator_test + sudo ./tests/process_iterator_test - name: Upload uses: actions/upload-artifact@v3 @@ -66,10 +66,10 @@ jobs: release: ${{ matrix.osver }} usesh: true prepare: | - pkg install -y lang/gcc gmake + pkg install -y lang/gcc gmake sudo run: | gmake - ./tests/process_iterator_test + sudo ./tests/process_iterator_test - name: Upload uses: actions/upload-artifact@v3 From 9be7bf12b4b4099ab69bd6cb72a0b83e799fa1a2 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 20 Jan 2023 01:43:58 +0800 Subject: [PATCH 050/209] more clear code style --- src/cpulimit.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index b081fe50..64083b95 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -262,9 +262,7 @@ static void limit_process(pid_t pid, double limit, int include_children) else { /* adjust workingrate */ - workingrate = limit * - workingrate / - (pcpu + EPSILON); + workingrate = workingrate * limit / MAX(pcpu, EPSILON); } workingrate = MAX(MIN(workingrate, 1 - EPSILON), EPSILON); From 0d195b4ddbc6f2150c5f1361ec89fca3fb9db694 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 20 Jan 2023 02:28:19 +0800 Subject: [PATCH 051/209] change process.cputime to double for higher precision --- src/process_iterator.h | 2 +- src/process_iterator_apple.c | 2 +- src/process_iterator_freebsd.c | 2 +- src/process_iterator_linux.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/process_iterator.h b/src/process_iterator.h index efa620c1..2599c3e7 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -60,7 +60,7 @@ struct process /* ppid of the process */ pid_t ppid; /* cputime used by the process (in milliseconds) */ - int cputime; + double cputime; /* actual cpu usage estimation (value in range 0-1) */ double cpu_usage; /* absolute path of the executable file */ diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 6ee45e6c..5ad6a3e6 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -89,7 +89,7 @@ static int pti2proc(struct proc_taskallinfo *ti, struct process *process) { process->pid = ti->pbsd.pbi_pid; process->ppid = ti->pbsd.pbi_ppid; - process->cputime = (ti->ptinfo.pti_total_user + ti->ptinfo.pti_total_system) / 1000000; + process->cputime = ti->ptinfo.pti_total_user / 1e6 + ti->ptinfo.pti_total_system / 1e6; if (ti->pbsd.pbi_name[0] != '\0') { memcpy(process->command, ti->pbsd.pbi_name, sizeof(ti->pbsd.pbi_name)); diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index 7a536a8c..c3011700 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -49,7 +49,7 @@ static int kproc2proc(kvm_t *kd, struct kinfo_proc *kproc, struct process *proc) char **args; proc->pid = kproc->ki_pid; proc->ppid = kproc->ki_ppid; - proc->cputime = kproc->ki_runtime / 1000; + proc->cputime = kproc->ki_runtime / 1000.0; if ((args = kvm_getargv(kd, kproc, sizeof(proc->command))) == NULL) return -1; memcpy(proc->command, args[0], strlen(args[0]) + 1); diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 032bbf72..a9113553 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -84,7 +84,7 @@ static int read_process_info(pid_t pid, struct process *p) } fclose(fd); p->ppid = (pid_t)ppid; - p->cputime = (int)((utime + stime) * 1000.0 / HZ); + p->cputime = utime * 1000.0 / HZ + stime * 1000.0 / HZ; return 0; error_out1: From 8a5ea01a1d5f9c90b702e33bda229de442243e5d Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 21 Jan 2023 04:03:29 +0800 Subject: [PATCH 052/209] fix process name comparison --- src/cpulimit.c | 4 ++-- src/process_group.c | 22 ++++++++-------------- src/process_group.h | 3 ++- src/process_iterator.h | 9 +++++++++ src/process_iterator_apple.c | 6 ++++-- src/process_iterator_freebsd.c | 1 + src/process_iterator_linux.c | 1 + tests/process_iterator_test.c | 18 ++---------------- 8 files changed, 29 insertions(+), 35 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 64083b95..ea33cba0 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -331,7 +331,7 @@ static void limit_process(pid_t pid, double limit, int include_children) int main(int argc, char *argv[]) { /* argument variables */ - const char *exe = NULL; + char *exe = NULL; int perclimit = 0; int exe_ok = 0; int pid_ok = 0; @@ -377,7 +377,7 @@ int main(int argc, char *argv[]) pid_ok = 1; break; case 'e': - exe = basename(optarg); + exe = optarg; exe_ok = 1; break; case 'l': diff --git a/src/process_group.c b/src/process_group.c index d23b36c3..bf1b0268 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -26,18 +26,11 @@ #include #include #include -#ifdef __APPLE__ -#include -#endif #include "process_iterator.h" #include "process_group.h" #include "list.h" -#ifndef MAX -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif - #if _POSIX_C_SOURCE >= 199309L #define get_time(ts) \ (clock_gettime(CLOCK_MONOTONIC, (ts))) @@ -70,7 +63,7 @@ process: the name of the wanted process. it can be an absolute path name to the 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 */ -pid_t find_process_by_name(const char *process_name) +pid_t find_process_by_name(char *process_name) { /* pid of the target process */ pid_t pid = -1; @@ -79,18 +72,19 @@ pid_t find_process_by_name(const char *process_name) struct process_iterator it; struct process proc; struct process_filter filter; + const char *process_basename = basename(process_name); + const char *command_basename; + int cmp_len; filter.pid = 0; filter.include_children = 0; init_process_iterator(&it, &filter); while (get_next_process(&it, &proc) != -1) { + command_basename = basename(proc.command); + cmp_len = proc.max_cmd_len - (command_basename - proc.command); /* process found */ -#ifdef __APPLE__ - if (strncmp(proc.command, process_name, - MAX(strlen(proc.command) + 1, MAXCOMLEN)) == 0) -#else - if (strcmp(basename(proc.command), process_name) == 0) -#endif + if (cmp_len > 0 && command_basename[0] != '\0' && + strncmp(command_basename, process_basename, cmp_len) == 0) { pid = proc.pid; break; diff --git a/src/process_group.h b/src/process_group.h index fec71db9..35f5b848 100644 --- a/src/process_group.h +++ b/src/process_group.h @@ -33,6 +33,7 @@ #define PIDHASH_SZ 1024 #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1)) +#undef basename #define basename(x) \ ((strrchr((x), '/') != NULL) ? (strrchr((x), '/') + 1) : (x)) @@ -54,7 +55,7 @@ int close_process_group(struct process_group *pgroup); pid_t find_process_by_pid(pid_t pid); -pid_t find_process_by_name(const char *process_name); +pid_t find_process_by_name(char *process_name); int remove_process(struct process_group *pgroup, pid_t pid); diff --git a/src/process_iterator.h b/src/process_iterator.h index 2599c3e7..c4f8c2c0 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -52,6 +52,13 @@ #include #endif +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + /* process descriptor */ struct process { @@ -65,6 +72,8 @@ struct process double cpu_usage; /* absolute path of the executable file */ char command[PATH_MAX + 1]; + /* maximum command length */ + int max_cmd_len; }; struct process_filter diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 5ad6a3e6..96c1ed80 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -92,11 +92,13 @@ static int pti2proc(struct proc_taskallinfo *ti, struct process *process) process->cputime = ti->ptinfo.pti_total_user / 1e6 + ti->ptinfo.pti_total_system / 1e6; if (ti->pbsd.pbi_name[0] != '\0') { - memcpy(process->command, ti->pbsd.pbi_name, sizeof(ti->pbsd.pbi_name)); + process->max_cmd_len = MIN(sizeof(process->command), sizeof(ti->pbsd.pbi_name)) - 1; + memcpy(process->command, ti->pbsd.pbi_name, process->max_cmd_len + 1); } else { - memcpy(process->command, ti->pbsd.pbi_comm, sizeof(ti->pbsd.pbi_comm)); + process->max_cmd_len = MIN(sizeof(process->command), sizeof(ti->pbsd.pbi_comm)) - 1; + memcpy(process->command, ti->pbsd.pbi_comm, process->max_cmd_len + 1); } return 0; } diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index c3011700..de647f99 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -53,6 +53,7 @@ static int kproc2proc(kvm_t *kd, struct kinfo_proc *kproc, struct process *proc) if ((args = kvm_getargv(kd, kproc, sizeof(proc->command))) == NULL) return -1; memcpy(proc->command, args[0], strlen(args[0]) + 1); + proc->max_cmd_len = sizeof(proc->command) - 1; return 0; } diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index a9113553..794f4612 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -62,6 +62,7 @@ static int read_process_info(pid_t pid, struct process *p) fd = fopen(exefile, "r"); if (fd == NULL) goto error_out1; + p->max_cmd_len = sizeof(p->command) - 1; if (fgets(p->command, sizeof(p->command), fd) == NULL) { fclose(fd); diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index 652974b0..a186e774 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -29,17 +29,10 @@ #include #include #include -#ifdef __APPLE__ -#include -#endif #include "process_iterator.h" #include "process_group.h" -#ifndef MAX -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif - #define MAX_PRIORITY -20 static void increase_priority(void) @@ -224,7 +217,7 @@ static void test_process_group_single(int include_children) kill(child, SIGINT); } -static void test_process_name(const char *command) +static void test_process_name(char *command) { struct process_iterator it; struct process process; @@ -235,14 +228,7 @@ static void test_process_name(const char *command) assert(get_next_process(&it, &process) == 0); assert(process.pid == getpid()); assert(process.ppid == getppid()); -#ifdef __APPLE__ - /* proc_pidinfo only gives us the first MAXCOMLEN-1 (15) chars - of the basename of the command on OSX. */ - assert(strncmp(basename(command), process.command, - MAX(strlen(process.command) + 1, MAXCOMLEN)) == 0); -#else - assert(strcmp(command, process.command) == 0); -#endif + assert(strcmp(basename(command), basename(process.command)) == 0); assert(get_next_process(&it, &process) != 0); close_process_iterator(&it); } From f59a645eaafc3e81f3da8a1f588951268a2289d3 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 21 Jan 2023 04:10:52 +0800 Subject: [PATCH 053/209] fix tests and add more tests --- tests/process_iterator_test.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index a186e774..f2614912 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -217,18 +217,26 @@ static void test_process_group_single(int include_children) kill(child, SIGINT); } -static void test_process_name(char *command) +char *command = NULL; + +static void test_process_name(void) { struct process_iterator it; struct process process; struct process_filter filter; + const char *command_basename; + const char *process_basename; + int cmp_len; 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(strcmp(basename(command), basename(process.command)) == 0); + command_basename = basename(command); + process_basename = basename(process.command); + cmp_len = process.max_cmd_len - (process_basename - process.command); + assert(strncmp(command_basename, process_basename, cmp_len) == 0); assert(get_next_process(&it, &process) != 0); close_process_iterator(&it); } @@ -247,12 +255,24 @@ static void test_process_group_wrong_pid(void) assert(close_process_group(&pgroup) == 0); } +static void test_find_process_by_pid(void) +{ + assert(find_process_by_pid(getpid()) == getpid()); +} + +static void test_find_process_by_name(void) +{ + assert(find_process_by_name(command) == getpid()); + assert(find_process_by_name("") == 0); +} + #ifdef __GNUC__ int main(__attribute__((__unused__)) int argc, char *argv[]) #else int main(int argc, char *argv[]) #endif { + command = argv[0]; increase_priority(); test_single_process(); test_multiple_process(); @@ -261,6 +281,8 @@ int main(int argc, char *argv[]) test_process_group_single(0); test_process_group_single(1); test_process_group_wrong_pid(); - test_process_name(argv[0]); + test_process_name(); + test_find_process_by_pid(); + test_find_process_by_name(); return 0; } From 3ea19f06ae9fbf0be6f461db197ecbcb5b276a5a Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 21 Jan 2023 04:24:06 +0800 Subject: [PATCH 054/209] test for long filenames that may be truncated --- .github/workflows/CI.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 23ad23c5..4dba68f7 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -34,12 +34,16 @@ jobs: run: | make sudo ./tests/process_iterator_test + cp ./tests/process_iterator_test ./tests/abcdefghijklmnopqrstuvwxyzabcdefghi + sudo ./tests/abcdefghijklmnopqrstuvwxyzabcdefghi - name: Compile static if: ${{ startsWith( matrix.os, 'ubuntu-' ) }} run: | make LDFLAGS="-static" sudo ./tests/process_iterator_test + cp ./tests/process_iterator_test ./tests/abcdefghijklmnopqrstuvwxyzabcdefghi + sudo ./tests/abcdefghijklmnopqrstuvwxyzabcdefghi - name: Upload uses: actions/upload-artifact@v3 @@ -70,6 +74,8 @@ jobs: run: | gmake sudo ./tests/process_iterator_test + cp ./tests/process_iterator_test ./tests/abcdefghijklmnopqrstuvwxyzabcdefghi + sudo ./tests/abcdefghijklmnopqrstuvwxyzabcdefghi - name: Upload uses: actions/upload-artifact@v3 From 2832a1d636a38715f82ce344013f02aab5858d0c Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 22 Jan 2023 15:34:22 +0800 Subject: [PATCH 055/209] remove program_name member from struct process_filter The program_name it is never used. --- src/process_iterator.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/process_iterator.h b/src/process_iterator.h index c4f8c2c0..332c9791 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -80,7 +80,6 @@ struct process_filter { pid_t pid; int include_children; - char program_name[PATH_MAX + 1]; }; struct process_iterator From 1d3e5b30ac1cc93699481ff2dc3d0db19b424ed8 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 22 Jan 2023 17:28:59 +0800 Subject: [PATCH 056/209] set last_update to current time in init_process_group --- src/process_group.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/process_group.c b/src/process_group.c index bf1b0268..fa45f5ce 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -112,7 +112,10 @@ int init_process_group(struct process_group *pgroup, pid_t target_pid, int inclu pgroup->include_children = include_children; pgroup->proclist = malloc(sizeof(struct list)); init_list(pgroup->proclist, sizeof(pid_t)); - memset(&pgroup->last_update, 0, sizeof(pgroup->last_update)); + if (get_time(&pgroup->last_update)) + { + exit(-1); + } update_process_group(pgroup); return 0; } From bd6abb3d3c5c04981c8dcafd9e85805afddd2568 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 23 Jan 2023 07:46:54 +0800 Subject: [PATCH 057/209] fix the judgment logic of the child process --- src/process_iterator_apple.c | 22 +++++++++++++++++++++- src/process_iterator_freebsd.c | 24 +++++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 96c1ed80..2acb7efd 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -123,6 +123,26 @@ static int get_process_pti(pid_t pid, struct proc_taskallinfo *ti) return 0; } +static pid_t getppid_of(pid_t pid) +{ + struct proc_taskallinfo ti; + if (get_process_pti(pid, &ti) == 0) + { + return ti.pbsd.pbi_ppid; + } + return (pid_t)(-1); +} + +static int is_child_of(pid_t child_pid, pid_t parent_pid) +{ + pid_t 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->i == it->count) @@ -157,7 +177,7 @@ int get_next_process(struct process_iterator *it, struct process *p) it->i++; if (p->pid != it->pidlist[it->i - 1]) /* I don't know why this can happen */ continue; - if (p->pid != it->filter->pid && p->ppid != it->filter->pid) + if (p->pid != it->filter->pid && !is_child_of(p->pid, it->filter->pid)) continue; return 0; } diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index de647f99..3c340a9e 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -69,6 +69,27 @@ static int get_single_process(kvm_t *kd, pid_t pid, struct process *process) return 0; } +static pid_t getppid_of(kvm_t *kd, pid_t pid) +{ + int count; + struct kinfo_proc *kproc = kvm_getprocs(kd, KERN_PROC_PID, pid, &count); + if (count == 0 || kproc == NULL) + { + return -1; + } + return kproc->ki_ppid; +} + +static int is_child_of(kvm_t *kd, pid_t child_pid, pid_t parent_pid) +{ + pid_t ppid = child_pid; + while (ppid > 1 && ppid != parent_pid) + { + ppid = getppid_of(kd, ppid); + } + return ppid == parent_pid; +} + int get_next_process(struct process_iterator *it, struct process *p) { if (it->i == it->count) @@ -98,7 +119,8 @@ int get_next_process(struct process_iterator *it, struct process *p) { kproc2proc(it->kd, kproc, p); it->i++; - if (p->pid != it->filter->pid && p->ppid != it->filter->pid) + if (p->pid != it->filter->pid && + !is_child_of(it->kd, p->pid, it->filter->pid)) continue; return 0; } From a3e2704775595a53fe3c807e97db839c667d330a Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 23 Jan 2023 08:51:45 +0800 Subject: [PATCH 058/209] improve kproc2proc --- src/process_iterator_freebsd.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index 3c340a9e..5324519f 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -23,6 +23,7 @@ #include #include #include +#include int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { @@ -50,10 +51,14 @@ static int 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.0; + proc->max_cmd_len = sizeof(proc->command) - 1; if ((args = kvm_getargv(kd, kproc, sizeof(proc->command))) == NULL) + { + proc->command[0] = '\0'; return -1; - memcpy(proc->command, args[0], strlen(args[0]) + 1); - proc->max_cmd_len = sizeof(proc->command) - 1; + } + strncpy(proc->command, args[0], proc->max_cmd_len); + proc->command[proc->max_cmd_len] = '\0'; return 0; } From 1fb4141ffabeafa20e9e636b145a9c830991a5e5 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 23 Jan 2023 13:34:36 +0800 Subject: [PATCH 059/209] try to find the parent process when processes share the same name --- src/process_group.c | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index fa45f5ce..af87fbf0 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -57,6 +57,32 @@ pid_t find_process_by_pid(pid_t pid) return (kill(pid, 0) == 0) ? pid : -pid; } +static pid_t getppid_of(pid_t pid) +{ + /* process iterator */ + int ret; + struct process_iterator it; + struct process proc; + struct process_filter filter; + filter.pid = pid; + filter.include_children = 0; + if (init_process_iterator(&it, &filter) != 0) + exit(1); + ret = get_next_process(&it, &proc); + if (close_process_iterator(&it) != 0) + exit(1); + return ret == 0 ? proc.ppid : (pid_t)(-1); +} + +static int is_child_of(pid_t child_pid, pid_t parent_pid) +{ + while (child_pid > 1 && child_pid != parent_pid) + { + child_pid = getppid_of(child_pid); + } + return child_pid == parent_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 @@ -86,8 +112,21 @@ pid_t find_process_by_name(char *process_name) if (cmp_len > 0 && command_basename[0] != '\0' && strncmp(command_basename, process_basename, cmp_len) == 0) { - pid = proc.pid; - break; + if (pid < 0) + { + pid = proc.pid; + } + else if (is_child_of(pid, proc.pid)) + { + pid = proc.pid; + } + else if (is_child_of(proc.pid, pid)) + { + } + else + { + pid = MIN(proc.pid, pid); + } } } if (close_process_iterator(&it) != 0) From 6c671ed6bd40fa2a2be37bcf2924a13c9b04131f Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 25 Jan 2023 00:26:32 +0800 Subject: [PATCH 060/209] refactor code to reduce redundancy --- src/process_group.c | 26 ------------------------ src/process_iterator.c | 11 +++++++++++ src/process_iterator.h | 4 ++++ src/process_iterator_apple.c | 12 +----------- src/process_iterator_freebsd.c | 36 ++++++++++++++++------------------ src/process_iterator_linux.c | 14 +++---------- 6 files changed, 36 insertions(+), 67 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index af87fbf0..9fe601ac 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -57,32 +57,6 @@ pid_t find_process_by_pid(pid_t pid) return (kill(pid, 0) == 0) ? pid : -pid; } -static pid_t getppid_of(pid_t pid) -{ - /* process iterator */ - int ret; - struct process_iterator it; - struct process proc; - struct process_filter filter; - filter.pid = pid; - filter.include_children = 0; - if (init_process_iterator(&it, &filter) != 0) - exit(1); - ret = get_next_process(&it, &proc); - if (close_process_iterator(&it) != 0) - exit(1); - return ret == 0 ? proc.ppid : (pid_t)(-1); -} - -static int is_child_of(pid_t child_pid, pid_t parent_pid) -{ - while (child_pid > 1 && child_pid != parent_pid) - { - child_pid = getppid_of(child_pid); - } - return child_pid == parent_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 diff --git a/src/process_iterator.c b/src/process_iterator.c index c23e065c..c8f78d26 100644 --- a/src/process_iterator.c +++ b/src/process_iterator.c @@ -27,6 +27,17 @@ /* See this link to port to other systems: http://www.steve.org.uk/Reference/Unix/faq_8.html#SEC85 */ +int is_child_of(pid_t child_pid, pid_t parent_pid) +{ + if (child_pid <= 0 || parent_pid <= 0) + return 0; + while (child_pid > 1 && child_pid != parent_pid) + { + child_pid = getppid_of(child_pid); + } + return child_pid == parent_pid; +} + #if defined(__linux__) #include "process_iterator_linux.c" diff --git a/src/process_iterator.h b/src/process_iterator.h index 332c9791..478a5393 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -105,4 +105,8 @@ int get_next_process(struct process_iterator *i, struct process *p); int close_process_iterator(struct process_iterator *i); +int is_child_of(pid_t child_pid, pid_t parent_pid); + +pid_t getppid_of(pid_t pid); + #endif diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 2acb7efd..480b50a8 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -123,7 +123,7 @@ static int get_process_pti(pid_t pid, struct proc_taskallinfo *ti) return 0; } -static pid_t getppid_of(pid_t pid) +pid_t getppid_of(pid_t pid) { struct proc_taskallinfo ti; if (get_process_pti(pid, &ti) == 0) @@ -133,16 +133,6 @@ static pid_t getppid_of(pid_t pid) return (pid_t)(-1); } -static int is_child_of(pid_t child_pid, pid_t parent_pid) -{ - pid_t 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->i == it->count) diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index 5324519f..3dab13ff 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -74,25 +74,23 @@ static int get_single_process(kvm_t *kd, pid_t pid, struct process *process) return 0; } -static pid_t getppid_of(kvm_t *kd, pid_t pid) +pid_t getppid_of(pid_t pid) { - int count; - struct kinfo_proc *kproc = kvm_getprocs(kd, KERN_PROC_PID, pid, &count); - if (count == 0 || kproc == NULL) - { - return -1; - } - return kproc->ki_ppid; -} - -static int is_child_of(kvm_t *kd, pid_t child_pid, pid_t parent_pid) -{ - pid_t ppid = child_pid; - while (ppid > 1 && ppid != parent_pid) - { - ppid = getppid_of(kd, ppid); - } - return ppid == parent_pid; + /* process iterator */ + int ret; + struct process_iterator it; + struct process proc; + struct process_filter filter; + if (pid <= 0) + return (pid_t)(-1); + filter.pid = pid; + filter.include_children = 0; + if (init_process_iterator(&it, &filter) != 0) + exit(1); + ret = get_next_process(&it, &proc); + if (close_process_iterator(&it) != 0) + exit(1); + return ret == 0 ? proc.ppid : (pid_t)(-1); } int get_next_process(struct process_iterator *it, struct process *p) @@ -125,7 +123,7 @@ int get_next_process(struct process_iterator *it, struct process *p) kproc2proc(it->kd, kproc, p); it->i++; if (p->pid != it->filter->pid && - !is_child_of(it->kd, p->pid, it->filter->pid)) + !is_child_of(p->pid, it->filter->pid)) continue; return 0; } diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 794f4612..8426f677 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -94,11 +94,13 @@ static int read_process_info(pid_t pid, struct process *p) return -1; } -static pid_t getppid_of(pid_t pid) +pid_t getppid_of(pid_t pid) { char statfile[32]; FILE *fd; long ppid = -1; + if (pid <= 0) + return (pid_t)(-1); sprintf(statfile, "/proc/%ld/stat", (long)pid); if ((fd = fopen(statfile, "r")) != NULL) { @@ -109,16 +111,6 @@ static pid_t getppid_of(pid_t pid) return (pid_t)ppid; } -static int is_child_of(pid_t child_pid, pid_t parent_pid) -{ - pid_t 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) { struct dirent *dit = NULL; From 63213251205a119464e6fc3ade6376e2f6abb04f Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 25 Jan 2023 03:21:19 +0800 Subject: [PATCH 061/209] revert efficient code for FreeBSD --- src/process_iterator_freebsd.c | 44 ++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index 3dab13ff..e28fb8b0 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -74,23 +74,37 @@ static int get_single_process(kvm_t *kd, pid_t pid, struct process *process) return 0; } +static pid_t _getppid_of(kvm_t *kd, pid_t pid) +{ + int count; + struct kinfo_proc *kproc = kvm_getprocs(kd, KERN_PROC_PID, pid, &count); + return (count == 0 || kproc == NULL) ? (pid_t)(-1) : kproc->ki_ppid; +} + pid_t getppid_of(pid_t pid) { - /* process iterator */ - int ret; - struct process_iterator it; - struct process proc; - struct process_filter filter; - if (pid <= 0) + static char errbuf[_POSIX2_LINE_MAX]; + kvm_t *kd; + pid_t ppid; + if ((kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf)) == NULL) + { + fprintf(stderr, "kvm_open: %s\n", errbuf); return (pid_t)(-1); - filter.pid = pid; - filter.include_children = 0; - if (init_process_iterator(&it, &filter) != 0) - exit(1); - ret = get_next_process(&it, &proc); - if (close_process_iterator(&it) != 0) - exit(1); - return ret == 0 ? proc.ppid : (pid_t)(-1); + } + ppid = _getppid_of(kd, pid); + kvm_close(kd); + return ppid; +} + +static int _is_child_of(kvm_t *kd, pid_t child_pid, pid_t parent_pid) +{ + if (child_pid <= 0 || parent_pid <= 0) + return 0; + while (child_pid > 1 && child_pid != parent_pid) + { + child_pid = _getppid_of(kd, child_pid); + } + return child_pid == parent_pid; } int get_next_process(struct process_iterator *it, struct process *p) @@ -123,7 +137,7 @@ int get_next_process(struct process_iterator *it, struct process *p) kproc2proc(it->kd, kproc, p); it->i++; if (p->pid != it->filter->pid && - !is_child_of(p->pid, it->filter->pid)) + !_is_child_of(it->kd, p->pid, it->filter->pid)) continue; return 0; } From 538db7881053d81ad0653ca3044c2a2acb3ebff5 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 25 Jan 2023 02:36:41 +0800 Subject: [PATCH 062/209] improve SIGINT and SIGTERM processing --- src/cpulimit.c | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index ea33cba0..f83bae8f 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -96,27 +96,14 @@ int verbose = 0; int lazy = 0; /* SIGINT and SIGTERM signal handler */ +volatile sig_atomic_t quit_flag = 0; #ifdef __GNUC__ static void quit(__attribute__((__unused__)) int sig) #else static void quit(int sig) #endif { - /* let all the processes continue if stopped */ - struct list_node *node = NULL; - if (pgroup.proclist != NULL) - { - for (node = pgroup.proclist->first; node != NULL; node = node->next) - { - struct process *p = (struct process *)(node->data); - kill(p->pid, SIGCONT); - } - close_process_group(&pgroup); - } - /* fix ^C little problem */ - printf("\r"); - fflush(stdout); - exit(0); + quit_flag = 1; } static void print_usage(FILE *stream, int exit_code) @@ -222,7 +209,7 @@ static void limit_process(pid_t pid, double limit, int include_children) printf("Members in the process group owned by %ld: %d\n", (long)pgroup.target_pid, pgroup.proclist->count); - while (1) + while (!quit_flag) { /* total cpu actual usage (range 0-1) */ /* 1 means that the processes are using 100% cpu */ @@ -325,7 +312,24 @@ static void limit_process(pid_t pid, double limit, int include_children) } c = (c + 1) % 200; } + + if (quit_flag) + { + for (node = pgroup.proclist->first; node != NULL; node = node->next) + { + struct process *p = (struct process *)(node->data); + kill(p->pid, SIGCONT); + } + } + close_process_group(&pgroup); + + if (quit_flag) + { + /* fix ^C little problem */ + printf("\r"); + exit(0); + } } int main(int argc, char *argv[]) @@ -360,6 +364,8 @@ int main(int argc, char *argv[]) struct timespec wait_time = {2, 0}; + struct sigaction sa; + /* get program name */ program_name = basename(argv[0]); /* get current pid */ @@ -447,8 +453,11 @@ int main(int argc, char *argv[]) } /* all arguments are ok! */ - signal(SIGINT, quit); - signal(SIGTERM, quit); + sa.sa_handler = quit; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); /* print the number of available cpu */ if (verbose) @@ -526,7 +535,6 @@ int main(int argc, char *argv[]) if (verbose) printf("Limiting process %ld\n", (long)child); limit_process(child, limit, include_children); - exit(0); } } } From 39a80bca3f2afe01669e58157d9287632c94fcb4 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 25 Jan 2023 23:52:28 +0800 Subject: [PATCH 063/209] fix cppcheck warnings --- src/process_group.c | 6 ++---- src/process_iterator_apple.c | 2 +- src/process_iterator_linux.c | 14 +++++--------- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index 9fe601ac..6018f290 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -73,15 +73,13 @@ pid_t find_process_by_name(char *process_name) struct process proc; struct process_filter filter; const char *process_basename = basename(process_name); - const char *command_basename; - int cmp_len; filter.pid = 0; filter.include_children = 0; init_process_iterator(&it, &filter); while (get_next_process(&it, &proc) != -1) { - command_basename = basename(proc.command); - cmp_len = proc.max_cmd_len - (command_basename - proc.command); + const char *command_basename = basename(proc.command); + int cmp_len = proc.max_cmd_len - (command_basename - proc.command); /* process found */ if (cmp_len > 0 && command_basename[0] != '\0' && strncmp(command_basename, process_basename, cmp_len) == 0) diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 480b50a8..76160ad9 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -117,7 +117,7 @@ static int get_process_pti(pid_t pid, struct proc_taskallinfo *ti) } else if (bytes < (int)sizeof(ti)) { - fprintf(stderr, "proc_pidinfo: too few bytes; expected %ld, got %d\n", sizeof(ti), bytes); + fprintf(stderr, "proc_pidinfo: too few bytes; expected %lu, got %d\n", (unsigned long)sizeof(ti), bytes); return -1; } return 0; diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 8426f677..d33142e0 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -141,16 +141,12 @@ int get_next_process(struct process_iterator *it, struct process *p) !is_child_of(p->pid, it->filter->pid)) continue; read_process_info(p->pid, p); - break; - } - if (dit == NULL) - { - /* end of processes */ - closedir(it->dip); - it->dip = NULL; - return -1; + return 0; } - return 0; + /* end of processes */ + closedir(it->dip); + it->dip = NULL; + return -1; } int close_process_iterator(struct process_iterator *it) From f0084e8da583de1616a6e367c978283a738fd3df Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 26 Jan 2023 00:06:31 +0800 Subject: [PATCH 064/209] improve tests --- tests/process_iterator_test.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index f2614912..a9cc0db3 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -30,8 +30,8 @@ #include #include -#include "process_iterator.h" -#include "process_group.h" +#include "../src/process_iterator.h" +#include "../src/process_group.h" #define MAX_PRIORITY -20 @@ -56,15 +56,12 @@ static void increase_priority(void) (nanosleep((t), NULL)) #endif -volatile sig_atomic_t child; - #ifdef __GNUC__ -static void kill_child(__attribute__((__unused__)) int sig) +static void ignore_signal(__attribute__((__unused__)) int sig) #else -static void kill_child(int sig) +static void ignore_signal(int sig) #endif { - kill(child, SIGINT); } static void test_single_process(void) @@ -133,7 +130,7 @@ static void test_multiple_process(void) } assert(count == 2); close_process_iterator(&it); - kill(child, SIGINT); + kill(child, SIGKILL); } static void test_all_processes(void) @@ -180,7 +177,7 @@ static void test_process_group_single(int include_children) struct process_group pgroup; int i; double tot_usage = 0; - child = fork(); + pid_t child = fork(); if (child == 0) { /* child is supposed to be killed by the parent :/ */ @@ -189,8 +186,6 @@ static void test_process_group_single(int include_children) ; exit(1); } - signal(SIGABRT, &kill_child); - signal(SIGTERM, &kill_child); assert(init_process_group(&pgroup, child, include_children) == 0); for (i = 0; i < 200; i++) { @@ -214,7 +209,7 @@ static void test_process_group_single(int include_children) } assert(tot_usage / i < 1.1 && tot_usage / i > 0.7); assert(close_process_group(&pgroup) == 0); - kill(child, SIGINT); + kill(child, SIGKILL); } char *command = NULL; @@ -272,6 +267,14 @@ int main(__attribute__((__unused__)) int argc, char *argv[]) int main(int argc, char *argv[]) #endif { + /* ignore SIGINT and SIGTERM during tests*/ + struct sigaction sa; + sa.sa_handler = ignore_signal; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + command = argv[0]; increase_priority(); test_single_process(); From 6f184881db91676b29756dd3fe516139e4e916cd Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 26 Jan 2023 02:10:58 +0800 Subject: [PATCH 065/209] fix process_iterator --- src/process_iterator.c | 11 ----- src/process_iterator_apple.c | 21 +++++++--- src/process_iterator_freebsd.c | 44 +++++++++++++------- src/process_iterator_linux.c | 73 +++++++++++++++++++++++----------- 4 files changed, 95 insertions(+), 54 deletions(-) diff --git a/src/process_iterator.c b/src/process_iterator.c index c8f78d26..c23e065c 100644 --- a/src/process_iterator.c +++ b/src/process_iterator.c @@ -27,17 +27,6 @@ /* See this link to port to other systems: http://www.steve.org.uk/Reference/Unix/faq_8.html#SEC85 */ -int is_child_of(pid_t child_pid, pid_t parent_pid) -{ - if (child_pid <= 0 || parent_pid <= 0) - return 0; - while (child_pid > 1 && child_pid != parent_pid) - { - child_pid = getppid_of(child_pid); - } - return child_pid == parent_pid; -} - #if defined(__linux__) #include "process_iterator_linux.c" diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 76160ad9..1f1426af 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -85,7 +85,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi return 0; } -static int pti2proc(struct proc_taskallinfo *ti, struct process *process) +static void pti2proc(struct proc_taskallinfo *ti, struct process *process) { process->pid = ti->pbsd.pbi_pid; process->ppid = ti->pbsd.pbi_ppid; @@ -100,7 +100,6 @@ static int pti2proc(struct proc_taskallinfo *ti, struct process *process) process->max_cmd_len = MIN(sizeof(process->command), sizeof(ti->pbsd.pbi_comm)) - 1; memcpy(process->command, ti->pbsd.pbi_comm, process->max_cmd_len + 1); } - return 0; } static int get_process_pti(pid_t pid, struct proc_taskallinfo *ti) @@ -133,6 +132,17 @@ pid_t getppid_of(pid_t pid) return (pid_t)(-1); } +int is_child_of(pid_t child_pid, pid_t parent_pid) +{ + if (child_pid <= 0 || parent_pid <= 0 || child_pid == parent_pid) + return 0; + while (child_pid > 1 && child_pid != parent_pid) + { + child_pid = getppid_of(child_pid); + } + return child_pid == parent_pid; +} + int get_next_process(struct process_iterator *it, struct process *p) { if (it->i == it->count) @@ -146,7 +156,8 @@ int get_next_process(struct process_iterator *it, struct process *p) return -1; } it->i = it->count = 1; - return pti2proc(&ti, p); + pti2proc(&ti, p); + return 0; } while (it->i < it->count) { @@ -163,8 +174,8 @@ int get_next_process(struct process_iterator *it, struct process *p) } if (it->filter->pid != 0 && it->filter->include_children) { - pti2proc(&ti, p); it->i++; + pti2proc(&ti, p); if (p->pid != it->pidlist[it->i - 1]) /* I don't know why this can happen */ continue; if (p->pid != it->filter->pid && !is_child_of(p->pid, it->filter->pid)) @@ -173,8 +184,8 @@ int get_next_process(struct process_iterator *it, struct process *p) } else if (it->filter->pid == 0) { - pti2proc(&ti, p); it->i++; + pti2proc(&ti, p); return 0; } } diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index e28fb8b0..feb62966 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -32,7 +32,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi /* 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); + fprintf(stderr, "kvm_openfiles: %s\n", errbuf); return -1; } /* Get the list of processes. */ @@ -45,21 +45,22 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi return 0; } -static int kproc2proc(kvm_t *kd, struct kinfo_proc *kproc, struct process *proc) +static void kproc2proc(kvm_t *kd, struct kinfo_proc *kproc, struct process *proc) { char **args; proc->pid = kproc->ki_pid; proc->ppid = kproc->ki_ppid; proc->cputime = kproc->ki_runtime / 1000.0; proc->max_cmd_len = sizeof(proc->command) - 1; - if ((args = kvm_getargv(kd, kproc, sizeof(proc->command))) == NULL) + if ((args = kvm_getargv(kd, kproc, sizeof(proc->command))) != NULL) + { + strncpy(proc->command, args[0], proc->max_cmd_len); + proc->command[proc->max_cmd_len] = '\0'; + } + else { proc->command[0] = '\0'; - return -1; } - strncpy(proc->command, args[0], proc->max_cmd_len); - proc->command[proc->max_cmd_len] = '\0'; - return 0; } static int get_single_process(kvm_t *kd, pid_t pid, struct process *process) @@ -83,12 +84,12 @@ static pid_t _getppid_of(kvm_t *kd, pid_t pid) pid_t getppid_of(pid_t pid) { - static char errbuf[_POSIX2_LINE_MAX]; - kvm_t *kd; pid_t ppid; - if ((kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf)) == NULL) + static char errbuf[_POSIX2_LINE_MAX]; + kvm_t *kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); + if (kd == NULL) { - fprintf(stderr, "kvm_open: %s\n", errbuf); + fprintf(stderr, "kvm_openfiles: %s\n", errbuf); return (pid_t)(-1); } ppid = _getppid_of(kd, pid); @@ -98,7 +99,7 @@ pid_t getppid_of(pid_t pid) static int _is_child_of(kvm_t *kd, pid_t child_pid, pid_t parent_pid) { - if (child_pid <= 0 || parent_pid <= 0) + if (child_pid <= 0 || parent_pid <= 0 || child_pid == parent_pid) return 0; while (child_pid > 1 && child_pid != parent_pid) { @@ -107,6 +108,21 @@ static int _is_child_of(kvm_t *kd, pid_t child_pid, pid_t parent_pid) return child_pid == parent_pid; } +int is_child_of(pid_t child_pid, pid_t parent_pid) +{ + int ret; + static char errbuf[_POSIX2_LINE_MAX]; + kvm_t *kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); + if (kd == NULL) + { + fprintf(stderr, "kvm_openfiles: %s\n", errbuf); + return (pid_t)(-1); + } + ret = _is_child_of(kd, child_pid, parent_pid); + kvm_close(kd); + return ret; +} + int get_next_process(struct process_iterator *it, struct process *p) { if (it->i == it->count) @@ -134,8 +150,8 @@ int get_next_process(struct process_iterator *it, struct process *p) } if (it->filter->pid != 0 && it->filter->include_children) { - kproc2proc(it->kd, kproc, p); it->i++; + kproc2proc(it->kd, kproc, p); if (p->pid != it->filter->pid && !_is_child_of(it->kd, p->pid, it->filter->pid)) continue; @@ -143,8 +159,8 @@ int get_next_process(struct process_iterator *it, struct process *p) } else if (it->filter->pid == 0) { - kproc2proc(it->kd, kproc, p); it->i++; + kproc2proc(it->kd, kproc, p); return 0; } } diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index d33142e0..17977ca0 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -54,44 +54,57 @@ static int read_process_info(pid_t pid, struct process *p) char statfile[32], exefile[32], state; long utime, stime, ppid; FILE *fd; + int ret = 0; p->pid = pid; /* read command line */ sprintf(exefile, "/proc/%ld/cmdline", (long)p->pid); - fd = fopen(exefile, "r"); - if (fd == NULL) - goto error_out1; - p->max_cmd_len = sizeof(p->command) - 1; - if (fgets(p->command, sizeof(p->command), fd) == NULL) + if ((fd = fopen(exefile, "r")) != NULL) { + if (fgets(p->command, sizeof(p->command), fd) == NULL) + { + ret = -1; + } + else + { + p->max_cmd_len = sizeof(p->command) - 1; + } fclose(fd); - goto error_out1; } - fclose(fd); + else + { + ret = -1; + } + + if (ret != 0) + { + return ret; + } /* read stat file */ sprintf(statfile, "/proc/%ld/stat", (long)p->pid); - fd = fopen(statfile, "r"); - if (fd == NULL) - goto error_out2; - - if (fscanf(fd, "%*d (%*[^)]) %c %ld %*d %*d %*d %*d %*d %*d %*d %*d %*d %ld %ld", - &state, &ppid, &utime, &stime) != 4 || - state == 'Z' || state == 'X') + if ((fd = fopen(statfile, "r")) != NULL) { + if (fscanf(fd, "%*d (%*[^)]) %c %ld %*d %*d %*d %*d %*d %*d %*d %*d %*d %ld %ld", + &state, &ppid, &utime, &stime) != 4 || + state == 'Z' || state == 'X') + { + ret = -1; + } + else + { + p->ppid = (pid_t)ppid; + p->cputime = utime * 1000.0 / HZ + stime * 1000.0 / HZ; + } fclose(fd); - goto error_out2; } - fclose(fd); - p->ppid = (pid_t)ppid; - p->cputime = utime * 1000.0 / HZ + stime * 1000.0 / HZ; - return 0; + else + { + ret = -1; + } -error_out1: - p->command[0] = '\0'; -error_out2: - return -1; + return ret; } pid_t getppid_of(pid_t pid) @@ -111,6 +124,17 @@ pid_t getppid_of(pid_t pid) return (pid_t)ppid; } +int is_child_of(pid_t child_pid, pid_t parent_pid) +{ + if (child_pid <= 0 || parent_pid <= 0 || child_pid == parent_pid) + return 0; + while (child_pid > 1 && child_pid != parent_pid) + { + child_pid = getppid_of(child_pid); + } + return child_pid == parent_pid; +} + int get_next_process(struct process_iterator *it, struct process *p) { struct dirent *dit = NULL; @@ -140,7 +164,8 @@ int get_next_process(struct process_iterator *it, struct process *p) it->filter->pid != p->pid && !is_child_of(p->pid, it->filter->pid)) continue; - read_process_info(p->pid, p); + if (read_process_info(p->pid, p) != 0) + continue; return 0; } /* end of processes */ From 29def4abf91e9cda69bb46a2da0f8ce675d09a69 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 26 Jan 2023 01:23:51 +0800 Subject: [PATCH 066/209] add test_getppid_of --- tests/process_iterator_test.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index a9cc0db3..989818fb 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -261,6 +261,22 @@ static void test_find_process_by_name(void) assert(find_process_by_name("") == 0); } +static void test_getppid_of(void) +{ + struct process_iterator it; + struct process process; + struct process_filter filter; + filter.pid = 0; + filter.include_children = 0; + init_process_iterator(&it, &filter); + while (get_next_process(&it, &process) == 0) + { + assert(getppid_of(process.pid) == process.ppid); + } + close_process_iterator(&it); + assert(getppid_of(getpid()) == getppid()); +} + #ifdef __GNUC__ int main(__attribute__((__unused__)) int argc, char *argv[]) #else @@ -287,5 +303,6 @@ int main(int argc, char *argv[]) test_process_name(); test_find_process_by_pid(); test_find_process_by_name(); + test_getppid_of(); return 0; } From dbcb7a75fd05b9bd86d7b1c2cf318b27290fdd83 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 26 Jan 2023 02:56:19 +0800 Subject: [PATCH 067/209] add conditional compilations --- src/process_iterator_apple.c | 8 ++++++++ src/process_iterator_freebsd.c | 8 ++++++++ src/process_iterator_linux.c | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 1f1426af..d8355e10 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -22,6 +22,11 @@ * */ +#ifdef __APPLE__ + +#ifndef __PROCESS_ITERATOR_APPLE_C +#define __PROCESS_ITERATOR_APPLE_C + #include #include #include @@ -201,3 +206,6 @@ int close_process_iterator(struct process_iterator *it) it->i = 0; return 0; } + +#endif +#endif diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index feb62966..8f4e0f4a 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -19,6 +19,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#ifdef __FreeBSD__ + +#ifndef __PROCESS_ITERATOR_FREEBSD_C +#define __PROCESS_ITERATOR_FREEBSD_C + #include #include #include @@ -176,3 +181,6 @@ int close_process_iterator(struct process_iterator *it) } return 0; } + +#endif +#endif diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 17977ca0..9684f6b1 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -19,6 +19,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#ifdef __linux__ + +#ifndef __PROCESS_ITERATOR_LINUX_C +#define __PROCESS_ITERATOR_LINUX_C + #include #include @@ -184,3 +189,6 @@ int close_process_iterator(struct process_iterator *it) it->dip = NULL; return 0; } + +#endif +#endif From efe761db41012b727527fa4187293653bc64b347 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 26 Jan 2023 05:37:52 +0800 Subject: [PATCH 068/209] add checks for malloc --- src/list.c | 4 ++++ src/process_group.c | 16 ++++++++++++++++ src/process_iterator_apple.c | 6 ++++++ 3 files changed, 26 insertions(+) diff --git a/src/list.c b/src/list.c index 04ed275a..d9da7fdc 100644 --- a/src/list.c +++ b/src/list.c @@ -36,6 +36,10 @@ void init_list(struct list *l, int keysize) struct list_node *add_elem(struct list *l, void *elem) { struct list_node *newnode = malloc(sizeof(struct list_node)); + if (newnode == NULL) + { + exit(-1); + } newnode->data = elem; newnode->previous = l->last; newnode->next = NULL; diff --git a/src/process_group.c b/src/process_group.c index 6018f290..5513a70d 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -122,6 +122,10 @@ int init_process_group(struct process_group *pgroup, pid_t target_pid, int inclu pgroup->target_pid = target_pid; pgroup->include_children = include_children; pgroup->proclist = malloc(sizeof(struct list)); + if (pgroup->proclist == NULL) + { + exit(-1); + } init_list(pgroup->proclist, sizeof(pid_t)); if (get_time(&pgroup->last_update)) { @@ -186,7 +190,15 @@ void update_process_group(struct process_group *pgroup) { /* empty bucket */ struct process *new_process = malloc(sizeof(struct process)); + if (new_process == NULL) + { + exit(-1); + } pgroup->proctable[hashkey] = malloc(sizeof(struct list)); + if (pgroup->proctable[hashkey] == NULL) + { + exit(-1); + } tmp_process.cpu_usage = -1; memcpy(new_process, &tmp_process, sizeof(struct process)); init_list(pgroup->proctable[hashkey], sizeof(pid_t)); @@ -201,6 +213,10 @@ void update_process_group(struct process_group *pgroup) { /* process is new. add it */ struct process *new_process = malloc(sizeof(struct process)); + if (new_process == NULL) + { + exit(-1); + } tmp_process.cpu_usage = -1; memcpy(new_process, &tmp_process, sizeof(struct process)); add_elem(pgroup->proctable[hashkey], new_process); diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index d8355e10..65e00af1 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -31,6 +31,7 @@ #include #include #include +#include static int unique_nonzero_pids(pid_t *arr_in, int len_in, pid_t *arr_out) { @@ -42,6 +43,10 @@ static int unique_nonzero_pids(pid_t *arr_in, int len_in, pid_t *arr_out) if (arr_in == arr_out) { source = malloc(sizeof(pid_t) * len_in); + if (source == NULL) + { + exit(-1); + } memcpy(source, arr_in, sizeof(pid_t) * len_in); memset(arr_out, -1, sizeof(pid_t) * len_in); } @@ -79,6 +84,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi if ((it->pidlist = malloc((it->count) * sizeof(pid_t))) == NULL) { fprintf(stderr, "malloc: %s\n", strerror(errno)); + exit(-1); } if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, it->pidlist, it->count)) <= 0) { From 71d8a3875bd529c4d96a6b0d5087f5f1fc58f460 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 26 Jan 2023 23:15:42 +0800 Subject: [PATCH 069/209] refine read_process_info for linux --- src/process_iterator_linux.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 9684f6b1..ef8af8f4 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -26,6 +26,7 @@ #include #include +#include static int check_proc(void) { @@ -57,7 +58,8 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi static int read_process_info(pid_t pid, struct process *p) { char statfile[32], exefile[32], state; - long utime, stime, ppid; + unsigned long utime, stime; + long ppid; FILE *fd; int ret = 0; @@ -91,9 +93,9 @@ static int read_process_info(pid_t pid, struct process *p) sprintf(statfile, "/proc/%ld/stat", (long)p->pid); if ((fd = fopen(statfile, "r")) != NULL) { - if (fscanf(fd, "%*d (%*[^)]) %c %ld %*d %*d %*d %*d %*d %*d %*d %*d %*d %ld %ld", + if (fscanf(fd, "%*d (%*[^)]) %c %ld %*d %*d %*d %*d %*d %*d %*d %*d %*d %lu %lu", &state, &ppid, &utime, &stime) != 4 || - state == 'Z' || state == 'X') + strchr("ZXx", state) != NULL) { ret = -1; } From 40b63ea628bbb8bf0029578688415d9b6b2e0993 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 29 Jan 2023 22:35:37 +0800 Subject: [PATCH 070/209] fix conditional compilation of get_time() --- src/process_group.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/process_group.c b/src/process_group.c index 5513a70d..84b8255e 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -31,7 +31,7 @@ #include "process_group.h" #include "list.h" -#if _POSIX_C_SOURCE >= 199309L +#ifdef CLOCK_MONOTONIC #define get_time(ts) \ (clock_gettime(CLOCK_MONOTONIC, (ts))) #else From 6ae1882892d783b84ec11c806e6b19203b7bedf9 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 29 Jan 2023 22:46:43 +0800 Subject: [PATCH 071/209] simplify process_iterator_apple --- src/process_iterator_apple.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 65e00af1..32a15ceb 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -48,7 +48,6 @@ static int unique_nonzero_pids(pid_t *arr_in, int len_in, pid_t *arr_out) exit(-1); } memcpy(source, arr_in, sizeof(pid_t) * len_in); - memset(arr_out, -1, sizeof(pid_t) * len_in); } for (i = 0; i < len_in; i++) { @@ -57,7 +56,7 @@ static int unique_nonzero_pids(pid_t *arr_in, int len_in, pid_t *arr_out) continue; for (j = 0; !found && j < len_out; j++) { - found = (source[i] == arr_out[j]) ? 1 : 0; + found = (source[i] == arr_out[j]); } if (!found) { From 4c77be24341cd8b2b742e9fed3cd994f568e52cc Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 29 Jan 2023 22:52:52 +0800 Subject: [PATCH 072/209] fix conditional compilation of sleep_timespec() --- src/cpulimit.c | 2 +- tests/process_iterator_test.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index f83bae8f..e7759c16 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -57,7 +57,7 @@ } while (0) /* inline int sleep_timespec(struct timespec *t); */ -#if _POSIX_C_SOURCE >= 200809L +#if _POSIX_C_SOURCE >= 200809L && defined(CLOCK_MONOTONIC) #define sleep_timespec(t) \ (clock_nanosleep(CLOCK_MONOTONIC, 0, (t), NULL)) #else diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index 989818fb..69700f45 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -48,7 +48,7 @@ static void increase_priority(void) } /* inline int sleep_timespec(struct timespec *t); */ -#if _POSIX_C_SOURCE >= 200809L +#if _POSIX_C_SOURCE >= 200809L && defined(CLOCK_MONOTONIC) #define sleep_timespec(t) \ (clock_nanosleep(CLOCK_MONOTONIC, 0, (t), NULL)) #else From e036c86d5d17a95ab1e24e3c6d3e847ea37d5a2f Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 1 Feb 2023 04:11:23 +0800 Subject: [PATCH 073/209] use basename() implemented in libgen.h --- src/cpulimit.c | 6 +++++- src/process_group.c | 15 ++++++++++++--- src/process_group.h | 4 ---- tests/process_iterator_test.c | 13 ++++++++----- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index e7759c16..507b88a0 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "process_group.h" #include "list.h" @@ -366,8 +367,11 @@ int main(int argc, char *argv[]) struct sigaction sa; + static char program_base_name[PATH_MAX + 1]; /* get program name */ - program_name = basename(argv[0]); + strncpy(program_base_name, basename(argv[0]), sizeof(program_base_name) - 1); + program_base_name[sizeof(program_base_name) - 1] = '\0'; + program_name = program_base_name; /* get current pid */ cpulimit_pid = getpid(); /* get cpu count */ diff --git a/src/process_group.c b/src/process_group.c index 84b8255e..f0ead74a 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "process_iterator.h" #include "process_group.h" @@ -72,14 +73,22 @@ pid_t find_process_by_name(char *process_name) struct process_iterator it; struct process proc; struct process_filter filter; - const char *process_basename = basename(process_name); + static char process_basename[PATH_MAX + 1]; + static char command_basename[PATH_MAX + 1]; + strncpy(process_basename, basename(process_name), + sizeof(process_basename) - 1); + process_basename[sizeof(process_basename) - 1] = '\0'; filter.pid = 0; filter.include_children = 0; init_process_iterator(&it, &filter); while (get_next_process(&it, &proc) != -1) { - const char *command_basename = basename(proc.command); - int cmp_len = proc.max_cmd_len - (command_basename - proc.command); + int cmp_len; + strncpy(command_basename, basename(proc.command), + sizeof(command_basename) - 1); + command_basename[sizeof(command_basename) - 1] = '\0'; + cmp_len = proc.max_cmd_len - + (strlen(proc.command) - strlen(command_basename)); /* process found */ if (cmp_len > 0 && command_basename[0] != '\0' && strncmp(command_basename, process_basename, cmp_len) == 0) diff --git a/src/process_group.h b/src/process_group.h index 35f5b848..c0b1339d 100644 --- a/src/process_group.h +++ b/src/process_group.h @@ -33,10 +33,6 @@ #define PIDHASH_SZ 1024 #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1)) -#undef basename -#define basename(x) \ - ((strrchr((x), '/') != NULL) ? (strrchr((x), '/') + 1) : (x)) - struct process_group { /* hashtable with all the processes (array of struct list of struct process) */ diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index 69700f45..659294cc 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "../src/process_iterator.h" #include "../src/process_group.h" @@ -219,8 +220,8 @@ static void test_process_name(void) struct process_iterator it; struct process process; struct process_filter filter; - const char *command_basename; - const char *process_basename; + static char command_basename[PATH_MAX + 1]; + static char process_basename[PATH_MAX + 1]; int cmp_len; filter.pid = getpid(); filter.include_children = 0; @@ -228,9 +229,11 @@ static void test_process_name(void) assert(get_next_process(&it, &process) == 0); assert(process.pid == getpid()); assert(process.ppid == getppid()); - command_basename = basename(command); - process_basename = basename(process.command); - cmp_len = process.max_cmd_len - (process_basename - process.command); + strncpy(command_basename, basename(command), sizeof(command_basename) - 1); + command_basename[sizeof(command_basename) - 1] = '\0'; + strncpy(process_basename, basename(process.command), sizeof(process_basename) - 1); + process_basename[sizeof(process_basename) - 1] = '\0'; + cmp_len = process.max_cmd_len - (strlen(process.command) - strlen(process_basename)); assert(strncmp(command_basename, process_basename, cmp_len) == 0); assert(get_next_process(&it, &process) != 0); close_process_iterator(&it); From deaed61f1c7215c3e8f83507f68c19dfa593521f Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 1 Feb 2023 06:09:03 +0800 Subject: [PATCH 074/209] fix termination of cpulimit --- src/cpulimit.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 507b88a0..c792143e 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -324,12 +324,14 @@ static void limit_process(pid_t pid, double limit, int include_children) } close_process_group(&pgroup); +} +static void quit_handler(void) +{ if (quit_flag) { /* fix ^C little problem */ printf("\r"); - exit(0); } } @@ -368,6 +370,9 @@ int main(int argc, char *argv[]) struct sigaction sa; static char program_base_name[PATH_MAX + 1]; + + atexit(quit_handler); + /* get program name */ strncpy(program_base_name, basename(argv[0]), sizeof(program_base_name) - 1); program_base_name[sizeof(program_base_name) - 1] = '\0'; @@ -539,11 +544,12 @@ int main(int argc, char *argv[]) if (verbose) printf("Limiting process %ld\n", (long)child); limit_process(child, limit, include_children); + exit(0); } } } - while (1) + while (!quit_flag) { /* look for the target process..or wait for it */ pid_t ret = 0; @@ -589,11 +595,11 @@ int main(int argc, char *argv[]) /* control */ limit_process(pid, limit, include_children); } - if (lazy) + if (lazy || quit_flag) break; /* wait for 2 seconds before next search */ sleep_timespec(&wait_time); - }; + } return 0; } From cdec02b7ddabaee3d3d1452364770de839b232a0 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 19 Feb 2023 05:10:33 +0800 Subject: [PATCH 075/209] fix build on non-linux platforms --- src/cpulimit.c | 4 ++-- src/process_group.c | 12 +++++++++--- tests/process_iterator_test.c | 4 ++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index c792143e..756956e9 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -58,9 +58,9 @@ } while (0) /* inline int sleep_timespec(struct timespec *t); */ -#if _POSIX_C_SOURCE >= 200809L && defined(CLOCK_MONOTONIC) +#if defined(__linux__) && defined(CLOCK_TAI) #define sleep_timespec(t) \ - (clock_nanosleep(CLOCK_MONOTONIC, 0, (t), NULL)) + (clock_nanosleep(CLOCK_TAI, 0, (t), NULL)) #else #define sleep_timespec(t) \ (nanosleep((t), NULL)) diff --git a/src/process_group.c b/src/process_group.c index f0ead74a..2b5d5bf6 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -32,10 +32,16 @@ #include "process_group.h" #include "list.h" -#ifdef CLOCK_MONOTONIC +#if defined(__linux__) +#if defined(CLOCK_TAI) +#define get_time(ts) \ + (clock_gettime(CLOCK_TAI, (ts))) +#elif defined(CLOCK_MONOTONIC) #define get_time(ts) \ (clock_gettime(CLOCK_MONOTONIC, (ts))) -#else +#endif +#endif +#ifndef get_time static int get_time(struct timespec *ts) { struct timeval tv; @@ -44,7 +50,7 @@ static int get_time(struct timespec *ts) return -1; } ts->tv_sec = tv.tv_sec; - ts->tv_nsec = tv.tv_usec * 1000; + ts->tv_nsec = tv.tv_usec * 1000L; return 0; } #endif diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index 659294cc..99e9b27a 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -49,9 +49,9 @@ static void increase_priority(void) } /* inline int sleep_timespec(struct timespec *t); */ -#if _POSIX_C_SOURCE >= 200809L && defined(CLOCK_MONOTONIC) +#if defined(__linux__) && defined(CLOCK_TAI) #define sleep_timespec(t) \ - (clock_nanosleep(CLOCK_MONOTONIC, 0, (t), NULL)) + (clock_nanosleep(CLOCK_TAI, 0, (t), NULL)) #else #define sleep_timespec(t) \ (nanosleep((t), NULL)) From 0ac8c80568e1902ca8606c299b751d6baf896100 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 23 Mar 2023 02:41:30 +0800 Subject: [PATCH 076/209] improve __attribute__((unused)) --- src/cpulimit.c | 10 +++++----- tests/busy.c | 10 +++++----- tests/process_iterator_test.c | 16 ++++++---------- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 756956e9..b822128f 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -41,6 +41,10 @@ #include "process_group.h" #include "list.h" +#ifndef __GNUC__ +#define __attribute__(attr) +#endif + /* some useful macro */ #ifndef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) @@ -98,11 +102,7 @@ int lazy = 0; /* SIGINT and SIGTERM signal handler */ volatile sig_atomic_t quit_flag = 0; -#ifdef __GNUC__ -static void quit(__attribute__((__unused__)) int sig) -#else -static void quit(int sig) -#endif +static void quit(int sig __attribute__((unused))) { quit_flag = 1; } diff --git a/tests/busy.c b/tests/busy.c index 4143a65a..eaaee028 100644 --- a/tests/busy.c +++ b/tests/busy.c @@ -5,6 +5,10 @@ #include #include +#ifndef __GNUC__ +#define __attribute__(attr) +#endif + #define MAX_PRIORITY -20 static void increase_priority(void) @@ -37,11 +41,7 @@ static int get_ncpu(void) return ncpu; } -#ifdef __GNUC__ -static void *loop(__attribute__((__unused__)) void *param) -#else -static void *loop(void *param) -#endif +static void *loop(void *param __attribute__((unused))) { while (1) ; diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index 99e9b27a..cc72586e 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -34,6 +34,10 @@ #include "../src/process_iterator.h" #include "../src/process_group.h" +#ifndef __GNUC__ +#define __attribute__(attr) +#endif + #define MAX_PRIORITY -20 static void increase_priority(void) @@ -57,11 +61,7 @@ static void increase_priority(void) (nanosleep((t), NULL)) #endif -#ifdef __GNUC__ -static void ignore_signal(__attribute__((__unused__)) int sig) -#else -static void ignore_signal(int sig) -#endif +static void ignore_signal(int sig __attribute__((unused))) { } @@ -280,11 +280,7 @@ static void test_getppid_of(void) assert(getppid_of(getpid()) == getppid()); } -#ifdef __GNUC__ -int main(__attribute__((__unused__)) int argc, char *argv[]) -#else -int main(int argc, char *argv[]) -#endif +int main(int argc __attribute__((unused)), char *argv[]) { /* ignore SIGINT and SIGTERM during tests*/ struct sigaction sa; From 96a71be689bf0a98ba07708d4f008a5ff9014c17 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 23 Mar 2023 02:58:39 +0800 Subject: [PATCH 077/209] fix comments --- src/cpulimit.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index b822128f..41fae347 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -100,8 +100,10 @@ int verbose = 0; /* lazy mode (exits if there is no process) */ int lazy = 0; -/* SIGINT and SIGTERM signal handler */ +/* quit flag for SIGINT and SIGTERM signals */ volatile sig_atomic_t quit_flag = 0; + +/* SIGINT and SIGTERM signal handler */ static void quit(int sig __attribute__((unused))) { quit_flag = 1; From 8bb30127061a936ff55badcbd8ed1c9439a0ec23 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 23 Jul 2023 23:12:51 +0800 Subject: [PATCH 078/209] update CI --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 4dba68f7..495d0d60 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -19,7 +19,7 @@ jobs: strategy: max-parallel: 20 matrix: - os: [ubuntu-22.04, ubuntu-20.04, macos-10.15, macos-11, macos-12] + os: [ubuntu-20.04, ubuntu-22.04, macos-11, macos-12, macos-13] runs-on: ${{ matrix.os }} steps: From 209a8f322603caf9bf01d4c560a04fcd38501966 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 6 Apr 2024 22:03:18 +0800 Subject: [PATCH 079/209] fix warning on basename() --- src/cpulimit.c | 8 +++++++- src/process_group.c | 8 +++++++- tests/process_iterator_test.c | 8 +++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 41fae347..0c21f0a4 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -36,7 +36,6 @@ #include #include #include -#include #include "process_group.h" #include "list.h" @@ -53,6 +52,13 @@ #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif +static char *__basename(char *path) +{ + char *p = strrchr(path, '/'); + return p != NULL ? p + 1 : path; +} +#define basename(path) __basename(path) + /* inline void nsec2timespec(double nsec, struct timespec *t); */ #define nsec2timespec(nsec, t) \ do \ diff --git a/src/process_group.c b/src/process_group.c index 2b5d5bf6..dfb459e0 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -26,7 +26,6 @@ #include #include #include -#include #include "process_iterator.h" #include "process_group.h" @@ -55,6 +54,13 @@ static int get_time(struct timespec *ts) } #endif +static char *__basename(char *path) +{ + char *p = strrchr(path, '/'); + return p != NULL ? p + 1 : path; +} +#define basename(path) __basename(path) + /* look for a process by pid search_pid : pid of the wanted process return: pid of the found process, if successful diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index cc72586e..00b0c87c 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -29,7 +29,6 @@ #include #include #include -#include #include "../src/process_iterator.h" #include "../src/process_group.h" @@ -61,6 +60,13 @@ static void increase_priority(void) (nanosleep((t), NULL)) #endif +static char *__basename(char *path) +{ + char *p = strrchr(path, '/'); + return p != NULL ? p + 1 : path; +} +#define basename(path) __basename(path) + static void ignore_signal(int sig __attribute__((unused))) { } From 1517fccfedea9c6e96224cb178c4280973149cf9 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 6 Apr 2024 22:08:20 +0800 Subject: [PATCH 080/209] improve get_time() --- src/process_group.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/process_group.c b/src/process_group.c index dfb459e0..f1fd597b 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -41,7 +41,7 @@ #endif #endif #ifndef get_time -static int get_time(struct timespec *ts) +static int __get_time(struct timespec *ts) { struct timeval tv; if (gettimeofday(&tv, NULL)) @@ -52,6 +52,7 @@ static int get_time(struct timespec *ts) ts->tv_nsec = tv.tv_usec * 1000L; return 0; } +#define get_time(ts) __get_time(ts) #endif static char *__basename(char *path) From 97cad360894be81d6729b6b8b673eafc077fec55 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 6 Apr 2024 22:23:16 +0800 Subject: [PATCH 081/209] improve code style --- src/cpulimit.c | 6 ++---- src/process_group.c | 6 ++---- tests/process_iterator_test.c | 6 ++---- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 0c21f0a4..4f52e31a 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -69,11 +69,9 @@ static char *__basename(char *path) /* inline int sleep_timespec(struct timespec *t); */ #if defined(__linux__) && defined(CLOCK_TAI) -#define sleep_timespec(t) \ - (clock_nanosleep(CLOCK_TAI, 0, (t), NULL)) +#define sleep_timespec(t) clock_nanosleep(CLOCK_TAI, 0, (t), NULL) #else -#define sleep_timespec(t) \ - (nanosleep((t), NULL)) +#define sleep_timespec(t) nanosleep((t), NULL) #endif #ifndef EPSILON diff --git a/src/process_group.c b/src/process_group.c index f1fd597b..4541a129 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -33,11 +33,9 @@ #if defined(__linux__) #if defined(CLOCK_TAI) -#define get_time(ts) \ - (clock_gettime(CLOCK_TAI, (ts))) +#define get_time(ts) clock_gettime(CLOCK_TAI, (ts)) #elif defined(CLOCK_MONOTONIC) -#define get_time(ts) \ - (clock_gettime(CLOCK_MONOTONIC, (ts))) +#define get_time(ts) clock_gettime(CLOCK_MONOTONIC, (ts)) #endif #endif #ifndef get_time diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index 00b0c87c..d2f6041d 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -53,11 +53,9 @@ static void increase_priority(void) /* inline int sleep_timespec(struct timespec *t); */ #if defined(__linux__) && defined(CLOCK_TAI) -#define sleep_timespec(t) \ - (clock_nanosleep(CLOCK_TAI, 0, (t), NULL)) +#define sleep_timespec(t) clock_nanosleep(CLOCK_TAI, 0, (t), NULL) #else -#define sleep_timespec(t) \ - (nanosleep((t), NULL)) +#define sleep_timespec(t) nanosleep((t), NULL) #endif static char *__basename(char *path) From 707849f18cbe1f2fb93cdfc89380f137777b6f0d Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 23 Dec 2023 19:37:15 +0800 Subject: [PATCH 082/209] CI: update --- .github/workflows/CI.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 495d0d60..1f8bb6be 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -17,14 +17,13 @@ jobs: permissions: contents: read strategy: - max-parallel: 20 matrix: os: [ubuntu-20.04, ubuntu-22.04, macos-11, macos-12, macos-13] runs-on: ${{ matrix.os }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@main with: ref: master fetch-depth: 0 @@ -46,7 +45,7 @@ jobs: sudo ./tests/abcdefghijklmnopqrstuvwxyzabcdefghi - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@main with: name: cpulimit-${{ matrix.os }} path: src/cpulimit @@ -56,16 +55,15 @@ jobs: permissions: contents: read strategy: - max-parallel: 20 matrix: - osver: ['12.3', '13.0', '13.1'] - runs-on: macos-12 + osver: ['13.2', '14.0'] + runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@main - name: Build in FreeBSD - uses: vmactions/freebsd-vm@v0 + uses: vmactions/freebsd-vm@v1 with: release: ${{ matrix.osver }} usesh: true @@ -78,7 +76,7 @@ jobs: sudo ./tests/abcdefghijklmnopqrstuvwxyzabcdefghi - name: Upload - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@main with: name: cpulimit-FreeBSD-${{ matrix.osver }} path: src/cpulimit From c61a0c47698b0ecba2f93c3114d25e0e63885ab5 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 6 Apr 2024 12:12:50 +0800 Subject: [PATCH 083/209] improve increase_priority() --- src/cpulimit.c | 12 ++++++------ tests/process_iterator_test.c | 11 ++++++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 4f52e31a..6904d368 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -135,16 +135,16 @@ static void increase_priority(void) /* find the best available nice value */ int old_priority, priority; old_priority = getpriority(PRIO_PROCESS, 0); - setpriority(PRIO_PROCESS, 0, MAX_PRIORITY); - priority = getpriority(PRIO_PROCESS, 0); - while (priority > MAX_PRIORITY && setpriority(PRIO_PROCESS, 0, priority - 1) == 0) + for (priority = MAX_PRIORITY; priority < old_priority; priority++) { - priority--; + if (setpriority(PRIO_PROCESS, 0, priority) == 0 && + getpriority(PRIO_PROCESS, 0) == priority) + break; } - if (priority != old_priority) + if (priority < old_priority) { if (verbose) - printf("Priority changed to %d\n", priority); + printf("Priority changed to %d.\n", priority); } else if (priority > MAX_PRIORITY) { diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index d2f6041d..fcb62a3a 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -42,12 +42,13 @@ static void increase_priority(void) { /* find the best available nice value */ - int priority; - setpriority(PRIO_PROCESS, 0, MAX_PRIORITY); - priority = getpriority(PRIO_PROCESS, 0); - while (priority > MAX_PRIORITY && setpriority(PRIO_PROCESS, 0, priority - 1) == 0) + int old_priority, priority; + old_priority = getpriority(PRIO_PROCESS, 0); + for (priority = MAX_PRIORITY; priority < old_priority; priority++) { - priority--; + if (setpriority(PRIO_PROCESS, 0, priority) == 0 && + getpriority(PRIO_PROCESS, 0) == priority) + break; } } From b6e48eb484fd1750f0ba46211469ac3425ad26fd Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 6 Apr 2024 22:31:00 +0800 Subject: [PATCH 084/209] add casting for malloc() --- src/cpulimit.c | 2 +- src/list.c | 2 +- src/process_group.c | 8 ++++---- src/process_iterator_apple.c | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 6904d368..62659cf8 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -485,7 +485,7 @@ int main(int argc, char *argv[]) /* executable file */ const char *cmd = argv[optind]; /* command line arguments */ - char **cmd_args = 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++) diff --git a/src/list.c b/src/list.c index d9da7fdc..dcc06989 100644 --- a/src/list.c +++ b/src/list.c @@ -35,7 +35,7 @@ void init_list(struct list *l, int keysize) struct list_node *add_elem(struct list *l, void *elem) { - struct list_node *newnode = malloc(sizeof(struct list_node)); + struct list_node *newnode = (struct list_node *)malloc(sizeof(struct list_node)); if (newnode == NULL) { exit(-1); diff --git a/src/process_group.c b/src/process_group.c index 4541a129..a89c8b26 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -141,7 +141,7 @@ int init_process_group(struct process_group *pgroup, pid_t target_pid, int inclu memset(&pgroup->proctable, 0, sizeof(pgroup->proctable)); pgroup->target_pid = target_pid; pgroup->include_children = include_children; - pgroup->proclist = malloc(sizeof(struct list)); + pgroup->proclist = (struct list *)malloc(sizeof(struct list)); if (pgroup->proclist == NULL) { exit(-1); @@ -209,12 +209,12 @@ void update_process_group(struct process_group *pgroup) if (pgroup->proctable[hashkey] == NULL) { /* empty bucket */ - struct process *new_process = malloc(sizeof(struct process)); + struct process *new_process = (struct process *)malloc(sizeof(struct process)); if (new_process == NULL) { exit(-1); } - pgroup->proctable[hashkey] = malloc(sizeof(struct list)); + pgroup->proctable[hashkey] = (struct list *)malloc(sizeof(struct list)); if (pgroup->proctable[hashkey] == NULL) { exit(-1); @@ -232,7 +232,7 @@ void update_process_group(struct process_group *pgroup) if (p == NULL) { /* process is new. add it */ - struct process *new_process = malloc(sizeof(struct process)); + struct process *new_process = (struct process *)malloc(sizeof(struct process)); if (new_process == NULL) { exit(-1); diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 32a15ceb..fcfba161 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -42,7 +42,7 @@ static int unique_nonzero_pids(pid_t *arr_in, int len_in, pid_t *arr_out) return -1; if (arr_in == arr_out) { - source = malloc(sizeof(pid_t) * len_in); + source = (pid_t *)malloc(sizeof(pid_t) * len_in); if (source == NULL) { exit(-1); @@ -80,7 +80,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi return -1; } /* Allocate and populate it->pidlist */ - if ((it->pidlist = malloc((it->count) * sizeof(pid_t))) == NULL) + if ((it->pidlist = (pid_t *)malloc((it->count) * sizeof(pid_t))) == NULL) { fprintf(stderr, "malloc: %s\n", strerror(errno)); exit(-1); From 1f8d82033530c8e3211c431bb142ea40fea5eba8 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 6 Apr 2024 23:05:47 +0800 Subject: [PATCH 085/209] refine signal handler --- src/cpulimit.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 62659cf8..74cf6a32 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -40,10 +40,6 @@ #include "process_group.h" #include "list.h" -#ifndef __GNUC__ -#define __attribute__(attr) -#endif - /* some useful macro */ #ifndef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) @@ -108,9 +104,17 @@ int lazy = 0; volatile sig_atomic_t quit_flag = 0; /* SIGINT and SIGTERM signal handler */ -static void quit(int sig __attribute__((unused))) +static void sig_handler(int sig) { - quit_flag = 1; + switch (sig) + { + case SIGINT: + case SIGTERM: + quit_flag = 1; + break; + default: + break; + } } static void print_usage(FILE *stream, int exit_code) @@ -468,7 +472,7 @@ int main(int argc, char *argv[]) } /* all arguments are ok! */ - sa.sa_handler = quit; + sa.sa_handler = sig_handler; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL); From aad5468ceef7ca613fececf644aa045b5241b4f7 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 7 Apr 2024 03:04:51 +0800 Subject: [PATCH 086/209] optimize code --- src/cpulimit.c | 4 ++-- src/list.c | 16 ++++++++-------- src/list.h | 16 ++++++++-------- tests/process_iterator_test.c | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 74cf6a32..cb384b35 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -240,7 +240,7 @@ static void limit_process(pid_t pid, double limit, int include_children) /* estimate how much the controlled processes are using the cpu in the working interval */ for (node = pgroup.proclist->first; node != NULL; node = node->next) { - struct process *proc = (struct process *)(node->data); + const struct process *proc = (const struct process *)(node->data); if (proc->cpu_usage < 0) { continue; @@ -328,7 +328,7 @@ static void limit_process(pid_t pid, double limit, int include_children) { for (node = pgroup.proclist->first; node != NULL; node = node->next) { - struct process *p = (struct process *)(node->data); + const struct process *p = (const struct process *)(node->data); kill(p->pid, SIGCONT); } } diff --git a/src/list.c b/src/list.c index dcc06989..508b71a4 100644 --- a/src/list.c +++ b/src/list.c @@ -88,12 +88,12 @@ void destroy_node(struct list *l, struct list_node *node) delete_node(l, node); } -int is_empty_list(struct list *l) +int is_empty_list(const struct list *l) { return (l->count == 0 ? TRUE : FALSE); } -int get_list_count(struct list *l) +int get_list_count(const struct list *l) { return l->count; } @@ -103,7 +103,7 @@ void *first_elem(struct list *l) return l->first->data; } -struct list_node *first_node(struct list *l) +struct list_node *first_node(const struct list *l) { return l->first; } @@ -113,12 +113,12 @@ void *last_elem(struct list *l) return l->last->data; } -struct list_node *last_node(struct list *l) +struct list_node *last_node(const struct list *l) { return l->last; } -struct list_node *xlocate_node(struct list *l, void *elem, int offset, int length) +struct list_node *xlocate_node(struct list *l, const void *elem, int offset, int length) { struct list_node *tmp; tmp = l->first; @@ -131,18 +131,18 @@ struct list_node *xlocate_node(struct list *l, void *elem, int offset, int lengt return EMPTYLIST; } -struct list_node *locate_node(struct list *l, void *elem) +struct list_node *locate_node(struct list *l, const void *elem) { return (xlocate_node(l, elem, 0, 0)); } -void *xlocate_elem(struct list *l, void *elem, int offset, int length) +void *xlocate_elem(struct list *l, const void *elem, int offset, int length) { struct list_node *node = xlocate_node(l, elem, offset, length); return (node == NULL ? NULL : node->data); } -void *locate_elem(struct list *l, void *elem) +void *locate_elem(struct list *l, const void *elem) { return (xlocate_elem(l, elem, 0, 0)); } diff --git a/src/list.h b/src/list.h index 39ee90bc..9f35ef57 100644 --- a/src/list.h +++ b/src/list.h @@ -75,12 +75,12 @@ void destroy_node(struct list *l, struct list_node *node); /* * Check whether a list is empty or not */ -int is_empty_list(struct list *l); +int is_empty_list(const struct list *l); /* * Return the element count of the list */ -int get_list_count(struct list *l); +int get_list_count(const struct list *l); /* * Return the first element (content of the node) from the list @@ -90,7 +90,7 @@ void *first_elem(struct list *l); /* * Return the first node from the list */ -struct list_node *first_node(struct list *l); +struct list_node *first_node(const struct list *l); /* * Return the last element (content of the node) from the list @@ -100,7 +100,7 @@ void *last_elem(struct list *l); /* * Return the last node from the list */ -struct list_node *last_node(struct list *l); +struct list_node *last_node(const struct list *l); /* * Search an element of the list by content @@ -110,22 +110,22 @@ struct list_node *last_node(struct list *l); * if the element is found, return the node address * else return NULL */ -struct list_node *xlocate_node(struct list *l, void *elem, int offset, int length); +struct list_node *xlocate_node(struct list *l, const void *elem, int offset, int length); /* * The same of xlocate_node(), but return the content of the node */ -void *xlocate_elem(struct list *l, void *elem, int offset, int length); +void *xlocate_elem(struct list *l, const void *elem, int offset, int length); /* * The same of calling xlocate_node() with offset=0 and length=0 */ -struct list_node *locate_node(struct list *l, void *elem); +struct list_node *locate_node(struct list *l, const void *elem); /* * The same of locate_node, but return the content of the node */ -void *locate_elem(struct list *l, void *elem); +void *locate_elem(struct list *l, const void *elem); /* * Delete all the elements in the list diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index fcb62a3a..158670bb 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -201,7 +201,7 @@ static void test_process_group_single(int include_children) update_process_group(&pgroup); for (node = pgroup.proclist->first; node != NULL; node = node->next) { - struct process *p = (struct process *)(node->data); + const struct process *p = (const struct process *)(node->data); assert(p->pid == child); assert(p->ppid == getpid()); assert(p->cpu_usage <= 1.2); From 0e29303f982b1b56d1a8c2de50e9810fd987f802 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 6 Apr 2024 22:48:21 +0800 Subject: [PATCH 087/209] re-organize includes and improve macros --- src/cpulimit.c | 12 +++++++++++- src/process_group.c | 10 +++++++++- src/process_group.h | 5 ++++- src/process_iterator.c | 6 ------ src/process_iterator.h | 5 ++++- src/process_iterator_apple.c | 4 +--- src/process_iterator_freebsd.c | 4 +++- src/process_iterator_linux.c | 2 +- tests/process_iterator_test.c | 10 +++++++++- 9 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index cb384b35..a6812d43 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -27,6 +27,10 @@ * */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include #include @@ -48,27 +52,33 @@ #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif +#ifndef basename static char *__basename(char *path) { char *p = strrchr(path, '/'); return p != NULL ? p + 1 : path; } #define basename(path) __basename(path) +#endif /* inline void nsec2timespec(double nsec, struct timespec *t); */ +#ifndef nsec2timespec #define nsec2timespec(nsec, t) \ do \ { \ (t)->tv_sec = (time_t)((nsec) / 1e9); \ (t)->tv_nsec = (long)((nsec) - (t)->tv_sec * 1e9); \ } while (0) +#endif /* inline int sleep_timespec(struct timespec *t); */ -#if defined(__linux__) && defined(CLOCK_TAI) +#ifndef sleep_timespec +#if defined(__linux__) && _POSIX_C_SOURCE >= 200112L && defined(CLOCK_TAI) #define sleep_timespec(t) clock_nanosleep(CLOCK_TAI, 0, (t), NULL) #else #define sleep_timespec(t) nanosleep((t), NULL) #endif +#endif #ifndef EPSILON #define EPSILON 1e-12 diff --git a/src/process_group.c b/src/process_group.c index a89c8b26..94d14eae 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -19,6 +19,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include #include @@ -31,13 +35,15 @@ #include "process_group.h" #include "list.h" -#if defined(__linux__) +#ifndef get_time +#if _POSIX_TIMERS > 0 #if defined(CLOCK_TAI) #define get_time(ts) clock_gettime(CLOCK_TAI, (ts)) #elif defined(CLOCK_MONOTONIC) #define get_time(ts) clock_gettime(CLOCK_MONOTONIC, (ts)) #endif #endif +#endif #ifndef get_time static int __get_time(struct timespec *ts) { @@ -53,12 +59,14 @@ static int __get_time(struct timespec *ts) #define get_time(ts) __get_time(ts) #endif +#ifndef basename static char *__basename(char *path) { char *p = strrchr(path, '/'); return p != NULL ? p + 1 : path; } #define basename(path) __basename(path) +#endif /* look for a process by pid search_pid : pid of the wanted process diff --git a/src/process_group.h b/src/process_group.h index c0b1339d..93f567a1 100644 --- a/src/process_group.h +++ b/src/process_group.h @@ -20,9 +20,12 @@ */ #ifndef __PROCESS_GROUP_H - #define __PROCESS_GROUP_H +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include diff --git a/src/process_iterator.c b/src/process_iterator.c index c23e065c..84d37612 100644 --- a/src/process_iterator.c +++ b/src/process_iterator.c @@ -19,12 +19,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include -#include -#include -#include -#include "process_iterator.h" - /* See this link to port to other systems: http://www.steve.org.uk/Reference/Unix/faq_8.html#SEC85 */ #if defined(__linux__) diff --git a/src/process_iterator.h b/src/process_iterator.h index 478a5393..a3508c9a 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -20,10 +20,13 @@ */ #ifndef __PROCESS_ITERATOR_H - #define __PROCESS_ITERATOR_H #include +#include +#include +#include +#include #include #include #if defined(__linux__) diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index fcfba161..4d34ecc1 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -28,10 +28,8 @@ #define __PROCESS_ITERATOR_APPLE_C #include -#include #include -#include -#include +#include "process_iterator.h" static int unique_nonzero_pids(pid_t *arr_in, int len_in, pid_t *arr_out) { diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index 8f4e0f4a..877e9eca 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -24,11 +24,13 @@ #ifndef __PROCESS_ITERATOR_FREEBSD_C #define __PROCESS_ITERATOR_FREEBSD_C +#include "process_iterator.h" #include #include #include +#include +#include #include -#include int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index ef8af8f4..0a29a122 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -26,7 +26,7 @@ #include #include -#include +#include "process_iterator.h" static int check_proc(void) { diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index 158670bb..b7bc8ec2 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -19,6 +19,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include #include @@ -53,18 +57,22 @@ static void increase_priority(void) } /* inline int sleep_timespec(struct timespec *t); */ -#if defined(__linux__) && defined(CLOCK_TAI) +#ifndef sleep_timespec +#if defined(__linux__) && _POSIX_C_SOURCE >= 200112L && defined(CLOCK_TAI) #define sleep_timespec(t) clock_nanosleep(CLOCK_TAI, 0, (t), NULL) #else #define sleep_timespec(t) nanosleep((t), NULL) #endif +#endif +#ifndef basename static char *__basename(char *path) { char *p = strrchr(path, '/'); return p != NULL ? p + 1 : path; } #define basename(path) __basename(path) +#endif static void ignore_signal(int sig __attribute__((unused))) { From daa699a1a86d2b0e6dd8228111dfafaf9ea33fef Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 13 Apr 2024 03:03:51 +0800 Subject: [PATCH 088/209] fix potential integer overflow --- src/process_iterator_linux.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 0a29a122..2f25e78e 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -58,7 +58,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi static int read_process_info(pid_t pid, struct process *p) { char statfile[32], exefile[32], state; - unsigned long utime, stime; + double utime, stime; long ppid; FILE *fd; int ret = 0; @@ -93,7 +93,7 @@ static int read_process_info(pid_t pid, struct process *p) sprintf(statfile, "/proc/%ld/stat", (long)p->pid); if ((fd = fopen(statfile, "r")) != NULL) { - if (fscanf(fd, "%*d (%*[^)]) %c %ld %*d %*d %*d %*d %*d %*d %*d %*d %*d %lu %lu", + if (fscanf(fd, "%*d (%*[^)]) %c %ld %*d %*d %*d %*d %*d %*d %*d %*d %*d %lf %lf", &state, &ppid, &utime, &stime) != 4 || strchr("ZXx", state) != NULL) { @@ -102,7 +102,7 @@ static int read_process_info(pid_t pid, struct process *p) else { p->ppid = (pid_t)ppid; - p->cputime = utime * 1000.0 / HZ + stime * 1000.0 / HZ; + p->cputime = (utime + stime) * 1000.0 / HZ; } fclose(fd); } From c1791f5d89ac49332d15139b579d26ae562cdb3a Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 15 Apr 2024 04:29:26 +0800 Subject: [PATCH 089/209] add type check for the directory entry if possible --- src/process_iterator_linux.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 2f25e78e..21ec3032 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -24,6 +24,10 @@ #ifndef __PROCESS_ITERATOR_LINUX_C #define __PROCESS_ITERATOR_LINUX_C +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include #include "process_iterator.h" @@ -164,6 +168,10 @@ int get_next_process(struct process_iterator *it, struct process *p) /* read in from /proc and seek for process dirs */ while ((dit = readdir(it->dip)) != NULL) { +#ifdef _DIRENT_HAVE_D_TYPE + if (dit->d_type != DT_DIR) + continue; +#endif if (strtok(dit->d_name, "0123456789") != NULL) continue; p->pid = (pid_t)atol(dit->d_name); From 4f1a7806525f24280b8b28a980e9501205a4a5b3 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 15 Apr 2024 06:04:23 +0800 Subject: [PATCH 090/209] simplify code --- src/process_iterator_linux.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 21ec3032..fd15f83a 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -31,6 +31,7 @@ #include #include #include "process_iterator.h" +#include static int check_proc(void) { @@ -168,13 +169,14 @@ int get_next_process(struct process_iterator *it, struct process *p) /* read in from /proc and seek for process dirs */ while ((dit = readdir(it->dip)) != NULL) { + char *ptr; #ifdef _DIRENT_HAVE_D_TYPE if (dit->d_type != DT_DIR) continue; #endif - if (strtok(dit->d_name, "0123456789") != NULL) + p->pid = (pid_t)strtol(dit->d_name, &ptr, 10); + if (p->pid <= 0 || *ptr != '\0' || errno == ERANGE) continue; - p->pid = (pid_t)atol(dit->d_name); if (it->filter->pid != 0 && it->filter->pid != p->pid && !is_child_of(p->pid, it->filter->pid)) From e985fd7fd7962d1e318deb3f720e1ae135346ce6 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 16 Apr 2024 01:38:27 +0800 Subject: [PATCH 091/209] speed up is_child_of() for Linux --- src/process_iterator_linux.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index fd15f83a..6878a598 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -31,6 +31,7 @@ #include #include #include "process_iterator.h" +#include #include static int check_proc(void) @@ -136,15 +137,37 @@ pid_t getppid_of(pid_t pid) return (pid_t)ppid; } +static time_t get_start_time(pid_t pid) +{ + struct stat procfs_stat; + char procfs_path[32]; + sprintf(procfs_path, "/proc/%ld", (long)pid); + if (stat(procfs_path, &procfs_stat) == 0) + return procfs_stat.st_ctime; + return (time_t)-1; +} + int is_child_of(pid_t child_pid, pid_t parent_pid) { - if (child_pid <= 0 || parent_pid <= 0 || child_pid == parent_pid) + time_t child_start_time, parent_start_time; + if (child_pid <= 1 || parent_pid <= 0 || child_pid == parent_pid) return 0; - while (child_pid > 1 && child_pid != parent_pid) + if (parent_pid == 1) + return 1; + parent_start_time = get_start_time(parent_pid); + while (child_pid > 1) { + if (parent_start_time >= 0) + { + child_start_time = get_start_time(child_pid); + if (child_start_time >= 0 && child_start_time < parent_start_time) + return 0; + } child_pid = getppid_of(child_pid); + if (child_pid == parent_pid) + return 1; } - return child_pid == parent_pid; + return 0; } int get_next_process(struct process_iterator *it, struct process *p) From 57b18511af71788abab002dea0185b0ce7a55bc7 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 18 Apr 2024 05:53:29 +0800 Subject: [PATCH 092/209] more strict check on procfs --- src/process_iterator_linux.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 6878a598..cf741c13 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -33,6 +33,7 @@ #include "process_iterator.h" #include #include +#include static int check_proc(void) { @@ -170,6 +171,13 @@ int is_child_of(pid_t child_pid, pid_t parent_pid) return 0; } +static int is_numeric(const char *str) +{ + for (; *str != '\0' && isdigit(*str); str++) + ; + return *str == '\0'; +} + int get_next_process(struct process_iterator *it, struct process *p) { struct dirent *dit = NULL; @@ -192,13 +200,12 @@ int get_next_process(struct process_iterator *it, struct process *p) /* read in from /proc and seek for process dirs */ while ((dit = readdir(it->dip)) != NULL) { - char *ptr; #ifdef _DIRENT_HAVE_D_TYPE if (dit->d_type != DT_DIR) continue; #endif - p->pid = (pid_t)strtol(dit->d_name, &ptr, 10); - if (p->pid <= 0 || *ptr != '\0' || errno == ERANGE) + if (!is_numeric(dit->d_name) || + (p->pid = (pid_t)atol(dit->d_name)) <= 0) continue; if (it->filter->pid != 0 && it->filter->pid != p->pid && From e5705dc7d24dec51f0fc6db5184ed574aa792439 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 16 Apr 2024 11:22:52 +0800 Subject: [PATCH 093/209] improve test --- tests/process_iterator_test.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index b7bc8ec2..ddb23674 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -190,18 +190,18 @@ static void test_process_group_single(int include_children) { struct process_group pgroup; int i; - double tot_usage = 0; pid_t child = fork(); if (child == 0) { /* child is supposed to be killed by the parent :/ */ + volatile int unused_value = 0; increase_priority(); while (1) - ; + (void)unused_value; exit(1); } assert(init_process_group(&pgroup, child, include_children) == 0); - for (i = 0; i < 200; i++) + for (i = 0; i < 100; i++) { struct list_node *node = NULL; int count = 0; @@ -212,16 +212,16 @@ static void test_process_group_single(int include_children) const struct process *p = (const struct process *)(node->data); assert(p->pid == child); assert(p->ppid == getpid()); - assert(p->cpu_usage <= 1.2); - tot_usage += p->cpu_usage; + /* p->cpu_usage should be -1 or [0, 1] */ + assert((p->cpu_usage >= (-1.00001) && p->cpu_usage <= (-0.99999)) || + (p->cpu_usage >= 0 && p->cpu_usage <= 1.05)); count++; } assert(count == 1); interval.tv_sec = 0; - interval.tv_nsec = 50000000; + interval.tv_nsec = 200000000; sleep_timespec(&interval); } - assert(tot_usage / i < 1.1 && tot_usage / i > 0.7); assert(close_process_group(&pgroup) == 0); kill(child, SIGKILL); } From a61fb98b58357a36e0dafda42ada0f763ca42749 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 19 Apr 2024 01:24:15 +0800 Subject: [PATCH 094/209] fix typos --- src/process_group.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index 94d14eae..cadb99a0 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -189,7 +189,7 @@ int close_process_group(struct process_group *pgroup) (((t1)->tv_sec - (t2)->tv_sec) * 1e3 + ((t1)->tv_nsec - (t2)->tv_nsec) / 1e6) /* parameter in range 0-1 */ -#define ALFA 0.08 +#define ALPHA 0.08 #define MIN_DT 20 void update_process_group(struct process_group *pgroup) @@ -266,7 +266,7 @@ void update_process_group(struct process_group *pgroup) else { /* usage adjustment */ - p->cpu_usage = (1.0 - ALFA) * p->cpu_usage + ALFA * sample; + p->cpu_usage = (1.0 - ALPHA) * p->cpu_usage + ALPHA * sample; } p->cputime = tmp_process.cputime; } From 02a2e37c597792849703678269d15d4ef3c7856a Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 19 Apr 2024 01:38:07 +0800 Subject: [PATCH 095/209] avoid == for folating point numbers --- src/process_group.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/process_group.c b/src/process_group.c index cadb99a0..08ab1bca 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -258,7 +258,7 @@ void update_process_group(struct process_group *pgroup) continue; /* process exists. update CPU usage */ sample = (tmp_process.cputime - p->cputime) / dt; - if (p->cpu_usage == -1) + if (p->cpu_usage < 0) { /* initialization */ p->cpu_usage = sample; From 6f7763cafc513a75eab0a1498d6150087626398f Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 28 Apr 2024 01:38:27 +0800 Subject: [PATCH 096/209] improve getting the process start time for Linux --- src/process_iterator_linux.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index cf741c13..dc015a09 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -34,6 +34,7 @@ #include #include #include +#include static int check_proc(void) { @@ -138,30 +139,39 @@ pid_t getppid_of(pid_t pid) return (pid_t)ppid; } -static time_t get_start_time(pid_t pid) +static int get_start_time(pid_t pid, struct timespec *start_time) { struct stat procfs_stat; char procfs_path[32]; + int ret; sprintf(procfs_path, "/proc/%ld", (long)pid); - if (stat(procfs_path, &procfs_stat) == 0) - return procfs_stat.st_ctime; - return (time_t)-1; + ret = stat(procfs_path, &procfs_stat); + if (ret == 0 && start_time != NULL) + *start_time = procfs_stat.st_ctim; + return ret; +} + +static int earlier_than(const struct timespec *t1, const struct timespec *t2) +{ + return t1->tv_sec < t2->tv_sec || + (t1->tv_sec == t2->tv_sec && t1->tv_nsec < t2->tv_nsec); } int is_child_of(pid_t child_pid, pid_t parent_pid) { - time_t child_start_time, parent_start_time; + int ret_child, ret_parent; + struct timespec child_start_time, parent_start_time; if (child_pid <= 1 || parent_pid <= 0 || child_pid == parent_pid) return 0; if (parent_pid == 1) return 1; - parent_start_time = get_start_time(parent_pid); + ret_parent = get_start_time(parent_pid, &parent_start_time); while (child_pid > 1) { - if (parent_start_time >= 0) + if (ret_parent == 0) { - child_start_time = get_start_time(child_pid); - if (child_start_time >= 0 && child_start_time < parent_start_time) + ret_child = get_start_time(child_pid, &child_start_time); + if (ret_child == 0 && earlier_than(&child_start_time, &parent_start_time)) return 0; } child_pid = getppid_of(child_pid); From 785803dc28dd5c5f8b7333a9e03ecd3c01ce1e0e Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 28 Apr 2024 02:12:49 +0800 Subject: [PATCH 097/209] remove an unnecessary macro of HZ --- src/process_iterator.h | 17 ----------------- src/process_iterator_linux.c | 2 +- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/src/process_iterator.h b/src/process_iterator.h index a3508c9a..fd3b9412 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -34,23 +34,6 @@ #endif #include -/* USER_HZ detection, from openssl code */ -#ifndef HZ -#if defined(_SC_CLK_TCK) && (!defined(OPENSSL_SYS_VMS) || __CTRL_VER >= 70000000) -#define HZ ((double)sysconf(_SC_CLK_TCK)) -#else -#ifndef CLK_TCK -#ifndef _BSD_CLK_TCK_ /* FreeBSD hack */ -#define HZ 100.0 -#else /* _BSD_CLK_TCK_ */ -#define HZ ((double)_BSD_CLK_TCK_) -#endif -#else /* CLK_TCK */ -#define HZ ((double)CLK_TCK) -#endif -#endif -#endif - #ifdef __FreeBSD__ #include #endif diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index dc015a09..e42ea333 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -110,7 +110,7 @@ static int read_process_info(pid_t pid, struct process *p) else { p->ppid = (pid_t)ppid; - p->cputime = (utime + stime) * 1000.0 / HZ; + p->cputime = (utime + stime) * 1000 / sysconf(_SC_CLK_TCK); } fclose(fd); } From 2013ec1cc35cb18f77fd164cd032b09b380139b0 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 30 Apr 2024 03:11:44 +0800 Subject: [PATCH 098/209] use strncpy instead of memcpy when copying a string --- src/process_iterator_apple.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 4d34ecc1..86ea1e55 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -101,13 +101,14 @@ static void pti2proc(struct proc_taskallinfo *ti, struct process *process) if (ti->pbsd.pbi_name[0] != '\0') { process->max_cmd_len = MIN(sizeof(process->command), sizeof(ti->pbsd.pbi_name)) - 1; - memcpy(process->command, ti->pbsd.pbi_name, process->max_cmd_len + 1); + strncpy(process->command, ti->pbsd.pbi_name, process->max_cmd_len); } else { process->max_cmd_len = MIN(sizeof(process->command), sizeof(ti->pbsd.pbi_comm)) - 1; - memcpy(process->command, ti->pbsd.pbi_comm, process->max_cmd_len + 1); + strncpy(process->command, ti->pbsd.pbi_comm, process->max_cmd_len); } + process->command[process->max_cmd_len] = '\0'; } static int get_process_pti(pid_t pid, struct proc_taskallinfo *ti) From 7e69cd4adf8d7398c32e37b506104667ec41c2cd Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 3 May 2024 10:30:35 +0800 Subject: [PATCH 099/209] fix FreeBSD build error --- src/process_iterator_freebsd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index 877e9eca..c9cdfc80 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -24,6 +24,10 @@ #ifndef __PROCESS_ITERATOR_FREEBSD_C #define __PROCESS_ITERATOR_FREEBSD_C +#if defined(__STRICT_ANSI__) || !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) +#define inline +#endif + #include "process_iterator.h" #include #include From 69c9608927256dd2e7a3e53913d35f9514c5f6cf Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 1 May 2024 23:51:49 +0800 Subject: [PATCH 100/209] update Makefile --- src/Makefile | 2 +- tests/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index c10af797..bad8aef3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,6 @@ CC ?= gcc override CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ - -D_GNU_SOURCE -std=gnu89 \ + -std=c89 \ -Wall -Wextra -pedantic \ -Wmissing-prototypes -Wstrict-prototypes \ -Wold-style-definition diff --git a/tests/Makefile b/tests/Makefile index 8c87784c..2793e30b 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,6 +1,6 @@ CC ?= gcc override CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ - -D_GNU_SOURCE -std=gnu89 \ + -std=c89 \ -Wall -Wextra -pedantic \ -Wmissing-prototypes -Wstrict-prototypes \ -Wold-style-definition From 70e085d084ec0adb00667d13ac3704365aac41f0 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 20 Jul 2024 23:48:31 +0800 Subject: [PATCH 101/209] get Ticks Per Second only once --- src/process_iterator_linux.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index e42ea333..381d0730 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -70,6 +70,7 @@ static int read_process_info(pid_t pid, struct process *p) long ppid; FILE *fd; int ret = 0; + static double sc_clk_tck = -1.0; p->pid = pid; @@ -110,7 +111,11 @@ static int read_process_info(pid_t pid, struct process *p) else { p->ppid = (pid_t)ppid; - p->cputime = (utime + stime) * 1000 / sysconf(_SC_CLK_TCK); + if (sc_clk_tck < 0) + { + sc_clk_tck = (double)sysconf(_SC_CLK_TCK); + } + p->cputime = (utime + stime) * 1000.0 / sc_clk_tck; } fclose(fd); } From d56108ca2d3787b03ceb4519bb5867bddd952271 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 21 Jul 2024 00:13:00 +0800 Subject: [PATCH 102/209] CI: update runners --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 1f8bb6be..2911ee81 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -18,7 +18,7 @@ jobs: contents: read strategy: matrix: - os: [ubuntu-20.04, ubuntu-22.04, macos-11, macos-12, macos-13] + os: [ubuntu-20.04, ubuntu-22.04, ubuntu-24.04, macos-12, macos-13, macos-14] runs-on: ${{ matrix.os }} steps: From af48e4a493f7eba3a462d8f9365fb452a4df0fc8 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 29 Aug 2024 02:51:31 +0800 Subject: [PATCH 103/209] refactor and improve the macros --- src/cpulimit.c | 37 +---------- src/process_group.c | 39 +----------- src/process_iterator.h | 7 -- src/util.c | 23 +++++++ src/util.h | 116 ++++++++++++++++++++++++++++++++++ tests/Makefile | 3 +- tests/process_iterator_test.c | 19 +----- 7 files changed, 144 insertions(+), 100 deletions(-) create mode 100644 src/util.c create mode 100644 src/util.h diff --git a/src/cpulimit.c b/src/cpulimit.c index a6812d43..395580f5 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -43,42 +43,7 @@ #include "process_group.h" #include "list.h" - -/* some useful macro */ -#ifndef MIN -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif -#ifndef MAX -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif - -#ifndef basename -static char *__basename(char *path) -{ - char *p = strrchr(path, '/'); - return p != NULL ? p + 1 : path; -} -#define basename(path) __basename(path) -#endif - -/* inline void nsec2timespec(double nsec, struct timespec *t); */ -#ifndef nsec2timespec -#define nsec2timespec(nsec, t) \ - do \ - { \ - (t)->tv_sec = (time_t)((nsec) / 1e9); \ - (t)->tv_nsec = (long)((nsec) - (t)->tv_sec * 1e9); \ - } while (0) -#endif - -/* inline int sleep_timespec(struct timespec *t); */ -#ifndef sleep_timespec -#if defined(__linux__) && _POSIX_C_SOURCE >= 200112L && defined(CLOCK_TAI) -#define sleep_timespec(t) clock_nanosleep(CLOCK_TAI, 0, (t), NULL) -#else -#define sleep_timespec(t) nanosleep((t), NULL) -#endif -#endif +#include "util.h" #ifndef EPSILON #define EPSILON 1e-12 diff --git a/src/process_group.c b/src/process_group.c index 08ab1bca..1e2e18a4 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -34,39 +34,7 @@ #include "process_iterator.h" #include "process_group.h" #include "list.h" - -#ifndef get_time -#if _POSIX_TIMERS > 0 -#if defined(CLOCK_TAI) -#define get_time(ts) clock_gettime(CLOCK_TAI, (ts)) -#elif defined(CLOCK_MONOTONIC) -#define get_time(ts) clock_gettime(CLOCK_MONOTONIC, (ts)) -#endif -#endif -#endif -#ifndef get_time -static int __get_time(struct timespec *ts) -{ - struct timeval tv; - if (gettimeofday(&tv, NULL)) - { - return -1; - } - ts->tv_sec = tv.tv_sec; - ts->tv_nsec = tv.tv_usec * 1000L; - return 0; -} -#define get_time(ts) __get_time(ts) -#endif - -#ifndef basename -static char *__basename(char *path) -{ - char *p = strrchr(path, '/'); - return p != NULL ? p + 1 : path; -} -#define basename(path) __basename(path) -#endif +#include "util.h" /* look for a process by pid search_pid : pid of the wanted process @@ -183,11 +151,6 @@ int close_process_group(struct process_group *pgroup) return 0; } -/* returns t1-t2 in millisecond */ -/* static inline double timediff_in_ms(const struct timespec *t1, const struct timespec *t2) */ -#define timediff_in_ms(t1, t2) \ - (((t1)->tv_sec - (t2)->tv_sec) * 1e3 + ((t1)->tv_nsec - (t2)->tv_nsec) / 1e6) - /* parameter in range 0-1 */ #define ALPHA 0.08 #define MIN_DT 20 diff --git a/src/process_iterator.h b/src/process_iterator.h index fd3b9412..572bcace 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -38,13 +38,6 @@ #include #endif -#ifndef MIN -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif -#ifndef MAX -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif - /* process descriptor */ struct process { diff --git a/src/util.c b/src/util.c new file mode 100644 index 00000000..e318010f --- /dev/null +++ b/src/util.c @@ -0,0 +1,23 @@ +#include "util.h" + +#ifdef __IMPL_BASENAME +const char *__basename(const char *path) +{ + const char *p = strrchr(path, '/'); + return p != NULL ? p + 1 : path; +} +#endif + +#ifdef __IMPL_GET_TIME +int __get_time(struct timespec *ts) +{ + struct timeval tv; + if (gettimeofday(&tv, NULL)) + { + return -1; + } + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000L; + return 0; +} +#endif diff --git a/src/util.h b/src/util.h new file mode 100644 index 00000000..de380be9 --- /dev/null +++ b/src/util.h @@ -0,0 +1,116 @@ +#ifndef __UTIL_H +#define __UTIL_H + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include + +/* some useful macro */ + +#if defined(__GNUC__) && !defined(__UNIQUE_ID) +#define ___PASTE(a, b) a##b +#define __PASTE(a, b) ___PASTE(a, b) +#define __UNIQUE_ID(prefix) \ + __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__) +#endif + +#ifndef MIN +#ifdef __GNUC__ +#define __min(t1, t2, min1, min2, x, y) \ + (__extension__({ \ + t1 min1 = (x); \ + t2 min2 = (y); \ + (void) (&min1 == &min2); \ + min1 < min2 ? min1 : min2; })) +#define MIN(x, y) \ + __min(__typeof__(x), __typeof__(y), \ + __UNIQUE_ID(min1_), __UNIQUE_ID(min2_), \ + x, y) +#else +#define MIN(a, b) \ + (((a) < (b)) ? (a) : (b)) +#endif +#endif + +#ifndef MAX +#ifdef __GNUC__ +#define __max(t1, t2, max1, max2, x, y) \ + (__extension__({ \ + t1 max1 = (x); \ + t2 max2 = (y); \ + (void) (&max1 == &max2); \ + max1 > max2 ? max1 : max2; })) +#define MAX(x, y) \ + __max(__typeof__(x), __typeof__(y), \ + __UNIQUE_ID(max1_), __UNIQUE_ID(max2_), \ + x, y) +#else +#define MAX(a, b) \ + (((a) > (b)) ? (a) : (b)) +#endif +#endif + +#ifndef basename +#ifdef __GNUC__ +#define __basename(path, path_full, p_pos) \ + (__extension__({ \ + const char *path_full = (path); \ + const char *p_pos = strrchr(path_full, '/'); \ + p_pos != NULL ? p_pos + 1 : path_full; \ + })) +#define basename(path) \ + __basename(path, __UNIQUE_ID(path_full_), __UNIQUE_ID(p_pos_)) +#else +const char *__basename(const char *path); +#define basename(path) __basename(path) +#define __IMPL_BASENAME +#endif +#endif + +/* inline void nsec2timespec(double nsec, struct timespec *t); */ +#ifndef nsec2timespec +#define nsec2timespec(nsec, t) \ + do \ + { \ + (t)->tv_sec = (time_t)((nsec) / 1e9); \ + (t)->tv_nsec = (long)((nsec) - (t)->tv_sec * 1e9); \ + } while (0) +#endif + +/* inline int sleep_timespec(struct timespec *t); */ +#ifndef sleep_timespec +#if defined(__linux__) && _POSIX_C_SOURCE >= 200112L && defined(CLOCK_TAI) +#define sleep_timespec(t) clock_nanosleep(CLOCK_TAI, 0, (t), NULL) +#else +#define sleep_timespec(t) nanosleep((t), NULL) +#endif +#endif + +#ifndef get_time +#if _POSIX_TIMERS > 0 +#if defined(CLOCK_TAI) +#define get_time(ts) clock_gettime(CLOCK_TAI, (ts)) +#elif defined(CLOCK_MONOTONIC) +#define get_time(ts) clock_gettime(CLOCK_MONOTONIC, (ts)) +#endif +#endif +#endif +#ifndef get_time +int __get_time(struct timespec *ts); +#define get_time(ts) __get_time(ts) +#define __IMPL_GET_TIME +#endif + +/* returns t1-t2 in millisecond */ +/* static inline double timediff_in_ms(const struct timespec *t1, const struct timespec *t2) */ +#ifndef timediff_in_ms +#define timediff_in_ms(t1, t2) \ + (((t1)->tv_sec - (t2)->tv_sec) * 1e3 + ((t1)->tv_nsec - (t2)->tv_nsec) / 1e6) +#endif + +#endif diff --git a/tests/Makefile b/tests/Makefile index 2793e30b..57f1df26 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -7,7 +7,8 @@ override CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ TARGETS = busy process_iterator_test SRC = ../src SYSLIBS ?= -lpthread -LIBS := $(SRC)/list.c $(SRC)/process_iterator.c $(SRC)/process_group.c +LIBS := $(SRC)/list.c $(SRC)/process_iterator.c \ + $(SRC)/process_group.c $(SRC)/util.c UNAME ?= $(shell uname) ifeq ($(UNAME), FreeBSD) diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index ddb23674..d724d2ca 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -36,6 +36,7 @@ #include "../src/process_iterator.h" #include "../src/process_group.h" +#include "../src/util.h" #ifndef __GNUC__ #define __attribute__(attr) @@ -56,24 +57,6 @@ static void increase_priority(void) } } -/* inline int sleep_timespec(struct timespec *t); */ -#ifndef sleep_timespec -#if defined(__linux__) && _POSIX_C_SOURCE >= 200112L && defined(CLOCK_TAI) -#define sleep_timespec(t) clock_nanosleep(CLOCK_TAI, 0, (t), NULL) -#else -#define sleep_timespec(t) nanosleep((t), NULL) -#endif -#endif - -#ifndef basename -static char *__basename(char *path) -{ - char *p = strrchr(path, '/'); - return p != NULL ? p + 1 : path; -} -#define basename(path) __basename(path) -#endif - static void ignore_signal(int sig __attribute__((unused))) { } From 16ef30c5b5de65009a8e86d259dd274027be11e4 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 29 Aug 2024 15:56:22 +0800 Subject: [PATCH 104/209] adapt the time slot to the system load --- src/cpulimit.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 395580f5..dbca7786 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -170,6 +170,22 @@ static pid_t get_pid_max(void) #endif } +static double get_dynamic_time_slot(void) +{ + static double time_slot = TIME_SLOT; + static const double MIN_TIME_SLOT = TIME_SLOT, + MAX_TIME_SLOT = TIME_SLOT * 5; + double load, new_time_slot; + if (getloadavg(&load, 1) != 1) + { + return time_slot; + } + new_time_slot = time_slot * load / NCPU / 0.3; + new_time_slot = MIN(MAX(new_time_slot, MIN_TIME_SLOT), MAX_TIME_SLOT); + time_slot = time_slot * 0.95 + new_time_slot * 0.05; + return time_slot; +} + static void limit_process(pid_t pid, double limit, int include_children) { /* slice of the slot in which the process is allowed to run */ @@ -203,6 +219,8 @@ static void limit_process(pid_t pid, double limit, int include_children) double twork_total_nsec, tsleep_total_nsec; + double time_slot; + update_process_group(&pgroup); if (pgroup.proclist->count == 0) @@ -239,10 +257,12 @@ static void limit_process(pid_t pid, double limit, int include_children) } workingrate = MAX(MIN(workingrate, 1 - EPSILON), EPSILON); - twork_total_nsec = (double)TIME_SLOT * 1000 * workingrate; + time_slot = get_dynamic_time_slot(); + + twork_total_nsec = time_slot * 1000 * workingrate; nsec2timespec(twork_total_nsec, &twork); - tsleep_total_nsec = (double)TIME_SLOT * 1000 - twork_total_nsec; + tsleep_total_nsec = time_slot * 1000 - twork_total_nsec; nsec2timespec(tsleep_total_nsec, &tsleep); if (verbose) From d2c30a5c7a2d3a723866ac91f457a2b5a0a9208f Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 29 Aug 2024 16:31:57 +0800 Subject: [PATCH 105/209] fix a memory leak --- src/cpulimit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cpulimit.c b/src/cpulimit.c index dbca7786..518239fb 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -514,6 +514,7 @@ int main(int argc, char *argv[]) int ret = execvp(cmd, cmd_args); /* if we are here there was an error, show it */ perror("Error"); + free(cmd_args); exit(ret); } else From 0a578f7503d08fad282c549056bf1fa4499330b0 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 29 Aug 2024 18:03:06 +0800 Subject: [PATCH 106/209] refactor --- src/cpulimit.c | 64 ----------------------------------- src/util.c | 58 +++++++++++++++++++++++++++++++ src/util.h | 7 ++++ tests/Makefile | 2 +- tests/busy.c | 36 ++------------------ tests/process_iterator_test.c | 15 -------- 6 files changed, 69 insertions(+), 113 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 518239fb..fd8bcf1c 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include "process_group.h" @@ -54,8 +53,6 @@ /* TODO: make it adaptive, based on the actual system load */ #define TIME_SLOT 100000 -#define MAX_PRIORITY -20 - /* GLOBAL VARIABLES */ /* the "family" */ @@ -109,67 +106,6 @@ static void print_usage(FILE *stream, int exit_code) exit(exit_code); } -static void increase_priority(void) -{ - /* find the best available nice value */ - int old_priority, priority; - old_priority = getpriority(PRIO_PROCESS, 0); - for (priority = MAX_PRIORITY; priority < old_priority; priority++) - { - if (setpriority(PRIO_PROCESS, 0, priority) == 0 && - getpriority(PRIO_PROCESS, 0) == priority) - break; - } - if (priority < old_priority) - { - if (verbose) - printf("Priority changed to %d.\n", priority); - } - else if (priority > MAX_PRIORITY) - { - if (verbose) - printf("Warning: Cannot change priority. Run as root or renice for best results.\n"); - } -} - -/* Get the number of CPUs */ -static int get_ncpu(void) -{ - int ncpu; -#if defined(_SC_NPROCESSORS_ONLN) - ncpu = sysconf(_SC_NPROCESSORS_ONLN); -#elif defined(__APPLE__) - int mib[2] = {CTL_HW, HW_NCPU}; - size_t len = sizeof(ncpu); - sysctl(mib, 2, &ncpu, &len, NULL, 0); -#elif defined(_GNU_SOURCE) - ncpu = get_nprocs(); -#else - ncpu = -1; -#endif - return ncpu; -} - -static pid_t get_pid_max(void) -{ -#if defined(__linux__) - /* read /proc/sys/kernel/pid_max */ - long pid_max = -1; - FILE *fd; - if ((fd = fopen("/proc/sys/kernel/pid_max", "r")) != NULL) - { - if (fscanf(fd, "%ld", &pid_max) != 1) - pid_max = -1; - fclose(fd); - } - return (pid_t)pid_max; -#elif defined(__FreeBSD__) - return (pid_t)99999; -#elif defined(__APPLE__) - return (pid_t)99998; -#endif -} - static double get_dynamic_time_slot(void) { static double time_slot = TIME_SLOT; diff --git a/src/util.c b/src/util.c index e318010f..64ca9070 100644 --- a/src/util.c +++ b/src/util.c @@ -1,4 +1,11 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include "util.h" +#include +#include +#include #ifdef __IMPL_BASENAME const char *__basename(const char *path) @@ -21,3 +28,54 @@ int __get_time(struct timespec *ts) return 0; } #endif + +void increase_priority(void) +{ + static const int MAX_PRIORITY = -20; + int old_priority, priority; + old_priority = getpriority(PRIO_PROCESS, 0); + for (priority = MAX_PRIORITY; priority < old_priority; priority++) + { + if (setpriority(PRIO_PROCESS, 0, priority) == 0 && + getpriority(PRIO_PROCESS, 0) == priority) + break; + } +} + +/* Get the number of CPUs */ +int get_ncpu(void) +{ + int ncpu; +#if defined(_SC_NPROCESSORS_ONLN) + ncpu = sysconf(_SC_NPROCESSORS_ONLN); +#elif defined(__APPLE__) + int mib[2] = {CTL_HW, HW_NCPU}; + size_t len = sizeof(ncpu); + sysctl(mib, 2, &ncpu, &len, NULL, 0); +#elif defined(_GNU_SOURCE) + ncpu = get_nprocs(); +#else + ncpu = -1; +#endif + return ncpu; +} + +pid_t get_pid_max(void) +{ +#if defined(__linux__) + /* read /proc/sys/kernel/pid_max */ + long pid_max = -1; + FILE *fd; + if ((fd = fopen("/proc/sys/kernel/pid_max", "r")) != NULL) + { + if (fscanf(fd, "%ld", &pid_max) != 1) + pid_max = -1; + fclose(fd); + } + return (pid_t)pid_max; +#elif defined(__FreeBSD__) + return (pid_t)99999; +#elif defined(__APPLE__) + return (pid_t)99998; +#endif +} diff --git a/src/util.h b/src/util.h index de380be9..ed183be6 100644 --- a/src/util.h +++ b/src/util.h @@ -113,4 +113,11 @@ int __get_time(struct timespec *ts); (((t1)->tv_sec - (t2)->tv_sec) * 1e3 + ((t1)->tv_nsec - (t2)->tv_nsec) / 1e6) #endif +void increase_priority(void); + +/* Get the number of CPUs */ +int get_ncpu(void); + +pid_t get_pid_max(void); + #endif diff --git a/tests/Makefile b/tests/Makefile index 57f1df26..470bd657 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -6,7 +6,7 @@ override CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ -Wold-style-definition TARGETS = busy process_iterator_test SRC = ../src -SYSLIBS ?= -lpthread +SYSLIBS ?= -lpthread $(SRC)/util.c LIBS := $(SRC)/list.c $(SRC)/process_iterator.c \ $(SRC)/process_group.c $(SRC)/util.c diff --git a/tests/busy.c b/tests/busy.c index eaaee028..1127b457 100644 --- a/tests/busy.c +++ b/tests/busy.c @@ -4,47 +4,17 @@ #include #include #include +#include "../src/util.h" #ifndef __GNUC__ #define __attribute__(attr) #endif -#define MAX_PRIORITY -20 - -static void increase_priority(void) -{ - /* find the best available nice value */ - int priority; - setpriority(PRIO_PROCESS, 0, MAX_PRIORITY); - priority = getpriority(PRIO_PROCESS, 0); - while (priority > MAX_PRIORITY && setpriority(PRIO_PROCESS, 0, priority - 1) == 0) - { - priority--; - } -} - -/* Get the number of CPUs */ -static int get_ncpu(void) -{ - int ncpu; -#if defined(_SC_NPROCESSORS_ONLN) - ncpu = sysconf(_SC_NPROCESSORS_ONLN); -#elif defined(__APPLE__) - int mib[2] = {CTL_HW, HW_NCPU}; - size_t len = sizeof(ncpu); - sysctl(mib, 2, &ncpu, &len, NULL, 0); -#elif defined(_GNU_SOURCE) - ncpu = get_nprocs(); -#else - ncpu = -1; -#endif - return ncpu; -} - static void *loop(void *param __attribute__((unused))) { + volatile int unused_value = 0; while (1) - ; + (void)unused_value; return NULL; } diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index d724d2ca..ecb28e47 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -42,21 +42,6 @@ #define __attribute__(attr) #endif -#define MAX_PRIORITY -20 - -static void increase_priority(void) -{ - /* find the best available nice value */ - int old_priority, priority; - old_priority = getpriority(PRIO_PROCESS, 0); - for (priority = MAX_PRIORITY; priority < old_priority; priority++) - { - if (setpriority(PRIO_PROCESS, 0, priority) == 0 && - getpriority(PRIO_PROCESS, 0) == priority) - break; - } -} - static void ignore_signal(int sig __attribute__((unused))) { } From 418c792a0319d05db60e6ffcf21240b76be6c44d Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 2 Sep 2024 01:17:39 +0800 Subject: [PATCH 107/209] refactor process hash table implementation --- src/process_group.c | 110 ++++++++++++++++---------------------------- src/process_group.h | 2 +- src/process_table.c | 90 ++++++++++++++++++++++++++++++++++++ src/process_table.h | 75 ++++++++++++++++++++++++++++++ tests/Makefile | 4 +- 5 files changed, 208 insertions(+), 73 deletions(-) create mode 100644 src/process_table.c create mode 100644 src/process_table.h diff --git a/src/process_group.c b/src/process_group.c index 1e2e18a4..334eb403 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -35,6 +35,7 @@ #include "process_group.h" #include "list.h" #include "util.h" +#include "process_table.h" /* look for a process by pid search_pid : pid of the wanted process @@ -114,7 +115,12 @@ pid_t find_process_by_name(char *process_name) int init_process_group(struct process_group *pgroup, pid_t target_pid, int include_children) { /* hashtable initialization */ - memset(&pgroup->proctable, 0, sizeof(pgroup->proctable)); + pgroup->proctable = (struct process_table *)malloc(sizeof(struct process_table)); + if (pgroup->proctable == NULL) + { + exit(-1); + } + process_table_init(pgroup->proctable, 2048); pgroup->target_pid = target_pid; pgroup->include_children = include_children; pgroup->proclist = (struct list *)malloc(sizeof(struct list)); @@ -133,24 +139,25 @@ int init_process_group(struct process_group *pgroup, pid_t target_pid, int inclu 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; + process_table_destroy(pgroup->proctable); + free(pgroup->proctable); + pgroup->proctable = NULL; return 0; } +static struct process *process_dup(struct process *proc) +{ + struct process *p = (struct process *)malloc(sizeof(struct process)); + if (p == NULL) + { + exit(-1); + } + return memcpy(p, proc, sizeof(struct process)); +} + /* parameter in range 0-1 */ #define ALPHA 0.08 #define MIN_DT 20 @@ -158,7 +165,7 @@ int close_process_group(struct process_group *pgroup) void update_process_group(struct process_group *pgroup) { struct process_iterator it; - struct process tmp_process; + struct process tmp_process, *p; struct process_filter filter; struct timespec now; double dt; @@ -176,63 +183,34 @@ void update_process_group(struct process_group *pgroup) while (get_next_process(&it, &tmp_process) != -1) { - int hashkey = pid_hashfn(tmp_process.pid); - if (pgroup->proctable[hashkey] == NULL) + p = process_table_find(pgroup->proctable, &tmp_process); + if (p == NULL) { - /* empty bucket */ - struct process *new_process = (struct process *)malloc(sizeof(struct process)); - if (new_process == NULL) - { - exit(-1); - } - pgroup->proctable[hashkey] = (struct list *)malloc(sizeof(struct list)); - if (pgroup->proctable[hashkey] == NULL) - { - exit(-1); - } + /* process is new. add it */ tmp_process.cpu_usage = -1; - memcpy(new_process, &tmp_process, sizeof(struct process)); - init_list(pgroup->proctable[hashkey], sizeof(pid_t)); - add_elem(pgroup->proctable[hashkey], new_process); - add_elem(pgroup->proclist, new_process); + p = process_dup(&tmp_process); + process_table_add(pgroup->proctable, p); + add_elem(pgroup->proclist, p); } else { - /* existing bucket */ - struct process *p = (struct process *)locate_elem(pgroup->proctable[hashkey], &tmp_process); - if (p == NULL) + double sample; + add_elem(pgroup->proclist, p); + if (dt < MIN_DT) + continue; + /* process exists. update CPU usage */ + sample = (tmp_process.cputime - p->cputime) / dt; + if (p->cpu_usage < 0) { - /* process is new. add it */ - struct process *new_process = (struct process *)malloc(sizeof(struct process)); - if (new_process == NULL) - { - exit(-1); - } - 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); + /* initialization */ + p->cpu_usage = sample; } else { - double sample; - add_elem(pgroup->proclist, p); - if (dt < MIN_DT) - continue; - /* process exists. update CPU usage */ - sample = (tmp_process.cputime - p->cputime) / dt; - if (p->cpu_usage < 0) - { - /* initialization */ - p->cpu_usage = sample; - } - else - { - /* usage adjustment */ - p->cpu_usage = (1.0 - ALPHA) * p->cpu_usage + ALPHA * sample; - } - p->cputime = tmp_process.cputime; + /* usage adjustment */ + p->cpu_usage = (1.0 - ALPHA) * p->cpu_usage + ALPHA * sample; } + p->cputime = tmp_process.cputime; } } close_process_iterator(&it); @@ -243,13 +221,5 @@ void update_process_group(struct process_group *pgroup) int remove_process(struct process_group *pgroup, pid_t pid) { - int hashkey = pid_hashfn(pid); - struct list_node *node; - if (pgroup->proctable[hashkey] == NULL) - return 1; /* nothing to delete */ - node = (struct list_node *)locate_node(pgroup->proctable[hashkey], &pid); - if (node == NULL) - return 2; - delete_node(pgroup->proctable[hashkey], node); - return 0; + return process_table_del_pid(pgroup->proctable, pid); } diff --git a/src/process_group.h b/src/process_group.h index 93f567a1..992ebb27 100644 --- a/src/process_group.h +++ b/src/process_group.h @@ -39,7 +39,7 @@ struct process_group { /* hashtable with all the processes (array of struct list of struct process) */ - struct list *proctable[PIDHASH_SZ]; + struct process_table *proctable; struct list *proclist; pid_t target_pid; int include_children; diff --git a/src/process_table.c b/src/process_table.c new file mode 100644 index 00000000..2431a60f --- /dev/null +++ b/src/process_table.c @@ -0,0 +1,90 @@ +#include +#include +#include "process_table.h" + +void process_table_init(struct process_table *pt, int hashsize) +{ + pt->hashsize = hashsize; + pt->table = (struct list **)malloc(sizeof(struct list *) * pt->hashsize); + if (pt->table == NULL) + { + exit(1); + } + memset(pt->table, 0, sizeof(struct list *) * pt->hashsize); +} + +static int proc_hash(const struct process_table *pt, const struct process *p) +{ + return p->pid % pt->hashsize; +} + +struct process *process_table_find(const struct process_table *pt, const struct process *p) +{ + int idx = proc_hash(pt, p); + if (pt->table[idx] == NULL) + { + return NULL; + } + return (struct process *)locate_elem(pt->table[idx], p); +} + +struct process *process_table_find_pid(const struct process_table *pt, pid_t pid) +{ + struct process p; + p.pid = pid; + return process_table_find(pt, &p); +} + +void process_table_add(struct process_table *pt, struct process *p) +{ + int idx = proc_hash(pt, p); + if (pt->table[idx] == NULL) + { + pt->table[idx] = (struct list *)malloc(sizeof(struct list)); + if (pt->table[idx] == NULL) + { + exit(-1); + } + init_list(pt->table[idx], sizeof(pid_t)); + } + add_elem(pt->table[idx], p); +} + +int process_table_del(struct process_table *pt, const struct process *p) +{ + struct list_node *node; + int idx = proc_hash(pt, p); + if (pt->table[idx] == NULL) + { + return 1; /* nothing to delete */ + } + node = (struct list_node *)locate_node(pt->table[idx], p); + if (node == NULL) + { + return 2; /* nothing to delete */ + } + delete_node(pt->table[idx], node); + return 0; +} + +int process_table_del_pid(struct process_table *pt, pid_t pid) +{ + struct process p; + p.pid = pid; + return process_table_del(pt, &p); +} + +void process_table_destroy(struct process_table *pt) +{ + int i; + for (i = 0; i < pt->hashsize; i++) + { + if (pt->table[i] != NULL) + { + destroy_list(pt->table[i]); + free(pt->table[i]); + } + } + free(pt->table); + pt->table = NULL; +} diff --git a/src/process_table.h b/src/process_table.h new file mode 100644 index 00000000..dae28f8a --- /dev/null +++ b/src/process_table.h @@ -0,0 +1,75 @@ +#ifndef __PROCESS_TABLE_H +#define __PROCESS_TABLE_H + +#include "process_iterator.h" +#include "list.h" + +/* + * Structure representing a process table. + */ +struct process_table +{ + struct list **table; /* Array of pointers to linked lists for storing processes */ + int hashsize; /* Size of the hash table for the process table */ +}; + +/** + * Initializes the process table with the given hash size. + * + * @param pt The process table to initialize + * @param hashsize The size of the hash table + */ +void process_table_init(struct process_table *pt, int hashsize); + +/** + * Finds a process in the process table based on the given process. + * + * @param pt The process table to search in + * @param p The process to find + * @return A pointer to the found process or NULL if not found + */ +struct process *process_table_find(const struct process_table *pt, const struct process *p); + +/** + * Finds a process in the process table based on the given PID. + * + * @param pt The process table to search in + * @param pid The PID of the process to find + * @return A pointer to the found process or NULL if not found + */ +struct process *process_table_find_pid(const struct process_table *pt, pid_t pid); + +/** + * Adds a process to the process table. + * + * @param pt The process table to add the process to + * @param p The process to add + */ +void process_table_add(struct process_table *pt, struct process *p); + +/** + * Deletes a process from the process table. + * + * @param pt The process table to delete the process from + * @param p The process to delete + * @return 0 if deletion is successful, 1 if process not found, 2 if node not found + */ +int process_table_del(struct process_table *pt, const struct process *p); + +/** + * Deletes a process from the process table based on the given PID. + * + * @param pt The process table to delete the process from + * @param pid The PID of the process to delete + * @return 0 if deletion is successful, 1 if process not found, 2 if node not found + */ +int process_table_del_pid(struct process_table *pt, pid_t pid); + +/** + * Destroys the process table and frees up the memory. + * + * @param pt The process table to destroy + */ +void process_table_destroy(struct process_table *pt); + +#endif diff --git a/tests/Makefile b/tests/Makefile index 470bd657..5129c1a1 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -7,8 +7,8 @@ override CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ TARGETS = busy process_iterator_test SRC = ../src SYSLIBS ?= -lpthread $(SRC)/util.c -LIBS := $(SRC)/list.c $(SRC)/process_iterator.c \ - $(SRC)/process_group.c $(SRC)/util.c +LIBS := $(SRC)/list.c $(SRC)/process_iterator.c $(SRC)/process_group.c \ + $(SRC)/process_table.c $(SRC)/util.c UNAME ?= $(shell uname) ifeq ($(UNAME), FreeBSD) From 49d0bddfe3a4b215422473e6633df98bb4bb8fff Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 2 Sep 2024 01:54:51 +0800 Subject: [PATCH 108/209] list: use safe_free macro --- src/list.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/list.c b/src/list.c index 508b71a4..50809e33 100644 --- a/src/list.c +++ b/src/list.c @@ -24,6 +24,16 @@ #include "list.h" +#define safe_free(p) \ + do \ + { \ + if ((p) != NULL) \ + { \ + free((p)); \ + (p) = NULL; \ + } \ + } while (0) + #define EMPTYLIST NULL void init_list(struct list *l, int keysize) @@ -78,13 +88,12 @@ void delete_node(struct list *l, struct list_node *node) node->next->previous = node->previous; } l->count--; - free(node); + safe_free(node); } void destroy_node(struct list *l, struct list_node *node) { - free(node->data); - node->data = NULL; + safe_free(node->data); delete_node(l, node); } @@ -154,8 +163,7 @@ void clear_list(struct list *l) struct list_node *tmp; tmp = l->first; l->first = l->first->next; - free(tmp); - tmp = NULL; + safe_free(tmp); } l->last = EMPTYLIST; l->count = 0; @@ -168,10 +176,8 @@ void destroy_list(struct list *l) struct list_node *tmp; tmp = l->first; l->first = l->first->next; - free(tmp->data); - tmp->data = NULL; - free(tmp); - tmp = NULL; + safe_free(tmp->data); + safe_free(tmp); } l->last = EMPTYLIST; l->count = 0; From d4f0018bfff4f8934250bf1576ab214108a05b08 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 2 Sep 2024 02:39:04 +0800 Subject: [PATCH 109/209] simplify code --- src/list.c | 2 +- src/list.h | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/list.c b/src/list.c index 50809e33..e90dea9b 100644 --- a/src/list.c +++ b/src/list.c @@ -99,7 +99,7 @@ void destroy_node(struct list *l, struct list_node *node) int is_empty_list(const struct list *l) { - return (l->count == 0 ? TRUE : FALSE); + return l->count == 0; } int get_list_count(const struct list *l) diff --git a/src/list.h b/src/list.h index 9f35ef57..323972a1 100644 --- a/src/list.h +++ b/src/list.h @@ -23,11 +23,6 @@ #define __LIST__ -#ifndef TRUE -#define TRUE 1 -#define FALSE 0 -#endif - struct list_node { /* pointer to the content of the node */ From 717ac81c8ec4e604b8cd0cc651d21501668ce41e Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 2 Sep 2024 23:53:12 +0800 Subject: [PATCH 110/209] unify parameter names for function declaration and definition --- src/process_iterator.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/process_iterator.h b/src/process_iterator.h index 572bcace..d8005314 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -78,11 +78,11 @@ struct process_iterator struct process_filter *filter; }; -int init_process_iterator(struct process_iterator *i, struct process_filter *filter); +int init_process_iterator(struct process_iterator *it, struct process_filter *filter); -int get_next_process(struct process_iterator *i, struct process *p); +int get_next_process(struct process_iterator *it, struct process *p); -int close_process_iterator(struct process_iterator *i); +int close_process_iterator(struct process_iterator *it); int is_child_of(pid_t child_pid, pid_t parent_pid); From 331c2a40e7547569492308825762c3f730a351f5 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 3 Sep 2024 01:00:49 +0800 Subject: [PATCH 111/209] rewrite comments --- src/list.h | 157 +++++++++++++++++++++++++++++------------ src/process_group.h | 55 ++++++++++++++- src/process_iterator.h | 80 +++++++++++++++++++-- src/process_table.h | 9 ++- src/util.h | 59 ++++++++++------ 5 files changed, 281 insertions(+), 79 deletions(-) diff --git a/src/list.h b/src/list.h index 323972a1..9c3e1637 100644 --- a/src/list.h +++ b/src/list.h @@ -23,112 +23,177 @@ #define __LIST__ +/** + * Structure representing a node in a doubly linked list. + */ struct list_node { - /* pointer to the content of the node */ + /* Pointer to the content of the node */ void *data; - /* pointer to previous node */ + + /* Pointer to the previous node in the list */ struct list_node *previous; - /* pointer to next node */ + + /* Pointer to the next node in the list */ struct list_node *next; }; +/** + * Structure representing a doubly linked list. + */ struct list { - /* first node */ + /* Pointer to the first node in the list */ struct list_node *first; - /* last node */ + + /* Pointer to the last node in the list */ struct list_node *last; - /* size of the search key in bytes */ + + /* Size of the search key in bytes */ int keysize; - /* element count */ + + /* Count of elements in the list */ int count; }; -/* - * Initialize a list, with a specified key size +/** + * Initializes a list with a specified key size. + * + * @param l Pointer to the list to initialize. + * @param keysize Size of the key used for comparisons. */ void init_list(struct list *l, int keysize); -/* - * Add a new element at the end of the list - * return the pointer to the new node +/** + * Adds a new element to the end of the list. + * + * @param l Pointer to the list to which the element will be added. + * @param elem Pointer to the element to add. + * @return Pointer to the newly created node containing the element. */ struct list_node *add_elem(struct list *l, void *elem); -/* - * Delete a node +/** + * Deletes a specified node from the list. + * + * @param l Pointer to the list from which to delete the node. + * @param node Pointer to the node to delete. */ void delete_node(struct list *l, struct list_node *node); -/* - * Delete a node from the list, even the content pointed by it - * Use only when the content is a dynamically allocated pointer +/** + * Deletes a node from the list and frees its content. + * + * This function should only be used when the content is dynamically allocated. + * + * @param l Pointer to the list from which to delete the node. + * @param node Pointer to the node to delete. */ void destroy_node(struct list *l, struct list_node *node); -/* - * Check whether a list is empty or not +/** + * Checks whether the list is empty. + * + * @param l Pointer to the list to check. + * @return Non-zero if the list is empty; zero otherwise. */ int is_empty_list(const struct list *l); -/* - * Return the element count of the list +/** + * Returns the count of elements in the list. + * + * @param l Pointer to the list. + * @return Number of elements in the list. */ int get_list_count(const struct list *l); -/* - * Return the first element (content of the node) from the list +/** + * Returns the content of the first element in the list. + * + * @param l Pointer to the list. + * @return Pointer to the content of the first node, or NULL if the list is empty. */ void *first_elem(struct list *l); -/* - * Return the first node from the list +/** + * Returns the first node in the list. + * + * @param l Pointer to the list. + * @return Pointer to the first node, or NULL if the list is empty. */ struct list_node *first_node(const struct list *l); -/* - * Return the last element (content of the node) from the list +/** + * Returns the content of the last element in the list. + * + * @param l Pointer to the list. + * @return Pointer to the content of the last node, or NULL if the list is empty. */ void *last_elem(struct list *l); -/* - * Return the last node from the list +/** + * Returns the last node in the list. + * + * @param l Pointer to the list. + * @return Pointer to the last node, or NULL if the list is empty. */ struct list_node *last_node(const struct list *l); -/* - * Search an element of the list by content - * the comparison is done from the specified offset and for a specified length - * if offset=0, the comparison starts from the address pointed by data - * if length=0, default keysize is used for length - * if the element is found, return the node address - * else return NULL +/** + * Searches for an element in the list by its content. + * + * Comparison is performed from the specified offset and for a specified length. + * If offset=0, comparison starts from the address pointed to by data. + * If length=0, the default keysize is used. + * + * @param l Pointer to the list to search. + * @param elem Pointer to the element to locate. + * @param offset Offset from which to start the comparison. + * @param length Length of the comparison. + * @return Pointer to the node if found; NULL if not found. */ struct list_node *xlocate_node(struct list *l, const void *elem, int offset, int length); -/* - * The same of xlocate_node(), but return the content of the node +/** + * Similar to xlocate_node(), but returns the content of the node. + * + * @param l Pointer to the list to search. + * @param elem Pointer to the element to locate. + * @param offset Offset from which to start the comparison. + * @param length Length of the comparison. + * @return Pointer to the content of the node if found; NULL if not found. */ void *xlocate_elem(struct list *l, const void *elem, int offset, int length); -/* - * The same of calling xlocate_node() with offset=0 and length=0 +/** + * Locates a node in the list using default parameters (offset=0, length=0). + * + * @param l Pointer to the list to search. + * @param elem Pointer to the element to locate. + * @return Pointer to the node if found; NULL if not found. */ struct list_node *locate_node(struct list *l, const void *elem); -/* - * The same of locate_node, but return the content of the node +/** + * Similar to locate_node(), but returns the content of the node. + * + * @param l Pointer to the list to search. + * @param elem Pointer to the element to locate. + * @return Pointer to the content of the node if found; NULL if not found. */ void *locate_elem(struct list *l, const void *elem); -/* - * Delete all the elements in the list +/** + * Deletes all elements in the list. + * + * @param l Pointer to the list to clear. */ void clear_list(struct list *l); -/* - * Delete every element in the list, and free the memory pointed by all the node data +/** + * Deletes every element in the list and frees the memory of all node data. + * + * @param l Pointer to the list to destroy. */ void destroy_list(struct list *l); diff --git a/src/process_group.h b/src/process_group.h index 992ebb27..2cf1fb97 100644 --- a/src/process_group.h +++ b/src/process_group.h @@ -36,26 +36,79 @@ #define PIDHASH_SZ 1024 #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1)) +/** + * Structure representing a group of processes for tracking. + */ struct process_group { - /* hashtable with all the processes (array of struct list of struct process) */ + /** Pointer to the process table for storing process information */ struct process_table *proctable; + + /** Pointer to the list of processes in this group */ struct list *proclist; + + /** PID of the target process to monitor */ pid_t target_pid; + + /** Flag indicating whether to include child processes (1 for yes, 0 for no) */ int include_children; + + /** Timestamp of the last update for this process group */ struct timespec last_update; }; +/** + * Initialize a process group for tracking processes. + * + * @param pgroup Pointer to the process group structure to initialize. + * @param target_pid PID of the target process to track. + * @param include_children Flag indicating whether to include child processes. + * @return 0 on success, exits with -1 on memory allocation failure. + */ int init_process_group(struct process_group *pgroup, pid_t target_pid, int include_children); +/** + * Update the process group with the latest process information. + * + * @param pgroup Pointer to the process group to update. + */ void update_process_group(struct process_group *pgroup); +/** + * Close a process group and free associated resources. + * + * @param pgroup Pointer to the process group to close. + * @return 0 on success. + */ int close_process_group(struct process_group *pgroup); +/** + * Look for a process by its PID. + * + * @param pid The PID of the desired process. + * @return PID of the found process if successful, + * Negative PID if the process does not exist or if the signal fails. + */ pid_t find_process_by_pid(pid_t pid); +/** + * Look for a process with a given name. + * + * @param process_name The name of the desired process. It can be an absolute path + * to the executable file or just the file name. + * @return PID of the found process if it is found, + * 0 if the process is not found, + * Negative PID if the process is found but cannot be controlled. + */ pid_t find_process_by_name(char *process_name); +/** + * Remove a process from the process group by its PID. + * + * @param pgroup Pointer to the process group from which to remove the process. + * @param pid The PID of the process to remove. + * @return Result of the process table deletion operation. + */ int remove_process(struct process_group *pgroup, pid_t pid); #endif diff --git a/src/process_iterator.h b/src/process_iterator.h index d8005314..0f4d680f 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -38,54 +38,120 @@ #include #endif -/* process descriptor */ +/** + * Structure representing a process descriptor. + */ struct process { - /* pid of the process */ + /* Process ID of the process */ pid_t pid; - /* ppid of the process */ + + /* Parent Process ID of the process */ pid_t ppid; - /* cputime used by the process (in milliseconds) */ + + /* CPU time used by the process (in milliseconds) */ double cputime; - /* actual cpu usage estimation (value in range 0-1) */ + + /* Actual CPU usage estimation (value in range 0-1) */ double cpu_usage; - /* absolute path of the executable file */ + + /* Absolute path of the executable file */ char command[PATH_MAX + 1]; - /* maximum command length */ + + /* Maximum command length */ int max_cmd_len; }; +/** + * Structure representing a filter for processes. + */ struct process_filter { + /* Process ID to filter */ pid_t pid; + + /* Flag indicating whether to include child processes (1 for yes, 0 for no) */ int include_children; }; +/** + * Structure representing an iterator for processes. + * This structure provides a way to iterate over processes + * in different operating systems with their specific implementations. + */ struct process_iterator { #if defined(__linux__) + /* Directory stream for accessing the /proc filesystem on Linux */ DIR *dip; #elif defined(__FreeBSD__) + /* Kernel virtual memory descriptor for accessing process information on FreeBSD */ kvm_t *kd; + + /* Array of process information structures */ struct kinfo_proc *procs; + + /* Total number of processes retrieved */ int count; + + /* Current index in the process array */ int i; #elif defined(__APPLE__) + /* Current index in the process list */ int i; + + /* Total number of processes retrieved */ int count; + + /* List of process IDs */ pid_t *pidlist; #endif + + /* Pointer to a process filter to apply during iteration */ struct process_filter *filter; }; +/** + * Initializes a process iterator. + * + * @param it Pointer to the process_iterator structure. + * @param filter Pointer to the process_filter structure. + * @return 0 on success, -1 on failure. + */ int init_process_iterator(struct process_iterator *it, struct process_filter *filter); +/** + * Retrieves the next process information in the process iterator. + * + * @param it Pointer to the process_iterator structure. + * @param p Pointer to the process structure to store process information. + * @return 0 on success, -1 if no more processes are available. + */ int get_next_process(struct process_iterator *it, struct process *p); +/** + * Closes the process iterator. + * + * @param it Pointer to the process_iterator structure. + * @return 0 on success, 1 on failure. + */ int close_process_iterator(struct process_iterator *it); +/** + * Determines if a process is a child of another process. + * + * @param child_pid The child process ID. + * @param parent_pid The parent process ID. + * @return 1 if the child process is a child of the parent process, 0 otherwise. + */ int is_child_of(pid_t child_pid, pid_t parent_pid); +/** + * Retrieves the parent process ID (PPID) of a given PID. + * + * @param pid The given PID. + * @return The parent process ID. + */ pid_t getppid_of(pid_t pid); #endif diff --git a/src/process_table.h b/src/process_table.h index dae28f8a..ecb6732a 100644 --- a/src/process_table.h +++ b/src/process_table.h @@ -4,13 +4,16 @@ #include "process_iterator.h" #include "list.h" -/* +/** * Structure representing a process table. */ struct process_table { - struct list **table; /* Array of pointers to linked lists for storing processes */ - int hashsize; /* Size of the hash table for the process table */ + /* Array of pointers to linked lists for storing processes */ + struct list **table; + + /* Size of the hash table for the process table */ + int hashsize; }; /** diff --git a/src/util.h b/src/util.h index ed183be6..ce8acc26 100644 --- a/src/util.h +++ b/src/util.h @@ -10,28 +10,34 @@ #include #include -/* some useful macro */ +/* Useful macros for unique ID generation and minimum/maximum functions */ #if defined(__GNUC__) && !defined(__UNIQUE_ID) +/* Helper macros to concatenate tokens */ #define ___PASTE(a, b) a##b #define __PASTE(a, b) ___PASTE(a, b) + +/* Generates a unique ID based on a prefix */ #define __UNIQUE_ID(prefix) \ __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__) #endif #ifndef MIN #ifdef __GNUC__ +/* Helper for finding the minimum of two values, utilizing type safety */ #define __min(t1, t2, min1, min2, x, y) \ - (__extension__({ \ - t1 min1 = (x); \ - t2 min2 = (y); \ - (void) (&min1 == &min2); \ - min1 < min2 ? min1 : min2; })) + (__extension__({ \ + t1 min1 = (x); \ + t2 min2 = (y); \ + (void)(&min1 == &min2); \ + min1 < min2 ? min1 : min2; \ + })) #define MIN(x, y) \ __min(__typeof__(x), __typeof__(y), \ __UNIQUE_ID(min1_), __UNIQUE_ID(min2_), \ x, y) #else +/* Simple macro to find the minimum of two values */ #define MIN(a, b) \ (((a) < (b)) ? (a) : (b)) #endif @@ -39,17 +45,20 @@ #ifndef MAX #ifdef __GNUC__ +/* Helper for finding the maximum of two values, utilizing type safety */ #define __max(t1, t2, max1, max2, x, y) \ - (__extension__({ \ - t1 max1 = (x); \ - t2 max2 = (y); \ - (void) (&max1 == &max2); \ - max1 > max2 ? max1 : max2; })) + (__extension__({ \ + t1 max1 = (x); \ + t2 max2 = (y); \ + (void)(&max1 == &max2); \ + max1 > max2 ? max1 : max2; \ + })) #define MAX(x, y) \ __max(__typeof__(x), __typeof__(y), \ __UNIQUE_ID(max1_), __UNIQUE_ID(max2_), \ x, y) #else +/* Simple macro to find the maximum of two values */ #define MAX(a, b) \ (((a) > (b)) ? (a) : (b)) #endif @@ -57,22 +66,24 @@ #ifndef basename #ifdef __GNUC__ -#define __basename(path, path_full, p_pos) \ - (__extension__({ \ - const char *path_full = (path); \ - const char *p_pos = strrchr(path_full, '/'); \ - p_pos != NULL ? p_pos + 1 : path_full; \ +/* Helper macro to get the basename of a path */ +#define __basename(path, path_full, last_slash) \ + (__extension__({ \ + const char *path_full = (path); \ + const char *last_slash = strrchr((path_full), '/'); \ + last_slash != NULL ? last_slash + 1 : path_full; \ })) #define basename(path) \ - __basename(path, __UNIQUE_ID(path_full_), __UNIQUE_ID(p_pos_)) + __basename((path), __UNIQUE_ID(path_full_), __UNIQUE_ID(p_pos_)) #else +/* Fallback function declaration for basename */ const char *__basename(const char *path); #define basename(path) __basename(path) #define __IMPL_BASENAME #endif #endif -/* inline void nsec2timespec(double nsec, struct timespec *t); */ +/* Converts nanoseconds to a timespec structure */ #ifndef nsec2timespec #define nsec2timespec(nsec, t) \ do \ @@ -82,7 +93,7 @@ const char *__basename(const char *path); } while (0) #endif -/* inline int sleep_timespec(struct timespec *t); */ +/* Sleep for a specified timespec duration */ #ifndef sleep_timespec #if defined(__linux__) && _POSIX_C_SOURCE >= 200112L && defined(CLOCK_TAI) #define sleep_timespec(t) clock_nanosleep(CLOCK_TAI, 0, (t), NULL) @@ -91,6 +102,7 @@ const char *__basename(const char *path); #endif #endif +/* Retrieves the current time into a timespec structure */ #ifndef get_time #if _POSIX_TIMERS > 0 #if defined(CLOCK_TAI) @@ -100,24 +112,27 @@ const char *__basename(const char *path); #endif #endif #endif + +/* Fallback function for getting time if not defined */ #ifndef get_time int __get_time(struct timespec *ts); #define get_time(ts) __get_time(ts) #define __IMPL_GET_TIME #endif -/* returns t1-t2 in millisecond */ -/* static inline double timediff_in_ms(const struct timespec *t1, const struct timespec *t2) */ +/* Returns the difference between two timespecs in milliseconds */ #ifndef timediff_in_ms #define timediff_in_ms(t1, t2) \ (((t1)->tv_sec - (t2)->tv_sec) * 1e3 + ((t1)->tv_nsec - (t2)->tv_nsec) / 1e6) #endif +/* Increases the priority of the current process */ void increase_priority(void); -/* Get the number of CPUs */ +/* Retrieves the number of available CPUs */ int get_ncpu(void); +/* Retrieves the maximum process ID */ pid_t get_pid_max(void); #endif From c586d08ee65a8eeebb255430ddd8d28f796c8894 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 3 Sep 2024 01:01:13 +0800 Subject: [PATCH 112/209] remove unused code --- src/process_group.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/process_group.h b/src/process_group.h index 2cf1fb97..25a1990a 100644 --- a/src/process_group.h +++ b/src/process_group.h @@ -33,9 +33,6 @@ #include "list.h" -#define PIDHASH_SZ 1024 -#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1)) - /** * Structure representing a group of processes for tracking. */ From 494a5dcd94d7a2e68bac3e6d1a463c74f3c7e712 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 3 Sep 2024 01:27:23 +0800 Subject: [PATCH 113/209] improve basename macro --- src/util.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/util.h b/src/util.h index ce8acc26..d32ddcc2 100644 --- a/src/util.h +++ b/src/util.h @@ -10,7 +10,7 @@ #include #include -/* Useful macros for unique ID generation and minimum/maximum functions */ +/* Useful macros */ #if defined(__GNUC__) && !defined(__UNIQUE_ID) /* Helper macros to concatenate tokens */ @@ -67,14 +67,14 @@ #ifndef basename #ifdef __GNUC__ /* Helper macro to get the basename of a path */ -#define __basename(path, path_full, last_slash) \ - (__extension__({ \ - const char *path_full = (path); \ - const char *last_slash = strrchr((path_full), '/'); \ - last_slash != NULL ? last_slash + 1 : path_full; \ +#define __basename(path, full_path, last_slash) \ + (__extension__({ \ + const char *full_path = (path); \ + const char *last_slash = strrchr(full_path, '/'); \ + (last_slash != NULL) ? (last_slash + 1) : full_path; \ })) #define basename(path) \ - __basename((path), __UNIQUE_ID(path_full_), __UNIQUE_ID(p_pos_)) + __basename((path), __UNIQUE_ID(full_path_), __UNIQUE_ID(last_slash_)) #else /* Fallback function declaration for basename */ const char *__basename(const char *path); From 1013312359b8101187f9897bdddf9482e9997247 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 6 Sep 2024 20:55:47 +0800 Subject: [PATCH 114/209] add .clang-format --- .clang-format | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..c68c757b --- /dev/null +++ b/.clang-format @@ -0,0 +1,4 @@ +SortIncludes: false +BasedOnStyle: Microsoft +ColumnLimit: 0 +IndentWidth: 4 From 8e23cc319dbec7ff733ed9fa4ea6bdf95fc81dd3 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 6 Sep 2024 20:57:59 +0800 Subject: [PATCH 115/209] format code --- src/cpulimit.c | 892 ++++++++++++++++----------------- src/list.c | 182 +++---- src/process_group.c | 286 +++++------ src/process_group.h | 20 +- src/process_iterator.h | 68 +-- src/process_iterator_apple.c | 296 +++++------ src/process_iterator_freebsd.c | 232 ++++----- src/process_iterator_linux.c | 338 ++++++------- src/util.c | 78 +-- src/util.h | 66 +-- tests/busy.c | 42 +- tests/process_iterator_test.c | 386 +++++++------- 12 files changed, 1443 insertions(+), 1443 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index fd8bcf1c..50080966 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -78,470 +78,470 @@ volatile sig_atomic_t quit_flag = 0; /* SIGINT and SIGTERM signal handler */ static void sig_handler(int sig) { - switch (sig) - { - case SIGINT: - case SIGTERM: - quit_flag = 1; - break; - default: - break; - } + switch (sig) + { + case SIGINT: + case SIGTERM: + quit_flag = 1; + break; + default: + break; + } } static void print_usage(FILE *stream, int exit_code) { - fprintf(stream, "Usage: %s [OPTIONS...] TARGET\n", program_name); - fprintf(stream, " OPTIONS\n"); - fprintf(stream, " -l, --limit=N percentage of cpu allowed from 0 to %d (required)\n", 100 * NCPU); - fprintf(stream, " -v, --verbose show control statistics\n"); - fprintf(stream, " -z, --lazy exit if there is no target process, or if it dies\n"); - fprintf(stream, " -i, --include-children limit also the children processes\n"); - fprintf(stream, " -h, --help display this help and exit\n"); - fprintf(stream, " TARGET must be exactly one of these:\n"); - fprintf(stream, " -p, --pid=N pid of the process (implies -z)\n"); - fprintf(stream, " -e, --exe=FILE name of the executable program file or path name\n"); - fprintf(stream, " COMMAND [ARGS] run this command and limit it (implies -z)\n"); - fprintf(stream, "\nReport bugs to .\n"); - exit(exit_code); + fprintf(stream, "Usage: %s [OPTIONS...] TARGET\n", program_name); + fprintf(stream, " OPTIONS\n"); + fprintf(stream, " -l, --limit=N percentage of cpu allowed from 0 to %d (required)\n", 100 * NCPU); + fprintf(stream, " -v, --verbose show control statistics\n"); + fprintf(stream, " -z, --lazy exit if there is no target process, or if it dies\n"); + fprintf(stream, " -i, --include-children limit also the children processes\n"); + fprintf(stream, " -h, --help display this help and exit\n"); + fprintf(stream, " TARGET must be exactly one of these:\n"); + fprintf(stream, " -p, --pid=N pid of the process (implies -z)\n"); + fprintf(stream, " -e, --exe=FILE name of the executable program file or path name\n"); + fprintf(stream, " COMMAND [ARGS] run this command and limit it (implies -z)\n"); + fprintf(stream, "\nReport bugs to .\n"); + exit(exit_code); } static double get_dynamic_time_slot(void) { - static double time_slot = TIME_SLOT; - static const double MIN_TIME_SLOT = TIME_SLOT, - MAX_TIME_SLOT = TIME_SLOT * 5; - double load, new_time_slot; - if (getloadavg(&load, 1) != 1) - { - return time_slot; - } - new_time_slot = time_slot * load / NCPU / 0.3; - new_time_slot = MIN(MAX(new_time_slot, MIN_TIME_SLOT), MAX_TIME_SLOT); - time_slot = time_slot * 0.95 + new_time_slot * 0.05; - return time_slot; + static double time_slot = TIME_SLOT; + static const double MIN_TIME_SLOT = TIME_SLOT, + MAX_TIME_SLOT = TIME_SLOT * 5; + double load, new_time_slot; + if (getloadavg(&load, 1) != 1) + { + return time_slot; + } + new_time_slot = time_slot * load / NCPU / 0.3; + new_time_slot = MIN(MAX(new_time_slot, MIN_TIME_SLOT), MAX_TIME_SLOT); + time_slot = time_slot * 0.95 + new_time_slot * 0.05; + return time_slot; } static 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; - /* slice of the slot in which the process is stopped */ - struct timespec tsleep; - /* generic list item */ - struct list_node *node; - /* counter */ - int c = 0; - - /* 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; - - /* get a better priority */ - increase_priority(); - - /* build the family */ - init_process_group(&pgroup, pid, include_children); - - if (verbose) - printf("Members in the process group owned by %ld: %d\n", - (long)pgroup.target_pid, pgroup.proclist->count); - - while (!quit_flag) - { - /* total cpu actual usage (range 0-1) */ - /* 1 means that the processes are using 100% cpu */ - double pcpu = -1; - - double twork_total_nsec, tsleep_total_nsec; - - double time_slot; - - update_process_group(&pgroup); - - if (pgroup.proclist->count == 0) - { - if (verbose) - printf("No more processes.\n"); - break; - } - - /* estimate how much the controlled processes are using the cpu in the working interval */ - for (node = pgroup.proclist->first; node != NULL; node = node->next) - { - const struct process *proc = (const struct process *)(node->data); - if (proc->cpu_usage < 0) - { - continue; - } - if (pcpu < 0) - pcpu = 0; - pcpu += proc->cpu_usage; - } - - /* adjust work and sleep time slices */ - if (pcpu < 0) - { - /* it's the 1st cycle, initialize workingrate */ - pcpu = limit; - workingrate = limit; - } - else - { - /* adjust workingrate */ - workingrate = workingrate * limit / MAX(pcpu, EPSILON); - } - workingrate = MAX(MIN(workingrate, 1 - EPSILON), EPSILON); - - time_slot = get_dynamic_time_slot(); - - twork_total_nsec = time_slot * 1000 * workingrate; - nsec2timespec(twork_total_nsec, &twork); - - tsleep_total_nsec = time_slot * 1000 - twork_total_nsec; - nsec2timespec(tsleep_total_nsec, &tsleep); - - if (verbose) - { - if (c % 200 == 0) - printf("\n %%CPU work quantum sleep quantum active rate\n"); - if (c % 10 == 0 && c > 0) - printf("%7.2f%% %9.0f us %10.0f us %10.2f%%\n", pcpu * 100, twork_total_nsec / 1000, tsleep_total_nsec / 1000, workingrate * 100); - } - - /* resume processes */ - node = pgroup.proclist->first; - while (node != NULL) - { - struct list_node *next_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, "SIGCONT failed. Process %ld dead!\n", (long)proc->pid); - /* remove process from group */ - delete_node(pgroup.proclist, node); - remove_process(&pgroup, proc->pid); - } - node = next_node; - } - - /* now processes are free to run (same working slice for all) */ - sleep_timespec(&twork); - - if (tsleep.tv_nsec > 0 || tsleep.tv_sec > 0) - { - /* stop processes only if tsleep>0 */ - node = pgroup.proclist->first; - while (node != NULL) - { - struct list_node *next_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, "SIGSTOP failed. Process %ld dead!\n", (long)proc->pid); - /* remove process from group */ - delete_node(pgroup.proclist, node); - remove_process(&pgroup, proc->pid); - } - node = next_node; - } - /* now the processes are sleeping */ - sleep_timespec(&tsleep); - } - c = (c + 1) % 200; - } - - if (quit_flag) - { - for (node = pgroup.proclist->first; node != NULL; node = node->next) - { - const struct process *p = (const struct process *)(node->data); - kill(p->pid, SIGCONT); - } - } - - close_process_group(&pgroup); + /* slice of the slot in which the process is allowed to run */ + struct timespec twork; + /* slice of the slot in which the process is stopped */ + struct timespec tsleep; + /* generic list item */ + struct list_node *node; + /* counter */ + int c = 0; + + /* 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; + + /* get a better priority */ + increase_priority(); + + /* build the family */ + init_process_group(&pgroup, pid, include_children); + + if (verbose) + printf("Members in the process group owned by %ld: %d\n", + (long)pgroup.target_pid, pgroup.proclist->count); + + while (!quit_flag) + { + /* total cpu actual usage (range 0-1) */ + /* 1 means that the processes are using 100% cpu */ + double pcpu = -1; + + double twork_total_nsec, tsleep_total_nsec; + + double time_slot; + + update_process_group(&pgroup); + + if (pgroup.proclist->count == 0) + { + if (verbose) + printf("No more processes.\n"); + break; + } + + /* estimate how much the controlled processes are using the cpu in the working interval */ + for (node = pgroup.proclist->first; node != NULL; node = node->next) + { + const struct process *proc = (const struct process *)(node->data); + if (proc->cpu_usage < 0) + { + continue; + } + if (pcpu < 0) + pcpu = 0; + pcpu += proc->cpu_usage; + } + + /* adjust work and sleep time slices */ + if (pcpu < 0) + { + /* it's the 1st cycle, initialize workingrate */ + pcpu = limit; + workingrate = limit; + } + else + { + /* adjust workingrate */ + workingrate = workingrate * limit / MAX(pcpu, EPSILON); + } + workingrate = MAX(MIN(workingrate, 1 - EPSILON), EPSILON); + + time_slot = get_dynamic_time_slot(); + + twork_total_nsec = time_slot * 1000 * workingrate; + nsec2timespec(twork_total_nsec, &twork); + + tsleep_total_nsec = time_slot * 1000 - twork_total_nsec; + nsec2timespec(tsleep_total_nsec, &tsleep); + + if (verbose) + { + if (c % 200 == 0) + printf("\n %%CPU work quantum sleep quantum active rate\n"); + if (c % 10 == 0 && c > 0) + printf("%7.2f%% %9.0f us %10.0f us %10.2f%%\n", pcpu * 100, twork_total_nsec / 1000, tsleep_total_nsec / 1000, workingrate * 100); + } + + /* resume processes */ + node = pgroup.proclist->first; + while (node != NULL) + { + struct list_node *next_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, "SIGCONT failed. Process %ld dead!\n", (long)proc->pid); + /* remove process from group */ + delete_node(pgroup.proclist, node); + remove_process(&pgroup, proc->pid); + } + node = next_node; + } + + /* now processes are free to run (same working slice for all) */ + sleep_timespec(&twork); + + if (tsleep.tv_nsec > 0 || tsleep.tv_sec > 0) + { + /* stop processes only if tsleep>0 */ + node = pgroup.proclist->first; + while (node != NULL) + { + struct list_node *next_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, "SIGSTOP failed. Process %ld dead!\n", (long)proc->pid); + /* remove process from group */ + delete_node(pgroup.proclist, node); + remove_process(&pgroup, proc->pid); + } + node = next_node; + } + /* now the processes are sleeping */ + sleep_timespec(&tsleep); + } + c = (c + 1) % 200; + } + + if (quit_flag) + { + for (node = pgroup.proclist->first; node != NULL; node = node->next) + { + const struct process *p = (const struct process *)(node->data); + kill(p->pid, SIGCONT); + } + } + + close_process_group(&pgroup); } static void quit_handler(void) { - if (quit_flag) - { - /* fix ^C little problem */ - printf("\r"); - } + if (quit_flag) + { + /* fix ^C little problem */ + printf("\r"); + } } int main(int argc, char *argv[]) { - /* argument variables */ - char *exe = NULL; - int perclimit = 0; - int exe_ok = 0; - int pid_ok = 0; - int limit_ok = 0; - pid_t pid = 0; - int include_children = 0; - int command_mode; - - /* parse arguments */ - int next_option; - int option_index = 0; - /* A string listing valid short options letters */ - const char *short_options = "+p:e:l:vzih"; - /* An array describing valid long options */ - const struct option long_options[] = { - {"pid", required_argument, NULL, 'p'}, - {"exe", required_argument, NULL, 'e'}, - {"limit", required_argument, NULL, 'l'}, - {"verbose", no_argument, NULL, 'v'}, - {"lazy", no_argument, NULL, 'z'}, - {"include-children", no_argument, NULL, 'i'}, - {"help", no_argument, NULL, 'h'}, - {0, 0, 0, 0}}; - - double limit; - - struct timespec wait_time = {2, 0}; - - struct sigaction sa; - - static char program_base_name[PATH_MAX + 1]; - - atexit(quit_handler); - - /* get program name */ - strncpy(program_base_name, basename(argv[0]), sizeof(program_base_name) - 1); - program_base_name[sizeof(program_base_name) - 1] = '\0'; - program_name = program_base_name; - /* get current pid */ - cpulimit_pid = getpid(); - /* get cpu count */ - NCPU = get_ncpu(); - - do - { - next_option = getopt_long(argc, argv, short_options, long_options, &option_index); - switch (next_option) - { - case 'p': - pid = (pid_t)atol(optarg); - pid_ok = 1; - break; - case 'e': - exe = optarg; - exe_ok = 1; - break; - case 'l': - perclimit = atoi(optarg); - limit_ok = 1; - break; - case 'v': - verbose = 1; - break; - case 'z': - lazy = 1; - break; - case 'i': - include_children = 1; - break; - case 'h': - print_usage(stdout, 1); - break; - case '?': - print_usage(stderr, 1); - break; - case -1: - break; - default: - abort(); - } - } while (next_option != -1); - - if (pid_ok && (pid <= 1 || pid >= get_pid_max())) - { - fprintf(stderr, "Error: Invalid value for argument PID\n"); - print_usage(stderr, 1); - exit(1); - } - if (pid != 0) - { - lazy = 1; - } - - if (!limit_ok) - { - fprintf(stderr, "Error: You must specify a cpu limit percentage\n"); - print_usage(stderr, 1); - exit(1); - } - 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); - } - - 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); - exit(1); - } - - if (exe_ok + pid_ok + command_mode > 1) - { - fprintf(stderr, "Error: You must specify exactly one target process, either by name, pid, or command line\n"); - print_usage(stderr, 1); - exit(1); - } - - /* all arguments are ok! */ - sa.sa_handler = sig_handler; - sa.sa_flags = 0; - sigemptyset(&sa.sa_mask); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - - /* print the number of available cpu */ - if (verbose) - printf("%d cpu detected\n", NCPU); - - if (command_mode) - { - int i; - pid_t child; - /* executable file */ - const char *cmd = argv[optind]; - /* command line arguments */ - 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]; - } - cmd_args[i] = NULL; - - if (verbose) - { - printf("Running command: '%s", cmd); - for (i = 1; i < argc - optind; i++) - { - printf(" %s", cmd_args[i]); - } - printf("'\n"); - } - - child = fork(); - if (child < 0) - { - exit(EXIT_FAILURE); - } - 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"); - free(cmd_args); - exit(ret); - } - else - { - /* parent code */ - pid_t limiter; - free(cmd_args); - limiter = fork(); - if (limiter < 0) - { - exit(EXIT_FAILURE); - } - else if (limiter > 0) - { - /* parent */ - int status_process; - int status_limiter; - waitpid(child, &status_process, 0); - waitpid(limiter, &status_limiter, 0); - if (WIFEXITED(status_process)) - { - if (verbose) - printf("Process %ld terminated with exit status %d\n", - (long)child, (int)WEXITSTATUS(status_process)); - exit(WEXITSTATUS(status_process)); - } - printf("Process %ld terminated abnormally\n", (long)child); - exit(status_process); - } - else - { - /* limiter code */ - if (verbose) - printf("Limiting process %ld\n", (long)child); - limit_process(child, limit, include_children); - exit(0); - } - } - } - - while (!quit_flag) - { - /* look for the target process..or wait for it */ - pid_t ret = 0; - if (pid_ok) - { - /* search by pid */ - ret = find_process_by_pid(pid); - if (ret == 0) - { - printf("No process found\n"); - } - else if (ret < 0) - { - printf("Process found but you aren't allowed to control it\n"); - } - } - else - { - /* search by file or path name */ - ret = find_process_by_name(exe); - if (ret == 0) - { - printf("No process found\n"); - } - else if (ret < 0) - { - printf("Process found but you aren't allowed to control it\n"); - } - else - { - pid = ret; - } - } - if (ret > 0) - { - if (ret == cpulimit_pid) - { - printf("Target process %ld is cpulimit itself! Aborting because it makes no sense\n", - (long)ret); - exit(1); - } - printf("Process %ld found\n", (long)pid); - /* control */ - limit_process(pid, limit, include_children); - } - if (lazy || quit_flag) - break; - /* wait for 2 seconds before next search */ - sleep_timespec(&wait_time); - } - - return 0; + /* argument variables */ + char *exe = NULL; + int perclimit = 0; + int exe_ok = 0; + int pid_ok = 0; + int limit_ok = 0; + pid_t pid = 0; + int include_children = 0; + int command_mode; + + /* parse arguments */ + int next_option; + int option_index = 0; + /* A string listing valid short options letters */ + const char *short_options = "+p:e:l:vzih"; + /* An array describing valid long options */ + const struct option long_options[] = { + {"pid", required_argument, NULL, 'p'}, + {"exe", required_argument, NULL, 'e'}, + {"limit", required_argument, NULL, 'l'}, + {"verbose", no_argument, NULL, 'v'}, + {"lazy", no_argument, NULL, 'z'}, + {"include-children", no_argument, NULL, 'i'}, + {"help", no_argument, NULL, 'h'}, + {0, 0, 0, 0}}; + + double limit; + + struct timespec wait_time = {2, 0}; + + struct sigaction sa; + + static char program_base_name[PATH_MAX + 1]; + + atexit(quit_handler); + + /* get program name */ + strncpy(program_base_name, basename(argv[0]), sizeof(program_base_name) - 1); + program_base_name[sizeof(program_base_name) - 1] = '\0'; + program_name = program_base_name; + /* get current pid */ + cpulimit_pid = getpid(); + /* get cpu count */ + NCPU = get_ncpu(); + + do + { + next_option = getopt_long(argc, argv, short_options, long_options, &option_index); + switch (next_option) + { + case 'p': + pid = (pid_t)atol(optarg); + pid_ok = 1; + break; + case 'e': + exe = optarg; + exe_ok = 1; + break; + case 'l': + perclimit = atoi(optarg); + limit_ok = 1; + break; + case 'v': + verbose = 1; + break; + case 'z': + lazy = 1; + break; + case 'i': + include_children = 1; + break; + case 'h': + print_usage(stdout, 1); + break; + case '?': + print_usage(stderr, 1); + break; + case -1: + break; + default: + abort(); + } + } while (next_option != -1); + + if (pid_ok && (pid <= 1 || pid >= get_pid_max())) + { + fprintf(stderr, "Error: Invalid value for argument PID\n"); + print_usage(stderr, 1); + exit(1); + } + if (pid != 0) + { + lazy = 1; + } + + if (!limit_ok) + { + fprintf(stderr, "Error: You must specify a cpu limit percentage\n"); + print_usage(stderr, 1); + exit(1); + } + 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); + } + + 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); + exit(1); + } + + if (exe_ok + pid_ok + command_mode > 1) + { + fprintf(stderr, "Error: You must specify exactly one target process, either by name, pid, or command line\n"); + print_usage(stderr, 1); + exit(1); + } + + /* all arguments are ok! */ + sa.sa_handler = sig_handler; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + /* print the number of available cpu */ + if (verbose) + printf("%d cpu detected\n", NCPU); + + if (command_mode) + { + int i; + pid_t child; + /* executable file */ + const char *cmd = argv[optind]; + /* command line arguments */ + 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]; + } + cmd_args[i] = NULL; + + if (verbose) + { + printf("Running command: '%s", cmd); + for (i = 1; i < argc - optind; i++) + { + printf(" %s", cmd_args[i]); + } + printf("'\n"); + } + + child = fork(); + if (child < 0) + { + exit(EXIT_FAILURE); + } + 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"); + free(cmd_args); + exit(ret); + } + else + { + /* parent code */ + pid_t limiter; + free(cmd_args); + limiter = fork(); + if (limiter < 0) + { + exit(EXIT_FAILURE); + } + else if (limiter > 0) + { + /* parent */ + int status_process; + int status_limiter; + waitpid(child, &status_process, 0); + waitpid(limiter, &status_limiter, 0); + if (WIFEXITED(status_process)) + { + if (verbose) + printf("Process %ld terminated with exit status %d\n", + (long)child, (int)WEXITSTATUS(status_process)); + exit(WEXITSTATUS(status_process)); + } + printf("Process %ld terminated abnormally\n", (long)child); + exit(status_process); + } + else + { + /* limiter code */ + if (verbose) + printf("Limiting process %ld\n", (long)child); + limit_process(child, limit, include_children); + exit(0); + } + } + } + + while (!quit_flag) + { + /* look for the target process..or wait for it */ + pid_t ret = 0; + if (pid_ok) + { + /* search by pid */ + ret = find_process_by_pid(pid); + if (ret == 0) + { + printf("No process found\n"); + } + else if (ret < 0) + { + printf("Process found but you aren't allowed to control it\n"); + } + } + else + { + /* search by file or path name */ + ret = find_process_by_name(exe); + if (ret == 0) + { + printf("No process found\n"); + } + else if (ret < 0) + { + printf("Process found but you aren't allowed to control it\n"); + } + else + { + pid = ret; + } + } + if (ret > 0) + { + if (ret == cpulimit_pid) + { + printf("Target process %ld is cpulimit itself! Aborting because it makes no sense\n", + (long)ret); + exit(1); + } + printf("Process %ld found\n", (long)pid); + /* control */ + limit_process(pid, limit, include_children); + } + if (lazy || quit_flag) + break; + /* wait for 2 seconds before next search */ + sleep_timespec(&wait_time); + } + + return 0; } diff --git a/src/list.c b/src/list.c index e90dea9b..f2b1e3fc 100644 --- a/src/list.c +++ b/src/list.c @@ -25,160 +25,160 @@ #include "list.h" #define safe_free(p) \ - do \ - { \ - if ((p) != NULL) \ - { \ - free((p)); \ - (p) = NULL; \ - } \ - } while (0) + do \ + { \ + if ((p) != NULL) \ + { \ + free((p)); \ + (p) = NULL; \ + } \ + } while (0) #define EMPTYLIST NULL void init_list(struct list *l, int keysize) { - l->first = l->last = NULL; - l->keysize = keysize; - l->count = 0; + l->first = l->last = NULL; + l->keysize = keysize; + l->count = 0; } struct list_node *add_elem(struct list *l, void *elem) { - struct list_node *newnode = (struct list_node *)malloc(sizeof(struct list_node)); - if (newnode == NULL) - { - exit(-1); - } - newnode->data = elem; - newnode->previous = l->last; - newnode->next = NULL; - if (l->count == 0) - { - l->first = l->last = newnode; - } - else - { - l->last->next = newnode; - l->last = newnode; - } - l->count++; - return newnode; + struct list_node *newnode = (struct list_node *)malloc(sizeof(struct list_node)); + if (newnode == NULL) + { + exit(-1); + } + newnode->data = elem; + newnode->previous = l->last; + newnode->next = NULL; + if (l->count == 0) + { + l->first = l->last = newnode; + } + else + { + l->last->next = newnode; + l->last = newnode; + } + l->count++; + return newnode; } void delete_node(struct list *l, struct list_node *node) { - if (l->count == 1) - { - l->first = l->last = NULL; - } - else if (node == l->first) - { - node->next->previous = NULL; - l->first = node->next; - } - else if (node == l->last) - { - node->previous->next = NULL; - l->last = node->previous; - } - else - { - node->previous->next = node->next; - node->next->previous = node->previous; - } - l->count--; - safe_free(node); + if (l->count == 1) + { + l->first = l->last = NULL; + } + else if (node == l->first) + { + node->next->previous = NULL; + l->first = node->next; + } + else if (node == l->last) + { + node->previous->next = NULL; + l->last = node->previous; + } + else + { + node->previous->next = node->next; + node->next->previous = node->previous; + } + l->count--; + safe_free(node); } void destroy_node(struct list *l, struct list_node *node) { - safe_free(node->data); - delete_node(l, node); + safe_free(node->data); + delete_node(l, node); } int is_empty_list(const struct list *l) { - return l->count == 0; + return l->count == 0; } int get_list_count(const struct list *l) { - return l->count; + return l->count; } void *first_elem(struct list *l) { - return l->first->data; + return l->first->data; } struct list_node *first_node(const struct list *l) { - return l->first; + return l->first; } void *last_elem(struct list *l) { - return l->last->data; + return l->last->data; } struct list_node *last_node(const struct list *l) { - return l->last; + return l->last; } struct list_node *xlocate_node(struct list *l, const void *elem, int offset, int length) { - struct list_node *tmp; - tmp = l->first; - while (tmp != NULL) - { - if (!memcmp((char *)tmp->data + offset, elem, length == 0 ? l->keysize : length)) - return (tmp); - tmp = tmp->next; - } - return EMPTYLIST; + struct list_node *tmp; + tmp = l->first; + while (tmp != NULL) + { + if (!memcmp((char *)tmp->data + offset, elem, length == 0 ? l->keysize : length)) + return (tmp); + tmp = tmp->next; + } + return EMPTYLIST; } struct list_node *locate_node(struct list *l, const void *elem) { - return (xlocate_node(l, elem, 0, 0)); + return (xlocate_node(l, elem, 0, 0)); } void *xlocate_elem(struct list *l, const void *elem, int offset, int length) { - struct list_node *node = xlocate_node(l, elem, offset, length); - return (node == NULL ? NULL : node->data); + struct list_node *node = xlocate_node(l, elem, offset, length); + return (node == NULL ? NULL : node->data); } void *locate_elem(struct list *l, const void *elem) { - return (xlocate_elem(l, elem, 0, 0)); + return (xlocate_elem(l, elem, 0, 0)); } void clear_list(struct list *l) { - while (l->first != EMPTYLIST) - { - struct list_node *tmp; - tmp = l->first; - l->first = l->first->next; - safe_free(tmp); - } - l->last = EMPTYLIST; - l->count = 0; + while (l->first != EMPTYLIST) + { + struct list_node *tmp; + tmp = l->first; + l->first = l->first->next; + safe_free(tmp); + } + l->last = EMPTYLIST; + l->count = 0; } void destroy_list(struct list *l) { - while (l->first != EMPTYLIST) - { - struct list_node *tmp; - tmp = l->first; - l->first = l->first->next; - safe_free(tmp->data); - safe_free(tmp); - } - l->last = EMPTYLIST; - l->count = 0; + while (l->first != EMPTYLIST) + { + struct list_node *tmp; + tmp = l->first; + l->first = l->first->next; + safe_free(tmp->data); + safe_free(tmp); + } + l->last = EMPTYLIST; + l->count = 0; } diff --git a/src/process_group.c b/src/process_group.c index 334eb403..dc730e18 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -43,7 +43,7 @@ return: pid of the found process, if successful negative pid, if the process does not exist or if the signal fails */ pid_t find_process_by_pid(pid_t pid) { - return (kill(pid, 0) == 0) ? pid : -pid; + return (kill(pid, 0) == 0) ? pid : -pid; } /* look for a process with a given name @@ -54,108 +54,108 @@ return: pid of the found process, if it is found negative pid, if it is found but it's not possible to control it */ pid_t find_process_by_name(char *process_name) { - /* pid of the target process */ - pid_t pid = -1; + /* pid of the target process */ + pid_t pid = -1; - /* process iterator */ - struct process_iterator it; - struct process proc; - struct process_filter filter; - static char process_basename[PATH_MAX + 1]; - static char command_basename[PATH_MAX + 1]; - strncpy(process_basename, basename(process_name), - sizeof(process_basename) - 1); - process_basename[sizeof(process_basename) - 1] = '\0'; - filter.pid = 0; - filter.include_children = 0; - init_process_iterator(&it, &filter); - while (get_next_process(&it, &proc) != -1) - { - int cmp_len; - strncpy(command_basename, basename(proc.command), - sizeof(command_basename) - 1); - command_basename[sizeof(command_basename) - 1] = '\0'; - cmp_len = proc.max_cmd_len - - (strlen(proc.command) - strlen(command_basename)); - /* process found */ - if (cmp_len > 0 && command_basename[0] != '\0' && - strncmp(command_basename, process_basename, cmp_len) == 0) - { - if (pid < 0) - { - pid = proc.pid; - } - else if (is_child_of(pid, proc.pid)) - { - pid = proc.pid; - } - else if (is_child_of(proc.pid, pid)) - { - } - else - { - pid = MIN(proc.pid, pid); - } - } - } - if (close_process_iterator(&it) != 0) - exit(1); - if (pid > 0) - { - /* the process was found */ - return find_process_by_pid(pid); - } - else - { - /* process not found */ - return 0; - } + /* process iterator */ + struct process_iterator it; + struct process proc; + struct process_filter filter; + static char process_basename[PATH_MAX + 1]; + static char command_basename[PATH_MAX + 1]; + strncpy(process_basename, basename(process_name), + sizeof(process_basename) - 1); + process_basename[sizeof(process_basename) - 1] = '\0'; + filter.pid = 0; + filter.include_children = 0; + init_process_iterator(&it, &filter); + while (get_next_process(&it, &proc) != -1) + { + int cmp_len; + strncpy(command_basename, basename(proc.command), + sizeof(command_basename) - 1); + command_basename[sizeof(command_basename) - 1] = '\0'; + cmp_len = proc.max_cmd_len - + (strlen(proc.command) - strlen(command_basename)); + /* process found */ + if (cmp_len > 0 && command_basename[0] != '\0' && + strncmp(command_basename, process_basename, cmp_len) == 0) + { + if (pid < 0) + { + pid = proc.pid; + } + else if (is_child_of(pid, proc.pid)) + { + pid = proc.pid; + } + else if (is_child_of(proc.pid, pid)) + { + } + else + { + pid = MIN(proc.pid, pid); + } + } + } + if (close_process_iterator(&it) != 0) + exit(1); + if (pid > 0) + { + /* the process was found */ + return find_process_by_pid(pid); + } + else + { + /* process not found */ + return 0; + } } int init_process_group(struct process_group *pgroup, pid_t target_pid, int include_children) { - /* hashtable initialization */ - pgroup->proctable = (struct process_table *)malloc(sizeof(struct process_table)); - if (pgroup->proctable == NULL) - { - exit(-1); - } - process_table_init(pgroup->proctable, 2048); - pgroup->target_pid = target_pid; - pgroup->include_children = include_children; - pgroup->proclist = (struct list *)malloc(sizeof(struct list)); - if (pgroup->proclist == NULL) - { - exit(-1); - } - init_list(pgroup->proclist, sizeof(pid_t)); - if (get_time(&pgroup->last_update)) - { - exit(-1); - } - update_process_group(pgroup); - return 0; + /* hashtable initialization */ + pgroup->proctable = (struct process_table *)malloc(sizeof(struct process_table)); + if (pgroup->proctable == NULL) + { + exit(-1); + } + process_table_init(pgroup->proctable, 2048); + pgroup->target_pid = target_pid; + pgroup->include_children = include_children; + pgroup->proclist = (struct list *)malloc(sizeof(struct list)); + if (pgroup->proclist == NULL) + { + exit(-1); + } + init_list(pgroup->proclist, sizeof(pid_t)); + if (get_time(&pgroup->last_update)) + { + exit(-1); + } + update_process_group(pgroup); + return 0; } int close_process_group(struct process_group *pgroup) { - clear_list(pgroup->proclist); - free(pgroup->proclist); - pgroup->proclist = NULL; - process_table_destroy(pgroup->proctable); - free(pgroup->proctable); - pgroup->proctable = NULL; - return 0; + clear_list(pgroup->proclist); + free(pgroup->proclist); + pgroup->proclist = NULL; + process_table_destroy(pgroup->proctable); + free(pgroup->proctable); + pgroup->proctable = NULL; + return 0; } static struct process *process_dup(struct process *proc) { - struct process *p = (struct process *)malloc(sizeof(struct process)); - if (p == NULL) - { - exit(-1); - } - return memcpy(p, proc, sizeof(struct process)); + struct process *p = (struct process *)malloc(sizeof(struct process)); + if (p == NULL) + { + exit(-1); + } + return memcpy(p, proc, sizeof(struct process)); } /* parameter in range 0-1 */ @@ -164,62 +164,62 @@ static struct process *process_dup(struct process *proc) void update_process_group(struct process_group *pgroup) { - struct process_iterator it; - struct process tmp_process, *p; - struct process_filter filter; - struct timespec now; - double dt; - if (get_time(&now)) - { - exit(1); - } - /* time elapsed from previous sample (in ms) */ - dt = timediff_in_ms(&now, &pgroup->last_update); - filter.pid = pgroup->target_pid; - filter.include_children = pgroup->include_children; - init_process_iterator(&it, &filter); - clear_list(pgroup->proclist); - init_list(pgroup->proclist, sizeof(pid_t)); + struct process_iterator it; + struct process tmp_process, *p; + struct process_filter filter; + struct timespec now; + double dt; + if (get_time(&now)) + { + exit(1); + } + /* time elapsed from previous sample (in ms) */ + dt = timediff_in_ms(&now, &pgroup->last_update); + filter.pid = pgroup->target_pid; + filter.include_children = pgroup->include_children; + init_process_iterator(&it, &filter); + clear_list(pgroup->proclist); + init_list(pgroup->proclist, sizeof(pid_t)); - while (get_next_process(&it, &tmp_process) != -1) - { - p = process_table_find(pgroup->proctable, &tmp_process); - if (p == NULL) - { - /* process is new. add it */ - tmp_process.cpu_usage = -1; - p = process_dup(&tmp_process); - process_table_add(pgroup->proctable, p); - add_elem(pgroup->proclist, p); - } - else - { - double sample; - add_elem(pgroup->proclist, p); - if (dt < MIN_DT) - continue; - /* process exists. update CPU usage */ - sample = (tmp_process.cputime - p->cputime) / dt; - if (p->cpu_usage < 0) - { - /* initialization */ - p->cpu_usage = sample; - } - else - { - /* usage adjustment */ - p->cpu_usage = (1.0 - ALPHA) * p->cpu_usage + ALPHA * sample; - } - p->cputime = tmp_process.cputime; - } - } - close_process_iterator(&it); - if (dt < MIN_DT) - return; - pgroup->last_update = now; + while (get_next_process(&it, &tmp_process) != -1) + { + p = process_table_find(pgroup->proctable, &tmp_process); + if (p == NULL) + { + /* process is new. add it */ + tmp_process.cpu_usage = -1; + p = process_dup(&tmp_process); + process_table_add(pgroup->proctable, p); + add_elem(pgroup->proclist, p); + } + else + { + double sample; + add_elem(pgroup->proclist, p); + if (dt < MIN_DT) + continue; + /* process exists. update CPU usage */ + sample = (tmp_process.cputime - p->cputime) / dt; + if (p->cpu_usage < 0) + { + /* initialization */ + p->cpu_usage = sample; + } + else + { + /* usage adjustment */ + p->cpu_usage = (1.0 - ALPHA) * p->cpu_usage + ALPHA * sample; + } + p->cputime = tmp_process.cputime; + } + } + close_process_iterator(&it); + if (dt < MIN_DT) + return; + pgroup->last_update = now; } int remove_process(struct process_group *pgroup, pid_t pid) { - return process_table_del_pid(pgroup->proctable, pid); + return process_table_del_pid(pgroup->proctable, pid); } diff --git a/src/process_group.h b/src/process_group.h index 25a1990a..9ddeff3f 100644 --- a/src/process_group.h +++ b/src/process_group.h @@ -38,20 +38,20 @@ */ struct process_group { - /** Pointer to the process table for storing process information */ - struct process_table *proctable; + /** Pointer to the process table for storing process information */ + struct process_table *proctable; - /** Pointer to the list of processes in this group */ - struct list *proclist; + /** Pointer to the list of processes in this group */ + struct list *proclist; - /** PID of the target process to monitor */ - pid_t target_pid; + /** PID of the target process to monitor */ + pid_t target_pid; - /** Flag indicating whether to include child processes (1 for yes, 0 for no) */ - int include_children; + /** Flag indicating whether to include child processes (1 for yes, 0 for no) */ + int include_children; - /** Timestamp of the last update for this process group */ - struct timespec last_update; + /** Timestamp of the last update for this process group */ + struct timespec last_update; }; /** diff --git a/src/process_iterator.h b/src/process_iterator.h index 0f4d680f..610de6ba 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -43,23 +43,23 @@ */ struct process { - /* Process ID of the process */ - pid_t pid; + /* Process ID of the process */ + pid_t pid; - /* Parent Process ID of the process */ - pid_t ppid; + /* Parent Process ID of the process */ + pid_t ppid; - /* CPU time used by the process (in milliseconds) */ - double cputime; + /* CPU time used by the process (in milliseconds) */ + double cputime; - /* Actual CPU usage estimation (value in range 0-1) */ - double cpu_usage; + /* Actual CPU usage estimation (value in range 0-1) */ + double cpu_usage; - /* Absolute path of the executable file */ - char command[PATH_MAX + 1]; + /* Absolute path of the executable file */ + char command[PATH_MAX + 1]; - /* Maximum command length */ - int max_cmd_len; + /* Maximum command length */ + int max_cmd_len; }; /** @@ -67,11 +67,11 @@ struct process */ struct process_filter { - /* Process ID to filter */ - pid_t pid; + /* Process ID to filter */ + pid_t pid; - /* Flag indicating whether to include child processes (1 for yes, 0 for no) */ - int include_children; + /* Flag indicating whether to include child processes (1 for yes, 0 for no) */ + int include_children; }; /** @@ -82,33 +82,33 @@ struct process_filter struct process_iterator { #if defined(__linux__) - /* Directory stream for accessing the /proc filesystem on Linux */ - DIR *dip; + /* Directory stream for accessing the /proc filesystem on Linux */ + DIR *dip; #elif defined(__FreeBSD__) - /* Kernel virtual memory descriptor for accessing process information on FreeBSD */ - kvm_t *kd; + /* Kernel virtual memory descriptor for accessing process information on FreeBSD */ + kvm_t *kd; - /* Array of process information structures */ - struct kinfo_proc *procs; + /* Array of process information structures */ + struct kinfo_proc *procs; - /* Total number of processes retrieved */ - int count; + /* Total number of processes retrieved */ + int count; - /* Current index in the process array */ - int i; + /* Current index in the process array */ + int i; #elif defined(__APPLE__) - /* Current index in the process list */ - int i; + /* Current index in the process list */ + int i; - /* Total number of processes retrieved */ - int count; + /* Total number of processes retrieved */ + int count; - /* List of process IDs */ - pid_t *pidlist; + /* List of process IDs */ + pid_t *pidlist; #endif - /* Pointer to a process filter to apply during iteration */ - struct process_filter *filter; + /* Pointer to a process filter to apply during iteration */ + struct process_filter *filter; }; /** diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 86ea1e55..be0a789f 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -33,182 +33,182 @@ static int unique_nonzero_pids(pid_t *arr_in, int len_in, pid_t *arr_out) { - pid_t *source = arr_in; - int len_out = 0; - int i, j; - if (arr_out == NULL) - return -1; - if (arr_in == arr_out) - { - source = (pid_t *)malloc(sizeof(pid_t) * len_in); - if (source == NULL) - { - exit(-1); - } - memcpy(source, arr_in, sizeof(pid_t) * len_in); - } - for (i = 0; i < len_in; i++) - { - int found = 0; - if (source[i] == 0) - continue; - for (j = 0; !found && j < len_out; j++) - { - found = (source[i] == arr_out[j]); - } - if (!found) - { - arr_out[len_out++] = source[i]; - } - } - if (arr_in == arr_out) - { - free(source); - } - return len_out - 1; + pid_t *source = arr_in; + int len_out = 0; + int i, j; + if (arr_out == NULL) + return -1; + if (arr_in == arr_out) + { + source = (pid_t *)malloc(sizeof(pid_t) * len_in); + if (source == NULL) + { + exit(-1); + } + memcpy(source, arr_in, sizeof(pid_t) * len_in); + } + for (i = 0; i < len_in; i++) + { + int found = 0; + if (source[i] == 0) + continue; + for (j = 0; !found && j < len_out; j++) + { + found = (source[i] == arr_out[j]); + } + if (!found) + { + arr_out[len_out++] = source[i]; + } + } + if (arr_in == arr_out) + { + free(source); + } + return len_out - 1; } int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { - it->i = 0; - /* Find out how much to allocate for it->pidlist */ - if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0)) <= 0) - { - fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); - return -1; - } - /* Allocate and populate it->pidlist */ - if ((it->pidlist = (pid_t *)malloc((it->count) * sizeof(pid_t))) == NULL) - { - fprintf(stderr, "malloc: %s\n", strerror(errno)); - exit(-1); - } - if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, it->pidlist, it->count)) <= 0) - { - fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); - return -1; - } - it->count = unique_nonzero_pids(it->pidlist, it->count, it->pidlist); - it->filter = filter; - return 0; + it->i = 0; + /* Find out how much to allocate for it->pidlist */ + if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0)) <= 0) + { + fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); + return -1; + } + /* Allocate and populate it->pidlist */ + if ((it->pidlist = (pid_t *)malloc((it->count) * sizeof(pid_t))) == NULL) + { + fprintf(stderr, "malloc: %s\n", strerror(errno)); + exit(-1); + } + if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, it->pidlist, it->count)) <= 0) + { + fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); + return -1; + } + it->count = unique_nonzero_pids(it->pidlist, it->count, it->pidlist); + it->filter = filter; + return 0; } static void pti2proc(struct proc_taskallinfo *ti, struct process *process) { - process->pid = ti->pbsd.pbi_pid; - process->ppid = ti->pbsd.pbi_ppid; - process->cputime = ti->ptinfo.pti_total_user / 1e6 + ti->ptinfo.pti_total_system / 1e6; - if (ti->pbsd.pbi_name[0] != '\0') - { - process->max_cmd_len = MIN(sizeof(process->command), sizeof(ti->pbsd.pbi_name)) - 1; - strncpy(process->command, ti->pbsd.pbi_name, process->max_cmd_len); - } - else - { - process->max_cmd_len = MIN(sizeof(process->command), sizeof(ti->pbsd.pbi_comm)) - 1; - strncpy(process->command, ti->pbsd.pbi_comm, process->max_cmd_len); - } - process->command[process->max_cmd_len] = '\0'; + process->pid = ti->pbsd.pbi_pid; + process->ppid = ti->pbsd.pbi_ppid; + process->cputime = ti->ptinfo.pti_total_user / 1e6 + ti->ptinfo.pti_total_system / 1e6; + if (ti->pbsd.pbi_name[0] != '\0') + { + process->max_cmd_len = MIN(sizeof(process->command), sizeof(ti->pbsd.pbi_name)) - 1; + strncpy(process->command, ti->pbsd.pbi_name, process->max_cmd_len); + } + else + { + process->max_cmd_len = MIN(sizeof(process->command), sizeof(ti->pbsd.pbi_comm)) - 1; + strncpy(process->command, ti->pbsd.pbi_comm, process->max_cmd_len); + } + process->command[process->max_cmd_len] = '\0'; } static int get_process_pti(pid_t pid, struct proc_taskallinfo *ti) { - int bytes; - bytes = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, ti, sizeof(*ti)); - if (bytes <= 0) - { - if (!(errno & (EPERM | ESRCH))) - { - fprintf(stderr, "proc_pidinfo: %s\n", strerror(errno)); - } - return -1; - } - else if (bytes < (int)sizeof(ti)) - { - fprintf(stderr, "proc_pidinfo: too few bytes; expected %lu, got %d\n", (unsigned long)sizeof(ti), bytes); - return -1; - } - return 0; + int bytes; + bytes = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, ti, sizeof(*ti)); + if (bytes <= 0) + { + if (!(errno & (EPERM | ESRCH))) + { + fprintf(stderr, "proc_pidinfo: %s\n", strerror(errno)); + } + return -1; + } + else if (bytes < (int)sizeof(ti)) + { + fprintf(stderr, "proc_pidinfo: too few bytes; expected %lu, got %d\n", (unsigned long)sizeof(ti), bytes); + return -1; + } + return 0; } pid_t getppid_of(pid_t pid) { - struct proc_taskallinfo ti; - if (get_process_pti(pid, &ti) == 0) - { - return ti.pbsd.pbi_ppid; - } - return (pid_t)(-1); + struct proc_taskallinfo ti; + if (get_process_pti(pid, &ti) == 0) + { + return ti.pbsd.pbi_ppid; + } + return (pid_t)(-1); } int is_child_of(pid_t child_pid, pid_t parent_pid) { - if (child_pid <= 0 || parent_pid <= 0 || child_pid == parent_pid) - return 0; - while (child_pid > 1 && child_pid != parent_pid) - { - child_pid = getppid_of(child_pid); - } - return child_pid == parent_pid; + if (child_pid <= 0 || parent_pid <= 0 || child_pid == parent_pid) + return 0; + while (child_pid > 1 && child_pid != parent_pid) + { + child_pid = getppid_of(child_pid); + } + return child_pid == parent_pid; } 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) - { - struct proc_taskallinfo ti; - if (get_process_pti(it->filter->pid, &ti) != 0) - { - it->i = it->count = 0; - return -1; - } - it->i = it->count = 1; - pti2proc(&ti, p); - return 0; - } - while (it->i < it->count) - { - struct proc_taskallinfo ti; - if (get_process_pti(it->pidlist[it->i], &ti) != 0) - { - it->i++; - continue; - } - if (ti.pbsd.pbi_flags & PROC_FLAG_SYSTEM) - { - it->i++; - continue; - } - if (it->filter->pid != 0 && it->filter->include_children) - { - it->i++; - pti2proc(&ti, p); - if (p->pid != it->pidlist[it->i - 1]) /* I don't know why this can happen */ - continue; - if (p->pid != it->filter->pid && !is_child_of(p->pid, it->filter->pid)) - continue; - return 0; - } - else if (it->filter->pid == 0) - { - it->i++; - pti2proc(&ti, p); - return 0; - } - } - return -1; + if (it->i == it->count) + return -1; + if (it->filter->pid != 0 && !it->filter->include_children) + { + struct proc_taskallinfo ti; + if (get_process_pti(it->filter->pid, &ti) != 0) + { + it->i = it->count = 0; + return -1; + } + it->i = it->count = 1; + pti2proc(&ti, p); + return 0; + } + while (it->i < it->count) + { + struct proc_taskallinfo ti; + if (get_process_pti(it->pidlist[it->i], &ti) != 0) + { + it->i++; + continue; + } + if (ti.pbsd.pbi_flags & PROC_FLAG_SYSTEM) + { + it->i++; + continue; + } + if (it->filter->pid != 0 && it->filter->include_children) + { + it->i++; + pti2proc(&ti, p); + if (p->pid != it->pidlist[it->i - 1]) /* I don't know why this can happen */ + continue; + if (p->pid != it->filter->pid && !is_child_of(p->pid, it->filter->pid)) + continue; + return 0; + } + else if (it->filter->pid == 0) + { + it->i++; + pti2proc(&ti, p); + return 0; + } + } + return -1; } int close_process_iterator(struct process_iterator *it) { - free(it->pidlist); - it->pidlist = NULL; - it->filter = NULL; - it->count = 0; - it->i = 0; - return 0; + free(it->pidlist); + it->pidlist = NULL; + it->filter = NULL; + it->count = 0; + it->i = 0; + return 0; } #endif diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index c9cdfc80..2ec5b3e5 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -38,154 +38,154 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { - static 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_openfiles: %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); - return -1; - } - it->filter = filter; - return 0; + static 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_openfiles: %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); + return -1; + } + it->filter = filter; + return 0; } static void kproc2proc(kvm_t *kd, struct kinfo_proc *kproc, struct process *proc) { - char **args; - proc->pid = kproc->ki_pid; - proc->ppid = kproc->ki_ppid; - proc->cputime = kproc->ki_runtime / 1000.0; - proc->max_cmd_len = sizeof(proc->command) - 1; - if ((args = kvm_getargv(kd, kproc, sizeof(proc->command))) != NULL) - { - strncpy(proc->command, args[0], proc->max_cmd_len); - proc->command[proc->max_cmd_len] = '\0'; - } - else - { - proc->command[0] = '\0'; - } + char **args; + proc->pid = kproc->ki_pid; + proc->ppid = kproc->ki_ppid; + proc->cputime = kproc->ki_runtime / 1000.0; + proc->max_cmd_len = sizeof(proc->command) - 1; + if ((args = kvm_getargv(kd, kproc, sizeof(proc->command))) != NULL) + { + strncpy(proc->command, args[0], proc->max_cmd_len); + proc->command[proc->max_cmd_len] = '\0'; + } + else + { + proc->command[0] = '\0'; + } } 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) - { - return -1; - } - kproc2proc(kd, kproc, process); - return 0; + int count; + struct kinfo_proc *kproc = kvm_getprocs(kd, KERN_PROC_PID, pid, &count); + if (count == 0 || kproc == NULL) + { + return -1; + } + kproc2proc(kd, kproc, process); + return 0; } static pid_t _getppid_of(kvm_t *kd, pid_t pid) { - int count; - struct kinfo_proc *kproc = kvm_getprocs(kd, KERN_PROC_PID, pid, &count); - return (count == 0 || kproc == NULL) ? (pid_t)(-1) : kproc->ki_ppid; + int count; + struct kinfo_proc *kproc = kvm_getprocs(kd, KERN_PROC_PID, pid, &count); + return (count == 0 || kproc == NULL) ? (pid_t)(-1) : kproc->ki_ppid; } pid_t getppid_of(pid_t pid) { - pid_t ppid; - static char errbuf[_POSIX2_LINE_MAX]; - kvm_t *kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); - if (kd == NULL) - { - fprintf(stderr, "kvm_openfiles: %s\n", errbuf); - return (pid_t)(-1); - } - ppid = _getppid_of(kd, pid); - kvm_close(kd); - return ppid; + pid_t ppid; + static char errbuf[_POSIX2_LINE_MAX]; + kvm_t *kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); + if (kd == NULL) + { + fprintf(stderr, "kvm_openfiles: %s\n", errbuf); + return (pid_t)(-1); + } + ppid = _getppid_of(kd, pid); + kvm_close(kd); + return ppid; } static int _is_child_of(kvm_t *kd, pid_t child_pid, pid_t parent_pid) { - if (child_pid <= 0 || parent_pid <= 0 || child_pid == parent_pid) - return 0; - while (child_pid > 1 && child_pid != parent_pid) - { - child_pid = _getppid_of(kd, child_pid); - } - return child_pid == parent_pid; + if (child_pid <= 0 || parent_pid <= 0 || child_pid == parent_pid) + return 0; + while (child_pid > 1 && child_pid != parent_pid) + { + child_pid = _getppid_of(kd, child_pid); + } + return child_pid == parent_pid; } int is_child_of(pid_t child_pid, pid_t parent_pid) { - int ret; - static char errbuf[_POSIX2_LINE_MAX]; - kvm_t *kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); - if (kd == NULL) - { - fprintf(stderr, "kvm_openfiles: %s\n", errbuf); - return (pid_t)(-1); - } - ret = _is_child_of(kd, child_pid, parent_pid); - kvm_close(kd); - return ret; + int ret; + static char errbuf[_POSIX2_LINE_MAX]; + kvm_t *kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); + if (kd == NULL) + { + fprintf(stderr, "kvm_openfiles: %s\n", errbuf); + return (pid_t)(-1); + } + ret = _is_child_of(kd, child_pid, parent_pid); + kvm_close(kd); + return ret; } 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) - { - if (get_single_process(it->kd, it->filter->pid, p) != 0) - { - it->i = it->count = 0; - return -1; - } - 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) - { - it->i++; - kproc2proc(it->kd, kproc, p); - if (p->pid != it->filter->pid && - !_is_child_of(it->kd, p->pid, it->filter->pid)) - continue; - return 0; - } - else if (it->filter->pid == 0) - { - it->i++; - kproc2proc(it->kd, kproc, p); - return 0; - } - } - return -1; + if (it->i == it->count) + { + return -1; + } + if (it->filter->pid != 0 && !it->filter->include_children) + { + if (get_single_process(it->kd, it->filter->pid, p) != 0) + { + it->i = it->count = 0; + return -1; + } + 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) + { + it->i++; + kproc2proc(it->kd, kproc, p); + if (p->pid != it->filter->pid && + !_is_child_of(it->kd, p->pid, it->filter->pid)) + continue; + return 0; + } + else if (it->filter->pid == 0) + { + it->i++; + kproc2proc(it->kd, kproc, p); + 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; + if (kvm_close(it->kd) == -1) + { + fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(it->kd)); + return -1; + } + return 0; } #endif diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 381d0730..b6741312 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -38,213 +38,213 @@ static int check_proc(void) { - struct statfs mnt; - if (statfs("/proc", &mnt) < 0) - return 0; - if (mnt.f_type != PROC_SUPER_MAGIC) - return 0; - return 1; + struct statfs mnt; + if (statfs("/proc", &mnt) < 0) + return 0; + if (mnt.f_type != PROC_SUPER_MAGIC) + 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; - return 0; + 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; + return 0; } static int read_process_info(pid_t pid, struct process *p) { - char statfile[32], exefile[32], state; - double utime, stime; - long ppid; - FILE *fd; - int ret = 0; - static double sc_clk_tck = -1.0; - - p->pid = pid; - - /* read command line */ - sprintf(exefile, "/proc/%ld/cmdline", (long)p->pid); - if ((fd = fopen(exefile, "r")) != NULL) - { - if (fgets(p->command, sizeof(p->command), fd) == NULL) - { - ret = -1; - } - else - { - p->max_cmd_len = sizeof(p->command) - 1; - } - fclose(fd); - } - else - { - ret = -1; - } - - if (ret != 0) - { - return ret; - } - - /* read stat file */ - sprintf(statfile, "/proc/%ld/stat", (long)p->pid); - if ((fd = fopen(statfile, "r")) != NULL) - { - if (fscanf(fd, "%*d (%*[^)]) %c %ld %*d %*d %*d %*d %*d %*d %*d %*d %*d %lf %lf", - &state, &ppid, &utime, &stime) != 4 || - strchr("ZXx", state) != NULL) - { - ret = -1; - } - else - { - p->ppid = (pid_t)ppid; - if (sc_clk_tck < 0) - { - sc_clk_tck = (double)sysconf(_SC_CLK_TCK); - } - p->cputime = (utime + stime) * 1000.0 / sc_clk_tck; - } - fclose(fd); - } - else - { - ret = -1; - } - - return ret; + char statfile[32], exefile[32], state; + double utime, stime; + long ppid; + FILE *fd; + int ret = 0; + static double sc_clk_tck = -1.0; + + p->pid = pid; + + /* read command line */ + sprintf(exefile, "/proc/%ld/cmdline", (long)p->pid); + if ((fd = fopen(exefile, "r")) != NULL) + { + if (fgets(p->command, sizeof(p->command), fd) == NULL) + { + ret = -1; + } + else + { + p->max_cmd_len = sizeof(p->command) - 1; + } + fclose(fd); + } + else + { + ret = -1; + } + + if (ret != 0) + { + return ret; + } + + /* read stat file */ + sprintf(statfile, "/proc/%ld/stat", (long)p->pid); + if ((fd = fopen(statfile, "r")) != NULL) + { + if (fscanf(fd, "%*d (%*[^)]) %c %ld %*d %*d %*d %*d %*d %*d %*d %*d %*d %lf %lf", + &state, &ppid, &utime, &stime) != 4 || + strchr("ZXx", state) != NULL) + { + ret = -1; + } + else + { + p->ppid = (pid_t)ppid; + if (sc_clk_tck < 0) + { + sc_clk_tck = (double)sysconf(_SC_CLK_TCK); + } + p->cputime = (utime + stime) * 1000.0 / sc_clk_tck; + } + fclose(fd); + } + else + { + ret = -1; + } + + return ret; } pid_t getppid_of(pid_t pid) { - char statfile[32]; - FILE *fd; - long ppid = -1; - if (pid <= 0) - return (pid_t)(-1); - sprintf(statfile, "/proc/%ld/stat", (long)pid); - if ((fd = fopen(statfile, "r")) != NULL) - { - if (fscanf(fd, "%*d (%*[^)]) %*c %ld", &ppid) != 1) - ppid = -1; - fclose(fd); - } - return (pid_t)ppid; + char statfile[32]; + FILE *fd; + long ppid = -1; + if (pid <= 0) + return (pid_t)(-1); + sprintf(statfile, "/proc/%ld/stat", (long)pid); + if ((fd = fopen(statfile, "r")) != NULL) + { + if (fscanf(fd, "%*d (%*[^)]) %*c %ld", &ppid) != 1) + ppid = -1; + fclose(fd); + } + return (pid_t)ppid; } static int get_start_time(pid_t pid, struct timespec *start_time) { - struct stat procfs_stat; - char procfs_path[32]; - int ret; - sprintf(procfs_path, "/proc/%ld", (long)pid); - ret = stat(procfs_path, &procfs_stat); - if (ret == 0 && start_time != NULL) - *start_time = procfs_stat.st_ctim; - return ret; + struct stat procfs_stat; + char procfs_path[32]; + int ret; + sprintf(procfs_path, "/proc/%ld", (long)pid); + ret = stat(procfs_path, &procfs_stat); + if (ret == 0 && start_time != NULL) + *start_time = procfs_stat.st_ctim; + return ret; } static int earlier_than(const struct timespec *t1, const struct timespec *t2) { - return t1->tv_sec < t2->tv_sec || - (t1->tv_sec == t2->tv_sec && t1->tv_nsec < t2->tv_nsec); + return t1->tv_sec < t2->tv_sec || + (t1->tv_sec == t2->tv_sec && t1->tv_nsec < t2->tv_nsec); } int is_child_of(pid_t child_pid, pid_t parent_pid) { - int ret_child, ret_parent; - struct timespec child_start_time, parent_start_time; - if (child_pid <= 1 || parent_pid <= 0 || child_pid == parent_pid) - return 0; - if (parent_pid == 1) - return 1; - ret_parent = get_start_time(parent_pid, &parent_start_time); - while (child_pid > 1) - { - if (ret_parent == 0) - { - ret_child = get_start_time(child_pid, &child_start_time); - if (ret_child == 0 && earlier_than(&child_start_time, &parent_start_time)) - return 0; - } - child_pid = getppid_of(child_pid); - if (child_pid == parent_pid) - return 1; - } - return 0; + int ret_child, ret_parent; + struct timespec child_start_time, parent_start_time; + if (child_pid <= 1 || parent_pid <= 0 || child_pid == parent_pid) + return 0; + if (parent_pid == 1) + return 1; + ret_parent = get_start_time(parent_pid, &parent_start_time); + while (child_pid > 1) + { + if (ret_parent == 0) + { + ret_child = get_start_time(child_pid, &child_start_time); + if (ret_child == 0 && earlier_than(&child_start_time, &parent_start_time)) + return 0; + } + child_pid = getppid_of(child_pid); + if (child_pid == parent_pid) + return 1; + } + return 0; } static int is_numeric(const char *str) { - for (; *str != '\0' && isdigit(*str); str++) - ; - return *str == '\0'; + for (; *str != '\0' && isdigit(*str); str++) + ; + return *str == '\0'; } int get_next_process(struct process_iterator *it, struct process *p) { - struct dirent *dit = NULL; - - if (it->dip == NULL) - { - /* end of processes */ - return -1; - } - if (it->filter->pid != 0 && !it->filter->include_children) - { - int ret = read_process_info(it->filter->pid, p); - closedir(it->dip); - it->dip = NULL; - if (ret != 0) - return -1; - return 0; - } - - /* read in from /proc and seek for process dirs */ - while ((dit = readdir(it->dip)) != NULL) - { + struct dirent *dit = NULL; + + if (it->dip == NULL) + { + /* end of processes */ + return -1; + } + if (it->filter->pid != 0 && !it->filter->include_children) + { + int ret = read_process_info(it->filter->pid, p); + closedir(it->dip); + it->dip = NULL; + if (ret != 0) + return -1; + return 0; + } + + /* read in from /proc and seek for process dirs */ + while ((dit = readdir(it->dip)) != NULL) + { #ifdef _DIRENT_HAVE_D_TYPE - if (dit->d_type != DT_DIR) - continue; + if (dit->d_type != DT_DIR) + continue; #endif - if (!is_numeric(dit->d_name) || - (p->pid = (pid_t)atol(dit->d_name)) <= 0) - continue; - if (it->filter->pid != 0 && - it->filter->pid != p->pid && - !is_child_of(p->pid, it->filter->pid)) - continue; - if (read_process_info(p->pid, p) != 0) - continue; - return 0; - } - /* end of processes */ - closedir(it->dip); - it->dip = NULL; - return -1; + if (!is_numeric(dit->d_name) || + (p->pid = (pid_t)atol(dit->d_name)) <= 0) + continue; + if (it->filter->pid != 0 && + it->filter->pid != p->pid && + !is_child_of(p->pid, it->filter->pid)) + continue; + if (read_process_info(p->pid, p) != 0) + continue; + return 0; + } + /* end of processes */ + closedir(it->dip); + it->dip = NULL; + return -1; } 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; + if (it->dip != NULL && closedir(it->dip) == -1) + { + perror("closedir"); + return 1; + } + it->dip = NULL; + return 0; } #endif diff --git a/src/util.c b/src/util.c index 64ca9070..9424fc94 100644 --- a/src/util.c +++ b/src/util.c @@ -10,72 +10,72 @@ #ifdef __IMPL_BASENAME const char *__basename(const char *path) { - const char *p = strrchr(path, '/'); - return p != NULL ? p + 1 : path; + const char *p = strrchr(path, '/'); + return p != NULL ? p + 1 : path; } #endif #ifdef __IMPL_GET_TIME int __get_time(struct timespec *ts) { - struct timeval tv; - if (gettimeofday(&tv, NULL)) - { - return -1; - } - ts->tv_sec = tv.tv_sec; - ts->tv_nsec = tv.tv_usec * 1000L; - return 0; + struct timeval tv; + if (gettimeofday(&tv, NULL)) + { + return -1; + } + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000L; + return 0; } #endif void increase_priority(void) { - static const int MAX_PRIORITY = -20; - int old_priority, priority; - old_priority = getpriority(PRIO_PROCESS, 0); - for (priority = MAX_PRIORITY; priority < old_priority; priority++) - { - if (setpriority(PRIO_PROCESS, 0, priority) == 0 && - getpriority(PRIO_PROCESS, 0) == priority) - break; - } + static const int MAX_PRIORITY = -20; + int old_priority, priority; + old_priority = getpriority(PRIO_PROCESS, 0); + for (priority = MAX_PRIORITY; priority < old_priority; priority++) + { + if (setpriority(PRIO_PROCESS, 0, priority) == 0 && + getpriority(PRIO_PROCESS, 0) == priority) + break; + } } /* Get the number of CPUs */ int get_ncpu(void) { - int ncpu; + int ncpu; #if defined(_SC_NPROCESSORS_ONLN) - ncpu = sysconf(_SC_NPROCESSORS_ONLN); + ncpu = sysconf(_SC_NPROCESSORS_ONLN); #elif defined(__APPLE__) - int mib[2] = {CTL_HW, HW_NCPU}; - size_t len = sizeof(ncpu); - sysctl(mib, 2, &ncpu, &len, NULL, 0); + int mib[2] = {CTL_HW, HW_NCPU}; + size_t len = sizeof(ncpu); + sysctl(mib, 2, &ncpu, &len, NULL, 0); #elif defined(_GNU_SOURCE) - ncpu = get_nprocs(); + ncpu = get_nprocs(); #else - ncpu = -1; + ncpu = -1; #endif - return ncpu; + return ncpu; } pid_t get_pid_max(void) { #if defined(__linux__) - /* read /proc/sys/kernel/pid_max */ - long pid_max = -1; - FILE *fd; - if ((fd = fopen("/proc/sys/kernel/pid_max", "r")) != NULL) - { - if (fscanf(fd, "%ld", &pid_max) != 1) - pid_max = -1; - fclose(fd); - } - return (pid_t)pid_max; + /* read /proc/sys/kernel/pid_max */ + long pid_max = -1; + FILE *fd; + if ((fd = fopen("/proc/sys/kernel/pid_max", "r")) != NULL) + { + if (fscanf(fd, "%ld", &pid_max) != 1) + pid_max = -1; + fclose(fd); + } + return (pid_t)pid_max; #elif defined(__FreeBSD__) - return (pid_t)99999; + return (pid_t)99999; #elif defined(__APPLE__) - return (pid_t)99998; + return (pid_t)99998; #endif } diff --git a/src/util.h b/src/util.h index d32ddcc2..ef2d1fac 100644 --- a/src/util.h +++ b/src/util.h @@ -19,27 +19,27 @@ /* Generates a unique ID based on a prefix */ #define __UNIQUE_ID(prefix) \ - __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__) + __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__) #endif #ifndef MIN #ifdef __GNUC__ /* Helper for finding the minimum of two values, utilizing type safety */ #define __min(t1, t2, min1, min2, x, y) \ - (__extension__({ \ - t1 min1 = (x); \ - t2 min2 = (y); \ - (void)(&min1 == &min2); \ - min1 < min2 ? min1 : min2; \ - })) + (__extension__({ \ + t1 min1 = (x); \ + t2 min2 = (y); \ + (void)(&min1 == &min2); \ + min1 < min2 ? min1 : min2; \ + })) #define MIN(x, y) \ - __min(__typeof__(x), __typeof__(y), \ - __UNIQUE_ID(min1_), __UNIQUE_ID(min2_), \ - x, y) + __min(__typeof__(x), __typeof__(y), \ + __UNIQUE_ID(min1_), __UNIQUE_ID(min2_), \ + x, y) #else /* Simple macro to find the minimum of two values */ #define MIN(a, b) \ - (((a) < (b)) ? (a) : (b)) + (((a) < (b)) ? (a) : (b)) #endif #endif @@ -47,20 +47,20 @@ #ifdef __GNUC__ /* Helper for finding the maximum of two values, utilizing type safety */ #define __max(t1, t2, max1, max2, x, y) \ - (__extension__({ \ - t1 max1 = (x); \ - t2 max2 = (y); \ - (void)(&max1 == &max2); \ - max1 > max2 ? max1 : max2; \ - })) + (__extension__({ \ + t1 max1 = (x); \ + t2 max2 = (y); \ + (void)(&max1 == &max2); \ + max1 > max2 ? max1 : max2; \ + })) #define MAX(x, y) \ - __max(__typeof__(x), __typeof__(y), \ - __UNIQUE_ID(max1_), __UNIQUE_ID(max2_), \ - x, y) + __max(__typeof__(x), __typeof__(y), \ + __UNIQUE_ID(max1_), __UNIQUE_ID(max2_), \ + x, y) #else /* Simple macro to find the maximum of two values */ #define MAX(a, b) \ - (((a) > (b)) ? (a) : (b)) + (((a) > (b)) ? (a) : (b)) #endif #endif @@ -68,13 +68,13 @@ #ifdef __GNUC__ /* Helper macro to get the basename of a path */ #define __basename(path, full_path, last_slash) \ - (__extension__({ \ - const char *full_path = (path); \ - const char *last_slash = strrchr(full_path, '/'); \ - (last_slash != NULL) ? (last_slash + 1) : full_path; \ - })) + (__extension__({ \ + const char *full_path = (path); \ + const char *last_slash = strrchr(full_path, '/'); \ + (last_slash != NULL) ? (last_slash + 1) : full_path; \ + })) #define basename(path) \ - __basename((path), __UNIQUE_ID(full_path_), __UNIQUE_ID(last_slash_)) + __basename((path), __UNIQUE_ID(full_path_), __UNIQUE_ID(last_slash_)) #else /* Fallback function declaration for basename */ const char *__basename(const char *path); @@ -86,11 +86,11 @@ const char *__basename(const char *path); /* Converts nanoseconds to a timespec structure */ #ifndef nsec2timespec #define nsec2timespec(nsec, t) \ - do \ - { \ - (t)->tv_sec = (time_t)((nsec) / 1e9); \ - (t)->tv_nsec = (long)((nsec) - (t)->tv_sec * 1e9); \ - } while (0) + do \ + { \ + (t)->tv_sec = (time_t)((nsec) / 1e9); \ + (t)->tv_nsec = (long)((nsec) - (t)->tv_sec * 1e9); \ + } while (0) #endif /* Sleep for a specified timespec duration */ @@ -123,7 +123,7 @@ int __get_time(struct timespec *ts); /* Returns the difference between two timespecs in milliseconds */ #ifndef timediff_in_ms #define timediff_in_ms(t1, t2) \ - (((t1)->tv_sec - (t2)->tv_sec) * 1e3 + ((t1)->tv_nsec - (t2)->tv_nsec) / 1e6) + (((t1)->tv_sec - (t2)->tv_sec) * 1e3 + ((t1)->tv_nsec - (t2)->tv_nsec) / 1e6) #endif /* Increases the priority of the current process */ diff --git a/tests/busy.c b/tests/busy.c index 1127b457..34b54d69 100644 --- a/tests/busy.c +++ b/tests/busy.c @@ -12,30 +12,30 @@ static void *loop(void *param __attribute__((unused))) { - volatile int unused_value = 0; - while (1) - (void)unused_value; - return NULL; + volatile int unused_value = 0; + while (1) + (void)unused_value; + return NULL; } int main(int argc, char *argv[]) { - int i = 0; - int num_threads = get_ncpu(); - increase_priority(); - if (argc == 2) - num_threads = atoi(argv[1]); - for (i = 0; i < num_threads - 1; i++) - { - pthread_t thread; - int ret; - if ((ret = pthread_create(&thread, NULL, loop, NULL)) != 0) - { - printf("pthread_create() failed. Error code %d\n", ret); - exit(1); - } - } - loop(NULL); - return 0; + int i = 0; + int num_threads = get_ncpu(); + increase_priority(); + if (argc == 2) + num_threads = atoi(argv[1]); + for (i = 0; i < num_threads - 1; i++) + { + pthread_t thread; + int ret; + if ((ret = pthread_create(&thread, NULL, loop, NULL)) != 0) + { + printf("pthread_create() failed. Error code %d\n", ret); + exit(1); + } + } + loop(NULL); + return 0; } diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index ecb28e47..569ab8f1 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -48,241 +48,241 @@ static void ignore_signal(int sig __attribute__((unused))) static void test_single_process(void) { - 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; - init_process_iterator(&it, &filter); - while (get_next_process(&it, &process) == 0) - { - assert(process.pid == getpid()); - assert(process.ppid == getppid()); - assert(process.cputime <= 100); - count++; - } - assert(count == 1); - close_process_iterator(&it); - /* iterate children */ - filter.pid = getpid(); - filter.include_children = 0; - count = 0; - init_process_iterator(&it, &filter); - while (get_next_process(&it, &process) == 0) - { - assert(process.pid == getpid()); - assert(process.ppid == getppid()); - assert(process.cputime <= 100); - count++; - } - assert(count == 1); - close_process_iterator(&it); + 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; + init_process_iterator(&it, &filter); + while (get_next_process(&it, &process) == 0) + { + assert(process.pid == getpid()); + assert(process.ppid == getppid()); + assert(process.cputime <= 100); + count++; + } + assert(count == 1); + close_process_iterator(&it); + /* iterate children */ + filter.pid = getpid(); + filter.include_children = 0; + count = 0; + init_process_iterator(&it, &filter); + while (get_next_process(&it, &process) == 0) + { + assert(process.pid == getpid()); + assert(process.ppid == getppid()); + assert(process.cputime <= 100); + count++; + } + assert(count == 1); + close_process_iterator(&it); } static void test_multiple_process(void) { - struct process_iterator it; - struct process process; - struct process_filter filter; - int count = 0; - pid_t child = fork(); - if (child == 0) - { - /* child is supposed to be killed by the parent :/ */ - while (1) - sleep(5); - exit(1); - } - filter.pid = getpid(); - filter.include_children = 1; - init_process_iterator(&it, &filter); - 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); - count++; - } - assert(count == 2); - close_process_iterator(&it); - kill(child, SIGKILL); + struct process_iterator it; + struct process process; + struct process_filter filter; + int count = 0; + pid_t child = fork(); + if (child == 0) + { + /* child is supposed to be killed by the parent :/ */ + while (1) + sleep(5); + exit(1); + } + filter.pid = getpid(); + filter.include_children = 1; + init_process_iterator(&it, &filter); + 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); + count++; + } + assert(count == 2); + close_process_iterator(&it); + kill(child, SIGKILL); } static void test_all_processes(void) { - struct process_iterator it; - struct process process; - struct process_filter filter; - int count = 0; - filter.pid = 0; - filter.include_children = 0; - init_process_iterator(&it, &filter); + struct process_iterator it; + struct process process; + struct process_filter filter; + int count = 0; + filter.pid = 0; + filter.include_children = 0; + init_process_iterator(&it, &filter); - while (get_next_process(&it, &process) == 0) - { - if (process.pid == getpid()) - { - assert(process.ppid == getppid()); - assert(process.cputime <= 100); - } - count++; - } - assert(count >= 10); - close_process_iterator(&it); + while (get_next_process(&it, &process) == 0) + { + if (process.pid == getpid()) + { + assert(process.ppid == getppid()); + assert(process.cputime <= 100); + } + count++; + } + assert(count >= 10); + close_process_iterator(&it); } static void test_process_group_all(void) { - struct process_group pgroup; - struct list_node *node = NULL; - int count = 0; - assert(init_process_group(&pgroup, 0, 0) == 0); - update_process_group(&pgroup); - for (node = pgroup.proclist->first; node != NULL; node = node->next) - { - count++; - } - assert(count > 10); - update_process_group(&pgroup); - assert(close_process_group(&pgroup) == 0); + struct process_group pgroup; + struct list_node *node = NULL; + int count = 0; + assert(init_process_group(&pgroup, 0, 0) == 0); + update_process_group(&pgroup); + for (node = pgroup.proclist->first; node != NULL; node = node->next) + { + count++; + } + assert(count > 10); + update_process_group(&pgroup); + assert(close_process_group(&pgroup) == 0); } static void test_process_group_single(int include_children) { - struct process_group pgroup; - int i; - pid_t child = fork(); - if (child == 0) - { - /* child is supposed to be killed by the parent :/ */ - volatile int unused_value = 0; - increase_priority(); - while (1) - (void)unused_value; - exit(1); - } - assert(init_process_group(&pgroup, child, include_children) == 0); - for (i = 0; i < 100; i++) - { - struct list_node *node = NULL; - int count = 0; - struct timespec interval; - update_process_group(&pgroup); - for (node = pgroup.proclist->first; node != NULL; node = node->next) - { - const struct process *p = (const struct process *)(node->data); - assert(p->pid == child); - assert(p->ppid == getpid()); - /* p->cpu_usage should be -1 or [0, 1] */ - assert((p->cpu_usage >= (-1.00001) && p->cpu_usage <= (-0.99999)) || - (p->cpu_usage >= 0 && p->cpu_usage <= 1.05)); - count++; - } - assert(count == 1); - interval.tv_sec = 0; - interval.tv_nsec = 200000000; - sleep_timespec(&interval); - } - assert(close_process_group(&pgroup) == 0); - kill(child, SIGKILL); + struct process_group pgroup; + int i; + pid_t child = fork(); + if (child == 0) + { + /* child is supposed to be killed by the parent :/ */ + volatile int unused_value = 0; + increase_priority(); + while (1) + (void)unused_value; + exit(1); + } + assert(init_process_group(&pgroup, child, include_children) == 0); + for (i = 0; i < 100; i++) + { + struct list_node *node = NULL; + int count = 0; + struct timespec interval; + update_process_group(&pgroup); + for (node = pgroup.proclist->first; node != NULL; node = node->next) + { + const struct process *p = (const struct process *)(node->data); + assert(p->pid == child); + assert(p->ppid == getpid()); + /* p->cpu_usage should be -1 or [0, 1] */ + assert((p->cpu_usage >= (-1.00001) && p->cpu_usage <= (-0.99999)) || + (p->cpu_usage >= 0 && p->cpu_usage <= 1.05)); + count++; + } + assert(count == 1); + interval.tv_sec = 0; + interval.tv_nsec = 200000000; + sleep_timespec(&interval); + } + assert(close_process_group(&pgroup) == 0); + kill(child, SIGKILL); } char *command = NULL; static void test_process_name(void) { - struct process_iterator it; - struct process process; - struct process_filter filter; - static char command_basename[PATH_MAX + 1]; - static char process_basename[PATH_MAX + 1]; - int cmp_len; - 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()); - strncpy(command_basename, basename(command), sizeof(command_basename) - 1); - command_basename[sizeof(command_basename) - 1] = '\0'; - strncpy(process_basename, basename(process.command), sizeof(process_basename) - 1); - process_basename[sizeof(process_basename) - 1] = '\0'; - cmp_len = process.max_cmd_len - (strlen(process.command) - strlen(process_basename)); - assert(strncmp(command_basename, process_basename, cmp_len) == 0); - assert(get_next_process(&it, &process) != 0); - close_process_iterator(&it); + struct process_iterator it; + struct process process; + struct process_filter filter; + static char command_basename[PATH_MAX + 1]; + static char process_basename[PATH_MAX + 1]; + int cmp_len; + 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()); + strncpy(command_basename, basename(command), sizeof(command_basename) - 1); + command_basename[sizeof(command_basename) - 1] = '\0'; + strncpy(process_basename, basename(process.command), sizeof(process_basename) - 1); + process_basename[sizeof(process_basename) - 1] = '\0'; + cmp_len = process.max_cmd_len - (strlen(process.command) - strlen(process_basename)); + assert(strncmp(command_basename, process_basename, cmp_len) == 0); + assert(get_next_process(&it, &process) != 0); + close_process_iterator(&it); } static void test_process_group_wrong_pid(void) { - struct process_group pgroup; - assert(init_process_group(&pgroup, -1, 0) == 0); - assert(pgroup.proclist->count == 0); - update_process_group(&pgroup); - assert(pgroup.proclist->count == 0); - assert(init_process_group(&pgroup, 9999999, 0) == 0); - assert(pgroup.proclist->count == 0); - update_process_group(&pgroup); - assert(pgroup.proclist->count == 0); - assert(close_process_group(&pgroup) == 0); + struct process_group pgroup; + assert(init_process_group(&pgroup, -1, 0) == 0); + assert(pgroup.proclist->count == 0); + update_process_group(&pgroup); + assert(pgroup.proclist->count == 0); + assert(init_process_group(&pgroup, 9999999, 0) == 0); + assert(pgroup.proclist->count == 0); + update_process_group(&pgroup); + assert(pgroup.proclist->count == 0); + assert(close_process_group(&pgroup) == 0); } static void test_find_process_by_pid(void) { - assert(find_process_by_pid(getpid()) == getpid()); + assert(find_process_by_pid(getpid()) == getpid()); } static void test_find_process_by_name(void) { - assert(find_process_by_name(command) == getpid()); - assert(find_process_by_name("") == 0); + assert(find_process_by_name(command) == getpid()); + assert(find_process_by_name("") == 0); } static void test_getppid_of(void) { - struct process_iterator it; - struct process process; - struct process_filter filter; - filter.pid = 0; - filter.include_children = 0; - init_process_iterator(&it, &filter); - while (get_next_process(&it, &process) == 0) - { - assert(getppid_of(process.pid) == process.ppid); - } - close_process_iterator(&it); - assert(getppid_of(getpid()) == getppid()); + struct process_iterator it; + struct process process; + struct process_filter filter; + filter.pid = 0; + filter.include_children = 0; + init_process_iterator(&it, &filter); + while (get_next_process(&it, &process) == 0) + { + assert(getppid_of(process.pid) == process.ppid); + } + close_process_iterator(&it); + assert(getppid_of(getpid()) == getppid()); } int main(int argc __attribute__((unused)), char *argv[]) { - /* ignore SIGINT and SIGTERM during tests*/ - struct sigaction sa; - sa.sa_handler = ignore_signal; - sa.sa_flags = 0; - sigemptyset(&sa.sa_mask); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); + /* ignore SIGINT and SIGTERM during tests*/ + struct sigaction sa; + sa.sa_handler = ignore_signal; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); - command = argv[0]; - increase_priority(); - 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_group_wrong_pid(); - test_process_name(); - test_find_process_by_pid(); - test_find_process_by_name(); - test_getppid_of(); - return 0; + command = argv[0]; + increase_priority(); + 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_group_wrong_pid(); + test_process_name(); + test_find_process_by_pid(); + test_find_process_by_name(); + test_getppid_of(); + return 0; } From a7a12348c338888f9186cada66b14b2d1d219ae9 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 6 Sep 2024 21:23:25 +0800 Subject: [PATCH 116/209] unify comments --- src/process_group.h | 10 +++---- src/util.h | 70 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 59 insertions(+), 21 deletions(-) diff --git a/src/process_group.h b/src/process_group.h index 9ddeff3f..19a02112 100644 --- a/src/process_group.h +++ b/src/process_group.h @@ -38,19 +38,19 @@ */ struct process_group { - /** Pointer to the process table for storing process information */ + /* Pointer to the process table for storing process information */ struct process_table *proctable; - /** Pointer to the list of processes in this group */ + /* Pointer to the list of processes in this group */ struct list *proclist; - /** PID of the target process to monitor */ + /* PID of the target process to monitor */ pid_t target_pid; - /** Flag indicating whether to include child processes (1 for yes, 0 for no) */ + /* Flag indicating whether to include child processes (1 for yes, 0 for no) */ int include_children; - /** Timestamp of the last update for this process group */ + /* Timestamp of the last update for this process group */ struct timespec last_update; }; diff --git a/src/util.h b/src/util.h index ef2d1fac..d92ed6de 100644 --- a/src/util.h +++ b/src/util.h @@ -13,18 +13,24 @@ /* Useful macros */ #if defined(__GNUC__) && !defined(__UNIQUE_ID) -/* Helper macros to concatenate tokens */ +/** + * Helper macros to concatenate tokens + */ #define ___PASTE(a, b) a##b #define __PASTE(a, b) ___PASTE(a, b) -/* Generates a unique ID based on a prefix */ +/** + * Generates a unique ID based on a prefix + */ #define __UNIQUE_ID(prefix) \ __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__) #endif #ifndef MIN #ifdef __GNUC__ -/* Helper for finding the minimum of two values, utilizing type safety */ +/** + * Helper for finding the minimum of two values, utilizing type safety + */ #define __min(t1, t2, min1, min2, x, y) \ (__extension__({ \ t1 min1 = (x); \ @@ -32,12 +38,17 @@ (void)(&min1 == &min2); \ min1 < min2 ? min1 : min2; \ })) +/** + * Macro to find the minimum of two values + */ #define MIN(x, y) \ __min(__typeof__(x), __typeof__(y), \ __UNIQUE_ID(min1_), __UNIQUE_ID(min2_), \ x, y) #else -/* Simple macro to find the minimum of two values */ +/** + * Simple macro to find the minimum of two values + */ #define MIN(a, b) \ (((a) < (b)) ? (a) : (b)) #endif @@ -45,7 +56,9 @@ #ifndef MAX #ifdef __GNUC__ -/* Helper for finding the maximum of two values, utilizing type safety */ +/** + * Helper for finding the maximum of two values, utilizing type safety + */ #define __max(t1, t2, max1, max2, x, y) \ (__extension__({ \ t1 max1 = (x); \ @@ -53,12 +66,17 @@ (void)(&max1 == &max2); \ max1 > max2 ? max1 : max2; \ })) +/** + * Macro to find the maximum of two values + */ #define MAX(x, y) \ __max(__typeof__(x), __typeof__(y), \ __UNIQUE_ID(max1_), __UNIQUE_ID(max2_), \ x, y) #else -/* Simple macro to find the maximum of two values */ +/** + * Simple macro to find the maximum of two values + */ #define MAX(a, b) \ (((a) > (b)) ? (a) : (b)) #endif @@ -66,7 +84,9 @@ #ifndef basename #ifdef __GNUC__ -/* Helper macro to get the basename of a path */ +/** + * Helper macro to get the basename of a path + */ #define __basename(path, full_path, last_slash) \ (__extension__({ \ const char *full_path = (path); \ @@ -76,14 +96,18 @@ #define basename(path) \ __basename((path), __UNIQUE_ID(full_path_), __UNIQUE_ID(last_slash_)) #else -/* Fallback function declaration for basename */ +/** + * Get the basename of a path + */ const char *__basename(const char *path); #define basename(path) __basename(path) #define __IMPL_BASENAME #endif #endif -/* Converts nanoseconds to a timespec structure */ +/** + * Converts nanoseconds to a timespec structure + */ #ifndef nsec2timespec #define nsec2timespec(nsec, t) \ do \ @@ -93,7 +117,9 @@ const char *__basename(const char *path); } while (0) #endif -/* Sleep for a specified timespec duration */ +/** + * Sleep for a specified timespec duration + */ #ifndef sleep_timespec #if defined(__linux__) && _POSIX_C_SOURCE >= 200112L && defined(CLOCK_TAI) #define sleep_timespec(t) clock_nanosleep(CLOCK_TAI, 0, (t), NULL) @@ -102,7 +128,9 @@ const char *__basename(const char *path); #endif #endif -/* Retrieves the current time into a timespec structure */ +/** + * Retrieves the current time into a timespec structure + */ #ifndef get_time #if _POSIX_TIMERS > 0 #if defined(CLOCK_TAI) @@ -113,26 +141,36 @@ const char *__basename(const char *path); #endif #endif -/* Fallback function for getting time if not defined */ +/** + * Fallback function for getting time if not defined + */ #ifndef get_time int __get_time(struct timespec *ts); #define get_time(ts) __get_time(ts) #define __IMPL_GET_TIME #endif -/* Returns the difference between two timespecs in milliseconds */ +/** + * Returns the difference between two timespecs in milliseconds + */ #ifndef timediff_in_ms #define timediff_in_ms(t1, t2) \ (((t1)->tv_sec - (t2)->tv_sec) * 1e3 + ((t1)->tv_nsec - (t2)->tv_nsec) / 1e6) #endif -/* Increases the priority of the current process */ +/** + * Increases the priority of the current process + */ void increase_priority(void); -/* Retrieves the number of available CPUs */ +/** + * Retrieves the number of available CPUs + */ int get_ncpu(void); -/* Retrieves the maximum process ID */ +/** + * Retrieves the maximum process ID + */ pid_t get_pid_max(void); #endif From ede0b3af48f970048746bf259f430ce2eeae28ba Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 8 Sep 2024 21:56:23 +0800 Subject: [PATCH 117/209] process_dup: add a cast for the returned pointer fix compatibility with g++ --- src/process_group.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index dc730e18..910520b8 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -40,7 +40,7 @@ /* 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 */ + negative pid, if the process does not exist or if the signal fails */ pid_t find_process_by_pid(pid_t pid) { return (kill(pid, 0) == 0) ? pid : -pid; @@ -48,10 +48,10 @@ pid_t find_process_by_pid(pid_t 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 + 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 */ + 0, if it's not found + negative pid, if it is found but it's not possible to control it */ pid_t find_process_by_name(char *process_name) { /* pid of the target process */ @@ -155,7 +155,7 @@ static struct process *process_dup(struct process *proc) { exit(-1); } - return memcpy(p, proc, sizeof(struct process)); + return (struct process *)memcpy(p, proc, sizeof(struct process)); } /* parameter in range 0-1 */ From f814c7cdf900252803dc87ed0a73c7b2b0a13036 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 9 Sep 2024 00:46:05 +0800 Subject: [PATCH 118/209] cpulimit.c: refine comments --- src/cpulimit.c | 196 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 137 insertions(+), 59 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 50080966..937d5a44 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -45,39 +45,48 @@ #include "util.h" #ifndef EPSILON +/* Define a very small value to avoid division by zero */ #define EPSILON 1e-12 #endif -/* control time slot in microseconds */ -/* each slot is splitted in a working slice and a sleeping slice */ -/* TODO: make it adaptive, based on the actual system load */ +/* Control time slot in microseconds */ +/* Each slot is split into a working slice and a sleeping slice */ #define TIME_SLOT 100000 /* GLOBAL VARIABLES */ -/* the "family" */ +/* Define a global process group (family of processes) */ struct process_group pgroup; -/* pid of cpulimit */ + +/* PID of cpulimit */ pid_t cpulimit_pid; -/* name of this program (maybe cpulimit...) */ + +/* Name of this program */ char *program_name; -/* number of cpu */ +/* Number of CPUs available in the system */ int NCPU; /* CONFIGURATION VARIABLES */ -/* verbose mode */ +/* Verbose mode flag */ int verbose = 0; -/* lazy mode (exits if there is no process) */ + +/* Lazy mode flag (exit if no process is found) */ int lazy = 0; -/* quit flag for SIGINT and SIGTERM signals */ +/* Quit flag for handling SIGINT and SIGTERM signals */ volatile sig_atomic_t quit_flag = 0; -/* SIGINT and SIGTERM signal handler */ +/** + * Signal handler for SIGINT and SIGTERM signals. + * Sets the quit_flag to 1 when a termination signal is received. + * + * @param sig Signal number (SIGINT or SIGTERM). + */ static void sig_handler(int sig) { + /* Handle SIGINT and SIGTERM signals by setting quit_flag to 1 */ switch (sig) { case SIGINT: @@ -89,8 +98,15 @@ static void sig_handler(int sig) } } +/** + * Prints the usage information for the program. + * + * @param stream The file stream to write the usage information to (e.g., stdout). + * @param exit_code The exit code to return after printing usage. + */ static void print_usage(FILE *stream, int exit_code) { + /* Print the usage message along with available options */ fprintf(stream, "Usage: %s [OPTIONS...] TARGET\n", program_name); fprintf(stream, " OPTIONS\n"); fprintf(stream, " -l, --limit=N percentage of cpu allowed from 0 to %d (required)\n", 100 * NCPU); @@ -106,59 +122,80 @@ static void print_usage(FILE *stream, int exit_code) exit(exit_code); } +/** + * Dynamically calculates the time slot based on system load. + * This allows the program to adapt to varying system conditions. + * + * @return The calculated dynamic time slot in microseconds. + */ static double get_dynamic_time_slot(void) { static double time_slot = TIME_SLOT; - static const double MIN_TIME_SLOT = TIME_SLOT, - MAX_TIME_SLOT = TIME_SLOT * 5; + static const double MIN_TIME_SLOT = TIME_SLOT, /* Minimum allowed time slot */ + MAX_TIME_SLOT = TIME_SLOT * 5; /* Maximum allowed time slot */ double load, new_time_slot; + + /* Get the system load average */ if (getloadavg(&load, 1) != 1) { return time_slot; } + + /* Adjust the time slot based on system load and number of CPUs */ new_time_slot = time_slot * load / NCPU / 0.3; new_time_slot = MIN(MAX(new_time_slot, MIN_TIME_SLOT), MAX_TIME_SLOT); + + /* Smoothly adjust the time slot using a moving average */ time_slot = time_slot * 0.95 + new_time_slot * 0.05; + return time_slot; } +/** + * Controls the CPU usage of a process (and optionally its children). + * Limits the amount of time the process can run based on a given percentage. + * + * @param pid Process ID of the target process. + * @param limit The CPU usage limit as a percentage (0.0 to 1.0). + * @param include_children Whether to include child processes. + */ static void limit_process(pid_t pid, double limit, int include_children) { - /* slice of the slot in which the process is allowed to run */ + /* Slice of time in which the process is allowed to work */ struct timespec twork; - /* slice of the slot in which the process is stopped */ + /* Slice of time in which the process is stopped */ struct timespec tsleep; - /* generic list item */ + /* Generic list item for iterating over processes */ struct list_node *node; - /* counter */ + /* Counter to help with printing status */ int c = 0; - /* rate at which we are keeping active the processes (range 0-1) */ - /* 1 means that the process are using all the twork slice */ + /* The ratio of the time the process is allowed to work (range 0 to 1) */ double workingrate = -1; - /* get a better priority */ + /* Increase priority of the current process to reduce overhead */ increase_priority(); - /* build the family */ + /* Initialize the process group (including children if needed) */ init_process_group(&pgroup, pid, include_children); if (verbose) printf("Members in the process group owned by %ld: %d\n", (long)pgroup.target_pid, pgroup.proclist->count); + /* Main loop to control the process until quit_flag is set */ while (!quit_flag) { - /* total cpu actual usage (range 0-1) */ + /* CPU usage of the controlled processes */ /* 1 means that the processes are using 100% cpu */ double pcpu = -1; - double twork_total_nsec, tsleep_total_nsec; - double time_slot; + /* Update the process group, including checking for dead processes */ update_process_group(&pgroup); + /* Exit if no more processes are running */ if (pgroup.proclist->count == 0) { if (verbose) @@ -166,7 +203,7 @@ static void limit_process(pid_t pid, double limit, int include_children) break; } - /* estimate how much the controlled processes are using the cpu in the working interval */ + /* Estimate CPU usage of all processes in the group */ for (node = pgroup.proclist->first; node != NULL; node = node->next) { const struct process *proc = (const struct process *)(node->data); @@ -179,22 +216,26 @@ static void limit_process(pid_t pid, double limit, int include_children) pcpu += proc->cpu_usage; } - /* adjust work and sleep time slices */ + /* Adjust the work and sleep time slices based on CPU usage */ if (pcpu < 0) { - /* it's the 1st cycle, initialize workingrate */ + /* Initialize workingrate if it's the first cycle */ pcpu = limit; workingrate = limit; } else { - /* adjust workingrate */ + /* Adjust workingrate based on CPU usage and limit */ workingrate = workingrate * limit / MAX(pcpu, EPSILON); } + + /* Clamp workingrate to the valid range (0, 1) */ workingrate = MAX(MIN(workingrate, 1 - EPSILON), EPSILON); + /* Get the dynamic time slot */ time_slot = get_dynamic_time_slot(); + /* Calculate work and sleep times in nanoseconds */ twork_total_nsec = time_slot * 1000 * workingrate; nsec2timespec(twork_total_nsec, &twork); @@ -203,13 +244,14 @@ static void limit_process(pid_t pid, double limit, int include_children) if (verbose) { + /* Print CPU usage statistics every 10 cycles */ if (c % 200 == 0) printf("\n %%CPU work quantum sleep quantum active rate\n"); if (c % 10 == 0 && c > 0) printf("%7.2f%% %9.0f us %10.0f us %10.2f%%\n", pcpu * 100, twork_total_nsec / 1000, tsleep_total_nsec / 1000, workingrate * 100); } - /* resume processes */ + /* Resume processes in the group */ node = pgroup.proclist->first; while (node != NULL) { @@ -217,22 +259,21 @@ static void limit_process(pid_t pid, double limit, int include_children) struct process *proc = (struct process *)(node->data); if (kill(proc->pid, SIGCONT) != 0) { - /* process is dead, remove it from family */ + /* If the process is dead, remove it from the group */ if (verbose) fprintf(stderr, "SIGCONT failed. Process %ld dead!\n", (long)proc->pid); - /* remove process from group */ delete_node(pgroup.proclist, node); remove_process(&pgroup, proc->pid); } node = next_node; } - /* now processes are free to run (same working slice for all) */ + /* Allow processes to run during the work slice */ sleep_timespec(&twork); if (tsleep.tv_nsec > 0 || tsleep.tv_sec > 0) { - /* stop processes only if tsleep>0 */ + /* Stop processes during the sleep slice if needed */ node = pgroup.proclist->first; while (node != NULL) { @@ -240,21 +281,21 @@ static void limit_process(pid_t pid, double limit, int include_children) struct process *proc = (struct process *)(node->data); if (kill(proc->pid, SIGSTOP) != 0) { - /* process is dead, remove it from family */ + /* If the process is dead, remove it from the group */ if (verbose) fprintf(stderr, "SIGSTOP failed. Process %ld dead!\n", (long)proc->pid); - /* remove process from group */ delete_node(pgroup.proclist, node); remove_process(&pgroup, proc->pid); } node = next_node; } - /* now the processes are sleeping */ + /* Allow the processes to sleep during the sleep slice */ sleep_timespec(&tsleep); } c = (c + 1) % 200; } + /* If the quit_flag is set, resume all processes before exiting */ if (quit_flag) { for (node = pgroup.proclist->first; node != NULL; node = node->next) @@ -264,21 +305,26 @@ static void limit_process(pid_t pid, double limit, int include_children) } } + /* Clean up the process group */ close_process_group(&pgroup); } +/** + * Handles the cleanup when a termination signal is received. + * Clears the current line on the console if the quit flag is set. + */ static void quit_handler(void) { + /* If quit_flag is set, clear the current line on console (fix for ^C issue) */ if (quit_flag) { - /* fix ^C little problem */ printf("\r"); } } int main(int argc, char *argv[]) { - /* argument variables */ + /* Variables to store user-provided arguments */ char *exe = NULL; int perclimit = 0; int exe_ok = 0; @@ -288,10 +334,11 @@ int main(int argc, char *argv[]) int include_children = 0; int command_mode; - /* parse arguments */ + /* For parsing command-line options */ int next_option; int option_index = 0; - /* A string listing valid short options letters */ + + /* Define valid short and long command-line options */ const char *short_options = "+p:e:l:vzih"; /* An array describing valid long options */ const struct option long_options[] = { @@ -306,62 +353,79 @@ int main(int argc, char *argv[]) double limit; + /* Set waiting time between process searches */ struct timespec wait_time = {2, 0}; + /* Signal action struct for handling interrupts */ struct sigaction sa; + /* Store the base name of the program */ static char program_base_name[PATH_MAX + 1]; + /* Register the quit handler to run at program exit */ atexit(quit_handler); - /* get program name */ + /* Extract the program name and store it in program_base_name */ strncpy(program_base_name, basename(argv[0]), sizeof(program_base_name) - 1); program_base_name[sizeof(program_base_name) - 1] = '\0'; program_name = program_base_name; - /* get current pid */ + + /* Get the current process ID */ cpulimit_pid = getpid(); - /* get cpu count */ + + /* Get the number of CPUs available */ NCPU = get_ncpu(); + /* Parse the command-line options */ do { next_option = getopt_long(argc, argv, short_options, long_options, &option_index); switch (next_option) { case 'p': + /* Store the PID provided by the user */ pid = (pid_t)atol(optarg); pid_ok = 1; break; case 'e': + /* Store the executable name provided by the user */ exe = optarg; exe_ok = 1; break; case 'l': + /* Store the CPU limit percentage provided by the user */ perclimit = atoi(optarg); limit_ok = 1; break; case 'v': + /* Enable verbose mode */ verbose = 1; break; case 'z': + /* Enable lazy mode */ lazy = 1; break; case 'i': + /* Include child processes in the limit */ include_children = 1; break; case 'h': + /* Print usage information and exit */ print_usage(stdout, 1); break; case '?': + /* Print usage information on invalid option */ print_usage(stderr, 1); break; case -1: + /* No more options to process */ break; default: abort(); } } while (next_option != -1); + /* Validate provided PID */ if (pid_ok && (pid <= 1 || pid >= get_pid_max())) { fprintf(stderr, "Error: Invalid value for argument PID\n"); @@ -370,15 +434,19 @@ int main(int argc, char *argv[]) } if (pid != 0) { + /* Implicitly enable lazy mode if a PID is provided */ lazy = 1; } + /* Ensure that a CPU limit was specified */ if (!limit_ok) { fprintf(stderr, "Error: You must specify a cpu limit percentage\n"); print_usage(stderr, 1); exit(1); } + + /* Calculate the CPU limit as a fraction */ limit = perclimit / 100.0; if (limit < 0 || limit > NCPU) { @@ -387,14 +455,16 @@ int main(int argc, char *argv[]) exit(1); } + /* Determine if a command was provided */ command_mode = optind < argc; + + /* Ensure exactly one target process (pid, executable, or command) is specified */ 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); exit(1); } - if (exe_ok + pid_ok + command_mode > 1) { fprintf(stderr, "Error: You must specify exactly one target process, either by name, pid, or command line\n"); @@ -402,33 +472,37 @@ int main(int argc, char *argv[]) exit(1); } - /* all arguments are ok! */ + /* Set up signal handlers for SIGINT and SIGTERM */ sa.sa_handler = sig_handler; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); - /* print the number of available cpu */ + /* Print number of CPUs if in verbose mode */ if (verbose) printf("%d cpu detected\n", NCPU); + /* Handle command mode (run a command and limit its CPU usage) */ if (command_mode) { int i; pid_t child; - /* executable file */ + /* Executable file */ const char *cmd = argv[optind]; - /* command line arguments */ + /* Command line arguments */ char **cmd_args = (char **)malloc((argc - optind + 1) * sizeof(char *)); if (cmd_args == NULL) exit(2); + + /* Prepare command arguments */ for (i = 0; i < argc - optind; i++) { cmd_args[i] = argv[i + optind]; } cmd_args[i] = NULL; + /* If verbose, print the command being executed */ if (verbose) { printf("Running command: '%s", cmd); @@ -439,6 +513,7 @@ int main(int argc, char *argv[]) printf("'\n"); } + /* Fork a child process to run the command */ child = fork(); if (child < 0) { @@ -446,16 +521,15 @@ int main(int argc, char *argv[]) } else if (child == 0) { - /* target process code */ + /* Execute the command in the child process */ int ret = execvp(cmd, cmd_args); - /* if we are here there was an error, show it */ - perror("Error"); + perror("Error"); /* Display error if execvp fails */ free(cmd_args); exit(ret); } else { - /* parent code */ + /* Parent process forks another limiter process to control CPU usage */ pid_t limiter; free(cmd_args); limiter = fork(); @@ -465,7 +539,7 @@ int main(int argc, char *argv[]) } else if (limiter > 0) { - /* parent */ + /* Wait for both child and limiter processes to complete */ int status_process; int status_limiter; waitpid(child, &status_process, 0); @@ -482,7 +556,7 @@ int main(int argc, char *argv[]) } else { - /* limiter code */ + /* Limiter process controls the CPU usage of the child process */ if (verbose) printf("Limiting process %ld\n", (long)child); limit_process(child, limit, include_children); @@ -491,13 +565,13 @@ int main(int argc, char *argv[]) } } + /* Monitor and limit the target process specified by PID or executable name */ while (!quit_flag) { - /* look for the target process..or wait for it */ pid_t ret = 0; if (pid_ok) { - /* search by pid */ + /* Search for the process by PID */ ret = find_process_by_pid(pid); if (ret == 0) { @@ -510,7 +584,7 @@ int main(int argc, char *argv[]) } else { - /* search by file or path name */ + /* Search for the process by executable name */ ret = find_process_by_name(exe); if (ret == 0) { @@ -525,6 +599,8 @@ int main(int argc, char *argv[]) pid = ret; } } + + /* If a process is found, start limiting its CPU usage */ if (ret > 0) { if (ret == cpulimit_pid) @@ -534,12 +610,14 @@ int main(int argc, char *argv[]) exit(1); } printf("Process %ld found\n", (long)pid); - /* control */ limit_process(pid, limit, include_children); } + + /* Break the loop if lazy mode is enabled or quit flag is set */ if (lazy || quit_flag) break; - /* wait for 2 seconds before next search */ + + /* Wait for 2 seconds before the next process search */ sleep_timespec(&wait_time); } From ddef98a2b490338344d0d9616ac797daf2d1f187 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 16 Sep 2024 16:59:11 +0800 Subject: [PATCH 119/209] get_pid_max: return -1 on unsupported platforms --- src/util.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/util.c b/src/util.c index 9424fc94..906904bf 100644 --- a/src/util.c +++ b/src/util.c @@ -77,5 +77,7 @@ pid_t get_pid_max(void) return (pid_t)99999; #elif defined(__APPLE__) return (pid_t)99998; +#else + return (pid_t)-1; #endif } From 6ed466db5300afdac1f44e987cb4a3af750a381b Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 29 Sep 2024 00:56:45 +0800 Subject: [PATCH 120/209] fix command name comparison --- src/process_group.c | 15 ++------------- src/process_iterator.h | 3 --- src/process_iterator_apple.c | 33 +++++++++++++-------------------- src/process_iterator_freebsd.c | 23 ++++++++++++++--------- src/process_iterator_linux.c | 4 ---- tests/process_iterator_test.c | 4 +--- 6 files changed, 30 insertions(+), 52 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index 910520b8..7fae2c5b 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -61,25 +61,14 @@ pid_t find_process_by_name(char *process_name) struct process_iterator it; struct process proc; struct process_filter filter; - static char process_basename[PATH_MAX + 1]; - static char command_basename[PATH_MAX + 1]; - strncpy(process_basename, basename(process_name), - sizeof(process_basename) - 1); - process_basename[sizeof(process_basename) - 1] = '\0'; + const char *process_basename = basename(process_name); filter.pid = 0; filter.include_children = 0; init_process_iterator(&it, &filter); while (get_next_process(&it, &proc) != -1) { - int cmp_len; - strncpy(command_basename, basename(proc.command), - sizeof(command_basename) - 1); - command_basename[sizeof(command_basename) - 1] = '\0'; - cmp_len = proc.max_cmd_len - - (strlen(proc.command) - strlen(command_basename)); /* process found */ - if (cmp_len > 0 && command_basename[0] != '\0' && - strncmp(command_basename, process_basename, cmp_len) == 0) + if (strcmp(basename(proc.command), process_basename) == 0) { if (pid < 0) { diff --git a/src/process_iterator.h b/src/process_iterator.h index 610de6ba..ba5626bc 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -57,9 +57,6 @@ struct process /* Absolute path of the executable file */ char command[PATH_MAX + 1]; - - /* Maximum command length */ - int max_cmd_len; }; /** diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index be0a789f..2c74d514 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -93,22 +93,14 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi return 0; } -static void pti2proc(struct proc_taskallinfo *ti, struct process *process) +static int pti2proc(struct proc_taskallinfo *ti, struct process *process) { process->pid = ti->pbsd.pbi_pid; process->ppid = ti->pbsd.pbi_ppid; process->cputime = ti->ptinfo.pti_total_user / 1e6 + ti->ptinfo.pti_total_system / 1e6; - if (ti->pbsd.pbi_name[0] != '\0') - { - process->max_cmd_len = MIN(sizeof(process->command), sizeof(ti->pbsd.pbi_name)) - 1; - strncpy(process->command, ti->pbsd.pbi_name, process->max_cmd_len); - } - else - { - process->max_cmd_len = MIN(sizeof(process->command), sizeof(ti->pbsd.pbi_comm)) - 1; - strncpy(process->command, ti->pbsd.pbi_comm, process->max_cmd_len); - } - process->command[process->max_cmd_len] = '\0'; + if (proc_pidpath(ti->pbsd.pbi_pid, process->command, sizeof(process->command)) <= 0) + return -1; + return 0; } static int get_process_pti(pid_t pid, struct proc_taskallinfo *ti) @@ -159,14 +151,13 @@ int get_next_process(struct process_iterator *it, struct process *p) if (it->filter->pid != 0 && !it->filter->include_children) { struct proc_taskallinfo ti; - if (get_process_pti(it->filter->pid, &ti) != 0) + if (get_process_pti(it->filter->pid, &ti) == 0 && pti2proc(&ti, p) == 0) { - it->i = it->count = 0; - return -1; + it->i = it->count = 1; + return 0; } - it->i = it->count = 1; - pti2proc(&ti, p); - return 0; + it->i = it->count = 0; + return -1; } while (it->i < it->count) { @@ -184,7 +175,8 @@ int get_next_process(struct process_iterator *it, struct process *p) if (it->filter->pid != 0 && it->filter->include_children) { it->i++; - pti2proc(&ti, p); + if (pti2proc(&ti, p) != 0) + continue; if (p->pid != it->pidlist[it->i - 1]) /* I don't know why this can happen */ continue; if (p->pid != it->filter->pid && !is_child_of(p->pid, it->filter->pid)) @@ -194,7 +186,8 @@ int get_next_process(struct process_iterator *it, struct process *p) else if (it->filter->pid == 0) { it->i++; - pti2proc(&ti, p); + if (pti2proc(&ti, p) != 0) + continue; return 0; } } diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index 2ec5b3e5..41957450 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -56,21 +56,23 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi return 0; } -static void kproc2proc(kvm_t *kd, struct kinfo_proc *kproc, struct process *proc) +static int kproc2proc(kvm_t *kd, struct kinfo_proc *kproc, struct process *proc) { char **args; + size_t len_max; proc->pid = kproc->ki_pid; proc->ppid = kproc->ki_ppid; proc->cputime = kproc->ki_runtime / 1000.0; - proc->max_cmd_len = sizeof(proc->command) - 1; - if ((args = kvm_getargv(kd, kproc, sizeof(proc->command))) != NULL) + len_max = sizeof(proc->command) - 1; + if ((args = kvm_getargv(kd, kproc, len_max)) != NULL) { - strncpy(proc->command, args[0], proc->max_cmd_len); - proc->command[proc->max_cmd_len] = '\0'; + strncpy(proc->command, args[0], len_max); + proc->command[len_max] = '\0'; + return 0; } else { - proc->command[0] = '\0'; + return -1; } } @@ -82,7 +84,8 @@ static int get_single_process(kvm_t *kd, pid_t pid, struct process *process) { return -1; } - kproc2proc(kd, kproc, process); + if (kproc2proc(kd, kproc, process) != 0) + return -1; return 0; } @@ -162,7 +165,8 @@ int get_next_process(struct process_iterator *it, struct process *p) if (it->filter->pid != 0 && it->filter->include_children) { it->i++; - kproc2proc(it->kd, kproc, p); + if (kproc2proc(it->kd, kproc, p) != 0) + continue; if (p->pid != it->filter->pid && !_is_child_of(it->kd, p->pid, it->filter->pid)) continue; @@ -171,7 +175,8 @@ int get_next_process(struct process_iterator *it, struct process *p) else if (it->filter->pid == 0) { it->i++; - kproc2proc(it->kd, kproc, p); + if (kproc2proc(it->kd, kproc, p) != 0) + continue; return 0; } } diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index b6741312..3841eaf8 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -82,10 +82,6 @@ static int read_process_info(pid_t pid, struct process *p) { ret = -1; } - else - { - p->max_cmd_len = sizeof(p->command) - 1; - } fclose(fd); } else diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index 569ab8f1..a496374f 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -203,7 +203,6 @@ static void test_process_name(void) struct process_filter filter; static char command_basename[PATH_MAX + 1]; static char process_basename[PATH_MAX + 1]; - int cmp_len; filter.pid = getpid(); filter.include_children = 0; init_process_iterator(&it, &filter); @@ -214,8 +213,7 @@ static void test_process_name(void) command_basename[sizeof(command_basename) - 1] = '\0'; strncpy(process_basename, basename(process.command), sizeof(process_basename) - 1); process_basename[sizeof(process_basename) - 1] = '\0'; - cmp_len = process.max_cmd_len - (strlen(process.command) - strlen(process_basename)); - assert(strncmp(command_basename, process_basename, cmp_len) == 0); + assert(strcmp(command_basename, process_basename) == 0); assert(get_next_process(&it, &process) != 0); close_process_iterator(&it); } From e89405748d414c60134f07b50dc1af36bda1fd40 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 29 Sep 2024 23:33:49 +0800 Subject: [PATCH 121/209] update CI --- .github/workflows/CI.yml | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 2911ee81..2206bacd 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -28,21 +28,25 @@ jobs: ref: master fetch-depth: 0 - - name: Compile dynamic + - name: Compile in macOS if: ${{ startsWith( matrix.os, 'macos-' ) }} run: | make sudo ./tests/process_iterator_test - cp ./tests/process_iterator_test ./tests/abcdefghijklmnopqrstuvwxyzabcdefghi - sudo ./tests/abcdefghijklmnopqrstuvwxyzabcdefghi + random_file="$(mktemp $(printf 'X%.0s' $(seq 1 255)))" + cp ./tests/process_iterator_test ./tests/${random_file} + echo "./tests/${random_file}" + sudo ./tests/${random_file} - - name: Compile static + - name: Compile in Ubuntu if: ${{ startsWith( matrix.os, 'ubuntu-' ) }} run: | make LDFLAGS="-static" sudo ./tests/process_iterator_test - cp ./tests/process_iterator_test ./tests/abcdefghijklmnopqrstuvwxyzabcdefghi - sudo ./tests/abcdefghijklmnopqrstuvwxyzabcdefghi + random_file="$(mktemp $(printf 'X%.0s' $(seq 1 255)))" + cp ./tests/process_iterator_test ./tests/${random_file} + echo "./tests/${random_file}" + sudo ./tests/${random_file} - name: Upload uses: actions/upload-artifact@main @@ -72,8 +76,10 @@ jobs: run: | gmake sudo ./tests/process_iterator_test - cp ./tests/process_iterator_test ./tests/abcdefghijklmnopqrstuvwxyzabcdefghi - sudo ./tests/abcdefghijklmnopqrstuvwxyzabcdefghi + random_file="$(mktemp $(printf 'X%.0s' $(seq 1 255)))" + cp ./tests/process_iterator_test ./tests/${random_file} + echo "./tests/${random_file}" + sudo ./tests/${random_file} - name: Upload uses: actions/upload-artifact@main From 17182008a99e32f301ddff41563aec8bdc55e191 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 3 Oct 2024 01:23:18 +0800 Subject: [PATCH 122/209] improve is_child_of for MacOS --- src/process_iterator_apple.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 2c74d514..b97675ec 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -135,8 +135,10 @@ pid_t getppid_of(pid_t pid) int is_child_of(pid_t child_pid, pid_t parent_pid) { - if (child_pid <= 0 || parent_pid <= 0 || child_pid == parent_pid) + if (child_pid <= 1 || parent_pid <= 0 || child_pid == parent_pid) return 0; + if (parent_pid == 1) + return 1; while (child_pid > 1 && child_pid != parent_pid) { child_pid = getppid_of(child_pid); From 6196303f61606c743683fd79e5060e939f8ad3fd Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 3 Oct 2024 12:33:13 +0800 Subject: [PATCH 123/209] add multi_process_busy.c --- tests/Makefile | 5 ++++- tests/multi_process_busy.c | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 tests/multi_process_busy.c diff --git a/tests/Makefile b/tests/Makefile index 5129c1a1..c846aa01 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -4,7 +4,7 @@ override CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ -Wall -Wextra -pedantic \ -Wmissing-prototypes -Wstrict-prototypes \ -Wold-style-definition -TARGETS = busy process_iterator_test +TARGETS = busy multi_process_busy process_iterator_test SRC = ../src SYSLIBS ?= -lpthread $(SRC)/util.c LIBS := $(SRC)/list.c $(SRC)/process_iterator.c $(SRC)/process_group.c \ @@ -26,6 +26,9 @@ all: $(TARGETS) busy: busy.c $(CC) $(CFLAGS) $^ $(SYSLIBS) $(LDFLAGS) -o $@ +multi_process_busy: multi_process_busy.c + $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ + process_iterator_test: process_iterator_test.c $(LIBS) $(CC) $(CFLAGS) -I$(SRC) $^ $(LDFLAGS) -o $@ diff --git a/tests/multi_process_busy.c b/tests/multi_process_busy.c new file mode 100644 index 00000000..00bedaa2 --- /dev/null +++ b/tests/multi_process_busy.c @@ -0,0 +1,10 @@ +#include + +int main(void) +{ + fork(); + fork(); + while (1) + ; + return 0; +} From 338453c3c4eaf87917971b2b5385c3c00737ed8a Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 4 Oct 2024 00:53:36 +0800 Subject: [PATCH 124/209] use NULL instead of EMPTYLIST --- src/list.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/list.c b/src/list.c index f2b1e3fc..c6e180a8 100644 --- a/src/list.c +++ b/src/list.c @@ -34,8 +34,6 @@ } \ } while (0) -#define EMPTYLIST NULL - void init_list(struct list *l, int keysize) { l->first = l->last = NULL; @@ -137,7 +135,7 @@ struct list_node *xlocate_node(struct list *l, const void *elem, int offset, int return (tmp); tmp = tmp->next; } - return EMPTYLIST; + return NULL; } struct list_node *locate_node(struct list *l, const void *elem) @@ -158,20 +156,20 @@ void *locate_elem(struct list *l, const void *elem) void clear_list(struct list *l) { - while (l->first != EMPTYLIST) + while (l->first != NULL) { struct list_node *tmp; tmp = l->first; l->first = l->first->next; safe_free(tmp); } - l->last = EMPTYLIST; + l->last = NULL; l->count = 0; } void destroy_list(struct list *l) { - while (l->first != EMPTYLIST) + while (l->first != NULL) { struct list_node *tmp; tmp = l->first; @@ -179,6 +177,6 @@ void destroy_list(struct list *l) safe_free(tmp->data); safe_free(tmp); } - l->last = EMPTYLIST; + l->last = NULL; l->count = 0; } From 1c0efa34ab8f11c123ef9d9c0e0ecd42c4a97877 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 4 Oct 2024 01:53:39 +0800 Subject: [PATCH 125/209] fix max path length --- src/cpulimit.c | 2 +- src/process_group.c | 2 +- src/process_iterator.h | 2 +- tests/process_iterator_test.c | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 937d5a44..779e8d49 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -360,7 +360,7 @@ int main(int argc, char *argv[]) struct sigaction sa; /* Store the base name of the program */ - static char program_base_name[PATH_MAX + 1]; + static char program_base_name[PATH_MAX]; /* Register the quit handler to run at program exit */ atexit(quit_handler); diff --git a/src/process_group.c b/src/process_group.c index 7fae2c5b..a08c203a 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -68,7 +68,7 @@ pid_t find_process_by_name(char *process_name) while (get_next_process(&it, &proc) != -1) { /* process found */ - if (strcmp(basename(proc.command), process_basename) == 0) + if (strncmp(basename(proc.command), process_basename, sizeof(proc.command)) == 0) { if (pid < 0) { diff --git a/src/process_iterator.h b/src/process_iterator.h index ba5626bc..1a03d681 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -56,7 +56,7 @@ struct process double cpu_usage; /* Absolute path of the executable file */ - char command[PATH_MAX + 1]; + char command[PATH_MAX]; }; /** diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index a496374f..40f595e4 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -201,8 +201,8 @@ static void test_process_name(void) struct process_iterator it; struct process process; struct process_filter filter; - static char command_basename[PATH_MAX + 1]; - static char process_basename[PATH_MAX + 1]; + static char command_basename[PATH_MAX]; + static char process_basename[PATH_MAX]; filter.pid = getpid(); filter.include_children = 0; init_process_iterator(&it, &filter); @@ -213,7 +213,7 @@ static void test_process_name(void) command_basename[sizeof(command_basename) - 1] = '\0'; strncpy(process_basename, basename(process.command), sizeof(process_basename) - 1); process_basename[sizeof(process_basename) - 1] = '\0'; - assert(strcmp(command_basename, process_basename) == 0); + assert(strncmp(command_basename, process_basename, sizeof(command_basename)) == 0); assert(get_next_process(&it, &process) != 0); close_process_iterator(&it); } From 3d4528c705d4bf0cc10d68b7723bbcc51225c004 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 5 Oct 2024 23:59:21 +0800 Subject: [PATCH 126/209] Utilize a pointer to a constant as the parameter for the process_dup function --- src/process_group.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/process_group.c b/src/process_group.c index a08c203a..7095f2b6 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -137,7 +137,7 @@ int close_process_group(struct process_group *pgroup) return 0; } -static struct process *process_dup(struct process *proc) +static struct process *process_dup(const struct process *proc) { struct process *p = (struct process *)malloc(sizeof(struct process)); if (p == NULL) From 06eaf4d3097f8172d82556f9f83849dd701d256c Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 6 Oct 2024 00:00:58 +0800 Subject: [PATCH 127/209] use explicit address of loop function --- tests/busy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/busy.c b/tests/busy.c index 34b54d69..e0c82c28 100644 --- a/tests/busy.c +++ b/tests/busy.c @@ -30,7 +30,7 @@ int main(int argc, char *argv[]) { pthread_t thread; int ret; - if ((ret = pthread_create(&thread, NULL, loop, NULL)) != 0) + if ((ret = pthread_create(&thread, NULL, &loop, NULL)) != 0) { printf("pthread_create() failed. Error code %d\n", ret); exit(1); From 814aa8aeb8e6c63edd1935284fd39da53605bd20 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 6 Oct 2024 00:36:57 +0800 Subject: [PATCH 128/209] Refactor __basename to eliminate the cppcheck warning --- src/util.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/util.h b/src/util.h index d92ed6de..dea9e183 100644 --- a/src/util.h +++ b/src/util.h @@ -87,14 +87,15 @@ /** * Helper macro to get the basename of a path */ -#define __basename(path, full_path, last_slash) \ +#define __basename(full_path, last_slash, path) \ (__extension__({ \ - const char *full_path = (path); \ - const char *last_slash = strrchr(full_path, '/'); \ + const char *full_path, *last_slash; \ + full_path = (path); \ + last_slash = strrchr(full_path, '/'); \ (last_slash != NULL) ? (last_slash + 1) : full_path; \ })) #define basename(path) \ - __basename((path), __UNIQUE_ID(full_path_), __UNIQUE_ID(last_slash_)) + __basename(__UNIQUE_ID(full_path_), __UNIQUE_ID(last_slash_), path) #else /** * Get the basename of a path From 723057445c7368e5fbc80b94e4457308d5d06c99 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 6 Oct 2024 00:38:44 +0800 Subject: [PATCH 129/209] add multi_process_busy to .gitignore --- tests/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/.gitignore b/tests/.gitignore index 0e9d6bed..032dbfeb 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,4 +1,5 @@ *.o *~ busy +multi_process_busy process_iterator_test From a78f65680a22fe002f107c731807983af7f70cbe Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 6 Oct 2024 00:41:03 +0800 Subject: [PATCH 130/209] Utilize explicit function pointers --- src/cpulimit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 779e8d49..817166ae 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -363,7 +363,7 @@ int main(int argc, char *argv[]) static char program_base_name[PATH_MAX]; /* Register the quit handler to run at program exit */ - atexit(quit_handler); + atexit(&quit_handler); /* Extract the program name and store it in program_base_name */ strncpy(program_base_name, basename(argv[0]), sizeof(program_base_name) - 1); @@ -473,7 +473,7 @@ int main(int argc, char *argv[]) } /* Set up signal handlers for SIGINT and SIGTERM */ - sa.sa_handler = sig_handler; + sa.sa_handler = &sig_handler; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL); From 5d93358d1d6db1ce09a65692e345ce8b0e277c58 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 6 Oct 2024 00:44:30 +0800 Subject: [PATCH 131/209] Resolve the warning regarding the unused return value of the atexit() function --- src/cpulimit.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 817166ae..5fcc8fec 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -363,7 +363,11 @@ int main(int argc, char *argv[]) static char program_base_name[PATH_MAX]; /* Register the quit handler to run at program exit */ - atexit(&quit_handler); + if (atexit(quit_handler) != 0) + { + fprintf(stderr, "Failed to register quit_handler\n"); + exit(1); + } /* Extract the program name and store it in program_base_name */ strncpy(program_base_name, basename(argv[0]), sizeof(program_base_name) - 1); From fb2447575a22bcd7ec31295c6c87e73aaa5cd54b Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 6 Oct 2024 00:48:39 +0800 Subject: [PATCH 132/209] Move the #include "util.h" directive to the end --- src/process_group.c | 2 +- src/util.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index 7095f2b6..d19e39d3 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -34,8 +34,8 @@ #include "process_iterator.h" #include "process_group.h" #include "list.h" -#include "util.h" #include "process_table.h" +#include "util.h" /* look for a process by pid search_pid : pid of the wanted process diff --git a/src/util.c b/src/util.c index 906904bf..dcf95686 100644 --- a/src/util.c +++ b/src/util.c @@ -2,10 +2,10 @@ #define _GNU_SOURCE #endif -#include "util.h" #include #include #include +#include "util.h" #ifdef __IMPL_BASENAME const char *__basename(const char *path) From bce0cc2662a344b513db88f8ed785b60d281eeb7 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 6 Oct 2024 00:55:35 +0800 Subject: [PATCH 133/209] Include the required header in util.h --- src/util.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util.h b/src/util.h index dea9e183..d2eb325e 100644 --- a/src/util.h +++ b/src/util.h @@ -7,6 +7,7 @@ #include #include +#include #include #include From 70f54f8efc255c6afba6ece8c5ebbcf063274938 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 6 Oct 2024 01:08:17 +0800 Subject: [PATCH 134/209] update CI --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 2206bacd..3a16b7ae 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -18,7 +18,7 @@ jobs: contents: read strategy: matrix: - os: [ubuntu-20.04, ubuntu-22.04, ubuntu-24.04, macos-12, macos-13, macos-14] + os: [ubuntu-20.04, ubuntu-22.04, ubuntu-24.04, macos-13, macos-14, macos-15] runs-on: ${{ matrix.os }} steps: @@ -60,7 +60,7 @@ jobs: contents: read strategy: matrix: - osver: ['13.2', '14.0'] + osver: ['13.4', '14.1'] runs-on: ubuntu-latest steps: - name: Checkout From 12df0da7954e27c7a6efc71c68609ac2b302ba4e Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 6 Oct 2024 02:32:31 +0800 Subject: [PATCH 135/209] Refactor the read_process_info function to enhance readability --- src/process_iterator_linux.c | 53 ++++++++++++++---------------------- 1 file changed, 20 insertions(+), 33 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 3841eaf8..d33c826f 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -69,58 +69,45 @@ static int read_process_info(pid_t pid, struct process *p) double utime, stime; long ppid; FILE *fd; - int ret = 0; static double sc_clk_tck = -1.0; p->pid = pid; /* read command line */ sprintf(exefile, "/proc/%ld/cmdline", (long)p->pid); - if ((fd = fopen(exefile, "r")) != NULL) + if ((fd = fopen(exefile, "r")) == NULL) { - if (fgets(p->command, sizeof(p->command), fd) == NULL) - { - ret = -1; - } - fclose(fd); - } - else - { - ret = -1; + return -1; } - - if (ret != 0) + if (fgets(p->command, sizeof(p->command), fd) == NULL) { - return ret; + fclose(fd); + return -1; } + fclose(fd); /* read stat file */ sprintf(statfile, "/proc/%ld/stat", (long)p->pid); - if ((fd = fopen(statfile, "r")) != NULL) + if ((fd = fopen(statfile, "r")) == NULL) + { + return -1; + } + if (fscanf(fd, "%*d (%*[^)]) %c %ld %*d %*d %*d %*d %*d %*d %*d %*d %*d %lf %lf", + &state, &ppid, &utime, &stime) != 4 || + strchr("ZXx", state) != NULL) { - if (fscanf(fd, "%*d (%*[^)]) %c %ld %*d %*d %*d %*d %*d %*d %*d %*d %*d %lf %lf", - &state, &ppid, &utime, &stime) != 4 || - strchr("ZXx", state) != NULL) - { - ret = -1; - } - else - { - p->ppid = (pid_t)ppid; - if (sc_clk_tck < 0) - { - sc_clk_tck = (double)sysconf(_SC_CLK_TCK); - } - p->cputime = (utime + stime) * 1000.0 / sc_clk_tck; - } fclose(fd); + return -1; } - else + fclose(fd); + p->ppid = (pid_t)ppid; + if (sc_clk_tck < 0) { - ret = -1; + sc_clk_tck = (double)sysconf(_SC_CLK_TCK); } + p->cputime = (utime + stime) * 1000.0 / sc_clk_tck; - return ret; + return 0; } pid_t getppid_of(pid_t pid) From f4992316d072041a52b26484f8d39b3b0c6abe68 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 6 Oct 2024 02:46:32 +0800 Subject: [PATCH 136/209] Enhance the implementation of the is_numeric function --- src/process_iterator_linux.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index d33c826f..a79ede7e 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -171,9 +171,14 @@ int is_child_of(pid_t child_pid, pid_t parent_pid) static int is_numeric(const char *str) { - for (; *str != '\0' && isdigit(*str); str++) - ; - return *str == '\0'; + if (str == NULL || *str == '\0') + return 0; + for (; *str != '\0'; str++) + { + if (!isdigit(*str)) + return 0; + } + return 1; } int get_next_process(struct process_iterator *it, struct process *p) From c5e3deb9ffd3595da98c3dd93873f815b5014d5d Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 6 Oct 2024 02:53:51 +0800 Subject: [PATCH 137/209] Simplify the check_proc function --- src/process_iterator_linux.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index a79ede7e..246a568b 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -39,11 +39,7 @@ static int check_proc(void) { struct statfs mnt; - if (statfs("/proc", &mnt) < 0) - return 0; - if (mnt.f_type != PROC_SUPER_MAGIC) - return 0; - return 1; + return statfs("/proc", &mnt) == 0 && mnt.f_type == PROC_SUPER_MAGIC; } int init_process_iterator(struct process_iterator *it, struct process_filter *filter) From aeefc68d9ec83ce1b3e0d22d52078d598b816b2b Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 6 Oct 2024 23:07:03 +0800 Subject: [PATCH 138/209] Refine getppid_of function for Linux --- src/process_iterator_linux.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 246a568b..944efb60 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -110,16 +110,15 @@ pid_t getppid_of(pid_t pid) { char statfile[32]; FILE *fd; - long ppid = -1; + long ppid; if (pid <= 0) return (pid_t)(-1); sprintf(statfile, "/proc/%ld/stat", (long)pid); - if ((fd = fopen(statfile, "r")) != NULL) - { - if (fscanf(fd, "%*d (%*[^)]) %*c %ld", &ppid) != 1) - ppid = -1; - fclose(fd); - } + if ((fd = fopen(statfile, "r")) == NULL) + return (pid_t)(-1); + if (fscanf(fd, "%*d (%*[^)]) %*c %ld", &ppid) != 1) + ppid = -1; + fclose(fd); return (pid_t)ppid; } From e84fbc6d899c72e90e51959cb6721b46e5a16c9a Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 6 Oct 2024 23:15:38 +0800 Subject: [PATCH 139/209] Refine get_start_time function for Linux --- src/process_iterator_linux.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 944efb60..1b292776 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -127,9 +127,10 @@ static int get_start_time(pid_t pid, struct timespec *start_time) struct stat procfs_stat; char procfs_path[32]; int ret; + if (start_time == NULL) + return -1; sprintf(procfs_path, "/proc/%ld", (long)pid); - ret = stat(procfs_path, &procfs_stat); - if (ret == 0 && start_time != NULL) + if ((ret = stat(procfs_path, &procfs_stat)) == 0) *start_time = procfs_stat.st_ctim; return ret; } From 60acf9d30fe74bef11cda54adc58b48cfcde40f6 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 6 Oct 2024 23:30:34 +0800 Subject: [PATCH 140/209] Simplify get_next_process for Linux --- src/process_iterator_linux.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 1b292776..b623907a 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -191,9 +191,7 @@ int get_next_process(struct process_iterator *it, struct process *p) int ret = read_process_info(it->filter->pid, p); closedir(it->dip); it->dip = NULL; - if (ret != 0) - return -1; - return 0; + return ret == 0 ? 0 : -1; } /* read in from /proc and seek for process dirs */ From e5d9d86e8a44dd62ab36275e170cbab2ee30a02a Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 6 Oct 2024 23:34:28 +0800 Subject: [PATCH 141/209] Address potential NULL pointer dereference in the close_process_iterator function for Linux --- src/process_iterator_linux.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index b623907a..c6c73574 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -220,12 +220,19 @@ int get_next_process(struct process_iterator *it, struct process *p) int close_process_iterator(struct process_iterator *it) { - if (it->dip != NULL && closedir(it->dip) == -1) + if (it == NULL) + return -1; /* Invalid argument */ + + if (it->dip != NULL) { - perror("closedir"); - return 1; + if (closedir(it->dip) == -1) + { + perror("closedir"); + return 1; /* Error while closing directory */ + } + it->dip = NULL; } - it->dip = NULL; + return 0; } From 0598038cccd25c8079c7859e3214d9d3fcc98877 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 7 Oct 2024 00:01:43 +0800 Subject: [PATCH 142/209] Refine kproc2proc function for FreeBSD --- src/process_iterator_freebsd.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index 41957450..e3f6b7b8 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -64,16 +64,11 @@ static int kproc2proc(kvm_t *kd, struct kinfo_proc *kproc, struct process *proc) proc->ppid = kproc->ki_ppid; proc->cputime = kproc->ki_runtime / 1000.0; len_max = sizeof(proc->command) - 1; - if ((args = kvm_getargv(kd, kproc, len_max)) != NULL) - { - strncpy(proc->command, args[0], len_max); - proc->command[len_max] = '\0'; - return 0; - } - else - { + if ((args = kvm_getargv(kd, kproc, len_max)) == NULL) return -1; - } + strncpy(proc->command, args[0], len_max); + proc->command[len_max] = '\0'; + return 0; } static int get_single_process(kvm_t *kd, pid_t pid, struct process *process) From 2c900595cc3d04bd239f664a2039e4da50df1fa5 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 7 Oct 2024 01:31:42 +0800 Subject: [PATCH 143/209] Simplify get_single_process for FreeBSD --- src/process_iterator_freebsd.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index e3f6b7b8..22070037 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -75,11 +75,7 @@ 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) - { - return -1; - } - if (kproc2proc(kd, kproc, process) != 0) + if (count == 0 || kproc == NULL || kproc2proc(kd, kproc, process) != 0) return -1; return 0; } From bc6e1e8502b9c4158664740c9c84835e2153ff7c Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 7 Oct 2024 09:39:21 +0800 Subject: [PATCH 144/209] Fix return value of close_process_iterator --- src/process_iterator.h | 2 +- src/process_iterator_linux.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/process_iterator.h b/src/process_iterator.h index 1a03d681..2e57f69b 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -130,7 +130,7 @@ int get_next_process(struct process_iterator *it, struct process *p); * Closes the process iterator. * * @param it Pointer to the process_iterator structure. - * @return 0 on success, 1 on failure. + * @return 0 on success, -1 on failure. */ int close_process_iterator(struct process_iterator *it); diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index c6c73574..0167405d 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -220,20 +220,20 @@ int get_next_process(struct process_iterator *it, struct process *p) int close_process_iterator(struct process_iterator *it) { + int ret = 0; if (it == NULL) return -1; /* Invalid argument */ if (it->dip != NULL) { - if (closedir(it->dip) == -1) + if ((ret = closedir(it->dip)) != 0) { perror("closedir"); - return 1; /* Error while closing directory */ } it->dip = NULL; } - return 0; + return ret == 0 ? 0 : -1; } #endif From 8acebed6bd194233c75ba46244e4c955ec8a64b0 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 7 Oct 2024 19:16:13 +0800 Subject: [PATCH 145/209] Simplify find_process_by_name function --- src/process_group.c | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index d19e39d3..59bb16b6 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -70,35 +70,16 @@ pid_t find_process_by_name(char *process_name) /* process found */ if (strncmp(basename(proc.command), process_basename, sizeof(proc.command)) == 0) { - if (pid < 0) + if (pid < 0 || is_child_of(pid, proc.pid)) { pid = proc.pid; } - else if (is_child_of(pid, proc.pid)) - { - pid = proc.pid; - } - else if (is_child_of(proc.pid, pid)) - { - } - else - { - pid = MIN(proc.pid, pid); - } } } if (close_process_iterator(&it) != 0) exit(1); - if (pid > 0) - { - /* the process was found */ - return find_process_by_pid(pid); - } - else - { - /* process not found */ - return 0; - } + + return (pid > 0) ? find_process_by_pid(pid) : 0; } int init_process_group(struct process_group *pgroup, pid_t target_pid, int include_children) From cc176daef6aa806e7588d24651aa798f69250240 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 7 Oct 2024 19:20:11 +0800 Subject: [PATCH 146/209] Refine close_process_group function --- src/process_group.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index 59bb16b6..78deecb8 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -109,12 +109,20 @@ int init_process_group(struct process_group *pgroup, pid_t target_pid, int inclu int close_process_group(struct process_group *pgroup) { - clear_list(pgroup->proclist); - free(pgroup->proclist); - pgroup->proclist = NULL; - process_table_destroy(pgroup->proctable); - free(pgroup->proctable); - pgroup->proctable = NULL; + if (pgroup->proclist != NULL) + { + clear_list(pgroup->proclist); + free(pgroup->proclist); + pgroup->proclist = NULL; + } + + if (pgroup->proctable != NULL) + { + process_table_destroy(pgroup->proctable); + free(pgroup->proctable); + pgroup->proctable = NULL; + } + return 0; } From 257cf5b5a0937948887061e34bc4c59078739476 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 11 Oct 2024 01:57:01 +0800 Subject: [PATCH 147/209] Reorganize header includes --- src/cpulimit.c | 4 +++- src/process_group.c | 3 +-- src/process_group.h | 2 -- src/process_iterator.h | 10 ++-------- src/process_iterator_apple.c | 3 +++ src/process_iterator_freebsd.c | 10 +++++++--- src/process_iterator_linux.c | 13 +++++++++---- src/process_table.c | 1 + src/process_table.h | 1 + src/util.c | 1 + tests/busy.c | 3 --- tests/process_iterator_test.c | 15 ++++++++------- 12 files changed, 36 insertions(+), 30 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 5fcc8fec..9b43c592 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -37,8 +37,10 @@ #include #include #include -#include +#include #include +#include +#include #include "process_group.h" #include "list.h" diff --git a/src/process_group.c b/src/process_group.c index 78deecb8..44aa78db 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -25,11 +25,10 @@ #include #include -#include #include -#include #include #include +#include #include "process_iterator.h" #include "process_group.h" diff --git a/src/process_group.h b/src/process_group.h index 19a02112..9d2bb7a1 100644 --- a/src/process_group.h +++ b/src/process_group.h @@ -27,10 +27,8 @@ #endif #include -#include #include "process_iterator.h" - #include "list.h" /** diff --git a/src/process_iterator.h b/src/process_iterator.h index 2e57f69b..649ca331 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -23,17 +23,11 @@ #define __PROCESS_ITERATOR_H #include -#include -#include -#include -#include -#include #include -#if defined(__linux__) +#ifdef __linux__ +#include #include #endif -#include - #ifdef __FreeBSD__ #include #endif diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index b97675ec..e877fa14 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -29,6 +29,9 @@ #include #include +#include +#include +#include #include "process_iterator.h" static int unique_nonzero_pids(pid_t *arr_in, int len_in, pid_t *arr_out) diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index 22070037..c9e9a33b 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -28,13 +28,17 @@ #define inline #endif -#include "process_iterator.h" -#include -#include #include #include #include +#include +#include #include +#include +#include +#include + +#include "process_iterator.h" int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 0167405d..23247ba9 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -28,13 +28,18 @@ #define _GNU_SOURCE #endif +#include +#include +#include +#include +#include +#include #include #include -#include "process_iterator.h" -#include -#include -#include #include +#include + +#include "process_iterator.h" static int check_proc(void) { diff --git a/src/process_table.c b/src/process_table.c index 2431a60f..d3dcded9 100644 --- a/src/process_table.c +++ b/src/process_table.c @@ -1,5 +1,6 @@ #include #include +#include #include "process_table.h" void process_table_init(struct process_table *pt, int hashsize) diff --git a/src/process_table.h b/src/process_table.h index ecb6732a..0cc8d201 100644 --- a/src/process_table.h +++ b/src/process_table.h @@ -1,6 +1,7 @@ #ifndef __PROCESS_TABLE_H #define __PROCESS_TABLE_H +#include #include "process_iterator.h" #include "list.h" diff --git a/src/util.c b/src/util.c index dcf95686..cbc2c1a4 100644 --- a/src/util.c +++ b/src/util.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "util.h" #ifdef __IMPL_BASENAME diff --git a/tests/busy.c b/tests/busy.c index e0c82c28..3093e26e 100644 --- a/tests/busy.c +++ b/tests/busy.c @@ -1,9 +1,6 @@ #include #include #include -#include -#include -#include #include "../src/util.h" #ifndef __GNUC__ diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index 40f595e4..8b1411e7 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -23,16 +23,17 @@ #define _GNU_SOURCE #endif -#include -#include -#include -#include #include -#include #include +#include +#include #include -#include -#include +#include +#include +#include +#ifdef __linux__ +#include +#endif #include "../src/process_iterator.h" #include "../src/process_group.h" From e8c8b854f4f93fd9fc3115e4779a78e80249ecb5 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 11 Oct 2024 21:39:54 +0800 Subject: [PATCH 148/209] Limit CPU usage within the range of 0 to 1 --- src/process_group.c | 1 + tests/process_iterator_test.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/process_group.c b/src/process_group.c index 44aa78db..f28b0d53 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -177,6 +177,7 @@ void update_process_group(struct process_group *pgroup) continue; /* process exists. update CPU usage */ sample = (tmp_process.cputime - p->cputime) / dt; + sample = MIN(sample, 1.0); if (p->cpu_usage < 0) { /* initialization */ diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index 8b1411e7..c92f8f9e 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -183,7 +183,7 @@ static void test_process_group_single(int include_children) assert(p->ppid == getpid()); /* p->cpu_usage should be -1 or [0, 1] */ assert((p->cpu_usage >= (-1.00001) && p->cpu_usage <= (-0.99999)) || - (p->cpu_usage >= 0 && p->cpu_usage <= 1.05)); + (p->cpu_usage >= 0 && p->cpu_usage <= 1.0)); count++; } assert(count == 1); From a65f116960c159487fc81b8c8feabb23e8559033 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 11 Oct 2024 22:17:16 +0800 Subject: [PATCH 149/209] Refine Makefile --- tests/Makefile | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index c846aa01..1e62c3e9 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -4,11 +4,8 @@ override CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ -Wall -Wextra -pedantic \ -Wmissing-prototypes -Wstrict-prototypes \ -Wold-style-definition -TARGETS = busy multi_process_busy process_iterator_test +TARGETS := $(patsubst %.c,%,$(wildcard *.c)) SRC = ../src -SYSLIBS ?= -lpthread $(SRC)/util.c -LIBS := $(SRC)/list.c $(SRC)/process_iterator.c $(SRC)/process_group.c \ - $(SRC)/process_table.c $(SRC)/util.c UNAME ?= $(shell uname) ifeq ($(UNAME), FreeBSD) @@ -23,14 +20,14 @@ endif all: $(TARGETS) -busy: busy.c - $(CC) $(CFLAGS) $^ $(SYSLIBS) $(LDFLAGS) -o $@ +busy: busy.c $(wildcard $(SRC)/util.*) + $(CC) $(CFLAGS) $(filter-out %.h, $^) -lpthread $(LDFLAGS) -o $@ multi_process_busy: multi_process_busy.c $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ -process_iterator_test: process_iterator_test.c $(LIBS) - $(CC) $(CFLAGS) -I$(SRC) $^ $(LDFLAGS) -o $@ +process_iterator_test: process_iterator_test.c $(filter-out $(SRC)/cpulimit.c, $(wildcard $(SRC)/*.c $(SRC)/*.h)) + $(CC) $(CFLAGS) $(filter-out $(SRC)/process_iterator_%.c %.h, $^) $(LDFLAGS) -o $@ clean: rm -f *~ $(TARGETS) From 89a8f1f2a4cf0796133d5f3d6db431fe05ee63f1 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 12 Oct 2024 22:19:33 +0800 Subject: [PATCH 150/209] Use dynamically allocated memory to store the process structure To avoid stack overflow. --- src/process_group.c | 34 ++++++++++------ src/process_table.c | 27 ++++++++++--- tests/process_iterator_test.c | 75 +++++++++++++++++++++-------------- 3 files changed, 88 insertions(+), 48 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index f28b0d53..11ace985 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -58,23 +58,27 @@ pid_t find_process_by_name(char *process_name) /* process iterator */ struct process_iterator it; - struct process proc; + struct process *proc; struct process_filter filter; const char *process_basename = basename(process_name); + proc = (struct process *)malloc(sizeof(struct process)); + if (proc == NULL) + exit(1); filter.pid = 0; filter.include_children = 0; init_process_iterator(&it, &filter); - while (get_next_process(&it, &proc) != -1) + while (get_next_process(&it, proc) != -1) { /* process found */ - if (strncmp(basename(proc.command), process_basename, sizeof(proc.command)) == 0) + if (strncmp(basename(proc->command), process_basename, sizeof(proc->command)) == 0) { - if (pid < 0 || is_child_of(pid, proc.pid)) + if (pid < 0 || is_child_of(pid, proc->pid)) { - pid = proc.pid; + pid = proc->pid; } } } + free(proc); if (close_process_iterator(&it) != 0) exit(1); @@ -142,7 +146,7 @@ static struct process *process_dup(const struct process *proc) void update_process_group(struct process_group *pgroup) { struct process_iterator it; - struct process tmp_process, *p; + struct process *tmp_process, *p; struct process_filter filter; struct timespec now; double dt; @@ -150,6 +154,11 @@ void update_process_group(struct process_group *pgroup) { exit(1); } + tmp_process = (struct process *)malloc(sizeof(struct process)); + if (tmp_process == NULL) + { + exit(1); + } /* time elapsed from previous sample (in ms) */ dt = timediff_in_ms(&now, &pgroup->last_update); filter.pid = pgroup->target_pid; @@ -158,14 +167,14 @@ void update_process_group(struct process_group *pgroup) clear_list(pgroup->proclist); init_list(pgroup->proclist, sizeof(pid_t)); - while (get_next_process(&it, &tmp_process) != -1) + while (get_next_process(&it, tmp_process) != -1) { - p = process_table_find(pgroup->proctable, &tmp_process); + p = process_table_find(pgroup->proctable, tmp_process); if (p == NULL) { /* process is new. add it */ - tmp_process.cpu_usage = -1; - p = process_dup(&tmp_process); + tmp_process->cpu_usage = -1; + p = process_dup(tmp_process); process_table_add(pgroup->proctable, p); add_elem(pgroup->proclist, p); } @@ -176,7 +185,7 @@ void update_process_group(struct process_group *pgroup) if (dt < MIN_DT) continue; /* process exists. update CPU usage */ - sample = (tmp_process.cputime - p->cputime) / dt; + sample = (tmp_process->cputime - p->cputime) / dt; sample = MIN(sample, 1.0); if (p->cpu_usage < 0) { @@ -188,9 +197,10 @@ void update_process_group(struct process_group *pgroup) /* usage adjustment */ p->cpu_usage = (1.0 - ALPHA) * p->cpu_usage + ALPHA * sample; } - p->cputime = tmp_process.cputime; + p->cputime = tmp_process->cputime; } } + free(tmp_process); close_process_iterator(&it); if (dt < MIN_DT) return; diff --git a/src/process_table.c b/src/process_table.c index d3dcded9..eb9f2919 100644 --- a/src/process_table.c +++ b/src/process_table.c @@ -31,9 +31,16 @@ struct process *process_table_find(const struct process_table *pt, const struct struct process *process_table_find_pid(const struct process_table *pt, pid_t pid) { - struct process p; - p.pid = pid; - return process_table_find(pt, &p); + struct process *p, *res; + p = (struct process *)malloc(sizeof(struct process)); + if (p == NULL) + { + exit(1); + } + p->pid = pid; + res = process_table_find(pt, p); + free(p); + return res; } void process_table_add(struct process_table *pt, struct process *p) @@ -70,9 +77,17 @@ int process_table_del(struct process_table *pt, const struct process *p) int process_table_del_pid(struct process_table *pt, pid_t pid) { - struct process p; - p.pid = pid; - return process_table_del(pt, &p); + struct process *p; + int ret; + p = (struct process *)malloc(sizeof(struct process)); + if (p == NULL) + { + exit(1); + } + p->pid = pid; + ret = process_table_del(pt, p); + free(p); + return ret; } void process_table_destroy(struct process_table *pt) diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index c92f8f9e..ccfb6914 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -50,19 +50,21 @@ static void ignore_signal(int sig __attribute__((unused))) static void test_single_process(void) { struct process_iterator it; - struct process process; + struct process *process; struct process_filter filter; int count; + process = (struct process *)malloc(sizeof(struct process)); + assert(process != NULL); /* don't iterate children */ filter.pid = getpid(); filter.include_children = 0; count = 0; init_process_iterator(&it, &filter); - while (get_next_process(&it, &process) == 0) + while (get_next_process(&it, process) == 0) { - assert(process.pid == getpid()); - assert(process.ppid == getppid()); - assert(process.cputime <= 100); + assert(process->pid == getpid()); + assert(process->ppid == getppid()); + assert(process->cputime <= 100); count++; } assert(count == 1); @@ -72,21 +74,22 @@ static void test_single_process(void) filter.include_children = 0; count = 0; init_process_iterator(&it, &filter); - while (get_next_process(&it, &process) == 0) + while (get_next_process(&it, process) == 0) { - assert(process.pid == getpid()); - assert(process.ppid == getppid()); - assert(process.cputime <= 100); + assert(process->pid == getpid()); + assert(process->ppid == getppid()); + assert(process->cputime <= 100); count++; } assert(count == 1); + free(process); close_process_iterator(&it); } static void test_multiple_process(void) { struct process_iterator it; - struct process process; + struct process *process; struct process_filter filter; int count = 0; pid_t child = fork(); @@ -97,21 +100,24 @@ static void test_multiple_process(void) sleep(5); exit(1); } + process = (struct process *)malloc(sizeof(struct process)); + assert(process != NULL); filter.pid = getpid(); filter.include_children = 1; init_process_iterator(&it, &filter); - while (get_next_process(&it, &process) == 0) + while (get_next_process(&it, process) == 0) { - if (process.pid == getpid()) - assert(process.ppid == getppid()); - else if (process.pid == child) - assert(process.ppid == getpid()); + 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->cputime <= 100); count++; } assert(count == 2); + free(process); close_process_iterator(&it); kill(child, SIGKILL); } @@ -119,23 +125,26 @@ static void test_multiple_process(void) static void test_all_processes(void) { struct process_iterator it; - struct process process; + struct process *process; struct process_filter filter; int count = 0; filter.pid = 0; filter.include_children = 0; + process = (struct process *)malloc(sizeof(struct process)); + assert(process != NULL); init_process_iterator(&it, &filter); - while (get_next_process(&it, &process) == 0) + while (get_next_process(&it, process) == 0) { - if (process.pid == getpid()) + if (process->pid == getpid()) { - assert(process.ppid == getppid()); - assert(process.cputime <= 100); + assert(process->ppid == getppid()); + assert(process->cputime <= 100); } count++; } assert(count >= 10); + free(process); close_process_iterator(&it); } @@ -200,22 +209,25 @@ char *command = NULL; static void test_process_name(void) { struct process_iterator it; - struct process process; + struct process *process; struct process_filter filter; static char command_basename[PATH_MAX]; static char process_basename[PATH_MAX]; + process = (struct process *)malloc(sizeof(struct process)); + assert(process != NULL); 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(get_next_process(&it, process) == 0); + assert(process->pid == getpid()); + assert(process->ppid == getppid()); strncpy(command_basename, basename(command), sizeof(command_basename) - 1); command_basename[sizeof(command_basename) - 1] = '\0'; - strncpy(process_basename, basename(process.command), sizeof(process_basename) - 1); + strncpy(process_basename, basename(process->command), sizeof(process_basename) - 1); process_basename[sizeof(process_basename) - 1] = '\0'; assert(strncmp(command_basename, process_basename, sizeof(command_basename)) == 0); - assert(get_next_process(&it, &process) != 0); + assert(get_next_process(&it, process) != 0); + free(process); close_process_iterator(&it); } @@ -247,15 +259,18 @@ static void test_find_process_by_name(void) static void test_getppid_of(void) { struct process_iterator it; - struct process process; + struct process *process; struct process_filter filter; filter.pid = 0; filter.include_children = 0; + process = (struct process *)malloc(sizeof(struct process)); + assert(process != NULL); init_process_iterator(&it, &filter); - while (get_next_process(&it, &process) == 0) + while (get_next_process(&it, process) == 0) { - assert(getppid_of(process.pid) == process.ppid); + assert(getppid_of(process->pid) == process->ppid); } + free(process); close_process_iterator(&it); assert(getppid_of(getpid()) == getppid()); } From 1e1a4fb0ba85c3772eb608a0917f70d0e3c28563 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 12 Oct 2024 22:54:00 +0800 Subject: [PATCH 151/209] Simplify the extraction of the program name --- src/cpulimit.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 9b43c592..b7c94f54 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -64,7 +64,7 @@ struct process_group pgroup; pid_t cpulimit_pid; /* Name of this program */ -char *program_name; +const char *program_name; /* Number of CPUs available in the system */ int NCPU; @@ -361,9 +361,6 @@ int main(int argc, char *argv[]) /* Signal action struct for handling interrupts */ struct sigaction sa; - /* Store the base name of the program */ - static char program_base_name[PATH_MAX]; - /* Register the quit handler to run at program exit */ if (atexit(quit_handler) != 0) { @@ -372,9 +369,7 @@ int main(int argc, char *argv[]) } /* Extract the program name and store it in program_base_name */ - strncpy(program_base_name, basename(argv[0]), sizeof(program_base_name) - 1); - program_base_name[sizeof(program_base_name) - 1] = '\0'; - program_name = program_base_name; + program_name = basename(argv[0]); /* Get the current process ID */ cpulimit_pid = getpid(); From 759760b328fee2a5495e9459e6428a97e4bfec42 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 12 Oct 2024 23:03:46 +0800 Subject: [PATCH 152/209] Simplify the test_process_name function --- tests/process_iterator_test.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index ccfb6914..8bba0784 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -211,8 +211,6 @@ static void test_process_name(void) struct process_iterator it; struct process *process; struct process_filter filter; - static char command_basename[PATH_MAX]; - static char process_basename[PATH_MAX]; process = (struct process *)malloc(sizeof(struct process)); assert(process != NULL); filter.pid = getpid(); @@ -221,11 +219,7 @@ static void test_process_name(void) assert(get_next_process(&it, process) == 0); assert(process->pid == getpid()); assert(process->ppid == getppid()); - strncpy(command_basename, basename(command), sizeof(command_basename) - 1); - command_basename[sizeof(command_basename) - 1] = '\0'; - strncpy(process_basename, basename(process->command), sizeof(process_basename) - 1); - process_basename[sizeof(process_basename) - 1] = '\0'; - assert(strncmp(command_basename, process_basename, sizeof(command_basename)) == 0); + assert(strncmp(basename(command), basename(process->command), sizeof(process->command)) == 0); assert(get_next_process(&it, process) != 0); free(process); close_process_iterator(&it); From b0e4ea6bd220488b24c9a4d7ae7556ff00252d05 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 12 Oct 2024 23:12:07 +0800 Subject: [PATCH 153/209] Dynamically allocate memory for large character arrays --- src/process_iterator_freebsd.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index c9e9a33b..b96be6a2 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -42,14 +42,20 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { - static char errbuf[_POSIX2_LINE_MAX]; + char *errbuf = (char *)malloc(sizeof(char) * _POSIX2_LINE_MAX); + if (errbuf == NULL) + { + exit(1); + } 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_openfiles: %s\n", errbuf); + free(errbuf); return -1; } + free(errbuf); /* Get the list of processes. */ if ((it->procs = kvm_getprocs(it->kd, KERN_PROC_PROC, 0, &it->count)) == NULL) { @@ -94,13 +100,19 @@ static pid_t _getppid_of(kvm_t *kd, pid_t pid) pid_t getppid_of(pid_t pid) { pid_t ppid; - static char errbuf[_POSIX2_LINE_MAX]; + char *errbuf = (char *)malloc(sizeof(char) * _POSIX2_LINE_MAX); + if (errbuf == NULL) + { + exit(1); + } kvm_t *kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); if (kd == NULL) { fprintf(stderr, "kvm_openfiles: %s\n", errbuf); + free(errbuf); return (pid_t)(-1); } + free(errbuf); ppid = _getppid_of(kd, pid); kvm_close(kd); return ppid; @@ -120,13 +132,19 @@ static int _is_child_of(kvm_t *kd, pid_t child_pid, pid_t parent_pid) int is_child_of(pid_t child_pid, pid_t parent_pid) { int ret; - static char errbuf[_POSIX2_LINE_MAX]; + char *errbuf = (char *)malloc(sizeof(char) * _POSIX2_LINE_MAX); + if (errbuf == NULL) + { + exit(1); + } kvm_t *kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); if (kd == NULL) { fprintf(stderr, "kvm_openfiles: %s\n", errbuf); + free(errbuf); return (pid_t)(-1); } + free(errbuf); ret = _is_child_of(kd, child_pid, parent_pid); kvm_close(kd); return ret; From 0c3a637179ba1e859e44d00ebdc6ce2d0571e7f5 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 13 Oct 2024 22:55:38 +0800 Subject: [PATCH 154/209] Refactor the code to address the declaration-after-statement error --- src/process_iterator_freebsd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index b96be6a2..db318915 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -100,12 +100,13 @@ static pid_t _getppid_of(kvm_t *kd, pid_t pid) pid_t getppid_of(pid_t pid) { pid_t ppid; + kvm_t *kd; char *errbuf = (char *)malloc(sizeof(char) * _POSIX2_LINE_MAX); if (errbuf == NULL) { exit(1); } - kvm_t *kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); + kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); if (kd == NULL) { fprintf(stderr, "kvm_openfiles: %s\n", errbuf); @@ -132,12 +133,13 @@ static int _is_child_of(kvm_t *kd, pid_t child_pid, pid_t parent_pid) int is_child_of(pid_t child_pid, pid_t parent_pid) { int ret; + kvm_t *kd; char *errbuf = (char *)malloc(sizeof(char) * _POSIX2_LINE_MAX); if (errbuf == NULL) { exit(1); } - kvm_t *kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); + kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); if (kd == NULL) { fprintf(stderr, "kvm_openfiles: %s\n", errbuf); From 5d8a0cca9ea9bc4ce80b2d08b363cdd7e27ce003 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 14 Oct 2024 02:02:37 +0800 Subject: [PATCH 155/209] Fix segment fault --- src/process_iterator_freebsd.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index db318915..974a9e68 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -42,12 +42,14 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { + struct kinfo_proc *procs; char *errbuf = (char *)malloc(sizeof(char) * _POSIX2_LINE_MAX); if (errbuf == NULL) { exit(1); } it->i = 0; + it->procs = NULL; /* Open the kvm interface, get a descriptor */ if ((it->kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf)) == NULL) { @@ -57,11 +59,15 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi } free(errbuf); /* Get the list of processes. */ - if ((it->procs = kvm_getprocs(it->kd, KERN_PROC_PROC, 0, &it->count)) == NULL) + if ((procs = kvm_getprocs(it->kd, KERN_PROC_PROC, 0, &it->count)) == NULL) { kvm_close(it->kd); return -1; } + it->procs = (struct kinfo_proc *)malloc(sizeof(struct kinfo_proc) * it->count); + if (it->procs == NULL) + exit(1); + memcpy(it->procs, procs, sizeof(struct kinfo_proc) * it->count); it->filter = filter; return 0; } @@ -200,6 +206,8 @@ int get_next_process(struct process_iterator *it, struct process *p) int close_process_iterator(struct process_iterator *it) { + free(it->procs); + it->procs = NULL; if (kvm_close(it->kd) == -1) { fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(it->kd)); From 8a287ffd57ac2f6d1b3d04261d8d29b56d282a59 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 14 Oct 2024 23:34:14 +0800 Subject: [PATCH 156/209] Correct the error handling for the kvm_close function --- src/process_iterator_freebsd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index 974a9e68..1fb0c09f 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -210,7 +210,7 @@ int close_process_iterator(struct process_iterator *it) it->procs = NULL; if (kvm_close(it->kd) == -1) { - fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(it->kd)); + perror("kvm_close"); return -1; } return 0; From e59c08a93e59104470f987eb7aac79275354be96 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Mon, 14 Oct 2024 23:54:18 +0800 Subject: [PATCH 157/209] minor improvements --- src/process_iterator_apple.c | 11 ++++++----- src/process_iterator_freebsd.c | 9 +++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index e877fa14..de1c4665 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -53,7 +53,7 @@ static int unique_nonzero_pids(pid_t *arr_in, int len_in, pid_t *arr_out) for (i = 0; i < len_in; i++) { int found = 0; - if (source[i] == 0) + if (source[i] == (pid_t)0) continue; for (j = 0; !found && j < len_out; j++) { @@ -68,7 +68,7 @@ static int unique_nonzero_pids(pid_t *arr_in, int len_in, pid_t *arr_out) { free(source); } - return len_out - 1; + return len_out; } int init_process_iterator(struct process_iterator *it, struct process_filter *filter) @@ -89,6 +89,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, it->pidlist, it->count)) <= 0) { fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); + free(it->pidlist); return -1; } it->count = unique_nonzero_pids(it->pidlist, it->count, it->pidlist); @@ -118,9 +119,9 @@ static int get_process_pti(pid_t pid, struct proc_taskallinfo *ti) } return -1; } - else if (bytes < (int)sizeof(ti)) + else if (bytes < (int)sizeof(*ti)) { - fprintf(stderr, "proc_pidinfo: too few bytes; expected %lu, got %d\n", (unsigned long)sizeof(ti), bytes); + fprintf(stderr, "proc_pidinfo: too few bytes; expected %lu, got %d\n", (unsigned long)sizeof(*ti), bytes); return -1; } return 0; @@ -151,7 +152,7 @@ int is_child_of(pid_t child_pid, pid_t parent_pid) int get_next_process(struct process_iterator *it, struct process *p) { - if (it->i == it->count) + if (it->i >= it->count) return -1; if (it->filter->pid != 0 && !it->filter->include_children) { diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index 1fb0c09f..c634442c 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -46,6 +46,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi char *errbuf = (char *)malloc(sizeof(char) * _POSIX2_LINE_MAX); if (errbuf == NULL) { + fprintf(stderr, "malloc: %s\n", strerror(errno)); exit(1); } it->i = 0; @@ -160,7 +161,7 @@ int is_child_of(pid_t child_pid, pid_t parent_pid) int get_next_process(struct process_iterator *it, struct process *p) { - if (it->i == it->count) + if (it->i >= it->count) { return -1; } @@ -176,11 +177,11 @@ int get_next_process(struct process_iterator *it, struct process *p) } while (it->i < it->count) { - struct kinfo_proc *kproc = &(it->procs[it->i]); + struct kinfo_proc *kproc = it->procs + it->i; if (kproc->ki_flag & P_SYSTEM) { - /* skip system processes */ it->i++; + /* skip system processes */ continue; } if (it->filter->pid != 0 && it->filter->include_children) @@ -210,7 +211,7 @@ int close_process_iterator(struct process_iterator *it) it->procs = NULL; if (kvm_close(it->kd) == -1) { - perror("kvm_close"); + fprintf(stderr, "kvm_close: %s\n", strerror(errno)); return -1; } return 0; From 74acd65816930be732c24f9f9069bac2cdcd563e Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 15 Oct 2024 15:05:36 +0800 Subject: [PATCH 158/209] Fix misuse of `proc_listpids` return value The `proc_listpids` function returns the buffer size in bytes, but the previous implementation misinterpreted this value as the number of PIDs. The correct calculation for the number of PIDs is the buffer size divided by `sizeof(pid_t)`. With this fix, the `unique_nonzero_pids` function is no longer needed and has been removed. Additionally, the error handling code for PID mismatches has been eliminated. --- src/process_iterator_apple.c | 49 +++++------------------------------- 1 file changed, 6 insertions(+), 43 deletions(-) diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index de1c4665..1e784b66 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -34,65 +34,30 @@ #include #include "process_iterator.h" -static int unique_nonzero_pids(pid_t *arr_in, int len_in, pid_t *arr_out) -{ - pid_t *source = arr_in; - int len_out = 0; - int i, j; - if (arr_out == NULL) - return -1; - if (arr_in == arr_out) - { - source = (pid_t *)malloc(sizeof(pid_t) * len_in); - if (source == NULL) - { - exit(-1); - } - memcpy(source, arr_in, sizeof(pid_t) * len_in); - } - for (i = 0; i < len_in; i++) - { - int found = 0; - if (source[i] == (pid_t)0) - continue; - for (j = 0; !found && j < len_out; j++) - { - found = (source[i] == arr_out[j]); - } - if (!found) - { - arr_out[len_out++] = source[i]; - } - } - if (arr_in == arr_out) - { - free(source); - } - return len_out; -} - int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { + int bufsize; it->i = 0; /* Find out how much to allocate for it->pidlist */ - if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0)) <= 0) + if ((bufsize = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0)) <= 0) { fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); return -1; } /* Allocate and populate it->pidlist */ - if ((it->pidlist = (pid_t *)malloc((it->count) * sizeof(pid_t))) == NULL) + if ((it->pidlist = (pid_t *)malloc(bufsize)) == NULL) { fprintf(stderr, "malloc: %s\n", strerror(errno)); exit(-1); } - if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, it->pidlist, it->count)) <= 0) + if ((bufsize = proc_listpids(PROC_ALL_PIDS, 0, it->pidlist, bufsize)) <= 0) { fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); free(it->pidlist); return -1; } - it->count = unique_nonzero_pids(it->pidlist, it->count, it->pidlist); + /* bufsize / sizeof(pid_t) gives the number of processes */ + it->count = bufsize / sizeof(pid_t); it->filter = filter; return 0; } @@ -183,8 +148,6 @@ int get_next_process(struct process_iterator *it, struct process *p) it->i++; if (pti2proc(&ti, p) != 0) continue; - if (p->pid != it->pidlist[it->i - 1]) /* I don't know why this can happen */ - continue; if (p->pid != it->filter->pid && !is_child_of(p->pid, it->filter->pid)) continue; return 0; From fe020e4aa3c95ad42bd55d8b8801c89b5120e3ea Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 15 Oct 2024 23:47:04 +0800 Subject: [PATCH 159/209] Improve `get_pid_max` --- src/util.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/util.c b/src/util.c index cbc2c1a4..5fabe708 100644 --- a/src/util.c +++ b/src/util.c @@ -6,6 +6,9 @@ #include #include #include +#if defined(__APPLE__) || defined(__FreeBSD__) +#include +#endif #include "util.h" #ifdef __IMPL_BASENAME @@ -64,21 +67,29 @@ int get_ncpu(void) pid_t get_pid_max(void) { #if defined(__linux__) - /* read /proc/sys/kernel/pid_max */ long pid_max = -1; FILE *fd; if ((fd = fopen("/proc/sys/kernel/pid_max", "r")) != NULL) { if (fscanf(fd, "%ld", &pid_max) != 1) + { + perror("fscanf"); pid_max = -1; + } fclose(fd); } return (pid_t)pid_max; -#elif defined(__FreeBSD__) - return (pid_t)99999; -#elif defined(__APPLE__) - return (pid_t)99998; +#elif defined(__APPLE__) || defined(__FreeBSD__) + int max_proc; + size_t size = sizeof(max_proc); + if (sysctlbyname("kern.maxproc", &max_proc, &size, NULL, 0) == -1) + { + perror("sysctl"); + return (pid_t)-1; + } + return (pid_t)max_proc; #else + fprintf(stderr, "Unsupported OS\n"); return (pid_t)-1; #endif } From 1ff8c2abef16cdd9f03a443828e14197f956e6e1 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 16 Oct 2024 01:25:30 +0800 Subject: [PATCH 160/209] Refine `get_ncpu` --- src/util.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/util.c b/src/util.c index 5fabe708..e8062108 100644 --- a/src/util.c +++ b/src/util.c @@ -49,17 +49,21 @@ void increase_priority(void) /* Get the number of CPUs */ int get_ncpu(void) { - int ncpu; + int ncpu = -1; #if defined(_SC_NPROCESSORS_ONLN) ncpu = sysconf(_SC_NPROCESSORS_ONLN); #elif defined(__APPLE__) - int mib[2] = {CTL_HW, HW_NCPU}; + int mib[2] = {CTL_HW, HW_AVAILCPU}; + size_t len = sizeof(ncpu); + sysctl(mib, 2, &ncpu, &len, NULL, 0); +#elif defined(__FreeBSD__) + int mib[2]{CTL_HW, HW_NCPU}; size_t len = sizeof(ncpu); sysctl(mib, 2, &ncpu, &len, NULL, 0); #elif defined(_GNU_SOURCE) ncpu = get_nprocs(); #else - ncpu = -1; +#error "Platform not supported" #endif return ncpu; } From ad2a0a3cd21114c9362909fb181c7b5495d39c18 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 16 Oct 2024 01:28:01 +0800 Subject: [PATCH 161/209] Standardize handling of unsupported platforms --- src/process_iterator.c | 2 +- src/util.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/process_iterator.c b/src/process_iterator.c index 84d37612..6e70789f 100644 --- a/src/process_iterator.c +++ b/src/process_iterator.c @@ -35,6 +35,6 @@ #else -#error Platform not supported +#error "Platform not supported" #endif diff --git a/src/util.c b/src/util.c index e8062108..d91440b9 100644 --- a/src/util.c +++ b/src/util.c @@ -93,7 +93,6 @@ pid_t get_pid_max(void) } return (pid_t)max_proc; #else - fprintf(stderr, "Unsupported OS\n"); - return (pid_t)-1; +#error "Platform not supported" #endif } From c7ce7ea6a5f6aeafb37a0eb6e132603fccb02956 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 16 Oct 2024 03:25:16 +0800 Subject: [PATCH 162/209] Refine `get_process_pti` --- src/process_iterator_apple.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 1e784b66..371889a2 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -74,11 +74,10 @@ static int pti2proc(struct proc_taskallinfo *ti, struct process *process) static int get_process_pti(pid_t pid, struct proc_taskallinfo *ti) { - int bytes; - bytes = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, ti, sizeof(*ti)); + int bytes = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, ti, sizeof(*ti)); if (bytes <= 0) { - if (!(errno & (EPERM | ESRCH))) + if (errno != EPERM && errno != ESRCH) { fprintf(stderr, "proc_pidinfo: %s\n", strerror(errno)); } From 93d35ee0c5b8e36f1f6fcaaa8cd55c967d442800 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 24 Oct 2024 23:28:26 +0800 Subject: [PATCH 163/209] Carefully check the data type of the buffer returned by proc_listpids --- src/process_iterator_apple.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 371889a2..a6ab01d3 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -32,12 +32,14 @@ #include #include #include +#include #include "process_iterator.h" int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { int bufsize; it->i = 0; + assert(sizeof(int) == sizeof(pid_t)); /* Find out how much to allocate for it->pidlist */ if ((bufsize = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0)) <= 0) { From f492848b6ab50e34ccfd8b043654355f4c47ac9f Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sun, 27 Oct 2024 01:53:35 +0800 Subject: [PATCH 164/209] Simplify error handling for unspecified or incorrectly specified target processes --- src/cpulimit.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index b7c94f54..9fc90720 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -460,15 +460,9 @@ int main(int argc, char *argv[]) command_mode = optind < argc; /* Ensure exactly one target process (pid, executable, or command) is specified */ - if (exe_ok + pid_ok + command_mode == 0) + if (exe_ok + pid_ok + command_mode != 1) { - fprintf(stderr, "Error: You must specify one target process, either by name, pid, or command line\n"); - print_usage(stderr, 1); - exit(1); - } - if (exe_ok + pid_ok + command_mode > 1) - { - fprintf(stderr, "Error: You must specify exactly one target process, either by name, pid, or command line\n"); + fprintf(stderr, "Error: You must specify exactly one target process by name, pid, or command line\n"); print_usage(stderr, 1); exit(1); } From 4abba9244ecb23df5c720cbb630b809dcfdd4755 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 30 Oct 2024 00:57:00 +0800 Subject: [PATCH 165/209] Verify that pid_t is equivalent to int for MacOS --- src/process_iterator_apple.c | 5 ++++- src/util.h | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index a6ab01d3..74dd1218 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -34,12 +34,15 @@ #include #include #include "process_iterator.h" +#include "util.h" + +STATIC_ASSERT(sizeof(pid_t) == sizeof(int) && ((pid_t)-1 < 0) == ((int)-1 < 0), + "pid_t is not equivalent to int."); int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { int bufsize; it->i = 0; - assert(sizeof(int) == sizeof(pid_t)); /* Find out how much to allocate for it->pidlist */ if ((bufsize = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0)) <= 0) { diff --git a/src/util.h b/src/util.h index d2eb325e..834784ac 100644 --- a/src/util.h +++ b/src/util.h @@ -175,4 +175,18 @@ int get_ncpu(void); */ pid_t get_pid_max(void); +#ifndef STATIC_ASSERT +/* C++11 and above, C23 and above */ +#if (defined(__cplusplus) && __cplusplus >= 201103L) || \ + (defined(__STDC__) && __STDC_VERSION__ >= 202311L) +#define STATIC_ASSERT(condition, message) static_assert(condition, message) +/* C11 and above */ +#elif defined(__STDC__) && __STDC_VERSION__ >= 201112L +#define STATIC_ASSERT(condition, message) _Static_assert(condition, message) +/* C89/C99/C++98 */ +#else +#define STATIC_ASSERT(condition, message) typedef char static_assertion_failed[(condition) ? 1 : -1] +#endif +#endif + #endif From dc9e4f476194b2d834ac75501dc3a66fda9998ef Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 30 Oct 2024 15:23:58 +0800 Subject: [PATCH 166/209] Refine `basename` calls This commit refines the `basename` calls to address long lines after macro expansion. --- src/process_group.c | 3 ++- tests/process_iterator_test.c | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index 11ace985..e61f3aa2 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -70,7 +70,8 @@ pid_t find_process_by_name(char *process_name) while (get_next_process(&it, proc) != -1) { /* process found */ - if (strncmp(basename(proc->command), process_basename, sizeof(proc->command)) == 0) + const char *cmd_basename = basename(proc->command); + if (strncmp(cmd_basename, process_basename, sizeof(proc->command)) == 0) { if (pid < 0 || is_child_of(pid, proc->pid)) { diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index 8bba0784..bfa124c7 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -211,6 +211,7 @@ static void test_process_name(void) struct process_iterator it; struct process *process; struct process_filter filter; + const char *proc_name1, *proc_name2; process = (struct process *)malloc(sizeof(struct process)); assert(process != NULL); filter.pid = getpid(); @@ -219,7 +220,9 @@ static void test_process_name(void) assert(get_next_process(&it, process) == 0); assert(process->pid == getpid()); assert(process->ppid == getppid()); - assert(strncmp(basename(command), basename(process->command), sizeof(process->command)) == 0); + proc_name1 = basename(command); + proc_name2 = basename(process->command); + assert(strncmp(proc_name1, proc_name2, sizeof(process->command)) == 0); assert(get_next_process(&it, process) != 0); free(process); close_process_iterator(&it); From 748e6c440c43016b309d8d40115ad7d73e919b9d Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 31 Oct 2024 03:00:28 +0800 Subject: [PATCH 167/209] Loosen procfs check for Linux Fix missing header in some Linux versions. --- src/process_iterator_linux.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 23247ba9..458dbf16 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include @@ -43,8 +42,8 @@ static int check_proc(void) { - struct statfs mnt; - return statfs("/proc", &mnt) == 0 && mnt.f_type == PROC_SUPER_MAGIC; + struct stat statbuf; + return stat("/proc", &statbuf) == 0 && S_ISDIR(statbuf.st_mode); } int init_process_iterator(struct process_iterator *it, struct process_filter *filter) From 1bfc0c686ecc0285abaabb117f93b4689882ce9e Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 2 Nov 2024 02:00:55 +0800 Subject: [PATCH 168/209] Add support for limit values represented in decimals --- src/cpulimit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 9fc90720..75fbfc2b 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -328,7 +328,7 @@ int main(int argc, char *argv[]) { /* Variables to store user-provided arguments */ char *exe = NULL; - int perclimit = 0; + double perclimit = 0.0; int exe_ok = 0; int pid_ok = 0; int limit_ok = 0; @@ -395,7 +395,7 @@ int main(int argc, char *argv[]) break; case 'l': /* Store the CPU limit percentage provided by the user */ - perclimit = atoi(optarg); + perclimit = atof(optarg); limit_ok = 1; break; case 'v': From 71af3f2cc6da4812fb200b69710bdb6e2a881c0c Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 19 Nov 2024 23:47:18 +0800 Subject: [PATCH 169/209] Refine `MAX` and `MIN` calls This commit refines the `MAX` and `MIN` calls to address long lines after macro expansion. --- src/cpulimit.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 75fbfc2b..311ec3bd 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -145,7 +145,8 @@ static double get_dynamic_time_slot(void) /* Adjust the time slot based on system load and number of CPUs */ new_time_slot = time_slot * load / NCPU / 0.3; - new_time_slot = MIN(MAX(new_time_slot, MIN_TIME_SLOT), MAX_TIME_SLOT); + new_time_slot = MAX(new_time_slot, MIN_TIME_SLOT); + new_time_slot = MIN(new_time_slot, MAX_TIME_SLOT); /* Smoothly adjust the time slot using a moving average */ time_slot = time_slot * 0.95 + new_time_slot * 0.05; @@ -232,7 +233,8 @@ static void limit_process(pid_t pid, double limit, int include_children) } /* Clamp workingrate to the valid range (0, 1) */ - workingrate = MAX(MIN(workingrate, 1 - EPSILON), EPSILON); + workingrate = MIN(workingrate, 1 - EPSILON); + workingrate = MAX(workingrate, EPSILON); /* Get the dynamic time slot */ time_slot = get_dynamic_time_slot(); From f1ce2dca5e0939a57f96bbb33b236b34876c1f2d Mon Sep 17 00:00:00 2001 From: ymc Date: Thu, 21 Nov 2024 11:23:23 +0800 Subject: [PATCH 170/209] Refactor code to eliminate type conversion warnings --- src/cpulimit.c | 2 +- src/list.c | 2 +- src/process_iterator_apple.c | 12 ++++++------ src/process_iterator_freebsd.c | 8 ++++---- src/process_table.c | 4 ++-- src/util.c | 2 +- src/util.h | 12 ++++++------ 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 311ec3bd..a06ec04a 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -488,7 +488,7 @@ int main(int argc, char *argv[]) /* 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((size_t)(argc - optind + 1) * sizeof(char *)); if (cmd_args == NULL) exit(2); diff --git a/src/list.c b/src/list.c index c6e180a8..88314c95 100644 --- a/src/list.c +++ b/src/list.c @@ -131,7 +131,7 @@ struct list_node *xlocate_node(struct list *l, const void *elem, int offset, int tmp = l->first; while (tmp != NULL) { - if (!memcmp((char *)tmp->data + offset, elem, length == 0 ? l->keysize : length)) + if (!memcmp((char *)tmp->data + offset, elem, (size_t)(length == 0 ? l->keysize : length))) return (tmp); tmp = tmp->next; } diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 74dd1218..20064286 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -50,7 +50,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi return -1; } /* Allocate and populate it->pidlist */ - if ((it->pidlist = (pid_t *)malloc(bufsize)) == NULL) + if ((it->pidlist = (pid_t *)malloc((size_t)bufsize)) == NULL) { fprintf(stderr, "malloc: %s\n", strerror(errno)); exit(-1); @@ -62,17 +62,17 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi return -1; } /* bufsize / sizeof(pid_t) gives the number of processes */ - it->count = bufsize / sizeof(pid_t); + it->count = bufsize / (int)sizeof(pid_t); it->filter = filter; return 0; } static int pti2proc(struct proc_taskallinfo *ti, struct process *process) { - process->pid = ti->pbsd.pbi_pid; - process->ppid = ti->pbsd.pbi_ppid; + process->pid = (pid_t)ti->pbsd.pbi_pid; + process->ppid = (pid_t)ti->pbsd.pbi_ppid; process->cputime = ti->ptinfo.pti_total_user / 1e6 + ti->ptinfo.pti_total_system / 1e6; - if (proc_pidpath(ti->pbsd.pbi_pid, process->command, sizeof(process->command)) <= 0) + if (proc_pidpath((int)ti->pbsd.pbi_pid, process->command, sizeof(process->command)) <= 0) return -1; return 0; } @@ -101,7 +101,7 @@ pid_t getppid_of(pid_t pid) struct proc_taskallinfo ti; if (get_process_pti(pid, &ti) == 0) { - return ti.pbsd.pbi_ppid; + return (pid_t)ti.pbsd.pbi_ppid; } return (pid_t)(-1); } diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index c634442c..9a8dd3d2 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -65,10 +65,10 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi kvm_close(it->kd); return -1; } - it->procs = (struct kinfo_proc *)malloc(sizeof(struct kinfo_proc) * it->count); + it->procs = (struct kinfo_proc *)malloc(sizeof(struct kinfo_proc) * (size_t)it->count); if (it->procs == NULL) exit(1); - memcpy(it->procs, procs, sizeof(struct kinfo_proc) * it->count); + memcpy(it->procs, procs, sizeof(struct kinfo_proc) * (size_t)it->count); it->filter = filter; return 0; } @@ -79,9 +79,9 @@ static int kproc2proc(kvm_t *kd, struct kinfo_proc *kproc, struct process *proc) size_t len_max; proc->pid = kproc->ki_pid; proc->ppid = kproc->ki_ppid; - proc->cputime = kproc->ki_runtime / 1000.0; + proc->cputime = (double)kproc->ki_runtime / 1000.0; len_max = sizeof(proc->command) - 1; - if ((args = kvm_getargv(kd, kproc, len_max)) == NULL) + if ((args = kvm_getargv(kd, kproc, (int)len_max)) == NULL) return -1; strncpy(proc->command, args[0], len_max); proc->command[len_max] = '\0'; diff --git a/src/process_table.c b/src/process_table.c index eb9f2919..57821893 100644 --- a/src/process_table.c +++ b/src/process_table.c @@ -6,12 +6,12 @@ void process_table_init(struct process_table *pt, int hashsize) { pt->hashsize = hashsize; - pt->table = (struct list **)malloc(sizeof(struct list *) * pt->hashsize); + pt->table = (struct list **)malloc(sizeof(struct list *) * (size_t)pt->hashsize); if (pt->table == NULL) { exit(1); } - memset(pt->table, 0, sizeof(struct list *) * pt->hashsize); + memset(pt->table, 0, sizeof(struct list *) * (size_t)pt->hashsize); } static int proc_hash(const struct process_table *pt, const struct process *p) diff --git a/src/util.c b/src/util.c index d91440b9..2749c538 100644 --- a/src/util.c +++ b/src/util.c @@ -51,7 +51,7 @@ int get_ncpu(void) { int ncpu = -1; #if defined(_SC_NPROCESSORS_ONLN) - ncpu = sysconf(_SC_NPROCESSORS_ONLN); + ncpu = (int)sysconf(_SC_NPROCESSORS_ONLN); #elif defined(__APPLE__) int mib[2] = {CTL_HW, HW_AVAILCPU}; size_t len = sizeof(ncpu); diff --git a/src/util.h b/src/util.h index 834784ac..64d39fdc 100644 --- a/src/util.h +++ b/src/util.h @@ -111,11 +111,11 @@ const char *__basename(const char *path); * Converts nanoseconds to a timespec structure */ #ifndef nsec2timespec -#define nsec2timespec(nsec, t) \ - do \ - { \ - (t)->tv_sec = (time_t)((nsec) / 1e9); \ - (t)->tv_nsec = (long)((nsec) - (t)->tv_sec * 1e9); \ +#define nsec2timespec(nsec, t) \ + do \ + { \ + (t)->tv_sec = (time_t)((nsec) / 1e9); \ + (t)->tv_nsec = (long)((nsec) - (double)(t)->tv_sec * 1e9); \ } while (0) #endif @@ -157,7 +157,7 @@ int __get_time(struct timespec *ts); */ #ifndef timediff_in_ms #define timediff_in_ms(t1, t2) \ - (((t1)->tv_sec - (t2)->tv_sec) * 1e3 + ((t1)->tv_nsec - (t2)->tv_nsec) / 1e6) + ((double)((t1)->tv_sec - (t2)->tv_sec) * 1e3 + (double)((t1)->tv_nsec - (t2)->tv_nsec) / 1e6) #endif /** From 095d2b0e7f28ea63982c79b756fdafdadb6e4e1b Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 26 Nov 2024 00:11:18 +0800 Subject: [PATCH 171/209] Improve code to ensure compatibility with the MUSL C library --- src/process_iterator.h | 5 ++++- src/process_table.c | 4 ++++ src/process_table.h | 4 ++++ tests/process_iterator_test.c | 3 --- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/process_iterator.h b/src/process_iterator.h index 649ca331..cee8dfe3 100644 --- a/src/process_iterator.h +++ b/src/process_iterator.h @@ -22,11 +22,14 @@ #ifndef __PROCESS_ITERATOR_H #define __PROCESS_ITERATOR_H +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include #ifdef __linux__ #include -#include #endif #ifdef __FreeBSD__ #include diff --git a/src/process_table.c b/src/process_table.c index 57821893..d0157c3f 100644 --- a/src/process_table.c +++ b/src/process_table.c @@ -1,3 +1,7 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include #include diff --git a/src/process_table.h b/src/process_table.h index 0cc8d201..00544a5a 100644 --- a/src/process_table.h +++ b/src/process_table.h @@ -1,6 +1,10 @@ #ifndef __PROCESS_TABLE_H #define __PROCESS_TABLE_H +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include "process_iterator.h" #include "list.h" diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index bfa124c7..903e1461 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -31,9 +31,6 @@ #include #include #include -#ifdef __linux__ -#include -#endif #include "../src/process_iterator.h" #include "../src/process_group.h" From d1c3b4c2e68b6253550604d1951610986c88b399 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 26 Nov 2024 01:37:42 +0800 Subject: [PATCH 172/209] Improve print output format --- src/cpulimit.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index a06ec04a..838f48a0 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -250,9 +250,13 @@ static void limit_process(pid_t pid, double limit, int include_children) { /* Print CPU usage statistics every 10 cycles */ if (c % 200 == 0) - printf("\n %%CPU work quantum sleep quantum active rate\n"); + printf("\n%9s%16s%16s%14s\n", + "%CPU", "work quantum", "sleep quantum", "active rate"); + if (c % 10 == 0 && c > 0) - printf("%7.2f%% %9.0f us %10.0f us %10.2f%%\n", pcpu * 100, twork_total_nsec / 1000, tsleep_total_nsec / 1000, workingrate * 100); + printf("%8.2f%%%13.0f us%13.0f us%13.2f%%\n", + pcpu * 100, twork_total_nsec / 1000, + tsleep_total_nsec / 1000, workingrate * 100); } /* Resume processes in the group */ From f879f98df3d55b8580cd8f45900e28db523c8407 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 26 Nov 2024 02:00:00 +0800 Subject: [PATCH 173/209] Standardize the naming convention for macros --- src/list.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/list.h b/src/list.h index 9c3e1637..d15565ee 100644 --- a/src/list.h +++ b/src/list.h @@ -19,9 +19,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef __LIST__ - -#define __LIST__ +#ifndef __LIST_H +#define __LIST_H /** * Structure representing a node in a doubly linked list. From 3cecf4dea7146c14f486cfb993cb93c3066e9764 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 27 Nov 2024 03:01:21 +0800 Subject: [PATCH 174/209] Add _GNU_SOURCE macro definition to more file headers This change adds the _GNU_SOURCE macro definition to additional headers in order to prevent compilation errors resulting from changes in file inclusion order. --- src/list.c | 4 ++++ src/list.h | 4 ++++ src/process_iterator.c | 4 ++++ src/process_iterator_apple.c | 4 ++++ src/process_iterator_freebsd.c | 4 ++++ 5 files changed, 20 insertions(+) diff --git a/src/list.c b/src/list.c index 88314c95..af04af18 100644 --- a/src/list.c +++ b/src/list.c @@ -19,6 +19,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include diff --git a/src/list.h b/src/list.h index d15565ee..85d58355 100644 --- a/src/list.h +++ b/src/list.h @@ -22,6 +22,10 @@ #ifndef __LIST_H #define __LIST_H +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + /** * Structure representing a node in a doubly linked list. */ diff --git a/src/process_iterator.c b/src/process_iterator.c index 6e70789f..73147540 100644 --- a/src/process_iterator.c +++ b/src/process_iterator.c @@ -21,6 +21,10 @@ /* See this link to port to other systems: http://www.steve.org.uk/Reference/Unix/faq_8.html#SEC85 */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #if defined(__linux__) #include "process_iterator_linux.c" diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 20064286..6e4ebfd1 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -27,6 +27,10 @@ #ifndef __PROCESS_ITERATOR_APPLE_C #define __PROCESS_ITERATOR_APPLE_C +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include #include diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index 9a8dd3d2..5b9642ad 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -24,6 +24,10 @@ #ifndef __PROCESS_ITERATOR_FREEBSD_C #define __PROCESS_ITERATOR_FREEBSD_C +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #if defined(__STRICT_ANSI__) || !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) #define inline #endif From 8cf3f00dfa2942563d386b166ebdda4d39d538a1 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 27 Nov 2024 23:32:15 +0800 Subject: [PATCH 175/209] Refactor: Use `calloc` instead of `malloc` + `memset` to simplify the code --- src/process_table.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/process_table.c b/src/process_table.c index d0157c3f..0207bfe7 100644 --- a/src/process_table.c +++ b/src/process_table.c @@ -10,12 +10,11 @@ void process_table_init(struct process_table *pt, int hashsize) { pt->hashsize = hashsize; - pt->table = (struct list **)malloc(sizeof(struct list *) * (size_t)pt->hashsize); + pt->table = (struct list **)calloc((size_t)pt->hashsize, sizeof(struct list *)); if (pt->table == NULL) { exit(1); } - memset(pt->table, 0, sizeof(struct list *) * (size_t)pt->hashsize); } static int proc_hash(const struct process_table *pt, const struct process *p) From b6d540f4d221023434a04afca09d828867fe63b1 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 28 Nov 2024 00:36:50 +0800 Subject: [PATCH 176/209] Remove redundant `exit` calls after `print_usage` The `print_usage` function already includes an `exit` call. --- src/cpulimit.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 838f48a0..2d4a98ec 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -437,7 +437,6 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Error: Invalid value for argument PID\n"); print_usage(stderr, 1); - exit(1); } if (pid != 0) { @@ -450,7 +449,6 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Error: You must specify a cpu limit percentage\n"); print_usage(stderr, 1); - exit(1); } /* Calculate the CPU limit as a fraction */ @@ -459,7 +457,6 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Error: limit must be in the range 0-%d00\n", NCPU); print_usage(stderr, 1); - exit(1); } /* Determine if a command was provided */ @@ -470,7 +467,6 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Error: You must specify exactly one target process by name, pid, or command line\n"); print_usage(stderr, 1); - exit(1); } /* Set up signal handlers for SIGINT and SIGTERM */ From 66d95f97e00bf64e12d4145123753639bbee8043 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 28 Nov 2024 00:43:46 +0800 Subject: [PATCH 177/209] Rename `print_usage` to `print_usage_and_exit` for clarity --- src/cpulimit.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 2d4a98ec..4924e0b2 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -101,12 +101,12 @@ static void sig_handler(int sig) } /** - * Prints the usage information for the program. + * Prints the usage information for the program and exit. * * @param stream The file stream to write the usage information to (e.g., stdout). * @param exit_code The exit code to return after printing usage. */ -static void print_usage(FILE *stream, int exit_code) +static void print_usage_and_exit(FILE *stream, int exit_code) { /* Print the usage message along with available options */ fprintf(stream, "Usage: %s [OPTIONS...] TARGET\n", program_name); @@ -418,11 +418,11 @@ int main(int argc, char *argv[]) break; case 'h': /* Print usage information and exit */ - print_usage(stdout, 1); + print_usage_and_exit(stdout, 1); break; case '?': /* Print usage information on invalid option */ - print_usage(stderr, 1); + print_usage_and_exit(stderr, 1); break; case -1: /* No more options to process */ @@ -436,7 +436,7 @@ int main(int argc, char *argv[]) if (pid_ok && (pid <= 1 || pid >= get_pid_max())) { fprintf(stderr, "Error: Invalid value for argument PID\n"); - print_usage(stderr, 1); + print_usage_and_exit(stderr, 1); } if (pid != 0) { @@ -448,7 +448,7 @@ int main(int argc, char *argv[]) if (!limit_ok) { fprintf(stderr, "Error: You must specify a cpu limit percentage\n"); - print_usage(stderr, 1); + print_usage_and_exit(stderr, 1); } /* Calculate the CPU limit as a fraction */ @@ -456,7 +456,7 @@ int main(int argc, char *argv[]) if (limit < 0 || limit > NCPU) { fprintf(stderr, "Error: limit must be in the range 0-%d00\n", NCPU); - print_usage(stderr, 1); + print_usage_and_exit(stderr, 1); } /* Determine if a command was provided */ @@ -466,7 +466,7 @@ int main(int argc, char *argv[]) if (exe_ok + pid_ok + command_mode != 1) { fprintf(stderr, "Error: You must specify exactly one target process by name, pid, or command line\n"); - print_usage(stderr, 1); + print_usage_and_exit(stderr, 1); } /* Set up signal handlers for SIGINT and SIGTERM */ From f37e82a31e52e570261f5108f8bd7caef217a54f Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 28 Nov 2024 03:56:11 +0800 Subject: [PATCH 178/209] Improve Makefile --- src/Makefile | 39 +++++++++++++++++++++++++++++++-------- tests/Makefile | 46 +++++++++++++++++++++++++++++++++++----------- 2 files changed, 66 insertions(+), 19 deletions(-) diff --git a/src/Makefile b/src/Makefile index bad8aef3..371f7944 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,26 +1,49 @@ +# Compiler configuration CC ?= gcc -override CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ - -std=c89 \ - -Wall -Wextra -pedantic \ - -Wmissing-prototypes -Wstrict-prototypes \ - -Wold-style-definition + +# Compiler flags +override CFLAGS := $(filter-out -std=% -ansi -W%, $(CFLAGS)) \ + -std=c89 \ + -Wall \ + -Wextra \ + -pedantic \ + -Wold-style-definition \ + -Wmissing-prototypes \ + -Wstrict-prototypes \ + -Werror + +# Target binary TARGET := cpulimit +# Detect operating system UNAME ?= $(shell uname) + +# Platform-specific linker flags ifeq ($(UNAME), FreeBSD) - override LDFLAGS += -lkvm + override LDFLAGS += -lkvm endif -ifeq ($(shell echo "int main(void){return 0;}" | $(CC) -x c - -lrt -o /dev/null 2>&1),) - override LDFLAGS += -lrt +ifeq ($(UNAME), Darwin) + override LDFLAGS += -lproc endif +# Check for librt availability +override LDFLAGS += $(shell \ + echo "int main(void){ return 0; }" | \ + $(CC) -x c -o /dev/null -lrt - 2>/dev/null && \ + echo -lrt \ +) + +# Phony targets .PHONY: all clean +# Default target all: $(TARGET) +# Build target $(TARGET): $(wildcard *.c *.h) $(CC) $(CFLAGS) $(filter-out process_iterator_%.c %.h, $^) $(LDFLAGS) -o $@ +# Clean target clean: rm -f *~ $(TARGET) diff --git a/tests/Makefile b/tests/Makefile index 1e62c3e9..25bc54a3 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,33 +1,57 @@ +# Compiler configuration CC ?= gcc -override CFLAGS := $(filter-out -std=% -ansi -W%,$(CFLAGS)) \ - -std=c89 \ - -Wall -Wextra -pedantic \ - -Wmissing-prototypes -Wstrict-prototypes \ - -Wold-style-definition -TARGETS := $(patsubst %.c,%,$(wildcard *.c)) -SRC = ../src +# Compiler flags +override CFLAGS := $(filter-out -std=% -ansi -W%, $(CFLAGS)) \ + -std=c89 \ + -Wall \ + -Wextra \ + -pedantic \ + -Wold-style-definition \ + -Wmissing-prototypes \ + -Wstrict-prototypes \ + -Werror + +# Target binaries and source directory +TARGETS := $(patsubst %.c, %, $(wildcard *.c)) +SRC := ../src + +# Detect operating system UNAME ?= $(shell uname) + +# Platform-specific linker flags ifeq ($(UNAME), FreeBSD) - override LDFLAGS += -lkvm + override LDFLAGS += -lkvm endif -ifeq ($(shell echo "int main(void){return 0;}" | $(CC) -x c - -lrt -o /dev/null 2>&1),) - override LDFLAGS += -lrt +ifeq ($(UNAME), Darwin) + override LDFLAGS += -lproc endif +# Check for librt availability +override LDFLAGS += $(shell \ + echo "int main(void){ return 0; }" | \ + $(CC) -x c -o /dev/null -lrt - 2>/dev/null && \ + echo -lrt \ +) + +# Phony targets .PHONY: all clean +# Default target all: $(TARGETS) +# Target rules busy: busy.c $(wildcard $(SRC)/util.*) $(CC) $(CFLAGS) $(filter-out %.h, $^) -lpthread $(LDFLAGS) -o $@ multi_process_busy: multi_process_busy.c $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ -process_iterator_test: process_iterator_test.c $(filter-out $(SRC)/cpulimit.c, $(wildcard $(SRC)/*.c $(SRC)/*.h)) +process_iterator_test: process_iterator_test.c \ + $(filter-out $(SRC)/cpulimit.c, $(wildcard $(SRC)/*.c $(SRC)/*.h)) $(CC) $(CFLAGS) $(filter-out $(SRC)/process_iterator_%.c %.h, $^) $(LDFLAGS) -o $@ +# Clean target clean: rm -f *~ $(TARGETS) From e879a9b2baec0367246cfb582800aac9ba4179bd Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 28 Nov 2024 04:17:03 +0800 Subject: [PATCH 179/209] Define an empty filename using C++-compatible syntax --- tests/process_iterator_test.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index 903e1461..de48ab66 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -246,8 +246,9 @@ static void test_find_process_by_pid(void) static void test_find_process_by_name(void) { + char empty_name[] = ""; assert(find_process_by_name(command) == getpid()); - assert(find_process_by_name("") == 0); + assert(find_process_by_name(empty_name) == 0); } static void test_getppid_of(void) From 79b9aa2cfe3731fba0c3d7a66ae288327fb2632e Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 29 Nov 2024 02:11:13 +0800 Subject: [PATCH 180/209] Enhance argument validation for stricter input checks --- src/cpulimit.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/cpulimit.c b/src/cpulimit.c index 4924e0b2..1c04da5d 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -387,6 +387,13 @@ int main(int argc, char *argv[]) do { next_option = getopt_long(argc, argv, short_options, long_options, &option_index); + if (strchr("pel", next_option) != NULL && optarg[0] == '-') + { + fprintf(stderr, "%s: option '%c' requires an argument.\n", + argv[0], next_option); + print_usage_and_exit(stderr, 1); + } + switch (next_option) { case 'p': From 228b9ccc517939c24f6c712aa1bed5ee43b9a9b4 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 29 Nov 2024 02:13:08 +0800 Subject: [PATCH 181/209] Change exit code to 0 for the -h (help) option --- src/cpulimit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 1c04da5d..81495138 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -425,7 +425,7 @@ int main(int argc, char *argv[]) break; case 'h': /* Print usage information and exit */ - print_usage_and_exit(stdout, 1); + print_usage_and_exit(stdout, 0); break; case '?': /* Print usage information on invalid option */ From d6a03fcf0a043f95813fdfb43ee4a0e15aa17b15 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 29 Nov 2024 02:42:39 +0800 Subject: [PATCH 182/209] Improve validation for arguments of `-p` and `-l` options --- src/cpulimit.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 81495138..7d9c7dc4 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -360,6 +360,7 @@ int main(int argc, char *argv[]) {0, 0, 0, 0}}; double limit; + char *endptr; /* Set waiting time between process searches */ struct timespec wait_time = {2, 0}; @@ -398,8 +399,8 @@ int main(int argc, char *argv[]) { case 'p': /* Store the PID provided by the user */ - pid = (pid_t)atol(optarg); - pid_ok = 1; + pid = (pid_t)strtol(optarg, &endptr, 10); + pid_ok = endptr != optarg && *endptr == '\0'; break; case 'e': /* Store the executable name provided by the user */ @@ -408,8 +409,8 @@ int main(int argc, char *argv[]) break; case 'l': /* Store the CPU limit percentage provided by the user */ - perclimit = atof(optarg); - limit_ok = 1; + perclimit = strtod(optarg, &endptr); + limit_ok = endptr != optarg && *endptr == '\0'; break; case 'v': /* Enable verbose mode */ From c0bd4a0ce22f9aa51fc69b625212f5c9dd49a37f Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 3 Dec 2024 00:18:54 +0800 Subject: [PATCH 183/209] Improve PID not found message to avoid confusion --- src/cpulimit.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 7d9c7dc4..73eb0843 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -578,13 +578,9 @@ int main(int argc, char *argv[]) { /* Search for the process by PID */ ret = find_process_by_pid(pid); - if (ret == 0) - { - printf("No process found\n"); - } - else if (ret < 0) + if (ret <= 0) { - printf("Process found but you aren't allowed to control it\n"); + printf("No process found or you aren't allowed to control it\n"); } } else From 9f86ba42e8a69713dd6808057a5ca5117dad80b7 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 3 Dec 2024 01:31:29 +0800 Subject: [PATCH 184/209] Use pointers to const variables wherever possible --- src/cpulimit.c | 4 ++-- src/process_iterator_freebsd.c | 2 +- src/process_iterator_linux.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 73eb0843..950c3bb6 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -264,7 +264,7 @@ static void limit_process(pid_t pid, double limit, int include_children) while (node != NULL) { struct list_node *next_node = node->next; - struct process *proc = (struct process *)(node->data); + const struct process *proc = (const struct process *)(node->data); if (kill(proc->pid, SIGCONT) != 0) { /* If the process is dead, remove it from the group */ @@ -286,7 +286,7 @@ static void limit_process(pid_t pid, double limit, int include_children) while (node != NULL) { struct list_node *next_node = node->next; - struct process *proc = (struct process *)(node->data); + const struct process *proc = (const struct process *)(node->data); if (kill(proc->pid, SIGSTOP) != 0) { /* If the process is dead, remove it from the group */ diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index 5b9642ad..92aa546a 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -46,7 +46,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { - struct kinfo_proc *procs; + const struct kinfo_proc *procs; char *errbuf = (char *)malloc(sizeof(char) * _POSIX2_LINE_MAX); if (errbuf == NULL) { diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 458dbf16..c138f29d 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -183,7 +183,7 @@ static int is_numeric(const char *str) int get_next_process(struct process_iterator *it, struct process *p) { - struct dirent *dit = NULL; + const struct dirent *dit = NULL; if (it->dip == NULL) { From 1b32e720a2b4ad9abf8a1143a676be4f6668a18d Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 3 Dec 2024 01:36:11 +0800 Subject: [PATCH 185/209] Remove NDEBUG to ensure assert statements are always executed --- tests/process_iterator_test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index de48ab66..eaa83a4a 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -23,6 +23,7 @@ #define _GNU_SOURCE #endif +#undef NDEBUG #include #include #include From d5c7adc5e25c40453e950a8a24eace2ce872c5a0 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 4 Dec 2024 21:47:35 +0800 Subject: [PATCH 186/209] Simplify the return value for removing a process from the process table --- src/process_group.h | 2 +- src/process_table.c | 2 +- src/process_table.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/process_group.h b/src/process_group.h index 9d2bb7a1..05822e1b 100644 --- a/src/process_group.h +++ b/src/process_group.h @@ -102,7 +102,7 @@ pid_t find_process_by_name(char *process_name); * * @param pgroup Pointer to the process group from which to remove the process. * @param pid The PID of the process to remove. - * @return Result of the process table deletion operation. + * @return 0 if removal is successful, or 1 if process is not found. */ int remove_process(struct process_group *pgroup, pid_t pid); diff --git a/src/process_table.c b/src/process_table.c index 0207bfe7..2f3b9c2e 100644 --- a/src/process_table.c +++ b/src/process_table.c @@ -72,7 +72,7 @@ int process_table_del(struct process_table *pt, const struct process *p) node = (struct list_node *)locate_node(pt->table[idx], p); if (node == NULL) { - return 2; /* nothing to delete */ + return 1; /* nothing to delete */ } delete_node(pt->table[idx], node); return 0; diff --git a/src/process_table.h b/src/process_table.h index 00544a5a..0dcee66f 100644 --- a/src/process_table.h +++ b/src/process_table.h @@ -60,7 +60,7 @@ void process_table_add(struct process_table *pt, struct process *p); * * @param pt The process table to delete the process from * @param p The process to delete - * @return 0 if deletion is successful, 1 if process not found, 2 if node not found + * @return 0 if deletion is successful, 1 if process not found */ int process_table_del(struct process_table *pt, const struct process *p); @@ -69,7 +69,7 @@ int process_table_del(struct process_table *pt, const struct process *p); * * @param pt The process table to delete the process from * @param pid The PID of the process to delete - * @return 0 if deletion is successful, 1 if process not found, 2 if node not found + * @return 0 if deletion is successful, 1 if process not found */ int process_table_del_pid(struct process_table *pt, pid_t pid); From 33979463ccbae6b149b9662edddd86da1f9b0666 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 4 Dec 2024 23:06:58 +0800 Subject: [PATCH 187/209] Use sysctl to retrieve the PID list in macOS --- src/process_iterator_apple.c | 67 ++++++++++++++++++++++++------------ src/util.h | 14 -------- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 6e4ebfd1..d09256a9 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -36,39 +36,62 @@ #include #include #include -#include +#include +#include #include "process_iterator.h" -#include "util.h" - -STATIC_ASSERT(sizeof(pid_t) == sizeof(int) && ((pid_t)-1 < 0) == ((int)-1 < 0), - "pid_t is not equivalent to int."); int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { - int bufsize; + size_t len; + struct kinfo_proc *procs; + int i; + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; + it->i = 0; - /* Find out how much to allocate for it->pidlist */ - if ((bufsize = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0)) <= 0) + it->pidlist = NULL; + it->filter = filter; + + /* Get the size of all process information */ + if (sysctl(mib, 4, NULL, &len, NULL, 0) < 0) { - fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); - return -1; + fprintf(stderr, "Failed to get process size: %s\n", strerror(errno)); + exit(1); /* Exit on error */ } - /* Allocate and populate it->pidlist */ - if ((it->pidlist = (pid_t *)malloc((size_t)bufsize)) == NULL) + + /* Allocate memory to store process information */ + procs = (struct kinfo_proc *)malloc(len); + if (procs == NULL) { - fprintf(stderr, "malloc: %s\n", strerror(errno)); - exit(-1); + fprintf(stderr, "Memory allocation failed for process information: %s\n", strerror(errno)); + exit(1); /* Exit on error */ } - if ((bufsize = proc_listpids(PROC_ALL_PIDS, 0, it->pidlist, bufsize)) <= 0) + + /* Get process information */ + if (sysctl(mib, 4, procs, &len, NULL, 0) < 0) { - fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); - free(it->pidlist); - return -1; + free(procs); + fprintf(stderr, "Failed to get process information: %s\n", strerror(errno)); + exit(1); /* Exit on error */ } - /* bufsize / sizeof(pid_t) gives the number of processes */ - it->count = bufsize / (int)sizeof(pid_t); - it->filter = filter; - return 0; + + /* Calculate the number of processes */ + it->count = (int)(len / sizeof(struct kinfo_proc)); + it->pidlist = (pid_t *)malloc((size_t)it->count * sizeof(pid_t)); /* Allocate PID array */ + if (it->pidlist == NULL) + { + free(procs); + fprintf(stderr, "Memory allocation failed for PID list: %s\n", strerror(errno)); + exit(1); /* Exit on error */ + } + + /* Fill the PID array */ + for (i = 0; i < it->count; i++) + { + it->pidlist[i] = (pid_t)procs[i].kp_proc.p_pid; + } + + free(procs); + return 0; /* Success */ } static int pti2proc(struct proc_taskallinfo *ti, struct process *process) diff --git a/src/util.h b/src/util.h index 64d39fdc..486f3f65 100644 --- a/src/util.h +++ b/src/util.h @@ -175,18 +175,4 @@ int get_ncpu(void); */ pid_t get_pid_max(void); -#ifndef STATIC_ASSERT -/* C++11 and above, C23 and above */ -#if (defined(__cplusplus) && __cplusplus >= 201103L) || \ - (defined(__STDC__) && __STDC_VERSION__ >= 202311L) -#define STATIC_ASSERT(condition, message) static_assert(condition, message) -/* C11 and above */ -#elif defined(__STDC__) && __STDC_VERSION__ >= 201112L -#define STATIC_ASSERT(condition, message) _Static_assert(condition, message) -/* C89/C99/C++98 */ -#else -#define STATIC_ASSERT(condition, message) typedef char static_assertion_failed[(condition) ? 1 : -1] -#endif -#endif - #endif From d370101a9360d7b20189e5928bd329bf4d483c22 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 6 Dec 2024 01:22:47 +0800 Subject: [PATCH 188/209] update CI --- .github/workflows/CI.yml | 67 ++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 37 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 3a16b7ae..5bb71470 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -13,7 +13,7 @@ concurrency: jobs: build: - name: build + name: Build on ${{ matrix.os }} permissions: contents: read strategy: @@ -28,9 +28,11 @@ jobs: ref: master fetch-depth: 0 - - name: Compile in macOS - if: ${{ startsWith( matrix.os, 'macos-' ) }} + - name: Compile and Test run: | + if [[ "${{ matrix.os }}" == ubuntu-* ]]; then + export LDFLAGS="$LDFLAGS -static" + fi make sudo ./tests/process_iterator_test random_file="$(mktemp $(printf 'X%.0s' $(seq 1 255)))" @@ -38,51 +40,42 @@ jobs: echo "./tests/${random_file}" sudo ./tests/${random_file} - - name: Compile in Ubuntu - if: ${{ startsWith( matrix.os, 'ubuntu-' ) }} - run: | - make LDFLAGS="-static" - sudo ./tests/process_iterator_test - random_file="$(mktemp $(printf 'X%.0s' $(seq 1 255)))" - cp ./tests/process_iterator_test ./tests/${random_file} - echo "./tests/${random_file}" - sudo ./tests/${random_file} - - - name: Upload + - name: Upload Artifacts uses: actions/upload-artifact@main with: name: cpulimit-${{ matrix.os }} path: src/cpulimit build-FreeBSD: - name: build-FreeBSD + name: Build on FreeBSD-${{ matrix.osver }} permissions: contents: read strategy: matrix: - osver: ['13.4', '14.1'] + osver: ['13.4', '14.1', '15.0'] runs-on: ubuntu-latest + steps: - - name: Checkout - uses: actions/checkout@main + - name: Checkout + uses: actions/checkout@main - - name: Build in FreeBSD - uses: vmactions/freebsd-vm@v1 - with: - release: ${{ matrix.osver }} - usesh: true - prepare: | - pkg install -y lang/gcc gmake sudo - run: | - gmake - sudo ./tests/process_iterator_test - random_file="$(mktemp $(printf 'X%.0s' $(seq 1 255)))" - cp ./tests/process_iterator_test ./tests/${random_file} - echo "./tests/${random_file}" - sudo ./tests/${random_file} + - name: Build on FreeBSD + uses: vmactions/freebsd-vm@v1 + with: + release: ${{ matrix.osver }} + usesh: true + prepare: | + pkg install -y lang/gcc gmake sudo + run: | + gmake + sudo ./tests/process_iterator_test + random_file="$(mktemp $(printf 'X%.0s' $(seq 1 255)))" + cp ./tests/process_iterator_test ./tests/${random_file} + echo "./tests/${random_file}" + sudo ./tests/${random_file} - - name: Upload - uses: actions/upload-artifact@main - with: - name: cpulimit-FreeBSD-${{ matrix.osver }} - path: src/cpulimit + - name: Upload Artifacts + uses: actions/upload-artifact@main + with: + name: cpulimit-FreeBSD-${{ matrix.osver }} + path: src/cpulimit From 705d6519513456426b01e6efae9e999cb7349443 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 6 Dec 2024 02:47:09 +0800 Subject: [PATCH 189/209] Rename variables --- src/process_iterator_linux.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index c138f29d..2de823a5 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -66,7 +66,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi static int read_process_info(pid_t pid, struct process *p) { char statfile[32], exefile[32], state; - double utime, stime; + double usertime, systime; long ppid; FILE *fd; static double sc_clk_tck = -1.0; @@ -93,7 +93,7 @@ static int read_process_info(pid_t pid, struct process *p) return -1; } if (fscanf(fd, "%*d (%*[^)]) %c %ld %*d %*d %*d %*d %*d %*d %*d %*d %*d %lf %lf", - &state, &ppid, &utime, &stime) != 4 || + &state, &ppid, &usertime, &systime) != 4 || strchr("ZXx", state) != NULL) { fclose(fd); @@ -105,7 +105,7 @@ static int read_process_info(pid_t pid, struct process *p) { sc_clk_tck = (double)sysconf(_SC_CLK_TCK); } - p->cputime = (utime + stime) * 1000.0 / sc_clk_tck; + p->cputime = (usertime + systime) * 1000.0 / sc_clk_tck; return 0; } From 6d7514617ea2bd72083b16941ba47412aeadf10a Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 13 Dec 2024 10:21:26 +0800 Subject: [PATCH 190/209] Remove unnecessary type casting --- src/process_iterator_apple.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index d09256a9..8a4880b0 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -87,7 +87,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi /* Fill the PID array */ for (i = 0; i < it->count; i++) { - it->pidlist[i] = (pid_t)procs[i].kp_proc.p_pid; + it->pidlist[i] = procs[i].kp_proc.p_pid; } free(procs); From 7fda7c1daf4f8c6dd226b85c1a4d433a7ac39b78 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 13 Dec 2024 10:28:57 +0800 Subject: [PATCH 191/209] Double the size of the process information buffer Prevent buffer overflow caused by an increase in the number of processes in the system. --- src/process_iterator_apple.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 8a4880b0..2e728db7 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -58,6 +58,13 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi exit(1); /* Exit on error */ } + /* + * Double the size of the process information buffer + * to prevent buffer overflow caused by an increase + * in the number of processes. + */ + len <<= 1; + /* Allocate memory to store process information */ procs = (struct kinfo_proc *)malloc(len); if (procs == NULL) From fc645a3dc397ebd95e71291a87d34b89b59d985e Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 13 Dec 2024 18:40:29 +0800 Subject: [PATCH 192/209] More tests on `find_process_by_name` function --- tests/process_iterator_test.c | 41 +++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index eaa83a4a..d06693c0 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -247,9 +247,46 @@ static void test_find_process_by_pid(void) static void test_find_process_by_name(void) { - char empty_name[] = ""; + char *wrong_name; + size_t len; + + wrong_name = (char *)malloc(PATH_MAX + 1); + assert(wrong_name != NULL); + + /* + * 'command' is the name of the current process (equivalent to argv[0]). + * Verify that the find_process_by_name function can find the current process + * (PID should match the return value of getpid()). + */ assert(find_process_by_name(command) == getpid()); - assert(find_process_by_name(empty_name) == 0); + + /* + * Test Case 1: Pass an empty string to find_process_by_name. + * Expectation: Should return 0 (process not found). + */ + strcpy(wrong_name, ""); + assert(find_process_by_name(wrong_name) == 0); + + /* + * Test Case 2: Pass an incorrect process name by appending 'x' + * to the current process's name. + * Expectation: Should return 0 (process not found). + */ + strcpy(wrong_name, command); /* Copy the current process's name */ + strcat(wrong_name, "x"); /* Append 'x' to make it non-matching */ + assert(find_process_by_name(wrong_name) == 0); + + /* + * Test Case 3: Pass a copy of the current process's name with + * the last character removed. + * Expectation: Should return 0 (process not found). + */ + strcpy(wrong_name, command); /* Copy the current process's name */ + len = strlen(wrong_name); + wrong_name[len - 1] = '\0'; /* Remove the last character */ + assert(find_process_by_name(wrong_name) == 0); + + free(wrong_name); } static void test_getppid_of(void) From 84f444ad7eb709858ae8357b5b2c6ded86fa0228 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 18 Dec 2024 09:33:11 +0800 Subject: [PATCH 193/209] Use `EXIT_FAILURE` and `EXIT_SUCCESS` macros for exit statuses --- src/cpulimit.c | 22 +++++++++++----------- src/list.c | 2 +- src/process_group.c | 16 ++++++++-------- src/process_iterator_apple.c | 8 ++++---- src/process_iterator_freebsd.c | 8 ++++---- src/process_iterator_linux.c | 2 +- src/process_table.c | 8 ++++---- tests/busy.c | 2 +- tests/process_iterator_test.c | 4 ++-- 9 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 950c3bb6..6e8b6ee8 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -372,7 +372,7 @@ int main(int argc, char *argv[]) if (atexit(quit_handler) != 0) { fprintf(stderr, "Failed to register quit_handler\n"); - exit(1); + exit(EXIT_FAILURE); } /* Extract the program name and store it in program_base_name */ @@ -392,7 +392,7 @@ int main(int argc, char *argv[]) { fprintf(stderr, "%s: option '%c' requires an argument.\n", argv[0], next_option); - print_usage_and_exit(stderr, 1); + print_usage_and_exit(stderr, EXIT_FAILURE); } switch (next_option) @@ -426,11 +426,11 @@ int main(int argc, char *argv[]) break; case 'h': /* Print usage information and exit */ - print_usage_and_exit(stdout, 0); + print_usage_and_exit(stdout, EXIT_SUCCESS); break; case '?': /* Print usage information on invalid option */ - print_usage_and_exit(stderr, 1); + print_usage_and_exit(stderr, EXIT_FAILURE); break; case -1: /* No more options to process */ @@ -444,7 +444,7 @@ int main(int argc, char *argv[]) if (pid_ok && (pid <= 1 || pid >= get_pid_max())) { fprintf(stderr, "Error: Invalid value for argument PID\n"); - print_usage_and_exit(stderr, 1); + print_usage_and_exit(stderr, EXIT_FAILURE); } if (pid != 0) { @@ -456,7 +456,7 @@ int main(int argc, char *argv[]) if (!limit_ok) { fprintf(stderr, "Error: You must specify a cpu limit percentage\n"); - print_usage_and_exit(stderr, 1); + print_usage_and_exit(stderr, EXIT_FAILURE); } /* Calculate the CPU limit as a fraction */ @@ -464,7 +464,7 @@ int main(int argc, char *argv[]) if (limit < 0 || limit > NCPU) { fprintf(stderr, "Error: limit must be in the range 0-%d00\n", NCPU); - print_usage_and_exit(stderr, 1); + print_usage_and_exit(stderr, EXIT_FAILURE); } /* Determine if a command was provided */ @@ -474,7 +474,7 @@ int main(int argc, char *argv[]) if (exe_ok + pid_ok + command_mode != 1) { fprintf(stderr, "Error: You must specify exactly one target process by name, pid, or command line\n"); - print_usage_and_exit(stderr, 1); + print_usage_and_exit(stderr, EXIT_FAILURE); } /* Set up signal handlers for SIGINT and SIGTERM */ @@ -498,7 +498,7 @@ int main(int argc, char *argv[]) /* Command line arguments */ char **cmd_args = (char **)malloc((size_t)(argc - optind + 1) * sizeof(char *)); if (cmd_args == NULL) - exit(2); + exit(EXIT_FAILURE); /* Prepare command arguments */ for (i = 0; i < argc - optind; i++) @@ -565,7 +565,7 @@ int main(int argc, char *argv[]) if (verbose) printf("Limiting process %ld\n", (long)child); limit_process(child, limit, include_children); - exit(0); + exit(EXIT_SUCCESS); } } } @@ -608,7 +608,7 @@ int main(int argc, char *argv[]) { printf("Target process %ld is cpulimit itself! Aborting because it makes no sense\n", (long)ret); - exit(1); + exit(EXIT_FAILURE); } printf("Process %ld found\n", (long)pid); limit_process(pid, limit, include_children); diff --git a/src/list.c b/src/list.c index af04af18..5813c55d 100644 --- a/src/list.c +++ b/src/list.c @@ -50,7 +50,7 @@ struct list_node *add_elem(struct list *l, void *elem) struct list_node *newnode = (struct list_node *)malloc(sizeof(struct list_node)); if (newnode == NULL) { - exit(-1); + exit(EXIT_FAILURE); } newnode->data = elem; newnode->previous = l->last; diff --git a/src/process_group.c b/src/process_group.c index e61f3aa2..eaba5ec2 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -63,7 +63,7 @@ pid_t find_process_by_name(char *process_name) const char *process_basename = basename(process_name); proc = (struct process *)malloc(sizeof(struct process)); if (proc == NULL) - exit(1); + exit(EXIT_FAILURE); filter.pid = 0; filter.include_children = 0; init_process_iterator(&it, &filter); @@ -81,7 +81,7 @@ pid_t find_process_by_name(char *process_name) } free(proc); if (close_process_iterator(&it) != 0) - exit(1); + exit(EXIT_FAILURE); return (pid > 0) ? find_process_by_pid(pid) : 0; } @@ -92,7 +92,7 @@ int init_process_group(struct process_group *pgroup, pid_t target_pid, int inclu pgroup->proctable = (struct process_table *)malloc(sizeof(struct process_table)); if (pgroup->proctable == NULL) { - exit(-1); + exit(EXIT_FAILURE); } process_table_init(pgroup->proctable, 2048); pgroup->target_pid = target_pid; @@ -100,12 +100,12 @@ int init_process_group(struct process_group *pgroup, pid_t target_pid, int inclu pgroup->proclist = (struct list *)malloc(sizeof(struct list)); if (pgroup->proclist == NULL) { - exit(-1); + exit(EXIT_FAILURE); } init_list(pgroup->proclist, sizeof(pid_t)); if (get_time(&pgroup->last_update)) { - exit(-1); + exit(EXIT_FAILURE); } update_process_group(pgroup); return 0; @@ -135,7 +135,7 @@ static struct process *process_dup(const struct process *proc) struct process *p = (struct process *)malloc(sizeof(struct process)); if (p == NULL) { - exit(-1); + exit(EXIT_FAILURE); } return (struct process *)memcpy(p, proc, sizeof(struct process)); } @@ -153,12 +153,12 @@ void update_process_group(struct process_group *pgroup) double dt; if (get_time(&now)) { - exit(1); + exit(EXIT_FAILURE); } tmp_process = (struct process *)malloc(sizeof(struct process)); if (tmp_process == NULL) { - exit(1); + exit(EXIT_FAILURE); } /* time elapsed from previous sample (in ms) */ dt = timediff_in_ms(&now, &pgroup->last_update); diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 2e728db7..9637809e 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -55,7 +55,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi if (sysctl(mib, 4, NULL, &len, NULL, 0) < 0) { fprintf(stderr, "Failed to get process size: %s\n", strerror(errno)); - exit(1); /* Exit on error */ + exit(EXIT_FAILURE); /* Exit on error */ } /* @@ -70,7 +70,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi if (procs == NULL) { fprintf(stderr, "Memory allocation failed for process information: %s\n", strerror(errno)); - exit(1); /* Exit on error */ + exit(EXIT_FAILURE); /* Exit on error */ } /* Get process information */ @@ -78,7 +78,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi { free(procs); fprintf(stderr, "Failed to get process information: %s\n", strerror(errno)); - exit(1); /* Exit on error */ + exit(EXIT_FAILURE); /* Exit on error */ } /* Calculate the number of processes */ @@ -88,7 +88,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi { free(procs); fprintf(stderr, "Memory allocation failed for PID list: %s\n", strerror(errno)); - exit(1); /* Exit on error */ + exit(EXIT_FAILURE); /* Exit on error */ } /* Fill the PID array */ diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index 92aa546a..d07aec57 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -51,7 +51,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi if (errbuf == NULL) { fprintf(stderr, "malloc: %s\n", strerror(errno)); - exit(1); + exit(EXIT_FAILURE); } it->i = 0; it->procs = NULL; @@ -71,7 +71,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi } it->procs = (struct kinfo_proc *)malloc(sizeof(struct kinfo_proc) * (size_t)it->count); if (it->procs == NULL) - exit(1); + exit(EXIT_FAILURE); memcpy(it->procs, procs, sizeof(struct kinfo_proc) * (size_t)it->count); it->filter = filter; return 0; @@ -115,7 +115,7 @@ pid_t getppid_of(pid_t pid) char *errbuf = (char *)malloc(sizeof(char) * _POSIX2_LINE_MAX); if (errbuf == NULL) { - exit(1); + exit(EXIT_FAILURE); } kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); if (kd == NULL) @@ -148,7 +148,7 @@ int is_child_of(pid_t child_pid, pid_t parent_pid) char *errbuf = (char *)malloc(sizeof(char) * _POSIX2_LINE_MAX); if (errbuf == NULL) { - exit(1); + exit(EXIT_FAILURE); } kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); if (kd == NULL) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 2de823a5..16cda9fb 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -51,7 +51,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi if (!check_proc()) { fprintf(stderr, "procfs is not mounted!\nAborting\n"); - exit(-2); + exit(EXIT_FAILURE); } /* open a directory stream to /proc directory */ if ((it->dip = opendir("/proc")) == NULL) diff --git a/src/process_table.c b/src/process_table.c index 2f3b9c2e..71f7c81d 100644 --- a/src/process_table.c +++ b/src/process_table.c @@ -13,7 +13,7 @@ void process_table_init(struct process_table *pt, int hashsize) pt->table = (struct list **)calloc((size_t)pt->hashsize, sizeof(struct list *)); if (pt->table == NULL) { - exit(1); + exit(EXIT_FAILURE); } } @@ -38,7 +38,7 @@ struct process *process_table_find_pid(const struct process_table *pt, pid_t pid p = (struct process *)malloc(sizeof(struct process)); if (p == NULL) { - exit(1); + exit(EXIT_FAILURE); } p->pid = pid; res = process_table_find(pt, p); @@ -54,7 +54,7 @@ void process_table_add(struct process_table *pt, struct process *p) pt->table[idx] = (struct list *)malloc(sizeof(struct list)); if (pt->table[idx] == NULL) { - exit(-1); + exit(EXIT_FAILURE); } init_list(pt->table[idx], sizeof(pid_t)); } @@ -85,7 +85,7 @@ int process_table_del_pid(struct process_table *pt, pid_t pid) p = (struct process *)malloc(sizeof(struct process)); if (p == NULL) { - exit(1); + exit(EXIT_FAILURE); } p->pid = pid; ret = process_table_del(pt, p); diff --git a/tests/busy.c b/tests/busy.c index 3093e26e..5aa3d29f 100644 --- a/tests/busy.c +++ b/tests/busy.c @@ -30,7 +30,7 @@ int main(int argc, char *argv[]) if ((ret = pthread_create(&thread, NULL, &loop, NULL)) != 0) { printf("pthread_create() failed. Error code %d\n", ret); - exit(1); + exit(EXIT_FAILURE); } } loop(NULL); diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index d06693c0..174f99ea 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -96,7 +96,7 @@ static void test_multiple_process(void) /* child is supposed to be killed by the parent :/ */ while (1) sleep(5); - exit(1); + exit(EXIT_SUCCESS); } process = (struct process *)malloc(sizeof(struct process)); assert(process != NULL); @@ -174,7 +174,7 @@ static void test_process_group_single(int include_children) increase_priority(); while (1) (void)unused_value; - exit(1); + exit(EXIT_SUCCESS); } assert(init_process_group(&pgroup, child, include_children) == 0); for (i = 0; i < 100; i++) From 26d3b3baa75659700740d35063787fb357d899d3 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 18 Dec 2024 11:01:22 +0800 Subject: [PATCH 194/209] Fix and enhance error handling --- src/cpulimit.c | 17 +++++++++++++++-- src/list.c | 3 ++- src/process_group.c | 9 +++++++++ src/process_iterator_apple.c | 13 +++++++------ src/process_iterator_freebsd.c | 10 ++++++++-- src/process_table.c | 5 +++++ 6 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index 6e8b6ee8..d0341205 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -269,7 +269,12 @@ static void limit_process(pid_t pid, double limit, int include_children) { /* If the process is dead, remove it from the group */ if (verbose) - fprintf(stderr, "SIGCONT failed. Process %ld dead!\n", (long)proc->pid); + { + char errbuf[100]; + sprintf(errbuf, "kill failed to send SIGCONT to process %ld", + (long)proc->pid); + perror(errbuf); + } delete_node(pgroup.proclist, node); remove_process(&pgroup, proc->pid); } @@ -291,7 +296,12 @@ static void limit_process(pid_t pid, double limit, int include_children) { /* If the process is dead, remove it from the group */ if (verbose) - fprintf(stderr, "SIGSTOP failed. Process %ld dead!\n", (long)proc->pid); + { + char errbuf[100]; + sprintf(errbuf, "kill failed to send SIGSTOP to process %ld", + (long)proc->pid); + perror(errbuf); + } delete_node(pgroup.proclist, node); remove_process(&pgroup, proc->pid); } @@ -498,7 +508,10 @@ int main(int argc, char *argv[]) /* Command line arguments */ char **cmd_args = (char **)malloc((size_t)(argc - optind + 1) * sizeof(char *)); if (cmd_args == NULL) + { + fprintf(stderr, "Memory allocation failed for cmd_args\n"); exit(EXIT_FAILURE); + } /* Prepare command arguments */ for (i = 0; i < argc - optind; i++) diff --git a/src/list.c b/src/list.c index 5813c55d..a7f47308 100644 --- a/src/list.c +++ b/src/list.c @@ -25,7 +25,7 @@ #include #include - +#include #include "list.h" #define safe_free(p) \ @@ -50,6 +50,7 @@ struct list_node *add_elem(struct list *l, void *elem) struct list_node *newnode = (struct list_node *)malloc(sizeof(struct list_node)); if (newnode == NULL) { + fprintf(stderr, "Memory allocation failed for the new list node\n"); exit(EXIT_FAILURE); } newnode->data = elem; diff --git a/src/process_group.c b/src/process_group.c index eaba5ec2..7b569b06 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -23,6 +23,7 @@ #define _GNU_SOURCE #endif +#include #include #include #include @@ -63,7 +64,11 @@ pid_t find_process_by_name(char *process_name) const char *process_basename = basename(process_name); proc = (struct process *)malloc(sizeof(struct process)); if (proc == NULL) + { + fprintf(stderr, "Memory allocation failed for the process\n"); exit(EXIT_FAILURE); + } + filter.pid = 0; filter.include_children = 0; init_process_iterator(&it, &filter); @@ -92,6 +97,7 @@ int init_process_group(struct process_group *pgroup, pid_t target_pid, int inclu pgroup->proctable = (struct process_table *)malloc(sizeof(struct process_table)); if (pgroup->proctable == NULL) { + fprintf(stderr, "Memory allocation failed for the process table\n"); exit(EXIT_FAILURE); } process_table_init(pgroup->proctable, 2048); @@ -100,6 +106,7 @@ int init_process_group(struct process_group *pgroup, pid_t target_pid, int inclu pgroup->proclist = (struct list *)malloc(sizeof(struct list)); if (pgroup->proclist == NULL) { + fprintf(stderr, "Memory allocation failed for the process list\n"); exit(EXIT_FAILURE); } init_list(pgroup->proclist, sizeof(pid_t)); @@ -135,6 +142,7 @@ static struct process *process_dup(const struct process *proc) struct process *p = (struct process *)malloc(sizeof(struct process)); if (p == NULL) { + fprintf(stderr, "Memory allocation failed for duplicated process\n"); exit(EXIT_FAILURE); } return (struct process *)memcpy(p, proc, sizeof(struct process)); @@ -158,6 +166,7 @@ void update_process_group(struct process_group *pgroup) tmp_process = (struct process *)malloc(sizeof(struct process)); if (tmp_process == NULL) { + fprintf(stderr, "Memory allocation failed for tmp_process\n"); exit(EXIT_FAILURE); } /* time elapsed from previous sample (in ms) */ diff --git a/src/process_iterator_apple.c b/src/process_iterator_apple.c index 9637809e..1f3f013e 100644 --- a/src/process_iterator_apple.c +++ b/src/process_iterator_apple.c @@ -54,7 +54,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi /* Get the size of all process information */ if (sysctl(mib, 4, NULL, &len, NULL, 0) < 0) { - fprintf(stderr, "Failed to get process size: %s\n", strerror(errno)); + perror("Failed to get process information buffer size"); exit(EXIT_FAILURE); /* Exit on error */ } @@ -69,7 +69,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi procs = (struct kinfo_proc *)malloc(len); if (procs == NULL) { - fprintf(stderr, "Memory allocation failed for process information: %s\n", strerror(errno)); + fprintf(stderr, "Memory allocation failed for process information buffer\n"); exit(EXIT_FAILURE); /* Exit on error */ } @@ -77,7 +77,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi if (sysctl(mib, 4, procs, &len, NULL, 0) < 0) { free(procs); - fprintf(stderr, "Failed to get process information: %s\n", strerror(errno)); + perror("Failed to get process information"); exit(EXIT_FAILURE); /* Exit on error */ } @@ -87,7 +87,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi if (it->pidlist == NULL) { free(procs); - fprintf(stderr, "Memory allocation failed for PID list: %s\n", strerror(errno)); + fprintf(stderr, "Memory allocation failed for PID list\n"); exit(EXIT_FAILURE); /* Exit on error */ } @@ -118,13 +118,14 @@ static int get_process_pti(pid_t pid, struct proc_taskallinfo *ti) { if (errno != EPERM && errno != ESRCH) { - fprintf(stderr, "proc_pidinfo: %s\n", strerror(errno)); + perror("proc_pidinfo"); } return -1; } else if (bytes < (int)sizeof(*ti)) { - fprintf(stderr, "proc_pidinfo: too few bytes; expected %lu, got %d\n", (unsigned long)sizeof(*ti), bytes); + fprintf(stderr, "proc_pidinfo: too few bytes; expected %lu, got %d\n", + (unsigned long)sizeof(*ti), bytes); return -1; } return 0; diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index d07aec57..a89b67d1 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -50,7 +50,7 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi char *errbuf = (char *)malloc(sizeof(char) * _POSIX2_LINE_MAX); if (errbuf == NULL) { - fprintf(stderr, "malloc: %s\n", strerror(errno)); + fprintf(stderr, "Memory allocation failed for the error buffer\n"); exit(EXIT_FAILURE); } it->i = 0; @@ -71,7 +71,11 @@ int init_process_iterator(struct process_iterator *it, struct process_filter *fi } it->procs = (struct kinfo_proc *)malloc(sizeof(struct kinfo_proc) * (size_t)it->count); if (it->procs == NULL) + { + fprintf(stderr, "Memory allocation failed for the process list\n"); exit(EXIT_FAILURE); + } + memcpy(it->procs, procs, sizeof(struct kinfo_proc) * (size_t)it->count); it->filter = filter; return 0; @@ -115,6 +119,7 @@ pid_t getppid_of(pid_t pid) char *errbuf = (char *)malloc(sizeof(char) * _POSIX2_LINE_MAX); if (errbuf == NULL) { + fprintf(stderr, "Memory allocation failed for the error buffer\n"); exit(EXIT_FAILURE); } kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); @@ -148,6 +153,7 @@ int is_child_of(pid_t child_pid, pid_t parent_pid) char *errbuf = (char *)malloc(sizeof(char) * _POSIX2_LINE_MAX); if (errbuf == NULL) { + fprintf(stderr, "Memory allocation failed for the error buffer\n"); exit(EXIT_FAILURE); } kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf); @@ -215,7 +221,7 @@ int close_process_iterator(struct process_iterator *it) it->procs = NULL; if (kvm_close(it->kd) == -1) { - fprintf(stderr, "kvm_close: %s\n", strerror(errno)); + perror("kvm_close"); return -1; } return 0; diff --git a/src/process_table.c b/src/process_table.c index 71f7c81d..d220a654 100644 --- a/src/process_table.c +++ b/src/process_table.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "process_table.h" void process_table_init(struct process_table *pt, int hashsize) @@ -13,6 +14,7 @@ void process_table_init(struct process_table *pt, int hashsize) pt->table = (struct list **)calloc((size_t)pt->hashsize, sizeof(struct list *)); if (pt->table == NULL) { + fprintf(stderr, "Memory allocation failed for the process table\n"); exit(EXIT_FAILURE); } } @@ -38,6 +40,7 @@ struct process *process_table_find_pid(const struct process_table *pt, pid_t pid p = (struct process *)malloc(sizeof(struct process)); if (p == NULL) { + fprintf(stderr, "Memory allocation failed for the process\n"); exit(EXIT_FAILURE); } p->pid = pid; @@ -54,6 +57,7 @@ void process_table_add(struct process_table *pt, struct process *p) pt->table[idx] = (struct list *)malloc(sizeof(struct list)); if (pt->table[idx] == NULL) { + fprintf(stderr, "Memory allocation failed for the process list\n"); exit(EXIT_FAILURE); } init_list(pt->table[idx], sizeof(pid_t)); @@ -85,6 +89,7 @@ int process_table_del_pid(struct process_table *pt, pid_t pid) p = (struct process *)malloc(sizeof(struct process)); if (p == NULL) { + fprintf(stderr, "Memory allocation failed for the process\n"); exit(EXIT_FAILURE); } p->pid = pid; From b4d508ff9b5a9d9ee746703538c5d811dadf0169 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Wed, 18 Dec 2024 22:47:51 +0800 Subject: [PATCH 195/209] Refactor the preparation of `execvp` arguments for simplicity --- src/cpulimit.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/cpulimit.c b/src/cpulimit.c index d0341205..22f63137 100644 --- a/src/cpulimit.c +++ b/src/cpulimit.c @@ -501,29 +501,14 @@ int main(int argc, char *argv[]) /* Handle command mode (run a command and limit its CPU usage) */ if (command_mode) { - int i; pid_t child; - /* Executable file */ - const char *cmd = argv[optind]; - /* Command line arguments */ - char **cmd_args = (char **)malloc((size_t)(argc - optind + 1) * sizeof(char *)); - if (cmd_args == NULL) - { - fprintf(stderr, "Memory allocation failed for cmd_args\n"); - exit(EXIT_FAILURE); - } - - /* Prepare command arguments */ - for (i = 0; i < argc - optind; i++) - { - cmd_args[i] = argv[i + optind]; - } - cmd_args[i] = NULL; + char *const *cmd_args = argv + optind; /* If verbose, print the command being executed */ if (verbose) { - printf("Running command: '%s", cmd); + int i; + printf("Running command: '%s", cmd_args[0]); for (i = 1; i < argc - optind; i++) { printf(" %s", cmd_args[i]); @@ -540,16 +525,14 @@ int main(int argc, char *argv[]) else if (child == 0) { /* Execute the command in the child process */ - int ret = execvp(cmd, cmd_args); + int ret = execvp(cmd_args[0], cmd_args); perror("Error"); /* Display error if execvp fails */ - free(cmd_args); exit(ret); } else { /* Parent process forks another limiter process to control CPU usage */ pid_t limiter; - free(cmd_args); limiter = fork(); if (limiter < 0) { From 0577a70874ea675dc3b25d7beecc4eecfe0ac336 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 21 Dec 2024 20:22:13 +0800 Subject: [PATCH 196/209] Fix a casting error --- src/process_iterator_linux.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/process_iterator_linux.c b/src/process_iterator_linux.c index 16cda9fb..dd5c2634 100644 --- a/src/process_iterator_linux.c +++ b/src/process_iterator_linux.c @@ -69,7 +69,7 @@ static int read_process_info(pid_t pid, struct process *p) double usertime, systime; long ppid; FILE *fd; - static double sc_clk_tck = -1.0; + static long sc_clk_tck = -1; p->pid = pid; @@ -103,9 +103,9 @@ static int read_process_info(pid_t pid, struct process *p) p->ppid = (pid_t)ppid; if (sc_clk_tck < 0) { - sc_clk_tck = (double)sysconf(_SC_CLK_TCK); + sc_clk_tck = sysconf(_SC_CLK_TCK); } - p->cputime = (usertime + systime) * 1000.0 / sc_clk_tck; + p->cputime = (usertime + systime) * 1000.0 / (double)sc_clk_tck; return 0; } From 05641f8186fd71f733b10c6259f0a15f16808b6d Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Sat, 21 Dec 2024 20:26:28 +0800 Subject: [PATCH 197/209] Remove unreachable code in `process_iterator_test.c` --- tests/process_iterator_test.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/process_iterator_test.c b/tests/process_iterator_test.c index 174f99ea..df8cfbf8 100644 --- a/tests/process_iterator_test.c +++ b/tests/process_iterator_test.c @@ -96,7 +96,6 @@ static void test_multiple_process(void) /* child is supposed to be killed by the parent :/ */ while (1) sleep(5); - exit(EXIT_SUCCESS); } process = (struct process *)malloc(sizeof(struct process)); assert(process != NULL); @@ -174,7 +173,6 @@ static void test_process_group_single(int include_children) increase_priority(); while (1) (void)unused_value; - exit(EXIT_SUCCESS); } assert(init_process_group(&pgroup, child, include_children) == 0); for (i = 0; i < 100; i++) From b330036304158ebe43ba748e3a22d38e5b021bda Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 26 Dec 2024 23:33:19 +0800 Subject: [PATCH 198/209] Improve Makefiles --- src/Makefile | 29 +++++++++++++++++++++-------- tests/Makefile | 29 +++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/Makefile b/src/Makefile index 371f7944..d2eec29d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -12,6 +12,24 @@ override CFLAGS := $(filter-out -std=% -ansi -W%, $(CFLAGS)) \ -Wstrict-prototypes \ -Werror +# If CHECK is set to 1, add stricter flags +ifeq ($(CHECK), 1) +override CFLAGS += -Wshadow \ + -Wfloat-equal \ + -Wformat=2 \ + -Wcast-align \ + -Wpointer-arith \ + -Wnull-dereference \ + -Wcast-qual \ + -Wmissing-declarations \ + -Wredundant-decls \ + -Wstrict-aliasing=2 \ + -Wswitch-enum \ + -Wswitch-default \ + -Wconversion \ + -Wsign-conversion +endif + # Target binary TARGET := cpulimit @@ -19,18 +37,13 @@ TARGET := cpulimit UNAME ?= $(shell uname) # Platform-specific linker flags -ifeq ($(UNAME), FreeBSD) - override LDFLAGS += -lkvm -endif - -ifeq ($(UNAME), Darwin) - override LDFLAGS += -lproc -endif +override LDFLAGS += $(if $(findstring FreeBSD, $(UNAME)), -lkvm,) \ + $(if $(findstring Darwin, $(UNAME)), -lproc,) # Check for librt availability override LDFLAGS += $(shell \ echo "int main(void){ return 0; }" | \ - $(CC) -x c -o /dev/null -lrt - 2>/dev/null && \ + $(CC) -x c -o /dev/null -lrt - >/dev/null 2>&1 && \ echo -lrt \ ) diff --git a/tests/Makefile b/tests/Makefile index 25bc54a3..e3d18e2a 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -12,6 +12,24 @@ override CFLAGS := $(filter-out -std=% -ansi -W%, $(CFLAGS)) \ -Wstrict-prototypes \ -Werror +# If CHECK is set to 1, add stricter flags +ifeq ($(CHECK), 1) +override CFLAGS += -Wshadow \ + -Wfloat-equal \ + -Wformat=2 \ + -Wcast-align \ + -Wpointer-arith \ + -Wnull-dereference \ + -Wcast-qual \ + -Wmissing-declarations \ + -Wredundant-decls \ + -Wstrict-aliasing=2 \ + -Wswitch-enum \ + -Wswitch-default \ + -Wconversion \ + -Wsign-conversion +endif + # Target binaries and source directory TARGETS := $(patsubst %.c, %, $(wildcard *.c)) SRC := ../src @@ -20,18 +38,13 @@ SRC := ../src UNAME ?= $(shell uname) # Platform-specific linker flags -ifeq ($(UNAME), FreeBSD) - override LDFLAGS += -lkvm -endif - -ifeq ($(UNAME), Darwin) - override LDFLAGS += -lproc -endif +override LDFLAGS += $(if $(findstring FreeBSD, $(UNAME)), -lkvm,) \ + $(if $(findstring Darwin, $(UNAME)), -lproc,) # Check for librt availability override LDFLAGS += $(shell \ echo "int main(void){ return 0; }" | \ - $(CC) -x c -o /dev/null -lrt - 2>/dev/null && \ + $(CC) -x c -o /dev/null -lrt - >/dev/null 2>&1 && \ echo -lrt \ ) From 70a49294d9bf16cf4de854dcb98b959a51d1f430 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 27 Dec 2024 12:37:24 +0800 Subject: [PATCH 199/209] CI: update --- .github/workflows/CI.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 5bb71470..9d49cfaf 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -30,10 +30,14 @@ jobs: - name: Compile and Test run: | + git clone https://github.com/jvoisin/fortify-headers if [[ "${{ matrix.os }}" == ubuntu-* ]]; then + export CFLAGS="$CFLAGS -I./fortify-headers -DFORTIFY_PEDANTIC_CHECKS -O3" export LDFLAGS="$LDFLAGS -static" + else + export CFLAGS="$CFLAGS -I./fortify-headers -D_FORTIFY_SOURCE=3 -DFORTIFY_PEDANTIC_CHECKS -O3" fi - make + make CHECK=1 sudo ./tests/process_iterator_test random_file="$(mktemp $(printf 'X%.0s' $(seq 1 255)))" cp ./tests/process_iterator_test ./tests/${random_file} @@ -65,9 +69,11 @@ jobs: release: ${{ matrix.osver }} usesh: true prepare: | - pkg install -y lang/gcc gmake sudo + pkg install -y lang/gcc gmake sudo git run: | - gmake + git clone https://github.com/jvoisin/fortify-headers + export CFLAGS="$CFLAGS -I./fortify-headers -D_FORTIFY_SOURCE=3 -DFORTIFY_PEDANTIC_CHECKS -O3" + gmake CHECK=1 sudo ./tests/process_iterator_test random_file="$(mktemp $(printf 'X%.0s' $(seq 1 255)))" cp ./tests/process_iterator_test ./tests/${random_file} From 53c96980b728bef2a87cf1a370d7c41133307468 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Thu, 2 Jan 2025 18:13:28 +0800 Subject: [PATCH 200/209] Fix a compilation error --- tests/busy.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/busy.c b/tests/busy.c index 5aa3d29f..c360eaa8 100644 --- a/tests/busy.c +++ b/tests/busy.c @@ -1,3 +1,7 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include #include From c01f289d408059d1b0f88bc7424e20be653e9342 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 3 Jan 2025 17:25:21 +0800 Subject: [PATCH 201/209] Fix a compilation error with uClibc --- src/util.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/util.h | 15 +++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/src/util.c b/src/util.c index 2749c538..78ccdcac 100644 --- a/src/util.c +++ b/src/util.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #if defined(__APPLE__) || defined(__FreeBSD__) #include #endif @@ -96,3 +98,53 @@ pid_t get_pid_max(void) #error "Platform not supported" #endif } + +#if defined(__linux__) && defined(__UCLIBC__) +int getloadavg(double *loadavg, int nelem) +{ + FILE *fp; + char buffer[65], *ptr; + int i; + + if (nelem < 0) + { + return -1; + } + else if (nelem == 0) + { + return 0; + } + else if (nelem > 3) + { + nelem = 3; + } + + if ((fp = fopen("/proc/loadavg", "r")) == NULL) + { + return -1; + } + + if (fgets(buffer, sizeof(buffer), fp) == NULL) + { + fclose(fp); + return -1; + } + fclose(fp); + + ptr = buffer; + + for (i = 0; i < nelem; i++) + { + char *endptr; + errno = 0; + loadavg[i] = strtod(ptr, &endptr); + if (errno != 0 || ptr == endptr) + { + return -1; + } + ptr = endptr; + } + + return nelem; +} +#endif diff --git a/src/util.h b/src/util.h index 486f3f65..fe2d1689 100644 --- a/src/util.h +++ b/src/util.h @@ -175,4 +175,19 @@ int get_ncpu(void); */ pid_t get_pid_max(void); +#if defined(__linux__) && defined(__UCLIBC__) +/** + * Retrieves up to nelem load averages for system processes over the + * last 1, 5, and 15 minutes. + * + * @param loadavg Pointer to an array for storing the load averages. + * It must have enough space for nelem samples. + * @param nelem Number of samples to retrieve (1 to 3). + * + * @return The number of samples retrieved, or -1 if the load + * average could not be obtained. + */ +int getloadavg(double *loadavg, int nelem); +#endif + #endif From 285be3275bd330f8f82357d504208bfd85ed4d22 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 7 Jan 2025 12:20:44 +0800 Subject: [PATCH 202/209] Define `inline` as `__inline` when using `-std=c89` --- src/process_iterator_freebsd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/process_iterator_freebsd.c b/src/process_iterator_freebsd.c index a89b67d1..9d73adaf 100644 --- a/src/process_iterator_freebsd.c +++ b/src/process_iterator_freebsd.c @@ -29,7 +29,7 @@ #endif #if defined(__STRICT_ANSI__) || !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) -#define inline +#define inline __inline #endif #include From 9d46beff08d951ca58e5b31b080fc478235d3b58 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 7 Jan 2025 18:38:51 +0800 Subject: [PATCH 203/209] Update Makefiles --- src/Makefile | 37 ++++++++++++++++++++++++++----------- tests/Makefile | 39 +++++++++++++++++++++++++++++---------- 2 files changed, 55 insertions(+), 21 deletions(-) diff --git a/src/Makefile b/src/Makefile index d2eec29d..e5a45e15 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,31 +4,46 @@ CC ?= gcc # Compiler flags override CFLAGS := $(filter-out -std=% -ansi -W%, $(CFLAGS)) \ -std=c89 \ + -Wpedantic \ -Wall \ -Wextra \ - -pedantic \ -Wold-style-definition \ -Wmissing-prototypes \ -Wstrict-prototypes \ + -Wdeclaration-after-statement \ -Werror # If CHECK is set to 1, add stricter flags ifeq ($(CHECK), 1) override CFLAGS += -Wshadow \ - -Wfloat-equal \ + -Wcast-qual \ + -Wconversion \ -Wformat=2 \ - -Wcast-align \ + -Wstrict-overflow=5 \ + -Wundef \ + -Wswitch-default \ + -Wswitch-enum \ + -Wunreachable-code \ + -Wfloat-equal \ -Wpointer-arith \ - -Wnull-dereference \ - -Wcast-qual \ - -Wmissing-declarations \ + -Wwrite-strings \ -Wredundant-decls \ + -Wmissing-declarations \ + -Winline \ + -Wcast-align \ -Wstrict-aliasing=2 \ - -Wswitch-enum \ - -Wswitch-default \ - -Wconversion \ - -Wsign-conversion -endif + -Wsign-conversion \ + -Wmissing-include-dirs \ + -Wunused \ + -Wbad-function-cast \ + -Waggregate-return \ + -Wmissing-format-attribute \ + -Wlarger-than=256 \ + -Wdisabled-optimization +ifeq ($(CC),gcc) + override CFLAGS += -Wunsafe-loop-optimizations +endif # $(CC) = gcc +endif # CHECK = 1 # Target binary TARGET := cpulimit diff --git a/tests/Makefile b/tests/Makefile index e3d18e2a..f44fbc5f 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -4,30 +4,49 @@ CC ?= gcc # Compiler flags override CFLAGS := $(filter-out -std=% -ansi -W%, $(CFLAGS)) \ -std=c89 \ + -Wpedantic \ -Wall \ -Wextra \ - -pedantic \ -Wold-style-definition \ -Wmissing-prototypes \ -Wstrict-prototypes \ + -Wdeclaration-after-statement \ -Werror # If CHECK is set to 1, add stricter flags ifeq ($(CHECK), 1) override CFLAGS += -Wshadow \ - -Wfloat-equal \ + -Wcast-qual \ + -Wconversion \ -Wformat=2 \ - -Wcast-align \ + -Wstrict-overflow=5 \ + -Wundef \ + -Wswitch-default \ + -Wswitch-enum \ + -Wunreachable-code \ + -Wfloat-equal \ -Wpointer-arith \ - -Wnull-dereference \ - -Wcast-qual \ - -Wmissing-declarations \ + -Wwrite-strings \ -Wredundant-decls \ + -Wmissing-declarations \ + -Winline \ + -Wcast-align \ -Wstrict-aliasing=2 \ - -Wswitch-enum \ - -Wswitch-default \ - -Wconversion \ - -Wsign-conversion + -Wsign-conversion \ + -Wmissing-include-dirs \ + -Wunused \ + -Wbad-function-cast \ + -Waggregate-return \ + -Wmissing-format-attribute \ + -Wlarger-than=256 \ + -Wdisabled-optimization +ifeq ($(CC),gcc) + override CFLAGS += -Wunsafe-loop-optimizations +endif # $(CC) = gcc +endif # CHECK = 1 + +ifeq ($(CC),gcc) + override CFLAGS += -Wunsafe-loop-optimizations endif # Target binaries and source directory From a355cafef51f7953382be598cd91a357c9f9d03d Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 7 Jan 2025 20:15:56 +0800 Subject: [PATCH 204/209] Fix `get_ncpu` function --- src/util.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/util.c b/src/util.c index 78ccdcac..3d9cdad9 100644 --- a/src/util.c +++ b/src/util.c @@ -9,8 +9,12 @@ #include #include #if defined(__APPLE__) || defined(__FreeBSD__) +#include #include #endif +#ifdef HAVE_SYS_SYSINFO_H +#include +#endif #include "util.h" #ifdef __IMPL_BASENAME @@ -51,23 +55,28 @@ void increase_priority(void) /* Get the number of CPUs */ int get_ncpu(void) { - int ncpu = -1; #if defined(_SC_NPROCESSORS_ONLN) - ncpu = (int)sysconf(_SC_NPROCESSORS_ONLN); -#elif defined(__APPLE__) + long ncpu = sysconf(_SC_NPROCESSORS_ONLN); + return ncpu > 0 ? (int)ncpu : 1; +#elif defined(__APPLE__) || defined(__FreeBSD__) + int ncpu = 0; int mib[2] = {CTL_HW, HW_AVAILCPU}; size_t len = sizeof(ncpu); - sysctl(mib, 2, &ncpu, &len, NULL, 0); -#elif defined(__FreeBSD__) - int mib[2]{CTL_HW, HW_NCPU}; - size_t len = sizeof(ncpu); - sysctl(mib, 2, &ncpu, &len, NULL, 0); -#elif defined(_GNU_SOURCE) - ncpu = get_nprocs(); + if (sysctl(mib, 2, &ncpu, &len, NULL, 0) != 0 || ncpu < 1) + { + mib[1] = HW_NCPU; + if (sysctl(mib, 2, &ncpu, &len, NULL, 0) != 0 || ncpu < 1) + { + return 1; + } + } + return ncpu; +#elif defined(__linux__) + int ncpu = get_nprocs(); + return ncpu > 0 ? ncpu : 1; #else -#error "Platform not supported" +#error "Unsupported platform" #endif - return ncpu; } pid_t get_pid_max(void) From e260e013a65a0db71b26de223234ba84ecc612ee Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 7 Jan 2025 23:52:08 +0800 Subject: [PATCH 205/209] Fix CI --- .github/workflows/CI.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 9d49cfaf..049a6371 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -32,10 +32,10 @@ jobs: run: | git clone https://github.com/jvoisin/fortify-headers if [[ "${{ matrix.os }}" == ubuntu-* ]]; then - export CFLAGS="$CFLAGS -I./fortify-headers -DFORTIFY_PEDANTIC_CHECKS -O3" + export CFLAGS="$CFLAGS -I../fortify-headers -DFORTIFY_PEDANTIC_CHECKS -O3" export LDFLAGS="$LDFLAGS -static" else - export CFLAGS="$CFLAGS -I./fortify-headers -D_FORTIFY_SOURCE=3 -DFORTIFY_PEDANTIC_CHECKS -O3" + export CFLAGS="$CFLAGS -I../fortify-headers -D_FORTIFY_SOURCE=3 -DFORTIFY_PEDANTIC_CHECKS -O3" fi make CHECK=1 sudo ./tests/process_iterator_test @@ -72,7 +72,7 @@ jobs: pkg install -y lang/gcc gmake sudo git run: | git clone https://github.com/jvoisin/fortify-headers - export CFLAGS="$CFLAGS -I./fortify-headers -D_FORTIFY_SOURCE=3 -DFORTIFY_PEDANTIC_CHECKS -O3" + export CFLAGS="$CFLAGS -I../fortify-headers -D_FORTIFY_SOURCE=3 -DFORTIFY_PEDANTIC_CHECKS -O3" gmake CHECK=1 sudo ./tests/process_iterator_test random_file="$(mktemp $(printf 'X%.0s' $(seq 1 255)))" From cfc83f763edb98ce3e78d03ade1cd9524e0f2428 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 10 Jan 2025 00:17:32 +0800 Subject: [PATCH 206/209] Add a forward declaration for `struct list_node` --- src/list.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/list.h b/src/list.h index 85d58355..1d31b082 100644 --- a/src/list.h +++ b/src/list.h @@ -26,6 +26,7 @@ #define _GNU_SOURCE #endif +struct list_node; /** * Structure representing a node in a doubly linked list. */ From 4338e175759f05dfaa48545fcb381345c10e0726 Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 10 Jan 2025 00:27:54 +0800 Subject: [PATCH 207/209] Fix includes --- src/process_group.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/process_group.h b/src/process_group.h index 05822e1b..dcf75891 100644 --- a/src/process_group.h +++ b/src/process_group.h @@ -27,9 +27,10 @@ #endif #include - -#include "process_iterator.h" +#include #include "list.h" +#include "process_iterator.h" +#include "process_table.h" /** * Structure representing a group of processes for tracking. From aa92df6ac3c41c8f30f271f5075fae546337405b Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Fri, 10 Jan 2025 01:07:01 +0800 Subject: [PATCH 208/209] Reduced scope of the variable `p` --- src/process_group.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/process_group.c b/src/process_group.c index 7b569b06..61646971 100644 --- a/src/process_group.c +++ b/src/process_group.c @@ -155,7 +155,7 @@ static struct process *process_dup(const struct process *proc) void update_process_group(struct process_group *pgroup) { struct process_iterator it; - struct process *tmp_process, *p; + struct process *tmp_process; struct process_filter filter; struct timespec now; double dt; @@ -179,7 +179,7 @@ void update_process_group(struct process_group *pgroup) while (get_next_process(&it, tmp_process) != -1) { - p = process_table_find(pgroup->proctable, tmp_process); + struct process *p = process_table_find(pgroup->proctable, tmp_process); if (p == NULL) { /* process is new. add it */ From 83b5c8b6d9068286a785aab8e918e67395bddd8f Mon Sep 17 00:00:00 2001 From: HiGarfield Date: Tue, 7 Jan 2025 22:16:49 +0800 Subject: [PATCH 209/209] Refine busy.c --- tests/busy.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/tests/busy.c b/tests/busy.c index c360eaa8..db6dd4fb 100644 --- a/tests/busy.c +++ b/tests/busy.c @@ -13,30 +13,43 @@ static void *loop(void *param __attribute__((unused))) { - volatile int unused_value = 0; while (1) - (void)unused_value; + ; return NULL; } int main(int argc, char *argv[]) { + int i, num_threads; + pthread_t *threads; + num_threads = argc == 2 ? atoi(argv[1]) : get_ncpu(); + num_threads = MAX(num_threads, 1); + + threads = (pthread_t *)malloc((size_t)num_threads * sizeof(pthread_t)); + if (threads == NULL) + { + fprintf(stderr, "malloc() failed.\n"); + exit(EXIT_FAILURE); + } - int i = 0; - int num_threads = get_ncpu(); increase_priority(); - if (argc == 2) - num_threads = atoi(argv[1]); - for (i = 0; i < num_threads - 1; i++) + + for (i = 0; i < num_threads; i++) { - pthread_t thread; int ret; - if ((ret = pthread_create(&thread, NULL, &loop, NULL)) != 0) + if ((ret = pthread_create(&threads[i], NULL, loop, NULL)) != 0) { - printf("pthread_create() failed. Error code %d\n", ret); + fprintf(stderr, "pthread_create() failed. Error code %d\n", ret); + free(threads); exit(EXIT_FAILURE); } } - loop(NULL); + + for (i = 0; i < num_threads; i++) + { + pthread_join(threads[i], NULL); + } + + free(threads); return 0; }