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 *th_warn=NULL;
164 char *th_crit=NULL;
165 char type[8] = "";
166 output chld_out, chld_err;
167 char *previous_string=NULL;
168 char *ap=NULL;
169 char *state_string=NULL;
170 size_t response_length, current_length, string_length;
171 char *temp_string=NULL;
172 int is_numeric=0;
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 is_numeric++;
339 }
340 else if (strstr (response, "Gauge32: ")) {
341 show = strstr (response, "Gauge32: ") + 9;
342 is_numeric++;
343 }
344 else if (strstr (response, "Counter32: ")) {
345 show = strstr (response, "Counter32: ") + 11;
346 is_numeric++;
347 is_counter=1;
348 if(!calculate_rate)
349 strcpy(type, "c");
350 }
351 else if (strstr (response, "Counter64: ")) {
352 show = strstr (response, "Counter64: ") + 11;
353 is_numeric++;
354 is_counter=1;
355 if(!calculate_rate)
356 strcpy(type, "c");
357 }
358 else if (strstr (response, "INTEGER: ")) {
359 show = strstr (response, "INTEGER: ") + 9;
360 is_numeric++;
361 }
362 else if (strstr (response, "STRING: ")) {
363 show = strstr (response, "STRING: ") + 8;
364 conv = "%.10g";
366 /* Get the rest of the string on multi-line strings */
367 ptr = show;
368 COUNT_SEQ(ptr, bk_count, dq_count)
369 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
370 ptr++;
371 GOBBLE_TOS(ptr, "\n\"\\")
372 COUNT_SEQ(ptr, bk_count, dq_count)
373 }
375 if (dq_count) { /* unfinished line */
376 /* copy show verbatim first */
377 if (!mult_resp) mult_resp = strdup("");
378 asprintf (&mult_resp, "%s%s:\n%s\n", mult_resp, oids[i], show);
379 /* then strip out unmatched double-quote from single-line output */
380 if (show[0] == '"') show++;
382 /* Keep reading until we match end of double-quoted string */
383 for (line++; line < chld_out.lines; line++) {
384 ptr = chld_out.line[line];
385 asprintf (&mult_resp, "%s%s\n", mult_resp, ptr);
387 COUNT_SEQ(ptr, bk_count, dq_count)
388 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
389 ptr++;
390 GOBBLE_TOS(ptr, "\n\"\\")
391 COUNT_SEQ(ptr, bk_count, dq_count)
392 }
393 /* Break for loop before next line increment when done */
394 if (!dq_count) break;
395 }
396 }
398 }
399 else if (strstr (response, "Timeticks: "))
400 show = strstr (response, "Timeticks: ");
401 else
402 show = response;
404 iresult = STATE_DEPENDENT;
406 /* Process this block for numeric comparisons */
407 if (is_numeric) {
408 ptr = strpbrk (show, "0123456789");
409 if (ptr == NULL)
410 die (STATE_UNKNOWN,_("No valid data returned"));
411 response_value[i] = strtod (ptr, NULL);
413 if(calculate_rate) {
414 if (previous_state!=NULL) {
415 duration = current_time-previous_state->time;
416 if(duration<=0)
417 die(STATE_UNKNOWN,_("Time duration between plugin calls is invalid"));
418 temp_double = response_value[i]-previous_value[i];
419 /* Simple overflow catcher (same as in rrdtool, rrd_update.c) */
420 if(is_counter) {
421 if(temp_double<(double)0.0)
422 temp_double+=(double)4294967296.0; /* 2^32 */
423 if(temp_double<(double)0.0)
424 temp_double+=(double)18446744069414584320.0; /* 2^64-2^32 */;
425 }
426 /* Convert to per second, then use multiplier */
427 temp_double = temp_double/(duration*rate_multiplier);
428 iresult = get_status(temp_double, thlds[i]);
429 asprintf (&show, conv, temp_double);
430 }
431 } else {
432 iresult = get_status(response_value[i], thlds[i]);
433 asprintf (&show, conv, response_value[i]);
434 }
435 }
437 /* Process this block for string matching */
438 else if (eval_method[i] & CRIT_STRING) {
439 if (strcmp (show, string_value))
440 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK;
441 else
442 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
443 }
445 /* Process this block for regex matching */
446 else if (eval_method[i] & CRIT_REGEX) {
447 excode = regexec (&preg, response, 10, pmatch, eflags);
448 if (excode == 0) {
449 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
450 }
451 else if (excode != REG_NOMATCH) {
452 regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER);
453 printf (_("Execute Error: %s\n"), errbuf);
454 exit (STATE_CRITICAL);
455 }
456 else {
457 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK;
458 }
459 }
461 /* Process this block for existence-nonexistence checks */
462 /* TV: Should this be outside of this else block? */
463 else {
464 if (eval_method[i] & CRIT_PRESENT)
465 iresult = STATE_CRITICAL;
466 else if (eval_method[i] & WARN_PRESENT)
467 iresult = STATE_WARNING;
468 else if (response && iresult == STATE_DEPENDENT)
469 iresult = STATE_OK;
470 }
472 /* Result is the worst outcome of all the OIDs tested */
473 result = max_state (result, iresult);
475 /* Prepend a label for this OID if there is one */
476 if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
477 asprintf (&outbuff, "%s%s%s %s%s%s", outbuff,
478 (i == 0) ? " " : output_delim,
479 labels[i], mark (iresult), show, mark (iresult));
480 else
481 asprintf (&outbuff, "%s%s%s%s%s", outbuff, (i == 0) ? " " : output_delim,
482 mark (iresult), show, mark (iresult));
484 /* Append a unit string for this OID if there is one */
485 if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL)
486 asprintf (&outbuff, "%s %s", outbuff, unitv[i]);
488 /* Write perfdata with whatever can be parsed by strtod, if possible */
489 ptr = NULL;
490 strtod(show, &ptr);
491 if (ptr > show) {
492 if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
493 temp_string=labels[i];
494 else
495 temp_string=oidname;
496 strncat(perfstr, temp_string, sizeof(perfstr)-strlen(perfstr)-1);
497 strncat(perfstr, "=", sizeof(perfstr)-strlen(perfstr)-1);
498 len = sizeof(perfstr)-strlen(perfstr)-1;
499 strncat(perfstr, show, len>ptr-show ? ptr-show : len);
501 if (type)
502 strncat(perfstr, type, sizeof(perfstr)-strlen(perfstr)-1);
503 strncat(perfstr, " ", sizeof(perfstr)-strlen(perfstr)-1);
504 }
505 }
506 total_oids=i;
508 /* Save state data, as all data collected now */
509 if(calculate_rate) {
510 string_length=1024;
511 state_string=malloc(string_length);
512 if(state_string==NULL)
513 die(STATE_UNKNOWN, _("Cannot malloc"));
515 current_length=0;
516 for(i=0; i<total_oids; i++) {
517 asprintf(&temp_string,"%.0f",response_value[i]);
518 if(temp_string==NULL)
519 die(STATE_UNKNOWN,_("Cannot asprintf()"));
520 response_length = strlen(temp_string);
521 if(current_length+response_length>string_length) {
522 string_length=current_length+1024;
523 state_string=realloc(state_string,string_length);
524 if(state_string==NULL)
525 die(STATE_UNKNOWN, _("Cannot realloc()"));
526 }
527 strcpy(&state_string[current_length],temp_string);
528 current_length=current_length+response_length;
529 state_string[current_length]=':';
530 current_length++;
531 free(temp_string);
532 }
533 state_string[--current_length]='\0';
534 if (verbose > 2)
535 printf("State string=%s\n",state_string);
537 /* This is not strictly the same as time now, but any subtle variations will cancel out */
538 np_state_write_string(current_time, state_string );
539 if(previous_state==NULL) {
540 /* Or should this be highest state? */
541 die( STATE_OK, _("No previous data to calculate rate - assume okay" ) );
542 }
543 }
545 printf ("%s %s -%s %s\n", label, state_text (result), outbuff, perfstr);
546 if (mult_resp) printf ("%s", mult_resp);
548 return result;
549 }
553 /* process command-line arguments */
554 int
555 process_arguments (int argc, char **argv)
556 {
557 char *ptr;
558 int c = 1;
559 int j = 0, jj = 0, ii = 0;
561 int option = 0;
562 static struct option longopts[] = {
563 STD_LONG_OPTS,
564 {"community", required_argument, 0, 'C'},
565 {"oid", required_argument, 0, 'o'},
566 {"object", required_argument, 0, 'o'},
567 {"delimiter", required_argument, 0, 'd'},
568 {"output-delimiter", required_argument, 0, 'D'},
569 {"string", required_argument, 0, 's'},
570 {"timeout", required_argument, 0, 't'},
571 {"regex", required_argument, 0, 'r'},
572 {"ereg", required_argument, 0, 'r'},
573 {"eregi", required_argument, 0, 'R'},
574 {"label", required_argument, 0, 'l'},
575 {"units", required_argument, 0, 'u'},
576 {"port", required_argument, 0, 'p'},
577 {"retries", required_argument, 0, 'e'},
578 {"miblist", required_argument, 0, 'm'},
579 {"protocol", required_argument, 0, 'P'},
580 {"seclevel", required_argument, 0, 'L'},
581 {"secname", required_argument, 0, 'U'},
582 {"authproto", required_argument, 0, 'a'},
583 {"privproto", required_argument, 0, 'x'},
584 {"authpasswd", required_argument, 0, 'A'},
585 {"privpasswd", required_argument, 0, 'X'},
586 {"next", no_argument, 0, 'n'},
587 {"rate", no_argument, 0, L_CALCULATE_RATE},
588 {"rate-multiplier", required_argument, 0, L_RATE_MULTIPLIER},
589 {"invert-search", no_argument, 0, L_INVERT_SEARCH},
590 {0, 0, 0, 0}
591 };
593 if (argc < 2)
594 return ERROR;
596 /* reverse compatibility for very old non-POSIX usage forms */
597 for (c = 1; c < argc; c++) {
598 if (strcmp ("-to", argv[c]) == 0)
599 strcpy (argv[c], "-t");
600 if (strcmp ("-wv", argv[c]) == 0)
601 strcpy (argv[c], "-w");
602 if (strcmp ("-cv", argv[c]) == 0)
603 strcpy (argv[c], "-c");
604 }
606 while (1) {
607 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:",
608 longopts, &option);
610 if (c == -1 || c == EOF)
611 break;
613 switch (c) {
614 case '?': /* usage */
615 usage5 ();
616 case 'h': /* help */
617 print_help ();
618 exit (STATE_OK);
619 case 'V': /* version */
620 print_revision (progname, NP_VERSION);
621 exit (STATE_OK);
622 case 'v': /* verbose */
623 verbose++;
624 break;
626 /* Connection info */
627 case 'C': /* group or community */
628 community = optarg;
629 break;
630 case 'H': /* Host or server */
631 server_address = optarg;
632 break;
633 case 'p': /* TCP port number */
634 port = optarg;
635 break;
636 case 'm': /* List of MIBS */
637 miblist = optarg;
638 break;
639 case 'n': /* usesnmpgetnext */
640 usesnmpgetnext = TRUE;
641 break;
642 case 'P': /* SNMP protocol version */
643 proto = optarg;
644 break;
645 case 'L': /* security level */
646 seclevel = optarg;
647 break;
648 case 'U': /* security username */
649 secname = optarg;
650 break;
651 case 'a': /* auth protocol */
652 authproto = optarg;
653 break;
654 case 'x': /* priv protocol */
655 privproto = optarg;
656 break;
657 case 'A': /* auth passwd */
658 authpasswd = optarg;
659 break;
660 case 'X': /* priv passwd */
661 privpasswd = optarg;
662 break;
663 case 't': /* timeout period */
664 if (!is_integer (optarg))
665 usage2 (_("Timeout interval must be a positive integer"), optarg);
666 else
667 timeout_interval = atoi (optarg);
668 break;
670 /* Test parameters */
671 case 'c': /* critical threshold */
672 critical_thresholds = optarg;
673 break;
674 case 'w': /* warning threshold */
675 warning_thresholds = optarg;
676 break;
677 case 'e': /* PRELIMINARY - may change */
678 case 'E': /* PRELIMINARY - may change */
679 if (!is_integer (optarg))
680 usage2 (_("Retries interval must be a positive integer"), optarg);
681 else
682 retries = atoi(optarg);
683 break;
684 case 'o': /* object identifier */
685 if ( strspn( optarg, "0123456789.," ) != strlen( optarg ) ) {
686 /*
687 * we have something other than digits, periods and comas,
688 * so we have a mib variable, rather than just an SNMP OID,
689 * so we have to actually read the mib files
690 */
691 needmibs = TRUE;
692 }
693 if (!oids) oids = calloc(MAX_OIDS, sizeof (char *));
694 for (ptr = strtok(optarg, ", "); ptr != NULL && j < MAX_OIDS; ptr = strtok(NULL, ", "), j++) {
695 oids[j] = strdup(ptr);
696 }
697 numoids = j;
698 if (c == 'E' || c == 'e') {
699 jj++;
700 ii++;
701 }
702 if (c == 'E')
703 eval_method[j+1] |= WARN_PRESENT;
704 else if (c == 'e')
705 eval_method[j+1] |= CRIT_PRESENT;
706 break;
707 case 's': /* string or substring */
708 strncpy (string_value, optarg, sizeof (string_value) - 1);
709 string_value[sizeof (string_value) - 1] = 0;
710 eval_method[jj++] = CRIT_STRING;
711 ii++;
712 break;
713 case 'R': /* regex */
714 cflags = REG_ICASE;
715 case 'r': /* regex */
716 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
717 strncpy (regex_expect, optarg, sizeof (regex_expect) - 1);
718 regex_expect[sizeof (regex_expect) - 1] = 0;
719 errcode = regcomp (&preg, regex_expect, cflags);
720 if (errcode != 0) {
721 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
722 printf (_("Could Not Compile Regular Expression"));
723 return ERROR;
724 }
725 eval_method[jj++] = CRIT_REGEX;
726 ii++;
727 break;
729 /* Format */
730 case 'd': /* delimiter */
731 delimiter = strscpy (delimiter, optarg);
732 break;
733 case 'D': /* output-delimiter */
734 output_delim = strscpy (output_delim, optarg);
735 break;
736 case 'l': /* label */
737 nlabels++;
738 if (nlabels >= labels_size) {
739 labels_size += 8;
740 labels = realloc (labels, labels_size);
741 if (labels == NULL)
742 die (STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels);
743 }
744 labels[nlabels - 1] = optarg;
745 ptr = thisarg (optarg);
746 labels[nlabels - 1] = ptr;
747 if (strstr (ptr, "'") == ptr)
748 labels[nlabels - 1] = ptr + 1;
749 while (ptr && (ptr = nextarg (ptr))) {
750 if (nlabels >= labels_size) {
751 labels_size += 8;
752 labels = realloc (labels, labels_size);
753 if (labels == NULL)
754 die (STATE_UNKNOWN, _("Could not reallocate labels\n"));
755 }
756 labels++;
757 ptr = thisarg (ptr);
758 if (strstr (ptr, "'") == ptr)
759 labels[nlabels - 1] = ptr + 1;
760 else
761 labels[nlabels - 1] = ptr;
762 }
763 break;
764 case 'u': /* units */
765 units = optarg;
766 nunits++;
767 if (nunits >= unitv_size) {
768 unitv_size += 8;
769 unitv = realloc (unitv, unitv_size);
770 if (unitv == NULL)
771 die (STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits);
772 }
773 unitv[nunits - 1] = optarg;
774 ptr = thisarg (optarg);
775 unitv[nunits - 1] = ptr;
776 if (strstr (ptr, "'") == ptr)
777 unitv[nunits - 1] = ptr + 1;
778 while (ptr && (ptr = nextarg (ptr))) {
779 if (nunits >= unitv_size) {
780 unitv_size += 8;
781 unitv = realloc (unitv, unitv_size);
782 if (units == NULL)
783 die (STATE_UNKNOWN, _("Could not realloc() units\n"));
784 }
785 nunits++;
786 ptr = thisarg (ptr);
787 if (strstr (ptr, "'") == ptr)
788 unitv[nunits - 1] = ptr + 1;
789 else
790 unitv[nunits - 1] = ptr;
791 }
792 break;
793 case L_CALCULATE_RATE:
794 if(calculate_rate==0)
795 np_enable_state(NULL, 1);
796 calculate_rate = 1;
797 break;
798 case L_RATE_MULTIPLIER:
799 if(!is_integer(optarg)||((rate_multiplier=atoi(optarg))<=0))
800 usage2(_("Rate multiplier must be a positive integer"),optarg);
801 break;
802 case L_INVERT_SEARCH:
803 invert_search=1;
804 break;
805 }
806 }
808 if (server_address == NULL)
809 server_address = argv[optind];
811 if (community == NULL)
812 community = strdup (DEFAULT_COMMUNITY);
814 return validate_arguments ();
815 }
818 /******************************************************************************
820 @@-
821 <sect3>
822 <title>validate_arguments</title>
824 <para>&PROTO_validate_arguments;</para>
826 <para>Checks to see if the default miblist needs to be loaded. Also verifies
827 the authentication and authorization combinations based on protocol version
828 selected.</para>
830 <para></para>
832 </sect3>
833 -@@
834 ******************************************************************************/
838 int
839 validate_arguments ()
840 {
841 /* check whether to load locally installed MIBS (CPU/disk intensive) */
842 if (miblist == NULL) {
843 if ( needmibs == TRUE ) {
844 miblist = strdup (DEFAULT_MIBLIST);
845 }else{
846 miblist = ""; /* don't read any mib files for numeric oids */
847 }
848 }
850 /* Check server_address is given */
851 if (server_address == NULL)
852 die(STATE_UNKNOWN, _("No host specified\n"));
854 /* Check oid is given */
855 if (numoids == 0)
856 die(STATE_UNKNOWN, _("No OIDs specified\n"));
858 if (proto == NULL)
859 asprintf(&proto, DEFAULT_PROTOCOL);
861 if ((strcmp(proto,"1") == 0) || (strcmp(proto, "2c")==0)) { /* snmpv1 or snmpv2c */
862 numauthpriv = 2;
863 authpriv = calloc (numauthpriv, sizeof (char *));
864 authpriv[0] = strdup ("-c");
865 authpriv[1] = strdup (community);
866 }
867 else if ( strcmp (proto, "3") == 0 ) { /* snmpv3 args */
868 if (seclevel == NULL)
869 asprintf(&seclevel, "noAuthNoPriv");
871 if (strcmp(seclevel, "noAuthNoPriv") == 0) {
872 numauthpriv = 2;
873 authpriv = calloc (numauthpriv, sizeof (char *));
874 authpriv[0] = strdup ("-l");
875 authpriv[1] = strdup ("noAuthNoPriv");
876 } else {
877 if (! ( (strcmp(seclevel, "authNoPriv")==0) || (strcmp(seclevel, "authPriv")==0) ) ) {
878 usage2 (_("Invalid seclevel"), seclevel);
879 }
881 if (authproto == NULL )
882 asprintf(&authproto, DEFAULT_AUTH_PROTOCOL);
884 if (secname == NULL)
885 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname");
887 if (authpasswd == NULL)
888 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd");
890 if ( strcmp(seclevel, "authNoPriv") == 0 ) {
891 numauthpriv = 8;
892 authpriv = calloc (numauthpriv, sizeof (char *));
893 authpriv[0] = strdup ("-l");
894 authpriv[1] = strdup ("authNoPriv");
895 authpriv[2] = strdup ("-a");
896 authpriv[3] = strdup (authproto);
897 authpriv[4] = strdup ("-u");
898 authpriv[5] = strdup (secname);
899 authpriv[6] = strdup ("-A");
900 authpriv[7] = strdup (authpasswd);
901 } else if ( strcmp(seclevel, "authPriv") == 0 ) {
902 if (privproto == NULL )
903 asprintf(&privproto, DEFAULT_PRIV_PROTOCOL);
905 if (privpasswd == NULL)
906 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd");
908 numauthpriv = 12;
909 authpriv = calloc (numauthpriv, sizeof (char *));
910 authpriv[0] = strdup ("-l");
911 authpriv[1] = strdup ("authPriv");
912 authpriv[2] = strdup ("-a");
913 authpriv[3] = strdup (authproto);
914 authpriv[4] = strdup ("-u");
915 authpriv[5] = strdup (secname);
916 authpriv[6] = strdup ("-A");
917 authpriv[7] = strdup (authpasswd);
918 authpriv[8] = strdup ("-x");
919 authpriv[9] = strdup (privproto);
920 authpriv[10] = strdup ("-X");
921 authpriv[11] = strdup (privpasswd);
922 }
923 }
925 }
926 else {
927 usage2 (_("Invalid SNMP version"), proto);
928 }
930 return OK;
931 }
935 /* trim leading whitespace
936 if there is a leading quote, make sure it balances */
938 char *
939 thisarg (char *str)
940 {
941 str += strspn (str, " \t\r\n"); /* trim any leading whitespace */
942 if (strstr (str, "'") == str) { /* handle SIMPLE quoted strings */
943 if (strlen (str) == 1 || !strstr (str + 1, "'"))
944 die (STATE_UNKNOWN, _("Unbalanced quotes\n"));
945 }
946 return str;
947 }
951 /* if there's a leading quote, advance to the trailing quote
952 set the trailing quote to '\x0'
953 if the string continues, advance beyond the comma */
955 char *
956 nextarg (char *str)
957 {
958 if (strstr (str, "'") == str) {
959 str[0] = 0;
960 if (strlen (str) > 1) {
961 str = strstr (str + 1, "'");
962 return (++str);
963 }
964 else {
965 return NULL;
966 }
967 }
968 if (strstr (str, ",") == str) {
969 str[0] = 0;
970 if (strlen (str) > 1) {
971 return (++str);
972 }
973 else {
974 return NULL;
975 }
976 }
977 if ((str = strstr (str, ",")) && strlen (str) > 1) {
978 str[0] = 0;
979 return (++str);
980 }
981 return NULL;
982 }
986 void
987 print_help (void)
988 {
989 print_revision (progname, NP_VERSION);
991 printf (COPYRIGHT, copyright, email);
993 printf ("%s\n", _("Check status of remote machines and obtain system information via SNMP"));
995 printf ("\n\n");
997 print_usage ();
999 printf (UT_HELP_VRSN);
1000 printf (UT_EXTRA_OPTS);
1002 printf (UT_HOST_PORT, 'p', DEFAULT_PORT);
1004 /* SNMP and Authentication Protocol */
1005 printf (" %s\n", "-n, --next");
1006 printf (" %s\n", _("Use SNMP GETNEXT instead of SNMP GET"));
1007 printf (" %s\n", "-P, --protocol=[1|2c|3]");
1008 printf (" %s\n", _("SNMP protocol version"));
1009 printf (" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]");
1010 printf (" %s\n", _("SNMPv3 securityLevel"));
1011 printf (" %s\n", "-a, --authproto=[MD5|SHA]");
1012 printf (" %s\n", _("SNMPv3 auth proto"));
1013 printf (" %s\n", "-x, --privproto=[DES|AES]");
1014 printf (" %s\n", _("SNMPv3 priv proto (default DES)"));
1016 /* Authentication Tokens*/
1017 printf (" %s\n", "-C, --community=STRING");
1018 printf (" %s ", _("Optional community string for SNMP communication"));
1019 printf ("(%s \"%s\")\n", _("default is") ,DEFAULT_COMMUNITY);
1020 printf (" %s\n", "-U, --secname=USERNAME");
1021 printf (" %s\n", _("SNMPv3 username"));
1022 printf (" %s\n", "-A, --authpassword=PASSWORD");
1023 printf (" %s\n", _("SNMPv3 authentication password"));
1024 printf (" %s\n", "-X, --privpasswd=PASSWORD");
1025 printf (" %s\n", _("SNMPv3 privacy password"));
1027 /* OID Stuff */
1028 printf (" %s\n", "-o, --oid=OID(s)");
1029 printf (" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query"));
1030 printf (" %s\n", "-m, --miblist=STRING");
1031 printf (" %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'"));
1032 printf (" %s\n", _("for symbolic OIDs.)"));
1033 printf (" %s\n", "-d, --delimiter=STRING");
1034 printf (" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER);
1035 printf (" %s\n", _("Any data on the right hand side of the delimiter is considered"));
1036 printf (" %s\n", _("to be the data that should be used in the evaluation."));
1038 /* Tests Against Integers */
1039 printf (" %s\n", "-w, --warning=THRESHOLD(s)");
1040 printf (" %s\n", _("Warning threshold range(s)"));
1041 printf (" %s\n", "-c, --critical=THRESHOLD(s)");
1042 printf (" %s\n", _("Critical threshold range(s)"));
1043 printf (" %s\n", "--rate");
1044 printf (" %s\n", _("Enable rate calculation. See 'Rate Calculation' below"));
1045 printf (" %s\n", "--rate-multiplier");
1046 printf (" %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute"));
1048 /* Tests Against Strings */
1049 printf (" %s\n", "-s, --string=STRING");
1050 printf (" %s\n", _("Return OK state (for that OID) if STRING is an exact match"));
1051 printf (" %s\n", "-r, --ereg=REGEX");
1052 printf (" %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches"));
1053 printf (" %s\n", "-R, --eregi=REGEX");
1054 printf (" %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches"));
1055 printf (" %s\n", "--invert-search");
1056 printf (" %s\n", _("Invert search result (CRITICAL if found)"));
1058 /* Output Formatting */
1059 printf (" %s\n", "-l, --label=STRING");
1060 printf (" %s\n", _("Prefix label for output from plugin"));
1061 printf (" %s\n", "-u, --units=STRING");
1062 printf (" %s\n", _("Units label(s) for output data (e.g., 'sec.')."));
1063 printf (" %s\n", "-D, --output-delimiter=STRING");
1064 printf (" %s\n", _("Separates output on multiple OID requests"));
1066 printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1067 printf (" %s\n", "-e, --retries=INTEGER");
1068 printf (" %s\n", _("Number of retries to be used in the requests"));
1070 printf (UT_VERBOSE);
1072 printf ("\n");
1073 printf ("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package."));
1074 printf ("%s\n", _("if you don't have the package installed, you will need to download it from"));
1075 printf ("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1077 printf ("\n");
1078 printf ("%s\n", _("Notes:"));
1079 printf (" %s\n", _("- Multiple OIDs may be indicated by a comma- or space-delimited list (lists with"));
1080 printf (" %s\n", _("internal spaces must be quoted) [max 8 OIDs]"));
1082 printf(" -%s", UT_THRESHOLDS_NOTES);
1084 printf (" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'"));
1085 printf (" %s\n", _("- Note that only one string and one regex may be checked at present"));
1086 printf (" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value"));
1087 printf (" %s\n", _("returned from the SNMP query is an unsigned integer."));
1089 printf("\n");
1090 printf("%s\n", _("Rate Calculation:"));
1091 printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when"));
1092 printf(" %s\n", _("calculating the counter difference since the last check. check_snmp"));
1093 printf(" %s\n", _("saves the last state information in a file so that the rate per second"));
1094 printf(" %s\n", _("can be calculated. Use the --rate option to save state information."));
1095 printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK."));
1096 printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so"));
1097 printf(" %s\n", _("changing the arguments will create a new state file."));
1099 printf (UT_SUPPORT);
1100 }
1104 void
1105 print_usage (void)
1106 {
1107 printf ("%s\n", _("Usage:"));
1108 printf ("%s -H <ip_address> -o <OID> [-w warn_range] [-c crit_range]\n",progname);
1109 printf ("[-C community] [-s string] [-r regex] [-R regexi] [-t timeout] [-e retries]\n");
1110 printf ("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n");
1111 printf ("[-m miblist] [-P snmp version] [-L seclevel] [-U secname] [-a authproto]\n");
1112 printf ("[-A authpasswd] [-x privproto] [-X privpasswd]\n");
1113 }