Code

Replace the lousy multiline parser with a robust one.
[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 /* Gobble to string - stop incrementing c when c[0] match one of the
63  * characters in s */
64 #define GOBBLE_TOS(c, s) while(c[0]!='\0' && strchr(s, c[0])==NULL) { c++; }
65 /* Given c, keep track of backslashes (bk) and double-quotes (dq)
66  * from c[0] */
67 #define COUNT_SEQ(c, bk, dq) switch(c[0]) {\
68         case '\\': \
69                 if (bk) bk--; \
70                 else bk++; \
71                 break; \
72         case '"': \
73                 if (!dq) { dq++; } \
74                 else if(!bk) { dq--; } \
75                 else { bk--; } \
76                 break; \
77         }
81 int process_arguments (int, char **);
82 int validate_arguments (void);
83 char *thisarg (char *str);
84 char *nextarg (char *str);
85 void print_usage (void);
86 void print_help (void);
88 #include "regex.h"
89 char regex_expect[MAX_INPUT_BUFFER] = "";
90 regex_t preg;
91 regmatch_t pmatch[10];
92 char errbuf[MAX_INPUT_BUFFER] = "";
93 char perfstr[MAX_INPUT_BUFFER] = "| ";
94 int cflags = REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
95 int eflags = 0;
96 int errcode, excode;
98 char *server_address = NULL;
99 char *community = NULL;
100 char **authpriv = NULL;
101 char *proto = NULL;
102 char *seclevel = NULL;
103 char *secname = NULL;
104 char *authproto = NULL;
105 char *privproto = NULL;
106 char *authpasswd = NULL;
107 char *privpasswd = NULL;
108 char **oids = NULL;
109 char *label;
110 char *units;
111 char *port;
112 char *snmpcmd;
113 char string_value[MAX_INPUT_BUFFER] = "";
114 char **labels = NULL;
115 char **unitv = NULL;
116 size_t nlabels = 0;
117 size_t labels_size = 8;
118 size_t nunits = 0;
119 size_t unitv_size = 8;
120 int numoids = 0;
121 int numauthpriv = 0;
122 int verbose = 0;
123 int usesnmpgetnext = FALSE;
124 char *warning_thresholds = NULL;
125 char *critical_thresholds = NULL;
126 thresholds *thlds[MAX_OIDS];
127 double response_value[MAX_OIDS];
128 int retries = 0;
129 int eval_method[MAX_OIDS];
130 char *delimiter;
131 char *output_delim;
132 char *miblist = NULL;
133 int needmibs = FALSE;
136 int
137 main (int argc, char **argv)
139         int i, len, line;
140         unsigned int bk_count = 0, dq_count = 0;
141         int iresult = STATE_UNKNOWN;
142         int result = STATE_UNKNOWN;
143         int return_code = 0;
144         int external_error = 0;
145         char **command_line = NULL;
146         char *cl_hidden_auth = NULL;
147         char *oidname = NULL;
148         char *response = NULL;
149         char *mult_resp = NULL;
150         char *outbuff;
151         char *ptr = NULL;
152         char *show = NULL;
153         char *th_warn=NULL;
154         char *th_crit=NULL;
155         char type[8] = "";
156         output chld_out, chld_err;
158         setlocale (LC_ALL, "");
159         bindtextdomain (PACKAGE, LOCALEDIR);
160         textdomain (PACKAGE);
162         labels = malloc (labels_size);
163         unitv = malloc (unitv_size);
164         for (i = 0; i < MAX_OIDS; i++)
165                 eval_method[i] = CHECK_UNDEF;
167         label = strdup ("SNMP");
168         units = strdup ("");
169         port = strdup (DEFAULT_PORT);
170         outbuff = strdup ("");
171         delimiter = strdup (" = ");
172         output_delim = strdup (DEFAULT_OUTPUT_DELIMITER);
173         timeout_interval = DEFAULT_TIMEOUT;
174         retries = DEFAULT_RETRIES;
176         /* Parse extra opts if any */
177         argv=np_extra_opts (&argc, argv, progname);
179         if (process_arguments (argc, argv) == ERROR)
180                 usage4 (_("Could not parse arguments"));
182         /* Populate the thresholds */
183         th_warn=warning_thresholds;
184         th_crit=critical_thresholds;
185         for (i=0; i<numoids; i++) {
186                 char *w = th_warn ? strndup(th_warn, strcspn(th_warn, ",")) : NULL;
187                 char *c = th_crit ? strndup(th_crit, strcspn(th_crit, ",")) : NULL;
188                 /* Skip empty thresholds, while avoiding segfault */
189                 set_thresholds(&thlds[i],
190                                w ? strpbrk(w, NP_THRESHOLDS_CHARS) : NULL,
191                                c ? strpbrk(c, NP_THRESHOLDS_CHARS) : NULL);
192                 if (w) {
193                         th_warn=strchr(th_warn, ',');
194                         if (th_warn) th_warn++;
195                         free(w);
196                 }
197                 if (c) {
198                         th_crit=strchr(th_crit, ',');
199                         if (th_crit) th_crit++;
200                         free(c);
201                 }
202         }
204         /* Create the command array to execute */
205         if(usesnmpgetnext == TRUE) {
206                 snmpcmd = strdup (PATH_TO_SNMPGETNEXT);
207         }else{
208                 snmpcmd = strdup (PATH_TO_SNMPGET);
209         }
211         /* 9 arguments to pass before authpriv options + 1 for host and numoids. Add one for terminating NULL */
212         command_line = calloc (9 + numauthpriv + 1 + numoids + 1, sizeof (char *));
213         command_line[0] = snmpcmd;
214         command_line[1] = strdup ("-t");
215         asprintf (&command_line[2], "%d", timeout_interval);
216         command_line[3] = strdup ("-r");
217         asprintf (&command_line[4], "%d", retries);
218         command_line[5] = strdup ("-m");
219         command_line[6] = strdup (miblist);
220         command_line[7] = "-v";
221         command_line[8] = strdup (proto);
223         for (i = 0; i < numauthpriv; i++) {
224                 command_line[9 + i] = authpriv[i];
225         }
227         asprintf (&command_line[9 + numauthpriv], "%s:%s", server_address, port);
229         /* This is just for display purposes, so it can remain a string */
230         asprintf(&cl_hidden_auth, "%s -t %d -r %d -m %s -v %s %s %s:%s",
231                 snmpcmd, timeout_interval, retries, strlen(miblist) ? miblist : "''", proto, "[authpriv]",
232                 server_address, port);
234         for (i = 0; i < numoids; i++) {
235                 command_line[9 + numauthpriv + 1 + i] = oids[i];
236                 asprintf(&cl_hidden_auth, "%s %s", cl_hidden_auth, oids[i]);    
237         }
239         command_line[9 + numauthpriv + 1 + numoids] = NULL;
241         if (verbose)
242                 printf ("%s\n", cl_hidden_auth);
244         /* Run the command */
245         return_code = cmd_run_array (command_line, &chld_out, &chld_err, 0);
247         /* Due to net-snmp sometimes showing stderr messages with poorly formed MIBs,
248            only return state unknown if return code is non zero or there is no stdout.
249            Do this way so that if there is stderr, will get added to output, which helps problem diagnosis
250         */
251         if (return_code != 0)
252                 external_error=1;
253         if (chld_out.lines == 0)
254                 external_error=1;
255         if (external_error) {
256                 if (chld_err.lines > 0) {
257                         printf (_("External command error: %s\n"), chld_err.line[0]);
258                         for (i = 1; i < chld_err.lines; i++) {
259                                 printf ("%s\n", chld_err.line[i]);
260                         }
261                 } else {
262                         printf(_("External command error with no output (return code: %d)\n"), return_code);
263                 }
264                 exit (STATE_UNKNOWN);
265         }
267         if (verbose) {
268                 for (i = 0; i < chld_out.lines; i++) {
269                         printf ("%s\n", chld_out.line[i]);
270                 }
271         }
273         for (line=0, i=0; line < chld_out.lines; line++, i++) {
274                 const char *conv = "%.0f";
276                 ptr = chld_out.line[line];
277                 oidname = strpcpy (oidname, ptr, delimiter);
278                 response = strstr (ptr, delimiter);
279                 if (response == NULL)
280                         break;
282                 if (verbose > 2) {
283                         printf("Processing oid %i (line %i)\n  oidname: %s\n  response: %s\n", i+1, line+1, oidname, response);
284                 }
286                 /* Clean up type array - Sol10 does not necessarily zero it out */
287                 bzero(type, sizeof(type));
289                 /* We strip out the datatype indicator for PHBs */
290                 if (strstr (response, "Gauge: "))
291                         show = strstr (response, "Gauge: ") + 7;
292                 else if (strstr (response, "Gauge32: "))
293                         show = strstr (response, "Gauge32: ") + 9;
294                 else if (strstr (response, "Counter32: ")) {
295                         show = strstr (response, "Counter32: ") + 11;
296                         strcpy(type, "c");
297                 }
298                 else if (strstr (response, "Counter64: ")) {
299                         show = strstr (response, "Counter64: ") + 11;
300                         strcpy(type, "c");
301                 }
302                 else if (strstr (response, "INTEGER: "))
303                         show = strstr (response, "INTEGER: ") + 9;
304                 else if (strstr (response, "STRING: ")) {
305                         show = strstr (response, "STRING: ") + 8;
306                         conv = "%.10g";
308                         /* Get the rest of the string on multi-line strings */
309                         ptr = show;
310                         COUNT_SEQ(ptr, bk_count, dq_count)
311                         while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
312                                 ptr++;
313                                 GOBBLE_TOS(ptr, "\n\"\\")
314                                 COUNT_SEQ(ptr, bk_count, dq_count)
315                         }
317                         if (dq_count) { /* unfinished line */
318                                 /* copy show verbatim first */
319                                 if (!mult_resp) mult_resp = strdup("");
320                                 asprintf (&mult_resp, "%s%s:\n%s\n", mult_resp, oids[i], show);
321                                 /* then strip out unmatched double-quote from single-line output */
322                                 if (show[0] == '"') show++;
324                                 /* Keep reading until we match end of double-quoted string */
325                                 for (line++; line < chld_out.lines; line++) {
326                                         ptr = chld_out.line[line];
327                                         asprintf (&mult_resp, "%s%s\n", mult_resp, ptr);
329                                         COUNT_SEQ(ptr, bk_count, dq_count)
330                                         while (dq_count && ptr[0] != '\n' && ptr[0] != '\0') {
331                                                 ptr++;
332                                                 GOBBLE_TOS(ptr, "\n\"\\")
333                                                 COUNT_SEQ(ptr, bk_count, dq_count)
334                                         }
335                                         /* Break for loop before next line increment when done */
336                                         if (!dq_count) break;
337                                 }
338                         }
340                 }
341                 else if (strstr (response, "Timeticks: "))
342                         show = strstr (response, "Timeticks: ");
343                 else
344                         show = response;
346                 iresult = STATE_DEPENDENT;
348                 /* Process this block for integer comparisons */
349                 if (thlds[i]->warning || thlds[i]->critical) {
350                         ptr = strpbrk (show, "0123456789");
351                         if (ptr == NULL)
352                                 die (STATE_UNKNOWN,_("No valid data returned"));
353                         response_value[i] = strtod (ptr, NULL);
354                         iresult = get_status(response_value[i], thlds[i]);
355                         asprintf (&show, conv, response_value[i]);
356                 }
358                 /* Process this block for string matching */
359                 else if (eval_method[i] & CRIT_STRING) {
360                         if (strcmp (show, string_value))
361                                 iresult = STATE_CRITICAL;
362                         else
363                                 iresult = STATE_OK;
364                 }
366                 /* Process this block for regex matching */
367                 else if (eval_method[i] & CRIT_REGEX) {
368                         excode = regexec (&preg, response, 10, pmatch, eflags);
369                         if (excode == 0) {
370                                 iresult = STATE_OK;
371                         }
372                         else if (excode != REG_NOMATCH) {
373                                 regerror (excode, &preg, errbuf, MAX_INPUT_BUFFER);
374                                 printf (_("Execute Error: %s\n"), errbuf);
375                                 exit (STATE_CRITICAL);
376                         }
377                         else {
378                                 iresult = STATE_CRITICAL;
379                         }
380                 }
382                 /* Process this block for existence-nonexistence checks */
383                 else {
384                         if (eval_method[i] & CRIT_PRESENT)
385                                 iresult = STATE_CRITICAL;
386                         else if (eval_method[i] & WARN_PRESENT)
387                                 iresult = STATE_WARNING;
388                         else if (response && iresult == STATE_DEPENDENT)
389                                 iresult = STATE_OK;
390                 }
392                 /* Result is the worst outcome of all the OIDs tested */
393                 result = max_state (result, iresult);
395                 /* Prepend a label for this OID if there is one */
396                 if (nlabels > (size_t)1 && (size_t)i < nlabels && labels[i] != NULL)
397                         asprintf (&outbuff, "%s%s%s %s%s%s", outbuff,
398                                 (i == 0) ? " " : output_delim,
399                                 labels[i], mark (iresult), show, mark (iresult));
400                 else
401                         asprintf (&outbuff, "%s%s%s%s%s", outbuff, (i == 0) ? " " : output_delim,
402                                 mark (iresult), show, mark (iresult));
404                 /* Append a unit string for this OID if there is one */
405                 if (nunits > (size_t)0 && (size_t)i < nunits && unitv[i] != NULL)
406                         asprintf (&outbuff, "%s %s", outbuff, unitv[i]);
408                 /* Write perfdata with whatever can be parsed by strtod, if possible */
409                 ptr = NULL;
410                 strtod(show, &ptr);
411                 if (ptr > show) {
412                         strncat(perfstr, oidname, sizeof(perfstr)-strlen(perfstr)-1);
413                         strncat(perfstr, "=", sizeof(perfstr)-strlen(perfstr)-1);
414                         len = sizeof(perfstr)-strlen(perfstr)-1;
415                         strncat(perfstr, show, len>ptr-show ? ptr-show : len);
417                         if (type)
418                                 strncat(perfstr, type, sizeof(perfstr)-strlen(perfstr)-1);
419                         strncat(perfstr, " ", sizeof(perfstr)-strlen(perfstr)-1);
420                 }
421         }
423         printf ("%s %s -%s %s\n", label, state_text (result), outbuff, perfstr);
424         if (mult_resp) printf ("%s", mult_resp);
426         return result;
431 /* process command-line arguments */
432 int
433 process_arguments (int argc, char **argv)
435         char *ptr;
436         int c = 1;
437         int j = 0, jj = 0, ii = 0;
439         int option = 0;
440         static struct option longopts[] = {
441                 STD_LONG_OPTS,
442                 {"community", required_argument, 0, 'C'},
443                 {"oid", required_argument, 0, 'o'},
444                 {"object", required_argument, 0, 'o'},
445                 {"delimiter", required_argument, 0, 'd'},
446                 {"output-delimiter", required_argument, 0, 'D'},
447                 {"string", required_argument, 0, 's'},
448                 {"timeout", required_argument, 0, 't'},
449                 {"regex", required_argument, 0, 'r'},
450                 {"ereg", required_argument, 0, 'r'},
451                 {"eregi", required_argument, 0, 'R'},
452                 {"label", required_argument, 0, 'l'},
453                 {"units", required_argument, 0, 'u'},
454                 {"port", required_argument, 0, 'p'},
455                 {"retries", required_argument, 0, 'e'},
456                 {"miblist", required_argument, 0, 'm'},
457                 {"protocol", required_argument, 0, 'P'},
458                 {"seclevel", required_argument, 0, 'L'},
459                 {"secname", required_argument, 0, 'U'},
460                 {"authproto", required_argument, 0, 'a'},
461                 {"privproto", required_argument, 0, 'x'},
462                 {"authpasswd", required_argument, 0, 'A'},
463                 {"privpasswd", required_argument, 0, 'X'},
464                 {"next", no_argument, 0, 'n'},
465                 {0, 0, 0, 0}
466         };
468         if (argc < 2)
469                 return ERROR;
471         /* reverse compatibility for very old non-POSIX usage forms */
472         for (c = 1; c < argc; c++) {
473                 if (strcmp ("-to", argv[c]) == 0)
474                         strcpy (argv[c], "-t");
475                 if (strcmp ("-wv", argv[c]) == 0)
476                         strcpy (argv[c], "-w");
477                 if (strcmp ("-cv", argv[c]) == 0)
478                         strcpy (argv[c], "-c");
479         }
481         while (1) {
482                 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:",
483                                                                          longopts, &option);
485                 if (c == -1 || c == EOF)
486                         break;
488                 switch (c) {
489                 case '?':       /* usage */
490                         usage5 ();
491                 case 'h':       /* help */
492                         print_help ();
493                         exit (STATE_OK);
494                 case 'V':       /* version */
495                         print_revision (progname, NP_VERSION);
496                         exit (STATE_OK);
497                 case 'v': /* verbose */
498                         verbose++;
499                         break;
501         /* Connection info */
502                 case 'C':                                                                       /* group or community */
503                         community = optarg;
504                         break;
505                 case 'H':                                                                       /* Host or server */
506                         server_address = optarg;
507                         break;
508                 case 'p':       /* TCP port number */
509                         port = optarg;
510                         break;
511                 case 'm':       /* List of MIBS */
512                         miblist = optarg;
513                         break;
514                 case 'n':       /* usesnmpgetnext */
515                         usesnmpgetnext = TRUE;
516                         break;
517                 case 'P':       /* SNMP protocol version */
518                         proto = optarg;
519                         break;
520                 case 'L':       /* security level */
521                         seclevel = optarg;
522                         break;
523                 case 'U':       /* security username */
524                         secname = optarg;
525                         break;
526                 case 'a':       /* auth protocol */
527                         authproto = optarg;
528                         break;
529                 case 'x':       /* priv protocol */
530                         privproto = optarg;
531                         break;
532                 case 'A':       /* auth passwd */
533                         authpasswd = optarg;
534                         break;
535                 case 'X':       /* priv passwd */
536                         privpasswd = optarg;
537                         break;
538                 case 't':       /* timeout period */
539                         if (!is_integer (optarg))
540                                 usage2 (_("Timeout interval must be a positive integer"), optarg);
541                         else
542                                 timeout_interval = atoi (optarg);
543                         break;
545         /* Test parameters */
546                 case 'c':                                                                       /* critical threshold */
547                         critical_thresholds = optarg;
548                         break;
549                 case 'w':                                                                       /* warning threshold */
550                         warning_thresholds = optarg;
551                         break;
552                 case 'e': /* PRELIMINARY - may change */
553                 case 'E': /* PRELIMINARY - may change */
554                         if (!is_integer (optarg))
555                                 usage2 (_("Retries interval must be a positive integer"), optarg);
556                         else
557                                 retries = atoi(optarg);
558                         break;
559                 case 'o':                                                                       /* object identifier */
560                         if ( strspn( optarg, "0123456789.," ) != strlen( optarg ) ) {
561                                         /*
562                                          * we have something other than digits, periods and comas,
563                                          * so we have a mib variable, rather than just an SNMP OID,
564                                          * so we have to actually read the mib files
565                                          */
566                                         needmibs = TRUE;
567                         }
568                         if (!oids) oids = calloc(MAX_OIDS, sizeof (char *));
569                         for (ptr = strtok(optarg, ", "); ptr != NULL && j < MAX_OIDS; ptr = strtok(NULL, ", "), j++) {
570                                 oids[j] = strdup(ptr);
571                         }
572                         numoids = j;
573                         if (c == 'E' || c == 'e') {
574                                 jj++;
575                                 ii++;
576                         }
577                         if (c == 'E')
578                                 eval_method[j+1] |= WARN_PRESENT;
579                         else if (c == 'e')
580                                 eval_method[j+1] |= CRIT_PRESENT;
581                         break;
582                 case 's':                                                                       /* string or substring */
583                         strncpy (string_value, optarg, sizeof (string_value) - 1);
584                         string_value[sizeof (string_value) - 1] = 0;
585                         eval_method[jj++] = CRIT_STRING;
586                         ii++;
587                         break;
588                 case 'R':                                                                       /* regex */
589                         cflags = REG_ICASE;
590                 case 'r':                                                                       /* regex */
591                         cflags |= REG_EXTENDED | REG_NOSUB | REG_NEWLINE;
592                         strncpy (regex_expect, optarg, sizeof (regex_expect) - 1);
593                         regex_expect[sizeof (regex_expect) - 1] = 0;
594                         errcode = regcomp (&preg, regex_expect, cflags);
595                         if (errcode != 0) {
596                                 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
597                                 printf (_("Could Not Compile Regular Expression"));
598                                 return ERROR;
599                         }
600                         eval_method[jj++] = CRIT_REGEX;
601                         ii++;
602                         break;
604         /* Format */
605                 case 'd':                                                                       /* delimiter */
606                         delimiter = strscpy (delimiter, optarg);
607                         break;
608                 case 'D':                                                                       /* output-delimiter */
609                         output_delim = strscpy (output_delim, optarg);
610                         break;
611                 case 'l':                                                                       /* label */
612                         label = optarg;
613                         nlabels++;
614                         if (nlabels >= labels_size) {
615                                 labels_size += 8;
616                                 labels = realloc (labels, labels_size);
617                                 if (labels == NULL)
618                                         die (STATE_UNKNOWN, _("Could not reallocate labels[%d]"), (int)nlabels);
619                         }
620                         labels[nlabels - 1] = optarg;
621                         ptr = thisarg (optarg);
622                         labels[nlabels - 1] = ptr;
623                         if (strstr (ptr, "'") == ptr)
624                                 labels[nlabels - 1] = ptr + 1;
625                         while (ptr && (ptr = nextarg (ptr))) {
626                                 if (nlabels >= labels_size) {
627                                         labels_size += 8;
628                                         labels = realloc (labels, labels_size);
629                                         if (labels == NULL)
630                                                 die (STATE_UNKNOWN, _("Could not reallocate labels\n"));
631                                 }
632                                 labels++;
633                                 ptr = thisarg (ptr);
634                                 if (strstr (ptr, "'") == ptr)
635                                         labels[nlabels - 1] = ptr + 1;
636                                 else
637                                         labels[nlabels - 1] = ptr;
638                         }
639                         break;
640                 case 'u':                                                                       /* units */
641                         units = optarg;
642                         nunits++;
643                         if (nunits >= unitv_size) {
644                                 unitv_size += 8;
645                                 unitv = realloc (unitv, unitv_size);
646                                 if (unitv == NULL)
647                                         die (STATE_UNKNOWN, _("Could not reallocate units [%d]\n"), (int)nunits);
648                         }
649                         unitv[nunits - 1] = optarg;
650                         ptr = thisarg (optarg);
651                         unitv[nunits - 1] = ptr;
652                         if (strstr (ptr, "'") == ptr)
653                                 unitv[nunits - 1] = ptr + 1;
654                         while (ptr && (ptr = nextarg (ptr))) {
655                                 if (nunits >= unitv_size) {
656                                         unitv_size += 8;
657                                         unitv = realloc (unitv, unitv_size);
658                                         if (units == NULL)
659                                                 die (STATE_UNKNOWN, _("Could not realloc() units\n"));
660                                 }
661                                 nunits++;
662                                 ptr = thisarg (ptr);
663                                 if (strstr (ptr, "'") == ptr)
664                                         unitv[nunits - 1] = ptr + 1;
665                                 else
666                                         unitv[nunits - 1] = ptr;
667                         }
668                         break;
670                 }
671         }
673         if (server_address == NULL)
674                 server_address = argv[optind];
676         if (community == NULL)
677                 community = strdup (DEFAULT_COMMUNITY);
679         return validate_arguments ();
683 /******************************************************************************
685 @@-
686 <sect3>
687 <title>validate_arguments</title>
689 <para>&PROTO_validate_arguments;</para>
691 <para>Checks to see if the default miblist needs to be loaded. Also verifies
692 the authentication and authorization combinations based on protocol version
693 selected.</para>
695 <para></para>
697 </sect3>
698 -@@
699 ******************************************************************************/
703 int
704 validate_arguments ()
706         /* check whether to load locally installed MIBS (CPU/disk intensive) */
707         if (miblist == NULL) {
708                 if ( needmibs == TRUE ) {
709                         miblist = strdup (DEFAULT_MIBLIST);
710                 }else{
711                         miblist = "";                   /* don't read any mib files for numeric oids */
712                 }
713         }
715         /* Check server_address is given */
716         if (server_address == NULL)
717                 die(STATE_UNKNOWN, _("No host specified\n"));
719         /* Check oid is given */
720         if (numoids == 0)
721                 die(STATE_UNKNOWN, _("No OIDs specified\n"));
723         if (proto == NULL)
724                 asprintf(&proto, DEFAULT_PROTOCOL);
726         if ((strcmp(proto,"1") == 0) || (strcmp(proto, "2c")==0)) {     /* snmpv1 or snmpv2c */
727                 numauthpriv = 2;
728                 authpriv = calloc (numauthpriv, sizeof (char *));
729                 authpriv[0] = strdup ("-c");
730                 authpriv[1] = strdup (community);
731         }
732         else if ( strcmp (proto, "3") == 0 ) {          /* snmpv3 args */
733                 if (seclevel == NULL)
734                         asprintf(&seclevel, "noAuthNoPriv");
736                 if (strcmp(seclevel, "noAuthNoPriv") == 0) {
737                         numauthpriv = 2;
738                         authpriv = calloc (numauthpriv, sizeof (char *));
739                         authpriv[0] = strdup ("-l");
740                         authpriv[1] = strdup ("noAuthNoPriv");
741                 } else {
742                         if (! ( (strcmp(seclevel, "authNoPriv")==0) || (strcmp(seclevel, "authPriv")==0) ) ) {
743                                 usage2 (_("Invalid seclevel"), seclevel);
744                         }
746                         if (authproto == NULL )
747                                 asprintf(&authproto, DEFAULT_AUTH_PROTOCOL);
749                         if (secname == NULL)
750                                 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "secname");
752                         if (authpasswd == NULL)
753                                 die(STATE_UNKNOWN, _("Required parameter: %s\n"), "authpasswd");
755                         if ( strcmp(seclevel, "authNoPriv") == 0 ) {
756                                 numauthpriv = 8;
757                                 authpriv = calloc (numauthpriv, sizeof (char *));
758                                 authpriv[0] = strdup ("-l");
759                                 authpriv[1] = strdup ("authNoPriv");
760                                 authpriv[2] = strdup ("-a");
761                                 authpriv[3] = strdup (authproto);
762                                 authpriv[4] = strdup ("-u");
763                                 authpriv[5] = strdup (secname);
764                                 authpriv[6] = strdup ("-A");
765                                 authpriv[7] = strdup (authpasswd);
766                         } else if ( strcmp(seclevel, "authPriv") == 0 ) {
767                                 if (privproto == NULL )
768                                         asprintf(&privproto, DEFAULT_PRIV_PROTOCOL);
770                                 if (privpasswd == NULL)
771                                         die(STATE_UNKNOWN, _("Required parameter: %s\n"), "privpasswd");
773                                 numauthpriv = 12;
774                                 authpriv = calloc (numauthpriv, sizeof (char *));
775                                 authpriv[0] = strdup ("-l");
776                                 authpriv[1] = strdup ("authPriv");
777                                 authpriv[2] = strdup ("-a");
778                                 authpriv[3] = strdup (authproto);
779                                 authpriv[4] = strdup ("-u");
780                                 authpriv[5] = strdup (secname);
781                                 authpriv[6] = strdup ("-A");
782                                 authpriv[7] = strdup (authpasswd);
783                                 authpriv[8] = strdup ("-x");
784                                 authpriv[9] = strdup (privproto);
785                                 authpriv[10] = strdup ("-X");
786                                 authpriv[11] = strdup (privpasswd);
787                         }
788                 }
790         }
791         else {
792                 usage2 (_("Invalid SNMP version"), proto);
793         }
795         return OK;
800 /* trim leading whitespace
801          if there is a leading quote, make sure it balances */
803 char *
804 thisarg (char *str)
806         str += strspn (str, " \t\r\n"); /* trim any leading whitespace */
807         if (strstr (str, "'") == str) { /* handle SIMPLE quoted strings */
808                 if (strlen (str) == 1 || !strstr (str + 1, "'"))
809                         die (STATE_UNKNOWN, _("Unbalanced quotes\n"));
810         }
811         return str;
816 /* if there's a leading quote, advance to the trailing quote
817          set the trailing quote to '\x0'
818          if the string continues, advance beyond the comma */
820 char *
821 nextarg (char *str)
823         if (strstr (str, "'") == str) {
824                 str[0] = 0;
825                 if (strlen (str) > 1) {
826                         str = strstr (str + 1, "'");
827                         return (++str);
828                 }
829                 else {
830                         return NULL;
831                 }
832         }
833         if (strstr (str, ",") == str) {
834                 str[0] = 0;
835                 if (strlen (str) > 1) {
836                         return (++str);
837                 }
838                 else {
839                         return NULL;
840                 }
841         }
842         if ((str = strstr (str, ",")) && strlen (str) > 1) {
843                 str[0] = 0;
844                 return (++str);
845         }
846         return NULL;
851 void
852 print_help (void)
854         print_revision (progname, NP_VERSION);
856         printf (COPYRIGHT, copyright, email);
858         printf ("%s\n", _("Check status of remote machines and obtain system information via SNMP"));
860         printf ("\n\n");
862         print_usage ();
864         printf (UT_HELP_VRSN);
865         printf (UT_EXTRA_OPTS);
867         printf (UT_HOST_PORT, 'p', DEFAULT_PORT);
869         /* SNMP and Authentication Protocol */
870         printf (" %s\n", "-n, --next");
871         printf ("    %s\n", _("Use SNMP GETNEXT instead of SNMP GET"));
872         printf (" %s\n", "-P, --protocol=[1|2c|3]");
873         printf ("    %s\n", _("SNMP protocol version"));
874         printf (" %s\n", "-L, --seclevel=[noAuthNoPriv|authNoPriv|authPriv]");
875         printf ("    %s\n", _("SNMPv3 securityLevel"));
876         printf (" %s\n", "-a, --authproto=[MD5|SHA]");
877         printf ("    %s\n", _("SNMPv3 auth proto"));
878         printf (" %s\n", "-x, --privproto=[DES|AES]");
879         printf ("    %s\n", _("SNMPv3 priv proto (default DES)"));
881         /* Authentication Tokens*/
882         printf (" %s\n", "-C, --community=STRING");
883         printf ("    %s ", _("Optional community string for SNMP communication"));
884         printf ("(%s \"%s\")\n", _("default is") ,DEFAULT_COMMUNITY);
885         printf (" %s\n", "-U, --secname=USERNAME");
886         printf ("    %s\n", _("SNMPv3 username"));
887         printf (" %s\n", "-A, --authpassword=PASSWORD");
888         printf ("    %s\n", _("SNMPv3 authentication password"));
889         printf (" %s\n", "-X, --privpasswd=PASSWORD");
890         printf ("    %s\n", _("SNMPv3 privacy password"));
892         /* OID Stuff */
893         printf (" %s\n", "-o, --oid=OID(s)");
894         printf ("    %s\n", _("Object identifier(s) or SNMP variables whose value you wish to query"));
895         printf (" %s\n", "-m, --miblist=STRING");
896         printf ("    %s\n", _("List of MIBS to be loaded (default = none if using numeric OIDs or 'ALL'"));
897         printf ("    %s\n", _("for symbolic OIDs.)"));
898         printf (" %s\n", "-d, --delimiter=STRING");
899         printf ("    %s \"%s\"\n", _("Delimiter to use when parsing returned data. Default is"), DEFAULT_DELIMITER);
900         printf ("    %s\n", _("Any data on the right hand side of the delimiter is considered"));
901         printf ("    %s\n", _("to be the data that should be used in the evaluation."));
903         /* Tests Against Integers */
904         printf (" %s\n", "-w, --warning=THRESHOLD(s)");
905         printf ("    %s\n", _("Warning threshold range(s)"));
906         printf (" %s\n", "-c, --critical=THRESHOLD(s)");
907         printf ("    %s\n", _("Critical threshold range(s)"));
909         /* Tests Against Strings */
910         printf (" %s\n", "-s, --string=STRING");
911         printf ("    %s\n", _("Return OK state (for that OID) if STRING is an exact match"));
912         printf (" %s\n", "-r, --ereg=REGEX");
913         printf ("    %s\n", _("Return OK state (for that OID) if extended regular expression REGEX matches"));
914         printf (" %s\n", "-R, --eregi=REGEX");
915         printf ("    %s\n", _("Return OK state (for that OID) if case-insensitive extended REGEX matches"));
916         printf (" %s\n", "-l, --label=STRING");
917         printf ("    %s\n", _("Prefix label for output from plugin (default -s 'SNMP')"));
919         /* Output Formatting */
920         printf (" %s\n", "-u, --units=STRING");
921         printf ("    %s\n", _("Units label(s) for output data (e.g., 'sec.')."));
922         printf (" %s\n", "-D, --output-delimiter=STRING");
923         printf ("    %s\n", _("Separates output on multiple OID requests"));
925         printf (UT_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
926         printf (" %s\n", "-e, --retries=INTEGER");
927         printf ("    %s\n", _("Number of retries to be used in the requests"));
929         printf (UT_VERBOSE);
931         printf ("\n");
932         printf ("%s\n", _("This plugin uses the 'snmpget' command included with the NET-SNMP package."));
933         printf ("%s\n", _("if you don't have the package installed, you will need to download it from"));
934         printf ("%s\n", _("http://net-snmp.sourceforge.net before you can use this plugin."));
936         printf ("\n");
937         printf ("%s\n", _("Notes:"));
938         printf (" %s\n", _("- Multiple OIDs may be indicated by a comma- or space-delimited list (lists with"));
939         printf ("   %s\n", _("internal spaces must be quoted) [max 8 OIDs]"));
941         printf(" -%s", UT_THRESHOLDS_NOTES);
943         printf (" %s\n", _("- When checking multiple OIDs, separate ranges by commas like '-w 1:10,1:,:20'"));
944         printf (" %s\n", _("- Note that only one string and one regex may be checked at present"));
945         printf (" %s\n", _("- All evaluation methods other than PR, STR, and SUBSTR expect that the value"));
946         printf ("   %s\n", _("returned from the SNMP query is an unsigned integer."));
947 #ifdef NP_EXTRA_OPTS
948         printf (" -%s", UT_EXTRA_OPTS_NOTES);
949 #endif
951         printf (UT_SUPPORT);
956 void
957 print_usage (void)
959         printf (_("Usage:"));
960         printf ("%s -H <ip_address> -o <OID> [-w warn_range] [-c crit_range]\n",progname);
961         printf ("[-C community] [-s string] [-r regex] [-R regexi] [-t timeout] [-e retries]\n");
962         printf ("[-l label] [-u units] [-p port-number] [-d delimiter] [-D output-delimiter]\n");
963         printf ("[-m miblist] [-P snmp version] [-L seclevel] [-U secname] [-a authproto]\n");
964         printf ("[-A authpasswd] [-x privproto] [-X privpasswd]\n");