1 /* $OpenBSD: cmds.c,v 1.19 2004/02/04 21:54:05 jmc Exp $ */
2
3 /*-
4 * Copyright (c) 1985, 1993 The Regents of the University of California.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include "timedc.h"
33 #include <sys/file.h>
34
35 #include <netinet/in_systm.h>
36 #include <netinet/ip.h>
37 #include <netinet/ip_icmp.h>
38
39 #include <poll.h>
40 #include <signal.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 __SCCSID("@(#)cmds.c 5.1 (Berkeley) 5/11/93");
46 __RCSID("$MirOS: src/usr.sbin/timed/timedc/cmds.c,v 1.4 2007/08/08 19:09:53 tg Exp $");
47
48 #define TSPTYPES
49 #include <protocols/timed.h>
50
51 #ifdef sgi
52 #include <bstring.h>
53 #include <sys/clock.h>
54 #else
55 #define SECHR (60*60)
56 #define SECDAY (24*SECHR)
57 #endif /* sgi */
58
59 # define DATE_PROTO "udp"
60 # define DATE_PORT "time"
61
62
63 int sock;
64 int sock_raw;
65 char myname[MAXHOSTNAMELEN];
66 struct hostent *hp;
67 struct sockaddr_in server;
68 struct sockaddr_in dayaddr;
69 extern int measure_delta;
70
71 void bytenetorder(struct tsp *);
72 void bytehostorder(struct tsp *);
73
74
75 #define BU ((unsigned long)2208988800U) /* seconds before UNIX epoch */
76
77
78 /* compute the difference between our date and another machine
79 */
80 static int /* difference in days from our time */
daydiff(char * hostname)81 daydiff(char *hostname)
82 {
83 struct sockaddr_in from;
84 struct timeval now;
85 struct pollfd pfd;
86 unsigned long sec;
87 int i, trials;
88 socklen_t fromlen;
89
90 for (trials = 0; trials < 10; trials++) {
91 /* ask for the time */
92 sec = 0;
93
94 siginterrupt(SIGINT, 1);
95 if (sendto(sock, &sec, sizeof(sec), 0,
96 (struct sockaddr *)&dayaddr, sizeof(dayaddr)) < 0) {
97 if (errno == EINTR && gotintr)
98 goto bail;
99 siginterrupt(SIGINT, 0);
100 perror("sendto(sock)");
101 goto bail;
102 }
103 siginterrupt(SIGINT, 0);
104
105 for (;;) {
106 pfd.fd = sock;
107 pfd.events = POLLIN;
108 i = poll(&pfd, 1, 2 * 1000);
109 if (i < 0) {
110 if (errno == EINTR) {
111 if (gotintr)
112 goto bail;
113 continue;
114 }
115 perror("poll(date read)");
116 goto bail;
117 }
118 if (i == 0)
119 break;
120
121 fromlen = sizeof(from);
122 siginterrupt(SIGINT, 1);
123 if (recvfrom(sock, &sec, sizeof(sec), 0,
124 (struct sockaddr *)&from, &fromlen) < 0) {
125 if (errno == EINTR && gotintr)
126 goto bail;
127 siginterrupt(SIGINT, 0);
128 perror("recvfrom(date read)");
129 goto bail;
130 }
131 siginterrupt(SIGINT, 0);
132
133 sec = ntohl(sec);
134 if (sec < BU) {
135 fprintf(stderr, "%s says it is before 1970: %lu",
136 hostname, sec);
137 goto bail;
138 }
139 sec -= BU;
140
141 (void)gettimeofday(&now, (struct timezone *)NULL);
142 return (sec - now.tv_sec);
143 }
144 }
145
146 /* if we get here, we tried too many times */
147 fprintf(stderr,"%s will not tell us the date\n", hostname);
148
149 bail:
150 siginterrupt(SIGINT, 0);
151 return (0);
152 }
153
154
155 /*
156 * Clockdiff computes the difference between the time of the machine on
157 * which it is called and the time of the machines given as argument.
158 * The time differences measured by clockdiff are obtained using a sequence
159 * of ICMP TSTAMP messages which are returned to the sender by the IP module
160 * in the remote machine.
161 * In order to compare clocks of machines in different time zones, the time
162 * is transmitted (as a 32-bit value) in milliseconds since midnight UT.
163 * If a hosts uses a different time format, it should set the high order
164 * bit of the 32-bit quantity it transmits.
165 * However, VMS apparently transmits the time in milliseconds since midnight
166 * local time (rather than GMT) without setting the high order bit.
167 * Furthermore, it does not understand daylight-saving time. This makes
168 * clockdiff behaving inconsistently with hosts running VMS.
169 *
170 * In order to reduce the sensitivity to the variance of message transmission
171 * time, clockdiff sends a sequence of messages. Yet, measures between
172 * two `distant' hosts can be affected by a small error. The error can,
173 * however, be reduced by increasing the number of messages sent in each
174 * measurement.
175 */
176 void
clockdiff(int argc,char * argv[])177 clockdiff(int argc, char *argv[])
178 {
179 struct servent *sp;
180 long avg;
181 int avg_cnt;
182 extern int measure(u_long, u_long, char *, struct sockaddr_in *, int);
183 int measure_status;
184
185 if (argc < 2) {
186 printf("Usage: clockdiff host ... \n");
187 return;
188 }
189
190 (void)gethostname(myname,sizeof(myname));
191
192 /* get the address for the date ready */
193 sp = getservbyname(DATE_PORT, DATE_PROTO);
194 if (!sp) {
195 (void)fprintf(stderr, "%s/%s is an unknown service\n",
196 DATE_PORT, DATE_PROTO);
197 dayaddr.sin_port = 0;
198 } else
199 dayaddr.sin_port = sp->s_port;
200
201 measure_status = 0;
202 while (argc > 1) {
203 argc--;
204 argv++;
205
206 siginterrupt(SIGINT, 1);
207 hp = gethostbyname(*argv);
208 if (hp == NULL) {
209 if (errno == EINTR && gotintr) {
210 siginterrupt(SIGINT, 0);
211 return;
212 }
213 siginterrupt(SIGINT, 0);
214 fprintf(stderr, "timedc: %s: ", *argv);
215 herror(0);
216 continue;
217 }
218 siginterrupt(SIGINT, 0);
219
220 server.sin_family = hp->h_addrtype;
221 memmove(&server.sin_addr.s_addr, hp->h_addr, hp->h_length);
222 for (avg_cnt = 0, avg = 0; avg_cnt < 16; avg_cnt++) {
223 measure_status = measure(10000, 100, *argv, &server, 1);
224 if (measure_status != GOOD)
225 break;
226 avg += measure_delta;
227 }
228 if (measure_status == GOOD)
229 measure_delta = avg/avg_cnt;
230
231 switch (measure_status) {
232 case HOSTDOWN:
233 printf("%s is down\n", hp->h_name);
234 continue;
235 case NONSTDTIME:
236 printf("%s transmits a non-standard time format\n",
237 hp->h_name);
238 continue;
239 case UNREACHABLE:
240 printf("%s is unreachable\n", hp->h_name);
241 continue;
242 }
243
244 /*
245 * Try to get the date only after using ICMP timestamps to
246 * get the time. This is because the date protocol
247 * is optional.
248 */
249 if (dayaddr.sin_port != 0) {
250 dayaddr.sin_family = hp->h_addrtype;
251 memmove(&dayaddr.sin_addr.s_addr, hp->h_addr,
252 hp->h_length);
253 avg = daydiff(*argv);
254 if (avg > SECDAY) {
255 printf("time on %s is %ld days ahead %s\n",
256 hp->h_name, avg/SECDAY, myname);
257 continue;
258 } else if (avg < -SECDAY) {
259 printf("time on %s is %ld days behind %s\n",
260 hp->h_name, -avg/SECDAY, myname);
261 continue;
262 }
263 }
264
265 if (measure_delta > 0) {
266 printf("time on %s is %d ms. ahead of time on %s\n",
267 hp->h_name, measure_delta, myname);
268 } else if (measure_delta == 0) {
269 printf("%s and %s have the same time\n",
270 hp->h_name, myname);
271 } else {
272 printf("time on %s is %d ms. behind time on %s\n",
273 hp->h_name, -measure_delta, myname);
274 }
275 }
276 }
277
278
279 /*
280 * finds location of master timedaemon
281 */
282 void
msite(int argc,char * argv[])283 msite(int argc, char *argv[])
284 {
285 struct sockaddr_in dest, from;
286 struct servent *srvp;
287 int i, cc;
288 socklen_t length;
289 struct pollfd pfd;
290 struct tsp msg;
291 char *tgtname;
292
293 if (argc < 1) {
294 printf("Usage: msite [hostname]\n");
295 return;
296 }
297
298 srvp = getservbyname("timed", "udp");
299 if (srvp == 0) {
300 fprintf(stderr, "udp/timed: unknown service\n");
301 return;
302 }
303 memset(&dest, 0, sizeof dest);
304 dest.sin_port = srvp->s_port;
305 dest.sin_family = AF_INET;
306
307 (void)gethostname(myname, sizeof(myname));
308 i = 1;
309
310 do {
311 tgtname = (i >= argc) ? myname : argv[i];
312 siginterrupt(SIGINT, 1);
313 hp = gethostbyname(tgtname);
314 if (hp == 0) {
315 if (errno == EINTR && gotintr)
316 goto bail;
317 siginterrupt(SIGINT, 0);
318 fprintf(stderr, "timedc: %s: ", tgtname);
319 herror(0);
320 continue;
321 }
322
323 memmove(&dest.sin_addr.s_addr, hp->h_addr, hp->h_length);
324 (void)strlcpy(msg.tsp_name, myname, sizeof msg.tsp_name);
325 msg.tsp_type = TSP_MSITE;
326 msg.tsp_vers = TSPVERSION;
327 bytenetorder(&msg);
328
329 if (sendto(sock, &msg, sizeof(struct tsp), 0,
330 (struct sockaddr *)&dest, sizeof(dest)) < 0) {
331 if (errno == EINTR && gotintr)
332 goto bail;
333 siginterrupt(SIGINT, 0);
334 perror("sendto");
335 continue;
336 }
337
338 pfd.fd = sock;
339 pfd.events = POLLIN;
340 switch (poll(&pfd, 1, 15 * 1000)) {
341 case -1:
342 if (errno == EINTR && gotintr)
343 goto bail;
344 siginterrupt(SIGINT, 0);
345 continue;
346 case 1:
347 length = sizeof(from);
348 cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
349 (struct sockaddr *)&from, &length);
350 if (cc < 0) {
351 if (errno == EINTR && gotintr)
352 goto bail;
353 siginterrupt(SIGINT, 0);
354 perror("recvfrom");
355 continue;
356 }
357 siginterrupt(SIGINT, 0);
358
359 if (cc < sizeof(struct tsp)) {
360 fprintf(stderr,
361 "short packet (%u/%zu bytes) from %s\n",
362 cc, sizeof(struct tsp),
363 inet_ntoa(from.sin_addr));
364 continue;
365 }
366 bytehostorder(&msg);
367 if (msg.tsp_type == TSP_ACK) {
368 printf("master timedaemon at %s is %s\n",
369 tgtname, msg.tsp_name);
370 } else {
371 if (msg.tsp_type >= TSPTYPENUMBER)
372 printf("received unknown ack: %u\n",
373 msg.tsp_type);
374 else
375 printf("received wrong ack: %s\n",
376 tsptype[msg.tsp_type]);
377 }
378 break;
379 case 0:
380 siginterrupt(SIGINT, 0);
381 printf("communication error with %s\n", tgtname);
382 break;
383 }
384 } while (++i < argc);
385
386 bail:
387 siginterrupt(SIGINT, 0);
388 }
389
390 /*
391 * quits timedc
392 */
393 void
quit(int ignored,char * unused[])394 quit(int ignored, char *unused[])
395 {
396 exit(0);
397 }
398
399
400 /*
401 * Causes the election timer to expire on the selected hosts
402 * It sends just one udp message per machine, relying on
403 * reliability of communication channel.
404 */
405 void
testing(int argc,char * argv[])406 testing(int argc, char *argv[])
407 {
408 struct sockaddr_in sin;
409 struct tsp msg;
410 struct servent *srvp;
411
412 if (argc < 2) {
413 printf("Usage: election host1 [host2 ...]\n");
414 return;
415 }
416
417 srvp = getservbyname("timed", "udp");
418 if (srvp == 0) {
419 fprintf(stderr, "udp/timed: unknown service\n");
420 return;
421 }
422
423 while (argc > 1) {
424 argc--;
425 argv++;
426
427 siginterrupt(SIGINT, 1);
428 hp = gethostbyname(*argv);
429 if (hp == NULL) {
430 if (errno == EINTR && gotintr)
431 goto bail;
432 siginterrupt(SIGINT, 0);
433 fprintf(stderr, "timedc: %s: ", *argv);
434 herror(0);
435 argc--;
436 argv++;
437 continue;
438 }
439
440 memset(&sin, 0, sizeof sin);
441 sin.sin_port = srvp->s_port;
442 sin.sin_family = hp->h_addrtype;
443 memmove(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
444
445 msg.tsp_type = TSP_TEST;
446 msg.tsp_vers = TSPVERSION;
447 (void)gethostname(myname, sizeof(myname));
448 (void)strncpy(msg.tsp_name, myname, sizeof(msg.tsp_name));
449 bytenetorder(&msg);
450
451 if (sendto(sock, &msg, sizeof(struct tsp), 0,
452 (struct sockaddr *)&sin, sizeof(sin)) < 0) {
453 if (errno == EINTR && gotintr)
454 goto bail;
455 siginterrupt(SIGINT, 0);
456 perror("sendto");
457 }
458 }
459 bail:
460 siginterrupt(SIGINT, 0);
461 }
462
463
464 /*
465 * Enables or disables tracing on local timedaemon
466 */
467 void
tracing(int argc,char * argv[])468 tracing(int argc, char *argv[])
469 {
470 struct sockaddr_in dest;
471 struct sockaddr_in from;
472 struct tsp msg;
473 struct servent *srvp;
474 struct pollfd pfd;
475 int cc, onflag;
476 socklen_t length;
477
478 if (argc != 2) {
479 printf("Usage: tracing { on | off }\n");
480 return;
481 }
482
483 srvp = getservbyname("timed", "udp");
484 if (srvp == 0) {
485 fprintf(stderr, "udp/timed: unknown service\n");
486 return;
487 }
488
489 memset(&dest, 0, sizeof dest);
490 dest.sin_port = srvp->s_port;
491 dest.sin_family = AF_INET;
492
493 (void)gethostname(myname,sizeof(myname));
494 siginterrupt(SIGINT, 1);
495 hp = gethostbyname(myname);
496 if (hp == NULL && errno == EINTR && gotintr)
497 goto bail;
498
499 memmove(&dest.sin_addr.s_addr, hp->h_addr, hp->h_length);
500
501 if (strcmp(argv[1], "on") == 0) {
502 msg.tsp_type = TSP_TRACEON;
503 onflag = ON;
504 } else {
505 msg.tsp_type = TSP_TRACEOFF;
506 onflag = OFF;
507 }
508
509 (void)strlcpy(msg.tsp_name, myname, sizeof msg.tsp_name);
510 msg.tsp_vers = TSPVERSION;
511 bytenetorder(&msg);
512 if (sendto(sock, &msg, sizeof(struct tsp), 0,
513 (struct sockaddr *)&dest, sizeof(dest)) < 0) {
514 if (errno == EINTR && gotintr)
515 goto bail;
516 siginterrupt(SIGINT, 0);
517 perror("sendto");
518 return;
519 }
520
521 pfd.fd = sock;
522 pfd.events = POLLIN;
523 switch (poll(&pfd, 1, 5 * 1000)) {
524 case 1:
525 length = sizeof(from);
526 cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
527 (struct sockaddr *)&from, &length);
528 if (cc < 0) {
529 if (errno == EINTR && gotintr)
530 goto bail;
531 siginterrupt(SIGINT, 0);
532 perror("recvfrom");
533 return;
534 }
535 siginterrupt(SIGINT, 0);
536 if (cc < sizeof(struct tsp)) {
537 fprintf(stderr, "short packet (%u/%zu bytes) from %s\n",
538 cc, sizeof(struct tsp), inet_ntoa(from.sin_addr));
539 goto bail;
540 }
541 bytehostorder(&msg);
542 if (msg.tsp_type == TSP_ACK) {
543 if (onflag)
544 printf("timed tracing enabled\n");
545 else
546 printf("timed tracing disabled\n");
547 } else {
548 if (msg.tsp_type >= TSPTYPENUMBER)
549 printf("unknown ack received: %u\n",
550 msg.tsp_type);
551 else
552 printf("wrong ack received: %s\n",
553 tsptype[msg.tsp_type]);
554 }
555 break;
556 case 0:
557 siginterrupt(SIGINT, 0);
558 printf("communication error\n");
559 break;
560 }
561 bail:
562 siginterrupt(SIGINT, 0);
563 }
564