mdconfig.c revision 102224
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 102224 2002-08-21 15:15:15Z imp $
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	mdio.md_version = MDIOVERSION;
165
166	mdmaybeload();
167	fd = open("/dev/" MDCTL_NAME, O_RDWR, 0);
168	if (fd < 0)
169		err(1, "open(/dev/%s)", MDCTL_NAME);
170	if (cmdline == 2
171	    && (mdio.md_type == MD_MALLOC || mdio.md_type == MD_SWAP))
172		if (mdio.md_size == 0)
173			errx(1, "must specify -s for -t malloc or -t swap");
174	if (action == LIST) {
175		if (mdio.md_options & MD_AUTOUNIT)
176			list(fd);
177		else
178			query(fd, mdio.md_unit);
179	} else if (action == ATTACH) {
180		i = ioctl(fd, MDIOCATTACH, &mdio);
181		if (i < 0)
182			err(1, "ioctl(/dev/%s)", MDCTL_NAME);
183		if (mdio.md_options & MD_AUTOUNIT)
184			printf("%s%d\n", MD_NAME, mdio.md_unit);
185	} else if (action == DETACH) {
186		if (mdio.md_options & MD_AUTOUNIT)
187			usage();
188		i = ioctl(fd, MDIOCDETACH, &mdio);
189		if (i < 0)
190			err(1, "ioctl(/dev/%s)", MDCTL_NAME);
191	} else
192		usage();
193	close (fd);
194	return (0);
195}
196
197struct dl {
198	int		unit;
199	SLIST_ENTRY(dl)	slist;
200};
201
202SLIST_HEAD(, dl) dlist = SLIST_HEAD_INITIALIZER(&dlist);
203
204int
205list(const int fd)
206{
207	char *disklist, *p, *p2, *p3;
208	int unit;
209	size_t dll;
210	struct dl *dp, *di, *dn;
211
212	if (sysctlbyname("kern.disks", NULL, &dll, NULL, 0) == -1)
213		err(1, "sysctlbyname: kern.disks");
214	if ( (disklist = malloc(dll)) == NULL)
215		err(1, "malloc");
216	if (sysctlbyname("kern.disks", disklist, &dll, NULL, 0) == -1)
217		err(1, "sysctlbyname: kern.disks");
218
219	for (p = disklist;
220	     (p2 = strsep(&p, " ")) != NULL;) {
221		if (strncmp(p2, MD_NAME, sizeof(MD_NAME) - 1) != 0)
222			continue;
223		p2 += sizeof(MD_NAME) - 1;
224		unit = strtoul(p2, &p3, 10);
225		if (p2 == p3)
226			continue;
227		dp = calloc(sizeof *dp, 1);
228		dp->unit = unit;
229		dn = SLIST_FIRST(&dlist);
230		if (dn == NULL || dn->unit > unit) {
231			SLIST_INSERT_HEAD(&dlist, dp, slist);
232		} else {
233			SLIST_FOREACH(di, &dlist, slist) {
234				dn = SLIST_NEXT(di, slist);
235				if (dn == NULL || dn->unit > unit) {
236					SLIST_INSERT_AFTER(di, dp, slist);
237					break;
238				}
239			}
240		}
241	}
242	SLIST_FOREACH(di, &dlist, slist)
243		query(fd, di->unit);
244	while (!SLIST_EMPTY(&dlist)) {
245		di = SLIST_FIRST(&dlist);
246		SLIST_REMOVE_HEAD(&dlist, slist);
247		free(di);
248	}
249	free(disklist);
250	return (0);
251}
252
253int
254query(const int fd, const int unit)
255{
256
257	mdio.md_version = MDIOVERSION;
258	mdio.md_unit = unit;
259
260	if (ioctl(fd, MDIOCQUERY, &mdio) < 0)
261		err(1, "ioctl(/dev/%s)", MDCTL_NAME);
262
263	switch (mdio.md_type) {
264	case MD_MALLOC:
265		(void)printf("%s%d\tmalloc\t%d KBytes\n", MD_NAME,
266		    mdio.md_unit, mdio.md_size / 2);
267		break;
268	case MD_PRELOAD:
269		(void)printf("%s%d\tpreload\t%d KBytes\n", MD_NAME,
270		    mdio.md_unit, mdio.md_size / 2);
271		break;
272	case MD_SWAP:
273		(void)printf("%s%d\tswap\t%d KBytes\n", MD_NAME,
274		    mdio.md_unit, mdio.md_size / 2);
275		break;
276	case MD_VNODE:
277		(void)printf("%s%d\tvnode\t%d KBytes\n", MD_NAME,
278		    mdio.md_unit, mdio.md_size / 2);
279		break;
280	}
281
282	return (0);
283}
284
285void
286mdmaybeload(void)
287{
288        struct module_stat mstat;
289        int fileid, modid;
290        const char *name;
291	char *cp;
292
293	name = MD_NAME;
294        /* scan files in kernel */
295        mstat.version = sizeof(struct module_stat);
296        for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) {
297                /* scan modules in file */
298                for (modid = kldfirstmod(fileid); modid > 0;
299                     modid = modfnext(modid)) {
300                        if (modstat(modid, &mstat) < 0)
301                                continue;
302                        /* strip bus name if present */
303                        if ((cp = strchr(mstat.name, '/')) != NULL) {
304                                cp++;
305                        } else {
306                                cp = mstat.name;
307                        }
308                        /* already loaded? */
309                        if (!strcmp(name, cp))
310                                return;
311                }
312        }
313        /* not present, we should try to load it */
314        kldload(name);
315}
316
317