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