OSDN Git Service

attrib: Fix memory leak
[android-x86/external-bluetooth-bluez.git] / attrib / interactive.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2011  Nokia Corporation
6  *
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <string.h>
29 #include <stdlib.h>
30 #include <stdarg.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <signal.h>
35 #include <sys/signalfd.h>
36 #include <glib.h>
37
38 #include <readline/readline.h>
39 #include <readline/history.h>
40
41 #include "src/shared/util.h"
42 #include "lib/uuid.h"
43 #include "btio/btio.h"
44 #include "att.h"
45 #include "gattrib.h"
46 #include "gatt.h"
47 #include "gatttool.h"
48 #include "client/display.h"
49
50 static GIOChannel *iochannel = NULL;
51 static GAttrib *attrib = NULL;
52 static GMainLoop *event_loop;
53 static GString *prompt;
54
55 static char *opt_src = NULL;
56 static char *opt_dst = NULL;
57 static char *opt_dst_type = NULL;
58 static char *opt_sec_level = NULL;
59 static int opt_psm = 0;
60 static int opt_mtu = 0;
61 static int start;
62 static int end;
63
64 static void cmd_help(int argcp, char **argvp);
65
66 static enum state {
67         STATE_DISCONNECTED,
68         STATE_CONNECTING,
69         STATE_CONNECTED
70 } conn_state;
71
72 #define error(fmt, arg...) \
73         rl_printf(COLOR_RED "Error: " COLOR_OFF fmt, ## arg)
74
75 #define failed(fmt, arg...) \
76         rl_printf(COLOR_RED "Command Failed: " COLOR_OFF fmt, ## arg)
77
78 static char *get_prompt(void)
79 {
80         if (conn_state == STATE_CONNECTED)
81                 g_string_assign(prompt, COLOR_BLUE);
82         else
83                 g_string_assign(prompt, "");
84
85         if (opt_dst)
86                 g_string_append_printf(prompt, "[%17s]", opt_dst);
87         else
88                 g_string_append_printf(prompt, "[%17s]", "");
89
90         if (conn_state == STATE_CONNECTED)
91                 g_string_append(prompt, COLOR_OFF);
92
93         if (opt_psm)
94                 g_string_append(prompt, "[BR]");
95         else
96                 g_string_append(prompt, "[LE]");
97
98         g_string_append(prompt, "> ");
99
100         return prompt->str;
101 }
102
103
104 static void set_state(enum state st)
105 {
106         conn_state = st;
107         rl_set_prompt(get_prompt());
108 }
109
110 static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
111 {
112         uint8_t *opdu;
113         uint16_t handle, i, olen;
114         size_t plen;
115         GString *s;
116
117         handle = get_le16(&pdu[1]);
118
119         switch (pdu[0]) {
120         case ATT_OP_HANDLE_NOTIFY:
121                 s = g_string_new(NULL);
122                 g_string_printf(s, "Notification handle = 0x%04x value: ",
123                                                                         handle);
124                 break;
125         case ATT_OP_HANDLE_IND:
126                 s = g_string_new(NULL);
127                 g_string_printf(s, "Indication   handle = 0x%04x value: ",
128                                                                         handle);
129                 break;
130         default:
131                 error("Invalid opcode\n");
132                 return;
133         }
134
135         for (i = 3; i < len; i++)
136                 g_string_append_printf(s, "%02x ", pdu[i]);
137
138         rl_printf("%s\n", s->str);
139         g_string_free(s, TRUE);
140
141         if (pdu[0] == ATT_OP_HANDLE_NOTIFY)
142                 return;
143
144         opdu = g_attrib_get_buffer(attrib, &plen);
145         olen = enc_confirmation(opdu, plen);
146
147         if (olen > 0)
148                 g_attrib_send(attrib, 0, opdu, olen, NULL, NULL, NULL);
149 }
150
151 static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
152 {
153         if (err) {
154                 set_state(STATE_DISCONNECTED);
155                 error("%s\n", err->message);
156                 return;
157         }
158
159         attrib = g_attrib_new(iochannel);
160         g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, GATTRIB_ALL_HANDLES,
161                                                 events_handler, attrib, NULL);
162         g_attrib_register(attrib, ATT_OP_HANDLE_IND, GATTRIB_ALL_HANDLES,
163                                                 events_handler, attrib, NULL);
164         set_state(STATE_CONNECTED);
165         rl_printf("Connection successful\n");
166 }
167
168 static void disconnect_io()
169 {
170         if (conn_state == STATE_DISCONNECTED)
171                 return;
172
173         g_attrib_unref(attrib);
174         attrib = NULL;
175         opt_mtu = 0;
176
177         g_io_channel_shutdown(iochannel, FALSE, NULL);
178         g_io_channel_unref(iochannel);
179         iochannel = NULL;
180
181         set_state(STATE_DISCONNECTED);
182 }
183
184 static void primary_all_cb(uint8_t status, GSList *services, void *user_data)
185 {
186         GSList *l;
187
188         if (status) {
189                 error("Discover all primary services failed: %s\n",
190                                                 att_ecode2str(status));
191                 return;
192         }
193
194         if (services == NULL) {
195                 error("No primary service found\n");
196                 return;
197         }
198
199         for (l = services; l; l = l->next) {
200                 struct gatt_primary *prim = l->data;
201                 rl_printf("attr handle: 0x%04x, end grp handle: 0x%04x uuid: %s\n",
202                                 prim->range.start, prim->range.end, prim->uuid);
203         }
204 }
205
206 static void primary_by_uuid_cb(uint8_t status, GSList *ranges, void *user_data)
207 {
208         GSList *l;
209
210         if (status) {
211                 error("Discover primary services by UUID failed: %s\n",
212                                                         att_ecode2str(status));
213                 return;
214         }
215
216         if (ranges == NULL) {
217                 error("No service UUID found\n");
218                 return;
219         }
220
221         for (l = ranges; l; l = l->next) {
222                 struct att_range *range = l->data;
223                 rl_printf("Starting handle: 0x%04x Ending handle: 0x%04x\n",
224                                                 range->start, range->end);
225         }
226 }
227
228 static void included_cb(uint8_t status, GSList *includes, void *user_data)
229 {
230         GSList *l;
231
232         if (status) {
233                 error("Find included services failed: %s\n",
234                                                         att_ecode2str(status));
235                 return;
236         }
237
238         if (includes == NULL) {
239                 rl_printf("No included services found for this range\n");
240                 return;
241         }
242
243         for (l = includes; l; l = l->next) {
244                 struct gatt_included *incl = l->data;
245                 rl_printf("handle: 0x%04x, start handle: 0x%04x, "
246                                         "end handle: 0x%04x uuid: %s\n",
247                                         incl->handle, incl->range.start,
248                                         incl->range.end, incl->uuid);
249         }
250 }
251
252 static void char_cb(uint8_t status, GSList *characteristics, void *user_data)
253 {
254         GSList *l;
255
256         if (status) {
257                 error("Discover all characteristics failed: %s\n",
258                                                         att_ecode2str(status));
259                 return;
260         }
261
262         for (l = characteristics; l; l = l->next) {
263                 struct gatt_char *chars = l->data;
264
265                 rl_printf("handle: 0x%04x, char properties: 0x%02x, char value "
266                                 "handle: 0x%04x, uuid: %s\n", chars->handle,
267                                 chars->properties, chars->value_handle,
268                                 chars->uuid);
269         }
270 }
271
272 static void char_desc_cb(uint8_t status, GSList *descriptors, void *user_data)
273 {
274         GSList *l;
275
276         if (status) {
277                 error("Discover descriptors failed: %s\n",
278                                                         att_ecode2str(status));
279                 return;
280         }
281
282         for (l = descriptors; l; l = l->next) {
283                 struct gatt_desc *desc = l->data;
284
285                 rl_printf("handle: 0x%04x, uuid: %s\n", desc->handle,
286                                                                 desc->uuid);
287         }
288 }
289
290 static void char_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
291                                                         gpointer user_data)
292 {
293         uint8_t value[plen];
294         ssize_t vlen;
295         int i;
296         GString *s;
297
298         if (status != 0) {
299                 error("Characteristic value/descriptor read failed: %s\n",
300                                                         att_ecode2str(status));
301                 return;
302         }
303
304         vlen = dec_read_resp(pdu, plen, value, sizeof(value));
305         if (vlen < 0) {
306                 error("Protocol error\n");
307                 return;
308         }
309
310         s = g_string_new("Characteristic value/descriptor: ");
311         for (i = 0; i < vlen; i++)
312                 g_string_append_printf(s, "%02x ", value[i]);
313
314         rl_printf("%s\n", s->str);
315         g_string_free(s, TRUE);
316 }
317
318 static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu,
319                                         guint16 plen, gpointer user_data)
320 {
321         struct att_data_list *list;
322         int i;
323         GString *s;
324
325         if (status != 0) {
326                 error("Read characteristics by UUID failed: %s\n",
327                                                         att_ecode2str(status));
328                 return;
329         }
330
331         list = dec_read_by_type_resp(pdu, plen);
332         if (list == NULL)
333                 return;
334
335         s = g_string_new(NULL);
336         for (i = 0; i < list->num; i++) {
337                 uint8_t *value = list->data[i];
338                 int j;
339
340                 g_string_printf(s, "handle: 0x%04x \t value: ",
341                                                         get_le16(value));
342                 value += 2;
343                 for (j = 0; j < list->len - 2; j++, value++)
344                         g_string_append_printf(s, "%02x ", *value);
345
346                 rl_printf("%s\n", s->str);
347         }
348
349         att_data_list_free(list);
350         g_string_free(s, TRUE);
351 }
352
353 static void cmd_exit(int argcp, char **argvp)
354 {
355         rl_callback_handler_remove();
356         g_main_loop_quit(event_loop);
357 }
358
359 static gboolean channel_watcher(GIOChannel *chan, GIOCondition cond,
360                                 gpointer user_data)
361 {
362         disconnect_io();
363
364         return FALSE;
365 }
366
367 static void cmd_connect(int argcp, char **argvp)
368 {
369         GError *gerr = NULL;
370
371         if (conn_state != STATE_DISCONNECTED)
372                 return;
373
374         if (argcp > 1) {
375                 g_free(opt_dst);
376                 opt_dst = g_strdup(argvp[1]);
377
378                 g_free(opt_dst_type);
379                 if (argcp > 2)
380                         opt_dst_type = g_strdup(argvp[2]);
381                 else
382                         opt_dst_type = g_strdup("public");
383         }
384
385         if (opt_dst == NULL) {
386                 error("Remote Bluetooth address required\n");
387                 return;
388         }
389
390         rl_printf("Attempting to connect to %s\n", opt_dst);
391         set_state(STATE_CONNECTING);
392         iochannel = gatt_connect(opt_src, opt_dst, opt_dst_type, opt_sec_level,
393                                         opt_psm, opt_mtu, connect_cb, &gerr);
394         if (iochannel == NULL) {
395                 set_state(STATE_DISCONNECTED);
396                 error("%s\n", gerr->message);
397                 g_error_free(gerr);
398         } else
399                 g_io_add_watch(iochannel, G_IO_HUP, channel_watcher, NULL);
400 }
401
402 static void cmd_disconnect(int argcp, char **argvp)
403 {
404         disconnect_io();
405 }
406
407 static void cmd_primary(int argcp, char **argvp)
408 {
409         bt_uuid_t uuid;
410
411         if (conn_state != STATE_CONNECTED) {
412                 failed("Disconnected\n");
413                 return;
414         }
415
416         if (argcp == 1) {
417                 gatt_discover_primary(attrib, NULL, primary_all_cb, NULL);
418                 return;
419         }
420
421         if (bt_string_to_uuid(&uuid, argvp[1]) < 0) {
422                 error("Invalid UUID\n");
423                 return;
424         }
425
426         gatt_discover_primary(attrib, &uuid, primary_by_uuid_cb, NULL);
427 }
428
429 static int strtohandle(const char *src)
430 {
431         char *e;
432         int dst;
433
434         errno = 0;
435         dst = strtoll(src, &e, 16);
436         if (errno != 0 || *e != '\0')
437                 return -EINVAL;
438
439         return dst;
440 }
441
442 static void cmd_included(int argcp, char **argvp)
443 {
444         int start = 0x0001;
445         int end = 0xffff;
446
447         if (conn_state != STATE_CONNECTED) {
448                 failed("Disconnected\n");
449                 return;
450         }
451
452         if (argcp > 1) {
453                 start = strtohandle(argvp[1]);
454                 if (start < 0) {
455                         error("Invalid start handle: %s\n", argvp[1]);
456                         return;
457                 }
458                 end = start;
459         }
460
461         if (argcp > 2) {
462                 end = strtohandle(argvp[2]);
463                 if (end < 0) {
464                         error("Invalid end handle: %s\n", argvp[2]);
465                         return;
466                 }
467         }
468
469         gatt_find_included(attrib, start, end, included_cb, NULL);
470 }
471
472 static void cmd_char(int argcp, char **argvp)
473 {
474         int start = 0x0001;
475         int end = 0xffff;
476
477         if (conn_state != STATE_CONNECTED) {
478                 failed("Disconnected\n");
479                 return;
480         }
481
482         if (argcp > 1) {
483                 start = strtohandle(argvp[1]);
484                 if (start < 0) {
485                         error("Invalid start handle: %s\n", argvp[1]);
486                         return;
487                 }
488         }
489
490         if (argcp > 2) {
491                 end = strtohandle(argvp[2]);
492                 if (end < 0) {
493                         error("Invalid end handle: %s\n", argvp[2]);
494                         return;
495                 }
496         }
497
498         if (argcp > 3) {
499                 bt_uuid_t uuid;
500
501                 if (bt_string_to_uuid(&uuid, argvp[3]) < 0) {
502                         error("Invalid UUID\n");
503                         return;
504                 }
505
506                 gatt_discover_char(attrib, start, end, &uuid, char_cb, NULL);
507                 return;
508         }
509
510         gatt_discover_char(attrib, start, end, NULL, char_cb, NULL);
511 }
512
513 static void cmd_char_desc(int argcp, char **argvp)
514 {
515         if (conn_state != STATE_CONNECTED) {
516                 failed("Disconnected\n");
517                 return;
518         }
519
520         if (argcp > 1) {
521                 start = strtohandle(argvp[1]);
522                 if (start < 0) {
523                         error("Invalid start handle: %s\n", argvp[1]);
524                         return;
525                 }
526         } else
527                 start = 0x0001;
528
529         if (argcp > 2) {
530                 end = strtohandle(argvp[2]);
531                 if (end < 0) {
532                         error("Invalid end handle: %s\n", argvp[2]);
533                         return;
534                 }
535         } else
536                 end = 0xffff;
537
538         gatt_discover_desc(attrib, start, end, NULL, char_desc_cb, NULL);
539 }
540
541 static void cmd_read_hnd(int argcp, char **argvp)
542 {
543         int handle;
544
545         if (conn_state != STATE_CONNECTED) {
546                 failed("Disconnected\n");
547                 return;
548         }
549
550         if (argcp < 2) {
551                 error("Missing argument: handle\n");
552                 return;
553         }
554
555         handle = strtohandle(argvp[1]);
556         if (handle < 0) {
557                 error("Invalid handle: %s\n", argvp[1]);
558                 return;
559         }
560
561         gatt_read_char(attrib, handle, char_read_cb, attrib);
562 }
563
564 static void cmd_read_uuid(int argcp, char **argvp)
565 {
566         int start = 0x0001;
567         int end = 0xffff;
568         bt_uuid_t uuid;
569
570         if (conn_state != STATE_CONNECTED) {
571                 failed("Disconnected\n");
572                 return;
573         }
574
575         if (argcp < 2) {
576                 error("Missing argument: UUID\n");
577                 return;
578         }
579
580         if (bt_string_to_uuid(&uuid, argvp[1]) < 0) {
581                 error("Invalid UUID\n");
582                 return;
583         }
584
585         if (argcp > 2) {
586                 start = strtohandle(argvp[2]);
587                 if (start < 0) {
588                         error("Invalid start handle: %s\n", argvp[1]);
589                         return;
590                 }
591         }
592
593         if (argcp > 3) {
594                 end = strtohandle(argvp[3]);
595                 if (end < 0) {
596                         error("Invalid end handle: %s\n", argvp[2]);
597                         return;
598                 }
599         }
600
601         gatt_read_char_by_uuid(attrib, start, end, &uuid, char_read_by_uuid_cb,
602                                                                         NULL);
603 }
604
605 static void char_write_req_cb(guint8 status, const guint8 *pdu, guint16 plen,
606                                                         gpointer user_data)
607 {
608         if (status != 0) {
609                 error("Characteristic Write Request failed: "
610                                                 "%s\n", att_ecode2str(status));
611                 return;
612         }
613
614         if (!dec_write_resp(pdu, plen) && !dec_exec_write_resp(pdu, plen)) {
615                 error("Protocol error\n");
616                 return;
617         }
618
619         rl_printf("Characteristic value was written successfully\n");
620 }
621
622 static void cmd_char_write(int argcp, char **argvp)
623 {
624         uint8_t *value;
625         size_t plen;
626         int handle;
627
628         if (conn_state != STATE_CONNECTED) {
629                 failed("Disconnected\n");
630                 return;
631         }
632
633         if (argcp < 3) {
634                 rl_printf("Usage: %s <handle> <new value>\n", argvp[0]);
635                 return;
636         }
637
638         handle = strtohandle(argvp[1]);
639         if (handle <= 0) {
640                 error("A valid handle is required\n");
641                 return;
642         }
643
644         plen = gatt_attr_data_from_string(argvp[2], &value);
645         if (plen == 0) {
646                 error("Invalid value\n");
647                 return;
648         }
649
650         if (g_strcmp0("char-write-req", argvp[0]) == 0)
651                 gatt_write_char(attrib, handle, value, plen,
652                                         char_write_req_cb, NULL);
653         else
654                 gatt_write_cmd(attrib, handle, value, plen, NULL, NULL);
655
656         g_free(value);
657 }
658
659 static void cmd_sec_level(int argcp, char **argvp)
660 {
661         GError *gerr = NULL;
662         BtIOSecLevel sec_level;
663
664         if (argcp < 2) {
665                 rl_printf("sec-level: %s\n", opt_sec_level);
666                 return;
667         }
668
669         if (strcasecmp(argvp[1], "medium") == 0)
670                 sec_level = BT_IO_SEC_MEDIUM;
671         else if (strcasecmp(argvp[1], "high") == 0)
672                 sec_level = BT_IO_SEC_HIGH;
673         else if (strcasecmp(argvp[1], "low") == 0)
674                 sec_level = BT_IO_SEC_LOW;
675         else {
676                 rl_printf("Allowed values: low | medium | high\n");
677                 return;
678         }
679
680         g_free(opt_sec_level);
681         opt_sec_level = g_strdup(argvp[1]);
682
683         if (conn_state != STATE_CONNECTED)
684                 return;
685
686         if (opt_psm) {
687                 rl_printf("Change will take effect on reconnection\n");
688                 return;
689         }
690
691         bt_io_set(iochannel, &gerr,
692                         BT_IO_OPT_SEC_LEVEL, sec_level,
693                         BT_IO_OPT_INVALID);
694         if (gerr) {
695                 error("%s\n", gerr->message);
696                 g_error_free(gerr);
697         }
698 }
699
700 static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen,
701                                                         gpointer user_data)
702 {
703         uint16_t mtu;
704
705         if (status != 0) {
706                 error("Exchange MTU Request failed: %s\n",
707                                                 att_ecode2str(status));
708                 return;
709         }
710
711         if (!dec_mtu_resp(pdu, plen, &mtu)) {
712                 error("Protocol error\n");
713                 return;
714         }
715
716         mtu = MIN(mtu, opt_mtu);
717         /* Set new value for MTU in client */
718         if (g_attrib_set_mtu(attrib, mtu))
719                 rl_printf("MTU was exchanged successfully: %d\n", mtu);
720         else
721                 error("Error exchanging MTU\n");
722 }
723
724 static void cmd_mtu(int argcp, char **argvp)
725 {
726         if (conn_state != STATE_CONNECTED) {
727                 failed("Disconnected\n");
728                 return;
729         }
730
731         if (opt_psm) {
732                 failed("Operation is only available for LE transport.\n");
733                 return;
734         }
735
736         if (argcp < 2) {
737                 rl_printf("Usage: mtu <value>\n");
738                 return;
739         }
740
741         if (opt_mtu) {
742                 failed("MTU exchange can only occur once per connection.\n");
743                 return;
744         }
745
746         errno = 0;
747         opt_mtu = strtoll(argvp[1], NULL, 0);
748         if (errno != 0 || opt_mtu < ATT_DEFAULT_LE_MTU) {
749                 error("Invalid value. Minimum MTU size is %d\n",
750                                                         ATT_DEFAULT_LE_MTU);
751                 return;
752         }
753
754         gatt_exchange_mtu(attrib, opt_mtu, exchange_mtu_cb, NULL);
755 }
756
757 static struct {
758         const char *cmd;
759         void (*func)(int argcp, char **argvp);
760         const char *params;
761         const char *desc;
762 } commands[] = {
763         { "help",               cmd_help,       "",
764                 "Show this help"},
765         { "exit",               cmd_exit,       "",
766                 "Exit interactive mode" },
767         { "quit",               cmd_exit,       "",
768                 "Exit interactive mode" },
769         { "connect",            cmd_connect,    "[address [address type]]",
770                 "Connect to a remote device" },
771         { "disconnect",         cmd_disconnect, "",
772                 "Disconnect from a remote device" },
773         { "primary",            cmd_primary,    "[UUID]",
774                 "Primary Service Discovery" },
775         { "included",           cmd_included,   "[start hnd [end hnd]]",
776                 "Find Included Services" },
777         { "characteristics",    cmd_char,       "[start hnd [end hnd [UUID]]]",
778                 "Characteristics Discovery" },
779         { "char-desc",          cmd_char_desc,  "[start hnd] [end hnd]",
780                 "Characteristics Descriptor Discovery" },
781         { "char-read-hnd",      cmd_read_hnd,   "<handle>",
782                 "Characteristics Value/Descriptor Read by handle" },
783         { "char-read-uuid",     cmd_read_uuid,  "<UUID> [start hnd] [end hnd]",
784                 "Characteristics Value/Descriptor Read by UUID" },
785         { "char-write-req",     cmd_char_write, "<handle> <new value>",
786                 "Characteristic Value Write (Write Request)" },
787         { "char-write-cmd",     cmd_char_write, "<handle> <new value>",
788                 "Characteristic Value Write (No response)" },
789         { "sec-level",          cmd_sec_level,  "[low | medium | high]",
790                 "Set security level. Default: low" },
791         { "mtu",                cmd_mtu,        "<value>",
792                 "Exchange MTU for GATT/ATT" },
793         { NULL, NULL, NULL}
794 };
795
796 static void cmd_help(int argcp, char **argvp)
797 {
798         int i;
799
800         for (i = 0; commands[i].cmd; i++)
801                 rl_printf("%-15s %-30s %s\n", commands[i].cmd,
802                                 commands[i].params, commands[i].desc);
803 }
804
805 static void parse_line(char *line_read)
806 {
807         char **argvp;
808         int argcp;
809         int i;
810
811         if (line_read == NULL) {
812                 rl_printf("\n");
813                 cmd_exit(0, NULL);
814                 return;
815         }
816
817         line_read = g_strstrip(line_read);
818
819         if (*line_read == '\0')
820                 goto done;
821
822         add_history(line_read);
823
824         if (g_shell_parse_argv(line_read, &argcp, &argvp, NULL) == FALSE)
825                 goto done;
826
827         for (i = 0; commands[i].cmd; i++)
828                 if (strcasecmp(commands[i].cmd, argvp[0]) == 0)
829                         break;
830
831         if (commands[i].cmd)
832                 commands[i].func(argcp, argvp);
833         else
834                 error("%s: command not found\n", argvp[0]);
835
836         g_strfreev(argvp);
837
838 done:
839         free(line_read);
840 }
841
842 static gboolean prompt_read(GIOChannel *chan, GIOCondition cond,
843                                                         gpointer user_data)
844 {
845         if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
846                 g_io_channel_unref(chan);
847                 return FALSE;
848         }
849
850         rl_callback_read_char();
851
852         return TRUE;
853 }
854
855 static char *completion_generator(const char *text, int state)
856 {
857         static int index = 0, len = 0;
858         const char *cmd = NULL;
859
860         if (state == 0) {
861                 index = 0;
862                 len = strlen(text);
863         }
864
865         while ((cmd = commands[index].cmd) != NULL) {
866                 index++;
867                 if (strncmp(cmd, text, len) == 0)
868                         return strdup(cmd);
869         }
870
871         return NULL;
872 }
873
874 static char **commands_completion(const char *text, int start, int end)
875 {
876         if (start == 0)
877                 return rl_completion_matches(text, &completion_generator);
878         else
879                 return NULL;
880 }
881
882 static guint setup_standard_input(void)
883 {
884         GIOChannel *channel;
885         guint source;
886
887         channel = g_io_channel_unix_new(fileno(stdin));
888
889         source = g_io_add_watch(channel,
890                                 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
891                                 prompt_read, NULL);
892
893         g_io_channel_unref(channel);
894
895         return source;
896 }
897
898 static gboolean signal_handler(GIOChannel *channel, GIOCondition condition,
899                                                         gpointer user_data)
900 {
901         static unsigned int __terminated = 0;
902         struct signalfd_siginfo si;
903         ssize_t result;
904         int fd;
905
906         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
907                 g_main_loop_quit(event_loop);
908                 return FALSE;
909         }
910
911         fd = g_io_channel_unix_get_fd(channel);
912
913         result = read(fd, &si, sizeof(si));
914         if (result != sizeof(si))
915                 return FALSE;
916
917         switch (si.ssi_signo) {
918         case SIGINT:
919                 rl_replace_line("", 0);
920                 rl_crlf();
921                 rl_on_new_line();
922                 rl_redisplay();
923                 break;
924         case SIGTERM:
925                 if (__terminated == 0) {
926                         rl_replace_line("", 0);
927                         rl_crlf();
928                         g_main_loop_quit(event_loop);
929                 }
930
931                 __terminated = 1;
932                 break;
933         }
934
935         return TRUE;
936 }
937
938 static guint setup_signalfd(void)
939 {
940         GIOChannel *channel;
941         guint source;
942         sigset_t mask;
943         int fd;
944
945         sigemptyset(&mask);
946         sigaddset(&mask, SIGINT);
947         sigaddset(&mask, SIGTERM);
948
949         if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
950                 perror("Failed to set signal mask");
951                 return 0;
952         }
953
954         fd = signalfd(-1, &mask, 0);
955         if (fd < 0) {
956                 perror("Failed to create signal descriptor");
957                 return 0;
958         }
959
960         channel = g_io_channel_unix_new(fd);
961
962         g_io_channel_set_close_on_unref(channel, TRUE);
963         g_io_channel_set_encoding(channel, NULL, NULL);
964         g_io_channel_set_buffered(channel, FALSE);
965
966         source = g_io_add_watch(channel,
967                                 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
968                                 signal_handler, NULL);
969
970         g_io_channel_unref(channel);
971
972         return source;
973 }
974
975 int interactive(const char *src, const char *dst,
976                 const char *dst_type, int psm)
977 {
978         guint input;
979         guint signal;
980
981         opt_sec_level = g_strdup("low");
982
983         opt_src = g_strdup(src);
984         opt_dst = g_strdup(dst);
985         opt_dst_type = g_strdup(dst_type);
986         opt_psm = psm;
987
988         prompt = g_string_new(NULL);
989
990         event_loop = g_main_loop_new(NULL, FALSE);
991
992         input = setup_standard_input();
993         signal = setup_signalfd();
994
995         rl_attempted_completion_function = commands_completion;
996         rl_erase_empty_line = 1;
997         rl_callback_handler_install(get_prompt(), parse_line);
998
999         g_main_loop_run(event_loop);
1000
1001         rl_callback_handler_remove();
1002         cmd_disconnect(0, NULL);
1003         g_source_remove(input);
1004         g_source_remove(signal);
1005         g_main_loop_unref(event_loop);
1006         g_string_free(prompt, TRUE);
1007
1008         g_free(opt_src);
1009         g_free(opt_dst);
1010         g_free(opt_sec_level);
1011
1012         return 0;
1013 }