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