]> git.ipfire.org Git - thirdparty/mdadm.git/blame - policy.c
Create: support --readonly flag.
[thirdparty/mdadm.git] / policy.c
CommitLineData
5527fc74
N
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
2cda7640
ML
43static void pol_new(struct dev_policy **pol, char *name, const char *val,
44 const char *metadata)
5527fc74 45{
503975b9 46 struct dev_policy *n = xmalloc(sizeof(*n));
5527fc74
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) {
2cda7640 68 static const char *prev = NULL;
5527fc74 69 if (prev != metadata) {
e7b84f9d 70 pr_err("metadata=%s unrecognised - ignoring rule\n",
5527fc74
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
83static 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
101static 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])
37194990
N
141 ^pol_lesseq(prev, p[next])
142 ^pol_lesseq(p[next], p[1-next])))
5527fc74
N
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
162static 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 */
182struct 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
192static 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/";
b451aa48 198 char nm[PATH_MAX];
5527fc74 199 struct dirent *ent;
b451aa48 200 int rv;
5527fc74
N
201
202 by_path = opendir(symlink);
203 if (!by_path)
204 return NULL;
205 prefix_len = strlen(symlink);
206
207 while ((ent = readdir(by_path)) != NULL) {
208 if (ent->d_type != DT_LNK)
209 continue;
210 strncpy(symlink + prefix_len,
211 ent->d_name,
212 sizeof(symlink) - prefix_len);
213 if (stat(symlink, &stb) < 0)
214 continue;
215 if ((stb.st_mode & S_IFMT) != S_IFBLK)
216 continue;
217 if (stb.st_rdev != makedev(disk->disk.major, disk->disk.minor))
218 continue;
219 closedir(by_path);
503975b9 220 return xstrdup(ent->d_name);
5527fc74
N
221 }
222 closedir(by_path);
b451aa48
N
223 /* A NULL path isn't really acceptable - use the devname.. */
224 sprintf(symlink, "/sys/dev/block/%d:%d", disk->disk.major, disk->disk.minor);
9cf014ec 225 rv = readlink(symlink, nm, sizeof(nm)-1);
b451aa48
N
226 if (rv > 0) {
227 char *dname;
228 nm[rv] = 0;
229 dname = strrchr(nm, '/');
230 if (dname)
503975b9 231 return xstrdup(dname + 1);
b451aa48 232 }
503975b9 233 return xstrdup("unknown");
5527fc74
N
234}
235
236char type_part[] = "part";
237char type_disk[] = "disk";
238static char *disk_type(struct mdinfo *disk)
239{
240 char buf[30+20+20];
241 struct stat stb;
242 sprintf(buf, "/sys/dev/block/%d:%d/partition",
243 disk->disk.major, disk->disk.minor);
244 if (stat(buf, &stb) == 0)
245 return type_part;
246 else
247 return type_disk;
248}
249
250static int pol_match(struct rule *rule, char *path, char *type)
251{
252 /* check if this rule matches on path and type */
253 int pathok = 0; /* 0 == no path, 1 == match, -1 == no match yet */
254 int typeok = 0;
255
256 while (rule) {
257 if (rule->name == rule_path) {
258 if (pathok == 0)
259 pathok = -1;
b451aa48 260 if (path && fnmatch(rule->value, path, 0) == 0)
5527fc74
N
261 pathok = 1;
262 }
263 if (rule->name == rule_type) {
264 if (typeok == 0)
265 typeok = -1;
b451aa48 266 if (type && strcmp(rule->value, type) == 0)
5527fc74
N
267 typeok = 1;
268 }
269 rule = rule->next;
270 }
271 return pathok >= 0 && typeok >= 0;
272}
273
274static void pol_merge(struct dev_policy **pol, struct rule *rule)
275{
276 /* copy any name assignments from rule into pol */
277 struct rule *r;
278 char *metadata = NULL;
279 for (r = rule; r ; r = r->next)
280 if (r->name == pol_metadata)
281 metadata = r->value;
282
283 for (r = rule; r ; r = r->next)
284 if (r->name == pol_act ||
b451aa48
N
285 r->name == pol_domain ||
286 r->name == pol_auto)
5527fc74
N
287 pol_new(pol, r->name, r->value, metadata);
288}
289
290static int path_has_part(char *path, char **part)
291{
292 /* check if path ends with "-partNN" and
293 * if it does, place a pointer to "-pathNN"
294 * in 'part'.
295 */
b451aa48
N
296 int l;
297 if (!path)
298 return 0;
299 l = strlen(path);
5527fc74
N
300 while (l > 1 && isdigit(path[l-1]))
301 l--;
302 if (l < 5 || strncmp(path+l-5, "-part", 5) != 0)
303 return 0;
304 *part = path+l-4;
305 return 1;
306}
307
308static void pol_merge_part(struct dev_policy **pol, struct rule *rule, char *part)
309{
310 /* copy any name assignments from rule into pol, appending
311 * -part to any domain. The string with -part appended is
312 * stored with the rule so it has a lifetime to match
313 * the rule.
314 */
315 struct rule *r;
316 char *metadata = NULL;
317 for (r = rule; r ; r = r->next)
318 if (r->name == pol_metadata)
319 metadata = r->value;
320
321 for (r = rule; r ; r = r->next) {
322 if (r->name == pol_act)
323 pol_new(pol, r->name, r->value, metadata);
324 else if (r->name == pol_domain) {
325 char *dom;
326 int len;
327 if (r->dups == NULL)
328 r->dups = dl_head();
329 len = strlen(r->value);
330 for (dom = dl_next(r->dups); dom != r->dups;
331 dom = dl_next(dom))
332 if (strcmp(dom+len+1, part)== 0)
333 break;
334 if (dom == r->dups) {
335 char *newdom = dl_strndup(
336 r->value, len + 1 + strlen(part));
337 strcat(strcat(newdom, "-"), part);
338 dl_add(r->dups, newdom);
339 dom = newdom;
340 }
341 pol_new(pol, r->name, dom, metadata);
342 }
343 }
344}
345
346static struct pol_rule *config_rules = NULL;
347static struct pol_rule **config_rules_end = NULL;
348static int config_rules_has_path = 0;
349
350/*
351 * most policy comes from a set policy rules that are
352 * read from the config file.
73c9c47c
N
353 * path_policy() gathers policy information for the
354 * disk described in the given a 'path' and a 'type'.
5527fc74 355 */
73c9c47c 356struct dev_policy *path_policy(char *path, char *type)
5527fc74 357{
5527fc74
N
358 struct pol_rule *rules;
359 struct dev_policy *pol = NULL;
2cda7640 360 int i;
5527fc74 361
5527fc74
N
362 rules = config_rules;
363
364 while (rules) {
365 char *part;
366 if (rules->type == rule_policy)
367 if (pol_match(rules->rule, path, type))
368 pol_merge(&pol, rules->rule);
369 if (rules->type == rule_part && strcmp(type, type_part) == 0)
370 if (path_has_part(path, &part)) {
371 *part = 0;
372 if (pol_match(rules->rule, path, type_disk))
373 pol_merge_part(&pol, rules->rule, part+1);
374 *part = '-';
375 }
376 rules = rules->next;
377 }
2cda7640
ML
378
379 /* Now add any metadata-specific internal knowledge
380 * about this path
381 */
b451aa48 382 for (i=0; path && superlist[i]; i++)
2cda7640
ML
383 if (superlist[i]->get_disk_controller_domain) {
384 const char *d =
385 superlist[i]->get_disk_controller_domain(path);
386 if (d)
387 pol_new(&pol, pol_domain, d, superlist[i]->name);
388 }
389
5527fc74
N
390 pol_sort(&pol);
391 pol_dedup(pol);
73c9c47c
N
392 return pol;
393}
394
e78dda3b
N
395void pol_add(struct dev_policy **pol,
396 char *name, char *val,
397 char *metadata)
398{
399 pol_new(pol, name, val, metadata);
400 pol_sort(pol);
401 pol_dedup(*pol);
402}
403
404
73c9c47c
N
405/*
406 * disk_policy() gathers policy information for the
407 * disk described in the given mdinfo (disk.{major,minor}).
408 */
409struct dev_policy *disk_policy(struct mdinfo *disk)
410{
411 char *path = NULL;
412 char *type = disk_type(disk);
413 struct dev_policy *pol = NULL;
414
b9b004eb 415 if (config_rules_has_path)
73c9c47c 416 path = disk_path(disk);
73c9c47c
N
417
418 pol = path_policy(path, type);
419
5527fc74
N
420 free(path);
421 return pol;
422}
423
4e8d9f0a
N
424struct dev_policy *devnum_policy(int dev)
425{
426 struct mdinfo disk;
427 disk.disk.major = major(dev);
428 disk.disk.minor = minor(dev);
429 return disk_policy(&disk);
430}
431
5527fc74
N
432/*
433 * process policy rules read from config file.
434 */
435
436char rule_path[] = "path";
437char rule_type[] = "type";
438
439char rule_policy[] = "policy";
440char rule_part[] = "part-policy";
441
442char pol_metadata[] = "metadata";
443char pol_act[] = "action";
444char pol_domain[] = "domain";
4e8d9f0a 445char pol_auto[] = "auto";
5527fc74
N
446
447static int try_rule(char *w, char *name, struct rule **rp)
448{
449 struct rule *r;
450 int len = strlen(name);
451 if (strncmp(w, name, len) != 0 ||
452 w[len] != '=')
453 return 0;
503975b9 454 r = xmalloc(sizeof(*r));
5527fc74
N
455 r->next = *rp;
456 r->name = name;
503975b9 457 r->value = xstrdup(w+len+1);
5527fc74
N
458 r->dups = NULL;
459 *rp = r;
460 return 1;
461}
462
463void policyline(char *line, char *type)
464{
465 struct pol_rule *pr;
466 char *w;
467
468 if (config_rules_end == NULL)
469 config_rules_end = &config_rules;
470
503975b9 471 pr = xmalloc(sizeof(*pr));
5527fc74
N
472 pr->type = type;
473 pr->rule = NULL;
474 for (w = dl_next(line); w != line ; w = dl_next(w)) {
475 if (try_rule(w, rule_path, &pr->rule))
476 config_rules_has_path = 1;
477 else if (! try_rule(w, rule_type, &pr->rule) &&
478 ! try_rule(w, pol_metadata, &pr->rule) &&
479 ! try_rule(w, pol_act, &pr->rule) &&
4e8d9f0a
N
480 ! try_rule(w, pol_domain, &pr->rule) &&
481 ! try_rule(w, pol_auto, &pr->rule))
e7b84f9d 482 pr_err("policy rule %s unrecognised and ignored\n",
5527fc74
N
483 w);
484 }
485 pr->next = config_rules;
486 config_rules = pr;
487}
488
4e8d9f0a
N
489void policy_add(char *type, ...)
490{
491 va_list ap;
492 struct pol_rule *pr;
493 char *name, *val;
494
503975b9 495 pr = xmalloc(sizeof(*pr));
4e8d9f0a
N
496 pr->type = type;
497 pr->rule = NULL;
498
499 va_start(ap, type);
500 while ((name = va_arg(ap, char*)) != NULL) {
501 struct rule *r;
502
503 val = va_arg(ap, char*);
503975b9 504 r = xmalloc(sizeof(*r));
4e8d9f0a
N
505 r->next = pr->rule;
506 r->name = name;
503975b9 507 r->value = xstrdup(val);
4e8d9f0a
N
508 r->dups = NULL;
509 pr->rule = r;
510 }
511 pr->next = config_rules;
512 config_rules = pr;
a5cd79fe 513 va_end(ap);
4e8d9f0a
N
514}
515
5527fc74
N
516void policy_free(void)
517{
518 while (config_rules) {
519 struct pol_rule *pr = config_rules;
520 struct rule *r;
521
522 config_rules = config_rules->next;
523
524 for (r = pr->rule; r; ) {
525 struct rule *next = r->next;
526 free(r->value);
527 if (r->dups)
528 free_line(r->dups);
529 free(r);
530 r = next;
531 }
532 free(pr);
533 }
534 config_rules_end = NULL;
535 config_rules_has_path = 0;
536}
537
538void dev_policy_free(struct dev_policy *p)
539{
540 struct dev_policy *t;
541 while (p) {
542 t = p;
543 p = p->next;
544 free(t);
545 }
546}
e3bb5f14 547
2cda7640 548static enum policy_action map_act(const char *act)
e3bb5f14
N
549{
550 if (strcmp(act, "include") == 0)
551 return act_include;
552 if (strcmp(act, "re-add") == 0)
553 return act_re_add;
554 if (strcmp(act, "spare") == 0)
555 return act_spare;
d2db3045
N
556 if (strcmp(act, "spare-same-slot") == 0)
557 return act_spare_same_slot;
e3bb5f14
N
558 if (strcmp(act, "force-spare") == 0)
559 return act_force_spare;
560 return act_err;
561}
562
563static enum policy_action policy_action(struct dev_policy *plist, const char *metadata)
564{
565 enum policy_action rv = act_default;
566 struct dev_policy *p;
567
568 plist = pol_find(plist, pol_act);
569 pol_for_each(p, plist, metadata) {
570 enum policy_action a = map_act(p->value);
571 if (a > rv)
572 rv = a;
573 }
574 return rv;
575}
576
577int policy_action_allows(struct dev_policy *plist, const char *metadata, enum policy_action want)
578{
579 enum policy_action act = policy_action(plist, metadata);
580
581 if (act == act_err)
582 return 0;
583 return (act >= want);
584}
585
586int disk_action_allows(struct mdinfo *disk, const char *metadata, enum policy_action want)
587{
588 struct dev_policy *pol = disk_policy(disk);
589 int rv = policy_action_allows(pol, metadata, want);
590
591 dev_policy_free(pol);
592 return rv;
593}
f5f12c84
N
594
595
596/* Domain policy:
597 * Any device can have a list of domains asserted by different policy
598 * statements.
599 * An array also has a list of domains comprising all the domains of
600 * all the devices in an array.
601 * Where an array has a spare-group, that becomes an addition domain for
602 * every device in the array and thus for the array.
603 *
604 * We keep the list of domains in a sorted linked list
605 * As dev policies are already sorted, this is fairly easy to manage.
606 */
607
2cda7640
ML
608static struct domainlist **domain_merge_one(struct domainlist **domp,
609 const char *domain)
f5f12c84
N
610{
611 /* merge a domain name into a sorted list and return the
612 * location of the insertion or match
613 */
614 struct domainlist *dom = *domp;
615
616 while (dom && strcmp(dom->dom, domain) < 0) {
617 domp = &dom->next;
618 dom = *domp;
619 }
620 if (dom == NULL || strcmp(dom->dom, domain) != 0) {
503975b9 621 dom = xmalloc(sizeof(*dom));
f5f12c84
N
622 dom->next = *domp;
623 dom->dom = domain;
624 *domp = dom;
625 }
626 return domp;
627}
628
2cda7640
ML
629#if (DEBUG)
630void dump_policy(struct dev_policy *policy)
631{
632 while (policy) {
633 dprintf("policy: %p name: %s value: %s metadata: %s\n",
634 policy,
635 policy->name,
636 policy->value,
637 policy->metadata);
638 policy = policy->next;
639 }
640}
641#endif
642
f5f12c84
N
643void domain_merge(struct domainlist **domp, struct dev_policy *pollist,
644 const char *metadata)
645{
646 /* Add to 'domp' all the domains in pol that apply to 'metadata'
647 * which are not already in domp
648 */
649 struct dev_policy *pol;
650 pollist = pol_find(pollist, pol_domain);
651 pol_for_each(pol, pollist, metadata)
e78dda3b 652 domain_merge_one(domp, pol->value);
f5f12c84
N
653}
654
655int domain_test(struct domainlist *dom, struct dev_policy *pol,
656 const char *metadata)
657{
658 /* Check that all domains in pol (for metadata) are also in
659 * dom. Both lists are sorted.
660 * If pol has no domains, we don't really know about this device
e5508b36
N
661 * so we allow caller to choose:
662 * -1: has no domains
663 * 0: has domains, not all match
664 * 1: has domains, all match
f5f12c84 665 */
e5508b36 666 int found_any = -1;
f5f12c84
N
667 struct dev_policy *p;
668
669 pol = pol_find(pol, pol_domain);
670 pol_for_each(p, pol, metadata) {
671 found_any = 1;
4e8d9f0a 672 while (dom && strcmp(dom->dom, p->value) < 0)
f5f12c84 673 dom = dom->next;
4e8d9f0a 674 if (!dom || strcmp(dom->dom, p->value) != 0)
f5f12c84
N
675 return 0;
676 }
677 return found_any;
678}
679
e78dda3b
N
680void domainlist_add_dev(struct domainlist **dom, int devnum, const char *metadata)
681{
682 struct dev_policy *pol = devnum_policy(devnum);
683 domain_merge(dom, pol, metadata);
684 dev_policy_free(pol);
685}
686
f5f12c84
N
687struct domainlist *domain_from_array(struct mdinfo *mdi, const char *metadata)
688{
689 struct domainlist *domlist = NULL;
690
75c2df65
N
691 if (!mdi)
692 return NULL;
e78dda3b
N
693 for (mdi = mdi->devs ; mdi ; mdi = mdi->next)
694 domainlist_add_dev(&domlist, makedev(mdi->disk.major,
695 mdi->disk.minor),
696 metadata);
f5f12c84 697
f5f12c84
N
698 return domlist;
699}
700
e78dda3b
N
701void domain_add(struct domainlist **domp, char *domain)
702{
703 domain_merge_one(domp, domain);
704}
705
706
f5f12c84
N
707void domain_free(struct domainlist *dl)
708{
709 while (dl) {
710 struct domainlist *head = dl;
711 dl = dl->next;
712 free(head);
713 }
714}
403410eb
PC
715
716/*
717 * same-path policy.
718 * Some policy decisions are guided by knowledge of which
719 * array previously owned the device at a given physical location (path).
720 * When removing a device from an array we might record the array against
721 * the path, and when finding a new device, we might look for which
722 * array previously used that path.
723 *
724 * The 'array' is described by a map_ent, and the path by a the disk in an
725 * mdinfo, or a string.
726 */
727
728void policy_save_path(char *id_path, struct map_ent *array)
729{
730 char path[PATH_MAX];
731 FILE *f = NULL;
732
733 if (mkdir(FAILED_SLOTS_DIR, S_IRWXU) < 0 && errno != EEXIST) {
e7b84f9d 734 pr_err("can't create file to save path "
403410eb
PC
735 "to old disk: %s\n", strerror(errno));
736 return;
737 }
738
739 snprintf(path, PATH_MAX, FAILED_SLOTS_DIR "/%s", id_path);
740 f = fopen(path, "w");
741 if (!f) {
e7b84f9d 742 pr_err("can't create file to"
403410eb
PC
743 " save path to old disk: %s\n",
744 strerror(errno));
745 return;
746 }
747
748 if (fprintf(f, "%s %08x:%08x:%08x:%08x\n",
749 array->metadata,
750 array->uuid[0], array->uuid[1],
751 array->uuid[2], array->uuid[3]) <= 0)
e7b84f9d
N
752 pr_err("Failed to write to "
753 "<id_path> cookie\n");
403410eb
PC
754
755 fclose(f);
756}
757
758int policy_check_path(struct mdinfo *disk, struct map_ent *array)
759{
760 char path[PATH_MAX];
761 FILE *f = NULL;
762 char *id_path = disk_path(disk);
763 int rv;
764
765 if (!id_path)
766 return 0;
767
768 snprintf(path, PATH_MAX, FAILED_SLOTS_DIR "/%s", id_path);
769 f = fopen(path, "r");
e3da7890
TJ
770 if (!f) {
771 free(id_path);
403410eb 772 return 0;
e3da7890 773 }
403410eb
PC
774
775 rv = fscanf(f, " %s %x:%x:%x:%x\n",
776 array->metadata,
777 array->uuid,
778 array->uuid+1,
779 array->uuid+2,
780 array->uuid+3);
781 fclose(f);
e3da7890 782 free(id_path);
403410eb
PC
783 return rv == 5;
784}
20b60dcd
LM
785
786/* invocation of udev rule file */
787char udev_template_start[] =
788"# do not edit this file, it is automatically generated by mdadm\n"
789"\n";
790
791/* find rule named rule_type and return its value */
792char *find_rule(struct rule *rule, char *rule_type)
793{
794 while (rule) {
795 if (rule->name == rule_type)
796 return rule->value;
797
798 rule = rule->next;
799 }
800 return NULL;
801}
802
803#define UDEV_RULE_FORMAT \
c8826c3e 804"ACTION==\"add\", SUBSYSTEM==\"block\", " \
20b60dcd 805"ENV{DEVTYPE}==\"%s\", ENV{ID_PATH}==\"%s\", " \
c8826c3e 806"RUN+=\"/sbin/mdadm --incremental $env{DEVNAME}\"\n"
20b60dcd
LM
807
808#define UDEV_RULE_FORMAT_NOTYPE \
c8826c3e 809"ACTION==\"add\", SUBSYSTEM==\"block\", " \
20b60dcd 810"ENV{ID_PATH}==\"%s\", " \
c8826c3e 811"RUN+=\"/sbin/mdadm --incremental $env{DEVNAME}\"\n"
20b60dcd
LM
812
813/* Write rule in the rule file. Use format from UDEV_RULE_FORMAT */
814int write_rule(struct rule *rule, int fd, int force_part)
815{
816 char line[1024];
817 char *pth = find_rule(rule, rule_path);
818 char *typ = find_rule(rule, rule_type);
819 if (!pth)
820 return -1;
821
822 if (force_part)
823 typ = type_part;
824 if (typ)
825 snprintf(line, sizeof(line) - 1, UDEV_RULE_FORMAT, typ, pth);
826 else
827 snprintf(line, sizeof(line) - 1, UDEV_RULE_FORMAT_NOTYPE, pth);
828 return write(fd, line, strlen(line)) == (int)strlen(line);
829}
830
831/* Generate single entry in udev rule basing on POLICY line found in config
832 * file. Take only those with paths, only first occurrence if paths are equal
833 * and if actions supports handling of spares (>=act_spare_same_slot)
834 */
835int generate_entries(int fd)
836{
837 struct pol_rule *loop, *dup;
838 char *loop_value, *dup_value;
839 int duplicate;
840
841 for (loop = config_rules; loop; loop = loop->next) {
842 if (loop->type != rule_policy && loop->type != rule_part)
843 continue;
844 duplicate = 0;
845
846 /* only policies with paths and with actions supporting
847 * bare disks are considered */
848 loop_value = find_rule(loop->rule, pol_act);
849 if (!loop_value || map_act(loop_value) < act_spare_same_slot)
850 continue;
851 loop_value = find_rule(loop->rule, rule_path);
852 if (!loop_value)
853 continue;
854 for (dup = config_rules; dup != loop; dup = dup->next) {
855 if (dup->type != rule_policy && loop->type != rule_part)
856 continue;
857 dup_value = find_rule(dup->rule, pol_act);
858 if (!dup_value || map_act(dup_value) < act_spare_same_slot)
859 continue;
860 dup_value = find_rule(dup->rule, rule_path);
861 if (!dup_value)
862 continue;
863 if (strcmp(loop_value, dup_value) == 0) {
864 duplicate = 1;
865 break;
866 }
867 }
868
869 /* not a dup or first occurrence */
870 if (!duplicate)
871 if (!write_rule(loop->rule, fd, loop->type == rule_part) )
872 return 0;
873 }
874 return 1;
875}
876
877/* Write_rules routine creates dynamic udev rules used to handle
878 * hot-plug events for bare devices (and making them spares)
879 */
880int Write_rules(char *rule_name)
881{
882 int fd;
883 char udev_rule_file[PATH_MAX];
884
885 if (rule_name) {
2b710bac
JS
886 strncpy(udev_rule_file, rule_name, sizeof(udev_rule_file) - 6);
887 udev_rule_file[sizeof(udev_rule_file) - 6] = '\0';
20b60dcd
LM
888 strcat(udev_rule_file, ".temp");
889 fd = creat(udev_rule_file,
890 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
891 if (fd == -1)
892 return 1;
893 } else
894 fd = 1;
895
896 /* write static invocation */
897 if (write(fd, udev_template_start,
898 sizeof(udev_template_start) - 1)
899 != (int)sizeof(udev_template_start)-1)
900 goto abort;
901
902 /* iterate, if none created or error occurred, remove file */
903 if (generate_entries(fd) < 0)
904 goto abort;
905
906 fsync(fd);
907 if (rule_name) {
908 close(fd);
909 rename(udev_rule_file, rule_name);
910 }
911 return 0;
912abort:
913 if (rule_name) {
914 close(fd);
915 unlink(udev_rule_file);
916 }
917 return 1;
918}