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