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 */
70 int privileged
; /**< Privileged command? */
71 int hidden
; /**< Hidden command? */
74 * Function validating entry in this node. Can be @c NULL.
76 int(*validate
)(struct cmd_env
*, void *);
78 * Function to execute when entering this node. May be @c NULL.
80 * This function can alter the environment
82 int(*execute
)(struct lldpctl_conn_t
*, struct writer
*,
83 struct cmd_env
*, void *);
84 void *arg
; /**< Magic argument for the previous two functions */
86 /* List of possible subentries */
87 TAILQ_HEAD(, cmd_node
) subentries
; /* List of subnodes */
93 * @return the root node.
98 return commands_new(NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
102 * Make a node accessible only to privileged users.
104 * @param node node to change privileges
105 * @return the modified node
107 * The node is modified. It is returned to ease chaining.
110 commands_privileged(struct cmd_node
*node
)
112 if (node
) node
->privileged
= 1;
117 * Hide a node from help or completion.
119 * @param node node to hide
120 * @return the modified node
122 * The node is modified. It is returned to ease chaining.
125 commands_hidden(struct cmd_node
*node
)
127 if (node
) node
->hidden
= 1;
132 * Create a new node acessible by any user.
134 * @param root The node we want to attach this node.
135 * @param token Token to enter this node. Or @c NULL if no token is needed.
136 * @param doc Documentation for this node.
137 * @param validate Function that should return 1 if we can enter the node.
138 * @param execute Function that should return 1 on successful execution of this node.
139 * @param arg Magic argument for precedent functions.
140 * @return the newly created node
143 commands_new(struct cmd_node
*root
,
144 const char *token
, const char *doc
,
145 int(*validate
)(struct cmd_env
*, void *),
146 int(*execute
)(struct lldpctl_conn_t
*, struct writer
*,
147 struct cmd_env
*, void *),
150 struct cmd_node
*new = calloc(1, sizeof(struct cmd_node
));
152 log_warn("lldpctl", "unable to allocate memory for new command node");
157 new->validate
= validate
;
158 new->execute
= execute
;
160 TAILQ_INIT(&new->subentries
);
162 TAILQ_INSERT_TAIL(&root
->subentries
, new, next
);
167 * Free a command tree.
169 * @param root The node we want to free.
172 commands_free(struct cmd_node
*root
)
174 struct cmd_node
*subcmd
, *subcmd_next
;
175 for (subcmd
= TAILQ_FIRST(&root
->subentries
); subcmd
!= NULL
;
176 subcmd
= subcmd_next
) {
177 subcmd_next
= TAILQ_NEXT(subcmd
, next
);
178 TAILQ_REMOVE(&root
->subentries
, subcmd
, next
);
179 commands_free(subcmd
);
185 * Return the current argument in the environment. This can be @c NEWLINE or
188 * @param env The environment.
189 * @return current argument.
192 cmdenv_arg(struct cmd_env
*env
)
194 if (env
->argp
< env
->argc
)
195 return env
->argv
[env
->argp
];
196 if (env
->argp
== env
->argc
)
202 * Get a value from the environment.
204 * @param env The environment.
205 * @param key The key for the requested value.
206 * @return @c NULL if not found or the requested value otherwise. If no value is
207 * associated, return the key.
210 cmdenv_get(struct cmd_env
*env
, const char *key
)
212 struct cmd_env_el
*el
;
213 TAILQ_FOREACH(el
, &env
->elements
, next
)
214 if (!strcmp(el
->key
, key
))
215 return el
->value
? el
->value
: el
->key
;
220 * Put a value in the environment.
222 * @param env The environment.
223 * @param key The key for the value.
224 * @param value The value.
225 * @return 0 on success, -1 otherwise.
228 cmdenv_put(struct cmd_env
*env
,
229 const char *key
, const char *value
)
231 struct cmd_env_el
*el
= malloc(sizeof(struct cmd_env_el
));
233 log_warn("lldpctl", "unable to allocate memory for new environment variable");
238 TAILQ_INSERT_TAIL(&env
->elements
, el
, next
);
243 * Pop some node from the execution stack.
245 * This allows to resume parsing on a previous state. Useful to call after
246 * parsing optional arguments.
248 * @param env The environment.
249 * @param n How many element we want to pop.
250 * @return 0 on success, -1 otherwise.
253 cmdenv_pop(struct cmd_env
*env
, int n
)
256 if (TAILQ_EMPTY(&env
->stack
)) {
257 log_warnx("lldpctl", "environment stack is empty");
260 struct cmd_env_stack
*first
= TAILQ_FIRST(&env
->stack
);
261 TAILQ_REMOVE(&env
->stack
,
269 * Push some node on the execution stack.
271 * @param env The environment.
272 * @param node The node to push.
273 * @return 0 on success, -1 on error.
276 cmdenv_push(struct cmd_env
*env
, struct cmd_node
*node
)
278 struct cmd_env_stack
*el
= malloc(sizeof(struct cmd_env_stack
));
280 log_warn("lldpctl", "not enough memory to allocate a stack element");
284 TAILQ_INSERT_HEAD(&env
->stack
, el
, next
);
289 * Return the top of the stack, without poping it.
291 * @param env The environment.
292 * @return the top element or @c NULL is the stack is empty.
294 static struct cmd_node
*
295 cmdenv_top(struct cmd_env
*env
)
297 if (TAILQ_EMPTY(&env
->stack
)) return NULL
;
298 return TAILQ_FIRST(&env
->stack
)->el
;
302 * Free execution environment.
304 * @param env The environment.
307 cmdenv_free(struct cmd_env
*env
)
309 while (!TAILQ_EMPTY(&env
->stack
)) cmdenv_pop(env
, 1);
311 struct cmd_env_el
*first
;
312 while (!TAILQ_EMPTY(&env
->elements
)) {
313 first
= TAILQ_FIRST(&env
->elements
);
314 TAILQ_REMOVE(&env
->elements
, first
, next
);
319 struct candidate_word
{
320 TAILQ_ENTRY(candidate_word
) next
;
327 * Execute or complete a command from the given node.
329 * @param conn Connection to lldpd.
330 * @param w Writer for output.
331 * @param root Root node we want to start from.
332 * @param argc Number of arguments.
333 * @param argv Array of arguments.
334 * @param word Completed word. Or NULL when no completion is required.
335 * @param all When completing, display possible completions even if only one choice is possible.
336 * @param priv Is the current user privileged?
337 * @return 0 on success, -1 otherwise.
340 _commands_execute(struct lldpctl_conn_t
*conn
, struct writer
*w
,
341 struct cmd_node
*root
, int argc
, const char **argv
,
342 char **word
, int all
, int priv
)
344 int n
, rc
= 0, completion
= (word
!= NULL
);
345 int help
= 0; /* Are we asking for help? */
346 int complete
= 0; /* Are we asking for possible completions? */
347 struct cmd_env env
= {
348 .elements
= TAILQ_HEAD_INITIALIZER(env
.elements
),
349 .stack
= TAILQ_HEAD_INITIALIZER(env
.stack
),
354 cmdenv_push(&env
, root
);
356 for (n
= 0; n
< argc
; n
++)
357 log_debug("lldpctl", "argument %02d: `%s`", n
, argv
[n
]);
358 if (completion
) *word
= NULL
;
360 #define CAN_EXECUTE(candidate) \
361 ((!candidate->privileged || priv || complete) && \
362 (!candidate->validate || \
363 candidate->validate(&env, candidate->arg) == 1))
365 /* When completion is in progress, we use the same algorithm than for
366 * execution until we reach the cursor position. */
367 struct cmd_node
*current
= NULL
;
368 while ((current
= cmdenv_top(&env
))) {
370 help
= !!cmdenv_get(&env
, "help"); /* Are we asking for help? */
371 complete
= !!cmdenv_get(&env
, "complete"); /* Or completion? */
374 struct cmd_node
*candidate
, *best
= NULL
;
375 const char *token
= (env
.argp
< env
.argc
) ? env
.argv
[env
.argp
] :
376 (env
.argp
== env
.argc
&& !help
&& !complete
) ? NEWLINE
: NULL
;
378 (completion
&& env
.argp
== env
.argc
- 1))
381 log_debug("lldpctl", "process argument %02d: `%s`",
383 TAILQ_FOREACH(candidate
, ¤t
->subentries
, next
) {
384 if (candidate
->token
&&
385 !strncmp(candidate
->token
, token
, strlen(token
)) &&
386 CAN_EXECUTE(candidate
)) {
387 if (candidate
->token
&&
388 !strcmp(candidate
->token
, token
)) {
393 if (!best
) best
= candidate
;
396 log_warnx("lldpctl", "ambiguous token: %s (%s or %s)",
397 token
, candidate
->token
, best
->token
);
404 /* Take first that validate */
405 TAILQ_FOREACH(candidate
, ¤t
->subentries
, next
) {
406 if (!candidate
->token
&&
407 CAN_EXECUTE(candidate
)) {
413 if (!best
&& env
.argp
== env
.argc
) goto end
;
416 log_warnx("lldpctl", "unknown command from argument %d: `%s`",
417 env
.argp
+ 1, token
);
422 /* Push and execute */
423 cmdenv_push(&env
, best
);
424 if (best
->execute
&& best
->execute(conn
, w
, &env
, best
->arg
) != 1) {
431 if (!completion
&& !help
&& !complete
) {
432 if (rc
== 0 && env
.argp
!= env
.argc
+ 1) {
433 log_warnx("lldpctl", "incomplete command");
436 } else if (rc
== 0 && (env
.argp
== env
.argc
- 1 || help
|| complete
)) {
437 /* We need to complete. Let's build the list of candidate words. */
438 struct cmd_node
*candidate
= NULL
;
439 size_t maxl
= 10; /* Max length of a word */
440 TAILQ_HEAD(, candidate_word
) words
; /* List of subnodes */
442 current
= cmdenv_top(&env
);
443 if (!TAILQ_EMPTY(¤t
->subentries
)) {
444 TAILQ_FOREACH(candidate
, ¤t
->subentries
, next
) {
445 if ((!candidate
->token
|| help
|| complete
||
446 !strncmp(env
.argv
[env
.argc
- 1], candidate
->token
,
447 strlen(env
.argv
[env
.argc
-1 ]))) &&
448 CAN_EXECUTE(candidate
)) {
449 struct candidate_word
*cword
=
450 malloc(sizeof(struct candidate_word
));
452 cword
->word
= candidate
->token
;
453 cword
->doc
= candidate
->doc
;
454 cword
->hidden
= candidate
->hidden
;
455 if (cword
->word
&& strlen(cword
->word
) > maxl
)
456 maxl
= strlen(cword
->word
);
457 TAILQ_INSERT_TAIL(&words
, cword
, next
);
461 if (!TAILQ_EMPTY(&words
)) {
462 /* Search if there is a common prefix, then return it. */
463 char prefix
[maxl
+ 2]; /* Extra space may be added at the end */
464 struct candidate_word
*cword
, *cword_next
;
465 memset(prefix
, 0, maxl
+2);
466 for (size_t n
= 0; n
< maxl
; n
++) {
467 int c
= 1; /* Set to 0 to exit outer loop */
468 TAILQ_FOREACH(cword
, &words
, next
) {
470 if (cword
->hidden
) continue;
471 if (cword
->word
== NULL
) break;
472 if (!strcmp(cword
->word
, NEWLINE
)) break;
473 if (strlen(cword
->word
) == n
) break;
474 if (prefix
[n
] == '\0') prefix
[n
] = cword
->word
[n
];
475 else if (prefix
[n
] != cword
->word
[n
]) break;
483 /* If the prefix is complete, add a space, otherwise,
484 * just return it as is. */
485 if (!all
&& !help
&& !complete
&& strcmp(prefix
, NEWLINE
) &&
486 strlen(prefix
) > 0 &&
487 strlen(env
.argv
[env
.argc
-1]) < strlen(prefix
)) {
488 TAILQ_FOREACH(cword
, &words
, next
) {
489 if (cword
->word
&& !strcmp(prefix
, cword
->word
)) {
490 prefix
[strlen(prefix
)] = ' ';
494 *word
= strdup(prefix
);
496 /* No common prefix, print possible completions */
498 fprintf(stderr
, "\n-- \033[1;34m%s\033[0m\n",
499 current
->doc
? current
->doc
: "Help");
500 TAILQ_FOREACH(cword
, &words
, next
) {
501 if (cword
->hidden
) continue;
505 snprintf(fmt
, sizeof(fmt
),
507 "\033[1m", (int)maxl
, "\033[0m");
509 cword
->word
? cword
->word
: "WORD",
510 cword
->doc
? cword
->doc
: "...");
512 if (!cword
->word
|| !strcmp(cword
->word
, NEWLINE
))
514 fprintf(stdout
, "%s %s\n",
515 cword
->word
? cword
->word
: "WORD",
516 cword
->doc
? cword
->doc
: "...");
520 for (cword
= TAILQ_FIRST(&words
); cword
!= NULL
;
521 cword
= cword_next
) {
522 cword_next
= TAILQ_NEXT(cword
, next
);
523 TAILQ_REMOVE(&words
, cword
, next
);
533 * Complete the given command.
536 commands_complete(struct cmd_node
*root
, int argc
, const char **argv
,
537 int all
, int privileged
)
540 if (_commands_execute(NULL
, NULL
, root
, argc
, argv
,
541 &word
, all
, privileged
) == 0)
547 * Execute the given commands.
550 commands_execute(struct lldpctl_conn_t
*conn
, struct writer
*w
,
551 struct cmd_node
*root
, int argc
, const char **argv
, int privileged
)
553 return _commands_execute(conn
, w
, root
, argc
, argv
, NULL
, 0, privileged
);
557 * Check if the environment does not contain the given key.
559 * @param env The environment.
560 * @param key The key to search for.
561 * @return 1 if the environment does not contain the key. 0 otherwise.
564 cmd_check_no_env(struct cmd_env
*env
, void *key
)
566 return cmdenv_get(env
, (const char*)key
) == NULL
;
570 * Check if the environment does contain the given key.
572 * @param env The environment.
573 * @param key The key to search for. Can be a comma separated list.
574 * @return 1 if the environment does contain the key. 0 otherwise.
577 cmd_check_env(struct cmd_env
*env
, void *key
)
579 struct cmd_env_el
*el
;
580 const char *list
= key
;
582 TAILQ_FOREACH(el
, &env
->elements
, next
) {
583 if (contains(list
, el
->key
))
586 while ((list
= strchr(list
, ','))) { list
++; count
--; }
591 * Store the given key in the environment.
593 * @param conn The connection.
594 * @param w The writer (not used).
595 * @param env The environment.
596 * @param key The key to store.
597 * @return 1 if the key was stored
600 cmd_store_env(struct lldpctl_conn_t
*conn
, struct writer
*w
,
601 struct cmd_env
*env
, void *key
)
603 return cmdenv_put(env
, key
, NULL
) != -1;
607 * Store the given key in the environment and pop one element from the stack.
609 * @param conn The connection.
610 * @param w The writer (not used).
611 * @param env The environment.
612 * @param key The key to store.
613 * @return 1 if the key was stored
616 cmd_store_env_and_pop(struct lldpctl_conn_t
*conn
, struct writer
*w
,
617 struct cmd_env
*env
, void *key
)
619 return (cmd_store_env(conn
, w
, env
, key
) != -1 &&
620 cmdenv_pop(env
, 1) != -1);
624 * Store the given key with a value being the current keyword in the environment
625 * and pop X elements from the stack.
627 * @param conn The connection.
628 * @param w The writer (not used).
629 * @param env The environment.
630 * @param key The key to store.
631 * @return 1 if the key was stored
634 cmd_store_env_value_and_pop(struct lldpctl_conn_t
*conn
, struct writer
*w
,
635 struct cmd_env
*env
, void *key
)
637 return (cmdenv_put(env
, key
, cmdenv_arg(env
)) != -1 &&
638 cmdenv_pop(env
, 1) != -1);
641 cmd_store_env_value_and_pop2(struct lldpctl_conn_t
*conn
, struct writer
*w
,
642 struct cmd_env
*env
, void *key
)
644 return (cmdenv_put(env
, key
, cmdenv_arg(env
)) != -1 &&
645 cmdenv_pop(env
, 2) != -1);
648 cmd_store_env_value(struct lldpctl_conn_t
*conn
, struct writer
*w
,
649 struct cmd_env
*env
, void *key
)
651 return (cmdenv_put(env
, key
, cmdenv_arg(env
)) != -1);
654 cmd_store_env_value_and_pop3(struct lldpctl_conn_t
*conn
, struct writer
*w
,
655 struct cmd_env
*env
, void *key
)
657 return (cmdenv_put(env
, key
, cmdenv_arg(env
)) != -1 &&
658 cmdenv_pop(env
, 3) != -1);
661 cmd_store_something_env_value_and_pop2(const char *what
,
662 struct cmd_env
*env
, void *value
)
664 return (cmdenv_put(env
, what
, value
) != -1 &&
665 cmdenv_pop(env
, 2) != -1);
668 cmd_store_something_env_value(const char *what
,
669 struct cmd_env
*env
, void *value
)
671 return (cmdenv_put(env
, what
, value
) != -1);
675 * Provide an iterator on all interfaces contained in "ports".
677 * @warning This function is not reentrant. It uses static variables to keep
678 * track of ports that have already been provided. Moreover, to release all
679 * resources, the iterator should be used until its end.
681 * @param conn The connection.
682 * @param env The environment.
683 * @return The next interface in the set of ports (or in all ports if no `ports`
684 * variable is present in the environment)
687 cmd_iterate_on_interfaces(struct lldpctl_conn_t
*conn
, struct cmd_env
*env
)
689 static lldpctl_atom_iter_t
*iter
= NULL
;
690 static lldpctl_atom_t
*iface_list
= NULL
;
691 static lldpctl_atom_t
*iface
= NULL
;
692 const char *interfaces
= cmdenv_get(env
, "ports");
696 iface_list
= lldpctl_get_interfaces(conn
);
698 log_warnx("lldpctl", "not able to get the list of interfaces. %s",
699 lldpctl_last_strerror(conn
));
702 iter
= lldpctl_atom_iter(iface_list
);
703 if (!iter
) return NULL
;
705 iter
= lldpctl_atom_iter_next(iface_list
, iter
);
707 lldpctl_atom_dec_ref(iface
);
711 lldpctl_atom_dec_ref(iface_list
);
716 iface
= lldpctl_atom_iter_value(iface_list
, iter
);
717 } while (interfaces
&& !contains(interfaces
,
718 lldpctl_atom_get_str(iface
, lldpctl_k_interface_name
)));
724 * Provide an iterator on all ports contained in "ports", as well as the
727 * @warning This function is not reentrant. It uses static variables to keep
728 * track of ports that have already been provided. Moreover, to release all
729 * resources, the iterator should be used until its end.
731 * @param conn The connection.
732 * @param env The environment.
733 * @param name Name of the interface (for logging purpose)
734 * @return The next interface in the set of ports (or in all ports if no `ports`
735 * variable is present in the environment), including the default port
736 * if no `ports` variable is present in the environment.
739 cmd_iterate_on_ports(struct lldpctl_conn_t
*conn
, struct cmd_env
*env
, const char **name
)
741 static int put_default
= 0;
742 static lldpctl_atom_t
*last_port
= NULL
;
743 const char *interfaces
= cmdenv_get(env
, "ports");
746 lldpctl_atom_dec_ref(last_port
);
750 lldpctl_atom_t
*iface
= cmd_iterate_on_interfaces(conn
, env
);
752 *name
= lldpctl_atom_get_str(iface
, lldpctl_k_interface_name
);
753 last_port
= lldpctl_get_port(iface
);
759 last_port
= lldpctl_get_default_port(conn
);
770 * Restrict the command to some ports.
773 cmd_restrict_ports(struct cmd_node
*root
)
775 /* Restrict to some ports. */
779 "Restrict configuration to some ports",
780 cmd_check_no_env
, NULL
, "ports"),
782 "Restrict configuration to the specified ports (comma-separated list)",
783 NULL
, cmd_store_env_value_and_pop2
, "ports");
787 * Restrict the command to specific protocol
790 cmd_restrict_protocol(struct cmd_node
*root
)
792 /* Restrict to some ports. */
796 "Restrict to specific protocol",
797 cmd_check_no_env
, NULL
, "protocol"),
799 "Restrict display to the specified protocol",
800 NULL
, cmd_store_env_value_and_pop2
, "protocol");