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