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