]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/client/lldpcli.c
lldpcli: make readline library optional.
[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
fe80711e 19#define _GNU_SOURCE
4b292b55
VB
20#include <stdio.h>
21#include <stdlib.h>
22#include <unistd.h>
23#include <time.h>
24#include <errno.h>
4e5f34c5 25#include <string.h>
4b292b55
VB
26#include <sys/types.h>
27#include <sys/socket.h>
28#include <sys/un.h>
29#include <arpa/inet.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*
23e7fa38 67prompt()
9a775667 68{
23e7fa38 69#define CESC "\033"
9a775667 70 int privileged = is_privileged();
23e7fa38
VB
71 if (isatty(STDIN_FILENO)) {
72 if (privileged)
73 return "[lldpcli] # ";
74 return "[lldpcli] $ ";
75 }
76 return "";
9a775667
VB
77}
78
79static int must_exit = 0;
80/**
81 * Exit the interpreter.
82 */
83static int
84cmd_exit(struct lldpctl_conn_t *conn, struct writer *w,
85 struct cmd_env *env, void *arg)
4e90a9e0 86{
fe80711e 87 log_info("lldpctl", "quit lldpcli");
9a775667
VB
88 must_exit = 1;
89 return 1;
90}
91
92/**
93 * Send an "update" request.
94 */
95static int
96cmd_update(struct lldpctl_conn_t *conn, struct writer *w,
97 struct cmd_env *env, void *arg)
98{
99 log_info("lldpctl", "ask for global update");
100
101 lldpctl_atom_t *config = lldpctl_get_configuration(conn);
102 if (config == NULL) {
103 log_warnx("lldpctl", "unable to get configuration from lldpd. %s",
104 lldpctl_last_strerror(conn));
105 return 0;
4e90a9e0 106 }
9a775667
VB
107 if (lldpctl_atom_set_int(config,
108 lldpctl_k_config_tx_interval, -1) == NULL) {
109 log_warnx("lldpctl", "unable to ask lldpd for immediate retransmission. %s",
110 lldpctl_last_strerror(conn));
111 lldpctl_atom_dec_ref(config);
112 return 0;
4e90a9e0 113 }
9a775667
VB
114 log_info("lldpctl", "immediate retransmission requested successfuly");
115 lldpctl_atom_dec_ref(config);
116 return 1;
117}
118
35f6f4fb 119#ifdef HAVE_LIBREADLINE
23e7fa38
VB
120static int
121_cmd_complete(int all)
9a775667 122{
23e7fa38
VB
123 char **argv = NULL;
124 int argc = 0;
125 int rc = 1;
126 char *line = malloc(strlen(rl_line_buffer) + 2);
127 if (!line) return -1;
128 strcpy(line, rl_line_buffer);
129 line[rl_point] = 2; /* empty character, will force a word */
130 line[rl_point+1] = 0;
131
132 if (tokenize_line(line, &argc, &argv) != 0)
9a775667
VB
133 goto end;
134
23e7fa38 135 char *compl = commands_complete(root, argc, (const char **)argv, all);
9a775667 136 if (compl) {
23e7fa38
VB
137 int from = rl_point - strlen(argv[argc-1]);
138 rl_delete_text(from, rl_point);
139 rl_point = from;
140 if (rl_insert_text(compl) < 0) {
9a775667
VB
141 free(compl);
142 goto end;
143 }
144 free(compl);
23e7fa38 145 rc = 0;
9a775667
VB
146 goto end;
147 }
23e7fa38 148 /* No completion or several completion available. */
35f6f4fb
VB
149 fprintf(stderr, "\n");
150 rl_forced_update_display();
23e7fa38 151 rc = 0;
9a775667 152end:
23e7fa38
VB
153 free(line);
154 tokenize_free(argc, argv);
9a775667
VB
155 return rc;
156}
157
23e7fa38
VB
158static int
159cmd_complete(int count, int ch)
9a775667 160{
23e7fa38 161 return _cmd_complete(0);
9a775667
VB
162}
163
23e7fa38
VB
164static int
165cmd_help(int count, int ch)
9a775667 166{
23e7fa38 167 return _cmd_complete(1);
9a775667 168}
35f6f4fb
VB
169#else
170static char*
171readline()
172{
173 static char line[2048];
174 fprintf(stderr, "%s", prompt());
175 fflush(stderr);
176 if (fgets(line, sizeof(line) - 2, stdin) == NULL)
177 return NULL;
178 return line;
179}
180#endif
9a775667
VB
181
182static struct cmd_node*
183register_commands()
184{
185 root = commands_root();
186 register_commands_show(root);
187 register_commands_watch(root);
188 if (is_privileged()) {
189 commands_new(
190 commands_new(root, "update", "Update information and send LLDPU on all ports",
191 NULL, NULL, NULL),
192 NEWLINE, "Update information and send LLDPU on all ports",
193 NULL, cmd_update, NULL);
194 register_commands_configure(root);
195 }
196 commands_new(
197 commands_new(root, "exit", "Exit interpreter", NULL, NULL, NULL),
198 NEWLINE, "Exit interpreter", NULL, cmd_exit, NULL);
199 return root;
4e90a9e0
VB
200}
201
fe80711e
VB
202static int
203is_lldpctl(const char *name)
204{
205 static int last_result = -1;
206 if (last_result == -1 && name) {
207 char *basec = strdup(name);
208 if (!basec) return 0;
209 char *bname = basename(basec);
210 last_result = (!strcmp(bname, "lldpctl"));
211 free(basec);
212 }
213 return (last_result == -1)?0:last_result;
214}
215
4b292b55
VB
216int
217main(int argc, char *argv[])
218{
9a775667 219 int ch, debug = 1, rc = EXIT_FAILURE;
4e90a9e0 220 char *fmt = "plain";
048355f3 221 lldpctl_conn_t *conn = NULL;
9a775667
VB
222 struct writer *w;
223
fe80711e 224 char *interfaces = NULL;
4b292b55
VB
225
226 /* Get and parse command line options */
9a775667 227 while ((ch = getopt(argc, argv, "hdvf:")) != -1) {
4b292b55
VB
228 switch (ch) {
229 case 'h':
230 usage();
231 break;
232 case 'd':
233 debug++;
234 break;
235 case 'v':
236 fprintf(stdout, "%s\n", PACKAGE_VERSION);
237 exit(0);
238 break;
4b292b55
VB
239 case 'f':
240 fmt = optarg;
241 break;
4b292b55
VB
242 default:
243 usage();
244 }
245 }
246
247 log_init(debug, __progname);
248
9a775667
VB
249 /* Register commands */
250 root = register_commands();
251
fe80711e
VB
252 if (is_lldpctl(argv[0])) {
253 for (int i = optind; i < argc; i++) {
254 char *prev = interfaces;
255 if (asprintf(&interfaces, "%s%s%s",
256 prev?prev:"", prev?",":"", argv[i]) == -1) {
257 log_warnx("lldpctl", "not enough memory to build list of interfaces");
258 goto end;
259 }
260 free(prev);
261 }
262 must_exit = 1;
23e7fa38
VB
263 } else if (optind < argc) {
264 /* More arguments! */
265 must_exit = 1;
9a775667 266 } else {
35f6f4fb 267#ifdef HAVE_LIBREADLINE
23e7fa38
VB
268 /* Shell session */
269 rl_bind_key('?', cmd_help);
270 rl_bind_key('\t', cmd_complete);
35f6f4fb 271#endif
4e90a9e0
VB
272 }
273
9a775667
VB
274 /* Make a connection */
275 log_debug("lldpctl", "connect to lldpd");
276 conn = lldpctl_new(NULL, NULL, NULL);
277 if (conn == NULL)
278 exit(EXIT_FAILURE);
279
fe80711e 280 do {
9a775667 281 const char *line;
23e7fa38
VB
282 char **cargv;
283 int n, cargc;
a8be34ba 284 if (!is_lldpctl(NULL) && (optind >= argc)) {
23e7fa38
VB
285 line = readline(prompt());
286 if (line == NULL) break; /* EOF */
fe80711e
VB
287
288 /* Tokenize it */
289 log_debug("lldpctl", "tokenize command line");
23e7fa38 290 n = tokenize_line(line, &cargc, &cargv);
fe80711e
VB
291 switch (n) {
292 case -1:
293 log_warnx("lldpctl", "internal error while tokenizing");
294 goto end;
295 case 1:
fe80711e 296 log_warnx("lldpctl", "unmatched quotes");
fe80711e
VB
297 continue;
298 }
23e7fa38 299 if (cargc == 0) continue;
35f6f4fb 300#ifdef HAVE_READLINE_HISTORY
23e7fa38 301 add_history(line);
35f6f4fb 302#endif
8729d69f 303 }
9a775667
VB
304
305 /* Init output formatter */
306 if (strcmp(fmt, "plain") == 0) w = txt_init(stdout);
307 else if (strcmp(fmt, "keyvalue") == 0) w = kv_init(stdout);
308#ifdef USE_XML
309 else if (strcmp(fmt, "xml") == 0) w = xml_init(stdout);
8b7150e4
VB
310#endif
311#ifdef USE_JSON
9a775667 312 else if (strcmp(fmt, "json") == 0) w = json_init(stdout);
4e90a9e0 313#endif
9a775667 314 else w = txt_init(stdout);
4e90a9e0 315
fe80711e
VB
316 if (is_lldpctl(NULL)) {
317 if (!interfaces) {
23e7fa38 318 cargv = (char*[]){ "show", "neighbors", "details" };
a8be34ba 319 cargc = 3;
fe80711e 320 } else {
23e7fa38 321 cargv = (char*[]){ "show", "neighbors", "ports", interfaces, "details" };
a8be34ba 322 cargc = 5;
fe80711e 323 }
a8be34ba 324 } else if (optind < argc) {
23e7fa38 325 cargv = argv;
a8be34ba
VB
326 cargv = &cargv[optind];
327 cargc = argc - optind;
fe80711e
VB
328 }
329
9a775667
VB
330 /* Execute command */
331 if (commands_execute(conn, w,
23e7fa38 332 root, cargc, (const char **)cargv) != 0)
9a775667
VB
333 log_info("lldpctl", "an error occurred while executing last command");
334 w->finish(w);
fe80711e 335
23e7fa38
VB
336 if (!is_lldpctl(NULL) && optind >= argc)
337 tokenize_free(cargc, cargv);
fe80711e 338 } while (!must_exit);
4b292b55 339
9a775667
VB
340 rc = EXIT_SUCCESS;
341end:
342 if (conn) lldpctl_release(conn);
9a775667 343 if (root) commands_free(root);
fe80711e 344 free(interfaces);
9a775667 345 return rc;
4b292b55 346}