]> git.ipfire.org Git - thirdparty/mdadm.git/commitdiff
Add policy framework.
authorNeilBrown <neilb@suse.de>
Mon, 5 Jul 2010 00:11:21 +0000 (10:11 +1000)
committerNeilBrown <neilb@suse.de>
Mon, 6 Sep 2010 01:03:43 +0000 (11:03 +1000)
Policy can be stated as lines in mdadm.conf like:

POLICY  type=disk path=pci-0000:00:1f.2-* action=ignore domain=onboard

This defines two distinct policies which apply to any disk (but not
partition) device reached through the pci device 0000:00:1f.2.
The policies are "action=ignore" which means certain actions will
ignore the device, and "domain=onboard" which means all such devices
as treated as being united under the name 'onboard'.

This patch just adds data structures and code to read and
manipulate them.  Future patches will actually use them.

Signed-off-by: NeilBrown <neilb@suse.de>
Makefile
config.c
mdadm.h
policy.c [new file with mode: 0644]

index 0cc9a87c2b2de604f4794a27636de00d377e219f..af9804c5a8b3cdf0f412a33383766502b55a8c61 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -95,26 +95,26 @@ MAN4DIR = $(MANDIR)/man4
 MAN5DIR = $(MANDIR)/man5
 MAN8DIR = $(MANDIR)/man8
 
-OBJS =  mdadm.o config.o mdstat.o  ReadMe.o util.o Manage.o Assemble.o Build.o \
+OBJS =  mdadm.o config.o policy.o mdstat.o  ReadMe.o util.o Manage.o Assemble.o Build.o \
        Create.o Detail.o Examine.o Grow.o Monitor.o dlink.o Kill.o Query.o \
        Incremental.o \
        mdopen.o super0.o super1.o super-ddf.o super-intel.o bitmap.o \
        restripe.o sysfs.o sha1.o mapfile.o crc32.o sg_io.o msg.o \
        platform-intel.o probe_roms.o
 
-SRCS =  mdadm.c config.c mdstat.c  ReadMe.c util.c Manage.c Assemble.c Build.c \
+SRCS =  mdadm.c config.c policy.c mdstat.c  ReadMe.c util.c Manage.c Assemble.c Build.c \
        Create.c Detail.c Examine.c Grow.c Monitor.c dlink.c Kill.c Query.c \
        Incremental.c \
        mdopen.c super0.c super1.c super-ddf.c super-intel.c bitmap.c \
        restripe.c sysfs.c sha1.c mapfile.c crc32.c sg_io.c msg.c \
        platform-intel.c probe_roms.c
 
-MON_OBJS = mdmon.o monitor.o managemon.o util.o mdstat.o sysfs.o config.o \
+MON_OBJS = mdmon.o monitor.o managemon.o util.o mdstat.o sysfs.o config.o policy.o \
        Kill.o sg_io.o dlink.o ReadMe.o super0.o super1.o super-intel.o \
        super-ddf.o sha1.o crc32.o msg.o bitmap.o \
        platform-intel.o probe_roms.o
 
-MON_SRCS = mdmon.c monitor.c managemon.c util.c mdstat.c sysfs.c config.c \
+MON_SRCS = mdmon.c monitor.c managemon.c util.c mdstat.c sysfs.c config.c policy.c \
        Kill.c sg_io.c dlink.c ReadMe.c super0.c super1.c super-intel.c \
        super-ddf.c sha1.c crc32.c msg.c bitmap.c \
        platform-intel.c probe_roms.c
@@ -122,7 +122,7 @@ MON_SRCS = mdmon.c monitor.c managemon.c util.c mdstat.c sysfs.c config.c \
 STATICSRC = pwgr.c
 STATICOBJS = pwgr.o
 
-ASSEMBLE_SRCS := mdassemble.c Assemble.c Manage.c config.c dlink.c util.c \
+ASSEMBLE_SRCS := mdassemble.c Assemble.c Manage.c config.c policy.c dlink.c util.c \
        super0.c super1.c super-ddf.c super-intel.c sha1.c crc32.c sg_io.c mdstat.c \
        platform-intel.c probe_roms.c sysfs.c
 ASSEMBLE_AUTO_SRCS := mdopen.c
index 541a85d83e7ff17b7a0c508cb4c2d86d380fcb43..3baf7af45ab031cd938a78d57b8fbe7d335d4bb7 100644 (file)
--- 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, Policy, PartPolicy, LTEnd };
 char *keywords[] = {
        [Devices]  = "devices",
        [Array]    = "array",
@@ -85,6 +85,8 @@ char *keywords[] = {
        [CreateDev]= "create",
        [Homehost] = "homehost",
        [AutoMode] = "auto",
+       [Policy]   = "policy",
+       [PartPolicy]="part-policy",
        [LTEnd]    = NULL
 };
 
@@ -767,6 +769,12 @@ void load_conffile(void)
                case AutoMode:
                        autoline(line);
                        break;
+               case Policy:
+                       policyline(line, rule_policy);
+                       break;
+               case PartPolicy:
+                       policyline(line, rule_part);
+                       break;
                default:
                        fprintf(stderr, Name ": Unknown keyword %s\n", line);
                }
diff --git a/mdadm.h b/mdadm.h
index 03dd41c61666cb67eabafff07db21e0beb681412..796d76239a18afa53cb1473c069e893447ba9c1a 100644 (file)
--- a/mdadm.h
+++ b/mdadm.h
@@ -734,6 +734,64 @@ extern void get_one_disk(int mdfd, mdu_array_info_t *ainf,
                         mdu_disk_info_t *disk);
 void wait_for(char *dev, int fd);
 
+/*
+ * Data structures for policy management.
+ * Each device can have a policy structure that lists
+ * various name/value pairs each possibly with a metadata associated.
+ * The policy list is sorted by name/value/metadata
+ */
+struct dev_policy {
+       struct dev_policy *next;
+       char *name;     /* None of these strings are allocated.  They are
+                        * all just references to strings which are known
+                        * to exist elsewhere.
+                        * name and metadata can be compared by address equality.
+                        */
+       const char *metadata;
+       char *value;
+};
+
+extern char pol_act[], pol_domain[], pol_metadata[];
+
+/* iterate over the sublist starting at list, having the same
+ * 'name' as 'list', and matching the given metadata (Where
+ * NULL matches anything
+ */
+#define pol_for_each(item, list, _metadata)                            \
+       for (item = list;                                               \
+            item && item->name == list->name;                          \
+            item = item->next)                                         \
+               if (!(!_metadata || !item->metadata || _metadata == item->metadata)) \
+                       ; else
+
+/*
+ * policy records read from mdadm are largely just name-value pairs.
+ * The names are constants, not strdupped
+ */
+struct pol_rule {
+       struct pol_rule *next;
+       char *type;     /* rule_policy or rule_part */
+       struct rule {
+               struct rule *next;
+               char *name;
+               char *value;
+               char *dups; /* duplicates of 'value' with a partNN appended */
+       } *rule;
+};
+
+extern char rule_policy[], rule_part[];
+extern char rule_path[], rule_type[];
+extern char type_part[], type_disk[];
+
+extern void policyline(char *line, char *type);
+extern void policy_free(void);
+
+extern struct dev_policy *disk_policy(struct mdinfo *disk);
+extern void dev_policy_free(struct dev_policy *p);
+
+extern void pol_new(struct dev_policy **pol, char *name, char *val, char *metadata);
+extern struct dev_policy *pol_find(struct dev_policy *pol, char *name);
+
 #if __GNUC__ < 3
 struct stat64;
 #endif
diff --git a/policy.c b/policy.c
new file mode 100644 (file)
index 0000000..abf9ee8
--- /dev/null
+++ b/policy.c
@@ -0,0 +1,461 @@
+/*
+ * mdadm - manage Linux "md" devices aka RAID arrays.
+ *
+ * Copyright (C) 2001-2009 Neil Brown <neilb@suse.de>
+ *
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *    Author: Neil Brown
+ *    Email: <neilb@suse.de>
+ */
+
+#include "mdadm.h"
+#include <dirent.h>
+#include <fnmatch.h>
+#include <ctype.h>
+#include "dlink.h"
+/*
+ * Policy module for mdadm.
+ * A policy statement about a device lists a set of values for each
+ * of a set of names.  Each value can have a metadata type as context.
+ *
+ * names include:
+ *   action - the actions that can be taken on hot-plug
+ *   domain - the domain(s) that the device is part of
+ *
+ * Policy information is extracted from various sources, but
+ * particularly from a set of policy rules in mdadm.conf
+ */
+
+void pol_new(struct dev_policy **pol, char *name, char *val, char *metadata)
+{
+       struct dev_policy *n = malloc(sizeof(*n));
+       const char *real_metadata = NULL;
+       int i;
+
+       n->name = name;
+       n->value = val;
+
+       /* We need to normalise the metadata name */
+       if (metadata) {
+               for (i = 0; superlist[i] ; i++)
+                       if (strcmp(metadata, superlist[i]->name) == 0) {
+                               real_metadata = superlist[i]->name;
+                               break;
+                       }
+               if (!real_metadata) {
+                       if (strcmp(metadata, "1") == 0 ||
+                           strcmp(metadata, "1.0") == 0 ||
+                           strcmp(metadata, "1.1") == 0 ||
+                           strcmp(metadata, "1.2") == 0)
+                               real_metadata = super1.name;
+               }
+               if (!real_metadata) {
+                       static char *prev = NULL;
+                       if (prev != metadata) {
+                               fprintf(stderr, Name ": metadata=%s unrecognised - ignoring rule\n",
+                                       metadata);
+                               prev = metadata;
+                       }
+                       real_metadata = "unknown";
+               }
+       }
+
+       n->metadata = real_metadata;
+       n->next = *pol;
+       *pol = n;
+}
+
+static int pol_lesseq(struct dev_policy *a, struct dev_policy *b)
+{
+       int cmp;
+
+       if (a->name < b->name)
+               return 1;
+       if (a->name > b->name)
+               return 0;
+
+       cmp = strcmp(a->value, b->value);
+       if (cmp < 0)
+               return 1;
+       if (cmp > 0)
+               return 0;
+
+       return (a->metadata <= b->metadata);
+}
+
+static void pol_sort(struct dev_policy **pol)
+{
+       /* sort policy list in *pol by name/metadata/value
+        * using merge sort
+        */
+
+       struct dev_policy *pl[2];
+       pl[0] = *pol;
+       pl[1] = NULL;
+
+       do {
+               struct dev_policy **plp[2], *p[2];
+               int curr = 0;
+               struct dev_policy nul = { NULL, NULL, NULL, NULL };
+               struct dev_policy *prev = &nul;
+               int next = 0;
+
+               /* p[] are the two lists that we are merging.
+                * plp[] are the ends of the two lists we create
+                * from the merge.
+                * 'curr' is which of plp[] that we are currently
+                *   adding items to.
+                * 'next' is which if p[] we will take the next
+                *   item from.
+                * 'prev' is that last value, which was placed in
+                * plp[curr].
+                */
+               plp[0] = &pl[0];
+               plp[1] = &pl[1];
+               p[0] = pl[0];
+               p[1] = pl[1];
+
+               /* take least of p[0] and p[1]
+                * if it is larger than prev, add to
+                * plp[curr], else swap curr then add
+                */
+               while (p[0] || p[1]) {
+                       if (p[next] == NULL ||
+                           (p[1-next] != NULL &&
+                            !(pol_lesseq(prev, p[1-next])
+                              ^pol_lesseq(p[1-next], p[next])
+                              ^pol_lesseq(p[next], prev)))
+                               )
+                               next = 1 - next;
+
+                       if (!pol_lesseq(prev, p[next]))
+                               curr = 1 - curr;
+
+                       *plp[curr] = prev = p[next];
+                       plp[curr] = &p[next]->next;
+                       p[next] = p[next]->next;
+               }
+               *plp[0] = NULL;
+               *plp[1] = NULL;
+       } while (pl[0] && pl[1]);
+       if (pl[0])
+               *pol = pl[0];
+       else
+               *pol = pl[1];
+}
+
+static void pol_dedup(struct dev_policy *pol)
+{
+       /* This is a sorted list - remove duplicates. */
+       while (pol && pol->next) {
+               if (pol_lesseq(pol->next, pol)) {
+                       struct dev_policy *tmp = pol->next;
+                       pol->next = tmp->next;
+                       free(tmp);
+               } else
+                       pol = pol->next;
+       }
+}
+
+/*
+ * pol_find finds the first entry in the policy
+ * list to match name.
+ * If it returns non-NULL there is at least one
+ * value, but how many can only be found by
+ * iterating through the list.
+ */
+struct dev_policy *pol_find(struct dev_policy *pol, char *name)
+{
+       while (pol && pol->name < name)
+               pol = pol->next;
+
+       if (!pol || pol->name != name)
+               return NULL;
+       return pol;
+}
+
+static char *disk_path(struct mdinfo *disk)
+{
+       struct stat stb;
+       int prefix_len;
+       DIR *by_path;
+       char symlink[PATH_MAX] = "/dev/disk/by-path/";
+       struct dirent *ent;
+
+       by_path = opendir(symlink);
+       if (!by_path)
+               return NULL;
+       prefix_len = strlen(symlink);
+
+       while ((ent = readdir(by_path)) != NULL) {
+               if (ent->d_type != DT_LNK)
+                       continue;
+               strncpy(symlink + prefix_len,
+                       ent->d_name,
+                       sizeof(symlink) - prefix_len);
+               if (stat(symlink, &stb) < 0)
+                       continue;
+               if ((stb.st_mode & S_IFMT) != S_IFBLK)
+                       continue;
+               if (stb.st_rdev != makedev(disk->disk.major, disk->disk.minor))
+                       continue;
+               closedir(by_path);
+               return strdup(ent->d_name);
+       }
+       closedir(by_path);
+       return NULL;
+}
+
+char type_part[] = "part";
+char type_disk[] = "disk";
+static char *disk_type(struct mdinfo *disk)
+{
+       char buf[30+20+20];
+       struct stat stb;
+       sprintf(buf, "/sys/dev/block/%d:%d/partition",
+               disk->disk.major, disk->disk.minor);
+       if (stat(buf, &stb) == 0)
+               return type_part;
+       else
+               return type_disk;
+}
+
+static int pol_match(struct rule *rule, char *path, char *type)
+{
+       /* check if this rule matches on path and type */
+       int pathok = 0; /* 0 == no path, 1 == match, -1 == no match yet */
+       int typeok = 0;
+
+       while (rule) {
+               if (rule->name == rule_path) {
+                       if (pathok == 0)
+                               pathok = -1;
+                       if (fnmatch(rule->value, path, 0) == 0)
+                               pathok = 1;
+               }
+               if (rule->name == rule_type) {
+                       if (typeok == 0)
+                               typeok = -1;
+                       if (strcmp(rule->value, type) == 0)
+                               typeok = 1;
+               }
+               rule = rule->next;
+       }
+       return pathok >= 0 && typeok >= 0;
+}
+
+static void pol_merge(struct dev_policy **pol, struct rule *rule)
+{
+       /* copy any name assignments from rule into pol */
+       struct rule *r;
+       char *metadata = NULL;
+       for (r = rule; r ; r = r->next)
+               if (r->name == pol_metadata)
+                       metadata = r->value;
+
+       for (r = rule; r ; r = r->next)
+               if (r->name == pol_act ||
+                   r->name == pol_domain)
+                       pol_new(pol, r->name, r->value, metadata);
+}
+
+static int path_has_part(char *path, char **part)
+{
+       /* check if path ends with "-partNN" and
+        * if it does, place a pointer to "-pathNN"
+        * in 'part'.
+        */
+       int l = strlen(path);
+       while (l > 1 && isdigit(path[l-1]))
+               l--;
+       if (l < 5 || strncmp(path+l-5, "-part", 5) != 0)
+               return 0;
+       *part = path+l-4;
+       return 1;
+}
+
+static void pol_merge_part(struct dev_policy **pol, struct rule *rule, char *part)
+{
+       /* copy any name assignments from rule into pol, appending
+        * -part to any domain.  The string with -part appended is
+        * stored with the rule so it has a lifetime to match
+        * the rule.
+        */
+       struct rule *r;
+       char *metadata = NULL;
+       for (r = rule; r ; r = r->next)
+               if (r->name == pol_metadata)
+                       metadata = r->value;
+
+       for (r = rule; r ; r = r->next) {
+               if (r->name == pol_act)
+                       pol_new(pol, r->name, r->value, metadata);
+               else if (r->name == pol_domain) {
+                       char *dom;
+                       int len;
+                       if (r->dups == NULL)
+                               r->dups = dl_head();
+                       len = strlen(r->value);
+                       for (dom = dl_next(r->dups); dom != r->dups;
+                            dom = dl_next(dom))
+                               if (strcmp(dom+len+1, part)== 0)
+                                       break;
+                       if (dom == r->dups) {
+                               char *newdom = dl_strndup(
+                                       r->value, len + 1 + strlen(part));
+                               strcat(strcat(newdom, "-"), part);
+                               dl_add(r->dups, newdom);
+                               dom = newdom;
+                       }
+                       pol_new(pol, r->name, dom, metadata);
+               }
+       }
+}
+
+static struct pol_rule *config_rules = NULL;
+static struct pol_rule **config_rules_end = NULL;
+static int config_rules_has_path = 0;
+
+/*
+ * most policy comes from a set policy rules that are
+ * read from the config file.
+ * disk_policy() gathers policy information for the
+ * disk described in the given mdinfo (disk.{major,minor}).
+ */
+struct dev_policy *disk_policy(struct mdinfo *disk)
+{
+       char *path = NULL;
+       char *type = disk_type(disk);
+       struct pol_rule *rules;
+       struct dev_policy *pol = NULL;
+
+       if (!type)
+               return NULL;
+       if (config_rules_has_path) {
+               path = disk_path(disk);
+               if (!path)
+                       return NULL;
+       }
+
+       rules = config_rules;
+
+       while (rules) {
+               char *part;
+               if (rules->type == rule_policy)
+                       if (pol_match(rules->rule, path, type))
+                               pol_merge(&pol, rules->rule);
+               if (rules->type == rule_part && strcmp(type, type_part) == 0)
+                       if (path_has_part(path, &part)) {
+                               *part = 0;
+                               if (pol_match(rules->rule, path, type_disk))
+                                       pol_merge_part(&pol, rules->rule, part+1);
+                               *part = '-';
+                       }
+               rules = rules->next;
+       }
+       pol_sort(&pol);
+       pol_dedup(pol);
+       free(path);
+       return pol;
+}
+
+/*
+ * process policy rules read from config file.
+ */
+
+char rule_path[] = "path";
+char rule_type[] = "type";
+
+char rule_policy[] = "policy";
+char rule_part[] = "part-policy";
+
+char pol_metadata[] = "metadata";
+char pol_act[] = "action";
+char pol_domain[] = "domain";
+
+static int try_rule(char *w, char *name, struct rule **rp)
+{
+       struct rule *r;
+       int len = strlen(name);
+       if (strncmp(w, name, len) != 0 ||
+           w[len] != '=')
+               return 0;
+       r = malloc(sizeof(*r));
+       r->next = *rp;
+       r->name = name;
+       r->value = strdup(w+len+1);
+       r->dups = NULL;
+       *rp = r;
+       return 1;
+}
+
+void policyline(char *line, char *type)
+{
+       struct pol_rule *pr;
+       char *w;
+
+       if (config_rules_end == NULL)
+               config_rules_end = &config_rules;
+
+       pr = malloc(sizeof(*pr));
+       pr->type = type;
+       pr->rule = NULL;
+       for (w = dl_next(line); w != line ; w = dl_next(w)) {
+               if (try_rule(w, rule_path, &pr->rule))
+                       config_rules_has_path = 1;
+               else if (! try_rule(w, rule_type, &pr->rule) &&
+                        ! try_rule(w, pol_metadata, &pr->rule) &&
+                        ! try_rule(w, pol_act, &pr->rule) &&
+                        ! try_rule(w, pol_domain, &pr->rule))
+                       fprintf(stderr, Name ": policy rule %s unrecognised and ignored\n",
+                               w);
+       }
+       pr->next = config_rules;
+       config_rules = pr;
+}
+
+void policy_free(void)
+{
+       while (config_rules) {
+               struct pol_rule *pr = config_rules;
+               struct rule *r;
+
+               config_rules = config_rules->next;
+
+               for (r = pr->rule; r; ) {
+                       struct rule *next = r->next;
+                       free(r->value);
+                       if (r->dups)
+                               free_line(r->dups);
+                       free(r);
+                       r = next;
+               }
+               free(pr);
+       }
+       config_rules_end = NULL;
+       config_rules_has_path = 0;
+}
+
+void dev_policy_free(struct dev_policy *p)
+{
+       struct dev_policy *t;
+       while (p) {
+               t = p;
+               p = p->next;
+               free(t);
+       }
+}