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