Merge branch 'collectd-5.6' into collectd-5.7
[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  * Copyright (C) 2012       Cosmin Ioiart
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU General Public License as published by the
14  * Free Software Foundation; either version 2 of the License, or (at your
15  * option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, write to the Free Software Foundation, Inc.,
24  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
25  *
26  * Authors:
27  *   Lyonel Vincent <lyonel at ezix.org>
28  *   Florian octo Forster <octo at collectd.org>
29  *   Oleg King <king2 at kaluga.ru>
30  *   Sebastian Harl <sh at tokkee.org>
31  *   Andrés J. Díaz <ajdiaz at connectical.com>
32  *   Manuel Sanmartin
33  *   Clément Stenac <clement.stenac at diwi.org>
34  *   Cosmin Ioiart <cioiart at gmail.com>
35  **/
36
37 #include "collectd.h"
38
39 #include "common.h"
40 #include "plugin.h"
41
42 /* Include header files for the mach system, if they exist.. */
43 #if HAVE_THREAD_INFO
44 #if HAVE_MACH_MACH_INIT_H
45 #include <mach/mach_init.h>
46 #endif
47 #if HAVE_MACH_HOST_PRIV_H
48 #include <mach/host_priv.h>
49 #endif
50 #if HAVE_MACH_MACH_ERROR_H
51 #include <mach/mach_error.h>
52 #endif
53 #if HAVE_MACH_MACH_HOST_H
54 #include <mach/mach_host.h>
55 #endif
56 #if HAVE_MACH_MACH_PORT_H
57 #include <mach/mach_port.h>
58 #endif
59 #if HAVE_MACH_MACH_TYPES_H
60 #include <mach/mach_types.h>
61 #endif
62 #if HAVE_MACH_MESSAGE_H
63 #include <mach/message.h>
64 #endif
65 #if HAVE_MACH_PROCESSOR_SET_H
66 #include <mach/processor_set.h>
67 #endif
68 #if HAVE_MACH_TASK_H
69 #include <mach/task.h>
70 #endif
71 #if HAVE_MACH_THREAD_ACT_H
72 #include <mach/thread_act.h>
73 #endif
74 #if HAVE_MACH_VM_REGION_H
75 #include <mach/vm_region.h>
76 #endif
77 #if HAVE_MACH_VM_MAP_H
78 #include <mach/vm_map.h>
79 #endif
80 #if HAVE_MACH_VM_PROT_H
81 #include <mach/vm_prot.h>
82 #endif
83 #if HAVE_SYS_SYSCTL_H
84 #include <sys/sysctl.h>
85 #endif
86 /* #endif HAVE_THREAD_INFO */
87
88 #elif KERNEL_LINUX
89 #if HAVE_LINUX_CONFIG_H
90 #include <linux/config.h>
91 #endif
92 #ifndef CONFIG_HZ
93 #define CONFIG_HZ 100
94 #endif
95 /* #endif KERNEL_LINUX */
96
97 #elif HAVE_LIBKVM_GETPROCS &&                                                  \
98     (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD)
99 #include <kvm.h>
100 #include <sys/param.h>
101 #include <sys/proc.h>
102 #include <sys/sysctl.h>
103 #include <sys/user.h>
104 /* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD ||
105  * HAVE_STRUCT_KINFO_PROC_OPENBSD) */
106
107 #elif HAVE_PROCINFO_H
108 #include <procinfo.h>
109 #include <sys/types.h>
110
111 #define MAXPROCENTRY 32
112 #define MAXTHRDENTRY 16
113 #define MAXARGLN 1024
114 /* #endif HAVE_PROCINFO_H */
115
116 #elif KERNEL_SOLARIS
117 /* Hack: Avoid #error when building a 32-bit binary with
118  * _FILE_OFFSET_BITS=64. There is a reason for this #error, as one
119  * of the structures in <sys/procfs.h> uses an off_t, but that
120  * isn't relevant to our usage of procfs. */
121 #if !defined(_LP64) && _FILE_OFFSET_BITS == 64
122 #define SAVE_FOB_64
123 #undef _FILE_OFFSET_BITS
124 #endif
125
126 #include <procfs.h>
127
128 #ifdef SAVE_FOB_64
129 #define _FILE_OFFSET_BITS 64
130 #undef SAVE_FOB_64
131 #endif
132
133 #include <dirent.h>
134 #include <sys/user.h>
135
136 #ifndef MAXCOMLEN
137 #define MAXCOMLEN 16
138 #endif
139
140 /* #endif KERNEL_SOLARIS */
141
142 #else
143 #error "No applicable input method."
144 #endif
145
146 #if HAVE_REGEX_H
147 #include <regex.h>
148 #endif
149
150 #if HAVE_KSTAT_H
151 #include <kstat.h>
152 #endif
153
154 #ifndef CMDLINE_BUFFER_SIZE
155 #if defined(ARG_MAX) && (ARG_MAX < 4096)
156 #define CMDLINE_BUFFER_SIZE ARG_MAX
157 #else
158 #define CMDLINE_BUFFER_SIZE 4096
159 #endif
160 #endif
161
162 typedef struct procstat_entry_s {
163   unsigned long id;
164   unsigned long age;
165
166   unsigned long num_proc;
167   unsigned long num_lwp;
168   unsigned long vmem_size;
169   unsigned long vmem_rss;
170   unsigned long vmem_data;
171   unsigned long vmem_code;
172   unsigned long stack_size;
173
174   derive_t vmem_minflt_counter;
175   derive_t vmem_majflt_counter;
176
177   derive_t cpu_user_counter;
178   derive_t cpu_system_counter;
179
180   /* io data */
181   derive_t io_rchar;
182   derive_t io_wchar;
183   derive_t io_syscr;
184   derive_t io_syscw;
185   _Bool has_io;
186
187   derive_t cswitch_vol;
188   derive_t cswitch_invol;
189   _Bool has_cswitch;
190
191   struct procstat_entry_s *next;
192 } procstat_entry_t;
193
194 #define PROCSTAT_NAME_LEN 256
195 typedef struct procstat {
196   char name[PROCSTAT_NAME_LEN];
197 #if HAVE_REGEX_H
198   regex_t *re;
199 #endif
200
201   unsigned long num_proc;
202   unsigned long num_lwp;
203   unsigned long vmem_size;
204   unsigned long vmem_rss;
205   unsigned long vmem_data;
206   unsigned long vmem_code;
207   unsigned long stack_size;
208
209   derive_t vmem_minflt_counter;
210   derive_t vmem_majflt_counter;
211
212   derive_t cpu_user_counter;
213   derive_t cpu_system_counter;
214
215   /* io data */
216   derive_t io_rchar;
217   derive_t io_wchar;
218   derive_t io_syscr;
219   derive_t io_syscw;
220
221   derive_t cswitch_vol;
222   derive_t cswitch_invol;
223
224   struct procstat *next;
225   struct procstat_entry_s *instances;
226 } procstat_t;
227
228 static procstat_t *list_head_g = NULL;
229
230 static _Bool want_init = 1;
231 static _Bool report_ctx_switch = 0;
232
233 #if HAVE_THREAD_INFO
234 static mach_port_t port_host_self;
235 static mach_port_t port_task_self;
236
237 static processor_set_name_array_t pset_list;
238 static mach_msg_type_number_t pset_list_len;
239 /* #endif HAVE_THREAD_INFO */
240
241 #elif KERNEL_LINUX
242 static long pagesize_g;
243 static void ps_fill_details(const procstat_t *ps, procstat_entry_t *entry);
244 /* #endif KERNEL_LINUX */
245
246 #elif HAVE_LIBKVM_GETPROCS &&                                                  \
247     (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD)
248 static int pagesize;
249 /* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD ||
250  * HAVE_STRUCT_KINFO_PROC_OPENBSD) */
251
252 #elif HAVE_PROCINFO_H
253 static struct procentry64 procentry[MAXPROCENTRY];
254 static struct thrdentry64 thrdentry[MAXTHRDENTRY];
255 static int pagesize;
256
257 #ifndef _AIXVERSION_610
258 int getprocs64(void *procsinfo, int sizproc, void *fdsinfo, int sizfd,
259                pid_t *index, int count);
260 int getthrds64(pid_t, void *, int, tid64_t *, int);
261 #endif
262 int getargs(void *processBuffer, int bufferLen, char *argsBuffer, int argsLen);
263 #endif /* HAVE_PROCINFO_H */
264
265 /* put name of process from config to list_head_g tree
266  * list_head_g is a list of 'procstat_t' structs with
267  * processes names we want to watch */
268 static void ps_list_register(const char *name, const char *regexp) {
269   procstat_t *new;
270   procstat_t *ptr;
271   int status;
272
273   new = calloc(1, sizeof(*new));
274   if (new == NULL) {
275     ERROR("processes plugin: ps_list_register: calloc failed.");
276     return;
277   }
278   sstrncpy(new->name, name, sizeof(new->name));
279
280 #if HAVE_REGEX_H
281   if (regexp != NULL) {
282     DEBUG("ProcessMatch: adding \"%s\" as criteria to process %s.", regexp,
283           name);
284     new->re = malloc(sizeof(*new->re));
285     if (new->re == NULL) {
286       ERROR("processes plugin: ps_list_register: malloc failed.");
287       sfree(new);
288       return;
289     }
290
291     status = regcomp(new->re, regexp, REG_EXTENDED | REG_NOSUB);
292     if (status != 0) {
293       DEBUG("ProcessMatch: compiling the regular expression \"%s\" failed.",
294             regexp);
295       sfree(new->re);
296       sfree(new);
297       return;
298     }
299   }
300 #else
301   if (regexp != NULL) {
302     ERROR("processes plugin: ps_list_register: "
303           "Regular expression \"%s\" found in config "
304           "file, but support for regular expressions "
305           "has been disabled at compile time.",
306           regexp);
307     sfree(new);
308     return;
309   }
310 #endif
311
312   for (ptr = list_head_g; ptr != NULL; ptr = ptr->next) {
313     if (strcmp(ptr->name, name) == 0) {
314       WARNING("processes plugin: You have configured more "
315               "than one `Process' or "
316               "`ProcessMatch' with the same name. "
317               "All but the first setting will be "
318               "ignored.");
319 #if HAVE_REGEX_H
320       sfree(new->re);
321 #endif
322       sfree(new);
323       return;
324     }
325
326     if (ptr->next == NULL)
327       break;
328   }
329
330   if (ptr == NULL)
331     list_head_g = new;
332   else
333     ptr->next = new;
334 } /* void ps_list_register */
335
336 /* try to match name against entry, returns 1 if success */
337 static int ps_list_match(const char *name, const char *cmdline,
338                          procstat_t *ps) {
339 #if HAVE_REGEX_H
340   if (ps->re != NULL) {
341     int status;
342     const char *str;
343
344     str = cmdline;
345     if ((str == NULL) || (str[0] == 0))
346       str = name;
347
348     assert(str != NULL);
349
350     status = regexec(ps->re, str,
351                      /* nmatch = */ 0,
352                      /* pmatch = */ NULL,
353                      /* eflags = */ 0);
354     if (status == 0)
355       return (1);
356   } else
357 #endif
358       if (strcmp(ps->name, name) == 0)
359     return (1);
360
361   return (0);
362 } /* int ps_list_match */
363
364 static void ps_update_counter(derive_t *group_counter, derive_t *curr_counter,
365                               derive_t new_counter) {
366   unsigned long curr_value;
367
368   if (want_init) {
369     *curr_counter = new_counter;
370     return;
371   }
372
373   if (new_counter < *curr_counter)
374     curr_value = new_counter + (ULONG_MAX - *curr_counter);
375   else
376     curr_value = new_counter - *curr_counter;
377
378   *curr_counter = new_counter;
379   *group_counter += curr_value;
380 }
381
382 /* add process entry to 'instances' of process 'name' (or refresh it) */
383 static void ps_list_add(const char *name, const char *cmdline,
384                         procstat_entry_t *entry) {
385   procstat_entry_t *pse;
386
387   if (entry->id == 0)
388     return;
389
390   for (procstat_t *ps = list_head_g; ps != NULL; ps = ps->next) {
391     if ((ps_list_match(name, cmdline, ps)) == 0)
392       continue;
393
394 #if KERNEL_LINUX
395     ps_fill_details(ps, entry);
396 #endif
397
398     for (pse = ps->instances; pse != NULL; pse = pse->next)
399       if ((pse->id == entry->id) || (pse->next == NULL))
400         break;
401
402     if ((pse == NULL) || (pse->id != entry->id)) {
403       procstat_entry_t *new;
404
405       new = calloc(1, sizeof(*new));
406       if (new == NULL)
407         return;
408       new->id = entry->id;
409
410       if (pse == NULL)
411         ps->instances = new;
412       else
413         pse->next = new;
414
415       pse = new;
416     }
417
418     pse->age = 0;
419     pse->num_proc = entry->num_proc;
420     pse->num_lwp = entry->num_lwp;
421     pse->vmem_size = entry->vmem_size;
422     pse->vmem_rss = entry->vmem_rss;
423     pse->vmem_data = entry->vmem_data;
424     pse->vmem_code = entry->vmem_code;
425     pse->stack_size = entry->stack_size;
426     pse->io_rchar = entry->io_rchar;
427     pse->io_wchar = entry->io_wchar;
428     pse->io_syscr = entry->io_syscr;
429     pse->io_syscw = entry->io_syscw;
430     pse->cswitch_vol = entry->cswitch_vol;
431     pse->cswitch_invol = entry->cswitch_invol;
432
433     ps->num_proc += pse->num_proc;
434     ps->num_lwp += pse->num_lwp;
435     ps->vmem_size += pse->vmem_size;
436     ps->vmem_rss += pse->vmem_rss;
437     ps->vmem_data += pse->vmem_data;
438     ps->vmem_code += pse->vmem_code;
439     ps->stack_size += pse->stack_size;
440
441     ps->io_rchar += ((pse->io_rchar == -1) ? 0 : pse->io_rchar);
442     ps->io_wchar += ((pse->io_wchar == -1) ? 0 : pse->io_wchar);
443     ps->io_syscr += ((pse->io_syscr == -1) ? 0 : pse->io_syscr);
444     ps->io_syscw += ((pse->io_syscw == -1) ? 0 : pse->io_syscw);
445
446     ps->cswitch_vol += ((pse->cswitch_vol == -1) ? 0 : pse->cswitch_vol);
447     ps->cswitch_invol += ((pse->cswitch_invol == -1) ? 0 : pse->cswitch_invol);
448
449     ps_update_counter(&ps->vmem_minflt_counter, &pse->vmem_minflt_counter,
450                       entry->vmem_minflt_counter);
451     ps_update_counter(&ps->vmem_majflt_counter, &pse->vmem_majflt_counter,
452                       entry->vmem_majflt_counter);
453
454     ps_update_counter(&ps->cpu_user_counter, &pse->cpu_user_counter,
455                       entry->cpu_user_counter);
456     ps_update_counter(&ps->cpu_system_counter, &pse->cpu_system_counter,
457                       entry->cpu_system_counter);
458   }
459 }
460
461 /* remove old entries from instances of processes in list_head_g */
462 static void ps_list_reset(void) {
463   procstat_entry_t *pse;
464   procstat_entry_t *pse_prev;
465
466   for (procstat_t *ps = list_head_g; ps != NULL; ps = ps->next) {
467     ps->num_proc = 0;
468     ps->num_lwp = 0;
469     ps->vmem_size = 0;
470     ps->vmem_rss = 0;
471     ps->vmem_data = 0;
472     ps->vmem_code = 0;
473     ps->stack_size = 0;
474     ps->io_rchar = -1;
475     ps->io_wchar = -1;
476     ps->io_syscr = -1;
477     ps->io_syscw = -1;
478     ps->cswitch_vol = -1;
479     ps->cswitch_invol = -1;
480
481     pse_prev = NULL;
482     pse = ps->instances;
483     while (pse != NULL) {
484       if (pse->age > 10) {
485         DEBUG("Removing this procstat entry cause it's too old: "
486               "id = %lu; name = %s;",
487               pse->id, ps->name);
488
489         if (pse_prev == NULL) {
490           ps->instances = pse->next;
491           free(pse);
492           pse = ps->instances;
493         } else {
494           pse_prev->next = pse->next;
495           free(pse);
496           pse = pse_prev->next;
497         }
498       } else {
499         pse->age++;
500         pse_prev = pse;
501         pse = pse->next;
502       }
503     } /* while (pse != NULL) */
504   }   /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
505 }
506
507 /* put all pre-defined 'Process' names from config to list_head_g tree */
508 static int ps_config(oconfig_item_t *ci) {
509 #if KERNEL_LINUX
510   const size_t max_procname_len = 15;
511 #elif KERNEL_SOLARIS || KERNEL_FREEBSD
512   const size_t max_procname_len = MAXCOMLEN - 1;
513 #endif
514
515   for (int i = 0; i < ci->children_num; ++i) {
516     oconfig_item_t *c = ci->children + i;
517
518     if (strcasecmp(c->key, "Process") == 0) {
519       if ((c->values_num != 1) || (OCONFIG_TYPE_STRING != c->values[0].type)) {
520         ERROR("processes plugin: `Process' expects exactly "
521               "one string argument (got %i).",
522               c->values_num);
523         continue;
524       }
525
526       if (c->children_num != 0) {
527         WARNING("processes plugin: the `Process' config option "
528                 "does not expect any child elements -- ignoring "
529                 "content (%i elements) of the <Process '%s'> block.",
530                 c->children_num, c->values[0].value.string);
531       }
532
533 #if KERNEL_LINUX || KERNEL_SOLARIS || KERNEL_FREEBSD
534       if (strlen(c->values[0].value.string) > max_procname_len) {
535         WARNING("processes plugin: this platform has a %zu character limit "
536                 "to process names. The `Process \"%s\"' option will "
537                 "not work as expected.",
538                 max_procname_len, c->values[0].value.string);
539       }
540 #endif
541
542       ps_list_register(c->values[0].value.string, NULL);
543     } else if (strcasecmp(c->key, "ProcessMatch") == 0) {
544       if ((c->values_num != 2) || (OCONFIG_TYPE_STRING != c->values[0].type) ||
545           (OCONFIG_TYPE_STRING != c->values[1].type)) {
546         ERROR("processes plugin: `ProcessMatch' needs exactly "
547               "two string arguments (got %i).",
548               c->values_num);
549         continue;
550       }
551
552       if (c->children_num != 0) {
553         WARNING("processes plugin: the `ProcessMatch' config option "
554                 "does not expect any child elements -- ignoring "
555                 "content (%i elements) of the <ProcessMatch '%s' '%s'> "
556                 "block.",
557                 c->children_num, c->values[0].value.string,
558                 c->values[1].value.string);
559       }
560
561       ps_list_register(c->values[0].value.string, c->values[1].value.string);
562     } else if (strcasecmp(c->key, "CollectContextSwitch") == 0) {
563       cf_util_get_boolean(c, &report_ctx_switch);
564     } else {
565       ERROR("processes plugin: The `%s' configuration option is not "
566             "understood and will be ignored.",
567             c->key);
568       continue;
569     }
570   }
571
572   return (0);
573 }
574
575 static int ps_init(void) {
576 #if HAVE_THREAD_INFO
577   kern_return_t status;
578
579   port_host_self = mach_host_self();
580   port_task_self = mach_task_self();
581
582   if (pset_list != NULL) {
583     vm_deallocate(port_task_self, (vm_address_t)pset_list,
584                   pset_list_len * sizeof(processor_set_t));
585     pset_list = NULL;
586     pset_list_len = 0;
587   }
588
589   if ((status = host_processor_sets(port_host_self, &pset_list,
590                                     &pset_list_len)) != KERN_SUCCESS) {
591     ERROR("host_processor_sets failed: %s\n", mach_error_string(status));
592     pset_list = NULL;
593     pset_list_len = 0;
594     return (-1);
595   }
596 /* #endif HAVE_THREAD_INFO */
597
598 #elif KERNEL_LINUX
599   pagesize_g = sysconf(_SC_PAGESIZE);
600   DEBUG("pagesize_g = %li; CONFIG_HZ = %i;", pagesize_g, CONFIG_HZ);
601 /* #endif KERNEL_LINUX */
602
603 #elif HAVE_LIBKVM_GETPROCS &&                                                  \
604     (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD)
605   pagesize = getpagesize();
606 /* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD ||
607  * HAVE_STRUCT_KINFO_PROC_OPENBSD) */
608
609 #elif HAVE_PROCINFO_H
610   pagesize = getpagesize();
611 #endif /* HAVE_PROCINFO_H */
612
613   return (0);
614 } /* int ps_init */
615
616 /* submit global state (e.g.: qty of zombies, running, etc..) */
617 static void ps_submit_state(const char *state, double value) {
618   value_list_t vl = VALUE_LIST_INIT;
619
620   vl.values = &(value_t){.gauge = value};
621   vl.values_len = 1;
622   sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
623   sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance));
624   sstrncpy(vl.type, "ps_state", sizeof(vl.type));
625   sstrncpy(vl.type_instance, state, sizeof(vl.type_instance));
626
627   plugin_dispatch_values(&vl);
628 }
629
630 /* submit info about specific process (e.g.: memory taken, cpu usage, etc..) */
631 static void ps_submit_proc_list(procstat_t *ps) {
632   value_list_t vl = VALUE_LIST_INIT;
633   value_t values[2];
634
635   vl.values = values;
636   sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
637   sstrncpy(vl.plugin_instance, ps->name, sizeof(vl.plugin_instance));
638
639   sstrncpy(vl.type, "ps_vm", sizeof(vl.type));
640   vl.values[0].gauge = ps->vmem_size;
641   vl.values_len = 1;
642   plugin_dispatch_values(&vl);
643
644   sstrncpy(vl.type, "ps_rss", sizeof(vl.type));
645   vl.values[0].gauge = ps->vmem_rss;
646   vl.values_len = 1;
647   plugin_dispatch_values(&vl);
648
649   sstrncpy(vl.type, "ps_data", sizeof(vl.type));
650   vl.values[0].gauge = ps->vmem_data;
651   vl.values_len = 1;
652   plugin_dispatch_values(&vl);
653
654   sstrncpy(vl.type, "ps_code", sizeof(vl.type));
655   vl.values[0].gauge = ps->vmem_code;
656   vl.values_len = 1;
657   plugin_dispatch_values(&vl);
658
659   sstrncpy(vl.type, "ps_stacksize", sizeof(vl.type));
660   vl.values[0].gauge = ps->stack_size;
661   vl.values_len = 1;
662   plugin_dispatch_values(&vl);
663
664   sstrncpy(vl.type, "ps_cputime", sizeof(vl.type));
665   vl.values[0].derive = ps->cpu_user_counter;
666   vl.values[1].derive = ps->cpu_system_counter;
667   vl.values_len = 2;
668   plugin_dispatch_values(&vl);
669
670   sstrncpy(vl.type, "ps_count", sizeof(vl.type));
671   vl.values[0].gauge = ps->num_proc;
672   vl.values[1].gauge = ps->num_lwp;
673   vl.values_len = 2;
674   plugin_dispatch_values(&vl);
675
676   sstrncpy(vl.type, "ps_pagefaults", sizeof(vl.type));
677   vl.values[0].derive = ps->vmem_minflt_counter;
678   vl.values[1].derive = ps->vmem_majflt_counter;
679   vl.values_len = 2;
680   plugin_dispatch_values(&vl);
681
682   if ((ps->io_rchar != -1) && (ps->io_wchar != -1)) {
683     sstrncpy(vl.type, "ps_disk_octets", sizeof(vl.type));
684     vl.values[0].derive = ps->io_rchar;
685     vl.values[1].derive = ps->io_wchar;
686     vl.values_len = 2;
687     plugin_dispatch_values(&vl);
688   }
689
690   if ((ps->io_syscr != -1) && (ps->io_syscw != -1)) {
691     sstrncpy(vl.type, "ps_disk_ops", sizeof(vl.type));
692     vl.values[0].derive = ps->io_syscr;
693     vl.values[1].derive = ps->io_syscw;
694     vl.values_len = 2;
695     plugin_dispatch_values(&vl);
696   }
697
698   if (report_ctx_switch) {
699     sstrncpy(vl.type, "contextswitch", sizeof(vl.type));
700     sstrncpy(vl.type_instance, "voluntary", sizeof(vl.type_instance));
701     vl.values[0].derive = ps->cswitch_vol;
702     vl.values_len = 1;
703     plugin_dispatch_values(&vl);
704
705     sstrncpy(vl.type, "contextswitch", sizeof(vl.type));
706     sstrncpy(vl.type_instance, "involuntary", sizeof(vl.type_instance));
707     vl.values[0].derive = ps->cswitch_invol;
708     vl.values_len = 1;
709     plugin_dispatch_values(&vl);
710   }
711
712   DEBUG("name = %s; num_proc = %lu; num_lwp = %lu; "
713         "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
714         "vmem_code = %lu; "
715         "vmem_minflt_counter = %" PRIi64 "; vmem_majflt_counter = %" PRIi64 "; "
716         "cpu_user_counter = %" PRIi64 "; cpu_system_counter = %" PRIi64 "; "
717         "io_rchar = %" PRIi64 "; io_wchar = %" PRIi64 "; "
718         "io_syscr = %" PRIi64 "; io_syscw = %" PRIi64 "; "
719         "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 ";",
720         ps->name, ps->num_proc, ps->num_lwp, ps->vmem_size, ps->vmem_rss,
721         ps->vmem_data, ps->vmem_code, ps->vmem_minflt_counter,
722         ps->vmem_majflt_counter, ps->cpu_user_counter, ps->cpu_system_counter,
723         ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw, ps->cswitch_vol,
724         ps->cswitch_invol);
725 } /* void ps_submit_proc_list */
726
727 #if KERNEL_LINUX || KERNEL_SOLARIS
728 static void ps_submit_fork_rate(derive_t value) {
729   value_list_t vl = VALUE_LIST_INIT;
730
731   vl.values = &(value_t){.derive = value};
732   vl.values_len = 1;
733   sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
734   sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance));
735   sstrncpy(vl.type, "fork_rate", sizeof(vl.type));
736   sstrncpy(vl.type_instance, "", sizeof(vl.type_instance));
737
738   plugin_dispatch_values(&vl);
739 }
740 #endif /* KERNEL_LINUX || KERNEL_SOLARIS*/
741
742 /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
743 #if KERNEL_LINUX
744 static int ps_read_tasks_status(procstat_entry_t *ps) {
745   char dirname[64];
746   DIR *dh;
747   char filename[64];
748   FILE *fh;
749   struct dirent *ent;
750   derive_t cswitch_vol = 0;
751   derive_t cswitch_invol = 0;
752   char buffer[1024];
753   char *fields[8];
754   int numfields;
755
756   ssnprintf(dirname, sizeof(dirname), "/proc/%li/task", ps->id);
757
758   if ((dh = opendir(dirname)) == NULL) {
759     DEBUG("Failed to open directory `%s'", dirname);
760     return (-1);
761   }
762
763   while ((ent = readdir(dh)) != NULL) {
764     char *tpid;
765
766     if (!isdigit((int)ent->d_name[0]))
767       continue;
768
769     tpid = ent->d_name;
770
771     ssnprintf(filename, sizeof(filename), "/proc/%li/task/%s/status", ps->id,
772               tpid);
773     if ((fh = fopen(filename, "r")) == NULL) {
774       DEBUG("Failed to open file `%s'", filename);
775       continue;
776     }
777
778     while (fgets(buffer, sizeof(buffer), fh) != NULL) {
779       derive_t tmp;
780       char *endptr;
781
782       if (strncmp(buffer, "voluntary_ctxt_switches", 23) != 0 &&
783           strncmp(buffer, "nonvoluntary_ctxt_switches", 26) != 0)
784         continue;
785
786       numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
787
788       if (numfields < 2)
789         continue;
790
791       errno = 0;
792       endptr = NULL;
793       tmp = (derive_t)strtoll(fields[1], &endptr, /* base = */ 10);
794       if ((errno == 0) && (endptr != fields[1])) {
795         if (strncmp(buffer, "voluntary_ctxt_switches", 23) == 0) {
796           cswitch_vol += tmp;
797         } else if (strncmp(buffer, "nonvoluntary_ctxt_switches", 26) == 0) {
798           cswitch_invol += tmp;
799         }
800       }
801     } /* while (fgets) */
802
803     if (fclose(fh)) {
804       char errbuf[1024];
805       WARNING("processes: fclose: %s",
806               sstrerror(errno, errbuf, sizeof(errbuf)));
807     }
808   }
809   closedir(dh);
810
811   ps->cswitch_vol = cswitch_vol;
812   ps->cswitch_invol = cswitch_invol;
813
814   return (0);
815 } /* int *ps_read_tasks_status */
816
817 /* Read data from /proc/pid/status */
818 static procstat_t *ps_read_status(long pid, procstat_t *ps) {
819   FILE *fh;
820   char buffer[1024];
821   char filename[64];
822   unsigned long lib = 0;
823   unsigned long exe = 0;
824   unsigned long data = 0;
825   unsigned long threads = 0;
826   char *fields[8];
827   int numfields;
828
829   ssnprintf(filename, sizeof(filename), "/proc/%li/status", pid);
830   if ((fh = fopen(filename, "r")) == NULL)
831     return (NULL);
832
833   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
834     unsigned long tmp;
835     char *endptr;
836
837     if (strncmp(buffer, "Vm", 2) != 0 && strncmp(buffer, "Threads", 7) != 0)
838       continue;
839
840     numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
841
842     if (numfields < 2)
843       continue;
844
845     errno = 0;
846     endptr = NULL;
847     tmp = strtoul(fields[1], &endptr, /* base = */ 10);
848     if ((errno == 0) && (endptr != fields[1])) {
849       if (strncmp(buffer, "VmData", 6) == 0) {
850         data = tmp;
851       } else if (strncmp(buffer, "VmLib", 5) == 0) {
852         lib = tmp;
853       } else if (strncmp(buffer, "VmExe", 5) == 0) {
854         exe = tmp;
855       } else if (strncmp(buffer, "Threads", 7) == 0) {
856         threads = tmp;
857       }
858     }
859   } /* while (fgets) */
860
861   if (fclose(fh)) {
862     char errbuf[1024];
863     WARNING("processes: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
864   }
865
866   ps->vmem_data = data * 1024;
867   ps->vmem_code = (exe + lib) * 1024;
868   if (threads != 0)
869     ps->num_lwp = threads;
870
871   return (ps);
872 } /* procstat_t *ps_read_vmem */
873
874 static int ps_read_io(procstat_entry_t *ps) {
875   FILE *fh;
876   char buffer[1024];
877   char filename[64];
878
879   char *fields[8];
880   int numfields;
881
882   ssnprintf(filename, sizeof(filename), "/proc/%li/io", ps->id);
883   if ((fh = fopen(filename, "r")) == NULL)
884     return (-1);
885
886   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
887     derive_t *val = NULL;
888     long long tmp;
889     char *endptr;
890
891     if (strncasecmp(buffer, "rchar:", 6) == 0)
892       val = &(ps->io_rchar);
893     else if (strncasecmp(buffer, "wchar:", 6) == 0)
894       val = &(ps->io_wchar);
895     else if (strncasecmp(buffer, "syscr:", 6) == 0)
896       val = &(ps->io_syscr);
897     else if (strncasecmp(buffer, "syscw:", 6) == 0)
898       val = &(ps->io_syscw);
899     else
900       continue;
901
902     numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
903
904     if (numfields < 2)
905       continue;
906
907     errno = 0;
908     endptr = NULL;
909     tmp = strtoll(fields[1], &endptr, /* base = */ 10);
910     if ((errno != 0) || (endptr == fields[1]))
911       *val = -1;
912     else
913       *val = (derive_t)tmp;
914   } /* while (fgets) */
915
916   if (fclose(fh)) {
917     char errbuf[1024];
918     WARNING("processes: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
919   }
920   return (0);
921 } /* int ps_read_io (...) */
922
923 static void ps_fill_details(const procstat_t *ps, procstat_entry_t *entry) {
924   if (entry->has_io == 0 && ps_read_io(entry) != 0) {
925     /* no io data */
926     entry->io_rchar = -1;
927     entry->io_wchar = -1;
928     entry->io_syscr = -1;
929     entry->io_syscw = -1;
930
931     DEBUG("ps_read_process: not get io data for pid %li", entry->id);
932   }
933   entry->has_io = 1;
934
935   if (report_ctx_switch) {
936     if (entry->has_cswitch == 0 && ps_read_tasks_status(entry) != 0) {
937       entry->cswitch_vol = -1;
938       entry->cswitch_invol = -1;
939
940       DEBUG("ps_read_tasks_status: not get context "
941             "switch data for pid %li",
942             entry->id);
943     }
944     entry->has_cswitch = 1;
945   }
946 } /* void ps_fill_details (...) */
947
948 static int ps_read_process(long pid, procstat_t *ps, char *state) {
949   char filename[64];
950   char buffer[1024];
951
952   char *fields[64];
953   char fields_len;
954
955   size_t buffer_len;
956
957   char *buffer_ptr;
958   size_t name_start_pos;
959   size_t name_end_pos;
960   size_t name_len;
961
962   derive_t cpu_user_counter;
963   derive_t cpu_system_counter;
964   long long unsigned vmem_size;
965   long long unsigned vmem_rss;
966   long long unsigned stack_size;
967
968   ssize_t status;
969
970   memset(ps, 0, sizeof(procstat_t));
971
972   ssnprintf(filename, sizeof(filename), "/proc/%li/stat", pid);
973
974   status = read_file_contents(filename, buffer, sizeof(buffer) - 1);
975   if (status <= 0)
976     return (-1);
977   buffer_len = (size_t)status;
978   buffer[buffer_len] = 0;
979
980   /* The name of the process is enclosed in parens. Since the name can
981    * contain parens itself, spaces, numbers and pretty much everything
982    * else, use these to determine the process name. We don't use
983    * strchr(3) and strrchr(3) to avoid pointer arithmetic which would
984    * otherwise be required to determine name_len. */
985   name_start_pos = 0;
986   while (name_start_pos < buffer_len && buffer[name_start_pos] != '(')
987     name_start_pos++;
988
989   name_end_pos = buffer_len;
990   while (name_end_pos > 0 && buffer[name_end_pos] != ')')
991     name_end_pos--;
992
993   /* Either '(' or ')' is not found or they are in the wrong order.
994    * Anyway, something weird that shouldn't happen ever. */
995   if (name_start_pos >= name_end_pos) {
996     ERROR("processes plugin: name_start_pos = %zu >= name_end_pos = %zu",
997           name_start_pos, name_end_pos);
998     return (-1);
999   }
1000
1001   name_len = (name_end_pos - name_start_pos) - 1;
1002   if (name_len >= sizeof(ps->name))
1003     name_len = sizeof(ps->name) - 1;
1004
1005   sstrncpy(ps->name, &buffer[name_start_pos + 1], name_len + 1);
1006
1007   if ((buffer_len - name_end_pos) < 2)
1008     return (-1);
1009   buffer_ptr = &buffer[name_end_pos + 2];
1010
1011   fields_len = strsplit(buffer_ptr, fields, STATIC_ARRAY_SIZE(fields));
1012   if (fields_len < 22) {
1013     DEBUG("processes plugin: ps_read_process (pid = %li):"
1014           " `%s' has only %i fields..",
1015           pid, filename, fields_len);
1016     return (-1);
1017   }
1018
1019   *state = fields[0][0];
1020
1021   if (*state == 'Z') {
1022     ps->num_lwp = 0;
1023     ps->num_proc = 0;
1024   } else {
1025     ps->num_lwp = strtoul(fields[17], /* endptr = */ NULL, /* base = */ 10);
1026     if ((ps_read_status(pid, ps)) == NULL) {
1027       /* No VMem data */
1028       ps->vmem_data = -1;
1029       ps->vmem_code = -1;
1030       DEBUG("ps_read_process: did not get vmem data for pid %li", pid);
1031     }
1032     if (ps->num_lwp == 0)
1033       ps->num_lwp = 1;
1034     ps->num_proc = 1;
1035   }
1036
1037   /* Leave the rest at zero if this is only a zombi */
1038   if (ps->num_proc == 0) {
1039     DEBUG("processes plugin: This is only a zombie: pid = %li; "
1040           "name = %s;",
1041           pid, ps->name);
1042     return (0);
1043   }
1044
1045   cpu_user_counter = atoll(fields[11]);
1046   cpu_system_counter = atoll(fields[12]);
1047   vmem_size = atoll(fields[20]);
1048   vmem_rss = atoll(fields[21]);
1049   ps->vmem_minflt_counter = atol(fields[7]);
1050   ps->vmem_majflt_counter = atol(fields[9]);
1051
1052   {
1053     unsigned long long stack_start = atoll(fields[25]);
1054     unsigned long long stack_ptr = atoll(fields[26]);
1055
1056     stack_size = (stack_start > stack_ptr) ? stack_start - stack_ptr
1057                                            : stack_ptr - stack_start;
1058   }
1059
1060   /* Convert jiffies to useconds */
1061   cpu_user_counter = cpu_user_counter * 1000000 / CONFIG_HZ;
1062   cpu_system_counter = cpu_system_counter * 1000000 / CONFIG_HZ;
1063   vmem_rss = vmem_rss * pagesize_g;
1064
1065   ps->cpu_user_counter = cpu_user_counter;
1066   ps->cpu_system_counter = cpu_system_counter;
1067   ps->vmem_size = (unsigned long)vmem_size;
1068   ps->vmem_rss = (unsigned long)vmem_rss;
1069   ps->stack_size = (unsigned long)stack_size;
1070
1071   /* success */
1072   return (0);
1073 } /* int ps_read_process (...) */
1074
1075 static char *ps_get_cmdline(long pid, char *name, char *buf, size_t buf_len) {
1076   char *buf_ptr;
1077   size_t len;
1078
1079   char file[PATH_MAX];
1080   int fd;
1081
1082   size_t n;
1083
1084   if ((pid < 1) || (NULL == buf) || (buf_len < 2))
1085     return NULL;
1086
1087   ssnprintf(file, sizeof(file), "/proc/%li/cmdline", pid);
1088
1089   errno = 0;
1090   fd = open(file, O_RDONLY);
1091   if (fd < 0) {
1092     char errbuf[4096];
1093     /* ENOENT means the process exited while we were handling it.
1094      * Don't complain about this, it only fills the logs. */
1095     if (errno != ENOENT)
1096       WARNING("processes plugin: Failed to open `%s': %s.", file,
1097               sstrerror(errno, errbuf, sizeof(errbuf)));
1098     return NULL;
1099   }
1100
1101   buf_ptr = buf;
1102   len = buf_len;
1103
1104   n = 0;
1105
1106   while (42) {
1107     ssize_t status;
1108
1109     status = read(fd, (void *)buf_ptr, len);
1110
1111     if (status < 0) {
1112       char errbuf[1024];
1113
1114       if ((EAGAIN == errno) || (EINTR == errno))
1115         continue;
1116
1117       WARNING("processes plugin: Failed to read from `%s': %s.", file,
1118               sstrerror(errno, errbuf, sizeof(errbuf)));
1119       close(fd);
1120       return NULL;
1121     }
1122
1123     n += status;
1124
1125     if (status == 0)
1126       break;
1127
1128     buf_ptr += status;
1129     len -= status;
1130
1131     if (len == 0)
1132       break;
1133   }
1134
1135   close(fd);
1136
1137   if (0 == n) {
1138     /* cmdline not available; e.g. kernel thread, zombie */
1139     if (NULL == name)
1140       return NULL;
1141
1142     ssnprintf(buf, buf_len, "[%s]", name);
1143     return buf;
1144   }
1145
1146   assert(n <= buf_len);
1147
1148   if (n == buf_len)
1149     --n;
1150   buf[n] = '\0';
1151
1152   --n;
1153   /* remove trailing whitespace */
1154   while ((n > 0) && (isspace(buf[n]) || ('\0' == buf[n]))) {
1155     buf[n] = '\0';
1156     --n;
1157   }
1158
1159   /* arguments are separated by '\0' in /proc/<pid>/cmdline */
1160   while (n > 0) {
1161     if ('\0' == buf[n])
1162       buf[n] = ' ';
1163     --n;
1164   }
1165   return buf;
1166 } /* char *ps_get_cmdline (...) */
1167
1168 static int read_fork_rate(void) {
1169   FILE *proc_stat;
1170   char buffer[1024];
1171   value_t value;
1172   _Bool value_valid = 0;
1173
1174   proc_stat = fopen("/proc/stat", "r");
1175   if (proc_stat == NULL) {
1176     char errbuf[1024];
1177     ERROR("processes plugin: fopen (/proc/stat) failed: %s",
1178           sstrerror(errno, errbuf, sizeof(errbuf)));
1179     return (-1);
1180   }
1181
1182   while (fgets(buffer, sizeof(buffer), proc_stat) != NULL) {
1183     int status;
1184     char *fields[3];
1185     int fields_num;
1186
1187     fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
1188     if (fields_num != 2)
1189       continue;
1190
1191     if (strcmp("processes", fields[0]) != 0)
1192       continue;
1193
1194     status = parse_value(fields[1], &value, DS_TYPE_DERIVE);
1195     if (status == 0)
1196       value_valid = 1;
1197
1198     break;
1199   }
1200   fclose(proc_stat);
1201
1202   if (!value_valid)
1203     return (-1);
1204
1205   ps_submit_fork_rate(value.derive);
1206   return (0);
1207 }
1208 #endif /*KERNEL_LINUX */
1209
1210 #if KERNEL_SOLARIS
1211 static char *ps_get_cmdline(long pid,
1212                             char *name __attribute__((unused)), /* {{{ */
1213                             char *buffer, size_t buffer_size) {
1214   char path[PATH_MAX];
1215   psinfo_t info;
1216   ssize_t status;
1217
1218   snprintf(path, sizeof(path), "/proc/%li/psinfo", pid);
1219
1220   status = read_file_contents(path, (void *)&info, sizeof(info));
1221   if ((status < 0) || (((size_t)status) != sizeof(info))) {
1222     ERROR("processes plugin: Unexpected return value "
1223           "while reading \"%s\": "
1224           "Returned %zd but expected %zu.",
1225           path, status, buffer_size);
1226     return (NULL);
1227   }
1228
1229   info.pr_psargs[sizeof(info.pr_psargs) - 1] = 0;
1230   sstrncpy(buffer, info.pr_psargs, buffer_size);
1231
1232   return (buffer);
1233 } /* }}} int ps_get_cmdline */
1234
1235 /*
1236  * Reads process information on the Solaris OS. The information comes mainly
1237  * from
1238  * /proc/PID/status, /proc/PID/psinfo and /proc/PID/usage
1239  * The values for input and ouput chars are calculated "by hand"
1240  * Added a few "solaris" specific process states as well
1241  */
1242 static int ps_read_process(long pid, procstat_t *ps, char *state) {
1243   char filename[64];
1244   char f_psinfo[64], f_usage[64];
1245   char *buffer;
1246
1247   pstatus_t *myStatus;
1248   psinfo_t *myInfo;
1249   prusage_t *myUsage;
1250
1251   snprintf(filename, sizeof(filename), "/proc/%li/status", pid);
1252   snprintf(f_psinfo, sizeof(f_psinfo), "/proc/%li/psinfo", pid);
1253   snprintf(f_usage, sizeof(f_usage), "/proc/%li/usage", pid);
1254
1255   buffer = calloc(1, sizeof(pstatus_t));
1256   read_file_contents(filename, buffer, sizeof(pstatus_t));
1257   myStatus = (pstatus_t *)buffer;
1258
1259   buffer = calloc(1, sizeof(psinfo_t));
1260   read_file_contents(f_psinfo, buffer, sizeof(psinfo_t));
1261   myInfo = (psinfo_t *)buffer;
1262
1263   buffer = calloc(1, sizeof(prusage_t));
1264   read_file_contents(f_usage, buffer, sizeof(prusage_t));
1265   myUsage = (prusage_t *)buffer;
1266
1267   sstrncpy(ps->name, myInfo->pr_fname, sizeof(myInfo->pr_fname));
1268   ps->num_lwp = myStatus->pr_nlwp;
1269   if (myInfo->pr_wstat != 0) {
1270     ps->num_proc = 0;
1271     ps->num_lwp = 0;
1272     *state = (char)'Z';
1273
1274     sfree(myStatus);
1275     sfree(myInfo);
1276     sfree(myUsage);
1277     return (0);
1278   } else {
1279     ps->num_proc = 1;
1280     ps->num_lwp = myInfo->pr_nlwp;
1281   }
1282
1283   /*
1284    * Convert system time and user time from nanoseconds to microseconds
1285    * for compatibility with the linux module
1286    */
1287   ps->cpu_system_counter = myStatus->pr_stime.tv_nsec / 1000;
1288   ps->cpu_user_counter = myStatus->pr_utime.tv_nsec / 1000;
1289
1290   /*
1291    * Convert rssize from KB to bytes to be consistent w/ the linux module
1292    */
1293   ps->vmem_rss = myInfo->pr_rssize * 1024;
1294   ps->vmem_size = myInfo->pr_size * 1024;
1295   ps->vmem_minflt_counter = myUsage->pr_minf;
1296   ps->vmem_majflt_counter = myUsage->pr_majf;
1297
1298   /*
1299    * TODO: Data and code segment calculations for Solaris
1300    */
1301
1302   ps->vmem_data = -1;
1303   ps->vmem_code = -1;
1304   ps->stack_size = myStatus->pr_stksize;
1305
1306   /*
1307    * Calculating input/ouput chars
1308    * Formula used is total chars / total blocks => chars/block
1309    * then convert input/output blocks to chars
1310    */
1311   ulong_t tot_chars = myUsage->pr_ioch;
1312   ulong_t tot_blocks = myUsage->pr_inblk + myUsage->pr_oublk;
1313   ulong_t chars_per_block = 1;
1314   if (tot_blocks != 0)
1315     chars_per_block = tot_chars / tot_blocks;
1316   ps->io_rchar = myUsage->pr_inblk * chars_per_block;
1317   ps->io_wchar = myUsage->pr_oublk * chars_per_block;
1318   ps->io_syscr = myUsage->pr_sysc;
1319   ps->io_syscw = myUsage->pr_sysc;
1320
1321   /*
1322    * TODO: context switch counters for Solaris
1323 */
1324   ps->cswitch_vol = -1;
1325   ps->cswitch_invol = -1;
1326
1327   /*
1328    * TODO: Find way of setting BLOCKED and PAGING status
1329    */
1330
1331   *state = (char)'R';
1332   if (myStatus->pr_flags & PR_ASLEEP)
1333     *state = (char)'S';
1334   else if (myStatus->pr_flags & PR_STOPPED)
1335     *state = (char)'T';
1336   else if (myStatus->pr_flags & PR_DETACH)
1337     *state = (char)'E';
1338   else if (myStatus->pr_flags & PR_DAEMON)
1339     *state = (char)'A';
1340   else if (myStatus->pr_flags & PR_ISSYS)
1341     *state = (char)'Y';
1342   else if (myStatus->pr_flags & PR_ORPHAN)
1343     *state = (char)'O';
1344
1345   sfree(myStatus);
1346   sfree(myInfo);
1347   sfree(myUsage);
1348
1349   return (0);
1350 }
1351
1352 /*
1353  * Reads the number of threads created since the last reboot. On Solaris these
1354  * are retrieved from kstat (module cpu, name sys, class misc, stat nthreads).
1355  * The result is the sum for all the threads created on each cpu
1356  */
1357 static int read_fork_rate(void) {
1358   extern kstat_ctl_t *kc;
1359   derive_t result = 0;
1360
1361   if (kc == NULL)
1362     return (-1);
1363
1364   for (kstat_t *ksp_chain = kc->kc_chain; ksp_chain != NULL;
1365        ksp_chain = ksp_chain->ks_next) {
1366     if ((strcmp(ksp_chain->ks_module, "cpu") == 0) &&
1367         (strcmp(ksp_chain->ks_name, "sys") == 0) &&
1368         (strcmp(ksp_chain->ks_class, "misc") == 0)) {
1369       long long tmp;
1370
1371       kstat_read(kc, ksp_chain, NULL);
1372
1373       tmp = get_kstat_value(ksp_chain, "nthreads");
1374       if (tmp != -1LL)
1375         result += tmp;
1376     }
1377   }
1378
1379   ps_submit_fork_rate(result);
1380   return (0);
1381 }
1382 #endif /* KERNEL_SOLARIS */
1383
1384 #if HAVE_THREAD_INFO
1385 static int mach_get_task_name(task_t t, int *pid, char *name,
1386                               size_t name_max_len) {
1387   int mib[4];
1388
1389   struct kinfo_proc kp;
1390   size_t kp_size;
1391
1392   mib[0] = CTL_KERN;
1393   mib[1] = KERN_PROC;
1394   mib[2] = KERN_PROC_PID;
1395
1396   if (pid_for_task(t, pid) != KERN_SUCCESS)
1397     return (-1);
1398   mib[3] = *pid;
1399
1400   kp_size = sizeof(kp);
1401   if (sysctl(mib, 4, &kp, &kp_size, NULL, 0) != 0)
1402     return (-1);
1403
1404   if (name_max_len > (MAXCOMLEN + 1))
1405     name_max_len = MAXCOMLEN + 1;
1406
1407   strncpy(name, kp.kp_proc.p_comm, name_max_len - 1);
1408   name[name_max_len - 1] = '\0';
1409
1410   DEBUG("pid = %i; name = %s;", *pid, name);
1411
1412   /* We don't do the special handling for `p_comm == "LaunchCFMApp"' as
1413    * `top' does it, because it is a lot of work and only used when
1414    * debugging. -octo */
1415
1416   return (0);
1417 }
1418 #endif /* HAVE_THREAD_INFO */
1419 /* ------- end of additional functions for KERNEL_LINUX/HAVE_THREAD_INFO -------
1420  */
1421
1422 /* do actual readings from kernel */
1423 static int ps_read(void) {
1424 #if HAVE_THREAD_INFO
1425   kern_return_t status;
1426
1427   processor_set_t port_pset_priv;
1428
1429   task_array_t task_list;
1430   mach_msg_type_number_t task_list_len;
1431
1432   int task_pid;
1433   char task_name[MAXCOMLEN + 1];
1434
1435   thread_act_array_t thread_list;
1436   mach_msg_type_number_t thread_list_len;
1437   thread_basic_info_data_t thread_data;
1438   mach_msg_type_number_t thread_data_len;
1439
1440   int running = 0;
1441   int sleeping = 0;
1442   int zombies = 0;
1443   int stopped = 0;
1444   int blocked = 0;
1445
1446   procstat_t *ps;
1447   procstat_entry_t pse;
1448
1449   ps_list_reset();
1450
1451   /*
1452    * The Mach-concept is a little different from the traditional UNIX
1453    * concept: All the work is done in threads. Threads are contained in
1454    * `tasks'. Therefore, `task status' doesn't make much sense, since
1455    * it's actually a `thread status'.
1456    * Tasks are assigned to sets of processors, so that's where you go to
1457    * get a list.
1458    */
1459   for (mach_msg_type_number_t pset = 0; pset < pset_list_len; pset++) {
1460     if ((status = host_processor_set_priv(port_host_self, pset_list[pset],
1461                                           &port_pset_priv)) != KERN_SUCCESS) {
1462       ERROR("host_processor_set_priv failed: %s\n", mach_error_string(status));
1463       continue;
1464     }
1465
1466     if ((status = processor_set_tasks(port_pset_priv, &task_list,
1467                                       &task_list_len)) != KERN_SUCCESS) {
1468       ERROR("processor_set_tasks failed: %s\n", mach_error_string(status));
1469       mach_port_deallocate(port_task_self, port_pset_priv);
1470       continue;
1471     }
1472
1473     for (mach_msg_type_number_t task = 0; task < task_list_len; task++) {
1474       ps = NULL;
1475       if (mach_get_task_name(task_list[task], &task_pid, task_name,
1476                              PROCSTAT_NAME_LEN) == 0) {
1477         /* search for at least one match */
1478         for (ps = list_head_g; ps != NULL; ps = ps->next)
1479           /* FIXME: cmdline should be here instead of NULL */
1480           if (ps_list_match(task_name, NULL, ps) == 1)
1481             break;
1482       }
1483
1484       /* Collect more detailed statistics for this process */
1485       if (ps != NULL) {
1486         task_basic_info_data_t task_basic_info;
1487         mach_msg_type_number_t task_basic_info_len;
1488         task_events_info_data_t task_events_info;
1489         mach_msg_type_number_t task_events_info_len;
1490         task_absolutetime_info_data_t task_absolutetime_info;
1491         mach_msg_type_number_t task_absolutetime_info_len;
1492
1493         memset(&pse, '\0', sizeof(pse));
1494         pse.id = task_pid;
1495
1496         task_basic_info_len = TASK_BASIC_INFO_COUNT;
1497         status = task_info(task_list[task], TASK_BASIC_INFO,
1498                            (task_info_t)&task_basic_info, &task_basic_info_len);
1499         if (status != KERN_SUCCESS) {
1500           ERROR("task_info failed: %s", mach_error_string(status));
1501           continue; /* with next thread_list */
1502         }
1503
1504         task_events_info_len = TASK_EVENTS_INFO_COUNT;
1505         status =
1506             task_info(task_list[task], TASK_EVENTS_INFO,
1507                       (task_info_t)&task_events_info, &task_events_info_len);
1508         if (status != KERN_SUCCESS) {
1509           ERROR("task_info failed: %s", mach_error_string(status));
1510           continue; /* with next thread_list */
1511         }
1512
1513         task_absolutetime_info_len = TASK_ABSOLUTETIME_INFO_COUNT;
1514         status = task_info(task_list[task], TASK_ABSOLUTETIME_INFO,
1515                            (task_info_t)&task_absolutetime_info,
1516                            &task_absolutetime_info_len);
1517         if (status != KERN_SUCCESS) {
1518           ERROR("task_info failed: %s", mach_error_string(status));
1519           continue; /* with next thread_list */
1520         }
1521
1522         pse.num_proc++;
1523         pse.vmem_size = task_basic_info.virtual_size;
1524         pse.vmem_rss = task_basic_info.resident_size;
1525         /* Does not seem to be easily exposed */
1526         pse.vmem_data = 0;
1527         pse.vmem_code = 0;
1528
1529         pse.vmem_minflt_counter = task_events_info.cow_faults;
1530         pse.vmem_majflt_counter = task_events_info.faults;
1531
1532         pse.cpu_user_counter = task_absolutetime_info.total_user;
1533         pse.cpu_system_counter = task_absolutetime_info.total_system;
1534
1535         /* context switch counters not implemented */
1536         pse.cswitch_vol = -1;
1537         pse.cswitch_invol = -1;
1538       }
1539
1540       status = task_threads(task_list[task], &thread_list, &thread_list_len);
1541       if (status != KERN_SUCCESS) {
1542         /* Apple's `top' treats this case a zombie. It
1543          * makes sense to some extend: A `zombie'
1544          * thread is nonsense, since the task/process
1545          * is dead. */
1546         zombies++;
1547         DEBUG("task_threads failed: %s", mach_error_string(status));
1548         if (task_list[task] != port_task_self)
1549           mach_port_deallocate(port_task_self, task_list[task]);
1550         continue; /* with next task_list */
1551       }
1552
1553       for (mach_msg_type_number_t thread = 0; thread < thread_list_len;
1554            thread++) {
1555         thread_data_len = THREAD_BASIC_INFO_COUNT;
1556         status = thread_info(thread_list[thread], THREAD_BASIC_INFO,
1557                              (thread_info_t)&thread_data, &thread_data_len);
1558         if (status != KERN_SUCCESS) {
1559           ERROR("thread_info failed: %s", mach_error_string(status));
1560           if (task_list[task] != port_task_self)
1561             mach_port_deallocate(port_task_self, thread_list[thread]);
1562           continue; /* with next thread_list */
1563         }
1564
1565         if (ps != NULL)
1566           pse.num_lwp++;
1567
1568         switch (thread_data.run_state) {
1569         case TH_STATE_RUNNING:
1570           running++;
1571           break;
1572         case TH_STATE_STOPPED:
1573         /* What exactly is `halted'? */
1574         case TH_STATE_HALTED:
1575           stopped++;
1576           break;
1577         case TH_STATE_WAITING:
1578           sleeping++;
1579           break;
1580         case TH_STATE_UNINTERRUPTIBLE:
1581           blocked++;
1582           break;
1583         /* There is no `zombie' case here,
1584          * since there are no zombie-threads.
1585          * There's only zombie tasks, which are
1586          * handled above. */
1587         default:
1588           WARNING("Unknown thread status: %i", thread_data.run_state);
1589           break;
1590         } /* switch (thread_data.run_state) */
1591
1592         if (task_list[task] != port_task_self) {
1593           status = mach_port_deallocate(port_task_self, thread_list[thread]);
1594           if (status != KERN_SUCCESS)
1595             ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
1596         }
1597       } /* for (thread_list) */
1598
1599       if ((status = vm_deallocate(port_task_self, (vm_address_t)thread_list,
1600                                   thread_list_len * sizeof(thread_act_t))) !=
1601           KERN_SUCCESS) {
1602         ERROR("vm_deallocate failed: %s", mach_error_string(status));
1603       }
1604       thread_list = NULL;
1605       thread_list_len = 0;
1606
1607       /* Only deallocate the task port, if it isn't our own.
1608        * Don't know what would happen in that case, but this
1609        * is what Apple's top does.. ;) */
1610       if (task_list[task] != port_task_self) {
1611         status = mach_port_deallocate(port_task_self, task_list[task]);
1612         if (status != KERN_SUCCESS)
1613           ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
1614       }
1615
1616       if (ps != NULL)
1617         /* FIXME: cmdline should be here instead of NULL */
1618         ps_list_add(task_name, NULL, &pse);
1619     } /* for (task_list) */
1620
1621     if ((status = vm_deallocate(port_task_self, (vm_address_t)task_list,
1622                                 task_list_len * sizeof(task_t))) !=
1623         KERN_SUCCESS) {
1624       ERROR("vm_deallocate failed: %s", mach_error_string(status));
1625     }
1626     task_list = NULL;
1627     task_list_len = 0;
1628
1629     if ((status = mach_port_deallocate(port_task_self, port_pset_priv)) !=
1630         KERN_SUCCESS) {
1631       ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
1632     }
1633   } /* for (pset_list) */
1634
1635   ps_submit_state("running", running);
1636   ps_submit_state("sleeping", sleeping);
1637   ps_submit_state("zombies", zombies);
1638   ps_submit_state("stopped", stopped);
1639   ps_submit_state("blocked", blocked);
1640
1641   for (ps = list_head_g; ps != NULL; ps = ps->next)
1642     ps_submit_proc_list(ps);
1643 /* #endif HAVE_THREAD_INFO */
1644
1645 #elif KERNEL_LINUX
1646   int running = 0;
1647   int sleeping = 0;
1648   int zombies = 0;
1649   int stopped = 0;
1650   int paging = 0;
1651   int blocked = 0;
1652
1653   struct dirent *ent;
1654   DIR *proc;
1655   long pid;
1656
1657   char cmdline[CMDLINE_BUFFER_SIZE];
1658
1659   int status;
1660   procstat_t ps;
1661   procstat_entry_t pse;
1662   char state;
1663
1664   running = sleeping = zombies = stopped = paging = blocked = 0;
1665   ps_list_reset();
1666
1667   if ((proc = opendir("/proc")) == NULL) {
1668     char errbuf[1024];
1669     ERROR("Cannot open `/proc': %s", sstrerror(errno, errbuf, sizeof(errbuf)));
1670     return (-1);
1671   }
1672
1673   while ((ent = readdir(proc)) != NULL) {
1674     if (!isdigit(ent->d_name[0]))
1675       continue;
1676
1677     if ((pid = atol(ent->d_name)) < 1)
1678       continue;
1679
1680     status = ps_read_process(pid, &ps, &state);
1681     if (status != 0) {
1682       DEBUG("ps_read_process failed: %i", status);
1683       continue;
1684     }
1685
1686     memset(&pse, 0, sizeof(pse));
1687     pse.id = pid;
1688     pse.age = 0;
1689
1690     pse.num_proc = ps.num_proc;
1691     pse.num_lwp = ps.num_lwp;
1692     pse.vmem_size = ps.vmem_size;
1693     pse.vmem_rss = ps.vmem_rss;
1694     pse.vmem_data = ps.vmem_data;
1695     pse.vmem_code = ps.vmem_code;
1696     pse.stack_size = ps.stack_size;
1697
1698     pse.vmem_minflt_counter = ps.vmem_minflt_counter;
1699     pse.vmem_majflt_counter = ps.vmem_majflt_counter;
1700
1701     pse.cpu_user_counter = ps.cpu_user_counter;
1702     pse.cpu_system_counter = ps.cpu_system_counter;
1703
1704     pse.io_rchar = ps.io_rchar;
1705     pse.io_wchar = ps.io_wchar;
1706     pse.io_syscr = ps.io_syscr;
1707     pse.io_syscw = ps.io_syscw;
1708
1709     pse.cswitch_vol = ps.cswitch_vol;
1710     pse.cswitch_invol = ps.cswitch_invol;
1711
1712     switch (state) {
1713     case 'R':
1714       running++;
1715       break;
1716     case 'S':
1717       sleeping++;
1718       break;
1719     case 'D':
1720       blocked++;
1721       break;
1722     case 'Z':
1723       zombies++;
1724       break;
1725     case 'T':
1726       stopped++;
1727       break;
1728     case 'W':
1729       paging++;
1730       break;
1731     }
1732
1733     ps_list_add(ps.name, ps_get_cmdline(pid, ps.name, cmdline, sizeof(cmdline)),
1734                 &pse);
1735   }
1736
1737   closedir(proc);
1738
1739   ps_submit_state("running", running);
1740   ps_submit_state("sleeping", sleeping);
1741   ps_submit_state("zombies", zombies);
1742   ps_submit_state("stopped", stopped);
1743   ps_submit_state("paging", paging);
1744   ps_submit_state("blocked", blocked);
1745
1746   for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
1747     ps_submit_proc_list(ps_ptr);
1748
1749   read_fork_rate();
1750 /* #endif KERNEL_LINUX */
1751
1752 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
1753   int running = 0;
1754   int sleeping = 0;
1755   int zombies = 0;
1756   int stopped = 0;
1757   int blocked = 0;
1758   int idle = 0;
1759   int wait = 0;
1760
1761   kvm_t *kd;
1762   char errbuf[_POSIX2_LINE_MAX];
1763   struct kinfo_proc *procs; /* array of processes */
1764   struct kinfo_proc *proc_ptr = NULL;
1765   int count; /* returns number of processes */
1766
1767   procstat_entry_t pse;
1768
1769   ps_list_reset();
1770
1771   /* Open the kvm interface, get a descriptor */
1772   kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf);
1773   if (kd == NULL) {
1774     ERROR("processes plugin: Cannot open kvm interface: %s", errbuf);
1775     return (0);
1776   }
1777
1778   /* Get the list of processes. */
1779   procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, &count);
1780   if (procs == NULL) {
1781     ERROR("processes plugin: Cannot get kvm processes list: %s",
1782           kvm_geterr(kd));
1783     kvm_close(kd);
1784     return (0);
1785   }
1786
1787   /* Iterate through the processes in kinfo_proc */
1788   for (int i = 0; i < count; i++) {
1789     /* Create only one process list entry per _process_, i.e.
1790      * filter out threads (duplicate PID entries). */
1791     if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) {
1792       char cmdline[CMDLINE_BUFFER_SIZE] = "";
1793       _Bool have_cmdline = 0;
1794
1795       proc_ptr = &(procs[i]);
1796       /* Don't probe system processes and processes without arguments */
1797       if (((procs[i].ki_flag & P_SYSTEM) == 0) && (procs[i].ki_args != NULL)) {
1798         char **argv;
1799         int argc;
1800         int status;
1801
1802         /* retrieve the arguments */
1803         argv = kvm_getargv(kd, proc_ptr, /* nchr = */ 0);
1804         argc = 0;
1805         if ((argv != NULL) && (argv[0] != NULL)) {
1806           while (argv[argc] != NULL)
1807             argc++;
1808
1809           status = strjoin(cmdline, sizeof(cmdline), argv, argc, " ");
1810           if (status < 0)
1811             WARNING("processes plugin: Command line did not fit into buffer.");
1812           else
1813             have_cmdline = 1;
1814         }
1815       } /* if (process has argument list) */
1816
1817       pse.id = procs[i].ki_pid;
1818       pse.age = 0;
1819
1820       pse.num_proc = 1;
1821       pse.num_lwp = procs[i].ki_numthreads;
1822
1823       pse.vmem_size = procs[i].ki_size;
1824       pse.vmem_rss = procs[i].ki_rssize * pagesize;
1825       pse.vmem_data = procs[i].ki_dsize * pagesize;
1826       pse.vmem_code = procs[i].ki_tsize * pagesize;
1827       pse.stack_size = procs[i].ki_ssize * pagesize;
1828       pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
1829       pse.vmem_majflt_counter = procs[i].ki_rusage.ru_majflt;
1830
1831       pse.cpu_user_counter = 0;
1832       pse.cpu_system_counter = 0;
1833       /*
1834        * The u-area might be swapped out, and we can't get
1835        * at it because we have a crashdump and no swap.
1836        * If it's here fill in these fields, otherwise, just
1837        * leave them 0.
1838        */
1839       if (procs[i].ki_flag & P_INMEM) {
1840         pse.cpu_user_counter = procs[i].ki_rusage.ru_utime.tv_usec +
1841                                (1000000lu * procs[i].ki_rusage.ru_utime.tv_sec);
1842         pse.cpu_system_counter =
1843             procs[i].ki_rusage.ru_stime.tv_usec +
1844             (1000000lu * procs[i].ki_rusage.ru_stime.tv_sec);
1845       }
1846
1847       /* no I/O data */
1848       pse.io_rchar = -1;
1849       pse.io_wchar = -1;
1850       pse.io_syscr = -1;
1851       pse.io_syscw = -1;
1852
1853       /* context switch counters not implemented */
1854       pse.cswitch_vol = -1;
1855       pse.cswitch_invol = -1;
1856
1857       ps_list_add(procs[i].ki_comm, have_cmdline ? cmdline : NULL, &pse);
1858
1859       switch (procs[i].ki_stat) {
1860       case SSTOP:
1861         stopped++;
1862         break;
1863       case SSLEEP:
1864         sleeping++;
1865         break;
1866       case SRUN:
1867         running++;
1868         break;
1869       case SIDL:
1870         idle++;
1871         break;
1872       case SWAIT:
1873         wait++;
1874         break;
1875       case SLOCK:
1876         blocked++;
1877         break;
1878       case SZOMB:
1879         zombies++;
1880         break;
1881       }
1882     } /* if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) */
1883   }
1884
1885   kvm_close(kd);
1886
1887   ps_submit_state("running", running);
1888   ps_submit_state("sleeping", sleeping);
1889   ps_submit_state("zombies", zombies);
1890   ps_submit_state("stopped", stopped);
1891   ps_submit_state("blocked", blocked);
1892   ps_submit_state("idle", idle);
1893   ps_submit_state("wait", wait);
1894
1895   for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
1896     ps_submit_proc_list(ps_ptr);
1897 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
1898
1899 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_OPENBSD
1900   int running = 0;
1901   int sleeping = 0;
1902   int zombies = 0;
1903   int stopped = 0;
1904   int onproc = 0;
1905   int idle = 0;
1906   int dead = 0;
1907
1908   kvm_t *kd;
1909   char errbuf[1024];
1910   struct kinfo_proc *procs; /* array of processes */
1911   struct kinfo_proc *proc_ptr = NULL;
1912   int count; /* returns number of processes */
1913
1914   procstat_entry_t pse;
1915
1916   ps_list_reset();
1917
1918   /* Open the kvm interface, get a descriptor */
1919   kd = kvm_open(NULL, NULL, NULL, 0, errbuf);
1920   if (kd == NULL) {
1921     ERROR("processes plugin: Cannot open kvm interface: %s", errbuf);
1922     return (0);
1923   }
1924
1925   /* Get the list of processes. */
1926   procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &count);
1927   if (procs == NULL) {
1928     ERROR("processes plugin: Cannot get kvm processes list: %s",
1929           kvm_geterr(kd));
1930     kvm_close(kd);
1931     return (0);
1932   }
1933
1934   /* Iterate through the processes in kinfo_proc */
1935   for (int i = 0; i < count; i++) {
1936     /* Create only one process list entry per _process_, i.e.
1937      * filter out threads (duplicate PID entries). */
1938     if ((proc_ptr == NULL) || (proc_ptr->p_pid != procs[i].p_pid)) {
1939       char cmdline[CMDLINE_BUFFER_SIZE] = "";
1940       _Bool have_cmdline = 0;
1941
1942       proc_ptr = &(procs[i]);
1943       /* Don't probe zombie processes  */
1944       if (!P_ZOMBIE(proc_ptr)) {
1945         char **argv;
1946         int argc;
1947         int status;
1948
1949         /* retrieve the arguments */
1950         argv = kvm_getargv(kd, proc_ptr, /* nchr = */ 0);
1951         argc = 0;
1952         if ((argv != NULL) && (argv[0] != NULL)) {
1953           while (argv[argc] != NULL)
1954             argc++;
1955
1956           status = strjoin(cmdline, sizeof(cmdline), argv, argc, " ");
1957           if (status < 0)
1958             WARNING("processes plugin: Command line did not fit into buffer.");
1959           else
1960             have_cmdline = 1;
1961         }
1962       } /* if (process has argument list) */
1963
1964       memset(&pse, 0, sizeof(pse));
1965       pse.id = procs[i].p_pid;
1966       pse.age = 0;
1967
1968       pse.num_proc = 1;
1969       pse.num_lwp = 1; /* XXX: accumulate p_tid values for a single p_pid ? */
1970
1971       pse.vmem_rss = procs[i].p_vm_rssize * pagesize;
1972       pse.vmem_data = procs[i].p_vm_dsize * pagesize;
1973       pse.vmem_code = procs[i].p_vm_tsize * pagesize;
1974       pse.stack_size = procs[i].p_vm_ssize * pagesize;
1975       pse.vmem_size = pse.stack_size + pse.vmem_code + pse.vmem_data;
1976       pse.vmem_minflt_counter = procs[i].p_uru_minflt;
1977       pse.vmem_majflt_counter = procs[i].p_uru_majflt;
1978
1979       pse.cpu_user_counter =
1980           procs[i].p_uutime_usec + (1000000lu * procs[i].p_uutime_sec);
1981       pse.cpu_system_counter =
1982           procs[i].p_ustime_usec + (1000000lu * procs[i].p_ustime_sec);
1983
1984       /* no I/O data */
1985       pse.io_rchar = -1;
1986       pse.io_wchar = -1;
1987       pse.io_syscr = -1;
1988       pse.io_syscw = -1;
1989
1990       /* context switch counters not implemented */
1991       pse.cswitch_vol = -1;
1992       pse.cswitch_invol = -1;
1993
1994       ps_list_add(procs[i].p_comm, have_cmdline ? cmdline : NULL, &pse);
1995
1996       switch (procs[i].p_stat) {
1997       case SSTOP:
1998         stopped++;
1999         break;
2000       case SSLEEP:
2001         sleeping++;
2002         break;
2003       case SRUN:
2004         running++;
2005         break;
2006       case SIDL:
2007         idle++;
2008         break;
2009       case SONPROC:
2010         onproc++;
2011         break;
2012       case SDEAD:
2013         dead++;
2014         break;
2015       case SZOMB:
2016         zombies++;
2017         break;
2018       }
2019     } /* if ((proc_ptr == NULL) || (proc_ptr->p_pid != procs[i].p_pid)) */
2020   }
2021
2022   kvm_close(kd);
2023
2024   ps_submit_state("running", running);
2025   ps_submit_state("sleeping", sleeping);
2026   ps_submit_state("zombies", zombies);
2027   ps_submit_state("stopped", stopped);
2028   ps_submit_state("onproc", onproc);
2029   ps_submit_state("idle", idle);
2030   ps_submit_state("dead", dead);
2031
2032   for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
2033     ps_submit_proc_list(ps_ptr);
2034 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_OPENBSD */
2035
2036 #elif HAVE_PROCINFO_H
2037   /* AIX */
2038   int running = 0;
2039   int sleeping = 0;
2040   int zombies = 0;
2041   int stopped = 0;
2042   int paging = 0;
2043   int blocked = 0;
2044
2045   pid_t pindex = 0;
2046   int nprocs;
2047
2048   procstat_entry_t pse;
2049
2050   ps_list_reset();
2051   while ((nprocs = getprocs64(procentry, sizeof(struct procentry64),
2052                               /* fdsinfo = */ NULL, sizeof(struct fdsinfo64),
2053                               &pindex, MAXPROCENTRY)) > 0) {
2054     for (int i = 0; i < nprocs; i++) {
2055       tid64_t thindex;
2056       int nthreads;
2057       char arglist[MAXARGLN + 1];
2058       char *cargs;
2059       char *cmdline;
2060
2061       if (procentry[i].pi_state == SNONE)
2062         continue;
2063       /* if (procentry[i].pi_state == SZOMB)  FIXME */
2064
2065       cmdline = procentry[i].pi_comm;
2066       cargs = procentry[i].pi_comm;
2067       if (procentry[i].pi_flags & SKPROC) {
2068         if (procentry[i].pi_pid == 0)
2069           cmdline = "swapper";
2070         cargs = cmdline;
2071       } else {
2072         if (getargs(&procentry[i], sizeof(struct procentry64), arglist,
2073                     MAXARGLN) >= 0) {
2074           int n;
2075
2076           n = -1;
2077           while (++n < MAXARGLN) {
2078             if (arglist[n] == '\0') {
2079               if (arglist[n + 1] == '\0')
2080                 break;
2081               arglist[n] = ' ';
2082             }
2083           }
2084           cargs = arglist;
2085         }
2086       }
2087
2088       pse.id = procentry[i].pi_pid;
2089       pse.age = 0;
2090       pse.num_lwp = procentry[i].pi_thcount;
2091       pse.num_proc = 1;
2092
2093       thindex = 0;
2094       while ((nthreads = getthrds64(procentry[i].pi_pid, thrdentry,
2095                                     sizeof(struct thrdentry64), &thindex,
2096                                     MAXTHRDENTRY)) > 0) {
2097         int j;
2098
2099         for (j = 0; j < nthreads; j++) {
2100           switch (thrdentry[j].ti_state) {
2101           /* case TSNONE: break; */
2102           case TSIDL:
2103             blocked++;
2104             break; /* FIXME is really blocked */
2105           case TSRUN:
2106             running++;
2107             break;
2108           case TSSLEEP:
2109             sleeping++;
2110             break;
2111           case TSSWAP:
2112             paging++;
2113             break;
2114           case TSSTOP:
2115             stopped++;
2116             break;
2117           case TSZOMB:
2118             zombies++;
2119             break;
2120           }
2121         }
2122         if (nthreads < MAXTHRDENTRY)
2123           break;
2124       }
2125
2126       /* tv_usec is nanosec ??? */
2127       pse.cpu_user_counter = procentry[i].pi_ru.ru_utime.tv_sec * 1000000 +
2128                              procentry[i].pi_ru.ru_utime.tv_usec / 1000;
2129
2130       /* tv_usec is nanosec ??? */
2131       pse.cpu_system_counter = procentry[i].pi_ru.ru_stime.tv_sec * 1000000 +
2132                                procentry[i].pi_ru.ru_stime.tv_usec / 1000;
2133
2134       pse.vmem_minflt_counter = procentry[i].pi_minflt;
2135       pse.vmem_majflt_counter = procentry[i].pi_majflt;
2136
2137       pse.vmem_size = procentry[i].pi_tsize + procentry[i].pi_dvm * pagesize;
2138       pse.vmem_rss = (procentry[i].pi_drss + procentry[i].pi_trss) * pagesize;
2139       /* Not supported */
2140       pse.vmem_data = 0;
2141       pse.vmem_code = 0;
2142       pse.stack_size = 0;
2143
2144       pse.io_rchar = -1;
2145       pse.io_wchar = -1;
2146       pse.io_syscr = -1;
2147       pse.io_syscw = -1;
2148
2149       pse.cswitch_vol = -1;
2150       pse.cswitch_invol = -1;
2151
2152       ps_list_add(cmdline, cargs, &pse);
2153     } /* for (i = 0 .. nprocs) */
2154
2155     if (nprocs < MAXPROCENTRY)
2156       break;
2157   } /* while (getprocs64() > 0) */
2158   ps_submit_state("running", running);
2159   ps_submit_state("sleeping", sleeping);
2160   ps_submit_state("zombies", zombies);
2161   ps_submit_state("stopped", stopped);
2162   ps_submit_state("paging", paging);
2163   ps_submit_state("blocked", blocked);
2164
2165   for (procstat_t *ps = list_head_g; ps != NULL; ps = ps->next)
2166     ps_submit_proc_list(ps);
2167 /* #endif HAVE_PROCINFO_H */
2168
2169 #elif KERNEL_SOLARIS
2170   /*
2171    * The Solaris section adds a few more process states and removes some
2172    * process states compared to linux. Most notably there is no "PAGING"
2173    * and "BLOCKED" state for a process.  The rest is similar to the linux
2174    * code.
2175    */
2176   int running = 0;
2177   int sleeping = 0;
2178   int zombies = 0;
2179   int stopped = 0;
2180   int detached = 0;
2181   int daemon = 0;
2182   int system = 0;
2183   int orphan = 0;
2184
2185   struct dirent *ent;
2186   DIR *proc;
2187
2188   int status;
2189   char state;
2190
2191   char cmdline[PRARGSZ];
2192
2193   ps_list_reset();
2194
2195   proc = opendir("/proc");
2196   if (proc == NULL)
2197     return (-1);
2198
2199   while ((ent = readdir(proc)) != NULL) {
2200     long pid;
2201     struct procstat ps;
2202     procstat_entry_t pse;
2203     char *endptr;
2204
2205     if (!isdigit((int)ent->d_name[0]))
2206       continue;
2207
2208     pid = strtol(ent->d_name, &endptr, 10);
2209     if (*endptr != 0) /* value didn't completely parse as a number */
2210       continue;
2211
2212     status = ps_read_process(pid, &ps, &state);
2213     if (status != 0) {
2214       DEBUG("ps_read_process failed: %i", status);
2215       continue;
2216     }
2217
2218     memset(&pse, 0, sizeof(pse));
2219     pse.id = pid;
2220     pse.age = 0;
2221
2222     pse.num_proc = ps.num_proc;
2223     pse.num_lwp = ps.num_lwp;
2224     pse.vmem_size = ps.vmem_size;
2225     pse.vmem_rss = ps.vmem_rss;
2226     pse.vmem_data = ps.vmem_data;
2227     pse.vmem_code = ps.vmem_code;
2228     pse.stack_size = ps.stack_size;
2229
2230     pse.vmem_minflt_counter = ps.vmem_minflt_counter;
2231     pse.vmem_majflt_counter = ps.vmem_majflt_counter;
2232
2233     pse.cpu_user_counter = ps.cpu_user_counter;
2234     pse.cpu_system_counter = ps.cpu_system_counter;
2235
2236     pse.io_rchar = ps.io_rchar;
2237     pse.io_wchar = ps.io_wchar;
2238     pse.io_syscr = ps.io_syscr;
2239     pse.io_syscw = ps.io_syscw;
2240
2241     pse.cswitch_vol = -1;
2242     pse.cswitch_invol = -1;
2243
2244     switch (state) {
2245     case 'R':
2246       running++;
2247       break;
2248     case 'S':
2249       sleeping++;
2250       break;
2251     case 'E':
2252       detached++;
2253       break;
2254     case 'Z':
2255       zombies++;
2256       break;
2257     case 'T':
2258       stopped++;
2259       break;
2260     case 'A':
2261       daemon++;
2262       break;
2263     case 'Y':
2264       system++;
2265       break;
2266     case 'O':
2267       orphan++;
2268       break;
2269     }
2270
2271     ps_list_add(ps.name, ps_get_cmdline(pid, ps.name, cmdline, sizeof(cmdline)),
2272                 &pse);
2273   } /* while(readdir) */
2274   closedir(proc);
2275
2276   ps_submit_state("running", running);
2277   ps_submit_state("sleeping", sleeping);
2278   ps_submit_state("zombies", zombies);
2279   ps_submit_state("stopped", stopped);
2280   ps_submit_state("detached", detached);
2281   ps_submit_state("daemon", daemon);
2282   ps_submit_state("system", system);
2283   ps_submit_state("orphan", orphan);
2284
2285   for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
2286     ps_submit_proc_list(ps_ptr);
2287
2288   read_fork_rate();
2289 #endif /* KERNEL_SOLARIS */
2290
2291   want_init = 0;
2292
2293   return (0);
2294 } /* int ps_read */
2295
2296 void module_register(void) {
2297   plugin_register_complex_config("processes", ps_config);
2298   plugin_register_init("processes", ps_init);
2299   plugin_register_read("processes", ps_read);
2300 } /* void module_register */