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

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

r5035@localhost: bruno | 2012-11-09 03:17:01 +0100

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