1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* Copyright (c) 1987, 1988 Microsoft Corporation */ 31 /* All Rights Reserved */ 32 33 #include <sys/types.h> 34 #include <sys/stat.h> 35 #include <stdio.h> 36 #include <string.h> 37 #include <strings.h> 38 #include <stdlib.h> 39 #include <ctype.h> 40 #include <libgen.h> 41 #include <fcntl.h> 42 #include <pwd.h> 43 #include <time.h> 44 #include <unistd.h> 45 #include <locale.h> 46 #include <sys/time.h> 47 #include <errno.h> 48 49 #define BADTIME "bad time specification" 50 51 static char *myname; 52 53 static int isnumber(char *); 54 static int atoi_for2(char *); 55 static void usage(const int); 56 static void touchabort(const char *); 57 static void parse_datetime(char *, timespec_t *); 58 static void parse_time(char *, timespec_t *); 59 static void parse_timespec(char *, timespec_t *); 60 61 int 62 main(int argc, char *argv[]) 63 { 64 int c; 65 66 int aflag = 0; 67 int cflag = 0; 68 int rflag = 0; 69 int mflag = 0; 70 int tflag = 0; 71 int stflag = 0; 72 int status = 0; 73 int usecurrenttime = 1; 74 int timespecified; 75 int optc; 76 int fd = -1; 77 mode_t cmode = 78 (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); 79 struct stat stbuf; 80 struct stat prstbuf; 81 timespec_t times[2]; 82 timespec_t *tsp; 83 84 (void) setlocale(LC_ALL, ""); 85 #if !defined(TEXT_DOMAIN) 86 #define TEXT_DOMAIN "SYS_TEST" 87 #endif 88 (void) textdomain(TEXT_DOMAIN); 89 90 myname = basename(argv[0]); 91 if (strcmp(myname, "settime") == 0) { 92 cflag++; 93 stflag++; 94 while ((optc = getopt(argc, argv, "f:")) != EOF) { 95 switch (optc) { 96 case 'f': 97 rflag++; 98 usecurrenttime = 0; 99 if (stat(optarg, &prstbuf) == -1) { 100 (void) fprintf(stderr, "%s: ", myname); 101 perror(optarg); 102 return (2); 103 } 104 break; 105 case '?': 106 usage(stflag); 107 break; 108 } 109 } 110 } else { 111 while ((optc = getopt(argc, argv, "acfmr:d:t:")) != EOF) { 112 switch (optc) { 113 case 'a': 114 aflag++; 115 break; 116 case 'c': 117 cflag++; 118 break; 119 case 'f': /* silently ignore for UCB compat */ 120 break; 121 case 'm': 122 mflag++; 123 break; 124 case 'r': /* same as settime's -f option */ 125 rflag++; 126 usecurrenttime = 0; 127 if (stat(optarg, &prstbuf) == -1) { 128 (void) fprintf(stderr, "%s: ", myname); 129 perror(optarg); 130 return (2); 131 } 132 break; 133 case 'd': 134 tflag++; 135 usecurrenttime = 0; 136 parse_datetime(optarg, &prstbuf.st_mtim); 137 prstbuf.st_atim = prstbuf.st_mtim; 138 break; 139 case 't': 140 tflag++; 141 usecurrenttime = 0; 142 parse_time(optarg, &prstbuf.st_mtim); 143 prstbuf.st_atim = prstbuf.st_mtim; 144 break; 145 case '?': 146 usage(stflag); 147 break; 148 } 149 } 150 } 151 152 argc -= optind; 153 argv += optind; 154 155 if ((argc < 1) || (rflag + tflag > 1)) 156 usage(stflag); 157 158 if ((aflag == 0) && (mflag == 0)) { 159 aflag = 1; 160 mflag = 1; 161 } 162 if ((aflag && !mflag) || (mflag && !aflag)) 163 usecurrenttime = 0; 164 165 /* 166 * If -r, -t or -d has been specified, 167 * use the specified time. 168 */ 169 timespecified = (rflag | tflag); 170 171 if (timespecified == 0 && argc >= 2 && isnumber(*argv) && 172 (strlen(*argv) == 8 || strlen(*argv) == 10)) { 173 /* 174 * time is specified as an operand; use it. 175 */ 176 parse_timespec(*argv++, &prstbuf.st_mtim); 177 prstbuf.st_atim = prstbuf.st_mtim; 178 usecurrenttime = 0; 179 timespecified = 1; 180 argc--; 181 } 182 183 for (c = 0; c < argc; c++) { 184 if (stat(argv[c], &stbuf)) { 185 /* 186 * If stat failed for reason other than ENOENT, the file 187 * should not be created, since this can clobber 188 * the contents of an existing file. 189 */ 190 if (errno != ENOENT) { 191 (void) fprintf(stderr, 192 gettext("%s: cannot stat %s: %s\n"), 193 myname, argv[c], strerror(errno)); 194 status++; 195 continue; 196 } else if (cflag) { 197 continue; 198 } else if ((fd = creat(argv[c], cmode)) < 0) { 199 (void) fprintf(stderr, 200 gettext("%s: cannot create %s: %s\n"), 201 myname, argv[c], strerror(errno)); 202 status++; 203 continue; 204 } 205 } 206 207 if (usecurrenttime) { 208 tsp = NULL; 209 } else { 210 if (mflag == 0) { 211 /* Keep the mtime of the file */ 212 times[1].tv_nsec = UTIME_OMIT; 213 } else if (timespecified) { 214 /* Set the specified time */ 215 times[1] = prstbuf.st_mtim; 216 } else { 217 /* Otherwise, use the current time */ 218 times[1].tv_nsec = UTIME_NOW; 219 } 220 221 if (aflag == 0) { 222 /* Keep the atime of the file */ 223 times[0].tv_nsec = UTIME_OMIT; 224 } else if (timespecified) { 225 /* Set the specified time */ 226 times[0] = prstbuf.st_atim; 227 } else { 228 /* Otherwise, use the current time */ 229 times[0].tv_nsec = UTIME_NOW; 230 } 231 232 tsp = times; 233 } 234 235 if ((fd >= 0 && futimens(fd, tsp) != 0) || 236 (fd < 0 && utimensat(AT_FDCWD, argv[c], tsp, 0) != 0)) { 237 (void) fprintf(stderr, 238 gettext("%s: cannot change times on %s: %s\n"), 239 myname, argv[c], strerror(errno)); 240 status++; 241 } 242 if (fd >= 0) { 243 (void) close(fd); 244 fd = -1; 245 } 246 } 247 return (status); 248 } 249 250 static int 251 isnumber(char *s) 252 { 253 int c; 254 255 while ((c = *s++) != '\0') 256 if (!isdigit(c)) 257 return (0); 258 return (1); 259 } 260 261 static void 262 parse_datetime(char *t, timespec_t *ts) 263 { 264 char date[64]; 265 char *year; 266 char *month; 267 char *day; 268 char *hour; 269 char *minute; 270 char *second; 271 char *fraction; 272 int utc = 0; 273 char *p; 274 time_t when; 275 int nanoseconds; 276 struct tm tm; 277 278 /* 279 * The date string has the format (defined by the touch(1) spec): 280 * YYYY-MM-DDThh:mm:SS[.frac][tz] 281 * YYYY-MM-DDThh:mm:SS[,frac][tz] 282 * T is either the literal 'T' or is a space character. 283 * tz is either empty (local time) or the literal 'Z' (UTC). 284 * All other fields are strings of digits. 285 */ 286 287 /* 288 * Make a copy of the date string so it can be tokenized. 289 */ 290 if (strlcpy(date, t, sizeof (date)) >= sizeof (date)) 291 touchabort(BADTIME); 292 293 /* deal with the optional trailing 'Z' first */ 294 p = date + strlen(date) - 1; 295 if (*p == 'Z') { 296 utc = 1; 297 *p = '\0'; 298 } 299 300 /* break out the component tokens */ 301 p = date; 302 year = strsep(&p, "-"); 303 month = strsep(&p, "-"); 304 day = strsep(&p, "T "); 305 hour = strsep(&p, ":"); 306 minute = strsep(&p, ":"); 307 second = strsep(&p, ".,"); 308 fraction = p; 309 310 /* verify the component tokens */ 311 if (year == NULL || strlen(year) < 4 || !isnumber(year) || 312 month == NULL || strlen(month) != 2 || !isnumber(month) || 313 day == NULL || strlen(day) != 2 || !isnumber(day) || 314 hour == NULL || strlen(hour) != 2 || !isnumber(hour) || 315 minute == NULL || strlen(minute) != 2 || !isnumber(minute) || 316 second == NULL || strlen(second) != 2 || !isnumber(second) || 317 (fraction != NULL && (*fraction == '\0' || !isnumber(fraction)))) 318 touchabort(BADTIME); 319 320 (void) memset(&tm, 0, sizeof (struct tm)); 321 322 tm.tm_year = atoi(year) - 1900; 323 tm.tm_mon = atoi(month) - 1; 324 tm.tm_mday = atoi(day); 325 tm.tm_hour = atoi(hour); 326 tm.tm_min = atoi(minute); 327 tm.tm_sec = atoi(second); 328 if (utc) { 329 (void) setenv("TZ", "GMT0", 1); 330 tzset(); 331 } 332 333 errno = 0; 334 if ((when = mktime(&tm)) == -1 && errno != 0) 335 touchabort(BADTIME); 336 if (tm.tm_isdst) 337 when -= (timezone - altzone); 338 339 if (fraction == NULL) { 340 nanoseconds = 0; 341 } else { 342 /* truncate beyond 9 digits (nanoseconds) */ 343 if (strlen(fraction) > 9) 344 fraction[9] = '\0'; 345 nanoseconds = atoi(fraction); 346 347 switch (strlen(fraction)) { 348 case 1: 349 nanoseconds *= 100000000; 350 break; 351 case 2: 352 nanoseconds *= 10000000; 353 break; 354 case 3: 355 nanoseconds *= 1000000; 356 break; 357 case 4: 358 nanoseconds *= 100000; 359 break; 360 case 5: 361 nanoseconds *= 10000; 362 break; 363 case 6: 364 nanoseconds *= 1000; 365 break; 366 case 7: 367 nanoseconds *= 100; 368 break; 369 case 8: 370 nanoseconds *= 10; 371 break; 372 case 9: 373 break; 374 } 375 } 376 377 ts->tv_sec = when; 378 ts->tv_nsec = nanoseconds; 379 } 380 381 static void 382 parse_time(char *t, timespec_t *ts) 383 { 384 int century = 0; 385 int seconds = 0; 386 char *p; 387 time_t when; 388 struct tm tm; 389 390 /* 391 * time in the following format (defined by the touch(1) spec): 392 * [[CC]YY]MMDDhhmm[.SS] 393 */ 394 if ((p = strchr(t, '.')) != NULL) { 395 if (strchr(p+1, '.') != NULL) 396 touchabort(BADTIME); 397 seconds = atoi_for2(p+1); 398 *p = '\0'; 399 } 400 401 (void) memset(&tm, 0, sizeof (struct tm)); 402 when = time(0); 403 tm.tm_year = localtime(&when)->tm_year; 404 405 switch (strlen(t)) { 406 case 12: /* CCYYMMDDhhmm */ 407 century = atoi_for2(t); 408 t += 2; 409 /* FALLTHROUGH */ 410 case 10: /* YYMMDDhhmm */ 411 tm.tm_year = atoi_for2(t); 412 t += 2; 413 if (century == 0) { 414 if (tm.tm_year < 69) 415 tm.tm_year += 100; 416 } else 417 tm.tm_year += (century - 19) * 100; 418 /* FALLTHROUGH */ 419 case 8: /* MMDDhhmm */ 420 tm.tm_mon = atoi_for2(t) - 1; 421 t += 2; 422 tm.tm_mday = atoi_for2(t); 423 t += 2; 424 tm.tm_hour = atoi_for2(t); 425 t += 2; 426 tm.tm_min = atoi_for2(t); 427 tm.tm_sec = seconds; 428 break; 429 default: 430 touchabort(BADTIME); 431 } 432 433 if ((when = mktime(&tm)) == -1) 434 touchabort(BADTIME); 435 if (tm.tm_isdst) 436 when -= (timezone-altzone); 437 438 ts->tv_sec = when; 439 ts->tv_nsec = 0; 440 } 441 442 static void 443 parse_timespec(char *t, timespec_t *ts) 444 { 445 time_t when; 446 struct tm tm; 447 448 /* 449 * time in the following format (defined by the touch(1) spec): 450 * MMDDhhmm[yy] 451 */ 452 453 (void) memset(&tm, 0, sizeof (struct tm)); 454 when = time(0); 455 tm.tm_year = localtime(&when)->tm_year; 456 457 switch (strlen(t)) { 458 case 10: /* MMDDhhmmyy */ 459 tm.tm_year = atoi_for2(t+8); 460 if (tm.tm_year < 69) 461 tm.tm_year += 100; 462 /* FALLTHROUGH */ 463 case 8: /* MMDDhhmm */ 464 tm.tm_mon = atoi_for2(t) - 1; 465 t += 2; 466 tm.tm_mday = atoi_for2(t); 467 t += 2; 468 tm.tm_hour = atoi_for2(t); 469 t += 2; 470 tm.tm_min = atoi_for2(t); 471 break; 472 default: 473 touchabort(BADTIME); 474 } 475 476 if ((when = mktime(&tm)) == -1) 477 touchabort(BADTIME); 478 if (tm.tm_isdst) 479 when -= (timezone - altzone); 480 481 ts->tv_sec = when; 482 ts->tv_nsec = 0; 483 } 484 485 static int 486 atoi_for2(char *p) 487 { 488 int value; 489 490 value = (*p - '0') * 10 + *(p+1) - '0'; 491 if ((value < 0) || (value > 99)) 492 touchabort(BADTIME); 493 return (value); 494 } 495 496 static void 497 touchabort(const char *message) 498 { 499 (void) fprintf(stderr, "%s: %s\n", myname, gettext(message)); 500 exit(1); 501 } 502 503 static void 504 usage(const int settime) 505 { 506 if (settime) { 507 (void) fprintf(stderr, gettext( 508 "usage: %s [-f file] [mmddhhmm[yy]] file...\n"), myname); 509 exit(2); 510 } 511 (void) fprintf(stderr, gettext( 512 "usage: %s [-acm] [-r ref_file] file...\n" 513 " %s [-acm] [-t [[CC]YY]MMDDhhmm[.SS]] file...\n" 514 " %s [-acm] [-d YYYY-MM-DDThh:mm:SS[.frac][Z]] file...\n" 515 " %s [-acm] [-d YYYY-MM-DDThh:mm:SS[,frac][Z]] file...\n" 516 " %s [-acm] [MMDDhhmm[yy]] file...\n"), 517 myname, myname, myname, myname, myname); 518 exit(2); 519 } 520