]>
git.ipfire.org Git - thirdparty/lldpd.git/blob - src/client/commands.c
65658ee6d0affde2f9bfa6d665a8ca7e3be9b161
1 /* -*- mode: c; c-file-style: "openbsd" -*- */
3 * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
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.
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.
20 #include <sys/queue.h>
23 * An element of the environment (a key and a value).
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 */
34 struct cmd_env_stack
{
35 TAILQ_ENTRY(cmd_env_stack
) next
; /**< Next element, down the stack */
36 struct cmd_node
*el
; /**< Stored element */
40 * Structure representing an environment for the current command.
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
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 */
55 * Structure representing a command node.
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).
66 TAILQ_ENTRY(cmd_node
) next
; /**< Next sibling */
68 const char *token
; /**< Token to enter this cnode */
69 const char *doc
; /**< Documentation string */
72 * Function validating entry in this node. Can be @c NULL.
74 int(*validate
)(struct cmd_env
*, void *);
76 * Function to execute when entering this node. May be @c NULL.
78 * This function can alter the environment
80 int(*execute
)(struct lldpctl_conn_t
*, struct writer
*,
81 struct cmd_env
*, void *);
82 void *arg
; /**< Magic argument for the previous two functions */
84 /* List of possible subentries */
85 TAILQ_HEAD(, cmd_node
) subentries
; /* List of subnodes */
91 * @return the root node.
96 return commands_new(NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
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
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 *),
118 struct cmd_node
*new = calloc(1, sizeof(struct cmd_node
));
120 log_warn("lldpctl", "unable to allocate memory for new command node");
125 new->validate
= validate
;
126 new->execute
= execute
;
128 TAILQ_INIT(&new->subentries
);
130 TAILQ_INSERT_TAIL(&root
->subentries
, new, next
);
135 * Free a command tree.
137 * @param root The node we want to free.
140 commands_free(struct cmd_node
*root
)
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
);
153 * Return the current argument in the environment. This can be @c NEWLINE or
156 * @param env The environment.
157 * @return current argument.
160 cmdenv_arg(struct cmd_env
*env
)
162 if (env
->argp
< env
->argc
)
163 return env
->argv
[env
->argp
];
164 if (env
->argp
== env
->argc
)
170 * Get a value from the environment.
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.
178 cmdenv_get(struct cmd_env
*env
, const char *key
)
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
;
188 * Put a value in the environment.
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.
196 cmdenv_put(struct cmd_env
*env
,
197 const char *key
, const char *value
)
199 struct cmd_env_el
*el
= malloc(sizeof(struct cmd_env_el
));
201 log_warn("lldpctl", "unable to allocate memory for new environment variable");
206 TAILQ_INSERT_TAIL(&env
->elements
, el
, next
);
211 * Pop some node from the execution stack.
213 * This allows to resume parsing on a previous state. Useful to call after
214 * parsing optional arguments.
216 * @param env The environment.
217 * @param n How many element we want to pop.
218 * @return 0 on success, -1 otherwise.
221 cmdenv_pop(struct cmd_env
*env
, int n
)
224 if (TAILQ_EMPTY(&env
->stack
)) {
225 log_warnx("lldpctl", "environment stack is empty");
228 struct cmd_env_stack
*first
= TAILQ_FIRST(&env
->stack
);
229 TAILQ_REMOVE(&env
->stack
,
237 * Push some node on the execution stack.
239 * @param env The environment.
240 * @param node The node to push.
241 * @return 0 on success, -1 on error.
244 cmdenv_push(struct cmd_env
*env
, struct cmd_node
*node
)
246 struct cmd_env_stack
*el
= malloc(sizeof(struct cmd_env_stack
));
248 log_warn("lldpctl", "not enough memory to allocate a stack element");
252 TAILQ_INSERT_HEAD(&env
->stack
, el
, next
);
257 * Return the top of the stack, without poping it.
259 * @param env The environment.
260 * @return the top element or @c NULL is the stack is empty.
262 static struct cmd_node
*
263 cmdenv_top(struct cmd_env
*env
)
265 if (TAILQ_EMPTY(&env
->stack
)) return NULL
;
266 return TAILQ_FIRST(&env
->stack
)->el
;
270 * Free execution environment.
272 * @param env The environment.
275 cmdenv_free(struct cmd_env
*env
)
277 while (!TAILQ_EMPTY(&env
->stack
)) cmdenv_pop(env
, 1);
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
);
287 struct candidate_word
{
288 TAILQ_ENTRY(candidate_word
) next
;
294 * Execute or complete a command from the given node.
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.
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
)
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
,
320 cmdenv_push(&env
, root
);
322 for (n
= 0; n
< argc
; n
++)
323 log_debug("lldpctl", "argument %02d: `%s`", n
, argv
[n
]);
324 if (completion
) *word
= NULL
;
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
;
335 log_debug("lldpctl", "process argument %02d: `%s`",
337 TAILQ_FOREACH(candidate
, ¤t
->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
)) {
348 if (!best
) best
= candidate
;
351 log_warnx("lldpctl", "ambiguous token: %s (%s or %s)",
352 token
, candidate
->token
, best
->token
);
360 /* Take first that validate */
361 TAILQ_FOREACH(candidate
, ¤t
->subentries
, next
) {
362 if (!candidate
->token
&& (!candidate
->validate
||
363 candidate
->validate(&env
, candidate
->arg
) == 1)) {
369 if (!best
&& env
.argp
== env
.argc
) goto end
;
372 log_warnx("lldpctl", "unknown command from argument %d: `%s`",
373 env
.argp
+ 1, token
);
378 /* Push and execute */
379 cmdenv_push(&env
, best
);
380 if (best
->execute
&& best
->execute(conn
, w
, &env
, best
->arg
) != 1) {
388 if (rc
== 0 && env
.argp
!= env
.argc
+ 1) {
389 log_warnx("lldpctl", "incomplete command");
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 */
398 current
= cmdenv_top(&env
);
399 TAILQ_FOREACH(candidate
, ¤t
->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
));
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
);
413 if (!TAILQ_EMPTY(&words
)) {
414 /* Search if there is a common prefix, then return it. */
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
) {
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;
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
)] = ' ';
445 *word
= strdup(prefix
);
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
) {
452 snprintf(fmt
, sizeof(fmt
),
454 "\033[1;30m", maxl
, "\033[0m");
456 cword
->word
? cword
->word
: "WORD",
457 cword
->doc
? cword
->doc
: "...");
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
);
473 * Complete the given command.
476 commands_complete(struct cmd_node
*root
, int argc
, const char **argv
,
477 int cursorc
, int cursoro
, int all
)
480 if (_commands_execute(NULL
, NULL
, root
, argc
, argv
,
481 cursorc
, cursoro
, &word
, all
) == 0)
487 * Execute the given commands.
490 commands_execute(struct lldpctl_conn_t
*conn
, struct writer
*w
,
491 struct cmd_node
*root
, int argc
, const char **argv
)
493 return _commands_execute(conn
, w
, root
, argc
, argv
, -1, -1, NULL
, 0);
497 * Check if the environment does not contain the given key.
499 * @param env The environment.
500 * @param arg The key to search for.
503 cmd_check_no_env(struct cmd_env
*env
, void *key
)
505 return cmdenv_get(env
, (const char*)key
) == NULL
;
509 * Check if the environment does contain the given key.
511 * @param env The environment.
512 * @param arg The key to search for. Can be a comma separated list.
515 cmd_check_env(struct cmd_env
*env
, void *key
)
517 struct cmd_env_el
*el
;
518 const char *list
= key
;
520 TAILQ_FOREACH(el
, &env
->elements
, next
) {
521 if (contains(list
, el
->key
))
524 while ((list
= strchr(list
, ','))) { list
++; count
--; }
529 * Store the given key in the environment.
531 * @param env The environment.
532 * @param key The key to store.
535 cmd_store_env(struct lldpctl_conn_t
*conn
, struct writer
*w
,
536 struct cmd_env
*env
, void *key
)
538 return cmdenv_put(env
, key
, NULL
) != -1;
542 * Store the given key in the environment and pop one element from the stack.
544 * @param env The environment.
545 * @param key The key to store.
548 cmd_store_env_and_pop(struct lldpctl_conn_t
*conn
, struct writer
*w
,
549 struct cmd_env
*env
, void *key
)
551 return (cmd_store_env(conn
, w
, env
, key
) != -1 &&
552 cmdenv_pop(env
, 1) != -1);
556 * Store the given key with a value being the current keyword in the environment
557 * and pop X elements from the stack.
559 * @param env The environment.
560 * @param key The key to store.
563 cmd_store_env_value_and_pop(struct lldpctl_conn_t
*conn
, struct writer
*w
,
564 struct cmd_env
*env
, void *key
)
566 return (cmdenv_put(env
, key
, cmdenv_arg(env
)) != -1 &&
567 cmdenv_pop(env
, 1) != -1);
570 cmd_store_env_value_and_pop2(struct lldpctl_conn_t
*conn
, struct writer
*w
,
571 struct cmd_env
*env
, void *key
)
573 return (cmdenv_put(env
, key
, cmdenv_arg(env
)) != -1 &&
574 cmdenv_pop(env
, 2) != -1);
577 cmd_store_env_value(struct lldpctl_conn_t
*conn
, struct writer
*w
,
578 struct cmd_env
*env
, void *key
)
580 return (cmdenv_put(env
, key
, cmdenv_arg(env
)) != -1);
583 cmd_store_env_value_and_pop3(struct lldpctl_conn_t
*conn
, struct writer
*w
,
584 struct cmd_env
*env
, void *key
)
586 return (cmdenv_put(env
, key
, cmdenv_arg(env
)) != -1 &&
587 cmdenv_pop(env
, 3) != -1);
591 * Provide an iterator on all interfaces contained in "ports".
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.
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)
603 cmd_iterate_on_interfaces(struct lldpctl_conn_t
*conn
, struct cmd_env
*env
)
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");
612 iface_list
= lldpctl_get_interfaces(conn
);
614 log_warnx("lldpctl", "not able to get the list of interfaces. %s",
615 lldpctl_last_strerror(conn
));
618 iter
= lldpctl_atom_iter(iface_list
);
619 if (!iter
) return NULL
;
621 iter
= lldpctl_atom_iter_next(iface_list
, iter
);
622 if (iface
) lldpctl_atom_dec_ref(iface
); iface
= NULL
;
624 lldpctl_atom_dec_ref(iface_list
);
629 iface
= lldpctl_atom_iter_value(iface_list
, iter
);
630 } while (interfaces
&& !contains(interfaces
,
631 lldpctl_atom_get_str(iface
, lldpctl_k_interface_name
)));