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