source: branches/2.2.2/mondo/src/common/libmondo-raid.c @ 1253

Last change on this file since 1253 was 1253, checked in by bruno, 12 years ago

Fix a bug on is_this_raid_personality_registered which was just plain wrong :-(
(report from John Pearson <johnp_at_gtagalenco.com.au>)

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