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