processes plugin: Implement collecting the number of pagefaults for a selected process.
[collectd.git] / src / processes.c
1 /**
2  * collectd - src/processes.c
3  * Copyright (C) 2005  Lyonel Vincent
4  * Copyright (C) 2006  Florian Forster (Mach code)
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; either version 2 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19  *
20  * Authors:
21  *   Lyonel Vincent <lyonel at ezix.org>
22  *   Florian octo Forster <octo at verplant.org>
23  **/
24
25 #include "collectd.h"
26 #include "common.h"
27 #include "plugin.h"
28 #include "utils_debug.h"
29 #include "configfile.h"
30
31 /* Include header files for the mach system, if they exist.. */
32 #if HAVE_THREAD_INFO
33 #  if HAVE_MACH_MACH_INIT_H
34 #    include <mach/mach_init.h>
35 #  endif
36 #  if HAVE_MACH_HOST_PRIV_H
37 #    include <mach/host_priv.h>
38 #  endif
39 #  if HAVE_MACH_MACH_ERROR_H
40 #    include <mach/mach_error.h>
41 #  endif
42 #  if HAVE_MACH_MACH_HOST_H
43 #    include <mach/mach_host.h>
44 #  endif
45 #  if HAVE_MACH_MACH_PORT_H
46 #    include <mach/mach_port.h>
47 #  endif
48 #  if HAVE_MACH_MACH_TYPES_H
49 #    include <mach/mach_types.h>
50 #  endif
51 #  if HAVE_MACH_MESSAGE_H
52 #    include <mach/message.h>
53 #  endif
54 #  if HAVE_MACH_PROCESSOR_SET_H
55 #    include <mach/processor_set.h>
56 #  endif
57 #  if HAVE_MACH_TASK_H
58 #    include <mach/task.h>
59 #  endif
60 #  if HAVE_MACH_THREAD_ACT_H
61 #    include <mach/thread_act.h>
62 #  endif
63 #  if HAVE_MACH_VM_REGION_H
64 #    include <mach/vm_region.h>
65 #  endif
66 #  if HAVE_MACH_VM_MAP_H
67 #    include <mach/vm_map.h>
68 #  endif
69 #  if HAVE_MACH_VM_PROT_H
70 #    include <mach/vm_prot.h>
71 #  endif
72 /* #endif HAVE_THREAD_INFO */
73
74 #elif KERNEL_LINUX
75 #  if HAVE_LINUX_CONFIG_H
76 #    include <linux/config.h>
77 #  endif
78 #  ifndef CONFIG_HZ
79 #    define CONFIG_HZ 100
80 #  endif
81 #endif /* KERNEL_LINUX */
82
83 #define MODULE_NAME "processes"
84
85 #if HAVE_THREAD_INFO || KERNEL_LINUX
86 # define PROCESSES_HAVE_READ 1
87 #else
88 # define PROCESSES_HAVE_READ 0
89 #endif
90
91 #define BUFSIZE 256
92
93 static char *processes_file = "processes.rrd";
94 static char *processes_ds_def[] =
95 {
96         "DS:running:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
97         "DS:sleeping:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
98         "DS:zombies:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
99         "DS:stopped:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
100         "DS:paging:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
101         "DS:blocked:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
102         NULL
103 };
104 static int processes_ds_num = 6;
105
106 static char *ps_rss_file = "processes/ps_rss-%s.rrd";
107 static char *ps_rss_ds_def[] =
108 {
109         /* max = 2^63 - 1 */
110         "DS:byte:GAUGE:"COLLECTD_HEARTBEAT":0:9223372036854775807",
111         NULL
112 };
113 static int ps_rss_ds_num = 1;
114
115 static char *ps_cputime_file = "processes/ps_cputime-%s.rrd";
116 static char *ps_cputime_ds_def[] =
117 {
118         /* 1 second in user-mode per second ought to be enough.. */
119         "DS:user:COUNTER:"COLLECTD_HEARTBEAT":0:1000000",
120         "DS:syst:COUNTER:"COLLECTD_HEARTBEAT":0:1000000",
121         NULL
122 };
123 static int ps_cputime_ds_num = 2;
124
125 static char *ps_count_file = "processes/ps_count-%s.rrd";
126 static char *ps_count_ds_def[] =
127 {
128         "DS:processes:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
129         "DS:threads:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
130         NULL
131 };
132 static int ps_count_ds_num = 2;
133
134 static char *ps_pagefaults_file = "processes/ps_pagefaults-%s.rrd";
135 static char *ps_pagefaults_ds_def[] =
136 {
137         /* max = 2^63 - 1 */
138         "DS:minflt:GAUGE:"COLLECTD_HEARTBEAT":0:9223372036854775807",
139         "DS:majflt:GAUGE:"COLLECTD_HEARTBEAT":0:9223372036854775807",
140         NULL
141 };
142 static int ps_pagefaults_ds_num = 2;
143
144 static char *config_keys[] =
145 {
146         "CollectName",
147         NULL
148 };
149 static int config_keys_num = 1;
150
151 typedef struct procstat
152 {
153 #define PROCSTAT_NAME_LEN 256
154         char               name[PROCSTAT_NAME_LEN];
155         unsigned int       num_proc;
156         unsigned int       num_lwp;
157         unsigned long      vmem_rss;
158         unsigned long      vmem_minflt;
159         unsigned long      vmem_majflt;
160         unsigned long long cpu_user;
161         unsigned long long cpu_system;
162         struct procstat   *next;
163 } procstat_t;
164
165 static procstat_t *list_head_g = NULL;
166
167 #if HAVE_THREAD_INFO
168 static mach_port_t port_host_self;
169 static mach_port_t port_task_self;
170
171 static processor_set_name_array_t pset_list;
172 static mach_msg_type_number_t     pset_list_len;
173 /* #endif HAVE_THREAD_INFO */
174
175 #elif KERNEL_LINUX
176 static long pagesize_g;
177 #endif /* KERNEL_LINUX */
178
179 static procstat_t *ps_list_append (procstat_t *list, const char *name)
180 {
181         procstat_t *new;
182         procstat_t *ptr;
183
184         if ((new = (procstat_t *) malloc (sizeof (procstat_t))) == NULL)
185                 return (NULL);
186         memset (new, 0, sizeof (procstat_t));
187         strncpy (new->name, name, PROCSTAT_NAME_LEN);
188
189         for (ptr = list; ptr != NULL; ptr = ptr->next)
190                 if (ptr->next == NULL)
191                         break;
192
193         if (ptr != NULL)
194                 ptr->next = new;
195
196         return (new);
197 }
198
199 static void ps_list_add (procstat_t *list, procstat_t *entry)
200 {
201         procstat_t *ptr;
202
203         ptr = list;
204         while ((ptr != NULL) && (strcmp (ptr->name, entry->name) != 0))
205                 ptr = ptr->next;
206
207         if (ptr == NULL)
208                 return;
209
210         ptr->num_proc    += entry->num_proc;
211         ptr->num_lwp     += entry->num_lwp;
212         ptr->vmem_rss    += entry->vmem_rss;
213         ptr->vmem_minflt += entry->vmem_minflt;
214         ptr->vmem_majflt += entry->vmem_majflt;
215         ptr->cpu_user    += entry->cpu_user;
216         ptr->cpu_system  += entry->cpu_system;
217 }
218
219 static void ps_list_reset (procstat_t *ps)
220 {
221         while (ps != NULL)
222         {
223                 ps->num_proc    = 0;
224                 ps->num_lwp     = 0;
225                 ps->vmem_rss    = 0;
226                 ps->vmem_minflt = 0;
227                 ps->vmem_majflt = 0;
228                 ps->cpu_user    = 0;
229                 ps->cpu_system  = 0;
230                 ps = ps->next;
231         }
232 }
233
234 static int ps_config (char *key, char *value)
235 {
236         if (strcasecmp (key, "CollectName") == 0)
237         {
238                 procstat_t *entry;
239
240                 entry = ps_list_append (list_head_g, value);
241                 if (entry == NULL)
242                 {
243                         syslog (LOG_ERR, "processes plugin: ps_list_append failed.");
244                         return (1);
245                 }
246                 if (list_head_g == NULL)
247                         list_head_g = entry;
248         }
249         else
250         {
251                 return (-1);
252         }
253
254         return (0);
255 }
256
257 static void ps_init (void)
258 {
259 #if HAVE_THREAD_INFO
260         kern_return_t status;
261
262         port_host_self = mach_host_self ();
263         port_task_self = mach_task_self ();
264
265         if (pset_list != NULL)
266         {
267                 vm_deallocate (port_task_self,
268                                 (vm_address_t) pset_list,
269                                 pset_list_len * sizeof (processor_set_t));
270                 pset_list = NULL;
271                 pset_list_len = 0;
272         }
273
274         if ((status = host_processor_sets (port_host_self,
275                                         &pset_list,
276                                         &pset_list_len)) != KERN_SUCCESS)
277         {
278                 syslog (LOG_ERR, "host_processor_sets failed: %s\n",
279                                 mach_error_string (status));
280                 pset_list = NULL;
281                 pset_list_len = 0;
282                 return;
283         }
284 /* #endif HAVE_THREAD_INFO */
285
286 #elif KERNEL_LINUX
287         pagesize_g = sysconf(_SC_PAGESIZE);
288         DBG ("pagesize_g = %li; CONFIG_HZ = %i;",
289                         pagesize_g, CONFIG_HZ);
290 #endif /* KERNEL_LINUX */
291
292         return;
293 }
294
295 static void ps_write (char *host, char *inst, char *val)
296 {
297         rrd_update_file (host, processes_file, val,
298                         processes_ds_def, processes_ds_num);
299 }
300
301 static void ps_rss_write (char *host, char *inst, char *val)
302 {
303         char filename[256];
304         int status;
305
306         status = snprintf (filename, 256, ps_rss_file, inst);
307         if ((status < 1) || (status >= 256))
308                 return;
309
310         rrd_update_file (host, filename, val, ps_rss_ds_def, ps_rss_ds_num);
311 }
312
313 static void ps_cputime_write (char *host, char *inst, char *val)
314 {
315         char filename[256];
316         int status;
317
318         status = snprintf (filename, 256, ps_cputime_file, inst);
319         if ((status < 1) || (status >= 256))
320                 return;
321
322         DBG ("host = %s; filename = %s; val = %s;",
323                         host, filename, val);
324         rrd_update_file (host, filename, val,
325                         ps_cputime_ds_def, ps_cputime_ds_num);
326 }
327
328 static void ps_count_write (char *host, char *inst, char *val)
329 {
330         char filename[256];
331         int status;
332
333         status = snprintf (filename, 256, ps_count_file, inst);
334         if ((status < 1) || (status >= 256))
335                 return;
336
337         DBG ("host = %s; filename = %s; val = %s;",
338                         host, filename, val);
339         rrd_update_file (host, filename, val,
340                         ps_count_ds_def, ps_count_ds_num);
341 }
342
343 static void ps_pagefaults_write (char *host, char *inst, char *val)
344 {
345         char filename[256];
346         int status;
347
348         status = snprintf (filename, 256, ps_pagefaults_file, inst);
349         if ((status < 1) || (status >= 256))
350                 return;
351
352         DBG ("host = %s; filename = %s; val = %s;",
353                         host, filename, val);
354         rrd_update_file (host, filename, val,
355                         ps_pagefaults_ds_def, ps_pagefaults_ds_num);
356 }
357
358 #if PROCESSES_HAVE_READ
359 static void ps_submit (int running,
360                 int sleeping,
361                 int zombies,
362                 int stopped,
363                 int paging,
364                 int blocked)
365 {
366         char buf[BUFSIZE];
367
368         if (snprintf (buf, BUFSIZE, "%u:%i:%i:%i:%i:%i:%i",
369                                 (unsigned int) curtime,
370                                 running, sleeping, zombies, stopped, paging,
371                                 blocked) >= BUFSIZE)
372                 return;
373
374         DBG ("running = %i; sleeping = %i; zombies = %i; stopped = %i; paging = %i; blocked = %i;",
375                         running, sleeping, zombies, stopped, paging, blocked);
376
377         plugin_submit (MODULE_NAME, "-", buf);
378 }
379
380 static void ps_submit_proc (procstat_t *ps)
381 {
382         char buffer[64];
383
384         if (ps == NULL)
385                 return;
386
387         snprintf (buffer, 64, "%u:%lu",
388                         (unsigned int) curtime,
389                         ps->vmem_rss);
390         buffer[63] = '\0';
391         plugin_submit ("ps_rss", ps->name, buffer);
392
393         snprintf (buffer, 64, "%u:%u:%u",
394                         (unsigned int) curtime,
395                         /* Make the counter overflow */
396                         (unsigned int) (ps->cpu_user   & 0xFFFFFFFF),
397                         (unsigned int) (ps->cpu_system & 0xFFFFFFFF));
398         buffer[63] = '\0';
399         plugin_submit ("ps_cputime", ps->name, buffer);
400
401         snprintf (buffer, 64, "%u:%u:%u",
402                         (unsigned int) curtime,
403                         ps->num_proc, ps->num_lwp);
404         buffer[63] = '\0';
405         plugin_submit ("ps_count", ps->name, buffer);
406
407         snprintf (buffer, 64, "%u:%lu:%lu",
408                         (unsigned int) curtime,
409                         ps->vmem_minflt, ps->vmem_majflt);
410         buffer[63] = '\0';
411         plugin_submit ("ps_pagefaults", ps->name, buffer);
412
413         DBG ("name = %s; num_proc = %i; num_lwp = %i; vmem_rss = %i; "
414                         "vmem_minflt = %i; vmem_majflt = %i; "
415                         "cpu_user = %i; cpu_system = %i;",
416                         ps->name, ps->num_proc, ps->num_lwp, ps->vmem_rss,
417                         ps->vmem_minflt, ps->vmem_majflt, ps->cpu_user,
418                         ps->cpu_system);
419
420 }
421
422 #if KERNEL_LINUX
423 static int *ps_read_tasks (int pid)
424 {
425         int *list = NULL;
426         int  list_size = 1; /* size of allocated space, in elements */
427         int  list_len = 0;  /* number of currently used elements */
428
429         char           dirname[64];
430         DIR           *dh;
431         struct dirent *ent;
432
433         snprintf (dirname, 64, "/proc/%i/task", pid);
434         dirname[63] = '\0';
435
436         if ((dh = opendir (dirname)) == NULL)
437         {
438                 syslog (LOG_NOTICE, "processes plugin: Failed to open directory `%s'",
439                                 dirname);
440                 return (NULL);
441         }
442
443         while ((ent = readdir (dh)) != NULL)
444         {
445                 if (!isdigit (ent->d_name[0]))
446                         continue;
447
448                 if ((list_len + 1) >= list_size)
449                 {
450                         int *new_ptr;
451                         int  new_size = 2 * list_size;
452                         /* Comes in sizes: 2, 4, 8, 16, ... */
453
454                         new_ptr = (int *) realloc (list, (size_t) (sizeof (int) * new_size));
455                         if (new_ptr == NULL)
456                         {
457                                 if (list != NULL)
458                                         free (list);
459                                 syslog (LOG_ERR, "processes plugin: "
460                                                 "Failed to allocate more memory.");
461                                 return (NULL);
462                         }
463
464                         list = new_ptr;
465                         list_size = new_size;
466
467                         memset (list + list_len, 0, sizeof (int) * (list_size - list_len));
468                 }
469
470                 list[list_len] = atoi (ent->d_name);
471                 if (list[list_len] != 0)
472                         list_len++;
473         }
474
475         closedir (dh);
476
477         assert (list_len < list_size);
478         assert (list[list_len] == 0);
479
480         return (list);
481 }
482
483 int ps_read_process (int pid, procstat_t *ps, char *state)
484 {
485         char  filename[64];
486         char  buffer[1024];
487         FILE *fh;
488
489         char *fields[64];
490         char  fields_len;
491
492         int  *tasks;
493         int   i;
494
495         int   ppid;
496         int   name_len;
497
498         memset (ps, 0, sizeof (procstat_t));
499
500         snprintf (filename, 64, "/proc/%i/stat", pid);
501         filename[63] = '\0';
502
503         if ((fh = fopen (filename, "r")) == NULL)
504                 return (-1);
505
506         if (fgets (buffer, 1024, fh) == NULL)
507         {
508                 fclose (fh);
509                 return (-1);
510         }
511
512         fclose (fh);
513
514         fields_len = strsplit (buffer, fields, 64);
515         if (fields_len < 24)
516         {
517                 DBG ("`%s' has only %i fields..",
518                                 filename, fields_len);
519                 return (-1);
520         }
521         else if (fields_len != 41)
522         {
523                 DBG ("WARNING: (fields_len = %i) != 41", fields_len);
524         }
525
526         /* copy the name, strip brackets in the process */
527         name_len = strlen (fields[1]) - 2;
528         if ((fields[1][0] != '(') || (fields[1][name_len + 1] != ')'))
529         {
530                 DBG ("No brackets found in process name: `%s'", fields[1]);
531                 return (-1);
532         }
533         fields[1] = fields[1] + 1;
534         fields[1][name_len] = '\0';
535         strncpy (ps->name, fields[1], PROCSTAT_NAME_LEN);
536
537         ppid = atoi (fields[3]);
538
539         if ((tasks = ps_read_tasks (pid)) == NULL)
540         {
541                 DBG ("ps_read_tasks (%i) failed.", pid);
542                 return (-1);
543         }
544
545         *state = '\0';
546         ps->num_lwp  = 0;
547         ps->num_proc = 1;
548         for (i = 0; tasks[i] != 0; i++)
549                 ps->num_lwp++;
550
551         free (tasks);
552         tasks = NULL;
553
554         /* Leave the rest at zero if this is only an LWP */
555         if (ps->num_proc == 0)
556         {
557                 DBG ("This is only an LWP: pid = %i; name = %s;",
558                                 pid, ps->name);
559                 return (0);
560         }
561
562         ps->vmem_minflt = atol  (fields[9]);
563         ps->vmem_majflt = atol  (fields[11]);
564         ps->cpu_user    = atoll (fields[13]);
565         ps->cpu_system  = atoll (fields[14]);
566         ps->vmem_rss    = atol  (fields[23]);
567         
568         /* Convert jiffies to useconds */
569         ps->cpu_user   = ps->cpu_user   * 1000000 / CONFIG_HZ;
570         ps->cpu_system = ps->cpu_system * 1000000 / CONFIG_HZ;
571         ps->vmem_rss   = ps->vmem_rss * pagesize_g;
572
573         *state = fields[2][0];
574
575         /* success */
576         return (0);
577 } /* int ps_read_process (...) */
578 #endif /* KERNEL_LINUX */
579
580 static void ps_read (void)
581 {
582 #if HAVE_THREAD_INFO
583         kern_return_t            status;
584
585         int                      pset;
586         processor_set_t          port_pset_priv;
587
588         int                      task;
589         task_array_t             task_list;
590         mach_msg_type_number_t   task_list_len;
591
592         int                      thread;
593         thread_act_array_t       thread_list;
594         mach_msg_type_number_t   thread_list_len;
595         thread_basic_info_data_t thread_data;
596         mach_msg_type_number_t   thread_data_len;
597
598         int running  = 0;
599         int sleeping = 0;
600         int zombies  = 0;
601         int stopped  = 0;
602         int blocked  = 0;
603
604         /*
605          * The Mach-concept is a little different from the traditional UNIX
606          * concept: All the work is done in threads. Threads are contained in
607          * `tasks'. Therefore, `task status' doesn't make much sense, since
608          * it's actually a `thread status'.
609          * Tasks are assigned to sets of processors, so that's where you go to
610          * get a list.
611          */
612         for (pset = 0; pset < pset_list_len; pset++)
613         {
614                 if ((status = host_processor_set_priv (port_host_self,
615                                                 pset_list[pset],
616                                                 &port_pset_priv)) != KERN_SUCCESS)
617                 {
618                         syslog (LOG_ERR, "host_processor_set_priv failed: %s\n",
619                                         mach_error_string (status));
620                         continue;
621                 }
622
623                 if ((status = processor_set_tasks (port_pset_priv,
624                                                 &task_list,
625                                                 &task_list_len)) != KERN_SUCCESS)
626                 {
627                         syslog (LOG_ERR, "processor_set_tasks failed: %s\n",
628                                         mach_error_string (status));
629                         mach_port_deallocate (port_task_self, port_pset_priv);
630                         continue;
631                 }
632
633                 for (task = 0; task < task_list_len; task++)
634                 {
635                         status = task_threads (task_list[task], &thread_list,
636                                         &thread_list_len);
637                         if (status != KERN_SUCCESS)
638                         {
639                                 /* Apple's `top' treats this case a zombie. It
640                                  * makes sense to some extend: A `zombie'
641                                  * thread is nonsense, since the task/process
642                                  * is dead. */
643                                 zombies++;
644                                 DBG ("task_threads failed: %s",
645                                                 mach_error_string (status));
646                                 if (task_list[task] != port_task_self)
647                                         mach_port_deallocate (port_task_self,
648                                                         task_list[task]);
649                                 continue; /* with next task_list */
650                         }
651
652                         for (thread = 0; thread < thread_list_len; thread++)
653                         {
654                                 thread_data_len = THREAD_BASIC_INFO_COUNT;
655                                 status = thread_info (thread_list[thread],
656                                                 THREAD_BASIC_INFO,
657                                                 (thread_info_t) &thread_data,
658                                                 &thread_data_len);
659                                 if (status != KERN_SUCCESS)
660                                 {
661                                         syslog (LOG_ERR, "thread_info failed: %s\n",
662                                                         mach_error_string (status));
663                                         if (task_list[task] != port_task_self)
664                                                 mach_port_deallocate (port_task_self,
665                                                                 thread_list[thread]);
666                                         continue; /* with next thread_list */
667                                 }
668
669                                 switch (thread_data.run_state)
670                                 {
671                                         case TH_STATE_RUNNING:
672                                                 running++;
673                                                 break;
674                                         case TH_STATE_STOPPED:
675                                         /* What exactly is `halted'? */
676                                         case TH_STATE_HALTED:
677                                                 stopped++;
678                                                 break;
679                                         case TH_STATE_WAITING:
680                                                 sleeping++;
681                                                 break;
682                                         case TH_STATE_UNINTERRUPTIBLE:
683                                                 blocked++;
684                                                 break;
685                                         /* There is no `zombie' case here,
686                                          * since there are no zombie-threads.
687                                          * There's only zombie tasks, which are
688                                          * handled above. */
689                                         default:
690                                                 syslog (LOG_WARNING,
691                                                                 "Unknown thread status: %s",
692                                                                 thread_data.run_state);
693                                                 break;
694                                 } /* switch (thread_data.run_state) */
695
696                                 if (task_list[task] != port_task_self)
697                                 {
698                                         status = mach_port_deallocate (port_task_self,
699                                                         thread_list[thread]);
700                                         if (status != KERN_SUCCESS)
701                                                 syslog (LOG_ERR, "mach_port_deallocate failed: %s",
702                                                                 mach_error_string (status));
703                                 }
704                         } /* for (thread_list) */
705
706                         if ((status = vm_deallocate (port_task_self,
707                                                         (vm_address_t) thread_list,
708                                                         thread_list_len * sizeof (thread_act_t)))
709                                         != KERN_SUCCESS)
710                         {
711                                 syslog (LOG_ERR, "vm_deallocate failed: %s",
712                                                 mach_error_string (status));
713                         }
714                         thread_list = NULL;
715                         thread_list_len = 0;
716
717                         /* Only deallocate the task port, if it isn't our own.
718                          * Don't know what would happen in that case, but this
719                          * is what Apple's top does.. ;) */
720                         if (task_list[task] != port_task_self)
721                         {
722                                 status = mach_port_deallocate (port_task_self,
723                                                 task_list[task]);
724                                 if (status != KERN_SUCCESS)
725                                         syslog (LOG_ERR, "mach_port_deallocate failed: %s",
726                                                         mach_error_string (status));
727                         }
728                 } /* for (task_list) */
729
730                 if ((status = vm_deallocate (port_task_self,
731                                 (vm_address_t) task_list,
732                                 task_list_len * sizeof (task_t))) != KERN_SUCCESS)
733                 {
734                         syslog (LOG_ERR, "vm_deallocate failed: %s",
735                                         mach_error_string (status));
736                 }
737                 task_list = NULL;
738                 task_list_len = 0;
739
740                 if ((status = mach_port_deallocate (port_task_self, port_pset_priv))
741                                 != KERN_SUCCESS)
742                 {
743                         syslog (LOG_ERR, "mach_port_deallocate failed: %s",
744                                         mach_error_string (status));
745                 }
746         } /* for (pset_list) */
747
748         ps_submit (running, sleeping, zombies, stopped, -1, blocked);
749 /* #endif HAVE_THREAD_INFO */
750
751 #elif KERNEL_LINUX
752         int running  = 0;
753         int sleeping = 0;
754         int zombies  = 0;
755         int stopped  = 0;
756         int paging   = 0;
757         int blocked  = 0;
758
759         struct dirent *ent;
760         DIR           *proc;
761         int            pid;
762
763         int        status;
764         procstat_t ps;
765         char       state;
766
767         procstat_t *ps_ptr;
768
769         running = sleeping = zombies = stopped = paging = blocked = 0;
770         ps_list_reset (list_head_g);
771
772         if ((proc = opendir ("/proc")) == NULL)
773         {
774                 syslog (LOG_ERR, "Cannot open `/proc': %s", strerror (errno));
775                 return;
776         }
777
778         while ((ent = readdir (proc)) != NULL)
779         {
780                 if (!isdigit (ent->d_name[0]))
781                         continue;
782
783                 if ((pid = atoi (ent->d_name)) < 1)
784                         continue;
785
786                 status = ps_read_process (pid, &ps, &state);
787                 if (status != 0)
788                 {
789                         DBG ("ps_read_process failed: %i", status);
790                         continue;
791                 }
792
793                 switch (state)
794                 {
795                         case 'R': running++;  break;
796                         case 'S': sleeping++; break;
797                         case 'D': blocked++;  break;
798                         case 'Z': zombies++;  break;
799                         case 'T': stopped++;  break;
800                         case 'W': paging++;   break;
801                 }
802
803                 if (list_head_g != NULL)
804                         ps_list_add (list_head_g, &ps);
805         }
806
807         closedir (proc);
808
809         ps_submit (running, sleeping, zombies, stopped, paging, blocked);
810
811         for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
812                 ps_submit_proc (ps_ptr);
813 #endif /* KERNEL_LINUX */
814 }
815 #else
816 # define ps_read NULL
817 #endif /* PROCESSES_HAVE_READ */
818
819 void module_register (void)
820 {
821         plugin_register (MODULE_NAME, ps_init, ps_read, ps_write);
822         plugin_register ("ps_rss", NULL, NULL, ps_rss_write);
823         plugin_register ("ps_cputime", NULL, NULL, ps_cputime_write);
824         plugin_register ("ps_count", NULL, NULL, ps_count_write);
825         plugin_register ("ps_pagefaults", NULL, NULL, ps_pagefaults_write);
826         cf_register (MODULE_NAME, ps_config, config_keys, config_keys_num);
827 }
828
829 #undef BUFSIZE
830 #undef MODULE_NAME