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