3316004d188bd30ee8639300a8b951f4fd4318db
[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 E_NOMEM;
236
237         cmdvec = vector_alloc();
238
239         if (!cmdvec) {
240                 free_strvec(strvec);
241                 return E_NOMEM;
242         }
243
244         vector_foreach_slot(strvec, buff, i) {
245                 if (*buff == '"')
246                         continue;
247                 if (get_param) {
248                         get_param = 0;
249                         cmdkw->param = strdup(buff);
250                         continue;
251                 }
252                 kw = find_key(buff);
253                 if (!kw) {
254                         r = E_SYNTAX;
255                         goto out;
256                 }
257                 cmdkw = alloc_key();
258                 if (!cmdkw) {
259                         r = E_NOMEM;
260                         goto out;
261                 }
262                 if (!vector_alloc_slot(cmdvec)) {
263                         FREE(cmdkw);
264                         r = E_NOMEM;
265                         goto out;
266                 }
267                 vector_set_slot(cmdvec, cmdkw);
268                 cmdkw->code = kw->code;
269                 cmdkw->has_param = kw->has_param;
270                 if (kw->has_param)
271                         get_param = 1;
272         }
273         if (get_param) {
274                 r = E_NOPARM;
275                 goto out;
276         }
277         *v = cmdvec;
278         free_strvec(strvec);
279         return 0;
280
281 out:
282         free_strvec(strvec);
283         free_keys(cmdvec);
284         return r;
285 }
286
287 static int
288 fingerprint(vector vec)
289 {
290         int i;
291         int fp = 0;
292         struct key * kw;
293
294         if (!vec)
295                 return 0;
296
297         vector_foreach_slot(vec, kw, i)
298                 fp += kw->code;
299
300         return fp;
301 }
302
303 int
304 alloc_handlers (void)
305 {
306         handlers = vector_alloc();
307
308         if (!handlers)
309                 return 1;
310
311         return 0;
312 }
313
314 static int
315 genhelp_sprint_aliases (char * reply, vector keys, struct key * refkw)
316 {
317         int i, fwd = 0;
318         struct key * kw;
319
320         vector_foreach_slot (keys, kw, i)
321                 if (kw->code == refkw->code && kw != refkw)
322                         fwd += sprintf(reply, "|%s", kw->str);
323
324         return fwd;
325 }
326
327 static char *
328 genhelp_handler (void)
329 {
330         int i, j;
331         int fp;
332         struct handler * h;
333         struct key * kw;
334         char * reply;
335         char * p;
336
337         reply = MALLOC(INITIAL_REPLY_LEN);
338
339         if (!reply)
340                 return NULL;
341
342         p = reply;
343         p += sprintf(p, VERSION_STRING);
344         p += sprintf(p, "CLI commands reference:\n");
345
346         vector_foreach_slot (handlers, h, i) {
347                 fp = h->fingerprint;
348                 vector_foreach_slot (keys, kw, j) {
349                         if ((kw->code & fp)) {
350                                 fp -= kw->code;
351                                 p += sprintf(p, " %s", kw->str);
352                                 p += genhelp_sprint_aliases(p, keys, kw);
353
354                                 if (kw->has_param)
355                                         p += sprintf(p, " $%s", kw->str);
356                         }
357                 }
358                 p += sprintf(p, "\n");
359         }
360
361         return reply;
362 }
363
364 int
365 parse_cmd (char * cmd, char ** reply, int * len, void * data)
366 {
367         int r;
368         struct handler * h;
369         vector cmdvec = NULL;
370
371         r = get_cmdvec(cmd, &cmdvec);
372
373         if (r) {
374                 *reply = genhelp_handler();
375                 *len = strlen(*reply) + 1;
376                 return 0;
377         }
378
379         h = find_handler(fingerprint(cmdvec));
380
381         if (!h || !h->fn) {
382                 *reply = genhelp_handler();
383                 *len = strlen(*reply) + 1;
384                 free_keys(cmdvec);
385                 return 0;
386         }
387
388         /*
389          * execute handler
390          */
391         r = h->fn(cmdvec, reply, len, data);
392         free_keys(cmdvec);
393
394         return r;
395 }
396
397 char *
398 get_keyparam (vector v, int code)
399 {
400         struct key * kw;
401         int i;
402
403         vector_foreach_slot(v, kw, i)
404                 if (kw->code == code)
405                         return kw->param;
406
407         return NULL;
408 }
409
410 int
411 cli_init (void) {
412         if (load_keys())
413                 return 1;
414
415         if (alloc_handlers())
416                 return 1;
417
418         add_handler(LIST+PATHS, NULL);
419         add_handler(LIST+PATHS+FMT, NULL);
420         add_handler(LIST+STATUS, NULL);
421         add_handler(LIST+MAPS, NULL);
422         add_handler(LIST+MAPS+STATUS, NULL);
423         add_handler(LIST+MAPS+STATS, NULL);
424         add_handler(LIST+MAPS+FMT, NULL);
425         add_handler(LIST+MAPS+TOPOLOGY, NULL);
426         add_handler(LIST+TOPOLOGY, NULL);
427         add_handler(LIST+MAP+TOPOLOGY, NULL);
428         add_handler(LIST+CONFIG, NULL);
429         add_handler(LIST+BLACKLIST, NULL);
430         add_handler(LIST+DEVICES, NULL);
431         add_handler(LIST+WILDCARDS, NULL);
432         add_handler(ADD+PATH, NULL);
433         add_handler(DEL+PATH, NULL);
434         add_handler(ADD+MAP, NULL);
435         add_handler(DEL+MAP, NULL);
436         add_handler(SWITCH+MAP+GROUP, NULL);
437         add_handler(RECONFIGURE, NULL);
438         add_handler(SUSPEND+MAP, NULL);
439         add_handler(RESUME+MAP, NULL);
440         add_handler(RESIZE+MAP, NULL);
441         add_handler(DISABLEQ+MAP, NULL);
442         add_handler(RESTOREQ+MAP, NULL);
443         add_handler(DISABLEQ+MAPS, NULL);
444         add_handler(RESTOREQ+MAPS, NULL);
445         add_handler(REINSTATE+PATH, NULL);
446         add_handler(FAIL+PATH, NULL);
447         add_handler(QUIT, NULL);
448
449         return 0;
450 }
451
452 void cli_exit(void)
453 {
454         free_handlers();
455         free_keys(keys);
456         keys = NULL;
457 }
458
459 static int
460 key_match_fingerprint (struct key * kw, int fp)
461 {
462         if (!fp)
463                 return 0;
464
465         return ((fp & kw->code) == kw->code);
466 }
467
468 /*
469  * This is the readline completion handler
470  */
471 char *
472 key_generator (const char * str, int state)
473 {
474         static int index, len, rlfp, has_param;
475         struct key * kw;
476         int i;
477         struct handler *h;
478         vector v = NULL;
479
480         if (!state) {
481                 index = 0;
482                 has_param = 0;
483                 rlfp = 0;
484                 len = strlen(str);
485                 int r = get_cmdvec(rl_line_buffer, &v);
486                 /*
487                  * If a word completion is in progess, we don't want
488                  * to take an exact keyword match in the fingerprint.
489                  * For ex "show map[tab]" would validate "map" and discard
490                  * "maps" as a valid candidate.
491                  */
492                 if (v && len)
493                         vector_del_slot(v, VECTOR_SIZE(v) - 1);
494                 /*
495                  * Clean up the mess if we dropped the last slot of a 1-slot
496                  * vector
497                  */
498                 if (v && !VECTOR_SIZE(v)) {
499                         vector_free(v);
500                         v = NULL;
501                 }
502                 /*
503                  * If last keyword takes a param, don't even try to guess
504                  */
505                 if (r == E_NOPARM) {
506                         has_param = 1;
507                         return (strdup("(value)"));
508                 }
509                 /*
510                  * Compute a command fingerprint to find out possible completions.
511                  * Once done, the vector is useless. Free it.
512                  */
513                 if (v) {
514                         rlfp = fingerprint(v);
515                         free_keys(v);
516                 }
517         }
518         /*
519          * No more completions for parameter placeholder.
520          * Brave souls might try to add parameter completion by walking paths and
521          * multipaths vectors.
522          */
523         if (has_param)
524                 return ((char *)NULL);
525         /*
526          * Loop through keywords for completion candidates
527          */
528         vector_foreach_slot_after (keys, kw, index) {
529                 if (!strncmp(kw->str, str, len)) {
530                         /*
531                          * Discard keywords already in the command line
532                          */
533                         if (key_match_fingerprint(kw, rlfp)) {
534                                 struct key * curkw = find_key(str);
535                                 if (!curkw || (curkw != kw))
536                                         continue;
537                         }
538                         /*
539                          * Discard keywords making syntax errors.
540                          *
541                          * nfp is the candidate fingerprint we try to
542                          * validate against all known command fingerprints.
543                          */
544                         int nfp = rlfp | kw->code;
545                         vector_foreach_slot(handlers, h, i) {
546                                 if (!rlfp || ((h->fingerprint & nfp) == nfp)) {
547                                         /*
548                                          * At least one full command is
549                                          * possible with this keyword :
550                                          * Consider it validated
551                                          */
552                                         index++;
553                                         return (strdup(kw->str));
554                                 }
555                         }
556                 }
557         }
558         /*
559          * No more candidates
560          */
561         return ((char *)NULL);
562 }
563