]> git.neil.brown.name Git - mdadm.git/blob - policy.c
Release mdadm-4.0
[mdadm.git] / policy.c
1 /*
2  * mdadm - manage Linux "md" devices aka RAID arrays.
3  *
4  * Copyright (C) 2001-2009 Neil Brown <neilb@suse.de>
5  *
6  *
7  *    This program is free software; you can redistribute it and/or modify
8  *    it under the terms of the GNU General Public License as published by
9  *    the Free Software Foundation; either version 2 of the License, or
10  *    (at your option) any later version.
11  *
12  *    This program is distributed in the hope that it will be useful,
13  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *    GNU General Public License for more details.
16  *
17  *    You should have received a copy of the GNU General Public License
18  *    along with this program; if not, write to the Free Software
19  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  *    Author: Neil Brown
22  *    Email: <neilb@suse.de>
23  */
24
25 #include "mdadm.h"
26 #include <dirent.h>
27 #include <fnmatch.h>
28 #include <ctype.h>
29 #include "dlink.h"
30 /*
31  * Policy module for mdadm.
32  * A policy statement about a device lists a set of values for each
33  * of a set of names.  Each value can have a metadata type as context.
34  *
35  * names include:
36  *   action - the actions that can be taken on hot-plug
37  *   domain - the domain(s) that the device is part of
38  *
39  * Policy information is extracted from various sources, but
40  * particularly from a set of policy rules in mdadm.conf
41  */
42
43 static void pol_new(struct dev_policy **pol, char *name, const char *val,
44                     const char *metadata)
45 {
46         struct dev_policy *n = xmalloc(sizeof(*n));
47         const char *real_metadata = NULL;
48         int i;
49
50         n->name = name;
51         n->value = val;
52
53         /* We need to normalise the metadata name */
54         if (metadata) {
55                 for (i = 0; superlist[i] ; i++)
56                         if (strcmp(metadata, superlist[i]->name) == 0) {
57                                 real_metadata = superlist[i]->name;
58                                 break;
59                         }
60                 if (!real_metadata) {
61                         if (strcmp(metadata, "1") == 0 ||
62                             strcmp(metadata, "1.0") == 0 ||
63                             strcmp(metadata, "1.1") == 0 ||
64                             strcmp(metadata, "1.2") == 0)
65                                 real_metadata = super1.name;
66                 }
67                 if (!real_metadata) {
68                         static const char *prev = NULL;
69                         if (prev != metadata) {
70                                 pr_err("metadata=%s unrecognised - ignoring rule\n",
71                                         metadata);
72                                 prev = metadata;
73                         }
74                         real_metadata = "unknown";
75                 }
76         }
77
78         n->metadata = real_metadata;
79         n->next = *pol;
80         *pol = n;
81 }
82
83 static int pol_lesseq(struct dev_policy *a, struct dev_policy *b)
84 {
85         int cmp;
86
87         if (a->name < b->name)
88                 return 1;
89         if (a->name > b->name)
90                 return 0;
91
92         cmp = strcmp(a->value, b->value);
93         if (cmp < 0)
94                 return 1;
95         if (cmp > 0)
96                 return 0;
97
98         return (a->metadata <= b->metadata);
99 }
100
101 static void pol_sort(struct dev_policy **pol)
102 {
103         /* sort policy list in *pol by name/metadata/value
104          * using merge sort
105          */
106
107         struct dev_policy *pl[2];
108         pl[0] = *pol;
109         pl[1] = NULL;
110
111         do {
112                 struct dev_policy **plp[2], *p[2];
113                 int curr = 0;
114                 struct dev_policy nul = { NULL, NULL, NULL, NULL };
115                 struct dev_policy *prev = &nul;
116                 int next = 0;
117
118                 /* p[] are the two lists that we are merging.
119                  * plp[] are the ends of the two lists we create
120                  * from the merge.
121                  * 'curr' is which of plp[] that we are currently
122                  *   adding items to.
123                  * 'next' is which if p[] we will take the next
124                  *   item from.
125                  * 'prev' is that last value, which was placed in
126                  * plp[curr].
127                  */
128                 plp[0] = &pl[0];
129                 plp[1] = &pl[1];
130                 p[0] = pl[0];
131                 p[1] = pl[1];
132
133                 /* take least of p[0] and p[1]
134                  * if it is larger than prev, add to
135                  * plp[curr], else swap curr then add
136                  */
137                 while (p[0] || p[1]) {
138                         if (p[next] == NULL ||
139                             (p[1-next] != NULL &&
140                              !(pol_lesseq(prev, p[1-next])
141                                ^pol_lesseq(prev, p[next])
142                                ^pol_lesseq(p[next], p[1-next])))
143                                 )
144                                 next = 1 - next;
145
146                         if (!pol_lesseq(prev, p[next]))
147                                 curr = 1 - curr;
148
149                         *plp[curr] = prev = p[next];
150                         plp[curr] = &p[next]->next;
151                         p[next] = p[next]->next;
152                 }
153                 *plp[0] = NULL;
154                 *plp[1] = NULL;
155         } while (pl[0] && pl[1]);
156         if (pl[0])
157                 *pol = pl[0];
158         else
159                 *pol = pl[1];
160 }
161
162 static void pol_dedup(struct dev_policy *pol)
163 {
164         /* This is a sorted list - remove duplicates. */
165         while (pol && pol->next) {
166                 if (pol_lesseq(pol->next, pol)) {
167                         struct dev_policy *tmp = pol->next;
168                         pol->next = tmp->next;
169                         free(tmp);
170                 } else
171                         pol = pol->next;
172         }
173 }
174
175 /*
176  * pol_find finds the first entry in the policy
177  * list to match name.
178  * If it returns non-NULL there is at least one
179  * value, but how many can only be found by
180  * iterating through the list.
181  */
182 struct dev_policy *pol_find(struct dev_policy *pol, char *name)
183 {
184         while (pol && pol->name < name)
185                 pol = pol->next;
186
187         if (!pol || pol->name != name)
188                 return NULL;
189         return pol;
190 }
191
192 static char *disk_path(struct mdinfo *disk)
193 {
194         struct stat stb;
195         int prefix_len;
196         DIR *by_path;
197         char symlink[PATH_MAX] = "/dev/disk/by-path/";
198         char nm[PATH_MAX];
199         struct dirent *ent;
200         int rv;
201
202         by_path = opendir(symlink);
203         if (by_path) {
204                 prefix_len = strlen(symlink);
205                 while ((ent = readdir(by_path)) != NULL) {
206                         if (ent->d_type != DT_LNK)
207                                 continue;
208                         strncpy(symlink + prefix_len,
209                                         ent->d_name,
210                                         sizeof(symlink) - prefix_len);
211                         if (stat(symlink, &stb) < 0)
212                                 continue;
213                         if ((stb.st_mode & S_IFMT) != S_IFBLK)
214                                 continue;
215                         if (stb.st_rdev != makedev(disk->disk.major, disk->disk.minor))
216                                 continue;
217                         closedir(by_path);
218                         return xstrdup(ent->d_name);
219                 }
220                 closedir(by_path);
221         }
222         /* A NULL path isn't really acceptable - use the devname.. */
223         sprintf(symlink, "/sys/dev/block/%d:%d", disk->disk.major, disk->disk.minor);
224         rv = readlink(symlink, nm, sizeof(nm)-1);
225         if (rv > 0) {
226                 char *dname;
227                 nm[rv] = 0;
228                 dname = strrchr(nm, '/');
229                 if (dname)
230                         return xstrdup(dname + 1);
231         }
232         return xstrdup("unknown");
233 }
234
235 char type_part[] = "part";
236 char type_disk[] = "disk";
237 static char *disk_type(struct mdinfo *disk)
238 {
239         char buf[30+20+20];
240         struct stat stb;
241         sprintf(buf, "/sys/dev/block/%d:%d/partition",
242                 disk->disk.major, disk->disk.minor);
243         if (stat(buf, &stb) == 0)
244                 return type_part;
245         else
246                 return type_disk;
247 }
248
249 static int pol_match(struct rule *rule, char *path, char *type)
250 {
251         /* check if this rule matches on path and type */
252         int pathok = 0; /* 0 == no path, 1 == match, -1 == no match yet */
253         int typeok = 0;
254
255         while (rule) {
256                 if (rule->name == rule_path) {
257                         if (pathok == 0)
258                                 pathok = -1;
259                         if (path && fnmatch(rule->value, path, 0) == 0)
260                                 pathok = 1;
261                 }
262                 if (rule->name == rule_type) {
263                         if (typeok == 0)
264                                 typeok = -1;
265                         if (type && strcmp(rule->value, type) == 0)
266                                 typeok = 1;
267                 }
268                 rule = rule->next;
269         }
270         return pathok >= 0 && typeok >= 0;
271 }
272
273 static void pol_merge(struct dev_policy **pol, struct rule *rule)
274 {
275         /* copy any name assignments from rule into pol */
276         struct rule *r;
277         char *metadata = NULL;
278         for (r = rule; r ; r = r->next)
279                 if (r->name == pol_metadata)
280                         metadata = r->value;
281
282         for (r = rule; r ; r = r->next)
283                 if (r->name == pol_act ||
284                     r->name == pol_domain ||
285                     r->name == pol_auto)
286                         pol_new(pol, r->name, r->value, metadata);
287 }
288
289 static int path_has_part(char *path, char **part)
290 {
291         /* check if path ends with "-partNN" and
292          * if it does, place a pointer to "-pathNN"
293          * in 'part'.
294          */
295         int l;
296         if (!path)
297                 return 0;
298         l = strlen(path);
299         while (l > 1 && isdigit(path[l-1]))
300                 l--;
301         if (l < 5 || strncmp(path+l-5, "-part", 5) != 0)
302                 return 0;
303         *part = path+l-4;
304         return 1;
305 }
306
307 static void pol_merge_part(struct dev_policy **pol, struct rule *rule, char *part)
308 {
309         /* copy any name assignments from rule into pol, appending
310          * -part to any domain.  The string with -part appended is
311          * stored with the rule so it has a lifetime to match
312          * the rule.
313          */
314         struct rule *r;
315         char *metadata = NULL;
316         for (r = rule; r ; r = r->next)
317                 if (r->name == pol_metadata)
318                         metadata = r->value;
319
320         for (r = rule; r ; r = r->next) {
321                 if (r->name == pol_act)
322                         pol_new(pol, r->name, r->value, metadata);
323                 else if (r->name == pol_domain) {
324                         char *dom;
325                         int len;
326                         if (r->dups == NULL)
327                                 r->dups = dl_head();
328                         len = strlen(r->value);
329                         for (dom = dl_next(r->dups); dom != r->dups;
330                              dom = dl_next(dom))
331                                 if (strcmp(dom+len+1, part)== 0)
332                                         break;
333                         if (dom == r->dups) {
334                                 char *newdom = dl_strndup(
335                                         r->value, len + 1 + strlen(part));
336                                 strcat(strcat(newdom, "-"), part);
337                                 dl_add(r->dups, newdom);
338                                 dom = newdom;
339                         }
340                         pol_new(pol, r->name, dom, metadata);
341                 }
342         }
343 }
344
345 static struct pol_rule *config_rules = NULL;
346 static struct pol_rule **config_rules_end = NULL;
347 static int config_rules_has_path = 0;
348
349 /*
350  * most policy comes from a set policy rules that are
351  * read from the config file.
352  * path_policy() gathers policy information for the
353  * disk described in the given a 'path' and a 'type'.
354  */
355 struct dev_policy *path_policy(char *path, char *type)
356 {
357         struct pol_rule *rules;
358         struct dev_policy *pol = NULL;
359         int i;
360
361         rules = config_rules;
362
363         while (rules) {
364                 char *part;
365                 if (rules->type == rule_policy)
366                         if (pol_match(rules->rule, path, type))
367                                 pol_merge(&pol, rules->rule);
368                 if (rules->type == rule_part && strcmp(type, type_part) == 0)
369                         if (path_has_part(path, &part)) {
370                                 *part = 0;
371                                 if (pol_match(rules->rule, path, type_disk))
372                                         pol_merge_part(&pol, rules->rule, part+1);
373                                 *part = '-';
374                         }
375                 rules = rules->next;
376         }
377
378         /* Now add any metadata-specific internal knowledge
379          * about this path
380          */
381         for (i=0; path && superlist[i]; i++)
382                 if (superlist[i]->get_disk_controller_domain) {
383                         const char *d =
384                                 superlist[i]->get_disk_controller_domain(path);
385                         if (d)
386                                 pol_new(&pol, pol_domain, d, superlist[i]->name);
387                 }
388
389         pol_sort(&pol);
390         pol_dedup(pol);
391         return pol;
392 }
393
394 void pol_add(struct dev_policy **pol,
395                     char *name, char *val,
396                     char *metadata)
397 {
398         pol_new(pol, name, val, metadata);
399         pol_sort(pol);
400         pol_dedup(*pol);
401 }
402
403 /*
404  * disk_policy() gathers policy information for the
405  * disk described in the given mdinfo (disk.{major,minor}).
406  */
407 struct dev_policy *disk_policy(struct mdinfo *disk)
408 {
409         char *path = NULL;
410         char *type = disk_type(disk);
411         struct dev_policy *pol = NULL;
412
413         if (config_rules_has_path)
414                 path = disk_path(disk);
415
416         pol = path_policy(path, type);
417
418         free(path);
419         return pol;
420 }
421
422 struct dev_policy *devid_policy(int dev)
423 {
424         struct mdinfo disk;
425         disk.disk.major = major(dev);
426         disk.disk.minor = minor(dev);
427         return disk_policy(&disk);
428 }
429
430 /*
431  * process policy rules read from config file.
432  */
433
434 char rule_path[] = "path";
435 char rule_type[] = "type";
436
437 char rule_policy[] = "policy";
438 char rule_part[] = "part-policy";
439
440 char pol_metadata[] = "metadata";
441 char pol_act[] = "action";
442 char pol_domain[] = "domain";
443 char pol_auto[] = "auto";
444
445 static int try_rule(char *w, char *name, struct rule **rp)
446 {
447         struct rule *r;
448         int len = strlen(name);
449         if (strncmp(w, name, len) != 0 ||
450             w[len] != '=')
451                 return 0;
452         r = xmalloc(sizeof(*r));
453         r->next = *rp;
454         r->name = name;
455         r->value = xstrdup(w+len+1);
456         r->dups = NULL;
457         *rp = r;
458         return 1;
459 }
460
461 void policyline(char *line, char *type)
462 {
463         struct pol_rule *pr;
464         char *w;
465
466         if (config_rules_end == NULL)
467                 config_rules_end = &config_rules;
468
469         pr = xmalloc(sizeof(*pr));
470         pr->type = type;
471         pr->rule = NULL;
472         for (w = dl_next(line); w != line ; w = dl_next(w)) {
473                 if (try_rule(w, rule_path, &pr->rule))
474                         config_rules_has_path = 1;
475                 else if (! try_rule(w, rule_type, &pr->rule) &&
476                          ! try_rule(w, pol_metadata, &pr->rule) &&
477                          ! try_rule(w, pol_act, &pr->rule) &&
478                          ! try_rule(w, pol_domain, &pr->rule) &&
479                          ! try_rule(w, pol_auto, &pr->rule))
480                         pr_err("policy rule %s unrecognised and ignored\n",
481                                 w);
482         }
483         pr->next = config_rules;
484         config_rules = pr;
485 }
486
487 void policy_add(char *type, ...)
488 {
489         va_list ap;
490         struct pol_rule *pr;
491         char *name, *val;
492
493         pr = xmalloc(sizeof(*pr));
494         pr->type = type;
495         pr->rule = NULL;
496
497         va_start(ap, type);
498         while ((name = va_arg(ap, char*)) != NULL) {
499                 struct rule *r;
500
501                 val = va_arg(ap, char*);
502                 r = xmalloc(sizeof(*r));
503                 r->next = pr->rule;
504                 r->name = name;
505                 r->value = xstrdup(val);
506                 r->dups = NULL;
507                 pr->rule = r;
508         }
509         pr->next = config_rules;
510         config_rules = pr;
511         va_end(ap);
512 }
513
514 void policy_free(void)
515 {
516         while (config_rules) {
517                 struct pol_rule *pr = config_rules;
518                 struct rule *r;
519
520                 config_rules = config_rules->next;
521
522                 for (r = pr->rule; r; ) {
523                         struct rule *next = r->next;
524                         free(r->value);
525                         if (r->dups)
526                                 free_line(r->dups);
527                         free(r);
528                         r = next;
529                 }
530                 free(pr);
531         }
532         config_rules_end = NULL;
533         config_rules_has_path = 0;
534 }
535
536 void dev_policy_free(struct dev_policy *p)
537 {
538         struct dev_policy *t;
539         while (p) {
540                 t = p;
541                 p = p->next;
542                 free(t);
543         }
544 }
545
546 static enum policy_action map_act(const char *act)
547 {
548         if (strcmp(act, "include") == 0)
549                 return act_include;
550         if (strcmp(act, "re-add") == 0)
551                 return act_re_add;
552         if (strcmp(act, "spare") == 0)
553                 return act_spare;
554         if (strcmp(act, "spare-same-slot") == 0)
555                 return act_spare_same_slot;
556         if (strcmp(act, "force-spare") == 0)
557                 return act_force_spare;
558         return act_err;
559 }
560
561 static enum policy_action policy_action(struct dev_policy *plist, const char *metadata)
562 {
563         enum policy_action rv = act_default;
564         struct dev_policy *p;
565
566         plist = pol_find(plist, pol_act);
567         pol_for_each(p, plist, metadata) {
568                 enum policy_action a = map_act(p->value);
569                 if (a > rv)
570                         rv = a;
571         }
572         return rv;
573 }
574
575 int policy_action_allows(struct dev_policy *plist, const char *metadata, enum policy_action want)
576 {
577         enum policy_action act = policy_action(plist, metadata);
578
579         if (act == act_err)
580                 return 0;
581         return (act >= want);
582 }
583
584 int disk_action_allows(struct mdinfo *disk, const char *metadata, enum policy_action want)
585 {
586         struct dev_policy *pol = disk_policy(disk);
587         int rv = policy_action_allows(pol, metadata, want);
588
589         dev_policy_free(pol);
590         return rv;
591 }
592
593 /* Domain policy:
594  * Any device can have a list of domains asserted by different policy
595  * statements.
596  * An array also has a list of domains comprising all the domains of
597  * all the devices in an array.
598  * Where an array has a spare-group, that becomes an addition domain for
599  * every device in the array and thus for the array.
600  *
601  * We keep the list of domains in a sorted linked list
602  * As dev policies are already sorted, this is fairly easy to manage.
603  */
604
605 static struct domainlist **domain_merge_one(struct domainlist **domp,
606                                             const char *domain)
607 {
608         /* merge a domain name into a sorted list and return the
609          * location of the insertion or match
610          */
611         struct domainlist *dom = *domp;
612
613         while (dom && strcmp(dom->dom, domain) < 0) {
614                 domp = &dom->next;
615                 dom = *domp;
616         }
617         if (dom == NULL || strcmp(dom->dom, domain) != 0) {
618                 dom = xmalloc(sizeof(*dom));
619                 dom->next = *domp;
620                 dom->dom = domain;
621                 *domp = dom;
622         }
623         return domp;
624 }
625
626 #if (DEBUG)
627 void dump_policy(struct dev_policy *policy)
628 {
629         while (policy) {
630                 dprintf("policy: %p name: %s value: %s metadata: %s\n",
631                         policy,
632                         policy->name,
633                         policy->value,
634                         policy->metadata);
635                 policy = policy->next;
636         }
637 }
638 #endif
639
640 void domain_merge(struct domainlist **domp, struct dev_policy *pollist,
641                          const char *metadata)
642 {
643         /* Add to 'domp' all the domains in pol that apply to 'metadata'
644          * which are not already in domp
645          */
646         struct dev_policy *pol;
647         pollist = pol_find(pollist, pol_domain);
648         pol_for_each(pol, pollist, metadata)
649                 domain_merge_one(domp, pol->value);
650 }
651
652 int domain_test(struct domainlist *dom, struct dev_policy *pol,
653                 const char *metadata)
654 {
655         /* Check that all domains in pol (for metadata) are also in
656          * dom.  Both lists are sorted.
657          * If pol has no domains, we don't really know about this device
658          * so we allow caller to choose:
659          * -1:  has no domains
660          *  0:  has domains, not all match
661          *  1:  has domains, all match
662          */
663         int found_any = -1;
664         struct dev_policy *p;
665
666         pol = pol_find(pol, pol_domain);
667         pol_for_each(p, pol, metadata) {
668                 found_any = 1;
669                 while (dom && strcmp(dom->dom, p->value) < 0)
670                         dom = dom->next;
671                 if (!dom || strcmp(dom->dom, p->value) != 0)
672                         return 0;
673         }
674         return found_any;
675 }
676
677 void domainlist_add_dev(struct domainlist **dom, int devid, const char *metadata)
678 {
679         struct dev_policy *pol = devid_policy(devid);
680         domain_merge(dom, pol, metadata);
681         dev_policy_free(pol);
682 }
683
684 struct domainlist *domain_from_array(struct mdinfo *mdi, const char *metadata)
685 {
686         struct domainlist *domlist = NULL;
687
688         if (!mdi)
689                 return NULL;
690         for (mdi = mdi->devs ; mdi ; mdi = mdi->next)
691                 domainlist_add_dev(&domlist, makedev(mdi->disk.major,
692                                                      mdi->disk.minor),
693                                    metadata);
694
695         return domlist;
696 }
697
698 void domain_add(struct domainlist **domp, char *domain)
699 {
700         domain_merge_one(domp, domain);
701 }
702
703 void domain_free(struct domainlist *dl)
704 {
705         while (dl) {
706                 struct domainlist *head = dl;
707                 dl = dl->next;
708                 free(head);
709         }
710 }
711
712 /*
713  * same-path policy.
714  * Some policy decisions are guided by knowledge of which
715  * array previously owned the device at a given physical location (path).
716  * When removing a device from an array we might record the array against
717  * the path, and when finding a new device, we might look for which
718  * array previously used that path.
719  *
720  * The 'array' is described by a map_ent, and the path by a the disk in an
721  * mdinfo, or a string.
722  */
723
724 void policy_save_path(char *id_path, struct map_ent *array)
725 {
726         char path[PATH_MAX];
727         FILE *f = NULL;
728
729         if (mkdir(FAILED_SLOTS_DIR, S_IRWXU) < 0 && errno != EEXIST) {
730                 pr_err("can't create file to save path to old disk: %s\n", strerror(errno));
731                 return;
732         }
733
734         snprintf(path, PATH_MAX, FAILED_SLOTS_DIR "/%s", id_path);
735         f = fopen(path, "w");
736         if (!f) {
737                 pr_err("can't create file to save path to old disk: %s\n",
738                         strerror(errno));
739                 return;
740         }
741
742         if (fprintf(f, "%s %08x:%08x:%08x:%08x\n",
743                     array->metadata,
744                     array->uuid[0], array->uuid[1],
745                     array->uuid[2], array->uuid[3]) <= 0)
746                 pr_err("Failed to write to <id_path> cookie\n");
747
748         fclose(f);
749 }
750
751 int policy_check_path(struct mdinfo *disk, struct map_ent *array)
752 {
753         char path[PATH_MAX];
754         FILE *f = NULL;
755         char *id_path = disk_path(disk);
756         int rv;
757
758         if (!id_path)
759                 return 0;
760
761         snprintf(path, PATH_MAX, FAILED_SLOTS_DIR "/%s", id_path);
762         f = fopen(path, "r");
763         if (!f) {
764                 free(id_path);
765                 return 0;
766         }
767
768         rv = fscanf(f, " %s %x:%x:%x:%x\n",
769                     array->metadata,
770                     array->uuid,
771                     array->uuid+1,
772                     array->uuid+2,
773                     array->uuid+3);
774         fclose(f);
775         free(id_path);
776         return rv == 5;
777 }
778
779 /* invocation of udev rule file */
780 char udev_template_start[] =
781 "# do not edit this file, it is automatically generated by mdadm\n"
782 "\n";
783
784 /* find rule named rule_type and return its value */
785 char *find_rule(struct rule *rule, char *rule_type)
786 {
787         while (rule) {
788                 if (rule->name == rule_type)
789                         return rule->value;
790
791                 rule = rule->next;
792         }
793         return NULL;
794 }
795
796 #define UDEV_RULE_FORMAT \
797 "ACTION==\"add\", SUBSYSTEM==\"block\", " \
798 "ENV{DEVTYPE}==\"%s\", ENV{ID_PATH}==\"%s\", " \
799 "RUN+=\"" BINDIR "/mdadm --incremental $env{DEVNAME}\"\n"
800
801 #define UDEV_RULE_FORMAT_NOTYPE \
802 "ACTION==\"add\", SUBSYSTEM==\"block\", " \
803 "ENV{ID_PATH}==\"%s\", " \
804 "RUN+=\"" BINDIR "/mdadm --incremental $env{DEVNAME}\"\n"
805
806 /* Write rule in the rule file. Use format from UDEV_RULE_FORMAT */
807 int write_rule(struct rule *rule, int fd, int force_part)
808 {
809         char line[1024];
810         char *pth = find_rule(rule, rule_path);
811         char *typ = find_rule(rule, rule_type);
812         if (!pth)
813                 return -1;
814
815         if (force_part)
816                 typ = type_part;
817         if (typ)
818                 snprintf(line, sizeof(line) - 1, UDEV_RULE_FORMAT, typ, pth);
819         else
820                 snprintf(line, sizeof(line) - 1, UDEV_RULE_FORMAT_NOTYPE, pth);
821         return write(fd, line, strlen(line)) == (int)strlen(line);
822 }
823
824 /* Generate single entry in udev rule basing on POLICY line found in config
825  * file. Take only those with paths, only first occurrence if paths are equal
826  * and if actions supports handling of spares (>=act_spare_same_slot)
827  */
828 int generate_entries(int fd)
829 {
830         struct pol_rule *loop, *dup;
831         char *loop_value, *dup_value;
832         int duplicate;
833
834         for (loop = config_rules; loop; loop = loop->next) {
835                 if (loop->type != rule_policy && loop->type != rule_part)
836                         continue;
837                 duplicate = 0;
838
839                 /* only policies with paths and with actions supporting
840                  * bare disks are considered */
841                 loop_value = find_rule(loop->rule, pol_act);
842                 if (!loop_value || map_act(loop_value) < act_spare_same_slot)
843                         continue;
844                 loop_value = find_rule(loop->rule, rule_path);
845                 if (!loop_value)
846                         continue;
847                 for (dup = config_rules; dup != loop; dup = dup->next) {
848                         if (dup->type != rule_policy && loop->type != rule_part)
849                                 continue;
850                         dup_value = find_rule(dup->rule, pol_act);
851                         if (!dup_value || map_act(dup_value) < act_spare_same_slot)
852                                 continue;
853                         dup_value = find_rule(dup->rule, rule_path);
854                         if (!dup_value)
855                                 continue;
856                         if (strcmp(loop_value, dup_value) == 0) {
857                                 duplicate = 1;
858                                 break;
859                         }
860                 }
861
862                 /* not a dup or first occurrence */
863                 if (!duplicate)
864                         if (!write_rule(loop->rule, fd, loop->type == rule_part) )
865                                 return 0;
866         }
867         return 1;
868 }
869
870 /* Write_rules routine creates dynamic udev rules used to handle
871  * hot-plug events for bare devices (and making them spares)
872  */
873 int Write_rules(char *rule_name)
874 {
875         int fd;
876         char udev_rule_file[PATH_MAX];
877
878         if (rule_name) {
879                 strncpy(udev_rule_file, rule_name, sizeof(udev_rule_file) - 6);
880                 udev_rule_file[sizeof(udev_rule_file) - 6] = '\0';
881                 strcat(udev_rule_file, ".temp");
882                 fd = creat(udev_rule_file,
883                            S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
884                 if (fd == -1)
885                         return 1;
886         } else
887                 fd = 1;
888
889         /* write static invocation */
890         if (write(fd, udev_template_start,
891                   sizeof(udev_template_start) - 1)
892             != (int)sizeof(udev_template_start)-1)
893                 goto abort;
894
895         /* iterate, if none created or error occurred, remove file */
896         if (generate_entries(fd) < 0)
897                 goto abort;
898
899         fsync(fd);
900         if (rule_name) {
901                 close(fd);
902                 rename(udev_rule_file, rule_name);
903         }
904         return 0;
905 abort:
906         if (rule_name) {
907                 close(fd);
908                 unlink(udev_rule_file);
909         }
910         return 1;
911 }