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