1 /*-
2 * Copyright (c) 1985, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 */
7
8 #ifndef lint
9 static char sccsid[] = "@(#)cmds.c 8.2 (Berkeley) 03/26/95";
10 #endif /* not lint */
11
12 #ifdef sgi
13 #ident "$Revision: 1.10 $"
14 #endif
15
16 #include "timedc.h"
17 #include <sys/file.h>
18
19 #include <netinet/in_systm.h>
20 #include <netinet/ip.h>
21 #include <netinet/ip_icmp.h>
22
23 #include <stdlib.h>
24 #include <strings.h>
25 #include <unistd.h>
26
27 #define TSPTYPES
28 #include <protocols/timed.h>
29
30 #ifdef sgi
31 #include <bstring.h>
32 #include <sys/clock.h>
33 #else
34 #define SECHR (60*60)
35 #define SECDAY (24*SECHR)
36 #endif /* sgi */
37
38 # define DATE_PROTO "udp"
39 # define DATE_PORT "time"
40
41
42 int sock;
43 int sock_raw;
44 char myname[MAXHOSTNAMELEN];
45 struct hostent *hp;
46 struct sockaddr_in server;
47 struct sockaddr_in dayaddr;
48 extern int measure_delta;
49
50 void bytenetorder(struct tsp *);
51 void bytehostorder(struct tsp *);
52
53
54 #define BU ((unsigned long)2208988800) /* seconds before UNIX epoch */
55
56
57 /* compute the difference between our date and another machine
58 */
59 static int /* difference in days from our time */
daydiff(hostname)60 daydiff(hostname)
61 char *hostname;
62 {
63 int i;
64 int trials;
65 struct timeval tout, now;
66 fd_set ready;
67 struct sockaddr from;
68 int fromlen;
69 unsigned long sec;
70
71
72 /* wait 2 seconds between 10 tries */
73 tout.tv_sec = 2;
74 tout.tv_usec = 0;
75 for (trials = 0; trials < 10; trials++) {
76 /* ask for the time */
77 sec = 0;
78 if (sendto(sock, &sec, sizeof(sec), 0,
79 (struct sockaddr*)&dayaddr, sizeof(dayaddr)) < 0) {
80 perror("sendto(sock)");
81 return 0;
82 }
83
84 for (;;) {
85 FD_ZERO(&ready);
86 FD_SET(sock, &ready);
87 i = select(sock+1, &ready, (fd_set *)0,
88 (fd_set *)0, &tout);
89 if (i < 0) {
90 if (errno == EINTR)
91 continue;
92 perror("select(date read)");
93 return 0;
94 }
95 if (0 == i)
96 break;
97
98 fromlen = sizeof(from);
99 if (recvfrom(sock,&sec,sizeof(sec),0,
100 &from,&fromlen) < 0) {
101 perror("recvfrom(date read)");
102 return 0;
103 }
104
105 sec = ntohl(sec);
106 if (sec < BU) {
107 fprintf(stderr,
108 "%s says it is before 1970: %lu",
109 hostname, sec);
110 return 0;
111 }
112 sec -= BU;
113
114 (void)gettimeofday(&now, (struct timezone*)0);
115 return (sec - now.tv_sec);
116 }
117 }
118
119 /* if we get here, we tried too many times */
120 fprintf(stderr,"%s will not tell us the date\n", hostname);
121 return 0;
122 }
123
124
125 /*
126 * Clockdiff computes the difference between the time of the machine on
127 * which it is called and the time of the machines given as argument.
128 * The time differences measured by clockdiff are obtained using a sequence
129 * of ICMP TSTAMP messages which are returned to the sender by the IP module
130 * in the remote machine.
131 * In order to compare clocks of machines in different time zones, the time
132 * is transmitted (as a 32-bit value) in milliseconds since midnight UT.
133 * If a hosts uses a different time format, it should set the high order
134 * bit of the 32-bit quantity it transmits.
135 * However, VMS apparently transmits the time in milliseconds since midnight
136 * local time (rather than GMT) without setting the high order bit.
137 * Furthermore, it does not understand daylight-saving time. This makes
138 * clockdiff behaving inconsistently with hosts running VMS.
139 *
140 * In order to reduce the sensitivity to the variance of message transmission
141 * time, clockdiff sends a sequence of messages. Yet, measures between
142 * two `distant' hosts can be affected by a small error. The error can,
143 * however, be reduced by increasing the number of messages sent in each
144 * measurement.
145 */
146 void
clockdiff(argc,argv)147 clockdiff(argc, argv)
148 int argc;
149 char *argv[];
150 {
151 int measure_status;
152 extern int measure(u_long, u_long, char *, struct sockaddr_in*, int);
153 register int avg_cnt;
154 register long avg;
155 struct servent *sp;
156
157 if (argc < 2) {
158 printf("Usage: clockdiff host ... \n");
159 return;
160 }
161
162 (void)gethostname(myname,sizeof(myname));
163
164 /* get the address for the date ready */
165 sp = getservbyname(DATE_PORT, DATE_PROTO);
166 if (!sp) {
167 (void)fprintf(stderr, "%s/%s is an unknown service\n",
168 DATE_PORT, DATE_PROTO);
169 dayaddr.sin_port = 0;
170 } else {
171 dayaddr.sin_port = sp->s_port;
172 }
173
174 while (argc > 1) {
175 argc--; argv++;
176 hp = gethostbyname(*argv);
177 if (hp == NULL) {
178 fprintf(stderr, "timedc: %s: ", *argv);
179 herror(0);
180 continue;
181 }
182
183 server.sin_family = hp->h_addrtype;
184 bcopy(hp->h_addr, &server.sin_addr.s_addr, hp->h_length);
185 for (avg_cnt = 0, avg = 0; avg_cnt < 16; avg_cnt++) {
186 measure_status = measure(10000,100, *argv, &server, 1);
187 if (measure_status != GOOD)
188 break;
189 avg += measure_delta;
190 }
191 if (measure_status == GOOD)
192 measure_delta = avg/avg_cnt;
193
194 switch (measure_status) {
195 case HOSTDOWN:
196 printf("%s is down\n", hp->h_name);
197 continue;
198 case NONSTDTIME:
199 printf("%s transmitts a non-standard time format\n",
200 hp->h_name);
201 continue;
202 case UNREACHABLE:
203 printf("%s is unreachable\n", hp->h_name);
204 continue;
205 }
206
207 /*
208 * Try to get the date only after using ICMP timestamps to
209 * get the time. This is because the date protocol
210 * is optional.
211 */
212 if (dayaddr.sin_port != 0) {
213 dayaddr.sin_family = hp->h_addrtype;
214 bcopy(hp->h_addr, &dayaddr.sin_addr.s_addr,
215 hp->h_length);
216 avg = daydiff(*argv);
217 if (avg > SECDAY) {
218 printf("time on %s is %ld days ahead %s\n",
219 hp->h_name, avg/SECDAY, myname);
220 continue;
221 } else if (avg < -SECDAY) {
222 printf("time on %s is %ld days behind %s\n",
223 hp->h_name, -avg/SECDAY, myname);
224 continue;
225 }
226 }
227
228 if (measure_delta > 0) {
229 printf("time on %s is %d ms. ahead of time on %s\n",
230 hp->h_name, measure_delta, myname);
231 } else if (measure_delta == 0) {
232 printf("%s and %s have the same time\n",
233 hp->h_name, myname);
234 } else {
235 printf("time on %s is %d ms. behind time on %s\n",
236 hp->h_name, -measure_delta, myname);
237 }
238 }
239 return;
240 }
241
242
243 /*
244 * finds location of master timedaemon
245 */
246 void
msite(argc,argv)247 msite(argc, argv)
248 int argc;
249 char *argv[];
250 {
251 int cc;
252 fd_set ready;
253 struct sockaddr_in dest;
254 int i, length;
255 struct sockaddr from;
256 struct timeval tout;
257 struct tsp msg;
258 struct servent *srvp;
259 char *tgtname;
260
261 if (argc < 1) {
262 printf("Usage: msite [hostname]\n");
263 return;
264 }
265
266 srvp = getservbyname("timed", "udp");
267 if (srvp == 0) {
268 fprintf(stderr, "udp/timed: unknown service\n");
269 return;
270 }
271 dest.sin_port = srvp->s_port;
272 dest.sin_family = AF_INET;
273
274 (void)gethostname(myname, sizeof(myname));
275 i = 1;
276 do {
277 tgtname = (i >= argc) ? myname : argv[i];
278 hp = gethostbyname(tgtname);
279 if (hp == 0) {
280 fprintf(stderr, "timedc: %s: ", tgtname);
281 herror(0);
282 continue;
283 }
284 bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length);
285
286 (void)strcpy(msg.tsp_name, myname);
287 msg.tsp_type = TSP_MSITE;
288 msg.tsp_vers = TSPVERSION;
289 bytenetorder(&msg);
290 if (sendto(sock, &msg, sizeof(struct tsp), 0,
291 (struct sockaddr*)&dest,
292 sizeof(struct sockaddr)) < 0) {
293 perror("sendto");
294 continue;
295 }
296
297 tout.tv_sec = 15;
298 tout.tv_usec = 0;
299 FD_ZERO(&ready);
300 FD_SET(sock, &ready);
301 if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0,
302 &tout)) {
303 length = sizeof(struct sockaddr);
304 cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
305 &from, &length);
306 if (cc < 0) {
307 perror("recvfrom");
308 continue;
309 }
310 bytehostorder(&msg);
311 if (msg.tsp_type == TSP_ACK) {
312 printf("master timedaemon at %s is %s\n",
313 tgtname, msg.tsp_name);
314 } else {
315 printf("received wrong ack: %s\n",
316 tsptype[msg.tsp_type]);
317 }
318 } else {
319 printf("communication error with %s\n", tgtname);
320 }
321 } while (++i < argc);
322 }
323
324 /*
325 * quits timedc
326 */
327 void
quit()328 quit()
329 {
330 exit(0);
331 }
332
333
334 /*
335 * Causes the election timer to expire on the selected hosts
336 * It sends just one udp message per machine, relying on
337 * reliability of communication channel.
338 */
339 void
testing(argc,argv)340 testing(argc, argv)
341 int argc;
342 char *argv[];
343 {
344 struct servent *srvp;
345 struct sockaddr_in sin;
346 struct tsp msg;
347
348 if (argc < 2) {
349 printf("Usage: election host1 [host2 ...]\n");
350 return;
351 }
352
353 srvp = getservbyname("timed", "udp");
354 if (srvp == 0) {
355 fprintf(stderr, "udp/timed: unknown service\n");
356 return;
357 }
358
359 while (argc > 1) {
360 argc--; argv++;
361 hp = gethostbyname(*argv);
362 if (hp == NULL) {
363 fprintf(stderr, "timedc: %s: ", *argv);
364 herror(0);
365 argc--; argv++;
366 continue;
367 }
368 sin.sin_port = srvp->s_port;
369 sin.sin_family = hp->h_addrtype;
370 bcopy(hp->h_addr, &sin.sin_addr.s_addr, hp->h_length);
371
372 msg.tsp_type = TSP_TEST;
373 msg.tsp_vers = TSPVERSION;
374 (void)gethostname(myname, sizeof(myname));
375 (void)strncpy(msg.tsp_name, myname, sizeof(msg.tsp_name));
376 bytenetorder(&msg);
377 if (sendto(sock, &msg, sizeof(struct tsp), 0,
378 (struct sockaddr*)&sin,
379 sizeof(struct sockaddr)) < 0) {
380 perror("sendto");
381 }
382 }
383 }
384
385
386 /*
387 * Enables or disables tracing on local timedaemon
388 */
389 void
tracing(argc,argv)390 tracing(argc, argv)
391 int argc;
392 char *argv[];
393 {
394 int onflag;
395 int length;
396 int cc;
397 fd_set ready;
398 struct sockaddr_in dest;
399 struct sockaddr from;
400 struct timeval tout;
401 struct tsp msg;
402 struct servent *srvp;
403
404 if (argc != 2) {
405 printf("Usage: tracing { on | off }\n");
406 return;
407 }
408
409 srvp = getservbyname("timed", "udp");
410 if (srvp == 0) {
411 fprintf(stderr, "udp/timed: unknown service\n");
412 return;
413 }
414 dest.sin_port = srvp->s_port;
415 dest.sin_family = AF_INET;
416
417 (void)gethostname(myname,sizeof(myname));
418 hp = gethostbyname(myname);
419 bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length);
420
421 if (strcmp(argv[1], "on") == 0) {
422 msg.tsp_type = TSP_TRACEON;
423 onflag = ON;
424 } else {
425 msg.tsp_type = TSP_TRACEOFF;
426 onflag = OFF;
427 }
428
429 (void)strcpy(msg.tsp_name, myname);
430 msg.tsp_vers = TSPVERSION;
431 bytenetorder(&msg);
432 if (sendto(sock, &msg, sizeof(struct tsp), 0,
433 (struct sockaddr*)&dest, sizeof(struct sockaddr)) < 0) {
434 perror("sendto");
435 return;
436 }
437
438 tout.tv_sec = 5;
439 tout.tv_usec = 0;
440 FD_ZERO(&ready);
441 FD_SET(sock, &ready);
442 if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) {
443 length = sizeof(struct sockaddr);
444 cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
445 &from, &length);
446 if (cc < 0) {
447 perror("recvfrom");
448 return;
449 }
450 bytehostorder(&msg);
451 if (msg.tsp_type == TSP_ACK)
452 if (onflag)
453 printf("timed tracing enabled\n");
454 else
455 printf("timed tracing disabled\n");
456 else
457 printf("wrong ack received: %s\n",
458 tsptype[msg.tsp_type]);
459 } else
460 printf("communication error\n");
461 }
462
463 int
priv_resources()464 priv_resources()
465 {
466 int port;
467 struct sockaddr_in sin;
468
469 sock = socket(AF_INET, SOCK_DGRAM, 0);
470 if (sock < 0) {
471 perror("opening socket");
472 return(-1);
473 }
474
475 sin.sin_family = AF_INET;
476 sin.sin_addr.s_addr = 0;
477 for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) {
478 sin.sin_port = htons((u_short)port);
479 if (bind(sock, (struct sockaddr*)&sin, sizeof (sin)) >= 0)
480 break;
481 if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) {
482 perror("bind");
483 (void) close(sock);
484 return(-1);
485 }
486 }
487 if (port == IPPORT_RESERVED / 2) {
488 fprintf(stderr, "all reserved ports in use\n");
489 (void) close(sock);
490 return(-1);
491 }
492
493 sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
494 if (sock_raw < 0) {
495 perror("opening raw socket");
496 (void) close(sock);
497 return(-1);
498 }
499 return(1);
500 }
501