geom_vinum_share.c revision 190507
1/*- 2 * Copyright (c) 2004, 2007 Lukas Ertl 3 * Copyright (c) 1997, 1998, 1999 4 * Nan Yang Computer Services Limited. All rights reserved. 5 * 6 * Parts written by Greg Lehey 7 * 8 * This software is distributed under the so-called ``Berkeley 9 * License'': 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by Nan Yang Computer 22 * Services Limited. 23 * 4. Neither the name of the Company nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * This software is provided ``as is'', and any express or implied 28 * warranties, including, but not limited to, the implied warranties of 29 * merchantability and fitness for a particular purpose are disclaimed. 30 * In no event shall the company or contributors be liable for any 31 * direct, indirect, incidental, special, exemplary, or consequential 32 * damages (including, but not limited to, procurement of substitute 33 * goods or services; loss of use, data, or profits; or business 34 * interruption) however caused and on any theory of liability, whether 35 * in contract, strict liability, or tort (including negligence or 36 * otherwise) arising in any way out of the use of this software, even if 37 * advised of the possibility of such damage. 38 * 39 */ 40 41/* This file is shared between kernel and userland. */ 42 43#include <sys/cdefs.h> 44__FBSDID("$FreeBSD: head/sys/geom/vinum/geom_vinum_share.c 190507 2009-03-28 17:20:08Z lulf $"); 45 46#include <sys/param.h> 47#ifdef _KERNEL 48#include <sys/malloc.h> 49#include <sys/systm.h> 50 51#include <geom/geom.h> 52#define iswhite(c) (((c) == ' ') || ((c) == '\t')) 53#else 54#include <ctype.h> 55#include <stdio.h> 56#include <stdlib.h> 57#include <string.h> 58#define iswhite isspace 59#define g_free free 60#endif /* _KERNEL */ 61 62#include <sys/mutex.h> 63#include <sys/queue.h> 64 65#include <geom/vinum/geom_vinum_var.h> 66#include <geom/vinum/geom_vinum_share.h> 67 68/* 69 * Take a blank separated list of tokens and turn it into a list of 70 * individual nul-delimited strings. Build a list of pointers at 71 * token, which must have enough space for the tokens. Return the 72 * number of tokens, or -1 on error (typically a missing string 73 * delimiter). 74 */ 75int 76gv_tokenize(char *cptr, char *token[], int maxtoken) 77{ 78 int tokennr; /* Index of this token. */ 79 char delim; /* Delimiter for searching for the partner. */ 80 81 for (tokennr = 0; tokennr < maxtoken;) { 82 83 /* Skip leading white space. */ 84 while (iswhite(*cptr)) 85 cptr++; 86 87 /* End of line. */ 88 if ((*cptr == '\0') || (*cptr == '\n') || (*cptr == '#')) 89 return tokennr; 90 91 delim = *cptr; 92 token[tokennr] = cptr; /* Point to it. */ 93 tokennr++; /* One more. */ 94 95 /* Run off the end? */ 96 if (tokennr == maxtoken) 97 return tokennr; 98 99 /* Quoted? */ 100 if ((delim == '\'') || (delim == '"')) { 101 for (;;) { 102 cptr++; 103 104 /* Found the partner. */ 105 if ((*cptr == delim) && (cptr[-1] != '\\')) { 106 cptr++; 107 108 /* Space after closing quote needed. */ 109 if (!iswhite(*cptr)) 110 return -1; 111 112 /* Delimit. */ 113 *cptr++ = '\0'; 114 115 /* End-of-line? */ 116 } else if ((*cptr == '\0') || (*cptr == '\n')) 117 return -1; 118 } 119 120 /* Not quoted. */ 121 } else { 122 while ((*cptr != '\0') && 123 (!iswhite(*cptr)) && 124 (*cptr != '\n')) 125 cptr++; 126 127 /* Not end-of-line; delimit and move to the next. */ 128 if (*cptr != '\0') 129 *cptr++ = '\0'; 130 } 131 } 132 133 /* Can't get here. */ 134 return maxtoken; 135} 136 137 138/* 139 * Take a number with an optional scale factor and convert it to a number of 140 * bytes. 141 * 142 * The scale factors are: 143 * 144 * s sectors (of 512 bytes) 145 * b blocks (of 512 bytes). This unit is deprecated, because it's 146 * confusing, but maintained to avoid confusing Veritas users. 147 * k kilobytes (1024 bytes) 148 * m megabytes (of 1024 * 1024 bytes) 149 * g gigabytes (of 1024 * 1024 * 1024 bytes) 150 * 151 * XXX: need a way to signal error 152 */ 153off_t 154gv_sizespec(char *spec) 155{ 156 uint64_t size; 157 char *s; 158 int sign; 159 160 size = 0; 161 sign = 1; 162 if (spec != NULL) { /* we have a parameter */ 163 s = spec; 164 if (*s == '-') { /* negative, */ 165 sign = -1; 166 s++; /* skip */ 167 } 168 169 /* It's numeric. */ 170 if ((*s >= '0') && (*s <= '9')) { 171 172 /* It's numeric. */ 173 while ((*s >= '0') && (*s <= '9')) 174 /* Convert it. */ 175 size = size * 10 + *s++ - '0'; 176 177 switch (*s) { 178 case '\0': 179 return size * sign; 180 181 case 'B': 182 case 'b': 183 case 'S': 184 case 's': 185 return size * sign * 512; 186 187 case 'K': 188 case 'k': 189 return size * sign * 1024; 190 191 case 'M': 192 case 'm': 193 return size * sign * 1024 * 1024; 194 195 case 'G': 196 case 'g': 197 return size * sign * 1024 * 1024 * 1024; 198 } 199 } 200 } 201 202 return (0); 203} 204 205const char * 206gv_drivestate(int state) 207{ 208 switch (state) { 209 case GV_DRIVE_DOWN: 210 return "down"; 211 case GV_DRIVE_UP: 212 return "up"; 213 default: 214 return "??"; 215 } 216} 217 218int 219gv_drivestatei(char *buf) 220{ 221 if (!strcmp(buf, "up")) 222 return (GV_DRIVE_UP); 223 else 224 return (GV_DRIVE_DOWN); 225} 226 227/* Translate from a string to a subdisk state. */ 228int 229gv_sdstatei(char *buf) 230{ 231 if (!strcmp(buf, "up")) 232 return (GV_SD_UP); 233 else if (!strcmp(buf, "reviving")) 234 return (GV_SD_REVIVING); 235 else if (!strcmp(buf, "initializing")) 236 return (GV_SD_INITIALIZING); 237 else if (!strcmp(buf, "stale")) 238 return (GV_SD_STALE); 239 else 240 return (GV_SD_DOWN); 241} 242 243/* Translate from a subdisk state to a string. */ 244const char * 245gv_sdstate(int state) 246{ 247 switch (state) { 248 case GV_SD_INITIALIZING: 249 return "initializing"; 250 case GV_SD_STALE: 251 return "stale"; 252 case GV_SD_DOWN: 253 return "down"; 254 case GV_SD_REVIVING: 255 return "reviving"; 256 case GV_SD_UP: 257 return "up"; 258 default: 259 return "??"; 260 } 261} 262 263/* Translate from a string to a plex state. */ 264int 265gv_plexstatei(char *buf) 266{ 267 if (!strcmp(buf, "up")) 268 return (GV_PLEX_UP); 269 else if (!strcmp(buf, "initializing")) 270 return (GV_PLEX_INITIALIZING); 271 else if (!strcmp(buf, "degraded")) 272 return (GV_PLEX_DEGRADED); 273 else if (!strcmp(buf, "growable")) 274 return (GV_PLEX_GROWABLE); 275 else 276 return (GV_PLEX_DOWN); 277} 278 279/* Translate from a plex state to a string. */ 280const char * 281gv_plexstate(int state) 282{ 283 switch (state) { 284 case GV_PLEX_DOWN: 285 return "down"; 286 case GV_PLEX_INITIALIZING: 287 return "initializing"; 288 case GV_PLEX_DEGRADED: 289 return "degraded"; 290 case GV_PLEX_GROWABLE: 291 return "growable"; 292 case GV_PLEX_UP: 293 return "up"; 294 default: 295 return "??"; 296 } 297} 298 299/* Translate from a string to a plex organization. */ 300int 301gv_plexorgi(char *buf) 302{ 303 if (!strcmp(buf, "concat")) 304 return (GV_PLEX_CONCAT); 305 else if (!strcmp(buf, "striped")) 306 return (GV_PLEX_STRIPED); 307 else if (!strcmp(buf, "raid5")) 308 return (GV_PLEX_RAID5); 309 else 310 return (GV_PLEX_DISORG); 311} 312 313int 314gv_volstatei(char *buf) 315{ 316 if (!strcmp(buf, "up")) 317 return (GV_VOL_UP); 318 else 319 return (GV_VOL_DOWN); 320} 321 322const char * 323gv_volstate(int state) 324{ 325 switch (state) { 326 case GV_VOL_UP: 327 return "up"; 328 case GV_VOL_DOWN: 329 return "down"; 330 default: 331 return "??"; 332 } 333} 334 335/* Translate from a plex organization to a string. */ 336const char * 337gv_plexorg(int org) 338{ 339 switch (org) { 340 case GV_PLEX_DISORG: 341 return "??"; 342 case GV_PLEX_CONCAT: 343 return "concat"; 344 case GV_PLEX_STRIPED: 345 return "striped"; 346 case GV_PLEX_RAID5: 347 return "raid5"; 348 default: 349 return "??"; 350 } 351} 352 353const char * 354gv_plexorg_short(int org) 355{ 356 switch (org) { 357 case GV_PLEX_DISORG: 358 return "??"; 359 case GV_PLEX_CONCAT: 360 return "C"; 361 case GV_PLEX_STRIPED: 362 return "S"; 363 case GV_PLEX_RAID5: 364 return "R5"; 365 default: 366 return "??"; 367 } 368} 369 370/* Get a new drive object. */ 371struct gv_drive * 372gv_new_drive(int max, char *token[]) 373{ 374 struct gv_drive *d; 375 int j, errors; 376 char *ptr; 377 378 if (token[1] == NULL || *token[1] == '\0') 379 return (NULL); 380 381#ifdef _KERNEL 382 d = g_malloc(sizeof(struct gv_drive), M_NOWAIT); 383#else 384 d = malloc(sizeof(struct gv_drive)); 385#endif 386 if (d == NULL) 387 return (NULL); 388 bzero(d, sizeof(struct gv_drive)); 389 390 errors = 0; 391 for (j = 1; j < max; j++) { 392 if (!strcmp(token[j], "state")) { 393 j++; 394 if (j >= max) { 395 errors++; 396 break; 397 } 398 d->state = gv_drivestatei(token[j]); 399 } else if (!strcmp(token[j], "device")) { 400 j++; 401 if (j >= max) { 402 errors++; 403 break; 404 } 405 ptr = token[j]; 406 407 if (strncmp(ptr, "/dev/", 5) == 0) 408 ptr += 5; 409 strlcpy(d->device, ptr, sizeof(d->device)); 410 } else { 411 /* We assume this is the drive name. */ 412 strlcpy(d->name, token[j], sizeof(d->name)); 413 } 414 } 415 416 if (strlen(d->name) == 0 || strlen(d->device) == 0) 417 errors++; 418 419 if (errors) { 420 g_free(d); 421 return (NULL); 422 } 423 424 return (d); 425} 426 427/* Get a new volume object. */ 428struct gv_volume * 429gv_new_volume(int max, char *token[]) 430{ 431 struct gv_volume *v; 432 int j, errors; 433 434 if (token[1] == NULL || *token[1] == '\0') 435 return (NULL); 436 437#ifdef _KERNEL 438 v = g_malloc(sizeof(struct gv_volume), M_NOWAIT); 439#else 440 v = malloc(sizeof(struct gv_volume)); 441#endif 442 if (v == NULL) 443 return (NULL); 444 bzero(v, sizeof(struct gv_volume)); 445 446 errors = 0; 447 for (j = 1; j < max; j++) { 448 if (!strcmp(token[j], "state")) { 449 j++; 450 if (j >= max) { 451 errors++; 452 break; 453 } 454 v->state = gv_volstatei(token[j]); 455 } else { 456 /* We assume this is the volume name. */ 457 strlcpy(v->name, token[j], sizeof(v->name)); 458 } 459 } 460 461 if (strlen(v->name) == 0) 462 errors++; 463 464 if (errors) { 465 g_free(v); 466 return (NULL); 467 } 468 469 return (v); 470} 471 472/* Get a new plex object. */ 473struct gv_plex * 474gv_new_plex(int max, char *token[]) 475{ 476 struct gv_plex *p; 477 int j, errors; 478 479 if (token[1] == NULL || *token[1] == '\0') 480 return (NULL); 481 482#ifdef _KERNEL 483 p = g_malloc(sizeof(struct gv_plex), M_NOWAIT); 484#else 485 p = malloc(sizeof(struct gv_plex)); 486#endif 487 if (p == NULL) 488 return (NULL); 489 bzero(p, sizeof(struct gv_plex)); 490 491 errors = 0; 492 for (j = 1; j < max; j++) { 493 if (!strcmp(token[j], "name")) { 494 j++; 495 if (j >= max) { 496 errors++; 497 break; 498 } 499 strlcpy(p->name, token[j], sizeof(p->name)); 500 } else if (!strcmp(token[j], "org")) { 501 j++; 502 if (j >= max) { 503 errors++; 504 break; 505 } 506 p->org = gv_plexorgi(token[j]); 507 if ((p->org == GV_PLEX_RAID5) || 508 (p->org == GV_PLEX_STRIPED)) { 509 j++; 510 if (j >= max) { 511 errors++; 512 break; 513 } 514 p->stripesize = gv_sizespec(token[j]); 515 if (p->stripesize == 0) { 516 errors++; 517 break; 518 } 519 } 520 } else if (!strcmp(token[j], "state")) { 521 j++; 522 if (j >= max) { 523 errors++; 524 break; 525 } 526 p->state = gv_plexstatei(token[j]); 527 } else if (!strcmp(token[j], "vol") || 528 !strcmp(token[j], "volume")) { 529 j++; 530 if (j >= max) { 531 errors++; 532 break; 533 } 534 strlcpy(p->volume, token[j], sizeof(p->volume)); 535 } else { 536 errors++; 537 break; 538 } 539 } 540 541 if (errors) { 542 g_free(p); 543 return (NULL); 544 } 545 546 return (p); 547} 548 549/* Get a new subdisk object. */ 550struct gv_sd * 551gv_new_sd(int max, char *token[]) 552{ 553 struct gv_sd *s; 554 int j, errors; 555 556 if (token[1] == NULL || *token[1] == '\0') 557 return (NULL); 558 559#ifdef _KERNEL 560 s = g_malloc(sizeof(struct gv_sd), M_NOWAIT); 561#else 562 s = malloc(sizeof(struct gv_sd)); 563#endif 564 if (s == NULL) 565 return (NULL); 566 bzero(s, sizeof(struct gv_sd)); 567 568 s->plex_offset = -1; 569 s->size = -1; 570 s->drive_offset = -1; 571 errors = 0; 572 for (j = 1; j < max; j++) { 573 if (!strcmp(token[j], "name")) { 574 j++; 575 if (j >= max) { 576 errors++; 577 break; 578 } 579 strlcpy(s->name, token[j], sizeof(s->name)); 580 } else if (!strcmp(token[j], "drive")) { 581 j++; 582 if (j >= max) { 583 errors++; 584 break; 585 } 586 strlcpy(s->drive, token[j], sizeof(s->drive)); 587 } else if (!strcmp(token[j], "plex")) { 588 j++; 589 if (j >= max) { 590 errors++; 591 break; 592 } 593 strlcpy(s->plex, token[j], sizeof(s->plex)); 594 } else if (!strcmp(token[j], "state")) { 595 j++; 596 if (j >= max) { 597 errors++; 598 break; 599 } 600 s->state = gv_sdstatei(token[j]); 601 } else if (!strcmp(token[j], "len") || 602 !strcmp(token[j], "length")) { 603 j++; 604 if (j >= max) { 605 errors++; 606 break; 607 } 608 s->size = gv_sizespec(token[j]); 609 if (s->size <= 0) 610 s->size = -1; 611 } else if (!strcmp(token[j], "driveoffset")) { 612 j++; 613 if (j >= max) { 614 errors++; 615 break; 616 } 617 s->drive_offset = gv_sizespec(token[j]); 618 if (s->drive_offset != 0 && 619 s->drive_offset < GV_DATA_START) { 620 errors++; 621 break; 622 } 623 } else if (!strcmp(token[j], "plexoffset")) { 624 j++; 625 if (j >= max) { 626 errors++; 627 break; 628 } 629 s->plex_offset = gv_sizespec(token[j]); 630 if (s->plex_offset < 0) { 631 errors++; 632 break; 633 } 634 } else { 635 errors++; 636 break; 637 } 638 } 639 640 if (strlen(s->drive) == 0) 641 errors++; 642 643 if (errors) { 644 g_free(s); 645 return (NULL); 646 } 647 648 return (s); 649} 650 651/* 652 * Take a size in bytes and return a pointer to a string which represents the 653 * size best. If lj is != 0, return left justified, otherwise in a fixed 10 654 * character field suitable for columnar printing. 655 * 656 * Note this uses a static string: it's only intended to be used immediately 657 * for printing. 658 */ 659const char * 660gv_roughlength(off_t bytes, int lj) 661{ 662 static char desc[16]; 663 664 /* Gigabytes. */ 665 if (bytes > (off_t)MEGABYTE * 10000) 666 snprintf(desc, sizeof(desc), lj ? "%jd GB" : "%10jd GB", 667 bytes / GIGABYTE); 668 669 /* Megabytes. */ 670 else if (bytes > KILOBYTE * 10000) 671 snprintf(desc, sizeof(desc), lj ? "%jd MB" : "%10jd MB", 672 bytes / MEGABYTE); 673 674 /* Kilobytes. */ 675 else if (bytes > 10000) 676 snprintf(desc, sizeof(desc), lj ? "%jd kB" : "%10jd kB", 677 bytes / KILOBYTE); 678 679 /* Bytes. */ 680 else 681 snprintf(desc, sizeof(desc), lj ? "%jd B" : "%10jd B", bytes); 682 683 return (desc); 684} 685