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