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