#include #include #include #include #include #include "debug.h" #include "checkers.h" #include "vector.h" struct checker_class { struct list_head node; void *handle; int refcount; int sync; char name[CHECKER_NAME_LEN]; int (*check)(struct checker *); int (*init)(struct checker *); /* to allocate the context */ int (*mp_init)(struct checker *); /* to allocate the mpcontext */ void (*free)(struct checker *); /* to free the context */ void (*reset)(void); /* to reset the global variables */ const char **msgtable; short msgtable_size; }; char *checker_state_names[] = { "wild", "unchecked", "down", "up", "shaky", "ghost", "pending", "timeout", "removed", "delayed", }; static LIST_HEAD(checkers); const char *checker_state_name(int i) { return checker_state_names[i]; } static struct checker_class *alloc_checker_class(void) { struct checker_class *c; c = MALLOC(sizeof(struct checker_class)); if (c) { INIT_LIST_HEAD(&c->node); c->refcount = 1; } return c; } void free_checker_class(struct checker_class *c) { if (!c) return; c->refcount--; if (c->refcount) { condlog(4, "%s checker refcount %d", c->name, c->refcount); return; } condlog(3, "unloading %s checker", c->name); list_del(&c->node); if (c->reset) c->reset(); if (c->handle) { if (dlclose(c->handle) != 0) { condlog(0, "Cannot unload checker %s: %s", c->name, dlerror()); } } FREE(c); } void cleanup_checkers (void) { struct checker_class *checker_loop; struct checker_class *checker_temp; list_for_each_entry_safe(checker_loop, checker_temp, &checkers, node) { free_checker_class(checker_loop); } } static struct checker_class *checker_class_lookup(const char *name) { struct checker_class *c; if (!name || !strlen(name)) return NULL; list_for_each_entry(c, &checkers, node) { if (!strncmp(name, c->name, CHECKER_NAME_LEN)) return c; } return NULL; } void reset_checker_classes(void) { struct checker_class *c; list_for_each_entry(c, &checkers, node) { if (c->reset) c->reset(); } } static struct checker_class *add_checker_class(const char *multipath_dir, const char *name) { char libname[LIB_CHECKER_NAMELEN]; struct stat stbuf; struct checker_class *c; char *errstr; c = alloc_checker_class(); if (!c) return NULL; snprintf(c->name, CHECKER_NAME_LEN, "%s", name); if (!strncmp(c->name, NONE, 4)) goto done; snprintf(libname, LIB_CHECKER_NAMELEN, "%s/libcheck%s.so", multipath_dir, name); if (stat(libname,&stbuf) < 0) { condlog(0,"Checker '%s' not found in %s", name, multipath_dir); goto out; } condlog(3, "loading %s checker", libname); c->handle = dlopen(libname, RTLD_NOW); if (!c->handle) { if ((errstr = dlerror()) != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); goto out; } c->check = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_check"); errstr = dlerror(); if (errstr != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); if (!c->check) goto out; c->init = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_init"); errstr = dlerror(); if (errstr != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); if (!c->init) goto out; c->mp_init = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_mp_init"); c->reset = (void (*)(void)) dlsym(c->handle, "libcheck_reset"); /* These 2 functions can be NULL. call dlerror() to clear out any * error string */ dlerror(); c->free = (void (*)(struct checker *)) dlsym(c->handle, "libcheck_free"); errstr = dlerror(); if (errstr != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); if (!c->free) goto out; c->msgtable_size = 0; c->msgtable = dlsym(c->handle, "libcheck_msgtable"); if (c->msgtable != NULL) { const char **p; for (p = c->msgtable; *p && (p - c->msgtable) < CHECKER_MSGTABLE_SIZE; p++) /* nothing */; c->msgtable_size = p - c->msgtable; } else c->msgtable_size = 0; condlog(3, "checker %s: message table size = %d", c->name, c->msgtable_size); done: c->sync = 1; list_add(&c->node, &checkers); return c; out: free_checker_class(c); return NULL; } void checker_set_fd (struct checker * c, int fd) { if (!c) return; c->fd = fd; } void checker_set_sync (struct checker * c) { if (!c || !c->cls) return; c->cls->sync = 1; } void checker_set_async (struct checker * c) { if (!c || !c->cls) return; c->cls->sync = 0; } void checker_enable (struct checker * c) { if (!c) return; c->disable = 0; } void checker_disable (struct checker * c) { if (!c) return; c->disable = 1; } int checker_init (struct checker * c, void ** mpctxt_addr) { if (!c || !c->cls) return 1; c->mpcontext = mpctxt_addr; if (c->cls->init && c->cls->init(c) != 0) return 1; if (mpctxt_addr && *mpctxt_addr == NULL && c->cls->mp_init && c->cls->mp_init(c) != 0) /* continue even if mp_init fails */ c->mpcontext = NULL; return 0; } int checker_mp_init(struct checker * c, void ** mpctxt_addr) { if (!c || !c->cls) return 1; if (c->mpcontext || !mpctxt_addr) return 0; c->mpcontext = mpctxt_addr; if (*mpctxt_addr == NULL && c->cls->mp_init && c->cls->mp_init(c) != 0) { c->mpcontext = NULL; return 1; } return 0; } void checker_clear (struct checker *c) { memset(c, 0x0, sizeof(struct checker)); c->fd = -1; } void checker_put (struct checker * dst) { struct checker_class *src; if (!dst) return; src = dst->cls; if (src && src->free) src->free(dst); checker_clear(dst); free_checker_class(src); } int checker_check (struct checker * c, int path_state) { int r; if (!c) return PATH_WILD; c->msgid = CHECKER_MSGID_NONE; if (c->disable) { c->msgid = CHECKER_MSGID_DISABLED; return PATH_UNCHECKED; } if (!strncmp(c->cls->name, NONE, 4)) return path_state; if (c->fd < 0) { c->msgid = CHECKER_MSGID_NO_FD; return PATH_WILD; } r = c->cls->check(c); return r; } const char *checker_name(const struct checker *c) { if (!c || !c->cls) return NULL; return c->cls->name; } int checker_is_sync(const struct checker *c) { return c && c->cls && c->cls->sync; } static const char *generic_msg[CHECKER_GENERIC_MSGTABLE_SIZE] = { [CHECKER_MSGID_NONE] = "", [CHECKER_MSGID_DISABLED] = " is disabled", [CHECKER_MSGID_NO_FD] = " has no usable fd", [CHECKER_MSGID_INVALID] = " provided invalid message id", [CHECKER_MSGID_UP] = " reports path is up", [CHECKER_MSGID_DOWN] = " reports path is down", [CHECKER_MSGID_GHOST] = " reports path is ghost", [CHECKER_MSGID_UNSUPPORTED] = " doesn't support this device", }; const char *checker_message(const struct checker *c) { int id; if (!c || !c->cls || c->msgid < 0 || (c->msgid >= CHECKER_GENERIC_MSGTABLE_SIZE && c->msgid < CHECKER_FIRST_MSGID)) goto bad_id; if (c->msgid < CHECKER_GENERIC_MSGTABLE_SIZE) return generic_msg[c->msgid]; id = c->msgid - CHECKER_FIRST_MSGID; if (id < c->cls->msgtable_size) return c->cls->msgtable[id]; bad_id: return generic_msg[CHECKER_MSGID_NONE]; } void checker_clear_message (struct checker *c) { if (!c) return; c->msgid = CHECKER_MSGID_NONE; } void checker_get(const char *multipath_dir, struct checker *dst, const char *name) { struct checker_class *src = NULL; if (!dst) return; if (name && strlen(name)) { src = checker_class_lookup(name); if (!src) src = add_checker_class(multipath_dir, name); } dst->cls = src; if (!src) return; src->refcount++; } int init_checkers(const char *multipath_dir) { if (!add_checker_class(multipath_dir, DEFAULT_CHECKER)) return 1; return 0; }