1 /**
2  * collectd - src/processes.c
3  * Copyright (C) 2005       Lyonel Vincent
4  * Copyright (C) 2006-2017  Florian octo Forster
5  * Copyright (C) 2008       Oleg King
6  * Copyright (C) 2009       Sebastian Harl
7  * Copyright (C) 2009       Andrés J. Díaz
8  * Copyright (C) 2009       Manuel Sanmartin
9  * Copyright (C) 2010       Clément Stenac
10  * Copyright (C) 2012       Cosmin Ioiart
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU General Public License as published by the
14  * Free Software Foundation; either version 2 of the License, or (at your
15  * option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, write to the Free Software Foundation, Inc.,
24  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
25  *
26  * Authors:
27  *   Lyonel Vincent <lyonel at ezix.org>
28  *   Florian octo Forster <octo at collectd.org>
29  *   Oleg King <king2 at kaluga.ru>
30  *   Sebastian Harl <sh at tokkee.org>
31  *   Andrés J. Díaz <ajdiaz at connectical.com>
32  *   Manuel Sanmartin
33  *   Clément Stenac <clement.stenac at diwi.org>
34  *   Cosmin Ioiart <cioiart at gmail.com>
35  *   Pavel Rochnyack <pavel2000 at ngs.ru>
36  *   Wilfried Goesgens <dothebart at citadel.org>
37  **/
38 
39 #include "collectd.h"
40 
41 #include "plugin.h"
42 #include "utils/common/common.h"
43 
44 #if HAVE_LIBTASKSTATS
45 #include "utils/taskstats/taskstats.h"
46 #include "utils_complain.h"
47 #endif
48 
49 /* Include header files for the mach system, if they exist.. */
50 #if HAVE_THREAD_INFO
51 #if HAVE_MACH_MACH_INIT_H
52 #include <mach/mach_init.h>
53 #endif
54 #if HAVE_MACH_HOST_PRIV_H
55 #include <mach/host_priv.h>
56 #endif
57 #if HAVE_MACH_MACH_ERROR_H
58 #include <mach/mach_error.h>
59 #endif
60 #if HAVE_MACH_MACH_HOST_H
61 #include <mach/mach_host.h>
62 #endif
63 #if HAVE_MACH_MACH_PORT_H
64 #include <mach/mach_port.h>
65 #endif
66 #if HAVE_MACH_MACH_TYPES_H
67 #include <mach/mach_types.h>
68 #endif
69 #if HAVE_MACH_MESSAGE_H
70 #include <mach/message.h>
71 #endif
72 #if HAVE_MACH_PROCESSOR_SET_H
73 #include <mach/processor_set.h>
74 #endif
75 #if HAVE_MACH_TASK_H
76 #include <mach/task.h>
77 #endif
78 #if HAVE_MACH_THREAD_ACT_H
79 #include <mach/thread_act.h>
80 #endif
81 #if HAVE_MACH_VM_REGION_H
82 #include <mach/vm_region.h>
83 #endif
84 #if HAVE_MACH_VM_MAP_H
85 #include <mach/vm_map.h>
86 #endif
87 #if HAVE_MACH_VM_PROT_H
88 #include <mach/vm_prot.h>
89 #endif
90 #if HAVE_SYS_SYSCTL_H
91 #include <sys/sysctl.h>
92 #endif
93 /* #endif HAVE_THREAD_INFO */
94 
95 #elif KERNEL_LINUX
96 #if HAVE_LINUX_CONFIG_H
97 #include <linux/config.h>
98 #endif
99 #ifndef CONFIG_HZ
100 #define CONFIG_HZ 100
101 #endif
102 /* #endif KERNEL_LINUX */
103 
104 #elif HAVE_LIBKVM_GETPROCS &&                                                  \
105     (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD || HAVE_STRUCT_KINFO_PROC_DRAGONFLY || \
106      HAVE_STRUCT_KINFO_PROC2_NETBSD)
107 #include <kvm.h>
108 #include <sys/param.h>
109 #ifndef HAVE_STRUCT_KINFO_PROC_DRAGONFLY
110 #include <sys/proc.h>
111 #endif
112 #include <sys/sysctl.h>
113 #if defined(__FreeBSD__) || defined(__DragonFly__)
114 #include <sys/user.h>
115 #endif
116 /* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD ||
117  * HAVE_STRUCT_KINFO_PROC_OPENBSD || HAVE_STRUCT_KINFO_PROC2_NETBSD) */
118 
119 #elif HAVE_PROCINFO_H
120 #include <procinfo.h>
121 #include <sys/types.h>
122 
123 #define MAXPROCENTRY 32
124 #define MAXTHRDENTRY 16
125 #define MAXARGLN 1024
126 /* #endif HAVE_PROCINFO_H */
127 
128 #elif KERNEL_SOLARIS
129 /* Hack: Avoid #error when building a 32-bit binary with
130  * _FILE_OFFSET_BITS=64. There is a reason for this #error, as one
131  * of the structures in <sys/procfs.h> uses an off_t, but that
132  * isn't relevant to our usage of procfs. */
133 #if !defined(_LP64) && _FILE_OFFSET_BITS == 64
134 #define SAVE_FOB_64
135 #undef _FILE_OFFSET_BITS
136 #endif
137 
138 #include <procfs.h>
139 
140 #ifdef SAVE_FOB_64
141 #define _FILE_OFFSET_BITS 64
142 #undef SAVE_FOB_64
143 #endif
144 
145 #include <dirent.h>
146 #include <sys/user.h>
147 
148 #ifndef MAXCOMLEN
149 #define MAXCOMLEN 16
150 #endif
151 
152 /* #endif KERNEL_SOLARIS */
153 
154 #else
155 #error "No applicable input method."
156 #endif
157 
158 #if HAVE_REGEX_H
159 #include <regex.h>
160 #endif
161 
162 #if HAVE_KSTAT_H
163 #include <kstat.h>
164 #endif
165 
166 #ifdef HAVE_SYS_CAPABILITY_H
167 #include <sys/capability.h>
168 #endif
169 
170 #ifndef CMDLINE_BUFFER_SIZE
171 #if defined(ARG_MAX) && (ARG_MAX < 4096)
172 #define CMDLINE_BUFFER_SIZE ARG_MAX
173 #else
174 #define CMDLINE_BUFFER_SIZE 4096
175 #endif
176 #endif
177 
178 #define PROCSTAT_NAME_LEN 256
179 typedef struct process_entry_s {
180   unsigned long id;
181   char name[PROCSTAT_NAME_LEN];
182 
183   unsigned long num_proc;
184   unsigned long num_lwp;
185   unsigned long num_fd;
186   unsigned long num_maps;
187   unsigned long vmem_size;
188   unsigned long vmem_rss;
189   unsigned long vmem_data;
190   unsigned long vmem_code;
191   unsigned long stack_size;
192 
193   derive_t vmem_minflt_counter;
194   derive_t vmem_majflt_counter;
195 
196   derive_t cpu_user_counter;
197   derive_t cpu_system_counter;
198 
199   /* io data */
200   derive_t io_rchar;
201   derive_t io_wchar;
202   derive_t io_syscr;
203   derive_t io_syscw;
204   derive_t io_diskr;
205   derive_t io_diskw;
206   bool has_io;
207 
208   derive_t cswitch_vol;
209   derive_t cswitch_invol;
210   bool has_cswitch;
211 
212 #if HAVE_LIBTASKSTATS
213   ts_delay_t delay;
214 #endif
215   bool has_delay;
216 
217   bool has_fd;
218 
219   bool has_maps;
220 } process_entry_t;
221 
222 typedef struct collectd_procstat_entry_s {
223   unsigned long id;
224   unsigned char age;
225 
226   derive_t vmem_minflt_counter;
227   derive_t vmem_majflt_counter;
228 
229   derive_t cpu_user_counter;
230   derive_t cpu_system_counter;
231 
232   /* io data */
233   derive_t io_rchar;
234   derive_t io_wchar;
235   derive_t io_syscr;
236   derive_t io_syscw;
237   derive_t io_diskr;
238   derive_t io_diskw;
239 
240   derive_t cswitch_vol;
241   derive_t cswitch_invol;
242 
243 #if HAVE_LIBTASKSTATS
244   value_to_rate_state_t delay_cpu;
245   value_to_rate_state_t delay_blkio;
246   value_to_rate_state_t delay_swapin;
247   value_to_rate_state_t delay_freepages;
248 #endif
249 
250   struct collectd_procstat_entry_s *next;
251 } collectd_procstat_entry_t;
252 
253 typedef struct collectd_procstat {
254   char name[PROCSTAT_NAME_LEN];
255 #if HAVE_REGEX_H
256   regex_t *re;
257 #endif
258 
259   unsigned long num_proc;
260   unsigned long num_lwp;
261   unsigned long num_fd;
262   unsigned long num_maps;
263   unsigned long vmem_size;
264   unsigned long vmem_rss;
265   unsigned long vmem_data;
266   unsigned long vmem_code;
267   unsigned long stack_size;
268 
269   derive_t vmem_minflt_counter;
270   derive_t vmem_majflt_counter;
271 
272   derive_t cpu_user_counter;
273   derive_t cpu_system_counter;
274 
275   /* io data */
276   derive_t io_rchar;
277   derive_t io_wchar;
278   derive_t io_syscr;
279   derive_t io_syscw;
280   derive_t io_diskr;
281   derive_t io_diskw;
282 
283   derive_t cswitch_vol;
284   derive_t cswitch_invol;
285 
286   /* Linux Delay Accounting. Unit is ns/s. */
287   gauge_t delay_cpu;
288   gauge_t delay_blkio;
289   gauge_t delay_swapin;
290   gauge_t delay_freepages;
291 
292   bool report_fd_num;
293   bool report_maps_num;
294   bool report_ctx_switch;
295   bool report_delay;
296 
297   struct collectd_procstat *next;
298   struct collectd_procstat_entry_s *instances;
299 } collectd_procstat_t;
300 
301 static collectd_procstat_t *list_head_g;
302 
303 static bool want_init = true;
304 static bool report_ctx_switch;
305 static bool report_fd_num;
306 static bool report_maps_num;
307 static bool report_delay;
308 
309 #if HAVE_THREAD_INFO
310 static mach_port_t port_host_self;
311 static mach_port_t port_task_self;
312 
313 static processor_set_name_array_t pset_list;
314 static mach_msg_type_number_t pset_list_len;
315 /* #endif HAVE_THREAD_INFO */
316 
317 #elif KERNEL_LINUX
318 static long pagesize_g;
319 static void ps_fill_details(const collectd_procstat_t *ps, process_entry_t *entry);
320 /* #endif KERNEL_LINUX */
321 
322 #elif HAVE_LIBKVM_GETPROCS &&                                                  \
323     (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD || HAVE_STRUCT_KINFO_PROC_DRAGONFLY || \
324      HAVE_STRUCT_KINFO_PROC2_NETBSD)
325 static int pagesize;
326 
327 #if KERNEL_NETBSD
328 int maxslp;
329 #endif
330 
331 /* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD ||
332  * HAVE_STRUCT_KINFO_PROC_OPENBSD || HAVE_STRUCT_KINFO_PROC2_NETBSD) */
333 
334 #elif HAVE_PROCINFO_H
335 static struct procentry64 procentry[MAXPROCENTRY];
336 static struct thrdentry64 thrdentry[MAXTHRDENTRY];
337 static int pagesize;
338 
339 #ifndef _AIXVERSION_610
340 int getprocs64(void *procsinfo, int sizproc, void *fdsinfo, int sizfd,
341                pid_t *index, int count);
342 int getthrds64(pid_t, void *, int, tid64_t *, int);
343 #endif
344 int getargs(void *processBuffer, int bufferLen, char *argsBuffer, int argsLen);
345 #endif /* HAVE_PROCINFO_H */
346 
347 #if HAVE_LIBTASKSTATS
348 static ts_t *taskstats_handle;
349 #endif
350 
351 /* put name of process from config to list_head_g tree
352  * list_head_g is a list of 'procstat_t' structs with
353  * processes names we want to watch */
ps_list_register(const char * name,const char * regexp)354 static collectd_procstat_t *ps_list_register(const char *name, const char *regexp) {
355   collectd_procstat_t *new;
356   collectd_procstat_t *ptr;
357   int status;
358 
359   new = calloc(1, sizeof(*new));
360   if (new == NULL) {
361     ERROR("processes plugin: ps_list_register: calloc failed.");
362     return NULL;
363   }
364   sstrncpy(new->name, name, sizeof(new->name));
365 
366   new->io_rchar = -1;
367   new->io_wchar = -1;
368   new->io_syscr = -1;
369   new->io_syscw = -1;
370   new->io_diskr = -1;
371   new->io_diskw = -1;
372   new->cswitch_vol = -1;
373   new->cswitch_invol = -1;
374 
375   new->report_fd_num = report_fd_num;
376   new->report_maps_num = report_maps_num;
377   new->report_ctx_switch = report_ctx_switch;
378   new->report_delay = report_delay;
379 
380 #if HAVE_REGEX_H
381   if (regexp != NULL) {
382     DEBUG("ProcessMatch: adding \"%s\" as criteria to process %s.", regexp,
383           name);
384     new->re = malloc(sizeof(*new->re));
385     if (new->re == NULL) {
386       ERROR("processes plugin: ps_list_register: malloc failed.");
387       sfree(new);
388       return NULL;
389     }
390 
391     status = regcomp(new->re, regexp, REG_EXTENDED | REG_NOSUB);
392     if (status != 0) {
393       DEBUG("ProcessMatch: compiling the regular expression \"%s\" failed.",
394             regexp);
395       sfree(new->re);
396       sfree(new);
397       return NULL;
398     }
399   }
400 #else
401   if (regexp != NULL) {
402     ERROR("processes plugin: ps_list_register: "
403           "Regular expression \"%s\" found in config "
404           "file, but support for regular expressions "
405           "has been disabled at compile time.",
406           regexp);
407     sfree(new);
408     return NULL;
409   }
410 #endif
411 
412   for (ptr = list_head_g; ptr != NULL; ptr = ptr->next) {
413     if (strcmp(ptr->name, name) == 0) {
414       WARNING("processes plugin: You have configured more "
415               "than one `Process' or "
416               "`ProcessMatch' with the same name. "
417               "All but the first setting will be "
418               "ignored.");
419 #if HAVE_REGEX_H
420       sfree(new->re);
421 #endif
422       sfree(new);
423       return NULL;
424     }
425 
426     if (ptr->next == NULL)
427       break;
428   }
429 
430   if (ptr == NULL)
431     list_head_g = new;
432   else
433     ptr->next = new;
434 
435   return new;
436 } /* void ps_list_register */
437 
438 /* try to match name against entry, returns 1 if success */
ps_list_match(const char * name,const char * cmdline,collectd_procstat_t * ps)439 static int ps_list_match(const char *name, const char *cmdline,
440                          collectd_procstat_t *ps) {
441 #if HAVE_REGEX_H
442   if (ps->re != NULL) {
443     int status;
444     const char *str;
445 
446     str = cmdline;
447     if ((str == NULL) || (str[0] == 0))
448       str = name;
449 
450     assert(str != NULL);
451 
452     status = regexec(ps->re, str,
453                      /* nmatch = */ 0,
454                      /* pmatch = */ NULL,
455                      /* eflags = */ 0);
456     if (status == 0)
457       return 1;
458   } else
459 #endif
460       if (strcmp(ps->name, name) == 0)
461     return 1;
462 
463   return 0;
464 } /* int ps_list_match */
465 
ps_update_counter(derive_t * group_counter,derive_t * curr_counter,derive_t new_counter)466 static void ps_update_counter(derive_t *group_counter, derive_t *curr_counter,
467                               derive_t new_counter) {
468   unsigned long curr_value;
469 
470   if (want_init) {
471     *curr_counter = new_counter;
472     return;
473   }
474 
475   if (new_counter < *curr_counter)
476     curr_value = new_counter + (ULONG_MAX - *curr_counter);
477   else
478     curr_value = new_counter - *curr_counter;
479 
480   if (*group_counter == -1)
481     *group_counter = 0;
482 
483   *curr_counter = new_counter;
484   *group_counter += curr_value;
485 }
486 
487 #if HAVE_LIBTASKSTATS
ps_update_delay_one(gauge_t * out_rate_sum,value_to_rate_state_t * state,uint64_t cnt,cdtime_t t)488 static void ps_update_delay_one(gauge_t *out_rate_sum,
489                                 value_to_rate_state_t *state, uint64_t cnt,
490                                 cdtime_t t) {
491   gauge_t rate = NAN;
492   int status = value_to_rate(&rate, (value_t){.counter = (counter_t)cnt},
493                              DS_TYPE_COUNTER, t, state);
494   if ((status != 0) || isnan(rate)) {
495     return;
496   }
497 
498   if (isnan(*out_rate_sum)) {
499     *out_rate_sum = rate;
500   } else {
501     *out_rate_sum += rate;
502   }
503 }
504 
ps_update_delay(collectd_procstat_t * out,collectd_procstat_entry_t * prev,process_entry_t * curr)505 static void ps_update_delay(collectd_procstat_t *out, collectd_procstat_entry_t *prev,
506                             process_entry_t *curr) {
507   cdtime_t now = cdtime();
508 
509   ps_update_delay_one(&out->delay_cpu, &prev->delay_cpu, curr->delay.cpu_ns,
510                       now);
511   ps_update_delay_one(&out->delay_blkio, &prev->delay_blkio,
512                       curr->delay.blkio_ns, now);
513   ps_update_delay_one(&out->delay_swapin, &prev->delay_swapin,
514                       curr->delay.swapin_ns, now);
515   ps_update_delay_one(&out->delay_freepages, &prev->delay_freepages,
516                       curr->delay.freepages_ns, now);
517 }
518 #endif
519 
520 /* add process entry to 'instances' of process 'name' (or refresh it) */
ps_list_add(const char * name,const char * cmdline,process_entry_t * entry)521 static void ps_list_add(const char *name, const char *cmdline,
522                         process_entry_t *entry) {
523   collectd_procstat_entry_t *pse;
524 
525   if (entry->id == 0)
526     return;
527 
528   for (collectd_procstat_t *ps = list_head_g; ps != NULL; ps = ps->next) {
529     if ((ps_list_match(name, cmdline, ps)) == 0)
530       continue;
531 
532 #if KERNEL_LINUX
533     ps_fill_details(ps, entry);
534 #endif
535 
536     for (pse = ps->instances; pse != NULL; pse = pse->next)
537       if ((pse->id == entry->id) || (pse->next == NULL))
538         break;
539 
540     if ((pse == NULL) || (pse->id != entry->id)) {
541       collectd_procstat_entry_t *new;
542 
543       new = calloc(1, sizeof(*new));
544       if (new == NULL)
545         return;
546       new->id = entry->id;
547 
548       if (pse == NULL)
549         ps->instances = new;
550       else
551         pse->next = new;
552 
553       pse = new;
554     }
555 
556     pse->age = 0;
557 
558     ps->num_proc += entry->num_proc;
559     ps->num_lwp += entry->num_lwp;
560     ps->num_fd += entry->num_fd;
561     ps->num_maps += entry->num_maps;
562     ps->vmem_size += entry->vmem_size;
563     ps->vmem_rss += entry->vmem_rss;
564     ps->vmem_data += entry->vmem_data;
565     ps->vmem_code += entry->vmem_code;
566     ps->stack_size += entry->stack_size;
567 
568     if ((entry->io_rchar != -1) && (entry->io_wchar != -1)) {
569       ps_update_counter(&ps->io_rchar, &pse->io_rchar, entry->io_rchar);
570       ps_update_counter(&ps->io_wchar, &pse->io_wchar, entry->io_wchar);
571     }
572 
573     if ((entry->io_syscr != -1) && (entry->io_syscw != -1)) {
574       ps_update_counter(&ps->io_syscr, &pse->io_syscr, entry->io_syscr);
575       ps_update_counter(&ps->io_syscw, &pse->io_syscw, entry->io_syscw);
576     }
577 
578     if ((entry->io_diskr != -1) && (entry->io_diskw != -1)) {
579       ps_update_counter(&ps->io_diskr, &pse->io_diskr, entry->io_diskr);
580       ps_update_counter(&ps->io_diskw, &pse->io_diskw, entry->io_diskw);
581     }
582 
583     if ((entry->cswitch_vol != -1) && (entry->cswitch_invol != -1)) {
584       ps_update_counter(&ps->cswitch_vol, &pse->cswitch_vol,
585                         entry->cswitch_vol);
586       ps_update_counter(&ps->cswitch_invol, &pse->cswitch_invol,
587                         entry->cswitch_invol);
588     }
589 
590     ps_update_counter(&ps->vmem_minflt_counter, &pse->vmem_minflt_counter,
591                       entry->vmem_minflt_counter);
592     ps_update_counter(&ps->vmem_majflt_counter, &pse->vmem_majflt_counter,
593                       entry->vmem_majflt_counter);
594 
595     ps_update_counter(&ps->cpu_user_counter, &pse->cpu_user_counter,
596                       entry->cpu_user_counter);
597     ps_update_counter(&ps->cpu_system_counter, &pse->cpu_system_counter,
598                       entry->cpu_system_counter);
599 
600 #if HAVE_LIBTASKSTATS
601     if (entry->has_delay)
602       ps_update_delay(ps, pse, entry);
603 #endif
604   }
605 }
606 
607 /* remove old entries from instances of processes in list_head_g */
ps_list_reset(void)608 static void ps_list_reset(void) {
609   collectd_procstat_entry_t *pse;
610   collectd_procstat_entry_t *pse_prev;
611 
612   for (collectd_procstat_t *ps = list_head_g; ps != NULL; ps = ps->next) {
613     ps->num_proc = 0;
614     ps->num_lwp = 0;
615     ps->num_fd = 0;
616     ps->num_maps = 0;
617     ps->vmem_size = 0;
618     ps->vmem_rss = 0;
619     ps->vmem_data = 0;
620     ps->vmem_code = 0;
621     ps->stack_size = 0;
622 
623     ps->delay_cpu = NAN;
624     ps->delay_blkio = NAN;
625     ps->delay_swapin = NAN;
626     ps->delay_freepages = NAN;
627 
628     pse_prev = NULL;
629     pse = ps->instances;
630     while (pse != NULL) {
631       if (pse->age > 0) {
632         DEBUG("Removing this collectd_procstat entry cause it's too old: "
633               "id = %lu; name = %s;",
634               pse->id, ps->name);
635 
636         if (pse_prev == NULL) {
637           ps->instances = pse->next;
638           free(pse);
639           pse = ps->instances;
640         } else {
641           pse_prev->next = pse->next;
642           free(pse);
643           pse = pse_prev->next;
644         }
645       } else {
646         pse->age = 1;
647         pse_prev = pse;
648         pse = pse->next;
649       }
650     } /* while (pse != NULL) */
651   }   /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
652 }
653 
ps_tune_instance(oconfig_item_t * ci,collectd_procstat_t * ps)654 static void ps_tune_instance(oconfig_item_t *ci, collectd_procstat_t *ps) {
655   for (int i = 0; i < ci->children_num; i++) {
656     oconfig_item_t *c = ci->children + i;
657 
658     if (strcasecmp(c->key, "CollectContextSwitch") == 0)
659       cf_util_get_boolean(c, &ps->report_ctx_switch);
660     else if (strcasecmp(c->key, "CollectFileDescriptor") == 0)
661       cf_util_get_boolean(c, &ps->report_fd_num);
662     else if (strcasecmp(c->key, "CollectMemoryMaps") == 0)
663       cf_util_get_boolean(c, &ps->report_maps_num);
664     else if (strcasecmp(c->key, "CollectDelayAccounting") == 0) {
665 #if HAVE_LIBTASKSTATS
666       cf_util_get_boolean(c, &ps->report_delay);
667 #else
668       WARNING("processes plugin: The plugin has been compiled without support "
669               "for the \"CollectDelayAccounting\" option.");
670 #endif
671     } else {
672       ERROR("processes plugin: Option \"%s\" not allowed here.", c->key);
673     }
674   } /* for (ci->children) */
675 } /* void ps_tune_instance */
676 
677 /* put all pre-defined 'Process' names from config to list_head_g tree */
ps_config(oconfig_item_t * ci)678 static int ps_config(oconfig_item_t *ci) {
679 #if KERNEL_LINUX
680   const size_t max_procname_len = 15;
681 #elif KERNEL_SOLARIS || KERNEL_FREEBSD || KERNEL_DRAGONFLY
682   const size_t max_procname_len = MAXCOMLEN - 1;
683 #endif
684 
685   collectd_procstat_t *ps;
686 
687   for (int i = 0; i < ci->children_num; ++i) {
688     oconfig_item_t *c = ci->children + i;
689 
690     if (strcasecmp(c->key, "Process") == 0) {
691       if ((c->values_num != 1) || (OCONFIG_TYPE_STRING != c->values[0].type)) {
692         ERROR("processes plugin: `Process' expects exactly "
693               "one string argument (got %i).",
694               c->values_num);
695         continue;
696       }
697 
698 #if KERNEL_LINUX || KERNEL_SOLARIS || KERNEL_FREEBSD || KERNEL_DRAGONFLY
699       if (strlen(c->values[0].value.string) > max_procname_len) {
700         WARNING("processes plugin: this platform has a %" PRIsz
701                 " character limit "
702                 "to process names. The `Process \"%s\"' option will "
703                 "not work as expected.",
704                 max_procname_len, c->values[0].value.string);
705       }
706 #endif
707 
708       ps = ps_list_register(c->values[0].value.string, NULL);
709 
710       if (c->children_num != 0 && ps != NULL)
711         ps_tune_instance(c, ps);
712     } else if (strcasecmp(c->key, "ProcessMatch") == 0) {
713       if ((c->values_num != 2) || (OCONFIG_TYPE_STRING != c->values[0].type) ||
714           (OCONFIG_TYPE_STRING != c->values[1].type)) {
715         ERROR("processes plugin: `ProcessMatch' needs exactly "
716               "two string arguments (got %i).",
717               c->values_num);
718         continue;
719       }
720 
721       ps = ps_list_register(c->values[0].value.string,
722                             c->values[1].value.string);
723 
724       if (c->children_num != 0 && ps != NULL)
725         ps_tune_instance(c, ps);
726     } else if (strcasecmp(c->key, "CollectContextSwitch") == 0) {
727       cf_util_get_boolean(c, &report_ctx_switch);
728     } else if (strcasecmp(c->key, "CollectFileDescriptor") == 0) {
729       cf_util_get_boolean(c, &report_fd_num);
730     } else if (strcasecmp(c->key, "CollectMemoryMaps") == 0) {
731       cf_util_get_boolean(c, &report_maps_num);
732     } else if (strcasecmp(c->key, "CollectDelayAccounting") == 0) {
733 #if HAVE_LIBTASKSTATS
734       cf_util_get_boolean(c, &report_delay);
735 #else
736       WARNING("processes plugin: The plugin has been compiled without support "
737               "for the \"CollectDelayAccounting\" option.");
738 #endif
739     } else {
740       ERROR("processes plugin: The `%s' configuration option is not "
741             "understood and will be ignored.",
742             c->key);
743       continue;
744     }
745   }
746 
747   return 0;
748 }
749 
ps_init(void)750 static int ps_init(void) {
751 #if HAVE_THREAD_INFO
752   kern_return_t status;
753 
754   port_host_self = mach_host_self();
755   port_task_self = mach_task_self();
756 
757   if (pset_list != NULL) {
758     vm_deallocate(port_task_self, (vm_address_t)pset_list,
759                   pset_list_len * sizeof(processor_set_t));
760     pset_list = NULL;
761     pset_list_len = 0;
762   }
763 
764   if ((status = host_processor_sets(port_host_self, &pset_list,
765                                     &pset_list_len)) != KERN_SUCCESS) {
766     ERROR("host_processor_sets failed: %s\n", mach_error_string(status));
767     pset_list = NULL;
768     pset_list_len = 0;
769     return -1;
770   }
771   /* #endif HAVE_THREAD_INFO */
772 
773 #elif KERNEL_LINUX
774   pagesize_g = sysconf(_SC_PAGESIZE);
775   DEBUG("pagesize_g = %li; CONFIG_HZ = %i;", pagesize_g, CONFIG_HZ);
776 
777 #if HAVE_LIBTASKSTATS
778   if (taskstats_handle == NULL) {
779     taskstats_handle = ts_create();
780     if (taskstats_handle == NULL) {
781       WARNING("processes plugin: Creating taskstats handle failed.");
782     }
783   }
784 #endif
785   /* #endif KERNEL_LINUX */
786 
787 #elif HAVE_LIBKVM_GETPROCS &&                                                  \
788     (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_OPENBSD || HAVE_STRUCT_KINFO_PROC_DRAGONFLY || \
789      HAVE_STRUCT_KINFO_PROC2_NETBSD)
790 #if KERNEL_NETBSD
791   int mib[2];
792   size_t size;
793 
794   mib[0] = CTL_VM;
795   mib[1] = VM_MAXSLP;
796   size = sizeof(maxslp);
797   if (sysctl(mib, 2, &maxslp, &size, NULL, 0) == -1)
798     maxslp = 20; /* reasonable default? */
799 #endif
800 
801   pagesize = getpagesize();
802   /* #endif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD ||
803    * HAVE_STRUCT_KINFO_PROC_OPENBSD || HAVE_STRUCT_KINFO_PROC2_NETBSD) */
804 
805 #elif HAVE_PROCINFO_H
806   pagesize = getpagesize();
807 #endif /* HAVE_PROCINFO_H */
808 
809   return 0;
810 } /* int ps_init */
811 
812 /* submit global state (e.g.: qty of zombies, running, etc..) */
ps_submit_state(const char * state,double value)813 static void ps_submit_state(const char *state, double value) {
814   value_list_t vl = VALUE_LIST_INIT;
815 
816   vl.values = &(value_t){.gauge = value};
817   vl.values_len = 1;
818   sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
819   sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance));
820   sstrncpy(vl.type, "ps_state", sizeof(vl.type));
821   sstrncpy(vl.type_instance, state, sizeof(vl.type_instance));
822 
823   plugin_dispatch_values(&vl);
824 }
825 
826 /* submit info about specific process (e.g.: memory taken, cpu usage, etc..) */
ps_submit_proc_list(collectd_procstat_t * ps)827 static void ps_submit_proc_list(collectd_procstat_t *ps) {
828   value_list_t vl = VALUE_LIST_INIT;
829   value_t values[2];
830 
831   vl.values = values;
832   sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
833   sstrncpy(vl.plugin_instance, ps->name, sizeof(vl.plugin_instance));
834 
835   sstrncpy(vl.type, "ps_vm", sizeof(vl.type));
836   vl.values[0].gauge = ps->vmem_size;
837   vl.values_len = 1;
838   plugin_dispatch_values(&vl);
839 
840   sstrncpy(vl.type, "ps_rss", sizeof(vl.type));
841   vl.values[0].gauge = ps->vmem_rss;
842   vl.values_len = 1;
843   plugin_dispatch_values(&vl);
844 
845   sstrncpy(vl.type, "ps_data", sizeof(vl.type));
846   vl.values[0].gauge = ps->vmem_data;
847   vl.values_len = 1;
848   plugin_dispatch_values(&vl);
849 
850   sstrncpy(vl.type, "ps_code", sizeof(vl.type));
851   vl.values[0].gauge = ps->vmem_code;
852   vl.values_len = 1;
853   plugin_dispatch_values(&vl);
854 
855   sstrncpy(vl.type, "ps_stacksize", sizeof(vl.type));
856   vl.values[0].gauge = ps->stack_size;
857   vl.values_len = 1;
858   plugin_dispatch_values(&vl);
859 
860   sstrncpy(vl.type, "ps_cputime", sizeof(vl.type));
861   vl.values[0].derive = ps->cpu_user_counter;
862   vl.values[1].derive = ps->cpu_system_counter;
863   vl.values_len = 2;
864   plugin_dispatch_values(&vl);
865 
866   sstrncpy(vl.type, "ps_count", sizeof(vl.type));
867   vl.values[0].gauge = ps->num_proc;
868   vl.values[1].gauge = ps->num_lwp;
869   vl.values_len = 2;
870   plugin_dispatch_values(&vl);
871 
872   sstrncpy(vl.type, "ps_pagefaults", sizeof(vl.type));
873   vl.values[0].derive = ps->vmem_minflt_counter;
874   vl.values[1].derive = ps->vmem_majflt_counter;
875   vl.values_len = 2;
876   plugin_dispatch_values(&vl);
877 
878   if ((ps->io_rchar != -1) && (ps->io_wchar != -1)) {
879     sstrncpy(vl.type, "io_octets", sizeof(vl.type));
880     vl.values[0].derive = ps->io_rchar;
881     vl.values[1].derive = ps->io_wchar;
882     vl.values_len = 2;
883     plugin_dispatch_values(&vl);
884   }
885 
886   if ((ps->io_syscr != -1) && (ps->io_syscw != -1)) {
887     sstrncpy(vl.type, "io_ops", sizeof(vl.type));
888     vl.values[0].derive = ps->io_syscr;
889     vl.values[1].derive = ps->io_syscw;
890     vl.values_len = 2;
891     plugin_dispatch_values(&vl);
892   }
893 
894   if ((ps->io_diskr != -1) && (ps->io_diskw != -1)) {
895     sstrncpy(vl.type, "disk_octets", sizeof(vl.type));
896     vl.values[0].derive = ps->io_diskr;
897     vl.values[1].derive = ps->io_diskw;
898     vl.values_len = 2;
899     plugin_dispatch_values(&vl);
900   }
901 
902   if (ps->num_fd > 0) {
903     sstrncpy(vl.type, "file_handles", sizeof(vl.type));
904     vl.values[0].gauge = ps->num_fd;
905     vl.values_len = 1;
906     plugin_dispatch_values(&vl);
907   }
908 
909   if (ps->num_maps > 0) {
910     sstrncpy(vl.type, "file_handles", sizeof(vl.type));
911     sstrncpy(vl.type_instance, "mapped", sizeof(vl.type_instance));
912     vl.values[0].gauge = ps->num_maps;
913     vl.values_len = 1;
914     plugin_dispatch_values(&vl);
915   }
916 
917   if ((ps->cswitch_vol != -1) && (ps->cswitch_invol != -1)) {
918     sstrncpy(vl.type, "contextswitch", sizeof(vl.type));
919     sstrncpy(vl.type_instance, "voluntary", sizeof(vl.type_instance));
920     vl.values[0].derive = ps->cswitch_vol;
921     vl.values_len = 1;
922     plugin_dispatch_values(&vl);
923 
924     sstrncpy(vl.type, "contextswitch", sizeof(vl.type));
925     sstrncpy(vl.type_instance, "involuntary", sizeof(vl.type_instance));
926     vl.values[0].derive = ps->cswitch_invol;
927     vl.values_len = 1;
928     plugin_dispatch_values(&vl);
929   }
930 
931   /* The ps->delay_* metrics are in nanoseconds per second. Convert to seconds
932    * per second. */
933   gauge_t const delay_factor = 1000000000.0;
934 
935   struct {
936     const char *type_instance;
937     gauge_t rate_ns;
938   } delay_metrics[] = {
939       {"delay-cpu", ps->delay_cpu},
940       {"delay-blkio", ps->delay_blkio},
941       {"delay-swapin", ps->delay_swapin},
942       {"delay-freepages", ps->delay_freepages},
943   };
944   for (size_t i = 0; i < STATIC_ARRAY_SIZE(delay_metrics); i++) {
945     if (isnan(delay_metrics[i].rate_ns)) {
946       continue;
947     }
948     sstrncpy(vl.type, "delay_rate", sizeof(vl.type));
949     sstrncpy(vl.type_instance, delay_metrics[i].type_instance,
950              sizeof(vl.type_instance));
951     vl.values[0].gauge = delay_metrics[i].rate_ns / delay_factor;
952     vl.values_len = 1;
953     plugin_dispatch_values(&vl);
954   }
955 
956   DEBUG(
957       "name = %s; num_proc = %lu; num_lwp = %lu; num_fd = %lu; num_maps = %lu; "
958       "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
959       "vmem_code = %lu; "
960       "vmem_minflt_counter = %" PRIi64 "; vmem_majflt_counter = %" PRIi64 "; "
961       "cpu_user_counter = %" PRIi64 "; cpu_system_counter = %" PRIi64 "; "
962       "io_rchar = %" PRIi64 "; io_wchar = %" PRIi64 "; "
963       "io_syscr = %" PRIi64 "; io_syscw = %" PRIi64 "; "
964       "io_diskr = %" PRIi64 "; io_diskw = %" PRIi64 "; "
965       "cswitch_vol = %" PRIi64 "; cswitch_invol = %" PRIi64 "; "
966       "delay_cpu = %g; delay_blkio = %g; "
967       "delay_swapin = %g; delay_freepages = %g;",
968       ps->name, ps->num_proc, ps->num_lwp, ps->num_fd, ps->num_maps,
969       ps->vmem_size, ps->vmem_rss, ps->vmem_data, ps->vmem_code,
970       ps->vmem_minflt_counter, ps->vmem_majflt_counter, ps->cpu_user_counter,
971       ps->cpu_system_counter, ps->io_rchar, ps->io_wchar, ps->io_syscr,
972       ps->io_syscw, ps->io_diskr, ps->io_diskw, ps->cswitch_vol,
973       ps->cswitch_invol, ps->delay_cpu, ps->delay_blkio, ps->delay_swapin,
974       ps->delay_freepages);
975 
976 } /* void ps_submit_proc_list */
977 
978 #if KERNEL_LINUX || KERNEL_SOLARIS
ps_submit_fork_rate(derive_t value)979 static void ps_submit_fork_rate(derive_t value) {
980   value_list_t vl = VALUE_LIST_INIT;
981 
982   vl.values = &(value_t){.derive = value};
983   vl.values_len = 1;
984   sstrncpy(vl.plugin, "processes", sizeof(vl.plugin));
985   sstrncpy(vl.plugin_instance, "", sizeof(vl.plugin_instance));
986   sstrncpy(vl.type, "fork_rate", sizeof(vl.type));
987   sstrncpy(vl.type_instance, "", sizeof(vl.type_instance));
988 
989   plugin_dispatch_values(&vl);
990 }
991 #endif /* KERNEL_LINUX || KERNEL_SOLARIS*/
992 
993 /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
994 #if KERNEL_LINUX
ps_read_tasks_status(process_entry_t * ps)995 static int ps_read_tasks_status(process_entry_t *ps) {
996   char dirname[64];
997   DIR *dh;
998   char filename[64];
999   FILE *fh;
1000   struct dirent *ent;
1001   derive_t cswitch_vol = 0;
1002   derive_t cswitch_invol = 0;
1003   char buffer[1024];
1004   char *fields[8];
1005   int numfields;
1006 
1007   snprintf(dirname, sizeof(dirname), "/proc/%li/task", ps->id);
1008 
1009   if ((dh = opendir(dirname)) == NULL) {
1010     DEBUG("Failed to open directory `%s'", dirname);
1011     return -1;
1012   }
1013 
1014   while ((ent = readdir(dh)) != NULL) {
1015     char *tpid;
1016 
1017     if (!isdigit((int)ent->d_name[0]))
1018       continue;
1019 
1020     tpid = ent->d_name;
1021 
1022     int r = snprintf(filename, sizeof(filename), "/proc/%li/task/%s/status",
1023                      ps->id, tpid);
1024     if ((size_t)r >= sizeof(filename)) {
1025       DEBUG("Filename too long: `%s'", filename);
1026       continue;
1027     }
1028 
1029     if ((fh = fopen(filename, "r")) == NULL) {
1030       DEBUG("Failed to open file `%s'", filename);
1031       continue;
1032     }
1033 
1034     while (fgets(buffer, sizeof(buffer), fh) != NULL) {
1035       derive_t tmp;
1036       char *endptr;
1037 
1038       if (strncmp(buffer, "voluntary_ctxt_switches", 23) != 0 &&
1039           strncmp(buffer, "nonvoluntary_ctxt_switches", 26) != 0)
1040         continue;
1041 
1042       numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
1043 
1044       if (numfields < 2)
1045         continue;
1046 
1047       errno = 0;
1048       endptr = NULL;
1049       tmp = (derive_t)strtoll(fields[1], &endptr, /* base = */ 10);
1050       if ((errno == 0) && (endptr != fields[1])) {
1051         if (strncmp(buffer, "voluntary_ctxt_switches", 23) == 0) {
1052           cswitch_vol += tmp;
1053         } else if (strncmp(buffer, "nonvoluntary_ctxt_switches", 26) == 0) {
1054           cswitch_invol += tmp;
1055         }
1056       }
1057     } /* while (fgets) */
1058 
1059     if (fclose(fh)) {
1060       WARNING("processes: fclose: %s", STRERRNO);
1061     }
1062   }
1063   closedir(dh);
1064 
1065   ps->cswitch_vol = cswitch_vol;
1066   ps->cswitch_invol = cswitch_invol;
1067 
1068   return 0;
1069 } /* int *ps_read_tasks_status */
1070 
1071 /* Read data from /proc/pid/status */
ps_read_status(long pid,process_entry_t * ps)1072 static int ps_read_status(long pid, process_entry_t *ps) {
1073   FILE *fh;
1074   char buffer[1024];
1075   char filename[64];
1076   unsigned long lib = 0;
1077   unsigned long exe = 0;
1078   unsigned long data = 0;
1079   unsigned long threads = 0;
1080   char *fields[8];
1081   int numfields;
1082 
1083   snprintf(filename, sizeof(filename), "/proc/%li/status", pid);
1084   if ((fh = fopen(filename, "r")) == NULL)
1085     return -1;
1086 
1087   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
1088     unsigned long tmp;
1089     char *endptr;
1090 
1091     if (strncmp(buffer, "Vm", 2) != 0 && strncmp(buffer, "Threads", 7) != 0)
1092       continue;
1093 
1094     numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
1095 
1096     if (numfields < 2)
1097       continue;
1098 
1099     errno = 0;
1100     endptr = NULL;
1101     tmp = strtoul(fields[1], &endptr, /* base = */ 10);
1102     if ((errno == 0) && (endptr != fields[1])) {
1103       if (strncmp(buffer, "VmData", 6) == 0) {
1104         data = tmp;
1105       } else if (strncmp(buffer, "VmLib", 5) == 0) {
1106         lib = tmp;
1107       } else if (strncmp(buffer, "VmExe", 5) == 0) {
1108         exe = tmp;
1109       } else if (strncmp(buffer, "Threads", 7) == 0) {
1110         threads = tmp;
1111       }
1112     }
1113   } /* while (fgets) */
1114 
1115   if (fclose(fh)) {
1116     WARNING("processes: fclose: %s", STRERRNO);
1117   }
1118 
1119   ps->vmem_data = data * 1024;
1120   ps->vmem_code = (exe + lib) * 1024;
1121   if (threads != 0)
1122     ps->num_lwp = threads;
1123 
1124   return 0;
1125 } /* int *ps_read_status */
1126 
ps_read_io(process_entry_t * ps)1127 static int ps_read_io(process_entry_t *ps) {
1128   FILE *fh;
1129   char buffer[1024];
1130   char filename[64];
1131 
1132   char *fields[8];
1133   int numfields;
1134 
1135   snprintf(filename, sizeof(filename), "/proc/%li/io", ps->id);
1136   if ((fh = fopen(filename, "r")) == NULL) {
1137     DEBUG("ps_read_io: Failed to open file `%s'", filename);
1138     return -1;
1139   }
1140 
1141   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
1142     derive_t *val = NULL;
1143     long long tmp;
1144     char *endptr;
1145 
1146     if (strncasecmp(buffer, "rchar:", 6) == 0)
1147       val = &(ps->io_rchar);
1148     else if (strncasecmp(buffer, "wchar:", 6) == 0)
1149       val = &(ps->io_wchar);
1150     else if (strncasecmp(buffer, "syscr:", 6) == 0)
1151       val = &(ps->io_syscr);
1152     else if (strncasecmp(buffer, "syscw:", 6) == 0)
1153       val = &(ps->io_syscw);
1154     else if (strncasecmp(buffer, "read_bytes:", 11) == 0)
1155       val = &(ps->io_diskr);
1156     else if (strncasecmp(buffer, "write_bytes:", 12) == 0)
1157       val = &(ps->io_diskw);
1158     else
1159       continue;
1160 
1161     numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
1162 
1163     if (numfields < 2)
1164       continue;
1165 
1166     errno = 0;
1167     endptr = NULL;
1168     tmp = strtoll(fields[1], &endptr, /* base = */ 10);
1169     if ((errno != 0) || (endptr == fields[1]))
1170       *val = -1;
1171     else
1172       *val = (derive_t)tmp;
1173   } /* while (fgets) */
1174 
1175   if (fclose(fh)) {
1176     WARNING("processes: fclose: %s", STRERRNO);
1177   }
1178   return 0;
1179 } /* int ps_read_io (...) */
1180 
ps_count_maps(pid_t pid)1181 static int ps_count_maps(pid_t pid) {
1182   FILE *fh;
1183   char buffer[1024];
1184   char filename[64];
1185   int count = 0;
1186 
1187   snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
1188   if ((fh = fopen(filename, "r")) == NULL) {
1189     DEBUG("ps_count_maps: Failed to open file `%s'", filename);
1190     return -1;
1191   }
1192 
1193   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
1194     if (strchr(buffer, '\n')) {
1195       count++;
1196     }
1197   } /* while (fgets) */
1198 
1199   if (fclose(fh)) {
1200     WARNING("processes: fclose: %s", STRERRNO);
1201   }
1202   return count;
1203 } /* int ps_count_maps (...) */
1204 
ps_count_fd(int pid)1205 static int ps_count_fd(int pid) {
1206   char dirname[64];
1207   DIR *dh;
1208   struct dirent *ent;
1209   int count = 0;
1210 
1211   snprintf(dirname, sizeof(dirname), "/proc/%i/fd", pid);
1212 
1213   if ((dh = opendir(dirname)) == NULL) {
1214     DEBUG("Failed to open directory `%s'", dirname);
1215     return -1;
1216   }
1217   while ((ent = readdir(dh)) != NULL) {
1218     if (!isdigit((int)ent->d_name[0]))
1219       continue;
1220     else
1221       count++;
1222   }
1223   closedir(dh);
1224 
1225   return (count >= 1) ? count : 1;
1226 } /* int ps_count_fd (pid) */
1227 
1228 #if HAVE_LIBTASKSTATS
ps_delay(process_entry_t * ps)1229 static int ps_delay(process_entry_t *ps) {
1230   if (taskstats_handle == NULL) {
1231     return ENOTCONN;
1232   }
1233 
1234   int status = ts_delay_by_tgid(taskstats_handle, (uint32_t)ps->id, &ps->delay);
1235   if (status == EPERM) {
1236     static c_complain_t c;
1237 #if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_NET_ADMIN)
1238     if (check_capability(CAP_NET_ADMIN) != 0) {
1239       if (getuid() == 0) {
1240         c_complain(
1241             LOG_ERR, &c,
1242             "processes plugin: Reading Delay Accounting metric failed: %s. "
1243             "collectd is running as root, but missing the CAP_NET_ADMIN "
1244             "capability. The most common cause for this is that the init "
1245             "system is dropping capabilities.",
1246             STRERROR(status));
1247       } else {
1248         c_complain(
1249             LOG_ERR, &c,
1250             "processes plugin: Reading Delay Accounting metric failed: %s. "
1251             "collectd is not running as root and missing the CAP_NET_ADMIN "
1252             "capability. Either run collectd as root or grant it the "
1253             "CAP_NET_ADMIN capability using \"setcap cap_net_admin=ep " PREFIX
1254             "/sbin/collectd\".",
1255             STRERROR(status));
1256       }
1257     } else {
1258       ERROR("processes plugin: ts_delay_by_tgid failed: %s. The CAP_NET_ADMIN "
1259             "capability is available (I checked), so this error is utterly "
1260             "unexpected.",
1261             STRERROR(status));
1262     }
1263 #else
1264     c_complain(LOG_ERR, &c,
1265                "processes plugin: Reading Delay Accounting metric failed: %s. "
1266                "Reading Delay Accounting metrics requires root privileges.",
1267                STRERROR(status));
1268 #endif
1269     return status;
1270   } else if (status != 0) {
1271     ERROR("processes plugin: ts_delay_by_tgid failed: %s", STRERROR(status));
1272     return status;
1273   }
1274 
1275   return 0;
1276 }
1277 #endif
1278 
ps_fill_details(const collectd_procstat_t * ps,process_entry_t * entry)1279 static void ps_fill_details(const collectd_procstat_t *ps, process_entry_t *entry) {
1280   if (entry->has_io == false) {
1281     ps_read_io(entry);
1282     entry->has_io = true;
1283   }
1284 
1285   if (ps->report_ctx_switch) {
1286     if (entry->has_cswitch == false) {
1287       ps_read_tasks_status(entry);
1288       entry->has_cswitch = true;
1289     }
1290   }
1291 
1292   if (ps->report_maps_num) {
1293     int num_maps;
1294     if (entry->has_maps == false && (num_maps = ps_count_maps(entry->id)) > 0) {
1295       entry->num_maps = num_maps;
1296     }
1297     entry->has_maps = true;
1298   }
1299 
1300   if (ps->report_fd_num) {
1301     int num_fd;
1302     if (entry->has_fd == false && (num_fd = ps_count_fd(entry->id)) > 0) {
1303       entry->num_fd = num_fd;
1304     }
1305     entry->has_fd = true;
1306   }
1307 
1308 #if HAVE_LIBTASKSTATS
1309   if (ps->report_delay && !entry->has_delay) {
1310     if (ps_delay(entry) == 0) {
1311       entry->has_delay = true;
1312     }
1313   }
1314 #endif
1315 } /* void ps_fill_details (...) */
1316 
1317 /* ps_read_process reads process counters on Linux. */
ps_read_process(long pid,process_entry_t * ps,char * state)1318 static int ps_read_process(long pid, process_entry_t *ps, char *state) {
1319   char filename[64];
1320   char buffer[1024];
1321 
1322   char *fields[64];
1323   char fields_len;
1324 
1325   size_t buffer_len;
1326 
1327   char *buffer_ptr;
1328   size_t name_start_pos;
1329   size_t name_end_pos;
1330   size_t name_len;
1331 
1332   derive_t cpu_user_counter;
1333   derive_t cpu_system_counter;
1334   long long unsigned vmem_size;
1335   long long unsigned vmem_rss;
1336   long long unsigned stack_size;
1337 
1338   ssize_t status;
1339 
1340   snprintf(filename, sizeof(filename), "/proc/%li/stat", pid);
1341 
1342   status = read_text_file_contents(filename, buffer, sizeof(buffer));
1343   if (status <= 0)
1344     return -1;
1345   buffer_len = (size_t)status;
1346 
1347   /* The name of the process is enclosed in parens. Since the name can
1348    * contain parens itself, spaces, numbers and pretty much everything
1349    * else, use these to determine the process name. We don't use
1350    * strchr(3) and strrchr(3) to avoid pointer arithmetic which would
1351    * otherwise be required to determine name_len. */
1352   name_start_pos = 0;
1353   while (name_start_pos < buffer_len && buffer[name_start_pos] != '(')
1354     name_start_pos++;
1355 
1356   name_end_pos = buffer_len;
1357   while (name_end_pos > 0 && buffer[name_end_pos] != ')')
1358     name_end_pos--;
1359 
1360   /* Either '(' or ')' is not found or they are in the wrong order.
1361    * Anyway, something weird that shouldn't happen ever. */
1362   if (name_start_pos >= name_end_pos) {
1363     ERROR("processes plugin: name_start_pos = %" PRIsz
1364           " >= name_end_pos = %" PRIsz,
1365           name_start_pos, name_end_pos);
1366     return -1;
1367   }
1368 
1369   name_len = (name_end_pos - name_start_pos) - 1;
1370   if (name_len >= sizeof(ps->name))
1371     name_len = sizeof(ps->name) - 1;
1372 
1373   sstrncpy(ps->name, &buffer[name_start_pos + 1], name_len + 1);
1374 
1375   if ((buffer_len - name_end_pos) < 2)
1376     return -1;
1377   buffer_ptr = &buffer[name_end_pos + 2];
1378 
1379   fields_len = strsplit(buffer_ptr, fields, STATIC_ARRAY_SIZE(fields));
1380   if (fields_len < 22) {
1381     DEBUG("processes plugin: ps_read_process (pid = %li):"
1382           " `%s' has only %i fields..",
1383           pid, filename, fields_len);
1384     return -1;
1385   }
1386 
1387   *state = fields[0][0];
1388 
1389   if (*state == 'Z') {
1390     ps->num_lwp = 0;
1391     ps->num_proc = 0;
1392   } else {
1393     ps->num_lwp = strtoul(fields[17], /* endptr = */ NULL, /* base = */ 10);
1394     if ((ps_read_status(pid, ps)) != 0) {
1395       /* No VMem data */
1396       ps->vmem_data = -1;
1397       ps->vmem_code = -1;
1398       DEBUG("ps_read_process: did not get vmem data for pid %li", pid);
1399     }
1400     if (ps->num_lwp == 0)
1401       ps->num_lwp = 1;
1402     ps->num_proc = 1;
1403   }
1404 
1405   /* Leave the rest at zero if this is only a zombi */
1406   if (ps->num_proc == 0) {
1407     DEBUG("processes plugin: This is only a zombie: pid = %li; "
1408           "name = %s;",
1409           pid, ps->name);
1410     return 0;
1411   }
1412 
1413   cpu_user_counter = atoll(fields[11]);
1414   cpu_system_counter = atoll(fields[12]);
1415   vmem_size = atoll(fields[20]);
1416   vmem_rss = atoll(fields[21]);
1417   ps->vmem_minflt_counter = atol(fields[7]);
1418   ps->vmem_majflt_counter = atol(fields[9]);
1419 
1420   {
1421     unsigned long long stack_start = atoll(fields[25]);
1422     unsigned long long stack_ptr = atoll(fields[26]);
1423 
1424     stack_size = (stack_start > stack_ptr) ? stack_start - stack_ptr
1425                                            : stack_ptr - stack_start;
1426   }
1427 
1428   /* Convert jiffies to useconds */
1429   cpu_user_counter = cpu_user_counter * 1000000 / CONFIG_HZ;
1430   cpu_system_counter = cpu_system_counter * 1000000 / CONFIG_HZ;
1431   vmem_rss = vmem_rss * pagesize_g;
1432 
1433   ps->cpu_user_counter = cpu_user_counter;
1434   ps->cpu_system_counter = cpu_system_counter;
1435   ps->vmem_size = (unsigned long)vmem_size;
1436   ps->vmem_rss = (unsigned long)vmem_rss;
1437   ps->stack_size = (unsigned long)stack_size;
1438 
1439   /* no data by default. May be filled by ps_fill_details () */
1440   ps->io_rchar = -1;
1441   ps->io_wchar = -1;
1442   ps->io_syscr = -1;
1443   ps->io_syscw = -1;
1444   ps->io_diskr = -1;
1445   ps->io_diskw = -1;
1446 
1447   ps->cswitch_vol = -1;
1448   ps->cswitch_invol = -1;
1449 
1450   /* success */
1451   return 0;
1452 } /* int ps_read_process (...) */
1453 
procs_running(void)1454 static int procs_running(void) {
1455   char buffer[65536] = {};
1456   char id[] = "procs_running "; /* white space terminated */
1457   char *running;
1458   char *endptr = NULL;
1459   long result = 0L;
1460 
1461   ssize_t status;
1462 
1463   status = read_file_contents("/proc/stat", buffer, sizeof(buffer) - 1);
1464   if (status <= 0) {
1465     return -1;
1466   }
1467 
1468   /* the data contains :
1469    * the literal string 'procs_running',
1470    * a whitespace
1471    * the number of running processes.
1472    * The parser does include the white-space character.
1473    */
1474   running = strstr(buffer, id);
1475   if (!running) {
1476     WARNING("procs_running not found");
1477     return -1;
1478   }
1479   running += strlen(id);
1480 
1481   result = strtol(running, &endptr, 10);
1482   if ((*running != '\0') && ((*endptr == '\0') || (*endptr == '\n'))) {
1483     return (int)result;
1484   }
1485 
1486   return -1;
1487 }
1488 
ps_get_cmdline(long pid,char * name,char * buf,size_t buf_len)1489 static char *ps_get_cmdline(long pid, char *name, char *buf, size_t buf_len) {
1490   char *buf_ptr;
1491   size_t len;
1492 
1493   char file[PATH_MAX];
1494   int fd;
1495 
1496   size_t n;
1497 
1498   if ((pid < 1) || (NULL == buf) || (buf_len < 2))
1499     return NULL;
1500 
1501   snprintf(file, sizeof(file), "/proc/%li/cmdline", pid);
1502 
1503   errno = 0;
1504   fd = open(file, O_RDONLY);
1505   if (fd < 0) {
1506     /* ENOENT means the process exited while we were handling it.
1507      * Don't complain about this, it only fills the logs. */
1508     if (errno != ENOENT)
1509       WARNING("processes plugin: Failed to open `%s': %s.", file, STRERRNO);
1510     return NULL;
1511   }
1512 
1513   buf_ptr = buf;
1514   len = buf_len;
1515 
1516   n = 0;
1517 
1518   while (42) {
1519     ssize_t status;
1520 
1521     status = read(fd, (void *)buf_ptr, len);
1522 
1523     if (status < 0) {
1524 
1525       if ((EAGAIN == errno) || (EINTR == errno))
1526         continue;
1527 
1528       WARNING("processes plugin: Failed to read from `%s': %s.", file,
1529               STRERRNO);
1530       close(fd);
1531       return NULL;
1532     }
1533 
1534     n += status;
1535 
1536     if (status == 0)
1537       break;
1538 
1539     buf_ptr += status;
1540     len -= status;
1541 
1542     if (len == 0)
1543       break;
1544   }
1545 
1546   close(fd);
1547 
1548   if (0 == n) {
1549     /* cmdline not available; e.g. kernel thread, zombie */
1550     if (NULL == name)
1551       return NULL;
1552 
1553     snprintf(buf, buf_len, "[%s]", name);
1554     return buf;
1555   }
1556 
1557   assert(n <= buf_len);
1558 
1559   if (n == buf_len)
1560     --n;
1561   buf[n] = '\0';
1562 
1563   --n;
1564   /* remove trailing whitespace */
1565   while ((n > 0) && (isspace(buf[n]) || ('\0' == buf[n]))) {
1566     buf[n] = '\0';
1567     --n;
1568   }
1569 
1570   /* arguments are separated by '\0' in /proc/<pid>/cmdline */
1571   while (n > 0) {
1572     if ('\0' == buf[n])
1573       buf[n] = ' ';
1574     --n;
1575   }
1576   return buf;
1577 } /* char *ps_get_cmdline (...) */
1578 
read_fork_rate(void)1579 static int read_fork_rate(void) {
1580   FILE *proc_stat;
1581   char buffer[1024];
1582   value_t value;
1583   bool value_valid = 0;
1584 
1585   proc_stat = fopen("/proc/stat", "r");
1586   if (proc_stat == NULL) {
1587     ERROR("processes plugin: fopen (/proc/stat) failed: %s", STRERRNO);
1588     return -1;
1589   }
1590 
1591   while (fgets(buffer, sizeof(buffer), proc_stat) != NULL) {
1592     int status;
1593     char *fields[3];
1594     int fields_num;
1595 
1596     fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
1597     if (fields_num != 2)
1598       continue;
1599 
1600     if (strcmp("processes", fields[0]) != 0)
1601       continue;
1602 
1603     status = parse_value(fields[1], &value, DS_TYPE_DERIVE);
1604     if (status == 0)
1605       value_valid = 1;
1606 
1607     break;
1608   }
1609   fclose(proc_stat);
1610 
1611   if (!value_valid)
1612     return -1;
1613 
1614   ps_submit_fork_rate(value.derive);
1615   return 0;
1616 }
1617 #endif /*KERNEL_LINUX */
1618 
1619 #if KERNEL_SOLARIS
ps_get_cmdline(long pid,char * name,char * buffer,size_t buffer_size)1620 static char *ps_get_cmdline(long pid,
1621                             char *name __attribute__((unused)), /* {{{ */
1622                             char *buffer, size_t buffer_size) {
1623   char path[PATH_MAX];
1624   psinfo_t info;
1625   ssize_t status;
1626 
1627   snprintf(path, sizeof(path), "/proc/%li/psinfo", pid);
1628 
1629   status = read_file_contents(path, &info, sizeof(info));
1630   if ((status < 0) || (((size_t)status) != sizeof(info))) {
1631     ERROR("processes plugin: Unexpected return value "
1632           "while reading \"%s\": "
1633           "Returned %zd but expected %" PRIsz ".",
1634           path, status, buffer_size);
1635     return NULL;
1636   }
1637 
1638   sstrncpy(buffer, info.pr_psargs, buffer_size);
1639 
1640   return buffer;
1641 } /* }}} int ps_get_cmdline */
1642 
1643 /*
1644  * Reads process information on the Solaris OS. The information comes mainly
1645  * from
1646  * /proc/PID/status, /proc/PID/psinfo and /proc/PID/usage
1647  * The values for input and ouput chars are calculated "by hand"
1648  * Added a few "solaris" specific process states as well
1649  */
ps_read_process(long pid,process_entry_t * ps,char * state)1650 static int ps_read_process(long pid, process_entry_t *ps, char *state) {
1651   char filename[64];
1652   char f_psinfo[64], f_usage[64];
1653   char *buffer;
1654 
1655   pstatus_t *myStatus;
1656   psinfo_t *myInfo;
1657   prusage_t *myUsage;
1658 
1659   snprintf(filename, sizeof(filename), "/proc/%li/status", pid);
1660   snprintf(f_psinfo, sizeof(f_psinfo), "/proc/%li/psinfo", pid);
1661   snprintf(f_usage, sizeof(f_usage), "/proc/%li/usage", pid);
1662 
1663   buffer = calloc(1, sizeof(pstatus_t));
1664   read_file_contents(filename, buffer, sizeof(pstatus_t));
1665   myStatus = (pstatus_t *)buffer;
1666 
1667   buffer = calloc(1, sizeof(psinfo_t));
1668   read_file_contents(f_psinfo, buffer, sizeof(psinfo_t));
1669   myInfo = (psinfo_t *)buffer;
1670 
1671   buffer = calloc(1, sizeof(prusage_t));
1672   read_file_contents(f_usage, buffer, sizeof(prusage_t));
1673   myUsage = (prusage_t *)buffer;
1674 
1675   sstrncpy(ps->name, myInfo->pr_fname, sizeof(myInfo->pr_fname));
1676   ps->num_lwp = myStatus->pr_nlwp;
1677   if (myInfo->pr_wstat != 0) {
1678     ps->num_proc = 0;
1679     ps->num_lwp = 0;
1680     *state = (char)'Z';
1681 
1682     sfree(myStatus);
1683     sfree(myInfo);
1684     sfree(myUsage);
1685     return 0;
1686   } else {
1687     ps->num_proc = 1;
1688     ps->num_lwp = myInfo->pr_nlwp;
1689   }
1690 
1691   /*
1692    * Convert system time and user time from nanoseconds to microseconds
1693    * for compatibility with the linux module
1694    */
1695   ps->cpu_system_counter = myStatus->pr_stime.tv_nsec / 1000;
1696   ps->cpu_user_counter = myStatus->pr_utime.tv_nsec / 1000;
1697 
1698   /*
1699    * Convert rssize from KB to bytes to be consistent w/ the linux module
1700    */
1701   ps->vmem_rss = myInfo->pr_rssize * 1024;
1702   ps->vmem_size = myInfo->pr_size * 1024;
1703   ps->vmem_minflt_counter = myUsage->pr_minf;
1704   ps->vmem_majflt_counter = myUsage->pr_majf;
1705 
1706   /*
1707    * TODO: Data and code segment calculations for Solaris
1708    */
1709 
1710   ps->vmem_data = -1;
1711   ps->vmem_code = -1;
1712   ps->stack_size = myStatus->pr_stksize;
1713 
1714   /*
1715    * TODO: File descriptor count for Solaris
1716    */
1717   ps->num_fd = 0;
1718 
1719   /* Number of memory mappings */
1720   ps->num_maps = 0;
1721 
1722   /*
1723    * Calculating input/ouput chars
1724    * Formula used is total chars / total blocks => chars/block
1725    * then convert input/output blocks to chars
1726    */
1727   ulong_t tot_chars = myUsage->pr_ioch;
1728   ulong_t tot_blocks = myUsage->pr_inblk + myUsage->pr_oublk;
1729   ulong_t chars_per_block = 1;
1730   if (tot_blocks != 0)
1731     chars_per_block = tot_chars / tot_blocks;
1732   ps->io_rchar = myUsage->pr_inblk * chars_per_block;
1733   ps->io_wchar = myUsage->pr_oublk * chars_per_block;
1734   ps->io_syscr = myUsage->pr_sysc;
1735   ps->io_syscw = myUsage->pr_sysc;
1736   ps->io_diskr = -1;
1737   ps->io_diskw = -1;
1738 
1739   /*
1740    * TODO: context switch counters for Solaris
1741    */
1742   ps->cswitch_vol = -1;
1743   ps->cswitch_invol = -1;
1744 
1745   /*
1746    * TODO: Find way of setting BLOCKED and PAGING status
1747    */
1748 
1749   *state = (char)'R';
1750   if (myStatus->pr_flags & PR_ASLEEP)
1751     *state = (char)'S';
1752   else if (myStatus->pr_flags & PR_STOPPED)
1753     *state = (char)'T';
1754   else if (myStatus->pr_flags & PR_DETACH)
1755     *state = (char)'E';
1756   else if (myStatus->pr_flags & PR_DAEMON)
1757     *state = (char)'A';
1758   else if (myStatus->pr_flags & PR_ISSYS)
1759     *state = (char)'Y';
1760   else if (myStatus->pr_flags & PR_ORPHAN)
1761     *state = (char)'O';
1762 
1763   sfree(myStatus);
1764   sfree(myInfo);
1765   sfree(myUsage);
1766 
1767   return 0;
1768 }
1769 
1770 /*
1771  * Reads the number of threads created since the last reboot. On Solaris these
1772  * are retrieved from kstat (module cpu, name sys, class misc, stat nthreads).
1773  * The result is the sum for all the threads created on each cpu
1774  */
read_fork_rate(void)1775 static int read_fork_rate(void) {
1776   extern kstat_ctl_t *kc;
1777   derive_t result = 0;
1778 
1779   if (kc == NULL)
1780     return -1;
1781 
1782   for (kstat_t *ksp_chain = kc->kc_chain; ksp_chain != NULL;
1783        ksp_chain = ksp_chain->ks_next) {
1784     if ((strcmp(ksp_chain->ks_module, "cpu") == 0) &&
1785         (strcmp(ksp_chain->ks_name, "sys") == 0) &&
1786         (strcmp(ksp_chain->ks_class, "misc") == 0)) {
1787       long long tmp;
1788 
1789       kstat_read(kc, ksp_chain, NULL);
1790 
1791       tmp = get_kstat_value(ksp_chain, "nthreads");
1792       if (tmp != -1LL)
1793         result += tmp;
1794     }
1795   }
1796 
1797   ps_submit_fork_rate(result);
1798   return 0;
1799 }
1800 #endif /* KERNEL_SOLARIS */
1801 
1802 #if HAVE_THREAD_INFO
mach_get_task_name(task_t t,int * pid,char * name,size_t name_max_len)1803 static int mach_get_task_name(task_t t, int *pid, char *name,
1804                               size_t name_max_len) {
1805   int mib[4];
1806 
1807   struct kinfo_proc kp;
1808   size_t kp_size;
1809 
1810   mib[0] = CTL_KERN;
1811   mib[1] = KERN_PROC;
1812   mib[2] = KERN_PROC_PID;
1813 
1814   if (pid_for_task(t, pid) != KERN_SUCCESS)
1815     return -1;
1816   mib[3] = *pid;
1817 
1818   kp_size = sizeof(kp);
1819   if (sysctl(mib, 4, &kp, &kp_size, NULL, 0) != 0)
1820     return -1;
1821 
1822   if (name_max_len > (MAXCOMLEN + 1))
1823     name_max_len = MAXCOMLEN + 1;
1824 
1825   strncpy(name, kp.kp_proc.p_comm, name_max_len - 1);
1826   name[name_max_len - 1] = '\0';
1827 
1828   DEBUG("pid = %i; name = %s;", *pid, name);
1829 
1830   /* We don't do the special handling for `p_comm == "LaunchCFMApp"' as
1831    * `top' does it, because it is a lot of work and only used when
1832    * debugging. -octo */
1833 
1834   return 0;
1835 }
1836 #endif /* HAVE_THREAD_INFO */
1837 /* end of additional functions for KERNEL_LINUX/HAVE_THREAD_INFO */
1838 
1839 /* do actual readings from kernel */
ps_read(void)1840 static int ps_read(void) {
1841 #if HAVE_THREAD_INFO
1842   kern_return_t status;
1843 
1844   processor_set_t port_pset_priv;
1845 
1846   task_array_t task_list;
1847   mach_msg_type_number_t task_list_len;
1848 
1849   int task_pid;
1850   char task_name[MAXCOMLEN + 1];
1851 
1852   thread_act_array_t thread_list;
1853   mach_msg_type_number_t thread_list_len;
1854   thread_basic_info_data_t thread_data;
1855   mach_msg_type_number_t thread_data_len;
1856 
1857   int running = 0;
1858   int sleeping = 0;
1859   int zombies = 0;
1860   int stopped = 0;
1861   int blocked = 0;
1862 
1863   collectd_procstat_t *ps;
1864   process_entry_t pse;
1865 
1866   ps_list_reset();
1867 
1868   /*
1869    * The Mach-concept is a little different from the traditional UNIX
1870    * concept: All the work is done in threads. Threads are contained in
1871    * `tasks'. Therefore, `task status' doesn't make much sense, since
1872    * it's actually a `thread status'.
1873    * Tasks are assigned to sets of processors, so that's where you go to
1874    * get a list.
1875    */
1876   for (mach_msg_type_number_t pset = 0; pset < pset_list_len; pset++) {
1877     if ((status = host_processor_set_priv(port_host_self, pset_list[pset],
1878                                           &port_pset_priv)) != KERN_SUCCESS) {
1879       ERROR("host_processor_set_priv failed: %s\n", mach_error_string(status));
1880       continue;
1881     }
1882 
1883     if ((status = processor_set_tasks(port_pset_priv, &task_list,
1884                                       &task_list_len)) != KERN_SUCCESS) {
1885       ERROR("processor_set_tasks failed: %s\n", mach_error_string(status));
1886       mach_port_deallocate(port_task_self, port_pset_priv);
1887       continue;
1888     }
1889 
1890     for (mach_msg_type_number_t task = 0; task < task_list_len; task++) {
1891       ps = NULL;
1892       if (mach_get_task_name(task_list[task], &task_pid, task_name,
1893                              PROCSTAT_NAME_LEN) == 0) {
1894         /* search for at least one match */
1895         for (ps = list_head_g; ps != NULL; ps = ps->next)
1896           /* FIXME: cmdline should be here instead of NULL */
1897           if (ps_list_match(task_name, NULL, ps) == 1)
1898             break;
1899       }
1900 
1901       /* Collect more detailed statistics for this process */
1902       if (ps != NULL) {
1903         task_basic_info_data_t task_basic_info;
1904         mach_msg_type_number_t task_basic_info_len;
1905         task_events_info_data_t task_events_info;
1906         mach_msg_type_number_t task_events_info_len;
1907         task_absolutetime_info_data_t task_absolutetime_info;
1908         mach_msg_type_number_t task_absolutetime_info_len;
1909 
1910         memset(&pse, '\0', sizeof(pse));
1911         pse.id = task_pid;
1912 
1913         task_basic_info_len = TASK_BASIC_INFO_COUNT;
1914         status = task_info(task_list[task], TASK_BASIC_INFO,
1915                            (task_info_t)&task_basic_info, &task_basic_info_len);
1916         if (status != KERN_SUCCESS) {
1917           ERROR("task_info failed: %s", mach_error_string(status));
1918           continue; /* with next thread_list */
1919         }
1920 
1921         task_events_info_len = TASK_EVENTS_INFO_COUNT;
1922         status =
1923             task_info(task_list[task], TASK_EVENTS_INFO,
1924                       (task_info_t)&task_events_info, &task_events_info_len);
1925         if (status != KERN_SUCCESS) {
1926           ERROR("task_info failed: %s", mach_error_string(status));
1927           continue; /* with next thread_list */
1928         }
1929 
1930         task_absolutetime_info_len = TASK_ABSOLUTETIME_INFO_COUNT;
1931         status = task_info(task_list[task], TASK_ABSOLUTETIME_INFO,
1932                            (task_info_t)&task_absolutetime_info,
1933                            &task_absolutetime_info_len);
1934         if (status != KERN_SUCCESS) {
1935           ERROR("task_info failed: %s", mach_error_string(status));
1936           continue; /* with next thread_list */
1937         }
1938 
1939         pse.num_proc++;
1940         pse.vmem_size = task_basic_info.virtual_size;
1941         pse.vmem_rss = task_basic_info.resident_size;
1942         /* Does not seem to be easily exposed */
1943         pse.vmem_data = 0;
1944         pse.vmem_code = 0;
1945 
1946         pse.io_rchar = -1;
1947         pse.io_wchar = -1;
1948         pse.io_syscr = -1;
1949         pse.io_syscw = -1;
1950         pse.io_diskr = -1;
1951         pse.io_diskw = -1;
1952 
1953         /* File descriptor count not implemented */
1954         pse.num_fd = 0;
1955 
1956         /* Number of memory mappings */
1957         pse.num_maps = 0;
1958 
1959         pse.vmem_minflt_counter = task_events_info.cow_faults;
1960         pse.vmem_majflt_counter = task_events_info.faults;
1961 
1962         pse.cpu_user_counter = task_absolutetime_info.total_user;
1963         pse.cpu_system_counter = task_absolutetime_info.total_system;
1964 
1965         /* context switch counters not implemented */
1966         pse.cswitch_vol = -1;
1967         pse.cswitch_invol = -1;
1968       }
1969 
1970       status = task_threads(task_list[task], &thread_list, &thread_list_len);
1971       if (status != KERN_SUCCESS) {
1972         /* Apple's `top' treats this case a zombie. It
1973          * makes sense to some extend: A `zombie'
1974          * thread is nonsense, since the task/process
1975          * is dead. */
1976         zombies++;
1977         DEBUG("task_threads failed: %s", mach_error_string(status));
1978         if (task_list[task] != port_task_self)
1979           mach_port_deallocate(port_task_self, task_list[task]);
1980         continue; /* with next task_list */
1981       }
1982 
1983       for (mach_msg_type_number_t thread = 0; thread < thread_list_len;
1984            thread++) {
1985         thread_data_len = THREAD_BASIC_INFO_COUNT;
1986         status = thread_info(thread_list[thread], THREAD_BASIC_INFO,
1987                              (thread_info_t)&thread_data, &thread_data_len);
1988         if (status != KERN_SUCCESS) {
1989           ERROR("thread_info failed: %s", mach_error_string(status));
1990           if (task_list[task] != port_task_self)
1991             mach_port_deallocate(port_task_self, thread_list[thread]);
1992           continue; /* with next thread_list */
1993         }
1994 
1995         if (ps != NULL)
1996           pse.num_lwp++;
1997 
1998         switch (thread_data.run_state) {
1999         case TH_STATE_RUNNING:
2000           running++;
2001           break;
2002         case TH_STATE_STOPPED:
2003         /* What exactly is `halted'? */
2004         case TH_STATE_HALTED:
2005           stopped++;
2006           break;
2007         case TH_STATE_WAITING:
2008           sleeping++;
2009           break;
2010         case TH_STATE_UNINTERRUPTIBLE:
2011           blocked++;
2012           break;
2013         /* There is no `zombie' case here,
2014          * since there are no zombie-threads.
2015          * There's only zombie tasks, which are
2016          * handled above. */
2017         default:
2018           WARNING("Unknown thread status: %i", thread_data.run_state);
2019           break;
2020         } /* switch (thread_data.run_state) */
2021 
2022         if (task_list[task] != port_task_self) {
2023           status = mach_port_deallocate(port_task_self, thread_list[thread]);
2024           if (status != KERN_SUCCESS)
2025             ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
2026         }
2027       } /* for (thread_list) */
2028 
2029       if ((status = vm_deallocate(port_task_self, (vm_address_t)thread_list,
2030                                   thread_list_len * sizeof(thread_act_t))) !=
2031           KERN_SUCCESS) {
2032         ERROR("vm_deallocate failed: %s", mach_error_string(status));
2033       }
2034       thread_list = NULL;
2035       thread_list_len = 0;
2036 
2037       /* Only deallocate the task port, if it isn't our own.
2038        * Don't know what would happen in that case, but this
2039        * is what Apple's top does.. ;) */
2040       if (task_list[task] != port_task_self) {
2041         status = mach_port_deallocate(port_task_self, task_list[task]);
2042         if (status != KERN_SUCCESS)
2043           ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
2044       }
2045 
2046       if (ps != NULL)
2047         /* FIXME: cmdline should be here instead of NULL */
2048         ps_list_add(task_name, NULL, &pse);
2049     } /* for (task_list) */
2050 
2051     if ((status = vm_deallocate(port_task_self, (vm_address_t)task_list,
2052                                 task_list_len * sizeof(task_t))) !=
2053         KERN_SUCCESS) {
2054       ERROR("vm_deallocate failed: %s", mach_error_string(status));
2055     }
2056     task_list = NULL;
2057     task_list_len = 0;
2058 
2059     if ((status = mach_port_deallocate(port_task_self, port_pset_priv)) !=
2060         KERN_SUCCESS) {
2061       ERROR("mach_port_deallocate failed: %s", mach_error_string(status));
2062     }
2063   } /* for (pset_list) */
2064 
2065   ps_submit_state("running", running);
2066   ps_submit_state("sleeping", sleeping);
2067   ps_submit_state("zombies", zombies);
2068   ps_submit_state("stopped", stopped);
2069   ps_submit_state("blocked", blocked);
2070 
2071   for (ps = list_head_g; ps != NULL; ps = ps->next)
2072     ps_submit_proc_list(ps);
2073     /* #endif HAVE_THREAD_INFO */
2074 
2075 #elif KERNEL_LINUX
2076   int running = 0;
2077   int sleeping = 0;
2078   int zombies = 0;
2079   int stopped = 0;
2080   int paging = 0;
2081   int blocked = 0;
2082 
2083   struct dirent *ent;
2084   DIR *proc;
2085   long pid;
2086 
2087   char cmdline[CMDLINE_BUFFER_SIZE];
2088 
2089   int status;
2090   process_entry_t pse;
2091   char state;
2092 
2093   running = sleeping = zombies = stopped = paging = blocked = 0;
2094   ps_list_reset();
2095 
2096   if ((proc = opendir("/proc")) == NULL) {
2097     ERROR("Cannot open `/proc': %s", STRERRNO);
2098     return -1;
2099   }
2100 
2101   while ((ent = readdir(proc)) != NULL) {
2102     if (!isdigit(ent->d_name[0]))
2103       continue;
2104 
2105     if ((pid = atol(ent->d_name)) < 1)
2106       continue;
2107 
2108     memset(&pse, 0, sizeof(pse));
2109     pse.id = pid;
2110 
2111     status = ps_read_process(pid, &pse, &state);
2112     if (status != 0) {
2113       DEBUG("ps_read_process failed: %i", status);
2114       continue;
2115     }
2116 
2117     switch (state) {
2118     case 'R':
2119       running++;
2120       break;
2121     case 'S':
2122       sleeping++;
2123       break;
2124     case 'D':
2125       blocked++;
2126       break;
2127     case 'Z':
2128       zombies++;
2129       break;
2130     case 'T':
2131       stopped++;
2132       break;
2133     case 'W':
2134       paging++;
2135       break;
2136     }
2137 
2138     ps_list_add(pse.name,
2139                 ps_get_cmdline(pid, pse.name, cmdline, sizeof(cmdline)), &pse);
2140   }
2141 
2142   closedir(proc);
2143 
2144   /* get procs_running from /proc/stat
2145    * scanning /proc/stat AND computing other process stats takes too much time.
2146    * Consequently, the number of running processes based on the occurences
2147    * of 'R' as character indicating the running state is typically zero. Due
2148    * to processes are actually changing state during the evaluation of it's
2149    * stat(s).
2150    * The 'procs_running' number in /proc/stat on the other hand is more
2151    * accurate, and can be retrieved in a single 'read' call. */
2152   running = procs_running();
2153 
2154   ps_submit_state("running", running);
2155   ps_submit_state("sleeping", sleeping);
2156   ps_submit_state("zombies", zombies);
2157   ps_submit_state("stopped", stopped);
2158   ps_submit_state("paging", paging);
2159   ps_submit_state("blocked", blocked);
2160 
2161   for (collectd_procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
2162     ps_submit_proc_list(ps_ptr);
2163 
2164   read_fork_rate();
2165   /* #endif KERNEL_LINUX */
2166 
2167 #elif HAVE_LIBKVM_GETPROCS && (HAVE_STRUCT_KINFO_PROC_FREEBSD || HAVE_STRUCT_KINFO_PROC_DRAGONFLY)
2168   int running = 0;
2169   int sleeping = 0;
2170   int zombies = 0;
2171   int stopped = 0;
2172   int blocked = 0;
2173   int idle = 0;
2174   int wait = 0;
2175 
2176   kvm_t *kd;
2177   char errbuf[_POSIX2_LINE_MAX];
2178   struct kinfo_proc *procs; /* array of processes */
2179   struct kinfo_proc *proc_ptr = NULL;
2180   int count; /* returns number of processes */
2181 
2182   process_entry_t pse;
2183 
2184   ps_list_reset();
2185 
2186   /* Open the kvm interface, get a descriptor */
2187   kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf);
2188   if (kd == NULL) {
2189     ERROR("processes plugin: Cannot open kvm interface: %s", errbuf);
2190     return 0;
2191   }
2192 
2193   /* Get the list of processes. */
2194   procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, &count);
2195   if (procs == NULL) {
2196     ERROR("processes plugin: Cannot get kvm processes list: %s",
2197           kvm_geterr(kd));
2198     kvm_close(kd);
2199     return 0;
2200   }
2201 
2202   /* Iterate through the processes in kinfo_proc */
2203   for (int i = 0; i < count; i++) {
2204     /* Create only one process list entry per _process_, i.e.
2205      * filter out threads (duplicate PID entries). */
2206 #ifdef HAVE_STRUCT_KINFO_PROC_DRAGONFLY
2207     if ((proc_ptr == NULL) || (proc_ptr->kp_pid != procs[i].kp_pid)) {
2208 #else
2209     if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) {
2210 #endif
2211       char cmdline[CMDLINE_BUFFER_SIZE] = "";
2212       bool have_cmdline = 0;
2213 
2214       proc_ptr = &(procs[i]);
2215       /* Don't probe system processes and processes without arguments */
2216 #ifdef HAVE_STRUCT_KINFO_PROC_DRAGONFLY
2217       if ((procs[i].kp_flags & P_SYSTEM) == 0) {
2218 #else
2219       if (((procs[i].ki_flag & P_SYSTEM) == 0) && (procs[i].ki_args != NULL)) {
2220 #endif
2221         char **argv;
2222         int argc;
2223         int status;
2224 
2225         /* retrieve the arguments */
2226         argv = kvm_getargv(kd, proc_ptr, /* nchr = */ 0);
2227         argc = 0;
2228         if ((argv != NULL) && (argv[0] != NULL)) {
2229           while (argv[argc] != NULL)
2230             argc++;
2231 
2232           status = strjoin(cmdline, sizeof(cmdline), argv, argc, " ");
2233           if (status < 0)
2234             WARNING("processes plugin: Command line did not fit into buffer.");
2235           else
2236             have_cmdline = 1;
2237         }
2238       } /* if (process has argument list) */
2239 
2240       memset(&pse, 0, sizeof(pse));
2241 #ifdef HAVE_STRUCT_KINFO_PROC_DRAGONFLY
2242       pse.id = procs[i].kp_pid;
2243       pse.num_lwp = procs[i].kp_nthreads;
2244 
2245       pse.vmem_size = procs[i].kp_vm_map_size;
2246       pse.vmem_rss = procs[i].kp_vm_rssize * pagesize;
2247       pse.vmem_data = procs[i].kp_vm_dsize * pagesize;
2248       pse.vmem_code = procs[i].kp_vm_tsize * pagesize;
2249       pse.stack_size = procs[i].kp_vm_ssize * pagesize;
2250       pse.vmem_minflt_counter = procs[i].kp_ru.ru_minflt;
2251       pse.vmem_majflt_counter = procs[i].kp_ru.ru_majflt;
2252 #else
2253       pse.id = procs[i].ki_pid;
2254 
2255       pse.num_lwp = procs[i].ki_numthreads;
2256 
2257       pse.vmem_size = procs[i].ki_size;
2258       pse.vmem_rss = procs[i].ki_rssize * pagesize;
2259       pse.vmem_data = procs[i].ki_dsize * pagesize;
2260       pse.vmem_code = procs[i].ki_tsize * pagesize;
2261       pse.stack_size = procs[i].ki_ssize * pagesize;
2262       pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
2263       pse.vmem_majflt_counter = procs[i].ki_rusage.ru_majflt;
2264 #endif
2265       pse.num_proc = 1;
2266 
2267       pse.cpu_user_counter = 0;
2268       pse.cpu_system_counter = 0;
2269 
2270 #ifdef HAVE_STRUCT_KINFO_PROC_FREEBSD
2271       /*
2272        * The u-area might be swapped out, and we can't get
2273        * at it because we have a crashdump and no swap.
2274        * If it's here fill in these fields, otherwise, just
2275        * leave them 0.
2276        */
2277       if (procs[i].ki_flag & P_INMEM) {
2278         pse.cpu_user_counter = procs[i].ki_rusage.ru_utime.tv_usec +
2279                                (1000000lu * procs[i].ki_rusage.ru_utime.tv_sec);
2280         pse.cpu_system_counter =
2281             procs[i].ki_rusage.ru_stime.tv_usec +
2282             (1000000lu * procs[i].ki_rusage.ru_stime.tv_sec);
2283       }
2284 #endif
2285 
2286       /* no I/O data */
2287       pse.io_rchar = -1;
2288       pse.io_wchar = -1;
2289       pse.io_syscr = -1;
2290       pse.io_syscw = -1;
2291       pse.io_diskr = -1;
2292       pse.io_diskw = -1;
2293 
2294       /* file descriptor count not implemented */
2295       pse.num_fd = 0;
2296 
2297       /* Number of memory mappings */
2298       pse.num_maps = 0;
2299 
2300       /* context switch counters not implemented */
2301       pse.cswitch_vol = -1;
2302       pse.cswitch_invol = -1;
2303 
2304 #ifdef HAVE_STRUCT_KINFO_PROC_FREEBSD
2305       ps_list_add(procs[i].ki_comm, have_cmdline ? cmdline : NULL, &pse);
2306 
2307       switch (procs[i].ki_stat) {
2308       case SSLEEP:
2309         sleeping++;
2310         break;
2311       case SRUN:
2312         running++;
2313         break;
2314       case SWAIT:
2315         wait++;
2316         break;
2317       case SLOCK:
2318         blocked++;
2319         break;
2320 #else
2321       ps_list_add(procs[i].kp_comm, have_cmdline ? cmdline : NULL, &pse);
2322 
2323       switch (procs[i].kp_stat) {
2324       case SACTIVE:
2325 	      switch(procs[i].kp_lwp.kl_stat) {
2326 	      case LSSLEEP:
2327 		      sleeping++;
2328 		      break;
2329 	      case LSRUN:
2330 		      running++;
2331 		      break;
2332 	      case LSSTOP:
2333 		      stopped++;
2334 		      break;
2335 	      }
2336 	      break;
2337 #endif
2338       case SSTOP:
2339         stopped++;
2340         break;
2341       case SZOMB:
2342         zombies++;
2343         break;
2344       case SIDL:
2345         idle++;
2346         break;
2347       }
2348     } /* if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) */
2349   }
2350 
2351   kvm_close(kd);
2352 
2353   ps_submit_state("running", running);
2354   ps_submit_state("sleeping", sleeping);
2355   ps_submit_state("zombies", zombies);
2356   ps_submit_state("stopped", stopped);
2357   ps_submit_state("blocked", blocked);
2358   ps_submit_state("idle", idle);
2359   ps_submit_state("wait", wait);
2360 
2361   for (collectd_procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
2362     ps_submit_proc_list(ps_ptr);
2363     /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
2364 
2365 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC2_NETBSD
2366   int running = 0;
2367   int sleeping = 0;
2368   int zombies = 0;
2369   int stopped = 0;
2370   int blocked = 0;
2371   int idle = 0;
2372   int wait = 0;
2373 
2374   kvm_t *kd;
2375   char errbuf[_POSIX2_LINE_MAX];
2376   struct kinfo_proc2 *procs; /* array of processes */
2377   struct kinfo_proc2 *proc_ptr = NULL;
2378   struct kinfo_proc2 *p;
2379   int count; /* returns number of processes */
2380   int i;
2381   int l, nlwps;
2382   struct kinfo_lwp *kl;
2383 
2384   collectd_procstat_t *ps_ptr;
2385   process_entry_t pse;
2386 
2387   ps_list_reset();
2388 
2389   /* Open the kvm interface, get a descriptor */
2390   kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
2391   if (kd == NULL) {
2392     ERROR("processes plugin: Cannot open kvm interface: %s", errbuf);
2393     return (0);
2394   }
2395 
2396   /* Get the list of processes. */
2397   procs =
2398       kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &count);
2399   if (procs == NULL) {
2400     ERROR("processes plugin: Cannot get kvm processes list: %s",
2401           kvm_geterr(kd));
2402     kvm_close(kd);
2403     return (0);
2404   }
2405 
2406   /* Iterate through the processes in kinfo_proc */
2407   for (i = 0; i < count; i++) {
2408     /* Create only one process list entry per _process_, i.e.
2409      * filter out threads (duplicate PID entries). */
2410     if ((proc_ptr == NULL) || (proc_ptr->p_pid != procs[i].p_pid)) {
2411       char cmdline[CMDLINE_BUFFER_SIZE] = "";
2412       _Bool have_cmdline = 0;
2413 
2414       proc_ptr = &(procs[i]);
2415       /* Don't probe system processes and processes without arguments */
2416       if (((procs[i].p_flag & P_SYSTEM) == 0) && (procs[i].p_comm[0] != 0)) {
2417         char **argv;
2418         int argc;
2419         int status;
2420 
2421         /* retrieve the arguments */
2422         argv = kvm_getargv2(kd, proc_ptr, 0);
2423         argc = 0;
2424         if ((argv != NULL) && (argv[0] != NULL)) {
2425           while (argv[argc] != NULL)
2426             argc++;
2427 
2428           status = strjoin(cmdline, sizeof(cmdline), argv, argc, " ");
2429           if (status < 0)
2430             WARNING("processes plugin: Command line did not fit into buffer.");
2431           else
2432             have_cmdline = 1;
2433         }
2434       } /* if (process has argument list) */
2435 
2436       memset(&pse, 0, sizeof(pse));
2437       pse.id = procs[i].p_pid;
2438 
2439       pse.num_proc = 1;
2440       pse.num_lwp = procs[i].p_nlwps;
2441 
2442       pse.vmem_size = procs[i].p_uru_maxrss * pagesize;
2443       pse.vmem_rss = procs[i].p_vm_rssize * pagesize;
2444       pse.vmem_data = procs[i].p_vm_dsize * pagesize;
2445       pse.vmem_code = procs[i].p_vm_tsize * pagesize;
2446       pse.stack_size = procs[i].p_vm_ssize * pagesize;
2447       pse.vmem_minflt_counter = procs[i].p_uru_minflt;
2448       pse.vmem_majflt_counter = procs[i].p_uru_majflt;
2449 
2450       pse.cpu_user_counter = 0;
2451       pse.cpu_system_counter = 0;
2452       /* context switch counters not implemented */
2453       pse.cswitch_vol = -1;
2454       pse.cswitch_invol = -1;
2455 
2456       /*
2457        * The u-area might be swapped out, and we can't get
2458        * at it because we have a crashdump and no swap.
2459        * If it's here fill in these fields, otherwise, just
2460        * leave them 0.
2461        */
2462       if (procs[i].p_flag & P_INMEM) {
2463         pse.cpu_user_counter =
2464             procs[i].p_uutime_usec + (1000000lu * procs[i].p_uutime_sec);
2465         pse.cpu_system_counter =
2466             procs[i].p_ustime_usec + (1000000lu * procs[i].p_ustime_sec);
2467       }
2468 
2469       /* no I/O data */
2470       pse.io_rchar = -1;
2471       pse.io_wchar = -1;
2472       pse.io_syscr = procs[i].p_uru_inblock;
2473       pse.io_syscw = procs[i].p_uru_oublock;
2474 
2475       /* file descriptor count not implemented */
2476       pse.num_fd = 0;
2477 
2478       /* Number of memory mappings */
2479       pse.num_maps = 0;
2480 
2481       /* context switch counters not implemented */
2482       pse.cswitch_vol = -1;
2483       pse.cswitch_invol = -1;
2484 
2485       ps_list_add(procs[i].p_comm, have_cmdline ? cmdline : NULL, &pse);
2486     } /* if ((proc_ptr == NULL) || (proc_ptr->ki_pid != procs[i].ki_pid)) */
2487 
2488     /* system processes' LWPs end up in "running" state */
2489     if ((procs[i].p_flag & P_SYSTEM) != 0)
2490       continue;
2491 
2492     switch (procs[i].p_realstat) {
2493     case SSTOP:
2494     case SACTIVE:
2495     case SIDL:
2496       p = &(procs[i]);
2497       /* get info about LWPs */
2498       kl = kvm_getlwps(kd, p->p_pid, (u_long)p->p_paddr,
2499                        sizeof(struct kinfo_lwp), &nlwps);
2500 
2501       for (l = 0; kl && l < nlwps; l++) {
2502         switch (kl[l].l_stat) {
2503         case LSONPROC:
2504         case LSRUN:
2505           running++;
2506           break;
2507         case LSSLEEP:
2508           if (kl[l].l_flag & L_SINTR) {
2509             if (kl[l].l_slptime > maxslp)
2510               idle++;
2511             else
2512               sleeping++;
2513           } else
2514             blocked++;
2515           break;
2516         case LSSTOP:
2517           stopped++;
2518           break;
2519         case LSIDL:
2520           idle++;
2521           break;
2522         }
2523       }
2524       break;
2525     case SZOMB:
2526     case SDYING:
2527     case SDEAD:
2528       zombies++;
2529       break;
2530     }
2531   }
2532 
2533   kvm_close(kd);
2534 
2535   ps_submit_state("running", running);
2536   ps_submit_state("sleeping", sleeping);
2537   ps_submit_state("zombies", zombies);
2538   ps_submit_state("stopped", stopped);
2539   ps_submit_state("blocked", blocked);
2540   ps_submit_state("idle", idle);
2541   ps_submit_state("wait", wait);
2542 
2543   for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
2544     ps_submit_proc_list(ps_ptr);
2545     /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC2_NETBSD */
2546 
2547 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_OPENBSD
2548   int running = 0;
2549   int sleeping = 0;
2550   int zombies = 0;
2551   int stopped = 0;
2552   int onproc = 0;
2553   int idle = 0;
2554   int dead = 0;
2555 
2556   kvm_t *kd;
2557   char errbuf[1024];
2558   struct kinfo_proc *procs; /* array of processes */
2559   struct kinfo_proc *proc_ptr = NULL;
2560   int count; /* returns number of processes */
2561 
2562   process_entry_t pse;
2563 
2564   ps_list_reset();
2565 
2566   /* Open the kvm interface, get a descriptor */
2567   kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
2568   if (kd == NULL) {
2569     ERROR("processes plugin: Cannot open kvm interface: %s", errbuf);
2570     return 0;
2571   }
2572 
2573   /* Get the list of processes. */
2574   procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &count);
2575   if (procs == NULL) {
2576     ERROR("processes plugin: Cannot get kvm processes list: %s",
2577           kvm_geterr(kd));
2578     kvm_close(kd);
2579     return 0;
2580   }
2581 
2582   /* Iterate through the processes in kinfo_proc */
2583   for (int i = 0; i < count; i++) {
2584     /* Create only one process list entry per _process_, i.e.
2585      * filter out threads (duplicate PID entries). */
2586     if ((proc_ptr == NULL) || (proc_ptr->p_pid != procs[i].p_pid)) {
2587       char cmdline[CMDLINE_BUFFER_SIZE] = "";
2588       bool have_cmdline = 0;
2589 
2590       proc_ptr = &(procs[i]);
2591       /* Don't probe zombie processes  */
2592       if (!P_ZOMBIE(proc_ptr)) {
2593         char **argv;
2594         int argc;
2595         int status;
2596 
2597         /* retrieve the arguments */
2598         argv = kvm_getargv(kd, proc_ptr, /* nchr = */ 0);
2599         argc = 0;
2600         if ((argv != NULL) && (argv[0] != NULL)) {
2601           while (argv[argc] != NULL)
2602             argc++;
2603 
2604           status = strjoin(cmdline, sizeof(cmdline), argv, argc, " ");
2605           if (status < 0)
2606             WARNING("processes plugin: Command line did not fit into buffer.");
2607           else
2608             have_cmdline = 1;
2609         }
2610       } /* if (process has argument list) */
2611 
2612       memset(&pse, 0, sizeof(pse));
2613       pse.id = procs[i].p_pid;
2614 
2615       pse.num_proc = 1;
2616       pse.num_lwp = 1; /* XXX: accumulate p_tid values for a single p_pid ? */
2617 
2618       pse.vmem_rss = procs[i].p_vm_rssize * pagesize;
2619       pse.vmem_data = procs[i].p_vm_dsize * pagesize;
2620       pse.vmem_code = procs[i].p_vm_tsize * pagesize;
2621       pse.stack_size = procs[i].p_vm_ssize * pagesize;
2622       pse.vmem_size = pse.stack_size + pse.vmem_code + pse.vmem_data;
2623       pse.vmem_minflt_counter = procs[i].p_uru_minflt;
2624       pse.vmem_majflt_counter = procs[i].p_uru_majflt;
2625 
2626       pse.cpu_user_counter =
2627           procs[i].p_uutime_usec + (1000000lu * procs[i].p_uutime_sec);
2628       pse.cpu_system_counter =
2629           procs[i].p_ustime_usec + (1000000lu * procs[i].p_ustime_sec);
2630 
2631       /* no I/O data */
2632       pse.io_rchar = -1;
2633       pse.io_wchar = -1;
2634       pse.io_syscr = -1;
2635       pse.io_syscw = -1;
2636       pse.io_diskr = -1;
2637       pse.io_diskw = -1;
2638 
2639       /* file descriptor count not implemented */
2640       pse.num_fd = 0;
2641 
2642       /* Number of memory mappings */
2643       pse.num_maps = 0;
2644 
2645       /* context switch counters not implemented */
2646       pse.cswitch_vol = -1;
2647       pse.cswitch_invol = -1;
2648 
2649       ps_list_add(procs[i].p_comm, have_cmdline ? cmdline : NULL, &pse);
2650 
2651       switch (procs[i].p_stat) {
2652       case SSTOP:
2653         stopped++;
2654         break;
2655       case SSLEEP:
2656         sleeping++;
2657         break;
2658       case SRUN:
2659         running++;
2660         break;
2661       case SIDL:
2662         idle++;
2663         break;
2664       case SONPROC:
2665         onproc++;
2666         break;
2667       case SDEAD:
2668         dead++;
2669         break;
2670       case SZOMB:
2671         zombies++;
2672         break;
2673       }
2674     } /* if ((proc_ptr == NULL) || (proc_ptr->p_pid != procs[i].p_pid)) */
2675   }
2676 
2677   kvm_close(kd);
2678 
2679   ps_submit_state("running", running);
2680   ps_submit_state("sleeping", sleeping);
2681   ps_submit_state("zombies", zombies);
2682   ps_submit_state("stopped", stopped);
2683   ps_submit_state("onproc", onproc);
2684   ps_submit_state("idle", idle);
2685   ps_submit_state("dead", dead);
2686 
2687   for (collectd_procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
2688     ps_submit_proc_list(ps_ptr);
2689     /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_OPENBSD */
2690 
2691 #elif HAVE_PROCINFO_H
2692   /* AIX */
2693   int running = 0;
2694   int sleeping = 0;
2695   int zombies = 0;
2696   int stopped = 0;
2697   int paging = 0;
2698   int blocked = 0;
2699 
2700   pid_t pindex = 0;
2701   int nprocs;
2702 
2703   process_entry_t pse;
2704 
2705   ps_list_reset();
2706   while ((nprocs = getprocs64(procentry, sizeof(struct procentry64),
2707                               /* fdsinfo = */ NULL, sizeof(struct fdsinfo64),
2708                               &pindex, MAXPROCENTRY)) > 0) {
2709     for (int i = 0; i < nprocs; i++) {
2710       tid64_t thindex;
2711       int nthreads;
2712       char arglist[MAXARGLN + 1];
2713       char *cargs;
2714       char *cmdline;
2715 
2716       if (procentry[i].pi_state == SNONE)
2717         continue;
2718       /* if (procentry[i].pi_state == SZOMB)  FIXME */
2719 
2720       cmdline = procentry[i].pi_comm;
2721       cargs = procentry[i].pi_comm;
2722       if (procentry[i].pi_flags & SKPROC) {
2723         if (procentry[i].pi_pid == 0)
2724           cmdline = "swapper";
2725         cargs = cmdline;
2726       } else {
2727         if (getargs(&procentry[i], sizeof(struct procentry64), arglist,
2728                     MAXARGLN) >= 0) {
2729           int n;
2730 
2731           n = -1;
2732           while (++n < MAXARGLN) {
2733             if (arglist[n] == '\0') {
2734               if (arglist[n + 1] == '\0')
2735                 break;
2736               arglist[n] = ' ';
2737             }
2738           }
2739           cargs = arglist;
2740         }
2741       }
2742 
2743       memset(&pse, 0, sizeof(pse));
2744 
2745       pse.id = procentry[i].pi_pid;
2746       pse.num_lwp = procentry[i].pi_thcount;
2747       pse.num_proc = 1;
2748 
2749       thindex = 0;
2750       while ((nthreads = getthrds64(procentry[i].pi_pid, thrdentry,
2751                                     sizeof(struct thrdentry64), &thindex,
2752                                     MAXTHRDENTRY)) > 0) {
2753         int j;
2754 
2755         for (j = 0; j < nthreads; j++) {
2756           switch (thrdentry[j].ti_state) {
2757           /* case TSNONE: break; */
2758           case TSIDL:
2759             blocked++;
2760             break; /* FIXME is really blocked */
2761           case TSRUN:
2762             running++;
2763             break;
2764           case TSSLEEP:
2765             sleeping++;
2766             break;
2767           case TSSWAP:
2768             paging++;
2769             break;
2770           case TSSTOP:
2771             stopped++;
2772             break;
2773           case TSZOMB:
2774             zombies++;
2775             break;
2776           }
2777         }
2778         if (nthreads < MAXTHRDENTRY)
2779           break;
2780       }
2781 
2782       /* tv_usec is nanosec ??? */
2783       pse.cpu_user_counter = procentry[i].pi_ru.ru_utime.tv_sec * 1000000 +
2784                              procentry[i].pi_ru.ru_utime.tv_usec / 1000;
2785 
2786       /* tv_usec is nanosec ??? */
2787       pse.cpu_system_counter = procentry[i].pi_ru.ru_stime.tv_sec * 1000000 +
2788                                procentry[i].pi_ru.ru_stime.tv_usec / 1000;
2789 
2790       pse.vmem_minflt_counter = procentry[i].pi_minflt;
2791       pse.vmem_majflt_counter = procentry[i].pi_majflt;
2792 
2793       pse.vmem_size = procentry[i].pi_tsize + procentry[i].pi_dvm * pagesize;
2794       pse.vmem_rss = (procentry[i].pi_drss + procentry[i].pi_trss) * pagesize;
2795       /* Not supported/implemented */
2796       pse.vmem_data = 0;
2797       pse.vmem_code = 0;
2798       pse.stack_size = 0;
2799 
2800       pse.io_rchar = -1;
2801       pse.io_wchar = -1;
2802       pse.io_syscr = -1;
2803       pse.io_syscw = -1;
2804       pse.io_diskr = -1;
2805       pse.io_diskw = -1;
2806 
2807       pse.num_fd = 0;
2808       pse.num_maps = 0;
2809 
2810       pse.cswitch_vol = -1;
2811       pse.cswitch_invol = -1;
2812 
2813       ps_list_add(cmdline, cargs, &pse);
2814     } /* for (i = 0 .. nprocs) */
2815 
2816     if (nprocs < MAXPROCENTRY)
2817       break;
2818   } /* while (getprocs64() > 0) */
2819   ps_submit_state("running", running);
2820   ps_submit_state("sleeping", sleeping);
2821   ps_submit_state("zombies", zombies);
2822   ps_submit_state("stopped", stopped);
2823   ps_submit_state("paging", paging);
2824   ps_submit_state("blocked", blocked);
2825 
2826   for (collectd_procstat_t *ps = list_head_g; ps != NULL; ps = ps->next)
2827     ps_submit_proc_list(ps);
2828     /* #endif HAVE_PROCINFO_H */
2829 
2830 #elif KERNEL_SOLARIS
2831   /*
2832    * The Solaris section adds a few more process states and removes some
2833    * process states compared to linux. Most notably there is no "PAGING"
2834    * and "BLOCKED" state for a process.  The rest is similar to the linux
2835    * code.
2836    */
2837   int running = 0;
2838   int sleeping = 0;
2839   int zombies = 0;
2840   int stopped = 0;
2841   int detached = 0;
2842   int daemon = 0;
2843   int system = 0;
2844   int orphan = 0;
2845 
2846   struct dirent *ent;
2847   DIR *proc;
2848 
2849   int status;
2850   char state;
2851 
2852   char cmdline[PRARGSZ];
2853 
2854   ps_list_reset();
2855 
2856   proc = opendir("/proc");
2857   if (proc == NULL)
2858     return -1;
2859 
2860   while ((ent = readdir(proc)) != NULL) {
2861     long pid;
2862     process_entry_t pse;
2863     char *endptr;
2864 
2865     if (!isdigit((int)ent->d_name[0]))
2866       continue;
2867 
2868     pid = strtol(ent->d_name, &endptr, 10);
2869     if (*endptr != 0) /* value didn't completely parse as a number */
2870       continue;
2871 
2872     memset(&pse, 0, sizeof(pse));
2873     pse.id = pid;
2874 
2875     status = ps_read_process(pid, &pse, &state);
2876     if (status != 0) {
2877       DEBUG("ps_read_process failed: %i", status);
2878       continue;
2879     }
2880 
2881     switch (state) {
2882     case 'R':
2883       running++;
2884       break;
2885     case 'S':
2886       sleeping++;
2887       break;
2888     case 'E':
2889       detached++;
2890       break;
2891     case 'Z':
2892       zombies++;
2893       break;
2894     case 'T':
2895       stopped++;
2896       break;
2897     case 'A':
2898       daemon++;
2899       break;
2900     case 'Y':
2901       system++;
2902       break;
2903     case 'O':
2904       orphan++;
2905       break;
2906     }
2907 
2908     ps_list_add(pse.name,
2909                 ps_get_cmdline(pid, pse.name, cmdline, sizeof(cmdline)), &pse);
2910   } /* while(readdir) */
2911   closedir(proc);
2912 
2913   ps_submit_state("running", running);
2914   ps_submit_state("sleeping", sleeping);
2915   ps_submit_state("zombies", zombies);
2916   ps_submit_state("stopped", stopped);
2917   ps_submit_state("detached", detached);
2918   ps_submit_state("daemon", daemon);
2919   ps_submit_state("system", system);
2920   ps_submit_state("orphan", orphan);
2921 
2922   for (collectd_procstat_t *ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
2923     ps_submit_proc_list(ps_ptr);
2924 
2925   read_fork_rate();
2926 #endif /* KERNEL_SOLARIS */
2927 
2928   want_init = false;
2929 
2930   return 0;
2931 } /* int ps_read */
2932 
2933 void module_register(void) {
2934   plugin_register_complex_config("processes", ps_config);
2935   plugin_register_init("processes", ps_init);
2936   plugin_register_read("processes", ps_read);
2937 } /* void module_register */
2938