]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/client/commands.c
build: bump cross-platform-actions/action from 0.27.0 to 0.28.0
[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>
0a46b330 21#include <sys/types.h>
c38c53d0
VB
22#include <sys/stat.h>
23#include <fcntl.h>
0a46b330
VB
24#include <sys/socket.h>
25#include <sys/un.h>
c38c53d0 26#include <libgen.h>
9a775667
VB
27
28/**
29 * An element of the environment (a key and a value).
30 */
31struct cmd_env_el {
32 TAILQ_ENTRY(cmd_env_el) next; /**< Next environment element */
33 const char *key; /**< Key for this element */
34 const char *value; /**< Value for this element */
35};
36
37/**
38 * A stack element.
39 */
40struct cmd_env_stack {
41 TAILQ_ENTRY(cmd_env_stack) next; /**< Next element, down the stack */
42 struct cmd_node *el; /**< Stored element */
43};
44
45/**
46 * Structure representing an environment for the current command.
47 *
48 * An environment is a list of values stored for use for the function executing
49 * as well as the current command, the current position in the command and a
50 * stack for cmd_node
51 */
52struct cmd_env {
53 TAILQ_HEAD(, cmd_env_el) elements; /**< List of environment variables */
54 TAILQ_HEAD(, cmd_env_stack) stack; /**< Stack */
8b549648
VB
55 int argc; /**< Number of argument in the command */
56 int argp; /**< Current argument */
57 const char **argv; /**< Arguments */
9a775667
VB
58};
59
60/**
61 * Structure representing a command node.
62 *
63 * Such a node contains a token accepted to enter the node (or @c NULL if there
64 * is no token needed), a documentation string to present the user, a function
65 * to validate the user input (or @c NULL if no function is needed) and a
66 * function to execute when entering the node. Because we can enter a node just
67 * by completing, the execution part should have no other effect than modifying
68 * the environment, with the exception of execution on @c NEWLINE (which cannot
69 * happen when completing).
70 */
71struct cmd_node {
72 TAILQ_ENTRY(cmd_node) next; /**< Next sibling */
73
8b549648
VB
74 const char *token; /**< Token to enter this cnode */
75 const char *doc; /**< Documentation string */
76 int privileged; /**< Privileged command? */
77 int lock; /**< Lock required for execution? */
78 int hidden; /**< Hidden command? */
9a775667
VB
79
80 /**
81 * Function validating entry in this node. Can be @c NULL.
82 */
f540397c 83 int (*validate)(struct cmd_env *, const void *);
9a775667
VB
84 /**
85 * Function to execute when entering this node. May be @c NULL.
86 *
87 * This function can alter the environment
88 */
8b549648 89 int (*execute)(struct lldpctl_conn_t *, struct writer *, struct cmd_env *,
f540397c
GS
90 const void *);
91 const void *arg; /**< Magic argument for the previous two functions */
9a775667
VB
92
93 /* List of possible subentries */
94 TAILQ_HEAD(, cmd_node) subentries; /* List of subnodes */
95};
96
97/**
98 * Create a root node.
99 *
100 * @return the root node.
101 */
8b549648 102struct cmd_node *
9a775667
VB
103commands_root(void)
104{
61979752
VB
105 struct cmd_node *new = calloc(1, sizeof(struct cmd_node));
106 if (new == NULL) fatalx("lldpctl", "out of memory");
107 TAILQ_INIT(&new->subentries);
108 return new;
9a775667
VB
109}
110
111/**
e13945c0
VB
112 * Make a node accessible only to privileged users.
113 *
314f382a 114 * @param node node to change privileges
e13945c0
VB
115 * @return the modified node
116 *
117 * The node is modified. It is returned to ease chaining.
118 */
8b549648 119struct cmd_node *
e13945c0
VB
120commands_privileged(struct cmd_node *node)
121{
122 if (node) node->privileged = 1;
123 return node;
124}
125
0a46b330
VB
126/**
127 * Make a node accessible only with a lock.
128 *
129 * @param node node to use lock to execute
130 * @return the modified node
131 *
132 * The node is modified. It is returned to ease chaining.
133 */
8b549648 134struct cmd_node *
0a46b330
VB
135commands_lock(struct cmd_node *node)
136{
137 if (node) node->lock = 1;
138 return node;
139}
140
314f382a
VB
141/**
142 * Hide a node from help or completion.
143 *
144 * @param node node to hide
145 * @return the modified node
146 *
147 * The node is modified. It is returned to ease chaining.
148 */
8b549648 149struct cmd_node *
314f382a
VB
150commands_hidden(struct cmd_node *node)
151{
152 if (node) node->hidden = 1;
153 return node;
154}
155
e13945c0
VB
156/**
157 * Create a new node acessible by any user.
9a775667
VB
158 *
159 * @param root The node we want to attach this node.
160 * @param token Token to enter this node. Or @c NULL if no token is needed.
161 * @param doc Documentation for this node.
162 * @param validate Function that should return 1 if we can enter the node.
163 * @param execute Function that should return 1 on successful execution of this node.
164 * @param arg Magic argument for precedent functions.
165 * @return the newly created node
166 */
8b549648
VB
167struct cmd_node *
168commands_new(struct cmd_node *root, const char *token, const char *doc,
f540397c
GS
169 int (*validate)(struct cmd_env *, const void *),
170 int (*execute)(struct lldpctl_conn_t *, struct writer *, struct cmd_env *,
171 const void *),
172 const void *arg)
9a775667
VB
173{
174 struct cmd_node *new = calloc(1, sizeof(struct cmd_node));
8b549648 175 if (new == NULL) fatalx("lldpctl", "out of memory");
9a775667
VB
176 new->token = token;
177 new->doc = doc;
178 new->validate = validate;
179 new->execute = execute;
180 new->arg = arg;
181 TAILQ_INIT(&new->subentries);
61979752 182 TAILQ_INSERT_TAIL(&root->subentries, new, next);
9a775667
VB
183 return new;
184}
185
186/**
187 * Free a command tree.
188 *
189 * @param root The node we want to free.
190 */
191void
192commands_free(struct cmd_node *root)
193{
194 struct cmd_node *subcmd, *subcmd_next;
195 for (subcmd = TAILQ_FIRST(&root->subentries); subcmd != NULL;
196 subcmd = subcmd_next) {
197 subcmd_next = TAILQ_NEXT(subcmd, next);
198 TAILQ_REMOVE(&root->subentries, subcmd, next);
199 commands_free(subcmd);
200 }
201 free(root);
202}
203
204/**
205 * Return the current argument in the environment. This can be @c NEWLINE or
206 * @c NULL.
207 *
208 * @param env The environment.
209 * @return current argument.
210 */
8b549648 211const char *
9a775667
VB
212cmdenv_arg(struct cmd_env *env)
213{
8b549648
VB
214 if (env->argp < env->argc) return env->argv[env->argp];
215 if (env->argp == env->argc) return NEWLINE;
9a775667
VB
216 return NULL;
217}
218
219/**
220 * Get a value from the environment.
221 *
222 * @param env The environment.
223 * @param key The key for the requested value.
224 * @return @c NULL if not found or the requested value otherwise. If no value is
225 * associated, return the key.
226 */
8b549648 227const char *
9a775667
VB
228cmdenv_get(struct cmd_env *env, const char *key)
229{
230 struct cmd_env_el *el;
8b549648
VB
231 TAILQ_FOREACH (el, &env->elements, next)
232 if (!strcmp(el->key, key)) return el->value ? el->value : el->key;
9a775667
VB
233 return NULL;
234}
235
236/**
237 * Put a value in the environment.
238 *
239 * @param env The environment.
240 * @param key The key for the value.
241 * @param value The value.
242 * @return 0 on success, -1 otherwise.
243 */
244int
8b549648 245cmdenv_put(struct cmd_env *env, const char *key, const char *value)
9a775667
VB
246{
247 struct cmd_env_el *el = malloc(sizeof(struct cmd_env_el));
248 if (el == NULL) {
8b549648
VB
249 log_warn("lldpctl",
250 "unable to allocate memory for new environment variable");
9a775667
VB
251 return -1;
252 }
253 el->key = key;
254 el->value = value;
255 TAILQ_INSERT_TAIL(&env->elements, el, next);
256 return 0;
257}
258
259/**
260 * Pop some node from the execution stack.
261 *
262 * This allows to resume parsing on a previous state. Useful to call after
263 * parsing optional arguments.
264 *
265 * @param env The environment.
266 * @param n How many element we want to pop.
267 * @return 0 on success, -1 otherwise.
268 */
269int
270cmdenv_pop(struct cmd_env *env, int n)
271{
272 while (n-- > 0) {
273 if (TAILQ_EMPTY(&env->stack)) {
274 log_warnx("lldpctl", "environment stack is empty");
275 return -1;
276 }
277 struct cmd_env_stack *first = TAILQ_FIRST(&env->stack);
8b549648 278 TAILQ_REMOVE(&env->stack, first, next);
9a775667
VB
279 free(first);
280 }
281 return 0;
282}
283
284/**
285 * Push some node on the execution stack.
286 *
287 * @param env The environment.
288 * @param node The node to push.
289 * @return 0 on success, -1 on error.
290 */
291static int
292cmdenv_push(struct cmd_env *env, struct cmd_node *node)
293{
294 struct cmd_env_stack *el = malloc(sizeof(struct cmd_env_stack));
295 if (el == NULL) {
296 log_warn("lldpctl", "not enough memory to allocate a stack element");
297 return -1;
298 }
299 el->el = node;
300 TAILQ_INSERT_HEAD(&env->stack, el, next);
301 return 0;
302}
303
304/**
305 * Return the top of the stack, without poping it.
306 *
307 * @param env The environment.
308 * @return the top element or @c NULL is the stack is empty.
309 */
8b549648 310static struct cmd_node *
9a775667
VB
311cmdenv_top(struct cmd_env *env)
312{
313 if (TAILQ_EMPTY(&env->stack)) return NULL;
314 return TAILQ_FIRST(&env->stack)->el;
315}
316
317/**
318 * Free execution environment.
319 *
320 * @param env The environment.
321 */
322static void
323cmdenv_free(struct cmd_env *env)
324{
8b549648
VB
325 while (!TAILQ_EMPTY(&env->stack))
326 cmdenv_pop(env, 1);
9a775667
VB
327
328 struct cmd_env_el *first;
329 while (!TAILQ_EMPTY(&env->elements)) {
330 first = TAILQ_FIRST(&env->elements);
331 TAILQ_REMOVE(&env->elements, first, next);
332 free(first);
333 }
334}
335
336struct candidate_word {
337 TAILQ_ENTRY(candidate_word) next;
338 const char *word;
339 const char *doc;
314f382a 340 int hidden;
9a775667
VB
341};
342
343/**
344 * Execute or complete a command from the given node.
345 *
346 * @param conn Connection to lldpd.
347 * @param w Writer for output.
348 * @param root Root node we want to start from.
349 * @param argc Number of arguments.
350 * @param argv Array of arguments.
9a775667 351 * @param word Completed word. Or NULL when no completion is required.
8b549648
VB
352 * @param all When completing, display possible completions even if only one choice
353 * is possible.
e13945c0 354 * @param priv Is the current user privileged?
9a775667
VB
355 * @return 0 on success, -1 otherwise.
356 */
357static int
8b549648
VB
358_commands_execute(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_node *root,
359 int argc, const char **argv, char **word, int all, int priv)
9a775667 360{
23e7fa38 361 int n, rc = 0, completion = (word != NULL);
8b549648
VB
362 int help = 0; /* Are we asking for help? */
363 int complete = 0; /* Are we asking for possible completions? */
364 int needlock = 0; /* Do we need a lock? */
365 struct cmd_env env = { .elements = TAILQ_HEAD_INITIALIZER(env.elements),
9a775667 366 .stack = TAILQ_HEAD_INITIALIZER(env.stack),
23e7fa38 367 .argc = argc,
9a775667 368 .argv = argv,
8b549648 369 .argp = 0 };
fc8164d8 370 static int lockfd = -1;
8b549648 371 static char *lockname = NULL; /* Name of lockfile */
9a775667
VB
372 cmdenv_push(&env, root);
373 if (!completion)
374 for (n = 0; n < argc; n++)
375 log_debug("lldpctl", "argument %02d: `%s`", n, argv[n]);
376 if (completion) *word = NULL;
377
8b549648
VB
378#define CAN_EXECUTE(candidate) \
379 ((!candidate->privileged || priv || complete) && \
380 (!candidate->validate || candidate->validate(&env, candidate->arg) == 1))
e13945c0 381
9a775667
VB
382 /* When completion is in progress, we use the same algorithm than for
383 * execution until we reach the cursor position. */
384 struct cmd_node *current = NULL;
385 while ((current = cmdenv_top(&env))) {
314f382a 386 if (!completion) {
25272118 387 help = !!cmdenv_get(&env, "help"); /* Are we asking for help? */
314f382a
VB
388 complete = !!cmdenv_get(&env, "complete"); /* Or completion? */
389 }
25272118 390
9a775667 391 struct cmd_node *candidate, *best = NULL;
8b549648
VB
392 const char *token = (env.argp < env.argc) ? env.argv[env.argp] :
393 (env.argp == env.argc && !help && !complete) ? NEWLINE :
394 NULL;
395 if (token == NULL || (completion && env.argp == env.argc - 1)) goto end;
9a775667 396 if (!completion)
8b549648
VB
397 log_debug("lldpctl", "process argument %02d: `%s`", env.argp,
398 token);
399 TAILQ_FOREACH (candidate, &current->subentries, next) {
9a775667 400 if (candidate->token &&
e13945c0
VB
401 !strncmp(candidate->token, token, strlen(token)) &&
402 CAN_EXECUTE(candidate)) {
403 if (candidate->token &&
404 !strcmp(candidate->token, token)) {
405 /* Exact match */
406 best = candidate;
0a46b330 407 needlock = needlock || candidate->lock;
e13945c0
VB
408 break;
409 }
8b549648
VB
410 if (!best)
411 best = candidate;
e13945c0
VB
412 else {
413 if (!completion)
8b549648
VB
414 log_warnx("lldpctl",
415 "ambiguous token: %s (%s or %s)",
416 token, candidate->token,
417 best->token);
e13945c0
VB
418 rc = -1;
419 goto end;
9a775667
VB
420 }
421 }
422 }
423 if (!best) {
424 /* Take first that validate */
8b549648
VB
425 TAILQ_FOREACH (candidate, &current->subentries, next) {
426 if (!candidate->token && CAN_EXECUTE(candidate)) {
9a775667 427 best = candidate;
0a46b330 428 needlock = needlock || candidate->lock;
9a775667
VB
429 break;
430 }
431 }
432 }
433 if (!best && env.argp == env.argc) goto end;
434 if (!best) {
435 if (!completion)
8b549648
VB
436 log_warnx("lldpctl",
437 "unknown command from argument %d: `%s`",
9a775667
VB
438 env.argp + 1, token);
439 rc = -1;
440 goto end;
441 }
442
443 /* Push and execute */
444 cmdenv_push(&env, best);
0a46b330 445 if (best->execute) {
0a46b330 446 if (needlock) {
fc8164d8 447 if (lockfd == -1) {
c38c53d0 448 if (lockname == NULL &&
8b549648
VB
449 asprintf(&lockname, "%s.lock", ctlname) ==
450 -1) {
c38c53d0
VB
451 log_warnx("lldpctl",
452 "not enough memory to build lock filename");
fc8164d8
VB
453 rc = -1;
454 goto end;
455 }
8b549648
VB
456 log_debug("lldpctl", "open %s for locking",
457 lockname);
a1c9d4be 458 if ((lockfd = open(lockname, O_RDWR)) == -1) {
8b549648
VB
459 log_warn("lldpctl",
460 "cannot open lock %s", lockname);
fc8164d8 461 rc = -1;
fc8164d8
VB
462 goto end;
463 }
0a46b330
VB
464 }
465 if (lockf(lockfd, F_LOCK, 0) == -1) {
8b549648
VB
466 log_warn("lldpctl", "cannot get lock on %s",
467 lockname);
0a46b330 468 rc = -1;
8b549648
VB
469 close(lockfd);
470 lockfd = -1;
0a46b330
VB
471 goto end;
472 }
473 }
fc8164d8
VB
474 rc = best->execute(conn, w, &env, best->arg) != 1 ? -1 : rc;
475 if (needlock && lockf(lockfd, F_ULOCK, 0) == -1) {
c38c53d0 476 log_warn("lldpctl", "cannot unlock %s", lockname);
8b549648
VB
477 close(lockfd);
478 lockfd = -1;
0a46b330 479 }
fc8164d8 480 if (rc == -1) goto end;
9a775667
VB
481 }
482 env.argp++;
483 }
484end:
314f382a 485 if (!completion && !help && !complete) {
9a775667
VB
486 if (rc == 0 && env.argp != env.argc + 1) {
487 log_warnx("lldpctl", "incomplete command");
488 rc = -1;
489 }
314f382a 490 } else if (rc == 0 && (env.argp == env.argc - 1 || help || complete)) {
9a775667
VB
491 /* We need to complete. Let's build the list of candidate words. */
492 struct cmd_node *candidate = NULL;
314f382a 493 size_t maxl = 10; /* Max length of a word */
9a775667
VB
494 TAILQ_HEAD(, candidate_word) words; /* List of subnodes */
495 TAILQ_INIT(&words);
496 current = cmdenv_top(&env);
aa015c26 497 if (!TAILQ_EMPTY(&current->subentries)) {
8b549648 498 TAILQ_FOREACH (candidate, &current->subentries, next) {
314f382a 499 if ((!candidate->token || help || complete ||
8b549648
VB
500 !strncmp(env.argv[env.argc - 1],
501 candidate->token,
502 strlen(env.argv[env.argc - 1]))) &&
e13945c0 503 CAN_EXECUTE(candidate)) {
aa015c26
VB
504 struct candidate_word *cword =
505 malloc(sizeof(struct candidate_word));
506 if (!cword) break;
507 cword->word = candidate->token;
508 cword->doc = candidate->doc;
314f382a 509 cword->hidden = candidate->hidden;
aa015c26
VB
510 if (cword->word && strlen(cword->word) > maxl)
511 maxl = strlen(cword->word);
512 TAILQ_INSERT_TAIL(&words, cword, next);
513 }
9a775667
VB
514 }
515 }
516 if (!TAILQ_EMPTY(&words)) {
517 /* Search if there is a common prefix, then return it. */
9a775667
VB
518 char prefix[maxl + 2]; /* Extra space may be added at the end */
519 struct candidate_word *cword, *cword_next;
8b549648 520 memset(prefix, 0, maxl + 2);
314f382a
VB
521 for (size_t n = 0; n < maxl; n++) {
522 int c = 1; /* Set to 0 to exit outer loop */
8b549648 523 TAILQ_FOREACH (cword, &words, next) {
9a775667 524 c = 0;
314f382a 525 if (cword->hidden) continue;
9a775667
VB
526 if (cword->word == NULL) break;
527 if (!strcmp(cword->word, NEWLINE)) break;
528 if (strlen(cword->word) == n) break;
8b549648
VB
529 if (prefix[n] == '\0')
530 prefix[n] = cword->word[n];
531 else if (prefix[n] != cword->word[n])
532 break;
9a775667
VB
533 c = 1;
534 }
535 if (c == 0) {
536 prefix[n] = '\0';
537 break;
538 }
539 }
540 /* If the prefix is complete, add a space, otherwise,
541 * just return it as is. */
314f382a 542 if (!all && !help && !complete && strcmp(prefix, NEWLINE) &&
23e7fa38 543 strlen(prefix) > 0 &&
8b549648
VB
544 strlen(env.argv[env.argc - 1]) < strlen(prefix)) {
545 TAILQ_FOREACH (cword, &words, next) {
546 if (cword->word &&
547 !strcmp(prefix, cword->word)) {
9a775667
VB
548 prefix[strlen(prefix)] = ' ';
549 break;
550 }
551 }
552 *word = strdup(prefix);
553 } else {
554 /* No common prefix, print possible completions */
314f382a
VB
555 if (!complete)
556 fprintf(stderr, "\n-- \033[1;34m%s\033[0m\n",
557 current->doc ? current->doc : "Help");
8b549648 558 TAILQ_FOREACH (cword, &words, next) {
314f382a
VB
559 if (cword->hidden) continue;
560
9a775667 561 char fmt[100];
314f382a
VB
562 if (!complete) {
563 snprintf(fmt, sizeof(fmt),
8b549648
VB
564 "%s%%%ds%s %%s\n", "\033[1m",
565 (int)maxl, "\033[0m");
314f382a
VB
566 fprintf(stderr, fmt,
567 cword->word ? cword->word : "WORD",
8b549648 568 cword->doc ? cword->doc : "...");
314f382a 569 } else {
8b549648
VB
570 if (!cword->word ||
571 !strcmp(cword->word, NEWLINE))
314f382a
VB
572 continue;
573 fprintf(stdout, "%s %s\n",
574 cword->word ? cword->word : "WORD",
8b549648 575 cword->doc ? cword->doc : "...");
314f382a 576 }
9a775667
VB
577 }
578 }
579 for (cword = TAILQ_FIRST(&words); cword != NULL;
580 cword = cword_next) {
581 cword_next = TAILQ_NEXT(cword, next);
582 TAILQ_REMOVE(&words, cword, next);
583 free(cword);
584 }
585 }
586 }
587 cmdenv_free(&env);
588 return rc;
589}
590
591/**
592 * Complete the given command.
593 */
594char *
8b549648
VB
595commands_complete(struct cmd_node *root, int argc, const char **argv, int all,
596 int privileged)
9a775667
VB
597{
598 char *word = NULL;
8b549648
VB
599 if (_commands_execute(NULL, NULL, root, argc, argv, &word, all, privileged) ==
600 0)
9a775667
VB
601 return word;
602 return NULL;
603}
604
605/**
606 * Execute the given commands.
607 */
608int
8b549648
VB
609commands_execute(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_node *root,
610 int argc, const char **argv, int privileged)
9a775667 611{
e13945c0 612 return _commands_execute(conn, w, root, argc, argv, NULL, 0, privileged);
9a775667
VB
613}
614
9a775667
VB
615/**
616 * Check if the environment does not contain the given key.
617 *
618 * @param env The environment.
9cac8fed
VB
619 * @param key The key to search for.
620 * @return 1 if the environment does not contain the key. 0 otherwise.
9a775667
VB
621 */
622int
f540397c 623cmd_check_no_env(struct cmd_env *env, const void *key)
9a775667 624{
8b549648 625 return cmdenv_get(env, (const char *)key) == NULL;
9a775667
VB
626}
627
628/**
629 * Check if the environment does contain the given key.
630 *
631 * @param env The environment.
9cac8fed
VB
632 * @param key The key to search for. Can be a comma separated list.
633 * @return 1 if the environment does contain the key. 0 otherwise.
9a775667
VB
634 */
635int
f540397c 636cmd_check_env(struct cmd_env *env, const void *key)
9a775667
VB
637{
638 struct cmd_env_el *el;
639 const char *list = key;
640 int count = 0;
8b549648
VB
641 TAILQ_FOREACH (el, &env->elements, next) {
642 if (contains(list, el->key)) count++;
643 }
644 while ((list = strchr(list, ','))) {
645 list++;
646 count--;
9a775667 647 }
9a775667
VB
648 return (count == 1);
649}
650
651/**
652 * Store the given key in the environment.
653 *
9cac8fed
VB
654 * @param conn The connection.
655 * @param w The writer (not used).
9a775667
VB
656 * @param env The environment.
657 * @param key The key to store.
9cac8fed 658 * @return 1 if the key was stored
9a775667
VB
659 */
660int
8b549648 661cmd_store_env(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
f540397c 662 const void *key)
9a775667
VB
663{
664 return cmdenv_put(env, key, NULL) != -1;
665}
666
667/**
668 * Store the given key in the environment and pop one element from the stack.
669 *
9cac8fed
VB
670 * @param conn The connection.
671 * @param w The writer (not used).
9a775667
VB
672 * @param env The environment.
673 * @param key The key to store.
9cac8fed 674 * @return 1 if the key was stored
9a775667
VB
675 */
676int
677cmd_store_env_and_pop(struct lldpctl_conn_t *conn, struct writer *w,
f540397c 678 struct cmd_env *env, const void *key)
9a775667 679{
8b549648 680 return (cmd_store_env(conn, w, env, key) != -1 && cmdenv_pop(env, 1) != -1);
9a775667
VB
681}
682
683/**
684 * Store the given key with a value being the current keyword in the environment
685 * and pop X elements from the stack.
686 *
9cac8fed
VB
687 * @param conn The connection.
688 * @param w The writer (not used).
9a775667
VB
689 * @param env The environment.
690 * @param key The key to store.
9cac8fed 691 * @return 1 if the key was stored
9a775667
VB
692 */
693int
694cmd_store_env_value_and_pop(struct lldpctl_conn_t *conn, struct writer *w,
f540397c 695 struct cmd_env *env, const void *key)
9a775667 696{
8b549648
VB
697 return (
698 cmdenv_put(env, key, cmdenv_arg(env)) != -1 && cmdenv_pop(env, 1) != -1);
9a775667
VB
699}
700int
701cmd_store_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w,
f540397c 702 struct cmd_env *env, const void *key)
9a775667 703{
8b549648
VB
704 return (
705 cmdenv_put(env, key, cmdenv_arg(env)) != -1 && cmdenv_pop(env, 2) != -1);
9a775667
VB
706}
707int
8b549648 708cmd_store_env_value(struct lldpctl_conn_t *conn, struct writer *w, struct cmd_env *env,
f540397c 709 const void *key)
9a775667
VB
710{
711 return (cmdenv_put(env, key, cmdenv_arg(env)) != -1);
712}
713int
714cmd_store_env_value_and_pop3(struct lldpctl_conn_t *conn, struct writer *w,
f540397c 715 struct cmd_env *env, const void *key)
9a775667 716{
8b549648
VB
717 return (
718 cmdenv_put(env, key, cmdenv_arg(env)) != -1 && cmdenv_pop(env, 3) != -1);
9a775667 719}
e15f96d2 720int
8b549648 721cmd_store_something_env_value_and_pop2(const char *what, struct cmd_env *env,
f540397c 722 const void *value)
e15f96d2 723{
8b549648 724 return (cmdenv_put(env, what, value) != -1 && cmdenv_pop(env, 2) != -1);
e15f96d2 725}
e7331ce9 726int
f540397c 727cmd_store_something_env_value(const char *what, struct cmd_env *env, const void *value)
e7331ce9
VB
728{
729 return (cmdenv_put(env, what, value) != -1);
730}
9a775667
VB
731
732/**
733 * Provide an iterator on all interfaces contained in "ports".
734 *
735 * @warning This function is not reentrant. It uses static variables to keep
736 * track of ports that have already been provided. Moreover, to release all
737 * resources, the iterator should be used until its end.
738 *
739 * @param conn The connection.
740 * @param env The environment.
741 * @return The next interface in the set of ports (or in all ports if no `ports`
742 * variable is present in the environment)
743 */
8b549648 744lldpctl_atom_t *
9a775667
VB
745cmd_iterate_on_interfaces(struct lldpctl_conn_t *conn, struct cmd_env *env)
746{
747 static lldpctl_atom_iter_t *iter = NULL;
748 static lldpctl_atom_t *iface_list = NULL;
749 static lldpctl_atom_t *iface = NULL;
750 const char *interfaces = cmdenv_get(env, "ports");
751
752 do {
753 if (iter == NULL) {
754 iface_list = lldpctl_get_interfaces(conn);
755 if (!iface_list) {
8b549648
VB
756 log_warnx("lldpctl",
757 "not able to get the list of interfaces. %s",
9a775667
VB
758 lldpctl_last_strerror(conn));
759 return NULL;
760 }
761 iter = lldpctl_atom_iter(iface_list);
762 if (!iter) return NULL;
763 } else {
764 iter = lldpctl_atom_iter_next(iface_list, iter);
f3f1e6a2
VB
765 if (iface) {
766 lldpctl_atom_dec_ref(iface);
767 iface = NULL;
768 }
9a775667
VB
769 if (!iter) {
770 lldpctl_atom_dec_ref(iface_list);
771 return NULL;
772 }
773 }
774
775 iface = lldpctl_atom_iter_value(iface_list, iter);
8b549648
VB
776 } while (interfaces &&
777 !contains(interfaces,
9a775667
VB
778 lldpctl_atom_get_str(iface, lldpctl_k_interface_name)));
779
780 return iface;
781}
e688f90f 782
9da663f7
VB
783/**
784 * Provide an iterator on all ports contained in "ports", as well as the
785 * default port.
786 *
787 * @warning This function is not reentrant. It uses static variables to keep
788 * track of ports that have already been provided. Moreover, to release all
789 * resources, the iterator should be used until its end.
790 *
791 * @param conn The connection.
792 * @param env The environment.
793 * @param name Name of the interface (for logging purpose)
794 * @return The next interface in the set of ports (or in all ports if no `ports`
795 * variable is present in the environment), including the default port
796 * if no `ports` variable is present in the environment.
797 */
8b549648
VB
798lldpctl_atom_t *
799cmd_iterate_on_ports(struct lldpctl_conn_t *conn, struct cmd_env *env,
800 const char **name)
9da663f7
VB
801{
802 static int put_default = 0;
803 static lldpctl_atom_t *last_port = NULL;
804 const char *interfaces = cmdenv_get(env, "ports");
805
806 if (last_port) {
807 lldpctl_atom_dec_ref(last_port);
808 last_port = NULL;
809 }
810 if (!put_default) {
811 lldpctl_atom_t *iface = cmd_iterate_on_interfaces(conn, env);
812 if (iface) {
813 *name = lldpctl_atom_get_str(iface, lldpctl_k_interface_name);
814 last_port = lldpctl_get_port(iface);
815 return last_port;
816 }
04f8e4a3 817 if (!interfaces) {
9da663f7
VB
818 put_default = 1;
819 *name = "(default)";
820 last_port = lldpctl_get_default_port(conn);
821 return last_port;
822 }
823 return NULL;
824 } else {
825 put_default = 0;
826 return NULL;
827 }
828}
829
e688f90f
VB
830/**
831 * Restrict the command to some ports.
832 */
833void
834cmd_restrict_ports(struct cmd_node *root)
835{
836 /* Restrict to some ports. */
8b549648
VB
837 commands_new(commands_new(root, "ports", "Restrict configuration to some ports",
838 cmd_check_no_env, NULL, "ports"),
839 NULL,
840 "Restrict configuration to the specified ports (comma-separated list)",
841 NULL, cmd_store_env_value_and_pop2, "ports");
e688f90f 842}
494264f0
ST
843
844/**
845 * Restrict the command to specific protocol
846 */
847void
848cmd_restrict_protocol(struct cmd_node *root)
849{
850 /* Restrict to some ports. */
8b549648
VB
851 commands_new(commands_new(root, "protocol", "Restrict to specific protocol",
852 cmd_check_no_env, NULL, "protocol"),
853 NULL, "Restrict display to the specified protocol", NULL,
854 cmd_store_env_value_and_pop2, "protocol");
494264f0 855}