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