Code

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