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