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	} else
639		errx(EXIT_FAILURE, "Invalid class name.");
640#endif /* !STATIC_GEOM_CLASSES */
641
642	set_class_name();
643	if (*argc < 1)
644		usage();
645}
646
647int
648main(int argc, char *argv[])
649{
650
651	get_class(&argc, &argv);
652	run_command(argc, argv);
653	/* NOTREACHED */
654
655	exit(EXIT_FAILURE);
656}
657
658static struct gclass *
659find_class(struct gmesh *mesh, const char *name)
660{
661	struct gclass *classp;
662
663	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
664		if (strcmp(classp->lg_name, name) == 0)
665			return (classp);
666	}
667	return (NULL);
668}
669
670static struct ggeom *
671find_geom(struct gclass *classp, const char *name)
672{
673	struct ggeom *gp;
674
675	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
676		if (strcmp(gp->lg_name, name) == 0)
677			return (gp);
678	}
679	return (NULL);
680}
681
682static void
683list_one_provider(struct gprovider *pp, const char *prefix)
684{
685	struct gconfig *conf;
686	char buf[5];
687
688	printf("Name: %s\n", pp->lg_name);
689	humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "",
690	    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
691	printf("%sMediasize: %jd (%s)\n", prefix, (intmax_t)pp->lg_mediasize,
692	    buf);
693	printf("%sSectorsize: %u\n", prefix, pp->lg_sectorsize);
694	if (pp->lg_stripesize > 0 || pp->lg_stripeoffset > 0) {
695		printf("%sStripesize: %ju\n", prefix, pp->lg_stripesize);
696		printf("%sStripeoffset: %ju\n", prefix, pp->lg_stripeoffset);
697	}
698	printf("%sMode: %s\n", prefix, pp->lg_mode);
699	LIST_FOREACH(conf, &pp->lg_config, lg_config) {
700		printf("%s%s: %s\n", prefix, conf->lg_name, conf->lg_val);
701	}
702}
703
704static void
705list_one_consumer(struct gconsumer *cp, const char *prefix)
706{
707	struct gprovider *pp;
708	struct gconfig *conf;
709
710	pp = cp->lg_provider;
711	if (pp == NULL)
712		printf("[no provider]\n");
713	else {
714		char buf[5];
715
716		printf("Name: %s\n", pp->lg_name);
717		humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "",
718		    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
719		printf("%sMediasize: %jd (%s)\n", prefix,
720		    (intmax_t)pp->lg_mediasize, buf);
721		printf("%sSectorsize: %u\n", prefix, pp->lg_sectorsize);
722		if (pp->lg_stripesize > 0 || pp->lg_stripeoffset > 0) {
723			printf("%sStripesize: %ju\n", prefix, pp->lg_stripesize);
724			printf("%sStripeoffset: %ju\n", prefix, pp->lg_stripeoffset);
725		}
726		printf("%sMode: %s\n", prefix, cp->lg_mode);
727	}
728	LIST_FOREACH(conf, &cp->lg_config, lg_config) {
729		printf("%s%s: %s\n", prefix, conf->lg_name, conf->lg_val);
730	}
731}
732
733static void
734list_one_geom(struct ggeom *gp)
735{
736	struct gprovider *pp;
737	struct gconsumer *cp;
738	struct gconfig *conf;
739	unsigned n;
740
741	printf("Geom name: %s\n", gp->lg_name);
742	LIST_FOREACH(conf, &gp->lg_config, lg_config) {
743		printf("%s: %s\n", conf->lg_name, conf->lg_val);
744	}
745	if (!LIST_EMPTY(&gp->lg_provider)) {
746		printf("Providers:\n");
747		n = 1;
748		LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
749			printf("%u. ", n++);
750			list_one_provider(pp, "   ");
751		}
752	}
753	if (!LIST_EMPTY(&gp->lg_consumer)) {
754		printf("Consumers:\n");
755		n = 1;
756		LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
757			printf("%u. ", n++);
758			list_one_consumer(cp, "   ");
759		}
760	}
761	printf("\n");
762}
763
764static void
765std_help(struct gctl_req *req __unused, unsigned flags __unused)
766{
767
768	usage();
769}
770
771static int
772std_list_available(void)
773{
774	struct gmesh mesh;
775	struct gclass *classp;
776	int error;
777
778	error = geom_gettree(&mesh);
779	if (error != 0)
780		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
781	classp = find_class(&mesh, gclass_name);
782	geom_deletetree(&mesh);
783	if (classp != NULL)
784		return (1);
785	return (0);
786}
787
788static void
789std_list(struct gctl_req *req, unsigned flags __unused)
790{
791	struct gmesh mesh;
792	struct gclass *classp;
793	struct ggeom *gp;
794	const char *name;
795	int all, error, i, nargs;
796
797	error = geom_gettree(&mesh);
798	if (error != 0)
799		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
800	classp = find_class(&mesh, gclass_name);
801	if (classp == NULL) {
802		geom_deletetree(&mesh);
803		errx(EXIT_FAILURE, "Class %s not found.", gclass_name);
804	}
805	nargs = gctl_get_int(req, "nargs");
806	all = gctl_get_int(req, "all");
807	if (nargs > 0) {
808		for (i = 0; i < nargs; i++) {
809			name = gctl_get_ascii(req, "arg%d", i);
810			gp = find_geom(classp, name);
811			if (gp == NULL)
812				errx(EXIT_FAILURE, "No such geom: %s.", name);
813			list_one_geom(gp);
814		}
815	} else {
816		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
817			if (LIST_EMPTY(&gp->lg_provider) && !all)
818				continue;
819			list_one_geom(gp);
820		}
821	}
822	geom_deletetree(&mesh);
823}
824
825static int
826std_status_available(void)
827{
828
829	/* 'status' command is available when 'list' command is. */
830	return (std_list_available());
831}
832
833static void
834status_update_len(struct ggeom *gp, int *name_len, int *status_len)
835{
836	struct gconfig *conf;
837	int len;
838
839	assert(gp != NULL);
840	assert(name_len != NULL);
841	assert(status_len != NULL);
842
843	len = strlen(gp->lg_name);
844	if (*name_len < len)
845		*name_len = len;
846	LIST_FOREACH(conf, &gp->lg_config, lg_config) {
847		if (strcasecmp(conf->lg_name, "state") == 0) {
848			len = strlen(conf->lg_val);
849			if (*status_len < len)
850				*status_len = len;
851		}
852	}
853}
854
855static void
856status_update_len_prs(struct ggeom *gp, int *name_len, int *status_len)
857{
858	struct gprovider *pp;
859	struct gconfig *conf;
860	int len, glen;
861
862	assert(gp != NULL);
863	assert(name_len != NULL);
864	assert(status_len != NULL);
865
866	glen = 0;
867	LIST_FOREACH(conf, &gp->lg_config, lg_config) {
868		if (strcasecmp(conf->lg_name, "state") == 0) {
869			glen = strlen(conf->lg_val);
870			break;
871		}
872	}
873	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
874		len = strlen(pp->lg_name);
875		if (*name_len < len)
876			*name_len = len;
877		len = glen;
878		LIST_FOREACH(conf, &pp->lg_config, lg_config) {
879			if (strcasecmp(conf->lg_name, "state") == 0) {
880				len = strlen(conf->lg_val);
881				break;
882			}
883		}
884		if (*status_len < len)
885			*status_len = len;
886	}
887}
888
889static char *
890status_one_consumer(struct gconsumer *cp)
891{
892	static char buf[256];
893	struct gprovider *pp;
894	struct gconfig *conf;
895	const char *state, *syncr;
896
897	pp = cp->lg_provider;
898	if (pp == NULL)
899		return (NULL);
900	state = NULL;
901	syncr = NULL;
902	LIST_FOREACH(conf, &cp->lg_config, lg_config) {
903		if (strcasecmp(conf->lg_name, "state") == 0)
904			state = conf->lg_val;
905		if (strcasecmp(conf->lg_name, "synchronized") == 0)
906			syncr = conf->lg_val;
907	}
908	if (state == NULL && syncr == NULL)
909		snprintf(buf, sizeof(buf), "%s", pp->lg_name);
910	else if (state != NULL && syncr != NULL) {
911		snprintf(buf, sizeof(buf), "%s (%s, %s)", pp->lg_name,
912		    state, syncr);
913	} else {
914		snprintf(buf, sizeof(buf), "%s (%s)", pp->lg_name,
915		    state ? state : syncr);
916	}
917	return (buf);
918}
919
920static void
921status_one_geom(struct ggeom *gp, int script, int name_len, int status_len)
922{
923	struct gconsumer *cp;
924	struct gconfig *conf;
925	const char *name, *status, *component;
926	int gotone;
927
928	name = gp->lg_name;
929	status = "N/A";
930	LIST_FOREACH(conf, &gp->lg_config, lg_config) {
931		if (strcasecmp(conf->lg_name, "state") == 0) {
932			status = conf->lg_val;
933			break;
934		}
935	}
936	gotone = 0;
937	LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
938		component = status_one_consumer(cp);
939		if (component == NULL)
940			continue;
941		gotone = 1;
942		printf("%*s  %*s  %s\n", name_len, name, status_len, status,
943		    component);
944		if (!script)
945			name = status = "";
946	}
947	if (!gotone) {
948		printf("%*s  %*s  %s\n", name_len, name, status_len, status,
949		    "N/A");
950	}
951}
952
953static void
954status_one_geom_prs(struct ggeom *gp, int script, int name_len, int status_len)
955{
956	struct gprovider *pp;
957	struct gconsumer *cp;
958	struct gconfig *conf;
959	const char *name, *status, *component;
960	int gotone;
961
962	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
963		name = pp->lg_name;
964		status = "N/A";
965		LIST_FOREACH(conf, &gp->lg_config, lg_config) {
966			if (strcasecmp(conf->lg_name, "state") == 0) {
967				status = conf->lg_val;
968				break;
969			}
970		}
971		LIST_FOREACH(conf, &pp->lg_config, lg_config) {
972			if (strcasecmp(conf->lg_name, "state") == 0) {
973				status = conf->lg_val;
974				break;
975			}
976		}
977		gotone = 0;
978		LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
979			component = status_one_consumer(cp);
980			if (component == NULL)
981				continue;
982			gotone = 1;
983			printf("%*s  %*s  %s\n", name_len, name,
984			    status_len, status, component);
985			if (!script)
986				name = status = "";
987		}
988		if (!gotone) {
989			printf("%*s  %*s  %s\n", name_len, name,
990			    status_len, status, "N/A");
991		}
992	}
993}
994
995static void
996std_status(struct gctl_req *req, unsigned flags __unused)
997{
998	struct gmesh mesh;
999	struct gclass *classp;
1000	struct ggeom *gp;
1001	const char *name;
1002	int name_len, status_len;
1003	int all, error, geoms, i, n, nargs, script;
1004
1005	error = geom_gettree(&mesh);
1006	if (error != 0)
1007		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
1008	classp = find_class(&mesh, gclass_name);
1009	if (classp == NULL)
1010		errx(EXIT_FAILURE, "Class %s not found.", gclass_name);
1011	nargs = gctl_get_int(req, "nargs");
1012	all = gctl_get_int(req, "all");
1013	geoms = gctl_get_int(req, "geoms");
1014	script = gctl_get_int(req, "script");
1015	if (script) {
1016		name_len = 0;
1017		status_len = 0;
1018	} else {
1019		name_len = strlen("Name");
1020		status_len = strlen("Status");
1021	}
1022	if (nargs > 0) {
1023		for (i = 0, n = 0; i < nargs; i++) {
1024			name = gctl_get_ascii(req, "arg%d", i);
1025			gp = find_geom(classp, name);
1026			if (gp == NULL)
1027				errx(EXIT_FAILURE, "No such geom: %s.", name);
1028			if (geoms) {
1029				status_update_len(gp,
1030				    &name_len, &status_len);
1031			} else {
1032				status_update_len_prs(gp,
1033				    &name_len, &status_len);
1034			}
1035			n++;
1036		}
1037		if (n == 0)
1038			goto end;
1039	} else {
1040		n = 0;
1041		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1042			if (LIST_EMPTY(&gp->lg_provider) && !all)
1043				continue;
1044			if (geoms) {
1045				status_update_len(gp,
1046				    &name_len, &status_len);
1047			} else {
1048				status_update_len_prs(gp,
1049				    &name_len, &status_len);
1050			}
1051			n++;
1052		}
1053		if (n == 0)
1054			goto end;
1055	}
1056	if (!script) {
1057		printf("%*s  %*s  %s\n", name_len, "Name", status_len, "Status",
1058		    "Components");
1059	}
1060	if (nargs > 0) {
1061		for (i = 0; i < nargs; i++) {
1062			name = gctl_get_ascii(req, "arg%d", i);
1063			gp = find_geom(classp, name);
1064			if (gp == NULL)
1065				continue;
1066			if (geoms) {
1067				status_one_geom(gp, script, name_len,
1068				    status_len);
1069			} else {
1070				status_one_geom_prs(gp, script, name_len,
1071				    status_len);
1072			}
1073		}
1074	} else {
1075		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
1076			if (LIST_EMPTY(&gp->lg_provider) && !all)
1077				continue;
1078			if (geoms) {
1079				status_one_geom(gp, script, name_len,
1080				    status_len);
1081			} else {
1082				status_one_geom_prs(gp, script, name_len,
1083				    status_len);
1084			}
1085		}
1086	}
1087end:
1088	geom_deletetree(&mesh);
1089}
1090
1091static int
1092std_load_available(void)
1093{
1094	char name[MAXPATHLEN], paths[MAXPATHLEN * 8], *p;
1095	struct stat sb;
1096	size_t len;
1097
1098	snprintf(name, sizeof(name), "g_%s", class_name);
1099	/*
1100	 * If already in kernel, "load" command is not available.
1101	 */
1102	if (modfind(name) >= 0)
1103		return (0);
1104	bzero(paths, sizeof(paths));
1105	len = sizeof(paths);
1106	if (sysctlbyname("kern.module_path", paths, &len, NULL, 0) < 0)
1107		err(EXIT_FAILURE, "sysctl(kern.module_path)");
1108	for (p = strtok(paths, ";"); p != NULL; p = strtok(NULL, ";")) {
1109		snprintf(name, sizeof(name), "%s/geom_%s.ko", p, class_name);
1110		/*
1111		 * If geom_<name>.ko file exists, "load" command is available.
1112		 */
1113		if (stat(name, &sb) == 0)
1114			return (1);
1115	}
1116	return (0);
1117}
1118
1119static void
1120std_load(struct gctl_req *req __unused, unsigned flags)
1121{
1122
1123	/*
1124	 * Do nothing special here, because of G_FLAG_LOADKLD flag,
1125	 * module is already loaded.
1126	 */
1127	if ((flags & G_FLAG_VERBOSE) != 0)
1128		printf("Module available.\n");
1129}
1130
1131static int
1132std_unload_available(void)
1133{
1134	char name[64];
1135	int id;
1136
1137	snprintf(name, sizeof(name), "geom_%s", class_name);
1138	id = kldfind(name);
1139	if (id >= 0)
1140		return (1);
1141	return (0);
1142}
1143
1144static void
1145std_unload(struct gctl_req *req, unsigned flags __unused)
1146{
1147	char name[64];
1148	int id;
1149
1150	snprintf(name, sizeof(name), "geom_%s", class_name);
1151	id = kldfind(name);
1152	if (id < 0) {
1153		gctl_error(req, "Could not find module: %s.", strerror(errno));
1154		return;
1155	}
1156	if (kldunload(id) < 0) {
1157		gctl_error(req, "Could not unload module: %s.",
1158		    strerror(errno));
1159		return;
1160	}
1161}
1162
1163static int
1164std_available(const char *name)
1165{
1166
1167	if (strcmp(name, "help") == 0)
1168		return (1);
1169	else if (strcmp(name, "list") == 0)
1170		return (std_list_available());
1171	else if (strcmp(name, "status") == 0)
1172		return (std_status_available());
1173	else if (strcmp(name, "load") == 0)
1174		return (std_load_available());
1175	else if (strcmp(name, "unload") == 0)
1176		return (std_unload_available());
1177	else
1178		assert(!"Unknown standard command.");
1179	return (0);
1180}
1181