1 /*****************************************************************************
3 * Monitoring check_snmp plugin
6 * Copyright (c) 1999-2024 Monitoring Plugins Development Team
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-2024";
33 const char *email
= "devel@monitoring-plugins.org";
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 " "
49 #define DEFAULT_BUFFER_SIZE 100
51 #define mark(a) ((a) != 0 ? "*" : "")
54 #define CRIT_PRESENT 1
57 #define WARN_PRESENT 8
59 #define OID_COUNT_STEP 8
61 /* Longopts only arguments */
62 #define L_CALCULATE_RATE CHAR_MAX + 1
63 #define L_RATE_MULTIPLIER CHAR_MAX + 2
64 #define L_INVERT_SEARCH CHAR_MAX + 3
65 #define L_OFFSET CHAR_MAX + 4
66 #define L_IGNORE_MIB_PARSING_ERRORS CHAR_MAX + 5
68 /* Gobble to string - stop incrementing c when c[0] match one of the
70 #define GOBBLE_TOS(c, s) \
71 while (c[0] != '\0' && strchr(s, c[0]) == NULL) { \
74 /* Given c, keep track of backslashes (bk) and double-quotes (dq)
76 #define COUNT_SEQ(c, bk, dq) \
95 static int process_arguments(int, char **);
96 static int validate_arguments(void);
97 static char *thisarg(char *str
);
98 static char *nextarg(char *str
);
99 void print_usage(void);
100 static void print_help(void);
101 static char *multiply(char *str
);
104 static char regex_expect
[MAX_INPUT_BUFFER
] = "";
106 static regmatch_t pmatch
[10];
107 static char errbuf
[MAX_INPUT_BUFFER
] = "";
108 static char perfstr
[MAX_INPUT_BUFFER
] = "| ";
109 static int cflags
= REG_EXTENDED
| REG_NOSUB
| REG_NEWLINE
;
110 static int eflags
= 0;
111 static int errcode
, excode
;
113 static char *server_address
= NULL
;
114 static char *community
= NULL
;
115 static char **contextargs
= NULL
;
116 static char *context
= NULL
;
117 static char **authpriv
= NULL
;
118 static char *proto
= NULL
;
119 static char *seclevel
= NULL
;
120 static char *secname
= NULL
;
121 static char *authproto
= NULL
;
122 static char *privproto
= NULL
;
123 static char *authpasswd
= NULL
;
124 static char *privpasswd
= NULL
;
125 static int nulloid
= STATE_UNKNOWN
;
126 static char **oids
= NULL
;
127 static size_t oids_size
= 0;
131 static char *snmpcmd
;
132 static char string_value
[MAX_INPUT_BUFFER
] = "";
133 static int invert_search
= 0;
134 static char **labels
= NULL
;
135 static char **unitv
= NULL
;
136 static size_t nlabels
= 0;
137 static size_t labels_size
= OID_COUNT_STEP
;
138 static size_t nunits
= 0;
139 static size_t unitv_size
= OID_COUNT_STEP
;
140 static size_t numoids
= 0;
141 static int numauthpriv
= 0;
142 static int numcontext
= 0;
143 static int verbose
= 0;
144 static bool usesnmpgetnext
= false;
145 static char *warning_thresholds
= NULL
;
146 static char *critical_thresholds
= NULL
;
147 static thresholds
**thlds
;
148 static size_t thlds_size
= OID_COUNT_STEP
;
149 static double *response_value
;
150 static size_t response_size
= OID_COUNT_STEP
;
151 static int retries
= 0;
152 static int *eval_method
;
153 static size_t eval_size
= OID_COUNT_STEP
;
154 static char *delimiter
;
155 static char *output_delim
;
156 static char *miblist
= NULL
;
157 static bool needmibs
= false;
158 static int calculate_rate
= 0;
159 static double offset
= 0.0;
160 static int rate_multiplier
= 1;
161 static state_data
*previous_state
;
162 static double *previous_value
;
163 static size_t previous_size
= OID_COUNT_STEP
;
164 static int perf_labels
= 1;
165 static char *ip_version
= "";
166 static double multiplier
= 1.0;
167 static char *fmtstr
= "";
168 static bool fmtstr_set
= false;
169 static char buffer
[DEFAULT_BUFFER_SIZE
];
170 static bool ignore_mib_parsing_errors
= false;
172 static char *fix_snmp_range(char *th
) {
178 if ((colon
= strchr(th
, ':')) == NULL
|| *(colon
+ 1) == '\0')
181 left
= strtod(th
, NULL
);
182 right
= strtod(colon
+ 1, NULL
);
186 if ((ret
= malloc(strlen(th
) + 2)) == NULL
)
187 die(STATE_UNKNOWN
, _("Cannot malloc"));
189 sprintf(ret
, "@%s:%s", colon
+ 1, th
);
194 int main(int argc
, char **argv
) {
198 unsigned int bk_count
= 0;
199 unsigned int dq_count
= 0;
200 int iresult
= STATE_UNKNOWN
;
201 int result
= STATE_UNKNOWN
;
203 int external_error
= 0;
204 char **command_line
= NULL
;
205 char *cl_hidden_auth
= NULL
;
206 char *oidname
= NULL
;
207 char *response
= NULL
;
208 char *mult_resp
= NULL
;
212 char *th_warn
= NULL
;
213 char *th_crit
= NULL
;
217 char *previous_string
= NULL
;
219 char *state_string
= NULL
;
220 size_t response_length
;
221 size_t current_length
;
222 size_t string_length
;
223 char *temp_string
= NULL
;
224 char *quote_string
= NULL
;
228 char *conv
= "12345678";
231 setlocale(LC_ALL
, "");
232 bindtextdomain(PACKAGE
, LOCALEDIR
);
235 labels
= malloc(labels_size
* sizeof(*labels
));
236 unitv
= malloc(unitv_size
* sizeof(*unitv
));
237 thlds
= malloc(thlds_size
* sizeof(*thlds
));
238 response_value
= malloc(response_size
* sizeof(*response_value
));
239 previous_value
= malloc(previous_size
* sizeof(*previous_value
));
240 eval_method
= calloc(eval_size
, sizeof(*eval_method
));
241 oids
= calloc(oids_size
, sizeof(char *));
243 label
= strdup("SNMP");
245 port
= strdup(DEFAULT_PORT
);
246 outbuff
= strdup("");
247 delimiter
= strdup(" = ");
248 output_delim
= strdup(DEFAULT_OUTPUT_DELIMITER
);
249 timeout_interval
= DEFAULT_SOCKET_TIMEOUT
;
250 retries
= DEFAULT_RETRIES
;
252 np_init((char *)progname
, argc
, argv
);
254 /* Parse extra opts if any */
255 argv
= np_extra_opts(&argc
, argv
, progname
);
257 np_set_args(argc
, argv
);
261 if (process_arguments(argc
, argv
) == ERROR
)
262 usage4(_("Could not parse arguments"));
264 if (calculate_rate
) {
265 if (!strcmp(label
, "SNMP"))
266 label
= strdup("SNMP RATE");
270 previous_state
= np_state_read();
271 if (previous_state
!= NULL
) {
272 /* Split colon separated values */
273 previous_string
= strdup((char *)previous_state
->data
);
274 while ((ap
= strsep(&previous_string
, ":")) != NULL
) {
276 printf("State for %zd=%s\n", i
, ap
);
277 while (i
>= previous_size
) {
278 previous_size
+= OID_COUNT_STEP
;
279 previous_value
= realloc(previous_value
, previous_size
* sizeof(*previous_value
));
281 previous_value
[i
++] = strtod(ap
, NULL
);
286 /* Populate the thresholds */
287 th_warn
= warning_thresholds
;
288 th_crit
= critical_thresholds
;
289 for (size_t i
= 0; i
< numoids
; i
++) {
290 char *w
= th_warn
? strndup(th_warn
, strcspn(th_warn
, ",")) : NULL
;
291 char *c
= th_crit
? strndup(th_crit
, strcspn(th_crit
, ",")) : NULL
;
292 /* translate "2:1" to "@1:2" for backwards compatibility */
293 w
= w
? fix_snmp_range(w
) : NULL
;
294 c
= c
? fix_snmp_range(c
) : NULL
;
296 while (i
>= thlds_size
) {
297 thlds_size
+= OID_COUNT_STEP
;
298 thlds
= realloc(thlds
, thlds_size
* sizeof(*thlds
));
301 /* Skip empty thresholds, while avoiding segfault */
302 set_thresholds(&thlds
[i
], w
? strpbrk(w
, NP_THRESHOLDS_CHARS
) : NULL
, c
? strpbrk(c
, NP_THRESHOLDS_CHARS
) : NULL
);
304 th_warn
= strchr(th_warn
, ',');
310 th_crit
= strchr(th_crit
, ',');
317 /* Create the command array to execute */
318 if (usesnmpgetnext
) {
319 snmpcmd
= strdup(PATH_TO_SNMPGETNEXT
);
321 snmpcmd
= strdup(PATH_TO_SNMPGET
);
324 /* 10 arguments to pass before context and authpriv options + 1 for host and numoids. Add one for terminating NULL */
327 command_line
= calloc(11 + numcontext
+ numauthpriv
+ 1 + numoids
+ 1, sizeof(char *));
329 command_line
[index
++] = snmpcmd
;
330 command_line
[index
++] = strdup("-Le");
331 command_line
[index
++] = strdup("-t");
332 xasprintf(&command_line
[index
++], "%d", timeout_interval
);
333 command_line
[index
++] = strdup("-r");
334 xasprintf(&command_line
[index
++], "%d", retries
);
335 command_line
[index
++] = strdup("-m");
336 command_line
[index
++] = strdup(miblist
);
337 command_line
[index
++] = "-v";
338 command_line
[index
++] = strdup(proto
);
340 xasprintf(&cl_hidden_auth
, "%s -Le -t %d -r %d -m %s -v %s", snmpcmd
, timeout_interval
, retries
, strlen(miblist
) ? miblist
: "''",
343 if (ignore_mib_parsing_errors
) {
344 command_line
[index
++] = "-Pe";
345 xasprintf(&cl_hidden_auth
, "%s -Pe", cl_hidden_auth
);
348 for (int i
= 0; i
< numcontext
; i
++) {
349 command_line
[index
++] = contextargs
[i
];
352 for (int i
= 0; i
< numauthpriv
; i
++) {
353 command_line
[index
++] = authpriv
[i
];
356 xasprintf(&command_line
[index
++], "%s:%s", server_address
, port
);
358 xasprintf(&cl_hidden_auth
, "%s [context] [authpriv] %s:%s", cl_hidden_auth
, server_address
, port
);
360 for (size_t i
= 0; i
< numoids
; i
++) {
361 command_line
[index
++] = oids
[i
];
362 xasprintf(&cl_hidden_auth
, "%s %s", cl_hidden_auth
, oids
[i
]);
365 command_line
[index
++] = NULL
;
368 printf("%s\n", cl_hidden_auth
);
371 /* Set signal handling and alarm */
372 if (signal(SIGALRM
, runcmd_timeout_alarm_handler
) == SIG_ERR
) {
373 usage4(_("Cannot catch SIGALRM"));
375 alarm(timeout_interval
* retries
+ 5);
377 /* Run the command */
378 return_code
= cmd_run_array(command_line
, &chld_out
, &chld_err
, 0);
380 /* disable alarm again */
383 /* Due to net-snmp sometimes showing stderr messages with poorly formed MIBs,
384 only return state unknown if return code is non zero or there is no stdout.
385 Do this way so that if there is stderr, will get added to output, which helps problem diagnosis
387 if (return_code
!= 0)
389 if (chld_out
.lines
== 0)
391 if (external_error
) {
392 if (chld_err
.lines
> 0) {
393 printf(_("External command error: %s\n"), chld_err
.line
[0]);
394 for (size_t i
= 1; i
< chld_err
.lines
; i
++) {
395 printf("%s\n", chld_err
.line
[i
]);
398 printf(_("External command error with no output (return code: %d)\n"), return_code
);
404 for (size_t i
= 0; i
< chld_out
.lines
; i
++) {
405 printf("%s\n", chld_out
.line
[i
]);
411 for (size_t i
= 0; line
< chld_out
.lines
&& i
< numoids
; line
++, i
++, total_oids
++) {
417 ptr
= chld_out
.line
[line
];
418 oidname
= strpcpy(oidname
, ptr
, delimiter
);
419 response
= strstr(ptr
, delimiter
);
420 if (response
== NULL
)
424 printf("Processing oid %zi (line %zi)\n oidname: %s\n response: %s\n", i
+ 1, line
+ 1, oidname
, response
);
427 /* Clean up type array - Sol10 does not necessarily zero it out */
428 bzero(type
, sizeof(type
));
431 /* We strip out the datatype indicator for PHBs */
432 if (strstr(response
, "Gauge: ")) {
433 show
= multiply(strstr(response
, "Gauge: ") + 7);
434 } else if (strstr(response
, "Gauge32: ")) {
435 show
= multiply(strstr(response
, "Gauge32: ") + 9);
436 } else if (strstr(response
, "Counter32: ")) {
437 show
= strstr(response
, "Counter32: ") + 11;
441 } else if (strstr(response
, "Counter64: ")) {
442 show
= strstr(response
, "Counter64: ") + 11;
446 } else if (strstr(response
, "INTEGER: ")) {
447 show
= multiply(strstr(response
, "INTEGER: ") + 9);
452 } else if (strstr(response
, "OID: ")) {
453 show
= strstr(response
, "OID: ") + 5;
454 } else if (strstr(response
, "STRING: ")) {
455 show
= strstr(response
, "STRING: ") + 8;
458 /* Get the rest of the string on multi-line strings */
460 COUNT_SEQ(ptr
, bk_count
, dq_count
)
461 while (dq_count
&& ptr
[0] != '\n' && ptr
[0] != '\0') {
463 GOBBLE_TOS(ptr
, "\n\"\\")
464 COUNT_SEQ(ptr
, bk_count
, dq_count
)
467 if (dq_count
) { /* unfinished line */
468 /* copy show verbatim first */
470 mult_resp
= strdup("");
471 xasprintf(&mult_resp
, "%s%s:\n%s\n", mult_resp
, oids
[i
], show
);
472 /* then strip out unmatched double-quote from single-line output */
476 /* Keep reading until we match end of double-quoted string */
477 for (line
++; line
< chld_out
.lines
; line
++) {
478 ptr
= chld_out
.line
[line
];
479 xasprintf(&mult_resp
, "%s%s\n", mult_resp
, ptr
);
481 COUNT_SEQ(ptr
, bk_count
, dq_count
)
482 while (dq_count
&& ptr
[0] != '\n' && ptr
[0] != '\0') {
484 GOBBLE_TOS(ptr
, "\n\"\\")
485 COUNT_SEQ(ptr
, bk_count
, dq_count
)
487 /* Break for loop before next line increment when done */
493 } else if (strstr(response
, "Timeticks: ")) {
494 show
= strstr(response
, "Timeticks: ");
498 iresult
= STATE_DEPENDENT
;
500 /* Process this block for numeric comparisons */
501 /* Make some special values,like Timeticks numeric only if a threshold is defined */
502 if (thlds
[i
]->warning
|| thlds
[i
]->critical
|| calculate_rate
) {
504 print_thresholds(" thresholds", thlds
[i
]);
506 ptr
= strpbrk(show
, "-0123456789");
509 die(STATE_UNKNOWN
, _("No valid data returned (%s)\n"), show
);
510 else if (nulloid
== 0)
511 die(STATE_OK
, _("No valid data returned (%s)\n"), show
);
512 else if (nulloid
== 1)
513 die(STATE_WARNING
, _("No valid data returned (%s)\n"), show
);
514 else if (nulloid
== 2)
515 die(STATE_CRITICAL
, _("No valid data returned (%s)\n"), show
);
517 while (i
>= response_size
) {
518 response_size
+= OID_COUNT_STEP
;
519 response_value
= realloc(response_value
, response_size
* sizeof(*response_value
));
521 response_value
[i
] = strtod(ptr
, NULL
) + offset
;
523 if (calculate_rate
) {
524 if (previous_state
!= NULL
) {
525 duration
= current_time
- previous_state
->time
;
527 die(STATE_UNKNOWN
, _("Time duration between plugin calls is invalid"));
528 temp_double
= response_value
[i
] - previous_value
[i
];
529 /* Simple overflow catcher (same as in rrdtool, rrd_update.c) */
531 if (temp_double
< (double)0.0)
532 temp_double
+= (double)4294967296.0; /* 2^32 */
533 if (temp_double
< (double)0.0)
534 temp_double
+= (double)18446744069414584320.0; /* 2^64-2^32 */
537 /* Convert to per second, then use multiplier */
538 temp_double
= temp_double
/ duration
* rate_multiplier
;
539 iresult
= get_status(temp_double
, thlds
[i
]);
540 xasprintf(&show
, conv
, temp_double
);
543 iresult
= get_status(response_value
[i
], thlds
[i
]);
544 xasprintf(&show
, conv
, response_value
[i
]);
548 /* Process this block for string matching */
549 else if (eval_size
> i
&& eval_method
[i
] & CRIT_STRING
) {
550 if (strcmp(show
, string_value
))
551 iresult
= (invert_search
== 0) ? STATE_CRITICAL
: STATE_OK
;
553 iresult
= (invert_search
== 0) ? STATE_OK
: STATE_CRITICAL
;
556 /* Process this block for regex matching */
557 else if (eval_size
> i
&& eval_method
[i
] & CRIT_REGEX
) {
558 excode
= regexec(&preg
, response
, 10, pmatch
, eflags
);
560 iresult
= (invert_search
== 0) ? STATE_OK
: STATE_CRITICAL
;
561 } else if (excode
!= REG_NOMATCH
) {
562 regerror(excode
, &preg
, errbuf
, MAX_INPUT_BUFFER
);
563 printf(_("Execute Error: %s\n"), errbuf
);
564 exit(STATE_CRITICAL
);
566 iresult
= (invert_search
== 0) ? STATE_CRITICAL
: STATE_OK
;
570 /* Process this block for existence-nonexistence checks */
571 /* TV: Should this be outside of this else block? */
573 if (eval_size
> i
&& eval_method
[i
] & CRIT_PRESENT
)
574 iresult
= STATE_CRITICAL
;
575 else if (eval_size
> i
&& eval_method
[i
] & WARN_PRESENT
)
576 iresult
= STATE_WARNING
;
577 else if (response
&& iresult
== STATE_DEPENDENT
)
581 /* Result is the worst outcome of all the OIDs tested */
582 result
= max_state(result
, iresult
);
584 /* Prepend a label for this OID if there is one */
585 if (nlabels
>= (size_t)1 && (size_t)i
< nlabels
&& labels
[i
] != NULL
)
586 xasprintf(&outbuff
, "%s%s%s %s%s%s", outbuff
, (i
== 0) ? " " : output_delim
, labels
[i
], mark(iresult
), show
, mark(iresult
));
588 xasprintf(&outbuff
, "%s%s%s%s%s", outbuff
, (i
== 0) ? " " : output_delim
, mark(iresult
), show
, mark(iresult
));
590 /* Append a unit string for this OID if there is one */
591 if (nunits
> (size_t)0 && (size_t)i
< nunits
&& unitv
[i
] != NULL
)
592 xasprintf(&outbuff
, "%s %s", outbuff
, unitv
[i
]);
594 /* Write perfdata with whatever can be parsed by strtod, if possible */
598 if (perf_labels
&& nlabels
>= (size_t)1 && (size_t)i
< nlabels
&& labels
[i
] != NULL
)
599 temp_string
= labels
[i
];
601 temp_string
= oidname
;
602 if (strpbrk(temp_string
, " ='\"") == NULL
) {
603 strncat(perfstr
, temp_string
, sizeof(perfstr
) - strlen(perfstr
) - 1);
605 if (strpbrk(temp_string
, "'") == NULL
) {
610 strncat(perfstr
, quote_string
, sizeof(perfstr
) - strlen(perfstr
) - 1);
611 strncat(perfstr
, temp_string
, sizeof(perfstr
) - strlen(perfstr
) - 1);
612 strncat(perfstr
, quote_string
, sizeof(perfstr
) - strlen(perfstr
) - 1);
614 strncat(perfstr
, "=", sizeof(perfstr
) - strlen(perfstr
) - 1);
615 len
= sizeof(perfstr
) - strlen(perfstr
) - 1;
616 strncat(perfstr
, show
, len
> ptr
- show
? ptr
- show
: len
);
618 if (strcmp(type
, "") != 0) {
619 strncat(perfstr
, type
, sizeof(perfstr
) - strlen(perfstr
) - 1);
622 if (warning_thresholds
) {
623 strncat(perfstr
, ";", sizeof(perfstr
) - strlen(perfstr
) - 1);
624 if (thlds
[i
]->warning
&& thlds
[i
]->warning
->text
)
625 strncat(perfstr
, thlds
[i
]->warning
->text
, sizeof(perfstr
) - strlen(perfstr
) - 1);
628 if (critical_thresholds
) {
629 if (!warning_thresholds
)
630 strncat(perfstr
, ";", sizeof(perfstr
) - strlen(perfstr
) - 1);
631 strncat(perfstr
, ";", sizeof(perfstr
) - strlen(perfstr
) - 1);
632 if (thlds
[i
]->critical
&& thlds
[i
]->critical
->text
)
633 strncat(perfstr
, thlds
[i
]->critical
->text
, sizeof(perfstr
) - strlen(perfstr
) - 1);
636 strncat(perfstr
, " ", sizeof(perfstr
) - strlen(perfstr
) - 1);
640 /* Save state data, as all data collected now */
641 if (calculate_rate
) {
642 string_length
= 1024;
643 state_string
= malloc(string_length
);
644 if (state_string
== NULL
)
645 die(STATE_UNKNOWN
, _("Cannot malloc"));
648 for (int i
= 0; i
< total_oids
; i
++) {
649 xasprintf(&temp_string
, "%.0f", response_value
[i
]);
650 if (temp_string
== NULL
)
651 die(STATE_UNKNOWN
, _("Cannot asprintf()"));
652 response_length
= strlen(temp_string
);
653 if (current_length
+ response_length
> string_length
) {
654 string_length
= current_length
+ 1024;
655 state_string
= realloc(state_string
, string_length
);
656 if (state_string
== NULL
)
657 die(STATE_UNKNOWN
, _("Cannot realloc()"));
659 strcpy(&state_string
[current_length
], temp_string
);
660 current_length
= current_length
+ response_length
;
661 state_string
[current_length
] = ':';
665 state_string
[--current_length
] = '\0';
667 printf("State string=%s\n", state_string
);
669 /* This is not strictly the same as time now, but any subtle variations will cancel out */
670 np_state_write_string(current_time
, state_string
);
671 if (previous_state
== NULL
) {
672 /* Or should this be highest state? */
673 die(STATE_OK
, _("No previous data to calculate rate - assume okay"));
677 printf("%s %s -%s %s\n", label
, state_text(result
), outbuff
, perfstr
);
679 printf("%s", mult_resp
);
684 /* process command-line arguments */
685 int process_arguments(int argc
, char **argv
) {
686 static struct option longopts
[] = {STD_LONG_OPTS
,
687 {"community", required_argument
, 0, 'C'},
688 {"oid", required_argument
, 0, 'o'},
689 {"object", required_argument
, 0, 'o'},
690 {"delimiter", required_argument
, 0, 'd'},
691 {"nulloid", required_argument
, 0, 'z'},
692 {"output-delimiter", required_argument
, 0, 'D'},
693 {"string", required_argument
, 0, 's'},
694 {"timeout", required_argument
, 0, 't'},
695 {"regex", required_argument
, 0, 'r'},
696 {"ereg", required_argument
, 0, 'r'},
697 {"eregi", required_argument
, 0, 'R'},
698 {"label", required_argument
, 0, 'l'},
699 {"units", required_argument
, 0, 'u'},
700 {"port", required_argument
, 0, 'p'},
701 {"retries", required_argument
, 0, 'e'},
702 {"miblist", required_argument
, 0, 'm'},
703 {"protocol", required_argument
, 0, 'P'},
704 {"context", required_argument
, 0, 'N'},
705 {"seclevel", required_argument
, 0, 'L'},
706 {"secname", required_argument
, 0, 'U'},
707 {"authproto", required_argument
, 0, 'a'},
708 {"privproto", required_argument
, 0, 'x'},
709 {"authpasswd", required_argument
, 0, 'A'},
710 {"privpasswd", required_argument
, 0, 'X'},
711 {"next", no_argument
, 0, 'n'},
712 {"rate", no_argument
, 0, L_CALCULATE_RATE
},
713 {"rate-multiplier", required_argument
, 0, L_RATE_MULTIPLIER
},
714 {"offset", required_argument
, 0, L_OFFSET
},
715 {"invert-search", no_argument
, 0, L_INVERT_SEARCH
},
716 {"perf-oids", no_argument
, 0, 'O'},
717 {"ipv4", no_argument
, 0, '4'},
718 {"ipv6", no_argument
, 0, '6'},
719 {"multiplier", required_argument
, 0, 'M'},
720 {"fmtstr", required_argument
, 0, 'f'},
721 {"ignore-mib-parsing-errors", no_argument
, false, L_IGNORE_MIB_PARSING_ERRORS
},
727 /* reverse compatibility for very old non-POSIX usage forms */
728 for (int c
= 1; c
< argc
; c
++) {
729 if (strcmp("-to", argv
[c
]) == 0)
730 strcpy(argv
[c
], "-t");
731 if (strcmp("-wv", argv
[c
]) == 0)
732 strcpy(argv
[c
], "-w");
733 if (strcmp("-cv", argv
[c
]) == 0)
734 strcpy(argv
[c
], "-c");
741 int option_char
= 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:M:f:z:", longopts
, &option
);
743 if (option_char
== -1 || option_char
== EOF
)
746 switch (option_char
) {
747 case '?': /* usage */
752 case 'V': /* version */
753 print_revision(progname
, NP_VERSION
);
755 case 'v': /* verbose */
759 /* Connection info */
760 case 'C': /* group or community */
763 case 'H': /* Host or server */
764 server_address
= optarg
;
766 case 'p': /* TCP port number */
769 case 'm': /* List of MIBS */
772 case 'n': /* usesnmpgetnext */
773 usesnmpgetnext
= true;
775 case 'P': /* SNMP protocol version */
778 case 'N': /* SNMPv3 context */
781 case 'L': /* security level */
784 case 'U': /* security username */
787 case 'a': /* auth protocol */
790 case 'x': /* priv protocol */
793 case 'A': /* auth passwd */
796 case 'X': /* priv passwd */
799 case 't': /* timeout period */
800 if (!is_integer(optarg
))
801 usage2(_("Timeout interval must be a positive integer"), optarg
);
803 timeout_interval
= atoi(optarg
);
806 /* Test parameters */
807 case 'c': /* critical threshold */
808 critical_thresholds
= optarg
;
810 case 'w': /* warning threshold */
811 warning_thresholds
= optarg
;
813 case 'e': /* PRELIMINARY - may change */
814 case 'E': /* PRELIMINARY - may change */
815 if (!is_integer(optarg
))
816 usage2(_("Retries interval must be a positive integer"), optarg
);
818 retries
= atoi(optarg
);
820 case 'o': /* object identifier */
821 if (strspn(optarg
, "0123456789.,") != strlen(optarg
)) {
823 * we have something other than digits, periods and comas,
824 * so we have a mib variable, rather than just an SNMP OID,
825 * so we have to actually read the mib files
829 for (char *ptr
= strtok(optarg
, ", "); ptr
!= NULL
; ptr
= strtok(NULL
, ", "), j
++) {
830 while (j
>= oids_size
) {
831 oids_size
+= OID_COUNT_STEP
;
832 oids
= realloc(oids
, oids_size
* sizeof(*oids
));
834 oids
[j
] = strdup(ptr
);
837 if (option_char
== 'E' || option_char
== 'e') {
839 while (j
+ 1 >= eval_size
) {
840 eval_size
+= OID_COUNT_STEP
;
841 eval_method
= realloc(eval_method
, eval_size
* sizeof(*eval_method
));
842 memset(eval_method
+ eval_size
- OID_COUNT_STEP
, 0, 8);
844 if (option_char
== 'E')
845 eval_method
[j
+ 1] |= WARN_PRESENT
;
846 else if (option_char
== 'e')
847 eval_method
[j
+ 1] |= CRIT_PRESENT
;
850 case 'z': /* Null OID Return Check */
851 if (!is_integer(optarg
))
852 usage2(_("Exit status must be a positive integer"), optarg
);
854 nulloid
= atoi(optarg
);
856 case 's': /* string or substring */
857 strncpy(string_value
, optarg
, sizeof(string_value
) - 1);
858 string_value
[sizeof(string_value
) - 1] = 0;
859 while (jj
>= eval_size
) {
860 eval_size
+= OID_COUNT_STEP
;
861 eval_method
= realloc(eval_method
, eval_size
* sizeof(*eval_method
));
862 memset(eval_method
+ eval_size
- OID_COUNT_STEP
, 0, 8);
864 eval_method
[jj
++] = CRIT_STRING
;
866 case 'R': /* regex */
869 case 'r': /* regex */
870 cflags
|= REG_EXTENDED
| REG_NOSUB
| REG_NEWLINE
;
871 strncpy(regex_expect
, optarg
, sizeof(regex_expect
) - 1);
872 regex_expect
[sizeof(regex_expect
) - 1] = 0;
873 errcode
= regcomp(&preg
, regex_expect
, cflags
);
875 regerror(errcode
, &preg
, errbuf
, MAX_INPUT_BUFFER
);
876 printf(_("Could Not Compile Regular Expression"));
879 while (jj
>= eval_size
) {
880 eval_size
+= OID_COUNT_STEP
;
881 eval_method
= realloc(eval_method
, eval_size
* sizeof(*eval_method
));
882 memset(eval_method
+ eval_size
- OID_COUNT_STEP
, 0, 8);
884 eval_method
[jj
++] = CRIT_REGEX
;
888 case 'd': /* delimiter */
889 delimiter
= strscpy(delimiter
, optarg
);
891 case 'D': /* output-delimiter */
892 output_delim
= strscpy(output_delim
, optarg
);
894 case 'l': /* label */
896 if (nlabels
> labels_size
) {
898 labels
= realloc(labels
, labels_size
* sizeof(*labels
));
900 die(STATE_UNKNOWN
, _("Could not reallocate labels[%d]"), (int)nlabels
);
902 labels
[nlabels
- 1] = optarg
;
903 char *ptr
= thisarg(optarg
);
904 labels
[nlabels
- 1] = ptr
;
906 labels
[nlabels
- 1] = ptr
+ 1;
907 while (ptr
&& (ptr
= nextarg(ptr
))) {
909 if (nlabels
> labels_size
) {
911 labels
= realloc(labels
, labels_size
* sizeof(*labels
));
913 die(STATE_UNKNOWN
, _("Could not reallocate labels\n"));
917 labels
[nlabels
- 1] = ptr
+ 1;
919 labels
[nlabels
- 1] = ptr
;
922 case 'u': /* units */
925 if (nunits
> unitv_size
) {
927 unitv
= realloc(unitv
, unitv_size
* sizeof(*unitv
));
929 die(STATE_UNKNOWN
, _("Could not reallocate units [%d]\n"), (int)nunits
);
931 unitv
[nunits
- 1] = optarg
;
932 ptr
= thisarg(optarg
);
933 unitv
[nunits
- 1] = ptr
;
935 unitv
[nunits
- 1] = ptr
+ 1;
936 while (ptr
&& (ptr
= nextarg(ptr
))) {
937 if (nunits
> unitv_size
) {
939 unitv
= realloc(unitv
, unitv_size
* sizeof(*unitv
));
941 die(STATE_UNKNOWN
, _("Could not realloc() units\n"));
946 unitv
[nunits
- 1] = ptr
+ 1;
948 unitv
[nunits
- 1] = ptr
;
951 case L_CALCULATE_RATE
:
952 if (calculate_rate
== 0)
953 np_enable_state(NULL
, 1);
956 case L_RATE_MULTIPLIER
:
957 if (!is_integer(optarg
) || ((rate_multiplier
= atoi(optarg
)) <= 0))
958 usage2(_("Rate multiplier must be a positive integer"), optarg
);
961 offset
= strtod(optarg
, NULL
);
963 case L_INVERT_SEARCH
:
972 xasprintf(&ip_version
, "udp6:");
974 printf("IPv6 detected! Will pass \"udp6:\" to snmpget.\n");
977 if (strspn(optarg
, "0123456789.,") == strlen(optarg
)) {
978 multiplier
= strtod(optarg
, NULL
);
982 if (multiplier
!= 1.0) {
987 case L_IGNORE_MIB_PARSING_ERRORS
:
988 ignore_mib_parsing_errors
= true;
992 if (server_address
== NULL
)
993 server_address
= argv
[optind
];
995 if (community
== NULL
)
996 community
= strdup(DEFAULT_COMMUNITY
);
998 return validate_arguments();
1001 /******************************************************************************
1005 <title>validate_arguments</title>
1007 <para>&PROTO_validate_arguments;</para>
1009 <para>Checks to see if the default miblist needs to be loaded. Also verifies
1010 the authentication and authorization combinations based on protocol version
1017 ******************************************************************************/
1019 static int validate_arguments() {
1020 /* check whether to load locally installed MIBS (CPU/disk intensive) */
1021 if (miblist
== NULL
) {
1023 miblist
= strdup(DEFAULT_MIBLIST
);
1025 miblist
= ""; /* don't read any mib files for numeric oids */
1029 /* Check server_address is given */
1030 if (server_address
== NULL
)
1031 die(STATE_UNKNOWN
, _("No host specified\n"));
1033 /* Check oid is given */
1035 die(STATE_UNKNOWN
, _("No OIDs specified\n"));
1038 xasprintf(&proto
, DEFAULT_PROTOCOL
);
1040 if ((strcmp(proto
, "1") == 0) || (strcmp(proto
, "2c") == 0)) { /* snmpv1 or snmpv2c */
1042 authpriv
= calloc(numauthpriv
, sizeof(char *));
1043 authpriv
[0] = strdup("-c");
1044 authpriv
[1] = strdup(community
);
1045 } else if (strcmp(proto
, "3") == 0) { /* snmpv3 args */
1046 if (!(context
== NULL
)) {
1048 contextargs
= calloc(numcontext
, sizeof(char *));
1049 contextargs
[0] = strdup("-n");
1050 contextargs
[1] = strdup(context
);
1053 if (seclevel
== NULL
)
1054 xasprintf(&seclevel
, "noAuthNoPriv");
1056 if (secname
== NULL
)
1057 die(STATE_UNKNOWN
, _("Required parameter: %s\n"), "secname");
1059 if (strcmp(seclevel
, "noAuthNoPriv") == 0) {
1061 authpriv
= calloc(numauthpriv
, sizeof(char *));
1062 authpriv
[0] = strdup("-l");
1063 authpriv
[1] = strdup("noAuthNoPriv");
1064 authpriv
[2] = strdup("-u");
1065 authpriv
[3] = strdup(secname
);
1067 if (!((strcmp(seclevel
, "authNoPriv") == 0) || (strcmp(seclevel
, "authPriv") == 0))) {
1068 usage2(_("Invalid seclevel"), seclevel
);
1071 if (authproto
== NULL
)
1072 xasprintf(&authproto
, DEFAULT_AUTH_PROTOCOL
);
1074 if (authpasswd
== NULL
)
1075 die(STATE_UNKNOWN
, _("Required parameter: %s\n"), "authpasswd");
1077 if (strcmp(seclevel
, "authNoPriv") == 0) {
1079 authpriv
= calloc(numauthpriv
, sizeof(char *));
1080 authpriv
[0] = strdup("-l");
1081 authpriv
[1] = strdup("authNoPriv");
1082 authpriv
[2] = strdup("-a");
1083 authpriv
[3] = strdup(authproto
);
1084 authpriv
[4] = strdup("-u");
1085 authpriv
[5] = strdup(secname
);
1086 authpriv
[6] = strdup("-A");
1087 authpriv
[7] = strdup(authpasswd
);
1088 } else if (strcmp(seclevel
, "authPriv") == 0) {
1089 if (privproto
== NULL
)
1090 xasprintf(&privproto
, DEFAULT_PRIV_PROTOCOL
);
1092 if (privpasswd
== NULL
)
1093 die(STATE_UNKNOWN
, _("Required parameter: %s\n"), "privpasswd");
1096 authpriv
= calloc(numauthpriv
, sizeof(char *));
1097 authpriv
[0] = strdup("-l");
1098 authpriv
[1] = strdup("authPriv");
1099 authpriv
[2] = strdup("-a");
1100 authpriv
[3] = strdup(authproto
);
1101 authpriv
[4] = strdup("-u");
1102 authpriv
[5] = strdup(secname
);
1103 authpriv
[6] = strdup("-A");
1104 authpriv
[7] = strdup(authpasswd
);
1105 authpriv
[8] = strdup("-x");
1106 authpriv
[9] = strdup(privproto
);
1107 authpriv
[10] = strdup("-X");
1108 authpriv
[11] = strdup(privpasswd
);
1113 usage2(_("Invalid SNMP version"), proto
);
1119 /* trim leading whitespace
1120 if there is a leading quote, make sure it balances */
1122 static char *thisarg(char *str
) {
1123 str
+= strspn(str
, " \t\r\n"); /* trim any leading whitespace */
1124 if (str
[0] == '\'') { /* handle SIMPLE quoted strings */
1125 if (strlen(str
) == 1 || !strstr(str
+ 1, "'"))
1126 die(STATE_UNKNOWN
, _("Unbalanced quotes\n"));
1131 /* if there's a leading quote, advance to the trailing quote
1132 set the trailing quote to '\x0'
1133 if the string continues, advance beyond the comma */
1135 static char *nextarg(char *str
) {
1136 if (str
[0] == '\'') {
1138 if (strlen(str
) > 1) {
1139 str
= strstr(str
+ 1, "'");
1145 if (str
[0] == ',') {
1147 if (strlen(str
) > 1) {
1153 if ((str
= strstr(str
, ",")) && strlen(str
) > 1) {
1160 /* multiply result (values 0 < n < 1 work as divider) */
1161 static char *multiply(char *str
) {
1162 if (multiplier
== 1)
1166 printf(" multiply input: %s\n", str
);
1169 double val
= strtod(str
, &endptr
);
1170 if ((val
== 0.0) && (endptr
== str
)) {
1171 die(STATE_UNKNOWN
, _("multiplier set (%.1f), but input is not a number: %s"), multiplier
, str
);
1175 printf(" multiply extracted double: %f\n", val
);
1182 if (val
== (int)val
) {
1183 snprintf(buffer
, DEFAULT_BUFFER_SIZE
, "%.0f", val
);
1186 printf(" multiply using format: %s\n", conv
);
1187 snprintf(buffer
, DEFAULT_BUFFER_SIZE
, conv
, val
);
1190 printf(" multiply result: %s\n", buffer
);
1194 static void print_help(void) {
1195 print_revision(progname
, NP_VERSION
);
1197 printf(COPYRIGHT
, copyright
, email
);
1199 printf("%s\n", _("Check status of remote machines and obtain system information via SNMP"));
1205 printf(UT_HELP_VRSN
);
1206 printf(UT_EXTRA_OPTS
);
1209 printf(UT_HOST_PORT
, 'p', DEFAULT_PORT
);
1211 /* SNMP and Authentication Protocol */
1212 printf(" %s\n", "-n, --next");
1213 printf(" %s\n", _("Use SNMP GETNEXT instead of SNMP GET"));
1214 printf(" %s\n", "-P, --protocol=[1|2c|3]");
1215 printf(" %s\n", _("SNMP protocol version"));
1216 printf(" %s\n", "-N, --context=CONTEXT");
1217 printf(" %s\n", _("SNMPv3 context"));
1218 printf(" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]");
1219 printf(" %s\n", _("SNMPv3 securityLevel"));
1220 printf(" %s\n", "-a, --authproto=AUTHENTICATION_PROTOCOL");
1222 _("SNMPv3 authentication protocol (default MD5), available options depend on the specific version of the net-snmp tools"));
1223 printf(" %s\n", _("if < 5.8 SHA (1) and MD5 should be available, if >= 5.8 additionally SHA-224, SHA-256, SHA-384 and SHA-512"));
1224 printf(" %s\n", "-x, --privproto=PRIVACY_PROTOCOL");
1225 printf(" %s\n", _("SNMPv3 privacy protocol (default DES), available options depend on the specific version of the net-snmp tools"));
1226 printf(" %s\n", _("if < 5.8 DES and AES should be available, if >= 5.8 additionally AES-192 and AES-256"));
1228 /* Authentication Tokens*/
1229 printf(" %s\n", "-C, --community=STRING");
1230 printf(" %s ", _("Optional community string for SNMP communication"));
1231 printf("(%s \"%s\")\n", _("default is"), DEFAULT_COMMUNITY
);
1232 printf(" %s\n", "-U, --secname=USERNAME");
1233 printf(" %s\n", _("SNMPv3 username"));
1234 printf(" %s\n", "-A, --authpasswd=PASSWORD");
1235 printf(" %s\n", _("SNMPv3 authentication password"));
1236 printf(" %s\n", "-X, --privpasswd=PASSWORD");
1237 printf(" %s\n", _("SNMPv3 privacy password"));
1240 printf(" %s\n", "-o, --oid=OID(s)");
1241 printf(" %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query"));
1242 printf(" %s\n", "-m, --miblist=STRING");
1243 printf(" %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'"));
1244 printf(" %s\n", _("for symbolic OIDs.)"));
1245 printf(" %s\n", "-d, --delimiter=STRING");
1246 printf(" %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER
);
1247 printf(" %s\n", _("Any data on the right hand side of the delimiter is considered"));
1248 printf(" %s\n", _("to be the data that should be used in the evaluation."));
1249 printf(" %s\n", "-z, --nulloid=#");
1250 printf(" %s\n", _("If the check returns a 0 length string or NULL value"));
1251 printf(" %s\n", _("This option allows you to choose what status you want it to exit"));
1252 printf(" %s\n", _("Excluding this option renders the default exit of 3(STATE_UNKNOWN)"));
1253 printf(" %s\n", _("0 = OK"));
1254 printf(" %s\n", _("1 = WARNING"));
1255 printf(" %s\n", _("2 = CRITICAL"));
1256 printf(" %s\n", _("3 = UNKNOWN"));
1258 /* Tests Against Integers */
1259 printf(" %s\n", "-w, --warning=THRESHOLD(s)");
1260 printf(" %s\n", _("Warning threshold range(s)"));
1261 printf(" %s\n", "-c, --critical=THRESHOLD(s)");
1262 printf(" %s\n", _("Critical threshold range(s)"));
1263 printf(" %s\n", "--rate");
1264 printf(" %s\n", _("Enable rate calculation. See 'Rate Calculation' below"));
1265 printf(" %s\n", "--rate-multiplier");
1266 printf(" %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute"));
1267 printf(" %s\n", "--offset=OFFSET");
1268 printf(" %s\n", _("Add/subtract the specified OFFSET to numeric sensor data"));
1270 /* Tests Against Strings */
1271 printf(" %s\n", "-s, --string=STRING");
1272 printf(" %s\n", _("Return OK state (for that OID) if STRING is an exact match"));
1273 printf(" %s\n", "-r, --ereg=REGEX");
1274 printf(" %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches"));
1275 printf(" %s\n", "-R, --eregi=REGEX");
1276 printf(" %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches"));
1277 printf(" %s\n", "--invert-search");
1278 printf(" %s\n", _("Invert search result (CRITICAL if found)"));
1280 /* Output Formatting */
1281 printf(" %s\n", "-l, --label=STRING");
1282 printf(" %s\n", _("Prefix label for output from plugin"));
1283 printf(" %s\n", "-u, --units=STRING");
1284 printf(" %s\n", _("Units label(s) for output data (e.g., 'sec.')."));
1285 printf(" %s\n", "-D, --output-delimiter=STRING");
1286 printf(" %s\n", _("Separates output on multiple OID requests"));
1287 printf(" %s\n", "-M, --multiplier=FLOAT");
1288 printf(" %s\n", _("Multiplies current value, 0 < n < 1 works as divider, defaults to 1"));
1289 printf(" %s\n", "-f, --fmtstr=STRING");
1290 printf(" %s\n", _("C-style format string for float values (see option -M)"));
1292 printf(UT_CONN_TIMEOUT
, DEFAULT_SOCKET_TIMEOUT
);
1293 printf(" %s\n", _("NOTE the final timeout value is calculated using this formula: timeout_interval * retries + 5"));
1294 printf(" %s\n", "-e, --retries=INTEGER");
1295 printf(" %s%i\n", _("Number of retries to be used in the requests, default: "), DEFAULT_RETRIES
);
1297 printf(" %s\n", "-O, --perf-oids");
1298 printf(" %s\n", _("Label performance data with OIDs instead of --label's"));
1300 printf(" %s\n", "--ignore-mib-parsing-errors");
1301 printf(" %s\n", _("Tell snmpget to not print errors encountered when parsing MIB files"));
1306 printf("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package."));
1307 printf("%s\n", _("if you don't have the package installed, you will need to download it from"));
1308 printf("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1311 printf("%s\n", _("Notes:"));
1312 printf(" %s\n", _("- Multiple OIDs (and labels) may be indicated by a comma or space-delimited "));
1313 printf(" %s\n", _("list (lists with internal spaces must be quoted)."));
1315 printf(" -%s", UT_THRESHOLDS_NOTES
);
1317 printf(" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'"));
1318 printf(" %s\n", _("- Note that only one string and one regex may be checked at present"));
1319 printf(" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value"));
1320 printf(" %s\n", _("returned from the SNMP query is an unsigned integer."));
1323 printf("%s\n", _("Rate Calculation:"));
1324 printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when"));
1325 printf(" %s\n", _("calculating the counter difference since the last check. check_snmp"));
1326 printf(" %s\n", _("saves the last state information in a file so that the rate per second"));
1327 printf(" %s\n", _("can be calculated. Use the --rate option to save state information."));
1328 printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK."));
1329 printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so"));
1330 printf(" %s\n", _("changing the arguments will create a new state file."));
1335 void print_usage(void) {
1336 printf("%s\n", _("Usage:"));
1337 printf("%s -H <ip_address> -o <OID> [-w warn_range] [-c crit_range]\n", progname
);
1338 printf("[-C community] [-s string] [-r regex] [-R regexi] [-t timeout] [-e retries]\n");
1339 printf("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n");
1340 printf("[-m miblist] [-P snmp version] [-N context] [-L seclevel] [-U secname]\n");
1341 printf("[-a authproto] [-A authpasswd] [-x privproto] [-X privpasswd] [-4|6]\n");
1342 printf("[-M multiplier [-f format]]\n");