Code

Fix --help output when MAX_OIDS is altered.
[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                         strncat(perfstr, temp_string, sizeof(perfstr)-strlen(perfstr)-1);
497                         strncat(perfstr, "=", sizeof(perfstr)-strlen(perfstr)-1);
498                         len = sizeof(perfstr)-strlen(perfstr)-1;
499                         strncat(perfstr, show, len>ptr-show ? ptr-show : len);
501                         if (type)
502                                 strncat(perfstr, type, sizeof(perfstr)-strlen(perfstr)-1);
503                         strncat(perfstr, " ", sizeof(perfstr)-strlen(perfstr)-1);
504                 }
505         }
506         total_oids=i;
508         /* Save state data, as all data collected now */
509         if(calculate_rate) {
510                 string_length=1024;
511                 state_string=malloc(string_length);
512                 if(state_string==NULL)
513                         die(STATE_UNKNOWN, _("Cannot malloc"));
514                 
515                 current_length=0;
516                 for(i=0; i<total_oids; i++) {
517                         asprintf(&temp_string,"%.0f",response_value[i]);
518                         if(temp_string==NULL)
519                                 die(STATE_UNKNOWN,_("Cannot asprintf()"));
520                         response_length = strlen(temp_string);
521                         if(current_length+response_length>string_length) {
522                                 string_length=current_length+1024;
523                                 state_string=realloc(state_string,string_length);
524                                 if(state_string==NULL)
525                                         die(STATE_UNKNOWN, _("Cannot realloc()"));
526                         }
527                         strcpy(&state_string[current_length],temp_string);
528                         current_length=current_length+response_length;
529                         state_string[current_length]=':';
530                         current_length++;
531                         free(temp_string);
532                 }
533                 state_string[--current_length]='\0';
534                 if (verbose > 2)
535                         printf("State string=%s\n",state_string);
536                 
537                 /* This is not strictly the same as time now, but any subtle variations will cancel out */
538                 np_state_write_string(current_time, state_string );
539                 if(previous_state==NULL) {
540                         /* Or should this be highest state? */
541                         die( STATE_OK, _("No previous data to calculate rate - assume okay" ) );
542                 }
543         }
545         printf ("%s %s -%s %s\n", label, state_text (result), outbuff, perfstr);
546         if (mult_resp) printf ("%s", mult_resp);
548         return result;
553 /* process command-line arguments */
554 int
555 process_arguments (int argc, char **argv)
557         char *ptr;
558         int c = 1;
559         int j = 0, jj = 0, ii = 0;
561         int option = 0;
562         static struct option longopts[] = {
563                 STD_LONG_OPTS,
564                 {"community", required_argument, 0, 'C'},
565                 {"oid", required_argument, 0, 'o'},
566                 {"object", required_argument, 0, 'o'},
567                 {"delimiter", required_argument, 0, 'd'},
568                 {"output-delimiter", required_argument, 0, 'D'},
569                 {"string", required_argument, 0, 's'},
570                 {"timeout", required_argument, 0, 't'},
571                 {"regex", required_argument, 0, 'r'},
572                 {"ereg", required_argument, 0, 'r'},
573                 {"eregi", required_argument, 0, 'R'},
574                 {"label", required_argument, 0, 'l'},
575                 {"units", required_argument, 0, 'u'},
576                 {"port", required_argument, 0, 'p'},
577                 {"retries", required_argument, 0, 'e'},
578                 {"miblist", required_argument, 0, 'm'},
579                 {"protocol", required_argument, 0, 'P'},
580                 {"seclevel", required_argument, 0, 'L'},
581                 {"secname", required_argument, 0, 'U'},
582                 {"authproto", required_argument, 0, 'a'},
583                 {"privproto", required_argument, 0, 'x'},
584                 {"authpasswd", required_argument, 0, 'A'},
585                 {"privpasswd", required_argument, 0, 'X'},
586                 {"next", no_argument, 0, 'n'},
587                 {"rate", no_argument, 0, L_CALCULATE_RATE},
588                 {"rate-multiplier", required_argument, 0, L_RATE_MULTIPLIER},
589                 {"invert-search", no_argument, 0, L_INVERT_SEARCH},
590                 {0, 0, 0, 0}
591         };
593         if (argc < 2)
594                 return ERROR;
596         /* reverse compatibility for very old non-POSIX usage forms */
597         for (c = 1; c < argc; c++) {
598                 if (strcmp ("-to", argv[c]) == 0)
599                         strcpy (argv[c], "-t");
600                 if (strcmp ("-wv", argv[c]) == 0)
601                         strcpy (argv[c], "-w");
602                 if (strcmp ("-cv", argv[c]) == 0)
603                         strcpy (argv[c], "-c");
604         }
606         while (1) {
607                 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:",
608                                                                          longopts, &option);
610                 if (c == -1 || c == EOF)
611                         break;
613                 switch (c) {
614                 case '?':       /* usage */
615                         usage5 ();
616                 case 'h':       /* help */
617                         print_help ();
618                         exit (STATE_OK);
619                 case 'V':       /* version */
620                         print_revision (progname, NP_VERSION);
621                         exit (STATE_OK);
622                 case 'v': /* verbose */
623                         verbose++;
624                         break;
626         /* Connection info */
627                 case 'C':                                                                       /* group or community */
628                         community = optarg;
629                         break;
630                 case 'H':                                                                       /* Host or server */
631                         server_address = optarg;
632                         break;
633                 case 'p':       /* TCP port number */
634                         port = optarg;
635                         break;
636                 case 'm':       /* List of MIBS */
637                         miblist = optarg;
638                         break;
639                 case 'n':       /* usesnmpgetnext */
640                         usesnmpgetnext = TRUE;
641                         break;
642                 case 'P':       /* SNMP protocol version */
643                         proto = optarg;
644                         break;
645                 case 'L':       /* security level */
646                         seclevel = optarg;
647                         break;
648                 case 'U':       /* security username */
649                         secname = optarg;
650                         break;
651                 case 'a':       /* auth protocol */
652                         authproto = optarg;
653                         break;
654                 case 'x':       /* priv protocol */
655                         privproto = optarg;
656                         break;
657                 case 'A':       /* auth passwd */
658                         authpasswd = optarg;
659                         break;
660                 case 'X':       /* priv passwd */
661                         privpasswd = optarg;
662                         break;
663                 case 't':       /* timeout period */
664                         if (!is_integer (optarg))
665                                 usage2 (_("Timeout interval must be a positive integer"), optarg);
666                         else
667                                 timeout_interval = atoi (optarg);
668                         break;
670         /* Test parameters */
671                 case 'c':                                                                       /* critical threshold */
672                         critical_thresholds = optarg;
673                         break;
674                 case 'w':                                                                       /* warning threshold */
675                         warning_thresholds = optarg;
676                         break;
677                 case 'e': /* PRELIMINARY - may change */
678                 case 'E': /* PRELIMINARY - may change */
679                         if (!is_integer (optarg))
680                                 usage2 (_("Retries interval must be a positive integer"), optarg);
681                         else
682                                 retries = atoi(optarg);
683                         break;
684                 case 'o':                                                                       /* object identifier */
685                         if ( strspn( optarg, "0123456789.," ) != strlen( optarg ) ) {
686                                         /*
687                                          * we have something other than digits, periods and comas,
688                                          * so we have a mib variable, rather than just an SNMP OID,
689                                          * so we have to actually read the mib files
690                                          */
691                                         needmibs = TRUE;
692                         }
693                         if (!oids) oids = calloc(MAX_OIDS, sizeof (char *));
694                         for (ptr = strtok(optarg, ", "); ptr != NULL && j < MAX_OIDS; ptr = strtok(NULL, ", "), j++) {
695                                 oids[j] = strdup(ptr);
696                         }
697                         numoids = j;
698                         if (c == 'E' || c == 'e') {
699                                 jj++;
700                                 ii++;
701                         }
702                         if (c == 'E')
703                                 eval_method[j+1] |= WARN_PRESENT;
704                         else if (c == 'e')
705                                 eval_method[j+1] |= CRIT_PRESENT;
706                         break;
707                 case 's':                                                                       /* string or substring */
708                         strncpy (string_value, optarg, sizeof (string_value) - 1);
709                         string_value[sizeof (string_value) - 1] = 0;
710                         eval_method[jj++] = CRIT_STRING;
711                         ii++;
712                         break;
713                 case 'R':                                                                       /* regex */
714                         cflags = REG_ICASE;
715                 case 'r':                                                                       /* regex */
716                         cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
717                         strncpy (regex_expect, optarg, sizeof (regex_expect) - 1);
718                         regex_expect[sizeof (regex_expect) - 1] = 0;
719                         errcode = regcomp (&preg, regex_expect, cflags);
720                         if (errcode != 0) {
721                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
722                                 printf (_("Could Not Compile Regular Expression"));
723                                 return ERROR;
724                         }
725                         eval_method[jj++] = CRIT_REGEX;
726                         ii++;
727                         break;
729         /* Format */
730                 case 'd':                                                                       /* delimiter */
731                         delimiter = strscpy (delimiter, optarg);
732                         break;
733                 case 'D':                                                                       /* output-delimiter */
734                         output_delim = strscpy (output_delim, optarg);
735                         break;
736                 case 'l':                                                                       /* label */
737                         nlabels++;
738                         if (nlabels >= labels_size) {
739                                 labels_size += 8;
740                                 labels = realloc (labels, labels_size);
741                                 if (labels == NULL)
742                                         die (STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels);
743                         }
744                         labels[nlabels - 1] = optarg;
745                         ptr = thisarg (optarg);
746                         labels[nlabels - 1] = ptr;
747                         if (strstr (ptr, "'") == ptr)
748                                 labels[nlabels - 1] = ptr + 1;
749                         while (ptr && (ptr = nextarg (ptr))) {
750                                 if (nlabels >= labels_size) {
751                                         labels_size += 8;
752                                         labels = realloc (labels, labels_size);
753                                         if (labels == NULL)
754                                                 die (STATE_UNKNOWN, _("Could not reallocate labels\n"));
755                                 }
756                                 labels++;
757                                 ptr = thisarg (ptr);
758                                 if (strstr (ptr, "'") == ptr)
759                                         labels[nlabels - 1] = ptr + 1;
760                                 else
761                                         labels[nlabels - 1] = ptr;
762                         }
763                         break;
764                 case 'u':                                                                       /* units */
765                         units = optarg;
766                         nunits++;
767                         if (nunits >= unitv_size) {
768                                 unitv_size += 8;
769                                 unitv = realloc (unitv, unitv_size);
770                                 if (unitv == NULL)
771                                         die (STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits);
772                         }
773                         unitv[nunits - 1] = optarg;
774                         ptr = thisarg (optarg);
775                         unitv[nunits - 1] = ptr;
776                         if (strstr (ptr, "'") == ptr)
777                                 unitv[nunits - 1] = ptr + 1;
778                         while (ptr && (ptr = nextarg (ptr))) {
779                                 if (nunits >= unitv_size) {
780                                         unitv_size += 8;
781                                         unitv = realloc (unitv, unitv_size);
782                                         if (units == NULL)
783                                                 die (STATE_UNKNOWN, _("Could not realloc() units\n"));
784                                 }
785                                 nunits++;
786                                 ptr = thisarg (ptr);
787                                 if (strstr (ptr, "'") == ptr)
788                                         unitv[nunits - 1] = ptr + 1;
789                                 else
790                                         unitv[nunits - 1] = ptr;
791                         }
792                         break;
793                 case L_CALCULATE_RATE:
794                         if(calculate_rate==0)
795                                 np_enable_state(NULL, 1);
796                         calculate_rate = 1;
797                         break;
798                 case L_RATE_MULTIPLIER:
799                         if(!is_integer(optarg)||((rate_multiplier=atoi(optarg))<=0))
800                                 usage2(_("Rate multiplier must be a positive integer"),optarg);
801                         break;
802                 case L_INVERT_SEARCH:
803                         invert_search=1;
804                         break;
805                 }
806         }
808         if (server_address == NULL)
809                 server_address = argv[optind];
811         if (community == NULL)
812                 community = strdup (DEFAULT_COMMUNITY);
814         return validate_arguments ();
818 /******************************************************************************
820 @@-
821 <sect3>
822 <title>validate_arguments</title>
824 <para>&PROTO_validate_arguments;</para>
826 <para>Checks to see if the default miblist needs to be loaded. Also verifies
827 the authentication and authorization combinations based on protocol version
828 selected.</para>
830 <para></para>
832 </sect3>
833 -@@
834 ******************************************************************************/
838 int
839 validate_arguments ()
841         /* check whether to load locally installed MIBS (CPU/disk intensive) */
842         if (miblist == NULL) {
843                 if ( needmibs == TRUE ) {
844                         miblist = strdup (DEFAULT_MIBLIST);
845                 }else{
846                         miblist = "";                   /* don't read any mib files for numeric oids */
847                 }
848         }
850         /* Check server_address is given */
851         if (server_address == NULL)
852                 die(STATE_UNKNOWN, _("No host specified\n"));
854         /* Check oid is given */
855         if (numoids == 0)
856                 die(STATE_UNKNOWN, _("No OIDs specified\n"));
858         if (proto == NULL)
859                 asprintf(&proto, DEFAULT_PROTOCOL);
861         if ((strcmp(proto,"1") == 0) || (strcmp(proto, "2c")==0)) {     /* snmpv1 or snmpv2c */
862                 numauthpriv = 2;
863                 authpriv = calloc (numauthpriv, sizeof (char *));
864                 authpriv[0] = strdup ("-c");
865                 authpriv[1] = strdup (community);
866         }
867         else if ( strcmp (proto, "3") == 0 ) {          /* snmpv3 args */
868                 if (seclevel == NULL)
869                         asprintf(&seclevel, "noAuthNoPriv");
871                 if (strcmp(seclevel, "noAuthNoPriv") == 0) {
872                         numauthpriv = 2;
873                         authpriv = calloc (numauthpriv, sizeof (char *));
874                         authpriv[0] = strdup ("-l");
875                         authpriv[1] = strdup ("noAuthNoPriv");
876                 } else {
877                         if (! ( (strcmp(seclevel, "authNoPriv")==0) || (strcmp(seclevel, "authPriv")==0) ) ) {
878                                 usage2 (_("Invalid seclevel"), seclevel);
879                         }
881                         if (authproto == NULL )
882                                 asprintf(&authproto, DEFAULT_AUTH_PROTOCOL);
884                         if (secname == NULL)
885                                 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname");
887                         if (authpasswd == NULL)
888                                 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd");
890                         if ( strcmp(seclevel, "authNoPriv") == 0 ) {
891                                 numauthpriv = 8;
892                                 authpriv = calloc (numauthpriv, sizeof (char *));
893                                 authpriv[0] = strdup ("-l");
894                                 authpriv[1] = strdup ("authNoPriv");
895                                 authpriv[2] = strdup ("-a");
896                                 authpriv[3] = strdup (authproto);
897                                 authpriv[4] = strdup ("-u");
898                                 authpriv[5] = strdup (secname);
899                                 authpriv[6] = strdup ("-A");
900                                 authpriv[7] = strdup (authpasswd);
901                         } else if ( strcmp(seclevel, "authPriv") == 0 ) {
902                                 if (privproto == NULL )
903                                         asprintf(&privproto, DEFAULT_PRIV_PROTOCOL);
905                                 if (privpasswd == NULL)
906                                         die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd");
908                                 numauthpriv = 12;
909                                 authpriv = calloc (numauthpriv, sizeof (char *));
910                                 authpriv[0] = strdup ("-l");
911                                 authpriv[1] = strdup ("authPriv");
912                                 authpriv[2] = strdup ("-a");
913                                 authpriv[3] = strdup (authproto);
914                                 authpriv[4] = strdup ("-u");
915                                 authpriv[5] = strdup (secname);
916                                 authpriv[6] = strdup ("-A");
917                                 authpriv[7] = strdup (authpasswd);
918                                 authpriv[8] = strdup ("-x");
919                                 authpriv[9] = strdup (privproto);
920                                 authpriv[10] = strdup ("-X");
921                                 authpriv[11] = strdup (privpasswd);
922                         }
923                 }
925         }
926         else {
927                 usage2 (_("Invalid SNMP version"), proto);
928         }
930         return OK;
935 /* trim leading whitespace
936          if there is a leading quote, make sure it balances */
938 char *
939 thisarg (char *str)
941         str += strspn (str, " \t\r\n"); /* trim any leading whitespace */
942         if (strstr (str, "'") == str) { /* handle SIMPLE quoted strings */
943                 if (strlen (str) == 1 || !strstr (str + 1, "'"))
944                         die (STATE_UNKNOWN, _("Unbalanced quotes\n"));
945         }
946         return str;
951 /* if there's a leading quote, advance to the trailing quote
952          set the trailing quote to '\x0'
953          if the string continues, advance beyond the comma */
955 char *
956 nextarg (char *str)
958         if (strstr (str, "'") == str) {
959                 str[0] = 0;
960                 if (strlen (str) > 1) {
961                         str = strstr (str + 1, "'");
962                         return (++str);
963                 }
964                 else {
965                         return NULL;
966                 }
967         }
968         if (strstr (str, ",") == str) {
969                 str[0] = 0;
970                 if (strlen (str) > 1) {
971                         return (++str);
972                 }
973                 else {
974                         return NULL;
975                 }
976         }
977         if ((str = strstr (str, ",")) && strlen (str) > 1) {
978                 str[0] = 0;
979                 return (++str);
980         }
981         return NULL;
986 void
987 print_help (void)
989         print_revision (progname, NP_VERSION);
991         printf (COPYRIGHT, copyright, email);
993         printf ("%s\n", _("Check status of remote machines and obtain system information via SNMP"));
995         printf ("\n\n");
997         print_usage ();
999         printf (UT_HELP_VRSN);
1000         printf (UT_EXTRA_OPTS);
1002         printf (UT_HOST_PORT, 'p', DEFAULT_PORT);
1004         /* SNMP and Authentication Protocol */
1005         printf (" %s\n", "-n, --next");
1006         printf ("    %s\n", _("Use SNMP GETNEXT instead of SNMP GET"));
1007         printf (" %s\n", "-P, --protocol=[1|2c|3]");
1008         printf ("    %s\n", _("SNMP protocol version"));
1009         printf (" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]");
1010         printf ("    %s\n", _("SNMPv3 securityLevel"));
1011         printf (" %s\n", "-a, --authproto=[MD5|SHA]");
1012         printf ("    %s\n", _("SNMPv3 auth proto"));
1013         printf (" %s\n", "-x, --privproto=[DES|AES]");
1014         printf ("    %s\n", _("SNMPv3 priv proto (default DES)"));
1016         /* Authentication Tokens*/
1017         printf (" %s\n", "-C, --community=STRING");
1018         printf ("    %s ", _("Optional community string for SNMP communication"));
1019         printf ("(%s \"%s\")\n", _("default is") ,DEFAULT_COMMUNITY);
1020         printf (" %s\n", "-U, --secname=USERNAME");
1021         printf ("    %s\n", _("SNMPv3 username"));
1022         printf (" %s\n", "-A, --authpassword=PASSWORD");
1023         printf ("    %s\n", _("SNMPv3 authentication password"));
1024         printf (" %s\n", "-X, --privpasswd=PASSWORD");
1025         printf ("    %s\n", _("SNMPv3 privacy password"));
1027         /* OID Stuff */
1028         printf (" %s\n", "-o, --oid=OID(s)");
1029         printf ("    %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query"));
1030         printf (" %s\n", "-m, --miblist=STRING");
1031         printf ("    %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'"));
1032         printf ("    %s\n", _("for symbolic OIDs.)"));
1033         printf (" %s\n", "-d, --delimiter=STRING");
1034         printf ("    %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER);
1035         printf ("    %s\n", _("Any data on the right hand side of the delimiter is considered"));
1036         printf ("    %s\n", _("to be the data that should be used in the evaluation."));
1038         /* Tests Against Integers */
1039         printf (" %s\n", "-w, --warning=THRESHOLD(s)");
1040         printf ("    %s\n", _("Warning threshold range(s)"));
1041         printf (" %s\n", "-c, --critical=THRESHOLD(s)");
1042         printf ("    %s\n", _("Critical threshold range(s)"));
1043         printf (" %s\n", "--rate");
1044         printf ("    %s\n", _("Enable rate calculation. See 'Rate Calculation' below"));
1045         printf (" %s\n", "--rate-multiplier");
1046         printf ("    %s\n", _("Converts rate per second. For example, set to 60 to convert to per minute"));
1048         /* Tests Against Strings */
1049         printf (" %s\n", "-s, --string=STRING");
1050         printf ("    %s\n", _("Return OK state (for that OID) if STRING is an exact match"));
1051         printf (" %s\n", "-r, --ereg=REGEX");
1052         printf ("    %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches"));
1053         printf (" %s\n", "-R, --eregi=REGEX");
1054         printf ("    %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches"));
1055         printf (" %s\n", "--invert-search");
1056         printf ("    %s\n", _("Invert search result (CRITICAL if found)"));
1058         /* Output Formatting */
1059         printf (" %s\n", "-l, --label=STRING");
1060         printf ("    %s\n", _("Prefix label for output from plugin"));
1061         printf (" %s\n", "-u, --units=STRING");
1062         printf ("    %s\n", _("Units label(s) for output data (e.g., 'sec.')."));
1063         printf (" %s\n", "-D, --output-delimiter=STRING");
1064         printf ("    %s\n", _("Separates output on multiple OID requests"));
1066         printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1067         printf (" %s\n", "-e, --retries=INTEGER");
1068         printf ("    %s\n", _("Number of retries to be used in the requests"));
1070         printf (UT_VERBOSE);
1072         printf ("\n");
1073         printf ("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package."));
1074         printf ("%s\n", _("if you don't have the package installed, you will need to download it from"));
1075         printf ("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
1077         printf ("\n");
1078         printf ("%s\n", _("Notes:"));
1079         printf (" %s\n", _("- Multiple OIDs may be indicated by a comma or space-delimited list (lists with"));
1080         printf ("   %s %i %s\n", _("internal spaces must be quoted). Maximum:"), MAX_OIDS, _("OIDs."));
1082         printf(" -%s", UT_THRESHOLDS_NOTES);
1084         printf (" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'"));
1085         printf (" %s\n", _("- Note that only one string and one regex may be checked at present"));
1086         printf (" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value"));
1087         printf ("   %s\n", _("returned from the SNMP query is an unsigned integer."));
1089         printf("\n");
1090         printf("%s\n", _("Rate Calculation:"));
1091         printf(" %s\n", _("In many places, SNMP returns counters that are only meaningful when"));
1092         printf(" %s\n", _("calculating the counter difference since the last check. check_snmp"));
1093         printf(" %s\n", _("saves the last state information in a file so that the rate per second"));
1094         printf(" %s\n", _("can be calculated. Use the --rate option to save state information."));
1095         printf(" %s\n", _("On the first run, there will be no prior state - this will return with OK."));
1096         printf(" %s\n", _("The state is uniquely determined by the arguments to the plugin, so"));
1097         printf(" %s\n", _("changing the arguments will create a new state file."));
1099         printf (UT_SUPPORT);
1104 void
1105 print_usage (void)
1107         printf ("%s\n", _("Usage:"));
1108         printf ("%s -H <ip_address> -o <OID> [-w warn_range] [-c crit_range]\n",progname);
1109         printf ("[-C community] [-s string] [-r regex] [-R regexi] [-t timeout] [-e retries]\n");
1110         printf ("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n");
1111         printf ("[-m miblist] [-P snmp version] [-L seclevel] [-U secname] [-a authproto]\n");
1112         printf ("[-A authpasswd] [-x privproto] [-X privpasswd]\n");