1 /*----------------------------------------------------------------------------*/
2 /* Xymon RRD handler module. */
3 /* */
4 /* Copyright (C) 2004-2011 Henrik Storner <henrik@hswn.dk> */
5 /* */
6 /* This program is released under the GNU General Public License (GPL), */
7 /* version 2. See the file "COPYING" for details. */
8 /* */
9 /*----------------------------------------------------------------------------*/
10
11 static char vmstat_rcsid[] = "$Id: do_vmstat.c 8066 2019-05-03 22:42:00Z jccleaver $";
12
13 typedef struct vmstat_layout_t {
14 int index;
15 char *name;
16 } vmstat_layout_t;
17
18 /* This one matches the vmstat output from Solaris 8, possibly earlier ones as well */
19 /* LARRD 0.43c compatible. */
20 static vmstat_layout_t vmstat_solaris_layout[] = {
21 { 0, "cpu_r" },
22 { 1, "cpu_b" },
23 { 2, "cpu_w" },
24 { 3, "mem_swap" },
25 { 4, "mem_free" },
26 { 5, "mem_re" },
27 { 6, "mem_mf" },
28 { 7, "mem_pi" },
29 { 8, "mem_po" },
30 { 11, "sr" },
31 { 16, "cpu_int" },
32 { 17, "cpu_syc" },
33 { 18, "cpu_csw" },
34 { 19, "cpu_usr" },
35 { 20, "cpu_sys" },
36 { 21, "cpu_idl" },
37 { -1, NULL }
38 };
39
40 /* This one for OSF */
41 /* LARRD 0.43c compatible */
42 static vmstat_layout_t vmstat_osf_layout[] = {
43 { 0, "cpu_r" },
44 { 1, "cpu_b" },
45 { 2, "cpu_w" },
46 { 4, "mem_free" },
47 { 6, "mem_mf" },
48 { 10, "mem_pi" },
49 { 11, "mem_po" },
50 { 12, "cpu_int" },
51 { 13, "cpu_syc" },
52 { 14, "cpu_csw" },
53 { 15, "cpu_usr" },
54 { 16, "cpu_sys" },
55 { 17, "cpu_idl" },
56 { -1, NULL }
57 };
58
59 /* This one for AIX */
60 /* LARRD 0.43c compatible */
61 static vmstat_layout_t vmstat_aix_layout[] = {
62 { 0, "cpu_r" },
63 { 1, "cpu_b" },
64 { 2, "mem_avm" },
65 { 3, "mem_free" },
66 { 4, "mem_re" },
67 { 5, "mem_pi" },
68 { 6, "mem_po" },
69 { 7, "mem_fr" },
70 { 8, "sr" },
71 { 9, "mem_cy" },
72 { 10, "cpu_int" },
73 { 11, "cpu_syc" },
74 { 12, "cpu_csw" },
75 { 13, "cpu_usr" },
76 { 14, "cpu_sys" },
77 { 15, "cpu_idl" },
78 { 16, "cpu_wait" },
79 { 17, "cpu_pc" },
80 { 18, "cpu_ec" },
81 { -1, NULL }
82 };
83
84 /* This is for AIX running on Power5 cpu's. */
85 static vmstat_layout_t vmstat_aix_power5_layout[] = {
86 { 0, "cpu_r" },
87 { 1, "cpu_b" },
88 { 2, "mem_avm" },
89 { 3, "mem_free" },
90 { 4, "mem_re" },
91 { 5, "mem_pi" },
92 { 6, "mem_po" },
93 { 7, "mem_fr" },
94 { 8, "sr" },
95 { 9, "mem_cy" },
96 { 10, "cpu_int" },
97 { 11, "cpu_syc" },
98 { 12, "cpu_csw" },
99 { 13, "cpu_usr" },
100 { 14, "cpu_sys" },
101 { 15, "cpu_idl" },
102 { 16, "cpu_wait" },
103 { 17, "cpu_pc" },
104 { 18, "cpu_ec" },
105 { -1, NULL }
106 };
107
108 /* This one for Christian Perrier's hacked IRIX "vmstat" done with sar */
109 static vmstat_layout_t vmstat_irix_layout[] = {
110 { 1, "cpu_usr" },
111 { 2, "cpu_sys" },
112 { 3, "cpu_int" },
113 { 4, "cpu_wait" },
114 { 5, "cpu_idl" },
115 { -1, "cpu_csw" }, /* Not available, but having it in the RRD makes vmstat3 graph (int+csw) work */
116 { -1, NULL }
117 };
118
119 /* This one matches FreeBSD 4.10 */
120 /* LARRD 0.43c compatible */
121 static vmstat_layout_t vmstat_freebsd4_layout[] = {
122 { 0, "cpu_r" },
123 { 1, "cpu_b" },
124 { 2, "cpu_w" },
125 { 3, "mem_avm" },
126 { 4, "mem_free" },
127 { 5, "mem_flt" },
128 { 6, "mem_re" },
129 { 7, "mem_pi" },
130 { 8, "mem_po" },
131 { 9, "mem_fr" },
132 { 10, "sr" },
133 { 11, "dsk_da0" },
134 { 12, "dsk_fd0" },
135 { 13, "cpu_int" },
136 { 15, "cpu_csw" },
137 { 16, "cpu_sys" },
138 { 17, "cpu_usr" },
139 { 18, "cpu_idl" },
140 { -1, NULL }
141 };
142
143 /* FreeBSD v6 and later, possibly v5 also */
144 static vmstat_layout_t vmstat_freebsd_layout[] = {
145 { 0, "cpu_r" },
146 { 1, "cpu_b" },
147 { 2, "cpu_w" },
148 { 3, "mem_avm" },
149 { 4, "mem_free" },
150 { 5, "mem_flt" },
151 { 6, "mem_re" },
152 { 7, "mem_pi" },
153 { 8, "mem_po" },
154 { 9, "mem_fr" },
155 { 10, "sr" },
156 { 11, "dsk_da0" },
157 { 12, "dsk_pa0" },
158 { 13, "cpu_int" },
159 { 14, "cpu_syc" },
160 { 15, "cpu_csw" },
161 { 16, "cpu_usr" },
162 { 17, "cpu_sys" },
163 { 18, "cpu_idl" },
164 { -1, NULL }
165 };
166
167 /* This one matches NetBSD 2.0 */
168 /* LARRD 0.43c does not support NetBSD */
169 static vmstat_layout_t vmstat_netbsd_layout[] = {
170 { 0, "cpu_r" },
171 { 1, "cpu_b" },
172 { 2, "cpu_w" },
173 { 3, "mem_avm" },
174 { 4, "mem_free" },
175 { 5, "mem_flt" },
176 { 6, "mem_re" },
177 { 7, "mem_pi" },
178 { 8, "mem_po" },
179 { 9, "mem_fr" },
180 { 10, "sr" },
181 { 11, "dsk_f0" },
182 { 12, "dsk_m0" },
183 { 13, "dsk_w0" },
184 { 14, "cpu_int" },
185 { 15, "cpu_syc" },
186 { 16, "cpu_csw" },
187 { 17, "cpu_usr" },
188 { 18, "cpu_sys" },
189 { 19, "cpu_idl" },
190 { -1, NULL }
191 };
192
193 static vmstat_layout_t vmstat_openbsd_layout[] = {
194 { 0, "cpu_r" },
195 { 1, "cpu_b" },
196 { 2, "cpu_w" },
197 { 3, "mem_avm" },
198 { 4, "mem_free" },
199 { 5, "mem_flt" },
200 { 6, "mem_re" },
201 { 7, "mem_pi" },
202 { 8, "mem_po" },
203 { 9, "mem_fr" },
204 { 10, "sr" },
205 { 11, "dsk_wd0" },
206 { 12, "dsk_cd0" },
207 { 13, "cpu_int" },
208 { 14, "cpu_syc" },
209 { 15, "cpu_csw" },
210 { 16, "cpu_usr" },
211 { 17, "cpu_sys" },
212 { 18, "cpu_idl" },
213 { -1, NULL }
214 };
215
216 /* This one for HP/UX */
217 /* LARRD 0.43c does not support HP-UX */
218 static vmstat_layout_t vmstat_hpux_layout[] = {
219 { 0, "cpu_r" },
220 { 1, "cpu_b" },
221 { 2, "cpu_w" },
222 { 3, "mem_avm" },
223 { 4, "mem_free" },
224 { 5, "mem_re" },
225 { 6, "mem_flt" },
226 { 7, "mem_pi" },
227 { 8, "mem_po" },
228 { 9, "mem_fr" },
229 { 11, "sr" },
230 { 12, "cpu_int" },
231 { 14, "cpu_csw" },
232 { 15, "cpu_usr" },
233 { 16, "cpu_sys" },
234 { 17, "cpu_idl" },
235 { -1, NULL }
236 };
237
238 /* This one is all newer Linux procps versions, with kernel 2.4+ */
239 /* NOT compatible with LARRD 0.43c */
240 static vmstat_layout_t vmstat_linux_layout[] = {
241 { 0, "cpu_r" },
242 { 1, "cpu_b" },
243 { -1, "cpu_w" }, /* Not present for 2.4+ kernels, so log as "Undefined" */
244 { 2, "mem_swpd" },
245 { 3, "mem_free" },
246 { 4, "mem_buff" },
247 { 5, "mem_cach" },
248 { 6, "mem_si" },
249 { 7, "mem_so" },
250 { 8, "dsk_bi" },
251 { 9, "dsk_bo" },
252 { 10, "cpu_int" },
253 { 11, "cpu_csw" },
254 { 12, "cpu_usr" },
255 { 13, "cpu_sys" },
256 { 14, "cpu_idl" },
257 { 15, "cpu_wait" }, /* Requires kernel 2.6, but may not be present */
258 { -1, NULL }
259 };
260
261 /*
262 * This one is for Red Hat Enterprise Linux 3. Identical to the "linux" layout,
263 * except Red Hat for some reason decided to swap the cpu_wait and cpu_idle columns.
264 */
265 /* NOT compatible with LARRD 0.43c */
266 static vmstat_layout_t vmstat_rhel3_layout[] = {
267 { 0, "cpu_r" },
268 { 1, "cpu_b" },
269 { -1, "cpu_w" },
270 { 2, "mem_swpd" },
271 { 3, "mem_free" },
272 { 4, "mem_buff" },
273 { 5, "mem_cach" },
274 { 6, "mem_si" },
275 { 7, "mem_so" },
276 { 8, "dsk_bi" },
277 { 9, "dsk_bo" },
278 { 10, "cpu_int" },
279 { 11, "cpu_csw" },
280 { 12, "cpu_usr" },
281 { 13, "cpu_sys" },
282 { 14, "cpu_wait" },
283 { 15, "cpu_idl" },
284 { -1, NULL }
285 };
286
287 /* This one is for Debian 3.0 (Woody), and possibly others with a Linux 2.2 kernel */
288 /* NOT compatible with LARRD 0.43c */
289 static vmstat_layout_t vmstat_linux22_layout[] = {
290 { 0, "cpu_r" },
291 { 1, "cpu_b" },
292 { 2, "cpu_w" },
293 { 3, "mem_swpd" },
294 { 4, "mem_free" },
295 { 5, "mem_buff" },
296 { 6, "mem_cach" },
297 { 7, "mem_si" },
298 { 8, "mem_so" },
299 { 9, "dsk_bi" },
300 { 10, "dsk_bo" },
301 { 11, "cpu_int" },
302 { 12, "cpu_csw" },
303 { 13, "cpu_usr" },
304 { 14, "cpu_sys" },
305 { 15, "cpu_idl" },
306 { -1, "cpu_wait" },
307 { -1, NULL }
308 };
309
310 /*This one is for sco_sv */
311 /* NOT compatible with LARRD 0.43c */
312 static vmstat_layout_t vmstat_sco_sv_layout[] = {
313 { 0, "cpu_r" },
314 { 1, "cpu_b" },
315 { 2, "cpu_w" },
316 { 3, "mem_free" },
317 { 4, "mem_dmd" },
318 { 5, "mem_swpd" },
319 { 6, "mem_cach" },
320 { 7, "mem_fil" },
321 { 8, "mem_flt" },
322 { 9, "mem_frd" },
323 { 10, "mem_pos" },
324 { 11, "mem_pif" },
325 { 12, "mem_pis" },
326 { 13, "mem_so" },
327 { 14, "mem_si" },
328 { 15, "sys_calls" },
329 { 16, "cpu_csw" },
330 { 17, "cpu_usr" },
331 { 18, "cpu_sys" },
332 { 19, "cpu_idl" },
333 /* { -1, "cpu_wait" }, */
334 { -1, NULL }
335 };
336
337 #define MAX_VMSTAT_VALUES 30
338
do_vmstat_rrd(char * hostname,char * testname,char * classname,char * pagepaths,char * msg,time_t tstamp)339 int do_vmstat_rrd(char *hostname, char *testname, char *classname, char *pagepaths, char *msg, time_t tstamp)
340 {
341 enum ostype_t ostype;
342 vmstat_layout_t *layout = NULL;
343 char *datapart = msg;
344 int values[MAX_VMSTAT_VALUES];
345 int defcount, defidx, datacount, result;
346 char *p;
347 char **creparams;
348
349 if ((strncmp(msg, "status", 6) == 0) || (strncmp(msg, "data", 4) == 0)) {
350 /* Full message, including "status" or "data" line - so skip the first line. */
351 datapart = strchr(msg, '\n');
352 if (datapart) {
353 datapart++;
354 }
355 else {
356 errprintf("Too few lines (only 1) in vmstat report from %s\n", hostname);
357 return -1;
358 }
359 }
360
361 ostype = get_ostype(datapart);
362 datapart = strchr(datapart, '\n');
363 if (datapart) {
364 datapart++;
365 }
366 else {
367 errprintf("Too few lines (only 1 or 2) in vmstat report from %s\n", hostname);
368 return -1;
369 }
370
371 /* Pick up the values in the datapart line. Stop at newline. */
372 p = strchr(datapart, '\n'); if (p) *p = '\0';
373 p = strtok(datapart, " "); datacount = 0;
374 while (p && (datacount < MAX_VMSTAT_VALUES)) {
375
376 /* Removing . and , from the numbers */
377 char *p1;
378 while ( (p1 = strchr(p,'.')) != NULL ) {
379 strcpy (p1, p1+1) ;
380 }
381 char *p2;
382 while ( (p2 = strchr(p,',')) != NULL ) {
383 strcpy (p2, p2+1) ;
384 }
385
386 values[datacount++] = atoi(p);
387 p = strtok(NULL, " ");
388 }
389
390 /* Must do this now, to check on the layout of any existing file */
391 setupfn("%s.rrd", "vmstat");
392
393 switch (ostype) {
394 case OS_SOLARIS:
395 layout = vmstat_solaris_layout; break;
396 case OS_OSF:
397 layout = vmstat_osf_layout; break;
398
399 case OS_AIX:
400 /* Special, because there are two layouts for AIX */
401 {
402 char **dsnames = NULL;
403 int dscount, i;
404
405 dscount = rrddatasets(hostname, &dsnames);
406 layout = ((dscount == 17) ? vmstat_aix_layout : vmstat_aix_power5_layout);
407
408 if ((dscount > 0) && dsnames) {
409 /* Free the dsnames list */
410 for (i=0; (i<dscount); i++) xfree(dsnames[i]);
411 xfree(dsnames);
412 }
413 }
414 break;
415
416 case OS_IRIX:
417 layout = vmstat_irix_layout; break;
418 case OS_HPUX:
419 layout = vmstat_hpux_layout; break;
420 case OS_FREEBSD:
421 /*
422 * Special, because there are two layouts for FreeBSD
423 * FreeBSD v4.x has 19 fields, later versions of FreeBSD
424 * are the same as OpenBSD, with 18 fields.
425 */
426 {
427 char **dsnames = NULL;
428 int dscount, i;
429
430 dscount = rrddatasets(hostname, &dsnames);
431 layout = ((dscount == 19) ? vmstat_freebsd4_layout : vmstat_freebsd_layout);
432
433 if ((dscount > 0) && dsnames) {
434 /* Free the dsnames list */
435 for (i=0; (i<dscount); i++) xfree(dsnames[i]);
436 xfree(dsnames);
437 }
438 }
439 break;
440 case OS_NETBSD:
441 layout = vmstat_netbsd_layout; break;
442 case OS_OPENBSD:
443 layout = vmstat_openbsd_layout; break;
444 case OS_LINUX22:
445 layout = vmstat_linux22_layout; break;
446 case OS_LINUX:
447 case OS_ZVM:
448 case OS_ZVSE:
449 case OS_ZOS:
450 layout = vmstat_linux_layout; break;
451 case OS_RHEL3:
452 layout = vmstat_rhel3_layout; break;
453 case OS_SCO_SV:
454 layout = vmstat_sco_sv_layout; break;
455 case OS_UNKNOWN:
456 errprintf("Host '%s' reports vmstat for an unknown OS\n", hostname);
457 return -1;
458 default:
459 errprintf("Cannot handle vmstat from host '%s' \n", hostname);
460 return -1;
461 }
462
463 if (layout == NULL) {
464 return -1;
465 }
466
467 /* How many values are defined in the dataset ? */
468 for (defcount = 0; (layout[defcount].name); defcount++) ;
469
470 /* Setup the create-parameters */
471 creparams = (char **)malloc((defcount+1)*sizeof(char *));
472 for (defidx=0; (defidx < defcount); defidx++) {
473 creparams[defidx] = (char *)malloc(strlen(layout[defidx].name) + strlen("DS::GAUGE:600:0:U") + 1);
474 sprintf(creparams[defidx], "DS:%s:GAUGE:600:0:U", layout[defidx].name);
475 }
476 creparams[defcount] = NULL;
477
478 /* Setup the update string, picking out values according to the layout */
479 p = rrdvalues + snprintf(rrdvalues, sizeof(rrdvalues), "%d", (int)tstamp);
480 for (defidx=0; (defidx < defcount); defidx++) {
481 int dataidx = layout[defidx].index;
482
483 if ((dataidx >= datacount) || (dataidx == -1)) {
484 p += snprintf(p, sizeof(rrdvalues)-(p-rrdvalues), ":U");
485 }
486 else {
487 p += snprintf(p, sizeof(rrdvalues)-(p-rrdvalues), ":%d", values[layout[defidx].index]);
488 }
489 }
490
491 result = create_and_update_rrd(hostname, testname, classname, pagepaths, creparams, NULL);
492
493 for (defidx=0; (defidx < defcount); defidx++) xfree(creparams[defidx]);
494 xfree(creparams);
495
496 return result;
497 }
498
499