amqp plugin: Fix compabitility with current librabbitmq.
[collectd.git] / src / processes.c
1 /**
2  * collectd - src/processes.c
3  * Copyright (C) 2005       Lyonel Vincent
4  * Copyright (C) 2006-2010  Florian octo Forster
5  * Copyright (C) 2008       Oleg King
6  * Copyright (C) 2009       Sebastian Harl
7  * Copyright (C) 2009       Andrés J. Díaz
8  * Copyright (C) 2009       Manuel Sanmartin
9  * Copyright (C) 2010       Clément Stenac
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU General Public License as published by the
13  * Free Software Foundation; either version 2 of the License, or (at your
14  * option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License along
22  * with this program; if not, write to the Free Software Foundation, Inc.,
23  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
24  *
25  * Authors:
26  *   Lyonel Vincent <lyonel at ezix.org>
27  *   Florian octo Forster <octo at verplant.org>
28  *   Oleg King <king2 at kaluga.ru>
29  *   Sebastian Harl <sh at tokkee.org>
30  *   Andrés J. Díaz <ajdiaz at connectical.com>
31  *   Manuel Sanmartin
32  *   Clément Stenac <clement.stenac at diwi.org>
33  **/
34
35 #include "collectd.h"
36 #include "common.h"
37 #include "plugin.h"
38 #include "configfile.h"
39
40 /* Include header files for the mach system, if they exist.. */
41 #if HAVE_THREAD_INFO
42 #  if HAVE_MACH_MACH_INIT_H
43 #    include <mach/mach_init.h>
44 #  endif
45 #  if HAVE_MACH_HOST_PRIV_H
46 #    include <mach/host_priv.h>
47 #  endif
48 #  if HAVE_MACH_MACH_ERROR_H
49 #    include <mach/mach_error.h>
50 #  endif
51 #  if HAVE_MACH_MACH_HOST_H
52 #    include <mach/mach_host.h>
53 #  endif
54 #  if HAVE_MACH_MACH_PORT_H
55 #    include <mach/mach_port.h>
56 #  endif
57 #  if HAVE_MACH_MACH_TYPES_H
58 #    include <mach/mach_types.h>
59 #  endif
60 #  if HAVE_MACH_MESSAGE_H
61 #    include <mach/message.h>
62 #  endif
63 #  if HAVE_MACH_PROCESSOR_SET_H
64 #    include <mach/processor_set.h>
65 #  endif
66 #  if HAVE_MACH_TASK_H
67 #    include <mach/task.h>
68 #  endif
69 #  if HAVE_MACH_THREAD_ACT_H
70 #    include <mach/thread_act.h>
71 #  endif
72 #  if HAVE_MACH_VM_REGION_H
73 #    include <mach/vm_region.h>
74 #  endif
75 #  if HAVE_MACH_VM_MAP_H
76 #    include <mach/vm_map.h>
77 #  endif
78 #  if HAVE_MACH_VM_PROT_H
79 #    include <mach/vm_prot.h>
80 #  endif
81 #  if HAVE_SYS_SYSCTL_H
82 #    include <sys/sysctl.h>
83 #  endif
84 /* #endif HAVE_THREAD_INFO */
85
86 #elif KERNEL_LINUX
87 #  if HAVE_LINUX_CONFIG_H
88 #    include <linux/config.h>
89 #  endif
90 #  ifndef CONFIG_HZ
91 #    define CONFIG_HZ 100
92 #  endif
93 /* #endif KERNEL_LINUX */
94
95 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
96 #  include <kvm.h>
97 #  include <sys/param.h>
98 #  include <sys/sysctl.h>
99 #  include <sys/user.h>
100 #  include <sys/proc.h>
101 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
102
103 #elif HAVE_PROCINFO_H
104 #  include <procinfo.h>
105 #  include <sys/types.h>
106
107 #define MAXPROCENTRY 32
108 #define MAXTHRDENTRY 16
109 #define MAXARGLN 1024
110 /* #endif HAVE_PROCINFO_H */
111
112 #else
113 # error "No applicable input method."
114 #endif
115
116 #if HAVE_REGEX_H
117 # include <regex.h>
118 #endif
119
120 #ifndef ARG_MAX
121 #  define ARG_MAX 4096
122 #endif
123
124 typedef struct procstat_entry_s
125 {
126         unsigned long id;
127         unsigned long age;
128
129         unsigned long num_proc;
130         unsigned long num_lwp;
131         unsigned long vmem_size;
132         unsigned long vmem_rss;
133         unsigned long vmem_data;
134         unsigned long vmem_code;
135         unsigned long stack_size;
136
137         unsigned long vmem_minflt;
138         unsigned long vmem_majflt;
139         derive_t      vmem_minflt_counter;
140         derive_t      vmem_majflt_counter;
141
142         unsigned long cpu_user;
143         unsigned long cpu_system;
144         derive_t      cpu_user_counter;
145         derive_t      cpu_system_counter;
146
147         /* io data */
148         derive_t io_rchar;
149         derive_t io_wchar;
150         derive_t io_syscr;
151         derive_t io_syscw;
152
153         struct procstat_entry_s *next;
154 } procstat_entry_t;
155
156 #define PROCSTAT_NAME_LEN 256
157 typedef struct procstat
158 {
159         char          name[PROCSTAT_NAME_LEN];
160 #if HAVE_REGEX_H
161         regex_t *re;
162 #endif
163
164         unsigned long num_proc;
165         unsigned long num_lwp;
166         unsigned long vmem_size;
167         unsigned long vmem_rss;
168         unsigned long vmem_data;
169         unsigned long vmem_code;
170         unsigned long stack_size;
171
172         derive_t vmem_minflt_counter;
173         derive_t vmem_majflt_counter;
174
175         derive_t cpu_user_counter;
176         derive_t cpu_system_counter;
177
178         /* io data */
179         derive_t io_rchar;
180         derive_t io_wchar;
181         derive_t io_syscr;
182         derive_t io_syscw;
183
184         struct procstat   *next;
185         struct procstat_entry_s *instances;
186 } procstat_t;
187
188 static procstat_t *list_head_g = NULL;
189
190 #if HAVE_THREAD_INFO
191 static mach_port_t port_host_self;
192 static mach_port_t port_task_self;
193
194 static processor_set_name_array_t pset_list;
195 static mach_msg_type_number_t     pset_list_len;
196 /* #endif HAVE_THREAD_INFO */
197
198 #elif KERNEL_LINUX
199 static long pagesize_g;
200 /* #endif KERNEL_LINUX */
201
202 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
203 /* no global variables */
204 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
205
206 #elif HAVE_PROCINFO_H
207 static  struct procentry64 procentry[MAXPROCENTRY];
208 static  struct thrdentry64 thrdentry[MAXTHRDENTRY];
209 static int pagesize;
210
211 #ifndef _AIXVERSION_610
212 int     getprocs64 (void *procsinfo, int sizproc, void *fdsinfo, int sizfd, pid_t *index, int count);
213 int     getthrds64( pid_t, void *, int, tid64_t *, int );
214 #endif
215 int getargs (struct procentry64 *processBuffer, int bufferLen, char *argsBuffer, int argsLen);
216 #endif /* HAVE_PROCINFO_H */
217
218 /* put name of process from config to list_head_g tree
219    list_head_g is a list of 'procstat_t' structs with
220    processes names we want to watch */
221 static void ps_list_register (const char *name, const char *regexp)
222 {
223         procstat_t *new;
224         procstat_t *ptr;
225         int status;
226
227         new = (procstat_t *) malloc (sizeof (procstat_t));
228         if (new == NULL)
229         {
230                 ERROR ("processes plugin: ps_list_register: malloc failed.");
231                 return;
232         }
233         memset (new, 0, sizeof (procstat_t));
234         sstrncpy (new->name, name, sizeof (new->name));
235
236 #if HAVE_REGEX_H
237         if (regexp != NULL)
238         {
239                 DEBUG ("ProcessMatch: adding \"%s\" as criteria to process %s.", regexp, name);
240                 new->re = (regex_t *) malloc (sizeof (regex_t));
241                 if (new->re == NULL)
242                 {
243                         ERROR ("processes plugin: ps_list_register: malloc failed.");
244                         sfree (new);
245                         return;
246                 }
247
248                 status = regcomp (new->re, regexp, REG_EXTENDED | REG_NOSUB);
249                 if (status != 0)
250                 {
251                         DEBUG ("ProcessMatch: compiling the regular expression \"%s\" failed.", regexp);
252                         sfree(new->re);
253                         return;
254                 }
255         }
256 #else
257         if (regexp != NULL)
258         {
259                 ERROR ("processes plugin: ps_list_register: "
260                                 "Regular expression \"%s\" found in config "
261                                 "file, but support for regular expressions "
262                                 "has been disabled at compile time.",
263                                 regexp);
264                 sfree (new);
265                 return;
266         }
267 #endif
268
269         for (ptr = list_head_g; ptr != NULL; ptr = ptr->next)
270         {
271                 if (strcmp (ptr->name, name) == 0)
272                 {
273                         WARNING ("processes plugin: You have configured more "
274                                         "than one `Process' or "
275                                         "`ProcessMatch' with the same name. "
276                                         "All but the first setting will be "
277                                         "ignored.");
278                         sfree (new->re);
279                         sfree (new);
280                         return;
281                 }
282
283                 if (ptr->next == NULL)
284                         break;
285         }
286
287         if (ptr == NULL)
288                 list_head_g = new;
289         else
290                 ptr->next = new;
291 } /* void ps_list_register */
292
293 /* try to match name against entry, returns 1 if success */
294 static int ps_list_match (const char *name, const char *cmdline, procstat_t *ps)
295 {
296 #if HAVE_REGEX_H
297         if (ps->re != NULL)
298         {
299                 int status;
300                 const char *str;
301
302                 str = cmdline;
303                 if ((str == NULL) || (str[0] == 0))
304                         str = name;
305
306                 assert (str != NULL);
307
308                 status = regexec (ps->re, str,
309                                 /* nmatch = */ 0,
310                                 /* pmatch = */ NULL,
311                                 /* eflags = */ 0);
312                 if (status == 0)
313                         return (1);
314         }
315         else
316 #endif
317         if (strcmp (ps->name, name) == 0)
318                 return (1);
319
320         return (0);
321 } /* int ps_list_match */
322
323 /* add process entry to 'instances' of process 'name' (or refresh it) */
324 static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t *entry)
325 {
326         procstat_t *ps;
327         procstat_entry_t *pse;
328
329         if (entry->id == 0)
330                 return;
331
332         for (ps = list_head_g; ps != NULL; ps = ps->next)
333         {
334                 if ((ps_list_match (name, cmdline, ps)) == 0)
335                         continue;
336
337                 for (pse = ps->instances; pse != NULL; pse = pse->next)
338                         if ((pse->id == entry->id) || (pse->next == NULL))
339                                 break;
340
341                 if ((pse == NULL) || (pse->id != entry->id))
342                 {
343                         procstat_entry_t *new;
344
345                         new = (procstat_entry_t *) malloc (sizeof (procstat_entry_t));
346                         if (new == NULL)
347                                 return;
348                         memset (new, 0, sizeof (procstat_entry_t));
349                         new->id = entry->id;
350
351                         if (pse == NULL)
352                                 ps->instances = new;
353                         else
354                                 pse->next = new;
355
356                         pse = new;
357                 }
358
359                 pse->age = 0;
360                 pse->num_proc   = entry->num_proc;
361                 pse->num_lwp    = entry->num_lwp;
362                 pse->vmem_size  = entry->vmem_size;
363                 pse->vmem_rss   = entry->vmem_rss;
364                 pse->vmem_data  = entry->vmem_data;
365                 pse->vmem_code  = entry->vmem_code;
366                 pse->stack_size = entry->stack_size;
367                 pse->io_rchar   = entry->io_rchar;
368                 pse->io_wchar   = entry->io_wchar;
369                 pse->io_syscr   = entry->io_syscr;
370                 pse->io_syscw   = entry->io_syscw;
371
372                 ps->num_proc   += pse->num_proc;
373                 ps->num_lwp    += pse->num_lwp;
374                 ps->vmem_size  += pse->vmem_size;
375                 ps->vmem_rss   += pse->vmem_rss;
376                 ps->vmem_data  += pse->vmem_data;
377                 ps->vmem_code  += pse->vmem_code;
378                 ps->stack_size += pse->stack_size;
379
380                 ps->io_rchar   += ((pse->io_rchar == -1)?0:pse->io_rchar);
381                 ps->io_wchar   += ((pse->io_wchar == -1)?0:pse->io_wchar);
382                 ps->io_syscr   += ((pse->io_syscr == -1)?0:pse->io_syscr);
383                 ps->io_syscw   += ((pse->io_syscw == -1)?0:pse->io_syscw);
384
385                 if ((entry->vmem_minflt_counter == 0)
386                                 && (entry->vmem_majflt_counter == 0))
387                 {
388                         pse->vmem_minflt_counter += entry->vmem_minflt;
389                         pse->vmem_minflt = entry->vmem_minflt;
390
391                         pse->vmem_majflt_counter += entry->vmem_majflt;
392                         pse->vmem_majflt = entry->vmem_majflt;
393                 }
394                 else
395                 {
396                         if (entry->vmem_minflt_counter < pse->vmem_minflt_counter)
397                         {
398                                 pse->vmem_minflt = entry->vmem_minflt_counter
399                                         + (ULONG_MAX - pse->vmem_minflt_counter);
400                         }
401                         else
402                         {
403                                 pse->vmem_minflt = entry->vmem_minflt_counter - pse->vmem_minflt_counter;
404                         }
405                         pse->vmem_minflt_counter = entry->vmem_minflt_counter;
406
407                         if (entry->vmem_majflt_counter < pse->vmem_majflt_counter)
408                         {
409                                 pse->vmem_majflt = entry->vmem_majflt_counter
410                                         + (ULONG_MAX - pse->vmem_majflt_counter);
411                         }
412                         else
413                         {
414                                 pse->vmem_majflt = entry->vmem_majflt_counter - pse->vmem_majflt_counter;
415                         }
416                         pse->vmem_majflt_counter = entry->vmem_majflt_counter;
417                 }
418
419                 ps->vmem_minflt_counter += pse->vmem_minflt;
420                 ps->vmem_majflt_counter += pse->vmem_majflt;
421
422                 if ((entry->cpu_user_counter == 0)
423                                 && (entry->cpu_system_counter == 0))
424                 {
425                         pse->cpu_user_counter += entry->cpu_user;
426                         pse->cpu_user = entry->cpu_user;
427
428                         pse->cpu_system_counter += entry->cpu_system;
429                         pse->cpu_system = entry->cpu_system;
430                 }
431                 else
432                 {
433                         if (entry->cpu_user_counter < pse->cpu_user_counter)
434                         {
435                                 pse->cpu_user = entry->cpu_user_counter
436                                         + (ULONG_MAX - pse->cpu_user_counter);
437                         }
438                         else
439                         {
440                                 pse->cpu_user = entry->cpu_user_counter - pse->cpu_user_counter;
441                         }
442                         pse->cpu_user_counter = entry->cpu_user_counter;
443
444                         if (entry->cpu_system_counter < pse->cpu_system_counter)
445                         {
446                                 pse->cpu_system = entry->cpu_system_counter
447                                         + (ULONG_MAX - pse->cpu_system_counter);
448                         }
449                         else
450                         {
451                                 pse->cpu_system = entry->cpu_system_counter - pse->cpu_system_counter;
452                         }
453                         pse->cpu_system_counter = entry->cpu_system_counter;
454                 }
455
456                 ps->cpu_user_counter   += pse->cpu_user;
457                 ps->cpu_system_counter += pse->cpu_system;
458         }
459 }
460
461 /* remove old entries from instances of processes in list_head_g */
462 static void ps_list_reset (void)
463 {
464         procstat_t *ps;
465         procstat_entry_t *pse;
466         procstat_entry_t *pse_prev;
467
468         for (ps = list_head_g; ps != NULL; ps = ps->next)
469         {
470                 ps->num_proc    = 0;
471                 ps->num_lwp     = 0;
472                 ps->vmem_size   = 0;
473                 ps->vmem_rss    = 0;
474                 ps->vmem_data   = 0;
475                 ps->vmem_code   = 0;
476                 ps->stack_size  = 0;
477                 ps->io_rchar = -1;
478                 ps->io_wchar = -1;
479                 ps->io_syscr = -1;
480                 ps->io_syscw = -1;
481
482                 pse_prev = NULL;
483                 pse = ps->instances;
484                 while (pse != NULL)
485                 {
486                         if (pse->age > 10)
487                         {
488                                 DEBUG ("Removing this procstat entry cause it's too old: "
489                                                 "id = %lu; name = %s;",
490                                                 pse->id, ps->name);
491
492                                 if (pse_prev == NULL)
493                                 {
494                                         ps->instances = pse->next;
495                                         free (pse);
496                                         pse = ps->instances;
497                                 }
498                                 else
499                                 {
500                                         pse_prev->next = pse->next;
501                                         free (pse);
502                                         pse = pse_prev->next;
503                                 }
504                         }
505                         else
506                         {
507                                 pse->age++;
508                                 pse_prev = pse;
509                                 pse = pse->next;
510                         }
511                 } /* while (pse != NULL) */
512         } /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
513 }
514
515 /* put all pre-defined 'Process' names from config to list_head_g tree */
516 static int ps_config (oconfig_item_t *ci)
517 {
518         int i;
519
520         for (i = 0; i < ci->children_num; ++i) {
521                 oconfig_item_t *c = ci->children + i;
522
523                 if (strcasecmp (c->key, "Process") == 0)
524                 {
525                         if ((c->values_num != 1)
526                                         || (OCONFIG_TYPE_STRING != c->values[0].type)) {
527                                 ERROR ("processes plugin: `Process' expects exactly "
528                                                 "one string argument (got %i).",
529                                                 c->values_num);
530                                 continue;
531                         }
532
533                         if (c->children_num != 0) {
534                                 WARNING ("processes plugin: the `Process' config option "
535                                                 "does not expect any child elements -- ignoring "
536                                                 "content (%i elements) of the <Process '%s'> block.",
537                                                 c->children_num, c->values[0].value.string);
538                         }
539
540                         ps_list_register (c->values[0].value.string, NULL);
541                 }
542                 else if (strcasecmp (c->key, "ProcessMatch") == 0)
543                 {
544                         if ((c->values_num != 2)
545                                         || (OCONFIG_TYPE_STRING != c->values[0].type)
546                                         || (OCONFIG_TYPE_STRING != c->values[1].type))
547                         {
548                                 ERROR ("processes plugin: `ProcessMatch' needs exactly "
549                                                 "two string arguments (got %i).",
550                                                 c->values_num);
551                                 continue;
552                         }
553
554                         if (c->children_num != 0) {
555                                 WARNING ("processes plugin: the `ProcessMatch' config option "
556                                                 "does not expect any child elements -- ignoring "
557                                                 "content (%i elements) of the <ProcessMatch '%s' '%s'> "
558                                                 "block.", c->children_num, c->values[0].value.string,
559                                                 c->values[1].value.string);
560                         }
561
562                         ps_list_register (c->values[0].value.string,
563                                         c->values[1].value.string);
564                 }
565                 else
566                 {
567                         ERROR ("processes plugin: The `%s' configuration option is not "
568                                         "understood and will be ignored.", c->key);
569                         continue;
570                 }
571         }
572
573         return (0);
574 }
575
576 static int ps_init (void)
577 {
578 #if HAVE_THREAD_INFO
579         kern_return_t status;
580
581         port_host_self = mach_host_self ();
582         port_task_self = mach_task_self ();
583
584         if (pset_list != NULL)
585         {
586                 vm_deallocate (port_task_self,
587                                 (vm_address_t) pset_list,
588                                 pset_list_len * sizeof (processor_set_t));
589                 pset_list = NULL;
590                 pset_list_len = 0;
591         }
592
593         if ((status = host_processor_sets (port_host_self,
594                                         &pset_list,
595                                         &pset_list_len)) != KERN_SUCCESS)
596         {
597                 ERROR ("host_processor_sets failed: %s\n",
598                                 mach_error_string (status));
599                 pset_list = NULL;
600                 pset_list_len = 0;
601                 return (-1);
602         }
603 /* #endif HAVE_THREAD_INFO */
604
605 #elif KERNEL_LINUX
606         pagesize_g = sysconf(_SC_PAGESIZE);
607         DEBUG ("pagesize_g = %li; CONFIG_HZ = %i;",
608                         pagesize_g, CONFIG_HZ);
609 /* #endif KERNEL_LINUX */
610
611 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
612 /* no initialization */
613 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
614
615 #elif HAVE_PROCINFO_H
616         pagesize = getpagesize();
617 #endif /* HAVE_PROCINFO_H */
618
619         return (0);
620 } /* int ps_init */
621
622 /* submit global state (e.g.: qty of zombies, running, etc..) */
623 static void ps_submit_state (const char *state, double value)
624 {
625         value_t values[1];
626         value_list_t vl = VALUE_LIST_INIT;
627
628         values[0].gauge = value;
629
630         vl.values = values;
631         vl.values_len = 1;
632         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
633         sstrncpy (vl.plugin, "processes", sizeof (vl.plugin));
634         sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
635         sstrncpy (vl.type, "ps_state", sizeof (vl.type));
636         sstrncpy (vl.type_instance, state, sizeof (vl.type_instance));
637
638         plugin_dispatch_values (&vl);
639 }
640
641 /* submit info about specific process (e.g.: memory taken, cpu usage, etc..) */
642 static void ps_submit_proc_list (procstat_t *ps)
643 {
644         value_t values[2];
645         value_list_t vl = VALUE_LIST_INIT;
646
647         vl.values = values;
648         vl.values_len = 2;
649         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
650         sstrncpy (vl.plugin, "processes", sizeof (vl.plugin));
651         sstrncpy (vl.plugin_instance, ps->name, sizeof (vl.plugin_instance));
652
653         sstrncpy (vl.type, "ps_vm", sizeof (vl.type));
654         vl.values[0].gauge = ps->vmem_size;
655         vl.values_len = 1;
656         plugin_dispatch_values (&vl);
657
658         sstrncpy (vl.type, "ps_rss", sizeof (vl.type));
659         vl.values[0].gauge = ps->vmem_rss;
660         vl.values_len = 1;
661         plugin_dispatch_values (&vl);
662
663         sstrncpy (vl.type, "ps_data", sizeof (vl.type));
664         vl.values[0].gauge = ps->vmem_data;
665         vl.values_len = 1;
666         plugin_dispatch_values (&vl);
667
668         sstrncpy (vl.type, "ps_code", sizeof (vl.type));
669         vl.values[0].gauge = ps->vmem_code;
670         vl.values_len = 1;
671         plugin_dispatch_values (&vl);
672
673         sstrncpy (vl.type, "ps_stacksize", sizeof (vl.type));
674         vl.values[0].gauge = ps->stack_size;
675         vl.values_len = 1;
676         plugin_dispatch_values (&vl);
677
678         sstrncpy (vl.type, "ps_cputime", sizeof (vl.type));
679         vl.values[0].derive = ps->cpu_user_counter;
680         vl.values[1].derive = ps->cpu_system_counter;
681         vl.values_len = 2;
682         plugin_dispatch_values (&vl);
683
684         sstrncpy (vl.type, "ps_count", sizeof (vl.type));
685         vl.values[0].gauge = ps->num_proc;
686         vl.values[1].gauge = ps->num_lwp;
687         vl.values_len = 2;
688         plugin_dispatch_values (&vl);
689
690         sstrncpy (vl.type, "ps_pagefaults", sizeof (vl.type));
691         vl.values[0].derive = ps->vmem_minflt_counter;
692         vl.values[1].derive = ps->vmem_majflt_counter;
693         vl.values_len = 2;
694         plugin_dispatch_values (&vl);
695
696         if ( (ps->io_rchar != -1) && (ps->io_wchar != -1) )
697         {
698                 sstrncpy (vl.type, "ps_disk_octets", sizeof (vl.type));
699                 vl.values[0].derive = ps->io_rchar;
700                 vl.values[1].derive = ps->io_wchar;
701                 vl.values_len = 2;
702                 plugin_dispatch_values (&vl);
703         }
704
705         if ( (ps->io_syscr != -1) && (ps->io_syscw != -1) )
706         {
707                 sstrncpy (vl.type, "ps_disk_ops", sizeof (vl.type));
708                 vl.values[0].derive = ps->io_syscr;
709                 vl.values[1].derive = ps->io_syscw;
710                 vl.values_len = 2;
711                 plugin_dispatch_values (&vl);
712         }
713
714         DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; "
715                         "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
716                         "vmem_code = %lu; "
717                         "vmem_minflt_counter = %"PRIi64"; vmem_majflt_counter = %"PRIi64"; "
718                         "cpu_user_counter = %"PRIi64"; cpu_system_counter = %"PRIi64"; "
719                         "io_rchar = %"PRIi64"; io_wchar = %"PRIi64"; "
720                         "io_syscr = %"PRIi64"; io_syscw = %"PRIi64";",
721                         ps->name, ps->num_proc, ps->num_lwp,
722                         ps->vmem_size, ps->vmem_rss,
723                         ps->vmem_data, ps->vmem_code,
724                         ps->vmem_minflt_counter, ps->vmem_majflt_counter,
725                         ps->cpu_user_counter, ps->cpu_system_counter,
726                         ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw);
727 } /* void ps_submit_proc_list */
728
729 /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
730 #if KERNEL_LINUX
731 static int ps_read_tasks (int pid)
732 {
733         char           dirname[64];
734         DIR           *dh;
735         struct dirent *ent;
736         int count = 0;
737
738         ssnprintf (dirname, sizeof (dirname), "/proc/%i/task", pid);
739
740         if ((dh = opendir (dirname)) == NULL)
741         {
742                 DEBUG ("Failed to open directory `%s'", dirname);
743                 return (-1);
744         }
745
746         while ((ent = readdir (dh)) != NULL)
747         {
748                 if (!isdigit ((int) ent->d_name[0]))
749                         continue;
750                 else
751                         count++;
752         }
753         closedir (dh);
754
755         return ((count >= 1) ? count : 1);
756 } /* int *ps_read_tasks */
757
758 /* Read advanced virtual memory data from /proc/pid/status */
759 static procstat_t *ps_read_vmem (int pid, procstat_t *ps)
760 {
761         FILE *fh;
762         char buffer[1024];
763         char filename[64];
764         unsigned long long lib = 0;
765         unsigned long long exe = 0;
766         unsigned long long data = 0;
767         char *fields[8];
768         int numfields;
769
770         ssnprintf (filename, sizeof (filename), "/proc/%i/status", pid);
771         if ((fh = fopen (filename, "r")) == NULL)
772                 return (NULL);
773
774         while (fgets (buffer, sizeof(buffer), fh) != NULL)
775         {
776                 long long tmp;
777                 char *endptr;
778
779                 if (strncmp (buffer, "Vm", 2) != 0)
780                         continue;
781
782                 numfields = strsplit (buffer, fields,
783                                       STATIC_ARRAY_SIZE (fields));
784
785                 if (numfields < 2)
786                         continue;
787
788                 errno = 0;
789                 endptr = NULL;
790                 tmp = strtoll (fields[1], &endptr, /* base = */ 10);
791                 if ((errno == 0) && (endptr != fields[1]))
792                 {
793                         if (strncmp (buffer, "VmData", 6) == 0) 
794                         {
795                                 data = tmp;
796                         }
797                         else if (strncmp (buffer, "VmLib", 5) == 0)
798                         {
799                                 lib = tmp;
800                         }
801                         else if  (strncmp(buffer, "VmExe", 5) == 0)
802                         {
803                                 exe = tmp;
804                         }
805                 }
806         } /* while (fgets) */
807
808         if (fclose (fh))
809         {
810                 char errbuf[1024];
811                 WARNING ("processes: fclose: %s",
812                                 sstrerror (errno, errbuf, sizeof (errbuf)));
813         }
814
815         ps->vmem_data = data * 1024;
816         ps->vmem_code = (exe + lib) * 1024;
817
818         return (ps);
819 } /* procstat_t *ps_read_vmem */
820
821 static procstat_t *ps_read_io (int pid, procstat_t *ps)
822 {
823         FILE *fh;
824         char buffer[1024];
825         char filename[64];
826
827         char *fields[8];
828         int numfields;
829
830         ssnprintf (filename, sizeof (filename), "/proc/%i/io", pid);
831         if ((fh = fopen (filename, "r")) == NULL)
832                 return (NULL);
833
834         while (fgets (buffer, sizeof (buffer), fh) != NULL)
835         {
836                 derive_t *val = NULL;
837                 long long tmp;
838                 char *endptr;
839
840                 if (strncasecmp (buffer, "rchar:", 6) == 0)
841                         val = &(ps->io_rchar);
842                 else if (strncasecmp (buffer, "wchar:", 6) == 0)
843                         val = &(ps->io_wchar);
844                 else if (strncasecmp (buffer, "syscr:", 6) == 0)
845                         val = &(ps->io_syscr);
846                 else if (strncasecmp (buffer, "syscw:", 6) == 0)
847                         val = &(ps->io_syscw);
848                 else
849                         continue;
850
851                 numfields = strsplit (buffer, fields,
852                                 STATIC_ARRAY_SIZE (fields));
853
854                 if (numfields < 2)
855                         continue;
856
857                 errno = 0;
858                 endptr = NULL;
859                 tmp = strtoll (fields[1], &endptr, /* base = */ 10);
860                 if ((errno != 0) || (endptr == fields[1]))
861                         *val = -1;
862                 else
863                         *val = (derive_t) tmp;
864         } /* while (fgets) */
865
866         if (fclose (fh))
867         {
868                 char errbuf[1024];
869                 WARNING ("processes: fclose: %s",
870                                 sstrerror (errno, errbuf, sizeof (errbuf)));
871         }
872
873         return (ps);
874 } /* procstat_t *ps_read_io */
875
876 int ps_read_process (int pid, procstat_t *ps, char *state)
877 {
878         char  filename[64];
879         char  buffer[1024];
880
881         char *fields[64];
882         char  fields_len;
883
884         int   i;
885
886         int   name_len;
887
888         derive_t cpu_user_counter;
889         derive_t cpu_system_counter;
890         long long unsigned vmem_size;
891         long long unsigned vmem_rss;
892         long long unsigned stack_size;
893
894         memset (ps, 0, sizeof (procstat_t));
895
896         ssnprintf (filename, sizeof (filename), "/proc/%i/stat", pid);
897
898         i = read_file_contents (filename, buffer, sizeof(buffer) - 1);
899         if (i <= 0)
900                 return (-1);
901         buffer[i] = 0;
902
903         fields_len = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
904         if (fields_len < 24)
905         {
906                 DEBUG ("processes plugin: ps_read_process (pid = %i):"
907                                 " `%s' has only %i fields..",
908                                 (int) pid, filename, fields_len);
909                 return (-1);
910         }
911
912         /* copy the name, strip brackets in the process */
913         name_len = strlen (fields[1]) - 2;
914         if ((fields[1][0] != '(') || (fields[1][name_len + 1] != ')'))
915         {
916                 DEBUG ("No brackets found in process name: `%s'", fields[1]);
917                 return (-1);
918         }
919         fields[1] = fields[1] + 1;
920         fields[1][name_len] = '\0';
921         strncpy (ps->name, fields[1], PROCSTAT_NAME_LEN);
922
923
924         *state = fields[2][0];
925
926         if (*state == 'Z')
927         {
928                 ps->num_lwp  = 0;
929                 ps->num_proc = 0;
930         }
931         else
932         {
933                 if ( (ps->num_lwp = ps_read_tasks (pid)) == -1 )
934                 {
935                         /* returns -1 => kernel 2.4 */
936                         ps->num_lwp = 1;
937                 }
938                 ps->num_proc = 1;
939         }
940
941         /* Leave the rest at zero if this is only a zombi */
942         if (ps->num_proc == 0)
943         {
944                 DEBUG ("processes plugin: This is only a zombi: pid = %i; "
945                                 "name = %s;", pid, ps->name);
946                 return (0);
947         }
948
949         cpu_user_counter   = atoll (fields[13]);
950         cpu_system_counter = atoll (fields[14]);
951         vmem_size          = atoll (fields[22]);
952         vmem_rss           = atoll (fields[23]);
953         ps->vmem_minflt_counter = atoll (fields[9]);
954         ps->vmem_majflt_counter = atoll (fields[11]);
955
956         {
957                 unsigned long long stack_start = atoll (fields[27]);
958                 unsigned long long stack_ptr   = atoll (fields[28]);
959
960                 stack_size = (stack_start > stack_ptr)
961                         ? stack_start - stack_ptr
962                         : stack_ptr - stack_start;
963         }
964
965         /* Convert jiffies to useconds */
966         cpu_user_counter   = cpu_user_counter   * 1000000 / CONFIG_HZ;
967         cpu_system_counter = cpu_system_counter * 1000000 / CONFIG_HZ;
968         vmem_rss = vmem_rss * pagesize_g;
969
970         if ( (ps_read_vmem(pid, ps)) == NULL)
971         {
972                 /* No VMem data */
973                 ps->vmem_data = -1;
974                 ps->vmem_code = -1;
975                 DEBUG("ps_read_process: did not get vmem data for pid %i",pid);
976         }
977
978         ps->cpu_user_counter = cpu_user_counter;
979         ps->cpu_system_counter = cpu_system_counter;
980         ps->vmem_size = (unsigned long) vmem_size;
981         ps->vmem_rss = (unsigned long) vmem_rss;
982         ps->stack_size = (unsigned long) stack_size;
983
984         if ( (ps_read_io (pid, ps)) == NULL)
985         {
986                 /* no io data */
987                 ps->io_rchar = -1;
988                 ps->io_wchar = -1;
989                 ps->io_syscr = -1;
990                 ps->io_syscw = -1;
991
992                 DEBUG("ps_read_process: not get io data for pid %i",pid);
993         }
994
995         /* success */
996         return (0);
997 } /* int ps_read_process (...) */
998
999 static char *ps_get_cmdline (pid_t pid, char *name, char *buf, size_t buf_len)
1000 {
1001         char  *buf_ptr;
1002         size_t len;
1003
1004         char file[PATH_MAX];
1005         int  fd;
1006
1007         size_t n;
1008
1009         if ((pid < 1) || (NULL == buf) || (buf_len < 2))
1010                 return NULL;
1011
1012         ssnprintf (file, sizeof (file), "/proc/%u/cmdline",
1013                         (unsigned int) pid);
1014
1015         errno = 0;
1016         fd = open (file, O_RDONLY);
1017         if (fd < 0) {
1018                 char errbuf[4096];
1019                 /* ENOENT means the process exited while we were handling it.
1020                  * Don't complain about this, it only fills the logs. */
1021                 if (errno != ENOENT)
1022                         WARNING ("processes plugin: Failed to open `%s': %s.", file,
1023                                         sstrerror (errno, errbuf, sizeof (errbuf)));
1024                 return NULL;
1025         }
1026
1027         buf_ptr = buf;
1028         len     = buf_len;
1029
1030         n = 0;
1031
1032         while (42) {
1033                 ssize_t status;
1034
1035                 status = read (fd, (void *)buf_ptr, len);
1036
1037                 if (status < 0) {
1038                         char errbuf[1024];
1039
1040                         if ((EAGAIN == errno) || (EINTR == errno))
1041                                 continue;
1042
1043                         WARNING ("processes plugin: Failed to read from `%s': %s.", file,
1044                                         sstrerror (errno, errbuf, sizeof (errbuf)));
1045                         close (fd);
1046                         return NULL;
1047                 }
1048
1049                 n += status;
1050
1051                 if (status == 0)
1052                         break;
1053
1054                 buf_ptr += status;
1055                 len     -= status;
1056
1057                 if (len <= 0)
1058                         break;
1059         }
1060
1061         close (fd);
1062
1063         if (0 == n) {
1064                 /* cmdline not available; e.g. kernel thread, zombie */
1065                 if (NULL == name)
1066                         return NULL;
1067
1068                 ssnprintf (buf, buf_len, "[%s]", name);
1069                 return buf;
1070         }
1071
1072         assert (n <= buf_len);
1073
1074         if (n == buf_len)
1075                 --n;
1076         buf[n] = '\0';
1077
1078         --n;
1079         /* remove trailing whitespace */
1080         while ((n > 0) && (isspace (buf[n]) || ('\0' == buf[n]))) {
1081                 buf[n] = '\0';
1082                 --n;
1083         }
1084
1085         /* arguments are separated by '\0' in /proc/<pid>/cmdline */
1086         while (n > 0) {
1087                 if ('\0' == buf[n])
1088                         buf[n] = ' ';
1089                 --n;
1090         }
1091         return buf;
1092 } /* char *ps_get_cmdline (...) */
1093
1094 static unsigned long read_fork_rate ()
1095 {
1096         FILE *proc_stat;
1097         char buf[1024];
1098         unsigned long result = 0;
1099         int numfields;
1100         char *fields[3];
1101
1102         proc_stat = fopen("/proc/stat", "r");
1103         if (proc_stat == NULL) {
1104                 char errbuf[1024];
1105                 ERROR ("processes plugin: fopen (/proc/stat) failed: %s",
1106                                 sstrerror (errno, errbuf, sizeof (errbuf)));
1107                 return ULONG_MAX;
1108         }
1109
1110         while (fgets (buf, sizeof(buf), proc_stat) != NULL)
1111         {
1112                 char *endptr;
1113
1114                 numfields = strsplit(buf, fields, STATIC_ARRAY_SIZE (fields));
1115                 if (numfields != 2)
1116                         continue;
1117
1118                 if (strcmp ("processes", fields[0]) != 0)
1119                         continue;
1120
1121                 errno = 0;
1122                 endptr = NULL;
1123                 result = strtoul(fields[1], &endptr, /* base = */ 10);
1124                 if ((endptr == fields[1]) || (errno != 0)) {
1125                         ERROR ("processes plugin: Cannot parse fork rate: %s",
1126                                         fields[1]);
1127                         result = ULONG_MAX;
1128                         break;
1129                 }
1130
1131                 break;
1132         }
1133
1134         fclose(proc_stat);
1135
1136         return result;
1137 }
1138
1139 static void ps_submit_fork_rate (unsigned long value)
1140 {
1141         value_t values[1];
1142         value_list_t vl = VALUE_LIST_INIT;
1143
1144         values[0].derive = (derive_t) value;
1145
1146         vl.values = values;
1147         vl.values_len = 1;
1148         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
1149         sstrncpy (vl.plugin, "processes", sizeof (vl.plugin));
1150         sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
1151         sstrncpy (vl.type, "fork_rate", sizeof (vl.type));
1152         sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
1153
1154         plugin_dispatch_values (&vl);
1155 }
1156
1157 #endif /* KERNEL_LINUX */
1158
1159 #if HAVE_THREAD_INFO
1160 static int mach_get_task_name (task_t t, int *pid, char *name, size_t name_max_len)
1161 {
1162         int mib[4];
1163
1164         struct kinfo_proc kp;
1165         size_t            kp_size;
1166
1167         mib[0] = CTL_KERN;
1168         mib[1] = KERN_PROC;
1169         mib[2] = KERN_PROC_PID;
1170
1171         if (pid_for_task (t, pid) != KERN_SUCCESS)
1172                 return (-1);
1173         mib[3] = *pid;
1174
1175         kp_size = sizeof (kp);
1176         if (sysctl (mib, 4, &kp, &kp_size, NULL, 0) != 0)
1177                 return (-1);
1178
1179         if (name_max_len > (MAXCOMLEN + 1))
1180                 name_max_len = MAXCOMLEN + 1;
1181
1182         strncpy (name, kp.kp_proc.p_comm, name_max_len - 1);
1183         name[name_max_len - 1] = '\0';
1184
1185         DEBUG ("pid = %i; name = %s;", *pid, name);
1186
1187         /* We don't do the special handling for `p_comm == "LaunchCFMApp"' as
1188          * `top' does it, because it is a lot of work and only used when
1189          * debugging. -octo */
1190
1191         return (0);
1192 }
1193 #endif /* HAVE_THREAD_INFO */
1194 /* ------- end of additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
1195
1196 /* do actual readings from kernel */
1197 static int ps_read (void)
1198 {
1199 #if HAVE_THREAD_INFO
1200         kern_return_t            status;
1201
1202         int                      pset;
1203         processor_set_t          port_pset_priv;
1204
1205         int                      task;
1206         task_array_t             task_list;
1207         mach_msg_type_number_t   task_list_len;
1208
1209         int                      task_pid;
1210         char                     task_name[MAXCOMLEN + 1];
1211
1212         int                      thread;
1213         thread_act_array_t       thread_list;
1214         mach_msg_type_number_t   thread_list_len;
1215         thread_basic_info_data_t thread_data;
1216         mach_msg_type_number_t   thread_data_len;
1217
1218         int running  = 0;
1219         int sleeping = 0;
1220         int zombies  = 0;
1221         int stopped  = 0;
1222         int blocked  = 0;
1223
1224         procstat_t *ps;
1225         procstat_entry_t pse;
1226
1227         ps_list_reset ();
1228
1229         /*
1230          * The Mach-concept is a little different from the traditional UNIX
1231          * concept: All the work is done in threads. Threads are contained in
1232          * `tasks'. Therefore, `task status' doesn't make much sense, since
1233          * it's actually a `thread status'.
1234          * Tasks are assigned to sets of processors, so that's where you go to
1235          * get a list.
1236          */
1237         for (pset = 0; pset < pset_list_len; pset++)
1238         {
1239                 if ((status = host_processor_set_priv (port_host_self,
1240                                                 pset_list[pset],
1241                                                 &port_pset_priv)) != KERN_SUCCESS)
1242                 {
1243                         ERROR ("host_processor_set_priv failed: %s\n",
1244                                         mach_error_string (status));
1245                         continue;
1246                 }
1247
1248                 if ((status = processor_set_tasks (port_pset_priv,
1249                                                 &task_list,
1250                                                 &task_list_len)) != KERN_SUCCESS)
1251                 {
1252                         ERROR ("processor_set_tasks failed: %s\n",
1253                                         mach_error_string (status));
1254                         mach_port_deallocate (port_task_self, port_pset_priv);
1255                         continue;
1256                 }
1257
1258                 for (task = 0; task < task_list_len; task++)
1259                 {
1260                         ps = NULL;
1261                         if (mach_get_task_name (task_list[task],
1262                                                 &task_pid,
1263                                                 task_name, PROCSTAT_NAME_LEN) == 0)
1264                         {
1265                                 /* search for at least one match */
1266                                 for (ps = list_head_g; ps != NULL; ps = ps->next)
1267                                         /* FIXME: cmdline should be here instead of NULL */
1268                                         if (ps_list_match (task_name, NULL, ps) == 1)
1269                                                 break;
1270                         }
1271
1272                         /* Collect more detailed statistics for this process */
1273                         if (ps != NULL)
1274                         {
1275                                 task_basic_info_data_t        task_basic_info;
1276                                 mach_msg_type_number_t        task_basic_info_len;
1277                                 task_events_info_data_t       task_events_info;
1278                                 mach_msg_type_number_t        task_events_info_len;
1279                                 task_absolutetime_info_data_t task_absolutetime_info;
1280                                 mach_msg_type_number_t        task_absolutetime_info_len;
1281
1282                                 memset (&pse, '\0', sizeof (pse));
1283                                 pse.id = task_pid;
1284
1285                                 task_basic_info_len = TASK_BASIC_INFO_COUNT;
1286                                 status = task_info (task_list[task],
1287                                                 TASK_BASIC_INFO,
1288                                                 (task_info_t) &task_basic_info,
1289                                                 &task_basic_info_len);
1290                                 if (status != KERN_SUCCESS)
1291                                 {
1292                                         ERROR ("task_info failed: %s",
1293                                                         mach_error_string (status));
1294                                         continue; /* with next thread_list */
1295                                 }
1296
1297                                 task_events_info_len = TASK_EVENTS_INFO_COUNT;
1298                                 status = task_info (task_list[task],
1299                                                 TASK_EVENTS_INFO,
1300                                                 (task_info_t) &task_events_info,
1301                                                 &task_events_info_len);
1302                                 if (status != KERN_SUCCESS)
1303                                 {
1304                                         ERROR ("task_info failed: %s",
1305                                                         mach_error_string (status));
1306                                         continue; /* with next thread_list */
1307                                 }
1308
1309                                 task_absolutetime_info_len = TASK_ABSOLUTETIME_INFO_COUNT;
1310                                 status = task_info (task_list[task],
1311                                                 TASK_ABSOLUTETIME_INFO,
1312                                                 (task_info_t) &task_absolutetime_info,
1313                                                 &task_absolutetime_info_len);
1314                                 if (status != KERN_SUCCESS)
1315                                 {
1316                                         ERROR ("task_info failed: %s",
1317                                                         mach_error_string (status));
1318                                         continue; /* with next thread_list */
1319                                 }
1320
1321                                 pse.num_proc++;
1322                                 pse.vmem_size = task_basic_info.virtual_size;
1323                                 pse.vmem_rss = task_basic_info.resident_size;
1324                                 /* Does not seem to be easily exposed */
1325                                 pse.vmem_data = 0;
1326                                 pse.vmem_code = 0;
1327
1328                                 pse.vmem_minflt_counter = task_events_info.cow_faults;
1329                                 pse.vmem_majflt_counter = task_events_info.faults;
1330
1331                                 pse.cpu_user_counter = task_absolutetime_info.total_user;
1332                                 pse.cpu_system_counter = task_absolutetime_info.total_system;
1333                         }
1334
1335                         status = task_threads (task_list[task], &thread_list,
1336                                         &thread_list_len);
1337                         if (status != KERN_SUCCESS)
1338                         {
1339                                 /* Apple's `top' treats this case a zombie. It
1340                                  * makes sense to some extend: A `zombie'
1341                                  * thread is nonsense, since the task/process
1342                                  * is dead. */
1343                                 zombies++;
1344                                 DEBUG ("task_threads failed: %s",
1345                                                 mach_error_string (status));
1346                                 if (task_list[task] != port_task_self)
1347                                         mach_port_deallocate (port_task_self,
1348                                                         task_list[task]);
1349                                 continue; /* with next task_list */
1350                         }
1351
1352                         for (thread = 0; thread < thread_list_len; thread++)
1353                         {
1354                                 thread_data_len = THREAD_BASIC_INFO_COUNT;
1355                                 status = thread_info (thread_list[thread],
1356                                                 THREAD_BASIC_INFO,
1357                                                 (thread_info_t) &thread_data,
1358                                                 &thread_data_len);
1359                                 if (status != KERN_SUCCESS)
1360                                 {
1361                                         ERROR ("thread_info failed: %s",
1362                                                         mach_error_string (status));
1363                                         if (task_list[task] != port_task_self)
1364                                                 mach_port_deallocate (port_task_self,
1365                                                                 thread_list[thread]);
1366                                         continue; /* with next thread_list */
1367                                 }
1368
1369                                 if (ps != NULL)
1370                                         pse.num_lwp++;
1371
1372                                 switch (thread_data.run_state)
1373                                 {
1374                                         case TH_STATE_RUNNING:
1375                                                 running++;
1376                                                 break;
1377                                         case TH_STATE_STOPPED:
1378                                         /* What exactly is `halted'? */
1379                                         case TH_STATE_HALTED:
1380                                                 stopped++;
1381                                                 break;
1382                                         case TH_STATE_WAITING:
1383                                                 sleeping++;
1384                                                 break;
1385                                         case TH_STATE_UNINTERRUPTIBLE:
1386                                                 blocked++;
1387                                                 break;
1388                                         /* There is no `zombie' case here,
1389                                          * since there are no zombie-threads.
1390                                          * There's only zombie tasks, which are
1391                                          * handled above. */
1392                                         default:
1393                                                 WARNING ("Unknown thread status: %i",
1394                                                                 thread_data.run_state);
1395                                                 break;
1396                                 } /* switch (thread_data.run_state) */
1397
1398                                 if (task_list[task] != port_task_self)
1399                                 {
1400                                         status = mach_port_deallocate (port_task_self,
1401                                                         thread_list[thread]);
1402                                         if (status != KERN_SUCCESS)
1403                                                 ERROR ("mach_port_deallocate failed: %s",
1404                                                                 mach_error_string (status));
1405                                 }
1406                         } /* for (thread_list) */
1407
1408                         if ((status = vm_deallocate (port_task_self,
1409                                                         (vm_address_t) thread_list,
1410                                                         thread_list_len * sizeof (thread_act_t)))
1411                                         != KERN_SUCCESS)
1412                         {
1413                                 ERROR ("vm_deallocate failed: %s",
1414                                                 mach_error_string (status));
1415                         }
1416                         thread_list = NULL;
1417                         thread_list_len = 0;
1418
1419                         /* Only deallocate the task port, if it isn't our own.
1420                          * Don't know what would happen in that case, but this
1421                          * is what Apple's top does.. ;) */
1422                         if (task_list[task] != port_task_self)
1423                         {
1424                                 status = mach_port_deallocate (port_task_self,
1425                                                 task_list[task]);
1426                                 if (status != KERN_SUCCESS)
1427                                         ERROR ("mach_port_deallocate failed: %s",
1428                                                         mach_error_string (status));
1429                         }
1430
1431                         if (ps != NULL)
1432                                 /* FIXME: cmdline should be here instead of NULL */
1433                                 ps_list_add (task_name, NULL, &pse);
1434                 } /* for (task_list) */
1435
1436                 if ((status = vm_deallocate (port_task_self,
1437                                 (vm_address_t) task_list,
1438                                 task_list_len * sizeof (task_t))) != KERN_SUCCESS)
1439                 {
1440                         ERROR ("vm_deallocate failed: %s",
1441                                         mach_error_string (status));
1442                 }
1443                 task_list = NULL;
1444                 task_list_len = 0;
1445
1446                 if ((status = mach_port_deallocate (port_task_self, port_pset_priv))
1447                                 != KERN_SUCCESS)
1448                 {
1449                         ERROR ("mach_port_deallocate failed: %s",
1450                                         mach_error_string (status));
1451                 }
1452         } /* for (pset_list) */
1453
1454         ps_submit_state ("running", running);
1455         ps_submit_state ("sleeping", sleeping);
1456         ps_submit_state ("zombies", zombies);
1457         ps_submit_state ("stopped", stopped);
1458         ps_submit_state ("blocked", blocked);
1459
1460         for (ps = list_head_g; ps != NULL; ps = ps->next)
1461                 ps_submit_proc_list (ps);
1462 /* #endif HAVE_THREAD_INFO */
1463
1464 #elif KERNEL_LINUX
1465         int running  = 0;
1466         int sleeping = 0;
1467         int zombies  = 0;
1468         int stopped  = 0;
1469         int paging   = 0;
1470         int blocked  = 0;
1471
1472         struct dirent *ent;
1473         DIR           *proc;
1474         int            pid;
1475
1476         char cmdline[ARG_MAX];
1477
1478         int        status;
1479         procstat_t ps;
1480         procstat_entry_t pse;
1481         char       state;
1482
1483         unsigned long fork_rate;
1484
1485         procstat_t *ps_ptr;
1486
1487         running = sleeping = zombies = stopped = paging = blocked = 0;
1488         ps_list_reset ();
1489
1490         if ((proc = opendir ("/proc")) == NULL)
1491         {
1492                 char errbuf[1024];
1493                 ERROR ("Cannot open `/proc': %s",
1494                                 sstrerror (errno, errbuf, sizeof (errbuf)));
1495                 return (-1);
1496         }
1497
1498         while ((ent = readdir (proc)) != NULL)
1499         {
1500                 if (!isdigit (ent->d_name[0]))
1501                         continue;
1502
1503                 if ((pid = atoi (ent->d_name)) < 1)
1504                         continue;
1505
1506                 status = ps_read_process (pid, &ps, &state);
1507                 if (status != 0)
1508                 {
1509                         DEBUG ("ps_read_process failed: %i", status);
1510                         continue;
1511                 }
1512
1513                 pse.id       = pid;
1514                 pse.age      = 0;
1515
1516                 pse.num_proc   = ps.num_proc;
1517                 pse.num_lwp    = ps.num_lwp;
1518                 pse.vmem_size  = ps.vmem_size;
1519                 pse.vmem_rss   = ps.vmem_rss;
1520                 pse.vmem_data  = ps.vmem_data;
1521                 pse.vmem_code  = ps.vmem_code;
1522                 pse.stack_size = ps.stack_size;
1523
1524                 pse.vmem_minflt = 0;
1525                 pse.vmem_minflt_counter = ps.vmem_minflt_counter;
1526                 pse.vmem_majflt = 0;
1527                 pse.vmem_majflt_counter = ps.vmem_majflt_counter;
1528
1529                 pse.cpu_user = 0;
1530                 pse.cpu_user_counter = ps.cpu_user_counter;
1531                 pse.cpu_system = 0;
1532                 pse.cpu_system_counter = ps.cpu_system_counter;
1533
1534                 pse.io_rchar = ps.io_rchar;
1535                 pse.io_wchar = ps.io_wchar;
1536                 pse.io_syscr = ps.io_syscr;
1537                 pse.io_syscw = ps.io_syscw;
1538
1539                 switch (state)
1540                 {
1541                         case 'R': running++;  break;
1542                         case 'S': sleeping++; break;
1543                         case 'D': blocked++;  break;
1544                         case 'Z': zombies++;  break;
1545                         case 'T': stopped++;  break;
1546                         case 'W': paging++;   break;
1547                 }
1548
1549                 ps_list_add (ps.name,
1550                                 ps_get_cmdline (pid, ps.name, cmdline, sizeof (cmdline)),
1551                                 &pse);
1552         }
1553
1554         closedir (proc);
1555
1556         ps_submit_state ("running",  running);
1557         ps_submit_state ("sleeping", sleeping);
1558         ps_submit_state ("zombies",  zombies);
1559         ps_submit_state ("stopped",  stopped);
1560         ps_submit_state ("paging",   paging);
1561         ps_submit_state ("blocked",  blocked);
1562
1563         for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
1564                 ps_submit_proc_list (ps_ptr);
1565
1566         fork_rate = read_fork_rate();
1567         if (fork_rate != ULONG_MAX)
1568                 ps_submit_fork_rate(fork_rate);
1569 /* #endif KERNEL_LINUX */
1570
1571 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
1572         int running  = 0;
1573         int sleeping = 0;
1574         int zombies  = 0;
1575         int stopped  = 0;
1576         int blocked  = 0;
1577         int idle     = 0;
1578         int wait     = 0;
1579
1580         kvm_t *kd;
1581         char errbuf[1024];
1582         char cmdline[ARG_MAX];
1583         char *cmdline_ptr;
1584         struct kinfo_proc *procs;          /* array of processes */
1585         char **argv;
1586         int count;                         /* returns number of processes */
1587         int i;
1588
1589         procstat_t *ps_ptr;
1590         procstat_entry_t pse;
1591
1592         ps_list_reset ();
1593
1594         /* Open the kvm interface, get a descriptor */
1595         kd = kvm_open (NULL, NULL, NULL, 0, errbuf);
1596         if (kd == NULL)
1597         {
1598                 ERROR ("processes plugin: Cannot open kvm interface: %s",
1599                                 errbuf);
1600                 return (0);
1601         }
1602
1603         /* Get the list of processes. */
1604         procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, &count);
1605         if (procs == NULL)
1606         {
1607                 ERROR ("processes plugin: Cannot get kvm processes list: %s",
1608                                 kvm_geterr(kd));
1609                 kvm_close (kd);
1610                 return (0);
1611         }
1612
1613         /* Iterate through the processes in kinfo_proc */
1614         for (i = 0; i < count; i++)
1615         {
1616                 /* retrieve the arguments */
1617                 cmdline[0] = 0;
1618                 cmdline_ptr = NULL;
1619
1620                 argv = kvm_getargv (kd, (const struct kinfo_proc *) &(procs[i]), 0);
1621                 if (argv != NULL)
1622                 {
1623                         int status;
1624                         int argc;
1625
1626                         argc = 0;
1627                         while (argv[argc] != NULL)
1628                                 argc++;
1629
1630                         status = strjoin (cmdline, sizeof (cmdline),
1631                                         argv, argc, " ");
1632
1633                         if (status < 0)
1634                         {
1635                                 WARNING ("processes plugin: Command line did "
1636                                                 "not fit into buffer.");
1637                         }
1638                         else
1639                         {
1640                                 cmdline_ptr = &cmdline[0];
1641                         }
1642                 }
1643
1644                 pse.id       = procs[i].ki_pid;
1645                 pse.age      = 0;
1646
1647                 pse.num_proc = 1;
1648                 pse.num_lwp  = procs[i].ki_numthreads;
1649
1650                 pse.vmem_size = procs[i].ki_size;
1651                 pse.vmem_rss = procs[i].ki_rssize * getpagesize();
1652                 pse.vmem_data = procs[i].ki_dsize * getpagesize();
1653                 pse.vmem_code = procs[i].ki_tsize * getpagesize();
1654                 pse.stack_size = procs[i].ki_ssize * getpagesize();
1655                 pse.vmem_minflt = 0;
1656                 pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
1657                 pse.vmem_majflt = 0;
1658                 pse.vmem_majflt_counter = procs[i].ki_rusage.ru_majflt;
1659
1660                 pse.cpu_user = 0;
1661                 pse.cpu_user_counter = procs[i].ki_rusage.ru_utime.tv_sec
1662                         * 1000
1663                         + procs[i].ki_rusage.ru_utime.tv_usec;
1664                 pse.cpu_system = 0;
1665                 pse.cpu_system_counter = procs[i].ki_rusage.ru_stime.tv_sec
1666                         * 1000
1667                         + procs[i].ki_rusage.ru_stime.tv_usec;
1668
1669                 /* no io data */
1670                 pse.io_rchar = -1;
1671                 pse.io_wchar = -1;
1672                 pse.io_syscr = -1;
1673                 pse.io_syscw = -1;
1674
1675                 switch (procs[i].ki_stat)
1676                 {
1677                         case SSTOP:     stopped++;      break;
1678                         case SSLEEP:    sleeping++;     break;
1679                         case SRUN:      running++;      break;
1680                         case SIDL:      idle++;         break;
1681                         case SWAIT:     wait++;         break;
1682                         case SLOCK:     blocked++;      break;
1683                         case SZOMB:     zombies++;      break;
1684                 }
1685
1686                 ps_list_add (procs[i].ki_comm, cmdline_ptr, &pse);
1687         }
1688
1689         kvm_close(kd);
1690
1691         ps_submit_state ("running",  running);
1692         ps_submit_state ("sleeping", sleeping);
1693         ps_submit_state ("zombies",  zombies);
1694         ps_submit_state ("stopped",  stopped);
1695         ps_submit_state ("blocked",  blocked);
1696         ps_submit_state ("idle",     idle);
1697         ps_submit_state ("wait",     wait);
1698
1699         for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
1700                 ps_submit_proc_list (ps_ptr);
1701 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
1702
1703 #elif HAVE_PROCINFO_H
1704         /* AIX */
1705         int running  = 0;
1706         int sleeping = 0;
1707         int zombies  = 0;
1708         int stopped  = 0;
1709         int paging   = 0;
1710         int blocked  = 0;
1711
1712         pid_t pindex = 0;
1713         int nprocs;
1714
1715         procstat_t *ps;
1716         procstat_entry_t pse;
1717
1718         ps_list_reset ();
1719         while ((nprocs = getprocs64 (procentry, sizeof(struct procentry64),
1720                                         /* fdsinfo = */ NULL, sizeof(struct fdsinfo64),
1721                                         &pindex, MAXPROCENTRY)) > 0)
1722         {
1723                 int i;
1724
1725                 for (i = 0; i < nprocs; i++)
1726                 {
1727                         tid64_t thindex;
1728                         int nthreads;
1729                         char arglist[MAXARGLN+1];
1730                         char *cargs;
1731                         char *cmdline;
1732
1733                         if (procentry[i].pi_state == SNONE) continue;
1734                         /* if (procentry[i].pi_state == SZOMB)  FIXME */
1735
1736                         cmdline = procentry[i].pi_comm;
1737                         cargs = procentry[i].pi_comm;
1738                         if ( procentry[i].pi_flags & SKPROC )
1739                         {
1740                                 if (procentry[i].pi_pid == 0)
1741                                         cmdline = "swapper";
1742                                 cargs = cmdline;
1743                         }
1744                         else
1745                         {
1746                                 if (getargs(&procentry[i], sizeof(struct procentry64), arglist, MAXARGLN) >= 0)
1747                                 {
1748                                         int n;
1749
1750                                         n = -1;
1751                                         while (++n < MAXARGLN)
1752                                         {
1753                                                 if (arglist[n] == '\0')
1754                                                 {
1755                                                         if (arglist[n+1] == '\0')
1756                                                                 break;
1757                                                         arglist[n] = ' ';
1758                                                 }
1759                                         }
1760                                         cargs = arglist;
1761                                 }
1762                         }
1763
1764                         pse.id       = procentry[i].pi_pid;
1765                         pse.age      = 0;
1766                         pse.num_lwp  = procentry[i].pi_thcount;
1767                         pse.num_proc = 1;
1768
1769                         thindex=0;
1770                         while ((nthreads = getthrds64(procentry[i].pi_pid,
1771                                                         thrdentry, sizeof(struct thrdentry64),
1772                                                         &thindex, MAXTHRDENTRY)) > 0)
1773                         {
1774                                 int j;
1775
1776                                 for (j=0; j< nthreads; j++)
1777                                 {
1778                                         switch (thrdentry[j].ti_state)
1779                                         {
1780                                                 /* case TSNONE: break; */
1781                                                 case TSIDL:     blocked++;      break; /* FIXME is really blocked */
1782                                                 case TSRUN:     running++;      break;
1783                                                 case TSSLEEP:   sleeping++;     break;
1784                                                 case TSSWAP:    paging++;       break;
1785                                                 case TSSTOP:    stopped++;      break;
1786                                                 case TSZOMB:    zombies++;      break;
1787                                         }
1788                                 }
1789                                 if (nthreads < MAXTHRDENTRY)
1790                                         break;
1791                         }
1792
1793                         pse.cpu_user = 0;
1794                         /* tv_usec is nanosec ??? */
1795                         pse.cpu_user_counter = procentry[i].pi_ru.ru_utime.tv_sec * 1000000 +
1796                                 procentry[i].pi_ru.ru_utime.tv_usec / 1000;
1797
1798                         pse.cpu_system = 0;
1799                         /* tv_usec is nanosec ??? */
1800                         pse.cpu_system_counter = procentry[i].pi_ru.ru_stime.tv_sec * 1000000 +
1801                                 procentry[i].pi_ru.ru_stime.tv_usec / 1000;
1802
1803                         pse.vmem_minflt = 0;
1804                         pse.vmem_minflt_counter = procentry[i].pi_minflt;
1805                         pse.vmem_majflt = 0;
1806                         pse.vmem_majflt_counter = procentry[i].pi_majflt;
1807
1808                         pse.vmem_size = procentry[i].pi_tsize + procentry[i].pi_dvm * pagesize;
1809                         pse.vmem_rss = (procentry[i].pi_drss + procentry[i].pi_trss) * pagesize;
1810                         /* Not supported */
1811                         pse.vmem_data = 0;
1812                         pse.vmem_code = 0;
1813                         pse.stack_size =  0;
1814
1815                         pse.io_rchar = -1;
1816                         pse.io_wchar = -1;
1817                         pse.io_syscr = -1;
1818                         pse.io_syscw = -1;
1819
1820                         ps_list_add (cmdline, cargs, &pse);
1821                 } /* for (i = 0 .. nprocs) */
1822
1823                 if (nprocs < MAXPROCENTRY)
1824                         break;
1825         } /* while (getprocs64() > 0) */
1826         ps_submit_state ("running",  running);
1827         ps_submit_state ("sleeping", sleeping);
1828         ps_submit_state ("zombies",  zombies);
1829         ps_submit_state ("stopped",  stopped);
1830         ps_submit_state ("paging",   paging);
1831         ps_submit_state ("blocked",  blocked);
1832
1833         for (ps = list_head_g; ps != NULL; ps = ps->next)
1834                 ps_submit_proc_list (ps);
1835 #endif /* HAVE_PROCINFO_H */
1836
1837         return (0);
1838 } /* int ps_read */
1839
1840 void module_register (void)
1841 {
1842         plugin_register_complex_config ("processes", ps_config);
1843         plugin_register_init ("processes", ps_init);
1844         plugin_register_read ("processes", ps_read);
1845 } /* void module_register */