1 /* $NetBSD: xinstall.c,v 1.42 2000/10/10 14:30:41 enami Exp $ */ 2 3 /* 4 * Copyright (c) 1987, 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. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 __COPYRIGHT("@(#) Copyright (c) 1987, 1993\n\ 39 The Regents of the University of California. All rights reserved.\n"); 40 #endif /* not lint */ 41 42 #ifndef lint 43 #if 0 44 static char sccsid[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93"; 45 #else 46 __RCSID("$NetBSD: xinstall.c,v 1.42 2000/10/10 14:30:41 enami Exp $"); 47 #endif 48 #endif /* not lint */ 49 50 #include <sys/param.h> 51 #include <sys/wait.h> 52 #include <sys/mman.h> 53 #include <sys/stat.h> 54 55 #include <ctype.h> 56 #include <errno.h> 57 #include <fcntl.h> 58 #include <grp.h> 59 #include <paths.h> 60 #include <pwd.h> 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 #include <unistd.h> 65 #include <err.h> 66 67 #include "pathnames.h" 68 #include "stat_flags.h" 69 70 #define STRIP_ARGS_MAX 32 71 #define BACKUP_SUFFIX ".old" 72 73 struct passwd *pp; 74 struct group *gp; 75 int dobackup=0, docopy=0, dodir=0, dostrip=0, dolink=0, dopreserve=0, 76 dorename = 0, unpriv = 0; 77 static int numberedbackup = 0; 78 int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; 79 char pathbuf[MAXPATHLEN]; 80 uid_t uid; 81 gid_t gid; 82 char *stripArgs=NULL; 83 char *suffix=BACKUP_SUFFIX; 84 85 #define LN_ABSOLUTE 0x01 86 #define LN_RELATIVE 0x02 87 #define LN_HARD 0x04 88 #define LN_SYMBOLIC 0x08 89 #define LN_MIXED 0x10 90 91 #define DIRECTORY 0x01 /* Tell install it's a directory. */ 92 #define SETFLAGS 0x02 /* Tell install to set flags. */ 93 94 void copy __P((int, char *, int, char *, off_t)); 95 void makelink __P((char *, char *)); 96 void install __P((char *, char *, u_long, u_int)); 97 void install_dir __P((char *)); 98 void strip __P((char *)); 99 void backup __P((const char *)); 100 void usage __P((void)); 101 int main __P((int, char *[])); 102 103 int 104 main(argc, argv) 105 int argc; 106 char *argv[]; 107 { 108 struct stat from_sb, to_sb; 109 void *set; 110 u_long fset; 111 u_int iflags; 112 int ch, no_target; 113 char *p; 114 char *flags = NULL, *to_name, *group = NULL, *owner = NULL; 115 116 iflags = 0; 117 while ((ch = getopt(argc, argv, "cbB:df:g:l:m:o:prsS:U")) != -1) 118 switch((char)ch) { 119 case 'B': 120 suffix = optarg; 121 numberedbackup = 0; 122 { 123 /* Check if given suffix really generates 124 different suffixes - catch e.g. ".%" */ 125 char suffix_expanded0[FILENAME_MAX], 126 suffix_expanded1[FILENAME_MAX]; 127 (void)snprintf(suffix_expanded0, FILENAME_MAX, 128 suffix, 0); 129 (void)snprintf(suffix_expanded1, FILENAME_MAX, 130 suffix, 1); 131 if (strcmp(suffix_expanded0, suffix_expanded1) 132 != 0) 133 numberedbackup = 1; 134 } 135 /* fall through; -B implies -b */ 136 case 'b': 137 dobackup = 1; 138 break; 139 case 'c': 140 docopy = 1; 141 break; 142 case 'd': 143 dodir = 1; 144 break; 145 case 'f': 146 flags = optarg; 147 if (string_to_flags(&flags, &fset, NULL)) 148 errx(1, "%s: invalid flag", flags); 149 iflags |= SETFLAGS; 150 break; 151 case 'g': 152 group = optarg; 153 break; 154 case 'l': 155 for (p = optarg; *p; p++) 156 switch (*p) { 157 case 's': 158 dolink &= ~(LN_HARD|LN_MIXED); 159 dolink |= LN_SYMBOLIC; 160 break; 161 case 'h': 162 dolink &= ~(LN_SYMBOLIC|LN_MIXED); 163 dolink |= LN_HARD; 164 break; 165 case 'm': 166 dolink &= ~(LN_SYMBOLIC|LN_HARD); 167 dolink |= LN_MIXED; 168 break; 169 case 'a': 170 dolink &= ~LN_RELATIVE; 171 dolink |= LN_ABSOLUTE; 172 break; 173 case 'r': 174 dolink &= ~LN_ABSOLUTE; 175 dolink |= LN_RELATIVE; 176 break; 177 default: 178 errx(1, "%c: invalid link type", *p); 179 break; 180 } 181 break; 182 case 'm': 183 if (!(set = setmode(optarg))) 184 errx(1, "%s: invalid file mode", optarg); 185 mode = getmode(set, 0); 186 free(set); 187 break; 188 case 'o': 189 owner = optarg; 190 break; 191 case 'p': 192 dopreserve = 1; 193 break; 194 case 'r': 195 dorename = 1; 196 break; 197 case 'S': 198 stripArgs = (char*)malloc(sizeof(char)*(strlen(optarg)+1)); 199 strcpy(stripArgs,optarg); 200 /* fall through; -S implies -s */ 201 case 's': 202 dostrip = 1; 203 break; 204 case 'U': 205 unpriv = 1; 206 break; 207 case '?': 208 default: 209 usage(); 210 } 211 argc -= optind; 212 argv += optind; 213 214 /* strip and link options make no sense when creating directories */ 215 if ((dostrip || dolink) && dodir) 216 usage(); 217 218 /* strip and flags make no sense with links */ 219 if ((dostrip || flags) && dolink) 220 usage(); 221 222 /* must have at least two arguments, except when creating directories */ 223 if (argc < 2 && !dodir) 224 usage(); 225 226 /* get group and owner id's */ 227 if (unpriv) 228 uid = gid = -1; 229 else { 230 if (group && !(gp = getgrnam(group)) && 231 !isdigit((unsigned char)*group)) 232 errx(1, "unknown group %s", group); 233 gid = (group) ? ((gp) ? gp->gr_gid : atoi(group)) : -1; 234 if (owner && !(pp = getpwnam(owner)) && 235 !isdigit((unsigned char)*owner)) 236 errx(1, "unknown user %s", owner); 237 uid = (owner) ? ((pp) ? pp->pw_uid : atoi(owner)) : -1; 238 } 239 240 if (dodir) { 241 for (; *argv != NULL; ++argv) 242 install_dir(*argv); 243 exit (0); 244 } 245 246 no_target = stat(to_name = argv[argc - 1], &to_sb); 247 if (!no_target && S_ISDIR(to_sb.st_mode)) { 248 for (; *argv != to_name; ++argv) 249 install(*argv, to_name, fset, iflags | DIRECTORY); 250 exit(0); 251 } 252 253 /* can't do file1 file2 directory/file */ 254 if (argc != 2) 255 usage(); 256 257 if (!no_target) { 258 if (stat(*argv, &from_sb)) 259 err(1, "%s", *argv); 260 if (!S_ISREG(to_sb.st_mode)) 261 errx(1, "%s: not a regular file", to_name); 262 if (!dolink && to_sb.st_dev == from_sb.st_dev && 263 to_sb.st_ino == from_sb.st_ino) 264 errx(1, "%s and %s are the same file", *argv, to_name); 265 /* 266 * Unlink now... avoid ETXTBSY errors later. Try and turn 267 * off the append/immutable bits -- if we fail, go ahead, 268 * it might work. 269 */ 270 #define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND) 271 if (to_sb.st_flags & NOCHANGEBITS) 272 (void)chflags(to_name, 273 to_sb.st_flags & ~(NOCHANGEBITS)); 274 if (dobackup) 275 backup(to_name); 276 else if (!dorename) 277 (void)unlink(to_name); 278 } 279 install(*argv, to_name, fset, iflags); 280 exit(0); 281 } 282 283 /* 284 * makelink -- 285 * make a link from source to destination 286 */ 287 void 288 makelink(from_name, to_name) 289 char *from_name; 290 char *to_name; 291 { 292 char src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN]; 293 294 /* Try hard links first */ 295 if (dolink & (LN_HARD|LN_MIXED)) { 296 if (link(from_name, to_name) == -1) { 297 if ((dolink & LN_HARD) || errno != EXDEV) 298 err(1, "link %s -> %s", from_name, to_name); 299 } 300 else 301 return; 302 } 303 304 /* Symbolic links */ 305 if (dolink & LN_ABSOLUTE) { 306 /* Convert source path to absolute */ 307 if (realpath(from_name, src) == NULL) 308 err(1, "%s", src); 309 if (symlink(src, to_name) == -1) 310 err(1, "symlink %s -> %s", src, to_name); 311 return; 312 } 313 314 if (dolink & LN_RELATIVE) { 315 char *s, *d; 316 317 /* Resolve pathnames */ 318 if (realpath(from_name, src) == NULL) 319 err(1, "%s", src); 320 if (realpath(to_name, dst) == NULL) 321 err(1, "%s", dst); 322 323 /* trim common path components */ 324 for (s = src, d = dst; *s == *d; s++, d++) 325 continue; 326 while (*s != '/') 327 s--, d--; 328 329 /* count the number of directories we need to backtrack */ 330 for (++d, lnk[0] = '\0'; *d; d++) 331 if (*d == '/') 332 (void) strcat(lnk, "../"); 333 334 (void) strcat(lnk, ++s); 335 336 if (symlink(lnk, dst) == -1) 337 err(1, "symlink %s -> %s", lnk, dst); 338 return; 339 } 340 341 /* 342 * If absolute or relative was not specified, 343 * try the names the user provided 344 */ 345 if (symlink(from_name, to_name) == -1) 346 err(1, "symlink %s -> %s", from_name, to_name); 347 348 } 349 350 /* 351 * install -- 352 * build a path name and install the file 353 */ 354 void 355 install(from_name, to_name, fset, flags) 356 char *from_name, *to_name; 357 u_long fset; 358 u_int flags; 359 { 360 struct stat from_sb, to_sb; 361 int devnull, from_fd, to_fd, serrno; 362 char *p, tmpl[MAXPATHLEN], *oto_name; 363 364 /* If try to install NULL file to a directory, fails. */ 365 if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) { 366 if (stat(from_name, &from_sb)) 367 err(1, "%s", from_name); 368 if (!S_ISREG(from_sb.st_mode)) 369 errx(1, "%s: not a regular file", to_name); 370 /* Build the target path. */ 371 if (flags & DIRECTORY) { 372 (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s", 373 to_name, 374 (p = strrchr(from_name, '/')) ? ++p : from_name); 375 to_name = pathbuf; 376 } 377 devnull = 0; 378 } else { 379 from_sb.st_flags = 0; /* XXX */ 380 devnull = 1; 381 } 382 383 /* 384 * Unlink now... avoid ETXTBSY errors later. Try and turn 385 * off the append/immutable bits -- if we fail, go ahead, 386 * it might work. 387 */ 388 if (stat(to_name, &to_sb) == 0 && 389 to_sb.st_flags & (NOCHANGEBITS)) 390 (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS)); 391 if (dorename) { 392 char *ptr, c, *dir; 393 394 if ((ptr = strrchr(to_name, '/')) != NULL) { 395 c = *++ptr; 396 *ptr = '\0'; 397 dir = to_name; 398 } else { 399 c = '\0'; /* pacify gcc */ 400 dir = tmpl; 401 *dir = '\0'; 402 } 403 (void)snprintf(tmpl, sizeof(tmpl), "%sinst.XXXXXX", dir); 404 if (ptr) 405 *ptr = c; 406 oto_name = to_name; 407 to_name = tmpl; 408 409 } else { 410 oto_name = NULL; /* pacify gcc */ 411 if (dobackup) 412 backup(to_name); 413 else 414 (void)unlink(to_name); 415 } 416 417 if (dolink) { 418 makelink(from_name, to_name); 419 return; 420 } 421 422 /* Create target. */ 423 if (dorename) { 424 if ((to_fd = mkstemp(to_name)) == -1) 425 err(1, "%s", to_name); 426 } else { 427 if ((to_fd = open(to_name, 428 O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) 429 err(1, "%s", to_name); 430 } 431 if (!devnull) { 432 if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) { 433 (void)unlink(to_name); 434 err(1, "%s", from_name); 435 } 436 copy(from_fd, from_name, to_fd, to_name, from_sb.st_size); 437 (void)close(from_fd); 438 } 439 440 if (dostrip) { 441 strip(to_name); 442 443 /* 444 * Re-open our fd on the target, in case we used a strip 445 * that does not work in-place -- like gnu binutils strip. 446 */ 447 close(to_fd); 448 if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0) 449 err(1, "stripping %s", to_name); 450 } 451 452 /* 453 * Set owner, group, mode for target; do the chown first, 454 * chown may lose the setuid bits. 455 */ 456 if ((gid != -1 || uid != -1) && fchown(to_fd, uid, gid)) { 457 serrno = errno; 458 (void)unlink(to_name); 459 errx(1, "%s: chown/chgrp: %s", to_name, strerror(serrno)); 460 } 461 if (fchmod(to_fd, mode)) { 462 serrno = errno; 463 (void)unlink(to_name); 464 errx(1, "%s: chmod: %s", to_name, strerror(serrno)); 465 } 466 467 /* 468 * If provided a set of flags, set them, otherwise, preserve the 469 * flags, except for the dump flag. 470 */ 471 if (fchflags(to_fd, 472 flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { 473 if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0) 474 warn("%s: chflags", to_name); 475 } 476 477 /* 478 * Preserve the date of the source file. 479 */ 480 if (dopreserve) { 481 struct timeval tv[2]; 482 483 #ifdef BSD4_4 484 TIMESPEC_TO_TIMEVAL(&tv[0], &from_sb.st_atimespec); 485 TIMESPEC_TO_TIMEVAL(&tv[1], &from_sb.st_mtimespec); 486 #else 487 tv[0].tv_sec = from_sb.st_atime; 488 tv[0].tv_usec = 0; 489 tv[1].tv_sec = from_sb.st_mtime; 490 tv[1].tv_usec = 0; 491 #endif 492 if (futimes(to_fd, tv) == -1) 493 warn("%s: futimes", to_name); 494 } 495 496 (void)close(to_fd); 497 498 if (dorename) 499 if (rename(to_name, oto_name) == -1) 500 err(1, "%s: rename", to_name); 501 502 if (!docopy && !devnull && unlink(from_name)) 503 err(1, "%s", from_name); 504 } 505 506 /* 507 * copy -- 508 * copy from one file to another 509 */ 510 void 511 copy(from_fd, from_name, to_fd, to_name, size) 512 int from_fd, to_fd; 513 char *from_name, *to_name; 514 off_t size; 515 { 516 ssize_t nr, nw; 517 int serrno; 518 char *p; 519 char buf[MAXBSIZE]; 520 521 /* 522 * There's no reason to do anything other than close the file 523 * now if it's empty, so let's not bother. 524 */ 525 if (size > 0) { 526 /* 527 * Mmap and write if less than 8M (the limit is so we don't totally 528 * trash memory on big files). This is really a minor hack, but it 529 * wins some CPU back. 530 */ 531 if (size <= 8 * 1048576) { 532 if ((p = mmap(NULL, (size_t)size, PROT_READ, 533 MAP_FILE|MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) { 534 serrno = errno; 535 (void)unlink(to_name); 536 errx(1, "%s: %s", from_name, strerror(serrno)); 537 } 538 if (madvise(p, (size_t)size, MADV_SEQUENTIAL) == -1 539 && errno != EOPNOTSUPP) 540 warnx("madvise: %s", strerror(errno)); 541 542 if (write(to_fd, p, size) != size) { 543 serrno = errno; 544 (void)unlink(to_name); 545 errx(1, "%s: %s", 546 to_name, strerror(serrno)); 547 } 548 } else { 549 while ((nr = read(from_fd, buf, sizeof(buf))) > 0) { 550 if ((nw = write(to_fd, buf, nr)) != nr) { 551 serrno = errno; 552 (void)unlink(to_name); 553 errx(1, "%s: %s", 554 to_name, strerror(nw > 0 ? EIO : serrno)); 555 } 556 } 557 if (nr != 0) { 558 serrno = errno; 559 (void)unlink(to_name); 560 errx(1, "%s: %s", from_name, strerror(serrno)); 561 } 562 } 563 } 564 } 565 566 /* 567 * strip -- 568 * use strip(1) to strip the target file 569 */ 570 void 571 strip(to_name) 572 char *to_name; 573 { 574 int serrno, status; 575 char *stripprog; 576 577 switch (vfork()) { 578 case -1: 579 serrno = errno; 580 (void)unlink(to_name); 581 errx(1, "vfork: %s", strerror(serrno)); 582 case 0: 583 stripprog = getenv("STRIP"); 584 if (stripprog == NULL) 585 stripprog = _PATH_STRIP; 586 587 if (stripArgs) { 588 /* build up a command line and let /bin/sh parse the arguments */ 589 char* cmd = (char*)malloc(sizeof(char)* 590 (3+strlen(stripprog)+ 591 strlen(stripArgs)+ 592 strlen(to_name))); 593 594 sprintf(cmd, "%s %s %s", stripprog, stripArgs, to_name); 595 596 execl(_PATH_BSHELL, "sh", "-c", cmd, NULL); 597 } else 598 execlp(stripprog, "strip", to_name, NULL); 599 600 warn("%s", stripprog); 601 _exit(1); 602 default: 603 if (wait(&status) == -1 || status) 604 (void)unlink(to_name); 605 } 606 } 607 608 /* 609 * backup file "to_name" to to_name.suffix 610 * if suffix contains a "%", it's taken as a printf(3) pattern 611 * used for a numbered backup. 612 */ 613 void 614 backup(to_name) 615 const char *to_name; 616 { 617 char backup[FILENAME_MAX]; 618 619 if (numberedbackup) { 620 /* Do numbered backup */ 621 int cnt; 622 char suffix_expanded[FILENAME_MAX]; 623 624 cnt=0; 625 do { 626 (void)snprintf(suffix_expanded, FILENAME_MAX, suffix, cnt); 627 (void)snprintf(backup, FILENAME_MAX, "%s%s", 628 to_name, suffix_expanded); 629 cnt++; 630 } while (access(backup, F_OK)==0); 631 } else { 632 /* Do simple backup */ 633 (void)snprintf(backup, FILENAME_MAX, "%s%s", to_name, suffix); 634 } 635 636 (void)rename(to_name, backup); 637 } 638 639 /* 640 * install_dir -- 641 * build directory heirarchy 642 */ 643 void 644 install_dir(path) 645 char *path; 646 { 647 char *p; 648 struct stat sb; 649 int ch; 650 651 for (p = path;; ++p) 652 if (!*p || (p != path && *p == '/')) { 653 ch = *p; 654 *p = '\0'; 655 if (stat(path, &sb)) { 656 if (errno != ENOENT || mkdir(path, 0777) < 0) { 657 err(1, "%s", path); 658 /* NOTREACHED */ 659 } 660 } 661 if (!(*p = ch)) 662 break; 663 } 664 665 if (((gid != -1 || uid != -1) && chown(path, uid, gid)) || 666 chmod(path, mode)) { 667 warn("%s", path); 668 } 669 } 670 671 /* 672 * usage -- 673 * print a usage message and die 674 */ 675 void 676 usage() 677 { 678 (void)fprintf(stderr, "\ 679 usage: install [-Ubcps] [-B suffix] [-f flags] [-m mode] [-o owner] [-g group] [-l linkflags] [-S stripflags] file1 file2\n\ 680 install [-Ubcps] [-B suffix] [-f flags] [-m mode] [-o owner] [-g group] [-l linkflags] [-S stripflags] file1 ... fileN directory\n\ 681 install [-Up] -d [-m mode] [-o owner] [-g group] directory ...\n"); 682 exit(1); 683 } 684