libmultipath: implement and use blacklist merging
[multipath-tools/.git] / libmultipath / blacklist.c
1 /*
2  * Copyright (c) 2004, 2005 Christophe Varoqui
3  */
4 #include <stdio.h>
5 #include <libudev.h>
6
7 #include "checkers.h"
8 #include "memory.h"
9 #include "vector.h"
10 #include "util.h"
11 #include "debug.h"
12 #include "structs.h"
13 #include "config.h"
14 #include "blacklist.h"
15
16 int store_ble(vector blist, char * str, int origin)
17 {
18         struct blentry * ble;
19
20         if (!str)
21                 return 0;
22
23         if (!blist)
24                 goto out;
25
26         ble = MALLOC(sizeof(struct blentry));
27
28         if (!ble)
29                 goto out;
30
31         if (regcomp(&ble->regex, str, REG_EXTENDED|REG_NOSUB))
32                 goto out1;
33
34         if (!vector_alloc_slot(blist))
35                 goto out1;
36
37         ble->str = str;
38         ble->origin = origin;
39         vector_set_slot(blist, ble);
40         return 0;
41 out1:
42         FREE(ble);
43 out:
44         FREE(str);
45         return 1;
46 }
47
48
49 int alloc_ble_device(vector blist)
50 {
51         struct blentry_device * ble = MALLOC(sizeof(struct blentry_device));
52
53         if (!ble)
54                 return 1;
55
56         if (!blist || !vector_alloc_slot(blist)) {
57                 FREE(ble);
58                 return 1;
59         }
60         vector_set_slot(blist, ble);
61         return 0;
62 }
63
64 int set_ble_device(vector blist, char * vendor, char * product, int origin)
65 {
66         struct blentry_device * ble;
67
68         if (!blist)
69                 return 1;
70
71         ble = VECTOR_LAST_SLOT(blist);
72
73         if (!ble)
74                 return 1;
75
76         if (vendor) {
77                 if (regcomp(&ble->vendor_reg, vendor,
78                             REG_EXTENDED|REG_NOSUB)) {
79                         FREE(vendor);
80                         if (product)
81                                 FREE(product);
82                         return 1;
83                 }
84                 ble->vendor = vendor;
85         }
86         if (product) {
87                 if (regcomp(&ble->product_reg, product,
88                             REG_EXTENDED|REG_NOSUB)) {
89                         FREE(product);
90                         if (vendor) {
91                                 ble->vendor = NULL;
92                                 FREE(vendor);
93                         }
94                         return 1;
95                 }
96                 ble->product = product;
97         }
98         ble->origin = origin;
99         return 0;
100 }
101
102 int
103 _blacklist_exceptions (vector elist, const char * str)
104 {
105         int i;
106         struct blentry * ele;
107
108         vector_foreach_slot (elist, ele, i) {
109                 if (!regexec(&ele->regex, str, 0, NULL, 0))
110                         return 1;
111         }
112         return 0;
113 }
114
115 int
116 _blacklist (vector blist, const char * str)
117 {
118         int i;
119         struct blentry * ble;
120
121         vector_foreach_slot (blist, ble, i) {
122                 if (!regexec(&ble->regex, str, 0, NULL, 0))
123                         return 1;
124         }
125         return 0;
126 }
127
128 int
129 _blacklist_exceptions_device(const struct _vector *elist, const char * vendor,
130                              const char * product)
131 {
132         int i;
133         struct blentry_device * ble;
134
135         vector_foreach_slot (elist, ble, i) {
136                 if (!ble->vendor && !ble->product)
137                         continue;
138                 if ((!ble->vendor ||
139                      !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) &&
140                     (!ble->product ||
141                      !regexec(&ble->product_reg, product, 0, NULL, 0)))
142                         return 1;
143         }
144         return 0;
145 }
146
147 int
148 _blacklist_device (const struct _vector *blist, const char * vendor,
149                    const char * product)
150 {
151         int i;
152         struct blentry_device * ble;
153
154         vector_foreach_slot (blist, ble, i) {
155                 if (!ble->vendor && !ble->product)
156                         continue;
157                 if ((!ble->vendor ||
158                      !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) &&
159                     (!ble->product ||
160                      !regexec(&ble->product_reg, product, 0, NULL, 0)))
161                         return 1;
162         }
163         return 0;
164 }
165
166 static int
167 find_blacklist_device (const struct _vector *blist, const char * vendor,
168                        const char * product)
169 {
170         int i;
171         struct blentry_device * ble;
172
173         vector_foreach_slot (blist, ble, i) {
174                 if (((!vendor && !ble->vendor) ||
175                      (vendor && ble->vendor &&
176                       !strcmp(vendor, ble->vendor))) &&
177                     ((!product && !ble->product) ||
178                      (product && ble->product &&
179                       !strcmp(product, ble->product))))
180                         return 1;
181         }
182         return 0;
183 }
184
185 int
186 setup_default_blist (struct config * conf)
187 {
188         struct blentry * ble;
189         struct hwentry *hwe;
190         char * str;
191         int i;
192
193         str = STRDUP("^(ram|raw|loop|fd|md|dm-|sr|scd|st|dcssblk)[0-9]");
194         if (!str)
195                 return 1;
196         if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
197                 return 1;
198
199         str = STRDUP("^(td|hd|vd)[a-z]");
200         if (!str)
201                 return 1;
202         if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
203                 return 1;
204
205         str = STRDUP("(SCSI_IDENT_|ID_WWN)");
206         if (!str)
207                 return 1;
208         if (store_ble(conf->elist_property, str, ORIGIN_DEFAULT))
209                 return 1;
210
211         vector_foreach_slot (conf->hwtable, hwe, i) {
212                 if (hwe->bl_product) {
213                         if (find_blacklist_device(conf->blist_device,
214                                                   hwe->vendor, hwe->bl_product))
215                                 continue;
216                         if (alloc_ble_device(conf->blist_device))
217                                 return 1;
218                         ble = VECTOR_SLOT(conf->blist_device,
219                                           VECTOR_SIZE(conf->blist_device) - 1);
220                         if (set_ble_device(conf->blist_device,
221                                            STRDUP(hwe->vendor),
222                                            STRDUP(hwe->bl_product),
223                                            ORIGIN_DEFAULT)) {
224                                 FREE(ble);
225                                 vector_del_slot(conf->blist_device, VECTOR_SIZE(conf->blist_device) - 1);
226                                 return 1;
227                         }
228                 }
229         }
230         return 0;
231 }
232
233 #define LOG_BLIST(M,S)                                                  \
234         if (vendor && product)                                          \
235                 condlog(3, "%s: (%s:%s) %s %s",                         \
236                         dev, vendor, product, (M), (S));                \
237         else if (wwid && !dev)                                          \
238                 condlog(3, "%s: %s %s", wwid, (M), (S));                \
239         else if (wwid)                                                  \
240                 condlog(3, "%s: %s %s %s", dev, (M), wwid, (S));        \
241         else if (env)                                                   \
242                 condlog(3, "%s: %s %s %s", dev, (M), env, (S));         \
243         else                                                            \
244                 condlog(3, "%s: %s %s", dev, (M), (S))
245
246 void
247 log_filter (const char *dev, char *vendor, char *product, char *wwid,
248             const char *env, int r)
249 {
250         /*
251          * Try to sort from most likely to least.
252          */
253         switch (r) {
254         case MATCH_NOTHING:
255                 break;
256         case MATCH_DEVICE_BLIST:
257                 LOG_BLIST("vendor/product", "blacklisted");
258                 break;
259         case MATCH_WWID_BLIST:
260                 LOG_BLIST("wwid", "blacklisted");
261                 break;
262         case MATCH_DEVNODE_BLIST:
263                 LOG_BLIST("device node name", "blacklisted");
264                 break;
265         case MATCH_PROPERTY_BLIST:
266                 LOG_BLIST("udev property", "blacklisted");
267                 break;
268         case MATCH_DEVICE_BLIST_EXCEPT:
269                 LOG_BLIST("vendor/product", "whitelisted");
270                 break;
271         case MATCH_WWID_BLIST_EXCEPT:
272                 LOG_BLIST("wwid", "whitelisted");
273                 break;
274         case MATCH_DEVNODE_BLIST_EXCEPT:
275                 LOG_BLIST("device node name", "whitelisted");
276                 break;
277         case MATCH_PROPERTY_BLIST_EXCEPT:
278                 LOG_BLIST("udev property", "whitelisted");
279                 break;
280         case MATCH_PROPERTY_BLIST_MISSING:
281                 LOG_BLIST("blacklisted,", "udev property missing");
282                 break;
283         }
284 }
285
286 int
287 _filter_device (vector blist, vector elist, char * vendor, char * product)
288 {
289         if (!vendor || !product)
290                 return 0;
291         if (_blacklist_exceptions_device(elist, vendor, product))
292                 return MATCH_DEVICE_BLIST_EXCEPT;
293         if (_blacklist_device(blist, vendor, product))
294                 return MATCH_DEVICE_BLIST;
295         return 0;
296 }
297
298 int
299 filter_device (vector blist, vector elist, char * vendor, char * product)
300 {
301         int r = _filter_device(blist, elist, vendor, product);
302         log_filter(NULL, vendor, product, NULL, NULL, r);
303         return r;
304 }
305
306 int
307 _filter_devnode (vector blist, vector elist, char * dev)
308 {
309         if (!dev)
310                 return 0;
311         if (_blacklist_exceptions(elist, dev))
312                 return MATCH_DEVNODE_BLIST_EXCEPT;
313         if (_blacklist(blist, dev))
314                 return MATCH_DEVNODE_BLIST;
315         return 0;
316 }
317
318 int
319 filter_devnode (vector blist, vector elist, char * dev)
320 {
321         int r = _filter_devnode(blist, elist, dev);
322         log_filter(dev, NULL, NULL, NULL, NULL, r);
323         return r;
324 }
325
326 int
327 _filter_wwid (vector blist, vector elist, char * wwid)
328 {
329         if (!wwid)
330                 return 0;
331         if (_blacklist_exceptions(elist, wwid))
332                 return MATCH_WWID_BLIST_EXCEPT;
333         if (_blacklist(blist, wwid))
334                 return MATCH_WWID_BLIST;
335         return 0;
336 }
337
338 int
339 filter_wwid (vector blist, vector elist, char * wwid, char * dev)
340 {
341         int r = _filter_wwid(blist, elist, wwid);
342         log_filter(dev, NULL, NULL, wwid, NULL, r);
343         return r;
344 }
345
346 int
347 _filter_path (struct config * conf, struct path * pp)
348 {
349         int r;
350
351         r = filter_property(conf, pp->udev);
352         if (r > 0)
353                 return r;
354
355         r = _filter_devnode(conf->blist_devnode, conf->elist_devnode,pp->dev);
356         if (r > 0)
357                 return r;
358         r = _filter_device(conf->blist_device, conf->elist_device,
359                            pp->vendor_id, pp->product_id);
360         if (r > 0)
361                 return r;
362         r = _filter_wwid(conf->blist_wwid, conf->elist_wwid, pp->wwid);
363         return r;
364 }
365
366 int
367 filter_path (struct config * conf, struct path * pp)
368 {
369         int r=_filter_path(conf, pp);
370         log_filter(pp->dev, pp->vendor_id, pp->product_id, pp->wwid, NULL, r);
371         return r;
372 }
373
374 int
375 _filter_property (struct config *conf, const char *env)
376 {
377         if (_blacklist_exceptions(conf->elist_property, env))
378                 return MATCH_PROPERTY_BLIST_EXCEPT;
379         if (_blacklist(conf->blist_property, env))
380                 return MATCH_PROPERTY_BLIST;
381
382         return 0;
383 }
384
385 int
386 filter_property(struct config * conf, struct udev_device * udev)
387 {
388         const char *devname = udev_device_get_sysname(udev);
389         struct udev_list_entry *list_entry;
390         int r;
391
392         if (!udev)
393                 return 0;
394
395         udev_list_entry_foreach(list_entry,
396                                 udev_device_get_properties_list_entry(udev)) {
397                 const char *env;
398
399                 env = udev_list_entry_get_name(list_entry);
400                 if (!env)
401                         continue;
402
403                 r = _filter_property(conf, env);
404                 if (r) {
405                         log_filter(devname, NULL, NULL, NULL, env, r);
406                         return r;
407                 }
408         }
409
410         /*
411          * This is the inverse of the 'normal' matching;
412          * the environment variable _has_ to match.
413          */
414         log_filter(devname, NULL, NULL, NULL, NULL,
415                    MATCH_PROPERTY_BLIST_MISSING);
416         return MATCH_PROPERTY_BLIST_MISSING;
417 }
418
419 static void free_ble(struct blentry *ble)
420 {
421         if (!ble)
422                 return;
423         regfree(&ble->regex);
424         FREE(ble->str);
425         FREE(ble);
426 }
427
428 void
429 free_blacklist (vector blist)
430 {
431         struct blentry * ble;
432         int i;
433
434         if (!blist)
435                 return;
436
437         vector_foreach_slot (blist, ble, i) {
438                 free_ble(ble);
439         }
440         vector_free(blist);
441 }
442
443 void merge_blacklist(vector blist)
444 {
445         struct blentry *bl1, *bl2;
446         int i, j;
447
448         vector_foreach_slot(blist, bl1, i) {
449                 j = i + 1;
450                 vector_foreach_slot_after(blist, bl2, j) {
451                         if (!bl1->str || !bl2->str || strcmp(bl1->str, bl2->str))
452                                 continue;
453                         condlog(3, "%s: duplicate blist entry section for %s",
454                                 __func__, bl1->str);
455                         free_ble(bl2);
456                         vector_del_slot(blist, j);
457                         j--;
458                 }
459         }
460 }
461
462 static void free_ble_device(struct blentry_device *ble)
463 {
464         if (ble) {
465                 if (ble->vendor) {
466                         regfree(&ble->vendor_reg);
467                         FREE(ble->vendor);
468                 }
469                 if (ble->product) {
470                         regfree(&ble->product_reg);
471                         FREE(ble->product);
472                 }
473                 FREE(ble);
474         }
475 }
476
477 void
478 free_blacklist_device (vector blist)
479 {
480         struct blentry_device * ble;
481         int i;
482
483         if (!blist)
484                 return;
485
486         vector_foreach_slot (blist, ble, i) {
487                 free_ble_device(ble);
488         }
489         vector_free(blist);
490 }
491
492 void merge_blacklist_device(vector blist)
493 {
494         struct blentry_device *bl1, *bl2;
495         int i, j;
496
497         vector_foreach_slot(blist, bl1, i) {
498                 if (!bl1->vendor && !bl1->product) {
499                         free_ble_device(bl1);
500                         vector_del_slot(blist, i);
501                         i--;
502                 }
503         }
504
505         vector_foreach_slot(blist, bl1, i) {
506                 j = i + 1;
507                 vector_foreach_slot_after(blist, bl2, j) {
508                         if ((!bl1->vendor && bl2->vendor) ||
509                             (bl1->vendor && !bl2->vendor) ||
510                             (bl1->vendor && bl2->vendor &&
511                              strcmp(bl1->vendor, bl2->vendor)))
512                                 continue;
513                         if ((!bl1->product && bl2->product) ||
514                             (bl1->product && !bl2->product) ||
515                             (bl1->product && bl2->product &&
516                              strcmp(bl1->product, bl2->product)))
517                                 continue;
518                         condlog(3, "%s: duplicate blist entry section for %s:%s",
519                                 __func__, bl1->vendor, bl1->product);
520                         free_ble_device(bl2);
521                         vector_del_slot(blist, j);
522                         j--;
523                 }
524         }
525 }