mdconfig.c revision 81258
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 81258 2001-08-07 19:27:46Z dd $
10 *
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <fcntl.h>
16#include <unistd.h>
17#include <string.h>
18#include <err.h>
19#include <sys/ioctl.h>
20#include <sys/param.h>
21#include <sys/module.h>
22#include <sys/linker.h>
23#include <sys/mdioctl.h>
24#include <sys/sysctl.h>
25#include <sys/queue.h>
26
27int	 list(const int);
28void	 mdmaybeload(void);
29int	 query(const int, const int);
30void	 usage(void);
31
32struct md_ioctl mdio;
33
34enum {UNSET, ATTACH, DETACH, LIST} action = UNSET;
35
36void
37usage()
38{
39	fprintf(stderr, "Usage:\n");
40	fprintf(stderr, "\tmdconfig -a -t type [-o [no]option]... [ -f file] [-s size] [-u unit]\n");
41	fprintf(stderr, "\tmdconfig -d -u unit\n");
42	fprintf(stderr, "\tmdconfig -l [-u unit]\n");
43	fprintf(stderr, "\t\ttype = {malloc, preload, vnode, swap}\n");
44	fprintf(stderr, "\t\toption = {cluster, compress, reserve}\n");
45	fprintf(stderr, "\t\tsize = %%d (512 byte blocks), %%dk (kB), %%dm (MB) or %%dg (GB)\n");
46	exit(1);
47}
48
49int
50main(int argc, char **argv)
51{
52	int ch, fd, i;
53	char *p;
54	int cmdline = 0;
55
56	for (;;) {
57		ch = getopt(argc, argv, "ab:df:lo:s:t:u:");
58		if (ch == -1)
59			break;
60		switch (ch) {
61		case 'a':
62			if (cmdline != 0)
63				usage();
64			action = ATTACH;
65			cmdline = 1;
66			break;
67		case 'd':
68			if (cmdline != 0)
69				usage();
70			action = DETACH;
71			mdio.md_options = MD_AUTOUNIT;
72			cmdline = 3;
73			break;
74		case 'l':
75			if (cmdline != 0)
76				usage();
77			action = LIST;
78			mdio.md_options = MD_AUTOUNIT;
79			cmdline = 3;
80			break;
81		case 't':
82			if (cmdline != 1)
83				usage();
84			if (!strcmp(optarg, "malloc")) {
85				mdio.md_type = MD_MALLOC;
86				mdio.md_options = MD_AUTOUNIT | MD_COMPRESS;
87			} else if (!strcmp(optarg, "preload")) {
88				mdio.md_type = MD_PRELOAD;
89				mdio.md_options = 0;
90			} else if (!strcmp(optarg, "vnode")) {
91				mdio.md_type = MD_VNODE;
92				mdio.md_options = MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
93			} else if (!strcmp(optarg, "swap")) {
94				mdio.md_type = MD_SWAP;
95				mdio.md_options = MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
96			} else {
97				usage();
98			}
99			cmdline=2;
100			break;
101		case 'f':
102			if (cmdline != 1 && cmdline != 2)
103				usage();
104			if (cmdline == 1) {
105				/* Imply ``-t vnode'' */
106				mdio.md_type = MD_VNODE;
107				mdio.md_options = MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
108			}
109			mdio.md_file = optarg;
110			break;
111		case 'o':
112			if (cmdline != 2)
113				usage();
114			if (!strcmp(optarg, "cluster"))
115				mdio.md_options |= MD_CLUSTER;
116			else if (!strcmp(optarg, "nocluster"))
117				mdio.md_options &= ~MD_CLUSTER;
118			else if (!strcmp(optarg, "compress"))
119				mdio.md_options |= MD_COMPRESS;
120			else if (!strcmp(optarg, "nocompress"))
121				mdio.md_options &= ~MD_COMPRESS;
122			else if (!strcmp(optarg, "force"))
123				mdio.md_options |= MD_FORCE;
124			else if (!strcmp(optarg, "noforce"))
125				mdio.md_options &= ~MD_FORCE;
126			else if (!strcmp(optarg, "reserve"))
127				mdio.md_options |= MD_RESERVE;
128			else if (!strcmp(optarg, "noreserve"))
129				mdio.md_options &= ~MD_RESERVE;
130			else
131				errx(1, "Unknown option.");
132			break;
133		case 's':
134			if (cmdline != 2)
135				usage();
136			mdio.md_size = strtoul(optarg, &p, 0);
137			if (p == NULL || *p == '\0')
138				;
139			else if (*p == 'k' || *p == 'K')
140				mdio.md_size *= (1024 / DEV_BSIZE);
141			else if (*p == 'm' || *p == 'M')
142				mdio.md_size *= (1024 * 1024 / DEV_BSIZE);
143			else if (*p == 'g' || *p == 'G')
144				mdio.md_size *= (1024 * 1024 * 1024 / DEV_BSIZE);
145			else
146				errx(1, "Unknown suffix on -s argument");
147			break;
148		case 'u':
149			if (cmdline != 2 && cmdline != 3)
150				usage();
151			if (!strncmp(optarg, "/dev/", 5))
152				optarg += 5;
153			if (!strncmp(optarg, MD_NAME, sizeof(MD_NAME) - 1))
154				optarg += sizeof(MD_NAME) - 1;
155			mdio.md_unit = strtoul(optarg, &p, 0);
156			if ((unsigned)mdio.md_unit == ULONG_MAX || *p != '\0')
157				errx(1, "bad unit: %s", optarg);
158			mdio.md_options &= ~MD_AUTOUNIT;
159			break;
160		default:
161			usage();
162		}
163	}
164
165	mdmaybeload();
166	fd = open("/dev/" MDCTL_NAME, O_RDWR, 0);
167	if (fd < 0)
168		err(1, "open(/dev/%s)", MDCTL_NAME);
169	if (cmdline == 2
170	    && (mdio.md_type == MD_MALLOC || mdio.md_type == MD_SWAP))
171		if (mdio.md_size == 0)
172			errx(1, "must specify -s for -t malloc or -t swap");
173	if (action == LIST) {
174		if (mdio.md_options & MD_AUTOUNIT)
175			list(fd);
176		else
177			query(fd, mdio.md_unit);
178	} else if (action == ATTACH) {
179		i = ioctl(fd, MDIOCATTACH, &mdio);
180		if (i < 0)
181			err(1, "ioctl(/dev/%s)", MDCTL_NAME);
182		if (mdio.md_options & MD_AUTOUNIT)
183			printf("%s%d\n", MD_NAME, mdio.md_unit);
184	} else if (action == DETACH) {
185		if (mdio.md_options & MD_AUTOUNIT)
186			usage();
187		i = ioctl(fd, MDIOCDETACH, &mdio);
188		if (i < 0)
189			err(1, "ioctl(/dev/%s)", MDCTL_NAME);
190	} else
191		usage();
192	close (fd);
193	return (0);
194}
195
196struct dl {
197	int		unit;
198	SLIST_ENTRY(dl)	slist;
199};
200
201SLIST_HEAD(, dl) dlist = SLIST_HEAD_INITIALIZER(&dlist);
202
203int
204list(const int fd)
205{
206	char *disklist, *p, *p2, *p3;
207	int unit;
208	size_t dll;
209	struct dl *dp, *di, *dn;
210
211	if (sysctlbyname("kern.disks", NULL, &dll, NULL, 0) == -1)
212		err(1, "sysctlbyname: kern.disks");
213	if ( (disklist = malloc(dll)) == NULL)
214		err(1, "malloc");
215	if (sysctlbyname("kern.disks", disklist, &dll, NULL, NULL) == -1)
216		err(1, "sysctlbyname: kern.disks");
217
218	for (p = disklist;
219	     (p2 = strsep(&p, " ")) != NULL;) {
220		if (strncmp(p2, MD_NAME, sizeof(MD_NAME) - 1) != 0)
221			continue;
222		p2 += sizeof(MD_NAME) - 1;
223		unit = strtoul(p2, &p3, 10);
224		if (p2 == p3)
225			continue;
226		dp = calloc(sizeof *dp, 1);
227		dp->unit = unit;
228		dn = SLIST_FIRST(&dlist);
229		if (dn == NULL || dn->unit > unit) {
230			SLIST_INSERT_HEAD(&dlist, dp, slist);
231		} else {
232			SLIST_FOREACH(di, &dlist, slist) {
233				dn = SLIST_NEXT(di, slist);
234				if (dn == NULL || dn->unit > unit) {
235					SLIST_INSERT_AFTER(di, dp, slist);
236					break;
237				}
238			}
239		}
240	}
241	SLIST_FOREACH(di, &dlist, slist)
242		query(fd, di->unit);
243	while (!SLIST_EMPTY(&dlist)) {
244		di = SLIST_FIRST(&dlist);
245		SLIST_REMOVE_HEAD(&dlist, slist);
246		free(di);
247	}
248	free(disklist);
249	return (0);
250}
251
252int
253query(const int fd, const int unit)
254{
255
256	mdio.md_unit = unit;
257
258	if (ioctl(fd, MDIOCQUERY, &mdio) < 0)
259		err(1, "ioctl(/dev/%s)", MDCTL_NAME);
260
261	switch (mdio.md_type) {
262	case MD_MALLOC:
263		(void)printf("%s%d\tmalloc\t%d KBytes\n", MD_NAME,
264		    mdio.md_unit, mdio.md_size / 2);
265		break;
266	case MD_PRELOAD:
267		(void)printf("%s%d\tpreload\t%d KBytes\n", MD_NAME,
268		    mdio.md_unit, mdio.md_size / 2);
269		break;
270	case MD_SWAP:
271		(void)printf("%s%d\tswap\t%d KBytes\n", MD_NAME,
272		    mdio.md_unit, mdio.md_size / 2);
273		break;
274	case MD_VNODE:
275		(void)printf("%s%d\tvnode\t%d KBytes\n", MD_NAME,
276		    mdio.md_unit, mdio.md_size / 2);
277		break;
278	}
279
280	return (0);
281}
282
283void
284mdmaybeload(void)
285{
286        struct module_stat mstat;
287        int fileid, modid;
288        const char *name;
289	char *cp;
290
291	name = MD_NAME;
292        /* scan files in kernel */
293        mstat.version = sizeof(struct module_stat);
294        for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) {
295                /* scan modules in file */
296                for (modid = kldfirstmod(fileid); modid > 0;
297                     modid = modfnext(modid)) {
298                        if (modstat(modid, &mstat) < 0)
299                                continue;
300                        /* strip bus name if present */
301                        if ((cp = strchr(mstat.name, '/')) != NULL) {
302                                cp++;
303                        } else {
304                                cp = mstat.name;
305                        }
306                        /* already loaded? */
307                        if (!strcmp(name, cp))
308                                return;
309                }
310        }
311        /* not present, we should try to load it */
312        kldload(name);
313}
314
315