]>
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 VB |
24 | #include <sys/types.h> |
25 | #include <sys/socket.h> | |
26 | #include <sys/un.h> | |
27 | #include <arpa/inet.h> | |
9a775667 | 28 | #include <histedit.h> |
4b292b55 | 29 | |
4b292b55 VB |
30 | #include "client.h" |
31 | ||
4b292b55 VB |
32 | #ifdef HAVE___PROGNAME |
33 | extern const char *__progname; | |
34 | #else | |
35 | # define __progname "lldpctl" | |
36 | #endif | |
37 | ||
9a775667 VB |
38 | /* Global for completion */ |
39 | static struct cmd_node *root = NULL; | |
4b292b55 VB |
40 | |
41 | static void | |
9a775667 | 42 | usage() |
4b292b55 | 43 | { |
9a775667 | 44 | fprintf(stderr, "Usage: %s [OPTIONS ...] [COMMAND ...]\n", __progname); |
4b292b55 VB |
45 | fprintf(stderr, "Version: %s\n", PACKAGE_STRING); |
46 | ||
47 | fprintf(stderr, "\n"); | |
48 | ||
49 | fprintf(stderr, "-d Enable more debugging information.\n"); | |
4b292b55 | 50 | fprintf(stderr, "-f format Choose output format (plain, keyvalue or xml).\n"); |
4b292b55 VB |
51 | |
52 | fprintf(stderr, "\n"); | |
53 | ||
54 | fprintf(stderr, "see manual page lldpctl(8) for more information\n"); | |
55 | exit(1); | |
56 | } | |
57 | ||
9a775667 VB |
58 | static int |
59 | is_privileged() | |
60 | { | |
61 | return (!(getuid() != geteuid() || getgid() != getegid())); | |
62 | } | |
63 | ||
64 | static char* | |
65 | prompt(EditLine *el) | |
66 | { | |
67 | int privileged = is_privileged(); | |
68 | if (privileged) | |
69 | return "[lldpctl] # "; | |
70 | return "[lldpctl] $ "; | |
71 | } | |
72 | ||
73 | static int must_exit = 0; | |
74 | /** | |
75 | * Exit the interpreter. | |
76 | */ | |
77 | static int | |
78 | cmd_exit(struct lldpctl_conn_t *conn, struct writer *w, | |
79 | struct cmd_env *env, void *arg) | |
4e90a9e0 | 80 | { |
9a775667 VB |
81 | log_info("lldpctl", "quit lldpctl"); |
82 | must_exit = 1; | |
83 | return 1; | |
84 | } | |
85 | ||
86 | /** | |
87 | * Send an "update" request. | |
88 | */ | |
89 | static int | |
90 | cmd_update(struct lldpctl_conn_t *conn, struct writer *w, | |
91 | struct cmd_env *env, void *arg) | |
92 | { | |
93 | log_info("lldpctl", "ask for global update"); | |
94 | ||
95 | lldpctl_atom_t *config = lldpctl_get_configuration(conn); | |
96 | if (config == NULL) { | |
97 | log_warnx("lldpctl", "unable to get configuration from lldpd. %s", | |
98 | lldpctl_last_strerror(conn)); | |
99 | return 0; | |
4e90a9e0 | 100 | } |
9a775667 VB |
101 | if (lldpctl_atom_set_int(config, |
102 | lldpctl_k_config_tx_interval, -1) == NULL) { | |
103 | log_warnx("lldpctl", "unable to ask lldpd for immediate retransmission. %s", | |
104 | lldpctl_last_strerror(conn)); | |
105 | lldpctl_atom_dec_ref(config); | |
106 | return 0; | |
4e90a9e0 | 107 | } |
9a775667 VB |
108 | log_info("lldpctl", "immediate retransmission requested successfuly"); |
109 | lldpctl_atom_dec_ref(config); | |
110 | return 1; | |
111 | } | |
112 | ||
113 | static unsigned char | |
114 | _cmd_complete(EditLine *el, int ch, int all) | |
115 | { | |
116 | int rc = CC_ERROR; | |
117 | Tokenizer *eltok; | |
118 | if ((eltok = tok_init(NULL)) == NULL) | |
119 | goto end; | |
120 | ||
121 | const LineInfo *li = el_line(el); | |
122 | ||
123 | const char **argv; | |
124 | char *compl; | |
125 | int argc, cursorc, cursoro; | |
126 | if (tok_line(eltok, li, &argc, &argv, &cursorc, &cursoro) != 0) | |
127 | goto end; | |
128 | compl = commands_complete(root, argc, argv, cursorc, cursoro, all); | |
129 | if (compl) { | |
130 | el_deletestr(el, cursoro); | |
131 | if (el_insertstr(el, compl) == -1) { | |
132 | free(compl); | |
133 | goto end; | |
134 | } | |
135 | free(compl); | |
136 | rc = CC_REDISPLAY; | |
137 | goto end; | |
138 | } | |
139 | /* No completion or several completion available. We beep. */ | |
140 | el_beep(el); | |
141 | rc = CC_REDISPLAY; | |
142 | end: | |
143 | if (eltok) tok_end(eltok); | |
144 | return rc; | |
145 | } | |
146 | ||
147 | static unsigned char | |
148 | cmd_complete(EditLine *el, int ch) | |
149 | { | |
150 | return _cmd_complete(el, ch, 0); | |
151 | } | |
152 | ||
153 | static unsigned char | |
154 | cmd_help(EditLine *el, int ch) | |
155 | { | |
156 | return _cmd_complete(el, ch, 1); | |
157 | } | |
158 | ||
159 | static struct cmd_node* | |
160 | register_commands() | |
161 | { | |
162 | root = commands_root(); | |
163 | register_commands_show(root); | |
164 | register_commands_watch(root); | |
165 | if (is_privileged()) { | |
166 | commands_new( | |
167 | commands_new(root, "update", "Update information and send LLDPU on all ports", | |
168 | NULL, NULL, NULL), | |
169 | NEWLINE, "Update information and send LLDPU on all ports", | |
170 | NULL, cmd_update, NULL); | |
171 | register_commands_configure(root); | |
172 | } | |
173 | commands_new( | |
174 | commands_new(root, "exit", "Exit interpreter", NULL, NULL, NULL), | |
175 | NEWLINE, "Exit interpreter", NULL, cmd_exit, NULL); | |
176 | return root; | |
4e90a9e0 VB |
177 | } |
178 | ||
4b292b55 VB |
179 | int |
180 | main(int argc, char *argv[]) | |
181 | { | |
9a775667 | 182 | int ch, debug = 1, rc = EXIT_FAILURE; |
4e90a9e0 | 183 | char *fmt = "plain"; |
4b292b55 | 184 | lldpctl_conn_t *conn; |
9a775667 VB |
185 | struct writer *w; |
186 | ||
187 | EditLine *el; | |
188 | History *elhistory; | |
189 | HistEvent elhistev; | |
190 | Tokenizer *eltok; | |
4b292b55 VB |
191 | |
192 | /* Get and parse command line options */ | |
9a775667 | 193 | while ((ch = getopt(argc, argv, "hdvf:")) != -1) { |
4b292b55 VB |
194 | switch (ch) { |
195 | case 'h': | |
196 | usage(); | |
197 | break; | |
198 | case 'd': | |
199 | debug++; | |
200 | break; | |
201 | case 'v': | |
202 | fprintf(stdout, "%s\n", PACKAGE_VERSION); | |
203 | exit(0); | |
204 | break; | |
4b292b55 VB |
205 | case 'f': |
206 | fmt = optarg; | |
207 | break; | |
4b292b55 VB |
208 | default: |
209 | usage(); | |
210 | } | |
211 | } | |
212 | ||
213 | log_init(debug, __progname); | |
214 | ||
9a775667 VB |
215 | /* Register commands */ |
216 | root = register_commands(); | |
217 | ||
218 | /* Init editline */ | |
219 | log_debug("lldpctl", "init editline"); | |
220 | el = el_init("lldpctl", stdin, stdout, stderr); | |
221 | if (el == NULL) { | |
222 | log_warnx("lldpctl", "unable to setup editline"); | |
223 | goto end; | |
224 | } | |
225 | el_set(el, EL_PROMPT, prompt); | |
226 | el_set(el, EL_SIGNAL, 0); | |
227 | el_set(el, EL_EDITOR, "emacs"); | |
228 | /* If on a TTY, setup completion */ | |
229 | if (isatty(STDERR_FILENO)) { | |
230 | el_set(el, EL_ADDFN, "command_complete", | |
231 | "Execute completion", cmd_complete); | |
232 | el_set(el, EL_ADDFN, "command_help", | |
233 | "Show completion", cmd_help); | |
234 | el_set(el, EL_BIND, "^I", "command_complete", NULL); | |
235 | el_set(el, EL_BIND, "?", "command_help", NULL); | |
4b292b55 VB |
236 | } |
237 | ||
9a775667 VB |
238 | /* Init history */ |
239 | elhistory = history_init(); | |
240 | if (elhistory == NULL) { | |
241 | log_warnx("lldpctl", "unable to enable history"); | |
242 | } else { | |
243 | history(elhistory, &elhistev, H_SETSIZE, 800); | |
244 | el_set(el, EL_HIST, history, elhistory); | |
4e90a9e0 VB |
245 | } |
246 | ||
9a775667 VB |
247 | /* Init tokenizer */ |
248 | eltok = tok_init(NULL); | |
249 | if (eltok == NULL) { | |
250 | log_warnx("lldpctl", "unable to initialize tokenizer"); | |
251 | goto end; | |
252 | } | |
253 | ||
254 | /* Make a connection */ | |
255 | log_debug("lldpctl", "connect to lldpd"); | |
256 | conn = lldpctl_new(NULL, NULL, NULL); | |
257 | if (conn == NULL) | |
258 | exit(EXIT_FAILURE); | |
259 | ||
260 | while (!must_exit) { | |
261 | const char *line; | |
262 | const char **argv; | |
263 | int count, n, argc; | |
264 | ||
265 | /* Read a new line. */ | |
266 | line = el_gets(el, &count); | |
267 | if (line == NULL) break; | |
268 | ||
269 | /* Tokenize it */ | |
270 | log_debug("lldpctl", "tokenize command line"); | |
271 | n = tok_str(eltok, line, &argc, &argv); | |
272 | switch (n) { | |
273 | case -1: | |
274 | log_warnx("lldpctl", "internal error while tokenizing"); | |
275 | goto end; | |
276 | case 1: | |
277 | case 2: | |
278 | case 3: | |
279 | /* TODO: handle multiline statements */ | |
280 | log_warnx("lldpctl", "unmatched quotes"); | |
281 | tok_reset(eltok); | |
282 | continue; | |
8729d69f | 283 | } |
9a775667 VB |
284 | if (argc == 0) { |
285 | tok_reset(eltok); | |
286 | continue; | |
8729d69f | 287 | } |
9a775667 VB |
288 | if (elhistory) history(elhistory, &elhistev, H_ENTER, line); |
289 | ||
290 | /* Init output formatter */ | |
291 | if (strcmp(fmt, "plain") == 0) w = txt_init(stdout); | |
292 | else if (strcmp(fmt, "keyvalue") == 0) w = kv_init(stdout); | |
293 | #ifdef USE_XML | |
294 | else if (strcmp(fmt, "xml") == 0) w = xml_init(stdout); | |
8b7150e4 VB |
295 | #endif |
296 | #ifdef USE_JSON | |
9a775667 | 297 | else if (strcmp(fmt, "json") == 0) w = json_init(stdout); |
4e90a9e0 | 298 | #endif |
9a775667 | 299 | else w = txt_init(stdout); |
4e90a9e0 | 300 | |
9a775667 VB |
301 | /* Execute command */ |
302 | if (commands_execute(conn, w, | |
303 | root, argc, argv) != 0) | |
304 | log_info("lldpctl", "an error occurred while executing last command"); | |
305 | w->finish(w); | |
306 | tok_reset(eltok); | |
307 | } | |
4b292b55 | 308 | |
9a775667 VB |
309 | rc = EXIT_SUCCESS; |
310 | end: | |
311 | if (conn) lldpctl_release(conn); | |
312 | if (eltok) tok_end(eltok); | |
313 | if (elhistory) history_end(elhistory); | |
314 | if (el) el_end(el); | |
315 | if (root) commands_free(root); | |
316 | return rc; | |
4b292b55 | 317 | } |