source: MondoRescue/branches/3.1/mondo/src/common/libmondo-raid.c@ 3190

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