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