1 /*-
2 * Copyright (c) 2004 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$");
45
46 #include <sys/param.h>
47 #ifdef _KERNEL
48 #include <sys/bio.h>
49 #include <sys/conf.h>
50 #include <sys/kernel.h>
51 #include <sys/kthread.h>
52 #include <sys/malloc.h>
53 #include <sys/systm.h>
54
55 #include <geom/geom.h>
56 #define iswhite(c) (((c) == ' ') || ((c) == '\t'))
57 #else
58 #include <ctype.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #define iswhite isspace
63 #define g_free free
64 #endif /* _KERNEL */
65
66 #include <sys/lock.h>
67 #include <sys/mutex.h>
68 #include <sys/queue.h>
69
70 #include <geom/vinum/geom_vinum_var.h>
71 #include <geom/vinum/geom_vinum_share.h>
72
73 /*
74 * Take a blank separated list of tokens and turn it into a list of
75 * individual nul-delimited strings. Build a list of pointers at
76 * token, which must have enough space for the tokens. Return the
77 * number of tokens, or -1 on error (typically a missing string
78 * delimiter).
79 */
80 int
81 gv_tokenize(char *cptr, char *token[], int maxtoken)
82 {
83 int tokennr; /* Index of this token. */
84 char delim; /* Delimiter for searching for the partner. */
85
86 for (tokennr = 0; tokennr < maxtoken;) {
87
88 /* Skip leading white space. */
89 while (iswhite(*cptr))
90 cptr++;
91
92 /* End of line. */
93 if ((*cptr == '\0') || (*cptr == '\n') || (*cptr == '#'))
94 return tokennr;
95
96 delim = *cptr;
97 token[tokennr] = cptr; /* Point to it. */
98 tokennr++; /* One more. */
99
100 /* Run off the end? */
101 if (tokennr == maxtoken)
102 return tokennr;
103
104 /* Quoted? */
105 if ((delim == '\'') || (delim == '"')) {
106 for (;;) {
107 cptr++;
108
109 /* Found the partner. */
110 if ((*cptr == delim) && (cptr[-1] != '\\')) {
111 cptr++;
112
113 /* Space after closing quote needed. */
114 if (!iswhite(*cptr))
115 return -1;
116
117 /* Delimit. */
118 *cptr++ = '\0';
119
120 /* End-of-line? */
121 } else if ((*cptr == '\0') || (*cptr == '\n'))
122 return -1;
123 }
124
125 /* Not quoted. */
126 } else {
127 while ((*cptr != '\0') &&
128 (!iswhite(*cptr)) &&
129 (*cptr != '\n'))
130 cptr++;
131
132 /* Not end-of-line; delimit and move to the next. */
133 if (*cptr != '\0')
134 *cptr++ = '\0';
135 }
136 }
137
138 /* Can't get here. */
139 return maxtoken;
140 }
141
142
143 /*
144 * Take a number with an optional scale factor and convert it to a number of
145 * bytes.
146 *
147 * The scale factors are:
148 *
149 * s sectors (of 512 bytes)
150 * b blocks (of 512 bytes). This unit is deprecated, because it's
151 * confusing, but maintained to avoid confusing Veritas users.
152 * k kilobytes (1024 bytes)
153 * m megabytes (of 1024 * 1024 bytes)
154 * g gigabytes (of 1024 * 1024 * 1024 bytes)
155 *
156 * XXX: need a way to signal error
157 */
158 off_t
159 gv_sizespec(char *spec)
160 {
161 uint64_t size;
162 char *s;
163 int sign;
164
165 size = 0;
166 sign = 1;
167 if (spec != NULL) { /* we have a parameter */
168 s = spec;
169 if (*s == '-') { /* negative, */
170 sign = -1;
171 s++; /* skip */
172 }
173
174 /* It's numeric. */
175 if ((*s >= '') && (*s <= '9')) {
176
177 /* It's numeric. */
178 while ((*s >= '') && (*s <= '9'))
179 /* Convert it. */
180 size = size * 10 + *s++ - '';
181
182 switch (*s) {
183 case '\0':
184 return size * sign;
185
186 case 'B':
187 case 'b':
188 case 'S':
189 case 's':
190 return size * sign * 512;
191
192 case 'K':
193 case 'k':
194 return size * sign * 1024;
195
196 case 'M':
197 case 'm':
198 return size * sign * 1024 * 1024;
199
200 case 'G':
201 case 'g':
202 return size * sign * 1024 * 1024 * 1024;
203 }
204 }
205 }
206
207 return (0);
208 }
209
210 const char *
211 gv_drivestate(int state)
212 {
213 switch (state) {
214 case GV_DRIVE_DOWN:
215 return "down";
216 case GV_DRIVE_UP:
217 return "up";
218 default:
219 return "??";
220 }
221 }
222
223 int
224 gv_drivestatei(char *buf)
225 {
226 if (!strcmp(buf, "up"))
227 return (GV_DRIVE_UP);
228 else
229 return (GV_DRIVE_DOWN);
230 }
231
232 /* Translate from a string to a subdisk state. */
233 int
234 gv_sdstatei(char *buf)
235 {
236 if (!strcmp(buf, "up"))
237 return (GV_SD_UP);
238 else if (!strcmp(buf, "reviving"))
239 return (GV_SD_REVIVING);
240 else if (!strcmp(buf, "stale"))
241 return (GV_SD_STALE);
242 else
243 return (GV_SD_DOWN);
244 }
245
246 /* Translate from a subdisk state to a string. */
247 const char *
248 gv_sdstate(int state)
249 {
250 switch (state) {
251 case GV_SD_INITIALIZING:
252 return "initializing";
253 case GV_SD_STALE:
254 return "stale";
255 case GV_SD_DOWN:
256 return "down";
257 case GV_SD_REVIVING:
258 return "reviving";
259 case GV_SD_UP:
260 return "up";
261 default:
262 return "??";
263 }
264 }
265
266 /* Translate from a string to a plex state. */
267 int
268 gv_plexstatei(char *buf)
269 {
270 if (!strcmp(buf, "up"))
271 return (GV_PLEX_UP);
272 else if (!strcmp(buf, "initializing"))
273 return (GV_PLEX_INITIALIZING);
274 else if (!strcmp(buf, "degraded"))
275 return (GV_PLEX_DEGRADED);
276 else
277 return (GV_PLEX_DOWN);
278 }
279
280 /* Translate from a plex state to a string. */
281 const char *
282 gv_plexstate(int state)
283 {
284 switch (state) {
285 case GV_PLEX_DOWN:
286 return "down";
287 case GV_PLEX_INITIALIZING:
288 return "initializing";
289 case GV_PLEX_DEGRADED:
290 return "degraded";
291 case GV_PLEX_UP:
292 return "up";
293 default:
294 return "??";
295 }
296 }
297
298 /* Translate from a string to a plex organization. */
299 int
300 gv_plexorgi(char *buf)
301 {
302 if (!strcmp(buf, "concat"))
303 return (GV_PLEX_CONCAT);
304 else if (!strcmp(buf, "striped"))
305 return (GV_PLEX_STRIPED);
306 else if (!strcmp(buf, "raid5"))
307 return (GV_PLEX_RAID5);
308 else
309 return (GV_PLEX_DISORG);
310 }
311
312 int
313 gv_volstatei(char *buf)
314 {
315 if (!strcmp(buf, "up"))
316 return (GV_VOL_UP);
317 else
318 return (GV_VOL_DOWN);
319 }
320
321 const char *
322 gv_volstate(int state)
323 {
324 switch (state) {
325 case GV_VOL_UP:
326 return "up";
327 case GV_VOL_DOWN:
328 return "down";
329 default:
330 return "??";
331 }
332 }
333
334 /* Translate from a plex organization to a string. */
335 const char *
336 gv_plexorg(int org)
337 {
338 switch (org) {
339 case GV_PLEX_DISORG:
340 return "??";
341 case GV_PLEX_CONCAT:
342 return "concat";
343 case GV_PLEX_STRIPED:
344 return "striped";
345 case GV_PLEX_RAID5:
346 return "raid5";
347 default:
348 return "??";
349 }
350 }
351
352 const char *
353 gv_plexorg_short(int org)
354 {
355 switch (org) {
356 case GV_PLEX_DISORG:
357 return "??";
358 case GV_PLEX_CONCAT:
359 return "C";
360 case GV_PLEX_STRIPED:
361 return "S";
362 case GV_PLEX_RAID5:
363 return "R5";
364 default:
365 return "??";
366 }
367 }
368
369 /* Get a new drive object. */
370 struct gv_drive *
371 gv_new_drive(int max, char *token[])
372 {
373 struct gv_drive *d;
374 int j, errors;
375 char *ptr;
376
377 if (token[1] == NULL || *token[1] == '\0')
378 return (NULL);
379
380 #ifdef _KERNEL
381 d = g_malloc(sizeof(struct gv_drive), M_WAITOK | M_ZERO);
382
383 #else
384 d = malloc(sizeof(struct gv_drive));
385 if (d == NULL)
386 return (NULL);
387 bzero(d, sizeof(struct gv_drive));
388 #endif
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 strncpy(d->device, ptr, GV_MAXDRIVENAME);
410 } else {
411 /* We assume this is the drive name. */
412 strncpy(d->name, token[j], GV_MAXDRIVENAME);
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. */
428 struct gv_volume *
429 gv_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_WAITOK | M_ZERO);
439
440 #else
441 v = malloc(sizeof(struct gv_volume));
442 if (v == NULL)
443 return (NULL);
444 bzero(v, sizeof(struct gv_volume));
445 #endif
446
447 errors = 0;
448 for (j = 1; j < max; j++) {
449 if (!strcmp(token[j], "state")) {
450 j++;
451 if (j >= max) {
452 errors++;
453 break;
454 }
455 v->state = gv_volstatei(token[j]);
456 } else {
457 /* We assume this is the volume name. */
458 strncpy(v->name, token[j], GV_MAXVOLNAME);
459 }
460 }
461
462 if (strlen(v->name) == 0)
463 errors++;
464
465 if (errors) {
466 g_free(v);
467 return (NULL);
468 }
469
470 return (v);
471 }
472
473 /* Get a new plex object. */
474 struct gv_plex *
475 gv_new_plex(int max, char *token[])
476 {
477 struct gv_plex *p;
478 int j, errors;
479
480 if (token[1] == NULL || *token[1] == '\0')
481 return (NULL);
482
483 #ifdef _KERNEL
484 p = g_malloc(sizeof(struct gv_plex), M_WAITOK | M_ZERO);
485 #else
486 p = malloc(sizeof(struct gv_plex));
487 if (p == NULL)
488 return (NULL);
489 bzero(p, sizeof(struct gv_plex));
490 #endif
491
492 errors = 0;
493 for (j = 1; j < max; j++) {
494 if (!strcmp(token[j], "name")) {
495 j++;
496 if (j >= max) {
497 errors++;
498 break;
499 }
500 strncpy(p->name, token[j], GV_MAXPLEXNAME);
501 } else if (!strcmp(token[j], "org")) {
502 j++;
503 if (j >= max) {
504 errors++;
505 break;
506 }
507 p->org = gv_plexorgi(token[j]);
508 if ((p->org == GV_PLEX_RAID5) ||
509 (p->org == GV_PLEX_STRIPED)) {
510 j++;
511 if (j >= max) {
512 errors++;
513 break;
514 }
515 p->stripesize = gv_sizespec(token[j]);
516 if (p->stripesize == 0) {
517 errors++;
518 break;
519 }
520 }
521 } else if (!strcmp(token[j], "state")) {
522 j++;
523 if (j >= max) {
524 errors++;
525 break;
526 }
527 p->state = gv_plexstatei(token[j]);
528 } else if (!strcmp(token[j], "vol") ||
529 !strcmp(token[j], "volume")) {
530 j++;
531 if (j >= max) {
532 errors++;
533 break;
534 }
535 strncpy(p->volume, token[j], GV_MAXVOLNAME);
536 } else {
537 errors++;
538 break;
539 }
540 }
541
542 if (errors) {
543 g_free(p);
544 return (NULL);
545 }
546
547 return (p);
548 }
549
550 /* Get a new subdisk object. */
551 struct gv_sd *
552 gv_new_sd(int max, char *token[])
553 {
554 struct gv_sd *s;
555 int j, errors;
556
557 if (token[1] == NULL || *token[1] == '\0')
558 return NULL;
559
560 #ifdef _KERNEL
561 s = g_malloc(sizeof(struct gv_sd), M_WAITOK | M_ZERO);
562 #else
563 s = malloc(sizeof(struct gv_sd));
564 if (s == NULL)
565 return NULL;
566 bzero(s, sizeof(struct gv_sd));
567 #endif
568
569 s->plex_offset = -1;
570 s->size = -1;
571 s->drive_offset = -1;
572 errors = 0;
573 for (j = 1; j < max; j++) {
574 if (!strcmp(token[j], "name")) {
575 j++;
576 if (j >= max) {
577 errors++;
578 break;
579 }
580 strncpy(s->name, token[j], GV_MAXSDNAME);
581 } else if (!strcmp(token[j], "drive")) {
582 j++;
583 if (j >= max) {
584 errors++;
585 break;
586 }
587 strncpy(s->drive, token[j], GV_MAXDRIVENAME);
588 } else if (!strcmp(token[j], "plex")) {
589 j++;
590 if (j >= max) {
591 errors++;
592 break;
593 }
594 strncpy(s->plex, token[j], GV_MAXPLEXNAME);
595 } else if (!strcmp(token[j], "state")) {
596 j++;
597 if (j >= max) {
598 errors++;
599 break;
600 }
601 s->state = gv_sdstatei(token[j]);
602 } else if (!strcmp(token[j], "len") ||
603 !strcmp(token[j], "length")) {
604 j++;
605 if (j >= max) {
606 errors++;
607 break;
608 }
609 s->size = gv_sizespec(token[j]);
610 if (s->size <= 0)
611 s->size = -1;
612 } else if (!strcmp(token[j], "driveoffset")) {
613 j++;
614 if (j >= max) {
615 errors++;
616 break;
617 }
618 s->drive_offset = gv_sizespec(token[j]);
619 if (s->drive_offset != 0 &&
620 s->drive_offset < GV_DATA_START) {
621 errors++;
622 break;
623 }
624 } else if (!strcmp(token[j], "plexoffset")) {
625 j++;
626 if (j >= max) {
627 errors++;
628 break;
629 }
630 s->plex_offset = gv_sizespec(token[j]);
631 if (s->plex_offset < 0) {
632 errors++;
633 break;
634 }
635 } else {
636 errors++;
637 break;
638 }
639 }
640
641 if (strlen(s->drive) == 0)
642 errors++;
643
644 if (errors) {
645 g_free(s);
646 return (NULL);
647 }
648
649 return (s);
650 }
651
652 /*
653 * Take a size in bytes and return a pointer to a string which represents the
654 * size best. If lj is != 0, return left justified, otherwise in a fixed 10
655 * character field suitable for columnar printing.
656 *
657 * Note this uses a static string: it's only intended to be used immediately
658 * for printing.
659 */
660 const char *
661 gv_roughlength(off_t bytes, int lj)
662 {
663 static char desc[16];
664
665 /* Gigabytes. */
666 if (bytes > (off_t)MEGABYTE * 10000)
667 snprintf(desc, sizeof(desc), lj ? "%jd GB" : "%10jd GB",
668 bytes / GIGABYTE);
669
670 /* Megabytes. */
671 else if (bytes > KILOBYTE * 10000)
672 snprintf(desc, sizeof(desc), lj ? "%jd MB" : "%10jd MB",
673 bytes / MEGABYTE);
674
675 /* Kilobytes. */
676 else if (bytes > 10000)
677 snprintf(desc, sizeof(desc), lj ? "%jd kB" : "%10jd kB",
678 bytes / KILOBYTE);
679
680 /* Bytes. */
681 else
682 snprintf(desc, sizeof(desc), lj ? "%jd B" : "%10jd B", bytes);
683
684 return (desc);
685 }
Cache object: a0fc7da3b1d6ba29e00d0a66c3f2f820
|