Merge commit 'trenkel/st/python'
[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         long io_rchar;
154         long io_wchar;
155         long io_syscr;
156         long 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         long io_rchar;
183         long io_wchar;
184         long io_syscr;
185         long 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 dispabled 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].counter = ps->io_rchar;
668                 vl.values[1].counter = 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].counter = ps->io_syscr;
677                 vl.values[1].counter = 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 = %ld; io_wchar = %ld; "
686                         "io_syscr = %ld; io_syscw = %ld;",
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                 long *val = NULL;
738
739                 if (strncasecmp (buffer, "rchar:", 6) == 0)
740                         val = &(ps->io_rchar);
741                 else if (strncasecmp (buffer, "wchar:", 6) == 0)
742                         val = &(ps->io_wchar);
743                 else if (strncasecmp (buffer, "syscr:", 6) == 0)
744                         val = &(ps->io_syscr);
745                 else if (strncasecmp (buffer, "syscw:", 6) == 0)
746                         val = &(ps->io_syscw);
747                 else
748                         continue;
749
750                 numfields = strsplit (buffer, fields, 8);
751
752                 if (numfields < 2)
753                         continue;
754
755                 *val = atol (fields[1]);
756         }
757
758         if (fclose (fh))
759         {
760                 char errbuf[1024];
761                 WARNING ("processes: fclose: %s",
762                                 sstrerror (errno, errbuf, sizeof (errbuf)));
763         }
764
765         return (ps);
766 } /* procstat_t *ps_read_io */
767
768 int ps_read_process (int pid, procstat_t *ps, char *state)
769 {
770         char  filename[64];
771         char  buffer[1024];
772
773         char *fields[64];
774         char  fields_len;
775
776         int   i;
777
778         int   ppid;
779         int   name_len;
780
781         long long unsigned cpu_user_counter;
782         long long unsigned cpu_system_counter;
783         long long unsigned vmem_size;
784         long long unsigned vmem_rss;
785         long long unsigned stack_size;
786
787         memset (ps, 0, sizeof (procstat_t));
788
789         ssnprintf (filename, sizeof (filename), "/proc/%i/stat", pid);
790
791         i = read_file_contents (filename, buffer, sizeof(buffer) - 1);
792         if (i <= 0)
793                 return (-1);
794         buffer[i] = 0;
795
796         fields_len = strsplit (buffer, fields, 64);
797         if (fields_len < 24)
798         {
799                 DEBUG ("processes plugin: ps_read_process (pid = %i):"
800                                 " `%s' has only %i fields..",
801                                 (int) pid, filename, fields_len);
802                 return (-1);
803         }
804
805         /* copy the name, strip brackets in the process */
806         name_len = strlen (fields[1]) - 2;
807         if ((fields[1][0] != '(') || (fields[1][name_len + 1] != ')'))
808         {
809                 DEBUG ("No brackets found in process name: `%s'", fields[1]);
810                 return (-1);
811         }
812         fields[1] = fields[1] + 1;
813         fields[1][name_len] = '\0';
814         strncpy (ps->name, fields[1], PROCSTAT_NAME_LEN);
815
816         ppid = atoi (fields[3]);
817
818         *state = fields[2][0];
819
820         if (*state == 'Z')
821         {
822                 ps->num_lwp  = 0;
823                 ps->num_proc = 0;
824         }
825         else
826         {
827                 if ( (ps->num_lwp = ps_read_tasks (pid)) == -1 )
828                 {
829                         /* returns -1 => kernel 2.4 */
830                         ps->num_lwp = 1;
831                 }
832                 ps->num_proc = 1;
833         }
834
835         /* Leave the rest at zero if this is only a zombi */
836         if (ps->num_proc == 0)
837         {
838                 DEBUG ("processes plugin: This is only a zombi: pid = %i; "
839                                 "name = %s;", pid, ps->name);
840                 return (0);
841         }
842
843         cpu_user_counter   = atoll (fields[13]);
844         cpu_system_counter = atoll (fields[14]);
845         vmem_size          = atoll (fields[22]);
846         vmem_rss           = atoll (fields[23]);
847         ps->vmem_minflt_counter = atol (fields[9]);
848         ps->vmem_majflt_counter = atol (fields[11]);
849
850         {
851                 unsigned long long stack_start = atoll (fields[27]);
852                 unsigned long long stack_ptr   = atoll (fields[28]);
853
854                 stack_size = (stack_start > stack_ptr)
855                         ? stack_start - stack_ptr
856                         : stack_ptr - stack_start;
857         }
858
859         /* Convert jiffies to useconds */
860         cpu_user_counter   = cpu_user_counter   * 1000000 / CONFIG_HZ;
861         cpu_system_counter = cpu_system_counter * 1000000 / CONFIG_HZ;
862         vmem_rss = vmem_rss * pagesize_g;
863
864         ps->cpu_user_counter = (unsigned long) cpu_user_counter;
865         ps->cpu_system_counter = (unsigned long) cpu_system_counter;
866         ps->vmem_size = (unsigned long) vmem_size;
867         ps->vmem_rss = (unsigned long) vmem_rss;
868         ps->stack_size = (unsigned long) stack_size;
869
870         if ( (ps_read_io (pid, ps)) == NULL)
871         {
872                 /* no io data */
873                 ps->io_rchar = -1;
874                 ps->io_wchar = -1;
875                 ps->io_syscr = -1;
876                 ps->io_syscw = -1;
877
878                 DEBUG("ps_read_process: not get io data for pid %i",pid);
879         }
880
881         /* success */
882         return (0);
883 } /* int ps_read_process (...) */
884
885 static char *ps_get_cmdline (pid_t pid, char *name, char *buf, size_t buf_len)
886 {
887         char  *buf_ptr;
888         size_t len;
889
890         char file[PATH_MAX];
891         int  fd;
892
893         size_t n;
894
895         if ((pid < 1) || (NULL == buf) || (buf_len < 2))
896                 return NULL;
897
898         ssnprintf (file, sizeof (file), "/proc/%u/cmdline", pid);
899
900         fd = open (file, O_RDONLY);
901         if (fd < 0) {
902                 char errbuf[4096];
903                 WARNING ("processes plugin: Failed to open `%s': %s.", file,
904                                 sstrerror (errno, errbuf, sizeof (errbuf)));
905                 return NULL;
906         }
907
908         buf_ptr = buf;
909         len     = buf_len;
910
911         n = 0;
912
913         while (42) {
914                 ssize_t status;
915
916                 status = read (fd, (void *)buf_ptr, len);
917
918                 if (status < 0) {
919                         char errbuf[4096];
920
921                         if ((EAGAIN == errno) || (EINTR == errno))
922                                 continue;
923
924                         WARNING ("processes plugin: Failed to read from `%s': %s.", file,
925                                         sstrerror (errno, errbuf, sizeof (errbuf)));
926                         close (fd);
927                         return NULL;
928                 }
929
930                 n += status;
931
932                 if (status == 0)
933                         break;
934
935                 buf_ptr += status;
936                 len     -= status;
937
938                 if (len <= 0)
939                         break;
940         }
941
942         close (fd);
943
944         if (0 == n) {
945                 /* cmdline not available; e.g. kernel thread, zombie */
946                 if (NULL == name)
947                         return NULL;
948
949                 ssnprintf (buf, buf_len, "[%s]", name);
950                 return buf;
951         }
952
953         assert (n <= buf_len);
954
955         if (n == buf_len)
956                 --n;
957         buf[n] = '\0';
958
959         --n;
960         /* remove trailing whitespace */
961         while ((n > 0) && (isspace (buf[n]) || ('\0' == buf[n]))) {
962                 buf[n] = '\0';
963                 --n;
964         }
965
966         /* arguments are separated by '\0' in /proc/<pid>/cmdline */
967         while (n > 0) {
968                 if ('\0' == buf[n])
969                         buf[n] = ' ';
970                 --n;
971         }
972         return buf;
973 } /* char *ps_get_cmdline (...) */
974
975 static unsigned long read_fork_rate ()
976 {
977         FILE *proc_stat;
978         char buf[1024];
979         unsigned long result = 0;
980         int numfields;
981         char *fields[3];
982
983         proc_stat = fopen("/proc/stat", "r");
984         if (proc_stat == NULL) {
985                 char errbuf[1024];
986                 ERROR ("processes plugin: fopen (/proc/stat) failed: %s",
987                                 sstrerror (errno, errbuf, sizeof (errbuf)));
988                 return ULONG_MAX;
989         }
990
991         while (fgets (buf, sizeof(buf), proc_stat) != NULL)
992         {
993                 char *endptr;
994
995                 numfields = strsplit(buf, fields, STATIC_ARRAY_SIZE (fields));
996                 if (numfields != 2)
997                         continue;
998
999                 if (strcmp ("processes", fields[0]) != 0)
1000                         continue;
1001
1002                 errno = 0;
1003                 endptr = NULL;
1004                 result = strtoul(fields[1], &endptr, 10);
1005                 if ((endptr == fields[1]) || (errno != 0)) {
1006                         ERROR ("processes plugin: Cannot parse fork rate: %s",
1007                                         fields[1]);
1008                         result = ULONG_MAX;
1009                         break;
1010                 }
1011
1012                 break;
1013         }
1014
1015         fclose(proc_stat);
1016
1017         return result;
1018 }
1019
1020 static void ps_submit_fork_rate (unsigned long value)
1021 {
1022         value_t values[1];
1023         value_list_t vl = VALUE_LIST_INIT;
1024
1025         values[0].derive = (derive_t) value;
1026
1027         vl.values = values;
1028         vl.values_len = 1;
1029         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
1030         sstrncpy (vl.plugin, "processes", sizeof (vl.plugin));
1031         sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
1032         sstrncpy (vl.type, "fork_rate", sizeof (vl.type));
1033         sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
1034
1035         plugin_dispatch_values (&vl);
1036 }
1037
1038 #endif /* KERNEL_LINUX */
1039
1040 #if HAVE_THREAD_INFO
1041 static int mach_get_task_name (task_t t, int *pid, char *name, size_t name_max_len)
1042 {
1043         int mib[4];
1044
1045         struct kinfo_proc kp;
1046         size_t            kp_size;
1047
1048         mib[0] = CTL_KERN;
1049         mib[1] = KERN_PROC;
1050         mib[2] = KERN_PROC_PID;
1051
1052         if (pid_for_task (t, pid) != KERN_SUCCESS)
1053                 return (-1);
1054         mib[3] = *pid;
1055
1056         kp_size = sizeof (kp);
1057         if (sysctl (mib, 4, &kp, &kp_size, NULL, 0) != 0)
1058                 return (-1);
1059
1060         if (name_max_len > (MAXCOMLEN + 1))
1061                 name_max_len = MAXCOMLEN + 1;
1062
1063         strncpy (name, kp.kp_proc.p_comm, name_max_len - 1);
1064         name[name_max_len - 1] = '\0';
1065
1066         DEBUG ("pid = %i; name = %s;", *pid, name);
1067
1068         /* We don't do the special handling for `p_comm == "LaunchCFMApp"' as
1069          * `top' does it, because it is a lot of work and only used when
1070          * debugging. -octo */
1071
1072         return (0);
1073 }
1074 #endif /* HAVE_THREAD_INFO */
1075 /* ------- end of additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
1076
1077 /* do actual readings from kernel */
1078 static int ps_read (void)
1079 {
1080 #if HAVE_THREAD_INFO
1081         kern_return_t            status;
1082
1083         int                      pset;
1084         processor_set_t          port_pset_priv;
1085
1086         int                      task;
1087         task_array_t             task_list;
1088         mach_msg_type_number_t   task_list_len;
1089
1090         int                      task_pid;
1091         char                     task_name[MAXCOMLEN + 1];
1092
1093         int                      thread;
1094         thread_act_array_t       thread_list;
1095         mach_msg_type_number_t   thread_list_len;
1096         thread_basic_info_data_t thread_data;
1097         mach_msg_type_number_t   thread_data_len;
1098
1099         int running  = 0;
1100         int sleeping = 0;
1101         int zombies  = 0;
1102         int stopped  = 0;
1103         int blocked  = 0;
1104
1105         procstat_t *ps;
1106         procstat_entry_t pse;
1107
1108         ps_list_reset ();
1109
1110         /*
1111          * The Mach-concept is a little different from the traditional UNIX
1112          * concept: All the work is done in threads. Threads are contained in
1113          * `tasks'. Therefore, `task status' doesn't make much sense, since
1114          * it's actually a `thread status'.
1115          * Tasks are assigned to sets of processors, so that's where you go to
1116          * get a list.
1117          */
1118         for (pset = 0; pset < pset_list_len; pset++)
1119         {
1120                 if ((status = host_processor_set_priv (port_host_self,
1121                                                 pset_list[pset],
1122                                                 &port_pset_priv)) != KERN_SUCCESS)
1123                 {
1124                         ERROR ("host_processor_set_priv failed: %s\n",
1125                                         mach_error_string (status));
1126                         continue;
1127                 }
1128
1129                 if ((status = processor_set_tasks (port_pset_priv,
1130                                                 &task_list,
1131                                                 &task_list_len)) != KERN_SUCCESS)
1132                 {
1133                         ERROR ("processor_set_tasks failed: %s\n",
1134                                         mach_error_string (status));
1135                         mach_port_deallocate (port_task_self, port_pset_priv);
1136                         continue;
1137                 }
1138
1139                 for (task = 0; task < task_list_len; task++)
1140                 {
1141                         ps = NULL;
1142                         if (mach_get_task_name (task_list[task],
1143                                                 &task_pid,
1144                                                 task_name, PROCSTAT_NAME_LEN) == 0)
1145                         {
1146                                 /* search for at least one match */
1147                                 for (ps = list_head_g; ps != NULL; ps = ps->next)
1148                                         /* FIXME: cmdline should be here instead of NULL */
1149                                         if (ps_list_match (task_name, NULL, ps) == 1)
1150                                                 break;
1151                         }
1152
1153                         /* Collect more detailed statistics for this process */
1154                         if (ps != NULL)
1155                         {
1156                                 task_basic_info_data_t        task_basic_info;
1157                                 mach_msg_type_number_t        task_basic_info_len;
1158                                 task_events_info_data_t       task_events_info;
1159                                 mach_msg_type_number_t        task_events_info_len;
1160                                 task_absolutetime_info_data_t task_absolutetime_info;
1161                                 mach_msg_type_number_t        task_absolutetime_info_len;
1162
1163                                 memset (&pse, '\0', sizeof (pse));
1164                                 pse.id = task_pid;
1165
1166                                 task_basic_info_len = TASK_BASIC_INFO_COUNT;
1167                                 status = task_info (task_list[task],
1168                                                 TASK_BASIC_INFO,
1169                                                 (task_info_t) &task_basic_info,
1170                                                 &task_basic_info_len);
1171                                 if (status != KERN_SUCCESS)
1172                                 {
1173                                         ERROR ("task_info failed: %s",
1174                                                         mach_error_string (status));
1175                                         continue; /* with next thread_list */
1176                                 }
1177
1178                                 task_events_info_len = TASK_EVENTS_INFO_COUNT;
1179                                 status = task_info (task_list[task],
1180                                                 TASK_EVENTS_INFO,
1181                                                 (task_info_t) &task_events_info,
1182                                                 &task_events_info_len);
1183                                 if (status != KERN_SUCCESS)
1184                                 {
1185                                         ERROR ("task_info failed: %s",
1186                                                         mach_error_string (status));
1187                                         continue; /* with next thread_list */
1188                                 }
1189
1190                                 task_absolutetime_info_len = TASK_ABSOLUTETIME_INFO_COUNT;
1191                                 status = task_info (task_list[task],
1192                                                 TASK_ABSOLUTETIME_INFO,
1193                                                 (task_info_t) &task_absolutetime_info,
1194                                                 &task_absolutetime_info_len);
1195                                 if (status != KERN_SUCCESS)
1196                                 {
1197                                         ERROR ("task_info failed: %s",
1198                                                         mach_error_string (status));
1199                                         continue; /* with next thread_list */
1200                                 }
1201
1202                                 pse.num_proc++;
1203                                 pse.vmem_rss = task_basic_info.resident_size;
1204
1205                                 pse.vmem_minflt_counter = task_events_info.cow_faults;
1206                                 pse.vmem_majflt_counter = task_events_info.faults;
1207
1208                                 pse.cpu_user_counter = task_absolutetime_info.total_user;
1209                                 pse.cpu_system_counter = task_absolutetime_info.total_system;
1210                         }
1211
1212                         status = task_threads (task_list[task], &thread_list,
1213                                         &thread_list_len);
1214                         if (status != KERN_SUCCESS)
1215                         {
1216                                 /* Apple's `top' treats this case a zombie. It
1217                                  * makes sense to some extend: A `zombie'
1218                                  * thread is nonsense, since the task/process
1219                                  * is dead. */
1220                                 zombies++;
1221                                 DEBUG ("task_threads failed: %s",
1222                                                 mach_error_string (status));
1223                                 if (task_list[task] != port_task_self)
1224                                         mach_port_deallocate (port_task_self,
1225                                                         task_list[task]);
1226                                 continue; /* with next task_list */
1227                         }
1228
1229                         for (thread = 0; thread < thread_list_len; thread++)
1230                         {
1231                                 thread_data_len = THREAD_BASIC_INFO_COUNT;
1232                                 status = thread_info (thread_list[thread],
1233                                                 THREAD_BASIC_INFO,
1234                                                 (thread_info_t) &thread_data,
1235                                                 &thread_data_len);
1236                                 if (status != KERN_SUCCESS)
1237                                 {
1238                                         ERROR ("thread_info failed: %s",
1239                                                         mach_error_string (status));
1240                                         if (task_list[task] != port_task_self)
1241                                                 mach_port_deallocate (port_task_self,
1242                                                                 thread_list[thread]);
1243                                         continue; /* with next thread_list */
1244                                 }
1245
1246                                 if (ps != NULL)
1247                                         pse.num_lwp++;
1248
1249                                 switch (thread_data.run_state)
1250                                 {
1251                                         case TH_STATE_RUNNING:
1252                                                 running++;
1253                                                 break;
1254                                         case TH_STATE_STOPPED:
1255                                         /* What exactly is `halted'? */
1256                                         case TH_STATE_HALTED:
1257                                                 stopped++;
1258                                                 break;
1259                                         case TH_STATE_WAITING:
1260                                                 sleeping++;
1261                                                 break;
1262                                         case TH_STATE_UNINTERRUPTIBLE:
1263                                                 blocked++;
1264                                                 break;
1265                                         /* There is no `zombie' case here,
1266                                          * since there are no zombie-threads.
1267                                          * There's only zombie tasks, which are
1268                                          * handled above. */
1269                                         default:
1270                                                 WARNING ("Unknown thread status: %i",
1271                                                                 thread_data.run_state);
1272                                                 break;
1273                                 } /* switch (thread_data.run_state) */
1274
1275                                 if (task_list[task] != port_task_self)
1276                                 {
1277                                         status = mach_port_deallocate (port_task_self,
1278                                                         thread_list[thread]);
1279                                         if (status != KERN_SUCCESS)
1280                                                 ERROR ("mach_port_deallocate failed: %s",
1281                                                                 mach_error_string (status));
1282                                 }
1283                         } /* for (thread_list) */
1284
1285                         if ((status = vm_deallocate (port_task_self,
1286                                                         (vm_address_t) thread_list,
1287                                                         thread_list_len * sizeof (thread_act_t)))
1288                                         != KERN_SUCCESS)
1289                         {
1290                                 ERROR ("vm_deallocate failed: %s",
1291                                                 mach_error_string (status));
1292                         }
1293                         thread_list = NULL;
1294                         thread_list_len = 0;
1295
1296                         /* Only deallocate the task port, if it isn't our own.
1297                          * Don't know what would happen in that case, but this
1298                          * is what Apple's top does.. ;) */
1299                         if (task_list[task] != port_task_self)
1300                         {
1301                                 status = mach_port_deallocate (port_task_self,
1302                                                 task_list[task]);
1303                                 if (status != KERN_SUCCESS)
1304                                         ERROR ("mach_port_deallocate failed: %s",
1305                                                         mach_error_string (status));
1306                         }
1307
1308                         if (ps != NULL)
1309                                 /* FIXME: cmdline should be here instead of NULL */
1310                                 ps_list_add (task_name, NULL, &pse);
1311                 } /* for (task_list) */
1312
1313                 if ((status = vm_deallocate (port_task_self,
1314                                 (vm_address_t) task_list,
1315                                 task_list_len * sizeof (task_t))) != KERN_SUCCESS)
1316                 {
1317                         ERROR ("vm_deallocate failed: %s",
1318                                         mach_error_string (status));
1319                 }
1320                 task_list = NULL;
1321                 task_list_len = 0;
1322
1323                 if ((status = mach_port_deallocate (port_task_self, port_pset_priv))
1324                                 != KERN_SUCCESS)
1325                 {
1326                         ERROR ("mach_port_deallocate failed: %s",
1327                                         mach_error_string (status));
1328                 }
1329         } /* for (pset_list) */
1330
1331         ps_submit_state ("running", running);
1332         ps_submit_state ("sleeping", sleeping);
1333         ps_submit_state ("zombies", zombies);
1334         ps_submit_state ("stopped", stopped);
1335         ps_submit_state ("blocked", blocked);
1336
1337         for (ps = list_head_g; ps != NULL; ps = ps->next)
1338                 ps_submit_proc_list (ps);
1339 /* #endif HAVE_THREAD_INFO */
1340
1341 #elif KERNEL_LINUX
1342         int running  = 0;
1343         int sleeping = 0;
1344         int zombies  = 0;
1345         int stopped  = 0;
1346         int paging   = 0;
1347         int blocked  = 0;
1348
1349         struct dirent *ent;
1350         DIR           *proc;
1351         int            pid;
1352
1353         char cmdline[ARG_MAX];
1354
1355         int        status;
1356         procstat_t ps;
1357         procstat_entry_t pse;
1358         char       state;
1359
1360         unsigned long fork_rate;
1361
1362         procstat_t *ps_ptr;
1363
1364         running = sleeping = zombies = stopped = paging = blocked = 0;
1365         ps_list_reset ();
1366
1367         if ((proc = opendir ("/proc")) == NULL)
1368         {
1369                 char errbuf[1024];
1370                 ERROR ("Cannot open `/proc': %s",
1371                                 sstrerror (errno, errbuf, sizeof (errbuf)));
1372                 return (-1);
1373         }
1374
1375         while ((ent = readdir (proc)) != NULL)
1376         {
1377                 if (!isdigit (ent->d_name[0]))
1378                         continue;
1379
1380                 if ((pid = atoi (ent->d_name)) < 1)
1381                         continue;
1382
1383                 status = ps_read_process (pid, &ps, &state);
1384                 if (status != 0)
1385                 {
1386                         DEBUG ("ps_read_process failed: %i", status);
1387                         continue;
1388                 }
1389
1390                 pse.id       = pid;
1391                 pse.age      = 0;
1392
1393                 pse.num_proc   = ps.num_proc;
1394                 pse.num_lwp    = ps.num_lwp;
1395                 pse.vmem_size  = ps.vmem_size;
1396                 pse.vmem_rss   = ps.vmem_rss;
1397                 pse.stack_size = ps.stack_size;
1398
1399                 pse.vmem_minflt = 0;
1400                 pse.vmem_minflt_counter = ps.vmem_minflt_counter;
1401                 pse.vmem_majflt = 0;
1402                 pse.vmem_majflt_counter = ps.vmem_majflt_counter;
1403
1404                 pse.cpu_user = 0;
1405                 pse.cpu_user_counter = ps.cpu_user_counter;
1406                 pse.cpu_system = 0;
1407                 pse.cpu_system_counter = ps.cpu_system_counter;
1408
1409                 pse.io_rchar = ps.io_rchar;
1410                 pse.io_wchar = ps.io_wchar;
1411                 pse.io_syscr = ps.io_syscr;
1412                 pse.io_syscw = ps.io_syscw;
1413
1414                 switch (state)
1415                 {
1416                         case 'R': running++;  break;
1417                         case 'S': sleeping++; break;
1418                         case 'D': blocked++;  break;
1419                         case 'Z': zombies++;  break;
1420                         case 'T': stopped++;  break;
1421                         case 'W': paging++;   break;
1422                 }
1423
1424                 ps_list_add (ps.name,
1425                                 ps_get_cmdline (pid, ps.name, cmdline, sizeof (cmdline)),
1426                                 &pse);
1427         }
1428
1429         closedir (proc);
1430
1431         ps_submit_state ("running",  running);
1432         ps_submit_state ("sleeping", sleeping);
1433         ps_submit_state ("zombies",  zombies);
1434         ps_submit_state ("stopped",  stopped);
1435         ps_submit_state ("paging",   paging);
1436         ps_submit_state ("blocked",  blocked);
1437
1438         for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
1439                 ps_submit_proc_list (ps_ptr);
1440
1441         fork_rate = read_fork_rate();
1442         if (fork_rate != ULONG_MAX)
1443                 ps_submit_fork_rate(fork_rate);
1444 /* #endif KERNEL_LINUX */
1445
1446 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
1447         int running  = 0;
1448         int sleeping = 0;
1449         int zombies  = 0;
1450         int stopped  = 0;
1451         int blocked  = 0;
1452         int idle     = 0;
1453         int wait     = 0;
1454
1455         kvm_t *kd;
1456         char errbuf[1024];
1457         char cmdline[ARG_MAX];
1458         char *cmdline_ptr;
1459         struct kinfo_proc *procs;          /* array of processes */
1460         char **argv;
1461         int count;                         /* returns number of processes */
1462         int i;
1463
1464         procstat_t *ps_ptr;
1465         procstat_entry_t pse;
1466
1467         ps_list_reset ();
1468
1469         /* Open the kvm interface, get a descriptor */
1470         kd = kvm_open (NULL, NULL, NULL, 0, errbuf);
1471         if (kd == NULL)
1472         {
1473                 ERROR ("processes plugin: Cannot open kvm interface: %s",
1474                                 errbuf);
1475                 return (0);
1476         }
1477
1478         /* Get the list of processes. */
1479         procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, &count);
1480         if (procs == NULL)
1481         {
1482                 kvm_close (kd);
1483                 ERROR ("processes plugin: Cannot get kvm processes list: %s",
1484                                 kvm_geterr(kd));
1485                 return (0);
1486         }
1487
1488         /* Iterate through the processes in kinfo_proc */
1489         for (i = 0; i < count; i++)
1490         {
1491                 /* retrieve the arguments */
1492                 cmdline[0] = 0;
1493                 cmdline_ptr = NULL;
1494
1495                 argv = kvm_getargv (kd, (const struct kinfo_proc *) &(procs[i]), 0);
1496                 if (argv != NULL)
1497                 {
1498                         int status;
1499                         int argc;
1500
1501                         argc = 0;
1502                         while (argv[argc] != NULL)
1503                                 argc++;
1504
1505                         status = strjoin (cmdline, sizeof (cmdline),
1506                                         argv, argc, " ");
1507
1508                         if (status < 0)
1509                         {
1510                                 WARNING ("processes plugin: Command line did "
1511                                                 "not fit into buffer.");
1512                         }
1513                         else
1514                         {
1515                                 cmdline_ptr = &cmdline[0];
1516                         }
1517                 }
1518
1519                 pse.id       = procs[i].ki_pid;
1520                 pse.age      = 0;
1521
1522                 pse.num_proc = 1;
1523                 pse.num_lwp  = procs[i].ki_numthreads;
1524
1525                 pse.vmem_size = procs[i].ki_size;
1526                 pse.vmem_rss = procs[i].ki_rssize * getpagesize();
1527                 pse.stack_size = procs[i].ki_ssize * getpagesize();
1528                 pse.vmem_minflt = 0;
1529                 pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
1530                 pse.vmem_majflt = 0;
1531                 pse.vmem_majflt_counter = procs[i].ki_rusage.ru_majflt;
1532
1533                 pse.cpu_user = 0;
1534                 pse.cpu_user_counter = procs[i].ki_rusage.ru_utime.tv_sec
1535                         * 1000
1536                         + procs[i].ki_rusage.ru_utime.tv_usec;
1537                 pse.cpu_system = 0;
1538                 pse.cpu_system_counter = procs[i].ki_rusage.ru_stime.tv_sec
1539                         * 1000
1540                         + procs[i].ki_rusage.ru_stime.tv_usec;
1541
1542                 /* no io data */
1543                 pse.io_rchar = -1;
1544                 pse.io_wchar = -1;
1545                 pse.io_syscr = -1;
1546                 pse.io_syscw = -1;
1547
1548                 switch (procs[i].ki_stat)
1549                 {
1550                         case SSTOP:     stopped++;      break;
1551                         case SSLEEP:    sleeping++;     break;
1552                         case SRUN:      running++;      break;
1553                         case SIDL:      idle++;         break;
1554                         case SWAIT:     wait++;         break;
1555                         case SLOCK:     blocked++;      break;
1556                         case SZOMB:     zombies++;      break;
1557                 }
1558
1559                 ps_list_add (procs[i].ki_comm, cmdline_ptr, &pse);
1560         }
1561
1562         kvm_close(kd);
1563
1564         ps_submit_state ("running",  running);
1565         ps_submit_state ("sleeping", sleeping);
1566         ps_submit_state ("zombies",  zombies);
1567         ps_submit_state ("stopped",  stopped);
1568         ps_submit_state ("blocked",  blocked);
1569         ps_submit_state ("idle",     idle);
1570         ps_submit_state ("wait",     wait);
1571
1572         for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
1573                 ps_submit_proc_list (ps_ptr);
1574 /* #endif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD */
1575
1576 #elif HAVE_PROCINFO_H
1577         /* AIX */
1578         int running  = 0;
1579         int sleeping = 0;
1580         int zombies  = 0;
1581         int stopped  = 0;
1582         int paging   = 0;
1583         int blocked  = 0;
1584
1585         pid_t pindex = 0;
1586         int nprocs;
1587
1588         procstat_t *ps;
1589         procstat_entry_t pse;
1590
1591         ps_list_reset ();
1592         while ((nprocs = getprocs64 (procentry, sizeof(struct procentry64),
1593                                         /* fdsinfo = */ NULL, sizeof(struct fdsinfo64),
1594                                         &pindex, MAXPROCENTRY)) > 0)
1595         {
1596                 int i;
1597
1598                 for (i = 0; i < nprocs; i++)
1599                 {
1600                         tid64_t thindex;
1601                         int nthreads;
1602                         char arglist[MAXARGLN+1];
1603                         char *cargs;
1604                         char *cmdline;
1605
1606                         if (procentry[i].pi_state == SNONE) continue;
1607                         /* if (procentry[i].pi_state == SZOMB)  FIXME */
1608
1609                         cmdline = procentry[i].pi_comm;
1610                         cargs = procentry[i].pi_comm;
1611                         if ( procentry[i].pi_flags & SKPROC )
1612                         {
1613                                 if (procentry[i].pi_pid == 0)
1614                                         cmdline = "swapper";
1615                                 cargs = cmdline;
1616                         }
1617                         else
1618                         {
1619                                 if (getargs(&procentry[i], sizeof(struct procentry64), arglist, MAXARGLN) >= 0)
1620                                 {
1621                                         int n;
1622
1623                                         n = -1;
1624                                         while (++n < MAXARGLN)
1625                                         {
1626                                                 if (arglist[n] == '\0')
1627                                                 {
1628                                                         if (arglist[n+1] == '\0')
1629                                                                 break;
1630                                                         arglist[n] = ' ';
1631                                                 }
1632                                         }
1633                                         cargs = arglist;
1634                                 }
1635                         }
1636
1637                         pse.id       = procentry[i].pi_pid;
1638                         pse.age      = 0;
1639                         pse.num_lwp  = procentry[i].pi_thcount;
1640                         pse.num_proc = 1;
1641
1642                         thindex=0;
1643                         while ((nthreads = getthrds64(procentry[i].pi_pid,
1644                                                         thrdentry, sizeof(struct thrdentry64),
1645                                                         &thindex, MAXTHRDENTRY)) > 0)
1646                         {
1647                                 int j;
1648
1649                                 for (j=0; j< nthreads; j++)
1650                                 {
1651                                         switch (thrdentry[j].ti_state)
1652                                         {
1653                                                 /* case TSNONE: break; */
1654                                                 case TSIDL:     blocked++;      break; /* FIXME is really blocked */
1655                                                 case TSRUN:     running++;      break;
1656                                                 case TSSLEEP:   sleeping++;     break;
1657                                                 case TSSWAP:    paging++;       break;
1658                                                 case TSSTOP:    stopped++;      break;
1659                                                 case TSZOMB:    zombies++;      break;
1660                                         }
1661                                 }
1662                                 if (nthreads < MAXTHRDENTRY)
1663                                         break;
1664                         }
1665
1666                         pse.cpu_user = 0;
1667                         /* tv_usec is nanosec ??? */
1668                         pse.cpu_user_counter = procentry[i].pi_ru.ru_utime.tv_sec * 1000000 +
1669                                 procentry[i].pi_ru.ru_utime.tv_usec / 1000;
1670
1671                         pse.cpu_system = 0;
1672                         /* tv_usec is nanosec ??? */
1673                         pse.cpu_system_counter = procentry[i].pi_ru.ru_stime.tv_sec * 1000000 +
1674                                 procentry[i].pi_ru.ru_stime.tv_usec / 1000;
1675
1676                         pse.vmem_minflt = 0;
1677                         pse.vmem_minflt_counter = procentry[i].pi_minflt;
1678                         pse.vmem_majflt = 0;
1679                         pse.vmem_majflt_counter = procentry[i].pi_majflt;
1680
1681                         pse.vmem_size = procentry[i].pi_tsize + procentry[i].pi_dvm * pagesize;
1682                         pse.vmem_rss = (procentry[i].pi_drss + procentry[i].pi_trss) * pagesize;
1683                         pse.stack_size =  0;
1684
1685                         ps_list_add (cmdline, cargs, &pse);
1686                 } /* for (i = 0 .. nprocs) */
1687
1688                 if (nprocs < MAXPROCENTRY)
1689                         break;
1690         } /* while (getprocs64() > 0) */
1691         ps_submit_state ("running",  running);
1692         ps_submit_state ("sleeping", sleeping);
1693         ps_submit_state ("zombies",  zombies);
1694         ps_submit_state ("stopped",  stopped);
1695         ps_submit_state ("paging",   paging);
1696         ps_submit_state ("blocked",  blocked);
1697
1698         for (ps = list_head_g; ps != NULL; ps = ps->next)
1699                 ps_submit_proc_list (ps);
1700 #endif /* HAVE_PROCINFO_H */
1701
1702         return (0);
1703 } /* int ps_read */
1704
1705 void module_register (void)
1706 {
1707         plugin_register_config ("processes", ps_config,
1708                         config_keys, config_keys_num);
1709         plugin_register_init ("processes", ps_init);
1710         plugin_register_read ("processes", ps_read);
1711 } /* void module_register */