CLI help improvements

PULLv2:
  - fix uninitialized "seentype" variable in qom-test
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJbt1jlAAoJENro4Ql1lpzl2dkQAIF4atepjSd2wjOaTVZFjN3A
 TM42MdaeoRu78srdsd3lOIJiy931BdD82WD+Az5EYjZ9/wLgzBtwZgWFSYD8LRQi
 g8xDioZQKjAUNQ7ERPOksxsHXpP3ulBagLsZWWxfY81loXfYdsscMuZc/T3cpHoV
 6wwf2CpdKcl5qq63DDeYpwuv9+6uemND1EKHoppXtAmGPhX7pqPYZ1+h9Cqriwu9
 X5FwlbpZUgig1QjmLZBmp8Akwi66c/NOXUv8BPMR7moQkTdyNfY5Vznvl9fIHEKw
 UxraDsecGXspUABE3gfbnHkGLC5dYK9uoQpBJ56zcFy0O3hUrvAxjwZRltd8hC8q
 iEJp0mvTb4FphOm0Ez0dtlDr594OjZF13FOy0b1TLCkqhz672Irb591o2uIKVavT
 ZHwTT3jRC4nOhz9qoV5MkDMNQcEoVVtu+k/wLloQvghLb4l5bbetuG68jBzw8xGt
 /5q6p2AUwkwFwAbu122M/8dDKnME+J4cABqkw4FIG6iCD+lmIiccgLcDSGj9/PFD
 eW0Gc6D+XHl4HLswiaAClXQRFUNaE1YwtL2JWk7ugzX0klDjW1Zye3b90w+rs+YN
 w6IaubJZJ0Pv/Q9HrajdZ/BKbdsS5rYFbJTdjW/JtRGi6nn+g+/5En3cHmLc5hP7
 lbR3Tdl3hXNIGn33/EO1
 =7blB
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/elmarco/tags/option-pull-request' into staging

CLI help improvements

PULLv2:
 - fix uninitialized "seentype" variable in qom-test

# gpg: Signature made Fri 05 Oct 2018 13:28:21 BST
# gpg:                using RSA key DAE8E10975969CE5
# gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>"
# gpg:                 aka "Marc-André Lureau <marcandre.lureau@gmail.com>"
# Primary key fingerprint: 87A9 BD93 3F87 C606 D276  F62D DAE8 E109 7596 9CE5

* remotes/elmarco/tags/option-pull-request:
  vl: list user creatable properties when 'help' is argument
  hostmem: add some properties description
  vl: handle -object help
  tests/qom-proplist: check class properties iterator
  tests/qom-proplist: check properties are not listed multiple times
  tests/qom-proplist: check duplicate "bv" property registration failed
  qom/object: register 'type' property as class property
  qom/object: fix iterating properties over a class
  qemu-option: improve qemu_opts_print_help() output
  qemu-option: add help fallback to print the list of options
  cutils: add qemu_pstrcmp0()
  qdev-monitor: print help to stdout

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-10-08 10:04:15 +01:00
commit df51a00519
12 changed files with 233 additions and 65 deletions

View file

@ -145,16 +145,25 @@ memfd_backend_class_init(ObjectClass *oc, void *data)
memfd_backend_get_hugetlb,
memfd_backend_set_hugetlb,
&error_abort);
object_class_property_set_description(oc, "hugetlb",
"Use huge pages",
&error_abort);
object_class_property_add(oc, "hugetlbsize", "int",
memfd_backend_get_hugetlbsize,
memfd_backend_set_hugetlbsize,
NULL, NULL, &error_abort);
object_class_property_set_description(oc, "hugetlbsize",
"Huge pages size (ex: 2M, 1G)",
&error_abort);
}
if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
object_class_property_add_bool(oc, "seal",
memfd_backend_get_seal,
memfd_backend_set_seal,
&error_abort);
object_class_property_set_description(oc, "seal",
"Seal growing & shrinking",
&error_abort);
}
}

View file

@ -397,27 +397,41 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
object_class_property_add_bool(oc, "merge",
host_memory_backend_get_merge,
host_memory_backend_set_merge, &error_abort);
object_class_property_set_description(oc, "merge",
"Mark memory as mergeable", &error_abort);
object_class_property_add_bool(oc, "dump",
host_memory_backend_get_dump,
host_memory_backend_set_dump, &error_abort);
object_class_property_set_description(oc, "dump",
"Set to 'off' to exclude from core dump", &error_abort);
object_class_property_add_bool(oc, "prealloc",
host_memory_backend_get_prealloc,
host_memory_backend_set_prealloc, &error_abort);
object_class_property_set_description(oc, "prealloc",
"Preallocate memory", &error_abort);
object_class_property_add(oc, "size", "int",
host_memory_backend_get_size,
host_memory_backend_set_size,
NULL, NULL, &error_abort);
object_class_property_set_description(oc, "size",
"Size of the memory region (ex: 500M)", &error_abort);
object_class_property_add(oc, "host-nodes", "int",
host_memory_backend_get_host_nodes,
host_memory_backend_set_host_nodes,
NULL, NULL, &error_abort);
object_class_property_set_description(oc, "host-nodes",
"Binds memory to the list of NUMA host nodes", &error_abort);
object_class_property_add_enum(oc, "policy", "HostMemPolicy",
&HostMemPolicy_lookup,
host_memory_backend_get_policy,
host_memory_backend_set_policy, &error_abort);
object_class_property_set_description(oc, "policy",
"Set the NUMA policy", &error_abort);
object_class_property_add_bool(oc, "share",
host_memory_backend_get_share, host_memory_backend_set_share,
&error_abort);
object_class_property_set_description(oc, "share",
"Mark the memory as private to QEMU or shared", &error_abort);
}
static const TypeInfo host_memory_backend_info = {

View file

@ -47,4 +47,7 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int dup_fd);
void monitor_fdset_dup_fd_remove(int dup_fd);
int monitor_fdset_dup_fd_find(int dup_fd);
void monitor_vfprintf(FILE *stream,
const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0);
#endif /* MONITOR_H */

View file

@ -169,4 +169,16 @@ bool test_buffer_is_zero_next_accel(void);
int uleb128_encode_small(uint8_t *out, uint32_t n);
int uleb128_decode_small(const uint8_t *in, uint32_t *n);
/**
* qemu_pstrcmp0:
* @str1: a non-NULL pointer to a C string (*str1 can be NULL)
* @str2: a non-NULL pointer to a C string (*str2 can be NULL)
*
* Compares *str1 and *str2 with g_strcmp0().
*
* Returns: an integer less than, equal to, or greater than zero, if
* *str1 is <, == or > than *str2.
*/
int qemu_pstrcmp0(const char **str1, const char **str2);
#endif

View file

@ -4492,6 +4492,20 @@ static void monitor_readline_flush(void *opaque)
monitor_flush(opaque);
}
/*
* Print to current monitor if we have one, else to stream.
* TODO should return int, so callers can calculate width, but that
* requires surgery to monitor_vprintf(). Left for another day.
*/
void monitor_vfprintf(FILE *stream, const char *fmt, va_list ap)
{
if (cur_mon && !monitor_cur_is_qmp()) {
monitor_vprintf(cur_mon, fmt, ap);
} else {
vfprintf(stream, fmt, ap);
}
}
/*
* Print to current monitor if we have one, else to stderr.
* TODO should return int, so callers can calculate width, but that
@ -4499,11 +4513,7 @@ static void monitor_readline_flush(void *opaque)
*/
void error_vprintf(const char *fmt, va_list ap)
{
if (cur_mon && !monitor_cur_is_qmp()) {
monitor_vprintf(cur_mon, fmt, ap);
} else {
vfprintf(stderr, fmt, ap);
}
monitor_vfprintf(stderr, fmt, ap);
}
void error_vprintf_unless_qmp(const char *fmt, va_list ap)

View file

@ -104,22 +104,31 @@ static bool qdev_class_has_alias(DeviceClass *dc)
return (qdev_class_get_alias(dc) != NULL);
}
static void out_printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
monitor_vfprintf(stdout, fmt, ap);
va_end(ap);
}
static void qdev_print_devinfo(DeviceClass *dc)
{
error_printf("name \"%s\"", object_class_get_name(OBJECT_CLASS(dc)));
out_printf("name \"%s\"", object_class_get_name(OBJECT_CLASS(dc)));
if (dc->bus_type) {
error_printf(", bus %s", dc->bus_type);
out_printf(", bus %s", dc->bus_type);
}
if (qdev_class_has_alias(dc)) {
error_printf(", alias \"%s\"", qdev_class_get_alias(dc));
out_printf(", alias \"%s\"", qdev_class_get_alias(dc));
}
if (dc->desc) {
error_printf(", desc \"%s\"", dc->desc);
out_printf(", desc \"%s\"", dc->desc);
}
if (!dc->user_creatable) {
error_printf(", no-user");
out_printf(", no-user");
}
error_printf("\n");
out_printf("\n");
}
static void qdev_print_devinfos(bool show_no_user)
@ -155,8 +164,7 @@ static void qdev_print_devinfos(bool show_no_user)
continue;
}
if (!cat_printed) {
error_printf("%s%s devices:\n", i ? "\n" : "",
cat_name[i]);
out_printf("%s%s devices:\n", i ? "\n" : "", cat_name[i]);
cat_printed = true;
}
qdev_print_devinfo(dc);
@ -278,13 +286,11 @@ int qdev_device_help(QemuOpts *opts)
}
for (prop = prop_list; prop; prop = prop->next) {
error_printf("%s.%s=%s", driver,
prop->value->name,
prop->value->type);
out_printf("%s.%s=%s", driver, prop->value->name, prop->value->type);
if (prop->value->has_description) {
error_printf(" (%s)\n", prop->value->description);
out_printf(" (%s)\n", prop->value->description);
} else {
error_printf("\n");
out_printf("\n");
}
}

View file

@ -1115,7 +1115,7 @@ void object_class_property_iter_init(ObjectPropertyIterator *iter,
ObjectClass *klass)
{
g_hash_table_iter_init(&iter->iter, klass->properties);
iter->nextclass = klass;
iter->nextclass = object_class_get_parent(klass);
}
ObjectProperty *object_class_property_find(ObjectClass *klass, const char *name,
@ -2430,9 +2430,10 @@ void object_class_property_set_description(ObjectClass *klass,
op->description = g_strdup(description);
}
static void object_instance_init(Object *obj)
static void object_class_init(ObjectClass *klass, void *data)
{
object_property_add_str(obj, "type", qdev_get_type, NULL, NULL);
object_class_property_add_str(klass, "type", qdev_get_type,
NULL, &error_abort);
}
static void register_types(void)
@ -2446,7 +2447,7 @@ static void register_types(void)
static TypeInfo object_info = {
.name = TYPE_OBJECT,
.instance_size = sizeof(Object),
.instance_init = object_instance_init,
.class_init = object_class_init,
.abstract = true,
};

View file

@ -141,14 +141,14 @@ Object *user_creatable_add_opts(QemuOpts *opts, Error **errp)
int user_creatable_add_opts_foreach(void *opaque, QemuOpts *opts, Error **errp)
{
bool (*type_predicate)(const char *) = opaque;
bool (*type_opt_predicate)(const char *, QemuOpts *) = opaque;
Object *obj = NULL;
Error *err = NULL;
const char *type;
type = qemu_opt_get(opts, "qom-type");
if (type && type_predicate &&
!type_predicate(type)) {
if (type && type_opt_predicate &&
!type_opt_predicate(type, opts)) {
return 0;
}

View file

@ -125,10 +125,13 @@ static char *dummy_get_sv(Object *obj,
static void dummy_init(Object *obj)
{
Error *err = NULL;
object_property_add_bool(obj, "bv",
dummy_get_bv,
dummy_set_bv,
NULL);
&err);
error_free_or_abort(&err);
}
@ -517,6 +520,32 @@ static void test_dummy_getenum(void)
}
static void test_dummy_prop_iterator(ObjectPropertyIterator *iter)
{
bool seenbv = false, seensv = false, seenav = false, seentype = false;
ObjectProperty *prop;
while ((prop = object_property_iter_next(iter))) {
if (!seenbv && g_str_equal(prop->name, "bv")) {
seenbv = true;
} else if (!seensv && g_str_equal(prop->name, "sv")) {
seensv = true;
} else if (!seenav && g_str_equal(prop->name, "av")) {
seenav = true;
} else if (!seentype && g_str_equal(prop->name, "type")) {
/* This prop comes from the base Object class */
seentype = true;
} else {
g_printerr("Found prop '%s'\n", prop->name);
g_assert_not_reached();
}
}
g_assert(seenbv);
g_assert(seenav);
g_assert(seensv);
g_assert(seentype);
}
static void test_dummy_iterator(void)
{
Object *parent = object_get_objects_root();
@ -529,35 +558,21 @@ static void test_dummy_iterator(void)
"sv", "Hiss hiss hiss",
"av", "platypus",
NULL));
ObjectProperty *prop;
ObjectPropertyIterator iter;
bool seenbv = false, seensv = false, seenav = false, seentype;
object_property_iter_init(&iter, OBJECT(dobj));
while ((prop = object_property_iter_next(&iter))) {
if (g_str_equal(prop->name, "bv")) {
seenbv = true;
} else if (g_str_equal(prop->name, "sv")) {
seensv = true;
} else if (g_str_equal(prop->name, "av")) {
seenav = true;
} else if (g_str_equal(prop->name, "type")) {
/* This prop comes from the base Object class */
seentype = true;
} else {
g_printerr("Found prop '%s'\n", prop->name);
g_assert_not_reached();
}
}
g_assert(seenbv);
g_assert(seenav);
g_assert(seensv);
g_assert(seentype);
test_dummy_prop_iterator(&iter);
object_unparent(OBJECT(dobj));
}
static void test_dummy_class_iterator(void)
{
ObjectPropertyIterator iter;
ObjectClass *klass = object_class_by_name(TYPE_DUMMY);
object_class_property_iter_init(&iter, klass);
test_dummy_prop_iterator(&iter);
}
static void test_dummy_delchild(void)
{
@ -629,6 +644,7 @@ int main(int argc, char **argv)
g_test_add_func("/qom/proplist/badenum", test_dummy_badenum);
g_test_add_func("/qom/proplist/getenum", test_dummy_getenum);
g_test_add_func("/qom/proplist/iterator", test_dummy_iterator);
g_test_add_func("/qom/proplist/class_iterator", test_dummy_class_iterator);
g_test_add_func("/qom/proplist/delchild", test_dummy_delchild);
g_test_add_func("/qom/resolve/partial", test_qom_partial_path);

View file

@ -769,3 +769,8 @@ char *size_to_str(uint64_t val)
return g_strdup_printf("%0.3g %sB", (double)val / div, suffixes[i]);
}
int qemu_pstrcmp0(const char **str1, const char **str2)
{
return g_strcmp0(*str1, *str2);
}

View file

@ -208,17 +208,51 @@ out:
return result;
}
static const char *opt_type_to_string(enum QemuOptType type)
{
switch (type) {
case QEMU_OPT_STRING:
return "str";
case QEMU_OPT_BOOL:
return "bool (on/off)";
case QEMU_OPT_NUMBER:
return "num";
case QEMU_OPT_SIZE:
return "size";
}
g_assert_not_reached();
}
void qemu_opts_print_help(QemuOptsList *list)
{
QemuOptDesc *desc;
int i;
GPtrArray *array = g_ptr_array_new();
assert(list);
desc = list->desc;
while (desc && desc->name) {
printf("%-16s %s\n", desc->name,
desc->help ? desc->help : "No description available");
GString *str = g_string_new(NULL);
if (list->name) {
g_string_append_printf(str, "%s.", list->name);
}
g_string_append_printf(str, "%s=%s", desc->name,
opt_type_to_string(desc->type));
if (desc->help) {
g_string_append_printf(str, " - %s", desc->help);
}
g_ptr_array_add(array, g_string_free(str, false));
desc++;
}
g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0);
for (i = 0; i < array->len; i++) {
printf("%s\n", (char *)array->pdata[i]);
}
g_ptr_array_set_free_func(array, g_free);
g_ptr_array_free(array, true);
}
/* ------------------------------------------------------------------ */
@ -486,7 +520,7 @@ int qemu_opt_unset(QemuOpts *opts, const char *name)
}
static void opt_set(QemuOpts *opts, const char *name, char *value,
bool prepend, Error **errp)
bool prepend, bool *invalidp, Error **errp)
{
QemuOpt *opt;
const QemuOptDesc *desc;
@ -496,6 +530,9 @@ static void opt_set(QemuOpts *opts, const char *name, char *value,
if (!desc && !opts_accepts_any(opts)) {
g_free(value);
error_setg(errp, QERR_INVALID_PARAMETER, name);
if (invalidp) {
*invalidp = true;
}
return;
}
@ -519,7 +556,7 @@ static void opt_set(QemuOpts *opts, const char *name, char *value,
void qemu_opt_set(QemuOpts *opts, const char *name, const char *value,
Error **errp)
{
opt_set(opts, name, g_strdup(value), false, errp);
opt_set(opts, name, g_strdup(value), false, NULL, errp);
}
void qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val,
@ -750,7 +787,8 @@ void qemu_opts_print(QemuOpts *opts, const char *separator)
}
static void opts_do_parse(QemuOpts *opts, const char *params,
const char *firstname, bool prepend, Error **errp)
const char *firstname, bool prepend,
bool *invalidp, Error **errp)
{
char *option = NULL;
char *value = NULL;
@ -785,7 +823,7 @@ static void opts_do_parse(QemuOpts *opts, const char *params,
}
if (strcmp(option, "id") != 0) {
/* store and parse */
opt_set(opts, option, value, prepend, &local_err);
opt_set(opts, option, value, prepend, invalidp, &local_err);
value = NULL;
if (local_err) {
error_propagate(errp, local_err);
@ -814,11 +852,12 @@ static void opts_do_parse(QemuOpts *opts, const char *params,
void qemu_opts_do_parse(QemuOpts *opts, const char *params,
const char *firstname, Error **errp)
{
opts_do_parse(opts, params, firstname, false, errp);
opts_do_parse(opts, params, firstname, false, NULL, errp);
}
static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
bool permit_abbrev, bool defaults, Error **errp)
bool permit_abbrev, bool defaults,
bool *invalidp, Error **errp)
{
const char *firstname;
char *id = NULL;
@ -850,7 +889,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
return NULL;
}
opts_do_parse(opts, params, firstname, defaults, &local_err);
opts_do_parse(opts, params, firstname, defaults, invalidp, &local_err);
if (local_err) {
error_propagate(errp, local_err);
qemu_opts_del(opts);
@ -870,7 +909,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params,
bool permit_abbrev, Error **errp)
{
return opts_parse(list, params, permit_abbrev, false, errp);
return opts_parse(list, params, permit_abbrev, false, NULL, errp);
}
/**
@ -886,10 +925,16 @@ QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params,
{
Error *err = NULL;
QemuOpts *opts;
bool invalidp = false;
opts = opts_parse(list, params, permit_abbrev, false, &err);
opts = opts_parse(list, params, permit_abbrev, false, &invalidp, &err);
if (err) {
error_report_err(err);
if (invalidp && has_help_option(params)) {
qemu_opts_print_help(list);
error_free(err);
} else {
error_report_err(err);
}
}
return opts;
}
@ -899,7 +944,7 @@ void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
{
QemuOpts *opts;
opts = opts_parse(list, params, permit_abbrev, true, NULL);
opts = opts_parse(list, params, permit_abbrev, true, NULL, NULL);
assert(opts);
}

53
vl.c
View file

@ -2696,8 +2696,55 @@ static int machine_set_property(void *opaque,
* cannot be created here, as it depends on the chardev
* already existing.
*/
static bool object_create_initial(const char *type)
static bool object_create_initial(const char *type, QemuOpts *opts)
{
ObjectClass *klass;
if (is_help_option(type)) {
GSList *l, *list;
printf("List of user creatable objects:\n");
list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false);
for (l = list; l != NULL; l = l->next) {
ObjectClass *oc = OBJECT_CLASS(l->data);
printf("%s\n", object_class_get_name(oc));
}
g_slist_free(list);
exit(0);
}
klass = object_class_by_name(type);
if (klass && qemu_opt_has_help_opt(opts)) {
ObjectPropertyIterator iter;
ObjectProperty *prop;
GPtrArray *array = g_ptr_array_new();
int i;
object_class_property_iter_init(&iter, klass);
while ((prop = object_property_iter_next(&iter))) {
GString *str;
if (!prop->set) {
continue;
}
str = g_string_new(NULL);
g_string_append_printf(str, "%s.%s=%s", type,
prop->name, prop->type);
if (prop->description) {
g_string_append_printf(str, " - %s", prop->description);
}
g_ptr_array_add(array, g_string_free(str, false));
}
g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0);
for (i = 0; i < array->len; i++) {
printf("%s\n", (char *)array->pdata[i]);
}
g_ptr_array_set_free_func(array, g_free);
g_ptr_array_free(array, true);
exit(0);
}
if (g_str_equal(type, "rng-egd") ||
g_str_has_prefix(type, "pr-manager-")) {
return false;
@ -2744,9 +2791,9 @@ static bool object_create_initial(const char *type)
* The remainder of object creation happens after the
* creation of chardev, fsdev, net clients and device data types.
*/
static bool object_create_delayed(const char *type)
static bool object_create_delayed(const char *type, QemuOpts *opts)
{
return !object_create_initial(type);
return !object_create_initial(type, opts);
}