geom.c revision 143558
1/*-
2 * Copyright (c) 2004-2005 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: head/sbin/geom/core/geom.c 143558 2005-03-14 08:34:02Z pjd $");
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
55static char comm[MAXPATHLEN], *class_name = NULL, *gclass_name = NULL;
56static uint32_t *version = NULL;
57static int verbose = 0;
58static struct g_command *class_commands = NULL;
59static void (*usage)(const char *name);
60
61#define	GEOM_CLASS_CMDS	0x01
62#define	GEOM_STD_CMDS	0x02
63static struct g_command *find_command(const char *cmdstr, int flags);
64static int std_available(const char *name);
65
66static void std_help(struct gctl_req *req, unsigned flags);
67static void std_list(struct gctl_req *req, unsigned flags);
68static void std_status(struct gctl_req *req, unsigned flags);
69static void std_load(struct gctl_req *req, unsigned flags);
70static void std_unload(struct gctl_req *req, unsigned flags);
71
72struct g_command std_commands[] = {
73	{ "help", 0, std_help, G_NULL_OPTS },
74	{ "list", 0, std_list, G_NULL_OPTS },
75	{ "status", 0, std_status, G_NULL_OPTS },
76	{ "load", G_FLAG_VERBOSE | G_FLAG_LOADKLD, std_load, G_NULL_OPTS },
77	{ "unload", G_FLAG_VERBOSE, std_unload, G_NULL_OPTS },
78	G_CMD_SENTINEL
79};
80
81static void
82std_usage(const char *name)
83{
84	struct g_command *cmd;
85	struct g_option *opt;
86	unsigned i, j;
87
88	for (i = 0; ; i++) {
89		cmd = &class_commands[i];
90		if (cmd->gc_name == NULL)
91			break;
92		fprintf(stderr, "%s %s %s %s", i == 0 ? "usage:" : "      ",
93		    name, class_name, cmd->gc_name);
94		if ((cmd->gc_flags & G_FLAG_VERBOSE) != 0)
95			fprintf(stderr, " [-v]");
96		for (j = 0; ; j++) {
97			opt = &cmd->gc_options[j];
98			if (opt->go_name == NULL)
99				break;
100			if (opt->go_val != NULL || opt->go_type == G_TYPE_NONE)
101				fprintf(stderr, " [");
102			else
103				fprintf(stderr, " ");
104			fprintf(stderr, "-%c", opt->go_char);
105			if (opt->go_type != G_TYPE_NONE)
106				fprintf(stderr, " %s", opt->go_name);
107			if (opt->go_val != NULL || opt->go_type == G_TYPE_NONE)
108				fprintf(stderr, "]");
109		}
110		fprintf(stderr, " ...\n");
111	}
112	exit(EXIT_FAILURE);
113}
114
115static void
116geom_usage(void)
117{
118
119	if (class_name == NULL) {
120		errx(EXIT_FAILURE, "usage: %s <class> <command> [options]",
121		    "geom");
122	} else {
123		const char *prefix;
124		unsigned i;
125
126		if (usage == NULL)
127			prefix = "usage:";
128		else {
129			usage(comm);
130			prefix = "      ";
131		}
132		for (i = 0; ; i++) {
133			struct g_command *cmd;
134
135			cmd = &std_commands[i];
136			if (cmd->gc_name == NULL)
137				break;
138			/*
139			 * If class defines command, which has the same name as
140			 * standard command, skip it, because it was already
141			 * shown on usage().
142			 */
143			if (find_command(cmd->gc_name, GEOM_CLASS_CMDS) != NULL)
144				continue;
145			fprintf(stderr, "%s %s %s", prefix, comm, cmd->gc_name);
146			if ((cmd->gc_flags & G_FLAG_VERBOSE) != 0)
147				fprintf(stderr, " [-v]");
148			fprintf(stderr, "\n");
149			prefix = "      ";
150		}
151		exit(EXIT_FAILURE);
152	}
153}
154
155static void
156load_module(void)
157{
158	char name1[64], name2[64];
159
160	snprintf(name1, sizeof(name1), "g_%s", class_name);
161	snprintf(name2, sizeof(name2), "geom_%s", class_name);
162	if (modfind(name1) < 0) {
163		/* Not present in kernel, try loading it. */
164		if (kldload(name2) < 0 || modfind(name1) < 0) {
165			if (errno != EEXIST) {
166				errx(EXIT_FAILURE,
167				    "%s module not available!", name2);
168			}
169		}
170	}
171}
172
173static int
174strlcatf(char *str, size_t size, const char *format, ...)
175{
176	size_t len;
177	va_list ap;
178	int ret;
179
180	len = strlen(str);
181	str += len;
182	size -= len;
183
184	va_start(ap, format);
185	ret = vsnprintf(str, size, format, ap);
186	va_end(ap);
187
188	return (ret);
189}
190
191/*
192 * Find given option in options available for given command.
193 */
194static struct g_option *
195find_option(struct g_command *cmd, char ch)
196{
197	struct g_option *opt;
198	unsigned i;
199
200	for (i = 0; ; i++) {
201		opt = &cmd->gc_options[i];
202		if (opt->go_name == NULL)
203			return (NULL);
204		if (opt->go_char == ch)
205			return (opt);
206	}
207	/* NOTREACHED */
208	return (NULL);
209}
210
211/*
212 * Add given option to gctl_req.
213 */
214static void
215set_option(struct gctl_req *req, struct g_option *opt, const char *val)
216{
217
218	if (opt->go_type == G_TYPE_NUMBER) {
219		intmax_t number;
220
221		errno = 0;
222		number = strtoimax(optarg, NULL, 0);
223		if (errno != 0) {
224			err(EXIT_FAILURE, "Invalid value for '%c' argument.",
225			    opt->go_char);
226		}
227		opt->go_val = malloc(sizeof(intmax_t));
228		if (opt->go_val == NULL)
229			errx(EXIT_FAILURE, "No memory.");
230		*(intmax_t *)opt->go_val = number;
231
232		gctl_ro_param(req, opt->go_name, sizeof(intmax_t), opt->go_val);
233	} else if (opt->go_type == G_TYPE_STRING) {
234		gctl_ro_param(req, opt->go_name, -1, optarg);
235	} else /* if (opt->go_type == G_TYPE_NONE) */ {
236		opt->go_val = malloc(sizeof(int));
237		if (opt->go_val == NULL)
238			errx(EXIT_FAILURE, "No memory.");
239		*(int *)opt->go_val = *val - '0';
240
241		gctl_ro_param(req, opt->go_name, sizeof(int),
242		    opt->go_val);
243	}
244}
245
246/*
247 * 1. Add given argument by caller.
248 * 2. Add default values of not given arguments.
249 * 3. Add the rest of arguments.
250 */
251static void
252parse_arguments(struct g_command *cmd, struct gctl_req *req, int *argc,
253    char ***argv)
254{
255	struct g_option *opt;
256	char opts[64];
257	unsigned i;
258	int ch;
259
260	*opts = '\0';
261	if ((cmd->gc_flags & G_FLAG_VERBOSE) != 0)
262		strlcat(opts, "v", sizeof(opts));
263	for (i = 0; ; i++) {
264		opt = &cmd->gc_options[i];
265		if (opt->go_name == NULL)
266			break;
267		strlcatf(opts, sizeof(opts), "%c", opt->go_char);
268		if (opt->go_type != G_TYPE_NONE)
269			strlcat(opts, ":", sizeof(opts));
270	}
271
272	/*
273	 * Add specified arguments.
274	 */
275	while ((ch = getopt(*argc, *argv, opts)) != -1) {
276		/* Standard (not passed to kernel) options. */
277		switch (ch) {
278		case 'v':
279			verbose = 1;
280			continue;
281		}
282		/* Options passed to kernel. */
283		opt = find_option(cmd, ch);
284		if (opt == NULL)
285			geom_usage();
286		if (G_OPT_ISDONE(opt)) {
287			fprintf(stderr, "Flag '%c' specified twice.\n",
288			    opt->go_char);
289			geom_usage();
290		}
291		G_OPT_DONE(opt);
292
293		if (opt->go_type == G_TYPE_NONE)
294			set_option(req, opt, "1");
295		else
296			set_option(req, opt, optarg);
297	}
298	*argc -= optind;
299	*argv += optind;
300
301	/*
302	 * Add not specified arguments, but with default values.
303	 */
304	for (i = 0; ; i++) {
305		opt = &cmd->gc_options[i];
306		if (opt->go_name == NULL)
307			break;
308		if (G_OPT_ISDONE(opt))
309			continue;
310
311		if (opt->go_type == G_TYPE_NONE) {
312			assert(opt->go_val == NULL);
313			set_option(req, opt, "0");
314		} else {
315			if (opt->go_val == NULL) {
316				fprintf(stderr, "Flag '%c' not specified.\n",
317				    opt->go_char);
318				geom_usage();
319			} else {
320				if (opt->go_type == G_TYPE_NUMBER) {
321					gctl_ro_param(req, opt->go_name,
322					    sizeof(intmax_t), opt->go_val);
323				} else /* if (opt->go_type == G_TYPE_STRING)*/ {
324					gctl_ro_param(req, opt->go_name, -1,
325					    opt->go_val);
326				}
327			}
328		}
329	}
330	/*
331	 * Add rest of given arguments.
332	 */
333	gctl_ro_param(req, "nargs", sizeof(int), argc);
334	for (i = 0; i < (unsigned)*argc; i++) {
335		char argname[16];
336
337		snprintf(argname, sizeof(argname), "arg%u", i);
338		gctl_ro_param(req, argname, -1, (*argv)[i]);
339	}
340}
341
342/*
343 * Find given command in commands available for given class.
344 */
345static struct g_command *
346find_command(const char *cmdstr, int flags)
347{
348	struct g_command *cmd;
349	unsigned i;
350
351	/*
352	 * First try to find command defined by loaded library.
353	 */
354	if ((flags & GEOM_CLASS_CMDS) != 0 && class_commands != NULL) {
355		for (i = 0; ; i++) {
356			cmd = &class_commands[i];
357			if (cmd->gc_name == NULL)
358				break;
359			if (strcmp(cmd->gc_name, cmdstr) == 0)
360				return (cmd);
361		}
362	}
363	/*
364	 * Now try to find in standard commands.
365	 */
366	if ((flags & GEOM_STD_CMDS) != 0) {
367		for (i = 0; ; i++) {
368			cmd = &std_commands[i];
369			if (cmd->gc_name == NULL)
370				break;
371			if (strcmp(cmd->gc_name, cmdstr) == 0)
372				return (cmd);
373		}
374	}
375	return (NULL);
376}
377
378static unsigned
379set_flags(struct g_command *cmd)
380{
381	unsigned flags = 0;
382
383	if ((cmd->gc_flags & G_FLAG_VERBOSE) != 0 && verbose)
384		flags |= G_FLAG_VERBOSE;
385
386	return (flags);
387}
388
389/*
390 * Run command.
391 */
392static void
393run_command(int argc, char *argv[])
394{
395	struct g_command *cmd;
396	struct gctl_req *req;
397	const char *errstr;
398	char buf[4096];
399
400	/* First try to find a command defined by a class. */
401	cmd = find_command(argv[0], GEOM_CLASS_CMDS);
402	if (cmd == NULL) {
403		/* Now, try to find a standard command. */
404		cmd = find_command(argv[0], GEOM_STD_CMDS);
405		if (cmd == NULL) {
406			fprintf(stderr, "Unknown command: %s\n", argv[0]);
407			geom_usage();
408		}
409		if (!std_available(cmd->gc_name)) {
410			fprintf(stderr, "Command '%s' not available.\n",
411			    argv[0]);
412			exit(EXIT_FAILURE);
413		}
414	}
415	if ((cmd->gc_flags & G_FLAG_LOADKLD) != 0)
416		load_module();
417
418	req = gctl_get_handle();
419	gctl_ro_param(req, "class", -1, gclass_name);
420	gctl_ro_param(req, "verb", -1, argv[0]);
421	if (version != NULL)
422		gctl_ro_param(req, "version", sizeof(*version), version);
423	parse_arguments(cmd, req, &argc, &argv);
424
425	if (cmd->gc_func != NULL) {
426		unsigned flags;
427
428		flags = set_flags(cmd);
429		cmd->gc_func(req, flags);
430		errstr = req->error;
431	} else {
432		bzero(buf, sizeof(buf));
433		gctl_rw_param(req, "output", sizeof(buf), buf);
434		errstr = gctl_issue(req);
435	}
436	if (errstr != NULL) {
437		fprintf(stderr, "%s\n", errstr);
438		if (strncmp(errstr, "warning: ", strlen("warning: ")) != 0) {
439			gctl_free(req);
440			exit(EXIT_FAILURE);
441		}
442	}
443	if (*buf != '\0')
444		printf("%s", buf);
445	gctl_free(req);
446	if (verbose)
447		printf("Done.\n");
448	exit(EXIT_SUCCESS);
449}
450
451static const char *
452library_path(void)
453{
454	const char *path;
455
456	path = getenv("GEOM_LIBRARY_PATH");
457	if (path == NULL)
458		path = CLASS_DIR;
459	return (path);
460}
461
462static void
463load_library(void)
464{
465	char path[MAXPATHLEN];
466	uint32_t *lib_version;
467	void *dlh;
468
469	snprintf(path, sizeof(path), "%s/geom_%s.so", library_path(),
470	    class_name);
471	dlh = dlopen(path, RTLD_NOW);
472	if (dlh == NULL) {
473#if 0
474		fprintf(stderr, "Cannot open library %s, but continuing "
475		    "anyway.\n", path);
476#endif
477		/*
478		 * Even if library cannot be loaded, standard commands are
479		 * available, so don't panic!
480		 */
481		return;
482	}
483	lib_version = dlsym(dlh, "lib_version");
484	if (lib_version == NULL) {
485		fprintf(stderr, "Cannot find symbol %s: %s.\n", "lib_version",
486		    dlerror());
487		dlclose(dlh);
488		exit(EXIT_FAILURE);
489	}
490	if (*lib_version != G_LIB_VERSION) {
491		dlclose(dlh);
492		errx(EXIT_FAILURE, "%s and %s are not synchronized.",
493		    getprogname(), path);
494	}
495	version = dlsym(dlh, "version");
496	if (version == NULL) {
497		fprintf(stderr, "Cannot find symbol %s: %s.\n", "version",
498		    dlerror());
499		dlclose(dlh);
500		exit(EXIT_FAILURE);
501	}
502	class_commands = dlsym(dlh, "class_commands");
503	if (class_commands == NULL) {
504		fprintf(stderr, "Cannot find symbol %s: %s.\n",
505		    "class_commands", dlerror());
506		dlclose(dlh);
507		exit(EXIT_FAILURE);
508	}
509	usage = dlsym(dlh, "usage");
510	if (usage == NULL)
511		usage = std_usage;
512}
513
514/*
515 * Class name should be all capital letters.
516 */
517static void
518set_class_name(void)
519{
520	char *s1, *s2;
521
522	gclass_name = malloc(strlen(class_name));
523	if (gclass_name == NULL)
524		errx(EXIT_FAILURE, "No memory");
525	s1 = gclass_name;
526	s2 = class_name;
527	for (; *s2 != '\0'; s2++)
528		*s1++ = toupper(*s2);
529	*s1 = '\0';
530}
531
532static void
533get_class(int *argc, char ***argv)
534{
535
536	snprintf(comm, sizeof(comm), "%s", basename((*argv)[0]));
537	if (strcmp(comm, "geom") == 0) {
538		if (*argc < 2)
539			geom_usage();
540		else if (*argc == 2) {
541			if (strcmp((*argv)[1], "-h") == 0 ||
542			    strcmp((*argv)[1], "help") == 0) {
543				geom_usage();
544			}
545		}
546		strlcatf(comm, sizeof(comm), " %s", (*argv)[1]);
547		class_name = (*argv)[1];
548		*argc -= 2;
549		*argv += 2;
550	} else if (*comm == 'g') {
551		class_name = comm + 1;
552		*argc -= 1;
553		*argv += 1;
554	} else {
555		errx(EXIT_FAILURE, "Invalid utility name.");
556	}
557	set_class_name();
558	load_library();
559	if (*argc < 1)
560		geom_usage();
561}
562
563int
564main(int argc, char *argv[])
565{
566
567	get_class(&argc, &argv);
568	run_command(argc, argv);
569	/* NOTREACHED */
570
571	exit(EXIT_FAILURE);
572}
573
574static struct gclass *
575find_class(struct gmesh *mesh, const char *name)
576{
577	struct gclass *classp;
578
579	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
580		if (strcmp(classp->lg_name, name) == 0)
581			return (classp);
582	}
583	return (NULL);
584}
585
586static struct ggeom *
587find_geom(struct gclass *classp, const char *name)
588{
589	struct ggeom *gp;
590
591	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
592		if (strcmp(gp->lg_name, name) == 0)
593			return (gp);
594	}
595	return (NULL);
596}
597
598static void
599list_one_provider(struct gprovider *pp, const char *prefix)
600{
601	struct gconfig *conf;
602	char buf[5];
603
604	printf("Name: %s\n", pp->lg_name);
605	humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "",
606	    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
607	printf("%sMediasize: %jd (%s)\n", prefix, (intmax_t)pp->lg_mediasize,
608	    buf);
609	printf("%sSectorsize: %u\n", prefix, pp->lg_sectorsize);
610	printf("%sMode: %s\n", prefix, pp->lg_mode);
611	LIST_FOREACH(conf, &pp->lg_config, lg_config) {
612		printf("%s%s: %s\n", prefix, conf->lg_name, conf->lg_val);
613	}
614}
615
616static void
617list_one_consumer(struct gconsumer *cp, const char *prefix)
618{
619	struct gprovider *pp;
620	struct gconfig *conf;
621
622	pp = cp->lg_provider;
623	if (pp == NULL)
624		printf("[no provider]\n");
625	else {
626		char buf[5];
627
628		printf("Name: %s\n", pp->lg_name);
629		humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "",
630		    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
631		printf("%sMediasize: %jd (%s)\n", prefix,
632		    (intmax_t)pp->lg_mediasize, buf);
633		printf("%sSectorsize: %u\n", prefix, pp->lg_sectorsize);
634		printf("%sMode: %s\n", prefix, cp->lg_mode);
635	}
636	LIST_FOREACH(conf, &cp->lg_config, lg_config) {
637		printf("%s%s: %s\n", prefix, conf->lg_name, conf->lg_val);
638	}
639}
640
641static void
642list_one_geom(struct ggeom *gp)
643{
644	struct gprovider *pp;
645	struct gconsumer *cp;
646	struct gconfig *conf;
647	unsigned n;
648
649	printf("Geom name: %s\n", gp->lg_name);
650	LIST_FOREACH(conf, &gp->lg_config, lg_config) {
651		printf("%s: %s\n", conf->lg_name, conf->lg_val);
652	}
653	if (!LIST_EMPTY(&gp->lg_provider)) {
654		printf("Providers:\n");
655		n = 1;
656		LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
657			printf("%u. ", n++);
658			list_one_provider(pp, "   ");
659		}
660	}
661	if (!LIST_EMPTY(&gp->lg_consumer)) {
662		printf("Consumers:\n");
663		n = 1;
664		LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
665			printf("%u. ", n++);
666			list_one_consumer(cp, "   ");
667		}
668	}
669	printf("\n");
670}
671
672static void
673std_help(struct gctl_req *req __unused, unsigned flags __unused)
674{
675
676	geom_usage();
677}
678
679static int
680std_list_available(void)
681{
682	struct gmesh mesh;
683	struct gclass *classp;
684	int error;
685
686	error = geom_gettree(&mesh);
687	if (error != 0) {
688		fprintf(stderr, "Cannot get GEOM tree: %s.\n", strerror(error));
689		exit(EXIT_FAILURE);
690	}
691	classp = find_class(&mesh, gclass_name);
692	geom_deletetree(&mesh);
693	if (classp != NULL)
694		return (1);
695	return (0);
696}
697
698static void
699std_list(struct gctl_req *req, unsigned flags __unused)
700{
701	struct gmesh mesh;
702	struct gclass *classp;
703	struct ggeom *gp;
704	int error, *nargs;
705
706	error = geom_gettree(&mesh);
707	if (error != 0) {
708		fprintf(stderr, "Cannot get GEOM tree: %s.\n", strerror(error));
709		exit(EXIT_FAILURE);
710	}
711	classp = find_class(&mesh, gclass_name);
712	if (classp == NULL) {
713		geom_deletetree(&mesh);
714		fprintf(stderr, "Class %s not found.\n", gclass_name);
715		return;
716	}
717	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
718	if (nargs == NULL) {
719		gctl_error(req, "No '%s' argument.", "nargs");
720		geom_deletetree(&mesh);
721		return;
722	}
723	if (*nargs > 0) {
724		int i;
725
726		for (i = 0; i < *nargs; i++) {
727			const char *name;
728			char param[16];
729
730			snprintf(param, sizeof(param), "arg%d", i);
731			name = gctl_get_asciiparam(req, param);
732			assert(name != NULL);
733			gp = find_geom(classp, name);
734			if (gp != NULL)
735				list_one_geom(gp);
736			else
737				fprintf(stderr, "No such geom: %s.\n", name);
738		}
739	} else {
740		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
741			if (LIST_EMPTY(&gp->lg_provider))
742				continue;
743			list_one_geom(gp);
744		}
745	}
746	geom_deletetree(&mesh);
747}
748
749static int
750std_status_available(void)
751{
752
753	/* 'status' command is available when 'list' command is. */
754	return (std_list_available());
755}
756
757static void
758status_update_len(struct ggeom *gp, int *name_len, int *status_len)
759{
760	struct gprovider *pp;
761	struct gconfig *conf;
762	int len;
763
764	assert(gp != NULL);
765	assert(name_len != NULL);
766	assert(status_len != NULL);
767
768	pp = LIST_FIRST(&gp->lg_provider);
769	if (pp != NULL)
770		len = strlen(pp->lg_name);
771	else
772		len = strlen(gp->lg_name);
773	if (*name_len < len)
774		*name_len = len;
775	LIST_FOREACH(conf, &gp->lg_config, lg_config) {
776		if (strcasecmp(conf->lg_name, "state") == 0) {
777			len = strlen(conf->lg_val);
778			if (*status_len < len)
779				*status_len = len;
780		}
781	}
782}
783
784static int
785status_one_consumer(struct gconsumer *cp)
786{
787	struct gprovider *pp;
788	struct gconfig *conf;
789
790	pp = cp->lg_provider;
791	if (pp == NULL)
792		return (0);
793	printf("  %s", pp->lg_name);
794	LIST_FOREACH(conf, &cp->lg_config, lg_config) {
795		if (strcasecmp(conf->lg_name, "synchronized") == 0)
796			printf(" (%s)", conf->lg_val);
797	}
798	printf("\n");
799	return (1);
800}
801
802static void
803status_one_geom(struct ggeom *gp, int name_len, int status_len)
804{
805	struct gprovider *pp;
806	struct gconsumer *cp;
807	struct gconfig *conf;
808	const char *name;
809	int newline = 0;
810
811	pp = LIST_FIRST(&gp->lg_provider);
812	if (pp != NULL)
813		name = pp->lg_name;
814	else
815		name = gp->lg_name;
816	printf("%*s", name_len, name);
817	LIST_FOREACH(conf, &gp->lg_config, lg_config) {
818		if (strcasecmp(conf->lg_name, "state") == 0) {
819			printf("  %*s", status_len, conf->lg_val);
820			break;
821		}
822	}
823	if (conf == NULL)
824		printf("  %*s", status_len, "N/A");
825	LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
826		if (cp != LIST_FIRST(&gp->lg_consumer))
827			printf("%*s  %*s", name_len, "", status_len, "");
828		if (status_one_consumer(cp) && !newline)
829			newline = 1;
830	}
831	if (!newline)
832		printf("\n");
833}
834
835static void
836std_status(struct gctl_req *req, unsigned flags __unused)
837{
838	struct gmesh mesh;
839	struct gclass *classp;
840	struct ggeom *gp;
841	int name_len, status_len;
842	int error, *nargs;
843
844	error = geom_gettree(&mesh);
845	if (error != 0) {
846		fprintf(stderr, "Cannot get GEOM tree: %s.\n", strerror(error));
847		exit(EXIT_FAILURE);
848	}
849	classp = find_class(&mesh, gclass_name);
850	if (classp == NULL) {
851		fprintf(stderr, "Class %s not found.\n", gclass_name);
852		goto end;
853	}
854	nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs));
855	if (nargs == NULL) {
856		gctl_error(req, "No '%s' argument.", "nargs");
857		goto end;
858	}
859	name_len = strlen("Name");
860	status_len = strlen("Status");
861	if (*nargs > 0) {
862		int i, n = 0;
863
864		for (i = 0; i < *nargs; i++) {
865			const char *name;
866			char param[16];
867
868			snprintf(param, sizeof(param), "arg%d", i);
869			name = gctl_get_asciiparam(req, param);
870			assert(name != NULL);
871			gp = find_geom(classp, name);
872			if (gp == NULL)
873				fprintf(stderr, "No such geom: %s.\n", name);
874			else {
875				status_update_len(gp, &name_len, &status_len);
876				n++;
877			}
878		}
879		if (n == 0)
880			goto end;
881	} else {
882		int n = 0;
883
884		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
885			if (LIST_EMPTY(&gp->lg_provider))
886				continue;
887			status_update_len(gp, &name_len, &status_len);
888			n++;
889		}
890		if (n == 0)
891			goto end;
892	}
893	printf("%*s  %*s  %s\n", name_len, "Name", status_len, "Status",
894	    "Components");
895	if (*nargs > 0) {
896		int i;
897
898		for (i = 0; i < *nargs; i++) {
899			const char *name;
900			char param[16];
901
902			snprintf(param, sizeof(param), "arg%d", i);
903			name = gctl_get_asciiparam(req, param);
904			assert(name != NULL);
905			gp = find_geom(classp, name);
906			if (gp != NULL) {
907				status_one_geom(gp, name_len, status_len);
908			}
909		}
910	} else {
911		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
912			if (LIST_EMPTY(&gp->lg_provider))
913				continue;
914			status_one_geom(gp, name_len, status_len);
915		}
916	}
917end:
918	geom_deletetree(&mesh);
919}
920
921static int
922std_load_available(void)
923{
924	char name[MAXPATHLEN], paths[MAXPATHLEN * 8], *p;
925	struct stat sb;
926	size_t len;
927
928	snprintf(name, sizeof(name), "g_%s", class_name);
929	/*
930	 * If already in kernel, "load" command is not available.
931	 */
932	if (modfind(name) >= 0)
933		return (0);
934	bzero(paths, sizeof(paths));
935	len = sizeof(paths);
936	if (sysctlbyname("kern.module_path", paths, &len, NULL, 0) < 0)
937		err(EXIT_FAILURE, "sysctl(kern.module_path)");
938	for (p = strtok(paths, ";"); p != NULL; p = strtok(NULL, ";")) {
939		snprintf(name, sizeof(name), "%s/geom_%s.ko", p, class_name);
940		/*
941		 * If geom_<name>.ko file exists, "load" command is available.
942		 */
943		if (stat(name, &sb) == 0)
944			return (1);
945	}
946	return (0);
947}
948
949static void
950std_load(struct gctl_req *req __unused, unsigned flags)
951{
952
953	/*
954	 * Do nothing special here, because of G_FLAG_LOADKLD flag,
955	 * module is already loaded.
956	 */
957	if ((flags & G_FLAG_VERBOSE) != 0)
958		printf("Module available.\n");
959}
960
961static int
962std_unload_available(void)
963{
964	char name[64];
965	int id;
966
967	snprintf(name, sizeof(name), "geom_%s", class_name);
968	id = kldfind(name);
969	if (id >= 0)
970		return (1);
971	return (0);
972}
973
974static void
975std_unload(struct gctl_req *req, unsigned flags __unused)
976{
977	char name[64];
978	int id;
979
980	snprintf(name, sizeof(name), "geom_%s", class_name);
981	id = kldfind(name);
982	if (id < 0) {
983		gctl_error(req, "Could not find module: %s.", strerror(errno));
984		return;
985	}
986	if (kldunload(id) < 0) {
987		gctl_error(req, "Could not unload module: %s.",
988		    strerror(errno));
989		return;
990	}
991}
992
993static int
994std_available(const char *name)
995{
996
997	if (strcmp(name, "help") == 0)
998		return (1);
999	else if (strcmp(name, "list") == 0)
1000		return (std_list_available());
1001	else if (strcmp(name, "status") == 0)
1002		return (std_status_available());
1003	else if (strcmp(name, "load") == 0)
1004		return (std_load_available());
1005	else if (strcmp(name, "unload") == 0)
1006		return (std_unload_available());
1007	else
1008		assert(!"Unknown standard command.");
1009	return (0);
1010}
1011