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