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