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 if(calculate_rate)
497 asprintf(&temp_string,"%s-rate",temp_string);
498 strncat(perfstr, temp_string, sizeof(perfstr)-strlen(perfstr)-1);
499 strncat(perfstr, "=", sizeof(perfstr)-strlen(perfstr)-1);
500 len = sizeof(perfstr)-strlen(perfstr)-1;
501 strncat(perfstr, show, len>ptr-show ? ptr-show : len);
503 if (type)
504 strncat(perfstr, type, sizeof(perfstr)-strlen(perfstr)-1);
505 strncat(perfstr, " ", sizeof(perfstr)-strlen(perfstr)-1);
506 }
507 }
508 total_oids=i;
510 /* Save state data, as all data collected now */
511 if(calculate_rate) {
512 string_length=1024;
513 state_string=malloc(string_length);
514 if(state_string==NULL)
515 die(STATE_UNKNOWN, _("Cannot malloc"));
517 current_length=0;
518 for(i=0; i<total_oids; i++) {
519 asprintf(&temp_string,"%.0f",response_value[i]);
520 if(temp_string==NULL)
521 die(STATE_UNKNOWN,_("Cannot asprintf()"));
522 response_length = strlen(temp_string);
523 if(current_length+response_length>string_length) {
524 string_length=current_length+1024;
525 state_string=realloc(state_string,string_length);
526 if(state_string==NULL)
527 die(STATE_UNKNOWN, _("Cannot realloc()"));
528 }
529 strcpy(&state_string[current_length],temp_string);
530 current_length=current_length+response_length;
531 state_string[current_length]=':';
532 current_length++;
533 free(temp_string);
534 }
535 state_string[--current_length]='\0';
536 if (verbose > 2)
537 printf("State string=%s\n",state_string);
539 /* This is not strictly the same as time now, but any subtle variations will cancel out */
540 np_state_write_string(current_time, state_string );
541 if(previous_state==NULL) {
542 /* Or should this be highest state? */
543 die( STATE_OK, _("No previous data to calculate rate - assume okay" ) );
544 }
545 }
547 printf ("%s %s -%s %s\n", label, state_text (result), outbuff, perfstr);
548 if (mult_resp) printf ("%s", mult_resp);
550 return result;
551 }
555 /* process command-line arguments */
556 int
557 process_arguments (int argc, char **argv)
558 {
559 char *ptr;
560 int c = 1;
561 int j = 0, jj = 0, ii = 0;
563 int option = 0;
564 static struct option longopts[] = {
565 STD_LONG_OPTS,
566 {"community", required_argument, 0, 'C'},
567 {"oid", required_argument, 0, 'o'},
568 {"object", required_argument, 0, 'o'},
569 {"delimiter", required_argument, 0, 'd'},
570 {"output-delimiter", required_argument, 0, 'D'},
571 {"string", required_argument, 0, 's'},
572 {"timeout", required_argument, 0, 't'},
573 {"regex", required_argument, 0, 'r'},
574 {"ereg", required_argument, 0, 'r'},
575 {"eregi", required_argument, 0, 'R'},
576 {"label", required_argument, 0, 'l'},
577 {"units", required_argument, 0, 'u'},
578 {"port", required_argument, 0, 'p'},
579 {"retries", required_argument, 0, 'e'},
580 {"miblist", required_argument, 0, 'm'},
581 {"protocol", required_argument, 0, 'P'},
582 {"seclevel", required_argument, 0, 'L'},
583 {"secname", required_argument, 0, 'U'},
584 {"authproto", required_argument, 0, 'a'},
585 {"privproto", required_argument, 0, 'x'},
586 {"authpasswd", required_argument, 0, 'A'},
587 {"privpasswd", required_argument, 0, 'X'},
588 {"next", no_argument, 0, 'n'},
589 {"rate", no_argument, 0, L_CALCULATE_RATE},
590 {"rate-multiplier", required_argument, 0, L_RATE_MULTIPLIER},
591 {"invert-search", no_argument, 0, L_INVERT_SEARCH},
592 {0, 0, 0, 0}
593 };
595 if (argc < 2)
596 return ERROR;
598 /* reverse compatibility for very old non-POSIX usage forms */
599 for (c = 1; c < argc; c++) {
600 if (strcmp ("-to", argv[c]) == 0)
601 strcpy (argv[c], "-t");
602 if (strcmp ("-wv", argv[c]) == 0)
603 strcpy (argv[c], "-w");
604 if (strcmp ("-cv", argv[c]) == 0)
605 strcpy (argv[c], "-c");
606 }
608 while (1) {
609 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:",
610 longopts, &option);
612 if (c == -1 || c == EOF)
613 break;
615 switch (c) {
616 case '?': /* usage */
617 usage5 ();
618 case 'h': /* help */
619 print_help ();
620 exit (STATE_OK);
621 case 'V': /* version */
622 print_revision (progname, NP_VERSION);
623 exit (STATE_OK);
624 case 'v': /* verbose */
625 verbose++;
626 break;
628 /* Connection info */
629 case 'C': /* group or community */
630 community = optarg;
631 break;
632 case 'H': /* Host or server */
633 server_address = optarg;
634 break;
635 case 'p': /* TCP port number */
636 port = optarg;
637 break;
638 case 'm': /* List of MIBS */
639 miblist = optarg;
640 break;
641 case 'n': /* usesnmpgetnext */
642 usesnmpgetnext = TRUE;
643 break;
644 case 'P': /* SNMP protocol version */
645 proto = optarg;
646 break;
647 case 'L': /* security level */
648 seclevel = optarg;
649 break;
650 case 'U': /* security username */
651 secname = optarg;
652 break;
653 case 'a': /* auth protocol */
654 authproto = optarg;
655 break;
656 case 'x': /* priv protocol */
657 privproto = optarg;
658 break;
659 case 'A': /* auth passwd */
660 authpasswd = optarg;
661 break;
662 case 'X': /* priv passwd */
663 privpasswd = optarg;
664 break;
665 case 't': /* timeout period */
666 if (!is_integer (optarg))
667 usage2 (_("Timeout interval must be a positive integer"), optarg);
668 else
669 timeout_interval = atoi (optarg);
670 break;
672 /* Test parameters */
673 case 'c': /* critical threshold */
674 critical_thresholds = optarg;
675 break;
676 case 'w': /* warning threshold */
677 warning_thresholds = optarg;
678 break;
679 case 'e': /* PRELIMINARY - may change */
680 case 'E': /* PRELIMINARY - may change */
681 if (!is_integer (optarg))
682 usage2 (_("Retries interval must be a positive integer"), optarg);
683 else
684 retries = atoi(optarg);
685 break;
686 case 'o': /* object identifier */
687 if ( strspn( optarg, "0123456789.," ) != strlen( optarg ) ) {
688 /*
689 * we have something other than digits, periods and comas,
690 * so we have a mib variable, rather than just an SNMP OID,
691 * so we have to actually read the mib files
692 */
693 needmibs = TRUE;
694 }
695 if (!oids) oids = calloc(MAX_OIDS, sizeof (char *));
696 for (ptr = strtok(optarg, ", "); ptr != NULL && j < MAX_OIDS; ptr = strtok(NULL, ", "), j++) {
697 oids[j] = strdup(ptr);
698 }
699 numoids = j;
700 if (c == 'E' || c == 'e') {
701 jj++;
702 ii++;
703 }
704 if (c == 'E')
705 eval_method[j+1] |= WARN_PRESENT;
706 else if (c == 'e')
707 eval_method[j+1] |= CRIT_PRESENT;
708 break;
709 case 's': /* string or substring */
710 strncpy (string_value, optarg, sizeof (string_value) - 1);
711 string_value[sizeof (string_value) - 1] = 0;
712 eval_method[jj++] = CRIT_STRING;
713 ii++;
714 break;
715 case 'R': /* regex */
716 cflags = REG_ICASE;
717 case 'r': /* regex */
718 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
719 strncpy (regex_expect, optarg, sizeof (regex_expect) - 1);
720 regex_expect[sizeof (regex_expect) - 1] = 0;
721 errcode = regcomp (&preg, regex_expect, cflags);
722 if (errcode != 0) {
723 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
724 printf (_("Could Not Compile Regular Expression"));
725 return ERROR;
726 }
727 eval_method[jj++] = CRIT_REGEX;
728 ii++;
729 break;
731 /* Format */
732 case 'd': /* delimiter */
733 delimiter = strscpy (delimiter, optarg);
734 break;
735 case 'D': /* output-delimiter */
736 output_delim = strscpy (output_delim, optarg);
737 break;
738 case 'l': /* label */
739 nlabels++;
740 if (nlabels >= labels_size) {
741 labels_size += 8;
742 labels = realloc (labels, labels_size);
743 if (labels == NULL)
744 die (STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels);
745 }
746 labels[nlabels - 1] = optarg;
747 ptr = thisarg (optarg);
748 labels[nlabels - 1] = ptr;
749 if (strstr (ptr, "'") == ptr)
750 labels[nlabels - 1] = ptr + 1;
751 while (ptr && (ptr = nextarg (ptr))) {
752 if (nlabels >= labels_size) {
753 labels_size += 8;
754 labels = realloc (labels, labels_size);
755 if (labels == NULL)
756 die (STATE_UNKNOWN, _("Could not reallocate labels\n"));
757 }
758 labels++;
759 ptr = thisarg (ptr);
760 if (strstr (ptr, "'") == ptr)
761 labels[nlabels - 1] = ptr + 1;
762 else
763 labels[nlabels - 1] = ptr;
764 }
765 break;
766 case 'u': /* units */
767 units = optarg;
768 nunits++;
769 if (nunits >= unitv_size) {
770 unitv_size += 8;
771 unitv = realloc (unitv, unitv_size);
772 if (unitv == NULL)
773 die (STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits);
774 }
775 unitv[nunits - 1] = optarg;
776 ptr = thisarg (optarg);
777 unitv[nunits - 1] = ptr;
778 if (strstr (ptr, "'") == ptr)
779 unitv[nunits - 1] = ptr + 1;
780 while (ptr && (ptr = nextarg (ptr))) {
781 if (nunits >= unitv_size) {
782 unitv_size += 8;
783 unitv = realloc (unitv, unitv_size);
784 if (units == NULL)
785 die (STATE_UNKNOWN, _("Could not realloc() units\n"));
786 }
787 nunits++;
788 ptr = thisarg (ptr);
789 if (strstr (ptr, "'") == ptr)
790 unitv[nunits - 1] = ptr + 1;
791 else
792 unitv[nunits - 1] = ptr;
793 }
794 break;
795 case L_CALCULATE_RATE:
796 if(calculate_rate==0)
797 np_enable_state(NULL, 1);
798 calculate_rate = 1;
799 break;
800 case L_RATE_MULTIPLIER:
801 if(!is_integer(optarg)||((rate_multiplier=atoi(optarg))<=0))
802 usage2(_("Rate multiplier must be a positive integer"),optarg);
803 break;
804 case L_INVERT_SEARCH:
805 invert_search=1;
806 break;
807 }
808 }
810 if (server_address == NULL)
811 server_address = argv[optind];
813 if (community == NULL)
814 community = strdup (DEFAULT_COMMUNITY);
816 return validate_arguments ();
817 }
820 /******************************************************************************
822 @@-
823 <sect3>
824 <title>validate_arguments</title>
826 <para>&PROTO_validate_arguments;</para>
828 <para>Checks to see if the default miblist needs to be loaded. Also verifies
829 the authentication and authorization combinations based on protocol version
830 selected.</para>
832 <para></para>
834 </sect3>
835 -@@
836 ******************************************************************************/
840 int
841 validate_arguments ()
842 {
843 /* check whether to load locally installed MIBS (CPU/disk intensive) */
844 if (miblist == NULL) {
845 if ( needmibs == TRUE ) {
846 miblist = strdup (DEFAULT_MIBLIST);
847 }else{
848 miblist = ""; /* don't read any mib files for numeric oids */
849 }
850 }
852 /* Check server_address is given */
853 if (server_address == NULL)
854 die(STATE_UNKNOWN, _("No host specified\n"));
856 /* Check oid is given */
857 if (numoids == 0)
858 die(STATE_UNKNOWN, _("No OIDs specified\n"));
860 if (proto == NULL)
861 asprintf(&proto, DEFAULT_PROTOCOL);
863 if ((strcmp(proto,"1") == 0) || (strcmp(proto, "2c")==0)) { /* snmpv1 or snmpv2c */
864 numauthpriv = 2;
865 authpriv = calloc (numauthpriv, sizeof (char *));
866 authpriv[0] = strdup ("-c");
867 authpriv[1] = strdup (community);
868 }
869 else if ( strcmp (proto, "3") == 0 ) { /* snmpv3 args */
870 if (seclevel == NULL)
871 asprintf(&seclevel, "noAuthNoPriv");
873 if (strcmp(seclevel, "noAuthNoPriv") == 0) {
874 numauthpriv = 2;
875 authpriv = calloc (numauthpriv, sizeof (char *));
876 authpriv[0] = strdup ("-l");
877 authpriv[1] = strdup ("noAuthNoPriv");
878 } else {
879 if (! ( (strcmp(seclevel, "authNoPriv")==0) || (strcmp(seclevel, "authPriv")==0) ) ) {
880 usage2 (_("Invalid seclevel"), seclevel);
881 }
883 if (authproto == NULL )
884 asprintf(&authproto, DEFAULT_AUTH_PROTOCOL);
886 if (secname == NULL)
887 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname");
889 if (authpasswd == NULL)
890 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd");
892 if ( strcmp(seclevel, "authNoPriv") == 0 ) {
893 numauthpriv = 8;
894 authpriv = calloc (numauthpriv, sizeof (char *));
895 authpriv[0] = strdup ("-l");
896 authpriv[1] = strdup ("authNoPriv");
897 authpriv[2] = strdup ("-a");
898 authpriv[3] = strdup (authproto);
899 authpriv[4] = strdup ("-u");
900 authpriv[5] = strdup (secname);
901 authpriv[6] = strdup ("-A");
902 authpriv[7] = strdup (authpasswd);
903 } else if ( strcmp(seclevel, "authPriv") == 0 ) {
904 if (privproto == NULL )
905 asprintf(&privproto, DEFAULT_PRIV_PROTOCOL);
907 if (privpasswd == NULL)
908 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd");
910 numauthpriv = 12;
911 authpriv = calloc (numauthpriv, sizeof (char *));
912 authpriv[0] = strdup ("-l");
913 authpriv[1] = strdup ("authPriv");
914 authpriv[2] = strdup ("-a");
915 authpriv[3] = strdup (authproto);
916 authpriv[4] = strdup ("-u");
917 authpriv[5] = strdup (secname);
918 authpriv[6] = strdup ("-A");
919 authpriv[7] = strdup (authpasswd);
920 authpriv[8] = strdup ("-x");
921 authpriv[9] = strdup (privproto);
922 authpriv[10] = strdup ("-X");
923 authpriv[11] = strdup (privpasswd);
924 }
925 }
927 }
928 else {
929 usage2 (_("Invalid SNMP version"), proto);
930 }
932 return OK;
933 }
937 /* trim leading whitespace
938 if there is a leading quote, make sure it balances */
940 char *
941 thisarg (char *str)
942 {
943 str += strspn (str, " \t\r\n"); /* trim any leading whitespace */
944 if (strstr (str, "'") == str) { /* handle SIMPLE quoted strings */
945 if (strlen (str) == 1 || !strstr (str + 1, "'"))
946 die (STATE_UNKNOWN, _("Unbalanced quotes\n"));
947 }
948 return str;
949 }
953 /* if there's a leading quote, advance to the trailing quote
954 set the trailing quote to '\x0'
955 if the string continues, advance beyond the comma */
957 char *
958 nextarg (char *str)
959 {
960 if (strstr (str, "'") == str) {
961 str[0] = 0;
962 if (strlen (str) > 1) {
963 str = strstr (str + 1, "'");
964 return (++str);
965 }
966 else {
967 return NULL;
968 }
969 }
970 if (strstr (str, ",") == str) {
971 str[0] = 0;
972 if (strlen (str) > 1) {
973 return (++str);
974 }
975 else {
976 return NULL;
977 }
978 }
979 if ((str = strstr (str, ",")) && strlen (str) > 1) {
980 str[0] = 0;
981 return (++str);
982 }
983 return NULL;
984 }
988 void
989 print_help (void)
990 {
991 print_revision (progname, NP_VERSION);
993 printf (COPYRIGHT, copyright, email);
995 printf ("%s\n", _("Check status of remote machines and obtain system information via SNMP"));
997 printf ("\n\n");
999 print_usage ();
1001 printf (UT_HELP_VRSN);
1002 printf (UT_EXTRA_OPTS);
1004 printf (UT_HOST_PORT, 'p', DEFAULT_PORT);
1006 /* SNMP and Authentication Protocol */
1007 printf (" %s\n", "-n, --next");
1008 printf (" %s\n", _("Use SNMP GETNEXT instead of SNMP GET"));
1009 printf (" %s\n", "-P, --protocol=[1|2c|3]");
1010 printf (" %s\n", _("SNMP protocol version"));
1011 printf (" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]");
1012 printf (" %s\n", _("SNMPv3 securityLevel"));
1013 printf (" %s\n", "-a, --authproto=[MD5|SHA]");
1014 printf (" %s\n", _("SNMPv3 auth proto"));
1015 printf (" %s\n", "-x, --privproto=[DES|AES]");
1016 printf (" %s\n", _("SNMPv3 priv proto (default DES)"));
1018 /* Authentication Tokens*/
1019 printf (" %s\n", "-C, --community=STRING");
1020 printf (" %s ", _("Optional community string for SNMP communication"));
1021 printf ("(%s \"%s\")\n", _("default is") ,DEFAULT_COMMUNITY);
1022 printf (" %s\n", "-U, --secname=USERNAME");
1023 printf (" %s\n", _("SNMPv3 username"));
1024 printf (" %s\n", "-A, --authpassword=PASSWORD");
1025 printf (" %s\n", _("SNMPv3 authentication password"));
1026 printf (" %s\n", "-X, --privpasswd=PASSWORD");
1027 printf (" %s\n", _("SNMPv3 privacy password"));
1029 /* OID Stuff */
1030 printf (" %s\n", "-o, --oid=OID(s)");
1031 printf (" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query"));
1032 printf (" %s\n", "-m, --miblist=STRING");
1033 printf (" %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'"));
1034 printf (" %s\n", _("for symbolic OIDs.)"));
1035 printf (" %s\n", "-d, --delimiter=STRING");
1036 printf (" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER);
1037 printf (" %s\n", _("Any data on the right hand side of the delimiter is considered"));
1038 printf (" %s\n", _("to be the data that should be used in the evaluation."));
1040 /* Tests Against Integers */
1041 printf (" %s\n", "-w, --warning=THRESHOLD(s)");
1042 printf (" %s\n", _("Warning threshold range(s)"));
1043 printf (" %s\n", "-c, --critical=THRESHOLD(s)");
1044 printf (" %s\n", _("Critical threshold range(s)"));
1045 printf (" %s\n", "--rate");
1046 printf (" %s\n", _("Enable rate calculation. See 'Rate Calculation' below"));
1047 printf (" %s\n", "--rate-multiplier");
1048 printf (" %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute"));
1050 /* Tests Against Strings */
1051 printf (" %s\n", "-s, --string=STRING");
1052 printf (" %s\n", _("Return OK state (for that OID) if STRING is an exact match"));
1053 printf (" %s\n", "-r, --ereg=REGEX");
1054 printf (" %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches"));
1055 printf (" %s\n", "-R, --eregi=REGEX");
1056 printf (" %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches"));
1057 printf (" %s\n", "--invert-search");
1058 printf (" %s\n", _("Invert search result (CRITICAL if found)"));
1060 /* Output Formatting */
1061 printf (" %s\n", "-l, --label=STRING");
1062 printf (" %s\n", _("Prefix label for output from plugin"));
1063 printf (" %s\n", "-u, --units=STRING");
1064 printf (" %s\n", _("Units label(s) for output data (e.g., 'sec.')."));
1065 printf (" %s\n", "-D, --output-delimiter=STRING");
1066 printf (" %s\n", _("Separates output on multiple OID requests"));
1068 printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1069 printf (" %s\n", "-e, --retries=INTEGER");
1070 printf (" %s\n", _("Number of retries to be used in the requests"));
1072 printf (UT_VERBOSE);
1074 printf ("\n");
1075 printf ("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package."));
1076 printf ("%s\n", _("if you don't have the package installed, you will need to download it from"));
1077 printf ("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1079 printf ("\n");
1080 printf ("%s\n", _("Notes:"));
1081 printf (" %s\n", _("- Multiple OIDs may be indicated by a comma- or space-delimited list (lists with"));
1082 printf (" %s\n", _("internal spaces must be quoted) [max 8 OIDs]"));
1084 printf(" -%s", UT_THRESHOLDS_NOTES);
1086 printf (" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'"));
1087 printf (" %s\n", _("- Note that only one string and one regex may be checked at present"));
1088 printf (" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value"));
1089 printf (" %s\n", _("returned from the SNMP query is an unsigned integer."));
1091 printf("\n");
1092 printf("%s\n", _("Rate Calculation:"));
1093 printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when"));
1094 printf(" %s\n", _("calculating the counter difference since the last check. check_snmp"));
1095 printf(" %s\n", _("saves the last state information in a file so that the rate per second"));
1096 printf(" %s\n", _("can be calculated. Use the --rate option to save state information."));
1097 printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK."));
1098 printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so"));
1099 printf(" %s\n", _("changing the arguments will create a new state file."));
1101 printf (UT_SUPPORT);
1102 }
1106 void
1107 print_usage (void)
1108 {
1109 printf ("%s\n", _("Usage:"));
1110 printf ("%s -H <ip_address> -o <OID> [-w warn_range] [-c crit_range]\n",progname);
1111 printf ("[-C community] [-s string] [-r regex] [-R regexi] [-t timeout] [-e retries]\n");
1112 printf ("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n");
1113 printf ("[-m miblist] [-P snmp version] [-L seclevel] [-U secname] [-a authproto]\n");
1114 printf ("[-A authpasswd] [-x privproto] [-X privpasswd]\n");
1115 }