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

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

merge -r1045:1078 £SVN_M/branches/stable

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