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; 448 char linebuf[12]; /* 32-bit unit (10) + '\n' (1) + '\0' (1) */ 449 int fd; /* Standard output of mdconfig invocation. */ 450 FILE *sfd; 451 int rv; 452 char *p; 453 size_t linelen; 454 unsigned long ul; 455 456 switch (mdtype) { 457 case MD_SWAP: 458 ta = "-t swap"; 459 break; 460 case MD_VNODE: 461 ta = "-t vnode"; 462 break; 463 case MD_MALLOC: 464 ta = "-t malloc"; 465 break; 466 default: 467 abort(); 468 } 469 rv = run(&fd, "%s -a %s%s", path_mdconfig, ta, args); 470 if (rv) 471 errx(1, "mdconfig (attach) exited %s %d", run_exitstr(rv), 472 run_exitnumber(rv)); 473 474 /* Receive the unit number. */ 475 if (norun) { /* Since we didn't run, we can't read. Fake it. */ 476 unit = 0; 477 return; 478 } 479 sfd = fdopen(fd, "r"); 480 if (sfd == NULL) 481 err(1, "fdopen"); 482 linep = fgetln(sfd, &linelen); 483 /* If the output format changes, we want to know about it. */ 484 if (linep == NULL || linelen <= mdnamelen + 1 || 485 linelen - mdnamelen >= sizeof(linebuf) || 486 strncmp(linep, mdname, mdnamelen) != 0) 487 errx(1, "unexpected output from mdconfig (attach)"); 488 linep += mdnamelen; 489 linelen -= mdnamelen; 490 /* Can't use strlcpy because linep is not NULL-terminated. */ 491 strncpy(linebuf, linep, linelen); 492 linebuf[linelen] = '\0'; 493 ul = strtoul(linebuf, &p, 10); 494 if (ul == ULONG_MAX || *p != '\n') 495 errx(1, "unexpected output from mdconfig (attach)"); 496 unit = ul; 497 498 fclose(sfd); 499 } 500 501 /* 502 * Detach a memory disk. 503 */ 504 static void 505 do_mdconfig_detach(void) 506 { 507 int rv; 508 509 rv = run(NULL, "%s -d -u %s%d", path_mdconfig, mdname, unit); 510 if (rv && debug) /* This is allowed to fail. */ 511 warnx("mdconfig (detach) exited %s %d (ignored)", 512 run_exitstr(rv), run_exitnumber(rv)); 513 } 514 515 /* 516 * Mount the configured memory disk. 517 */ 518 static void 519 do_mount_md(const char *args, const char *mtpoint) 520 { 521 int rv; 522 523 rv = run(NULL, "%s%s /dev/%s%d%s %s", _PATH_MOUNT, args, 524 mdname, unit, mdsuffix, mtpoint); 525 if (rv) 526 errx(1, "mount exited %s %d", run_exitstr(rv), 527 run_exitnumber(rv)); 528 } 529 530 /* 531 * Mount the configured tmpfs. 532 */ 533 static void 534 do_mount_tmpfs(const char *args, const char *mtpoint) 535 { 536 int rv; 537 538 rv = run(NULL, "%s -t tmpfs %s tmp %s", _PATH_MOUNT, args, mtpoint); 539 if (rv) 540 errx(1, "tmpfs mount exited %s %d", run_exitstr(rv), 541 run_exitnumber(rv)); 542 } 543 544 /* 545 * Various configuration of the mountpoint. Mostly, enact 'mip'. 546 */ 547 static void 548 do_mtptsetup(const char *mtpoint, struct mtpt_info *mip) 549 { 550 struct statfs sfs; 551 552 if (!mip->mi_have_mode && !mip->mi_have_uid && !mip->mi_have_gid) 553 return; 554 555 if (!norun) { 556 if (statfs(mtpoint, &sfs) == -1) { 557 warn("statfs: %s", mtpoint); 558 return; 559 } 560 if ((sfs.f_flags & MNT_RDONLY) != 0) { 561 if (mip->mi_forced_pw) { 562 warnx( 563 "Not changing mode/owner of %s since it is read-only", 564 mtpoint); 565 } else { 566 debugprintf( 567 "Not changing mode/owner of %s since it is read-only", 568 mtpoint); 569 } 570 return; 571 } 572 } 573 574 if (mip->mi_have_mode) { 575 debugprintf("changing mode of %s to %o.", mtpoint, 576 mip->mi_mode); 577 if (!norun) 578 if (chmod(mtpoint, mip->mi_mode) == -1) 579 err(1, "chmod: %s", mtpoint); 580 } 581 /* 582 * We have to do these separately because the user may have 583 * only specified one of them. 584 */ 585 if (mip->mi_have_uid) { 586 debugprintf("changing owner (user) or %s to %u.", mtpoint, 587 mip->mi_uid); 588 if (!norun) 589 if (chown(mtpoint, mip->mi_uid, -1) == -1) 590 err(1, "chown %s to %u (user)", mtpoint, 591 mip->mi_uid); 592 } 593 if (mip->mi_have_gid) { 594 debugprintf("changing owner (group) or %s to %u.", mtpoint, 595 mip->mi_gid); 596 if (!norun) 597 if (chown(mtpoint, -1, mip->mi_gid) == -1) 598 err(1, "chown %s to %u (group)", mtpoint, 599 mip->mi_gid); 600 } 601 } 602 603 /* 604 * Put a file system on the memory disk. 605 */ 606 static void 607 do_newfs(const char *args) 608 { 609 int rv; 610 611 rv = run(NULL, "%s%s /dev/%s%d", _PATH_NEWFS, args, mdname, unit); 612 if (rv) 613 errx(1, "newfs exited %s %d", run_exitstr(rv), 614 run_exitnumber(rv)); 615 } 616 617 /* 618 * 'str' should be a user and group name similar to the last argument 619 * to chown(1); i.e., a user, followed by a colon, followed by a 620 * group. The user and group in 'str' may be either a [ug]id or a 621 * name. Upon return, the uid and gid fields in 'mip' will contain 622 * the uid and gid of the user and group name in 'str', respectively. 623 * 624 * In other words, this derives a user and group id from a string 625 * formatted like the last argument to chown(1). 626 * 627 * Notice: At this point we don't support only a username or only a 628 * group name. do_mtptsetup already does, so when this feature is 629 * desired, this is the only routine that needs to be changed. 630 */ 631 static void 632 extract_ugid(const char *str, struct mtpt_info *mip) 633 { 634 char *ug; /* Writable 'str'. */ 635 char *user, *group; /* Result of extracton. */ 636 struct passwd *pw; 637 struct group *gr; 638 char *p; 639 uid_t *uid; 640 gid_t *gid; 641 642 uid = &mip->mi_uid; 643 gid = &mip->mi_gid; 644 mip->mi_have_uid = mip->mi_have_gid = false; 645 646 /* Extract the user and group from 'str'. Format above. */ 647 ug = strdup(str); 648 assert(ug != NULL); 649 group = ug; 650 user = strsep(&group, ":"); 651 if (user == NULL || group == NULL || *user == '\0' || *group == '\0') 652 usage(); 653 654 /* Derive uid. */ 655 *uid = strtoul(user, &p, 10); 656 if (*uid == (uid_t)ULONG_MAX) 657 usage(); 658 if (*p != '\0') { 659 pw = getpwnam(user); 660 if (pw == NULL) 661 errx(1, "invalid user: %s", user); 662 *uid = pw->pw_uid; 663 } 664 mip->mi_have_uid = true; 665 666 /* Derive gid. */ 667 *gid = strtoul(group, &p, 10); 668 if (*gid == (gid_t)ULONG_MAX) 669 usage(); 670 if (*p != '\0') { 671 gr = getgrnam(group); 672 if (gr == NULL) 673 errx(1, "invalid group: %s", group); 674 *gid = gr->gr_gid; 675 } 676 mip->mi_have_gid = true; 677 678 free(ug); 679 } 680 681 /* 682 * Run a process with command name and arguments pointed to by the 683 * formatted string 'cmdline'. Since system(3) is not used, the first 684 * space-delimited token of 'cmdline' must be the full pathname of the 685 * program to run. 686 * 687 * The return value is the return code of the process spawned, or a negative 688 * signal number if the process exited due to an uncaught signal. 689 * 690 * If 'ofd' is non-NULL, it is set to the standard output of 691 * the program spawned (i.e., you can read from ofd and get the output 692 * of the program). 693 */ 694 static int 695 run(int *ofd, const char *cmdline, ...) 696 { 697 char **argv, **argvp; /* Result of splitting 'cmd'. */ 698 int argc; 699 char *cmd; /* Expansion of 'cmdline'. */ 700 int pid, status; /* Child info. */ 701 int pfd[2]; /* Pipe to the child. */ 702 int nfd; /* Null (/dev/null) file descriptor. */ 703 bool dup2dn; /* Dup /dev/null to stdout? */ 704 va_list ap; 705 char *p; 706 int rv, i; 707 708 dup2dn = true; 709 va_start(ap, cmdline); 710 rv = vasprintf(&cmd, cmdline, ap); 711 if (rv == -1) 712 err(1, "vasprintf"); 713 va_end(ap); 714 715 /* Split up 'cmd' into 'argv' for use with execve. */ 716 for (argc = 1, p = cmd; (p = strchr(p, ' ')) != NULL; p++) 717 argc++; /* 'argc' generation loop. */ 718 argv = (char **)malloc(sizeof(*argv) * (argc + 1)); 719 assert(argv != NULL); 720 for (p = cmd, argvp = argv; (*argvp = strsep(&p, " ")) != NULL;) 721 if (**argvp != '\0') 722 if (++argvp >= &argv[argc]) { 723 *argvp = NULL; 724 break; 725 } 726 assert(*argv); 727 /* The argv array ends up NULL-terminated here. */ 728 729 /* Make sure the above loop works as expected. */ 730 if (debug) { 731 /* 732 * We can't, but should, use debugprintf here. First, 733 * it appends a trailing newline to the output, and 734 * second it prepends "DEBUG: " to the output. The 735 * former is a problem for this would-be first call, 736 * and the latter for the would-be call inside the 737 * loop. 738 */ 739 (void)fprintf(stderr, "DEBUG: running:"); 740 /* Should be equivalent to 'cmd' (before strsep, of course). */ 741 for (i = 0; argv[i] != NULL; i++) 742 (void)fprintf(stderr, " %s", argv[i]); 743 (void)fprintf(stderr, "\n"); 744 } 745 746 /* Create a pipe if necessary and fork the helper program. */ 747 if (ofd != NULL) { 748 if (pipe(&pfd[0]) == -1) 749 err(1, "pipe"); 750 *ofd = pfd[0]; 751 dup2dn = false; 752 } 753 pid = fork(); 754 switch (pid) { 755 case 0: 756 /* XXX can we call err() in here? */ 757 if (norun) 758 _exit(0); 759 if (ofd != NULL) 760 if (dup2(pfd[1], STDOUT_FILENO) < 0) 761 err(1, "dup2"); 762 if (!loudsubs) { 763 nfd = open(_PATH_DEVNULL, O_RDWR); 764 if (nfd == -1) 765 err(1, "open: %s", _PATH_DEVNULL); 766 if (dup2(nfd, STDIN_FILENO) < 0) 767 err(1, "dup2"); 768 if (dup2dn) 769 if (dup2(nfd, STDOUT_FILENO) < 0) 770 err(1, "dup2"); 771 if (dup2(nfd, STDERR_FILENO) < 0) 772 err(1, "dup2"); 773 } 774 775 (void)execv(argv[0], argv); 776 warn("exec: %s", argv[0]); 777 _exit(-1); 778 case -1: 779 err(1, "fork"); 780 } 781 782 free(cmd); 783 free(argv); 784 while (waitpid(pid, &status, 0) != pid) 785 ; 786 if (WIFEXITED(status)) 787 return (WEXITSTATUS(status)); 788 if (WIFSIGNALED(status)) 789 return (-WTERMSIG(status)); 790 err(1, "unexpected waitpid status: 0x%x", status); 791 } 792 793 /* 794 * If run() returns non-zero, provide a string explaining why. 795 */ 796 static const char * 797 run_exitstr(int rv) 798 { 799 if (rv > 0) 800 return ("with error code"); 801 if (rv < 0) 802 return ("with signal"); 803 return (NULL); 804 } 805 806 /* 807 * If run returns non-zero, provide a relevant number. 808 */ 809 static int 810 run_exitnumber(int rv) 811 { 812 if (rv < 0) 813 return (-rv); 814 return (rv); 815 } 816 817 static void 818 usage(void) 819 { 820 821 fprintf(stderr, 822 "usage: %s [-DLlMNnPStUX] [-a maxcontig] [-b block-size]\n" 823 "\t[-c blocks-per-cylinder-group][-d max-extent-size] [-E path-mdconfig]\n" 824 "\t[-e maxbpg] [-F file] [-f frag-size] [-i bytes] [-m percent-free]\n" 825 "\t[-O optimization] [-o mount-options]\n" 826 "\t[-p permissions] [-s size] [-v version] [-w user:group]\n" 827 "\tmd-device mount-point\n", getprogname()); 828 exit(1); 829 } 830