diff options
Diffstat (limited to 'src/procutils.c')
-rw-r--r-- | src/procutils.c | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/src/procutils.c b/src/procutils.c new file mode 100644 index 0000000..83f3e58 --- /dev/null +++ b/src/procutils.c @@ -0,0 +1,453 @@ +/** + * + * cpulimit - a cpu limiter for Linux + * + * Copyright (C) 2005-2008, by: Angelo Marletta <marlonx80@hotmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <limits.h> +#include <fcntl.h> +#include <sys/utsname.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "procutils.h" + +#ifdef __APPLE__ +#include <sys/sysctl.h> +#include <errno.h> +#endif + +/* PROCESS STATISTICS FUNCTIONS */ + +//deprecated +// returns pid of the parent process +static pid_t getppid_of(pid_t pid) +{ +#ifdef __linux__ + char file[20]; + char buffer[1024]; + sprintf(file, "/proc/%d/stat", pid); + FILE *fd = fopen(file, "r"); + if (fd==NULL) return -1; + if (fgets(buffer, sizeof(buffer), fd)==NULL) return -1; + fclose(fd); + char *p = buffer; + p = memchr(p+1,')', sizeof(buffer) - (p-buffer)); + int sp = 2; + while (sp--) + p = memchr(p+1,' ',sizeof(buffer) - (p-buffer)); + //pid of the parent process + pid_t ppid = atoi(p+1); + return ppid; +#elif defined __APPLE__ + struct process p; + get_proc_info(&p, pid); + return p.ppid; +#endif +} + +#ifdef __linux__ +// detects whether a process is a kernel thread or not +static int is_kernel_thread(pid_t pid) +{ + static char statfile[20]; + static char buffer[64]; + int ret; + sprintf(statfile, "/proc/%d/statm", pid); + FILE *fd = fopen(statfile, "r"); + if (fd==NULL) return -1; + if (fgets(buffer, sizeof(buffer), fd)==NULL) return -1; + ret = strncmp(buffer,"0 0 0",3)==0; + fclose(fd); + return ret; +} +#endif + +//deprecated +// returns 1 if pid is an existing pid, 0 otherwise +static int process_exists(pid_t pid) { +#ifdef __linux__ + static char procdir[20]; + struct stat procstat; + sprintf(procdir, "/proc/%d", pid); + return stat(procdir, &procstat)==0; +#elif defined __APPLE__ + struct process p; + return get_proc_info(&p, pid)==0; +#endif +} + +/* PID HASH FUNCTIONS */ + +static int hash_process(struct process_family *f, struct process *p) +{ + int ret; + struct list **l = &(f->proctable[pid_hashfn(p->pid)]); + if (*l == NULL) { + //there is no process in this hashtable item + //allocate the list + *l = (struct list*)malloc(sizeof(struct list)); + init_list(*l, 4); + add_elem(*l, p); + ret = 0; + f->count++; + } + else { + //list already exists + struct process *tmp = (struct process*)locate_elem(*l, p); + if (tmp != NULL) { + //update process info + memcpy(tmp, p, sizeof(struct process)); + free(p); + p = NULL; + ret = 1; + } + else { + //add new process + add_elem(*l, p); + ret = 0; + f->count++; + } + } + return ret; +} + +static void unhash_process(struct process_family *f, pid_t pid) { + //remove process from hashtable + struct list **l = &(f->proctable[pid_hashfn(pid)]); + if (*l == NULL) + return; //nothing done + struct list_node *node = locate_node(*l, &pid); + if (node != NULL) + destroy_node(*l, node); + f->count--; +} + +static struct process *seek_process(struct process_family *f, pid_t pid) +{ + struct list **l = &(f->proctable[pid_hashfn(pid)]); + return (*l != NULL) ? (struct process*)locate_elem(*l, &pid) : NULL; +} + +/* PROCESS ITERATOR STUFF */ + +// creates an object that browse all running processes +int init_process_iterator(struct process_iterator *i) { +#ifdef __linux__ + //open a directory stream to /proc directory + if ((i->dip = opendir("/proc")) == NULL) { + perror("opendir"); + return -1; + } +#elif defined __APPLE__ + + int err; + struct kinfo_proc *result = NULL; + size_t length; + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; + + /* We start by calling sysctl with result == NULL and length == 0. + That will succeed, and set length to the appropriate length. + We then allocate a buffer of that size and call sysctl again + with that buffer. + */ + length = 0; + err = sysctl(mib, 4, NULL, &length, NULL, 0); + if (err == -1) { + err = errno; + } + if (err == 0) { + result = malloc(length); + err = sysctl(mib, 4, result, &length, NULL, 0); + if (err == -1) + err = errno; + if (err == ENOMEM) { + free(result); /* clean up */ + result = NULL; + } + } + + i->procList = result; + i->count = err == 0 ? length / sizeof *result : 0; + i->c = 0; + +#endif + i->current = (struct process*)malloc(sizeof(struct process)); + memset(i->current, 0, sizeof(struct process)); + return 0; +} + +// reads the next user process from /process +// automatic closing if the end of the list is reached +int read_next_process(struct process_iterator *i) { +#ifdef __linux__ + pid_t pid = 0; +//TODO read this to port to other systems: http://www.steve.org.uk/Reference/Unix/faq_8.html#SEC85 + //read in from /proc and seek for process dirs + while ((i->dit = readdir(i->dip)) != NULL) { + if(strtok(i->dit->d_name, "0123456789") != NULL) + continue; + pid = atoi(i->dit->d_name); + if (is_kernel_thread(pid)) + continue; + //return the first found process + break; + } + if (pid == 0) { + //no more processes + closedir(i->dip); + free(i->current); + i->current = NULL; + return -1; + } + //read the executable link + char statfile[20]; + sprintf(statfile,"/proc/%d/cmdline",pid); + FILE *fd = fopen(statfile, "r"); + if (fd == NULL) return -1; + char buffer[1024]; + if (fgets(buffer, sizeof(buffer), fd)==NULL) return -2; + fclose(fd); + sscanf(buffer, "%s", (char*)&i->current->command); + i->current->pid = pid; + +#elif defined __APPLE__ + if (i->c >= i->count) { + //no more processes + free(i->procList); + i->procList = NULL; + free(i->current); + i->current = NULL; + return -1; + } + i->current->pid = i->procList[i->c].kp_proc.p_pid; + strncpy(i->current->command, i->procList[i->c].kp_proc.p_comm, MAXCOMLEN); +printf("%d %d %s\n", i->c, i->current->pid, i->current->command);//i->procList[i->c].kp_proc.p_comm); +//printf("%d %d %s\n", i->c, i->current->pid, i->proc[i->c].kp_proc.p_comm); + i->c++; +#endif + return 0; +} + +/* PUBLIC FUNCTIONS */ + +// search for all the processes derived from father and stores them +// in the process family struct +int create_process_family(struct process_family *f, pid_t father) +{ + //process list initialization (4 bytes key) + init_list(&(f->members), 4); + //hashtable initialization + memset(&(f->proctable), 0, sizeof(f->proctable)); + f->count = 0; + f->father = father; + //process iterator + struct process_iterator iter; + init_process_iterator(&iter); + int pid = 0; + while (read_next_process(&iter)==0) { + pid = iter.current->pid; + //check if process belongs to the family + int ppid = pid; + //TODO: optimize adding also these parents, and continue if process is already present + while(ppid!=1 && ppid!=father) { + ppid = getppid_of(ppid); + } + //allocate process descriptor + struct process *p = (struct process*)malloc(sizeof(struct process)); + //init process + process_init(p, pid); + if (ppid==1) { + //the init process + p->member = 0; + } + else if (pid != getpid()) { + //add to members (but exclude the current cpulimit process!) + p->member = 1; + add_elem(&(f->members), p); + } + //add to hashtable + hash_process(f, p); + } + return 0; +} + +// checks if there are new processes born in the specified family +// if any they are added to the members list +// the number of new born processes is returned +int update_process_family(struct process_family *f) +{ + int ret = 0; + //process iterator + struct process_iterator iter; + init_process_iterator(&iter); + int pid = 0; + while (read_next_process(&iter)==0) { + pid = iter.current->pid; + struct process *newp = seek_process(f, pid); + if (newp != NULL) continue; //already known //TODO: what if newp is a new process with the same PID?? + //the process is new, check if it belongs to the family + int ppid = getppid_of(pid); + //search the youngest known ancestor of the process + struct process *ancestor = NULL; + while((ancestor=seek_process(f, ppid))==NULL) { + ppid = getppid_of(ppid); + } + if (ancestor == NULL) { + //this should never happen! if does, find and correct the bug + fprintf(stderr, "Fatal bug! Process %d is without parent\n", pid); + exit(1); + } + //allocate and insert the process + struct process *p = (struct process*)malloc(sizeof(struct process)); + //init process + process_init(p, pid); + if (ancestor->member) { + //add to members + p->member = 1; + add_elem(&(f->members), p); + ret++; + } + else { + //not a member + p->member = 0; + } + //add to hashtable + hash_process(f, p); + } + return ret; +} + +// removes a process from the family by its pid +void remove_process_from_family(struct process_family *f, pid_t pid) +{ + struct list_node *node = locate_node(&(f->members), &pid); + if (node != NULL) { +// struct process *p = (struct process*)(node->data); +// free(p->history); +// p->history = NULL; + delete_node(&(f->members), node); + } + unhash_process(f, pid); +} + +// free the heap memory used by a process family +void cleanup_process_family(struct process_family *f) +{ + int i; + int size = sizeof(f->proctable) / sizeof(struct process*); + for (i=0; i<size; i++) { + if (f->proctable[i] != NULL) { + //free() history for each process + struct list_node *node = NULL; + for (node=f->proctable[i]->first; node!=NULL; node=node->next) { +// struct process *p = (struct process*)(node->data); +// free(p->history); +// p->history = NULL; + } + destroy_list(f->proctable[i]); + free(f->proctable[i]); + f->proctable[i] = NULL; + } + } + flush_list(&(f->members)); + f->count = 0; + f->father = 0; +} + +// look for a process by pid +// search_pid : pid of the wanted process +// return: pid of the found process, if it is found +// 0, if it's not found +// negative pid, if it is found but it's not possible to control it +int look_for_process_by_pid(pid_t pid) +{ + if (process_exists(pid)) + return (kill(pid,0)==0) ? pid : -pid; + return 0; +} + +// look for a process with a given name +// process: the name of the wanted process. it can be an absolute path name to the executable file +// or just the file name +// return: pid of the found process, if it is found +// 0, if it's not found +// negative pid, if it is found but it's not possible to control it +int look_for_process_by_name(const char *process_name) +{ + //whether the variable process_name is the absolute path or not + int is_absolute_path = process_name[0] == '/'; + //flag indicating if the a process with given name was found + int found = 0; + + //process iterator + struct process_iterator iter; + init_process_iterator(&iter); + pid_t pid = 0; + +printf("name\n"); + + while (read_next_process(&iter)==0) { + pid = iter.current->pid; + + int size = strlen(iter.current->command); + + found = 0; + if (is_absolute_path && strncmp(iter.current->command, process_name, size)==0 && size==strlen(process_name)) { + //process found + found = 1; + } + else { + //process found + if (strncmp(iter.current->command + size - strlen(process_name), process_name, strlen(process_name))==0) { + found = 1; + } + } + if (found==1) { + if (kill(pid,SIGCONT)==0) { + //process is ok! + break; + } + else { + //we don't have permission to send signal to that process + //so, don't exit from the loop and look for another one with the same name + found = -1; + } + } + } + if (found == 1) { + //ok, the process was found + return pid; + } + else if (found == 0) { + //no process found + return 0; + } + else if (found == -1) { + //the process was found, but we haven't permission to control it + return -pid; + } + //this MUST NOT happen + return 0; +} + |