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