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