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