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