mpd password
[monky] / top.c
1 /*
2  * Conky, a system monitor, based on torsmo
3  *
4  * This program is licensed under BSD license, read COPYING
5  *
6  *  $Id$
7  */
8
9 #include "top.h"
10
11 static regex_t *exclusion_expression = 0;
12 static unsigned int g_time = 0;
13 static int previous_total = 0;
14 static struct process *first_process = 0;
15
16 static struct process *find_process(pid_t pid)
17 {
18         struct process *p = first_process;
19         while (p) {
20                 if (p->pid == pid)
21                         return p;
22                 p = p->next;
23         }
24         return 0;
25 }
26
27 /*
28 * Create a new process object and insert it into the process list
29 */
30 static struct process *new_process(int p)
31 {
32         struct process *process;
33         process = malloc(sizeof(struct process));
34
35         /*
36          * Do stitching necessary for doubly linked list
37          */
38         process->name = 0;
39         process->previous = 0;
40         process->next = first_process;
41         if (process->next)
42                 process->next->previous = process;
43         first_process = process;
44
45         process->pid = p;
46         process->time_stamp = 0;
47         process->previous_user_time = INT_MAX;
48         process->previous_kernel_time = INT_MAX;
49         process->counted = 1;
50
51         /*    process_find_name(process); */
52
53         return process;
54 }
55
56 /******************************************/
57 /* Functions                              */
58 /******************************************/
59
60 static int process_parse_stat(struct process *);
61 static int update_process_table(void);
62 static int calculate_cpu(struct process *);
63 static void process_cleanup(void);
64 static void delete_process(struct process *);
65 /*inline void draw_processes(void);*/
66 static int calc_cpu_total(void);
67 static void calc_cpu_each(int);
68
69
70 /******************************************/
71 /* Extract information from /proc         */
72 /******************************************/
73
74 /*
75 * These are the guts that extract information out of /proc.
76 * Anyone hoping to port wmtop should look here first.
77 */
78 static int process_parse_stat(struct process *process)
79 {
80         struct information *cur;
81         cur = &info;
82         char line[BUFFER_LEN], filename[BUFFER_LEN], procname[BUFFER_LEN];
83         int ps;
84         int user_time, kernel_time;
85         int rc;
86         char *r, *q;
87         char deparenthesised_name[BUFFER_LEN];
88         int endl;
89         int nice_val;
90
91         snprintf(filename, sizeof(filename), PROCFS_TEMPLATE,
92                  process->pid);
93
94         ps = open(filename, O_RDONLY);
95         if (ps < 0)
96                 /*
97                  * The process must have finished in the last few jiffies!
98                  */
99                 return 1;
100
101         /*
102          * Mark process as up-to-date.
103          */
104         process->time_stamp = g_time;
105
106         rc = read(ps, line, sizeof(line));
107         close(ps);
108         if (rc < 0)
109                 return 1;
110
111         /*
112          * Extract cpu times from data in /proc filesystem
113          */
114         rc = sscanf(line,
115                     "%*s %s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %d %d %*s %*s %*s %d %*s %*s %*s %d %d",
116                     procname, &process->user_time, &process->kernel_time,
117                     &nice_val, &process->vsize, &process->rss);
118         if (rc < 5)
119                 return 1;
120         /*
121          * Remove parentheses from the process name stored in /proc/ under Linux...
122          */
123         r = procname + 1;
124         /* remove any "kdeinit: " */
125         if (r == strstr(r, "kdeinit")) {
126                 snprintf(filename, sizeof(filename),
127                          PROCFS_CMDLINE_TEMPLATE, process->pid);
128
129                 ps = open(filename, O_RDONLY);
130                 if (ps < 0)
131                         /*
132                          * The process must have finished in the last few jiffies!
133                          */
134                         return 1;
135
136                 endl = read(ps, line, sizeof(line));
137                 close(ps);
138
139                 /* null terminate the input */
140                 line[endl] = 0;
141                 /* account for "kdeinit: " */
142                 if ((char *) line == strstr(line, "kdeinit: "))
143                         r = ((char *) line) + 9;
144                 else
145                         r = (char *) line;
146
147                 q = deparenthesised_name;
148                 /* stop at space */
149                 while (*r && *r != ' ')
150                         *q++ = *r++;
151                 *q = 0;
152         } else {
153                 q = deparenthesised_name;
154                 while (*r && *r != ')')
155                         *q++ = *r++;
156                 *q = 0;
157         }
158
159         if (process->name)
160                 free(process->name);
161         process->name = strdup(deparenthesised_name);
162         process->rss *= getpagesize();
163
164         if (!cur->memmax)
165                 update_total_processes();
166
167         process->totalmem = ((float) process->rss / cur->memmax) / 10;
168
169         if (process->previous_user_time == INT_MAX)
170                 process->previous_user_time = process->user_time;
171         if (process->previous_kernel_time == INT_MAX)
172                 process->previous_kernel_time = process->kernel_time;
173
174         /* store the difference of the user_time */
175         user_time = process->user_time - process->previous_user_time;
176         kernel_time = process->kernel_time - process->previous_kernel_time;
177
178         /* backup the process->user_time for next time around */
179         process->previous_user_time = process->user_time;
180         process->previous_kernel_time = process->kernel_time;
181
182         /* store only the difference of the user_time here... */
183         process->user_time = user_time;
184         process->kernel_time = kernel_time;
185
186
187         return 0;
188 }
189
190 /******************************************/
191 /* Update process table                   */
192 /******************************************/
193
194 static int update_process_table()
195 {
196         DIR *dir;
197         struct dirent *entry;
198
199         if (!(dir = opendir("/proc")))
200                 return 1;
201
202         ++g_time;
203
204         /*
205          * Get list of processes from /proc directory
206          */
207         while ((entry = readdir(dir))) {
208                 pid_t pid;
209
210                 if (!entry) {
211                         /*
212                          * Problem reading list of processes
213                          */
214                         closedir(dir);
215                         return 1;
216                 }
217
218                 if (sscanf(entry->d_name, "%d", &pid) > 0) {
219                         struct process *p;
220                         p = find_process(pid);
221                         if (!p)
222                                 p = new_process(pid);
223
224                         /* compute each process cpu usage */
225                         calculate_cpu(p);
226                 }
227         }
228
229         closedir(dir);
230
231         return 0;
232 }
233
234 /******************************************/
235 /* Get process structure for process pid  */
236 /******************************************/
237
238 /*
239 * This function seems to hog all of the CPU time. I can't figure out why - it
240 * doesn't do much.
241 */
242 static int calculate_cpu(struct process *process)
243 {
244         int rc;
245
246         /* compute each process cpu usage by reading /proc/<proc#>/stat */
247         rc = process_parse_stat(process);
248         if (rc)
249                 return 1;
250         /*rc = process_parse_statm(process);
251            if (rc)
252            return 1; */
253
254         /*
255          * Check name against the exclusion list
256          */
257         if (process->counted && exclusion_expression
258             && !regexec(exclusion_expression, process->name, 0, 0, 0))
259                 process->counted = 0;
260
261         return 0;
262 }
263
264 /******************************************/
265 /* Strip dead process entries             */
266 /******************************************/
267
268 static void process_cleanup()
269 {
270
271         struct process *p = first_process;
272         while (p) {
273                 struct process *current = p;
274
275 #if defined(PARANOID)
276                 assert(p->id == 0x0badfeed);
277 #endif                          /* defined(PARANOID) */
278
279                 p = p->next;
280                 /*
281                  * Delete processes that have died
282                  */
283                 if (current->time_stamp != g_time)
284                         delete_process(current);
285         }
286 }
287
288 /******************************************/
289 /* Destroy and remove a process           */
290 /******************************************/
291
292 static void delete_process(struct process *p)
293 {
294 #if defined(PARANOID)
295         assert(p->id == 0x0badfeed);
296
297         /*
298          * Ensure that deleted processes aren't reused.
299          */
300         p->id = 0x007babe;
301 #endif                          /* defined(PARANOID) */
302
303         /*
304          * Maintain doubly linked list.
305          */
306         if (p->next)
307                 p->next->previous = p->previous;
308         if (p->previous)
309                 p->previous->next = p->next;
310         else
311                 first_process = p->next;
312
313         if (p->name)
314                 free(p->name);
315         free(p);
316 }
317
318 /******************************************/
319 /* Calculate cpu total                    */
320 /******************************************/
321
322 static int calc_cpu_total()
323 {
324         int total, t;
325         int rc;
326         int ps;
327         char line[BUFFER_LEN];
328         int cpu, nice, system, idle;
329
330         ps = open("/proc/stat", O_RDONLY);
331         rc = read(ps, line, sizeof(line));
332         close(ps);
333         if (rc < 0)
334                 return 0;
335         sscanf(line, "%*s %d %d %d %d", &cpu, &nice, &system, &idle);
336         total = cpu + nice + system + idle;
337
338         t = total - previous_total;
339         previous_total = total;
340
341         if (t < 0)
342                 t = 0;
343
344         return t;
345 }
346
347 /******************************************/
348 /* Calculate each processes cpu           */
349 /******************************************/
350
351 inline static void calc_cpu_each(int total)
352 {
353         struct process *p = first_process;
354         while (p) {
355                 /*p->amount = total ?
356                    (100.0 * (float) (p->user_time + p->kernel_time) /
357                    total) : 0; */
358                 p->amount =
359                     (100.0 * (p->user_time + p->kernel_time) / total);
360
361 /*              if (p->amount > 100)
362                 p->amount = 0;*/
363                 p = p->next;
364         }
365 }
366
367 /******************************************/
368 /* Find the top processes                 */
369 /******************************************/
370
371 /*
372 * Result is stored in decreasing order in best[0-9].
373 */
374
375 static struct process **sorttmp;
376 static size_t sorttmp_size = 10;
377
378 inline void process_find_top(struct process **cpu, struct process **mem)
379 {
380         struct process *pr;
381         if (sorttmp == NULL) {
382                 sorttmp = malloc(sizeof(struct process) * sorttmp_size);
383                 assert(sorttmp != NULL);
384         }
385         int total;
386         unsigned int i, max;
387
388         total = calc_cpu_total();       /* calculate the total of the processor */
389
390         update_process_table(); /* update the table with process list */
391         calc_cpu_each(total);   /* and then the percentage for each task */
392         process_cleanup();      /* cleanup list from exited processes */
393
394         /*
395          * this is really ugly,
396          * not to mention probably not too efficient.
397          * the main problem is that there could be any number of processes,
398          * however we have to use a fixed size for the "best" array.
399          * right now i can't think of a better way to do this,
400          * although i'm sure there is one.
401          * Perhaps just using a linked list would be more effecient?
402          * I'm too fucking lazy to do that right now.
403          */
404         if (top_cpu) {
405                 pr = first_process;
406                 i = 0;
407                 while (pr) {
408                         if (i < sorttmp_size && pr->counted) {
409                                 sorttmp[i] = pr;
410                                 i++;
411                         } else if (i == sorttmp_size && pr->counted) {
412                                 sorttmp_size++;
413                                 sorttmp =
414                                     realloc(sorttmp,
415                                             sizeof(struct process) *
416                                             sorttmp_size);
417                                 sorttmp[i] = pr;
418                                 i++;
419                         }
420                         pr = pr->next;
421                 }
422                 if (i + 1 < sorttmp_size) {
423                         sorttmp =
424                             realloc(sorttmp,
425                                     sizeof(struct process) * sorttmp_size);
426                 }
427                 max = i;
428                 for (i = 0; i < max - 1; i++) {
429                         while (sorttmp[i + 1]->amount > sorttmp[i]->amount) {
430                                 pr = sorttmp[i];
431                                 sorttmp[i] = sorttmp[i + 1];
432                                 sorttmp[i + 1] = pr;
433                                 if (i > 0)
434                                         i--;
435                                 else
436                                         break;
437                         }
438
439                 }
440                 for (i = max; i > 1; i--);
441                 {
442                         while (sorttmp[i]->amount > sorttmp[i - 1]->amount) {
443                                 pr = sorttmp[i];
444                                 sorttmp[i] = sorttmp[i - 1];
445                                 sorttmp[i - 1] = pr;
446                                 if (i < max)
447                                         i++;
448                                 else
449                                         break;
450                         }
451                 }
452                 for (i = 0; i < 10; i++) {
453                         cpu[i] = sorttmp[i];
454
455                 }
456         }
457         if (top_mem) {
458                 pr = first_process;
459                 i = 0;
460                 while (pr) {
461                         if (i < sorttmp_size && pr->counted) {
462                                 sorttmp[i] = pr;
463                                 i++;
464                         } else if (i == sorttmp_size && pr->counted) {
465                                 sorttmp_size++;
466                                 sorttmp =
467                                     realloc(sorttmp,
468                                             sizeof(struct process) *
469                                             sorttmp_size);
470                                 sorttmp[i] = pr;
471                                 i++;
472                         }
473                         pr = pr->next;
474                 }
475                 if (i + 1 < sorttmp_size) {
476                         sorttmp =
477                             realloc(sorttmp,
478                                     sizeof(struct process) * sorttmp_size);
479                 }
480                 max = i;
481                 for (i = 0; i < max - 1; i++) {
482                         while (sorttmp[i + 1]->totalmem >
483                                sorttmp[i]->totalmem) {
484                                 pr = sorttmp[i];
485                                 sorttmp[i] = sorttmp[i + 1];
486                                 sorttmp[i + 1] = pr;
487                                 if (i > 0)
488                                         i--;
489                                 else
490                                         break;
491                         }
492
493                 }
494                 for (i = max; i > 1; i--);
495                 {
496                         while (sorttmp[i]->totalmem >
497                                sorttmp[i - 1]->totalmem) {
498                                 pr = sorttmp[i];
499                                 sorttmp[i] = sorttmp[i - 1];
500                                 sorttmp[i - 1] = pr;
501                                 if (i < max)
502                                         i++;
503                                 else
504                                         break;
505                         }
506                 }
507                 for (i = 0; i < 10; i++) {
508                         mem[i] = sorttmp[i];
509
510                 }
511         }
512 }