Code

check_snmp: Remove that is_numeric madness
[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         time_t current_time;
174         double temp_double;
175         time_t duration;
176         char *conv = "12345678";
177         int is_counter=0;
179         setlocale (LC_ALL, "");
180         bindtextdomain (PACKAGE, LOCALEDIR);
181         textdomain (PACKAGE);
183         labels = malloc (labels_size);
184         unitv = malloc (unitv_size);
185         for (i = 0; i < MAX_OIDS; i++)
186                 eval_method[i] = CHECK_UNDEF;
188         label = strdup ("SNMP");
189         units = strdup ("");
190         port = strdup (DEFAULT_PORT);
191         outbuff = strdup ("");
192         delimiter = strdup (" = ");
193         output_delim = strdup (DEFAULT_OUTPUT_DELIMITER);
194         timeout_interval = DEFAULT_TIMEOUT;
195         retries = DEFAULT_RETRIES;
197         np_init( (char *) progname, argc, argv );
199         /* Parse extra opts if any */
200         argv=np_extra_opts (&argc, argv, progname);
202         np_set_args(argc, argv);
204         if (process_arguments (argc, argv) == ERROR)
205                 usage4 (_("Could not parse arguments"));
207         if(calculate_rate) {
208                 if (!strcmp(label, "SNMP"))
209                         label = strdup("SNMP RATE");
210                 time(&current_time);
211                 i=0;
212                 previous_state = np_state_read();
213                 if(previous_state!=NULL) {
214                         /* Split colon separated values */
215                         previous_string = strdup((char *) previous_state->data);
216                         while((ap = strsep(&previous_string, ":")) != NULL) {
217                                 if(verbose>2)
218                                         printf("State for %d=%s\n", i, ap);
219                                 previous_value[i++]=strtod(ap,NULL);
220                         }
221                 }
222         }
224         /* Populate the thresholds */
225         th_warn=warning_thresholds;
226         th_crit=critical_thresholds;
227         for (i=0; i<numoids; i++) {
228                 char *w = th_warn ? strndup(th_warn, strcspn(th_warn, ",")) : NULL;
229                 char *c = th_crit ? strndup(th_crit, strcspn(th_crit, ",")) : NULL;
230                 /* Skip empty thresholds, while avoiding segfault */
231                 set_thresholds(&thlds[i],
232                                w ? strpbrk(w, NP_THRESHOLDS_CHARS) : NULL,
233                                c ? strpbrk(c, NP_THRESHOLDS_CHARS) : NULL);
234                 if (w) {
235                         th_warn=strchr(th_warn, ',');
236                         if (th_warn) th_warn++;
237                         free(w);
238                 }
239                 if (c) {
240                         th_crit=strchr(th_crit, ',');
241                         if (th_crit) th_crit++;
242                         free(c);
243                 }
244         }
246         /* Create the command array to execute */
247         if(usesnmpgetnext == TRUE) {
248                 snmpcmd = strdup (PATH_TO_SNMPGETNEXT);
249         }else{
250                 snmpcmd = strdup (PATH_TO_SNMPGET);
251         }
253         /* 9 arguments to pass before authpriv options + 1 for host and numoids. Add one for terminating NULL */
254         command_line = calloc (9 + numauthpriv + 1 + numoids + 1, sizeof (char *));
255         command_line[0] = snmpcmd;
256         command_line[1] = strdup ("-t");
257         asprintf (&command_line[2], "%d", timeout_interval);
258         command_line[3] = strdup ("-r");
259         asprintf (&command_line[4], "%d", retries);
260         command_line[5] = strdup ("-m");
261         command_line[6] = strdup (miblist);
262         command_line[7] = "-v";
263         command_line[8] = strdup (proto);
265         for (i = 0; i < numauthpriv; i++) {
266                 command_line[9 + i] = authpriv[i];
267         }
269         asprintf (&command_line[9 + numauthpriv], "%s:%s", server_address, port);
271         /* This is just for display purposes, so it can remain a string */
272         asprintf(&cl_hidden_auth, "%s -t %d -r %d -m %s -v %s %s %s:%s",
273                 snmpcmd, timeout_interval, retries, strlen(miblist) ? miblist : "''", proto, "[authpriv]",
274                 server_address, port);
276         for (i = 0; i < numoids; i++) {
277                 command_line[9 + numauthpriv + 1 + i] = oids[i];
278                 asprintf(&cl_hidden_auth, "%s %s", cl_hidden_auth, oids[i]);    
279         }
281         command_line[9 + numauthpriv + 1 + numoids] = NULL;
283         if (verbose)
284                 printf ("%s\n", cl_hidden_auth);
286         /* Run the command */
287         return_code = cmd_run_array (command_line, &chld_out, &chld_err, 0);
289         /* Due to net-snmp sometimes showing stderr messages with poorly formed MIBs,
290            only return state unknown if return code is non zero or there is no stdout.
291            Do this way so that if there is stderr, will get added to output, which helps problem diagnosis
292         */
293         if (return_code != 0)
294                 external_error=1;
295         if (chld_out.lines == 0)
296                 external_error=1;
297         if (external_error) {
298                 if (chld_err.lines > 0) {
299                         printf (_("External command error: %s\n"), chld_err.line[0]);
300                         for (i = 1; i < chld_err.lines; i++) {
301                                 printf ("%s\n", chld_err.line[i]);
302                         }
303                 } else {
304                         printf(_("External command error with no output (return code: %d)\n"), return_code);
305                 }
306                 exit (STATE_UNKNOWN);
307         }
309         if (verbose) {
310                 for (i = 0; i < chld_out.lines; i++) {
311                         printf ("%s\n", chld_out.line[i]);
312                 }
313         }
315         for (line=0, i=0; line < chld_out.lines; line++, i++) {
316                 if(calculate_rate)
317                         conv = "%.10g";
318                 else
319                         conv = "%.0f";
321                 ptr = chld_out.line[line];
322                 oidname = strpcpy (oidname, ptr, delimiter);
323                 response = strstr (ptr, delimiter);
324                 if (response == NULL)
325                         break;
327                 if (verbose > 2) {
328                         printf("Processing oid %i (line %i)\n  oidname: %s\n  response: %s\n", i+1, line+1, oidname, response);
329                 }
331                 /* Clean up type array - Sol10 does not necessarily zero it out */
332                 bzero(type, sizeof(type));
334                 is_counter=0;
335                 /* We strip out the datatype indicator for PHBs */
336                 if (strstr (response, "Gauge: ")) {
337                         show = strstr (response, "Gauge: ") + 7;
338                 } 
339                 else if (strstr (response, "Gauge32: ")) {
340                         show = strstr (response, "Gauge32: ") + 9;
341                 } 
342                 else if (strstr (response, "Counter32: ")) {
343                         show = strstr (response, "Counter32: ") + 11;
344                         is_counter=1;
345                         if(!calculate_rate) 
346                                 strcpy(type, "c");
347                 }
348                 else if (strstr (response, "Counter64: ")) {
349                         show = strstr (response, "Counter64: ") + 11;
350                         is_counter=1;
351                         if(!calculate_rate)
352                                 strcpy(type, "c");
353                 }
354                 else if (strstr (response, "INTEGER: ")) {
355                         show = strstr (response, "INTEGER: ") + 9;
356                 }
357                 else if (strstr (response, "STRING: ")) {
358                         show = strstr (response, "STRING: ") + 8;
359                         conv = "%.10g";
361                         /* Get the rest of the string on multi-line strings */
362                         ptr = show;
363                         COUNT_SEQ(ptr, bk_count, dq_count)
364                         while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
365                                 ptr++;
366                                 GOBBLE_TOS(ptr, "\n\"\\")
367                                 COUNT_SEQ(ptr, bk_count, dq_count)
368                         }
370                         if (dq_count) { /* unfinished line */
371                                 /* copy show verbatim first */
372                                 if (!mult_resp) mult_resp = strdup("");
373                                 asprintf (&mult_resp, "%s%s:\n%s\n", mult_resp, oids[i], show);
374                                 /* then strip out unmatched double-quote from single-line output */
375                                 if (show[0] == '"') show++;
377                                 /* Keep reading until we match end of double-quoted string */
378                                 for (line++; line < chld_out.lines; line++) {
379                                         ptr = chld_out.line[line];
380                                         asprintf (&mult_resp, "%s%s\n", mult_resp, ptr);
382                                         COUNT_SEQ(ptr, bk_count, dq_count)
383                                         while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
384                                                 ptr++;
385                                                 GOBBLE_TOS(ptr, "\n\"\\")
386                                                 COUNT_SEQ(ptr, bk_count, dq_count)
387                                         }
388                                         /* Break for loop before next line increment when done */
389                                         if (!dq_count) break;
390                                 }
391                         }
393                         /* Allow numeric conversion if whole string is a number. Make concession for strings with " at beginning or end */
394                         /* This duplicates the conversion a bit later, but is cleaner to separate out the checking against the conversion */
395                         ptr = show;
396                         if (*ptr == '"')
397                                 ptr++;
398                         if (*ptr != '\0' ) {
399                                 strtod( ptr, &endptr );
400                                 if (*endptr == '"')
401                                         endptr++;
402                                 if (*endptr == '\0')
403                                         is_numeric=1;
404                         }
406                 }
407                 else if (strstr (response, "Timeticks: ")) {
408                         show = strstr (response, "Timeticks: ");
409                 }
410                 else
411                         show = response;
413                 iresult = STATE_DEPENDENT;
415                 /* Process this block for numeric comparisons */
416                 /* Make some special values,like Timeticks numeric only if a threshold is defined */
417                 if (thlds[i]->warning || thlds[i]->critical || calculate_rate) {
418                         ptr = strpbrk (show, "0123456789");
419                         if (ptr == NULL)
420                                 die (STATE_UNKNOWN,_("No valid data returned"));
421                         response_value[i] = strtod (ptr, NULL);
423                         if(calculate_rate) {
424                                 if (previous_state!=NULL) {
425                                         duration = current_time-previous_state->time;
426                                         if(duration<=0)
427                                                 die(STATE_UNKNOWN,_("Time duration between plugin calls is invalid"));
428                                         temp_double = response_value[i]-previous_value[i];
429                                         /* Simple overflow catcher (same as in rrdtool, rrd_update.c) */
430                                         if(is_counter) {
431                                                 if(temp_double<(double)0.0)
432                                                         temp_double+=(double)4294967296.0; /* 2^32 */
433                                                 if(temp_double<(double)0.0)
434                                                         temp_double+=(double)18446744069414584320.0; /* 2^64-2^32 */;
435                                         }
436                                         /* Convert to per second, then use multiplier */
437                                         temp_double = temp_double/duration*rate_multiplier;
438                                         iresult = get_status(temp_double, thlds[i]);
439                                         asprintf (&show, conv, temp_double);
440                                 }
441                         } else {
442                                 iresult = get_status(response_value[i], thlds[i]);
443                                 asprintf (&show, conv, response_value[i]);
444                         }
445                 }
447                 /* Process this block for string matching */
448                 else if (eval_method[i] & CRIT_STRING) {
449                         if (strcmp (show, string_value))
450                                 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK;
451                         else
452                                 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
453                 }
455                 /* Process this block for regex matching */
456                 else if (eval_method[i] & CRIT_REGEX) {
457                         excode = regexec (&preg, response, 10, pmatch, eflags);
458                         if (excode == 0) {
459                                 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
460                         }
461                         else if (excode != REG_NOMATCH) {
462                                 regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER);
463                                 printf (_("Execute Error: %s\n"), errbuf);
464                                 exit (STATE_CRITICAL);
465                         }
466                         else {
467                                 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK;
468                         }
469                 }
471                 /* Process this block for existence-nonexistence checks */
472                 /* TV: Should this be outside of this else block? */
473                 else {
474                         if (eval_method[i] & CRIT_PRESENT)
475                                 iresult = STATE_CRITICAL;
476                         else if (eval_method[i] & WARN_PRESENT)
477                                 iresult = STATE_WARNING;
478                         else if (response && iresult == STATE_DEPENDENT)
479                                 iresult = STATE_OK;
480                 }
482                 /* Result is the worst outcome of all the OIDs tested */
483                 result = max_state (result, iresult);
485                 /* Prepend a label for this OID if there is one */
486                 if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
487                         asprintf (&outbuff, "%s%s%s %s%s%s", outbuff,
488                                 (i == 0) ? " " : output_delim,
489                                 labels[i], mark (iresult), show, mark (iresult));
490                 else
491                         asprintf (&outbuff, "%s%s%s%s%s", outbuff, (i == 0) ? " " : output_delim,
492                                 mark (iresult), show, mark (iresult));
494                 /* Append a unit string for this OID if there is one */
495                 if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL)
496                         asprintf (&outbuff, "%s %s", outbuff, unitv[i]);
498                 /* Write perfdata with whatever can be parsed by strtod, if possible */
499                 ptr = NULL;
500                 strtod(show, &ptr);
501                 if (ptr > show) {
502                         if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
503                                 temp_string=labels[i];
504                         else
505                                 temp_string=oidname;
506                         strncat(perfstr, temp_string, sizeof(perfstr)-strlen(perfstr)-1);
507                         strncat(perfstr, "=", sizeof(perfstr)-strlen(perfstr)-1);
508                         len = sizeof(perfstr)-strlen(perfstr)-1;
509                         strncat(perfstr, show, len>ptr-show ? ptr-show : len);
511                         if (type)
512                                 strncat(perfstr, type, sizeof(perfstr)-strlen(perfstr)-1);
513                         strncat(perfstr, " ", sizeof(perfstr)-strlen(perfstr)-1);
514                 }
515         }
516         total_oids=i;
518         /* Save state data, as all data collected now */
519         if(calculate_rate) {
520                 string_length=1024;
521                 state_string=malloc(string_length);
522                 if(state_string==NULL)
523                         die(STATE_UNKNOWN, _("Cannot malloc"));
524                 
525                 current_length=0;
526                 for(i=0; i<total_oids; i++) {
527                         asprintf(&temp_string,"%.0f",response_value[i]);
528                         if(temp_string==NULL)
529                                 die(STATE_UNKNOWN,_("Cannot asprintf()"));
530                         response_length = strlen(temp_string);
531                         if(current_length+response_length>string_length) {
532                                 string_length=current_length+1024;
533                                 state_string=realloc(state_string,string_length);
534                                 if(state_string==NULL)
535                                         die(STATE_UNKNOWN, _("Cannot realloc()"));
536                         }
537                         strcpy(&state_string[current_length],temp_string);
538                         current_length=current_length+response_length;
539                         state_string[current_length]=':';
540                         current_length++;
541                         free(temp_string);
542                 }
543                 state_string[--current_length]='\0';
544                 if (verbose > 2)
545                         printf("State string=%s\n",state_string);
546                 
547                 /* This is not strictly the same as time now, but any subtle variations will cancel out */
548                 np_state_write_string(current_time, state_string );
549                 if(previous_state==NULL) {
550                         /* Or should this be highest state? */
551                         die( STATE_OK, _("No previous data to calculate rate - assume okay" ) );
552                 }
553         }
555         printf ("%s %s -%s %s\n", label, state_text (result), outbuff, perfstr);
556         if (mult_resp) printf ("%s", mult_resp);
558         return result;
563 /* process command-line arguments */
564 int
565 process_arguments (int argc, char **argv)
567         char *ptr;
568         int c = 1;
569         int j = 0, jj = 0, ii = 0;
571         int option = 0;
572         static struct option longopts[] = {
573                 STD_LONG_OPTS,
574                 {"community", required_argument, 0, 'C'},
575                 {"oid", required_argument, 0, 'o'},
576                 {"object", required_argument, 0, 'o'},
577                 {"delimiter", required_argument, 0, 'd'},
578                 {"output-delimiter", required_argument, 0, 'D'},
579                 {"string", required_argument, 0, 's'},
580                 {"timeout", required_argument, 0, 't'},
581                 {"regex", required_argument, 0, 'r'},
582                 {"ereg", required_argument, 0, 'r'},
583                 {"eregi", required_argument, 0, 'R'},
584                 {"label", required_argument, 0, 'l'},
585                 {"units", required_argument, 0, 'u'},
586                 {"port", required_argument, 0, 'p'},
587                 {"retries", required_argument, 0, 'e'},
588                 {"miblist", required_argument, 0, 'm'},
589                 {"protocol", required_argument, 0, 'P'},
590                 {"seclevel", required_argument, 0, 'L'},
591                 {"secname", required_argument, 0, 'U'},
592                 {"authproto", required_argument, 0, 'a'},
593                 {"privproto", required_argument, 0, 'x'},
594                 {"authpasswd", required_argument, 0, 'A'},
595                 {"privpasswd", required_argument, 0, 'X'},
596                 {"next", no_argument, 0, 'n'},
597                 {"rate", no_argument, 0, L_CALCULATE_RATE},
598                 {"rate-multiplier", required_argument, 0, L_RATE_MULTIPLIER},
599                 {"invert-search", no_argument, 0, L_INVERT_SEARCH},
600                 {0, 0, 0, 0}
601         };
603         if (argc < 2)
604                 return ERROR;
606         /* reverse compatibility for very old non-POSIX usage forms */
607         for (c = 1; c < argc; c++) {
608                 if (strcmp ("-to", argv[c]) == 0)
609                         strcpy (argv[c], "-t");
610                 if (strcmp ("-wv", argv[c]) == 0)
611                         strcpy (argv[c], "-w");
612                 if (strcmp ("-cv", argv[c]) == 0)
613                         strcpy (argv[c], "-c");
614         }
616         while (1) {
617                 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:",
618                                                                          longopts, &option);
620                 if (c == -1 || c == EOF)
621                         break;
623                 switch (c) {
624                 case '?':       /* usage */
625                         usage5 ();
626                 case 'h':       /* help */
627                         print_help ();
628                         exit (STATE_OK);
629                 case 'V':       /* version */
630                         print_revision (progname, NP_VERSION);
631                         exit (STATE_OK);
632                 case 'v': /* verbose */
633                         verbose++;
634                         break;
636         /* Connection info */
637                 case 'C':                                                                       /* group or community */
638                         community = optarg;
639                         break;
640                 case 'H':                                                                       /* Host or server */
641                         server_address = optarg;
642                         break;
643                 case 'p':       /* TCP port number */
644                         port = optarg;
645                         break;
646                 case 'm':       /* List of MIBS */
647                         miblist = optarg;
648                         break;
649                 case 'n':       /* usesnmpgetnext */
650                         usesnmpgetnext = TRUE;
651                         break;
652                 case 'P':       /* SNMP protocol version */
653                         proto = optarg;
654                         break;
655                 case 'L':       /* security level */
656                         seclevel = optarg;
657                         break;
658                 case 'U':       /* security username */
659                         secname = optarg;
660                         break;
661                 case 'a':       /* auth protocol */
662                         authproto = optarg;
663                         break;
664                 case 'x':       /* priv protocol */
665                         privproto = optarg;
666                         break;
667                 case 'A':       /* auth passwd */
668                         authpasswd = optarg;
669                         break;
670                 case 'X':       /* priv passwd */
671                         privpasswd = optarg;
672                         break;
673                 case 't':       /* timeout period */
674                         if (!is_integer (optarg))
675                                 usage2 (_("Timeout interval must be a positive integer"), optarg);
676                         else
677                                 timeout_interval = atoi (optarg);
678                         break;
680         /* Test parameters */
681                 case 'c':                                                                       /* critical threshold */
682                         critical_thresholds = optarg;
683                         break;
684                 case 'w':                                                                       /* warning threshold */
685                         warning_thresholds = optarg;
686                         break;
687                 case 'e': /* PRELIMINARY - may change */
688                 case 'E': /* PRELIMINARY - may change */
689                         if (!is_integer (optarg))
690                                 usage2 (_("Retries interval must be a positive integer"), optarg);
691                         else
692                                 retries = atoi(optarg);
693                         break;
694                 case 'o':                                                                       /* object identifier */
695                         if ( strspn( optarg, "0123456789.," ) != strlen( optarg ) ) {
696                                         /*
697                                          * we have something other than digits, periods and comas,
698                                          * so we have a mib variable, rather than just an SNMP OID,
699                                          * so we have to actually read the mib files
700                                          */
701                                         needmibs = TRUE;
702                         }
703                         if (!oids) oids = calloc(MAX_OIDS, sizeof (char *));
704                         for (ptr = strtok(optarg, ", "); ptr != NULL && j < MAX_OIDS; ptr = strtok(NULL, ", "), j++) {
705                                 oids[j] = strdup(ptr);
706                         }
707                         numoids = j;
708                         if (c == 'E' || c == 'e') {
709                                 jj++;
710                                 ii++;
711                         }
712                         if (c == 'E')
713                                 eval_method[j+1] |= WARN_PRESENT;
714                         else if (c == 'e')
715                                 eval_method[j+1] |= CRIT_PRESENT;
716                         break;
717                 case 's':                                                                       /* string or substring */
718                         strncpy (string_value, optarg, sizeof (string_value) - 1);
719                         string_value[sizeof (string_value) - 1] = 0;
720                         eval_method[jj++] = CRIT_STRING;
721                         ii++;
722                         break;
723                 case 'R':                                                                       /* regex */
724                         cflags = REG_ICASE;
725                 case 'r':                                                                       /* regex */
726                         cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
727                         strncpy (regex_expect, optarg, sizeof (regex_expect) - 1);
728                         regex_expect[sizeof (regex_expect) - 1] = 0;
729                         errcode = regcomp (&preg, regex_expect, cflags);
730                         if (errcode != 0) {
731                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
732                                 printf (_("Could Not Compile Regular Expression"));
733                                 return ERROR;
734                         }
735                         eval_method[jj++] = CRIT_REGEX;
736                         ii++;
737                         break;
739         /* Format */
740                 case 'd':                                                                       /* delimiter */
741                         delimiter = strscpy (delimiter, optarg);
742                         break;
743                 case 'D':                                                                       /* output-delimiter */
744                         output_delim = strscpy (output_delim, optarg);
745                         break;
746                 case 'l':                                                                       /* label */
747                         nlabels++;
748                         if (nlabels >= labels_size) {
749                                 labels_size += 8;
750                                 labels = realloc (labels, labels_size);
751                                 if (labels == NULL)
752                                         die (STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels);
753                         }
754                         labels[nlabels - 1] = optarg;
755                         ptr = thisarg (optarg);
756                         labels[nlabels - 1] = ptr;
757                         if (strstr (ptr, "'") == ptr)
758                                 labels[nlabels - 1] = ptr + 1;
759                         while (ptr && (ptr = nextarg (ptr))) {
760                                 if (nlabels >= labels_size) {
761                                         labels_size += 8;
762                                         labels = realloc (labels, labels_size);
763                                         if (labels == NULL)
764                                                 die (STATE_UNKNOWN, _("Could not reallocate labels\n"));
765                                 }
766                                 labels++;
767                                 ptr = thisarg (ptr);
768                                 if (strstr (ptr, "'") == ptr)
769                                         labels[nlabels - 1] = ptr + 1;
770                                 else
771                                         labels[nlabels - 1] = ptr;
772                         }
773                         break;
774                 case 'u':                                                                       /* units */
775                         units = optarg;
776                         nunits++;
777                         if (nunits >= unitv_size) {
778                                 unitv_size += 8;
779                                 unitv = realloc (unitv, unitv_size);
780                                 if (unitv == NULL)
781                                         die (STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits);
782                         }
783                         unitv[nunits - 1] = optarg;
784                         ptr = thisarg (optarg);
785                         unitv[nunits - 1] = ptr;
786                         if (strstr (ptr, "'") == ptr)
787                                 unitv[nunits - 1] = ptr + 1;
788                         while (ptr && (ptr = nextarg (ptr))) {
789                                 if (nunits >= unitv_size) {
790                                         unitv_size += 8;
791                                         unitv = realloc (unitv, unitv_size);
792                                         if (units == NULL)
793                                                 die (STATE_UNKNOWN, _("Could not realloc() units\n"));
794                                 }
795                                 nunits++;
796                                 ptr = thisarg (ptr);
797                                 if (strstr (ptr, "'") == ptr)
798                                         unitv[nunits - 1] = ptr + 1;
799                                 else
800                                         unitv[nunits - 1] = ptr;
801                         }
802                         break;
803                 case L_CALCULATE_RATE:
804                         if(calculate_rate==0)
805                                 np_enable_state(NULL, 1);
806                         calculate_rate = 1;
807                         break;
808                 case L_RATE_MULTIPLIER:
809                         if(!is_integer(optarg)||((rate_multiplier=atoi(optarg))<=0))
810                                 usage2(_("Rate multiplier must be a positive integer"),optarg);
811                         break;
812                 case L_INVERT_SEARCH:
813                         invert_search=1;
814                         break;
815                 }
816         }
818         if (server_address == NULL)
819                 server_address = argv[optind];
821         if (community == NULL)
822                 community = strdup (DEFAULT_COMMUNITY);
824         return validate_arguments ();
828 /******************************************************************************
830 @@-
831 <sect3>
832 <title>validate_arguments</title>
834 <para>&PROTO_validate_arguments;</para>
836 <para>Checks to see if the default miblist needs to be loaded. Also verifies
837 the authentication and authorization combinations based on protocol version
838 selected.</para>
840 <para></para>
842 </sect3>
843 -@@
844 ******************************************************************************/
848 int
849 validate_arguments ()
851         /* check whether to load locally installed MIBS (CPU/disk intensive) */
852         if (miblist == NULL) {
853                 if ( needmibs == TRUE ) {
854                         miblist = strdup (DEFAULT_MIBLIST);
855                 }else{
856                         miblist = "";                   /* don't read any mib files for numeric oids */
857                 }
858         }
860         /* Check server_address is given */
861         if (server_address == NULL)
862                 die(STATE_UNKNOWN, _("No host specified\n"));
864         /* Check oid is given */
865         if (numoids == 0)
866                 die(STATE_UNKNOWN, _("No OIDs specified\n"));
868         if (proto == NULL)
869                 asprintf(&proto, DEFAULT_PROTOCOL);
871         if ((strcmp(proto,"1") == 0) || (strcmp(proto, "2c")==0)) {     /* snmpv1 or snmpv2c */
872                 numauthpriv = 2;
873                 authpriv = calloc (numauthpriv, sizeof (char *));
874                 authpriv[0] = strdup ("-c");
875                 authpriv[1] = strdup (community);
876         }
877         else if ( strcmp (proto, "3") == 0 ) {          /* snmpv3 args */
878                 if (seclevel == NULL)
879                         asprintf(&seclevel, "noAuthNoPriv");
881                 if (strcmp(seclevel, "noAuthNoPriv") == 0) {
882                         numauthpriv = 2;
883                         authpriv = calloc (numauthpriv, sizeof (char *));
884                         authpriv[0] = strdup ("-l");
885                         authpriv[1] = strdup ("noAuthNoPriv");
886                 } else {
887                         if (! ( (strcmp(seclevel, "authNoPriv")==0) || (strcmp(seclevel, "authPriv")==0) ) ) {
888                                 usage2 (_("Invalid seclevel"), seclevel);
889                         }
891                         if (authproto == NULL )
892                                 asprintf(&authproto, DEFAULT_AUTH_PROTOCOL);
894                         if (secname == NULL)
895                                 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname");
897                         if (authpasswd == NULL)
898                                 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd");
900                         if ( strcmp(seclevel, "authNoPriv") == 0 ) {
901                                 numauthpriv = 8;
902                                 authpriv = calloc (numauthpriv, sizeof (char *));
903                                 authpriv[0] = strdup ("-l");
904                                 authpriv[1] = strdup ("authNoPriv");
905                                 authpriv[2] = strdup ("-a");
906                                 authpriv[3] = strdup (authproto);
907                                 authpriv[4] = strdup ("-u");
908                                 authpriv[5] = strdup (secname);
909                                 authpriv[6] = strdup ("-A");
910                                 authpriv[7] = strdup (authpasswd);
911                         } else if ( strcmp(seclevel, "authPriv") == 0 ) {
912                                 if (privproto == NULL )
913                                         asprintf(&privproto, DEFAULT_PRIV_PROTOCOL);
915                                 if (privpasswd == NULL)
916                                         die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd");
918                                 numauthpriv = 12;
919                                 authpriv = calloc (numauthpriv, sizeof (char *));
920                                 authpriv[0] = strdup ("-l");
921                                 authpriv[1] = strdup ("authPriv");
922                                 authpriv[2] = strdup ("-a");
923                                 authpriv[3] = strdup (authproto);
924                                 authpriv[4] = strdup ("-u");
925                                 authpriv[5] = strdup (secname);
926                                 authpriv[6] = strdup ("-A");
927                                 authpriv[7] = strdup (authpasswd);
928                                 authpriv[8] = strdup ("-x");
929                                 authpriv[9] = strdup (privproto);
930                                 authpriv[10] = strdup ("-X");
931                                 authpriv[11] = strdup (privpasswd);
932                         }
933                 }
935         }
936         else {
937                 usage2 (_("Invalid SNMP version"), proto);
938         }
940         return OK;
945 /* trim leading whitespace
946          if there is a leading quote, make sure it balances */
948 char *
949 thisarg (char *str)
951         str += strspn (str, " \t\r\n"); /* trim any leading whitespace */
952         if (strstr (str, "'") == str) { /* handle SIMPLE quoted strings */
953                 if (strlen (str) == 1 || !strstr (str + 1, "'"))
954                         die (STATE_UNKNOWN, _("Unbalanced quotes\n"));
955         }
956         return str;
961 /* if there's a leading quote, advance to the trailing quote
962          set the trailing quote to '\x0'
963          if the string continues, advance beyond the comma */
965 char *
966 nextarg (char *str)
968         if (strstr (str, "'") == str) {
969                 str[0] = 0;
970                 if (strlen (str) > 1) {
971                         str = strstr (str + 1, "'");
972                         return (++str);
973                 }
974                 else {
975                         return NULL;
976                 }
977         }
978         if (strstr (str, ",") == str) {
979                 str[0] = 0;
980                 if (strlen (str) > 1) {
981                         return (++str);
982                 }
983                 else {
984                         return NULL;
985                 }
986         }
987         if ((str = strstr (str, ",")) && strlen (str) > 1) {
988                 str[0] = 0;
989                 return (++str);
990         }
991         return NULL;
996 void
997 print_help (void)
999         print_revision (progname, NP_VERSION);
1001         printf (COPYRIGHT, copyright, email);
1003         printf ("%s\n", _("Check status of remote machines and obtain system information via SNMP"));
1005         printf ("\n\n");
1007         print_usage ();
1009         printf (UT_HELP_VRSN);
1010         printf (UT_EXTRA_OPTS);
1012         printf (UT_HOST_PORT, 'p', DEFAULT_PORT);
1014         /* SNMP and Authentication Protocol */
1015         printf (" %s\n", "-n, --next");
1016         printf ("    %s\n", _("Use SNMP GETNEXT instead of SNMP GET"));
1017         printf (" %s\n", "-P, --protocol=[1|2c|3]");
1018         printf ("    %s\n", _("SNMP protocol version"));
1019         printf (" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]");
1020         printf ("    %s\n", _("SNMPv3 securityLevel"));
1021         printf (" %s\n", "-a, --authproto=[MD5|SHA]");
1022         printf ("    %s\n", _("SNMPv3 auth proto"));
1023         printf (" %s\n", "-x, --privproto=[DES|AES]");
1024         printf ("    %s\n", _("SNMPv3 priv proto (default DES)"));
1026         /* Authentication Tokens*/
1027         printf (" %s\n", "-C, --community=STRING");
1028         printf ("    %s ", _("Optional community string for SNMP communication"));
1029         printf ("(%s \"%s\")\n", _("default is") ,DEFAULT_COMMUNITY);
1030         printf (" %s\n", "-U, --secname=USERNAME");
1031         printf ("    %s\n", _("SNMPv3 username"));
1032         printf (" %s\n", "-A, --authpassword=PASSWORD");
1033         printf ("    %s\n", _("SNMPv3 authentication password"));
1034         printf (" %s\n", "-X, --privpasswd=PASSWORD");
1035         printf ("    %s\n", _("SNMPv3 privacy password"));
1037         /* OID Stuff */
1038         printf (" %s\n", "-o, --oid=OID(s)");
1039         printf ("    %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query"));
1040         printf (" %s\n", "-m, --miblist=STRING");
1041         printf ("    %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'"));
1042         printf ("    %s\n", _("for symbolic OIDs.)"));
1043         printf (" %s\n", "-d, --delimiter=STRING");
1044         printf ("    %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER);
1045         printf ("    %s\n", _("Any data on the right hand side of the delimiter is considered"));
1046         printf ("    %s\n", _("to be the data that should be used in the evaluation."));
1048         /* Tests Against Integers */
1049         printf (" %s\n", "-w, --warning=THRESHOLD(s)");
1050         printf ("    %s\n", _("Warning threshold range(s)"));
1051         printf (" %s\n", "-c, --critical=THRESHOLD(s)");
1052         printf ("    %s\n", _("Critical threshold range(s)"));
1053         printf (" %s\n", "--rate");
1054         printf ("    %s\n", _("Enable rate calculation. See 'Rate Calculation' below"));
1055         printf (" %s\n", "--rate-multiplier");
1056         printf ("    %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute"));
1058         /* Tests Against Strings */
1059         printf (" %s\n", "-s, --string=STRING");
1060         printf ("    %s\n", _("Return OK state (for that OID) if STRING is an exact match"));
1061         printf (" %s\n", "-r, --ereg=REGEX");
1062         printf ("    %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches"));
1063         printf (" %s\n", "-R, --eregi=REGEX");
1064         printf ("    %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches"));
1065         printf (" %s\n", "--invert-search");
1066         printf ("    %s\n", _("Invert search result (CRITICAL if found)"));
1068         /* Output Formatting */
1069         printf (" %s\n", "-l, --label=STRING");
1070         printf ("    %s\n", _("Prefix label for output from plugin"));
1071         printf (" %s\n", "-u, --units=STRING");
1072         printf ("    %s\n", _("Units label(s) for output data (e.g., 'sec.')."));
1073         printf (" %s\n", "-D, --output-delimiter=STRING");
1074         printf ("    %s\n", _("Separates output on multiple OID requests"));
1076         printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1077         printf (" %s\n", "-e, --retries=INTEGER");
1078         printf ("    %s\n", _("Number of retries to be used in the requests"));
1080         printf (UT_VERBOSE);
1082         printf ("\n");
1083         printf ("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package."));
1084         printf ("%s\n", _("if you don't have the package installed, you will need to download it from"));
1085         printf ("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1087         printf ("\n");
1088         printf ("%s\n", _("Notes:"));
1089         printf (" %s\n", _("- Multiple OIDs may be indicated by a comma or space-delimited list (lists with"));
1090         printf ("   %s %i %s\n", _("internal spaces must be quoted). Maximum:"), MAX_OIDS, _("OIDs."));
1092         printf(" -%s", UT_THRESHOLDS_NOTES);
1094         printf (" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'"));
1095         printf (" %s\n", _("- Note that only one string and one regex may be checked at present"));
1096         printf (" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value"));
1097         printf ("   %s\n", _("returned from the SNMP query is an unsigned integer."));
1099         printf("\n");
1100         printf("%s\n", _("Rate Calculation:"));
1101         printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when"));
1102         printf(" %s\n", _("calculating the counter difference since the last check. check_snmp"));
1103         printf(" %s\n", _("saves the last state information in a file so that the rate per second"));
1104         printf(" %s\n", _("can be calculated. Use the --rate option to save state information."));
1105         printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK."));
1106         printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so"));
1107         printf(" %s\n", _("changing the arguments will create a new state file."));
1109         printf (UT_SUPPORT);
1114 void
1115 print_usage (void)
1117         printf ("%s\n", _("Usage:"));
1118         printf ("%s -H <ip_address> -o <OID> [-w warn_range] [-c crit_range]\n",progname);
1119         printf ("[-C community] [-s string] [-r regex] [-R regexi] [-t timeout] [-e retries]\n");
1120         printf ("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n");
1121         printf ("[-m miblist] [-P snmp version] [-L seclevel] [-U secname] [-a authproto]\n");
1122         printf ("[-A authpasswd] [-x privproto] [-X privpasswd]\n");