]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/client/lldpcli.c
lldpcli: don't propose configure for options not compiled
[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
fe80711e 18#define _GNU_SOURCE
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
VB
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <sys/un.h>
28#include <arpa/inet.h>
9a775667 29#include <histedit.h>
fe80711e 30#include <libgen.h>
4b292b55 31
4b292b55
VB
32#include "client.h"
33
4b292b55
VB
34#ifdef HAVE___PROGNAME
35extern const char *__progname;
36#else
fe80711e 37# define __progname "lldpcli"
4b292b55
VB
38#endif
39
9a775667
VB
40/* Global for completion */
41static struct cmd_node *root = NULL;
4b292b55
VB
42
43static void
9a775667 44usage()
4b292b55 45{
9a775667 46 fprintf(stderr, "Usage: %s [OPTIONS ...] [COMMAND ...]\n", __progname);
4b292b55
VB
47 fprintf(stderr, "Version: %s\n", PACKAGE_STRING);
48
49 fprintf(stderr, "\n");
50
51 fprintf(stderr, "-d Enable more debugging information.\n");
4b292b55 52 fprintf(stderr, "-f format Choose output format (plain, keyvalue or xml).\n");
4b292b55
VB
53
54 fprintf(stderr, "\n");
55
fe80711e 56 fprintf(stderr, "see manual page lldpcli(8) for more information\n");
4b292b55
VB
57 exit(1);
58}
59
9a775667
VB
60static int
61is_privileged()
62{
63 return (!(getuid() != geteuid() || getgid() != getegid()));
64}
65
66static char*
67prompt(EditLine *el)
68{
69 int privileged = is_privileged();
70 if (privileged)
fe80711e
VB
71 return "[lldpcli] # ";
72 return "[lldpcli] $ ";
9a775667
VB
73}
74
75static int must_exit = 0;
76/**
77 * Exit the interpreter.
78 */
79static int
80cmd_exit(struct lldpctl_conn_t *conn, struct writer *w,
81 struct cmd_env *env, void *arg)
4e90a9e0 82{
fe80711e 83 log_info("lldpctl", "quit lldpcli");
9a775667
VB
84 must_exit = 1;
85 return 1;
86}
87
88/**
89 * Send an "update" request.
90 */
91static int
92cmd_update(struct lldpctl_conn_t *conn, struct writer *w,
93 struct cmd_env *env, void *arg)
94{
95 log_info("lldpctl", "ask for global update");
96
97 lldpctl_atom_t *config = lldpctl_get_configuration(conn);
98 if (config == NULL) {
99 log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
100 lldpctl_last_strerror(conn));
101 return 0;
4e90a9e0 102 }
9a775667
VB
103 if (lldpctl_atom_set_int(config,
104 lldpctl_k_config_tx_interval, -1) == NULL) {
105 log_warnx("lldpctl", "unable to ask lldpd for immediate retransmission. %s",
106 lldpctl_last_strerror(conn));
107 lldpctl_atom_dec_ref(config);
108 return 0;
4e90a9e0 109 }
9a775667
VB
110 log_info("lldpctl", "immediate retransmission requested successfuly");
111 lldpctl_atom_dec_ref(config);
112 return 1;
113}
114
115static unsigned char
116_cmd_complete(EditLine *el, int ch, int all)
117{
118 int rc = CC_ERROR;
119 Tokenizer *eltok;
120 if ((eltok = tok_init(NULL)) == NULL)
121 goto end;
122
123 const LineInfo *li = el_line(el);
124
125 const char **argv;
126 char *compl;
127 int argc, cursorc, cursoro;
128 if (tok_line(eltok, li, &argc, &argv, &cursorc, &cursoro) != 0)
129 goto end;
130 compl = commands_complete(root, argc, argv, cursorc, cursoro, all);
131 if (compl) {
132 el_deletestr(el, cursoro);
133 if (el_insertstr(el, compl) == -1) {
134 free(compl);
135 goto end;
136 }
137 free(compl);
138 rc = CC_REDISPLAY;
139 goto end;
140 }
141 /* No completion or several completion available. We beep. */
142 el_beep(el);
143 rc = CC_REDISPLAY;
144end:
145 if (eltok) tok_end(eltok);
146 return rc;
147}
148
149static unsigned char
150cmd_complete(EditLine *el, int ch)
151{
152 return _cmd_complete(el, ch, 0);
153}
154
155static unsigned char
156cmd_help(EditLine *el, int ch)
157{
158 return _cmd_complete(el, ch, 1);
159}
160
161static struct cmd_node*
162register_commands()
163{
164 root = commands_root();
165 register_commands_show(root);
166 register_commands_watch(root);
167 if (is_privileged()) {
168 commands_new(
169 commands_new(root, "update", "Update information and send LLDPU on all ports",
170 NULL, NULL, NULL),
171 NEWLINE, "Update information and send LLDPU on all ports",
172 NULL, cmd_update, NULL);
173 register_commands_configure(root);
174 }
175 commands_new(
176 commands_new(root, "exit", "Exit interpreter", NULL, NULL, NULL),
177 NEWLINE, "Exit interpreter", NULL, cmd_exit, NULL);
178 return root;
4e90a9e0
VB
179}
180
fe80711e
VB
181static int
182is_lldpctl(const char *name)
183{
184 static int last_result = -1;
185 if (last_result == -1 && name) {
186 char *basec = strdup(name);
187 if (!basec) return 0;
188 char *bname = basename(basec);
189 last_result = (!strcmp(bname, "lldpctl"));
190 free(basec);
191 }
192 return (last_result == -1)?0:last_result;
193}
194
4b292b55
VB
195int
196main(int argc, char *argv[])
197{
9a775667 198 int ch, debug = 1, rc = EXIT_FAILURE;
4e90a9e0 199 char *fmt = "plain";
4b292b55 200 lldpctl_conn_t *conn;
9a775667
VB
201 struct writer *w;
202
fe80711e
VB
203 EditLine *el = NULL;
204 History *elhistory = NULL;
9a775667 205 HistEvent elhistev;
fe80711e
VB
206 Tokenizer *eltok = NULL;
207
208 char *interfaces = NULL;
4b292b55
VB
209
210 /* Get and parse command line options */
9a775667 211 while ((ch = getopt(argc, argv, "hdvf:")) != -1) {
4b292b55
VB
212 switch (ch) {
213 case 'h':
214 usage();
215 break;
216 case 'd':
217 debug++;
218 break;
219 case 'v':
220 fprintf(stdout, "%s\n", PACKAGE_VERSION);
221 exit(0);
222 break;
4b292b55
VB
223 case 'f':
224 fmt = optarg;
225 break;
4b292b55
VB
226 default:
227 usage();
228 }
229 }
230
231 log_init(debug, __progname);
232
9a775667
VB
233 /* Register commands */
234 root = register_commands();
235
fe80711e
VB
236 if (is_lldpctl(argv[0])) {
237 for (int i = optind; i < argc; i++) {
238 char *prev = interfaces;
239 if (asprintf(&interfaces, "%s%s%s",
240 prev?prev:"", prev?",":"", argv[i]) == -1) {
241 log_warnx("lldpctl", "not enough memory to build list of interfaces");
242 goto end;
243 }
244 free(prev);
245 }
246 must_exit = 1;
247 goto skipeditline;
a8be34ba
VB
248 } else {
249 if (optind < argc) {
250 /* More arguments! */
251 must_exit = 1;
252 }
fe80711e
VB
253 }
254
9a775667
VB
255 /* Init editline */
256 log_debug("lldpctl", "init editline");
257 el = el_init("lldpctl", stdin, stdout, stderr);
258 if (el == NULL) {
259 log_warnx("lldpctl", "unable to setup editline");
260 goto end;
261 }
262 el_set(el, EL_PROMPT, prompt);
fdc876d6 263 el_set(el, EL_SIGNAL, 1);
9a775667
VB
264 el_set(el, EL_EDITOR, "emacs");
265 /* If on a TTY, setup completion */
266 if (isatty(STDERR_FILENO)) {
267 el_set(el, EL_ADDFN, "command_complete",
268 "Execute completion", cmd_complete);
269 el_set(el, EL_ADDFN, "command_help",
270 "Show completion", cmd_help);
271 el_set(el, EL_BIND, "^I", "command_complete", NULL);
272 el_set(el, EL_BIND, "?", "command_help", NULL);
4b292b55
VB
273 }
274
9a775667
VB
275 /* Init history */
276 elhistory = history_init();
277 if (elhistory == NULL) {
278 log_warnx("lldpctl", "unable to enable history");
279 } else {
280 history(elhistory, &elhistev, H_SETSIZE, 800);
281 el_set(el, EL_HIST, history, elhistory);
4e90a9e0
VB
282 }
283
9a775667
VB
284 /* Init tokenizer */
285 eltok = tok_init(NULL);
286 if (eltok == NULL) {
287 log_warnx("lldpctl", "unable to initialize tokenizer");
288 goto end;
289 }
290
fe80711e 291skipeditline:
9a775667
VB
292 /* Make a connection */
293 log_debug("lldpctl", "connect to lldpd");
294 conn = lldpctl_new(NULL, NULL, NULL);
295 if (conn == NULL)
296 exit(EXIT_FAILURE);
297
fe80711e 298 do {
9a775667 299 const char *line;
a8be34ba
VB
300 const char **cargv;
301 int count, n, cargc;
9a775667 302
a8be34ba 303 if (!is_lldpctl(NULL) && (optind >= argc)) {
fe80711e
VB
304 /* Read a new line. */
305 line = el_gets(el, &count);
306 if (line == NULL) break;
307
308 /* Tokenize it */
309 log_debug("lldpctl", "tokenize command line");
a8be34ba 310 n = tok_str(eltok, line, &cargc, &cargv);
fe80711e
VB
311 switch (n) {
312 case -1:
313 log_warnx("lldpctl", "internal error while tokenizing");
314 goto end;
315 case 1:
316 case 2:
317 case 3:
318 /* TODO: handle multiline statements */
319 log_warnx("lldpctl", "unmatched quotes");
320 tok_reset(eltok);
321 continue;
322 }
a8be34ba 323 if (cargc == 0) {
fe80711e
VB
324 tok_reset(eltok);
325 continue;
326 }
327 if (elhistory) history(elhistory, &elhistev, H_ENTER, line);
8729d69f 328 }
9a775667
VB
329
330 /* Init output formatter */
331 if (strcmp(fmt, "plain") == 0) w = txt_init(stdout);
332 else if (strcmp(fmt, "keyvalue") == 0) w = kv_init(stdout);
333#ifdef USE_XML
334 else if (strcmp(fmt, "xml") == 0) w = xml_init(stdout);
8b7150e4
VB
335#endif
336#ifdef USE_JSON
9a775667 337 else if (strcmp(fmt, "json") == 0) w = json_init(stdout);
4e90a9e0 338#endif
9a775667 339 else w = txt_init(stdout);
4e90a9e0 340
fe80711e
VB
341 if (is_lldpctl(NULL)) {
342 if (!interfaces) {
a8be34ba
VB
343 cargv = (const char*[]){ "show", "neighbors", "details" };
344 cargc = 3;
fe80711e 345 } else {
a8be34ba
VB
346 cargv = (const char*[]){ "show", "neighbors", "ports", interfaces, "details" };
347 cargc = 5;
fe80711e 348 }
a8be34ba
VB
349 } else if (optind < argc) {
350 cargv = (const char **)argv;
351 cargv = &cargv[optind];
352 cargc = argc - optind;
fe80711e
VB
353 }
354
9a775667
VB
355 /* Execute command */
356 if (commands_execute(conn, w,
a8be34ba 357 root, cargc, cargv) != 0)
9a775667
VB
358 log_info("lldpctl", "an error occurred while executing last command");
359 w->finish(w);
fe80711e
VB
360
361 if (eltok) tok_reset(eltok);
362 } while (!must_exit);
4b292b55 363
9a775667
VB
364 rc = EXIT_SUCCESS;
365end:
366 if (conn) lldpctl_release(conn);
367 if (eltok) tok_end(eltok);
368 if (elhistory) history_end(elhistory);
369 if (el) el_end(el);
370 if (root) commands_free(root);
fe80711e 371 free(interfaces);
9a775667 372 return rc;
4b292b55 373}