check_icmp.c 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199
  1. /*
  2. * $Id$
  3. *
  4. * Author: Andreas Ericsson <ae@op5.se>
  5. *
  6. * License: GNU GPL 2.0 or any later version.
  7. *
  8. * Relevant RFC's: 792 (ICMP), 791 (IP)
  9. *
  10. * This program was modeled somewhat after the check_icmp program,
  11. * which was in turn a hack of fping (www.fping.org) but has been
  12. * completely rewritten since to generate higher precision rta values,
  13. * and support several different modes as well as setting ttl to control.
  14. * redundant routes. The only remainders of fping is currently a few
  15. * function names.
  16. *
  17. */
  18. #include <sys/time.h>
  19. #include <sys/types.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <stdarg.h>
  23. #include <unistd.h>
  24. #include <stddef.h>
  25. #include <errno.h>
  26. #include <string.h>
  27. #include <ctype.h>
  28. #include <netdb.h>
  29. #include <sys/socket.h>
  30. #include <netinet/in_systm.h>
  31. #include <netinet/in.h>
  32. #include <netinet/ip.h>
  33. #include <netinet/ip_icmp.h>
  34. #include <arpa/inet.h>
  35. #include <signal.h>
  36. /** sometimes undefined system macros (quite a few, actually) **/
  37. #ifndef MAXTTL
  38. # define MAXTTL 255
  39. #endif
  40. #ifndef INADDR_NONE
  41. # define INADDR_NONE 0xffffffU
  42. #endif
  43. #ifndef SOL_IP
  44. #define SOL_IP 0
  45. #endif
  46. /* we bundle these in one #ifndef, since they're all from BSD
  47. * Put individual #ifndef's around those that bother you */
  48. #ifndef ICMP_UNREACH_NET_UNKNOWN
  49. # define ICMP_UNREACH_NET_UNKNOWN 6
  50. # define ICMP_UNREACH_HOST_UNKNOWN 7
  51. # define ICMP_UNREACH_ISOLATED 8
  52. # define ICMP_UNREACH_NET_PROHIB 9
  53. # define ICMP_UNREACH_HOST_PROHIB 10
  54. # define ICMP_UNREACH_TOSNET 11
  55. # define ICMP_UNREACH_TOSHOST 12
  56. #endif
  57. /* tru64 has the ones above, but not these */
  58. #ifndef ICMP_UNREACH_FILTER_PROHIB
  59. # define ICMP_UNREACH_FILTER_PROHIB 13
  60. # define ICMP_UNREACH_HOST_PRECEDENCE 14
  61. # define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
  62. #endif
  63. /** typedefs and such **/
  64. enum states {
  65. STATE_OK = 0,
  66. STATE_WARNING,
  67. STATE_CRITICAL,
  68. STATE_UNKNOWN,
  69. STATE_DEPENDENT,
  70. STATE_OOB
  71. };
  72. typedef unsigned short range_t; /* type for get_range() -- unimplemented */
  73. typedef struct rta_host {
  74. unsigned short id; /* id in **table, and icmp pkts */
  75. char *name; /* arg used for adding this host */
  76. char *msg; /* icmp error message, if any */
  77. struct sockaddr_in saddr_in; /* the address of this host */
  78. struct in_addr error_addr; /* stores address of error replies */
  79. unsigned long long time_waited; /* total time waited, in usecs */
  80. unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */
  81. unsigned char icmp_type, icmp_code; /* type and code from errors */
  82. unsigned short flags; /* control/status flags */
  83. double rta; /* measured RTA */
  84. unsigned char pl; /* measured packet loss */
  85. struct rta_host *next; /* linked list */
  86. } rta_host;
  87. #define FLAG_LOST_CAUSE 0x01 /* decidedly dead target. */
  88. /* threshold structure. all values are maximum allowed, exclusive */
  89. typedef struct threshold {
  90. unsigned char pl; /* max allowed packet loss in percent */
  91. unsigned int rta; /* roundtrip time average, microseconds */
  92. } threshold;
  93. /* the data structure */
  94. typedef struct icmp_ping_data {
  95. struct timeval stime; /* timestamp (saved in protocol struct as well) */
  96. unsigned short ping_id;
  97. } icmp_ping_data;
  98. /* the different modes of this program are as follows:
  99. * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
  100. * MODE_HOSTCHECK: Return immediately upon any sign of life
  101. * In addition, sends packets to ALL addresses assigned
  102. * to this host (as returned by gethostbyname() or
  103. * gethostbyaddr() and expects one host only to be checked at
  104. * a time. Therefore, any packet response what so ever will
  105. * count as a sign of life, even when received outside
  106. * crit.rta limit. Do not misspell any additional IP's.
  107. * MODE_ALL: Requires packets from ALL requested IP to return OK (default).
  108. * MODE_ICMP: implement something similar to check_icmp (MODE_RTA without
  109. * tcp and udp args does this)
  110. */
  111. #define MODE_RTA 0
  112. #define MODE_HOSTCHECK 1
  113. #define MODE_ALL 2
  114. #define MODE_ICMP 3
  115. /* the different ping types we can do
  116. * TODO: investigate ARP ping as well */
  117. #define HAVE_ICMP 1
  118. #define HAVE_UDP 2
  119. #define HAVE_TCP 4
  120. #define HAVE_ARP 8
  121. #define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data)
  122. #define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */
  123. #define IP_HDR_SIZE 20
  124. #define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN)
  125. #define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44)
  126. /* various target states */
  127. #define TSTATE_INACTIVE 0x01 /* don't ping this host anymore */
  128. #define TSTATE_WAITING 0x02 /* unanswered packets on the wire */
  129. #define TSTATE_ALIVE 0x04 /* target is alive (has answered something) */
  130. #define TSTATE_UNREACH 0x08
  131. /** prototypes **/
  132. static void usage(unsigned char, char *);
  133. static u_int get_timevar(const char *);
  134. static u_int get_timevaldiff(struct timeval *, struct timeval *);
  135. static int wait_for_reply(int, u_int);
  136. static int recvfrom_wto(int, char *, unsigned int, struct sockaddr *, u_int *);
  137. static int send_icmp_ping(int, struct rta_host *);
  138. static int get_threshold(char *str, threshold *th);
  139. static void run_checks(void);
  140. static int add_target(char *);
  141. static int add_target_ip(char *, struct in_addr *);
  142. static int handle_random_icmp(struct icmp *, struct sockaddr_in *);
  143. static unsigned short icmp_checksum(unsigned short *, int);
  144. static void finish(int);
  145. static void crash(const char *, ...);
  146. /** external **/
  147. extern int optind, opterr, optopt;
  148. extern char *optarg;
  149. extern char **environ;
  150. /** global variables **/
  151. static char *progname;
  152. static struct rta_host **table, *cursor, *list;
  153. static threshold crit = {80, 500000}, warn = {40, 200000};
  154. static int mode, protocols, sockets, debug = 0, timeout = 10;
  155. static unsigned short icmp_pkt_size, icmp_data_size = DEFAULT_PING_DATA_SIZE;
  156. static unsigned int icmp_sent = 0, icmp_recv = 0, icmp_lost = 0;
  157. #define icmp_pkts_en_route (icmp_sent - (icmp_recv + icmp_lost))
  158. static unsigned short targets_down = 0, targets = 0, packets = 0;
  159. #define targets_alive (targets - targets_down)
  160. static unsigned int retry_interval, pkt_interval, target_interval;
  161. static int icmp_sock, tcp_sock, udp_sock, status = STATE_OK;
  162. static pid_t pid;
  163. static struct timezone tz;
  164. static struct timeval prog_start;
  165. static unsigned long long max_completion_time = 0;
  166. static unsigned char ttl = 0; /* outgoing ttl */
  167. static unsigned int warn_down = 1, crit_down = 1; /* host down threshold values */
  168. float pkt_backoff_factor = 1.5;
  169. float target_backoff_factor = 1.5;
  170. /** code start **/
  171. static void
  172. crash(const char *fmt, ...)
  173. {
  174. va_list ap;
  175. printf("%s: ", progname);
  176. va_start(ap, fmt);
  177. vprintf(fmt, ap);
  178. va_end(ap);
  179. if(errno) printf(": %s", strerror(errno));
  180. puts("");
  181. exit(3);
  182. }
  183. static char *
  184. get_icmp_error_msg(unsigned char icmp_type, unsigned char icmp_code)
  185. {
  186. char *msg = "unreachable";
  187. if(debug > 1) printf("get_icmp_error_msg(%u, %u)\n", icmp_type, icmp_code);
  188. switch(icmp_type) {
  189. case ICMP_UNREACH:
  190. switch(icmp_code) {
  191. case ICMP_UNREACH_NET: msg = "Net unreachable"; break;
  192. case ICMP_UNREACH_HOST: msg = "Host unreachable"; break;
  193. case ICMP_UNREACH_PROTOCOL: msg = "Protocol unreachable (firewall?)"; break;
  194. case ICMP_UNREACH_PORT: msg = "Port unreachable (firewall?)"; break;
  195. case ICMP_UNREACH_NEEDFRAG: msg = "Fragmentation needed"; break;
  196. case ICMP_UNREACH_SRCFAIL: msg = "Source route failed"; break;
  197. case ICMP_UNREACH_ISOLATED: msg = "Source host isolated"; break;
  198. case ICMP_UNREACH_NET_UNKNOWN: msg = "Unknown network"; break;
  199. case ICMP_UNREACH_HOST_UNKNOWN: msg = "Unknown host"; break;
  200. case ICMP_UNREACH_NET_PROHIB: msg = "Network denied (firewall?)"; break;
  201. case ICMP_UNREACH_HOST_PROHIB: msg = "Host denied (firewall?)"; break;
  202. case ICMP_UNREACH_TOSNET: msg = "Bad TOS for network (firewall?)"; break;
  203. case ICMP_UNREACH_TOSHOST: msg = "Bad TOS for host (firewall?)"; break;
  204. case ICMP_UNREACH_FILTER_PROHIB: msg = "Prohibited by filter (firewall)"; break;
  205. case ICMP_UNREACH_HOST_PRECEDENCE: msg = "Host precedence violation"; break;
  206. case ICMP_UNREACH_PRECEDENCE_CUTOFF: msg = "Precedence cutoff"; break;
  207. default: msg = "Invalid code"; break;
  208. }
  209. break;
  210. case ICMP_TIMXCEED:
  211. /* really 'out of reach', or non-existant host behind a router serving
  212. * two different subnets */
  213. switch(icmp_code) {
  214. case ICMP_TIMXCEED_INTRANS: msg = "Time to live exceeded in transit"; break;
  215. case ICMP_TIMXCEED_REASS: msg = "Fragment reassembly time exceeded"; break;
  216. default: msg = "Invalid code"; break;
  217. }
  218. break;
  219. case ICMP_SOURCEQUENCH: msg = "Transmitting too fast"; break;
  220. case ICMP_REDIRECT: msg = "Redirect (change route)"; break;
  221. case ICMP_PARAMPROB: msg = "Bad IP header (required option absent)"; break;
  222. /* the following aren't error messages, so ignore */
  223. case ICMP_TSTAMP:
  224. case ICMP_TSTAMPREPLY:
  225. case ICMP_IREQ:
  226. case ICMP_IREQREPLY:
  227. case ICMP_MASKREQ:
  228. case ICMP_MASKREPLY:
  229. default: msg = ""; break;
  230. }
  231. return msg;
  232. }
  233. static int
  234. handle_random_icmp(struct icmp *p, struct sockaddr_in *addr)
  235. {
  236. struct icmp *sent_icmp = NULL;
  237. struct rta_host *host = NULL;
  238. unsigned char *ptr;
  239. if(p->icmp_type == ICMP_ECHO && p->icmp_id == pid) {
  240. /* echo request from us to us (pinging localhost) */
  241. return 0;
  242. }
  243. ptr = (unsigned char *)p;
  244. if(debug) printf("handle_random_icmp(%p, %p)\n", (void *)p, (void *)addr);
  245. /* only handle a few types, since others can't possibly be replies to
  246. * us in a sane network (if it is anyway, it will be counted as lost
  247. * at summary time, but not as quickly as a proper response */
  248. /* TIMXCEED can be an unreach from a router with multiple IP's which
  249. * serves two different subnets on the same interface and a dead host
  250. * on one net is pinged from the other. The router will respond to
  251. * itself and thus set TTL=0 so as to not loop forever. Even when
  252. * TIMXCEED actually sends a proper icmp response we will have passed
  253. * too many hops to have a hope of reaching it later, in which case it
  254. * indicates overconfidence in the network, poor routing or both. */
  255. if(p->icmp_type != ICMP_UNREACH && p->icmp_type != ICMP_TIMXCEED &&
  256. p->icmp_type != ICMP_SOURCEQUENCH && p->icmp_type != ICMP_PARAMPROB)
  257. {
  258. return 0;
  259. }
  260. /* might be for us. At least it holds the original package (according
  261. * to RFC 792). If it isn't, just ignore it */
  262. sent_icmp = (struct icmp *)(ptr + 28);
  263. if(sent_icmp->icmp_type != ICMP_ECHO || sent_icmp->icmp_id != pid ||
  264. sent_icmp->icmp_seq >= targets)
  265. {
  266. if(debug) printf("Packet is no response to a packet we sent\n");
  267. return 0;
  268. }
  269. /* it is indeed a response for us */
  270. host = table[sent_icmp->icmp_seq];
  271. if(debug) {
  272. printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n",
  273. get_icmp_error_msg(p->icmp_type, p->icmp_code),
  274. inet_ntoa(addr->sin_addr), host->name);
  275. }
  276. icmp_lost++;
  277. host->icmp_lost++;
  278. /* don't spend time on lost hosts any more */
  279. if(host->flags & FLAG_LOST_CAUSE) return 0;
  280. /* source quench means we're sending too fast, so increase the
  281. * interval and mark this packet lost */
  282. if(p->icmp_type == ICMP_SOURCEQUENCH) {
  283. pkt_interval *= pkt_backoff_factor;
  284. target_interval *= target_backoff_factor;
  285. }
  286. else {
  287. targets_down++;
  288. host->flags |= FLAG_LOST_CAUSE;
  289. }
  290. host->icmp_type = p->icmp_type;
  291. host->icmp_code = p->icmp_code;
  292. host->error_addr.s_addr = addr->sin_addr.s_addr;
  293. return 0;
  294. }
  295. int
  296. main(int argc, char **argv)
  297. {
  298. int i;
  299. char *ptr;
  300. long int arg;
  301. int icmp_sockerrno, udp_sockerrno, tcp_sockerrno;
  302. int result;
  303. struct rta_host *host;
  304. /* we only need to be setsuid when we get the sockets, so do
  305. * that before pointer magic (esp. on network data) */
  306. icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0;
  307. if((icmp_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) != -1)
  308. sockets |= HAVE_ICMP;
  309. else icmp_sockerrno = errno;
  310. /* if((udp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1) */
  311. /* sockets |= HAVE_UDP; */
  312. /* else udp_sockerrno = errno; */
  313. /* if((tcp_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) */
  314. /* sockets |= HAVE_TCP; */
  315. /* else tcp_sockerrno = errno; */
  316. /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
  317. setuid(getuid());
  318. /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
  319. environ = NULL;
  320. /* use the pid to mark packets as ours */
  321. pid = getpid();
  322. /* printf("pid = %u\n", pid); */
  323. /* get calling name the old-fashioned way for portability instead
  324. * of relying on the glibc-ism __progname */
  325. ptr = strrchr(argv[0], '/');
  326. if(ptr) progname = &ptr[1];
  327. else progname = argv[0];
  328. /* now set defaults. Use progname to set them initially (allows for
  329. * superfast check_host program when target host is up */
  330. cursor = list = NULL;
  331. table = NULL;
  332. mode = MODE_RTA;
  333. crit.rta = 500000;
  334. crit.pl = 80;
  335. warn.rta = 200000;
  336. warn.pl = 40;
  337. protocols = HAVE_ICMP | HAVE_UDP | HAVE_TCP;
  338. pkt_interval = 80000; /* 80 msec packet interval by default */
  339. packets = 5;
  340. if(!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) {
  341. mode = MODE_ICMP;
  342. protocols = HAVE_ICMP;
  343. }
  344. else if(!strcmp(progname, "check_host")) {
  345. mode = MODE_HOSTCHECK;
  346. pkt_interval = 1000000;
  347. packets = 5;
  348. crit.rta = warn.rta = 1000000;
  349. crit.pl = warn.pl = 100;
  350. }
  351. else if(!strcmp(progname, "check_rta_multi")) {
  352. mode = MODE_ALL;
  353. target_interval = 0;
  354. pkt_interval = 50000;
  355. packets = 5;
  356. }
  357. /* parse the arguments */
  358. for(i = 1; i < argc; i++) {
  359. while((arg = getopt(argc, argv, "vhVw:c:n:p:t:H:i:b:I:l:")) != EOF) {
  360. switch(arg) {
  361. case 'v':
  362. debug++;
  363. break;
  364. case 'b':
  365. /* silently ignored for now */
  366. break;
  367. case 'i':
  368. pkt_interval = get_timevar(optarg);
  369. break;
  370. case 'I':
  371. target_interval = get_timevar(optarg);
  372. break;
  373. case 'w':
  374. get_threshold(optarg, &warn);
  375. break;
  376. case 'c':
  377. get_threshold(optarg, &crit);
  378. break;
  379. case 'n':
  380. case 'p':
  381. packets = strtoul(optarg, NULL, 0);
  382. break;
  383. case 't':
  384. timeout = strtoul(optarg, NULL, 0);
  385. if(!timeout) timeout = 10;
  386. break;
  387. case 'H':
  388. add_target(optarg);
  389. break;
  390. case 'l':
  391. ttl = (unsigned char)strtoul(optarg, NULL, 0);
  392. break;
  393. case 'd': /* implement later, for cluster checks */
  394. warn_down = (unsigned char)strtoul(optarg, &ptr, 0);
  395. if(ptr) {
  396. crit_down = (unsigned char)strtoul(ptr + 1, NULL, 0);
  397. }
  398. break;
  399. case 'h': case 'V': default:
  400. usage(arg, NULL);
  401. break;
  402. }
  403. }
  404. }
  405. argv = &argv[optind];
  406. while(*argv) {
  407. add_target(*argv);
  408. argv++;
  409. }
  410. if(!targets) {
  411. errno = 0;
  412. crash("No hosts to check");
  413. exit(3);
  414. }
  415. if(!sockets) {
  416. if(icmp_sock == -1) {
  417. errno = icmp_sockerrno;
  418. crash("Failed to obtain ICMP socket");
  419. return -1;
  420. }
  421. /* if(udp_sock == -1) { */
  422. /* errno = icmp_sockerrno; */
  423. /* crash("Failed to obtain UDP socket"); */
  424. /* return -1; */
  425. /* } */
  426. /* if(tcp_sock == -1) { */
  427. /* errno = icmp_sockerrno; */
  428. /* crash("Failed to obtain TCP socker"); */
  429. /* return -1; */
  430. /* } */
  431. }
  432. if(!ttl) ttl = 64;
  433. if(icmp_sock) {
  434. result = setsockopt(icmp_sock, SOL_IP, IP_TTL, &ttl, sizeof(ttl));
  435. if(debug) {
  436. if(result == -1) printf("setsockopt failed\n");
  437. else printf("ttl set to %u\n", ttl);
  438. }
  439. }
  440. /* stupid users should be able to give whatever thresholds they want
  441. * (nothing will break if they do), but some anal plugin maintainer
  442. * will probably add some printf() thing here later, so it might be
  443. * best to at least show them where to do it. ;) */
  444. if(warn.pl > crit.pl) warn.pl = crit.pl;
  445. if(warn.rta > crit.rta) warn.rta = crit.rta;
  446. if(warn_down > crit_down) crit_down = warn_down;
  447. signal(SIGINT, finish);
  448. signal(SIGHUP, finish);
  449. signal(SIGTERM, finish);
  450. signal(SIGALRM, finish);
  451. if(debug) printf("Setting alarm timeout to %u seconds\n", timeout);
  452. alarm(timeout);
  453. /* make sure we don't wait any longer than necessary */
  454. gettimeofday(&prog_start, &tz);
  455. max_completion_time =
  456. ((targets * packets * pkt_interval) + (targets * target_interval)) +
  457. (targets * packets * crit.rta) + crit.rta;
  458. if(debug) {
  459. printf("packets: %u, targets: %u\n"
  460. "target_interval: %0.3f, pkt_interval %0.3f\n"
  461. "crit.rta: %0.3f\n"
  462. "max_completion_time: %0.3f\n",
  463. packets, targets,
  464. (float)target_interval / 1000, (float)pkt_interval / 1000,
  465. (float)crit.rta / 1000,
  466. (float)max_completion_time / 1000);
  467. }
  468. if(debug) {
  469. if(max_completion_time > (u_int)timeout * 1000000) {
  470. printf("max_completion_time: %llu timeout: %u\n",
  471. max_completion_time, timeout);
  472. printf("Timout must be at lest %llu\n",
  473. max_completion_time / 1000000 + 1);
  474. }
  475. }
  476. icmp_pkt_size = icmp_data_size + ICMP_MINLEN;
  477. if(debug > 2) printf("icmp_pkt_size = %u\n", icmp_pkt_size);
  478. if(icmp_pkt_size < sizeof(struct icmp) + sizeof(struct icmp_ping_data)) {
  479. icmp_pkt_size = sizeof(struct icmp) + sizeof(struct icmp_ping_data);
  480. }
  481. if(debug > 2) printf("icmp_pkt_size = %u\n", icmp_pkt_size);
  482. if(debug) {
  483. printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n",
  484. crit.rta, crit.pl, warn.rta, warn.pl);
  485. printf("pkt_interval: %u target_interval: %u retry_interval: %u\n",
  486. pkt_interval, target_interval, retry_interval);
  487. printf("icmp_pkt_size: %u timeout: %u\n",
  488. icmp_pkt_size, timeout);
  489. }
  490. if(packets > 20) {
  491. errno = 0;
  492. crash("packets is > 20 (%d)", packets);
  493. }
  494. host = list;
  495. table = malloc(sizeof(struct rta_host **) * (argc - 1));
  496. i = 0;
  497. while(host) {
  498. host->id = i;
  499. table[i] = host;
  500. host = host->next;
  501. i++;
  502. }
  503. run_checks();
  504. errno = 0;
  505. finish(0);
  506. return(0);
  507. }
  508. static void
  509. run_checks()
  510. {
  511. u_int i, t, result;
  512. u_int final_wait, time_passed;
  513. /* this loop might actually violate the pkt_interval or target_interval
  514. * settings, but only if there aren't any packets on the wire which
  515. * indicates that the target can handle an increased packet rate */
  516. for(i = 0; i < packets; i++) {
  517. for(t = 0; t < targets; t++) {
  518. /* don't send useless packets */
  519. if(!targets_alive) finish(0);
  520. if(table[t]->flags & FLAG_LOST_CAUSE) {
  521. if(debug) printf("%s is a lost cause. not sending any more\n",
  522. table[t]->name);
  523. continue;
  524. }
  525. /* we're still in the game, so send next packet */
  526. (void)send_icmp_ping(icmp_sock, table[t]);
  527. result = wait_for_reply(icmp_sock, target_interval);
  528. }
  529. result = wait_for_reply(icmp_sock, pkt_interval * targets);
  530. }
  531. if(icmp_pkts_en_route && targets_alive) {
  532. time_passed = get_timevaldiff(NULL, NULL);
  533. final_wait = max_completion_time - time_passed;
  534. if(debug) {
  535. printf("time_passed: %u final_wait: %u max_completion_time: %llu\n",
  536. time_passed, final_wait, max_completion_time);
  537. }
  538. if(time_passed > max_completion_time) {
  539. if(debug) printf("Time passed. Finishing up\n");
  540. finish(0);
  541. }
  542. /* catch the packets that might come in within the timeframe, but
  543. * haven't yet */
  544. if(debug) printf("Waiting for %u micro-seconds (%0.3f msecs)\n",
  545. final_wait, (float)final_wait / 1000);
  546. result = wait_for_reply(icmp_sock, final_wait);
  547. }
  548. }
  549. /* response structure:
  550. * ip header : 20 bytes
  551. * icmp header : 28 bytes
  552. * icmp echo reply : the rest
  553. */
  554. static int
  555. wait_for_reply(int sock, u_int t)
  556. {
  557. int n, hlen;
  558. static char buf[4096];
  559. struct sockaddr_in resp_addr;
  560. struct ip *ip;
  561. struct icmp *icp, *sent_icmp;
  562. struct rta_host *host;
  563. struct icmp_ping_data *data;
  564. struct timeval wait_start, now;
  565. u_int tdiff, i, per_pkt_wait;
  566. /* if we can't listen or don't have anything to listen to, just return */
  567. if(!t || !icmp_pkts_en_route) return 0;
  568. gettimeofday(&wait_start, &tz);
  569. i = t;
  570. per_pkt_wait = t / icmp_pkts_en_route;
  571. while(icmp_pkts_en_route && get_timevaldiff(&wait_start, NULL) < i) {
  572. t = per_pkt_wait;
  573. /* wrap up if all targets are declared dead */
  574. if(!targets_alive ||
  575. get_timevaldiff(&prog_start, NULL) >= max_completion_time ||
  576. (mode == MODE_HOSTCHECK && targets_down))
  577. {
  578. finish(0);
  579. }
  580. /* reap responses until we hit a timeout */
  581. n = recvfrom_wto(sock, buf, sizeof(buf),
  582. (struct sockaddr *)&resp_addr, &t);
  583. if(!n) {
  584. if(debug > 1) {
  585. printf("recvfrom_wto() timed out during a %u usecs wait\n",
  586. per_pkt_wait);
  587. }
  588. continue; /* timeout for this one, so keep trying */
  589. }
  590. if(n < 0) {
  591. if(debug) printf("recvfrom_wto() returned errors\n");
  592. return n;
  593. }
  594. ip = (struct ip *)buf;
  595. if(debug > 1) printf("received %u bytes from %s\n",
  596. ntohs(ip->ip_len), inet_ntoa(resp_addr.sin_addr));
  597. /* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */
  598. /* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */
  599. /* alpha headers are decidedly broken. Using an ansi compiler,
  600. * they provide ip_vhl instead of ip_hl and ip_v, so we mask
  601. * off the bottom 4 bits */
  602. /* hlen = (ip->ip_vhl & 0x0f) << 2; */
  603. /* #else */
  604. hlen = ip->ip_hl << 2;
  605. /* #endif */
  606. if(n < (hlen + ICMP_MINLEN)) {
  607. crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n",
  608. n, hlen + icmp_pkt_size, inet_ntoa(resp_addr.sin_addr));
  609. }
  610. /* else if(debug) { */
  611. /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */
  612. /* hlen, ntohs(ip->ip_len) - hlen, */
  613. /* sizeof(struct ip), icmp_pkt_size); */
  614. /* } */
  615. /* check the response */
  616. icp = (struct icmp *)(buf + hlen);
  617. sent_icmp = (struct icmp *)(buf + hlen + ICMP_MINLEN);
  618. /* printf("buf: %p, icp: %p, distance: %u (expected %u)\n", */
  619. /* buf, icp, */
  620. /* (u_int)icp - (u_int)buf, hlen); */
  621. /* printf("buf: %p, sent_icmp: %p, distance: %u (expected %u)\n", */
  622. /* buf, sent_icmp, */
  623. /* (u_int)sent_icmp - (u_int)buf, hlen + ICMP_MINLEN); */
  624. if(icp->icmp_id != pid) {
  625. handle_random_icmp(icp, &resp_addr);
  626. continue;
  627. }
  628. if(icp->icmp_type != ICMP_ECHOREPLY || icp->icmp_seq >= targets) {
  629. if(debug > 2) printf("not a proper ICMP_ECHOREPLY\n");
  630. handle_random_icmp(icp, &resp_addr);
  631. continue;
  632. }
  633. /* this is indeed a valid response */
  634. data = (struct icmp_ping_data *)(icp->icmp_data);
  635. host = table[icp->icmp_seq];
  636. gettimeofday(&now, &tz);
  637. tdiff = get_timevaldiff(&data->stime, &now);
  638. host->time_waited += tdiff;
  639. host->icmp_recv++;
  640. icmp_recv++;
  641. if(debug) {
  642. printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u\n",
  643. (float)tdiff / 1000, inet_ntoa(resp_addr.sin_addr),
  644. ttl, ip->ip_ttl);
  645. }
  646. /* if we're in hostcheck mode, exit with limited printouts */
  647. if(mode == MODE_HOSTCHECK) {
  648. printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|"
  649. "pkt=%u;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n",
  650. host->name, icmp_recv, (float)tdiff / 1000,
  651. icmp_recv, packets, (float)tdiff / 1000,
  652. (float)warn.rta / 1000, (float)crit.rta / 1000);
  653. exit(STATE_OK);
  654. }
  655. }
  656. return 0;
  657. }
  658. /* the ping functions */
  659. static int
  660. send_icmp_ping(int sock, struct rta_host *host)
  661. {
  662. static char *buf = NULL; /* re-use so we prevent leaks */
  663. long int len;
  664. struct icmp *icp;
  665. struct icmp_ping_data *data;
  666. struct timeval tv;
  667. struct sockaddr *addr;
  668. if(sock == -1) {
  669. errno = 0;
  670. crash("Attempt to send on bogus socket");
  671. return -1;
  672. }
  673. addr = (struct sockaddr *)&host->saddr_in;
  674. if(!buf) {
  675. buf = (char *)malloc(icmp_pkt_size + sizeof(struct ip));
  676. if(!buf) {
  677. crash("send_icmp_ping(): failed to malloc %d bytes for send buffer",
  678. icmp_pkt_size);
  679. return -1; /* might be reached if we're in debug mode */
  680. }
  681. }
  682. memset(buf, 0, icmp_pkt_size + sizeof(struct ip));
  683. if((gettimeofday(&tv, &tz)) == -1) return -1;
  684. icp = (struct icmp *)buf;
  685. icp->icmp_type = ICMP_ECHO;
  686. icp->icmp_code = 0;
  687. icp->icmp_cksum = 0;
  688. icp->icmp_id = pid;
  689. icp->icmp_seq = host->id;
  690. data = (struct icmp_ping_data *)icp->icmp_data;
  691. data->ping_id = 10; /* host->icmp.icmp_sent; */
  692. memcpy(&data->stime, &tv, sizeof(struct timeval));
  693. icp->icmp_cksum = icmp_checksum((u_short *)icp, icmp_pkt_size);
  694. len = sendto(sock, buf, icmp_pkt_size, 0, (struct sockaddr *)addr,
  695. sizeof(struct sockaddr));
  696. if(len < 0 || (unsigned int)len != icmp_pkt_size) {
  697. if(debug) printf("Failed to send ping to %s\n",
  698. inet_ntoa(host->saddr_in.sin_addr));
  699. return -1;
  700. }
  701. icmp_sent++;
  702. host->icmp_sent++;
  703. return 0;
  704. }
  705. static int
  706. recvfrom_wto(int sock, char *buf, unsigned int len, struct sockaddr *saddr,
  707. u_int *timo)
  708. {
  709. u_int slen;
  710. int n;
  711. struct timeval to, then, now;
  712. fd_set rd, wr;
  713. if(!*timo) {
  714. if(debug) printf("*timo is not\n");
  715. return 0;
  716. }
  717. to.tv_sec = *timo / 1000000;
  718. to.tv_usec = (*timo - (to.tv_sec * 1000000));
  719. FD_ZERO(&rd);
  720. FD_ZERO(&wr);
  721. FD_SET(sock, &rd);
  722. errno = 0;
  723. gettimeofday(&then, &tz);
  724. n = select(sock + 1, &rd, &wr, NULL, &to);
  725. if(n < 0) crash("select() in recvfrom_wto");
  726. gettimeofday(&now, &tz);
  727. *timo = get_timevaldiff(&then, &now);
  728. if(!n) return 0; /* timeout */
  729. slen = sizeof(struct sockaddr);
  730. return recvfrom(sock, buf, len, 0, saddr, &slen);
  731. }
  732. static void
  733. finish(int sig)
  734. {
  735. u_int i = 0;
  736. unsigned char pl;
  737. double rta;
  738. struct rta_host *host;
  739. char *status_string[] =
  740. {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"};
  741. alarm(0);
  742. if(debug > 1) printf("finish(%d) called\n", sig);
  743. if(icmp_sock != -1) close(icmp_sock);
  744. if(udp_sock != -1) close(udp_sock);
  745. if(tcp_sock != -1) close(tcp_sock);
  746. if(debug) {
  747. printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n",
  748. icmp_sent, icmp_recv, icmp_lost);
  749. printf("targets: %u targets_alive: %u\n", targets, targets_alive);
  750. }
  751. /* iterate thrice to calculate values, give output, and print perfparse */
  752. host = list;
  753. while(host) {
  754. if(!host->icmp_recv) {
  755. /* rta 0 is ofcourse not entirely correct, but will still show up
  756. * conspicuosly as missing entries in perfparse and cacti */
  757. pl = 100;
  758. rta = 0;
  759. status = STATE_CRITICAL;
  760. /* up the down counter if not already counted */
  761. if(!(host->flags & FLAG_LOST_CAUSE) && targets_alive) targets_down++;
  762. }
  763. else {
  764. pl = ((host->icmp_sent - host->icmp_recv) * 100) / host->icmp_sent;
  765. rta = (double)host->time_waited / host->icmp_recv;
  766. }
  767. host->pl = pl;
  768. host->rta = rta;
  769. if(!status && (pl >= warn.pl || rta >= warn.rta)) status = STATE_WARNING;
  770. if(pl >= crit.pl || rta >= crit.rta) status = STATE_CRITICAL;
  771. host = host->next;
  772. }
  773. /* this is inevitable */
  774. if(!targets_alive) status = STATE_CRITICAL;
  775. printf("%s - ", status_string[status]);
  776. host = list;
  777. while(host) {
  778. if(debug) puts("");
  779. if(i) {
  780. if(i < targets) printf(" :: ");
  781. else printf("\n");
  782. }
  783. i++;
  784. if(!host->icmp_recv) {
  785. status = STATE_CRITICAL;
  786. if(host->flags & FLAG_LOST_CAUSE) {
  787. printf("%s: %s @ %s. rta nan, lost %d%%",
  788. host->name,
  789. get_icmp_error_msg(host->icmp_type, host->icmp_code),
  790. inet_ntoa(host->error_addr),
  791. 100);
  792. }
  793. else { /* not marked as lost cause, so we have no flags for it */
  794. printf("%s: rta nan, lost 100%%", host->name);
  795. }
  796. }
  797. else { /* !icmp_recv */
  798. printf("%s: rta %0.3fms, lost %u%%",
  799. host->name, host->rta / 1000, host->pl);
  800. }
  801. host = host->next;
  802. }
  803. /* iterate once more for pretty perfparse output */
  804. printf("|");
  805. i = 0;
  806. host = list;
  807. while(host) {
  808. if(debug) puts("");
  809. printf("%srta=%0.3fms;%0.3f;%0.3f;0; %spl=%u%%;%u;%u;; ",
  810. (targets > 1) ? host->name : "",
  811. host->rta / 1000, (float)warn.rta / 1000, (float)crit.rta / 1000,
  812. (targets > 1) ? host->name : "",
  813. host->pl, warn.pl, crit.pl);
  814. host = host->next;
  815. }
  816. /* finish with an empty line */
  817. puts("");
  818. if(debug) printf("targets: %u, targets_alive: %u\n",
  819. targets, targets_alive);
  820. exit(status);
  821. }
  822. static u_int
  823. get_timevaldiff(struct timeval *early, struct timeval *later)
  824. {
  825. u_int ret;
  826. struct timeval now;
  827. if(!later) {
  828. gettimeofday(&now, &tz);
  829. later = &now;
  830. }
  831. if(!early) early = &prog_start;
  832. /* if early > later we return 0 so as to indicate a timeout */
  833. if(early->tv_sec > early->tv_sec ||
  834. (early->tv_sec == later->tv_sec && early->tv_usec > later->tv_usec))
  835. {
  836. return 0;
  837. }
  838. ret = (later->tv_sec - early->tv_sec) * 1000000;
  839. ret += later->tv_usec - early->tv_usec;
  840. return ret;
  841. }
  842. static int
  843. add_target_ip(char *arg, struct in_addr *in)
  844. {
  845. struct rta_host *host;
  846. /* disregard obviously stupid addresses */
  847. if(in->s_addr == INADDR_NONE || in->s_addr == INADDR_ANY)
  848. return -1;
  849. /* no point in adding two identical IP's, so don't. ;) */
  850. host = list;
  851. while(host) {
  852. if(host->saddr_in.sin_addr.s_addr == in->s_addr) {
  853. if(debug) printf("Identical IP already exists. Not adding %s\n", arg);
  854. return -1;
  855. }
  856. host = host->next;
  857. }
  858. /* add the fresh ip */
  859. host = malloc(sizeof(struct rta_host));
  860. if(!host) {
  861. crash("add_target_ip(%s, %s): malloc(%d) failed",
  862. arg, inet_ntoa(*in), sizeof(struct rta_host));
  863. }
  864. memset(host, 0, sizeof(struct rta_host));
  865. /* set the values. use calling name for output */
  866. host->name = strdup(arg);
  867. /* fill out the sockaddr_in struct */
  868. host->saddr_in.sin_family = AF_INET;
  869. host->saddr_in.sin_addr.s_addr = in->s_addr;
  870. if(!list) list = cursor = host;
  871. else cursor->next = host;
  872. cursor = host;
  873. targets++;
  874. return 0;
  875. }
  876. /* wrapper for add_target_ip */
  877. static int
  878. add_target(char *arg)
  879. {
  880. int i;
  881. struct hostent *he;
  882. struct in_addr *in, ip;
  883. /* don't resolve if we don't have to */
  884. if((ip.s_addr = inet_addr(arg)) != INADDR_NONE) {
  885. /* don't add all ip's if we were given a specific one */
  886. return add_target_ip(arg, &ip);
  887. /* he = gethostbyaddr((char *)in, sizeof(struct in_addr), AF_INET); */
  888. /* if(!he) return add_target_ip(arg, in); */
  889. }
  890. else {
  891. errno = 0;
  892. he = gethostbyname(arg);
  893. if(!he) {
  894. errno = 0;
  895. crash("Failed to resolve %s", arg);
  896. return -1;
  897. }
  898. }
  899. /* possibly add all the IP's as targets */
  900. for(i = 0; he->h_addr_list[i]; i++) {
  901. in = (struct in_addr *)he->h_addr_list[i];
  902. add_target_ip(arg, in);
  903. /* this is silly, but it works */
  904. if(mode == MODE_HOSTCHECK || mode == MODE_ALL) {
  905. printf("mode: %d\n", mode);
  906. continue;
  907. }
  908. break;
  909. }
  910. return 0;
  911. }
  912. /*
  913. * u = micro
  914. * m = milli
  915. * s = seconds
  916. * return value is in microseconds
  917. */
  918. static u_int
  919. get_timevar(const char *str)
  920. {
  921. char p, u, *ptr;
  922. unsigned int len;
  923. u_int i, d; /* integer and decimal, respectively */
  924. u_int factor = 1000; /* default to milliseconds */
  925. if(!str) return 0;
  926. len = strlen(str);
  927. if(!len) return 0;
  928. /* unit might be given as ms|m (millisec),
  929. * us|u (microsec) or just plain s, for seconds */
  930. u = p = '\0';
  931. u = str[len - 1];
  932. if(len >= 2 && !isdigit((int)str[len - 2])) p = str[len - 2];
  933. if(p && u == 's') u = p;
  934. else if(!p) p = u;
  935. if(debug > 2) printf("evaluating %s, u: %c, p: %c\n", str, u, p);
  936. if(u == 'u') factor = 1; /* microseconds */
  937. else if(u == 'm') factor = 1000; /* milliseconds */
  938. else if(u == 's') factor = 1000000; /* seconds */
  939. if(debug > 2) printf("factor is %u\n", factor);
  940. i = strtoul(str, &ptr, 0);
  941. if(!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1)
  942. return i * factor;
  943. /* time specified in usecs can't have decimal points, so ignore them */
  944. if(factor == 1) return i;
  945. d = strtoul(ptr + 1, NULL, 0);
  946. /* d is decimal, so get rid of excess digits */
  947. while(d >= factor) d /= 10;
  948. /* the last parenthesis avoids floating point exceptions. */
  949. return ((i * factor) + (d * (factor / 10)));
  950. }
  951. /* not too good at checking errors, but it'll do (main() should barfe on -1) */
  952. static int
  953. get_threshold(char *str, threshold *th)
  954. {
  955. char *p = NULL, i = 0;
  956. if(!str || !strlen(str) || !th) return -1;
  957. /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */
  958. p = &str[strlen(str) - 1];
  959. while(p != &str[1]) {
  960. if(*p == '%') *p = '\0';
  961. else if(*p == ',' && i) {
  962. *p = '\0'; /* reset it so get_timevar(str) works nicely later */
  963. th->pl = (unsigned char)strtoul(p+1, NULL, 0);
  964. break;
  965. }
  966. i = 1;
  967. p--;
  968. }
  969. th->rta = get_timevar(str);
  970. if(!th->rta) return -1;
  971. if(th->rta > MAXTTL * 1000000) th->rta = MAXTTL * 1000000;
  972. if(th->pl > 100) th->pl = 100;
  973. return 0;
  974. }
  975. unsigned short
  976. icmp_checksum(unsigned short *p, int n)
  977. {
  978. register unsigned short cksum;
  979. register long sum = 0;
  980. while(n > 1) {
  981. sum += *p++;
  982. n -= 2;
  983. }
  984. /* mop up the occasional odd byte */
  985. if(n == 1) sum += (unsigned char)*p;
  986. sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
  987. sum += (sum >> 16); /* add carry */
  988. cksum = ~sum; /* ones-complement, trunc to 16 bits */
  989. return cksum;
  990. }
  991. /* make core plugin developers happy (silly, really) */
  992. static void
  993. usage(unsigned char arg, char *msg)
  994. {
  995. if(msg) printf("%s: %s\n", progname, msg);
  996. if(arg == 'V') {
  997. printf("$Id$\n");
  998. exit(STATE_UNKNOWN);
  999. }
  1000. printf("Usage: %s [options] [-H] host1 host2 hostn\n\n", progname);
  1001. if(arg != 'h') exit(3);
  1002. printf("Where options are any combination of:\n"
  1003. " * -H | --host specify a target\n"
  1004. " * -w | --warn warning threshold (currently %0.3fms,%u%%)\n"
  1005. " * -c | --crit critical threshold (currently %0.3fms,%u%%)\n"
  1006. " * -n | --packets number of packets to send (currently %u)\n"
  1007. " * -i | --interval max packet interval (currently %0.3fms)\n"
  1008. " * -I | --hostint max target interval (currently %0.3fms)\n"
  1009. " * -l | --ttl TTL on outgoing packets (currently %u)\n"
  1010. " * -t | --timeout timeout value (seconds, currently %u)\n"
  1011. " * -b | --bytes icmp packet size (currenly ignored)\n"
  1012. " -v | --verbose verbosity++\n"
  1013. " -h | --help this cruft\n",
  1014. (float)warn.rta / 1000, warn.pl, (float)crit.rta / 1000, crit.pl,
  1015. packets,
  1016. (float)pkt_interval / 1000, (float)target_interval / 1000,
  1017. ttl, timeout);
  1018. puts("\nThe -H switch is optional. Naming a host (or several) to check is not.\n\n"
  1019. "Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%\n"
  1020. "packet loss. The default values should work well for most users.\n"
  1021. "You can specify different RTA factors using the standardized abbreviations\n"
  1022. "us (microseconds), ms (milliseconds, default) or just plain s for seconds.\n\n"
  1023. "Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 hops\n"
  1024. "are spent and CRITICAL if >= 14 hops are spent.\n"
  1025. "NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do not.\n\n"
  1026. "The -v switch can be specified several times for increased verbosity.\n\n"
  1027. "Long options are currently unsupported.\n\n"
  1028. "Options marked with * require an argument\n");
  1029. puts("The latest version of this plugin can be found at http://oss.op5.se/nagios\n"
  1030. "or https://devel.op5.se/oss until the day it is included in the official\n"
  1031. "plugin distribution.\n");
  1032. exit(3);
  1033. }