X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=policy.c;h=38b0072b5d111ab6f76168185c575efc0a5d4d8c;hb=20b60dcd6cea6cb517cea31056241607a2964e5d;hp=cc126949a7f98ae8c675b773028c222c9c3fd5d1;hpb=d2db30455891a97ac0ac17bb64848de52bfb1566;p=thirdparty%2Fmdadm.git diff --git a/policy.c b/policy.c index cc126949..38b0072b 100644 --- a/policy.c +++ b/policy.c @@ -40,7 +40,8 @@ * particularly from a set of policy rules in mdadm.conf */ -void pol_new(struct dev_policy **pol, char *name, char *val, char *metadata) +static void pol_new(struct dev_policy **pol, char *name, const char *val, + const char *metadata) { struct dev_policy *n = malloc(sizeof(*n)); const char *real_metadata = NULL; @@ -64,7 +65,7 @@ void pol_new(struct dev_policy **pol, char *name, char *val, char *metadata) real_metadata = super1.name; } if (!real_metadata) { - static char *prev = NULL; + static const char *prev = NULL; if (prev != metadata) { fprintf(stderr, Name ": metadata=%s unrecognised - ignoring rule\n", metadata); @@ -340,6 +341,7 @@ struct dev_policy *path_policy(char *path, char *type) { struct pol_rule *rules; struct dev_policy *pol = NULL; + int i; if (!type) return NULL; @@ -360,11 +362,33 @@ struct dev_policy *path_policy(char *path, char *type) } rules = rules->next; } + + /* Now add any metadata-specific internal knowledge + * about this path + */ + for (i=0; superlist[i]; i++) + if (superlist[i]->get_disk_controller_domain) { + const char *d = + superlist[i]->get_disk_controller_domain(path); + if (d) + pol_new(&pol, pol_domain, d, superlist[i]->name); + } + pol_sort(&pol); pol_dedup(pol); return pol; } +void pol_add(struct dev_policy **pol, + char *name, char *val, + char *metadata) +{ + pol_new(pol, name, val, metadata); + pol_sort(pol); + pol_dedup(*pol); +} + + /* * disk_policy() gathers policy information for the * disk described in the given mdinfo (disk.{major,minor}). @@ -511,7 +535,7 @@ void dev_policy_free(struct dev_policy *p) } } -static enum policy_action map_act(char *act) +static enum policy_action map_act(const char *act) { if (strcmp(act, "include") == 0) return act_include; @@ -571,7 +595,8 @@ int disk_action_allows(struct mdinfo *disk, const char *metadata, enum policy_ac * As dev policies are already sorted, this is fairly easy to manage. */ -static struct domainlist **domain_merge_one(struct domainlist **domp, char *domain) +static struct domainlist **domain_merge_one(struct domainlist **domp, + const char *domain) { /* merge a domain name into a sorted list and return the * location of the insertion or match @@ -591,6 +616,20 @@ static struct domainlist **domain_merge_one(struct domainlist **domp, char *doma return domp; } +#if (DEBUG) +void dump_policy(struct dev_policy *policy) +{ + while (policy) { + dprintf("policy: %p name: %s value: %s metadata: %s\n", + policy, + policy->name, + policy->value, + policy->metadata); + policy = policy->next; + } +} +#endif + void domain_merge(struct domainlist **domp, struct dev_policy *pollist, const char *metadata) { @@ -600,7 +639,7 @@ void domain_merge(struct domainlist **domp, struct dev_policy *pollist, struct dev_policy *pol; pollist = pol_find(pollist, pol_domain); pol_for_each(pol, pollist, metadata) - domp = domain_merge_one(domp, pol->value); + domain_merge_one(domp, pol->value); } int domain_test(struct domainlist *dom, struct dev_policy *pol, @@ -625,19 +664,31 @@ int domain_test(struct domainlist *dom, struct dev_policy *pol, return found_any; } +void domainlist_add_dev(struct domainlist **dom, int devnum, const char *metadata) +{ + struct dev_policy *pol = devnum_policy(devnum); + domain_merge(dom, pol, metadata); + dev_policy_free(pol); +} + struct domainlist *domain_from_array(struct mdinfo *mdi, const char *metadata) { struct domainlist *domlist = NULL; - for (mdi = mdi->devs ; mdi ; mdi = mdi->next) { - struct dev_policy *pol = disk_policy(mdi); + for (mdi = mdi->devs ; mdi ; mdi = mdi->next) + domainlist_add_dev(&domlist, makedev(mdi->disk.major, + mdi->disk.minor), + metadata); - domain_merge(&domlist, pol, metadata); - dev_policy_free(pol); - } return domlist; } +void domain_add(struct domainlist **domp, char *domain) +{ + domain_merge_one(domp, domain); +} + + void domain_free(struct domainlist *dl) { while (dl) { @@ -713,3 +764,136 @@ int policy_check_path(struct mdinfo *disk, struct map_ent *array) fclose(f); return rv == 5; } + +/* invocation of udev rule file */ +char udev_template_start[] = +"# do not edit this file, it is automatically generated by mdadm\n" +"\n"; + +/* find rule named rule_type and return its value */ +char *find_rule(struct rule *rule, char *rule_type) +{ + while (rule) { + if (rule->name == rule_type) + return rule->value; + + rule = rule->next; + } + return NULL; +} + +#define UDEV_RULE_FORMAT \ +"ACTION==\"add\", SUBSYSTEM=\"block\", " \ +"ENV{DEVTYPE}==\"%s\", ENV{ID_PATH}==\"%s\", " \ +"RUN+=\"/sbin/mdadm --incremental $env{DEVNAME}\", " + +#define UDEV_RULE_FORMAT_NOTYPE \ +"ACTION==\"add\", SUBSYSTEM=\"block\", " \ +"ENV{ID_PATH}==\"%s\", " \ +"RUN+=\"/sbin/mdadm --incremental $env{DEVNAME}\", " + +/* Write rule in the rule file. Use format from UDEV_RULE_FORMAT */ +int write_rule(struct rule *rule, int fd, int force_part) +{ + char line[1024]; + char *pth = find_rule(rule, rule_path); + char *typ = find_rule(rule, rule_type); + if (!pth) + return -1; + + if (force_part) + typ = type_part; + if (typ) + snprintf(line, sizeof(line) - 1, UDEV_RULE_FORMAT, typ, pth); + else + snprintf(line, sizeof(line) - 1, UDEV_RULE_FORMAT_NOTYPE, pth); + return write(fd, line, strlen(line)) == (int)strlen(line); +} + +/* Generate single entry in udev rule basing on POLICY line found in config + * file. Take only those with paths, only first occurrence if paths are equal + * and if actions supports handling of spares (>=act_spare_same_slot) + */ +int generate_entries(int fd) +{ + struct pol_rule *loop, *dup; + char *loop_value, *dup_value; + int duplicate; + + for (loop = config_rules; loop; loop = loop->next) { + if (loop->type != rule_policy && loop->type != rule_part) + continue; + duplicate = 0; + + /* only policies with paths and with actions supporting + * bare disks are considered */ + loop_value = find_rule(loop->rule, pol_act); + if (!loop_value || map_act(loop_value) < act_spare_same_slot) + continue; + loop_value = find_rule(loop->rule, rule_path); + if (!loop_value) + continue; + for (dup = config_rules; dup != loop; dup = dup->next) { + if (dup->type != rule_policy && loop->type != rule_part) + continue; + dup_value = find_rule(dup->rule, pol_act); + if (!dup_value || map_act(dup_value) < act_spare_same_slot) + continue; + dup_value = find_rule(dup->rule, rule_path); + if (!dup_value) + continue; + if (strcmp(loop_value, dup_value) == 0) { + duplicate = 1; + break; + } + } + + /* not a dup or first occurrence */ + if (!duplicate) + if (!write_rule(loop->rule, fd, loop->type == rule_part) ) + return 0; + } + return 1; +} + +/* Write_rules routine creates dynamic udev rules used to handle + * hot-plug events for bare devices (and making them spares) + */ +int Write_rules(char *rule_name) +{ + int fd; + char udev_rule_file[PATH_MAX]; + + if (rule_name) { + strcpy(udev_rule_file, rule_name); + strcat(udev_rule_file, ".temp"); + fd = creat(udev_rule_file, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd == -1) + return 1; + } else + fd = 1; + + /* write static invocation */ + if (write(fd, udev_template_start, + sizeof(udev_template_start) - 1) + != (int)sizeof(udev_template_start)-1) + goto abort; + + /* iterate, if none created or error occurred, remove file */ + if (generate_entries(fd) < 0) + goto abort; + + fsync(fd); + if (rule_name) { + close(fd); + rename(udev_rule_file, rule_name); + } + return 0; +abort: + if (rule_name) { + close(fd); + unlink(udev_rule_file); + } + return 1; +}