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

Last change on this file since 900 was 900, checked in by Bruno Cornec, 14 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.