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

Last change on this file since 3154 was 3154, checked in by Bruno Cornec, 11 years ago
  • Remove the introduction of LogProgress in ListKernelModulePaths which should stay quiet
  • Remove a remaining support of max-noof-media in confidure.in now gone.
  • Fix #640 by cleaning up fully the managemnt of tmpdir and scratchdir (one function for the creation, one point of removal at end, intermediate removal if needed, clean naming conventions, unify mkdtemp usage, remove sensibly_set_tmpdir_and_scratchdir function). Consequence is that default dir for scratchdir if nothing else specified is /tmp using CLI and the largest partition using the GUI.
  • Fix a bug introduced in process_the_s_switch with intermediate variables which weren't initialized correctly.
  • Property svn:keywords set to Id
File size: 34.4 KB
Line 
1/* libmondo-raid.c subroutines for handling RAID
2 $Id: libmondo-raid.c 3154 2013-06-22 22:34:33Z 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 3154 2013-06-22 22:34:33Z 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
338 fprintf(fout, "\nvolume %s\n", raidrec->volname);
339 for (i = 0; i < raidrec->plexes; ++i) {
340 char org[24];
341 switch (raidrec->plex[i].raidlevel) {
342 case -1:
343 strcpy(org, "concat");
344 break;
345 case 0:
346 strcpy(org, "striped");
347 break;
348 case 5:
349 strcpy(org, "raid5");
350 break;
351 }
352 fprintf(fout, " plex org %s", org);
353 if (raidrec->plex[i].raidlevel != -1) {
354 fprintf(fout, " %ik", raidrec->plex[i].stripesize);
355 }
356 fprintf(fout, "\n");
357
358 for (j = 0; j < raidrec->plex[i].subdisks; ++j) {
359 fprintf(fout, " sd drive %s size 0\n",
360 raidrec->plex[i].sd[j].which_device);
361 }
362 }
363#else
364 assert(raidrec != NULL);
365 assert(fout != NULL);
366
367 fprintf(fout, "raiddev %s\n", raidrec->raid_device);
368 if (raidrec->raid_level == -2) {
369 fprintf(fout, " raid-level multipath\n");
370 } else if (raidrec->raid_level == -1) {
371 fprintf(fout, " raid-level linear\n");
372 } else {
373 fprintf(fout, " raid-level %d\n",
374 raidrec->raid_level);
375 }
376 fprintf(fout, " nr-raid-disks %d\n",
377 raidrec->data_disks.entries);
378 if (raidrec->spare_disks.entries > 0) {
379 fprintf(fout, " nr-spare-disks %d\n",
380 raidrec->spare_disks.entries);
381 }
382 if (raidrec->parity_disks.entries > 0) {
383 fprintf(fout, " nr-parity-disks %d\n",
384 raidrec->parity_disks.entries);
385 }
386 fprintf(fout, " persistent-superblock %d\n",
387 raidrec->persistent_superblock);
388 if (raidrec->chunk_size > -1) {
389 fprintf(fout, " chunk-size %d\n", raidrec->chunk_size);
390 }
391 if (raidrec->parity > -1) {
392 switch(raidrec->parity) {
393 case 0:
394 fprintf(fout, " parity-algorithm left-asymmetric\n");
395 break;
396 case 1:
397 fprintf(fout, " parity-algorithm right-asymmetric\n");
398 break;
399 case 2:
400 fprintf(fout, " parity-algorithm left-symmetric\n");
401 break;
402 case 3:
403 fprintf(fout, " parity-algorithm right-symmetric\n");
404 break;
405 default:
406 fatal_error("Unknown RAID parity algorithm.");
407 break;
408 }
409 }
410 save_additional_vars_to_file(&raidrec->additional_vars, fout);
411 fprintf(fout, "\n");
412 save_disklist_to_file("raid-disk", &raidrec->data_disks, fout);
413 save_disklist_to_file("spare-disk", &raidrec->spare_disks, fout);
414 save_disklist_to_file("parity-disk", &raidrec->parity_disks, fout);
415 save_disklist_to_file("failed-disk", &raidrec->failed_disks, fout);
416 fprintf(fout, "\n");
417#endif
418}
419
420/**
421 * Retrieve the next line from a raidtab stream.
422 * @param fin The file to read the input from.
423 * @param label Where to put the line's label.
424 * @param value Where to put the line's value.
425 * @return 0 if the line was read and stored successfully, 1 if we're at end of file.
426 */
427int get_next_raidtab_line(FILE * fin, char *label, char *value)
428{
429 char *incoming;
430 char *p;
431 char *q;
432
433 malloc_string(incoming);
434 assert(fin != NULL);
435 assert(label != NULL);
436 assert(value != NULL);
437
438 label[0] = value[0] = '\0';
439 if (feof(fin)) {
440 paranoid_free(incoming);
441 return (1);
442 }
443 for (q = fgets(incoming, MAX_STR_LEN - 1, fin); !feof(fin) && (q != NULL);
444 q = fgets(incoming, MAX_STR_LEN - 1, fin)) {
445 strip_spaces(incoming);
446 p = strchr(incoming, ' ');
447 if (strlen(incoming) < 3 || incoming[0] == '#' || !p) {
448 continue;
449 }
450 *(p++) = '\0';
451 while (*p == ' ') {
452 p++;
453 }
454 strcpy(label, incoming);
455 strcpy(value, p);
456 paranoid_free(incoming);
457 return (0);
458 }
459 return (1);
460}
461
462
463
464/**
465 * Load a raidtab file into a raidlist structure.
466 * @param raidlist The raidlist to fill.
467 * @param fname The file to read from.
468 * @return 0 for success, 1 for failure.
469 */
470#ifdef __FreeBSD__
471int load_raidtab_into_raidlist(struct raidlist_itself *raidlist,
472 char *fname)
473{
474 FILE *fin;
475 char *tmp1 = NULL;
476 int items;
477
478 raidlist->spares.entries = 0;
479 raidlist->disks.entries = 0;
480 if (length_of_file(fname) < 5) {
481 log_it("Raidtab is very small or non-existent. Ignoring it.");
482 raidlist->entries = 0;
483 return (0);
484 }
485 if (!(fin = fopen(fname, "r"))) {
486 log_it("Cannot open raidtab");
487 return (1);
488 }
489 items = 0;
490 log_it("Loading raidtab...");
491 while (!feof(fin)) {
492 int argc;
493 char **argv = get_next_vinum_conf_line(fin, &argc);
494 if (!argv)
495 break;
496 if (!strcmp(argv[0], "drive")) {
497 char *drivename, *devname;
498 if (argc < 4)
499 continue;
500 drivename = argv[1];
501 devname = get_option_val(argc, argv, "device");
502 if (!devname)
503 continue;
504
505 if (get_option_state(argc, argv, "hotspare")) {
506 strcpy(raidlist->spares.el[raidlist->spares.entries].name,
507 drivename);
508 strcpy(raidlist->spares.el[raidlist->spares.entries].
509 device, devname);
510 raidlist->spares.el[raidlist->spares.entries].index =
511 raidlist->disks.entries;
512 raidlist->spares.entries++;
513 } else {
514 strcpy(raidlist->disks.el[raidlist->disks.entries].name,
515 drivename);
516 strcpy(raidlist->disks.el[raidlist->disks.entries].device,
517 devname);
518 raidlist->disks.el[raidlist->disks.entries].index =
519 raidlist->disks.entries;
520 raidlist->disks.entries++;
521 }
522 } else if (!strcmp(argv[0], "volume")) {
523 char *volname;
524 if (argc < 2)
525 continue;
526 volname = argv[1];
527 strcpy(raidlist->el[raidlist->entries].volname, volname);
528 raidlist->el[raidlist->entries].plexes = 0;
529 raidlist->entries++;
530 } else if (!strcmp(argv[0], "plex")) {
531 int raidlevel, stripesize;
532 char *org = 0;
533 char **tmp = 0;
534 if (argc < 3)
535 continue;
536 org = get_option_val(argc, argv, "org");
537 if (!org)
538 continue;
539 if (strcmp(org, "concat")) {
540 tmp = get_option_vals(argc, argv, "org", 2);
541 if (tmp && tmp[1]) {
542 stripesize = (int) (size_spec(tmp[1]) / 1024);
543 } else
544 stripesize = 279;
545 } else
546 stripesize = 0;
547
548 if (!strcmp(org, "concat")) {
549 raidlevel = -1;
550 } else if (!strcmp(org, "striped")) {
551 raidlevel = 0;
552 } else if (!strcmp(org, "raid5")) {
553 raidlevel = 5;
554 } else
555 continue;
556
557 raidlist->el[raidlist->entries - 1].plex
558 [raidlist->el[raidlist->entries - 1].plexes].raidlevel =
559 raidlevel;
560 raidlist->el[raidlist->entries -
561 1].plex[raidlist->el[raidlist->entries -
562 1].plexes].stripesize =
563 stripesize;
564 raidlist->el[raidlist->entries -
565 1].plex[raidlist->el[raidlist->entries -
566 1].plexes].subdisks = 0;
567 raidlist->el[raidlist->entries - 1].plexes++;
568 } else if ((!strcmp(argv[0], "sd"))
569 || (!strcmp(argv[0], "subdisk"))) {
570 char *drive = 0;
571 if (argc < 3)
572 continue;
573 drive = get_option_val(argc, argv, "drive");
574 if (!drive)
575 continue;
576
577 strcpy(raidlist->el[raidlist->entries - 1].plex
578 [raidlist->el[raidlist->entries - 1].plexes - 1].sd
579 [raidlist->el[raidlist->entries - 1].plex
580 [raidlist->el[raidlist->entries - 1].plexes -
581 1].subdisks].which_device, drive);
582 raidlist->el[raidlist->entries -
583 1].plex[raidlist->el[raidlist->entries -
584 1].plexes - 1].subdisks++;
585 }
586 }
587 fclose(fin);
588 log_it("Raidtab loaded successfully.");
589 mr_asprintf(&tmp1, "%d RAID devices in raidtab", raidlist->entries);
590 log_it(tmp1);
591 mr_free(tmp1);
592 return (0);
593}
594
595
596#else
597
598int load_raidtab_into_raidlist(struct raidlist_itself *raidlist,
599 char *fname)
600{
601 FILE *fin;
602 char *label;
603 char *value;
604 int items;
605 int v;
606
607 assert(raidlist != NULL);
608 assert_string_is_neither_NULL_nor_zerolength(fname);
609
610 if (length_of_file(fname) < 5) {
611 log_it("Raidtab is very small or non-existent. Ignoring it.");
612 raidlist->entries = 0;
613 return (0);
614 }
615 if (!(fin = fopen(fname, "r"))) {
616 log_it("Cannot open raidtab");
617 return (1);
618 }
619 malloc_string(label);
620 malloc_string(value);
621 items = 0;
622 log_it("Loading raidtab...");
623 get_next_raidtab_line(fin, label, value);
624 while (!feof(fin)) {
625 log_msg(1, "Looking for raid record #%d", items);
626 initialize_raidrec(&raidlist->el[items]);
627 v = 0;
628 /* find the 'raiddev' entry, indicating the start of the block of info */
629 while (!feof(fin) && strcmp(label, "raiddev")) {
630 strcpy(raidlist->el[items].additional_vars.el[v].label, label);
631 strcpy(raidlist->el[items].additional_vars.el[v].value, value);
632 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);
633 v++;
634 get_next_raidtab_line(fin, label, value);
635 }
636 raidlist->el[items].additional_vars.entries = v;
637 if (feof(fin)) {
638 log_msg(1, "No more records.");
639 continue;
640 }
641 log_msg(2, "Record #%d (%s) found", items, value);
642 strcpy(raidlist->el[items].raid_device, value);
643 for (get_next_raidtab_line(fin, label, value);
644 !feof(fin) && strcmp(label, "raiddev");
645 get_next_raidtab_line(fin, label, value)) {
646 process_raidtab_line(fin, &raidlist->el[items], label, value);
647 }
648 items++;
649 }
650 paranoid_fclose(fin);
651 raidlist->entries = items;
652 log_msg(1, "Raidtab loaded successfully.");
653 log_msg(1, "%d RAID devices in raidtab", items);
654 paranoid_free(label);
655 paranoid_free(value);
656 return (0);
657}
658#endif
659
660
661
662
663
664
665
666
667#ifndef __FreeBSD__
668/**
669 * Process a single line from the raidtab and store the results into @p raidrec.
670 * @param fin The stream to read the line from.
671 * @param raidrec The RAID device record to update.
672 * @param label Where to put the label processed.
673 * @param value Where to put the value processed.
674 */
675void
676process_raidtab_line(FILE * fin,
677 struct raid_device_record *raidrec,
678 char *label, char *value)
679{
680
681 /*@ add mallocs * */
682 char *tmp = NULL;
683 char *labelB;
684 char *valueB;
685
686 struct list_of_disks *disklist;
687 int index;
688 int v;
689
690 malloc_string(labelB);
691 malloc_string(valueB);
692 assert(fin != NULL);
693 assert(raidrec != NULL);
694 assert_string_is_neither_NULL_nor_zerolength(label);
695 assert(value != NULL);
696
697 if (!strcmp(label, "raid-level")) {
698 if (!strcmp(value, "multipath")) {
699 raidrec->raid_level = -2;
700 } else if (!strcmp(value, "linear")) {
701 raidrec->raid_level = -1;
702 } else {
703 raidrec->raid_level = atoi(value);
704 }
705 log_msg(4,"Found raid level %d",raidrec->raid_level);
706 } else if (!strcmp(label, "nr-raid-disks")) { /* ignore it */
707 } else if (!strcmp(label, "nr-spare-disks")) { /* ignore it */
708 } else if (!strcmp(label, "nr-parity-disks")) { /* ignore it */
709 } else if (!strcmp(label, "nr-failed-disks")) { /* ignore it */
710 } else if (!strcmp(label, "persistent-superblock")) {
711 raidrec->persistent_superblock = atoi(value);
712 } else if (!strcmp(label, "chunk-size")) {
713 raidrec->chunk_size = atoi(value);
714 } else if (!strcmp(label, "parity-algorithm")) {
715 if (!strcmp(value, "left-asymmetric")) {
716 raidrec->parity = 0;
717 } else if (!strcmp(value, "right-asymmetric")) {
718 raidrec->parity = 1;
719 } else if (!strcmp(value, "left-symmetric")) {
720 raidrec->parity = 2;
721 } else if (!strcmp(value, "right-symmetric")) {
722 raidrec->parity = 3;
723 } else {
724 log_msg(1, "Unknown RAID parity algorithm '%s'\n.", value);
725 }
726 log_msg(4,"Found raid parity %d",raidrec->parity);
727 } else if (!strcmp(label, "device")) {
728 get_next_raidtab_line(fin, labelB, valueB);
729 if (!strcmp(labelB, "raid-disk")) {
730 disklist = &raidrec->data_disks;
731 } else if (!strcmp(labelB, "spare-disk")) {
732 disklist = &raidrec->spare_disks;
733 } else if (!strcmp(labelB, "parity-disk")) {
734 disklist = &raidrec->parity_disks;
735 } else if (!strcmp(labelB, "failed-disk")) {
736 disklist = &raidrec->failed_disks;
737 } else {
738 disklist = NULL;
739 }
740 if (!disklist) {
741 mr_asprintf(&tmp, "Ignoring '%s %s' pair of disk %s", labelB, valueB, label);
742 log_it(tmp);
743 mr_free(tmp);
744 } else {
745 index = atoi(valueB);
746 add_disk_to_raid_device(disklist, value, index);
747 }
748 } else {
749 v = raidrec->additional_vars.entries;
750 strcpy(raidrec->additional_vars.el[v].label, label);
751 strcpy(raidrec->additional_vars.el[v].value, value);
752 log_msg(4,"Found additional raid pair #%d: %s / %s",v,raidrec->additional_vars.el[v].label,raidrec->additional_vars.el[v].value);
753 v++;
754 raidrec->additional_vars.entries = v;
755 }
756 paranoid_free(labelB);
757 paranoid_free(valueB);
758}
759#endif
760
761
762/**
763 * Save a disklist to a stream in raidtab format.
764 * @param listname One of "raid-disk", "spare-disk", "parity-disk", or "failed-disk".
765 * @param disklist The disklist to save to @p fout.
766 * @param fout The stream to write to.
767 */
768void
769save_disklist_to_file(char *listname,
770 struct list_of_disks *disklist, FILE * fout)
771{
772 int i;
773
774 assert_string_is_neither_NULL_nor_zerolength(listname);
775 assert(disklist != NULL);
776 assert(fout != NULL);
777
778 for (i = 0; i < disklist->entries; i++) {
779 fprintf(fout, " device %s\n",
780 disklist->el[i].device);
781 fprintf(fout, " %-21s %d\n", listname, disklist->el[i].index);
782 }
783}
784
785
786
787
788
789#ifdef __FreeBSD__
790/**
791 * Add a new plex to a volume. The index of the plex will be <tt>v-\>plexes - 1</tt>.
792 * @param v The volume to operate on.
793 * @param raidlevel The RAID level of the new plex.
794 * @param stripesize The stripe size (chunk size) of the new plex.
795 */
796void add_plex_to_volume(struct vinum_volume *v, int raidlevel,
797 int stripesize)
798{
799 v->plex[v->plexes].raidlevel = raidlevel;
800 v->plex[v->plexes].stripesize = stripesize;
801 v->plex[v->plexes].subdisks = 0;
802 ++v->plexes;
803}
804
805/**
806 * For internal use only.
807 */
808char **get_next_vinum_conf_line(FILE * f, int *argc)
809{
810 int cnt = 0;
811 static char *argv[64];
812 char **ap;
813 char *q;
814 char *line = (char *) malloc(MAX_STR_LEN);
815
816 if (!line)
817 errx(1,
818 "unable to allocate %i bytes of memory for `char *line' at %s:%i",
819 MAX_STR_LEN, __FILE__, __LINE__);
820 q = fgets(line, MAX_STR_LEN, f);
821 if (feof(f) && (q != NULL)) {
822 log_it("[GNVCL] Uh... I reached the EOF.");
823 return 0;
824 }
825
826 for (ap = argv; (*ap = strsep(&line, " \t")) != NULL;)
827 if (**ap != '\0') {
828 if (++ap >= &argv[64])
829 break;
830 cnt++;
831 }
832
833 if (strchr(argv[cnt - 1], '\n')) {
834 *(strchr(argv[cnt - 1], '\n')) = '\0';
835 }
836
837 if (argc)
838 *argc = cnt;
839 return argv;
840}
841
842/**
843 * For internal use only.
844 */
845char *get_option_val(int argc, char **argv, char *option)
846{
847 int i;
848 for (i = 0; i < (argc - 1); ++i) {
849 if (!strcmp(argv[i], option)) {
850 return argv[i + 1];
851 }
852 }
853 return 0;
854}
855
856/**
857 * For internal use only.
858 */
859char **get_option_vals(int argc, char **argv, char *option, int nval)
860{
861 int i, j;
862 static char **ret;
863 ret = (char **) malloc(nval * sizeof(char *));
864 for (i = 0; i < (argc - nval); ++i) {
865 if (!strcmp(argv[i], option)) {
866 for (j = 0; j < nval; ++j) {
867 ret[j] = (char *) malloc(strlen(argv[i + j + 1]) + 1);
868 strcpy(ret[j], argv[i + j + 1]);
869 }
870 return ret;
871 }
872 }
873 return 0;
874}
875
876/**
877 * For internal use only.
878 */
879bool get_option_state(int argc, char **argv, char *option)
880{
881 int i;
882 for (i = 0; i < argc; ++i)
883 if (!strcmp(argv[i], option))
884 return TRUE;
885
886 return FALSE;
887}
888
889/**
890 * Taken from Vinum source -- for internal use only.
891 */
892long long size_spec(char *spec)
893{
894 u_int64_t size;
895 char *s;
896 int sign = 1; /* -1 if negative */
897
898 size = 0;
899 if (spec != NULL) { /* we have a parameter */
900 s = spec;
901 if (*s == '-') { /* negative, */
902 sign = -1;
903 s++; /* skip */
904 }
905 if ((*s >= '0') && (*s <= '9')) { /* it's numeric */
906 while ((*s >= '0') && (*s <= '9')) /* it's numeric */
907 size = size * 10 + *s++ - '0'; /* convert it */
908 switch (*s) {
909 case '\0':
910 return size * sign;
911
912 case 'B':
913 case 'b':
914 case 'S':
915 case 's':
916 return size * sign * 512;
917
918 case 'K':
919 case 'k':
920 return size * sign * 1024;
921
922 case 'M':
923 case 'm':
924 return size * sign * 1024 * 1024;
925
926 case 'G':
927 case 'g':
928 return size * sign * 1024 * 1024 * 1024;
929
930 case 'T':
931 case 't':
932 log_it
933 ("Ok, I'm scared... Someone did a TERABYTE+ size-spec");
934 return size * sign * 1024 * 1024 * 1024 * 1024;
935
936 case 'P':
937 case 'p':
938 log_it
939 ("If I was scared last time, I'm freaked out now. Someone actually has a PETABYTE?!?!?!?!");
940 return size * sign * 1024 * 1024 * 1024 * 1024 * 1024;
941
942 case 'E':
943 case 'e':
944 log_it
945 ("Okay, I'm REALLY freaked out. Who could devote a whole EXABYTE to their data?!?!");
946 return size * sign * 1024 * 1024 * 1024 * 1024 * 1024 *
947 1024;
948
949 case 'Z':
950 case 'z':
951 log_it
952 ("WHAT!?!? A ZETABYTE!?!? You've GOT to be kidding me!!!");
953 return size * sign * 1024 * 1024 * 1024 * 1024 * 1024 *
954 1024 * 1024;
955
956 case 'Y':
957 case 'y':
958 log_it
959 ("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?!?!");
960 popup_and_OK
961 ("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 :-)");
962 return size * sign * 1024 * 1024 * 1024 * 1024 * 1024 *
963 1024 * 1024 * 1024;
964 }
965 }
966 }
967 return size * sign;
968}
969
970#endif
971
972
973
974
975int parse_mdstat(char *mdstat_fname, struct raidlist_itself *raidlist, char *device_prefix) {
976
977const char delims[] = " ";
978
979FILE *fin = NULL;
980int res = 0, row, i, index_min;
981int v = 0;
982int lastpos = 0;
983size_t len = 0;
984char *token = NULL;
985char *string = NULL;
986char *cmd = NULL;
987char *pos = NULL;
988char type;
989char *strtmp = NULL;
990char *strtmp2 = NULL;
991
992// open file
993if (!(fin = fopen(mdstat_fname, "r"))) {
994 log_msg(1, "Could not open %s.\n", mdstat_fname);
995 return 1;
996}
997// initialise record, build progress and row counters
998raidlist->entries = 0;
999raidlist->el[raidlist->entries].progress = 999;
1000row = 1;
1001// skip first output row - contains registered RAID levels
1002res = getline(&strtmp2, &len, fin);
1003mr_free(strtmp2);
1004// parse the rest
1005while ( !feof_unlocked(fin) ) {
1006 res = getline(&string, &len, fin);
1007 log_msg(8, "mdstat line '%s' read.\n", string);
1008 if (res <= 0) break;
1009 // trim spaces
1010 strip_spaces(string);
1011 log_msg(8, "mdstat line 2 '%s' read.\n", string);
1012 // if we have newline after only spaces, this is a blank line, update
1013 // counters, otherwise do normal parsing
1014 if (!strcmp(string,"")) {
1015 row = 1;
1016 raidlist->entries++;
1017 raidlist->el[raidlist->entries].progress = 999;
1018 } else {
1019 switch (row) {
1020 case 1: // device information
1021 // check whether last line of record and if so skip
1022 log_msg(8, "This is the device line\n");
1023 pos = strcasestr(string, "unused devices: ");
1024 if (pos != NULL) {
1025 break;
1026 }
1027 // tokenise string
1028 token = mr_strtok(string, delims, &lastpos);
1029 if (token == NULL) {
1030 // should not happen !
1031 break;
1032 }
1033 // get RAID device name
1034 mr_asprintf(&strtmp,"%s%s", device_prefix, token);
1035 strncpy(raidlist->el[raidlist->entries].raid_device, strtmp, 63);
1036 raidlist->el[raidlist->entries].raid_device[63] = '\0';
1037 mr_free(strtmp);
1038 mr_free(token);
1039 // store the UUID value in the additional_vars structure
1040 v = raidlist->el[raidlist->entries].additional_vars.entries;
1041 strcpy(raidlist->el[raidlist->entries].additional_vars.el[v].label, "UUID");
1042 mr_asprintf(&cmd,"mdadm --detail %s | grep UUID | cut -d: -f2- | sed 's/^ *//'", raidlist->el[raidlist->entries].raid_device);
1043 mr_asprintf(&strtmp, "%s", call_program_and_get_last_line_of_output(cmd));
1044 strcpy(raidlist->el[raidlist->entries].additional_vars.el[v].value, strtmp);
1045 mr_free(strtmp);
1046 v++;
1047 // store the Version value in the additional_vars structure
1048 strcpy(raidlist->el[raidlist->entries].additional_vars.el[v].label, "Version");
1049 mr_asprintf(&cmd,"mdadm --detail %s | grep Version | cut -d: -f2- | sed 's/^ *//'", raidlist->el[raidlist->entries].raid_device);
1050 mr_asprintf(&strtmp, "%s", call_program_and_get_last_line_of_output(cmd));
1051 strcpy(raidlist->el[raidlist->entries].additional_vars.el[v].value, strtmp);
1052 mr_free(strtmp);
1053 v++;
1054 raidlist->el[raidlist->entries].additional_vars.entries = v;
1055 // skip ':' and status
1056 token = mr_strtok(string, delims, &lastpos);
1057 if (token == NULL) {
1058 // should not happen !
1059 break;
1060 }
1061 mr_free(token);
1062 token = mr_strtok(string, delims, &lastpos);
1063 if (token == NULL) {
1064 // should not happen !
1065 break;
1066 }
1067 if (!strcmp(token, "inactive")) {
1068 log_msg(1, "RAID device '%s' inactive.\n", raidlist->el[raidlist->entries].raid_device);
1069 mr_free(string);
1070 mr_free(token);
1071 return 1;
1072 }
1073 mr_free(token);
1074
1075 // get RAID level
1076 token = mr_strtok(string, delims, &lastpos);
1077 if (token == NULL) {
1078 // should not happen !
1079 break;
1080 }
1081 // skip potential auto-read-only entry
1082 if (!strcmp(token, "(auto-read-only)")) {
1083 mr_free(token);
1084 token = mr_strtok (string, delims, &lastpos);
1085 if (token == NULL) {
1086 // should not happen !
1087 break;
1088 }
1089 }
1090 if (!strcmp(token, "multipath")) {
1091 raidlist->el[raidlist->entries].raid_level = -2;
1092 } else if (!strcmp(token, "linear")) {
1093 raidlist->el[raidlist->entries].raid_level = -1;
1094 } else if (!strcmp(token, "raid0")) {
1095 raidlist->el[raidlist->entries].raid_level = 0;
1096 } else if (!strcmp(token, "raid1")) {
1097 raidlist->el[raidlist->entries].raid_level = 1;
1098 } else if (!strcmp(token, "raid4")) {
1099 raidlist->el[raidlist->entries].raid_level = 4;
1100 } else if (!strcmp(token, "raid5")) {
1101 raidlist->el[raidlist->entries].raid_level = 5;
1102 } else if (!strcmp(token, "raid6")) {
1103 raidlist->el[raidlist->entries].raid_level = 6;
1104 } else if (!strcmp(token, "raid10")) {
1105 raidlist->el[raidlist->entries].raid_level = 10;
1106 } else {
1107 log_msg(1, "Unknown RAID level '%s'.\n", token);
1108 mr_free(string);
1109 mr_free(token);
1110 return 1;
1111 }
1112 mr_free(token);
1113
1114 // get RAID devices (type, index, device)
1115 // Note: parity disk for RAID4 is last normal disk, there is no '(P)'
1116 raidlist->el[raidlist->entries].data_disks.entries = 0;
1117 raidlist->el[raidlist->entries].spare_disks.entries = 0;
1118 raidlist->el[raidlist->entries].failed_disks.entries = 0;
1119 while((token = mr_strtok (string, delims, &lastpos))) {
1120 if ((pos = strstr(token, "("))) {
1121 type = *(pos+1);
1122 } else {
1123 type = ' ';
1124 }
1125 pos = strstr(token, "[");
1126 *pos = '\0';
1127 switch(type) {
1128 case ' ': // normal data disks
1129 raidlist->el[raidlist->entries].data_disks.el[raidlist->el[raidlist->entries].data_disks.entries].index = atoi(pos + 1);
1130 mr_asprintf(&strtmp,"%s%s", device_prefix, token);
1131 strcpy(raidlist->el[raidlist->entries].data_disks.el[raidlist->el[raidlist->entries].data_disks.entries].device, strtmp);
1132 mr_free(strtmp);
1133 raidlist->el[raidlist->entries].data_disks.entries++;
1134 break;
1135 case 'S': // spare disks
1136 raidlist->el[raidlist->entries].spare_disks.el[raidlist->el[raidlist->entries].spare_disks.entries].index = atoi(pos + 1);
1137 mr_asprintf(&strtmp,"%s%s", device_prefix, token);
1138 strcpy(raidlist->el[raidlist->entries].spare_disks.el[raidlist->el[raidlist->entries].spare_disks.entries].device, strtmp);
1139 mr_free(strtmp);
1140 raidlist->el[raidlist->entries].spare_disks.entries++;
1141 break;
1142 case 'F': // failed disks
1143 raidlist->el[raidlist->entries].failed_disks.el[raidlist->el[raidlist->entries].failed_disks.entries].index = atoi(pos + 1);
1144 mr_asprintf(&strtmp,"%s%s", device_prefix, token);
1145 strcpy(raidlist->el[raidlist->entries].failed_disks.el[raidlist->el[raidlist->entries].failed_disks.entries].device, strtmp);
1146 mr_free(strtmp);
1147 raidlist->el[raidlist->entries].failed_disks.entries++;
1148 log_it("At least one failed disk found in RAID array.\n");
1149 break;
1150 default: // error
1151 log_msg(1, "Unknown device type '%c'\n", type);
1152 mr_free(string);
1153 mr_free(token);
1154 return 1;
1155 break;
1156 }
1157 mr_free(token);
1158 }
1159
1160 // adjust index for each device so that it starts with 0 for every type
1161 index_min = 99;
1162 for (i=0; i<raidlist->el[raidlist->entries].data_disks.entries;i++) {
1163 if (raidlist->el[raidlist->entries].data_disks.el[i].index < index_min) {
1164 index_min = raidlist->el[raidlist->entries].data_disks.el[i].index;
1165 }
1166 }
1167 if (index_min > 0) {
1168 for (i=0; i<raidlist->el[raidlist->entries].data_disks.entries;i++) {
1169 raidlist->el[raidlist->entries].data_disks.el[i].index = raidlist->el[raidlist->entries].data_disks.el[i].index - index_min;
1170 }
1171 }
1172 index_min = 99;
1173 for (i=0; i<raidlist->el[raidlist->entries].spare_disks.entries;i++) {
1174 if (raidlist->el[raidlist->entries].spare_disks.el[i].index < index_min) {
1175 index_min = raidlist->el[raidlist->entries].spare_disks.el[i].index;
1176 }
1177 }
1178 if (index_min > 0) {
1179 for (i=0; i<raidlist->el[raidlist->entries].spare_disks.entries;i++) {
1180 raidlist->el[raidlist->entries].spare_disks.el[i].index = raidlist->el[raidlist->entries].spare_disks.el[i].index - index_min;
1181 }
1182 }
1183 index_min = 99;
1184 for (i=0; i<raidlist->el[raidlist->entries].failed_disks.entries;i++) {
1185 if (raidlist->el[raidlist->entries].failed_disks.el[i].index < index_min) {
1186 index_min = raidlist->el[raidlist->entries].failed_disks.el[i].index;
1187 }
1188 }
1189 if (index_min > 0) {
1190 for (i=0; i<raidlist->el[raidlist->entries].failed_disks.entries;i++) {
1191 raidlist->el[raidlist->entries].failed_disks.el[i].index = raidlist->el[raidlist->entries].failed_disks.el[i].index - index_min;
1192 }
1193 }
1194 break;
1195 case 2: // config information
1196 // check for persistent super block
1197 if (strcasestr(string, "super non-persistent")) {
1198 raidlist->el[raidlist->entries].persistent_superblock = 0;
1199 } else {
1200 raidlist->el[raidlist->entries].persistent_superblock = 1;
1201 }
1202 // extract chunk size
1203 if (!(pos = strcasestr(string, "k chunk"))) {
1204 raidlist->el[raidlist->entries].chunk_size = -1;
1205 } else {
1206 while (*pos != ' ') {
1207 pos -= 1;
1208 if (pos < string) {
1209 log_it("String underflow!\n");
1210 mr_free(string);
1211 return 1;
1212 }
1213 }
1214 raidlist->el[raidlist->entries].chunk_size = atoi(pos + 1);
1215 }
1216 // extract parity if present
1217 if ((pos = strcasestr(string, "algorithm"))) {
1218 raidlist->el[raidlist->entries].parity = atoi(pos + 9);
1219 } else {
1220 raidlist->el[raidlist->entries].parity = -1;
1221 }
1222 break;
1223 case 3: // optional build status information
1224 if (!(pos = strchr(string, '\%'))) {
1225 if (strcasestr(string, "delayed")) {
1226 raidlist->el[raidlist->entries].progress = -1; // delayed (therefore, stuck at 0%)
1227 } else {
1228 raidlist->el[raidlist->entries].progress = 999; // not found
1229 }
1230 } else {
1231 while (*pos != ' ') {
1232 pos -= 1;
1233 if (pos < string) {
1234 printf("ERROR: String underflow!\n");
1235 mr_free(string);
1236 return 1;
1237 }
1238 }
1239 raidlist->el[raidlist->entries].progress = atoi(pos);
1240 }
1241 break;
1242 default: // error or IN PROGRESS
1243 if (raidlist->el[raidlist->entries].progress != -1 &&
1244 raidlist->el[raidlist->entries].progress != 999) {
1245 log_msg(1, "Row %d should not occur in record!\n", row);
1246 }
1247 break;
1248 }
1249 row++;
1250 }
1251 // free string
1252 mr_free(string);
1253}
1254// close file
1255fclose(fin);
1256// return success
1257return 0;
1258
1259}
1260
1261
1262
1263
1264int create_raidtab_from_mdstat(char *mdstat_fname,char *raidtab_fname)
1265{
1266 struct raidlist_itself *raidlist;
1267 int retval = 0;
1268
1269 raidlist = malloc(sizeof(struct raidlist_itself));
1270
1271 // FIXME: Prefix '/dev/' should really be dynamic!
1272 if (parse_mdstat(mdstat_fname,raidlist, "/dev/")) {
1273 log_to_screen("Sorry, cannot read %s", mdstat_fname);
1274 return (1);
1275 }
1276
1277 retval += save_raidlist_to_raidtab(raidlist, raidtab_fname);
1278 return (retval);
1279}
1280
1281
1282
1283/* @} - end of raidGroup */
Note: See TracBrowser for help on using the repository browser.