Replace the custom message parsing with protobuf

Plain-text was useful for the initial implementation where the counterpart was
netcat but now that both parts are in place, protobuf is a much more
convenient system to handle a frequently-changing protocol.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer 2020-07-31 12:24:44 +10:00
parent f94a7bae85
commit 9f851bcccb
5 changed files with 250 additions and 130 deletions

View file

@ -15,6 +15,8 @@ add_project_arguments(cppflags, language: 'cpp')
config_h = configuration_data()
config_h.set('_GNU_SOURCE', '1')
subdir('proto')
lib_util = static_library('util',
'src/util-io.h',
'src/util-list.h',
@ -37,7 +39,8 @@ lib_libei = shared_library('ei',
'src/libei.c',
'src/libei-device.c',
'src/libei-socket.c',
dependencies: [dep_libutil],
proto_headers,
dependencies: [dep_libutil, dep_protobuf],
install: true
)
install_headers('src/libei.h')
@ -59,7 +62,8 @@ lib_libeis = shared_library('eis',
'src/libeis-client.c',
'src/libeis-device.c',
'src/libeis-socket.c',
dependencies: [dep_libutil],
proto_headers,
dependencies: [dep_libutil, dep_protobuf],
install: true
)
install_headers('src/libeis.h')

60
proto/ei.proto Normal file
View file

@ -0,0 +1,60 @@
syntax = "proto3";
message Connect {
string name = 1;
}
message Disconnect {
}
message AddDevice {
uint32 deviceid = 1;
uint32 capabilities = 2;
}
message RemoveDevice {
uint32 deviceid = 1;
}
message PointerRelative {
uint32 deviceid = 1;
int32 x = 2;
int32 y = 3;
}
message ClientMessage {
oneof msg {
Connect connect = 1;
Disconnect disconnect = 2;
AddDevice add = 3;
RemoveDevice remove = 4;
PointerRelative rel = 5;
}
}
message Hello {
}
message Connected {
}
message Disconnected {
}
message Accepted {
uint32 deviceid = 1;
}
message Removed {
uint32 deviceid = 1;
}
message ServerMessage {
oneof msg {
Hello hello = 1;
Connected connected = 2;
Disconnected disconnected = 3;
Accepted accepted = 4;
Removed removed = 5;
}
}

9
proto/meson.build Normal file
View file

@ -0,0 +1,9 @@
dep_protobuf = dependency('libprotobuf-c')
protoc = find_program('protoc')
proto_headers = custom_target('proto-headers',
input: 'ei.proto',
output: ['ei.pb-c.c', 'ei.pb-c.h'],
command: [protoc,
'--proto_path=@0@'.format(meson.current_source_dir()),
'--c_out=@0@'.format(meson.current_build_dir()),
'ei.proto'])

View file

@ -38,6 +38,8 @@
#include "libei.h"
#include "libei-private.h"
#include "proto/ei.pb-c.h"
/* The message type for the wire format */
enum message_type {
MESSAGE_HELLO,
@ -210,19 +212,28 @@ ei_queue_removed_event(struct ei_device *device)
ei_queue_event(ei, &e->base);
}
static int
connection_send_msg(struct ei *ei, const ClientMessage *msg)
{
size_t sz = client_message__get_packed_size(msg);
uint8_t buf[sz];
size_t len = client_message__pack(msg, buf);
return min(0, xwrite(source_get_fd(ei->source), buf, len));
}
static int
connection_send_connect(struct ei *ei)
{
if (ei->state == EI_STATE_DISCONNECTED)
return 0;
char buf[128];
ClientMessage msg = CLIENT_MESSAGE__INIT;
Connect connect = CONNECT__INIT;
if (!xsnprintf(buf, sizeof(buf), "connect %s\n",
ei->name ? ei->name : "unnamed"))
return -ENOMEM;
msg.connect = &connect;
msg.msg_case = CLIENT_MESSAGE__MSG_CONNECT;
return min(0, xwrite(source_get_fd(ei->source), buf, strlen(buf) + 1));
return connection_send_msg(ei, &msg);
}
static int
@ -231,8 +242,13 @@ connection_send_disconnect(struct ei *ei)
if (ei->state == EI_STATE_DISCONNECTED)
return 0;
const char buf[] = "disconnect\n";
return min(0, xwrite(source_get_fd(ei->source), buf, sizeof(buf)));
ClientMessage msg = CLIENT_MESSAGE__INIT;
Disconnect disconnect = DISCONNECT__INIT;
msg.disconnect = &disconnect;
msg.msg_case = CLIENT_MESSAGE__MSG_DISCONNECT;
return connection_send_msg(ei, &msg);
}
static int
@ -241,12 +257,16 @@ connection_send_add(struct ei *ei, struct ei_device *device)
if (ei->state == EI_STATE_DISCONNECTED)
return 0;
char buf[64];
ClientMessage msg = CLIENT_MESSAGE__INIT;
AddDevice add = ADD_DEVICE__INIT;
if (!xsnprintf(buf, sizeof(buf), "add %d %d\n", device->id, device->capabilities))
return -ENOMEM;
add.deviceid = device->id;
add.capabilities = device->capabilities;
return min(0, xwrite(source_get_fd(ei->source), buf, strlen(buf) + 1));
msg.add = &add;
msg.msg_case = CLIENT_MESSAGE__MSG_ADD;
return connection_send_msg(ei, &msg);
}
static int
@ -255,12 +275,15 @@ connection_send_remove(struct ei *ei, struct ei_device *device)
if (ei->state == EI_STATE_DISCONNECTED)
return 0;
char buf[64];
ClientMessage msg = CLIENT_MESSAGE__INIT;
RemoveDevice remove = REMOVE_DEVICE__INIT;
if (!xsnprintf(buf, sizeof(buf), "remove %d\n", device->id))
return -ENOMEM;
remove.deviceid = device->id;
return min(0, xwrite(source_get_fd(ei->source), buf, strlen(buf) + 1));
msg.remove = &remove;
msg.msg_case = CLIENT_MESSAGE__MSG_REMOVE;
return connection_send_msg(ei, &msg);
}
static int
@ -269,12 +292,17 @@ connection_send_rel(struct ei *ei, struct ei_device *device, int32_t x, int32_t
if (ei->state == EI_STATE_DISCONNECTED)
return 0;
char buf[64];
ClientMessage msg = CLIENT_MESSAGE__INIT;
PointerRelative rel = POINTER_RELATIVE__INIT;
if (!xsnprintf(buf, sizeof(buf), "rel %d %d %d\n", device->id, x, y))
return -ENOMEM;
rel.deviceid = device->id;
rel.x = x;
rel.y = y;
return min(0, xwrite(source_get_fd(ei->source), buf, strlen(buf) + 1));
msg.rel = &rel;
msg.msg_case = CLIENT_MESSAGE__MSG_REL;
return connection_send_msg(ei, &msg);
}
static void
@ -379,52 +407,52 @@ static struct message *
connection_parse_message(const char *data_in, size_t len)
{
_cleanup_(message_freep) struct message *msg = xalloc(sizeof(*msg));
_cleanup_free_ char *data = strstrip(data_in, "\n");
_cleanup_(strv_freep) char **tokens = strv_from_string(data, " ");
ServerMessage *proto = server_message__unpack(NULL, len,
(const unsigned char*)data_in);
if (!proto)
return NULL;
if (streq(tokens[0], "hello")) {
switch (proto->msg_case) {
case SERVER_MESSAGE__MSG_HELLO:
*msg = (struct message) {
.type = MESSAGE_HELLO,
};
} else if (streq(tokens[0], "connected")) {
break;
case SERVER_MESSAGE__MSG_CONNECTED:
*msg = (struct message) {
.type = MESSAGE_CONNECTED,
};
} else if (streq(tokens[0], "disconnected")) {
break;
case SERVER_MESSAGE__MSG_DISCONNECTED:
*msg = (struct message) {
.type = MESSAGE_DISCONNECTED,
};
} else if (streq(tokens[0], "accept")) {
if (!tokens[1])
goto error;
uint32_t deviceid;
if (!xatou(tokens[1], &deviceid))
goto error;
*msg = (struct message) {
.type = MESSAGE_ACCEPT,
.accept.deviceid = deviceid,
};
} else if (streq(tokens[0], "remove")) {
if (!tokens[1])
goto error;
uint32_t deviceid;
if (!xatou(tokens[1], &deviceid))
goto error;
*msg = (struct message) {
.type = MESSAGE_REMOVE,
.remove.deviceid = deviceid,
};
} else {
goto error;
break;
case SERVER_MESSAGE__MSG_ACCEPTED:
{
Accepted *a = proto->accepted;
*msg = (struct message) {
.type = MESSAGE_ACCEPT,
.accept.deviceid = a->deviceid,
};
}
break;
case SERVER_MESSAGE__MSG_REMOVED:
{
Removed *r = proto->removed;
*msg = (struct message) {
.type = MESSAGE_REMOVE,
.remove.deviceid = r->deviceid,
};
}
break;
default:
server_message__free_unpacked(proto, NULL);
return NULL;
}
server_message__free_unpacked(proto, NULL);
return steal(&msg);
error:
return NULL;
}
static int

View file

@ -38,6 +38,8 @@
#include "libeis-private.h"
#include "proto/ei.pb-c.h"
/* The message type for the wire format */
enum message_type {
MESSAGE_CONNECT,
@ -112,47 +114,75 @@ eis_client_get_context(struct eis_client *client)
return eis_client_parent(client);
}
static int
client_send_msg(struct eis_client *client, const ServerMessage *msg)
{
size_t sz = server_message__get_packed_size(msg);
uint8_t buf[sz];
size_t len = server_message__pack(msg, buf);
return min(0, xwrite(source_get_fd(client->source), buf, len));
}
static int
client_send_hello(struct eis_client *client)
{
const char buf[] = "hello\n";
return min(0, xwrite(source_get_fd(client->source), buf, sizeof(buf)));
ServerMessage msg = SERVER_MESSAGE__INIT;
Hello hello = HELLO__INIT;
msg.hello = &hello;
msg.msg_case = SERVER_MESSAGE__MSG_HELLO;
return client_send_msg(client, &msg);
}
static int
client_send_disconnect(struct eis_client *client)
{
const char buf[] = "disconnected\n";
return min(0, xwrite(source_get_fd(client->source), buf, sizeof(buf)));
ServerMessage msg = SERVER_MESSAGE__INIT;
Disconnected disconnected = DISCONNECTED__INIT;
msg.disconnected = &disconnected;
msg.msg_case = SERVER_MESSAGE__MSG_DISCONNECTED;
return client_send_msg(client, &msg);
}
static int
client_send_connect(struct eis_client *client)
{
const char buf[] = "connected\n";
return min(0, xwrite(source_get_fd(client->source), buf, sizeof(buf)));
ServerMessage msg = SERVER_MESSAGE__INIT;
Connected connected = CONNECTED__INIT;
msg.connected = &connected;
msg.msg_case = SERVER_MESSAGE__MSG_CONNECTED;
return client_send_msg(client, &msg);
}
static int
client_send_accepted(struct eis_client *client, struct eis_device *device)
{
char buf[64];
ServerMessage msg = SERVER_MESSAGE__INIT;
Accepted accepted = ACCEPTED__INIT;
if (!xsnprintf(buf, sizeof(buf), "accept %d\n", device->id))
return -ENOMEM;
accepted.deviceid = device->id;
return min(0, xwrite(source_get_fd(client->source), buf, strlen(buf) + 1));
msg.accepted = &accepted;
msg.msg_case = SERVER_MESSAGE__MSG_ACCEPTED;
return client_send_msg(client, &msg);
}
static int
client_send_removed(struct eis_client *client, struct eis_device *device)
{
char buf[64];
ServerMessage msg = SERVER_MESSAGE__INIT;
Removed removed = REMOVED__INIT;
if (!xsnprintf(buf, sizeof(buf), "remove %d\n", device->id))
return -ENOMEM;
msg.removed = &removed;
msg.msg_case = SERVER_MESSAGE__MSG_REMOVED;
return min(0, xwrite(source_get_fd(client->source), buf, strlen(buf) + 1));
return client_send_msg(client, &msg);
}
_public_ void
@ -320,76 +350,65 @@ static struct message *
client_parse_message(const char *data_in, size_t len)
{
_cleanup_(message_freep) struct message *msg = xalloc(sizeof(*msg));
_cleanup_free_ char *data = strstrip(data_in, "\n");
_cleanup_(strv_freep) char **tokens = strv_from_string(data, " ");
ClientMessage *proto = client_message__unpack(NULL, len,
(const unsigned char*)data_in);
if (streq(tokens[0], "connect")) {
if (!tokens[1])
goto error;
if (!proto)
return NULL;
char *name = tokens[1];
*msg = (struct message) {
.type = MESSAGE_CONNECT,
.connect.name = xstrdup(name),
};
} else if (streq(tokens[0], "disconnect")) {
switch (proto->msg_case) {
case CLIENT_MESSAGE__MSG_CONNECT:
{
Connect *c = proto->connect;
*msg = (struct message) {
.type = MESSAGE_CONNECT,
.connect.name = xstrdup(c->name),
};
}
break;
case CLIENT_MESSAGE__MSG_DISCONNECT:
*msg = (struct message) {
.type = MESSAGE_DISCONNECT,
};
} else if (streq(tokens[0], "add")) {
if (!tokens[1] || !tokens[2])
goto error;
uint32_t deviceid;
if (!xatou(tokens[1], &deviceid))
goto error;
uint32_t capabilities;
if (!xatou(tokens[2], &capabilities))
goto error;
*msg = (struct message) {
.type = MESSAGE_ADD_DEVICE,
.add_device.deviceid = deviceid,
.add_device.capabilities = capabilities,
};
} else if (streq(tokens[0], "remove")) {
if (!tokens[1])
goto error;
uint32_t deviceid;
if (!xatou(tokens[1], &deviceid))
goto error;
*msg = (struct message) {
.type = MESSAGE_REMOVE_DEVICE,
.remove_device.deviceid = deviceid,
};
} else if (streq(tokens[0], "rel")) {
if (!tokens[1] || !tokens[2] || !tokens[3])
return NULL;
uint32_t deviceid;
if (!xatou(tokens[1], &deviceid))
goto error;
int x, y;
if (!xatoi(tokens[2], &x) || !xatoi(tokens[3], &y))
goto error;
*msg = (struct message) {
.type = MESSAGE_POINTER_REL,
.pointer_rel.deviceid = deviceid,
.pointer_rel.x = x,
.pointer_rel.y = y,
};
} else {
goto error;
break;
case CLIENT_MESSAGE__MSG_ADD:
{
AddDevice *a = proto->add;
*msg = (struct message) {
.type = MESSAGE_ADD_DEVICE,
.add_device.deviceid = a->deviceid,
.add_device.capabilities = a->capabilities,
};
}
break;
case CLIENT_MESSAGE__MSG_REMOVE:
{
RemoveDevice *r = proto->remove;
*msg = (struct message) {
.type = MESSAGE_REMOVE_DEVICE,
.remove_device.deviceid = r->deviceid,
};
}
break;
case CLIENT_MESSAGE__MSG_REL:
{
PointerRelative *r = proto->rel;
*msg = (struct message) {
.type = MESSAGE_POINTER_REL,
.pointer_rel.deviceid = r->deviceid,
.pointer_rel.x = r->x,
.pointer_rel.y = r->y,
};
}
break;
default:
client_message__free_unpacked(proto, NULL);
return NULL;
}
client_message__free_unpacked(proto, NULL);
return steal(&msg);
error:
return NULL;
}
static void