]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - policy.c
Allow domain_test to report that no domains were found.
[thirdparty/mdadm.git] / policy.c
index ad5850bd8244a8a1eae9a07595d3ad826355f7b0..afb640f594a48c74896b25de9cc59cf756a83b72 100644 (file)
--- a/policy.c
+++ b/policy.c
@@ -40,7 +40,8 @@
  * particularly from a set of policy rules in mdadm.conf
  */
 
-static 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 @@ static void pol_new(struct dev_policy **pol, char *name, char *val, char *metada
                                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,6 +362,18 @@ 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;
@@ -521,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;
@@ -581,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
@@ -601,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)
 {
@@ -619,9 +648,12 @@ int domain_test(struct domainlist *dom, struct dev_policy *pol,
        /* Check that all domains in pol (for metadata) are also in
         * dom.  Both lists are sorted.
         * If pol has no domains, we don't really know about this device
-        * so we reject the match.
+        * so we allow caller to choose:
+        * -1:  has no domains
+        *  0:  has domains, not all match
+        *  1:  has domains, all match
         */
-       int found_any = 0;
+       int found_any = -1;
        struct dev_policy *p;
 
        pol = pol_find(pol, pol_domain);
@@ -735,3 +767,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;
+}