]>
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 | ||
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 |
35 | extern const char *__progname; | |
36 | #else | |
fe80711e | 37 | # define __progname "lldpcli" |
4b292b55 VB |
38 | #endif |
39 | ||
9a775667 VB |
40 | /* Global for completion */ |
41 | static struct cmd_node *root = NULL; | |
4b292b55 VB |
42 | |
43 | static void | |
9a775667 | 44 | usage() |
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 |
60 | static int |
61 | is_privileged() | |
62 | { | |
63 | return (!(getuid() != geteuid() || getgid() != getegid())); | |
64 | } | |
65 | ||
66 | static char* | |
23e7fa38 | 67 | prompt() |
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 | ||
79 | static int must_exit = 0; | |
80 | /** | |
81 | * Exit the interpreter. | |
82 | */ | |
83 | static int | |
84 | cmd_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 | */ | |
95 | static int | |
96 | cmd_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 |
120 | static 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 | 152 | end: |
23e7fa38 VB |
153 | free(line); |
154 | tokenize_free(argc, argv); | |
9a775667 VB |
155 | return rc; |
156 | } | |
157 | ||
23e7fa38 VB |
158 | static int |
159 | cmd_complete(int count, int ch) | |
9a775667 | 160 | { |
23e7fa38 | 161 | return _cmd_complete(0); |
9a775667 VB |
162 | } |
163 | ||
23e7fa38 VB |
164 | static int |
165 | cmd_help(int count, int ch) | |
9a775667 | 166 | { |
23e7fa38 | 167 | return _cmd_complete(1); |
9a775667 | 168 | } |
35f6f4fb VB |
169 | #else |
170 | static char* | |
171 | readline() | |
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 | |
182 | static struct cmd_node* | |
183 | register_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 |
202 | static int |
203 | is_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 |
216 | int |
217 | main(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; |
341 | end: | |
342 | if (conn) lldpctl_release(conn); | |
9a775667 | 343 | if (root) commands_free(root); |
fe80711e | 344 | free(interfaces); |
9a775667 | 345 | return rc; |
4b292b55 | 346 | } |