Code

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