processes plugin: Updated copyright information.
[collectd.git] / src / processes.c
1 /**
2  * collectd - src/processes.c
3  * Copyright (C) 2005  Lyonel Vincent
4  * Copyright (C) 2006-2008  Florian Forster (Mach code)
5  * Copyright (C) 2008  Oleg King
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation; either version 2 of the License, or (at your
10  * option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20  *
21  * Authors:
22  *   Lyonel Vincent <lyonel at ezix.org>
23  *   Florian octo Forster <octo at verplant.org>
24  *   Oleg King <king2 at kaluga.ru>
25  **/
26
27 #include "collectd.h"
28 #include "common.h"
29 #include "plugin.h"
30 #include "configfile.h"
31
32 /* Include header files for the mach system, if they exist.. */
33 #if HAVE_THREAD_INFO
34 #  if HAVE_MACH_MACH_INIT_H
35 #    include <mach/mach_init.h>
36 #  endif
37 #  if HAVE_MACH_HOST_PRIV_H
38 #    include <mach/host_priv.h>
39 #  endif
40 #  if HAVE_MACH_MACH_ERROR_H
41 #    include <mach/mach_error.h>
42 #  endif
43 #  if HAVE_MACH_MACH_HOST_H
44 #    include <mach/mach_host.h>
45 #  endif
46 #  if HAVE_MACH_MACH_PORT_H
47 #    include <mach/mach_port.h>
48 #  endif
49 #  if HAVE_MACH_MACH_TYPES_H
50 #    include <mach/mach_types.h>
51 #  endif
52 #  if HAVE_MACH_MESSAGE_H
53 #    include <mach/message.h>
54 #  endif
55 #  if HAVE_MACH_PROCESSOR_SET_H
56 #    include <mach/processor_set.h>
57 #  endif
58 #  if HAVE_MACH_TASK_H
59 #    include <mach/task.h>
60 #  endif
61 #  if HAVE_MACH_THREAD_ACT_H
62 #    include <mach/thread_act.h>
63 #  endif
64 #  if HAVE_MACH_VM_REGION_H
65 #    include <mach/vm_region.h>
66 #  endif
67 #  if HAVE_MACH_VM_MAP_H
68 #    include <mach/vm_map.h>
69 #  endif
70 #  if HAVE_MACH_VM_PROT_H
71 #    include <mach/vm_prot.h>
72 #  endif
73 #  if HAVE_SYS_SYSCTL_H
74 #    include <sys/sysctl.h>
75 #  endif
76 /* #endif HAVE_THREAD_INFO */
77
78 #elif KERNEL_LINUX
79 #  if HAVE_LINUX_CONFIG_H
80 #    include <linux/config.h>
81 #  endif
82 #  ifndef CONFIG_HZ
83 #    define CONFIG_HZ 100
84 #  endif
85 /* #endif KERNEL_LINUX */
86
87 #elif HAVE_KVM_H
88 #  include <kvm.h>
89 #  include <sys/user.h>
90 #  include <sys/proc.h>
91 #  if HAVE_SYS_SYSCTL_H
92 #    include <sys/sysctl.h>
93 #  endif
94 /* #endif HAVE_KVM_H */
95
96 #else
97 # error "No applicable input method."
98 #endif
99
100 #if HAVE_REGEX_H
101 # include <regex.h>
102 #endif
103
104 #define BUFSIZE 256
105
106 static const char *config_keys[] =
107 {
108         "Process",
109         "ProcessMatch",
110         NULL
111 };
112 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
113
114 typedef struct procstat_entry_s
115 {
116         unsigned long id;
117         unsigned long age;
118
119         unsigned long num_proc;
120         unsigned long num_lwp;
121         unsigned long vmem_rss;
122
123         unsigned long vmem_minflt;
124         unsigned long vmem_majflt;
125         unsigned long vmem_minflt_counter;
126         unsigned long vmem_majflt_counter;
127
128         unsigned long cpu_user;
129         unsigned long cpu_system;
130         unsigned long cpu_user_counter;
131         unsigned long cpu_system_counter;
132
133         struct procstat_entry_s *next;
134 } procstat_entry_t;
135
136 #define PROCSTAT_NAME_LEN 256
137 typedef struct procstat
138 {
139         char          name[PROCSTAT_NAME_LEN];
140 #if HAVE_REGEX_H
141         regex_t *re;
142 #endif
143
144         unsigned long num_proc;
145         unsigned long num_lwp;
146         unsigned long vmem_rss;
147
148         unsigned long vmem_minflt_counter;
149         unsigned long vmem_majflt_counter;
150
151         unsigned long cpu_user_counter;
152         unsigned long cpu_system_counter;
153
154         struct procstat   *next;
155         struct procstat_entry_s *instances;
156 } procstat_t;
157
158 static procstat_t *list_head_g = NULL;
159
160 #if HAVE_THREAD_INFO
161 static mach_port_t port_host_self;
162 static mach_port_t port_task_self;
163
164 static processor_set_name_array_t pset_list;
165 static mach_msg_type_number_t     pset_list_len;
166 /* #endif HAVE_THREAD_INFO */
167
168 #elif KERNEL_LINUX
169 static long pagesize_g;
170 #endif /* KERNEL_LINUX */
171
172 /* put name of process from config to list_head_g tree
173    list_head_g is a list of 'procstat_t' structs with
174    processes names we want to watch */
175 static void ps_list_register (const char *name, const char *regexp)
176 {
177         procstat_t *new;
178         procstat_t *ptr;
179         int status;
180
181         new = (procstat_t *) malloc (sizeof (procstat_t));
182         if (new == NULL)
183         {
184                 ERROR ("processes plugin: ps_list_register: malloc failed.");
185                 return;
186         }
187         memset (new, 0, sizeof (procstat_t));
188         sstrncpy (new->name, name, sizeof (new->name));
189
190 #if HAVE_REGEX_H
191         if (regexp != NULL)
192         {
193                 DEBUG ("ProcessMatch: adding \"%s\" as criteria to process %s.", regexp, name);
194                 new->re = (regex_t *) malloc (sizeof (regex_t));
195                 if (new->re == NULL)
196                 {
197                         ERROR ("processes plugin: ps_list_register: malloc failed.");
198                         sfree (new);
199                         return;
200                 }
201
202                 status = regcomp (new->re, regexp, REG_EXTENDED | REG_NOSUB);
203                 if (status != 0)
204                 {
205                         DEBUG ("ProcessMatch: compiling the regular expression \"%s\" failed.", regexp);
206                         sfree(new->re);
207                         return;
208                 }
209         }
210 #else
211         if (regexp != NULL)
212         {
213                 ERROR ("processes plugin: ps_list_register: "
214                                 "Regular expression \"%s\" found in config "
215                                 "file, but support for regular expressions "
216                                 "has been dispabled at compile time.",
217                                 regexp);
218                 sfree (new);
219                 return;
220         }
221 #endif
222         
223         for (ptr = list_head_g; ptr != NULL; ptr = ptr->next)
224         {
225                 if (strcmp (ptr->name, name) == 0)
226                 {
227                         WARNING ("processes plugin: You have configured more "
228                                         "than one `Process' or "
229                                         "`ProcessMatch' with the same name. "
230                                         "All but the first setting will be "
231                                         "ignored.");
232                         sfree (new->re);
233                         sfree (new);
234                         return;
235                 }
236
237                 if (ptr->next == NULL)
238                         break;
239         }
240
241         if (ptr == NULL)
242                 list_head_g = new;
243         else
244                 ptr->next = new;
245 } /* void ps_list_register */
246
247 /* try to match name against entry, returns 1 if success */
248 static int ps_list_match (const char *name, const char *cmdline, procstat_t *ps)
249 {
250         if ((ps->re != NULL) && (regexec(ps->re, (strlen(cmdline)!=0)?cmdline:name, 0, NULL, 0) == 0))
251                 return (1);
252         if (strcmp (ps->name, name) == 0) {
253                 return (1);
254         }
255         return (0);
256 }
257
258 /* add process entry to 'instances' of process 'name' (or refresh it) */
259 static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t *entry)
260 {
261         procstat_t *ps;
262         procstat_entry_t *pse;
263
264         if (entry->id == 0)
265                 return;
266
267         for (ps = list_head_g; ps != NULL; ps = ps->next)
268         {
269
270                 if ((ps_list_match (name, cmdline, ps)) == 0)
271                         continue;
272
273                 for (pse = ps->instances; pse != NULL; pse = pse->next)
274                         if ((pse->id == entry->id) || (pse->next == NULL))
275                                 break;
276
277                 if ((pse == NULL) || (pse->id != entry->id))
278                 {
279                         procstat_entry_t *new;
280                         
281                         new = (procstat_entry_t *) malloc (sizeof (procstat_entry_t));
282                         if (new == NULL)
283                                 return;
284                         memset (new, 0, sizeof (procstat_entry_t));
285                         new->id = entry->id;
286                         
287                         if (pse == NULL)
288                                 ps->instances = new;
289                         else
290                                 pse->next = new;
291
292                         pse = new;
293                 }
294
295                 pse->age = 0;
296                 pse->num_proc = entry->num_proc;
297                 pse->num_lwp  = entry->num_lwp;
298                 pse->vmem_rss = entry->vmem_rss;
299
300                 ps->num_proc += pse->num_proc;
301                 ps->num_lwp  += pse->num_lwp;
302                 ps->vmem_rss += pse->vmem_rss;
303
304                 if ((entry->vmem_minflt_counter == 0)
305                                 && (entry->vmem_majflt_counter == 0))
306                 {
307                         pse->vmem_minflt_counter += entry->vmem_minflt;
308                         pse->vmem_minflt = entry->vmem_minflt;
309
310                         pse->vmem_majflt_counter += entry->vmem_majflt;
311                         pse->vmem_majflt = entry->vmem_majflt;
312                 }
313                 else
314                 {
315                         if (entry->vmem_minflt_counter < pse->vmem_minflt_counter)
316                         {
317                                 pse->vmem_minflt = entry->vmem_minflt_counter
318                                         + (ULONG_MAX - pse->vmem_minflt_counter);
319                         }
320                         else
321                         {
322                                 pse->vmem_minflt = entry->vmem_minflt_counter - pse->vmem_minflt_counter;
323                         }
324                         pse->vmem_minflt_counter = entry->vmem_minflt_counter;
325                         
326                         if (entry->vmem_majflt_counter < pse->vmem_majflt_counter)
327                         {
328                                 pse->vmem_majflt = entry->vmem_majflt_counter
329                                         + (ULONG_MAX - pse->vmem_majflt_counter);
330                         }
331                         else
332                         {
333                                 pse->vmem_majflt = entry->vmem_majflt_counter - pse->vmem_majflt_counter;
334                         }
335                         pse->vmem_majflt_counter = entry->vmem_majflt_counter;
336                 }
337
338                 ps->vmem_minflt_counter += pse->vmem_minflt;
339                 ps->vmem_majflt_counter += pse->vmem_majflt;
340
341                 if ((entry->cpu_user_counter == 0)
342                                 && (entry->cpu_system_counter == 0))
343                 {
344                         pse->cpu_user_counter += entry->cpu_user;
345                         pse->cpu_user = entry->cpu_user;
346
347                         pse->cpu_system_counter += entry->cpu_system;
348                         pse->cpu_system = entry->cpu_system;
349                 }
350                 else
351                 {
352                         if (entry->cpu_user_counter < pse->cpu_user_counter)
353                         {
354                                 pse->cpu_user = entry->cpu_user_counter
355                                         + (ULONG_MAX - pse->cpu_user_counter);
356                         }
357                         else
358                         {
359                                 pse->cpu_user = entry->cpu_user_counter - pse->cpu_user_counter;
360                         }
361                         pse->cpu_user_counter = entry->cpu_user_counter;
362                         
363                         if (entry->cpu_system_counter < pse->cpu_system_counter)
364                         {
365                                 pse->cpu_system = entry->cpu_system_counter
366                                         + (ULONG_MAX - pse->cpu_system_counter);
367                         }
368                         else
369                         {
370                                 pse->cpu_system = entry->cpu_system_counter - pse->cpu_system_counter;
371                         }
372                         pse->cpu_system_counter = entry->cpu_system_counter;
373                 }
374
375                 ps->cpu_user_counter   += pse->cpu_user;
376                 ps->cpu_system_counter += pse->cpu_system;
377         }
378 }
379
380 /* remove old entries from instances of processes in list_head_g */
381 static void ps_list_reset (void)
382 {
383         procstat_t *ps;
384         procstat_entry_t *pse;
385         procstat_entry_t *pse_prev;
386
387         for (ps = list_head_g; ps != NULL; ps = ps->next)
388         {
389                 ps->num_proc    = 0;
390                 ps->num_lwp     = 0;
391                 ps->vmem_rss    = 0;
392
393                 pse_prev = NULL;
394                 pse = ps->instances;
395                 while (pse != NULL)
396                 {
397                         if (pse->age > 10)
398                         {
399                                 DEBUG ("Removing this procstat entry cause it's too old: "
400                                                 "id = %lu; name = %s;",
401                                                 pse->id, ps->name);
402
403                                 if (pse_prev == NULL)
404                                 {
405                                         ps->instances = pse->next;
406                                         free (pse);
407                                         pse = ps->instances;
408                                 }
409                                 else
410                                 {
411                                         pse_prev->next = pse->next;
412                                         free (pse);
413                                         pse = pse_prev->next;
414                                 }
415                         }
416                         else
417                         {
418                                 pse->age++;
419                                 pse_prev = pse;
420                                 pse = pse->next;
421                         }
422                 } /* while (pse != NULL) */
423         } /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
424 }
425
426 /* put all pre-defined 'Process' names from config to list_head_g tree */
427 static int ps_config (const char *key, const char *value)
428 {
429         char *new_val;  
430         char *fields[2];
431         int fields_num;
432
433         if (strcasecmp (key, "Process") == 0)
434         {
435                 ps_list_register (value, NULL);
436                 return (0);
437         }
438
439         if (strcasecmp (key, "ProcessMatch") == 0)
440         {
441                 new_val = strdup (value);
442                 if (new_val == NULL)
443                         return (-1);
444                 fields_num = strsplit (new_val, fields, 2);
445                 if (fields_num != 2)
446                 {
447                         sfree (new_val);
448                         return (-1);
449                 }
450                 ps_list_register (fields[0], fields[1]);
451                 sfree (new_val);
452                 return (0);
453         }
454
455         return (-1);
456 }
457
458 static int ps_init (void)
459 {
460 #if HAVE_THREAD_INFO
461         kern_return_t status;
462
463         port_host_self = mach_host_self ();
464         port_task_self = mach_task_self ();
465
466         if (pset_list != NULL)
467         {
468                 vm_deallocate (port_task_self,
469                                 (vm_address_t) pset_list,
470                                 pset_list_len * sizeof (processor_set_t));
471                 pset_list = NULL;
472                 pset_list_len = 0;
473         }
474
475         if ((status = host_processor_sets (port_host_self,
476                                         &pset_list,
477                                         &pset_list_len)) != KERN_SUCCESS)
478         {
479                 ERROR ("host_processor_sets failed: %s\n",
480                                 mach_error_string (status));
481                 pset_list = NULL;
482                 pset_list_len = 0;
483                 return (-1);
484         }
485 /* #endif HAVE_THREAD_INFO */
486
487 #elif KERNEL_LINUX
488         pagesize_g = sysconf(_SC_PAGESIZE);
489         DEBUG ("pagesize_g = %li; CONFIG_HZ = %i;",
490                         pagesize_g, CONFIG_HZ);
491 #endif /* KERNEL_LINUX */
492
493         return (0);
494 } /* int ps_init */
495
496 /* submit global state (e.g.: qty of zombies, running, etc..) */
497 static void ps_submit_state (const char *state, double value)
498 {
499         value_t values[1];
500         value_list_t vl = VALUE_LIST_INIT;
501
502         values[0].gauge = value;
503
504         vl.values = values;
505         vl.values_len = 1;
506         vl.time = time (NULL);
507         strcpy (vl.host, hostname_g);
508         strcpy (vl.plugin, "processes");
509         strcpy (vl.plugin_instance, "");
510         strcpy (vl.type, "ps_state");
511         sstrncpy (vl.type_instance, state, sizeof (vl.type_instance));
512
513         plugin_dispatch_values (&vl);
514 }
515
516 /* submit info about specific process (e.g.: memory taken, cpu usage, etc..) */
517 static void ps_submit_proc_list (procstat_t *ps)
518 {
519         value_t values[2];
520         value_list_t vl = VALUE_LIST_INIT;
521
522         vl.values = values;
523         vl.values_len = 2;
524         vl.time = time (NULL);
525         strcpy (vl.host, hostname_g);
526         strcpy (vl.plugin, "processes");
527         sstrncpy (vl.plugin_instance, ps->name, sizeof (vl.plugin_instance));
528
529         strcpy (vl.type, "ps_rss");
530         vl.values[0].gauge = ps->vmem_rss;
531         vl.values_len = 1;
532         plugin_dispatch_values (&vl);
533
534         strcpy (vl.type, "ps_cputime");
535         vl.values[0].counter = ps->cpu_user_counter;
536         vl.values[1].counter = ps->cpu_system_counter;
537         vl.values_len = 2;
538         plugin_dispatch_values (&vl);
539
540         strcpy (vl.type, "ps_count");
541         vl.values[0].gauge = ps->num_proc;
542         vl.values[1].gauge = ps->num_lwp;
543         vl.values_len = 2;
544         plugin_dispatch_values (&vl);
545
546         strcpy (vl.type, "ps_pagefaults");
547         vl.values[0].counter = ps->vmem_minflt_counter;
548         vl.values[1].counter = ps->vmem_majflt_counter;
549         vl.values_len = 2;
550         plugin_dispatch_values (&vl);
551
552         DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; vmem_rss = %lu; "
553                         "vmem_minflt_counter = %lu; vmem_majflt_counter = %lu; "
554                         "cpu_user_counter = %lu; cpu_system_counter = %lu;",
555                         ps->name, ps->num_proc, ps->num_lwp, ps->vmem_rss,
556                         ps->vmem_minflt_counter, ps->vmem_majflt_counter,
557                         ps->cpu_user_counter, ps->cpu_system_counter);
558 } /* void ps_submit_proc_list */
559
560 /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
561 #if KERNEL_LINUX
562 static int *ps_read_tasks (int pid)
563 {
564         int *list = NULL;
565         int  list_size = 1; /* size of allocated space, in elements */
566         int  list_len = 0;  /* number of currently used elements */
567
568         char           dirname[64];
569         DIR           *dh;
570         struct dirent *ent;
571
572         ssnprintf (dirname, sizeof (dirname), "/proc/%i/task", pid);
573
574         if ((dh = opendir (dirname)) == NULL)
575         {
576                 DEBUG ("Failed to open directory `%s'", dirname);
577                 return (NULL);
578         }
579
580         while ((ent = readdir (dh)) != NULL)
581         {
582                 if (!isdigit (ent->d_name[0]))
583                         continue;
584
585                 if ((list_len + 1) >= list_size)
586                 {
587                         int *new_ptr;
588                         int  new_size = 2 * list_size;
589                         /* Comes in sizes: 2, 4, 8, 16, ... */
590
591                         new_ptr = (int *) realloc (list, (size_t) (sizeof (int) * new_size));
592                         if (new_ptr == NULL)
593                         {
594                                 if (list != NULL)
595                                         free (list);
596                                 ERROR ("processes plugin: "
597                                                 "Failed to allocate more memory.");
598                                 return (NULL);
599                         }
600
601                         list = new_ptr;
602                         list_size = new_size;
603
604                         memset (list + list_len, 0, sizeof (int) * (list_size - list_len));
605                 }
606
607                 list[list_len] = atoi (ent->d_name);
608                 if (list[list_len] != 0)
609                         list_len++;
610         }
611
612         closedir (dh);
613
614         if (list_len == 0)
615                 return (NULL);
616
617         assert (list_len < list_size);
618         assert (list[list_len] == 0);
619
620         return (list);
621 } /* int *ps_read_tasks */
622
623 int ps_read_process (int pid, procstat_t *ps, char *state)
624 {
625         char  filename[64];
626         char  buffer[1024];
627         FILE *fh;
628
629         char *fields[64];
630         char  fields_len;
631
632         int  *tasks;
633         int   i;
634
635         int   ppid;
636         int   name_len;
637
638         long long unsigned cpu_user_counter;
639         long long unsigned cpu_system_counter;
640         long long unsigned vmem_rss;
641
642         memset (ps, 0, sizeof (procstat_t));
643
644         ssnprintf (filename, sizeof (filename), "/proc/%i/stat", pid);
645
646         if ((fh = fopen (filename, "r")) == NULL)
647                 return (-1);
648
649         if (fgets (buffer, 1024, fh) == NULL)
650         {
651                 fclose (fh);
652                 return (-1);
653         }
654
655         fclose (fh);
656
657         fields_len = strsplit (buffer, fields, 64);
658         if (fields_len < 24)
659         {
660                 DEBUG ("processes plugin: ps_read_process (pid = %i):"
661                                 " `%s' has only %i fields..",
662                                 (int) pid, filename, fields_len);
663                 return (-1);
664         }
665
666         /* copy the name, strip brackets in the process */
667         name_len = strlen (fields[1]) - 2;
668         if ((fields[1][0] != '(') || (fields[1][name_len + 1] != ')'))
669         {
670                 DEBUG ("No brackets found in process name: `%s'", fields[1]);
671                 return (-1);
672         }
673         fields[1] = fields[1] + 1;
674         fields[1][name_len] = '\0';
675         strncpy (ps->name, fields[1], PROCSTAT_NAME_LEN);
676
677         ppid = atoi (fields[3]);
678
679         *state = fields[2][0];
680
681         if (*state == 'Z')
682         {
683                 ps->num_lwp  = 0;
684                 ps->num_proc = 0;
685         }
686         else if ((tasks = ps_read_tasks (pid)) == NULL)
687         {
688                 /* Kernel 2.4 or so */
689                 ps->num_lwp  = 1;
690                 ps->num_proc = 1;
691         }
692         else
693         {
694                 ps->num_lwp  = 0;
695                 ps->num_proc = 1;
696                 for (i = 0; tasks[i] != 0; i++)
697                         ps->num_lwp++;
698
699                 free (tasks);
700                 tasks = NULL;
701         }
702
703         /* Leave the rest at zero if this is only a zombi */
704         if (ps->num_proc == 0)
705         {
706                 DEBUG ("processes plugin: This is only a zombi: pid = %i; "
707                                 "name = %s;", pid, ps->name);
708                 return (0);
709         }
710
711         cpu_user_counter   = atoll (fields[13]);
712         cpu_system_counter = atoll (fields[14]);
713         vmem_rss = atoll (fields[23]);
714         ps->vmem_minflt_counter = atol (fields[9]);
715         ps->vmem_majflt_counter = atol (fields[11]);
716         
717         /* Convert jiffies to useconds */
718         cpu_user_counter   = cpu_user_counter   * 1000000 / CONFIG_HZ;
719         cpu_system_counter = cpu_system_counter * 1000000 / CONFIG_HZ;
720         vmem_rss = vmem_rss * pagesize_g;
721
722         ps->cpu_user_counter = (unsigned long) cpu_user_counter;
723         ps->cpu_system_counter = (unsigned long) cpu_system_counter;
724         ps->vmem_rss = (unsigned long) vmem_rss;
725
726         /* success */
727         return (0);
728 } /* int ps_read_process (...) */
729 #endif /* KERNEL_LINUX */
730
731 #if HAVE_THREAD_INFO
732 static int mach_get_task_name (task_t t, int *pid, char *name, size_t name_max_len)
733 {
734         int mib[4];
735
736         struct kinfo_proc kp;
737         size_t            kp_size;
738
739         mib[0] = CTL_KERN;
740         mib[1] = KERN_PROC;
741         mib[2] = KERN_PROC_PID;
742
743         if (pid_for_task (t, pid) != KERN_SUCCESS)
744                 return (-1);
745         mib[3] = *pid;
746
747         kp_size = sizeof (kp);
748         if (sysctl (mib, 4, &kp, &kp_size, NULL, 0) != 0)
749                 return (-1);
750
751         if (name_max_len > (MAXCOMLEN + 1))
752                 name_max_len = MAXCOMLEN + 1;
753
754         strncpy (name, kp.kp_proc.p_comm, name_max_len - 1);
755         name[name_max_len - 1] = '\0';
756
757         DEBUG ("pid = %i; name = %s;", *pid, name);
758
759         /* We don't do the special handling for `p_comm == "LaunchCFMApp"' as
760          * `top' does it, because it is a lot of work and only used when
761          * debugging. -octo */
762
763         return (0);
764 }
765 #endif /* HAVE_THREAD_INFO */
766 /* ------- end of additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
767
768 /* do actual readings from kernel */
769 static int ps_read (void)
770 {
771 #if HAVE_THREAD_INFO
772         kern_return_t            status;
773
774         int                      pset;
775         processor_set_t          port_pset_priv;
776
777         int                      task;
778         task_array_t             task_list;
779         mach_msg_type_number_t   task_list_len;
780
781         int                      task_pid;
782         char                     task_name[MAXCOMLEN + 1];
783
784         int                      thread;
785         thread_act_array_t       thread_list;
786         mach_msg_type_number_t   thread_list_len;
787         thread_basic_info_data_t thread_data;
788         mach_msg_type_number_t   thread_data_len;
789
790         int running  = 0;
791         int sleeping = 0;
792         int zombies  = 0;
793         int stopped  = 0;
794         int blocked  = 0;
795
796         procstat_t *ps;
797         procstat_entry_t pse;
798
799         ps_list_reset ();
800
801         /*
802          * The Mach-concept is a little different from the traditional UNIX
803          * concept: All the work is done in threads. Threads are contained in
804          * `tasks'. Therefore, `task status' doesn't make much sense, since
805          * it's actually a `thread status'.
806          * Tasks are assigned to sets of processors, so that's where you go to
807          * get a list.
808          */
809         for (pset = 0; pset < pset_list_len; pset++)
810         {
811                 if ((status = host_processor_set_priv (port_host_self,
812                                                 pset_list[pset],
813                                                 &port_pset_priv)) != KERN_SUCCESS)
814                 {
815                         ERROR ("host_processor_set_priv failed: %s\n",
816                                         mach_error_string (status));
817                         continue;
818                 }
819
820                 if ((status = processor_set_tasks (port_pset_priv,
821                                                 &task_list,
822                                                 &task_list_len)) != KERN_SUCCESS)
823                 {
824                         ERROR ("processor_set_tasks failed: %s\n",
825                                         mach_error_string (status));
826                         mach_port_deallocate (port_task_self, port_pset_priv);
827                         continue;
828                 }
829
830                 for (task = 0; task < task_list_len; task++)
831                 {
832                         ps = NULL;
833                         if (mach_get_task_name (task_list[task],
834                                                 &task_pid,
835                                                 task_name, PROCSTAT_NAME_LEN) == 0)
836                         {
837                                 /* search for at least one match */
838                                 for (ps = list_head_g; ps != NULL; ps = ps->next)
839                                         if (ps_list_match(task_name, NULL, ps) == 1) //!!! cmdline should be here instead of NULL
840                                                 break;
841                         }
842
843                         /* Collect more detailed statistics for this process */
844                         if (ps != NULL)
845                         {
846                                 task_basic_info_data_t        task_basic_info;
847                                 mach_msg_type_number_t        task_basic_info_len;
848                                 task_events_info_data_t       task_events_info;
849                                 mach_msg_type_number_t        task_events_info_len;
850                                 task_absolutetime_info_data_t task_absolutetime_info;
851                                 mach_msg_type_number_t        task_absolutetime_info_len;
852
853                                 memset (&pse, '\0', sizeof (pse));
854                                 pse.id = task_pid;
855
856                                 task_basic_info_len = TASK_BASIC_INFO_COUNT;
857                                 status = task_info (task_list[task],
858                                                 TASK_BASIC_INFO,
859                                                 (task_info_t) &task_basic_info,
860                                                 &task_basic_info_len);
861                                 if (status != KERN_SUCCESS)
862                                 {
863                                         ERROR ("task_info failed: %s",
864                                                         mach_error_string (status));
865                                         continue; /* with next thread_list */
866                                 }
867
868                                 task_events_info_len = TASK_EVENTS_INFO_COUNT;
869                                 status = task_info (task_list[task],
870                                                 TASK_EVENTS_INFO,
871                                                 (task_info_t) &task_events_info,
872                                                 &task_events_info_len);
873                                 if (status != KERN_SUCCESS)
874                                 {
875                                         ERROR ("task_info failed: %s",
876                                                         mach_error_string (status));
877                                         continue; /* with next thread_list */
878                                 }
879
880                                 task_absolutetime_info_len = TASK_ABSOLUTETIME_INFO_COUNT;
881                                 status = task_info (task_list[task],
882                                                 TASK_ABSOLUTETIME_INFO,
883                                                 (task_info_t) &task_absolutetime_info,
884                                                 &task_absolutetime_info_len);
885                                 if (status != KERN_SUCCESS)
886                                 {
887                                         ERROR ("task_info failed: %s",
888                                                         mach_error_string (status));
889                                         continue; /* with next thread_list */
890                                 }
891
892                                 pse.num_proc++;
893                                 pse.vmem_rss = task_basic_info.resident_size;
894
895                                 pse.vmem_minflt_counter = task_events_info.cow_faults;
896                                 pse.vmem_majflt_counter = task_events_info.faults;
897
898                                 pse.cpu_user_counter = task_absolutetime_info.total_user;
899                                 pse.cpu_system_counter = task_absolutetime_info.total_system;
900                         }
901
902                         status = task_threads (task_list[task], &thread_list,
903                                         &thread_list_len);
904                         if (status != KERN_SUCCESS)
905                         {
906                                 /* Apple's `top' treats this case a zombie. It
907                                  * makes sense to some extend: A `zombie'
908                                  * thread is nonsense, since the task/process
909                                  * is dead. */
910                                 zombies++;
911                                 DEBUG ("task_threads failed: %s",
912                                                 mach_error_string (status));
913                                 if (task_list[task] != port_task_self)
914                                         mach_port_deallocate (port_task_self,
915                                                         task_list[task]);
916                                 continue; /* with next task_list */
917                         }
918
919                         for (thread = 0; thread < thread_list_len; thread++)
920                         {
921                                 thread_data_len = THREAD_BASIC_INFO_COUNT;
922                                 status = thread_info (thread_list[thread],
923                                                 THREAD_BASIC_INFO,
924                                                 (thread_info_t) &thread_data,
925                                                 &thread_data_len);
926                                 if (status != KERN_SUCCESS)
927                                 {
928                                         ERROR ("thread_info failed: %s",
929                                                         mach_error_string (status));
930                                         if (task_list[task] != port_task_self)
931                                                 mach_port_deallocate (port_task_self,
932                                                                 thread_list[thread]);
933                                         continue; /* with next thread_list */
934                                 }
935
936                                 if (ps != NULL)
937                                         pse.num_lwp++;
938
939                                 switch (thread_data.run_state)
940                                 {
941                                         case TH_STATE_RUNNING:
942                                                 running++;
943                                                 break;
944                                         case TH_STATE_STOPPED:
945                                         /* What exactly is `halted'? */
946                                         case TH_STATE_HALTED:
947                                                 stopped++;
948                                                 break;
949                                         case TH_STATE_WAITING:
950                                                 sleeping++;
951                                                 break;
952                                         case TH_STATE_UNINTERRUPTIBLE:
953                                                 blocked++;
954                                                 break;
955                                         /* There is no `zombie' case here,
956                                          * since there are no zombie-threads.
957                                          * There's only zombie tasks, which are
958                                          * handled above. */
959                                         default:
960                                                 WARNING ("Unknown thread status: %s",
961                                                                 thread_data.run_state);
962                                                 break;
963                                 } /* switch (thread_data.run_state) */
964
965                                 if (task_list[task] != port_task_self)
966                                 {
967                                         status = mach_port_deallocate (port_task_self,
968                                                         thread_list[thread]);
969                                         if (status != KERN_SUCCESS)
970                                                 ERROR ("mach_port_deallocate failed: %s",
971                                                                 mach_error_string (status));
972                                 }
973                         } /* for (thread_list) */
974
975                         if ((status = vm_deallocate (port_task_self,
976                                                         (vm_address_t) thread_list,
977                                                         thread_list_len * sizeof (thread_act_t)))
978                                         != KERN_SUCCESS)
979                         {
980                                 ERROR ("vm_deallocate failed: %s",
981                                                 mach_error_string (status));
982                         }
983                         thread_list = NULL;
984                         thread_list_len = 0;
985
986                         /* Only deallocate the task port, if it isn't our own.
987                          * Don't know what would happen in that case, but this
988                          * is what Apple's top does.. ;) */
989                         if (task_list[task] != port_task_self)
990                         {
991                                 status = mach_port_deallocate (port_task_self,
992                                                 task_list[task]);
993                                 if (status != KERN_SUCCESS)
994                                         ERROR ("mach_port_deallocate failed: %s",
995                                                         mach_error_string (status));
996                         }
997
998                         if (ps != NULL)
999                                 ps_list_add (task_name, NULL, &pse); //!!! cmdline should be here instead of NULL
1000                 } /* for (task_list) */
1001
1002                 if ((status = vm_deallocate (port_task_self,
1003                                 (vm_address_t) task_list,
1004                                 task_list_len * sizeof (task_t))) != KERN_SUCCESS)
1005                 {
1006                         ERROR ("vm_deallocate failed: %s",
1007                                         mach_error_string (status));
1008                 }
1009                 task_list = NULL;
1010                 task_list_len = 0;
1011
1012                 if ((status = mach_port_deallocate (port_task_self, port_pset_priv))
1013                                 != KERN_SUCCESS)
1014                 {
1015                         ERROR ("mach_port_deallocate failed: %s",
1016                                         mach_error_string (status));
1017                 }
1018         } /* for (pset_list) */
1019
1020         ps_submit_state ("running", running);
1021         ps_submit_state ("sleeping", sleeping);
1022         ps_submit_state ("zombies", zombies);
1023         ps_submit_state ("stopped", stopped);
1024         ps_submit_state ("blocked", blocked);
1025
1026         for (ps = list_head_g; ps != NULL; ps = ps->next)
1027                 ps_submit_proc_list (ps);
1028 /* #endif HAVE_THREAD_INFO */
1029
1030 #elif KERNEL_LINUX
1031         int running  = 0;
1032         int sleeping = 0;
1033         int zombies  = 0;
1034         int stopped  = 0;
1035         int paging   = 0;
1036         int blocked  = 0;
1037
1038         struct dirent *ent;
1039         DIR           *proc;
1040         int            pid;
1041
1042         int        status;
1043         procstat_t ps;
1044         procstat_entry_t pse;
1045         char       state;
1046
1047         procstat_t *ps_ptr;
1048
1049         running = sleeping = zombies = stopped = paging = blocked = 0;
1050         ps_list_reset ();
1051
1052         if ((proc = opendir ("/proc")) == NULL)
1053         {
1054                 char errbuf[1024];
1055                 ERROR ("Cannot open `/proc': %s",
1056                                 sstrerror (errno, errbuf, sizeof (errbuf)));
1057                 return (-1);
1058         }
1059
1060         while ((ent = readdir (proc)) != NULL)
1061         {
1062                 if (!isdigit (ent->d_name[0]))
1063                         continue;
1064
1065                 if ((pid = atoi (ent->d_name)) < 1)
1066                         continue;
1067
1068                 status = ps_read_process (pid, &ps, &state);
1069                 if (status != 0)
1070                 {
1071                         DEBUG ("ps_read_process failed: %i", status);
1072                         continue;
1073                 }
1074
1075                 pse.id       = pid;
1076                 pse.age      = 0;
1077
1078                 pse.num_proc = ps.num_proc;
1079                 pse.num_lwp  = ps.num_lwp;
1080                 pse.vmem_rss = ps.vmem_rss;
1081
1082                 pse.vmem_minflt = 0;
1083                 pse.vmem_minflt_counter = ps.vmem_minflt_counter;
1084                 pse.vmem_majflt = 0;
1085                 pse.vmem_majflt_counter = ps.vmem_majflt_counter;
1086
1087                 pse.cpu_user = 0;
1088                 pse.cpu_user_counter = ps.cpu_user_counter;
1089                 pse.cpu_system = 0;
1090                 pse.cpu_system_counter = ps.cpu_system_counter;
1091
1092                 switch (state)
1093                 {
1094                         case 'R': running++;  break;
1095                         case 'S': sleeping++; break;
1096                         case 'D': blocked++;  break;
1097                         case 'Z': zombies++;  break;
1098                         case 'T': stopped++;  break;
1099                         case 'W': paging++;   break;
1100                 }
1101
1102                 ps_list_add (ps.name, NULL, &pse); //!!! cmdline should be here instead of NULL
1103         }
1104
1105         closedir (proc);
1106
1107         ps_submit_state ("running",  running);
1108         ps_submit_state ("sleeping", sleeping);
1109         ps_submit_state ("zombies",  zombies);
1110         ps_submit_state ("stopped",  stopped);
1111         ps_submit_state ("paging",   paging);
1112         ps_submit_state ("blocked",  blocked);
1113
1114         for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
1115                 ps_submit_proc_list (ps_ptr);
1116 /* #endif KERNEL_LINUX */
1117
1118 #elif HAVE_LIBKVM
1119         int running  = 0;
1120         int sleeping = 0;
1121         int zombies  = 0;
1122         int stopped  = 0;
1123         int blocked  = 0;
1124         int idle     = 0;
1125         int wait     = 0;
1126
1127         kvm_t *kd;
1128         char errbuf[1024];
1129         char cmdline[ARG_MAX];
1130         struct kinfo_proc *procs;          /* array of processes */
1131         char ** argv;
1132         int count;                         /* returns number of processes */
1133         int i, j;
1134
1135         procstat_t *ps_ptr;
1136         procstat_entry_t pse;
1137
1138         ps_list_reset ();
1139
1140         /* Open the kvm interface, get a descriptor */
1141         if ((kd = kvm_open(NULL, NULL, NULL, 0, errbuf)) == NULL) {
1142                 ERROR ("Cannot open kvm interface: %s", errbuf);
1143                 return (0);
1144         }  
1145      
1146         /* Get the list of processes. */
1147         if ((procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, &count)) == NULL) {
1148                 kvm_close(kd);
1149                 ERROR ("Cannot get kvm processes list: %s", kvm_geterr(kd));
1150                 return (0);
1151         }
1152
1153         /* Iterate through the processes in kinfo_proc */
1154         for (i=0; i < count; i++) {
1155                 // retrieve the arguments
1156                 *cmdline = '\0';
1157                 argv = kvm_getargv(kd, (const struct kinfo_proc *) &(procs[i]), 0);
1158                 if (argv) {
1159                         j = 0;
1160                         while (argv[j] && strlen(cmdline) <= ARG_MAX) {
1161                                 if (j)
1162                                         strncat(cmdline, " ", 1);
1163                                 strncat(cmdline, argv[j], strlen(argv[j]));
1164                                 j++;
1165                         }
1166                 }  
1167
1168                 pse.id       = procs[i].ki_pid;
1169                 pse.age      = 0;
1170
1171                 pse.num_proc = 1;
1172                 pse.num_lwp  = procs[i].ki_numthreads;
1173
1174                 pse.vmem_rss = procs[i].ki_rssize * getpagesize();
1175                 pse.vmem_minflt = 0;
1176                 pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
1177                 pse.vmem_majflt = 0;
1178                 pse.vmem_majflt_counter = procs[i].ki_rusage.ru_majflt;
1179
1180                 pse.cpu_user = 0;
1181                 pse.cpu_user_counter = procs[i].ki_rusage.ru_utime.tv_sec*1000 + procs[i].ki_rusage.ru_utime.tv_usec;
1182                 pse.cpu_system = 0;
1183                 pse.cpu_system_counter = procs[i].ki_rusage.ru_stime.tv_sec*1000 + procs[i].ki_rusage.ru_stime.tv_usec;
1184
1185                 switch (procs[i].ki_stat) {
1186                         case SSTOP:     stopped++;      break;
1187                         case SSLEEP:    sleeping++;     break;
1188                         case SRUN:      running++;      break;
1189                         case SIDL:      idle++;         break;
1190                         case SWAIT:     wait++;         break;
1191                         case SLOCK:     blocked++;      break;
1192                         case SZOMB:     zombies++;      break;
1193                 }
1194
1195                 ps_list_add (procs[i].ki_comm, cmdline, &pse);
1196         }
1197
1198         if (kd) kvm_close(kd);
1199
1200         ps_submit_state ("running",  running);
1201         ps_submit_state ("sleeping", sleeping);
1202         ps_submit_state ("zombies",  zombies);
1203         ps_submit_state ("stopped",  stopped);
1204         ps_submit_state ("blocked",  blocked);
1205         ps_submit_state ("idle",     idle);
1206         ps_submit_state ("wait",     wait);
1207
1208         for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
1209                 ps_submit_proc_list (ps_ptr);
1210
1211 #endif /* HAVE_LIBKVM */
1212
1213         return (0);
1214 } /* int ps_read */
1215
1216 void module_register (void)
1217 {
1218         plugin_register_config ("processes", ps_config,
1219                         config_keys, config_keys_num);
1220         plugin_register_init ("processes", ps_init);
1221         plugin_register_read ("processes", ps_read);
1222 } /* void module_register */