mdmfs.c revision 78711
178447Sdd/* 278447Sdd * Copyright (c) 2001 Dima Dorfman <dd@FreeBSD.org> 378447Sdd * All rights reserved. 478447Sdd * 578447Sdd * Redistribution and use in source and binary forms, with or without 678447Sdd * modification, are permitted provided that the following conditions 778447Sdd * are met: 878447Sdd * 1. Redistributions of source code must retain the above copyright 978447Sdd * notice, this list of conditions and the following disclaimer. 1078447Sdd * 2. Redistributions in binary form must reproduce the above copyright 1178447Sdd * notice, this list of conditions and the following disclaimer in the 1278447Sdd * documentation and/or other materials provided with the distribution. 1378447Sdd * 1478447Sdd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1578447Sdd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1678447Sdd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1778447Sdd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1878447Sdd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1978447Sdd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2078447Sdd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2178447Sdd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2278447Sdd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2378447Sdd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2478447Sdd * SUCH DAMAGE. 2578447Sdd */ 2678447Sdd 2778447Sdd/* 2878447Sdd * mdmfs (md/MFS) is a wrapper around mdconfig(8), disklabel(8), 2978447Sdd * newfs(8), and mount(8) that mimics the command line option set of 3078447Sdd * the deprecated mount_mfs(8). 3178447Sdd */ 3278447Sdd 3378447Sdd#ifndef lint 3478447Sddstatic const char rcsid[] = 3578447Sdd "$FreeBSD: head/sbin/mdmfs/mdmfs.c 78711 2001-06-24 18:21:52Z dd $"; 3678447Sdd#endif /* not lint */ 3778447Sdd 3878447Sdd#include <sys/param.h> 3978447Sdd#include <sys/mdioctl.h> 4078447Sdd#include <sys/stat.h> 4178447Sdd#include <sys/wait.h> 4278447Sdd 4378447Sdd#include <assert.h> 4478447Sdd#include <err.h> 4578447Sdd#include <fcntl.h> 4678447Sdd#include <grp.h> 4778447Sdd#include <paths.h> 4878447Sdd#include <pwd.h> 4978447Sdd#include <stdarg.h> 5078447Sdd#include <stdio.h> 5178447Sdd#include <stdlib.h> 5278447Sdd#include <string.h> 5378447Sdd#include <unistd.h> 5478447Sdd 5578447Sdd#include "pathnames.h" 5678447Sdd 5778447Sddtypedef enum { false, true } bool; 5878447Sdd 5978447Sddstruct mtpt_info { 6078447Sdd uid_t mi_uid; 6178447Sdd bool mi_have_uid; 6278447Sdd gid_t mi_gid; 6378447Sdd bool mi_have_gid; 6478447Sdd mode_t mi_mode; 6578447Sdd bool mi_have_mode; 6678447Sdd}; 6778447Sdd 6878447Sddstatic bool debug; /* Emit debugging information? */ 6978447Sddstatic bool loudsubs; /* Suppress output from helper programs? */ 7078447Sddstatic bool norun; /* Actually run the helper programs? */ 7178447Sddstatic int unit; /* The unit we're working with. */ 7278447Sddstatic const char *mdname; /* Name of memory disk device (e.g., "md"). */ 7378447Sddstatic size_t mdnamelen; /* Length of mdname. */ 7478447Sdd 7578447Sddstatic void argappend(char **, const char *, ...); 7678447Sddstatic void debugprintf(const char *, ...); 7778447Sddstatic void do_disklabel(void); 7878447Sddstatic void do_mdconfig_attach(const char *, const enum md_types); 7978447Sddstatic void do_mdconfig_attach_au(const char *, const enum md_types); 8078447Sddstatic void do_mdconfig_detach(void); 8178447Sddstatic void do_mount(const char *, const char *); 8278447Sddstatic void do_mtptsetup(const char *, struct mtpt_info *); 8378447Sddstatic void do_newfs(const char *); 8478447Sddstatic void extract_ugid(const char *, struct mtpt_info *); 8578447Sddstatic int run(int *, const char *, ...); 8678447Sddstatic void usage(void); 8778447Sdd 8878447Sddint 8978447Sddmain(int ac, char **av) 9078447Sdd{ 9178447Sdd struct mtpt_info mi; /* Mountpoint info. */ 9278447Sdd char *mdconfig_arg, *newfs_arg, /* Args to helper programs. */ 9378447Sdd *mount_arg; 9478447Sdd enum md_types mdtype; /* The type of our memory disk. */ 9578447Sdd bool have_mdtype; 9678447Sdd bool detach, softdep, autounit; 9778447Sdd char *mtpoint, *unitstr; 9878447Sdd char ch, *p; 9978447Sdd 10078447Sdd /* Misc. initialization. */ 10178447Sdd (void)memset(&mi, '\0', sizeof(mi)); 10278447Sdd detach = true; 10378447Sdd softdep = true; 10478447Sdd autounit = false; 10578447Sdd have_mdtype = false; 10678447Sdd mdname = MD_NAME; 10778447Sdd mdnamelen = strlen(mdname); 10878447Sdd /* 10978447Sdd * Can't set these to NULL. They may be passed to the 11078447Sdd * respective programs without modification. I.e., we may not 11178447Sdd * receive any command-line options which will caused them to 11278447Sdd * be modified. 11378447Sdd */ 11478447Sdd mdconfig_arg = strdup(""); 11578447Sdd newfs_arg = strdup(""); 11678447Sdd mount_arg = strdup(""); 11778447Sdd 11878447Sdd while ((ch = getopt(ac, av, 11978447Sdd "a:b:c:Dd:e:F:f:hi:LMm:Nn:O:o:p:Ss:t:w:X")) != -1) 12078447Sdd switch (ch) { 12178447Sdd case 'a': 12278447Sdd argappend(&newfs_arg, "-a %s", optarg); 12378447Sdd break; 12478447Sdd case 'b': 12578447Sdd argappend(&newfs_arg, "-b %s", optarg); 12678447Sdd break; 12778447Sdd case 'c': 12878447Sdd argappend(&newfs_arg, "-c %s", optarg); 12978447Sdd break; 13078447Sdd case 'D': 13178447Sdd detach = false; 13278447Sdd break; 13378447Sdd case 'd': 13478447Sdd argappend(&newfs_arg, "-d %s", optarg); 13578447Sdd break; 13678447Sdd case 'e': 13778447Sdd argappend(&newfs_arg, "-e %s", optarg); 13878447Sdd break; 13978447Sdd case 'F': 14078447Sdd if (have_mdtype) 14178447Sdd usage(); 14278447Sdd mdtype = MD_VNODE; 14378447Sdd have_mdtype = true; 14478447Sdd argappend(&mdconfig_arg, "-f %s", optarg); 14578447Sdd break; 14678447Sdd case 'f': 14778447Sdd argappend(&newfs_arg, "-f %s", optarg); 14878447Sdd break; 14978447Sdd case 'h': 15078447Sdd usage(); 15178447Sdd break; 15278447Sdd case 'i': 15378447Sdd argappend(&newfs_arg, "-i %s", optarg); 15478447Sdd break; 15578447Sdd case 'L': 15678447Sdd loudsubs = true; 15778447Sdd break; 15878447Sdd case 'M': 15978447Sdd if (have_mdtype) 16078447Sdd usage(); 16178447Sdd mdtype = MD_MALLOC; 16278447Sdd have_mdtype = true; 16378447Sdd break; 16478447Sdd case 'm': 16578447Sdd argappend(&newfs_arg, "-m %s", optarg); 16678447Sdd break; 16778447Sdd case 'N': 16878447Sdd norun = true; 16978447Sdd break; 17078447Sdd case 'n': 17178447Sdd argappend(&newfs_arg, "-n %s", optarg); 17278447Sdd break; 17378447Sdd case 'O': 17478447Sdd argappend(&newfs_arg, "-o %s", optarg); 17578447Sdd break; 17678447Sdd case 'o': 17778447Sdd argappend(&mount_arg, "-o %s", optarg); 17878447Sdd break; 17978447Sdd case 'p': 18078447Sdd if (*optarg >= '0' && *optarg <= '7') 18178447Sdd mi.mi_mode = strtol(optarg, NULL, 8); 18278447Sdd if ((mi.mi_mode & ~07777) != 0) 18378447Sdd usage(); 18478447Sdd mi.mi_have_mode = true; 18578447Sdd break; 18678447Sdd case 'S': 18778447Sdd softdep = false; 18878447Sdd break; 18978447Sdd case 's': 19078447Sdd argappend(&mdconfig_arg, "-s %s", optarg); 19178447Sdd break; 19278447Sdd case 'w': 19378447Sdd extract_ugid(optarg, &mi); 19478447Sdd break; 19578447Sdd case 'X': 19678447Sdd debug = true; 19778447Sdd break; 19878447Sdd default: 19978447Sdd usage(); 20078447Sdd } 20178447Sdd ac -= optind; 20278447Sdd av += optind; 20378447Sdd if (ac < 2) 20478447Sdd usage(); 20578447Sdd 20678447Sdd /* Derive 'unit' (global). */ 20778447Sdd unitstr = av[0]; 20878447Sdd if (strncmp(unitstr, "/dev/", 5) == 0) 20978447Sdd unitstr += 5; 21078447Sdd if (strncmp(unitstr, mdname, mdnamelen) == 0) 21178447Sdd unitstr += mdnamelen; 21278447Sdd if (*unitstr == '\0') { 21378447Sdd autounit = true; 21478447Sdd unit = -1; 21578447Sdd } else { 21678447Sdd unit = strtoul(unitstr, &p, 10); 21778447Sdd if ((unsigned)unit == ULONG_MAX || *p != '\0') 21878447Sdd errx(1, "bad device unit: %s", unitstr); 21978447Sdd } 22078447Sdd 22178447Sdd mtpoint = av[1]; 22278447Sdd if (!have_mdtype) 22378447Sdd mdtype = MD_SWAP; 22478447Sdd if (softdep) 22578447Sdd argappend(&newfs_arg, "-U"); 22678447Sdd 22778447Sdd /* Do the work. */ 22878447Sdd if (detach && !autounit) 22978447Sdd do_mdconfig_detach(); 23078447Sdd if (autounit) 23178447Sdd do_mdconfig_attach_au(mdconfig_arg, mdtype); 23278447Sdd else 23378447Sdd do_mdconfig_attach(mdconfig_arg, mdtype); 23478447Sdd do_disklabel(); 23578447Sdd do_newfs(newfs_arg); 23678447Sdd do_mount(mount_arg, mtpoint); 23778447Sdd do_mtptsetup(mtpoint, &mi); 23878447Sdd 23978447Sdd return (0); 24078447Sdd} 24178447Sdd 24278447Sdd/* 24378447Sdd * Append the expansion of 'fmt' to the buffer pointed to by '*dstp'; 24478447Sdd * reallocate as required. 24578447Sdd */ 24678447Sddstatic void 24778447Sddargappend(char **dstp, const char *fmt, ...) 24878447Sdd{ 24978447Sdd char *old, *new; 25078447Sdd va_list ap; 25178447Sdd 25278447Sdd old = *dstp; 25378447Sdd assert(old != NULL); 25478447Sdd 25578447Sdd va_start(ap, fmt); 25678447Sdd if (vasprintf(&new, fmt,ap) == -1) 25778447Sdd errx(1, "vasprintf"); 25878447Sdd va_end(ap); 25978447Sdd 26078447Sdd *dstp = new; 26178447Sdd if (asprintf(&new, "%s %s", old, new) == -1) 26278447Sdd errx(1, "asprintf"); 26378447Sdd free(*dstp); 26478447Sdd free(old); 26578447Sdd 26678447Sdd *dstp = new; 26778447Sdd} 26878447Sdd 26978447Sdd/* 27078447Sdd * If run-time debugging is enabled, print the expansion of 'fmt'. 27178447Sdd * Otherwise, do nothing. 27278447Sdd */ 27378447Sddstatic void 27478447Sdddebugprintf(const char *fmt, ...) 27578447Sdd{ 27678447Sdd va_list ap; 27778447Sdd 27878447Sdd if (!debug) 27978447Sdd return; 28078447Sdd fprintf(stderr, "DEBUG: "); 28178447Sdd va_start(ap, fmt); 28278447Sdd vfprintf(stderr, fmt, ap); 28378447Sdd va_end(ap); 28478447Sdd fprintf(stderr, "\n"); 28578447Sdd fflush(stderr); 28678447Sdd} 28778447Sdd 28878447Sdd/* 28978447Sdd * Label the memory disk. 29078447Sdd */ 29178447Sddstatic void 29278447Sdddo_disklabel(void) 29378447Sdd{ 29478447Sdd int rv; 29578447Sdd 29678447Sdd rv = run(NULL, "%s -r -w %s%d auto", PATH_DISKLABEL, mdname, unit); 29778447Sdd if (rv) 29878447Sdd errx(1, "disklabel exited with error code %d", rv); 29978447Sdd} 30078447Sdd 30178447Sdd/* 30278447Sdd * Attach a memory disk with a known unit. 30378447Sdd */ 30478447Sddstatic void 30578447Sdddo_mdconfig_attach(const char *args, const enum md_types mdtype) 30678447Sdd{ 30778447Sdd int rv; 30878447Sdd const char *ta; /* Type arg. */ 30978447Sdd 31078447Sdd switch (mdtype) { 31178447Sdd case MD_SWAP: 31278447Sdd ta = "-t swap"; 31378447Sdd break; 31478447Sdd case MD_VNODE: 31578447Sdd ta = "-t vnode"; 31678447Sdd break; 31778447Sdd case MD_MALLOC: 31878447Sdd ta = "-t malloc"; 31978447Sdd break; 32078447Sdd default: 32178447Sdd abort(); 32278447Sdd } 32378447Sdd rv = run(NULL, "%s -a %s%s -u %s%d", PATH_MDCONFIG, ta, args, 32478447Sdd mdname, unit); 32578447Sdd if (rv) 32678447Sdd errx(1, "mdconfig (attach) exited with error code %d", rv); 32778447Sdd} 32878447Sdd 32978447Sdd/* 33078447Sdd * Attach a memory disk with an unknown unit; use autounit. 33178447Sdd */ 33278447Sddstatic void 33378447Sdddo_mdconfig_attach_au(const char *args, const enum md_types mdtype) 33478447Sdd{ 33578447Sdd const char *ta; /* Type arg. */ 33678447Sdd char *linep, *linebuf; /* Line pointer, line buffer. */ 33778447Sdd int fd; /* Standard output of mdconfig invocation. */ 33878447Sdd FILE *sfd; 33978447Sdd int rv; 34078447Sdd char *p; 34178447Sdd size_t linelen; 34278447Sdd 34378447Sdd switch (mdtype) { 34478447Sdd case MD_SWAP: 34578447Sdd ta = "-t swap"; 34678447Sdd break; 34778447Sdd case MD_VNODE: 34878447Sdd ta = "-t vnode"; 34978447Sdd break; 35078447Sdd case MD_MALLOC: 35178447Sdd ta = "-t malloc"; 35278447Sdd break; 35378447Sdd default: 35478447Sdd abort(); 35578447Sdd } 35678447Sdd rv = run(&fd, "%s -a %s%s", PATH_MDCONFIG, ta, args); 35778447Sdd if (rv) 35878447Sdd errx(1, "mdconfig (attach) exited with error code %d", rv); 35978447Sdd 36078447Sdd /* Receive the unit number. */ 36178447Sdd if (norun) { /* Since we didn't run, we can't read. Fake it. */ 36278447Sdd unit = -1; 36378447Sdd return; 36478447Sdd } 36578447Sdd sfd = fdopen(fd, "r"); 36678447Sdd if (sfd == NULL) 36778447Sdd err(1, "fdopen"); 36878447Sdd linep = fgetln(sfd, &linelen); 36978447Sdd if (linep == NULL && linelen < mdnamelen + 1) 37078447Sdd errx(1, "unexpected output from mdconfig (attach)"); 37178447Sdd /* If the output format changes, we want to know about it. */ 37278447Sdd assert(strncmp(linep, mdname, mdnamelen) == 0); 37378447Sdd linebuf = malloc(linelen - mdnamelen + 1); 37478447Sdd assert(linebuf != NULL); 37578447Sdd /* Can't use strlcpy because linep is not NULL-terminated. */ 37678447Sdd strncpy(linebuf, linep + mdnamelen, linelen); 37778447Sdd linebuf[linelen] = '\0'; 37878447Sdd unit = strtoul(linebuf, &p, 10); 37978447Sdd if ((unsigned)unit == ULONG_MAX || *p != '\n') 38078447Sdd errx(1, "unexpected output from mdconfig (attach)"); 38178447Sdd 38278447Sdd fclose(sfd); 38378447Sdd close(fd); 38478447Sdd} 38578447Sdd 38678447Sdd/* 38778447Sdd * Detach a memory disk. 38878447Sdd */ 38978447Sddstatic void 39078447Sdddo_mdconfig_detach(void) 39178447Sdd{ 39278447Sdd int rv; 39378447Sdd 39478447Sdd rv = run(NULL, "%s -d -u %s%d", PATH_MDCONFIG, mdname, unit); 39578447Sdd if (rv && debug) /* This is allowed to fail. */ 39678447Sdd warnx("mdconfig (detach) exited with error code %d (ignored)", 39778447Sdd rv); 39878447Sdd} 39978447Sdd 40078447Sdd/* 40178447Sdd * Mount the configured memory disk. 40278447Sdd */ 40378447Sddstatic void 40478447Sdddo_mount(const char *args, const char *mtpoint) 40578447Sdd{ 40678447Sdd int rv; 40778447Sdd 40878447Sdd rv = run(NULL, "%s%s /dev/%s%dc %s", PATH_MOUNT, args, 40978447Sdd mdname, unit, mtpoint); 41078447Sdd if (rv) 41178447Sdd errx(1, "mount exited with error code %d", rv); 41278447Sdd} 41378447Sdd 41478447Sdd/* 41578447Sdd * Various configuration of the mountpoint. Mostly, enact 'mip'. 41678447Sdd */ 41778447Sddstatic void 41878447Sdddo_mtptsetup(const char *mtpoint, struct mtpt_info *mip) 41978447Sdd{ 42078447Sdd 42178447Sdd if (mip->mi_have_mode) { 42278447Sdd debugprintf("changing mode of %s to %o.", mtpoint, 42378447Sdd mip->mi_mode); 42478447Sdd if (!norun) 42578447Sdd if (chmod(mtpoint, mip->mi_mode) == -1) 42678447Sdd err(1, "chmod: %s", mtpoint); 42778447Sdd } 42878447Sdd /* 42978447Sdd * We have to do these separately because the user may have 43078447Sdd * only specified one of them. 43178447Sdd */ 43278447Sdd if (mip->mi_have_uid) { 43378447Sdd debugprintf("changing owner (user) or %s to %u.", mtpoint, 43478447Sdd mip->mi_uid); 43578447Sdd if (!norun) 43678447Sdd if (chown(mtpoint, mip->mi_uid, -1) == -1) 43778447Sdd err(1, "chown %s to %u (user)", mtpoint, 43878447Sdd mip->mi_uid); 43978447Sdd } 44078447Sdd if (mip->mi_have_gid) { 44178447Sdd debugprintf("changing owner (group) or %s to %u.", mtpoint, 44278447Sdd mip->mi_gid); 44378447Sdd if (!norun) 44478447Sdd if (chown(mtpoint, -1, mip->mi_gid) == -1) 44578447Sdd err(1, "chown %s to %u (group)", mtpoint, 44678447Sdd mip->mi_gid); 44778447Sdd } 44878447Sdd} 44978447Sdd 45078447Sdd/* 45178447Sdd * Put a filesystem on the memory disk. 45278447Sdd */ 45378447Sddstatic void 45478447Sdddo_newfs(const char *args) 45578447Sdd{ 45678447Sdd int rv; 45778447Sdd 45878447Sdd rv = run(NULL, "%s%s /dev/%s%dc", PATH_NEWFS, args, mdname, unit); 45978447Sdd if (rv) 46078447Sdd errx(1, "newfs exited with error code %d", rv); 46178447Sdd} 46278447Sdd 46378447Sdd/* 46478447Sdd * 'str' should be a user and group name similar to the last argument 46578447Sdd * to chown(1); i.e., a user, followed by a colon, followed by a 46678447Sdd * group. The user and group in 'str' may be either a [ug]id or a 46778447Sdd * name. Upon return, the uid and gid fields in 'mip' will contain 46878447Sdd * the uid and gid of the user and group name in 'str', respectively. 46978447Sdd * 47078447Sdd * In other words, this derives a user and group id from a string 47178447Sdd * formatted like the last argument to chown(1). 47278447Sdd */ 47378447Sddstatic void 47478447Sddextract_ugid(const char *str, struct mtpt_info *mip) 47578447Sdd{ 47678447Sdd char *ug; /* Writable 'str'. */ 47778447Sdd char *user, *group; /* Result of extracton. */ 47878447Sdd struct passwd *pw; 47978447Sdd struct group *gr; 48078447Sdd char *p; 48178447Sdd uid_t *uid; 48278447Sdd gid_t *gid; 48378447Sdd 48478447Sdd uid = &mip->mi_uid; 48578447Sdd gid = &mip->mi_gid; 48678447Sdd mip->mi_have_uid = mip->mi_have_gid = false; 48778447Sdd 48878447Sdd /* Extract the user and group from 'str'. Format above. */ 48978711Sdd ug = strdup(str); 49078447Sdd assert(ug != NULL); 49178447Sdd group = ug; 49278447Sdd user = strsep(&group, ":"); 49378447Sdd if (user == NULL || group == NULL || *user == '\0' || *group == '\0') 49478447Sdd usage(); 49578447Sdd 49678447Sdd /* Derive uid. */ 49778447Sdd *uid = strtoul(user, &p, 10); 49878447Sdd if ((unsigned)*uid == ULONG_MAX) 49978447Sdd usage(); 50078447Sdd if (*p != '\0') { 50178447Sdd pw = getpwnam(user); 50278447Sdd if (pw == NULL) 50378447Sdd errx(1, "invalid user: %s", user); 50478447Sdd *uid = pw->pw_uid; 50578447Sdd mip->mi_have_uid = true; 50678447Sdd } 50778447Sdd 50878447Sdd /* Derive gid. */ 50978447Sdd *gid = strtoul(group, &p, 10); 51078447Sdd if ((unsigned)*gid == ULONG_MAX) 51178447Sdd usage(); 51278447Sdd if (*p != '\0') { 51378447Sdd gr = getgrnam(group); 51478447Sdd if (gr == NULL) 51578447Sdd errx(1, "invalid group: %s", group); 51678447Sdd *gid = gr->gr_gid; 51778447Sdd mip->mi_have_gid = true; 51878447Sdd } 51978447Sdd 52078447Sdd free(ug); 52178447Sdd /* 52278447Sdd * At this point we don't support only a username or only a 52378447Sdd * group name. do_mtptsetup already does, so when this 52478447Sdd * feature is desired, this is the only routine that needs to 52578447Sdd * be changed. 52678447Sdd */ 52778447Sdd assert(mip->mi_have_uid); 52878447Sdd assert(mip->mi_have_gid); 52978447Sdd} 53078447Sdd 53178447Sdd/* 53278447Sdd * Run a process with command name and arguments pointed to by the 53378447Sdd * formatted string 'cmdline'. Since system(3) is not used, the first 53478447Sdd * space-delimited token of 'cmdline' must be the full pathname of the 53578447Sdd * program to run. The return value is the return code of the process 53678447Sdd * spawned. If 'ofd' is non-NULL, it is set to the standard output of 53778447Sdd * the program spawned (i.e., you can read from ofd and get the output 53878447Sdd * of the program). 53978447Sdd */ 54078447Sddstatic int 54178447Sddrun(int *ofd, const char *cmdline, ...) 54278447Sdd{ 54378447Sdd char **av, **avp; /* Result of splitting 'cmd'. */ 54478447Sdd int ac; 54578447Sdd char *cmd; /* Expansion of 'cmdline'. */ 54678447Sdd int pid, status; /* Child info. */ 54778447Sdd int pfd[2]; /* Pipe to the child. */ 54878447Sdd int nfd; /* Null (/dev/null) file descriptor. */ 54978447Sdd bool dup2dn; /* Dup /dev/null to stdout? */ 55078447Sdd va_list ap; 55178447Sdd char *p; 55278447Sdd int rv, i; 55378447Sdd 55478447Sdd dup2dn = true; 55578447Sdd va_start(ap, cmdline); 55678447Sdd rv = vasprintf(&cmd, cmdline, ap); 55778447Sdd if (rv == -1) 55878447Sdd err(1, "vasprintf"); 55978447Sdd va_end(ap); 56078447Sdd 56178447Sdd /* Split up 'cmd' into 'av' for use with execve. */ 56278447Sdd for (ac = 1, p = cmd; (p = strchr(p, ' ')) != NULL; p++) 56378447Sdd ac++; /* 'ac' generation loop. */ 56478447Sdd av = (char **)malloc(sizeof(*av) * (ac + 1)); 56578447Sdd assert(av != NULL); 56678447Sdd for (p = cmd, avp = av; (*avp = strsep(&p, " ")) != NULL;) 56778447Sdd if (**av != '\0') 56878447Sdd if (++avp >= &av[ac]) { 56978447Sdd *avp = NULL; 57078447Sdd break; 57178447Sdd } 57278447Sdd assert(*av); 57378447Sdd 57478447Sdd /* Make sure the above loop works as expected. */ 57578447Sdd if (debug) { 57678447Sdd /* 57778447Sdd * We can't, but should, use debugprintf here. First, 57878447Sdd * it appends a trailing newline to the output, and 57978447Sdd * second it prepends "DEBUG: " to the output. The 58078447Sdd * former is a problem for this would-be first call, 58178447Sdd * and the latter for the would-be call inside the 58278447Sdd * loop. 58378447Sdd */ 58478447Sdd (void)fprintf(stderr, "DEBUG: running:"); 58578447Sdd /* Should be equivilent to 'cmd' (before strsep, of course). */ 58678447Sdd for (i = 0; av[i] != NULL; i++) 58778447Sdd (void)fprintf(stderr, " %s", av[i]); 58878447Sdd (void)fprintf(stderr, "\n"); 58978447Sdd } 59078447Sdd 59178447Sdd /* Create a pipe if necessary and fork the helper program. */ 59278447Sdd if (ofd != NULL) { 59378447Sdd if (pipe(&pfd[0]) == -1) 59478447Sdd err(1, "pipe"); 59578447Sdd *ofd = pfd[0]; 59678447Sdd dup2dn = false; 59778447Sdd } 59878447Sdd pid = fork(); 59978447Sdd switch (pid) { 60078447Sdd case 0: 60178447Sdd /* XXX can we call err() in here? */ 60278447Sdd if (norun) 60378447Sdd _exit(0); 60478447Sdd if (ofd != NULL) 60578447Sdd if (dup2(pfd[1], STDOUT_FILENO) < 0) 60678447Sdd err(1, "dup2"); 60778447Sdd if (!loudsubs) { 60878447Sdd nfd = open(_PATH_DEVNULL, O_RDWR); 60978447Sdd if (nfd == -1) 61078447Sdd err(1, "open: %s", _PATH_DEVNULL); 61178447Sdd if (dup2(nfd, STDIN_FILENO) < 0) 61278447Sdd err(1, "dup2"); 61378447Sdd if (dup2dn) 61478447Sdd if (dup2(nfd, STDOUT_FILENO) < 0) 61578447Sdd err(1, "dup2"); 61678447Sdd if (dup2(nfd, STDERR_FILENO) < 0) 61778447Sdd err(1, "dup2"); 61878447Sdd } 61978447Sdd 62078447Sdd (void)execv(av[0], av); 62178447Sdd warn("exec: %s", av[0]); 62278447Sdd _exit(-1); 62378447Sdd case -1: 62478447Sdd err(1, "fork"); 62578447Sdd } 62678447Sdd 62778447Sdd free(cmd); 62878447Sdd free(av); 62978447Sdd while (waitpid(pid, &status, 0) != pid) 63078447Sdd ; 63178447Sdd return (WEXITSTATUS(status)); 63278447Sdd} 63378447Sdd 63478447Sddstatic void 63578447Sddusage(void) 63678447Sdd{ 63778447Sdd 63878447Sdd fprintf(stderr, 63978447Sdd"usage: %s [-DLMNSX] [-a maxcontig] [-b block-size] [-c cylinders]\n" 64078447Sdd"\t[-d rotdelay] [-e maxbpg] [-F file] [-f frag-size] [-i bytes]\n" 64178447Sdd"\t[-m percent-free] [-n rotational-positions] [-O optimization]\n" 64278447Sdd"\t[-o mount-options] [-p permissions] [-s size] [-w user:group]\n" 64378447Sdd"\tmd-device mount-point\n", getprogname()); 64478447Sdd exit(1); 64578447Sdd} 646