xref: /freebsd/sys/geom/vinum/geom_vinum_list.c (revision b9f654b163bce26de79705e77b872427c9f2afa1)
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 
43 void	gv_lvi(struct gv_volume *, struct sbuf *, int);
44 void	gv_lpi(struct gv_plex *, struct sbuf *, int);
45 void	gv_lsi(struct gv_sd *, struct sbuf *, int);
46 void	gv_ldi(struct gv_drive *, struct sbuf *, int);
47 
48 void
49 gv_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. */
219 void
220 gv_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. */
242 void
243 gv_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. */
275 void
276 gv_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. */
298 void
299 gv_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. */
365 void
366 gv_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. */
388 void
389 gv_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. */
440 void
441 gv_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. */
463 void
464 gv_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