]> git.ipfire.org Git - thirdparty/mdadm.git/blob - policy.c
Monitor: policy based spare migration.
[thirdparty/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, char *val, char *metadata)
44 {
45 struct dev_policy *n = malloc(sizeof(*n));
46 const char *real_metadata = NULL;
47 int i;
48
49 n->name = name;
50 n->value = val;
51
52 /* We need to normalise the metadata name */
53 if (metadata) {
54 for (i = 0; superlist[i] ; i++)
55 if (strcmp(metadata, superlist[i]->name) == 0) {
56 real_metadata = superlist[i]->name;
57 break;
58 }
59 if (!real_metadata) {
60 if (strcmp(metadata, "1") == 0 ||
61 strcmp(metadata, "1.0") == 0 ||
62 strcmp(metadata, "1.1") == 0 ||
63 strcmp(metadata, "1.2") == 0)
64 real_metadata = super1.name;
65 }
66 if (!real_metadata) {
67 static char *prev = NULL;
68 if (prev != metadata) {
69 fprintf(stderr, Name ": metadata=%s unrecognised - ignoring rule\n",
70 metadata);
71 prev = metadata;
72 }
73 real_metadata = "unknown";
74 }
75 }
76
77 n->metadata = real_metadata;
78 n->next = *pol;
79 *pol = n;
80 }
81
82 static int pol_lesseq(struct dev_policy *a, struct dev_policy *b)
83 {
84 int cmp;
85
86 if (a->name < b->name)
87 return 1;
88 if (a->name > b->name)
89 return 0;
90
91 cmp = strcmp(a->value, b->value);
92 if (cmp < 0)
93 return 1;
94 if (cmp > 0)
95 return 0;
96
97 return (a->metadata <= b->metadata);
98 }
99
100 static void pol_sort(struct dev_policy **pol)
101 {
102 /* sort policy list in *pol by name/metadata/value
103 * using merge sort
104 */
105
106 struct dev_policy *pl[2];
107 pl[0] = *pol;
108 pl[1] = NULL;
109
110 do {
111 struct dev_policy **plp[2], *p[2];
112 int curr = 0;
113 struct dev_policy nul = { NULL, NULL, NULL, NULL };
114 struct dev_policy *prev = &nul;
115 int next = 0;
116
117 /* p[] are the two lists that we are merging.
118 * plp[] are the ends of the two lists we create
119 * from the merge.
120 * 'curr' is which of plp[] that we are currently
121 * adding items to.
122 * 'next' is which if p[] we will take the next
123 * item from.
124 * 'prev' is that last value, which was placed in
125 * plp[curr].
126 */
127 plp[0] = &pl[0];
128 plp[1] = &pl[1];
129 p[0] = pl[0];
130 p[1] = pl[1];
131
132 /* take least of p[0] and p[1]
133 * if it is larger than prev, add to
134 * plp[curr], else swap curr then add
135 */
136 while (p[0] || p[1]) {
137 if (p[next] == NULL ||
138 (p[1-next] != NULL &&
139 !(pol_lesseq(prev, p[1-next])
140 ^pol_lesseq(prev, p[next])
141 ^pol_lesseq(p[next], p[1-next])))
142 )
143 next = 1 - next;
144
145 if (!pol_lesseq(prev, p[next]))
146 curr = 1 - curr;
147
148 *plp[curr] = prev = p[next];
149 plp[curr] = &p[next]->next;
150 p[next] = p[next]->next;
151 }
152 *plp[0] = NULL;
153 *plp[1] = NULL;
154 } while (pl[0] && pl[1]);
155 if (pl[0])
156 *pol = pl[0];
157 else
158 *pol = pl[1];
159 }
160
161 static void pol_dedup(struct dev_policy *pol)
162 {
163 /* This is a sorted list - remove duplicates. */
164 while (pol && pol->next) {
165 if (pol_lesseq(pol->next, pol)) {
166 struct dev_policy *tmp = pol->next;
167 pol->next = tmp->next;
168 free(tmp);
169 } else
170 pol = pol->next;
171 }
172 }
173
174 /*
175 * pol_find finds the first entry in the policy
176 * list to match name.
177 * If it returns non-NULL there is at least one
178 * value, but how many can only be found by
179 * iterating through the list.
180 */
181 struct dev_policy *pol_find(struct dev_policy *pol, char *name)
182 {
183 while (pol && pol->name < name)
184 pol = pol->next;
185
186 if (!pol || pol->name != name)
187 return NULL;
188 return pol;
189 }
190
191 static char *disk_path(struct mdinfo *disk)
192 {
193 struct stat stb;
194 int prefix_len;
195 DIR *by_path;
196 char symlink[PATH_MAX] = "/dev/disk/by-path/";
197 struct dirent *ent;
198
199 by_path = opendir(symlink);
200 if (!by_path)
201 return NULL;
202 prefix_len = strlen(symlink);
203
204 while ((ent = readdir(by_path)) != NULL) {
205 if (ent->d_type != DT_LNK)
206 continue;
207 strncpy(symlink + prefix_len,
208 ent->d_name,
209 sizeof(symlink) - prefix_len);
210 if (stat(symlink, &stb) < 0)
211 continue;
212 if ((stb.st_mode & S_IFMT) != S_IFBLK)
213 continue;
214 if (stb.st_rdev != makedev(disk->disk.major, disk->disk.minor))
215 continue;
216 closedir(by_path);
217 return strdup(ent->d_name);
218 }
219 closedir(by_path);
220 return NULL;
221 }
222
223 char type_part[] = "part";
224 char type_disk[] = "disk";
225 static char *disk_type(struct mdinfo *disk)
226 {
227 char buf[30+20+20];
228 struct stat stb;
229 sprintf(buf, "/sys/dev/block/%d:%d/partition",
230 disk->disk.major, disk->disk.minor);
231 if (stat(buf, &stb) == 0)
232 return type_part;
233 else
234 return type_disk;
235 }
236
237 static int pol_match(struct rule *rule, char *path, char *type)
238 {
239 /* check if this rule matches on path and type */
240 int pathok = 0; /* 0 == no path, 1 == match, -1 == no match yet */
241 int typeok = 0;
242
243 while (rule) {
244 if (rule->name == rule_path) {
245 if (pathok == 0)
246 pathok = -1;
247 if (fnmatch(rule->value, path, 0) == 0)
248 pathok = 1;
249 }
250 if (rule->name == rule_type) {
251 if (typeok == 0)
252 typeok = -1;
253 if (strcmp(rule->value, type) == 0)
254 typeok = 1;
255 }
256 rule = rule->next;
257 }
258 return pathok >= 0 && typeok >= 0;
259 }
260
261 static void pol_merge(struct dev_policy **pol, struct rule *rule)
262 {
263 /* copy any name assignments from rule into pol */
264 struct rule *r;
265 char *metadata = NULL;
266 for (r = rule; r ; r = r->next)
267 if (r->name == pol_metadata)
268 metadata = r->value;
269
270 for (r = rule; r ; r = r->next)
271 if (r->name == pol_act ||
272 r->name == pol_domain)
273 pol_new(pol, r->name, r->value, metadata);
274 }
275
276 static int path_has_part(char *path, char **part)
277 {
278 /* check if path ends with "-partNN" and
279 * if it does, place a pointer to "-pathNN"
280 * in 'part'.
281 */
282 int l = strlen(path);
283 while (l > 1 && isdigit(path[l-1]))
284 l--;
285 if (l < 5 || strncmp(path+l-5, "-part", 5) != 0)
286 return 0;
287 *part = path+l-4;
288 return 1;
289 }
290
291 static void pol_merge_part(struct dev_policy **pol, struct rule *rule, char *part)
292 {
293 /* copy any name assignments from rule into pol, appending
294 * -part to any domain. The string with -part appended is
295 * stored with the rule so it has a lifetime to match
296 * the rule.
297 */
298 struct rule *r;
299 char *metadata = NULL;
300 for (r = rule; r ; r = r->next)
301 if (r->name == pol_metadata)
302 metadata = r->value;
303
304 for (r = rule; r ; r = r->next) {
305 if (r->name == pol_act)
306 pol_new(pol, r->name, r->value, metadata);
307 else if (r->name == pol_domain) {
308 char *dom;
309 int len;
310 if (r->dups == NULL)
311 r->dups = dl_head();
312 len = strlen(r->value);
313 for (dom = dl_next(r->dups); dom != r->dups;
314 dom = dl_next(dom))
315 if (strcmp(dom+len+1, part)== 0)
316 break;
317 if (dom == r->dups) {
318 char *newdom = dl_strndup(
319 r->value, len + 1 + strlen(part));
320 strcat(strcat(newdom, "-"), part);
321 dl_add(r->dups, newdom);
322 dom = newdom;
323 }
324 pol_new(pol, r->name, dom, metadata);
325 }
326 }
327 }
328
329 static struct pol_rule *config_rules = NULL;
330 static struct pol_rule **config_rules_end = NULL;
331 static int config_rules_has_path = 0;
332
333 /*
334 * most policy comes from a set policy rules that are
335 * read from the config file.
336 * path_policy() gathers policy information for the
337 * disk described in the given a 'path' and a 'type'.
338 */
339 struct dev_policy *path_policy(char *path, char *type)
340 {
341 struct pol_rule *rules;
342 struct dev_policy *pol = NULL;
343
344 if (!type)
345 return NULL;
346
347 rules = config_rules;
348
349 while (rules) {
350 char *part;
351 if (rules->type == rule_policy)
352 if (pol_match(rules->rule, path, type))
353 pol_merge(&pol, rules->rule);
354 if (rules->type == rule_part && strcmp(type, type_part) == 0)
355 if (path_has_part(path, &part)) {
356 *part = 0;
357 if (pol_match(rules->rule, path, type_disk))
358 pol_merge_part(&pol, rules->rule, part+1);
359 *part = '-';
360 }
361 rules = rules->next;
362 }
363 pol_sort(&pol);
364 pol_dedup(pol);
365 return pol;
366 }
367
368 void pol_add(struct dev_policy **pol,
369 char *name, char *val,
370 char *metadata)
371 {
372 pol_new(pol, name, val, metadata);
373 pol_sort(pol);
374 pol_dedup(*pol);
375 }
376
377
378 /*
379 * disk_policy() gathers policy information for the
380 * disk described in the given mdinfo (disk.{major,minor}).
381 */
382 struct dev_policy *disk_policy(struct mdinfo *disk)
383 {
384 char *path = NULL;
385 char *type = disk_type(disk);
386 struct dev_policy *pol = NULL;
387
388 if (!type)
389 return NULL;
390 if (config_rules_has_path)
391 path = disk_path(disk);
392 if (!path)
393 return NULL;
394
395 pol = path_policy(path, type);
396
397 free(path);
398 return pol;
399 }
400
401 struct dev_policy *devnum_policy(int dev)
402 {
403 struct mdinfo disk;
404 disk.disk.major = major(dev);
405 disk.disk.minor = minor(dev);
406 return disk_policy(&disk);
407 }
408
409 /*
410 * process policy rules read from config file.
411 */
412
413 char rule_path[] = "path";
414 char rule_type[] = "type";
415
416 char rule_policy[] = "policy";
417 char rule_part[] = "part-policy";
418
419 char pol_metadata[] = "metadata";
420 char pol_act[] = "action";
421 char pol_domain[] = "domain";
422 char pol_auto[] = "auto";
423
424 static int try_rule(char *w, char *name, struct rule **rp)
425 {
426 struct rule *r;
427 int len = strlen(name);
428 if (strncmp(w, name, len) != 0 ||
429 w[len] != '=')
430 return 0;
431 r = malloc(sizeof(*r));
432 r->next = *rp;
433 r->name = name;
434 r->value = strdup(w+len+1);
435 r->dups = NULL;
436 *rp = r;
437 return 1;
438 }
439
440 void policyline(char *line, char *type)
441 {
442 struct pol_rule *pr;
443 char *w;
444
445 if (config_rules_end == NULL)
446 config_rules_end = &config_rules;
447
448 pr = malloc(sizeof(*pr));
449 pr->type = type;
450 pr->rule = NULL;
451 for (w = dl_next(line); w != line ; w = dl_next(w)) {
452 if (try_rule(w, rule_path, &pr->rule))
453 config_rules_has_path = 1;
454 else if (! try_rule(w, rule_type, &pr->rule) &&
455 ! try_rule(w, pol_metadata, &pr->rule) &&
456 ! try_rule(w, pol_act, &pr->rule) &&
457 ! try_rule(w, pol_domain, &pr->rule) &&
458 ! try_rule(w, pol_auto, &pr->rule))
459 fprintf(stderr, Name ": policy rule %s unrecognised and ignored\n",
460 w);
461 }
462 pr->next = config_rules;
463 config_rules = pr;
464 }
465
466 void policy_add(char *type, ...)
467 {
468 va_list ap;
469 struct pol_rule *pr;
470 char *name, *val;
471
472 pr = malloc(sizeof(*pr));
473 pr->type = type;
474 pr->rule = NULL;
475
476 va_start(ap, type);
477 while ((name = va_arg(ap, char*)) != NULL) {
478 struct rule *r;
479
480 val = va_arg(ap, char*);
481 r = malloc(sizeof(*r));
482 r->next = pr->rule;
483 r->name = name;
484 r->value = strdup(val);
485 r->dups = NULL;
486 pr->rule = r;
487 }
488 pr->next = config_rules;
489 config_rules = pr;
490 }
491
492 void policy_free(void)
493 {
494 while (config_rules) {
495 struct pol_rule *pr = config_rules;
496 struct rule *r;
497
498 config_rules = config_rules->next;
499
500 for (r = pr->rule; r; ) {
501 struct rule *next = r->next;
502 free(r->value);
503 if (r->dups)
504 free_line(r->dups);
505 free(r);
506 r = next;
507 }
508 free(pr);
509 }
510 config_rules_end = NULL;
511 config_rules_has_path = 0;
512 }
513
514 void dev_policy_free(struct dev_policy *p)
515 {
516 struct dev_policy *t;
517 while (p) {
518 t = p;
519 p = p->next;
520 free(t);
521 }
522 }
523
524 static enum policy_action map_act(char *act)
525 {
526 if (strcmp(act, "include") == 0)
527 return act_include;
528 if (strcmp(act, "re-add") == 0)
529 return act_re_add;
530 if (strcmp(act, "spare") == 0)
531 return act_spare;
532 if (strcmp(act, "spare-same-slot") == 0)
533 return act_spare_same_slot;
534 if (strcmp(act, "force-spare") == 0)
535 return act_force_spare;
536 return act_err;
537 }
538
539 static enum policy_action policy_action(struct dev_policy *plist, const char *metadata)
540 {
541 enum policy_action rv = act_default;
542 struct dev_policy *p;
543
544 plist = pol_find(plist, pol_act);
545 pol_for_each(p, plist, metadata) {
546 enum policy_action a = map_act(p->value);
547 if (a > rv)
548 rv = a;
549 }
550 return rv;
551 }
552
553 int policy_action_allows(struct dev_policy *plist, const char *metadata, enum policy_action want)
554 {
555 enum policy_action act = policy_action(plist, metadata);
556
557 if (act == act_err)
558 return 0;
559 return (act >= want);
560 }
561
562 int disk_action_allows(struct mdinfo *disk, const char *metadata, enum policy_action want)
563 {
564 struct dev_policy *pol = disk_policy(disk);
565 int rv = policy_action_allows(pol, metadata, want);
566
567 dev_policy_free(pol);
568 return rv;
569 }
570
571
572 /* Domain policy:
573 * Any device can have a list of domains asserted by different policy
574 * statements.
575 * An array also has a list of domains comprising all the domains of
576 * all the devices in an array.
577 * Where an array has a spare-group, that becomes an addition domain for
578 * every device in the array and thus for the array.
579 *
580 * We keep the list of domains in a sorted linked list
581 * As dev policies are already sorted, this is fairly easy to manage.
582 */
583
584 static struct domainlist **domain_merge_one(struct domainlist **domp, char *domain)
585 {
586 /* merge a domain name into a sorted list and return the
587 * location of the insertion or match
588 */
589 struct domainlist *dom = *domp;
590
591 while (dom && strcmp(dom->dom, domain) < 0) {
592 domp = &dom->next;
593 dom = *domp;
594 }
595 if (dom == NULL || strcmp(dom->dom, domain) != 0) {
596 dom = malloc(sizeof(*dom));
597 dom->next = *domp;
598 dom->dom = domain;
599 *domp = dom;
600 }
601 return domp;
602 }
603
604 void domain_merge(struct domainlist **domp, struct dev_policy *pollist,
605 const char *metadata)
606 {
607 /* Add to 'domp' all the domains in pol that apply to 'metadata'
608 * which are not already in domp
609 */
610 struct dev_policy *pol;
611 pollist = pol_find(pollist, pol_domain);
612 pol_for_each(pol, pollist, metadata)
613 domain_merge_one(domp, pol->value);
614 }
615
616 int domain_test(struct domainlist *dom, struct dev_policy *pol,
617 const char *metadata)
618 {
619 /* Check that all domains in pol (for metadata) are also in
620 * dom. Both lists are sorted.
621 * If pol has no domains, we don't really know about this device
622 * so we reject the match.
623 */
624 int found_any = 0;
625 struct dev_policy *p;
626
627 pol = pol_find(pol, pol_domain);
628 pol_for_each(p, pol, metadata) {
629 found_any = 1;
630 while (dom && strcmp(dom->dom, p->value) < 0)
631 dom = dom->next;
632 if (!dom || strcmp(dom->dom, p->value) != 0)
633 return 0;
634 }
635 return found_any;
636 }
637
638 void domainlist_add_dev(struct domainlist **dom, int devnum, const char *metadata)
639 {
640 struct dev_policy *pol = devnum_policy(devnum);
641 domain_merge(dom, pol, metadata);
642 dev_policy_free(pol);
643 }
644
645 struct domainlist *domain_from_array(struct mdinfo *mdi, const char *metadata)
646 {
647 struct domainlist *domlist = NULL;
648
649 for (mdi = mdi->devs ; mdi ; mdi = mdi->next)
650 domainlist_add_dev(&domlist, makedev(mdi->disk.major,
651 mdi->disk.minor),
652 metadata);
653
654 return domlist;
655 }
656
657 void domain_add(struct domainlist **domp, char *domain)
658 {
659 domain_merge_one(domp, domain);
660 }
661
662
663 void domain_free(struct domainlist *dl)
664 {
665 while (dl) {
666 struct domainlist *head = dl;
667 dl = dl->next;
668 free(head);
669 }
670 }
671
672 /*
673 * same-path policy.
674 * Some policy decisions are guided by knowledge of which
675 * array previously owned the device at a given physical location (path).
676 * When removing a device from an array we might record the array against
677 * the path, and when finding a new device, we might look for which
678 * array previously used that path.
679 *
680 * The 'array' is described by a map_ent, and the path by a the disk in an
681 * mdinfo, or a string.
682 */
683
684 void policy_save_path(char *id_path, struct map_ent *array)
685 {
686 char path[PATH_MAX];
687 FILE *f = NULL;
688
689 if (mkdir(FAILED_SLOTS_DIR, S_IRWXU) < 0 && errno != EEXIST) {
690 fprintf(stderr, Name ": can't create file to save path "
691 "to old disk: %s\n", strerror(errno));
692 return;
693 }
694
695 snprintf(path, PATH_MAX, FAILED_SLOTS_DIR "/%s", id_path);
696 f = fopen(path, "w");
697 if (!f) {
698 fprintf(stderr, Name ": can't create file to"
699 " save path to old disk: %s\n",
700 strerror(errno));
701 return;
702 }
703
704 if (fprintf(f, "%s %08x:%08x:%08x:%08x\n",
705 array->metadata,
706 array->uuid[0], array->uuid[1],
707 array->uuid[2], array->uuid[3]) <= 0)
708 fprintf(stderr, Name ": Failed to write to "
709 "<id_path> cookie\n");
710
711 fclose(f);
712 }
713
714 int policy_check_path(struct mdinfo *disk, struct map_ent *array)
715 {
716 char path[PATH_MAX];
717 FILE *f = NULL;
718 char *id_path = disk_path(disk);
719 int rv;
720
721 if (!id_path)
722 return 0;
723
724 snprintf(path, PATH_MAX, FAILED_SLOTS_DIR "/%s", id_path);
725 f = fopen(path, "r");
726 if (!f)
727 return 0;
728
729 rv = fscanf(f, " %s %x:%x:%x:%x\n",
730 array->metadata,
731 array->uuid,
732 array->uuid+1,
733 array->uuid+2,
734 array->uuid+3);
735 fclose(f);
736 return rv == 5;
737 }