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