1 /* $NetBSD: date.c,v 1.61 2014/09/01 21:42:21 dholland 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 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT( 35 "@(#) Copyright (c) 1985, 1987, 1988, 1993\ 36 The Regents of the University of California. All rights reserved."); 37 #endif /* not lint */ 38 39 #ifndef lint 40 #if 0 41 static char sccsid[] = "@(#)date.c 8.2 (Berkeley) 4/28/95"; 42 #else 43 __RCSID("$NetBSD: date.c,v 1.61 2014/09/01 21:42:21 dholland Exp $"); 44 #endif 45 #endif /* not lint */ 46 47 #include <sys/param.h> 48 #include <sys/time.h> 49 50 #include <ctype.h> 51 #include <err.h> 52 #include <fcntl.h> 53 #include <errno.h> 54 #include <locale.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <syslog.h> 59 #include <time.h> 60 #include <tzfile.h> 61 #include <unistd.h> 62 #include <util.h> 63 64 #include "extern.h" 65 66 static time_t tval; 67 static int aflag, jflag, rflag, nflag; 68 69 __dead static void badcanotime(const char *, const char *, size_t); 70 static void setthetime(const char *); 71 __dead static void usage(void); 72 73 int 74 main(int argc, char *argv[]) 75 { 76 char *buf; 77 size_t bufsiz; 78 const char *format; 79 int ch; 80 long long val; 81 struct tm *tm; 82 83 setprogname(argv[0]); 84 (void)setlocale(LC_ALL, ""); 85 86 while ((ch = getopt(argc, argv, "ad:jnr:u")) != -1) { 87 switch (ch) { 88 case 'a': /* adjust time slowly */ 89 aflag = 1; 90 nflag = 1; 91 break; 92 case 'd': 93 rflag = 1; 94 tval = parsedate(optarg, NULL, NULL); 95 if (tval == -1) { 96 errx(EXIT_FAILURE, 97 "%s: Unrecognized date format", optarg); 98 } 99 break; 100 case 'j': /* don't set time */ 101 jflag = 1; 102 break; 103 case 'n': /* don't set network */ 104 nflag = 1; 105 break; 106 case 'r': /* user specified seconds */ 107 if (optarg[0] == '\0') { 108 errx(EXIT_FAILURE, "<empty>: Invalid number"); 109 } 110 errno = 0; 111 val = strtoll(optarg, &buf, 0); 112 if (errno) { 113 err(EXIT_FAILURE, "%s", optarg); 114 } 115 if (optarg[0] == '\0' || *buf != '\0') { 116 errx(EXIT_FAILURE, 117 "%s: Invalid number", optarg); 118 } 119 rflag = 1; 120 tval = (time_t)val; 121 break; 122 case 'u': /* do everything in UTC */ 123 (void)setenv("TZ", "UTC0", 1); 124 break; 125 default: 126 usage(); 127 } 128 } 129 argc -= optind; 130 argv += optind; 131 132 if (!rflag && time(&tval) == -1) 133 err(EXIT_FAILURE, "time"); 134 135 136 /* allow the operands in any order */ 137 if (*argv && **argv == '+') { 138 format = *argv; 139 ++argv; 140 } else 141 format = "+%a %b %e %H:%M:%S %Z %Y"; 142 143 if (*argv) { 144 setthetime(*argv); 145 ++argv; 146 } 147 148 if (*argv && **argv == '+') 149 format = *argv; 150 151 if ((buf = malloc(bufsiz = 1024)) == NULL) 152 goto bad; 153 154 if ((tm = localtime(&tval)) == NULL) 155 err(EXIT_FAILURE, "%lld: localtime", (long long)tval); 156 157 while (strftime(buf, bufsiz, format, tm) == 0) 158 if ((buf = realloc(buf, bufsiz <<= 1)) == NULL) 159 goto bad; 160 161 (void)printf("%s\n", buf + 1); 162 free(buf); 163 return 0; 164 bad: 165 err(EXIT_FAILURE, "Cannot allocate format buffer"); 166 } 167 168 static void 169 badcanotime(const char *msg, const char *val, size_t where) 170 { 171 warnx("%s in canonical time", msg); 172 warnx("%s", val); 173 warnx("%*s", (int)where + 1, "^"); 174 usage(); 175 } 176 177 #define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0')) 178 179 static void 180 setthetime(const char *p) 181 { 182 struct timeval tv; 183 time_t new_time; 184 struct tm *lt; 185 const char *dot, *t, *op; 186 size_t len; 187 int yearset; 188 189 for (t = p, dot = NULL; *t; ++t) { 190 if (*t == '.') { 191 if (dot == NULL) { 192 dot = t; 193 } else { 194 badcanotime("Unexpected dot", p, t - p); 195 } 196 } else if (!isdigit((unsigned char)*t)) { 197 badcanotime("Expected digit", p, t - p); 198 } 199 } 200 201 if ((lt = localtime(&tval)) == NULL) 202 err(EXIT_FAILURE, "%lld: localtime", (long long)tval); 203 204 lt->tm_isdst = -1; /* Divine correct DST */ 205 206 if (dot != NULL) { /* .ss */ 207 len = strlen(dot); 208 if (len > 3) { 209 badcanotime("Unexpected digit after seconds field", 210 p, strlen(p) - 1); 211 } else if (len < 3) { 212 badcanotime("Expected digit in seconds field", 213 p, strlen(p)); 214 } 215 ++dot; 216 lt->tm_sec = ATOI2(dot); 217 if (lt->tm_sec > 61) 218 badcanotime("Seconds out of range", p, strlen(p) - 1); 219 } else { 220 len = 0; 221 lt->tm_sec = 0; 222 } 223 224 op = p; 225 yearset = 0; 226 switch (strlen(p) - len) { 227 case 12: /* cc */ 228 lt->tm_year = ATOI2(p) * 100 - TM_YEAR_BASE; 229 if (lt->tm_year < 0) 230 badcanotime("Year before 1900", op, p - op + 1); 231 yearset = 1; 232 /* FALLTHROUGH */ 233 case 10: /* yy */ 234 if (yearset) { 235 lt->tm_year += ATOI2(p); 236 } else { 237 yearset = ATOI2(p); 238 if (yearset < 69) 239 lt->tm_year = yearset + 2000 - TM_YEAR_BASE; 240 else 241 lt->tm_year = yearset + 1900 - TM_YEAR_BASE; 242 } 243 /* FALLTHROUGH */ 244 case 8: /* mm */ 245 lt->tm_mon = ATOI2(p); 246 if (lt->tm_mon > 12 || lt->tm_mon == 0) 247 badcanotime("Month out of range", op, p - op - 1); 248 --lt->tm_mon; /* time struct is 0 - 11 */ 249 /* FALLTHROUGH */ 250 case 6: /* dd */ 251 lt->tm_mday = ATOI2(p); 252 switch (lt->tm_mon) { 253 case 0: 254 case 2: 255 case 4: 256 case 6: 257 case 7: 258 case 9: 259 case 11: 260 if (lt->tm_mday > 31 || lt->tm_mday == 0) 261 badcanotime("Day out of range (max 31)", 262 op, p - op - 1); 263 break; 264 case 3: 265 case 5: 266 case 8: 267 case 10: 268 if (lt->tm_mday > 30 || lt->tm_mday == 0) 269 badcanotime("Day out of range (max 30)", 270 op, p - op - 1); 271 break; 272 case 1: 273 if (isleap(lt->tm_year + TM_YEAR_BASE)) { 274 if (lt->tm_mday > 29 || lt->tm_mday == 0) { 275 badcanotime("Day out of range " 276 "(max 29)", 277 op, p - op - 1); 278 } 279 } else { 280 if (lt->tm_mday > 28 || lt->tm_mday == 0) { 281 badcanotime("Day out of range " 282 "(max 28)", 283 op, p - op - 1); 284 } 285 } 286 break; 287 default: 288 /* 289 * If the month was given, it's already been 290 * checked. If a bad value came back from 291 * localtime, something's badly broken. 292 * (make this an assertion?) 293 */ 294 errx(EXIT_FAILURE, "localtime gave invalid month %d", 295 lt->tm_mon); 296 } 297 /* FALLTHROUGH */ 298 case 4: /* hh */ 299 lt->tm_hour = ATOI2(p); 300 if (lt->tm_hour > 23) 301 badcanotime("Hour out of range", op, p - op - 1); 302 /* FALLTHROUGH */ 303 case 2: /* mm */ 304 lt->tm_min = ATOI2(p); 305 if (lt->tm_min > 59) 306 badcanotime("Minute out of range", op, p - op - 1); 307 break; 308 case 0: /* was just .sss */ 309 if (len != 0) 310 break; 311 /* FALLTHROUGH */ 312 default: 313 if (strlen(p) - len > 12) { 314 badcanotime("Too many digits", p, 12); 315 } else { 316 badcanotime("Not enough digits", p, strlen(p) - len); 317 } 318 } 319 320 /* convert broken-down time to UTC clock time */ 321 if ((new_time = mktime(lt)) == -1) { 322 /* Can this actually happen? */ 323 err(EXIT_FAILURE, "%s: mktime", op); 324 } 325 326 /* if jflag is set, don't actually change the time, just return */ 327 if (jflag) { 328 tval = new_time; 329 return; 330 } 331 332 /* set the time */ 333 if (nflag || netsettime(new_time)) { 334 logwtmp("|", "date", ""); 335 if (aflag) { 336 tv.tv_sec = new_time - tval; 337 tv.tv_usec = 0; 338 if (adjtime(&tv, NULL)) 339 err(EXIT_FAILURE, "adjtime"); 340 } else { 341 tval = new_time; 342 tv.tv_sec = tval; 343 tv.tv_usec = 0; 344 if (settimeofday(&tv, NULL)) 345 err(EXIT_FAILURE, "settimeofday"); 346 } 347 logwtmp("{", "date", ""); 348 } 349 350 if ((p = getlogin()) == NULL) 351 p = "???"; 352 syslog(LOG_AUTH | LOG_NOTICE, "date set by %s", p); 353 } 354 355 static void 356 usage(void) 357 { 358 (void)fprintf(stderr, 359 "Usage: %s [-ajnu] [-d date] [-r seconds] [+format]", 360 getprogname()); 361 (void)fprintf(stderr, " [[[[[[CC]yy]mm]dd]HH]MM[.SS]]\n"); 362 exit(EXIT_FAILURE); 363 /* NOTREACHED */ 364 } 365