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