aboutsummaryrefslogtreecommitdiffstats
path: root/src/procutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/procutils.c')
-rw-r--r--src/procutils.c453
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;
+}
+
Un proyecto texto-plano.xyz