libmultipath: change loading and resetting in directio
[multipath-tools/.git] / libmultipath / checkers.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stddef.h>
4 #include <dlfcn.h>
5 #include <sys/stat.h>
6
7 #include "debug.h"
8 #include "checkers.h"
9 #include "vector.h"
10
11 struct checker_class {
12         struct list_head node;
13         void *handle;
14         int refcount;
15         int sync;
16         char name[CHECKER_NAME_LEN];
17         int (*check)(struct checker *);
18         int (*init)(struct checker *);       /* to allocate the context */
19         int (*mp_init)(struct checker *);    /* to allocate the mpcontext */
20         void (*free)(struct checker *);      /* to free the context */
21         void (*reset)(void);                 /* to reset the global variables */
22         const char **msgtable;
23         short msgtable_size;
24 };
25
26 char *checker_state_names[] = {
27         "wild",
28         "unchecked",
29         "down",
30         "up",
31         "shaky",
32         "ghost",
33         "pending",
34         "timeout",
35         "removed",
36         "delayed",
37 };
38
39 static LIST_HEAD(checkers);
40
41 const char *checker_state_name(int i)
42 {
43         return checker_state_names[i];
44 }
45
46 static struct checker_class *alloc_checker_class(void)
47 {
48         struct checker_class *c;
49
50         c = MALLOC(sizeof(struct checker_class));
51         if (c) {
52                 INIT_LIST_HEAD(&c->node);
53                 c->refcount = 1;
54         }
55         return c;
56 }
57
58 void free_checker_class(struct checker_class *c)
59 {
60         if (!c)
61                 return;
62         c->refcount--;
63         if (c->refcount) {
64                 condlog(4, "%s checker refcount %d",
65                         c->name, c->refcount);
66                 return;
67         }
68         condlog(3, "unloading %s checker", c->name);
69         list_del(&c->node);
70         if (c->reset)
71                 c->reset();
72         if (c->handle) {
73                 if (dlclose(c->handle) != 0) {
74                         condlog(0, "Cannot unload checker %s: %s",
75                                 c->name, dlerror());
76                 }
77         }
78         FREE(c);
79 }
80
81 void cleanup_checkers (void)
82 {
83         struct checker_class *checker_loop;
84         struct checker_class *checker_temp;
85
86         list_for_each_entry_safe(checker_loop, checker_temp, &checkers, node) {
87                 free_checker_class(checker_loop);
88         }
89 }
90
91 static struct checker_class *checker_class_lookup(const char *name)
92 {
93         struct checker_class *c;
94
95         if (!name || !strlen(name))
96                 return NULL;
97         list_for_each_entry(c, &checkers, node) {
98                 if (!strncmp(name, c->name, CHECKER_NAME_LEN))
99                         return c;
100         }
101         return NULL;
102 }
103
104 void reset_checker_classes(void)
105 {
106         struct checker_class *c;
107
108         list_for_each_entry(c, &checkers, node) {
109                 if (c->reset)
110                         c->reset();
111         }
112 }
113
114 static struct checker_class *add_checker_class(const char *multipath_dir,
115                                                const char *name)
116 {
117         char libname[LIB_CHECKER_NAMELEN];
118         struct stat stbuf;
119         struct checker_class *c;
120         char *errstr;
121
122         c = alloc_checker_class();
123         if (!c)
124                 return NULL;
125         snprintf(c->name, CHECKER_NAME_LEN, "%s", name);
126         if (!strncmp(c->name, NONE, 4))
127                 goto done;
128         snprintf(libname, LIB_CHECKER_NAMELEN, "%s/libcheck%s.so",
129                  multipath_dir, name);
130         if (stat(libname,&stbuf) < 0) {
131                 condlog(0,"Checker '%s' not found in %s",
132                         name, multipath_dir);
133                 goto out;
134         }
135         condlog(3, "loading %s checker", libname);
136         c->handle = dlopen(libname, RTLD_NOW);
137         if (!c->handle) {
138                 if ((errstr = dlerror()) != NULL)
139                         condlog(0, "A dynamic linking error occurred: (%s)",
140                                 errstr);
141                 goto out;
142         }
143         c->check = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_check");
144         errstr = dlerror();
145         if (errstr != NULL)
146                 condlog(0, "A dynamic linking error occurred: (%s)", errstr);
147         if (!c->check)
148                 goto out;
149
150         c->init = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_init");
151         errstr = dlerror();
152         if (errstr != NULL)
153                 condlog(0, "A dynamic linking error occurred: (%s)", errstr);
154         if (!c->init)
155                 goto out;
156
157         c->mp_init = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_mp_init");
158         c->reset = (void (*)(void)) dlsym(c->handle, "libcheck_reset");
159         /* These 2 functions can be NULL. call dlerror() to clear out any
160          * error string */
161         dlerror();
162
163         c->free = (void (*)(struct checker *)) dlsym(c->handle, "libcheck_free");
164         errstr = dlerror();
165         if (errstr != NULL)
166                 condlog(0, "A dynamic linking error occurred: (%s)", errstr);
167         if (!c->free)
168                 goto out;
169
170         c->msgtable_size = 0;
171         c->msgtable = dlsym(c->handle, "libcheck_msgtable");
172
173         if (c->msgtable != NULL) {
174                 const char **p;
175
176                 for (p = c->msgtable;
177                      *p && (p - c->msgtable) < CHECKER_MSGTABLE_SIZE; p++)
178                         /* nothing */;
179
180                 c->msgtable_size = p - c->msgtable;
181         } else
182                 c->msgtable_size = 0;
183         condlog(3, "checker %s: message table size = %d",
184                 c->name, c->msgtable_size);
185
186 done:
187         c->sync = 1;
188         list_add(&c->node, &checkers);
189         return c;
190 out:
191         free_checker_class(c);
192         return NULL;
193 }
194
195 void checker_set_fd (struct checker * c, int fd)
196 {
197         if (!c)
198                 return;
199         c->fd = fd;
200 }
201
202 void checker_set_sync (struct checker * c)
203 {
204         if (!c || !c->cls)
205                 return;
206         c->cls->sync = 1;
207 }
208
209 void checker_set_async (struct checker * c)
210 {
211         if (!c || !c->cls)
212                 return;
213         c->cls->sync = 0;
214 }
215
216 void checker_enable (struct checker * c)
217 {
218         if (!c)
219                 return;
220         c->disable = 0;
221 }
222
223 void checker_disable (struct checker * c)
224 {
225         if (!c)
226                 return;
227         c->disable = 1;
228 }
229
230 int checker_init (struct checker * c, void ** mpctxt_addr)
231 {
232         if (!c || !c->cls)
233                 return 1;
234         c->mpcontext = mpctxt_addr;
235         if (c->cls->init && c->cls->init(c) != 0)
236                 return 1;
237         if (mpctxt_addr && *mpctxt_addr == NULL && c->cls->mp_init &&
238             c->cls->mp_init(c) != 0) /* continue even if mp_init fails */
239                 c->mpcontext = NULL;
240         return 0;
241 }
242
243 int checker_mp_init(struct checker * c, void ** mpctxt_addr)
244 {
245         if (!c || !c->cls)
246                 return 1;
247         if (c->mpcontext || !mpctxt_addr)
248                 return 0;
249         c->mpcontext = mpctxt_addr;
250         if (*mpctxt_addr == NULL && c->cls->mp_init &&
251             c->cls->mp_init(c) != 0) {
252                 c->mpcontext = NULL;
253                 return 1;
254         }
255         return 0;
256 }
257
258 void checker_clear (struct checker *c)
259 {
260         memset(c, 0x0, sizeof(struct checker));
261         c->fd = -1;
262 }
263
264 void checker_put (struct checker * dst)
265 {
266         struct checker_class *src;
267
268         if (!dst)
269                 return;
270         src = dst->cls;
271
272         if (src && src->free)
273                 src->free(dst);
274         checker_clear(dst);
275         free_checker_class(src);
276 }
277
278 int checker_check (struct checker * c, int path_state)
279 {
280         int r;
281
282         if (!c)
283                 return PATH_WILD;
284
285         c->msgid = CHECKER_MSGID_NONE;
286         if (c->disable) {
287                 c->msgid = CHECKER_MSGID_DISABLED;
288                 return PATH_UNCHECKED;
289         }
290         if (!strncmp(c->cls->name, NONE, 4))
291                 return path_state;
292
293         if (c->fd < 0) {
294                 c->msgid = CHECKER_MSGID_NO_FD;
295                 return PATH_WILD;
296         }
297         r = c->cls->check(c);
298
299         return r;
300 }
301
302 const char *checker_name(const struct checker *c)
303 {
304         if (!c || !c->cls)
305                 return NULL;
306         return c->cls->name;
307 }
308
309 int checker_is_sync(const struct checker *c)
310 {
311         return c && c->cls && c->cls->sync;
312 }
313
314 static const char *generic_msg[CHECKER_GENERIC_MSGTABLE_SIZE] = {
315         [CHECKER_MSGID_NONE] = "",
316         [CHECKER_MSGID_DISABLED] = " is disabled",
317         [CHECKER_MSGID_NO_FD] = " has no usable fd",
318         [CHECKER_MSGID_INVALID] = " provided invalid message id",
319         [CHECKER_MSGID_UP] = " reports path is up",
320         [CHECKER_MSGID_DOWN] = " reports path is down",
321         [CHECKER_MSGID_GHOST] = " reports path is ghost",
322         [CHECKER_MSGID_UNSUPPORTED] = " doesn't support this device",
323 };
324
325 const char *checker_message(const struct checker *c)
326 {
327         int id;
328
329         if (!c || !c->cls || c->msgid < 0 ||
330             (c->msgid >= CHECKER_GENERIC_MSGTABLE_SIZE &&
331              c->msgid < CHECKER_FIRST_MSGID))
332                 goto bad_id;
333
334         if (c->msgid < CHECKER_GENERIC_MSGTABLE_SIZE)
335                 return generic_msg[c->msgid];
336
337         id = c->msgid - CHECKER_FIRST_MSGID;
338         if (id < c->cls->msgtable_size)
339                 return c->cls->msgtable[id];
340
341 bad_id:
342         return generic_msg[CHECKER_MSGID_NONE];
343 }
344
345 void checker_clear_message (struct checker *c)
346 {
347         if (!c)
348                 return;
349         c->msgid = CHECKER_MSGID_NONE;
350 }
351
352 void checker_get(const char *multipath_dir, struct checker *dst,
353                  const char *name)
354 {
355         struct checker_class *src = NULL;
356
357         if (!dst)
358                 return;
359
360         if (name && strlen(name)) {
361                 src = checker_class_lookup(name);
362                 if (!src)
363                         src = add_checker_class(multipath_dir, name);
364         }
365         dst->cls = src;
366         if (!src)
367                 return;
368
369         src->refcount++;
370 }
371
372 int init_checkers(const char *multipath_dir)
373 {
374         if (!add_checker_class(multipath_dir, DEFAULT_CHECKER))
375                 return 1;
376         return 0;
377 }