summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDoug Ledford <dledford@redhat.com>2010-04-14 16:41:07 -0400
committerDoug Ledford <dledford@redhat.com>2010-04-14 16:41:07 -0400
commit53818c957806e7b539febfa8ff0295d54be0885f (patch)
tree8c4e099aa874577621010a18af77522713ab08a4
parent57f145e73ab65a76f5b3a560de00b11c72ccaa56 (diff)
downloadmdadm-53818c957806e7b539febfa8ff0295d54be0885f.tar.gz
mdadm-53818c957806e7b539febfa8ff0295d54be0885f.tar.xz
mdadm-53818c957806e7b539febfa8ff0295d54be0885f.zip
Initial stab at the config option parsing for new DOMAIN line support
Signed-off-by: Doug Ledford <dledford@redhat.com>
-rw-r--r--config.c247
-rw-r--r--mdadm.h17
-rw-r--r--util.c41
3 files changed, 304 insertions, 1 deletions
diff --git a/config.c b/config.c
index 20c46e9..2ccd2fd 100644
--- a/config.c
+++ b/config.c
@@ -75,7 +75,7 @@ char DefaultConfFile[] = CONFFILE;
char DefaultAltConfFile[] = CONFFILE2;
enum linetype { Devices, Array, Mailaddr, Mailfrom, Program, CreateDev,
- Homehost, AutoMode, LTEnd };
+ Homehost, AutoMode, Domain, LTEnd };
char *keywords[] = {
[Devices] = "devices",
[Array] = "array",
@@ -85,6 +85,7 @@ char *keywords[] = {
[CreateDev]= "create",
[Homehost] = "homehost",
[AutoMode] = "auto",
+ [Domain] = "domain",
[LTEnd] = NULL
};
@@ -694,6 +695,164 @@ void autoline(char *line)
}
}
+struct domain_ent {
+ char *spare_group; /* only set this in monitor mode
+ when we know what arrays we
+ are watching and can reconcile
+ them to domains by checking
+ constituent device paths */
+ struct domain_path {
+ char *path;
+ struct domain_path *next;
+ } *paths;
+ int action;
+ struct supertype *st;
+ struct domain_ent *next;
+};
+static struct domain_ent *domain_list = NULL;
+
+void free_domain(struct domain_ent *de)
+{
+ struct domain_path *dp;
+
+ while (de->paths) {
+ dp = de->paths;
+ de->paths = dp->next;
+ free(dp->path);
+ free(dp);
+ }
+ free(de);
+}
+
+void domainline(char *line)
+{
+ char *w;
+ struct domain_ent *de;
+ struct domain_path *path;
+ int offset, a_seen=0, m_seen=0, sg_seen=0;
+
+ de = malloc(sizeof(struct domain_ent));
+ if (!de) {
+ fprintf(stderr, Name ": unable to allocate memory for domain "
+ "entry\n");
+ return;
+ }
+ de->paths = NULL;
+ de->spare_group = NULL;
+ de->action = incremental;
+ de->st = NULL;
+ de->next = NULL;
+
+ for (w=dl_next(line); w!=line; w=dl_next(w)) {
+ if (strncasecmp("path=", w, 5) == 0) {
+ path = malloc(sizeof(struct domain_path));
+ if (!path) {
+ fprintf(stderr, Name ": unable to allocate "
+ "memory for domain path\n");
+ free_domain(de);
+ return;
+ }
+ path->path = strdup(w+5);
+ if (!path->path) {
+ fprintf(stderr, Name ": unable to allocate "
+ "memory for domain path\n");
+ free_domain(de);
+ return;
+ }
+ path->next = de->paths;
+ de->paths = path;
+ } else if (strncasecmp("action=", w, 7) == 0) {
+ if (!a_seen)
+ a_seen = 1;
+ else {
+ fprintf(stderr, Name ": only one action= entry "
+ "allowed per domain line, ignoring\n");
+ continue;
+ }
+ offset = 7;
+ if (strncasecmp("force-", w+offset, 6) == 0)
+ offset = 13;
+ if (strncasecmp("ign", w+offset, 3) == 0)
+ de->action = ignore;
+ else if (strncasecmp("inc", w+offset, 3) == 0)
+ de->action = incremental;
+ else if (strncasecmp("spa", w+offset, 3) == 0)
+ de->action = spare;
+ else if (strncasecmp("gro", w+offset, 3) == 0)
+ de->action = grow;
+ else if (strncasecmp("par", w+offset, 3) == 0)
+ de->action = partition;
+ if (offset == 13)
+ de->action |= force;
+ } else if (strncasecmp("metadata=", w, 9) == 0) {
+ int i;
+ if (!m_seen)
+ m_seen = 1;
+ else {
+ fprintf(stderr, Name ": only one metadata= "
+ "entry allowed per domain line, "
+ "ignoring\n");
+ continue;
+ }
+ /* style of metadata on the devices. */
+
+ for(i=0; superlist[i] && !de->st; i++)
+ de->st = superlist[i]->match_metadata_desc(w+9);
+
+ if (!de->st)
+ fprintf(stderr, Name ": metadata format %s "
+ "unknown, ignored.\n", w+9);
+ } else if (strncasecmp("spare-group=", w, 12) == 0) {
+ if (!sg_seen)
+ sg_seen = 1;
+ else {
+ fprintf(stderr, Name ": only one spare-group= "
+ "entry allowed per domain line, "
+ "ignoring\n");
+ continue;
+ }
+ de->spare_group = strdup(w+12);
+ if (!de->spare_group) {
+ fprintf(stderr, Name ": failed to allocate "
+ "memory for spare_group\n");
+ free_domain(de);
+ return;
+ }
+ } else {
+ fprintf(stderr, Name ": unrecognized option %s on "
+ "domain line\n", w);
+ }
+ }
+ /* Some sanity checks now that all the options are parsed */
+ if ((de->action & force) &&
+ ((de->action & (force - 1)) <= incremental)) {
+ fprintf(stderr, Name ": force makes no sense with ignore or "
+ "incremental, removing.\n");
+ de->action &= (force - 1);
+ }
+ if (de->spare_group && de->action <= incremental) {
+ fprintf(stderr, Name ": defined a spare group when we aren't "
+ "allowed to add any spares, removing spare group.\n");
+ free(de->spare_group);
+ de->spare_group = NULL;
+ }
+ de->next = domain_list;
+ domain_list = de;
+}
+
+struct domain_ent *get_domain_from_devpath(char *devpath)
+{
+ struct domain_ent *de;
+ struct domain_path *path;
+
+ for (de = domain_list; de; de = de->next)
+ for (path = de->paths; path; path = path->next)
+ if (fnmatch(path->path, devpath, 0) == 0)
+ return de;
+ return NULL;
+}
+
+
int loaded = 0;
static char *conffile = NULL;
@@ -766,6 +925,9 @@ void load_conffile(void)
case AutoMode:
autoline(line);
break;
+ case Domain:
+ domainline(line);
+ break;
default:
fprintf(stderr, Name ": Unknown keyword %s\n", line);
}
@@ -1101,3 +1263,86 @@ struct mddev_ident_s *conf_match(struct mdinfo *info, struct supertype *st)
}
return match;
}
+
+/*
+ * Return a linked list of arrays that are in the same domain as the
+ * constituent device devname. So, if we have a domain that lists ports 2
+ * through 6 on the main SATA controller in a machine, and we plug in a
+ * new drive on port 6, we want to know what actual up and running arrays,
+ * as listed via /proc/mdstat, are also in the same domain. We will use
+ * this information to determine what array we might add our new device
+ * to either as a replacement drive or as a hot spare.
+ */
+struct mdstat_ent *arrays_in_domain(char *devname)
+{
+ struct mdstat_ent *me, *mdstat, *array_list = NULL;
+ struct dev_member *m;
+ struct domain_ent *domain, *de;
+ char *name;
+ char *devpath;
+
+ load_conffile();
+ /* make sure name is a kernel internal name, not a path */
+ name = strrchr(devname, '/');
+ if (!name++)
+ name = devname;
+ devpath = get_devpath_from_devname(name);
+ if (!devpath)
+ return NULL;
+ domain = get_domain_from_devpath(devpath);
+ if (!domain)
+ return NULL;
+
+ mdstat = mdstat_read(0, 0);
+ while (mdstat) {
+ if (mdstat->metadata_version &&
+ strncmp(mdstat->metadata_version, "external:", 9) == 0 &&
+ is_subarray(mdstat->metadata_version+9))
+ /* don't return subarrays, only containers */
+ m = NULL;
+ else for (m = mdstat->members; m; m = m->next) {
+ char *member_path = get_devpath_from_devname(m->name);
+ if (member_path) {
+ de = get_domain_from_devpath(member_path);
+ free(member_path);
+ if (de == domain)
+ /* array has at least one member in our domain*/
+ break;
+ }
+ }
+ if (m) {
+ me = mdstat;
+ mdstat = mdstat->next;
+ me->next = array_list;
+ array_list = me;
+ } else {
+ me = mdstat;
+ mdstat = mdstat->next;
+ me->next = NULL;
+ free_mdstat(me);
+ }
+ }
+ free(devpath);
+ return array_list;
+}
+
+int conf_get_domain_action(char *devname)
+{
+ char *name = strrchr(devname, '/');
+ char *path;
+ struct domain_ent *domain;
+
+ load_conffile();
+ if (!domain_list)
+ return incremental;
+ if (!name++)
+ name = devname;
+ path = get_devpath_from_devname(name);
+ if (!path)
+ return incremental;
+ domain = get_domain_from_devpath(path);
+ free(path);
+ if (!domain)
+ return incremental;
+ return domain->action;
+}
diff --git a/mdadm.h b/mdadm.h
index 51abf29..6247f94 100644
--- a/mdadm.h
+++ b/mdadm.h
@@ -277,6 +277,20 @@ enum special_options {
DetailPlatform,
};
+enum domain_actions {
+ ignore,
+ incremental,
+ spare,
+ grow,
+ partition, /* only use on whole devices in order to put a standard
+ partition table on them, which will invoke new udev
+ calls for the partitions, and then we will do other
+ appropriate things with the partitions */
+ force=512, /* so we can bitwise & this with actions to signify we
+ should forcibly take over drives even if they have
+ other metadata on them */
+};
+
/* structures read from config file */
/* List of mddevice names and identifiers
* Identifiers can be:
@@ -853,6 +867,8 @@ extern int same_dev(char *one, char *two);
extern int parse_auto(char *str, char *msg, int config);
extern mddev_ident_t conf_get_ident(char *dev);
extern mddev_dev_t conf_get_devs(void);
+extern int conf_get_domain_action(char *devname);
+extern struct mdstat_ent *arrays_in_domain(char *devname);
extern int conf_test_dev(char *devname);
extern int conf_test_metadata(const char *version, int is_homehost);
extern struct createinfo *conf_get_create_info(void);
@@ -927,6 +943,7 @@ extern char *devnum2devname(int num);
extern int devname2devnum(char *name);
extern int stat2devnum(struct stat *st);
extern int fd2devnum(int fd);
+extern char *get_devpath_from_devname(char *devname);
static inline int dev2major(int d)
{
diff --git a/util.c b/util.c
index bdb19f9..9327b75 100644
--- a/util.c
+++ b/util.c
@@ -1648,3 +1648,44 @@ void append_metadata_update(struct supertype *st, void *buf, int len)
unsigned int __invalid_size_argument_for_IOC = 0;
#endif
+/*
+ * Resolve from a device name, such as /dev/sdb2, to a device path, such
+ * as pci-0000:00:1f.2-scsi-1:0:0:0-part2 which can then be used to match
+ * against paths in DOMAIN lines from the config file
+ */
+char *get_devpath_from_devname(char *devname)
+{
+ char *name = strrchr(devname, '/'), *link;
+ DIR *by_path;
+ struct dirent *ent;
+ char symlink[PATH_MAX] = "/dev/disk/by-path/";
+ char *symlinkp, target[PATH_MAX];
+ int read;
+
+ if (!name++)
+ name = devname;
+ if ((by_path = opendir("/dev/disk/by-path")) == NULL) {
+ fprintf(stderr, Name ": unable to open /dev/disk/by-path, "
+ "domain lookups not possible\n");
+ return NULL;
+ }
+ symlinkp = &symlink[0] + strlen(symlink);
+ while ((ent = readdir(by_path)) != NULL) {
+ if (ent->d_type != DT_LNK)
+ continue;
+ strncpy(symlinkp, ent->d_name,
+ sizeof(symlink) - (symlinkp - &symlink[0]));
+ read = readlink(symlink, target, sizeof(target));
+ if (read < 0)
+ continue;
+ link = strrchr(target, '/');
+ if (!link++)
+ link = target;
+ if (strcmp(name, link) == 0) {
+ closedir(by_path);
+ return strdup(ent->d_name);
+ }
+ }
+ closedir(by_path);
+ return NULL;
+}