1 /*-
2  * Copyright (c) 2007-2011 Varnish Software AS
3  * All rights reserved.
4  *
5  * Author: Cecilie Fritzvold <cecilihf@linpro.no>
6  * Author: Tollef Fog Heen <tfheen@varnish-software.com>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $Id$
30  *
31  * Nagios plugin for Varnish
32  */
33 
34 #include <inttypes.h>
35 #include <limits.h>
36 #include <stdint.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <time.h>
41 #include <unistd.h>
42 #include <sys/time.h>
43 #include <locale.h>
44 #include <assert.h>
45 
46 #if defined(HAVE_VARNISHAPI_4) || defined(HAVE_VARNISHAPI_4_1)
47 #include <vapi/vsc.h>
48 #include <vapi/vsm.h>
49 #elif defined(HAVE_VARNISHAPI_3)
50 #include "vsc.h"
51 #include "varnishapi.h"
52 #endif
53 
54 static int verbose = 0;
55 
56 struct range {
57 	intmax_t	lo;
58 	intmax_t	hi;
59 	int		inverted:1;
60 	int		defined:1;
61 };
62 
63 static struct range critical;
64 static struct range warning;
65 
66 enum {
67 	NAGIOS_OK = 0,
68 	NAGIOS_WARNING = 1,
69 	NAGIOS_CRITICAL = 2,
70 	NAGIOS_UNKNOWN = 3,
71 };
72 
73 static const char *status_text[] = {
74 	[NAGIOS_OK] = "OK",
75 	[NAGIOS_WARNING] = "WARNING",
76 	[NAGIOS_CRITICAL] = "CRITICAL",
77 	[NAGIOS_UNKNOWN] = "UNKNOWN",
78 };
79 
80 /*
81  * Parse a range specification
82  */
83 static int
parse_range(const char * spec,struct range * range)84 parse_range(const char *spec, struct range *range)
85 {
86 	const char *delim;
87 	char *end;
88 
89 	/* @ means invert the range */
90 	if (*spec == '@') {
91 		++spec;
92 		range->inverted = 1;
93 	} else {
94 		range->inverted = 0;
95 	}
96 
97 	/* empty spec... */
98 	if (*spec == '\0')
99 		return (-1);
100 
101 	if ((delim = strchr(spec, ':')) != NULL) {
102 		/*
103 		 * The Nagios plugin documentation says nothing about how
104 		 * to interpret ":N", so we disallow it.  Allowed forms
105 		 * are "~:N", "~:", "M:" and "M:N".
106 		 */
107 		if (delim - spec == 1 && *spec == '~') {
108 			range->lo = INTMAX_MIN;
109 		} else {
110 			range->lo = strtoimax(spec, &end, 10);
111 			if (end != delim)
112 				return (-1);
113 		}
114 		if (*(delim + 1) != '\0') {
115 			range->hi = strtoimax(delim + 1, &end, 10);
116 			if (*end != '\0')
117 				return (-1);
118 		} else {
119 			range->hi = INTMAX_MAX;
120 		}
121 	} else {
122 		/*
123 		 * Allowed forms are N
124 		 */
125 		range->lo = 0;
126 		range->hi = strtol(spec, &end, 10);
127 		if (*end != '\0')
128 			return (-1);
129 	}
130 
131 	/*
132 	 * Sanity
133 	 */
134 	if (range->lo > range->hi)
135 		return (-1);
136 
137 	range->defined = 1;
138 	return (0);
139 }
140 
141 /*
142  * Check if a given value is within a given range.
143  */
144 static int
inside_range(intmax_t value,const struct range * range)145 inside_range(intmax_t value, const struct range *range)
146 {
147 
148 	if (range->inverted)
149 		return (value < range->lo || value > range->hi);
150 	return (value >= range->lo && value <= range->hi);
151 }
152 
153 /*
154  * Check if the thresholds against the value and return the appropriate
155  * status code.
156  */
157 static int
check_thresholds(intmax_t value)158 check_thresholds(intmax_t value)
159 {
160 
161 	if (!warning.defined && !critical.defined)
162 		return (NAGIOS_UNKNOWN);
163 	if (critical.defined && !inside_range(value, &critical))
164 		return (NAGIOS_CRITICAL);
165 	if (warning.defined && !inside_range(value, &warning))
166 		return (NAGIOS_WARNING);
167 	return (NAGIOS_OK);
168 }
169 
170 struct stat_priv {
171 	char *param;
172 	const char *info;
173 	intmax_t value;
174 	int found;
175 	intmax_t cache_hit;
176 	intmax_t cache_miss;
177 };
178 
179 static int
check_stats_cb(void * priv,const struct VSC_point * const pt)180 check_stats_cb(void *priv, const struct VSC_point * const pt)
181 {
182 	struct stat_priv *p;
183 	char tmp[1024];
184 
185 	if (pt == NULL)
186 		return(0);
187 
188 #if defined(HAVE_VARNISHAPI_4_1) || defined(HAVE_VARNISHAPI_4)
189 	assert(sizeof(tmp) > (strlen(pt->section->fantom->type) + 1 +
190 			      strlen(pt->section->fantom->ident) + 1 +
191 			      strlen(pt->desc->name) + 1));
192 	snprintf(tmp, sizeof(tmp), "%s%s%s%s%s",
193 		(pt->section->fantom->type[0] == 0 ? "" : pt->section->fantom->type),
194 		(pt->section->fantom->type[0] == 0 ? "" : "."),
195 		(pt->section->fantom->ident[0] == 0 ? "" : pt->section->fantom->ident),
196 		(pt->section->fantom->ident[0] == 0 ? "" : "."),
197 		 pt->desc->name);
198 	p = priv;
199 #endif
200 #if defined(HAVE_VARNISHAPI_4_1)
201 	assert(!strcmp(pt->desc->ctype, "uint64_t"));
202 #elif defined(HAVE_VARNISHAPI_4)
203 	assert(!strcmp(pt->desc->fmt, "uint64_t"));
204 #elif defined(HAVE_VARNISHAPI_3)
205 	assert(sizeof(tmp) > (strlen(pt->class) + 1 +
206 			      strlen(pt->ident) + 1 +
207 			      strlen(pt->name) + 1));
208 	snprintf(tmp, sizeof(tmp), "%s%s%s%s%s",
209 		(pt->class[0] == 0 ? "" : pt->class),
210 		(pt->class[0] == 0 ? "" : "."),
211 		(pt->ident[0] == 0 ? "" : pt->ident),
212 		(pt->ident[0] == 0 ? "" : "."),
213 		 pt->name);
214 	p = priv;
215 	assert(!strcmp(pt->fmt, "uint64_t"));
216 #endif
217 	if (strcmp(tmp, p->param) == 0) {
218 		p->found = 1;
219 #if defined(HAVE_VARNISHAPI_4) || defined(HAVE_VARNISHAPI_4_1)
220 		p->info = pt->desc->sdesc;
221 #elif defined(HAVE_VARNISHAPI_3)
222 		p->info = pt->desc;
223 #endif
224 		p->value = *(const volatile uint64_t*)pt->ptr;
225 	} else if (strcmp(p->param, "ratio") == 0) {
226 		if (strcmp(tmp, "cache_hit") == 0 || strcmp(tmp, "MAIN.cache_hit") == 0) {
227 			p->found = 1;
228 			p->cache_hit = *(const volatile uint64_t*)pt->ptr;
229 		} else if (strcmp(tmp, "cache_miss") == 0 || strcmp(tmp, "MAIN.cache_miss") == 0) {
230 			p->cache_miss = *(const volatile uint64_t*)pt->ptr;
231 		}
232 	}
233 	return (0);
234 }
235 
236 /*
237  * Check the statistics for the requested parameter.
238  */
239 static void
check_stats(struct VSM_data * vd,char * param)240 check_stats(struct VSM_data *vd, char *param)
241 {
242 	int status;
243 	struct stat_priv priv;
244 
245 	priv.found = 0;
246 	priv.param = param;
247 
248 #if defined(HAVE_VARNISHAPI_4) || defined(HAVE_VARNISHAPI_4_1)
249 	(void)VSC_Iter(vd, NULL, check_stats_cb, &priv);
250 #elif defined(HAVE_VARNISHAPI_3)
251 	(void)VSC_Iter(vd, check_stats_cb, &priv);
252 #endif
253 	if (strcmp(param, "ratio") == 0) {
254 		intmax_t total = priv.cache_hit + priv.cache_miss;
255 		priv.value = total ? (100 * priv.cache_hit / total) : 0;
256 		priv.info = "Cache hit ratio";
257 	}
258 	if (priv.found != 1) {
259 		printf("Unknown parameter '%s'\n", param);
260 		exit(1);
261 	}
262 
263 	status = check_thresholds(priv.value);
264 	printf("VARNISH %s: %s (%'jd)|%s=%jd\n", status_text[status],
265 	       priv.info, priv.value, param, priv.value);
266 	exit(status);
267 }
268 
269 /*-------------------------------------------------------------------------------*/
270 
271 static void
help(void)272 help(void)
273 {
274 
275 	fprintf(stderr, "usage: "
276 	    "check_varnish [-lv] [-n varnish_name] [-p param_name [-c N] [-w N]]\n"
277 	    "\n"
278 	    "-v              Increase verbosity.\n"
279 	    "-n varnish_name Specify the Varnish instance name\n"
280 	    "-p param_name   Specify the parameter to check (see below).\n"
281 	    "                The default is 'ratio'.\n"
282 	    "-c [@][lo:]hi   Set critical threshold\n"
283 	    "-w [@][lo:]hi   Set warning threshold\n"
284 	    "\n"
285 	    "All items reported by varnishstat(1) are available - use the\n"
286 	    "identifier listed in the left column by 'varnishstat -l'.\n"
287 	    "\n"
288 	    "Examples for Varnish 3.x:\n"
289 	    "\n"
290 	    "uptime          How long the cache has been running (in seconds)\n"
291 	    "ratio           The cache hit ratio expressed as a percentage of hits to\n"
292 	    "                hits + misses.  Default thresholds are 95 and 90.\n"
293 	    "usage           Cache file usage as a percentage of the total cache space.\n"
294 	    "\n"
295 	    "Examples for Varnish 4.x:\n"
296 	    "\n"
297 	    "ratio           The cache hit ratio expressed as a percentage of hits to\n"
298 	    "                hits + misses.  Default thresholds are 95 and 90.\n"
299 	    "MAIN.uptime     How long the cache has been running (in seconds).\n"
300 	    "MAIN.n_purges   Number of purge operations executed.\n"
301 	);
302 	exit(0);
303 }
304 
305 static void
usage(void)306 usage(void)
307 {
308 
309 	fprintf(stderr, "usage: "
310 	    "check_varnish [-lv] [-n varnish_name] [-p param_name [-c N] [-w N]]\n");
311 	exit(3);
312 }
313 
314 
315 int
main(int argc,char ** argv)316 main(int argc, char **argv)
317 {
318 	struct VSM_data *vd;
319 	char *param = NULL;
320 	int opt;
321 
322 	setlocale(LC_ALL, "");
323 
324 	vd = VSM_New();
325 #if defined(HAVE_VARNISHAPI_3)
326 	VSC_Setup(vd);
327 #endif
328 
329 	while ((opt = getopt(argc, argv, "f:n:N:c:hn:p:vw:")) != -1) {
330 		switch (opt) {
331 		case 'c':
332 			if (parse_range(optarg, &critical) != 0)
333 				usage();
334 			break;
335 		case 'h':
336 			help();
337 			break;
338 		case 'n':
339 			VSC_Arg(vd, opt, optarg);
340 			break;
341 		case 'p':
342 			param = strdup(optarg);
343 			break;
344 		case 'v':
345 			++verbose;
346 			break;
347 		case 'w':
348 			if (parse_range(optarg, &warning) != 0)
349 				usage();
350 			break;
351 		default:
352 			if (VSC_Arg(vd, opt, optarg) > 0)
353 				break;
354 			usage();
355 		}
356 	}
357 
358 #if defined(HAVE_VARNISHAPI_4) || defined(HAVE_VARNISHAPI_4_1)
359 	if (VSM_Open(vd))
360 		exit(1);
361 #elif defined(HAVE_VARNISHAPI_3)
362 	if (VSC_Open(vd, 1))
363 		exit(1);
364 #endif
365 
366 	/* Default: if no param specified, check hit ratio.  If no warning
367 	 * and critical values are specified either, set these to default.
368 	 */
369 	if (param == NULL) {
370 		param = strdup("ratio");
371 		if (!warning.defined)
372 			parse_range("95:", &warning);
373 		if (!critical.defined)
374 			parse_range("90:", &critical);
375 	}
376 
377 	if (!param)
378 		usage();
379 
380 	check_stats(vd, param);
381 
382 	exit(0);
383 }
384