From patchwork Sun Jan 5 12:02:31 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amos Kong X-Patchwork-Id: 306953 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id B21C22C0040 for ; Sun, 5 Jan 2014 23:03:44 +1100 (EST) Received: from localhost ([::1]:57564 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VzmQe-0002KI-PF for incoming@patchwork.ozlabs.org; Sun, 05 Jan 2014 07:03:40 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:35844) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VzmQ3-0002AI-GI for qemu-devel@nongnu.org; Sun, 05 Jan 2014 07:03:09 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1VzmPw-0005EB-Eb for qemu-devel@nongnu.org; Sun, 05 Jan 2014 07:03:03 -0500 Received: from mx1.redhat.com ([209.132.183.28]:60821) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VzmPw-0005Dy-3j for qemu-devel@nongnu.org; Sun, 05 Jan 2014 07:02:56 -0500 Received: from int-mx12.intmail.prod.int.phx2.redhat.com (int-mx12.intmail.prod.int.phx2.redhat.com [10.5.11.25]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s05C2nTG001464 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Sun, 5 Jan 2014 07:02:49 -0500 Received: from amosk.info.com (vpn1-112-27.nay.redhat.com [10.66.112.27]) by int-mx12.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s05C2ZPB005042; Sun, 5 Jan 2014 07:02:45 -0500 From: Amos Kong To: qemu-devel@nongnu.org Date: Sun, 5 Jan 2014 20:02:31 +0800 Message-Id: <1388923351-10556-4-git-send-email-akong@redhat.com> In-Reply-To: <1388923351-10556-1-git-send-email-akong@redhat.com> References: <1388923351-10556-1-git-send-email-akong@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.25 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: mdroth@linux.vnet.ibm.com, qiaonuohan@cn.fujitsu.com, xiawenc@linux.vnet.ibm.com, lcapitulino@redhat.com Subject: [Qemu-devel] [PATCH v3 3/3] qmp: full introspection support for QMP X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This patch introduces a new monitor command to query QMP schema information, the return data is a range of schema structs, which contains the useful metadata to help management to check supported features, QMP commands detail, etc. It parses all json definition in qapi-schema.json, and generate a dynamic struct tree, QMP infrastructure will convert the tree to json string and return to QMP client. I defined a 'DataObject' union in qapi-schema.json, it's used to describe the dynamic data struct. I also added a document about QMP full introspection support (docs/qmp-full-introspection.txt), it helps to use the new interface and understand the abstract method in describing the dynamic struct. TODO: Wenchao Xia is working to convert QMP events to qapi-schema.json, then event can also be queried by this interface. I will introduce another command 'query-qga-schema' to query QGA schema information, it's easy to add this support based on this patch. Signed-off-by: Amos Kong --- docs/qmp-full-introspection.txt | 97 ++++++++++ qapi-schema.json | 150 ++++++++++++++++ qmp-commands.hx | 43 ++++- qmp.c | 382 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 671 insertions(+), 1 deletion(-) create mode 100644 docs/qmp-full-introspection.txt diff --git a/docs/qmp-full-introspection.txt b/docs/qmp-full-introspection.txt new file mode 100644 index 0000000..1617df7 --- /dev/null +++ b/docs/qmp-full-introspection.txt @@ -0,0 +1,97 @@ += Full introspection support for QMP = + + +== Purpose == + +Add a new monitor command for management to query QMP schema +information, it returns a range of schema structs, which contain the +useful metadata to help management to check supported features, QMP +commands detail, etc. + +== Usage == + +Json schema: + { 'type': 'NameInfo', 'data': {'*name': 'str'} } + { 'command': 'query-name', 'returns': 'NameInfo' } + +Execute QMP command: + + { "execute": "query-qmp-schema" } + +Returns: + + { "return": [ + { + "name": "query-name", + "type": "command", + "returns": { + "name": "NameInfo", + "type": "type", + "data": [ + { + "name": "name", + "optional": true, + "recursive": false, + "type": "str" + } + ] + } + }, + ... + } + +The whole schema information will be returned in one go, it contains +all the schema entries. It doesn't support to be filtered by type +or name. Currently it takes about 5 seconds to return about 1.5M string. + +== 'DataObject' union == + +{ 'union': 'DataObject', + 'base': 'DataObjectBase', + 'discriminator': 'type', + 'data': { + 'anonymous-struct': 'DataObjectAnonymousStruct', + 'command': 'DataObjectCommand', + 'enumeration': 'DataObjectEnumeration', + 'reference-type': 'String', + 'type': 'DataObjectType', + 'unionobj': 'DataObjectUnion' } } + +Currently we have schema difinitions for type, command, enumeration, +union. Some arbitrary structs (dictionary, list or string) and native +types are also used in the body of definitions. + +Here we use "DataObject" union to abstract all above schema. We want +to provide more useful metadata, and used some enum/unions to indicate +the dynamic type. In the output, some simple data is processed too +unwieldy. In another side, some complex data is described clearly. +It's also caused by some limitation of QAPI infrastructure. + +So we define 'DataObject' to be an union, it always has an object name +except anonymous struct. + +'command', 'enumeration', 'type', 'unionobj' are common schema type, +'union' is a build-in type, so I used unionobj here. + +'reference-type' will be used to describe native types and unextended +types. + +'anonymous-struct' will be used to describe arbitrary structs +(dictionary, list or string). + +== Avoid dead loop in recursive extending == + +We have four types (ImageInfo, BlockStats, PciDeviceInfo, ObjectData) +that uses themself in their own define data directly or indirectly, +we will not repeatedly extend them to avoid dead loop. + +We use a string to record the visit path, type index of each node +will be saved to the string, indexes are split by ':'. + +Push index to visit_path_str before extending, and pop index from +visit_path_str after extending. + +If the type was already extended in parent node, we don't extend it +again to avoid dead loop. + +'recursive' indicates if the type is extended or not. diff --git a/qapi-schema.json b/qapi-schema.json index c3c939c..db500ab 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -4235,3 +4235,153 @@ # Since: 1.7 ## { 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } } + +## +# @DataObjectBase +# +# Base of @DataObject +# +# @name: #optional @DataObject name +# @type: @DataObject type +# +# Since: 1.8 +## +{ 'type': 'DataObjectBase', + 'data': { '*name': 'str', 'type': 'str' } } + +## +# @DataObjectMemberType +# +# Type of @DabaObjectMember +# +# @reference: reference string +# @anonymous: arbitrary struct +# @extend: the @DataObjectMember +# +# Since: 1.8 +## +{ 'union': 'DataObjectMemberType', + 'discriminator': {}, + 'data': { 'reference': 'str', + 'anonymous': 'DataObject', + 'extend': 'DataObject' } } + +## +# @DataObjectMember +# +# General member of @DataObject +# +# @type: type of @DataObjectMember +# @name: #optional name +# @optional: #optional key to indicate if the @DataObjectMember is optional +# @recursive: #optional key to indicate if it's defined recursively +# +# Since: 1.8 +## +{ 'type': 'DataObjectMember', + 'data': { 'type': 'DataObjectMemberType', '*name': 'str', + '*optional': 'bool', '*recursive': 'bool' } } + +## +# @DataObjectAnonymousStruct +# +# Arbitrary struct, it can be dictionary, list or string +# +# @data: content of arbitrary struct +# +# Since: 1.8 +## +{ 'type': 'DataObjectAnonymousStruct', + 'data': { 'data': [ 'DataObjectMember' ] } } + +## +# @DataObjectCommand +# +# QMP Command schema +# +# @data: QMP command content +# @returns: returns of executing command +# @gen: a key to suppress code generation +# +# Since: 1.8 +## +{ 'type': 'DataObjectCommand', + 'data': { '*data': [ 'DataObjectMember' ], + '*returns': 'DataObject', + '*gen': 'bool' } } + +## +# @DataObjectEnumeration +# +# Enumeration schema +# +# @data: enumeration content, it's a string list +# +# Since: 1.8 +## +{ 'type': 'DataObjectEnumeration', + 'data': { 'data': [ 'str' ] } } + +## +# @DataObjectType +# +# Type schema +# +# @data: type content +# +# Since: 1.8 +## +{ 'type': 'DataObjectType', + 'data': { 'data': [ 'DataObjectMember' ] } } + +## +# @DataObjectUnion +# +# Union schema +# +# @data: union content +# @base: union base +# @discriminator: union discriminator +# +# Since: 1.8 +## +{ 'type': 'DataObjectUnion', + 'data': { 'data': [ 'DataObjectMember' ], '*base': 'str', + '*discriminator': 'str' } } + +## +# @DataObject +# +# Dynamic data struct, it can be command, enumeration, type, union, arbitrary +# struct or native type. +# +# @anonymous-struct: arbitrary struct, it can be dictionary, list or string +# @command: QMP command schema +# @enumeration: enumeration schema +# @reference-type: native type or unextended type +# @type: type schema, it will be extended +# @unionobj: union schema +# +# Since: 1.8 +## +{ 'union': 'DataObject', + 'base': 'DataObjectBase', + 'discriminator': 'type', + 'data': { + 'anonymous-struct': 'DataObjectAnonymousStruct', + 'command': 'DataObjectCommand', + 'enumeration': 'DataObjectEnumeration', + 'reference-type': 'String', + 'type': 'DataObjectType', + 'unionobj': 'DataObjectUnion' } } + +## +# @query-qmp-schema +# +# Query QMP schema information +# +# @returns: list of @DataObject +# +# Since: 1.8 +## +{ 'command': 'query-qmp-schema', 'returns': ['DataObject'] } diff --git a/qmp-commands.hx b/qmp-commands.hx index fba15cd..cf9c4aa 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -3237,7 +3237,48 @@ Example: "broadcast-allowed": false } ] - } + +EQMP + { + .name = "query-qmp-schema", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_query_qmp_schema, + }, + + +SQMP +query-qmp-schema +---------------- + +query qmp schema information + +Return a json-object with the following information: + +- "name": qmp schema name (json-string) +- "type": qmp schema type, it can be 'comand', 'type', 'enum', 'union' +- "returns": return data of qmp command (json-object, optional) + +Example: + +-> { "execute": "query-qmp-schema" } +-> { "return": [ + { + "name": "query-name", + "type": "command", + "returns": { + "name": "NameInfo", + "type": "type", + "data": [ + { + "name": "name", + "optional": true, + "recursive": false, + "type": "str" + } + ] + } + } + } EQMP diff --git a/qmp.c b/qmp.c index 1d7a04d..e9bba06 100644 --- a/qmp.c +++ b/qmp.c @@ -25,6 +25,8 @@ #include "sysemu/blockdev.h" #include "qom/qom-qobject.h" #include "hw/boards.h" +#include "qmp-schema.h" +#include "qapi/qmp/qjson.h" NameInfo *qmp_query_name(Error **errp) { @@ -486,6 +488,386 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) return arch_query_cpu_definitions(errp); } +/* + * use a passed string to record the visit path, schema index of + * each extended node will be appended to the string, indexes are + * split by ':' + */ +static char visit_path_str[1024]; + +/* push the type index into visit_path_str */ +static void push_id(int id) +{ + char *end = strrchr(visit_path_str, ':'); + char type_idx[256]; + int num; + + num = sprintf(type_idx, "%d:", id); + + if (end) { + /* avoid overflow */ + assert(end - visit_path_str + 1 + num < sizeof(visit_path_str)); + sprintf(end + 1, "%d:", id); + } else { + sprintf(visit_path_str, "%d:", id); + } +} + +/* pop the type index from visit_path_str */ +static void pop_id(void) +{ + char *p = strrchr(visit_path_str, ':'); + + assert(p != NULL); + *p = '\0'; + p = strrchr(visit_path_str, ':'); + if (p) { + *(p + 1) = '\0'; + } else { + visit_path_str[0] = '\0'; + } +} + +static const char *qstring_copy_str(QObject *data) +{ + QString *qstr; + + if (!data) { + return NULL; + } + qstr = qobject_to_qstring(data); + if (qstr) { + return qstring_get_str(qstr); + } else { + return NULL; + } +} + +static QObject *get_definition(const char *str, bool update_path) +{ + QObject *data, *value; + QDict *qdict; + int i; + + if (!strcmp(str, "str") || !strcmp(str, "int") || + !strcmp(str, "number") || !strcmp(str, "bool") || + !strcmp(str, "int8") || !strcmp(str, "int16") || + !strcmp(str, "int32") || !strcmp(str, "int64") || + !strcmp(str, "uint8") || !strcmp(str, "uint16") || + !strcmp(str, "uint32") || !strcmp(str, "uint64") || + !strcmp(str, "visitor") || !strcmp(str, "**") || + !strcmp(str, "size")) { + return NULL; + } + + for (i = 0; qmp_schema_table[i]; i++) { + data = qobject_from_json(qmp_schema_table[i]); + qdict = qobject_to_qdict(data); + assert(qdict != NULL); + + if (qdict_get(qdict, "enum")) { + value = qdict_get(qdict, "enum"); + } else if (qdict_get(qdict, "type")) { + value = qdict_get(qdict, "type"); + } else if (qdict_get(qdict, "union")) { + value = qdict_get(qdict, "union"); + } else { + continue; + } + + if (!strcmp(str, qstring_copy_str(value)) && update_path) { + char *start, *end; + char cur_idx[256]; + char type_idx[256]; + + start = visit_path_str; + sprintf(type_idx, "%d", i); + while (start) { + end = strchr(start, ':'); + if (!end) { + break; + } + snprintf(cur_idx, end - start + 1, "%s", start); + start = end + 1; + /* if the type was already extended in parent node, + * we don't extend it again to avoid dead loop. */ + if (!strcmp(cur_idx, type_idx)) { + return NULL; + } + } + /* push index to visit_path_str before extending */ + push_id(i); + } + if (!strcmp(str, qstring_copy_str(value))) { + + return qobject_from_json(qmp_schema_table[i]); + } + } + return NULL; +} + +static DataObject *visit_qobj_dict(QObject *data); +static DataObject *visit_qobj_list(QObject *data); + +/* extend defined type to json object */ +static DataObject *extend_type(const char *str) +{ + QObject *data; + DataObject *obj; + + data = get_definition(str, true); + + if (data) { + obj = visit_qobj_dict(data); + pop_id(); + } else { + obj = g_malloc0(sizeof(struct DataObject)); + obj->kind = DATA_OBJECT_KIND_REFERENCE_TYPE; + obj->reference_type = g_malloc0(sizeof(String)); + obj->reference_type->str = g_strdup(str); + } + + return obj; +} + +static DataObjectMemberList *list_to_memberlist(QObject *data) +{ + DataObjectMemberList *mem_list, *entry, *last_entry; + QList *qlist; + const QListEntry *lent; + + qlist = qobject_to_qlist(data); + + mem_list = NULL; + for (lent = qlist_first(qlist); lent; lent = qlist_next(lent)) { + entry = g_malloc0(sizeof(DataObjectMemberList *)); + entry->value = g_malloc0(sizeof(DataObjectMember)); + entry->value->type = g_malloc0(sizeof(DataObjectMemberType)); + + if (get_definition(qstring_copy_str(lent->value), false)) { + entry->value->type->kind = DATA_OBJECT_MEMBER_TYPE_KIND_EXTEND; + entry->value->has_recursive = true; + entry->value->recursive = true; + entry->value->type->extend = + extend_type(qstring_copy_str(lent->value)); + } else { + entry->value->type->kind = + DATA_OBJECT_MEMBER_TYPE_KIND_REFERENCE; + entry->value->has_recursive = true; + entry->value->recursive = false; + entry->value->type->reference = + g_strdup(qstring_copy_str(lent->value)); + } + + entry->next = NULL; + if (!mem_list) { + mem_list = entry; + } else { + last_entry->next = entry; + } + last_entry = entry; + } + return mem_list; +} + +static DataObjectMemberList *dict_to_memberlist(QObject *data) +{ + DataObjectMemberList *mem_list, *entry, *last_entry; + QDict *qdict; + const QDictEntry *dent; + + qdict = qobject_to_qdict(data); + + mem_list = NULL; + for (dent = qdict_first(qdict); dent; dent = qdict_next(qdict, dent)) { + entry = g_malloc0(sizeof(DataObjectMemberList *)); + entry->value = g_malloc0(sizeof(DataObjectMember)); + + entry->value->type = g_malloc0(sizeof(DataObjectMemberType)); + + if (dent->value->type->code == QTYPE_QDICT) { + entry->value->type->kind = DATA_OBJECT_MEMBER_TYPE_KIND_EXTEND; + entry->value->type->extend = visit_qobj_dict(dent->value); + } else if (dent->value->type->code == QTYPE_QLIST) { + entry->value->type->kind = DATA_OBJECT_MEMBER_TYPE_KIND_EXTEND; + entry->value->type->extend = visit_qobj_list(dent->value); + } else if (get_definition(qstring_copy_str(dent->value), false)) { + entry->value->type->kind = DATA_OBJECT_MEMBER_TYPE_KIND_EXTEND; + entry->value->has_recursive = true; + entry->value->recursive = true; + entry->value->type->extend = + extend_type(qstring_copy_str(dent->value)); + } else { + entry->value->type->kind = + DATA_OBJECT_MEMBER_TYPE_KIND_REFERENCE; + entry->value->has_recursive = true; + entry->value->recursive = false; + entry->value->type->reference = + g_strdup(qstring_copy_str(dent->value)); + } + entry->value->has_optional = true; + entry->value->has_name = true; + if (dent->key[0] == '*') { + entry->value->optional = true; + entry->value->name = g_strdup(dent->key + 1); + } else { + entry->value->name = g_strdup(dent->key); + } + + entry->next = NULL; + if (!mem_list) { + mem_list = entry; + } else { + last_entry->next = entry; + } + last_entry = entry; + } + return mem_list; +} + +static DataObject *visit_qobj_list(QObject *data) +{ + DataObject *obj; + + obj = g_malloc0(sizeof(struct DataObject)); + obj->kind = DATA_OBJECT_KIND_ANONYMOUS_STRUCT; + obj->anonymous_struct = g_malloc0(sizeof(struct + DataObjectAnonymousStruct)); + obj->anonymous_struct->data = list_to_memberlist(data); + + return obj; +} + +static strList *get_str_list(QObject *data) +{ + strList *str_list, *last_str_entry, *str_entry; + QList *qlist; + const QListEntry *lent; + + qlist = qobject_to_qlist(data); + str_list = NULL; + for (lent = qlist_first(qlist); lent; lent = qlist_next(lent)) { + str_entry = g_malloc0(sizeof(strList *)); + str_entry->value = g_strdup(qstring_copy_str(lent->value)); + str_entry->next = NULL; + if (!str_list) { + str_list = str_entry; + } else { + last_str_entry->next = str_entry; + } + last_str_entry = str_entry; + } + + return str_list; +} + +static DataObject *visit_qobj_dict(QObject *data) +{ + DataObject *obj; + QObject *subdata; + QDict *qdict; + + qdict = qobject_to_qdict(data); + assert(qdict != NULL); + obj = g_malloc0(sizeof(*obj)); + + if (qdict_get(qdict, "command")) { + obj->kind = DATA_OBJECT_KIND_COMMAND; + obj->has_name = true; + obj->name = g_strdup(qstring_copy_str(qdict_get(qdict, "command"))); + obj->command = g_malloc0(sizeof(struct DataObjectCommand)); + + subdata = qdict_get(qdict, "data"); + if (subdata && subdata->type->code == QTYPE_QDICT) { + obj->command->has_data = true; + obj->command->data = dict_to_memberlist(subdata); + } else if (subdata && subdata->type->code == QTYPE_QLIST) { + abort(); + } else if (subdata) { + obj->command->has_data = true; + obj->command->data = + dict_to_memberlist(get_definition(qstring_copy_str(subdata), + true)); + pop_id(); + } + + subdata = qdict_get(qdict, "returns"); + if (subdata && subdata->type->code == QTYPE_QDICT) { + abort(); + } else if (subdata && subdata->type->code == QTYPE_QLIST) { + obj->command->has_returns = true; + obj->command->returns = visit_qobj_list(subdata); + } else if (subdata && subdata->type->code == QTYPE_QSTRING) { + obj->command->has_returns = true; + obj->command->returns = extend_type(qstring_copy_str(subdata)); + } + + subdata = qdict_get(qdict, "gen"); + if (subdata && subdata->type->code == QTYPE_QSTRING) { + obj->command->has_gen = true; + if (!strcmp(qstring_copy_str(subdata), "no")) { + obj->command->gen = false; + } else { + obj->command->gen = true; + } + } + } else if (qdict_get(qdict, "union")) { + obj->kind = DATA_OBJECT_KIND_UNIONOBJ; + obj->has_name = true; + obj->name = g_strdup(qstring_copy_str(qdict_get(qdict, "union"))); + obj->unionobj = g_malloc0(sizeof(struct DataObjectUnion)); + subdata = qdict_get(qdict, "data"); + obj->unionobj->data = dict_to_memberlist(subdata); + } else if (qdict_get(qdict, "type")) { + obj->kind = DATA_OBJECT_KIND_TYPE; + obj->has_name = true; + obj->name = g_strdup(qstring_copy_str(qdict_get(qdict, "type"))); + obj->type = g_malloc0(sizeof(struct DataObjectType)); + subdata = qdict_get(qdict, "data"); + obj->type->data = dict_to_memberlist(subdata); + } else if (qdict_get(qdict, "enum")) { + obj->kind = DATA_OBJECT_KIND_ENUMERATION; + obj->has_name = true; + obj->name = g_strdup(qstring_copy_str(qdict_get(qdict, "enum"))); + obj->enumeration = g_malloc0(sizeof(struct DataObjectEnumeration)); + subdata = qdict_get(qdict, "data"); + obj->enumeration->data = get_str_list(subdata); + } else { + obj->kind = DATA_OBJECT_KIND_ANONYMOUS_STRUCT; + obj->anonymous_struct = g_malloc0(sizeof(struct + DataObjectAnonymousStruct)); + obj->anonymous_struct->data = dict_to_memberlist(data); + } + + return obj; +} + +DataObjectList *qmp_query_qmp_schema(Error **errp) +{ + DataObjectList *list, *last_entry, *entry; + QObject *data; + int i; + + list = NULL; + for (i = 0; qmp_schema_table[i]; i++) { + data = qobject_from_json(qmp_schema_table[i]); + assert(data != NULL); + + entry = g_malloc0(sizeof(DataObjectList *)); + memset(visit_path_str, 0, sizeof(visit_path_str)); + entry->value = visit_qobj_dict(data); + entry->next = NULL; + if (!list) { + list = entry; + } else { + last_entry->next = entry; + } + last_entry = entry; + } + + return list; +} + void qmp_add_client(const char *protocol, const char *fdname, bool has_skipauth, bool skipauth, bool has_tls, bool tls, Error **errp)