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