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