diff options
author | Anthony Liguori <aliguori@us.ibm.com> | 2010-07-06 08:31:17 -0500 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2010-07-06 08:31:17 -0500 |
commit | 02d0ba1420803562109185f47be6f7430bfdefae (patch) | |
tree | 7d7185e9edd5bcf30ea38760d2a7f12fd21bb1c0 /monitor.c | |
parent | 9f5a1fae7ee1a7c66462e5b8e9d21552d4dc5027 (diff) | |
parent | a6c4d36425871fafc55ce3937bebd05e86f5ea81 (diff) |
Merge remote branch 'qmp/for-anthony' into staging
Diffstat (limited to 'monitor.c')
-rw-r--r-- | monitor.c | 435 |
1 files changed, 255 insertions, 180 deletions
@@ -113,7 +113,7 @@ typedef struct mon_cmd_t { int (*cmd_async)(Monitor *mon, const QDict *params, MonitorCompletion *cb, void *opaque); } mhandler; - int async; + int flags; } mon_cmd_t; /* file descriptors passed via SCM_RIGHTS */ @@ -178,6 +178,9 @@ static inline void mon_print_count_init(Monitor *mon) { } static inline int mon_print_count_get(const Monitor *mon) { return 0; } #endif /* CONFIG_DEBUG_MONITOR */ +/* QMP checker flags */ +#define QMP_ACCEPT_UNKNOWNS 1 + static QLIST_HEAD(mon_list, Monitor) mon_list; static const mon_cmd_t mon_cmds[]; @@ -328,7 +331,12 @@ static inline int monitor_handler_ported(const mon_cmd_t *cmd) static inline bool monitor_handler_is_async(const mon_cmd_t *cmd) { - return cmd->async != 0; + return cmd->flags & MONITOR_CMD_ASYNC; +} + +static inline bool monitor_cmd_user_only(const mon_cmd_t *cmd) +{ + return (cmd->flags & MONITOR_CMD_USER_ONLY); } static inline int monitor_has_error(const Monitor *mon) @@ -547,10 +555,10 @@ static void qmp_monitor_complete(void *opaque, QObject *ret_data) monitor_protocol_emitter(opaque, ret_data); } -static void qmp_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd, - const QDict *params) +static int qmp_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd, + const QDict *params) { - cmd->mhandler.cmd_async(mon, params, qmp_monitor_complete, mon); + return cmd->mhandler.cmd_async(mon, params, qmp_monitor_complete, mon); } static void qmp_async_info_handler(Monitor *mon, const mon_cmd_t *cmd) @@ -613,6 +621,11 @@ static int do_info(Monitor *mon, const QDict *qdict, QObject **ret_data) goto help; } + if (monitor_ctrl_mode(mon) && monitor_cmd_user_only(cmd)) { + qerror_report(QERR_COMMAND_NOT_FOUND, item); + return -1; + } + if (monitor_handler_is_async(cmd)) { if (monitor_ctrl_mode(mon)) { qmp_async_info_handler(mon, cmd); @@ -710,13 +723,14 @@ static void do_info_commands(Monitor *mon, QObject **ret_data) cmd_list = qlist_new(); for (cmd = mon_cmds; cmd->name != NULL; cmd++) { - if (monitor_handler_ported(cmd) && !compare_cmd(cmd->name, "info")) { + if (monitor_handler_ported(cmd) && !monitor_cmd_user_only(cmd) && + !compare_cmd(cmd->name, "info")) { qlist_append_obj(cmd_list, get_cmd_dict(cmd->name)); } } for (cmd = info_cmds; cmd->name != NULL; cmd++) { - if (monitor_handler_ported(cmd)) { + if (monitor_handler_ported(cmd) && !monitor_cmd_user_only(cmd)) { char buf[128]; snprintf(buf, sizeof(buf), "query-%s", cmd->name); qlist_append_obj(cmd_list, get_cmd_dict(buf)); @@ -2537,7 +2551,7 @@ static const mon_cmd_t info_cmds[] = { .help = "show balloon information", .user_print = monitor_print_balloon, .mhandler.info_async = do_info_balloon, - .async = 1, + .flags = MONITOR_CMD_ASYNC, }, { .name = "qtree", @@ -3566,7 +3580,7 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon, case '-': { const char *tmp = p; - int has_option, skip_key = 0; + int skip_key = 0; /* option */ c = *typestr++; @@ -3574,7 +3588,6 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon, goto bad_type; while (qemu_isspace(*p)) p++; - has_option = 0; if (*p == '-') { p++; if(c != *p) { @@ -3590,11 +3603,11 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon, if(skip_key) { p = tmp; } else { + /* has option */ p++; - has_option = 1; + qdict_put(qdict, key, qbool_from_int(1)); } } - qdict_put(qdict, key, qint_from_int(has_option)); } break; default: @@ -3883,8 +3896,9 @@ static void monitor_find_completion(const char *cmdline) next arg */ len = strlen(cmdline); if (len > 0 && qemu_isspace(cmdline[len - 1])) { - if (nb_args >= MAX_ARGS) - return; + if (nb_args >= MAX_ARGS) { + goto cleanup; + } args[nb_args++] = qemu_strdup(""); } if (nb_args <= 1) { @@ -3899,12 +3913,15 @@ static void monitor_find_completion(const char *cmdline) } } else { /* find the command */ - for(cmd = mon_cmds; cmd->name != NULL; cmd++) { - if (compare_cmd(args[0], cmd->name)) - goto found; + for (cmd = mon_cmds; cmd->name != NULL; cmd++) { + if (compare_cmd(args[0], cmd->name)) { + break; + } } - return; - found: + if (!cmd->name) { + goto cleanup; + } + ptype = next_arg_type(cmd->args_type); for(i = 0; i < nb_args - 2; i++) { if (*ptype != '\0') { @@ -3915,7 +3932,7 @@ static void monitor_find_completion(const char *cmdline) } str = args[nb_args - 1]; if (*ptype == '-' && ptype[1] != '\0') { - ptype += 2; + ptype = next_arg_type(ptype); } switch(*ptype) { case 'F': @@ -3954,8 +3971,11 @@ static void monitor_find_completion(const char *cmdline) break; } } - for(i = 0; i < nb_args; i++) + +cleanup: + for (i = 0; i < nb_args; i++) { qemu_free(args[i]); + } } static int monitor_can_read(void *opaque) @@ -3965,191 +3985,256 @@ static int monitor_can_read(void *opaque) return (mon->suspend_cnt == 0) ? 1 : 0; } -typedef struct CmdArgs { - QString *name; - int type; - int flag; - int optional; -} CmdArgs; - -static int check_opt(const CmdArgs *cmd_args, const char *name, QDict *args) +static int invalid_qmp_mode(const Monitor *mon, const char *cmd_name) { - if (!cmd_args->optional) { - qerror_report(QERR_MISSING_PARAMETER, name); - return -1; - } - - if (cmd_args->type == '-') { - /* handlers expect a value, they need to be changed */ - qdict_put(args, name, qint_from_int(0)); - } - - return 0; + int is_cap = compare_cmd(cmd_name, "qmp_capabilities"); + return (qmp_cmd_mode(mon) ? is_cap : !is_cap); } -static int check_arg(const CmdArgs *cmd_args, QDict *args) +/* + * Argument validation rules: + * + * 1. The argument must exist in cmd_args qdict + * 2. The argument type must be the expected one + * + * Special case: If the argument doesn't exist in cmd_args and + * the QMP_ACCEPT_UNKNOWNS flag is set, then the + * checking is skipped for it. + */ +static int check_client_args_type(const QDict *client_args, + const QDict *cmd_args, int flags) { - QObject *value; - const char *name; + const QDictEntry *ent; - name = qstring_get_str(cmd_args->name); - - if (!args) { - return check_opt(cmd_args, name, args); - } + for (ent = qdict_first(client_args); ent;ent = qdict_next(client_args,ent)){ + QObject *obj; + QString *arg_type; + const QObject *client_arg = qdict_entry_value(ent); + const char *client_arg_name = qdict_entry_key(ent); + + obj = qdict_get(cmd_args, client_arg_name); + if (!obj) { + if (flags & QMP_ACCEPT_UNKNOWNS) { + /* handler accepts unknowns */ + continue; + } + /* client arg doesn't exist */ + qerror_report(QERR_INVALID_PARAMETER, client_arg_name); + return -1; + } - value = qdict_get(args, name); - if (!value) { - return check_opt(cmd_args, name, args); - } + arg_type = qobject_to_qstring(obj); + assert(arg_type != NULL); - switch (cmd_args->type) { + /* check if argument's type is correct */ + switch (qstring_get_str(arg_type)[0]) { case 'F': case 'B': case 's': - if (qobject_type(value) != QTYPE_QSTRING) { - qerror_report(QERR_INVALID_PARAMETER_TYPE, name, "string"); + if (qobject_type(client_arg) != QTYPE_QSTRING) { + qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name, + "string"); return -1; } - break; - case '/': { - int i; - const char *keys[] = { "count", "format", "size", NULL }; - - for (i = 0; keys[i]; i++) { - QObject *obj = qdict_get(args, keys[i]); - if (!obj) { - qerror_report(QERR_MISSING_PARAMETER, name); - return -1; - } - if (qobject_type(obj) != QTYPE_QINT) { - qerror_report(QERR_INVALID_PARAMETER_TYPE, name, "int"); - return -1; - } - } - break; - } + break; case 'i': case 'l': case 'M': - if (qobject_type(value) != QTYPE_QINT) { - qerror_report(QERR_INVALID_PARAMETER_TYPE, name, "int"); - return -1; + if (qobject_type(client_arg) != QTYPE_QINT) { + qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name, + "int"); + return -1; } break; case 'f': case 'T': - if (qobject_type(value) != QTYPE_QINT && qobject_type(value) != QTYPE_QFLOAT) { - qerror_report(QERR_INVALID_PARAMETER_TYPE, name, "number"); - return -1; + if (qobject_type(client_arg) != QTYPE_QINT && + qobject_type(client_arg) != QTYPE_QFLOAT) { + qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name, + "number"); + return -1; } break; case 'b': - if (qobject_type(value) != QTYPE_QBOOL) { - qerror_report(QERR_INVALID_PARAMETER_TYPE, name, "bool"); - return -1; - } - break; case '-': - if (qobject_type(value) != QTYPE_QINT && - qobject_type(value) != QTYPE_QBOOL) { - qerror_report(QERR_INVALID_PARAMETER_TYPE, name, "bool"); - return -1; - } - if (qobject_type(value) == QTYPE_QBOOL) { - /* handlers expect a QInt, they need to be changed */ - qdict_put(args, name, - qint_from_int(qbool_get_int(qobject_to_qbool(value)))); + if (qobject_type(client_arg) != QTYPE_QBOOL) { + qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name, + "bool"); + return -1; } break; case 'O': + assert(flags & QMP_ACCEPT_UNKNOWNS); + break; + case '/': + case '.': + /* + * These types are not supported by QMP and thus are not + * handled here. Fall through. + */ default: - /* impossible */ abort(); + } + } + + return 0; +} + +/* + * - Check if the client has passed all mandatory args + * - Set special flags for argument validation + */ +static int check_mandatory_args(const QDict *cmd_args, + const QDict *client_args, int *flags) +{ + const QDictEntry *ent; + + for (ent = qdict_first(cmd_args); ent; ent = qdict_next(cmd_args, ent)) { + const char *cmd_arg_name = qdict_entry_key(ent); + QString *type = qobject_to_qstring(qdict_entry_value(ent)); + assert(type != NULL); + + if (qstring_get_str(type)[0] == 'O') { + assert((*flags & QMP_ACCEPT_UNKNOWNS) == 0); + *flags |= QMP_ACCEPT_UNKNOWNS; + } else if (qstring_get_str(type)[0] != '-' && + qstring_get_str(type)[1] != '?' && + !qdict_haskey(client_args, cmd_arg_name)) { + qerror_report(QERR_MISSING_PARAMETER, cmd_arg_name); + return -1; + } } return 0; } -static void cmd_args_init(CmdArgs *cmd_args) +static QDict *qdict_from_args_type(const char *args_type) { - cmd_args->name = qstring_new(); - cmd_args->type = cmd_args->flag = cmd_args->optional = 0; + int i; + QDict *qdict; + QString *key, *type, *cur_qs; + + assert(args_type != NULL); + + qdict = qdict_new(); + + if (args_type == NULL || args_type[0] == '\0') { + /* no args, empty qdict */ + goto out; + } + + key = qstring_new(); + type = qstring_new(); + + cur_qs = key; + + for (i = 0;; i++) { + switch (args_type[i]) { + case ',': + case '\0': + qdict_put(qdict, qstring_get_str(key), type); + QDECREF(key); + if (args_type[i] == '\0') { + goto out; + } + type = qstring_new(); /* qdict has ref */ + cur_qs = key = qstring_new(); + break; + case ':': + cur_qs = type; + break; + default: + qstring_append_chr(cur_qs, args_type[i]); + break; + } + } + +out: + return qdict; } -static int check_opts(QemuOptsList *opts_list, QDict *args) +/* + * Client argument checking rules: + * + * 1. Client must provide all mandatory arguments + * 2. Each argument provided by the client must be expected + * 3. Each argument provided by the client must have the type expected + * by the command + */ +static int qmp_check_client_args(const mon_cmd_t *cmd, QDict *client_args) { - assert(!opts_list->desc->name); - return 0; + int flags, err; + QDict *cmd_args; + + cmd_args = qdict_from_args_type(cmd->args_type); + + flags = 0; + err = check_mandatory_args(cmd_args, client_args, &flags); + if (err) { + goto out; + } + + err = check_client_args_type(client_args, cmd_args, flags); + +out: + QDECREF(cmd_args); + return err; } /* - * This is not trivial, we have to parse Monitor command's argument - * type syntax to be able to check the arguments provided by clients. + * Input object checking rules * - * In the near future we will be using an array for that and will be - * able to drop all this parsing... + * 1. Input object must be a dict + * 2. The "execute" key must exist + * 3. The "execute" key must be a string + * 4. If the "arguments" key exists, it must be a dict + * 5. If the "id" key exists, it can be anything (ie. json-value) + * 6. Any argument not listed above is considered invalid */ -static int monitor_check_qmp_args(const mon_cmd_t *cmd, QDict *args) +static QDict *qmp_check_input_obj(QObject *input_obj) { - int err; - const char *p; - CmdArgs cmd_args; - QemuOptsList *opts_list; + const QDictEntry *ent; + int has_exec_key = 0; + QDict *input_dict; - if (cmd->args_type == NULL) { - return (qdict_size(args) == 0 ? 0 : -1); + if (qobject_type(input_obj) != QTYPE_QDICT) { + qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "object"); + return NULL; } - err = 0; - cmd_args_init(&cmd_args); - opts_list = NULL; + input_dict = qobject_to_qdict(input_obj); - for (p = cmd->args_type;; p++) { - if (*p == ':') { - cmd_args.type = *++p; - p++; - if (cmd_args.type == '-') { - cmd_args.flag = *p++; - cmd_args.optional = 1; - } else if (cmd_args.type == 'O') { - opts_list = qemu_find_opts(qstring_get_str(cmd_args.name)); - assert(opts_list); - } else if (*p == '?') { - cmd_args.optional = 1; - p++; - } + for (ent = qdict_first(input_dict); ent; ent = qdict_next(input_dict, ent)){ + const char *arg_name = qdict_entry_key(ent); + const QObject *arg_obj = qdict_entry_value(ent); - assert(*p == ',' || *p == '\0'); - if (opts_list) { - err = check_opts(opts_list, args); - opts_list = NULL; - } else { - err = check_arg(&cmd_args, args); - QDECREF(cmd_args.name); - cmd_args_init(&cmd_args); + if (!strcmp(arg_name, "execute")) { + if (qobject_type(arg_obj) != QTYPE_QSTRING) { + qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute", + "string"); + return NULL; } - - if (err < 0) { - break; + has_exec_key = 1; + } else if (!strcmp(arg_name, "arguments")) { + if (qobject_type(arg_obj) != QTYPE_QDICT) { + qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "arguments", + "object"); + return NULL; } + } else if (!strcmp(arg_name, "id")) { + /* FIXME: check duplicated IDs for async commands */ } else { - qstring_append_chr(cmd_args.name, *p); - } - - if (*p == '\0') { - break; + qerror_report(QERR_QMP_EXTRA_MEMBER, arg_name); + return NULL; } } - QDECREF(cmd_args.name); - return err; -} + if (!has_exec_key) { + qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "execute"); + return NULL; + } -static int invalid_qmp_mode(const Monitor *mon, const char *cmd_name) -{ - int is_cap = compare_cmd(cmd_name, "qmp_capabilities"); - return (qmp_cmd_mode(mon) ? is_cap : !is_cap); + return input_dict; } static void handle_qmp_command(JSONMessageParser *parser, QList *tokens) @@ -4161,38 +4246,28 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens) Monitor *mon = cur_mon; const char *cmd_name, *info_item; - args = NULL; + args = input = NULL; obj = json_parser_parse(tokens, NULL); if (!obj) { // FIXME: should be triggered in json_parser_parse() qerror_report(QERR_JSON_PARSING); goto err_out; - } else if (qobject_type(obj) != QTYPE_QDICT) { - qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "object"); + } + + input = qmp_check_input_obj(obj); + if (!input) { qobject_decref(obj); goto err_out; } - input = qobject_to_qdict(obj); - mon->mc->id = qdict_get(input, "id"); qobject_incref(mon->mc->id); - obj = qdict_get(input, "execute"); - if (!obj) { - qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "execute"); - goto err_input; - } else if (qobject_type(obj) != QTYPE_QSTRING) { - qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute", "string"); - goto err_input; - } - - cmd_name = qstring_get_str(qobject_to_qstring(obj)); - + cmd_name = qdict_get_str(input, "execute"); if (invalid_qmp_mode(mon, cmd_name)) { qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name); - goto err_input; + goto err_out; } /* @@ -4201,49 +4276,49 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens) */ if (compare_cmd(cmd_name, "info")) { qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name); - goto err_input; + goto err_out; } else if (strstart(cmd_name, "query-", &info_item)) { cmd = monitor_find_command("info"); qdict_put_obj(input, "arguments", qobject_from_jsonf("{ 'item': %s }", info_item)); } else { cmd = monitor_find_command(cmd_name); - if (!cmd || !monitor_handler_ported(cmd)) { + if (!cmd || !monitor_handler_ported(cmd) + || monitor_cmd_user_only(cmd)) { qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name); - goto err_input; + goto err_out; } } obj = qdict_get(input, "arguments"); if (!obj) { args = qdict_new(); - } else if (qobject_type(obj) != QTYPE_QDICT) { - qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "arguments", "object"); - goto err_input; } else { args = qobject_to_qdict(obj); QINCREF(args); } - QDECREF(input); - - err = monitor_check_qmp_args(cmd, args); + err = qmp_check_client_args(cmd, args); if (err < 0) { goto err_out; } if (monitor_handler_is_async(cmd)) { - qmp_async_cmd_handler(mon, cmd, args); + err = qmp_async_cmd_handler(mon, cmd, args); + if (err) { + /* emit the error response */ + goto err_out; + } } else { monitor_call_handler(mon, cmd, args); } + goto out; -err_input: - QDECREF(input); err_out: monitor_protocol_emitter(mon, NULL); out: + QDECREF(input); QDECREF(args); } |