]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/client/commands.c
clang: additional fixes detected by clang analyzer
[thirdparty/lldpd.git] / src / client / commands.c
CommitLineData
9a775667
VB
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 */
25struct 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 */
34struct 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 */
46struct 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 */
65struct 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 */
93struct cmd_node*
94commands_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 */
110struct cmd_node*
111commands_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 */
139void
140commands_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 */
159const char*
160cmdenv_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 */
177const char*
178cmdenv_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 */
195int
196cmdenv_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 */
220int
221cmdenv_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 */
243static int
244cmdenv_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 */
262static struct cmd_node*
263cmdenv_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 */
274static void
275cmdenv_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
287struct 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.
9a775667
VB
301 * @param word Completed word. Or NULL when no completion is required.
302 * @param all When completing, display possible completions even if only one choice is possible.
303 * @return 0 on success, -1 otherwise.
304 */
305static int
306_commands_execute(struct lldpctl_conn_t *conn, struct writer *w,
307 struct cmd_node *root, int argc, const char **argv,
23e7fa38 308 char **word, int all)
9a775667 309{
23e7fa38 310 int n, rc = 0, completion = (word != NULL);
25272118 311 int help = 0; /* Are we asking for help? */
9a775667
VB
312 struct cmd_env env = {
313 .elements = TAILQ_HEAD_INITIALIZER(env.elements),
314 .stack = TAILQ_HEAD_INITIALIZER(env.stack),
23e7fa38 315 .argc = argc,
9a775667
VB
316 .argv = argv,
317 .argp = 0
318 };
319 cmdenv_push(&env, root);
320 if (!completion)
321 for (n = 0; n < argc; n++)
322 log_debug("lldpctl", "argument %02d: `%s`", n, argv[n]);
323 if (completion) *word = NULL;
324
325 /* When completion is in progress, we use the same algorithm than for
326 * execution until we reach the cursor position. */
327 struct cmd_node *current = NULL;
328 while ((current = cmdenv_top(&env))) {
25272118
VB
329 if (!completion)
330 help = !!cmdenv_get(&env, "help"); /* Are we asking for help? */
331
9a775667
VB
332 struct cmd_node *candidate, *best = NULL;
333 const char *token = (env.argp < env.argc) ? env.argv[env.argp] :
25272118 334 (env.argp == env.argc && !help) ? NEWLINE : NULL;
23e7fa38
VB
335 if (token == NULL ||
336 (completion && env.argp == env.argc - 1))
337 goto end;
9a775667
VB
338 if (!completion)
339 log_debug("lldpctl", "process argument %02d: `%s`",
340 env.argp, token);
341 TAILQ_FOREACH(candidate, &current->subentries, next) {
342 if (candidate->token &&
343 !strncmp(candidate->token, token, strlen(token))) {
344 if (!candidate->validate ||
345 candidate->validate(&env, candidate->arg) == 1) {
346 if (candidate->token &&
347 !strcmp(candidate->token, token)) {
348 /* Exact match */
349 best = candidate;
350 break;
351 }
352 if (!best) best = candidate;
353 else {
354 if (!completion)
355 log_warnx("lldpctl", "ambiguous token: %s (%s or %s)",
356 token, candidate->token, best->token);
357 rc = -1;
358 goto end;
359 }
360 }
361 }
362 }
363 if (!best) {
364 /* Take first that validate */
365 TAILQ_FOREACH(candidate, &current->subentries, next) {
366 if (!candidate->token && (!candidate->validate ||
367 candidate->validate(&env, candidate->arg) == 1)) {
368 best = candidate;
369 break;
370 }
371 }
372 }
373 if (!best && env.argp == env.argc) goto end;
374 if (!best) {
375 if (!completion)
376 log_warnx("lldpctl", "unknown command from argument %d: `%s`",
377 env.argp + 1, token);
378 rc = -1;
379 goto end;
380 }
381
382 /* Push and execute */
383 cmdenv_push(&env, best);
384 if (best->execute && best->execute(conn, w, &env, best->arg) != 1) {
385 rc = -1;
386 goto end;
387 }
388 env.argp++;
389 }
390end:
25272118 391 if (!completion && !help) {
9a775667
VB
392 if (rc == 0 && env.argp != env.argc + 1) {
393 log_warnx("lldpctl", "incomplete command");
394 rc = -1;
395 }
25272118 396 } else if (rc == 0 && (env.argp == env.argc - 1 || help)) {
9a775667
VB
397 /* We need to complete. Let's build the list of candidate words. */
398 struct cmd_node *candidate = NULL;
399 int maxl = 10; /* Max length of a word */
400 TAILQ_HEAD(, candidate_word) words; /* List of subnodes */
401 TAILQ_INIT(&words);
402 current = cmdenv_top(&env);
aa015c26
VB
403 if (!TAILQ_EMPTY(&current->subentries)) {
404 TAILQ_FOREACH(candidate, &current->subentries, next) {
405 if ((!candidate->token || help ||
406 !strncmp(env.argv[env.argc - 1], candidate->token,
407 strlen(env.argv[env.argc -1 ]))) &&
408 (!candidate->validate ||
409 candidate->validate(&env, candidate->arg) == 1)) {
410 struct candidate_word *cword =
411 malloc(sizeof(struct candidate_word));
412 if (!cword) break;
413 cword->word = candidate->token;
414 cword->doc = candidate->doc;
415 if (cword->word && strlen(cword->word) > maxl)
416 maxl = strlen(cword->word);
417 TAILQ_INSERT_TAIL(&words, cword, next);
418 }
9a775667
VB
419 }
420 }
421 if (!TAILQ_EMPTY(&words)) {
422 /* Search if there is a common prefix, then return it. */
423 int c = 0;
424 char prefix[maxl + 2]; /* Extra space may be added at the end */
425 struct candidate_word *cword, *cword_next;
426 memset(prefix, 0, maxl+2);
427 for (int n = 0; n < maxl; n++) {
428 c = 1; /* Set to 0 to exit outer loop */
429 TAILQ_FOREACH(cword, &words, next) {
430 c = 0;
431 if (cword->word == NULL) break;
432 if (!strcmp(cword->word, NEWLINE)) break;
433 if (strlen(cword->word) == n) break;
434 if (prefix[n] == '\0') prefix[n] = cword->word[n];
435 else if (prefix[n] != cword->word[n]) break;
436 c = 1;
437 }
438 if (c == 0) {
439 prefix[n] = '\0';
440 break;
441 }
442 }
443 /* If the prefix is complete, add a space, otherwise,
444 * just return it as is. */
25272118 445 if (!all && !help && strcmp(prefix, NEWLINE) &&
23e7fa38
VB
446 strlen(prefix) > 0 &&
447 strlen(env.argv[env.argc-1]) < strlen(prefix)) {
9a775667
VB
448 TAILQ_FOREACH(cword, &words, next) {
449 if (cword->word && !strcmp(prefix, cword->word)) {
450 prefix[strlen(prefix)] = ' ';
451 break;
452 }
453 }
454 *word = strdup(prefix);
455 } else {
456 /* No common prefix, print possible completions */
457 fprintf(stderr, "\n-- \033[1;34m%s\033[0m\n",
458 current->doc ? current->doc : "Help");
459 TAILQ_FOREACH(cword, &words, next) {
460 char fmt[100];
461 snprintf(fmt, sizeof(fmt),
462 "%s%%%ds%s %%s\n",
463 "\033[1;30m", maxl, "\033[0m");
464 fprintf(stderr, fmt,
465 cword->word ? cword->word : "WORD",
466 cword->doc ? cword->doc : "...");
467 }
468 }
469 for (cword = TAILQ_FIRST(&words); cword != NULL;
470 cword = cword_next) {
471 cword_next = TAILQ_NEXT(cword, next);
472 TAILQ_REMOVE(&words, cword, next);
473 free(cword);
474 }
475 }
476 }
477 cmdenv_free(&env);
478 return rc;
479}
480
481/**
482 * Complete the given command.
483 */
484char *
485commands_complete(struct cmd_node *root, int argc, const char **argv,
23e7fa38 486 int all)
9a775667
VB
487{
488 char *word = NULL;
489 if (_commands_execute(NULL, NULL, root, argc, argv,
23e7fa38 490 &word, all) == 0)
9a775667
VB
491 return word;
492 return NULL;
493}
494
495/**
496 * Execute the given commands.
497 */
498int
499commands_execute(struct lldpctl_conn_t *conn, struct writer *w,
500 struct cmd_node *root, int argc, const char **argv)
501{
23e7fa38 502 return _commands_execute(conn, w, root, argc, argv, NULL, 0);
9a775667
VB
503}
504
9a775667
VB
505/**
506 * Check if the environment does not contain the given key.
507 *
508 * @param env The environment.
9cac8fed
VB
509 * @param key The key to search for.
510 * @return 1 if the environment does not contain the key. 0 otherwise.
9a775667
VB
511 */
512int
513cmd_check_no_env(struct cmd_env *env, void *key)
514{
515 return cmdenv_get(env, (const char*)key) == NULL;
516}
517
518/**
519 * Check if the environment does contain the given key.
520 *
521 * @param env The environment.
9cac8fed
VB
522 * @param key The key to search for. Can be a comma separated list.
523 * @return 1 if the environment does contain the key. 0 otherwise.
9a775667
VB
524 */
525int
526cmd_check_env(struct cmd_env *env, void *key)
527{
528 struct cmd_env_el *el;
529 const char *list = key;
530 int count = 0;
531 TAILQ_FOREACH(el, &env->elements, next) {
532 if (contains(list, el->key))
533 count++;
534 }
535 while ((list = strchr(list, ','))) { list++; count--; }
536 return (count == 1);
537}
538
539/**
540 * Store the given key in the environment.
541 *
9cac8fed
VB
542 * @param conn The connection.
543 * @param w The writer (not used).
9a775667
VB
544 * @param env The environment.
545 * @param key The key to store.
9cac8fed 546 * @return 1 if the key was stored
9a775667
VB
547 */
548int
549cmd_store_env(struct lldpctl_conn_t *conn, struct writer *w,
550 struct cmd_env *env, void *key)
551{
552 return cmdenv_put(env, key, NULL) != -1;
553}
554
555/**
556 * Store the given key in the environment and pop one element from the stack.
557 *
9cac8fed
VB
558 * @param conn The connection.
559 * @param w The writer (not used).
9a775667
VB
560 * @param env The environment.
561 * @param key The key to store.
9cac8fed 562 * @return 1 if the key was stored
9a775667
VB
563 */
564int
565cmd_store_env_and_pop(struct lldpctl_conn_t *conn, struct writer *w,
566 struct cmd_env *env, void *key)
567{
568 return (cmd_store_env(conn, w, env, key) != -1 &&
569 cmdenv_pop(env, 1) != -1);
570}
571
572/**
573 * Store the given key with a value being the current keyword in the environment
574 * and pop X elements from the stack.
575 *
9cac8fed
VB
576 * @param conn The connection.
577 * @param w The writer (not used).
9a775667
VB
578 * @param env The environment.
579 * @param key The key to store.
9cac8fed 580 * @return 1 if the key was stored
9a775667
VB
581 */
582int
583cmd_store_env_value_and_pop(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, 1) != -1);
588}
589int
590cmd_store_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w,
591 struct cmd_env *env, void *key)
592{
593 return (cmdenv_put(env, key, cmdenv_arg(env)) != -1 &&
594 cmdenv_pop(env, 2) != -1);
595}
596int
597cmd_store_env_value(struct lldpctl_conn_t *conn, struct writer *w,
598 struct cmd_env *env, void *key)
599{
600 return (cmdenv_put(env, key, cmdenv_arg(env)) != -1);
601}
602int
603cmd_store_env_value_and_pop3(struct lldpctl_conn_t *conn, struct writer *w,
604 struct cmd_env *env, void *key)
605{
606 return (cmdenv_put(env, key, cmdenv_arg(env)) != -1 &&
607 cmdenv_pop(env, 3) != -1);
608}
609
610/**
611 * Provide an iterator on all interfaces contained in "ports".
612 *
613 * @warning This function is not reentrant. It uses static variables to keep
614 * track of ports that have already been provided. Moreover, to release all
615 * resources, the iterator should be used until its end.
616 *
617 * @param conn The connection.
618 * @param env The environment.
619 * @return The next interface in the set of ports (or in all ports if no `ports`
620 * variable is present in the environment)
621 */
622lldpctl_atom_t*
623cmd_iterate_on_interfaces(struct lldpctl_conn_t *conn, struct cmd_env *env)
624{
625 static lldpctl_atom_iter_t *iter = NULL;
626 static lldpctl_atom_t *iface_list = NULL;
627 static lldpctl_atom_t *iface = NULL;
628 const char *interfaces = cmdenv_get(env, "ports");
629
630 do {
631 if (iter == NULL) {
632 iface_list = lldpctl_get_interfaces(conn);
633 if (!iface_list) {
634 log_warnx("lldpctl", "not able to get the list of interfaces. %s",
635 lldpctl_last_strerror(conn));
636 return NULL;
637 }
638 iter = lldpctl_atom_iter(iface_list);
639 if (!iter) return NULL;
640 } else {
641 iter = lldpctl_atom_iter_next(iface_list, iter);
642 if (iface) lldpctl_atom_dec_ref(iface); iface = NULL;
643 if (!iter) {
644 lldpctl_atom_dec_ref(iface_list);
645 return NULL;
646 }
647 }
648
649 iface = lldpctl_atom_iter_value(iface_list, iter);
650 } while (interfaces && !contains(interfaces,
651 lldpctl_atom_get_str(iface, lldpctl_k_interface_name)));
652
653 return iface;
654}