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