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