]>
Commit | Line | Data |
---|---|---|
44a7a5ab A |
1 | /* $NetBSD: xinstall.c,v 1.27 1998/10/01 18:23:52 erh 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.27 1998/10/01 18:23:52 erh 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 | ||
69 | #define STRIP_ARGS_MAX 32 | |
70 | ||
71 | struct passwd *pp; | |
72 | struct group *gp; | |
73 | int docopy=0, dodir=0, dostrip=0, dolink=0, dopreserve=0; | |
74 | int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; | |
75 | char pathbuf[MAXPATHLEN]; | |
76 | uid_t uid; | |
77 | gid_t gid; | |
78 | char *stripArgs=NULL; | |
79 | ||
80 | #define LN_ABSOLUTE 0x01 | |
81 | #define LN_RELATIVE 0x02 | |
82 | #define LN_HARD 0x04 | |
83 | #define LN_SYMBOLIC 0x08 | |
84 | #define LN_MIXED 0x10 | |
85 | ||
86 | #define DIRECTORY 0x01 /* Tell install it's a directory. */ | |
87 | #define SETFLAGS 0x02 /* Tell install to set flags. */ | |
88 | ||
89 | void copy __P((int, char *, int, char *, off_t)); | |
90 | void makelink __P((char *, char *)); | |
91 | void install __P((char *, char *, u_long, u_int)); | |
92 | void install_dir __P((char *)); | |
93 | u_long string_to_flags __P((char **, u_long *, u_long *)); | |
94 | void strip __P((char *)); | |
95 | void usage __P((void)); | |
96 | int main __P((int, char *[])); | |
97 | ||
98 | int | |
99 | main(argc, argv) | |
100 | int argc; | |
101 | char *argv[]; | |
102 | { | |
103 | struct stat from_sb, to_sb; | |
104 | mode_t *set; | |
105 | u_long fset; | |
106 | u_int iflags; | |
107 | int ch, no_target; | |
108 | char *p; | |
109 | char *flags = NULL, *to_name, *group = NULL, *owner = NULL; | |
110 | ||
111 | iflags = 0; | |
112 | while ((ch = getopt(argc, argv, "cdf:g:l:m:o:psS:")) != -1) | |
113 | switch((char)ch) { | |
114 | case 'c': | |
115 | docopy = 1; | |
116 | break; | |
117 | case 'd': | |
118 | dodir = 1; | |
119 | break; | |
120 | case 'f': | |
121 | flags = optarg; | |
122 | if (string_to_flags(&flags, &fset, NULL)) | |
123 | errx(1, "%s: invalid flag", flags); | |
124 | iflags |= SETFLAGS; | |
125 | break; | |
126 | case 'g': | |
127 | group = optarg; | |
128 | break; | |
129 | case 'l': | |
130 | for (p = optarg; *p; p++) | |
131 | switch (*p) { | |
132 | case 's': | |
133 | dolink &= ~(LN_HARD|LN_MIXED); | |
134 | dolink |= LN_SYMBOLIC; | |
135 | break; | |
136 | case 'h': | |
137 | dolink &= ~(LN_SYMBOLIC|LN_MIXED); | |
138 | dolink |= LN_HARD; | |
139 | break; | |
140 | case 'm': | |
141 | dolink &= ~(LN_SYMBOLIC|LN_HARD); | |
142 | dolink |= LN_MIXED; | |
143 | break; | |
144 | case 'a': | |
145 | dolink &= ~LN_RELATIVE; | |
146 | dolink |= LN_ABSOLUTE; | |
147 | break; | |
148 | case 'r': | |
149 | dolink &= ~LN_ABSOLUTE; | |
150 | dolink |= LN_RELATIVE; | |
151 | break; | |
152 | default: | |
153 | errx(1, "%c: invalid link type", *p); | |
154 | break; | |
155 | } | |
156 | break; | |
157 | case 'm': | |
158 | if (!(set = setmode(optarg))) | |
159 | errx(1, "%s: invalid file mode", optarg); | |
160 | mode = getmode(set, 0); | |
161 | break; | |
162 | case 'o': | |
163 | owner = optarg; | |
164 | break; | |
165 | case 'p': | |
166 | dopreserve = 1; | |
167 | break; | |
168 | case 'S': | |
169 | stripArgs = (char*)malloc(sizeof(char)*(strlen(optarg)+1)); | |
170 | strcpy(stripArgs,optarg); | |
171 | /* fall through; -S implies -s */ | |
172 | case 's': | |
173 | dostrip = 1; | |
174 | break; | |
175 | case '?': | |
176 | default: | |
177 | usage(); | |
178 | } | |
179 | argc -= optind; | |
180 | argv += optind; | |
181 | ||
182 | /* strip and link options make no sense when creating directories */ | |
183 | if ((dostrip || dolink) && dodir) | |
184 | usage(); | |
185 | ||
186 | /* strip and flags make no sense with links */ | |
187 | if ((dostrip || flags) && dolink) | |
188 | usage(); | |
189 | ||
190 | /* must have at least two arguments, except when creating directories */ | |
191 | if (argc < 2 && !dodir) | |
192 | usage(); | |
193 | ||
194 | /* get group and owner id's */ | |
195 | if (group && !(gp = getgrnam(group)) && !isdigit(*group)) | |
196 | errx(1, "unknown group %s", group); | |
197 | gid = (group) ? ((gp) ? gp->gr_gid : atoi(group)) : -1; | |
198 | if (owner && !(pp = getpwnam(owner)) && !isdigit(*owner)) | |
199 | errx(1, "unknown user %s", owner); | |
200 | uid = (owner) ? ((pp) ? pp->pw_uid : atoi(owner)) : -1; | |
201 | ||
202 | if (dodir) { | |
203 | for (; *argv != NULL; ++argv) | |
204 | install_dir(*argv); | |
205 | exit (0); | |
206 | } | |
207 | ||
208 | no_target = stat(to_name = argv[argc - 1], &to_sb); | |
209 | if (!no_target && S_ISDIR(to_sb.st_mode)) { | |
210 | for (; *argv != to_name; ++argv) | |
211 | install(*argv, to_name, fset, iflags | DIRECTORY); | |
212 | exit(0); | |
213 | } | |
214 | ||
215 | /* can't do file1 file2 directory/file */ | |
216 | if (argc != 2) | |
217 | usage(); | |
218 | ||
219 | if (!no_target) { | |
220 | if (stat(*argv, &from_sb)) | |
221 | err(1, "%s", *argv); | |
222 | if (!S_ISREG(to_sb.st_mode)) | |
223 | errx(1, "%s: %s", to_name, strerror(EFTYPE)); | |
224 | if (!dolink && to_sb.st_dev == from_sb.st_dev && | |
225 | to_sb.st_ino == from_sb.st_ino) | |
226 | errx(1, "%s and %s are the same file", *argv, to_name); | |
227 | /* | |
228 | * Unlink now... avoid ETXTBSY errors later. Try and turn | |
229 | * off the append/immutable bits -- if we fail, go ahead, | |
230 | * it might work. | |
231 | */ | |
232 | #define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND) | |
233 | if (to_sb.st_flags & NOCHANGEBITS) | |
234 | (void)chflags(to_name, | |
235 | to_sb.st_flags & ~(NOCHANGEBITS)); | |
236 | (void)unlink(to_name); | |
237 | } | |
238 | install(*argv, to_name, fset, iflags); | |
239 | exit(0); | |
240 | } | |
241 | ||
242 | /* | |
243 | * makelink -- | |
244 | * make a link from source to destination | |
245 | */ | |
246 | void | |
247 | makelink(from_name, to_name) | |
248 | char *from_name; | |
249 | char *to_name; | |
250 | { | |
251 | char src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN]; | |
252 | ||
253 | /* Try hard links first */ | |
254 | if (dolink & (LN_HARD|LN_MIXED)) { | |
255 | if (link(from_name, to_name) == -1) { | |
256 | if ((dolink & LN_HARD) || errno != EXDEV) | |
257 | err(1, "link %s -> %s", from_name, to_name); | |
258 | } | |
259 | else | |
260 | return; | |
261 | } | |
262 | ||
263 | /* Symbolic links */ | |
264 | if (dolink & LN_ABSOLUTE) { | |
265 | /* Convert source path to absolute */ | |
266 | if (realpath(from_name, src) == NULL) | |
267 | err(1, "%s", src); | |
268 | if (symlink(src, to_name) == -1) | |
269 | err(1, "symlink %s -> %s", src, to_name); | |
270 | return; | |
271 | } | |
272 | ||
273 | if (dolink & LN_RELATIVE) { | |
274 | char *s, *d; | |
275 | ||
276 | /* Resolve pathnames */ | |
277 | if (realpath(from_name, src) == NULL) | |
278 | err(1, "%s", src); | |
279 | if (realpath(to_name, dst) == NULL) | |
280 | err(1, "%s", dst); | |
281 | ||
282 | /* trim common path components */ | |
283 | for (s = src, d = dst; *s == *d; s++, d++) | |
284 | continue; | |
285 | while (*s != '/') | |
286 | s--, d--; | |
287 | ||
288 | /* count the number of directories we need to backtrack */ | |
289 | for (++d, lnk[0] = '\0'; *d; d++) | |
290 | if (*d == '/') | |
291 | (void) strcat(lnk, "../"); | |
292 | ||
293 | (void) strcat(lnk, ++s); | |
294 | ||
295 | if (symlink(lnk, dst) == -1) | |
296 | err(1, "symlink %s -> %s", lnk, dst); | |
297 | return; | |
298 | } | |
299 | ||
300 | /* | |
301 | * If absolute or relative was not specified, | |
302 | * try the names the user provided | |
303 | */ | |
304 | if (symlink(from_name, to_name) == -1) | |
305 | err(1, "symlink %s -> %s", from_name, to_name); | |
306 | ||
307 | } | |
308 | ||
309 | /* | |
310 | * install -- | |
311 | * build a path name and install the file | |
312 | */ | |
313 | void | |
314 | install(from_name, to_name, fset, flags) | |
315 | char *from_name, *to_name; | |
316 | u_long fset; | |
317 | u_int flags; | |
318 | { | |
319 | struct stat from_sb, to_sb; | |
320 | int devnull, from_fd, to_fd, serrno; | |
321 | char *p; | |
322 | ||
323 | /* If try to install NULL file to a directory, fails. */ | |
324 | if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) { | |
325 | if (stat(from_name, &from_sb)) | |
326 | err(1, "%s", from_name); | |
327 | if (!S_ISREG(from_sb.st_mode)) | |
328 | errx(1, "%s: %s", from_name, strerror(EFTYPE)); | |
329 | /* Build the target path. */ | |
330 | if (flags & DIRECTORY) { | |
331 | (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s", | |
332 | to_name, | |
333 | (p = strrchr(from_name, '/')) ? ++p : from_name); | |
334 | to_name = pathbuf; | |
335 | } | |
336 | devnull = 0; | |
337 | } else { | |
338 | from_sb.st_flags = 0; /* XXX */ | |
339 | devnull = 1; | |
340 | } | |
341 | ||
342 | /* | |
343 | * Unlink now... avoid ETXTBSY errors later. Try and turn | |
344 | * off the append/immutable bits -- if we fail, go ahead, | |
345 | * it might work. | |
346 | */ | |
347 | if (stat(to_name, &to_sb) == 0 && | |
348 | to_sb.st_flags & (NOCHANGEBITS)) | |
349 | (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS)); | |
350 | (void)unlink(to_name); | |
351 | ||
352 | if (dolink) { | |
353 | makelink(from_name, to_name); | |
354 | return; | |
355 | } | |
356 | ||
357 | /* Create target. */ | |
358 | if ((to_fd = open(to_name, | |
359 | O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) | |
360 | err(1, "%s", to_name); | |
361 | if (!devnull) { | |
362 | if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) { | |
363 | (void)unlink(to_name); | |
364 | err(1, "%s", from_name); | |
365 | } | |
366 | copy(from_fd, from_name, to_fd, to_name, from_sb.st_size); | |
367 | (void)close(from_fd); | |
368 | } | |
369 | ||
370 | if (dostrip) { | |
371 | strip(to_name); | |
372 | ||
373 | /* | |
374 | * Re-open our fd on the target, in case we used a strip | |
375 | * that does not work in-place -- like gnu binutils strip. | |
376 | */ | |
377 | close(to_fd); | |
378 | if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0) | |
379 | err(1, "stripping %s", to_name); | |
380 | } | |
381 | ||
382 | /* | |
383 | * Set owner, group, mode for target; do the chown first, | |
384 | * chown may lose the setuid bits. | |
385 | */ | |
386 | if ((gid != -1 || uid != -1) && fchown(to_fd, uid, gid)) { | |
387 | serrno = errno; | |
388 | (void)unlink(to_name); | |
389 | errx(1, "%s: chown/chgrp: %s", to_name, strerror(serrno)); | |
390 | } | |
391 | if (fchmod(to_fd, mode)) { | |
392 | serrno = errno; | |
393 | (void)unlink(to_name); | |
394 | errx(1, "%s: chmod: %s", to_name, strerror(serrno)); | |
395 | } | |
396 | ||
397 | /* | |
398 | * If provided a set of flags, set them, otherwise, preserve the | |
399 | * flags, except for the dump flag. | |
400 | */ | |
401 | if (fchflags(to_fd, | |
402 | flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { | |
403 | if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0) | |
404 | warn("%s: chflags", to_name); | |
405 | } | |
406 | ||
407 | /* | |
408 | * Preserve the date of the source file. | |
409 | */ | |
410 | if (dopreserve) { | |
411 | struct timeval tv[2]; | |
412 | ||
413 | TIMESPEC_TO_TIMEVAL(&tv[0], &from_sb.st_atimespec); | |
414 | TIMESPEC_TO_TIMEVAL(&tv[1], &from_sb.st_mtimespec); | |
415 | #ifndef __APPLE__ | |
416 | if (futimes(to_fd, tv) == -1) | |
417 | warn("%s: futimes", to_name); | |
418 | #else | |
419 | if (utimes(to_name, tv) == -1) | |
420 | warn("%s: utimes", to_name); | |
421 | #endif | |
422 | } | |
423 | ||
424 | (void)close(to_fd); | |
425 | if (!docopy && !devnull && unlink(from_name)) | |
426 | err(1, "%s", from_name); | |
427 | } | |
428 | ||
429 | /* | |
430 | * copy -- | |
431 | * copy from one file to another | |
432 | */ | |
433 | void | |
434 | copy(from_fd, from_name, to_fd, to_name, size) | |
435 | int from_fd, to_fd; | |
436 | char *from_name, *to_name; | |
437 | off_t size; | |
438 | { | |
439 | int nr, nw; | |
440 | int serrno; | |
441 | char *p; | |
442 | char buf[MAXBSIZE]; | |
443 | ||
444 | /* | |
445 | * There's no reason to do anything other than close the file | |
446 | * now if it's empty, so let's not bother. | |
447 | */ | |
448 | if (size > 0) { | |
449 | /* | |
450 | * Mmap and write if less than 8M (the limit is so we don't totally | |
451 | * trash memory on big files). This is really a minor hack, but it | |
452 | * wins some CPU back. | |
453 | */ | |
454 | if (size <= 8 * 1048576) { | |
455 | if ((p = mmap(NULL, (size_t)size, PROT_READ, | |
456 | MAP_FILE|MAP_SHARED, from_fd, (off_t)0)) == (char *)-1) | |
457 | err(1, "%s", from_name); | |
458 | if (write(to_fd, p, size) != size) | |
459 | err(1, "%s", to_name); | |
460 | } else { | |
461 | while ((nr = read(from_fd, buf, sizeof(buf))) > 0) | |
462 | if ((nw = write(to_fd, buf, nr)) != nr) { | |
463 | serrno = errno; | |
464 | (void)unlink(to_name); | |
465 | errx(1, "%s: %s", | |
466 | to_name, strerror(nw > 0 ? EIO : serrno)); | |
467 | } | |
468 | if (nr != 0) { | |
469 | serrno = errno; | |
470 | (void)unlink(to_name); | |
471 | errx(1, "%s: %s", from_name, strerror(serrno)); | |
472 | } | |
473 | } | |
474 | } | |
475 | } | |
476 | ||
477 | /* | |
478 | * strip -- | |
479 | * use strip(1) to strip the target file | |
480 | */ | |
481 | void | |
482 | strip(to_name) | |
483 | char *to_name; | |
484 | { | |
485 | int serrno, status; | |
486 | char *stripprog; | |
487 | ||
488 | switch (vfork()) { | |
489 | case -1: | |
490 | serrno = errno; | |
491 | (void)unlink(to_name); | |
492 | errx(1, "vfork: %s", strerror(serrno)); | |
493 | case 0: | |
494 | stripprog = getenv("STRIP"); | |
495 | if (stripprog == NULL) | |
496 | stripprog = _PATH_STRIP; | |
497 | ||
498 | if (stripArgs) { | |
499 | /* build up a command line and let /bin/sh parse the arguments */ | |
500 | char* cmd = (char*)malloc(sizeof(char)* | |
501 | (3+strlen(stripprog)+ | |
502 | strlen(stripArgs)+ | |
503 | strlen(to_name))); | |
504 | ||
505 | sprintf(cmd, "%s %s %s", stripprog, stripArgs, to_name); | |
506 | ||
507 | execl(_PATH_BSHELL, "sh", "-c", cmd, NULL); | |
508 | } else | |
509 | execl(stripprog, "strip", to_name, NULL); | |
510 | ||
511 | warn("%s", stripprog); | |
512 | _exit(1); | |
513 | default: | |
514 | if (wait(&status) == -1 || status) | |
515 | (void)unlink(to_name); | |
516 | } | |
517 | } | |
518 | ||
519 | /* | |
520 | * install_dir -- | |
521 | * build directory heirarchy | |
522 | */ | |
523 | void | |
524 | install_dir(path) | |
525 | char *path; | |
526 | { | |
527 | char *p; | |
528 | struct stat sb; | |
529 | int ch; | |
530 | ||
531 | for (p = path;; ++p) | |
532 | if (!*p || (p != path && *p == '/')) { | |
533 | ch = *p; | |
534 | *p = '\0'; | |
535 | if (stat(path, &sb)) { | |
536 | if (errno != ENOENT || mkdir(path, 0777) < 0) { | |
537 | err(1, "%s", path); | |
538 | /* NOTREACHED */ | |
539 | } | |
540 | } | |
541 | if (!(*p = ch)) | |
542 | break; | |
543 | } | |
544 | ||
545 | if (((gid != -1 || uid != -1) && chown(path, uid, gid)) || | |
546 | chmod(path, mode)) { | |
547 | warn("%s", path); | |
548 | } | |
549 | } | |
550 | ||
551 | /* | |
552 | * usage -- | |
553 | * print a usage message and die | |
554 | */ | |
555 | void | |
556 | usage() | |
557 | { | |
558 | (void)fprintf(stderr, "\ | |
559 | usage: install [-cps] [-f flags] [-m mode] [-o owner] [-g group] [-l linkflags] [-S stripflags] file1 file2\n\ | |
560 | install [-cps] [-f flags] [-m mode] [-o owner] [-g group] [-l linkflags] [-S stripflags] file1 ... fileN directory\n\ | |
561 | install -pd [-m mode] [-o owner] [-g group] directory ...\n"); | |
562 | exit(1); | |
563 | } |