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