Add -Le option to snmpcmd to avoid error message of 'No Log Handling Enabled...'
[monitoring-plugins.git] / plugins / check_snmp.c
blobb79c0200f7cbf1c3502ef352a182536883b6012b
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
12 * Check status of remote machines and obtain system information via SNMP
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.
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.
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/>.
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; \
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];
144 int perf_labels = 1;
147 static char *fix_snmp_range(char *th)
149 double left, right;
150 char *colon, *ret;
151 if (!(colon = strchr(th, ':')))
152 return th;
153 *colon = 0;
155 left = strtod(th, NULL);
156 right = strtod(colon + 1, NULL);
157 if (right >= left) {
158 return th;
160 ret = malloc(strlen(th) + strlen(colon + 1) + 2);
161 sprintf(ret, "@%s:%s", colon + 1, th);
162 free(th);
163 return ret;
167 main (int argc, char **argv)
169 int i, len, line, total_oids;
170 unsigned int bk_count = 0, dq_count = 0;
171 int iresult = STATE_UNKNOWN;
172 int result = STATE_UNKNOWN;
173 int return_code = 0;
174 int external_error = 0;
175 char **command_line = NULL;
176 char *cl_hidden_auth = NULL;
177 char *oidname = NULL;
178 char *response = NULL;
179 char *mult_resp = NULL;
180 char *outbuff;
181 char *ptr = NULL;
182 char *show = NULL;
183 char *th_warn=NULL;
184 char *th_crit=NULL;
185 char type[8] = "";
186 output chld_out, chld_err;
187 char *previous_string=NULL;
188 char *ap=NULL;
189 char *state_string=NULL;
190 size_t response_length, current_length, string_length;
191 char *temp_string=NULL;
192 char *quote_string=NULL;
193 time_t current_time;
194 double temp_double;
195 time_t duration;
196 char *conv = "12345678";
197 int is_counter=0;
199 setlocale (LC_ALL, "");
200 bindtextdomain (PACKAGE, LOCALEDIR);
201 textdomain (PACKAGE);
203 labels = malloc (labels_size * sizeof(*labels));
204 unitv = malloc (unitv_size * sizeof(*unitv));
205 for (i = 0; i < MAX_OIDS; i++)
206 eval_method[i] = CHECK_UNDEF;
208 label = strdup ("SNMP");
209 units = strdup ("");
210 port = strdup (DEFAULT_PORT);
211 outbuff = strdup ("");
212 delimiter = strdup (" = ");
213 output_delim = strdup (DEFAULT_OUTPUT_DELIMITER);
214 timeout_interval = DEFAULT_TIMEOUT;
215 retries = DEFAULT_RETRIES;
217 np_init( (char *) progname, argc, argv );
219 /* Parse extra opts if any */
220 argv=np_extra_opts (&argc, argv, progname);
222 np_set_args(argc, argv);
224 if (process_arguments (argc, argv) == ERROR)
225 usage4 (_("Could not parse arguments"));
227 if(calculate_rate) {
228 if (!strcmp(label, "SNMP"))
229 label = strdup("SNMP RATE");
230 time(&current_time);
231 i=0;
232 previous_state = np_state_read();
233 if(previous_state!=NULL) {
234 /* Split colon separated values */
235 previous_string = strdup((char *) previous_state->data);
236 while((ap = strsep(&previous_string, ":")) != NULL) {
237 if(verbose>2)
238 printf("State for %d=%s\n", i, ap);
239 previous_value[i++]=strtod(ap,NULL);
244 /* Populate the thresholds */
245 th_warn=warning_thresholds;
246 th_crit=critical_thresholds;
247 for (i=0; i<numoids; i++) {
248 char *w = th_warn ? strndup(th_warn, strcspn(th_warn, ",")) : NULL;
249 char *c = th_crit ? strndup(th_crit, strcspn(th_crit, ",")) : NULL;
250 /* translate "2:1" to "@1:2" for backwards compatibility */
251 w = w ? fix_snmp_range(w) : NULL;
252 c = c ? fix_snmp_range(c) : NULL;
254 /* Skip empty thresholds, while avoiding segfault */
255 set_thresholds(&thlds[i],
256 w ? strpbrk(w, NP_THRESHOLDS_CHARS) : NULL,
257 c ? strpbrk(c, NP_THRESHOLDS_CHARS) : NULL);
258 if (w) {
259 th_warn=strchr(th_warn, ',');
260 if (th_warn) th_warn++;
261 free(w);
263 if (c) {
264 th_crit=strchr(th_crit, ',');
265 if (th_crit) th_crit++;
266 free(c);
270 /* Create the command array to execute */
271 if(usesnmpgetnext == TRUE) {
272 snmpcmd = strdup (PATH_TO_SNMPGETNEXT);
273 }else{
274 snmpcmd = strdup (PATH_TO_SNMPGET);
277 /* 9 arguments to pass before authpriv options + 1 for host and numoids. Add one for terminating NULL */
278 command_line = calloc (9 + numauthpriv + 1 + numoids + 1, sizeof (char *));
279 command_line[0] = snmpcmd;
280 command_line[1] = strdup ("-Le");
281 command_line[2] = strdup ("-t");
282 xasprintf (&command_line[3], "%d", timeout_interval);
283 command_line[4] = strdup ("-r");
284 xasprintf (&command_line[5], "%d", retries);
285 command_line[6] = strdup ("-m");
286 command_line[7] = strdup (miblist);
287 command_line[8] = "-v";
288 command_line[9] = strdup (proto);
290 for (i = 0; i < numauthpriv; i++) {
291 command_line[10 + i] = authpriv[i];
294 xasprintf (&command_line[10 + numauthpriv], "%s:%s", server_address, port);
296 /* This is just for display purposes, so it can remain a string */
297 xasprintf(&cl_hidden_auth, "%s -Le -t %d -r %d -m %s -v %s %s %s:%s",
298 snmpcmd, timeout_interval, retries, strlen(miblist) ? miblist : "''", proto, "[authpriv]",
299 server_address, port);
301 for (i = 0; i < numoids; i++) {
302 command_line[10 + numauthpriv + 1 + i] = oids[i];
303 xasprintf(&cl_hidden_auth, "%s %s", cl_hidden_auth, oids[i]);
306 command_line[10 + numauthpriv + 1 + numoids] = NULL;
308 if (verbose)
309 printf ("%s\n", cl_hidden_auth);
311 /* Run the command */
312 return_code = cmd_run_array (command_line, &chld_out, &chld_err, 0);
314 /* Due to net-snmp sometimes showing stderr messages with poorly formed MIBs,
315 only return state unknown if return code is non zero or there is no stdout.
316 Do this way so that if there is stderr, will get added to output, which helps problem diagnosis
318 if (return_code != 0)
319 external_error=1;
320 if (chld_out.lines == 0)
321 external_error=1;
322 if (external_error) {
323 if (chld_err.lines > 0) {
324 printf (_("External command error: %s\n"), chld_err.line[0]);
325 for (i = 1; i < chld_err.lines; i++) {
326 printf ("%s\n", chld_err.line[i]);
328 } else {
329 printf(_("External command error with no output (return code: %d)\n"), return_code);
331 exit (STATE_UNKNOWN);
334 if (verbose) {
335 for (i = 0; i < chld_out.lines; i++) {
336 printf ("%s\n", chld_out.line[i]);
340 for (line=0, i=0; line < chld_out.lines; line++, i++) {
341 if(calculate_rate)
342 conv = "%.10g";
343 else
344 conv = "%.0f";
346 ptr = chld_out.line[line];
347 oidname = strpcpy (oidname, ptr, delimiter);
348 response = strstr (ptr, delimiter);
349 if (response == NULL)
350 break;
352 if (verbose > 2) {
353 printf("Processing oid %i (line %i)\n oidname: %s\n response: %s\n", i+1, line+1, oidname, response);
356 /* Clean up type array - Sol10 does not necessarily zero it out */
357 bzero(type, sizeof(type));
359 is_counter=0;
360 /* We strip out the datatype indicator for PHBs */
361 if (strstr (response, "Gauge: ")) {
362 show = strstr (response, "Gauge: ") + 7;
364 else if (strstr (response, "Gauge32: ")) {
365 show = strstr (response, "Gauge32: ") + 9;
367 else if (strstr (response, "Counter32: ")) {
368 show = strstr (response, "Counter32: ") + 11;
369 is_counter=1;
370 if(!calculate_rate)
371 strcpy(type, "c");
373 else if (strstr (response, "Counter64: ")) {
374 show = strstr (response, "Counter64: ") + 11;
375 is_counter=1;
376 if(!calculate_rate)
377 strcpy(type, "c");
379 else if (strstr (response, "INTEGER: ")) {
380 show = strstr (response, "INTEGER: ") + 9;
382 else if (strstr (response, "STRING: ")) {
383 show = strstr (response, "STRING: ") + 8;
384 conv = "%.10g";
386 /* Get the rest of the string on multi-line strings */
387 ptr = show;
388 COUNT_SEQ(ptr, bk_count, dq_count)
389 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
390 ptr++;
391 GOBBLE_TOS(ptr, "\n\"\\")
392 COUNT_SEQ(ptr, bk_count, dq_count)
395 if (dq_count) { /* unfinished line */
396 /* copy show verbatim first */
397 if (!mult_resp) mult_resp = strdup("");
398 xasprintf (&mult_resp, "%s%s:\n%s\n", mult_resp, oids[i], show);
399 /* then strip out unmatched double-quote from single-line output */
400 if (show[0] == '"') show++;
402 /* Keep reading until we match end of double-quoted string */
403 for (line++; line < chld_out.lines; line++) {
404 ptr = chld_out.line[line];
405 xasprintf (&mult_resp, "%s%s\n", mult_resp, ptr);
407 COUNT_SEQ(ptr, bk_count, dq_count)
408 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
409 ptr++;
410 GOBBLE_TOS(ptr, "\n\"\\")
411 COUNT_SEQ(ptr, bk_count, dq_count)
413 /* Break for loop before next line increment when done */
414 if (!dq_count) break;
419 else if (strstr (response, "Timeticks: ")) {
420 show = strstr (response, "Timeticks: ");
422 else
423 show = response + 3;
425 iresult = STATE_DEPENDENT;
427 /* Process this block for numeric comparisons */
428 /* Make some special values,like Timeticks numeric only if a threshold is defined */
429 if (thlds[i]->warning || thlds[i]->critical || calculate_rate) {
430 ptr = strpbrk (show, "0123456789");
431 if (ptr == NULL)
432 die (STATE_UNKNOWN,_("No valid data returned (%s)\n"), show);
433 response_value[i] = strtod (ptr, NULL);
435 if(calculate_rate) {
436 if (previous_state!=NULL) {
437 duration = current_time-previous_state->time;
438 if(duration<=0)
439 die(STATE_UNKNOWN,_("Time duration between plugin calls is invalid"));
440 temp_double = response_value[i]-previous_value[i];
441 /* Simple overflow catcher (same as in rrdtool, rrd_update.c) */
442 if(is_counter) {
443 if(temp_double<(double)0.0)
444 temp_double+=(double)4294967296.0; /* 2^32 */
445 if(temp_double<(double)0.0)
446 temp_double+=(double)18446744069414584320.0; /* 2^64-2^32 */;
448 /* Convert to per second, then use multiplier */
449 temp_double = temp_double/duration*rate_multiplier;
450 iresult = get_status(temp_double, thlds[i]);
451 xasprintf (&show, conv, temp_double);
453 } else {
454 iresult = get_status(response_value[i], thlds[i]);
455 xasprintf (&show, conv, response_value[i]);
459 /* Process this block for string matching */
460 else if (eval_method[i] & CRIT_STRING) {
461 if (strcmp (show, string_value))
462 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK;
463 else
464 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
467 /* Process this block for regex matching */
468 else if (eval_method[i] & CRIT_REGEX) {
469 excode = regexec (&preg, response, 10, pmatch, eflags);
470 if (excode == 0) {
471 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
473 else if (excode != REG_NOMATCH) {
474 regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER);
475 printf (_("Execute Error: %s\n"), errbuf);
476 exit (STATE_CRITICAL);
478 else {
479 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK;
483 /* Process this block for existence-nonexistence checks */
484 /* TV: Should this be outside of this else block? */
485 else {
486 if (eval_method[i] & CRIT_PRESENT)
487 iresult = STATE_CRITICAL;
488 else if (eval_method[i] & WARN_PRESENT)
489 iresult = STATE_WARNING;
490 else if (response && iresult == STATE_DEPENDENT)
491 iresult = STATE_OK;
494 /* Result is the worst outcome of all the OIDs tested */
495 result = max_state (result, iresult);
497 /* Prepend a label for this OID if there is one */
498 if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
499 xasprintf (&outbuff, "%s%s%s %s%s%s", outbuff,
500 (i == 0) ? " " : output_delim,
501 labels[i], mark (iresult), show, mark (iresult));
502 else
503 xasprintf (&outbuff, "%s%s%s%s%s", outbuff, (i == 0) ? " " : output_delim,
504 mark (iresult), show, mark (iresult));
506 /* Append a unit string for this OID if there is one */
507 if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL)
508 xasprintf (&outbuff, "%s %s", outbuff, unitv[i]);
510 /* Write perfdata with whatever can be parsed by strtod, if possible */
511 ptr = NULL;
512 strtod(show, &ptr);
513 if (ptr > show) {
514 if (perf_labels && nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
515 temp_string=labels[i];
516 else
517 temp_string=oidname;
518 if (strpbrk (temp_string, " ='\"") == NULL) {
519 strncat(perfstr, temp_string, sizeof(perfstr)-strlen(perfstr)-1);
520 } else {
521 if (strpbrk (temp_string, "'") == NULL) {
522 quote_string="'";
523 } else {
524 quote_string="\"";
526 strncat(perfstr, quote_string, sizeof(perfstr)-strlen(perfstr)-1);
527 strncat(perfstr, temp_string, sizeof(perfstr)-strlen(perfstr)-1);
528 strncat(perfstr, quote_string, sizeof(perfstr)-strlen(perfstr)-1);
530 strncat(perfstr, "=", sizeof(perfstr)-strlen(perfstr)-1);
531 len = sizeof(perfstr)-strlen(perfstr)-1;
532 strncat(perfstr, show, len>ptr-show ? ptr-show : len);
534 if (type)
535 strncat(perfstr, type, sizeof(perfstr)-strlen(perfstr)-1);
536 strncat(perfstr, " ", sizeof(perfstr)-strlen(perfstr)-1);
539 total_oids=i;
541 /* Save state data, as all data collected now */
542 if(calculate_rate) {
543 string_length=1024;
544 state_string=malloc(string_length);
545 if(state_string==NULL)
546 die(STATE_UNKNOWN, _("Cannot malloc"));
548 current_length=0;
549 for(i=0; i<total_oids; i++) {
550 xasprintf(&temp_string,"%.0f",response_value[i]);
551 if(temp_string==NULL)
552 die(STATE_UNKNOWN,_("Cannot asprintf()"));
553 response_length = strlen(temp_string);
554 if(current_length+response_length>string_length) {
555 string_length=current_length+1024;
556 state_string=realloc(state_string,string_length);
557 if(state_string==NULL)
558 die(STATE_UNKNOWN, _("Cannot realloc()"));
560 strcpy(&state_string[current_length],temp_string);
561 current_length=current_length+response_length;
562 state_string[current_length]=':';
563 current_length++;
564 free(temp_string);
566 state_string[--current_length]='\0';
567 if (verbose > 2)
568 printf("State string=%s\n",state_string);
570 /* This is not strictly the same as time now, but any subtle variations will cancel out */
571 np_state_write_string(current_time, state_string );
572 if(previous_state==NULL) {
573 /* Or should this be highest state? */
574 die( STATE_OK, _("No previous data to calculate rate - assume okay" ) );
578 printf ("%s %s -%s %s\n", label, state_text (result), outbuff, perfstr);
579 if (mult_resp) printf ("%s", mult_resp);
581 return result;
586 /* process command-line arguments */
588 process_arguments (int argc, char **argv)
590 char *ptr;
591 int c = 1;
592 int j = 0, jj = 0, ii = 0;
594 int option = 0;
595 static struct option longopts[] = {
596 STD_LONG_OPTS,
597 {"community", required_argument, 0, 'C'},
598 {"oid", required_argument, 0, 'o'},
599 {"object", required_argument, 0, 'o'},
600 {"delimiter", required_argument, 0, 'd'},
601 {"output-delimiter", required_argument, 0, 'D'},
602 {"string", required_argument, 0, 's'},
603 {"timeout", required_argument, 0, 't'},
604 {"regex", required_argument, 0, 'r'},
605 {"ereg", required_argument, 0, 'r'},
606 {"eregi", required_argument, 0, 'R'},
607 {"label", required_argument, 0, 'l'},
608 {"units", required_argument, 0, 'u'},
609 {"port", required_argument, 0, 'p'},
610 {"retries", required_argument, 0, 'e'},
611 {"miblist", required_argument, 0, 'm'},
612 {"protocol", required_argument, 0, 'P'},
613 {"seclevel", required_argument, 0, 'L'},
614 {"secname", required_argument, 0, 'U'},
615 {"authproto", required_argument, 0, 'a'},
616 {"privproto", required_argument, 0, 'x'},
617 {"authpasswd", required_argument, 0, 'A'},
618 {"privpasswd", required_argument, 0, 'X'},
619 {"next", no_argument, 0, 'n'},
620 {"rate", no_argument, 0, L_CALCULATE_RATE},
621 {"rate-multiplier", required_argument, 0, L_RATE_MULTIPLIER},
622 {"invert-search", no_argument, 0, L_INVERT_SEARCH},
623 {"perf-oids", no_argument, 0, 'O'},
624 {0, 0, 0, 0}
627 if (argc < 2)
628 return ERROR;
630 /* reverse compatibility for very old non-POSIX usage forms */
631 for (c = 1; c < argc; c++) {
632 if (strcmp ("-to", argv[c]) == 0)
633 strcpy (argv[c], "-t");
634 if (strcmp ("-wv", argv[c]) == 0)
635 strcpy (argv[c], "-w");
636 if (strcmp ("-cv", argv[c]) == 0)
637 strcpy (argv[c], "-c");
640 while (1) {
641 c = getopt_long (argc, argv, "nhvVOt:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:L:U:a:x:A:X:",
642 longopts, &option);
644 if (c == -1 || c == EOF)
645 break;
647 switch (c) {
648 case '?': /* usage */
649 usage5 ();
650 case 'h': /* help */
651 print_help ();
652 exit (STATE_OK);
653 case 'V': /* version */
654 print_revision (progname, NP_VERSION);
655 exit (STATE_OK);
656 case 'v': /* verbose */
657 verbose++;
658 break;
660 /* Connection info */
661 case 'C': /* group or community */
662 community = optarg;
663 break;
664 case 'H': /* Host or server */
665 server_address = optarg;
666 break;
667 case 'p': /* TCP port number */
668 port = optarg;
669 break;
670 case 'm': /* List of MIBS */
671 miblist = optarg;
672 break;
673 case 'n': /* usesnmpgetnext */
674 usesnmpgetnext = TRUE;
675 break;
676 case 'P': /* SNMP protocol version */
677 proto = optarg;
678 break;
679 case 'L': /* security level */
680 seclevel = optarg;
681 break;
682 case 'U': /* security username */
683 secname = optarg;
684 break;
685 case 'a': /* auth protocol */
686 authproto = optarg;
687 break;
688 case 'x': /* priv protocol */
689 privproto = optarg;
690 break;
691 case 'A': /* auth passwd */
692 authpasswd = optarg;
693 break;
694 case 'X': /* priv passwd */
695 privpasswd = optarg;
696 break;
697 case 't': /* timeout period */
698 if (!is_integer (optarg))
699 usage2 (_("Timeout interval must be a positive integer"), optarg);
700 else
701 timeout_interval = atoi (optarg);
702 break;
704 /* Test parameters */
705 case 'c': /* critical threshold */
706 critical_thresholds = optarg;
707 break;
708 case 'w': /* warning threshold */
709 warning_thresholds = optarg;
710 break;
711 case 'e': /* PRELIMINARY - may change */
712 case 'E': /* PRELIMINARY - may change */
713 if (!is_integer (optarg))
714 usage2 (_("Retries interval must be a positive integer"), optarg);
715 else
716 retries = atoi(optarg);
717 break;
718 case 'o': /* object identifier */
719 if ( strspn( optarg, "0123456789.," ) != strlen( optarg ) ) {
721 * we have something other than digits, periods and comas,
722 * so we have a mib variable, rather than just an SNMP OID,
723 * so we have to actually read the mib files
725 needmibs = TRUE;
727 if (!oids) oids = calloc(MAX_OIDS, sizeof (char *));
728 for (ptr = strtok(optarg, ", "); ptr != NULL && j < MAX_OIDS; ptr = strtok(NULL, ", "), j++) {
729 oids[j] = strdup(ptr);
731 numoids = j;
732 if (c == 'E' || c == 'e') {
733 jj++;
734 ii++;
736 if (c == 'E')
737 eval_method[j+1] |= WARN_PRESENT;
738 else if (c == 'e')
739 eval_method[j+1] |= CRIT_PRESENT;
740 break;
741 case 's': /* string or substring */
742 strncpy (string_value, optarg, sizeof (string_value) - 1);
743 string_value[sizeof (string_value) - 1] = 0;
744 eval_method[jj++] = CRIT_STRING;
745 ii++;
746 break;
747 case 'R': /* regex */
748 cflags = REG_ICASE;
749 case 'r': /* regex */
750 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
751 strncpy (regex_expect, optarg, sizeof (regex_expect) - 1);
752 regex_expect[sizeof (regex_expect) - 1] = 0;
753 errcode = regcomp (&preg, regex_expect, cflags);
754 if (errcode != 0) {
755 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
756 printf (_("Could Not Compile Regular Expression"));
757 return ERROR;
759 eval_method[jj++] = CRIT_REGEX;
760 ii++;
761 break;
763 /* Format */
764 case 'd': /* delimiter */
765 delimiter = strscpy (delimiter, optarg);
766 break;
767 case 'D': /* output-delimiter */
768 output_delim = strscpy (output_delim, optarg);
769 break;
770 case 'l': /* label */
771 nlabels++;
772 if (nlabels > labels_size) {
773 labels_size += 8;
774 labels = realloc (labels, labels_size * sizeof(*labels));
775 if (labels == NULL)
776 die (STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels);
778 labels[nlabels - 1] = optarg;
779 ptr = thisarg (optarg);
780 labels[nlabels - 1] = ptr;
781 if (ptr[0] == '\'')
782 labels[nlabels - 1] = ptr + 1;
783 while (ptr && (ptr = nextarg (ptr))) {
784 nlabels++;
785 if (nlabels > labels_size) {
786 labels_size += 8;
787 labels = realloc (labels, labels_size * sizeof(*labels));
788 if (labels == NULL)
789 die (STATE_UNKNOWN, _("Could not reallocate labels\n"));
791 ptr = thisarg (ptr);
792 if (ptr[0] == '\'')
793 labels[nlabels - 1] = ptr + 1;
794 else
795 labels[nlabels - 1] = ptr;
797 break;
798 case 'u': /* units */
799 units = optarg;
800 nunits++;
801 if (nunits > unitv_size) {
802 unitv_size += 8;
803 unitv = realloc (unitv, unitv_size * sizeof(*unitv));
804 if (unitv == NULL)
805 die (STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits);
807 unitv[nunits - 1] = optarg;
808 ptr = thisarg (optarg);
809 unitv[nunits - 1] = ptr;
810 if (ptr[0] == '\'')
811 unitv[nunits - 1] = ptr + 1;
812 while (ptr && (ptr = nextarg (ptr))) {
813 if (nunits > unitv_size) {
814 unitv_size += 8;
815 unitv = realloc (unitv, unitv_size * sizeof(*unitv));
816 if (units == NULL)
817 die (STATE_UNKNOWN, _("Could not realloc() units\n"));
819 nunits++;
820 ptr = thisarg (ptr);
821 if (ptr[0] == '\'')
822 unitv[nunits - 1] = ptr + 1;
823 else
824 unitv[nunits - 1] = ptr;
826 break;
827 case L_CALCULATE_RATE:
828 if(calculate_rate==0)
829 np_enable_state(NULL, 1);
830 calculate_rate = 1;
831 break;
832 case L_RATE_MULTIPLIER:
833 if(!is_integer(optarg)||((rate_multiplier=atoi(optarg))<=0))
834 usage2(_("Rate multiplier must be a positive integer"),optarg);
835 break;
836 case L_INVERT_SEARCH:
837 invert_search=1;
838 break;
839 case 'O':
840 perf_labels=0;
841 break;
845 if (server_address == NULL)
846 server_address = argv[optind];
848 if (community == NULL)
849 community = strdup (DEFAULT_COMMUNITY);
851 return validate_arguments ();
855 /******************************************************************************
858 <sect3>
859 <title>validate_arguments</title>
861 <para>&PROTO_validate_arguments;</para>
863 <para>Checks to see if the default miblist needs to be loaded. Also verifies
864 the authentication and authorization combinations based on protocol version
865 selected.</para>
867 <para></para>
869 </sect3>
871 ******************************************************************************/
876 validate_arguments ()
878 /* check whether to load locally installed MIBS (CPU/disk intensive) */
879 if (miblist == NULL) {
880 if ( needmibs == TRUE ) {
881 miblist = strdup (DEFAULT_MIBLIST);
882 }else{
883 miblist = ""; /* don't read any mib files for numeric oids */
887 /* Check server_address is given */
888 if (server_address == NULL)
889 die(STATE_UNKNOWN, _("No host specified\n"));
891 /* Check oid is given */
892 if (numoids == 0)
893 die(STATE_UNKNOWN, _("No OIDs specified\n"));
895 if (proto == NULL)
896 xasprintf(&proto, DEFAULT_PROTOCOL);
898 if ((strcmp(proto,"1") == 0) || (strcmp(proto, "2c")==0)) { /* snmpv1 or snmpv2c */
899 numauthpriv = 2;
900 authpriv = calloc (numauthpriv, sizeof (char *));
901 authpriv[0] = strdup ("-c");
902 authpriv[1] = strdup (community);
904 else if ( strcmp (proto, "3") == 0 ) { /* snmpv3 args */
905 if (seclevel == NULL)
906 xasprintf(&seclevel, "noAuthNoPriv");
908 if (strcmp(seclevel, "noAuthNoPriv") == 0) {
909 numauthpriv = 2;
910 authpriv = calloc (numauthpriv, sizeof (char *));
911 authpriv[0] = strdup ("-l");
912 authpriv[1] = strdup ("noAuthNoPriv");
913 } else {
914 if (! ( (strcmp(seclevel, "authNoPriv")==0) || (strcmp(seclevel, "authPriv")==0) ) ) {
915 usage2 (_("Invalid seclevel"), seclevel);
918 if (authproto == NULL )
919 xasprintf(&authproto, DEFAULT_AUTH_PROTOCOL);
921 if (secname == NULL)
922 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname");
924 if (authpasswd == NULL)
925 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd");
927 if ( strcmp(seclevel, "authNoPriv") == 0 ) {
928 numauthpriv = 8;
929 authpriv = calloc (numauthpriv, sizeof (char *));
930 authpriv[0] = strdup ("-l");
931 authpriv[1] = strdup ("authNoPriv");
932 authpriv[2] = strdup ("-a");
933 authpriv[3] = strdup (authproto);
934 authpriv[4] = strdup ("-u");
935 authpriv[5] = strdup (secname);
936 authpriv[6] = strdup ("-A");
937 authpriv[7] = strdup (authpasswd);
938 } else if ( strcmp(seclevel, "authPriv") == 0 ) {
939 if (privproto == NULL )
940 xasprintf(&privproto, DEFAULT_PRIV_PROTOCOL);
942 if (privpasswd == NULL)
943 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd");
945 numauthpriv = 12;
946 authpriv = calloc (numauthpriv, sizeof (char *));
947 authpriv[0] = strdup ("-l");
948 authpriv[1] = strdup ("authPriv");
949 authpriv[2] = strdup ("-a");
950 authpriv[3] = strdup (authproto);
951 authpriv[4] = strdup ("-u");
952 authpriv[5] = strdup (secname);
953 authpriv[6] = strdup ("-A");
954 authpriv[7] = strdup (authpasswd);
955 authpriv[8] = strdup ("-x");
956 authpriv[9] = strdup (privproto);
957 authpriv[10] = strdup ("-X");
958 authpriv[11] = strdup (privpasswd);
963 else {
964 usage2 (_("Invalid SNMP version"), proto);
967 return OK;
972 /* trim leading whitespace
973 if there is a leading quote, make sure it balances */
975 char *
976 thisarg (char *str)
978 str += strspn (str, " \t\r\n"); /* trim any leading whitespace */
979 if (str[0] == '\'') { /* handle SIMPLE quoted strings */
980 if (strlen (str) == 1 || !strstr (str + 1, "'"))
981 die (STATE_UNKNOWN, _("Unbalanced quotes\n"));
983 return str;
988 /* if there's a leading quote, advance to the trailing quote
989 set the trailing quote to '\x0'
990 if the string continues, advance beyond the comma */
992 char *
993 nextarg (char *str)
995 if (str[0] == '\'') {
996 str[0] = 0;
997 if (strlen (str) > 1) {
998 str = strstr (str + 1, "'");
999 return (++str);
1001 else {
1002 return NULL;
1005 if (str[0] == ',') {
1006 str[0] = 0;
1007 if (strlen (str) > 1) {
1008 return (++str);
1010 else {
1011 return NULL;
1014 if ((str = strstr (str, ",")) && strlen (str) > 1) {
1015 str[0] = 0;
1016 return (++str);
1018 return NULL;
1023 void
1024 print_help (void)
1026 print_revision (progname, NP_VERSION);
1028 printf (COPYRIGHT, copyright, email);
1030 printf ("%s\n", _("Check status of remote machines and obtain system information via SNMP"));
1032 printf ("\n\n");
1034 print_usage ();
1036 printf (UT_HELP_VRSN);
1037 printf (UT_EXTRA_OPTS);
1039 printf (UT_HOST_PORT, 'p', DEFAULT_PORT);
1041 /* SNMP and Authentication Protocol */
1042 printf (" %s\n", "-n, --next");
1043 printf (" %s\n", _("Use SNMP GETNEXT instead of SNMP GET"));
1044 printf (" %s\n", "-P, --protocol=[1|2c|3]");
1045 printf (" %s\n", _("SNMP protocol version"));
1046 printf (" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]");
1047 printf (" %s\n", _("SNMPv3 securityLevel"));
1048 printf (" %s\n", "-a, --authproto=[MD5|SHA]");
1049 printf (" %s\n", _("SNMPv3 auth proto"));
1050 printf (" %s\n", "-x, --privproto=[DES|AES]");
1051 printf (" %s\n", _("SNMPv3 priv proto (default DES)"));
1053 /* Authentication Tokens*/
1054 printf (" %s\n", "-C, --community=STRING");
1055 printf (" %s ", _("Optional community string for SNMP communication"));
1056 printf ("(%s \"%s\")\n", _("default is") ,DEFAULT_COMMUNITY);
1057 printf (" %s\n", "-U, --secname=USERNAME");
1058 printf (" %s\n", _("SNMPv3 username"));
1059 printf (" %s\n", "-A, --authpassword=PASSWORD");
1060 printf (" %s\n", _("SNMPv3 authentication password"));
1061 printf (" %s\n", "-X, --privpasswd=PASSWORD");
1062 printf (" %s\n", _("SNMPv3 privacy password"));
1064 /* OID Stuff */
1065 printf (" %s\n", "-o, --oid=OID(s)");
1066 printf (" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query"));
1067 printf (" %s\n", "-m, --miblist=STRING");
1068 printf (" %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'"));
1069 printf (" %s\n", _("for symbolic OIDs.)"));
1070 printf (" %s\n", "-d, --delimiter=STRING");
1071 printf (" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER);
1072 printf (" %s\n", _("Any data on the right hand side of the delimiter is considered"));
1073 printf (" %s\n", _("to be the data that should be used in the evaluation."));
1075 /* Tests Against Integers */
1076 printf (" %s\n", "-w, --warning=THRESHOLD(s)");
1077 printf (" %s\n", _("Warning threshold range(s)"));
1078 printf (" %s\n", "-c, --critical=THRESHOLD(s)");
1079 printf (" %s\n", _("Critical threshold range(s)"));
1080 printf (" %s\n", "--rate");
1081 printf (" %s\n", _("Enable rate calculation. See 'Rate Calculation' below"));
1082 printf (" %s\n", "--rate-multiplier");
1083 printf (" %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute"));
1085 /* Tests Against Strings */
1086 printf (" %s\n", "-s, --string=STRING");
1087 printf (" %s\n", _("Return OK state (for that OID) if STRING is an exact match"));
1088 printf (" %s\n", "-r, --ereg=REGEX");
1089 printf (" %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches"));
1090 printf (" %s\n", "-R, --eregi=REGEX");
1091 printf (" %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches"));
1092 printf (" %s\n", "--invert-search");
1093 printf (" %s\n", _("Invert search result (CRITICAL if found)"));
1095 /* Output Formatting */
1096 printf (" %s\n", "-l, --label=STRING");
1097 printf (" %s\n", _("Prefix label for output from plugin"));
1098 printf (" %s\n", "-u, --units=STRING");
1099 printf (" %s\n", _("Units label(s) for output data (e.g., 'sec.')."));
1100 printf (" %s\n", "-D, --output-delimiter=STRING");
1101 printf (" %s\n", _("Separates output on multiple OID requests"));
1103 printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1104 printf (" %s\n", "-e, --retries=INTEGER");
1105 printf (" %s\n", _("Number of retries to be used in the requests"));
1107 printf (" %s\n", "-O, --perf-oids");
1108 printf (" %s\n", _("Label performance data with OIDs instead of --label's"));
1110 printf (UT_VERBOSE);
1112 printf ("\n");
1113 printf ("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package."));
1114 printf ("%s\n", _("if you don't have the package installed, you will need to download it from"));
1115 printf ("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1117 printf ("\n");
1118 printf ("%s\n", _("Notes:"));
1119 printf (" %s\n", _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited "));
1120 printf (" %s %i %s\n", _("list (lists with internal spaces must be quoted). Maximum:"), MAX_OIDS, _("OIDs."));
1122 printf(" -%s", UT_THRESHOLDS_NOTES);
1124 printf (" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'"));
1125 printf (" %s\n", _("- Note that only one string and one regex may be checked at present"));
1126 printf (" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value"));
1127 printf (" %s\n", _("returned from the SNMP query is an unsigned integer."));
1129 printf("\n");
1130 printf("%s\n", _("Rate Calculation:"));
1131 printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when"));
1132 printf(" %s\n", _("calculating the counter difference since the last check. check_snmp"));
1133 printf(" %s\n", _("saves the last state information in a file so that the rate per second"));
1134 printf(" %s\n", _("can be calculated. Use the --rate option to save state information."));
1135 printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK."));
1136 printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so"));
1137 printf(" %s\n", _("changing the arguments will create a new state file."));
1139 printf (UT_SUPPORT);
1144 void
1145 print_usage (void)
1147 printf ("%s\n", _("Usage:"));
1148 printf ("%s -H <ip_address> -o <OID> [-w warn_range] [-c crit_range]\n",progname);
1149 printf ("[-C community] [-s string] [-r regex] [-R regexi] [-t timeout] [-e retries]\n");
1150 printf ("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n");
1151 printf ("[-m miblist] [-P snmp version] [-L seclevel] [-U secname] [-a authproto]\n");
1152 printf ("[-A authpasswd] [-x privproto] [-X privpasswd]\n");