]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/client/commands.c
b96b77f8dae011f21919f52250f6aa98412e4c54
[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 int privileged; /**< Privileged command? */
71 int hidden; /**< Hidden command? */
72
73 /**
74 * Function validating entry in this node. Can be @c NULL.
75 */
76 int(*validate)(struct cmd_env*, void *);
77 /**
78 * Function to execute when entering this node. May be @c NULL.
79 *
80 * This function can alter the environment
81 */
82 int(*execute)(struct lldpctl_conn_t*, struct writer*,
83 struct cmd_env*, void *);
84 void *arg; /**< Magic argument for the previous two functions */
85
86 /* List of possible subentries */
87 TAILQ_HEAD(, cmd_node) subentries; /* List of subnodes */
88 };
89
90 /**
91 * Create a root node.
92 *
93 * @return the root node.
94 */
95 struct cmd_node*
96 commands_root(void)
97 {
98 return commands_new(NULL, NULL, NULL, NULL, NULL, NULL);
99 }
100
101 /**
102 * Make a node accessible only to privileged users.
103 *
104 * @param node node to change privileges
105 * @return the modified node
106 *
107 * The node is modified. It is returned to ease chaining.
108 */
109 struct cmd_node*
110 commands_privileged(struct cmd_node *node)
111 {
112 if (node) node->privileged = 1;
113 return node;
114 }
115
116 /**
117 * Hide a node from help or completion.
118 *
119 * @param node node to hide
120 * @return the modified node
121 *
122 * The node is modified. It is returned to ease chaining.
123 */
124 struct cmd_node*
125 commands_hidden(struct cmd_node *node)
126 {
127 if (node) node->hidden = 1;
128 return node;
129 }
130
131 /**
132 * Create a new node acessible by any user.
133 *
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
141 */
142 struct cmd_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 *),
148 void *arg)
149 {
150 struct cmd_node *new = calloc(1, sizeof(struct cmd_node));
151 if (new == NULL) {
152 log_warn("lldpctl", "unable to allocate memory for new command node");
153 return NULL;
154 }
155 new->token = token;
156 new->doc = doc;
157 new->validate = validate;
158 new->execute = execute;
159 new->arg = arg;
160 TAILQ_INIT(&new->subentries);
161 if (root != NULL)
162 TAILQ_INSERT_TAIL(&root->subentries, new, next);
163 return new;
164 }
165
166 /**
167 * Free a command tree.
168 *
169 * @param root The node we want to free.
170 */
171 void
172 commands_free(struct cmd_node *root)
173 {
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);
180 }
181 free(root);
182 }
183
184 /**
185 * Return the current argument in the environment. This can be @c NEWLINE or
186 * @c NULL.
187 *
188 * @param env The environment.
189 * @return current argument.
190 */
191 const char*
192 cmdenv_arg(struct cmd_env *env)
193 {
194 if (env->argp < env->argc)
195 return env->argv[env->argp];
196 if (env->argp == env->argc)
197 return NEWLINE;
198 return NULL;
199 }
200
201 /**
202 * Get a value from the environment.
203 *
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.
208 */
209 const char*
210 cmdenv_get(struct cmd_env *env, const char *key)
211 {
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;
216 return NULL;
217 }
218
219 /**
220 * Put a value in the environment.
221 *
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.
226 */
227 int
228 cmdenv_put(struct cmd_env *env,
229 const char *key, const char *value)
230 {
231 struct cmd_env_el *el = malloc(sizeof(struct cmd_env_el));
232 if (el == NULL) {
233 log_warn("lldpctl", "unable to allocate memory for new environment variable");
234 return -1;
235 }
236 el->key = key;
237 el->value = value;
238 TAILQ_INSERT_TAIL(&env->elements, el, next);
239 return 0;
240 }
241
242 /**
243 * Pop some node from the execution stack.
244 *
245 * This allows to resume parsing on a previous state. Useful to call after
246 * parsing optional arguments.
247 *
248 * @param env The environment.
249 * @param n How many element we want to pop.
250 * @return 0 on success, -1 otherwise.
251 */
252 int
253 cmdenv_pop(struct cmd_env *env, int n)
254 {
255 while (n-- > 0) {
256 if (TAILQ_EMPTY(&env->stack)) {
257 log_warnx("lldpctl", "environment stack is empty");
258 return -1;
259 }
260 struct cmd_env_stack *first = TAILQ_FIRST(&env->stack);
261 TAILQ_REMOVE(&env->stack,
262 first, next);
263 free(first);
264 }
265 return 0;
266 }
267
268 /**
269 * Push some node on the execution stack.
270 *
271 * @param env The environment.
272 * @param node The node to push.
273 * @return 0 on success, -1 on error.
274 */
275 static int
276 cmdenv_push(struct cmd_env *env, struct cmd_node *node)
277 {
278 struct cmd_env_stack *el = malloc(sizeof(struct cmd_env_stack));
279 if (el == NULL) {
280 log_warn("lldpctl", "not enough memory to allocate a stack element");
281 return -1;
282 }
283 el->el = node;
284 TAILQ_INSERT_HEAD(&env->stack, el, next);
285 return 0;
286 }
287
288 /**
289 * Return the top of the stack, without poping it.
290 *
291 * @param env The environment.
292 * @return the top element or @c NULL is the stack is empty.
293 */
294 static struct cmd_node*
295 cmdenv_top(struct cmd_env *env)
296 {
297 if (TAILQ_EMPTY(&env->stack)) return NULL;
298 return TAILQ_FIRST(&env->stack)->el;
299 }
300
301 /**
302 * Free execution environment.
303 *
304 * @param env The environment.
305 */
306 static void
307 cmdenv_free(struct cmd_env *env)
308 {
309 while (!TAILQ_EMPTY(&env->stack)) cmdenv_pop(env, 1);
310
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);
315 free(first);
316 }
317 }
318
319 struct candidate_word {
320 TAILQ_ENTRY(candidate_word) next;
321 const char *word;
322 const char *doc;
323 int hidden;
324 };
325
326 /**
327 * Execute or complete a command from the given node.
328 *
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.
338 */
339 static int
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)
343 {
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),
350 .argc = argc,
351 .argv = argv,
352 .argp = 0
353 };
354 cmdenv_push(&env, root);
355 if (!completion)
356 for (n = 0; n < argc; n++)
357 log_debug("lldpctl", "argument %02d: `%s`", n, argv[n]);
358 if (completion) *word = NULL;
359
360 #define CAN_EXECUTE(candidate) \
361 ((!candidate->privileged || priv || complete) && \
362 (!candidate->validate || \
363 candidate->validate(&env, candidate->arg) == 1))
364
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))) {
369 if (!completion) {
370 help = !!cmdenv_get(&env, "help"); /* Are we asking for help? */
371 complete = !!cmdenv_get(&env, "complete"); /* Or completion? */
372 }
373
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;
377 if (token == NULL ||
378 (completion && env.argp == env.argc - 1))
379 goto end;
380 if (!completion)
381 log_debug("lldpctl", "process argument %02d: `%s`",
382 env.argp, token);
383 TAILQ_FOREACH(candidate, &current->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)) {
389 /* Exact match */
390 best = candidate;
391 break;
392 }
393 if (!best) best = candidate;
394 else {
395 if (!completion)
396 log_warnx("lldpctl", "ambiguous token: %s (%s or %s)",
397 token, candidate->token, best->token);
398 rc = -1;
399 goto end;
400 }
401 }
402 }
403 if (!best) {
404 /* Take first that validate */
405 TAILQ_FOREACH(candidate, &current->subentries, next) {
406 if (!candidate->token &&
407 CAN_EXECUTE(candidate)) {
408 best = candidate;
409 break;
410 }
411 }
412 }
413 if (!best && env.argp == env.argc) goto end;
414 if (!best) {
415 if (!completion)
416 log_warnx("lldpctl", "unknown command from argument %d: `%s`",
417 env.argp + 1, token);
418 rc = -1;
419 goto end;
420 }
421
422 /* Push and execute */
423 cmdenv_push(&env, best);
424 if (best->execute && best->execute(conn, w, &env, best->arg) != 1) {
425 rc = -1;
426 goto end;
427 }
428 env.argp++;
429 }
430 end:
431 if (!completion && !help && !complete) {
432 if (rc == 0 && env.argp != env.argc + 1) {
433 log_warnx("lldpctl", "incomplete command");
434 rc = -1;
435 }
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 */
441 TAILQ_INIT(&words);
442 current = cmdenv_top(&env);
443 if (!TAILQ_EMPTY(&current->subentries)) {
444 TAILQ_FOREACH(candidate, &current->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));
451 if (!cword) break;
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);
458 }
459 }
460 }
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) {
469 c = 0;
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;
476 c = 1;
477 }
478 if (c == 0) {
479 prefix[n] = '\0';
480 break;
481 }
482 }
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)] = ' ';
491 break;
492 }
493 }
494 *word = strdup(prefix);
495 } else {
496 /* No common prefix, print possible completions */
497 if (!complete)
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;
502
503 char fmt[100];
504 if (!complete) {
505 snprintf(fmt, sizeof(fmt),
506 "%s%%%ds%s %%s\n",
507 "\033[1m", (int)maxl, "\033[0m");
508 fprintf(stderr, fmt,
509 cword->word ? cword->word : "WORD",
510 cword->doc ? cword->doc : "...");
511 } else {
512 if (!cword->word || !strcmp(cword->word, NEWLINE))
513 continue;
514 fprintf(stdout, "%s %s\n",
515 cword->word ? cword->word : "WORD",
516 cword->doc ? cword->doc : "...");
517 }
518 }
519 }
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);
524 free(cword);
525 }
526 }
527 }
528 cmdenv_free(&env);
529 return rc;
530 }
531
532 /**
533 * Complete the given command.
534 */
535 char *
536 commands_complete(struct cmd_node *root, int argc, const char **argv,
537 int all, int privileged)
538 {
539 char *word = NULL;
540 if (_commands_execute(NULL, NULL, root, argc, argv,
541 &word, all, privileged) == 0)
542 return word;
543 return NULL;
544 }
545
546 /**
547 * Execute the given commands.
548 */
549 int
550 commands_execute(struct lldpctl_conn_t *conn, struct writer *w,
551 struct cmd_node *root, int argc, const char **argv, int privileged)
552 {
553 return _commands_execute(conn, w, root, argc, argv, NULL, 0, privileged);
554 }
555
556 /**
557 * Check if the environment does not contain the given key.
558 *
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.
562 */
563 int
564 cmd_check_no_env(struct cmd_env *env, void *key)
565 {
566 return cmdenv_get(env, (const char*)key) == NULL;
567 }
568
569 /**
570 * Check if the environment does contain the given key.
571 *
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.
575 */
576 int
577 cmd_check_env(struct cmd_env *env, void *key)
578 {
579 struct cmd_env_el *el;
580 const char *list = key;
581 int count = 0;
582 TAILQ_FOREACH(el, &env->elements, next) {
583 if (contains(list, el->key))
584 count++;
585 }
586 while ((list = strchr(list, ','))) { list++; count--; }
587 return (count == 1);
588 }
589
590 /**
591 * Store the given key in the environment.
592 *
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
598 */
599 int
600 cmd_store_env(struct lldpctl_conn_t *conn, struct writer *w,
601 struct cmd_env *env, void *key)
602 {
603 return cmdenv_put(env, key, NULL) != -1;
604 }
605
606 /**
607 * Store the given key in the environment and pop one element from the stack.
608 *
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
614 */
615 int
616 cmd_store_env_and_pop(struct lldpctl_conn_t *conn, struct writer *w,
617 struct cmd_env *env, void *key)
618 {
619 return (cmd_store_env(conn, w, env, key) != -1 &&
620 cmdenv_pop(env, 1) != -1);
621 }
622
623 /**
624 * Store the given key with a value being the current keyword in the environment
625 * and pop X elements from the stack.
626 *
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
632 */
633 int
634 cmd_store_env_value_and_pop(struct lldpctl_conn_t *conn, struct writer *w,
635 struct cmd_env *env, void *key)
636 {
637 return (cmdenv_put(env, key, cmdenv_arg(env)) != -1 &&
638 cmdenv_pop(env, 1) != -1);
639 }
640 int
641 cmd_store_env_value_and_pop2(struct lldpctl_conn_t *conn, struct writer *w,
642 struct cmd_env *env, void *key)
643 {
644 return (cmdenv_put(env, key, cmdenv_arg(env)) != -1 &&
645 cmdenv_pop(env, 2) != -1);
646 }
647 int
648 cmd_store_env_value(struct lldpctl_conn_t *conn, struct writer *w,
649 struct cmd_env *env, void *key)
650 {
651 return (cmdenv_put(env, key, cmdenv_arg(env)) != -1);
652 }
653 int
654 cmd_store_env_value_and_pop3(struct lldpctl_conn_t *conn, struct writer *w,
655 struct cmd_env *env, void *key)
656 {
657 return (cmdenv_put(env, key, cmdenv_arg(env)) != -1 &&
658 cmdenv_pop(env, 3) != -1);
659 }
660 int
661 cmd_store_something_env_value_and_pop2(const char *what,
662 struct cmd_env *env, void *value)
663 {
664 return (cmdenv_put(env, what, value) != -1 &&
665 cmdenv_pop(env, 2) != -1);
666 }
667 int
668 cmd_store_something_env_value(const char *what,
669 struct cmd_env *env, void *value)
670 {
671 return (cmdenv_put(env, what, value) != -1);
672 }
673
674 /**
675 * Provide an iterator on all interfaces contained in "ports".
676 *
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.
680 *
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)
685 */
686 lldpctl_atom_t*
687 cmd_iterate_on_interfaces(struct lldpctl_conn_t *conn, struct cmd_env *env)
688 {
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");
693
694 do {
695 if (iter == NULL) {
696 iface_list = lldpctl_get_interfaces(conn);
697 if (!iface_list) {
698 log_warnx("lldpctl", "not able to get the list of interfaces. %s",
699 lldpctl_last_strerror(conn));
700 return NULL;
701 }
702 iter = lldpctl_atom_iter(iface_list);
703 if (!iter) return NULL;
704 } else {
705 iter = lldpctl_atom_iter_next(iface_list, iter);
706 if (iface) {
707 lldpctl_atom_dec_ref(iface);
708 iface = NULL;
709 }
710 if (!iter) {
711 lldpctl_atom_dec_ref(iface_list);
712 return NULL;
713 }
714 }
715
716 iface = lldpctl_atom_iter_value(iface_list, iter);
717 } while (interfaces && !contains(interfaces,
718 lldpctl_atom_get_str(iface, lldpctl_k_interface_name)));
719
720 return iface;
721 }
722
723 /**
724 * Provide an iterator on all ports contained in "ports", as well as the
725 * default port.
726 *
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.
730 *
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.
737 */
738 lldpctl_atom_t*
739 cmd_iterate_on_ports(struct lldpctl_conn_t *conn, struct cmd_env *env, const char **name)
740 {
741 static int put_default = 0;
742 static lldpctl_atom_t *last_port = NULL;
743 const char *interfaces = cmdenv_get(env, "ports");
744
745 if (last_port) {
746 lldpctl_atom_dec_ref(last_port);
747 last_port = NULL;
748 }
749 if (!put_default) {
750 lldpctl_atom_t *iface = cmd_iterate_on_interfaces(conn, env);
751 if (iface) {
752 *name = lldpctl_atom_get_str(iface, lldpctl_k_interface_name);
753 last_port = lldpctl_get_port(iface);
754 return last_port;
755 }
756 if (!interfaces) {
757 put_default = 1;
758 *name = "(default)";
759 last_port = lldpctl_get_default_port(conn);
760 return last_port;
761 }
762 return NULL;
763 } else {
764 put_default = 0;
765 return NULL;
766 }
767 }
768
769 /**
770 * Restrict the command to some ports.
771 */
772 void
773 cmd_restrict_ports(struct cmd_node *root)
774 {
775 /* Restrict to some ports. */
776 commands_new(
777 commands_new(root,
778 "ports",
779 "Restrict configuration to some ports",
780 cmd_check_no_env, NULL, "ports"),
781 NULL,
782 "Restrict configuration to the specified ports (comma-separated list)",
783 NULL, cmd_store_env_value_and_pop2, "ports");
784 }
785
786 /**
787 * Restrict the command to specific protocol
788 */
789 void
790 cmd_restrict_protocol(struct cmd_node *root)
791 {
792 /* Restrict to some ports. */
793 commands_new(
794 commands_new(root,
795 "protocol",
796 "Restrict to specific protocol",
797 cmd_check_no_env, NULL, "protocol"),
798 NULL,
799 "Restrict display to the specified protocol",
800 NULL, cmd_store_env_value_and_pop2, "protocol");
801 }