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