[svn-upgrade] Integrating new upstream version, cpulimit (1.12~svn10)
[debian/cpulimit.git] / procutils.c
1 /**
2  *
3  * cpulimit - a cpu limiter for Linux
4  *
5  * Copyright (C) 2005-2008, by:  Angelo Marletta <marlonx80@hotmail.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20  */
21
22 #include <sys/utsname.h>
23 #include "procutils.h"
24
25 /* PROCESS STATISTICS FUNCTIONS */
26
27 // returns pid of the parent process
28 static int getppid_of(int pid)
29 {
30         char file[20];
31         char buffer[1024];
32         sprintf(file, "/proc/%d/stat", pid);
33         FILE *fd = fopen(file, "r");
34                 if (fd==NULL) return -1;
35         fgets(buffer, sizeof(buffer), fd);
36         fclose(fd);
37         char *p = buffer;
38         p = memchr(p+1,')', sizeof(buffer) - (p-buffer));
39         int sp = 2;
40         while (sp--)
41                 p = memchr(p+1,' ',sizeof(buffer) - (p-buffer));
42         //pid of the parent process
43         int ppid = atoi(p+1);
44         return ppid;
45 }
46
47 // returns the start time of a process (used with pid to identify a process)
48 static int get_starttime(int pid)
49 {
50         char file[20];
51         char buffer[1024];
52         sprintf(file, "/proc/%d/stat", pid);
53         FILE *fd = fopen(file, "r");
54                 if (fd==NULL) return -1;
55         fgets(buffer, sizeof(buffer), fd);
56         fclose(fd);
57         char *p = buffer;
58         p = memchr(p+1,')', sizeof(buffer) - (p-buffer));
59         int sp = 20;
60         while (sp--)
61                 p = memchr(p+1,' ',sizeof(buffer) - (p-buffer));
62         //start time of the process
63         int time = atoi(p+1);
64         return time;
65 }
66
67 // detects whether a process is a kernel thread or not
68 static int is_kernel_thread(int pid)
69 {
70         static char statfile[20];
71         static char buffer[64];
72         int ret;
73         sprintf(statfile, "/proc/%d/statm", pid);
74         FILE *fd = fopen(statfile, "r");
75         if (fd==NULL) return -1;
76         fgets(buffer, sizeof(buffer), fd);
77         ret = strncmp(buffer,"0 0 0",3)==0;
78         fclose(fd);
79         return ret;
80 }
81
82 // returns 1 if pid is a user process, 0 otherwise
83 static int process_exists(int pid) {
84         static char statfile[20];
85         static char buffer[64];
86         int ret;
87         sprintf(statfile, "/proc/%d/statm", pid);
88         FILE *fd = fopen(statfile, "r");
89         if (fd==NULL) return 0;
90         fgets(buffer, sizeof(buffer), fd);
91         ret = strncmp(buffer,"0 0 0",3)!=0;
92         fclose(fd);
93         return ret;
94 }
95
96 /* PID HASH FUNCTIONS */
97
98 static int hash_process(struct process_family *f, struct process *p)
99 {
100         int ret;
101         struct list **l = &(f->hashtable[pid_hashfn(p->pid)]);
102         if (*l == NULL) {
103                 //there is no process in this hashtable item
104                 //allocate the list
105                 *l = malloc(sizeof(struct list));
106                 init_list(*l, 4);
107                 add_elem(*l, p);
108                 ret = 0;
109                 f->count++;
110         }
111         else {
112                 //list already exists
113                 struct process *tmp = (struct process*)locate_elem(*l, p);
114                 if (tmp != NULL) {
115                         //update process info
116                         memcpy(tmp, p, sizeof(struct process));
117                         ret = 1;
118                 }
119                 else {
120                         //add new process
121                         add_elem(*l, p);
122                         ret = 0;
123                         f->count++;
124                 }
125         }
126         return ret;
127 }
128
129 static void unhash_process(struct process_family *f, int pid) {
130         //remove process from hashtable
131         struct list **l = &(f->hashtable[pid_hashfn(pid)]);
132         if (*l == NULL)
133                 return; //nothing done
134         struct list_node *node = locate_node(*l, &pid);
135         if (node != NULL)
136                 destroy_node(*l, node);
137         f->count--;
138 }
139
140 static struct process *find_process(struct process_family *f, int pid)
141 {
142         struct list **l = &(f->hashtable[pid_hashfn(pid)]);
143         return (*l != NULL) ? (struct process*)locate_elem(*l, &pid) : NULL;
144 }
145
146 /*
147 static int is_member(struct process_family *f, int pid) {
148         struct process *p = find_process(f, pid);
149         return (p!=NULL && p->member);
150 }
151
152 static int exists(struct process_family *f, int pid) {
153         struct process *p = find_process(f, pid);
154         return p!=NULL;
155 }
156 */
157
158 /* PROCESS ITERATOR STUFF */
159
160 // creates an object that browse all running processes
161 static int init_process_iterator(struct process_iterator *i) {
162         //open a directory stream to /proc directory
163         if ((i->dip = opendir("/proc")) == NULL) {
164                 perror("opendir");
165                 return -1;
166         }
167         return 0;
168 }
169
170 // reads the next user process from /process
171 // automatic closing if the end of the list is reached
172 static int read_next_process(struct process_iterator *i) {
173         //read in from /proc and seek for process dirs
174         int pid = 0;
175         while ((i->dit = readdir(i->dip)) != NULL) {
176                 pid = atoi(i->dit->d_name);
177                 if (pid<=0 || is_kernel_thread(pid))
178                         continue;
179                 //return the first found process
180                 break;
181         }
182         if (pid == 0) {
183                 //no more processes, release resources
184                 closedir(i->dip);
185         }
186         return pid;
187 }
188
189 /* PUBLIC FUNCTIONS */
190
191 // searches for all the processes derived from father and stores them
192 // in the process family struct
193 int create_process_family(struct process_family *f, int father)
194 {
195         //process list initialization
196         init_list(&(f->members), 4);
197         //hashtable initialization
198         memset(&(f->hashtable), 0, sizeof(f->hashtable));
199         f->count = 0;
200         f->father = father;
201         //process iterator
202         struct process_iterator iter;
203         init_process_iterator(&iter);
204         int pid = 0;
205         while ((pid = read_next_process(&iter))) {
206                 //check if process belongs to the family
207                 int ppid = pid;
208                 //TODO: optimize (add also parents)
209                 while(ppid!=1 && ppid!=father) {
210                         ppid = getppid_of(ppid);
211                 }
212                 //allocate and insert the process
213                 struct process *p = malloc(sizeof(struct process));
214                 p->pid = pid;
215                 p->starttime = get_starttime(pid);
216                 if (ppid==1) {
217                         p->member = 0;
218                 }
219                 else if (pid != getpid()) {
220                         //add to members (only if it's not the current cpulimit process!)
221                         p->member = 1;
222                         add_elem(&(f->members), p);
223                 }
224                 //init history
225                 p->history = malloc(sizeof(struct process_history));
226                 process_init(p->history, pid);
227                 //add to hashtable
228                 hash_process(f, p);
229         }
230         return 0;
231 }
232
233 // checks if there are new processes born in the specified family
234 // if any they are added to the members list
235 // the number of new born processes is returned
236 int check_new_members(struct process_family *f)
237 {
238         int ret = 0;
239         //process iterator
240         struct process_iterator iter;
241         init_process_iterator(&iter);
242         int pid = 0;
243         while ((pid = read_next_process(&iter))) {
244                 struct process *newp = find_process(f, pid);
245                 if (newp != NULL) continue; //already known
246                 //the process is new, check if it belongs to the family
247                 int ppid = getppid_of(pid);
248                 //search the youngest known ancestor of the process
249                 struct process *ancestor = NULL;
250                 while((ancestor=find_process(f, ppid))==NULL) {
251                         ppid = getppid_of(ppid);
252                 }
253                 if (ancestor == NULL) {
254                         //this should never happen! if does, find and correct the bug
255                         printf("Fatal bug! Process %d is without parent\n", pid);
256                         exit(1);
257                 }
258                 //allocate and insert the process
259                 struct process *p = malloc(sizeof(struct process));
260                 p->pid = pid;
261                 p->starttime = get_starttime(pid);
262                 p->member = 0;
263                 if (ancestor->member) {
264                         //add to members
265                         p->member = 1;
266                         add_elem(&(f->members), p);
267                         ret++;
268                 }
269                 //init history
270                 p->history = malloc(sizeof(struct process_history));
271                 process_init(p->history, pid);
272                 //add to hashtable
273                 hash_process(f, p);
274         }
275         return ret;
276 }
277
278 // removes a process from the family by its pid
279 void remove_process_from_family(struct process_family *f, int pid)
280 {
281         struct list_node *node = locate_node(&(f->members), &pid);
282         if (node != NULL) {
283                 struct process *p = (struct process*)(node->data);
284                 free(p->history);
285                 p->history = NULL;
286                 destroy_node(&(f->members), node);
287         }
288         unhash_process(f, pid);
289 }
290
291 // free the heap memory used by a process family
292 void cleanup_process_family(struct process_family *f)
293 {
294         int i;
295         int size = sizeof(f->hashtable) / sizeof(struct process*);
296         for (i=0; i<size; i++) {
297                 if (f->hashtable[i] != NULL) {
298                         //free() history for each process
299                         struct list_node *node = NULL;
300                         for (node=f->hashtable[i]->first; node!=NULL; node=node->next) {
301                                 struct process *p = (struct process*)(node->data);
302                                 free(p->history);
303                                 p->history = NULL;
304                         }
305                         destroy_list(f->hashtable[i]);
306                         free(f->hashtable[i]);
307                         f->hashtable[i] = NULL;
308                 }
309         }
310         flush_list(&(f->members));
311         f->count = 0;
312         f->father = 0;
313 }
314
315 // look for a process by pid
316 // search_pid   : pid of the wanted process
317 // return:  pid of the found process, if it is found
318 //          0, if it's not found
319 //          negative pid, if it is found but it's not possible to control it
320 int look_for_process_by_pid(int pid)
321 {
322         if (process_exists(pid))
323                 return (kill(pid,SIGCONT)==0) ? pid : -pid;
324         return 0;
325 }
326
327 // look for a process with a given name
328 // process: the name of the wanted process. it can be an absolute path name to the executable file
329 //         or just the file name
330 // return:  pid of the found process, if it is found
331 //         0, if it's not found
332 //         negative pid, if it is found but it's not possible to control it
333 int look_for_process_by_name(const char *process)
334 {
335         //the name of /proc/pid/exe symbolic link pointing to the executable file
336         char exelink[20];
337         //the name of the executable file
338         char exepath[PATH_MAX+1];
339         //whether the variable process is the absolute path or not
340         int is_absolute_path = process[0] == '/';
341         //flag indicating if the a process with given name was found
342         int found = 0;
343
344         //process iterator
345         struct process_iterator iter;
346         init_process_iterator(&iter);
347         int pid = 0;
348         while ((pid = read_next_process(&iter))) {
349                 //read the executable link
350                 sprintf(exelink,"/proc/%d/exe",pid);
351                 int size = readlink(exelink, exepath, sizeof(exepath));
352                 if (size>0) {
353                         found = 0;
354                         if (is_absolute_path && strncmp(exepath, process, size)==0 && size==strlen(process)) {
355                                 //process found
356                                 found = 1;
357                         }
358                         else {
359                                 //process found
360                                 if (strncmp(exepath + size - strlen(process), process, strlen(process))==0) {
361                                         found = 1;
362                                 }
363                         }
364                         if (found==1) {
365                                 if (kill(pid,SIGCONT)==0) {
366                                         //process is ok!
367                                         break;
368                                 }
369                                 else {
370                                         //we don't have permission to send signal to that process
371                                         //so, don't exit from the loop and look for another one with the same name
372                                         found = -1;
373                                 }
374                         }
375                 }
376         }
377         if (found == 1) {
378                 //ok, the process was found
379                 return pid;
380         }
381         else if (found == 0) {
382                 //no process found
383                 return 0;
384         }
385         else if (found == -1) {
386                 //the process was found, but we haven't permission to control it
387                 return -pid;
388         }
389         //this MUST NOT happen
390         return 0;
391 }