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