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