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