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