xref: /original-bsd/bin/date/date.c (revision 33de07be)
1e8f3668aSmckusick /*
2e8f3668aSmckusick  * Copyright (c) 1985 Regents of the University of California.
3e8f3668aSmckusick  * All rights reserved.  The Berkeley software License Agreement
4e8f3668aSmckusick  * specifies the terms and conditions for redistribution.
5e8f3668aSmckusick  */
6e8f3668aSmckusick 
7ad4dcd8fSsam #ifndef lint
8e8f3668aSmckusick char copyright[] =
9e8f3668aSmckusick "@(#) Copyright (c) 1985 Regents of the University of California.\n\
10e8f3668aSmckusick  All rights reserved.\n";
11e8f3668aSmckusick #endif not lint
12e8f3668aSmckusick 
13e8f3668aSmckusick #ifndef lint
14*33de07beSbostic static char sccsid[] = "@(#)date.c	4.20 (Berkeley) 3/24/87";
158df64c17Sgusella #endif not lint
16ad4dcd8fSsam 
1763fd2155Sbill /*
18a3ca036fSleres  * Date - print and set date
1963fd2155Sbill  */
20a3ca036fSleres 
2136fa1a6bSkarels #include <sys/param.h>
2229f671e3Swnj #include <sys/time.h>
238df64c17Sgusella #include <sys/file.h>
248df64c17Sgusella #include <errno.h>
258df64c17Sgusella #include <syslog.h>
2663fd2155Sbill #include <utmp.h>
27fe3493caSbostic #include <tzfile.h>
28fe3493caSbostic #include <stdio.h>
29fe3493caSbostic #include <ctype.h>
30fe3493caSbostic #include <strings.h>
318df64c17Sgusella 
32a3ca036fSleres #define	WTMP		"/usr/adm/wtmp"
33fe3493caSbostic #define	ATOI2(ar)	(ar[0] - '0') * 10 + (ar[1] - '0'); ar += 2;
34ad4dcd8fSsam 
35fe3493caSbostic static struct timeval	tv;
36fe3493caSbostic static int	retval;
3763fd2155Sbill 
38fe3493caSbostic static int	dmsize[] =
39fe3493caSbostic 	{ -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
4063fd2155Sbill 
41fe3493caSbostic static struct utmp	wtmp[2] = {
423ebd153dSsam 	{ "|", "", "", 0 },
433ebd153dSsam 	{ "{", "", "", 0 }
443ebd153dSsam };
4563fd2155Sbill 
4663fd2155Sbill main(argc,argv)
47ad4dcd8fSsam 	int	argc;
48fe3493caSbostic 	char	**argv;
4963fd2155Sbill {
50fe3493caSbostic 	extern int	optind;
51fe3493caSbostic 	extern char	*optarg;
52fe3493caSbostic 	struct timezone	tz;
53fe3493caSbostic 	char	*ap,			/* time string */
54fe3493caSbostic 		*tzn;			/* time zone */
55fe3493caSbostic 	int	ch,			/* getopts char */
56fe3493caSbostic 		uflag,			/* do it in GMT */
57fe3493caSbostic 		nflag,			/* only set time locally */
58fe3493caSbostic 		wf;			/* wtmp file descriptor */
59fe3493caSbostic 	long	time();
60fe3493caSbostic 	uid_t	getuid();
61fe3493caSbostic 	char	*username, *getlogin();
6263fd2155Sbill 
63fe3493caSbostic 	nflag = uflag = 0;
64*33de07beSbostic 	tz.tz_dsttime = tz.tz_minuteswest = 0;
65*33de07beSbostic 	while ((ch = getopt(argc,argv,"d:nut:")) != EOF)
66fe3493caSbostic 		switch((char)ch) {
67fe3493caSbostic 		case 'd':
68fe3493caSbostic 			tz.tz_dsttime = atoi(optarg) ? 1 : 0;
69fe3493caSbostic 			break;
701a1d0a1fSkarels 		case 'n':
71fe3493caSbostic 			nflag = 1;
721a1d0a1fSkarels 			break;
731a1d0a1fSkarels 		case 'u':
74fe3493caSbostic 			uflag = 1;
751a1d0a1fSkarels 			break;
76*33de07beSbostic 		case 't':	/* error check; we can't allow "PST" */
77*33de07beSbostic 			if (isdigit(*optarg)) {
78*33de07beSbostic 				tz.tz_minuteswest = atoi(optarg);
79*33de07beSbostic 				break;
80*33de07beSbostic 			}
81*33de07beSbostic 			/*FALLTHROUGH*/
821a1d0a1fSkarels 		default:
83*33de07beSbostic 			usage();
841a1d0a1fSkarels 			exit(1);
851a1d0a1fSkarels 		}
86fe3493caSbostic 	argc -= optind;
87fe3493caSbostic 	argv += optind;
88fe3493caSbostic 
89fe3493caSbostic 	if (argc > 1) {
90*33de07beSbostic 		usage();
918df64c17Sgusella 		exit(1);
928df64c17Sgusella 	}
938df64c17Sgusella 
94fe3493caSbostic 	if ((tz.tz_minuteswest || tz.tz_dsttime) &&
95fe3493caSbostic 	    settimeofday((struct timeval *)NULL,&tz)) {
9636fa1a6bSkarels 		perror("settimeofday");
97a645e934Slepreau 		retval = 1;
9836fa1a6bSkarels 		goto display;
9936fa1a6bSkarels 	}
100fe3493caSbostic 
101fe3493caSbostic 	if (gettimeofday(&tv,&tz)) {
102fe3493caSbostic 		perror("gettimeofday");
103fe3493caSbostic 		exit(1);
104fe3493caSbostic 	}
105fe3493caSbostic 
106fe3493caSbostic 	if (!argc)
107fe3493caSbostic 		goto display;
108fe3493caSbostic 
109fe3493caSbostic 	wtmp[0].ut_time = tv.tv_sec;
110fe3493caSbostic 	if (gtime(*argv)) {
111*33de07beSbostic 		usage();
112fe3493caSbostic 		retval = 1;
113fe3493caSbostic 		goto display;
114fe3493caSbostic 	}
115fe3493caSbostic 
116fe3493caSbostic 	if (!uflag) {		/* convert to GMT assuming local time */
117fe3493caSbostic 		tv.tv_sec += (long)tz.tz_minuteswest * SECS_PER_MIN;
118fe3493caSbostic 				/* now fix up local daylight time */
119fe3493caSbostic 		if (localtime((time_t *)&tv.tv_sec)->tm_isdst)
120fe3493caSbostic 			tv.tv_sec -= SECS_PER_HOUR;
121fe3493caSbostic 	}
122fe3493caSbostic 	if (nflag || !netsettime(tv)) {
123fe3493caSbostic 		if (settimeofday(&tv,(struct timezone *)0)) {
124fe3493caSbostic 			perror("settimeofday");
125fe3493caSbostic 			retval = 1;
126fe3493caSbostic 			goto display;
127fe3493caSbostic 		}
128fe3493caSbostic 		if ((wf = open(WTMP,O_WRONLY|O_APPEND)) < 0)
129fe3493caSbostic 			fputs("date: can't write wtmp file.\n",stderr);
130fe3493caSbostic 		else {
13136fa1a6bSkarels 			(void)time((time_t *)&wtmp[1].ut_time);
132fe3493caSbostic 			/*NOSTRICT*/
13336fa1a6bSkarels 			(void)write(wf,(char *)wtmp,sizeof(wtmp));
13436fa1a6bSkarels 			(void)close(wf);
13536fa1a6bSkarels 		}
1368df64c17Sgusella 	}
137fe3493caSbostic 
138fe3493caSbostic 	username = getlogin();
139fe3493caSbostic 	if (!username || *username == '\0')	/* single-user or no tty */
140fe3493caSbostic 		username = "root";
141*33de07beSbostic 	syslog(LOG_AUTH | LOG_NOTICE,"date set by %s",username);
1428df64c17Sgusella 
1438df64c17Sgusella display:
144fe3493caSbostic 	if (gettimeofday(&tv,(struct timezone *)0)) {
145fe3493caSbostic 		perror("gettimeofday");
146fe3493caSbostic 		exit(1);
147fe3493caSbostic 	}
14863fd2155Sbill 	if (uflag) {
14936fa1a6bSkarels 		ap = asctime(gmtime((time_t *)&tv.tv_sec));
15063fd2155Sbill 		tzn = "GMT";
151fe3493caSbostic 	}
152fe3493caSbostic 	else {
15363fd2155Sbill 		struct tm	*tp;
154fe3493caSbostic 
15536fa1a6bSkarels 		tp = localtime((time_t *)&tv.tv_sec);
15663fd2155Sbill 		ap = asctime(tp);
157fe3493caSbostic 		tzn = tp->tm_zone;
15863fd2155Sbill 	}
159fe3493caSbostic 	printf("%.20s%s%s",ap,tzn,ap + 19);
160a645e934Slepreau 	exit(retval);
16163fd2155Sbill }
16263fd2155Sbill 
163fe3493caSbostic /*
164fe3493caSbostic  * gtime --
165fe3493caSbostic  *	convert user's time into number of seconds
166fe3493caSbostic  */
167fe3493caSbostic static
168fe3493caSbostic gtime(ap)
169fe3493caSbostic 	register char	*ap;		/* user argument */
17063fd2155Sbill {
171fe3493caSbostic 	register int	year, month;
172fe3493caSbostic 	register char	*C;		/* pointer into time argument */
17363fd2155Sbill 	struct tm	*L;
174fe3493caSbostic 	int	day, hour, mins, secs;
17563fd2155Sbill 
176fe3493caSbostic 	for (secs = 0, C = ap;*C;++C) {
177fe3493caSbostic 		if (*C == '.') {		/* seconds provided */
178fe3493caSbostic 			if (strlen(C) != 3)
17963fd2155Sbill 				return(1);
180fe3493caSbostic 			*C = NULL;
181fe3493caSbostic 			secs = (C[1] - '0') * 10 + (C[2] - '0');
182fe3493caSbostic 			break;
183fe3493caSbostic 		}
184fe3493caSbostic 		if (!isdigit(*C))
185fe3493caSbostic 			return(-1);
186fe3493caSbostic 	}
187fe3493caSbostic 
188fe3493caSbostic 	L = localtime((time_t *)&tv.tv_sec);
189fe3493caSbostic 	year = L->tm_year;			/* defaults */
190fe3493caSbostic 	month = L->tm_mon + 1;
191fe3493caSbostic 	day = L->tm_mday;
192fe3493caSbostic 
193fe3493caSbostic 	switch ((int)(C - ap)) {		/* length */
194fe3493caSbostic 		case 10:			/* yymmddhhmm */
195fe3493caSbostic 			year = ATOI2(ap);
196fe3493caSbostic 		case 8:				/* mmddhhmm */
197fe3493caSbostic 			month = ATOI2(ap);
198fe3493caSbostic 		case 6:				/* ddhhmm */
199fe3493caSbostic 			day = ATOI2(ap);
200fe3493caSbostic 		case 4:				/* hhmm */
201fe3493caSbostic 			hour = ATOI2(ap);
202fe3493caSbostic 			mins = ATOI2(ap);
203fe3493caSbostic 			break;
204fe3493caSbostic 		default:
205fe3493caSbostic 			return(1);
206fe3493caSbostic 	}
207fe3493caSbostic 
208fe3493caSbostic 	if (*ap || month < 1 || month > 12 || day < 1 || day > 31 ||
209fe3493caSbostic 	     mins < 0 || mins > 59 || secs < 0 || secs > 59)
21063fd2155Sbill 		return(1);
21163fd2155Sbill 	if (hour == 24) {
212fe3493caSbostic 		++day;
213a3ca036fSleres 		hour = 0;
21463fd2155Sbill 	}
215fe3493caSbostic 	else if (hour < 0 || hour > 23)
21663fd2155Sbill 		return(1);
217fe3493caSbostic 
218ad4dcd8fSsam 	tv.tv_sec = 0;
219fe3493caSbostic 	year += TM_YEAR_BASE;
220fe3493caSbostic 	if (isleap(year) && month > 2)
221fe3493caSbostic 		++tv.tv_sec;
222fe3493caSbostic 	for (--year;year >= EPOCH_YEAR;--year)
223fe3493caSbostic 		tv.tv_sec += isleap(year) ? DAYS_PER_LYEAR : DAYS_PER_NYEAR;
22463fd2155Sbill 	while (--month)
225fe3493caSbostic 		tv.tv_sec += dmsize[month];
226ad4dcd8fSsam 	tv.tv_sec += day - 1;
227fe3493caSbostic 	tv.tv_sec = HOURS_PER_DAY * tv.tv_sec + hour;
228fe3493caSbostic 	tv.tv_sec = MINS_PER_HOUR * tv.tv_sec + mins;
229fe3493caSbostic 	tv.tv_sec = SECS_PER_MIN * tv.tv_sec + secs;
23063fd2155Sbill 	return(0);
23163fd2155Sbill }
23263fd2155Sbill 
2331d873399Skarels #include <sys/socket.h>
2341d873399Skarels #include <netinet/in.h>
2351d873399Skarels #include <netdb.h>
2361d873399Skarels #define TSPTYPES
2371d873399Skarels #include <protocols/timed.h>
2381d873399Skarels 
2391d873399Skarels #define WAITACK		2	/* seconds */
2401d873399Skarels #define WAITDATEACK	5	/* seconds */
2411d873399Skarels 
2421d873399Skarels extern	int errno;
2431d873399Skarels /*
2441d873399Skarels  * Set the date in the machines controlled by timedaemons
2451d873399Skarels  * by communicating the new date to the local timedaemon.
2461d873399Skarels  * If the timedaemon is in the master state, it performs the
2471d873399Skarels  * correction on all slaves.  If it is in the slave state, it
2481d873399Skarels  * notifies the master that a correction is needed.
249a645e934Slepreau  * Returns 1 on success, 0 on failure.
2501d873399Skarels  */
251fe3493caSbostic static
252fe3493caSbostic netsettime(ntv)
253fe3493caSbostic 	struct timeval ntv;
2541d873399Skarels {
2551d873399Skarels 	int s, length, port, timed_ack, found, err;
2561d873399Skarels 	long waittime;
2571d873399Skarels 	fd_set ready;
2581d873399Skarels 	char hostname[MAXHOSTNAMELEN];
2591d873399Skarels 	struct timeval tout;
2601d873399Skarels 	struct servent *sp;
2611d873399Skarels 	struct tsp msg;
2621d873399Skarels 	struct sockaddr_in sin, dest, from;
2631d873399Skarels 
2641d873399Skarels 	sp = getservbyname("timed", "udp");
2651d873399Skarels 	if (sp == 0) {
266fe3493caSbostic 		fputs("udp/timed: unknown service\n",stderr);
267a645e934Slepreau 		retval = 2;
2681d873399Skarels 		return (0);
2691d873399Skarels 	}
2701d873399Skarels 	dest.sin_port = sp->s_port;
2711d873399Skarels 	dest.sin_family = AF_INET;
2721d873399Skarels 	dest.sin_addr.s_addr = htonl((u_long)INADDR_ANY);
2731d873399Skarels 	s = socket(AF_INET, SOCK_DGRAM, 0);
2741d873399Skarels 	if (s < 0) {
2751d873399Skarels 		if (errno != EPROTONOSUPPORT)
2761d873399Skarels 			perror("date: socket");
2771d873399Skarels 		goto bad;
2781d873399Skarels 	}
2793416d647Slepreau 	bzero((char *)&sin, sizeof (sin));
2801d873399Skarels 	sin.sin_family = AF_INET;
2811d873399Skarels 	for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) {
2821d873399Skarels 		sin.sin_port = htons((u_short)port);
2831d873399Skarels 		if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) >= 0)
2841d873399Skarels 			break;
2851d873399Skarels 		if (errno != EADDRINUSE) {
2861d873399Skarels 			if (errno != EADDRNOTAVAIL)
2871d873399Skarels 				perror("date: bind");
2881d873399Skarels 			goto bad;
2891d873399Skarels 		}
2901d873399Skarels 	}
2911d873399Skarels 	if (port == IPPORT_RESERVED / 2) {
292fe3493caSbostic 		fputs("date: All ports in use\n",stderr);
2931d873399Skarels 		goto bad;
2941d873399Skarels 	}
295c37c46dcSbloom 	msg.tsp_type = TSP_SETDATE;
2961d873399Skarels 	msg.tsp_vers = TSPVERSION;
297fe3493caSbostic 	if (gethostname(hostname, sizeof (hostname))) {
298fe3493caSbostic 		perror("gethostname");
299fe3493caSbostic 		goto bad;
300fe3493caSbostic 	}
3011d873399Skarels 	(void) strncpy(msg.tsp_name, hostname, sizeof (hostname));
3021d873399Skarels 	msg.tsp_seq = htons((u_short)0);
303fe3493caSbostic 	msg.tsp_time.tv_sec = htonl((u_long)ntv.tv_sec);
304fe3493caSbostic 	msg.tsp_time.tv_usec = htonl((u_long)ntv.tv_usec);
3051d873399Skarels 	length = sizeof (struct sockaddr_in);
3061d873399Skarels 	if (connect(s, &dest, length) < 0) {
3071d873399Skarels 		perror("date: connect");
3081d873399Skarels 		goto bad;
3091d873399Skarels 	}
3103416d647Slepreau 	if (send(s, (char *)&msg, sizeof (struct tsp), 0) < 0) {
3111d873399Skarels 		if (errno != ECONNREFUSED)
3121d873399Skarels 			perror("date: send");
3131d873399Skarels 		goto bad;
3141d873399Skarels 	}
3151d873399Skarels 	timed_ack = -1;
3161d873399Skarels 	waittime = WAITACK;
3171d873399Skarels loop:
3181d873399Skarels 	tout.tv_sec = waittime;
3191d873399Skarels 	tout.tv_usec = 0;
3201d873399Skarels 	FD_ZERO(&ready);
3211d873399Skarels 	FD_SET(s, &ready);
3221d873399Skarels 	found = select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout);
3231d873399Skarels 	length = sizeof(err);
3243416d647Slepreau 	if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char *)&err, &length) == 0
3253416d647Slepreau 	    && err) {
3261d873399Skarels 		errno = err;
3271d873399Skarels 		if (errno != ECONNREFUSED)
3281d873399Skarels 			perror("date: send (delayed error)");
3291d873399Skarels 		goto bad;
3301d873399Skarels 	}
3311d873399Skarels 	if (found > 0 && FD_ISSET(s, &ready)) {
3321d873399Skarels 		length = sizeof (struct sockaddr_in);
3333416d647Slepreau 		if (recvfrom(s, (char *)&msg, sizeof (struct tsp), 0, &from,
3341d873399Skarels 		    &length) < 0) {
3351d873399Skarels 			if (errno != ECONNREFUSED)
3361d873399Skarels 				perror("date: recvfrom");
3371d873399Skarels 			goto bad;
3381d873399Skarels 		}
3391d873399Skarels 		msg.tsp_seq = ntohs(msg.tsp_seq);
3401d873399Skarels 		msg.tsp_time.tv_sec = ntohl(msg.tsp_time.tv_sec);
3411d873399Skarels 		msg.tsp_time.tv_usec = ntohl(msg.tsp_time.tv_usec);
3421d873399Skarels 		switch (msg.tsp_type) {
3431d873399Skarels 
3441d873399Skarels 		case TSP_ACK:
3451d873399Skarels 			timed_ack = TSP_ACK;
3461d873399Skarels 			waittime = WAITDATEACK;
3471d873399Skarels 			goto loop;
3481d873399Skarels 
3491d873399Skarels 		case TSP_DATEACK:
3503416d647Slepreau 			(void)close(s);
3511d873399Skarels 			return (1);
3521d873399Skarels 
3531d873399Skarels 		default:
3541d873399Skarels 			fprintf(stderr,
3551d873399Skarels 			    "date: Wrong ack received from timed: %s\n",
3561d873399Skarels 			    tsptype[msg.tsp_type]);
3571d873399Skarels 			timed_ack = -1;
3581d873399Skarels 			break;
3591d873399Skarels 		}
3601d873399Skarels 	}
3611d873399Skarels 	if (timed_ack == -1)
362fe3493caSbostic 		fputs("date: Can't reach time daemon, time set locally.\n",
363fe3493caSbostic 		    stderr);
3641d873399Skarels bad:
3653416d647Slepreau 	(void)close(s);
366a645e934Slepreau 	retval = 2;
3671d873399Skarels 	return (0);
3681d873399Skarels }
369*33de07beSbostic 
370*33de07beSbostic usage()
371*33de07beSbostic {
372*33de07beSbostic 	fputs("usage: date [-nu] [-d dst] [-t minutes_west] [yymmddhhmm[.ss]]\n",stderr);
373*33de07beSbostic }
374