check_fping_in.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. /******************************************************************************
  2. *
  3. * CHECK_INET_FPING.C
  4. *
  5. * Program: Fping plugin for Nagios
  6. * License: GPL
  7. * Copyright (c) 1999 Didi Rieder (adrieder@sbox.tu-graz.ac.at)
  8. * $Id$
  9. *
  10. * Modifications:
  11. *
  12. * 08-24-1999 Didi Rieder (adrieder@sbox.tu-graz.ac.at)
  13. * Intial Coding
  14. * 09-11-1999 Karl DeBisschop (kdebiss@alum.mit.edu)
  15. * Change to spopen
  16. * Fix so that state unknown is returned by default
  17. * (formerly would give state ok if no fping specified)
  18. * Add server_name to output
  19. * Reformat to 80-character standard screen
  20. * 11-18-1999 Karl DeBisschop (kdebiss@alum.mit.edu)
  21. * set STATE_WARNING of stderr written or nonzero status returned
  22. * 09-29-2000 Matthew Grant (matthewg@plain.co.nz)
  23. * changes for monitoring multiple hosts for checking Internet
  24. * reachibility
  25. *
  26. * Description:
  27. *
  28. * This plugin will use the /bin/fping command (from nagios) to ping
  29. * the specified host for a fast check if the host is alive. Note that
  30. * it is necessary to set the suid flag on fping.
  31. ******************************************************************************/
  32. #include "config.h"
  33. #include "common.h"
  34. #include "popen.h"
  35. #include "utils.h"
  36. #define PROGNAME "check_fping"
  37. #define PACKET_COUNT 15
  38. #define PACKET_SIZE 56
  39. #define CRITICAL_COUNT 2
  40. #define WARNING_COUNT 1
  41. #define UNKNOWN_PACKET_LOSS 200 /* 200% */
  42. #define UNKNOWN_TRIP_TIME -1.0 /* -1 seconds */
  43. #define STRSZ 100
  44. int textscan(char *buf);
  45. int process_arguments(int, char **);
  46. int get_threshold (char *arg, char *rv[2]);
  47. void print_usage(void);
  48. void print_help(void);
  49. char *server_names=NULL;
  50. char *name="INTERNET";
  51. int cthresh=CRITICAL_COUNT;
  52. int wthresh=WARNING_COUNT;
  53. int nnames=0;
  54. int tpl=UNKNOWN_PACKET_LOSS;
  55. double trta=UNKNOWN_TRIP_TIME;
  56. int packet_size=PACKET_SIZE;
  57. int packet_count=PACKET_COUNT;
  58. int verbose=FALSE;
  59. int fail = 0;
  60. int not_found = 0;
  61. int rta_fail = 0;
  62. int pl_fail = 0;
  63. int unreachable = 0;
  64. int main(int argc, char **argv){
  65. int result;
  66. int status=STATE_UNKNOWN;
  67. char *servers=NULL;
  68. char *command_line=NULL;
  69. char *input_buffer=NULL;
  70. char *pl_buffer=NULL;
  71. char *rta_buffer=NULL;
  72. input_buffer=malloc(MAX_INPUT_BUFFER);
  73. rta_buffer = malloc(80);
  74. pl_buffer = malloc(80);
  75. memset(rta_buffer, 0, 80);
  76. memset(pl_buffer, 0, 80);
  77. if(process_arguments(argc,argv)==ERROR)
  78. usage("Could not parse arguments\n");
  79. servers=strscpy(servers,server_names);
  80. /* compose the command */
  81. command_line=ssprintf
  82. (command_line,"%s -b %d -c %d %s",
  83. PATH_TO_FPING,
  84. packet_size,
  85. packet_count,
  86. servers);
  87. if (verbose) printf("%s\n",command_line);
  88. /* run the command */
  89. child_process=spopen(command_line);
  90. if(child_process==NULL){
  91. printf("Unable to open pipe: %s\n",command_line);
  92. return STATE_UNKNOWN;
  93. }
  94. child_stderr=fdopen(child_stderr_array[fileno(child_process)],"r");
  95. if(child_stderr==NULL){
  96. printf("Could not open stderr for %s\n",command_line);
  97. }
  98. while (fgets(input_buffer,MAX_INPUT_BUFFER-1,child_process)) {
  99. if (verbose) printf("%s",input_buffer);
  100. result = textscan(input_buffer);
  101. status = max(status,result);
  102. }
  103. while(fgets(input_buffer,MAX_INPUT_BUFFER-1,child_stderr)) {
  104. if (verbose) printf("%s",input_buffer);
  105. result = textscan(input_buffer);
  106. status = max(status,result);
  107. }
  108. (void)fclose(child_stderr);
  109. /* close the pipe */
  110. if(spclose(child_process))
  111. status=max(status,STATE_WARNING);
  112. /* Analyse fail count and produce results */
  113. if (fail >= wthresh) {
  114. status = max(status, STATE_WARNING);
  115. }
  116. if (fail >= cthresh) {
  117. status = max(status, STATE_CRITICAL);
  118. }
  119. if( tpl != UNKNOWN_PACKET_LOSS ) {
  120. snprintf(pl_buffer, 80, ", %d PL", pl_fail);
  121. }
  122. if( trta != UNKNOWN_TRIP_TIME ) {
  123. snprintf(rta_buffer, 80, ", %d RTA", rta_fail);
  124. }
  125. printf("FPING %s - %s, %d of %d fail, %d NF, %d UR%s%s\n",
  126. state_text(status),
  127. (name != NULL ? name : server_names),
  128. fail,
  129. nnames,
  130. not_found,
  131. unreachable,
  132. pl_buffer,
  133. rta_buffer);
  134. return status;
  135. }
  136. /* analyse fping output - each event resulting in an increment of fail
  137. * must be mutually exclusive. packet loss and round trip time analysed
  138. * together, both at once just results in one increment of fail
  139. */
  140. int textscan(char *buf)
  141. {
  142. char *rtastr=NULL;
  143. char *losstr=NULL;
  144. double loss;
  145. double rta;
  146. int status=STATE_OK;
  147. if (strstr(buf,"not found")) {
  148. fail++;
  149. not_found++;
  150. } else if(strstr(buf,"xmt/rcv/%loss")
  151. && strstr(buf,"min/avg/max")) {
  152. losstr = strstr(buf,"=");
  153. losstr = 1+strstr(losstr,"/");
  154. losstr = 1+strstr(losstr,"/");
  155. rtastr = strstr(buf,"min/avg/max");
  156. rtastr = strstr(rtastr,"=");
  157. rtastr = 1+index(rtastr,'/');
  158. loss = strtod(losstr,NULL);
  159. rta = strtod(rtastr,NULL);
  160. /* Increment fail counter
  161. */
  162. if (tpl!=UNKNOWN_PACKET_LOSS && loss>tpl) {
  163. fail++;
  164. }
  165. else if (trta!=UNKNOWN_TRIP_TIME && rta>trta) {
  166. fail++;
  167. }
  168. else if (loss >= 100) {
  169. fail++;
  170. }
  171. /* Increment other counters
  172. */
  173. if (trta!=UNKNOWN_TRIP_TIME && rta>trta)
  174. rta_fail++;
  175. if (tpl!=UNKNOWN_PACKET_LOSS && loss>tpl)
  176. pl_fail++;
  177. if (loss >= 100)
  178. unreachable++;
  179. } else if(strstr(buf,"xmt/rcv/%loss") ) {
  180. losstr = strstr(buf,"=");
  181. losstr = 1+strstr(losstr,"/");
  182. losstr = 1+strstr(losstr,"/");
  183. loss = strtod(losstr,NULL);
  184. /* Increment fail counter
  185. */
  186. if (tpl!=UNKNOWN_PACKET_LOSS && loss>tpl) {
  187. fail++;
  188. }
  189. else if (loss >= 100) {
  190. fail++;
  191. }
  192. /* Increment other counters
  193. */
  194. if (tpl!=UNKNOWN_PACKET_LOSS && loss>tpl)
  195. pl_fail++;
  196. if (loss >= 100)
  197. unreachable++;
  198. }
  199. return status;
  200. }
  201. /* process command-line arguments */
  202. int process_arguments(int argc, char **argv)
  203. {
  204. int c;
  205. #ifdef HAVE_GETOPT_H
  206. int option_index = 0;
  207. static struct option long_options[] =
  208. {
  209. {"hostname" ,required_argument,0,'H'},
  210. {"critical" ,required_argument,0,'c'},
  211. {"warning" ,required_argument,0,'w'},
  212. {"bytes" ,required_argument,0,'b'},
  213. {"number" ,required_argument,0,'n'},
  214. {"pl-threshold" ,required_argument,0,'p'},
  215. {"rta-threshold" ,required_argument,0,'r'},
  216. {"name" ,required_argument,0,'N'},
  217. {"verbose" ,no_argument, 0,'v'},
  218. {"version" ,no_argument, 0,'V'},
  219. {"help" ,no_argument, 0,'h'},
  220. {0,0,0,0}
  221. };
  222. #else
  223. if(argc<2) return ERROR;
  224. if (!is_option(argv[1])){
  225. server_names=argv[1];
  226. argv[1]=argv[0];
  227. argv=&argv[1];
  228. argc--;
  229. }
  230. #endif
  231. while (1){
  232. #ifdef HAVE_GETOPT_H
  233. c = getopt_long(argc,argv,"+hVvH:c:w:b:n:N:p:r:",long_options,&option_index);
  234. #else
  235. c = getopt(argc,argv,"+hVvH:c:w:b:n:N:p:r:");
  236. #endif
  237. if (c==-1||c==EOF||c==1)
  238. break;
  239. switch (c)
  240. {
  241. case '?': /* print short usage statement if args not parsable */
  242. printf("%s: Unknown argument: %s\n\n",my_basename(argv[0]),optarg);
  243. print_usage();
  244. exit(STATE_UNKNOWN);
  245. case 'h': /* help */
  246. print_help();
  247. exit(STATE_OK);
  248. case 'V': /* version */
  249. print_revision(my_basename(argv[0]),"$Revision$");
  250. exit(STATE_OK);
  251. case 'v': /* verbose mode */
  252. verbose=TRUE;
  253. break;
  254. case 'H': /* hostname */
  255. if(is_host(optarg)==FALSE){
  256. printf("Invalid host name/address\n\n");
  257. print_usage();
  258. exit(STATE_UNKNOWN);
  259. }
  260. if (server_names != NULL)
  261. server_names=strscat(server_names," ");
  262. server_names=strscat(server_names,optarg);
  263. nnames++;
  264. break;
  265. case 'c':
  266. if (is_intpos(optarg))
  267. cthresh = atoi(optarg);
  268. else
  269. usage("Critical threshold must be a positive integer");
  270. break;
  271. case 'w':
  272. if (is_intpos(optarg))
  273. wthresh = atoi(optarg);
  274. else
  275. usage("Warning threshold must be a postive integer");
  276. break;
  277. case 'r':
  278. if (is_intpos(optarg)) {
  279. trta=strtod(optarg,NULL);
  280. }
  281. else {
  282. usage("RTA threshold must be a positive integer");
  283. }
  284. break;
  285. case 'p':
  286. if (is_intpos(optarg)) {
  287. tpl=strtod(optarg,NULL);
  288. }
  289. else {
  290. usage("RTA threshold must be a positive integer");
  291. }
  292. break;
  293. case 'b': /* bytes per packet */
  294. if (is_intpos(optarg))
  295. packet_size=atoi(optarg);
  296. else
  297. usage("Packet size must be a positive integer");
  298. break;
  299. case 'N': /* Name of service */
  300. name = optarg;
  301. break;
  302. case 'n': /* number of packets */
  303. if (is_intpos(optarg))
  304. packet_count=atoi(optarg);
  305. else
  306. usage("Packet count must be a positive integer");
  307. break;
  308. }
  309. }
  310. while (optind < argc) {
  311. if(is_host(argv[optind])==FALSE) {
  312. printf("Invalid host name/address\n\n");
  313. print_usage();
  314. exit(STATE_UNKNOWN);
  315. }
  316. if (server_names != NULL)
  317. server_names=strscat(server_names," ");
  318. server_names=strscat(server_names,argv[optind]);
  319. nnames++;
  320. optind++;
  321. }
  322. if (server_names==NULL || nnames < 2)
  323. usage("At least 2 hostnames must be supplied\n\n");
  324. if (cthresh < 2)
  325. usage("Critical threshold must be at least 2");
  326. if (cthresh > nnames)
  327. usage("Critical threshold cannot be greater than number of hosts tested");
  328. if (wthresh < 1)
  329. usage("Warning threshold must be at least 1");
  330. if (wthresh > nnames)
  331. usage("Warning threshold cannot be greater than number of hosts tested");
  332. if(wthresh >= cthresh)
  333. usage("Warning threshold must be less than the critical threshold");
  334. return OK;
  335. }
  336. void print_usage(void)
  337. {
  338. printf("Usage: %s <host_address> <host_address> [<host_address>] ...\n",PROGNAME);
  339. }
  340. void print_help(void)
  341. {
  342. print_revision(PROGNAME,"$Revision$");
  343. printf
  344. ("Copyright (c) 1999 Didi Rieder (adrieder@sbox.tu-graz.ac.at)\n"
  345. " (c) 2000 Matthew Grant (matthewg@plain.co.nz)\n"
  346. "This plugin will use the /bin/fping command (from saint) to ping the\n"
  347. "specified hosts for a fast check to see if the Internet is still \n"
  348. "reachable, and the results of the testing aggregated. Note that it\n"
  349. "is necessary to set the suid flag on fping.\n\n");
  350. print_usage();
  351. printf
  352. ("\nOptions:\n"
  353. "-b, --bytes=INTEGER\n"
  354. " Size of ICMP packet (default: %d)\n"
  355. "-c, --critical=INTEGER (default: %d)\n"
  356. " critical threshold failure count\n"
  357. "-n, --number=INTEGER\n"
  358. " Number of ICMP packets to send (default: %d)\n"
  359. "-H, --hostname=HOST\n"
  360. " Name or IP Address of host to ping (IP Address bypasses name lookup,\n"
  361. " reducing system load)\n"
  362. "-h, --help\n"
  363. " Print this help screen\n"
  364. "-N, --name\n"
  365. " Service name to print in results, defaults to INTERNET\n"
  366. "-p, --pl-threshold\n"
  367. " Packet loss threshold - specify to turn on packet loss testing\n"
  368. "-r, --rta-threshold\n"
  369. " Round trip average threshold - specify to turn on RTA testing\n"
  370. "-V, --version\n"
  371. " Print version information\n"
  372. "-v, --verbose\n"
  373. " Show details for command-line debugging (do not use with nagios server)\n"
  374. "-w, --warning=INTEGER (default: %d)\n"
  375. " warning threshold failure count\n",
  376. PACKET_SIZE, CRITICAL_COUNT, PACKET_COUNT, WARNING_COUNT);
  377. }