1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 *  Copyright (c) 2004, 2007 Lukas Ertl
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 AUTHOR 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 AUTHOR 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
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD$");
32
33#include <sys/types.h>
34#include <sys/libkern.h>
35#include <sys/malloc.h>
36#include <sys/sbuf.h>
37
38#include <geom/geom.h>
39#include <geom/vinum/geom_vinum_var.h>
40#include <geom/vinum/geom_vinum.h>
41#include <geom/vinum/geom_vinum_share.h>
42
43void	gv_lvi(struct gv_volume *, struct sbuf *, int);
44void	gv_lpi(struct gv_plex *, struct sbuf *, int);
45void	gv_lsi(struct gv_sd *, struct sbuf *, int);
46void	gv_ldi(struct gv_drive *, struct sbuf *, int);
47
48void
49gv_list(struct g_geom *gp, struct gctl_req *req)
50{
51	struct gv_softc *sc;
52	struct gv_drive *d;
53	struct gv_plex *p;
54	struct gv_sd *s;
55	struct gv_volume *v;
56	struct sbuf *sb;
57	int *argc, i, *flags, type;
58	char *arg, buf[20], *cmd;
59
60	argc = gctl_get_paraml(req, "argc", sizeof(*argc));
61
62	if (argc == NULL) {
63		gctl_error(req, "no arguments given");
64		return;
65	}
66
67	flags = gctl_get_paraml(req, "flags", sizeof(*flags));
68	if (flags == NULL) {
69		gctl_error(req, "no flags given");
70		return;
71	}
72
73	sc = gp->softc;
74
75	sb = sbuf_new(NULL, NULL, GV_CFG_LEN, SBUF_FIXEDLEN);
76
77	/* Figure out which command was given. */
78	cmd = gctl_get_param(req, "cmd", NULL);
79	if (cmd == NULL) {
80		gctl_error(req, "no command given");
81		return;
82	}
83
84	/* List specific objects or everything. */
85	if (!strcmp(cmd, "list") || !strcmp(cmd, "l")) {
86		if (*argc) {
87			for (i = 0; i < *argc; i++) {
88				snprintf(buf, sizeof(buf), "argv%d", i);
89				arg = gctl_get_param(req, buf, NULL);
90				if (arg == NULL)
91					continue;
92				type = gv_object_type(sc, arg);
93				switch (type) {
94				case GV_TYPE_VOL:
95					v = gv_find_vol(sc, arg);
96					gv_lvi(v, sb, *flags);
97					break;
98				case GV_TYPE_PLEX:
99					p = gv_find_plex(sc, arg);
100					gv_lpi(p, sb, *flags);
101					break;
102				case GV_TYPE_SD:
103					s = gv_find_sd(sc, arg);
104					gv_lsi(s, sb, *flags);
105					break;
106				case GV_TYPE_DRIVE:
107					d = gv_find_drive(sc, arg);
108					gv_ldi(d, sb, *flags);
109					break;
110				default:
111					gctl_error(req, "unknown object '%s'",
112					    arg);
113					break;
114				}
115			}
116		} else {
117			gv_ld(gp, req, sb);
118			sbuf_printf(sb, "\n");
119			gv_lv(gp, req, sb);
120			sbuf_printf(sb, "\n");
121			gv_lp(gp, req, sb);
122			sbuf_printf(sb, "\n");
123			gv_ls(gp, req, sb);
124		}
125
126	/* List drives. */
127	} else if (!strcmp(cmd, "ld")) {
128		if (*argc) {
129			for (i = 0; i < *argc; i++) {
130				snprintf(buf, sizeof(buf), "argv%d", i);
131				arg = gctl_get_param(req, buf, NULL);
132				if (arg == NULL)
133					continue;
134				type = gv_object_type(sc, arg);
135				if (type != GV_TYPE_DRIVE) {
136					gctl_error(req, "'%s' is not a drive",
137					    arg);
138					continue;
139				} else {
140					d = gv_find_drive(sc, arg);
141					gv_ldi(d, sb, *flags);
142				}
143			}
144		} else
145			gv_ld(gp, req, sb);
146
147	/* List volumes. */
148	} else if (!strcmp(cmd, "lv")) {
149		if (*argc) {
150			for (i = 0; i < *argc; i++) {
151				snprintf(buf, sizeof(buf), "argv%d", i);
152				arg = gctl_get_param(req, buf, NULL);
153				if (arg == NULL)
154					continue;
155				type = gv_object_type(sc, arg);
156				if (type != GV_TYPE_VOL) {
157					gctl_error(req, "'%s' is not a volume",
158					    arg);
159					continue;
160				} else {
161					v = gv_find_vol(sc, arg);
162					gv_lvi(v, sb, *flags);
163				}
164			}
165		} else
166			gv_lv(gp, req, sb);
167
168	/* List plexes. */
169	} else if (!strcmp(cmd, "lp")) {
170		if (*argc) {
171			for (i = 0; i < *argc; i++) {
172				snprintf(buf, sizeof(buf), "argv%d", i);
173				arg = gctl_get_param(req, buf, NULL);
174				if (arg == NULL)
175					continue;
176				type = gv_object_type(sc, arg);
177				if (type != GV_TYPE_PLEX) {
178					gctl_error(req, "'%s' is not a plex",
179					    arg);
180					continue;
181				} else {
182					p = gv_find_plex(sc, arg);
183					gv_lpi(p, sb, *flags);
184				}
185			}
186		} else
187			gv_lp(gp, req, sb);
188
189	/* List subdisks. */
190	} else if (!strcmp(cmd, "ls")) {
191		if (*argc) {
192			for (i = 0; i < *argc; i++) {
193				snprintf(buf, sizeof(buf), "argv%d", i);
194				arg = gctl_get_param(req, buf, NULL);
195				if (arg == NULL)
196					continue;
197				type = gv_object_type(sc, arg);
198				if (type != GV_TYPE_SD) {
199					gctl_error(req, "'%s' is not a subdisk",
200					    arg);
201					continue;
202				} else {
203					s = gv_find_sd(sc, arg);
204					gv_lsi(s, sb, *flags);
205				}
206			}
207		} else
208			gv_ls(gp, req, sb);
209
210	} else
211		gctl_error(req, "unknown command '%s'", cmd);
212
213	sbuf_finish(sb);
214	gctl_set_param(req, "config", sbuf_data(sb), sbuf_len(sb) + 1);
215	sbuf_delete(sb);
216}
217
218/* List one or more volumes. */
219void
220gv_lv(struct g_geom *gp, struct gctl_req *req, struct sbuf *sb)
221{
222	struct gv_softc *sc;
223	struct gv_volume *v;
224	int i, *flags;
225
226	sc = gp->softc;
227	i = 0;
228
229	LIST_FOREACH(v, &sc->volumes, volume)
230		i++;
231
232	sbuf_printf(sb, "%d volume%s:\n", i, i == 1 ? "" : "s");
233
234	if (i) {
235		flags = gctl_get_paraml(req, "flags", sizeof(*flags));
236		LIST_FOREACH(v, &sc->volumes, volume)
237			gv_lvi(v, sb, *flags);
238	}
239}
240
241/* List a single volume. */
242void
243gv_lvi(struct gv_volume *v, struct sbuf *sb, int flags)
244{
245	struct gv_plex *p;
246	int i;
247
248	if (flags & GV_FLAG_V) {
249		sbuf_printf(sb, "Volume %s:\tSize: %jd bytes (%jd MB)\n",
250		    v->name, (intmax_t)v->size, (intmax_t)v->size / MEGABYTE);
251		sbuf_printf(sb, "\t\tState: %s\n", gv_volstate(v->state));
252	} else {
253		sbuf_printf(sb, "V %-21s State: %s\tPlexes: %7d\tSize: %s\n",
254		    v->name, gv_volstate(v->state), v->plexcount,
255		    gv_roughlength(v->size, 0));
256	}
257
258	if (flags & GV_FLAG_VV) {
259		i = 0;
260		LIST_FOREACH(p, &v->plexes, in_volume) {
261			sbuf_printf(sb, "\t\tPlex %2d:\t%s\t(%s), %s\n", i,
262			    p->name, gv_plexstate(p->state),
263			    gv_roughlength(p->size, 0));
264			i++;
265		}
266	}
267
268	if (flags & GV_FLAG_R) {
269		LIST_FOREACH(p, &v->plexes, in_volume)
270			gv_lpi(p, sb, flags);
271	}
272}
273
274/* List one or more plexes. */
275void
276gv_lp(struct g_geom *gp, struct gctl_req *req, struct sbuf *sb)
277{
278	struct gv_softc *sc;
279	struct gv_plex *p;
280	int i, *flags;
281
282	sc = gp->softc;
283	i = 0;
284
285	LIST_FOREACH(p, &sc->plexes, plex)
286		i++;
287
288	sbuf_printf(sb, "%d plex%s:\n", i, i == 1 ? "" : "es");
289
290	if (i) {
291		flags = gctl_get_paraml(req, "flags", sizeof(*flags));
292		LIST_FOREACH(p, &sc->plexes, plex)
293			gv_lpi(p, sb, *flags);
294	}
295}
296
297/* List a single plex. */
298void
299gv_lpi(struct gv_plex *p, struct sbuf *sb, int flags)
300{
301	struct gv_sd *s;
302	int i;
303
304	if (flags & GV_FLAG_V) {
305		sbuf_printf(sb, "Plex %s:\tSize:\t%9jd bytes (%jd MB)\n",
306		    p->name, (intmax_t)p->size, (intmax_t)p->size / MEGABYTE);
307		sbuf_printf(sb, "\t\tSubdisks: %8d\n", p->sdcount);
308		sbuf_printf(sb, "\t\tState: %s\n", gv_plexstate(p->state));
309		if ((p->flags & GV_PLEX_SYNCING) ||
310		    (p->flags & GV_PLEX_GROWING) ||
311		    (p->flags & GV_PLEX_REBUILDING)) {
312			sbuf_printf(sb, "\t\tSynced: ");
313			sbuf_printf(sb, "%16jd bytes (%d%%)\n",
314			    (intmax_t)p->synced,
315			    (p->size > 0) ? (int)((p->synced * 100) / p->size) :
316			    0);
317		}
318		sbuf_printf(sb, "\t\tOrganization: %s", gv_plexorg(p->org));
319		if (gv_is_striped(p)) {
320			sbuf_printf(sb, "\tStripe size: %s\n",
321			    gv_roughlength(p->stripesize, 1));
322		}
323		sbuf_printf(sb, "\t\tFlags: %d\n", p->flags);
324		if (p->vol_sc != NULL) {
325			sbuf_printf(sb, "\t\tPart of volume %s\n", p->volume);
326		}
327	} else {
328		sbuf_printf(sb, "P %-18s %2s State: ", p->name,
329		gv_plexorg_short(p->org));
330		if ((p->flags & GV_PLEX_SYNCING) ||
331		    (p->flags & GV_PLEX_GROWING) ||
332		    (p->flags & GV_PLEX_REBUILDING)) {
333			sbuf_printf(sb, "S %d%%\t", (int)((p->synced * 100) /
334			    p->size));
335		} else {
336			sbuf_printf(sb, "%s\t", gv_plexstate(p->state));
337		}
338		sbuf_printf(sb, "Subdisks: %5d\tSize: %s\n", p->sdcount,
339		    gv_roughlength(p->size, 0));
340	}
341
342	if (flags & GV_FLAG_VV) {
343		i = 0;
344		LIST_FOREACH(s, &p->subdisks, in_plex) {
345			sbuf_printf(sb, "\t\tSubdisk %d:\t%s\n", i, s->name);
346			sbuf_printf(sb, "\t\t  state: %s\tsize %11jd "
347			    "(%jd MB)\n", gv_sdstate(s->state),
348			    (intmax_t)s->size, (intmax_t)s->size / MEGABYTE);
349			if (p->org == GV_PLEX_CONCAT) {
350				sbuf_printf(sb, "\t\t\toffset %9jd (0x%jx)\n",
351				    (intmax_t)s->plex_offset,
352				    (intmax_t)s->plex_offset);
353			}
354			i++;
355		}
356	}
357
358	if (flags & GV_FLAG_R) {
359		LIST_FOREACH(s, &p->subdisks, in_plex)
360			gv_lsi(s, sb, flags);
361	}
362}
363
364/* List one or more subdisks. */
365void
366gv_ls(struct g_geom *gp, struct gctl_req *req, struct sbuf *sb)
367{
368	struct gv_softc *sc;
369	struct gv_sd *s;
370	int i, *flags;
371
372	sc = gp->softc;
373	i = 0;
374
375	LIST_FOREACH(s, &sc->subdisks, sd)
376		i++;
377
378	sbuf_printf(sb, "%d subdisk%s:\n", i, i == 1 ? "" : "s");
379
380	if (i) {
381		flags = gctl_get_paraml(req, "flags", sizeof(*flags));
382		LIST_FOREACH(s, &sc->subdisks, sd)
383			gv_lsi(s, sb, *flags);
384	}
385}
386
387/* List a single subdisk. */
388void
389gv_lsi(struct gv_sd *s, struct sbuf *sb, int flags)
390{
391	if (flags & GV_FLAG_V) {
392		sbuf_printf(sb, "Subdisk %s:\n", s->name);
393		sbuf_printf(sb, "\t\tSize: %16jd bytes (%jd MB)\n",
394		    (intmax_t)s->size, (intmax_t)s->size / MEGABYTE);
395		sbuf_printf(sb, "\t\tState: %s\n", gv_sdstate(s->state));
396
397		if (s->state == GV_SD_INITIALIZING ||
398		    s->state == GV_SD_REVIVING) {
399			if (s->state == GV_SD_INITIALIZING)
400				sbuf_printf(sb, "\t\tInitialized: ");
401			else
402				sbuf_printf(sb, "\t\tRevived: ");
403
404			sbuf_printf(sb, "%16jd bytes (%d%%)\n",
405			    (intmax_t)s->initialized,
406			    (int)((s->initialized * 100) / s->size));
407		}
408
409		if (s->plex_sc != NULL) {
410			sbuf_printf(sb, "\t\tPlex %s at offset %jd (%s)\n",
411			    s->plex, (intmax_t)s->plex_offset,
412			    gv_roughlength(s->plex_offset, 1));
413		}
414
415		sbuf_printf(sb, "\t\tDrive %s (%s) at offset %jd (%s)\n",
416		    s->drive,
417		    s->drive_sc == NULL ? "*missing*" : s->drive_sc->name,
418		    (intmax_t)s->drive_offset,
419		    gv_roughlength(s->drive_offset, 1));
420		sbuf_printf(sb, "\t\tFlags: %d\n", s->flags);
421	} else {
422		sbuf_printf(sb, "S %-21s State: ", s->name);
423		if (s->state == GV_SD_INITIALIZING ||
424		    s->state == GV_SD_REVIVING) {
425			if (s->state == GV_SD_INITIALIZING)
426				sbuf_printf(sb, "I ");
427			else
428				sbuf_printf(sb, "R ");
429			sbuf_printf(sb, "%d%%\t",
430			    (int)((s->initialized * 100) / s->size));
431		} else {
432			sbuf_printf(sb, "%s\t", gv_sdstate(s->state));
433		}
434		sbuf_printf(sb, "D: %-12s Size: %s\n", s->drive,
435		    gv_roughlength(s->size, 0));
436	}
437}
438
439/* List one or more drives. */
440void
441gv_ld(struct g_geom *gp, struct gctl_req *req, struct sbuf *sb)
442{
443	struct gv_softc *sc;
444	struct gv_drive *d;
445	int i, *flags;
446
447	sc = gp->softc;
448	i = 0;
449
450	LIST_FOREACH(d, &sc->drives, drive)
451		i++;
452
453	sbuf_printf(sb, "%d drive%s:\n", i, i == 1 ? "" : "s");
454
455	if (i) {
456		flags = gctl_get_paraml(req, "flags", sizeof(*flags));
457		LIST_FOREACH(d, &sc->drives, drive)
458			gv_ldi(d, sb, *flags);
459	}
460}
461
462/* List a single drive. */
463void
464gv_ldi(struct gv_drive *d, struct sbuf *sb, int flags)
465{
466	struct gv_freelist *fl;
467	struct gv_sd *s;
468
469	/* Verbose listing. */
470	if (flags & GV_FLAG_V) {
471		sbuf_printf(sb, "Drive %s:\tDevice %s\n", d->name, d->device);
472		sbuf_printf(sb, "\t\tSize: %16jd bytes (%jd MB)\n",
473		    (intmax_t)d->size, (intmax_t)d->size / MEGABYTE);
474		sbuf_printf(sb, "\t\tUsed: %16jd bytes (%jd MB)\n",
475		    (intmax_t)d->size - d->avail,
476		    (intmax_t)(d->size - d->avail) / MEGABYTE);
477		sbuf_printf(sb, "\t\tAvailable: %11jd bytes (%jd MB)\n",
478		    (intmax_t)d->avail, (intmax_t)d->avail / MEGABYTE);
479		sbuf_printf(sb, "\t\tState: %s\n", gv_drivestate(d->state));
480		sbuf_printf(sb, "\t\tFlags: %d\n", d->flags);
481
482		/* Be very verbose. */
483		if (flags & GV_FLAG_VV) {
484			sbuf_printf(sb, "\t\tFree list contains %d entries:\n",
485			    d->freelist_entries);
486			sbuf_printf(sb, "\t\t   Offset\t     Size\n");
487			LIST_FOREACH(fl, &d->freelist, freelist)
488				sbuf_printf(sb, "\t\t%9jd\t%9jd\n",
489				    (intmax_t)fl->offset, (intmax_t)fl->size);
490		}
491	} else {
492		sbuf_printf(sb, "D %-21s State: %s\t/dev/%s\tA: %jd/%jd MB "
493		    "(%d%%)\n", d->name, gv_drivestate(d->state), d->device,
494		    (intmax_t)d->avail / MEGABYTE, (intmax_t)d->size / MEGABYTE,
495		    d->size > 0 ? (int)((d->avail * 100) / d->size) : 0);
496	}
497
498	/* Recursive listing. */
499	if (flags & GV_FLAG_R) {
500		LIST_FOREACH(s, &d->subdisks, from_drive)
501			gv_lsi(s, sb, flags);
502	}
503}
504