Commit 4ff78e0d authored by Anthony Liguori's avatar Anthony Liguori
Browse files

Merge remote-tracking branch 'luiz/queue/qmp' into staging

# By Wenchao Xia (15) and Stefan Weil (1)
# Via Luiz Capitulino
* luiz/queue/qmp:
  monitor: improve auto complete of "help" for single command in sub group
  monitor: allow "help" show message for single command in sub group
  monitor: support sub command in auto completion
  monitor: refine monitor_find_completion()
  monitor: support sub command in help
  monitor: refine parse_cmdline()
  monitor: code move for parse_cmdline()
  monitor: avoid direct use of global variable *mon_cmds
  monitor: split off monitor_data_init()
  monitor: call sortcmdlist() only one time
  monitor: avoid use of global *cur_mon in readline_completion()
  monitor: avoid use of global *cur_mon in monitor_find_completion()
  monitor: avoid use of global *cur_mon in block_completion_it()
  monitor: avoid use of global *cur_mon in file_completion()
  monitor: avoid use of global *cur_mon in cmd_completion()
  monitor: Add missing attributes to local function

Message-id: 1377865357-6742-1-git-send-email-lcapitulino@redhat.com
parents b95fdc0e 7ca0e061
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@ ETEXI

    {
        .name       = "help|?",
        .args_type  = "name:s?",
        .args_type  = "name:S?",
        .params     = "[cmd]",
        .help       = "show the help",
        .mhandler.cmd = do_help_cmd,
+2 −1
Original line number Diff line number Diff line
@@ -8,7 +8,8 @@
#define READLINE_MAX_COMPLETIONS 256

typedef void ReadLineFunc(Monitor *mon, const char *str, void *opaque);
typedef void ReadLineCompletionFunc(const char *cmdline);
typedef void ReadLineCompletionFunc(Monitor *mon,
                                    const char *cmdline);

typedef struct ReadLineState {
    char cmd_buf[READLINE_CMD_BUF_SIZE + 1];
+300 −162
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@
 * 'F'          filename
 * 'B'          block device name
 * 's'          string (accept optional quote)
 * 'S'          it just appends the rest of the string (accept optional quote)
 * 'O'          option string of the form NAME=VALUE,...
 *              parsed according to QemuOptsList given by its name
 *              Example: 'device:O' uses qemu_device_opts.
@@ -195,6 +196,7 @@ struct Monitor {
    CPUState *mon_cpu;
    BlockDriverCompletionFunc *password_completion_cb;
    void *password_opaque;
    mon_cmd_t *cmd_table;
    QError *error;
    QLIST_HEAD(,mon_fd_t) fds;
    QLIST_ENTRY(Monitor) entry;
@@ -683,14 +685,26 @@ static int do_qmp_capabilities(Monitor *mon, const QDict *params,

static void handle_user_command(Monitor *mon, const char *cmdline);

static void monitor_data_init(Monitor *mon)
{
    memset(mon, 0, sizeof(Monitor));
    mon->outbuf = qstring_new();
    /* Use *mon_cmds by default. */
    mon->cmd_table = mon_cmds;
}

static void monitor_data_destroy(Monitor *mon)
{
    QDECREF(mon->outbuf);
}

char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
                                int64_t cpu_index, Error **errp)
{
    char *output = NULL;
    Monitor *old_mon, hmp;

    memset(&hmp, 0, sizeof(hmp));
    hmp.outbuf = qstring_new();
    monitor_data_init(&hmp);
    hmp.skip_flush = true;

    old_mon = cur_mon;
@@ -716,7 +730,7 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
    }

out:
    QDECREF(hmp.outbuf);
    monitor_data_destroy(&hmp);
    return output;
}

@@ -740,35 +754,204 @@ static int compare_cmd(const char *name, const char *list)
    return 0;
}

static int get_str(char *buf, int buf_size, const char **pp)
{
    const char *p;
    char *q;
    int c;

    q = buf;
    p = *pp;
    while (qemu_isspace(*p)) {
        p++;
    }
    if (*p == '\0') {
    fail:
        *q = '\0';
        *pp = p;
        return -1;
    }
    if (*p == '\"') {
        p++;
        while (*p != '\0' && *p != '\"') {
            if (*p == '\\') {
                p++;
                c = *p++;
                switch (c) {
                case 'n':
                    c = '\n';
                    break;
                case 'r':
                    c = '\r';
                    break;
                case '\\':
                case '\'':
                case '\"':
                    break;
                default:
                    qemu_printf("unsupported escape code: '\\%c'\n", c);
                    goto fail;
                }
                if ((q - buf) < buf_size - 1) {
                    *q++ = c;
                }
            } else {
                if ((q - buf) < buf_size - 1) {
                    *q++ = *p;
                }
                p++;
            }
        }
        if (*p != '\"') {
            qemu_printf("unterminated string\n");
            goto fail;
        }
        p++;
    } else {
        while (*p != '\0' && !qemu_isspace(*p)) {
            if ((q - buf) < buf_size - 1) {
                *q++ = *p;
            }
            p++;
        }
    }
    *q = '\0';
    *pp = p;
    return 0;
}

#define MAX_ARGS 16

static void free_cmdline_args(char **args, int nb_args)
{
    int i;

    assert(nb_args <= MAX_ARGS);

    for (i = 0; i < nb_args; i++) {
        g_free(args[i]);
    }

}

/*
 * Parse the command line to get valid args.
 * @cmdline: command line to be parsed.
 * @pnb_args: location to store the number of args, must NOT be NULL.
 * @args: location to store the args, which should be freed by caller, must
 *        NOT be NULL.
 *
 * Returns 0 on success, negative on failure.
 *
 * NOTE: this parser is an approximate form of the real command parser. Number
 *       of args have a limit of MAX_ARGS. If cmdline contains more, it will
 *       return with failure.
 */
static int parse_cmdline(const char *cmdline,
                         int *pnb_args, char **args)
{
    const char *p;
    int nb_args, ret;
    char buf[1024];

    p = cmdline;
    nb_args = 0;
    for (;;) {
        while (qemu_isspace(*p)) {
            p++;
        }
        if (*p == '\0') {
            break;
        }
        if (nb_args >= MAX_ARGS) {
            goto fail;
        }
        ret = get_str(buf, sizeof(buf), &p);
        if (ret < 0) {
            goto fail;
        }
        args[nb_args] = g_strdup(buf);
        nb_args++;
    }
    *pnb_args = nb_args;
    return 0;

 fail:
    free_cmdline_args(args, nb_args);
    return -1;
}

static void help_cmd_dump_one(Monitor *mon,
                              const mon_cmd_t *cmd,
                              char **prefix_args,
                              int prefix_args_nb)
{
    int i;

    for (i = 0; i < prefix_args_nb; i++) {
        monitor_printf(mon, "%s ", prefix_args[i]);
    }
    monitor_printf(mon, "%s %s -- %s\n", cmd->name, cmd->params, cmd->help);
}

/* @args[@arg_index] is the valid command need to find in @cmds */
static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds,
                          const char *prefix, const char *name)
                          char **args, int nb_args, int arg_index)
{
    const mon_cmd_t *cmd;

    /* No valid arg need to compare with, dump all in *cmds */
    if (arg_index >= nb_args) {
        for (cmd = cmds; cmd->name != NULL; cmd++) {
            help_cmd_dump_one(mon, cmd, args, arg_index);
        }
        return;
    }

    /* Find one entry to dump */
    for (cmd = cmds; cmd->name != NULL; cmd++) {
        if (!name || !strcmp(name, cmd->name))
            monitor_printf(mon, "%s%s %s -- %s\n", prefix, cmd->name,
                           cmd->params, cmd->help);
        if (compare_cmd(args[arg_index], cmd->name)) {
            if (cmd->sub_table) {
                /* continue with next arg */
                help_cmd_dump(mon, cmd->sub_table,
                              args, nb_args, arg_index + 1);
            } else {
                help_cmd_dump_one(mon, cmd, args, arg_index);
            }
            break;
        }
    }
}

static void help_cmd(Monitor *mon, const char *name)
{
    if (name && !strcmp(name, "info")) {
        help_cmd_dump(mon, info_cmds, "info ", NULL);
    } else {
        help_cmd_dump(mon, mon_cmds, "", name);
        if (name && !strcmp(name, "log")) {
    char *args[MAX_ARGS];
    int nb_args = 0;

    /* 1. parse user input */
    if (name) {
        /* special case for log, directly dump and return */
        if (!strcmp(name, "log")) {
            const QEMULogItem *item;
            monitor_printf(mon, "Log items (comma separated):\n");
            monitor_printf(mon, "%-10s %s\n", "none", "remove all logs");
            for (item = qemu_log_items; item->mask != 0; item++) {
                monitor_printf(mon, "%-10s %s\n", item->name, item->help);
            }
            return;
        }

        if (parse_cmdline(name, &nb_args, args) < 0) {
            return;
        }
    }

    /* 2. dump the contents according to parsed args */
    help_cmd_dump(mon, mon->cmd_table, args, nb_args, 0);

    free_cmdline_args(args, nb_args);
}

static void do_help_cmd(Monitor *mon, const QDict *qdict)
{
    help_cmd(mon, qdict_get_try_str(qdict, "name"));
@@ -3171,7 +3354,8 @@ static const MonitorDef monitor_defs[] = {
    { NULL },
};

static void expr_error(Monitor *mon, const char *fmt, ...)
static void GCC_FMT_ATTR(2, 3) QEMU_NORETURN
expr_error(Monitor *mon, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
@@ -3420,71 +3604,6 @@ static int get_double(Monitor *mon, double *pval, const char **pp)
    return 0;
}

static int get_str(char *buf, int buf_size, const char **pp)
{
    const char *p;
    char *q;
    int c;

    q = buf;
    p = *pp;
    while (qemu_isspace(*p))
        p++;
    if (*p == '\0') {
    fail:
        *q = '\0';
        *pp = p;
        return -1;
    }
    if (*p == '\"') {
        p++;
        while (*p != '\0' && *p != '\"') {
            if (*p == '\\') {
                p++;
                c = *p++;
                switch(c) {
                case 'n':
                    c = '\n';
                    break;
                case 'r':
                    c = '\r';
                    break;
                case '\\':
                case '\'':
                case '\"':
                    break;
                default:
                    qemu_printf("unsupported escape code: '\\%c'\n", c);
                    goto fail;
                }
                if ((q - buf) < buf_size - 1) {
                    *q++ = c;
                }
            } else {
                if ((q - buf) < buf_size - 1) {
                    *q++ = *p;
                }
                p++;
            }
        }
        if (*p != '\"') {
            qemu_printf("unterminated string\n");
            goto fail;
        }
        p++;
    } else {
        while (*p != '\0' && !qemu_isspace(*p)) {
            if ((q - buf) < buf_size - 1) {
                *q++ = *p;
            }
            p++;
        }
    }
    *q = '\0';
    *pp = p;
    return 0;
}

/*
 * Store the command-name in cmdname, and return a pointer to
 * the remaining of the command string.
@@ -3541,8 +3660,6 @@ static char *key_get_info(const char *type, char **key)
static int default_fmt_format = 'x';
static int default_fmt_size = 4;

#define MAX_ARGS 16

static int is_valid_option(const char *c, const char *typestr)
{
    char option[3];
@@ -3931,6 +4048,31 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon,
                }
            }
            break;
        case 'S':
            {
                /* package all remaining string */
                int len;

                while (qemu_isspace(*p)) {
                    p++;
                }
                if (*typestr == '?') {
                    typestr++;
                    if (*p == '\0') {
                        /* no remaining string: NULL argument */
                        break;
                    }
                }
                len = strlen(p);
                if (len <= 0) {
                    monitor_printf(mon, "%s: string expected\n",
                                   cmdname);
                    break;
                }
                qdict_put(qdict, key, qstring_from_str(p));
                p += len;
            }
            break;
        default:
        bad_type:
            monitor_printf(mon, "%s: unknown type '%c'\n", cmdname, c);
@@ -3984,7 +4126,7 @@ static void handle_user_command(Monitor *mon, const char *cmdline)

    qdict = qdict_new();

    cmd = monitor_parse_command(mon, cmdline, 0, mon_cmds, qdict);
    cmd = monitor_parse_command(mon, cmdline, 0, mon->cmd_table, qdict);
    if (!cmd)
        goto out;

@@ -4008,7 +4150,7 @@ out:
    QDECREF(qdict);
}

static void cmd_completion(const char *name, const char *list)
static void cmd_completion(Monitor *mon, const char *name, const char *list)
{
    const char *p, *pstart;
    char cmd[128];
@@ -4026,7 +4168,7 @@ static void cmd_completion(const char *name, const char *list)
        memcpy(cmd, pstart, len);
        cmd[len] = '\0';
        if (name[0] == '\0' || !strncmp(name, cmd, strlen(name))) {
            readline_add_completion(cur_mon->rs, cmd);
            readline_add_completion(mon->rs, cmd);
        }
        if (*p == '\0')
            break;
@@ -4034,7 +4176,7 @@ static void cmd_completion(const char *name, const char *list)
    }
}

static void file_completion(const char *input)
static void file_completion(Monitor *mon, const char *input)
{
    DIR *ffs;
    struct dirent *d;
@@ -4057,7 +4199,7 @@ static void file_completion(const char *input)
        pstrcpy(file_prefix, sizeof(file_prefix), p + 1);
    }
#ifdef DEBUG_COMPLETION
    monitor_printf(cur_mon, "input='%s' path='%s' prefix='%s'\n",
    monitor_printf(mon, "input='%s' path='%s' prefix='%s'\n",
                   input, path, file_prefix);
#endif
    ffs = opendir(path);
@@ -4084,47 +4226,28 @@ static void file_completion(const char *input)
            if (stat(file, &sb) == 0 && S_ISDIR(sb.st_mode)) {
                pstrcat(file, sizeof(file), "/");
            }
            readline_add_completion(cur_mon->rs, file);
            readline_add_completion(mon->rs, file);
        }
    }
    closedir(ffs);
}

typedef struct MonitorBlockComplete {
    Monitor *mon;
    const char *input;
} MonitorBlockComplete;

static void block_completion_it(void *opaque, BlockDriverState *bs)
{
    const char *name = bdrv_get_device_name(bs);
    const char *input = opaque;
    MonitorBlockComplete *mbc = opaque;
    Monitor *mon = mbc->mon;
    const char *input = mbc->input;

    if (input[0] == '\0' ||
        !strncmp(name, (char *)input, strlen(input))) {
        readline_add_completion(cur_mon->rs, name);
    }
}

/* NOTE: this parser is an approximate form of the real command parser */
static void parse_cmdline(const char *cmdline,
                         int *pnb_args, char **args)
{
    const char *p;
    int nb_args, ret;
    char buf[1024];

    p = cmdline;
    nb_args = 0;
    for(;;) {
        while (qemu_isspace(*p))
            p++;
        if (*p == '\0')
            break;
        if (nb_args >= MAX_ARGS)
            break;
        ret = get_str(buf, sizeof(buf), &p);
        args[nb_args] = g_strdup(buf);
        nb_args++;
        if (ret < 0)
            break;
        readline_add_completion(mon->rs, name);
    }
    *pnb_args = nb_args;
}

static const char *next_arg_type(const char *typestr)
@@ -4133,49 +4256,42 @@ static const char *next_arg_type(const char *typestr)
    return (p != NULL ? ++p : typestr);
}

static void monitor_find_completion(const char *cmdline)
static void monitor_find_completion_by_table(Monitor *mon,
                                             const mon_cmd_t *cmd_table,
                                             char **args,
                                             int nb_args)
{
    const char *cmdname;
    char *args[MAX_ARGS];
    int nb_args, i, len;
    int i;
    const char *ptype, *str;
    const mon_cmd_t *cmd;
    MonitorBlockComplete mbs;

    parse_cmdline(cmdline, &nb_args, args);
#ifdef DEBUG_COMPLETION
    for(i = 0; i < nb_args; i++) {
        monitor_printf(cur_mon, "arg%d = '%s'\n", i, (char *)args[i]);
    }
#endif

    /* if the line ends with a space, it means we want to complete the
       next arg */
    len = strlen(cmdline);
    if (len > 0 && qemu_isspace(cmdline[len - 1])) {
        if (nb_args >= MAX_ARGS) {
            goto cleanup;
        }
        args[nb_args++] = g_strdup("");
    }
    if (nb_args <= 1) {
        /* command completion */
        if (nb_args == 0)
            cmdname = "";
        else
            cmdname = args[0];
        readline_set_completion_index(cur_mon->rs, strlen(cmdname));
        for(cmd = mon_cmds; cmd->name != NULL; cmd++) {
            cmd_completion(cmdname, cmd->name);
        readline_set_completion_index(mon->rs, strlen(cmdname));
        for (cmd = cmd_table; cmd->name != NULL; cmd++) {
            cmd_completion(mon, cmdname, cmd->name);
        }
    } else {
        /* find the command */
        for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
        for (cmd = cmd_table; cmd->name != NULL; cmd++) {
            if (compare_cmd(args[0], cmd->name)) {
                break;
            }
        }
        if (!cmd->name) {
            goto cleanup;
            return;
        }

        if (cmd->sub_table) {
            /* do the job again */
            return monitor_find_completion_by_table(mon, cmd->sub_table,
                                                    &args[1], nb_args - 1);
        }

        ptype = next_arg_type(cmd->args_type);
@@ -4193,45 +4309,68 @@ static void monitor_find_completion(const char *cmdline)
        switch(*ptype) {
        case 'F':
            /* file completion */
            readline_set_completion_index(cur_mon->rs, strlen(str));
            file_completion(str);
            readline_set_completion_index(mon->rs, strlen(str));
            file_completion(mon, str);
            break;
        case 'B':
            /* block device name completion */
            readline_set_completion_index(cur_mon->rs, strlen(str));
            bdrv_iterate(block_completion_it, (void *)str);
            mbs.mon = mon;
            mbs.input = str;
            readline_set_completion_index(mon->rs, strlen(str));
            bdrv_iterate(block_completion_it, &mbs);
            break;
        case 's':
            /* XXX: more generic ? */
            if (!strcmp(cmd->name, "info")) {
                readline_set_completion_index(cur_mon->rs, strlen(str));
                for(cmd = info_cmds; cmd->name != NULL; cmd++) {
                    cmd_completion(str, cmd->name);
                }
            } else if (!strcmp(cmd->name, "sendkey")) {
        case 'S':
            if (!strcmp(cmd->name, "sendkey")) {
                char *sep = strrchr(str, '-');
                if (sep)
                    str = sep + 1;
                readline_set_completion_index(cur_mon->rs, strlen(str));
                readline_set_completion_index(mon->rs, strlen(str));
                for (i = 0; i < Q_KEY_CODE_MAX; i++) {
                    cmd_completion(str, QKeyCode_lookup[i]);
                    cmd_completion(mon, str, QKeyCode_lookup[i]);
                }
            } else if (!strcmp(cmd->name, "help|?")) {
                readline_set_completion_index(cur_mon->rs, strlen(str));
                for (cmd = mon_cmds; cmd->name != NULL; cmd++) {
                    cmd_completion(str, cmd->name);
                }
                monitor_find_completion_by_table(mon, cmd_table,
                                                 &args[1], nb_args - 1);
            }
            break;
        default:
            break;
        }
    }
}

cleanup:
static void monitor_find_completion(Monitor *mon,
                                    const char *cmdline)
{
    char *args[MAX_ARGS];
    int nb_args, len;

    /* 1. parse the cmdline */
    if (parse_cmdline(cmdline, &nb_args, args) < 0) {
        return;
    }
#ifdef DEBUG_COMPLETION
    for (i = 0; i < nb_args; i++) {
        g_free(args[i]);
        monitor_printf(mon, "arg%d = '%s'\n", i, args[i]);
    }
#endif

    /* if the line ends with a space, it means we want to complete the
       next arg */
    len = strlen(cmdline);
    if (len > 0 && qemu_isspace(cmdline[len - 1])) {
        if (nb_args >= MAX_ARGS) {
            goto cleanup;
        }
        args[nb_args++] = g_strdup("");
    }

    /* 2. auto complete according to args */
    monitor_find_completion_by_table(mon, mon->cmd_table, args, nb_args);

cleanup:
    free_cmdline_args(args, nb_args);
}

static int monitor_can_read(void *opaque)
@@ -4751,11 +4890,12 @@ void monitor_init(CharDriverState *chr, int flags)

    if (is_first_init) {
        monitor_protocol_event_init();
        sortcmdlist();
        is_first_init = 0;
    }

    mon = g_malloc0(sizeof(*mon));
    mon->outbuf = qstring_new();
    mon = g_malloc(sizeof(*mon));
    monitor_data_init(mon);

    mon->chr = chr;
    mon->flags = flags;
@@ -4780,8 +4920,6 @@ void monitor_init(CharDriverState *chr, int flags)
    QLIST_INSERT_HEAD(&mon_list, mon, entry);
    if (!default_mon || (flags & MONITOR_IS_DEFAULT))
        default_mon = mon;

    sortcmdlist();
}

static void bdrv_password_cb(Monitor *mon, const char *password, void *opaque)
+2 −3
Original line number Diff line number Diff line
@@ -276,7 +276,6 @@ void readline_set_completion_index(ReadLineState *rs, int index)

static void readline_completion(ReadLineState *rs)
{
    Monitor *mon = cur_mon;
    int len, i, j, max_width, nb_cols, max_prefix;
    char *cmdline;

@@ -285,7 +284,7 @@ static void readline_completion(ReadLineState *rs)
    cmdline = g_malloc(rs->cmd_buf_index + 1);
    memcpy(cmdline, rs->cmd_buf, rs->cmd_buf_index);
    cmdline[rs->cmd_buf_index] = '\0';
    rs->completion_finder(cmdline);
    rs->completion_finder(rs->mon, cmdline);
    g_free(cmdline);

    /* no completion found */
@@ -300,7 +299,7 @@ static void readline_completion(ReadLineState *rs)
        if (len > 0 && rs->completions[0][len - 1] != '/')
            readline_insert_char(rs, ' ');
    } else {
        monitor_printf(mon, "\n");
        monitor_printf(rs->mon, "\n");
        max_width = 0;
        max_prefix = 0;	
        for(i = 0; i < rs->nb_completions; i++) {