wlantest: Add command for fetching wlantest version
[mech_eap.git] / wlantest / wlantest_cli.c
1 /*
2  * wlantest controller
3  * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14
15 #include "utils/includes.h"
16 #include <sys/un.h>
17
18 #include "utils/common.h"
19 #include "wlantest_ctrl.h"
20
21
22 static u8 * attr_get(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr,
23                      size_t *len)
24 {
25         u8 *pos = buf;
26
27         while (pos + 8 <= buf + buflen) {
28                 enum wlantest_ctrl_attr a;
29                 size_t alen;
30                 a = WPA_GET_BE32(pos);
31                 pos += 4;
32                 alen = WPA_GET_BE32(pos);
33                 pos += 4;
34                 if (pos + alen > buf + buflen) {
35                         printf("Invalid control message attribute\n");
36                         return NULL;
37                 }
38                 if (a == attr) {
39                         *len = alen;
40                         return pos;
41                 }
42                 pos += alen;
43         }
44
45         return NULL;
46 }
47
48
49 static u8 * attr_hdr_add(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
50                          size_t len)
51 {
52         if (pos == NULL || end - pos < 8 + len)
53                 return NULL;
54         WPA_PUT_BE32(pos, attr);
55         pos += 4;
56         WPA_PUT_BE32(pos, len);
57         pos += 4;
58         return pos;
59 }
60
61
62 static u8 * attr_add_be32(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
63                           u32 val)
64 {
65         if (pos == NULL || end - pos < 12)
66                 return NULL;
67         WPA_PUT_BE32(pos, attr);
68         pos += 4;
69         WPA_PUT_BE32(pos, 4);
70         pos += 4;
71         WPA_PUT_BE32(pos, val);
72         pos += 4;
73         return pos;
74 }
75
76
77 static int cmd_send_and_recv(int s, const u8 *cmd, size_t cmd_len,
78                              u8 *resp, size_t max_resp_len)
79 {
80         int res;
81         enum wlantest_ctrl_cmd cmd_resp;
82
83         if (send(s, cmd, cmd_len, 0) < 0)
84                 return -1;
85         res = recv(s, resp, max_resp_len, 0);
86         if (res < 4)
87                 return -1;
88
89         cmd_resp = WPA_GET_BE32(resp);
90         if (cmd_resp == WLANTEST_CTRL_SUCCESS)
91                 return res;
92
93         if (cmd_resp == WLANTEST_CTRL_UNKNOWN_CMD)
94                 printf("Unknown command\n");
95         else if (cmd_resp == WLANTEST_CTRL_INVALID_CMD)
96                 printf("Invalid command\n");
97
98         return -1;
99 }
100
101
102 static int cmd_simple(int s, enum wlantest_ctrl_cmd cmd)
103 {
104         u8 buf[4];
105         int res;
106         WPA_PUT_BE32(buf, cmd);
107         res = cmd_send_and_recv(s, buf, sizeof(buf), buf, sizeof(buf));
108         return res < 0 ? -1 : 0;
109 }
110
111
112 static int cmd_ping(int s, int argc, char *argv[])
113 {
114         int res = cmd_simple(s, WLANTEST_CTRL_PING);
115         if (res == 0)
116                 printf("PONG\n");
117         return res == 0;
118 }
119
120
121 static int cmd_terminate(int s, int argc, char *argv[])
122 {
123         return cmd_simple(s, WLANTEST_CTRL_TERMINATE);
124 }
125
126
127 static int cmd_list_bss(int s, int argc, char *argv[])
128 {
129         u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
130         u8 buf[4];
131         u8 *bssid;
132         size_t len;
133         int rlen, i;
134
135         WPA_PUT_BE32(buf, WLANTEST_CTRL_LIST_BSS);
136         rlen = cmd_send_and_recv(s, buf, sizeof(buf), resp, sizeof(resp));
137         if (rlen < 0)
138                 return -1;
139
140         bssid = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_BSSID, &len);
141         if (bssid == NULL)
142                 return -1;
143
144         for (i = 0; i < len / ETH_ALEN; i++)
145                 printf(MACSTR " ", MAC2STR(bssid + ETH_ALEN * i));
146         printf("\n");
147
148         return 0;
149 }
150
151
152 static int cmd_list_sta(int s, int argc, char *argv[])
153 {
154         u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
155         u8 buf[100], *pos;
156         u8 *addr;
157         size_t len;
158         int rlen, i;
159
160         if (argc < 1) {
161                 printf("list_sta needs one argument: BSSID\n");
162                 return -1;
163         }
164
165         pos = buf;
166         WPA_PUT_BE32(pos, WLANTEST_CTRL_LIST_STA);
167         pos += 4;
168         WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
169         pos += 4;
170         WPA_PUT_BE32(pos, ETH_ALEN);
171         pos += 4;
172         if (hwaddr_aton(argv[0], pos) < 0) {
173                 printf("Invalid BSSID '%s'\n", argv[0]);
174                 return -1;
175         }
176         pos += ETH_ALEN;
177
178         rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
179         if (rlen < 0)
180                 return -1;
181
182         addr = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_STA_ADDR, &len);
183         if (addr == NULL)
184                 return -1;
185
186         for (i = 0; i < len / ETH_ALEN; i++)
187                 printf(MACSTR " ", MAC2STR(addr + ETH_ALEN * i));
188         printf("\n");
189
190         return 0;
191 }
192
193
194 static int cmd_flush(int s, int argc, char *argv[])
195 {
196         return cmd_simple(s, WLANTEST_CTRL_FLUSH);
197 }
198
199
200 static int cmd_clear_sta_counters(int s, int argc, char *argv[])
201 {
202         u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
203         u8 buf[100], *pos;
204         int rlen;
205
206         if (argc < 2) {
207                 printf("clear_sta_counters needs two arguments: BSSID and "
208                        "STA address\n");
209                 return -1;
210         }
211
212         pos = buf;
213         WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_STA_COUNTERS);
214         pos += 4;
215         WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
216         pos += 4;
217         WPA_PUT_BE32(pos, ETH_ALEN);
218         pos += 4;
219         if (hwaddr_aton(argv[0], pos) < 0) {
220                 printf("Invalid BSSID '%s'\n", argv[0]);
221                 return -1;
222         }
223         pos += ETH_ALEN;
224
225         WPA_PUT_BE32(pos, WLANTEST_ATTR_STA_ADDR);
226         pos += 4;
227         WPA_PUT_BE32(pos, ETH_ALEN);
228         pos += 4;
229         if (hwaddr_aton(argv[1], pos) < 0) {
230                 printf("Invalid STA address '%s'\n", argv[1]);
231                 return -1;
232         }
233         pos += ETH_ALEN;
234
235         rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
236         if (rlen < 0)
237                 return -1;
238         printf("OK\n");
239         return 0;
240 }
241
242
243 static int cmd_clear_bss_counters(int s, int argc, char *argv[])
244 {
245         u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
246         u8 buf[100], *pos;
247         int rlen;
248
249         if (argc < 1) {
250                 printf("clear_bss_counters needs one argument: BSSID\n");
251                 return -1;
252         }
253
254         pos = buf;
255         WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_BSS_COUNTERS);
256         pos += 4;
257         WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
258         pos += 4;
259         WPA_PUT_BE32(pos, ETH_ALEN);
260         pos += 4;
261         if (hwaddr_aton(argv[0], pos) < 0) {
262                 printf("Invalid BSSID '%s'\n", argv[0]);
263                 return -1;
264         }
265         pos += ETH_ALEN;
266
267         rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
268         if (rlen < 0)
269                 return -1;
270         printf("OK\n");
271         return 0;
272 }
273
274
275 struct sta_counters {
276         const char *name;
277         enum wlantest_sta_counter num;
278 };
279
280 static const struct sta_counters sta_counters[] = {
281         { "auth_tx", WLANTEST_STA_COUNTER_AUTH_TX },
282         { "auth_rx", WLANTEST_STA_COUNTER_AUTH_RX },
283         { "assocreq_tx", WLANTEST_STA_COUNTER_ASSOCREQ_TX },
284         { "reassocreq_tx", WLANTEST_STA_COUNTER_REASSOCREQ_TX },
285         { "ptk_learned", WLANTEST_STA_COUNTER_PTK_LEARNED },
286         { "valid_deauth_tx", WLANTEST_STA_COUNTER_VALID_DEAUTH_TX },
287         { "valid_deauth_rx", WLANTEST_STA_COUNTER_VALID_DEAUTH_RX },
288         { "invalid_deauth_tx", WLANTEST_STA_COUNTER_INVALID_DEAUTH_TX },
289         { "invalid_deauth_rx", WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX },
290         { "valid_disassoc_tx", WLANTEST_STA_COUNTER_VALID_DISASSOC_TX },
291         { "valid_disassoc_rx", WLANTEST_STA_COUNTER_VALID_DISASSOC_RX },
292         { "invalid_disassoc_tx", WLANTEST_STA_COUNTER_INVALID_DISASSOC_TX },
293         { "invalid_disassoc_rx", WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX },
294         { "valid_saqueryreq_tx", WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_TX },
295         { "valid_saqueryreq_rx", WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_RX },
296         { "invalid_saqueryreq_tx",
297           WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_TX },
298         { "invalid_saqueryreq_rx",
299           WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_RX },
300         { "valid_saqueryresp_tx", WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_TX },
301         { "valid_saqueryresp_rx", WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_RX },
302         { "invalid_saqueryresp_tx",
303           WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_TX },
304         { "invalid_saqueryresp_rx",
305           WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_RX },
306         { NULL, 0 }
307 };
308
309 static int cmd_get_sta_counter(int s, int argc, char *argv[])
310 {
311         u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
312         u8 buf[100], *end, *pos;
313         int rlen, i;
314         size_t len;
315
316         if (argc != 3) {
317                 printf("get_sta_counter needs at three arguments: "
318                        "counter name, BSSID, and STA address\n");
319                 return -1;
320         }
321
322         pos = buf;
323         end = buf + sizeof(buf);
324         WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_STA_COUNTER);
325         pos += 4;
326
327         for (i = 0; sta_counters[i].name; i++) {
328                 if (os_strcasecmp(sta_counters[i].name, argv[0]) == 0)
329                         break;
330         }
331         if (sta_counters[i].name == NULL) {
332                 printf("Unknown STA counter '%s'\n", argv[0]);
333                 printf("Counters:");
334                 for (i = 0; sta_counters[i].name; i++)
335                         printf(" %s", sta_counters[i].name);
336                 printf("\n");
337                 return -1;
338         }
339
340         pos = attr_add_be32(pos, end, WLANTEST_ATTR_STA_COUNTER,
341                             sta_counters[i].num);
342         pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
343         if (hwaddr_aton(argv[1], pos) < 0) {
344                 printf("Invalid BSSID '%s'\n", argv[1]);
345                 return -1;
346         }
347         pos += ETH_ALEN;
348
349         pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
350         if (hwaddr_aton(argv[2], pos) < 0) {
351                 printf("Invalid STA address '%s'\n", argv[2]);
352                 return -1;
353         }
354         pos += ETH_ALEN;
355
356         rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
357         if (rlen < 0)
358                 return -1;
359
360         pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
361         if (pos == NULL || len != 4)
362                 return -1;
363         printf("%u\n", WPA_GET_BE32(pos));
364         return 0;
365 }
366
367
368 struct bss_counters {
369         const char *name;
370         enum wlantest_bss_counter num;
371 };
372
373 static const struct bss_counters bss_counters[] = {
374         { "valid_bip_mmie", WLANTEST_BSS_COUNTER_VALID_BIP_MMIE },
375         { "invalid_bip_mmie", WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE },
376         { "missing_bip_mmie", WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE },
377         { NULL, 0 }
378 };
379
380 static int cmd_get_bss_counter(int s, int argc, char *argv[])
381 {
382         u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
383         u8 buf[100], *end, *pos;
384         int rlen, i;
385         size_t len;
386
387         if (argc != 2) {
388                 printf("get_bss_counter needs at three arguments: "
389                        "counter name and BSSID\n");
390                 return -1;
391         }
392
393         pos = buf;
394         end = buf + sizeof(buf);
395         WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_BSS_COUNTER);
396         pos += 4;
397
398         for (i = 0; bss_counters[i].name; i++) {
399                 if (os_strcasecmp(bss_counters[i].name, argv[0]) == 0)
400                         break;
401         }
402         if (bss_counters[i].name == NULL) {
403                 printf("Unknown BSS counter '%s'\n", argv[0]);
404                 printf("Counters:");
405                 for (i = 0; bss_counters[i].name; i++)
406                         printf(" %s", bss_counters[i].name);
407                 printf("\n");
408                 return -1;
409         }
410
411         pos = attr_add_be32(pos, end, WLANTEST_ATTR_BSS_COUNTER,
412                             bss_counters[i].num);
413         pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
414         if (hwaddr_aton(argv[1], pos) < 0) {
415                 printf("Invalid BSSID '%s'\n", argv[1]);
416                 return -1;
417         }
418         pos += ETH_ALEN;
419
420         rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
421         if (rlen < 0)
422                 return -1;
423
424         pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
425         if (pos == NULL || len != 4)
426                 return -1;
427         printf("%u\n", WPA_GET_BE32(pos));
428         return 0;
429 }
430
431
432 struct inject_frames {
433         const char *name;
434         enum wlantest_inject_frame frame;
435 };
436
437 static const struct inject_frames inject_frames[] = {
438         { "auth", WLANTEST_FRAME_AUTH },
439         { "assocreq", WLANTEST_FRAME_ASSOCREQ },
440         { "reassocreq", WLANTEST_FRAME_REASSOCREQ },
441         { "deauth", WLANTEST_FRAME_DEAUTH },
442         { "disassoc", WLANTEST_FRAME_DISASSOC },
443         { "saqueryreq", WLANTEST_FRAME_SAQUERYREQ },
444         { NULL, 0 }
445 };
446
447 static int cmd_inject(int s, int argc, char *argv[])
448 {
449         u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
450         u8 buf[100], *end, *pos;
451         int rlen, i;
452         enum wlantest_inject_protection prot;
453
454         /* <frame> <prot> <sender> <BSSID> <STA/ff:ff:ff:ff:ff:ff> */
455
456         if (argc < 5) {
457                 printf("inject needs five arguments: frame, protection, "
458                        "sender, BSSID, STA/ff:ff:ff:ff:ff:ff\n");
459                 return -1;
460         }
461
462         pos = buf;
463         end = buf + sizeof(buf);
464         WPA_PUT_BE32(pos, WLANTEST_CTRL_INJECT);
465         pos += 4;
466
467         for (i = 0; inject_frames[i].name; i++) {
468                 if (os_strcasecmp(inject_frames[i].name, argv[0]) == 0)
469                         break;
470         }
471         if (inject_frames[i].name == NULL) {
472                 printf("Unknown inject frame '%s'\n", argv[0]);
473                 printf("Frames:");
474                 for (i = 0; inject_frames[i].name; i++)
475                         printf(" %s", inject_frames[i].name);
476                 printf("\n");
477                 return -1;
478         }
479
480         pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_FRAME,
481                             inject_frames[i].frame);
482
483         if (os_strcasecmp(argv[1], "normal") == 0)
484                 prot = WLANTEST_INJECT_NORMAL;
485         else if (os_strcasecmp(argv[1], "protected") == 0)
486                 prot = WLANTEST_INJECT_PROTECTED;
487         else if (os_strcasecmp(argv[1], "unprotected") == 0)
488                 prot = WLANTEST_INJECT_UNPROTECTED;
489         else if (os_strcasecmp(argv[1], "incorrect") == 0)
490                 prot = WLANTEST_INJECT_INCORRECT_KEY;
491         else {
492                 printf("Unknown protection type '%s'\n", argv[1]);
493                 printf("Protection types: normal protected unprotected "
494                        "incorrect\n");
495                 return -1;
496         }
497         pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_PROTECTION, prot);
498
499         if (os_strcasecmp(argv[2], "ap") == 0) {
500                 pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_SENDER_AP,
501                                     1);
502         } else if (os_strcasecmp(argv[2], "sta") == 0) {
503                 pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_SENDER_AP,
504                                     0);
505         } else {
506                 printf("Unknown sender '%s'\n", argv[2]);
507                 printf("Sender types: ap sta\n");
508                 return -1;
509         }
510
511         pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
512         if (hwaddr_aton(argv[3], pos) < 0) {
513                 printf("Invalid BSSID '%s'\n", argv[3]);
514                 return -1;
515         }
516         pos += ETH_ALEN;
517
518         pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
519         if (hwaddr_aton(argv[4], pos) < 0) {
520                 printf("Invalid STA '%s'\n", argv[4]);
521                 return -1;
522         }
523         pos += ETH_ALEN;
524
525         rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
526         if (rlen < 0)
527                 return -1;
528         printf("OK\n");
529         return 0;
530 }
531
532
533 static int cmd_version(int s, int argc, char *argv[])
534 {
535         u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
536         u8 buf[4];
537         char *version;
538         size_t len;
539         int rlen, i;
540
541         WPA_PUT_BE32(buf, WLANTEST_CTRL_VERSION);
542         rlen = cmd_send_and_recv(s, buf, sizeof(buf), resp, sizeof(resp));
543         if (rlen < 0)
544                 return -1;
545
546         version = (char *) attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_VERSION,
547                                     &len);
548         if (version == NULL)
549                 return -1;
550
551         for (i = 0; i < len; i++)
552                 putchar(version[i]);
553         printf("\n");
554
555         return 0;
556 }
557
558
559 struct wlantest_cli_cmd {
560         const char *cmd;
561         int (*handler)(int s, int argc, char *argv[]);
562         const char *usage;
563 };
564
565 static const struct wlantest_cli_cmd wlantest_cli_commands[] = {
566         { "ping", cmd_ping, "= test connection to wlantest" },
567         { "terminate", cmd_terminate, "= terminate wlantest" },
568         { "list_bss", cmd_list_bss, "= get BSS list" },
569         { "list_sta", cmd_list_sta, "<BSSID> = get STA list" },
570         { "flush", cmd_flush, "= drop all collected BSS data" },
571         { "clear_sta_counters", cmd_clear_sta_counters,
572           "<BSSID> <STA> = clear STA counters" },
573         { "clear_bss_counters", cmd_clear_bss_counters,
574           "<BSSID> = clear BSS counters" },
575         { "get_sta_counter", cmd_get_sta_counter,
576           "<counter> <BSSID> <STA> = get STA counter value" },
577         { "get_bss_counter", cmd_get_bss_counter,
578           "<counter> <BSSID> = get BSS counter value" },
579         { "inject", cmd_inject,
580           "<frame> <prot> <sender> <BSSID> <STA/ff:ff:ff:ff:ff:ff>" },
581         { "version", cmd_version, "= get wlantest version" },
582         { NULL, NULL, NULL }
583 };
584
585
586 static int ctrl_command(int s, int argc, char *argv[])
587 {
588         const struct wlantest_cli_cmd *cmd, *match = NULL;
589         int count = 0;
590         int ret = 0;
591
592         for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) {
593                 if (os_strncasecmp(cmd->cmd, argv[0], os_strlen(argv[0])) == 0)
594                 {
595                         match = cmd;
596                         if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
597                                 /* exact match */
598                                 count = 1;
599                                 break;
600                         }
601                         count++;
602                 }
603         }
604
605         if (count > 1) {
606                 printf("Ambiguous command '%s'; possible commands:", argv[0]);
607                 for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) {
608                         if (os_strncasecmp(cmd->cmd, argv[0],
609                                            os_strlen(argv[0])) == 0) {
610                                 printf(" %s", cmd->cmd);
611                         }
612                 }
613                 printf("\n");
614                 ret = 1;
615         } else if (count == 0) {
616                 printf("Unknown command '%s'\n", argv[0]);
617                 ret = 1;
618         } else {
619                 ret = match->handler(s, argc - 1, &argv[1]);
620         }
621
622         return ret;
623 }
624
625
626 int main(int argc, char *argv[])
627 {
628         int s;
629         struct sockaddr_un addr;
630         int ret = 0;
631
632         s = socket(AF_UNIX, SOCK_SEQPACKET, 0);
633         if (s < 0) {
634                 perror("socket");
635                 return -1;
636         }
637
638         os_memset(&addr, 0, sizeof(addr));
639         addr.sun_family = AF_UNIX;
640         os_strlcpy(addr.sun_path + 1, WLANTEST_SOCK_NAME,
641                    sizeof(addr.sun_path) - 1);
642         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
643                 perror("connect");
644                 close(s);
645                 return -1;
646         }
647
648         if (argc > 1) {
649                 ret = ctrl_command(s, argc - 1, &argv[1]);
650                 if (ret < 0)
651                         printf("FAIL\n");
652         } else {
653                 /* TODO: interactive */
654         }
655
656         close(s);
657         return ret;
658 }