Merge branch 'collectd-4.8' into collectd-4.9
[collectd.git] / src / processes.c
1 /**
2  * collectd - src/processes.c
3  * Copyright (C) 2005       Lyonel Vincent
4  * Copyright (C) 2006-2008  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  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU General Public License as published by the
12  * Free Software Foundation; either version 2 of the License, or (at your
13  * option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License along
21  * with this program; if not, write to the Free Software Foundation, Inc.,
22  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
23  *
24  * Authors:
25  *   Lyonel Vincent <lyonel at ezix.org>
26  *   Florian octo Forster <octo at verplant.org>
27  *   Oleg King <king2 at kaluga.ru>
28  *   Sebastian Harl <sh at tokkee.org>
29  *   Andrés J. Díaz <ajdiaz at connectical.com>
30  *   Manuel Sanmartin
31  **/
32
33 #include "collectd.h"
34 #include "common.h"
35 #include "plugin.h"
36 #include "configfile.h"
37
38 /* Include header files for the mach system, if they exist.. */
39 #if HAVE_THREAD_INFO
40 #  if HAVE_MACH_MACH_INIT_H
41 #    include <mach/mach_init.h>
42 #  endif
43 #  if HAVE_MACH_HOST_PRIV_H
44 #    include <mach/host_priv.h>
45 #  endif
46 #  if HAVE_MACH_MACH_ERROR_H
47 #    include <mach/mach_error.h>
48 #  endif
49 #  if HAVE_MACH_MACH_HOST_H
50 #    include <mach/mach_host.h>
51 #  endif
52 #  if HAVE_MACH_MACH_PORT_H
53 #    include <mach/mach_port.h>
54 #  endif
55 #  if HAVE_MACH_MACH_TYPES_H
56 #    include <mach/mach_types.h>
57 #  endif
58 #  if HAVE_MACH_MESSAGE_H
59 #    include <mach/message.h>
60 #  endif
61 #  if HAVE_MACH_PROCESSOR_SET_H
62 #    include <mach/processor_set.h>
63 #  endif
64 #  if HAVE_MACH_TASK_H
65 #    include <mach/task.h>
66 #  endif
67 #  if HAVE_MACH_THREAD_ACT_H
68 #    include <mach/thread_act.h>
69 #  endif
70 #  if HAVE_MACH_VM_REGION_H
71 #    include <mach/vm_region.h>
72 #  endif
73 #  if HAVE_MACH_VM_MAP_H
74 #    include <mach/vm_map.h>
75 #  endif
76 #  if HAVE_MACH_VM_PROT_H
77 #    include <mach/vm_prot.h>
78 #  endif
79 #  if HAVE_SYS_SYSCTL_H
80 #    include <sys/sysctl.h>
81 #  endif
82 /* #endif HAVE_THREAD_INFO */
83
84 #elif KERNEL_LINUX
85 #  if HAVE_LINUX_CONFIG_H
86 #    include <linux/config.h>
87 #  endif
88 #  ifndef CONFIG_HZ
89 #    define CONFIG_HZ 100
90 #  endif
91 /* #endif KERNEL_LINUX */
92
93 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
94 #  include <kvm.h>
95 #  include <sys/param.h>
96 #  include <sys/sysctl.h>
97 #  include <sys/user.h>
98 #  include <sys/proc.h>
99 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
100
101 #elif HAVE_PROCINFO_H
102 #  include <procinfo.h>
103 #  include <sys/types.h>
104
105 #define MAXPROCENTRY 32
106 #define MAXTHRDENTRY 16
107 #define MAXARGLN 1024
108 /* #endif HAVE_PROCINFO_H */
109
110 #else
111 # error "No applicable input method."
112 #endif
113
114 #if HAVE_REGEX_H
115 # include <regex.h>
116 #endif
117
118 #ifndef ARG_MAX
119 #  define ARG_MAX 4096
120 #endif
121
122 #define BUFSIZE 256
123
124 static const char *config_keys[] =
125 {
126         "Process",
127         "ProcessMatch"
128 };
129 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
130
131 typedef struct procstat_entry_s
132 {
133         unsigned long id;
134         unsigned long age;
135
136         unsigned long num_proc;
137         unsigned long num_lwp;
138         unsigned long vmem_size;
139         unsigned long vmem_rss;
140         unsigned long stack_size;
141
142         unsigned long vmem_minflt;
143         unsigned long vmem_majflt;
144         unsigned long vmem_minflt_counter;
145         unsigned long vmem_majflt_counter;
146
147         unsigned long cpu_user;
148         unsigned long cpu_system;
149         unsigned long cpu_user_counter;
150         unsigned long cpu_system_counter;
151
152         /* io data */
153         derive_t io_rchar;
154         derive_t io_wchar;
155         derive_t io_syscr;
156         derive_t io_syscw;
157
158         struct procstat_entry_s *next;
159 } procstat_entry_t;
160
161 #define PROCSTAT_NAME_LEN 256
162 typedef struct procstat
163 {
164         char          name[PROCSTAT_NAME_LEN];
165 #if HAVE_REGEX_H
166         regex_t *re;
167 #endif
168
169         unsigned long num_proc;
170         unsigned long num_lwp;
171         unsigned long vmem_size;
172         unsigned long vmem_rss;
173         unsigned long stack_size;
174
175         unsigned long vmem_minflt_counter;
176         unsigned long vmem_majflt_counter;
177
178         unsigned long cpu_user_counter;
179         unsigned long cpu_system_counter;
180
181         /* io data */
182         derive_t io_rchar;
183         derive_t io_wchar;
184         derive_t io_syscr;
185         derive_t io_syscw;
186
187         struct procstat   *next;
188         struct procstat_entry_s *instances;
189 } procstat_t;
190
191 static procstat_t *list_head_g = NULL;
192
193 #if HAVE_THREAD_INFO
194 static mach_port_t port_host_self;
195 static mach_port_t port_task_self;
196
197 static processor_set_name_array_t pset_list;
198 static mach_msg_type_number_t     pset_list_len;
199 /* #endif HAVE_THREAD_INFO */
200
201 #elif KERNEL_LINUX
202 static long pagesize_g;
203 /* #endif KERNEL_LINUX */
204
205 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
206 /* no global variables */
207 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
208
209 #elif HAVE_PROCINFO_H
210 static  struct procentry64 procentry[MAXPROCENTRY];
211 static  struct thrdentry64 thrdentry[MAXTHRDENTRY];
212 static int pagesize;
213
214 #ifndef _AIXVERSION_610
215 int     getprocs64 (void *procsinfo, int sizproc, void *fdsinfo, int sizfd, pid_t *index, int count);
216 int     getthrds64( pid_t, void *, int, tid64_t *, int );
217 #endif
218 int getargs (struct procentry64 *processBuffer, int bufferLen, char *argsBuffer, int argsLen);
219 #endif /* HAVE_PROCINFO_H */
220
221 /* put name of process from config to list_head_g tree
222    list_head_g is a list of 'procstat_t' structs with
223    processes names we want to watch */
224 static void ps_list_register (const char *name, const char *regexp)
225 {
226         procstat_t *new;
227         procstat_t *ptr;
228         int status;
229
230         new = (procstat_t *) malloc (sizeof (procstat_t));
231         if (new == NULL)
232         {
233                 ERROR ("processes plugin: ps_list_register: malloc failed.");
234                 return;
235         }
236         memset (new, 0, sizeof (procstat_t));
237         sstrncpy (new->name, name, sizeof (new->name));
238
239 #if HAVE_REGEX_H
240         if (regexp != NULL)
241         {
242                 DEBUG ("ProcessMatch: adding \"%s\" as criteria to process %s.", regexp, name);
243                 new->re = (regex_t *) malloc (sizeof (regex_t));
244                 if (new->re == NULL)
245                 {
246                         ERROR ("processes plugin: ps_list_register: malloc failed.");
247                         sfree (new);
248                         return;
249                 }
250
251                 status = regcomp (new->re, regexp, REG_EXTENDED | REG_NOSUB);
252                 if (status != 0)
253                 {
254                         DEBUG ("ProcessMatch: compiling the regular expression \"%s\" failed.", regexp);
255                         sfree(new->re);
256                         return;
257                 }
258         }
259 #else
260         if (regexp != NULL)
261         {
262                 ERROR ("processes plugin: ps_list_register: "
263                                 "Regular expression \"%s\" found in config "
264                                 "file, but support for regular expressions "
265                                 "has been disabled at compile time.",
266                                 regexp);
267                 sfree (new);
268                 return;
269         }
270 #endif
271
272         for (ptr = list_head_g; ptr != NULL; ptr = ptr->next)
273         {
274                 if (strcmp (ptr->name, name) == 0)
275                 {
276                         WARNING ("processes plugin: You have configured more "
277                                         "than one `Process' or "
278                                         "`ProcessMatch' with the same name. "
279                                         "All but the first setting will be "
280                                         "ignored.");
281                         sfree (new->re);
282                         sfree (new);
283                         return;
284                 }
285
286                 if (ptr->next == NULL)
287                         break;
288         }
289
290         if (ptr == NULL)
291                 list_head_g = new;
292         else
293                 ptr->next = new;
294 } /* void ps_list_register */
295
296 /* try to match name against entry, returns 1 if success */
297 static int ps_list_match (const char *name, const char *cmdline, procstat_t *ps)
298 {
299 #if HAVE_REGEX_H
300         if (ps->re != NULL)
301         {
302                 int status;
303                 const char *str;
304
305                 str = cmdline;
306                 if ((str == NULL) || (str[0] == 0))
307                         str = name;
308
309                 assert (str != NULL);
310
311                 status = regexec (ps->re, str,
312                                 /* nmatch = */ 0,
313                                 /* pmatch = */ NULL,
314                                 /* eflags = */ 0);
315                 if (status == 0)
316                         return (1);
317         }
318         else
319 #endif
320         if (strcmp (ps->name, name) == 0)
321                 return (1);
322
323         return (0);
324 } /* int ps_list_match */
325
326 /* add process entry to 'instances' of process 'name' (or refresh it) */
327 static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t *entry)
328 {
329         procstat_t *ps;
330         procstat_entry_t *pse;
331
332         if (entry->id == 0)
333                 return;
334
335         for (ps = list_head_g; ps != NULL; ps = ps->next)
336         {
337                 if ((ps_list_match (name, cmdline, ps)) == 0)
338                         continue;
339
340                 for (pse = ps->instances; pse != NULL; pse = pse->next)
341                         if ((pse->id == entry->id) || (pse->next == NULL))
342                                 break;
343
344                 if ((pse == NULL) || (pse->id != entry->id))
345                 {
346                         procstat_entry_t *new;
347
348                         new = (procstat_entry_t *) malloc (sizeof (procstat_entry_t));
349                         if (new == NULL)
350                                 return;
351                         memset (new, 0, sizeof (procstat_entry_t));
352                         new->id = entry->id;
353
354                         if (pse == NULL)
355                                 ps->instances = new;
356                         else
357                                 pse->next = new;
358
359                         pse = new;
360                 }
361
362                 pse->age = 0;
363                 pse->num_proc   = entry->num_proc;
364                 pse->num_lwp    = entry->num_lwp;
365                 pse->vmem_size  = entry->vmem_size;
366                 pse->vmem_rss   = entry->vmem_rss;
367                 pse->stack_size = entry->stack_size;
368                 pse->io_rchar   = entry->io_rchar;
369                 pse->io_wchar   = entry->io_wchar;
370                 pse->io_syscr   = entry->io_syscr;
371                 pse->io_syscw   = entry->io_syscw;
372
373                 ps->num_proc   += pse->num_proc;
374                 ps->num_lwp    += pse->num_lwp;
375                 ps->vmem_size  += pse->vmem_size;
376                 ps->vmem_rss   += pse->vmem_rss;
377                 ps->stack_size += pse->stack_size;
378
379                 ps->io_rchar   += ((pse->io_rchar == -1)?0:pse->io_rchar);
380                 ps->io_wchar   += ((pse->io_wchar == -1)?0:pse->io_wchar);
381                 ps->io_syscr   += ((pse->io_syscr == -1)?0:pse->io_syscr);
382                 ps->io_syscw   += ((pse->io_syscw == -1)?0:pse->io_syscw);
383
384                 if ((entry->vmem_minflt_counter == 0)
385                                 && (entry->vmem_majflt_counter == 0))
386                 {
387                         pse->vmem_minflt_counter += entry->vmem_minflt;
388                         pse->vmem_minflt = entry->vmem_minflt;
389
390                         pse->vmem_majflt_counter += entry->vmem_majflt;
391                         pse->vmem_majflt = entry->vmem_majflt;
392                 }
393                 else
394                 {
395                         if (entry->vmem_minflt_counter < pse->vmem_minflt_counter)
396                         {
397                                 pse->vmem_minflt = entry->vmem_minflt_counter
398                                         + (ULONG_MAX - pse->vmem_minflt_counter);
399                         }
400                         else
401                         {
402                                 pse->vmem_minflt = entry->vmem_minflt_counter - pse->vmem_minflt_counter;
403                         }
404                         pse->vmem_minflt_counter = entry->vmem_minflt_counter;
405
406                         if (entry->vmem_majflt_counter < pse->vmem_majflt_counter)
407                         {
408                                 pse->vmem_majflt = entry->vmem_majflt_counter
409                                         + (ULONG_MAX - pse->vmem_majflt_counter);
410                         }
411                         else
412                         {
413                                 pse->vmem_majflt = entry->vmem_majflt_counter - pse->vmem_majflt_counter;
414                         }
415                         pse->vmem_majflt_counter = entry->vmem_majflt_counter;
416                 }
417
418                 ps->vmem_minflt_counter += pse->vmem_minflt;
419                 ps->vmem_majflt_counter += pse->vmem_majflt;
420
421                 if ((entry->cpu_user_counter == 0)
422                                 && (entry->cpu_system_counter == 0))
423                 {
424                         pse->cpu_user_counter += entry->cpu_user;
425                         pse->cpu_user = entry->cpu_user;
426
427                         pse->cpu_system_counter += entry->cpu_system;
428                         pse->cpu_system = entry->cpu_system;
429                 }
430                 else
431                 {
432                         if (entry->cpu_user_counter < pse->cpu_user_counter)
433                         {
434                                 pse->cpu_user = entry->cpu_user_counter
435                                         + (ULONG_MAX - pse->cpu_user_counter);
436                         }
437                         else
438                         {
439                                 pse->cpu_user = entry->cpu_user_counter - pse->cpu_user_counter;
440                         }
441                         pse->cpu_user_counter = entry->cpu_user_counter;
442
443                         if (entry->cpu_system_counter < pse->cpu_system_counter)
444                         {
445                                 pse->cpu_system = entry->cpu_system_counter
446                                         + (ULONG_MAX - pse->cpu_system_counter);
447                         }
448                         else
449                         {
450                                 pse->cpu_system = entry->cpu_system_counter - pse->cpu_system_counter;
451                         }
452                         pse->cpu_system_counter = entry->cpu_system_counter;
453                 }
454
455                 ps->cpu_user_counter   += pse->cpu_user;
456                 ps->cpu_system_counter += pse->cpu_system;
457         }
458 }
459
460 /* remove old entries from instances of processes in list_head_g */
461 static void ps_list_reset (void)
462 {
463         procstat_t *ps;
464         procstat_entry_t *pse;
465         procstat_entry_t *pse_prev;
466
467         for (ps = list_head_g; ps != NULL; ps = ps->next)
468         {
469                 ps->num_proc    = 0;
470                 ps->num_lwp     = 0;
471                 ps->vmem_size   = 0;
472                 ps->vmem_rss    = 0;
473                 ps->stack_size  = 0;
474                 ps->io_rchar = -1;
475                 ps->io_wchar = -1;
476                 ps->io_syscr = -1;
477                 ps->io_syscw = -1;
478
479                 pse_prev = NULL;
480                 pse = ps->instances;
481                 while (pse != NULL)
482                 {
483                         if (pse->age > 10)
484                         {
485                                 DEBUG ("Removing this procstat entry cause it's too old: "
486                                                 "id = %lu; name = %s;",
487                                                 pse->id, ps->name);
488
489                                 if (pse_prev == NULL)
490                                 {
491                                         ps->instances = pse->next;
492                                         free (pse);
493                                         pse = ps->instances;
494                                 }
495                                 else
496                                 {
497                                         pse_prev->next = pse->next;
498                                         free (pse);
499                                         pse = pse_prev->next;
500                                 }
501                         }
502                         else
503                         {
504                                 pse->age++;
505                                 pse_prev = pse;
506                                 pse = pse->next;
507                         }
508                 } /* while (pse != NULL) */
509         } /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
510 }
511
512 /* put all pre-defined 'Process' names from config to list_head_g tree */
513 static int ps_config (const char *key, const char *value)
514 {
515         if (strcasecmp (key, "Process") == 0)
516         {
517                 ps_list_register (value, NULL);
518         }
519         else if (strcasecmp (key, "ProcessMatch") == 0)
520         {
521                 char *new_val;
522                 char *fields[3];
523                 int fields_num;
524
525                 new_val = strdup (value);
526                 if (new_val == NULL) {
527                         ERROR ("processes plugin: strdup failed when processing "
528                                         "`ProcessMatch %s'.", value);
529                         return (1);
530                 }
531
532                 fields_num = strsplit (new_val, fields,
533                                 STATIC_ARRAY_SIZE (fields));
534                 if (fields_num != 2)
535                 {
536                         ERROR ("processes plugin: `ProcessMatch' needs exactly "
537                                         "two string arguments.");
538                         sfree (new_val);
539                         return (1);
540                 }
541                 ps_list_register (fields[0], fields[1]);
542                 sfree (new_val);
543         }
544         else
545         {
546                 ERROR ("processes plugin: The `%s' configuration option is not "
547                                 "understood and will be ignored.", key);
548                 return (-1);
549         }
550
551         return (0);
552 }
553
554 static int ps_init (void)
555 {
556 #if HAVE_THREAD_INFO
557         kern_return_t status;
558
559         port_host_self = mach_host_self ();
560         port_task_self = mach_task_self ();
561
562         if (pset_list != NULL)
563         {
564                 vm_deallocate (port_task_self,
565                                 (vm_address_t) pset_list,
566                                 pset_list_len * sizeof (processor_set_t));
567                 pset_list = NULL;
568                 pset_list_len = 0;
569         }
570
571         if ((status = host_processor_sets (port_host_self,
572                                         &pset_list,
573                                         &pset_list_len)) != KERN_SUCCESS)
574         {
575                 ERROR ("host_processor_sets failed: %s\n",
576                                 mach_error_string (status));
577                 pset_list = NULL;
578                 pset_list_len = 0;
579                 return (-1);
580         }
581 /* #endif HAVE_THREAD_INFO */
582
583 #elif KERNEL_LINUX
584         pagesize_g = sysconf(_SC_PAGESIZE);
585         DEBUG ("pagesize_g = %li; CONFIG_HZ = %i;",
586                         pagesize_g, CONFIG_HZ);
587 /* #endif KERNEL_LINUX */
588
589 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
590 /* no initialization */
591 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
592
593 #elif HAVE_PROCINFO_H
594         pagesize = getpagesize();
595 #endif /* HAVE_PROCINFO_H */
596
597         return (0);
598 } /* int ps_init */
599
600 /* submit global state (e.g.: qty of zombies, running, etc..) */
601 static void ps_submit_state (const char *state, double value)
602 {
603         value_t values[1];
604         value_list_t vl = VALUE_LIST_INIT;
605
606         values[0].gauge = value;
607
608         vl.values = values;
609         vl.values_len = 1;
610         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
611         sstrncpy (vl.plugin, "processes", sizeof (vl.plugin));
612         sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
613         sstrncpy (vl.type, "ps_state", sizeof (vl.type));
614         sstrncpy (vl.type_instance, state, sizeof (vl.type_instance));
615
616         plugin_dispatch_values (&vl);
617 }
618
619 /* submit info about specific process (e.g.: memory taken, cpu usage, etc..) */
620 static void ps_submit_proc_list (procstat_t *ps)
621 {
622         value_t values[2];
623         value_list_t vl = VALUE_LIST_INIT;
624
625         vl.values = values;
626         vl.values_len = 2;
627         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
628         sstrncpy (vl.plugin, "processes", sizeof (vl.plugin));
629         sstrncpy (vl.plugin_instance, ps->name, sizeof (vl.plugin_instance));
630
631         sstrncpy (vl.type, "ps_vm", sizeof (vl.type));
632         vl.values[0].gauge = ps->vmem_size;
633         vl.values_len = 1;
634         plugin_dispatch_values (&vl);
635
636         sstrncpy (vl.type, "ps_rss", sizeof (vl.type));
637         vl.values[0].gauge = ps->vmem_rss;
638         vl.values_len = 1;
639         plugin_dispatch_values (&vl);
640
641         sstrncpy (vl.type, "ps_stacksize", sizeof (vl.type));
642         vl.values[0].gauge = ps->stack_size;
643         vl.values_len = 1;
644         plugin_dispatch_values (&vl);
645
646         sstrncpy (vl.type, "ps_cputime", sizeof (vl.type));
647         vl.values[0].counter = ps->cpu_user_counter;
648         vl.values[1].counter = ps->cpu_system_counter;
649         vl.values_len = 2;
650         plugin_dispatch_values (&vl);
651
652         sstrncpy (vl.type, "ps_count", sizeof (vl.type));
653         vl.values[0].gauge = ps->num_proc;
654         vl.values[1].gauge = ps->num_lwp;
655         vl.values_len = 2;
656         plugin_dispatch_values (&vl);
657
658         sstrncpy (vl.type, "ps_pagefaults", sizeof (vl.type));
659         vl.values[0].counter = ps->vmem_minflt_counter;
660         vl.values[1].counter = ps->vmem_majflt_counter;
661         vl.values_len = 2;
662         plugin_dispatch_values (&vl);
663
664         if ( (ps->io_rchar != -1) && (ps->io_wchar != -1) )
665         {
666                 sstrncpy (vl.type, "ps_disk_octets", sizeof (vl.type));
667                 vl.values[0].derive = ps->io_rchar;
668                 vl.values[1].derive = ps->io_wchar;
669                 vl.values_len = 2;
670                 plugin_dispatch_values (&vl);
671         }
672
673         if ( (ps->io_syscr != -1) && (ps->io_syscw != -1) )
674         {
675                 sstrncpy (vl.type, "ps_disk_ops", sizeof (vl.type));
676                 vl.values[0].derive = ps->io_syscr;
677                 vl.values[1].derive = ps->io_syscw;
678                 vl.values_len = 2;
679                 plugin_dispatch_values (&vl);
680         }
681
682         DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; vmem_rss = %lu; "
683                         "vmem_minflt_counter = %lu; vmem_majflt_counter = %lu; "
684                         "cpu_user_counter = %lu; cpu_system_counter = %lu; "
685                         "io_rchar = %"PRIi64"; io_wchar = %"PRIi64"; "
686                         "io_syscr = %"PRIi64"; io_syscw = %"PRIi64";",
687                         ps->name, ps->num_proc, ps->num_lwp, ps->vmem_rss,
688                         ps->vmem_minflt_counter, ps->vmem_majflt_counter,
689                         ps->cpu_user_counter, ps->cpu_system_counter,
690                         ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw);
691 } /* void ps_submit_proc_list */
692
693 /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
694 #if KERNEL_LINUX
695 static int ps_read_tasks (int pid)
696 {
697         char           dirname[64];
698         DIR           *dh;
699         struct dirent *ent;
700         int count = 0;
701
702         ssnprintf (dirname, sizeof (dirname), "/proc/%i/task", pid);
703
704         if ((dh = opendir (dirname)) == NULL)
705         {
706                 DEBUG ("Failed to open directory `%s'", dirname);
707                 return (-1);
708         }
709
710         while ((ent = readdir (dh)) != NULL)
711         {
712                 if (!isdigit ((int) ent->d_name[0]))
713                         continue;
714                 else
715                         count++;
716         }
717         closedir (dh);
718
719         return ((count >= 1) ? count : 1);
720 } /* int *ps_read_tasks */
721
722 static procstat_t *ps_read_io (int pid, procstat_t *ps)
723 {
724         FILE *fh;
725         char buffer[1024];
726         char filename[64];
727
728         char *fields[8];
729         int numfields;
730
731         ssnprintf (filename, sizeof (filename), "/proc/%i/io", pid);
732         if ((fh = fopen (filename, "r")) == NULL)
733                 return (NULL);
734
735         while (fgets (buffer, 1024, fh) != NULL)
736         {
737                 derive_t *val = NULL;
738                 long long tmp;
739                 char *endptr;
740
741                 if (strncasecmp (buffer, "rchar:", 6) == 0)
742                         val = &(ps->io_rchar);
743                 else if (strncasecmp (buffer, "wchar:", 6) == 0)
744                         val = &(ps->io_wchar);
745                 else if (strncasecmp (buffer, "syscr:", 6) == 0)
746                         val = &(ps->io_syscr);
747                 else if (strncasecmp (buffer, "syscw:", 6) == 0)
748                         val = &(ps->io_syscw);
749                 else
750                         continue;
751
752                 numfields = strsplit (buffer, fields, 8);
753
754                 if (numfields < 2)
755                         continue;
756
757                 errno = 0;
758                 endptr = NULL;
759                 tmp = strtoll (fields[1], &endptr, /* base = */ 10);
760                 if ((errno != 0) || (endptr == fields[1]))
761                         *val = -1;
762                 else
763                         *val = (derive_t) tmp;
764         } /* while (fgets) */
765
766         if (fclose (fh))
767         {
768                 char errbuf[1024];
769                 WARNING ("processes: fclose: %s",
770                                 sstrerror (errno, errbuf, sizeof (errbuf)));
771         }
772
773         return (ps);
774 } /* procstat_t *ps_read_io */
775
776 int ps_read_process (int pid, procstat_t *ps, char *state)
777 {
778         char  filename[64];
779         char  buffer[1024];
780
781         char *fields[64];
782         char  fields_len;
783
784         int   i;
785
786         int   ppid;
787         int   name_len;
788
789         long long unsigned cpu_user_counter;
790         long long unsigned cpu_system_counter;
791         long long unsigned vmem_size;
792         long long unsigned vmem_rss;
793         long long unsigned stack_size;
794
795         memset (ps, 0, sizeof (procstat_t));
796
797         ssnprintf (filename, sizeof (filename), "/proc/%i/stat", pid);
798
799         i = read_file_contents (filename, buffer, sizeof(buffer) - 1);
800         if (i <= 0)
801                 return (-1);
802         buffer[i] = 0;
803
804         fields_len = strsplit (buffer, fields, 64);
805         if (fields_len < 24)
806         {
807                 DEBUG ("processes plugin: ps_read_process (pid = %i):"
808                                 " `%s' has only %i fields..",
809                                 (int) pid, filename, fields_len);
810                 return (-1);
811         }
812
813         /* copy the name, strip brackets in the process */
814         name_len = strlen (fields[1]) - 2;
815         if ((fields[1][0] != '(') || (fields[1][name_len + 1] != ')'))
816         {
817                 DEBUG ("No brackets found in process name: `%s'", fields[1]);
818                 return (-1);
819         }
820         fields[1] = fields[1] + 1;
821         fields[1][name_len] = '\0';
822         strncpy (ps->name, fields[1], PROCSTAT_NAME_LEN);
823
824         ppid = atoi (fields[3]);
825
826         *state = fields[2][0];
827
828         if (*state == 'Z')
829         {
830                 ps->num_lwp  = 0;
831                 ps->num_proc = 0;
832         }
833         else
834         {
835                 if ( (ps->num_lwp = ps_read_tasks (pid)) == -1 )
836                 {
837                         /* returns -1 => kernel 2.4 */
838                         ps->num_lwp = 1;
839                 }
840                 ps->num_proc = 1;
841         }
842
843         /* Leave the rest at zero if this is only a zombi */
844         if (ps->num_proc == 0)
845         {
846                 DEBUG ("processes plugin: This is only a zombi: pid = %i; "
847                                 "name = %s;", pid, ps->name);
848                 return (0);
849         }
850
851         cpu_user_counter   = atoll (fields[13]);
852         cpu_system_counter = atoll (fields[14]);
853         vmem_size          = atoll (fields[22]);
854         vmem_rss           = atoll (fields[23]);
855         ps->vmem_minflt_counter = atol (fields[9]);
856         ps->vmem_majflt_counter = atol (fields[11]);
857
858         {
859                 unsigned long long stack_start = atoll (fields[27]);
860                 unsigned long long stack_ptr   = atoll (fields[28]);
861
862                 stack_size = (stack_start > stack_ptr)
863                         ? stack_start - stack_ptr
864                         : stack_ptr - stack_start;
865         }
866
867         /* Convert jiffies to useconds */
868         cpu_user_counter   = cpu_user_counter   * 1000000 / CONFIG_HZ;
869         cpu_system_counter = cpu_system_counter * 1000000 / CONFIG_HZ;
870         vmem_rss = vmem_rss * pagesize_g;
871
872         ps->cpu_user_counter = (unsigned long) cpu_user_counter;
873         ps->cpu_system_counter = (unsigned long) cpu_system_counter;
874         ps->vmem_size = (unsigned long) vmem_size;
875         ps->vmem_rss = (unsigned long) vmem_rss;
876         ps->stack_size = (unsigned long) stack_size;
877
878         if ( (ps_read_io (pid, ps)) == NULL)
879         {
880                 /* no io data */
881                 ps->io_rchar = -1;
882                 ps->io_wchar = -1;
883                 ps->io_syscr = -1;
884                 ps->io_syscw = -1;
885
886                 DEBUG("ps_read_process: not get io data for pid %i",pid);
887         }
888
889         /* success */
890         return (0);
891 } /* int ps_read_process (...) */
892
893 static char *ps_get_cmdline (pid_t pid, char *name, char *buf, size_t buf_len)
894 {
895         char  *buf_ptr;
896         size_t len;
897
898         char file[PATH_MAX];
899         int  fd;
900
901         size_t n;
902
903         if ((pid < 1) || (NULL == buf) || (buf_len < 2))
904                 return NULL;
905
906         ssnprintf (file, sizeof (file), "/proc/%u/cmdline", pid);
907
908         fd = open (file, O_RDONLY);
909         if (fd < 0) {
910                 char errbuf[4096];
911                 WARNING ("processes plugin: Failed to open `%s': %s.", file,
912                                 sstrerror (errno, errbuf, sizeof (errbuf)));
913                 return NULL;
914         }
915
916         buf_ptr = buf;
917         len     = buf_len;
918
919         n = 0;
920
921         while (42) {
922                 ssize_t status;
923
924                 status = read (fd, (void *)buf_ptr, len);
925
926                 if (status < 0) {
927                         char errbuf[4096];
928
929                         if ((EAGAIN == errno) || (EINTR == errno))
930                                 continue;
931
932                         WARNING ("processes plugin: Failed to read from `%s': %s.", file,
933                                         sstrerror (errno, errbuf, sizeof (errbuf)));
934                         close (fd);
935                         return NULL;
936                 }
937
938                 n += status;
939
940                 if (status == 0)
941                         break;
942
943                 buf_ptr += status;
944                 len     -= status;
945
946                 if (len <= 0)
947                         break;
948         }
949
950         close (fd);
951
952         if (0 == n) {
953                 /* cmdline not available; e.g. kernel thread, zombie */
954                 if (NULL == name)
955                         return NULL;
956
957                 ssnprintf (buf, buf_len, "[%s]", name);
958                 return buf;
959         }
960
961         assert (n <= buf_len);
962
963         if (n == buf_len)
964                 --n;
965         buf[n] = '\0';
966
967         --n;
968         /* remove trailing whitespace */
969         while ((n > 0) && (isspace (buf[n]) || ('\0' == buf[n]))) {
970                 buf[n] = '\0';
971                 --n;
972         }
973
974         /* arguments are separated by '\0' in /proc/<pid>/cmdline */
975         while (n > 0) {
976                 if ('\0' == buf[n])
977                         buf[n] = ' ';
978                 --n;
979         }
980         return buf;
981 } /* char *ps_get_cmdline (...) */
982
983 static unsigned long read_fork_rate ()
984 {
985         FILE *proc_stat;
986         char buf[1024];
987         unsigned long result = 0;
988         int numfields;
989         char *fields[3];
990
991         proc_stat = fopen("/proc/stat", "r");
992         if (proc_stat == NULL) {
993                 char errbuf[1024];
994                 ERROR ("processes plugin: fopen (/proc/stat) failed: %s",
995                                 sstrerror (errno, errbuf, sizeof (errbuf)));
996                 return ULONG_MAX;
997         }
998
999         while (fgets (buf, sizeof(buf), proc_stat) != NULL)
1000         {
1001                 char *endptr;
1002
1003                 numfields = strsplit(buf, fields, STATIC_ARRAY_SIZE (fields));
1004                 if (numfields != 2)
1005                         continue;
1006
1007                 if (strcmp ("processes", fields[0]) != 0)
1008                         continue;
1009
1010                 errno = 0;
1011                 endptr = NULL;
1012                 result = strtoul(fields[1], &endptr, 10);
1013                 if ((endptr == fields[1]) || (errno != 0)) {
1014                         ERROR ("processes plugin: Cannot parse fork rate: %s",
1015                                         fields[1]);
1016                         result = ULONG_MAX;
1017                         break;
1018                 }
1019
1020                 break;
1021         }
1022
1023         fclose(proc_stat);
1024
1025         return result;
1026 }
1027
1028 static void ps_submit_fork_rate (unsigned long value)
1029 {
1030         value_t values[1];
1031         value_list_t vl = VALUE_LIST_INIT;
1032
1033         values[0].derive = (derive_t) value;
1034
1035         vl.values = values;
1036         vl.values_len = 1;
1037         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
1038         sstrncpy (vl.plugin, "processes", sizeof (vl.plugin));
1039         sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
1040         sstrncpy (vl.type, "fork_rate", sizeof (vl.type));
1041         sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
1042
1043         plugin_dispatch_values (&vl);
1044 }
1045
1046 #endif /* KERNEL_LINUX */
1047
1048 #if HAVE_THREAD_INFO
1049 static int mach_get_task_name (task_t t, int *pid, char *name, size_t name_max_len)
1050 {
1051         int mib[4];
1052
1053         struct kinfo_proc kp;
1054         size_t            kp_size;
1055
1056         mib[0] = CTL_KERN;
1057         mib[1] = KERN_PROC;
1058         mib[2] = KERN_PROC_PID;
1059
1060         if (pid_for_task (t, pid) != KERN_SUCCESS)
1061                 return (-1);
1062         mib[3] = *pid;
1063
1064         kp_size = sizeof (kp);
1065         if (sysctl (mib, 4, &kp, &kp_size, NULL, 0) != 0)
1066                 return (-1);
1067
1068         if (name_max_len > (MAXCOMLEN + 1))
1069                 name_max_len = MAXCOMLEN + 1;
1070
1071         strncpy (name, kp.kp_proc.p_comm, name_max_len - 1);
1072         name[name_max_len - 1] = '\0';
1073
1074         DEBUG ("pid = %i; name = %s;", *pid, name);
1075
1076         /* We don't do the special handling for `p_comm == "LaunchCFMApp"' as
1077          * `top' does it, because it is a lot of work and only used when
1078          * debugging. -octo */
1079
1080         return (0);
1081 }
1082 #endif /* HAVE_THREAD_INFO */
1083 /* ------- end of additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
1084
1085 /* do actual readings from kernel */
1086 static int ps_read (void)
1087 {
1088 #if HAVE_THREAD_INFO
1089         kern_return_t            status;
1090
1091         int                      pset;
1092         processor_set_t          port_pset_priv;
1093
1094         int                      task;
1095         task_array_t             task_list;
1096         mach_msg_type_number_t   task_list_len;
1097
1098         int                      task_pid;
1099         char                     task_name[MAXCOMLEN + 1];
1100
1101         int                      thread;
1102         thread_act_array_t       thread_list;
1103         mach_msg_type_number_t   thread_list_len;
1104         thread_basic_info_data_t thread_data;
1105         mach_msg_type_number_t   thread_data_len;
1106
1107         int running  = 0;
1108         int sleeping = 0;
1109         int zombies  = 0;
1110         int stopped  = 0;
1111         int blocked  = 0;
1112
1113         procstat_t *ps;
1114         procstat_entry_t pse;
1115
1116         ps_list_reset ();
1117
1118         /*
1119          * The Mach-concept is a little different from the traditional UNIX
1120          * concept: All the work is done in threads. Threads are contained in
1121          * `tasks'. Therefore, `task status' doesn't make much sense, since
1122          * it's actually a `thread status'.
1123          * Tasks are assigned to sets of processors, so that's where you go to
1124          * get a list.
1125          */
1126         for (pset = 0; pset < pset_list_len; pset++)
1127         {
1128                 if ((status = host_processor_set_priv (port_host_self,
1129                                                 pset_list[pset],
1130                                                 &port_pset_priv)) != KERN_SUCCESS)
1131                 {
1132                         ERROR ("host_processor_set_priv failed: %s\n",
1133                                         mach_error_string (status));
1134                         continue;
1135                 }
1136
1137                 if ((status = processor_set_tasks (port_pset_priv,
1138                                                 &task_list,
1139                                                 &task_list_len)) != KERN_SUCCESS)
1140                 {
1141                         ERROR ("processor_set_tasks failed: %s\n",
1142                                         mach_error_string (status));
1143                         mach_port_deallocate (port_task_self, port_pset_priv);
1144                         continue;
1145                 }
1146
1147                 for (task = 0; task < task_list_len; task++)
1148                 {
1149                         ps = NULL;
1150                         if (mach_get_task_name (task_list[task],
1151                                                 &task_pid,
1152                                                 task_name, PROCSTAT_NAME_LEN) == 0)
1153                         {
1154                                 /* search for at least one match */
1155                                 for (ps = list_head_g; ps != NULL; ps = ps->next)
1156                                         /* FIXME: cmdline should be here instead of NULL */
1157                                         if (ps_list_match (task_name, NULL, ps) == 1)
1158                                                 break;
1159                         }
1160
1161                         /* Collect more detailed statistics for this process */
1162                         if (ps != NULL)
1163                         {
1164                                 task_basic_info_data_t        task_basic_info;
1165                                 mach_msg_type_number_t        task_basic_info_len;
1166                                 task_events_info_data_t       task_events_info;
1167                                 mach_msg_type_number_t        task_events_info_len;
1168                                 task_absolutetime_info_data_t task_absolutetime_info;
1169                                 mach_msg_type_number_t        task_absolutetime_info_len;
1170
1171                                 memset (&pse, '\0', sizeof (pse));
1172                                 pse.id = task_pid;
1173
1174                                 task_basic_info_len = TASK_BASIC_INFO_COUNT;
1175                                 status = task_info (task_list[task],
1176                                                 TASK_BASIC_INFO,
1177                                                 (task_info_t) &task_basic_info,
1178                                                 &task_basic_info_len);
1179                                 if (status != KERN_SUCCESS)
1180                                 {
1181                                         ERROR ("task_info failed: %s",
1182                                                         mach_error_string (status));
1183                                         continue; /* with next thread_list */
1184                                 }
1185
1186                                 task_events_info_len = TASK_EVENTS_INFO_COUNT;
1187                                 status = task_info (task_list[task],
1188                                                 TASK_EVENTS_INFO,
1189                                                 (task_info_t) &task_events_info,
1190                                                 &task_events_info_len);
1191                                 if (status != KERN_SUCCESS)
1192                                 {
1193                                         ERROR ("task_info failed: %s",
1194                                                         mach_error_string (status));
1195                                         continue; /* with next thread_list */
1196                                 }
1197
1198                                 task_absolutetime_info_len = TASK_ABSOLUTETIME_INFO_COUNT;
1199                                 status = task_info (task_list[task],
1200                                                 TASK_ABSOLUTETIME_INFO,
1201                                                 (task_info_t) &task_absolutetime_info,
1202                                                 &task_absolutetime_info_len);
1203                                 if (status != KERN_SUCCESS)
1204                                 {
1205                                         ERROR ("task_info failed: %s",
1206                                                         mach_error_string (status));
1207                                         continue; /* with next thread_list */
1208                                 }
1209
1210                                 pse.num_proc++;
1211                                 pse.vmem_rss = task_basic_info.resident_size;
1212
1213                                 pse.vmem_minflt_counter = task_events_info.cow_faults;
1214                                 pse.vmem_majflt_counter = task_events_info.faults;
1215
1216                                 pse.cpu_user_counter = task_absolutetime_info.total_user;
1217                                 pse.cpu_system_counter = task_absolutetime_info.total_system;
1218                         }
1219
1220                         status = task_threads (task_list[task], &thread_list,
1221                                         &thread_list_len);
1222                         if (status != KERN_SUCCESS)
1223                         {
1224                                 /* Apple's `top' treats this case a zombie. It
1225                                  * makes sense to some extend: A `zombie'
1226                                  * thread is nonsense, since the task/process
1227                                  * is dead. */
1228                                 zombies++;
1229                                 DEBUG ("task_threads failed: %s",
1230                                                 mach_error_string (status));
1231                                 if (task_list[task] != port_task_self)
1232                                         mach_port_deallocate (port_task_self,
1233                                                         task_list[task]);
1234                                 continue; /* with next task_list */
1235                         }
1236
1237                         for (thread = 0; thread < thread_list_len; thread++)
1238                         {
1239                                 thread_data_len = THREAD_BASIC_INFO_COUNT;
1240                                 status = thread_info (thread_list[thread],
1241                                                 THREAD_BASIC_INFO,
1242                                                 (thread_info_t) &thread_data,
1243                                                 &thread_data_len);
1244                                 if (status != KERN_SUCCESS)
1245                                 {
1246                                         ERROR ("thread_info failed: %s",
1247                                                         mach_error_string (status));
1248                                         if (task_list[task] != port_task_self)
1249                                                 mach_port_deallocate (port_task_self,
1250                                                                 thread_list[thread]);
1251                                         continue; /* with next thread_list */
1252                                 }
1253
1254                                 if (ps != NULL)
1255                                         pse.num_lwp++;
1256
1257                                 switch (thread_data.run_state)
1258                                 {
1259                                         case TH_STATE_RUNNING:
1260                                                 running++;
1261                                                 break;
1262                                         case TH_STATE_STOPPED:
1263                                         /* What exactly is `halted'? */
1264                                         case TH_STATE_HALTED:
1265                                                 stopped++;
1266                                                 break;
1267                                         case TH_STATE_WAITING:
1268                                                 sleeping++;
1269                                                 break;
1270                                         case TH_STATE_UNINTERRUPTIBLE:
1271                                                 blocked++;
1272                                                 break;
1273                                         /* There is no `zombie' case here,
1274                                          * since there are no zombie-threads.
1275                                          * There's only zombie tasks, which are
1276                                          * handled above. */
1277                                         default:
1278                                                 WARNING ("Unknown thread status: %i",
1279                                                                 thread_data.run_state);
1280                                                 break;
1281                                 } /* switch (thread_data.run_state) */
1282
1283                                 if (task_list[task] != port_task_self)
1284                                 {
1285                                         status = mach_port_deallocate (port_task_self,
1286                                                         thread_list[thread]);
1287                                         if (status != KERN_SUCCESS)
1288                                                 ERROR ("mach_port_deallocate failed: %s",
1289                                                                 mach_error_string (status));
1290                                 }
1291                         } /* for (thread_list) */
1292
1293                         if ((status = vm_deallocate (port_task_self,
1294                                                         (vm_address_t) thread_list,
1295                                                         thread_list_len * sizeof (thread_act_t)))
1296                                         != KERN_SUCCESS)
1297                         {
1298                                 ERROR ("vm_deallocate failed: %s",
1299                                                 mach_error_string (status));
1300                         }
1301                         thread_list = NULL;
1302                         thread_list_len = 0;
1303
1304                         /* Only deallocate the task port, if it isn't our own.
1305                          * Don't know what would happen in that case, but this
1306                          * is what Apple's top does.. ;) */
1307                         if (task_list[task] != port_task_self)
1308                         {
1309                                 status = mach_port_deallocate (port_task_self,
1310                                                 task_list[task]);
1311                                 if (status != KERN_SUCCESS)
1312                                         ERROR ("mach_port_deallocate failed: %s",
1313                                                         mach_error_string (status));
1314                         }
1315
1316                         if (ps != NULL)
1317                                 /* FIXME: cmdline should be here instead of NULL */
1318                                 ps_list_add (task_name, NULL, &pse);
1319                 } /* for (task_list) */
1320
1321                 if ((status = vm_deallocate (port_task_self,
1322                                 (vm_address_t) task_list,
1323                                 task_list_len * sizeof (task_t))) != KERN_SUCCESS)
1324                 {
1325                         ERROR ("vm_deallocate failed: %s",
1326                                         mach_error_string (status));
1327                 }
1328                 task_list = NULL;
1329                 task_list_len = 0;
1330
1331                 if ((status = mach_port_deallocate (port_task_self, port_pset_priv))
1332                                 != KERN_SUCCESS)
1333                 {
1334                         ERROR ("mach_port_deallocate failed: %s",
1335                                         mach_error_string (status));
1336                 }
1337         } /* for (pset_list) */
1338
1339         ps_submit_state ("running", running);
1340         ps_submit_state ("sleeping", sleeping);
1341         ps_submit_state ("zombies", zombies);
1342         ps_submit_state ("stopped", stopped);
1343         ps_submit_state ("blocked", blocked);
1344
1345         for (ps = list_head_g; ps != NULL; ps = ps->next)
1346                 ps_submit_proc_list (ps);
1347 /* #endif HAVE_THREAD_INFO */
1348
1349 #elif KERNEL_LINUX
1350         int running  = 0;
1351         int sleeping = 0;
1352         int zombies  = 0;
1353         int stopped  = 0;
1354         int paging   = 0;
1355         int blocked  = 0;
1356
1357         struct dirent *ent;
1358         DIR           *proc;
1359         int            pid;
1360
1361         char cmdline[ARG_MAX];
1362
1363         int        status;
1364         procstat_t ps;
1365         procstat_entry_t pse;
1366         char       state;
1367
1368         unsigned long fork_rate;
1369
1370         procstat_t *ps_ptr;
1371
1372         running = sleeping = zombies = stopped = paging = blocked = 0;
1373         ps_list_reset ();
1374
1375         if ((proc = opendir ("/proc")) == NULL)
1376         {
1377                 char errbuf[1024];
1378                 ERROR ("Cannot open `/proc': %s",
1379                                 sstrerror (errno, errbuf, sizeof (errbuf)));
1380                 return (-1);
1381         }
1382
1383         while ((ent = readdir (proc)) != NULL)
1384         {
1385                 if (!isdigit (ent->d_name[0]))
1386                         continue;
1387
1388                 if ((pid = atoi (ent->d_name)) < 1)
1389                         continue;
1390
1391                 status = ps_read_process (pid, &ps, &state);
1392                 if (status != 0)
1393                 {
1394                         DEBUG ("ps_read_process failed: %i", status);
1395                         continue;
1396                 }
1397
1398                 pse.id       = pid;
1399                 pse.age      = 0;
1400
1401                 pse.num_proc   = ps.num_proc;
1402                 pse.num_lwp    = ps.num_lwp;
1403                 pse.vmem_size  = ps.vmem_size;
1404                 pse.vmem_rss   = ps.vmem_rss;
1405                 pse.stack_size = ps.stack_size;
1406
1407                 pse.vmem_minflt = 0;
1408                 pse.vmem_minflt_counter = ps.vmem_minflt_counter;
1409                 pse.vmem_majflt = 0;
1410                 pse.vmem_majflt_counter = ps.vmem_majflt_counter;
1411
1412                 pse.cpu_user = 0;
1413                 pse.cpu_user_counter = ps.cpu_user_counter;
1414                 pse.cpu_system = 0;
1415                 pse.cpu_system_counter = ps.cpu_system_counter;
1416
1417                 pse.io_rchar = ps.io_rchar;
1418                 pse.io_wchar = ps.io_wchar;
1419                 pse.io_syscr = ps.io_syscr;
1420                 pse.io_syscw = ps.io_syscw;
1421
1422                 switch (state)
1423                 {
1424                         case 'R': running++;  break;
1425                         case 'S': sleeping++; break;
1426                         case 'D': blocked++;  break;
1427                         case 'Z': zombies++;  break;
1428                         case 'T': stopped++;  break;
1429                         case 'W': paging++;   break;
1430                 }
1431
1432                 ps_list_add (ps.name,
1433                                 ps_get_cmdline (pid, ps.name, cmdline, sizeof (cmdline)),
1434                                 &pse);
1435         }
1436
1437         closedir (proc);
1438
1439         ps_submit_state ("running",  running);
1440         ps_submit_state ("sleeping", sleeping);
1441         ps_submit_state ("zombies",  zombies);
1442         ps_submit_state ("stopped",  stopped);
1443         ps_submit_state ("paging",   paging);
1444         ps_submit_state ("blocked",  blocked);
1445
1446         for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
1447                 ps_submit_proc_list (ps_ptr);
1448
1449         fork_rate = read_fork_rate();
1450         if (fork_rate != ULONG_MAX)
1451                 ps_submit_fork_rate(fork_rate);
1452 /* #endif KERNEL_LINUX */
1453
1454 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
1455         int running  = 0;
1456         int sleeping = 0;
1457         int zombies  = 0;
1458         int stopped  = 0;
1459         int blocked  = 0;
1460         int idle     = 0;
1461         int wait     = 0;
1462
1463         kvm_t *kd;
1464         char errbuf[1024];
1465         char cmdline[ARG_MAX];
1466         char *cmdline_ptr;
1467         struct kinfo_proc *procs;          /* array of processes */
1468         char **argv;
1469         int count;                         /* returns number of processes */
1470         int i;
1471
1472         procstat_t *ps_ptr;
1473         procstat_entry_t pse;
1474
1475         ps_list_reset ();
1476
1477         /* Open the kvm interface, get a descriptor */
1478         kd = kvm_open (NULL, NULL, NULL, 0, errbuf);
1479         if (kd == NULL)
1480         {
1481                 ERROR ("processes plugin: Cannot open kvm interface: %s",
1482                                 errbuf);
1483                 return (0);
1484         }
1485
1486         /* Get the list of processes. */
1487         procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, &count);
1488         if (procs == NULL)
1489         {
1490                 kvm_close (kd);
1491                 ERROR ("processes plugin: Cannot get kvm processes list: %s",
1492                                 kvm_geterr(kd));
1493                 return (0);
1494         }
1495
1496         /* Iterate through the processes in kinfo_proc */
1497         for (i = 0; i < count; i++)
1498         {
1499                 /* retrieve the arguments */
1500                 cmdline[0] = 0;
1501                 cmdline_ptr = NULL;
1502
1503                 argv = kvm_getargv (kd, (const struct kinfo_proc *) &(procs[i]), 0);
1504                 if (argv != NULL)
1505                 {
1506                         int status;
1507                         int argc;
1508
1509                         argc = 0;
1510                         while (argv[argc] != NULL)
1511                                 argc++;
1512
1513                         status = strjoin (cmdline, sizeof (cmdline),
1514                                         argv, argc, " ");
1515
1516                         if (status < 0)
1517                         {
1518                                 WARNING ("processes plugin: Command line did "
1519                                                 "not fit into buffer.");
1520                         }
1521                         else
1522                         {
1523                                 cmdline_ptr = &cmdline[0];
1524                         }
1525                 }
1526
1527                 pse.id       = procs[i].ki_pid;
1528                 pse.age      = 0;
1529
1530                 pse.num_proc = 1;
1531                 pse.num_lwp  = procs[i].ki_numthreads;
1532
1533                 pse.vmem_size = procs[i].ki_size;
1534                 pse.vmem_rss = procs[i].ki_rssize * getpagesize();
1535                 pse.stack_size = procs[i].ki_ssize * getpagesize();
1536                 pse.vmem_minflt = 0;
1537                 pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
1538                 pse.vmem_majflt = 0;
1539                 pse.vmem_majflt_counter = procs[i].ki_rusage.ru_majflt;
1540
1541                 pse.cpu_user = 0;
1542                 pse.cpu_user_counter = procs[i].ki_rusage.ru_utime.tv_sec
1543                         * 1000
1544                         + procs[i].ki_rusage.ru_utime.tv_usec;
1545                 pse.cpu_system = 0;
1546                 pse.cpu_system_counter = procs[i].ki_rusage.ru_stime.tv_sec
1547                         * 1000
1548                         + procs[i].ki_rusage.ru_stime.tv_usec;
1549
1550                 /* no io data */
1551                 pse.io_rchar = -1;
1552                 pse.io_wchar = -1;
1553                 pse.io_syscr = -1;
1554                 pse.io_syscw = -1;
1555
1556                 switch (procs[i].ki_stat)
1557                 {
1558                         case SSTOP:     stopped++;      break;
1559                         case SSLEEP:    sleeping++;     break;
1560                         case SRUN:      running++;      break;
1561                         case SIDL:      idle++;         break;
1562                         case SWAIT:     wait++;         break;
1563                         case SLOCK:     blocked++;      break;
1564                         case SZOMB:     zombies++;      break;
1565                 }
1566
1567                 ps_list_add (procs[i].ki_comm, cmdline_ptr, &pse);
1568         }
1569
1570         kvm_close(kd);
1571
1572         ps_submit_state ("running",  running);
1573         ps_submit_state ("sleeping", sleeping);
1574         ps_submit_state ("zombies",  zombies);
1575         ps_submit_state ("stopped",  stopped);
1576         ps_submit_state ("blocked",  blocked);
1577         ps_submit_state ("idle",     idle);
1578         ps_submit_state ("wait",     wait);
1579
1580         for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
1581                 ps_submit_proc_list (ps_ptr);
1582 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
1583
1584 #elif HAVE_PROCINFO_H
1585         /* AIX */
1586         int running  = 0;
1587         int sleeping = 0;
1588         int zombies  = 0;
1589         int stopped  = 0;
1590         int paging   = 0;
1591         int blocked  = 0;
1592
1593         pid_t pindex = 0;
1594         int nprocs;
1595
1596         procstat_t *ps;
1597         procstat_entry_t pse;
1598
1599         ps_list_reset ();
1600         while ((nprocs = getprocs64 (procentry, sizeof(struct procentry64),
1601                                         /* fdsinfo = */ NULL, sizeof(struct fdsinfo64),
1602                                         &pindex, MAXPROCENTRY)) > 0)
1603         {
1604                 int i;
1605
1606                 for (i = 0; i < nprocs; i++)
1607                 {
1608                         tid64_t thindex;
1609                         int nthreads;
1610                         char arglist[MAXARGLN+1];
1611                         char *cargs;
1612                         char *cmdline;
1613
1614                         if (procentry[i].pi_state == SNONE) continue;
1615                         /* if (procentry[i].pi_state == SZOMB)  FIXME */
1616
1617                         cmdline = procentry[i].pi_comm;
1618                         cargs = procentry[i].pi_comm;
1619                         if ( procentry[i].pi_flags & SKPROC )
1620                         {
1621                                 if (procentry[i].pi_pid == 0)
1622                                         cmdline = "swapper";
1623                                 cargs = cmdline;
1624                         }
1625                         else
1626                         {
1627                                 if (getargs(&procentry[i], sizeof(struct procentry64), arglist, MAXARGLN) >= 0)
1628                                 {
1629                                         int n;
1630
1631                                         n = -1;
1632                                         while (++n < MAXARGLN)
1633                                         {
1634                                                 if (arglist[n] == '\0')
1635                                                 {
1636                                                         if (arglist[n+1] == '\0')
1637                                                                 break;
1638                                                         arglist[n] = ' ';
1639                                                 }
1640                                         }
1641                                         cargs = arglist;
1642                                 }
1643                         }
1644
1645                         pse.id       = procentry[i].pi_pid;
1646                         pse.age      = 0;
1647                         pse.num_lwp  = procentry[i].pi_thcount;
1648                         pse.num_proc = 1;
1649
1650                         thindex=0;
1651                         while ((nthreads = getthrds64(procentry[i].pi_pid,
1652                                                         thrdentry, sizeof(struct thrdentry64),
1653                                                         &thindex, MAXTHRDENTRY)) > 0)
1654                         {
1655                                 int j;
1656
1657                                 for (j=0; j< nthreads; j++)
1658                                 {
1659                                         switch (thrdentry[j].ti_state)
1660                                         {
1661                                                 /* case TSNONE: break; */
1662                                                 case TSIDL:     blocked++;      break; /* FIXME is really blocked */
1663                                                 case TSRUN:     running++;      break;
1664                                                 case TSSLEEP:   sleeping++;     break;
1665                                                 case TSSWAP:    paging++;       break;
1666                                                 case TSSTOP:    stopped++;      break;
1667                                                 case TSZOMB:    zombies++;      break;
1668                                         }
1669                                 }
1670                                 if (nthreads < MAXTHRDENTRY)
1671                                         break;
1672                         }
1673
1674                         pse.cpu_user = 0;
1675                         /* tv_usec is nanosec ??? */
1676                         pse.cpu_user_counter = procentry[i].pi_ru.ru_utime.tv_sec * 1000000 +
1677                                 procentry[i].pi_ru.ru_utime.tv_usec / 1000;
1678
1679                         pse.cpu_system = 0;
1680                         /* tv_usec is nanosec ??? */
1681                         pse.cpu_system_counter = procentry[i].pi_ru.ru_stime.tv_sec * 1000000 +
1682                                 procentry[i].pi_ru.ru_stime.tv_usec / 1000;
1683
1684                         pse.vmem_minflt = 0;
1685                         pse.vmem_minflt_counter = procentry[i].pi_minflt;
1686                         pse.vmem_majflt = 0;
1687                         pse.vmem_majflt_counter = procentry[i].pi_majflt;
1688
1689                         pse.vmem_size = procentry[i].pi_tsize + procentry[i].pi_dvm * pagesize;
1690                         pse.vmem_rss = (procentry[i].pi_drss + procentry[i].pi_trss) * pagesize;
1691                         pse.stack_size =  0;
1692
1693                         ps_list_add (cmdline, cargs, &pse);
1694                 } /* for (i = 0 .. nprocs) */
1695
1696                 if (nprocs < MAXPROCENTRY)
1697                         break;
1698         } /* while (getprocs64() > 0) */
1699         ps_submit_state ("running",  running);
1700         ps_submit_state ("sleeping", sleeping);
1701         ps_submit_state ("zombies",  zombies);
1702         ps_submit_state ("stopped",  stopped);
1703         ps_submit_state ("paging",   paging);
1704         ps_submit_state ("blocked",  blocked);
1705
1706         for (ps = list_head_g; ps != NULL; ps = ps->next)
1707                 ps_submit_proc_list (ps);
1708 #endif /* HAVE_PROCINFO_H */
1709
1710         return (0);
1711 } /* int ps_read */
1712
1713 void module_register (void)
1714 {
1715         plugin_register_config ("processes", ps_config,
1716                         config_keys, config_keys_num);
1717         plugin_register_init ("processes", ps_init);
1718         plugin_register_read ("processes", ps_read);
1719 } /* void module_register */