xref: /freebsd/sbin/mdconfig/mdconfig.c (revision a10cee30c94cf5944826d2a495e9cdf339dfbcc8)
1 /*-
2  * Copyright (c) 2000-2004 Poul-Henning Kamp <phk@FreeBSD.org>
3  * Copyright (c) 2012 The FreeBSD Foundation
4  * All rights reserved.
5  *
6  * Portions of this software were developed by Edward Tomasz Napierala
7  * under sponsorship from the FreeBSD Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $FreeBSD$
31  */
32 
33 #include <sys/param.h>
34 #include <sys/devicestat.h>
35 #include <sys/ioctl.h>
36 #include <sys/linker.h>
37 #include <sys/mdioctl.h>
38 #include <sys/module.h>
39 #include <sys/resource.h>
40 #include <sys/stat.h>
41 
42 #include <assert.h>
43 #include <devstat.h>
44 #include <err.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <inttypes.h>
48 #include <libgeom.h>
49 #include <libutil.h>
50 #include <stdarg.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55 
56 static struct md_ioctl mdio;
57 static enum {UNSET, ATTACH, DETACH, RESIZE, LIST} action = UNSET;
58 static int nflag;
59 
60 static void usage(void);
61 static void md_set_file(const char *);
62 static int md_find(char *, const char *);
63 static int md_query(char *name);
64 static int md_list(char *units, int opt);
65 static char *geom_config_get(struct gconf *g, const char *name);
66 static void md_prthumanval(char *length);
67 
68 #define OPT_VERBOSE	0x01
69 #define OPT_UNIT	0x02
70 #define OPT_DONE	0x04
71 #define OPT_LIST	0x10
72 
73 #define CLASS_NAME_MD	"MD"
74 
75 static void
76 usage(void)
77 {
78 
79 	fprintf(stderr,
80 "usage: mdconfig -a -t type [-n] [-o [no]option] ... [-f file]\n"
81 "                [-s size] [-S sectorsize] [-u unit]\n"
82 "                [-x sectors/track] [-y heads/cylinder]\n"
83 "       mdconfig -d -u unit [-o [no]force]\n"
84 "       mdconfig -r -u unit -s size [-o [no]force]\n"
85 "       mdconfig -l [-v] [-n] [-u unit]\n"
86 "       mdconfig file\n");
87 	fprintf(stderr, "\t\ttype = {malloc, preload, vnode, swap}\n");
88 	fprintf(stderr, "\t\toption = {cluster, compress, reserve}\n");
89 	fprintf(stderr, "\t\tsize = %%d (512 byte blocks), %%db (B),\n");
90 	fprintf(stderr, "\t\t       %%dk (kB), %%dm (MB), %%dg (GB) or\n");
91 	fprintf(stderr, "\t\t       %%dt (TB)\n");
92 	exit(1);
93 }
94 
95 int
96 main(int argc, char **argv)
97 {
98 	int ch, fd, i, vflag;
99 	char *p;
100 	char *fflag = NULL, *sflag = NULL, *tflag = NULL, *uflag = NULL;
101 
102 	bzero(&mdio, sizeof(mdio));
103 	mdio.md_file = malloc(PATH_MAX);
104 	if (mdio.md_file == NULL)
105 		err(1, "could not allocate memory");
106 	vflag = 0;
107 	bzero(mdio.md_file, PATH_MAX);
108 
109 	if (argc == 1)
110 		usage();
111 
112 	while ((ch = getopt(argc, argv, "ab:df:lno:rs:S:t:u:vx:y:")) != -1) {
113 		switch (ch) {
114 		case 'a':
115 			if (action != UNSET && action != ATTACH)
116 				errx(1, "-a is mutually exclusive "
117 				    "with -d, -r, and -l");
118 			action = ATTACH;
119 			break;
120 		case 'd':
121 			if (action != UNSET && action != DETACH)
122 				errx(1, "-d is mutually exclusive "
123 				    "with -a, -r, and -l");
124 			action = DETACH;
125 			mdio.md_options |= MD_AUTOUNIT;
126 			break;
127 		case 'r':
128 			if (action != UNSET && action != RESIZE)
129 				errx(1, "-r is mutually exclusive "
130 				    "with -a, -d, and -l");
131 			action = RESIZE;
132 			mdio.md_options |= MD_AUTOUNIT;
133 			break;
134 		case 'l':
135 			if (action != UNSET && action != LIST)
136 				errx(1, "-l is mutually exclusive "
137 				    "with -a, -r, and -d");
138 			action = LIST;
139 			mdio.md_options |= MD_AUTOUNIT;
140 			break;
141 		case 'n':
142 			nflag = 1;
143 			break;
144 		case 't':
145 			if (tflag != NULL)
146 				errx(1, "-t can be passed only once");
147 			tflag = optarg;
148 			if (!strcmp(optarg, "malloc")) {
149 				mdio.md_type = MD_MALLOC;
150 				mdio.md_options |= MD_AUTOUNIT | MD_COMPRESS;
151 			} else if (!strcmp(optarg, "preload")) {
152 				mdio.md_type = MD_PRELOAD;
153 			} else if (!strcmp(optarg, "vnode")) {
154 				mdio.md_type = MD_VNODE;
155 				mdio.md_options |= MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
156 			} else if (!strcmp(optarg, "swap")) {
157 				mdio.md_type = MD_SWAP;
158 				mdio.md_options |= MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
159 			} else
160 				errx(1, "unknown type: %s", optarg);
161 			break;
162 		case 'f':
163 			if (fflag != NULL)
164 				errx(1, "-f can be passed only once");
165 			fflag = optarg;
166 			break;
167 		case 'o':
168 			if (!strcmp(optarg, "async"))
169 				mdio.md_options |= MD_ASYNC;
170 			else if (!strcmp(optarg, "noasync"))
171 				mdio.md_options &= ~MD_ASYNC;
172 			else if (!strcmp(optarg, "cluster"))
173 				mdio.md_options |= MD_CLUSTER;
174 			else if (!strcmp(optarg, "nocluster"))
175 				mdio.md_options &= ~MD_CLUSTER;
176 			else if (!strcmp(optarg, "compress"))
177 				mdio.md_options |= MD_COMPRESS;
178 			else if (!strcmp(optarg, "nocompress"))
179 				mdio.md_options &= ~MD_COMPRESS;
180 			else if (!strcmp(optarg, "force"))
181 				mdio.md_options |= MD_FORCE;
182 			else if (!strcmp(optarg, "noforce"))
183 				mdio.md_options &= ~MD_FORCE;
184 			else if (!strcmp(optarg, "readonly"))
185 				mdio.md_options |= MD_READONLY;
186 			else if (!strcmp(optarg, "noreadonly"))
187 				mdio.md_options &= ~MD_READONLY;
188 			else if (!strcmp(optarg, "reserve"))
189 				mdio.md_options |= MD_RESERVE;
190 			else if (!strcmp(optarg, "noreserve"))
191 				mdio.md_options &= ~MD_RESERVE;
192 			else
193 				errx(1, "unknown option: %s", optarg);
194 			break;
195 		case 'S':
196 			mdio.md_sectorsize = strtoul(optarg, &p, 0);
197 			break;
198 		case 's':
199 			if (sflag != NULL)
200 				errx(1, "-s can be passed only once");
201 			sflag = optarg;
202 			mdio.md_mediasize = (off_t)strtoumax(optarg, &p, 0);
203 			if (p == NULL || *p == '\0')
204 				mdio.md_mediasize *= DEV_BSIZE;
205 			else if (*p == 'b' || *p == 'B')
206 				; /* do nothing */
207 			else if (*p == 'k' || *p == 'K')
208 				mdio.md_mediasize <<= 10;
209 			else if (*p == 'm' || *p == 'M')
210 				mdio.md_mediasize <<= 20;
211 			else if (*p == 'g' || *p == 'G')
212 				mdio.md_mediasize <<= 30;
213 			else if (*p == 't' || *p == 'T') {
214 				mdio.md_mediasize <<= 30;
215 				mdio.md_mediasize <<= 10;
216 			} else
217 				errx(1, "unknown suffix on -s argument");
218 			break;
219 		case 'u':
220 			if (!strncmp(optarg, "/dev/", 5))
221 				optarg += 5;
222 			if (!strncmp(optarg, MD_NAME, sizeof(MD_NAME) - 1))
223 				optarg += sizeof(MD_NAME) - 1;
224 			uflag = optarg;
225 			break;
226 		case 'v':
227 			vflag = OPT_VERBOSE;
228 			break;
229 		case 'x':
230 			mdio.md_fwsectors = strtoul(optarg, &p, 0);
231 			break;
232 		case 'y':
233 			mdio.md_fwheads = strtoul(optarg, &p, 0);
234 			break;
235 		default:
236 			usage();
237 		}
238 	}
239 
240 	argc -= optind;
241 	argv += optind;
242 
243 	if (action == UNSET)
244 		action = ATTACH;
245 
246 	if (action == ATTACH) {
247 		if (tflag == NULL) {
248 			/*
249 			 * Try to infer the type based on other arguments.
250 			 */
251 			if (fflag != NULL || argc > 0) {
252 				/* Imply ``-t vnode'' */
253 				mdio.md_type = MD_VNODE;
254 				mdio.md_options |= MD_CLUSTER | MD_AUTOUNIT |
255 				    MD_COMPRESS;
256 			} else if (sflag != NULL) {
257 				/* Imply ``-t swap'' */
258 				mdio.md_type = MD_SWAP;
259 				mdio.md_options |= MD_CLUSTER | MD_AUTOUNIT |
260 				    MD_COMPRESS;
261 			} else
262 				errx(1, "unable to determine type");
263 		}
264 
265 		if ((fflag != NULL || argc > 0) && mdio.md_type != MD_VNODE)
266 			errx(1, "only -t vnode can be used with file name");
267 
268 		if (mdio.md_type == MD_VNODE) {
269 			if (fflag != NULL) {
270 				if (argc != 0)
271 					usage();
272 				md_set_file(fflag);
273 			} else {
274 				if (argc != 1)
275 					usage();
276 				md_set_file(*argv);
277 			}
278 
279 			if ((mdio.md_options & MD_READONLY) == 0 &&
280 			    access(mdio.md_file, W_OK) < 0 &&
281 			    (errno == EACCES || errno == EPERM ||
282 			     errno == EROFS)) {
283 				warnx("WARNING: opening backing store: %s "
284 				    "readonly", mdio.md_file);
285 				mdio.md_options |= MD_READONLY;
286 			}
287 		}
288 
289 		if ((mdio.md_type == MD_MALLOC || mdio.md_type == MD_SWAP) &&
290 		    sflag == NULL)
291 			errx(1, "must specify -s for -t malloc or -t swap");
292 		if (mdio.md_type == MD_VNODE && mdio.md_file[0] == '\0')
293 			errx(1, "must specify -f for -t vnode");
294 	} else {
295 		if (mdio.md_sectorsize != 0)
296 			errx(1, "-S can only be used with -a");
297 		if (action != RESIZE && sflag != NULL)
298 			errx(1, "-s can only be used with -a and -r");
299 		if (mdio.md_fwsectors != 0)
300 			errx(1, "-x can only be used with -a");
301 		if (mdio.md_fwheads != 0)
302 			errx(1, "-y can only be used with -a");
303 		if (fflag != NULL)
304 			errx(1, "-f can only be used with -a");
305 		if (tflag != NULL)
306 			errx(1, "-t can only be used with -a");
307 		if (argc > 0)
308 			errx(1, "file can only be used with -a");
309 		if ((action != DETACH && action != RESIZE) &&
310 		    (mdio.md_options & ~MD_AUTOUNIT) != 0)
311 			errx(1, "-o can only be used with -a, -d, and -r");
312 		if (action == DETACH &&
313 		    (mdio.md_options & ~(MD_FORCE | MD_AUTOUNIT)) != 0)
314 			errx(1, "only -o [no]force can be used with -d");
315 		if (action == RESIZE &&
316 		    (mdio.md_options & ~(MD_FORCE | MD_RESERVE | MD_AUTOUNIT)) != 0)
317 			errx(1, "only -o [no]force and -o [no]reserve can be used with -r");
318 	}
319 
320 	if (action == RESIZE && sflag == NULL)
321 		errx(1, "must specify -s for -r");
322 
323 	if (action != LIST && vflag == OPT_VERBOSE)
324 		errx(1, "-v can only be used with -l");
325 
326 	if (uflag != NULL) {
327 		mdio.md_unit = strtoul(uflag, &p, 0);
328 		if (mdio.md_unit == (unsigned)ULONG_MAX || *p != '\0')
329 			errx(1, "bad unit: %s", uflag);
330 		mdio.md_options &= ~MD_AUTOUNIT;
331 	}
332 
333 	mdio.md_version = MDIOVERSION;
334 
335 	if (!kld_isloaded("g_md") && kld_load("geom_md") == -1)
336 		err(1, "failed to load geom_md module");
337 
338 	fd = open("/dev/" MDCTL_NAME, O_RDWR, 0);
339 	if (fd < 0)
340 		err(1, "open(/dev/%s)", MDCTL_NAME);
341 
342 	if (action == ATTACH) {
343 		i = ioctl(fd, MDIOCATTACH, &mdio);
344 		if (i < 0)
345 			err(1, "ioctl(/dev/%s)", MDCTL_NAME);
346 		if (mdio.md_options & MD_AUTOUNIT)
347 			printf("%s%d\n", nflag ? "" : MD_NAME, mdio.md_unit);
348 	} else if (action == DETACH) {
349 		if (mdio.md_options & MD_AUTOUNIT)
350 			errx(1, "-d requires -u");
351 		i = ioctl(fd, MDIOCDETACH, &mdio);
352 		if (i < 0)
353 			err(1, "ioctl(/dev/%s)", MDCTL_NAME);
354 	} else if (action == RESIZE) {
355 		if (mdio.md_options & MD_AUTOUNIT)
356 			errx(1, "-r requires -u");
357 		i = ioctl(fd, MDIOCRESIZE, &mdio);
358 		if (i < 0)
359 			err(1, "ioctl(/dev/%s)", MDCTL_NAME);
360 	} else if (action == LIST) {
361 		if (mdio.md_options & MD_AUTOUNIT) {
362 			/*
363 			 * Listing all devices. This is why we pass NULL
364 			 * together with OPT_LIST.
365 			 */
366 			md_list(NULL, OPT_LIST | vflag);
367 		} else
368 			return (md_query(uflag));
369 	} else
370 		usage();
371 	close(fd);
372 	return (0);
373 }
374 
375 static void
376 md_set_file(const char *fn)
377 {
378 	struct stat sb;
379 	int fd;
380 
381 	if (realpath(fn, mdio.md_file) == NULL)
382 		err(1, "could not find full path for %s", fn);
383 	fd = open(mdio.md_file, O_RDONLY);
384 	if (fd < 0)
385 		err(1, "could not open %s", fn);
386 	if (fstat(fd, &sb) == -1)
387 		err(1, "could not stat %s", fn);
388 	if (!S_ISREG(sb.st_mode))
389 		errx(1, "%s is not a regular file", fn);
390 	if (mdio.md_mediasize == 0)
391 		mdio.md_mediasize = sb.st_size;
392 	close(fd);
393 }
394 
395 /*
396  * Lists md(4) disks. Is used also as a query routine, since it handles XML
397  * interface. 'units' can be NULL for listing memory disks. It might be
398  * coma-separated string containing md(4) disk names. 'opt' distinguished
399  * between list and query mode.
400  */
401 static int
402 md_list(char *units, int opt)
403 {
404 	struct gmesh gm;
405 	struct gprovider *pp;
406 	struct gconf *gc;
407 	struct gident *gid;
408 	struct devstat *gsp;
409 	struct ggeom *gg;
410 	struct gclass *gcl;
411 	void *sq;
412 	int retcode, found;
413 	char *type, *file, *length;
414 
415 	type = file = length = NULL;
416 
417 	retcode = geom_gettree(&gm);
418 	if (retcode != 0)
419 		return (-1);
420 	retcode = geom_stats_open();
421 	if (retcode != 0)
422 		return (-1);
423 	sq = geom_stats_snapshot_get();
424 	if (sq == NULL)
425 		return (-1);
426 
427 	found = 0;
428 	while ((gsp = geom_stats_snapshot_next(sq)) != NULL) {
429 		gid = geom_lookupid(&gm, gsp->id);
430 		if (gid == NULL)
431 			continue;
432 		if (gid->lg_what == ISPROVIDER) {
433 			pp = gid->lg_ptr;
434 			gg = pp->lg_geom;
435 			gcl = gg->lg_class;
436 			if (strcmp(gcl->lg_name, CLASS_NAME_MD) != 0)
437 				continue;
438 			if ((opt & OPT_UNIT) && (units != NULL)) {
439 				retcode = md_find(units, pp->lg_name);
440 				if (retcode != 1)
441 					continue;
442 				else
443 					found = 1;
444 			}
445 			gc = &pp->lg_config;
446 			if (nflag && strncmp(pp->lg_name, "md", 2) == 0)
447 				printf("%s", pp->lg_name + 2);
448 			else
449 				printf("%s", pp->lg_name);
450 
451 			if (opt & OPT_VERBOSE || opt & OPT_UNIT) {
452 				type = geom_config_get(gc, "type");
453 				if (strcmp(type, "vnode") == 0)
454 					file = geom_config_get(gc, "file");
455 				length = geom_config_get(gc, "length");
456 				printf("\t%s\t", type);
457 				if (length != NULL)
458 					md_prthumanval(length);
459 				if (file != NULL) {
460 					printf("\t%s", file);
461 					file = NULL;
462 				}
463 			}
464 			opt |= OPT_DONE;
465 			if ((opt & OPT_LIST) && !(opt & OPT_VERBOSE))
466 				printf(" ");
467 			else
468 				printf("\n");
469 		}
470 	}
471 	if ((opt & OPT_LIST) && (opt & OPT_DONE) && !(opt & OPT_VERBOSE))
472 		printf("\n");
473 	/* XXX: Check if it's enough to clean everything. */
474 	geom_stats_snapshot_free(sq);
475 	if ((opt & OPT_UNIT) && found)
476 		return (0);
477 	else
478 		return (-1);
479 }
480 
481 /*
482  * Returns value of 'name' from gconfig structure.
483  */
484 static char *
485 geom_config_get(struct gconf *g, const char *name)
486 {
487 	struct gconfig *gce;
488 
489 	LIST_FOREACH(gce, g, lg_config) {
490 		if (strcmp(gce->lg_name, name) == 0)
491 			return (gce->lg_val);
492 	}
493 	return (NULL);
494 }
495 
496 /*
497  * List is comma separated list of MD disks. name is a
498  * device name we look for.  Returns 1 if found and 0
499  * otherwise.
500  */
501 static int
502 md_find(char *list, const char *name)
503 {
504 	int ret;
505 	char num[16];
506 	char *ptr, *p, *u;
507 
508 	ret = 0;
509 	ptr = strdup(list);
510 	if (ptr == NULL)
511 		return (-1);
512 	for (p = ptr; (u = strsep(&p, ",")) != NULL;) {
513 		if (strncmp(u, "/dev/", 5) == 0)
514 			u += 5;
515 		/* Just in case user specified number instead of full name */
516 		snprintf(num, sizeof(num), "md%s", u);
517 		if (strcmp(u, name) == 0 || strcmp(num, name) == 0) {
518 			ret = 1;
519 			break;
520 		}
521 	}
522 	free(ptr);
523 	return (ret);
524 }
525 
526 static void
527 md_prthumanval(char *length)
528 {
529 	char buf[6];
530 	uintmax_t bytes;
531 	char *endptr;
532 
533 	errno = 0;
534 	bytes = strtoumax(length, &endptr, 10);
535 	if (errno != 0 || *endptr != '\0' || bytes > INT64_MAX)
536 		return;
537 	humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
538 	    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
539 	(void)printf("%6s", buf);
540 }
541 
542 static int
543 md_query(char *name)
544 {
545 
546 	return (md_list(name, OPT_UNIT));
547 }
548