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

Last change on this file since 3154 was 3154, checked in by bruno, 6 years ago
  • Remove the introduction of LogProgress? in ListKernelModulePaths? which should stay quiet
  • Remove a remaining support of max-noof-media in confidure.in now gone.
  • Fix #640 by cleaning up fully the managemnt of tmpdir and scratchdir (one function for the creation, one point of removal at end, intermediate removal if needed, clean naming conventions, unify mkdtemp usage, remove sensibly_set_tmpdir_and_scratchdir function). Consequence is that default dir for scratchdir if nothing else specified is /tmp using CLI and the largest partition using the GUI.
  • Fix a bug introduced in process_the_s_switch with intermediate variables which weren't initialized correctly.
  • 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 3154 2013-06-22 22:34:33Z 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 3154 2013-06-22 22:34:33Z 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", raidlist->el[raidlist->entries].raid_device);
1069                mr_free(string);
1070                mr_free(token);
1071                return 1;
1072            }
1073            mr_free(token);
1074
1075            // get RAID level
1076            token = mr_strtok(string, delims, &lastpos);
1077            if (token == NULL) {
1078                // should not happen !
1079                break;
1080            }
1081            // skip potential auto-read-only entry
1082            if (!strcmp(token, "(auto-read-only)")) {
1083                mr_free(token);
1084                token = mr_strtok (string, delims, &lastpos);
1085                if (token == NULL) {
1086                    // should not happen !
1087                    break;
1088                }
1089            }
1090            if (!strcmp(token, "multipath")) {
1091                raidlist->el[raidlist->entries].raid_level = -2;
1092            } else if (!strcmp(token, "linear")) {
1093                raidlist->el[raidlist->entries].raid_level = -1;
1094            } else if (!strcmp(token, "raid0")) {
1095                raidlist->el[raidlist->entries].raid_level = 0;
1096            } else if (!strcmp(token, "raid1")) {
1097                raidlist->el[raidlist->entries].raid_level = 1;
1098            } else if (!strcmp(token, "raid4")) {
1099                raidlist->el[raidlist->entries].raid_level = 4;
1100            } else if (!strcmp(token, "raid5")) {
1101                raidlist->el[raidlist->entries].raid_level = 5;
1102            } else if (!strcmp(token, "raid6")) {
1103                raidlist->el[raidlist->entries].raid_level = 6;
1104            } else if (!strcmp(token, "raid10")) {
1105                raidlist->el[raidlist->entries].raid_level = 10;
1106            } else {
1107                log_msg(1, "Unknown RAID level '%s'.\n", token);
1108                mr_free(string);
1109                mr_free(token);
1110                return 1;
1111            }
1112            mr_free(token);
1113
1114            // get RAID devices (type, index, device)
1115            // Note: parity disk for RAID4 is last normal disk, there is no '(P)'
1116            raidlist->el[raidlist->entries].data_disks.entries = 0;
1117            raidlist->el[raidlist->entries].spare_disks.entries = 0;
1118            raidlist->el[raidlist->entries].failed_disks.entries = 0;
1119            while((token = mr_strtok (string, delims, &lastpos))) {
1120                if ((pos = strstr(token, "("))) {
1121                    type = *(pos+1);
1122                } else {
1123                    type = ' ';
1124                }
1125                pos = strstr(token, "[");
1126                *pos = '\0';
1127                switch(type) {
1128                case ' ': // normal data disks
1129                    raidlist->el[raidlist->entries].data_disks.el[raidlist->el[raidlist->entries].data_disks.entries].index = atoi(pos + 1);
1130                    mr_asprintf(&strtmp,"%s%s", device_prefix, token);
1131                    strcpy(raidlist->el[raidlist->entries].data_disks.el[raidlist->el[raidlist->entries].data_disks.entries].device, strtmp);
1132                    mr_free(strtmp);
1133                    raidlist->el[raidlist->entries].data_disks.entries++;
1134                    break;
1135                case 'S': // spare disks
1136                    raidlist->el[raidlist->entries].spare_disks.el[raidlist->el[raidlist->entries].spare_disks.entries].index = atoi(pos + 1);
1137                    mr_asprintf(&strtmp,"%s%s", device_prefix, token);
1138                    strcpy(raidlist->el[raidlist->entries].spare_disks.el[raidlist->el[raidlist->entries].spare_disks.entries].device, strtmp);
1139                    mr_free(strtmp);
1140                    raidlist->el[raidlist->entries].spare_disks.entries++;
1141                    break;
1142                case 'F': // failed disks
1143                    raidlist->el[raidlist->entries].failed_disks.el[raidlist->el[raidlist->entries].failed_disks.entries].index = atoi(pos + 1);
1144                    mr_asprintf(&strtmp,"%s%s", device_prefix, token);
1145                    strcpy(raidlist->el[raidlist->entries].failed_disks.el[raidlist->el[raidlist->entries].failed_disks.entries].device, strtmp);
1146                    mr_free(strtmp);
1147                    raidlist->el[raidlist->entries].failed_disks.entries++;
1148                    log_it("At least one failed disk found in RAID array.\n");
1149                    break;
1150                default: // error
1151                    log_msg(1, "Unknown device type '%c'\n", type);
1152                    mr_free(string);
1153                    mr_free(token);
1154                    return 1;
1155                    break;
1156                }
1157                mr_free(token);
1158            }
1159
1160            // adjust index for each device so that it starts with 0 for every type
1161            index_min = 99;
1162            for (i=0; i<raidlist->el[raidlist->entries].data_disks.entries;i++) {
1163                if (raidlist->el[raidlist->entries].data_disks.el[i].index < index_min) {
1164                    index_min = raidlist->el[raidlist->entries].data_disks.el[i].index;
1165                }
1166            }
1167            if (index_min > 0) {
1168                for (i=0; i<raidlist->el[raidlist->entries].data_disks.entries;i++) {
1169                    raidlist->el[raidlist->entries].data_disks.el[i].index = raidlist->el[raidlist->entries].data_disks.el[i].index - index_min;   
1170                }
1171            }
1172            index_min = 99;
1173            for (i=0; i<raidlist->el[raidlist->entries].spare_disks.entries;i++) {
1174                if (raidlist->el[raidlist->entries].spare_disks.el[i].index < index_min) {
1175                    index_min = raidlist->el[raidlist->entries].spare_disks.el[i].index;
1176                }
1177            }
1178            if (index_min > 0) {
1179                for (i=0; i<raidlist->el[raidlist->entries].spare_disks.entries;i++) {
1180                    raidlist->el[raidlist->entries].spare_disks.el[i].index = raidlist->el[raidlist->entries].spare_disks.el[i].index - index_min; 
1181                }
1182            }
1183            index_min = 99;
1184            for (i=0; i<raidlist->el[raidlist->entries].failed_disks.entries;i++) {
1185                if (raidlist->el[raidlist->entries].failed_disks.el[i].index < index_min) {
1186                    index_min = raidlist->el[raidlist->entries].failed_disks.el[i].index;
1187                }
1188            }
1189            if (index_min > 0) {
1190                for (i=0; i<raidlist->el[raidlist->entries].failed_disks.entries;i++) {
1191                    raidlist->el[raidlist->entries].failed_disks.el[i].index = raidlist->el[raidlist->entries].failed_disks.el[i].index - index_min;   
1192                }
1193            }
1194            break;
1195        case 2:  // config information
1196            // check for persistent super block
1197            if (strcasestr(string, "super non-persistent")) {
1198                raidlist->el[raidlist->entries].persistent_superblock = 0;
1199            } else {
1200                raidlist->el[raidlist->entries].persistent_superblock = 1;
1201            }
1202            // extract chunk size
1203            if (!(pos = strcasestr(string, "k chunk"))) {
1204                raidlist->el[raidlist->entries].chunk_size = -1;
1205            } else {
1206                while (*pos != ' ') {
1207                    pos -= 1;
1208                    if (pos < string) {
1209                            log_it("String underflow!\n");
1210                            mr_free(string);
1211                            return 1;
1212                    }
1213                }
1214                raidlist->el[raidlist->entries].chunk_size = atoi(pos + 1);
1215            }
1216            // extract parity if present
1217            if ((pos = strcasestr(string, "algorithm"))) {
1218                raidlist->el[raidlist->entries].parity = atoi(pos + 9);
1219            } else {
1220                raidlist->el[raidlist->entries].parity = -1;
1221            }
1222            break;
1223        case 3:  // optional build status information
1224            if (!(pos = strchr(string, '\%'))) {
1225                if (strcasestr(string, "delayed")) {
1226                    raidlist->el[raidlist->entries].progress = -1;  // delayed (therefore, stuck at 0%)
1227                } else {
1228                    raidlist->el[raidlist->entries].progress = 999; // not found
1229                }
1230            } else {
1231                while (*pos != ' ') {
1232                    pos -= 1;
1233                    if (pos < string) {
1234                        printf("ERROR: String underflow!\n");
1235                            mr_free(string);
1236                            return 1;
1237                    }
1238                }
1239                raidlist->el[raidlist->entries].progress = atoi(pos);
1240            }
1241            break;
1242        default: // error or IN PROGRESS
1243            if (raidlist->el[raidlist->entries].progress != -1 &&
1244                raidlist->el[raidlist->entries].progress != 999) {
1245                log_msg(1, "Row %d should not occur in record!\n", row);
1246            }
1247            break;
1248        }
1249        row++;
1250    }
1251    // free string
1252    mr_free(string);
1253}
1254// close file
1255fclose(fin);
1256// return success
1257return 0;
1258
1259}
1260
1261
1262
1263
1264int create_raidtab_from_mdstat(char *mdstat_fname,char *raidtab_fname)
1265{
1266    struct raidlist_itself *raidlist;
1267    int retval = 0;
1268
1269    raidlist = malloc(sizeof(struct raidlist_itself));
1270
1271    // FIXME: Prefix '/dev/' should really be dynamic!
1272    if (parse_mdstat(mdstat_fname,raidlist, "/dev/")) {
1273        log_to_screen("Sorry, cannot read %s", mdstat_fname);
1274        return (1);
1275    }
1276
1277    retval += save_raidlist_to_raidtab(raidlist, raidtab_fname);
1278    return (retval);
1279}
1280
1281
1282
1283/* @} - end of raidGroup */
Note: See TracBrowser for help on using the repository browser.