Code

check_snmp now considers strings returned by SNMP that contain just
[nagiosplug.git] / plugins / check_snmp.c
1 /*****************************************************************************
2
3 * Nagios check_snmp plugin
4
5 * License: GPL
6 * Copyright (c) 1999-2007 Nagios Plugins Development Team
7
8 * Description:
9
10 * This file contains the check_snmp plugin
11
12 * Check status of remote machines and obtain system information via SNMP
13
14
15 * This program is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation, either version 3 of the License, or
18 * (at your option) any later version.
19
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 * GNU General Public License for more details.
24
25 * You should have received a copy of the GNU General Public License
26 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
27
28
29 *****************************************************************************/
31 const char *progname = "check_snmp";
32 const char *copyright = "1999-2007";
33 const char *email = "nagiosplug-devel@lists.sourceforge.net";
35 #include "common.h"
36 #include "utils.h"
37 #include "utils_cmd.h"
39 #define DEFAULT_COMMUNITY "public"
40 #define DEFAULT_PORT "161"
41 #define DEFAULT_MIBLIST "ALL"
42 #define DEFAULT_PROTOCOL "1"
43 #define DEFAULT_TIMEOUT 1
44 #define DEFAULT_RETRIES 5
45 #define DEFAULT_AUTH_PROTOCOL "MD5"
46 #define DEFAULT_PRIV_PROTOCOL "DES"
47 #define DEFAULT_DELIMITER "="
48 #define DEFAULT_OUTPUT_DELIMITER " "
50 #define mark(a) ((a)!=0?"*":"")
52 #define CHECK_UNDEF 0
53 #define CRIT_PRESENT 1
54 #define CRIT_STRING 2
55 #define CRIT_REGEX 4
56 #define WARN_PRESENT 8
57 #define WARN_STRING 16
58 #define WARN_REGEX 32
60 #define MAX_OIDS 8
62 /* Longopts only arguments */
63 #define L_CALCULATE_RATE CHAR_MAX+1
64 #define L_RATE_MULTIPLIER CHAR_MAX+2
65 #define L_INVERT_SEARCH CHAR_MAX+3
67 /* Gobble to string - stop incrementing c when c[0] match one of the
68  * characters in s */
69 #define GOBBLE_TOS(c, s) while(c[0]!='\0' && strchr(s, c[0])==NULL) { c++; }
70 /* Given c, keep track of backslashes (bk) and double-quotes (dq)
71  * from c[0] */
72 #define COUNT_SEQ(c, bk, dq) switch(c[0]) {\
73         case '\\': \
74                 if (bk) bk--; \
75                 else bk++; \
76                 break; \
77         case '"': \
78                 if (!dq) { dq++; } \
79                 else if(!bk) { dq--; } \
80                 else { bk--; } \
81                 break; \
82         }
86 int process_arguments (int, char **);
87 int validate_arguments (void);
88 char *thisarg (char *str);
89 char *nextarg (char *str);
90 void print_usage (void);
91 void print_help (void);
93 #include "regex.h"
94 char regex_expect[MAX_INPUT_BUFFER] = "";
95 regex_t preg;
96 regmatch_t pmatch[10];
97 char errbuf[MAX_INPUT_BUFFER] = "";
98 char perfstr[MAX_INPUT_BUFFER] = "| ";
99 int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
100 int eflags = 0;
101 int errcode, excode;
103 char *server_address = NULL;
104 char *community = NULL;
105 char **authpriv = NULL;
106 char *proto = NULL;
107 char *seclevel = NULL;
108 char *secname = NULL;
109 char *authproto = NULL;
110 char *privproto = NULL;
111 char *authpasswd = NULL;
112 char *privpasswd = NULL;
113 char **oids = NULL;
114 char *label;
115 char *units;
116 char *port;
117 char *snmpcmd;
118 char string_value[MAX_INPUT_BUFFER] = "";
119 int  invert_search=0;
120 char **labels = NULL;
121 char **unitv = NULL;
122 size_t nlabels = 0;
123 size_t labels_size = 8;
124 size_t nunits = 0;
125 size_t unitv_size = 8;
126 int numoids = 0;
127 int numauthpriv = 0;
128 int verbose = 0;
129 int usesnmpgetnext = FALSE;
130 char *warning_thresholds = NULL;
131 char *critical_thresholds = NULL;
132 thresholds *thlds[MAX_OIDS];
133 double response_value[MAX_OIDS];
134 int retries = 0;
135 int eval_method[MAX_OIDS];
136 char *delimiter;
137 char *output_delim;
138 char *miblist = NULL;
139 int needmibs = FALSE;
140 int calculate_rate = 0;
141 int rate_multiplier = 1;
142 state_data *previous_state;
143 double previous_value[MAX_OIDS];
146 int
147 main (int argc, char **argv)
149         int i, len, line, total_oids;
150         unsigned int bk_count = 0, dq_count = 0;
151         int iresult = STATE_UNKNOWN;
152         int result = STATE_UNKNOWN;
153         int return_code = 0;
154         int external_error = 0;
155         char **command_line = NULL;
156         char *cl_hidden_auth = NULL;
157         char *oidname = NULL;
158         char *response = NULL;
159         char *mult_resp = NULL;
160         char *outbuff;
161         char *ptr = NULL;
162         char *show = NULL;
163         char *endptr = NULL;
164         char *th_warn=NULL;
165         char *th_crit=NULL;
166         char type[8] = "";
167         output chld_out, chld_err;
168         char *previous_string=NULL;
169         char *ap=NULL;
170         char *state_string=NULL;
171         size_t response_length, current_length, string_length;
172         char *temp_string=NULL;
173         int is_numeric=0;
174         time_t current_time;
175         double temp_double;
176         time_t duration;
177         char *conv = "12345678";
178         int is_counter=0;
180         setlocale (LC_ALL, "");
181         bindtextdomain (PACKAGE, LOCALEDIR);
182         textdomain (PACKAGE);
184         labels = malloc (labels_size);
185         unitv = malloc (unitv_size);
186         for (i = 0; i < MAX_OIDS; i++)
187                 eval_method[i] = CHECK_UNDEF;
189         label = strdup ("SNMP");
190         units = strdup ("");
191         port = strdup (DEFAULT_PORT);
192         outbuff = strdup ("");
193         delimiter = strdup (" = ");
194         output_delim = strdup (DEFAULT_OUTPUT_DELIMITER);
195         timeout_interval = DEFAULT_TIMEOUT;
196         retries = DEFAULT_RETRIES;
198         np_init( (char *) progname, argc, argv );
200         /* Parse extra opts if any */
201         argv=np_extra_opts (&argc, argv, progname);
203         np_set_args(argc, argv);
205         if (process_arguments (argc, argv) == ERROR)
206                 usage4 (_("Could not parse arguments"));
208         if(calculate_rate) {
209                 if (!strcmp(label, "SNMP"))
210                         label = strdup("SNMP RATE");
211                 time(&current_time);
212                 i=0;
213                 previous_state = np_state_read();
214                 if(previous_state!=NULL) {
215                         /* Split colon separated values */
216                         previous_string = strdup((char *) previous_state->data);
217                         while((ap = strsep(&previous_string, ":")) != NULL) {
218                                 if(verbose>2)
219                                         printf("State for %d=%s\n", i, ap);
220                                 previous_value[i++]=strtod(ap,NULL);
221                         }
222                 }
223         }
225         /* Populate the thresholds */
226         th_warn=warning_thresholds;
227         th_crit=critical_thresholds;
228         for (i=0; i<numoids; i++) {
229                 char *w = th_warn ? strndup(th_warn, strcspn(th_warn, ",")) : NULL;
230                 char *c = th_crit ? strndup(th_crit, strcspn(th_crit, ",")) : NULL;
231                 /* Skip empty thresholds, while avoiding segfault */
232                 set_thresholds(&thlds[i],
233                                w ? strpbrk(w, NP_THRESHOLDS_CHARS) : NULL,
234                                c ? strpbrk(c, NP_THRESHOLDS_CHARS) : NULL);
235                 if (w) {
236                         th_warn=strchr(th_warn, ',');
237                         if (th_warn) th_warn++;
238                         free(w);
239                 }
240                 if (c) {
241                         th_crit=strchr(th_crit, ',');
242                         if (th_crit) th_crit++;
243                         free(c);
244                 }
245         }
247         /* Create the command array to execute */
248         if(usesnmpgetnext == TRUE) {
249                 snmpcmd = strdup (PATH_TO_SNMPGETNEXT);
250         }else{
251                 snmpcmd = strdup (PATH_TO_SNMPGET);
252         }
254         /* 9 arguments to pass before authpriv options + 1 for host and numoids. Add one for terminating NULL */
255         command_line = calloc (9 + numauthpriv + 1 + numoids + 1, sizeof (char *));
256         command_line[0] = snmpcmd;
257         command_line[1] = strdup ("-t");
258         asprintf (&command_line[2], "%d", timeout_interval);
259         command_line[3] = strdup ("-r");
260         asprintf (&command_line[4], "%d", retries);
261         command_line[5] = strdup ("-m");
262         command_line[6] = strdup (miblist);
263         command_line[7] = "-v";
264         command_line[8] = strdup (proto);
266         for (i = 0; i < numauthpriv; i++) {
267                 command_line[9 + i] = authpriv[i];
268         }
270         asprintf (&command_line[9 + numauthpriv], "%s:%s", server_address, port);
272         /* This is just for display purposes, so it can remain a string */
273         asprintf(&cl_hidden_auth, "%s -t %d -r %d -m %s -v %s %s %s:%s",
274                 snmpcmd, timeout_interval, retries, strlen(miblist) ? miblist : "''", proto, "[authpriv]",
275                 server_address, port);
277         for (i = 0; i < numoids; i++) {
278                 command_line[9 + numauthpriv + 1 + i] = oids[i];
279                 asprintf(&cl_hidden_auth, "%s %s", cl_hidden_auth, oids[i]);    
280         }
282         command_line[9 + numauthpriv + 1 + numoids] = NULL;
284         if (verbose)
285                 printf ("%s\n", cl_hidden_auth);
287         /* Run the command */
288         return_code = cmd_run_array (command_line, &chld_out, &chld_err, 0);
290         /* Due to net-snmp sometimes showing stderr messages with poorly formed MIBs,
291            only return state unknown if return code is non zero or there is no stdout.
292            Do this way so that if there is stderr, will get added to output, which helps problem diagnosis
293         */
294         if (return_code != 0)
295                 external_error=1;
296         if (chld_out.lines == 0)
297                 external_error=1;
298         if (external_error) {
299                 if (chld_err.lines > 0) {
300                         printf (_("External command error: %s\n"), chld_err.line[0]);
301                         for (i = 1; i < chld_err.lines; i++) {
302                                 printf ("%s\n", chld_err.line[i]);
303                         }
304                 } else {
305                         printf(_("External command error with no output (return code: %d)\n"), return_code);
306                 }
307                 exit (STATE_UNKNOWN);
308         }
310         if (verbose) {
311                 for (i = 0; i < chld_out.lines; i++) {
312                         printf ("%s\n", chld_out.line[i]);
313                 }
314         }
316         for (line=0, i=0; line < chld_out.lines; line++, i++) {
317                 if(calculate_rate)
318                         conv = "%.10g";
319                 else
320                         conv = "%.0f";
322                 ptr = chld_out.line[line];
323                 oidname = strpcpy (oidname, ptr, delimiter);
324                 response = strstr (ptr, delimiter);
325                 if (response == NULL)
326                         break;
328                 if (verbose > 2) {
329                         printf("Processing oid %i (line %i)\n  oidname: %s\n  response: %s\n", i+1, line+1, oidname, response);
330                 }
332                 /* Clean up type array - Sol10 does not necessarily zero it out */
333                 bzero(type, sizeof(type));
335                 is_counter=0;
336                 /* We strip out the datatype indicator for PHBs */
337                 if (strstr (response, "Gauge: ")) {
338                         show = strstr (response, "Gauge: ") + 7;
339                         is_numeric++;
340                 } 
341                 else if (strstr (response, "Gauge32: ")) {
342                         show = strstr (response, "Gauge32: ") + 9;
343                         is_numeric++;
344                 } 
345                 else if (strstr (response, "Counter32: ")) {
346                         show = strstr (response, "Counter32: ") + 11;
347                         is_numeric++;
348                         is_counter=1;
349                         if(!calculate_rate) 
350                                 strcpy(type, "c");
351                 }
352                 else if (strstr (response, "Counter64: ")) {
353                         show = strstr (response, "Counter64: ") + 11;
354                         is_numeric++;
355                         is_counter=1;
356                         if(!calculate_rate)
357                                 strcpy(type, "c");
358                 }
359                 else if (strstr (response, "INTEGER: ")) {
360                         show = strstr (response, "INTEGER: ") + 9;
361                         is_numeric++;
362                 }
363                 else if (strstr (response, "STRING: ")) {
364                         show = strstr (response, "STRING: ") + 8;
365                         conv = "%.10g";
367                         /* Get the rest of the string on multi-line strings */
368                         ptr = show;
369                         COUNT_SEQ(ptr, bk_count, dq_count)
370                         while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
371                                 ptr++;
372                                 GOBBLE_TOS(ptr, "\n\"\\")
373                                 COUNT_SEQ(ptr, bk_count, dq_count)
374                         }
376                         if (dq_count) { /* unfinished line */
377                                 /* copy show verbatim first */
378                                 if (!mult_resp) mult_resp = strdup("");
379                                 asprintf (&mult_resp, "%s%s:\n%s\n", mult_resp, oids[i], show);
380                                 /* then strip out unmatched double-quote from single-line output */
381                                 if (show[0] == '"') show++;
383                                 /* Keep reading until we match end of double-quoted string */
384                                 for (line++; line < chld_out.lines; line++) {
385                                         ptr = chld_out.line[line];
386                                         asprintf (&mult_resp, "%s%s\n", mult_resp, ptr);
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)
393                                         }
394                                         /* Break for loop before next line increment when done */
395                                         if (!dq_count) break;
396                                 }
397                         }
399                         /* Allow numeric conversion if whole string is a number. Make concession for strings with " at beginning or end */
400                         /* This duplicates the conversion a bit later, but is cleaner to separate out the checking against the conversion */
401                         ptr = show;
402                         if (*ptr == '"')
403                                 ptr++;
404                         if (*ptr != '\0' ) {
405                                 strtod( ptr, &endptr );
406                                 if (*endptr == '"')
407                                         endptr++;
408                                 if (*endptr == '\0')
409                                         is_numeric=1;
410                         }
412                 }
413                 else if (strstr (response, "Timeticks: "))
414                         show = strstr (response, "Timeticks: ");
415                 else
416                         show = response;
418                 iresult = STATE_DEPENDENT;
420                 /* Process this block for numeric comparisons */
421                  if (is_numeric) {
422                         ptr = strpbrk (show, "0123456789");
423                         if (ptr == NULL)
424                                 die (STATE_UNKNOWN,_("No valid data returned"));
425                         response_value[i] = strtod (ptr, NULL);
427                         if(calculate_rate) {
428                                 if (previous_state!=NULL) {
429                                         duration = current_time-previous_state->time;
430                                         if(duration<=0)
431                                                 die(STATE_UNKNOWN,_("Time duration between plugin calls is invalid"));
432                                         temp_double = response_value[i]-previous_value[i];
433                                         /* Simple overflow catcher (same as in rrdtool, rrd_update.c) */
434                                         if(is_counter) {
435                                                 if(temp_double<(double)0.0)
436                                                         temp_double+=(double)4294967296.0; /* 2^32 */
437                                                 if(temp_double<(double)0.0)
438                                                         temp_double+=(double)18446744069414584320.0; /* 2^64-2^32 */;
439                                         }
440                                         /* Convert to per second, then use multiplier */
441                                         temp_double = temp_double/duration*rate_multiplier;
442                                         iresult = get_status(temp_double, thlds[i]);
443                                         asprintf (&show, conv, temp_double);
444                                 }
445                         } else {
446                                 iresult = get_status(response_value[i], thlds[i]);
447                                 asprintf (&show, conv, response_value[i]);
448                         }
449                 }
451                 /* Process this block for string matching */
452                 else if (eval_method[i] & CRIT_STRING) {
453                         if (strcmp (show, string_value))
454                                 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK;
455                         else
456                                 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
457                 }
459                 /* Process this block for regex matching */
460                 else if (eval_method[i] & CRIT_REGEX) {
461                         excode = regexec (&preg, response, 10, pmatch, eflags);
462                         if (excode == 0) {
463                                 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
464                         }
465                         else if (excode != REG_NOMATCH) {
466                                 regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER);
467                                 printf (_("Execute Error: %s\n"), errbuf);
468                                 exit (STATE_CRITICAL);
469                         }
470                         else {
471                                 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK;
472                         }
473                 }
475                 /* Process this block for existence-nonexistence checks */
476                 /* TV: Should this be outside of this else block? */
477                 else {
478                         if (eval_method[i] & CRIT_PRESENT)
479                                 iresult = STATE_CRITICAL;
480                         else if (eval_method[i] & WARN_PRESENT)
481                                 iresult = STATE_WARNING;
482                         else if (response && iresult == STATE_DEPENDENT)
483                                 iresult = STATE_OK;
484                 }
486                 /* Result is the worst outcome of all the OIDs tested */
487                 result = max_state (result, iresult);
489                 /* Prepend a label for this OID if there is one */
490                 if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
491                         asprintf (&outbuff, "%s%s%s %s%s%s", outbuff,
492                                 (i == 0) ? " " : output_delim,
493                                 labels[i], mark (iresult), show, mark (iresult));
494                 else
495                         asprintf (&outbuff, "%s%s%s%s%s", outbuff, (i == 0) ? " " : output_delim,
496                                 mark (iresult), show, mark (iresult));
498                 /* Append a unit string for this OID if there is one */
499                 if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL)
500                         asprintf (&outbuff, "%s %s", outbuff, unitv[i]);
502                 /* Write perfdata with whatever can be parsed by strtod, if possible */
503                 ptr = NULL;
504                 strtod(show, &ptr);
505                 if (ptr > show) {
506                         if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
507                                 temp_string=labels[i];
508                         else
509                                 temp_string=oidname;
510                         strncat(perfstr, temp_string, sizeof(perfstr)-strlen(perfstr)-1);
511                         strncat(perfstr, "=", sizeof(perfstr)-strlen(perfstr)-1);
512                         len = sizeof(perfstr)-strlen(perfstr)-1;
513                         strncat(perfstr, show, len>ptr-show ? ptr-show : len);
515                         if (type)
516                                 strncat(perfstr, type, sizeof(perfstr)-strlen(perfstr)-1);
517                         strncat(perfstr, " ", sizeof(perfstr)-strlen(perfstr)-1);
518                 }
519         }
520         total_oids=i;
522         /* Save state data, as all data collected now */
523         if(calculate_rate) {
524                 string_length=1024;
525                 state_string=malloc(string_length);
526                 if(state_string==NULL)
527                         die(STATE_UNKNOWN, _("Cannot malloc"));
528                 
529                 current_length=0;
530                 for(i=0; i<total_oids; i++) {
531                         asprintf(&temp_string,"%.0f",response_value[i]);
532                         if(temp_string==NULL)
533                                 die(STATE_UNKNOWN,_("Cannot asprintf()"));
534                         response_length = strlen(temp_string);
535                         if(current_length+response_length>string_length) {
536                                 string_length=current_length+1024;
537                                 state_string=realloc(state_string,string_length);
538                                 if(state_string==NULL)
539                                         die(STATE_UNKNOWN, _("Cannot realloc()"));
540                         }
541                         strcpy(&state_string[current_length],temp_string);
542                         current_length=current_length+response_length;
543                         state_string[current_length]=':';
544                         current_length++;
545                         free(temp_string);
546                 }
547                 state_string[--current_length]='\0';
548                 if (verbose > 2)
549                         printf("State string=%s\n",state_string);
550                 
551                 /* This is not strictly the same as time now, but any subtle variations will cancel out */
552                 np_state_write_string(current_time, state_string );
553                 if(previous_state==NULL) {
554                         /* Or should this be highest state? */
555                         die( STATE_OK, _("No previous data to calculate rate - assume okay" ) );
556                 }
557         }
559         printf ("%s %s -%s %s\n", label, state_text (result), outbuff, perfstr);
560         if (mult_resp) printf ("%s", mult_resp);
562         return result;
567 /* process command-line arguments */
568 int
569 process_arguments (int argc, char **argv)
571         char *ptr;
572         int c = 1;
573         int j = 0, jj = 0, ii = 0;
575         int option = 0;
576         static struct option longopts[] = {
577                 STD_LONG_OPTS,
578                 {"community", required_argument, 0, 'C'},
579                 {"oid", required_argument, 0, 'o'},
580                 {"object", required_argument, 0, 'o'},
581                 {"delimiter", required_argument, 0, 'd'},
582                 {"output-delimiter", required_argument, 0, 'D'},
583                 {"string", required_argument, 0, 's'},
584                 {"timeout", required_argument, 0, 't'},
585                 {"regex", required_argument, 0, 'r'},
586                 {"ereg", required_argument, 0, 'r'},
587                 {"eregi", required_argument, 0, 'R'},
588                 {"label", required_argument, 0, 'l'},
589                 {"units", required_argument, 0, 'u'},
590                 {"port", required_argument, 0, 'p'},
591                 {"retries", required_argument, 0, 'e'},
592                 {"miblist", required_argument, 0, 'm'},
593                 {"protocol", required_argument, 0, 'P'},
594                 {"seclevel", required_argument, 0, 'L'},
595                 {"secname", required_argument, 0, 'U'},
596                 {"authproto", required_argument, 0, 'a'},
597                 {"privproto", required_argument, 0, 'x'},
598                 {"authpasswd", required_argument, 0, 'A'},
599                 {"privpasswd", required_argument, 0, 'X'},
600                 {"next", no_argument, 0, 'n'},
601                 {"rate", no_argument, 0, L_CALCULATE_RATE},
602                 {"rate-multiplier", required_argument, 0, L_RATE_MULTIPLIER},
603                 {"invert-search", no_argument, 0, L_INVERT_SEARCH},
604                 {0, 0, 0, 0}
605         };
607         if (argc < 2)
608                 return ERROR;
610         /* reverse compatibility for very old non-POSIX usage forms */
611         for (c = 1; c < argc; c++) {
612                 if (strcmp ("-to", argv[c]) == 0)
613                         strcpy (argv[c], "-t");
614                 if (strcmp ("-wv", argv[c]) == 0)
615                         strcpy (argv[c], "-w");
616                 if (strcmp ("-cv", argv[c]) == 0)
617                         strcpy (argv[c], "-c");
618         }
620         while (1) {
621                 c = getopt_long (argc, argv, "nhvVt:c:w:H:C:o:e:E:d:D:s:t:R:r:l:u:p:m:P:L:U:a:x:A:X:",
622                                                                          longopts, &option);
624                 if (c == -1 || c == EOF)
625                         break;
627                 switch (c) {
628                 case '?':       /* usage */
629                         usage5 ();
630                 case 'h':       /* help */
631                         print_help ();
632                         exit (STATE_OK);
633                 case 'V':       /* version */
634                         print_revision (progname, NP_VERSION);
635                         exit (STATE_OK);
636                 case 'v': /* verbose */
637                         verbose++;
638                         break;
640         /* Connection info */
641                 case 'C':                                                                       /* group or community */
642                         community = optarg;
643                         break;
644                 case 'H':                                                                       /* Host or server */
645                         server_address = optarg;
646                         break;
647                 case 'p':       /* TCP port number */
648                         port = optarg;
649                         break;
650                 case 'm':       /* List of MIBS */
651                         miblist = optarg;
652                         break;
653                 case 'n':       /* usesnmpgetnext */
654                         usesnmpgetnext = TRUE;
655                         break;
656                 case 'P':       /* SNMP protocol version */
657                         proto = optarg;
658                         break;
659                 case 'L':       /* security level */
660                         seclevel = optarg;
661                         break;
662                 case 'U':       /* security username */
663                         secname = optarg;
664                         break;
665                 case 'a':       /* auth protocol */
666                         authproto = optarg;
667                         break;
668                 case 'x':       /* priv protocol */
669                         privproto = optarg;
670                         break;
671                 case 'A':       /* auth passwd */
672                         authpasswd = optarg;
673                         break;
674                 case 'X':       /* priv passwd */
675                         privpasswd = optarg;
676                         break;
677                 case 't':       /* timeout period */
678                         if (!is_integer (optarg))
679                                 usage2 (_("Timeout interval must be a positive integer"), optarg);
680                         else
681                                 timeout_interval = atoi (optarg);
682                         break;
684         /* Test parameters */
685                 case 'c':                                                                       /* critical threshold */
686                         critical_thresholds = optarg;
687                         break;
688                 case 'w':                                                                       /* warning threshold */
689                         warning_thresholds = optarg;
690                         break;
691                 case 'e': /* PRELIMINARY - may change */
692                 case 'E': /* PRELIMINARY - may change */
693                         if (!is_integer (optarg))
694                                 usage2 (_("Retries interval must be a positive integer"), optarg);
695                         else
696                                 retries = atoi(optarg);
697                         break;
698                 case 'o':                                                                       /* object identifier */
699                         if ( strspn( optarg, "0123456789.," ) != strlen( optarg ) ) {
700                                         /*
701                                          * we have something other than digits, periods and comas,
702                                          * so we have a mib variable, rather than just an SNMP OID,
703                                          * so we have to actually read the mib files
704                                          */
705                                         needmibs = TRUE;
706                         }
707                         if (!oids) oids = calloc(MAX_OIDS, sizeof (char *));
708                         for (ptr = strtok(optarg, ", "); ptr != NULL && j < MAX_OIDS; ptr = strtok(NULL, ", "), j++) {
709                                 oids[j] = strdup(ptr);
710                         }
711                         numoids = j;
712                         if (c == 'E' || c == 'e') {
713                                 jj++;
714                                 ii++;
715                         }
716                         if (c == 'E')
717                                 eval_method[j+1] |= WARN_PRESENT;
718                         else if (c == 'e')
719                                 eval_method[j+1] |= CRIT_PRESENT;
720                         break;
721                 case 's':                                                                       /* string or substring */
722                         strncpy (string_value, optarg, sizeof (string_value) - 1);
723                         string_value[sizeof (string_value) - 1] = 0;
724                         eval_method[jj++] = CRIT_STRING;
725                         ii++;
726                         break;
727                 case 'R':                                                                       /* regex */
728                         cflags = REG_ICASE;
729                 case 'r':                                                                       /* regex */
730                         cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
731                         strncpy (regex_expect, optarg, sizeof (regex_expect) - 1);
732                         regex_expect[sizeof (regex_expect) - 1] = 0;
733                         errcode = regcomp (&preg, regex_expect, cflags);
734                         if (errcode != 0) {
735                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
736                                 printf (_("Could Not Compile Regular Expression"));
737                                 return ERROR;
738                         }
739                         eval_method[jj++] = CRIT_REGEX;
740                         ii++;
741                         break;
743         /* Format */
744                 case 'd':                                                                       /* delimiter */
745                         delimiter = strscpy (delimiter, optarg);
746                         break;
747                 case 'D':                                                                       /* output-delimiter */
748                         output_delim = strscpy (output_delim, optarg);
749                         break;
750                 case 'l':                                                                       /* label */
751                         nlabels++;
752                         if (nlabels >= labels_size) {
753                                 labels_size += 8;
754                                 labels = realloc (labels, labels_size);
755                                 if (labels == NULL)
756                                         die (STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels);
757                         }
758                         labels[nlabels - 1] = optarg;
759                         ptr = thisarg (optarg);
760                         labels[nlabels - 1] = ptr;
761                         if (strstr (ptr, "'") == ptr)
762                                 labels[nlabels - 1] = ptr + 1;
763                         while (ptr && (ptr = nextarg (ptr))) {
764                                 if (nlabels >= labels_size) {
765                                         labels_size += 8;
766                                         labels = realloc (labels, labels_size);
767                                         if (labels == NULL)
768                                                 die (STATE_UNKNOWN, _("Could not reallocate labels\n"));
769                                 }
770                                 labels++;
771                                 ptr = thisarg (ptr);
772                                 if (strstr (ptr, "'") == ptr)
773                                         labels[nlabels - 1] = ptr + 1;
774                                 else
775                                         labels[nlabels - 1] = ptr;
776                         }
777                         break;
778                 case 'u':                                                                       /* units */
779                         units = optarg;
780                         nunits++;
781                         if (nunits >= unitv_size) {
782                                 unitv_size += 8;
783                                 unitv = realloc (unitv, unitv_size);
784                                 if (unitv == NULL)
785                                         die (STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits);
786                         }
787                         unitv[nunits - 1] = optarg;
788                         ptr = thisarg (optarg);
789                         unitv[nunits - 1] = ptr;
790                         if (strstr (ptr, "'") == ptr)
791                                 unitv[nunits - 1] = ptr + 1;
792                         while (ptr && (ptr = nextarg (ptr))) {
793                                 if (nunits >= unitv_size) {
794                                         unitv_size += 8;
795                                         unitv = realloc (unitv, unitv_size);
796                                         if (units == NULL)
797                                                 die (STATE_UNKNOWN, _("Could not realloc() units\n"));
798                                 }
799                                 nunits++;
800                                 ptr = thisarg (ptr);
801                                 if (strstr (ptr, "'") == ptr)
802                                         unitv[nunits - 1] = ptr + 1;
803                                 else
804                                         unitv[nunits - 1] = ptr;
805                         }
806                         break;
807                 case L_CALCULATE_RATE:
808                         if(calculate_rate==0)
809                                 np_enable_state(NULL, 1);
810                         calculate_rate = 1;
811                         break;
812                 case L_RATE_MULTIPLIER:
813                         if(!is_integer(optarg)||((rate_multiplier=atoi(optarg))<=0))
814                                 usage2(_("Rate multiplier must be a positive integer"),optarg);
815                         break;
816                 case L_INVERT_SEARCH:
817                         invert_search=1;
818                         break;
819                 }
820         }
822         if (server_address == NULL)
823                 server_address = argv[optind];
825         if (community == NULL)
826                 community = strdup (DEFAULT_COMMUNITY);
828         return validate_arguments ();
832 /******************************************************************************
834 @@-
835 <sect3>
836 <title>validate_arguments</title>
838 <para>&PROTO_validate_arguments;</para>
840 <para>Checks to see if the default miblist needs to be loaded. Also verifies
841 the authentication and authorization combinations based on protocol version
842 selected.</para>
844 <para></para>
846 </sect3>
847 -@@
848 ******************************************************************************/
852 int
853 validate_arguments ()
855         /* check whether to load locally installed MIBS (CPU/disk intensive) */
856         if (miblist == NULL) {
857                 if ( needmibs == TRUE ) {
858                         miblist = strdup (DEFAULT_MIBLIST);
859                 }else{
860                         miblist = "";                   /* don't read any mib files for numeric oids */
861                 }
862         }
864         /* Check server_address is given */
865         if (server_address == NULL)
866                 die(STATE_UNKNOWN, _("No host specified\n"));
868         /* Check oid is given */
869         if (numoids == 0)
870                 die(STATE_UNKNOWN, _("No OIDs specified\n"));
872         if (proto == NULL)
873                 asprintf(&proto, DEFAULT_PROTOCOL);
875         if ((strcmp(proto,"1") == 0) || (strcmp(proto, "2c")==0)) {     /* snmpv1 or snmpv2c */
876                 numauthpriv = 2;
877                 authpriv = calloc (numauthpriv, sizeof (char *));
878                 authpriv[0] = strdup ("-c");
879                 authpriv[1] = strdup (community);
880         }
881         else if ( strcmp (proto, "3") == 0 ) {          /* snmpv3 args */
882                 if (seclevel == NULL)
883                         asprintf(&seclevel, "noAuthNoPriv");
885                 if (strcmp(seclevel, "noAuthNoPriv") == 0) {
886                         numauthpriv = 2;
887                         authpriv = calloc (numauthpriv, sizeof (char *));
888                         authpriv[0] = strdup ("-l");
889                         authpriv[1] = strdup ("noAuthNoPriv");
890                 } else {
891                         if (! ( (strcmp(seclevel, "authNoPriv")==0) || (strcmp(seclevel, "authPriv")==0) ) ) {
892                                 usage2 (_("Invalid seclevel"), seclevel);
893                         }
895                         if (authproto == NULL )
896                                 asprintf(&authproto, DEFAULT_AUTH_PROTOCOL);
898                         if (secname == NULL)
899                                 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname");
901                         if (authpasswd == NULL)
902                                 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd");
904                         if ( strcmp(seclevel, "authNoPriv") == 0 ) {
905                                 numauthpriv = 8;
906                                 authpriv = calloc (numauthpriv, sizeof (char *));
907                                 authpriv[0] = strdup ("-l");
908                                 authpriv[1] = strdup ("authNoPriv");
909                                 authpriv[2] = strdup ("-a");
910                                 authpriv[3] = strdup (authproto);
911                                 authpriv[4] = strdup ("-u");
912                                 authpriv[5] = strdup (secname);
913                                 authpriv[6] = strdup ("-A");
914                                 authpriv[7] = strdup (authpasswd);
915                         } else if ( strcmp(seclevel, "authPriv") == 0 ) {
916                                 if (privproto == NULL )
917                                         asprintf(&privproto, DEFAULT_PRIV_PROTOCOL);
919                                 if (privpasswd == NULL)
920                                         die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd");
922                                 numauthpriv = 12;
923                                 authpriv = calloc (numauthpriv, sizeof (char *));
924                                 authpriv[0] = strdup ("-l");
925                                 authpriv[1] = strdup ("authPriv");
926                                 authpriv[2] = strdup ("-a");
927                                 authpriv[3] = strdup (authproto);
928                                 authpriv[4] = strdup ("-u");
929                                 authpriv[5] = strdup (secname);
930                                 authpriv[6] = strdup ("-A");
931                                 authpriv[7] = strdup (authpasswd);
932                                 authpriv[8] = strdup ("-x");
933                                 authpriv[9] = strdup (privproto);
934                                 authpriv[10] = strdup ("-X");
935                                 authpriv[11] = strdup (privpasswd);
936                         }
937                 }
939         }
940         else {
941                 usage2 (_("Invalid SNMP version"), proto);
942         }
944         return OK;
949 /* trim leading whitespace
950          if there is a leading quote, make sure it balances */
952 char *
953 thisarg (char *str)
955         str += strspn (str, " \t\r\n"); /* trim any leading whitespace */
956         if (strstr (str, "'") == str) { /* handle SIMPLE quoted strings */
957                 if (strlen (str) == 1 || !strstr (str + 1, "'"))
958                         die (STATE_UNKNOWN, _("Unbalanced quotes\n"));
959         }
960         return str;
965 /* if there's a leading quote, advance to the trailing quote
966          set the trailing quote to '\x0'
967          if the string continues, advance beyond the comma */
969 char *
970 nextarg (char *str)
972         if (strstr (str, "'") == str) {
973                 str[0] = 0;
974                 if (strlen (str) > 1) {
975                         str = strstr (str + 1, "'");
976                         return (++str);
977                 }
978                 else {
979                         return NULL;
980                 }
981         }
982         if (strstr (str, ",") == str) {
983                 str[0] = 0;
984                 if (strlen (str) > 1) {
985                         return (++str);
986                 }
987                 else {
988                         return NULL;
989                 }
990         }
991         if ((str = strstr (str, ",")) && strlen (str) > 1) {
992                 str[0] = 0;
993                 return (++str);
994         }
995         return NULL;
1000 void
1001 print_help (void)
1003         print_revision (progname, NP_VERSION);
1005         printf (COPYRIGHT, copyright, email);
1007         printf ("%s\n", _("Check status of remote machines and obtain system information via SNMP"));
1009         printf ("\n\n");
1011         print_usage ();
1013         printf (UT_HELP_VRSN);
1014         printf (UT_EXTRA_OPTS);
1016         printf (UT_HOST_PORT, 'p', DEFAULT_PORT);
1018         /* SNMP and Authentication Protocol */
1019         printf (" %s\n", "-n, --next");
1020         printf ("    %s\n", _("Use SNMP GETNEXT instead of SNMP GET"));
1021         printf (" %s\n", "-P, --protocol=[1|2c|3]");
1022         printf ("    %s\n", _("SNMP protocol version"));
1023         printf (" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]");
1024         printf ("    %s\n", _("SNMPv3 securityLevel"));
1025         printf (" %s\n", "-a, --authproto=[MD5|SHA]");
1026         printf ("    %s\n", _("SNMPv3 auth proto"));
1027         printf (" %s\n", "-x, --privproto=[DES|AES]");
1028         printf ("    %s\n", _("SNMPv3 priv proto (default DES)"));
1030         /* Authentication Tokens*/
1031         printf (" %s\n", "-C, --community=STRING");
1032         printf ("    %s ", _("Optional community string for SNMP communication"));
1033         printf ("(%s \"%s\")\n", _("default is") ,DEFAULT_COMMUNITY);
1034         printf (" %s\n", "-U, --secname=USERNAME");
1035         printf ("    %s\n", _("SNMPv3 username"));
1036         printf (" %s\n", "-A, --authpassword=PASSWORD");
1037         printf ("    %s\n", _("SNMPv3 authentication password"));
1038         printf (" %s\n", "-X, --privpasswd=PASSWORD");
1039         printf ("    %s\n", _("SNMPv3 privacy password"));
1041         /* OID Stuff */
1042         printf (" %s\n", "-o, --oid=OID(s)");
1043         printf ("    %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query"));
1044         printf (" %s\n", "-m, --miblist=STRING");
1045         printf ("    %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'"));
1046         printf ("    %s\n", _("for symbolic OIDs.)"));
1047         printf (" %s\n", "-d, --delimiter=STRING");
1048         printf ("    %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER);
1049         printf ("    %s\n", _("Any data on the right hand side of the delimiter is considered"));
1050         printf ("    %s\n", _("to be the data that should be used in the evaluation."));
1052         /* Tests Against Integers */
1053         printf (" %s\n", "-w, --warning=THRESHOLD(s)");
1054         printf ("    %s\n", _("Warning threshold range(s)"));
1055         printf (" %s\n", "-c, --critical=THRESHOLD(s)");
1056         printf ("    %s\n", _("Critical threshold range(s)"));
1057         printf (" %s\n", "--rate");
1058         printf ("    %s\n", _("Enable rate calculation. See 'Rate Calculation' below"));
1059         printf (" %s\n", "--rate-multiplier");
1060         printf ("    %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute"));
1062         /* Tests Against Strings */
1063         printf (" %s\n", "-s, --string=STRING");
1064         printf ("    %s\n", _("Return OK state (for that OID) if STRING is an exact match"));
1065         printf (" %s\n", "-r, --ereg=REGEX");
1066         printf ("    %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches"));
1067         printf (" %s\n", "-R, --eregi=REGEX");
1068         printf ("    %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches"));
1069         printf (" %s\n", "--invert-search");
1070         printf ("    %s\n", _("Invert search result (CRITICAL if found)"));
1072         /* Output Formatting */
1073         printf (" %s\n", "-l, --label=STRING");
1074         printf ("    %s\n", _("Prefix label for output from plugin"));
1075         printf (" %s\n", "-u, --units=STRING");
1076         printf ("    %s\n", _("Units label(s) for output data (e.g., 'sec.')."));
1077         printf (" %s\n", "-D, --output-delimiter=STRING");
1078         printf ("    %s\n", _("Separates output on multiple OID requests"));
1080         printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1081         printf (" %s\n", "-e, --retries=INTEGER");
1082         printf ("    %s\n", _("Number of retries to be used in the requests"));
1084         printf (UT_VERBOSE);
1086         printf ("\n");
1087         printf ("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package."));
1088         printf ("%s\n", _("if you don't have the package installed, you will need to download it from"));
1089         printf ("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1091         printf ("\n");
1092         printf ("%s\n", _("Notes:"));
1093         printf (" %s\n", _("- Multiple OIDs may be indicated by a comma or space-delimited list (lists with"));
1094         printf ("   %s %i %s\n", _("internal spaces must be quoted). Maximum:"), MAX_OIDS, _("OIDs."));
1096         printf(" -%s", UT_THRESHOLDS_NOTES);
1098         printf (" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'"));
1099         printf (" %s\n", _("- Note that only one string and one regex may be checked at present"));
1100         printf (" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value"));
1101         printf ("   %s\n", _("returned from the SNMP query is an unsigned integer."));
1103         printf("\n");
1104         printf("%s\n", _("Rate Calculation:"));
1105         printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when"));
1106         printf(" %s\n", _("calculating the counter difference since the last check. check_snmp"));
1107         printf(" %s\n", _("saves the last state information in a file so that the rate per second"));
1108         printf(" %s\n", _("can be calculated. Use the --rate option to save state information."));
1109         printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK."));
1110         printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so"));
1111         printf(" %s\n", _("changing the arguments will create a new state file."));
1113         printf (UT_SUPPORT);
1118 void
1119 print_usage (void)
1121         printf ("%s\n", _("Usage:"));
1122         printf ("%s -H <ip_address> -o <OID> [-w warn_range] [-c crit_range]\n",progname);
1123         printf ("[-C community] [-s string] [-r regex] [-R regexi] [-t timeout] [-e retries]\n");
1124         printf ("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n");
1125         printf ("[-m miblist] [-P snmp version] [-L seclevel] [-U secname] [-a authproto]\n");
1126         printf ("[-A authpasswd] [-x privproto] [-X privpasswd]\n");