diff options
-rw-r--r-- | Makefile | 10 | ||||
-rw-r--r-- | config.c | 10 | ||||
-rw-r--r-- | mdadm.h | 58 | ||||
-rw-r--r-- | policy.c | 461 |
4 files changed, 533 insertions, 6 deletions
@@ -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 @@ -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); } @@ -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 index 00000000..abf9ee8c --- /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); + } +} |