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

Last change on this file since 2989 was 2989, checked in by bruno, 7 years ago

r4626@localhost: bruno | 2012-03-30 16:24:25 +0200

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