Code

Tests for --rate-multiplier option
[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 *th_warn=NULL;
164         char *th_crit=NULL;
165         char type[8] = "";
166         output chld_out, chld_err;
167         char *previous_string=NULL;
168         char *ap=NULL;
169         char *state_string=NULL;
170         size_t response_length, current_length, string_length;
171         char *temp_string=NULL;
172         int is_numeric=0;
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                         is_numeric++;
339                 } 
340                 else if (strstr (response, "Gauge32: ")) {
341                         show = strstr (response, "Gauge32: ") + 9;
342                         is_numeric++;
343                 } 
344                 else if (strstr (response, "Counter32: ")) {
345                         show = strstr (response, "Counter32: ") + 11;
346                         is_numeric++;
347                         is_counter=1;
348                         if(!calculate_rate) 
349                                 strcpy(type, "c");
350                 }
351                 else if (strstr (response, "Counter64: ")) {
352                         show = strstr (response, "Counter64: ") + 11;
353                         is_numeric++;
354                         is_counter=1;
355                         if(!calculate_rate)
356                                 strcpy(type, "c");
357                 }
358                 else if (strstr (response, "INTEGER: ")) {
359                         show = strstr (response, "INTEGER: ") + 9;
360                         is_numeric++;
361                 }
362                 else if (strstr (response, "STRING: ")) {
363                         show = strstr (response, "STRING: ") + 8;
364                         conv = "%.10g";
366                         /* Get the rest of the string on multi-line strings */
367                         ptr = show;
368                         COUNT_SEQ(ptr, bk_count, dq_count)
369                         while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
370                                 ptr++;
371                                 GOBBLE_TOS(ptr, "\n\"\\")
372                                 COUNT_SEQ(ptr, bk_count, dq_count)
373                         }
375                         if (dq_count) { /* unfinished line */
376                                 /* copy show verbatim first */
377                                 if (!mult_resp) mult_resp = strdup("");
378                                 asprintf (&mult_resp, "%s%s:\n%s\n", mult_resp, oids[i], show);
379                                 /* then strip out unmatched double-quote from single-line output */
380                                 if (show[0] == '"') show++;
382                                 /* Keep reading until we match end of double-quoted string */
383                                 for (line++; line < chld_out.lines; line++) {
384                                         ptr = chld_out.line[line];
385                                         asprintf (&mult_resp, "%s%s\n", mult_resp, ptr);
387                                         COUNT_SEQ(ptr, bk_count, dq_count)
388                                         while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
389                                                 ptr++;
390                                                 GOBBLE_TOS(ptr, "\n\"\\")
391                                                 COUNT_SEQ(ptr, bk_count, dq_count)
392                                         }
393                                         /* Break for loop before next line increment when done */
394                                         if (!dq_count) break;
395                                 }
396                         }
398                 }
399                 else if (strstr (response, "Timeticks: "))
400                         show = strstr (response, "Timeticks: ");
401                 else
402                         show = response;
404                 iresult = STATE_DEPENDENT;
406                 /* Process this block for numeric comparisons */
407                  if (is_numeric) {
408                         ptr = strpbrk (show, "0123456789");
409                         if (ptr == NULL)
410                                 die (STATE_UNKNOWN,_("No valid data returned"));
411                         response_value[i] = strtod (ptr, NULL);
413                         if(calculate_rate) {
414                                 if (previous_state!=NULL) {
415                                         duration = current_time-previous_state->time;
416                                         if(duration<=0)
417                                                 die(STATE_UNKNOWN,_("Time duration between plugin calls is invalid"));
418                                         temp_double = response_value[i]-previous_value[i];
419                                         /* Simple overflow catcher (same as in rrdtool, rrd_update.c) */
420                                         if(is_counter) {
421                                                 if(temp_double<(double)0.0)
422                                                         temp_double+=(double)4294967296.0; /* 2^32 */
423                                                 if(temp_double<(double)0.0)
424                                                         temp_double+=(double)18446744069414584320.0; /* 2^64-2^32 */;
425                                         }
426                                         /* Convert to per second, then use multiplier */
427                                         temp_double = temp_double/(duration*rate_multiplier);
428                                         iresult = get_status(temp_double, thlds[i]);
429                                         asprintf (&show, conv, temp_double);
430                                 }
431                         } else {
432                                 iresult = get_status(response_value[i], thlds[i]);
433                                 asprintf (&show, conv, response_value[i]);
434                         }
435                 }
437                 /* Process this block for string matching */
438                 else if (eval_method[i] & CRIT_STRING) {
439                         if (strcmp (show, string_value))
440                                 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK;
441                         else
442                                 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
443                 }
445                 /* Process this block for regex matching */
446                 else if (eval_method[i] & CRIT_REGEX) {
447                         excode = regexec (&preg, response, 10, pmatch, eflags);
448                         if (excode == 0) {
449                                 iresult = (invert_search==0) ? STATE_OK : STATE_CRITICAL;
450                         }
451                         else if (excode != REG_NOMATCH) {
452                                 regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER);
453                                 printf (_("Execute Error: %s\n"), errbuf);
454                                 exit (STATE_CRITICAL);
455                         }
456                         else {
457                                 iresult = (invert_search==0) ? STATE_CRITICAL : STATE_OK;
458                         }
459                 }
461                 /* Process this block for existence-nonexistence checks */
462                 /* TV: Should this be outside of this else block? */
463                 else {
464                         if (eval_method[i] & CRIT_PRESENT)
465                                 iresult = STATE_CRITICAL;
466                         else if (eval_method[i] & WARN_PRESENT)
467                                 iresult = STATE_WARNING;
468                         else if (response && iresult == STATE_DEPENDENT)
469                                 iresult = STATE_OK;
470                 }
472                 /* Result is the worst outcome of all the OIDs tested */
473                 result = max_state (result, iresult);
475                 /* Prepend a label for this OID if there is one */
476                 if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
477                         asprintf (&outbuff, "%s%s%s %s%s%s", outbuff,
478                                 (i == 0) ? " " : output_delim,
479                                 labels[i], mark (iresult), show, mark (iresult));
480                 else
481                         asprintf (&outbuff, "%s%s%s%s%s", outbuff, (i == 0) ? " " : output_delim,
482                                 mark (iresult), show, mark (iresult));
484                 /* Append a unit string for this OID if there is one */
485                 if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL)
486                         asprintf (&outbuff, "%s %s", outbuff, unitv[i]);
488                 /* Write perfdata with whatever can be parsed by strtod, if possible */
489                 ptr = NULL;
490                 strtod(show, &ptr);
491                 if (ptr > show) {
492                         if (nlabels >= (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
493                                 temp_string=labels[i];
494                         else
495                                 temp_string=oidname;
496                         if(calculate_rate)
497                                 asprintf(&temp_string,"%s-rate",temp_string);
498                         strncat(perfstr, temp_string, sizeof(perfstr)-strlen(perfstr)-1);
499                         strncat(perfstr, "=", sizeof(perfstr)-strlen(perfstr)-1);
500                         len = sizeof(perfstr)-strlen(perfstr)-1;
501                         strncat(perfstr, show, len>ptr-show ? ptr-show : len);
503                         if (type)
504                                 strncat(perfstr, type, sizeof(perfstr)-strlen(perfstr)-1);
505                         strncat(perfstr, " ", sizeof(perfstr)-strlen(perfstr)-1);
506                 }
507         }
508         total_oids=i;
510         /* Save state data, as all data collected now */
511         if(calculate_rate) {
512                 string_length=1024;
513                 state_string=malloc(string_length);
514                 if(state_string==NULL)
515                         die(STATE_UNKNOWN, _("Cannot malloc"));
516                 
517                 current_length=0;
518                 for(i=0; i<total_oids; i++) {
519                         asprintf(&temp_string,"%.0f",response_value[i]);
520                         if(temp_string==NULL)
521                                 die(STATE_UNKNOWN,_("Cannot asprintf()"));
522                         response_length = strlen(temp_string);
523                         if(current_length+response_length>string_length) {
524                                 string_length=current_length+1024;
525                                 state_string=realloc(state_string,string_length);
526                                 if(state_string==NULL)
527                                         die(STATE_UNKNOWN, _("Cannot realloc()"));
528                         }
529                         strcpy(&state_string[current_length],temp_string);
530                         current_length=current_length+response_length;
531                         state_string[current_length]=':';
532                         current_length++;
533                         free(temp_string);
534                 }
535                 state_string[--current_length]='\0';
536                 if (verbose > 2)
537                         printf("State string=%s\n",state_string);
538                 
539                 /* This is not strictly the same as time now, but any subtle variations will cancel out */
540                 np_state_write_string(current_time, state_string );
541                 if(previous_state==NULL) {
542                         /* Or should this be highest state? */
543                         die( STATE_OK, _("No previous data to calculate rate - assume okay" ) );
544                 }
545         }
547         printf ("%s %s -%s %s\n", label, state_text (result), outbuff, perfstr);
548         if (mult_resp) printf ("%s", mult_resp);
550         return result;
555 /* process command-line arguments */
556 int
557 process_arguments (int argc, char **argv)
559         char *ptr;
560         int c = 1;
561         int j = 0, jj = 0, ii = 0;
563         int option = 0;
564         static struct option longopts[] = {
565                 STD_LONG_OPTS,
566                 {"community", required_argument, 0, 'C'},
567                 {"oid", required_argument, 0, 'o'},
568                 {"object", required_argument, 0, 'o'},
569                 {"delimiter", required_argument, 0, 'd'},
570                 {"output-delimiter", required_argument, 0, 'D'},
571                 {"string", required_argument, 0, 's'},
572                 {"timeout", required_argument, 0, 't'},
573                 {"regex", required_argument, 0, 'r'},
574                 {"ereg", required_argument, 0, 'r'},
575                 {"eregi", required_argument, 0, 'R'},
576                 {"label", required_argument, 0, 'l'},
577                 {"units", required_argument, 0, 'u'},
578                 {"port", required_argument, 0, 'p'},
579                 {"retries", required_argument, 0, 'e'},
580                 {"miblist", required_argument, 0, 'm'},
581                 {"protocol", required_argument, 0, 'P'},
582                 {"seclevel", required_argument, 0, 'L'},
583                 {"secname", required_argument, 0, 'U'},
584                 {"authproto", required_argument, 0, 'a'},
585                 {"privproto", required_argument, 0, 'x'},
586                 {"authpasswd", required_argument, 0, 'A'},
587                 {"privpasswd", required_argument, 0, 'X'},
588                 {"next", no_argument, 0, 'n'},
589                 {"rate", no_argument, 0, L_CALCULATE_RATE},
590                 {"rate-multiplier", required_argument, 0, L_RATE_MULTIPLIER},
591                 {"invert-search", no_argument, 0, L_INVERT_SEARCH},
592                 {0, 0, 0, 0}
593         };
595         if (argc < 2)
596                 return ERROR;
598         /* reverse compatibility for very old non-POSIX usage forms */
599         for (c = 1; c < argc; c++) {
600                 if (strcmp ("-to", argv[c]) == 0)
601                         strcpy (argv[c], "-t");
602                 if (strcmp ("-wv", argv[c]) == 0)
603                         strcpy (argv[c], "-w");
604                 if (strcmp ("-cv", argv[c]) == 0)
605                         strcpy (argv[c], "-c");
606         }
608         while (1) {
609                 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:",
610                                                                          longopts, &option);
612                 if (c == -1 || c == EOF)
613                         break;
615                 switch (c) {
616                 case '?':       /* usage */
617                         usage5 ();
618                 case 'h':       /* help */
619                         print_help ();
620                         exit (STATE_OK);
621                 case 'V':       /* version */
622                         print_revision (progname, NP_VERSION);
623                         exit (STATE_OK);
624                 case 'v': /* verbose */
625                         verbose++;
626                         break;
628         /* Connection info */
629                 case 'C':                                                                       /* group or community */
630                         community = optarg;
631                         break;
632                 case 'H':                                                                       /* Host or server */
633                         server_address = optarg;
634                         break;
635                 case 'p':       /* TCP port number */
636                         port = optarg;
637                         break;
638                 case 'm':       /* List of MIBS */
639                         miblist = optarg;
640                         break;
641                 case 'n':       /* usesnmpgetnext */
642                         usesnmpgetnext = TRUE;
643                         break;
644                 case 'P':       /* SNMP protocol version */
645                         proto = optarg;
646                         break;
647                 case 'L':       /* security level */
648                         seclevel = optarg;
649                         break;
650                 case 'U':       /* security username */
651                         secname = optarg;
652                         break;
653                 case 'a':       /* auth protocol */
654                         authproto = optarg;
655                         break;
656                 case 'x':       /* priv protocol */
657                         privproto = optarg;
658                         break;
659                 case 'A':       /* auth passwd */
660                         authpasswd = optarg;
661                         break;
662                 case 'X':       /* priv passwd */
663                         privpasswd = optarg;
664                         break;
665                 case 't':       /* timeout period */
666                         if (!is_integer (optarg))
667                                 usage2 (_("Timeout interval must be a positive integer"), optarg);
668                         else
669                                 timeout_interval = atoi (optarg);
670                         break;
672         /* Test parameters */
673                 case 'c':                                                                       /* critical threshold */
674                         critical_thresholds = optarg;
675                         break;
676                 case 'w':                                                                       /* warning threshold */
677                         warning_thresholds = optarg;
678                         break;
679                 case 'e': /* PRELIMINARY - may change */
680                 case 'E': /* PRELIMINARY - may change */
681                         if (!is_integer (optarg))
682                                 usage2 (_("Retries interval must be a positive integer"), optarg);
683                         else
684                                 retries = atoi(optarg);
685                         break;
686                 case 'o':                                                                       /* object identifier */
687                         if ( strspn( optarg, "0123456789.," ) != strlen( optarg ) ) {
688                                         /*
689                                          * we have something other than digits, periods and comas,
690                                          * so we have a mib variable, rather than just an SNMP OID,
691                                          * so we have to actually read the mib files
692                                          */
693                                         needmibs = TRUE;
694                         }
695                         if (!oids) oids = calloc(MAX_OIDS, sizeof (char *));
696                         for (ptr = strtok(optarg, ", "); ptr != NULL && j < MAX_OIDS; ptr = strtok(NULL, ", "), j++) {
697                                 oids[j] = strdup(ptr);
698                         }
699                         numoids = j;
700                         if (c == 'E' || c == 'e') {
701                                 jj++;
702                                 ii++;
703                         }
704                         if (c == 'E')
705                                 eval_method[j+1] |= WARN_PRESENT;
706                         else if (c == 'e')
707                                 eval_method[j+1] |= CRIT_PRESENT;
708                         break;
709                 case 's':                                                                       /* string or substring */
710                         strncpy (string_value, optarg, sizeof (string_value) - 1);
711                         string_value[sizeof (string_value) - 1] = 0;
712                         eval_method[jj++] = CRIT_STRING;
713                         ii++;
714                         break;
715                 case 'R':                                                                       /* regex */
716                         cflags = REG_ICASE;
717                 case 'r':                                                                       /* regex */
718                         cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
719                         strncpy (regex_expect, optarg, sizeof (regex_expect) - 1);
720                         regex_expect[sizeof (regex_expect) - 1] = 0;
721                         errcode = regcomp (&preg, regex_expect, cflags);
722                         if (errcode != 0) {
723                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
724                                 printf (_("Could Not Compile Regular Expression"));
725                                 return ERROR;
726                         }
727                         eval_method[jj++] = CRIT_REGEX;
728                         ii++;
729                         break;
731         /* Format */
732                 case 'd':                                                                       /* delimiter */
733                         delimiter = strscpy (delimiter, optarg);
734                         break;
735                 case 'D':                                                                       /* output-delimiter */
736                         output_delim = strscpy (output_delim, optarg);
737                         break;
738                 case 'l':                                                                       /* label */
739                         nlabels++;
740                         if (nlabels >= labels_size) {
741                                 labels_size += 8;
742                                 labels = realloc (labels, labels_size);
743                                 if (labels == NULL)
744                                         die (STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels);
745                         }
746                         labels[nlabels - 1] = optarg;
747                         ptr = thisarg (optarg);
748                         labels[nlabels - 1] = ptr;
749                         if (strstr (ptr, "'") == ptr)
750                                 labels[nlabels - 1] = ptr + 1;
751                         while (ptr && (ptr = nextarg (ptr))) {
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\n"));
757                                 }
758                                 labels++;
759                                 ptr = thisarg (ptr);
760                                 if (strstr (ptr, "'") == ptr)
761                                         labels[nlabels - 1] = ptr + 1;
762                                 else
763                                         labels[nlabels - 1] = ptr;
764                         }
765                         break;
766                 case 'u':                                                                       /* units */
767                         units = optarg;
768                         nunits++;
769                         if (nunits >= unitv_size) {
770                                 unitv_size += 8;
771                                 unitv = realloc (unitv, unitv_size);
772                                 if (unitv == NULL)
773                                         die (STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits);
774                         }
775                         unitv[nunits - 1] = optarg;
776                         ptr = thisarg (optarg);
777                         unitv[nunits - 1] = ptr;
778                         if (strstr (ptr, "'") == ptr)
779                                 unitv[nunits - 1] = ptr + 1;
780                         while (ptr && (ptr = nextarg (ptr))) {
781                                 if (nunits >= unitv_size) {
782                                         unitv_size += 8;
783                                         unitv = realloc (unitv, unitv_size);
784                                         if (units == NULL)
785                                                 die (STATE_UNKNOWN, _("Could not realloc() units\n"));
786                                 }
787                                 nunits++;
788                                 ptr = thisarg (ptr);
789                                 if (strstr (ptr, "'") == ptr)
790                                         unitv[nunits - 1] = ptr + 1;
791                                 else
792                                         unitv[nunits - 1] = ptr;
793                         }
794                         break;
795                 case L_CALCULATE_RATE:
796                         if(calculate_rate==0)
797                                 np_enable_state(NULL, 1);
798                         calculate_rate = 1;
799                         break;
800                 case L_RATE_MULTIPLIER:
801                         if(!is_integer(optarg)||((rate_multiplier=atoi(optarg))<=0))
802                                 usage2(_("Rate multiplier must be a positive integer"),optarg);
803                         break;
804                 case L_INVERT_SEARCH:
805                         invert_search=1;
806                         break;
807                 }
808         }
810         if (server_address == NULL)
811                 server_address = argv[optind];
813         if (community == NULL)
814                 community = strdup (DEFAULT_COMMUNITY);
816         return validate_arguments ();
820 /******************************************************************************
822 @@-
823 <sect3>
824 <title>validate_arguments</title>
826 <para>&PROTO_validate_arguments;</para>
828 <para>Checks to see if the default miblist needs to be loaded. Also verifies
829 the authentication and authorization combinations based on protocol version
830 selected.</para>
832 <para></para>
834 </sect3>
835 -@@
836 ******************************************************************************/
840 int
841 validate_arguments ()
843         /* check whether to load locally installed MIBS (CPU/disk intensive) */
844         if (miblist == NULL) {
845                 if ( needmibs == TRUE ) {
846                         miblist = strdup (DEFAULT_MIBLIST);
847                 }else{
848                         miblist = "";                   /* don't read any mib files for numeric oids */
849                 }
850         }
852         /* Check server_address is given */
853         if (server_address == NULL)
854                 die(STATE_UNKNOWN, _("No host specified\n"));
856         /* Check oid is given */
857         if (numoids == 0)
858                 die(STATE_UNKNOWN, _("No OIDs specified\n"));
860         if (proto == NULL)
861                 asprintf(&proto, DEFAULT_PROTOCOL);
863         if ((strcmp(proto,"1") == 0) || (strcmp(proto, "2c")==0)) {     /* snmpv1 or snmpv2c */
864                 numauthpriv = 2;
865                 authpriv = calloc (numauthpriv, sizeof (char *));
866                 authpriv[0] = strdup ("-c");
867                 authpriv[1] = strdup (community);
868         }
869         else if ( strcmp (proto, "3") == 0 ) {          /* snmpv3 args */
870                 if (seclevel == NULL)
871                         asprintf(&seclevel, "noAuthNoPriv");
873                 if (strcmp(seclevel, "noAuthNoPriv") == 0) {
874                         numauthpriv = 2;
875                         authpriv = calloc (numauthpriv, sizeof (char *));
876                         authpriv[0] = strdup ("-l");
877                         authpriv[1] = strdup ("noAuthNoPriv");
878                 } else {
879                         if (! ( (strcmp(seclevel, "authNoPriv")==0) || (strcmp(seclevel, "authPriv")==0) ) ) {
880                                 usage2 (_("Invalid seclevel"), seclevel);
881                         }
883                         if (authproto == NULL )
884                                 asprintf(&authproto, DEFAULT_AUTH_PROTOCOL);
886                         if (secname == NULL)
887                                 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname");
889                         if (authpasswd == NULL)
890                                 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd");
892                         if ( strcmp(seclevel, "authNoPriv") == 0 ) {
893                                 numauthpriv = 8;
894                                 authpriv = calloc (numauthpriv, sizeof (char *));
895                                 authpriv[0] = strdup ("-l");
896                                 authpriv[1] = strdup ("authNoPriv");
897                                 authpriv[2] = strdup ("-a");
898                                 authpriv[3] = strdup (authproto);
899                                 authpriv[4] = strdup ("-u");
900                                 authpriv[5] = strdup (secname);
901                                 authpriv[6] = strdup ("-A");
902                                 authpriv[7] = strdup (authpasswd);
903                         } else if ( strcmp(seclevel, "authPriv") == 0 ) {
904                                 if (privproto == NULL )
905                                         asprintf(&privproto, DEFAULT_PRIV_PROTOCOL);
907                                 if (privpasswd == NULL)
908                                         die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd");
910                                 numauthpriv = 12;
911                                 authpriv = calloc (numauthpriv, sizeof (char *));
912                                 authpriv[0] = strdup ("-l");
913                                 authpriv[1] = strdup ("authPriv");
914                                 authpriv[2] = strdup ("-a");
915                                 authpriv[3] = strdup (authproto);
916                                 authpriv[4] = strdup ("-u");
917                                 authpriv[5] = strdup (secname);
918                                 authpriv[6] = strdup ("-A");
919                                 authpriv[7] = strdup (authpasswd);
920                                 authpriv[8] = strdup ("-x");
921                                 authpriv[9] = strdup (privproto);
922                                 authpriv[10] = strdup ("-X");
923                                 authpriv[11] = strdup (privpasswd);
924                         }
925                 }
927         }
928         else {
929                 usage2 (_("Invalid SNMP version"), proto);
930         }
932         return OK;
937 /* trim leading whitespace
938          if there is a leading quote, make sure it balances */
940 char *
941 thisarg (char *str)
943         str += strspn (str, " \t\r\n"); /* trim any leading whitespace */
944         if (strstr (str, "'") == str) { /* handle SIMPLE quoted strings */
945                 if (strlen (str) == 1 || !strstr (str + 1, "'"))
946                         die (STATE_UNKNOWN, _("Unbalanced quotes\n"));
947         }
948         return str;
953 /* if there's a leading quote, advance to the trailing quote
954          set the trailing quote to '\x0'
955          if the string continues, advance beyond the comma */
957 char *
958 nextarg (char *str)
960         if (strstr (str, "'") == str) {
961                 str[0] = 0;
962                 if (strlen (str) > 1) {
963                         str = strstr (str + 1, "'");
964                         return (++str);
965                 }
966                 else {
967                         return NULL;
968                 }
969         }
970         if (strstr (str, ",") == str) {
971                 str[0] = 0;
972                 if (strlen (str) > 1) {
973                         return (++str);
974                 }
975                 else {
976                         return NULL;
977                 }
978         }
979         if ((str = strstr (str, ",")) && strlen (str) > 1) {
980                 str[0] = 0;
981                 return (++str);
982         }
983         return NULL;
988 void
989 print_help (void)
991         print_revision (progname, NP_VERSION);
993         printf (COPYRIGHT, copyright, email);
995         printf ("%s\n", _("Check status of remote machines and obtain system information via SNMP"));
997         printf ("\n\n");
999         print_usage ();
1001         printf (UT_HELP_VRSN);
1002         printf (UT_EXTRA_OPTS);
1004         printf (UT_HOST_PORT, 'p', DEFAULT_PORT);
1006         /* SNMP and Authentication Protocol */
1007         printf (" %s\n", "-n, --next");
1008         printf ("    %s\n", _("Use SNMP GETNEXT instead of SNMP GET"));
1009         printf (" %s\n", "-P, --protocol=[1|2c|3]");
1010         printf ("    %s\n", _("SNMP protocol version"));
1011         printf (" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]");
1012         printf ("    %s\n", _("SNMPv3 securityLevel"));
1013         printf (" %s\n", "-a, --authproto=[MD5|SHA]");
1014         printf ("    %s\n", _("SNMPv3 auth proto"));
1015         printf (" %s\n", "-x, --privproto=[DES|AES]");
1016         printf ("    %s\n", _("SNMPv3 priv proto (default DES)"));
1018         /* Authentication Tokens*/
1019         printf (" %s\n", "-C, --community=STRING");
1020         printf ("    %s ", _("Optional community string for SNMP communication"));
1021         printf ("(%s \"%s\")\n", _("default is") ,DEFAULT_COMMUNITY);
1022         printf (" %s\n", "-U, --secname=USERNAME");
1023         printf ("    %s\n", _("SNMPv3 username"));
1024         printf (" %s\n", "-A, --authpassword=PASSWORD");
1025         printf ("    %s\n", _("SNMPv3 authentication password"));
1026         printf (" %s\n", "-X, --privpasswd=PASSWORD");
1027         printf ("    %s\n", _("SNMPv3 privacy password"));
1029         /* OID Stuff */
1030         printf (" %s\n", "-o, --oid=OID(s)");
1031         printf ("    %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query"));
1032         printf (" %s\n", "-m, --miblist=STRING");
1033         printf ("    %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'"));
1034         printf ("    %s\n", _("for symbolic OIDs.)"));
1035         printf (" %s\n", "-d, --delimiter=STRING");
1036         printf ("    %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER);
1037         printf ("    %s\n", _("Any data on the right hand side of the delimiter is considered"));
1038         printf ("    %s\n", _("to be the data that should be used in the evaluation."));
1040         /* Tests Against Integers */
1041         printf (" %s\n", "-w, --warning=THRESHOLD(s)");
1042         printf ("    %s\n", _("Warning threshold range(s)"));
1043         printf (" %s\n", "-c, --critical=THRESHOLD(s)");
1044         printf ("    %s\n", _("Critical threshold range(s)"));
1045         printf (" %s\n", "--rate");
1046         printf ("    %s\n", _("Enable rate calculation. See 'Rate Calculation' below"));
1047         printf (" %s\n", "--rate-multiplier");
1048         printf ("    %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute"));
1050         /* Tests Against Strings */
1051         printf (" %s\n", "-s, --string=STRING");
1052         printf ("    %s\n", _("Return OK state (for that OID) if STRING is an exact match"));
1053         printf (" %s\n", "-r, --ereg=REGEX");
1054         printf ("    %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches"));
1055         printf (" %s\n", "-R, --eregi=REGEX");
1056         printf ("    %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches"));
1057         printf (" %s\n", "--invert-search");
1058         printf ("    %s\n", _("Invert search result (CRITICAL if found)"));
1060         /* Output Formatting */
1061         printf (" %s\n", "-l, --label=STRING");
1062         printf ("    %s\n", _("Prefix label for output from plugin"));
1063         printf (" %s\n", "-u, --units=STRING");
1064         printf ("    %s\n", _("Units label(s) for output data (e.g., 'sec.')."));
1065         printf (" %s\n", "-D, --output-delimiter=STRING");
1066         printf ("    %s\n", _("Separates output on multiple OID requests"));
1068         printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1069         printf (" %s\n", "-e, --retries=INTEGER");
1070         printf ("    %s\n", _("Number of retries to be used in the requests"));
1072         printf (UT_VERBOSE);
1074         printf ("\n");
1075         printf ("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package."));
1076         printf ("%s\n", _("if you don't have the package installed, you will need to download it from"));
1077         printf ("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1079         printf ("\n");
1080         printf ("%s\n", _("Notes:"));
1081         printf (" %s\n", _("- Multiple OIDs may be indicated by a comma- or space-delimited list (lists with"));
1082         printf ("   %s\n", _("internal spaces must be quoted) [max 8 OIDs]"));
1084         printf(" -%s", UT_THRESHOLDS_NOTES);
1086         printf (" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'"));
1087         printf (" %s\n", _("- Note that only one string and one regex may be checked at present"));
1088         printf (" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value"));
1089         printf ("   %s\n", _("returned from the SNMP query is an unsigned integer."));
1091         printf("\n");
1092         printf("%s\n", _("Rate Calculation:"));
1093         printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when"));
1094         printf(" %s\n", _("calculating the counter difference since the last check. check_snmp"));
1095         printf(" %s\n", _("saves the last state information in a file so that the rate per second"));
1096         printf(" %s\n", _("can be calculated. Use the --rate option to save state information."));
1097         printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK."));
1098         printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so"));
1099         printf(" %s\n", _("changing the arguments will create a new state file."));
1101         printf (UT_SUPPORT);
1106 void
1107 print_usage (void)
1109         printf ("%s\n", _("Usage:"));
1110         printf ("%s -H <ip_address> -o <OID> [-w warn_range] [-c crit_range]\n",progname);
1111         printf ("[-C community] [-s string] [-r regex] [-R regexi] [-t timeout] [-e retries]\n");
1112         printf ("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n");
1113         printf ("[-m miblist] [-P snmp version] [-L seclevel] [-U secname] [-a authproto]\n");
1114         printf ("[-A authpasswd] [-x privproto] [-X privpasswd]\n");