2 Copyright (c) 2018 Martin Wilck, SUSE Linux GmbH
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.
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.
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/>.
18 #include <sys/sysmacros.h>
33 #include "structs_vec.h"
36 static vector foreigns;
38 /* This protects vector foreigns */
39 static pthread_rwlock_t foreign_lock = PTHREAD_RWLOCK_INITIALIZER;
41 static void rdlock_foreigns(void)
43 pthread_rwlock_rdlock(&foreign_lock);
46 static void wrlock_foreigns(void)
48 pthread_rwlock_wrlock(&foreign_lock);
51 static void unlock_foreigns(void *unused)
53 pthread_rwlock_unlock(&foreign_lock);
56 #define get_dlsym(foreign, sym, lbl) \
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); \
66 static void free_foreign(struct foreign *fgn)
78 if (fgn->handle != NULL)
83 void _cleanup_foreign(void)
91 vector_foreach_slot_backwards(foreigns, fgn, i) {
92 vector_del_slot(foreigns, i);
95 vector_free(foreigns);
99 void cleanup_foreign(void)
103 unlock_foreigns(NULL);
106 static const char foreign_pattern[] = "libforeign-*.so";
108 static int select_foreign_libs(const struct dirent *di)
111 return fnmatch(foreign_pattern, di->d_name, FNM_FILE_NAME) == 0;
114 static int _init_foreign(const char *multipath_dir)
116 char pathbuf[PATH_MAX];
118 struct scandir_result sr;
121 foreigns = vector_alloc();
122 if (foreigns == NULL)
125 r = scandir(multipath_dir, &di, select_foreign_libs, alphasort);
128 condlog(3, "%s: no foreign multipath libraries found",
133 condlog(1, "%s: error %d scanning foreign multipath libraries",
141 pthread_cleanup_push_cast(free_scandir_result, &sr);
142 for (i = 0; i < r; i++) {
143 const char *msg, *fn, *c;
151 if (len < sizeof(foreign_pattern) - 1 || c == NULL) {
152 condlog(0, "%s: bad file name %s, fnmatch error?",
157 condlog(4, "%s: found %s", __func__, fn);
159 namesz = len - sizeof(foreign_pattern) + 3;
160 fgn = malloc(sizeof(*fgn) + namesz);
163 memset(fgn, 0, sizeof(*fgn));
164 strlcpy((char*)fgn + offsetof(struct foreign, name), c, namesz);
166 snprintf(pathbuf, sizeof(pathbuf), "%s/%s", multipath_dir, fn);
167 fgn->handle = dlopen(pathbuf, RTLD_NOW|RTLD_LOCAL);
169 if (fgn->handle == NULL) {
170 condlog(1, "%s: failed to dlopen %s: %s", __func__,
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);
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);
195 if (vector_alloc_slot(foreigns) == NULL) {
199 vector_set_slot(foreigns, fgn);
200 condlog(3, "foreign library \"%s\" loaded successfully",
208 pthread_cleanup_pop(1);
212 int init_foreign(const char *multipath_dir)
218 if (foreigns != NULL) {
219 unlock_foreigns(NULL);
220 condlog(0, "%s: already initialized", __func__);
224 pthread_cleanup_push(unlock_foreigns, NULL);
225 ret = _init_foreign(multipath_dir);
226 pthread_cleanup_pop(1);
231 int add_foreign(struct udev_device *udev)
236 int r = FOREIGN_IGNORED;
239 condlog(1, "%s called with NULL udev", __func__);
244 if (foreigns == NULL) {
245 unlock_foreigns(NULL);
248 pthread_cleanup_push(unlock_foreigns, NULL);
250 dt = udev_device_get_devnum(udev);
251 vector_foreach_slot(foreigns, fgn, j) {
252 r = fgn->add(fgn->context, udev);
254 if (r == FOREIGN_CLAIMED) {
255 condlog(3, "%s: foreign \"%s\" claims device %d:%d",
256 __func__, fgn->name, major(dt), minor(dt));
258 } else if (r == FOREIGN_OK) {
259 condlog(4, "%s: foreign \"%s\" owns device %d:%d",
260 __func__, fgn->name, major(dt), minor(dt));
262 } else if (r != FOREIGN_IGNORED) {
263 condlog(1, "%s: unexpected return value %d from \"%s\"",
264 __func__, r, fgn->name);
268 pthread_cleanup_pop(1);
272 int change_foreign(struct udev_device *udev)
277 int r = FOREIGN_IGNORED;
280 condlog(1, "%s called with NULL udev", __func__);
285 if (foreigns == NULL) {
286 unlock_foreigns(NULL);
289 pthread_cleanup_push(unlock_foreigns, NULL);
291 dt = udev_device_get_devnum(udev);
292 vector_foreach_slot(foreigns, fgn, j) {
293 r = fgn->change(fgn->context, udev);
295 if (r == FOREIGN_OK) {
296 condlog(4, "%s: foreign \"%s\" completed %d:%d",
297 __func__, fgn->name, major(dt), minor(dt));
299 } else if (r != FOREIGN_IGNORED) {
300 condlog(1, "%s: unexpected return value %d from \"%s\"",
301 __func__, r, fgn->name);
305 pthread_cleanup_pop(1);
309 int delete_foreign(struct udev_device *udev)
314 int r = FOREIGN_IGNORED;
317 condlog(1, "%s called with NULL udev", __func__);
322 if (foreigns == NULL) {
323 unlock_foreigns(NULL);
326 pthread_cleanup_push(unlock_foreigns, NULL);
328 dt = udev_device_get_devnum(udev);
329 vector_foreach_slot(foreigns, fgn, j) {
330 r = fgn->delete(fgn->context, udev);
332 if (r == FOREIGN_OK) {
333 condlog(3, "%s: foreign \"%s\" deleted device %d:%d",
334 __func__, fgn->name, major(dt), minor(dt));
336 } else if (r != FOREIGN_IGNORED) {
337 condlog(1, "%s: unexpected return value %d from \"%s\"",
338 __func__, r, fgn->name);
342 pthread_cleanup_pop(1);
346 int delete_all_foreign(void)
352 if (foreigns == NULL) {
353 unlock_foreigns(NULL);
356 pthread_cleanup_push(unlock_foreigns, NULL);
358 vector_foreach_slot(foreigns, fgn, j) {
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);
368 pthread_cleanup_pop(1);
372 void check_foreign(void)
378 if (foreigns == NULL) {
379 unlock_foreigns(NULL);
382 pthread_cleanup_push(unlock_foreigns, NULL);
384 vector_foreach_slot(foreigns, fgn, j) {
385 fgn->check(fgn->context);
388 pthread_cleanup_pop(1);
391 /* Call this after get_path_layout */
392 void foreign_path_layout(void)
398 if (foreigns == NULL) {
399 unlock_foreigns(NULL);
402 pthread_cleanup_push(unlock_foreigns, NULL);
404 vector_foreach_slot(foreigns, fgn, i) {
405 const struct _vector *vec;
407 fgn->lock(fgn->context);
408 pthread_cleanup_push(fgn->unlock, fgn->context);
410 vec = fgn->get_paths(fgn->context);
412 _get_path_layout(vec, LAYOUT_RESET_NOT);
414 fgn->release_paths(fgn->context, vec);
416 pthread_cleanup_pop(1);
419 pthread_cleanup_pop(1);
422 /* Call this after get_multipath_layout */
423 void foreign_multipath_layout(void)
429 if (foreigns == NULL) {
430 unlock_foreigns(NULL);
433 pthread_cleanup_push(unlock_foreigns, NULL);
435 vector_foreach_slot(foreigns, fgn, i) {
436 const struct _vector *vec;
438 fgn->lock(fgn->context);
439 pthread_cleanup_push(fgn->unlock, fgn->context);
441 vec = fgn->get_multipaths(fgn->context);
443 _get_multipath_layout(vec, LAYOUT_RESET_NOT);
445 fgn->release_multipaths(fgn->context, vec);
447 pthread_cleanup_pop(1);
450 pthread_cleanup_pop(1);
453 int snprint_foreign_topology(char *buf, int len, int verbosity)
460 if (foreigns == NULL) {
461 unlock_foreigns(NULL);
464 pthread_cleanup_push(unlock_foreigns, NULL);
466 vector_foreach_slot(foreigns, fgn, i) {
467 const struct _vector *vec;
468 const struct gen_multipath *gm;
471 fgn->lock(fgn->context);
472 pthread_cleanup_push(fgn->unlock, fgn->context);
474 vec = fgn->get_multipaths(fgn->context);
476 vector_foreach_slot(vec, gm, j) {
478 c += _snprint_multipath_topology(gm, c,
481 if (c >= buf + len - 1)
484 if (c >= buf + len - 1)
487 fgn->release_multipaths(fgn->context, vec);
488 pthread_cleanup_pop(1);
491 pthread_cleanup_pop(1);
495 void print_foreign_topology(int verbosity)
497 int buflen = MAX_LINE_LEN * MAX_LINES;
498 char *buf = NULL, *tmp = NULL;
500 buf = malloc(buflen);
502 while (buf != NULL) {
505 c += snprint_foreign_topology(buf, buflen,
507 if (c < buf + buflen - 1)
512 buf = realloc(buf, buflen);
515 if (buf == NULL && tmp != NULL)
524 int snprint_foreign_paths(char *buf, int len, const char *style, int pretty)
531 if (foreigns == NULL) {
532 unlock_foreigns(NULL);
535 pthread_cleanup_push(unlock_foreigns, NULL);
537 vector_foreach_slot(foreigns, fgn, i) {
538 const struct _vector *vec;
539 const struct gen_path *gp;
542 fgn->lock(fgn->context);
543 pthread_cleanup_push(fgn->unlock, fgn->context);
545 vec = fgn->get_paths(fgn->context);
547 vector_foreach_slot(vec, gp, j) {
548 c += _snprint_path(gp, c, buf + len - c,
550 if (c >= buf + len - 1)
553 if (c >= buf + len - 1)
556 fgn->release_paths(fgn->context, vec);
557 pthread_cleanup_pop(1);
560 pthread_cleanup_pop(1);
564 int snprint_foreign_multipaths(char *buf, int len,
565 const char *style, int pretty)
572 if (foreigns == NULL) {
573 unlock_foreigns(NULL);
576 pthread_cleanup_push(unlock_foreigns, NULL);
578 vector_foreach_slot(foreigns, fgn, i) {
579 const struct _vector *vec;
580 const struct gen_multipath *gm;
583 fgn->lock(fgn->context);
584 pthread_cleanup_push(fgn->unlock, fgn->context);
586 vec = fgn->get_multipaths(fgn->context);
588 vector_foreach_slot(vec, gm, j) {
589 c += _snprint_multipath(gm, c, buf + len - c,
591 if (c >= buf + len - 1)
594 if (c >= buf + len - 1)
597 fgn->release_multipaths(fgn->context, vec);
598 pthread_cleanup_pop(1);
601 pthread_cleanup_pop(1);