xref: /original-bsd/bin/date/date.c (revision fe3493ca)
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*fe3493caSbostic static char sccsid[] = "@(#)date.c	4.20 (Berkeley) 03/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>
27*fe3493caSbostic #include <tzfile.h>
28*fe3493caSbostic #include <stdio.h>
29*fe3493caSbostic #include <ctype.h>
30*fe3493caSbostic #include <strings.h>
318df64c17Sgusella 
32a3ca036fSleres #define	WTMP		"/usr/adm/wtmp"
33*fe3493caSbostic #define	ATOI2(ar)	(ar[0] - '0') * 10 + (ar[1] - '0'); ar += 2;
34ad4dcd8fSsam 
35*fe3493caSbostic static struct timeval	tv;
36*fe3493caSbostic static int	retval;
3763fd2155Sbill 
38*fe3493caSbostic static int	dmsize[] =
39*fe3493caSbostic 	{ -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
4063fd2155Sbill 
41*fe3493caSbostic static struct utmp	wtmp[2] = {
423ebd153dSsam 	{ "|", "", "", 0 },
433ebd153dSsam 	{ "{", "", "", 0 }
443ebd153dSsam };
4563fd2155Sbill 
4663fd2155Sbill main(argc,argv)
47ad4dcd8fSsam 	int	argc;
48*fe3493caSbostic 	char	**argv;
4963fd2155Sbill {
50*fe3493caSbostic 	extern int	optind;
51*fe3493caSbostic 	extern char	*optarg;
52*fe3493caSbostic 	static char	usage[] = "usage: date [-n] [-u] [yymmddhhmm[.ss]]\n";
53*fe3493caSbostic 	struct timezone	tz;
54*fe3493caSbostic 	char	*ap,			/* time string */
55*fe3493caSbostic 		*tzn;			/* time zone */
56*fe3493caSbostic 	int	ch,			/* getopts char */
57*fe3493caSbostic 		uflag,			/* do it in GMT */
58*fe3493caSbostic 		nflag,			/* only set time locally */
59*fe3493caSbostic 		wf;			/* wtmp file descriptor */
60*fe3493caSbostic 	long	time();
61*fe3493caSbostic 	uid_t	getuid();
62*fe3493caSbostic 	char	*username, *getlogin();
6363fd2155Sbill 
64b3f53864Seric 	openlog("date", LOG_ODELAY, LOG_AUTH);
658df64c17Sgusella 
66*fe3493caSbostic 	nflag = uflag = 0;
67*fe3493caSbostic 	while ((ch = getopt(argc,argv,"nu")) != EOF)
68*fe3493caSbostic 		switch((char)ch) {
69*fe3493caSbostic 		case 'd':
70*fe3493caSbostic 			tz.tz_dsttime = atoi(optarg) ? 1 : 0;
71*fe3493caSbostic 			break;
721a1d0a1fSkarels 		case 'n':
73*fe3493caSbostic 			nflag = 1;
741a1d0a1fSkarels 			break;
75*fe3493caSbostic 		case 't':
76*fe3493caSbostic 			tz.tz_minuteswest = atoi(optarg);
77*fe3493caSbostic 			break;
781a1d0a1fSkarels 		case 'u':
79*fe3493caSbostic 			uflag = 1;
801a1d0a1fSkarels 			break;
811a1d0a1fSkarels 		default:
82*fe3493caSbostic 			fputs(usage,stderr);
831a1d0a1fSkarels 			exit(1);
841a1d0a1fSkarels 		}
85*fe3493caSbostic 	argc -= optind;
86*fe3493caSbostic 	argv += optind;
87*fe3493caSbostic 
88*fe3493caSbostic 	if (argc > 1) {
89*fe3493caSbostic 		fputs(usage,stderr);
908df64c17Sgusella 		exit(1);
918df64c17Sgusella 	}
928df64c17Sgusella 
93*fe3493caSbostic 	if ((tz.tz_minuteswest || tz.tz_dsttime) &&
94*fe3493caSbostic 	    settimeofday((struct timeval *)NULL,&tz)) {
9536fa1a6bSkarels 		perror("settimeofday");
96a645e934Slepreau 		retval = 1;
9736fa1a6bSkarels 		goto display;
9836fa1a6bSkarels 	}
99*fe3493caSbostic 
100*fe3493caSbostic 	if (gettimeofday(&tv,&tz)) {
101*fe3493caSbostic 		perror("gettimeofday");
102*fe3493caSbostic 		exit(1);
103*fe3493caSbostic 	}
104*fe3493caSbostic 
105*fe3493caSbostic 	if (!argc)
106*fe3493caSbostic 		goto display;
107*fe3493caSbostic 
108*fe3493caSbostic 	if (getuid()) {
109*fe3493caSbostic 		fputs("You are not superuser: date not set.\n",stderr);
110*fe3493caSbostic 		retval = 1;
111*fe3493caSbostic 		goto display;
112*fe3493caSbostic 	}
113*fe3493caSbostic 
114*fe3493caSbostic 	wtmp[0].ut_time = tv.tv_sec;
115*fe3493caSbostic 	if (gtime(*argv)) {
116*fe3493caSbostic 		fprintf(stderr,usage);
117*fe3493caSbostic 		retval = 1;
118*fe3493caSbostic 		goto display;
119*fe3493caSbostic 	}
120*fe3493caSbostic 
121*fe3493caSbostic 	if (!uflag) {		/* convert to GMT assuming local time */
122*fe3493caSbostic 		tv.tv_sec += (long)tz.tz_minuteswest * SECS_PER_MIN;
123*fe3493caSbostic 				/* now fix up local daylight time */
124*fe3493caSbostic 		if (localtime((time_t *)&tv.tv_sec)->tm_isdst)
125*fe3493caSbostic 			tv.tv_sec -= SECS_PER_HOUR;
126*fe3493caSbostic 	}
127*fe3493caSbostic 	if (nflag || !netsettime(tv)) {
128*fe3493caSbostic 		if (settimeofday(&tv,(struct timezone *)0)) {
129*fe3493caSbostic 			perror("settimeofday");
130*fe3493caSbostic 			retval = 1;
131*fe3493caSbostic 			goto display;
132*fe3493caSbostic 		}
133*fe3493caSbostic 		if ((wf = open(WTMP,O_WRONLY|O_APPEND)) < 0)
134*fe3493caSbostic 			fputs("date: can't write wtmp file.\n",stderr);
135*fe3493caSbostic 		else {
13636fa1a6bSkarels 			(void)time((time_t *)&wtmp[1].ut_time);
137*fe3493caSbostic 			/*NOSTRICT*/
13836fa1a6bSkarels 			(void)write(wf,(char *)wtmp,sizeof(wtmp));
13936fa1a6bSkarels 			(void)close(wf);
14036fa1a6bSkarels 		}
1418df64c17Sgusella 	}
142*fe3493caSbostic 
143*fe3493caSbostic 	username = getlogin();
144*fe3493caSbostic 	if (!username || *username == '\0')	/* single-user or no tty */
145*fe3493caSbostic 		username = "root";
1461d873399Skarels 	syslog(LOG_NOTICE,"set by %s",username);
1478df64c17Sgusella 
1488df64c17Sgusella display:
149*fe3493caSbostic 	if (gettimeofday(&tv,(struct timezone *)0)) {
150*fe3493caSbostic 		perror("gettimeofday");
151*fe3493caSbostic 		exit(1);
152*fe3493caSbostic 	}
15363fd2155Sbill 	if (uflag) {
15436fa1a6bSkarels 		ap = asctime(gmtime((time_t *)&tv.tv_sec));
15563fd2155Sbill 		tzn = "GMT";
156*fe3493caSbostic 	}
157*fe3493caSbostic 	else {
15863fd2155Sbill 		struct tm	*tp;
159*fe3493caSbostic 
16036fa1a6bSkarels 		tp = localtime((time_t *)&tv.tv_sec);
16163fd2155Sbill 		ap = asctime(tp);
162*fe3493caSbostic 		tzn = tp->tm_zone;
16363fd2155Sbill 	}
164*fe3493caSbostic 	printf("%.20s%s%s",ap,tzn,ap + 19);
165a645e934Slepreau 	exit(retval);
16663fd2155Sbill }
16763fd2155Sbill 
168*fe3493caSbostic /*
169*fe3493caSbostic  * gtime --
170*fe3493caSbostic  *	convert user's time into number of seconds
171*fe3493caSbostic  */
172*fe3493caSbostic static
173*fe3493caSbostic gtime(ap)
174*fe3493caSbostic 	register char	*ap;		/* user argument */
17563fd2155Sbill {
176*fe3493caSbostic 	register int	year, month;
177*fe3493caSbostic 	register char	*C;		/* pointer into time argument */
17863fd2155Sbill 	struct tm	*L;
179*fe3493caSbostic 	int	day, hour, mins, secs;
18063fd2155Sbill 
181*fe3493caSbostic 	for (secs = 0, C = ap;*C;++C) {
182*fe3493caSbostic 		if (*C == '.') {		/* seconds provided */
183*fe3493caSbostic 			if (strlen(C) != 3)
18463fd2155Sbill 				return(1);
185*fe3493caSbostic 			*C = NULL;
186*fe3493caSbostic 			secs = (C[1] - '0') * 10 + (C[2] - '0');
187*fe3493caSbostic 			break;
188*fe3493caSbostic 		}
189*fe3493caSbostic 		if (!isdigit(*C))
190*fe3493caSbostic 			return(-1);
191*fe3493caSbostic 	}
192*fe3493caSbostic 
193*fe3493caSbostic 	L = localtime((time_t *)&tv.tv_sec);
194*fe3493caSbostic 	year = L->tm_year;			/* defaults */
195*fe3493caSbostic 	month = L->tm_mon + 1;
196*fe3493caSbostic 	day = L->tm_mday;
197*fe3493caSbostic 
198*fe3493caSbostic 	switch ((int)(C - ap)) {		/* length */
199*fe3493caSbostic 		case 10:			/* yymmddhhmm */
200*fe3493caSbostic 			year = ATOI2(ap);
201*fe3493caSbostic 		case 8:				/* mmddhhmm */
202*fe3493caSbostic 			month = ATOI2(ap);
203*fe3493caSbostic 		case 6:				/* ddhhmm */
204*fe3493caSbostic 			day = ATOI2(ap);
205*fe3493caSbostic 		case 4:				/* hhmm */
206*fe3493caSbostic 			hour = ATOI2(ap);
207*fe3493caSbostic 			mins = ATOI2(ap);
208*fe3493caSbostic 			break;
209*fe3493caSbostic 		default:
210*fe3493caSbostic 			return(1);
211*fe3493caSbostic 	}
212*fe3493caSbostic 
213*fe3493caSbostic 	if (*ap || month < 1 || month > 12 || day < 1 || day > 31 ||
214*fe3493caSbostic 	     mins < 0 || mins > 59 || secs < 0 || secs > 59)
21563fd2155Sbill 		return(1);
21663fd2155Sbill 	if (hour == 24) {
217*fe3493caSbostic 		++day;
218a3ca036fSleres 		hour = 0;
21963fd2155Sbill 	}
220*fe3493caSbostic 	else if (hour < 0 || hour > 23)
22163fd2155Sbill 		return(1);
222*fe3493caSbostic 
223ad4dcd8fSsam 	tv.tv_sec = 0;
224*fe3493caSbostic 	year += TM_YEAR_BASE;
225*fe3493caSbostic 	if (isleap(year) && month > 2)
226*fe3493caSbostic 		++tv.tv_sec;
227*fe3493caSbostic 	for (--year;year >= EPOCH_YEAR;--year)
228*fe3493caSbostic 		tv.tv_sec += isleap(year) ? DAYS_PER_LYEAR : DAYS_PER_NYEAR;
22963fd2155Sbill 	while (--month)
230*fe3493caSbostic 		tv.tv_sec += dmsize[month];
231ad4dcd8fSsam 	tv.tv_sec += day - 1;
232*fe3493caSbostic 	tv.tv_sec = HOURS_PER_DAY * tv.tv_sec + hour;
233*fe3493caSbostic 	tv.tv_sec = MINS_PER_HOUR * tv.tv_sec + mins;
234*fe3493caSbostic 	tv.tv_sec = SECS_PER_MIN * tv.tv_sec + secs;
23563fd2155Sbill 	return(0);
23663fd2155Sbill }
23763fd2155Sbill 
2381d873399Skarels #include <sys/socket.h>
2391d873399Skarels #include <netinet/in.h>
2401d873399Skarels #include <netdb.h>
2411d873399Skarels #define TSPTYPES
2421d873399Skarels #include <protocols/timed.h>
2431d873399Skarels 
2441d873399Skarels #define WAITACK		2	/* seconds */
2451d873399Skarels #define WAITDATEACK	5	/* seconds */
2461d873399Skarels 
2471d873399Skarels extern	int errno;
2481d873399Skarels /*
2491d873399Skarels  * Set the date in the machines controlled by timedaemons
2501d873399Skarels  * by communicating the new date to the local timedaemon.
2511d873399Skarels  * If the timedaemon is in the master state, it performs the
2521d873399Skarels  * correction on all slaves.  If it is in the slave state, it
2531d873399Skarels  * notifies the master that a correction is needed.
254a645e934Slepreau  * Returns 1 on success, 0 on failure.
2551d873399Skarels  */
256*fe3493caSbostic static
257*fe3493caSbostic netsettime(ntv)
258*fe3493caSbostic 	struct timeval ntv;
2591d873399Skarels {
2601d873399Skarels 	int s, length, port, timed_ack, found, err;
2611d873399Skarels 	long waittime;
2621d873399Skarels 	fd_set ready;
2631d873399Skarels 	char hostname[MAXHOSTNAMELEN];
2641d873399Skarels 	struct timeval tout;
2651d873399Skarels 	struct servent *sp;
2661d873399Skarels 	struct tsp msg;
2671d873399Skarels 	struct sockaddr_in sin, dest, from;
2681d873399Skarels 
2691d873399Skarels 	sp = getservbyname("timed", "udp");
2701d873399Skarels 	if (sp == 0) {
271*fe3493caSbostic 		fputs("udp/timed: unknown service\n",stderr);
272a645e934Slepreau 		retval = 2;
2731d873399Skarels 		return (0);
2741d873399Skarels 	}
2751d873399Skarels 	dest.sin_port = sp->s_port;
2761d873399Skarels 	dest.sin_family = AF_INET;
2771d873399Skarels 	dest.sin_addr.s_addr = htonl((u_long)INADDR_ANY);
2781d873399Skarels 	s = socket(AF_INET, SOCK_DGRAM, 0);
2791d873399Skarels 	if (s < 0) {
2801d873399Skarels 		if (errno != EPROTONOSUPPORT)
2811d873399Skarels 			perror("date: socket");
2821d873399Skarels 		goto bad;
2831d873399Skarels 	}
2843416d647Slepreau 	bzero((char *)&sin, sizeof (sin));
2851d873399Skarels 	sin.sin_family = AF_INET;
2861d873399Skarels 	for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) {
2871d873399Skarels 		sin.sin_port = htons((u_short)port);
2881d873399Skarels 		if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) >= 0)
2891d873399Skarels 			break;
2901d873399Skarels 		if (errno != EADDRINUSE) {
2911d873399Skarels 			if (errno != EADDRNOTAVAIL)
2921d873399Skarels 				perror("date: bind");
2931d873399Skarels 			goto bad;
2941d873399Skarels 		}
2951d873399Skarels 	}
2961d873399Skarels 	if (port == IPPORT_RESERVED / 2) {
297*fe3493caSbostic 		fputs("date: All ports in use\n",stderr);
2981d873399Skarels 		goto bad;
2991d873399Skarels 	}
300c37c46dcSbloom 	msg.tsp_type = TSP_SETDATE;
3011d873399Skarels 	msg.tsp_vers = TSPVERSION;
302*fe3493caSbostic 	if (gethostname(hostname, sizeof (hostname))) {
303*fe3493caSbostic 		perror("gethostname");
304*fe3493caSbostic 		goto bad;
305*fe3493caSbostic 	}
3061d873399Skarels 	(void) strncpy(msg.tsp_name, hostname, sizeof (hostname));
3071d873399Skarels 	msg.tsp_seq = htons((u_short)0);
308*fe3493caSbostic 	msg.tsp_time.tv_sec = htonl((u_long)ntv.tv_sec);
309*fe3493caSbostic 	msg.tsp_time.tv_usec = htonl((u_long)ntv.tv_usec);
3101d873399Skarels 	length = sizeof (struct sockaddr_in);
3111d873399Skarels 	if (connect(s, &dest, length) < 0) {
3121d873399Skarels 		perror("date: connect");
3131d873399Skarels 		goto bad;
3141d873399Skarels 	}
3153416d647Slepreau 	if (send(s, (char *)&msg, sizeof (struct tsp), 0) < 0) {
3161d873399Skarels 		if (errno != ECONNREFUSED)
3171d873399Skarels 			perror("date: send");
3181d873399Skarels 		goto bad;
3191d873399Skarels 	}
3201d873399Skarels 	timed_ack = -1;
3211d873399Skarels 	waittime = WAITACK;
3221d873399Skarels loop:
3231d873399Skarels 	tout.tv_sec = waittime;
3241d873399Skarels 	tout.tv_usec = 0;
3251d873399Skarels 	FD_ZERO(&ready);
3261d873399Skarels 	FD_SET(s, &ready);
3271d873399Skarels 	found = select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout);
3281d873399Skarels 	length = sizeof(err);
3293416d647Slepreau 	if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char *)&err, &length) == 0
3303416d647Slepreau 	    && err) {
3311d873399Skarels 		errno = err;
3321d873399Skarels 		if (errno != ECONNREFUSED)
3331d873399Skarels 			perror("date: send (delayed error)");
3341d873399Skarels 		goto bad;
3351d873399Skarels 	}
3361d873399Skarels 	if (found > 0 && FD_ISSET(s, &ready)) {
3371d873399Skarels 		length = sizeof (struct sockaddr_in);
3383416d647Slepreau 		if (recvfrom(s, (char *)&msg, sizeof (struct tsp), 0, &from,
3391d873399Skarels 		    &length) < 0) {
3401d873399Skarels 			if (errno != ECONNREFUSED)
3411d873399Skarels 				perror("date: recvfrom");
3421d873399Skarels 			goto bad;
3431d873399Skarels 		}
3441d873399Skarels 		msg.tsp_seq = ntohs(msg.tsp_seq);
3451d873399Skarels 		msg.tsp_time.tv_sec = ntohl(msg.tsp_time.tv_sec);
3461d873399Skarels 		msg.tsp_time.tv_usec = ntohl(msg.tsp_time.tv_usec);
3471d873399Skarels 		switch (msg.tsp_type) {
3481d873399Skarels 
3491d873399Skarels 		case TSP_ACK:
3501d873399Skarels 			timed_ack = TSP_ACK;
3511d873399Skarels 			waittime = WAITDATEACK;
3521d873399Skarels 			goto loop;
3531d873399Skarels 
3541d873399Skarels 		case TSP_DATEACK:
3553416d647Slepreau 			(void)close(s);
3561d873399Skarels 			return (1);
3571d873399Skarels 
3581d873399Skarels 		default:
3591d873399Skarels 			fprintf(stderr,
3601d873399Skarels 			    "date: Wrong ack received from timed: %s\n",
3611d873399Skarels 			    tsptype[msg.tsp_type]);
3621d873399Skarels 			timed_ack = -1;
3631d873399Skarels 			break;
3641d873399Skarels 		}
3651d873399Skarels 	}
3661d873399Skarels 	if (timed_ack == -1)
367*fe3493caSbostic 		fputs("date: Can't reach time daemon, time set locally.\n",
368*fe3493caSbostic 		    stderr);
3691d873399Skarels bad:
3703416d647Slepreau 	(void)close(s);
371a645e934Slepreau 	retval = 2;
3721d873399Skarels 	return (0);
3731d873399Skarels }
374