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