xref: /original-bsd/bin/date/date.c (revision e2944021)
1 /*
2  * Copyright (c) 1985 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1985 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)date.c	4.17 (Berkeley) 04/29/86";
15 #endif not lint
16 
17 /*
18  * Date - print and set date
19  */
20 
21 #include <sys/param.h>
22 #include <stdio.h>
23 #include <sys/time.h>
24 #include <sys/file.h>
25 #include <errno.h>
26 #include <syslog.h>
27 #include <utmp.h>
28 
29 #define WTMP	"/usr/adm/wtmp"
30 
31 struct	timeval tv, now;
32 struct	timezone tz;
33 char	*ap, *ep, *sp;
34 int	uflag, nflag;
35 
36 char	*timezone();
37 static	int dmsize[12] =
38     { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
39 static	char *usage = "usage: date [-n] [-u] [yymmddhhmm[.ss]]\n";
40 
41 struct utmp wtmp[2] = {
42 	{ "|", "", "", 0 },
43 	{ "{", "", "", 0 }
44 };
45 
46 char	*ctime();
47 char	*asctime();
48 struct	tm *localtime();
49 struct	tm *gmtime();
50 char	*strcpy();
51 char	*username, *getlogin();
52 
53 main(argc, argv)
54 	int argc;
55 	char *argv[];
56 {
57 	register char *tzn;
58 
59 	openlog("date", LOG_ODELAY, LOG_AUTH);
60 	(void) gettimeofday(&tv, &tz);
61 	now = tv;
62 
63 	while (argc > 1 && argv[1][0] == '-') {
64 		while (*++argv[1])
65 		    switch ((int)argv[1][0]) {
66 
67 		    case 'n':
68 			nflag++;
69 			break;
70 
71 		    case 'u':
72 			uflag++;
73 			break;
74 
75 		    default:
76 			printf(usage);
77 			exit(1);
78 		}
79 		argc--;
80 		argv++;
81 	}
82 	if (argc > 2) {
83 		printf(usage);
84 		exit(1);
85 	}
86 	if (argc == 1)
87 		goto display;
88 
89 	if (getuid() != 0) {
90 		printf("You are not superuser: date not set\n");
91 		goto display;
92 	}
93 	username = getlogin();
94 	if (username == NULL)		/* single-user or no tty */
95 		username = "root";
96 
97 	ap = argv[1];
98 	wtmp[0].ut_time = tv.tv_sec;
99 	if (gtime()) {
100 		printf(usage);
101 		goto display;
102 	}
103 	/* convert to GMT assuming local time */
104 	if (uflag == 0) {
105 		tv.tv_sec += (long)tz.tz_minuteswest*60;
106 		/* now fix up local daylight time */
107 		if (localtime((time_t *)&tv.tv_sec)->tm_isdst)
108 			tv.tv_sec -= 60*60;
109 	}
110 	if (nflag || !settime(tv)) {
111 		int wf;
112 
113 		if (settimeofday(&tv, (struct timezone *)0) < 0) {
114 			perror("settimeofday");
115 			goto display;
116 		}
117 		if ((wf = open(WTMP, O_WRONLY|O_APPEND)) >= 0) {
118 			(void) time((time_t *)&wtmp[1].ut_time);
119 			(void) write(wf, (char *)wtmp, sizeof(wtmp));
120 			(void) close(wf);
121 		}
122 	}
123 	syslog(LOG_NOTICE, "set by %s", username);
124 
125 display:
126 	(void) gettimeofday(&tv, (struct timezone *)0);
127 	if (uflag) {
128 		ap = asctime(gmtime((time_t *)&tv.tv_sec));
129 		tzn = "GMT";
130 	} else {
131 		struct tm *tp;
132 		tp = localtime((time_t *)&tv.tv_sec);
133 		ap = asctime(tp);
134 		tzn = timezone(tz.tz_minuteswest, tp->tm_isdst);
135 	}
136 	printf("%.20s", ap);
137 	if (tzn)
138 		printf("%s", tzn);
139 	printf("%s", ap+19);
140 }
141 
142 gtime()
143 {
144 	register int i, year, month;
145 	int day, hour, mins, secs;
146 	struct tm *L;
147 	char x;
148 
149 	ep = ap;
150 	while(*ep) ep++;
151 	sp = ap;
152 	while(sp < ep) {
153 		x = *sp;
154 		*sp++ = *--ep;
155 		*ep = x;
156 	}
157 	sp = ap;
158 	(void) gettimeofday(&tv, (struct timezone *)0);
159 	L = localtime((time_t *)&tv.tv_sec);
160 	secs = gp(-1);
161 	if (*sp != '.') {
162 		mins = secs;
163 		secs = 0;
164 	} else {
165 		sp++;
166 		mins = gp(-1);
167 	}
168 	hour = gp(-1);
169 	day = gp(L->tm_mday);
170 	month = gp(L->tm_mon+1);
171 	year = gp(L->tm_year);
172 	if (*sp)
173 		return (1);
174 	if (month < 1 || month > 12 ||
175 	    day < 1 || day > 31 ||
176 	    mins < 0 || mins > 59 ||
177 	    secs < 0 || secs > 59)
178 		return (1);
179 	if (hour == 24) {
180 		hour = 0;
181 		day++;
182 	}
183 	if (hour < 0 || hour > 23)
184 		return (1);
185 	tv.tv_sec = 0;
186 	year += 1900;
187 	for (i = 1970; i < year; i++)
188 		tv.tv_sec += dysize(i);
189 	/* Leap year */
190 	if (dysize(year) == 366 && month >= 3)
191 		tv.tv_sec++;
192 	while (--month)
193 		tv.tv_sec += dmsize[month-1];
194 	tv.tv_sec += day-1;
195 	tv.tv_sec = 24*tv.tv_sec + hour;
196 	tv.tv_sec = 60*tv.tv_sec + mins;
197 	tv.tv_sec = 60*tv.tv_sec + secs;
198 	return (0);
199 }
200 
201 gp(dfault)
202 {
203 	register int c, d;
204 
205 	if (*sp == 0)
206 		return (dfault);
207 	c = (*sp++) - '0';
208 	d = (*sp ? (*sp++) - '0' : 0);
209 	if (c < 0 || c > 9 || d < 0 || d > 9)
210 		return (-1);
211 	return (c+10*d);
212 }
213 
214 #include <sys/socket.h>
215 #include <netinet/in.h>
216 #include <netdb.h>
217 #define TSPTYPES
218 #include <protocols/timed.h>
219 
220 #define WAITACK		2	/* seconds */
221 #define WAITDATEACK	5	/* seconds */
222 
223 extern	int errno;
224 /*
225  * Set the date in the machines controlled by timedaemons
226  * by communicating the new date to the local timedaemon.
227  * If the timedaemon is in the master state, it performs the
228  * correction on all slaves.  If it is in the slave state, it
229  * notifies the master that a correction is needed.
230  */
231 settime(tv)
232 	struct timeval tv;
233 {
234 	int s, length, port, timed_ack, found, err;
235 	long waittime;
236 	fd_set ready;
237 	char hostname[MAXHOSTNAMELEN];
238 	struct timeval tout;
239 	struct servent *sp;
240 	struct tsp msg;
241 	struct sockaddr_in sin, dest, from;
242 
243 	sp = getservbyname("timed", "udp");
244 	if (sp == 0) {
245 		fprintf(stderr, "udp/timed: unknown service\n");
246 		return (0);
247 	}
248 	dest.sin_port = sp->s_port;
249 	dest.sin_family = AF_INET;
250 	dest.sin_addr.s_addr = htonl((u_long)INADDR_ANY);
251 	s = socket(AF_INET, SOCK_DGRAM, 0);
252 	if (s < 0) {
253 		if (errno != EPROTONOSUPPORT)
254 			perror("date: socket");
255 		goto bad;
256 	}
257 	bzero(&sin, sizeof (sin));
258 	sin.sin_family = AF_INET;
259 	for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) {
260 		sin.sin_port = htons((u_short)port);
261 		if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) >= 0)
262 			break;
263 		if (errno != EADDRINUSE) {
264 			if (errno != EADDRNOTAVAIL)
265 				perror("date: bind");
266 			goto bad;
267 		}
268 	}
269 	if (port == IPPORT_RESERVED / 2) {
270 		fprintf(stderr, "date: All ports in use\n");
271 		goto bad;
272 	}
273 	msg.tsp_type = TSP_SETDATE;
274 	msg.tsp_vers = TSPVERSION;
275 	(void) gethostname(hostname, sizeof (hostname));
276 	(void) strncpy(msg.tsp_name, hostname, sizeof (hostname));
277 	msg.tsp_seq = htons((u_short)0);
278 	msg.tsp_time.tv_sec = htonl((u_long)tv.tv_sec);
279 	msg.tsp_time.tv_usec = htonl((u_long)tv.tv_usec);
280 	length = sizeof (struct sockaddr_in);
281 	if (connect(s, &dest, length) < 0) {
282 		perror("date: connect");
283 		goto bad;
284 	}
285 	if (send(s, &msg, sizeof (struct tsp), 0) < 0) {
286 		if (errno != ECONNREFUSED)
287 			perror("date: send");
288 		goto bad;
289 	}
290 	timed_ack = -1;
291 	waittime = WAITACK;
292 loop:
293 	tout.tv_sec = waittime;
294 	tout.tv_usec = 0;
295 	FD_ZERO(&ready);
296 	FD_SET(s, &ready);
297 	found = select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout);
298 	length = sizeof(err);
299 	if (getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &length) == 0 && err) {
300 		errno = err;
301 		if (errno != ECONNREFUSED)
302 			perror("date: send (delayed error)");
303 		goto bad;
304 	}
305 	if (found > 0 && FD_ISSET(s, &ready)) {
306 		length = sizeof (struct sockaddr_in);
307 		if (recvfrom(s, &msg, sizeof (struct tsp), 0, &from,
308 		    &length) < 0) {
309 			if (errno != ECONNREFUSED)
310 				perror("date: recvfrom");
311 			goto bad;
312 		}
313 		msg.tsp_seq = ntohs(msg.tsp_seq);
314 		msg.tsp_time.tv_sec = ntohl(msg.tsp_time.tv_sec);
315 		msg.tsp_time.tv_usec = ntohl(msg.tsp_time.tv_usec);
316 		switch (msg.tsp_type) {
317 
318 		case TSP_ACK:
319 			timed_ack = TSP_ACK;
320 			waittime = WAITDATEACK;
321 			goto loop;
322 
323 		case TSP_DATEACK:
324 			close(s);
325 			return (1);
326 
327 		default:
328 			fprintf(stderr,
329 			    "date: Wrong ack received from timed: %s\n",
330 			    tsptype[msg.tsp_type]);
331 			timed_ack = -1;
332 			break;
333 		}
334 	}
335 	if (timed_ack == -1)
336 		fprintf(stderr,
337 		    "date: Can't reach time daemon, time set locally.\n");
338 bad:
339 	close(s);
340 	return (0);
341 }
342 
343 timevalsub(t1, t2)
344 	register struct timeval *t1, *t2;
345 {
346 	t1->tv_sec -= t2->tv_sec;
347 	t1->tv_usec -= t2->tv_usec;
348 	if (t1->tv_usec < 0) {
349 		t1->tv_sec--;
350 		t1->tv_usec += 1000000;
351 	}
352 	if (t1->tv_usec >= 1000000) {
353 		t1->tv_sec++;
354 		t1->tv_usec -= 1000000;
355 	}
356 }
357