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