]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/geom/core/geom.c
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / sbin / geom / core / geom.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2004-2009 Pawel Jakub Dawidek <pjd@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/linker.h>
34 #include <sys/module.h>
35 #include <sys/stat.h>
36 #include <sys/sysctl.h>
37 #include <ctype.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <stdarg.h>
43 #include <stdbool.h>
44 #include <stdint.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <libgen.h>
48 #include <libutil.h>
49 #include <inttypes.h>
50 #include <dlfcn.h>
51 #include <assert.h>
52 #include <libgeom.h>
53 #include <geom.h>
54
55 #include "misc/subr.h"
56
57 #ifdef STATIC_GEOM_CLASSES
58 extern uint32_t gpart_version;
59 extern struct g_command gpart_class_commands[];
60 extern uint32_t glabel_version;
61 extern struct g_command glabel_class_commands[];
62 #endif
63
64 static char comm[MAXPATHLEN], *class_name = NULL, *gclass_name = NULL;
65 static uint32_t *version = NULL;
66 static int verbose = 0;
67 static struct g_command *class_commands = NULL;
68
69 #define GEOM_CLASS_CMDS         0x01
70 #define GEOM_STD_CMDS           0x02
71
72 #define GEOM_CLASS_WIDTH        10
73
74 static struct g_command *find_command(const char *cmdstr, int flags);
75 static void list_one_geom_by_provider(const char *provider_name);
76 static int std_available(const char *name);
77
78 static void std_help(struct gctl_req *req, unsigned flags);
79 static void std_list(struct gctl_req *req, unsigned flags);
80 static void std_status(struct gctl_req *req, unsigned flags);
81 static void std_load(struct gctl_req *req, unsigned flags);
82 static void std_unload(struct gctl_req *req, unsigned flags);
83
84 static struct g_command std_commands[] = {
85         { "help", 0, std_help, G_NULL_OPTS, NULL },
86         { "list", 0, std_list,
87             {
88                 { 'a', "all", NULL, G_TYPE_BOOL },
89                 G_OPT_SENTINEL
90             },
91             "[-a] [name ...]"
92         },
93         { "status", 0, std_status,
94             {
95                 { 'a', "all", NULL, G_TYPE_BOOL },
96                 { 'g', "geoms", NULL, G_TYPE_BOOL },
97                 { 's', "script", NULL, G_TYPE_BOOL },
98                 G_OPT_SENTINEL
99             },
100             "[-ags] [name ...]"
101         },
102         { "load", G_FLAG_VERBOSE | G_FLAG_LOADKLD, std_load, G_NULL_OPTS,
103             NULL },
104         { "unload", G_FLAG_VERBOSE, std_unload, G_NULL_OPTS, NULL },
105         G_CMD_SENTINEL
106 };
107
108 static void
109 usage_command(struct g_command *cmd, const char *prefix)
110 {
111         struct g_option *opt;
112         unsigned i;
113
114         if (cmd->gc_usage != NULL) {
115                 char *pos, *ptr, *sptr;
116
117                 sptr = ptr = strdup(cmd->gc_usage);
118                 while ((pos = strsep(&ptr, "\n")) != NULL) {
119                         if (*pos == '\0')
120                                 continue;
121                         fprintf(stderr, "%s %s %s %s\n", prefix, comm,
122                             cmd->gc_name, pos);
123                 }
124                 free(sptr);
125                 return;
126         }
127
128         fprintf(stderr, "%s %s %s", prefix, comm, cmd->gc_name);
129         if ((cmd->gc_flags & G_FLAG_VERBOSE) != 0)
130                 fprintf(stderr, " [-v]");
131         for (i = 0; ; i++) {
132                 opt = &cmd->gc_options[i];
133                 if (opt->go_name == NULL)
134                         break;
135                 if (opt->go_val != NULL || G_OPT_TYPE(opt) == G_TYPE_BOOL)
136                         fprintf(stderr, " [");
137                 else
138                         fprintf(stderr, " ");
139                 fprintf(stderr, "-%c", opt->go_char);
140                 if (G_OPT_TYPE(opt) != G_TYPE_BOOL)
141                         fprintf(stderr, " %s", opt->go_name);
142                 if (opt->go_val != NULL || G_OPT_TYPE(opt) == G_TYPE_BOOL)
143                         fprintf(stderr, "]");
144         }
145         fprintf(stderr, "\n");
146 }
147
148 static void
149 usage(void)
150 {
151
152         if (class_name == NULL) {
153                 fprintf(stderr, "usage: geom <class> <command> [options]\n");
154                 fprintf(stderr, "       geom -p <provider-name>\n");
155                 fprintf(stderr, "       geom -t\n");
156                 exit(EXIT_FAILURE);
157         } else {
158                 struct g_command *cmd;
159                 const char *prefix;
160                 unsigned i;
161
162                 prefix = "usage:";
163                 if (class_commands != NULL) {
164                         for (i = 0; ; i++) {
165                                 cmd = &class_commands[i];
166                                 if (cmd->gc_name == NULL)
167                                         break;
168                                 usage_command(cmd, prefix);
169                                 prefix = "      ";
170                         }
171                 }
172                 for (i = 0; ; i++) {
173                         cmd = &std_commands[i];
174                         if (cmd->gc_name == NULL)
175                                 break;
176                         /*
177                          * If class defines command, which has the same name as
178                          * standard command, skip it, because it was already
179                          * shown on usage().
180                          */
181                         if (find_command(cmd->gc_name, GEOM_CLASS_CMDS) != NULL)
182                                 continue;
183                         usage_command(cmd, prefix);
184                         prefix = "      ";
185                 }
186                 exit(EXIT_FAILURE);
187         }
188 }
189
190 static void
191 load_module(void)
192 {
193         char name1[64], name2[64];
194
195         snprintf(name1, sizeof(name1), "g_%s", class_name);
196         snprintf(name2, sizeof(name2), "geom_%s", class_name);
197         if (modfind(name1) < 0) {
198                 /* Not present in kernel, try loading it. */
199                 if (kldload(name2) < 0 || modfind(name1) < 0) {
200                         if (errno != EEXIST) {
201                                 err(EXIT_FAILURE, "cannot load %s", name2);
202                         }
203                 }
204         }
205 }
206
207 static int
208 strlcatf(char *str, size_t size, const char *format, ...)
209 {
210         size_t len;
211         va_list ap;
212         int ret;
213
214         len = strlen(str);
215         str += len;
216         size -= len;
217
218         va_start(ap, format);
219         ret = vsnprintf(str, size, format, ap);
220         va_end(ap);
221
222         return (ret);
223 }
224
225 /*
226  * Find given option in options available for given command.
227  */
228 static struct g_option *
229 find_option(struct g_command *cmd, char ch)
230 {
231         struct g_option *opt;
232         unsigned i;
233
234         for (i = 0; ; i++) {
235                 opt = &cmd->gc_options[i];
236                 if (opt->go_name == NULL)
237                         return (NULL);
238                 if (opt->go_char == ch)
239                         return (opt);
240         }
241         /* NOTREACHED */
242         return (NULL);
243 }
244
245 /*
246  * Add given option to gctl_req.
247  */
248 static void
249 set_option(struct gctl_req *req, struct g_option *opt, const char *val)
250 {
251         const char *optname;
252         uint64_t number;
253         void *ptr;
254
255         if (G_OPT_ISMULTI(opt)) {
256                 size_t optnamesize;
257
258                 if (G_OPT_NUM(opt) == UCHAR_MAX)
259                         errx(EXIT_FAILURE, "Too many -%c options.", opt->go_char);
260
261                 /*
262                  * Base option name length plus 3 bytes for option number
263                  * (max. 255 options) plus 1 byte for terminating '\0'.
264                  */
265                 optnamesize = strlen(opt->go_name) + 3 + 1;
266                 ptr = malloc(optnamesize);
267                 if (ptr == NULL)
268                         errx(EXIT_FAILURE, "No memory.");
269                 snprintf(ptr, optnamesize, "%s%u", opt->go_name, G_OPT_NUM(opt));
270                 G_OPT_NUMINC(opt);
271                 optname = ptr;
272         } else {
273                 optname = opt->go_name;
274         }
275
276         if (G_OPT_TYPE(opt) == G_TYPE_NUMBER) {
277                 if (expand_number(val, &number) == -1) {
278                         err(EXIT_FAILURE, "Invalid value for '%c' argument",
279                             opt->go_char);
280                 }
281                 ptr = malloc(sizeof(intmax_t));
282                 if (ptr == NULL)
283                         errx(EXIT_FAILURE, "No memory.");
284                 *(intmax_t *)ptr = number;
285                 opt->go_val = ptr;
286                 gctl_ro_param(req, optname, sizeof(intmax_t), opt->go_val);
287         } else if (G_OPT_TYPE(opt) == G_TYPE_STRING) {
288                 gctl_ro_param(req, optname, -1, val);
289         } else if (G_OPT_TYPE(opt) == G_TYPE_BOOL) {
290                 ptr = malloc(sizeof(int));
291                 if (ptr == NULL)
292                         errx(EXIT_FAILURE, "No memory.");
293                 *(int *)ptr = *val - '0';
294                 opt->go_val = ptr;
295                 gctl_ro_param(req, optname, sizeof(int), opt->go_val);
296         } else {
297                 assert(!"Invalid type");
298         }
299
300         if (G_OPT_ISMULTI(opt))
301                 free(__DECONST(char *, optname));
302 }
303
304 /*
305  * 1. Add given argument by caller.
306  * 2. Add default values of not given arguments.
307  * 3. Add the rest of arguments.
308  */
309 static void
310 parse_arguments(struct g_command *cmd, struct gctl_req *req, int *argc,
311     char ***argv)
312 {
313         struct g_option *opt;
314         char opts[64];
315         unsigned i;
316         int ch;
317
318         *opts = '\0';
319         if ((cmd->gc_flags & G_FLAG_VERBOSE) != 0)
320                 strlcat(opts, "v", sizeof(opts));
321         for (i = 0; ; i++) {
322                 opt = &cmd->gc_options[i];
323                 if (opt->go_name == NULL)
324                         break;
325                 assert(G_OPT_TYPE(opt) != 0);
326                 assert((opt->go_type & ~(G_TYPE_MASK | G_TYPE_MULTI)) == 0);
327                 /* Multiple bool arguments makes no sense. */
328                 assert(G_OPT_TYPE(opt) != G_TYPE_BOOL ||
329                     (opt->go_type & G_TYPE_MULTI) == 0);
330                 strlcatf(opts, sizeof(opts), "%c", opt->go_char);
331                 if (G_OPT_TYPE(opt) != G_TYPE_BOOL)
332                         strlcat(opts, ":", sizeof(opts));
333         }
334
335         /*
336          * Add specified arguments.
337          */
338         while ((ch = getopt(*argc, *argv, opts)) != -1) {
339                 /* Standard (not passed to kernel) options. */
340                 switch (ch) {
341                 case 'v':
342                         verbose = 1;
343                         continue;
344                 }
345                 /* Options passed to kernel. */
346                 opt = find_option(cmd, ch);
347                 if (opt == NULL)
348                         usage();
349                 if (!G_OPT_ISMULTI(opt) && G_OPT_ISDONE(opt)) {
350                         warnx("Option '%c' specified twice.", opt->go_char);
351                         usage();
352                 }
353                 G_OPT_DONE(opt);
354
355                 if (G_OPT_TYPE(opt) == G_TYPE_BOOL)
356                         set_option(req, opt, "1");
357                 else
358                         set_option(req, opt, optarg);
359         }
360         *argc -= optind;
361         *argv += optind;
362
363         /*
364          * Add not specified arguments, but with default values.
365          */
366         for (i = 0; ; i++) {
367                 opt = &cmd->gc_options[i];
368                 if (opt->go_name == NULL)
369                         break;
370                 if (G_OPT_ISDONE(opt))
371                         continue;
372
373                 if (G_OPT_TYPE(opt) == G_TYPE_BOOL) {
374                         assert(opt->go_val == NULL);
375                         set_option(req, opt, "0");
376                 } else {
377                         if (opt->go_val == NULL) {
378                                 warnx("Option '%c' not specified.",
379                                     opt->go_char);
380                                 usage();
381                         } else if (opt->go_val == G_VAL_OPTIONAL) {
382                                 /* add nothing. */
383                         } else {
384                                 set_option(req, opt, opt->go_val);
385                         }
386                 }
387         }
388
389         /*
390          * Add rest of given arguments.
391          */
392         gctl_ro_param(req, "nargs", sizeof(int), argc);
393         for (i = 0; i < (unsigned)*argc; i++) {
394                 char argname[16];
395
396                 snprintf(argname, sizeof(argname), "arg%u", i);
397                 gctl_ro_param(req, argname, -1, (*argv)[i]);
398         }
399 }
400
401 /*
402  * Find given command in commands available for given class.
403  */
404 static struct g_command *
405 find_command(const char *cmdstr, int flags)
406 {
407         struct g_command *cmd;
408         unsigned i;
409
410         /*
411          * First try to find command defined by loaded library.
412          */
413         if ((flags & GEOM_CLASS_CMDS) != 0 && class_commands != NULL) {
414                 for (i = 0; ; i++) {
415                         cmd = &class_commands[i];
416                         if (cmd->gc_name == NULL)
417                                 break;
418                         if (strcmp(cmd->gc_name, cmdstr) == 0)
419                                 return (cmd);
420                 }
421         }
422         /*
423          * Now try to find in standard commands.
424          */
425         if ((flags & GEOM_STD_CMDS) != 0) {
426                 for (i = 0; ; i++) {
427                         cmd = &std_commands[i];
428                         if (cmd->gc_name == NULL)
429                                 break;
430                         if (strcmp(cmd->gc_name, cmdstr) == 0)
431                                 return (cmd);
432                 }
433         }
434         return (NULL);
435 }
436
437 static unsigned
438 set_flags(struct g_command *cmd)
439 {
440         unsigned flags = 0;
441
442         if ((cmd->gc_flags & G_FLAG_VERBOSE) != 0 && verbose)
443                 flags |= G_FLAG_VERBOSE;
444
445         return (flags);
446 }
447
448 /*
449  * Run command.
450  */
451 static void
452 run_command(int argc, char *argv[])
453 {
454         struct g_command *cmd;
455         struct gctl_req *req;
456         const char *errstr;
457         char buf[4096];
458
459         /* First try to find a command defined by a class. */
460         cmd = find_command(argv[0], GEOM_CLASS_CMDS);
461         if (cmd == NULL) {
462                 /* Now, try to find a standard command. */
463                 cmd = find_command(argv[0], GEOM_STD_CMDS);
464                 if (cmd == NULL) {
465                         warnx("Unknown command: %s.", argv[0]);
466                         usage();
467                 }
468                 if (!std_available(cmd->gc_name)) {
469                         warnx("Command '%s' not available; "
470                             "try 'load' first.", argv[0]);
471                         exit(EXIT_FAILURE);
472                 }
473         }
474         if ((cmd->gc_flags & G_FLAG_LOADKLD) != 0)
475                 load_module();
476
477         req = gctl_get_handle();
478         gctl_ro_param(req, "class", -1, gclass_name);
479         gctl_ro_param(req, "verb", -1, argv[0]);
480         if (version != NULL)
481                 gctl_ro_param(req, "version", sizeof(*version), version);
482         parse_arguments(cmd, req, &argc, &argv);
483
484         bzero(buf, sizeof(buf));
485         if (cmd->gc_func != NULL) {
486                 unsigned flags;
487
488                 flags = set_flags(cmd);
489                 cmd->gc_func(req, flags);
490                 errstr = req->error;
491         } else {
492                 gctl_rw_param(req, "output", sizeof(buf), buf);
493                 errstr = gctl_issue(req);
494         }
495         if (errstr != NULL && errstr[0] != '\0') {
496                 warnx("%s", errstr);
497                 if (strncmp(errstr, "warning: ", strlen("warning: ")) != 0) {
498                         gctl_free(req);
499                         exit(EXIT_FAILURE);
500                 }
501         }
502         if (buf[0] != '\0')
503                 printf("%s", buf);
504         gctl_free(req);
505         if (verbose)
506                 printf("Done.\n");
507         exit(EXIT_SUCCESS);
508 }
509
510 #ifndef STATIC_GEOM_CLASSES
511 static const char *
512 library_path(void)
513 {
514         const char *path;
515
516         path = getenv("GEOM_LIBRARY_PATH");
517         if (path == NULL)
518                 path = GEOM_CLASS_DIR;
519         return (path);
520 }
521
522 static void
523 load_library(void)
524 {
525         char *curpath, path[MAXPATHLEN], *tofree, *totalpath;
526         uint32_t *lib_version;
527         void *dlh;
528         int ret;
529
530         ret = 0;
531         tofree = totalpath = strdup(library_path());
532         if (totalpath == NULL)
533                 err(EXIT_FAILURE, "Not enough memory for library path");
534
535         if (strchr(totalpath, ':') != NULL)
536                 curpath = strsep(&totalpath, ":");
537         else
538                 curpath = totalpath;
539         /* Traverse the paths to find one that contains the library we want. */
540         while (curpath != NULL) {
541                 snprintf(path, sizeof(path), "%s/geom_%s.so", curpath,
542                     class_name);
543                 ret = access(path, F_OK);
544                 if (ret == -1) {
545                         if (errno == ENOENT) {
546                                 /*
547                                  * If we cannot find library, try the next
548                                  * path.
549                                  */
550                                 curpath = strsep(&totalpath, ":");
551                                 continue;
552                         }
553                         err(EXIT_FAILURE, "Cannot access library");
554                 }
555                 break;
556         }
557         free(tofree);
558         /* No library was found, but standard commands can still be used */
559         if (ret == -1)
560                 return;
561         dlh = dlopen(path, RTLD_NOW);
562         if (dlh == NULL)
563                 errx(EXIT_FAILURE, "Cannot open library: %s.", dlerror());
564         lib_version = dlsym(dlh, "lib_version");
565         if (lib_version == NULL) {
566                 warnx("Cannot find symbol %s: %s.", "lib_version", dlerror());
567                 dlclose(dlh);
568                 exit(EXIT_FAILURE);
569         }
570         if (*lib_version != G_LIB_VERSION) {
571                 dlclose(dlh);
572                 errx(EXIT_FAILURE, "%s and %s are not synchronized.",
573                     getprogname(), path);
574         }
575         version = dlsym(dlh, "version");
576         if (version == NULL) {
577                 warnx("Cannot find symbol %s: %s.", "version", dlerror());
578                 dlclose(dlh);
579                 exit(EXIT_FAILURE);
580         }
581         class_commands = dlsym(dlh, "class_commands");
582         if (class_commands == NULL) {
583                 warnx("Cannot find symbol %s: %s.", "class_commands",
584                     dlerror());
585                 dlclose(dlh);
586                 exit(EXIT_FAILURE);
587         }
588 }
589 #endif  /* !STATIC_GEOM_CLASSES */
590
591 /*
592  * Class name should be all capital letters.
593  */
594 static void
595 set_class_name(void)
596 {
597         char *s1, *s2;
598
599         s1 = class_name;
600         for (; *s1 != '\0'; s1++)
601                 *s1 = tolower(*s1);
602         gclass_name = malloc(strlen(class_name) + 1);
603         if (gclass_name == NULL)
604                 errx(EXIT_FAILURE, "No memory");
605         s1 = gclass_name;
606         s2 = class_name;
607         for (; *s2 != '\0'; s2++)
608                 *s1++ = toupper(*s2);
609         *s1 = '\0';
610 }
611
612 static void
613 get_class(int *argc, char ***argv)
614 {
615
616         snprintf(comm, sizeof(comm), "%s", basename((*argv)[0]));
617         if (strcmp(comm, "geom") == 0) {
618                 if (*argc < 2)
619                         usage();
620                 else if (*argc == 2) {
621                         if (strcmp((*argv)[1], "-h") == 0 ||
622                             strcmp((*argv)[1], "help") == 0) {
623                                 usage();
624                         }
625                 }
626                 strlcatf(comm, sizeof(comm), " %s", (*argv)[1]);
627                 class_name = (*argv)[1];
628                 *argc -= 2;
629                 *argv += 2;
630         } else if (*comm == 'g') {
631                 class_name = comm + 1;
632                 *argc -= 1;
633                 *argv += 1;
634         } else {
635                 errx(EXIT_FAILURE, "Invalid utility name.");
636         }
637
638 #ifndef STATIC_GEOM_CLASSES
639         load_library();
640 #else
641         if (!strcasecmp(class_name, "part")) {
642                 version = &gpart_version;
643                 class_commands = gpart_class_commands;
644         } else if (!strcasecmp(class_name, "label")) {
645                 version = &glabel_version;
646                 class_commands = glabel_class_commands;
647         }
648 #endif /* !STATIC_GEOM_CLASSES */
649
650         set_class_name();
651
652         /* If we can't load or list, it's not a class. */
653         if (!std_available("load") && !std_available("list"))
654                 errx(EXIT_FAILURE, "Invalid class name '%s'.", class_name);
655
656         if (*argc < 1)
657                 usage();
658 }
659
660 static struct ggeom *
661 find_geom_by_provider(struct gmesh *mesh, const char *name)
662 {
663         struct gclass *classp;
664         struct ggeom *gp;
665         struct gprovider *pp;
666
667         LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
668                 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
669                         LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
670                                 if (strcmp(pp->lg_name, name) == 0)
671                                         return (gp);
672                         }
673                 }
674         }
675
676         return (NULL);
677 }
678
679 static int
680 compute_tree_width_geom(struct gmesh *mesh, struct ggeom *gp, int indent)
681 {
682         struct gclass *classp2;
683         struct ggeom *gp2;
684         struct gconsumer *cp2;
685         struct gprovider *pp;
686         int max_width, width;
687
688         max_width = width = indent + strlen(gp->lg_name);
689
690         LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
691                 LIST_FOREACH(classp2, &mesh->lg_class, lg_class) {
692                         LIST_FOREACH(gp2, &classp2->lg_geom, lg_geom) {
693                                 LIST_FOREACH(cp2,
694                                     &gp2->lg_consumer, lg_consumer) {
695                                         if (pp != cp2->lg_provider)
696                                                 continue;
697                                         width = compute_tree_width_geom(mesh,
698                                             gp2, indent + 2);
699                                         if (width > max_width)
700                                                 max_width = width;
701                                 }
702                         }
703                 }
704         }
705
706         return (max_width);
707 }
708
709 static int
710 compute_tree_width(struct gmesh *mesh)
711 {
712         struct gclass *classp;
713         struct ggeom *gp;
714         int max_width, width;
715
716         max_width = width = 0;
717
718         LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
719                 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
720                         if (!LIST_EMPTY(&gp->lg_consumer))
721                                 continue;
722                         width = compute_tree_width_geom(mesh, gp, 0);
723                         if (width > max_width)
724                                 max_width = width;
725                 }
726         }
727
728         return (max_width);
729 }
730
731 static void
732 show_tree_geom(struct gmesh *mesh, struct ggeom *gp, int indent, int width)
733 {
734         struct gclass *classp2;
735         struct ggeom *gp2;
736         struct gconsumer *cp2;
737         struct gprovider *pp;
738
739         if (LIST_EMPTY(&gp->lg_provider)) {
740                 printf("%*s%-*.*s %-*.*s\n", indent, "",
741                     width - indent, width - indent, gp->lg_name,
742                     GEOM_CLASS_WIDTH, GEOM_CLASS_WIDTH, gp->lg_class->lg_name);
743                 return;
744         }
745
746         LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
747                 printf("%*s%-*.*s %-*.*s %s\n", indent, "",
748                     width - indent, width - indent, gp->lg_name,
749                     GEOM_CLASS_WIDTH, GEOM_CLASS_WIDTH, gp->lg_class->lg_name,
750                     pp->lg_name);
751
752                 LIST_FOREACH(classp2, &mesh->lg_class, lg_class) {
753                         LIST_FOREACH(gp2, &classp2->lg_geom, lg_geom) {
754                                 LIST_FOREACH(cp2,
755                                     &gp2->lg_consumer, lg_consumer) {
756                                         if (pp != cp2->lg_provider)
757                                                 continue;
758                                         show_tree_geom(mesh, gp2,
759                                             indent + 2, width);
760                                 }
761                         }
762                 }
763         }
764 }
765
766 static void
767 show_tree(void)
768 {
769         struct gmesh mesh;
770         struct gclass *classp;
771         struct ggeom *gp;
772         int error, width;
773
774         error = geom_gettree(&mesh);
775         if (error != 0)
776                 errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
777
778         width = compute_tree_width(&mesh);
779
780         printf("%-*.*s %-*.*s %s\n",
781             width, width, "Geom",
782             GEOM_CLASS_WIDTH, GEOM_CLASS_WIDTH, "Class",
783             "Provider");
784
785         LIST_FOREACH(classp, &mesh.lg_class, lg_class) {
786                 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
787                         if (!LIST_EMPTY(&gp->lg_consumer))
788                                 continue;
789                         show_tree_geom(&mesh, gp, 0, width);
790                 }
791         }
792 }
793
794 int
795 main(int argc, char *argv[])
796 {
797         char *provider_name;
798         bool tflag;
799         int ch;
800
801         provider_name = NULL;
802         tflag = false;
803
804         if (strcmp(getprogname(), "geom") == 0) {
805                 while ((ch = getopt(argc, argv, "hp:t")) != -1) {
806                         switch (ch) {
807                         case 'p':
808                                 provider_name = strdup(optarg);
809                                 if (provider_name == NULL)
810                                         err(1, "strdup");
811                                 break;
812                         case 't':
813                                 tflag = true;
814                                 break;
815                         case 'h':
816                         default:
817                                 usage();
818                         }
819                 }
820
821                 /*
822                  * Don't adjust argc and argv, it would break get_class().
823                  */
824         }
825
826         if (tflag && provider_name != NULL) {
827                 errx(EXIT_FAILURE,
828                     "At most one of -P and -t may be specified.");
829         }
830
831         if (provider_name != NULL) {
832                 list_one_geom_by_provider(provider_name);
833                 return (0);
834         }
835
836         if (tflag) {
837                 show_tree();
838                 return (0);
839         }
840
841         get_class(&argc, &argv);
842         run_command(argc, argv);
843         /* NOTREACHED */
844
845         exit(EXIT_FAILURE);
846 }
847
848 static struct gclass *
849 find_class(struct gmesh *mesh, const char *name)
850 {
851         struct gclass *classp;
852
853         LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
854                 if (strcmp(classp->lg_name, name) == 0)
855                         return (classp);
856         }
857         return (NULL);
858 }
859
860 static struct ggeom *
861 find_geom(struct gclass *classp, const char *name)
862 {
863         struct ggeom *gp;
864
865         LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
866                 if (strcmp(gp->lg_name, name) == 0)
867                         return (gp);
868         }
869         return (NULL);
870 }
871
872 static void
873 list_one_provider(struct gprovider *pp, const char *prefix)
874 {
875         struct gconfig *conf;
876         char buf[5];
877
878         printf("Name: %s\n", pp->lg_name);
879         humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "",
880             HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
881         printf("%sMediasize: %jd (%s)\n", prefix, (intmax_t)pp->lg_mediasize,
882             buf);
883         printf("%sSectorsize: %u\n", prefix, pp->lg_sectorsize);
884         if (pp->lg_stripesize > 0 || pp->lg_stripeoffset > 0) {
885                 printf("%sStripesize: %ju\n", prefix, pp->lg_stripesize);
886                 printf("%sStripeoffset: %ju\n", prefix, pp->lg_stripeoffset);
887         }
888         printf("%sMode: %s\n", prefix, pp->lg_mode);
889         LIST_FOREACH(conf, &pp->lg_config, lg_config) {
890                 printf("%s%s: %s\n", prefix, conf->lg_name, conf->lg_val);
891         }
892 }
893
894 static void
895 list_one_consumer(struct gconsumer *cp, const char *prefix)
896 {
897         struct gprovider *pp;
898         struct gconfig *conf;
899
900         pp = cp->lg_provider;
901         if (pp == NULL)
902                 printf("[no provider]\n");
903         else {
904                 char buf[5];
905
906                 printf("Name: %s\n", pp->lg_name);
907                 humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "",
908                     HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
909                 printf("%sMediasize: %jd (%s)\n", prefix,
910                     (intmax_t)pp->lg_mediasize, buf);
911                 printf("%sSectorsize: %u\n", prefix, pp->lg_sectorsize);
912                 if (pp->lg_stripesize > 0 || pp->lg_stripeoffset > 0) {
913                         printf("%sStripesize: %ju\n", prefix, pp->lg_stripesize);
914                         printf("%sStripeoffset: %ju\n", prefix, pp->lg_stripeoffset);
915                 }
916                 printf("%sMode: %s\n", prefix, cp->lg_mode);
917         }
918         LIST_FOREACH(conf, &cp->lg_config, lg_config) {
919                 printf("%s%s: %s\n", prefix, conf->lg_name, conf->lg_val);
920         }
921 }
922
923 static void
924 list_one_geom(struct ggeom *gp)
925 {
926         struct gprovider *pp;
927         struct gconsumer *cp;
928         struct gconfig *conf;
929         unsigned n;
930
931         printf("Geom name: %s\n", gp->lg_name);
932         LIST_FOREACH(conf, &gp->lg_config, lg_config) {
933                 printf("%s: %s\n", conf->lg_name, conf->lg_val);
934         }
935         if (!LIST_EMPTY(&gp->lg_provider)) {
936                 printf("Providers:\n");
937                 n = 1;
938                 LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
939                         printf("%u. ", n++);
940                         list_one_provider(pp, "   ");
941                 }
942         }
943         if (!LIST_EMPTY(&gp->lg_consumer)) {
944                 printf("Consumers:\n");
945                 n = 1;
946                 LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
947                         printf("%u. ", n++);
948                         list_one_consumer(cp, "   ");
949                 }
950         }
951         printf("\n");
952 }
953
954 static void
955 list_one_geom_by_provider(const char *provider_name)
956 {
957         struct gmesh mesh;
958         struct ggeom *gp;
959         int error;
960
961         error = geom_gettree(&mesh);
962         if (error != 0)
963                 errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
964
965         gp = find_geom_by_provider(&mesh, provider_name);
966         if (gp == NULL)
967                 errx(EXIT_FAILURE, "Cannot find provider '%s'.", provider_name);
968
969         printf("Geom class: %s\n", gp->lg_class->lg_name);
970         list_one_geom(gp);
971 }
972
973 static void
974 std_help(struct gctl_req *req __unused, unsigned flags __unused)
975 {
976
977         usage();
978 }
979
980 static int
981 std_list_available(void)
982 {
983         struct gmesh mesh;
984         struct gclass *classp;
985         int error;
986
987         error = geom_gettree(&mesh);
988         if (error != 0)
989                 errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
990         classp = find_class(&mesh, gclass_name);
991         geom_deletetree(&mesh);
992         if (classp != NULL)
993                 return (1);
994         return (0);
995 }
996
997 static void
998 std_list(struct gctl_req *req, unsigned flags __unused)
999 {
1000         struct gmesh mesh;
1001         struct gclass *classp;
1002         struct ggeom *gp;
1003         const char *name;
1004         int all, error, i, nargs;
1005
1006         error = geom_gettree(&mesh);
1007         if (error != 0)
1008                 errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
1009         classp = find_class(&mesh, gclass_name);
1010         if (classp == NULL) {
1011                 geom_deletetree(&mesh);
1012                 errx(EXIT_FAILURE, "Class '%s' not found.", gclass_name);
1013         }
1014         nargs = gctl_get_int(req, "nargs");
1015         all = gctl_get_int(req, "all");
1016         if (nargs > 0) {
1017                 for (i = 0; i < nargs; i++) {
1018                         name = gctl_get_ascii(req, "arg%d", i);
1019                         gp = find_geom(classp, name);
1020                         if (gp == NULL) {
1021                                 errx(EXIT_FAILURE, "Class '%s' does not have "
1022                                     "an instance named '%s'.",
1023                                     gclass_name, name);
1024                         }
1025                         list_one_geom(gp);
1026                 }
1027         } else {
1028                 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1029                         if (LIST_EMPTY(&gp->lg_provider) && !all)
1030                                 continue;
1031                         list_one_geom(gp);
1032                 }
1033         }
1034         geom_deletetree(&mesh);
1035 }
1036
1037 static int
1038 std_status_available(void)
1039 {
1040
1041         /* 'status' command is available when 'list' command is. */
1042         return (std_list_available());
1043 }
1044
1045 static void
1046 status_update_len(struct ggeom *gp, int *name_len, int *status_len)
1047 {
1048         struct gconfig *conf;
1049         int len;
1050
1051         assert(gp != NULL);
1052         assert(name_len != NULL);
1053         assert(status_len != NULL);
1054
1055         len = strlen(gp->lg_name);
1056         if (*name_len < len)
1057                 *name_len = len;
1058         LIST_FOREACH(conf, &gp->lg_config, lg_config) {
1059                 if (strcasecmp(conf->lg_name, "state") == 0) {
1060                         len = strlen(conf->lg_val);
1061                         if (*status_len < len)
1062                                 *status_len = len;
1063                 }
1064         }
1065 }
1066
1067 static void
1068 status_update_len_prs(struct ggeom *gp, int *name_len, int *status_len)
1069 {
1070         struct gprovider *pp;
1071         struct gconfig *conf;
1072         int len, glen;
1073
1074         assert(gp != NULL);
1075         assert(name_len != NULL);
1076         assert(status_len != NULL);
1077
1078         glen = 0;
1079         LIST_FOREACH(conf, &gp->lg_config, lg_config) {
1080                 if (strcasecmp(conf->lg_name, "state") == 0) {
1081                         glen = strlen(conf->lg_val);
1082                         break;
1083                 }
1084         }
1085         LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1086                 len = strlen(pp->lg_name);
1087                 if (*name_len < len)
1088                         *name_len = len;
1089                 len = glen;
1090                 LIST_FOREACH(conf, &pp->lg_config, lg_config) {
1091                         if (strcasecmp(conf->lg_name, "state") == 0) {
1092                                 len = strlen(conf->lg_val);
1093                                 break;
1094                         }
1095                 }
1096                 if (*status_len < len)
1097                         *status_len = len;
1098         }
1099 }
1100
1101 static char *
1102 status_one_consumer(struct gconsumer *cp)
1103 {
1104         static char buf[256];
1105         struct gprovider *pp;
1106         struct gconfig *conf;
1107         const char *state, *syncr;
1108
1109         pp = cp->lg_provider;
1110         if (pp == NULL)
1111                 return (NULL);
1112         state = NULL;
1113         syncr = NULL;
1114         LIST_FOREACH(conf, &cp->lg_config, lg_config) {
1115                 if (strcasecmp(conf->lg_name, "state") == 0)
1116                         state = conf->lg_val;
1117                 if (strcasecmp(conf->lg_name, "synchronized") == 0)
1118                         syncr = conf->lg_val;
1119         }
1120         if (state == NULL && syncr == NULL)
1121                 snprintf(buf, sizeof(buf), "%s", pp->lg_name);
1122         else if (state != NULL && syncr != NULL) {
1123                 snprintf(buf, sizeof(buf), "%s (%s, %s)", pp->lg_name,
1124                     state, syncr);
1125         } else {
1126                 snprintf(buf, sizeof(buf), "%s (%s)", pp->lg_name,
1127                     state ? state : syncr);
1128         }
1129         return (buf);
1130 }
1131
1132 static void
1133 status_one_geom(struct ggeom *gp, int script, int name_len, int status_len)
1134 {
1135         struct gconsumer *cp;
1136         struct gconfig *conf;
1137         const char *name, *status, *component;
1138         int gotone;
1139
1140         name = gp->lg_name;
1141         status = "N/A";
1142         LIST_FOREACH(conf, &gp->lg_config, lg_config) {
1143                 if (strcasecmp(conf->lg_name, "state") == 0) {
1144                         status = conf->lg_val;
1145                         break;
1146                 }
1147         }
1148         gotone = 0;
1149         LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
1150                 component = status_one_consumer(cp);
1151                 if (component == NULL)
1152                         continue;
1153                 gotone = 1;
1154                 printf("%*s  %*s  %s\n", name_len, name, status_len, status,
1155                     component);
1156                 if (!script)
1157                         name = status = "";
1158         }
1159         if (!gotone) {
1160                 printf("%*s  %*s  %s\n", name_len, name, status_len, status,
1161                     "N/A");
1162         }
1163 }
1164
1165 static void
1166 status_one_geom_prs(struct ggeom *gp, int script, int name_len, int status_len)
1167 {
1168         struct gprovider *pp;
1169         struct gconsumer *cp;
1170         struct gconfig *conf;
1171         const char *name, *status, *component;
1172         int gotone;
1173
1174         LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1175                 name = pp->lg_name;
1176                 status = "N/A";
1177                 LIST_FOREACH(conf, &gp->lg_config, lg_config) {
1178                         if (strcasecmp(conf->lg_name, "state") == 0) {
1179                                 status = conf->lg_val;
1180                                 break;
1181                         }
1182                 }
1183                 LIST_FOREACH(conf, &pp->lg_config, lg_config) {
1184                         if (strcasecmp(conf->lg_name, "state") == 0) {
1185                                 status = conf->lg_val;
1186                                 break;
1187                         }
1188                 }
1189                 gotone = 0;
1190                 LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
1191                         component = status_one_consumer(cp);
1192                         if (component == NULL)
1193                                 continue;
1194                         gotone = 1;
1195                         printf("%*s  %*s  %s\n", name_len, name,
1196                             status_len, status, component);
1197                         if (!script)
1198                                 name = status = "";
1199                 }
1200                 if (!gotone) {
1201                         printf("%*s  %*s  %s\n", name_len, name,
1202                             status_len, status, "N/A");
1203                 }
1204         }
1205 }
1206
1207 static void
1208 std_status(struct gctl_req *req, unsigned flags __unused)
1209 {
1210         struct gmesh mesh;
1211         struct gclass *classp;
1212         struct ggeom *gp;
1213         const char *name;
1214         int name_len, status_len;
1215         int all, error, geoms, i, n, nargs, script;
1216
1217         error = geom_gettree(&mesh);
1218         if (error != 0)
1219                 errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
1220         classp = find_class(&mesh, gclass_name);
1221         if (classp == NULL)
1222                 errx(EXIT_FAILURE, "Class %s not found.", gclass_name);
1223         nargs = gctl_get_int(req, "nargs");
1224         all = gctl_get_int(req, "all");
1225         geoms = gctl_get_int(req, "geoms");
1226         script = gctl_get_int(req, "script");
1227         if (script) {
1228                 name_len = 0;
1229                 status_len = 0;
1230         } else {
1231                 name_len = strlen("Name");
1232                 status_len = strlen("Status");
1233         }
1234         if (nargs > 0) {
1235                 for (i = 0, n = 0; i < nargs; i++) {
1236                         name = gctl_get_ascii(req, "arg%d", i);
1237                         gp = find_geom(classp, name);
1238                         if (gp == NULL)
1239                                 errx(EXIT_FAILURE, "No such geom: %s.", name);
1240                         if (geoms) {
1241                                 status_update_len(gp,
1242                                     &name_len, &status_len);
1243                         } else {
1244                                 status_update_len_prs(gp,
1245                                     &name_len, &status_len);
1246                         }
1247                         n++;
1248                 }
1249                 if (n == 0)
1250                         goto end;
1251         } else {
1252                 n = 0;
1253                 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1254                         if (LIST_EMPTY(&gp->lg_provider) && !all)
1255                                 continue;
1256                         if (geoms) {
1257                                 status_update_len(gp,
1258                                     &name_len, &status_len);
1259                         } else {
1260                                 status_update_len_prs(gp,
1261                                     &name_len, &status_len);
1262                         }
1263                         n++;
1264                 }
1265                 if (n == 0)
1266                         goto end;
1267         }
1268         if (!script) {
1269                 printf("%*s  %*s  %s\n", name_len, "Name", status_len, "Status",
1270                     "Components");
1271         }
1272         if (nargs > 0) {
1273                 for (i = 0; i < nargs; i++) {
1274                         name = gctl_get_ascii(req, "arg%d", i);
1275                         gp = find_geom(classp, name);
1276                         if (gp == NULL)
1277                                 continue;
1278                         if (geoms) {
1279                                 status_one_geom(gp, script, name_len,
1280                                     status_len);
1281                         } else {
1282                                 status_one_geom_prs(gp, script, name_len,
1283                                     status_len);
1284                         }
1285                 }
1286         } else {
1287                 LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1288                         if (LIST_EMPTY(&gp->lg_provider) && !all)
1289                                 continue;
1290                         if (geoms) {
1291                                 status_one_geom(gp, script, name_len,
1292                                     status_len);
1293                         } else {
1294                                 status_one_geom_prs(gp, script, name_len,
1295                                     status_len);
1296                         }
1297                 }
1298         }
1299 end:
1300         geom_deletetree(&mesh);
1301 }
1302
1303 static int
1304 std_load_available(void)
1305 {
1306         char name[MAXPATHLEN], paths[MAXPATHLEN * 8], *p;
1307         struct stat sb;
1308         size_t len;
1309
1310         snprintf(name, sizeof(name), "g_%s", class_name);
1311         /*
1312          * If already in kernel, "load" command is not available.
1313          */
1314         if (modfind(name) >= 0)
1315                 return (0);
1316         bzero(paths, sizeof(paths));
1317         len = sizeof(paths);
1318         if (sysctlbyname("kern.module_path", paths, &len, NULL, 0) < 0)
1319                 err(EXIT_FAILURE, "sysctl(kern.module_path)");
1320         for (p = strtok(paths, ";"); p != NULL; p = strtok(NULL, ";")) {
1321                 snprintf(name, sizeof(name), "%s/geom_%s.ko", p, class_name);
1322                 /*
1323                  * If geom_<name>.ko file exists, "load" command is available.
1324                  */
1325                 if (stat(name, &sb) == 0)
1326                         return (1);
1327         }
1328         return (0);
1329 }
1330
1331 static void
1332 std_load(struct gctl_req *req __unused, unsigned flags)
1333 {
1334
1335         /*
1336          * Do nothing special here, because of G_FLAG_LOADKLD flag,
1337          * module is already loaded.
1338          */
1339         if ((flags & G_FLAG_VERBOSE) != 0)
1340                 printf("Module available.\n");
1341 }
1342
1343 static int
1344 std_unload_available(void)
1345 {
1346         char name[64];
1347         int id;
1348
1349         snprintf(name, sizeof(name), "geom_%s", class_name);
1350         id = kldfind(name);
1351         if (id >= 0)
1352                 return (1);
1353         return (0);
1354 }
1355
1356 static void
1357 std_unload(struct gctl_req *req, unsigned flags __unused)
1358 {
1359         char name[64];
1360         int id;
1361
1362         snprintf(name, sizeof(name), "geom_%s", class_name);
1363         id = kldfind(name);
1364         if (id < 0) {
1365                 gctl_error(req, "Could not find module: %s.", strerror(errno));
1366                 return;
1367         }
1368         if (kldunload(id) < 0) {
1369                 gctl_error(req, "Could not unload module: %s.",
1370                     strerror(errno));
1371                 return;
1372         }
1373 }
1374
1375 static int
1376 std_available(const char *name)
1377 {
1378
1379         if (strcmp(name, "help") == 0)
1380                 return (1);
1381         else if (strcmp(name, "list") == 0)
1382                 return (std_list_available());
1383         else if (strcmp(name, "status") == 0)
1384                 return (std_status_available());
1385         else if (strcmp(name, "load") == 0)
1386                 return (std_load_available());
1387         else if (strcmp(name, "unload") == 0)
1388                 return (std_unload_available());
1389         else
1390                 assert(!"Unknown standard command.");
1391         return (0);
1392 }