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