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