libei/src/libei.c
Peter Hutterer 5c017654e8 ei: always queue a disconnect event locally
In the case of a refused portal connection we get disconnected before we
had a chance to send the CONNECT message. This still needs to queue a
disconnect event and thus bubble back up to the caller.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2021-08-26 16:43:06 +10:00

1039 lines
23 KiB
C

/*
* Copyright © 2020 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "util-io.h"
#include "util-macros.h"
#include "util-object.h"
#include "util-sources.h"
#include "util-strings.h"
#include "libei.h"
#include "libei-private.h"
#include "libei-proto.h"
#include "brei-shared.h"
static struct ei_seat *
ei_find_seat(struct ei *ei, uint32_t seatid)
{
struct ei_seat *seat;
list_for_each(seat, &ei->seats, link) {
if (seat->id == seatid)
return seat;
}
return NULL;
}
static struct ei_device *
ei_find_device(struct ei *ei, uint32_t deviceid)
{
struct ei_seat *seat;
list_for_each(seat, &ei->seats, link) {
struct ei_device *device = ei_seat_find_device(seat, deviceid);
if (device)
return device;
}
return NULL;
}
static void
ei_destroy(struct ei *ei)
{
ei_disconnect(ei);
struct ei_event *e;
while ((e = ei_get_event(ei)) != NULL)
ei_event_unref(e);
if (ei->backend_interface.destroy)
ei->backend_interface.destroy(ei, ei->backend);
ei->backend = NULL;
sink_unref(ei->sink);
free(ei->name);
struct ei_property *prop;
list_for_each_safe(prop, &ei->properties, link) {
ei_property_unref(prop);
}
}
static
OBJECT_IMPLEMENT_CREATE(ei);
_public_
OBJECT_IMPLEMENT_REF(ei);
_public_
OBJECT_IMPLEMENT_UNREF(ei);
_public_
OBJECT_IMPLEMENT_SETTER(ei, user_data, void *);
_public_
OBJECT_IMPLEMENT_GETTER(ei, user_data, void *);
DEFINE_UNREF_CLEANUP_FUNC(ei_device);
DEFINE_UNREF_CLEANUP_FUNC(ei_region);
static void
set_prop_cmdline(struct ei *ei)
{
char path[PATH_MAX];
xsnprintf(path, sizeof(path), "/proc/%u/cmdline", getpid());
_cleanup_close_ int fd = open(path, O_RDONLY);
if (fd < 0)
return;
int len;
if ((len = read(fd, path, sizeof(path) - 1)) < 0)
return;
path[len] = '\0';
ei_property_update(ei, "ei.application.cmdline", path, EI_PROPERTY_PERM_NONE);
}
static void
set_prop_pid(struct ei *ei)
{
char pid[64];
xsnprintf(pid, sizeof(pid), "%u", getpid());
ei_property_update(ei, "ei.application.pid", pid, EI_PROPERTY_PERM_NONE);
}
static void
set_prop_type(struct ei *ei)
{
ei_property_update(ei, "ei.connection.type", "socket", EI_PROPERTY_PERM_NONE);
}
_public_ struct ei *
ei_new(void *user_data)
{
_unref_(ei) *ei = ei_create(NULL);
ei->state = EI_STATE_NEW;
ei->requests = ei_proto_get_requests();
list_init(&ei->event_queue);
list_init(&ei->seats);
list_init(&ei->properties);
ei_log_set_handler(ei, NULL);
ei_log_set_priority(ei, EI_LOG_PRIORITY_INFO);
ei->sink = sink_new();
if (!ei->sink)
return NULL;
ei->user_data = user_data;
ei->backend = NULL;
set_prop_pid(ei);
set_prop_cmdline(ei);
set_prop_type(ei);
return steal(&ei);
}
_public_ int
ei_get_fd(struct ei *ei)
{
return sink_get_fd(ei->sink);
}
_public_ void
ei_dispatch(struct ei *ei)
{
sink_dispatch(ei->sink);
}
static void
queue_event(struct ei *ei, struct ei_event *event)
{
log_debug(ei, "queuing event type %s (%d)\n",
ei_event_type_to_string(event->type), event->type);
list_append(&ei->event_queue, &event->link);
}
static void
insert_event(struct ei *ei, struct ei_event *event)
{
log_debug(ei, "inserting event type %s (%d)\n",
ei_event_type_to_string(event->type), event->type);
list_insert(&ei->event_queue, &event->link);
}
static void
queue_connect_event(struct ei *ei)
{
struct ei_event *e = ei_event_new(ei);
e->type = EI_EVENT_CONNECT;
queue_event(ei, e);
}
static void
queue_disconnect_event(struct ei *ei)
{
struct ei_event *e = ei_event_new(ei);
e->type = EI_EVENT_DISCONNECT;
queue_event(ei, e);
}
static void
queue_seat_added_event(struct ei_seat *seat)
{
struct ei *ei= ei_seat_get_context(seat);
struct ei_event *e = ei_event_new(ei);
e->type = EI_EVENT_SEAT_ADDED;
e->seat = ei_seat_ref(seat);
queue_event(ei, e);
}
static void
queue_seat_removed_event(struct ei_seat *seat)
{
struct ei *ei= ei_seat_get_context(seat);
struct ei_event *e = ei_event_new(ei);
e->type = EI_EVENT_SEAT_REMOVED;
e->seat = ei_seat_ref(seat);
queue_event(ei, e);
}
void
ei_queue_seat_removed_event(struct ei_seat *seat)
{
queue_seat_removed_event(seat);
}
static void
queue_device_added_event(struct ei_device *device)
{
struct ei *ei= ei_device_get_context(device);
struct ei_event *e = ei_event_new(ei);
e->type = EI_EVENT_DEVICE_ADDED;
e->seat = ei_seat_ref(ei_device_get_seat(device));
e->device = ei_device_ref(device);
queue_event(ei, e);
}
static void
queue_device_removed_event(struct ei_device *device)
{
struct ei *ei= ei_device_get_context(device);
struct ei_event *e = ei_event_new(ei);
e->type = EI_EVENT_DEVICE_REMOVED;
e->seat = ei_seat_ref(ei_device_get_seat(device));
e->device = ei_device_ref(device);
queue_event(ei, e);
}
static void
insert_device_removed_event(struct ei_device *device)
{
struct ei *ei= ei_device_get_context(device);
struct ei_event *e = ei_event_new(ei);
e->type = EI_EVENT_DEVICE_REMOVED;
e->seat = ei_seat_ref(ei_device_get_seat(device));
e->device = ei_device_ref(device);
insert_event(ei, e);
}
static void
queue_paused_event(struct ei_device *device)
{
struct ei *ei= ei_device_get_context(device);
struct ei_event *e = ei_event_new(ei);
e->type = EI_EVENT_DEVICE_PAUSED;
e->seat = ei_seat_ref(ei_device_get_seat(device));
e->device = ei_device_ref(device);
queue_event(ei, e);
}
static void
queue_resumed_event(struct ei_device *device)
{
struct ei *ei= ei_device_get_context(device);
struct ei_event *e = ei_event_new(ei);
e->type = EI_EVENT_DEVICE_RESUMED;
e->seat = ei_seat_ref(ei_device_get_seat(device));
e->device = ei_device_ref(device);
queue_event(ei, e);
}
static void
queue_keyboard_modifiers_event(struct ei_device *device,
const struct ei_xkb_modifiers *mods)
{
struct ei *ei= ei_device_get_context(device);
struct ei_event *e = ei_event_new(ei);
e->type = EI_EVENT_KEYBOARD_MODIFIERS;
e->seat = ei_seat_ref(ei_device_get_seat(device));
e->device = ei_device_ref(device);
e->modifiers = *mods;
queue_event(ei, e);
}
static void
queue_property_event(struct ei *ei, const char *name,
const char *value, uint32_t permissions)
{
struct ei_event *e = ei_event_new(ei);
e->type = EI_EVENT_PROPERTY;
e->prop.name = xstrdup(name);
e->prop.value = xstrdup(value);
e->prop.permissions = permissions;
queue_event(ei, e);
}
void
ei_disconnect(struct ei *ei)
{
if (ei->state == EI_STATE_DISCONNECTED ||
ei->state == EI_STATE_DISCONNECTING)
return;
enum ei_state state = ei->state;
/* We need the disconnecting state to be re-entrant
ei_device_remove() may call ei_disconnect() on a socket error */
ei->state = EI_STATE_DISCONNECTING;
struct ei_seat *seat;
list_for_each_safe(seat, &ei->seats, link) {
ei_seat_remove(seat);
}
if (state != EI_STATE_NEW) {
ei->requests->disconnect(ei);
}
queue_disconnect_event(ei);
ei->state = EI_STATE_DISCONNECTED;
if (ei->source)
source_remove(ei->source);
ei->source = source_unref(ei->source);
}
static int
handle_msg_seat_added(struct ei *ei, uint32_t seatid,
const char *name, uint32_t capabilities)
{
log_debug(ei, "Added seat %#x '%s' with caps %#x\n",
seatid, name, capabilities);
struct ei_seat *seat = ei_seat_new(ei, seatid, name, capabilities);
/* seats list owns the ref */
list_append(&ei->seats, &seat->link);
queue_seat_added_event(seat);
return 0;
}
static int
handle_msg_seat_removed(struct ei *ei, uint32_t seatid)
{
log_debug(ei, "server removed seat %#x\n", seatid);
struct ei_seat *seat = ei_find_seat(ei, seatid);
if (seat) {
ei_seat_remove(seat);
}
return 0;
}
static int
handle_msg_device_added(struct ei *ei, uint32_t deviceid, uint32_t seatid,
const char *name, uint32_t capabilities)
{
struct ei_seat *seat = ei_find_seat(ei, seatid);
if (!seat) {
log_bug(ei, "Invalid seat id %d for device %s (%d)\n",
seatid, name, deviceid);
return 0;
}
/* Wrong device id or a device already removed by the client but we
* won't know which unless we keep some device ID table. Not worth
* it, so just silently ignore */
if (ei_seat_find_device(seat, deviceid)) {
log_error(ei, "Server sent duplicate device id %#x\n", deviceid);
return -EINVAL;
}
_unref_(ei_device) *device = ei_device_new(seat, deviceid);
ei_device_set_name(device, name);
ei_device_set_capabilities(device, capabilities);
ei_device_added(device);
log_debug(ei,
"Added device %#x '%s' caps: %s%s%s%s seat: %s\n",
deviceid, name,
ei_device_has_capability(device, EI_DEVICE_CAP_POINTER) ? "p" : "",
ei_device_has_capability(device, EI_DEVICE_CAP_POINTER_ABSOLUTE) ? "a" : "",
ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD) ? "k" : "",
ei_device_has_capability(device, EI_DEVICE_CAP_TOUCH) ? "t" : "",
ei_seat_get_name(seat));
return 0;
}
static int
handle_msg_device_keymap(struct ei *ei, uint32_t deviceid,
enum ei_keymap_type keymap_type,
int keymap_fd, size_t keymap_sz)
{
log_debug(ei, "Adding keymap for %#x\n", deviceid);
struct ei_device *device = ei_find_device(ei, deviceid);
if (!device)
return 0;
ei_device_set_keymap(device, keymap_type, keymap_fd, keymap_sz);
return 0;
}
void
ei_queue_device_removed_event(struct ei_device *device)
{
queue_device_removed_event(device);
}
void
ei_insert_device_removed_event(struct ei_device *device)
{
insert_device_removed_event(device);
}
static int
handle_msg_device_added_done(struct ei *ei, uint32_t deviceid)
{
log_debug(ei, "Done with device %#x\n", deviceid);
struct ei_device *device = ei_find_device(ei, deviceid);
if (!device)
return 0;
queue_device_added_event(device);
ei_device_done(device);
return 0;
}
static int
handle_msg_device_region(struct ei *ei, uint32_t deviceid,
uint32_t x, uint32_t y,
uint32_t w, uint32_t h,
double scale)
{
log_debug(ei, "Adding device region for %#x\n", deviceid);
struct ei_device *device = ei_find_device(ei, deviceid);
if (!device)
return 0;
_unref_(ei_region) *r = ei_region_new();
ei_region_set_offset(r, x, y);
ei_region_set_size(r, w, h);
ei_region_set_physical_scale(r, scale);
ei_device_add_region(device, r);
return 0;
}
static int
handle_msg_keyboard_modifiers(struct ei *ei, uint32_t deviceid,
uint32_t depressed, uint32_t latched,
uint32_t locked, uint32_t group)
{
log_debug(ei, "Setting modifiers for %#x\n", deviceid);
struct ei_device *device = ei_find_device(ei, deviceid);
if (!device)
return 0;
if (!ei_device_has_capability(device, EI_DEVICE_CAP_KEYBOARD)) {
log_bug(ei,"Modifier event for non-keyboard\n");
return -EPROTO;
}
struct ei_xkb_modifiers mods = {
.depressed = depressed,
.latched = latched,
.locked = locked,
.group = group,
};
queue_keyboard_modifiers_event(device, &mods);
return 0;
}
static int
handle_msg_property(struct ei *ei, const char *name, const char *value,
uint32_t permissions)
{
int rc = ei_property_update(ei, name, value, permissions);
if (rc == 0)
queue_property_event(ei, name, value, permissions);
return 0;
}
static int
handle_msg_device_removed(struct ei *ei, uint32_t deviceid)
{
log_debug(ei, "Removed device %#x\n", deviceid);
struct ei_device *device = ei_find_device(ei, deviceid);
if (!device)
return 0;
ei_device_removed_by_server(device);
return 0;
}
static int
handle_msg_device_resumed(struct ei *ei, uint32_t deviceid)
{
log_debug(ei, "Resumed device %#x\n", deviceid);
struct ei_device *device = ei_find_device(ei, deviceid);
if (device) {
ei_device_resumed(device);
queue_resumed_event(device);
}
return 0;
}
static int
handle_msg_device_paused(struct ei *ei, uint32_t deviceid)
{
log_debug(ei, "Paused device %d\n", deviceid);
struct ei_device *device = ei_find_device(ei, deviceid);
if (device) {
ei_device_paused(device);
queue_paused_event(device);
}
return 0;
}
int
ei_send_property(struct ei *ei, const char *name, const char *value, uint32_t permissions)
{
/* properties before CONNECTED_DONE are handled in a custom way */
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->property(ei, name, value, permissions);
if (rc)
ei_disconnect(ei);
return rc;
}
int
ei_send_close_device(struct ei_device *device)
{
struct ei *ei = ei_device_get_context(device);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->close_device(device);
if (rc)
ei_disconnect(ei);
return rc;
}
int
ei_send_start_emulating(struct ei_device *device)
{
struct ei *ei = ei_device_get_context(device);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->start_emulating(device);
if (rc)
ei_disconnect(ei);
return rc;
}
int
ei_send_stop_emulating(struct ei_device *device)
{
struct ei *ei = ei_device_get_context(device);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->stop_emulating(device);
if (rc)
ei_disconnect(ei);
return rc;
}
int
ei_send_seat_bind(struct ei_seat *seat, uint32_t capabilities)
{
struct ei *ei = ei_seat_get_context(seat);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->bind_seat(seat, capabilities);
if (rc)
ei_disconnect(ei);
return rc;
}
int
ei_send_seat_unbind(struct ei_seat *seat)
{
struct ei *ei = ei_seat_get_context(seat);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->unbind_seat(seat);
if (rc)
ei_disconnect(ei);
return rc;
}
int
ei_send_frame(struct ei_device *device)
{
struct ei *ei = ei_device_get_context(device);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->frame(device);
if (rc)
ei_disconnect(ei);
return rc;
}
int
ei_send_pointer_rel(struct ei_device *device, double x, double y)
{
struct ei *ei = ei_device_get_context(device);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->rel(device, x, y);
if (rc)
ei_disconnect(ei);
return rc;
}
int
ei_send_pointer_abs(struct ei_device *device, double x, double y)
{
struct ei *ei = ei_device_get_context(device);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->abs(device, x, y);
if (rc)
ei_disconnect(ei);
return rc;
}
int
ei_send_pointer_button(struct ei_device *device, uint32_t button, bool is_press)
{
struct ei *ei = ei_device_get_context(device);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->button(device, button, is_press);
if (rc)
ei_disconnect(ei);
return rc;
}
int ei_send_pointer_scroll(struct ei_device *device, double x, double y)
{
struct ei *ei = ei_device_get_context(device);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->scroll(device, x, y);
if (rc)
ei_disconnect(ei);
return rc;
}
int ei_send_pointer_scroll_stop(struct ei_device *device, double x, double y)
{
struct ei *ei = ei_device_get_context(device);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->scroll_stop(device, x, y);
if (rc)
ei_disconnect(ei);
return rc;
}
int ei_send_pointer_scroll_cancel(struct ei_device *device, double x, double y)
{
struct ei *ei = ei_device_get_context(device);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->scroll_cancel(device, x, y);
if (rc)
ei_disconnect(ei);
return rc;
}
int ei_send_pointer_scroll_discrete(struct ei_device *device, int32_t x, int32_t y)
{
struct ei *ei = ei_device_get_context(device);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->scroll_discrete(device, x, y);
if (rc)
ei_disconnect(ei);
return rc;
}
int
ei_send_keyboard_key(struct ei_device *device, uint32_t key, bool is_press)
{
struct ei *ei = ei_device_get_context(device);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->key(device, key, is_press);
if (rc)
ei_disconnect(ei);
return rc;
}
int
ei_send_touch_down(struct ei_device *device, uint32_t tid,
double x, double y)
{
struct ei *ei = ei_device_get_context(device);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->touch_down(device, tid, x, y);
if (rc)
ei_disconnect(ei);
return rc;
}
int
ei_send_touch_motion(struct ei_device *device, uint32_t tid,
double x, double y)
{
struct ei *ei = ei_device_get_context(device);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->touch_motion(device, tid, x, y);
if (rc)
ei_disconnect(ei);
return rc;
}
int
ei_send_touch_up(struct ei_device *device, uint32_t tid)
{
struct ei *ei = ei_device_get_context(device);
if (ei->state == EI_STATE_NEW || ei->state == EI_STATE_DISCONNECTED)
return 0;
int rc = ei->requests->touch_up(device, tid);
if (rc)
ei_disconnect(ei);
return rc;
}
_public_ struct ei_event*
ei_get_event(struct ei *ei)
{
if (list_empty(&ei->event_queue))
return NULL;
struct ei_event *e = list_first_entry(&ei->event_queue, e, link);
list_remove(&e->link);
return e;
}
_public_ struct ei_event*
ei_peek_event(struct ei *ei)
{
if (list_empty(&ei->event_queue))
return NULL;
struct ei_event *e = list_first_entry(&ei->event_queue, e, link);
return ei_event_ref(e);
}
static int handle_msg_connected(struct ei *ei) {
ei->state = EI_STATE_CONNECTED;
queue_connect_event(ei);
return 0;
}
static int handle_msg_disconnected(struct ei *ei) {
return -ECANCELED;
}
static const struct ei_proto_interface intf_state_backend = {
/* Everything triggers -EPROTO */
.connected = NULL,
};
static const struct ei_proto_interface intf_state_connecting = {
.connected = handle_msg_connected,
.disconnected = handle_msg_disconnected,
};
static const struct ei_proto_interface intf_state_connected = {
.disconnected = handle_msg_disconnected,
.seat_added = handle_msg_seat_added,
.seat_removed = handle_msg_seat_removed,
.device_added = handle_msg_device_added,
.device_removed = handle_msg_device_removed,
.device_resumed = handle_msg_device_resumed,
.device_paused = handle_msg_device_paused,
.device_region = handle_msg_device_region,
.device_keymap = handle_msg_device_keymap,
.device_done = handle_msg_device_added_done,
.keyboard_modifiers = handle_msg_keyboard_modifiers,
.property = handle_msg_property,
};
static const struct ei_proto_interface *interfaces[] = {
[EI_STATE_NEW] = NULL,
[EI_STATE_BACKEND] = &intf_state_backend,
[EI_STATE_CONNECTING] = &intf_state_connecting,
[EI_STATE_CONNECTED] = &intf_state_connected,
[EI_STATE_DISCONNECTING] = NULL,
[EI_STATE_DISCONNECTED] = NULL,
};
static int
connection_message_callback(struct brei_message *bmsg, void *userdata)
{
struct ei *ei = userdata;
assert(ei->state < ARRAY_LENGTH(interfaces));
const struct ei_proto_interface *intf = interfaces[ei->state];
return ei_proto_handle_message(ei, intf, bmsg);
}
static void
connection_dispatch(struct source *source, void *userdata)
{
struct ei *ei = userdata;
enum ei_state old_state = ei->state;
int rc = brei_dispatch(source_get_fd(source), connection_message_callback, ei);
if (rc < 0)
ei_disconnect(ei);
static const char *states[] = {
"NEW",
"BACKEND",
"CONNECTING",
"CONNECTED",
"DISCONNECTED",
"DISCONNECTING",
};
if (rc == -ECANCELED)
log_info(ei, "Disconnected\n");
else if (rc)
log_warn(ei, "Connnection error: %s\n", strerror(-rc));
if (old_state != ei->state)
log_debug(ei, "Connnection dispatch: %s -> %s\n",
states[old_state],
states[ei->state]);
}
int
ei_set_connection(struct ei *ei, int fd)
{
struct source *source = source_new(fd, connection_dispatch, ei);
int rc = sink_add_source(ei->sink, source);
if (rc == 0) {
ei->source = source_ref(source);
ei->state = EI_STATE_BACKEND;
rc = ei->requests->connect(ei);
struct ei_property *prop;
list_for_each_safe(prop, &ei->properties, link) {
if (rc == 0)
rc = ei->requests->property(ei, prop->name, prop->value, prop->permissions);
}
if (rc == 0) {
rc = ei->requests->connect_done(ei);
}
if (rc == 0) {
ei->state = EI_STATE_CONNECTING;
}
if (rc != 0) {
log_error(ei, "message failed to send: %s\n", strerror(-rc));
ei_disconnect(ei);
}
}
source_unref(source);
return rc;
}
_public_ void
ei_configure_name(struct ei *ei, const char *name)
{
if (ei->state != EI_STATE_NEW) {
log_bug_client(ei,"Client is already connected\n");
return;
}
if (strlen(name) > 1024) {
log_bug_client(ei, "Client name too long\n");
return;
}
free(ei->name);
ei->name = xstrdup(name);
}
#ifdef _enable_tests_
#include "util-munit.h"
MUNIT_TEST(test_init_unref)
{
struct ei *ei = ei_new(NULL);
munit_assert_int(ei->state, ==, EI_STATE_NEW);
munit_assert(list_empty(&ei->event_queue));
munit_assert(list_empty(&ei->seats));
munit_assert_not_null(ei->sink);
struct ei *refd = ei_ref(ei);
munit_assert_ptr_equal(ei, refd);
munit_assert_int(ei->object.refcount, ==, 2);
struct ei *unrefd = ei_unref(ei);
munit_assert_null(unrefd);
unrefd = ei_unref(ei);
munit_assert_null(unrefd);
return MUNIT_OK;
}
MUNIT_TEST(test_configure_name)
{
struct ei *ei = ei_new(NULL);
ei_configure_name(ei, "foo");
munit_assert_string_equal(ei->name, "foo");
ei_configure_name(ei, "bar");
munit_assert_string_equal(ei->name, "bar");
/* ignore names that are too long */
char buf[1200] = {0};
memset(buf, 'a', sizeof(buf) - 1);
ei_configure_name(ei, buf);
munit_assert_string_equal(ei->name, "bar");
/* ignore names in all other states */
for (enum ei_state state = EI_STATE_NEW + 1;
state <= EI_STATE_DISCONNECTED;
state++) {
ei->state = state;
ei_configure_name(ei, "expect ignored");
munit_assert_string_equal(ei->name, "bar");
}
ei_unref(ei);
return MUNIT_OK;
}
#endif