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