From 5535692ee0a81487215bca450a54761069e73112 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 26 Jul 2022 10:44:32 +1000 Subject: [PATCH] proto: allow the client to set the protocol version Let the client set the version number it wants on Connect. There is new public API to query the client/server's version as set once the connect finished (eis_client_get_version() and ei_get_version()) but there is currently no public API for the client to select the version it actually wants, other than whatever both support. IOW, it's not possible for the client to say "I want version 8 but if that's not supported, use version 5". --- proto/ei.proto | 5 +++-- src/libei-private.h | 1 + src/libei-proto.c | 3 ++- src/libei-proto.h | 2 +- src/libei.c | 33 +++++++++++++++++++++++++++++---- src/libei.h | 15 +++++++++++++++ src/libeis-client.c | 10 +++++++++- src/libeis-private.h | 1 + src/libeis-proto.c | 3 ++- src/libeis-proto.h | 2 +- src/libeis.h | 6 ++++++ test/eierpecken.c | 3 +++ test/test-eis.c | 21 +++++++++++++++++++++ 13 files changed, 94 insertions(+), 11 deletions(-) diff --git a/proto/ei.proto b/proto/ei.proto index 74392d4..d0ed8ac 100644 --- a/proto/ei.proto +++ b/proto/ei.proto @@ -84,8 +84,9 @@ message ConfigureCapabilities { } message Connect { - string name = 1; - bool is_sender = 2; + uint32 version = 1; /* Must be equal or less to the server's GetVersion response */ + string name = 2; + bool is_sender = 3; } message ConnectDone { diff --git a/src/libei-private.h b/src/libei-private.h index 610a974..10e04fa 100644 --- a/src/libei-private.h +++ b/src/libei-private.h @@ -71,6 +71,7 @@ struct ei { bool is_sender; uint32_t server_version; + uint32_t client_version; }; enum ei_seat_state { diff --git a/src/libei-proto.c b/src/libei-proto.c index 7c274c2..4f3a569 100644 --- a/src/libei-proto.c +++ b/src/libei-proto.c @@ -325,12 +325,13 @@ ei_proto_send_get_version(struct ei *ei) } static int -ei_proto_send_connect(struct ei *ei) +ei_proto_send_connect(struct ei *ei, uint32_t version) { prepare_msg(CONNECT, Connect, connect); connect.name = ei->name; connect.is_sender = ei->is_sender; + connect.version = version; return ei_proto_send_msg(ei, &msg); } diff --git a/src/libei-proto.h b/src/libei-proto.h index 5738819..d224194 100644 --- a/src/libei-proto.h +++ b/src/libei-proto.h @@ -80,7 +80,7 @@ struct ei_proto_interface { }; struct ei_proto_requests { - int (*connect)(struct ei *ei); + int (*connect)(struct ei *ei, uint32_t version); int (*connect_done)(struct ei *ei); int (*disconnect)(struct ei *ei); int (*bind_seat)(struct ei_seat *seat, enum ei_device_capability cap); diff --git a/src/libei.c b/src/libei.c index f60c350..6e00286 100644 --- a/src/libei.c +++ b/src/libei.c @@ -52,7 +52,7 @@ _Static_assert(sizeof(enum ei_event_type) == sizeof(int), "Invalid enum size"); _Static_assert(sizeof(enum ei_log_priority) == sizeof(int), "Invalid enum size"); static int -ei_finish_set_connection(struct ei *ei); +ei_finish_set_connection(struct ei *ei, uint32_t version); static struct ei_seat * ei_find_seat(struct ei *ei, uint32_t seatid) @@ -196,6 +196,27 @@ ei_get_fd(struct ei *ei) return sink_get_fd(ei->sink); } +_public_ uint32_t +ei_get_version(struct ei *ei) +{ + switch (ei->state) { + case EI_STATE_NEW: + case EI_STATE_BACKEND: + case EI_STATE_VERSION_QUERY: + case EI_STATE_CONNECTING: + break; + case EI_STATE_CONNECTED: + case EI_STATE_DISCONNECTING: + return ei->client_version; + case EI_STATE_DISCONNECTED: + break; + } + + log_bug(ei, "Version is only available on successful connection"); + + return 0; +} + _public_ void ei_dispatch(struct ei *ei) { @@ -1354,7 +1375,11 @@ handle_msg_version_during_connection(struct ei *ei, uint32_t version) if (rc != 0) return rc; - return ei_finish_set_connection(ei); + const uint32_t our_version = VERSION_V(1); + + ei->client_version = min(our_version, version); + + return ei_finish_set_connection(ei, ei->client_version); } static const struct ei_proto_interface intf_state_backend = { @@ -1487,9 +1512,9 @@ ei_set_connection(struct ei *ei, int fd) } static int -ei_finish_set_connection(struct ei *ei) +ei_finish_set_connection(struct ei *ei, uint32_t version) { - int rc = ei->requests->connect(ei); + int rc = ei->requests->connect(ei, version); struct ei_property *prop; list_for_each_safe(prop, &ei->properties, link) { diff --git a/src/libei.h b/src/libei.h index 6024d53..4b584c5 100644 --- a/src/libei.h +++ b/src/libei.h @@ -656,6 +656,21 @@ ei_get_fd(struct ei *ei); void ei_dispatch(struct ei *ei); +/** + * Return the protocol version for this client. Version negotiation is handled by libei + * during the initial connection, the client's version is the minimum version between + * the server's version and the client supported version. + * + * @note libei currently has no API to request specific versions + * + * It is an application bug to call this function before receiving the @ref + * EI_EVENT_CONNECT event. + * + * @return The protocol version. + */ +uint32_t +ei_get_version(struct ei *ei); + /** * Return the next event from the event queue, removing it from the queue. * diff --git a/src/libeis-client.c b/src/libeis-client.c index 821a14a..c7cd833 100644 --- a/src/libeis-client.c +++ b/src/libeis-client.c @@ -72,6 +72,8 @@ OBJECT_IMPLEMENT_UNREF_CLEANUP(eis_client); _public_ OBJECT_IMPLEMENT_GETTER(eis_client, name, const char*); _public_ +OBJECT_IMPLEMENT_GETTER(eis_client, version, uint32_t); +_public_ OBJECT_IMPLEMENT_SETTER(eis_client, user_data, void*); _public_ OBJECT_IMPLEMENT_GETTER(eis_client, user_data, void*); @@ -524,8 +526,14 @@ client_msg_configure_capabilities(struct eis_client *client, uint32_t allowed_ca } static int -client_msg_connect(struct eis_client *client, const char *name, bool is_sender) +client_msg_connect(struct eis_client *client, uint32_t version, + const char *name, bool is_sender) { + if (client->version > EIS_PROTOCOL_VERSION) + return -EPROTO; + + client->version = version; + if (client->name == NULL) client->name = xstrdup(name); diff --git a/src/libeis-private.h b/src/libeis-private.h index a3896d7..aadf2b2 100644 --- a/src/libeis-private.h +++ b/src/libeis-private.h @@ -68,6 +68,7 @@ struct eis_client { void *user_data; struct list link; struct source *source; + uint32_t version; uint32_t id; enum eis_client_state state; char *name; diff --git a/src/libeis-proto.c b/src/libeis-proto.c index 207260e..3f24568 100644 --- a/src/libeis-proto.c +++ b/src/libeis-proto.c @@ -524,7 +524,8 @@ eis_proto_handle_message(struct eis_client *client, int rc; switch (proto->msg_case) { case CLIENT_MESSAGE__MSG_CONNECT: - rc = call(connect, client, proto->connect->name, proto->connect->is_sender); + rc = call(connect, client, proto->connect->version, + proto->connect->name, proto->connect->is_sender); break; case CLIENT_MESSAGE__MSG_CONNECT_DONE: rc = call(connect_done, client); diff --git a/src/libeis-proto.h b/src/libeis-proto.h index a1ff587..c52df2a 100644 --- a/src/libeis-proto.h +++ b/src/libeis-proto.h @@ -36,7 +36,7 @@ /* callbacks invoked during eis_proto_parse_message() */ struct eis_proto_interface { - int (*connect)(struct eis_client *client, const char *name, bool is_sender); + int (*connect)(struct eis_client *client, uint32_t version, const char *name, bool is_sender); int (*connect_done)(struct eis_client *client); int (*disconnect)(struct eis_client *client); int (*bind_seat)(struct eis_client *client, uint32_t seatid, enum eis_device_capability cap); diff --git a/src/libeis.h b/src/libeis.h index 546833c..12c76fe 100644 --- a/src/libeis.h +++ b/src/libeis.h @@ -517,6 +517,12 @@ eis_client_set_user_data(struct eis_client *eis_client, void *user_data); const char * eis_client_get_name(struct eis_client *client); +/** + * Return the protocol version supported by this client. + */ +uint32_t +eis_client_get_version(struct eis_client *client); + /** * Allow connection from the client. This can only be done once, further * calls to this functions are ignored. diff --git a/test/eierpecken.c b/test/eierpecken.c index 3a75452..4892326 100644 --- a/test/eierpecken.c +++ b/test/eierpecken.c @@ -731,6 +731,9 @@ _peck_dispatch_eis(struct peck *peck, int lineno) switch (eis_event_get_type(e)) { case EIS_EVENT_CLIENT_CONNECT: case EIS_EVENT_CLIENT_DISCONNECT: + /* version is currently hardcoded, remove when necessary */ + munit_assert_int(eis_client_get_version(eis_event_get_client(e)), ==, 1); + if (flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_ACCEPT_CLIENT) || flag_is_set(peck->eis_behavior, PECK_EIS_BEHAVIOR_REJECT_CLIENT)) process_event = tristate_yes; diff --git a/test/test-eis.c b/test/test-eis.c index cc7b3aa..f6d90d9 100644 --- a/test/test-eis.c +++ b/test/test-eis.c @@ -25,6 +25,7 @@ #include "config.h" #include "util-munit.h" +#include "util-version.h" #include "eierpecken.h" @@ -45,6 +46,26 @@ MUNIT_TEST(eistest_ref_unref) return MUNIT_OK; } +MUNIT_TEST(eistest_version) +{ + _unref_(peck) *peck = peck_new(); + + peck_enable_eis_behavior(peck, PECK_EIS_BEHAVIOR_NONE); + peck_enable_ei_behavior(peck, PECK_EI_BEHAVIOR_HANDLE_CONNECT); + + peck_dispatch_until_stable(peck); + + with_server(peck) { + _unref_(eis_event) *connect = peck_eis_next_event(eis, EIS_EVENT_CLIENT_CONNECT); + struct eis_client *client = eis_event_get_client(connect); + + /* We can't set the client version yet, but we know it's hardcoded to 1 */ + munit_assert_int(eis_client_get_version(client), ==, VERSION_V(1)); + } + + return MUNIT_OK; +} + MUNIT_TEST(eistest_name) { _unref_(peck) *peck = peck_new();