Merge branch 'ssnprintf-cleanup'
[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   snprintf(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     if (snprintf(filename, sizeof(filename), "/proc/%li/task/%s/status", ps->id,
824                  tpid) >= sizeof(filename)) {
825       DEBUG("Filename too long: `%s'", filename);
826       continue;
827     }
828
829     if ((fh = fopen(filename, "r")) == NULL) {
830       DEBUG("Failed to open file `%s'", filename);
831       continue;
832     }
833
834     while (fgets(buffer, sizeof(buffer), fh) != NULL) {
835       derive_t tmp;
836       char *endptr;
837
838       if (strncmp(buffer, "voluntary_ctxt_switches", 23) != 0 &&
839           strncmp(buffer, "nonvoluntary_ctxt_switches", 26) != 0)
840         continue;
841
842       numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
843
844       if (numfields < 2)
845         continue;
846
847       errno = 0;
848       endptr = NULL;
849       tmp = (derive_t)strtoll(fields[1], &endptr, /* base = */ 10);
850       if ((errno == 0) && (endptr != fields[1])) {
851         if (strncmp(buffer, "voluntary_ctxt_switches", 23) == 0) {
852           cswitch_vol += tmp;
853         } else if (strncmp(buffer, "nonvoluntary_ctxt_switches", 26) == 0) {
854           cswitch_invol += tmp;
855         }
856       }
857     } /* while (fgets) */
858
859     if (fclose(fh)) {
860       char errbuf[1024];
861       WARNING("processes: fclose: %s",
862               sstrerror(errno, errbuf, sizeof(errbuf)));
863     }
864   }
865   closedir(dh);
866
867   ps->cswitch_vol = cswitch_vol;
868   ps->cswitch_invol = cswitch_invol;
869
870   return 0;
871 } /* int *ps_read_tasks_status */
872
873 /* Read data from /proc/pid/status */
874 static int ps_read_status(long pid, process_entry_t *ps) {
875   FILE *fh;
876   char buffer[1024];
877   char filename[64];
878   unsigned long lib = 0;
879   unsigned long exe = 0;
880   unsigned long data = 0;
881   unsigned long threads = 0;
882   char *fields[8];
883   int numfields;
884
885   snprintf(filename, sizeof(filename), "/proc/%li/status", pid);
886   if ((fh = fopen(filename, "r")) == NULL)
887     return -1;
888
889   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
890     unsigned long tmp;
891     char *endptr;
892
893     if (strncmp(buffer, "Vm", 2) != 0 && strncmp(buffer, "Threads", 7) != 0)
894       continue;
895
896     numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
897
898     if (numfields < 2)
899       continue;
900
901     errno = 0;
902     endptr = NULL;
903     tmp = strtoul(fields[1], &endptr, /* base = */ 10);
904     if ((errno == 0) && (endptr != fields[1])) {
905       if (strncmp(buffer, "VmData", 6) == 0) {
906         data = tmp;
907       } else if (strncmp(buffer, "VmLib", 5) == 0) {
908         lib = tmp;
909       } else if (strncmp(buffer, "VmExe", 5) == 0) {
910         exe = tmp;
911       } else if (strncmp(buffer, "Threads", 7) == 0) {
912         threads = tmp;
913       }
914     }
915   } /* while (fgets) */
916
917   if (fclose(fh)) {
918     char errbuf[1024];
919     WARNING("processes: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
920   }
921
922   ps->vmem_data = data * 1024;
923   ps->vmem_code = (exe + lib) * 1024;
924   if (threads != 0)
925     ps->num_lwp = threads;
926
927   return 0;
928 } /* int *ps_read_status */
929
930 static int ps_read_io(process_entry_t *ps) {
931   FILE *fh;
932   char buffer[1024];
933   char filename[64];
934
935   char *fields[8];
936   int numfields;
937
938   snprintf(filename, sizeof(filename), "/proc/%li/io", ps->id);
939   if ((fh = fopen(filename, "r")) == NULL) {
940     DEBUG("ps_read_io: Failed to open file `%s'", filename);
941     return -1;
942   }
943
944   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
945     derive_t *val = NULL;
946     long long tmp;
947     char *endptr;
948
949     if (strncasecmp(buffer, "rchar:", 6) == 0)
950       val = &(ps->io_rchar);
951     else if (strncasecmp(buffer, "wchar:", 6) == 0)
952       val = &(ps->io_wchar);
953     else if (strncasecmp(buffer, "syscr:", 6) == 0)
954       val = &(ps->io_syscr);
955     else if (strncasecmp(buffer, "syscw:", 6) == 0)
956       val = &(ps->io_syscw);
957     else
958       continue;
959
960     numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
961
962     if (numfields < 2)
963       continue;
964
965     errno = 0;
966     endptr = NULL;
967     tmp = strtoll(fields[1], &endptr, /* base = */ 10);
968     if ((errno != 0) || (endptr == fields[1]))
969       *val = -1;
970     else
971       *val = (derive_t)tmp;
972   } /* while (fgets) */
973
974   if (fclose(fh)) {
975     char errbuf[1024];
976     WARNING("processes: fclose: %s", sstrerror(errno, errbuf, sizeof(errbuf)));
977   }
978   return 0;
979 } /* int ps_read_io (...) */
980
981 static int ps_count_fd(int pid) {
982   char dirname[64];
983   DIR *dh;
984   struct dirent *ent;
985   int count = 0;
986
987   snprintf(dirname, sizeof(dirname), "/proc/%i/fd", pid);
988
989   if ((dh = opendir(dirname)) == NULL) {
990     DEBUG("Failed to open directory `%s'", dirname);
991     return -1;
992   }
993   while ((ent = readdir(dh)) != NULL) {
994     if (!isdigit((int)ent->d_name[0]))
995       continue;
996     else
997       count++;
998   }
999   closedir(dh);
1000
1001   return (count >= 1) ? count : 1;
1002 } /* int ps_count_fd (pid) */
1003
1004 static void ps_fill_details(const procstat_t *ps, process_entry_t *entry) {
1005   if (entry->has_io == 0) {
1006     ps_read_io(entry);
1007     entry->has_io = 1;
1008   }
1009
1010   if (ps->report_ctx_switch) {
1011     if (entry->has_cswitch == 0) {
1012       ps_read_tasks_status(entry);
1013       entry->has_cswitch = 1;
1014     }
1015   }
1016
1017   if (ps->report_fd_num) {
1018     int num_fd;
1019     if (entry->has_fd == 0 && (num_fd = ps_count_fd(entry->id)) > 0) {
1020       entry->num_fd = num_fd;
1021     }
1022     entry->has_fd = 1;
1023   }
1024 } /* void ps_fill_details (...) */
1025
1026 static int ps_read_process(long pid, process_entry_t *ps, char *state) {
1027   char filename[64];
1028   char buffer[1024];
1029
1030   char *fields[64];
1031   char fields_len;
1032
1033   size_t buffer_len;
1034
1035   char *buffer_ptr;
1036   size_t name_start_pos;
1037   size_t name_end_pos;
1038   size_t name_len;
1039
1040   derive_t cpu_user_counter;
1041   derive_t cpu_system_counter;
1042   long long unsigned vmem_size;
1043   long long unsigned vmem_rss;
1044   long long unsigned stack_size;
1045
1046   ssize_t status;
1047
1048   snprintf(filename, sizeof(filename), "/proc/%li/stat", pid);
1049
1050   status = read_file_contents(filename, buffer, sizeof(buffer) - 1);
1051   if (status <= 0)
1052     return -1;
1053   buffer_len = (size_t)status;
1054   buffer[buffer_len] = 0;
1055
1056   /* The name of the process is enclosed in parens. Since the name can
1057    * contain parens itself, spaces, numbers and pretty much everything
1058    * else, use these to determine the process name. We don't use
1059    * strchr(3) and strrchr(3) to avoid pointer arithmetic which would
1060    * otherwise be required to determine name_len. */
1061   name_start_pos = 0;
1062   while (name_start_pos < buffer_len && buffer[name_start_pos] != '(')
1063     name_start_pos++;
1064
1065   name_end_pos = buffer_len;
1066   while (name_end_pos > 0 && buffer[name_end_pos] != ')')
1067     name_end_pos--;
1068
1069   /* Either '(' or ')' is not found or they are in the wrong order.
1070    * Anyway, something weird that shouldn't happen ever. */
1071   if (name_start_pos >= name_end_pos) {
1072     ERROR("processes plugin: name_start_pos = %zu >= name_end_pos = %zu",
1073           name_start_pos, name_end_pos);
1074     return -1;
1075   }
1076
1077   name_len = (name_end_pos - name_start_pos) - 1;
1078   if (name_len >= sizeof(ps->name))
1079     name_len = sizeof(ps->name) - 1;
1080
1081   sstrncpy(ps->name, &buffer[name_start_pos + 1], name_len + 1);
1082
1083   if ((buffer_len - name_end_pos) < 2)
1084     return -1;
1085   buffer_ptr = &buffer[name_end_pos + 2];
1086
1087   fields_len = strsplit(buffer_ptr, fields, STATIC_ARRAY_SIZE(fields));
1088   if (fields_len < 22) {
1089     DEBUG("processes plugin: ps_read_process (pid = %li):"
1090           " `%s' has only %i fields..",
1091           pid, filename, fields_len);
1092     return -1;
1093   }
1094
1095   *state = fields[0][0];
1096
1097   if (*state == 'Z') {
1098     ps->num_lwp = 0;
1099     ps->num_proc = 0;
1100   } else {
1101     ps->num_lwp = strtoul(fields[17], /* endptr = */ NULL, /* base = */ 10);
1102     if ((ps_read_status(pid, ps)) != 0) {
1103       /* No VMem data */
1104       ps->vmem_data = -1;
1105       ps->vmem_code = -1;
1106       DEBUG("ps_read_process: did not get vmem data for pid %li", pid);
1107     }
1108     if (ps->num_lwp == 0)
1109       ps->num_lwp = 1;
1110     ps->num_proc = 1;
1111   }
1112
1113   /* Leave the rest at zero if this is only a zombi */
1114   if (ps->num_proc == 0) {
1115     DEBUG("processes plugin: This is only a zombie: pid = %li; "
1116           "name = %s;",
1117           pid, ps->name);
1118     return 0;
1119   }
1120
1121   cpu_user_counter = atoll(fields[11]);
1122   cpu_system_counter = atoll(fields[12]);
1123   vmem_size = atoll(fields[20]);
1124   vmem_rss = atoll(fields[21]);
1125   ps->vmem_minflt_counter = atol(fields[7]);
1126   ps->vmem_majflt_counter = atol(fields[9]);
1127
1128   {
1129     unsigned long long stack_start = atoll(fields[25]);
1130     unsigned long long stack_ptr = atoll(fields[26]);
1131
1132     stack_size = (stack_start > stack_ptr) ? stack_start - stack_ptr
1133                                            : stack_ptr - stack_start;
1134   }
1135
1136   /* Convert jiffies to useconds */
1137   cpu_user_counter = cpu_user_counter * 1000000 / CONFIG_HZ;
1138   cpu_system_counter = cpu_system_counter * 1000000 / CONFIG_HZ;
1139   vmem_rss = vmem_rss * pagesize_g;
1140
1141   ps->cpu_user_counter = cpu_user_counter;
1142   ps->cpu_system_counter = cpu_system_counter;
1143   ps->vmem_size = (unsigned long)vmem_size;
1144   ps->vmem_rss = (unsigned long)vmem_rss;
1145   ps->stack_size = (unsigned long)stack_size;
1146
1147   /* no data by default. May be filled by ps_fill_details () */
1148   ps->io_rchar = -1;
1149   ps->io_wchar = -1;
1150   ps->io_syscr = -1;
1151   ps->io_syscw = -1;
1152
1153   ps->cswitch_vol = -1;
1154   ps->cswitch_invol = -1;
1155
1156   /* success */
1157   return 0;
1158 } /* int ps_read_process (...) */
1159
1160 static char *ps_get_cmdline(long pid, char *name, char *buf, size_t buf_len) {
1161   char *buf_ptr;
1162   size_t len;
1163
1164   char file[PATH_MAX];
1165   int fd;
1166
1167   size_t n;
1168
1169   if ((pid < 1) || (NULL == buf) || (buf_len < 2))
1170     return NULL;
1171
1172   snprintf(file, sizeof(file), "/proc/%li/cmdline", pid);
1173
1174   errno = 0;
1175   fd = open(file, O_RDONLY);
1176   if (fd < 0) {
1177     char errbuf[4096];
1178     /* ENOENT means the process exited while we were handling it.
1179      * Don't complain about this, it only fills the logs. */
1180     if (errno != ENOENT)
1181       WARNING("processes plugin: Failed to open `%s': %s.", file,
1182               sstrerror(errno, errbuf, sizeof(errbuf)));
1183     return NULL;
1184   }
1185
1186   buf_ptr = buf;
1187   len = buf_len;
1188
1189   n = 0;
1190
1191   while (42) {
1192     ssize_t status;
1193
1194     status = read(fd, (void *)buf_ptr, len);
1195
1196     if (status < 0) {
1197       char errbuf[1024];
1198
1199       if ((EAGAIN == errno) || (EINTR == errno))
1200         continue;
1201
1202       WARNING("processes plugin: Failed to read from `%s': %s.", file,
1203               sstrerror(errno, errbuf, sizeof(errbuf)));
1204       close(fd);
1205       return NULL;
1206     }
1207
1208     n += status;
1209
1210     if (status == 0)
1211       break;
1212
1213     buf_ptr += status;
1214     len -= status;
1215
1216     if (len == 0)
1217       break;
1218   }
1219
1220   close(fd);
1221
1222   if (0 == n) {
1223     /* cmdline not available; e.g. kernel thread, zombie */
1224     if (NULL == name)
1225       return NULL;
1226
1227     snprintf(buf, buf_len, "[%s]", name);
1228     return buf;
1229   }
1230
1231   assert(n <= buf_len);
1232
1233   if (n == buf_len)
1234     --n;
1235   buf[n] = '\0';
1236
1237   --n;
1238   /* remove trailing whitespace */
1239   while ((n > 0) && (isspace(buf[n]) || ('\0' == buf[n]))) {
1240     buf[n] = '\0';
1241     --n;
1242   }
1243
1244   /* arguments are separated by '\0' in /proc/<pid>/cmdline */
1245   while (n > 0) {
1246     if ('\0' == buf[n])
1247       buf[n] = ' ';
1248     --n;
1249   }
1250   return buf;
1251 } /* char *ps_get_cmdline (...) */
1252
1253 static int read_fork_rate(void) {
1254   FILE *proc_stat;
1255   char buffer[1024];
1256   value_t value;
1257   _Bool value_valid = 0;
1258
1259   proc_stat = fopen("/proc/stat", "r");
1260   if (proc_stat == NULL) {
1261     char errbuf[1024];
1262     ERROR("processes plugin: fopen (/proc/stat) failed: %s",
1263           sstrerror(errno, errbuf, sizeof(errbuf)));
1264     return -1;
1265   }
1266
1267   while (fgets(buffer, sizeof(buffer), proc_stat) != NULL) {
1268     int status;
1269     char *fields[3];
1270     int fields_num;
1271
1272     fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
1273     if (fields_num != 2)
1274       continue;
1275
1276     if (strcmp("processes", fields[0]) != 0)
1277       continue;
1278
1279     status = parse_value(fields[1], &value, DS_TYPE_DERIVE);
1280     if (status == 0)
1281       value_valid = 1;
1282
1283     break;
1284   }
1285   fclose(proc_stat);
1286
1287   if (!value_valid)
1288     return -1;
1289
1290   ps_submit_fork_rate(value.derive);
1291   return 0;
1292 }
1293 #endif /*KERNEL_LINUX */
1294
1295 #if KERNEL_SOLARIS
1296 static char *ps_get_cmdline(long pid,
1297                             char *name __attribute__((unused)), /* {{{ */
1298                             char *buffer, size_t buffer_size) {
1299   char path[PATH_MAX];
1300   psinfo_t info;
1301   ssize_t status;
1302
1303   snprintf(path, sizeof(path), "/proc/%li/psinfo", pid);
1304
1305   status = read_file_contents(path, (void *)&info, sizeof(info));
1306   if ((status < 0) || (((size_t)status) != sizeof(info))) {
1307     ERROR("processes plugin: Unexpected return value "
1308           "while reading \"%s\": "
1309           "Returned %zd but expected %zu.",
1310           path, status, buffer_size);
1311     return NULL;
1312   }
1313
1314   info.pr_psargs[sizeof(info.pr_psargs) - 1] = 0;
1315   sstrncpy(buffer, info.pr_psargs, buffer_size);
1316
1317   return buffer;
1318 } /* }}} int ps_get_cmdline */
1319
1320 /*
1321  * Reads process information on the Solaris OS. The information comes mainly
1322  * from
1323  * /proc/PID/status, /proc/PID/psinfo and /proc/PID/usage
1324  * The values for input and ouput chars are calculated "by hand"
1325  * Added a few "solaris" specific process states as well
1326  */
1327 static int ps_read_process(long pid, process_entry_t *ps, char *state) {
1328   char filename[64];
1329   char f_psinfo[64], f_usage[64];
1330   char *buffer;
1331
1332   pstatus_t *myStatus;
1333   psinfo_t *myInfo;
1334   prusage_t *myUsage;
1335
1336   snprintf(filename, sizeof(filename), "/proc/%li/status", pid);
1337   snprintf(f_psinfo, sizeof(f_psinfo), "/proc/%li/psinfo", pid);
1338   snprintf(f_usage, sizeof(f_usage), "/proc/%li/usage", pid);
1339
1340   buffer = calloc(1, sizeof(pstatus_t));
1341   read_file_contents(filename, buffer, sizeof(pstatus_t));
1342   myStatus = (pstatus_t *)buffer;
1343
1344   buffer = calloc(1, sizeof(psinfo_t));
1345   read_file_contents(f_psinfo, buffer, sizeof(psinfo_t));
1346   myInfo = (psinfo_t *)buffer;
1347
1348   buffer = calloc(1, sizeof(prusage_t));
1349   read_file_contents(f_usage, buffer, sizeof(prusage_t));
1350   myUsage = (prusage_t *)buffer;
1351
1352   sstrncpy(ps->name, myInfo->pr_fname, sizeof(myInfo->pr_fname));
1353   ps->num_lwp = myStatus->pr_nlwp;
1354   if (myInfo->pr_wstat != 0) {
1355     ps->num_proc = 0;
1356     ps->num_lwp = 0;
1357     *state = (char)'Z';
1358
1359     sfree(myStatus);
1360     sfree(myInfo);
1361     sfree(myUsage);
1362     return 0;
1363   } else {
1364     ps->num_proc = 1;
1365     ps->num_lwp = myInfo->pr_nlwp;
1366   }
1367
1368   /*
1369    * Convert system time and user time from nanoseconds to microseconds
1370    * for compatibility with the linux module
1371    */
1372   ps->cpu_system_counter = myStatus->pr_stime.tv_nsec / 1000;
1373   ps->cpu_user_counter = myStatus->pr_utime.tv_nsec / 1000;
1374
1375   /*
1376    * Convert rssize from KB to bytes to be consistent w/ the linux module
1377    */
1378   ps->vmem_rss = myInfo->pr_rssize * 1024;
1379   ps->vmem_size = myInfo->pr_size * 1024;
1380   ps->vmem_minflt_counter = myUsage->pr_minf;
1381   ps->vmem_majflt_counter = myUsage->pr_majf;
1382
1383   /*
1384    * TODO: Data and code segment calculations for Solaris
1385    */
1386
1387   ps->vmem_data = -1;
1388   ps->vmem_code = -1;
1389   ps->stack_size = myStatus->pr_stksize;
1390
1391   /*
1392    * TODO: File descriptor count for Solaris
1393    */
1394   ps->num_fd = 0;
1395
1396   /*
1397    * Calculating input/ouput chars
1398    * Formula used is total chars / total blocks => chars/block
1399    * then convert input/output blocks to chars
1400    */
1401   ulong_t tot_chars = myUsage->pr_ioch;
1402   ulong_t tot_blocks = myUsage->pr_inblk + myUsage->pr_oublk;
1403   ulong_t chars_per_block = 1;
1404   if (tot_blocks != 0)
1405     chars_per_block = tot_chars / tot_blocks;
1406   ps->io_rchar = myUsage->pr_inblk * chars_per_block;
1407   ps->io_wchar = myUsage->pr_oublk * chars_per_block;
1408   ps->io_syscr = myUsage->pr_sysc;
1409   ps->io_syscw = myUsage->pr_sysc;
1410
1411   /*
1412    * TODO: context switch counters for Solaris
1413 */
1414   ps->cswitch_vol = -1;
1415   ps->cswitch_invol = -1;
1416
1417   /*
1418    * TODO: Find way of setting BLOCKED and PAGING status
1419    */
1420
1421   *state = (char)'R';
1422   if (myStatus->pr_flags & PR_ASLEEP)
1423     *state = (char)'S';
1424   else if (myStatus->pr_flags & PR_STOPPED)
1425     *state = (char)'T';
1426   else if (myStatus->pr_flags & PR_DETACH)
1427     *state = (char)'E';
1428   else if (myStatus->pr_flags & PR_DAEMON)
1429     *state = (char)'A';
1430   else if (myStatus->pr_flags & PR_ISSYS)
1431     *state = (char)'Y';
1432   else if (myStatus->pr_flags & PR_ORPHAN)
1433     *state = (char)'O';
1434
1435   sfree(myStatus);
1436   sfree(myInfo);
1437   sfree(myUsage);
1438
1439   return 0;
1440 }
1441
1442 /*
1443  * Reads the number of threads created since the last reboot. On Solaris these
1444  * are retrieved from kstat (module cpu, name sys, class misc, stat nthreads).
1445  * The result is the sum for all the threads created on each cpu
1446  */
1447 static int read_fork_rate(void) {
1448   extern kstat_ctl_t *kc;
1449   derive_t result = 0;
1450
1451   if (kc == NULL)
1452     return -1;
1453
1454   for (kstat_t *ksp_chain = kc->kc_chain; ksp_chain != NULL;
1455        ksp_chain = ksp_chain->ks_next) {
1456     if ((strcmp(ksp_chain->ks_module, "cpu") == 0) &&
1457         (strcmp(ksp_chain->ks_name, "sys") == 0) &&
1458         (strcmp(ksp_chain->ks_class, "misc") == 0)) {
1459       long long tmp;
1460
1461       kstat_read(kc, ksp_chain, NULL);
1462
1463       tmp = get_kstat_value(ksp_chain, "nthreads");
1464       if (tmp != -1LL)
1465         result += tmp;
1466     }
1467   }
1468
1469   ps_submit_fork_rate(result);
1470   return 0;
1471 }
1472 #endif /* KERNEL_SOLARIS */
1473
1474 #if HAVE_THREAD_INFO
1475 static int mach_get_task_name(task_t t, int *pid, char *name,
1476                               size_t name_max_len) {
1477   int mib[4];
1478
1479   struct kinfo_proc kp;
1480   size_t kp_size;
1481
1482   mib[0] = CTL_KERN;
1483   mib[1] = KERN_PROC;
1484   mib[2] = KERN_PROC_PID;
1485
1486   if (pid_for_task(t, pid) != KERN_SUCCESS)
1487     return -1;
1488   mib[3] = *pid;
1489
1490   kp_size = sizeof(kp);
1491   if (sysctl(mib, 4, &kp, &kp_size, NULL, 0) != 0)
1492     return -1;
1493
1494   if (name_max_len > (MAXCOMLEN + 1))
1495     name_max_len = MAXCOMLEN + 1;
1496
1497   strncpy(name, kp.kp_proc.p_comm, name_max_len - 1);
1498   name[name_max_len - 1] = '\0';
1499
1500   DEBUG("pid = %i; name = %s;", *pid, name);
1501
1502   /* We don't do the special handling for `p_comm == "LaunchCFMApp"' as
1503    * `top' does it, because it is a lot of work and only used when
1504    * debugging. -octo */
1505
1506   return 0;
1507 }
1508 #endif /* HAVE_THREAD_INFO */
1509 /* ------- end of additional functions for KERNEL_LINUX/HAVE_THREAD_INFO -------
1510  */
1511
1512 /* do actual readings from kernel */
1513 static int ps_read(void) {
1514 #if HAVE_THREAD_INFO
1515   kern_return_t status;
1516
1517   processor_set_t port_pset_priv;
1518
1519   task_array_t task_list;
1520   mach_msg_type_number_t task_list_len;
1521
1522   int task_pid;
1523   char task_name[MAXCOMLEN + 1];
1524
1525   thread_act_array_t thread_list;
1526   mach_msg_type_number_t thread_list_len;
1527   thread_basic_info_data_t thread_data;
1528   mach_msg_type_number_t thread_data_len;
1529
1530   int running = 0;
1531   int sleeping = 0;
1532   int zombies = 0;
1533   int stopped = 0;
1534   int blocked = 0;
1535
1536   procstat_t *ps;
1537   process_entry_t pse;
1538
1539   ps_list_reset();
1540
1541   /*
1542    * The Mach-concept is a little different from the traditional UNIX
1543    * concept: All the work is done in threads. Threads are contained in
1544    * `tasks'. Therefore, `task status' doesn't make much sense, since
1545    * it's actually a `thread status'.
1546    * Tasks are assigned to sets of processors, so that's where you go to
1547    * get a list.
1548    */
1549   for (mach_msg_type_number_t pset = 0; pset < pset_list_len; pset++) {
1550     if ((status = host_processor_set_priv(port_host_self, pset_list[pset],
1551                                           &port_pset_priv)) != KERN_SUCCESS) {
1552       ERROR("host_processor_set_priv failed: %s\n", mach_error_string(status));
1553       continue;
1554     }
1555
1556     if ((status = processor_set_tasks(port_pset_priv, &task_list,
1557                                       &task_list_len)) != KERN_SUCCESS) {
1558       ERROR("processor_set_tasks failed: %s\n", mach_error_string(status));
1559       mach_port_deallocate(port_task_self, port_pset_priv);
1560       continue;
1561     }
1562
1563     for (mach_msg_type_number_t task = 0; task < task_list_len; task++) {
1564       ps = NULL;
1565       if (mach_get_task_name(task_list[task], &task_pid, task_name,
1566                              PROCSTAT_NAME_LEN) == 0) {
1567         /* search for at least one match */
1568         for (ps = list_head_g; ps != NULL; ps = ps->next)
1569           /* FIXME: cmdline should be here instead of NULL */
1570           if (ps_list_match(task_name, NULL, ps) == 1)
1571             break;
1572       }
1573
1574       /* Collect more detailed statistics for this process */
1575       if (ps != NULL) {
1576         task_basic_info_data_t task_basic_info;
1577         mach_msg_type_number_t task_basic_info_len;
1578         task_events_info_data_t task_events_info;
1579         mach_msg_type_number_t task_events_info_len;
1580         task_absolutetime_info_data_t task_absolutetime_info;
1581         mach_msg_type_number_t task_absolutetime_info_len;
1582
1583         memset(&pse, '\0', sizeof(pse));
1584         pse.id = task_pid;
1585
1586         task_basic_info_len = TASK_BASIC_INFO_COUNT;
1587         status = task_info(task_list[task], TASK_BASIC_INFO,
1588                            (task_info_t)&task_basic_info, &task_basic_info_len);
1589         if (status != KERN_SUCCESS) {
1590           ERROR("task_info failed: %s", mach_error_string(status));
1591           continue; /* with next thread_list */
1592         }
1593
1594         task_events_info_len = TASK_EVENTS_INFO_COUNT;
1595         status =
1596             task_info(task_list[task], TASK_EVENTS_INFO,
1597                       (task_info_t)&task_events_info, &task_events_info_len);
1598         if (status != KERN_SUCCESS) {
1599           ERROR("task_info failed: %s", mach_error_string(status));
1600           continue; /* with next thread_list */
1601         }
1602
1603         task_absolutetime_info_len = TASK_ABSOLUTETIME_INFO_COUNT;
1604         status = task_info(task_list[task], TASK_ABSOLUTETIME_INFO,
1605                            (task_info_t)&task_absolutetime_info,
1606                            &task_absolutetime_info_len);
1607         if (status != KERN_SUCCESS) {
1608           ERROR("task_info failed: %s", mach_error_string(status));
1609           continue; /* with next thread_list */
1610         }
1611
1612         pse.num_proc++;
1613         pse.vmem_size = task_basic_info.virtual_size;
1614         pse.vmem_rss = task_basic_info.resident_size;
1615         /* Does not seem to be easily exposed */
1616         pse.vmem_data = 0;
1617         pse.vmem_code = 0;
1618
1619         pse.io_rchar = -1;
1620         pse.io_wchar = -1;
1621         pse.io_syscr = -1;
1622         pse.io_syscw = -1;
1623
1624         /* File descriptor count not implemented */
1625         pse.num_fd = 0;
1626
1627         pse.vmem_minflt_counter = task_events_info.cow_faults;
1628         pse.vmem_majflt_counter = task_events_info.faults;
1629
1630         pse.cpu_user_counter = task_absolutetime_info.total_user;
1631         pse.cpu_system_counter = task_absolutetime_info.total_system;
1632
1633         /* context switch counters not implemented */
1634         pse.cswitch_vol = -1;
1635         pse.cswitch_invol = -1;
1636       }
1637
1638       status = task_threads(task_list[task], &thread_list, &thread_list_len);
1639       if (status != KERN_SUCCESS) {
1640         /* Apple's `top' treats this case a zombie. It
1641          * makes sense to some extend: A `zombie'
1642          * thread is nonsense, since the task/process
1643          * is dead. */
1644         zombies++;
1645         DEBUG("task_threads failed: %s", mach_error_string(status));
1646         if (task_list[task] != port_task_self)
1647           mach_port_deallocate(port_task_self, task_list[task]);
1648         continue; /* with next task_list */
1649       }
1650
1651       for (mach_msg_type_number_t thread = 0; thread < thread_list_len;
1652            thread++) {
1653         thread_data_len = THREAD_BASIC_INFO_COUNT;
1654         status = thread_info(thread_list[thread], THREAD_BASIC_INFO,
1655                              (thread_info_t)&thread_data, &thread_data_len);
1656         if (status != KERN_SUCCESS) {
1657           ERROR("thread_info failed: %s", mach_error_string(status));
1658           if (task_list[task] != port_task_self)
1659             mach_port_deallocate(port_task_self, thread_list[thread]);
1660           continue; /* with next thread_list */
1661         }
1662
1663         if (ps != NULL)
1664           pse.num_lwp++;
1665
1666         switch (thread_data.run_state) {
1667         case TH_STATE_RUNNING:
1668           running++;
1669           break;
1670         case TH_STATE_STOPPED:
1671         /* What exactly is `halted'? */
1672         case TH_STATE_HALTED:
1673           stopped++;
1674           break;
1675         case TH_STATE_WAITING:
1676           sleeping++;
1677           break;
1678         case TH_STATE_UNINTERRUPTIBLE:
1679           blocked++;
1680           break;
1681         /* There is no `zombie' case here,
1682          * since there are no zombie-threads.
1683          * There's only zombie tasks, which are
1684          * handled above. */
1685         default:
1686           WARNING("Unknown thread status: %i", thread_data.run_state);
1687           break;
1688         } /* switch (thread_data.run_state) */
1689
1690         if (task_list[task] != port_task_self) {
1691           status = mach_port_deallocate(port_task_self, thread_list[thread]);
1692           if (status != KERN_SUCCESS)
1693             ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
1694         }
1695       } /* for (thread_list) */
1696
1697       if ((status = vm_deallocate(port_task_self, (vm_address_t)thread_list,
1698                                   thread_list_len * sizeof(thread_act_t))) !=
1699           KERN_SUCCESS) {
1700         ERROR("vm_deallocate failed: %s", mach_error_string(status));
1701       }
1702       thread_list = NULL;
1703       thread_list_len = 0;
1704
1705       /* Only deallocate the task port, if it isn't our own.
1706        * Don't know what would happen in that case, but this
1707        * is what Apple's top does.. ;) */
1708       if (task_list[task] != port_task_self) {
1709         status = mach_port_deallocate(port_task_self, task_list[task]);
1710         if (status != KERN_SUCCESS)
1711           ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
1712       }
1713
1714       if (ps != NULL)
1715         /* FIXME: cmdline should be here instead of NULL */
1716         ps_list_add(task_name, NULL, &pse);
1717     } /* for (task_list) */
1718
1719     if ((status = vm_deallocate(port_task_self, (vm_address_t)task_list,
1720                                 task_list_len * sizeof(task_t))) !=
1721         KERN_SUCCESS) {
1722       ERROR("vm_deallocate failed: %s", mach_error_string(status));
1723     }
1724     task_list = NULL;
1725     task_list_len = 0;
1726
1727     if ((status = mach_port_deallocate(port_task_self, port_pset_priv)) !=
1728         KERN_SUCCESS) {
1729       ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
1730     }
1731   } /* for (pset_list) */
1732
1733   ps_submit_state("running", running);
1734   ps_submit_state("sleeping", sleeping);
1735   ps_submit_state("zombies", zombies);
1736   ps_submit_state("stopped", stopped);
1737   ps_submit_state("blocked", blocked);
1738
1739   for (ps = list_head_g; ps != NULL; ps = ps->next)
1740     ps_submit_proc_list(ps);
1741 /* #endif HAVE_THREAD_INFO */
1742
1743 #elif KERNEL_LINUX
1744   int running = 0;
1745   int sleeping = 0;
1746   int zombies = 0;
1747   int stopped = 0;
1748   int paging = 0;
1749   int blocked = 0;
1750
1751   struct dirent *ent;
1752   DIR *proc;
1753   long pid;
1754
1755   char cmdline[CMDLINE_BUFFER_SIZE];
1756
1757   int status;
1758   process_entry_t pse;
1759   char state;
1760
1761   running = sleeping = zombies = stopped = paging = blocked = 0;
1762   ps_list_reset();
1763
1764   if ((proc = opendir("/proc")) == NULL) {
1765     char errbuf[1024];
1766     ERROR("Cannot open `/proc': %s", sstrerror(errno, errbuf, sizeof(errbuf)));
1767     return -1;
1768   }
1769
1770   while ((ent = readdir(proc)) != NULL) {
1771     if (!isdigit(ent->d_name[0]))
1772       continue;
1773
1774     if ((pid = atol(ent->d_name)) < 1)
1775       continue;
1776
1777     memset(&pse, 0, sizeof(pse));
1778     pse.id = pid;
1779
1780     status = ps_read_process(pid, &pse, &state);
1781     if (status != 0) {
1782       DEBUG("ps_read_process failed: %i", status);
1783       continue;
1784     }
1785
1786     switch (state) {
1787     case 'R':
1788       running++;
1789       break;
1790     case 'S':
1791       sleeping++;
1792       break;
1793     case 'D':
1794       blocked++;
1795       break;
1796     case 'Z':
1797       zombies++;
1798       break;
1799     case 'T':
1800       stopped++;
1801       break;
1802     case 'W':
1803       paging++;
1804       break;
1805     }
1806
1807     ps_list_add(pse.name,
1808                 ps_get_cmdline(pid, pse.name, cmdline, sizeof(cmdline)), &pse);
1809   }
1810
1811   closedir(proc);
1812
1813   ps_submit_state("running", running);
1814   ps_submit_state("sleeping", sleeping);
1815   ps_submit_state("zombies", zombies);
1816   ps_submit_state("stopped", stopped);
1817   ps_submit_state("paging", paging);
1818   ps_submit_state("blocked", blocked);
1819
1820   for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
1821     ps_submit_proc_list(ps_ptr);
1822
1823   read_fork_rate();
1824 /* #endif KERNEL_LINUX */
1825
1826 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
1827   int running = 0;
1828   int sleeping = 0;
1829   int zombies = 0;
1830   int stopped = 0;
1831   int blocked = 0;
1832   int idle = 0;
1833   int wait = 0;
1834
1835   kvm_t *kd;
1836   char errbuf[_POSIX2_LINE_MAX];
1837   struct kinfo_proc *procs; /* array of processes */
1838   struct kinfo_proc *proc_ptr = NULL;
1839   int count; /* returns number of processes */
1840
1841   process_entry_t pse;
1842
1843   ps_list_reset();
1844
1845   /* Open the kvm interface, get a descriptor */
1846   kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf);
1847   if (kd == NULL) {
1848     ERROR("processes plugin: Cannot open kvm interface: %s", errbuf);
1849     return 0;
1850   }
1851
1852   /* Get the list of processes. */
1853   procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, &count);
1854   if (procs == NULL) {
1855     ERROR("processes plugin: Cannot get kvm processes list: %s",
1856           kvm_geterr(kd));
1857     kvm_close(kd);
1858     return 0;
1859   }
1860
1861   /* Iterate through the processes in kinfo_proc */
1862   for (int i = 0; i < count; i++) {
1863     /* Create only one process list entry per _process_, i.e.
1864      * filter out threads (duplicate PID entries). */
1865     if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) {
1866       char cmdline[CMDLINE_BUFFER_SIZE] = "";
1867       _Bool have_cmdline = 0;
1868
1869       proc_ptr = &(procs[i]);
1870       /* Don't probe system processes and processes without arguments */
1871       if (((procs[i].ki_flag & P_SYSTEM) == 0) && (procs[i].ki_args != NULL)) {
1872         char **argv;
1873         int argc;
1874         int status;
1875
1876         /* retrieve the arguments */
1877         argv = kvm_getargv(kd, proc_ptr, /* nchr = */ 0);
1878         argc = 0;
1879         if ((argv != NULL) && (argv[0] != NULL)) {
1880           while (argv[argc] != NULL)
1881             argc++;
1882
1883           status = strjoin(cmdline, sizeof(cmdline), argv, argc, " ");
1884           if (status < 0)
1885             WARNING("processes plugin: Command line did not fit into buffer.");
1886           else
1887             have_cmdline = 1;
1888         }
1889       } /* if (process has argument list) */
1890
1891       memset(&pse, 0, sizeof(pse));
1892       pse.id = procs[i].ki_pid;
1893
1894       pse.num_proc = 1;
1895       pse.num_lwp = procs[i].ki_numthreads;
1896
1897       pse.vmem_size = procs[i].ki_size;
1898       pse.vmem_rss = procs[i].ki_rssize * pagesize;
1899       pse.vmem_data = procs[i].ki_dsize * pagesize;
1900       pse.vmem_code = procs[i].ki_tsize * pagesize;
1901       pse.stack_size = procs[i].ki_ssize * pagesize;
1902       pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
1903       pse.vmem_majflt_counter = procs[i].ki_rusage.ru_majflt;
1904
1905       pse.cpu_user_counter = 0;
1906       pse.cpu_system_counter = 0;
1907       /*
1908        * The u-area might be swapped out, and we can't get
1909        * at it because we have a crashdump and no swap.
1910        * If it's here fill in these fields, otherwise, just
1911        * leave them 0.
1912        */
1913       if (procs[i].ki_flag & P_INMEM) {
1914         pse.cpu_user_counter = procs[i].ki_rusage.ru_utime.tv_usec +
1915                                (1000000lu * procs[i].ki_rusage.ru_utime.tv_sec);
1916         pse.cpu_system_counter =
1917             procs[i].ki_rusage.ru_stime.tv_usec +
1918             (1000000lu * procs[i].ki_rusage.ru_stime.tv_sec);
1919       }
1920
1921       /* no I/O data */
1922       pse.io_rchar = -1;
1923       pse.io_wchar = -1;
1924       pse.io_syscr = -1;
1925       pse.io_syscw = -1;
1926
1927       /* file descriptor count not implemented */
1928       pse.num_fd = 0;
1929
1930       /* context switch counters not implemented */
1931       pse.cswitch_vol = -1;
1932       pse.cswitch_invol = -1;
1933
1934       ps_list_add(procs[i].ki_comm, have_cmdline ? cmdline : NULL, &pse);
1935
1936       switch (procs[i].ki_stat) {
1937       case SSTOP:
1938         stopped++;
1939         break;
1940       case SSLEEP:
1941         sleeping++;
1942         break;
1943       case SRUN:
1944         running++;
1945         break;
1946       case SIDL:
1947         idle++;
1948         break;
1949       case SWAIT:
1950         wait++;
1951         break;
1952       case SLOCK:
1953         blocked++;
1954         break;
1955       case SZOMB:
1956         zombies++;
1957         break;
1958       }
1959     } /* if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) */
1960   }
1961
1962   kvm_close(kd);
1963
1964   ps_submit_state("running", running);
1965   ps_submit_state("sleeping", sleeping);
1966   ps_submit_state("zombies", zombies);
1967   ps_submit_state("stopped", stopped);
1968   ps_submit_state("blocked", blocked);
1969   ps_submit_state("idle", idle);
1970   ps_submit_state("wait", wait);
1971
1972   for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
1973     ps_submit_proc_list(ps_ptr);
1974 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
1975
1976 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_OPENBSD
1977   int running = 0;
1978   int sleeping = 0;
1979   int zombies = 0;
1980   int stopped = 0;
1981   int onproc = 0;
1982   int idle = 0;
1983   int dead = 0;
1984
1985   kvm_t *kd;
1986   char errbuf[1024];
1987   struct kinfo_proc *procs; /* array of processes */
1988   struct kinfo_proc *proc_ptr = NULL;
1989   int count; /* returns number of processes */
1990
1991   process_entry_t pse;
1992
1993   ps_list_reset();
1994
1995   /* Open the kvm interface, get a descriptor */
1996   kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
1997   if (kd == NULL) {
1998     ERROR("processes plugin: Cannot open kvm interface: %s", errbuf);
1999     return 0;
2000   }
2001
2002   /* Get the list of processes. */
2003   procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &count);
2004   if (procs == NULL) {
2005     ERROR("processes plugin: Cannot get kvm processes list: %s",
2006           kvm_geterr(kd));
2007     kvm_close(kd);
2008     return 0;
2009   }
2010
2011   /* Iterate through the processes in kinfo_proc */
2012   for (int i = 0; i < count; i++) {
2013     /* Create only one process list entry per _process_, i.e.
2014      * filter out threads (duplicate PID entries). */
2015     if ((proc_ptr == NULL) || (proc_ptr->p_pid != procs[i].p_pid)) {
2016       char cmdline[CMDLINE_BUFFER_SIZE] = "";
2017       _Bool have_cmdline = 0;
2018
2019       proc_ptr = &(procs[i]);
2020       /* Don't probe zombie processes  */
2021       if (!P_ZOMBIE(proc_ptr)) {
2022         char **argv;
2023         int argc;
2024         int status;
2025
2026         /* retrieve the arguments */
2027         argv = kvm_getargv(kd, proc_ptr, /* nchr = */ 0);
2028         argc = 0;
2029         if ((argv != NULL) && (argv[0] != NULL)) {
2030           while (argv[argc] != NULL)
2031             argc++;
2032
2033           status = strjoin(cmdline, sizeof(cmdline), argv, argc, " ");
2034           if (status < 0)
2035             WARNING("processes plugin: Command line did not fit into buffer.");
2036           else
2037             have_cmdline = 1;
2038         }
2039       } /* if (process has argument list) */
2040
2041       memset(&pse, 0, sizeof(pse));
2042       pse.id = procs[i].p_pid;
2043
2044       pse.num_proc = 1;
2045       pse.num_lwp = 1; /* XXX: accumulate p_tid values for a single p_pid ? */
2046
2047       pse.vmem_rss = procs[i].p_vm_rssize * pagesize;
2048       pse.vmem_data = procs[i].p_vm_dsize * pagesize;
2049       pse.vmem_code = procs[i].p_vm_tsize * pagesize;
2050       pse.stack_size = procs[i].p_vm_ssize * pagesize;
2051       pse.vmem_size = pse.stack_size + pse.vmem_code + pse.vmem_data;
2052       pse.vmem_minflt_counter = procs[i].p_uru_minflt;
2053       pse.vmem_majflt_counter = procs[i].p_uru_majflt;
2054
2055       pse.cpu_user_counter =
2056           procs[i].p_uutime_usec + (1000000lu * procs[i].p_uutime_sec);
2057       pse.cpu_system_counter =
2058           procs[i].p_ustime_usec + (1000000lu * procs[i].p_ustime_sec);
2059
2060       /* no I/O data */
2061       pse.io_rchar = -1;
2062       pse.io_wchar = -1;
2063       pse.io_syscr = -1;
2064       pse.io_syscw = -1;
2065
2066       /* file descriptor count not implemented */
2067       pse.num_fd = 0;
2068
2069       /* context switch counters not implemented */
2070       pse.cswitch_vol = -1;
2071       pse.cswitch_invol = -1;
2072
2073       ps_list_add(procs[i].p_comm, have_cmdline ? cmdline : NULL, &pse);
2074
2075       switch (procs[i].p_stat) {
2076       case SSTOP:
2077         stopped++;
2078         break;
2079       case SSLEEP:
2080         sleeping++;
2081         break;
2082       case SRUN:
2083         running++;
2084         break;
2085       case SIDL:
2086         idle++;
2087         break;
2088       case SONPROC:
2089         onproc++;
2090         break;
2091       case SDEAD:
2092         dead++;
2093         break;
2094       case SZOMB:
2095         zombies++;
2096         break;
2097       }
2098     } /* if ((proc_ptr == NULL) || (proc_ptr->p_pid != procs[i].p_pid)) */
2099   }
2100
2101   kvm_close(kd);
2102
2103   ps_submit_state("running", running);
2104   ps_submit_state("sleeping", sleeping);
2105   ps_submit_state("zombies", zombies);
2106   ps_submit_state("stopped", stopped);
2107   ps_submit_state("onproc", onproc);
2108   ps_submit_state("idle", idle);
2109   ps_submit_state("dead", dead);
2110
2111   for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
2112     ps_submit_proc_list(ps_ptr);
2113 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_OPENBSD */
2114
2115 #elif HAVE_PROCINFO_H
2116   /* AIX */
2117   int running = 0;
2118   int sleeping = 0;
2119   int zombies = 0;
2120   int stopped = 0;
2121   int paging = 0;
2122   int blocked = 0;
2123
2124   pid_t pindex = 0;
2125   int nprocs;
2126
2127   process_entry_t pse;
2128
2129   ps_list_reset();
2130   while ((nprocs = getprocs64(procentry, sizeof(struct procentry64),
2131                               /* fdsinfo = */ NULL, sizeof(struct fdsinfo64),
2132                               &pindex, MAXPROCENTRY)) > 0) {
2133     for (int i = 0; i < nprocs; i++) {
2134       tid64_t thindex;
2135       int nthreads;
2136       char arglist[MAXARGLN + 1];
2137       char *cargs;
2138       char *cmdline;
2139
2140       if (procentry[i].pi_state == SNONE)
2141         continue;
2142       /* if (procentry[i].pi_state == SZOMB)  FIXME */
2143
2144       cmdline = procentry[i].pi_comm;
2145       cargs = procentry[i].pi_comm;
2146       if (procentry[i].pi_flags & SKPROC) {
2147         if (procentry[i].pi_pid == 0)
2148           cmdline = "swapper";
2149         cargs = cmdline;
2150       } else {
2151         if (getargs(&procentry[i], sizeof(struct procentry64), arglist,
2152                     MAXARGLN) >= 0) {
2153           int n;
2154
2155           n = -1;
2156           while (++n < MAXARGLN) {
2157             if (arglist[n] == '\0') {
2158               if (arglist[n + 1] == '\0')
2159                 break;
2160               arglist[n] = ' ';
2161             }
2162           }
2163           cargs = arglist;
2164         }
2165       }
2166
2167       memset(&pse, 0, sizeof(pse));
2168
2169       pse.id = procentry[i].pi_pid;
2170       pse.num_lwp = procentry[i].pi_thcount;
2171       pse.num_proc = 1;
2172
2173       thindex = 0;
2174       while ((nthreads = getthrds64(procentry[i].pi_pid, thrdentry,
2175                                     sizeof(struct thrdentry64), &thindex,
2176                                     MAXTHRDENTRY)) > 0) {
2177         int j;
2178
2179         for (j = 0; j < nthreads; j++) {
2180           switch (thrdentry[j].ti_state) {
2181           /* case TSNONE: break; */
2182           case TSIDL:
2183             blocked++;
2184             break; /* FIXME is really blocked */
2185           case TSRUN:
2186             running++;
2187             break;
2188           case TSSLEEP:
2189             sleeping++;
2190             break;
2191           case TSSWAP:
2192             paging++;
2193             break;
2194           case TSSTOP:
2195             stopped++;
2196             break;
2197           case TSZOMB:
2198             zombies++;
2199             break;
2200           }
2201         }
2202         if (nthreads < MAXTHRDENTRY)
2203           break;
2204       }
2205
2206       /* tv_usec is nanosec ??? */
2207       pse.cpu_user_counter = procentry[i].pi_ru.ru_utime.tv_sec * 1000000 +
2208                              procentry[i].pi_ru.ru_utime.tv_usec / 1000;
2209
2210       /* tv_usec is nanosec ??? */
2211       pse.cpu_system_counter = procentry[i].pi_ru.ru_stime.tv_sec * 1000000 +
2212                                procentry[i].pi_ru.ru_stime.tv_usec / 1000;
2213
2214       pse.vmem_minflt_counter = procentry[i].pi_minflt;
2215       pse.vmem_majflt_counter = procentry[i].pi_majflt;
2216
2217       pse.vmem_size = procentry[i].pi_tsize + procentry[i].pi_dvm * pagesize;
2218       pse.vmem_rss = (procentry[i].pi_drss + procentry[i].pi_trss) * pagesize;
2219       /* Not supported/implemented */
2220       pse.vmem_data = 0;
2221       pse.vmem_code = 0;
2222       pse.stack_size = 0;
2223
2224       pse.io_rchar = -1;
2225       pse.io_wchar = -1;
2226       pse.io_syscr = -1;
2227       pse.io_syscw = -1;
2228
2229       pse.num_fd = 0;
2230
2231       pse.cswitch_vol = -1;
2232       pse.cswitch_invol = -1;
2233
2234       ps_list_add(cmdline, cargs, &pse);
2235     } /* for (i = 0 .. nprocs) */
2236
2237     if (nprocs < MAXPROCENTRY)
2238       break;
2239   } /* while (getprocs64() > 0) */
2240   ps_submit_state("running", running);
2241   ps_submit_state("sleeping", sleeping);
2242   ps_submit_state("zombies", zombies);
2243   ps_submit_state("stopped", stopped);
2244   ps_submit_state("paging", paging);
2245   ps_submit_state("blocked", blocked);
2246
2247   for (procstat_t *ps = list_head_g; ps != NULL; ps = ps->next)
2248     ps_submit_proc_list(ps);
2249 /* #endif HAVE_PROCINFO_H */
2250
2251 #elif KERNEL_SOLARIS
2252   /*
2253    * The Solaris section adds a few more process states and removes some
2254    * process states compared to linux. Most notably there is no "PAGING"
2255    * and "BLOCKED" state for a process.  The rest is similar to the linux
2256    * code.
2257    */
2258   int running = 0;
2259   int sleeping = 0;
2260   int zombies = 0;
2261   int stopped = 0;
2262   int detached = 0;
2263   int daemon = 0;
2264   int system = 0;
2265   int orphan = 0;
2266
2267   struct dirent *ent;
2268   DIR *proc;
2269
2270   int status;
2271   char state;
2272
2273   char cmdline[PRARGSZ];
2274
2275   ps_list_reset();
2276
2277   proc = opendir("/proc");
2278   if (proc == NULL)
2279     return -1;
2280
2281   while ((ent = readdir(proc)) != NULL) {
2282     long pid;
2283     process_entry_t pse;
2284     char *endptr;
2285
2286     if (!isdigit((int)ent->d_name[0]))
2287       continue;
2288
2289     pid = strtol(ent->d_name, &endptr, 10);
2290     if (*endptr != 0) /* value didn't completely parse as a number */
2291       continue;
2292
2293     memset(&pse, 0, sizeof(pse));
2294     pse.id = pid;
2295
2296     status = ps_read_process(pid, &pse, &state);
2297     if (status != 0) {
2298       DEBUG("ps_read_process failed: %i", status);
2299       continue;
2300     }
2301
2302     switch (state) {
2303     case 'R':
2304       running++;
2305       break;
2306     case 'S':
2307       sleeping++;
2308       break;
2309     case 'E':
2310       detached++;
2311       break;
2312     case 'Z':
2313       zombies++;
2314       break;
2315     case 'T':
2316       stopped++;
2317       break;
2318     case 'A':
2319       daemon++;
2320       break;
2321     case 'Y':
2322       system++;
2323       break;
2324     case 'O':
2325       orphan++;
2326       break;
2327     }
2328
2329     ps_list_add(pse.name,
2330                 ps_get_cmdline(pid, pse.name, cmdline, sizeof(cmdline)), &pse);
2331   } /* while(readdir) */
2332   closedir(proc);
2333
2334   ps_submit_state("running", running);
2335   ps_submit_state("sleeping", sleeping);
2336   ps_submit_state("zombies", zombies);
2337   ps_submit_state("stopped", stopped);
2338   ps_submit_state("detached", detached);
2339   ps_submit_state("daemon", daemon);
2340   ps_submit_state("system", system);
2341   ps_submit_state("orphan", orphan);
2342
2343   for (procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
2344     ps_submit_proc_list(ps_ptr);
2345
2346   read_fork_rate();
2347 #endif /* KERNEL_SOLARIS */
2348
2349   want_init = 0;
2350
2351   return 0;
2352 } /* int ps_read */
2353
2354 void module_register(void) {
2355   plugin_register_complex_config("processes", ps_config);
2356   plugin_register_init("processes", ps_init);
2357   plugin_register_read("processes", ps_read);
2358 } /* void module_register */