]>
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 | ||
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 |
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* | |
67 | prompt(EditLine *el) | |
68 | { | |
69 | int privileged = is_privileged(); | |
70 | if (privileged) | |
fe80711e VB |
71 | return "[lldpcli] # "; |
72 | return "[lldpcli] $ "; | |
9a775667 VB |
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) | |
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 | */ | |
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; | |
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 | ||
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; | |
4e90a9e0 VB |
179 | } |
180 | ||
fe80711e VB |
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 | ||
4b292b55 VB |
195 | int |
196 | main(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 | 291 | skipeditline: |
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; |
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); | |
fe80711e | 371 | free(interfaces); |
9a775667 | 372 | return rc; |
4b292b55 | 373 | } |