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