source: MondoRescue/branches/3.0/mondo/src/common/libmondo-raid.c@ 2989

Last change on this file since 2989 was 2989, checked in by Bruno Cornec, 12 years ago

r4626@localhost: bruno | 2012-03-30 16:24:25 +0200

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