• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <string.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <glib.h>
28 
29 #include <bluetooth/uuid.h>
30 
31 #include <readline/readline.h>
32 #include <readline/history.h>
33 
34 #include "att.h"
35 #include "btio.h"
36 #include "gattrib.h"
37 #include "gatt.h"
38 #include "gatttool.h"
39 
40 static GIOChannel *iochannel = NULL;
41 static GAttrib *attrib = NULL;
42 static GMainLoop *event_loop;
43 static GString *prompt;
44 
45 static gchar *opt_src = NULL;
46 static gchar *opt_dst = NULL;
47 static gchar *opt_sec_level = NULL;
48 static int opt_psm = 0;
49 static int opt_mtu = 0;
50 
51 struct characteristic_data {
52 	uint16_t orig_start;
53 	uint16_t start;
54 	uint16_t end;
55 	bt_uuid_t uuid;
56 };
57 
58 static void cmd_help(int argcp, char **argvp);
59 
60 enum state {
61 	STATE_DISCONNECTED,
62 	STATE_CONNECTING,
63 	STATE_CONNECTED
64 } conn_state;
65 
get_prompt(void)66 static char *get_prompt(void)
67 {
68 	if (conn_state == STATE_CONNECTING) {
69 		g_string_assign(prompt, "Connecting... ");
70 		return prompt->str;
71 	}
72 
73 	if (conn_state == STATE_CONNECTED)
74 		g_string_assign(prompt, "[CON]");
75 	else
76 		g_string_assign(prompt, "[   ]");
77 
78 	if (opt_dst)
79 		g_string_append_printf(prompt, "[%17s]", opt_dst);
80 	else
81 		g_string_append_printf(prompt, "[%17s]", "");
82 
83 	if (opt_psm)
84 		g_string_append(prompt, "[BR]");
85 	else
86 		g_string_append(prompt, "[LE]");
87 
88 	g_string_append(prompt, "> ");
89 
90 	return prompt->str;
91 }
92 
93 
set_state(enum state st)94 static void set_state(enum state st)
95 {
96 	conn_state = st;
97 	rl_set_prompt(get_prompt());
98 	rl_redisplay();
99 }
100 
events_handler(const uint8_t * pdu,uint16_t len,gpointer user_data)101 static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
102 {
103 	uint8_t opdu[ATT_MAX_MTU];
104 	uint16_t handle, i, olen;
105 
106 	handle = att_get_u16(&pdu[1]);
107 
108 	printf("\n");
109 	switch (pdu[0]) {
110 	case ATT_OP_HANDLE_NOTIFY:
111 		printf("Notification handle = 0x%04x value: ", handle);
112 		break;
113 	case ATT_OP_HANDLE_IND:
114 		printf("Indication   handle = 0x%04x value: ", handle);
115 		break;
116 	default:
117 		printf("Invalid opcode\n");
118 		return;
119 	}
120 
121 	for (i = 3; i < len; i++)
122 		printf("%02x ", pdu[i]);
123 
124 	printf("\n");
125 	rl_forced_update_display();
126 
127 	if (pdu[0] == ATT_OP_HANDLE_NOTIFY)
128 		return;
129 
130 	olen = enc_confirmation(opdu, sizeof(opdu));
131 
132 	if (olen > 0)
133 		g_attrib_send(attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL);
134 }
135 
connect_cb(GIOChannel * io,GError * err,gpointer user_data)136 static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
137 {
138 	if (err) {
139 		printf("connect error: %s\n", err->message);
140 		set_state(STATE_DISCONNECTED);
141 		return;
142 	}
143 
144 	attrib = g_attrib_new(iochannel);
145 	g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, events_handler,
146 							attrib, NULL);
147 	g_attrib_register(attrib, ATT_OP_HANDLE_IND, events_handler,
148 							attrib, NULL);
149 	set_state(STATE_CONNECTED);
150 }
151 
primary_all_cb(GSList * services,guint8 status,gpointer user_data)152 static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
153 {
154 	GSList *l;
155 
156 	if (status) {
157 		printf("Discover all primary services failed: %s\n",
158 							att_ecode2str(status));
159 		return;
160 	}
161 
162 	printf("\n");
163 	for (l = services; l; l = l->next) {
164 		struct att_primary *prim = l->data;
165 		printf("attr handle: 0x%04x, end grp handle: 0x%04x "
166 			"uuid: %s\n", prim->start, prim->end, prim->uuid);
167 	}
168 
169 	rl_forced_update_display();
170 }
171 
primary_by_uuid_cb(GSList * ranges,guint8 status,gpointer user_data)172 static void primary_by_uuid_cb(GSList *ranges, guint8 status,
173 							gpointer user_data)
174 {
175 	GSList *l;
176 
177 	if (status) {
178 		printf("Discover primary services by UUID failed: %s\n",
179 							att_ecode2str(status));
180 		return;
181 	}
182 
183 	printf("\n");
184 	for (l = ranges; l; l = l->next) {
185 		struct att_range *range = l->data;
186 		g_print("Starting handle: 0x%04x Ending handle: 0x%04x\n",
187 						range->start, range->end);
188 	}
189 
190 	rl_forced_update_display();
191 }
192 
char_cb(GSList * characteristics,guint8 status,gpointer user_data)193 static void char_cb(GSList *characteristics, guint8 status, gpointer user_data)
194 {
195 	GSList *l;
196 
197 	if (status) {
198 		printf("Discover all characteristics failed: %s\n",
199 							att_ecode2str(status));
200 		return;
201 	}
202 
203 	printf("\n");
204 	for (l = characteristics; l; l = l->next) {
205 		struct att_char *chars = l->data;
206 
207 		printf("handle: 0x%04x, char properties: 0x%02x, char value "
208 				"handle: 0x%04x, uuid: %s\n", chars->handle,
209 				chars->properties, chars->value_handle,
210 				chars->uuid);
211 	}
212 
213 	rl_forced_update_display();
214 }
215 
char_desc_cb(guint8 status,const guint8 * pdu,guint16 plen,gpointer user_data)216 static void char_desc_cb(guint8 status, const guint8 *pdu, guint16 plen,
217 							gpointer user_data)
218 {
219 	struct att_data_list *list;
220 	guint8 format;
221 	int i;
222 
223 	if (status != 0) {
224 		printf("Discover all characteristic descriptors failed: "
225 						"%s\n", att_ecode2str(status));
226 		return;
227 	}
228 
229 	list = dec_find_info_resp(pdu, plen, &format);
230 	if (list == NULL)
231 		return;
232 
233 	printf("\n");
234 	for (i = 0; i < list->num; i++) {
235 		char uuidstr[MAX_LEN_UUID_STR];
236 		uint16_t handle;
237 		uint8_t *value;
238 		bt_uuid_t uuid;
239 
240 		value = list->data[i];
241 		handle = att_get_u16(value);
242 
243 		if (format == 0x01)
244 			uuid = att_get_uuid16(&value[2]);
245 		else
246 			uuid = att_get_uuid128(&value[2]);
247 
248 		bt_uuid_to_string(&uuid, uuidstr, MAX_LEN_UUID_STR);
249 		printf("handle: 0x%04x, uuid: %s\n", handle, uuidstr);
250 	}
251 
252 	att_data_list_free(list);
253 
254 	rl_forced_update_display();
255 }
256 
char_read_cb(guint8 status,const guint8 * pdu,guint16 plen,gpointer user_data)257 static void char_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
258 							gpointer user_data)
259 {
260 	uint8_t value[ATT_MAX_MTU];
261 	int i, vlen;
262 
263 	if (status != 0) {
264 		printf("Characteristic value/descriptor read failed: %s\n",
265 							att_ecode2str(status));
266 		return;
267 	}
268 
269 	if (!dec_read_resp(pdu, plen, value, &vlen)) {
270 		printf("Protocol error\n");
271 		return;
272 	}
273 
274 	printf("\nCharacteristic value/descriptor: ");
275 	for (i = 0; i < vlen; i++)
276 		printf("%02x ", value[i]);
277 	printf("\n");
278 
279 	rl_forced_update_display();
280 }
281 
char_read_by_uuid_cb(guint8 status,const guint8 * pdu,guint16 plen,gpointer user_data)282 static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu,
283 					guint16 plen, gpointer user_data)
284 {
285 	struct characteristic_data *char_data = user_data;
286 	struct att_data_list *list;
287 	int i;
288 
289 	if (status == ATT_ECODE_ATTR_NOT_FOUND &&
290 				char_data->start != char_data->orig_start)
291 		goto done;
292 
293 	if (status != 0) {
294 		printf("Read characteristics by UUID failed: %s\n",
295 							att_ecode2str(status));
296 		goto done;
297 	}
298 
299 	list = dec_read_by_type_resp(pdu, plen);
300 	if (list == NULL)
301 		goto done;
302 
303 	for (i = 0; i < list->num; i++) {
304 		uint8_t *value = list->data[i];
305 		int j;
306 
307 		char_data->start = att_get_u16(value) + 1;
308 
309 		printf("\nhandle: 0x%04x \t value: ", att_get_u16(value));
310 		value += 2;
311 		for (j = 0; j < list->len - 2; j++, value++)
312 			printf("%02x ", *value);
313 		printf("\n");
314 	}
315 
316 	att_data_list_free(list);
317 
318 	rl_forced_update_display();
319 
320 done:
321 	g_free(char_data);
322 }
323 
cmd_exit(int argcp,char ** argvp)324 static void cmd_exit(int argcp, char **argvp)
325 {
326 	rl_callback_handler_remove();
327 	g_main_loop_quit(event_loop);
328 }
329 
cmd_connect(int argcp,char ** argvp)330 static void cmd_connect(int argcp, char **argvp)
331 {
332 	if (conn_state != STATE_DISCONNECTED)
333 		return;
334 
335 	if (argcp > 1) {
336 		g_free(opt_dst);
337 		opt_dst = g_strdup(argvp[1]);
338 	}
339 
340 	if (opt_dst == NULL) {
341 		printf("Remote Bluetooth address required\n");
342 		return;
343 	}
344 
345 	set_state(STATE_CONNECTING);
346 	iochannel = gatt_connect(opt_src, opt_dst, opt_sec_level, opt_psm,
347 						opt_mtu, connect_cb);
348 	if (iochannel == NULL)
349 		set_state(STATE_DISCONNECTED);
350 }
351 
cmd_disconnect(int argcp,char ** argvp)352 static void cmd_disconnect(int argcp, char **argvp)
353 {
354 	if (conn_state == STATE_DISCONNECTED)
355 		return;
356 
357 	g_attrib_unref(attrib);
358 	attrib = NULL;
359 	opt_mtu = 0;
360 
361 	g_io_channel_shutdown(iochannel, FALSE, NULL);
362 	g_io_channel_unref(iochannel);
363 	iochannel = NULL;
364 
365 	set_state(STATE_DISCONNECTED);
366 }
367 
cmd_primary(int argcp,char ** argvp)368 static void cmd_primary(int argcp, char **argvp)
369 {
370 	bt_uuid_t uuid;
371 
372 	if (conn_state != STATE_CONNECTED) {
373 		printf("Command failed: disconnected\n");
374 		return;
375 	}
376 
377 	if (argcp == 1) {
378 		gatt_discover_primary(attrib, NULL, primary_all_cb, NULL);
379 		return;
380 	}
381 
382 	if (bt_string_to_uuid(&uuid, argvp[1]) < 0) {
383 		printf("Invalid UUID\n");
384 		return;
385 	}
386 
387 	gatt_discover_primary(attrib, &uuid, primary_by_uuid_cb, NULL);
388 }
389 
strtohandle(const char * src)390 static int strtohandle(const char *src)
391 {
392 	char *e;
393 	int dst;
394 
395 	errno = 0;
396 	dst = strtoll(src, &e, 16);
397 	if (errno != 0 || *e != '\0')
398 		return -EINVAL;
399 
400 	return dst;
401 }
402 
cmd_char(int argcp,char ** argvp)403 static void cmd_char(int argcp, char **argvp)
404 {
405 	int start = 0x0001;
406 	int end = 0xffff;
407 
408 	if (conn_state != STATE_CONNECTED) {
409 		printf("Command failed: disconnected\n");
410 		return;
411 	}
412 
413 	if (argcp > 1) {
414 		start = strtohandle(argvp[1]);
415 		if (start < 0) {
416 			printf("Invalid start handle: %s\n", argvp[1]);
417 			return;
418 		}
419 	}
420 
421 	if (argcp > 2) {
422 		end = strtohandle(argvp[2]);
423 		if (end < 0) {
424 			printf("Invalid end handle: %s\n", argvp[2]);
425 			return;
426 		}
427 	}
428 
429 	if (argcp > 3) {
430 		bt_uuid_t uuid;
431 
432 		if (bt_string_to_uuid(&uuid, argvp[3]) < 0) {
433 			printf("Invalid UUID\n");
434 			return;
435 		}
436 
437 		gatt_discover_char(attrib, start, end, &uuid, char_cb, NULL);
438 		return;
439 	}
440 
441 	gatt_discover_char(attrib, start, end, NULL, char_cb, NULL);
442 }
443 
cmd_char_desc(int argcp,char ** argvp)444 static void cmd_char_desc(int argcp, char **argvp)
445 {
446 	int start = 0x0001;
447 	int end = 0xffff;
448 
449 	if (conn_state != STATE_CONNECTED) {
450 		printf("Command failed: disconnected\n");
451 		return;
452 	}
453 
454 	if (argcp > 1) {
455 		start = strtohandle(argvp[1]);
456 		if (start < 0) {
457 			printf("Invalid start handle: %s\n", argvp[1]);
458 			return;
459 		}
460 	}
461 
462 	if (argcp > 2) {
463 		end = strtohandle(argvp[2]);
464 		if (end < 0) {
465 			printf("Invalid end handle: %s\n", argvp[2]);
466 			return;
467 		}
468 	}
469 
470 	gatt_find_info(attrib, start, end, char_desc_cb, NULL);
471 }
472 
cmd_read_hnd(int argcp,char ** argvp)473 static void cmd_read_hnd(int argcp, char **argvp)
474 {
475 	int handle;
476 	int offset = 0;
477 
478 	if (conn_state != STATE_CONNECTED) {
479 		printf("Command failed: disconnected\n");
480 		return;
481 	}
482 
483 	if (argcp < 2) {
484 		printf("Missing argument: handle\n");
485 		return;
486 	}
487 
488 	handle = strtohandle(argvp[1]);
489 	if (handle < 0) {
490 		printf("Invalid handle: %s\n", argvp[1]);
491 		return;
492 	}
493 
494 	if (argcp > 2) {
495 		char *e;
496 
497 		errno = 0;
498 		offset = strtol(argvp[2], &e, 0);
499 		if (errno != 0 || *e != '\0') {
500 			printf("Invalid offset: %s\n", argvp[2]);
501 			return;
502 		}
503 	}
504 
505 	gatt_read_char(attrib, handle, offset, char_read_cb, attrib);
506 }
507 
cmd_read_uuid(int argcp,char ** argvp)508 static void cmd_read_uuid(int argcp, char **argvp)
509 {
510 	struct characteristic_data *char_data;
511 	int start = 0x0001;
512 	int end = 0xffff;
513 	bt_uuid_t uuid;
514 
515 	if (conn_state != STATE_CONNECTED) {
516 		printf("Command failed: disconnected\n");
517 		return;
518 	}
519 
520 	if (argcp < 2) {
521 		printf("Missing argument: UUID\n");
522 		return;
523 	}
524 
525 	if (bt_string_to_uuid(&uuid, argvp[1]) < 0) {
526 		printf("Invalid UUID\n");
527 		return;
528 	}
529 
530 	if (argcp > 2) {
531 		start = strtohandle(argvp[2]);
532 		if (start < 0) {
533 			printf("Invalid start handle: %s\n", argvp[1]);
534 			return;
535 		}
536 	}
537 
538 	if (argcp > 3) {
539 		end = strtohandle(argvp[3]);
540 		if (end < 0) {
541 			printf("Invalid end handle: %s\n", argvp[2]);
542 			return;
543 		}
544 	}
545 
546 	char_data = g_new(struct characteristic_data, 1);
547 	char_data->orig_start = start;
548 	char_data->start = start;
549 	char_data->end = end;
550 	char_data->uuid = uuid;
551 
552 	gatt_read_char_by_uuid(attrib, start, end, &char_data->uuid,
553 					char_read_by_uuid_cb, char_data);
554 }
555 
char_write_req_cb(guint8 status,const guint8 * pdu,guint16 plen,gpointer user_data)556 static void char_write_req_cb(guint8 status, const guint8 *pdu, guint16 plen,
557 							gpointer user_data)
558 {
559 	if (status != 0) {
560 		printf("Characteristic Write Request failed: "
561 						"%s\n", att_ecode2str(status));
562 		return;
563 	}
564 
565 	if (!dec_write_resp(pdu, plen)) {
566 		printf("Protocol error\n");
567 		return;
568 	}
569 
570 	printf("Characteristic value was written successfully\n");
571 }
572 
cmd_char_write(int argcp,char ** argvp)573 static void cmd_char_write(int argcp, char **argvp)
574 {
575 	uint8_t *value;
576 	size_t plen;
577 	int handle;
578 
579 	if (conn_state != STATE_CONNECTED) {
580 		printf("Command failed: disconnected\n");
581 		return;
582 	}
583 
584 	if (argcp < 3) {
585 		printf("Usage: %s <handle> <new value>\n", argvp[0]);
586 		return;
587 	}
588 
589 	handle = strtoll(argvp[1], NULL, 16);
590 	if (errno != 0 || handle <= 0) {
591 		printf("A valid handle is required\n");
592 		return;
593 	}
594 
595 	plen = gatt_attr_data_from_string(argvp[2], &value);
596 	if (plen == 0) {
597 		g_printerr("Invalid value\n");
598 		return;
599 	}
600 
601 	if (g_strcmp0("char-write-req", argvp[0]) == 0)
602 		gatt_write_char(attrib, handle, value, plen,
603 					char_write_req_cb, NULL);
604 	else
605 		gatt_write_char(attrib, handle, value, plen, NULL, NULL);
606 
607 	g_free(value);
608 }
609 
cmd_sec_level(int argcp,char ** argvp)610 static void cmd_sec_level(int argcp, char **argvp)
611 {
612 	GError *gerr = NULL;
613 	BtIOSecLevel sec_level;
614 
615 	if (argcp < 2) {
616 		printf("sec-level: %s\n", opt_sec_level);
617 		return;
618 	}
619 
620 	if (strcasecmp(argvp[1], "medium") == 0)
621 		sec_level = BT_IO_SEC_MEDIUM;
622 	else if (strcasecmp(argvp[1], "high") == 0)
623 		sec_level = BT_IO_SEC_HIGH;
624 	else if (strcasecmp(argvp[1], "low") == 0)
625 		sec_level = BT_IO_SEC_LOW;
626 	else {
627 		printf("Allowed values: low | medium | high\n");
628 		return;
629 	}
630 
631 	g_free(opt_sec_level);
632 	opt_sec_level = g_strdup(argvp[1]);
633 
634 	if (conn_state != STATE_CONNECTED)
635 		return;
636 
637 	if (opt_psm) {
638 		printf("It must be reconnected to this change take effect\n");
639 		return;
640 	}
641 
642 	bt_io_set(iochannel, BT_IO_L2CAP, &gerr,
643 			BT_IO_OPT_SEC_LEVEL, sec_level,
644 			BT_IO_OPT_INVALID);
645 
646 	if (gerr) {
647 		printf("Error: %s\n", gerr->message);
648 		g_error_free(gerr);
649 	}
650 }
651 
exchange_mtu_cb(guint8 status,const guint8 * pdu,guint16 plen,gpointer user_data)652 static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen,
653 							gpointer user_data)
654 {
655 	uint16_t mtu;
656 
657 	if (status != 0) {
658 		printf("Exchange MTU Request failed: %s\n",
659 							att_ecode2str(status));
660 		return;
661 	}
662 
663 	if (!dec_mtu_resp(pdu, plen, &mtu)) {
664 		printf("Protocol error\n");
665 		return;
666 	}
667 
668 	mtu = MIN(mtu, opt_mtu);
669 	/* Set new value for MTU in client */
670 	if (g_attrib_set_mtu(attrib, mtu))
671 		printf("MTU was exchanged successfully: %d\n", mtu);
672 	else
673 		printf("Error exchanging MTU\n");
674 }
675 
cmd_mtu(int argcp,char ** argvp)676 static void cmd_mtu(int argcp, char **argvp)
677 {
678 	if (conn_state != STATE_CONNECTED) {
679 		printf("Command failed: not connected.\n");
680 		return;
681 	}
682 
683 	if (opt_psm) {
684 		printf("Command failed: operation is only available for LE"
685 							" transport.\n");
686 		return;
687 	}
688 
689 	if (argcp < 2) {
690 		printf("Usage: mtu <value>\n");
691 		return;
692 	}
693 
694 	if (opt_mtu) {
695 		printf("Command failed: MTU exchange can only occur once per"
696 							" connection.\n");
697 		return;
698 	}
699 
700 	errno = 0;
701 	opt_mtu = strtoll(argvp[1], NULL, 0);
702 	if (errno != 0 || opt_mtu < ATT_DEFAULT_LE_MTU) {
703 		printf("Invalid value. Minimum MTU size is %d\n",
704 							ATT_DEFAULT_LE_MTU);
705 		return;
706 	}
707 
708 	gatt_exchange_mtu(attrib, opt_mtu, exchange_mtu_cb, NULL);
709 }
710 
711 static struct {
712 	const char *cmd;
713 	void (*func)(int argcp, char **argvp);
714 	const char *params;
715 	const char *desc;
716 } commands[] = {
717 	{ "help",		cmd_help,	"",
718 		"Show this help"},
719 	{ "exit",		cmd_exit,	"",
720 		"Exit interactive mode" },
721 	{ "connect",		cmd_connect,	"[address]",
722 		"Connect to a remote device" },
723 	{ "disconnect",		cmd_disconnect,	"",
724 		"Disconnect from a remote device" },
725 	{ "primary",		cmd_primary,	"[UUID]",
726 		"Primary Service Discovery" },
727 	{ "characteristics",	cmd_char,	"[start hnd [end hnd [UUID]]]",
728 		"Characteristics Discovery" },
729 	{ "char-desc",		cmd_char_desc,	"[start hnd] [end hnd]",
730 		"Characteristics Descriptor Discovery" },
731 	{ "char-read-hnd",	cmd_read_hnd,	"<handle> [offset]",
732 		"Characteristics Value/Descriptor Read by handle" },
733 	{ "char-read-uuid",	cmd_read_uuid,	"<UUID> [start hnd] [end hnd]",
734 		"Characteristics Value/Descriptor Read by UUID" },
735 	{ "char-write-req",	cmd_char_write,	"<handle> <new value>",
736 		"Characteristic Value Write (Write Request)" },
737 	{ "char-write-cmd",	cmd_char_write,	"<handle> <new value>",
738 		"Characteristic Value Write (No response)" },
739 	{ "sec-level",		cmd_sec_level,	"[low | medium | high]",
740 		"Set security level. Default: low" },
741 	{ "mtu",		cmd_mtu,	"<value>",
742 		"Exchange MTU for GATT/ATT" },
743 	{ NULL, NULL, NULL}
744 };
745 
cmd_help(int argcp,char ** argvp)746 static void cmd_help(int argcp, char **argvp)
747 {
748 	int i;
749 
750 	for (i = 0; commands[i].cmd; i++)
751 		printf("%-15s %-30s %s\n", commands[i].cmd,
752 				commands[i].params, commands[i].desc);
753 }
754 
parse_line(char * line_read)755 static void parse_line(char *line_read)
756 {
757 	gchar **argvp;
758 	int argcp;
759 	int i;
760 
761 	if (line_read == NULL) {
762 		printf("\n");
763 		cmd_exit(0, NULL);
764 		return;
765 	}
766 
767 	line_read = g_strstrip(line_read);
768 
769 	if (*line_read == '\0')
770 		return;
771 
772 	add_history(line_read);
773 
774 	g_shell_parse_argv(line_read, &argcp, &argvp, NULL);
775 
776 	for (i = 0; commands[i].cmd; i++)
777 		if (strcasecmp(commands[i].cmd, argvp[0]) == 0)
778 			break;
779 
780 	if (commands[i].cmd)
781 		commands[i].func(argcp, argvp);
782 	else
783 		printf("%s: command not found\n", argvp[0]);
784 
785 	g_strfreev(argvp);
786 }
787 
prompt_read(GIOChannel * chan,GIOCondition cond,gpointer user_data)788 static gboolean prompt_read(GIOChannel *chan, GIOCondition cond,
789 							gpointer user_data)
790 {
791 	if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
792 		g_io_channel_unref(chan);
793 		return FALSE;
794 	}
795 
796 	rl_callback_read_char();
797 
798 	return TRUE;
799 }
800 
interactive(const gchar * src,const gchar * dst,int psm)801 int interactive(const gchar *src, const gchar *dst, int psm)
802 {
803 	GIOChannel *pchan;
804 	gint events;
805 
806 	opt_sec_level = g_strdup("low");
807 
808 	opt_src = g_strdup(src);
809 	opt_dst = g_strdup(dst);
810 	opt_psm = psm;
811 
812 	prompt = g_string_new(NULL);
813 
814 	event_loop = g_main_loop_new(NULL, FALSE);
815 
816 	pchan = g_io_channel_unix_new(fileno(stdin));
817 	g_io_channel_set_close_on_unref(pchan, TRUE);
818 	events = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
819 	g_io_add_watch(pchan, events, prompt_read, NULL);
820 
821 	rl_callback_handler_install(get_prompt(), parse_line);
822 
823 	g_main_loop_run(event_loop);
824 
825 	rl_callback_handler_remove();
826 	cmd_disconnect(0, NULL);
827 	g_io_channel_unref(pchan);
828 	g_main_loop_unref(event_loop);
829 	g_string_free(prompt, TRUE);
830 
831 	g_free(opt_src);
832 	g_free(opt_dst);
833 	g_free(opt_sec_level);
834 
835 	return 0;
836 }
837