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