xinstall.c revision 1.128.2.1 1 /* $NetBSD: xinstall.c,v 1.128.2.1 2025/08/02 05:58:45 perseant 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. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*-
33 * Copyright (c) 2015 The NetBSD Foundation, Inc.
34 * All rights reserved.
35 *
36 * This code is derived from software contributed to The NetBSD Foundation
37 * by Christos Zoulas.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
49 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
50 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
51 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
52 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
53 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
55 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
56 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
57 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
58 * POSSIBILITY OF SUCH DAMAGE.
59 */
60
61 #define __MKTEMP_OK__ /* All uses of mktemp have been checked */
62
63 #if HAVE_NBTOOL_CONFIG_H
64 #include "nbtool_config.h"
65 #else
66 #define HAVE_FUTIMES 1
67 #define HAVE_POSIX_SPAWN 1
68 #define HAVE_STRUCT_STAT_ST_FLAGS 1
69 #endif
70
71 #include <sys/cdefs.h>
72 #if defined(__COPYRIGHT) && !defined(lint)
73 __COPYRIGHT("@(#) Copyright (c) 1987, 1993\
74 The Regents of the University of California. All rights reserved.");
75 #endif /* not lint */
76
77 #if defined(__RCSID) && !defined(lint)
78 #if 0
79 static char sccsid[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93";
80 #else
81 __RCSID("$NetBSD: xinstall.c,v 1.128.2.1 2025/08/02 05:58:45 perseant Exp $");
82 #endif
83 #endif /* not lint */
84
85 #include <sys/param.h>
86 #include <sys/mman.h>
87 #include <sys/stat.h>
88 #include <sys/wait.h>
89 #include <sys/time.h>
90
91 #include <ctype.h>
92 #include <err.h>
93 #include <errno.h>
94 #include <fcntl.h>
95 #include <grp.h>
96 #include <libgen.h>
97 #include <paths.h>
98 #include <pwd.h>
99 #include <stdio.h>
100 #include <stdlib.h>
101 #include <string.h>
102 #include <unistd.h>
103 #include <util.h>
104 #include <vis.h>
105
106 #ifdef HAVE_POSIX_SPAWN
107 #include <spawn.h>
108 #endif
109
110 #include <md5.h>
111 #include <rmd160.h>
112 #include <sha1.h>
113 #include <sha2.h>
114
115 #include "pathnames.h"
116 #include "mtree.h"
117
118 #define BACKUP_SUFFIX ".old"
119
120 static int dobackup, dodir, dostrip, dolink, dopreserve, dorename, dounpriv;
121 static int haveopt_f, haveopt_g, haveopt_m, haveopt_o;
122 static int numberedbackup;
123 static int verbose;
124 static int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
125 static char pathbuf[MAXPATHLEN];
126 static uid_t uid = (uid_t)-1;
127 static gid_t gid = (gid_t)-1;
128 static char *group, *owner, *fflags, *tags;
129 static FILE *metafp;
130 static char *metafile;
131 static u_long fileflags;
132 static char *stripArgs;
133 static char *afterinstallcmd;
134 static const char *suffix = BACKUP_SUFFIX;
135 static char *destdir;
136
137 static enum {
138 DIGEST_NONE = 0,
139 DIGEST_MD5,
140 DIGEST_RMD160,
141 DIGEST_SHA1,
142 DIGEST_SHA256,
143 DIGEST_SHA384,
144 DIGEST_SHA512,
145 } digesttype = DIGEST_NONE;
146
147 static char *digest;
148
149 #define LN_ABSOLUTE 0x01
150 #define LN_RELATIVE 0x02
151 #define LN_HARD 0x04
152 #define LN_SYMBOLIC 0x08
153 #define LN_MIXED 0x10
154
155 #define DIRECTORY 0x01 /* Tell install it's a directory. */
156 #define SETFLAGS 0x02 /* Tell install to set flags. */
157 #define HASUID 0x04 /* Tell install the uid was given */
158 #define HASGID 0x08 /* Tell install the gid was given */
159
160 static void afterinstall(const char *, const char *, int);
161 static void backup(const char *);
162 static char *copy(int, char *, int, char *, off_t);
163 static int do_link(char *, char *);
164 static void do_symlink(char *, char *);
165 static void install(char *, char *, u_int);
166 static void install_dir(char *, u_int);
167 static void makelink(char *, char *);
168 static void metadata_log(const char *, const char *, struct timeval *,
169 const char *, const char *, off_t);
170 static int parseid(const char *, id_t *);
171 static void run(const char *, const char *, const char *, int);
172 static void strip(const char *);
173 __dead static void usage(void);
174 static char *xbasename(char *);
175 static char *xdirname(char *);
176 static int needshell(const char *, int);
177
178 int
179 main(int argc, char *argv[])
180 {
181 struct stat from_sb, to_sb;
182 void *set;
183 u_int iflags;
184 int ch, no_target;
185 char *p, *to_name;
186
187 setprogname(argv[0]);
188
189 iflags = 0;
190 while ((ch = getopt(argc, argv, "a:cbB:dD:f:g:h:l:m:M:N:o:prsS:T:Uv"))
191 != -1)
192 switch((char)ch) {
193 case 'a':
194 afterinstallcmd = strdup(optarg);
195 if (afterinstallcmd == NULL)
196 err(EXIT_FAILURE,
197 "Can't allocate after command");
198 break;
199 case 'B':
200 suffix = optarg;
201 numberedbackup = 0;
202 {
203 /* Check if given suffix really generates
204 different suffixes - catch e.g. ".%" */
205 char suffix_expanded0[FILENAME_MAX],
206 suffix_expanded1[FILENAME_MAX];
207 (void)snprintf(suffix_expanded0, FILENAME_MAX,
208 suffix, 0);
209 (void)snprintf(suffix_expanded1, FILENAME_MAX,
210 suffix, 1);
211 if (strcmp(suffix_expanded0, suffix_expanded1)
212 != 0)
213 numberedbackup = 1;
214 }
215 /* fall through; -B implies -b */
216 /*FALLTHROUGH*/
217 case 'b':
218 dobackup = 1;
219 break;
220 case 'c':
221 /* ignored; was "docopy" which is now the default. */
222 break;
223 case 'd':
224 dodir = 1;
225 break;
226 case 'D':
227 destdir = optarg;
228 break;
229 #if ! HAVE_NBTOOL_CONFIG_H
230 case 'f':
231 haveopt_f = 1;
232 fflags = optarg;
233 break;
234 #endif
235 case 'g':
236 haveopt_g = 1;
237 group = optarg;
238 break;
239 case 'h':
240 digest = optarg;
241 break;
242 case 'l':
243 for (p = optarg; *p; p++)
244 switch (*p) {
245 case 's':
246 dolink &= ~(LN_HARD|LN_MIXED);
247 dolink |= LN_SYMBOLIC;
248 break;
249 case 'h':
250 dolink &= ~(LN_SYMBOLIC|LN_MIXED);
251 dolink |= LN_HARD;
252 break;
253 case 'm':
254 dolink &= ~(LN_SYMBOLIC|LN_HARD);
255 dolink |= LN_MIXED;
256 break;
257 case 'a':
258 dolink &= ~LN_RELATIVE;
259 dolink |= LN_ABSOLUTE;
260 break;
261 case 'r':
262 dolink &= ~LN_ABSOLUTE;
263 dolink |= LN_RELATIVE;
264 break;
265 default:
266 errx(EXIT_FAILURE, "%c: invalid link type", *p);
267 /* NOTREACHED */
268 }
269 break;
270 case 'm':
271 haveopt_m = 1;
272 if (!(set = setmode(optarg)))
273 err(EXIT_FAILURE, "Cannot set file mode `%s'", optarg);
274 mode = getmode(set, 0);
275 free(set);
276 break;
277 case 'M':
278 metafile = optarg;
279 break;
280 case 'N':
281 if (! setup_getid(optarg))
282 errx(EXIT_FAILURE,
283 "Unable to use user and group databases in `%s'",
284 optarg);
285 break;
286 case 'o':
287 haveopt_o = 1;
288 owner = optarg;
289 break;
290 case 'p':
291 dopreserve = 1;
292 break;
293 case 'r':
294 dorename = 1;
295 break;
296 case 'S':
297 stripArgs = strdup(optarg);
298 if (stripArgs == NULL)
299 err(EXIT_FAILURE, "Can't allocate options");
300 /* fall through; -S implies -s */
301 /*FALLTHROUGH*/
302 case 's':
303 dostrip = 1;
304 break;
305 case 'T':
306 tags = optarg;
307 break;
308 case 'U':
309 dounpriv = 1;
310 break;
311 case 'v':
312 verbose = 1;
313 break;
314 case '?':
315 default:
316 usage();
317 }
318 argc -= optind;
319 argv += optind;
320
321 /* strip and link options make no sense when creating directories */
322 if ((dostrip || dolink) && dodir)
323 usage();
324
325 /* strip and flags make no sense with links */
326 if ((dostrip || fflags) && dolink)
327 usage();
328
329 /* must have at least two arguments, except when creating directories */
330 if (argc < 2 && !dodir)
331 usage();
332
333 if (digest) {
334 if (0) {
335 } else if (strcmp(digest, "none") == 0) {
336 digesttype = DIGEST_NONE;
337 } else if (strcmp(digest, "md5") == 0) {
338 digesttype = DIGEST_MD5;
339 } else if (strcmp(digest, "rmd160") == 0) {
340 digesttype = DIGEST_RMD160;
341 } else if (strcmp(digest, "sha1") == 0) {
342 digesttype = DIGEST_SHA1;
343 } else if (strcmp(digest, "sha256") == 0) {
344 digesttype = DIGEST_SHA256;
345 } else if (strcmp(digest, "sha384") == 0) {
346 digesttype = DIGEST_SHA384;
347 } else if (strcmp(digest, "sha512") == 0) {
348 digesttype = DIGEST_SHA512;
349 } else {
350 warnx("unknown digest `%s'", digest);
351 usage();
352 }
353 }
354
355 /* get group and owner id's */
356 if (group && !dounpriv) {
357 if (gid_from_group(group, &gid) == -1) {
358 id_t id;
359 if (!parseid(group, &id))
360 errx(EXIT_FAILURE, "unknown group %s", group);
361 gid = id;
362 }
363 iflags |= HASGID;
364 }
365 if (owner && !dounpriv) {
366 if (uid_from_user(owner, &uid) == -1) {
367 id_t id;
368 if (!parseid(owner, &id))
369 errx(EXIT_FAILURE, "unknown user %s", owner);
370 uid = id;
371 }
372 iflags |= HASUID;
373 }
374
375 #if ! HAVE_NBTOOL_CONFIG_H
376 if (fflags && !dounpriv) {
377 if (string_to_flags(&fflags, &fileflags, NULL))
378 errx(EXIT_FAILURE, "%s: invalid flag", fflags);
379 /* restore fflags since string_to_flags() changed it */
380 fflags = flags_to_string(fileflags, "-");
381 iflags |= SETFLAGS;
382 }
383 #endif
384
385 if (metafile) {
386 if ((metafp = fopen(metafile, "a")) == NULL)
387 warn("open %s", metafile);
388 } else
389 digesttype = DIGEST_NONE;
390
391 if (dodir) {
392 for (; *argv != NULL; ++argv)
393 install_dir(*argv, iflags);
394 exit (0);
395 }
396
397 no_target = stat(to_name = argv[argc - 1], &to_sb);
398 if (!no_target && S_ISDIR(to_sb.st_mode)) {
399 for (; *argv != to_name; ++argv)
400 install(*argv, to_name, iflags | DIRECTORY);
401 exit(0);
402 }
403
404 /* can't do file1 file2 directory/file */
405 if (argc != 2) {
406 errx(EXIT_FAILURE, "the last argument (%s) "
407 "must name an existing directory", argv[argc - 1]);
408 /* NOTREACHED */
409 }
410
411 if (!no_target) {
412 /* makelink() handles checks for links */
413 if (!dolink) {
414 if (stat(*argv, &from_sb))
415 err(EXIT_FAILURE, "%s: stat", *argv);
416 if (!S_ISREG(to_sb.st_mode))
417 errx(EXIT_FAILURE, "%s: not a regular file", to_name);
418 if (to_sb.st_dev == from_sb.st_dev &&
419 to_sb.st_ino == from_sb.st_ino)
420 errx(EXIT_FAILURE, "%s and %s are the same file", *argv,
421 to_name);
422 }
423 /*
424 * Unlink now... avoid ETXTBSY errors later. Try and turn
425 * off the append/immutable bits -- if we fail, go ahead,
426 * it might work.
427 */
428 #if ! HAVE_NBTOOL_CONFIG_H
429 #define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
430 if (to_sb.st_flags & NOCHANGEBITS)
431 (void)chflags(to_name,
432 to_sb.st_flags & ~(NOCHANGEBITS));
433 #endif
434 if (dobackup)
435 backup(to_name);
436 else if (!dorename)
437 (void)unlink(to_name);
438 }
439 install(*argv, to_name, iflags);
440 exit(0);
441 }
442
443 /*
444 * parseid --
445 * parse uid or gid from arg into id, returning non-zero if successful
446 */
447 static int
448 parseid(const char *name, id_t *id)
449 {
450 char *ep;
451
452 errno = 0;
453 *id = (id_t)strtoul(name, &ep, 10);
454 if (errno || *ep != '\0')
455 return (0);
456 return (1);
457 }
458
459 /*
460 * do_link --
461 * make a hard link, obeying dorename if set
462 * return -1 on failure
463 */
464 static int
465 do_link(char *from_name, char *to_name)
466 {
467 char tmpl[MAXPATHLEN];
468 int ret;
469
470 if (dorename) {
471 (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name);
472 /* This usage is safe. */
473 if (mktemp(tmpl) == NULL)
474 err(EXIT_FAILURE, "%s: mktemp", tmpl);
475 ret = link(from_name, tmpl);
476 if (ret == 0) {
477 ret = rename(tmpl, to_name);
478 /* If rename has posix semantics, then the temporary
479 * file may still exist when from_name and to_name point
480 * to the same file, so unlink it unconditionally.
481 */
482 (void)unlink(tmpl);
483 }
484 } else {
485 ret = link(from_name, to_name);
486 }
487 if (ret == 0 && verbose)
488 (void)printf("install: link %s -> %s\n", from_name, to_name);
489 return ret;
490 }
491
492 /*
493 * do_symlink --
494 * make a symbolic link, obeying dorename if set
495 * exit on failure
496 */
497 static void
498 do_symlink(char *from_name, char *to_name)
499 {
500 char tmpl[MAXPATHLEN];
501
502 if (dorename) {
503 (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name);
504 /* This usage is safe. */
505 if (mktemp(tmpl) == NULL)
506 err(EXIT_FAILURE, "%s: mktemp", tmpl);
507
508 if (symlink(from_name, tmpl) == -1)
509 err(EXIT_FAILURE, "symlink %s -> %s", from_name, tmpl);
510 if (rename(tmpl, to_name) == -1) {
511 /* remove temporary link before exiting */
512 (void)unlink(tmpl);
513 err(EXIT_FAILURE, "%s: rename", to_name);
514 }
515 } else {
516 if (symlink(from_name, to_name) == -1)
517 err(EXIT_FAILURE, "symlink %s -> %s", from_name, to_name);
518 }
519 if (verbose)
520 (void)printf("install: symlink %s -> %s\n", from_name, to_name);
521 }
522
523 /*
524 * makelink --
525 * make a link from source to destination
526 */
527 static void
528 makelink(char *from_name, char *to_name)
529 {
530 char src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN];
531 struct stat to_sb;
532
533 /* Try hard links first */
534 if (dolink & (LN_HARD|LN_MIXED)) {
535 if (do_link(from_name, to_name) == -1) {
536 if ((dolink & LN_HARD) || errno != EXDEV)
537 err(EXIT_FAILURE, "link %s -> %s", from_name, to_name);
538 } else {
539 if (stat(to_name, &to_sb))
540 err(EXIT_FAILURE, "%s: stat", to_name);
541 if (S_ISREG(to_sb.st_mode)) {
542 /* XXX: hard links to anything
543 * other than plain files are not
544 * metalogged
545 */
546 int omode;
547 char *oowner, *ogroup, *offlags;
548 char *dres;
549
550 /* XXX: use underlying perms,
551 * unless overridden on command line.
552 */
553 omode = mode;
554 if (!haveopt_m)
555 mode = (to_sb.st_mode & 0777);
556 oowner = owner;
557 if (!haveopt_o)
558 owner = NULL;
559 ogroup = group;
560 if (!haveopt_g)
561 group = NULL;
562 offlags = fflags;
563 if (!haveopt_f)
564 fflags = NULL;
565 switch (digesttype) {
566 case DIGEST_MD5:
567 dres = MD5File(from_name, NULL);
568 break;
569 case DIGEST_RMD160:
570 dres = RMD160File(from_name, NULL);
571 break;
572 case DIGEST_SHA1:
573 dres = SHA1File(from_name, NULL);
574 break;
575 case DIGEST_SHA256:
576 dres = SHA256_File(from_name, NULL);
577 break;
578 case DIGEST_SHA384:
579 dres = SHA384_File(from_name, NULL);
580 break;
581 case DIGEST_SHA512:
582 dres = SHA512_File(from_name, NULL);
583 break;
584 default:
585 dres = NULL;
586 }
587 metadata_log(to_name, "file", NULL, NULL,
588 dres, to_sb.st_size);
589 free(dres);
590 mode = omode;
591 owner = oowner;
592 group = ogroup;
593 fflags = offlags;
594 }
595 return;
596 }
597 }
598
599 /* Symbolic links */
600 if (dolink & LN_ABSOLUTE) {
601 /* Convert source path to absolute */
602 if (realpath(from_name, src) == NULL)
603 err(EXIT_FAILURE, "%s: realpath", from_name);
604 do_symlink(src, to_name);
605 /* XXX: src may point outside of destdir */
606 metadata_log(to_name, "link", NULL, src, NULL, 0);
607 return;
608 }
609
610 if (dolink & LN_RELATIVE) {
611 char *cp, *d, *s;
612
613 /* Resolve pathnames */
614 if (realpath(from_name, src) == NULL)
615 err(EXIT_FAILURE, "%s: realpath", from_name);
616
617 /*
618 * The last component of to_name may be a symlink,
619 * so use realpath to resolve only the directory.
620 */
621 cp = xdirname(to_name);
622 if (realpath(cp, dst) == NULL)
623 err(EXIT_FAILURE, "%s: realpath", cp);
624 /* .. and add the last component */
625 if (strcmp(dst, "/") != 0) {
626 if (strlcat(dst, "/", sizeof(dst)) > sizeof(dst))
627 errx(EXIT_FAILURE, "resolved pathname too long");
628 }
629 cp = xbasename(to_name);
630 if (strlcat(dst, cp, sizeof(dst)) > sizeof(dst))
631 errx(EXIT_FAILURE, "resolved pathname too long");
632
633 /* trim common path components */
634 for (s = src, d = dst; *s == *d; s++, d++)
635 continue;
636 while (*s != '/')
637 s--, d--;
638
639 /* count the number of directories we need to backtrack */
640 for (++d, lnk[0] = '\0'; *d; d++)
641 if (*d == '/')
642 (void)strlcat(lnk, "../", sizeof(lnk));
643
644 (void)strlcat(lnk, ++s, sizeof(lnk));
645
646 do_symlink(lnk, to_name);
647 /* XXX: lnk may point outside of destdir */
648 metadata_log(to_name, "link", NULL, lnk, NULL, 0);
649 return;
650 }
651
652 /*
653 * If absolute or relative was not specified,
654 * try the names the user provided
655 */
656 do_symlink(from_name, to_name);
657 /* XXX: from_name may point outside of destdir */
658 metadata_log(to_name, "link", NULL, from_name, NULL, 0);
659 }
660
661 /*
662 * install --
663 * build a path name and install the file
664 */
665 static void
666 install(char *from_name, char *to_name, u_int flags)
667 {
668 struct stat from_sb;
669 struct stat to_sb;
670 struct timeval tv[2];
671 off_t size;
672 int devnull, from_fd, to_fd, serrno, tmpmode;
673 char *p, tmpl[MAXPATHLEN], *oto_name, *digestresult;
674
675 size = -1;
676 if (!dolink) {
677 /* ensure that from_sb & tv are sane if !dolink */
678 if (stat(from_name, &from_sb))
679 err(EXIT_FAILURE, "%s: stat", from_name);
680 size = from_sb.st_size;
681 #if BSD4_4 && !HAVE_NBTOOL_CONFIG_H
682 TIMESPEC_TO_TIMEVAL(&tv[0], &from_sb.st_atimespec);
683 TIMESPEC_TO_TIMEVAL(&tv[1], &from_sb.st_mtimespec);
684 #else
685 tv[0].tv_sec = from_sb.st_atime;
686 tv[0].tv_usec = 0;
687 tv[1].tv_sec = from_sb.st_mtime;
688 tv[1].tv_usec = 0;
689 #endif
690 }
691
692 if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL) != 0) {
693 devnull = 0;
694 if (!dolink) {
695 if (!S_ISREG(from_sb.st_mode))
696 errx(EXIT_FAILURE, "%s: not a regular file", from_name);
697 }
698 /* Build the target path. */
699 if (flags & DIRECTORY) {
700 (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
701 to_name,
702 (p = strrchr(from_name, '/')) ? ++p : from_name);
703 to_name = pathbuf;
704 }
705 } else {
706 devnull = 1;
707 size = 0;
708 #if HAVE_STRUCT_STAT_ST_FLAGS
709 from_sb.st_flags = 0; /* XXX */
710 #endif
711 }
712
713 /*
714 * Unlink now... avoid ETXTBSY errors later. Try and turn
715 * off the append/immutable bits -- if we fail, go ahead,
716 * it might work.
717 */
718 #if ! HAVE_NBTOOL_CONFIG_H
719 if (stat(to_name, &to_sb) == 0 &&
720 to_sb.st_flags & (NOCHANGEBITS))
721 (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS));
722 #endif
723 if (dorename) {
724 (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name);
725 oto_name = to_name;
726 to_name = tmpl;
727 } else {
728 oto_name = NULL; /* pacify gcc */
729 if (dobackup)
730 backup(to_name);
731 else
732 (void)unlink(to_name);
733 }
734
735 if (dolink) {
736 makelink(from_name, dorename ? oto_name : to_name);
737 return;
738 }
739
740 /* Create target. */
741 if (dorename) {
742 if ((to_fd = mkstemp(to_name)) == -1)
743 err(EXIT_FAILURE, "%s: mkstemp", to_name);
744 } else {
745 if ((to_fd = open(to_name,
746 O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) < 0)
747 err(EXIT_FAILURE, "%s: open", to_name);
748 }
749 digestresult = NULL;
750 if (!devnull) {
751 if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
752 (void)unlink(to_name);
753 err(EXIT_FAILURE, "%s: open", from_name);
754 }
755 digestresult =
756 copy(from_fd, from_name, to_fd, to_name, from_sb.st_size);
757 (void)close(from_fd);
758 }
759
760 if (dostrip) {
761 strip(to_name);
762
763 /*
764 * Re-open our fd on the target, in case we used a strip
765 * that does not work in-place -- like gnu binutils strip.
766 */
767 close(to_fd);
768 if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0)
769 err(EXIT_FAILURE, "stripping %s", to_name);
770
771 /*
772 * Recalculate size and digestresult after stripping.
773 */
774 if (fstat(to_fd, &to_sb) != 0)
775 err(EXIT_FAILURE, "%s: fstat", to_name);
776 size = to_sb.st_size;
777 digestresult =
778 copy(to_fd, to_name, -1, NULL, size);
779
780 }
781
782 if (afterinstallcmd != NULL) {
783 afterinstall(afterinstallcmd, to_name, 1);
784
785 /*
786 * Re-open our fd on the target, in case we used an
787 * after-install command that does not work in-place
788 */
789 close(to_fd);
790 if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0)
791 err(EXIT_FAILURE, "running after install command on %s", to_name);
792 }
793
794 /*
795 * Set owner, group, mode for target; do the chown first,
796 * chown may lose the setuid bits.
797 */
798 if (!dounpriv &&
799 (flags & (HASUID | HASGID)) && fchown(to_fd, uid, gid) == -1) {
800 serrno = errno;
801 (void)unlink(to_name);
802 errc(EXIT_FAILURE, serrno, "%s: chown/chgrp", to_name);
803 }
804 tmpmode = mode;
805 if (dounpriv)
806 tmpmode &= S_IRWXU|S_IRWXG|S_IRWXO;
807 if (fchmod(to_fd, tmpmode) == -1) {
808 serrno = errno;
809 (void)unlink(to_name);
810 errc(EXIT_FAILURE, serrno, "%s: chmod", to_name);
811 }
812
813 /*
814 * Preserve the date of the source file.
815 */
816 if (dopreserve) {
817 #if HAVE_FUTIMES
818 if (futimes(to_fd, tv) == -1)
819 warn("%s: futimes", to_name);
820 #else
821 if (utimes(to_name, tv) == -1)
822 warn("%s: utimes", to_name);
823 #endif
824 }
825
826 (void)close(to_fd);
827
828 if (dorename) {
829 if (rename(to_name, oto_name) == -1)
830 err(EXIT_FAILURE, "%s: rename", to_name);
831 to_name = oto_name;
832 }
833 if (verbose)
834 (void)printf("install: %s -> %s\n", from_name, to_name);
835
836 /*
837 * If provided a set of flags, set them, otherwise, preserve the
838 * flags, except for the dump flag.
839 */
840 #if ! HAVE_NBTOOL_CONFIG_H
841 if (!dounpriv && chflags(to_name,
842 flags & SETFLAGS ? fileflags : from_sb.st_flags & ~UF_NODUMP) == -1)
843 {
844 if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0)
845 warn("%s: chflags", to_name);
846 }
847 #endif
848
849 metadata_log(to_name, "file", tv, NULL, digestresult, size);
850 free(digestresult);
851 }
852
853 /*
854 * copy --
855 * copy from one file to another, returning a digest.
856 *
857 * If to_fd < 0, just calculate a digest, don't copy.
858 */
859 static char *
860 copy(int from_fd, char *from_name, int to_fd, char *to_name, off_t size)
861 {
862 ssize_t nr, nw;
863 int serrno;
864 u_char *p;
865 u_char buf[MAXBSIZE];
866 MD5_CTX ctxMD5;
867 RMD160_CTX ctxRMD160;
868 SHA1_CTX ctxSHA1;
869 SHA256_CTX ctxSHA256;
870 SHA384_CTX ctxSHA384;
871 SHA512_CTX ctxSHA512;
872
873 switch (digesttype) {
874 case DIGEST_MD5:
875 MD5Init(&ctxMD5);
876 break;
877 case DIGEST_RMD160:
878 RMD160Init(&ctxRMD160);
879 break;
880 case DIGEST_SHA1:
881 SHA1Init(&ctxSHA1);
882 break;
883 case DIGEST_SHA256:
884 SHA256_Init(&ctxSHA256);
885 break;
886 case DIGEST_SHA384:
887 SHA384_Init(&ctxSHA384);
888 break;
889 case DIGEST_SHA512:
890 SHA512_Init(&ctxSHA512);
891 break;
892 case DIGEST_NONE:
893 if (to_fd < 0)
894 return NULL; /* no need to do anything */
895 /*FALLTHROUGH*/
896 default:
897 break;
898 }
899 /*
900 * There's no reason to do anything other than close the file
901 * now if it's empty, so let's not bother.
902 */
903 if (size > 0) {
904
905 /*
906 * Mmap and write if less than 8M (the limit is so we
907 * don't totally trash memory on big files). This is
908 * really a minor hack, but it wins some CPU back.
909 */
910
911 if (size <= 8 * 1048576) {
912 if ((p = mmap(NULL, (size_t)size, PROT_READ,
913 MAP_FILE|MAP_SHARED, from_fd, (off_t)0))
914 == MAP_FAILED) {
915 goto mmap_failed;
916 }
917 #if defined(MADV_SEQUENTIAL) && !defined(__APPLE__)
918 if (madvise(p, (size_t)size, MADV_SEQUENTIAL) == -1
919 && errno != EOPNOTSUPP)
920 warn("madvise");
921 #endif
922
923 if (to_fd >= 0 && write(to_fd, p, size) != size) {
924 serrno = errno;
925 (void)unlink(to_name);
926 errc(EXIT_FAILURE, serrno, "%s: write",
927 to_name);
928 }
929 switch (digesttype) {
930 case DIGEST_MD5:
931 MD5Update(&ctxMD5, p, size);
932 break;
933 case DIGEST_RMD160:
934 RMD160Update(&ctxRMD160, p, size);
935 break;
936 case DIGEST_SHA1:
937 SHA1Update(&ctxSHA1, p, size);
938 break;
939 case DIGEST_SHA256:
940 SHA256_Update(&ctxSHA256, p, size);
941 break;
942 case DIGEST_SHA384:
943 SHA384_Update(&ctxSHA384, p, size);
944 break;
945 case DIGEST_SHA512:
946 SHA512_Update(&ctxSHA512, p, size);
947 break;
948 default:
949 break;
950 }
951 (void)munmap(p, size);
952 } else {
953 mmap_failed:
954 while ((nr = read(from_fd, buf, sizeof(buf))) > 0) {
955 if (to_fd >= 0 &&
956 (nw = write(to_fd, buf, nr)) != nr) {
957 serrno = errno;
958 (void)unlink(to_name);
959 errc(EXIT_FAILURE,
960 nw > 0 ? EIO : serrno,
961 "%s: write", to_name);
962 }
963 switch (digesttype) {
964 case DIGEST_MD5:
965 MD5Update(&ctxMD5, buf, nr);
966 break;
967 case DIGEST_RMD160:
968 RMD160Update(&ctxRMD160, buf, nr);
969 break;
970 case DIGEST_SHA1:
971 SHA1Update(&ctxSHA1, buf, nr);
972 break;
973 case DIGEST_SHA256:
974 SHA256_Update(&ctxSHA256, buf, nr);
975 break;
976 case DIGEST_SHA384:
977 SHA384_Update(&ctxSHA384, buf, nr);
978 break;
979 case DIGEST_SHA512:
980 SHA512_Update(&ctxSHA512, buf, nr);
981 break;
982 default:
983 break;
984 }
985 }
986 if (nr != 0) {
987 serrno = errno;
988 (void)unlink(to_name);
989 errc(EXIT_FAILURE, serrno, "%s: read",
990 from_name);
991 }
992 }
993 }
994 switch (digesttype) {
995 case DIGEST_MD5:
996 return MD5End(&ctxMD5, NULL);
997 case DIGEST_RMD160:
998 return RMD160End(&ctxRMD160, NULL);
999 case DIGEST_SHA1:
1000 return SHA1End(&ctxSHA1, NULL);
1001 case DIGEST_SHA256:
1002 return SHA256_End(&ctxSHA256, NULL);
1003 case DIGEST_SHA384:
1004 return SHA384_End(&ctxSHA384, NULL);
1005 case DIGEST_SHA512:
1006 return SHA512_End(&ctxSHA512, NULL);
1007 default:
1008 return NULL;
1009 }
1010 }
1011
1012 static void
1013 run(const char *command, const char *flags, const char *to_name, int errunlink)
1014 {
1015 char *args[4];
1016 char *cmd;
1017 int status;
1018 int rv;
1019 size_t i;
1020
1021 i = 1;
1022 status = 0;
1023
1024 if (needshell(command, 1)) {
1025 rv = asprintf(&cmd, "%s %s%s%s", command, flags ? flags : "",
1026 flags ? " " : "", to_name);
1027 if (rv < 0) {
1028 warn("Cannot execute %s", command);
1029 goto out;
1030 }
1031 command = _PATH_BSHELL;
1032 flags = "-c";
1033 } else
1034 cmd = __UNCONST(to_name);
1035
1036 args[0] = __UNCONST(command);
1037 if (flags)
1038 args[i++] = __UNCONST(flags);
1039 args[i++] = cmd;
1040 args[i] = NULL;
1041
1042 #ifdef HAVE_POSIX_SPAWN
1043 if (*command == '/')
1044 rv = posix_spawn(NULL, command, NULL, NULL, args, NULL);
1045 else
1046 rv = posix_spawnp(NULL, command, NULL, NULL, args, NULL);
1047 if (rv != 0)
1048 warnc(rv, "Cannot execute %s", command);
1049 /*
1050 * the wait below will fail if we did not create a child it will
1051 * make rv negative.
1052 */
1053 #else
1054 switch (vfork()) {
1055 case -1:
1056 rv = errno;
1057 if (errunlink)
1058 (void)unlink(to_name);
1059 errc(EXIT_FAILURE, rv, "vfork");
1060 /*NOTREACHED*/
1061 case 0:
1062 if (*command == '/')
1063 execv(command, args);
1064 else
1065 execvp(command, args);
1066 rv = errno;
1067 const char *arr[] = {
1068 getprogname(),
1069 ": exec failed for ",
1070 command,
1071 " (",
1072 strerror(rv),
1073 ")\n",
1074 };
1075 for (i = 0; i < __arraycount(arr); i++)
1076 write(STDERR_FILENO, arr[i], strlen(arr[i]));
1077 _exit(1);
1078 /*NOTREACHED*/
1079 default:
1080 break;
1081 }
1082 #endif
1083 rv = wait(&status);
1084 if (cmd != to_name)
1085 free(cmd);
1086 out:
1087 if ((rv < 0 || status) && errunlink)
1088 (void)unlink(to_name);
1089 }
1090
1091 /*
1092 * strip --
1093 * use strip(1) to strip the target file
1094 */
1095 static void
1096 strip(const char *to_name)
1097 {
1098 const char *stripprog;
1099
1100 if ((stripprog = getenv("STRIP")) == NULL || *stripprog == '\0') {
1101 #ifdef TARGET_STRIP
1102 stripprog = TARGET_STRIP;
1103 #else
1104 stripprog = _PATH_STRIP;
1105 #endif
1106 }
1107 run(stripprog, stripArgs, to_name, 1);
1108 }
1109
1110 /*
1111 * afterinstall --
1112 * run provided command on the target file or directory after it's been
1113 * installed and stripped, but before permissions are set or it's renamed
1114 */
1115 static void
1116 afterinstall(const char *command, const char *to_name, int errunlink)
1117 {
1118 run(command, NULL, to_name, errunlink);
1119 }
1120
1121 /*
1122 * backup --
1123 * backup file "to_name" to to_name.suffix
1124 * if suffix contains a "%", it's taken as a printf(3) pattern
1125 * used for a numbered backup.
1126 */
1127 static void
1128 backup(const char *to_name)
1129 {
1130 char bname[FILENAME_MAX];
1131
1132 if (numberedbackup) {
1133 /* Do numbered backup */
1134 int cnt;
1135 char suffix_expanded[FILENAME_MAX];
1136
1137 cnt=0;
1138 do {
1139 (void)snprintf(suffix_expanded, FILENAME_MAX, suffix,
1140 cnt);
1141 (void)snprintf(bname, FILENAME_MAX, "%s%s", to_name,
1142 suffix_expanded);
1143 cnt++;
1144 } while (access(bname, F_OK) == 0);
1145 } else {
1146 /* Do simple backup */
1147 (void)snprintf(bname, FILENAME_MAX, "%s%s", to_name, suffix);
1148 }
1149
1150 if (rename(to_name, bname) == 0) {
1151 if (verbose)
1152 (void)printf("install: %s -> %s\n", to_name, bname);
1153 }
1154 }
1155
1156 /*
1157 * install_dir --
1158 * build directory hierarchy
1159 */
1160 static void
1161 install_dir(char *path, u_int flags)
1162 {
1163 char *p;
1164 struct stat sb;
1165 int ch;
1166
1167 for (p = path;; ++p)
1168 if (!*p || (p != path && *p == '/')) {
1169 ch = *p;
1170 *p = '\0';
1171 if (mkdir(path, 0777) < 0) {
1172 /*
1173 * Can't create; path exists or no perms.
1174 * stat() path to determine what's there now.
1175 */
1176 int sverrno;
1177 sverrno = errno;
1178 if (stat(path, &sb) < 0) {
1179 /* Not there; use mkdir()s error */
1180 errno = sverrno;
1181 err(EXIT_FAILURE, "%s: mkdir", path);
1182 }
1183 if (!S_ISDIR(sb.st_mode)) {
1184 errx(EXIT_FAILURE,
1185 "%s exists but is not a directory",
1186 path);
1187 }
1188 }
1189 if (verbose)
1190 (void)printf("install: mkdir %s\n", path);
1191 if (!(*p = ch))
1192 break;
1193 }
1194
1195 if (afterinstallcmd != NULL)
1196 afterinstall(afterinstallcmd, path, 0);
1197
1198 if (!dounpriv && (
1199 ((flags & (HASUID | HASGID)) && chown(path, uid, gid) == -1)
1200 || chmod(path, mode) == -1 )) {
1201 warn("%s: chown/chmod", path);
1202 }
1203 metadata_log(path, "dir", NULL, NULL, NULL, 0);
1204 }
1205
1206 /*
1207 * printid --
1208 * Print a user or group id or name into the metalog
1209 */
1210 static void
1211 printid(char ug, const char *str)
1212 {
1213 id_t id;
1214
1215 if (!str)
1216 return;
1217
1218 fputc(' ', metafp);
1219 fputc(ug, metafp);
1220 if (parseid(str, &id))
1221 fprintf(metafp, "id=%jd", (intmax_t)id);
1222 else
1223 fprintf(metafp, "name=%s", str);
1224 }
1225
1226 /*
1227 * metadata_log --
1228 * if metafp is not NULL, output mtree(8) full path name and settings to
1229 * metafp, to allow permissions to be set correctly by other tools,
1230 * or to allow integrity checks to be performed.
1231 */
1232 static void
1233 metadata_log(const char *path, const char *type, struct timeval *tv,
1234 const char *slink, const char *digestresult, off_t size)
1235 {
1236 static const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' };
1237 const char *p;
1238 char *buf;
1239 size_t destlen;
1240 struct flock metalog_lock;
1241
1242 if (!metafp)
1243 return;
1244 buf = malloc(4 * strlen(path) + 1); /* buf for strsvis(3) */
1245 if (buf == NULL) {
1246 warn("Can't allocate metadata");
1247 return;
1248 }
1249 /* lock log file */
1250 metalog_lock.l_start = 0;
1251 metalog_lock.l_len = 0;
1252 metalog_lock.l_whence = SEEK_SET;
1253 metalog_lock.l_type = F_WRLCK;
1254 if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) {
1255 warn("can't lock %s", metafile);
1256 free(buf);
1257 return;
1258 }
1259
1260 p = path; /* remove destdir */
1261 if (destdir) {
1262 destlen = strlen(destdir);
1263 if (strncmp(p, destdir, destlen) == 0 &&
1264 (p[destlen] == '/' || p[destlen] == '\0'))
1265 p += destlen;
1266 }
1267 while (*p && *p == '/') /* remove leading /s */
1268 p++;
1269 strsvis(buf, p, VIS_CSTYLE, extra); /* encode name */
1270 p = buf;
1271 /* print details */
1272 fprintf(metafp, ".%s%s type=%s", *p ? "/" : "", p, type);
1273 printid('u', owner);
1274 printid('g', group);
1275 fprintf(metafp, " mode=%#o", mode);
1276 if (slink) {
1277 strsvis(buf, slink, VIS_CSTYLE, extra); /* encode link */
1278 fprintf(metafp, " link=%s", buf);
1279 }
1280 if (*type == 'f') /* type=file */
1281 fprintf(metafp, " size=%lld", (long long)size);
1282 if (tv != NULL && dopreserve)
1283 fprintf(metafp, " time=%lld.%0*lld",
1284 (long long)tv[1].tv_sec,
1285 (tv[1].tv_usec == 0 ? 1 : 9),
1286 (long long)tv[1].tv_usec * 1000);
1287 if (digestresult && digest)
1288 fprintf(metafp, " %s=%s", digest, digestresult);
1289 if (fflags)
1290 fprintf(metafp, " flags=%s", fflags);
1291 if (tags)
1292 fprintf(metafp, " tags=%s", tags);
1293 fputc('\n', metafp);
1294 fflush(metafp); /* flush output */
1295 /* unlock log file */
1296 metalog_lock.l_type = F_UNLCK;
1297 if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) {
1298 warn("can't unlock %s", metafile);
1299 }
1300 free(buf);
1301 }
1302
1303 /*
1304 * xbasename --
1305 * libc basename(3) that returns a pointer to a static buffer
1306 * instead of overwriting that passed-in string.
1307 */
1308 static char *
1309 xbasename(char *path)
1310 {
1311 static char tmp[MAXPATHLEN];
1312
1313 (void)strlcpy(tmp, path, sizeof(tmp));
1314 return (basename(tmp));
1315 }
1316
1317 /*
1318 * xdirname --
1319 * libc dirname(3) that returns a pointer to a static buffer
1320 * instead of overwriting that passed-in string.
1321 */
1322 static char *
1323 xdirname(char *path)
1324 {
1325 static char tmp[MAXPATHLEN];
1326
1327 (void)strlcpy(tmp, path, sizeof(tmp));
1328 return (dirname(tmp));
1329 }
1330
1331 /*
1332 * usage --
1333 * print a usage message and die
1334 */
1335 static void
1336 usage(void)
1337 {
1338 const char *prog;
1339
1340 prog = getprogname();
1341
1342 (void)fprintf(stderr,
1343 "usage: %s [-bcprsUv] [-M log] [-D dest] [-T tags] [-B suffix]\n"
1344 " [-a aftercmd] [-f flags] [-m mode] [-N dbdir] [-o owner] [-g group] \n"
1345 " [-l linkflags] [-h hash] [-S stripflags] file1 file2\n"
1346 " %s [-bcprsUv] [-M log] [-D dest] [-T tags] [-B suffix]\n"
1347 " [-a aftercmd] [-f flags] [-m mode] [-N dbdir] [-o owner] [-g group]\n"
1348 " [-l linkflags] [-h hash] [-S stripflags] file1 ... fileN directory\n"
1349 " %s -d [-pUv] [-M log] [-D dest] [-T tags] [-a aftercmd] [-m mode]\n"
1350 " [-N dbdir] [-o owner] [-g group] directory ...\n",
1351 prog, prog, prog);
1352 exit(1);
1353 }
1354
1355 /*
1356 * The following array is used to make a fast determination of which
1357 * characters are interpreted specially by the shell. If a command
1358 * contains any of these characters, it is executed by the shell, not
1359 * directly by us.
1360 */
1361 static unsigned char _metachar[128] = {
1362 /* nul soh stx etx eot enq ack bel */
1363 1, 0, 0, 0, 0, 0, 0, 0,
1364 /* bs ht nl vt np cr so si */
1365 0, 0, 1, 0, 0, 0, 0, 0,
1366 /* dle dc1 dc2 dc3 dc4 nak syn etb */
1367 0, 0, 0, 0, 0, 0, 0, 0,
1368 /* can em sub esc fs gs rs us */
1369 0, 0, 0, 0, 0, 0, 0, 0,
1370 /* sp ! " # $ % & ' */
1371 0, 1, 1, 1, 1, 0, 1, 1,
1372 /* ( ) * + , - . / */
1373 1, 1, 1, 0, 0, 0, 0, 0,
1374 /* 0 1 2 3 4 5 6 7 */
1375 0, 0, 0, 0, 0, 0, 0, 0,
1376 /* 8 9 : ; < = > ? */
1377 0, 0, 0, 1, 1, 0, 1, 1,
1378 /* @ A B C D E F G */
1379 0, 0, 0, 0, 0, 0, 0, 0,
1380 /* H I J K L M N O */
1381 0, 0, 0, 0, 0, 0, 0, 0,
1382 /* P Q R S T U V W */
1383 0, 0, 0, 0, 0, 0, 0, 0,
1384 /* X Y Z [ \ ] ^ _ */
1385 0, 0, 0, 1, 1, 1, 1, 0,
1386 /* ` a b c d e f g */
1387 1, 0, 0, 0, 0, 0, 0, 0,
1388 /* h i j k l m n o */
1389 0, 0, 0, 0, 0, 0, 0, 0,
1390 /* p q r s t u v w */
1391 0, 0, 0, 0, 0, 0, 0, 0,
1392 /* x y z { | } ~ del */
1393 0, 0, 0, 1, 1, 1, 1, 0,
1394 };
1395
1396 #define ismeta(c) _metachar[(c) & 0x7f]
1397
1398 static int
1399 needshell(const char *cmd, int white)
1400 {
1401 while (!ismeta(*cmd) && *cmd != ':' && *cmd != '=') {
1402 if (white && isspace((unsigned char)*cmd))
1403 break;
1404 cmd++;
1405 }
1406
1407 return *cmd != '\0';
1408 }
1409