xref: /freebsd/sbin/mdconfig/mdconfig.c (revision c6ec7d31830ab1c80edae95ad5e4b9dba10c47ac)
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, 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, "vnode")) {
152 				mdio.md_type = MD_VNODE;
153 				mdio.md_options |= MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
154 			} else if (!strcmp(optarg, "swap")) {
155 				mdio.md_type = MD_SWAP;
156 				mdio.md_options |= MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
157 			} else
158 				errx(1, "unknown type: %s", optarg);
159 			break;
160 		case 'f':
161 			if (fflag != NULL)
162 				errx(1, "-f can be passed only once");
163 			fflag = optarg;
164 			break;
165 		case 'o':
166 			if (!strcmp(optarg, "async"))
167 				mdio.md_options |= MD_ASYNC;
168 			else if (!strcmp(optarg, "noasync"))
169 				mdio.md_options &= ~MD_ASYNC;
170 			else if (!strcmp(optarg, "cluster"))
171 				mdio.md_options |= MD_CLUSTER;
172 			else if (!strcmp(optarg, "nocluster"))
173 				mdio.md_options &= ~MD_CLUSTER;
174 			else if (!strcmp(optarg, "compress"))
175 				mdio.md_options |= MD_COMPRESS;
176 			else if (!strcmp(optarg, "nocompress"))
177 				mdio.md_options &= ~MD_COMPRESS;
178 			else if (!strcmp(optarg, "force"))
179 				mdio.md_options |= MD_FORCE;
180 			else if (!strcmp(optarg, "noforce"))
181 				mdio.md_options &= ~MD_FORCE;
182 			else if (!strcmp(optarg, "readonly"))
183 				mdio.md_options |= MD_READONLY;
184 			else if (!strcmp(optarg, "noreadonly"))
185 				mdio.md_options &= ~MD_READONLY;
186 			else if (!strcmp(optarg, "reserve"))
187 				mdio.md_options |= MD_RESERVE;
188 			else if (!strcmp(optarg, "noreserve"))
189 				mdio.md_options &= ~MD_RESERVE;
190 			else
191 				errx(1, "unknown option: %s", optarg);
192 			break;
193 		case 'S':
194 			mdio.md_sectorsize = strtoul(optarg, &p, 0);
195 			break;
196 		case 's':
197 			if (sflag != NULL)
198 				errx(1, "-s can be passed only once");
199 			sflag = optarg;
200 			mdio.md_mediasize = (off_t)strtoumax(optarg, &p, 0);
201 			if (p == NULL || *p == '\0')
202 				mdio.md_mediasize *= DEV_BSIZE;
203 			else if (*p == 'b' || *p == 'B')
204 				; /* do nothing */
205 			else if (*p == 'k' || *p == 'K')
206 				mdio.md_mediasize <<= 10;
207 			else if (*p == 'm' || *p == 'M')
208 				mdio.md_mediasize <<= 20;
209 			else if (*p == 'g' || *p == 'G')
210 				mdio.md_mediasize <<= 30;
211 			else if (*p == 't' || *p == 'T') {
212 				mdio.md_mediasize <<= 30;
213 				mdio.md_mediasize <<= 10;
214 			} else
215 				errx(1, "unknown suffix on -s argument");
216 			break;
217 		case 'u':
218 			if (!strncmp(optarg, "/dev/", 5))
219 				optarg += 5;
220 			if (!strncmp(optarg, MD_NAME, sizeof(MD_NAME) - 1))
221 				optarg += sizeof(MD_NAME) - 1;
222 			uflag = optarg;
223 			break;
224 		case 'v':
225 			vflag = OPT_VERBOSE;
226 			break;
227 		case 'x':
228 			mdio.md_fwsectors = strtoul(optarg, &p, 0);
229 			break;
230 		case 'y':
231 			mdio.md_fwheads = strtoul(optarg, &p, 0);
232 			break;
233 		default:
234 			usage();
235 		}
236 	}
237 
238 	argc -= optind;
239 	argv += optind;
240 
241 	if (action == UNSET)
242 		action = ATTACH;
243 
244 	if (action == ATTACH) {
245 		if (tflag == NULL) {
246 			/*
247 			 * Try to infer the type based on other arguments.
248 			 */
249 			if (fflag != NULL || argc > 0) {
250 				/* Imply ``-t vnode'' */
251 				mdio.md_type = MD_VNODE;
252 				mdio.md_options |= MD_CLUSTER | MD_AUTOUNIT |
253 				    MD_COMPRESS;
254 			} else if (sflag != NULL) {
255 				/* Imply ``-t swap'' */
256 				mdio.md_type = MD_SWAP;
257 				mdio.md_options |= MD_CLUSTER | MD_AUTOUNIT |
258 				    MD_COMPRESS;
259 			} else
260 				errx(1, "unable to determine type");
261 		}
262 
263 		if ((fflag != NULL || argc > 0) && mdio.md_type != MD_VNODE)
264 			errx(1, "only -t vnode can be used with file name");
265 
266 		if (mdio.md_type == MD_VNODE) {
267 			if (fflag != NULL) {
268 				if (argc != 0)
269 					usage();
270 				md_set_file(fflag);
271 			} else {
272 				if (argc != 1)
273 					usage();
274 				md_set_file(*argv);
275 			}
276 
277 			if ((mdio.md_options & MD_READONLY) == 0 &&
278 			    access(mdio.md_file, W_OK) < 0 &&
279 			    (errno == EACCES || errno == EPERM ||
280 			     errno == EROFS)) {
281 				warnx("WARNING: opening backing store: %s "
282 				    "readonly", mdio.md_file);
283 				mdio.md_options |= MD_READONLY;
284 			}
285 		}
286 
287 		if ((mdio.md_type == MD_MALLOC || mdio.md_type == MD_SWAP) &&
288 		    sflag == NULL)
289 			errx(1, "must specify -s for -t malloc or -t swap");
290 		if (mdio.md_type == MD_VNODE && mdio.md_file[0] == '\0')
291 			errx(1, "must specify -f for -t vnode");
292 	} else {
293 		if (mdio.md_sectorsize != 0)
294 			errx(1, "-S can only be used with -a");
295 		if (action != RESIZE && sflag != NULL)
296 			errx(1, "-s can only be used with -a and -r");
297 		if (mdio.md_fwsectors != 0)
298 			errx(1, "-x can only be used with -a");
299 		if (mdio.md_fwheads != 0)
300 			errx(1, "-y can only be used with -a");
301 		if (fflag != NULL)
302 			errx(1, "-f can only be used with -a");
303 		if (tflag != NULL)
304 			errx(1, "-t can only be used with -a");
305 		if (argc > 0)
306 			errx(1, "file can only be used with -a");
307 		if ((action != DETACH && action != RESIZE) &&
308 		    (mdio.md_options & ~MD_AUTOUNIT) != 0)
309 			errx(1, "-o can only be used with -a, -d, and -r");
310 		if (action == DETACH &&
311 		    (mdio.md_options & ~(MD_FORCE | MD_AUTOUNIT)) != 0)
312 			errx(1, "only -o [no]force can be used with -d");
313 		if (action == RESIZE &&
314 		    (mdio.md_options & ~(MD_FORCE | MD_RESERVE | MD_AUTOUNIT)) != 0)
315 			errx(1, "only -o [no]force and -o [no]reserve can be used with -r");
316 	}
317 
318 	if (action == RESIZE && sflag == NULL)
319 		errx(1, "must specify -s for -r");
320 
321 	if (action != LIST && vflag == OPT_VERBOSE)
322 		errx(1, "-v can only be used with -l");
323 
324 	if (uflag != NULL) {
325 		mdio.md_unit = strtoul(uflag, &p, 0);
326 		if (mdio.md_unit == (unsigned)ULONG_MAX || *p != '\0')
327 			errx(1, "bad unit: %s", uflag);
328 		mdio.md_options &= ~MD_AUTOUNIT;
329 	}
330 
331 	mdio.md_version = MDIOVERSION;
332 
333 	if (!kld_isloaded("g_md") && kld_load("geom_md") == -1)
334 		err(1, "failed to load geom_md module");
335 
336 	fd = open("/dev/" MDCTL_NAME, O_RDWR, 0);
337 	if (fd < 0)
338 		err(1, "open(/dev/%s)", MDCTL_NAME);
339 
340 	if (action == ATTACH) {
341 		i = ioctl(fd, MDIOCATTACH, &mdio);
342 		if (i < 0)
343 			err(1, "ioctl(/dev/%s)", MDCTL_NAME);
344 		if (mdio.md_options & MD_AUTOUNIT)
345 			printf("%s%d\n", nflag ? "" : MD_NAME, mdio.md_unit);
346 	} else if (action == DETACH) {
347 		if (mdio.md_options & MD_AUTOUNIT)
348 			errx(1, "-d requires -u");
349 		i = ioctl(fd, MDIOCDETACH, &mdio);
350 		if (i < 0)
351 			err(1, "ioctl(/dev/%s)", MDCTL_NAME);
352 	} else if (action == RESIZE) {
353 		if (mdio.md_options & MD_AUTOUNIT)
354 			errx(1, "-r requires -u");
355 		i = ioctl(fd, MDIOCRESIZE, &mdio);
356 		if (i < 0)
357 			err(1, "ioctl(/dev/%s)", MDCTL_NAME);
358 	} else if (action == LIST) {
359 		if (mdio.md_options & MD_AUTOUNIT) {
360 			/*
361 			 * Listing all devices. This is why we pass NULL
362 			 * together with OPT_LIST.
363 			 */
364 			md_list(NULL, OPT_LIST | vflag);
365 		} else
366 			return (md_query(uflag));
367 	} else
368 		usage();
369 	close(fd);
370 	return (0);
371 }
372 
373 static void
374 md_set_file(const char *fn)
375 {
376 	struct stat sb;
377 	int fd;
378 
379 	if (realpath(fn, mdio.md_file) == NULL)
380 		err(1, "could not find full path for %s", fn);
381 	fd = open(mdio.md_file, O_RDONLY);
382 	if (fd < 0)
383 		err(1, "could not open %s", fn);
384 	if (fstat(fd, &sb) == -1)
385 		err(1, "could not stat %s", fn);
386 	if (!S_ISREG(sb.st_mode))
387 		errx(1, "%s is not a regular file", fn);
388 	if (mdio.md_mediasize == 0)
389 		mdio.md_mediasize = sb.st_size;
390 	close(fd);
391 }
392 
393 /*
394  * Lists md(4) disks. Is used also as a query routine, since it handles XML
395  * interface. 'units' can be NULL for listing memory disks. It might be
396  * coma-separated string containing md(4) disk names. 'opt' distinguished
397  * between list and query mode.
398  */
399 static int
400 md_list(char *units, int opt)
401 {
402 	struct gmesh gm;
403 	struct gprovider *pp;
404 	struct gconf *gc;
405 	struct gident *gid;
406 	struct devstat *gsp;
407 	struct ggeom *gg;
408 	struct gclass *gcl;
409 	void *sq;
410 	int retcode, found;
411 	char *type, *file, *length;
412 
413 	type = file = length = NULL;
414 
415 	retcode = geom_gettree(&gm);
416 	if (retcode != 0)
417 		return (-1);
418 	retcode = geom_stats_open();
419 	if (retcode != 0)
420 		return (-1);
421 	sq = geom_stats_snapshot_get();
422 	if (sq == NULL)
423 		return (-1);
424 
425 	found = 0;
426 	while ((gsp = geom_stats_snapshot_next(sq)) != NULL) {
427 		gid = geom_lookupid(&gm, gsp->id);
428 		if (gid == NULL)
429 			continue;
430 		if (gid->lg_what == ISPROVIDER) {
431 			pp = gid->lg_ptr;
432 			gg = pp->lg_geom;
433 			gcl = gg->lg_class;
434 			if (strcmp(gcl->lg_name, CLASS_NAME_MD) != 0)
435 				continue;
436 			if ((opt & OPT_UNIT) && (units != NULL)) {
437 				retcode = md_find(units, pp->lg_name);
438 				if (retcode != 1)
439 					continue;
440 				else
441 					found = 1;
442 			}
443 			gc = &pp->lg_config;
444 			if (nflag && strncmp(pp->lg_name, "md", 2) == 0)
445 				printf("%s", pp->lg_name + 2);
446 			else
447 				printf("%s", pp->lg_name);
448 
449 			if (opt & OPT_VERBOSE || opt & OPT_UNIT) {
450 				type = geom_config_get(gc, "type");
451 				if (strcmp(type, "vnode") == 0)
452 					file = geom_config_get(gc, "file");
453 				length = geom_config_get(gc, "length");
454 				printf("\t%s\t", type);
455 				if (length != NULL)
456 					md_prthumanval(length);
457 				if (file != NULL) {
458 					printf("\t%s", file);
459 					file = NULL;
460 				}
461 			}
462 			opt |= OPT_DONE;
463 			if ((opt & OPT_LIST) && !(opt & OPT_VERBOSE))
464 				printf(" ");
465 			else
466 				printf("\n");
467 		}
468 	}
469 	if ((opt & OPT_LIST) && (opt & OPT_DONE) && !(opt & OPT_VERBOSE))
470 		printf("\n");
471 	/* XXX: Check if it's enough to clean everything. */
472 	geom_stats_snapshot_free(sq);
473 	if ((opt & OPT_UNIT) && found)
474 		return (0);
475 	else
476 		return (-1);
477 }
478 
479 /*
480  * Returns value of 'name' from gconfig structure.
481  */
482 static char *
483 geom_config_get(struct gconf *g, const char *name)
484 {
485 	struct gconfig *gce;
486 
487 	LIST_FOREACH(gce, g, lg_config) {
488 		if (strcmp(gce->lg_name, name) == 0)
489 			return (gce->lg_val);
490 	}
491 	return (NULL);
492 }
493 
494 /*
495  * List is comma separated list of MD disks. name is a
496  * device name we look for.  Returns 1 if found and 0
497  * otherwise.
498  */
499 static int
500 md_find(char *list, const char *name)
501 {
502 	int ret;
503 	char num[16];
504 	char *ptr, *p, *u;
505 
506 	ret = 0;
507 	ptr = strdup(list);
508 	if (ptr == NULL)
509 		return (-1);
510 	for (p = ptr; (u = strsep(&p, ",")) != NULL;) {
511 		if (strncmp(u, "/dev/", 5) == 0)
512 			u += 5;
513 		/* Just in case user specified number instead of full name */
514 		snprintf(num, sizeof(num), "md%s", u);
515 		if (strcmp(u, name) == 0 || strcmp(num, name) == 0) {
516 			ret = 1;
517 			break;
518 		}
519 	}
520 	free(ptr);
521 	return (ret);
522 }
523 
524 static void
525 md_prthumanval(char *length)
526 {
527 	char buf[6];
528 	uintmax_t bytes;
529 	char *endptr;
530 
531 	errno = 0;
532 	bytes = strtoumax(length, &endptr, 10);
533 	if (errno != 0 || *endptr != '\0' || bytes > INT64_MAX)
534 		return;
535 	humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
536 	    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
537 	(void)printf("%6s", buf);
538 }
539 
540 static int
541 md_query(char *name)
542 {
543 
544 	return (md_list(name, OPT_UNIT));
545 }
546