xinstall.c revision 9911
1121986Sjhb/* 2121986Sjhb * Copyright (c) 1987, 1993 3121986Sjhb * The Regents of the University of California. All rights reserved. 4121986Sjhb * 5121986Sjhb * Redistribution and use in source and binary forms, with or without 6121986Sjhb * modification, are permitted provided that the following conditions 7121986Sjhb * are met: 8121986Sjhb * 1. Redistributions of source code must retain the above copyright 9121986Sjhb * notice, this list of conditions and the following disclaimer. 10121986Sjhb * 2. Redistributions in binary form must reproduce the above copyright 11121986Sjhb * notice, this list of conditions and the following disclaimer in the 12121986Sjhb * documentation and/or other materials provided with the distribution. 13121986Sjhb * 3. All advertising materials mentioning features or use of this software 14121986Sjhb * must display the following acknowledgement: 15121986Sjhb * This product includes software developed by the University of 16121986Sjhb * California, Berkeley and its contributors. 17121986Sjhb * 4. Neither the name of the University nor the names of its contributors 18121986Sjhb * may be used to endorse or promote products derived from this software 19121986Sjhb * without specific prior written permission. 20121986Sjhb * 21121986Sjhb * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22121986Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23121986Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24121986Sjhb * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25121986Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26121986Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27121986Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28121986Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29121986Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30121986Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31121986Sjhb * SUCH DAMAGE. 32121986Sjhb */ 33121986Sjhb 34121986Sjhb#ifndef lint 35121986Sjhbstatic char copyright[] = 36167240Sjhb"@(#) Copyright (c) 1987, 1993\n\ 37121986Sjhb The Regents of the University of California. All rights reserved.\n"; 38167240Sjhb#endif /* not lint */ 39121986Sjhb 40148538Sjhb#ifndef lint 41121986Sjhbstatic char sccsid[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93"; 42167240Sjhb#endif /* not lint */ 43167240Sjhb 44167240Sjhb#include <sys/param.h> 45121986Sjhb#include <sys/wait.h> 46121986Sjhb#include <sys/mman.h> 47121986Sjhb#include <sys/stat.h> 48214631Sjhb 49121986Sjhb#include <ctype.h> 50121986Sjhb#include <errno.h> 51121986Sjhb#include <fcntl.h> 52167747Sjhb#include <grp.h> 53121986Sjhb#include <paths.h> 54121986Sjhb#include <pwd.h> 55121986Sjhb#include <stdio.h> 56121986Sjhb#include <stdlib.h> 57121986Sjhb#include <string.h> 58121986Sjhb#include <unistd.h> 59121986Sjhb 60151979Sjhb#include "pathnames.h" 61151979Sjhb 62151979Sjhbstruct passwd *pp; 63151979Sjhbstruct group *gp; 64121986Sjhbint docopy, dostrip; 65151897Srwatsonint mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; 66121986Sjhbchar *group, *owner, pathbuf[MAXPATHLEN]; 67121986Sjhb 68151979Sjhb#define DIRECTORY 0x01 /* Tell install it's a directory. */ 69151979Sjhb#define SETFLAGS 0x02 /* Tell install to set flags. */ 70151979Sjhb 71151979Sjhbvoid copy __P((int, char *, int, char *, off_t)); 72151979Sjhbvoid err __P((const char *, ...)); 73151979Sjhbvoid install __P((char *, char *, u_long, u_int)); 74151979Sjhbu_long string_to_flags __P((char **, u_long *, u_long *)); 75152461Sandrevoid strip __P((char *)); 76152461Sandrevoid usage __P((void)); 77152461Sandre 78152461Sandreint 79121986Sjhbmain(argc, argv) 80121986Sjhb int argc; 81121986Sjhb char *argv[]; 82121986Sjhb{ 83151979Sjhb struct stat from_sb, to_sb; 84122124Sjhb mode_t *set; 85122124Sjhb u_long fset; 86156124Sjhb u_int iflags; 87122124Sjhb int ch, no_target; 88122124Sjhb char *flags, *to_name; 89122124Sjhb 90130980Sjhb iflags = 0; 91157541Sjhb while ((ch = getopt(argc, argv, "cf:g:m:o:s")) != EOF) 92121986Sjhb switch((char)ch) { 93121986Sjhb case 'c': 94121986Sjhb docopy = 1; 95121986Sjhb break; 96121986Sjhb case 'f': 97121986Sjhb flags = optarg; 98121986Sjhb if (string_to_flags(&flags, &fset, NULL)) 99121986Sjhb err("%s: invalid flag", flags); 100121986Sjhb iflags |= SETFLAGS; 101167747Sjhb break; 102121986Sjhb case 'g': 103121986Sjhb group = optarg; 104121986Sjhb break; 105121986Sjhb case 'm': 106121986Sjhb if (!(set = setmode(optarg))) 107121986Sjhb err("%s: invalid file mode", optarg); 108130980Sjhb mode = getmode(set, 0); 109151979Sjhb break; 110121986Sjhb case 'o': 111133017Sscottl owner = optarg; 112121986Sjhb break; 113121986Sjhb case 's': 114169391Sjhb dostrip = 1; 115121986Sjhb break; 116121986Sjhb case '?': 117128931Sjhb default: 118128931Sjhb usage(); 119255726Sgibbs } 120195249Sjhb argc -= optind; 121129964Sjhb argv += optind; 122121986Sjhb if (argc < 2) 123129097Sjhb usage(); 124121986Sjhb 125121986Sjhb /* get group and owner id's */ 126169391Sjhb if (group && !(gp = getgrnam(group))) 127169391Sjhb err("unknown group %s", group); 128156124Sjhb if (owner && !(pp = getpwnam(owner))) 129121986Sjhb err("unknown user %s", owner); 130156124Sjhb 131156124Sjhb no_target = stat(to_name = argv[argc - 1], &to_sb); 132156124Sjhb if (!no_target && (to_sb.st_mode & S_IFMT) == S_IFDIR) { 133227309Sed for (; *argv != to_name; ++argv) 134148538Sjhb install(*argv, to_name, fset, iflags | DIRECTORY); 135148538Sjhb exit(0); 136148538Sjhb } 137148538Sjhb 138148538Sjhb /* can't do file1 file2 directory/file */ 139133017Sscottl if (argc != 2) 140133017Sscottl usage(); 141133017Sscottl 142133017Sscottl if (!no_target) { 143133017Sscottl if (stat(*argv, &from_sb)) 144133017Sscottl err("%s: %s", *argv, strerror(errno)); 145121986Sjhb if (!S_ISREG(to_sb.st_mode)) 146121986Sjhb err("%s: %s", to_name, strerror(EFTYPE)); 147121986Sjhb if (to_sb.st_dev == from_sb.st_dev && 148121986Sjhb to_sb.st_ino == from_sb.st_ino) 149121986Sjhb err("%s and %s are the same file", *argv, to_name); 150121986Sjhb /* 151121986Sjhb * Unlink now... avoid ETXTBSY errors later. Try and turn 152121986Sjhb * off the append/immutable bits -- if we fail, go ahead, 153121986Sjhb * it might work. 154121986Sjhb */ 155121986Sjhb#define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND) 156121986Sjhb if (to_sb.st_flags & NOCHANGEBITS) 157121986Sjhb (void)chflags(to_name, 158121986Sjhb to_sb.st_flags & ~(NOCHANGEBITS)); 159121986Sjhb (void)unlink(to_name); 160121986Sjhb } 161121986Sjhb install(*argv, to_name, fset, iflags); 162121986Sjhb exit(0); 163130980Sjhb} 164130980Sjhb 165130980Sjhb/* 166130980Sjhb * install -- 167130980Sjhb * build a path name and install the file 168130980Sjhb */ 169130980Sjhbvoid 170130980Sjhbinstall(from_name, to_name, fset, flags) 171130980Sjhb char *from_name, *to_name; 172130980Sjhb u_long fset; 173130980Sjhb u_int flags; 174130980Sjhb{ 175130980Sjhb struct stat from_sb, to_sb; 176130980Sjhb int devnull, from_fd, to_fd, serrno; 177130980Sjhb char *p; 178130980Sjhb 179121986Sjhb /* If try to install NULL file to a directory, fails. */ 180151979Sjhb if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) { 181130980Sjhb if (stat(from_name, &from_sb)) 182130980Sjhb err("%s: %s", from_name, strerror(errno)); 183151979Sjhb if (!S_ISREG(from_sb.st_mode)) 184151979Sjhb err("%s: %s", from_name, strerror(EFTYPE)); 185130980Sjhb /* Build the target path. */ 186130980Sjhb if (flags & DIRECTORY) { 187151979Sjhb (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s", 188130980Sjhb to_name, 189130980Sjhb (p = rindex(from_name, '/')) ? ++p : from_name); 190151979Sjhb to_name = pathbuf; 191130980Sjhb } 192130980Sjhb devnull = 0; 193151979Sjhb } else { 194130980Sjhb from_sb.st_flags = 0; /* XXX */ 195130980Sjhb devnull = 1; 196130980Sjhb } 197130980Sjhb 198151979Sjhb /* 199130980Sjhb * Unlink now... avoid ETXTBSY errors later. Try and turn 200130980Sjhb * off the append/immutable bits -- if we fail, go ahead, 201130980Sjhb * it might work. 202130980Sjhb */ 203121986Sjhb if (stat(to_name, &to_sb) == 0 && 204121986Sjhb to_sb.st_flags & (NOCHANGEBITS)) 205121986Sjhb (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS)); 206121986Sjhb (void)unlink(to_name); 207121986Sjhb 208121986Sjhb /* Create target. */ 209121986Sjhb if ((to_fd = open(to_name, 210121986Sjhb O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) 211157541Sjhb err("%s: %s", to_name, strerror(errno)); 212121986Sjhb if (!devnull) { 213121986Sjhb if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) { 214121986Sjhb (void)unlink(to_name); 215121986Sjhb err("%s: %s", from_name, strerror(errno)); 216121986Sjhb } 217121986Sjhb copy(from_fd, from_name, to_fd, to_name, from_sb.st_size); 218121986Sjhb (void)close(from_fd); 219121986Sjhb } 220133017Sscottl if (dostrip) 221121986Sjhb strip(to_name); 222121986Sjhb /* 223121986Sjhb * Set owner, group, mode for target; do the chown first, 224121986Sjhb * chown may lose the setuid bits. 225121986Sjhb */ 226121986Sjhb if ((group || owner) && 227121986Sjhb fchown(to_fd, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1)) { 228157541Sjhb serrno = errno; 229121986Sjhb (void)unlink(to_name); 230121986Sjhb err("%s: chown/chgrp: %s", to_name, strerror(serrno)); 231121986Sjhb } 232121986Sjhb if (fchmod(to_fd, mode)) { 233133017Sscottl serrno = errno; 234133017Sscottl (void)unlink(to_name); 235133017Sscottl err("%s: chmod: %s", to_name, strerror(serrno)); 236133017Sscottl } 237121986Sjhb 238121986Sjhb /* 239121986Sjhb * If provided a set of flags, set them, otherwise, preserve the 240121986Sjhb * flags, except for the dump flag. 241121986Sjhb */ 242121986Sjhb if (fchflags(to_fd, 243122148Sjhb flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { 244133017Sscottl serrno = errno; 245121986Sjhb (void)unlink(to_name); 246121986Sjhb err("%s: chflags: %s", to_name, strerror(serrno)); 247121986Sjhb } 248129964Sjhb 249129964Sjhb (void)close(to_fd); 250129964Sjhb if (!docopy && !devnull && unlink(from_name)) 251129964Sjhb err("%s: %s", from_name, strerror(errno)); 252129964Sjhb} 253129964Sjhb 254129964Sjhb/* 255129964Sjhb * copy -- 256129964Sjhb * copy from one file to another 257151979Sjhb */ 258151979Sjhbvoid 259151979Sjhbcopy(from_fd, from_name, to_fd, to_name, size) 260151979Sjhb register int from_fd, to_fd; 261208915Sjhb char *from_name, *to_name; 262151979Sjhb off_t size; 263151979Sjhb{ 264129964Sjhb register int nr, nw; 265129964Sjhb int serrno; 266129964Sjhb char *p, buf[MAXBSIZE]; 267129964Sjhb 268129964Sjhb /* 269129964Sjhb * Mmap and write if less than 8M (the limit is so we don't totally 270129964Sjhb * trash memory on big files. This is really a minor hack, but it 271129964Sjhb * wins some CPU back. 272129964Sjhb */ 273129964Sjhb if (size <= 8 * 1048576) { 274156124Sjhb if ((p = mmap(NULL, (size_t)size, PROT_READ, 275156124Sjhb 0, from_fd, (off_t)0)) == (char *)-1) 276129964Sjhb err("%s: %s", from_name, strerror(errno)); 277129964Sjhb if (write(to_fd, p, size) != size) 278129964Sjhb err("%s: %s", to_name, strerror(errno)); 279129964Sjhb } else { 280129964Sjhb while ((nr = read(from_fd, buf, sizeof(buf))) > 0) 281129964Sjhb if ((nw = write(to_fd, buf, nr)) != nr) { 282129964Sjhb serrno = errno; 283129964Sjhb (void)unlink(to_name); 284129964Sjhb err("%s: %s", 285129964Sjhb to_name, strerror(nw > 0 ? EIO : serrno)); 286129964Sjhb } 287129964Sjhb if (nr != 0) { 288151979Sjhb serrno = errno; 289151979Sjhb (void)unlink(to_name); 290129964Sjhb err("%s: %s", from_name, strerror(serrno)); 291148538Sjhb } 292129964Sjhb } 293129964Sjhb} 294151979Sjhb 295129964Sjhb/* 296129964Sjhb * strip -- 297129964Sjhb * use strip(1) to strip the target file 298129964Sjhb */ 299151979Sjhbvoid 300129964Sjhbstrip(to_name) 301129964Sjhb char *to_name; 302129964Sjhb{ 303129964Sjhb int serrno, status; 304129964Sjhb 305151979Sjhb switch (vfork()) { 306151979Sjhb case -1: 307156124Sjhb serrno = errno; 308129964Sjhb (void)unlink(to_name); 309129964Sjhb err("forks: %s", strerror(errno)); 310129964Sjhb case 0: 311129964Sjhb execl(_PATH_STRIP, "strip", to_name, NULL); 312129964Sjhb err("%s: %s", _PATH_STRIP, strerror(errno)); 313129964Sjhb default: 314129964Sjhb if (wait(&status) == -1) 315243764Savg (void)unlink(to_name); 316243764Savg } 317129964Sjhb} 318129964Sjhb 319195249Sjhb/* 320156124Sjhb * usage -- 321121986Sjhb * print a usage message and die 322156124Sjhb */ 323156124Sjhbvoid 324195415Sjhbusage() 325187880Sjeff{ 326121986Sjhb (void)fprintf(stderr, 327187880Sjeff"usage: install [-cs] [-f flags] [-g group] [-m mode] [-o owner] file1 file2;\n\tor file1 ... fileN directory\n"); 328187880Sjeff exit(1); 329187880Sjeff} 330187880Sjeff 331187880Sjeff#if __STDC__ 332187880Sjeff#include <stdarg.h> 333187880Sjeff#else 334187880Sjeff#include <varargs.h> 335187880Sjeff#endif 336187880Sjeff 337187880Sjeffvoid 338187880Sjeff#if __STDC__ 339195249Sjhberr(const char *fmt, ...) 340187880Sjeff#else 341187880Sjefferr(fmt, va_alist) 342187880Sjeff char *fmt; 343187880Sjeff va_dcl 344187880Sjeff#endif 345195415Sjhb{ 346195415Sjhb va_list ap; 347195249Sjhb#if __STDC__ 348195249Sjhb va_start(ap, fmt); 349208915Sjhb#else 350208915Sjhb va_start(ap); 351208915Sjhb#endif 352208915Sjhb (void)fprintf(stderr, "install: "); 353208915Sjhb (void)vfprintf(stderr, fmt, ap); 354208915Sjhb va_end(ap); 355208915Sjhb (void)fprintf(stderr, "\n"); 356208991Smav exit(1); 357208915Sjhb /* NOTREACHED */ 358208915Sjhb} 359216679Sjhb