]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - config.c
Detail: export MD_UUID from mapfile
[thirdparty/mdadm.git] / config.c
index 9eb8197a9382a1b805f50adb3e8f2d6bc55266ec..c962afdaaed829d0122550166ce0f072d9ccea97 100644 (file)
--- a/config.c
+++ b/config.c
@@ -1,7 +1,7 @@
 /*
  * mdadm - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2001-2006 Neil Brown <neilb@suse.de>
+ * Copyright (C) 2001-2009 Neil Brown <neilb@suse.de>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  *    Author: Neil Brown
- *    Email: <neilb@cse.unsw.edu.au>
- *    Paper: Neil Brown
- *           School of Computer Science and Engineering
- *           The University of New South Wales
- *           Sydney, 2052
- *           Australia
+ *    Email: <neilb@suse.de>
  */
 
 #include       "mdadm.h"
@@ -56,7 +51,7 @@
  *  with a key word, and not be indented, or must start with a
  *  non-key-word and must be indented.
  *
- * Keywords are DEVICE and ARRAY
+ * Keywords are DEVICE and ARRAY ... and several others.
  * DEV{ICE} introduces some devices that might contain raid components.
  * e.g.
  *   DEV style=0 /dev/sda* /dev/hd*
@@ -79,7 +74,8 @@
 char DefaultConfFile[] = CONFFILE;
 char DefaultAltConfFile[] = CONFFILE2;
 
-enum linetype { Devices, Array, Mailaddr, Mailfrom, Program, CreateDev, Homehost, LTEnd };
+enum linetype { Devices, Array, Mailaddr, Mailfrom, Program, CreateDev,
+               Homehost, AutoMode, LTEnd };
 char *keywords[] = {
        [Devices]  = "devices",
        [Array]    = "array",
@@ -88,6 +84,7 @@ char *keywords[] = {
        [Program]  = "program",
        [CreateDev]= "create",
        [Homehost] = "homehost",
+       [AutoMode] = "auto",
        [LTEnd]    = NULL
 };
 
@@ -261,12 +258,44 @@ mddev_dev_t load_partitions(void)
                d->devname = strdup(name);
                d->next = rv;
                d->used = 0;
+               d->content = NULL;
                rv = d;
        }
        fclose(f);
        return rv;
 }
 
+mddev_dev_t load_containers(void)
+{
+       struct mdstat_ent *mdstat = mdstat_read(1, 0);
+       struct mdstat_ent *ent;
+       mddev_dev_t d;
+       mddev_dev_t rv = NULL;
+
+       if (!mdstat)
+               return NULL;
+
+       for (ent = mdstat; ent; ent = ent->next)
+               if (ent->metadata_version &&
+                   strncmp(ent->metadata_version, "external:", 9) == 0 &&
+                   !is_subarray(&ent->metadata_version[9])) {
+                       d = malloc(sizeof(*d));
+                       if (!d)
+                               continue;
+                       if (asprintf(&d->devname, "/dev/%s", ent->dev) < 0) {
+                               free(d);
+                               continue;
+                       }
+                       d->next = rv;
+                       d->used = 0;
+                       d->content = NULL;
+                       rv = d;
+               }
+       free_mdstat(mdstat);
+
+       return rv;
+}
+
 struct createinfo createinfo = {
        .autof = 2, /* by default, create devices with standard names */
        .symlinks = 1,
@@ -398,7 +427,8 @@ void devline(char *line)
        struct conf_dev *cd;
 
        for (w=dl_next(line); w != line; w=dl_next(w)) {
-               if (w[0] == '/' || strcasecmp(w, "partitions") == 0) {
+               if (w[0] == '/' || strcasecmp(w, "partitions") == 0 ||
+                   strcasecmp(w, "containers") == 0) {
                        cd = malloc(sizeof(*cd));
                        cd->name = strdup(w);
                        cd->next = cdevlist;
@@ -413,6 +443,17 @@ void devline(char *line)
 mddev_ident_t mddevlist = NULL;
 mddev_ident_t *mddevlp = &mddevlist;
 
+static int is_number(char *w)
+{
+       /* check if there are 1 or more digits and nothing else */
+       int digits = 0;
+       while (*w && isdigit(*w)) {
+               digits++;
+               w++;
+       }
+       return (digits && ! *w);
+}
+
 void arrayline(char *line)
 {
        char *w;
@@ -438,11 +479,35 @@ void arrayline(char *line)
        mis.member = NULL;
 
        for (w=dl_next(line); w!=line; w=dl_next(w)) {
-               if (w[0] == '/') {
-                       if (mis.devname)
-                               fprintf(stderr, Name ": only give one device per ARRAY line: %s and %s\n",
-                                       mis.devname, w);
-                       else mis.devname = w;
+               if (w[0] == '/' || strchr(w, '=') == NULL) {
+                       /* This names the device, or is '<ignore>'.
+                        * The rules match those in create_mddev.
+                        * 'w' must be:
+                        *  /dev/md/{anything}
+                        *  /dev/mdNN
+                        *  /dev/md_dNN
+                        *  <ignore>
+                        *  or anything that doesn't start '/' or '<'
+                        */
+                       if (strcasecmp(w, "<ignore>") == 0 ||
+                           strncmp(w, "/dev/md/", 8) == 0 ||
+                           (w[0] != '/' && w[0] != '<') ||
+                           (strncmp(w, "/dev/md", 7) == 0 && 
+                            is_number(w+7)) ||
+                           (strncmp(w, "/dev/md_d", 9) == 0 &&
+                            is_number(w+9))
+                               ) {
+                               /* This is acceptable */;
+                               if (mis.devname)
+                                       fprintf(stderr, Name ": only give one "
+                                               "device per ARRAY line: %s and %s\n",
+                                               mis.devname, w);
+                               else
+                                       mis.devname = w;
+                       }else {
+                               fprintf(stderr, Name ": %s is an invalid name for "
+                                       "an md device - ignored.\n", w);
+                       }
                } else if (strncasecmp(w, "uuid=", 5)==0 ) {
                        if (mis.uuid_set)
                                fprintf(stderr, Name ": only specify uuid once, %s ignored.\n",
@@ -530,7 +595,9 @@ void arrayline(char *line)
                                w);
                }
        }
-       if (mis.uuid_set == 0 && mis.devices == NULL && mis.super_minor == UnSet && mis.name[0] == 0)
+       if (mis.uuid_set == 0 && mis.devices == NULL &&
+           mis.super_minor == UnSet && mis.name[0] == 0 &&
+           (mis.container == NULL || mis.member == NULL))
                fprintf(stderr, Name ": ARRAY line %s has no identity information.\n", mis.devname);
        else {
                mi = malloc(sizeof(*mi));
@@ -567,7 +634,7 @@ void mailfromline(char *line)
                else {
                        char *t = NULL;
 
-                       if (asprintf(&t, "%s %s", alert_mail_from, w) > 0) {
+                       if (xasprintf(&t, "%s %s", alert_mail_from, w) > 0) {
                                free(alert_mail_from);
                                alert_mail_from = t;
                        }
@@ -591,12 +658,15 @@ void programline(char *line)
 }
 
 static char *home_host = NULL;
+static int require_homehost = 1;
 void homehostline(char *line)
 {
        char *w;
 
        for (w=dl_next(line); w != line ; w=dl_next(w)) {
-               if (home_host == NULL)
+               if (strcasecmp(w, "<ignore>")==0)
+                       require_homehost = 0;
+               else if (home_host == NULL)
                        home_host = strdup(w);
                else
                        fprintf(stderr, Name ": excess host name on HOMEHOST line: %s - ignored\n",
@@ -604,6 +674,16 @@ void homehostline(char *line)
        }
 }
 
+static char *auto_options = NULL;
+void autoline(char *line)
+{
+       if (auto_options) {
+               fprintf(stderr, Name ": AUTO line may only be give once."
+                       "  Subsequent lines ignored\n");
+               return;
+       }
+       auto_options = line;            
+}
 
 int loaded = 0;
 
@@ -674,6 +754,9 @@ void load_conffile(void)
                case Homehost:
                        homehostline(line);
                        break;
+               case AutoMode:
+                       autoline(line);
+                       break;
                default:
                        fprintf(stderr, Name ": Unknown keyword %s\n", line);
                }
@@ -703,9 +786,11 @@ char *conf_get_program(void)
        return alert_program;
 }
 
-char *conf_get_homehost(void)
+char *conf_get_homehost(int *require_homehostp)
 {
        load_conffile();
+       if (require_homehostp)
+               *require_homehostp = require_homehost;
        return home_host;
 }
 
@@ -721,11 +806,18 @@ mddev_ident_t conf_get_ident(char *dev)
        load_conffile();
        rv = mddevlist;
        while (dev && rv && (rv->devname == NULL
-                            || strcmp(dev, rv->devname)!=0))
+                            || !devname_matches(dev, rv->devname)))
                rv = rv->next;
        return rv;
 }
 
+static void append_dlist(mddev_dev_t *dlp, mddev_dev_t list)
+{
+       while (*dlp)
+               dlp = &(*dlp)->next;
+       *dlp = list;
+}
+
 mddev_dev_t conf_get_devs()
 {
        glob_t globbuf;
@@ -743,13 +835,17 @@ mddev_dev_t conf_get_devs()
 
        load_conffile();
 
-       if (cdevlist == NULL)
-               /* default to 'partitions */
+       if (cdevlist == NULL) {
+               /* default to 'partitions' and 'containers' */
                dlist = load_partitions();
+               append_dlist(&dlist, load_containers());
+       }
 
        for (cd=cdevlist; cd; cd=cd->next) {
-               if (strcasecmp(cd->name, "partitions")==0 && dlist == NULL)
-                       dlist = load_partitions();
+               if (strcasecmp(cd->name, "partitions")==0)
+                       append_dlist(&dlist, load_partitions());
+               else if (strcasecmp(cd->name, "containers")==0)
+                       append_dlist(&dlist, load_containers());
                else {
                        glob(cd->name, flags, NULL, &globbuf);
                        flags |= GLOB_APPEND;
@@ -761,6 +857,7 @@ mddev_dev_t conf_get_devs()
                        t->devname = strdup(globbuf.gl_pathv[i]);
                        t->next = dlist;
                        t->used = 0;
+                       t->content = NULL;
                        dlist = t;
 /*     printf("one dev is %s\n", t->devname);*/
                }
@@ -785,6 +882,52 @@ int conf_test_dev(char *devname)
        return 0;
 }
 
+int conf_test_metadata(const char *version)
+{
+       /* Check if the given metadata version is allowed
+        * to be auto-assembled.
+        * The default is 'yes' but the 'auto' line might over-ride that.
+        * Word in auto_options are processed in order with the first
+        * match winning.
+        * word can be:
+        *   +version   - that version can be assembled
+        *   -version   - that version cannot be auto-assembled
+        *   yes or +all - any other version can be assembled
+        *   no or -all  - no other version can be assembled.
+        */
+       char *w;
+       load_conffile();
+       if (!auto_options)
+               return 1;
+       for (w = dl_next(auto_options); w != auto_options; w = dl_next(w)) {
+               int rv;
+               if (strcasecmp(w, "yes") == 0)
+                       return 1;
+               if (strcasecmp(w, "no") == 0)
+                       return 0;
+               if (w[0] == '+')
+                       rv = 1;
+               else if (w[0] == '-')
+                       rv = 0;
+               else continue;
+
+               if (strcasecmp(w+1, "all") == 0)
+                       return rv;
+               if (strcasecmp(w+1, version) == 0)
+                       return rv;
+               /* allow  '0' to match version '0.90'
+                * and 1 or 1.whatever to match version '1.x'
+                */
+               if (version[1] == '.' &&
+                   strlen(w+1) == 1 &&
+                   w[1] == version[0])
+                       return rv;
+               if (version[1] == '.' && version[2] == 'x' &&
+                   strncmp(w+1, version, 2) == 0)
+                       return rv;
+       }
+       return 1;
+}
 
 int match_oneof(char *devices, char *devname)
 {
@@ -810,3 +953,128 @@ int match_oneof(char *devices, char *devname)
     }
     return 0;
 }
+
+int devname_matches(char *name, char *match)
+{
+       /* See if the given array name matches the
+        * given match from config file.
+        *
+        * First strip and /dev/md/ or /dev/, then
+        * see if there might be a numeric match of
+        *  mdNN with NN
+        * then just strcmp
+        */
+       if (strncmp(name, "/dev/md/", 8) == 0)
+               name += 8;
+       else if (strncmp(name, "/dev/", 5) == 0)
+               name += 5;
+
+       if (strncmp(match, "/dev/md/", 8) == 0)
+               match += 8;
+       else if (strncmp(match, "/dev/", 5) == 0)
+               match += 5;
+
+
+       if (strncmp(name, "md", 2) == 0 &&
+           isdigit(name[2]))
+               name += 2;
+       if (strncmp(match, "md", 2) == 0 &&
+           isdigit(match[2]))
+               match += 2;
+
+       return (strcmp(name, match) == 0);
+}
+
+int conf_name_is_free(char *name)
+{
+       /* Check if this name is already take by an ARRAY entry in
+        * the config file.
+        * It can be taken either by a match on devname, name, or
+        * even super-minor.
+        */
+       mddev_ident_t dev;
+
+       load_conffile();
+       for (dev = mddevlist; dev; dev = dev->next) {
+               char nbuf[100];
+               if (dev->devname && devname_matches(name, dev->devname))
+                       return 0;
+               if (dev->name[0] && devname_matches(name, dev->name))
+                       return 0;
+               sprintf(nbuf, "%d", dev->super_minor);
+               if (dev->super_minor != UnSet &&
+                   devname_matches(name, nbuf))
+                       return 0;
+       }
+       return 1;
+}
+
+struct mddev_ident_s *conf_match(struct mdinfo *info, struct supertype *st)
+{
+       struct mddev_ident_s *array_list, *match;
+       int verbose = 0;
+       char *devname = NULL;
+       array_list = conf_get_ident(NULL);
+       match = NULL;
+       for (; array_list; array_list = array_list->next) {
+               if (array_list->uuid_set &&
+                   same_uuid(array_list->uuid, info->uuid, st->ss->swapuuid)
+                   == 0) {
+                       if (verbose >= 2 && array_list->devname)
+                               fprintf(stderr, Name
+                                       ": UUID differs from %s.\n",
+                                       array_list->devname);
+                       continue;
+               }
+               if (array_list->name[0] &&
+                   strcasecmp(array_list->name, info->name) != 0) {
+                       if (verbose >= 2 && array_list->devname)
+                               fprintf(stderr, Name
+                                       ": Name differs from %s.\n",
+                                       array_list->devname);
+                       continue;
+               }
+               if (array_list->devices && devname &&
+                   !match_oneof(array_list->devices, devname)) {
+                       if (verbose >= 2 && array_list->devname)
+                               fprintf(stderr, Name
+                                       ": Not a listed device for %s.\n",
+                                       array_list->devname);
+                       continue;
+               }
+               if (array_list->super_minor != UnSet &&
+                   array_list->super_minor != info->array.md_minor) {
+                       if (verbose >= 2 && array_list->devname)
+                               fprintf(stderr, Name
+                                       ": Different super-minor to %s.\n",
+                                       array_list->devname);
+                       continue;
+               }
+               if (!array_list->uuid_set &&
+                   !array_list->name[0] &&
+                   !array_list->devices &&
+                   array_list->super_minor == UnSet) {
+                       if (verbose >= 2 && array_list->devname)
+                               fprintf(stderr, Name
+                            ": %s doesn't have any identifying information.\n",
+                                       array_list->devname);
+                       continue;
+               }
+               /* FIXME, should I check raid_disks and level too?? */
+
+               if (match) {
+                       if (verbose >= 0) {
+                               if (match->devname && array_list->devname)
+                                       fprintf(stderr, Name
+                  ": we match both %s and %s - cannot decide which to use.\n",
+                                               match->devname, array_list->devname);
+                               else
+                                       fprintf(stderr, Name
+                                               ": multiple lines in mdadm.conf match\n");
+                       }
+                       return NULL;
+               }
+               match = array_list;
+       }
+       return match;
+}