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