fix stale static process pointer in top.c and add nodename to X window
[monky] / src / 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 long g_time = 0;
13 static unsigned long previous_total = 0;
14 static struct process *first_process = 0;
15
16 struct process *get_first_process()
17 {
18         return first_process;
19 }
20
21
22
23 void free_all_processes()
24 {
25         struct process *next = NULL, *pr = first_process;
26         while (pr) {
27                 next = pr->next;
28                 if (pr->name) {
29                         free(pr->name);
30                 }
31                 free(pr);
32                 pr = next;
33         }
34         first_process = NULL;
35 }
36
37 static struct process *find_process(pid_t pid)
38 {
39         struct process *p = first_process;
40         while (p) {
41                 if (p->pid == pid)
42                         return p;
43                 p = p->next;
44         }
45         return 0;
46 }
47
48 /*
49 * Create a new process object and insert it into the process list
50 */
51 static struct process *new_process(int p)
52 {
53         struct process *process;
54         process = (struct process*)malloc(sizeof(struct process));
55
56         // clean up memory first
57         memset(process, 0, sizeof(struct process));
58
59         /*
60          * Do stitching necessary for doubly linked list
61          */
62         process->name = 0;
63         process->previous = 0;
64         process->next = first_process;
65         if (process->next)
66                 process->next->previous = process;
67         first_process = process;
68
69         process->pid = p;
70         process->time_stamp = 0;
71         process->previous_user_time = ULONG_MAX;
72         process->previous_kernel_time = ULONG_MAX;
73         process->counted = 1;
74
75         
76         /*    process_find_name(process); */
77
78         return process;
79 }
80
81 /******************************************/
82 /* Functions                              */
83 /******************************************/
84
85 static int process_parse_stat(struct process *);
86 static int update_process_table(void);
87 static int calculate_cpu(struct process *);
88 static void process_cleanup(void);
89 static void delete_process(struct process *);
90 /*inline void draw_processes(void);*/
91 static unsigned long calc_cpu_total(void);
92 static void calc_cpu_each(unsigned long);
93
94
95 /******************************************/
96 /* Extract information from /proc         */
97 /******************************************/
98
99 /*
100 * These are the guts that extract information out of /proc.
101 * Anyone hoping to port wmtop should look here first.
102 */
103 static int process_parse_stat(struct process *process)
104 {
105         struct information *cur;
106         cur = &info;
107         char line[BUFFER_LEN], filename[BUFFER_LEN], procname[BUFFER_LEN];
108         int ps;
109         unsigned long user_time = 0;
110         unsigned long kernel_time = 0;
111         int rc;
112         char *r, *q;
113         char deparenthesised_name[BUFFER_LEN];
114         int endl;
115         int nice_val;
116
117         snprintf(filename, sizeof(filename), PROCFS_TEMPLATE,
118                  process->pid);
119
120         ps = open(filename, O_RDONLY);
121         if (ps < 0)
122                 /*
123                  * The process must have finished in the last few jiffies!
124                  */
125                 return 1;
126
127         /*
128          * Mark process as up-to-date.
129          */
130         process->time_stamp = g_time;
131
132         rc = read(ps, line, sizeof(line));
133         close(ps);
134         if (rc < 0)
135                 return 1;
136
137         /*
138          * Extract cpu times from data in /proc filesystem
139          */
140         rc = sscanf(line,
141                     "%*s %s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %lu %lu %*s %*s %*s %d %*s %*s %*s %d %d",
142                     procname, &process->user_time, &process->kernel_time,
143                     &nice_val, &process->vsize, &process->rss);
144         if (rc < 5)
145                 return 1;
146         /*
147          * Remove parentheses from the process name stored in /proc/ under Linux...
148          */
149         r = procname + 1;
150         /* remove any "kdeinit: " */
151         if (r == strstr(r, "kdeinit")) {
152                 snprintf(filename, sizeof(filename),
153                          PROCFS_CMDLINE_TEMPLATE, process->pid);
154
155                 ps = open(filename, O_RDONLY);
156                 if (ps < 0)
157                         /*
158                          * The process must have finished in the last few jiffies!
159                          */
160                         return 1;
161
162                 endl = read(ps, line, sizeof(line));
163                 close(ps);
164
165                 /* null terminate the input */
166                 line[endl] = 0;
167                 /* account for "kdeinit: " */
168                 if ((char *) line == strstr(line, "kdeinit: "))
169                         r = ((char *) line) + 9;
170                 else
171                         r = (char *) line;
172
173                 q = deparenthesised_name;
174                 /* stop at space */
175                 while (*r && *r != ' ')
176                         *q++ = *r++;
177                 *q = 0;
178         } else {
179                 q = deparenthesised_name;
180                 while (*r && *r != ')')
181                         *q++ = *r++;
182                 *q = 0;
183         }
184
185         if (process->name) {
186                 free(process->name);
187         }
188         process->name = strdup(deparenthesised_name);
189         process->rss *= getpagesize();
190
191         if (!cur->memmax)
192                 update_total_processes();
193
194
195
196         process->totalmem = (float)(((float) process->rss / cur->memmax) / 10);
197         if (process->previous_user_time == ULONG_MAX)
198                 process->previous_user_time = process->user_time;
199         if (process->previous_kernel_time == ULONG_MAX)
200                 process->previous_kernel_time = process->kernel_time;
201
202         /* store the difference of the user_time */
203         user_time = process->user_time - process->previous_user_time;
204         kernel_time = process->kernel_time - process->previous_kernel_time;
205
206         /* backup the process->user_time for next time around */
207         process->previous_user_time = process->user_time;
208         process->previous_kernel_time = process->kernel_time;
209
210         /* store only the difference of the user_time here... */
211         process->user_time = user_time;
212         process->kernel_time = kernel_time;
213
214
215         return 0;
216 }
217
218 /******************************************/
219 /* Update process table                   */
220 /******************************************/
221
222 static int update_process_table()
223 {
224         DIR *dir;
225         struct dirent *entry;
226
227         if (!(dir = opendir("/proc")))
228                 return 1;
229
230         ++g_time;
231
232         /*
233          * Get list of processes from /proc directory
234          */
235         while ((entry = readdir(dir))) {
236                 pid_t pid;
237
238                 if (!entry) {
239                         /*
240                          * Problem reading list of processes
241                          */
242                         closedir(dir);
243                         return 1;
244                 }
245
246                 if (sscanf(entry->d_name, "%d", &pid) > 0) {
247                         struct process *p;
248                         p = find_process(pid);
249                         if (!p)
250                                 p = new_process(pid);
251
252                         /* compute each process cpu usage */
253                         calculate_cpu(p);
254                 }
255         }
256
257         closedir(dir);
258
259         return 0;
260 }
261
262 /******************************************/
263 /* Get process structure for process pid  */
264 /******************************************/
265
266 /*
267 * This function seems to hog all of the CPU time. I can't figure out why - it
268 * doesn't do much.
269 */
270 static int calculate_cpu(struct process *process)
271 {
272         int rc;
273
274         /* compute each process cpu usage by reading /proc/<proc#>/stat */
275         rc = process_parse_stat(process);
276         if (rc)
277                 return 1;
278         /*rc = process_parse_statm(process);
279            if (rc)
280            return 1; */
281
282         /*
283          * Check name against the exclusion list
284          */
285         if (process->counted && exclusion_expression
286             && !regexec(exclusion_expression, process->name, 0, 0, 0))
287                 process->counted = 0;
288
289         return 0;
290 }
291
292 /******************************************/
293 /* Strip dead process entries             */
294 /******************************************/
295
296 static void process_cleanup()
297 {
298
299         struct process *p = first_process;
300         while (p) {
301                 struct process *current = p;
302
303 #if defined(PARANOID)
304                 assert(p->id == 0x0badfeed);
305 #endif                          /* defined(PARANOID) */
306
307                 p = p->next;
308                 /*
309                  * Delete processes that have died
310                  */
311                 if (current->time_stamp != g_time)
312                         delete_process(current);
313         }
314 }
315
316 /******************************************/
317 /* Destroy and remove a process           */
318 /******************************************/
319
320 static void delete_process(struct process *p)
321 {
322 #if defined(PARANOID)
323         assert(p->id == 0x0badfeed);
324
325         /*
326          * Ensure that deleted processes aren't reused.
327          */
328         p->id = 0x007babe;
329 #endif                          /* defined(PARANOID) */
330
331         /*
332          * Maintain doubly linked list.
333          */
334         if (p->next)
335                 p->next->previous = p->previous;
336         if (p->previous)
337                 p->previous->next = p->next;
338         else
339                 first_process = p->next;
340
341         if (p->name) {
342                 free(p->name);
343         }
344         free(p);
345 }
346
347 /******************************************/
348 /* Calculate cpu total                    */
349 /******************************************/
350
351 static unsigned long calc_cpu_total()
352 {
353         unsigned long total = 0;
354         unsigned long t = 0;
355         int rc;
356         int ps;
357         char line[BUFFER_LEN];
358         unsigned long cpu = 0;
359         unsigned long nice = 0;
360         unsigned long system = 0;
361         unsigned long idle = 0;
362
363         ps = open("/proc/stat", O_RDONLY);
364         rc = read(ps, line, sizeof(line));
365         close(ps);
366         if (rc < 0)
367                 return 0;
368         sscanf(line, "%*s %lu %lu %lu %lu", &cpu, &nice, &system, &idle);
369         total = cpu + nice + system + idle;
370
371         t = total - previous_total;
372         previous_total = total;
373
374
375         return t;
376 }
377
378 /******************************************/
379 /* Calculate each processes cpu           */
380 /******************************************/
381
382 inline static void calc_cpu_each(unsigned long total)
383 {
384         struct process *p = first_process;
385         while (p) {
386                 /*p->amount = total ?
387                    (100.0 * (float) (p->user_time + p->kernel_time) /
388                    total) : 0; */
389                 p->amount =
390                     (100.0 * (p->user_time + p->kernel_time) / total);
391
392 /*              if (p->amount > 100)
393                 p->amount = 0;*/
394                 p = p->next;
395         }
396 }
397
398 /******************************************/
399 /* Find the top processes                 */
400 /******************************************/
401
402 //static int tot_struct;  //for debugging..uncomment this and the 2 printfs in the next two functs
403
404 /*
405  * free a  sp_process structure
406 */
407 void free_sp(struct sorted_process * sp) {
408         free(sp);
409 //      printf("free: %d structs\n",--tot_struct );
410 }
411
412 /*
413  * create a new sp_process structure
414 */
415 struct sorted_process * malloc_sp(struct process * proc) {
416         struct sorted_process * sp;
417         sp = malloc(sizeof(struct sorted_process));
418         sp->greater = NULL;
419         sp->less = NULL;
420         sp->proc = proc;
421 //      printf("malloc: %d structs\n", ++tot_struct);
422         return(sp);
423
424
425 /*
426  * cpu comparison function for insert_sp_element 
427  */
428 int compare_cpu(struct process *a, struct process *b) {
429         if (a->amount < b->amount) return 1; 
430         return 0;
431 }
432
433 /*
434  * mem comparison function for insert_sp_element 
435  */
436 int compare_mem(struct process *a, struct process *b) {
437         if (a->totalmem < b->totalmem) return 1; 
438         return 0;
439 }
440
441 /*
442  * insert this process into the list in a sorted fashion,
443  * or destroy it if it doesn't fit on the list
444 */ 
445 int insert_sp_element(
446                      struct sorted_process * sp_cur
447                    , struct sorted_process ** p_sp_head
448                    , struct sorted_process ** p_sp_tail
449                    , int max_elements
450                    , int (*compare_funct) (struct process *, struct process *)
451                   ) {
452
453         struct sorted_process * sp_readthru=NULL, * sp_destroy=NULL;
454         int did_insert = 0, x = 0;
455
456         if (*p_sp_head == NULL) {
457                 *p_sp_head = sp_cur;
458                 *p_sp_tail = sp_cur;
459                 return(1);
460         }
461         for(sp_readthru=*p_sp_head, x=0; sp_readthru != NULL && x < max_elements; sp_readthru=sp_readthru->less, x++) {
462                 if (compare_funct(sp_readthru->proc, sp_cur->proc) && !did_insert) {
463                         /* sp_cur is bigger than sp_readthru so insert it before sp_readthru */
464                         sp_cur->less=sp_readthru;
465                         if (sp_readthru == *p_sp_head) { 
466                                 *p_sp_head = sp_cur;  /* insert as the new head of the list */
467                         } else {
468                                 sp_readthru->greater->less = sp_cur;  /* insert inside  the list */
469                                 sp_cur->greater = sp_readthru->greater; 
470                         }
471                         sp_readthru->greater=sp_cur;
472                         did_insert = ++x;  /* element was inserted, so increase the counter */
473                 }
474         }
475         if (x < max_elements && sp_readthru == NULL && !did_insert) {
476                 /* sp_cur is the smallest element and list isn't full, so insert at the end */  
477                 (*p_sp_tail)->less=sp_cur;
478                 sp_cur->greater=*p_sp_tail;
479                 *p_sp_tail = sp_cur;
480                 did_insert=x;
481         } else if (x >= max_elements) {
482                 /* we inserted an element and now the list is too big by one. Destroy the smallest element */
483                 sp_destroy = *p_sp_tail;
484                 *p_sp_tail = sp_destroy->greater;
485                 (*p_sp_tail)->less = NULL;
486                 free_sp(sp_destroy);
487         }
488         if (!did_insert) {
489                 /* sp_cur wasn't added to the sorted list, so destroy it */
490                 free_sp(sp_cur);
491         }
492         return did_insert;
493 }
494   
495 /*
496  * copy the procs in the sorted list to the array, and destroy the list 
497  */
498 void sp_acopy(struct sorted_process *sp_head, struct process ** ar, int max_size)
499 {
500         struct sorted_process * sp_cur, * sp_tmp;
501         int x;
502         sp_cur = sp_head;
503         for (x=0; x < max_size && sp_cur != NULL; x++) {
504                 ar[x] = sp_cur->proc;   
505                 sp_tmp = sp_cur;
506                 sp_cur= sp_cur->less;
507                 free_sp(sp_tmp);        
508         }
509 }
510
511 // stole from common.c
512 #define NEED(a) ((need_mask & (1 << a)) && ((info.mask & (1 << a)) == 0))
513
514 /* ****************************************************************** */
515 /* Get a sorted list of the top cpu hogs and top mem hogs.            */
516 /* Results are stored in the cpu,mem arrays in decreasing order[0-9]. */
517 /* ****************************************************************** */
518
519 inline void process_find_top(struct process **cpu, struct process **mem)
520 {
521         struct sorted_process *spc_head = NULL, *spc_tail = NULL, *spc_cur = NULL;
522         struct sorted_process *spm_head = NULL, *spm_tail = NULL, *spm_cur = NULL;
523         struct process *cur_proc = NULL;
524         unsigned long total = 0;
525
526         if (!top_cpu && !top_mem) return;
527
528         total = calc_cpu_total();       /* calculate the total of the processor */
529         update_process_table();         /* update the table with process list */
530         calc_cpu_each(total);           /* and then the percentage for each task */
531         process_cleanup();              /* cleanup list from exited processes */
532         
533         cur_proc = first_process;
534
535         while (cur_proc !=NULL) {
536                 //printf("\n\n cur_proc: %s %f %f\n",cur_proc->name, cur_proc->totalmem, cur_proc->amount );
537                 if (top_cpu) {
538                         spc_cur = malloc_sp(cur_proc);
539                         insert_sp_element(spc_cur, &spc_head, &spc_tail, MAX_SP, &compare_cpu);
540                 }
541                 if (top_mem) {
542                         spm_cur = malloc_sp(cur_proc);
543                         insert_sp_element(spm_cur, &spm_head, &spm_tail, MAX_SP, &compare_mem);
544                 }
545                 cur_proc = cur_proc->next;
546         }
547         sp_acopy(spc_head, cpu, MAX_SP);
548         sp_acopy(spm_head, mem, MAX_SP);
549 }
550