Imported Upstream version 0.7.0
[platform/upstream/multipath-tools.git] / libmultipath / checkers.c
index 5cc31d2..05e024f 100644 (file)
@@ -2,20 +2,23 @@
 #include <string.h>
 #include <stddef.h>
 #include <dlfcn.h>
+#include <sys/stat.h>
 
 #include "debug.h"
 #include "checkers.h"
 #include "vector.h"
-#include "config.h"
 
 char *checker_state_names[] = {
-      "wild",
-      "unchecked",
-      "down",
-      "up",
-      "shaky",
-      "ghost",
-      "pending"
+       "wild",
+       "unchecked",
+       "down",
+       "up",
+       "shaky",
+       "ghost",
+       "pending",
+       "timeout",
+       "removed",
+       "delayed",
 };
 
 static LIST_HEAD(checkers);
@@ -25,20 +28,43 @@ char * checker_state_name (int i)
        return checker_state_names[i];
 }
 
-int init_checkers (void)
+int init_checkers (char *multipath_dir)
 {
-       if (!add_checker(DEFAULT_CHECKER))
+       if (!add_checker(multipath_dir, DEFAULT_CHECKER))
                return 1;
        return 0;
 }
 
 struct checker * alloc_checker (void)
 {
-       return MALLOC(sizeof(struct checker));
+       struct checker *c;
+
+       c = MALLOC(sizeof(struct checker));
+       if (c) {
+               INIT_LIST_HEAD(&c->node);
+               c->refcount = 1;
+       }
+       return c;
 }
 
 void free_checker (struct checker * c)
 {
+       if (!c)
+               return;
+       c->refcount--;
+       if (c->refcount) {
+               condlog(3, "%s checker refcount %d",
+                       c->name, c->refcount);
+               return;
+       }
+       condlog(3, "unloading %s checker", c->name);
+       list_del(&c->node);
+       if (c->handle) {
+               if (dlclose(c->handle) != 0) {
+                       condlog(0, "Cannot unload checker %s: %s",
+                               c->name, dlerror());
+               }
+       }
        FREE(c);
 }
 
@@ -48,7 +74,6 @@ void cleanup_checkers (void)
        struct checker * checker_temp;
 
        list_for_each_entry_safe(checker_loop, checker_temp, &checkers, node) {
-               list_del(&checker_loop->node);
                free_checker(checker_loop);
        }
 }
@@ -57,55 +82,70 @@ struct checker * checker_lookup (char * name)
 {
        struct checker * 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 add_checker(name);
+       return NULL;
 }
 
-struct checker * add_checker (char * name)
+struct checker * add_checker (char *multipath_dir, char * name)
 {
        char libname[LIB_CHECKER_NAMELEN];
-       void * handle;
+       struct stat stbuf;
        struct checker * c;
        char *errstr;
 
        c = alloc_checker();
        if (!c)
                return NULL;
+       snprintf(c->name, CHECKER_NAME_LEN, "%s", name);
        snprintf(libname, LIB_CHECKER_NAMELEN, "%s/libcheck%s.so",
-                conf->multipath_dir, name);
+                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);
-       handle = dlopen(libname, RTLD_NOW);
-       errstr = dlerror();
-       if (errstr != NULL)
-       condlog(0, "A dynamic linking error occurred: (%s)", errstr);
-       if (!handle)
+       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(handle, "libcheck_check");
+       }
+       c->check = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_check");
        errstr = dlerror();
        if (errstr != NULL)
-       condlog(0, "A dynamic linking error occurred: (%s)", errstr);
+               condlog(0, "A dynamic linking error occurred: (%s)", errstr);
        if (!c->check)
                goto out;
 
-       c->init = (int (*)(struct checker *)) dlsym(handle, "libcheck_init");
+       c->init = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_init");
        errstr = dlerror();
        if (errstr != NULL)
-       condlog(0, "A dynamic linking error occurred: (%s)", errstr);
+               condlog(0, "A dynamic linking error occurred: (%s)", errstr);
        if (!c->init)
                goto out;
 
-       c->free = (void (*)(struct checker *)) dlsym(handle, "libcheck_free");
+       c->free = (void (*)(struct checker *)) dlsym(c->handle, "libcheck_free");
        errstr = dlerror();
        if (errstr != NULL)
-       condlog(0, "A dynamic linking error occurred: (%s)", errstr);
+               condlog(0, "A dynamic linking error occurred: (%s)", errstr);
        if (!c->free)
                goto out;
 
-       snprintf(c->name, CHECKER_NAME_LEN, "%s", name);
+       c->repair = (void (*)(struct checker *)) dlsym(c->handle,
+                                                      "libcheck_repair");
+       errstr = dlerror();
+       if (errstr != NULL)
+               condlog(0, "A dynamic linking error occurred: (%s)", errstr);
+       if (!c->repair)
+               goto out;
+
        c->fd = 0;
        c->sync = 1;
        list_add(&c->node, &checkers);
@@ -117,48 +157,86 @@ out:
 
 void checker_set_fd (struct checker * c, int fd)
 {
+       if (!c)
+               return;
        c->fd = fd;
 }
 
 void checker_set_sync (struct checker * c)
 {
+       if (!c)
+               return;
        c->sync = 1;
 }
 
 void checker_set_async (struct checker * c)
 {
+       if (!c)
+               return;
        c->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)
+               return 1;
        c->mpcontext = mpctxt_addr;
        return c->init(c);
 }
 
-void checker_put (struct checker * c)
+void checker_put (struct checker * dst)
 {
-       if (c->free)
-               c->free(c);
-       memset(c, 0x0, sizeof(struct checker));
+       struct checker * src;
+
+       if (!dst || !dst->check)
+               return;
+       src = checker_lookup(dst->name);
+       if (dst->free)
+               dst->free(dst);
+       memset(dst, 0x0, sizeof(struct checker));
+       free_checker(src);
+}
+
+void checker_repair (struct checker * c)
+{
+       if (!checker_selected(c))
+               return;
+
+       c->message[0] = '\0';
+       if (c->disable) {
+               MSG(c, "checker disabled");
+               return;
+       }
+
+       c->repair(c);
 }
 
 int checker_check (struct checker * c)
 {
        int r;
 
-       if (c->disable)
+       if (!c)
+               return PATH_WILD;
+
+       c->message[0] = '\0';
+       if (c->disable) {
+               MSG(c, "checker disabled");
                return PATH_UNCHECKED;
+       }
        if (c->fd <= 0) {
                MSG(c, "no usable fd");
                return PATH_WILD;
@@ -170,28 +248,44 @@ int checker_check (struct checker * c)
 
 int checker_selected (struct checker * c)
 {
+       if (!c)
+               return 0;
        return (c->check) ? 1 : 0;
 }
 
 char * checker_name (struct checker * c)
 {
+       if (!c)
+               return NULL;
        return c->name;
 }
 
 char * checker_message (struct checker * c)
 {
+       if (!c)
+               return NULL;
        return c->message;
 }
 
 void checker_clear_message (struct checker *c)
 {
+       if (!c)
+               return;
        c->message[0] = '\0';
 }
 
-void checker_get (struct checker * dst, char * name)
+void checker_get (char *multipath_dir, struct checker * dst, char * name)
 {
-       struct checker * src = checker_lookup(name);
+       struct checker * src = NULL;
 
+       if (!dst)
+               return;
+
+       if (name && strlen(name)) {
+               src = checker_lookup(name);
+               if (!src)
+                       src = add_checker(multipath_dir, name);
+       }
        if (!src) {
                dst->check = NULL;
                return;
@@ -200,7 +294,10 @@ void checker_get (struct checker * dst, char * name)
        dst->sync = src->sync;
        strncpy(dst->name, src->name, CHECKER_NAME_LEN);
        strncpy(dst->message, src->message, CHECKER_MSG_LEN);
+       dst->repair = src->repair;
        dst->check = src->check;
        dst->init = src->init;
        dst->free = src->free;
+       dst->handle = NULL;
+       src->refcount++;
 }