date.c revision 1.62 1 1.62 christos /* $NetBSD: date.c,v 1.62 2021/05/26 20:19:51 christos Exp $ */
2 1.9 cgd
3 1.1 cgd /*
4 1.8 mycroft * Copyright (c) 1985, 1987, 1988, 1993
5 1.8 mycroft * The Regents of the University of California. All rights reserved.
6 1.1 cgd *
7 1.1 cgd * Redistribution and use in source and binary forms, with or without
8 1.1 cgd * modification, are permitted provided that the following conditions
9 1.1 cgd * are met:
10 1.1 cgd * 1. Redistributions of source code must retain the above copyright
11 1.1 cgd * notice, this list of conditions and the following disclaimer.
12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 cgd * notice, this list of conditions and the following disclaimer in the
14 1.1 cgd * documentation and/or other materials provided with the distribution.
15 1.37 agc * 3. Neither the name of the University nor the names of its contributors
16 1.1 cgd * may be used to endorse or promote products derived from this software
17 1.1 cgd * without specific prior written permission.
18 1.1 cgd *
19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 1.1 cgd * SUCH DAMAGE.
30 1.1 cgd */
31 1.1 cgd
32 1.62 christos #if HAVE_NBTOOL_CONFIG_H
33 1.62 christos #include "nbtool_config.h"
34 1.62 christos #endif
35 1.62 christos
36 1.15 thorpej #include <sys/cdefs.h>
37 1.1 cgd #ifndef lint
38 1.15 thorpej __COPYRIGHT(
39 1.52 lukem "@(#) Copyright (c) 1985, 1987, 1988, 1993\
40 1.52 lukem The Regents of the University of California. All rights reserved.");
41 1.1 cgd #endif /* not lint */
42 1.1 cgd
43 1.1 cgd #ifndef lint
44 1.10 cgd #if 0
45 1.11 jtc static char sccsid[] = "@(#)date.c 8.2 (Berkeley) 4/28/95";
46 1.10 cgd #else
47 1.62 christos __RCSID("$NetBSD: date.c,v 1.62 2021/05/26 20:19:51 christos Exp $");
48 1.10 cgd #endif
49 1.1 cgd #endif /* not lint */
50 1.1 cgd
51 1.1 cgd #include <sys/param.h>
52 1.1 cgd #include <sys/time.h>
53 1.8 mycroft
54 1.8 mycroft #include <ctype.h>
55 1.8 mycroft #include <err.h>
56 1.8 mycroft #include <fcntl.h>
57 1.59 christos #include <errno.h>
58 1.34 wiz #include <locale.h>
59 1.1 cgd #include <stdio.h>
60 1.1 cgd #include <stdlib.h>
61 1.1 cgd #include <string.h>
62 1.8 mycroft #include <syslog.h>
63 1.22 kleink #include <time.h>
64 1.18 mycroft #include <tzfile.h>
65 1.8 mycroft #include <unistd.h>
66 1.14 cgd #include <util.h>
67 1.8 mycroft
68 1.8 mycroft #include "extern.h"
69 1.1 cgd
70 1.38 dsl static time_t tval;
71 1.46 jdarrow static int aflag, jflag, rflag, nflag;
72 1.1 cgd
73 1.61 dholland __dead static void badcanotime(const char *, const char *, size_t);
74 1.34 wiz static void setthetime(const char *);
75 1.60 joerg __dead static void usage(void);
76 1.8 mycroft
77 1.8 mycroft int
78 1.34 wiz main(int argc, char *argv[])
79 1.1 cgd {
80 1.43 christos char *buf;
81 1.43 christos size_t bufsiz;
82 1.40 christos const char *format;
83 1.38 dsl int ch;
84 1.59 christos long long val;
85 1.59 christos struct tm *tm;
86 1.1 cgd
87 1.35 wiz setprogname(argv[0]);
88 1.16 cgd (void)setlocale(LC_ALL, "");
89 1.7 jtc
90 1.47 christos while ((ch = getopt(argc, argv, "ad:jnr:u")) != -1) {
91 1.42 peter switch (ch) {
92 1.38 dsl case 'a': /* adjust time slowly */
93 1.38 dsl aflag = 1;
94 1.38 dsl nflag = 1;
95 1.38 dsl break;
96 1.47 christos case 'd':
97 1.62 christos #ifndef HAVE_NBTOOL_CONFIG_H
98 1.47 christos rflag = 1;
99 1.49 christos tval = parsedate(optarg, NULL, NULL);
100 1.61 dholland if (tval == -1) {
101 1.61 dholland errx(EXIT_FAILURE,
102 1.61 dholland "%s: Unrecognized date format", optarg);
103 1.61 dholland }
104 1.47 christos break;
105 1.62 christos #else
106 1.62 christos errx(EXIT_FAILURE,
107 1.62 christos "-d not supported in the tool version");
108 1.62 christos #endif
109 1.46 jdarrow case 'j': /* don't set time */
110 1.46 jdarrow jflag = 1;
111 1.46 jdarrow break;
112 1.1 cgd case 'n': /* don't set network */
113 1.1 cgd nflag = 1;
114 1.1 cgd break;
115 1.1 cgd case 'r': /* user specified seconds */
116 1.61 dholland if (optarg[0] == '\0') {
117 1.61 dholland errx(EXIT_FAILURE, "<empty>: Invalid number");
118 1.61 dholland }
119 1.59 christos errno = 0;
120 1.59 christos val = strtoll(optarg, &buf, 0);
121 1.61 dholland if (errno) {
122 1.61 dholland err(EXIT_FAILURE, "%s", optarg);
123 1.61 dholland }
124 1.61 dholland if (optarg[0] == '\0' || *buf != '\0') {
125 1.61 dholland errx(EXIT_FAILURE,
126 1.61 dholland "%s: Invalid number", optarg);
127 1.61 dholland }
128 1.1 cgd rflag = 1;
129 1.59 christos tval = (time_t)val;
130 1.1 cgd break;
131 1.27 mycroft case 'u': /* do everything in UTC */
132 1.55 gson (void)setenv("TZ", "UTC0", 1);
133 1.1 cgd break;
134 1.1 cgd default:
135 1.1 cgd usage();
136 1.1 cgd }
137 1.38 dsl }
138 1.1 cgd argc -= optind;
139 1.1 cgd argv += optind;
140 1.1 cgd
141 1.8 mycroft if (!rflag && time(&tval) == -1)
142 1.42 peter err(EXIT_FAILURE, "time");
143 1.1 cgd
144 1.1 cgd
145 1.1 cgd /* allow the operands in any order */
146 1.1 cgd if (*argv && **argv == '+') {
147 1.53 dholland format = *argv;
148 1.1 cgd ++argv;
149 1.59 christos } else
150 1.59 christos format = "+%a %b %e %H:%M:%S %Z %Y";
151 1.1 cgd
152 1.1 cgd if (*argv) {
153 1.1 cgd setthetime(*argv);
154 1.1 cgd ++argv;
155 1.1 cgd }
156 1.1 cgd
157 1.1 cgd if (*argv && **argv == '+')
158 1.53 dholland format = *argv;
159 1.1 cgd
160 1.43 christos if ((buf = malloc(bufsiz = 1024)) == NULL)
161 1.43 christos goto bad;
162 1.59 christos
163 1.59 christos if ((tm = localtime(&tval)) == NULL)
164 1.61 dholland err(EXIT_FAILURE, "%lld: localtime", (long long)tval);
165 1.59 christos
166 1.59 christos while (strftime(buf, bufsiz, format, tm) == 0)
167 1.43 christos if ((buf = realloc(buf, bufsiz <<= 1)) == NULL)
168 1.43 christos goto bad;
169 1.59 christos
170 1.59 christos (void)printf("%s\n", buf + 1);
171 1.43 christos free(buf);
172 1.43 christos return 0;
173 1.43 christos bad:
174 1.59 christos err(EXIT_FAILURE, "Cannot allocate format buffer");
175 1.1 cgd }
176 1.1 cgd
177 1.34 wiz static void
178 1.61 dholland badcanotime(const char *msg, const char *val, size_t where)
179 1.34 wiz {
180 1.61 dholland warnx("%s in canonical time", msg);
181 1.61 dholland warnx("%s", val);
182 1.61 dholland warnx("%*s", (int)where + 1, "^");
183 1.45 elad usage();
184 1.45 elad }
185 1.45 elad
186 1.34 wiz #define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
187 1.21 mycroft
188 1.33 gmcgarry static void
189 1.34 wiz setthetime(const char *p)
190 1.1 cgd {
191 1.34 wiz struct timeval tv;
192 1.38 dsl time_t new_time;
193 1.12 tls struct tm *lt;
194 1.61 dholland const char *dot, *t, *op;
195 1.50 cbiere size_t len;
196 1.50 cbiere int yearset;
197 1.1 cgd
198 1.8 mycroft for (t = p, dot = NULL; *t; ++t) {
199 1.61 dholland if (*t == '.') {
200 1.61 dholland if (dot == NULL) {
201 1.61 dholland dot = t;
202 1.61 dholland } else {
203 1.61 dholland badcanotime("Unexpected dot", p, t - p);
204 1.61 dholland }
205 1.61 dholland } else if (!isdigit((unsigned char)*t)) {
206 1.61 dholland badcanotime("Expected digit", p, t - p);
207 1.8 mycroft }
208 1.8 mycroft }
209 1.1 cgd
210 1.59 christos if ((lt = localtime(&tval)) == NULL)
211 1.61 dholland err(EXIT_FAILURE, "%lld: localtime", (long long)tval);
212 1.31 bjh21
213 1.31 bjh21 lt->tm_isdst = -1; /* Divine correct DST */
214 1.1 cgd
215 1.8 mycroft if (dot != NULL) { /* .ss */
216 1.23 mycroft len = strlen(dot);
217 1.61 dholland if (len > 3) {
218 1.61 dholland badcanotime("Unexpected digit after seconds field",
219 1.61 dholland p, strlen(p) - 1);
220 1.61 dholland } else if (len < 3) {
221 1.61 dholland badcanotime("Expected digit in seconds field",
222 1.61 dholland p, strlen(p));
223 1.61 dholland }
224 1.23 mycroft ++dot;
225 1.8 mycroft lt->tm_sec = ATOI2(dot);
226 1.45 elad if (lt->tm_sec > 61)
227 1.61 dholland badcanotime("Seconds out of range", p, strlen(p) - 1);
228 1.23 mycroft } else {
229 1.23 mycroft len = 0;
230 1.1 cgd lt->tm_sec = 0;
231 1.23 mycroft }
232 1.1 cgd
233 1.61 dholland op = p;
234 1.18 mycroft yearset = 0;
235 1.23 mycroft switch (strlen(p) - len) {
236 1.21 mycroft case 12: /* cc */
237 1.21 mycroft lt->tm_year = ATOI2(p) * 100 - TM_YEAR_BASE;
238 1.45 elad if (lt->tm_year < 0)
239 1.61 dholland badcanotime("Year before 1900", op, p - op + 1);
240 1.18 mycroft yearset = 1;
241 1.18 mycroft /* FALLTHROUGH */
242 1.1 cgd case 10: /* yy */
243 1.18 mycroft if (yearset) {
244 1.21 mycroft lt->tm_year += ATOI2(p);
245 1.18 mycroft } else {
246 1.18 mycroft yearset = ATOI2(p);
247 1.21 mycroft if (yearset < 69)
248 1.21 mycroft lt->tm_year = yearset + 2000 - TM_YEAR_BASE;
249 1.18 mycroft else
250 1.21 mycroft lt->tm_year = yearset + 1900 - TM_YEAR_BASE;
251 1.18 mycroft }
252 1.1 cgd /* FALLTHROUGH */
253 1.1 cgd case 8: /* mm */
254 1.1 cgd lt->tm_mon = ATOI2(p);
255 1.45 elad if (lt->tm_mon > 12 || lt->tm_mon == 0)
256 1.61 dholland badcanotime("Month out of range", op, p - op - 1);
257 1.1 cgd --lt->tm_mon; /* time struct is 0 - 11 */
258 1.1 cgd /* FALLTHROUGH */
259 1.1 cgd case 6: /* dd */
260 1.1 cgd lt->tm_mday = ATOI2(p);
261 1.45 elad switch (lt->tm_mon) {
262 1.45 elad case 0:
263 1.45 elad case 2:
264 1.45 elad case 4:
265 1.45 elad case 6:
266 1.45 elad case 7:
267 1.45 elad case 9:
268 1.45 elad case 11:
269 1.45 elad if (lt->tm_mday > 31 || lt->tm_mday == 0)
270 1.61 dholland badcanotime("Day out of range (max 31)",
271 1.61 dholland op, p - op - 1);
272 1.45 elad break;
273 1.45 elad case 3:
274 1.45 elad case 5:
275 1.45 elad case 8:
276 1.45 elad case 10:
277 1.45 elad if (lt->tm_mday > 30 || lt->tm_mday == 0)
278 1.61 dholland badcanotime("Day out of range (max 30)",
279 1.61 dholland op, p - op - 1);
280 1.45 elad break;
281 1.45 elad case 1:
282 1.61 dholland if (isleap(lt->tm_year + TM_YEAR_BASE)) {
283 1.61 dholland if (lt->tm_mday > 29 || lt->tm_mday == 0) {
284 1.61 dholland badcanotime("Day out of range "
285 1.61 dholland "(max 29)",
286 1.61 dholland op, p - op - 1);
287 1.61 dholland }
288 1.61 dholland } else {
289 1.61 dholland if (lt->tm_mday > 28 || lt->tm_mday == 0) {
290 1.61 dholland badcanotime("Day out of range "
291 1.61 dholland "(max 28)",
292 1.61 dholland op, p - op - 1);
293 1.61 dholland }
294 1.61 dholland }
295 1.45 elad break;
296 1.45 elad default:
297 1.61 dholland /*
298 1.61 dholland * If the month was given, it's already been
299 1.61 dholland * checked. If a bad value came back from
300 1.61 dholland * localtime, something's badly broken.
301 1.61 dholland * (make this an assertion?)
302 1.61 dholland */
303 1.61 dholland errx(EXIT_FAILURE, "localtime gave invalid month %d",
304 1.61 dholland lt->tm_mon);
305 1.45 elad }
306 1.1 cgd /* FALLTHROUGH */
307 1.1 cgd case 4: /* hh */
308 1.1 cgd lt->tm_hour = ATOI2(p);
309 1.45 elad if (lt->tm_hour > 23)
310 1.61 dholland badcanotime("Hour out of range", op, p - op - 1);
311 1.1 cgd /* FALLTHROUGH */
312 1.1 cgd case 2: /* mm */
313 1.1 cgd lt->tm_min = ATOI2(p);
314 1.45 elad if (lt->tm_min > 59)
315 1.61 dholland badcanotime("Minute out of range", op, p - op - 1);
316 1.1 cgd break;
317 1.38 dsl case 0: /* was just .sss */
318 1.38 dsl if (len != 0)
319 1.38 dsl break;
320 1.38 dsl /* FALLTHROUGH */
321 1.1 cgd default:
322 1.61 dholland if (strlen(p) - len > 12) {
323 1.61 dholland badcanotime("Too many digits", p, 12);
324 1.61 dholland } else {
325 1.61 dholland badcanotime("Not enough digits", p, strlen(p) - len);
326 1.61 dholland }
327 1.1 cgd }
328 1.1 cgd
329 1.27 mycroft /* convert broken-down time to UTC clock time */
330 1.61 dholland if ((new_time = mktime(lt)) == -1) {
331 1.61 dholland /* Can this actually happen? */
332 1.61 dholland err(EXIT_FAILURE, "%s: mktime", op);
333 1.61 dholland }
334 1.1 cgd
335 1.46 jdarrow /* if jflag is set, don't actually change the time, just return */
336 1.46 jdarrow if (jflag) {
337 1.46 jdarrow tval = new_time;
338 1.46 jdarrow return;
339 1.46 jdarrow }
340 1.46 jdarrow
341 1.1 cgd /* set the time */
342 1.62 christos #ifndef HAVE_NBTOOL_CONFIG_H
343 1.38 dsl if (nflag || netsettime(new_time)) {
344 1.1 cgd logwtmp("|", "date", "");
345 1.38 dsl if (aflag) {
346 1.38 dsl tv.tv_sec = new_time - tval;
347 1.38 dsl tv.tv_usec = 0;
348 1.41 hubertf if (adjtime(&tv, NULL))
349 1.44 jnemeth err(EXIT_FAILURE, "adjtime");
350 1.38 dsl } else {
351 1.39 dsl tval = new_time;
352 1.38 dsl tv.tv_sec = tval;
353 1.38 dsl tv.tv_usec = 0;
354 1.41 hubertf if (settimeofday(&tv, NULL))
355 1.44 jnemeth err(EXIT_FAILURE, "settimeofday");
356 1.1 cgd }
357 1.1 cgd logwtmp("{", "date", "");
358 1.1 cgd }
359 1.62 christos #else
360 1.62 christos errx(EXIT_FAILURE, "Can't set the time in the tools version");
361 1.62 christos #endif
362 1.8 mycroft
363 1.8 mycroft if ((p = getlogin()) == NULL)
364 1.8 mycroft p = "???";
365 1.8 mycroft syslog(LOG_AUTH | LOG_NOTICE, "date set by %s", p);
366 1.1 cgd }
367 1.1 cgd
368 1.8 mycroft static void
369 1.34 wiz usage(void)
370 1.1 cgd {
371 1.1 cgd (void)fprintf(stderr,
372 1.59 christos "Usage: %s [-ajnu] [-d date] [-r seconds] [+format]",
373 1.59 christos getprogname());
374 1.46 jdarrow (void)fprintf(stderr, " [[[[[[CC]yy]mm]dd]HH]MM[.SS]]\n");
375 1.41 hubertf exit(EXIT_FAILURE);
376 1.24 mycroft /* NOTREACHED */
377 1.1 cgd }
378