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