xref: /freebsd/sbin/mdconfig/mdconfig.c (revision 262e143bd46171a6415a5b28af260a5efa2a3db8)
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$
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 
23 #include <sys/ioctl.h>
24 #include <sys/param.h>
25 #include <sys/module.h>
26 #include <sys/linker.h>
27 #include <sys/mdioctl.h>
28 #include <sys/stat.h>
29 
30 int	 list(const int);
31 void	 mdmaybeload(void);
32 int	 query(const int, const int);
33 void	 usage(void);
34 
35 struct md_ioctl mdio;
36 
37 enum {UNSET, ATTACH, DETACH, LIST} action = UNSET;
38 
39 int nflag;
40 
41 void
42 usage()
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 
58 int
59 main(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 	mdmaybeload();
227 	fd = open("/dev/" MDCTL_NAME, O_RDWR, 0);
228 	if (fd < 0)
229 		err(1, "open(/dev/%s)", MDCTL_NAME);
230 	if (cmdline == 2
231 	    && (mdio.md_type == MD_MALLOC || mdio.md_type == MD_SWAP))
232 		if (mdio.md_mediasize == 0)
233 			errx(1, "must specify -s for -t malloc or -t swap");
234 	if (cmdline == 2 && mdio.md_type == MD_VNODE)
235 		if (mdio.md_file[0] == '\0')
236 			errx(1, "must specify -f for -t vnode");
237 	if (mdio.md_type == MD_VNODE &&
238 	    (mdio.md_options & MD_READONLY) == 0) {
239 		if (access(mdio.md_file, W_OK) < 0 &&
240 		    (errno == EACCES || errno == EPERM || errno == EROFS)) {
241 			fprintf(stderr,
242 			    "WARNING: opening backing store: %s readonly\n",
243 			    mdio.md_file);
244 			mdio.md_options |= MD_READONLY;
245 		}
246 	}
247 	if (action == LIST) {
248 		if (mdio.md_options & MD_AUTOUNIT)
249 			list(fd);
250 		else
251 			query(fd, mdio.md_unit);
252 	} else if (action == ATTACH) {
253 		if (cmdline < 2)
254 			usage();
255 		i = ioctl(fd, MDIOCATTACH, &mdio);
256 		if (i < 0)
257 			err(1, "ioctl(/dev/%s)", MDCTL_NAME);
258 		if (mdio.md_options & MD_AUTOUNIT)
259 			printf("%s%d\n", nflag ? "" : MD_NAME, mdio.md_unit);
260 	} else if (action == DETACH) {
261 		if (mdio.md_options & MD_AUTOUNIT)
262 			usage();
263 		i = ioctl(fd, MDIOCDETACH, &mdio);
264 		if (i < 0)
265 			err(1, "ioctl(/dev/%s)", MDCTL_NAME);
266 	} else
267 		usage();
268 	close (fd);
269 	return (0);
270 }
271 
272 int
273 list(const int fd)
274 {
275 	int unit;
276 
277 	if (ioctl(fd, MDIOCLIST, &mdio) < 0)
278 		err(1, "ioctl(/dev/%s)", MDCTL_NAME);
279 	for (unit = 0; unit < mdio.md_pad[0] && unit < MDNPAD - 1; unit++) {
280 		printf("%s%s%d", unit > 0 ? " " : "",
281 		    nflag ? "" : MD_NAME, mdio.md_pad[unit + 1]);
282 	}
283 	if (mdio.md_pad[0] - unit > 0)
284 		printf(" ... %d more", mdio.md_pad[0] - unit);
285 	if (unit > 0)
286 		printf("\n");
287 	return (0);
288 }
289 
290 static void
291 prthumanval(int64_t bytes)
292 {
293 	char buf[6];
294 
295 	humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1),
296 	    bytes, "", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
297 	(void)printf("%6s", buf);
298 }
299 
300 int
301 query(const int fd, const int unit)
302 {
303 
304 	mdio.md_version = MDIOVERSION;
305 	mdio.md_unit = unit;
306 
307 	if (ioctl(fd, MDIOCQUERY, &mdio) < 0)
308 		err(1, "ioctl(/dev/%s)", MDCTL_NAME);
309 
310 	(void)printf("%s%d\t", MD_NAME, mdio.md_unit);
311 	switch (mdio.md_type) {
312 	case MD_MALLOC:
313 		(void)printf("malloc");
314 		break;
315 	case MD_PRELOAD:
316 		(void)printf("preload");
317 		break;
318 	case MD_SWAP:
319 		(void)printf("swap");
320 		break;
321 	case MD_VNODE:
322 		(void)printf("vnode");
323 		break;
324 	}
325 	printf("\t");
326 	prthumanval(mdio.md_mediasize);
327 	if (mdio.md_type == MD_VNODE)
328 		printf("\t%s", mdio.md_file);
329 	printf("\n");
330 
331 	return (0);
332 }
333 
334 void
335 mdmaybeload(void)
336 {
337 	char name1[64], name2[64];
338 
339 	snprintf(name1, sizeof(name1), "g_%s", MD_NAME);
340 	snprintf(name2, sizeof(name2), "geom_%s", MD_NAME);
341 	if (modfind(name1) == -1) {
342 		/* Not present in kernel, try loading it. */
343 		if (kldload(name2) == -1 || modfind(name1) == -1) {
344 			if (errno != EEXIST) {
345 				errx(EXIT_FAILURE,
346 				    "%s module not available!", name2);
347 			}
348 		}
349 	}
350 }
351