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