mdmfs.c revision 163952
147398Sdfr/*
247398Sdfr * Copyright (c) 2001 Dima Dorfman.
347398Sdfr * All rights reserved.
447398Sdfr *
547398Sdfr * Redistribution and use in source and binary forms, with or without
647398Sdfr * modification, are permitted provided that the following conditions
747398Sdfr * are met:
847398Sdfr * 1. Redistributions of source code must retain the above copyright
947398Sdfr *    notice, this list of conditions and the following disclaimer.
1047398Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1147398Sdfr *    notice, this list of conditions and the following disclaimer in the
1247398Sdfr *    documentation and/or other materials provided with the distribution.
1347398Sdfr *
1447398Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1547398Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1647398Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1747398Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1847398Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1947398Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2047398Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2147398Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2247398Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2347398Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2447398Sdfr * SUCH DAMAGE.
2547398Sdfr */
2650477Speter
2747398Sdfr/*
2847398Sdfr * mdmfs (md/MFS) is a wrapper around mdconfig(8),
2947398Sdfr * newfs(8), and mount(8) that mimics the command line option set of
3047398Sdfr * the deprecated mount_mfs(8).
3147398Sdfr */
3247398Sdfr
3347398Sdfr#include <sys/cdefs.h>
3447398Sdfr__FBSDID("$FreeBSD: head/sbin/mdmfs/mdmfs.c 163952 2006-11-03 12:02:24Z ru $");
3547398Sdfr
3647398Sdfr#include <sys/param.h>
3747398Sdfr#include <sys/mdioctl.h>
3847398Sdfr#include <sys/stat.h>
3947398Sdfr#include <sys/wait.h>
4047398Sdfr
4147398Sdfr#include <assert.h>
4247398Sdfr#include <err.h>
4347398Sdfr#include <fcntl.h>
4447398Sdfr#include <grp.h>
4547398Sdfr#include <paths.h>
4647398Sdfr#include <pwd.h>
4747398Sdfr#include <stdarg.h>
4847398Sdfr#include <stdio.h>
4947398Sdfr#include <stdlib.h>
5047398Sdfr#include <string.h>
5147398Sdfr#include <unistd.h>
5247398Sdfr
5347398Sdfrtypedef enum { false, true } bool;
5447398Sdfr
5547398Sdfrstruct mtpt_info {
5647398Sdfr	uid_t		 mi_uid;
5747398Sdfr	bool		 mi_have_uid;
5847398Sdfr	gid_t		 mi_gid;
5947398Sdfr	bool		 mi_have_gid;
6047398Sdfr	mode_t		 mi_mode;
6147398Sdfr	bool		 mi_have_mode;
6247398Sdfr};
6347398Sdfr
6447398Sdfrstatic	bool debug;		/* Emit debugging information? */
6547398Sdfrstatic	bool loudsubs;		/* Suppress output from helper programs? */
6647398Sdfrstatic	bool norun;		/* Actually run the helper programs? */
6747398Sdfrstatic	int unit;      		/* The unit we're working with. */
6847398Sdfrstatic	const char *mdname;	/* Name of memory disk device (e.g., "md"). */
6947398Sdfrstatic	size_t mdnamelen;	/* Length of mdname. */
7047398Sdfrstatic	const char *path_mdconfig =_PATH_MDCONFIG;
7147398Sdfr
7247398Sdfrstatic void	 argappend(char **, const char *, ...) __printflike(2, 3);
7347398Sdfrstatic void	 debugprintf(const char *, ...) __printflike(1, 2);
7447398Sdfrstatic void	 do_mdconfig_attach(const char *, const enum md_types);
7547398Sdfrstatic void	 do_mdconfig_attach_au(const char *, const enum md_types);
7647398Sdfrstatic void	 do_mdconfig_detach(void);
7747398Sdfrstatic void	 do_mount(const char *, const char *);
7847398Sdfrstatic void	 do_mtptsetup(const char *, struct mtpt_info *);
7962987Sjhbstatic void	 do_newfs(const char *);
8062987Sjhbstatic void	 extract_ugid(const char *, struct mtpt_info *);
8169774Sphkstatic int	 run(int *, const char *, ...) __printflike(2, 3);
8247398Sdfrstatic void	 usage(void);
8347398Sdfr
8453094Sdfrint
8547398Sdfrmain(int argc, char **argv)
8647398Sdfr{
8747398Sdfr	struct mtpt_info mi;		/* Mountpoint info. */
8847398Sdfr	char *mdconfig_arg, *newfs_arg,	/* Args to helper programs. */
8947398Sdfr	    *mount_arg;
9047398Sdfr	enum md_types mdtype;		/* The type of our memory disk. */
9147398Sdfr	bool have_mdtype;
9247398Sdfr	bool detach, softdep, autounit, newfs;
9347398Sdfr	char *mtpoint, *unitstr;
9447398Sdfr	char *p;
9588376Stmm	int ch;
9653094Sdfr	void *set;
9747398Sdfr	unsigned long ul;
9847398Sdfr
9947398Sdfr	/* Misc. initialization. */
10047398Sdfr	(void)memset(&mi, '\0', sizeof(mi));
10147398Sdfr	detach = true;
10247398Sdfr	softdep = true;
10347398Sdfr	autounit = false;
10447398Sdfr	newfs = true;
10550769Sdfr	have_mdtype = false;
10647398Sdfr	mdtype = MD_SWAP;
10747398Sdfr	mdname = MD_NAME;
10847398Sdfr	mdnamelen = strlen(mdname);
10947398Sdfr	/*
11047398Sdfr	 * Can't set these to NULL.  They may be passed to the
11147398Sdfr	 * respective programs without modification.  I.e., we may not
11250769Sdfr	 * receive any command-line options which will caused them to
11350769Sdfr	 * be modified.
11450769Sdfr	 */
11550769Sdfr	mdconfig_arg = strdup("");
11650769Sdfr	newfs_arg = strdup("");
11750769Sdfr	mount_arg = strdup("");
11850769Sdfr
11950769Sdfr	/* If we were started as mount_mfs or mfs, imply -C. */
12050769Sdfr	if (strcmp(getprogname(), "mount_mfs") == 0 ||
12150769Sdfr	    strcmp(getprogname(), "mfs") == 0) {
12250769Sdfr		/* Make compatibility assumptions. */
12350769Sdfr		mi.mi_mode = 01777;
12450769Sdfr		mi.mi_have_mode = true;
12550769Sdfr	}
12650769Sdfr
12750769Sdfr	while ((ch = getopt(argc, argv,
12852174Sdfr	    "a:b:Cc:Dd:E:e:F:f:hi:LlMm:Nn:O:o:Pp:Ss:t:Uv:w:X")) != -1)
12950769Sdfr		switch (ch) {
13050769Sdfr		case 'a':
13150769Sdfr			argappend(&newfs_arg, "-a %s", optarg);
13250769Sdfr			break;
13350769Sdfr		case 'b':
13450769Sdfr			argappend(&newfs_arg, "-b %s", optarg);
13550769Sdfr			break;
13683051Syokota		case 'C':
13783051Syokota			/* Ignored for compatibility. */
13883051Syokota			break;
13983051Syokota		case 'c':
14083051Syokota			argappend(&newfs_arg, "-c %s", optarg);
14183051Syokota			break;
14283051Syokota		case 'D':
14383051Syokota			detach = false;
14483051Syokota			break;
14583051Syokota		case 'd':
14683051Syokota			argappend(&newfs_arg, "-d %s", optarg);
14783051Syokota			break;
14850769Sdfr		case 'E':
14950769Sdfr			path_mdconfig = optarg;
15050769Sdfr			break;
15150769Sdfr		case 'e':
15250769Sdfr			argappend(&newfs_arg, "-e %s", optarg);
15352174Sdfr			break;
15450769Sdfr		case 'F':
15550769Sdfr			if (have_mdtype)
15650769Sdfr				usage();
15757132Smsmith			mdtype = MD_VNODE;
15850769Sdfr			have_mdtype = true;
15950769Sdfr			argappend(&mdconfig_arg, "-f %s", optarg);
16050769Sdfr			break;
16150769Sdfr		case 'f':
16250769Sdfr			argappend(&newfs_arg, "-f %s", optarg);
16350769Sdfr			break;
16450769Sdfr		case 'h':
16550769Sdfr			usage();
16650769Sdfr			break;
16750769Sdfr		case 'i':
16850769Sdfr			argappend(&newfs_arg, "-i %s", optarg);
16950769Sdfr			break;
17050769Sdfr		case 'L':
17150769Sdfr			loudsubs = true;
17250769Sdfr			break;
17350769Sdfr		case 'l':
17450769Sdfr			argappend(&newfs_arg, "-l");
17550769Sdfr			break;
17650769Sdfr		case 'M':
17750769Sdfr			if (have_mdtype)
17850769Sdfr				usage();
17950769Sdfr			mdtype = MD_MALLOC;
18050769Sdfr			have_mdtype = true;
18150769Sdfr			break;
18250769Sdfr		case 'm':
18350769Sdfr			argappend(&newfs_arg, "-m %s", optarg);
18450769Sdfr			break;
18550769Sdfr		case 'N':
18650769Sdfr			norun = true;
18750769Sdfr			break;
18850769Sdfr		case 'n':
18950769Sdfr			argappend(&newfs_arg, "-n %s", optarg);
19050769Sdfr			break;
19150769Sdfr		case 'O':
19250769Sdfr			argappend(&newfs_arg, "-o %s", optarg);
19350769Sdfr			break;
19450769Sdfr		case 'o':
19550769Sdfr			argappend(&mount_arg, "-o %s", optarg);
19650769Sdfr			break;
19750769Sdfr		case 'P':
19850769Sdfr			newfs = false;
19950769Sdfr			break;
20050769Sdfr		case 'p':
20150769Sdfr			if ((set = setmode(optarg)) == NULL)
20250769Sdfr				usage();
20352174Sdfr			mi.mi_mode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO);
20450769Sdfr			mi.mi_have_mode = true;
20550769Sdfr			free(set);
20650769Sdfr			break;
20750769Sdfr		case 'S':
20850769Sdfr			softdep = false;
20950769Sdfr			break;
21050769Sdfr		case 's':
21183051Syokota			argappend(&mdconfig_arg, "-s %s", optarg);
21283051Syokota			break;
21383051Syokota		case 'U':
21483051Syokota			softdep = true;
21583051Syokota			break;
21683051Syokota		case 'v':
21783051Syokota			argappend(&newfs_arg, "-O %s", optarg);
21883051Syokota			break;
21983051Syokota		case 'w':
22083051Syokota			extract_ugid(optarg, &mi);
22183051Syokota			break;
22283051Syokota		case 'X':
22350769Sdfr			debug = true;
22450769Sdfr			break;
22550769Sdfr		default:
22650769Sdfr			usage();
22750769Sdfr		}
22852174Sdfr	argc -= optind;
22950769Sdfr	argv += optind;
23050769Sdfr	if (argc < 2)
23150769Sdfr		usage();
23257132Smsmith
23350769Sdfr	/* Derive 'unit' (global). */
23450769Sdfr	unitstr = argv[0];
23550769Sdfr	if (strncmp(unitstr, "/dev/", 5) == 0)
23650769Sdfr		unitstr += 5;
23750769Sdfr	if (strncmp(unitstr, mdname, mdnamelen) == 0)
23850769Sdfr		unitstr += mdnamelen;
23950769Sdfr	if (*unitstr == '\0') {
24050769Sdfr		autounit = true;
24150769Sdfr		unit = -1;
24250769Sdfr	} else {
24350769Sdfr		ul = strtoul(unitstr, &p, 10);
24450769Sdfr		if (ul == ULONG_MAX || *p != '\0')
24550769Sdfr			errx(1, "bad device unit: %s", unitstr);
24650769Sdfr		unit = ul;
24750769Sdfr	}
24850769Sdfr
24950769Sdfr	mtpoint = argv[1];
25050769Sdfr	if (!have_mdtype)
25150769Sdfr		mdtype = MD_SWAP;
25250769Sdfr	if (softdep)
25350769Sdfr		argappend(&newfs_arg, "-U");
25450769Sdfr	if (mdtype != MD_VNODE && !newfs)
25550769Sdfr		errx(1, "-P requires a vnode-backed disk");
25650769Sdfr
25750769Sdfr	/* Do the work. */
25850769Sdfr	if (detach && !autounit)
25950769Sdfr		do_mdconfig_detach();
26050769Sdfr	if (autounit)
26150769Sdfr		do_mdconfig_attach_au(mdconfig_arg, mdtype);
26250769Sdfr	else
26350769Sdfr		do_mdconfig_attach(mdconfig_arg, mdtype);
26450769Sdfr	if (newfs)
26550769Sdfr		do_newfs(newfs_arg);
26650769Sdfr	do_mount(mount_arg, mtpoint);
26750769Sdfr	do_mtptsetup(mtpoint, &mi);
26850769Sdfr
26950769Sdfr	return (0);
27050769Sdfr}
27150769Sdfr
27250769Sdfr/*
27350769Sdfr * Append the expansion of 'fmt' to the buffer pointed to by '*dstp';
27450769Sdfr * reallocate as required.
27550769Sdfr */
27650769Sdfrstatic void
27750769Sdfrargappend(char **dstp, const char *fmt, ...)
27850769Sdfr{
27950769Sdfr	char *old, *new;
28050769Sdfr	va_list ap;
28150769Sdfr
28250769Sdfr	old = *dstp;
28350769Sdfr	assert(old != NULL);
28450769Sdfr
28550769Sdfr	va_start(ap, fmt);
28650769Sdfr	if (vasprintf(&new, fmt,ap) == -1)
28750769Sdfr		errx(1, "vasprintf");
28850769Sdfr	va_end(ap);
28950769Sdfr
29050769Sdfr	*dstp = new;
29150769Sdfr	if (asprintf(&new, "%s %s", old, new) == -1)
29250769Sdfr		errx(1, "asprintf");
29350769Sdfr	free(*dstp);
29450769Sdfr	free(old);
29550769Sdfr
29650769Sdfr	*dstp = new;
29750769Sdfr}
29850769Sdfr
29950769Sdfr/*
30050769Sdfr * If run-time debugging is enabled, print the expansion of 'fmt'.
30152174Sdfr * Otherwise, do nothing.
30250769Sdfr */
30350769Sdfrstatic void
30450769Sdfrdebugprintf(const char *fmt, ...)
30550769Sdfr{
30650769Sdfr	va_list ap;
30750769Sdfr
30850769Sdfr	if (!debug)
30950769Sdfr		return;
31083051Syokota	fprintf(stderr, "DEBUG: ");
31183051Syokota	va_start(ap, fmt);
31283051Syokota	vfprintf(stderr, fmt, ap);
31383051Syokota	va_end(ap);
31483051Syokota	fprintf(stderr, "\n");
31583051Syokota	fflush(stderr);
31683051Syokota}
31750769Sdfr
31850769Sdfr/*
31950769Sdfr * Attach a memory disk with a known unit.
32052174Sdfr */
32150769Sdfrstatic void
32250769Sdfrdo_mdconfig_attach(const char *args, const enum md_types mdtype)
32350769Sdfr{
32457132Smsmith	int rv;
32550769Sdfr	const char *ta;		/* Type arg. */
32650769Sdfr
32750769Sdfr	switch (mdtype) {
32850769Sdfr	case MD_SWAP:
32950769Sdfr		ta = "-t swap";
33050769Sdfr		break;
33150769Sdfr	case MD_VNODE:
33250769Sdfr		ta = "-t vnode";
33350769Sdfr		break;
33450769Sdfr	case MD_MALLOC:
33550769Sdfr		ta = "-t malloc";
33650769Sdfr		break;
33750769Sdfr	default:
33850769Sdfr		abort();
33950769Sdfr	}
34050769Sdfr	rv = run(NULL, "%s -a %s%s -u %s%d", path_mdconfig, ta, args,
34150769Sdfr	    mdname, unit);
34250769Sdfr	if (rv)
34350769Sdfr		errx(1, "mdconfig (attach) exited with error code %d", rv);
34450769Sdfr}
34550769Sdfr
34650769Sdfr/*
34750769Sdfr * Attach a memory disk with an unknown unit; use autounit.
34850769Sdfr */
34950769Sdfrstatic void
35050769Sdfrdo_mdconfig_attach_au(const char *args, const enum md_types mdtype)
35150769Sdfr{
35250769Sdfr	const char *ta;		/* Type arg. */
35350769Sdfr	char *linep, *linebuf; 	/* Line pointer, line buffer. */
35450769Sdfr	int fd;			/* Standard output of mdconfig invocation. */
35550769Sdfr	FILE *sfd;
35650769Sdfr	int rv;
35750769Sdfr	char *p;
35850769Sdfr	size_t linelen;
35950769Sdfr	unsigned long ul;
36050769Sdfr
36150769Sdfr	switch (mdtype) {
36250769Sdfr	case MD_SWAP:
36350769Sdfr		ta = "-t swap";
36450769Sdfr		break;
36550769Sdfr	case MD_VNODE:
36650769Sdfr		ta = "-t vnode";
36752174Sdfr		break;
36850769Sdfr	case MD_MALLOC:
36950769Sdfr		ta = "-t malloc";
37050769Sdfr		break;
37150769Sdfr	default:
37250769Sdfr		abort();
37350769Sdfr	}
37450769Sdfr	rv = run(&fd, "%s -a %s%s", path_mdconfig, ta, args);
37550769Sdfr	if (rv)
37683051Syokota		errx(1, "mdconfig (attach) exited with error code %d", rv);
37783051Syokota
37883051Syokota	/* Receive the unit number. */
37983051Syokota	if (norun) {	/* Since we didn't run, we can't read.  Fake it. */
38083051Syokota		unit = 0;
38183051Syokota		return;
38283051Syokota	}
38350769Sdfr	sfd = fdopen(fd, "r");
38450769Sdfr	if (sfd == NULL)
38550769Sdfr		err(1, "fdopen");
38652174Sdfr	linep = fgetln(sfd, &linelen);
38750769Sdfr	if (linep == NULL && linelen < mdnamelen + 1)
38850769Sdfr		errx(1, "unexpected output from mdconfig (attach)");
38950769Sdfr	/* If the output format changes, we want to know about it. */
39057132Smsmith	assert(strncmp(linep, mdname, mdnamelen) == 0);
39150769Sdfr	linebuf = malloc(linelen - mdnamelen + 1);
39250769Sdfr	assert(linebuf != NULL);
39350769Sdfr	/* Can't use strlcpy because linep is not NULL-terminated. */
39450769Sdfr	strncpy(linebuf, linep + mdnamelen, linelen);
39550769Sdfr	linebuf[linelen] = '\0';
39650769Sdfr	ul = strtoul(linebuf, &p, 10);
39750769Sdfr	if (ul == ULONG_MAX || *p != '\n')
39850769Sdfr		errx(1, "unexpected output from mdconfig (attach)");
39950769Sdfr	unit = ul;
40050769Sdfr
40150769Sdfr	fclose(sfd);
40250769Sdfr	close(fd);
40350769Sdfr}
40450769Sdfr
40550769Sdfr/*
40650769Sdfr * Detach a memory disk.
40750769Sdfr */
40850769Sdfrstatic void
40950769Sdfrdo_mdconfig_detach(void)
41050769Sdfr{
41150769Sdfr	int rv;
41250769Sdfr
41350769Sdfr	rv = run(NULL, "%s -d -u %s%d", path_mdconfig, mdname, unit);
41450769Sdfr	if (rv && debug)	/* This is allowed to fail. */
41550769Sdfr		warnx("mdconfig (detach) exited with error code %d (ignored)",
41650769Sdfr		      rv);
41750769Sdfr}
41850769Sdfr
41950769Sdfr/*
42050769Sdfr * Mount the configured memory disk.
42150769Sdfr */
42250769Sdfrstatic void
42350769Sdfrdo_mount(const char *args, const char *mtpoint)
42450769Sdfr{
42581401Sjulian	int rv;
42691206Salfred
42791206Salfred	rv = run(NULL, "%s%s /dev/%s%d %s", _PATH_MOUNT, args,
42891206Salfred	    mdname, unit, mtpoint);
42981401Sjulian	if (rv)
43081401Sjulian		errx(1, "mount exited with error code %d", rv);
43181401Sjulian}
43250769Sdfr
43391202Salfred/*
43481401Sjulian * Various configuration of the mountpoint.  Mostly, enact 'mip'.
43550769Sdfr */
43691202Salfredstatic void
43781401Sjuliando_mtptsetup(const char *mtpoint, struct mtpt_info *mip)
43850769Sdfr{
43991202Salfred
44081401Sjulian	if (mip->mi_have_mode) {
44150769Sdfr		debugprintf("changing mode of %s to %o.", mtpoint,
44291202Salfred		    mip->mi_mode);
44381401Sjulian		if (!norun)
44450769Sdfr			if (chmod(mtpoint, mip->mi_mode) == -1)
44550769Sdfr				err(1, "chmod: %s", mtpoint);
44650769Sdfr	}
44750769Sdfr	/*
44850769Sdfr	 * We have to do these separately because the user may have
44950769Sdfr	 * only specified one of them.
45091202Salfred	 */
45150769Sdfr	if (mip->mi_have_uid) {
45250769Sdfr		debugprintf("changing owner (user) or %s to %u.", mtpoint,
45381401Sjulian		    mip->mi_uid);
45481401Sjulian		if (!norun)
45550769Sdfr			if (chown(mtpoint, mip->mi_uid, -1) == -1)
45650769Sdfr				err(1, "chown %s to %u (user)", mtpoint,
45750769Sdfr				    mip->mi_uid);
45850769Sdfr	}
45950769Sdfr	if (mip->mi_have_gid) {
46050769Sdfr		debugprintf("changing owner (group) or %s to %u.", mtpoint,
46150769Sdfr		    mip->mi_gid);
46262987Sjhb		if (!norun)
46391202Salfred			if (chown(mtpoint, -1, mip->mi_gid) == -1)
46462987Sjhb				err(1, "chown %s to %u (group)", mtpoint,
46562987Sjhb				    mip->mi_gid);
46681401Sjulian	}
46750769Sdfr}
46881401Sjulian
46950769Sdfr/*
47050769Sdfr * Put a file system on the memory disk.
47181401Sjulian */
47250769Sdfrstatic void
47350769Sdfrdo_newfs(const char *args)
47450769Sdfr{
47550769Sdfr	int rv;
47683051Syokota
47783051Syokota	rv = run(NULL, "%s%s /dev/%s%d", _PATH_NEWFS, args, mdname, unit);
47883051Syokota	if (rv)
47983051Syokota		errx(1, "newfs exited with error code %d", rv);
48083051Syokota}
48183051Syokota
48283051Syokota/*
48383051Syokota * 'str' should be a user and group name similar to the last argument
48483051Syokota * to chown(1); i.e., a user, followed by a colon, followed by a
48583051Syokota * group.  The user and group in 'str' may be either a [ug]id or a
48683051Syokota * name.  Upon return, the uid and gid fields in 'mip' will contain
48783051Syokota * the uid and gid of the user and group name in 'str', respectively.
48883051Syokota *
48983051Syokota * In other words, this derives a user and group id from a string
49083051Syokota * formatted like the last argument to chown(1).
49183051Syokota *
49283051Syokota * Notice: At this point we don't support only a username or only a
49383051Syokota * group name. do_mtptsetup already does, so when this feature is
49483051Syokota * desired, this is the only routine that needs to be changed.
49583051Syokota */
49683051Syokotastatic void
49783051Syokotaextract_ugid(const char *str, struct mtpt_info *mip)
49883051Syokota{
49983051Syokota	char *ug;			/* Writable 'str'. */
50083051Syokota	char *user, *group;		/* Result of extracton. */
50183051Syokota	struct passwd *pw;
50283051Syokota	struct group *gr;
50383051Syokota	char *p;
50483051Syokota	uid_t *uid;
50583051Syokota	gid_t *gid;
50683051Syokota
50783051Syokota	uid = &mip->mi_uid;
50883051Syokota	gid = &mip->mi_gid;
50983051Syokota	mip->mi_have_uid = mip->mi_have_gid = false;
51083051Syokota
51183051Syokota	/* Extract the user and group from 'str'.  Format above. */
51283051Syokota	ug = strdup(str);
51383051Syokota	assert(ug != NULL);
51483051Syokota	group = ug;
51583051Syokota	user = strsep(&group, ":");
51683051Syokota	if (user == NULL || group == NULL || *user == '\0' || *group == '\0')
51783051Syokota		usage();
51883051Syokota
51983051Syokota	/* Derive uid. */
52083051Syokota	*uid = strtoul(user, &p, 10);
52183051Syokota	if (*uid == (uid_t)ULONG_MAX)
52283051Syokota		usage();
52383051Syokota	if (*p != '\0') {
52483051Syokota		pw = getpwnam(user);
52550769Sdfr		if (pw == NULL)
52650769Sdfr			errx(1, "invalid user: %s", user);
52750769Sdfr		*uid = pw->pw_uid;
52850769Sdfr	}
52950769Sdfr	mip->mi_have_uid = true;
53050769Sdfr
53181401Sjulian	/* Derive gid. */
53250769Sdfr	*gid = strtoul(group, &p, 10);
53350769Sdfr	if (*gid == (gid_t)ULONG_MAX)
53453094Sdfr		usage();
53553094Sdfr	if (*p != '\0') {
53653094Sdfr		gr = getgrnam(group);
53753094Sdfr		if (gr == NULL)
53853094Sdfr			errx(1, "invalid group: %s", group);
53950769Sdfr		*gid = gr->gr_gid;
54050769Sdfr	}
54150769Sdfr	mip->mi_have_gid = true;
54250769Sdfr
54351905Sdfr	free(ug);
54451905Sdfr}
54551905Sdfr
54653094Sdfr/*
54753094Sdfr * Run a process with command name and arguments pointed to by the
54881401Sjulian * formatted string 'cmdline'.  Since system(3) is not used, the first
54981401Sjulian * space-delimited token of 'cmdline' must be the full pathname of the
55081401Sjulian * program to run.  The return value is the return code of the process
55181401Sjulian * spawned.  If 'ofd' is non-NULL, it is set to the standard output of
55281401Sjulian * the program spawned (i.e., you can read from ofd and get the output
55381401Sjulian * of the program).
55481401Sjulian */
55551905Sdfrstatic int
55651905Sdfrrun(int *ofd, const char *cmdline, ...)
55751905Sdfr{
55851905Sdfr	char **argv, **argvp;		/* Result of splitting 'cmd'. */
55981401Sjulian	int argc;
56051905Sdfr	char *cmd;			/* Expansion of 'cmdline'. */
56181401Sjulian	int pid, status;		/* Child info. */
56251905Sdfr	int pfd[2];			/* Pipe to the child. */
56351905Sdfr	int nfd;			/* Null (/dev/null) file descriptor. */
56481401Sjulian	bool dup2dn;			/* Dup /dev/null to stdout? */
56581401Sjulian	va_list ap;
56651905Sdfr	char *p;
56751905Sdfr	int rv, i;
56850769Sdfr
56950769Sdfr	dup2dn = true;
57053094Sdfr	va_start(ap, cmdline);
57153094Sdfr	rv = vasprintf(&cmd, cmdline, ap);
57250769Sdfr	if (rv == -1)
57350769Sdfr		err(1, "vasprintf");
57450769Sdfr	va_end(ap);
57550769Sdfr
57650769Sdfr	/* Split up 'cmd' into 'argv' for use with execve. */
57750769Sdfr	for (argc = 1, p = cmd; (p = strchr(p, ' ')) != NULL; p++)
57850769Sdfr		argc++;		/* 'argc' generation loop. */
57950769Sdfr	argv = (char **)malloc(sizeof(*argv) * (argc + 1));
58050769Sdfr	assert(argv != NULL);
58150769Sdfr	for (p = cmd, argvp = argv; (*argvp = strsep(&p, " ")) != NULL;)
58250769Sdfr		if (**argv != '\0')
58351905Sdfr			if (++argvp >= &argv[argc]) {
58450769Sdfr				*argvp = NULL;
58553094Sdfr				break;
58653094Sdfr			}
58750769Sdfr	assert(*argv);
58850769Sdfr
58950769Sdfr	/* Make sure the above loop works as expected. */
59050769Sdfr	if (debug) {
59150769Sdfr		/*
59250769Sdfr		 * We can't, but should, use debugprintf here.  First,
59350769Sdfr		 * it appends a trailing newline to the output, and
59450769Sdfr		 * second it prepends "DEBUG: " to the output.  The
59552174Sdfr		 * former is a problem for this would-be first call,
59650769Sdfr		 * and the latter for the would-be call inside the
59750769Sdfr		 * loop.
59850769Sdfr		 */
59950769Sdfr		(void)fprintf(stderr, "DEBUG: running:");
60050769Sdfr		/* Should be equivilent to 'cmd' (before strsep, of course). */
60150769Sdfr		for (i = 0; argv[i] != NULL; i++)
60250769Sdfr			(void)fprintf(stderr, " %s", argv[i]);
60350769Sdfr		(void)fprintf(stderr, "\n");
60452174Sdfr	}
60550769Sdfr
60650769Sdfr	/* Create a pipe if necessary and fork the helper program. */
60752174Sdfr	if (ofd != NULL) {
60850769Sdfr		if (pipe(&pfd[0]) == -1)
60950769Sdfr			err(1, "pipe");
61062059Sdfr		*ofd = pfd[0];
61150769Sdfr		dup2dn = false;
61250769Sdfr	}
61350769Sdfr	pid = fork();
61450769Sdfr	switch (pid) {
61550769Sdfr	case 0:
61650769Sdfr		/* XXX can we call err() in here? */
61753094Sdfr		if (norun)
61853094Sdfr			_exit(0);
61950769Sdfr		if (ofd != NULL)
62050769Sdfr			if (dup2(pfd[1], STDOUT_FILENO) < 0)
62150769Sdfr				err(1, "dup2");
62247398Sdfr		if (!loudsubs) {
62347398Sdfr			nfd = open(_PATH_DEVNULL, O_RDWR);
62447398Sdfr			if (nfd == -1)
62547578Sdfr				err(1, "open: %s", _PATH_DEVNULL);
62647398Sdfr			if (dup2(nfd, STDIN_FILENO) < 0)
62754073Smdodd				err(1, "dup2");
62847398Sdfr			if (dup2dn)
62947398Sdfr				if (dup2(nfd, STDOUT_FILENO) < 0)
630104179Sphk				   err(1, "dup2");
631104179Sphk			if (dup2(nfd, STDERR_FILENO) < 0)
632104179Sphk				err(1, "dup2");
633104179Sphk		}
63469781Sdwmalone
63547398Sdfr		(void)execv(argv[0], argv);
63647398Sdfr		warn("exec: %s", argv[0]);
63747398Sdfr		_exit(-1);
63847398Sdfr	case -1:
63950769Sdfr		err(1, "fork");
64047398Sdfr	}
641104179Sphk
64254073Smdodd	free(cmd);
643104179Sphk	free(argv);
64447398Sdfr	while (waitpid(pid, &status, 0) != pid)
64547398Sdfr		;
64662059Sdfr	return (WEXITSTATUS(status));
64762059Sdfr}
64847398Sdfr
64947398Sdfrstatic void
65047398Sdfrusage(void)
65149195Smdodd{
65247398Sdfr
65351052Sdfr	fprintf(stderr,
65449195Smdodd"usage: %s [-DLlMNPSUX] [-a maxcontig] [-b block-size] [-c cylinders]\n"
65547398Sdfr"\t[-d rotdelay] [-E path-mdconfig] [-e maxbpg] [-F file] [-f frag-size]\n"
65688376Stmm"\t[-i bytes] [-m percent-free] [-n rotational-positions] [-O optimization]\n"
65788376Stmm"\t[-o mount-options] [-p permissions] [-s size] [-v version]\n"
65888376Stmm"\t[-w user:group] md-device mount-point\n", getprogname());
65988376Stmm	exit(1);
66051052Sdfr}
66151052Sdfr