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