source: MondoRescue/trunk/mondo/src/common/libmondo-raid.c@ 900

Last change on this file since 900 was 900, checked in by Bruno Cornec, 18 years ago

Huge patch to introduce low level functions that will bw used everywhere (mr_free, mr_asprintf, ...)
Nearly linking now due to that.

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