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