1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
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/param.h>
30#include <sys/linker.h>
31#include <sys/module.h>
32#include <sys/stat.h>
33#include <sys/sysctl.h>
34#include <ctype.h>
35#include <err.h>
36#include <errno.h>
37#include <paths.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <stdarg.h>
41#include <stdbool.h>
42#include <stdint.h>
43#include <string.h>
44#include <unistd.h>
45#include <libgen.h>
46#include <libutil.h>
47#include <inttypes.h>
48#include <dlfcn.h>
49#include <assert.h>
50#include <libgeom.h>
51#include <geom.h>
52
53#include "misc/subr.h"
54
55#ifdef STATIC_GEOM_CLASSES
56extern uint32_t gpart_version;
57extern struct g_command gpart_class_commands[];
58extern uint32_t glabel_version;
59extern struct g_command glabel_class_commands[];
60#endif
61
62static char comm[MAXPATHLEN], *class_name = NULL, *gclass_name = NULL;
63static uint32_t *version = NULL;
64static int verbose = 0;
65static struct g_command *class_commands = NULL;
66
67#define	GEOM_CLASS_CMDS		0x01
68#define	GEOM_STD_CMDS		0x02
69
70#define	GEOM_CLASS_WIDTH	10
71
72static struct g_command *find_command(const char *cmdstr, int flags);
73static void list_one_geom_by_provider(const char *provider_name);
74static int std_available(const char *name);
75static int std_list_available(void);
76static int std_load_available(void);
77
78static void std_help(struct gctl_req *req, unsigned flags);
79static void std_list(struct gctl_req *req, unsigned flags);
80static void std_status(struct gctl_req *req, unsigned flags);
81static void std_load(struct gctl_req *req, unsigned flags);
82static void std_unload(struct gctl_req *req, unsigned flags);
83
84static 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
108static void
109usage_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
148static void
149usage(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
190static void
191load_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
207static int
208strlcatf(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 */
228static struct g_option *
229find_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 */
248static void
249set_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 */
309static void
310parse_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, vcount;
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	vcount = 0;
339	while ((ch = getopt(*argc, *argv, opts)) != -1) {
340		/* Standard (not passed to kernel) options. */
341		if (ch == 'v' && (cmd->gc_flags & G_FLAG_VERBOSE) != 0)
342			verbose = 1;
343		/* Options passed to kernel. */
344		opt = find_option(cmd, ch);
345		if (opt == NULL) {
346			if (ch == 'v' && (cmd->gc_flags & G_FLAG_VERBOSE) != 0){
347				if (++vcount < 2)
348					continue;
349				else
350					warnx("Option 'v' specified twice.");
351			}
352			usage();
353		}
354		if (!G_OPT_ISMULTI(opt) && G_OPT_ISDONE(opt)) {
355			warnx("Option '%c' specified twice.", opt->go_char);
356			usage();
357		}
358		G_OPT_DONE(opt);
359
360		if (G_OPT_TYPE(opt) == G_TYPE_BOOL)
361			set_option(req, opt, "1");
362		else
363			set_option(req, opt, optarg);
364	}
365	*argc -= optind;
366	*argv += optind;
367
368	/*
369	 * Add not specified arguments, but with default values.
370	 */
371	for (i = 0; ; i++) {
372		opt = &cmd->gc_options[i];
373		if (opt->go_name == NULL)
374			break;
375		if (G_OPT_ISDONE(opt))
376			continue;
377
378		if (G_OPT_TYPE(opt) == G_TYPE_BOOL) {
379			assert(opt->go_val == NULL);
380			set_option(req, opt, "0");
381		} else {
382			if (opt->go_val == NULL) {
383				warnx("Option '%c' not specified.",
384				    opt->go_char);
385				usage();
386			} else if (opt->go_val == G_VAL_OPTIONAL) {
387				/* add nothing. */
388			} else {
389				set_option(req, opt, opt->go_val);
390			}
391		}
392	}
393
394	/*
395	 * Add rest of given arguments.
396	 */
397	gctl_ro_param(req, "nargs", sizeof(int), argc);
398	for (i = 0; i < (unsigned)*argc; i++) {
399		char argname[16];
400
401		snprintf(argname, sizeof(argname), "arg%u", i);
402		gctl_ro_param(req, argname, -1, (*argv)[i]);
403	}
404}
405
406/*
407 * Find given command in commands available for given class.
408 */
409static struct g_command *
410find_command(const char *cmdstr, int flags)
411{
412	struct g_command *cmd;
413	unsigned i;
414
415	/*
416	 * First try to find command defined by loaded library.
417	 */
418	if ((flags & GEOM_CLASS_CMDS) != 0 && class_commands != NULL) {
419		for (i = 0; ; i++) {
420			cmd = &class_commands[i];
421			if (cmd->gc_name == NULL)
422				break;
423			if (strcmp(cmd->gc_name, cmdstr) == 0)
424				return (cmd);
425		}
426	}
427	/*
428	 * Now try to find in standard commands.
429	 */
430	if ((flags & GEOM_STD_CMDS) != 0) {
431		for (i = 0; ; i++) {
432			cmd = &std_commands[i];
433			if (cmd->gc_name == NULL)
434				break;
435			if (strcmp(cmd->gc_name, cmdstr) == 0)
436				return (cmd);
437		}
438	}
439	return (NULL);
440}
441
442static unsigned
443set_flags(struct g_command *cmd)
444{
445	unsigned flags = 0;
446
447	if ((cmd->gc_flags & G_FLAG_VERBOSE) != 0 && verbose)
448		flags |= G_FLAG_VERBOSE;
449
450	return (flags);
451}
452
453/*
454 * Run command.
455 */
456static void
457run_command(int argc, char *argv[])
458{
459	struct g_command *cmd;
460	struct gctl_req *req;
461	const char *errstr;
462	char buf[4096];
463
464	/* First try to find a command defined by a class. */
465	cmd = find_command(argv[0], GEOM_CLASS_CMDS);
466	if (cmd == NULL) {
467		/* Now, try to find a standard command. */
468		cmd = find_command(argv[0], GEOM_STD_CMDS);
469		if (cmd == NULL) {
470			warnx("Unknown command: %s.", argv[0]);
471			usage();
472		}
473		if (!std_available(cmd->gc_name)) {
474			warnx("Command '%s' not available; "
475			    "try 'load' first.", argv[0]);
476			exit(EXIT_FAILURE);
477		}
478	}
479	if ((cmd->gc_flags & G_FLAG_LOADKLD) != 0)
480		load_module();
481
482	req = gctl_get_handle();
483	gctl_ro_param(req, "class", -1, gclass_name);
484	gctl_ro_param(req, "verb", -1, argv[0]);
485	if (version != NULL)
486		gctl_ro_param(req, "version", sizeof(*version), version);
487	parse_arguments(cmd, req, &argc, &argv);
488
489	buf[0] = '\0';
490	if (cmd->gc_func != NULL) {
491		unsigned flags;
492
493		flags = set_flags(cmd);
494		cmd->gc_func(req, flags);
495		errstr = req->error;
496	} else {
497		gctl_add_param(req, "output", sizeof(buf), buf,
498		    GCTL_PARAM_WR | GCTL_PARAM_ASCII);
499		errstr = gctl_issue(req);
500	}
501	if (errstr != NULL && errstr[0] != '\0') {
502		warnx("%s", errstr);
503		/* Suppress EXIT_FAILURE for warnings */
504		if (strncmp(errstr, "warning: ", strlen("warning: ")) == 0)
505			req->nerror = 0;
506		if (req->nerror != 0) {
507			gctl_free(req);
508			exit(EXIT_FAILURE);
509		}
510	}
511	if (buf[0] != '\0')
512		printf("%s", buf);
513	gctl_free(req);
514	if (verbose)
515		printf("Done.\n");
516	exit(EXIT_SUCCESS);
517}
518
519#ifndef STATIC_GEOM_CLASSES
520static const char *
521library_path(void)
522{
523	const char *path;
524
525	path = getenv("GEOM_LIBRARY_PATH");
526	if (path == NULL)
527		path = GEOM_CLASS_DIR;
528	return (path);
529}
530
531static void
532load_library(void)
533{
534	char *curpath, path[MAXPATHLEN], *tofree, *totalpath;
535	uint32_t *lib_version;
536	void *dlh;
537	int ret;
538
539	ret = 0;
540	tofree = totalpath = strdup(library_path());
541	if (totalpath == NULL)
542		err(EXIT_FAILURE, "Not enough memory for library path");
543
544	if (strchr(totalpath, ':') != NULL)
545		curpath = strsep(&totalpath, ":");
546	else
547		curpath = totalpath;
548	/* Traverse the paths to find one that contains the library we want. */
549	while (curpath != NULL) {
550		snprintf(path, sizeof(path), "%s/geom_%s.so", curpath,
551		    class_name);
552		ret = access(path, F_OK);
553		if (ret == -1) {
554			if (errno == ENOENT) {
555				/*
556				 * If we cannot find library, try the next
557				 * path.
558				 */
559				curpath = strsep(&totalpath, ":");
560				continue;
561			}
562			err(EXIT_FAILURE, "Cannot access library");
563		}
564		break;
565	}
566	free(tofree);
567	/* No library was found, but standard commands can still be used */
568	if (ret == -1)
569		return;
570	dlh = dlopen(path, RTLD_NOW);
571	if (dlh == NULL)
572		errx(EXIT_FAILURE, "Cannot open library: %s.", dlerror());
573	lib_version = dlsym(dlh, "lib_version");
574	if (lib_version == NULL) {
575		warnx("Cannot find symbol %s: %s.", "lib_version", dlerror());
576		dlclose(dlh);
577		exit(EXIT_FAILURE);
578	}
579	if (*lib_version != G_LIB_VERSION) {
580		dlclose(dlh);
581		errx(EXIT_FAILURE, "%s and %s are not synchronized.",
582		    getprogname(), path);
583	}
584	version = dlsym(dlh, "version");
585	if (version == NULL) {
586		warnx("Cannot find symbol %s: %s.", "version", dlerror());
587		dlclose(dlh);
588		exit(EXIT_FAILURE);
589	}
590	class_commands = dlsym(dlh, "class_commands");
591	if (class_commands == NULL) {
592		warnx("Cannot find symbol %s: %s.", "class_commands",
593		    dlerror());
594		dlclose(dlh);
595		exit(EXIT_FAILURE);
596	}
597}
598#endif	/* !STATIC_GEOM_CLASSES */
599
600/*
601 * Class name should be all capital letters.
602 */
603static void
604set_class_name(void)
605{
606	char *s1, *s2;
607
608	s1 = class_name;
609	for (; *s1 != '\0'; s1++)
610		*s1 = tolower(*s1);
611	gclass_name = malloc(strlen(class_name) + 1);
612	if (gclass_name == NULL)
613		errx(EXIT_FAILURE, "No memory");
614	s1 = gclass_name;
615	s2 = class_name;
616	for (; *s2 != '\0'; s2++)
617		*s1++ = toupper(*s2);
618	*s1 = '\0';
619}
620
621static void
622get_class(int *argc, char ***argv)
623{
624
625	snprintf(comm, sizeof(comm), "%s", basename((*argv)[0]));
626	if (strcmp(comm, "geom") == 0) {
627		if (*argc < 2)
628			usage();
629		else if (*argc == 2) {
630			if (strcmp((*argv)[1], "-h") == 0 ||
631			    strcmp((*argv)[1], "help") == 0) {
632				usage();
633			}
634		}
635		strlcatf(comm, sizeof(comm), " %s", (*argv)[1]);
636		class_name = (*argv)[1];
637		*argc -= 2;
638		*argv += 2;
639	} else if (*comm == 'g') {
640		class_name = comm + 1;
641		*argc -= 1;
642		*argv += 1;
643	} else {
644		errx(EXIT_FAILURE, "Invalid utility name.");
645	}
646
647#ifndef STATIC_GEOM_CLASSES
648	load_library();
649#else
650	if (!strcasecmp(class_name, "part")) {
651		version = &gpart_version;
652		class_commands = gpart_class_commands;
653	} else if (!strcasecmp(class_name, "label")) {
654		version = &glabel_version;
655		class_commands = glabel_class_commands;
656	}
657#endif /* !STATIC_GEOM_CLASSES */
658
659	set_class_name();
660
661	/* If we can't load or list, it's not a class. */
662	if (!std_load_available() && !std_list_available())
663		errx(EXIT_FAILURE, "Invalid class name '%s'.", class_name);
664
665	if (*argc < 1)
666		usage();
667}
668
669static struct ggeom *
670find_geom_by_provider(struct gmesh *mesh, const char *name)
671{
672	struct gclass *classp;
673	struct ggeom *gp;
674	struct gprovider *pp;
675
676	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
677		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
678			LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
679				if (strcmp(pp->lg_name, name) == 0)
680					return (gp);
681			}
682		}
683	}
684
685	return (NULL);
686}
687
688static int
689compute_tree_width_geom(struct gmesh *mesh, struct ggeom *gp, int indent)
690{
691	struct gclass *classp2;
692	struct ggeom *gp2;
693	struct gconsumer *cp2;
694	struct gprovider *pp;
695	int max_width, width;
696
697	max_width = width = indent + strlen(gp->lg_name);
698
699	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
700		LIST_FOREACH(classp2, &mesh->lg_class, lg_class) {
701			LIST_FOREACH(gp2, &classp2->lg_geom, lg_geom) {
702				LIST_FOREACH(cp2,
703				    &gp2->lg_consumer, lg_consumer) {
704					if (pp != cp2->lg_provider)
705						continue;
706					width = compute_tree_width_geom(mesh,
707					    gp2, indent + 2);
708					if (width > max_width)
709						max_width = width;
710				}
711			}
712		}
713	}
714
715	return (max_width);
716}
717
718static int
719compute_tree_width(struct gmesh *mesh)
720{
721	struct gclass *classp;
722	struct ggeom *gp;
723	int max_width, width;
724
725	max_width = width = 0;
726
727	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
728		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
729			if (!LIST_EMPTY(&gp->lg_consumer))
730				continue;
731			width = compute_tree_width_geom(mesh, gp, 0);
732			if (width > max_width)
733				max_width = width;
734		}
735	}
736
737	return (max_width);
738}
739
740static void
741show_tree_geom(struct gmesh *mesh, struct ggeom *gp, int indent, int width)
742{
743	struct gclass *classp2;
744	struct ggeom *gp2;
745	struct gconsumer *cp2;
746	struct gprovider *pp;
747
748	if (LIST_EMPTY(&gp->lg_provider)) {
749		printf("%*s%-*.*s %-*.*s\n", indent, "",
750		    width - indent, width - indent, gp->lg_name,
751		    GEOM_CLASS_WIDTH, GEOM_CLASS_WIDTH, gp->lg_class->lg_name);
752		return;
753	}
754
755	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
756		printf("%*s%-*.*s %-*.*s %s\n", indent, "",
757		    width - indent, width - indent, gp->lg_name,
758		    GEOM_CLASS_WIDTH, GEOM_CLASS_WIDTH, gp->lg_class->lg_name,
759		    pp->lg_name);
760
761		LIST_FOREACH(classp2, &mesh->lg_class, lg_class) {
762			LIST_FOREACH(gp2, &classp2->lg_geom, lg_geom) {
763				LIST_FOREACH(cp2,
764				    &gp2->lg_consumer, lg_consumer) {
765					if (pp != cp2->lg_provider)
766						continue;
767					show_tree_geom(mesh, gp2,
768					    indent + 2, width);
769				}
770			}
771		}
772	}
773}
774
775static void
776show_tree(void)
777{
778	struct gmesh mesh;
779	struct gclass *classp;
780	struct ggeom *gp;
781	int error, width;
782
783	error = geom_gettree(&mesh);
784	if (error != 0)
785		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
786
787	width = compute_tree_width(&mesh);
788
789	printf("%-*.*s %-*.*s %s\n",
790	    width, width, "Geom",
791	    GEOM_CLASS_WIDTH, GEOM_CLASS_WIDTH, "Class",
792	    "Provider");
793
794	LIST_FOREACH(classp, &mesh.lg_class, lg_class) {
795		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
796			if (!LIST_EMPTY(&gp->lg_consumer))
797				continue;
798			show_tree_geom(&mesh, gp, 0, width);
799		}
800	}
801}
802
803int
804main(int argc, char *argv[])
805{
806	char *provider_name;
807	bool tflag;
808	int ch;
809
810	provider_name = NULL;
811	tflag = false;
812
813	if (strcmp(getprogname(), "geom") == 0) {
814		while ((ch = getopt(argc, argv, "hp:t")) != -1) {
815			switch (ch) {
816			case 'p':
817				provider_name = strdup(optarg);
818				if (provider_name == NULL)
819					err(1, "strdup");
820				break;
821			case 't':
822				tflag = true;
823				break;
824			case 'h':
825			default:
826				usage();
827			}
828		}
829
830		/*
831		 * Don't adjust argc and argv, it would break get_class().
832		 */
833	}
834
835	if (tflag && provider_name != NULL) {
836		errx(EXIT_FAILURE,
837		    "At most one of -P and -t may be specified.");
838	}
839
840	if (provider_name != NULL) {
841		list_one_geom_by_provider(provider_name);
842		return (0);
843	}
844
845	if (tflag) {
846		show_tree();
847		return (0);
848	}
849
850	get_class(&argc, &argv);
851	run_command(argc, argv);
852	/* NOTREACHED */
853
854	exit(EXIT_FAILURE);
855}
856
857static struct gclass *
858find_class(struct gmesh *mesh, const char *name)
859{
860	struct gclass *classp;
861
862	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
863		if (strcmp(classp->lg_name, name) == 0)
864			return (classp);
865	}
866	return (NULL);
867}
868
869static struct ggeom *
870find_geom(struct gclass *classp, const char *name)
871{
872	struct ggeom *gp;
873
874	if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
875		name += sizeof(_PATH_DEV) - 1;
876
877	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
878		if (strcmp(gp->lg_name, name) == 0)
879			return (gp);
880	}
881	return (NULL);
882}
883
884static void
885list_one_provider(struct gprovider *pp, const char *prefix)
886{
887	struct gconfig *conf;
888	char buf[5];
889
890	printf("Name: %s\n", pp->lg_name);
891	humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "",
892	    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
893	printf("%sMediasize: %jd (%s)\n", prefix, (intmax_t)pp->lg_mediasize,
894	    buf);
895	printf("%sSectorsize: %u\n", prefix, pp->lg_sectorsize);
896	if (pp->lg_stripesize > 0 || pp->lg_stripeoffset > 0) {
897		printf("%sStripesize: %ju\n", prefix, pp->lg_stripesize);
898		printf("%sStripeoffset: %ju\n", prefix, pp->lg_stripeoffset);
899	}
900	printf("%sMode: %s\n", prefix, pp->lg_mode);
901	LIST_FOREACH(conf, &pp->lg_config, lg_config) {
902		printf("%s%s: %s\n", prefix, conf->lg_name, conf->lg_val);
903	}
904}
905
906static void
907list_one_consumer(struct gconsumer *cp, const char *prefix)
908{
909	struct gprovider *pp;
910	struct gconfig *conf;
911
912	pp = cp->lg_provider;
913	if (pp == NULL)
914		printf("[no provider]\n");
915	else {
916		char buf[5];
917
918		printf("Name: %s\n", pp->lg_name);
919		humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "",
920		    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
921		printf("%sMediasize: %jd (%s)\n", prefix,
922		    (intmax_t)pp->lg_mediasize, buf);
923		printf("%sSectorsize: %u\n", prefix, pp->lg_sectorsize);
924		if (pp->lg_stripesize > 0 || pp->lg_stripeoffset > 0) {
925			printf("%sStripesize: %ju\n", prefix, pp->lg_stripesize);
926			printf("%sStripeoffset: %ju\n", prefix, pp->lg_stripeoffset);
927		}
928		printf("%sMode: %s\n", prefix, cp->lg_mode);
929	}
930	LIST_FOREACH(conf, &cp->lg_config, lg_config) {
931		printf("%s%s: %s\n", prefix, conf->lg_name, conf->lg_val);
932	}
933}
934
935static void
936list_one_geom(struct ggeom *gp)
937{
938	struct gprovider *pp;
939	struct gconsumer *cp;
940	struct gconfig *conf;
941	unsigned n;
942
943	printf("Geom name: %s\n", gp->lg_name);
944	LIST_FOREACH(conf, &gp->lg_config, lg_config) {
945		printf("%s: %s\n", conf->lg_name, conf->lg_val);
946	}
947	if (!LIST_EMPTY(&gp->lg_provider)) {
948		printf("Providers:\n");
949		n = 1;
950		LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
951			printf("%u. ", n++);
952			list_one_provider(pp, "   ");
953		}
954	}
955	if (!LIST_EMPTY(&gp->lg_consumer)) {
956		printf("Consumers:\n");
957		n = 1;
958		LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
959			printf("%u. ", n++);
960			list_one_consumer(cp, "   ");
961		}
962	}
963	printf("\n");
964}
965
966static void
967list_one_geom_by_provider(const char *provider_name)
968{
969	struct gmesh mesh;
970	struct ggeom *gp;
971	int error;
972
973	error = geom_gettree(&mesh);
974	if (error != 0)
975		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
976
977	gp = find_geom_by_provider(&mesh, provider_name);
978	if (gp == NULL)
979		errx(EXIT_FAILURE, "Cannot find provider '%s'.", provider_name);
980
981	printf("Geom class: %s\n", gp->lg_class->lg_name);
982	list_one_geom(gp);
983}
984
985static void
986std_help(struct gctl_req *req __unused, unsigned flags __unused)
987{
988
989	usage();
990}
991
992static int
993std_list_available(void)
994{
995	struct gmesh mesh;
996	struct gclass *classp;
997	int error;
998
999	error = geom_gettree_geom(&mesh, gclass_name, "", 0);
1000	if (error != 0)
1001		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
1002	classp = find_class(&mesh, gclass_name);
1003	geom_deletetree(&mesh);
1004	if (classp != NULL)
1005		return (1);
1006	return (0);
1007}
1008
1009static void
1010std_list(struct gctl_req *req, unsigned flags __unused)
1011{
1012	struct gmesh mesh;
1013	struct gclass *classp;
1014	struct ggeom *gp;
1015	const char *name;
1016	int all, error, i, nargs;
1017
1018	nargs = gctl_get_int(req, "nargs");
1019	if (nargs == 1) {
1020		error = geom_gettree_geom(&mesh, gclass_name,
1021		    gctl_get_ascii(req, "arg0"), 1);
1022	} else
1023		error = geom_gettree(&mesh);
1024	if (error != 0)
1025		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
1026	classp = find_class(&mesh, gclass_name);
1027	if (classp == NULL) {
1028		geom_deletetree(&mesh);
1029		errx(EXIT_FAILURE, "Class '%s' not found.", gclass_name);
1030	}
1031	all = gctl_get_int(req, "all");
1032	if (nargs > 0) {
1033		for (i = 0; i < nargs; i++) {
1034			name = gctl_get_ascii(req, "arg%d", i);
1035			gp = find_geom(classp, name);
1036			if (gp == NULL) {
1037				errx(EXIT_FAILURE, "Class '%s' does not have "
1038				    "an instance named '%s'.",
1039				    gclass_name, name);
1040			}
1041			list_one_geom(gp);
1042		}
1043	} else {
1044		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1045			if (LIST_EMPTY(&gp->lg_provider) && !all)
1046				continue;
1047			list_one_geom(gp);
1048		}
1049	}
1050	geom_deletetree(&mesh);
1051}
1052
1053static int
1054std_status_available(void)
1055{
1056
1057	/* 'status' command is available when 'list' command is. */
1058	return (std_list_available());
1059}
1060
1061static void
1062status_update_len(struct ggeom *gp, int *name_len, int *status_len)
1063{
1064	struct gconfig *conf;
1065	int len;
1066
1067	assert(gp != NULL);
1068	assert(name_len != NULL);
1069	assert(status_len != NULL);
1070
1071	len = strlen(gp->lg_name);
1072	if (*name_len < len)
1073		*name_len = len;
1074	LIST_FOREACH(conf, &gp->lg_config, lg_config) {
1075		if (strcasecmp(conf->lg_name, "state") == 0) {
1076			len = strlen(conf->lg_val);
1077			if (*status_len < len)
1078				*status_len = len;
1079		}
1080	}
1081}
1082
1083static void
1084status_update_len_prs(struct ggeom *gp, int *name_len, int *status_len)
1085{
1086	struct gprovider *pp;
1087	struct gconfig *conf;
1088	int len, glen;
1089
1090	assert(gp != NULL);
1091	assert(name_len != NULL);
1092	assert(status_len != NULL);
1093
1094	glen = 0;
1095	LIST_FOREACH(conf, &gp->lg_config, lg_config) {
1096		if (strcasecmp(conf->lg_name, "state") == 0) {
1097			glen = strlen(conf->lg_val);
1098			break;
1099		}
1100	}
1101	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1102		len = strlen(pp->lg_name);
1103		if (*name_len < len)
1104			*name_len = len;
1105		len = glen;
1106		LIST_FOREACH(conf, &pp->lg_config, lg_config) {
1107			if (strcasecmp(conf->lg_name, "state") == 0) {
1108				len = strlen(conf->lg_val);
1109				break;
1110			}
1111		}
1112		if (*status_len < len)
1113			*status_len = len;
1114	}
1115}
1116
1117static char *
1118status_one_consumer(struct gconsumer *cp)
1119{
1120	static char buf[256];
1121	struct gprovider *pp;
1122	struct gconfig *conf;
1123	const char *state, *syncr;
1124
1125	pp = cp->lg_provider;
1126	if (pp == NULL)
1127		return (NULL);
1128	state = NULL;
1129	syncr = NULL;
1130	LIST_FOREACH(conf, &cp->lg_config, lg_config) {
1131		if (strcasecmp(conf->lg_name, "state") == 0)
1132			state = conf->lg_val;
1133		if (strcasecmp(conf->lg_name, "synchronized") == 0)
1134			syncr = conf->lg_val;
1135	}
1136	if (state == NULL && syncr == NULL)
1137		snprintf(buf, sizeof(buf), "%s", pp->lg_name);
1138	else if (state != NULL && syncr != NULL) {
1139		snprintf(buf, sizeof(buf), "%s (%s, %s)", pp->lg_name,
1140		    state, syncr);
1141	} else {
1142		snprintf(buf, sizeof(buf), "%s (%s)", pp->lg_name,
1143		    state ? state : syncr);
1144	}
1145	return (buf);
1146}
1147
1148static void
1149status_one_geom(struct ggeom *gp, int script, int name_len, int status_len)
1150{
1151	struct gconsumer *cp;
1152	struct gconfig *conf;
1153	const char *name, *status, *component;
1154	int gotone;
1155
1156	name = gp->lg_name;
1157	status = "N/A";
1158	LIST_FOREACH(conf, &gp->lg_config, lg_config) {
1159		if (strcasecmp(conf->lg_name, "state") == 0) {
1160			status = conf->lg_val;
1161			break;
1162		}
1163	}
1164	gotone = 0;
1165	LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
1166		component = status_one_consumer(cp);
1167		if (component == NULL)
1168			continue;
1169		gotone = 1;
1170		printf("%*s  %*s  %s\n", name_len, name, status_len, status,
1171		    component);
1172		if (!script)
1173			name = status = "";
1174	}
1175	if (!gotone) {
1176		printf("%*s  %*s  %s\n", name_len, name, status_len, status,
1177		    "N/A");
1178	}
1179}
1180
1181static void
1182status_one_geom_prs(struct ggeom *gp, int script, int name_len, int status_len)
1183{
1184	struct gprovider *pp;
1185	struct gconsumer *cp;
1186	struct gconfig *conf;
1187	const char *name, *status, *component;
1188	int gotone;
1189
1190	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1191		name = pp->lg_name;
1192		status = "N/A";
1193		LIST_FOREACH(conf, &gp->lg_config, lg_config) {
1194			if (strcasecmp(conf->lg_name, "state") == 0) {
1195				status = conf->lg_val;
1196				break;
1197			}
1198		}
1199		LIST_FOREACH(conf, &pp->lg_config, lg_config) {
1200			if (strcasecmp(conf->lg_name, "state") == 0) {
1201				status = conf->lg_val;
1202				break;
1203			}
1204		}
1205		gotone = 0;
1206		LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
1207			component = status_one_consumer(cp);
1208			if (component == NULL)
1209				continue;
1210			gotone = 1;
1211			printf("%*s  %*s  %s\n", name_len, name,
1212			    status_len, status, component);
1213			if (!script)
1214				name = status = "";
1215		}
1216		if (!gotone) {
1217			printf("%*s  %*s  %s\n", name_len, name,
1218			    status_len, status, "N/A");
1219		}
1220	}
1221}
1222
1223static void
1224std_status(struct gctl_req *req, unsigned flags __unused)
1225{
1226	struct gmesh mesh;
1227	struct gclass *classp;
1228	struct ggeom *gp;
1229	const char *name;
1230	int name_len, status_len;
1231	int all, error, geoms, i, n, nargs, script;
1232
1233	error = geom_gettree(&mesh);
1234	if (error != 0)
1235		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
1236	classp = find_class(&mesh, gclass_name);
1237	if (classp == NULL)
1238		errx(EXIT_FAILURE, "Class %s not found.", gclass_name);
1239	nargs = gctl_get_int(req, "nargs");
1240	all = gctl_get_int(req, "all");
1241	geoms = gctl_get_int(req, "geoms");
1242	script = gctl_get_int(req, "script");
1243	if (script) {
1244		name_len = 0;
1245		status_len = 0;
1246	} else {
1247		name_len = strlen("Name");
1248		status_len = strlen("Status");
1249	}
1250	if (nargs > 0) {
1251		for (i = 0, n = 0; i < nargs; i++) {
1252			name = gctl_get_ascii(req, "arg%d", i);
1253			gp = find_geom(classp, name);
1254			if (gp == NULL)
1255				errx(EXIT_FAILURE, "No such geom: %s.", name);
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	} else {
1268		n = 0;
1269		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1270			if (LIST_EMPTY(&gp->lg_provider) && !all)
1271				continue;
1272			if (geoms) {
1273				status_update_len(gp,
1274				    &name_len, &status_len);
1275			} else {
1276				status_update_len_prs(gp,
1277				    &name_len, &status_len);
1278			}
1279			n++;
1280		}
1281		if (n == 0)
1282			goto end;
1283	}
1284	if (!script) {
1285		printf("%*s  %*s  %s\n", name_len, "Name", status_len, "Status",
1286		    "Components");
1287	}
1288	if (nargs > 0) {
1289		for (i = 0; i < nargs; i++) {
1290			name = gctl_get_ascii(req, "arg%d", i);
1291			gp = find_geom(classp, name);
1292			if (gp == NULL)
1293				continue;
1294			if (geoms) {
1295				status_one_geom(gp, script, name_len,
1296				    status_len);
1297			} else {
1298				status_one_geom_prs(gp, script, name_len,
1299				    status_len);
1300			}
1301		}
1302	} else {
1303		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1304			if (LIST_EMPTY(&gp->lg_provider) && !all)
1305				continue;
1306			if (geoms) {
1307				status_one_geom(gp, script, name_len,
1308				    status_len);
1309			} else {
1310				status_one_geom_prs(gp, script, name_len,
1311				    status_len);
1312			}
1313		}
1314	}
1315end:
1316	geom_deletetree(&mesh);
1317}
1318
1319static int
1320std_load_available(void)
1321{
1322	char name[MAXPATHLEN], paths[MAXPATHLEN * 8], *p;
1323	struct stat sb;
1324	size_t len;
1325
1326	snprintf(name, sizeof(name), "g_%s", class_name);
1327	/*
1328	 * If already in kernel, "load" command is NOP.
1329	 */
1330	if (modfind(name) >= 0)
1331		return (1);
1332	bzero(paths, sizeof(paths));
1333	len = sizeof(paths);
1334	if (sysctlbyname("kern.module_path", paths, &len, NULL, 0) < 0)
1335		err(EXIT_FAILURE, "sysctl(kern.module_path)");
1336	for (p = strtok(paths, ";"); p != NULL; p = strtok(NULL, ";")) {
1337		snprintf(name, sizeof(name), "%s/geom_%s.ko", p, class_name);
1338		/*
1339		 * If geom_<name>.ko file exists, "load" command is available.
1340		 */
1341		if (stat(name, &sb) == 0)
1342			return (1);
1343	}
1344	return (0);
1345}
1346
1347static void
1348std_load(struct gctl_req *req __unused, unsigned flags)
1349{
1350
1351	/*
1352	 * Do nothing special here, because of G_FLAG_LOADKLD flag,
1353	 * module is already loaded.
1354	 */
1355	if ((flags & G_FLAG_VERBOSE) != 0)
1356		printf("Module available.\n");
1357}
1358
1359static int
1360std_unload_available(void)
1361{
1362	char name[64];
1363	int id;
1364
1365	snprintf(name, sizeof(name), "geom_%s", class_name);
1366	id = kldfind(name);
1367	if (id >= 0)
1368		return (1);
1369	return (0);
1370}
1371
1372static void
1373std_unload(struct gctl_req *req, unsigned flags __unused)
1374{
1375	char name[64];
1376	int id;
1377
1378	snprintf(name, sizeof(name), "geom_%s", class_name);
1379	id = kldfind(name);
1380	if (id < 0) {
1381		gctl_error(req, "Could not find module: %s.", strerror(errno));
1382		return;
1383	}
1384	if (kldunload(id) < 0) {
1385		gctl_error(req, "Could not unload module: %s.",
1386		    strerror(errno));
1387		return;
1388	}
1389}
1390
1391static int
1392std_available(const char *name)
1393{
1394
1395	if (strcmp(name, "help") == 0)
1396		return (1);
1397	else if (strcmp(name, "list") == 0)
1398		return (std_list_available());
1399	else if (strcmp(name, "status") == 0)
1400		return (std_status_available());
1401	else if (strcmp(name, "load") == 0)
1402		return (std_load_available());
1403	else if (strcmp(name, "unload") == 0)
1404		return (std_unload_available());
1405	else
1406		assert(!"Unknown standard command.");
1407	return (0);
1408}
1409