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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* Copyright (c) 1987, 1988 Microsoft Corporation */ 30 /* All Rights Reserved */ 31 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <stdio.h> 35 #include <string.h> 36 #include <strings.h> 37 #include <stdlib.h> 38 #include <ctype.h> 39 #include <libgen.h> 40 #include <fcntl.h> 41 #include <pwd.h> 42 #include <time.h> 43 #include <unistd.h> 44 #include <locale.h> 45 #include <sys/time.h> 46 #include <errno.h> 47 48 #define BADTIME "bad time specification" 49 50 static char *myname; 51 52 static int isnumber(char *); 53 static int atoi_for2(char *); 54 static void usage(const int); 55 static void touchabort(const char *); 56 static void parse_time(char *, timestruc_t *); 57 static void parse_datetime(char *, timestruc_t *); 58 static void timestruc_to_timeval(timestruc_t *, struct timeval *); 59 60 int 61 main(int argc, char *argv[]) 62 { 63 int c; 64 65 int aflag = 0; 66 int cflag = 0; 67 int rflag = 0; 68 int mflag = 0; 69 int tflag = 0; 70 int stflag = 0; 71 int status = 0; 72 int usecurrenttime = 1; 73 int timespecified; 74 int optc; 75 int fd; 76 struct stat stbuf; 77 struct stat prstbuf; 78 struct timeval times[2]; 79 80 (void) setlocale(LC_ALL, ""); 81 #if !defined(TEXT_DOMAIN) 82 #define TEXT_DOMAIN "SYS_TEST" 83 #endif 84 (void) textdomain(TEXT_DOMAIN); 85 86 myname = basename(argv[0]); 87 if (strcmp(myname, "settime") == NULL) { 88 cflag++; 89 stflag++; 90 while ((optc = getopt(argc, argv, "f:")) != EOF) 91 switch (optc) { 92 case 'f': 93 rflag++; 94 usecurrenttime = 0; 95 if (stat(optarg, &prstbuf) == -1) { 96 (void) fprintf(stderr, "%s: ", myname); 97 perror(optarg); 98 return (2); 99 } 100 break; 101 case '?': 102 usage(stflag); 103 break; 104 }; 105 } else 106 while ((optc = getopt(argc, argv, "acfmr:t:")) != EOF) 107 switch (optc) { 108 case 'a': 109 aflag++; 110 usecurrenttime = 0; 111 break; 112 case 'c': 113 cflag++; 114 break; 115 case 'f': /* silently ignore for UCB compat */ 116 break; 117 case 'm': 118 mflag++; 119 usecurrenttime = 0; 120 break; 121 case 'r': /* same as settime's -f option */ 122 rflag++; 123 usecurrenttime = 0; 124 if (stat(optarg, &prstbuf) == -1) { 125 (void) fprintf(stderr, "%s: ", myname); 126 perror(optarg); 127 return (2); 128 } 129 break; 130 case 't': 131 tflag++; 132 usecurrenttime = 0; 133 parse_time(optarg, &prstbuf.st_mtim); 134 prstbuf.st_atim = prstbuf.st_mtim; 135 break; 136 case '?': 137 usage(stflag); 138 break; 139 } 140 141 argc -= optind; 142 argv += optind; 143 144 if ((argc < 1) || (rflag + tflag > 1)) 145 usage(stflag); 146 147 if ((aflag == 0) && (mflag == 0)) { 148 aflag = 1; 149 mflag = 1; 150 } 151 152 /* 153 * If either -r or -t has been specified, 154 * use the specified time. 155 */ 156 timespecified = rflag || tflag; 157 158 if (timespecified == 0) { 159 if (argc >= 2 && isnumber(*argv) && (strlen(*argv) == 8 || 160 strlen(*argv) == 10)) { 161 /* 162 * time is specified as an operand. 163 * use it. 164 */ 165 parse_datetime(*argv++, &prstbuf.st_mtim); 166 prstbuf.st_atim = prstbuf.st_mtim; 167 usecurrenttime = 0; 168 timespecified = 1; 169 argc--; 170 } else { 171 /* 172 * no time information is specified. 173 * use the current time. 174 */ 175 (void) gettimeofday(times, NULL); 176 times[1] = times[0]; 177 } 178 } 179 for (c = 0; c < argc; c++) { 180 if (stat(argv[c], &stbuf)) { 181 /* 182 * If stat failed for reasons other than EOVERFLOW or 183 * ENOENT, the file should not be created, since this 184 * can clobber the contents of an existing file. 185 */ 186 if (errno == EOVERFLOW) { 187 if (aflag == 0 || mflag == 0) { 188 (void) fprintf(stderr, 189 gettext("%s: %s: current timestamps" 190 " unavailable:\n%s"), 191 myname, argv[c], aflag > 0 ? 192 gettext("consider trying again" 193 " without -a option\n") : 194 gettext("consider trying again" 195 " without -m option\n")); 196 status++; 197 continue; 198 } 199 /* 200 * Since we have EOVERFLOW, we know the file 201 * exists. Since both atime and mtime are being 202 * changed to known values, we don't care that 203 * st_atime and st_mtime from the file aren't 204 * available. 205 */ 206 } else if (errno != ENOENT) { 207 (void) fprintf(stderr, 208 gettext("%s: cannot stat %s: %s\n"), 209 myname, argv[c], strerror(errno)); 210 status++; 211 continue; 212 } else if (cflag) { 213 continue; 214 } else if ((fd = creat(argv[c], 215 (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH))) 216 < 0) { 217 (void) fprintf(stderr, 218 gettext("%s: cannot create %s: %s\n"), 219 myname, argv[c], strerror(errno)); 220 status++; 221 continue; 222 } else { 223 (void) close(fd); 224 if (stat(argv[c], &stbuf)) { 225 (void) fprintf(stderr, 226 gettext("%s: cannot stat %s: %s\n"), 227 myname, argv[c], strerror(errno)); 228 status++; 229 continue; 230 } 231 } 232 } 233 234 if (mflag == 0) { 235 /* Keep the mtime of the file */ 236 timestruc_to_timeval(&stbuf.st_mtim, times + 1); 237 } else { 238 if (timespecified) { 239 /* Set the specified time */ 240 timestruc_to_timeval(&prstbuf.st_mtim, 241 times + 1); 242 } 243 /* Otherwise, use the current time by gettimeofday */ 244 } 245 246 if (aflag == 0) { 247 /* Keep the atime of the file */ 248 timestruc_to_timeval(&stbuf.st_atim, times); 249 } else { 250 if (timespecified) { 251 /* Set the specified time */ 252 timestruc_to_timeval(&prstbuf.st_atim, times); 253 } 254 /* Otherwise, use the current time by gettimeofday */ 255 } 256 257 if (utimes(argv[c], (usecurrenttime) ? NULL : times)) { 258 (void) fprintf(stderr, 259 gettext("%s: cannot change times on %s: %s\n"), 260 myname, argv[c], strerror(errno)); 261 status++; 262 continue; 263 } 264 } 265 return (status); 266 } 267 268 static int 269 isnumber(char *s) 270 { 271 int c; 272 273 while ((c = *s++) != '\0') 274 if (!isdigit(c)) 275 return (0); 276 return (1); 277 } 278 279 280 static void 281 parse_time(char *t, timestruc_t *ts) 282 { 283 int century = 0; 284 int seconds = 0; 285 char *p; 286 time_t when; 287 struct tm tm; 288 289 /* 290 * time in the following format (defined by the touch(1) spec): 291 * [[CC]YY]MMDDhhmm[.SS] 292 */ 293 if ((p = strchr(t, '.')) != NULL) { 294 if (strchr(p+1, '.') != NULL) 295 touchabort(BADTIME); 296 seconds = atoi_for2(p+1); 297 *p = '\0'; 298 } 299 300 (void) memset(&tm, 0, sizeof (struct tm)); 301 when = time(0); 302 tm.tm_year = localtime(&when)->tm_year; 303 304 switch (strlen(t)) { 305 case 12: /* CCYYMMDDhhmm */ 306 century = atoi_for2(t); 307 t += 2; 308 /* FALLTHROUGH */ 309 case 10: /* YYMMDDhhmm */ 310 tm.tm_year = atoi_for2(t); 311 t += 2; 312 if (century == 0) { 313 if (tm.tm_year < 69) 314 tm.tm_year += 100; 315 } else 316 tm.tm_year += (century - 19) * 100; 317 /* FALLTHROUGH */ 318 case 8: /* MMDDhhmm */ 319 tm.tm_mon = atoi_for2(t) - 1; 320 t += 2; 321 tm.tm_mday = atoi_for2(t); 322 t += 2; 323 tm.tm_hour = atoi_for2(t); 324 t += 2; 325 tm.tm_min = atoi_for2(t); 326 tm.tm_sec = seconds; 327 break; 328 default: 329 touchabort(BADTIME); 330 } 331 332 if ((when = mktime(&tm)) == -1) 333 touchabort(BADTIME); 334 if (tm.tm_isdst) 335 when -= (timezone-altzone); 336 337 ts->tv_sec = when; 338 ts->tv_nsec = 0; 339 } 340 341 static void 342 parse_datetime(char *t, timestruc_t *ts) 343 { 344 time_t when; 345 struct tm tm; 346 347 /* 348 * time in the following format (defined by the touch(1) spec): 349 * MMDDhhmm[yy] 350 */ 351 352 (void) memset(&tm, 0, sizeof (struct tm)); 353 when = time(0); 354 tm.tm_year = localtime(&when)->tm_year; 355 356 switch (strlen(t)) { 357 case 10: /* MMDDhhmmyy */ 358 tm.tm_year = atoi_for2(t+8); 359 if (tm.tm_year < 69) 360 tm.tm_year += 100; 361 /* FALLTHROUGH */ 362 case 8: /* MMDDhhmm */ 363 tm.tm_mon = atoi_for2(t) - 1; 364 t += 2; 365 tm.tm_mday = atoi_for2(t); 366 t += 2; 367 tm.tm_hour = atoi_for2(t); 368 t += 2; 369 tm.tm_min = atoi_for2(t); 370 break; 371 default: 372 touchabort(BADTIME); 373 } 374 375 if ((when = mktime(&tm)) == -1) 376 touchabort(BADTIME); 377 if (tm.tm_isdst) 378 when -= (timezone - altzone); 379 380 ts->tv_sec = when; 381 ts->tv_nsec = 0; 382 } 383 384 static int 385 atoi_for2(char *p) 386 { 387 int value; 388 389 value = (*p - '0') * 10 + *(p+1) - '0'; 390 if ((value < 0) || (value > 99)) 391 touchabort(BADTIME); 392 return (value); 393 } 394 395 static void 396 touchabort(const char *message) 397 { 398 (void) fprintf(stderr, "%s: %s\n", myname, gettext(message)); 399 exit(1); 400 } 401 402 static void 403 usage(const int settime) 404 { 405 if (settime) 406 (void) fprintf(stderr, gettext( 407 "usage: %s [-f file] [mmddhhmm[yy]] file...\n"), myname); 408 else 409 (void) fprintf(stderr, gettext( 410 "usage: %s [-acm] [-r ref_file] file...\n" 411 " %s [-acm] [MMDDhhmm[yy]] file...\n" 412 " %s [-acm] [-t [[CC]YY]MMDDhhmm[.SS]] file...\n"), 413 myname, myname, myname); 414 exit(2); 415 } 416 417 /* 418 * nanoseconds are rounded off to microseconds by flooring. 419 */ 420 static void 421 timestruc_to_timeval(timestruc_t *ts, struct timeval *tv) 422 { 423 tv->tv_sec = ts->tv_sec; 424 tv->tv_usec = ts->tv_nsec / 1000; 425 } 426