Merge pull request #1563 from jacobbaungard/ipv6_check_icmp
[monitoring-plugins.git] / plugins / check_snmp.c
blobda9638c40c11f85d2fb9aad3dd700f2ef15e2f0e
1 /*****************************************************************************
2 *
3 * Monitoring check_snmp plugin
4 *
5 * License: GPL
6 * Copyright (c) 1999-2007 Monitoring 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 = "devel@monitoring-plugins.org";
35 #include "common.h"
36 #include "runcmd.h"
37 #include "utils.h"
38 #include "utils_cmd.h"
40 #define DEFAULT_COMMUNITY "public"
41 #define DEFAULT_PORT "161"
42 #define DEFAULT_MIBLIST "ALL"
43 #define DEFAULT_PROTOCOL "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 OID_COUNT_STEP 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
66 #define L_OFFSET CHAR_MAX+4
68 /* Gobble to string - stop incrementing c when c[0] match one of the
69 * characters in s */
70 #define GOBBLE_TOS(c, s) while(c[0]!='\0' && strchr(s, c[0])==NULL) { c++; }
71 /* Given c, keep track of backslashes (bk) and double-quotes (dq)
72 * from c[0] */
73 #define COUNT_SEQ(c, bk, dq) switch(c[0]) {\
74 case '\\': \
75 if (bk) bk--; \
76 else bk++; \
77 break; \
78 case '"': \
79 if (!dq) { dq++; } \
80 else if(!bk) { dq--; } \
81 else { bk--; } \
82 break; \
87 int process_arguments (int, char **);
88 int validate_arguments (void);
89 char *thisarg (char *str);
90 char *nextarg (char *str);
91 void print_usage (void);
92 void print_help (void);
94 #include "regex.h"
95 char regex_expect[MAX_INPUT_BUFFER] = "";
96 regex_t preg;
97 regmatch_t pmatch[10];
98 char errbuf[MAX_INPUT_BUFFER] = "";
99 char perfstr[MAX_INPUT_BUFFER] = "| ";
100 int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
101 int eflags = 0;
102 int errcode, excode;
104 char *server_address = NULL;
105 char *community = NULL;
106 char **contextargs = NULL;
107 char *context = NULL;
108 char **authpriv = NULL;
109 char *proto = NULL;
110 char *seclevel = NULL;
111 char *secname = NULL;
112 char *authproto = NULL;
113 char *privproto = NULL;
114 char *authpasswd = NULL;
115 char *privpasswd = NULL;
116 char **oids = NULL;
117 size_t oids_size = 0;
118 char *label;
119 char *units;
120 char *port;
121 char *snmpcmd;
122 char string_value[MAX_INPUT_BUFFER] = "";
123 int invert_search=0;
124 char **labels = NULL;
125 char **unitv = NULL;
126 size_t nlabels = 0;
127 size_t labels_size = OID_COUNT_STEP;
128 size_t nunits = 0;
129 size_t unitv_size = OID_COUNT_STEP;
130 int numoids = 0;
131 int numauthpriv = 0;
132 int numcontext = 0;
133 int verbose = 0;
134 int usesnmpgetnext = FALSE;
135 char *warning_thresholds = NULL;
136 char *critical_thresholds = NULL;
137 thresholds **thlds;
138 size_t thlds_size = OID_COUNT_STEP;
139 double *response_value;
140 size_t response_size = OID_COUNT_STEP;
141 int retries = 0;
142 int *eval_method;
143 size_t eval_size = OID_COUNT_STEP;
144 char *delimiter;
145 char *output_delim;
146 char *miblist = NULL;
147 int needmibs = FALSE;
148 int calculate_rate = 0;
149 double offset = 0.0;
150 int rate_multiplier = 1;
151 state_data *previous_state;
152 double *previous_value;
153 size_t previous_size = OID_COUNT_STEP;
154 int perf_labels = 1;
155 char* ip_version = "";
157 static char *fix_snmp_range(char *th)
159 double left, right;
160 char *colon, *ret;
162 if ((colon = strchr(th, ':')) == NULL || *(colon + 1) == '\0')
163 return th;
165 left = strtod(th, NULL);
166 right = strtod(colon + 1, NULL);
167 if (right >= left)
168 return th;
170 if ((ret = malloc(strlen(th) + 2)) == NULL)
171 die(STATE_UNKNOWN, _("Cannot malloc"));
172 *colon = '\0';
173 sprintf(ret, "@%s:%s", colon + 1, th);
174 free(th);
175 return ret;
179 main (int argc, char **argv)
181 int i, len, line, total_oids;
182 unsigned int bk_count = 0, dq_count = 0;
183 int iresult = STATE_UNKNOWN;
184 int result = STATE_UNKNOWN;
185 int return_code = 0;
186 int external_error = 0;
187 char **command_line = NULL;
188 char *cl_hidden_auth = NULL;
189 char *oidname = NULL;
190 char *response = NULL;
191 char *mult_resp = NULL;
192 char *outbuff;
193 char *ptr = NULL;
194 char *show = NULL;
195 char *th_warn=NULL;
196 char *th_crit=NULL;
197 char type[8] = "";
198 output chld_out, chld_err;
199 char *previous_string=NULL;
200 char *ap=NULL;
201 char *state_string=NULL;
202 size_t response_length, current_length, string_length;
203 char *temp_string=NULL;
204 char *quote_string=NULL;
205 time_t current_time;
206 double temp_double;
207 time_t duration;
208 char *conv = "12345678";
209 int is_counter=0;
211 setlocale (LC_ALL, "");
212 bindtextdomain (PACKAGE, LOCALEDIR);
213 textdomain (PACKAGE);
215 labels = malloc (labels_size * sizeof(*labels));
216 unitv = malloc (unitv_size * sizeof(*unitv));
217 thlds = malloc (thlds_size * sizeof(*thlds));
218 response_value = malloc (response_size * sizeof(*response_value));
219 previous_value = malloc (previous_size * sizeof(*previous_value));
220 eval_method = calloc (eval_size, sizeof(*eval_method));
221 oids = calloc(oids_size, sizeof (char *));
223 label = strdup ("SNMP");
224 units = strdup ("");
225 port = strdup (DEFAULT_PORT);
226 outbuff = strdup ("");
227 delimiter = strdup (" = ");
228 output_delim = strdup (DEFAULT_OUTPUT_DELIMITER);
229 timeout_interval = DEFAULT_SOCKET_TIMEOUT;
230 retries = DEFAULT_RETRIES;
232 np_init( (char *) progname, argc, argv );
234 /* Parse extra opts if any */
235 argv=np_extra_opts (&argc, argv, progname);
237 np_set_args(argc, argv);
239 time(&current_time);
241 if (process_arguments (argc, argv) == ERROR)
242 usage4 (_("Could not parse arguments"));
244 if(calculate_rate) {
245 if (!strcmp(label, "SNMP"))
246 label = strdup("SNMP RATE");
247 i=0;
248 previous_state = np_state_read();
249 if(previous_state!=NULL) {
250 /* Split colon separated values */
251 previous_string = strdup((char *) previous_state->data);
252 while((ap = strsep(&previous_string, ":")) != NULL) {
253 if(verbose>2)
254 printf("State for %d=%s\n", i, ap);
255 while (i >= previous_size) {
256 previous_size += OID_COUNT_STEP;
257 previous_value = realloc(previous_value, previous_size * sizeof(*previous_value));
259 previous_value[i++]=strtod(ap,NULL);
264 /* Populate the thresholds */
265 th_warn=warning_thresholds;
266 th_crit=critical_thresholds;
267 for (i=0; i<numoids; i++) {
268 char *w = th_warn ? strndup(th_warn, strcspn(th_warn, ",")) : NULL;
269 char *c = th_crit ? strndup(th_crit, strcspn(th_crit, ",")) : NULL;
270 /* translate "2:1" to "@1:2" for backwards compatibility */
271 w = w ? fix_snmp_range(w) : NULL;
272 c = c ? fix_snmp_range(c) : NULL;
274 while (i >= thlds_size) {
275 thlds_size += OID_COUNT_STEP;
276 thlds = realloc(thlds, thlds_size * sizeof(*thlds));
279 /* Skip empty thresholds, while avoiding segfault */
280 set_thresholds(&thlds[i],
281 w ? strpbrk(w, NP_THRESHOLDS_CHARS) : NULL,
282 c ? strpbrk(c, NP_THRESHOLDS_CHARS) : NULL);
283 if (w) {
284 th_warn=strchr(th_warn, ',');
285 if (th_warn) th_warn++;
286 free(w);
288 if (c) {
289 th_crit=strchr(th_crit, ',');
290 if (th_crit) th_crit++;
291 free(c);
295 /* Create the command array to execute */
296 if(usesnmpgetnext == TRUE) {
297 snmpcmd = strdup (PATH_TO_SNMPGETNEXT);
298 }else{
299 snmpcmd = strdup (PATH_TO_SNMPGET);
302 /* 10 arguments to pass before context and authpriv options + 1 for host and numoids. Add one for terminating NULL */
303 command_line = calloc (10 + numcontext + numauthpriv + 1 + numoids + 1, sizeof (char *));
304 command_line[0] = snmpcmd;
305 command_line[1] = strdup ("-Le");
306 command_line[2] = strdup ("-t");
307 xasprintf (&command_line[3], "%d", timeout_interval);
308 command_line[4] = strdup ("-r");
309 xasprintf (&command_line[5], "%d", retries);
310 command_line[6] = strdup ("-m");
311 command_line[7] = strdup (miblist);
312 command_line[8] = "-v";
313 command_line[9] = strdup (proto);
315 for (i = 0; i < numcontext; i++) {
316 command_line[10 + i] = contextargs[i];
319 for (i = 0; i < numauthpriv; i++) {
320 command_line[10 + numcontext + i] = authpriv[i];
323 xasprintf (&command_line[10 + numcontext + numauthpriv], "%s:%s", server_address, port);
325 /* This is just for display purposes, so it can remain a string */
326 xasprintf(&cl_hidden_auth, "%s -Le -t %d -r %d -m %s -v %s %s %s %s:%s",
327 snmpcmd, timeout_interval, retries, strlen(miblist) ? miblist : "''", proto, "[context]", "[authpriv]",
328 server_address, port);
330 for (i = 0; i < numoids; i++) {
331 command_line[10 + numcontext + numauthpriv + 1 + i] = oids[i];
332 xasprintf(&cl_hidden_auth, "%s %s", cl_hidden_auth, oids[i]);
335 command_line[10 + numcontext + numauthpriv + 1 + numoids] = NULL;
337 if (verbose)
338 printf ("%s\n", cl_hidden_auth);
340 /* Set signal handling and alarm */
341 if (signal (SIGALRM, runcmd_timeout_alarm_handler) == SIG_ERR) {
342 usage4 (_("Cannot catch SIGALRM"));
344 alarm(timeout_interval * retries + 5);
346 /* Run the command */
347 return_code = cmd_run_array (command_line, &chld_out, &chld_err, 0);
349 /* disable alarm again */
350 alarm(0);
352 /* Due to net-snmp sometimes showing stderr messages with poorly formed MIBs,
353 only return state unknown if return code is non zero or there is no stdout.
354 Do this way so that if there is stderr, will get added to output, which helps problem diagnosis
356 if (return_code != 0)
357 external_error=1;
358 if (chld_out.lines == 0)
359 external_error=1;
360 if (external_error) {
361 if (chld_err.lines > 0) {
362 printf (_("External command error: %s\n"), chld_err.line[0]);
363 for (i = 1; i < chld_err.lines; i++) {
364 printf ("%s\n", chld_err.line[i]);
366 } else {
367 printf(_("External command error with no output (return code: %d)\n"), return_code);
369 exit (STATE_UNKNOWN);
372 if (verbose) {
373 for (i = 0; i < chld_out.lines; i++) {
374 printf ("%s\n", chld_out.line[i]);
378 for (line=0, i=0; line < chld_out.lines; line++, i++) {
379 if(calculate_rate)
380 conv = "%.10g";
381 else
382 conv = "%.0f";
384 ptr = chld_out.line[line];
385 oidname = strpcpy (oidname, ptr, delimiter);
386 response = strstr (ptr, delimiter);
387 if (response == NULL)
388 break;
390 if (verbose > 2) {
391 printf("Processing oid %i (line %i)\n oidname: %s\n response: %s\n", i+1, line+1, oidname, response);
394 /* Clean up type array - Sol10 does not necessarily zero it out */
395 bzero(type, sizeof(type));
397 is_counter=0;
398 /* We strip out the datatype indicator for PHBs */
399 if (strstr (response, "Gauge: ")) {
400 show = strstr (response, "Gauge: ") + 7;
402 else if (strstr (response, "Gauge32: ")) {
403 show = strstr (response, "Gauge32: ") + 9;
405 else if (strstr (response, "Counter32: ")) {
406 show = strstr (response, "Counter32: ") + 11;
407 is_counter=1;
408 if(!calculate_rate)
409 strcpy(type, "c");
411 else if (strstr (response, "Counter64: ")) {
412 show = strstr (response, "Counter64: ") + 11;
413 is_counter=1;
414 if(!calculate_rate)
415 strcpy(type, "c");
417 else if (strstr (response, "INTEGER: ")) {
418 show = strstr (response, "INTEGER: ") + 9;
420 else if (strstr (response, "OID: ")) {
421 show = strstr (response, "OID: ") + 5;
423 else if (strstr (response, "STRING: ")) {
424 show = strstr (response, "STRING: ") + 8;
425 conv = "%.10g";
427 /* Get the rest of the string on multi-line strings */
428 ptr = show;
429 COUNT_SEQ(ptr, bk_count, dq_count)
430 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
431 ptr++;
432 GOBBLE_TOS(ptr, "\n\"\\")
433 COUNT_SEQ(ptr, bk_count, dq_count)
436 if (dq_count) { /* unfinished line */
437 /* copy show verbatim first */
438 if (!mult_resp) mult_resp = strdup("");
439 xasprintf (&mult_resp, "%s%s:\n%s\n", mult_resp, oids[i], show);
440 /* then strip out unmatched double-quote from single-line output */
441 if (show[0] == '"') show++;
443 /* Keep reading until we match end of double-quoted string */
444 for (line++; line < chld_out.lines; line++) {
445 ptr = chld_out.line[line];
446 xasprintf (&mult_resp, "%s%s\n", mult_resp, ptr);
448 COUNT_SEQ(ptr, bk_count, dq_count)
449 while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
450 ptr++;
451 GOBBLE_TOS(ptr, "\n\"\\")
452 COUNT_SEQ(ptr, bk_count, dq_count)
454 /* Break for loop before next line increment when done */
455 if (!dq_count) break;
460 else if (strstr (response, "Timeticks: ")) {
461 show = strstr (response, "Timeticks: ");
463 else
464 show = response + 3;
466 iresult = STATE_DEPENDENT;
468 /* Process this block for numeric comparisons */
469 /* Make some special values,like Timeticks numeric only if a threshold is defined */
470 if (thlds[i]->warning || thlds[i]->critical || calculate_rate) {
471 ptr = strpbrk (show, "-0123456789");
472 if (ptr == NULL)
473 die (STATE_UNKNOWN,_("No valid data returned (%s)\n"), show);
474 while (i >= response_size) {
475 response_size += OID_COUNT_STEP;
476 response_value = realloc(response_value, response_size * sizeof(*response_value));
478 response_value[i] = strtod (ptr, NULL) + offset;
480 if(calculate_rate) {
481 if (previous_state!=NULL) {
482 duration = current_time-previous_state->time;
483 if(duration<=0)
484 die(STATE_UNKNOWN,_("Time duration between plugin calls is invalid"));
485 temp_double = response_value[i]-previous_value[i];
486 /* Simple overflow catcher (same as in rrdtool, rrd_update.c) */
487 if(is_counter) {
488 if(temp_double<(double)0.0)
489 temp_double+=(double)4294967296.0; /* 2^32 */
490 if(temp_double<(double)0.0)
491 temp_double+=(double)18446744069414584320.0; /* 2^64-2^32 */;
493 /* Convert to per second, then use multiplier */
494 temp_double = temp_double/duration*rate_multiplier;
495 iresult = get_status(temp_double, thlds[i]);
496 xasprintf (&show, conv, temp_double);
498 } else {
499 iresult = get_status(response_value[i], thlds[i]);
500 xasprintf (&show, conv, response_value[i]);
504 /* Process this block for string matching */
505 else if (eval_size > i && eval_method[i] & CRIT_STRING) {
506 if (strcmp (show, string_value))
507 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK;
508 else
509 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
512 /* Process this block for regex matching */
513 else if (eval_size > i && eval_method[i] & CRIT_REGEX) {
514 excode = regexec (&preg, response, 10, pmatch, eflags);
515 if (excode == 0) {
516 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
518 else if (excode != REG_NOMATCH) {
519 regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER);
520 printf (_("Execute Error: %s\n"), errbuf);
521 exit (STATE_CRITICAL);
523 else {
524 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK;
528 /* Process this block for existence-nonexistence checks */
529 /* TV: Should this be outside of this else block? */
530 else {
531 if (eval_size > i && eval_method[i] & CRIT_PRESENT)
532 iresult = STATE_CRITICAL;
533 else if (eval_size > i && eval_method[i] & WARN_PRESENT)
534 iresult = STATE_WARNING;
535 else if (response && iresult == STATE_DEPENDENT)
536 iresult = STATE_OK;
539 /* Result is the worst outcome of all the OIDs tested */
540 result = max_state (result, iresult);
542 /* Prepend a label for this OID if there is one */
543 if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
544 xasprintf (&outbuff, "%s%s%s %s%s%s", outbuff,
545 (i == 0) ? " " : output_delim,
546 labels[i], mark (iresult), show, mark (iresult));
547 else
548 xasprintf (&outbuff, "%s%s%s%s%s", outbuff, (i == 0) ? " " : output_delim,
549 mark (iresult), show, mark (iresult));
551 /* Append a unit string for this OID if there is one */
552 if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL)
553 xasprintf (&outbuff, "%s %s", outbuff, unitv[i]);
555 /* Write perfdata with whatever can be parsed by strtod, if possible */
556 ptr = NULL;
557 strtod(show, &ptr);
558 if (ptr > show) {
559 if (perf_labels && nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
560 temp_string=labels[i];
561 else
562 temp_string=oidname;
563 if (strpbrk (temp_string, " ='\"") == NULL) {
564 strncat(perfstr, temp_string, sizeof(perfstr)-strlen(perfstr)-1);
565 } else {
566 if (strpbrk (temp_string, "'") == NULL) {
567 quote_string="'";
568 } else {
569 quote_string="\"";
571 strncat(perfstr, quote_string, sizeof(perfstr)-strlen(perfstr)-1);
572 strncat(perfstr, temp_string, sizeof(perfstr)-strlen(perfstr)-1);
573 strncat(perfstr, quote_string, sizeof(perfstr)-strlen(perfstr)-1);
575 strncat(perfstr, "=", sizeof(perfstr)-strlen(perfstr)-1);
576 len = sizeof(perfstr)-strlen(perfstr)-1;
577 strncat(perfstr, show, len>ptr-show ? ptr-show : len);
579 if (warning_thresholds) {
580 strncat(perfstr, ";", sizeof(perfstr)-strlen(perfstr)-1);
581 strncat(perfstr, warning_thresholds, sizeof(perfstr)-strlen(perfstr)-1);
584 if (critical_thresholds) {
585 if (!warning_thresholds)
586 strncat(perfstr, ";", sizeof(perfstr)-strlen(perfstr)-1);
587 strncat(perfstr, ";", sizeof(perfstr)-strlen(perfstr)-1);
588 strncat(perfstr, critical_thresholds, sizeof(perfstr)-strlen(perfstr)-1);
591 if (type)
592 strncat(perfstr, type, sizeof(perfstr)-strlen(perfstr)-1);
593 strncat(perfstr, " ", sizeof(perfstr)-strlen(perfstr)-1);
596 total_oids=i;
598 /* Save state data, as all data collected now */
599 if(calculate_rate) {
600 string_length=1024;
601 state_string=malloc(string_length);
602 if(state_string==NULL)
603 die(STATE_UNKNOWN, _("Cannot malloc"));
605 current_length=0;
606 for(i=0; i<total_oids; i++) {
607 xasprintf(&temp_string,"%.0f",response_value[i]);
608 if(temp_string==NULL)
609 die(STATE_UNKNOWN,_("Cannot asprintf()"));
610 response_length = strlen(temp_string);
611 if(current_length+response_length>string_length) {
612 string_length=current_length+1024;
613 state_string=realloc(state_string,string_length);
614 if(state_string==NULL)
615 die(STATE_UNKNOWN, _("Cannot realloc()"));
617 strcpy(&state_string[current_length],temp_string);
618 current_length=current_length+response_length;
619 state_string[current_length]=':';
620 current_length++;
621 free(temp_string);
623 state_string[--current_length]='\0';
624 if (verbose > 2)
625 printf("State string=%s\n",state_string);
627 /* This is not strictly the same as time now, but any subtle variations will cancel out */
628 np_state_write_string(current_time, state_string );
629 if(previous_state==NULL) {
630 /* Or should this be highest state? */
631 die( STATE_OK, _("No previous data to calculate rate - assume okay" ) );
635 printf ("%s %s -%s %s\n", label, state_text (result), outbuff, perfstr);
636 if (mult_resp) printf ("%s", mult_resp);
638 return result;
643 /* process command-line arguments */
645 process_arguments (int argc, char **argv)
647 char *ptr;
648 int c = 1;
649 int j = 0, jj = 0, ii = 0;
651 int option = 0;
652 static struct option longopts[] = {
653 STD_LONG_OPTS,
654 {"community", required_argument, 0, 'C'},
655 {"oid", required_argument, 0, 'o'},
656 {"object", required_argument, 0, 'o'},
657 {"delimiter", required_argument, 0, 'd'},
658 {"output-delimiter", required_argument, 0, 'D'},
659 {"string", required_argument, 0, 's'},
660 {"timeout", required_argument, 0, 't'},
661 {"regex", required_argument, 0, 'r'},
662 {"ereg", required_argument, 0, 'r'},
663 {"eregi", required_argument, 0, 'R'},
664 {"label", required_argument, 0, 'l'},
665 {"units", required_argument, 0, 'u'},
666 {"port", required_argument, 0, 'p'},
667 {"retries", required_argument, 0, 'e'},
668 {"miblist", required_argument, 0, 'm'},
669 {"protocol", required_argument, 0, 'P'},
670 {"context", required_argument, 0, 'N'},
671 {"seclevel", required_argument, 0, 'L'},
672 {"secname", required_argument, 0, 'U'},
673 {"authproto", required_argument, 0, 'a'},
674 {"privproto", required_argument, 0, 'x'},
675 {"authpasswd", required_argument, 0, 'A'},
676 {"privpasswd", required_argument, 0, 'X'},
677 {"next", no_argument, 0, 'n'},
678 {"rate", no_argument, 0, L_CALCULATE_RATE},
679 {"rate-multiplier", required_argument, 0, L_RATE_MULTIPLIER},
680 {"offset", required_argument, 0, L_OFFSET},
681 {"invert-search", no_argument, 0, L_INVERT_SEARCH},
682 {"perf-oids", no_argument, 0, 'O'},
683 {"ipv4", no_argument, 0, '4'},
684 {"ipv6", no_argument, 0, '6'},
685 {0, 0, 0, 0}
688 if (argc < 2)
689 return ERROR;
691 /* reverse compatibility for very old non-POSIX usage forms */
692 for (c = 1; c < argc; c++) {
693 if (strcmp ("-to", argv[c]) == 0)
694 strcpy (argv[c], "-t");
695 if (strcmp ("-wv", argv[c]) == 0)
696 strcpy (argv[c], "-w");
697 if (strcmp ("-cv", argv[c]) == 0)
698 strcpy (argv[c], "-c");
701 while (1) {
702 c = getopt_long (argc, argv, "nhvVO46t:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:N:L:U:a:x:A:X:",
703 longopts, &option);
705 if (c == -1 || c == EOF)
706 break;
708 switch (c) {
709 case '?': /* usage */
710 usage5 ();
711 case 'h': /* help */
712 print_help ();
713 exit (STATE_UNKNOWN);
714 case 'V': /* version */
715 print_revision (progname, NP_VERSION);
716 exit (STATE_UNKNOWN);
717 case 'v': /* verbose */
718 verbose++;
719 break;
721 /* Connection info */
722 case 'C': /* group or community */
723 community = optarg;
724 break;
725 case 'H': /* Host or server */
726 server_address = optarg;
727 break;
728 case 'p': /* TCP port number */
729 port = optarg;
730 break;
731 case 'm': /* List of MIBS */
732 miblist = optarg;
733 break;
734 case 'n': /* usesnmpgetnext */
735 usesnmpgetnext = TRUE;
736 break;
737 case 'P': /* SNMP protocol version */
738 proto = optarg;
739 break;
740 case 'N': /* SNMPv3 context */
741 context = optarg;
742 break;
743 case 'L': /* security level */
744 seclevel = optarg;
745 break;
746 case 'U': /* security username */
747 secname = optarg;
748 break;
749 case 'a': /* auth protocol */
750 authproto = optarg;
751 break;
752 case 'x': /* priv protocol */
753 privproto = optarg;
754 break;
755 case 'A': /* auth passwd */
756 authpasswd = optarg;
757 break;
758 case 'X': /* priv passwd */
759 privpasswd = optarg;
760 break;
761 case 't': /* timeout period */
762 if (!is_integer (optarg))
763 usage2 (_("Timeout interval must be a positive integer"), optarg);
764 else
765 timeout_interval = atoi (optarg);
766 break;
768 /* Test parameters */
769 case 'c': /* critical threshold */
770 critical_thresholds = optarg;
771 break;
772 case 'w': /* warning threshold */
773 warning_thresholds = optarg;
774 break;
775 case 'e': /* PRELIMINARY - may change */
776 case 'E': /* PRELIMINARY - may change */
777 if (!is_integer (optarg))
778 usage2 (_("Retries interval must be a positive integer"), optarg);
779 else
780 retries = atoi(optarg);
781 break;
782 case 'o': /* object identifier */
783 if ( strspn( optarg, "0123456789.," ) != strlen( optarg ) ) {
785 * we have something other than digits, periods and comas,
786 * so we have a mib variable, rather than just an SNMP OID,
787 * so we have to actually read the mib files
789 needmibs = TRUE;
791 for (ptr = strtok(optarg, ", "); ptr != NULL; ptr = strtok(NULL, ", "), j++) {
792 while (j >= oids_size) {
793 oids_size += OID_COUNT_STEP;
794 oids = realloc(oids, oids_size * sizeof (*oids));
796 oids[j] = strdup(ptr);
798 numoids = j;
799 if (c == 'E' || c == 'e') {
800 jj++;
801 ii++;
802 while (j+1 >= eval_size) {
803 eval_size += OID_COUNT_STEP;
804 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method));
805 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8);
807 if (c == 'E')
808 eval_method[j+1] |= WARN_PRESENT;
809 else if (c == 'e')
810 eval_method[j+1] |= CRIT_PRESENT;
812 break;
813 case 's': /* string or substring */
814 strncpy (string_value, optarg, sizeof (string_value) - 1);
815 string_value[sizeof (string_value) - 1] = 0;
816 while (jj >= eval_size) {
817 eval_size += OID_COUNT_STEP;
818 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method));
819 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8);
821 eval_method[jj++] = CRIT_STRING;
822 ii++;
823 break;
824 case 'R': /* regex */
825 cflags = REG_ICASE;
826 case 'r': /* regex */
827 cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
828 strncpy (regex_expect, optarg, sizeof (regex_expect) - 1);
829 regex_expect[sizeof (regex_expect) - 1] = 0;
830 errcode = regcomp (&preg, regex_expect, cflags);
831 if (errcode != 0) {
832 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
833 printf (_("Could Not Compile Regular Expression"));
834 return ERROR;
836 while (jj >= eval_size) {
837 eval_size += OID_COUNT_STEP;
838 eval_method = realloc(eval_method, eval_size * sizeof(*eval_method));
839 memset(eval_method + eval_size - OID_COUNT_STEP, 0, 8);
841 eval_method[jj++] = CRIT_REGEX;
842 ii++;
843 break;
845 /* Format */
846 case 'd': /* delimiter */
847 delimiter = strscpy (delimiter, optarg);
848 break;
849 case 'D': /* output-delimiter */
850 output_delim = strscpy (output_delim, optarg);
851 break;
852 case 'l': /* label */
853 nlabels++;
854 if (nlabels > labels_size) {
855 labels_size += 8;
856 labels = realloc (labels, labels_size * sizeof(*labels));
857 if (labels == NULL)
858 die (STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels);
860 labels[nlabels - 1] = optarg;
861 ptr = thisarg (optarg);
862 labels[nlabels - 1] = ptr;
863 if (ptr[0] == '\'')
864 labels[nlabels - 1] = ptr + 1;
865 while (ptr && (ptr = nextarg (ptr))) {
866 nlabels++;
867 if (nlabels > labels_size) {
868 labels_size += 8;
869 labels = realloc (labels, labels_size * sizeof(*labels));
870 if (labels == NULL)
871 die (STATE_UNKNOWN, _("Could not reallocate labels\n"));
873 ptr = thisarg (ptr);
874 if (ptr[0] == '\'')
875 labels[nlabels - 1] = ptr + 1;
876 else
877 labels[nlabels - 1] = ptr;
879 break;
880 case 'u': /* units */
881 units = optarg;
882 nunits++;
883 if (nunits > unitv_size) {
884 unitv_size += 8;
885 unitv = realloc (unitv, unitv_size * sizeof(*unitv));
886 if (unitv == NULL)
887 die (STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits);
889 unitv[nunits - 1] = optarg;
890 ptr = thisarg (optarg);
891 unitv[nunits - 1] = ptr;
892 if (ptr[0] == '\'')
893 unitv[nunits - 1] = ptr + 1;
894 while (ptr && (ptr = nextarg (ptr))) {
895 if (nunits > unitv_size) {
896 unitv_size += 8;
897 unitv = realloc (unitv, unitv_size * sizeof(*unitv));
898 if (units == NULL)
899 die (STATE_UNKNOWN, _("Could not realloc() units\n"));
901 nunits++;
902 ptr = thisarg (ptr);
903 if (ptr[0] == '\'')
904 unitv[nunits - 1] = ptr + 1;
905 else
906 unitv[nunits - 1] = ptr;
908 break;
909 case L_CALCULATE_RATE:
910 if(calculate_rate==0)
911 np_enable_state(NULL, 1);
912 calculate_rate = 1;
913 break;
914 case L_RATE_MULTIPLIER:
915 if(!is_integer(optarg)||((rate_multiplier=atoi(optarg))<=0))
916 usage2(_("Rate multiplier must be a positive integer"),optarg);
917 break;
918 case L_OFFSET:
919 offset=strtod(optarg,NULL);
920 break;
921 case L_INVERT_SEARCH:
922 invert_search=1;
923 break;
924 case 'O':
925 perf_labels=0;
926 break;
927 case '4':
928 break;
929 case '6':
930 xasprintf(&ip_version, "udp6:");
931 if(verbose>2)
932 printf("IPv6 detected! Will pass \"udp6:\" to snmpget.\n");
933 break;
937 if (server_address == NULL)
938 server_address = argv[optind];
940 if (community == NULL)
941 community = strdup (DEFAULT_COMMUNITY);
943 return validate_arguments ();
947 /******************************************************************************
950 <sect3>
951 <title>validate_arguments</title>
953 <para>&PROTO_validate_arguments;</para>
955 <para>Checks to see if the default miblist needs to be loaded. Also verifies
956 the authentication and authorization combinations based on protocol version
957 selected.</para>
959 <para></para>
961 </sect3>
963 ******************************************************************************/
968 validate_arguments ()
970 /* check whether to load locally installed MIBS (CPU/disk intensive) */
971 if (miblist == NULL) {
972 if ( needmibs == TRUE ) {
973 miblist = strdup (DEFAULT_MIBLIST);
974 }else{
975 miblist = ""; /* don't read any mib files for numeric oids */
979 /* Check server_address is given */
980 if (server_address == NULL)
981 die(STATE_UNKNOWN, _("No host specified\n"));
983 /* Check oid is given */
984 if (numoids == 0)
985 die(STATE_UNKNOWN, _("No OIDs specified\n"));
987 if (proto == NULL)
988 xasprintf(&proto, DEFAULT_PROTOCOL);
990 if ((strcmp(proto,"1") == 0) || (strcmp(proto, "2c")==0)) { /* snmpv1 or snmpv2c */
991 numauthpriv = 2;
992 authpriv = calloc (numauthpriv, sizeof (char *));
993 authpriv[0] = strdup ("-c");
994 authpriv[1] = strdup (community);
996 else if ( strcmp (proto, "3") == 0 ) { /* snmpv3 args */
997 if (!(context == NULL)) {
998 numcontext = 2;
999 contextargs = calloc (numcontext, sizeof (char *));
1000 contextargs[0] = strdup ("-n");
1001 contextargs[1] = strdup (context);
1004 if (seclevel == NULL)
1005 xasprintf(&seclevel, "noAuthNoPriv");
1007 if (secname == NULL)
1008 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname");
1010 if (strcmp(seclevel, "noAuthNoPriv") == 0) {
1011 numauthpriv = 4;
1012 authpriv = calloc (numauthpriv, sizeof (char *));
1013 authpriv[0] = strdup ("-l");
1014 authpriv[1] = strdup ("noAuthNoPriv");
1015 authpriv[2] = strdup ("-u");
1016 authpriv[3] = strdup (secname);
1017 } else {
1018 if (! ( (strcmp(seclevel, "authNoPriv")==0) || (strcmp(seclevel, "authPriv")==0) ) ) {
1019 usage2 (_("Invalid seclevel"), seclevel);
1022 if (authproto == NULL )
1023 xasprintf(&authproto, DEFAULT_AUTH_PROTOCOL);
1025 if (authpasswd == NULL)
1026 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd");
1028 if ( strcmp(seclevel, "authNoPriv") == 0 ) {
1029 numauthpriv = 8;
1030 authpriv = calloc (numauthpriv, sizeof (char *));
1031 authpriv[0] = strdup ("-l");
1032 authpriv[1] = strdup ("authNoPriv");
1033 authpriv[2] = strdup ("-a");
1034 authpriv[3] = strdup (authproto);
1035 authpriv[4] = strdup ("-u");
1036 authpriv[5] = strdup (secname);
1037 authpriv[6] = strdup ("-A");
1038 authpriv[7] = strdup (authpasswd);
1039 } else if ( strcmp(seclevel, "authPriv") == 0 ) {
1040 if (privproto == NULL )
1041 xasprintf(&privproto, DEFAULT_PRIV_PROTOCOL);
1043 if (privpasswd == NULL)
1044 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd");
1046 numauthpriv = 12;
1047 authpriv = calloc (numauthpriv, sizeof (char *));
1048 authpriv[0] = strdup ("-l");
1049 authpriv[1] = strdup ("authPriv");
1050 authpriv[2] = strdup ("-a");
1051 authpriv[3] = strdup (authproto);
1052 authpriv[4] = strdup ("-u");
1053 authpriv[5] = strdup (secname);
1054 authpriv[6] = strdup ("-A");
1055 authpriv[7] = strdup (authpasswd);
1056 authpriv[8] = strdup ("-x");
1057 authpriv[9] = strdup (privproto);
1058 authpriv[10] = strdup ("-X");
1059 authpriv[11] = strdup (privpasswd);
1064 else {
1065 usage2 (_("Invalid SNMP version"), proto);
1068 return OK;
1073 /* trim leading whitespace
1074 if there is a leading quote, make sure it balances */
1076 char *
1077 thisarg (char *str)
1079 str += strspn (str, " \t\r\n"); /* trim any leading whitespace */
1080 if (str[0] == '\'') { /* handle SIMPLE quoted strings */
1081 if (strlen (str) == 1 || !strstr (str + 1, "'"))
1082 die (STATE_UNKNOWN, _("Unbalanced quotes\n"));
1084 return str;
1089 /* if there's a leading quote, advance to the trailing quote
1090 set the trailing quote to '\x0'
1091 if the string continues, advance beyond the comma */
1093 char *
1094 nextarg (char *str)
1096 if (str[0] == '\'') {
1097 str[0] = 0;
1098 if (strlen (str) > 1) {
1099 str = strstr (str + 1, "'");
1100 return (++str);
1102 else {
1103 return NULL;
1106 if (str[0] == ',') {
1107 str[0] = 0;
1108 if (strlen (str) > 1) {
1109 return (++str);
1111 else {
1112 return NULL;
1115 if ((str = strstr (str, ",")) && strlen (str) > 1) {
1116 str[0] = 0;
1117 return (++str);
1119 return NULL;
1124 void
1125 print_help (void)
1127 print_revision (progname, NP_VERSION);
1129 printf (COPYRIGHT, copyright, email);
1131 printf ("%s\n", _("Check status of remote machines and obtain system information via SNMP"));
1133 printf ("\n\n");
1135 print_usage ();
1137 printf (UT_HELP_VRSN);
1138 printf (UT_EXTRA_OPTS);
1139 printf (UT_IPv46);
1141 printf (UT_HOST_PORT, 'p', DEFAULT_PORT);
1143 /* SNMP and Authentication Protocol */
1144 printf (" %s\n", "-n, --next");
1145 printf (" %s\n", _("Use SNMP GETNEXT instead of SNMP GET"));
1146 printf (" %s\n", "-P, --protocol=[1|2c|3]");
1147 printf (" %s\n", _("SNMP protocol version"));
1148 printf (" %s\n", "-N, --context=CONTEXT");
1149 printf (" %s\n", _("SNMPv3 context"));
1150 printf (" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]");
1151 printf (" %s\n", _("SNMPv3 securityLevel"));
1152 printf (" %s\n", "-a, --authproto=[MD5|SHA]");
1153 printf (" %s\n", _("SNMPv3 auth proto"));
1154 printf (" %s\n", "-x, --privproto=[DES|AES]");
1155 printf (" %s\n", _("SNMPv3 priv proto (default DES)"));
1157 /* Authentication Tokens*/
1158 printf (" %s\n", "-C, --community=STRING");
1159 printf (" %s ", _("Optional community string for SNMP communication"));
1160 printf ("(%s \"%s\")\n", _("default is") ,DEFAULT_COMMUNITY);
1161 printf (" %s\n", "-U, --secname=USERNAME");
1162 printf (" %s\n", _("SNMPv3 username"));
1163 printf (" %s\n", "-A, --authpassword=PASSWORD");
1164 printf (" %s\n", _("SNMPv3 authentication password"));
1165 printf (" %s\n", "-X, --privpasswd=PASSWORD");
1166 printf (" %s\n", _("SNMPv3 privacy password"));
1168 /* OID Stuff */
1169 printf (" %s\n", "-o, --oid=OID(s)");
1170 printf (" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query"));
1171 printf (" %s\n", "-m, --miblist=STRING");
1172 printf (" %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'"));
1173 printf (" %s\n", _("for symbolic OIDs.)"));
1174 printf (" %s\n", "-d, --delimiter=STRING");
1175 printf (" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER);
1176 printf (" %s\n", _("Any data on the right hand side of the delimiter is considered"));
1177 printf (" %s\n", _("to be the data that should be used in the evaluation."));
1179 /* Tests Against Integers */
1180 printf (" %s\n", "-w, --warning=THRESHOLD(s)");
1181 printf (" %s\n", _("Warning threshold range(s)"));
1182 printf (" %s\n", "-c, --critical=THRESHOLD(s)");
1183 printf (" %s\n", _("Critical threshold range(s)"));
1184 printf (" %s\n", "--rate");
1185 printf (" %s\n", _("Enable rate calculation. See 'Rate Calculation' below"));
1186 printf (" %s\n", "--rate-multiplier");
1187 printf (" %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute"));
1188 printf (" %s\n", "--offset=OFFSET");
1189 printf (" %s\n", _("Add/substract the specified OFFSET to numeric sensor data"));
1191 /* Tests Against Strings */
1192 printf (" %s\n", "-s, --string=STRING");
1193 printf (" %s\n", _("Return OK state (for that OID) if STRING is an exact match"));
1194 printf (" %s\n", "-r, --ereg=REGEX");
1195 printf (" %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches"));
1196 printf (" %s\n", "-R, --eregi=REGEX");
1197 printf (" %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches"));
1198 printf (" %s\n", "--invert-search");
1199 printf (" %s\n", _("Invert search result (CRITICAL if found)"));
1201 /* Output Formatting */
1202 printf (" %s\n", "-l, --label=STRING");
1203 printf (" %s\n", _("Prefix label for output from plugin"));
1204 printf (" %s\n", "-u, --units=STRING");
1205 printf (" %s\n", _("Units label(s) for output data (e.g., 'sec.')."));
1206 printf (" %s\n", "-D, --output-delimiter=STRING");
1207 printf (" %s\n", _("Separates output on multiple OID requests"));
1209 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1210 printf (" %s\n", "-e, --retries=INTEGER");
1211 printf (" %s\n", _("Number of retries to be used in the requests"));
1213 printf (" %s\n", "-O, --perf-oids");
1214 printf (" %s\n", _("Label performance data with OIDs instead of --label's"));
1216 printf (UT_VERBOSE);
1218 printf ("\n");
1219 printf ("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package."));
1220 printf ("%s\n", _("if you don't have the package installed, you will need to download it from"));
1221 printf ("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1223 printf ("\n");
1224 printf ("%s\n", _("Notes:"));
1225 printf (" %s\n", _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited "));
1226 printf (" %s\n", _("list (lists with internal spaces must be quoted)."));
1228 printf(" -%s", UT_THRESHOLDS_NOTES);
1230 printf (" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'"));
1231 printf (" %s\n", _("- Note that only one string and one regex may be checked at present"));
1232 printf (" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value"));
1233 printf (" %s\n", _("returned from the SNMP query is an unsigned integer."));
1235 printf("\n");
1236 printf("%s\n", _("Rate Calculation:"));
1237 printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when"));
1238 printf(" %s\n", _("calculating the counter difference since the last check. check_snmp"));
1239 printf(" %s\n", _("saves the last state information in a file so that the rate per second"));
1240 printf(" %s\n", _("can be calculated. Use the --rate option to save state information."));
1241 printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK."));
1242 printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so"));
1243 printf(" %s\n", _("changing the arguments will create a new state file."));
1245 printf (UT_SUPPORT);
1250 void
1251 print_usage (void)
1253 printf ("%s\n", _("Usage:"));
1254 printf ("%s -H <ip_address> -o <OID> [-w warn_range] [-c crit_range]\n",progname);
1255 printf ("[-C community] [-s string] [-r regex] [-R regexi] [-t timeout] [-e retries]\n");
1256 printf ("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n");
1257 printf ("[-m miblist] [-P snmp version] [-N context] [-L seclevel] [-U secname]\n");
1258 printf ("[-a authproto] [-A authpasswd] [-x privproto] [-X privpasswd] [-4|6]\n");