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