Implemented reading of memory usage under Mach/Darwin
[collectd.git] / src / processes.c
1 /**
2  * collectd - src/processes.c
3  * Copyright (C) 2005  Lyonel Vincent
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Lyonel Vincent <lyonel at ezix.org>
21  *   Florian octo Forster <octo at verplant.org>
22  **/
23
24 #include "collectd.h"
25 #include "common.h"
26 #include "plugin.h"
27 #include "utils_debug.h"
28
29 #if HAVE_SYS_SYSCTL_H
30 # include <sys/sysctl.h>
31 #endif
32
33 #define MODULE_NAME "processes"
34
35 #if defined(KERNEL_LINUX) || defined(HAVE_SYSCTLBYNAME)
36 # define PROCESSES_HAVE_READ 1
37 #else
38 # define PROCESSES_HAVE_READ 0
39 #endif
40
41 #define BUFSIZE 256
42
43 static char *ps_file = "processes.rrd";
44
45 static char *ds_def[] =
46 {
47         "DS:running:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
48         "DS:sleeping:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
49         "DS:zombies:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
50         "DS:stopped:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
51         "DS:paging:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
52         "DS:blocked:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
53         NULL
54 };
55 static int ds_num = 6;
56
57 static void ps_init (void)
58 {
59 }
60
61 static void ps_write (char *host, char *inst, char *val)
62 {
63         rrd_update_file (host, ps_file, val, ds_def, ds_num);
64 }
65
66 #if PROCESSES_HAVE_READ
67 static void ps_submit (unsigned int running,
68                 unsigned int sleeping,
69                 unsigned int zombies,
70                 unsigned int stopped,
71                 unsigned int paging,
72                 unsigned int blocked)
73 {
74         char buf[BUFSIZE];
75
76         if (snprintf (buf, BUFSIZE, "%u:%u:%u:%u:%u:%u:%u",
77                                 (unsigned int) curtime,
78                                 running, sleeping, zombies, stopped, paging,
79                                 blocked) >= BUFSIZE)
80                 return;
81
82         plugin_submit (MODULE_NAME, "-", buf);
83 }
84
85 static void ps_read (void)
86 {
87 #ifdef KERNEL_LINUX
88         unsigned int running, sleeping, zombies, stopped, paging, blocked;
89
90         char buf[BUFSIZE];
91         char filename[20]; /* need 17 bytes */
92         char *fields[BUFSIZE];
93
94         struct dirent *ent;
95         DIR *proc;
96         FILE *fh;
97
98         running = sleeping = zombies = stopped = paging = blocked = 0;
99
100         if ((proc = opendir ("/proc")) == NULL)
101         {
102                 syslog (LOG_ERR, "Cannot open `/proc': %s", strerror (errno));
103                 return;
104         }
105
106         while ((ent = readdir (proc)) != NULL)
107         {
108                 if (!isdigit (ent->d_name[0]))
109                         continue;
110
111                 if (snprintf (filename, 20, "/proc/%s/stat", ent->d_name) >= 20)
112                         continue;
113
114                 if ((fh = fopen (filename, "r")) == NULL)
115                 {
116                         syslog (LOG_ERR, "Cannot open `%s': %s", filename, strerror (errno));
117                         continue;
118                 }
119
120                 if (fgets (buf, BUFSIZE, fh) == NULL)
121                 {
122                         fclose (fh);
123                         continue;
124                 }
125
126                 fclose (fh);
127
128                 if (strsplit (buf, fields, BUFSIZE) < 3)
129                         continue;
130
131                 switch (fields[2][0])
132                 {
133                         case 'R': running++;  break;
134                         case 'S': sleeping++; break;
135                         case 'D': blocked++;  break;
136                         case 'Z': zombies++;  break;
137                         case 'T': stopped++;  break;
138                         case 'W': paging++;   break;
139                 }
140         }
141
142         closedir(proc);
143
144         ps_submit (running, sleeping, zombies, stopped, paging, blocked);
145 /* #endif defined(KERNEL_LINUX) */
146
147 #elif HAVE_SYSCTLBYNAME
148         int mib[3];
149         size_t len;
150         size_t num;
151         int i;
152         int tries;
153         struct kinfo_proc *kp;
154
155         unsigned int state_idle   = 0;
156         unsigned int state_run    = 0;
157         unsigned int state_sleep  = 0;
158         unsigned int state_stop   = 0;
159         unsigned int state_zombie = 0;
160
161         mib[0] = CTL_KERN;
162         mib[1] = KERN_PROC;
163         mib[2] = KERN_PROC_ALL;
164
165         tries = 0;
166         kp    = NULL;
167         while (1)
168         {
169                 if (tries >= 3)
170                         return;
171                 tries++;
172
173                 len = 0;
174                 if (sysctl(mib, 3, NULL, &len, NULL, 0) != 0)
175                 {
176                         syslog (LOG_ERR, "processes: sysctl failed: %s",
177                                         strerror (errno));
178                         return;
179                 }
180
181                 if ((kp = (struct kinfo_proc *) malloc (len)) == NULL)
182                 {
183                         syslog (LOG_ERR, "processes: malloc failed: %s",
184                                         strerror (errno));
185                         return;
186                 }
187
188                 if (sysctl(mib, 3, (void *) kp, &len, NULL, 0) != 0)
189                 {
190                         syslog (LOG_WARNING, "processes: sysctl failed: %s",
191                                         strerror (errno));
192                         free (kp);
193                         kp = NULL;
194                         continue;
195                 }
196
197                 break;
198         } /* while true */
199
200         /* If we get past the while-loop, `kp' containes a valid `struct
201          * kinfo_proc'. */
202
203         num = len / sizeof (struct kinfo_proc);
204
205         for (i = 0; i < num; i++)
206         {
207                 DBG ("%3i: Process %i is in state %i", i,
208                                 (int) kp[i].kp_proc.p_pid,
209                                 (int) kp[i].kp_proc.p_stat);
210
211                 switch (kp[i].kp_proc.p_stat)
212                 {
213                         case SIDL:
214                                 state_idle++;
215                                 break;
216
217                         case SRUN:
218                                 state_run++;
219                                 break;
220
221                         case SSLEEP:
222 #ifdef P_SINTR
223                                 if ((kp[i].kp_proc.p_flag & P_SINTR) == 0)
224                                         state_sleep++; /* TODO change this to `state_blocked' or something.. */
225                                 else
226 #endif /* P_SINTR */
227                                         state_sleep++;
228                                 break;
229
230                         case SSTOP:
231                                 state_stop++;
232                                 break;
233
234                         case SZOMB:
235                                 state_zombie++;
236                                 break;
237
238                         default:
239                                 syslog (LOG_WARNING, "processes: PID %i in unknown state 0x%2x",
240                                                 (int) kp[i].kp_proc.p_pid,
241                                                 (int) kp[i].kp_proc.p_stat);
242                 } /* switch (state) */
243         } /* for (i = 0 .. num-1) */
244
245         free (kp);
246
247         if (state_run || state_idle || state_sleep || state_zombie)
248                 ps_submit (state_run, state_idle + state_sleep, state_zombie,
249                                 state_stop, -1, -1);
250 #endif /* HAVE_SYSCTLBYNAME */
251 }
252 #else
253 # define ps_read NULL
254 #endif /* PROCESSES_HAVE_READ */
255
256 void module_register (void)
257 {
258         plugin_register (MODULE_NAME, ps_init, ps_read, ps_write);
259 }
260
261 #undef BUFSIZE
262 #undef MODULE_NAME