1 /*****************************************************************************
2 *
3 * Nagios check_snmp plugin
4 *
5 * License: GPL
6 * Copyright (c) 1999-2007 Nagios Plugins Development Team
7 *
8 * Description:
9 *
10 * This file contains the check_snmp plugin
11 *
12 * Check status of remote machines and obtain system information via SNMP
13 *
14 *
15 * This program is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation, either version 3 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27 *
28 *
29 *****************************************************************************/
31 const char *progname = "check_snmp";
32 const char *copyright = "1999-2007";
33 const char *email = "nagiosplug-devel@lists.sourceforge.net";
35 #include "common.h"
36 #include "utils.h"
37 #include "utils_cmd.h"
39 #define DEFAULT_COMMUNITY "public"
40 #define DEFAULT_PORT "161"
41 #define DEFAULT_MIBLIST "ALL"
42 #define DEFAULT_PROTOCOL "1"
43 #define DEFAULT_TIMEOUT 1
44 #define DEFAULT_RETRIES 5
45 #define DEFAULT_AUTH_PROTOCOL "MD5"
46 #define DEFAULT_PRIV_PROTOCOL "DES"
47 #define DEFAULT_DELIMITER "="
48 #define DEFAULT_OUTPUT_DELIMITER " "
50 #define mark(a) ((a)!=0?"*":"")
52 #define CHECK_UNDEF 0
53 #define CRIT_PRESENT 1
54 #define CRIT_STRING 2
55 #define CRIT_REGEX 4
56 #define WARN_PRESENT 8
57 #define WARN_STRING 16
58 #define WARN_REGEX 32
60 #define MAX_OIDS 8
62 /* Longopts only arguments */
63 #define L_CALCULATE_RATE CHAR_MAX+1
64 #define L_RATE_MULTIPLIER CHAR_MAX+2
65 #define L_INVERT_SEARCH CHAR_MAX+3
67 /* Gobble to string - stop incrementing c when c[0] match one of the
68 * characters in s */
69 #define GOBBLE_TOS(c, s) while(c[0]!='\0' && strchr(s, c[0])==NULL) { c++; }
70 /* Given c, keep track of backslashes (bk) and double-quotes (dq)
71 * from c[0] */
72 #define COUNT_SEQ(c, bk, dq) switch(c[0]) {\
73 case '\\': \
74 if (bk) bk--; \
75 else bk++; \
76 break; \
77 case '"': \
78 if (!dq) { dq++; } \
79 else if(!bk) { dq--; } \
80 else { bk--; } \
81 break; \
82 }
86 int process_arguments (int, char **);
87 int validate_arguments (void);
88 char *thisarg (char *str);
89 char *nextarg (char *str);
90 void print_usage (void);
91 void print_help (void);
93 #include "regex.h"
94 char regex_expect[MAX_INPUT_BUFFER] = "";
95 regex_t preg;
96 regmatch_t pmatch[10];
97 char errbuf[MAX_INPUT_BUFFER] = "";
98 char perfstr[MAX_INPUT_BUFFER] = "| ";
99 int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
100 int eflags = 0;
101 int errcode, excode;
103 char *server_address = NULL;
104 char *community = NULL;
105 char **authpriv = NULL;
106 char *proto = NULL;
107 char *seclevel = NULL;
108 char *secname = NULL;
109 char *authproto = NULL;
110 char *privproto = NULL;
111 char *authpasswd = NULL;
112 char *privpasswd = NULL;
113 char **oids = NULL;
114 char *label;
115 char *units;
116 char *port;
117 char *snmpcmd;
118 char string_value[MAX_INPUT_BUFFER] = "";
119 int invert_search=0;
120 char **labels = NULL;
121 char **unitv = NULL;
122 size_t nlabels = 0;
123 size_t labels_size = 8;
124 size_t nunits = 0;
125 size_t unitv_size = 8;
126 int numoids = 0;
127 int numauthpriv = 0;
128 int verbose = 0;
129 int usesnmpgetnext = FALSE;
130 char *warning_thresholds = NULL;
131 char *critical_thresholds = NULL;
132 thresholds *thlds[MAX_OIDS];
133 double response_value[MAX_OIDS];
134 int retries = 0;
135 int eval_method[MAX_OIDS];
136 char *delimiter;
137 char *output_delim;
138 char *miblist = NULL;
139 int needmibs = FALSE;
140 int calculate_rate = 0;
141 int rate_multiplier = 1;
142 state_data *previous_state;
143 double previous_value[MAX_OIDS];
146 int
147 main (int argc, char **argv)
148 {
149 int i, len, line, total_oids;
150 unsigned int bk_count = 0, dq_count = 0;
151 int iresult = STATE_UNKNOWN;
152 int result = STATE_UNKNOWN;
153 int return_code = 0;
154 int external_error = 0;
155 char **command_line = NULL;
156 char *cl_hidden_auth = NULL;
157 char *oidname = NULL;
158 char *response = NULL;
159 char *mult_resp = NULL;
160 char *outbuff;
161 char *ptr = NULL;
162 char *show = NULL;
163 char *endptr = NULL;
164 char *th_warn=NULL;
165 char *th_crit=NULL;
166 char type[8] = "";
167 output chld_out, chld_err;
168 char *previous_string=NULL;
169 char *ap=NULL;
170 char *state_string=NULL;
171 size_t response_length, current_length, string_length;
172 char *temp_string=NULL;
173 time_t current_time;
174 double temp_double;
175 time_t duration;
176 char *conv = "12345678";
177 int is_counter=0;
179 setlocale (LC_ALL, "");
180 bindtextdomain (PACKAGE, LOCALEDIR);
181 textdomain (PACKAGE);
183 labels = malloc (labels_size);
184 unitv = malloc (unitv_size);
185 for (i = 0; i < MAX_OIDS; i++)
186 eval_method[i] = CHECK_UNDEF;
188 label = strdup ("SNMP");
189 units = strdup ("");
190 port = strdup (DEFAULT_PORT);
191 outbuff = strdup ("");
192 delimiter = strdup (" = ");
193 output_delim = strdup (DEFAULT_OUTPUT_DELIMITER);
194 timeout_interval = DEFAULT_TIMEOUT;
195 retries = DEFAULT_RETRIES;
197 np_init( (char *) progname, argc, argv );
199 /* Parse extra opts if any */
200 argv=np_extra_opts (&argc, argv, progname);
202 np_set_args(argc, argv);
204 if (process_arguments (argc, argv) == ERROR)
205 usage4 (_("Could not parse arguments"));
207 if(calculate_rate) {
208 if (!strcmp(label, "SNMP"))
209 label = strdup("SNMP RATE");
210 time(¤t_time);
211 i=0;
212 previous_state = np_state_read();
213 if(previous_state!=NULL) {
214 /* Split colon separated values */
215 previous_string = strdup((char *) previous_state->data);
216 while((ap = strsep(&previous_string, ":")) != NULL) {
217 if(verbose>2)
218 printf("State for %d=%s\n", i, ap);
219 previous_value[i++]=strtod(ap,NULL);
220 }
221 }
222 }
224 /* Populate the thresholds */
225 th_warn=warning_thresholds;
226 th_crit=critical_thresholds;
227 for (i=0; i<numoids; i++) {
228 char *w = th_warn ? strndup(th_warn, strcspn(th_warn, ",")) : NULL;
229 char *c = th_crit ? strndup(th_crit, strcspn(th_crit, ",")) : NULL;
230 /* Skip empty thresholds, while avoiding segfault */
231 set_thresholds(&thlds[i],
232 w ? strpbrk(w, NP_THRESHOLDS_CHARS) : NULL,
233 c ? strpbrk(c, NP_THRESHOLDS_CHARS) : NULL);
234 if (w) {
235 th_warn=strchr(th_warn, ',');
236 if (th_warn) th_warn++;
237 free(w);
238 }
239 if (c) {
240 th_crit=strchr(th_crit, ',');
241 if (th_crit) th_crit++;
242 free(c);
243 }
244 }
246 /* Create the command array to execute */
247 if(usesnmpgetnext == TRUE) {
248 snmpcmd = strdup (PATH_TO_SNMPGETNEXT);
249 }else{
250 snmpcmd = strdup (PATH_TO_SNMPGET);
251 }
253 /* 9 arguments to pass before authpriv options + 1 for host and numoids. Add one for terminating NULL */
254 command_line = calloc (9 + numauthpriv + 1 + numoids + 1, sizeof (char *));
255 command_line[0] = snmpcmd;
256 command_line[1] = strdup ("-t");
257 asprintf (&command_line[2], "%d", timeout_interval);
258 command_line[3] = strdup ("-r");
259 asprintf (&command_line[4], "%d", retries);
260 command_line[5] = strdup ("-m");
261 command_line[6] = strdup (miblist);
262 command_line[7] = "-v";
263 command_line[8] = strdup (proto);
265 for (i = 0; i < numauthpriv; i++) {
266 command_line[9 + i] = authpriv[i];
267 }
269 asprintf (&command_line[9 + numauthpriv], "%s:%s", server_address, port);
271 /* This is just for display purposes, so it can remain a string */
272 asprintf(&cl_hidden_auth, "%s -t %d -r %d -m %s -v %s %s %s:%s",
273 snmpcmd, timeout_interval, retries, strlen(miblist) ? miblist : "''", proto, "[authpriv]",
274 server_address, port);
276 for (i = 0; i < numoids; i++) {
277 command_line[9 + numauthpriv + 1 + i] = oids[i];
278 asprintf(&cl_hidden_auth, "%s %s", cl_hidden_auth, oids[i]);
279 }
281 command_line[9 + numauthpriv + 1 + numoids] = NULL;
283 if (verbose)
284 printf ("%s\n", cl_hidden_auth);
286 /* Run the command */
287 return_code = cmd_run_array (command_line, &chld_out, &chld_err, 0);
289 /* Due to net-snmp sometimes showing stderr messages with poorly formed MIBs,
290 only return state unknown if return code is non zero or there is no stdout.
291 Do this way so that if there is stderr, will get added to output, which helps problem diagnosis
292 */
293 if (return_code != 0)
294 external_error=1;
295 if (chld_out.lines == 0)
296 external_error=1;
297 if (external_error) {
298 if (chld_err.lines > 0) {
299 printf (_("External command error: %s\n"), chld_err.line[0]);
300 for (i = 1; i < chld_err.lines; i++) {
301 printf ("%s\n", chld_err.line[i]);
302 }
303 } else {
304 printf(_("External command error with no output (return code: %d)\n"), return_code);
305 }
306 exit (STATE_UNKNOWN);
307 }
309 if (verbose) {
310 for (i = 0; i < chld_out.lines; i++) {
311 printf ("%s\n", chld_out.line[i]);
312 }
313 }
315 for (line=0, i=0; line < chld_out.lines; line++, i++) {
316 if(calculate_rate)
317 conv = "%.10g";
318 else
319 conv = "%.0f";
321 ptr = chld_out.line[line];
322 oidname = strpcpy (oidname, ptr, delimiter);
323 response = strstr (ptr, delimiter);
324 if (response == NULL)
325 break;
327 if (verbose > 2) {
328 printf("Processing oid %i (line %i)\n oidname: %s\n response: %s\n", i+1, line+1, oidname, response);
329 }
331 /* Clean up type array - Sol10 does not necessarily zero it out */
332 bzero(type, sizeof(type));
334 is_counter=0;
335 /* We strip out the datatype indicator for PHBs */
336 if (strstr (response, "Gauge: ")) {
337 show = strstr (response, "Gauge: ") + 7;
338 }
339 else if (strstr (response, "Gauge32: ")) {
340 show = strstr (response, "Gauge32: ") + 9;
341 }
342 else if (strstr (response, "Counter32: ")) {
343 show = strstr (response, "Counter32: ") + 11;
344 is_counter=1;
345 if(!calculate_rate)
346 strcpy(type, "c");
347 }
348 else if (strstr (response, "Counter64: ")) {
349 show = strstr (response, "Counter64: ") + 11;
350 is_counter=1;
351 if(!calculate_rate)
352 strcpy(type, "c");
353 }
354 else if (strstr (response, "INTEGER: ")) {
355 show = strstr (response, "INTEGER: ") + 9;
356 }
357 else if (strstr (response, "STRING: ")) {
358 show = strstr (response, "STRING: ") + 8;
359 conv = "%.10g";
361 /* Get the rest of the string on multi-line strings */
362 ptr = show;
363 COUNT_SEQ(ptr, bk_count, dq_count)
364 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
365 ptr++;
366 GOBBLE_TOS(ptr, "\n\"\\")
367 COUNT_SEQ(ptr, bk_count, dq_count)
368 }
370 if (dq_count) { /* unfinished line */
371 /* copy show verbatim first */
372 if (!mult_resp) mult_resp = strdup("");
373 asprintf (&mult_resp, "%s%s:\n%s\n", mult_resp, oids[i], show);
374 /* then strip out unmatched double-quote from single-line output */
375 if (show[0] == '"') show++;
377 /* Keep reading until we match end of double-quoted string */
378 for (line++; line < chld_out.lines; line++) {
379 ptr = chld_out.line[line];
380 asprintf (&mult_resp, "%s%s\n", mult_resp, ptr);
382 COUNT_SEQ(ptr, bk_count, dq_count)
383 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
384 ptr++;
385 GOBBLE_TOS(ptr, "\n\"\\")
386 COUNT_SEQ(ptr, bk_count, dq_count)
387 }
388 /* Break for loop before next line increment when done */
389 if (!dq_count) break;
390 }
391 }
393 /* Allow numeric conversion if whole string is a number. Make concession for strings with " at beginning or end */
394 /* This duplicates the conversion a bit later, but is cleaner to separate out the checking against the conversion */
395 ptr = show;
396 if (*ptr == '"')
397 ptr++;
398 if (*ptr != '\0' ) {
399 strtod( ptr, &endptr );
400 if (*endptr == '"')
401 endptr++;
402 if (*endptr == '\0')
403 is_numeric=1;
404 }
406 }
407 else if (strstr (response, "Timeticks: ")) {
408 show = strstr (response, "Timeticks: ");
409 }
410 else
411 show = response;
413 iresult = STATE_DEPENDENT;
415 /* Process this block for numeric comparisons */
416 /* Make some special values,like Timeticks numeric only if a threshold is defined */
417 if (thlds[i]->warning || thlds[i]->critical || calculate_rate) {
418 ptr = strpbrk (show, "0123456789");
419 if (ptr == NULL)
420 die (STATE_UNKNOWN,_("No valid data returned"));
421 response_value[i] = strtod (ptr, NULL);
423 if(calculate_rate) {
424 if (previous_state!=NULL) {
425 duration = current_time-previous_state->time;
426 if(duration<=0)
427 die(STATE_UNKNOWN,_("Time duration between plugin calls is invalid"));
428 temp_double = response_value[i]-previous_value[i];
429 /* Simple overflow catcher (same as in rrdtool, rrd_update.c) */
430 if(is_counter) {
431 if(temp_double<(double)0.0)
432 temp_double+=(double)4294967296.0; /* 2^32 */
433 if(temp_double<(double)0.0)
434 temp_double+=(double)18446744069414584320.0; /* 2^64-2^32 */;
435 }
436 /* Convert to per second, then use multiplier */
437 temp_double = temp_double/duration*rate_multiplier;
438 iresult = get_status(temp_double, thlds[i]);
439 asprintf (&show, conv, temp_double);
440 }
441 } else {
442 iresult = get_status(response_value[i], thlds[i]);
443 asprintf (&show, conv, response_value[i]);
444 }
445 }
447 /* Process this block for string matching */
448 else if (eval_method[i] & CRIT_STRING) {
449 if (strcmp (show, string_value))
450 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK;
451 else
452 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
453 }
455 /* Process this block for regex matching */
456 else if (eval_method[i] & CRIT_REGEX) {
457 excode = regexec (&preg, response, 10, pmatch, eflags);
458 if (excode == 0) {
459 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
460 }
461 else if (excode != REG_NOMATCH) {
462 regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER);
463 printf (_("Execute Error: %s\n"), errbuf);
464 exit (STATE_CRITICAL);
465 }
466 else {
467 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK;
468 }
469 }
471 /* Process this block for existence-nonexistence checks */
472 /* TV: Should this be outside of this else block? */
473 else {
474 if (eval_method[i] & CRIT_PRESENT)
475 iresult = STATE_CRITICAL;
476 else if (eval_method[i] & WARN_PRESENT)
477 iresult = STATE_WARNING;
478 else if (response && iresult == STATE_DEPENDENT)
479 iresult = STATE_OK;
480 }
482 /* Result is the worst outcome of all the OIDs tested */
483 result = max_state (result, iresult);
485 /* Prepend a label for this OID if there is one */
486 if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
487 asprintf (&outbuff, "%s%s%s %s%s%s", outbuff,
488 (i == 0) ? " " : output_delim,
489 labels[i], mark (iresult), show, mark (iresult));
490 else
491 asprintf (&outbuff, "%s%s%s%s%s", outbuff, (i == 0) ? " " : output_delim,
492 mark (iresult), show, mark (iresult));
494 /* Append a unit string for this OID if there is one */
495 if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL)
496 asprintf (&outbuff, "%s %s", outbuff, unitv[i]);
498 /* Write perfdata with whatever can be parsed by strtod, if possible */
499 ptr = NULL;
500 strtod(show, &ptr);
501 if (ptr > show) {
502 if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
503 temp_string=labels[i];
504 else
505 temp_string=oidname;
506 strncat(perfstr, temp_string, sizeof(perfstr)-strlen(perfstr)-1);
507 strncat(perfstr, "=", sizeof(perfstr)-strlen(perfstr)-1);
508 len = sizeof(perfstr)-strlen(perfstr)-1;
509 strncat(perfstr, show, len>ptr-show ? ptr-show : len);
511 if (type)
512 strncat(perfstr, type, sizeof(perfstr)-strlen(perfstr)-1);
513 strncat(perfstr, " ", sizeof(perfstr)-strlen(perfstr)-1);
514 }
515 }
516 total_oids=i;
518 /* Save state data, as all data collected now */
519 if(calculate_rate) {
520 string_length=1024;
521 state_string=malloc(string_length);
522 if(state_string==NULL)
523 die(STATE_UNKNOWN, _("Cannot malloc"));
525 current_length=0;
526 for(i=0; i<total_oids; i++) {
527 asprintf(&temp_string,"%.0f",response_value[i]);
528 if(temp_string==NULL)
529 die(STATE_UNKNOWN,_("Cannot asprintf()"));
530 response_length = strlen(temp_string);
531 if(current_length+response_length>string_length) {
532 string_length=current_length+1024;
533 state_string=realloc(state_string,string_length);
534 if(state_string==NULL)
535 die(STATE_UNKNOWN, _("Cannot realloc()"));
536 }
537 strcpy(&state_string[current_length],temp_string);
538 current_length=current_length+response_length;
539 state_string[current_length]=':';
540 current_length++;
541 free(temp_string);
542 }
543 state_string[--current_length]='\0';
544 if (verbose > 2)
545 printf("State string=%s\n",state_string);
547 /* This is not strictly the same as time now, but any subtle variations will cancel out */
548 np_state_write_string(current_time, state_string );
549 if(previous_state==NULL) {
550 /* Or should this be highest state? */
551 die( STATE_OK, _("No previous data to calculate rate - assume okay" ) );
552 }
553 }
555 printf ("%s %s -%s %s\n", label, state_text (result), outbuff, perfstr);
556 if (mult_resp) printf ("%s", mult_resp);
558 return result;
559 }
563 /* process command-line arguments */
564 int
565 process_arguments (int argc, char **argv)
566 {
567 char *ptr;
568 int c = 1;
569 int j = 0, jj = 0, ii = 0;
571 int option = 0;
572 static struct option longopts[] = {
573 STD_LONG_OPTS,
574 {"community", required_argument, 0, 'C'},
575 {"oid", required_argument, 0, 'o'},
576 {"object", required_argument, 0, 'o'},
577 {"delimiter", required_argument, 0, 'd'},
578 {"output-delimiter", required_argument, 0, 'D'},
579 {"string", required_argument, 0, 's'},
580 {"timeout", required_argument, 0, 't'},
581 {"regex", required_argument, 0, 'r'},
582 {"ereg", required_argument, 0, 'r'},
583 {"eregi", required_argument, 0, 'R'},
584 {"label", required_argument, 0, 'l'},
585 {"units", required_argument, 0, 'u'},
586 {"port", required_argument, 0, 'p'},
587 {"retries", required_argument, 0, 'e'},
588 {"miblist", required_argument, 0, 'm'},
589 {"protocol", required_argument, 0, 'P'},
590 {"seclevel", required_argument, 0, 'L'},
591 {"secname", required_argument, 0, 'U'},
592 {"authproto", required_argument, 0, 'a'},
593 {"privproto", required_argument, 0, 'x'},
594 {"authpasswd", required_argument, 0, 'A'},
595 {"privpasswd", required_argument, 0, 'X'},
596 {"next", no_argument, 0, 'n'},
597 {"rate", no_argument, 0, L_CALCULATE_RATE},
598 {"rate-multiplier", required_argument, 0, L_RATE_MULTIPLIER},
599 {"invert-search", no_argument, 0, L_INVERT_SEARCH},
600 {0, 0, 0, 0}
601 };
603 if (argc < 2)
604 return ERROR;
606 /* reverse compatibility for very old non-POSIX usage forms */
607 for (c = 1; c < argc; c++) {
608 if (strcmp ("-to", argv[c]) == 0)
609 strcpy (argv[c], "-t");
610 if (strcmp ("-wv", argv[c]) == 0)
611 strcpy (argv[c], "-w");
612 if (strcmp ("-cv", argv[c]) == 0)
613 strcpy (argv[c], "-c");
614 }
616 while (1) {
617 c = getopt_long (argc, argv, "nhvVt:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:L:U:a:x:A:X:",
618 longopts, &option);
620 if (c == -1 || c == EOF)
621 break;
623 switch (c) {
624 case '?': /* usage */
625 usage5 ();
626 case 'h': /* help */
627 print_help ();
628 exit (STATE_OK);
629 case 'V': /* version */
630 print_revision (progname, NP_VERSION);
631 exit (STATE_OK);
632 case 'v': /* verbose */
633 verbose++;
634 break;
636 /* Connection info */
637 case 'C': /* group or community */
638 community = optarg;
639 break;
640 case 'H': /* Host or server */
641 server_address = optarg;
642 break;
643 case 'p': /* TCP port number */
644 port = optarg;
645 break;
646 case 'm': /* List of MIBS */
647 miblist = optarg;
648 break;
649 case 'n': /* usesnmpgetnext */
650 usesnmpgetnext = TRUE;
651 break;
652 case 'P': /* SNMP protocol version */
653 proto = optarg;
654 break;
655 case 'L': /* security level */
656 seclevel = optarg;
657 break;
658 case 'U': /* security username */
659 secname = optarg;
660 break;
661 case 'a': /* auth protocol */
662 authproto = optarg;
663 break;
664 case 'x': /* priv protocol */
665 privproto = optarg;
666 break;
667 case 'A': /* auth passwd */
668 authpasswd = optarg;
669 break;
670 case 'X': /* priv passwd */
671 privpasswd = optarg;
672 break;
673 case 't': /* timeout period */
674 if (!is_integer (optarg))
675 usage2 (_("Timeout interval must be a positive integer"), optarg);
676 else
677 timeout_interval = atoi (optarg);
678 break;
680 /* Test parameters */
681 case 'c': /* critical threshold */
682 critical_thresholds = optarg;
683 break;
684 case 'w': /* warning threshold */
685 warning_thresholds = optarg;
686 break;
687 case 'e': /* PRELIMINARY - may change */
688 case 'E': /* PRELIMINARY - may change */
689 if (!is_integer (optarg))
690 usage2 (_("Retries interval must be a positive integer"), optarg);
691 else
692 retries = atoi(optarg);
693 break;
694 case 'o': /* object identifier */
695 if ( strspn( optarg, "0123456789.," ) != strlen( optarg ) ) {
696 /*
697 * we have something other than digits, periods and comas,
698 * so we have a mib variable, rather than just an SNMP OID,
699 * so we have to actually read the mib files
700 */
701 needmibs = TRUE;
702 }
703 if (!oids) oids = calloc(MAX_OIDS, sizeof (char *));
704 for (ptr = strtok(optarg, ", "); ptr != NULL && j < MAX_OIDS; ptr = strtok(NULL, ", "), j++) {
705 oids[j] = strdup(ptr);
706 }
707 numoids = j;
708 if (c == 'E' || c == 'e') {
709 jj++;
710 ii++;
711 }
712 if (c == 'E')
713 eval_method[j+1] |= WARN_PRESENT;
714 else if (c == 'e')
715 eval_method[j+1] |= CRIT_PRESENT;
716 break;
717 case 's': /* string or substring */
718 strncpy (string_value, optarg, sizeof (string_value) - 1);
719 string_value[sizeof (string_value) - 1] = 0;
720 eval_method[jj++] = CRIT_STRING;
721 ii++;
722 break;
723 case 'R': /* regex */
724 cflags = REG_ICASE;
725 case 'r': /* regex */
726 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
727 strncpy (regex_expect, optarg, sizeof (regex_expect) - 1);
728 regex_expect[sizeof (regex_expect) - 1] = 0;
729 errcode = regcomp (&preg, regex_expect, cflags);
730 if (errcode != 0) {
731 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
732 printf (_("Could Not Compile Regular Expression"));
733 return ERROR;
734 }
735 eval_method[jj++] = CRIT_REGEX;
736 ii++;
737 break;
739 /* Format */
740 case 'd': /* delimiter */
741 delimiter = strscpy (delimiter, optarg);
742 break;
743 case 'D': /* output-delimiter */
744 output_delim = strscpy (output_delim, optarg);
745 break;
746 case 'l': /* label */
747 nlabels++;
748 if (nlabels >= labels_size) {
749 labels_size += 8;
750 labels = realloc (labels, labels_size);
751 if (labels == NULL)
752 die (STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels);
753 }
754 labels[nlabels - 1] = optarg;
755 ptr = thisarg (optarg);
756 labels[nlabels - 1] = ptr;
757 if (strstr (ptr, "'") == ptr)
758 labels[nlabels - 1] = ptr + 1;
759 while (ptr && (ptr = nextarg (ptr))) {
760 if (nlabels >= labels_size) {
761 labels_size += 8;
762 labels = realloc (labels, labels_size);
763 if (labels == NULL)
764 die (STATE_UNKNOWN, _("Could not reallocate labels\n"));
765 }
766 labels++;
767 ptr = thisarg (ptr);
768 if (strstr (ptr, "'") == ptr)
769 labels[nlabels - 1] = ptr + 1;
770 else
771 labels[nlabels - 1] = ptr;
772 }
773 break;
774 case 'u': /* units */
775 units = optarg;
776 nunits++;
777 if (nunits >= unitv_size) {
778 unitv_size += 8;
779 unitv = realloc (unitv, unitv_size);
780 if (unitv == NULL)
781 die (STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits);
782 }
783 unitv[nunits - 1] = optarg;
784 ptr = thisarg (optarg);
785 unitv[nunits - 1] = ptr;
786 if (strstr (ptr, "'") == ptr)
787 unitv[nunits - 1] = ptr + 1;
788 while (ptr && (ptr = nextarg (ptr))) {
789 if (nunits >= unitv_size) {
790 unitv_size += 8;
791 unitv = realloc (unitv, unitv_size);
792 if (units == NULL)
793 die (STATE_UNKNOWN, _("Could not realloc() units\n"));
794 }
795 nunits++;
796 ptr = thisarg (ptr);
797 if (strstr (ptr, "'") == ptr)
798 unitv[nunits - 1] = ptr + 1;
799 else
800 unitv[nunits - 1] = ptr;
801 }
802 break;
803 case L_CALCULATE_RATE:
804 if(calculate_rate==0)
805 np_enable_state(NULL, 1);
806 calculate_rate = 1;
807 break;
808 case L_RATE_MULTIPLIER:
809 if(!is_integer(optarg)||((rate_multiplier=atoi(optarg))<=0))
810 usage2(_("Rate multiplier must be a positive integer"),optarg);
811 break;
812 case L_INVERT_SEARCH:
813 invert_search=1;
814 break;
815 }
816 }
818 if (server_address == NULL)
819 server_address = argv[optind];
821 if (community == NULL)
822 community = strdup (DEFAULT_COMMUNITY);
824 return validate_arguments ();
825 }
828 /******************************************************************************
830 @@-
831 <sect3>
832 <title>validate_arguments</title>
834 <para>&PROTO_validate_arguments;</para>
836 <para>Checks to see if the default miblist needs to be loaded. Also verifies
837 the authentication and authorization combinations based on protocol version
838 selected.</para>
840 <para></para>
842 </sect3>
843 -@@
844 ******************************************************************************/
848 int
849 validate_arguments ()
850 {
851 /* check whether to load locally installed MIBS (CPU/disk intensive) */
852 if (miblist == NULL) {
853 if ( needmibs == TRUE ) {
854 miblist = strdup (DEFAULT_MIBLIST);
855 }else{
856 miblist = ""; /* don't read any mib files for numeric oids */
857 }
858 }
860 /* Check server_address is given */
861 if (server_address == NULL)
862 die(STATE_UNKNOWN, _("No host specified\n"));
864 /* Check oid is given */
865 if (numoids == 0)
866 die(STATE_UNKNOWN, _("No OIDs specified\n"));
868 if (proto == NULL)
869 asprintf(&proto, DEFAULT_PROTOCOL);
871 if ((strcmp(proto,"1") == 0) || (strcmp(proto, "2c")==0)) { /* snmpv1 or snmpv2c */
872 numauthpriv = 2;
873 authpriv = calloc (numauthpriv, sizeof (char *));
874 authpriv[0] = strdup ("-c");
875 authpriv[1] = strdup (community);
876 }
877 else if ( strcmp (proto, "3") == 0 ) { /* snmpv3 args */
878 if (seclevel == NULL)
879 asprintf(&seclevel, "noAuthNoPriv");
881 if (strcmp(seclevel, "noAuthNoPriv") == 0) {
882 numauthpriv = 2;
883 authpriv = calloc (numauthpriv, sizeof (char *));
884 authpriv[0] = strdup ("-l");
885 authpriv[1] = strdup ("noAuthNoPriv");
886 } else {
887 if (! ( (strcmp(seclevel, "authNoPriv")==0) || (strcmp(seclevel, "authPriv")==0) ) ) {
888 usage2 (_("Invalid seclevel"), seclevel);
889 }
891 if (authproto == NULL )
892 asprintf(&authproto, DEFAULT_AUTH_PROTOCOL);
894 if (secname == NULL)
895 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname");
897 if (authpasswd == NULL)
898 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd");
900 if ( strcmp(seclevel, "authNoPriv") == 0 ) {
901 numauthpriv = 8;
902 authpriv = calloc (numauthpriv, sizeof (char *));
903 authpriv[0] = strdup ("-l");
904 authpriv[1] = strdup ("authNoPriv");
905 authpriv[2] = strdup ("-a");
906 authpriv[3] = strdup (authproto);
907 authpriv[4] = strdup ("-u");
908 authpriv[5] = strdup (secname);
909 authpriv[6] = strdup ("-A");
910 authpriv[7] = strdup (authpasswd);
911 } else if ( strcmp(seclevel, "authPriv") == 0 ) {
912 if (privproto == NULL )
913 asprintf(&privproto, DEFAULT_PRIV_PROTOCOL);
915 if (privpasswd == NULL)
916 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd");
918 numauthpriv = 12;
919 authpriv = calloc (numauthpriv, sizeof (char *));
920 authpriv[0] = strdup ("-l");
921 authpriv[1] = strdup ("authPriv");
922 authpriv[2] = strdup ("-a");
923 authpriv[3] = strdup (authproto);
924 authpriv[4] = strdup ("-u");
925 authpriv[5] = strdup (secname);
926 authpriv[6] = strdup ("-A");
927 authpriv[7] = strdup (authpasswd);
928 authpriv[8] = strdup ("-x");
929 authpriv[9] = strdup (privproto);
930 authpriv[10] = strdup ("-X");
931 authpriv[11] = strdup (privpasswd);
932 }
933 }
935 }
936 else {
937 usage2 (_("Invalid SNMP version"), proto);
938 }
940 return OK;
941 }
945 /* trim leading whitespace
946 if there is a leading quote, make sure it balances */
948 char *
949 thisarg (char *str)
950 {
951 str += strspn (str, " \t\r\n"); /* trim any leading whitespace */
952 if (strstr (str, "'") == str) { /* handle SIMPLE quoted strings */
953 if (strlen (str) == 1 || !strstr (str + 1, "'"))
954 die (STATE_UNKNOWN, _("Unbalanced quotes\n"));
955 }
956 return str;
957 }
961 /* if there's a leading quote, advance to the trailing quote
962 set the trailing quote to '\x0'
963 if the string continues, advance beyond the comma */
965 char *
966 nextarg (char *str)
967 {
968 if (strstr (str, "'") == str) {
969 str[0] = 0;
970 if (strlen (str) > 1) {
971 str = strstr (str + 1, "'");
972 return (++str);
973 }
974 else {
975 return NULL;
976 }
977 }
978 if (strstr (str, ",") == str) {
979 str[0] = 0;
980 if (strlen (str) > 1) {
981 return (++str);
982 }
983 else {
984 return NULL;
985 }
986 }
987 if ((str = strstr (str, ",")) && strlen (str) > 1) {
988 str[0] = 0;
989 return (++str);
990 }
991 return NULL;
992 }
996 void
997 print_help (void)
998 {
999 print_revision (progname, NP_VERSION);
1001 printf (COPYRIGHT, copyright, email);
1003 printf ("%s\n", _("Check status of remote machines and obtain system information via SNMP"));
1005 printf ("\n\n");
1007 print_usage ();
1009 printf (UT_HELP_VRSN);
1010 printf (UT_EXTRA_OPTS);
1012 printf (UT_HOST_PORT, 'p', DEFAULT_PORT);
1014 /* SNMP and Authentication Protocol */
1015 printf (" %s\n", "-n, --next");
1016 printf (" %s\n", _("Use SNMP GETNEXT instead of SNMP GET"));
1017 printf (" %s\n", "-P, --protocol=[1|2c|3]");
1018 printf (" %s\n", _("SNMP protocol version"));
1019 printf (" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]");
1020 printf (" %s\n", _("SNMPv3 securityLevel"));
1021 printf (" %s\n", "-a, --authproto=[MD5|SHA]");
1022 printf (" %s\n", _("SNMPv3 auth proto"));
1023 printf (" %s\n", "-x, --privproto=[DES|AES]");
1024 printf (" %s\n", _("SNMPv3 priv proto (default DES)"));
1026 /* Authentication Tokens*/
1027 printf (" %s\n", "-C, --community=STRING");
1028 printf (" %s ", _("Optional community string for SNMP communication"));
1029 printf ("(%s \"%s\")\n", _("default is") ,DEFAULT_COMMUNITY);
1030 printf (" %s\n", "-U, --secname=USERNAME");
1031 printf (" %s\n", _("SNMPv3 username"));
1032 printf (" %s\n", "-A, --authpassword=PASSWORD");
1033 printf (" %s\n", _("SNMPv3 authentication password"));
1034 printf (" %s\n", "-X, --privpasswd=PASSWORD");
1035 printf (" %s\n", _("SNMPv3 privacy password"));
1037 /* OID Stuff */
1038 printf (" %s\n", "-o, --oid=OID(s)");
1039 printf (" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query"));
1040 printf (" %s\n", "-m, --miblist=STRING");
1041 printf (" %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'"));
1042 printf (" %s\n", _("for symbolic OIDs.)"));
1043 printf (" %s\n", "-d, --delimiter=STRING");
1044 printf (" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER);
1045 printf (" %s\n", _("Any data on the right hand side of the delimiter is considered"));
1046 printf (" %s\n", _("to be the data that should be used in the evaluation."));
1048 /* Tests Against Integers */
1049 printf (" %s\n", "-w, --warning=THRESHOLD(s)");
1050 printf (" %s\n", _("Warning threshold range(s)"));
1051 printf (" %s\n", "-c, --critical=THRESHOLD(s)");
1052 printf (" %s\n", _("Critical threshold range(s)"));
1053 printf (" %s\n", "--rate");
1054 printf (" %s\n", _("Enable rate calculation. See 'Rate Calculation' below"));
1055 printf (" %s\n", "--rate-multiplier");
1056 printf (" %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute"));
1058 /* Tests Against Strings */
1059 printf (" %s\n", "-s, --string=STRING");
1060 printf (" %s\n", _("Return OK state (for that OID) if STRING is an exact match"));
1061 printf (" %s\n", "-r, --ereg=REGEX");
1062 printf (" %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches"));
1063 printf (" %s\n", "-R, --eregi=REGEX");
1064 printf (" %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches"));
1065 printf (" %s\n", "--invert-search");
1066 printf (" %s\n", _("Invert search result (CRITICAL if found)"));
1068 /* Output Formatting */
1069 printf (" %s\n", "-l, --label=STRING");
1070 printf (" %s\n", _("Prefix label for output from plugin"));
1071 printf (" %s\n", "-u, --units=STRING");
1072 printf (" %s\n", _("Units label(s) for output data (e.g., 'sec.')."));
1073 printf (" %s\n", "-D, --output-delimiter=STRING");
1074 printf (" %s\n", _("Separates output on multiple OID requests"));
1076 printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1077 printf (" %s\n", "-e, --retries=INTEGER");
1078 printf (" %s\n", _("Number of retries to be used in the requests"));
1080 printf (UT_VERBOSE);
1082 printf ("\n");
1083 printf ("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package."));
1084 printf ("%s\n", _("if you don't have the package installed, you will need to download it from"));
1085 printf ("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1087 printf ("\n");
1088 printf ("%s\n", _("Notes:"));
1089 printf (" %s\n", _("- Multiple OIDs may be indicated by a comma or space-delimited list (lists with"));
1090 printf (" %s %i %s\n", _("internal spaces must be quoted). Maximum:"), MAX_OIDS, _("OIDs."));
1092 printf(" -%s", UT_THRESHOLDS_NOTES);
1094 printf (" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'"));
1095 printf (" %s\n", _("- Note that only one string and one regex may be checked at present"));
1096 printf (" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value"));
1097 printf (" %s\n", _("returned from the SNMP query is an unsigned integer."));
1099 printf("\n");
1100 printf("%s\n", _("Rate Calculation:"));
1101 printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when"));
1102 printf(" %s\n", _("calculating the counter difference since the last check. check_snmp"));
1103 printf(" %s\n", _("saves the last state information in a file so that the rate per second"));
1104 printf(" %s\n", _("can be calculated. Use the --rate option to save state information."));
1105 printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK."));
1106 printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so"));
1107 printf(" %s\n", _("changing the arguments will create a new state file."));
1109 printf (UT_SUPPORT);
1110 }
1114 void
1115 print_usage (void)
1116 {
1117 printf ("%s\n", _("Usage:"));
1118 printf ("%s -H <ip_address> -o <OID> [-w warn_range] [-c crit_range]\n",progname);
1119 printf ("[-C community] [-s string] [-r regex] [-R regexi] [-t timeout] [-e retries]\n");
1120 printf ("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n");
1121 printf ("[-m miblist] [-P snmp version] [-L seclevel] [-U secname] [-a authproto]\n");
1122 printf ("[-A authpasswd] [-x privproto] [-X privpasswd]\n");
1123 }