mdmfs.c revision 78569
155714Skris/*
255714Skris * Copyright (c) 2001 Dima Dorfman <dd@FreeBSD.org>
355714Skris * All rights reserved.
455714Skris *
555714Skris * Redistribution and use in source and binary forms, with or without
655714Skris * modification, are permitted provided that the following conditions
755714Skris * are met:
8280304Sjkim * 1. Redistributions of source code must retain the above copyright
955714Skris *    notice, this list of conditions and the following disclaimer.
1055714Skris * 2. Redistributions in binary form must reproduce the above copyright
1155714Skris *    notice, this list of conditions and the following disclaimer in the
1255714Skris *    documentation and/or other materials provided with the distribution.
1355714Skris *
1455714Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15280304Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1655714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1755714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1855714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1955714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2055714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2155714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22280304Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2355714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2455714Skris * SUCH DAMAGE.
2555714Skris */
2655714Skris
2755714Skris/*
2855714Skris * mdmfs (md/MFS) is a wrapper around mdconfig(8), disklabel(8),
2955714Skris * newfs(8), and mount(8) that mimics the command line option set of
3055714Skris * the deprecated mount_mfs(8).
3155714Skris */
3255714Skris
3355714Skris#ifndef lint
3455714Skrisstatic const char rcsid[] =
3555714Skris  "$FreeBSD: head/sbin/mdmfs/mdmfs.c 78569 2001-06-21 22:34:50Z mjacob $";
3655714Skris#endif /* not lint */
37280304Sjkim
3855714Skris#include <sys/param.h>
3955714Skris#include <sys/mdioctl.h>
40280304Sjkim#include <sys/stat.h>
4155714Skris#include <sys/wait.h>
4255714Skris
4355714Skris#include <assert.h>
4455714Skris#include <err.h>
4555714Skris#include <fcntl.h>
4655714Skris#include <grp.h>
4755714Skris#include <paths.h>
4855714Skris#include <pwd.h>
4955714Skris#include <stdarg.h>
5055714Skris#include <stdio.h>
5155714Skris#include <stdlib.h>
52280304Sjkim#include <string.h>
5355714Skris#include <unistd.h>
5455714Skris
5555714Skris#include "pathnames.h"
5655714Skris
5755714Skristypedef enum { false, true } bool;
5889840Skris
59238405Sjkimstruct mtpt_info {
6089840Skris	uid_t		 mi_uid;
6189840Skris	bool		 mi_have_uid;
6289840Skris	gid_t		 mi_gid;
6389840Skris	bool		 mi_have_gid;
6489840Skris	mode_t		 mi_mode;
6589840Skris	bool		 mi_have_mode;
66280304Sjkim};
6789840Skris
6889840Skrisstatic	bool debug;		/* Emit debugging information? */
6989840Skrisstatic	bool loudsubs;		/* Suppress output from helper programs? */
7089840Skrisstatic	bool norun;		/* Actually run the helper programs? */
7189840Skrisstatic	int unit;      		/* The unit we're working with. */
7289840Skrisstatic	const char *mdname;	/* Name of memory disk device (e.g., "md"). */
7389840Skrisstatic	size_t mdnamelen;	/* Length of mdname. */
7489840Skris
7589840Skrisstatic void	 argappend(char **, const char *, ...);
7689840Skrisstatic void	 debugprintf(const char *, ...);
7789840Skrisstatic void	 do_disklabel(void);
7889840Skrisstatic void	 do_mdconfig_attach(const char *, const enum md_types);
7989840Skrisstatic void	 do_mdconfig_attach_au(const char *, const enum md_types);
8089840Skrisstatic void	 do_mdconfig_detach(void);
8189840Skrisstatic void	 do_mount(const char *, const char *);
8289840Skrisstatic void	 do_mtptsetup(const char *, struct mtpt_info *);
8389840Skrisstatic void	 do_newfs(const char *);
8489840Skrisstatic void	 extract_ugid(const char *, struct mtpt_info *);
8589840Skrisstatic int	 run(int *, const char *, ...);
8689840Skrisstatic void	 usage(void);
8789840Skris
8889840Skrisint
8989840Skrismain(int ac, char **av)
9089840Skris{
9189840Skris	struct mtpt_info mi;		/* Mountpoint info. */
9289840Skris	char *mdconfig_arg, *newfs_arg,	/* Args to helper programs. */
9389840Skris	    *mount_arg;
9489840Skris	enum md_types mdtype;		/* The type of our memory disk. */
9589840Skris	bool have_mdtype;
9689840Skris	bool detach, softdep, autounit;
9789840Skris	char *mtpoint, *unitstr;
9889840Skris	char ch, *p;
9989840Skris
10089840Skris	/* Misc. initialization. */
10189840Skris	(void)memset(&mi, '\0', sizeof(mi));
10289840Skris	detach = true;
10389840Skris	softdep = true;
10489840Skris	autounit = false;
10589840Skris	have_mdtype = false;
10689840Skris	mdname = MD_NAME;
10789840Skris	mdnamelen = strlen(mdname);
10889840Skris	/*
10989840Skris	 * Can't set these to NULL.  They may be passed to the
11089840Skris	 * respective programs without modification.  I.e., we may not
11155714Skris	 * receive any command-line options which will caused them to
11255714Skris	 * be modified.
113110007Smarkm	 */
11455714Skris	mdconfig_arg = strdup("");
11555714Skris	newfs_arg = strdup("");
11655714Skris	mount_arg = strdup("");
11755714Skris
118238405Sjkim	while ((ch = getopt(ac, av,
119280304Sjkim	    "a:b:c:Dd:e:F:f:hi:LMm:Nn:O:o:p:Ss:t:w:X")) != -1)
120238405Sjkim		switch (ch) {
12155714Skris		case 'a':
122238405Sjkim			argappend(&newfs_arg, "-a %s", optarg);
12355714Skris			break;
124238405Sjkim		case 'b':
125280304Sjkim			argappend(&newfs_arg, "-b %s", optarg);
126110007Smarkm			break;
127280304Sjkim		case 'c':
128280304Sjkim			argappend(&newfs_arg, "-c %s", optarg);
12955949Skris			break;
130273149Sjkim		case 'D':
131280304Sjkim			detach = false;
132280304Sjkim			break;
133273149Sjkim		case 'd':
134280304Sjkim			argappend(&newfs_arg, "-d %s", optarg);
135280304Sjkim			break;
136280304Sjkim		case 'e':
137280304Sjkim			argappend(&newfs_arg, "-e %s", optarg);
138280304Sjkim			break;
139280304Sjkim		case 'F':
140280304Sjkim			if (have_mdtype)
141280304Sjkim				usage();
142280304Sjkim			mdtype = MD_VNODE;
14355714Skris			have_mdtype = true;
144160817Ssimon			argappend(&mdconfig_arg, "-f %s", optarg);
145280304Sjkim			break;
146280304Sjkim		case 'f':
14755714Skris			argappend(&newfs_arg, "-f %s", optarg);
14855714Skris			break;
149280304Sjkim		case 'h':
150280304Sjkim			usage();
151280304Sjkim			break;
152280304Sjkim		case 'i':
153280304Sjkim			argappend(&newfs_arg, "-i %s", optarg);
154280304Sjkim			break;
15555714Skris		case 'L':
156280304Sjkim			loudsubs = true;
157280304Sjkim			break;
158280304Sjkim		case 'M':
15955714Skris			if (have_mdtype)
160280304Sjkim				usage();
161280304Sjkim			mdtype = MD_MALLOC;
162280304Sjkim			have_mdtype = true;
163280304Sjkim			break;
16455714Skris		case 'm':
165280304Sjkim			argappend(&newfs_arg, "-m %s", optarg);
166280304Sjkim			break;
167280304Sjkim		case 'N':
16855714Skris			norun = true;
169280304Sjkim			break;
170280304Sjkim		case 'n':
17155714Skris			argappend(&newfs_arg, "-n %s", optarg);
172280304Sjkim			break;
173280304Sjkim		case 'O':
174280304Sjkim			argappend(&newfs_arg, "-o %s", optarg);
175280304Sjkim			break;
176280304Sjkim		case 'o':
17755714Skris			argappend(&mount_arg, "-o %s", optarg);
178280304Sjkim			break;
179280304Sjkim		case 'p':
180280304Sjkim			if (*optarg >= '0' && *optarg <= '7')
18155714Skris				mi.mi_mode = strtol(optarg, NULL, 8);
182280304Sjkim			if ((mi.mi_mode & ~07777) != 0)
183280304Sjkim				usage();
18455714Skris			mi.mi_have_mode = true;
185280304Sjkim			break;
186280304Sjkim		case 'S':
187280304Sjkim			softdep = false;
188280304Sjkim			break;
189280304Sjkim		case 's':
190280304Sjkim			argappend(&mdconfig_arg, "-s %s", optarg);
191280304Sjkim			break;
192280304Sjkim		case 'w':
193280304Sjkim			extract_ugid(optarg, &mi);
194280304Sjkim			break;
195280304Sjkim		case 'X':
196280304Sjkim			debug = true;
19755714Skris			break;
198280304Sjkim		default:
19955714Skris			usage();
200280304Sjkim		}
201280304Sjkim	ac -= optind;
202280304Sjkim	av += optind;
203280304Sjkim	if (ac < 2)
20455714Skris		usage();
205280304Sjkim
206280304Sjkim	/* Derive 'unit' (global). */
20755714Skris	unitstr = av[0];
208280304Sjkim	if (strncmp(unitstr, "/dev/", 5) == 0)
209280304Sjkim		unitstr += 5;
210280304Sjkim	if (strncmp(unitstr, mdname, mdnamelen) == 0)
211280304Sjkim		unitstr += mdnamelen;
212280304Sjkim	if (*unitstr == '\0') {
213280304Sjkim		autounit = true;
21455714Skris		unit = -1;
215280304Sjkim	} else {
216280304Sjkim		unit = strtoul(unitstr, &p, 10);
217280304Sjkim		if ((unsigned)unit == ULONG_MAX || *p != '\0')
218280304Sjkim			errx(1, "bad device unit: %s", unitstr);
219280304Sjkim	}
220280304Sjkim
22155714Skris	mtpoint = av[1];
222280304Sjkim	if (!have_mdtype)
223280304Sjkim		mdtype = MD_SWAP;
224280304Sjkim	if (softdep)
225280304Sjkim		argappend(&newfs_arg, "-U");
226280304Sjkim
227280304Sjkim	/* Do the work. */
228280304Sjkim	if (detach && !autounit)
229280304Sjkim		do_mdconfig_detach();
230280304Sjkim	if (autounit)
231280304Sjkim		do_mdconfig_attach_au(mdconfig_arg, mdtype);
232280304Sjkim	else
233280304Sjkim		do_mdconfig_attach(mdconfig_arg, mdtype);
234280304Sjkim	do_disklabel();
23555714Skris	do_newfs(newfs_arg);
23655714Skris	do_mount(mount_arg, mtpoint);
237280304Sjkim	do_mtptsetup(mtpoint, &mi);
238280304Sjkim
239280304Sjkim	return (0);
240280304Sjkim}
241280304Sjkim
242280304Sjkim/*
243280304Sjkim * Append the expansion of 'fmt' to the buffer pointed to by '*dstp';
244280304Sjkim * reallocate as required.
245280304Sjkim */
246280304Sjkimstatic void
247280304Sjkimargappend(char **dstp, const char *fmt, ...)
248280304Sjkim{
249280304Sjkim	char *old, *new;
250280304Sjkim	va_list ap;
251280304Sjkim
252280304Sjkim	old = *dstp;
253280304Sjkim	assert(old != NULL);
254280304Sjkim
255280304Sjkim	va_start(ap, fmt);
256280304Sjkim	if (vasprintf(&new, fmt,ap) == -1)
257280304Sjkim		errx(1, "vasprintf");
258280304Sjkim	va_end(ap);
259280304Sjkim
26055714Skris	*dstp = new;
261280304Sjkim	if (asprintf(&new, "%s %s", old, new) == -1)
262280304Sjkim		errx(1, "asprintf");
263280304Sjkim	free(*dstp);
26459194Skris	free(old);
265280304Sjkim
266280304Sjkim	*dstp = new;
26755714Skris}
268280304Sjkim
269280304Sjkim/*
270280304Sjkim * If run-time debugging is enabled, print the expansion of 'fmt'.
27155714Skris * Otherwise, do nothing.
272280304Sjkim */
27355714Skrisstatic void
274280304Sjkimdebugprintf(const char *fmt, ...)
27555714Skris{
276280304Sjkim	va_list ap;
277280304Sjkim
278280304Sjkim	if (!debug)
279280304Sjkim		return;
280280304Sjkim	fprintf(stderr, "DEBUG: ");
281280304Sjkim	va_start(ap, fmt);
282280304Sjkim	vfprintf(stderr, fmt, ap);
283280304Sjkim	va_end(ap);
284280304Sjkim	fprintf(stderr, "\n");
285280304Sjkim	fflush(stderr);
286280304Sjkim}
287280304Sjkim
288280304Sjkim/*
289280304Sjkim * Label the memory disk.
290280304Sjkim */
291280304Sjkimstatic void
292280304Sjkimdo_disklabel(void)
293280304Sjkim{
294280304Sjkim	int rv;
295280304Sjkim
296280304Sjkim	rv = run(NULL, "%s -r -w %s%d auto", PATH_DISKLABEL, mdname, unit);
297280304Sjkim	if (rv)
298280304Sjkim		errx(1, "disklabel exited with error code %d", rv);
299280304Sjkim}
300280304Sjkim
301280304Sjkim/*
302280304Sjkim * Attach a memory disk with a known unit.
303280304Sjkim */
304280304Sjkimstatic void
305280304Sjkimdo_mdconfig_attach(const char *args, const enum md_types mdtype)
306280304Sjkim{
307280304Sjkim	int rv;
308280304Sjkim	const char *ta;		/* Type arg. */
309280304Sjkim
310280304Sjkim	switch (mdtype) {
311280304Sjkim	case MD_SWAP:
312280304Sjkim		ta = "-t swap";
313280304Sjkim		break;
314280304Sjkim	case MD_VNODE:
315280304Sjkim		ta = "-t vnode";
316280304Sjkim		break;
317280304Sjkim	case MD_MALLOC:
318280304Sjkim		ta = "-t malloc";
319280304Sjkim		break;
320280304Sjkim	default:
321280304Sjkim		abort();
322280304Sjkim	}
323280304Sjkim	rv = run(NULL, "%s -a %s%s -u %s%d", PATH_MDCONFIG, ta, args,
324280304Sjkim	    mdname, unit);
32555714Skris	if (rv)
326280304Sjkim		errx(1, "mdconfig (attach) exited with error code %d", rv);
327280304Sjkim}
328280304Sjkim
329280304Sjkim/*
330280304Sjkim * Attach a memory disk with an unknown unit; use autounit.
331280304Sjkim */
332280304Sjkimstatic void
333280304Sjkimdo_mdconfig_attach_au(const char *args, const enum md_types mdtype)
334280304Sjkim{
335280304Sjkim	const char *ta;		/* Type arg. */
33655714Skris	char *linep, *linebuf; 	/* Line pointer, line buffer. */
337280304Sjkim	int fd;			/* Standard output of mdconfig invocation. */
338280304Sjkim	FILE *sfd;
339280304Sjkim	int rv;
340280304Sjkim	char *p;
341280304Sjkim	size_t linelen;
342280304Sjkim
343280304Sjkim	switch (mdtype) {
344280304Sjkim	case MD_SWAP:
345280304Sjkim		ta = "-t swap";
346280304Sjkim		break;
347280304Sjkim	case MD_VNODE:
348280304Sjkim		ta = "-t vnode";
349280304Sjkim		break;
350280304Sjkim	case MD_MALLOC:
351280304Sjkim		ta = "-t malloc";
352280304Sjkim		break;
353280304Sjkim	default:
354280304Sjkim		abort();
355280304Sjkim	}
356280304Sjkim	rv = run(&fd, "%s -a %s%s", PATH_MDCONFIG, ta, args);
357280304Sjkim	if (rv)
358280304Sjkim		errx(1, "mdconfig (attach) exited with error code %d", rv);
359280304Sjkim
360280304Sjkim	/* Receive the unit number. */
361280304Sjkim	if (norun) {	/* Since we didn't run, we can't read.  Fake it. */
362280304Sjkim		unit = -1;
363280304Sjkim		return;
364280304Sjkim	}
365280304Sjkim	sfd = fdopen(fd, "r");
366280304Sjkim	if (sfd == NULL)
367280304Sjkim		err(1, "fdopen");
368280304Sjkim	linep = fgetln(sfd, &linelen);
369280304Sjkim	if (linep == NULL && linelen < mdnamelen + 1)
370280304Sjkim		errx(1, "unexpected output from mdconfig (attach)");
371280304Sjkim	/* If the output format changes, we want to know about it. */
372280304Sjkim	assert(strncmp(linep, mdname, mdnamelen) == 0);
373280304Sjkim	linebuf = malloc(linelen - mdnamelen + 1);
374280304Sjkim	assert(linebuf != NULL);
375280304Sjkim	/* Can't use strlcpy because linep is not NULL-terminated. */
376280304Sjkim	strncpy(linebuf, linep + mdnamelen, linelen);
377280304Sjkim	linebuf[linelen] = '\0';
378280304Sjkim	unit = strtoul(linebuf, &p, 10);
379280304Sjkim	if ((unsigned)unit == ULONG_MAX || *p != '\n')
380280304Sjkim		errx(1, "unexpected output from mdconfig (attach)");
381280304Sjkim
382280304Sjkim	fclose(sfd);
383280304Sjkim	close(fd);
384280304Sjkim}
385280304Sjkim
386280304Sjkim/*
387280304Sjkim * Detach a memory disk.
388280304Sjkim */
389280304Sjkimstatic void
390280304Sjkimdo_mdconfig_detach(void)
391280304Sjkim{
392280304Sjkim	int rv;
393280304Sjkim
394280304Sjkim	rv = run(NULL, "%s -d -u %s%d", PATH_MDCONFIG, mdname, unit);
395280304Sjkim	if (rv && debug)	/* This is allowed to fail. */
396280304Sjkim		warnx("mdconfig (detach) exited with error code %d (ignored)",
397280304Sjkim		      rv);
398280304Sjkim}
399280304Sjkim
400280304Sjkim/*
401273149Sjkim * Mount the configured memory disk.
402280304Sjkim */
403280304Sjkimstatic void
404280304Sjkimdo_mount(const char *args, const char *mtpoint)
405194206Ssimon{
406280304Sjkim	int rv;
407280304Sjkim
408280304Sjkim	rv = run(NULL, "%s%s /dev/%s%dc %s", PATH_MOUNT, args,
409280304Sjkim	    mdname, unit, mtpoint);
410280304Sjkim	if (rv)
411194206Ssimon		errx(1, "mount exited with error code %d", rv);
412194206Ssimon}
413280304Sjkim
414280304Sjkim/*
415280304Sjkim * Various configuration of the mountpoint.  Mostly, enact 'mip'.
416280304Sjkim */
417280304Sjkimstatic void
41859194Skrisdo_mtptsetup(const char *mtpoint, struct mtpt_info *mip)
419280304Sjkim{
420280304Sjkim
421280304Sjkim	if (mip->mi_have_mode) {
422280304Sjkim		debugprintf("changing mode of %s to %o.", mtpoint,
42359194Skris		    mip->mi_mode);
424280304Sjkim		if (!norun)
425280304Sjkim			if (chmod(mtpoint, mip->mi_mode) == -1)
426280304Sjkim				err(1, "chmod: %s", mtpoint);
427280304Sjkim	}
428280304Sjkim	/*
429280304Sjkim	 * We have to do these separately because the user may have
430280304Sjkim	 * only specified one of them.
431280304Sjkim	 */
432280304Sjkim	if (mip->mi_have_uid) {
433280304Sjkim		debugprintf("changing owner (user) or %s to %u.", mtpoint,
434280304Sjkim		    mip->mi_uid);
435280304Sjkim		if (!norun)
436280304Sjkim			if (chown(mtpoint, mip->mi_uid, -1) == -1)
437280304Sjkim				err(1, "chown %s to %u (user)", mtpoint,
438280304Sjkim				    mip->mi_uid);
439280304Sjkim	}
440280304Sjkim	if (mip->mi_have_gid) {
441280304Sjkim		debugprintf("changing owner (group) or %s to %u.", mtpoint,
442280304Sjkim		    mip->mi_gid);
443280304Sjkim		if (!norun)
444280304Sjkim			if (chown(mtpoint, -1, mip->mi_gid) == -1)
445280304Sjkim				err(1, "chown %s to %u (group)", mtpoint,
446280304Sjkim				    mip->mi_gid);
44755714Skris	}
448280304Sjkim}
449280304Sjkim
450280304Sjkim/*
451280304Sjkim * Put a filesystem on the memory disk.
452280304Sjkim */
453280304Sjkimstatic void
454280304Sjkimdo_newfs(const char *args)
45555714Skris{
456280304Sjkim	int rv;
45755714Skris
458280304Sjkim	rv = run(NULL, "%s%s /dev/%s%dc", PATH_NEWFS, args, mdname, unit);
459280304Sjkim	if (rv)
460280304Sjkim		errx(1, "newfs exited with error code %d", rv);
461280304Sjkim}
46255714Skris
463280304Sjkim/*
464280304Sjkim * 'str' should be a user and group name similar to the last argument
465280304Sjkim * to chown(1); i.e., a user, followed by a colon, followed by a
466280304Sjkim * group.  The user and group in 'str' may be either a [ug]id or a
467280304Sjkim * name.  Upon return, the uid and gid fields in 'mip' will contain
468280304Sjkim * the uid and gid of the user and group name in 'str', respectively.
469280304Sjkim *
470280304Sjkim * In other words, this derives a user and group id from a string
471280304Sjkim * formatted like the last argument to chown(1).
472280304Sjkim */
473280304Sjkimstatic void
474280304Sjkimextract_ugid(const char *str, struct mtpt_info *mip)
475280304Sjkim{
476280304Sjkim	char *ug;			/* Writable 'str'. */
477280304Sjkim	char *user, *group;		/* Result of extracton. */
478280304Sjkim	size_t strl;			/* Length of 'str' incl. NULL. */
479280304Sjkim	struct passwd *pw;
480280304Sjkim	struct group *gr;
481110007Smarkm	char *p;
482280304Sjkim	uid_t *uid;
483280304Sjkim	gid_t *gid;
484280304Sjkim	size_t rv;
485280304Sjkim
486280304Sjkim	uid = &mip->mi_uid;
48755714Skris	gid = &mip->mi_gid;
488280304Sjkim	mip->mi_have_uid = mip->mi_have_gid = false;
489280304Sjkim
490280304Sjkim	/* Extract the user and group from 'str'.  Format above. */
49155714Skris	strl = strlen(str) + 1;
492280304Sjkim	ug = malloc(strl);
493280304Sjkim	assert(ug != NULL);
494280304Sjkim	rv = strlcpy(ug, str, strl);
495280304Sjkim	if (rv >= strl)
496280304Sjkim		errx(1, "-w word too long (%ld >= %ld)", (long)rv, (long)strl);
497280304Sjkim	group = ug;
49855714Skris	user = strsep(&group, ":");
499280304Sjkim	if (user == NULL || group == NULL || *user == '\0' || *group == '\0')
500280304Sjkim		usage();
50155714Skris
502280304Sjkim	/* Derive uid. */
503280304Sjkim	*uid = strtoul(user, &p, 10);
504280304Sjkim	if ((unsigned)*uid == ULONG_MAX)
505280304Sjkim		usage();
506280304Sjkim	if (*p != '\0') {
507280304Sjkim		pw = getpwnam(user);
508280304Sjkim		if (pw == NULL)
509280304Sjkim			errx(1, "invalid user: %s", user);
510280304Sjkim		*uid = pw->pw_uid;
511280304Sjkim		mip->mi_have_uid = true;
512280304Sjkim	}
513280304Sjkim
514280304Sjkim	/* Derive gid. */
515280304Sjkim	*gid = strtoul(group, &p, 10);
516280304Sjkim	if ((unsigned)*gid == ULONG_MAX)
517280304Sjkim		usage();
518280304Sjkim	if (*p != '\0') {
519238405Sjkim		gr = getgrnam(group);
520280304Sjkim		if (gr == NULL)
521280304Sjkim			errx(1, "invalid group: %s", group);
522280304Sjkim		*gid = gr->gr_gid;
523280304Sjkim		mip->mi_have_gid = true;
524280304Sjkim	}
525238405Sjkim
526238405Sjkim	free(ug);
527280304Sjkim	/*
528280304Sjkim	 * At this point we don't support only a username or only a
52955714Skris	 * group name.  do_mtptsetup already does, so when this
530280304Sjkim	 * feature is desired, this is the only routine that needs to
531280304Sjkim	 * be changed.
532280304Sjkim	 */
533280304Sjkim	assert(mip->mi_have_uid);
534280304Sjkim	assert(mip->mi_have_gid);
53555714Skris}
536280304Sjkim
537280304Sjkim/*
53859194Skris * Run a process with command name and arguments pointed to by the
539280304Sjkim * formatted string 'cmdline'.  Since system(3) is not used, the first
540110007Smarkm * space-delimited token of 'cmdline' must be the full pathname of the
541280304Sjkim * program to run.  The return value is the return code of the process
542280304Sjkim * spawned.  If 'ofd' is non-NULL, it is set to the standard output of
54355949Skris * the program spawned (i.e., you can read from ofd and get the output
544280304Sjkim * of the program).
545280304Sjkim */
546280304Sjkimstatic int
547280304Sjkimrun(int *ofd, const char *cmdline, ...)
548280304Sjkim{
54955714Skris	char **av, **avp;		/* Result of splitting 'cmd'. */
550280304Sjkim	int ac;
551280304Sjkim	char *cmd;			/* Expansion of 'cmdline'. */
552280304Sjkim	int pid, status;		/* Child info. */
553280304Sjkim	int pfd[2];			/* Pipe to the child. */
554280304Sjkim	int nfd;			/* Null (/dev/null) file descriptor. */
55555714Skris	bool dup2dn;			/* Dup /dev/null to stdout? */
556280304Sjkim	va_list ap;
557280304Sjkim	char *p;
55855714Skris	int rv, i;
559280304Sjkim
560280304Sjkim	dup2dn = true;
561280304Sjkim	va_start(ap, cmdline);
562280304Sjkim	rv = vasprintf(&cmd, cmdline, ap);
56355714Skris	if (rv == -1)
564280304Sjkim		err(1, "vasprintf");
565280304Sjkim	va_end(ap);
566280304Sjkim
567280304Sjkim	/* Split up 'cmd' into 'av' for use with execve. */
568280304Sjkim	for (ac = 1, p = cmd; (p = strchr(p, ' ')) != NULL; p++)
569280304Sjkim		ac++;		/* 'ac' generation loop. */
570280304Sjkim	av = (char **)malloc(sizeof(*av) * (ac + 1));
571280304Sjkim	assert(av != NULL);
572280304Sjkim	for (p = cmd, avp = av; (*avp = strsep(&p, " ")) != NULL;)
57355714Skris		if (**av != '\0')
574280304Sjkim			if (++avp >= &av[ac]) {
575280304Sjkim				*avp = NULL;
576280304Sjkim				break;
577280304Sjkim			}
578280304Sjkim	assert(*av);
579280304Sjkim
580280304Sjkim	/* Make sure the above loop works as expected. */
581280304Sjkim	if (debug) {
582280304Sjkim		/*
583280304Sjkim		 * We can't, but should, use debugprintf here.  First,
58455714Skris		 * it appends a trailing newline to the output, and
585280304Sjkim		 * second it prepends "DEBUG: " to the output.  The
586280304Sjkim		 * former is a problem for this would-be first call,
58755949Skris		 * and the latter for the would-be call inside the
588280304Sjkim		 * loop.
58955714Skris		 */
590280304Sjkim		(void)fprintf(stderr, "DEBUG: running:");
591280304Sjkim		/* Should be equivilent to 'cmd' (before strsep, of course). */
592280304Sjkim		for (i = 0; av[i] != NULL; i++)
593280304Sjkim			(void)fprintf(stderr, " %s", av[i]);
594280304Sjkim		(void)fprintf(stderr, "\n");
595280304Sjkim	}
596280304Sjkim
597280304Sjkim	/* Create a pipe if necessary and fork the helper program. */
598280304Sjkim	if (ofd != NULL) {
599280304Sjkim		if (pipe(&pfd[0]) == -1)
600280304Sjkim			err(1, "pipe");
60155714Skris		*ofd = pfd[0];
602280304Sjkim		dup2dn = false;
603280304Sjkim	}
60455714Skris	pid = fork();
605280304Sjkim	switch (pid) {
606280304Sjkim	case 0:
60755714Skris		/* XXX can we call err() in here? */
608280304Sjkim		if (norun)
609280304Sjkim			_exit(0);
610280304Sjkim		if (ofd != NULL)
611280304Sjkim			if (dup2(pfd[1], STDOUT_FILENO) < 0)
612280304Sjkim				err(1, "dup2");
613280304Sjkim		if (!loudsubs) {
614280304Sjkim			nfd = open(_PATH_DEVNULL, O_RDWR);
615280304Sjkim			if (nfd == -1)
616280304Sjkim				err(1, "open: %s", _PATH_DEVNULL);
617238405Sjkim			if (dup2(nfd, STDIN_FILENO) < 0)
618280304Sjkim				err(1, "dup2");
619280304Sjkim			if (dup2dn)
620280304Sjkim				if (dup2(nfd, STDOUT_FILENO) < 0)
621280304Sjkim				   err(1, "dup2");
622280304Sjkim			if (dup2(nfd, STDERR_FILENO) < 0)
623280304Sjkim				err(1, "dup2");
624280304Sjkim		}
625280304Sjkim
626280304Sjkim		(void)execv(av[0], av);
627280304Sjkim		warn("exec: %s", av[0]);
628280304Sjkim		_exit(-1);
62959194Skris	case -1:
630280304Sjkim		err(1, "fork");
631280304Sjkim	}
63255714Skris
633280304Sjkim	free(cmd);
634280304Sjkim	free(av);
635280304Sjkim	while (waitpid(pid, &status, 0) != pid)
636280304Sjkim		;
637280304Sjkim	return (WEXITSTATUS(status));
638280304Sjkim}
639280304Sjkim
640280304Sjkimstatic void
641280304Sjkimusage(void)
642280304Sjkim{
643280304Sjkim
644280304Sjkim	fprintf(stderr,
645280304Sjkim"usage: %s [-DLMNSX] [-a maxcontig] [-b block-size] [-c cylinders]\n"
646280304Sjkim"\t[-d rotdelay] [-e maxbpg] [-F file] [-f frag-size] [-i bytes]\n"
647280304Sjkim"\t[-m percent-free] [-n rotational-positions] [-O optimization]\n"
648"\t[-o mount-options] [-p permissions] [-s size] [-w user:group]\n"
649"\tmd-device mount-point\n", getprogname());
650	exit(1);
651}
652