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