From 42d29b6a89f0e5a0bed5c94546e1ae2f53a667dc Mon Sep 17 00:00:00 2001 From: Christophe Fergeau Date: Wed, 23 Mar 2011 17:43:51 +0100 Subject: autotools: move source files to a src/ subdir --- Makefile.am | 24 +- console-kit.c | 360 ------------ console-kit.h | 39 -- src/console-kit.c | 360 ++++++++++++ src/console-kit.h | 39 ++ src/udscs.c | 572 ++++++++++++++++++ src/udscs.h | 126 ++++ src/vdagent-virtio-port.c | 357 ++++++++++++ src/vdagent-virtio-port.h | 92 +++ src/vdagent-x11.c | 1307 ++++++++++++++++++++++++++++++++++++++++++ src/vdagent-x11.h | 48 ++ src/vdagent.c | 220 +++++++ src/vdagentd-proto-strings.h | 34 ++ src/vdagentd-proto.h | 44 ++ src/vdagentd-uinput.c | 212 +++++++ src/vdagentd-uinput.h | 38 ++ src/vdagentd.c | 771 +++++++++++++++++++++++++ udscs.c | 572 ------------------ udscs.h | 126 ---- vdagent-virtio-port.c | 357 ------------ vdagent-virtio-port.h | 92 --- vdagent-x11.c | 1307 ------------------------------------------ vdagent-x11.h | 48 -- vdagent.c | 220 ------- vdagentd-proto-strings.h | 34 -- vdagentd-proto.h | 44 -- vdagentd-uinput.c | 212 ------- vdagentd-uinput.h | 38 -- vdagentd.c | 771 ------------------------- 29 files changed, 4232 insertions(+), 4232 deletions(-) delete mode 100644 console-kit.c delete mode 100644 console-kit.h create mode 100644 src/console-kit.c create mode 100644 src/console-kit.h create mode 100644 src/udscs.c create mode 100644 src/udscs.h create mode 100644 src/vdagent-virtio-port.c create mode 100644 src/vdagent-virtio-port.h create mode 100644 src/vdagent-x11.c create mode 100644 src/vdagent-x11.h create mode 100644 src/vdagent.c create mode 100644 src/vdagentd-proto-strings.h create mode 100644 src/vdagentd-proto.h create mode 100644 src/vdagentd-uinput.c create mode 100644 src/vdagentd-uinput.h create mode 100644 src/vdagentd.c delete mode 100644 udscs.c delete mode 100644 udscs.h delete mode 100644 vdagent-virtio-port.c delete mode 100644 vdagent-virtio-port.h delete mode 100644 vdagent-x11.c delete mode 100644 vdagent-x11.h delete mode 100644 vdagent.c delete mode 100644 vdagentd-proto-strings.h delete mode 100644 vdagentd-proto.h delete mode 100644 vdagentd-uinput.c delete mode 100644 vdagentd-uinput.h delete mode 100644 vdagentd.c diff --git a/Makefile.am b/Makefile.am index 634db79..aa9ef43 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,16 +9,16 @@ spice_vdagent_SOURCES = src/vdagent.c src/vdagent-x11.c src/udscs.c spice_vdagentd_CFLAGS = $(DBUS_CFLAGS) $(SPICE_CFLAGS) spice_vdagentd_LDADD = $(DBUS_LIBS) $(SPICE_LIBS) -spice_vdagentd_SOURCES = vdagentd.c \ - vdagentd-uinput.c \ - vdagent-virtio-port.c \ - console-kit.c \ - udscs.c +spice_vdagentd_SOURCES = src/vdagentd.c \ + src/vdagentd-uinput.c \ + src/vdagent-virtio-port.c \ + src/console-kit.c \ + src/udscs.c -noinst_HEADERS = console-kit.h \ - vdagentd-proto-strings.h \ - vdagent-x11.h \ - udscs.h \ - vdagentd-uinput.h \ - vdagentd-proto.h \ - vdagent-virtio-port.h +noinst_HEADERS = src/console-kit.h \ + src/vdagentd-proto-strings.h \ + src/vdagent-x11.h \ + src/udscs.h \ + src/vdagentd-uinput.h \ + src/vdagentd-proto.h \ + src/vdagent-virtio-port.h diff --git a/console-kit.c b/console-kit.c deleted file mode 100644 index eb15939..0000000 --- a/console-kit.c +++ /dev/null @@ -1,360 +0,0 @@ -/* console-kit.h vdagentd ConsoleKit integration code - - Copyright 2010 Red Hat, Inc. - - Red Hat Authors: - Hans de Goede - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include "console-kit.h" -#include -#include -#include -#include -#include - -struct console_kit { - DBusConnection *connection; - int fd; - char *seat; - char *active_session; - FILE *errfile; -}; - -static char *console_kit_get_first_seat(struct console_kit *ck); -static char *console_kit_check_active_session_change(struct console_kit *ck); - -struct console_kit *console_kit_create(FILE *errfile) -{ - struct console_kit *ck; - DBusError error; - char match[1024]; - - ck = calloc(1, sizeof(*ck)); - if (!ck) - return NULL; - - ck->errfile = errfile; - - dbus_error_init(&error); - ck->connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); - if (ck->connection == NULL || dbus_error_is_set(&error)) { - if (dbus_error_is_set(&error)) { - fprintf(ck->errfile, "Unable to connect to system bus: %s\n", - error.message); - dbus_error_free(&error); - } else - fprintf(ck->errfile, "Unable to connect to system bus\n"); - free(ck); - return NULL; - } - - if (!dbus_connection_get_unix_fd(ck->connection, &ck->fd)) { - fprintf(ck->errfile, "Unable to get connection fd\n"); - console_kit_destroy(ck); - return NULL; - } - - if (!console_kit_get_first_seat(ck)) { - console_kit_destroy(ck); - return NULL; - } - - /* Register for active session changes */ - snprintf(match, sizeof(match), - "type='signal',interface='org.freedesktop.ConsoleKit.Seat'," - "path='%s',member='ActiveSessionChanged'", ck->seat); - dbus_error_init(&error); - dbus_bus_add_match(ck->connection, match, &error); - if (dbus_error_is_set(&error)) { - fprintf(ck->errfile, "Match Error (%s)\n", error.message); - console_kit_destroy(ck); - return NULL; - } - - return ck; -} - -void console_kit_destroy(struct console_kit *ck) -{ - if (!ck) - return; - - dbus_connection_close(ck->connection); - free(ck->seat); - free(ck->active_session); - free(ck); -} - -int console_kit_get_fd(struct console_kit *ck) -{ - return ck->fd; -} - -static char *console_kit_get_first_seat(struct console_kit *ck) -{ - DBusError error; - DBusMessage *message = NULL; - DBusMessage *reply = NULL; - DBusMessageIter iter, subiter; - int type; - char *seat = NULL; - - message = dbus_message_new_method_call("org.freedesktop.ConsoleKit", - "/org/freedesktop/ConsoleKit/Manager", - "org.freedesktop.ConsoleKit.Manager", - "GetSeats"); - if (message == NULL) { - fprintf(ck->errfile, "Unable to create dbus message\n"); - goto exit; - } - - dbus_error_init(&error); - reply = dbus_connection_send_with_reply_and_block(ck->connection, - message, - -1, - &error); - if (reply == NULL || dbus_error_is_set(&error)) { - if (dbus_error_is_set(&error)) { - fprintf(ck->errfile, "GetSeats failed: %s\n", - error.message); - dbus_error_free(&error); - } else - fprintf(ck->errfile, "GetSeats failed\n"); - goto exit; - } - - dbus_message_iter_init(reply, &iter); - type = dbus_message_iter_get_arg_type(&iter); - if (type != DBUS_TYPE_ARRAY) { - fprintf(ck->errfile, - "expected an array return value, got a '%c' instead\n", type); - goto exit; - } - - dbus_message_iter_recurse(&iter, &subiter); - type = dbus_message_iter_get_arg_type(&subiter); - if (type != DBUS_TYPE_OBJECT_PATH) { - fprintf(ck->errfile, - "expected an object path element, got a '%c' instead\n", type); - goto exit; - } - - dbus_message_iter_get_basic(&subiter, &seat); - ck->seat = strdup(seat); - -exit: - if (reply != NULL) { - dbus_message_unref(reply); - } - - if (message != NULL) { - dbus_message_unref(message); - } - - return ck->seat; -} - -const char *console_kit_get_active_session(struct console_kit *ck) -{ - DBusError error; - DBusMessage *message = NULL; - DBusMessage *reply = NULL; - char *session = NULL; - - if (!ck) - return NULL; - - if (ck->active_session) - return console_kit_check_active_session_change(ck); - - message = dbus_message_new_method_call("org.freedesktop.ConsoleKit", - ck->seat, - "org.freedesktop.ConsoleKit.Seat", - "GetActiveSession"); - if (message == NULL) { - fprintf(ck->errfile, "Unable to create dbus message\n"); - goto exit; - } - - dbus_error_init(&error); - reply = dbus_connection_send_with_reply_and_block(ck->connection, - message, - -1, - &error); - if (reply == NULL || dbus_error_is_set(&error)) { - if (dbus_error_is_set(&error)) { - fprintf(ck->errfile, "GetActiveSession failed: %s\n", - error.message); - dbus_error_free(&error); - } else - fprintf(ck->errfile, "GetActiveSession failed\n"); - goto exit; - } - - dbus_error_init(&error); - if (!dbus_message_get_args(reply, - &error, - DBUS_TYPE_OBJECT_PATH, &session, - DBUS_TYPE_INVALID)) { - if (dbus_error_is_set(&error)) { - fprintf(ck->errfile, "error getting ssid from reply: %s\n", - error.message); - dbus_error_free(&error); - } else - fprintf(ck->errfile, "error getting ssid from reply\n"); - session = NULL; - goto exit; - } - - ck->active_session = strdup(session); - -exit: - if (reply != NULL) { - dbus_message_unref(reply); - } - - if (message != NULL) { - dbus_message_unref(message); - } - - /* In case the session was changed while we were running */ - return console_kit_check_active_session_change(ck); -} - -char *console_kit_session_for_pid(struct console_kit *ck, uint32_t pid) -{ - DBusError error; - DBusMessage *message = NULL; - DBusMessage *reply = NULL; - DBusMessageIter args; - char *ssid = NULL; - - if (!ck) - return NULL; - - message = dbus_message_new_method_call("org.freedesktop.ConsoleKit", - "/org/freedesktop/ConsoleKit/Manager", - "org.freedesktop.ConsoleKit.Manager", - "GetSessionForUnixProcess"); - if (message == NULL) { - fprintf(ck->errfile, "Unable to create dbus message\n"); - goto exit; - } - - dbus_message_iter_init_append(message, &args); - if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &pid)) { - fprintf(ck->errfile, "Unable to append dbus message args\n"); - goto exit; - } - - dbus_error_init(&error); - reply = dbus_connection_send_with_reply_and_block(ck->connection, - message, - -1, - &error); - if (reply == NULL || dbus_error_is_set(&error)) { - if (dbus_error_is_set(&error)) { - fprintf(ck->errfile, "GetSessionForUnixProcess failed: %s\n", - error.message); - dbus_error_free(&error); - } else - fprintf(ck->errfile, "GetSessionForUnixProces failed\n"); - goto exit; - } - - dbus_error_init(&error); - if (!dbus_message_get_args(reply, - &error, - DBUS_TYPE_OBJECT_PATH, &ssid, - DBUS_TYPE_INVALID)) { - if (dbus_error_is_set(&error)) { - fprintf(ck->errfile, "error getting ssid from reply: %s\n", - error.message); - dbus_error_free(&error); - } else - fprintf(ck->errfile, "error getting ssid from reply\n"); - ssid = NULL; - goto exit; - } - - ssid = strdup(ssid); - -exit: - if (reply != NULL) { - dbus_message_unref(reply); - } - - if (message != NULL) { - dbus_message_unref(message); - } - - return ssid; -} - -static char *console_kit_check_active_session_change(struct console_kit *ck) -{ - DBusMessage *message = NULL; - DBusMessageIter iter; - char *session; - int type; - - /* non blocking read of the next available message */ - dbus_connection_read_write(ck->connection, 0); - while ((message = dbus_connection_pop_message(ck->connection))) { - if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL) { - const char *member = dbus_message_get_member (message); - if (!strcmp(member, "NameAcquired")) { - dbus_message_unref(message); - continue; - } - if (strcmp(member, "ActiveSessionChanged")) { - fprintf(ck->errfile, "unexpected signal member: %s\n", member); - dbus_message_unref(message); - continue; - } - } else { - fprintf(ck->errfile, "received non signal message!\n"); - dbus_message_unref(message); - continue; - } - - free(ck->active_session); - ck->active_session = NULL; - - dbus_message_iter_init(message, &iter); - type = dbus_message_iter_get_arg_type(&iter); - /* Session should be an object path, but there is a bug in - ConsoleKit where it sends a string rather then an object_path - accept object_path too in case the bug ever gets fixed */ - if (type != DBUS_TYPE_STRING && type != DBUS_TYPE_OBJECT_PATH) { - fprintf(ck->errfile, - "ActiveSessionChanged message has unexpected type: '%c'\n", - type); - dbus_message_unref(message); - continue; - } - - dbus_message_iter_get_basic(&iter, &session); - ck->active_session = strdup(session); - dbus_message_unref(message); - - /* non blocking read of the next available message */ - dbus_connection_read_write(ck->connection, 0); - } - - return ck->active_session; -} diff --git a/console-kit.h b/console-kit.h deleted file mode 100644 index f041f86..0000000 --- a/console-kit.h +++ /dev/null @@ -1,39 +0,0 @@ -/* console-kit.h vdagentd ConsoleKit integration code - header - - Copyright 2010 Red Hat, Inc. - - Red Hat Authors: - Hans de Goede - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef __CONSOLE_KIT_H -#define __CONSOLE_KIT_H - -#include -#include - -struct console_kit; - -struct console_kit *console_kit_create(FILE *errfile); -void console_kit_destroy(struct console_kit *ck); - -int console_kit_get_fd(struct console_kit *ck); - -const char *console_kit_get_active_session(struct console_kit *ck); -/* Note result must be free()-ed by caller */ -char *console_kit_session_for_pid(struct console_kit *ck, uint32_t pid); - -#endif diff --git a/src/console-kit.c b/src/console-kit.c new file mode 100644 index 0000000..eb15939 --- /dev/null +++ b/src/console-kit.c @@ -0,0 +1,360 @@ +/* console-kit.h vdagentd ConsoleKit integration code + + Copyright 2010 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "console-kit.h" +#include +#include +#include +#include +#include + +struct console_kit { + DBusConnection *connection; + int fd; + char *seat; + char *active_session; + FILE *errfile; +}; + +static char *console_kit_get_first_seat(struct console_kit *ck); +static char *console_kit_check_active_session_change(struct console_kit *ck); + +struct console_kit *console_kit_create(FILE *errfile) +{ + struct console_kit *ck; + DBusError error; + char match[1024]; + + ck = calloc(1, sizeof(*ck)); + if (!ck) + return NULL; + + ck->errfile = errfile; + + dbus_error_init(&error); + ck->connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + if (ck->connection == NULL || dbus_error_is_set(&error)) { + if (dbus_error_is_set(&error)) { + fprintf(ck->errfile, "Unable to connect to system bus: %s\n", + error.message); + dbus_error_free(&error); + } else + fprintf(ck->errfile, "Unable to connect to system bus\n"); + free(ck); + return NULL; + } + + if (!dbus_connection_get_unix_fd(ck->connection, &ck->fd)) { + fprintf(ck->errfile, "Unable to get connection fd\n"); + console_kit_destroy(ck); + return NULL; + } + + if (!console_kit_get_first_seat(ck)) { + console_kit_destroy(ck); + return NULL; + } + + /* Register for active session changes */ + snprintf(match, sizeof(match), + "type='signal',interface='org.freedesktop.ConsoleKit.Seat'," + "path='%s',member='ActiveSessionChanged'", ck->seat); + dbus_error_init(&error); + dbus_bus_add_match(ck->connection, match, &error); + if (dbus_error_is_set(&error)) { + fprintf(ck->errfile, "Match Error (%s)\n", error.message); + console_kit_destroy(ck); + return NULL; + } + + return ck; +} + +void console_kit_destroy(struct console_kit *ck) +{ + if (!ck) + return; + + dbus_connection_close(ck->connection); + free(ck->seat); + free(ck->active_session); + free(ck); +} + +int console_kit_get_fd(struct console_kit *ck) +{ + return ck->fd; +} + +static char *console_kit_get_first_seat(struct console_kit *ck) +{ + DBusError error; + DBusMessage *message = NULL; + DBusMessage *reply = NULL; + DBusMessageIter iter, subiter; + int type; + char *seat = NULL; + + message = dbus_message_new_method_call("org.freedesktop.ConsoleKit", + "/org/freedesktop/ConsoleKit/Manager", + "org.freedesktop.ConsoleKit.Manager", + "GetSeats"); + if (message == NULL) { + fprintf(ck->errfile, "Unable to create dbus message\n"); + goto exit; + } + + dbus_error_init(&error); + reply = dbus_connection_send_with_reply_and_block(ck->connection, + message, + -1, + &error); + if (reply == NULL || dbus_error_is_set(&error)) { + if (dbus_error_is_set(&error)) { + fprintf(ck->errfile, "GetSeats failed: %s\n", + error.message); + dbus_error_free(&error); + } else + fprintf(ck->errfile, "GetSeats failed\n"); + goto exit; + } + + dbus_message_iter_init(reply, &iter); + type = dbus_message_iter_get_arg_type(&iter); + if (type != DBUS_TYPE_ARRAY) { + fprintf(ck->errfile, + "expected an array return value, got a '%c' instead\n", type); + goto exit; + } + + dbus_message_iter_recurse(&iter, &subiter); + type = dbus_message_iter_get_arg_type(&subiter); + if (type != DBUS_TYPE_OBJECT_PATH) { + fprintf(ck->errfile, + "expected an object path element, got a '%c' instead\n", type); + goto exit; + } + + dbus_message_iter_get_basic(&subiter, &seat); + ck->seat = strdup(seat); + +exit: + if (reply != NULL) { + dbus_message_unref(reply); + } + + if (message != NULL) { + dbus_message_unref(message); + } + + return ck->seat; +} + +const char *console_kit_get_active_session(struct console_kit *ck) +{ + DBusError error; + DBusMessage *message = NULL; + DBusMessage *reply = NULL; + char *session = NULL; + + if (!ck) + return NULL; + + if (ck->active_session) + return console_kit_check_active_session_change(ck); + + message = dbus_message_new_method_call("org.freedesktop.ConsoleKit", + ck->seat, + "org.freedesktop.ConsoleKit.Seat", + "GetActiveSession"); + if (message == NULL) { + fprintf(ck->errfile, "Unable to create dbus message\n"); + goto exit; + } + + dbus_error_init(&error); + reply = dbus_connection_send_with_reply_and_block(ck->connection, + message, + -1, + &error); + if (reply == NULL || dbus_error_is_set(&error)) { + if (dbus_error_is_set(&error)) { + fprintf(ck->errfile, "GetActiveSession failed: %s\n", + error.message); + dbus_error_free(&error); + } else + fprintf(ck->errfile, "GetActiveSession failed\n"); + goto exit; + } + + dbus_error_init(&error); + if (!dbus_message_get_args(reply, + &error, + DBUS_TYPE_OBJECT_PATH, &session, + DBUS_TYPE_INVALID)) { + if (dbus_error_is_set(&error)) { + fprintf(ck->errfile, "error getting ssid from reply: %s\n", + error.message); + dbus_error_free(&error); + } else + fprintf(ck->errfile, "error getting ssid from reply\n"); + session = NULL; + goto exit; + } + + ck->active_session = strdup(session); + +exit: + if (reply != NULL) { + dbus_message_unref(reply); + } + + if (message != NULL) { + dbus_message_unref(message); + } + + /* In case the session was changed while we were running */ + return console_kit_check_active_session_change(ck); +} + +char *console_kit_session_for_pid(struct console_kit *ck, uint32_t pid) +{ + DBusError error; + DBusMessage *message = NULL; + DBusMessage *reply = NULL; + DBusMessageIter args; + char *ssid = NULL; + + if (!ck) + return NULL; + + message = dbus_message_new_method_call("org.freedesktop.ConsoleKit", + "/org/freedesktop/ConsoleKit/Manager", + "org.freedesktop.ConsoleKit.Manager", + "GetSessionForUnixProcess"); + if (message == NULL) { + fprintf(ck->errfile, "Unable to create dbus message\n"); + goto exit; + } + + dbus_message_iter_init_append(message, &args); + if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &pid)) { + fprintf(ck->errfile, "Unable to append dbus message args\n"); + goto exit; + } + + dbus_error_init(&error); + reply = dbus_connection_send_with_reply_and_block(ck->connection, + message, + -1, + &error); + if (reply == NULL || dbus_error_is_set(&error)) { + if (dbus_error_is_set(&error)) { + fprintf(ck->errfile, "GetSessionForUnixProcess failed: %s\n", + error.message); + dbus_error_free(&error); + } else + fprintf(ck->errfile, "GetSessionForUnixProces failed\n"); + goto exit; + } + + dbus_error_init(&error); + if (!dbus_message_get_args(reply, + &error, + DBUS_TYPE_OBJECT_PATH, &ssid, + DBUS_TYPE_INVALID)) { + if (dbus_error_is_set(&error)) { + fprintf(ck->errfile, "error getting ssid from reply: %s\n", + error.message); + dbus_error_free(&error); + } else + fprintf(ck->errfile, "error getting ssid from reply\n"); + ssid = NULL; + goto exit; + } + + ssid = strdup(ssid); + +exit: + if (reply != NULL) { + dbus_message_unref(reply); + } + + if (message != NULL) { + dbus_message_unref(message); + } + + return ssid; +} + +static char *console_kit_check_active_session_change(struct console_kit *ck) +{ + DBusMessage *message = NULL; + DBusMessageIter iter; + char *session; + int type; + + /* non blocking read of the next available message */ + dbus_connection_read_write(ck->connection, 0); + while ((message = dbus_connection_pop_message(ck->connection))) { + if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL) { + const char *member = dbus_message_get_member (message); + if (!strcmp(member, "NameAcquired")) { + dbus_message_unref(message); + continue; + } + if (strcmp(member, "ActiveSessionChanged")) { + fprintf(ck->errfile, "unexpected signal member: %s\n", member); + dbus_message_unref(message); + continue; + } + } else { + fprintf(ck->errfile, "received non signal message!\n"); + dbus_message_unref(message); + continue; + } + + free(ck->active_session); + ck->active_session = NULL; + + dbus_message_iter_init(message, &iter); + type = dbus_message_iter_get_arg_type(&iter); + /* Session should be an object path, but there is a bug in + ConsoleKit where it sends a string rather then an object_path + accept object_path too in case the bug ever gets fixed */ + if (type != DBUS_TYPE_STRING && type != DBUS_TYPE_OBJECT_PATH) { + fprintf(ck->errfile, + "ActiveSessionChanged message has unexpected type: '%c'\n", + type); + dbus_message_unref(message); + continue; + } + + dbus_message_iter_get_basic(&iter, &session); + ck->active_session = strdup(session); + dbus_message_unref(message); + + /* non blocking read of the next available message */ + dbus_connection_read_write(ck->connection, 0); + } + + return ck->active_session; +} diff --git a/src/console-kit.h b/src/console-kit.h new file mode 100644 index 0000000..f041f86 --- /dev/null +++ b/src/console-kit.h @@ -0,0 +1,39 @@ +/* console-kit.h vdagentd ConsoleKit integration code - header + + Copyright 2010 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __CONSOLE_KIT_H +#define __CONSOLE_KIT_H + +#include +#include + +struct console_kit; + +struct console_kit *console_kit_create(FILE *errfile); +void console_kit_destroy(struct console_kit *ck); + +int console_kit_get_fd(struct console_kit *ck); + +const char *console_kit_get_active_session(struct console_kit *ck); +/* Note result must be free()-ed by caller */ +char *console_kit_session_for_pid(struct console_kit *ck, uint32_t pid); + +#endif diff --git a/src/udscs.c b/src/udscs.c new file mode 100644 index 0000000..3d9a9e3 --- /dev/null +++ b/src/udscs.c @@ -0,0 +1,572 @@ +/* udscs.c Unix Domain Socket Client Server framework. A framework for quickly + creating select() based servers capable of handling multiple clients and + matching select() based clients using variable size messages. + + Copyright 2010 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "udscs.h" + +struct udscs_buf { + uint8_t *buf; + size_t pos; + size_t size; + + struct udscs_buf *next; +}; + +struct udscs_connection { + int fd; + const char * const *type_to_string; + int no_types; + FILE *logfile; + FILE *errfile; + struct ucred peer_cred; + void *user_data; + + /* Read stuff, single buffer, separate header and data buffer */ + int header_read; + struct udscs_message_header header; + struct udscs_buf data; + + /* Writes are stored in a linked list of buffers, with both the header + + data for a single message in 1 buffer. */ + struct udscs_buf *write_buf; + + /* Callbacks */ + udscs_read_callback read_callback; + udscs_disconnect_callback disconnect_callback; + + struct udscs_connection *next; + struct udscs_connection *prev; +}; + +struct udscs_server { + int fd; + const char * const *type_to_string; + int no_types; + FILE *logfile; + FILE *errfile; + struct udscs_connection connections_head; + udscs_connect_callback connect_callback; + udscs_read_callback read_callback; + udscs_disconnect_callback disconnect_callback; +}; + +static void udscs_do_write(struct udscs_connection **connp); +static void udscs_do_read(struct udscs_connection **connp); + + +struct udscs_server *udscs_create_server(const char *socketname, + udscs_connect_callback connect_callback, + udscs_read_callback read_callback, + udscs_disconnect_callback disconnect_callback, + const char * const type_to_string[], int no_types, + FILE *logfile, FILE *errfile) +{ + int c; + struct sockaddr_un address; + struct udscs_server *server; + + server = calloc(1, sizeof(*server)); + if (!server) + return NULL; + + server->logfile = logfile; + server->errfile = errfile; + server->type_to_string = type_to_string; + server->no_types = no_types; + + server->fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (server->fd == -1) { + fprintf(server->errfile, "creating unix domain socket: %s\n", + strerror(errno)); + free(server); + return NULL; + } + + address.sun_family = AF_UNIX; + snprintf(address.sun_path, sizeof(address.sun_path), "%s", socketname); + c = bind(server->fd, (struct sockaddr *)&address, sizeof(address)); + if (c != 0) { + fprintf(server->errfile, "bind %s: %s\n", socketname, strerror(errno)); + free(server); + return NULL; + } + + c = listen(server->fd, 5); + if (c != 0) { + fprintf(server->errfile, "listen: %s\n", strerror(errno)); + free(server); + return NULL; + } + + server->connect_callback = connect_callback; + server->read_callback = read_callback; + server->disconnect_callback = disconnect_callback; + + return server; +} + +void udscs_destroy_server(struct udscs_server *server) +{ + struct udscs_connection *conn, *next_conn; + + if (!server) + return; + + conn = server->connections_head.next; + while (conn) { + next_conn = conn->next; + udscs_destroy_connection(&conn); + conn = next_conn; + } + close(server->fd); + free(server); +} + +struct udscs_connection *udscs_connect(const char *socketname, + udscs_read_callback read_callback, + udscs_disconnect_callback disconnect_callback, + const char * const type_to_string[], int no_types, + FILE *logfile, FILE *errfile) +{ + int c; + struct sockaddr_un address; + struct udscs_connection *conn; + + conn = calloc(1, sizeof(*conn)); + if (!conn) + return NULL; + + conn->logfile = logfile; + conn->errfile = errfile; + conn->type_to_string = type_to_string; + conn->no_types = no_types; + + conn->fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (conn->fd == -1) { + fprintf(conn->errfile, "creating unix domain socket: %s\n", + strerror(errno)); + free(conn); + return NULL; + } + + address.sun_family = AF_UNIX; + snprintf(address.sun_path, sizeof(address.sun_path), "%s", socketname); + c = connect(conn->fd, (struct sockaddr *)&address, sizeof(address)); + if (c != 0) { + fprintf(conn->errfile, "connect %s: %s\n", socketname, + strerror(errno)); + free(conn); + return NULL; + } + + conn->read_callback = read_callback; + conn->disconnect_callback = disconnect_callback; + + if (conn->logfile) + fprintf(conn->logfile, "%p connected to %s\n", conn, socketname); + + return conn; +} + +void udscs_destroy_connection(struct udscs_connection **connp) +{ + struct udscs_buf *wbuf, *next_wbuf; + struct udscs_connection *conn = *connp; + + if (!conn) + return; + + if (conn->disconnect_callback) + conn->disconnect_callback(conn); + + wbuf = conn->write_buf; + while (wbuf) { + next_wbuf = wbuf->next; + free(wbuf->buf); + free(wbuf); + wbuf = next_wbuf; + } + + free(conn->data.buf); + + if (conn->next) + conn->next->prev = conn->prev; + if (conn->prev) + conn->prev->next = conn->next; + + close(conn->fd); + + if (conn->logfile) + fprintf(conn->logfile, "%p disconnected\n", conn); + + free(conn); + *connp = NULL; +} + +struct ucred udscs_get_peer_cred(struct udscs_connection *conn) +{ + return conn->peer_cred; +} + +int udscs_server_fill_fds(struct udscs_server *server, fd_set *readfds, + fd_set *writefds) +{ + struct udscs_connection *conn; + int nfds = server->fd + 1; + + if (!server) + return -1; + + FD_SET(server->fd, readfds); + + conn = server->connections_head.next; + while (conn) { + int conn_nfds = udscs_client_fill_fds(conn, readfds, writefds); + if (conn_nfds > nfds) + nfds = conn_nfds; + + conn = conn->next; + } + + return nfds; +} + +int udscs_client_fill_fds(struct udscs_connection *conn, fd_set *readfds, + fd_set *writefds) +{ + if (!conn) + return -1; + + FD_SET(conn->fd, readfds); + if (conn->write_buf) + FD_SET(conn->fd, writefds); + + return conn->fd + 1; +} + +static void udscs_server_accept(struct udscs_server *server) { + struct udscs_connection *new_conn, *conn; + struct sockaddr_un address; + socklen_t length = sizeof(address); + int r, fd; + + fd = accept(server->fd, (struct sockaddr *)&address, &length); + if (fd == -1) { + if (errno == EINTR) + return; + fprintf(server->errfile, "accept: %s\n", strerror(errno)); + return; + } + + new_conn = calloc(1, sizeof(*conn)); + if (!new_conn) { + fprintf(server->errfile, "out of memory, disconnecting new client\n"); + close(fd); + return; + } + + new_conn->fd = fd; + new_conn->logfile = server->logfile; + new_conn->errfile = server->errfile; + new_conn->type_to_string = server->type_to_string; + new_conn->no_types = server->no_types; + new_conn->read_callback = server->read_callback; + new_conn->disconnect_callback = server->disconnect_callback; + + length = sizeof(new_conn->peer_cred); + r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &new_conn->peer_cred, &length); + if (r != 0) { + fprintf(server->errfile, + "Could not get peercred, disconnecting new client\n"); + close(fd); + free(new_conn); + return; + } + + conn = &server->connections_head; + while (conn->next) + conn = conn->next; + + new_conn->prev = conn; + conn->next = new_conn; + + if (server->logfile) + fprintf(server->logfile, "new client accepted: %p, pid: %d\n", + new_conn, (int)new_conn->peer_cred.pid); + + if (server->connect_callback) + server->connect_callback(new_conn); +} + +void udscs_server_handle_fds(struct udscs_server *server, fd_set *readfds, + fd_set *writefds) +{ + struct udscs_connection *conn, *next_conn; + + if (!server) + return; + + if (FD_ISSET(server->fd, readfds)) + udscs_server_accept(server); + + conn = server->connections_head.next; + while (conn) { + /* conn maybe destroyed by udscs_client_handle_fds (when disconnected), + so get the next connection first. */ + next_conn = conn->next; + udscs_client_handle_fds(&conn, readfds, writefds); + conn = next_conn; + } +} + +void udscs_client_handle_fds(struct udscs_connection **connp, fd_set *readfds, + fd_set *writefds) +{ + if (!*connp) + return; + + if (FD_ISSET((*connp)->fd, readfds)) + udscs_do_read(connp); + + if (*connp && FD_ISSET((*connp)->fd, writefds)) + udscs_do_write(connp); +} + +int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t arg1, + uint32_t arg2, const uint8_t *data, uint32_t size) +{ + struct udscs_buf *wbuf, *new_wbuf; + struct udscs_message_header header; + + new_wbuf = malloc(sizeof(*new_wbuf)); + if (!new_wbuf) + return -1; + + new_wbuf->pos = 0; + new_wbuf->size = sizeof(header) + size; + new_wbuf->next = NULL; + new_wbuf->buf = malloc(new_wbuf->size); + if (!new_wbuf->buf) { + free(new_wbuf); + return -1; + } + + header.type = type; + header.arg1 = arg1; + header.arg2 = arg2; + header.size = size; + + memcpy(new_wbuf->buf, &header, sizeof(header)); + memcpy(new_wbuf->buf + sizeof(header), data, size); + + if (conn->logfile) { + if (type < conn->no_types) + fprintf(conn->logfile, "%p sent %s, arg1: %u, arg2: %u, size %u\n", + conn, conn->type_to_string[type], arg1, arg2, size); + else + fprintf(conn->logfile, + "%p sent invalid message %u, arg1: %u, arg2: %u, size %u\n", + conn, type, arg1, arg2, size); + } + + if (!conn->write_buf) { + conn->write_buf = new_wbuf; + return 0; + } + + /* maybe we should limit the write_buf stack depth ? */ + wbuf = conn->write_buf; + while (wbuf->next) + wbuf = wbuf->next; + + wbuf->next = new_wbuf; + + return 0; +} + +int udscs_server_write_all(struct udscs_server *server, + uint32_t type, uint32_t arg1, uint32_t arg2, + const uint8_t *data, uint32_t size) +{ + struct udscs_connection *conn; + + conn = server->connections_head.next; + while (conn) { + if (udscs_write(conn, type, arg1, arg2, data, size)) + return -1; + conn = conn->next; + } + + return 0; +} + +int udscs_server_for_all_clients(struct udscs_server *server, + udscs_for_all_clients_callback func, void *priv) +{ + int r = 0; + struct udscs_connection *conn, *next_conn; + + if (!server) + return 0; + + conn = server->connections_head.next; + while (conn) { + /* Get next conn as func may destroy the current conn */ + next_conn = conn->next; + r += func(&conn, priv); + conn = next_conn; + } + return r; +} + +static void udscs_read_complete(struct udscs_connection **connp) +{ + struct udscs_connection *conn = *connp; + + if (conn->logfile) { + if (conn->header.type < conn->no_types) + fprintf(conn->logfile, + "%p received %s, arg1: %u, arg2: %u, size %u\n", + conn, conn->type_to_string[conn->header.type], + conn->header.arg1, conn->header.arg2, conn->header.size); + else + fprintf(conn->logfile, + "%p received invalid message %u, arg1: %u, arg2: %u, size %u\n", + conn, conn->header.type, conn->header.arg1, conn->header.arg2, + conn->header.size); + } + + if (conn->read_callback) { + conn->read_callback(connp, &conn->header, conn->data.buf); + if (!*connp) /* Was the connection disconnected by the callback ? */ + return; + } + + conn->header_read = 0; + memset(&conn->data, 0, sizeof(conn->data)); +} + +static void udscs_do_read(struct udscs_connection **connp) +{ + ssize_t n; + size_t to_read; + uint8_t *dest; + struct udscs_connection *conn = *connp; + + if (conn->header_read < sizeof(conn->header)) { + to_read = sizeof(conn->header) - conn->header_read; + dest = (uint8_t *)&conn->header + conn->header_read; + } else { + to_read = conn->data.size - conn->data.pos; + dest = conn->data.buf + conn->data.pos; + } + + n = read(conn->fd, dest, to_read); + if (n < 0) { + if (errno == EINTR) + return; + fprintf(conn->errfile, + "reading unix domain socket: %s, disconnecting %p\n", + strerror(errno), conn); + } + if (n <= 0) { + udscs_destroy_connection(connp); + return; + } + + if (conn->header_read < sizeof(conn->header)) { + conn->header_read += n; + if (conn->header_read == sizeof(conn->header)) { + if (conn->header.size == 0) { + udscs_read_complete(connp); + return; + } + conn->data.pos = 0; + conn->data.size = conn->header.size; + conn->data.buf = malloc(conn->data.size); + if (!conn->data.buf) { + fprintf(conn->errfile, "out of memory, disconnecting %p\n", + conn); + udscs_destroy_connection(connp); + return; + } + } + } else { + conn->data.pos += n; + if (conn->data.pos == conn->data.size) + udscs_read_complete(connp); + } +} + +static void udscs_do_write(struct udscs_connection **connp) +{ + ssize_t n; + size_t to_write; + struct udscs_connection *conn = *connp; + + struct udscs_buf* wbuf = conn->write_buf; + if (!wbuf) { + fprintf(conn->errfile, + "%p do_write called on a connection without a write buf ?!\n", + conn); + return; + } + + to_write = wbuf->size - wbuf->pos; + n = write(conn->fd, wbuf->buf + wbuf->pos, to_write); + if (n < 0) { + if (errno == EINTR) + return; + fprintf(conn->errfile, + "writing to unix domain socket: %s, disconnecting %p\n", + strerror(errno), conn); + udscs_destroy_connection(connp); + return; + } + + wbuf->pos += n; + if (wbuf->pos == wbuf->size) { + conn->write_buf = wbuf->next; + free(wbuf->buf); + free(wbuf); + } +} + +void udscs_set_user_data(struct udscs_connection *conn, void *data) +{ + conn->user_data = data; +} + +void *udscs_get_user_data(struct udscs_connection *conn) +{ + if (!conn) + return NULL; + + return conn->user_data; +} diff --git a/src/udscs.h b/src/udscs.h new file mode 100644 index 0000000..251e2d0 --- /dev/null +++ b/src/udscs.h @@ -0,0 +1,126 @@ +/* udscs.h Unix Domain Socket Client Server framework header file + + Copyright 2010 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __UDSCS_H +#define __UDSCS_H + +#include +#include +#include +#include + +struct udscs_connection; +struct udscs_server; +struct udscs_message_header { + uint32_t type; + uint32_t arg1; + uint32_t arg2; + uint32_t size; +}; + +/* Callbacks with this type will be called when a new connection to a + server is accepted. */ +typedef void (*udscs_connect_callback)(struct udscs_connection *conn); +/* Callbacks with this type will be called when a complete message has been + received. The callback may call udscs_destroy_connection, in which case + *connp must be made NULL (which udscs_destroy_connection takes care of) */ +typedef void (*udscs_read_callback)(struct udscs_connection **connp, + struct udscs_message_header *header, uint8_t *data); +/* Callback type for udscs_server_for_all_clients. Clients can be disconnected + from this callback just like with a read callback. */ +typedef int (*udscs_for_all_clients_callback)(struct udscs_connection **connp, + void *priv); +/* Callbacks with this type will be called when the connection is disconnected. + Note: + 1) udscs will destroy the connection in question itself after + this callback has completed! + 2) This callback is always called, even if the disconnect is initiated + by the udscs user through returning -1 from a read callback, or + by explictly calling udscs_destroy_connection */ +typedef void (*udscs_disconnect_callback)(struct udscs_connection *conn); + +/* Create a unix domain socket named name and start listening on it. */ +struct udscs_server *udscs_create_server(const char *socketname, + udscs_connect_callback connect_callback, + udscs_read_callback read_callback, + udscs_disconnect_callback disconnect_callback, + const char * const type_to_string[], int no_types, + FILE *logfile, FILE *errfile); + +void udscs_destroy_server(struct udscs_server *server); + +/* Connect to a unix domain socket named name. */ +struct udscs_connection *udscs_connect(const char *socketname, + udscs_read_callback read_callback, + udscs_disconnect_callback disconnect_callback, + const char * const type_to_string[], int no_types, + FILE *logfile, FILE *errfile); + +/* The contents of connp will be made NULL */ +void udscs_destroy_connection(struct udscs_connection **connp); + + +/* Given an udscs server or client fill the fd_sets pointed to by readfds and + writefds for select() usage. + + Return value: value of the highest fd + 1 */ +int udscs_server_fill_fds(struct udscs_server *server, fd_set *readfds, + fd_set *writefds); + +int udscs_client_fill_fds(struct udscs_connection *conn, fd_set *readfds, + fd_set *writefds); + +/* Handle any events flagged by select for the given udscs server or client. */ +void udscs_server_handle_fds(struct udscs_server *server, fd_set *readfds, + fd_set *writefds); + +/* Note the connection may be destroyed (when disconnected) by this call + in this case the disconnect calllback will get called before the + destruction and the contents of connp will be made NULL */ +void udscs_client_handle_fds(struct udscs_connection **connp, fd_set *readfds, + fd_set *writefds); + + +/* Queue a message for delivery to the client connected through conn. + + Returns 0 on success -1 on error (only happens when malloc fails) */ +int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t arg1, + uint32_t arg2, const uint8_t *data, uint32_t size); + +/* Like udscs_write, but then send the message to all clients connected to + the server */ +int udscs_server_write_all(struct udscs_server *server, + uint32_t type, uint32_t arg1, uint32_t arg2, + const uint8_t *data, uint32_t size); +/* Call func for all clients connected to the server, passing through + priv to all func calls. Returns the total of the return values from all + calls to func */ +int udscs_server_for_all_clients(struct udscs_server *server, + udscs_for_all_clients_callback func, void *priv); + + +struct ucred udscs_get_peer_cred(struct udscs_connection *conn); + +/* For server use, to associate per connection data with a connection */ +void udscs_set_user_data(struct udscs_connection *conn, void *data); +void *udscs_get_user_data(struct udscs_connection *conn); + +#endif diff --git a/src/vdagent-virtio-port.c b/src/vdagent-virtio-port.c new file mode 100644 index 0000000..02457d1 --- /dev/null +++ b/src/vdagent-virtio-port.c @@ -0,0 +1,357 @@ +/* vdagent-virtio-port.c virtio port communication code + + Copyright 2010 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include "vdagent-virtio-port.h" + +#define VDP_LAST_PORT VDP_SERVER_PORT + +struct vdagent_virtio_port_buf { + uint8_t *buf; + size_t pos; + size_t size; + + struct vdagent_virtio_port_buf *next; +}; + +/* Data to keep track of the assembling of vdagent messages per chunk port, + for de-multiplexing the messages */ +struct vdagent_virtio_port_chunk_port_data { + int message_header_read; + int message_data_pos; + VDAgentMessage message_header; + uint8_t *message_data; +}; + +struct vdagent_virtio_port { + int fd; + FILE *errfile; + + /* Chunk read stuff, single buffer, separate header and data buffer */ + int chunk_header_read; + int chunk_data_pos; + VDIChunkHeader chunk_header; + uint8_t chunk_data[VD_AGENT_MAX_DATA_SIZE]; + + /* Per chunk port data */ + struct vdagent_virtio_port_chunk_port_data port_data[VDP_LAST_PORT + 1]; + + /* Writes are stored in a linked list of buffers, with both the header + + data for a single message in 1 buffer. */ + struct vdagent_virtio_port_buf *write_buf; + + /* Callbacks */ + vdagent_virtio_port_read_callback read_callback; + vdagent_virtio_port_disconnect_callback disconnect_callback; +}; + +static void vdagent_virtio_port_do_write(struct vdagent_virtio_port **vportp); +static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **vportp); + +struct vdagent_virtio_port *vdagent_virtio_port_create(const char *portname, + vdagent_virtio_port_read_callback read_callback, + vdagent_virtio_port_disconnect_callback disconnect_callback, + FILE *errfile) +{ + struct vdagent_virtio_port *vport; + + vport = calloc(1, sizeof(*vport)); + if (!vport) + return 0; + + vport->errfile = errfile; + vport->fd = open(portname, O_RDWR); + if (vport->fd == -1) { + fprintf(vport->errfile, "open %s: %s\n", portname, strerror(errno)); + free(vport); + return NULL; + } + + vport->read_callback = read_callback; + vport->disconnect_callback = disconnect_callback; + + return vport; +} + +void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp) +{ + struct vdagent_virtio_port_buf *wbuf, *next_wbuf; + struct vdagent_virtio_port *vport = *vportp; + int i; + + if (!vport) + return; + + if (vport->disconnect_callback) + vport->disconnect_callback(vport); + + wbuf = vport->write_buf; + while (wbuf) { + next_wbuf = wbuf->next; + free(wbuf->buf); + free(wbuf); + wbuf = next_wbuf; + } + + for (i = 0; i <= VDP_LAST_PORT; i++) { + free(vport->port_data[i].message_data); + } + + close(vport->fd); + free(vport); + *vportp = NULL; +} + +int vdagent_virtio_port_fill_fds(struct vdagent_virtio_port *vport, + fd_set *readfds, fd_set *writefds) +{ + if (!vport) + return -1; + + FD_SET(vport->fd, readfds); + if (vport->write_buf) + FD_SET(vport->fd, writefds); + + return vport->fd + 1; +} + +void vdagent_virtio_port_handle_fds(struct vdagent_virtio_port **vportp, + fd_set *readfds, fd_set *writefds) +{ + if (!*vportp) + return; + + if (FD_ISSET((*vportp)->fd, readfds)) + vdagent_virtio_port_do_read(vportp); + + if (*vportp && FD_ISSET((*vportp)->fd, writefds)) + vdagent_virtio_port_do_write(vportp); +} + +int vdagent_virtio_port_write( + struct vdagent_virtio_port *vport, + uint32_t port_nr, + uint32_t message_type, + uint32_t message_opaque, + const uint8_t *data, + uint32_t data_size) +{ + struct vdagent_virtio_port_buf *wbuf, *new_wbuf; + VDIChunkHeader chunk_header; + VDAgentMessage message_header; + + new_wbuf = malloc(sizeof(*new_wbuf)); + if (!new_wbuf) + return -1; + + new_wbuf->pos = 0; + new_wbuf->size = sizeof(chunk_header) + sizeof(message_header) + data_size; + new_wbuf->next = NULL; + new_wbuf->buf = malloc(new_wbuf->size); + if (!new_wbuf->buf) { + free(new_wbuf); + return -1; + } + + chunk_header.port = port_nr; + chunk_header.size = sizeof(message_header) + data_size; + message_header.protocol = VD_AGENT_PROTOCOL; + message_header.type = message_type; + message_header.opaque = message_opaque; + message_header.size = data_size; + + memcpy(new_wbuf->buf, &chunk_header, sizeof(chunk_header)); + memcpy(new_wbuf->buf + sizeof(chunk_header), &message_header, + sizeof(message_header)); + memcpy(new_wbuf->buf + sizeof(chunk_header) + sizeof(message_header), + data, data_size); + + if (!vport->write_buf) { + vport->write_buf = new_wbuf; + return 0; + } + + /* maybe we should limit the write_buf stack depth ? */ + wbuf = vport->write_buf; + while (wbuf->next) + wbuf = wbuf->next; + + wbuf->next = new_wbuf; + + return 0; +} + +void vdagent_virtio_port_flush(struct vdagent_virtio_port **vportp) +{ + while (*vportp && (*vportp)->write_buf) + vdagent_virtio_port_do_write(vportp); +} + +static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp) +{ + int avail, read, pos = 0; + struct vdagent_virtio_port *vport = *vportp; + struct vdagent_virtio_port_chunk_port_data *port = + &vport->port_data[vport->chunk_header.port]; + + if (port->message_header_read < sizeof(port->message_header)) { + read = sizeof(port->message_header) - port->message_header_read; + if (read > vport->chunk_header.size) { + read = vport->chunk_header.size; + } + memcpy((uint8_t *)&port->message_header + port->message_header_read, + vport->chunk_data, read); + port->message_header_read += read; + if (port->message_header_read == sizeof(port->message_header) && + port->message_header.size) { + port->message_data = malloc(port->message_header.size); + if (!port->message_data) { + fprintf(vport->errfile, "out of memory, disconnecting virtio\n"); + vdagent_virtio_port_destroy(vportp); + return; + } + } + pos = read; + } + + if (port->message_header_read == sizeof(port->message_header)) { + read = port->message_header.size - port->message_data_pos; + avail = vport->chunk_header.size - pos; + + if (avail > read) { + fprintf(vport->errfile, "chunk larger then message, lost sync?\n"); + vdagent_virtio_port_destroy(vportp); + return; + } + + if (avail < read) + read = avail; + + if (read) { + memcpy(port->message_data + port->message_data_pos, + vport->chunk_data + pos, read); + port->message_data_pos += read; + } + + if (port->message_data_pos == port->message_header.size) { + if (vport->read_callback) { + int r = vport->read_callback(vport, vport->chunk_header.port, + &port->message_header, port->message_data); + if (r == -1) { + vdagent_virtio_port_destroy(vportp); + return; + } + } + port->message_header_read = 0; + port->message_data_pos = 0; + free(port->message_data); + port->message_data = NULL; + } + } +} + +static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **vportp) +{ + ssize_t n; + size_t to_read; + uint8_t *dest; + struct vdagent_virtio_port *vport = *vportp; + + if (vport->chunk_header_read < sizeof(vport->chunk_header)) { + to_read = sizeof(vport->chunk_header) - vport->chunk_header_read; + dest = (uint8_t *)&vport->chunk_header + vport->chunk_header_read; + } else { + to_read = vport->chunk_header.size - vport->chunk_data_pos; + dest = vport->chunk_data + vport->chunk_data_pos; + } + + n = read(vport->fd, dest, to_read); + if (n < 0) { + if (errno == EINTR) + return; + fprintf(vport->errfile, "reading from vdagent virtio port: %s\n", + strerror(errno)); + } + if (n <= 0) { + vdagent_virtio_port_destroy(vportp); + return; + } + + if (vport->chunk_header_read < sizeof(vport->chunk_header)) { + vport->chunk_header_read += n; + if (vport->chunk_header_read == sizeof(vport->chunk_header)) { + if (vport->chunk_header.size > VD_AGENT_MAX_DATA_SIZE) { + fprintf(vport->errfile, "chunk size too large\n"); + vdagent_virtio_port_destroy(vportp); + return; + } + if (vport->chunk_header.port > VDP_LAST_PORT) { + fprintf(vport->errfile, "chunk port out of range\n"); + vdagent_virtio_port_destroy(vportp); + return; + } + } + } else { + vport->chunk_data_pos += n; + if (vport->chunk_data_pos == vport->chunk_header.size) { + vdagent_virtio_port_do_chunk(vportp); + vport->chunk_header_read = 0; + vport->chunk_data_pos = 0; + } + } +} + +static void vdagent_virtio_port_do_write(struct vdagent_virtio_port **vportp) +{ + ssize_t n; + size_t to_write; + struct vdagent_virtio_port *vport = *vportp; + + struct vdagent_virtio_port_buf* wbuf = vport->write_buf; + if (!wbuf) { + fprintf(vport->errfile, + "do_write called on a port without a write buf ?!\n"); + return; + } + + to_write = wbuf->size - wbuf->pos; + n = write(vport->fd, wbuf->buf + wbuf->pos, to_write); + if (n < 0) { + if (errno == EINTR) + return; + fprintf(vport->errfile, "writing to vdagent virtio port: %s\n", + strerror(errno)); + vdagent_virtio_port_destroy(vportp); + return; + } + + wbuf->pos += n; + if (wbuf->pos == wbuf->size) { + vport->write_buf = wbuf->next; + free(wbuf->buf); + free(wbuf); + } +} diff --git a/src/vdagent-virtio-port.h b/src/vdagent-virtio-port.h new file mode 100644 index 0000000..db446a1 --- /dev/null +++ b/src/vdagent-virtio-port.h @@ -0,0 +1,92 @@ +/* vdagent-virtio-port.h virtio port communication header + + Copyright 2010 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __VIRTIO_PORT_H +#define __VIRTIO_PORT_H + +#include +#include +#include +#include + +struct vdagent_virtio_port; + +/* Callbacks with this type will be called when a complete message has been + received. Sometimes the callback may want to close the port, in this + case do *not* call vdagent_virtio_port_destroy from the callback. The desire + to close the port can be indicated be returning -1 from the callback, + in other cases return 0. */ +typedef int (*vdagent_virtio_port_read_callback)( + struct vdagent_virtio_port *vport, + int port_nr, + VDAgentMessage *message_header, + uint8_t *data); + +/* Callbacks with this type will be called when the port is disconnected. + Note: + 1) vdagent_virtio_port will destroy the port in question itself after + this callback has completed! + 2) This callback is always called, even if the disconnect is initiated + by the vdagent_virtio_port user through returning -1 from a read + callback, or by explictly calling vdagent_virtio_port_destroy */ +typedef void (*vdagent_virtio_port_disconnect_callback)( + struct vdagent_virtio_port *conn); + + +/* Create a vdagent virtio port object for port portname */ +struct vdagent_virtio_port *vdagent_virtio_port_create(const char *portname, + vdagent_virtio_port_read_callback read_callback, + vdagent_virtio_port_disconnect_callback disconnect_callback, + FILE *errfile); + +/* The contents of portp will be made NULL */ +void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp); + + +/* Given a vdagent_virtio_port fill the fd_sets pointed to by readfds and + writefds for select() usage. + + Return value: value of the highest fd + 1 */ +int vdagent_virtio_port_fill_fds(struct vdagent_virtio_port *vport, + fd_set *readfds, fd_set *writefds); + +/* Handle any events flagged by select for the given vdagent_virtio_port. + Note the port may be destroyed (when disconnected) by this call + in this case the disconnect calllback will get called before the + destruction and the contents of connp will be made NULL */ +void vdagent_virtio_port_handle_fds(struct vdagent_virtio_port **vportp, + fd_set *readfds, fd_set *writefds); + + +/* Queue a message for delivery + + Returns 0 on success -1 on error (only happens when malloc fails) */ +int vdagent_virtio_port_write( + struct vdagent_virtio_port *vport, + uint32_t port_nr, + uint32_t message_type, + uint32_t message_opaque, + const uint8_t *data, + uint32_t data_size); + +void vdagent_virtio_port_flush(struct vdagent_virtio_port **vportp); + +#endif diff --git a/src/vdagent-x11.c b/src/vdagent-x11.c new file mode 100644 index 0000000..5068bc9 --- /dev/null +++ b/src/vdagent-x11.c @@ -0,0 +1,1307 @@ +/* vdagent-x11.c vdagent x11 code + + Copyright 2010 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* Note: Our event loop is only called when there is data to be read from the + X11 socket. If events have arrived and have already been read by libX11 from + the socket triggered by other libX11 calls from this file, the select for + read in the main loop, won't see these and our event loop won't get called! + + Thus we must make sure that all queued events have been consumed, whenever + we return to the main loop. IOW all (externally callable) functions in this + file must end with calling XPending and consuming all queued events. + + Calling XPending when-ever we return to the mainloop also ensures any + pending writes are flushed. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "vdagentd-proto.h" +#include "vdagent-x11.h" + +enum { owner_none, owner_guest, owner_client }; + +/* X11 terminology is confusing a selection request is a request from an + app to get clipboard data from us, so iow from the spice client through + the vdagent channel. We handle these one at a time and queue any which + come in while we are still handling the current one. */ +struct vdagent_x11_selection_request { + XEvent event; + uint8_t selection; + struct vdagent_x11_selection_request *next; +}; + +/* A conversion request is X11 speak for asking an other app to give its + clipboard data to us, we do these on behalf of the spice client to copy + data from the guest to the client. Like selection requests we process + these one at a time. */ +struct vdagent_x11_conversion_request { + Atom target; + uint8_t selection; + struct vdagent_x11_conversion_request *next; +}; + +struct clipboard_format_tmpl { + uint32_t type; + const char *atom_names[16]; +}; + +struct clipboard_format_info { + uint32_t type; + Atom atoms[16]; + int atom_count; +}; + +static const struct clipboard_format_tmpl clipboard_format_templates[] = { + { VD_AGENT_CLIPBOARD_UTF8_TEXT, { "UTF8_STRING", + "text/plain;charset=UTF-8", "text/plain;charset=utf-8", NULL }, }, + { VD_AGENT_CLIPBOARD_IMAGE_PNG, { "image/png", NULL }, }, + { VD_AGENT_CLIPBOARD_IMAGE_BMP, { "image/bmp", "image/x-bmp", + "image/x-MS-bmp", "image/x-win-bitmap", NULL }, }, + { VD_AGENT_CLIPBOARD_IMAGE_TIFF, { "image/tiff", NULL }, }, + { VD_AGENT_CLIPBOARD_IMAGE_JPG, { "image/jpeg", NULL }, }, +}; + +#define clipboard_format_count (sizeof(clipboard_format_templates)/sizeof(clipboard_format_templates[0])) + +struct vdagent_x11 { + struct clipboard_format_info clipboard_formats[clipboard_format_count]; + Display *display; + Atom clipboard_atom; + Atom clipboard_primary_atom; + Atom targets_atom; + Atom incr_atom; + Atom multiple_atom; + Window root_window; + Window selection_window; + struct udscs_connection *vdagentd; + FILE *errfile; + int verbose; + int fd; + int screen; + int width; + int height; + int has_xrandr; + int has_xfixes; + int xfixes_event_base; + int max_prop_size; + int expected_targets_notifies[256]; + int clipboard_owner[256]; + int clipboard_type_count[256]; + uint32_t clipboard_agent_types[256][256]; + Atom clipboard_x11_targets[256][256]; + /* Data for conversion_req which is currently being processed */ + struct vdagent_x11_conversion_request *conversion_req; + int expect_property_notify; + uint8_t *clipboard_data; + uint32_t clipboard_data_size; + uint32_t clipboard_data_space; + /* Data for selection_req which is currently being processed */ + struct vdagent_x11_selection_request *selection_req; + uint8_t *selection_req_data; + uint32_t selection_req_data_pos; + uint32_t selection_req_data_size; + Atom selection_req_atom; +}; + +static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11); +static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11, + XEvent *event, int incr); +static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11); +static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11, + XEvent *event); +static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11, + XEvent *del_event); +static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11, + Atom prop, struct vdagent_x11_selection_request *request); +static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11, + uint8_t selection, int new_owner); + +struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd, + FILE *errfile, int verbose) +{ + struct vdagent_x11 *x11; + XWindowAttributes attrib; + int i, j, major, minor; + + x11 = calloc(1, sizeof(*x11)); + if (!x11) { + fprintf(errfile, "out of memory allocating vdagent_x11 struct\n"); + return NULL; + } + + x11->vdagentd = vdagentd; + x11->errfile = errfile; + x11->verbose = verbose; + + x11->display = XOpenDisplay(NULL); + if (!x11->display) { + fprintf(x11->errfile, "could not connect to X-server\n"); + free(x11); + return NULL; + } + + x11->screen = DefaultScreen(x11->display); + x11->root_window = RootWindow(x11->display, x11->screen); + x11->fd = ConnectionNumber(x11->display); + x11->clipboard_atom = XInternAtom(x11->display, "CLIPBOARD", False); + x11->clipboard_primary_atom = XInternAtom(x11->display, "PRIMARY", False); + x11->targets_atom = XInternAtom(x11->display, "TARGETS", False); + x11->incr_atom = XInternAtom(x11->display, "INCR", False); + x11->multiple_atom = XInternAtom(x11->display, "MULTIPLE", False); + for(i = 0; i < clipboard_format_count; i++) { + x11->clipboard_formats[i].type = clipboard_format_templates[i].type; + for(j = 0; clipboard_format_templates[i].atom_names[j]; j++) { + x11->clipboard_formats[i].atoms[j] = + XInternAtom(x11->display, + clipboard_format_templates[i].atom_names[j], + False); + } + x11->clipboard_formats[i].atom_count = j; + } + + /* We should not store properties (for selections) on the root window */ + x11->selection_window = XCreateSimpleWindow(x11->display, x11->root_window, + 0, 0, 1, 1, 0, 0, 0); + if (x11->verbose) + fprintf(x11->errfile, "Selection window: %u\n", + (unsigned int)x11->selection_window); + + if (XRRQueryExtension(x11->display, &i, &i)) + x11->has_xrandr = 1; + else + fprintf(x11->errfile, "no xrandr\n"); + + if (XFixesQueryExtension(x11->display, &x11->xfixes_event_base, &i) && + XFixesQueryVersion(x11->display, &major, &minor) && major >= 1) { + x11->has_xfixes = 1; + XFixesSelectSelectionInput(x11->display, x11->root_window, + x11->clipboard_atom, + XFixesSetSelectionOwnerNotifyMask| + XFixesSelectionWindowDestroyNotifyMask| + XFixesSelectionClientCloseNotifyMask); + XFixesSelectSelectionInput(x11->display, x11->root_window, + x11->clipboard_primary_atom, + XFixesSetSelectionOwnerNotifyMask| + XFixesSelectionWindowDestroyNotifyMask| + XFixesSelectionClientCloseNotifyMask); + } else + fprintf(x11->errfile, + "no xfixes, no guest -> client copy paste support\n"); + + x11->max_prop_size = XExtendedMaxRequestSize(x11->display); + if (x11->max_prop_size) { + x11->max_prop_size -= 100; + } else { + x11->max_prop_size = XMaxRequestSize(x11->display) - 100; + } + /* Be a good X11 citizen and maximize the amount of data we send at once */ + if (x11->max_prop_size > 262144) + x11->max_prop_size = 262144; + + /* Catch resolution changes */ + XSelectInput(x11->display, x11->root_window, StructureNotifyMask); + + /* Get the current resolution */ + XGetWindowAttributes(x11->display, x11->root_window, &attrib); + x11->width = attrib.width; + x11->height = attrib.height; + vdagent_x11_send_daemon_guest_xorg_res(x11); + + /* Flush output buffers and consume any pending events */ + vdagent_x11_do_read(x11); + + return x11; +} + +void vdagent_x11_destroy(struct vdagent_x11 *x11) +{ + uint8_t sel; + + if (!x11) + return; + + for (sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; ++sel) { + vdagent_x11_set_clipboard_owner(x11, sel, owner_none); + } + + XCloseDisplay(x11->display); + free(x11); +} + +int vdagent_x11_get_fd(struct vdagent_x11 *x11) +{ + return x11->fd; +} + +static void vdagent_x11_next_selection_request(struct vdagent_x11 *x11) +{ + struct vdagent_x11_selection_request *selection_request; + selection_request = x11->selection_req; + x11->selection_req = selection_request->next; + free(selection_request); +} + +static void vdagent_x11_next_conversion_request(struct vdagent_x11 *x11) +{ + struct vdagent_x11_conversion_request *conversion_req; + conversion_req = x11->conversion_req; + x11->conversion_req = conversion_req->next; + free(conversion_req); +} + +static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11, + uint8_t selection, int new_owner) +{ + struct vdagent_x11_selection_request *prev_sel, *curr_sel, *next_sel; + struct vdagent_x11_conversion_request *prev_conv, *curr_conv, *next_conv; + int once; + + /* Clear pending requests and clipboard data */ + once = 1; + prev_sel = NULL; + next_sel = x11->selection_req; + while (next_sel) { + curr_sel = next_sel; + next_sel = curr_sel->next; + if (curr_sel->selection == selection) { + if (once) { + fprintf(x11->errfile, + "selection requests pending on clipboard ownership " + "change, clearing\n"); + once = 0; + } + vdagent_x11_send_selection_notify(x11, None, curr_sel); + if (curr_sel == x11->selection_req) { + x11->selection_req = next_sel; + free(x11->selection_req_data); + x11->selection_req_data = NULL; + x11->selection_req_data_pos = 0; + x11->selection_req_data_size = 0; + x11->selection_req_atom = None; + } else { + prev_sel->next = next_sel; + } + free(curr_sel); + } else { + prev_sel = curr_sel; + } + } + + once = 1; + prev_conv = NULL; + next_conv = x11->conversion_req; + while (next_conv) { + curr_conv = next_conv; + next_conv = curr_conv->next; + if (curr_conv->selection == selection) { + if (once) { + fprintf(x11->errfile, + "client clipboard request pending on clipboard " + "ownership change, clearing\n"); + once = 0; + } + udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, selection, + VD_AGENT_CLIPBOARD_NONE, NULL, 0); + if (curr_conv == x11->conversion_req) { + x11->conversion_req = next_conv; + x11->clipboard_data_size = 0; + x11->expect_property_notify = 0; + } else { + prev_conv->next = next_conv; + } + free(curr_conv); + } else { + prev_conv = curr_conv; + } + } + + if (new_owner == owner_none) { + /* When going from owner_guest to owner_none we need to send a + clipboard release message to the client */ + if (x11->clipboard_owner[selection] == owner_guest) { + udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_RELEASE, selection, + 0, NULL, 0); + } + x11->clipboard_type_count[selection] = 0; + } + x11->clipboard_owner[selection] = new_owner; +} + +static int vdagent_x11_get_clipboard_atom(struct vdagent_x11 *x11, uint8_t selection, Atom* clipboard) +{ + if (selection == VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) { + *clipboard = x11->clipboard_atom; + } else if (selection == VD_AGENT_CLIPBOARD_SELECTION_PRIMARY) { + *clipboard = x11->clipboard_primary_atom; + } else { + fprintf(x11->errfile, "selection_get_grab: unknown selection\n"); + return -1; + } + + return 0; +} + +static int vdagent_x11_get_clipboard_selection(struct vdagent_x11 *x11, + XEvent *event, uint8_t *selection) +{ + Atom atom; + + if (event->type == x11->xfixes_event_base) { + XFixesSelectionNotifyEvent *xfev = (XFixesSelectionNotifyEvent *)event; + atom = xfev->selection; + } else if (event->type == SelectionNotify) { + atom = event->xselection.selection; + } else if (event->type == SelectionRequest) { + atom = event->xselectionrequest.selection; + } else { + fprintf(x11->errfile, "get_clipboard_selection: unknown event type\n"); + return -1; + } + + if (atom == x11->clipboard_atom) { + *selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD; + } else if (atom == x11->clipboard_primary_atom) { + *selection = VD_AGENT_CLIPBOARD_SELECTION_PRIMARY; + } else { + fprintf(x11->errfile, "get_clipboard_selection: unknown selection\n"); + return -1; + } + + return 0; +} + +static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event) +{ + int handled = 0; + uint8_t selection; + + if (event.type == x11->xfixes_event_base) { + union { + XEvent ev; + XFixesSelectionNotifyEvent xfev; + } ev; + + if (vdagent_x11_get_clipboard_selection(x11, &event, &selection)) { + return; + } + + ev.ev = event; + switch (ev.xfev.subtype) { + case XFixesSetSelectionOwnerNotify: + break; + /* Treat ... as a SelectionOwnerNotify None */ + case XFixesSelectionWindowDestroyNotify: + case XFixesSelectionClientCloseNotify: + ev.xfev.owner = None; + break; + default: + if (x11->verbose) + fprintf(x11->errfile, + "unexpected xfix event subtype %d window %d\n", + (int)ev.xfev.subtype, (int)event.xany.window); + return; + } + + if (x11->verbose) + fprintf(x11->errfile, "New selection owner: %u\n", + (unsigned int)ev.xfev.owner); + + /* Ignore becoming the owner ourselves */ + if (ev.xfev.owner == x11->selection_window) + return; + + /* If the clipboard owner is changed we no longer own it */ + vdagent_x11_set_clipboard_owner(x11, selection, owner_none); + + if (ev.xfev.owner == None) + return; + + /* Request the supported targets from the new owner */ + XConvertSelection(x11->display, ev.xfev.selection, x11->targets_atom, + x11->targets_atom, x11->selection_window, + CurrentTime); + x11->expected_targets_notifies[selection]++; + return; + } + + switch (event.type) { + case ConfigureNotify: + if (event.xconfigure.window != x11->root_window) + break; + + handled = 1; + + if (event.xconfigure.width == x11->width && + event.xconfigure.height == x11->height) + break; + + x11->width = event.xconfigure.width; + x11->height = event.xconfigure.height; + + vdagent_x11_send_daemon_guest_xorg_res(x11); + break; + case SelectionNotify: + if (event.xselection.target == x11->targets_atom) + vdagent_x11_handle_targets_notify(x11, &event); + else + vdagent_x11_handle_selection_notify(x11, &event, 0); + + handled = 1; + break; + case PropertyNotify: + if (x11->expect_property_notify && + event.xproperty.state == PropertyNewValue) { + vdagent_x11_handle_selection_notify(x11, &event, 1); + } + if (x11->selection_req_data && + event.xproperty.state == PropertyDelete) { + vdagent_x11_handle_property_delete_notify(x11, &event); + } + /* Always mark as handled, since we cannot unselect input for property + notifications once we are done with handling the incr transfer. */ + handled = 1; + break; + case SelectionClear: + /* Do nothing the clipboard ownership will get updated through + the XFixesSetSelectionOwnerNotify event */ + handled = 1; + break; + case SelectionRequest: { + struct vdagent_x11_selection_request *req, *new_req; + + if (vdagent_x11_get_clipboard_selection(x11, &event, &selection)) { + return; + } + + new_req = malloc(sizeof(*new_req)); + if (!new_req) { + fprintf(x11->errfile, + "out of memory on SelectionRequest, ignoring.\n"); + break; + } + + handled = 1; + + new_req->event = event; + new_req->selection = selection; + new_req->next = NULL; + + if (!x11->selection_req) { + x11->selection_req = new_req; + vdagent_x11_handle_selection_request(x11); + break; + } + + /* maybe we should limit the selection_request stack depth ? */ + req = x11->selection_req; + while (req->next) + req = req->next; + + req->next = new_req; + break; + } + } + if (!handled && x11->verbose) + fprintf(x11->errfile, "unhandled x11 event, type %d, window %d\n", + (int)event.type, (int)event.xany.window); +} + +void vdagent_x11_do_read(struct vdagent_x11 *x11) +{ + XEvent event; + + while (XPending(x11->display)) { + XNextEvent(x11->display, &event); + vdagent_x11_handle_event(x11, event); + } +} + +static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11) +{ + struct vdagentd_guest_xorg_resolution res; + + res.width = x11->width; + res.height = x11->height; + + udscs_write(x11->vdagentd, VDAGENTD_GUEST_XORG_RESOLUTION, 0, 0, + (uint8_t *)&res, sizeof(res)); +} + +static const char *vdagent_x11_get_atom_name(struct vdagent_x11 *x11, Atom a) +{ + if (a == None) + return "None"; + + return XGetAtomName(x11->display, a); +} + +static int vdagent_x11_get_selection(struct vdagent_x11 *x11, XEvent *event, + Atom type, Atom prop, int format, + unsigned char **data_ret, int incr) +{ + Bool del = incr ? True: False; + Atom type_ret; + int format_ret, ret_val = -1; + unsigned long len, remain; + unsigned char *data = NULL; + + *data_ret = NULL; + + if (!incr) { + if (event->xselection.property == None) { + if (x11->verbose) + fprintf(x11->errfile, + "XConvertSelection refused by clipboard owner\n"); + goto exit; + } + + if (event->xselection.requestor != x11->selection_window || + event->xselection.property != prop) { + fprintf(x11->errfile, "SelectionNotify parameters mismatch\n"); + goto exit; + } + } + + if (XGetWindowProperty(x11->display, x11->selection_window, prop, 0, + LONG_MAX, del, type, &type_ret, &format_ret, &len, + &remain, &data) != Success) { + fprintf(x11->errfile, "XGetWindowProperty failed\n"); + goto exit; + } + + if (!incr && prop != x11->targets_atom) { + if (type_ret == x11->incr_atom) { + int prop_min_size = *(uint32_t*)data; + + if (x11->expect_property_notify) { + fprintf(x11->errfile, + "received an incr SelectionNotify while " + "still reading another incr property\n"); + goto exit; + } + + if (x11->clipboard_data_space < prop_min_size) { + free(x11->clipboard_data); + x11->clipboard_data = malloc(prop_min_size); + if (!x11->clipboard_data) { + fprintf(x11->errfile, + "out of memory allocating clipboard buffer\n"); + x11->clipboard_data_space = 0; + goto exit; + } + x11->clipboard_data_space = prop_min_size; + } + x11->expect_property_notify = 1; + XSelectInput(x11->display, x11->selection_window, + PropertyChangeMask); + XDeleteProperty(x11->display, x11->selection_window, prop); + XFree(data); + return 0; /* Wait for more data */ + } + XDeleteProperty(x11->display, x11->selection_window, prop); + } + + if (type_ret != type) { + fprintf(x11->errfile, "expected property type: %s, got: %s\n", + vdagent_x11_get_atom_name(x11, type), + vdagent_x11_get_atom_name(x11, type_ret)); + goto exit; + } + + if (format_ret != format) { + fprintf(x11->errfile, "expected %d bit format, got %d bits\n", format, + format_ret); + goto exit; + } + + /* Convert len to bytes */ + switch(format) { + case 8: + break; + case 16: + len *= sizeof(short); + break; + case 32: + len *= sizeof(long); + break; + } + + if (incr) { + if (len) { + if (x11->clipboard_data_size + len > x11->clipboard_data_space) { + void *old_clipboard_data = x11->clipboard_data; + + x11->clipboard_data_space = x11->clipboard_data_size + len; + x11->clipboard_data = realloc(x11->clipboard_data, + x11->clipboard_data_space); + if (!x11->clipboard_data) { + fprintf(x11->errfile, + "out of memory allocating clipboard buffer\n"); + x11->clipboard_data_space = 0; + free(old_clipboard_data); + goto exit; + } + } + memcpy(x11->clipboard_data + x11->clipboard_data_size, data, len); + x11->clipboard_data_size += len; + if (x11->verbose) + fprintf(x11->errfile, "Appended %ld bytes to buffer\n", len); + XFree(data); + return 0; /* Wait for more data */ + } + len = x11->clipboard_data_size; + *data_ret = x11->clipboard_data; + } else + *data_ret = data; + + if (len > 0) { + ret_val = len; + } else { + fprintf(x11->errfile, "property contains no data (zero length)\n"); + *data_ret = NULL; + } + +exit: + if ((incr || ret_val == -1) && data) + XFree(data); + + if (incr) { + x11->clipboard_data_size = 0; + x11->expect_property_notify = 0; + } + + return ret_val; +} + +static void vdagent_x11_get_selection_free(struct vdagent_x11 *x11, + unsigned char *data, int incr) +{ + if (incr) { + /* If the clipboard has grown large return the memory to the system */ + if (x11->clipboard_data_space > 512 * 1024) { + free(x11->clipboard_data); + x11->clipboard_data = NULL; + x11->clipboard_data_space = 0; + } + } else if (data) + XFree(data); +} + +static uint32_t vdagent_x11_target_to_type(struct vdagent_x11 *x11, + Atom target) +{ + int i, j; + + for (i = 0; i < clipboard_format_count; i++) { + for (j = 0; j < x11->clipboard_formats[i].atom_count; i++) { + if (x11->clipboard_formats[i].atoms[j] == target) { + return x11->clipboard_formats[i].type; + } + } + } + + fprintf(x11->errfile, "unexpected selection type %s\n", + vdagent_x11_get_atom_name(x11, target)); + return VD_AGENT_CLIPBOARD_NONE; +} + +static Atom vdagent_x11_type_to_target(struct vdagent_x11 *x11, + uint8_t selection, uint32_t type) +{ + int i; + + for (i = 0; i < x11->clipboard_type_count[selection]; i++) { + if (x11->clipboard_agent_types[selection][i] == type) { + return x11->clipboard_x11_targets[selection][i]; + } + } + fprintf(x11->errfile, "client requested unavailable type %u\n", type); + return None; +} + +static void vdagent_x11_handle_conversion_request(struct vdagent_x11 *x11) +{ + Atom clip; + + if (!x11->conversion_req) { + return; + } + + vdagent_x11_get_clipboard_atom(x11, x11->conversion_req->selection, &clip); + XConvertSelection(x11->display, clip, x11->conversion_req->target, + clip, x11->selection_window, CurrentTime); +} + +static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11, + XEvent *event, int incr) +{ + int len = 0; + unsigned char *data = NULL; + uint32_t type; + uint8_t selection; + Atom clip; + + if (!x11->conversion_req) { + fprintf(x11->errfile, "SelectionNotify received without a target\n"); + return; + } + vdagent_x11_get_clipboard_atom(x11, x11->conversion_req->selection, &clip); + + if (incr) { + if (event->xproperty.atom != clip || + event->xproperty.window != x11->selection_window) { + return; + } + } else { + if (vdagent_x11_get_clipboard_selection(x11, event, &selection)) { + len = -1; + } else if (selection != x11->conversion_req->selection) { + fprintf(x11->errfile, "Requested data for selection %d, got %d\n", + (int)x11->conversion_req->selection, (int)selection); + len = -1; + } + if (event->xselection.target != x11->conversion_req->target && + event->xselection.target != x11->incr_atom) { + fprintf(x11->errfile, "Requested %s target got %s\n", + vdagent_x11_get_atom_name(x11, x11->conversion_req->target), + vdagent_x11_get_atom_name(x11, event->xselection.target)); + len = -1; + } + } + + selection = x11->conversion_req->selection; + type = vdagent_x11_target_to_type(x11, x11->conversion_req->target); + if (len == 0) { /* No errors so far */ + len = vdagent_x11_get_selection(x11, event, + x11->conversion_req->target, + clip, 8, &data, incr); + if (len == 0) { /* waiting for more data? */ + return; + } + } + if (len == -1) { + type = VD_AGENT_CLIPBOARD_NONE; + len = 0; + } + + udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, selection, type, + data, len); + vdagent_x11_get_selection_free(x11, data, incr); + + vdagent_x11_next_conversion_request(x11); + vdagent_x11_handle_conversion_request(x11); +} + +static Atom atom_lists_overlap(Atom *atoms1, Atom *atoms2, int l1, int l2) +{ + int i, j; + + for (i = 0; i < l1; i++) + for (j = 0; j < l2; j++) + if (atoms1[i] == atoms2[j]) + return atoms1[i]; + + return 0; +} + +static void vdagent_x11_print_targets(struct vdagent_x11 *x11, + const char *action, Atom *atoms, int c) +{ + int i; + + if (!x11->verbose) + return; + + fprintf(x11->errfile, "%s %d targets:\n", action, c); + for (i = 0; i < c; i++) + fprintf(x11->errfile, "%s\n", + vdagent_x11_get_atom_name(x11, atoms[i])); +} + +static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11, + XEvent *event) +{ + int i, len; + Atom atom, *atoms = NULL; + uint8_t selection; + int *type_count; + + if (vdagent_x11_get_clipboard_selection(x11, event, &selection)) { + return; + } + + if (!x11->expected_targets_notifies[selection]) { + fprintf(x11->errfile, "unexpected selection notify TARGETS\n"); + return; + } + + x11->expected_targets_notifies[selection]--; + + /* If we have more targets_notifies pending, ignore this one, we + are only interested in the targets list of the current owner + (which is the last one we've requested a targets list from) */ + if (x11->expected_targets_notifies[selection]) { + return; + } + + len = vdagent_x11_get_selection(x11, event, XA_ATOM, x11->targets_atom, 32, + (unsigned char **)&atoms, 0); + if (len == 0 || len == -1) /* waiting for more data or error? */ + return; + + /* bytes -> atoms */ + len /= sizeof(Atom); + vdagent_x11_print_targets(x11, "received", atoms, len); + + type_count = &x11->clipboard_type_count[selection]; + *type_count = 0; + for (i = 0; i < clipboard_format_count; i++) { + atom = atom_lists_overlap(x11->clipboard_formats[i].atoms, atoms, + x11->clipboard_formats[i].atom_count, len); + if (atom) { + x11->clipboard_agent_types[selection][*type_count] = + x11->clipboard_formats[i].type; + x11->clipboard_x11_targets[selection][*type_count] = atom; + (*type_count)++; + if (*type_count == + sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t)) { + fprintf(x11->errfile, + "handle_targets_notify: too many types\n"); + break; + } + } + } + + if (*type_count) { + udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_GRAB, selection, 0, + (uint8_t *)x11->clipboard_agent_types[selection], + *type_count * sizeof(uint32_t)); + vdagent_x11_set_clipboard_owner(x11, selection, owner_guest); + } + + vdagent_x11_get_selection_free(x11, (unsigned char *)atoms, 0); +} + +static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11, + Atom prop, struct vdagent_x11_selection_request *request) +{ + XEvent res, *event; + + if (request) { + event = &request->event; + } else { + event = &x11->selection_req->event; + } + + res.xselection.property = prop; + res.xselection.type = SelectionNotify; + res.xselection.display = event->xselectionrequest.display; + res.xselection.requestor = event->xselectionrequest.requestor; + res.xselection.selection = event->xselectionrequest.selection; + res.xselection.target = event->xselectionrequest.target; + res.xselection.time = event->xselectionrequest.time; + XSendEvent(x11->display, event->xselectionrequest.requestor, 0, 0, &res); + + if (!request) { + vdagent_x11_next_selection_request(x11); + vdagent_x11_handle_selection_request(x11); + } +} + +static void vdagent_x11_send_targets(struct vdagent_x11 *x11, + uint8_t selection, XEvent *event) +{ + Atom prop, targets[256] = { x11->targets_atom, }; + int i, j, k, target_count = 1; + + for (i = 0; i < x11->clipboard_type_count[selection]; i++) { + for (j = 0; j < clipboard_format_count; j++) { + if (x11->clipboard_formats[j].type != + x11->clipboard_agent_types[selection][i]) + continue; + + for (k = 0; k < x11->clipboard_formats[j].atom_count; k++) { + targets[target_count] = x11->clipboard_formats[j].atoms[k]; + target_count++; + if (target_count == sizeof(targets)/sizeof(Atom)) { + fprintf(x11->errfile, "send_targets: too many targets\n"); + goto exit_loop; + } + } + } + } +exit_loop: + + prop = event->xselectionrequest.property; + if (prop == None) + prop = event->xselectionrequest.target; + + XChangeProperty(x11->display, event->xselectionrequest.requestor, prop, + XA_ATOM, 32, PropModeReplace, (unsigned char *)&targets, + target_count); + vdagent_x11_print_targets(x11, "sent", targets, target_count); + vdagent_x11_send_selection_notify(x11, prop, NULL); +} + +static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11) +{ + XEvent *event; + uint32_t type = VD_AGENT_CLIPBOARD_NONE; + uint8_t selection; + + if (!x11->selection_req) + return; + + event = &x11->selection_req->event; + selection = x11->selection_req->selection; + + if (x11->clipboard_owner[selection] != owner_client) { + fprintf(x11->errfile, + "received selection request event for target %s, " + "while not owning client clipboard\n", + vdagent_x11_get_atom_name(x11, event->xselectionrequest.target)); + vdagent_x11_send_selection_notify(x11, None, NULL); + return; + } + + if (event->xselectionrequest.target == x11->multiple_atom) { + fprintf(x11->errfile, "multiple target not supported\n"); + vdagent_x11_send_selection_notify(x11, None, NULL); + return; + } + + if (event->xselectionrequest.target == x11->targets_atom) { + vdagent_x11_send_targets(x11, selection, event); + return; + } + + type = vdagent_x11_target_to_type(x11, event->xselectionrequest.target); + if (type == VD_AGENT_CLIPBOARD_NONE) { + vdagent_x11_send_selection_notify(x11, None, NULL); + return; + } + + udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_REQUEST, selection, type, + NULL, 0); +} + +static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11, + XEvent *del_event) +{ + XEvent *sel_event; + int len; + + assert(x11->selection_req); + sel_event = &x11->selection_req->event; + if (del_event->xproperty.window != sel_event->xselectionrequest.requestor + || del_event->xproperty.atom != x11->selection_req_atom) { + return; + } + + len = x11->selection_req_data_size - x11->selection_req_data_pos; + if (len > x11->max_prop_size) { + len = x11->max_prop_size; + } + + if (x11->verbose) { + if (len) { + fprintf(x11->errfile, "Sending %d-%d/%d bytes of clipboard data\n", + x11->selection_req_data_pos, + x11->selection_req_data_pos + len - 1, + x11->selection_req_data_size); + } else { + fprintf(x11->errfile, "Ending incr send of clipboard data\n"); + } + } + XChangeProperty(x11->display, sel_event->xselectionrequest.requestor, + x11->selection_req_atom, + sel_event->xselectionrequest.target, 8, PropModeReplace, + x11->selection_req_data + x11->selection_req_data_pos, + len); + x11->selection_req_data_pos += len; + + /* Note we must explictly send a 0 sized XChangeProperty to signal the + incr transfer is done. Hence we do not check if we've send all data + but instead check we've send the final 0 sized XChangeProperty. */ + if (len == 0) { + free(x11->selection_req_data); + x11->selection_req_data = NULL; + x11->selection_req_data_pos = 0; + x11->selection_req_data_size = 0; + x11->selection_req_atom = None; + vdagent_x11_next_selection_request(x11); + vdagent_x11_handle_selection_request(x11); + } +} + +void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11, + VDAgentMonitorsConfig *mon_config) +{ + int i, num_sizes = 0; + int best = -1; + unsigned int closest_diff = -1; + XRRScreenSize* sizes; + XRRScreenConfiguration* config; + Rotation rotation; + + if (!x11->has_xrandr) + return; + + if (mon_config->num_of_monitors != 1) { + fprintf(x11->errfile, + "Only 1 monitor supported, ignoring additional monitors\n"); + } + + sizes = XRRSizes(x11->display, x11->screen, &num_sizes); + if (!sizes || !num_sizes) { + fprintf(x11->errfile, "XRRSizes failed\n"); + return; + } + + /* Find the closest size which will fit within the monitor */ + for (i = 0; i < num_sizes; i++) { + if (sizes[i].width > mon_config->monitors[0].width || + sizes[i].height > mon_config->monitors[0].height) + continue; /* Too large for the monitor */ + + unsigned int wdiff = mon_config->monitors[0].width - sizes[i].width; + unsigned int hdiff = mon_config->monitors[0].height - sizes[i].height; + unsigned int diff = wdiff * wdiff + hdiff * hdiff; + if (diff < closest_diff) { + closest_diff = diff; + best = i; + } + } + + if (best == -1) { + fprintf(x11->errfile, "no suitable resolution found for monitor\n"); + return; + } + + config = XRRGetScreenInfo(x11->display, x11->root_window); + if(!config) { + fprintf(x11->errfile, "get screen info failed\n"); + return; + } + XRRConfigCurrentConfiguration(config, &rotation); + XRRSetScreenConfig(x11->display, config, x11->root_window, best, + rotation, CurrentTime); + XRRFreeScreenConfigInfo(config); + x11->width = sizes[best].width; + x11->height = sizes[best].height; + vdagent_x11_send_daemon_guest_xorg_res(x11); + + /* Flush output buffers and consume any pending events */ + vdagent_x11_do_read(x11); +} + +void vdagent_x11_clipboard_request(struct vdagent_x11 *x11, + uint8_t selection, uint32_t type) +{ + Atom target, clip; + struct vdagent_x11_conversion_request *req, *new_req; + + /* We don't use clip here, but we call get_clipboard_atom to verify + selection is valid */ + if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) { + goto none; + } + + if (x11->clipboard_owner[selection] != owner_guest) { + fprintf(x11->errfile, + "received clipboard req while not owning guest clipboard\n"); + goto none; + } + + target = vdagent_x11_type_to_target(x11, selection, type); + if (target == None) { + goto none; + } + + new_req = malloc(sizeof(*new_req)); + if (!new_req) { + fprintf(x11->errfile, + "out of memory on client clipboard request, ignoring.\n"); + return; + } + + new_req->target = target; + new_req->selection = selection; + new_req->next = NULL; + + if (!x11->conversion_req) { + x11->conversion_req = new_req; + vdagent_x11_handle_conversion_request(x11); + /* Flush output buffers and consume any pending events */ + vdagent_x11_do_read(x11); + return; + } + + /* maybe we should limit the conversion_request stack depth ? */ + req = x11->conversion_req; + while (req->next) + req = req->next; + + req->next = new_req; + return; + +none: + udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, + selection, VD_AGENT_CLIPBOARD_NONE, NULL, 0); +} + +void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint8_t selection, + uint32_t *types, uint32_t type_count) +{ + Atom clip; + + if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) { + return; + } + + if (type_count > sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t)) { + fprintf(x11->errfile, "x11_clipboard_grab: too many types\n"); + type_count = sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t); + } + + memcpy(x11->clipboard_agent_types[selection], types, + type_count * sizeof(uint32_t)); + x11->clipboard_type_count[selection] = type_count; + + XSetSelectionOwner(x11->display, clip, + x11->selection_window, CurrentTime); + vdagent_x11_set_clipboard_owner(x11, selection, owner_client); + + /* Flush output buffers and consume any pending events */ + vdagent_x11_do_read(x11); +} + +void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint8_t selection, + uint32_t type, uint8_t *data, uint32_t size) +{ + Atom prop; + XEvent *event; + uint32_t type_from_event; + + if (x11->selection_req_data) { + if (type || size) { + fprintf(x11->errfile, "received clipboard data while still sending" + " data from previous request, ignoring\n"); + } + free(data); + return; + } + + if (!x11->selection_req) { + if (type || size) { + fprintf(x11->errfile, "received clipboard data without an " + "outstanding selection request, ignoring\n"); + } + free(data); + return; + } + + event = &x11->selection_req->event; + type_from_event = vdagent_x11_target_to_type(x11, + event->xselectionrequest.target); + if (type_from_event != type || + selection != x11->selection_req->selection) { + if (type_from_event != type) { + fprintf(x11->errfile, "expecting type %u clipboard data got %u\n", + type_from_event, type); + } else { + fprintf(x11->errfile, "expecting data for selection %d got %d\n", + (int)x11->selection_req->selection, (int)selection); + } + vdagent_x11_send_selection_notify(x11, None, NULL); + free(data); + + /* Flush output buffers and consume any pending events */ + vdagent_x11_do_read(x11); + return; + } + + prop = event->xselectionrequest.property; + if (prop == None) + prop = event->xselectionrequest.target; + + if (size > x11->max_prop_size) { + unsigned long len = size; + if (x11->verbose) + fprintf(x11->errfile, "Starting incr send of clipboard data\n"); + x11->selection_req_data = data; + x11->selection_req_data_pos = 0; + x11->selection_req_data_size = size; + x11->selection_req_atom = prop; + XSelectInput(x11->display, event->xselectionrequest.requestor, + PropertyChangeMask); + XChangeProperty(x11->display, event->xselectionrequest.requestor, prop, + x11->incr_atom, 32, PropModeReplace, + (unsigned char*)&len, 1); + vdagent_x11_send_selection_notify(x11, prop, x11->selection_req); + } else { + XChangeProperty(x11->display, event->xselectionrequest.requestor, prop, + event->xselectionrequest.target, 8, PropModeReplace, + data, size); + vdagent_x11_send_selection_notify(x11, prop, NULL); + free(data); + } + + /* Flush output buffers and consume any pending events */ + vdagent_x11_do_read(x11); +} + +void vdagent_x11_clipboard_release(struct vdagent_x11 *x11, uint8_t selection) +{ + XEvent event; + Atom clip; + + if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) { + return; + } + + if (x11->clipboard_owner[selection] != owner_client) { + fprintf(x11->errfile, + "received clipboard release while not owning client clipboard\n"); + return; + } + + XSetSelectionOwner(x11->display, clip, None, CurrentTime); + /* Make sure we process the XFixesSetSelectionOwnerNotify event caused + by this, so we don't end up changing the clipboard owner to none, after + it has already been re-owned because this event is still pending. */ + XSync(x11->display, False); + while (XCheckTypedEvent(x11->display, x11->xfixes_event_base, + &event)) + vdagent_x11_handle_event(x11, event); + + /* Note no need to do a set_clipboard_owner(owner_none) here, as that is + already done by processing the XFixesSetSelectionOwnerNotify event. */ + + /* Flush output buffers and consume any pending events */ + vdagent_x11_do_read(x11); +} diff --git a/src/vdagent-x11.h b/src/vdagent-x11.h new file mode 100644 index 0000000..800cbd0 --- /dev/null +++ b/src/vdagent-x11.h @@ -0,0 +1,48 @@ +/* vdagent-x11.h vdagent x11 code header file + + Copyright 2010 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __VDAGENT_H +#define __VDAGENT_H + +#include +#include +#include "udscs.h" + +struct vdagent_x11; + +struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd, + FILE *errfile, int verbose); +void vdagent_x11_destroy(struct vdagent_x11 *x11); + +int vdagent_x11_get_fd(struct vdagent_x11 *x11); +void vdagent_x11_do_read(struct vdagent_x11 *x11); + +void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11, + VDAgentMonitorsConfig *mon_config); +void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint8_t selection, + uint32_t *types, uint32_t type_count); +void vdagent_x11_clipboard_request(struct vdagent_x11 *x11, + uint8_t selection, uint32_t type); +void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint8_t selection, + uint32_t type, uint8_t *data, uint32_t size); +void vdagent_x11_clipboard_release(struct vdagent_x11 *x11, uint8_t selection); + +#endif diff --git a/src/vdagent.c b/src/vdagent.c new file mode 100644 index 0000000..2a72adb --- /dev/null +++ b/src/vdagent.c @@ -0,0 +1,220 @@ +/* vdagent.c xorg-client to vdagentd (daemon). + + Copyright 2010 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udscs.h" +#include "vdagentd-proto.h" +#include "vdagentd-proto-strings.h" +#include "vdagent-x11.h" + +static int verbose = 0; +static struct vdagent_x11 *x11 = NULL; +static struct udscs_connection *client = NULL; +static FILE *logfile = NULL; +static int quit = 0; + +void daemon_read_complete(struct udscs_connection **connp, + struct udscs_message_header *header, uint8_t *data) +{ + switch (header->type) { + case VDAGENTD_MONITORS_CONFIG: + vdagent_x11_set_monitor_config(x11, (VDAgentMonitorsConfig *)data); + free(data); + break; + case VDAGENTD_CLIPBOARD_REQUEST: + vdagent_x11_clipboard_request(x11, header->arg1, header->arg2); + free(data); + break; + case VDAGENTD_CLIPBOARD_GRAB: + vdagent_x11_clipboard_grab(x11, header->arg1, (uint32_t *)data, + header->size / sizeof(uint32_t)); + free(data); + break; + case VDAGENTD_CLIPBOARD_DATA: + vdagent_x11_clipboard_data(x11, header->arg1, header->arg2, + data, header->size); + /* vdagent_x11_clipboard_data takes ownership of the data (or frees + it immediately) */ + break; + case VDAGENTD_CLIPBOARD_RELEASE: + vdagent_x11_clipboard_release(x11, header->arg1); + free(data); + break; + default: + if (verbose) + fprintf(logfile, "Unknown message from vdagentd type: %d\n", + header->type); + free(data); + } +} + +static void usage(FILE *fp) +{ + fprintf(fp, + "vdagent -- spice agent xorg client\n" + "options:\n" + " -h print this text\n" + " -d log debug messages\n" + " -x don't daemonize (and log to logfile)\n"); +} + +static void quit_handler(int sig) +{ + quit = 1; +} + +void daemonize(void) +{ + int x, retval = 0; + + /* detach from terminal */ + switch (fork()) { + case 0: + close(0); close(1); close(2); + setsid(); + x = open("/dev/null", O_RDWR); dup(x); dup(x); + break; + case -1: + fprintf(logfile, "fork: %s\n", strerror(errno)); + retval = 1; + default: + udscs_destroy_connection(&client); + if (logfile != stderr) + fclose(logfile); + exit(retval); + } +} + +int main(int argc, char *argv[]) +{ + fd_set readfds, writefds; + int c, n, nfds, x11_fd, retval = 0; + int do_daemonize = 1; + char *home, filename[1024]; + struct sigaction act; + + for (;;) { + if (-1 == (c = getopt(argc, argv, "-dxh"))) + break; + switch (c) { + case 'd': + verbose++; + break; + case 'x': + do_daemonize = 0; + break; + case 'h': + usage(stdout); + return 0; + default: + usage(stderr); + return 1; + } + } + + memset(&act, 0, sizeof(act)); + act.sa_flags = SA_RESTART; + act.sa_handler = quit_handler; + sigaction(SIGINT, &act, NULL); + sigaction(SIGHUP, &act, NULL); + sigaction(SIGTERM, &act, NULL); + sigaction(SIGQUIT, &act, NULL); + + logfile = stderr; + home = getenv("HOME"); + if (home) { + snprintf(filename, sizeof(filename), "%s/.spice-vdagent", home); + n = mkdir(filename, 0755); + snprintf(filename, sizeof(filename), "%s/.spice-vdagent/log", home); + if (do_daemonize) { + logfile = fopen(filename, "w"); + if (!logfile) { + fprintf(stderr, "Error opening %s: %s\n", filename, + strerror(errno)); + logfile = stderr; + } + } + } else { + fprintf(stderr, "Could not get home directory, logging to stderr\n"); + } + + client = udscs_connect(VDAGENTD_SOCKET, daemon_read_complete, NULL, + vdagentd_messages, VDAGENTD_NO_MESSAGES, + verbose? logfile:NULL, logfile); + if (!client) { + if (logfile != stderr) + fclose(logfile); + return 1; + } + + if (do_daemonize) + daemonize(); + + x11 = vdagent_x11_create(client, logfile, verbose); + if (!x11) { + udscs_destroy_connection(&client); + if (logfile != stderr) + fclose(logfile); + return 1; + } + + while (client && !quit) { + FD_ZERO(&readfds); + FD_ZERO(&writefds); + + nfds = udscs_client_fill_fds(client, &readfds, &writefds); + x11_fd = vdagent_x11_get_fd(x11); + FD_SET(x11_fd, &readfds); + if (x11_fd >= nfds) + nfds = x11_fd + 1; + + n = select(nfds, &readfds, &writefds, NULL, NULL); + if (n == -1) { + if (errno == EINTR) + continue; + fprintf(logfile, "Fatal error select: %s\n", strerror(errno)); + retval = 1; + break; + } + + if (FD_ISSET(x11_fd, &readfds)) + vdagent_x11_do_read(x11); + udscs_client_handle_fds(&client, &readfds, &writefds); + fflush(logfile); + } + + vdagent_x11_destroy(x11); + udscs_destroy_connection(&client); + if (logfile != stderr) + fclose(logfile); + + return retval; +} diff --git a/src/vdagentd-proto-strings.h b/src/vdagentd-proto-strings.h new file mode 100644 index 0000000..02adf01 --- /dev/null +++ b/src/vdagentd-proto-strings.h @@ -0,0 +1,34 @@ +/* vdagentd-proto-strings.h header file + + Copyright 2010 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __VDAGENTD_PROTO_STRINGS_H +#define __VDAGENTD_PROTO_STRINGS_H + +static const char * const vdagentd_messages[] = { + "guest xorg resolution", + "monitors config", + "clipboard grab", + "clipboard request", + "clipboard data", + "clipboard release", +}; + +#endif diff --git a/src/vdagentd-proto.h b/src/vdagentd-proto.h new file mode 100644 index 0000000..e570d2b --- /dev/null +++ b/src/vdagentd-proto.h @@ -0,0 +1,44 @@ +/* vdagentd-proto.h header file for the protocol over the unix domain socket + between the vdagent process / xorg-client and the vdagentd (daemon). + + Copyright 2010 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __VDAGENTD_PROTO_H +#define __VDAGENTD_PROTO_H + +#define VDAGENTD_SOCKET "/var/run/spice-vdagentd/spice-vdagent-sock" + +enum { + VDAGENTD_GUEST_XORG_RESOLUTION, /* client -> daemon */ + VDAGENTD_MONITORS_CONFIG, /* daemon -> client, VDAgentMonitorsConfig + followed by num_monitors VDAgentMonConfig-s */ + VDAGENTD_CLIPBOARD_GRAB, /* arg1: sel, data: array of supported types */ + VDAGENTD_CLIPBOARD_REQUEST, /* arg1: selection, arg 2 = type */ + VDAGENTD_CLIPBOARD_DATA, /* arg1: sel, arg 2: type, data: data */ + VDAGENTD_CLIPBOARD_RELEASE, /* arg1: selection */ + VDAGENTD_NO_MESSAGES /* Must always be last */ +}; + +struct vdagentd_guest_xorg_resolution { + int width; + int height; +}; + +#endif diff --git a/src/vdagentd-uinput.c b/src/vdagentd-uinput.c new file mode 100644 index 0000000..54cccb0 --- /dev/null +++ b/src/vdagentd-uinput.c @@ -0,0 +1,212 @@ +/* vdagentd-uinput.c vdagentd uinput handling code + + Copyright 2010 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede + Gerd Hoffmann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "vdagentd-uinput.h" + +struct vdagentd_uinput { + const char *devname; + int fd; + int width; + int height; + int verbose; + FILE *errfile; + VDAgentMouseState last; +}; + +struct vdagentd_uinput *vdagentd_uinput_create(const char *devname, + int width, int height, FILE *errfile, int verbose) +{ + struct vdagentd_uinput *uinput; + + uinput = calloc(1, sizeof(*uinput)); + if (!uinput) + return NULL; + + uinput->devname = devname; + uinput->fd = -1; /* Gets opened by vdagentd_uinput_update_size() */ + uinput->verbose = verbose; + uinput->errfile = errfile; + + vdagentd_uinput_update_size(&uinput, width, height); + + return uinput; +} + +void vdagentd_uinput_destroy(struct vdagentd_uinput **uinputp) +{ + struct vdagentd_uinput *uinput = *uinputp; + + if (!uinput) + return; + + if (uinput->fd != -1) + close(uinput->fd); + free(uinput); + *uinputp = NULL; +} + +void vdagentd_uinput_update_size(struct vdagentd_uinput **uinputp, + int width, int height) +{ + struct vdagentd_uinput *uinput = *uinputp; + struct uinput_user_dev device = { + .name = "spice vdagent tablet", + .absmax [ ABS_X ] = width, + .absmax [ ABS_Y ] = height, + }; + int rc; + + if (uinput->width == width && uinput->height == height) + return; + + uinput->width = width; + uinput->height = height; + + if (uinput->fd != -1) + close(uinput->fd); + + uinput->fd = open(uinput->devname, O_RDWR); + if (uinput->fd == -1) { + fprintf(uinput->errfile, "open %s: %s\n", + uinput->devname, strerror(errno)); + vdagentd_uinput_destroy(uinputp); + return; + } + + rc = write(uinput->fd, &device, sizeof(device)); + if (rc != sizeof(device)) { + fprintf(uinput->errfile, "write %s: %s\n", + uinput->devname, strerror(errno)); + vdagentd_uinput_destroy(uinputp); + return; + } + + /* buttons */ + ioctl(uinput->fd, UI_SET_EVBIT, EV_KEY); + ioctl(uinput->fd, UI_SET_KEYBIT, BTN_LEFT); + ioctl(uinput->fd, UI_SET_KEYBIT, BTN_MIDDLE); + ioctl(uinput->fd, UI_SET_KEYBIT, BTN_RIGHT); + + /* wheel */ + ioctl(uinput->fd, UI_SET_EVBIT, EV_REL); + ioctl(uinput->fd, UI_SET_RELBIT, REL_WHEEL); + + /* abs ptr */ + ioctl(uinput->fd, UI_SET_EVBIT, EV_ABS); + ioctl(uinput->fd, UI_SET_ABSBIT, ABS_X); + ioctl(uinput->fd, UI_SET_ABSBIT, ABS_Y); + + rc = ioctl(uinput->fd, UI_DEV_CREATE); + if (rc < 0) { + fprintf(uinput->errfile, "create %s: %s\n", + uinput->devname, strerror(errno)); + vdagentd_uinput_destroy(uinputp); + } +} + +static void uinput_send_event(struct vdagentd_uinput **uinputp, + __u16 type, __u16 code, __s32 value) +{ + struct vdagentd_uinput *uinput = *uinputp; + struct input_event event = { + .type = type, + .code = code, + .value = value, + }; + int rc; + + rc = write(uinput->fd, &event, sizeof(event)); + if (rc != sizeof(event)) { + fprintf(uinput->errfile, "write %s: %s\n", + uinput->devname, strerror(errno)); + vdagentd_uinput_destroy(uinputp); + } +} + +void vdagentd_uinput_do_mouse(struct vdagentd_uinput **uinputp, + VDAgentMouseState *mouse) +{ + struct vdagentd_uinput *uinput = *uinputp; + struct button_s { + const char *name; + int mask; + int btn; + }; + static const struct button_s btns[] = { + { .name = "left", .mask = VD_AGENT_LBUTTON_MASK, .btn = BTN_LEFT }, + { .name = "middle", .mask = VD_AGENT_MBUTTON_MASK, .btn = BTN_MIDDLE }, + { .name = "right", .mask = VD_AGENT_RBUTTON_MASK, .btn = BTN_RIGHT }, + }; + static const struct button_s wheel[] = { + { .name = "up", .mask = VD_AGENT_UBUTTON_MASK, .btn = 1 }, + { .name = "down", .mask = VD_AGENT_DBUTTON_MASK, .btn = -1 }, + }; + int i, down; + + if (*uinputp && uinput->last.x != mouse->x) { + if (uinput->verbose) + fprintf(uinput->errfile, "mouse: abs-x %d\n", mouse->x); + uinput_send_event(uinputp, EV_ABS, ABS_X, mouse->x); + } + if (*uinputp && uinput->last.y != mouse->y) { + if (uinput->verbose) + fprintf(uinput->errfile, "mouse: abs-y %d\n", mouse->y); + uinput_send_event(uinputp, EV_ABS, ABS_Y, mouse->y); + } + for (i = 0; i < sizeof(btns)/sizeof(btns[0]) && *uinputp; i++) { + if ((uinput->last.buttons & btns[i].mask) == + (mouse->buttons & btns[i].mask)) + continue; + down = !!(mouse->buttons & btns[i].mask); + if (uinput->verbose) + fprintf(uinput->errfile, "mouse: btn-%s %s\n", + btns[i].name, down ? "down" : "up"); + uinput_send_event(uinputp, EV_KEY, btns[i].btn, down); + } + for (i = 0; i < sizeof(wheel)/sizeof(wheel[0]) && *uinputp; i++) { + if ((uinput->last.buttons & wheel[i].mask) == + (mouse->buttons & wheel[i].mask)) + continue; + if (mouse->buttons & wheel[i].mask) { + if (uinput->verbose) + fprintf(uinput->errfile, "mouse: wheel-%s\n", wheel[i].name); + uinput_send_event(uinputp, EV_REL, REL_WHEEL, wheel[i].btn); + } + } + + if (*uinputp) { + if (uinput->verbose) + fprintf(uinput->errfile, "mouse: syn\n"); + uinput_send_event(uinputp, EV_SYN, SYN_REPORT, 0); + } + + if (*uinputp) + uinput->last = *mouse; +} diff --git a/src/vdagentd-uinput.h b/src/vdagentd-uinput.h new file mode 100644 index 0000000..132986b --- /dev/null +++ b/src/vdagentd-uinput.h @@ -0,0 +1,38 @@ +/* vdagentd-uinput.c vdagentd uinput handling header + + Copyright 2010 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede + Gerd Hoffmann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef __VDAGENTD_UINPUT_H +#define __VDAGENTD_UINPUT_H + +#include + +struct vdagentd_uinput; + +struct vdagentd_uinput *vdagentd_uinput_create(const char *devname, + int width, int height, FILE *errfile, int verbose); +void vdagentd_uinput_destroy(struct vdagentd_uinput **uinputp); + +void vdagentd_uinput_do_mouse(struct vdagentd_uinput **uinputp, + VDAgentMouseState *mouse); +void vdagentd_uinput_update_size(struct vdagentd_uinput **uinputp, + int width, int height); + +#endif diff --git a/src/vdagentd.c b/src/vdagentd.c new file mode 100644 index 0000000..21588c5 --- /dev/null +++ b/src/vdagentd.c @@ -0,0 +1,771 @@ +/* vdagentd.c vdagentd (daemon) code + + Copyright 2010 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede + Gerd Hoffmann + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udscs.h" +#include "vdagentd-proto.h" +#include "vdagentd-proto-strings.h" +#include "vdagentd-uinput.h" +#include "vdagent-virtio-port.h" +#include "console-kit.h" + +struct agent_data { + char *session; + int width; + int height; +}; + +/* variables */ +static const char *logfilename = "/var/log/spice-vdagentd/spice-vdagentd.log"; +static const char *pidfilename = "/var/run/spice-vdagentd/spice-vdagentd.pid"; +static const char *portdev = "/dev/virtio-ports/com.redhat.spice.0"; +static const char *uinput_device = "/dev/uinput"; +static int debug = 0; +static struct udscs_server *server = NULL; +static struct vdagent_virtio_port *virtio_port = NULL; +static struct console_kit *console_kit = NULL; +static struct vdagentd_uinput *uinput = NULL; +static VDAgentMonitorsConfig *mon_config = NULL; +static uint32_t *capabilities = NULL; +static int capabilities_size = 0; +static const char *active_session = NULL; +static struct udscs_connection *active_session_conn = NULL; +static int agent_owns_clipboard[256] = { 0, }; +static FILE *logfile = NULL; +static int quit = 0; +static int retval = 0; + +/* utility functions */ +/* vdagentd <-> spice-client communication handling */ +static void send_capabilities(struct vdagent_virtio_port *vport, + uint32_t request) +{ + VDAgentAnnounceCapabilities *caps; + uint32_t size; + + size = sizeof(*caps) + VD_AGENT_CAPS_BYTES; + caps = calloc(1, size); + if (!caps) { + fprintf(logfile, + "out of memory allocating capabilities array (write)\n"); + return; + } + + caps->request = request; + VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MOUSE_STATE); + VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MONITORS_CONFIG); + VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_REPLY); + VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND); + VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_SELECTION); + + vdagent_virtio_port_write(vport, VDP_CLIENT_PORT, + VD_AGENT_ANNOUNCE_CAPABILITIES, 0, + (uint8_t *)caps, size); + free(caps); +} + +static void do_client_monitors(struct vdagent_virtio_port *vport, int port_nr, + VDAgentMessage *message_header, VDAgentMonitorsConfig *new_monitors) +{ + VDAgentReply reply; + uint32_t size; + + /* Store monitor config to send to agents when they connect */ + size = sizeof(VDAgentMonitorsConfig) + + new_monitors->num_of_monitors * sizeof(VDAgentMonConfig); + if (message_header->size != size) { + fprintf(logfile, "invalid message size for VDAgentMonitorsConfig\n"); + return; + } + + if (!mon_config || + mon_config->num_of_monitors != new_monitors->num_of_monitors) { + free(mon_config); + mon_config = malloc(size); + if (!mon_config) { + fprintf(logfile, "out of memory allocating monitors config\n"); + return; + } + } + memcpy(mon_config, new_monitors, size); + + /* Send monitor config to currently connected agents */ + udscs_server_write_all(server, VDAGENTD_MONITORS_CONFIG, 0, 0, + (uint8_t *)mon_config, size); + + /* Acknowledge reception of monitors config to spice server / client */ + reply.type = VD_AGENT_MONITORS_CONFIG; + reply.error = VD_AGENT_SUCCESS; + vdagent_virtio_port_write(vport, port_nr, VD_AGENT_REPLY, 0, + (uint8_t *)&reply, sizeof(reply)); +} + +static void do_client_capabilities(struct vdagent_virtio_port *vport, + VDAgentMessage *message_header, + VDAgentAnnounceCapabilities *caps) +{ + int new_size = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header->size); + + if (capabilities_size != new_size) { + capabilities_size = new_size; + free(capabilities); + capabilities = malloc(capabilities_size * sizeof(uint32_t)); + if (!capabilities) { + fprintf(logfile, + "out of memory allocating capabilities array (read)\n"); + capabilities_size = 0; + return; + } + } + memcpy(capabilities, caps->caps, capabilities_size * sizeof(uint32_t)); + if (caps->request) + send_capabilities(vport, 0); +} + +static void do_client_clipboard(struct vdagent_virtio_port *vport, + VDAgentMessage *message_header, uint8_t *data) +{ + uint32_t msg_type = 0, data_type = 0, size = message_header->size; + uint8_t selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD; + + if (!active_session_conn) { + fprintf(logfile, + "Could not find an agent connnection belonging to the " + "active session, ignoring client clipboard request\n"); + return; + } + + if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size, + VD_AGENT_CAP_CLIPBOARD_SELECTION)) { + selection = data[0]; + data += 4; + size -= 4; + } + + switch (message_header->type) { + case VD_AGENT_CLIPBOARD_GRAB: + msg_type = VDAGENTD_CLIPBOARD_GRAB; + agent_owns_clipboard[selection] = 0; + break; + case VD_AGENT_CLIPBOARD_REQUEST: { + VDAgentClipboardRequest *req = (VDAgentClipboardRequest *)data; + msg_type = VDAGENTD_CLIPBOARD_REQUEST; + data_type = req->type; + data = NULL; + size = 0; + break; + } + case VD_AGENT_CLIPBOARD: { + VDAgentClipboard *clipboard = (VDAgentClipboard *)data; + msg_type = VDAGENTD_CLIPBOARD_DATA; + data_type = clipboard->type; + size = size - sizeof(VDAgentClipboard); + data = clipboard->data; + break; + } + case VD_AGENT_CLIPBOARD_RELEASE: + msg_type = VDAGENTD_CLIPBOARD_RELEASE; + data = NULL; + size = 0; + break; + } + + udscs_write(active_session_conn, msg_type, selection, data_type, + data, size); +} + +int virtio_port_read_complete( + struct vdagent_virtio_port *vport, + int port_nr, + VDAgentMessage *message_header, + uint8_t *data) +{ + uint32_t min_size = 0; + + if (message_header->protocol != VD_AGENT_PROTOCOL) { + fprintf(logfile, "message with wrong protocol version ignoring\n"); + return 0; + } + + switch (message_header->type) { + case VD_AGENT_MOUSE_STATE: + if (message_header->size != sizeof(VDAgentMouseState)) + goto size_error; + vdagentd_uinput_do_mouse(&uinput, (VDAgentMouseState *)data); + if (!uinput) { + /* Try to re-open the tablet */ + struct agent_data *agent_data = + udscs_get_user_data(active_session_conn); + if (agent_data) + uinput = vdagentd_uinput_create(uinput_device, + agent_data->width, + agent_data->height, + logfile, debug > 1); + if (!uinput) { + fprintf(logfile, "Fatal uinput error\n"); + retval = 1; + quit = 1; + } + } + break; + case VD_AGENT_MONITORS_CONFIG: + if (message_header->size < sizeof(VDAgentMonitorsConfig)) + goto size_error; + do_client_monitors(vport, port_nr, message_header, + (VDAgentMonitorsConfig *)data); + break; + case VD_AGENT_ANNOUNCE_CAPABILITIES: + if (message_header->size < sizeof(VDAgentAnnounceCapabilities)) + goto size_error; + do_client_capabilities(vport, message_header, + (VDAgentAnnounceCapabilities *)data); + break; + case VD_AGENT_CLIPBOARD_GRAB: + case VD_AGENT_CLIPBOARD_REQUEST: + case VD_AGENT_CLIPBOARD: + case VD_AGENT_CLIPBOARD_RELEASE: + switch (message_header->type) { + case VD_AGENT_CLIPBOARD_GRAB: + min_size = sizeof(VDAgentClipboardGrab); break; + case VD_AGENT_CLIPBOARD_REQUEST: + min_size = sizeof(VDAgentClipboardRequest); break; + case VD_AGENT_CLIPBOARD: + min_size = sizeof(VDAgentClipboard); break; + } + if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size, + VD_AGENT_CAP_CLIPBOARD_SELECTION)) { + min_size += 4; + } + if (message_header->size < min_size) { + goto size_error; + } + do_client_clipboard(vport, message_header, data); + break; + default: + if (debug) + fprintf(logfile, "unknown message type %d\n", message_header->type); + break; + } + + return 0; + +size_error: + fprintf(logfile, "read: invalid message size: %u for message type: %u\n", + message_header->size, message_header->type); + return 0; +} + +/* vdagentd <-> vdagent communication handling */ +int do_agent_clipboard(struct udscs_connection *conn, + struct udscs_message_header *header, const uint8_t *data) +{ + const uint8_t *msg; + uint8_t *buf = NULL, selection = header->arg1; + uint32_t msg_type = 0, data_type = -1, size = header->size; + + if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size, + VD_AGENT_CAP_CLIPBOARD_BY_DEMAND)) + goto error; + + /* Check that this agent is from the currently active session */ + if (conn != active_session_conn) { + fprintf(logfile, "Clipboard request from agent " + "which is not in the active session?\n"); + goto error; + } + + if (!virtio_port) { + fprintf(logfile, + "Clipboard request from agent but no client connection\n"); + goto error; + } + + if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size, + VD_AGENT_CAP_CLIPBOARD_SELECTION) && + selection != VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) { + goto error; + } + + switch (header->type) { + case VDAGENTD_CLIPBOARD_GRAB: + msg_type = VD_AGENT_CLIPBOARD_GRAB; + agent_owns_clipboard[selection] = 1; + break; + case VDAGENTD_CLIPBOARD_REQUEST: + msg_type = VD_AGENT_CLIPBOARD_REQUEST; + data_type = header->arg2; + size = 0; + break; + case VDAGENTD_CLIPBOARD_DATA: + msg_type = VD_AGENT_CLIPBOARD; + data_type = header->arg2; + break; + case VDAGENTD_CLIPBOARD_RELEASE: + msg_type = VD_AGENT_CLIPBOARD_RELEASE; + size = 0; + agent_owns_clipboard[selection] = 0; + break; + } + + if (size != header->size) { + fprintf(logfile, + "unexpected extra data in clipboard msg, disconnecting agent\n"); + return -1; + } + + if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size, + VD_AGENT_CAP_CLIPBOARD_SELECTION)) { + size += 4; + } + if (data_type != -1) { + size += 4; + } + if (size != header->size) { + uint8_t *p; + buf = p = malloc(size); + if (!buf) { + fprintf(logfile, "out of memory allocating clipboard (write)\n"); + return -1; + } + if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size, + VD_AGENT_CAP_CLIPBOARD_SELECTION)) { + p[0] = selection; + p[1] = 0; + p[2] = 0; + p[3] = 0; + p += 4; + } + if (data_type != -1) { + uint32_t *u = (uint32_t *)p; + u[0] = data_type; + p += 4; + } + memcpy(p, data, header->size); + msg = buf; + } else { + msg = data; + } + + vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT, msg_type, + 0, msg, size); + free(buf); + return 0; + +error: + if (header->type == VDAGENTD_CLIPBOARD_REQUEST) { + /* Let the agent know no answer is coming */ + udscs_write(conn, VDAGENTD_CLIPBOARD_DATA, + selection, VD_AGENT_CLIPBOARD_NONE, NULL, 0); + } + return 0; +} + +/* When we open the vdagent virtio channel, the server automatically goes into + client mouse mode, so we can only have the channel open when we know the + active session resolution. This function checks that we have an agent in the + active session, and that it has told us its resolution. If these conditions + are met it sets the uinput tablet device's resolution and opens the virtio + channel (if it is not already open). If these conditions are not met, it + closes both. */ +static void check_xorg_resolution(void) +{ + struct agent_data *agent_data = udscs_get_user_data(active_session_conn); + + if (agent_data && agent_data->width) { + if (!uinput) + uinput = vdagentd_uinput_create(uinput_device, + agent_data->width, + agent_data->height, + logfile, debug > 1); + else + vdagentd_uinput_update_size(&uinput, agent_data->width, + agent_data->height); + if (!uinput) { + fprintf(logfile, "Fatal uinput error\n"); + retval = 1; + quit = 1; + return; + } + + if (!virtio_port) { + fprintf(logfile, "opening vdagent virtio channel\n"); + virtio_port = vdagent_virtio_port_create(portdev, + virtio_port_read_complete, + NULL, logfile); + if (!virtio_port) { + fprintf(logfile, + "Fatal error opening vdagent virtio channel\n"); + retval = 1; + quit = 1; + return; + } + send_capabilities(virtio_port, 1); + } + } else { + vdagentd_uinput_destroy(&uinput); + if (virtio_port) { + vdagent_virtio_port_flush(&virtio_port); + vdagent_virtio_port_destroy(&virtio_port); + fprintf(logfile, "closed vdagent virtio channel\n"); + } + } +} + +static int connection_matches_active_session(struct udscs_connection **connp, + void *priv) +{ + struct udscs_connection **conn_ret = (struct udscs_connection **)priv; + struct agent_data *agent_data = udscs_get_user_data(*connp); + + /* Check if this connection matches the currently active session */ + if (!agent_data->session || !active_session) + return 0; + if (strcmp(agent_data->session, active_session)) + return 0; + + *conn_ret = *connp; + return 1; +} + +void release_clipboards(void) +{ + uint8_t sel; + + for (sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; ++sel) { + if (agent_owns_clipboard[sel] && virtio_port) { + vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT, + VD_AGENT_CLIPBOARD_RELEASE, 0, &sel, 1); + } + agent_owns_clipboard[sel] = 0; + } +} + +void update_active_session_connection(void) +{ + struct udscs_connection *new_conn = NULL; + int n; + + if (!active_session) + active_session = console_kit_get_active_session(console_kit); + + n = udscs_server_for_all_clients(server, connection_matches_active_session, + (void*)&new_conn); + if (n != 1) + new_conn = NULL; + + if (new_conn == active_session_conn) + return; + + active_session_conn = new_conn; + + release_clipboards(); + + check_xorg_resolution(); +} + +void agent_connect(struct udscs_connection *conn) +{ + uint32_t pid; + struct agent_data *agent_data; + + agent_data = calloc(1, sizeof(*agent_data)); + if (!agent_data) { + fprintf(logfile, "Out of memory allocating agent data, disconnecting\n"); + udscs_destroy_connection(&conn); + return; + } + + pid = udscs_get_peer_cred(conn).pid; + agent_data->session = console_kit_session_for_pid(console_kit, pid); + udscs_set_user_data(conn, (void *)agent_data); + update_active_session_connection(); + + if (mon_config) + udscs_write(conn, VDAGENTD_MONITORS_CONFIG, 0, 0, + (uint8_t *)mon_config, sizeof(VDAgentMonitorsConfig) + + mon_config->num_of_monitors * sizeof(VDAgentMonConfig)); +} + +void agent_disconnect(struct udscs_connection *conn) +{ + struct agent_data *agent_data = udscs_get_user_data(conn); + + free(agent_data->session); + agent_data->session = NULL; + update_active_session_connection(); + + free(agent_data); +} + +void agent_read_complete(struct udscs_connection **connp, + struct udscs_message_header *header, uint8_t *data) +{ + struct agent_data *agent_data = udscs_get_user_data(*connp); + + switch (header->type) { + case VDAGENTD_GUEST_XORG_RESOLUTION: { + struct vdagentd_guest_xorg_resolution *res = + (struct vdagentd_guest_xorg_resolution *)data; + + if (header->size != sizeof(*res)) { + fprintf(logfile, + "guest xorg resolution message has wrong size, disconnecting agent\n"); + udscs_destroy_connection(connp); + return; + } + + agent_data->width = res->width; + agent_data->height = res->height; + check_xorg_resolution(); + break; + } + case VDAGENTD_CLIPBOARD_GRAB: + case VDAGENTD_CLIPBOARD_REQUEST: + case VDAGENTD_CLIPBOARD_DATA: + case VDAGENTD_CLIPBOARD_RELEASE: + if (do_agent_clipboard(*connp, header, data)) { + udscs_destroy_connection(connp); + return; + } + break; + default: + fprintf(logfile, "unknown message from vdagent: %u, ignoring\n", + header->type); + } + free(data); +} + +/* main */ + +static void usage(FILE *fp) +{ + fprintf(fp, + "vdagentd\n" + "options:\n" + " -h print this text\n" + " -d log debug messages (use twice for extra info)\n" + " -s set virtio serial port [%s]\n" + " -u set uinput device [%s]\n" + " -x don't daemonize (and log to logfile)\n", + portdev, uinput_device); +} + +void daemonize(void) +{ + int x; + FILE *pidfile; + + /* detach from terminal */ + switch (fork()) { + case 0: + close(0); close(1); close(2); + setsid(); + x = open("/dev/null", O_RDWR); dup(x); dup(x); + pidfile = fopen(pidfilename, "w"); + if (pidfile) { + fprintf(pidfile, "%d\n", (int)getpid()); + fclose(pidfile); + } + break; + case -1: + fprintf(logfile, "fork: %s\n", strerror(errno)); + retval = 1; + default: + udscs_destroy_server(server); + if (logfile != stderr) + fclose(logfile); + exit(retval); + } +} + +void main_loop(void) +{ + fd_set readfds, writefds; + int n, nfds, ck_fd = 0; + + while (!quit) { + FD_ZERO(&readfds); + FD_ZERO(&writefds); + + nfds = udscs_server_fill_fds(server, &readfds, &writefds); + n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds); + if (n >= nfds) + nfds = n + 1; + + ck_fd = console_kit_get_fd(console_kit); + FD_SET(ck_fd, &readfds); + if (ck_fd >= nfds) + nfds = ck_fd + 1; + + n = select(nfds, &readfds, &writefds, NULL, NULL); + if (n == -1) { + if (errno == EINTR) + continue; + fprintf(logfile, "Fatal error select: %s\n", strerror(errno)); + retval = 1; + break; + } + + udscs_server_handle_fds(server, &readfds, &writefds); + + if (virtio_port) { + vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds); + if (!virtio_port) { + fprintf(logfile, + "AIIEEE lost spice client connection, reconnecting\n"); + virtio_port = vdagent_virtio_port_create(portdev, + virtio_port_read_complete, + NULL, logfile); + } + if (!virtio_port) { + fprintf(logfile, + "Fatal error opening vdagent virtio channel\n"); + retval = 1; + break; + } + } + + if (FD_ISSET(ck_fd, &readfds)) { + active_session = console_kit_get_active_session(console_kit); + update_active_session_connection(); + } + fflush(logfile); + } +} + +static void quit_handler(int sig) +{ + quit = 1; +} + +int main(int argc, char *argv[]) +{ + int c; + int do_daemonize = 1; + struct sigaction act; + + for (;;) { + if (-1 == (c = getopt(argc, argv, "-dhxs:u:"))) + break; + switch (c) { + case 'd': + debug++; + break; + case 's': + portdev = optarg; + break; + case 'u': + uinput_device = optarg; + break; + case 'x': + do_daemonize = 0; + break; + case 'h': + usage(stdout); + return 0; + default: + usage(stderr); + return 1; + } + } + + memset(&act, 0, sizeof(act)); + act.sa_flags = SA_RESTART; + act.sa_handler = quit_handler; + sigaction(SIGINT, &act, NULL); + sigaction(SIGHUP, &act, NULL); + sigaction(SIGTERM, &act, NULL); + sigaction(SIGQUIT, &act, NULL); + + if (do_daemonize) { + logfile = fopen(logfilename, "a"); + if (!logfile) { + fprintf(stderr, "Error opening %s: %s\n", logfilename, + strerror(errno)); + logfile = stderr; + } + } else + logfile = stderr; + + /* Setup communication with vdagent process(es) */ + server = udscs_create_server(VDAGENTD_SOCKET, agent_connect, + agent_read_complete, agent_disconnect, + vdagentd_messages, VDAGENTD_NO_MESSAGES, + debug? logfile:NULL, logfile); + if (!server) { + fprintf(logfile, "Fatal could not create server socket %s\n", + VDAGENTD_SOCKET); + if (logfile != stderr) + fclose(logfile); + return 1; + } + if (chmod(VDAGENTD_SOCKET, 0666)) { + fprintf(logfile, "Fatal could not change permissions on %s: %s\n", + VDAGENTD_SOCKET, strerror(errno)); + udscs_destroy_server(server); + if (logfile != stderr) + fclose(logfile); + return 1; + } + + if (do_daemonize) + daemonize(); + + console_kit = console_kit_create(logfile); + if (!console_kit) { + fprintf(logfile, "Fatal could not connect to console kit\n"); + udscs_destroy_server(server); + if (logfile != stderr) + fclose(logfile); + return 1; + } + + main_loop(); + + release_clipboards(); + + vdagentd_uinput_destroy(&uinput); + vdagent_virtio_port_flush(&virtio_port); + vdagent_virtio_port_destroy(&virtio_port); + console_kit_destroy(console_kit); + udscs_destroy_server(server); + if (unlink(VDAGENTD_SOCKET) != 0) + fprintf(logfile, "unlink %s: %s\n", VDAGENTD_SOCKET, strerror(errno)); + fprintf(logfile, "vdagentd quiting, returning status %d\n", retval); + if (logfile != stderr) + fclose(logfile); + + if (do_daemonize) + unlink(pidfilename); + + return retval; +} diff --git a/udscs.c b/udscs.c deleted file mode 100644 index 3d9a9e3..0000000 --- a/udscs.c +++ /dev/null @@ -1,572 +0,0 @@ -/* udscs.c Unix Domain Socket Client Server framework. A framework for quickly - creating select() based servers capable of handling multiple clients and - matching select() based clients using variable size messages. - - Copyright 2010 Red Hat, Inc. - - Red Hat Authors: - Hans de Goede - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include -#include -#include -#include -#include -#include "udscs.h" - -struct udscs_buf { - uint8_t *buf; - size_t pos; - size_t size; - - struct udscs_buf *next; -}; - -struct udscs_connection { - int fd; - const char * const *type_to_string; - int no_types; - FILE *logfile; - FILE *errfile; - struct ucred peer_cred; - void *user_data; - - /* Read stuff, single buffer, separate header and data buffer */ - int header_read; - struct udscs_message_header header; - struct udscs_buf data; - - /* Writes are stored in a linked list of buffers, with both the header - + data for a single message in 1 buffer. */ - struct udscs_buf *write_buf; - - /* Callbacks */ - udscs_read_callback read_callback; - udscs_disconnect_callback disconnect_callback; - - struct udscs_connection *next; - struct udscs_connection *prev; -}; - -struct udscs_server { - int fd; - const char * const *type_to_string; - int no_types; - FILE *logfile; - FILE *errfile; - struct udscs_connection connections_head; - udscs_connect_callback connect_callback; - udscs_read_callback read_callback; - udscs_disconnect_callback disconnect_callback; -}; - -static void udscs_do_write(struct udscs_connection **connp); -static void udscs_do_read(struct udscs_connection **connp); - - -struct udscs_server *udscs_create_server(const char *socketname, - udscs_connect_callback connect_callback, - udscs_read_callback read_callback, - udscs_disconnect_callback disconnect_callback, - const char * const type_to_string[], int no_types, - FILE *logfile, FILE *errfile) -{ - int c; - struct sockaddr_un address; - struct udscs_server *server; - - server = calloc(1, sizeof(*server)); - if (!server) - return NULL; - - server->logfile = logfile; - server->errfile = errfile; - server->type_to_string = type_to_string; - server->no_types = no_types; - - server->fd = socket(PF_UNIX, SOCK_STREAM, 0); - if (server->fd == -1) { - fprintf(server->errfile, "creating unix domain socket: %s\n", - strerror(errno)); - free(server); - return NULL; - } - - address.sun_family = AF_UNIX; - snprintf(address.sun_path, sizeof(address.sun_path), "%s", socketname); - c = bind(server->fd, (struct sockaddr *)&address, sizeof(address)); - if (c != 0) { - fprintf(server->errfile, "bind %s: %s\n", socketname, strerror(errno)); - free(server); - return NULL; - } - - c = listen(server->fd, 5); - if (c != 0) { - fprintf(server->errfile, "listen: %s\n", strerror(errno)); - free(server); - return NULL; - } - - server->connect_callback = connect_callback; - server->read_callback = read_callback; - server->disconnect_callback = disconnect_callback; - - return server; -} - -void udscs_destroy_server(struct udscs_server *server) -{ - struct udscs_connection *conn, *next_conn; - - if (!server) - return; - - conn = server->connections_head.next; - while (conn) { - next_conn = conn->next; - udscs_destroy_connection(&conn); - conn = next_conn; - } - close(server->fd); - free(server); -} - -struct udscs_connection *udscs_connect(const char *socketname, - udscs_read_callback read_callback, - udscs_disconnect_callback disconnect_callback, - const char * const type_to_string[], int no_types, - FILE *logfile, FILE *errfile) -{ - int c; - struct sockaddr_un address; - struct udscs_connection *conn; - - conn = calloc(1, sizeof(*conn)); - if (!conn) - return NULL; - - conn->logfile = logfile; - conn->errfile = errfile; - conn->type_to_string = type_to_string; - conn->no_types = no_types; - - conn->fd = socket(PF_UNIX, SOCK_STREAM, 0); - if (conn->fd == -1) { - fprintf(conn->errfile, "creating unix domain socket: %s\n", - strerror(errno)); - free(conn); - return NULL; - } - - address.sun_family = AF_UNIX; - snprintf(address.sun_path, sizeof(address.sun_path), "%s", socketname); - c = connect(conn->fd, (struct sockaddr *)&address, sizeof(address)); - if (c != 0) { - fprintf(conn->errfile, "connect %s: %s\n", socketname, - strerror(errno)); - free(conn); - return NULL; - } - - conn->read_callback = read_callback; - conn->disconnect_callback = disconnect_callback; - - if (conn->logfile) - fprintf(conn->logfile, "%p connected to %s\n", conn, socketname); - - return conn; -} - -void udscs_destroy_connection(struct udscs_connection **connp) -{ - struct udscs_buf *wbuf, *next_wbuf; - struct udscs_connection *conn = *connp; - - if (!conn) - return; - - if (conn->disconnect_callback) - conn->disconnect_callback(conn); - - wbuf = conn->write_buf; - while (wbuf) { - next_wbuf = wbuf->next; - free(wbuf->buf); - free(wbuf); - wbuf = next_wbuf; - } - - free(conn->data.buf); - - if (conn->next) - conn->next->prev = conn->prev; - if (conn->prev) - conn->prev->next = conn->next; - - close(conn->fd); - - if (conn->logfile) - fprintf(conn->logfile, "%p disconnected\n", conn); - - free(conn); - *connp = NULL; -} - -struct ucred udscs_get_peer_cred(struct udscs_connection *conn) -{ - return conn->peer_cred; -} - -int udscs_server_fill_fds(struct udscs_server *server, fd_set *readfds, - fd_set *writefds) -{ - struct udscs_connection *conn; - int nfds = server->fd + 1; - - if (!server) - return -1; - - FD_SET(server->fd, readfds); - - conn = server->connections_head.next; - while (conn) { - int conn_nfds = udscs_client_fill_fds(conn, readfds, writefds); - if (conn_nfds > nfds) - nfds = conn_nfds; - - conn = conn->next; - } - - return nfds; -} - -int udscs_client_fill_fds(struct udscs_connection *conn, fd_set *readfds, - fd_set *writefds) -{ - if (!conn) - return -1; - - FD_SET(conn->fd, readfds); - if (conn->write_buf) - FD_SET(conn->fd, writefds); - - return conn->fd + 1; -} - -static void udscs_server_accept(struct udscs_server *server) { - struct udscs_connection *new_conn, *conn; - struct sockaddr_un address; - socklen_t length = sizeof(address); - int r, fd; - - fd = accept(server->fd, (struct sockaddr *)&address, &length); - if (fd == -1) { - if (errno == EINTR) - return; - fprintf(server->errfile, "accept: %s\n", strerror(errno)); - return; - } - - new_conn = calloc(1, sizeof(*conn)); - if (!new_conn) { - fprintf(server->errfile, "out of memory, disconnecting new client\n"); - close(fd); - return; - } - - new_conn->fd = fd; - new_conn->logfile = server->logfile; - new_conn->errfile = server->errfile; - new_conn->type_to_string = server->type_to_string; - new_conn->no_types = server->no_types; - new_conn->read_callback = server->read_callback; - new_conn->disconnect_callback = server->disconnect_callback; - - length = sizeof(new_conn->peer_cred); - r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &new_conn->peer_cred, &length); - if (r != 0) { - fprintf(server->errfile, - "Could not get peercred, disconnecting new client\n"); - close(fd); - free(new_conn); - return; - } - - conn = &server->connections_head; - while (conn->next) - conn = conn->next; - - new_conn->prev = conn; - conn->next = new_conn; - - if (server->logfile) - fprintf(server->logfile, "new client accepted: %p, pid: %d\n", - new_conn, (int)new_conn->peer_cred.pid); - - if (server->connect_callback) - server->connect_callback(new_conn); -} - -void udscs_server_handle_fds(struct udscs_server *server, fd_set *readfds, - fd_set *writefds) -{ - struct udscs_connection *conn, *next_conn; - - if (!server) - return; - - if (FD_ISSET(server->fd, readfds)) - udscs_server_accept(server); - - conn = server->connections_head.next; - while (conn) { - /* conn maybe destroyed by udscs_client_handle_fds (when disconnected), - so get the next connection first. */ - next_conn = conn->next; - udscs_client_handle_fds(&conn, readfds, writefds); - conn = next_conn; - } -} - -void udscs_client_handle_fds(struct udscs_connection **connp, fd_set *readfds, - fd_set *writefds) -{ - if (!*connp) - return; - - if (FD_ISSET((*connp)->fd, readfds)) - udscs_do_read(connp); - - if (*connp && FD_ISSET((*connp)->fd, writefds)) - udscs_do_write(connp); -} - -int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t arg1, - uint32_t arg2, const uint8_t *data, uint32_t size) -{ - struct udscs_buf *wbuf, *new_wbuf; - struct udscs_message_header header; - - new_wbuf = malloc(sizeof(*new_wbuf)); - if (!new_wbuf) - return -1; - - new_wbuf->pos = 0; - new_wbuf->size = sizeof(header) + size; - new_wbuf->next = NULL; - new_wbuf->buf = malloc(new_wbuf->size); - if (!new_wbuf->buf) { - free(new_wbuf); - return -1; - } - - header.type = type; - header.arg1 = arg1; - header.arg2 = arg2; - header.size = size; - - memcpy(new_wbuf->buf, &header, sizeof(header)); - memcpy(new_wbuf->buf + sizeof(header), data, size); - - if (conn->logfile) { - if (type < conn->no_types) - fprintf(conn->logfile, "%p sent %s, arg1: %u, arg2: %u, size %u\n", - conn, conn->type_to_string[type], arg1, arg2, size); - else - fprintf(conn->logfile, - "%p sent invalid message %u, arg1: %u, arg2: %u, size %u\n", - conn, type, arg1, arg2, size); - } - - if (!conn->write_buf) { - conn->write_buf = new_wbuf; - return 0; - } - - /* maybe we should limit the write_buf stack depth ? */ - wbuf = conn->write_buf; - while (wbuf->next) - wbuf = wbuf->next; - - wbuf->next = new_wbuf; - - return 0; -} - -int udscs_server_write_all(struct udscs_server *server, - uint32_t type, uint32_t arg1, uint32_t arg2, - const uint8_t *data, uint32_t size) -{ - struct udscs_connection *conn; - - conn = server->connections_head.next; - while (conn) { - if (udscs_write(conn, type, arg1, arg2, data, size)) - return -1; - conn = conn->next; - } - - return 0; -} - -int udscs_server_for_all_clients(struct udscs_server *server, - udscs_for_all_clients_callback func, void *priv) -{ - int r = 0; - struct udscs_connection *conn, *next_conn; - - if (!server) - return 0; - - conn = server->connections_head.next; - while (conn) { - /* Get next conn as func may destroy the current conn */ - next_conn = conn->next; - r += func(&conn, priv); - conn = next_conn; - } - return r; -} - -static void udscs_read_complete(struct udscs_connection **connp) -{ - struct udscs_connection *conn = *connp; - - if (conn->logfile) { - if (conn->header.type < conn->no_types) - fprintf(conn->logfile, - "%p received %s, arg1: %u, arg2: %u, size %u\n", - conn, conn->type_to_string[conn->header.type], - conn->header.arg1, conn->header.arg2, conn->header.size); - else - fprintf(conn->logfile, - "%p received invalid message %u, arg1: %u, arg2: %u, size %u\n", - conn, conn->header.type, conn->header.arg1, conn->header.arg2, - conn->header.size); - } - - if (conn->read_callback) { - conn->read_callback(connp, &conn->header, conn->data.buf); - if (!*connp) /* Was the connection disconnected by the callback ? */ - return; - } - - conn->header_read = 0; - memset(&conn->data, 0, sizeof(conn->data)); -} - -static void udscs_do_read(struct udscs_connection **connp) -{ - ssize_t n; - size_t to_read; - uint8_t *dest; - struct udscs_connection *conn = *connp; - - if (conn->header_read < sizeof(conn->header)) { - to_read = sizeof(conn->header) - conn->header_read; - dest = (uint8_t *)&conn->header + conn->header_read; - } else { - to_read = conn->data.size - conn->data.pos; - dest = conn->data.buf + conn->data.pos; - } - - n = read(conn->fd, dest, to_read); - if (n < 0) { - if (errno == EINTR) - return; - fprintf(conn->errfile, - "reading unix domain socket: %s, disconnecting %p\n", - strerror(errno), conn); - } - if (n <= 0) { - udscs_destroy_connection(connp); - return; - } - - if (conn->header_read < sizeof(conn->header)) { - conn->header_read += n; - if (conn->header_read == sizeof(conn->header)) { - if (conn->header.size == 0) { - udscs_read_complete(connp); - return; - } - conn->data.pos = 0; - conn->data.size = conn->header.size; - conn->data.buf = malloc(conn->data.size); - if (!conn->data.buf) { - fprintf(conn->errfile, "out of memory, disconnecting %p\n", - conn); - udscs_destroy_connection(connp); - return; - } - } - } else { - conn->data.pos += n; - if (conn->data.pos == conn->data.size) - udscs_read_complete(connp); - } -} - -static void udscs_do_write(struct udscs_connection **connp) -{ - ssize_t n; - size_t to_write; - struct udscs_connection *conn = *connp; - - struct udscs_buf* wbuf = conn->write_buf; - if (!wbuf) { - fprintf(conn->errfile, - "%p do_write called on a connection without a write buf ?!\n", - conn); - return; - } - - to_write = wbuf->size - wbuf->pos; - n = write(conn->fd, wbuf->buf + wbuf->pos, to_write); - if (n < 0) { - if (errno == EINTR) - return; - fprintf(conn->errfile, - "writing to unix domain socket: %s, disconnecting %p\n", - strerror(errno), conn); - udscs_destroy_connection(connp); - return; - } - - wbuf->pos += n; - if (wbuf->pos == wbuf->size) { - conn->write_buf = wbuf->next; - free(wbuf->buf); - free(wbuf); - } -} - -void udscs_set_user_data(struct udscs_connection *conn, void *data) -{ - conn->user_data = data; -} - -void *udscs_get_user_data(struct udscs_connection *conn) -{ - if (!conn) - return NULL; - - return conn->user_data; -} diff --git a/udscs.h b/udscs.h deleted file mode 100644 index 251e2d0..0000000 --- a/udscs.h +++ /dev/null @@ -1,126 +0,0 @@ -/* udscs.h Unix Domain Socket Client Server framework header file - - Copyright 2010 Red Hat, Inc. - - Red Hat Authors: - Hans de Goede - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef __UDSCS_H -#define __UDSCS_H - -#include -#include -#include -#include - -struct udscs_connection; -struct udscs_server; -struct udscs_message_header { - uint32_t type; - uint32_t arg1; - uint32_t arg2; - uint32_t size; -}; - -/* Callbacks with this type will be called when a new connection to a - server is accepted. */ -typedef void (*udscs_connect_callback)(struct udscs_connection *conn); -/* Callbacks with this type will be called when a complete message has been - received. The callback may call udscs_destroy_connection, in which case - *connp must be made NULL (which udscs_destroy_connection takes care of) */ -typedef void (*udscs_read_callback)(struct udscs_connection **connp, - struct udscs_message_header *header, uint8_t *data); -/* Callback type for udscs_server_for_all_clients. Clients can be disconnected - from this callback just like with a read callback. */ -typedef int (*udscs_for_all_clients_callback)(struct udscs_connection **connp, - void *priv); -/* Callbacks with this type will be called when the connection is disconnected. - Note: - 1) udscs will destroy the connection in question itself after - this callback has completed! - 2) This callback is always called, even if the disconnect is initiated - by the udscs user through returning -1 from a read callback, or - by explictly calling udscs_destroy_connection */ -typedef void (*udscs_disconnect_callback)(struct udscs_connection *conn); - -/* Create a unix domain socket named name and start listening on it. */ -struct udscs_server *udscs_create_server(const char *socketname, - udscs_connect_callback connect_callback, - udscs_read_callback read_callback, - udscs_disconnect_callback disconnect_callback, - const char * const type_to_string[], int no_types, - FILE *logfile, FILE *errfile); - -void udscs_destroy_server(struct udscs_server *server); - -/* Connect to a unix domain socket named name. */ -struct udscs_connection *udscs_connect(const char *socketname, - udscs_read_callback read_callback, - udscs_disconnect_callback disconnect_callback, - const char * const type_to_string[], int no_types, - FILE *logfile, FILE *errfile); - -/* The contents of connp will be made NULL */ -void udscs_destroy_connection(struct udscs_connection **connp); - - -/* Given an udscs server or client fill the fd_sets pointed to by readfds and - writefds for select() usage. - - Return value: value of the highest fd + 1 */ -int udscs_server_fill_fds(struct udscs_server *server, fd_set *readfds, - fd_set *writefds); - -int udscs_client_fill_fds(struct udscs_connection *conn, fd_set *readfds, - fd_set *writefds); - -/* Handle any events flagged by select for the given udscs server or client. */ -void udscs_server_handle_fds(struct udscs_server *server, fd_set *readfds, - fd_set *writefds); - -/* Note the connection may be destroyed (when disconnected) by this call - in this case the disconnect calllback will get called before the - destruction and the contents of connp will be made NULL */ -void udscs_client_handle_fds(struct udscs_connection **connp, fd_set *readfds, - fd_set *writefds); - - -/* Queue a message for delivery to the client connected through conn. - - Returns 0 on success -1 on error (only happens when malloc fails) */ -int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t arg1, - uint32_t arg2, const uint8_t *data, uint32_t size); - -/* Like udscs_write, but then send the message to all clients connected to - the server */ -int udscs_server_write_all(struct udscs_server *server, - uint32_t type, uint32_t arg1, uint32_t arg2, - const uint8_t *data, uint32_t size); -/* Call func for all clients connected to the server, passing through - priv to all func calls. Returns the total of the return values from all - calls to func */ -int udscs_server_for_all_clients(struct udscs_server *server, - udscs_for_all_clients_callback func, void *priv); - - -struct ucred udscs_get_peer_cred(struct udscs_connection *conn); - -/* For server use, to associate per connection data with a connection */ -void udscs_set_user_data(struct udscs_connection *conn, void *data); -void *udscs_get_user_data(struct udscs_connection *conn); - -#endif diff --git a/vdagent-virtio-port.c b/vdagent-virtio-port.c deleted file mode 100644 index 02457d1..0000000 --- a/vdagent-virtio-port.c +++ /dev/null @@ -1,357 +0,0 @@ -/* vdagent-virtio-port.c virtio port communication code - - Copyright 2010 Red Hat, Inc. - - Red Hat Authors: - Hans de Goede - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include -#include -#include -#include -#include "vdagent-virtio-port.h" - -#define VDP_LAST_PORT VDP_SERVER_PORT - -struct vdagent_virtio_port_buf { - uint8_t *buf; - size_t pos; - size_t size; - - struct vdagent_virtio_port_buf *next; -}; - -/* Data to keep track of the assembling of vdagent messages per chunk port, - for de-multiplexing the messages */ -struct vdagent_virtio_port_chunk_port_data { - int message_header_read; - int message_data_pos; - VDAgentMessage message_header; - uint8_t *message_data; -}; - -struct vdagent_virtio_port { - int fd; - FILE *errfile; - - /* Chunk read stuff, single buffer, separate header and data buffer */ - int chunk_header_read; - int chunk_data_pos; - VDIChunkHeader chunk_header; - uint8_t chunk_data[VD_AGENT_MAX_DATA_SIZE]; - - /* Per chunk port data */ - struct vdagent_virtio_port_chunk_port_data port_data[VDP_LAST_PORT + 1]; - - /* Writes are stored in a linked list of buffers, with both the header - + data for a single message in 1 buffer. */ - struct vdagent_virtio_port_buf *write_buf; - - /* Callbacks */ - vdagent_virtio_port_read_callback read_callback; - vdagent_virtio_port_disconnect_callback disconnect_callback; -}; - -static void vdagent_virtio_port_do_write(struct vdagent_virtio_port **vportp); -static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **vportp); - -struct vdagent_virtio_port *vdagent_virtio_port_create(const char *portname, - vdagent_virtio_port_read_callback read_callback, - vdagent_virtio_port_disconnect_callback disconnect_callback, - FILE *errfile) -{ - struct vdagent_virtio_port *vport; - - vport = calloc(1, sizeof(*vport)); - if (!vport) - return 0; - - vport->errfile = errfile; - vport->fd = open(portname, O_RDWR); - if (vport->fd == -1) { - fprintf(vport->errfile, "open %s: %s\n", portname, strerror(errno)); - free(vport); - return NULL; - } - - vport->read_callback = read_callback; - vport->disconnect_callback = disconnect_callback; - - return vport; -} - -void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp) -{ - struct vdagent_virtio_port_buf *wbuf, *next_wbuf; - struct vdagent_virtio_port *vport = *vportp; - int i; - - if (!vport) - return; - - if (vport->disconnect_callback) - vport->disconnect_callback(vport); - - wbuf = vport->write_buf; - while (wbuf) { - next_wbuf = wbuf->next; - free(wbuf->buf); - free(wbuf); - wbuf = next_wbuf; - } - - for (i = 0; i <= VDP_LAST_PORT; i++) { - free(vport->port_data[i].message_data); - } - - close(vport->fd); - free(vport); - *vportp = NULL; -} - -int vdagent_virtio_port_fill_fds(struct vdagent_virtio_port *vport, - fd_set *readfds, fd_set *writefds) -{ - if (!vport) - return -1; - - FD_SET(vport->fd, readfds); - if (vport->write_buf) - FD_SET(vport->fd, writefds); - - return vport->fd + 1; -} - -void vdagent_virtio_port_handle_fds(struct vdagent_virtio_port **vportp, - fd_set *readfds, fd_set *writefds) -{ - if (!*vportp) - return; - - if (FD_ISSET((*vportp)->fd, readfds)) - vdagent_virtio_port_do_read(vportp); - - if (*vportp && FD_ISSET((*vportp)->fd, writefds)) - vdagent_virtio_port_do_write(vportp); -} - -int vdagent_virtio_port_write( - struct vdagent_virtio_port *vport, - uint32_t port_nr, - uint32_t message_type, - uint32_t message_opaque, - const uint8_t *data, - uint32_t data_size) -{ - struct vdagent_virtio_port_buf *wbuf, *new_wbuf; - VDIChunkHeader chunk_header; - VDAgentMessage message_header; - - new_wbuf = malloc(sizeof(*new_wbuf)); - if (!new_wbuf) - return -1; - - new_wbuf->pos = 0; - new_wbuf->size = sizeof(chunk_header) + sizeof(message_header) + data_size; - new_wbuf->next = NULL; - new_wbuf->buf = malloc(new_wbuf->size); - if (!new_wbuf->buf) { - free(new_wbuf); - return -1; - } - - chunk_header.port = port_nr; - chunk_header.size = sizeof(message_header) + data_size; - message_header.protocol = VD_AGENT_PROTOCOL; - message_header.type = message_type; - message_header.opaque = message_opaque; - message_header.size = data_size; - - memcpy(new_wbuf->buf, &chunk_header, sizeof(chunk_header)); - memcpy(new_wbuf->buf + sizeof(chunk_header), &message_header, - sizeof(message_header)); - memcpy(new_wbuf->buf + sizeof(chunk_header) + sizeof(message_header), - data, data_size); - - if (!vport->write_buf) { - vport->write_buf = new_wbuf; - return 0; - } - - /* maybe we should limit the write_buf stack depth ? */ - wbuf = vport->write_buf; - while (wbuf->next) - wbuf = wbuf->next; - - wbuf->next = new_wbuf; - - return 0; -} - -void vdagent_virtio_port_flush(struct vdagent_virtio_port **vportp) -{ - while (*vportp && (*vportp)->write_buf) - vdagent_virtio_port_do_write(vportp); -} - -static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp) -{ - int avail, read, pos = 0; - struct vdagent_virtio_port *vport = *vportp; - struct vdagent_virtio_port_chunk_port_data *port = - &vport->port_data[vport->chunk_header.port]; - - if (port->message_header_read < sizeof(port->message_header)) { - read = sizeof(port->message_header) - port->message_header_read; - if (read > vport->chunk_header.size) { - read = vport->chunk_header.size; - } - memcpy((uint8_t *)&port->message_header + port->message_header_read, - vport->chunk_data, read); - port->message_header_read += read; - if (port->message_header_read == sizeof(port->message_header) && - port->message_header.size) { - port->message_data = malloc(port->message_header.size); - if (!port->message_data) { - fprintf(vport->errfile, "out of memory, disconnecting virtio\n"); - vdagent_virtio_port_destroy(vportp); - return; - } - } - pos = read; - } - - if (port->message_header_read == sizeof(port->message_header)) { - read = port->message_header.size - port->message_data_pos; - avail = vport->chunk_header.size - pos; - - if (avail > read) { - fprintf(vport->errfile, "chunk larger then message, lost sync?\n"); - vdagent_virtio_port_destroy(vportp); - return; - } - - if (avail < read) - read = avail; - - if (read) { - memcpy(port->message_data + port->message_data_pos, - vport->chunk_data + pos, read); - port->message_data_pos += read; - } - - if (port->message_data_pos == port->message_header.size) { - if (vport->read_callback) { - int r = vport->read_callback(vport, vport->chunk_header.port, - &port->message_header, port->message_data); - if (r == -1) { - vdagent_virtio_port_destroy(vportp); - return; - } - } - port->message_header_read = 0; - port->message_data_pos = 0; - free(port->message_data); - port->message_data = NULL; - } - } -} - -static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **vportp) -{ - ssize_t n; - size_t to_read; - uint8_t *dest; - struct vdagent_virtio_port *vport = *vportp; - - if (vport->chunk_header_read < sizeof(vport->chunk_header)) { - to_read = sizeof(vport->chunk_header) - vport->chunk_header_read; - dest = (uint8_t *)&vport->chunk_header + vport->chunk_header_read; - } else { - to_read = vport->chunk_header.size - vport->chunk_data_pos; - dest = vport->chunk_data + vport->chunk_data_pos; - } - - n = read(vport->fd, dest, to_read); - if (n < 0) { - if (errno == EINTR) - return; - fprintf(vport->errfile, "reading from vdagent virtio port: %s\n", - strerror(errno)); - } - if (n <= 0) { - vdagent_virtio_port_destroy(vportp); - return; - } - - if (vport->chunk_header_read < sizeof(vport->chunk_header)) { - vport->chunk_header_read += n; - if (vport->chunk_header_read == sizeof(vport->chunk_header)) { - if (vport->chunk_header.size > VD_AGENT_MAX_DATA_SIZE) { - fprintf(vport->errfile, "chunk size too large\n"); - vdagent_virtio_port_destroy(vportp); - return; - } - if (vport->chunk_header.port > VDP_LAST_PORT) { - fprintf(vport->errfile, "chunk port out of range\n"); - vdagent_virtio_port_destroy(vportp); - return; - } - } - } else { - vport->chunk_data_pos += n; - if (vport->chunk_data_pos == vport->chunk_header.size) { - vdagent_virtio_port_do_chunk(vportp); - vport->chunk_header_read = 0; - vport->chunk_data_pos = 0; - } - } -} - -static void vdagent_virtio_port_do_write(struct vdagent_virtio_port **vportp) -{ - ssize_t n; - size_t to_write; - struct vdagent_virtio_port *vport = *vportp; - - struct vdagent_virtio_port_buf* wbuf = vport->write_buf; - if (!wbuf) { - fprintf(vport->errfile, - "do_write called on a port without a write buf ?!\n"); - return; - } - - to_write = wbuf->size - wbuf->pos; - n = write(vport->fd, wbuf->buf + wbuf->pos, to_write); - if (n < 0) { - if (errno == EINTR) - return; - fprintf(vport->errfile, "writing to vdagent virtio port: %s\n", - strerror(errno)); - vdagent_virtio_port_destroy(vportp); - return; - } - - wbuf->pos += n; - if (wbuf->pos == wbuf->size) { - vport->write_buf = wbuf->next; - free(wbuf->buf); - free(wbuf); - } -} diff --git a/vdagent-virtio-port.h b/vdagent-virtio-port.h deleted file mode 100644 index db446a1..0000000 --- a/vdagent-virtio-port.h +++ /dev/null @@ -1,92 +0,0 @@ -/* vdagent-virtio-port.h virtio port communication header - - Copyright 2010 Red Hat, Inc. - - Red Hat Authors: - Hans de Goede - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef __VIRTIO_PORT_H -#define __VIRTIO_PORT_H - -#include -#include -#include -#include - -struct vdagent_virtio_port; - -/* Callbacks with this type will be called when a complete message has been - received. Sometimes the callback may want to close the port, in this - case do *not* call vdagent_virtio_port_destroy from the callback. The desire - to close the port can be indicated be returning -1 from the callback, - in other cases return 0. */ -typedef int (*vdagent_virtio_port_read_callback)( - struct vdagent_virtio_port *vport, - int port_nr, - VDAgentMessage *message_header, - uint8_t *data); - -/* Callbacks with this type will be called when the port is disconnected. - Note: - 1) vdagent_virtio_port will destroy the port in question itself after - this callback has completed! - 2) This callback is always called, even if the disconnect is initiated - by the vdagent_virtio_port user through returning -1 from a read - callback, or by explictly calling vdagent_virtio_port_destroy */ -typedef void (*vdagent_virtio_port_disconnect_callback)( - struct vdagent_virtio_port *conn); - - -/* Create a vdagent virtio port object for port portname */ -struct vdagent_virtio_port *vdagent_virtio_port_create(const char *portname, - vdagent_virtio_port_read_callback read_callback, - vdagent_virtio_port_disconnect_callback disconnect_callback, - FILE *errfile); - -/* The contents of portp will be made NULL */ -void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp); - - -/* Given a vdagent_virtio_port fill the fd_sets pointed to by readfds and - writefds for select() usage. - - Return value: value of the highest fd + 1 */ -int vdagent_virtio_port_fill_fds(struct vdagent_virtio_port *vport, - fd_set *readfds, fd_set *writefds); - -/* Handle any events flagged by select for the given vdagent_virtio_port. - Note the port may be destroyed (when disconnected) by this call - in this case the disconnect calllback will get called before the - destruction and the contents of connp will be made NULL */ -void vdagent_virtio_port_handle_fds(struct vdagent_virtio_port **vportp, - fd_set *readfds, fd_set *writefds); - - -/* Queue a message for delivery - - Returns 0 on success -1 on error (only happens when malloc fails) */ -int vdagent_virtio_port_write( - struct vdagent_virtio_port *vport, - uint32_t port_nr, - uint32_t message_type, - uint32_t message_opaque, - const uint8_t *data, - uint32_t data_size); - -void vdagent_virtio_port_flush(struct vdagent_virtio_port **vportp); - -#endif diff --git a/vdagent-x11.c b/vdagent-x11.c deleted file mode 100644 index 5068bc9..0000000 --- a/vdagent-x11.c +++ /dev/null @@ -1,1307 +0,0 @@ -/* vdagent-x11.c vdagent x11 code - - Copyright 2010 Red Hat, Inc. - - Red Hat Authors: - Hans de Goede - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* Note: Our event loop is only called when there is data to be read from the - X11 socket. If events have arrived and have already been read by libX11 from - the socket triggered by other libX11 calls from this file, the select for - read in the main loop, won't see these and our event loop won't get called! - - Thus we must make sure that all queued events have been consumed, whenever - we return to the main loop. IOW all (externally callable) functions in this - file must end with calling XPending and consuming all queued events. - - Calling XPending when-ever we return to the mainloop also ensures any - pending writes are flushed. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "vdagentd-proto.h" -#include "vdagent-x11.h" - -enum { owner_none, owner_guest, owner_client }; - -/* X11 terminology is confusing a selection request is a request from an - app to get clipboard data from us, so iow from the spice client through - the vdagent channel. We handle these one at a time and queue any which - come in while we are still handling the current one. */ -struct vdagent_x11_selection_request { - XEvent event; - uint8_t selection; - struct vdagent_x11_selection_request *next; -}; - -/* A conversion request is X11 speak for asking an other app to give its - clipboard data to us, we do these on behalf of the spice client to copy - data from the guest to the client. Like selection requests we process - these one at a time. */ -struct vdagent_x11_conversion_request { - Atom target; - uint8_t selection; - struct vdagent_x11_conversion_request *next; -}; - -struct clipboard_format_tmpl { - uint32_t type; - const char *atom_names[16]; -}; - -struct clipboard_format_info { - uint32_t type; - Atom atoms[16]; - int atom_count; -}; - -static const struct clipboard_format_tmpl clipboard_format_templates[] = { - { VD_AGENT_CLIPBOARD_UTF8_TEXT, { "UTF8_STRING", - "text/plain;charset=UTF-8", "text/plain;charset=utf-8", NULL }, }, - { VD_AGENT_CLIPBOARD_IMAGE_PNG, { "image/png", NULL }, }, - { VD_AGENT_CLIPBOARD_IMAGE_BMP, { "image/bmp", "image/x-bmp", - "image/x-MS-bmp", "image/x-win-bitmap", NULL }, }, - { VD_AGENT_CLIPBOARD_IMAGE_TIFF, { "image/tiff", NULL }, }, - { VD_AGENT_CLIPBOARD_IMAGE_JPG, { "image/jpeg", NULL }, }, -}; - -#define clipboard_format_count (sizeof(clipboard_format_templates)/sizeof(clipboard_format_templates[0])) - -struct vdagent_x11 { - struct clipboard_format_info clipboard_formats[clipboard_format_count]; - Display *display; - Atom clipboard_atom; - Atom clipboard_primary_atom; - Atom targets_atom; - Atom incr_atom; - Atom multiple_atom; - Window root_window; - Window selection_window; - struct udscs_connection *vdagentd; - FILE *errfile; - int verbose; - int fd; - int screen; - int width; - int height; - int has_xrandr; - int has_xfixes; - int xfixes_event_base; - int max_prop_size; - int expected_targets_notifies[256]; - int clipboard_owner[256]; - int clipboard_type_count[256]; - uint32_t clipboard_agent_types[256][256]; - Atom clipboard_x11_targets[256][256]; - /* Data for conversion_req which is currently being processed */ - struct vdagent_x11_conversion_request *conversion_req; - int expect_property_notify; - uint8_t *clipboard_data; - uint32_t clipboard_data_size; - uint32_t clipboard_data_space; - /* Data for selection_req which is currently being processed */ - struct vdagent_x11_selection_request *selection_req; - uint8_t *selection_req_data; - uint32_t selection_req_data_pos; - uint32_t selection_req_data_size; - Atom selection_req_atom; -}; - -static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11); -static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11, - XEvent *event, int incr); -static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11); -static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11, - XEvent *event); -static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11, - XEvent *del_event); -static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11, - Atom prop, struct vdagent_x11_selection_request *request); -static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11, - uint8_t selection, int new_owner); - -struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd, - FILE *errfile, int verbose) -{ - struct vdagent_x11 *x11; - XWindowAttributes attrib; - int i, j, major, minor; - - x11 = calloc(1, sizeof(*x11)); - if (!x11) { - fprintf(errfile, "out of memory allocating vdagent_x11 struct\n"); - return NULL; - } - - x11->vdagentd = vdagentd; - x11->errfile = errfile; - x11->verbose = verbose; - - x11->display = XOpenDisplay(NULL); - if (!x11->display) { - fprintf(x11->errfile, "could not connect to X-server\n"); - free(x11); - return NULL; - } - - x11->screen = DefaultScreen(x11->display); - x11->root_window = RootWindow(x11->display, x11->screen); - x11->fd = ConnectionNumber(x11->display); - x11->clipboard_atom = XInternAtom(x11->display, "CLIPBOARD", False); - x11->clipboard_primary_atom = XInternAtom(x11->display, "PRIMARY", False); - x11->targets_atom = XInternAtom(x11->display, "TARGETS", False); - x11->incr_atom = XInternAtom(x11->display, "INCR", False); - x11->multiple_atom = XInternAtom(x11->display, "MULTIPLE", False); - for(i = 0; i < clipboard_format_count; i++) { - x11->clipboard_formats[i].type = clipboard_format_templates[i].type; - for(j = 0; clipboard_format_templates[i].atom_names[j]; j++) { - x11->clipboard_formats[i].atoms[j] = - XInternAtom(x11->display, - clipboard_format_templates[i].atom_names[j], - False); - } - x11->clipboard_formats[i].atom_count = j; - } - - /* We should not store properties (for selections) on the root window */ - x11->selection_window = XCreateSimpleWindow(x11->display, x11->root_window, - 0, 0, 1, 1, 0, 0, 0); - if (x11->verbose) - fprintf(x11->errfile, "Selection window: %u\n", - (unsigned int)x11->selection_window); - - if (XRRQueryExtension(x11->display, &i, &i)) - x11->has_xrandr = 1; - else - fprintf(x11->errfile, "no xrandr\n"); - - if (XFixesQueryExtension(x11->display, &x11->xfixes_event_base, &i) && - XFixesQueryVersion(x11->display, &major, &minor) && major >= 1) { - x11->has_xfixes = 1; - XFixesSelectSelectionInput(x11->display, x11->root_window, - x11->clipboard_atom, - XFixesSetSelectionOwnerNotifyMask| - XFixesSelectionWindowDestroyNotifyMask| - XFixesSelectionClientCloseNotifyMask); - XFixesSelectSelectionInput(x11->display, x11->root_window, - x11->clipboard_primary_atom, - XFixesSetSelectionOwnerNotifyMask| - XFixesSelectionWindowDestroyNotifyMask| - XFixesSelectionClientCloseNotifyMask); - } else - fprintf(x11->errfile, - "no xfixes, no guest -> client copy paste support\n"); - - x11->max_prop_size = XExtendedMaxRequestSize(x11->display); - if (x11->max_prop_size) { - x11->max_prop_size -= 100; - } else { - x11->max_prop_size = XMaxRequestSize(x11->display) - 100; - } - /* Be a good X11 citizen and maximize the amount of data we send at once */ - if (x11->max_prop_size > 262144) - x11->max_prop_size = 262144; - - /* Catch resolution changes */ - XSelectInput(x11->display, x11->root_window, StructureNotifyMask); - - /* Get the current resolution */ - XGetWindowAttributes(x11->display, x11->root_window, &attrib); - x11->width = attrib.width; - x11->height = attrib.height; - vdagent_x11_send_daemon_guest_xorg_res(x11); - - /* Flush output buffers and consume any pending events */ - vdagent_x11_do_read(x11); - - return x11; -} - -void vdagent_x11_destroy(struct vdagent_x11 *x11) -{ - uint8_t sel; - - if (!x11) - return; - - for (sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; ++sel) { - vdagent_x11_set_clipboard_owner(x11, sel, owner_none); - } - - XCloseDisplay(x11->display); - free(x11); -} - -int vdagent_x11_get_fd(struct vdagent_x11 *x11) -{ - return x11->fd; -} - -static void vdagent_x11_next_selection_request(struct vdagent_x11 *x11) -{ - struct vdagent_x11_selection_request *selection_request; - selection_request = x11->selection_req; - x11->selection_req = selection_request->next; - free(selection_request); -} - -static void vdagent_x11_next_conversion_request(struct vdagent_x11 *x11) -{ - struct vdagent_x11_conversion_request *conversion_req; - conversion_req = x11->conversion_req; - x11->conversion_req = conversion_req->next; - free(conversion_req); -} - -static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11, - uint8_t selection, int new_owner) -{ - struct vdagent_x11_selection_request *prev_sel, *curr_sel, *next_sel; - struct vdagent_x11_conversion_request *prev_conv, *curr_conv, *next_conv; - int once; - - /* Clear pending requests and clipboard data */ - once = 1; - prev_sel = NULL; - next_sel = x11->selection_req; - while (next_sel) { - curr_sel = next_sel; - next_sel = curr_sel->next; - if (curr_sel->selection == selection) { - if (once) { - fprintf(x11->errfile, - "selection requests pending on clipboard ownership " - "change, clearing\n"); - once = 0; - } - vdagent_x11_send_selection_notify(x11, None, curr_sel); - if (curr_sel == x11->selection_req) { - x11->selection_req = next_sel; - free(x11->selection_req_data); - x11->selection_req_data = NULL; - x11->selection_req_data_pos = 0; - x11->selection_req_data_size = 0; - x11->selection_req_atom = None; - } else { - prev_sel->next = next_sel; - } - free(curr_sel); - } else { - prev_sel = curr_sel; - } - } - - once = 1; - prev_conv = NULL; - next_conv = x11->conversion_req; - while (next_conv) { - curr_conv = next_conv; - next_conv = curr_conv->next; - if (curr_conv->selection == selection) { - if (once) { - fprintf(x11->errfile, - "client clipboard request pending on clipboard " - "ownership change, clearing\n"); - once = 0; - } - udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, selection, - VD_AGENT_CLIPBOARD_NONE, NULL, 0); - if (curr_conv == x11->conversion_req) { - x11->conversion_req = next_conv; - x11->clipboard_data_size = 0; - x11->expect_property_notify = 0; - } else { - prev_conv->next = next_conv; - } - free(curr_conv); - } else { - prev_conv = curr_conv; - } - } - - if (new_owner == owner_none) { - /* When going from owner_guest to owner_none we need to send a - clipboard release message to the client */ - if (x11->clipboard_owner[selection] == owner_guest) { - udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_RELEASE, selection, - 0, NULL, 0); - } - x11->clipboard_type_count[selection] = 0; - } - x11->clipboard_owner[selection] = new_owner; -} - -static int vdagent_x11_get_clipboard_atom(struct vdagent_x11 *x11, uint8_t selection, Atom* clipboard) -{ - if (selection == VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) { - *clipboard = x11->clipboard_atom; - } else if (selection == VD_AGENT_CLIPBOARD_SELECTION_PRIMARY) { - *clipboard = x11->clipboard_primary_atom; - } else { - fprintf(x11->errfile, "selection_get_grab: unknown selection\n"); - return -1; - } - - return 0; -} - -static int vdagent_x11_get_clipboard_selection(struct vdagent_x11 *x11, - XEvent *event, uint8_t *selection) -{ - Atom atom; - - if (event->type == x11->xfixes_event_base) { - XFixesSelectionNotifyEvent *xfev = (XFixesSelectionNotifyEvent *)event; - atom = xfev->selection; - } else if (event->type == SelectionNotify) { - atom = event->xselection.selection; - } else if (event->type == SelectionRequest) { - atom = event->xselectionrequest.selection; - } else { - fprintf(x11->errfile, "get_clipboard_selection: unknown event type\n"); - return -1; - } - - if (atom == x11->clipboard_atom) { - *selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD; - } else if (atom == x11->clipboard_primary_atom) { - *selection = VD_AGENT_CLIPBOARD_SELECTION_PRIMARY; - } else { - fprintf(x11->errfile, "get_clipboard_selection: unknown selection\n"); - return -1; - } - - return 0; -} - -static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event) -{ - int handled = 0; - uint8_t selection; - - if (event.type == x11->xfixes_event_base) { - union { - XEvent ev; - XFixesSelectionNotifyEvent xfev; - } ev; - - if (vdagent_x11_get_clipboard_selection(x11, &event, &selection)) { - return; - } - - ev.ev = event; - switch (ev.xfev.subtype) { - case XFixesSetSelectionOwnerNotify: - break; - /* Treat ... as a SelectionOwnerNotify None */ - case XFixesSelectionWindowDestroyNotify: - case XFixesSelectionClientCloseNotify: - ev.xfev.owner = None; - break; - default: - if (x11->verbose) - fprintf(x11->errfile, - "unexpected xfix event subtype %d window %d\n", - (int)ev.xfev.subtype, (int)event.xany.window); - return; - } - - if (x11->verbose) - fprintf(x11->errfile, "New selection owner: %u\n", - (unsigned int)ev.xfev.owner); - - /* Ignore becoming the owner ourselves */ - if (ev.xfev.owner == x11->selection_window) - return; - - /* If the clipboard owner is changed we no longer own it */ - vdagent_x11_set_clipboard_owner(x11, selection, owner_none); - - if (ev.xfev.owner == None) - return; - - /* Request the supported targets from the new owner */ - XConvertSelection(x11->display, ev.xfev.selection, x11->targets_atom, - x11->targets_atom, x11->selection_window, - CurrentTime); - x11->expected_targets_notifies[selection]++; - return; - } - - switch (event.type) { - case ConfigureNotify: - if (event.xconfigure.window != x11->root_window) - break; - - handled = 1; - - if (event.xconfigure.width == x11->width && - event.xconfigure.height == x11->height) - break; - - x11->width = event.xconfigure.width; - x11->height = event.xconfigure.height; - - vdagent_x11_send_daemon_guest_xorg_res(x11); - break; - case SelectionNotify: - if (event.xselection.target == x11->targets_atom) - vdagent_x11_handle_targets_notify(x11, &event); - else - vdagent_x11_handle_selection_notify(x11, &event, 0); - - handled = 1; - break; - case PropertyNotify: - if (x11->expect_property_notify && - event.xproperty.state == PropertyNewValue) { - vdagent_x11_handle_selection_notify(x11, &event, 1); - } - if (x11->selection_req_data && - event.xproperty.state == PropertyDelete) { - vdagent_x11_handle_property_delete_notify(x11, &event); - } - /* Always mark as handled, since we cannot unselect input for property - notifications once we are done with handling the incr transfer. */ - handled = 1; - break; - case SelectionClear: - /* Do nothing the clipboard ownership will get updated through - the XFixesSetSelectionOwnerNotify event */ - handled = 1; - break; - case SelectionRequest: { - struct vdagent_x11_selection_request *req, *new_req; - - if (vdagent_x11_get_clipboard_selection(x11, &event, &selection)) { - return; - } - - new_req = malloc(sizeof(*new_req)); - if (!new_req) { - fprintf(x11->errfile, - "out of memory on SelectionRequest, ignoring.\n"); - break; - } - - handled = 1; - - new_req->event = event; - new_req->selection = selection; - new_req->next = NULL; - - if (!x11->selection_req) { - x11->selection_req = new_req; - vdagent_x11_handle_selection_request(x11); - break; - } - - /* maybe we should limit the selection_request stack depth ? */ - req = x11->selection_req; - while (req->next) - req = req->next; - - req->next = new_req; - break; - } - } - if (!handled && x11->verbose) - fprintf(x11->errfile, "unhandled x11 event, type %d, window %d\n", - (int)event.type, (int)event.xany.window); -} - -void vdagent_x11_do_read(struct vdagent_x11 *x11) -{ - XEvent event; - - while (XPending(x11->display)) { - XNextEvent(x11->display, &event); - vdagent_x11_handle_event(x11, event); - } -} - -static void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11) -{ - struct vdagentd_guest_xorg_resolution res; - - res.width = x11->width; - res.height = x11->height; - - udscs_write(x11->vdagentd, VDAGENTD_GUEST_XORG_RESOLUTION, 0, 0, - (uint8_t *)&res, sizeof(res)); -} - -static const char *vdagent_x11_get_atom_name(struct vdagent_x11 *x11, Atom a) -{ - if (a == None) - return "None"; - - return XGetAtomName(x11->display, a); -} - -static int vdagent_x11_get_selection(struct vdagent_x11 *x11, XEvent *event, - Atom type, Atom prop, int format, - unsigned char **data_ret, int incr) -{ - Bool del = incr ? True: False; - Atom type_ret; - int format_ret, ret_val = -1; - unsigned long len, remain; - unsigned char *data = NULL; - - *data_ret = NULL; - - if (!incr) { - if (event->xselection.property == None) { - if (x11->verbose) - fprintf(x11->errfile, - "XConvertSelection refused by clipboard owner\n"); - goto exit; - } - - if (event->xselection.requestor != x11->selection_window || - event->xselection.property != prop) { - fprintf(x11->errfile, "SelectionNotify parameters mismatch\n"); - goto exit; - } - } - - if (XGetWindowProperty(x11->display, x11->selection_window, prop, 0, - LONG_MAX, del, type, &type_ret, &format_ret, &len, - &remain, &data) != Success) { - fprintf(x11->errfile, "XGetWindowProperty failed\n"); - goto exit; - } - - if (!incr && prop != x11->targets_atom) { - if (type_ret == x11->incr_atom) { - int prop_min_size = *(uint32_t*)data; - - if (x11->expect_property_notify) { - fprintf(x11->errfile, - "received an incr SelectionNotify while " - "still reading another incr property\n"); - goto exit; - } - - if (x11->clipboard_data_space < prop_min_size) { - free(x11->clipboard_data); - x11->clipboard_data = malloc(prop_min_size); - if (!x11->clipboard_data) { - fprintf(x11->errfile, - "out of memory allocating clipboard buffer\n"); - x11->clipboard_data_space = 0; - goto exit; - } - x11->clipboard_data_space = prop_min_size; - } - x11->expect_property_notify = 1; - XSelectInput(x11->display, x11->selection_window, - PropertyChangeMask); - XDeleteProperty(x11->display, x11->selection_window, prop); - XFree(data); - return 0; /* Wait for more data */ - } - XDeleteProperty(x11->display, x11->selection_window, prop); - } - - if (type_ret != type) { - fprintf(x11->errfile, "expected property type: %s, got: %s\n", - vdagent_x11_get_atom_name(x11, type), - vdagent_x11_get_atom_name(x11, type_ret)); - goto exit; - } - - if (format_ret != format) { - fprintf(x11->errfile, "expected %d bit format, got %d bits\n", format, - format_ret); - goto exit; - } - - /* Convert len to bytes */ - switch(format) { - case 8: - break; - case 16: - len *= sizeof(short); - break; - case 32: - len *= sizeof(long); - break; - } - - if (incr) { - if (len) { - if (x11->clipboard_data_size + len > x11->clipboard_data_space) { - void *old_clipboard_data = x11->clipboard_data; - - x11->clipboard_data_space = x11->clipboard_data_size + len; - x11->clipboard_data = realloc(x11->clipboard_data, - x11->clipboard_data_space); - if (!x11->clipboard_data) { - fprintf(x11->errfile, - "out of memory allocating clipboard buffer\n"); - x11->clipboard_data_space = 0; - free(old_clipboard_data); - goto exit; - } - } - memcpy(x11->clipboard_data + x11->clipboard_data_size, data, len); - x11->clipboard_data_size += len; - if (x11->verbose) - fprintf(x11->errfile, "Appended %ld bytes to buffer\n", len); - XFree(data); - return 0; /* Wait for more data */ - } - len = x11->clipboard_data_size; - *data_ret = x11->clipboard_data; - } else - *data_ret = data; - - if (len > 0) { - ret_val = len; - } else { - fprintf(x11->errfile, "property contains no data (zero length)\n"); - *data_ret = NULL; - } - -exit: - if ((incr || ret_val == -1) && data) - XFree(data); - - if (incr) { - x11->clipboard_data_size = 0; - x11->expect_property_notify = 0; - } - - return ret_val; -} - -static void vdagent_x11_get_selection_free(struct vdagent_x11 *x11, - unsigned char *data, int incr) -{ - if (incr) { - /* If the clipboard has grown large return the memory to the system */ - if (x11->clipboard_data_space > 512 * 1024) { - free(x11->clipboard_data); - x11->clipboard_data = NULL; - x11->clipboard_data_space = 0; - } - } else if (data) - XFree(data); -} - -static uint32_t vdagent_x11_target_to_type(struct vdagent_x11 *x11, - Atom target) -{ - int i, j; - - for (i = 0; i < clipboard_format_count; i++) { - for (j = 0; j < x11->clipboard_formats[i].atom_count; i++) { - if (x11->clipboard_formats[i].atoms[j] == target) { - return x11->clipboard_formats[i].type; - } - } - } - - fprintf(x11->errfile, "unexpected selection type %s\n", - vdagent_x11_get_atom_name(x11, target)); - return VD_AGENT_CLIPBOARD_NONE; -} - -static Atom vdagent_x11_type_to_target(struct vdagent_x11 *x11, - uint8_t selection, uint32_t type) -{ - int i; - - for (i = 0; i < x11->clipboard_type_count[selection]; i++) { - if (x11->clipboard_agent_types[selection][i] == type) { - return x11->clipboard_x11_targets[selection][i]; - } - } - fprintf(x11->errfile, "client requested unavailable type %u\n", type); - return None; -} - -static void vdagent_x11_handle_conversion_request(struct vdagent_x11 *x11) -{ - Atom clip; - - if (!x11->conversion_req) { - return; - } - - vdagent_x11_get_clipboard_atom(x11, x11->conversion_req->selection, &clip); - XConvertSelection(x11->display, clip, x11->conversion_req->target, - clip, x11->selection_window, CurrentTime); -} - -static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11, - XEvent *event, int incr) -{ - int len = 0; - unsigned char *data = NULL; - uint32_t type; - uint8_t selection; - Atom clip; - - if (!x11->conversion_req) { - fprintf(x11->errfile, "SelectionNotify received without a target\n"); - return; - } - vdagent_x11_get_clipboard_atom(x11, x11->conversion_req->selection, &clip); - - if (incr) { - if (event->xproperty.atom != clip || - event->xproperty.window != x11->selection_window) { - return; - } - } else { - if (vdagent_x11_get_clipboard_selection(x11, event, &selection)) { - len = -1; - } else if (selection != x11->conversion_req->selection) { - fprintf(x11->errfile, "Requested data for selection %d, got %d\n", - (int)x11->conversion_req->selection, (int)selection); - len = -1; - } - if (event->xselection.target != x11->conversion_req->target && - event->xselection.target != x11->incr_atom) { - fprintf(x11->errfile, "Requested %s target got %s\n", - vdagent_x11_get_atom_name(x11, x11->conversion_req->target), - vdagent_x11_get_atom_name(x11, event->xselection.target)); - len = -1; - } - } - - selection = x11->conversion_req->selection; - type = vdagent_x11_target_to_type(x11, x11->conversion_req->target); - if (len == 0) { /* No errors so far */ - len = vdagent_x11_get_selection(x11, event, - x11->conversion_req->target, - clip, 8, &data, incr); - if (len == 0) { /* waiting for more data? */ - return; - } - } - if (len == -1) { - type = VD_AGENT_CLIPBOARD_NONE; - len = 0; - } - - udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, selection, type, - data, len); - vdagent_x11_get_selection_free(x11, data, incr); - - vdagent_x11_next_conversion_request(x11); - vdagent_x11_handle_conversion_request(x11); -} - -static Atom atom_lists_overlap(Atom *atoms1, Atom *atoms2, int l1, int l2) -{ - int i, j; - - for (i = 0; i < l1; i++) - for (j = 0; j < l2; j++) - if (atoms1[i] == atoms2[j]) - return atoms1[i]; - - return 0; -} - -static void vdagent_x11_print_targets(struct vdagent_x11 *x11, - const char *action, Atom *atoms, int c) -{ - int i; - - if (!x11->verbose) - return; - - fprintf(x11->errfile, "%s %d targets:\n", action, c); - for (i = 0; i < c; i++) - fprintf(x11->errfile, "%s\n", - vdagent_x11_get_atom_name(x11, atoms[i])); -} - -static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11, - XEvent *event) -{ - int i, len; - Atom atom, *atoms = NULL; - uint8_t selection; - int *type_count; - - if (vdagent_x11_get_clipboard_selection(x11, event, &selection)) { - return; - } - - if (!x11->expected_targets_notifies[selection]) { - fprintf(x11->errfile, "unexpected selection notify TARGETS\n"); - return; - } - - x11->expected_targets_notifies[selection]--; - - /* If we have more targets_notifies pending, ignore this one, we - are only interested in the targets list of the current owner - (which is the last one we've requested a targets list from) */ - if (x11->expected_targets_notifies[selection]) { - return; - } - - len = vdagent_x11_get_selection(x11, event, XA_ATOM, x11->targets_atom, 32, - (unsigned char **)&atoms, 0); - if (len == 0 || len == -1) /* waiting for more data or error? */ - return; - - /* bytes -> atoms */ - len /= sizeof(Atom); - vdagent_x11_print_targets(x11, "received", atoms, len); - - type_count = &x11->clipboard_type_count[selection]; - *type_count = 0; - for (i = 0; i < clipboard_format_count; i++) { - atom = atom_lists_overlap(x11->clipboard_formats[i].atoms, atoms, - x11->clipboard_formats[i].atom_count, len); - if (atom) { - x11->clipboard_agent_types[selection][*type_count] = - x11->clipboard_formats[i].type; - x11->clipboard_x11_targets[selection][*type_count] = atom; - (*type_count)++; - if (*type_count == - sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t)) { - fprintf(x11->errfile, - "handle_targets_notify: too many types\n"); - break; - } - } - } - - if (*type_count) { - udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_GRAB, selection, 0, - (uint8_t *)x11->clipboard_agent_types[selection], - *type_count * sizeof(uint32_t)); - vdagent_x11_set_clipboard_owner(x11, selection, owner_guest); - } - - vdagent_x11_get_selection_free(x11, (unsigned char *)atoms, 0); -} - -static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11, - Atom prop, struct vdagent_x11_selection_request *request) -{ - XEvent res, *event; - - if (request) { - event = &request->event; - } else { - event = &x11->selection_req->event; - } - - res.xselection.property = prop; - res.xselection.type = SelectionNotify; - res.xselection.display = event->xselectionrequest.display; - res.xselection.requestor = event->xselectionrequest.requestor; - res.xselection.selection = event->xselectionrequest.selection; - res.xselection.target = event->xselectionrequest.target; - res.xselection.time = event->xselectionrequest.time; - XSendEvent(x11->display, event->xselectionrequest.requestor, 0, 0, &res); - - if (!request) { - vdagent_x11_next_selection_request(x11); - vdagent_x11_handle_selection_request(x11); - } -} - -static void vdagent_x11_send_targets(struct vdagent_x11 *x11, - uint8_t selection, XEvent *event) -{ - Atom prop, targets[256] = { x11->targets_atom, }; - int i, j, k, target_count = 1; - - for (i = 0; i < x11->clipboard_type_count[selection]; i++) { - for (j = 0; j < clipboard_format_count; j++) { - if (x11->clipboard_formats[j].type != - x11->clipboard_agent_types[selection][i]) - continue; - - for (k = 0; k < x11->clipboard_formats[j].atom_count; k++) { - targets[target_count] = x11->clipboard_formats[j].atoms[k]; - target_count++; - if (target_count == sizeof(targets)/sizeof(Atom)) { - fprintf(x11->errfile, "send_targets: too many targets\n"); - goto exit_loop; - } - } - } - } -exit_loop: - - prop = event->xselectionrequest.property; - if (prop == None) - prop = event->xselectionrequest.target; - - XChangeProperty(x11->display, event->xselectionrequest.requestor, prop, - XA_ATOM, 32, PropModeReplace, (unsigned char *)&targets, - target_count); - vdagent_x11_print_targets(x11, "sent", targets, target_count); - vdagent_x11_send_selection_notify(x11, prop, NULL); -} - -static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11) -{ - XEvent *event; - uint32_t type = VD_AGENT_CLIPBOARD_NONE; - uint8_t selection; - - if (!x11->selection_req) - return; - - event = &x11->selection_req->event; - selection = x11->selection_req->selection; - - if (x11->clipboard_owner[selection] != owner_client) { - fprintf(x11->errfile, - "received selection request event for target %s, " - "while not owning client clipboard\n", - vdagent_x11_get_atom_name(x11, event->xselectionrequest.target)); - vdagent_x11_send_selection_notify(x11, None, NULL); - return; - } - - if (event->xselectionrequest.target == x11->multiple_atom) { - fprintf(x11->errfile, "multiple target not supported\n"); - vdagent_x11_send_selection_notify(x11, None, NULL); - return; - } - - if (event->xselectionrequest.target == x11->targets_atom) { - vdagent_x11_send_targets(x11, selection, event); - return; - } - - type = vdagent_x11_target_to_type(x11, event->xselectionrequest.target); - if (type == VD_AGENT_CLIPBOARD_NONE) { - vdagent_x11_send_selection_notify(x11, None, NULL); - return; - } - - udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_REQUEST, selection, type, - NULL, 0); -} - -static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11, - XEvent *del_event) -{ - XEvent *sel_event; - int len; - - assert(x11->selection_req); - sel_event = &x11->selection_req->event; - if (del_event->xproperty.window != sel_event->xselectionrequest.requestor - || del_event->xproperty.atom != x11->selection_req_atom) { - return; - } - - len = x11->selection_req_data_size - x11->selection_req_data_pos; - if (len > x11->max_prop_size) { - len = x11->max_prop_size; - } - - if (x11->verbose) { - if (len) { - fprintf(x11->errfile, "Sending %d-%d/%d bytes of clipboard data\n", - x11->selection_req_data_pos, - x11->selection_req_data_pos + len - 1, - x11->selection_req_data_size); - } else { - fprintf(x11->errfile, "Ending incr send of clipboard data\n"); - } - } - XChangeProperty(x11->display, sel_event->xselectionrequest.requestor, - x11->selection_req_atom, - sel_event->xselectionrequest.target, 8, PropModeReplace, - x11->selection_req_data + x11->selection_req_data_pos, - len); - x11->selection_req_data_pos += len; - - /* Note we must explictly send a 0 sized XChangeProperty to signal the - incr transfer is done. Hence we do not check if we've send all data - but instead check we've send the final 0 sized XChangeProperty. */ - if (len == 0) { - free(x11->selection_req_data); - x11->selection_req_data = NULL; - x11->selection_req_data_pos = 0; - x11->selection_req_data_size = 0; - x11->selection_req_atom = None; - vdagent_x11_next_selection_request(x11); - vdagent_x11_handle_selection_request(x11); - } -} - -void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11, - VDAgentMonitorsConfig *mon_config) -{ - int i, num_sizes = 0; - int best = -1; - unsigned int closest_diff = -1; - XRRScreenSize* sizes; - XRRScreenConfiguration* config; - Rotation rotation; - - if (!x11->has_xrandr) - return; - - if (mon_config->num_of_monitors != 1) { - fprintf(x11->errfile, - "Only 1 monitor supported, ignoring additional monitors\n"); - } - - sizes = XRRSizes(x11->display, x11->screen, &num_sizes); - if (!sizes || !num_sizes) { - fprintf(x11->errfile, "XRRSizes failed\n"); - return; - } - - /* Find the closest size which will fit within the monitor */ - for (i = 0; i < num_sizes; i++) { - if (sizes[i].width > mon_config->monitors[0].width || - sizes[i].height > mon_config->monitors[0].height) - continue; /* Too large for the monitor */ - - unsigned int wdiff = mon_config->monitors[0].width - sizes[i].width; - unsigned int hdiff = mon_config->monitors[0].height - sizes[i].height; - unsigned int diff = wdiff * wdiff + hdiff * hdiff; - if (diff < closest_diff) { - closest_diff = diff; - best = i; - } - } - - if (best == -1) { - fprintf(x11->errfile, "no suitable resolution found for monitor\n"); - return; - } - - config = XRRGetScreenInfo(x11->display, x11->root_window); - if(!config) { - fprintf(x11->errfile, "get screen info failed\n"); - return; - } - XRRConfigCurrentConfiguration(config, &rotation); - XRRSetScreenConfig(x11->display, config, x11->root_window, best, - rotation, CurrentTime); - XRRFreeScreenConfigInfo(config); - x11->width = sizes[best].width; - x11->height = sizes[best].height; - vdagent_x11_send_daemon_guest_xorg_res(x11); - - /* Flush output buffers and consume any pending events */ - vdagent_x11_do_read(x11); -} - -void vdagent_x11_clipboard_request(struct vdagent_x11 *x11, - uint8_t selection, uint32_t type) -{ - Atom target, clip; - struct vdagent_x11_conversion_request *req, *new_req; - - /* We don't use clip here, but we call get_clipboard_atom to verify - selection is valid */ - if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) { - goto none; - } - - if (x11->clipboard_owner[selection] != owner_guest) { - fprintf(x11->errfile, - "received clipboard req while not owning guest clipboard\n"); - goto none; - } - - target = vdagent_x11_type_to_target(x11, selection, type); - if (target == None) { - goto none; - } - - new_req = malloc(sizeof(*new_req)); - if (!new_req) { - fprintf(x11->errfile, - "out of memory on client clipboard request, ignoring.\n"); - return; - } - - new_req->target = target; - new_req->selection = selection; - new_req->next = NULL; - - if (!x11->conversion_req) { - x11->conversion_req = new_req; - vdagent_x11_handle_conversion_request(x11); - /* Flush output buffers and consume any pending events */ - vdagent_x11_do_read(x11); - return; - } - - /* maybe we should limit the conversion_request stack depth ? */ - req = x11->conversion_req; - while (req->next) - req = req->next; - - req->next = new_req; - return; - -none: - udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, - selection, VD_AGENT_CLIPBOARD_NONE, NULL, 0); -} - -void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint8_t selection, - uint32_t *types, uint32_t type_count) -{ - Atom clip; - - if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) { - return; - } - - if (type_count > sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t)) { - fprintf(x11->errfile, "x11_clipboard_grab: too many types\n"); - type_count = sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t); - } - - memcpy(x11->clipboard_agent_types[selection], types, - type_count * sizeof(uint32_t)); - x11->clipboard_type_count[selection] = type_count; - - XSetSelectionOwner(x11->display, clip, - x11->selection_window, CurrentTime); - vdagent_x11_set_clipboard_owner(x11, selection, owner_client); - - /* Flush output buffers and consume any pending events */ - vdagent_x11_do_read(x11); -} - -void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint8_t selection, - uint32_t type, uint8_t *data, uint32_t size) -{ - Atom prop; - XEvent *event; - uint32_t type_from_event; - - if (x11->selection_req_data) { - if (type || size) { - fprintf(x11->errfile, "received clipboard data while still sending" - " data from previous request, ignoring\n"); - } - free(data); - return; - } - - if (!x11->selection_req) { - if (type || size) { - fprintf(x11->errfile, "received clipboard data without an " - "outstanding selection request, ignoring\n"); - } - free(data); - return; - } - - event = &x11->selection_req->event; - type_from_event = vdagent_x11_target_to_type(x11, - event->xselectionrequest.target); - if (type_from_event != type || - selection != x11->selection_req->selection) { - if (type_from_event != type) { - fprintf(x11->errfile, "expecting type %u clipboard data got %u\n", - type_from_event, type); - } else { - fprintf(x11->errfile, "expecting data for selection %d got %d\n", - (int)x11->selection_req->selection, (int)selection); - } - vdagent_x11_send_selection_notify(x11, None, NULL); - free(data); - - /* Flush output buffers and consume any pending events */ - vdagent_x11_do_read(x11); - return; - } - - prop = event->xselectionrequest.property; - if (prop == None) - prop = event->xselectionrequest.target; - - if (size > x11->max_prop_size) { - unsigned long len = size; - if (x11->verbose) - fprintf(x11->errfile, "Starting incr send of clipboard data\n"); - x11->selection_req_data = data; - x11->selection_req_data_pos = 0; - x11->selection_req_data_size = size; - x11->selection_req_atom = prop; - XSelectInput(x11->display, event->xselectionrequest.requestor, - PropertyChangeMask); - XChangeProperty(x11->display, event->xselectionrequest.requestor, prop, - x11->incr_atom, 32, PropModeReplace, - (unsigned char*)&len, 1); - vdagent_x11_send_selection_notify(x11, prop, x11->selection_req); - } else { - XChangeProperty(x11->display, event->xselectionrequest.requestor, prop, - event->xselectionrequest.target, 8, PropModeReplace, - data, size); - vdagent_x11_send_selection_notify(x11, prop, NULL); - free(data); - } - - /* Flush output buffers and consume any pending events */ - vdagent_x11_do_read(x11); -} - -void vdagent_x11_clipboard_release(struct vdagent_x11 *x11, uint8_t selection) -{ - XEvent event; - Atom clip; - - if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) { - return; - } - - if (x11->clipboard_owner[selection] != owner_client) { - fprintf(x11->errfile, - "received clipboard release while not owning client clipboard\n"); - return; - } - - XSetSelectionOwner(x11->display, clip, None, CurrentTime); - /* Make sure we process the XFixesSetSelectionOwnerNotify event caused - by this, so we don't end up changing the clipboard owner to none, after - it has already been re-owned because this event is still pending. */ - XSync(x11->display, False); - while (XCheckTypedEvent(x11->display, x11->xfixes_event_base, - &event)) - vdagent_x11_handle_event(x11, event); - - /* Note no need to do a set_clipboard_owner(owner_none) here, as that is - already done by processing the XFixesSetSelectionOwnerNotify event. */ - - /* Flush output buffers and consume any pending events */ - vdagent_x11_do_read(x11); -} diff --git a/vdagent-x11.h b/vdagent-x11.h deleted file mode 100644 index 800cbd0..0000000 --- a/vdagent-x11.h +++ /dev/null @@ -1,48 +0,0 @@ -/* vdagent-x11.h vdagent x11 code header file - - Copyright 2010 Red Hat, Inc. - - Red Hat Authors: - Hans de Goede - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef __VDAGENT_H -#define __VDAGENT_H - -#include -#include -#include "udscs.h" - -struct vdagent_x11; - -struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd, - FILE *errfile, int verbose); -void vdagent_x11_destroy(struct vdagent_x11 *x11); - -int vdagent_x11_get_fd(struct vdagent_x11 *x11); -void vdagent_x11_do_read(struct vdagent_x11 *x11); - -void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11, - VDAgentMonitorsConfig *mon_config); -void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint8_t selection, - uint32_t *types, uint32_t type_count); -void vdagent_x11_clipboard_request(struct vdagent_x11 *x11, - uint8_t selection, uint32_t type); -void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint8_t selection, - uint32_t type, uint8_t *data, uint32_t size); -void vdagent_x11_clipboard_release(struct vdagent_x11 *x11, uint8_t selection); - -#endif diff --git a/vdagent.c b/vdagent.c deleted file mode 100644 index 2a72adb..0000000 --- a/vdagent.c +++ /dev/null @@ -1,220 +0,0 @@ -/* vdagent.c xorg-client to vdagentd (daemon). - - Copyright 2010 Red Hat, Inc. - - Red Hat Authors: - Hans de Goede - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udscs.h" -#include "vdagentd-proto.h" -#include "vdagentd-proto-strings.h" -#include "vdagent-x11.h" - -static int verbose = 0; -static struct vdagent_x11 *x11 = NULL; -static struct udscs_connection *client = NULL; -static FILE *logfile = NULL; -static int quit = 0; - -void daemon_read_complete(struct udscs_connection **connp, - struct udscs_message_header *header, uint8_t *data) -{ - switch (header->type) { - case VDAGENTD_MONITORS_CONFIG: - vdagent_x11_set_monitor_config(x11, (VDAgentMonitorsConfig *)data); - free(data); - break; - case VDAGENTD_CLIPBOARD_REQUEST: - vdagent_x11_clipboard_request(x11, header->arg1, header->arg2); - free(data); - break; - case VDAGENTD_CLIPBOARD_GRAB: - vdagent_x11_clipboard_grab(x11, header->arg1, (uint32_t *)data, - header->size / sizeof(uint32_t)); - free(data); - break; - case VDAGENTD_CLIPBOARD_DATA: - vdagent_x11_clipboard_data(x11, header->arg1, header->arg2, - data, header->size); - /* vdagent_x11_clipboard_data takes ownership of the data (or frees - it immediately) */ - break; - case VDAGENTD_CLIPBOARD_RELEASE: - vdagent_x11_clipboard_release(x11, header->arg1); - free(data); - break; - default: - if (verbose) - fprintf(logfile, "Unknown message from vdagentd type: %d\n", - header->type); - free(data); - } -} - -static void usage(FILE *fp) -{ - fprintf(fp, - "vdagent -- spice agent xorg client\n" - "options:\n" - " -h print this text\n" - " -d log debug messages\n" - " -x don't daemonize (and log to logfile)\n"); -} - -static void quit_handler(int sig) -{ - quit = 1; -} - -void daemonize(void) -{ - int x, retval = 0; - - /* detach from terminal */ - switch (fork()) { - case 0: - close(0); close(1); close(2); - setsid(); - x = open("/dev/null", O_RDWR); dup(x); dup(x); - break; - case -1: - fprintf(logfile, "fork: %s\n", strerror(errno)); - retval = 1; - default: - udscs_destroy_connection(&client); - if (logfile != stderr) - fclose(logfile); - exit(retval); - } -} - -int main(int argc, char *argv[]) -{ - fd_set readfds, writefds; - int c, n, nfds, x11_fd, retval = 0; - int do_daemonize = 1; - char *home, filename[1024]; - struct sigaction act; - - for (;;) { - if (-1 == (c = getopt(argc, argv, "-dxh"))) - break; - switch (c) { - case 'd': - verbose++; - break; - case 'x': - do_daemonize = 0; - break; - case 'h': - usage(stdout); - return 0; - default: - usage(stderr); - return 1; - } - } - - memset(&act, 0, sizeof(act)); - act.sa_flags = SA_RESTART; - act.sa_handler = quit_handler; - sigaction(SIGINT, &act, NULL); - sigaction(SIGHUP, &act, NULL); - sigaction(SIGTERM, &act, NULL); - sigaction(SIGQUIT, &act, NULL); - - logfile = stderr; - home = getenv("HOME"); - if (home) { - snprintf(filename, sizeof(filename), "%s/.spice-vdagent", home); - n = mkdir(filename, 0755); - snprintf(filename, sizeof(filename), "%s/.spice-vdagent/log", home); - if (do_daemonize) { - logfile = fopen(filename, "w"); - if (!logfile) { - fprintf(stderr, "Error opening %s: %s\n", filename, - strerror(errno)); - logfile = stderr; - } - } - } else { - fprintf(stderr, "Could not get home directory, logging to stderr\n"); - } - - client = udscs_connect(VDAGENTD_SOCKET, daemon_read_complete, NULL, - vdagentd_messages, VDAGENTD_NO_MESSAGES, - verbose? logfile:NULL, logfile); - if (!client) { - if (logfile != stderr) - fclose(logfile); - return 1; - } - - if (do_daemonize) - daemonize(); - - x11 = vdagent_x11_create(client, logfile, verbose); - if (!x11) { - udscs_destroy_connection(&client); - if (logfile != stderr) - fclose(logfile); - return 1; - } - - while (client && !quit) { - FD_ZERO(&readfds); - FD_ZERO(&writefds); - - nfds = udscs_client_fill_fds(client, &readfds, &writefds); - x11_fd = vdagent_x11_get_fd(x11); - FD_SET(x11_fd, &readfds); - if (x11_fd >= nfds) - nfds = x11_fd + 1; - - n = select(nfds, &readfds, &writefds, NULL, NULL); - if (n == -1) { - if (errno == EINTR) - continue; - fprintf(logfile, "Fatal error select: %s\n", strerror(errno)); - retval = 1; - break; - } - - if (FD_ISSET(x11_fd, &readfds)) - vdagent_x11_do_read(x11); - udscs_client_handle_fds(&client, &readfds, &writefds); - fflush(logfile); - } - - vdagent_x11_destroy(x11); - udscs_destroy_connection(&client); - if (logfile != stderr) - fclose(logfile); - - return retval; -} diff --git a/vdagentd-proto-strings.h b/vdagentd-proto-strings.h deleted file mode 100644 index 02adf01..0000000 --- a/vdagentd-proto-strings.h +++ /dev/null @@ -1,34 +0,0 @@ -/* vdagentd-proto-strings.h header file - - Copyright 2010 Red Hat, Inc. - - Red Hat Authors: - Hans de Goede - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef __VDAGENTD_PROTO_STRINGS_H -#define __VDAGENTD_PROTO_STRINGS_H - -static const char * const vdagentd_messages[] = { - "guest xorg resolution", - "monitors config", - "clipboard grab", - "clipboard request", - "clipboard data", - "clipboard release", -}; - -#endif diff --git a/vdagentd-proto.h b/vdagentd-proto.h deleted file mode 100644 index e570d2b..0000000 --- a/vdagentd-proto.h +++ /dev/null @@ -1,44 +0,0 @@ -/* vdagentd-proto.h header file for the protocol over the unix domain socket - between the vdagent process / xorg-client and the vdagentd (daemon). - - Copyright 2010 Red Hat, Inc. - - Red Hat Authors: - Hans de Goede - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef __VDAGENTD_PROTO_H -#define __VDAGENTD_PROTO_H - -#define VDAGENTD_SOCKET "/var/run/spice-vdagentd/spice-vdagent-sock" - -enum { - VDAGENTD_GUEST_XORG_RESOLUTION, /* client -> daemon */ - VDAGENTD_MONITORS_CONFIG, /* daemon -> client, VDAgentMonitorsConfig - followed by num_monitors VDAgentMonConfig-s */ - VDAGENTD_CLIPBOARD_GRAB, /* arg1: sel, data: array of supported types */ - VDAGENTD_CLIPBOARD_REQUEST, /* arg1: selection, arg 2 = type */ - VDAGENTD_CLIPBOARD_DATA, /* arg1: sel, arg 2: type, data: data */ - VDAGENTD_CLIPBOARD_RELEASE, /* arg1: selection */ - VDAGENTD_NO_MESSAGES /* Must always be last */ -}; - -struct vdagentd_guest_xorg_resolution { - int width; - int height; -}; - -#endif diff --git a/vdagentd-uinput.c b/vdagentd-uinput.c deleted file mode 100644 index 54cccb0..0000000 --- a/vdagentd-uinput.c +++ /dev/null @@ -1,212 +0,0 @@ -/* vdagentd-uinput.c vdagentd uinput handling code - - Copyright 2010 Red Hat, Inc. - - Red Hat Authors: - Hans de Goede - Gerd Hoffmann - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "vdagentd-uinput.h" - -struct vdagentd_uinput { - const char *devname; - int fd; - int width; - int height; - int verbose; - FILE *errfile; - VDAgentMouseState last; -}; - -struct vdagentd_uinput *vdagentd_uinput_create(const char *devname, - int width, int height, FILE *errfile, int verbose) -{ - struct vdagentd_uinput *uinput; - - uinput = calloc(1, sizeof(*uinput)); - if (!uinput) - return NULL; - - uinput->devname = devname; - uinput->fd = -1; /* Gets opened by vdagentd_uinput_update_size() */ - uinput->verbose = verbose; - uinput->errfile = errfile; - - vdagentd_uinput_update_size(&uinput, width, height); - - return uinput; -} - -void vdagentd_uinput_destroy(struct vdagentd_uinput **uinputp) -{ - struct vdagentd_uinput *uinput = *uinputp; - - if (!uinput) - return; - - if (uinput->fd != -1) - close(uinput->fd); - free(uinput); - *uinputp = NULL; -} - -void vdagentd_uinput_update_size(struct vdagentd_uinput **uinputp, - int width, int height) -{ - struct vdagentd_uinput *uinput = *uinputp; - struct uinput_user_dev device = { - .name = "spice vdagent tablet", - .absmax [ ABS_X ] = width, - .absmax [ ABS_Y ] = height, - }; - int rc; - - if (uinput->width == width && uinput->height == height) - return; - - uinput->width = width; - uinput->height = height; - - if (uinput->fd != -1) - close(uinput->fd); - - uinput->fd = open(uinput->devname, O_RDWR); - if (uinput->fd == -1) { - fprintf(uinput->errfile, "open %s: %s\n", - uinput->devname, strerror(errno)); - vdagentd_uinput_destroy(uinputp); - return; - } - - rc = write(uinput->fd, &device, sizeof(device)); - if (rc != sizeof(device)) { - fprintf(uinput->errfile, "write %s: %s\n", - uinput->devname, strerror(errno)); - vdagentd_uinput_destroy(uinputp); - return; - } - - /* buttons */ - ioctl(uinput->fd, UI_SET_EVBIT, EV_KEY); - ioctl(uinput->fd, UI_SET_KEYBIT, BTN_LEFT); - ioctl(uinput->fd, UI_SET_KEYBIT, BTN_MIDDLE); - ioctl(uinput->fd, UI_SET_KEYBIT, BTN_RIGHT); - - /* wheel */ - ioctl(uinput->fd, UI_SET_EVBIT, EV_REL); - ioctl(uinput->fd, UI_SET_RELBIT, REL_WHEEL); - - /* abs ptr */ - ioctl(uinput->fd, UI_SET_EVBIT, EV_ABS); - ioctl(uinput->fd, UI_SET_ABSBIT, ABS_X); - ioctl(uinput->fd, UI_SET_ABSBIT, ABS_Y); - - rc = ioctl(uinput->fd, UI_DEV_CREATE); - if (rc < 0) { - fprintf(uinput->errfile, "create %s: %s\n", - uinput->devname, strerror(errno)); - vdagentd_uinput_destroy(uinputp); - } -} - -static void uinput_send_event(struct vdagentd_uinput **uinputp, - __u16 type, __u16 code, __s32 value) -{ - struct vdagentd_uinput *uinput = *uinputp; - struct input_event event = { - .type = type, - .code = code, - .value = value, - }; - int rc; - - rc = write(uinput->fd, &event, sizeof(event)); - if (rc != sizeof(event)) { - fprintf(uinput->errfile, "write %s: %s\n", - uinput->devname, strerror(errno)); - vdagentd_uinput_destroy(uinputp); - } -} - -void vdagentd_uinput_do_mouse(struct vdagentd_uinput **uinputp, - VDAgentMouseState *mouse) -{ - struct vdagentd_uinput *uinput = *uinputp; - struct button_s { - const char *name; - int mask; - int btn; - }; - static const struct button_s btns[] = { - { .name = "left", .mask = VD_AGENT_LBUTTON_MASK, .btn = BTN_LEFT }, - { .name = "middle", .mask = VD_AGENT_MBUTTON_MASK, .btn = BTN_MIDDLE }, - { .name = "right", .mask = VD_AGENT_RBUTTON_MASK, .btn = BTN_RIGHT }, - }; - static const struct button_s wheel[] = { - { .name = "up", .mask = VD_AGENT_UBUTTON_MASK, .btn = 1 }, - { .name = "down", .mask = VD_AGENT_DBUTTON_MASK, .btn = -1 }, - }; - int i, down; - - if (*uinputp && uinput->last.x != mouse->x) { - if (uinput->verbose) - fprintf(uinput->errfile, "mouse: abs-x %d\n", mouse->x); - uinput_send_event(uinputp, EV_ABS, ABS_X, mouse->x); - } - if (*uinputp && uinput->last.y != mouse->y) { - if (uinput->verbose) - fprintf(uinput->errfile, "mouse: abs-y %d\n", mouse->y); - uinput_send_event(uinputp, EV_ABS, ABS_Y, mouse->y); - } - for (i = 0; i < sizeof(btns)/sizeof(btns[0]) && *uinputp; i++) { - if ((uinput->last.buttons & btns[i].mask) == - (mouse->buttons & btns[i].mask)) - continue; - down = !!(mouse->buttons & btns[i].mask); - if (uinput->verbose) - fprintf(uinput->errfile, "mouse: btn-%s %s\n", - btns[i].name, down ? "down" : "up"); - uinput_send_event(uinputp, EV_KEY, btns[i].btn, down); - } - for (i = 0; i < sizeof(wheel)/sizeof(wheel[0]) && *uinputp; i++) { - if ((uinput->last.buttons & wheel[i].mask) == - (mouse->buttons & wheel[i].mask)) - continue; - if (mouse->buttons & wheel[i].mask) { - if (uinput->verbose) - fprintf(uinput->errfile, "mouse: wheel-%s\n", wheel[i].name); - uinput_send_event(uinputp, EV_REL, REL_WHEEL, wheel[i].btn); - } - } - - if (*uinputp) { - if (uinput->verbose) - fprintf(uinput->errfile, "mouse: syn\n"); - uinput_send_event(uinputp, EV_SYN, SYN_REPORT, 0); - } - - if (*uinputp) - uinput->last = *mouse; -} diff --git a/vdagentd-uinput.h b/vdagentd-uinput.h deleted file mode 100644 index 132986b..0000000 --- a/vdagentd-uinput.h +++ /dev/null @@ -1,38 +0,0 @@ -/* vdagentd-uinput.c vdagentd uinput handling header - - Copyright 2010 Red Hat, Inc. - - Red Hat Authors: - Hans de Goede - Gerd Hoffmann - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ -#ifndef __VDAGENTD_UINPUT_H -#define __VDAGENTD_UINPUT_H - -#include - -struct vdagentd_uinput; - -struct vdagentd_uinput *vdagentd_uinput_create(const char *devname, - int width, int height, FILE *errfile, int verbose); -void vdagentd_uinput_destroy(struct vdagentd_uinput **uinputp); - -void vdagentd_uinput_do_mouse(struct vdagentd_uinput **uinputp, - VDAgentMouseState *mouse); -void vdagentd_uinput_update_size(struct vdagentd_uinput **uinputp, - int width, int height); - -#endif diff --git a/vdagentd.c b/vdagentd.c deleted file mode 100644 index 21588c5..0000000 --- a/vdagentd.c +++ /dev/null @@ -1,771 +0,0 @@ -/* vdagentd.c vdagentd (daemon) code - - Copyright 2010 Red Hat, Inc. - - Red Hat Authors: - Hans de Goede - Gerd Hoffmann - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udscs.h" -#include "vdagentd-proto.h" -#include "vdagentd-proto-strings.h" -#include "vdagentd-uinput.h" -#include "vdagent-virtio-port.h" -#include "console-kit.h" - -struct agent_data { - char *session; - int width; - int height; -}; - -/* variables */ -static const char *logfilename = "/var/log/spice-vdagentd/spice-vdagentd.log"; -static const char *pidfilename = "/var/run/spice-vdagentd/spice-vdagentd.pid"; -static const char *portdev = "/dev/virtio-ports/com.redhat.spice.0"; -static const char *uinput_device = "/dev/uinput"; -static int debug = 0; -static struct udscs_server *server = NULL; -static struct vdagent_virtio_port *virtio_port = NULL; -static struct console_kit *console_kit = NULL; -static struct vdagentd_uinput *uinput = NULL; -static VDAgentMonitorsConfig *mon_config = NULL; -static uint32_t *capabilities = NULL; -static int capabilities_size = 0; -static const char *active_session = NULL; -static struct udscs_connection *active_session_conn = NULL; -static int agent_owns_clipboard[256] = { 0, }; -static FILE *logfile = NULL; -static int quit = 0; -static int retval = 0; - -/* utility functions */ -/* vdagentd <-> spice-client communication handling */ -static void send_capabilities(struct vdagent_virtio_port *vport, - uint32_t request) -{ - VDAgentAnnounceCapabilities *caps; - uint32_t size; - - size = sizeof(*caps) + VD_AGENT_CAPS_BYTES; - caps = calloc(1, size); - if (!caps) { - fprintf(logfile, - "out of memory allocating capabilities array (write)\n"); - return; - } - - caps->request = request; - VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MOUSE_STATE); - VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MONITORS_CONFIG); - VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_REPLY); - VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND); - VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_SELECTION); - - vdagent_virtio_port_write(vport, VDP_CLIENT_PORT, - VD_AGENT_ANNOUNCE_CAPABILITIES, 0, - (uint8_t *)caps, size); - free(caps); -} - -static void do_client_monitors(struct vdagent_virtio_port *vport, int port_nr, - VDAgentMessage *message_header, VDAgentMonitorsConfig *new_monitors) -{ - VDAgentReply reply; - uint32_t size; - - /* Store monitor config to send to agents when they connect */ - size = sizeof(VDAgentMonitorsConfig) + - new_monitors->num_of_monitors * sizeof(VDAgentMonConfig); - if (message_header->size != size) { - fprintf(logfile, "invalid message size for VDAgentMonitorsConfig\n"); - return; - } - - if (!mon_config || - mon_config->num_of_monitors != new_monitors->num_of_monitors) { - free(mon_config); - mon_config = malloc(size); - if (!mon_config) { - fprintf(logfile, "out of memory allocating monitors config\n"); - return; - } - } - memcpy(mon_config, new_monitors, size); - - /* Send monitor config to currently connected agents */ - udscs_server_write_all(server, VDAGENTD_MONITORS_CONFIG, 0, 0, - (uint8_t *)mon_config, size); - - /* Acknowledge reception of monitors config to spice server / client */ - reply.type = VD_AGENT_MONITORS_CONFIG; - reply.error = VD_AGENT_SUCCESS; - vdagent_virtio_port_write(vport, port_nr, VD_AGENT_REPLY, 0, - (uint8_t *)&reply, sizeof(reply)); -} - -static void do_client_capabilities(struct vdagent_virtio_port *vport, - VDAgentMessage *message_header, - VDAgentAnnounceCapabilities *caps) -{ - int new_size = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header->size); - - if (capabilities_size != new_size) { - capabilities_size = new_size; - free(capabilities); - capabilities = malloc(capabilities_size * sizeof(uint32_t)); - if (!capabilities) { - fprintf(logfile, - "out of memory allocating capabilities array (read)\n"); - capabilities_size = 0; - return; - } - } - memcpy(capabilities, caps->caps, capabilities_size * sizeof(uint32_t)); - if (caps->request) - send_capabilities(vport, 0); -} - -static void do_client_clipboard(struct vdagent_virtio_port *vport, - VDAgentMessage *message_header, uint8_t *data) -{ - uint32_t msg_type = 0, data_type = 0, size = message_header->size; - uint8_t selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD; - - if (!active_session_conn) { - fprintf(logfile, - "Could not find an agent connnection belonging to the " - "active session, ignoring client clipboard request\n"); - return; - } - - if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size, - VD_AGENT_CAP_CLIPBOARD_SELECTION)) { - selection = data[0]; - data += 4; - size -= 4; - } - - switch (message_header->type) { - case VD_AGENT_CLIPBOARD_GRAB: - msg_type = VDAGENTD_CLIPBOARD_GRAB; - agent_owns_clipboard[selection] = 0; - break; - case VD_AGENT_CLIPBOARD_REQUEST: { - VDAgentClipboardRequest *req = (VDAgentClipboardRequest *)data; - msg_type = VDAGENTD_CLIPBOARD_REQUEST; - data_type = req->type; - data = NULL; - size = 0; - break; - } - case VD_AGENT_CLIPBOARD: { - VDAgentClipboard *clipboard = (VDAgentClipboard *)data; - msg_type = VDAGENTD_CLIPBOARD_DATA; - data_type = clipboard->type; - size = size - sizeof(VDAgentClipboard); - data = clipboard->data; - break; - } - case VD_AGENT_CLIPBOARD_RELEASE: - msg_type = VDAGENTD_CLIPBOARD_RELEASE; - data = NULL; - size = 0; - break; - } - - udscs_write(active_session_conn, msg_type, selection, data_type, - data, size); -} - -int virtio_port_read_complete( - struct vdagent_virtio_port *vport, - int port_nr, - VDAgentMessage *message_header, - uint8_t *data) -{ - uint32_t min_size = 0; - - if (message_header->protocol != VD_AGENT_PROTOCOL) { - fprintf(logfile, "message with wrong protocol version ignoring\n"); - return 0; - } - - switch (message_header->type) { - case VD_AGENT_MOUSE_STATE: - if (message_header->size != sizeof(VDAgentMouseState)) - goto size_error; - vdagentd_uinput_do_mouse(&uinput, (VDAgentMouseState *)data); - if (!uinput) { - /* Try to re-open the tablet */ - struct agent_data *agent_data = - udscs_get_user_data(active_session_conn); - if (agent_data) - uinput = vdagentd_uinput_create(uinput_device, - agent_data->width, - agent_data->height, - logfile, debug > 1); - if (!uinput) { - fprintf(logfile, "Fatal uinput error\n"); - retval = 1; - quit = 1; - } - } - break; - case VD_AGENT_MONITORS_CONFIG: - if (message_header->size < sizeof(VDAgentMonitorsConfig)) - goto size_error; - do_client_monitors(vport, port_nr, message_header, - (VDAgentMonitorsConfig *)data); - break; - case VD_AGENT_ANNOUNCE_CAPABILITIES: - if (message_header->size < sizeof(VDAgentAnnounceCapabilities)) - goto size_error; - do_client_capabilities(vport, message_header, - (VDAgentAnnounceCapabilities *)data); - break; - case VD_AGENT_CLIPBOARD_GRAB: - case VD_AGENT_CLIPBOARD_REQUEST: - case VD_AGENT_CLIPBOARD: - case VD_AGENT_CLIPBOARD_RELEASE: - switch (message_header->type) { - case VD_AGENT_CLIPBOARD_GRAB: - min_size = sizeof(VDAgentClipboardGrab); break; - case VD_AGENT_CLIPBOARD_REQUEST: - min_size = sizeof(VDAgentClipboardRequest); break; - case VD_AGENT_CLIPBOARD: - min_size = sizeof(VDAgentClipboard); break; - } - if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size, - VD_AGENT_CAP_CLIPBOARD_SELECTION)) { - min_size += 4; - } - if (message_header->size < min_size) { - goto size_error; - } - do_client_clipboard(vport, message_header, data); - break; - default: - if (debug) - fprintf(logfile, "unknown message type %d\n", message_header->type); - break; - } - - return 0; - -size_error: - fprintf(logfile, "read: invalid message size: %u for message type: %u\n", - message_header->size, message_header->type); - return 0; -} - -/* vdagentd <-> vdagent communication handling */ -int do_agent_clipboard(struct udscs_connection *conn, - struct udscs_message_header *header, const uint8_t *data) -{ - const uint8_t *msg; - uint8_t *buf = NULL, selection = header->arg1; - uint32_t msg_type = 0, data_type = -1, size = header->size; - - if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size, - VD_AGENT_CAP_CLIPBOARD_BY_DEMAND)) - goto error; - - /* Check that this agent is from the currently active session */ - if (conn != active_session_conn) { - fprintf(logfile, "Clipboard request from agent " - "which is not in the active session?\n"); - goto error; - } - - if (!virtio_port) { - fprintf(logfile, - "Clipboard request from agent but no client connection\n"); - goto error; - } - - if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size, - VD_AGENT_CAP_CLIPBOARD_SELECTION) && - selection != VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) { - goto error; - } - - switch (header->type) { - case VDAGENTD_CLIPBOARD_GRAB: - msg_type = VD_AGENT_CLIPBOARD_GRAB; - agent_owns_clipboard[selection] = 1; - break; - case VDAGENTD_CLIPBOARD_REQUEST: - msg_type = VD_AGENT_CLIPBOARD_REQUEST; - data_type = header->arg2; - size = 0; - break; - case VDAGENTD_CLIPBOARD_DATA: - msg_type = VD_AGENT_CLIPBOARD; - data_type = header->arg2; - break; - case VDAGENTD_CLIPBOARD_RELEASE: - msg_type = VD_AGENT_CLIPBOARD_RELEASE; - size = 0; - agent_owns_clipboard[selection] = 0; - break; - } - - if (size != header->size) { - fprintf(logfile, - "unexpected extra data in clipboard msg, disconnecting agent\n"); - return -1; - } - - if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size, - VD_AGENT_CAP_CLIPBOARD_SELECTION)) { - size += 4; - } - if (data_type != -1) { - size += 4; - } - if (size != header->size) { - uint8_t *p; - buf = p = malloc(size); - if (!buf) { - fprintf(logfile, "out of memory allocating clipboard (write)\n"); - return -1; - } - if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size, - VD_AGENT_CAP_CLIPBOARD_SELECTION)) { - p[0] = selection; - p[1] = 0; - p[2] = 0; - p[3] = 0; - p += 4; - } - if (data_type != -1) { - uint32_t *u = (uint32_t *)p; - u[0] = data_type; - p += 4; - } - memcpy(p, data, header->size); - msg = buf; - } else { - msg = data; - } - - vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT, msg_type, - 0, msg, size); - free(buf); - return 0; - -error: - if (header->type == VDAGENTD_CLIPBOARD_REQUEST) { - /* Let the agent know no answer is coming */ - udscs_write(conn, VDAGENTD_CLIPBOARD_DATA, - selection, VD_AGENT_CLIPBOARD_NONE, NULL, 0); - } - return 0; -} - -/* When we open the vdagent virtio channel, the server automatically goes into - client mouse mode, so we can only have the channel open when we know the - active session resolution. This function checks that we have an agent in the - active session, and that it has told us its resolution. If these conditions - are met it sets the uinput tablet device's resolution and opens the virtio - channel (if it is not already open). If these conditions are not met, it - closes both. */ -static void check_xorg_resolution(void) -{ - struct agent_data *agent_data = udscs_get_user_data(active_session_conn); - - if (agent_data && agent_data->width) { - if (!uinput) - uinput = vdagentd_uinput_create(uinput_device, - agent_data->width, - agent_data->height, - logfile, debug > 1); - else - vdagentd_uinput_update_size(&uinput, agent_data->width, - agent_data->height); - if (!uinput) { - fprintf(logfile, "Fatal uinput error\n"); - retval = 1; - quit = 1; - return; - } - - if (!virtio_port) { - fprintf(logfile, "opening vdagent virtio channel\n"); - virtio_port = vdagent_virtio_port_create(portdev, - virtio_port_read_complete, - NULL, logfile); - if (!virtio_port) { - fprintf(logfile, - "Fatal error opening vdagent virtio channel\n"); - retval = 1; - quit = 1; - return; - } - send_capabilities(virtio_port, 1); - } - } else { - vdagentd_uinput_destroy(&uinput); - if (virtio_port) { - vdagent_virtio_port_flush(&virtio_port); - vdagent_virtio_port_destroy(&virtio_port); - fprintf(logfile, "closed vdagent virtio channel\n"); - } - } -} - -static int connection_matches_active_session(struct udscs_connection **connp, - void *priv) -{ - struct udscs_connection **conn_ret = (struct udscs_connection **)priv; - struct agent_data *agent_data = udscs_get_user_data(*connp); - - /* Check if this connection matches the currently active session */ - if (!agent_data->session || !active_session) - return 0; - if (strcmp(agent_data->session, active_session)) - return 0; - - *conn_ret = *connp; - return 1; -} - -void release_clipboards(void) -{ - uint8_t sel; - - for (sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; ++sel) { - if (agent_owns_clipboard[sel] && virtio_port) { - vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT, - VD_AGENT_CLIPBOARD_RELEASE, 0, &sel, 1); - } - agent_owns_clipboard[sel] = 0; - } -} - -void update_active_session_connection(void) -{ - struct udscs_connection *new_conn = NULL; - int n; - - if (!active_session) - active_session = console_kit_get_active_session(console_kit); - - n = udscs_server_for_all_clients(server, connection_matches_active_session, - (void*)&new_conn); - if (n != 1) - new_conn = NULL; - - if (new_conn == active_session_conn) - return; - - active_session_conn = new_conn; - - release_clipboards(); - - check_xorg_resolution(); -} - -void agent_connect(struct udscs_connection *conn) -{ - uint32_t pid; - struct agent_data *agent_data; - - agent_data = calloc(1, sizeof(*agent_data)); - if (!agent_data) { - fprintf(logfile, "Out of memory allocating agent data, disconnecting\n"); - udscs_destroy_connection(&conn); - return; - } - - pid = udscs_get_peer_cred(conn).pid; - agent_data->session = console_kit_session_for_pid(console_kit, pid); - udscs_set_user_data(conn, (void *)agent_data); - update_active_session_connection(); - - if (mon_config) - udscs_write(conn, VDAGENTD_MONITORS_CONFIG, 0, 0, - (uint8_t *)mon_config, sizeof(VDAgentMonitorsConfig) + - mon_config->num_of_monitors * sizeof(VDAgentMonConfig)); -} - -void agent_disconnect(struct udscs_connection *conn) -{ - struct agent_data *agent_data = udscs_get_user_data(conn); - - free(agent_data->session); - agent_data->session = NULL; - update_active_session_connection(); - - free(agent_data); -} - -void agent_read_complete(struct udscs_connection **connp, - struct udscs_message_header *header, uint8_t *data) -{ - struct agent_data *agent_data = udscs_get_user_data(*connp); - - switch (header->type) { - case VDAGENTD_GUEST_XORG_RESOLUTION: { - struct vdagentd_guest_xorg_resolution *res = - (struct vdagentd_guest_xorg_resolution *)data; - - if (header->size != sizeof(*res)) { - fprintf(logfile, - "guest xorg resolution message has wrong size, disconnecting agent\n"); - udscs_destroy_connection(connp); - return; - } - - agent_data->width = res->width; - agent_data->height = res->height; - check_xorg_resolution(); - break; - } - case VDAGENTD_CLIPBOARD_GRAB: - case VDAGENTD_CLIPBOARD_REQUEST: - case VDAGENTD_CLIPBOARD_DATA: - case VDAGENTD_CLIPBOARD_RELEASE: - if (do_agent_clipboard(*connp, header, data)) { - udscs_destroy_connection(connp); - return; - } - break; - default: - fprintf(logfile, "unknown message from vdagent: %u, ignoring\n", - header->type); - } - free(data); -} - -/* main */ - -static void usage(FILE *fp) -{ - fprintf(fp, - "vdagentd\n" - "options:\n" - " -h print this text\n" - " -d log debug messages (use twice for extra info)\n" - " -s set virtio serial port [%s]\n" - " -u set uinput device [%s]\n" - " -x don't daemonize (and log to logfile)\n", - portdev, uinput_device); -} - -void daemonize(void) -{ - int x; - FILE *pidfile; - - /* detach from terminal */ - switch (fork()) { - case 0: - close(0); close(1); close(2); - setsid(); - x = open("/dev/null", O_RDWR); dup(x); dup(x); - pidfile = fopen(pidfilename, "w"); - if (pidfile) { - fprintf(pidfile, "%d\n", (int)getpid()); - fclose(pidfile); - } - break; - case -1: - fprintf(logfile, "fork: %s\n", strerror(errno)); - retval = 1; - default: - udscs_destroy_server(server); - if (logfile != stderr) - fclose(logfile); - exit(retval); - } -} - -void main_loop(void) -{ - fd_set readfds, writefds; - int n, nfds, ck_fd = 0; - - while (!quit) { - FD_ZERO(&readfds); - FD_ZERO(&writefds); - - nfds = udscs_server_fill_fds(server, &readfds, &writefds); - n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds); - if (n >= nfds) - nfds = n + 1; - - ck_fd = console_kit_get_fd(console_kit); - FD_SET(ck_fd, &readfds); - if (ck_fd >= nfds) - nfds = ck_fd + 1; - - n = select(nfds, &readfds, &writefds, NULL, NULL); - if (n == -1) { - if (errno == EINTR) - continue; - fprintf(logfile, "Fatal error select: %s\n", strerror(errno)); - retval = 1; - break; - } - - udscs_server_handle_fds(server, &readfds, &writefds); - - if (virtio_port) { - vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds); - if (!virtio_port) { - fprintf(logfile, - "AIIEEE lost spice client connection, reconnecting\n"); - virtio_port = vdagent_virtio_port_create(portdev, - virtio_port_read_complete, - NULL, logfile); - } - if (!virtio_port) { - fprintf(logfile, - "Fatal error opening vdagent virtio channel\n"); - retval = 1; - break; - } - } - - if (FD_ISSET(ck_fd, &readfds)) { - active_session = console_kit_get_active_session(console_kit); - update_active_session_connection(); - } - fflush(logfile); - } -} - -static void quit_handler(int sig) -{ - quit = 1; -} - -int main(int argc, char *argv[]) -{ - int c; - int do_daemonize = 1; - struct sigaction act; - - for (;;) { - if (-1 == (c = getopt(argc, argv, "-dhxs:u:"))) - break; - switch (c) { - case 'd': - debug++; - break; - case 's': - portdev = optarg; - break; - case 'u': - uinput_device = optarg; - break; - case 'x': - do_daemonize = 0; - break; - case 'h': - usage(stdout); - return 0; - default: - usage(stderr); - return 1; - } - } - - memset(&act, 0, sizeof(act)); - act.sa_flags = SA_RESTART; - act.sa_handler = quit_handler; - sigaction(SIGINT, &act, NULL); - sigaction(SIGHUP, &act, NULL); - sigaction(SIGTERM, &act, NULL); - sigaction(SIGQUIT, &act, NULL); - - if (do_daemonize) { - logfile = fopen(logfilename, "a"); - if (!logfile) { - fprintf(stderr, "Error opening %s: %s\n", logfilename, - strerror(errno)); - logfile = stderr; - } - } else - logfile = stderr; - - /* Setup communication with vdagent process(es) */ - server = udscs_create_server(VDAGENTD_SOCKET, agent_connect, - agent_read_complete, agent_disconnect, - vdagentd_messages, VDAGENTD_NO_MESSAGES, - debug? logfile:NULL, logfile); - if (!server) { - fprintf(logfile, "Fatal could not create server socket %s\n", - VDAGENTD_SOCKET); - if (logfile != stderr) - fclose(logfile); - return 1; - } - if (chmod(VDAGENTD_SOCKET, 0666)) { - fprintf(logfile, "Fatal could not change permissions on %s: %s\n", - VDAGENTD_SOCKET, strerror(errno)); - udscs_destroy_server(server); - if (logfile != stderr) - fclose(logfile); - return 1; - } - - if (do_daemonize) - daemonize(); - - console_kit = console_kit_create(logfile); - if (!console_kit) { - fprintf(logfile, "Fatal could not connect to console kit\n"); - udscs_destroy_server(server); - if (logfile != stderr) - fclose(logfile); - return 1; - } - - main_loop(); - - release_clipboards(); - - vdagentd_uinput_destroy(&uinput); - vdagent_virtio_port_flush(&virtio_port); - vdagent_virtio_port_destroy(&virtio_port); - console_kit_destroy(console_kit); - udscs_destroy_server(server); - if (unlink(VDAGENTD_SOCKET) != 0) - fprintf(logfile, "unlink %s: %s\n", VDAGENTD_SOCKET, strerror(errno)); - fprintf(logfile, "vdagentd quiting, returning status %d\n", retval); - if (logfile != stderr) - fclose(logfile); - - if (do_daemonize) - unlink(pidfilename); - - return retval; -} -- cgit v1.2.3