xref: /freebsd/sbin/mdmfs/mdmfs.c (revision 5b21d4ad060acb06c72e0458daebec9bcbf0cefd)
11de7b4b8SPedro F. Giffuni /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
4d69f5deeSDima Dorfman  * Copyright (c) 2001 Dima Dorfman.
54d7de91fSDima Dorfman  * All rights reserved.
64d7de91fSDima Dorfman  *
74d7de91fSDima Dorfman  * Redistribution and use in source and binary forms, with or without
84d7de91fSDima Dorfman  * modification, are permitted provided that the following conditions
94d7de91fSDima Dorfman  * are met:
104d7de91fSDima Dorfman  * 1. Redistributions of source code must retain the above copyright
114d7de91fSDima Dorfman  *    notice, this list of conditions and the following disclaimer.
124d7de91fSDima Dorfman  * 2. Redistributions in binary form must reproduce the above copyright
134d7de91fSDima Dorfman  *    notice, this list of conditions and the following disclaimer in the
144d7de91fSDima Dorfman  *    documentation and/or other materials provided with the distribution.
154d7de91fSDima Dorfman  *
164d7de91fSDima Dorfman  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
174d7de91fSDima Dorfman  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
184d7de91fSDima Dorfman  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
194d7de91fSDima Dorfman  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
204d7de91fSDima Dorfman  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
214d7de91fSDima Dorfman  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
224d7de91fSDima Dorfman  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
234d7de91fSDima Dorfman  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
244d7de91fSDima Dorfman  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
254d7de91fSDima Dorfman  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
264d7de91fSDima Dorfman  * SUCH DAMAGE.
274d7de91fSDima Dorfman  */
284d7de91fSDima Dorfman 
294d7de91fSDima Dorfman /*
30b7e368f7SPoul-Henning Kamp  * mdmfs (md/MFS) is a wrapper around mdconfig(8),
314d7de91fSDima Dorfman  * newfs(8), and mount(8) that mimics the command line option set of
324d7de91fSDima Dorfman  * the deprecated mount_mfs(8).
334d7de91fSDima Dorfman  */
344d7de91fSDima Dorfman 
354d7de91fSDima Dorfman #include <sys/param.h>
3650e3590cSIan Lepore #include <sys/linker.h>
374d7de91fSDima Dorfman #include <sys/mdioctl.h>
3850e3590cSIan Lepore #include <sys/module.h>
39e50e74d8SKonstantin Belousov #include <sys/mount.h>
404d7de91fSDima Dorfman #include <sys/stat.h>
414d7de91fSDima Dorfman #include <sys/wait.h>
424d7de91fSDima Dorfman 
434d7de91fSDima Dorfman #include <assert.h>
444d7de91fSDima Dorfman #include <err.h>
4550e3590cSIan Lepore #include <errno.h>
464d7de91fSDima Dorfman #include <fcntl.h>
474d7de91fSDima Dorfman #include <grp.h>
4850e3590cSIan Lepore #include <inttypes.h>
494d7de91fSDima Dorfman #include <paths.h>
504d7de91fSDima Dorfman #include <pwd.h>
514d7de91fSDima Dorfman #include <stdarg.h>
524d7de91fSDima Dorfman #include <stdio.h>
534d7de91fSDima Dorfman #include <stdlib.h>
544d7de91fSDima Dorfman #include <string.h>
558c6a98deSMatteo Riondato #include <ctype.h>
564d7de91fSDima Dorfman #include <unistd.h>
574d7de91fSDima Dorfman 
584d7de91fSDima Dorfman typedef enum { false, true } bool;
594d7de91fSDima Dorfman 
604d7de91fSDima Dorfman struct mtpt_info {
614d7de91fSDima Dorfman 	uid_t		 mi_uid;
624d7de91fSDima Dorfman 	bool		 mi_have_uid;
634d7de91fSDima Dorfman 	gid_t		 mi_gid;
644d7de91fSDima Dorfman 	bool		 mi_have_gid;
654d7de91fSDima Dorfman 	mode_t		 mi_mode;
664d7de91fSDima Dorfman 	bool		 mi_have_mode;
67e50e74d8SKonstantin Belousov 	bool		 mi_forced_pw;
684d7de91fSDima Dorfman };
694d7de91fSDima Dorfman 
704d7de91fSDima Dorfman static	bool debug;		/* Emit debugging information? */
714d7de91fSDima Dorfman static	bool loudsubs;		/* Suppress output from helper programs? */
724d7de91fSDima Dorfman static	bool norun;		/* Actually run the helper programs? */
734d7de91fSDima Dorfman static	int unit;      		/* The unit we're working with. */
744d7de91fSDima Dorfman static	const char *mdname;	/* Name of memory disk device (e.g., "md"). */
758c6a98deSMatteo Riondato static	const char *mdsuffix;	/* Suffix of memory disk device (e.g., ".uzip"). */
764d7de91fSDima Dorfman static	size_t mdnamelen;	/* Length of mdname. */
7763f8ddbeSMaxim Sobolev static	const char *path_mdconfig =_PATH_MDCONFIG;
784d7de91fSDima Dorfman 
79bc56b93aSKris Kennaway static void	 argappend(char **, const char *, ...) __printflike(2, 3);
80bc56b93aSKris Kennaway static void	 debugprintf(const char *, ...) __printflike(1, 2);
814d7de91fSDima Dorfman static void	 do_mdconfig_attach(const char *, const enum md_types);
824d7de91fSDima Dorfman static void	 do_mdconfig_attach_au(const char *, const enum md_types);
834d7de91fSDima Dorfman static void	 do_mdconfig_detach(void);
8450e3590cSIan Lepore static void	 do_mount_md(const char *, const char *);
8550e3590cSIan Lepore static void	 do_mount_tmpfs(const char *, const char *);
864d7de91fSDima Dorfman static void	 do_mtptsetup(const char *, struct mtpt_info *);
874d7de91fSDima Dorfman static void	 do_newfs(const char *);
88f8526186SKyle Evans static void	 do_copy(const char *, const char *);
894d7de91fSDima Dorfman static void	 extract_ugid(const char *, struct mtpt_info *);
90bc56b93aSKris Kennaway static int	 run(int *, const char *, ...) __printflike(2, 3);
91594fb8f5SConrad Meyer static const char *run_exitstr(int);
92594fb8f5SConrad Meyer static int	 run_exitnumber(int);
934d7de91fSDima Dorfman static void	 usage(void);
944d7de91fSDima Dorfman 
954d7de91fSDima Dorfman int
main(int argc,char ** argv)9694ddc5afSDavid E. O'Brien main(int argc, char **argv)
974d7de91fSDima Dorfman {
984d7de91fSDima Dorfman 	struct mtpt_info mi;		/* Mountpoint info. */
9950e3590cSIan Lepore 	intmax_t mdsize;
1004d7de91fSDima Dorfman 	char *mdconfig_arg, *newfs_arg,	/* Args to helper programs. */
1014d7de91fSDima Dorfman 	    *mount_arg;
1024d7de91fSDima Dorfman 	enum md_types mdtype;		/* The type of our memory disk. */
10350e3590cSIan Lepore 	bool have_mdtype, mlmac;
10405b2fd30SDima Dorfman 	bool detach, softdep, autounit, newfs;
105f8526186SKyle Evans 	const char *mtpoint, *size_arg, *skel, *unitstr;
1065cfe0423SPeter Grehan 	char *p;
107*5b21d4adSKirk McKusick 	int ch, idx, rv;
108ce03e3a7SYaroslav Tykhiy 	void *set;
10947376189SSuleiman Souhlal 	unsigned long ul;
1104d7de91fSDima Dorfman 
1114d7de91fSDima Dorfman 	/* Misc. initialization. */
1124d7de91fSDima Dorfman 	(void)memset(&mi, '\0', sizeof(mi));
1134d7de91fSDima Dorfman 	detach = true;
1144d7de91fSDima Dorfman 	softdep = true;
1154d7de91fSDima Dorfman 	autounit = false;
11650e3590cSIan Lepore 	mlmac = false;
11705b2fd30SDima Dorfman 	newfs = true;
1184d7de91fSDima Dorfman 	have_mdtype = false;
119f8526186SKyle Evans 	skel = NULL;
1206449237fSSuleiman Souhlal 	mdtype = MD_SWAP;
1214d7de91fSDima Dorfman 	mdname = MD_NAME;
1224d7de91fSDima Dorfman 	mdnamelen = strlen(mdname);
123f3b5058cSAndreas Tobler 	mdsize = 0;
1244d7de91fSDima Dorfman 	/*
1254d7de91fSDima Dorfman 	 * Can't set these to NULL.  They may be passed to the
1264d7de91fSDima Dorfman 	 * respective programs without modification.  I.e., we may not
1274d7de91fSDima Dorfman 	 * receive any command-line options which will caused them to
1284d7de91fSDima Dorfman 	 * be modified.
1294d7de91fSDima Dorfman 	 */
1304d7de91fSDima Dorfman 	mdconfig_arg = strdup("");
1314d7de91fSDima Dorfman 	newfs_arg = strdup("");
1324d7de91fSDima Dorfman 	mount_arg = strdup("");
13350e3590cSIan Lepore 	size_arg = NULL;
1344d7de91fSDima Dorfman 
1352dc4ac06SIan Dowse 	/* If we were started as mount_mfs or mfs, imply -C. */
1362dc4ac06SIan Dowse 	if (strcmp(getprogname(), "mount_mfs") == 0 ||
137957d7c8fSRuslan Ermilov 	    strcmp(getprogname(), "mfs") == 0) {
138957d7c8fSRuslan Ermilov 		/* Make compatibility assumptions. */
139957d7c8fSRuslan Ermilov 		mi.mi_mode = 01777;
140957d7c8fSRuslan Ermilov 		mi.mi_have_mode = true;
141957d7c8fSRuslan Ermilov 	}
142f7acb7e4SDima Dorfman 
14394ddc5afSDavid E. O'Brien 	while ((ch = getopt(argc, argv,
144f8526186SKyle Evans 	    "a:b:Cc:Dd:E:e:F:f:hi:k:LlMm:NnO:o:Pp:Ss:tT:Uv:w:X")) != -1)
1454d7de91fSDima Dorfman 		switch (ch) {
1464d7de91fSDima Dorfman 		case 'a':
1474d7de91fSDima Dorfman 			argappend(&newfs_arg, "-a %s", optarg);
1484d7de91fSDima Dorfman 			break;
1494d7de91fSDima Dorfman 		case 'b':
1504d7de91fSDima Dorfman 			argappend(&newfs_arg, "-b %s", optarg);
1514d7de91fSDima Dorfman 			break;
152f7acb7e4SDima Dorfman 		case 'C':
153957d7c8fSRuslan Ermilov 			/* Ignored for compatibility. */
154f7acb7e4SDima Dorfman 			break;
1554d7de91fSDima Dorfman 		case 'c':
1564d7de91fSDima Dorfman 			argappend(&newfs_arg, "-c %s", optarg);
1574d7de91fSDima Dorfman 			break;
1584d7de91fSDima Dorfman 		case 'D':
1594d7de91fSDima Dorfman 			detach = false;
1604d7de91fSDima Dorfman 			break;
1614d7de91fSDima Dorfman 		case 'd':
1624d7de91fSDima Dorfman 			argappend(&newfs_arg, "-d %s", optarg);
1634d7de91fSDima Dorfman 			break;
16463f8ddbeSMaxim Sobolev 		case 'E':
16563f8ddbeSMaxim Sobolev 			path_mdconfig = optarg;
16663f8ddbeSMaxim Sobolev 			break;
1674d7de91fSDima Dorfman 		case 'e':
1684d7de91fSDima Dorfman 			argappend(&newfs_arg, "-e %s", optarg);
1694d7de91fSDima Dorfman 			break;
1704d7de91fSDima Dorfman 		case 'F':
1714d7de91fSDima Dorfman 			if (have_mdtype)
1724d7de91fSDima Dorfman 				usage();
1734d7de91fSDima Dorfman 			mdtype = MD_VNODE;
1744d7de91fSDima Dorfman 			have_mdtype = true;
1754d7de91fSDima Dorfman 			argappend(&mdconfig_arg, "-f %s", optarg);
1764d7de91fSDima Dorfman 			break;
1774d7de91fSDima Dorfman 		case 'f':
1784d7de91fSDima Dorfman 			argappend(&newfs_arg, "-f %s", optarg);
1794d7de91fSDima Dorfman 			break;
1804d7de91fSDima Dorfman 		case 'h':
1814d7de91fSDima Dorfman 			usage();
1824d7de91fSDima Dorfman 			break;
1834d7de91fSDima Dorfman 		case 'i':
1844d7de91fSDima Dorfman 			argappend(&newfs_arg, "-i %s", optarg);
1854d7de91fSDima Dorfman 			break;
186f8526186SKyle Evans 		case 'k':
187f8526186SKyle Evans 			skel = optarg;
188f8526186SKyle Evans 			break;
1894d7de91fSDima Dorfman 		case 'L':
1904d7de91fSDima Dorfman 			loudsubs = true;
1914d7de91fSDima Dorfman 			break;
1921d3170aaSRobert Watson 		case 'l':
19350e3590cSIan Lepore 			mlmac = true;
1941d3170aaSRobert Watson 			argappend(&newfs_arg, "-l");
1951d3170aaSRobert Watson 			break;
1964d7de91fSDima Dorfman 		case 'M':
1974d7de91fSDima Dorfman 			if (have_mdtype)
1984d7de91fSDima Dorfman 				usage();
1994d7de91fSDima Dorfman 			mdtype = MD_MALLOC;
2004d7de91fSDima Dorfman 			have_mdtype = true;
201c9b6ff9cSKyle Evans 			argappend(&mdconfig_arg, "-o reserve");
2024d7de91fSDima Dorfman 			break;
2034d7de91fSDima Dorfman 		case 'm':
2044d7de91fSDima Dorfman 			argappend(&newfs_arg, "-m %s", optarg);
2054d7de91fSDima Dorfman 			break;
2064d7de91fSDima Dorfman 		case 'N':
2074d7de91fSDima Dorfman 			norun = true;
2084d7de91fSDima Dorfman 			break;
2094d7de91fSDima Dorfman 		case 'n':
210c918e9f7SRemko Lodder 			argappend(&newfs_arg, "-n");
2114d7de91fSDima Dorfman 			break;
2124d7de91fSDima Dorfman 		case 'O':
2134d7de91fSDima Dorfman 			argappend(&newfs_arg, "-o %s", optarg);
2144d7de91fSDima Dorfman 			break;
2154d7de91fSDima Dorfman 		case 'o':
2164d7de91fSDima Dorfman 			argappend(&mount_arg, "-o %s", optarg);
2174d7de91fSDima Dorfman 			break;
21805b2fd30SDima Dorfman 		case 'P':
21905b2fd30SDima Dorfman 			newfs = false;
22005b2fd30SDima Dorfman 			break;
2214d7de91fSDima Dorfman 		case 'p':
222ce03e3a7SYaroslav Tykhiy 			if ((set = setmode(optarg)) == NULL)
2234d7de91fSDima Dorfman 				usage();
224ce03e3a7SYaroslav Tykhiy 			mi.mi_mode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO);
2254d7de91fSDima Dorfman 			mi.mi_have_mode = true;
226e50e74d8SKonstantin Belousov 			mi.mi_forced_pw = true;
227ce03e3a7SYaroslav Tykhiy 			free(set);
2284d7de91fSDima Dorfman 			break;
2294d7de91fSDima Dorfman 		case 'S':
2304d7de91fSDima Dorfman 			softdep = false;
2314d7de91fSDima Dorfman 			break;
2324d7de91fSDima Dorfman 		case 's':
23350e3590cSIan Lepore 			size_arg = optarg;
2344d7de91fSDima Dorfman 			break;
235bf6619baSKonstantin Belousov 		case 't':
236bf6619baSKonstantin Belousov 			argappend(&newfs_arg, "-t");
237bf6619baSKonstantin Belousov 			break;
2389bb18677SAlex Kozlov 		case 'T':
2399bb18677SAlex Kozlov 			argappend(&mount_arg, "-t %s", optarg);
2409bb18677SAlex Kozlov 			break;
241f7acb7e4SDima Dorfman 		case 'U':
242f7acb7e4SDima Dorfman 			softdep = true;
243f7acb7e4SDima Dorfman 			break;
244622448faSRobert Watson 		case 'v':
245622448faSRobert Watson 			argappend(&newfs_arg, "-O %s", optarg);
246622448faSRobert Watson 			break;
2474d7de91fSDima Dorfman 		case 'w':
2484d7de91fSDima Dorfman 			extract_ugid(optarg, &mi);
249e50e74d8SKonstantin Belousov 			mi.mi_forced_pw = true;
2504d7de91fSDima Dorfman 			break;
2514d7de91fSDima Dorfman 		case 'X':
2524d7de91fSDima Dorfman 			debug = true;
2534d7de91fSDima Dorfman 			break;
2544d7de91fSDima Dorfman 		default:
2554d7de91fSDima Dorfman 			usage();
2564d7de91fSDima Dorfman 		}
25794ddc5afSDavid E. O'Brien 	argc -= optind;
25894ddc5afSDavid E. O'Brien 	argv += optind;
25994ddc5afSDavid E. O'Brien 	if (argc < 2)
2604d7de91fSDima Dorfman 		usage();
2614d7de91fSDima Dorfman 
26250e3590cSIan Lepore 	/*
26350e3590cSIan Lepore 	 * Historically our size arg was passed directly to mdconfig, which
26450e3590cSIan Lepore 	 * treats a number without a suffix as a count of 512-byte sectors;
26550e3590cSIan Lepore 	 * tmpfs would treat it as a count of bytes.  To get predictable
26650e3590cSIan Lepore 	 * behavior for 'auto' we document that the size always uses mdconfig
26750e3590cSIan Lepore 	 * rules.  To make that work, decode the size here so it can be passed
26850e3590cSIan Lepore 	 * to either tmpfs or mdconfig as a count of bytes.
26950e3590cSIan Lepore 	 */
27050e3590cSIan Lepore 	if (size_arg != NULL) {
27150e3590cSIan Lepore 		mdsize = (intmax_t)strtoumax(size_arg, &p, 0);
27250e3590cSIan Lepore 		if (p == size_arg || (p[0] != 0 && p[1] != 0) || mdsize < 0)
27350e3590cSIan Lepore 			errx(1, "invalid size '%s'", size_arg);
27450e3590cSIan Lepore 		switch (*p) {
27550e3590cSIan Lepore 		case 'p':
27650e3590cSIan Lepore 		case 'P':
27750e3590cSIan Lepore 			mdsize *= 1024;
27850e3590cSIan Lepore 		case 't':
27950e3590cSIan Lepore 		case 'T':
28050e3590cSIan Lepore 			mdsize *= 1024;
28150e3590cSIan Lepore 		case 'g':
28250e3590cSIan Lepore 		case 'G':
28350e3590cSIan Lepore 			mdsize *= 1024;
28450e3590cSIan Lepore 		case 'm':
28550e3590cSIan Lepore 		case 'M':
28650e3590cSIan Lepore 			mdsize *= 1024;
28750e3590cSIan Lepore 		case 'k':
28850e3590cSIan Lepore 		case 'K':
28950e3590cSIan Lepore 			mdsize *= 1024;
29050e3590cSIan Lepore 		case 'b':
29150e3590cSIan Lepore 		case 'B':
29250e3590cSIan Lepore 			break;
29350e3590cSIan Lepore 		case '\0':
29450e3590cSIan Lepore 			mdsize *= 512;
29550e3590cSIan Lepore 			break;
29650e3590cSIan Lepore 		default:
29750e3590cSIan Lepore 			errx(1, "invalid size suffix on '%s'", size_arg);
29850e3590cSIan Lepore 		}
29950e3590cSIan Lepore 	}
30050e3590cSIan Lepore 
30150e3590cSIan Lepore 	/*
30250e3590cSIan Lepore 	 * Based on the command line 'md-device' either mount a tmpfs filesystem
30350e3590cSIan Lepore 	 * or configure the md device then format and mount a filesystem on it.
30450e3590cSIan Lepore 	 * If the device is 'auto' use tmpfs if it is available and there is no
30550e3590cSIan Lepore 	 * request for multilabel MAC (which tmpfs does not support).
30650e3590cSIan Lepore 	 */
30794ddc5afSDavid E. O'Brien 	unitstr = argv[0];
30850e3590cSIan Lepore 	mtpoint = argv[1];
30950e3590cSIan Lepore 
31050e3590cSIan Lepore 	if (strcmp(unitstr, "auto") == 0) {
31150e3590cSIan Lepore 		if (mlmac)
31250e3590cSIan Lepore 			idx = -1; /* Must use md for mlmac. */
31350e3590cSIan Lepore 		else if ((idx = modfind("tmpfs")) == -1)
31450e3590cSIan Lepore 			idx = kldload("tmpfs");
31550e3590cSIan Lepore 		if (idx == -1)
31650e3590cSIan Lepore 			unitstr = "md";
31750e3590cSIan Lepore 		else
31850e3590cSIan Lepore 			unitstr = "tmpfs";
31950e3590cSIan Lepore 	}
32050e3590cSIan Lepore 
32150e3590cSIan Lepore 	if (strcmp(unitstr, "tmpfs") == 0) {
32250e3590cSIan Lepore 		if (size_arg != NULL && mdsize != 0)
32350e3590cSIan Lepore 			argappend(&mount_arg, "-o size=%jd", mdsize);
32450e3590cSIan Lepore 		do_mount_tmpfs(mount_arg, mtpoint);
32550e3590cSIan Lepore 	} else {
32650e3590cSIan Lepore 		if (size_arg != NULL)
32750e3590cSIan Lepore 			argappend(&mdconfig_arg, "-s %jdB", mdsize);
3284d7de91fSDima Dorfman 		if (strncmp(unitstr, "/dev/", 5) == 0)
3294d7de91fSDima Dorfman 			unitstr += 5;
3304d7de91fSDima Dorfman 		if (strncmp(unitstr, mdname, mdnamelen) == 0)
3314d7de91fSDima Dorfman 			unitstr += mdnamelen;
3328c6a98deSMatteo Riondato 		if (!isdigit(*unitstr)) {
3334d7de91fSDima Dorfman 			autounit = true;
3344d7de91fSDima Dorfman 			unit = -1;
3358c6a98deSMatteo Riondato 			mdsuffix = unitstr;
3364d7de91fSDima Dorfman 		} else {
33747376189SSuleiman Souhlal 			ul = strtoul(unitstr, &p, 10);
3388c6a98deSMatteo Riondato 			if (ul == ULONG_MAX)
3394d7de91fSDima Dorfman 				errx(1, "bad device unit: %s", unitstr);
34047376189SSuleiman Souhlal 			unit = ul;
3419a7d93d6SYaroslav Tykhiy 			mdsuffix = p;	/* can be empty */
3424d7de91fSDima Dorfman 		}
3434d7de91fSDima Dorfman 
3444d7de91fSDima Dorfman 		if (!have_mdtype)
3454d7de91fSDima Dorfman 			mdtype = MD_SWAP;
3464d7de91fSDima Dorfman 		if (softdep)
3474d7de91fSDima Dorfman 			argappend(&newfs_arg, "-U");
34805b2fd30SDima Dorfman 		if (mdtype != MD_VNODE && !newfs)
34905b2fd30SDima Dorfman 			errx(1, "-P requires a vnode-backed disk");
3504d7de91fSDima Dorfman 
3514d7de91fSDima Dorfman 		/* Do the work. */
3524d7de91fSDima Dorfman 		if (detach && !autounit)
3534d7de91fSDima Dorfman 			do_mdconfig_detach();
3544d7de91fSDima Dorfman 		if (autounit)
3554d7de91fSDima Dorfman 			do_mdconfig_attach_au(mdconfig_arg, mdtype);
3564d7de91fSDima Dorfman 		else
3574d7de91fSDima Dorfman 			do_mdconfig_attach(mdconfig_arg, mdtype);
35805b2fd30SDima Dorfman 		if (newfs)
3594d7de91fSDima Dorfman 			do_newfs(newfs_arg);
360*5b21d4adSKirk McKusick 		if (!softdep) {
361*5b21d4adSKirk McKusick 			rv = run(NULL, "%s %s /dev/%s%d", _PATH_TUNEFS,
362*5b21d4adSKirk McKusick 			    "-n disable", mdname, unit);
363*5b21d4adSKirk McKusick 			if (rv)
364*5b21d4adSKirk McKusick 				errx(1, "tunefs exited %s %d", run_exitstr(rv),
365*5b21d4adSKirk McKusick 				    run_exitnumber(rv));
366*5b21d4adSKirk McKusick 		}
36750e3590cSIan Lepore 		do_mount_md(mount_arg, mtpoint);
36850e3590cSIan Lepore 	}
36950e3590cSIan Lepore 
3704d7de91fSDima Dorfman 	do_mtptsetup(mtpoint, &mi);
371f8526186SKyle Evans 	if (skel != NULL)
372f8526186SKyle Evans 		do_copy(mtpoint, skel);
3734d7de91fSDima Dorfman 
3744d7de91fSDima Dorfman 	return (0);
3754d7de91fSDima Dorfman }
3764d7de91fSDima Dorfman 
3774d7de91fSDima Dorfman /*
3784d7de91fSDima Dorfman  * Append the expansion of 'fmt' to the buffer pointed to by '*dstp';
3794d7de91fSDima Dorfman  * reallocate as required.
3804d7de91fSDima Dorfman  */
3814d7de91fSDima Dorfman static void
argappend(char ** dstp,const char * fmt,...)3824d7de91fSDima Dorfman argappend(char **dstp, const char *fmt, ...)
3834d7de91fSDima Dorfman {
3844d7de91fSDima Dorfman 	char *old, *new;
3854d7de91fSDima Dorfman 	va_list ap;
3864d7de91fSDima Dorfman 
3874d7de91fSDima Dorfman 	old = *dstp;
3884d7de91fSDima Dorfman 	assert(old != NULL);
3894d7de91fSDima Dorfman 
3904d7de91fSDima Dorfman 	va_start(ap, fmt);
3914d7de91fSDima Dorfman 	if (vasprintf(&new, fmt,ap) == -1)
3924d7de91fSDima Dorfman 		errx(1, "vasprintf");
3934d7de91fSDima Dorfman 	va_end(ap);
3944d7de91fSDima Dorfman 
3954d7de91fSDima Dorfman 	*dstp = new;
3964d7de91fSDima Dorfman 	if (asprintf(&new, "%s %s", old, new) == -1)
3974d7de91fSDima Dorfman 		errx(1, "asprintf");
3984d7de91fSDima Dorfman 	free(*dstp);
3994d7de91fSDima Dorfman 	free(old);
4004d7de91fSDima Dorfman 
4014d7de91fSDima Dorfman 	*dstp = new;
4024d7de91fSDima Dorfman }
4034d7de91fSDima Dorfman 
4044d7de91fSDima Dorfman /*
4054d7de91fSDima Dorfman  * If run-time debugging is enabled, print the expansion of 'fmt'.
4064d7de91fSDima Dorfman  * Otherwise, do nothing.
4074d7de91fSDima Dorfman  */
4084d7de91fSDima Dorfman static void
debugprintf(const char * fmt,...)4094d7de91fSDima Dorfman debugprintf(const char *fmt, ...)
4104d7de91fSDima Dorfman {
4114d7de91fSDima Dorfman 	va_list ap;
4124d7de91fSDima Dorfman 
4134d7de91fSDima Dorfman 	if (!debug)
4144d7de91fSDima Dorfman 		return;
4154d7de91fSDima Dorfman 	fprintf(stderr, "DEBUG: ");
4164d7de91fSDima Dorfman 	va_start(ap, fmt);
4174d7de91fSDima Dorfman 	vfprintf(stderr, fmt, ap);
4184d7de91fSDima Dorfman 	va_end(ap);
4194d7de91fSDima Dorfman 	fprintf(stderr, "\n");
4204d7de91fSDima Dorfman 	fflush(stderr);
4214d7de91fSDima Dorfman }
4224d7de91fSDima Dorfman 
4234d7de91fSDima Dorfman /*
4244d7de91fSDima Dorfman  * Attach a memory disk with a known unit.
4254d7de91fSDima Dorfman  */
4264d7de91fSDima Dorfman static void
do_mdconfig_attach(const char * args,const enum md_types mdtype)4274d7de91fSDima Dorfman do_mdconfig_attach(const char *args, const enum md_types mdtype)
4284d7de91fSDima Dorfman {
4294d7de91fSDima Dorfman 	int rv;
4304d7de91fSDima Dorfman 	const char *ta;		/* Type arg. */
4314d7de91fSDima Dorfman 
4324d7de91fSDima Dorfman 	switch (mdtype) {
4334d7de91fSDima Dorfman 	case MD_SWAP:
4344d7de91fSDima Dorfman 		ta = "-t swap";
4354d7de91fSDima Dorfman 		break;
4364d7de91fSDima Dorfman 	case MD_VNODE:
4374d7de91fSDima Dorfman 		ta = "-t vnode";
4384d7de91fSDima Dorfman 		break;
4394d7de91fSDima Dorfman 	case MD_MALLOC:
4404d7de91fSDima Dorfman 		ta = "-t malloc";
4414d7de91fSDima Dorfman 		break;
4424d7de91fSDima Dorfman 	default:
4434d7de91fSDima Dorfman 		abort();
4444d7de91fSDima Dorfman 	}
44563f8ddbeSMaxim Sobolev 	rv = run(NULL, "%s -a %s%s -u %s%d", path_mdconfig, ta, args,
4464d7de91fSDima Dorfman 	    mdname, unit);
4474d7de91fSDima Dorfman 	if (rv)
448594fb8f5SConrad Meyer 		errx(1, "mdconfig (attach) exited %s %d", run_exitstr(rv),
449594fb8f5SConrad Meyer 		    run_exitnumber(rv));
4504d7de91fSDima Dorfman }
4514d7de91fSDima Dorfman 
4524d7de91fSDima Dorfman /*
4534d7de91fSDima Dorfman  * Attach a memory disk with an unknown unit; use autounit.
4544d7de91fSDima Dorfman  */
4554d7de91fSDima Dorfman static void
do_mdconfig_attach_au(const char * args,const enum md_types mdtype)4564d7de91fSDima Dorfman do_mdconfig_attach_au(const char *args, const enum md_types mdtype)
4574d7de91fSDima Dorfman {
4584d7de91fSDima Dorfman 	const char *ta;		/* Type arg. */
459f95509a4SBrooks Davis 	char *linep;
460f95509a4SBrooks Davis 	char linebuf[12];	/* 32-bit unit (10) + '\n' (1) + '\0' (1) */
4614d7de91fSDima Dorfman 	int fd;			/* Standard output of mdconfig invocation. */
4624d7de91fSDima Dorfman 	FILE *sfd;
4634d7de91fSDima Dorfman 	int rv;
4644d7de91fSDima Dorfman 	char *p;
4654d7de91fSDima Dorfman 	size_t linelen;
46647376189SSuleiman Souhlal 	unsigned long ul;
4674d7de91fSDima Dorfman 
4684d7de91fSDima Dorfman 	switch (mdtype) {
4694d7de91fSDima Dorfman 	case MD_SWAP:
4704d7de91fSDima Dorfman 		ta = "-t swap";
4714d7de91fSDima Dorfman 		break;
4724d7de91fSDima Dorfman 	case MD_VNODE:
4734d7de91fSDima Dorfman 		ta = "-t vnode";
4744d7de91fSDima Dorfman 		break;
4754d7de91fSDima Dorfman 	case MD_MALLOC:
4764d7de91fSDima Dorfman 		ta = "-t malloc";
4774d7de91fSDima Dorfman 		break;
4784d7de91fSDima Dorfman 	default:
4794d7de91fSDima Dorfman 		abort();
4804d7de91fSDima Dorfman 	}
48163f8ddbeSMaxim Sobolev 	rv = run(&fd, "%s -a %s%s", path_mdconfig, ta, args);
4824d7de91fSDima Dorfman 	if (rv)
483594fb8f5SConrad Meyer 		errx(1, "mdconfig (attach) exited %s %d", run_exitstr(rv),
484594fb8f5SConrad Meyer 		    run_exitnumber(rv));
4854d7de91fSDima Dorfman 
4864d7de91fSDima Dorfman 	/* Receive the unit number. */
4874d7de91fSDima Dorfman 	if (norun) {	/* Since we didn't run, we can't read.  Fake it. */
488541ce3c1SDima Dorfman 		unit = 0;
4894d7de91fSDima Dorfman 		return;
4904d7de91fSDima Dorfman 	}
4914d7de91fSDima Dorfman 	sfd = fdopen(fd, "r");
4924d7de91fSDima Dorfman 	if (sfd == NULL)
4934d7de91fSDima Dorfman 		err(1, "fdopen");
4944d7de91fSDima Dorfman 	linep = fgetln(sfd, &linelen);
4954d7de91fSDima Dorfman 	/* If the output format changes, we want to know about it. */
496f95509a4SBrooks Davis 	if (linep == NULL || linelen <= mdnamelen + 1 ||
497f95509a4SBrooks Davis 	    linelen - mdnamelen >= sizeof(linebuf) ||
498f95509a4SBrooks Davis 	    strncmp(linep, mdname, mdnamelen) != 0)
499f95509a4SBrooks Davis 		errx(1, "unexpected output from mdconfig (attach)");
500f95509a4SBrooks Davis 	linep += mdnamelen;
501f95509a4SBrooks Davis 	linelen -= mdnamelen;
5024d7de91fSDima Dorfman 	/* Can't use strlcpy because linep is not NULL-terminated. */
503f95509a4SBrooks Davis 	strncpy(linebuf, linep, linelen);
5044d7de91fSDima Dorfman 	linebuf[linelen] = '\0';
50547376189SSuleiman Souhlal 	ul = strtoul(linebuf, &p, 10);
50647376189SSuleiman Souhlal 	if (ul == ULONG_MAX || *p != '\n')
5074d7de91fSDima Dorfman 		errx(1, "unexpected output from mdconfig (attach)");
50847376189SSuleiman Souhlal 	unit = ul;
5094d7de91fSDima Dorfman 
5104d7de91fSDima Dorfman 	fclose(sfd);
5114d7de91fSDima Dorfman }
5124d7de91fSDima Dorfman 
5134d7de91fSDima Dorfman /*
5144d7de91fSDima Dorfman  * Detach a memory disk.
5154d7de91fSDima Dorfman  */
5164d7de91fSDima Dorfman static void
do_mdconfig_detach(void)5174d7de91fSDima Dorfman do_mdconfig_detach(void)
5184d7de91fSDima Dorfman {
5194d7de91fSDima Dorfman 	int rv;
5204d7de91fSDima Dorfman 
52163f8ddbeSMaxim Sobolev 	rv = run(NULL, "%s -d -u %s%d", path_mdconfig, mdname, unit);
5224d7de91fSDima Dorfman 	if (rv && debug)	/* This is allowed to fail. */
523594fb8f5SConrad Meyer 		warnx("mdconfig (detach) exited %s %d (ignored)",
524594fb8f5SConrad Meyer 		    run_exitstr(rv), run_exitnumber(rv));
5254d7de91fSDima Dorfman }
5264d7de91fSDima Dorfman 
5274d7de91fSDima Dorfman /*
5284d7de91fSDima Dorfman  * Mount the configured memory disk.
5294d7de91fSDima Dorfman  */
5304d7de91fSDima Dorfman static void
do_mount_md(const char * args,const char * mtpoint)53150e3590cSIan Lepore do_mount_md(const char *args, const char *mtpoint)
5324d7de91fSDima Dorfman {
5334d7de91fSDima Dorfman 	int rv;
5344d7de91fSDima Dorfman 
5358c6a98deSMatteo Riondato 	rv = run(NULL, "%s%s /dev/%s%d%s %s", _PATH_MOUNT, args,
5368c6a98deSMatteo Riondato 	    mdname, unit, mdsuffix, mtpoint);
5374d7de91fSDima Dorfman 	if (rv)
538594fb8f5SConrad Meyer 		errx(1, "mount exited %s %d", run_exitstr(rv),
539594fb8f5SConrad Meyer 		    run_exitnumber(rv));
5404d7de91fSDima Dorfman }
5414d7de91fSDima Dorfman 
5424d7de91fSDima Dorfman /*
54350e3590cSIan Lepore  * Mount the configured tmpfs.
54450e3590cSIan Lepore  */
54550e3590cSIan Lepore static void
do_mount_tmpfs(const char * args,const char * mtpoint)54650e3590cSIan Lepore do_mount_tmpfs(const char *args, const char *mtpoint)
54750e3590cSIan Lepore {
54850e3590cSIan Lepore 	int rv;
54950e3590cSIan Lepore 
55050e3590cSIan Lepore 	rv = run(NULL, "%s -t tmpfs %s tmp %s", _PATH_MOUNT, args, mtpoint);
55150e3590cSIan Lepore 	if (rv)
552594fb8f5SConrad Meyer 		errx(1, "tmpfs mount exited %s %d", run_exitstr(rv),
553594fb8f5SConrad Meyer 		    run_exitnumber(rv));
55450e3590cSIan Lepore }
55550e3590cSIan Lepore 
55650e3590cSIan Lepore /*
5574d7de91fSDima Dorfman  * Various configuration of the mountpoint.  Mostly, enact 'mip'.
5584d7de91fSDima Dorfman  */
5594d7de91fSDima Dorfman static void
do_mtptsetup(const char * mtpoint,struct mtpt_info * mip)5604d7de91fSDima Dorfman do_mtptsetup(const char *mtpoint, struct mtpt_info *mip)
5614d7de91fSDima Dorfman {
562e50e74d8SKonstantin Belousov 	struct statfs sfs;
563e50e74d8SKonstantin Belousov 
564e50e74d8SKonstantin Belousov 	if (!mip->mi_have_mode && !mip->mi_have_uid && !mip->mi_have_gid)
565e50e74d8SKonstantin Belousov 		return;
566e50e74d8SKonstantin Belousov 
567e50e74d8SKonstantin Belousov 	if (!norun) {
568e50e74d8SKonstantin Belousov 		if (statfs(mtpoint, &sfs) == -1) {
569e50e74d8SKonstantin Belousov 			warn("statfs: %s", mtpoint);
570e50e74d8SKonstantin Belousov 			return;
571e50e74d8SKonstantin Belousov 		}
572e50e74d8SKonstantin Belousov 		if ((sfs.f_flags & MNT_RDONLY) != 0) {
573e50e74d8SKonstantin Belousov 			if (mip->mi_forced_pw) {
574e50e74d8SKonstantin Belousov 				warnx(
575e50e74d8SKonstantin Belousov 	"Not changing mode/owner of %s since it is read-only",
576e50e74d8SKonstantin Belousov 				    mtpoint);
577e50e74d8SKonstantin Belousov 			} else {
578e50e74d8SKonstantin Belousov 				debugprintf(
579e50e74d8SKonstantin Belousov 	"Not changing mode/owner of %s since it is read-only",
580e50e74d8SKonstantin Belousov 				    mtpoint);
581e50e74d8SKonstantin Belousov 			}
582e50e74d8SKonstantin Belousov 			return;
583e50e74d8SKonstantin Belousov 		}
584e50e74d8SKonstantin Belousov 	}
5854d7de91fSDima Dorfman 
5864d7de91fSDima Dorfman 	if (mip->mi_have_mode) {
5874d7de91fSDima Dorfman 		debugprintf("changing mode of %s to %o.", mtpoint,
5884d7de91fSDima Dorfman 		    mip->mi_mode);
5894d7de91fSDima Dorfman 		if (!norun)
5904d7de91fSDima Dorfman 			if (chmod(mtpoint, mip->mi_mode) == -1)
5914d7de91fSDima Dorfman 				err(1, "chmod: %s", mtpoint);
5924d7de91fSDima Dorfman 	}
5934d7de91fSDima Dorfman 	/*
5944d7de91fSDima Dorfman 	 * We have to do these separately because the user may have
5954d7de91fSDima Dorfman 	 * only specified one of them.
5964d7de91fSDima Dorfman 	 */
5974d7de91fSDima Dorfman 	if (mip->mi_have_uid) {
5984d7de91fSDima Dorfman 		debugprintf("changing owner (user) or %s to %u.", mtpoint,
5994d7de91fSDima Dorfman 		    mip->mi_uid);
6004d7de91fSDima Dorfman 		if (!norun)
6014d7de91fSDima Dorfman 			if (chown(mtpoint, mip->mi_uid, -1) == -1)
6024d7de91fSDima Dorfman 				err(1, "chown %s to %u (user)", mtpoint,
6034d7de91fSDima Dorfman 				    mip->mi_uid);
6044d7de91fSDima Dorfman 	}
6054d7de91fSDima Dorfman 	if (mip->mi_have_gid) {
6064d7de91fSDima Dorfman 		debugprintf("changing owner (group) or %s to %u.", mtpoint,
6074d7de91fSDima Dorfman 		    mip->mi_gid);
6084d7de91fSDima Dorfman 		if (!norun)
6094d7de91fSDima Dorfman 			if (chown(mtpoint, -1, mip->mi_gid) == -1)
6104d7de91fSDima Dorfman 				err(1, "chown %s to %u (group)", mtpoint,
6114d7de91fSDima Dorfman 				    mip->mi_gid);
6124d7de91fSDima Dorfman 	}
6134d7de91fSDima Dorfman }
6144d7de91fSDima Dorfman 
6154d7de91fSDima Dorfman /*
6164d7de91fSDima Dorfman  * Put a file system on the memory disk.
6174d7de91fSDima Dorfman  */
6184d7de91fSDima Dorfman static void
do_newfs(const char * args)6194d7de91fSDima Dorfman do_newfs(const char *args)
6204d7de91fSDima Dorfman {
6214d7de91fSDima Dorfman 	int rv;
6224d7de91fSDima Dorfman 
6231386defaSGordon Tetlow 	rv = run(NULL, "%s%s /dev/%s%d", _PATH_NEWFS, args, mdname, unit);
6244d7de91fSDima Dorfman 	if (rv)
625594fb8f5SConrad Meyer 		errx(1, "newfs exited %s %d", run_exitstr(rv),
626594fb8f5SConrad Meyer 		    run_exitnumber(rv));
6274d7de91fSDima Dorfman }
6284d7de91fSDima Dorfman 
629f8526186SKyle Evans 
630f8526186SKyle Evans /*
631f8526186SKyle Evans  * Copy skel into the mountpoint.
632f8526186SKyle Evans  */
633f8526186SKyle Evans static void
do_copy(const char * mtpoint,const char * skel)634f8526186SKyle Evans do_copy(const char *mtpoint, const char *skel)
635f8526186SKyle Evans {
636f8526186SKyle Evans 	int rv;
637f8526186SKyle Evans 
638f8526186SKyle Evans 	rv = chdir(skel);
639f8526186SKyle Evans 	if (rv != 0)
640f8526186SKyle Evans 		err(1, "chdir to %s", skel);
641f8526186SKyle Evans 	rv = run(NULL, "/bin/pax -rw -pe . %s", mtpoint);
642f8526186SKyle Evans 	if (rv != 0)
643f8526186SKyle Evans 		errx(1, "skel copy failed");
644f8526186SKyle Evans }
645f8526186SKyle Evans 
6464d7de91fSDima Dorfman /*
6474d7de91fSDima Dorfman  * 'str' should be a user and group name similar to the last argument
6484d7de91fSDima Dorfman  * to chown(1); i.e., a user, followed by a colon, followed by a
6494d7de91fSDima Dorfman  * group.  The user and group in 'str' may be either a [ug]id or a
6504d7de91fSDima Dorfman  * name.  Upon return, the uid and gid fields in 'mip' will contain
6514d7de91fSDima Dorfman  * the uid and gid of the user and group name in 'str', respectively.
6524d7de91fSDima Dorfman  *
6534d7de91fSDima Dorfman  * In other words, this derives a user and group id from a string
6544d7de91fSDima Dorfman  * formatted like the last argument to chown(1).
655de90a634SRalf S. Engelschall  *
656de90a634SRalf S. Engelschall  * Notice: At this point we don't support only a username or only a
657de90a634SRalf S. Engelschall  * group name. do_mtptsetup already does, so when this feature is
658de90a634SRalf S. Engelschall  * desired, this is the only routine that needs to be changed.
6594d7de91fSDima Dorfman  */
6604d7de91fSDima Dorfman static void
extract_ugid(const char * str,struct mtpt_info * mip)6614d7de91fSDima Dorfman extract_ugid(const char *str, struct mtpt_info *mip)
6624d7de91fSDima Dorfman {
6634d7de91fSDima Dorfman 	char *ug;			/* Writable 'str'. */
6644d7de91fSDima Dorfman 	char *user, *group;		/* Result of extracton. */
6654d7de91fSDima Dorfman 	struct passwd *pw;
6664d7de91fSDima Dorfman 	struct group *gr;
6674d7de91fSDima Dorfman 	char *p;
6684d7de91fSDima Dorfman 	uid_t *uid;
6694d7de91fSDima Dorfman 	gid_t *gid;
6704d7de91fSDima Dorfman 
6714d7de91fSDima Dorfman 	uid = &mip->mi_uid;
6724d7de91fSDima Dorfman 	gid = &mip->mi_gid;
6734d7de91fSDima Dorfman 	mip->mi_have_uid = mip->mi_have_gid = false;
6744d7de91fSDima Dorfman 
6754d7de91fSDima Dorfman 	/* Extract the user and group from 'str'.  Format above. */
6768d3c1246SDima Dorfman 	ug = strdup(str);
6774d7de91fSDima Dorfman 	assert(ug != NULL);
6784d7de91fSDima Dorfman 	group = ug;
6794d7de91fSDima Dorfman 	user = strsep(&group, ":");
6804d7de91fSDima Dorfman 	if (user == NULL || group == NULL || *user == '\0' || *group == '\0')
6814d7de91fSDima Dorfman 		usage();
6824d7de91fSDima Dorfman 
6834d7de91fSDima Dorfman 	/* Derive uid. */
6844d7de91fSDima Dorfman 	*uid = strtoul(user, &p, 10);
6858a50130bSAlexander Kabaev 	if (*uid == (uid_t)ULONG_MAX)
6864d7de91fSDima Dorfman 		usage();
6874d7de91fSDima Dorfman 	if (*p != '\0') {
6884d7de91fSDima Dorfman 		pw = getpwnam(user);
6894d7de91fSDima Dorfman 		if (pw == NULL)
6904d7de91fSDima Dorfman 			errx(1, "invalid user: %s", user);
6914d7de91fSDima Dorfman 		*uid = pw->pw_uid;
6924d7de91fSDima Dorfman 	}
693de90a634SRalf S. Engelschall 	mip->mi_have_uid = true;
6944d7de91fSDima Dorfman 
6954d7de91fSDima Dorfman 	/* Derive gid. */
6964d7de91fSDima Dorfman 	*gid = strtoul(group, &p, 10);
6978a50130bSAlexander Kabaev 	if (*gid == (gid_t)ULONG_MAX)
6984d7de91fSDima Dorfman 		usage();
6994d7de91fSDima Dorfman 	if (*p != '\0') {
7004d7de91fSDima Dorfman 		gr = getgrnam(group);
7014d7de91fSDima Dorfman 		if (gr == NULL)
7024d7de91fSDima Dorfman 			errx(1, "invalid group: %s", group);
7034d7de91fSDima Dorfman 		*gid = gr->gr_gid;
7044d7de91fSDima Dorfman 	}
705de90a634SRalf S. Engelschall 	mip->mi_have_gid = true;
7064d7de91fSDima Dorfman 
7074d7de91fSDima Dorfman 	free(ug);
7084d7de91fSDima Dorfman }
7094d7de91fSDima Dorfman 
7104d7de91fSDima Dorfman /*
7114d7de91fSDima Dorfman  * Run a process with command name and arguments pointed to by the
7124d7de91fSDima Dorfman  * formatted string 'cmdline'.  Since system(3) is not used, the first
7134d7de91fSDima Dorfman  * space-delimited token of 'cmdline' must be the full pathname of the
714594fb8f5SConrad Meyer  * program to run.
715594fb8f5SConrad Meyer  *
716594fb8f5SConrad Meyer  * The return value is the return code of the process spawned, or a negative
717594fb8f5SConrad Meyer  * signal number if the process exited due to an uncaught signal.
718594fb8f5SConrad Meyer  *
719594fb8f5SConrad Meyer  * If 'ofd' is non-NULL, it is set to the standard output of
7204d7de91fSDima Dorfman  * the program spawned (i.e., you can read from ofd and get the output
7214d7de91fSDima Dorfman  * of the program).
7224d7de91fSDima Dorfman  */
7234d7de91fSDima Dorfman static int
run(int * ofd,const char * cmdline,...)7244d7de91fSDima Dorfman run(int *ofd, const char *cmdline, ...)
7254d7de91fSDima Dorfman {
72694ddc5afSDavid E. O'Brien 	char **argv, **argvp;		/* Result of splitting 'cmd'. */
72794ddc5afSDavid E. O'Brien 	int argc;
7284d7de91fSDima Dorfman 	char *cmd;			/* Expansion of 'cmdline'. */
7294d7de91fSDima Dorfman 	int pid, status;		/* Child info. */
7304d7de91fSDima Dorfman 	int pfd[2];			/* Pipe to the child. */
7314d7de91fSDima Dorfman 	int nfd;			/* Null (/dev/null) file descriptor. */
7324d7de91fSDima Dorfman 	bool dup2dn;			/* Dup /dev/null to stdout? */
7334d7de91fSDima Dorfman 	va_list ap;
7344d7de91fSDima Dorfman 	char *p;
7354d7de91fSDima Dorfman 	int rv, i;
7364d7de91fSDima Dorfman 
7374d7de91fSDima Dorfman 	dup2dn = true;
7384d7de91fSDima Dorfman 	va_start(ap, cmdline);
7394d7de91fSDima Dorfman 	rv = vasprintf(&cmd, cmdline, ap);
7404d7de91fSDima Dorfman 	if (rv == -1)
7414d7de91fSDima Dorfman 		err(1, "vasprintf");
7424d7de91fSDima Dorfman 	va_end(ap);
7434d7de91fSDima Dorfman 
74494ddc5afSDavid E. O'Brien 	/* Split up 'cmd' into 'argv' for use with execve. */
74594ddc5afSDavid E. O'Brien 	for (argc = 1, p = cmd; (p = strchr(p, ' ')) != NULL; p++)
74694ddc5afSDavid E. O'Brien 		argc++;		/* 'argc' generation loop. */
74794ddc5afSDavid E. O'Brien 	argv = (char **)malloc(sizeof(*argv) * (argc + 1));
74894ddc5afSDavid E. O'Brien 	assert(argv != NULL);
74994ddc5afSDavid E. O'Brien 	for (p = cmd, argvp = argv; (*argvp = strsep(&p, " ")) != NULL;)
7503cb8dc7cSYaroslav Tykhiy 		if (**argvp != '\0')
75194ddc5afSDavid E. O'Brien 			if (++argvp >= &argv[argc]) {
75294ddc5afSDavid E. O'Brien 				*argvp = NULL;
7534d7de91fSDima Dorfman 				break;
7544d7de91fSDima Dorfman 			}
75594ddc5afSDavid E. O'Brien 	assert(*argv);
7563cb8dc7cSYaroslav Tykhiy 	/* The argv array ends up NULL-terminated here. */
7574d7de91fSDima Dorfman 
7584d7de91fSDima Dorfman 	/* Make sure the above loop works as expected. */
7594d7de91fSDima Dorfman 	if (debug) {
7604d7de91fSDima Dorfman 		/*
7614d7de91fSDima Dorfman 		 * We can't, but should, use debugprintf here.  First,
7624d7de91fSDima Dorfman 		 * it appends a trailing newline to the output, and
7634d7de91fSDima Dorfman 		 * second it prepends "DEBUG: " to the output.  The
7644d7de91fSDima Dorfman 		 * former is a problem for this would-be first call,
7654d7de91fSDima Dorfman 		 * and the latter for the would-be call inside the
7664d7de91fSDima Dorfman 		 * loop.
7674d7de91fSDima Dorfman 		 */
7684d7de91fSDima Dorfman 		(void)fprintf(stderr, "DEBUG: running:");
7694b85a12fSUlrich Spörlein 		/* Should be equivalent to 'cmd' (before strsep, of course). */
77094ddc5afSDavid E. O'Brien 		for (i = 0; argv[i] != NULL; i++)
77194ddc5afSDavid E. O'Brien 			(void)fprintf(stderr, " %s", argv[i]);
7724d7de91fSDima Dorfman 		(void)fprintf(stderr, "\n");
7734d7de91fSDima Dorfman 	}
7744d7de91fSDima Dorfman 
7754d7de91fSDima Dorfman 	/* Create a pipe if necessary and fork the helper program. */
7764d7de91fSDima Dorfman 	if (ofd != NULL) {
7774d7de91fSDima Dorfman 		if (pipe(&pfd[0]) == -1)
7784d7de91fSDima Dorfman 			err(1, "pipe");
7794d7de91fSDima Dorfman 		*ofd = pfd[0];
7804d7de91fSDima Dorfman 		dup2dn = false;
7814d7de91fSDima Dorfman 	}
7824d7de91fSDima Dorfman 	pid = fork();
7834d7de91fSDima Dorfman 	switch (pid) {
7844d7de91fSDima Dorfman 	case 0:
7854d7de91fSDima Dorfman 		/* XXX can we call err() in here? */
7864d7de91fSDima Dorfman 		if (norun)
7874d7de91fSDima Dorfman 			_exit(0);
7884d7de91fSDima Dorfman 		if (ofd != NULL)
7894d7de91fSDima Dorfman 			if (dup2(pfd[1], STDOUT_FILENO) < 0)
7904d7de91fSDima Dorfman 				err(1, "dup2");
7914d7de91fSDima Dorfman 		if (!loudsubs) {
7924d7de91fSDima Dorfman 			nfd = open(_PATH_DEVNULL, O_RDWR);
7934d7de91fSDima Dorfman 			if (nfd == -1)
7944d7de91fSDima Dorfman 				err(1, "open: %s", _PATH_DEVNULL);
7954d7de91fSDima Dorfman 			if (dup2(nfd, STDIN_FILENO) < 0)
7964d7de91fSDima Dorfman 				err(1, "dup2");
7974d7de91fSDima Dorfman 			if (dup2dn)
7984d7de91fSDima Dorfman 				if (dup2(nfd, STDOUT_FILENO) < 0)
7994d7de91fSDima Dorfman 				   err(1, "dup2");
8004d7de91fSDima Dorfman 			if (dup2(nfd, STDERR_FILENO) < 0)
8014d7de91fSDima Dorfman 				err(1, "dup2");
8024d7de91fSDima Dorfman 		}
8034d7de91fSDima Dorfman 
80494ddc5afSDavid E. O'Brien 		(void)execv(argv[0], argv);
80594ddc5afSDavid E. O'Brien 		warn("exec: %s", argv[0]);
8064d7de91fSDima Dorfman 		_exit(-1);
8074d7de91fSDima Dorfman 	case -1:
8084d7de91fSDima Dorfman 		err(1, "fork");
8094d7de91fSDima Dorfman 	}
8104d7de91fSDima Dorfman 
8114d7de91fSDima Dorfman 	free(cmd);
81294ddc5afSDavid E. O'Brien 	free(argv);
8134d7de91fSDima Dorfman 	while (waitpid(pid, &status, 0) != pid)
8144d7de91fSDima Dorfman 		;
815594fb8f5SConrad Meyer 	if (WIFEXITED(status))
8164d7de91fSDima Dorfman 		return (WEXITSTATUS(status));
817594fb8f5SConrad Meyer 	if (WIFSIGNALED(status))
818594fb8f5SConrad Meyer 		return (-WTERMSIG(status));
819594fb8f5SConrad Meyer 	err(1, "unexpected waitpid status: 0x%x", status);
820594fb8f5SConrad Meyer }
821594fb8f5SConrad Meyer 
822594fb8f5SConrad Meyer /*
823594fb8f5SConrad Meyer  * If run() returns non-zero, provide a string explaining why.
824594fb8f5SConrad Meyer  */
825594fb8f5SConrad Meyer static const char *
run_exitstr(int rv)826594fb8f5SConrad Meyer run_exitstr(int rv)
827594fb8f5SConrad Meyer {
828594fb8f5SConrad Meyer 	if (rv > 0)
829594fb8f5SConrad Meyer 		return ("with error code");
830594fb8f5SConrad Meyer 	if (rv < 0)
831594fb8f5SConrad Meyer 		return ("with signal");
832594fb8f5SConrad Meyer 	return (NULL);
833594fb8f5SConrad Meyer }
834594fb8f5SConrad Meyer 
835594fb8f5SConrad Meyer /*
836594fb8f5SConrad Meyer  * If run returns non-zero, provide a relevant number.
837594fb8f5SConrad Meyer  */
838594fb8f5SConrad Meyer static int
run_exitnumber(int rv)839594fb8f5SConrad Meyer run_exitnumber(int rv)
840594fb8f5SConrad Meyer {
841594fb8f5SConrad Meyer 	if (rv < 0)
842594fb8f5SConrad Meyer 		return (-rv);
843594fb8f5SConrad Meyer 	return (rv);
8444d7de91fSDima Dorfman }
8454d7de91fSDima Dorfman 
8464d7de91fSDima Dorfman static void
usage(void)8474d7de91fSDima Dorfman usage(void)
8484d7de91fSDima Dorfman {
8494d7de91fSDima Dorfman 
8504d7de91fSDima Dorfman 	fprintf(stderr,
851bf6619baSKonstantin Belousov "usage: %s [-DLlMNnPStUX] [-a maxcontig] [-b block-size]\n"
852c72132f0SMatteo Riondato "\t[-c blocks-per-cylinder-group][-d max-extent-size] [-E path-mdconfig]\n"
853f8526186SKyle Evans "\t[-e maxbpg] [-F file] [-f frag-size] [-i bytes] [-k skel]\n"
854f8526186SKyle Evans "\t[-m percent-free] [-O optimization] [-o mount-options]\n"
855c72132f0SMatteo Riondato "\t[-p permissions] [-s size] [-v version] [-w user:group]\n"
856c72132f0SMatteo Riondato "\tmd-device mount-point\n", getprogname());
8574d7de91fSDima Dorfman 	exit(1);
8584d7de91fSDima Dorfman }
859