1/*	$OpenBSD: date.c,v 1.60 2024/04/28 16:43:15 florian Exp $	*/
2/*	$NetBSD: date.c,v 1.11 1995/09/07 06:21:05 jtc Exp $	*/
3
4/*
5 * Copyright (c) 1985, 1987, 1988, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/types.h>
34#include <sys/time.h>
35
36#include <ctype.h>
37#include <err.h>
38#include <fcntl.h>
39#include <limits.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <syslog.h>
44#include <time.h>
45#include <unistd.h>
46#include <util.h>
47
48extern	char *__progname;
49
50time_t tval;
51int jflag;
52int slidetime;
53
54static void setthetime(char *, const char *);
55static void badformat(void);
56static void __dead usage(void);
57
58int
59main(int argc, char *argv[])
60{
61	const char *errstr;
62	struct tm *tp;
63	int ch, rflag;
64	char *format, buf[1024], *outzone = NULL;
65	const char *pformat = NULL;
66
67	rflag = 0;
68	while ((ch = getopt(argc, argv, "af:jr:uz:")) != -1)
69		switch(ch) {
70		case 'a':
71			slidetime = 1;
72			break;
73		case 'f':		/* parse with strptime */
74			pformat = optarg;
75			break;
76		case 'j':		/* don't set */
77			jflag = 1;
78			break;
79		case 'r':		/* user specified seconds */
80			rflag = 1;
81			tval = strtonum(optarg, LLONG_MIN, LLONG_MAX, &errstr);
82			if (errstr)
83				errx(1, "seconds is %s: %s", errstr, optarg);
84			break;
85		case 'u':		/* do everything in UTC */
86			if (setenv("TZ", "UTC", 1) == -1)
87				err(1, "cannot unsetenv TZ");
88			break;
89		case 'z':
90			outzone = optarg;
91			break;
92		default:
93			usage();
94		}
95	argc -= optind;
96	argv += optind;
97
98	if (!rflag && time(&tval) == -1)
99		err(1, "time");
100
101	format = "%a %b %e %H:%M:%S %Z %Y";
102
103	/* allow the operands in any order */
104	if (*argv && **argv == '+') {
105		format = *argv + 1;
106		argv++;
107		argc--;
108	}
109
110	if (*argv) {
111		setthetime(*argv, pformat);
112		argv++;
113		argc--;
114	}
115
116	if (pledge("stdio", NULL) == -1)
117		err(1, "pledge");
118
119	if (*argv && **argv == '+') {
120		format = *argv + 1;
121		argc--;
122	}
123
124	if (argc > 0)
125		errx(1, "too many arguments");
126
127	if (outzone)
128		setenv("TZ", outzone, 1);
129
130	tp = localtime(&tval);
131	if (tp == NULL)
132		errx(1, "conversion error");
133	(void)strftime(buf, sizeof(buf), format, tp);
134	(void)printf("%s\n", buf);
135	return 0;
136}
137
138#define	ATOI2(ar)	((ar) += 2, ((ar)[-2] - '0') * 10 + ((ar)[-1] - '0'))
139void
140setthetime(char *p, const char *pformat)
141{
142	struct tm *lt, tm;
143	struct timeval tv;
144	char *dot, *t;
145	time_t now;
146	int yearset = 0;
147
148	/* Let us set the time even if logwtmp would fail. */
149	unveil("/var/log/wtmp", "w");
150	if (pledge("stdio settime wpath", NULL) == -1)
151		err(1, "pledge");
152
153	lt = localtime(&tval);
154	if (lt == NULL)
155		errx(1, "conversion error");
156
157	lt->tm_isdst = -1;			/* correct for DST */
158
159	if (pformat) {
160		tm = *lt;
161		if (strptime(p, pformat, &tm) == NULL) {
162			fprintf(stderr, "trouble %s %s\n", p, pformat);
163			badformat();
164		}
165		lt = &tm;
166	} else {
167		for (t = p, dot = NULL; *t; ++t) {
168			if (isdigit((unsigned char)*t))
169				continue;
170			if (*t == '.' && dot == NULL) {
171				dot = t;
172				continue;
173			}
174			badformat();
175		}
176
177		if (dot != NULL) {			/* .SS */
178			*dot++ = '\0';
179			if (strlen(dot) != 2)
180				badformat();
181			lt->tm_sec = ATOI2(dot);
182			if (lt->tm_sec > 61)
183				badformat();
184		} else
185			lt->tm_sec = 0;
186
187		switch (strlen(p)) {
188		case 12:				/* cc */
189			lt->tm_year = (ATOI2(p) * 100) - 1900;
190			yearset = 1;
191			/* FALLTHROUGH */
192		case 10:				/* yy */
193			if (!yearset) {
194				/* mask out current year, leaving only century */
195				lt->tm_year = ((lt->tm_year / 100) * 100);
196			}
197			lt->tm_year += ATOI2(p);
198			/* FALLTHROUGH */
199		case 8:					/* mm */
200			lt->tm_mon = ATOI2(p);
201			if ((lt->tm_mon > 12) || !lt->tm_mon)
202				badformat();
203			--lt->tm_mon;			/* time struct is 0 - 11 */
204			/* FALLTHROUGH */
205		case 6:					/* dd */
206			lt->tm_mday = ATOI2(p);
207			if ((lt->tm_mday > 31) || !lt->tm_mday)
208				badformat();
209			/* FALLTHROUGH */
210		case 4:					/* HH */
211			lt->tm_hour = ATOI2(p);
212			if (lt->tm_hour > 23)
213				badformat();
214			/* FALLTHROUGH */
215		case 2:					/* MM */
216			lt->tm_min = ATOI2(p);
217			if (lt->tm_min > 59)
218				badformat();
219			break;
220		default:
221			badformat();
222		}
223	}
224
225	/* convert broken-down time to UTC clock time */
226	if (pformat != NULL && strstr(pformat, "%s") != NULL)
227		tval = timegm(lt);
228	else
229		tval = mktime(lt);
230	if (tval == -1)
231		errx(1, "specified date is outside allowed range");
232
233	if (jflag)
234		return;
235
236	/* set the time */
237	if (slidetime) {
238		if ((now = time(NULL)) == -1)
239			err(1, "time");
240		tv.tv_sec = tval - now;
241		tv.tv_usec = 0;
242		if (adjtime(&tv, NULL) == -1)
243			err(1, "adjtime");
244	} else {
245#ifndef SMALL
246		logwtmp("|", "date", "");
247#endif
248		tv.tv_sec = tval;
249		tv.tv_usec = 0;
250		if (settimeofday(&tv, NULL))
251			err(1, "settimeofday");
252#ifndef SMALL
253		logwtmp("{", "date", "");
254#endif
255	}
256
257	if ((p = getlogin()) == NULL)
258		p = "???";
259	syslog(LOG_AUTH | LOG_NOTICE, "date set by %s", p);
260}
261
262static void
263badformat(void)
264{
265	warnx("illegal time format");
266	usage();
267}
268
269static void __dead
270usage(void)
271{
272	fprintf(stderr,
273	    "usage: %s [-aju] [-f pformat] [-r seconds]\n"
274	    "\t[-z output_zone] [+format] [[[[[[cc]yy]mm]dd]HH]MM[.SS]]\n",
275	    __progname);
276	exit(1);
277}
278