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