xref: /freebsd/sys/geom/vinum/geom_vinum_list.c (revision a4e5e0106ac7145f56eb39a691e302cabb4635be)
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 
40 void	gv_lvi(struct gv_volume *, struct sbuf *, int);
41 void	gv_lpi(struct gv_plex *, struct sbuf *, int);
42 void	gv_lsi(struct gv_sd *, struct sbuf *, int);
43 void	gv_ldi(struct gv_drive *, struct sbuf *, int);
44 
45 void
46 gv_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. */
216 void
217 gv_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. */
239 void
240 gv_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. */
272 void
273 gv_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. */
295 void
296 gv_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. */
362 void
363 gv_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. */
385 void
386 gv_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. */
437 void
438 gv_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. */
460 void
461 gv_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