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