multipath.conf: add "enable_foreign" parameter
[multipath-tools/.git] / libmultipath / foreign.c
1 /*
2   Copyright (c) 2018 Martin Wilck, SUSE Linux GmbH
3
4   This program is free software; you can redistribute it and/or
5   modify it under the terms of the GNU General Public License
6   as published by the Free Software Foundation; either version 2
7   of the License, or (at your option) any later version.
8
9   This program is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU General Public License for more details.
13
14   You should have received a copy of the GNU General Public License
15   along with this program.  If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include <sys/sysmacros.h>
19 #include <sys/types.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <dirent.h>
26 #include <fnmatch.h>
27 #include <dlfcn.h>
28 #include <libudev.h>
29 #include <regex.h>
30 #include "vector.h"
31 #include "debug.h"
32 #include "util.h"
33 #include "foreign.h"
34 #include "structs.h"
35 #include "structs_vec.h"
36 #include "print.h"
37
38 static vector foreigns;
39
40 /* This protects vector foreigns */
41 static pthread_rwlock_t foreign_lock = PTHREAD_RWLOCK_INITIALIZER;
42
43 static void rdlock_foreigns(void)
44 {
45         pthread_rwlock_rdlock(&foreign_lock);
46 }
47
48 static void wrlock_foreigns(void)
49 {
50         pthread_rwlock_wrlock(&foreign_lock);
51 }
52
53 static void unlock_foreigns(void *unused)
54 {
55         pthread_rwlock_unlock(&foreign_lock);
56 }
57
58 #define get_dlsym(foreign, sym, lbl)                                    \
59         do {                                                            \
60                 foreign->sym =  dlsym(foreign->handle, #sym);           \
61                 if (foreign->sym == NULL) {                             \
62                         condlog(0, "%s: symbol \"%s\" not found in \"%s\"", \
63                                 __func__, #sym, foreign->name);         \
64                         goto lbl;                                       \
65                 }                                                       \
66         } while(0)
67
68 static void free_foreign(struct foreign *fgn)
69 {
70         struct context *ctx;
71
72         if (fgn == NULL)
73                 return;
74
75         ctx = fgn->context;
76         fgn->context = NULL;
77         if (ctx != NULL)
78                 fgn->cleanup(ctx);
79
80         if (fgn->handle != NULL)
81                 dlclose(fgn->handle);
82         free(fgn);
83 }
84
85 void _cleanup_foreign(void)
86 {
87         struct foreign *fgn;
88         int i;
89
90         if (foreigns == NULL)
91                 return;
92
93         vector_foreach_slot_backwards(foreigns, fgn, i) {
94                 vector_del_slot(foreigns, i);
95                 free_foreign(fgn);
96         }
97         vector_free(foreigns);
98         foreigns = NULL;
99 }
100
101 void cleanup_foreign(void)
102 {
103         wrlock_foreigns();
104         _cleanup_foreign();
105         unlock_foreigns(NULL);
106 }
107
108 static const char foreign_pattern[] = "libforeign-*.so";
109
110 static int select_foreign_libs(const struct dirent *di)
111 {
112
113         return fnmatch(foreign_pattern, di->d_name, FNM_FILE_NAME) == 0;
114 }
115
116 static void free_pre(void *arg)
117 {
118         regex_t **pre = arg;
119
120         if (pre != NULL && *pre != NULL) {
121                 regfree(*pre);
122                 free(*pre);
123                 *pre = NULL;
124         }
125 }
126
127 static int _init_foreign(const char *multipath_dir, const char *enable)
128 {
129         char pathbuf[PATH_MAX];
130         struct dirent **di;
131         struct scandir_result sr;
132         int r, i;
133         regex_t *enable_re = NULL;
134
135         foreigns = vector_alloc();
136         if (foreigns == NULL)
137                 return -ENOMEM;
138
139         pthread_cleanup_push(free_pre, &enable_re);
140         enable_re = calloc(1, sizeof(*enable_re));
141         if (enable_re) {
142                 const char *str = enable ? enable : DEFAULT_ENABLE_FOREIGN;
143
144                 r = regcomp(enable_re, str, REG_EXTENDED|REG_NOSUB);
145                 if (r != 0) {
146                         char errbuf[64];
147
148                         (void)regerror(r, enable_re, errbuf, sizeof(errbuf));
149                         condlog (2, "%s: error compiling enable_foreign = \"%s\": \"%s\"",
150                                  __func__, str, errbuf);
151                         free_pre(&enable_re);
152                 }
153         }
154
155         r = scandir(multipath_dir, &di, select_foreign_libs, alphasort);
156
157         if (r == 0) {
158                 condlog(3, "%s: no foreign multipath libraries found",
159                         __func__);
160                 return 0;
161         } else if (r < 0) {
162                 r = errno;
163                 condlog(1, "%s: error %d scanning foreign multipath libraries",
164                         __func__, r);
165                 _cleanup_foreign();
166                 return -r;
167         }
168
169         sr.di = di;
170         sr.n = r;
171         pthread_cleanup_push_cast(free_scandir_result, &sr);
172         for (i = 0; i < r; i++) {
173                 const char *msg, *fn, *c;
174                 struct foreign *fgn;
175                 int len, namesz;
176
177                 fn = di[i]->d_name;
178
179                 len = strlen(fn);
180                 c = strchr(fn, '-');
181                 if (len < sizeof(foreign_pattern) - 1 || c == NULL) {
182                         condlog(0, "%s: bad file name %s, fnmatch error?",
183                                 __func__, fn);
184                         continue;
185                 }
186                 c++;
187                 condlog(4, "%s: found %s", __func__, fn);
188
189                 namesz = len - sizeof(foreign_pattern) + 3;
190                 fgn = malloc(sizeof(*fgn) + namesz);
191                 if (fgn == NULL)
192                         continue;
193                 memset(fgn, 0, sizeof(*fgn));
194                 strlcpy((char*)fgn + offsetof(struct foreign, name), c, namesz);
195
196                 if (enable_re != NULL) {
197                         int ret = regexec(enable_re, fgn->name, 0, NULL, 0);
198
199                         if (ret == REG_NOMATCH) {
200                                 condlog(3, "%s: foreign library \"%s\" is not enabled",
201                                         __func__, fgn->name);
202                                 free(fgn);
203                                 continue;
204                         } else if (ret != 0)
205                                 /* assume it matches */
206                                 condlog(2, "%s: error %d in regexec() for %s",
207                                         __func__, ret, fgn->name);
208                 }
209
210                 snprintf(pathbuf, sizeof(pathbuf), "%s/%s", multipath_dir, fn);
211                 fgn->handle = dlopen(pathbuf, RTLD_NOW|RTLD_LOCAL);
212                 msg = dlerror();
213                 if (fgn->handle == NULL) {
214                         condlog(1, "%s: failed to dlopen %s: %s", __func__,
215                                 pathbuf, msg);
216                         goto dl_err;
217                 }
218
219                 get_dlsym(fgn, init, dl_err);
220                 get_dlsym(fgn, cleanup, dl_err);
221                 get_dlsym(fgn, add, dl_err);
222                 get_dlsym(fgn, change, dl_err);
223                 get_dlsym(fgn, delete, dl_err);
224                 get_dlsym(fgn, delete_all, dl_err);
225                 get_dlsym(fgn, check, dl_err);
226                 get_dlsym(fgn, lock, dl_err);
227                 get_dlsym(fgn, unlock, dl_err);
228                 get_dlsym(fgn, get_multipaths, dl_err);
229                 get_dlsym(fgn, release_multipaths, dl_err);
230                 get_dlsym(fgn, get_paths, dl_err);
231                 get_dlsym(fgn, release_paths, dl_err);
232
233                 fgn->context = fgn->init(LIBMP_FOREIGN_API, fgn->name);
234                 if (fgn->context == NULL) {
235                         condlog(0, "%s: init() failed for %s", __func__, fn);
236                         goto dl_err;
237                 }
238
239                 if (vector_alloc_slot(foreigns) == NULL) {
240                         goto dl_err;
241                 }
242
243                 vector_set_slot(foreigns, fgn);
244                 condlog(3, "foreign library \"%s\" loaded successfully",
245                         fgn->name);
246
247                 continue;
248
249         dl_err:
250                 free_foreign(fgn);
251         }
252         pthread_cleanup_pop(1); /* free_scandir_result */
253         pthread_cleanup_pop(1); /* free_pre */
254         return 0;
255 }
256
257 int init_foreign(const char *multipath_dir, const char *enable)
258 {
259         int ret;
260
261         wrlock_foreigns();
262
263         if (foreigns != NULL) {
264                 unlock_foreigns(NULL);
265                 condlog(0, "%s: already initialized", __func__);
266                 return -EEXIST;
267         }
268
269         pthread_cleanup_push(unlock_foreigns, NULL);
270         ret = _init_foreign(multipath_dir, enable);
271         pthread_cleanup_pop(1);
272
273         return ret;
274 }
275
276 int add_foreign(struct udev_device *udev)
277 {
278         struct foreign *fgn;
279         dev_t dt;
280         int j;
281         int r = FOREIGN_IGNORED;
282
283         if (udev == NULL) {
284                 condlog(1, "%s called with NULL udev", __func__);
285                 return FOREIGN_ERR;
286         }
287
288         rdlock_foreigns();
289         if (foreigns == NULL) {
290                 unlock_foreigns(NULL);
291                 return FOREIGN_ERR;
292         }
293         pthread_cleanup_push(unlock_foreigns, NULL);
294
295         dt = udev_device_get_devnum(udev);
296         vector_foreach_slot(foreigns, fgn, j) {
297                 r = fgn->add(fgn->context, udev);
298
299                 if (r == FOREIGN_CLAIMED) {
300                         condlog(3, "%s: foreign \"%s\" claims device %d:%d",
301                                 __func__, fgn->name, major(dt), minor(dt));
302                         break;
303                 } else if (r == FOREIGN_OK) {
304                         condlog(4, "%s: foreign \"%s\" owns device %d:%d",
305                                 __func__, fgn->name, major(dt), minor(dt));
306                         break;
307                 } else if (r != FOREIGN_IGNORED) {
308                         condlog(1, "%s: unexpected return value %d from \"%s\"",
309                                 __func__, r, fgn->name);
310                 }
311         }
312
313         pthread_cleanup_pop(1);
314         return r;
315 }
316
317 int change_foreign(struct udev_device *udev)
318 {
319         struct foreign *fgn;
320         int j;
321         dev_t dt;
322         int r = FOREIGN_IGNORED;
323
324         if (udev == NULL) {
325                 condlog(1, "%s called with NULL udev", __func__);
326                 return FOREIGN_ERR;
327         }
328
329         rdlock_foreigns();
330         if (foreigns == NULL) {
331                 unlock_foreigns(NULL);
332                 return FOREIGN_ERR;
333         }
334         pthread_cleanup_push(unlock_foreigns, NULL);
335
336         dt = udev_device_get_devnum(udev);
337         vector_foreach_slot(foreigns, fgn, j) {
338                 r = fgn->change(fgn->context, udev);
339
340                 if (r == FOREIGN_OK) {
341                         condlog(4, "%s: foreign \"%s\" completed %d:%d",
342                                 __func__, fgn->name, major(dt), minor(dt));
343                         break;
344                 } else if (r != FOREIGN_IGNORED) {
345                         condlog(1, "%s: unexpected return value %d from \"%s\"",
346                                 __func__, r, fgn->name);
347                 }
348         }
349
350         pthread_cleanup_pop(1);
351         return r;
352 }
353
354 int delete_foreign(struct udev_device *udev)
355 {
356         struct foreign *fgn;
357         int j;
358         dev_t dt;
359         int r = FOREIGN_IGNORED;
360
361         if (udev == NULL) {
362                 condlog(1, "%s called with NULL udev", __func__);
363                 return FOREIGN_ERR;
364         }
365
366         rdlock_foreigns();
367         if (foreigns == NULL) {
368                 unlock_foreigns(NULL);
369                 return FOREIGN_ERR;
370         }
371         pthread_cleanup_push(unlock_foreigns, NULL);
372
373         dt = udev_device_get_devnum(udev);
374         vector_foreach_slot(foreigns, fgn, j) {
375                 r = fgn->delete(fgn->context, udev);
376
377                 if (r == FOREIGN_OK) {
378                         condlog(3, "%s: foreign \"%s\" deleted device %d:%d",
379                                 __func__, fgn->name, major(dt), minor(dt));
380                         break;
381                 } else if (r != FOREIGN_IGNORED) {
382                         condlog(1, "%s: unexpected return value %d from \"%s\"",
383                                 __func__, r, fgn->name);
384                 }
385         }
386
387         pthread_cleanup_pop(1);
388         return r;
389 }
390
391 int delete_all_foreign(void)
392 {
393         struct foreign *fgn;
394         int j;
395
396         rdlock_foreigns();
397         if (foreigns == NULL) {
398                 unlock_foreigns(NULL);
399                 return FOREIGN_ERR;
400         }
401         pthread_cleanup_push(unlock_foreigns, NULL);
402
403         vector_foreach_slot(foreigns, fgn, j) {
404                 int r;
405
406                 r = fgn->delete_all(fgn->context);
407                 if (r != FOREIGN_IGNORED && r != FOREIGN_OK) {
408                         condlog(1, "%s: unexpected return value %d from \"%s\"",
409                                 __func__, r, fgn->name);
410                 }
411         }
412
413         pthread_cleanup_pop(1);
414         return FOREIGN_OK;
415 }
416
417 void check_foreign(void)
418 {
419         struct foreign *fgn;
420         int j;
421
422         rdlock_foreigns();
423         if (foreigns == NULL) {
424                 unlock_foreigns(NULL);
425                 return;
426         }
427         pthread_cleanup_push(unlock_foreigns, NULL);
428
429         vector_foreach_slot(foreigns, fgn, j) {
430                 fgn->check(fgn->context);
431         }
432
433         pthread_cleanup_pop(1);
434 }
435
436 /* Call this after get_path_layout */
437 void foreign_path_layout(void)
438 {
439         struct foreign *fgn;
440         int i;
441
442         rdlock_foreigns();
443         if (foreigns == NULL) {
444                 unlock_foreigns(NULL);
445                 return;
446         }
447         pthread_cleanup_push(unlock_foreigns, NULL);
448
449         vector_foreach_slot(foreigns, fgn, i) {
450                 const struct _vector *vec;
451
452                 fgn->lock(fgn->context);
453                 pthread_cleanup_push(fgn->unlock, fgn->context);
454
455                 vec = fgn->get_paths(fgn->context);
456                 if (vec != NULL) {
457                         _get_path_layout(vec, LAYOUT_RESET_NOT);
458                 }
459                 fgn->release_paths(fgn->context, vec);
460
461                 pthread_cleanup_pop(1);
462         }
463
464         pthread_cleanup_pop(1);
465 }
466
467 /* Call this after get_multipath_layout */
468 void foreign_multipath_layout(void)
469 {
470         struct foreign *fgn;
471         int i;
472
473         rdlock_foreigns();
474         if (foreigns == NULL) {
475                 unlock_foreigns(NULL);
476                 return;
477         }
478         pthread_cleanup_push(unlock_foreigns, NULL);
479
480         vector_foreach_slot(foreigns, fgn, i) {
481                 const struct _vector *vec;
482
483                 fgn->lock(fgn->context);
484                 pthread_cleanup_push(fgn->unlock, fgn->context);
485
486                 vec = fgn->get_multipaths(fgn->context);
487                 if (vec != NULL) {
488                         _get_multipath_layout(vec, LAYOUT_RESET_NOT);
489                 }
490                 fgn->release_multipaths(fgn->context, vec);
491
492                 pthread_cleanup_pop(1);
493         }
494
495         pthread_cleanup_pop(1);
496 }
497
498 int snprint_foreign_topology(char *buf, int len, int verbosity)
499 {
500         struct foreign *fgn;
501         int i;
502         char *c = buf;
503
504         rdlock_foreigns();
505         if (foreigns == NULL) {
506                 unlock_foreigns(NULL);
507                 return 0;
508         }
509         pthread_cleanup_push(unlock_foreigns, NULL);
510
511         vector_foreach_slot(foreigns, fgn, i) {
512                 const struct _vector *vec;
513                 const struct gen_multipath *gm;
514                 int j;
515
516                 fgn->lock(fgn->context);
517                 pthread_cleanup_push(fgn->unlock, fgn->context);
518
519                 vec = fgn->get_multipaths(fgn->context);
520                 if (vec != NULL) {
521                         vector_foreach_slot(vec, gm, j) {
522
523                                 c += _snprint_multipath_topology(gm, c,
524                                                                  buf + len - c,
525                                                                  verbosity);
526                                 if (c >= buf + len - 1)
527                                         break;
528                         }
529                         if (c >= buf + len - 1)
530                                 break;
531                 }
532                 fgn->release_multipaths(fgn->context, vec);
533                 pthread_cleanup_pop(1);
534         }
535
536         pthread_cleanup_pop(1);
537         return c - buf;
538 }
539
540 void print_foreign_topology(int verbosity)
541 {
542         int buflen = MAX_LINE_LEN * MAX_LINES;
543         char *buf = NULL, *tmp = NULL;
544
545         buf = malloc(buflen);
546         buf[0] = '\0';
547         while (buf != NULL) {
548                 char *c = buf;
549
550                 c += snprint_foreign_topology(buf, buflen,
551                                                    verbosity);
552                 if (c < buf + buflen - 1)
553                         break;
554
555                 buflen *= 2;
556                 tmp = buf;
557                 buf = realloc(buf, buflen);
558         }
559
560         if (buf == NULL && tmp != NULL)
561                 buf = tmp;
562
563         if (buf != NULL) {
564                 printf("%s", buf);
565                 free(buf);
566         }
567 }
568
569 int snprint_foreign_paths(char *buf, int len, const char *style, int pretty)
570 {
571         struct foreign *fgn;
572         int i;
573         char *c = buf;
574
575         rdlock_foreigns();
576         if (foreigns == NULL) {
577                 unlock_foreigns(NULL);
578                 return 0;
579         }
580         pthread_cleanup_push(unlock_foreigns, NULL);
581
582         vector_foreach_slot(foreigns, fgn, i) {
583                 const struct _vector *vec;
584                 const struct gen_path *gp;
585                 int j;
586
587                 fgn->lock(fgn->context);
588                 pthread_cleanup_push(fgn->unlock, fgn->context);
589
590                 vec = fgn->get_paths(fgn->context);
591                 if (vec != NULL) {
592                         vector_foreach_slot(vec, gp, j) {
593                                 c += _snprint_path(gp, c, buf + len - c,
594                                                    style, pretty);
595                                 if (c >= buf + len - 1)
596                                         break;
597                         }
598                         if (c >= buf + len - 1)
599                                 break;
600                 }
601                 fgn->release_paths(fgn->context, vec);
602                 pthread_cleanup_pop(1);
603         }
604
605         pthread_cleanup_pop(1);
606         return c - buf;
607 }
608
609 int snprint_foreign_multipaths(char *buf, int len,
610                                const char *style, int pretty)
611 {
612         struct foreign *fgn;
613         int i;
614         char *c = buf;
615
616         rdlock_foreigns();
617         if (foreigns == NULL) {
618                 unlock_foreigns(NULL);
619                 return 0;
620         }
621         pthread_cleanup_push(unlock_foreigns, NULL);
622
623         vector_foreach_slot(foreigns, fgn, i) {
624                 const struct _vector *vec;
625                 const struct gen_multipath *gm;
626                 int j;
627
628                 fgn->lock(fgn->context);
629                 pthread_cleanup_push(fgn->unlock, fgn->context);
630
631                 vec = fgn->get_multipaths(fgn->context);
632                 if (vec != NULL) {
633                         vector_foreach_slot(vec, gm, j) {
634                                 c += _snprint_multipath(gm, c, buf + len - c,
635                                                         style, pretty);
636                                 if (c >= buf + len - 1)
637                                         break;
638                         }
639                         if (c >= buf + len - 1)
640                                 break;
641                 }
642                 fgn->release_multipaths(fgn->context, vec);
643                 pthread_cleanup_pop(1);
644         }
645
646         pthread_cleanup_pop(1);
647         return c - buf;
648 }