Annotation of embedaddon/ntp/scripts/monitoring/ntptrap, revision 1.1.1.1
1.1 misho 1: #!/local/bin/perl --*-perl-*-
2: ;#
3: ;# ntptrap,v 3.1 1993/07/06 01:09:15 jbj Exp
4: ;#
5: ;# a client for the xntp mode 6 trap mechanism
6: ;#
7: ;# Copyright (c) 1992
8: ;# Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg
9: ;#
10: ;#
11: ;#############################################################
12: $0 =~ s!^.*/([^/]+)$!$1!; # strip to filename
13: ;# enforce STDOUT and STDERR to be line buffered
14: $| = 1;
15: select((select(STDERR),$|=1)[$[]);
16:
17: ;#######################################
18: ;# load utility routines and definitions
19: ;#
20: require('ntp.pl'); # implementation of the NTP protocol
21: use Socket;
22:
23: #eval { require('sys/socket.ph'); require('netinet/in.ph') unless defined(&INADDR_ANY); } ||
24: #do {
25: #die("$0: $@") unless $[ == index($@, "Can't locate ");
26: #warn "$0: $@";
27: #warn "$0: supplying some default definitions\n";
28: #eval 'sub INADDR_ANY { 0; } sub AF_INET {2;} sub SOCK_DGRAM {2;} 1;' || die "$0: $@";
29: #};
30: require('getopts.pl'); # option parsing
31: require('ctime.pl'); # date/time formatting
32:
33: ;######################################
34: ;# define some global constants
35: ;#
36: $BASE_TIMEOUT=10;
37: $FRAG_TIMEOUT=10;
38: $MAX_TRY = 5;
39: $REFRESH_TIME=60*15; # 15 minutes (server uses 1 hour)
40: $ntp'timeout = $FRAG_TIMEOUT; #';
41: $ntp'timeout if 0;
42:
43: ;######################################
44: ;# now process options
45: ;#
46: sub usage
47: {
48: die("usage: $0 [-n] [-p <port>] [-l <logfile>] [host] ...\n");
49: }
50:
51: $opt_l = "/dev/null"; # where to write debug messages to
52: $opt_p = 0; # port to use locally - (0 does mean: will be choosen by kernel)
53:
54: &usage unless &Getopts('l:p:');
55: &Getopts if 0; # make -w happy
56:
57: @Hosts = ($#ARGV < $[) ? ("localhost") : @ARGV;
58:
59: ;# setup for debug output
60: $DEBUGFILE=$opt_l;
61: $DEBUGFILE="&STDERR" if $DEBUGFILE eq '-';
62:
63: open(DEBUG,">>$DEBUGFILE") || die("Cannot open \"$DEBUGFILE\": $!\n");
64: select((select(DEBUG),$|=1)[$[]);
65:
66: ;# &log prints a single trap record (adding a (local) time stamp)
67: sub log
68: {
69: chop($date=&ctime(time));
70: print "$date ",@_,"\n";
71: }
72:
73: sub debug
74: {
75: print DEBUG @_,"\n";
76: }
77: ;#
78: $proto_udp = (getprotobyname('udp'))[$[+2] ||
79: (warn("$0: Could not get protocoll number for 'udp' using 17"), 17);
80:
81: $ntp_port = (getservbyname('ntp','udp'))[$[+2] ||
82: (warn("$0: Could not get port number for service ntp/udp using 123"), 123);
83:
84: ;#
85: socket(S, &AF_INET, &SOCK_DGRAM, $proto_udp) || die("Cannot open socket: $!\n");
86:
87: ;#
88: bind(S, pack("S n a4 x8", &AF_INET, $opt_p, &INADDR_ANY)) ||
89: die("Cannot bind: $!\n");
90:
91: ($my_port, $my_addr) = (unpack("S n a4 x8",getsockname(S)))[$[+1,$[+2];
92: &log(sprintf("Listening at address %d.%d.%d.%d port %d",
93: unpack("C4",$my_addr), $my_port));
94:
95: ;# disregister with all servers in case of termination
96: sub cleanup
97: {
98: &log("Aborted by signal \"$_[$[]\"") if defined($_[$[]);
99:
100: foreach (@Hosts)
101: {
102: if ( ! defined($Host{$_}) )
103: {
104: print "no info for host '$_'\n";
105: next;
106: }
107: &ntp'send(S,31,0,"",pack("Sna4x8",&AF_INET,$ntp_port,$Host{$_})); #';
108: }
109: close(S);
110: exit(2);
111: }
112:
113: $SIG{'HUP'} = 'cleanup';
114: $SIG{'INT'} = 'cleanup';
115: $SIG{'QUIT'} = 'cleanup';
116: $SIG{'TERM'} = 'cleanup';
117:
118: 0 && $a && $b;
119: sub timeouts # sort timeout id array
120: {
121: $TIMEOUTS{$a} <=> $TIMEOUTS{$b};
122: }
123:
124: ;# a Request element looks like: pack("a4SC",addr,associd,op)
125: @Requests= ();
126:
127: ;# compute requests for set trap control msgs to each host given
128: {
129: local($name,$addr);
130:
131: foreach (@Hosts)
132: {
133: if (/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/)
134: {
135: ($name,$addr) =
136: (gethostbyaddr(pack("C4",$1,$2,$3,$4),&AF_INET))[$[,$[+4];
137: unless (defined($name))
138: {
139: $name = sprintf("[[%d.%d.%d.%d]]",$1,$2,$3,$4);
140: $addr = pack("C4",$1,$2,$3,$4);
141: }
142: }
143: else
144: {
145: ($name,$addr) = (gethostbyname($_))[$[,$[+4];
146: unless (defined($name))
147: {
148: warn "$0: unknown host \"$_\" - ignored\n";
149: next;
150: }
151: }
152: next if defined($Host{$name});
153: $Host{$name} = $addr;
154: $Host{$_} = $addr;
155: push(@Requests,pack("a4SC",$addr,0,6)); # schedule a set trap request for $name
156: }
157: }
158:
159: sub hostname
160: {
161: local($addr) = @_;
162: return $HostName{$addr} if defined($HostName{$addr});
163: local($name) = gethostbyaddr($addr,&AF_INET);
164: &debug(sprintf("hostname(%d.%d.%d.%d) = \"%s\"",unpack("C4",$addr),$name))
165: if defined($name);
166: defined($name) && ($HostName{$addr} = $name) && (return $name);
167: &debug(sprintf("Failed to get name for %d.%d.%d.%d",unpack("C4",$addr)));
168: return sprintf("[%d.%d.%d.%d]",unpack("C4",$addr));
169: }
170:
171: ;# when no hosts were given on the commandline no requests have been scheduled
172: &usage unless (@Requests);
173:
174: &debug(sprintf("%d request(s) scheduled",scalar(@Requests)));
175: grep(&debug(" - ".$_),keys(%Host));
176:
177: ;# allocate variables;
178: $addr="";
179: $assoc=0;
180: $op = 0;
181: $timeout = 0;
182: $ret="";
183: %TIMEOUTS = ();
184: %TIMEOUT_PROCS = ();
185: @TIMEOUTS = ();
186:
187: $len = 512;
188: $buf = " " x $len;
189:
190: while (1)
191: {
192: if (@Requests || @TIMEOUTS) # if there is some work pending
193: {
194: if (@Requests)
195: {
196: ($addr,$assoc,$op) = unpack("a4SC",($req = shift(@Requests)));
197: &debug(sprintf("Request: %s: %s(%d)",&hostname($addr), &ntp'cntrlop_name($op), $assoc)); #';))
198: $ret = &ntp'send(S,$op,$assoc,"", #'(
199: pack("Sna4x8",&AF_INET,$ntp_port,$addr));
200: &set_timeout("retry-".unpack("H*",$req),time+$BASE_TIMEOUT,
201: sprintf("&retry(\"%s\");",unpack("H*",$req)));
202:
203: last unless (defined($ret)); # warn called by ntp'send();
204:
205: ;# if there are more requests just have a quick look for new messages
206: ;# otherwise grant server time for a response
207: $timeout = @Requests ? 0 : $BASE_TIMEOUT;
208: }
209: if ($timeout && @TIMEOUTS)
210: {
211: ;# ensure not to miss a timeout
212: if ($timeout + time > $TIMEOUTS{$TIMEOUTS[$[]})
213: {
214: $timeout = $TIMEOUTS{$TIMEOUTS[$[]} - time;
215: $timeout = 0 if $timeout < 0;
216: }
217: }
218: }
219: else
220: {
221: ;# no work yet - wait for some messages dropping in
222: ;# usually this will not hapen as the refresh semantic will
223: ;# always have a pending timeout
224: undef($timeout);
225: }
226:
227: vec($mask="",fileno(S),1) = 1;
228: $ret = select($mask,undef,undef,$timeout);
229:
230: warn("$0: select: $!\n"),last if $ret < 0; # give up on error return from select
231:
232: if ($ret == 0)
233: {
234: ;# timeout
235: if (@TIMEOUTS && time > $TIMEOUTS{$TIMEOUTS[$[]})
236: {
237: ;# handle timeout
238: $timeout_proc =
239: (delete $TIMEOUT_PROCS{$TIMEOUTS[$[]},
240: delete $TIMEOUTS{shift(@TIMEOUTS)})[$[];
241: eval $timeout_proc;
242: die "timeout eval (\"$timeout_proc\"): $@\n" if $@;
243: }
244: ;# else: there may be something to be sent
245: }
246: else
247: {
248: ;# data avail
249: $from = recv(S,$buf,$len,0);
250: ;# give up on error return from recv
251: warn("$0: recv: $!\n"), last unless (defined($from));
252:
253: $from = (unpack("Sna4",$from))[$[+2]; # keep host addr only
254: ;# could check for ntp_port - but who cares
255: &debug("-Packet from ",&hostname($from));
256:
257: ;# stuff packet into ntp mode 6 receive machinery
258: ($ret,$data,$status,$associd,$op,$seq,$auth_keyid) =
259: &ntp'handle_packet($buf,$from); # ';
260: &debug(sprintf("%s uses auth_keyid %d",&hostname($from),$auth_keyid)) if defined($auth_keyid);
261: next unless defined($ret);
262:
263: if ($ret eq "")
264: {
265: ;# handle packet
266: ;# simple trap response messages have neither timeout nor retries
267: &clear_timeout("retry-".unpack("H*",pack("a4SC",$from,$associd,$op))) unless $op == 7;
268: delete $RETRY{pack("a4SC",$from,$associd,$op)} unless $op == 7;
269:
270: &process_response($from,$ret,$data,$status,$associd,$op,$seq,$auth_keyid);
271: }
272: else
273: {
274: ;# some kind of error
275: &log(sprintf("%50s: %s: %s",(gethostbyaddr($from,&AF_INET))[$[],$ret,$data));
276: if ($ret ne "TIMEOUT" && $ret ne "ERROR")
277: {
278: &clear_timeout("retry-".unpack("H*",pack("a4SC",$from,$associd,$op)));
279: }
280: }
281: }
282:
283: }
284:
285: warn("$0: terminating\n");
286: &cleanup;
287: exit 0;
288:
289: ;##################################################
290: ;# timeout support
291: ;#
292: sub set_timeout
293: {
294: local($id,$time,$proc) = @_;
295:
296: $TIMEOUTS{$id} = $time;
297: $TIMEOUT_PROCS{$id} = $proc;
298: @TIMEOUTS = sort timeouts keys(%TIMEOUTS);
299: chop($date=&ctime($time));
300: &debug(sprintf("Schedule timeout \"%s\" for %s", $id, $date));
301: }
302:
303: sub clear_timeout
304: {
305: local($id) = @_;
306: delete $TIMEOUTS{$id};
307: delete $TIMEOUT_PROCS{$id};
308: @TIMEOUTS = sort timeouts keys(%TIMEOUTS);
309: &debug("Clear timeout \"$id\"");
310: }
311:
312: 0 && &refresh;
313: sub refresh
314: {
315: local($addr) = @_[$[];
316: $addr = pack("H*",$addr);
317: &debug(sprintf("Refreshing trap for %s", &hostname($addr)));
318: push(@Requests,pack("a4SC",$addr,0,6));
319: }
320:
321: 0 && &retry;
322: sub retry
323: {
324: local($tag) = @_;
325: $tag = pack("H*",$tag);
326: $RETRY{$tag} = 0 if (!defined($RETRY{$tag}));
327:
328: if (++$RETRY{$tag} > $MAX_TRY)
329: {
330: &debug(sprintf("Retry failed: %s assoc %5d op %d",
331: &hostname(substr($tag,$[,4)),
332: unpack("x4SC",$tag)));
333: return;
334: }
335: &debug(sprintf("Retrying: %s assoc %5d op %d",
336: &hostname(substr($tag,$[,4)),
337: unpack("x4SC",$tag)));
338: push(@Requests,$tag);
339: }
340:
341: sub process_response
342: {
343: local($from,$ret,$data,$status,$associd,$op,$seq,$auth_keyid) = @_;
344:
345: $msg="";
346: if ($op == 7) # trap response
347: {
348: $msg .= sprintf("%40s trap#%-5d",
349: &hostname($from),$seq);
350: &debug (sprintf("\nTrap %d associd %d:\n%s\n===============\n",$seq,$associd,$data));
351: if ($associd == 0) # system event
352: {
353: $msg .= " SYSTEM ";
354: $evnt = &ntp'SystemEvent($status); #';
355: $msg .= "$evnt ";
356: ;# for special cases add additional info
357: ($stratum) = ($data =~ /stratum=(\d+)/);
358: ($refid) = ($data =~ /refid=([\w\.]+)/);
359: $msg .= "stratum=$stratum refid=$refid";
360: if ($refid =~ /\[?(\d+)\.(\d+)\.(\d+)\.(\d+)/)
361: {
362: local($x) = (gethostbyaddr(pack("C4",$1,$2,$3,$4),&AF_INET));
363: $msg .= " " . $x if defined($x)
364: }
365: if ($evnt eq "event_sync_chg")
366: {
367: $msg .= sprintf("%s %s ",
368: &ntp'LI($status), #',
369: &ntp'ClockSource($status) #'
370: );
371: }
372: elsif ($evnt eq "event_sync/strat_chg")
373: {
374: ($peer) = ($data =~ /peer=([0-9]+)/);
375: $msg .= " peer=$peer";
376: }
377: elsif ($evnt eq "event_clock_excptn")
378: {
379: if (($device) = ($data =~ /device=\"([^\"]+)\"/))
380: {
381: ($cstatus) = ($data =~ /refclockstatus=0?x?([\da-fA-F]+)/);
382: $Cstatus = hex($cstatus);
383: $msg .= sprintf("- %-32s",&ntp'clock_status($Cstatus)); #');
384: ($timecode) = ($data =~ /timecode=\"([^\"]+)\"/);
385: $msg .= " \"$device\" \"$timecode\"";
386: }
387: else
388: {
389: push(@Requests,pack("a4SC",$from, $associd, 4));
390: }
391: }
392: }
393: else # peer event
394: {
395: $msg .= sprintf("peer %5d ",$associd);
396: ($srcadr) = ($data =~ /srcadr=\[?([\d\.]+)/);
397: $msg .= sprintf("%-18s %40s ", "[$srcadr]",
398: &hostname(pack("C4",split(/\./,$srcadr))));
399: $evnt = &ntp'PeerEvent($status); #';
400: $msg .= "$evnt ";
401: ;# for special cases include additional info
402: if ($evnt eq "event_clock_excptn")
403: {
404: if (($device) = ($data =~ /device=\"([^\"]+)\"/))
405: {
406: ;#&debug("----\n$data\n====\n");
407: ($cstatus) = ($data =~ /refclockstatus=0?x?([\da-fA-F]+)/);
408: $Cstatus = hex($cstatus);
409: $msg .= sprintf("- %-32s",&ntp'clock_status($Cstatus)); #');
410: ($timecode) = ($data =~ /timecode=\"([^\"]+)\"/);
411: $msg .= " \"$device\" \"$timecode\"";
412: }
413: else
414: {
415: ;# no clockvars included - post a cv request
416: push(@Requests,pack("a4SC",$from, $associd, 4));
417: }
418: }
419: elsif ($evnt eq "event_stratum_chg")
420: {
421: ($stratum) = ($data =~ /stratum=(\d+)/);
422: $msg .= "new stratum $stratum";
423: }
424: }
425: }
426: elsif ($op == 6) # set trap resonse
427: {
428: &debug("Set trap ok from ",&hostname($from));
429: &set_timeout("refresh-".unpack("H*",$from),time+$REFRESH_TIME,
430: sprintf("&refresh(\"%s\");",unpack("H*",$from)));
431: return;
432: }
433: elsif ($op == 4) # read clock variables response
434: {
435: ;# status of clock
436: $msg .= sprintf(" %40s ", &hostname($from));
437: if ($associd == 0)
438: {
439: $msg .= "system clock status: ";
440: }
441: else
442: {
443: $msg .= sprintf("peer %5d clock",$associd);
444: }
445: $msg .= sprintf("%-32s",&ntp'clock_status($status)); #');
446: ($device) = ($data =~ /device=\"([^\"]+)\"/);
447: ($timecode) = ($data =~ /timecode=\"([^\"]+)\"/);
448: $msg .= " \"$device\" \"$timecode\"";
449: }
450: elsif ($op == 31) # unset trap response (UNOFFICIAL op)
451: {
452: ;# clear timeout
453: &debug("Clear Trap ok from ",&hostname($from));
454: &clear_timeout("refresh-".unpack("H*",$from));
455: return;
456: }
457: else # unexpected response
458: {
459: $msg .= "unexpected response to op $op assoc=$associd";
460: $msg .= sprintf(" status=%04x",$status);
461: }
462: &log($msg);
463: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>