]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/client/lldpcli.c
redhat: enable SNMP support on RHEL 5 and CentOS 5 by default
[thirdparty/lldpd.git] / src / client / lldpcli.c
CommitLineData
4b292b55
VB
1/* -*- mode: c; c-file-style: "openbsd" -*- */
2/*
3 * Copyright (c) 2008 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
23e7fa38 18
4b292b55
VB
19#include <stdio.h>
20#include <stdlib.h>
21#include <unistd.h>
22#include <time.h>
23#include <errno.h>
4e5f34c5 24#include <string.h>
4b292b55 25#include <sys/types.h>
6402fd2c 26#include <sys/stat.h>
4b292b55
VB
27#include <sys/socket.h>
28#include <sys/un.h>
29#include <arpa/inet.h>
fe80711e 30#include <libgen.h>
6402fd2c
VB
31#include <dirent.h>
32#include <sys/queue.h>
4b292b55 33
4b292b55
VB
34#include "client.h"
35
4b292b55
VB
36#ifdef HAVE___PROGNAME
37extern const char *__progname;
38#else
fe80711e 39# define __progname "lldpcli"
4b292b55
VB
40#endif
41
9a775667
VB
42/* Global for completion */
43static struct cmd_node *root = NULL;
fe8f9650 44const char *ctlname = NULL;
4b292b55 45
6402fd2c
VB
46static int
47is_lldpctl(const char *name)
48{
49 static int last_result = -1;
50 if (last_result == -1 && name) {
51 char *basec = strdup(name);
52 if (!basec) return 0;
53 char *bname = basename(basec);
54 last_result = (!strcmp(bname, "lldpctl"));
55 free(basec);
56 }
57 return (last_result == -1)?0:last_result;
58}
59
4b292b55 60static void
9a775667 61usage()
4b292b55 62{
9a775667 63 fprintf(stderr, "Usage: %s [OPTIONS ...] [COMMAND ...]\n", __progname);
4b292b55
VB
64 fprintf(stderr, "Version: %s\n", PACKAGE_STRING);
65
66 fprintf(stderr, "\n");
67
68 fprintf(stderr, "-d Enable more debugging information.\n");
0262adbb 69 fprintf(stderr, "-u Specify the Unix-domain socket used for communication with lldpd(8).\n");
4b292b55 70 fprintf(stderr, "-f format Choose output format (plain, keyvalue or xml).\n");
6402fd2c
VB
71 if (!is_lldpctl(NULL))
72 fprintf(stderr, "-c Read the provided configuration file.\n");
4b292b55
VB
73
74 fprintf(stderr, "\n");
75
fe80711e 76 fprintf(stderr, "see manual page lldpcli(8) for more information\n");
4b292b55
VB
77 exit(1);
78}
79
9a775667
VB
80static int
81is_privileged()
82{
fe8f9650
VB
83 /* Check we can access the control socket with read/write
84 * privileges. The `access()` function uses the real UID and real GID,
85 * therefore we don't have to mangle with our identity. */
86 return (ctlname && access(ctlname, R_OK|W_OK) == 0);
9a775667
VB
87}
88
89static char*
23e7fa38 90prompt()
9a775667 91{
23e7fa38 92#define CESC "\033"
9a775667 93 int privileged = is_privileged();
23e7fa38
VB
94 if (isatty(STDIN_FILENO)) {
95 if (privileged)
96 return "[lldpcli] # ";
97 return "[lldpcli] $ ";
98 }
99 return "";
9a775667
VB
100}
101
102static int must_exit = 0;
103/**
104 * Exit the interpreter.
105 */
106static int
107cmd_exit(struct lldpctl_conn_t *conn, struct writer *w,
108 struct cmd_env *env, void *arg)
4e90a9e0 109{
fe80711e 110 log_info("lldpctl", "quit lldpcli");
9a775667
VB
111 must_exit = 1;
112 return 1;
113}
114
115/**
116 * Send an "update" request.
117 */
118static int
119cmd_update(struct lldpctl_conn_t *conn, struct writer *w,
120 struct cmd_env *env, void *arg)
121{
122 log_info("lldpctl", "ask for global update");
123
124 lldpctl_atom_t *config = lldpctl_get_configuration(conn);
125 if (config == NULL) {
126 log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
127 lldpctl_last_strerror(conn));
128 return 0;
4e90a9e0 129 }
9a775667
VB
130 if (lldpctl_atom_set_int(config,
131 lldpctl_k_config_tx_interval, -1) == NULL) {
132 log_warnx("lldpctl", "unable to ask lldpd for immediate retransmission. %s",
133 lldpctl_last_strerror(conn));
134 lldpctl_atom_dec_ref(config);
135 return 0;
4e90a9e0 136 }
9a775667
VB
137 log_info("lldpctl", "immediate retransmission requested successfuly");
138 lldpctl_atom_dec_ref(config);
139 return 1;
140}
141
e4ff3ed5
VB
142/**
143 * Pause or resume execution of lldpd.
144 *
145 * @param conn The connection to lldpd.
146 * @param pause 1 if we want to pause lldpd, 0 otherwise
147 * @return 1 on success, 0 on error
148 */
149static int
150cmd_pause_resume(lldpctl_conn_t *conn, int pause)
151{
152 lldpctl_atom_t *config = lldpctl_get_configuration(conn);
153 if (config == NULL) {
154 log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
155 lldpctl_last_strerror(conn));
156 return 0;
157 }
158 if (lldpctl_atom_get_int(config, lldpctl_k_config_paused) == pause) {
159 log_info("lldpctl", "lldpd is already %s",
160 pause?"paused":"resumed");
161 lldpctl_atom_dec_ref(config);
162 return 0;
163 }
164 if (lldpctl_atom_set_int(config,
165 lldpctl_k_config_paused, pause) == NULL) {
166 log_warnx("lldpctl", "unable to ask lldpd to %s operations. %s",
167 pause?"pause":"resume",
168 lldpctl_last_strerror(conn));
169 lldpctl_atom_dec_ref(config);
170 return 0;
171 }
172 log_info("lldpctl", "lldpd should %s operations",
173 pause?"pause":"resume");
174 lldpctl_atom_dec_ref(config);
175 return 1;
176}
177static int
178cmd_pause(struct lldpctl_conn_t *conn, struct writer *w,
179 struct cmd_env *env, void *arg) {
180 (void)w; (void)env;
181 return cmd_pause_resume(conn, 1);
182}
183static int
184cmd_resume(struct lldpctl_conn_t *conn, struct writer *w,
185 struct cmd_env *env, void *arg) {
186 (void)w; (void)env;
187 return cmd_pause_resume(conn, 0);
188}
189
190
35f6f4fb 191#ifdef HAVE_LIBREADLINE
23e7fa38
VB
192static int
193_cmd_complete(int all)
9a775667 194{
23e7fa38
VB
195 char **argv = NULL;
196 int argc = 0;
197 int rc = 1;
608b5669
VB
198 size_t len = strlen(rl_line_buffer);
199 char *line = malloc(len + 2);
23e7fa38 200 if (!line) return -1;
608b5669 201 strlcpy(line, rl_line_buffer, len + 2);
23e7fa38
VB
202 line[rl_point] = 2; /* empty character, will force a word */
203 line[rl_point+1] = 0;
204
205 if (tokenize_line(line, &argc, &argv) != 0)
9a775667
VB
206 goto end;
207
23e7fa38 208 char *compl = commands_complete(root, argc, (const char **)argv, all);
07f2c567
VB
209 if (compl && strlen(argv[argc-1]) < strlen(compl)) {
210 if (rl_insert_text(compl + strlen(argv[argc-1])) < 0) {
9a775667
VB
211 free(compl);
212 goto end;
213 }
214 free(compl);
23e7fa38 215 rc = 0;
9a775667
VB
216 goto end;
217 }
23e7fa38 218 /* No completion or several completion available. */
35f6f4fb
VB
219 fprintf(stderr, "\n");
220 rl_forced_update_display();
23e7fa38 221 rc = 0;
9a775667 222end:
23e7fa38
VB
223 free(line);
224 tokenize_free(argc, argv);
9a775667
VB
225 return rc;
226}
227
23e7fa38
VB
228static int
229cmd_complete(int count, int ch)
9a775667 230{
23e7fa38 231 return _cmd_complete(0);
9a775667
VB
232}
233
23e7fa38
VB
234static int
235cmd_help(int count, int ch)
9a775667 236{
23e7fa38 237 return _cmd_complete(1);
9a775667 238}
35f6f4fb
VB
239#else
240static char*
02987888 241readline(const char *p)
35f6f4fb
VB
242{
243 static char line[2048];
02987888 244 fprintf(stderr, "%s", p);
35f6f4fb
VB
245 fflush(stderr);
246 if (fgets(line, sizeof(line) - 2, stdin) == NULL)
247 return NULL;
248 return line;
249}
250#endif
9a775667 251
6402fd2c
VB
252/**
253 * Execute a tokenized command and display its output.
254 *
255 * @param conn The connection to lldpd.
256 * @param fmt Output format.
257 * @param argc Number of arguments.
258 * @param argv Array of arguments.
259 * @return 0 if an error occurred, 1 otherwise
260 */
261static int
262cmd_exec(lldpctl_conn_t *conn, const char *fmt, int argc, const char **argv)
263{
264 /* Init output formatter */
265 struct writer *w;
266
267 if (strcmp(fmt, "plain") == 0) w = txt_init(stdout);
268 else if (strcmp(fmt, "keyvalue") == 0) w = kv_init(stdout);
269#ifdef USE_XML
270 else if (strcmp(fmt, "xml") == 0) w = xml_init(stdout);
271#endif
272#ifdef USE_JSON
273 else if (strcmp(fmt, "json") == 0) w = json_init(stdout);
274#endif
275 else w = txt_init(stdout);
276
277 /* Execute command */
278 int rc = commands_execute(conn, w,
279 root, argc, argv);
280 if (rc != 0) {
281 log_info("lldpctl", "an error occurred while executing last command");
282 w->finish(w);
283 return 0;
284 }
285 w->finish(w);
286 return 1;
287}
288
289/**
290 * Execute a command line and display its output.
291 *
292 * @param conn The connection to lldpd.
293 * @param fmt Output format.
294 * @param line Line to execute.
295 * @return -1 if an error occurred, 0 if nothing was executed. 1 otherwise.
296 */
297static int
298parse_and_exec(lldpctl_conn_t *conn, const char *fmt, const char *line)
299{
300 int cargc = 0; char **cargv = NULL;
301 int n;
302 log_debug("lldpctl", "tokenize command line");
303 n = tokenize_line(line, &cargc, &cargv);
304 switch (n) {
305 case -1:
306 log_warnx("lldpctl", "internal error while tokenizing");
307 return -1;
308 case 1:
309 log_warnx("lldpctl", "unmatched quotes");
310 return -1;
311 }
ddd75166
VB
312 if (cargc != 0)
313 n = cmd_exec(conn, fmt, cargc, (const char **)cargv);
6402fd2c 314 tokenize_free(cargc, cargv);
ddd75166
VB
315 return (cargc == 0)?0:
316 (n == 0)?-1:
317 1;
6402fd2c
VB
318}
319
9a775667
VB
320static struct cmd_node*
321register_commands()
322{
323 root = commands_root();
324 register_commands_show(root);
325 register_commands_watch(root);
326 if (is_privileged()) {
327 commands_new(
328 commands_new(root, "update", "Update information and send LLDPU on all ports",
329 NULL, NULL, NULL),
330 NEWLINE, "Update information and send LLDPU on all ports",
331 NULL, cmd_update, NULL);
332 register_commands_configure(root);
333 }
25272118
VB
334 commands_new(root, "help", "Get help on a possible command",
335 NULL, cmd_store_env_and_pop, "help");
e4ff3ed5
VB
336 commands_new(
337 commands_new(root, "pause", "Pause lldpd operations", NULL, NULL, NULL),
338 NEWLINE, "Pause lldpd operations", NULL, cmd_pause, NULL);
339 commands_new(
340 commands_new(root, "resume", "Resume lldpd operations", NULL, NULL, NULL),
341 NEWLINE, "Resume lldpd operations", NULL, cmd_resume, NULL);
9a775667
VB
342 commands_new(
343 commands_new(root, "exit", "Exit interpreter", NULL, NULL, NULL),
344 NEWLINE, "Exit interpreter", NULL, cmd_exit, NULL);
345 return root;
4e90a9e0
VB
346}
347
6402fd2c
VB
348struct input {
349 TAILQ_ENTRY(input) next;
350 char *name;
351};
352TAILQ_HEAD(inputs, input);
fe80711e 353static int
6402fd2c 354filter(const struct dirent *dir)
fe80711e 355{
6402fd2c
VB
356 if (strlen(dir->d_name) < 5) return 0;
357 if (strcmp(dir->d_name + strlen(dir->d_name) - 5, ".conf")) return 0;
358 return 1;
359}
360
361/**
362 * Append a new input file/directory to the list of inputs.
363 *
364 * @param arg Directory or file name to add.
365 * @param inputs List of inputs
366 * @param acceptdir 1 if we accept a directory, 0 otherwise
367 */
368static void
369input_append(const char *arg, struct inputs *inputs, int acceptdir)
370{
371 struct stat statbuf;
372 if (stat(arg, &statbuf) == -1) {
373 log_info("lldpctl", "cannot find configuration file/directory %s",
374 arg);
375 return;
fe80711e 376 }
6402fd2c
VB
377
378 if (!S_ISDIR(statbuf.st_mode)) {
379 struct input *input = malloc(sizeof(struct input));
380 if (!input) {
381 log_warn("lldpctl", "not enough memory to process %s",
382 arg);
383 return;
384 }
385 log_debug("lldpctl", "input: %s", arg);
386 input->name = strdup(arg);
387 TAILQ_INSERT_TAIL(inputs, input, next);
388 return;
389 }
390 if (!acceptdir) {
391 log_debug("lldpctl", "skip directory %s",
392 arg);
393 return;
394 }
395
396 struct dirent **namelist = NULL;
397 int n = scandir(arg, &namelist, filter, alphasort);
398 if (n < 0) {
399 log_warnx("lldpctl", "unable to read directory %s",
400 arg);
401 return;
402 }
403 for (int i=0; i < n; i++) {
404 char *fullname;
405 if (asprintf(&fullname, "%s/%s", arg, namelist[i]->d_name) != -1) {
406 input_append(fullname, inputs, 0);
407 free(fullname);
408 }
409 free(namelist[i]);
410 }
411 free(namelist);
fe80711e
VB
412}
413
4b292b55
VB
414int
415main(int argc, char *argv[])
416{
9a775667 417 int ch, debug = 1, rc = EXIT_FAILURE;
6402fd2c 418 const char *fmt = "plain";
048355f3 419 lldpctl_conn_t *conn = NULL;
f7242beb 420 const char *options = is_lldpctl(argv[0])?"hdvf:":"hdsvf:c:u:";
9a775667 421
6402fd2c
VB
422 int gotinputs = 0;
423 struct inputs inputs;
424 TAILQ_INIT(&inputs);
425
fe8f9650
VB
426 ctlname = lldpctl_get_default_transport();
427
6402fd2c
VB
428 /* Initialize logging */
429 while ((ch = getopt(argc, argv, options)) != -1) {
430 switch (ch) {
431 case 'd': debug++; break;
51534ef3 432 case 's': debug--; break;
6402fd2c
VB
433 }
434 }
435 log_init(debug, __progname);
4b292b55
VB
436
437 /* Get and parse command line options */
6402fd2c
VB
438 optind = 1;
439 while ((ch = getopt(argc, argv, options)) != -1) {
4b292b55 440 switch (ch) {
6402fd2c 441 case 'd': break;
51534ef3 442 case 's': break;
4b292b55
VB
443 case 'h':
444 usage();
445 break;
0262adbb
ZM
446 case 'u':
447 ctlname = optarg;
448 break;
4b292b55
VB
449 case 'v':
450 fprintf(stdout, "%s\n", PACKAGE_VERSION);
451 exit(0);
452 break;
4b292b55
VB
453 case 'f':
454 fmt = optarg;
455 break;
6402fd2c
VB
456 case 'c':
457 gotinputs = 1;
458 input_append(optarg, &inputs, 1);
459 break;
4b292b55
VB
460 default:
461 usage();
462 }
463 }
464
9a775667
VB
465 /* Register commands */
466 root = register_commands();
467
6402fd2c
VB
468 /* Make a connection */
469 log_debug("lldpctl", "connect to lldpd");
0262adbb 470 conn = lldpctl_new_name(ctlname, NULL, NULL, NULL);
6402fd2c
VB
471 if (conn == NULL) goto end;
472
473 /* Process file inputs */
474 while (gotinputs && !TAILQ_EMPTY(&inputs)) {
46a5f939
VB
475 /* TAILQ_REMOVE does the right thing */
476 /* coverity[use_after_free] */
6402fd2c
VB
477 struct input *first = TAILQ_FIRST(&inputs);
478 log_debug("lldpctl", "process: %s", first->name);
479 FILE *file = fopen(first->name, "r");
480 if (file) {
481 size_t len;
482 char *line;
483 while ((line = fgetln(file, &len))) {
5920dbf7 484 line = strndup(line, len);
6402fd2c
VB
485 if (line[len - 1] == '\n') {
486 line[len - 1] = '\0';
487 parse_and_exec(conn, fmt, line);
488 }
5920dbf7 489 free(line);
6402fd2c
VB
490 }
491 fclose(file);
492 } else {
493 log_warn("lldpctl", "unable to open %s",
494 first->name);
495 }
496 TAILQ_REMOVE(&inputs, first, next);
497 free(first->name);
498 free(first);
499 }
500
501 /* Process additional arguments. First if we are lldpctl (interfaces) */
502 if (is_lldpctl(NULL)) {
503 char *line = NULL;
fe80711e 504 for (int i = optind; i < argc; i++) {
6402fd2c
VB
505 char *prev = line;
506 if (asprintf(&line, "%s%s%s",
507 prev?prev:"show neigh ports ", argv[i],
508 (i == argc - 1)?" details":",") == -1) {
fe80711e 509 log_warnx("lldpctl", "not enough memory to build list of interfaces");
6402fd2c 510 free(prev);
fe80711e
VB
511 goto end;
512 }
513 free(prev);
514 }
6402fd2c
VB
515 if (line == NULL && (line = strdup("show neigh details")) == NULL) {
516 log_warnx("lldpctl", "not enough memory to build command line");
517 goto end;
518 }
519 log_debug("lldpctl", "execute %s", line);
520 if (parse_and_exec(conn, fmt, line) != -1)
521 rc = EXIT_SUCCESS;
522 free(line);
523 goto end;
4e90a9e0
VB
524 }
525
6402fd2c
VB
526 /* Then, if we are regular lldpcli (command line) */
527 if (optind < argc) {
528 const char **cargv;
529 int cargc;
530 cargv = &((const char **)argv)[optind];
531 cargc = argc - optind;
532 if (cmd_exec(conn, fmt, cargc, cargv) != -1)
533 rc = EXIT_SUCCESS;
534 goto end;
535 }
9a775667 536
6402fd2c 537 if (gotinputs) goto end;
9a775667 538
6402fd2c
VB
539 /* Interactive session */
540#ifdef HAVE_LIBREADLINE
541 rl_bind_key('?', cmd_help);
542 rl_bind_key('\t', cmd_complete);
8b7150e4 543#endif
6402fd2c
VB
544 const char *line;
545 do {
546 if ((line = readline(prompt()))) {
547 int n = parse_and_exec(conn, fmt, line);
548 (void)n;
549#ifdef HAVE_READLINE_HISTORY
550 if (n != 0) add_history(line);
4e90a9e0 551#endif
fe80711e 552 }
4a22e2c1 553 } while (!must_exit && line != NULL);
9a775667 554 rc = EXIT_SUCCESS;
6402fd2c 555
9a775667 556end:
6402fd2c 557 while (!TAILQ_EMPTY(&inputs)) {
46a5f939
VB
558 /* TAILQ_REMOVE does the right thing */
559 /* coverity[use_after_free] */
6402fd2c
VB
560 struct input *first = TAILQ_FIRST(&inputs);
561 TAILQ_REMOVE(&inputs, first, next);
562 free(first->name);
563 free(first);
564 }
9a775667 565 if (conn) lldpctl_release(conn);
9a775667
VB
566 if (root) commands_free(root);
567 return rc;
4b292b55 568}