1 /*****************************************************************************
2 *
3 * Nagios check_fping plugin
4 *
5 * License: GPL
6 * Copyright (c) 2000-2014 Nagios Plugins Development Team
7 *
8 * Description:
9 *
10 * This file contains the check_disk plugin
11 *
12 * This plugin will use the fping command to ping the specified host for a
13 * fast check
14 *
15 *
16 * This program is free software: you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation, either version 3 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28 *
29 *
30 *****************************************************************************/
31 
32 const char *progname = "check_fping";
33 const char *copyright = "2000-2014";
34 const char *email = "devel@nagios-plugins.org";
35 
36 #include "common.h"
37 #include "popen.h"
38 #include "netutils.h"
39 #include "utils.h"
40 
41 enum {
42   PACKET_COUNT = 1,
43   PACKET_SIZE = 56,
44   PL = 0,
45   RTA = 1
46 };
47 
48 int textscan (char *buf);
49 int process_arguments (int, char **);
50 int get_threshold (char *arg, char *rv[2]);
51 void print_help (void);
52 void print_usage (void);
53 
54 char *server_name = NULL;
55 char *sourceip = NULL;
56 char *sourceif = NULL;
57 int packet_size = PACKET_SIZE;
58 int packet_count = PACKET_COUNT;
59 int target_timeout = 0;
60 int packet_interval = 0;
61 int verbose = FALSE;
62 int cpl;
63 int wpl;
64 double crta;
65 double wrta;
66 int cpl_p = FALSE;
67 int wpl_p = FALSE;
68 int crta_p = FALSE;
69 int wrta_p = FALSE;
70 
71 int
main(int argc,char ** argv)72 main (int argc, char **argv)
73 {
74 /* normally should be  int result = STATE_UNKNOWN; */
75 
76   int status = STATE_UNKNOWN;
77   int result = 0;
78   char *fping_prog = NULL;
79   char *server = NULL;
80   char *command_line = NULL;
81   char *input_buffer = NULL;
82   char *option_string = "";
83   input_buffer = malloc (MAX_INPUT_BUFFER);
84 
85   setlocale (LC_ALL, ""); setlocale(LC_NUMERIC, "C");
86   bindtextdomain (PACKAGE, LOCALEDIR);
87   textdomain (PACKAGE);
88 
89   /* Parse extra opts if any */
90   argv=np_extra_opts (&argc, argv, progname);
91 
92   if (process_arguments (argc, argv) == ERROR)
93     usage4 (_("Could not parse arguments"));
94 
95   server = strscpy (server, server_name);
96 
97   /* compose the command */
98   if (target_timeout)
99     xasprintf(&option_string, "%s-t %d ", option_string, target_timeout);
100   if (packet_interval)
101     xasprintf(&option_string, "%s-p %d ", option_string, packet_interval);
102   if (sourceip)
103     xasprintf(&option_string, "%s-S %s ", option_string, sourceip);
104   if (sourceif)
105     xasprintf(&option_string, "%s-I %s ", option_string, sourceif);
106 
107 #ifdef PATH_TO_FPING6
108   if (address_family != AF_INET && is_inet6_addr(server))
109     fping_prog = strdup(PATH_TO_FPING6);
110   else
111     fping_prog = strdup(PATH_TO_FPING);
112 #else
113   fping_prog = strdup(PATH_TO_FPING);
114 #endif
115 
116   xasprintf (&command_line, "%s %s-b %d -c %d %s", fping_prog,
117             option_string, packet_size, packet_count, server);
118 
119   if (verbose)
120     printf ("%s\n", command_line);
121 
122   /* run the command */
123   child_process = spopen (command_line);
124   if (child_process == NULL) {
125     printf (_("Could not open pipe: %s\n"), command_line);
126     return STATE_UNKNOWN;
127   }
128 
129   child_stderr = fdopen (child_stderr_array[fileno (child_process)], "r");
130   if (child_stderr == NULL) {
131     printf (_("Could not open stderr for %s\n"), command_line);
132   }
133 
134   while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_process)) {
135     if (verbose)
136       printf ("%s", input_buffer);
137     status = max_state (status, textscan (input_buffer));
138   }
139 
140   /* If we get anything on STDERR, at least set warning */
141   while (fgets (input_buffer, MAX_INPUT_BUFFER - 1, child_stderr)) {
142     status = max_state (status, STATE_WARNING);
143     if (verbose)
144       printf ("%s", input_buffer);
145     status = max_state (status, textscan (input_buffer));
146   }
147   (void) fclose (child_stderr);
148 
149   /* close the pipe */
150   if ((result = spclose (child_process)))
151     /* need to use max_state not max */
152     status = max_state (status, STATE_WARNING);
153 
154   if (result > 1 ) {
155     status = max_state (status, STATE_UNKNOWN);
156     if (result == 2) {
157       die (STATE_UNKNOWN, _("FPING UNKNOWN - IP address not found\n"));
158     }
159     if (result == 3) {
160       die (STATE_UNKNOWN, _("FPING UNKNOWN - invalid commandline argument\n"));
161     }
162     if (result == 4) {
163       die (STATE_UNKNOWN, _("FPING UNKNOWN - failed system call\n"));
164     }
165 
166   }
167 
168   printf ("FPING %s - %s\n", state_text (status), server_name);
169 
170   return status;
171 }
172 
173 
174 
175 int
textscan(char * buf)176 textscan (char *buf)
177 {
178   char *rtastr = NULL;
179   char *losstr = NULL;
180   char *xmtstr = NULL;
181   double loss;
182   double rta;
183   double xmt;
184   int status = STATE_UNKNOWN;
185 
186   if (strstr (buf, "not found")) {
187     die (STATE_CRITICAL, _("FPING UNKNOWN - %s not found\n"), server_name);
188 
189   }
190   else if (strstr (buf, "is unreachable") || strstr (buf, "Unreachable")) {
191     die (STATE_CRITICAL, _("FPING CRITICAL - %s is unreachable\n"),
192                "host");
193 
194   }
195   else if (strstr (buf, "Operation not permitted") || strstr (buf, "No such device") ) {
196     die (STATE_UNKNOWN, _("FPING UNKNOWN - %s parameter error\n"),
197                "host");
198   }
199   else if (strstr (buf, "is down")) {
200     die (STATE_CRITICAL, _("FPING CRITICAL - %s is down\n"), server_name);
201 
202   }
203   else if (strstr (buf, "is alive")) {
204     status = STATE_OK;
205 
206   }
207   else if (strstr (buf, "xmt/rcv/%loss") && strstr (buf, "min/avg/max")) {
208     losstr = strstr (buf, "=");
209     losstr = 1 + strstr (losstr, "/");
210     losstr = 1 + strstr (losstr, "/");
211     rtastr = strstr (buf, "min/avg/max");
212     rtastr = strstr (rtastr, "=");
213     rtastr = 1 + index (rtastr, '/');
214     loss = strtod (losstr, NULL);
215     rta = strtod (rtastr, NULL);
216     if (cpl_p == TRUE && loss > cpl)
217       status = STATE_CRITICAL;
218     else if (crta_p == TRUE  && rta > crta)
219       status = STATE_CRITICAL;
220     else if (wpl_p == TRUE && loss > wpl)
221       status = STATE_WARNING;
222     else if (wrta_p == TRUE && rta > wrta)
223       status = STATE_WARNING;
224     else
225       status = STATE_OK;
226     die (status,
227           _("FPING %s - %s (loss=%.0f%%, rta=%f ms)|%s %s\n"),
228          state_text (status), server_name, loss, rta,
229          perfdata ("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, TRUE, 0, TRUE, 100),
230          fperfdata ("rta", rta/1.0e3, "s", wrta_p, wrta/1.0e3, crta_p, crta/1.0e3, TRUE, 0, FALSE, 0));
231 
232   }
233   else if(strstr (buf, "xmt/rcv/%loss") ) {
234     /* no min/max/avg if host was unreachable in fping v2.2.b1 */
235     /* in v2.4b2: 10.99.0.1 : xmt/rcv/%loss = 0/0/0% */
236     losstr = strstr (buf, "=");
237     xmtstr = 1 + losstr;
238     xmt = strtod (xmtstr, NULL);
239     if(xmt == 0)
240         die (STATE_CRITICAL, _("FPING CRITICAL - %s is down\n"), server_name);
241     losstr = 1 + strstr (losstr, "/");
242     losstr = 1 + strstr (losstr, "/");
243     loss = strtod (losstr, NULL);
244     if (atoi(losstr) == 100)
245       status = STATE_CRITICAL;
246     else if (cpl_p == TRUE && loss > cpl)
247       status = STATE_CRITICAL;
248     else if (wpl_p == TRUE && loss > wpl)
249       status = STATE_WARNING;
250     else
251       status = STATE_OK;
252     /* loss=%.0f%%;%d;%d;0;100 */
253     die (status, _("FPING %s - %s (loss=%.0f%% )|%s\n"),
254          state_text (status), server_name, loss ,
255          perfdata ("loss", (long int)loss, "%", wpl_p, wpl, cpl_p, cpl, TRUE, 0, TRUE, 100));
256 
257   }
258   else {
259     status = max_state (status, STATE_WARNING);
260   }
261 
262   return status;
263 }
264 
265 
266 
267 /* process command-line arguments */
268 int
process_arguments(int argc,char ** argv)269 process_arguments (int argc, char **argv)
270 {
271   int c;
272   char *rv[2];
273 
274   int option = 0;
275   static struct option longopts[] = {
276     {"hostname", required_argument, 0, 'H'},
277     {"sourceip", required_argument, 0, 'S'},
278     {"sourceif", required_argument, 0, 'I'},
279     {"critical", required_argument, 0, 'c'},
280     {"warning", required_argument, 0, 'w'},
281     {"bytes", required_argument, 0, 'b'},
282     {"number", required_argument, 0, 'n'},
283     {"target-timeout", required_argument, 0, 'T'},
284     {"interval", required_argument, 0, 'i'},
285     {"verbose", no_argument, 0, 'v'},
286     {"version", no_argument, 0, 'V'},
287     {"help", no_argument, 0, 'h'},
288     {"use-ipv4", no_argument, 0, '4'},
289     {"use-ipv6", no_argument, 0, '6'},
290     {0, 0, 0, 0}
291   };
292 
293   rv[PL] = NULL;
294   rv[RTA] = NULL;
295 
296   if (argc < 2)
297     return ERROR;
298 
299   if (!is_option (argv[1])) {
300     server_name = argv[1];
301     argv[1] = argv[0];
302     argv = &argv[1];
303     argc--;
304   }
305 
306   while (1) {
307     c = getopt_long (argc, argv, "+hVvH:S:c:w:b:n:T:i:I:46", longopts, &option);
308 
309     if (c == -1 || c == EOF || c == 1)
310       break;
311 
312     switch (c) {
313     case '?':                 /* print short usage statement if args not parsable */
314       usage5 ();
315     case 'h':                 /* help */
316       print_help ();
317       exit (STATE_OK);
318     case 'V':                 /* version */
319       print_revision (progname, NP_VERSION);
320       exit (STATE_OK);
321     case 'v':                 /* verbose mode */
322       verbose = TRUE;
323       break;
324     case 'H':                 /* hostname */
325       if (is_host (optarg) == FALSE) {
326         usage2 (_("Invalid hostname/address"), optarg);
327       }
328       server_name = strscpy (server_name, optarg);
329       break;
330     case 'S':                 /* sourceip */
331       if (is_host (optarg) == FALSE) {
332         usage2 (_("Invalid hostname/address"), optarg);
333       }
334       sourceip = strscpy (sourceip, optarg);
335       break;
336     case 'I':                 /* sourceip */
337       sourceif = strscpy (sourceif, optarg);
338     case '4':                 /* IPv4 only */
339       address_family = AF_INET;
340       break;
341     case '6':                 /* IPv6 only */
342 #ifdef USE_IPV6
343       address_family = AF_INET6;
344 #else
345       usage (_("IPv6 support not available\n"));
346 #endif
347       break;
348     case 'c':
349       get_threshold (optarg, rv);
350       if (rv[RTA]) {
351         crta = strtod (rv[RTA], NULL);
352         crta_p = TRUE;
353         rv[RTA] = NULL;
354       }
355       if (rv[PL]) {
356         cpl = atoi (rv[PL]);
357         cpl_p = TRUE;
358         rv[PL] = NULL;
359       }
360       break;
361     case 'w':
362       get_threshold (optarg, rv);
363       if (rv[RTA]) {
364         wrta = strtod (rv[RTA], NULL);
365         wrta_p = TRUE;
366         rv[RTA] = NULL;
367       }
368       if (rv[PL]) {
369         wpl = atoi (rv[PL]);
370         wpl_p = TRUE;
371         rv[PL] = NULL;
372       }
373       break;
374     case 'b':                 /* bytes per packet */
375       if (is_intpos (optarg))
376         packet_size = atoi (optarg);
377       else
378         usage (_("Packet size must be a positive integer"));
379       break;
380     case 'n':                 /* number of packets */
381       if (is_intpos (optarg))
382         packet_count = atoi (optarg);
383       else
384         usage (_("Packet count must be a positive integer"));
385       break;
386     case 'T':                 /* timeout in msec */
387       if (is_intpos (optarg))
388         target_timeout = atoi (optarg);
389       else
390         usage (_("Target timeout must be a positive integer"));
391       break;
392     case 'i':                 /* interval in msec */
393       if (is_intpos (optarg))
394         packet_interval = atoi (optarg);
395       else
396         usage (_("Interval must be a positive integer"));
397       break;
398     }
399   }
400 
401   if (server_name == NULL)
402     usage4 (_("Hostname was not supplied"));
403 
404   return OK;
405 }
406 
407 
408 int
get_threshold(char * arg,char * rv[2])409 get_threshold (char *arg, char *rv[2])
410 {
411   char *arg1 = NULL;
412   char *arg2 = NULL;
413 
414   arg1 = strscpy (arg1, arg);
415   if (strpbrk (arg1, ",:"))
416     arg2 = 1 + strpbrk (arg1, ",:");
417 
418   if (arg2) {
419     arg1[strcspn (arg1, ",:")] = 0;
420     if (strstr (arg1, "%") && strstr (arg2, "%"))
421       die (STATE_UNKNOWN,
422                  _("%s: Only one threshold may be packet loss (%s)\n"), progname,
423                  arg);
424     if (!strstr (arg1, "%") && !strstr (arg2, "%"))
425       die (STATE_UNKNOWN,
426                  _("%s: Only one threshold must be packet loss (%s)\n"),
427                  progname, arg);
428   }
429 
430   if (arg2 && strstr (arg2, "%")) {
431     rv[PL] = arg2;
432     rv[RTA] = arg1;
433   }
434   else if (arg2) {
435     rv[PL] = arg1;
436     rv[RTA] = arg2;
437   }
438   else if (strstr (arg1, "%")) {
439     rv[PL] = arg1;
440   }
441   else {
442     rv[RTA] = arg1;
443   }
444 
445   return OK;
446 }
447 
448 
449 void
print_help(void)450 print_help (void)
451 {
452 
453   print_revision (progname, NP_VERSION);
454 
455   printf ("Copyright (c) 1999 Didi Rieder <adrieder@sbox.tu-graz.ac.at>\n");
456   printf (COPYRIGHT, copyright, email);
457 
458   printf ("%s\n", _("This plugin will use the fping command to ping the specified host for a fast check"));
459 
460   printf ("%s\n", _("Note that it is necessary to set the suid flag on fping."));
461 
462   printf ("\n\n");
463 
464   print_usage ();
465 
466   printf (UT_HELP_VRSN);
467   printf (UT_EXTRA_OPTS);
468 
469   printf (UT_IPv46);
470 
471   printf (" %s\n", "-H, --hostname=HOST");
472   printf ("    %s\n", _("name or IP Address of host to ping (IP Address bypasses name lookup, reducing system load)"));
473   printf (" %s\n", "-w, --warning=THRESHOLD");
474   printf ("    %s\n", _("warning threshold pair"));
475   printf (" %s\n", "-c, --critical=THRESHOLD");
476   printf ("    %s\n", _("critical threshold pair"));
477   printf (" %s\n", "-b, --bytes=INTEGER");
478   printf ("    %s (default: %d)\n", _("size of ICMP packet"),PACKET_SIZE);
479   printf (" %s\n", "-n, --number=INTEGER");
480   printf ("    %s (default: %d)\n", _("number of ICMP packets to send"),PACKET_COUNT);
481   printf (" %s\n", "-T, --target-timeout=INTEGER");
482   printf ("    %s (default: fping's default for -t)\n", _("Target timeout (ms)"));
483   printf (" %s\n", "-i, --interval=INTEGER");
484   printf ("    %s (default: fping's default for -p)\n", _("Interval (ms) between sending packets"));
485   printf (" %s\n", "-S, --sourceip=HOST");
486   printf ("    %s\n", _("name or IP Address of sourceip"));
487   printf (" %s\n", "-I, --sourceif=IF");
488   printf ("    %s\n", _("source interface name"));
489   printf (UT_VERBOSE);
490   printf ("\n");
491   printf (" %s\n", _("THRESHOLD is <rta>,<pl>%% where <rta> is the round trip average travel time (ms)"));
492   printf (" %s\n", _("which triggers a WARNING or CRITICAL state, and <pl> is the percentage of"));
493   printf (" %s\n", _("packet loss to trigger an alarm state."));
494 
495   printf ("\n");
496   printf (" %s\n", _("IPv4 is used by default. Specify -6 to use IPv6."));
497 
498   printf (UT_SUPPORT);
499 }
500 
501 
502 void
print_usage(void)503 print_usage (void)
504 {
505   printf ("%s\n", _("Usage:"));
506   printf (" %s <host_address> -w limit -c limit [-b size] [-n number] [-T number] [-i number]\n", progname);
507 }
508