source: MondoRescue/branches/2.2.9/mondo/src/common/libmondo-raid.c@ 2178

Last change on this file since 2178 was 1261, checked in by Bruno Cornec, 17 years ago

That version has no mr_msg yet (replacing it with log_msg)
Mandriva 2007.1 added

  • Property svn:keywords set to Id
File size: 32.9 KB
Line 
1/* libmondo-raid.c subroutines for handling RAID
2 $Id: libmondo-raid.c 1261 2007-03-21 23:29:27Z bruno $
3.
4
5
606/29
7- added create_raidtab_from_mdstat()
8- changed char[MAX_STR_LEN] to char*
9
1010/21/2003
11- get_next_raidtab_line() --- correctly handle multiple spaces
12 between label and value
13
1407/03
15- line 447 - changed assert()
16
1705/08
18- cleaned up some FreeBSd-specific stuff
19
2005/05
21- added Joshua Oreman's FreeBSD patches
22
2304/25
24- added a bunch of RAID utilities from mondorestore/mondo-restore.c
25
2604/24/2003
27- added some assert()'s and log_OS_error()'s
28
2910/19/2002
30- added some comments
31
3207/24
33- created
34*/
35
36
37/**
38 * @file
39 * Functions for handling RAID (especially during restore).
40 */
41
42#include "my-stuff.h"
43#include "mondostructures.h"
44#include "libmondo-gui-EXT.h"
45#include "libmondo-files-EXT.h"
46#include "libmondo-tools-EXT.h"
47#include "libmondo-string-EXT.h"
48#include "lib-common-externs.h"
49#include "libmondo-raid.h"
50#include "mr_string.h"
51
52#ifdef __FreeBSD__
53/* Nonstandard library functions: */
54extern void errx(int exitval, const char *fmt, ...);
55extern char *strsep(char **stringp, const char *delim);
56#endif
57
58/*@unused@*/
59//static char cvsid[] = "$Id: libmondo-raid.c 1261 2007-03-21 23:29:27Z bruno $";
60
61
62/**
63 * @addtogroup raidGroup
64 * @{
65 */
66/**
67 * See if a particular RAID level is supported by the kernel.
68 * @param raidno The RAID level (-1 through 5) to check. -1 means "linear" under Linux and
69 * "concatenated" under FreeBSD. It's really the same thing, just different wording.
70 * @return TRUE if it's supported, FALSE if not.
71 */
72bool is_this_raid_personality_registered(int raidno)
73{
74#ifdef __FreeBSD__
75 return ((raidno == -1) || (raidno == 0) || (raidno == 1)
76 || (raidno == 5)) ? TRUE : FALSE;
77#else
78 /*@ buffer ********************************************************** */
79 char *command;
80 int res;
81
82 command = malloc(MAX_STR_LEN * 2);
83 strcpy(command, "grep \"");
84 if (raidno == -1) {
85 strcat(command, "linear");
86 } else {
87 sprintf(command + strlen(command), "raid%d", raidno);
88 }
89 strcat(command, "\" /proc/mdstat > /dev/null 2> /dev/null");
90 log_it("Is raid %d registered? Command = '%s'", raidno, command);
91 res = system(command);
92 paranoid_free(command);
93 if (res) {
94 return (FALSE);
95 } else {
96 return (TRUE);
97 }
98#endif
99}
100
101
102
103
104
105
106/**
107 * Search for @p device in @p disklist.
108 * @param disklist The disklist to search in.
109 * @param device The device to search for.
110 * @return The index number of @p device, or -1 if it does not exist.
111 */
112int
113where_in_drivelist_is_drive(struct list_of_disks *disklist, char *device)
114{
115
116 /*@ int ************************************************************* */
117 int i = 0;
118
119 assert(disklist != NULL);
120 assert_string_is_neither_NULL_nor_zerolength(device);
121
122 for (i = 0; i < disklist->entries; i++) {
123 if (!strcmp(disklist->el[i].device, device)) {
124 break;
125 }
126 }
127 if (i == disklist->entries) {
128 return (-1);
129 } else {
130 return (i);
131 }
132}
133
134
135
136
137
138
139
140
141/**
142 * Determine which RAID device is using a particular partition.
143 * @param raidlist The RAID information structure.
144 * @param device The partition to find out about.
145 * @return The index number of the RAID device using @p device, or -1 if there is none.
146 */
147int
148which_raid_device_is_using_this_partition(struct raidlist_itself *raidlist,
149 char *device)
150{
151#ifdef __FreeBSD__
152// FreeBSD-specific version of which_raid_device_is_using_this_partition()
153 /*@ int ********************************************************* */
154 int i = 0;
155
156 for (i = 0; i < raidlist->entries; i++) {
157 bool thisone = FALSE;
158 int j, k, l;
159
160 for (j = 0; j < raidlist->el[i].plexes; ++j) {
161 for (k = 0; k < raidlist->el[i].plex[j].subdisks; ++k) {
162 for (l = 0; l < raidlist->disks.entries; ++l) {
163 if (!strcmp(raidlist->disks.el[l].device,
164 device) &&
165 !strcmp(raidlist->el[i].plex[j].sd[k].which_device,
166 raidlist->disks.el[l].name))
167 thisone = TRUE;
168 }
169 }
170 }
171
172 if (thisone) {
173 break;
174 }
175 }
176 if (i == raidlist->entries) {
177 return (-1);
178 } else {
179 return (i);
180 }
181}
182
183#else
184// Linux-specific version of which_raid_device_is_using_this_partition()
185// and one other function which FreeBSD doesn't use
186
187 int current_raiddev = 0;
188
189 assert_string_is_neither_NULL_nor_zerolength(device);
190 assert(raidlist != NULL);
191
192 for (current_raiddev = 0; current_raiddev < raidlist->entries;
193 current_raiddev++) {
194 if (where_in_drivelist_is_drive
195 (&raidlist->el[current_raiddev].data_disks, device) >= 0
196 || where_in_drivelist_is_drive(&raidlist->el[current_raiddev].
197 spare_disks, device) >= 0
198 || where_in_drivelist_is_drive(&raidlist->el[current_raiddev].
199 parity_disks, device) >= 0
200 || where_in_drivelist_is_drive(&raidlist->el[current_raiddev].
201 failed_disks, device) >= 0) {
202 break;
203 }
204 }
205 if (current_raiddev == raidlist->entries) {
206 return (-1);
207 } else {
208 return (current_raiddev);
209 }
210}
211
212/**
213 * Write an @c int variable to a list of RAID variables.
214 * @param raidrec The RAID device record to write to.
215 * @param lino The variable index number to modify/create.
216 * @param label The label to write.
217 * @param value The value to write.
218 */
219void
220write_variableINT_to_raid_var_line(struct raid_device_record *raidrec,
221 int lino, char *label, int value)
222{
223 /*@ buffers ***************************************************** */
224 char *sz_value;
225
226 malloc_string(sz_value);
227 assert(raidrec != NULL);
228 assert(label != NULL);
229
230 sprintf(sz_value, "%d", value);
231 strcpy(raidrec->additional_vars.el[lino].label, label);
232 strcpy(raidrec->additional_vars.el[lino].value, sz_value);
233 paranoid_free(sz_value);
234}
235#endif
236
237
238
239
240
241
242
243
244#ifdef __FreeBSD__
245/**
246 * Add a disk to a RAID plex.
247 * @param p The plex to add the device to.
248 * @param device_to_add The device to add to @p p.
249 */
250void add_disk_to_raid_device(struct vinum_plex *p, char *device_to_add)
251{
252 strcpy(p->sd[p->subdisks].which_device, device_to_add);
253 ++p->subdisks;
254
255}
256#else
257/**
258 * Add a disk to a RAID device.
259 * @param disklist The disklist to add the device to.
260 * @param device_to_add The device to add to @p disklist.
261 * @param index The index number of the disklist entry we're creating.
262 */
263void add_disk_to_raid_device(struct list_of_disks *disklist,
264 char *device_to_add, int index)
265{
266 int items;
267
268 assert(disklist != NULL);
269 assert_string_is_neither_NULL_nor_zerolength(device_to_add);
270 items = disklist->entries;
271 strcpy(disklist->el[items].device, device_to_add);
272 disklist->el[items].index = index;
273 items++;
274 disklist->entries = items;
275}
276#endif
277
278
279/**
280 * Save the additional RAID variables to a stream.
281 * @param vars The RAID variable list to save.
282 * @param fout The FILE pointer to save them to.
283 */
284void
285save_additional_vars_to_file(struct additional_raid_variables *vars,
286 FILE * fout)
287{
288 int i;
289
290 assert(vars != NULL);
291 assert(fout != NULL);
292
293 for (i = 0; i < vars->entries; i++) {
294 fprintf(fout, " %-21s %s\n", vars->el[i].label,
295 vars->el[i].value);
296 }
297}
298
299
300/**
301 * Save a raidlist structure to disk in raidtab format.
302 * @param raidlist The raidlist to save.
303 * @param fname The file to save it to.
304 * @return 0, always.
305 * @bug Return value is redundant.
306 */
307int save_raidlist_to_raidtab(struct raidlist_itself *raidlist, char *fname)
308{
309 FILE *fout;
310 int current_raid_device;
311#ifdef __FreeBSD__
312 int i;
313#else
314// Linux
315#endif
316
317 assert(raidlist != NULL);
318 assert_string_is_neither_NULL_nor_zerolength(fname);
319
320 if (raidlist->entries <= 0) {
321 unlink(fname);
322 log_it("Deleting raidtab (no RAID devs anyway)");
323 return (0);
324 }
325 if (!(fout = fopen(fname, "w"))) {
326 log_OS_error("Failed to save raidlist");
327 return (1);
328 }
329 fprintf(fout, "# Generated by Mondo Rescue\n");
330
331#ifdef __FreeBSD__
332 for (i = 0; i < raidlist->disks.entries; ++i) {
333 fprintf(fout, "drive %s device %s\n", raidlist->disks.el[i].name,
334 raidlist->disks.el[i].device);
335 }
336 for (i = 0; i < (raidlist->spares.entries); ++i) {
337 fprintf(fout, "drive %s device %s hotspare\n",
338 raidlist->spares.el[i].name,
339 raidlist->spares.el[i].device);
340 }
341#endif
342
343 for (current_raid_device = 0; current_raid_device < raidlist->entries;
344 current_raid_device++) {
345 save_raidrec_to_file(&raidlist->el[current_raid_device], fout);
346 }
347 paranoid_fclose(fout);
348 return (0);
349}
350
351
352/**
353 * Save an individual RAID device record to a stream.
354 * @param raidrec The RAID device record to save.
355 * @param fout The stream to save it to.
356 */
357void save_raidrec_to_file(struct
358#ifdef __FreeBSD__
359 vinum_volume
360#else
361 raid_device_record
362#endif
363 * raidrec, FILE * fout)
364{
365#ifdef __FreeBSD__
366 int i, j;
367
368 fprintf(fout, "\nvolume %s\n", raidrec->volname);
369 for (i = 0; i < raidrec->plexes; ++i) {
370 char org[24];
371 switch (raidrec->plex[i].raidlevel) {
372 case -1:
373 strcpy(org, "concat");
374 break;
375 case 0:
376 strcpy(org, "striped");
377 break;
378 case 5:
379 strcpy(org, "raid5");
380 break;
381 }
382 fprintf(fout, " plex org %s", org);
383 if (raidrec->plex[i].raidlevel != -1) {
384 fprintf(fout, " %ik", raidrec->plex[i].stripesize);
385 }
386 fprintf(fout, "\n");
387
388 for (j = 0; j < raidrec->plex[i].subdisks; ++j) {
389 fprintf(fout, " sd drive %s size 0\n",
390 raidrec->plex[i].sd[j].which_device);
391 }
392 }
393#else
394 assert(raidrec != NULL);
395 assert(fout != NULL);
396
397 fprintf(fout, "raiddev %s\n", raidrec->raid_device);
398 if (raidrec->raid_level == -2) {
399 fprintf(fout, " raid-level multipath\n");
400 } else if (raidrec->raid_level == -1) {
401 fprintf(fout, " raid-level linear\n");
402 } else {
403 fprintf(fout, " raid-level %d\n",
404 raidrec->raid_level);
405 }
406 fprintf(fout, " nr-raid-disks %d\n",
407 raidrec->data_disks.entries);
408 if (raidrec->spare_disks.entries > 0) {
409 fprintf(fout, " nr-spare-disks %d\n",
410 raidrec->spare_disks.entries);
411 }
412 if (raidrec->parity_disks.entries > 0) {
413 fprintf(fout, " nr-parity-disks %d\n",
414 raidrec->parity_disks.entries);
415 }
416 fprintf(fout, " persistent-superblock %d\n",
417 raidrec->persistent_superblock);
418 if (raidrec->chunk_size > -1) {
419 fprintf(fout, " chunk-size %d\n", raidrec->chunk_size);
420 }
421 if (raidrec->parity > -1) {
422 switch(raidrec->parity) {
423 case 0:
424 fprintf(fout, " parity-algorithm left-asymmetric\n");
425 break;
426 case 1:
427 fprintf(fout, " parity-algorithm right-asymmetric\n");
428 break;
429 case 2:
430 fprintf(fout, " parity-algorithm left-symmetric\n");
431 break;
432 case 3:
433 fprintf(fout, " parity-algorithm right-symmetric\n");
434 break;
435 default:
436 fatal_error("Unknown RAID parity algorithm.");
437 break;
438 }
439 }
440 save_additional_vars_to_file(&raidrec->additional_vars, fout);
441 fprintf(fout, "\n");
442 save_disklist_to_file("raid-disk", &raidrec->data_disks, fout);
443 save_disklist_to_file("spare-disk", &raidrec->spare_disks, fout);
444 save_disklist_to_file("parity-disk", &raidrec->parity_disks, fout);
445 save_disklist_to_file("failed-disk", &raidrec->failed_disks, fout);
446 fprintf(fout, "\n");
447#endif
448}
449
450/**
451 * Retrieve the next line from a raidtab stream.
452 * @param fin The file to read the input from.
453 * @param label Where to put the line's label.
454 * @param value Where to put the line's value.
455 * @return 0 if the line was read and stored successfully, 1 if we're at end of file.
456 */
457int get_next_raidtab_line(FILE * fin, char *label, char *value)
458{
459 char *incoming;
460 char *p;
461
462 malloc_string(incoming);
463 assert(fin != NULL);
464 assert(label != NULL);
465 assert(value != NULL);
466
467 label[0] = value[0] = '\0';
468 if (feof(fin)) {
469 paranoid_free(incoming);
470 return (1);
471 }
472 for (fgets(incoming, MAX_STR_LEN - 1, fin); !feof(fin);
473 fgets(incoming, MAX_STR_LEN - 1, fin)) {
474 strip_spaces(incoming);
475 p = strchr(incoming, ' ');
476 if (strlen(incoming) < 3 || incoming[0] == '#' || !p) {
477 continue;
478 }
479 *(p++) = '\0';
480 while (*p == ' ') {
481 p++;
482 }
483 strcpy(label, incoming);
484 strcpy(value, p);
485 paranoid_free(incoming);
486 return (0);
487 }
488 return (1);
489}
490
491
492
493/**
494 * Load a raidtab file into a raidlist structure.
495 * @param raidlist The raidlist to fill.
496 * @param fname The file to read from.
497 * @return 0 for success, 1 for failure.
498 */
499#ifdef __FreeBSD__
500int load_raidtab_into_raidlist(struct raidlist_itself *raidlist,
501 char *fname)
502{
503 FILE *fin;
504 char *tmp;
505 int items;
506
507 malloc_string(tmp);
508 raidlist->spares.entries = 0;
509 raidlist->disks.entries = 0;
510 if (length_of_file(fname) < 5) {
511 log_it("Raidtab is very small or non-existent. Ignoring it.");
512 raidlist->entries = 0;
513 paranoid_free(tmp);
514 return (0);
515 }
516 if (!(fin = fopen(fname, "r"))) {
517 log_it("Cannot open raidtab");
518 paranoid_free(tmp);
519 return (1);
520 }
521 items = 0;
522 log_it("Loading raidtab...");
523 while (!feof(fin)) {
524 int argc;
525 char **argv = get_next_vinum_conf_line(fin, &argc);
526 if (!argv)
527 break;
528 if (!strcmp(argv[0], "drive")) {
529 char *drivename, *devname;
530 if (argc < 4)
531 continue;
532 drivename = argv[1];
533 devname = get_option_val(argc, argv, "device");
534 if (!devname)
535 continue;
536
537 if (get_option_state(argc, argv, "hotspare")) {
538 strcpy(raidlist->spares.el[raidlist->spares.entries].name,
539 drivename);
540 strcpy(raidlist->spares.el[raidlist->spares.entries].
541 device, devname);
542 raidlist->spares.el[raidlist->spares.entries].index =
543 raidlist->disks.entries;
544 raidlist->spares.entries++;
545 } else {
546 strcpy(raidlist->disks.el[raidlist->disks.entries].name,
547 drivename);
548 strcpy(raidlist->disks.el[raidlist->disks.entries].device,
549 devname);
550 raidlist->disks.el[raidlist->disks.entries].index =
551 raidlist->disks.entries;
552 raidlist->disks.entries++;
553 }
554 } else if (!strcmp(argv[0], "volume")) {
555 char *volname;
556 if (argc < 2)
557 continue;
558 volname = argv[1];
559 strcpy(raidlist->el[raidlist->entries].volname, volname);
560 raidlist->el[raidlist->entries].plexes = 0;
561 raidlist->entries++;
562 } else if (!strcmp(argv[0], "plex")) {
563 int raidlevel, stripesize;
564 char *org = 0;
565 char **tmp = 0;
566 if (argc < 3)
567 continue;
568 org = get_option_val(argc, argv, "org");
569 if (!org)
570 continue;
571 if (strcmp(org, "concat")) {
572 tmp = get_option_vals(argc, argv, "org", 2);
573 if (tmp && tmp[1]) {
574 stripesize = (int) (size_spec(tmp[1]) / 1024);
575 } else
576 stripesize = 279;
577 } else
578 stripesize = 0;
579
580 if (!strcmp(org, "concat")) {
581 raidlevel = -1;
582 } else if (!strcmp(org, "striped")) {
583 raidlevel = 0;
584 } else if (!strcmp(org, "raid5")) {
585 raidlevel = 5;
586 } else
587 continue;
588
589 raidlist->el[raidlist->entries - 1].plex
590 [raidlist->el[raidlist->entries - 1].plexes].raidlevel =
591 raidlevel;
592 raidlist->el[raidlist->entries -
593 1].plex[raidlist->el[raidlist->entries -
594 1].plexes].stripesize =
595 stripesize;
596 raidlist->el[raidlist->entries -
597 1].plex[raidlist->el[raidlist->entries -
598 1].plexes].subdisks = 0;
599 raidlist->el[raidlist->entries - 1].plexes++;
600 } else if ((!strcmp(argv[0], "sd"))
601 || (!strcmp(argv[0], "subdisk"))) {
602 char *drive = 0;
603 if (argc < 3)
604 continue;
605 drive = get_option_val(argc, argv, "drive");
606 if (!drive)
607 continue;
608
609 strcpy(raidlist->el[raidlist->entries - 1].plex
610 [raidlist->el[raidlist->entries - 1].plexes - 1].sd
611 [raidlist->el[raidlist->entries - 1].plex
612 [raidlist->el[raidlist->entries - 1].plexes -
613 1].subdisks].which_device, drive);
614 raidlist->el[raidlist->entries -
615 1].plex[raidlist->el[raidlist->entries -
616 1].plexes - 1].subdisks++;
617 }
618 }
619 fclose(fin);
620 log_it("Raidtab loaded successfully.");
621 sprintf(tmp, "%d RAID devices in raidtab", raidlist->entries);
622 log_it(tmp);
623 paranoid_free(tmp);
624 return (0);
625}
626
627
628#else
629
630int load_raidtab_into_raidlist(struct raidlist_itself *raidlist,
631 char *fname)
632{
633 FILE *fin;
634 char *tmp;
635 char *label;
636 char *value;
637 int items;
638 int v;
639
640 malloc_string(tmp);
641 malloc_string(label);
642 malloc_string(value);
643 assert(raidlist != NULL);
644 assert_string_is_neither_NULL_nor_zerolength(fname);
645
646 if (length_of_file(fname) < 5) {
647 log_it("Raidtab is very small or non-existent. Ignoring it.");
648 raidlist->entries = 0;
649 paranoid_free(tmp);
650 paranoid_free(label);
651 paranoid_free(value);
652 return (0);
653 }
654 if (!(fin = fopen(fname, "r"))) {
655 log_it("Cannot open raidtab");
656 paranoid_free(tmp);
657 paranoid_free(label);
658 paranoid_free(value);
659 return (1);
660 }
661 items = 0;
662 log_it("Loading raidtab...");
663 get_next_raidtab_line(fin, label, value);
664 while (!feof(fin)) {
665 log_msg(1, "Looking for raid record #%d", items);
666 initialize_raidrec(&raidlist->el[items]);
667 v = 0;
668 /* find the 'raiddev' entry, indicating the start of the block of info */
669 while (!feof(fin) && strcmp(label, "raiddev")) {
670 strcpy(raidlist->el[items].additional_vars.el[v].label, label);
671 strcpy(raidlist->el[items].additional_vars.el[v].value, value);
672 v++;
673 get_next_raidtab_line(fin, label, value);
674 log_it(tmp);
675 }
676 raidlist->el[items].additional_vars.entries = v;
677 if (feof(fin)) {
678 log_msg(1, "No more records.");
679 continue;
680 }
681 log_msg(2, "Record #%d (%s) found", items, value);
682 strcpy(raidlist->el[items].raid_device, value);
683 for (get_next_raidtab_line(fin, label, value);
684 !feof(fin) && strcmp(label, "raiddev");
685 get_next_raidtab_line(fin, label, value)) {
686 process_raidtab_line(fin, &raidlist->el[items], label, value);
687 }
688 items++;
689 }
690 paranoid_fclose(fin);
691 raidlist->entries = items;
692 log_msg(1, "Raidtab loaded successfully.");
693 log_msg(1, "%d RAID devices in raidtab", items);
694 paranoid_free(tmp);
695 paranoid_free(label);
696 paranoid_free(value);
697 return (0);
698}
699#endif
700
701
702
703
704
705
706
707
708#ifndef __FreeBSD__
709/**
710 * Process a single line from the raidtab and store the results into @p raidrec.
711 * @param fin The stream to read the line from.
712 * @param raidrec The RAID device record to update.
713 * @param label Where to put the label processed.
714 * @param value Where to put the value processed.
715 */
716void
717process_raidtab_line(FILE * fin,
718 struct raid_device_record *raidrec,
719 char *label, char *value)
720{
721
722 /*@ add mallocs * */
723 char *tmp;
724 char *labelB;
725 char *valueB;
726
727 struct list_of_disks *disklist;
728 int index;
729 int v;
730
731 malloc_string(tmp);
732 malloc_string(labelB);
733 malloc_string(valueB);
734 assert(fin != NULL);
735 assert(raidrec != NULL);
736 assert_string_is_neither_NULL_nor_zerolength(label);
737 assert(value != NULL);
738
739 if (!strcmp(label, "raid-level")) {
740 if (!strcmp(value, "multipath")) {
741 raidrec->raid_level = -2;
742 } else if (!strcmp(value, "linear")) {
743 raidrec->raid_level = -1;
744 } else {
745 raidrec->raid_level = atoi(value);
746 }
747 } else if (!strcmp(label, "nr-raid-disks")) { /* ignore it */
748 } else if (!strcmp(label, "nr-spare-disks")) { /* ignore it */
749 } else if (!strcmp(label, "nr-parity-disks")) { /* ignore it */
750 } else if (!strcmp(label, "nr-failed-disks")) { /* ignore it */
751 } else if (!strcmp(label, "persistent-superblock")) {
752 raidrec->persistent_superblock = atoi(value);
753 } else if (!strcmp(label, "chunk-size")) {
754 raidrec->chunk_size = atoi(value);
755 } else if (!strcmp(label, "parity-algorithm")) {
756 if (!strcmp(value, "left-asymmetric")) {
757 raidrec->parity = 0;
758 } else if (!strcmp(value, "right-asymmetric")) {
759 raidrec->parity = 1;
760 } else if (!strcmp(value, "left-symmetric")) {
761 raidrec->parity = 2;
762 } else if (!strcmp(value, "right-symmetric")) {
763 raidrec->parity = 3;
764 } else {
765 log_msg(1, "Unknown RAID parity algorithm '%s'\n.", value);
766 }
767 } else if (!strcmp(label, "device")) {
768 get_next_raidtab_line(fin, labelB, valueB);
769 if (!strcmp(labelB, "raid-disk")) {
770 disklist = &raidrec->data_disks;
771 } else if (!strcmp(labelB, "spare-disk")) {
772 disklist = &raidrec->spare_disks;
773 } else if (!strcmp(labelB, "parity-disk")) {
774 disklist = &raidrec->parity_disks;
775 } else if (!strcmp(labelB, "failed-disk")) {
776 disklist = &raidrec->failed_disks;
777 } else {
778 disklist = NULL;
779 }
780 if (!disklist) {
781 sprintf(tmp,
782 "Ignoring '%s %s' pair of disk %s", labelB, valueB,
783 label);
784 log_it(tmp);
785 } else {
786 index = atoi(valueB);
787 add_disk_to_raid_device(disklist, value, index);
788 }
789 } else {
790 v = raidrec->additional_vars.entries;
791 strcpy(raidrec->additional_vars.el[v].label, label);
792 strcpy(raidrec->additional_vars.el[v].value, value);
793 raidrec->additional_vars.entries = ++v;
794 }
795 paranoid_free(tmp);
796 paranoid_free(labelB);
797 paranoid_free(valueB);
798}
799#endif
800
801
802/**
803 * Save a disklist to a stream in raidtab format.
804 * @param listname One of "raid-disk", "spare-disk", "parity-disk", or "failed-disk".
805 * @param disklist The disklist to save to @p fout.
806 * @param fout The stream to write to.
807 */
808void
809save_disklist_to_file(char *listname,
810 struct list_of_disks *disklist, FILE * fout)
811{
812 int i;
813
814 assert_string_is_neither_NULL_nor_zerolength(listname);
815 assert(disklist != NULL);
816 assert(fout != NULL);
817
818 for (i = 0; i < disklist->entries; i++) {
819 fprintf(fout, " device %s\n",
820 disklist->el[i].device);
821 fprintf(fout, " %-21s %d\n", listname, disklist->el[i].index);
822 }
823}
824
825
826
827
828
829#ifdef __FreeBSD__
830/**
831 * Add a new plex to a volume. The index of the plex will be <tt>v-\>plexes - 1</tt>.
832 * @param v The volume to operate on.
833 * @param raidlevel The RAID level of the new plex.
834 * @param stripesize The stripe size (chunk size) of the new plex.
835 */
836void add_plex_to_volume(struct vinum_volume *v, int raidlevel,
837 int stripesize)
838{
839 v->plex[v->plexes].raidlevel = raidlevel;
840 v->plex[v->plexes].stripesize = stripesize;
841 v->plex[v->plexes].subdisks = 0;
842 ++v->plexes;
843}
844
845/**
846 * For internal use only.
847 */
848char **get_next_vinum_conf_line(FILE * f, int *argc)
849{
850 int cnt = 0;
851 static char *argv[64];
852 char **ap;
853 char *line = (char *) malloc(MAX_STR_LEN);
854 if (!line)
855 errx(1,
856 "unable to allocate %i bytes of memory for `char *line' at %s:%i",
857 MAX_STR_LEN, __FILE__, __LINE__);
858 (void) fgets(line, MAX_STR_LEN, f);
859 if (feof(f)) {
860 log_it("[GNVCL] Uh... I reached the EOF.");
861 return 0;
862 }
863
864 for (ap = argv; (*ap = strsep(&line, " \t")) != NULL;)
865 if (**ap != '\0') {
866 if (++ap >= &argv[64])
867 break;
868 cnt++;
869 }
870
871 if (strchr(argv[cnt - 1], '\n')) {
872 *(strchr(argv[cnt - 1], '\n')) = '\0';
873 }
874
875 if (argc)
876 *argc = cnt;
877 return argv;
878}
879
880/**
881 * For internal use only.
882 */
883char *get_option_val(int argc, char **argv, char *option)
884{
885 int i;
886 for (i = 0; i < (argc - 1); ++i) {
887 if (!strcmp(argv[i], option)) {
888 return argv[i + 1];
889 }
890 }
891 return 0;
892}
893
894/**
895 * For internal use only.
896 */
897char **get_option_vals(int argc, char **argv, char *option, int nval)
898{
899 int i, j;
900 static char **ret;
901 ret = (char **) malloc(nval * sizeof(char *));
902 for (i = 0; i < (argc - nval); ++i) {
903 if (!strcmp(argv[i], option)) {
904 for (j = 0; j < nval; ++j) {
905 ret[j] = (char *) malloc(strlen(argv[i + j + 1]) + 1);
906 strcpy(ret[j], argv[i + j + 1]);
907 }
908 return ret;
909 }
910 }
911 return 0;
912}
913
914/**
915 * For internal use only.
916 */
917bool get_option_state(int argc, char **argv, char *option)
918{
919 int i;
920 for (i = 0; i < argc; ++i)
921 if (!strcmp(argv[i], option))
922 return TRUE;
923
924 return FALSE;
925}
926
927/**
928 * Taken from Vinum source -- for internal use only.
929 */
930long long size_spec(char *spec)
931{
932 u_int64_t size;
933 char *s;
934 int sign = 1; /* -1 if negative */
935
936 size = 0;
937 if (spec != NULL) { /* we have a parameter */
938 s = spec;
939 if (*s == '-') { /* negative, */
940 sign = -1;
941 s++; /* skip */
942 }
943 if ((*s >= '0') && (*s <= '9')) { /* it's numeric */
944 while ((*s >= '0') && (*s <= '9')) /* it's numeric */
945 size = size * 10 + *s++ - '0'; /* convert it */
946 switch (*s) {
947 case '\0':
948 return size * sign;
949
950 case 'B':
951 case 'b':
952 case 'S':
953 case 's':
954 return size * sign * 512;
955
956 case 'K':
957 case 'k':
958 return size * sign * 1024;
959
960 case 'M':
961 case 'm':
962 return size * sign * 1024 * 1024;
963
964 case 'G':
965 case 'g':
966 return size * sign * 1024 * 1024 * 1024;
967
968 case 'T':
969 case 't':
970 log_it
971 ("Ok, I'm scared... Someone did a TERABYTE+ size-spec");
972 return size * sign * 1024 * 1024 * 1024 * 1024;
973
974 case 'P':
975 case 'p':
976 log_it
977 ("If I was scared last time, I'm freaked out now. Someone actually has a PETABYTE?!?!?!?!");
978 return size * sign * 1024 * 1024 * 1024 * 1024 * 1024;
979
980 case 'E':
981 case 'e':
982 log_it
983 ("Okay, I'm REALLY freaked out. Who could devote a whole EXABYTE to their data?!?!");
984 return size * sign * 1024 * 1024 * 1024 * 1024 * 1024 *
985 1024;
986
987 case 'Z':
988 case 'z':
989 log_it
990 ("WHAT!?!? A ZETABYTE!?!? You've GOT to be kidding me!!!");
991 return size * sign * 1024 * 1024 * 1024 * 1024 * 1024 *
992 1024 * 1024;
993
994 case 'Y':
995 case 'y':
996 log_it
997 ("Oh my gosh. You actually think a YOTTABYTE will get you anywhere? What're you going to do with 1,208,925,819,614,629,174,706,176 bytes?!?!");
998 popup_and_OK
999 ("That sizespec is more than 1,208,925,819,614,629,174,706,176 bytes. You have a shocking amount of data. Please send a screenshot to the list :-)");
1000 return size * sign * 1024 * 1024 * 1024 * 1024 * 1024 *
1001 1024 * 1024 * 1024;
1002 }
1003 }
1004 }
1005 return size * sign;
1006}
1007
1008#endif
1009
1010
1011
1012
1013int parse_mdstat(struct raidlist_itself *raidlist, char *device_prefix) {
1014
1015 const char delims[] = " ";
1016
1017 FILE *fin;
1018 int res = 0, row, i, index_min;
1019 int lastpos = 0;
1020 size_t len = 0;
1021 char *token;
1022 char *string = NULL;
1023 char *pos;
1024 char type;
1025 char *strtmp;
1026
1027 // open file
1028 if (!(fin = fopen(MDSTAT_FILE, "r"))) {
1029 log_msg(1, "Could not open %s.\n", MDSTAT_FILE);
1030 return 1;
1031 }
1032 // initialise record, build progress and row counters
1033 raidlist->entries = 0;
1034 raidlist->el[raidlist->entries].progress = 999;
1035 row = 1;
1036 // skip first output row - contains registered RAID levels
1037 res = getline(&string, &len, fin);
1038 // parse the rest
1039 while ( !feof_unlocked(fin) ) {
1040 res = getline(&string, &len, fin);
1041 if (res <= 0) break;
1042 // trim leading spaces
1043 pos = string;
1044 while (*pos == ' ') pos += 1;
1045 asprintf(&strtmp, pos);
1046 strcpy(string, strtmp);
1047 paranoid_free(strtmp);
1048 // if we have newline after only spaces, this is a blank line, update
1049 // counters, otherwise do normal parsing
1050 if (*string == '\n') {
1051 row = 1;
1052 raidlist->entries++;
1053 raidlist->el[raidlist->entries].progress = 999;
1054 } else {
1055 switch (row) {
1056 case 1: // device information
1057 // check whether last line of record and if so skip
1058 pos = strcasestr(string, "unused devices: ");
1059 if (pos == string) {
1060 //raidlist->entries--;
1061 break;
1062 }
1063 // tokenise string
1064 token = mr_strtok (string, delims, &lastpos);
1065 // get RAID device name
1066 asprintf(&strtmp,"%s%s", device_prefix, token);
1067 strcpy(raidlist->el[raidlist->entries].raid_device, strtmp);
1068 paranoid_free(strtmp);
1069 paranoid_free(token);
1070 // skip ':' and status
1071 token = mr_strtok (string, delims, &lastpos);
1072 paranoid_free(token);
1073 token = mr_strtok (string, delims, &lastpos);
1074 if (!strcmp(token, "inactive")) {
1075 log_msg(1, "RAID device '%s' inactive.\n",
1076 raidlist->el[raidlist->entries].raid_device);
1077 paranoid_free(string);
1078 paranoid_free(token);
1079 return 1;
1080 }
1081 paranoid_free(token);
1082
1083 // get RAID level
1084 token = mr_strtok (string, delims, &lastpos);
1085 if (!strcmp(token, "multipath")) {
1086 raidlist->el[raidlist->entries].raid_level = -2;
1087 } else if (!strcmp(token, "linear")) {
1088 raidlist->el[raidlist->entries].raid_level = -1;
1089 } else if (!strcmp(token, "raid0")) {
1090 raidlist->el[raidlist->entries].raid_level = 0;
1091 } else if (!strcmp(token, "raid1")) {
1092 raidlist->el[raidlist->entries].raid_level = 1;
1093 } else if (!strcmp(token, "raid4")) {
1094 raidlist->el[raidlist->entries].raid_level = 4;
1095 } else if (!strcmp(token, "raid5")) {
1096 raidlist->el[raidlist->entries].raid_level = 5;
1097 } else if (!strcmp(token, "raid6")) {
1098 raidlist->el[raidlist->entries].raid_level = 6;
1099 } else if (!strcmp(token, "raid10")) {
1100 raidlist->el[raidlist->entries].raid_level = 10;
1101 } else {
1102 log_msg(1, "Unknown RAID level '%s'.\n", token);
1103 paranoid_free(string);
1104 paranoid_free(token);
1105 return 1;
1106 }
1107 paranoid_free(token);
1108
1109 // get RAID devices (type, index, device)
1110 // Note: parity disk for RAID4 is last normal disk, there is no '(P)'
1111 raidlist->el[raidlist->entries].data_disks.entries = 0;
1112 raidlist->el[raidlist->entries].spare_disks.entries = 0;
1113 raidlist->el[raidlist->entries].failed_disks.entries = 0;
1114 while((token = mr_strtok (string, delims, &lastpos))) {
1115 if ((pos = strstr(token, "("))) {
1116 type = *(pos+1);
1117 } else {
1118 type = ' ';
1119 }
1120 pos = strstr(token, "[");
1121 *pos = '\0';
1122 switch(type) {
1123 case ' ': // normal data disks
1124 raidlist->el[raidlist->entries].data_disks.el[raidlist->el[raidlist->entries].data_disks.entries].index = atoi(pos + 1);
1125 asprintf(&strtmp,"%s%s", device_prefix, token);
1126 strcpy(raidlist->el[raidlist->entries].data_disks.el[raidlist->el[raidlist->entries].data_disks.entries].device, strtmp);
1127 paranoid_free(strtmp);
1128 raidlist->el[raidlist->entries].data_disks.entries++;
1129 break;
1130 case 'S': // spare disks
1131 raidlist->el[raidlist->entries].spare_disks.el[raidlist->el[raidlist->entries].spare_disks.entries].index = atoi(pos + 1);
1132 asprintf(&strtmp,"%s%s", device_prefix, token);
1133 strcpy(raidlist->el[raidlist->entries].spare_disks.el[raidlist->el[raidlist->entries].spare_disks.entries].device, strtmp);
1134 paranoid_free(strtmp);
1135 raidlist->el[raidlist->entries].spare_disks.entries++;
1136 break;
1137 case 'F': // failed disks
1138 raidlist->el[raidlist->entries].failed_disks.el[raidlist->el[raidlist->entries].failed_disks.entries].index = atoi(pos + 1);
1139 asprintf(&strtmp,"%s%s", device_prefix, token);
1140 strcpy(raidlist->el[raidlist->entries].failed_disks.el[raidlist->el[raidlist->entries].failed_disks.entries].device, strtmp);
1141 paranoid_free(strtmp);
1142 raidlist->el[raidlist->entries].failed_disks.entries++;
1143 log_it("At least one failed disk found in RAID array.\n");
1144 break;
1145 default: // error
1146 log_msg(1, "Unknown device type '%c'\n", type);
1147 paranoid_free(string);
1148 paranoid_free(token);
1149 return 1;
1150 break;
1151 }
1152 paranoid_free(token);
1153 }
1154
1155 // adjust index for each device so that it starts with 0 for every type
1156 index_min = 99;
1157 for (i=0; i<raidlist->el[raidlist->entries].data_disks.entries;i++) {
1158 if (raidlist->el[raidlist->entries].data_disks.el[i].index < index_min) {
1159 index_min = raidlist->el[raidlist->entries].data_disks.el[i].index;
1160 }
1161 }
1162 if (index_min > 0) {
1163 for (i=0; i<raidlist->el[raidlist->entries].data_disks.entries;i++) {
1164 raidlist->el[raidlist->entries].data_disks.el[i].index = raidlist->el[raidlist->entries].data_disks.el[i].index - index_min;
1165 }
1166 }
1167 index_min = 99;
1168 for (i=0; i<raidlist->el[raidlist->entries].spare_disks.entries;i++) {
1169 if (raidlist->el[raidlist->entries].spare_disks.el[i].index < index_min) {
1170 index_min = raidlist->el[raidlist->entries].spare_disks.el[i].index;
1171 }
1172 }
1173 if (index_min > 0) {
1174 for (i=0; i<raidlist->el[raidlist->entries].spare_disks.entries;i++) {
1175 raidlist->el[raidlist->entries].spare_disks.el[i].index = raidlist->el[raidlist->entries].spare_disks.el[i].index - index_min;
1176 }
1177 }
1178 index_min = 99;
1179 for (i=0; i<raidlist->el[raidlist->entries].failed_disks.entries;i++) {
1180 if (raidlist->el[raidlist->entries].failed_disks.el[i].index < index_min) {
1181 index_min = raidlist->el[raidlist->entries].failed_disks.el[i].index;
1182 }
1183 }
1184 if (index_min > 0) {
1185 for (i=0; i<raidlist->el[raidlist->entries].failed_disks.entries;i++) {
1186 raidlist->el[raidlist->entries].failed_disks.el[i].index = raidlist->el[raidlist->entries].failed_disks.el[i].index - index_min;
1187 }
1188 }
1189 break;
1190 case 2: // config information
1191 // check for persistent super block
1192 if (strcasestr(string, "super non-persistent")) {
1193 raidlist->el[raidlist->entries].persistent_superblock = 0;
1194 } else {
1195 raidlist->el[raidlist->entries].persistent_superblock = 1;
1196 }
1197 // extract chunk size
1198 if (!(pos = strcasestr(string, "k chunk"))) {
1199 raidlist->el[raidlist->entries].chunk_size = -1;
1200 } else {
1201 while (*pos != ' ') {
1202 pos -= 1;
1203 if (pos < string) {
1204 log_it("String underflow!\n");
1205 paranoid_free(string);
1206 return 1;
1207 }
1208 }
1209 raidlist->el[raidlist->entries].chunk_size = atoi(pos + 1);
1210 }
1211 // extract parity if present
1212 if ((pos = strcasestr(string, "algorithm"))) {
1213 raidlist->el[raidlist->entries].parity = atoi(pos + 9);
1214 } else {
1215 raidlist->el[raidlist->entries].parity = -1;
1216 }
1217 break;
1218 case 3: // optional build status information
1219 if (!(pos = strchr(string, '\%'))) {
1220 if (strcasestr(string, "delayed")) {
1221 raidlist->el[raidlist->entries].progress = -1; // delayed (therefore, stuck at 0%)
1222 } else {
1223 raidlist->el[raidlist->entries].progress = 999; // not found
1224 }
1225 } else {
1226 while (*pos != ' ') {
1227 pos -= 1;
1228 if (pos < string) {
1229 printf("ERROR: String underflow!\n");
1230 paranoid_free(string);
1231 return 1;
1232 }
1233 }
1234 raidlist->el[raidlist->entries].progress = atoi(pos);
1235 }
1236 break;
1237 default: // error or IN PROGRESS
1238 if (raidlist->el[raidlist->entries].progress != -1 &&
1239 raidlist->el[raidlist->entries].progress != 999) {
1240 log_msg(1, "Row %d should not occur in record!\n", row);
1241 }
1242 break;
1243 }
1244 row++;
1245 }
1246 }
1247 // close file
1248 fclose(fin);
1249 // free string
1250 paranoid_free(string);
1251 // return success
1252 return 0;
1253
1254}
1255
1256
1257
1258
1259int create_raidtab_from_mdstat(char *raidtab_fname)
1260{
1261 struct raidlist_itself *raidlist;
1262 int retval = 0;
1263
1264 raidlist = malloc(sizeof(struct raidlist_itself));
1265
1266 // FIXME: Prefix '/dev/' should really be dynamic!
1267 if (parse_mdstat(raidlist, "/dev/")) {
1268 log_to_screen("Sorry, cannot read %s", MDSTAT_FILE);
1269 return (1);
1270 }
1271
1272 retval += save_raidlist_to_raidtab(raidlist, raidtab_fname);
1273 return (retval);
1274}
1275
1276
1277
1278/* @} - end of raidGroup */
Note: See TracBrowser for help on using the repository browser.