multipathd: Remove handling of 'umount' events
[multipath-tools/.git] / multipathd / cli.c
1 /*
2  * Copyright (c) 2005 Christophe Varoqui
3  */
4 #include <memory.h>
5 #include <vector.h>
6 #include <parser.h>
7 #include <util.h>
8 #include <version.h>
9 #include <readline/readline.h>
10
11 #include "cli.h"
12
13 static vector keys;
14 static vector handlers;
15
16 static struct key *
17 alloc_key (void)
18 {
19         return (struct key *)MALLOC(sizeof(struct key));
20 }
21
22 static struct handler *
23 alloc_handler (void)
24 {
25         return (struct handler *)MALLOC(sizeof(struct handler));
26 }
27
28 static int
29 add_key (vector vec, char * str, int code, int has_param)
30 {
31         struct key * kw;
32
33         kw = alloc_key();
34
35         if (!kw)
36                 return 1;
37
38         kw->code = code;
39         kw->has_param = has_param;
40         kw->str = STRDUP(str);
41
42         if (!kw->str)
43                 goto out;
44
45         if (!vector_alloc_slot(vec))
46                 goto out1;
47
48         vector_set_slot(vec, kw);
49
50         return 0;
51
52 out1:
53         FREE(kw->str);
54 out:
55         FREE(kw);
56         return 1;
57 }
58
59 int
60 add_handler (int fp, int (*fn)(void *, char **, int *, void *))
61 {
62         struct handler * h;
63
64         h = alloc_handler();
65
66         if (!h)
67                 return 1;
68
69         if (!vector_alloc_slot(handlers)) {
70                 FREE(h);
71                 return 1;
72         }
73
74         vector_set_slot(handlers, h);
75         h->fingerprint = fp;
76         h->fn = fn;
77
78         return 0;
79 }
80
81 static struct handler *
82 find_handler (int fp)
83 {
84         int i;
85         struct handler *h;
86
87         vector_foreach_slot (handlers, h, i)
88                 if (h->fingerprint == fp)
89                         return h;
90
91         return NULL;
92 }
93
94 int
95 set_handler_callback (int fp, int (*fn)(void *, char **, int *, void *))
96 {
97         struct handler * h = find_handler(fp);
98
99         if (!h)
100                 return 1;
101         h->fn = fn;
102         return 0;
103 }
104
105 static void
106 free_key (struct key * kw)
107 {
108         if (kw->str)
109                 FREE(kw->str);
110
111         if (kw->param)
112                 FREE(kw->param);
113
114         FREE(kw);
115 }
116
117 void
118 free_keys (vector vec)
119 {
120         int i;
121         struct key * kw;
122
123         vector_foreach_slot (vec, kw, i)
124                 free_key(kw);
125
126         vector_free(vec);
127 }
128
129 void
130 free_handlers (void)
131 {
132         int i;
133         struct handler * h;
134
135         vector_foreach_slot (handlers, h, i)
136                 FREE(h);
137
138         vector_free(handlers);
139         handlers = NULL;
140 }
141
142 int
143 load_keys (void)
144 {
145         int r = 0;
146         keys = vector_alloc();
147
148         if (!keys)
149                 return 1;
150
151         r += add_key(keys, "list", LIST, 0);
152         r += add_key(keys, "show", LIST, 0);
153         r += add_key(keys, "add", ADD, 0);
154         r += add_key(keys, "remove", DEL, 0);
155         r += add_key(keys, "del", DEL, 0);
156         r += add_key(keys, "switch", SWITCH, 0);
157         r += add_key(keys, "switchgroup", SWITCH, 0);
158         r += add_key(keys, "suspend", SUSPEND, 0);
159         r += add_key(keys, "resume", RESUME, 0);
160         r += add_key(keys, "reinstate", REINSTATE, 0);
161         r += add_key(keys, "fail", FAIL, 0);
162         r += add_key(keys, "resize", RESIZE, 0);
163         r += add_key(keys, "disablequeueing", DISABLEQ, 0);
164         r += add_key(keys, "restorequeueing", RESTOREQ, 0);
165         r += add_key(keys, "paths", PATHS, 0);
166         r += add_key(keys, "maps", MAPS, 0);
167         r += add_key(keys, "multipaths", MAPS, 0);
168         r += add_key(keys, "path", PATH, 1);
169         r += add_key(keys, "map", MAP, 1);
170         r += add_key(keys, "multipath", MAP, 1);
171         r += add_key(keys, "group", GROUP, 1);
172         r += add_key(keys, "reconfigure", RECONFIGURE, 0);
173         r += add_key(keys, "status", STATUS, 0);
174         r += add_key(keys, "stats", STATS, 0);
175         r += add_key(keys, "topology", TOPOLOGY, 0);
176         r += add_key(keys, "config", CONFIG, 0);
177         r += add_key(keys, "blacklist", BLACKLIST, 0);
178         r += add_key(keys, "devices", DEVICES, 0);
179         r += add_key(keys, "format", FMT, 1);
180         r += add_key(keys, "wildcards", WILDCARDS, 0);
181         r += add_key(keys, "quit", QUIT, 0);
182         r += add_key(keys, "exit", QUIT, 0);
183
184         if (r) {
185                 free_keys(keys);
186                 keys = NULL;
187                 return 1;
188         }
189         return 0;
190 }
191
192 static struct key *
193 find_key (const char * str)
194 {
195         int i;
196         int len, klen;
197         struct key * kw = NULL;
198         struct key * foundkw = NULL;
199
200         len = strlen(str);
201
202         vector_foreach_slot (keys, kw, i) {
203                 if (strncmp(kw->str, str, len))
204                         continue;
205                 klen = strlen(kw->str);
206                 if (len == klen)
207                         return kw; /* exact match */
208                 if (len < klen) {
209                         if (!foundkw)
210                                 foundkw = kw; /* shortcut match */
211                         else
212                                 return NULL; /* ambiguous word */
213                 }
214         }
215         return foundkw;
216 }
217
218 #define E_SYNTAX        1
219 #define E_NOPARM        2
220 #define E_NOMEM         3
221
222 static int
223 get_cmdvec (char * cmd, vector *v)
224 {
225         int i;
226         int r = 0;
227         int get_param = 0;
228         char * buff;
229         struct key * kw = NULL;
230         struct key * cmdkw = NULL;
231         vector cmdvec, strvec;
232
233         strvec = alloc_strvec(cmd);
234         if (!strvec)
235                 return 0;
236
237         cmdvec = vector_alloc();
238         *v = cmdvec;
239
240         if (!cmdvec) {
241                 free_strvec(strvec);
242                 return E_NOMEM;
243         }
244
245         vector_foreach_slot(strvec, buff, i) {
246                 if (*buff == '"')
247                         continue;
248                 if (get_param) {
249                         get_param = 0;
250                         cmdkw->param = strdup(buff);
251                         continue;
252                 }
253                 kw = find_key(buff);
254                 if (!kw) {
255                         r = E_SYNTAX;
256                         goto out;
257                 }
258                 cmdkw = alloc_key();
259                 if (!cmdkw) {
260                         r = E_NOMEM;
261                         goto out;
262                 }
263                 if (!vector_alloc_slot(cmdvec)) {
264                         FREE(cmdkw);
265                         r = E_NOMEM;
266                         goto out;
267                 }
268                 vector_set_slot(cmdvec, cmdkw);
269                 cmdkw->code = kw->code;
270                 cmdkw->has_param = kw->has_param;
271                 if (kw->has_param)
272                         get_param = 1;
273         }
274         if (get_param) {
275                 r = E_NOPARM;
276                 goto out;
277         }
278         free_strvec(strvec);
279         return 0;
280
281 out:
282         free_strvec(strvec);
283         free_keys(cmdvec);
284         *v = NULL;
285         return r;
286 }
287
288 static int 
289 fingerprint(vector vec)
290 {
291         int i;
292         int fp = 0;
293         struct key * kw;
294
295         if (!vec)
296                 return 0;
297
298         vector_foreach_slot(vec, kw, i)
299                 fp += kw->code;
300
301         return fp;
302 }
303
304 int
305 alloc_handlers (void)
306 {
307         handlers = vector_alloc();
308
309         if (!handlers)
310                 return 1;
311
312         return 0;
313 }
314
315 static int
316 genhelp_sprint_aliases (char * reply, vector keys, struct key * refkw)
317 {
318         int i, fwd = 0;
319         struct key * kw;
320
321         vector_foreach_slot (keys, kw, i)
322                 if (kw->code == refkw->code && kw != refkw)
323                         fwd += sprintf(reply, "|%s", kw->str);
324
325         return fwd;
326 }
327
328 static char *
329 genhelp_handler (void)
330 {
331         int i, j;
332         int fp;
333         struct handler * h;
334         struct key * kw;
335         char * reply;
336         char * p;
337
338         reply = MALLOC(INITIAL_REPLY_LEN);
339
340         if (!reply)
341                 return NULL;
342
343         p = reply;
344         p += sprintf(p, VERSION_STRING);
345         p += sprintf(p, "CLI commands reference:\n");
346
347         vector_foreach_slot (handlers, h, i) {
348                 fp = h->fingerprint;
349                 vector_foreach_slot (keys, kw, j) {
350                         if ((kw->code & fp)) {
351                                 fp -= kw->code;
352                                 p += sprintf(p, " %s", kw->str);
353                                 p += genhelp_sprint_aliases(p, keys, kw);
354
355                                 if (kw->has_param)
356                                         p += sprintf(p, " $%s", kw->str);
357                         }
358                 }
359                 p += sprintf(p, "\n");
360         }
361
362         return reply;
363 }
364
365 int
366 parse_cmd (char * cmd, char ** reply, int * len, void * data)
367 {
368         int r;
369         struct handler * h;
370         vector cmdvec = NULL;
371
372         r = get_cmdvec(cmd, &cmdvec);
373
374         if (r) {
375                 if (cmdvec)
376                         free_keys(cmdvec);
377                 *reply = genhelp_handler();
378                 *len = strlen(*reply) + 1;
379                 return 0;
380         }
381
382         h = find_handler(fingerprint(cmdvec));
383
384         if (!h) {
385                 *reply = genhelp_handler();
386                 *len = strlen(*reply) + 1;
387                 free_keys(cmdvec);
388                 return 0;
389         }
390
391         /*
392          * execute handler
393          */
394         r = h->fn(cmdvec, reply, len, data);
395         free_keys(cmdvec);
396
397         return r;
398 }
399
400 char *
401 get_keyparam (vector v, int code)
402 {
403         struct key * kw;
404         int i;
405
406         vector_foreach_slot(v, kw, i)
407                 if (kw->code == code)
408                         return kw->param;
409
410         return NULL;
411 }
412
413 int
414 cli_init (void) {
415         if (load_keys())
416                 return 1;
417
418         if (alloc_handlers())
419                 return 1;
420
421         add_handler(LIST+PATHS, NULL);
422         add_handler(LIST+PATHS+FMT, NULL);
423         add_handler(LIST+STATUS, NULL);
424         add_handler(LIST+MAPS, NULL);
425         add_handler(LIST+MAPS+STATUS, NULL);
426         add_handler(LIST+MAPS+STATS, NULL);
427         add_handler(LIST+MAPS+FMT, NULL);
428         add_handler(LIST+MAPS+TOPOLOGY, NULL);
429         add_handler(LIST+TOPOLOGY, NULL);
430         add_handler(LIST+MAP+TOPOLOGY, NULL);
431         add_handler(LIST+CONFIG, NULL);
432         add_handler(LIST+BLACKLIST, NULL);
433         add_handler(LIST+DEVICES, NULL);
434         add_handler(LIST+WILDCARDS, NULL);
435         add_handler(ADD+PATH, NULL);
436         add_handler(DEL+PATH, NULL);
437         add_handler(ADD+MAP, NULL);
438         add_handler(DEL+MAP, NULL);
439         add_handler(SWITCH+MAP+GROUP, NULL);
440         add_handler(RECONFIGURE, NULL);
441         add_handler(SUSPEND+MAP, NULL);
442         add_handler(RESUME+MAP, NULL);
443         add_handler(RESIZE+MAP, NULL);
444         add_handler(DISABLEQ+MAP, NULL);
445         add_handler(RESTOREQ+MAP, NULL);
446         add_handler(DISABLEQ+MAPS, NULL);
447         add_handler(RESTOREQ+MAPS, NULL);
448         add_handler(REINSTATE+PATH, NULL);
449         add_handler(FAIL+PATH, NULL);
450         add_handler(QUIT, NULL);
451
452         return 0;
453 }
454
455 void cli_exit(void)
456 {
457         free_handlers();
458         free_keys(keys);
459         keys = NULL;
460 }
461
462 static int
463 key_match_fingerprint (struct key * kw, int fp)
464 {
465         if (!fp)
466                 return 0;
467
468         return ((fp & kw->code) == kw->code);
469 }
470
471 /*
472  * This is the readline completion handler
473  */
474 char *
475 key_generator (const char * str, int state)
476 {
477         static int index, len, rlfp, has_param;
478         struct key * kw;
479         int i;
480         struct handler *h;
481         vector v = NULL;
482
483         if (!state) {
484                 index = 0;
485                 has_param = 0;
486                 rlfp = 0;
487                 len = strlen(str);
488                 int r = get_cmdvec(rl_line_buffer, &v);
489                 /*
490                  * If a word completion is in progess, we don't want
491                  * to take an exact keyword match in the fingerprint.
492                  * For ex "show map[tab]" would validate "map" and discard
493                  * "maps" as a valid candidate.
494                  */
495                 if (v && len)
496                         vector_del_slot(v, VECTOR_SIZE(v) - 1);
497                 /*
498                  * Clean up the mess if we dropped the last slot of a 1-slot
499                  * vector
500                  */
501                 if (v && !VECTOR_SIZE(v)) {
502                         vector_free(v);
503                         v = NULL;
504                 }
505                 /*
506                  * If last keyword takes a param, don't even try to guess
507                  */
508                 if (r == E_NOPARM) {
509                         has_param = 1;
510                         return (strdup("(value)"));
511                 }
512                 /*
513                  * Compute a command fingerprint to find out possible completions.
514                  * Once done, the vector is useless. Free it.
515                  */
516                 if (v) {
517                         rlfp = fingerprint(v);
518                         free_keys(v);
519                 }
520         }
521         /*
522          * No more completions for parameter placeholder.
523          * Brave souls might try to add parameter completion by walking paths and
524          * multipaths vectors.
525          */
526         if (has_param)
527                 return ((char *)NULL);
528         /*
529          * Loop through keywords for completion candidates
530          */
531         vector_foreach_slot_after (keys, kw, index) {
532                 if (!strncmp(kw->str, str, len)) {
533                         /*
534                          * Discard keywords already in the command line
535                          */
536                         if (key_match_fingerprint(kw, rlfp)) {
537                                 struct key * curkw = find_key(str);
538                                 if (!curkw || (curkw != kw))
539                                         continue;
540                         }
541                         /*
542                          * Discard keywords making syntax errors.
543                          *
544                          * nfp is the candidate fingerprint we try to
545                          * validate against all known command fingerprints.
546                          */
547                         int nfp = rlfp | kw->code;
548                         vector_foreach_slot(handlers, h, i) {
549                                 if (!rlfp || ((h->fingerprint & nfp) == nfp)) {
550                                         /*
551                                          * At least one full command is
552                                          * possible with this keyword :
553                                          * Consider it validated
554                                          */
555                                         index++;
556                                         return (strdup(kw->str));
557                                 }
558                         }
559                 }
560         }
561         /*
562          * No more candidates
563          */
564         return ((char *)NULL);
565 }
566