]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/client/lldpcli.c
lldpcli: ensure `conn` is initialized to NULL to avoid a compiler warning
[thirdparty/lldpd.git] / src / client / lldpcli.c
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
18 #define _GNU_SOURCE
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <time.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 #include <arpa/inet.h>
29 #include <histedit.h>
30 #include <libgen.h>
31
32 #include "client.h"
33
34 #ifdef HAVE___PROGNAME
35 extern const char *__progname;
36 #else
37 # define __progname "lldpcli"
38 #endif
39
40 /* Global for completion */
41 static struct cmd_node *root = NULL;
42
43 static void
44 usage()
45 {
46 fprintf(stderr, "Usage: %s [OPTIONS ...] [COMMAND ...]\n", __progname);
47 fprintf(stderr, "Version: %s\n", PACKAGE_STRING);
48
49 fprintf(stderr, "\n");
50
51 fprintf(stderr, "-d Enable more debugging information.\n");
52 fprintf(stderr, "-f format Choose output format (plain, keyvalue or xml).\n");
53
54 fprintf(stderr, "\n");
55
56 fprintf(stderr, "see manual page lldpcli(8) for more information\n");
57 exit(1);
58 }
59
60 static int
61 is_privileged()
62 {
63 return (!(getuid() != geteuid() || getgid() != getegid()));
64 }
65
66 static char*
67 prompt(EditLine *el)
68 {
69 int privileged = is_privileged();
70 if (privileged)
71 return "[lldpcli] # ";
72 return "[lldpcli] $ ";
73 }
74
75 static int must_exit = 0;
76 /**
77 * Exit the interpreter.
78 */
79 static int
80 cmd_exit(struct lldpctl_conn_t *conn, struct writer *w,
81 struct cmd_env *env, void *arg)
82 {
83 log_info("lldpctl", "quit lldpcli");
84 must_exit = 1;
85 return 1;
86 }
87
88 /**
89 * Send an "update" request.
90 */
91 static int
92 cmd_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;
102 }
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;
109 }
110 log_info("lldpctl", "immediate retransmission requested successfuly");
111 lldpctl_atom_dec_ref(config);
112 return 1;
113 }
114
115 static 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;
144 end:
145 if (eltok) tok_end(eltok);
146 return rc;
147 }
148
149 static unsigned char
150 cmd_complete(EditLine *el, int ch)
151 {
152 return _cmd_complete(el, ch, 0);
153 }
154
155 static unsigned char
156 cmd_help(EditLine *el, int ch)
157 {
158 return _cmd_complete(el, ch, 1);
159 }
160
161 static struct cmd_node*
162 register_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;
179 }
180
181 static int
182 is_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
195 int
196 main(int argc, char *argv[])
197 {
198 int ch, debug = 1, rc = EXIT_FAILURE;
199 char *fmt = "plain";
200 lldpctl_conn_t *conn = NULL;
201 struct writer *w;
202
203 EditLine *el = NULL;
204 History *elhistory = NULL;
205 HistEvent elhistev;
206 Tokenizer *eltok = NULL;
207
208 char *interfaces = NULL;
209
210 /* Get and parse command line options */
211 while ((ch = getopt(argc, argv, "hdvf:")) != -1) {
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;
223 case 'f':
224 fmt = optarg;
225 break;
226 default:
227 usage();
228 }
229 }
230
231 log_init(debug, __progname);
232
233 /* Register commands */
234 root = register_commands();
235
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;
248 } else {
249 if (optind < argc) {
250 /* More arguments! */
251 must_exit = 1;
252 }
253 }
254
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);
263 el_set(el, EL_SIGNAL, 1);
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);
273 }
274
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);
282 }
283
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
291 skipeditline:
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
298 do {
299 const char *line;
300 const char **cargv;
301 int count, n, cargc;
302
303 if (!is_lldpctl(NULL) && (optind >= argc)) {
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");
310 n = tok_str(eltok, line, &cargc, &cargv);
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 }
323 if (cargc == 0) {
324 tok_reset(eltok);
325 continue;
326 }
327 if (elhistory) history(elhistory, &elhistev, H_ENTER, line);
328 }
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);
335 #endif
336 #ifdef USE_JSON
337 else if (strcmp(fmt, "json") == 0) w = json_init(stdout);
338 #endif
339 else w = txt_init(stdout);
340
341 if (is_lldpctl(NULL)) {
342 if (!interfaces) {
343 cargv = (const char*[]){ "show", "neighbors", "details" };
344 cargc = 3;
345 } else {
346 cargv = (const char*[]){ "show", "neighbors", "ports", interfaces, "details" };
347 cargc = 5;
348 }
349 } else if (optind < argc) {
350 cargv = (const char **)argv;
351 cargv = &cargv[optind];
352 cargc = argc - optind;
353 }
354
355 /* Execute command */
356 if (commands_execute(conn, w,
357 root, cargc, cargv) != 0)
358 log_info("lldpctl", "an error occurred while executing last command");
359 w->finish(w);
360
361 if (eltok) tok_reset(eltok);
362 } while (!must_exit);
363
364 rc = EXIT_SUCCESS;
365 end:
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);
371 free(interfaces);
372 return rc;
373 }