]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - sbin/mdconfig/mdconfig.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / sbin / mdconfig / mdconfig.c
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 #include <sys/param.h>
13 #include <sys/devicestat.h>
14 #include <sys/ioctl.h>
15 #include <sys/linker.h>
16 #include <sys/mdioctl.h>
17 #include <sys/module.h>
18 #include <sys/resource.h>
19 #include <sys/stat.h>
20
21 #include <assert.h>
22 #include <devstat.h>
23 #include <err.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <inttypes.h>
27 #include <libgeom.h>
28 #include <libutil.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34
35
36 static struct md_ioctl mdio;
37 static enum {UNSET, ATTACH, DETACH, LIST} action = UNSET;
38 static int nflag;
39
40 static void usage(void);
41 static void md_set_file(const char *);
42 static int md_find(char *, const char *);
43 static int md_query(char *name);
44 static int md_list(char *units, int opt);
45 static char *geom_config_get(struct gconf *g, const char *name);
46 static void md_prthumanval(char *length);
47
48 #define OPT_VERBOSE     0x01
49 #define OPT_UNIT        0x02
50 #define OPT_DONE        0x04
51 #define OPT_LIST        0x10
52
53 #define CLASS_NAME_MD   "MD"
54
55 static void
56 usage(void)
57 {
58         fprintf(stderr,
59 "usage: mdconfig -a -t type [-n] [-o [no]option] ... [-f file]\n"
60 "                [-s size] [-S sectorsize] [-u unit]\n"
61 "                [-x sectors/track] [-y heads/cylinder]\n"
62 "       mdconfig -d -u unit [-o [no]force]\n"
63 "       mdconfig -l [-v] [-n] [-u unit]\n"
64 "       mdconfig file\n");
65         fprintf(stderr, "\t\ttype = {malloc, preload, vnode, swap}\n");
66         fprintf(stderr, "\t\toption = {cluster, compress, reserve}\n");
67         fprintf(stderr, "\t\tsize = %%d (512 byte blocks), %%db (B),\n");
68         fprintf(stderr, "\t\t       %%dk (kB), %%dm (MB), %%dg (GB) or\n");
69         fprintf(stderr, "\t\t       %%dt (TB)\n");
70         exit(1);
71 }
72
73 int
74 main(int argc, char **argv)
75 {
76         int ch, fd, i, vflag;
77         char *p;
78         int cmdline = 0;
79         char *mdunit = NULL;
80
81         bzero(&mdio, sizeof(mdio));
82         mdio.md_file = malloc(PATH_MAX);
83         if (mdio.md_file == NULL)
84                 err(1, "could not allocate memory");
85         vflag = 0;
86         bzero(mdio.md_file, PATH_MAX);
87         while ((ch = getopt(argc, argv, "ab:df:lno:s:S:t:u:vx:y:")) != -1) {
88                 switch (ch) {
89                 case 'a':
90                         if (cmdline != 0)
91                                 usage();
92                         action = ATTACH;
93                         cmdline = 1;
94                         break;
95                 case 'd':
96                         if (cmdline != 0)
97                                 usage();
98                         action = DETACH;
99                         mdio.md_options = MD_AUTOUNIT;
100                         cmdline = 3;
101                         break;
102                 case 'l':
103                         if (cmdline != 0)
104                                 usage();
105                         action = LIST;
106                         mdio.md_options = MD_AUTOUNIT;
107                         cmdline = 3;
108                         break;
109                 case 'n':
110                         nflag = 1;
111                         break;
112                 case 't':
113                         if (cmdline != 1)
114                                 usage();
115                         if (!strcmp(optarg, "malloc")) {
116                                 mdio.md_type = MD_MALLOC;
117                                 mdio.md_options = MD_AUTOUNIT | MD_COMPRESS;
118                         } else if (!strcmp(optarg, "preload")) {
119                                 mdio.md_type = MD_PRELOAD;
120                                 mdio.md_options = 0;
121                         } else if (!strcmp(optarg, "vnode")) {
122                                 mdio.md_type = MD_VNODE;
123                                 mdio.md_options = MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
124                         } else if (!strcmp(optarg, "swap")) {
125                                 mdio.md_type = MD_SWAP;
126                                 mdio.md_options = MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
127                         } else {
128                                 usage();
129                         }
130                         cmdline=2;
131                         break;
132                 case 'f':
133                         if (cmdline == 0) {
134                                 action = ATTACH;
135                                 cmdline = 1;
136                         }
137                         if (cmdline == 1) {
138                                 /* Imply ``-t vnode'' */
139                                 mdio.md_type = MD_VNODE;
140                                 mdio.md_options = MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
141                                 cmdline = 2;
142                         }
143                         if (cmdline != 2)
144                                 usage();
145                         md_set_file(optarg);
146                         break;
147                 case 'o':
148                         if (action == DETACH) {
149                                 if (!strcmp(optarg, "force"))
150                                         mdio.md_options |= MD_FORCE;
151                                 else if (!strcmp(optarg, "noforce"))
152                                         mdio.md_options &= ~MD_FORCE;
153                                 else
154                                         errx(1, "Unknown option: %s.", optarg);
155                                 break;
156                         }
157
158                         if (cmdline != 2)
159                                 usage();
160                         if (!strcmp(optarg, "async"))
161                                 mdio.md_options |= MD_ASYNC;
162                         else if (!strcmp(optarg, "noasync"))
163                                 mdio.md_options &= ~MD_ASYNC;
164                         else if (!strcmp(optarg, "cluster"))
165                                 mdio.md_options |= MD_CLUSTER;
166                         else if (!strcmp(optarg, "nocluster"))
167                                 mdio.md_options &= ~MD_CLUSTER;
168                         else if (!strcmp(optarg, "compress"))
169                                 mdio.md_options |= MD_COMPRESS;
170                         else if (!strcmp(optarg, "nocompress"))
171                                 mdio.md_options &= ~MD_COMPRESS;
172                         else if (!strcmp(optarg, "force"))
173                                 mdio.md_options |= MD_FORCE;
174                         else if (!strcmp(optarg, "noforce"))
175                                 mdio.md_options &= ~MD_FORCE;
176                         else if (!strcmp(optarg, "readonly"))
177                                 mdio.md_options |= MD_READONLY;
178                         else if (!strcmp(optarg, "noreadonly"))
179                                 mdio.md_options &= ~MD_READONLY;
180                         else if (!strcmp(optarg, "reserve"))
181                                 mdio.md_options |= MD_RESERVE;
182                         else if (!strcmp(optarg, "noreserve"))
183                                 mdio.md_options &= ~MD_RESERVE;
184                         else
185                                 errx(1, "Unknown option: %s.", optarg);
186                         break;
187                 case 'S':
188                         if (cmdline != 2)
189                                 usage();
190                         mdio.md_sectorsize = strtoul(optarg, &p, 0);
191                         break;
192                 case 's':
193                         if (cmdline == 0) {
194                                 /* Imply ``-a'' */
195                                 action = ATTACH;
196                                 cmdline = 1;
197                         }
198                         if (cmdline == 1) {
199                                 /* Imply ``-t swap'' */
200                                 mdio.md_type = MD_SWAP;
201                                 mdio.md_options = MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
202                                 cmdline = 2;
203                         }
204                         if (cmdline != 2)
205                                 usage();
206                         mdio.md_mediasize = (off_t)strtoumax(optarg, &p, 0);
207                         if (p == NULL || *p == '\0')
208                                 mdio.md_mediasize *= DEV_BSIZE;
209                         else if (*p == 'b' || *p == 'B')
210                                 ; /* do nothing */
211                         else if (*p == 'k' || *p == 'K')
212                                 mdio.md_mediasize <<= 10;
213                         else if (*p == 'm' || *p == 'M')
214                                 mdio.md_mediasize <<= 20;
215                         else if (*p == 'g' || *p == 'G')
216                                 mdio.md_mediasize <<= 30;
217                         else if (*p == 't' || *p == 'T') {
218                                 mdio.md_mediasize <<= 30;
219                                 mdio.md_mediasize <<= 10;
220                         } else
221                                 errx(1, "Unknown suffix on -s argument");
222                         break;
223                 case 'u':
224                         if (cmdline != 2 && cmdline != 3)
225                                 usage();
226                         if (!strncmp(optarg, "/dev/", 5))
227                                 optarg += 5;
228                         if (!strncmp(optarg, MD_NAME, sizeof(MD_NAME) - 1))
229                                 optarg += sizeof(MD_NAME) - 1;
230                         mdio.md_unit = strtoul(optarg, &p, 0);
231                         if (mdio.md_unit == (unsigned)ULONG_MAX || *p != '\0')
232                                 errx(1, "bad unit: %s", optarg);
233                         mdunit = optarg;
234                         mdio.md_options &= ~MD_AUTOUNIT;
235                         break;
236                 case 'v':
237                         if (cmdline != 3)
238                                 usage();
239                         vflag = OPT_VERBOSE;
240                         break;
241                 case 'x':
242                         if (cmdline != 2)
243                                 usage();
244                         mdio.md_fwsectors = strtoul(optarg, &p, 0);
245                         break;
246                 case 'y':
247                         if (cmdline != 2)
248                                 usage();
249                         mdio.md_fwheads = strtoul(optarg, &p, 0);
250                         break;
251                 default:
252                         usage();
253                 }
254         }
255
256         argc -= optind;
257         argv += optind;
258         if (action == UNSET) {
259                 if (argc != 1)
260                         usage();
261                 action = ATTACH;
262                 mdio.md_type = MD_VNODE;
263                 mdio.md_options = MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS;
264                 cmdline = 2;
265                 md_set_file(*argv);
266         }
267
268         mdio.md_version = MDIOVERSION;
269
270         if (!kld_isloaded("g_md") && kld_load("geom_md") == -1)
271                 err(1, "failed to load geom_md module");
272
273         fd = open("/dev/" MDCTL_NAME, O_RDWR, 0);
274         if (fd < 0)
275                 err(1, "open(/dev/%s)", MDCTL_NAME);
276         if (cmdline == 2
277             && (mdio.md_type == MD_MALLOC || mdio.md_type == MD_SWAP))
278                 if (mdio.md_mediasize == 0)
279                         errx(1, "must specify -s for -t malloc or -t swap");
280         if (cmdline == 2 && mdio.md_type == MD_VNODE)
281                 if (mdio.md_file[0] == '\0')
282                         errx(1, "must specify -f for -t vnode");
283         if (mdio.md_type == MD_VNODE &&
284             (mdio.md_options & MD_READONLY) == 0) {
285                 if (access(mdio.md_file, W_OK) < 0 &&
286                     (errno == EACCES || errno == EPERM || errno == EROFS)) {
287                         fprintf(stderr,
288                             "WARNING: opening backing store: %s readonly\n",
289                             mdio.md_file);
290                         mdio.md_options |= MD_READONLY;
291                 }
292         }
293         if (action == LIST) {
294                 if (mdio.md_options & MD_AUTOUNIT) {
295                         /*
296                          * Listing all devices. This is why we pass NULL
297                          * together with OPT_LIST.
298                          */
299                         md_list(NULL, OPT_LIST | vflag);
300                 } else {
301                         return (md_query(mdunit));
302                 }
303         } else if (action == ATTACH) {
304                 if (cmdline < 2)
305                         usage();
306                 i = ioctl(fd, MDIOCATTACH, &mdio);
307                 if (i < 0)
308                         err(1, "ioctl(/dev/%s)", MDCTL_NAME);
309                 if (mdio.md_options & MD_AUTOUNIT)
310                         printf("%s%d\n", nflag ? "" : MD_NAME, mdio.md_unit);
311         } else if (action == DETACH) {
312                 if (mdio.md_options & MD_AUTOUNIT)
313                         usage();
314                 i = ioctl(fd, MDIOCDETACH, &mdio);
315                 if (i < 0)
316                         err(1, "ioctl(/dev/%s)", MDCTL_NAME);
317         } else
318                 usage();
319         close (fd);
320         return (0);
321 }
322
323 static void
324 md_set_file(const char *fn)
325 {
326         struct stat sb;
327         int fd;
328
329         if (realpath(fn, mdio.md_file) == NULL)
330                 err(1, "could not find full path for %s", fn);
331         fd = open(mdio.md_file, O_RDONLY);
332         if (fd < 0)
333                 err(1, "could not open %s", fn);
334         if (fstat(fd, &sb) == -1)
335                 err(1, "could not stat %s", fn);
336         if (!S_ISREG(sb.st_mode))
337                 errx(1, "%s is not a regular file", fn);
338         if (mdio.md_mediasize == 0)
339                 mdio.md_mediasize = sb.st_size;
340         close(fd);
341 }
342
343 /*
344  * Lists md(4) disks. Is used also as a query routine, since it handles XML
345  * interface. 'units' can be NULL for listing memory disks. It might be
346  * coma-separated string containing md(4) disk names. 'opt' distinguished
347  * between list and query mode.
348  */
349 static int
350 md_list(char *units, int opt)
351 {
352         struct gmesh gm;
353         struct gprovider *pp;
354         struct gconf *gc;
355         struct gident *gid;
356         struct devstat *gsp;
357         struct ggeom *gg;
358         struct gclass *gcl;
359         void *sq;
360         int retcode, found;
361         char *type, *file, *length;
362
363         type = file = length = NULL;
364
365         retcode = geom_gettree(&gm);
366         if (retcode != 0)
367                 return (-1);
368         retcode = geom_stats_open();
369         if (retcode != 0)
370                 return (-1);
371         sq = geom_stats_snapshot_get();
372         if (sq == NULL)
373                 return (-1);
374
375         found = 0;
376         while ((gsp = geom_stats_snapshot_next(sq)) != NULL) {
377                 gid = geom_lookupid(&gm, gsp->id);
378                 if (gid == NULL)
379                         continue;
380                 if (gid->lg_what == ISPROVIDER) {
381                         pp = gid->lg_ptr;
382                         gg = pp->lg_geom;
383                         gcl = gg->lg_class;
384                         if (strcmp(gcl->lg_name, CLASS_NAME_MD) != 0)
385                                 continue;
386                         if ((opt & OPT_UNIT) && (units != NULL)) {
387                                 retcode = md_find(units, pp->lg_name);
388                                 if (retcode != 1)
389                                         continue;
390                                 else
391                                         found = 1;
392                         }
393                         gc = &pp->lg_config;
394                         if (nflag && strncmp(pp->lg_name, "md", 2) == 0)
395                                 printf("%s", pp->lg_name + 2);
396                         else
397                                 printf("%s", pp->lg_name);
398
399                         if (opt & OPT_VERBOSE || opt & OPT_UNIT) {
400                                 type = geom_config_get(gc, "type");
401                                 if (strcmp(type, "vnode") == 0)
402                                         file = geom_config_get(gc, "file");
403                                 length = geom_config_get(gc, "length");
404                                 printf("\t%s\t", type);
405                                 if (length != NULL)
406                                         md_prthumanval(length);
407                                 if (file != NULL) {
408                                         printf("\t%s", file);
409                                         file = NULL;
410                                 }
411                         }
412                         opt |= OPT_DONE;
413                         if ((opt & OPT_LIST) && !(opt & OPT_VERBOSE))
414                                 printf(" ");
415                         else
416                                 printf("\n");
417                 }
418         }
419         if ((opt & OPT_LIST) && (opt & OPT_DONE) && !(opt & OPT_VERBOSE))
420                 printf("\n");
421         /* XXX: Check if it's enough to clean everything. */
422         geom_stats_snapshot_free(sq);
423         if ((opt & OPT_UNIT) && found)
424                 return (0);
425         else
426                 return (-1);
427 }
428
429 /*
430  * Returns value of 'name' from gconfig structure.
431  */
432 static char *
433 geom_config_get(struct gconf *g, const char *name)
434 {
435         struct gconfig *gce;
436
437         LIST_FOREACH(gce, g, lg_config) {
438                 if (strcmp(gce->lg_name, name) == 0)
439                         return (gce->lg_val);
440         }
441         return (NULL);
442 }
443
444 /*
445  * List is comma separated list of MD disks. name is a
446  * device name we look for.  Returns 1 if found and 0
447  * otherwise.
448  */
449 static int
450 md_find(char *list, const char *name)
451 {
452         int ret;
453         char num[16];
454         char *ptr, *p, *u;
455
456         ret = 0;
457         ptr = strdup(list);
458         if (ptr == NULL)
459                 return (-1);
460         for (p = ptr; (u = strsep(&p, ",")) != NULL;) {
461                 if (strncmp(u, "/dev/", 5) == 0)
462                         u += 5;
463                 /* Just in case user specified number instead of full name */
464                 snprintf(num, sizeof(num), "md%s", u);
465                 if (strcmp(u, name) == 0 || strcmp(num, name) == 0) {
466                         ret = 1;
467                         break;
468                 }
469         }
470         free(ptr);
471         return (ret);
472 }
473
474 static void
475 md_prthumanval(char *length)
476 {
477         char buf[6];
478         uintmax_t bytes;
479         char *endptr;
480
481         errno = 0;
482         bytes = strtoumax(length, &endptr, 10);
483         if (errno != 0 || *endptr != '\0' || bytes > INT64_MAX)
484                 return;
485         humanize_number(buf, sizeof(buf), (int64_t)bytes, "",
486             HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
487         (void)printf("%6s", buf);
488 }
489
490 int
491 md_query(char *name)
492 {
493         return (md_list(name, OPT_UNIT));
494 }