1d514b0f3Smrg#ifdef HAVE_CONFIG_H 2d514b0f3Smrg#include "config.h" 3d514b0f3Smrg#endif 4d514b0f3Smrg 5d514b0f3Smrg#include <errno.h> 6d514b0f3Smrg#include <sys/socket.h> 7d514b0f3Smrg#include <sys/un.h> 8d514b0f3Smrg#include <fcntl.h> 9d514b0f3Smrg#include <unistd.h> 10d514b0f3Smrg 11d514b0f3Smrg#include "qxl_option_helpers.h" 12d514b0f3Smrg 13d514b0f3Smrg#include "spiceqxl_util.h" 14d514b0f3Smrg#include "spiceqxl_uinput.h" 15d514b0f3Smrg#include "spiceqxl_vdagent.h" 16d514b0f3Smrg 17d514b0f3Smrgstatic const char *vdagent_virtio_filename; 18d514b0f3Smrgstatic int virtio_fd; 19d514b0f3Smrgstatic int virtio_client_fd = -1; 20d514b0f3Smrgstatic SpiceWatch *virtio_client_watch; 21d514b0f3Smrg 22d514b0f3Smrgtypedef struct XSpiceVdagentCharDeviceInstance { 23d514b0f3Smrg SpiceCharDeviceInstance base; 24d514b0f3Smrg qxl_screen_t *qxl; 25d514b0f3Smrg} XSpiceVdagentCharDeviceInstance; 26d514b0f3Smrg 27d514b0f3Smrgstatic XSpiceVdagentCharDeviceInstance vdagent_sin = { 28d514b0f3Smrg .base = { 29d514b0f3Smrg .subtype = "vdagent" 30d514b0f3Smrg } 31d514b0f3Smrg}; 32d514b0f3Smrg 33d514b0f3Smrgstatic int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len) 34d514b0f3Smrg{ 35d514b0f3Smrg int written; 36d514b0f3Smrg 37d514b0f3Smrg if (virtio_client_fd == -1) { 38d514b0f3Smrg return 0; 39d514b0f3Smrg } 40d514b0f3Smrg written = send(virtio_client_fd, buf, len, 0); 41d514b0f3Smrg if (written != len) { 42d514b0f3Smrg fprintf(stderr, "%s: ERROR: short write to vdagentd - TODO buffering\n", __func__); 43d514b0f3Smrg } 44d514b0f3Smrg return written; 45d514b0f3Smrg} 46d514b0f3Smrg 47d514b0f3Smrgstatic int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) 48d514b0f3Smrg{ 49d514b0f3Smrg int nbytes; 50d514b0f3Smrg 51d514b0f3Smrg if (virtio_client_fd == -1) { 52d514b0f3Smrg return 0; 53d514b0f3Smrg } 54d514b0f3Smrg nbytes = recv(virtio_client_fd, buf, len, 0); 55d514b0f3Smrg if (nbytes <= 0) { 56d514b0f3Smrg if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { 57d514b0f3Smrg return 0; 58d514b0f3Smrg } 59d514b0f3Smrg fprintf(stderr, "ERROR: vdagent died\n"); 60d514b0f3Smrg close(virtio_client_fd); 61d514b0f3Smrg virtio_client_fd = -1; 62d514b0f3Smrg vdagent_sin.qxl->core->watch_remove(virtio_client_watch); 63d514b0f3Smrg virtio_client_watch = NULL; 64d514b0f3Smrg spice_server_remove_interface(&vdagent_sin.base.base); 65d514b0f3Smrg spiceqxl_uinput_watch(vdagent_sin.qxl, FALSE); 66d514b0f3Smrg } 67d514b0f3Smrg return nbytes; 68d514b0f3Smrg} 69d514b0f3Smrg 70d514b0f3Smrgstatic void on_read_available(int fd, int event, void *opaque) 71d514b0f3Smrg{ 72d514b0f3Smrg if (virtio_client_fd == -1) { 73d514b0f3Smrg return; 74d514b0f3Smrg } 75d514b0f3Smrg spice_server_char_device_wakeup(&vdagent_sin.base); 76d514b0f3Smrg} 77d514b0f3Smrg 78d514b0f3Smrg#if SPICE_SERVER_VERSION >= 0x000c02 79d514b0f3Smrgstatic void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event) 80d514b0f3Smrg{ 81d514b0f3Smrg} 82d514b0f3Smrg#endif 83d514b0f3Smrg 84d514b0f3Smrgstatic void vmc_state(SpiceCharDeviceInstance *sin, int connected) 85d514b0f3Smrg{ 86d514b0f3Smrg} 87d514b0f3Smrg 88d514b0f3Smrgstatic SpiceCharDeviceInterface vmc_interface = { 89d514b0f3Smrg .base.type = SPICE_INTERFACE_CHAR_DEVICE, 90d514b0f3Smrg .base.description = "Xspice virtual channel char device", 91d514b0f3Smrg .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR, 92d514b0f3Smrg .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR, 93d514b0f3Smrg .state = vmc_state, 94d514b0f3Smrg .write = vmc_write, 95d514b0f3Smrg .read = vmc_read, 96d514b0f3Smrg#if SPICE_SERVER_VERSION >= 0x000c02 97d514b0f3Smrg .event = vmc_event, 98d514b0f3Smrg#endif 99d514b0f3Smrg}; 100d514b0f3Smrg 101d514b0f3Smrgstatic void on_accept(int fd, int event, void *opaque) 102d514b0f3Smrg{ 103d514b0f3Smrg qxl_screen_t *qxl = opaque; 104d514b0f3Smrg struct sockaddr_un address; 105d514b0f3Smrg socklen_t length = sizeof(address); 106d514b0f3Smrg int flags; 107d514b0f3Smrg 108d514b0f3Smrg virtio_client_fd = accept(virtio_fd, (struct sockaddr *)&address, &length); 109d514b0f3Smrg if (virtio_client_fd == -1) { 110d514b0f3Smrg fprintf(stderr, "error accepting on unix domain socket: %s\n", strerror(errno)); 111d514b0f3Smrg return; 112d514b0f3Smrg } 113d514b0f3Smrg flags = fcntl(virtio_client_fd, F_GETFL); 114d514b0f3Smrg if (flags == -1) { 115d514b0f3Smrg fprintf(stderr, "error getting flags from uds client fd: %s\n", strerror(errno)); 116d514b0f3Smrg goto error; 117d514b0f3Smrg } 118d514b0f3Smrg if (fcntl(virtio_client_fd, F_SETFL, flags | O_NONBLOCK | O_CLOEXEC) == -1) { 119d514b0f3Smrg fprintf(stderr, "error setting CLOEXEC & NONBLOCK flags from uds client fd: %s\n", 120d514b0f3Smrg strerror(errno)); 121d514b0f3Smrg goto error; 122d514b0f3Smrg } 123d514b0f3Smrg virtio_client_watch = qxl->core->watch_add(virtio_client_fd, SPICE_WATCH_EVENT_READ 124d514b0f3Smrg /* TODO - SPICE_WATCH_EVENT_WRITE */, on_read_available, qxl); 125d514b0f3Smrg 126d514b0f3Smrg spice_server_add_interface(qxl->spice_server, &vdagent_sin.base.base); 127d514b0f3Smrg spiceqxl_uinput_watch(qxl, TRUE); 128d514b0f3Smrg 129d514b0f3Smrg return; 130d514b0f3Smrg 131d514b0f3Smrgerror: 132d514b0f3Smrg if (virtio_client_fd != -1) { 133d514b0f3Smrg close(virtio_client_fd); 134d514b0f3Smrg virtio_client_fd = -1; 135d514b0f3Smrg } 136d514b0f3Smrg} 137d514b0f3Smrg 138d514b0f3Smrgvoid spiceqxl_vdagent_init(qxl_screen_t *qxl) 139d514b0f3Smrg{ 140d514b0f3Smrg struct sockaddr_un address; 141d514b0f3Smrg int c; 142d514b0f3Smrg int enabled; 143d514b0f3Smrg 144d514b0f3Smrg vdagent_sin.qxl = qxl; 145d514b0f3Smrg vdagent_virtio_filename = get_str_option(qxl->options, OPTION_SPICE_VDAGENT_VIRTIO_PATH, 146d514b0f3Smrg "XSPICE_VDAGENT_VIRTIO_PATH"); 147d514b0f3Smrg enabled = get_bool_option(qxl->options, OPTION_SPICE_VDAGENT_ENABLED, "XSPICE_VDAGENT_ENABLED"); 148d514b0f3Smrg 149d514b0f3Smrg if (!enabled || !vdagent_virtio_filename) { 150d514b0f3Smrg return; 151d514b0f3Smrg } 152d514b0f3Smrg 153d514b0f3Smrg virtio_fd = socket(PF_UNIX, SOCK_STREAM, 0); 154d514b0f3Smrg if (virtio_fd == -1) { 155d514b0f3Smrg fprintf(stderr, "error creating unix domain socket\n"); 156d514b0f3Smrg return; 157d514b0f3Smrg } 158d514b0f3Smrg address.sun_family = AF_UNIX; 159d514b0f3Smrg snprintf(address.sun_path, sizeof(address.sun_path), "%s", vdagent_virtio_filename); 160d514b0f3Smrg c = bind(virtio_fd, (struct sockaddr *)&address, sizeof(address)); 161d514b0f3Smrg if (c != 0) { 162d514b0f3Smrg fprintf(stderr, "error binding unix domain socket to %s: %s\n", 163d514b0f3Smrg vdagent_virtio_filename, strerror(errno)); 164d514b0f3Smrg return; 165d514b0f3Smrg } 166d514b0f3Smrg spiceqxl_chown_agent_file(qxl, vdagent_virtio_filename); 167d514b0f3Smrg c = listen(virtio_fd, 1); 168d514b0f3Smrg if (c != 0) { 169d514b0f3Smrg fprintf(stderr, "error listening to unix domain socket: %s\n", strerror(errno)); 170d514b0f3Smrg return; 171d514b0f3Smrg } 172d514b0f3Smrg qxl->core->watch_add(virtio_fd, SPICE_WATCH_EVENT_READ 173d514b0f3Smrg /* TODO - SPICE_WATCH_EVENT_WRITE */, on_accept, qxl); 174d514b0f3Smrg 175d514b0f3Smrg vdagent_sin.base.base.sif = &vmc_interface.base; 176d514b0f3Smrg spiceqxl_uinput_init(qxl); 177d514b0f3Smrg} 178