]> git.sur5r.net Git - ngadmin/blob - cli/src/admin.c
Cli: switch to filename completion after terminal commands
[ngadmin] / cli / src / admin.c
1
2 #ifdef HAVE_CONFIG_H
3 #include "config.h"
4 #endif
5
6 #include <stdio.h>
7 #include <signal.h>
8 #include <unistd.h>
9 #include <setjmp.h>
10
11 #include <getopt.h>
12 #ifdef HAVE_LIBREADLINE
13 #include <readline/readline.h>
14 #include <readline/history.h>
15 #endif
16
17 #include "common.h"
18 #include "commands.h"
19
20
21 #define MAXCOM  32
22
23
24
25 static const struct TreeNode* getSubCom (char **com, int n, int *t)
26 {
27         int i;
28         const struct TreeNode *cur, *next;
29         
30         
31         cur = &commands;
32         for (i = 0; i < n; i++) {
33                 /* we have reached a terminal command, exit */
34                 if (cur->sub == NULL)
35                         break;
36                 
37                 /* search sub command in sub command array */
38                 for (next = cur->sub; next->name != NULL && strcmp(next->name, com[i]) != 0; next++);
39                 
40                 /* sub command not found, exit */
41                 if (next->name == NULL)
42                         break;
43                 
44                 /* next command is now the current one */
45                 cur = next;
46         }
47         
48         *t = i;
49         
50         
51         return cur;
52 }
53
54
55 #ifdef HAVE_LIBREADLINE
56 static const struct TreeNode *compcur;
57
58
59 static char* my_generator (const char* text, int state)
60 {
61         static int len;
62         static const struct TreeNode *tn;
63         const char *name;
64         
65         
66         if (compcur == NULL) {
67                 /* sub command not found */
68                 return NULL;
69         } else if (state == 0) {
70                 tn = compcur->sub;
71                 len = strlen(text);
72         }
73         
74         if (tn == NULL) /* terminal command */
75                 return NULL;
76         
77         while ((name = tn++->name) != NULL) {
78                 if (strncmp(name, text, len) == 0)
79                         return strdup(name);
80         }
81         
82         
83         return NULL;
84 }
85
86
87 static char** my_completion (const char *text, int start, int end UNUSED)
88 {
89         char *line, *com[MAXCOM];
90         int i, n;
91         
92         
93         memset(com, 0, MAXCOM * sizeof(char*));
94         line = strdup(rl_line_buffer);
95         line[start] = '\0';
96         trim(line, start);
97         n = explode(line, com, MAXCOM);
98         free(line);
99         
100         compcur = getSubCom(com, n, &i);
101         for (i = 0; com[i] != NULL; i++)
102                 free(com[i]);
103         
104         if (i < n) /* unknown command */
105                 return NULL;
106         else if (compcur->sub == NULL) /* terminal command */
107                 return rl_completion_matches(text, rl_filename_completion_function);
108         else /* intermediate command */
109                 return rl_completion_matches(text, my_generator);
110 }
111 #endif /* HAVE_LIBREADLINE */
112
113
114 int main_loop_continue;
115 static struct ngadmin *nga;
116 static sigjmp_buf jmpbuf;
117 static struct termios orig_term;
118 struct termios current_term;
119 static bool batch;
120
121
122 NORET static void handler (int sig)
123 {
124         switch (sig) {
125         
126         case SIGTERM:
127         case SIGINT:
128                 printf("interrupt\n");
129                 
130                 current_term.c_lflag |= ECHO;
131                 tcsetattr(STDIN_FILENO, TCSANOW, &current_term);
132                 
133                 if (!batch && main_loop_continue)
134                         siglongjmp(jmpbuf, 1);
135         
136         default:
137                 ngadmin_close(nga);
138                 
139                 tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
140                 
141                 exit(0);
142         }
143 }
144
145
146 static int pre_login (const struct ether_addr *mac, int retries)
147 {
148         const struct swi_attr *sa;
149         int i, n, err;
150         
151         
152         for (i = 1; retries <= 0 || i <= retries; i++) {
153                 /* scan */
154                 printf("scan... ");
155                 fflush(stdout);
156                 err = ngadmin_scan(nga);
157                 if (err < 0) {
158                         printErrCode(err);
159                         return err;
160                 }
161                 
162                 /* search switch with requested MAC */
163                 sa = ngadmin_getSwitchTab(nga, &n);
164                 while (--n >= 0) {
165                         if (memcmp(mac, &sa[n].mac, ETH_ALEN) == 0)
166                                 break;
167                 }
168         
169                 if (n < 0) {
170                         printf("no switch found\n");
171                 } else {
172                         printf("done\n");
173                         break;
174                 }
175         }
176         
177         if (n < 0)
178                 return 1;
179
180         /* login */
181         printf("login... ");
182         fflush(stdout);
183         err = ngadmin_login(nga, n);
184         if (err < 0)
185                 printErrCode(err);
186         else
187                 printf("done\n");
188         
189         return err;
190 }
191
192
193 int main (int argc, char **argv)
194 {
195         static const struct option opts[] = {
196                 {"batch", no_argument, NULL, 'a'},
197                 {"keep-broadcasting", no_argument, NULL, 'b'},
198                 {"force-interface", no_argument, NULL, 'f'},
199                 {"help", no_argument, NULL, 'h'},
200                 {"interface", required_argument, NULL, 'i'},
201                 {"local-broadcast", no_argument, NULL, 'l'},
202                 {"mac", required_argument, NULL, 'm'},
203                 {"password", required_argument, NULL, 'p'},
204                 {"retries", required_argument, NULL, 'r'},
205                 {"timeout", required_argument, NULL, 't'},
206                 {0, 0, 0, 0}
207         };
208         char *line, *com[MAXCOM];
209         const char *iface = "eth0", *password = NULL;
210         float timeout = 0.f;
211         bool kb = false, force = false, global = true;
212         struct timeval tv;
213         const struct TreeNode *cur, *next;
214         struct ether_addr *mac = NULL;
215         int i, n, retries = 3;
216         
217         
218         tcgetattr(STDIN_FILENO, &orig_term);
219         current_term = orig_term;
220 #ifdef HAVE_LIBREADLINE
221         batch = false;
222 #else
223         batch = true;
224 #endif
225         
226         opterr = 0;
227         
228         while ((n = getopt_long(argc, argv, "abfhi:lm:p:r:t:", opts, NULL)) != -1) {
229                 switch (n) {
230                 
231                 case 'a':
232                         batch = true;
233                         break;
234                 
235                 case 'b':
236                         kb = true;
237                         break;
238                 
239                 case 'f':
240                         force = true;
241                         break;
242                 
243                 case 'h':
244                         printf("usage: %s [-a] [-b] [-f] [-g] [-i <interface>] [-m <MAC>] [-p <password>]\n", argv[0]);
245                         goto end;
246                 
247                 case 'i':
248                         iface = optarg;
249                         break;
250                 
251                 case 'l':
252                         global = false;
253                         break;
254                 
255                 case 'm':
256                         mac = ether_aton(optarg);
257                         if (mac == NULL) {
258                                 printf("invalid MAC\n");
259                                 goto end;
260                         }
261                         break;
262                 
263                 case 'p':
264                         password = optarg;
265                         break;
266                 
267                 case 'r':
268                         retries = strtol(optarg, NULL, 0);
269                         break;
270                 
271                 case 't':
272                         timeout = strtof(optarg, NULL);
273                         break;
274                 
275                 case '?':
276                         printf("unknown option: \"%s\"\n", argv[optind - 1]);
277                         goto end;
278                 }
279         }
280         
281         argc -= optind;
282         argv += optind;
283         
284         if (argc != 0) {
285                 printf("unknown trailing options\n");
286                 goto end;
287         }
288         
289         
290         memset(com, 0, MAXCOM * sizeof(char*));
291         
292         nga = ngadmin_init(iface);
293         if (nga == NULL) {
294                 fprintf(stderr, "initialization error\n");
295                 goto end;
296         }
297         
298         /* set timeout */
299         if (timeout > 0.f) {
300                 tv.tv_sec = (int)timeout;
301                 tv.tv_usec = (int)((timeout - (float)tv.tv_sec) * 1.e6f);
302                 ngadmin_setTimeout(nga, &tv);
303         }
304         
305         
306         if (ngadmin_setKeepBroadcasting(nga, kb) != ERR_OK)
307                 goto end;
308         
309         if (force && ngadmin_forceInterface(nga) != ERR_OK)
310                 goto end;
311         
312         if (ngadmin_useGlobalBroadcast(nga, global) != ERR_OK)
313                 goto end;
314         
315         /* non-TTY inputs are automatically set to batch mode */
316         if (!isatty(STDIN_FILENO))
317                 batch = true;
318         
319         if (password != NULL)
320                 ngadmin_setPassword(nga, password);
321         
322         signal(SIGTERM, handler);
323         signal(SIGINT, handler);
324         
325         /* automatic scan & login when switch MAC is specified on the command line */
326         if (mac != NULL && pre_login(mac, retries) != 0)
327                 goto end;
328         
329         if (batch) {
330                 /* in batch mode, we must be logged to continue */
331                 if (ngadmin_getCurrentSwitch(nga) == NULL) {
332                         printf("must be logged\n");
333                         goto end;
334                 }
335         } else {
336 #ifdef HAVE_LIBREADLINE
337                 /* initialize readline functions */
338                 rl_attempted_completion_function = my_completion;
339                 rl_completion_entry_function = my_generator;
340                 
341                 sigsetjmp(jmpbuf, 1);
342 #endif
343         }
344         
345         main_loop_continue = 1;
346         while (main_loop_continue) {
347                 /* read user input */
348                 line = NULL;
349                 n = 0;
350                 if (batch)
351                         n = getline(&line, (size_t*)&i, stdin);
352 #ifdef HAVE_LIBREADLINE
353                 else
354                         line = readline("> ");
355 #endif
356                 if (n < 0 || line == NULL)
357                         goto end;
358                 
359                 /* split string into words */
360                 trim(line, strlen(line));
361                 n = explode(line, com, MAXCOM);
362                 
363                 if (n == 0) {
364                         free(line);
365                         continue;
366                 } else {
367 #ifdef HAVE_LIBREADLINE
368                         if (!batch)
369                                 add_history(line);
370 #endif
371                         free(line);
372                 }
373                 
374                 cur = getSubCom(com, n, &i);
375                 
376                 if (cur->sub != NULL) {
377                         /* not terminal command */
378                         if (i == 0) {
379                                 /* root command */
380                                 printf("unknown command: %s\n", com[i]);
381                         } else if (i < n) {
382                                 /* intermediate command, remaining string */
383                                 printf("unknown %s subcommand: %s\n", com[i - 1], com[i]);
384                         } else {
385                                 /* intermediate command, no remaining string */
386                                 /* print available subcommands */
387                                 for (next = cur->sub; next->name != NULL; next++)
388                                         printf("%s ", next->name);
389                                 putchar('\n');
390                         }
391                 } else if (cur->comfunc == NULL) {
392                         /* erroneous terminal command without function */
393                         printf("terminal command without function\n");
394                 } else {
395                         /* execute terminal command */
396                         cur->comfunc(n - i, (const char**)&com[i], nga);
397                 }
398                 
399                 for (i = 0; com[i] != NULL; i++) {
400                         free(com[i]);
401                         com[i] = NULL;
402                 }
403         }
404         
405 end:
406         handler(0);
407 }
408
409