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