1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2001 Dima Dorfman. 5 * 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 /* 30 * mdmfs (md/MFS) is a wrapper around mdconfig(8), 31 * newfs(8), and mount(8) that mimics the command line option set of 32 * the deprecated mount_mfs(8). 33 */ 34 35 #include <sys/param.h> 36 #include <sys/linker.h> 37 #include <sys/mdioctl.h> 38 #include <sys/module.h> 39 #include <sys/mount.h> 40 #include <sys/stat.h> 41 #include <sys/wait.h> 42 43 #include <assert.h> 44 #include <err.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <grp.h> 48 #include <inttypes.h> 49 #include <paths.h> 50 #include <pwd.h> 51 #include <stdarg.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <ctype.h> 56 #include <unistd.h> 57 58 typedef enum { false, true } bool; 59 60 struct mtpt_info { 61 uid_t mi_uid; 62 bool mi_have_uid; 63 gid_t mi_gid; 64 bool mi_have_gid; 65 mode_t mi_mode; 66 bool mi_have_mode; 67 bool mi_forced_pw; 68 }; 69 70 static bool debug; /* Emit debugging information? */ 71 static bool loudsubs; /* Suppress output from helper programs? */ 72 static bool norun; /* Actually run the helper programs? */ 73 static int unit; /* The unit we're working with. */ 74 static const char *mdname; /* Name of memory disk device (e.g., "md"). */ 75 static const char *mdsuffix; /* Suffix of memory disk device (e.g., ".uzip"). */ 76 static size_t mdnamelen; /* Length of mdname. */ 77 static const char *path_mdconfig =_PATH_MDCONFIG; 78 79 static void argappend(char **, const char *, ...) __printflike(2, 3); 80 static void debugprintf(const char *, ...) __printflike(1, 2); 81 static void do_mdconfig_attach(const char *, const enum md_types); 82 static void do_mdconfig_attach_au(const char *, const enum md_types); 83 static void do_mdconfig_detach(void); 84 static void do_mount_md(const char *, const char *); 85 static void do_mount_tmpfs(const char *, const char *); 86 static void do_mtptsetup(const char *, struct mtpt_info *); 87 static void do_newfs(const char *); 88 static void do_copy(const char *, const char *); 89 static void extract_ugid(const char *, struct mtpt_info *); 90 static int run(int *, const char *, ...) __printflike(2, 3); 91 static const char *run_exitstr(int); 92 static int run_exitnumber(int); 93 static void usage(void); 94 95 int 96 main(int argc, char **argv) 97 { 98 struct mtpt_info mi; /* Mountpoint info. */ 99 intmax_t mdsize; 100 char *mdconfig_arg, *newfs_arg, /* Args to helper programs. */ 101 *mount_arg; 102 enum md_types mdtype; /* The type of our memory disk. */ 103 bool have_mdtype, mlmac; 104 bool detach, softdep, autounit, newfs; 105 const char *mtpoint, *size_arg, *skel, *unitstr; 106 char *p; 107 int ch, idx, rv; 108 void *set; 109 unsigned long ul; 110 111 /* Misc. initialization. */ 112 (void)memset(&mi, '\0', sizeof(mi)); 113 detach = true; 114 softdep = true; 115 autounit = false; 116 mlmac = false; 117 newfs = true; 118 have_mdtype = false; 119 skel = NULL; 120 mdtype = MD_SWAP; 121 mdname = MD_NAME; 122 mdnamelen = strlen(mdname); 123 mdsize = 0; 124 /* 125 * Can't set these to NULL. They may be passed to the 126 * respective programs without modification. I.e., we may not 127 * receive any command-line options which will caused them to 128 * be modified. 129 */ 130 mdconfig_arg = strdup(""); 131 newfs_arg = strdup(""); 132 mount_arg = strdup(""); 133 size_arg = NULL; 134 135 /* If we were started as mount_mfs or mfs, imply -C. */ 136 if (strcmp(getprogname(), "mount_mfs") == 0 || 137 strcmp(getprogname(), "mfs") == 0) { 138 /* Make compatibility assumptions. */ 139 mi.mi_mode = 01777; 140 mi.mi_have_mode = true; 141 } 142 143 while ((ch = getopt(argc, argv, 144 "a:b:Cc:Dd:E:e:F:f:hi:k:LlMm:NnO:o:Pp:Ss:tT:Uv:w:X")) != -1) 145 switch (ch) { 146 case 'a': 147 argappend(&newfs_arg, "-a %s", optarg); 148 break; 149 case 'b': 150 argappend(&newfs_arg, "-b %s", optarg); 151 break; 152 case 'C': 153 /* Ignored for compatibility. */ 154 break; 155 case 'c': 156 argappend(&newfs_arg, "-c %s", optarg); 157 break; 158 case 'D': 159 detach = false; 160 break; 161 case 'd': 162 argappend(&newfs_arg, "-d %s", optarg); 163 break; 164 case 'E': 165 path_mdconfig = optarg; 166 break; 167 case 'e': 168 argappend(&newfs_arg, "-e %s", optarg); 169 break; 170 case 'F': 171 if (have_mdtype) 172 usage(); 173 mdtype = MD_VNODE; 174 have_mdtype = true; 175 argappend(&mdconfig_arg, "-f %s", optarg); 176 break; 177 case 'f': 178 argappend(&newfs_arg, "-f %s", optarg); 179 break; 180 case 'h': 181 usage(); 182 break; 183 case 'i': 184 argappend(&newfs_arg, "-i %s", optarg); 185 break; 186 case 'k': 187 skel = optarg; 188 break; 189 case 'L': 190 loudsubs = true; 191 break; 192 case 'l': 193 mlmac = true; 194 argappend(&newfs_arg, "-l"); 195 break; 196 case 'M': 197 if (have_mdtype) 198 usage(); 199 mdtype = MD_MALLOC; 200 have_mdtype = true; 201 argappend(&mdconfig_arg, "-o reserve"); 202 break; 203 case 'm': 204 argappend(&newfs_arg, "-m %s", optarg); 205 break; 206 case 'N': 207 norun = true; 208 break; 209 case 'n': 210 argappend(&newfs_arg, "-n"); 211 break; 212 case 'O': 213 argappend(&newfs_arg, "-o %s", optarg); 214 break; 215 case 'o': 216 argappend(&mount_arg, "-o %s", optarg); 217 break; 218 case 'P': 219 newfs = false; 220 break; 221 case 'p': 222 if ((set = setmode(optarg)) == NULL) 223 usage(); 224 mi.mi_mode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO); 225 mi.mi_have_mode = true; 226 mi.mi_forced_pw = true; 227 free(set); 228 break; 229 case 'S': 230 softdep = false; 231 break; 232 case 's': 233 size_arg = optarg; 234 break; 235 case 't': 236 argappend(&newfs_arg, "-t"); 237 break; 238 case 'T': 239 argappend(&mount_arg, "-t %s", optarg); 240 break; 241 case 'U': 242 softdep = true; 243 break; 244 case 'v': 245 argappend(&newfs_arg, "-O %s", optarg); 246 break; 247 case 'w': 248 extract_ugid(optarg, &mi); 249 mi.mi_forced_pw = true; 250 break; 251 case 'X': 252 debug = true; 253 break; 254 default: 255 usage(); 256 } 257 argc -= optind; 258 argv += optind; 259 if (argc < 2) 260 usage(); 261 262 /* 263 * Historically our size arg was passed directly to mdconfig, which 264 * treats a number without a suffix as a count of 512-byte sectors; 265 * tmpfs would treat it as a count of bytes. To get predictable 266 * behavior for 'auto' we document that the size always uses mdconfig 267 * rules. To make that work, decode the size here so it can be passed 268 * to either tmpfs or mdconfig as a count of bytes. 269 */ 270 if (size_arg != NULL) { 271 mdsize = (intmax_t)strtoumax(size_arg, &p, 0); 272 if (p == size_arg || (p[0] != 0 && p[1] != 0) || mdsize < 0) 273 errx(1, "invalid size '%s'", size_arg); 274 switch (*p) { 275 case 'p': 276 case 'P': 277 mdsize *= 1024; 278 case 't': 279 case 'T': 280 mdsize *= 1024; 281 case 'g': 282 case 'G': 283 mdsize *= 1024; 284 case 'm': 285 case 'M': 286 mdsize *= 1024; 287 case 'k': 288 case 'K': 289 mdsize *= 1024; 290 case 'b': 291 case 'B': 292 break; 293 case '\0': 294 mdsize *= 512; 295 break; 296 default: 297 errx(1, "invalid size suffix on '%s'", size_arg); 298 } 299 } 300 301 /* 302 * Based on the command line 'md-device' either mount a tmpfs filesystem 303 * or configure the md device then format and mount a filesystem on it. 304 * If the device is 'auto' use tmpfs if it is available and there is no 305 * request for multilabel MAC (which tmpfs does not support). 306 */ 307 unitstr = argv[0]; 308 mtpoint = argv[1]; 309 310 if (strcmp(unitstr, "auto") == 0) { 311 if (mlmac) 312 idx = -1; /* Must use md for mlmac. */ 313 else if ((idx = modfind("tmpfs")) == -1) 314 idx = kldload("tmpfs"); 315 if (idx == -1) 316 unitstr = "md"; 317 else 318 unitstr = "tmpfs"; 319 } 320 321 if (strcmp(unitstr, "tmpfs") == 0) { 322 if (size_arg != NULL && mdsize != 0) 323 argappend(&mount_arg, "-o size=%jd", mdsize); 324 do_mount_tmpfs(mount_arg, mtpoint); 325 } else { 326 if (size_arg != NULL) 327 argappend(&mdconfig_arg, "-s %jdB", mdsize); 328 if (strncmp(unitstr, "/dev/", 5) == 0) 329 unitstr += 5; 330 if (strncmp(unitstr, mdname, mdnamelen) == 0) 331 unitstr += mdnamelen; 332 if (!isdigit(*unitstr)) { 333 autounit = true; 334 unit = -1; 335 mdsuffix = unitstr; 336 } else { 337 ul = strtoul(unitstr, &p, 10); 338 if (ul == ULONG_MAX) 339 errx(1, "bad device unit: %s", unitstr); 340 unit = ul; 341 mdsuffix = p; /* can be empty */ 342 } 343 344 if (!have_mdtype) 345 mdtype = MD_SWAP; 346 if (softdep) 347 argappend(&newfs_arg, "-U"); 348 if (mdtype != MD_VNODE && !newfs) 349 errx(1, "-P requires a vnode-backed disk"); 350 351 /* Do the work. */ 352 if (detach && !autounit) 353 do_mdconfig_detach(); 354 if (autounit) 355 do_mdconfig_attach_au(mdconfig_arg, mdtype); 356 else 357 do_mdconfig_attach(mdconfig_arg, mdtype); 358 if (newfs) 359 do_newfs(newfs_arg); 360 if (!softdep) { 361 rv = run(NULL, "%s %s /dev/%s%d", _PATH_TUNEFS, 362 "-n disable", mdname, unit); 363 if (rv) 364 errx(1, "tunefs exited %s %d", run_exitstr(rv), 365 run_exitnumber(rv)); 366 } 367 do_mount_md(mount_arg, mtpoint); 368 } 369 370 do_mtptsetup(mtpoint, &mi); 371 if (skel != NULL) 372 do_copy(mtpoint, skel); 373 374 return (0); 375 } 376 377 /* 378 * Append the expansion of 'fmt' to the buffer pointed to by '*dstp'; 379 * reallocate as required. 380 */ 381 static void 382 argappend(char **dstp, const char *fmt, ...) 383 { 384 char *old, *new; 385 va_list ap; 386 387 old = *dstp; 388 assert(old != NULL); 389 390 va_start(ap, fmt); 391 if (vasprintf(&new, fmt,ap) == -1) 392 errx(1, "vasprintf"); 393 va_end(ap); 394 395 *dstp = new; 396 if (asprintf(&new, "%s %s", old, new) == -1) 397 errx(1, "asprintf"); 398 free(*dstp); 399 free(old); 400 401 *dstp = new; 402 } 403 404 /* 405 * If run-time debugging is enabled, print the expansion of 'fmt'. 406 * Otherwise, do nothing. 407 */ 408 static void 409 debugprintf(const char *fmt, ...) 410 { 411 va_list ap; 412 413 if (!debug) 414 return; 415 fprintf(stderr, "DEBUG: "); 416 va_start(ap, fmt); 417 vfprintf(stderr, fmt, ap); 418 va_end(ap); 419 fprintf(stderr, "\n"); 420 fflush(stderr); 421 } 422 423 /* 424 * Attach a memory disk with a known unit. 425 */ 426 static void 427 do_mdconfig_attach(const char *args, const enum md_types mdtype) 428 { 429 int rv; 430 const char *ta; /* Type arg. */ 431 432 switch (mdtype) { 433 case MD_SWAP: 434 ta = "-t swap"; 435 break; 436 case MD_VNODE: 437 ta = "-t vnode"; 438 break; 439 case MD_MALLOC: 440 ta = "-t malloc"; 441 break; 442 default: 443 abort(); 444 } 445 rv = run(NULL, "%s -a %s%s -u %s%d", path_mdconfig, ta, args, 446 mdname, unit); 447 if (rv) 448 errx(1, "mdconfig (attach) exited %s %d", run_exitstr(rv), 449 run_exitnumber(rv)); 450 } 451 452 /* 453 * Attach a memory disk with an unknown unit; use autounit. 454 */ 455 static void 456 do_mdconfig_attach_au(const char *args, const enum md_types mdtype) 457 { 458 const char *ta; /* Type arg. */ 459 char *linep; 460 char linebuf[12]; /* 32-bit unit (10) + '\n' (1) + '\0' (1) */ 461 int fd; /* Standard output of mdconfig invocation. */ 462 FILE *sfd; 463 int rv; 464 char *p; 465 size_t linelen; 466 unsigned long ul; 467 468 switch (mdtype) { 469 case MD_SWAP: 470 ta = "-t swap"; 471 break; 472 case MD_VNODE: 473 ta = "-t vnode"; 474 break; 475 case MD_MALLOC: 476 ta = "-t malloc"; 477 break; 478 default: 479 abort(); 480 } 481 rv = run(&fd, "%s -a %s%s", path_mdconfig, ta, args); 482 if (rv) 483 errx(1, "mdconfig (attach) exited %s %d", run_exitstr(rv), 484 run_exitnumber(rv)); 485 486 /* Receive the unit number. */ 487 if (norun) { /* Since we didn't run, we can't read. Fake it. */ 488 unit = 0; 489 return; 490 } 491 sfd = fdopen(fd, "r"); 492 if (sfd == NULL) 493 err(1, "fdopen"); 494 linep = fgetln(sfd, &linelen); 495 /* If the output format changes, we want to know about it. */ 496 if (linep == NULL || linelen <= mdnamelen + 1 || 497 linelen - mdnamelen >= sizeof(linebuf) || 498 strncmp(linep, mdname, mdnamelen) != 0) 499 errx(1, "unexpected output from mdconfig (attach)"); 500 linep += mdnamelen; 501 linelen -= mdnamelen; 502 /* Can't use strlcpy because linep is not NULL-terminated. */ 503 strncpy(linebuf, linep, linelen); 504 linebuf[linelen] = '\0'; 505 ul = strtoul(linebuf, &p, 10); 506 if (ul == ULONG_MAX || *p != '\n') 507 errx(1, "unexpected output from mdconfig (attach)"); 508 unit = ul; 509 510 fclose(sfd); 511 } 512 513 /* 514 * Detach a memory disk. 515 */ 516 static void 517 do_mdconfig_detach(void) 518 { 519 int rv; 520 521 rv = run(NULL, "%s -d -u %s%d", path_mdconfig, mdname, unit); 522 if (rv && debug) /* This is allowed to fail. */ 523 warnx("mdconfig (detach) exited %s %d (ignored)", 524 run_exitstr(rv), run_exitnumber(rv)); 525 } 526 527 /* 528 * Mount the configured memory disk. 529 */ 530 static void 531 do_mount_md(const char *args, const char *mtpoint) 532 { 533 int rv; 534 535 rv = run(NULL, "%s%s /dev/%s%d%s %s", _PATH_MOUNT, args, 536 mdname, unit, mdsuffix, mtpoint); 537 if (rv) 538 errx(1, "mount exited %s %d", run_exitstr(rv), 539 run_exitnumber(rv)); 540 } 541 542 /* 543 * Mount the configured tmpfs. 544 */ 545 static void 546 do_mount_tmpfs(const char *args, const char *mtpoint) 547 { 548 int rv; 549 550 rv = run(NULL, "%s -t tmpfs %s tmp %s", _PATH_MOUNT, args, mtpoint); 551 if (rv) 552 errx(1, "tmpfs mount exited %s %d", run_exitstr(rv), 553 run_exitnumber(rv)); 554 } 555 556 /* 557 * Various configuration of the mountpoint. Mostly, enact 'mip'. 558 */ 559 static void 560 do_mtptsetup(const char *mtpoint, struct mtpt_info *mip) 561 { 562 struct statfs sfs; 563 564 if (!mip->mi_have_mode && !mip->mi_have_uid && !mip->mi_have_gid) 565 return; 566 567 if (!norun) { 568 if (statfs(mtpoint, &sfs) == -1) { 569 warn("statfs: %s", mtpoint); 570 return; 571 } 572 if ((sfs.f_flags & MNT_RDONLY) != 0) { 573 if (mip->mi_forced_pw) { 574 warnx( 575 "Not changing mode/owner of %s since it is read-only", 576 mtpoint); 577 } else { 578 debugprintf( 579 "Not changing mode/owner of %s since it is read-only", 580 mtpoint); 581 } 582 return; 583 } 584 } 585 586 if (mip->mi_have_mode) { 587 debugprintf("changing mode of %s to %o.", mtpoint, 588 mip->mi_mode); 589 if (!norun) 590 if (chmod(mtpoint, mip->mi_mode) == -1) 591 err(1, "chmod: %s", mtpoint); 592 } 593 /* 594 * We have to do these separately because the user may have 595 * only specified one of them. 596 */ 597 if (mip->mi_have_uid) { 598 debugprintf("changing owner (user) or %s to %u.", mtpoint, 599 mip->mi_uid); 600 if (!norun) 601 if (chown(mtpoint, mip->mi_uid, -1) == -1) 602 err(1, "chown %s to %u (user)", mtpoint, 603 mip->mi_uid); 604 } 605 if (mip->mi_have_gid) { 606 debugprintf("changing owner (group) or %s to %u.", mtpoint, 607 mip->mi_gid); 608 if (!norun) 609 if (chown(mtpoint, -1, mip->mi_gid) == -1) 610 err(1, "chown %s to %u (group)", mtpoint, 611 mip->mi_gid); 612 } 613 } 614 615 /* 616 * Put a file system on the memory disk. 617 */ 618 static void 619 do_newfs(const char *args) 620 { 621 int rv; 622 623 rv = run(NULL, "%s%s /dev/%s%d", _PATH_NEWFS, args, mdname, unit); 624 if (rv) 625 errx(1, "newfs exited %s %d", run_exitstr(rv), 626 run_exitnumber(rv)); 627 } 628 629 630 /* 631 * Copy skel into the mountpoint. 632 */ 633 static void 634 do_copy(const char *mtpoint, const char *skel) 635 { 636 int rv; 637 638 rv = chdir(skel); 639 if (rv != 0) 640 err(1, "chdir to %s", skel); 641 rv = run(NULL, "/bin/pax -rw -pe . %s", mtpoint); 642 if (rv != 0) 643 errx(1, "skel copy failed"); 644 } 645 646 /* 647 * 'str' should be a user and group name similar to the last argument 648 * to chown(1); i.e., a user, followed by a colon, followed by a 649 * group. The user and group in 'str' may be either a [ug]id or a 650 * name. Upon return, the uid and gid fields in 'mip' will contain 651 * the uid and gid of the user and group name in 'str', respectively. 652 * 653 * In other words, this derives a user and group id from a string 654 * formatted like the last argument to chown(1). 655 * 656 * Notice: At this point we don't support only a username or only a 657 * group name. do_mtptsetup already does, so when this feature is 658 * desired, this is the only routine that needs to be changed. 659 */ 660 static void 661 extract_ugid(const char *str, struct mtpt_info *mip) 662 { 663 char *ug; /* Writable 'str'. */ 664 char *user, *group; /* Result of extracton. */ 665 struct passwd *pw; 666 struct group *gr; 667 char *p; 668 uid_t *uid; 669 gid_t *gid; 670 671 uid = &mip->mi_uid; 672 gid = &mip->mi_gid; 673 mip->mi_have_uid = mip->mi_have_gid = false; 674 675 /* Extract the user and group from 'str'. Format above. */ 676 ug = strdup(str); 677 assert(ug != NULL); 678 group = ug; 679 user = strsep(&group, ":"); 680 if (user == NULL || group == NULL || *user == '\0' || *group == '\0') 681 usage(); 682 683 /* Derive uid. */ 684 *uid = strtoul(user, &p, 10); 685 if (*uid == (uid_t)ULONG_MAX) 686 usage(); 687 if (*p != '\0') { 688 pw = getpwnam(user); 689 if (pw == NULL) 690 errx(1, "invalid user: %s", user); 691 *uid = pw->pw_uid; 692 } 693 mip->mi_have_uid = true; 694 695 /* Derive gid. */ 696 *gid = strtoul(group, &p, 10); 697 if (*gid == (gid_t)ULONG_MAX) 698 usage(); 699 if (*p != '\0') { 700 gr = getgrnam(group); 701 if (gr == NULL) 702 errx(1, "invalid group: %s", group); 703 *gid = gr->gr_gid; 704 } 705 mip->mi_have_gid = true; 706 707 free(ug); 708 } 709 710 /* 711 * Run a process with command name and arguments pointed to by the 712 * formatted string 'cmdline'. Since system(3) is not used, the first 713 * space-delimited token of 'cmdline' must be the full pathname of the 714 * program to run. 715 * 716 * The return value is the return code of the process spawned, or a negative 717 * signal number if the process exited due to an uncaught signal. 718 * 719 * If 'ofd' is non-NULL, it is set to the standard output of 720 * the program spawned (i.e., you can read from ofd and get the output 721 * of the program). 722 */ 723 static int 724 run(int *ofd, const char *cmdline, ...) 725 { 726 char **argv, **argvp; /* Result of splitting 'cmd'. */ 727 int argc; 728 char *cmd; /* Expansion of 'cmdline'. */ 729 int pid, status; /* Child info. */ 730 int pfd[2]; /* Pipe to the child. */ 731 int nfd; /* Null (/dev/null) file descriptor. */ 732 bool dup2dn; /* Dup /dev/null to stdout? */ 733 va_list ap; 734 char *p; 735 int rv, i; 736 737 dup2dn = true; 738 va_start(ap, cmdline); 739 rv = vasprintf(&cmd, cmdline, ap); 740 if (rv == -1) 741 err(1, "vasprintf"); 742 va_end(ap); 743 744 /* Split up 'cmd' into 'argv' for use with execve. */ 745 for (argc = 1, p = cmd; (p = strchr(p, ' ')) != NULL; p++) 746 argc++; /* 'argc' generation loop. */ 747 argv = (char **)malloc(sizeof(*argv) * (argc + 1)); 748 assert(argv != NULL); 749 for (p = cmd, argvp = argv; (*argvp = strsep(&p, " ")) != NULL;) 750 if (**argvp != '\0') 751 if (++argvp >= &argv[argc]) { 752 *argvp = NULL; 753 break; 754 } 755 assert(*argv); 756 /* The argv array ends up NULL-terminated here. */ 757 758 /* Make sure the above loop works as expected. */ 759 if (debug) { 760 /* 761 * We can't, but should, use debugprintf here. First, 762 * it appends a trailing newline to the output, and 763 * second it prepends "DEBUG: " to the output. The 764 * former is a problem for this would-be first call, 765 * and the latter for the would-be call inside the 766 * loop. 767 */ 768 (void)fprintf(stderr, "DEBUG: running:"); 769 /* Should be equivalent to 'cmd' (before strsep, of course). */ 770 for (i = 0; argv[i] != NULL; i++) 771 (void)fprintf(stderr, " %s", argv[i]); 772 (void)fprintf(stderr, "\n"); 773 } 774 775 /* Create a pipe if necessary and fork the helper program. */ 776 if (ofd != NULL) { 777 if (pipe(&pfd[0]) == -1) 778 err(1, "pipe"); 779 *ofd = pfd[0]; 780 dup2dn = false; 781 } 782 pid = fork(); 783 switch (pid) { 784 case 0: 785 /* XXX can we call err() in here? */ 786 if (norun) 787 _exit(0); 788 if (ofd != NULL) 789 if (dup2(pfd[1], STDOUT_FILENO) < 0) 790 err(1, "dup2"); 791 if (!loudsubs) { 792 nfd = open(_PATH_DEVNULL, O_RDWR); 793 if (nfd == -1) 794 err(1, "open: %s", _PATH_DEVNULL); 795 if (dup2(nfd, STDIN_FILENO) < 0) 796 err(1, "dup2"); 797 if (dup2dn) 798 if (dup2(nfd, STDOUT_FILENO) < 0) 799 err(1, "dup2"); 800 if (dup2(nfd, STDERR_FILENO) < 0) 801 err(1, "dup2"); 802 } 803 804 (void)execv(argv[0], argv); 805 warn("exec: %s", argv[0]); 806 _exit(-1); 807 case -1: 808 err(1, "fork"); 809 } 810 811 free(cmd); 812 free(argv); 813 while (waitpid(pid, &status, 0) != pid) 814 ; 815 if (WIFEXITED(status)) 816 return (WEXITSTATUS(status)); 817 if (WIFSIGNALED(status)) 818 return (-WTERMSIG(status)); 819 err(1, "unexpected waitpid status: 0x%x", status); 820 } 821 822 /* 823 * If run() returns non-zero, provide a string explaining why. 824 */ 825 static const char * 826 run_exitstr(int rv) 827 { 828 if (rv > 0) 829 return ("with error code"); 830 if (rv < 0) 831 return ("with signal"); 832 return (NULL); 833 } 834 835 /* 836 * If run returns non-zero, provide a relevant number. 837 */ 838 static int 839 run_exitnumber(int rv) 840 { 841 if (rv < 0) 842 return (-rv); 843 return (rv); 844 } 845 846 static void 847 usage(void) 848 { 849 850 fprintf(stderr, 851 "usage: %s [-DLlMNnPStUX] [-a maxcontig] [-b block-size]\n" 852 "\t[-c blocks-per-cylinder-group][-d max-extent-size] [-E path-mdconfig]\n" 853 "\t[-e maxbpg] [-F file] [-f frag-size] [-i bytes] [-k skel]\n" 854 "\t[-m percent-free] [-O optimization] [-o mount-options]\n" 855 "\t[-p permissions] [-s size] [-v version] [-w user:group]\n" 856 "\tmd-device mount-point\n", getprogname()); 857 exit(1); 858 } 859