Re: [PATCH BlueZ 4/9] btgatt-client: Rewrite to use bt_shell

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Simon,

On Thu, Mar 23, 2023 at 3:44 AM Simon Mikuda
<simon.mikuda@xxxxxxxxxxxxxxxxxxx> wrote:

This is a pretty good idea, but make sure you cover each procedure
properly, also be aware that with introduction of the following series
you will probably be able to create scripts:

https://patchwork.kernel.org/project/bluetooth/list/?series=732537

Also in theory much of btgatt-client is now supported in bluetoothctl,
so I wonder why you are not using the daemon to connect, etc?

> ---
>  Makefile.tools        |    2 +-
>  src/shared/shell.h    |    1 +
>  tools/btgatt-client.c | 1087 ++++++++++++++++-------------------------
>  3 files changed, 412 insertions(+), 678 deletions(-)
>
> diff --git a/Makefile.tools b/Makefile.tools
> index df4cad065..6125e57f7 100644
> --- a/Makefile.tools
> +++ b/Makefile.tools
> @@ -315,7 +315,7 @@ tools_ibeacon_LDADD = src/libshared-mainloop.la
>
>  tools_btgatt_client_SOURCES = tools/btgatt-client.c src/uuid-helper.c
>  tools_btgatt_client_LDADD = src/libshared-mainloop.la \
> -                                               lib/libbluetooth-internal.la
> +                                               lib/libbluetooth-internal.la -lreadline
>
>  tools_btgatt_server_SOURCES = tools/btgatt-server.c src/uuid-helper.c
>  tools_btgatt_server_LDADD = src/libshared-mainloop.la \
> diff --git a/src/shared/shell.h b/src/shared/shell.h
> index 87fb5c415..8793835c0 100644
> --- a/src/shared/shell.h
> +++ b/src/shared/shell.h
> @@ -15,6 +15,7 @@
>  #define COLOR_GREEN    "\001\x1B[0;92m\002"
>  #define COLOR_YELLOW   "\001\x1B[0;93m\002"
>  #define COLOR_BLUE     "\001\x1B[0;94m\002"
> +#define COLOR_MAGENTA  "\x1B[0;95m"
>  #define COLOR_BOLDGRAY "\001\x1B[1;30m\002"
>  #define COLOR_BOLDWHITE        "\001\x1B[1;37m\002"
>  #define COLOR_HIGHLIGHT        "\001\x1B[1;39m\002"
> diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
> index 2a0cb5181..ecfe3f3f1 100644
> --- a/tools/btgatt-client.c
> +++ b/tools/btgatt-client.c
> @@ -28,6 +28,7 @@
>  #include "lib/uuid.h"
>
>  #include "src/shared/mainloop.h"
> +#include "src/shared/shell.h"
>  #include "src/shared/util.h"
>  #include "src/shared/att.h"
>  #include "src/shared/queue.h"
> @@ -37,19 +38,29 @@
>  #define ATT_CID 4
>  #define ATT_PSM 31
>
> -#define PRLOG(...) \
> -       printf(__VA_ARGS__); print_prompt();
> -
> -#define COLOR_OFF      "\x1B[0m"
> -#define COLOR_RED      "\x1B[0;91m"
> -#define COLOR_GREEN    "\x1B[0;92m"
> -#define COLOR_YELLOW   "\x1B[0;93m"
> -#define COLOR_BLUE     "\x1B[0;94m"
> -#define COLOR_MAGENTA  "\x1B[0;95m"
> -#define COLOR_BOLDGRAY "\x1B[1;30m"
> -#define COLOR_BOLDWHITE        "\x1B[1;37m"
> +#define MAX_LEN_LINE 512
>
> +struct client *cli;
>  static bool verbose = false;
> +static bool shell_running = false;
> +
> +#define print(fmt, arg...) do { \
> +       if (shell_running) \
> +               bt_shell_printf(fmt "\n", ## arg); \
> +       else \
> +               printf(fmt "\n", ## arg); \
> +} while (0)
> +
> +#define error(fmt, arg...) do { \
> +       if (shell_running) \
> +               bt_shell_printf(COLOR_RED fmt "\n" COLOR_OFF, ## arg); \
> +       else \
> +               fprintf(stderr, COLOR_RED fmt "\n" COLOR_OFF, ## arg); \
> +} while (0)
> +
> +#define append(str, fmt, arg...) do { \
> +       sprintf(strchr(str, '\0'), fmt, ## arg); \
> +} while (0)
>
>  struct client {
>         int fd;
> @@ -60,10 +71,11 @@ struct client {
>         unsigned int reliable_session_id;
>  };
>
> -static void print_prompt(void)
> +static void update_prompt(void)
>  {
> -       printf(COLOR_BLUE "[GATT client]" COLOR_OFF "# ");
> -       fflush(stdout);
> +       char str[32];
> +       sprintf(str, COLOR_BLUE "[GATT client]" COLOR_OFF "# ");
> +       bt_shell_set_prompt(str);
>  }
>
>  static const char *ecode_to_string(uint8_t ecode)
> @@ -116,7 +128,7 @@ static const char *ecode_to_string(uint8_t ecode)
>
>  static void att_disconnect_cb(int err, void *user_data)
>  {
> -       printf("Device disconnected: %s\n", strerror(err));
> +       print("Device disconnected: %s", strerror(err));
>
>         mainloop_quit();
>  }
> @@ -125,14 +137,14 @@ static void att_debug_cb(const char *str, void *user_data)
>  {
>         const char *prefix = user_data;
>
> -       PRLOG(COLOR_BOLDGRAY "%s" COLOR_BOLDWHITE "%s\n" COLOR_OFF, prefix, str);
> +       print(COLOR_BOLDGRAY "%s" COLOR_BOLDWHITE "%s" COLOR_OFF, prefix, str);
>  }
>
>  static void gatt_debug_cb(const char *str, void *user_data)
>  {
>         const char *prefix = user_data;
>
> -       PRLOG(COLOR_GREEN "%s%s\n" COLOR_OFF, prefix, str);
> +       print(COLOR_GREEN "%s%s" COLOR_OFF, prefix, str);
>  }
>
>  static void ready_cb(bool success, uint8_t att_ecode, void *user_data);
> @@ -150,7 +162,7 @@ static void log_service_event(struct gatt_db_attribute *attr, const char *str)
>
>         gatt_db_attribute_get_service_handles(attr, &start, &end);
>
> -       PRLOG("%s - UUID: %s start: 0x%04x end: 0x%04x\n", str, uuid_str,
> +       print("%s - UUID: %s start: 0x%04x end: 0x%04x", str, uuid_str,
>                                                                 start, end);
>  }
>
> @@ -170,20 +182,20 @@ static struct client *client_create(int fd, uint16_t mtu)
>
>         cli = new0(struct client, 1);
>         if (!cli) {
> -               fprintf(stderr, "Failed to allocate memory for client\n");
> +               error("Failed to allocate memory for client");
>                 return NULL;
>         }
>
>         cli->att = bt_att_new(fd, false);
>         if (!cli->att) {
> -               fprintf(stderr, "Failed to initialze ATT transport layer\n");
> +               error("Failed to initialze ATT transport layer");
>                 bt_att_unref(cli->att);
>                 free(cli);
>                 return NULL;
>         }
>
>         if (!bt_att_set_close_on_unref(cli->att, true)) {
> -               fprintf(stderr, "Failed to set up ATT transport layer\n");
> +               error("Failed to set up ATT transport layer");
>                 bt_att_unref(cli->att);
>                 free(cli);
>                 return NULL;
> @@ -191,7 +203,7 @@ static struct client *client_create(int fd, uint16_t mtu)
>
>         if (!bt_att_register_disconnect(cli->att, att_disconnect_cb, NULL,
>                                                                 NULL)) {
> -               fprintf(stderr, "Failed to set ATT disconnect handler\n");
> +               error("Failed to set ATT disconnect handler");
>                 bt_att_unref(cli->att);
>                 free(cli);
>                 return NULL;
> @@ -200,7 +212,7 @@ static struct client *client_create(int fd, uint16_t mtu)
>         cli->fd = fd;
>         cli->db = gatt_db_new();
>         if (!cli->db) {
> -               fprintf(stderr, "Failed to create GATT database\n");
> +               error("Failed to create GATT database");
>                 bt_att_unref(cli->att);
>                 free(cli);
>                 return NULL;
> @@ -208,7 +220,7 @@ static struct client *client_create(int fd, uint16_t mtu)
>
>         cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu, 0);
>         if (!cli->gatt) {
> -               fprintf(stderr, "Failed to create GATT client\n");
> +               error("Failed to create GATT client");
>                 gatt_db_unref(cli->db);
>                 bt_att_unref(cli->att);
>                 free(cli);
> @@ -225,8 +237,8 @@ static struct client *client_create(int fd, uint16_t mtu)
>                                                                         NULL);
>         }
>
> -       bt_gatt_client_ready_register(cli->gatt, ready_cb, cli, NULL);
> -       bt_gatt_client_set_service_changed(cli->gatt, service_changed_cb, cli,
> +       bt_gatt_client_ready_register(cli->gatt, ready_cb, NULL, NULL);
> +       bt_gatt_client_set_service_changed(cli->gatt, service_changed_cb, NULL,
>                                                                         NULL);
>
>         /* bt_gatt_client already holds a reference */
> @@ -242,7 +254,7 @@ static void client_destroy(struct client *cli)
>         free(cli);
>  }
>
> -static void print_uuid(const bt_uuid_t *uuid)
> +static void append_uuid(char *str, const bt_uuid_t *uuid)
>  {
>         char uuid_str[MAX_LEN_UUID_STR];
>         bt_uuid_t uuid128;
> @@ -250,15 +262,15 @@ static void print_uuid(const bt_uuid_t *uuid)
>         bt_uuid_to_uuid128(uuid, &uuid128);
>         bt_uuid_to_string(&uuid128, uuid_str, sizeof(uuid_str));
>
> -       printf("%s\n", uuid_str);
> +       append(str, "%s", uuid_str);
>  }
>
>  static void print_incl(struct gatt_db_attribute *attr, void *user_data)
>  {
> -       struct client *cli = user_data;
>         uint16_t handle, start, end;
>         struct gatt_db_attribute *service;
>         bt_uuid_t uuid;
> +       char line[MAX_LEN_LINE] = {0};
>
>         if (!gatt_db_attribute_get_incl_data(attr, &handle, &start, &end))
>                 return;
> @@ -269,18 +281,21 @@ static void print_incl(struct gatt_db_attribute *attr, void *user_data)
>
>         gatt_db_attribute_get_service_uuid(service, &uuid);
>
> -       printf("\t  " COLOR_GREEN "include" COLOR_OFF " - handle: "
> +       append(line, "\t  " COLOR_GREEN "include" COLOR_OFF " - handle: "
>                                         "0x%04x, - start: 0x%04x, end: 0x%04x,"
>                                         "uuid: ", handle, start, end);
> -       print_uuid(&uuid);
> +       append_uuid(line, &uuid);
> +       print("%s", line);
>  }
>
>  static void print_desc(struct gatt_db_attribute *attr, void *user_data)
>  {
> -       printf("\t\t  " COLOR_MAGENTA "descr" COLOR_OFF
> +       char line[MAX_LEN_LINE] = {0};
> +       append(line, "\t\t  " COLOR_MAGENTA "descr" COLOR_OFF
>                                         " - handle: 0x%04x, uuid: ",
>                                         gatt_db_attribute_get_handle(attr));
> -       print_uuid(gatt_db_attribute_get_type(attr));
> +       append_uuid(line, gatt_db_attribute_get_type(attr));
> +       print("%s", line);
>  }
>
>  static void print_chrc(struct gatt_db_attribute *attr, void *user_data)
> @@ -289,6 +304,7 @@ static void print_chrc(struct gatt_db_attribute *attr, void *user_data)
>         uint8_t properties;
>         uint16_t ext_prop;
>         bt_uuid_t uuid;
> +       char line[MAX_LEN_LINE] = {0};
>
>         if (!gatt_db_attribute_get_char_data(attr, &handle,
>                                                                 &value_handle,
> @@ -297,170 +313,133 @@ static void print_chrc(struct gatt_db_attribute *attr, void *user_data)
>                                                                 &uuid))
>                 return;
>
> -       printf("\t  " COLOR_YELLOW "charac" COLOR_OFF
> +       append(line, "\t  " COLOR_YELLOW "charac" COLOR_OFF
>                                 " - start: 0x%04x, value: 0x%04x, "
>                                 "props: 0x%02x, ext_props: 0x%04x, uuid: ",
>                                 handle, value_handle, properties, ext_prop);
> -       print_uuid(&uuid);
> +       append_uuid(line, &uuid);
> +       print("%s", line);
>
>         gatt_db_service_foreach_desc(attr, print_desc, NULL);
>  }
>
>  static void print_service(struct gatt_db_attribute *attr, void *user_data)
>  {
> -       struct client *cli = user_data;
>         uint16_t start, end;
>         bool primary;
>         bt_uuid_t uuid;
> +       char line[MAX_LEN_LINE] = {0};
>
>         if (!gatt_db_attribute_get_service_data(attr, &start, &end, &primary,
>                                                                         &uuid))
>                 return;
>
> -       printf(COLOR_RED "service" COLOR_OFF " - start: 0x%04x, "
> +       append(line, COLOR_RED "service" COLOR_OFF " - start: 0x%04x, "
>                                 "end: 0x%04x, type: %s, uuid: ",
>                                 start, end, primary ? "primary" : "secondary");
> -       print_uuid(&uuid);
> +       append_uuid(line, &uuid);
> +       print("%s", line);
>
> -       gatt_db_service_foreach_incl(attr, print_incl, cli);
> +       gatt_db_service_foreach_incl(attr, print_incl, NULL);
>         gatt_db_service_foreach_char(attr, print_chrc, NULL);
> -
> -       printf("\n");
>  }
>
>  static void print_services(struct client *cli)
>  {
> -       printf("\n");
> -
> -       gatt_db_foreach_service(cli->db, NULL, print_service, cli);
> +       gatt_db_foreach_service(cli->db, NULL, print_service, NULL);
>  }
>
> -static void print_services_by_uuid(struct client *cli, const bt_uuid_t *uuid)
> +static void print_services_by_uuid(const bt_uuid_t *uuid)
>  {
> -       printf("\n");
> -
> -       gatt_db_foreach_service(cli->db, uuid, print_service, cli);
> +       gatt_db_foreach_service(cli->db, uuid, print_service, NULL);
>  }
>
> -static void print_services_by_handle(struct client *cli, uint16_t handle)
> +static void print_services_by_handle(uint16_t handle)
>  {
> -       printf("\n");
> -
>         /* TODO: Filter by handle */
>         gatt_db_foreach_service(cli->db, NULL, print_service, cli);
>  }
>
>  static void ready_cb(bool success, uint8_t att_ecode, void *user_data)
>  {
> -       struct client *cli = user_data;
> -
>         if (!success) {
> -               PRLOG("GATT discovery procedures failed - error code: 0x%02x\n",
> +               error("GATT discovery procedures failed - error code: 0x%02x",
>                                                                 att_ecode);
>                 return;
>         }
>
> -       PRLOG("GATT discovery procedures complete\n");
> +       print("GATT discovery procedures complete");
>
>         print_services(cli);
> -       print_prompt();
>  }
>
>  static void service_changed_cb(uint16_t start_handle, uint16_t end_handle,
>                                                                 void *user_data)
>  {
> -       struct client *cli = user_data;
> -
> -       printf("\nService Changed handled - start: 0x%04x end: 0x%04x\n",
> +       print("Service Changed handled - start: 0x%04x end: 0x%04x",
>                                                 start_handle, end_handle);
>
> -       gatt_db_foreach_service_in_range(cli->db, NULL, print_service, cli,
> +       gatt_db_foreach_service_in_range(cli->db, NULL, print_service, NULL,
>                                                 start_handle, end_handle);
> -       print_prompt();
> -}
> -
> -static void services_usage(void)
> -{
> -       printf("Usage: services [options]\nOptions:\n"
> -               "\t -u, --uuid <uuid>\tService UUID\n"
> -               "\t -a, --handle <handle>\tService start handle\n"
> -               "\t -h, --help\t\tShow help message\n"
> -               "e.g.:\n"
> -               "\tservices\n\tservices -u 0x180d\n\tservices -a 0x0009\n");
>  }
>
> -static bool parse_args(char *str, int expected_argc,  char **argv, int *argc)
> -{
> -       char **ap;
> -
> -       for (ap = argv; (*ap = strsep(&str, " \t")) != NULL;) {
> -               if (**ap == '\0')
> -                       continue;
> -
> -               (*argc)++;
> -               ap++;
> -
> -               if (*argc > expected_argc)
> -                       return false;
> -       }
> -
> -       return true;
> -}
> +static struct option services_options[] = {
> +       { "uuid",       1, 0, 'u' },
> +       { "handle",     1, 0, 'a' },
> +       { "help",       0, 0, 'h' },
> +       { 0, 0, 0, 0 }
> +};
>
> -static void cmd_services(struct client *cli, char *cmd_str)
> +static void cmd_services(int argc, char **argv)
>  {
> -       char *argv[3];
> -       int argc = 0;
> +       int opt;
> +       bool use_uuid = false;
> +       bt_uuid_t tmp, uuid;
> +       uint16_t handle = 0;
> +       char *endptr = NULL;
>
>         if (!bt_gatt_client_is_ready(cli->gatt)) {
> -               printf("GATT client not initialized\n");
> -               return;
> -       }
> -
> -       if (!parse_args(cmd_str, 2, argv, &argc)) {
> -               services_usage();
> -               return;
> -       }
> -
> -       if (!argc) {
> -               print_services(cli);
> +               print("GATT client not initialized");
>                 return;
>         }
>
> -       if (argc != 2) {
> -               services_usage();
> -               return;
> -       }
> -
> -       if (!strcmp(argv[0], "-u") || !strcmp(argv[0], "--uuid")) {
> -               bt_uuid_t tmp, uuid;
> -
> -               if (bt_string_to_uuid(&tmp, argv[1]) < 0) {
> -                       printf("Invalid UUID: %s\n", argv[1]);
> -                       return;
> -               }
> -
> -               bt_uuid_to_uuid128(&tmp, &uuid);
> -
> -               print_services_by_uuid(cli, &uuid);
> -       } else if (!strcmp(argv[0], "-a") || !strcmp(argv[0], "--handle")) {
> -               uint16_t handle;
> -               char *endptr = NULL;
> -
> -               handle = strtol(argv[1], &endptr, 0);
> -               if (!endptr || *endptr != '\0') {
> -                       printf("Invalid start handle: %s\n", argv[1]);
> -                       return;
> +       while ((opt = getopt_long(argc, argv, "u:a:", services_options,
> +                                                               NULL)) != -1) {
> +               switch (opt) {
> +               case 'u':
> +                       if (bt_string_to_uuid(&tmp, optarg) < 0) {
> +                               error("Invalid UUID: %s", optarg);
> +                               optind = 0;
> +                               return bt_shell_noninteractive_quit(EXIT_FAILURE);
> +                       }
> +                       bt_uuid_to_uuid128(&tmp, &uuid);
> +                       use_uuid = true;
> +                       break;
> +               case 'a':
> +                       handle = strtol(optarg, &endptr, 0);
> +                       if (!endptr || *endptr != '\0') {
> +                               error("Invalid start handle: %s", optarg);
> +                               optind = 0;
> +                               return bt_shell_noninteractive_quit(EXIT_FAILURE);
> +                       }
> +                       break;
> +               case 'h':
> +                       bt_shell_usage();
> +                       optind = 0;
> +                       return bt_shell_noninteractive_quit(EXIT_SUCCESS);
> +               default:
> +                       bt_shell_usage();
> +                       optind = 0;
> +                       return bt_shell_noninteractive_quit(EXIT_FAILURE);
>                 }
> +       }
>
> -               print_services_by_handle(cli, handle);
> -       } else
> -               services_usage();
> -}
> +       optind = 0;
>
> -static void read_multiple_usage(void)
> -{
> -       printf("Usage: read-multiple <handle_1> <handle_2> ...\n");
> +       if (use_uuid)
> +               print_services_by_uuid(&uuid);
> +       else
> +               print_services_by_handle(handle);
>  }
>
>  static void read_multiple_cb(bool success, uint8_t att_ecode,
> @@ -468,43 +447,37 @@ static void read_multiple_cb(bool success, uint8_t att_ecode,
>                                         void *user_data)
>  {
>         int i;
> +       char line[MAX_LEN_LINE] = {0};
>
>         if (!success) {
> -               PRLOG("\nRead multiple request failed: 0x%02x\n", att_ecode);
> +               error("Read multiple request failed: 0x%02x", att_ecode);
>                 return;
>         }
>
> -       printf("\nRead multiple value (%u bytes):", length);
> +       append(line, "Read multiple value (%u bytes):", length);
>
>         for (i = 0; i < length; i++)
> -               printf("%02x ", value[i]);
> +               append(line, "%02x ", value[i]);
>
> -       PRLOG("\n");
> +       print("%s", line);
>  }
>
> -static void cmd_read_multiple(struct client *cli, char *cmd_str)
> +static void cmd_read_multiple(int argc, char **argv)
>  {
> -       int argc = 0;
>         uint16_t *value;
> -       char *argv[512];
>         int i;
>         char *endptr = NULL;
>
> -       if (!parse_args(cmd_str, sizeof(argv), argv, &argc) || argc < 2) {
> -               read_multiple_usage();
> -               return;
> -       }
> -
>         value = malloc(sizeof(uint16_t) * argc);
>         if (!value) {
> -               printf("Failed to construct value\n");
> +               error("Failed to construct value");
>                 return;
>         }
>
> -       for (i = 0; i < argc; i++) {
> +       for (i = 1; i < argc; i++) {
>                 value[i] = strtol(argv[i], &endptr, 0);
>                 if (endptr == argv[i] || *endptr != '\0' || !value[i]) {
> -                       printf("Invalid value byte: %s\n", argv[i]);
> +                       error("Invalid value byte: %s", argv[i]);
>                         free(value);
>                         return;
>                 }
> @@ -512,133 +485,98 @@ static void cmd_read_multiple(struct client *cli, char *cmd_str)
>
>         if (!bt_gatt_client_read_multiple(cli->gatt, value, argc,
>                                                 read_multiple_cb, NULL, NULL))
> -               printf("Failed to initiate read multiple procedure\n");
> +               error("Failed to initiate read multiple procedure");
>
>         free(value);
>  }
>
> -static void read_value_usage(void)
> -{
> -       printf("Usage: read-value <value_handle>\n");
> -}
> -
>  static void read_cb(bool success, uint8_t att_ecode, const uint8_t *value,
>                                         uint16_t length, void *user_data)
>  {
>         int i;
> +       char line[MAX_LEN_LINE] = {0};
>
>         if (!success) {
> -               PRLOG("\nRead request failed: %s (0x%02x)\n",
> +               error("Read request failed: %s (0x%02x)",
>                                 ecode_to_string(att_ecode), att_ecode);
>                 return;
>         }
>
> -       printf("\nRead value");
> +       append(line, "Read value");
>
>         if (length == 0) {
> -               PRLOG(": 0 bytes\n");
> +               print("%s: 0 bytes", line);
>                 return;
>         }
>
> -       printf(" (%u bytes): ", length);
> +       append(line, " (%u bytes): ", length);
>
>         for (i = 0; i < length; i++)
> -               printf("%02x ", value[i]);
> +               append(line, "%02x ", value[i]);
>
> -       PRLOG("\n");
> +       print("%s", line);
>  }
>
> -static void cmd_read_value(struct client *cli, char *cmd_str)
> +static void cmd_read_value(int argc, char **argv)
>  {
> -       char *argv[2];
> -       int argc = 0;
>         uint16_t handle;
>         char *endptr = NULL;
>
> -       if (!parse_args(cmd_str, 1, argv, &argc) || argc != 1) {
> -               read_value_usage();
> -               return;
> -       }
> -
> -       handle = strtol(argv[0], &endptr, 0);
> +       handle = strtol(argv[1], &endptr, 0);
>         if (!endptr || *endptr != '\0' || !handle) {
> -               printf("Invalid value handle: %s\n", argv[0]);
> +               error("Invalid value handle: %s", argv[1]);
>                 return;
>         }
>
>         if (!bt_gatt_client_read_value(cli->gatt, handle, read_cb,
>                                                                 NULL, NULL))
> -               printf("Failed to initiate read value procedure\n");
> -}
> -
> -static void read_long_value_usage(void)
> -{
> -       printf("Usage: read-long-value <value_handle> <offset>\n");
> +               error("Failed to initiate read value procedure");
>  }
>
> -static void cmd_read_long_value(struct client *cli, char *cmd_str)
> +static void cmd_read_long_value(int argc, char **argv)
>  {
> -       char *argv[3];
> -       int argc = 0;
>         uint16_t handle;
>         uint16_t offset;
>         char *endptr = NULL;
>
> -       if (!parse_args(cmd_str, 2, argv, &argc) || argc != 2) {
> -               read_long_value_usage();
> -               return;
> -       }
> -
> -       handle = strtol(argv[0], &endptr, 0);
> +       handle = strtol(argv[1], &endptr, 0);
>         if (!endptr || *endptr != '\0' || !handle) {
> -               printf("Invalid value handle: %s\n", argv[0]);
> +               error("Invalid value handle: %s", argv[1]);
>                 return;
>         }
>
>         endptr = NULL;
> -       offset = strtol(argv[1], &endptr, 0);
> +       offset = strtol(argv[2], &endptr, 0);
>         if (!endptr || *endptr != '\0') {
> -               printf("Invalid offset: %s\n", argv[1]);
> +               error("Invalid offset: %s", argv[2]);
>                 return;
>         }
>
>         if (!bt_gatt_client_read_long_value(cli->gatt, handle, offset, read_cb,
>                                                                 NULL, NULL))
> -               printf("Failed to initiate read long value procedure\n");
> -}
> -
> -static void write_value_usage(void)
> -{
> -       printf("Usage: write-value [options] <value_handle> <value>\n"
> -               "Options:\n"
> -               "\t-w, --without-response\tWrite without response\n"
> -               "\t-s, --signed-write\tSigned write command\n"
> -               "e.g.:\n"
> -               "\twrite-value 0x0001 00 01 00\n");
> +               error("Failed to initiate read long value procedure");
>  }
>
>  static struct option write_value_options[] = {
>         { "without-response",   0, 0, 'w' },
>         { "signed-write",       0, 0, 's' },
> +       { "help",       0, 0, 'h' },
>         { }
>  };
>
>  static void write_cb(bool success, uint8_t att_ecode, void *user_data)
>  {
>         if (success) {
> -               PRLOG("\nWrite successful\n");
> +               print("Write successful");
>         } else {
> -               PRLOG("\nWrite failed: %s (0x%02x)\n",
> +               error("Write failed: %s (0x%02x)",
>                                 ecode_to_string(att_ecode), att_ecode);
>         }
>  }
>
> -static void cmd_write_value(struct client *cli, char *cmd_str)
> +static void cmd_write_value(int argc, char **argv)
>  {
>         int opt, i, val;
> -       char *argvbuf[516];
> -       char **argv = argvbuf;
> -       int argc = 1;
>         uint16_t handle;
>         char *endptr = NULL;
>         int length;
> @@ -646,14 +584,6 @@ static void cmd_write_value(struct client *cli, char *cmd_str)
>         bool without_response = false;
>         bool signed_write = false;
>
> -       if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
> -               printf("Too many arguments\n");
> -               write_value_usage();
> -               return;
> -       }
> -
> -       optind = 0;
> -       argv[0] = "write-value";
>         while ((opt = getopt_long(argc, argv, "+ws", write_value_options,
>                                                                 NULL)) != -1) {
>                 switch (opt) {
> @@ -663,23 +593,24 @@ static void cmd_write_value(struct client *cli, char *cmd_str)
>                 case 's':
>                         signed_write = true;
>                         break;
> +               case 'h':
> +                       bt_shell_usage();
> +                       optind = 0;
> +                       return bt_shell_noninteractive_quit(EXIT_SUCCESS);
>                 default:
> -                       write_value_usage();
> -                       return;
> +                       bt_shell_usage();
> +                       optind = 0;
> +                       return bt_shell_noninteractive_quit(EXIT_FAILURE);
>                 }
>         }
>
>         argc -= optind;
>         argv += optind;
> -
> -       if (argc < 1) {
> -               write_value_usage();
> -               return;
> -       }
> +       optind = 0;
>
>         handle = strtol(argv[0], &endptr, 0);
>         if (!endptr || *endptr != '\0' || !handle) {
> -               printf("Invalid handle: %s\n", argv[0]);
> +               error("Invalid handle: %s", argv[1]);
>                 return;
>         }
>
> @@ -687,13 +618,13 @@ static void cmd_write_value(struct client *cli, char *cmd_str)
>
>         if (length > 0) {
>                 if (length > UINT16_MAX) {
> -                       printf("Write value too long\n");
> +                       error("Write value too long");
>                         return;
>                 }
>
>                 value = malloc(length);
>                 if (!value) {
> -                       printf("Failed to construct write value\n");
> +                       error("Failed to construct write value");
>                         return;
>                 }
>
> @@ -701,7 +632,7 @@ static void cmd_write_value(struct client *cli, char *cmd_str)
>                         val = strtol(argv[i], &endptr, 0);
>                         if (endptr == argv[i] || *endptr != '\0'
>                                 || errno == ERANGE || val < 0 || val > 255) {
> -                               printf("Invalid value byte: %s\n",
> +                               error("Invalid value byte: %s",
>                                                                 argv[i]);
>                                 goto done;
>                         }
> @@ -712,36 +643,27 @@ static void cmd_write_value(struct client *cli, char *cmd_str)
>         if (without_response) {
>                 if (!bt_gatt_client_write_without_response(cli->gatt, handle,
>                                                 signed_write, value, length)) {
> -                       printf("Failed to initiate write without response "
> -                                                               "procedure\n");
> +                       error("Failed to initiate write without response "
> +                                                               "procedure");
>                         goto done;
>                 }
>
> -               printf("Write command sent\n");
> +               print("Write command sent");
>                 goto done;
>         }
>
>         if (!bt_gatt_client_write_value(cli->gatt, handle, value, length,
>                                                                 write_cb,
>                                                                 NULL, NULL))
> -               printf("Failed to initiate write procedure\n");
> +               error("Failed to initiate write procedure");
>
>  done:
>         free(value);
>  }
>
> -static void write_long_value_usage(void)
> -{
> -       printf("Usage: write-long-value [options] <value_handle> <offset> "
> -                               "<value>\n"
> -                               "Options:\n"
> -                               "\t-r, --reliable-write\tReliable write\n"
> -                               "e.g.:\n"
> -                               "\twrite-long-value 0x0001 0 00 01 00\n");
> -}
> -
>  static struct option write_long_value_options[] = {
>         { "reliable-write",     0, 0, 'r' },
> +       { "help", 0, 0, 'h' },
>         { }
>  };
>
> @@ -749,21 +671,18 @@ static void write_long_cb(bool success, bool reliable_error, uint8_t att_ecode,
>                                                                 void *user_data)
>  {
>         if (success) {
> -               PRLOG("Write successful\n");
> +               print("Write successful");
>         } else if (reliable_error) {
> -               PRLOG("Reliable write not verified\n");
> +               error("Reliable write not verified");
>         } else {
> -               PRLOG("\nWrite failed: %s (0x%02x)\n",
> +               error("Write failed: %s (0x%02x)",
>                                 ecode_to_string(att_ecode), att_ecode);
>         }
>  }
>
> -static void cmd_write_long_value(struct client *cli, char *cmd_str)
> +static void cmd_write_long_value(int argc, char **argv)
>  {
>         int opt, i, val;
> -       char *argvbuf[516];
> -       char **argv = argvbuf;
> -       int argc = 1;
>         uint16_t handle;
>         uint16_t offset;
>         char *endptr = NULL;
> @@ -771,44 +690,44 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str)
>         uint8_t *value = NULL;
>         bool reliable_writes = false;
>
> -       if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
> -               printf("Too many arguments\n");
> -               write_value_usage();
> -               return;
> -       }
> -
> -       optind = 0;
> -       argv[0] = "write-long-value";
>         while ((opt = getopt_long(argc, argv, "+r", write_long_value_options,
>                                                                 NULL)) != -1) {
>                 switch (opt) {
>                 case 'r':
>                         reliable_writes = true;
>                         break;
> +               case 'h':
> +                       bt_shell_usage();
> +                       optind = 0;
> +                       return bt_shell_noninteractive_quit(EXIT_SUCCESS);
>                 default:
> -                       write_long_value_usage();
> -                       return;
> +                       bt_shell_usage();
> +                       optind = 0;
> +                       return bt_shell_noninteractive_quit(EXIT_FAILURE);
>                 }
>         }
>
>         argc -= optind;
>         argv += optind;
> +       optind = 0;
>
> -       if (argc < 2) {
> -               write_long_value_usage();
> +       if (argc > 514) {
> +               error("Too many arguments");
> +               bt_shell_usage();
> +               optind = 0;
>                 return;
>         }
>
>         handle = strtol(argv[0], &endptr, 0);
>         if (!endptr || *endptr != '\0' || !handle) {
> -               printf("Invalid handle: %s\n", argv[0]);
> +               error("Invalid handle: %s", argv[1]);
>                 return;
>         }
>
>         endptr = NULL;
>         offset = strtol(argv[1], &endptr, 0);
>         if (!endptr || *endptr != '\0' || errno == ERANGE) {
> -               printf("Invalid offset: %s\n", argv[1]);
> +               error("Invalid offset: %s", argv[2]);
>                 return;
>         }
>
> @@ -816,13 +735,13 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str)
>
>         if (length > 0) {
>                 if (length > UINT16_MAX) {
> -                       printf("Write value too long\n");
> +                       error("Write value too long");
>                         return;
>                 }
>
>                 value = malloc(length);
>                 if (!value) {
> -                       printf("Failed to construct write value\n");
> +                       error("Failed to construct write value");
>                         return;
>                 }
>
> @@ -830,7 +749,7 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str)
>                         val = strtol(argv[i], &endptr, 0);
>                         if (endptr == argv[i] || *endptr != '\0'
>                                 || errno == ERANGE || val < 0 || val > 255) {
> -                               printf("Invalid value byte: %s\n",
> +                               error("Invalid value byte: %s",
>                                                                 argv[i]);
>                                 free(value);
>                                 return;
> @@ -843,32 +762,20 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str)
>                                                         offset, value, length,
>                                                         write_long_cb,
>                                                         NULL, NULL))
> -               printf("Failed to initiate long write procedure\n");
> +               error("Failed to initiate long write procedure");
>
>         free(value);
>  }
>
> -static void write_prepare_usage(void)
> -{
> -       printf("Usage: write-prepare [options] <value_handle> <offset> "
> -                               "<value>\n"
> -                               "Options:\n"
> -                               "\t-s, --session-id\tSession id\n"
> -                               "e.g.:\n"
> -                               "\twrite-prepare -s 1 0x0001 00 01 00\n");
> -}
> -
>  static struct option write_prepare_options[] = {
>         { "session-id",         1, 0, 's' },
> +       { "help", 0, 0, 'h' },
>         { }
>  };
>
> -static void cmd_write_prepare(struct client *cli, char *cmd_str)
> +static void cmd_write_prepare(int argc, char **argv)
>  {
>         int opt, i, val;
> -       char *argvbuf[516];
> -       char **argv = argvbuf;
> -       int argc = 0;
>         unsigned int id = 0;
>         uint16_t handle;
>         uint16_t offset;
> @@ -876,59 +783,50 @@ static void cmd_write_prepare(struct client *cli, char *cmd_str)
>         unsigned int length;
>         uint8_t *value = NULL;
>
> -       if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
> -               printf("Too many arguments\n");
> -               write_value_usage();
> -               return;
> -       }
> -
> -       /* Add command name for getopt_long */
> -       argc++;
> -       argv[0] = "write-prepare";
> -
> -       optind = 0;
>         while ((opt = getopt_long(argc, argv , "s:", write_prepare_options,
>                                                                 NULL)) != -1) {
>                 switch (opt) {
>                 case 's':
> -                       if (!optarg) {
> -                               write_prepare_usage();
> -                               return;
> -                       }
> -
>                         id = atoi(optarg);
> -
>                         break;
> +               case 'h':
> +                       bt_shell_usage();
> +                       optind = 0;
> +                       return bt_shell_noninteractive_quit(EXIT_SUCCESS);
>                 default:
> -                       write_prepare_usage();
> -                       return;
> +                       bt_shell_usage();
> +                       optind = 0;
> +                       return bt_shell_noninteractive_quit(EXIT_FAILURE);
>                 }
>         }
>
>         argc -= optind;
>         argv += optind;
> +       optind = 0;
>
> -       if (argc < 3) {
> -               write_prepare_usage();
> +       if (argc > 514) {
> +               error("Too many arguments");
> +               bt_shell_usage();
> +               optind = 0;
>                 return;
>         }
>
>         if (cli->reliable_session_id != id) {
> -               printf("Session id != Ongoing session id (%u!=%u)\n", id,
> +               error("Session id != Ongoing session id (%u!=%u)", id,
>                                                 cli->reliable_session_id);
>                 return;
>         }
>
>         handle = strtol(argv[0], &endptr, 0);
>         if (!endptr || *endptr != '\0' || !handle) {
> -               printf("Invalid handle: %s\n", argv[0]);
> +               error("Invalid handle: %s", argv[1]);
>                 return;
>         }
>
>         endptr = NULL;
>         offset = strtol(argv[1], &endptr, 0);
>         if (!endptr || *endptr != '\0' || errno == ERANGE) {
> -               printf("Invalid offset: %s\n", argv[1]);
> +               error("Invalid offset: %s", argv[2]);
>                 return;
>         }
>
> @@ -942,13 +840,13 @@ static void cmd_write_prepare(struct client *cli, char *cmd_str)
>                 goto done;
>
>         if (length > UINT16_MAX) {
> -               printf("Write value too long\n");
> +               error("Write value too long");
>                 return;
>         }
>
>         value = malloc(length);
>         if (!value) {
> -               printf("Failed to allocate memory for value\n");
> +               error("Failed to allocate memory for value");
>                 return;
>         }
>
> @@ -956,7 +854,7 @@ static void cmd_write_prepare(struct client *cli, char *cmd_str)
>                 val = strtol(argv[i], &endptr, 0);
>                 if (endptr == argv[i] || *endptr != '\0' || errno == ERANGE
>                                                 || val < 0 || val > 255) {
> -                       printf("Invalid value byte: %s\n", argv[i]);
> +                       error("Invalid value byte: %s", argv[i]);
>                         free(value);
>                         return;
>                 }
> @@ -971,64 +869,43 @@ done:
>                                                         write_long_cb, NULL,
>                                                         NULL);
>         if (!cli->reliable_session_id)
> -               printf("Failed to proceed prepare write\n");
> +               error("Failed to proceed prepare write");
>         else
> -               printf("Prepare write success.\n"
> -                               "Session id: %d to be used on next write\n",
> +               print("Prepare write success."
> +                               "Session id: %d to be used on next write",
>                                                 cli->reliable_session_id);
>
>         free(value);
>  }
>
> -static void write_execute_usage(void)
> -{
> -       printf("Usage: write-execute <session_id> <execute>\n"
> -                               "e.g.:\n"
> -                               "\twrite-execute 1 0\n");
> -}
> -
> -static void cmd_write_execute(struct client *cli, char *cmd_str)
> +static void cmd_write_execute(int argc, char **argv)
>  {
> -       char *argvbuf[516];
> -       char **argv = argvbuf;
> -       int argc = 0;
>         char *endptr = NULL;
>         unsigned int session_id;
>         bool execute;
>
> -       if (!parse_args(cmd_str, 514, argv, &argc)) {
> -               printf("Too many arguments\n");
> -               write_value_usage();
> -               return;
> -       }
> -
> -       if (argc < 2) {
> -               write_execute_usage();
> -               return;
> -       }
> -
> -       session_id = strtol(argv[0], &endptr, 0);
> +       session_id = strtol(argv[1], &endptr, 0);
>         if (!endptr || *endptr != '\0') {
> -               printf("Invalid session id: %s\n", argv[0]);
> +               error("Invalid session id: %s", argv[1]);
>                 return;
>         }
>
>         if (session_id != cli->reliable_session_id) {
> -               printf("Invalid session id: %u != %u\n", session_id,
> +               error("Invalid session id: %u != %u", session_id,
>                                                 cli->reliable_session_id);
>                 return;
>         }
>
> -       execute = !!strtol(argv[1], &endptr, 0);
> +       execute = !!strtol(argv[2], &endptr, 0);
>         if (!endptr || *endptr != '\0') {
> -               printf("Invalid execute: %s\n", argv[1]);
> +               error("Invalid execute: %s", argv[2]);
>                 return;
>         }
>
>         if (execute) {
>                 if (!bt_gatt_client_write_execute(cli->gatt, session_id,
>                                                         write_cb, NULL, NULL))
> -                       printf("Failed to proceed write execute\n");
> +                       error("Failed to proceed write execute");
>         } else {
>                 bt_gatt_client_cancel(cli->gatt, session_id);
>         }
> @@ -1036,46 +913,40 @@ static void cmd_write_execute(struct client *cli, char *cmd_str)
>         cli->reliable_session_id = 0;
>  }
>
> -static void register_notify_usage(void)
> -{
> -       printf("Usage: register-notify <chrc value handle>\n");
> -}
> -
>  static void notify_cb(uint16_t value_handle, const uint8_t *value,
>                                         uint16_t length, void *user_data)
>  {
>         int i;
> +       char line[MAX_LEN_LINE] = {0};
>
> -       printf("\n\tHandle Value Not/Ind: 0x%04x - ", value_handle);
> +       append(line, "\tHandle Value Not/Ind: 0x%04x - ", value_handle);
>
>         if (length == 0) {
> -               PRLOG("(0 bytes)\n");
> +               print("%s(0 bytes)", line);
>                 return;
>         }
>
> -       printf("(%u bytes): ", length);
> +       append(line, "(%u bytes): ", length);
>
>         for (i = 0; i < length; i++)
> -               printf("%02x ", value[i]);
> +               append(line, "%02x ", value[i]);
>
> -       PRLOG("\n");
> +       print("%s", line);
>  }
>
>  static void register_notify_cb(uint16_t att_ecode, void *user_data)
>  {
>         if (att_ecode) {
> -               PRLOG("Failed to register notify handler "
> -                                       "- error code: 0x%02x\n", att_ecode);
> +               error("Failed to register notify handler "
> +                                       "- error code: 0x%02x", att_ecode);
>                 return;
>         }
>
> -       PRLOG("Registered notify handler!\n");
> +       print("Registered notify handler!");
>  }
>
> -static void cmd_register_notify(struct client *cli, char *cmd_str)
> +static void cmd_register_notify(int argc, char **argv)
>  {
> -       char *argv[2];
> -       int argc = 0;
>         uint16_t value_handle;
>         unsigned int id;
>         char *endptr = NULL;
> @@ -1085,14 +956,9 @@ static void cmd_register_notify(struct client *cli, char *cmd_str)
>                 return;
>         }
>
> -       if (!parse_args(cmd_str, 1, argv, &argc) || argc != 1) {
> -               register_notify_usage();
> -               return;
> -       }
> -
> -       value_handle = strtol(argv[0], &endptr, 0);
> +       value_handle = strtol(argv[1], &endptr, 0);
>         if (!endptr || *endptr != '\0' || !value_handle) {
> -               printf("Invalid value handle: %s\n", argv[0]);
> +               error("Invalid value handle: %s", argv[1]);
>                 return;
>         }
>
> @@ -1100,22 +966,15 @@ static void cmd_register_notify(struct client *cli, char *cmd_str)
>                                                         register_notify_cb,
>                                                         notify_cb, NULL, NULL);
>         if (!id) {
> -               printf("Failed to register notify handler\n");
> +               error("Failed to register notify handler");
>                 return;
>         }
>
> -       printf("Registering notify handler with id: %u\n", id);
> +       print("Registering notify handler with id: %u", id);
>  }
>
> -static void unregister_notify_usage(void)
> +static void cmd_unregister_notify(int argc, char **argv)
>  {
> -       printf("Usage: unregister-notify <notify id>\n");
> -}
> -
> -static void cmd_unregister_notify(struct client *cli, char *cmd_str)
> -{
> -       char *argv[2];
> -       int argc = 0;
>         unsigned int id;
>         char *endptr = NULL;
>
> @@ -1124,72 +983,46 @@ static void cmd_unregister_notify(struct client *cli, char *cmd_str)
>                 return;
>         }
>
> -       if (!parse_args(cmd_str, 1, argv, &argc) || argc != 1) {
> -               unregister_notify_usage();
> -               return;
> -       }
> -
> -       id = strtol(argv[0], &endptr, 0);
> +       id = strtol(argv[1], &endptr, 0);
>         if (!endptr || *endptr != '\0' || !id) {
> -               printf("Invalid notify id: %s\n", argv[0]);
> +               error("Invalid notify id: %s", argv[1]);
>                 return;
>         }
>
>         if (!bt_gatt_client_unregister_notify(cli->gatt, id)) {
> -               printf("Failed to unregister notify handler with id: %u\n", id);
> +               error("Failed to unregister notify handler with id: %u", id);
>                 return;
>         }
>
> -       printf("Unregistered notify handler with id: %u\n", id);
> -}
> -
> -static void set_security_usage(void)
> -{
> -       printf("Usage: set-security <level>\n"
> -               "level: 1-3\n"
> -               "e.g.:\n"
> -               "\tset-security 2\n");
> +       print("Unregistered notify handler with id: %u", id);
>  }
>
> -static void cmd_set_security(struct client *cli, char *cmd_str)
> +static void cmd_set_security(int argc, char **argv)
>  {
> -       char *argv[2];
> -       int argc = 0;
>         char *endptr = NULL;
>         int level;
>
> -       if (!parse_args(cmd_str, 1, argv, &argc)) {
> -               printf("Too many arguments\n");
> -               set_security_usage();
> -               return;
> -       }
> -
> -       if (argc < 1) {
> -               set_security_usage();
> -               return;
> -       }
> -
> -       level = strtol(argv[0], &endptr, 0);
> +       level = strtol(argv[1], &endptr, 0);
>         if (!endptr || *endptr != '\0' || level < 1 || level > 3) {
> -               printf("Invalid level: %s\n", argv[0]);
> +               error("Invalid level: %s", argv[1]);
>                 return;
>         }
>
>         if (!bt_gatt_client_set_security(cli->gatt, level))
> -               printf("Could not set sec level\n");
> +               error("Could not set sec level");
>         else
> -               printf("Setting security level %d success\n", level);
> +               print("Setting security level %d success", level);
>  }
>
> -static void cmd_get_security(struct client *cli, char *cmd_str)
> +static void cmd_get_security(int argc, char **argv)
>  {
>         int level;
>
>         level = bt_gatt_client_get_security(cli->gatt);
>         if (level < 0)
> -               printf("Could not set sec level\n");
> +               error("Could not get sec level");
>         else
> -               printf("Security level: %u\n", level);
> +               print("Security level: %u", level);
>  }
>
>  static bool convert_sign_key(char *optarg, uint8_t key[16])
> @@ -1197,7 +1030,7 @@ static bool convert_sign_key(char *optarg, uint8_t key[16])
>         int i;
>
>         if (strlen(optarg) != 32) {
> -               printf("sign-key length is invalid\n");
> +               error("sign-key length is invalid");
>                 return false;
>         }
>
> @@ -1209,14 +1042,6 @@ static bool convert_sign_key(char *optarg, uint8_t key[16])
>         return true;
>  }
>
> -static void set_sign_key_usage(void)
> -{
> -       printf("Usage: set-sign-key [options]\nOptions:\n"
> -               "\t -c, --sign-key <csrk>\tCSRK\n"
> -               "e.g.:\n"
> -               "\tset-sign-key -c D8515948451FEA320DC05A2E88308188\n");
> -}
> -
>  static bool local_counter(uint32_t *sign_cnt, void *user_data)
>  {
>         static uint32_t cnt = 0;
> @@ -1226,141 +1051,74 @@ static bool local_counter(uint32_t *sign_cnt, void *user_data)
>         return true;
>  }
>
> -static void cmd_set_sign_key(struct client *cli, char *cmd_str)
> +static void cmd_set_sign_key(int argc, char **argv)
>  {
> -       char *argv[3];
> -       int argc = 0;
>         uint8_t key[16];
>
>         memset(key, 0, 16);
>
> -       if (!parse_args(cmd_str, 2, argv, &argc)) {
> -               set_sign_key_usage();
> -               return;
> -       }
> -
> -       if (argc != 2) {
> -               set_sign_key_usage();
> -               return;
> -       }
> -
> -       if (!strcmp(argv[0], "-c") || !strcmp(argv[0], "--sign-key")) {
> -               if (convert_sign_key(argv[1], key))
> +       if (!strcmp(argv[1], "-c") || !strcmp(argv[1], "--sign-key")) {
> +               if (convert_sign_key(argv[2], key))
>                         bt_att_set_local_key(cli->att, key, local_counter, cli);
> -       } else
> -               set_sign_key_usage();
> -}
> -
> -static void cmd_help(struct client *cli, char *cmd_str);
> -
> -typedef void (*command_func_t)(struct client *cli, char *cmd_str);
> -
> -static struct {
> -       char *cmd;
> -       command_func_t func;
> -       char *doc;
> -} command[] = {
> -       { "help", cmd_help, "\tDisplay help message" },
> -       { "services", cmd_services, "\tShow discovered services" },
> -       { "read-value", cmd_read_value,
> -                               "\tRead a characteristic or descriptor value" },
> -       { "read-long-value", cmd_read_long_value,
> -               "\tRead a long characteristic or desctriptor value" },
> -       { "read-multiple", cmd_read_multiple, "\tRead Multiple" },
> -       { "write-value", cmd_write_value,
> -                       "\tWrite a characteristic or descriptor value" },
> -       { "write-long-value", cmd_write_long_value,
> -                       "Write long characteristic or descriptor value" },
> -       { "write-prepare", cmd_write_prepare,
> -                       "\tWrite prepare characteristic or descriptor value" },
> -       { "write-execute", cmd_write_execute,
> -                       "\tExecute already prepared write" },
> -       { "register-notify", cmd_register_notify,
> -                       "\tSubscribe to not/ind from a characteristic" },
> -       { "unregister-notify", cmd_unregister_notify,
> -                                               "Unregister a not/ind session"},
> -       { "set-security", cmd_set_security,
> -                               "\tSet security level on le connection"},
> -       { "get-security", cmd_get_security,
> -                               "\tGet security level on le connection"},
> -       { "set-sign-key", cmd_set_sign_key,
> -                               "\tSet signing key for signed write command"},
> -       { }
> -};
> -
> -static void cmd_help(struct client *cli, char *cmd_str)
> -{
> -       int i;
> -
> -       printf("Commands:\n");
> -       for (i = 0; command[i].cmd; i++)
> -               printf("\t%-15s\t%s\n", command[i].cmd, command[i].doc);
> -}
> -
> -static void prompt_read_cb(int fd, uint32_t events, void *user_data)
> -{
> -       ssize_t read;
> -       size_t len = 0;
> -       char *line = NULL;
> -       char *cmd = NULL, *args;
> -       struct client *cli = user_data;
> -       int i;
> -
> -       if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
> -               mainloop_quit();
> -               return;
> -       }
> -
> -       read = getline(&line, &len, stdin);
> -       if (read < 0) {
> -               free(line);
> -               return;
> -       }
> -
> -       if (read <= 1) {
> -               cmd_help(cli, NULL);
> -               print_prompt();
> -               free(line);
> -               return;
> -       }
> -
> -       line[read-1] = '\0';
> -       args = line;
> -
> -       while ((cmd = strsep(&args, " \t")))
> -               if (*cmd != '\0')
> -                       break;
> -
> -       if (!cmd)
> -               goto failed;
> -
> -       for (i = 0; command[i].cmd; i++) {
> -               if (strcmp(command[i].cmd, cmd) == 0)
> -                       break;
> +       } else {
> +               bt_shell_usage();
> +               optind = 0;
>         }
> -
> -       if (command[i].cmd)
> -               command[i].func(cli, args);
> -       else
> -               fprintf(stderr, "Unknown command: %s\n", line);
> -
> -failed:
> -       print_prompt();
> -
> -       free(line);
>  }
>
> -static void signal_cb(int signum, void *user_data)
> -{
> -       switch (signum) {
> -       case SIGINT:
> -       case SIGTERM:
> -               mainloop_quit();
> -               break;
> -       default:
> -               break;
> -       }
> -}
> +static const struct bt_shell_menu main_menu = {
> +       .name = "main",
> +       .entries = {
> +       { "services", "[options...]", cmd_services,
> +               "Show discovered services\n"
> +               "Options:\n"
> +                       "\t -u, --uuid <uuid>\tService UUID\n"
> +                       "\t -a, --handle <handle>\tService start handle\n"
> +               "e.g.:\n"
> +                       "\tservices\n\tservices -u 0x180d\n\tservices -a 0x0009"
> +       },
> +       { "read-value", "<value_handle>",
> +               cmd_read_value, "Read a characteristic or descriptor value" },
> +       { "read-long-value", "<value_handle> <offset>",
> +               cmd_read_long_value, "Read a long characteristic or desctriptor value" },
> +       { "read-multiple", "<handles...>",
> +               cmd_read_multiple, "Read Multiple" },
> +       { "write-value", " [-w|-s] <value_handle> <value...>",
> +               cmd_write_value, "Write a characteristic or descriptor value\n"
> +               "Options:\n"
> +                       "\t-w, --without-response\tWrite without response\n"
> +                       "\t-s, --signed-write\tSigned write command\n"
> +               "e.g.:\n"
> +                       "\twrite-value 0x0001 00 01 00"
> +       },
> +       { "write-long-value", "[-r] <value_handle> <offset>",
> +               cmd_write_long_value, "Write long characteristic or descriptor value\n"
> +               "Options:\n"
> +                       "\t-r, --reliable-write\tReliable write\n"
> +               "e.g.:\n"
> +                       "\twrite-long-value 0x0001 0 00 01 00"
> +       },
> +       { "write-prepare", " [options...] <value_handle> <value>",
> +               cmd_write_prepare, "Write prepare characteristic or descriptor value\n"
> +               "Options:\n"
> +                       "\t-s, --session-id\tSession id\n"
> +               "e.g.:\n"
> +                       "\twrite-prepare -s 1 0x0001 00 01 00"
> +       },
> +       { "write-execute", " <session_id> <execute>",
> +               cmd_write_execute, "Execute already prepared write" },
> +       { "register-notify", "<chrc_value_handle>",
> +               cmd_register_notify, "Subscribe to not/ind from a characteristic" },
> +       { "unregister-notify", "<notify_id>",
> +               cmd_unregister_notify, "Unregister a not/ind session"},
> +       { "set-security", "<level 1-3>",
> +               cmd_set_security, "Set security level on connection"},
> +       { "get-security", NULL,
> +               cmd_get_security, "Get security level on connection"},
> +       { "set-sign-key", "<csrk>",
> +               cmd_set_sign_key, "Set signing key for signed write command"},
> +       {} },
> +};
>
>  static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
>                                                                         int sec)
> @@ -1375,15 +1133,15 @@ static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
>                 ba2str(src, srcaddr_str);
>                 ba2str(dst, dstaddr_str);
>
> -               printf("btgatt-client: Opening L2CAP %s connection on ATT "
> -                                       "channel:\n\t src: %s\n\tdest: %s\n",
> +               print("btgatt-client: Opening L2CAP %s connection on ATT "
> +                                       "channel:\n\t src: %s\n\tdest: %s",
>                                         (dst_type == BDADDR_BREDR ? "BR/EDR" : "LE"),
>                                         srcaddr_str, dstaddr_str);
>         }
>
>         sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
>         if (sock < 0) {
> -               perror("Failed to create L2CAP socket");
> +               error("Failed to create L2CAP socket");
>                 return -1;
>         }
>
> @@ -1398,7 +1156,7 @@ static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
>         bacpy(&srcaddr.l2_bdaddr, src);
>
>         if (bind(sock, (struct sockaddr *)&srcaddr, sizeof(srcaddr)) < 0) {
> -               perror("Failed to bind L2CAP socket");
> +               error("Failed to bind L2CAP socket");
>                 close(sock);
>                 return -1;
>         }
> @@ -1408,7 +1166,7 @@ static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
>         btsec.level = sec;
>         if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &btsec,
>                                                         sizeof(btsec)) != 0) {
> -               fprintf(stderr, "Failed to set L2CAP security level\n");
> +               error("Failed to set L2CAP security level");
>                 close(sock);
>                 return -1;
>         }
> @@ -1423,164 +1181,144 @@ static int l2cap_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
>         dstaddr.l2_bdaddr_type = dst_type;
>         bacpy(&dstaddr.l2_bdaddr, dst);
>
> -       printf("Connecting to device...");
> +       print("Connecting to device...");
>         fflush(stdout);
>
>         if (connect(sock, (struct sockaddr *) &dstaddr, sizeof(dstaddr)) < 0) {
> -               perror(" Failed to connect");
> +               error("Failed to connect");
>                 close(sock);
>                 return -1;
>         }
>
> -       printf(" Done\n");
> +       print("Done");
>
>         return sock;
>  }
>
> -static void usage(void)
> -{
> -       printf("btgatt-client\n");
> -       printf("Usage:\n\tbtgatt-client [options]\n");
> -
> -       printf("Options:\n"
> -               "\t-i, --index <id>\t\tSpecify adapter index, e.g. hci0\n"
> -               "\t-d, --dest <addr>\t\tSpecify the destination address\n"
> -               "\t-t, --type [random|public|bredr] \tSpecify the address type\n"
> -               "\t-m, --mtu <mtu> \t\tThe ATT MTU to use\n"
> -               "\t-s, --security-level <sec> \tSet security level (low|medium|"
> -                                                               "high|fips)\n"
> -               "\t-v, --verbose\t\t\tEnable extra logging\n"
> -               "\t-h, --help\t\t\tDisplay help\n");
> -}
> -
>  static struct option main_options[] = {
> -       { "index",              1, 0, 'i' },
> -       { "dest",               1, 0, 'd' },
> -       { "type",               1, 0, 't' },
> -       { "mtu",                1, 0, 'm' },
> -       { "security-level",     1, 0, 's' },
> -       { "verbose",            0, 0, 'v' },
> -       { "help",               0, 0, 'h' },
> +       { "index",     required_argument, NULL, 'i' },
> +       { "dst-addr",  required_argument, NULL, 'd' },
> +       { "type",      required_argument, NULL, 'T' },
> +       { "mtu",       required_argument, NULL, 'M' },
> +       { "sec-level", required_argument, NULL, 's' },
> +       { "verbose",   no_argument,       NULL, 'V' },
>         { }
>  };
>
> +static const char *index_option;
> +static const char *dst_addr_option;
> +static const char *type_option;
> +static const char *mtu_option;
> +static const char *security_level_option;
> +static const char *verbose_option;
> +
> +static const char **optargs[] = {
> +       &index_option,
> +       &dst_addr_option,
> +       &type_option,
> +       &mtu_option,
> +       &security_level_option,
> +       &verbose_option,
> +};
> +
> +static const char *help[] = {
> +       "Specify adapter index, e.g. hci0",
> +       "Specify the destination address",
> +       "Specify the address type (random|public|bredr)",
> +       "The ATT MTU to use",
> +       "Set security level (low|medium|high|fips)",
> +       "Enable extra logging"
> +};
> +
> +static const struct bt_shell_opt opt = {
> +       .options = main_options,
> +       .optno = sizeof(main_options) / sizeof(struct option),
> +       .optstr = "i:d:T:M:s:V",
> +       .optarg = optargs,
> +       .help = help,
> +};
> +
>  int main(int argc, char *argv[])
>  {
> -       int opt;
>         int sec = BT_SECURITY_LOW;
>         uint16_t mtu = 0;
>         uint8_t dst_type = BDADDR_LE_PUBLIC;
> -       bool dst_addr_given = false;
>         bdaddr_t src_addr, dst_addr;
>         int dev_id = -1;
>         int fd;
> -       struct client *cli;
> -
> -       while ((opt = getopt_long(argc, argv, "+hvs:m:t:d:i:",
> -                                               main_options, NULL)) != -1) {
> -               switch (opt) {
> -               case 'h':
> -                       usage();
> -                       return EXIT_SUCCESS;
> -               case 'v':
> -                       verbose = true;
> -                       break;
> -               case 's':
> -                       if (strcmp(optarg, "low") == 0)
> -                               sec = BT_SECURITY_LOW;
> -                       else if (strcmp(optarg, "medium") == 0)
> -                               sec = BT_SECURITY_MEDIUM;
> -                       else if (strcmp(optarg, "high") == 0)
> -                               sec = BT_SECURITY_HIGH;
> -                       else if (strcmp(optarg, "fips") == 0)
> -                               sec = BT_SECURITY_FIPS;
> -                       else {
> -                               fprintf(stderr, "Invalid security level\n");
> -                               return EXIT_FAILURE;
> -                       }
> -                       break;
> -               case 'm': {
> -                       int arg;
> -
> -                       arg = atoi(optarg);
> -                       if (arg <= 0) {
> -                               fprintf(stderr, "Invalid MTU: %d\n", arg);
> -                               return EXIT_FAILURE;
> -                       }
> -
> -                       if (arg > UINT16_MAX) {
> -                               fprintf(stderr, "MTU too large: %d\n", arg);
> -                               return EXIT_FAILURE;
> -                       }
> -
> -                       mtu = (uint16_t)arg;
> -                       break;
> +       int status;
> +
> +       bt_shell_init(argc, argv, &opt);
> +       bt_shell_set_menu(&main_menu);
> +
> +       if (verbose_option)
> +               verbose = true;
> +       if (security_level_option) {
> +               if (strcmp(security_level_option, "low") == 0)
> +                       sec = BT_SECURITY_LOW;
> +               else if (strcmp(security_level_option, "medium") == 0)
> +                       sec = BT_SECURITY_MEDIUM;
> +               else if (strcmp(security_level_option, "high") == 0)
> +                       sec = BT_SECURITY_HIGH;
> +               else if (strcmp(security_level_option, "fips") == 0)
> +                       sec = BT_SECURITY_FIPS;
> +               else {
> +                       error("Invalid security level");
> +                       return EXIT_FAILURE;
>                 }
> -               case 't':
> -                       if (strcmp(optarg, "random") == 0)
> -                               dst_type = BDADDR_LE_RANDOM;
> -                       else if (strcmp(optarg, "public") == 0)
> -                               dst_type = BDADDR_LE_PUBLIC;
> -                       else if (strcmp(optarg, "bredr") == 0)
> -                               dst_type = BDADDR_BREDR;
> -                       else {
> -                               fprintf(stderr,
> -                                       "Allowed types: random, public, bredr\n");
> -                               return EXIT_FAILURE;
> -                       }
> -                       break;
> -               case 'd':
> -                       if (str2ba(optarg, &dst_addr) < 0) {
> -                               fprintf(stderr, "Invalid remote address: %s\n",
> -                                                                       optarg);
> -                               return EXIT_FAILURE;
> -                       }
> +       }
> +       if (mtu_option) {
> +               int arg;
>
> -                       dst_addr_given = true;
> -                       break;
> +               arg = atoi(mtu_option);
> +               if (arg <= 0) {
> +                       error("Invalid MTU: %d", arg);
> +                       return EXIT_FAILURE;
> +               }
>
> -               case 'i':
> -                       dev_id = hci_devid(optarg);
> -                       if (dev_id < 0) {
> -                               perror("Invalid adapter");
> -                               return EXIT_FAILURE;
> -                       }
> +               if (arg > UINT16_MAX) {
> +                       error("MTU too large: %d", arg);
> +                       return EXIT_FAILURE;
> +               }
>
> -                       break;
> -               default:
> -                       fprintf(stderr, "Invalid option: %c\n", opt);
> +               mtu = (uint16_t)arg;
> +       }
> +       if (type_option) {
> +               if (strcmp(type_option, "random") == 0)
> +                       dst_type = BDADDR_LE_RANDOM;
> +               else if (strcmp(type_option, "public") == 0)
> +                       dst_type = BDADDR_LE_PUBLIC;
> +               else if (strcmp(type_option, "bredr") == 0)
> +                       dst_type = BDADDR_BREDR;
> +               else {
> +                       error("Allowed types: random, public, bredr");
>                         return EXIT_FAILURE;
>                 }
>         }
> -
> -       if (!argc) {
> -               usage();
> -               return EXIT_SUCCESS;
> +       if (dst_addr_option) {
> +               if (str2ba(dst_addr_option, &dst_addr) < 0) {
> +                       error("Invalid remote address: %s", dst_addr_option);
> +                       return EXIT_FAILURE;
> +               }
> +       } else {
> +               error("Destination address required!");
> +               return EXIT_FAILURE;
>         }
> -
> -       argc -= optind;
> -       argv += optind;
> -       optind = 0;
> -
> -       if (argc) {
> -               usage();
> -               return EXIT_SUCCESS;
> +       if (index_option) {
> +               dev_id = hci_devid(index_option);
> +               if (dev_id < 0) {
> +                       error("Invalid adapter");
> +                       return EXIT_FAILURE;
> +               }
>         }
>
>         if (dev_id == -1)
>                 bacpy(&src_addr, BDADDR_ANY);
>         else if (hci_devba(dev_id, &src_addr) < 0) {
> -               perror("Adapter not available");
> +               error("Adapter not available");
>                 return EXIT_FAILURE;
>         }
>
> -       if (!dst_addr_given) {
> -               fprintf(stderr, "Destination address required!\n");
> -               return EXIT_FAILURE;
> -       }
> -
> -       mainloop_init();
> -
>         fd = l2cap_att_connect(&src_addr, &dst_addr, dst_type, sec);
>         if (fd < 0)
>                 return EXIT_FAILURE;
> @@ -1591,20 +1329,15 @@ int main(int argc, char *argv[])
>                 return EXIT_FAILURE;
>         }
>
> -       if (mainloop_add_fd(fileno(stdin),
> -                               EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR,
> -                               prompt_read_cb, cli, NULL) < 0) {
> -               fprintf(stderr, "Failed to initialize console\n");
> -               return EXIT_FAILURE;
> -       }
> -
> -       print_prompt();
> -
> -       mainloop_run_with_signal(signal_cb, NULL);
> +       bt_shell_attach(fileno(stdin));
> +       update_prompt();
> +       shell_running = true;
> +       status = bt_shell_run();
> +       shell_running = false;
>
> -       printf("\n\nShutting down...\n");
> +       print("Shutting down...");
>
>         client_destroy(cli);
>
> -       return EXIT_SUCCESS;
> +       return status;
>  }
> --
> 2.34.1
>


-- 
Luiz Augusto von Dentz




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux