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