xref: /original-bsd/bin/date/date.c (revision 87febec0)
1 /*
2  * Copyright (c) 1985, 1987, 1988 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1985, 1987, 1988 The Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)date.c	4.25 (Berkeley) 03/14/89";
26 #endif /* not lint */
27 
28 /*
29  * Date - print and set date
30  */
31 
32 #include <sys/param.h>
33 #include <sys/time.h>
34 #include <sys/file.h>
35 #include <errno.h>
36 #include <syslog.h>
37 #include <utmp.h>
38 #include <tzfile.h>
39 #include <stdio.h>
40 #include <ctype.h>
41 #include <strings.h>
42 
43 #define	ATOI2(ar)	(ar[0] - '0') * 10 + (ar[1] - '0'); ar += 2;
44 
45 static struct timeval	tv;
46 static int	retval;
47 
48 static int	dmsize[] =
49 	{ -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
50 
51 main(argc, argv)
52 	int argc;
53 	char **argv;
54 {
55 	extern int optind;
56 	extern char *optarg;
57 	struct timezone tz;
58 	char *ap, *tzn;
59 	int ch, uflag, nflag;
60 	char *username, *getlogin();
61 	time_t time();
62 
63 	nflag = uflag = 0;
64 	tz.tz_dsttime = tz.tz_minuteswest = 0;
65 	while ((ch = getopt(argc, argv, "d:nut:")) != EOF)
66 		switch((char)ch) {
67 		case 'd':		/* daylight savings time */
68 			tz.tz_dsttime = atoi(optarg) ? 1 : 0;
69 			break;
70 		case 'n':		/* don't set network */
71 			nflag = 1;
72 			break;
73 		case 'u':		/* do it in GMT */
74 			uflag = 1;
75 			break;
76 		case 't':		/* minutes west of GMT */
77 					/* error check; we can't allow "PST" */
78 			if (isdigit(*optarg)) {
79 				tz.tz_minuteswest = atoi(optarg);
80 				break;
81 			}
82 			/*FALLTHROUGH*/
83 		default:
84 			usage();
85 			exit(1);
86 		}
87 	argc -= optind;
88 	argv += optind;
89 
90 	if (argc > 1) {
91 		usage();
92 		exit(1);
93 	}
94 
95 	if ((tz.tz_minuteswest || tz.tz_dsttime) &&
96 	    settimeofday((struct timeval *)NULL, &tz)) {
97 		perror("date: settimeofday");
98 		exit(1);
99 	}
100 
101 	if (gettimeofday(&tv, &tz)) {
102 		perror("date: gettimeofday");
103 		exit(1);
104 	}
105 
106 	if (!argc)
107 		goto display;
108 
109 	if (gtime(*argv)) {
110 		usage();
111 		exit(1);
112 	}
113 
114 	if (!uflag) {		/* convert to GMT assuming local time */
115 		tv.tv_sec += (long)tz.tz_minuteswest * SECSPERMIN;
116 				/* now fix up local daylight time */
117 		if (localtime((time_t *)&tv.tv_sec)->tm_isdst)
118 			tv.tv_sec -= SECSPERHOUR;
119 	}
120 	if (nflag || !netsettime(tv)) {
121 		logwtmp("|", "date", "");
122 		if (settimeofday(&tv, (struct timezone *)NULL)) {
123 			perror("date: settimeofday");
124 			exit(1);
125 		}
126 		logwtmp("{", "date", "");
127 	}
128 
129 	username = getlogin();
130 	if (!username || *username == '\0')	/* single-user or no tty */
131 		username = "root";
132 	syslog(LOG_AUTH | LOG_NOTICE, "date set by %s", username);
133 
134 display:
135 	if (gettimeofday(&tv, (struct timezone *)NULL)) {
136 		perror("date: gettimeofday");
137 		exit(1);
138 	}
139 	if (uflag) {
140 		ap = asctime(gmtime((time_t *)&tv.tv_sec));
141 		tzn = "GMT";
142 	}
143 	else {
144 		struct tm *tp;
145 
146 		tp = localtime((time_t *)&tv.tv_sec);
147 		ap = asctime(tp);
148 		tzn = tp->tm_zone;
149 	}
150 	printf("%.20s%s%s", ap, tzn, ap + 19);
151 	exit(retval);
152 }
153 
154 /*
155  * gtime --
156  *	convert user's time into number of seconds
157  */
158 static
159 gtime(ap)
160 	register char *ap;
161 {
162 	register int year, month;
163 	register char *C;
164 	struct tm *L;
165 	int day, hour, mins, secs;
166 
167 	for (secs = 0, C = ap; *C; ++C) {
168 		if (*C == '.') {		/* seconds provided */
169 			if (strlen(C) != 3)
170 				return(1);
171 			*C = NULL;
172 			secs = (C[1] - '0') * 10 + (C[2] - '0');
173 			break;
174 		}
175 		if (!isdigit(*C))
176 			return(-1);
177 	}
178 
179 	L = localtime((time_t *)&tv.tv_sec);
180 	year = L->tm_year;			/* defaults */
181 	month = L->tm_mon + 1;
182 	day = L->tm_mday;
183 
184 	switch ((int)(C - ap)) {		/* length */
185 		case 10:			/* yymmddhhmm */
186 			year = ATOI2(ap);
187 		case 8:				/* mmddhhmm */
188 			month = ATOI2(ap);
189 		case 6:				/* ddhhmm */
190 			day = ATOI2(ap);
191 		case 4:				/* hhmm */
192 			hour = ATOI2(ap);
193 			mins = ATOI2(ap);
194 			break;
195 		default:
196 			return(1);
197 	}
198 
199 	if (*ap || month < 1 || month > 12 || day < 1 || day > 31 ||
200 	     hour < 0 || hour > 23 || mins < 0 || mins > 59 ||
201 	     secs < 0 || secs > 59)
202 		return(1);
203 
204 	tv.tv_sec = 0;
205 	year += TM_YEAR_BASE;
206 	if (isleap(year) && month > 2)
207 		++tv.tv_sec;
208 	for (--year;year >= EPOCH_YEAR;--year)
209 		tv.tv_sec += isleap(year) ? DAYSPERLYEAR : DAYSPERNYEAR;
210 	while (--month)
211 		tv.tv_sec += dmsize[month];
212 	tv.tv_sec += day - 1;
213 	tv.tv_sec = HOURSPERDAY * tv.tv_sec + hour;
214 	tv.tv_sec = MINSPERHOUR * tv.tv_sec + mins;
215 	tv.tv_sec = SECSPERMIN * tv.tv_sec + secs;
216 	return(0);
217 }
218 
219 #include <sys/socket.h>
220 #include <netinet/in.h>
221 #include <netdb.h>
222 #define TSPTYPES
223 #include <protocols/timed.h>
224 
225 #define	WAITACK		2	/* seconds */
226 #define	WAITDATEACK	5	/* seconds */
227 
228 extern	int errno;
229 /*
230  * Set the date in the machines controlled by timedaemons
231  * by communicating the new date to the local timedaemon.
232  * If the timedaemon is in the master state, it performs the
233  * correction on all slaves.  If it is in the slave state, it
234  * notifies the master that a correction is needed.
235  * Returns 1 on success, 0 on failure.
236  */
237 netsettime(ntv)
238 	struct timeval ntv;
239 {
240 	int s, length, port, timed_ack, found, err;
241 	long waittime;
242 	fd_set ready;
243 	char hostname[MAXHOSTNAMELEN];
244 	struct timeval tout;
245 	struct servent *sp;
246 	struct tsp msg;
247 	struct sockaddr_in sin, dest, from;
248 
249 	sp = getservbyname("timed", "udp");
250 	if (sp == 0) {
251 		fputs("udp/timed: unknown service\n", stderr);
252 		retval = 2;
253 		return (0);
254 	}
255 	dest.sin_port = sp->s_port;
256 	dest.sin_family = AF_INET;
257 	dest.sin_addr.s_addr = htonl((u_long)INADDR_ANY);
258 	s = socket(AF_INET, SOCK_DGRAM, 0);
259 	if (s < 0) {
260 		if (errno != EPROTONOSUPPORT)
261 			perror("date: socket");
262 		goto bad;
263 	}
264 	bzero((char *)&sin, sizeof (sin));
265 	sin.sin_family = AF_INET;
266 	for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) {
267 		sin.sin_port = htons((u_short)port);
268 		if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) >= 0)
269 			break;
270 		if (errno != EADDRINUSE) {
271 			if (errno != EADDRNOTAVAIL)
272 				perror("date: bind");
273 			goto bad;
274 		}
275 	}
276 	if (port == IPPORT_RESERVED / 2) {
277 		fputs("date: All ports in use\n", stderr);
278 		goto bad;
279 	}
280 	msg.tsp_type = TSP_SETDATE;
281 	msg.tsp_vers = TSPVERSION;
282 	if (gethostname(hostname, sizeof (hostname))) {
283 		perror("date: gethostname");
284 		goto bad;
285 	}
286 	(void) strncpy(msg.tsp_name, hostname, sizeof (hostname));
287 	msg.tsp_seq = htons((u_short)0);
288 	msg.tsp_time.tv_sec = htonl((u_long)ntv.tv_sec);
289 	msg.tsp_time.tv_usec = htonl((u_long)ntv.tv_usec);
290 	length = sizeof (struct sockaddr_in);
291 	if (connect(s, &dest, length) < 0) {
292 		perror("date: connect");
293 		goto bad;
294 	}
295 	if (send(s, (char *)&msg, sizeof (struct tsp), 0) < 0) {
296 		if (errno != ECONNREFUSED)
297 			perror("date: send");
298 		goto bad;
299 	}
300 	timed_ack = -1;
301 	waittime = WAITACK;
302 loop:
303 	tout.tv_sec = waittime;
304 	tout.tv_usec = 0;
305 	FD_ZERO(&ready);
306 	FD_SET(s, &ready);
307 	found = select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout);
308 	length = sizeof(err);
309 	if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char *)&err, &length) == 0
310 	    && err) {
311 		errno = err;
312 		if (errno != ECONNREFUSED)
313 			perror("date: send (delayed error)");
314 		goto bad;
315 	}
316 	if (found > 0 && FD_ISSET(s, &ready)) {
317 		length = sizeof (struct sockaddr_in);
318 		if (recvfrom(s, (char *)&msg, sizeof (struct tsp), 0, &from,
319 		    &length) < 0) {
320 			if (errno != ECONNREFUSED)
321 				perror("date: recvfrom");
322 			goto bad;
323 		}
324 		msg.tsp_seq = ntohs(msg.tsp_seq);
325 		msg.tsp_time.tv_sec = ntohl(msg.tsp_time.tv_sec);
326 		msg.tsp_time.tv_usec = ntohl(msg.tsp_time.tv_usec);
327 		switch (msg.tsp_type) {
328 
329 		case TSP_ACK:
330 			timed_ack = TSP_ACK;
331 			waittime = WAITDATEACK;
332 			goto loop;
333 
334 		case TSP_DATEACK:
335 			(void)close(s);
336 			return (1);
337 
338 		default:
339 			fprintf(stderr,
340 			    "date: Wrong ack received from timed: %s\n",
341 			    tsptype[msg.tsp_type]);
342 			timed_ack = -1;
343 			break;
344 		}
345 	}
346 	if (timed_ack == -1)
347 		fputs("date: Can't reach time daemon, time set locally.\n",
348 		    stderr);
349 bad:
350 	(void)close(s);
351 	retval = 2;
352 	return (0);
353 }
354 
355 usage()
356 {
357 	fputs("usage: date [-nu] [-d dst] [-t minutes_west] [yymmddhhmm[.ss]]\n", stderr);
358 }
359