mdconfig.c revision 155807
1/*
2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
8 *
9 * $FreeBSD: head/sbin/mdconfig/mdconfig.c 155807 2006-02-18 11:40:24Z pjd $
10 *
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <errno.h>
16#include <fcntl.h>
17#include <unistd.h>
18#include <inttypes.h>
19#include <libutil.h>
20#include <string.h>
21#include <err.h>
22#include <assert.h>
23
24#include <sys/ioctl.h>
25#include <sys/param.h>
26#include <sys/module.h>
27#include <sys/linker.h>
28#include <sys/mdioctl.h>
29#include <sys/stat.h>
30
31int	 list(const int);
32int	 query(const int, const int);
33void	 usage(void);
34
35struct md_ioctl mdio;
36
37enum {UNSET, ATTACH, DETACH, LIST} action = UNSET;
38
39int nflag;
40
41void
42usage()
43{
44	fprintf(stderr,
45"usage: mdconfig -a -t type [-n] [-o [no]option] ... [-f file]\n"
46"                [-s size] [-S sectorsize] [-u unit]\n"
47"                [-x sectors/track] [-y heads/cyl]\n"
48"       mdconfig -d -u unit\n"
49"       mdconfig -l [-n] [-u unit]\n");
50	fprintf(stderr, "\t\ttype = {malloc, preload, vnode, swap}\n");
51	fprintf(stderr, "\t\toption = {cluster, compress, reserve}\n");
52	fprintf(stderr, "\t\tsize = %%d (512 byte blocks), %%db (B),\n");
53	fprintf(stderr, "\t\t       %%dk (kB), %%dm (MB), %%dg (GB) or\n");
54	fprintf(stderr, "\t\t       %%dt (TB)\n");
55	exit(1);
56}
57
58int
59main(int argc, char **argv)
60{
61	int ch, fd, i;
62	char *p;
63	int cmdline = 0;
64
65	bzero(&mdio, sizeof(mdio));
66	mdio.md_file = malloc(PATH_MAX);
67	if (mdio.md_file == NULL)
68		err(1, "could not allocate memory");
69	bzero(mdio.md_file, PATH_MAX);
70	for (;;) {
71		ch = getopt(argc, argv, "ab:df:lno:s:S:t:u:x:y:");
72		if (ch == -1)
73			break;
74		switch (ch) {
75		case 'a':
76			if (cmdline != 0)
77				usage();
78			action = ATTACH;
79			cmdline = 1;
80			break;
81		case 'd':
82			if (cmdline != 0)
83				usage();
84			action = DETACH;
85			mdio.md_options = MD_AUTOUNIT;
86			cmdline = 3;
87			break;
88		case 'l':
89			if (cmdline != 0)
90				usage();
91			action = LIST;
92			mdio.md_options = MD_AUTOUNIT;
93			cmdline = 3;
94			break;
95		case 'n':
96			nflag = 1;
97			break;
98		case 't':
99			if (cmdline != 1)
100				usage();
101			if (!strcmp(optarg, "malloc")) {
102				mdio.md_type = MD_MALLOC;
103				mdio.md_options = MD_AUTOUNIT | MD_COMPRESS;
104			} else if (!strcmp(optarg, "preload")) {
105				mdio.md_type = MD_PRELOAD;
106				mdio.md_options = 0;
107			} else if (!strcmp(optarg, "vnode")) {
108				mdio.md_type = MD_VNODE;
109				mdio.md_options = MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
110			} else if (!strcmp(optarg, "swap")) {
111				mdio.md_type = MD_SWAP;
112				mdio.md_options = MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
113			} else {
114				usage();
115			}
116			cmdline=2;
117			break;
118		case 'f':
119			if (cmdline != 1 && cmdline != 2)
120				usage();
121			if (cmdline == 1) {
122				/* Imply ``-t vnode'' */
123				mdio.md_type = MD_VNODE;
124				mdio.md_options = MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
125				cmdline = 2;
126			}
127			if (realpath(optarg, mdio.md_file) == NULL) {
128				err(1, "could not find full path for %s",
129				    optarg);
130			}
131			fd = open(mdio.md_file, O_RDONLY);
132			if (fd < 0)
133				err(1, "could not open %s", optarg);
134			else if (mdio.md_mediasize == 0) {
135				struct stat sb;
136
137				if (fstat(fd, &sb) == -1)
138					err(1, "could not stat %s", optarg);
139				mdio.md_mediasize = sb.st_size;
140			}
141			close(fd);
142			break;
143		case 'o':
144			if (cmdline != 2)
145				usage();
146			if (!strcmp(optarg, "async"))
147				mdio.md_options |= MD_ASYNC;
148			else if (!strcmp(optarg, "noasync"))
149				mdio.md_options &= ~MD_ASYNC;
150			else if (!strcmp(optarg, "cluster"))
151				mdio.md_options |= MD_CLUSTER;
152			else if (!strcmp(optarg, "nocluster"))
153				mdio.md_options &= ~MD_CLUSTER;
154			else if (!strcmp(optarg, "compress"))
155				mdio.md_options |= MD_COMPRESS;
156			else if (!strcmp(optarg, "nocompress"))
157				mdio.md_options &= ~MD_COMPRESS;
158			else if (!strcmp(optarg, "force"))
159				mdio.md_options |= MD_FORCE;
160			else if (!strcmp(optarg, "noforce"))
161				mdio.md_options &= ~MD_FORCE;
162			else if (!strcmp(optarg, "readonly"))
163				mdio.md_options |= MD_READONLY;
164			else if (!strcmp(optarg, "noreadonly"))
165				mdio.md_options &= ~MD_READONLY;
166			else if (!strcmp(optarg, "reserve"))
167				mdio.md_options |= MD_RESERVE;
168			else if (!strcmp(optarg, "noreserve"))
169				mdio.md_options &= ~MD_RESERVE;
170			else
171				errx(1, "Unknown option: %s.", optarg);
172			break;
173		case 'S':
174			if (cmdline != 2)
175				usage();
176			mdio.md_sectorsize = strtoul(optarg, &p, 0);
177			break;
178		case 's':
179			if (cmdline != 2)
180				usage();
181			mdio.md_mediasize = (off_t)strtoumax(optarg, &p, 0);
182			if (p == NULL || *p == '\0')
183				mdio.md_mediasize *= DEV_BSIZE;
184			else if (*p == 'b' || *p == 'B')
185				; /* do nothing */
186			else if (*p == 'k' || *p == 'K')
187				mdio.md_mediasize <<= 10;
188			else if (*p == 'm' || *p == 'M')
189				mdio.md_mediasize <<= 20;
190			else if (*p == 'g' || *p == 'G')
191				mdio.md_mediasize <<= 30;
192			else if (*p == 't' || *p == 'T') {
193				mdio.md_mediasize <<= 30;
194				mdio.md_mediasize <<= 10;
195			} else
196				errx(1, "Unknown suffix on -s argument");
197			break;
198		case 'u':
199			if (cmdline != 2 && cmdline != 3)
200				usage();
201			if (!strncmp(optarg, "/dev/", 5))
202				optarg += 5;
203			if (!strncmp(optarg, MD_NAME, sizeof(MD_NAME) - 1))
204				optarg += sizeof(MD_NAME) - 1;
205			mdio.md_unit = strtoul(optarg, &p, 0);
206			if (mdio.md_unit == (unsigned)ULONG_MAX || *p != '\0')
207				errx(1, "bad unit: %s", optarg);
208			mdio.md_options &= ~MD_AUTOUNIT;
209			break;
210		case 'x':
211			if (cmdline != 2)
212				usage();
213			mdio.md_fwsectors = strtoul(optarg, &p, 0);
214			break;
215		case 'y':
216			if (cmdline != 2)
217				usage();
218			mdio.md_fwheads = strtoul(optarg, &p, 0);
219			break;
220		default:
221			usage();
222		}
223	}
224	mdio.md_version = MDIOVERSION;
225
226	if (!kld_isloaded("g_md") && kld_load("geom_md") == -1)
227		err(1, "failed to load geom_md module");
228
229	fd = open("/dev/" MDCTL_NAME, O_RDWR, 0);
230	if (fd < 0)
231		err(1, "open(/dev/%s)", MDCTL_NAME);
232	if (cmdline == 2
233	    && (mdio.md_type == MD_MALLOC || mdio.md_type == MD_SWAP))
234		if (mdio.md_mediasize == 0)
235			errx(1, "must specify -s for -t malloc or -t swap");
236	if (cmdline == 2 && mdio.md_type == MD_VNODE)
237		if (mdio.md_file[0] == '\0')
238			errx(1, "must specify -f for -t vnode");
239	if (mdio.md_type == MD_VNODE &&
240	    (mdio.md_options & MD_READONLY) == 0) {
241		if (access(mdio.md_file, W_OK) < 0 &&
242		    (errno == EACCES || errno == EPERM || errno == EROFS)) {
243			fprintf(stderr,
244			    "WARNING: opening backing store: %s readonly\n",
245			    mdio.md_file);
246			mdio.md_options |= MD_READONLY;
247		}
248	}
249	if (action == LIST) {
250		if (mdio.md_options & MD_AUTOUNIT)
251			list(fd);
252		else
253			query(fd, mdio.md_unit);
254	} else if (action == ATTACH) {
255		if (cmdline < 2)
256			usage();
257		i = ioctl(fd, MDIOCATTACH, &mdio);
258		if (i < 0)
259			err(1, "ioctl(/dev/%s)", MDCTL_NAME);
260		if (mdio.md_options & MD_AUTOUNIT)
261			printf("%s%d\n", nflag ? "" : MD_NAME, mdio.md_unit);
262	} else if (action == DETACH) {
263		if (mdio.md_options & MD_AUTOUNIT)
264			usage();
265		i = ioctl(fd, MDIOCDETACH, &mdio);
266		if (i < 0)
267			err(1, "ioctl(/dev/%s)", MDCTL_NAME);
268	} else
269		usage();
270	close (fd);
271	return (0);
272}
273
274static int
275mdunitcmp(const void *a, const void *b)
276{
277	return (*(int *)a - *(int *)b);
278}
279
280int
281list(const int fd)
282{
283	int unit;
284	int mdcount;
285
286	if (ioctl(fd, MDIOCLIST, &mdio) < 0)
287		err(1, "ioctl(/dev/%s)", MDCTL_NAME);
288	mdcount = mdio.md_pad[0];
289	assert(mdcount < MDNPAD - 1);
290	if (mdcount > 0)
291		qsort(&mdio.md_pad[1], mdcount, sizeof(mdio.md_pad[0]), mdunitcmp);
292	for (unit = 0; unit < mdcount; unit++) {
293		printf("%s%s%d", unit > 0 ? " " : "",
294		    nflag ? "" : MD_NAME, mdio.md_pad[unit + 1]);
295	}
296	if (unit > 0)
297		printf("\n");
298	return (0);
299}
300
301static void
302prthumanval(int64_t bytes)
303{
304	char buf[6];
305
306	humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1),
307	    bytes, "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
308	(void)printf("%6s", buf);
309}
310
311int
312query(const int fd, const int unit)
313{
314
315	mdio.md_version = MDIOVERSION;
316	mdio.md_unit = unit;
317
318	if (ioctl(fd, MDIOCQUERY, &mdio) < 0)
319		err(1, "ioctl(/dev/%s)", MDCTL_NAME);
320
321	(void)printf("%s%d\t", MD_NAME, mdio.md_unit);
322	switch (mdio.md_type) {
323	case MD_MALLOC:
324		(void)printf("malloc");
325		break;
326	case MD_PRELOAD:
327		(void)printf("preload");
328		break;
329	case MD_SWAP:
330		(void)printf("swap");
331		break;
332	case MD_VNODE:
333		(void)printf("vnode");
334		break;
335	}
336	printf("\t");
337	prthumanval(mdio.md_mediasize);
338	if (mdio.md_type == MD_VNODE)
339		printf("\t%s", mdio.md_file);
340	printf("\n");
341
342	return (0);
343}
344