]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/client/commands.c
65658ee6d0affde2f9bfa6d665a8ca7e3be9b161
[thirdparty/lldpd.git] / src / client / commands.c
1 /* -*- mode: c; c-file-style: "openbsd" -*- */
2 /*
3 * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include "client.h"
19 #include <string.h>
20 #include <sys/queue.h>
21
22 /**
23 * An element of the environment (a key and a value).
24 */
25 struct cmd_env_el {
26 TAILQ_ENTRY(cmd_env_el) next; /**< Next environment element */
27 const char *key; /**< Key for this element */
28 const char *value; /**< Value for this element */
29 };
30
31 /**
32 * A stack element.
33 */
34 struct cmd_env_stack {
35 TAILQ_ENTRY(cmd_env_stack) next; /**< Next element, down the stack */
36 struct cmd_node *el; /**< Stored element */
37 };
38
39 /**
40 * Structure representing an environment for the current command.
41 *
42 * An environment is a list of values stored for use for the function executing
43 * as well as the current command, the current position in the command and a
44 * stack for cmd_node
45 */
46 struct cmd_env {
47 TAILQ_HEAD(, cmd_env_el) elements; /**< List of environment variables */
48 TAILQ_HEAD(, cmd_env_stack) stack; /**< Stack */
49 int argc; /**< Number of argument in the command */
50 int argp; /**< Current argument */
51 const char **argv; /**< Arguments */
52 };
53
54 /**
55 * Structure representing a command node.
56 *
57 * Such a node contains a token accepted to enter the node (or @c NULL if there
58 * is no token needed), a documentation string to present the user, a function
59 * to validate the user input (or @c NULL if no function is needed) and a
60 * function to execute when entering the node. Because we can enter a node just
61 * by completing, the execution part should have no other effect than modifying
62 * the environment, with the exception of execution on @c NEWLINE (which cannot
63 * happen when completing).
64 */
65 struct cmd_node {
66 TAILQ_ENTRY(cmd_node) next; /**< Next sibling */
67
68 const char *token; /**< Token to enter this cnode */
69 const char *doc; /**< Documentation string */
70
71 /**
72 * Function validating entry in this node. Can be @c NULL.
73 */
74 int(*validate)(struct cmd_env*, void *);
75 /**
76 * Function to execute when entering this node. May be @c NULL.
77 *
78 * This function can alter the environment
79 */
80 int(*execute)(struct lldpctl_conn_t*, struct writer*,
81 struct cmd_env*, void *);
82 void *arg; /**< Magic argument for the previous two functions */
83
84 /* List of possible subentries */
85 TAILQ_HEAD(, cmd_node) subentries; /* List of subnodes */
86 };
87
88 /**
89 * Create a root node.
90 *
91 * @return the root node.
92 */
93 struct cmd_node*
94 commands_root(void)
95 {
96 return commands_new(NULL, NULL, NULL, NULL, NULL, NULL);
97 }
98
99 /**
100 * Create a new node.
101 *
102 * @param root The node we want to attach this node.
103 * @param token Token to enter this node. Or @c NULL if no token is needed.
104 * @param doc Documentation for this node.
105 * @param validate Function that should return 1 if we can enter the node.
106 * @param execute Function that should return 1 on successful execution of this node.
107 * @param arg Magic argument for precedent functions.
108 * @return the newly created node
109 */
110 struct cmd_node*
111 commands_new(struct cmd_node *root,
112 const char *token, const char *doc,
113 int(*validate)(struct cmd_env*, void *),
114 int(*execute)(struct lldpctl_conn_t*, struct writer*,
115 struct cmd_env*, void *),
116 void *arg)
117 {
118 struct cmd_node *new = calloc(1, sizeof(struct cmd_node));
119 if (new == NULL) {
120 log_warn("lldpctl", "unable to allocate memory for new command node");
121 return NULL;
122 }
123 new->token = token;
124 new->doc = doc;
125 new->validate = validate;
126 new->execute = execute;
127 new->arg = arg;
128 TAILQ_INIT(&new->subentries);
129 if (root != NULL)
130 TAILQ_INSERT_TAIL(&root->subentries, new, next);
131 return new;
132 }
133
134 /**
135 * Free a command tree.
136 *
137 * @param root The node we want to free.
138 */
139 void
140 commands_free(struct cmd_node *root)
141 {
142 struct cmd_node *subcmd, *subcmd_next;
143 for (subcmd = TAILQ_FIRST(&root->subentries); subcmd != NULL;
144 subcmd = subcmd_next) {
145 subcmd_next = TAILQ_NEXT(subcmd, next);
146 TAILQ_REMOVE(&root->subentries, subcmd, next);
147 commands_free(subcmd);
148 }
149 free(root);
150 }
151
152 /**
153 * Return the current argument in the environment. This can be @c NEWLINE or
154 * @c NULL.
155 *
156 * @param env The environment.
157 * @return current argument.
158 */
159 const char*
160 cmdenv_arg(struct cmd_env *env)
161 {
162 if (env->argp < env->argc)
163 return env->argv[env->argp];
164 if (env->argp == env->argc)
165 return NEWLINE;
166 return NULL;
167 }
168
169 /**
170 * Get a value from the environment.
171 *
172 * @param env The environment.
173 * @param key The key for the requested value.
174 * @return @c NULL if not found or the requested value otherwise. If no value is
175 * associated, return the key.
176 */
177 const char*
178 cmdenv_get(struct cmd_env *env, const char *key)
179 {
180 struct cmd_env_el *el;
181 TAILQ_FOREACH(el, &env->elements, next)
182 if (!strcmp(el->key, key))
183 return el->value ? el->value : el->key;
184 return NULL;
185 }
186
187 /**
188 * Put a value in the environment.
189 *
190 * @param env The environment.
191 * @param key The key for the value.
192 * @param value The value.
193 * @return 0 on success, -1 otherwise.
194 */
195 int
196 cmdenv_put(struct cmd_env *env,
197 const char *key, const char *value)
198 {
199 struct cmd_env_el *el = malloc(sizeof(struct cmd_env_el));
200 if (el == NULL) {
201 log_warn("lldpctl", "unable to allocate memory for new environment variable");
202 return -1;
203 }
204 el->key = key;
205 el->value = value;
206 TAILQ_INSERT_TAIL(&env->elements, el, next);
207 return 0;
208 }
209
210 /**
211 * Pop some node from the execution stack.
212 *
213 * This allows to resume parsing on a previous state. Useful to call after
214 * parsing optional arguments.
215 *
216 * @param env The environment.
217 * @param n How many element we want to pop.
218 * @return 0 on success, -1 otherwise.
219 */
220 int
221 cmdenv_pop(struct cmd_env *env, int n)
222 {
223 while (n-- > 0) {
224 if (TAILQ_EMPTY(&env->stack)) {
225 log_warnx("lldpctl", "environment stack is empty");
226 return -1;
227 }
228 struct cmd_env_stack *first = TAILQ_FIRST(&env->stack);
229 TAILQ_REMOVE(&env->stack,
230 first, next);
231 free(first);
232 }
233 return 0;
234 }
235
236 /**
237 * Push some node on the execution stack.
238 *
239 * @param env The environment.
240 * @param node The node to push.
241 * @return 0 on success, -1 on error.
242 */
243 static int
244 cmdenv_push(struct cmd_env *env, struct cmd_node *node)
245 {
246 struct cmd_env_stack *el = malloc(sizeof(struct cmd_env_stack));
247 if (el == NULL) {
248 log_warn("lldpctl", "not enough memory to allocate a stack element");
249 return -1;
250 }
251 el->el = node;
252 TAILQ_INSERT_HEAD(&env->stack, el, next);
253 return 0;
254 }
255
256 /**
257 * Return the top of the stack, without poping it.
258 *
259 * @param env The environment.
260 * @return the top element or @c NULL is the stack is empty.
261 */
262 static struct cmd_node*
263 cmdenv_top(struct cmd_env *env)
264 {
265 if (TAILQ_EMPTY(&env->stack)) return NULL;
266 return TAILQ_FIRST(&env->stack)->el;
267 }
268
269 /**
270 * Free execution environment.
271 *
272 * @param env The environment.
273 */
274 static void
275 cmdenv_free(struct cmd_env *env)
276 {
277 while (!TAILQ_EMPTY(&env->stack)) cmdenv_pop(env, 1);
278
279 struct cmd_env_el *first;
280 while (!TAILQ_EMPTY(&env->elements)) {
281 first = TAILQ_FIRST(&env->elements);
282 TAILQ_REMOVE(&env->elements, first, next);
283 free(first);
284 }
285 }
286
287 struct candidate_word {
288 TAILQ_ENTRY(candidate_word) next;
289 const char *word;
290 const char *doc;
291 };
292
293 /**
294 * Execute or complete a command from the given node.
295 *
296 * @param conn Connection to lldpd.
297 * @param w Writer for output.
298 * @param root Root node we want to start from.
299 * @param argc Number of arguments.
300 * @param argv Array of arguments.
301 * @param cursorc On which argument the cursor is. -1 if no completion is required.
302 * @param cursoro On which offset the cursor is. -1 if no completion is required.
303 * @param word Completed word. Or NULL when no completion is required.
304 * @param all When completing, display possible completions even if only one choice is possible.
305 * @return 0 on success, -1 otherwise.
306 */
307 static int
308 _commands_execute(struct lldpctl_conn_t *conn, struct writer *w,
309 struct cmd_node *root, int argc, const char **argv,
310 int cursorc, int cursoro, char **word, int all)
311 {
312 int n, rc = 0, completion = (cursorc != -1 && cursoro != -1 && word != NULL);
313 struct cmd_env env = {
314 .elements = TAILQ_HEAD_INITIALIZER(env.elements),
315 .stack = TAILQ_HEAD_INITIALIZER(env.stack),
316 .argc = completion?cursorc:argc,
317 .argv = argv,
318 .argp = 0
319 };
320 cmdenv_push(&env, root);
321 if (!completion)
322 for (n = 0; n < argc; n++)
323 log_debug("lldpctl", "argument %02d: `%s`", n, argv[n]);
324 if (completion) *word = NULL;
325
326 /* When completion is in progress, we use the same algorithm than for
327 * execution until we reach the cursor position. */
328 struct cmd_node *current = NULL;
329 while ((current = cmdenv_top(&env))) {
330 struct cmd_node *candidate, *best = NULL;
331 const char *token = (env.argp < env.argc) ? env.argv[env.argp] :
332 (env.argp == env.argc && !completion) ? NEWLINE : NULL;
333 if (token == NULL) goto end;
334 if (!completion)
335 log_debug("lldpctl", "process argument %02d: `%s`",
336 env.argp, token);
337 TAILQ_FOREACH(candidate, &current->subentries, next) {
338 if (candidate->token &&
339 !strncmp(candidate->token, token, strlen(token))) {
340 if (!candidate->validate ||
341 candidate->validate(&env, candidate->arg) == 1) {
342 if (candidate->token &&
343 !strcmp(candidate->token, token)) {
344 /* Exact match */
345 best = candidate;
346 break;
347 }
348 if (!best) best = candidate;
349 else {
350 if (!completion)
351 log_warnx("lldpctl", "ambiguous token: %s (%s or %s)",
352 token, candidate->token, best->token);
353 rc = -1;
354 goto end;
355 }
356 }
357 }
358 }
359 if (!best) {
360 /* Take first that validate */
361 TAILQ_FOREACH(candidate, &current->subentries, next) {
362 if (!candidate->token && (!candidate->validate ||
363 candidate->validate(&env, candidate->arg) == 1)) {
364 best = candidate;
365 break;
366 }
367 }
368 }
369 if (!best && env.argp == env.argc) goto end;
370 if (!best) {
371 if (!completion)
372 log_warnx("lldpctl", "unknown command from argument %d: `%s`",
373 env.argp + 1, token);
374 rc = -1;
375 goto end;
376 }
377
378 /* Push and execute */
379 cmdenv_push(&env, best);
380 if (best->execute && best->execute(conn, w, &env, best->arg) != 1) {
381 rc = -1;
382 goto end;
383 }
384 env.argp++;
385 }
386 end:
387 if (!completion) {
388 if (rc == 0 && env.argp != env.argc + 1) {
389 log_warnx("lldpctl", "incomplete command");
390 rc = -1;
391 }
392 } else if (rc == 0 && env.argp == env.argc) {
393 /* We need to complete. Let's build the list of candidate words. */
394 struct cmd_node *candidate = NULL;
395 int maxl = 10; /* Max length of a word */
396 TAILQ_HEAD(, candidate_word) words; /* List of subnodes */
397 TAILQ_INIT(&words);
398 current = cmdenv_top(&env);
399 TAILQ_FOREACH(candidate, &current->subentries, next) {
400 if ((!candidate->token ||
401 !strncmp(env.argv[cursorc], candidate->token, cursoro)) &&
402 (!candidate->validate ||
403 candidate->validate(&env, candidate->arg) == 1)) {
404 struct candidate_word *cword = malloc(sizeof(struct candidate_word));
405 if (!cword) break;
406 cword->word = candidate->token;
407 cword->doc = candidate->doc;
408 if (cword->word && strlen(cword->word) > maxl)
409 maxl = strlen(cword->word);
410 TAILQ_INSERT_TAIL(&words, cword, next);
411 }
412 }
413 if (!TAILQ_EMPTY(&words)) {
414 /* Search if there is a common prefix, then return it. */
415 int c = 0;
416 char prefix[maxl + 2]; /* Extra space may be added at the end */
417 struct candidate_word *cword, *cword_next;
418 memset(prefix, 0, maxl+2);
419 for (int n = 0; n < maxl; n++) {
420 c = 1; /* Set to 0 to exit outer loop */
421 TAILQ_FOREACH(cword, &words, next) {
422 c = 0;
423 if (cword->word == NULL) break;
424 if (!strcmp(cword->word, NEWLINE)) break;
425 if (strlen(cword->word) == n) break;
426 if (prefix[n] == '\0') prefix[n] = cword->word[n];
427 else if (prefix[n] != cword->word[n]) break;
428 c = 1;
429 }
430 if (c == 0) {
431 prefix[n] = '\0';
432 break;
433 }
434 }
435 /* If the prefix is complete, add a space, otherwise,
436 * just return it as is. */
437 if (!all && strcmp(prefix, NEWLINE) &&
438 strlen(prefix) > 0 && cursoro < strlen(prefix)) {
439 TAILQ_FOREACH(cword, &words, next) {
440 if (cword->word && !strcmp(prefix, cword->word)) {
441 prefix[strlen(prefix)] = ' ';
442 break;
443 }
444 }
445 *word = strdup(prefix);
446 } else {
447 /* No common prefix, print possible completions */
448 fprintf(stderr, "\n-- \033[1;34m%s\033[0m\n",
449 current->doc ? current->doc : "Help");
450 TAILQ_FOREACH(cword, &words, next) {
451 char fmt[100];
452 snprintf(fmt, sizeof(fmt),
453 "%s%%%ds%s %%s\n",
454 "\033[1;30m", maxl, "\033[0m");
455 fprintf(stderr, fmt,
456 cword->word ? cword->word : "WORD",
457 cword->doc ? cword->doc : "...");
458 }
459 }
460 for (cword = TAILQ_FIRST(&words); cword != NULL;
461 cword = cword_next) {
462 cword_next = TAILQ_NEXT(cword, next);
463 TAILQ_REMOVE(&words, cword, next);
464 free(cword);
465 }
466 }
467 }
468 cmdenv_free(&env);
469 return rc;
470 }
471
472 /**
473 * Complete the given command.
474 */
475 char *
476 commands_complete(struct cmd_node *root, int argc, const char **argv,
477 int cursorc, int cursoro, int all)
478 {
479 char *word = NULL;
480 if (_commands_execute(NULL, NULL, root, argc, argv,
481 cursorc, cursoro, &word, all) == 0)
482 return word;
483 return NULL;
484 }
485
486 /**
487 * Execute the given commands.
488 */
489 int
490 commands_execute(struct lldpctl_conn_t *conn, struct writer *w,
491 struct cmd_node *root, int argc, const char **argv)
492 {
493 return _commands_execute(conn, w, root, argc, argv, -1, -1, NULL, 0);
494 }
495
496 /**
497 * Check if the environment does not contain the given key.
498 *
499 * @param env The environment.
500 * @param arg The key to search for.
501 */
502 int
503 cmd_check_no_env(struct cmd_env *env, void *key)
504 {
505 return cmdenv_get(env, (const char*)key) == NULL;
506 }
507
508 /**
509 * Check if the environment does contain the given key.
510 *
511 * @param env The environment.
512 * @param arg The key to search for. Can be a comma separated list.
513 */
514 int
515 cmd_check_env(struct cmd_env *env, void *key)
516 {
517 struct cmd_env_el *el;
518 const char *list = key;
519 int count = 0;
520 TAILQ_FOREACH(el, &env->elements, next) {
521 if (contains(list, el->key))
522 count++;
523 }
524 while ((list = strchr(list, ','))) { list++; count--; }
525 return (count == 1);
526 }
527
528 /**
529 * Store the given key in the environment.
530 *
531 * @param env The environment.
532 * @param key The key to store.
533 */
534 int
535 cmd_store_env(struct lldpctl_conn_t *conn, struct writer *w,
536 struct cmd_env *env, void *key)
537 {
538 return cmdenv_put(env, key, NULL) != -1;
539 }
540
541 /**
542 * Store the given key in the environment and pop one element from the stack.
543 *
544 * @param env The environment.
545 * @param key The key to store.
546 */
547 int
548 cmd_store_env_and_pop(struct lldpctl_conn_t *conn, struct writer *w,
549 struct cmd_env *env, void *key)
550 {
551 return (cmd_store_env(conn, w, env, key) != -1 &&
552 cmdenv_pop(env, 1) != -1);
553 }
554
555 /**
556 * Store the given key with a value being the current keyword in the environment
557 * and pop X elements from the stack.
558 *
559 * @param env The environment.
560 * @param key The key to store.
561 */
562 int
563 cmd_store_env_value_and_pop(struct lldpctl_conn_t *conn, struct writer *w,
564 struct cmd_env *env, void *key)
565 {
566 return (cmdenv_put(env, key, cmdenv_arg(env)) != -1 &&
567 cmdenv_pop(env, 1) != -1);
568 }
569 int
570 cmd_store_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w,
571 struct cmd_env *env, void *key)
572 {
573 return (cmdenv_put(env, key, cmdenv_arg(env)) != -1 &&
574 cmdenv_pop(env, 2) != -1);
575 }
576 int
577 cmd_store_env_value(struct lldpctl_conn_t *conn, struct writer *w,
578 struct cmd_env *env, void *key)
579 {
580 return (cmdenv_put(env, key, cmdenv_arg(env)) != -1);
581 }
582 int
583 cmd_store_env_value_and_pop3(struct lldpctl_conn_t *conn, struct writer *w,
584 struct cmd_env *env, void *key)
585 {
586 return (cmdenv_put(env, key, cmdenv_arg(env)) != -1 &&
587 cmdenv_pop(env, 3) != -1);
588 }
589
590 /**
591 * Provide an iterator on all interfaces contained in "ports".
592 *
593 * @warning This function is not reentrant. It uses static variables to keep
594 * track of ports that have already been provided. Moreover, to release all
595 * resources, the iterator should be used until its end.
596 *
597 * @param conn The connection.
598 * @param env The environment.
599 * @return The next interface in the set of ports (or in all ports if no `ports`
600 * variable is present in the environment)
601 */
602 lldpctl_atom_t*
603 cmd_iterate_on_interfaces(struct lldpctl_conn_t *conn, struct cmd_env *env)
604 {
605 static lldpctl_atom_iter_t *iter = NULL;
606 static lldpctl_atom_t *iface_list = NULL;
607 static lldpctl_atom_t *iface = NULL;
608 const char *interfaces = cmdenv_get(env, "ports");
609
610 do {
611 if (iter == NULL) {
612 iface_list = lldpctl_get_interfaces(conn);
613 if (!iface_list) {
614 log_warnx("lldpctl", "not able to get the list of interfaces. %s",
615 lldpctl_last_strerror(conn));
616 return NULL;
617 }
618 iter = lldpctl_atom_iter(iface_list);
619 if (!iter) return NULL;
620 } else {
621 iter = lldpctl_atom_iter_next(iface_list, iter);
622 if (iface) lldpctl_atom_dec_ref(iface); iface = NULL;
623 if (!iter) {
624 lldpctl_atom_dec_ref(iface_list);
625 return NULL;
626 }
627 }
628
629 iface = lldpctl_atom_iter_value(iface_list, iter);
630 } while (interfaces && !contains(interfaces,
631 lldpctl_atom_get_str(iface, lldpctl_k_interface_name)));
632
633 return iface;
634 }