vdagent FD_SET -> FD_ISSET
[vd_agent/hramrach.git] / vdagentd.c
blob05b48827682f1ec9e7c8799fbcf2d633bc8faf51
1 /* vdagentd.c vdagentd (daemon) code
3 Copyright 2010 Red Hat, Inc.
5 Red Hat Authors:
6 Hans de Goede <hdegoede@redhat.com>
7 Gerd Hoffmann <kraxel@redhat.com>
9 This program is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <sys/select.h>
30 #include <spice/vd_agent.h>
32 #include "udscs.h"
33 #include "vdagentd-proto.h"
34 #include "vdagentd-proto-strings.h"
35 #include "vdagentd-uinput.h"
36 #include "vdagent-virtio-port.h"
38 /* variables */
39 static const char *portdev = "/dev/virtio-ports/com.redhat.spice.0";
40 static const char *uinput = "/dev/uinput";
41 static int connection_count = 0;
42 static int debug = 0;
43 static struct udscs_server *server = NULL;
44 static struct vdagent_virtio_port *virtio_port = NULL;
45 static VDAgentMonitorsConfig *mon_config = NULL;
46 static uint32_t *capabilities = NULL;
47 static int capabilities_size = 0;
49 /* vdagent virtio port handling */
50 static void send_capabilities(struct vdagent_virtio_port *port,
51 uint32_t request)
53 VDAgentAnnounceCapabilities *caps;
54 uint32_t size;
56 size = sizeof(*caps) + VD_AGENT_CAPS_BYTES;
57 caps = calloc(1, size);
58 if (!caps) {
59 fprintf(stderr,
60 "out of memory allocating capabilities array (write)\n");
61 return;
64 caps->request = request;
65 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MOUSE_STATE);
66 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MONITORS_CONFIG);
67 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_REPLY);
68 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND);
70 vdagent_virtio_port_write(port, VDP_CLIENT_PORT,
71 VD_AGENT_ANNOUNCE_CAPABILITIES, 0,
72 (uint8_t *)caps, size);
73 free(caps);
76 static void do_monitors(struct vdagent_virtio_port *port, int port_nr,
77 VDAgentMessage *message_header, VDAgentMonitorsConfig *new_monitors)
79 VDAgentReply reply;
80 uint32_t size;
82 /* Store monitor config to send to agents when they connect */
83 size = sizeof(VDAgentMonitorsConfig) +
84 new_monitors->num_of_monitors * sizeof(VDAgentMonConfig);
85 if (message_header->size != size) {
86 fprintf(stderr, "invalid message size for VDAgentMonitorsConfig\n");
87 return;
90 if (!mon_config ||
91 mon_config->num_of_monitors != new_monitors->num_of_monitors) {
92 free(mon_config);
93 mon_config = malloc(size);
94 if (!mon_config) {
95 fprintf(stderr, "out of memory allocating monitors config\n");
96 return;
99 memcpy(mon_config, new_monitors, size);
101 /* Send monitor config to currently connected agents */
102 udscs_server_write_all(server, VDAGENTD_MONITORS_CONFIG, 0,
103 (uint8_t *)mon_config, size);
105 /* Acknowledge reception of monitors config to spice server / client */
106 reply.type = VD_AGENT_MONITORS_CONFIG;
107 reply.error = VD_AGENT_SUCCESS;
108 vdagent_virtio_port_write(port, port_nr, VD_AGENT_REPLY, 0,
109 (uint8_t *)&reply, sizeof(reply));
112 static void do_capabilities(struct vdagent_virtio_port *port,
113 VDAgentMessage *message_header,
114 VDAgentAnnounceCapabilities *caps)
116 capabilities_size = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header->size);
118 free(capabilities);
119 capabilities = malloc(capabilities_size * sizeof(uint32_t));
120 if (!capabilities) {
121 fprintf(stderr,
122 "out of memory allocating capabilities array (read)\n");
123 capabilities_size = 0;
124 return;
126 memcpy(capabilities, caps->caps, capabilities_size * sizeof(uint32_t));
127 if (caps->request)
128 send_capabilities(port, 0);
131 static void do_clipboard(struct vdagent_virtio_port *port,
132 VDAgentMessage *message_header, uint8_t *message_data)
134 uint32_t type = 0, opaque = 0, size = 0;
135 uint8_t *data = NULL;
137 switch (message_header->type) {
138 case VD_AGENT_CLIPBOARD_GRAB:
139 type = VDAGENTD_CLIPBOARD_GRAB;
140 data = message_data;
141 size = message_header->size;
142 break;
143 case VD_AGENT_CLIPBOARD_REQUEST: {
144 VDAgentClipboardRequest *req = (VDAgentClipboardRequest *)message_data;
145 type = VDAGENTD_CLIPBOARD_REQUEST;
146 opaque = req->type;
147 break;
149 case VD_AGENT_CLIPBOARD: {
150 VDAgentClipboard *clipboard = (VDAgentClipboard *)message_data;
151 type = VDAGENTD_CLIPBOARD_DATA;
152 opaque = clipboard->type;
153 size = message_header->size - sizeof(VDAgentClipboard);
154 data = clipboard->data;
155 break;
157 case VD_AGENT_CLIPBOARD_RELEASE:
158 type = VDAGENTD_CLIPBOARD_RELEASE;
159 break;
162 /* FIXME send only to agent in active session */
163 udscs_server_write_all(server, type, opaque, data, size);
166 int virtio_port_read_complete(
167 struct vdagent_virtio_port *port,
168 VDIChunkHeader *chunk_header,
169 VDAgentMessage *message_header,
170 uint8_t *data)
172 uint32_t min_size = 0;
174 if (message_header->protocol != VD_AGENT_PROTOCOL) {
175 fprintf(stderr, "message with wrong protocol version ignoring\n");
176 return 0;
179 switch (message_header->type) {
180 case VD_AGENT_MOUSE_STATE:
181 if (message_header->size != sizeof(VDAgentMouseState))
182 goto size_error;
183 uinput_do_mouse((VDAgentMouseState *)data, debug > 1);
184 break;
185 case VD_AGENT_MONITORS_CONFIG:
186 if (message_header->size < sizeof(VDAgentMonitorsConfig))
187 goto size_error;
188 do_monitors(port, chunk_header->port, message_header,
189 (VDAgentMonitorsConfig *)data);
190 break;
191 case VD_AGENT_ANNOUNCE_CAPABILITIES:
192 if (message_header->size < sizeof(VDAgentAnnounceCapabilities))
193 goto size_error;
194 do_capabilities(port, message_header,
195 (VDAgentAnnounceCapabilities *)data);
196 break;
197 case VD_AGENT_CLIPBOARD_GRAB:
198 case VD_AGENT_CLIPBOARD_REQUEST:
199 case VD_AGENT_CLIPBOARD:
200 case VD_AGENT_CLIPBOARD_RELEASE:
201 switch (message_header->type) {
202 case VD_AGENT_CLIPBOARD_GRAB:
203 min_size = sizeof(VDAgentClipboardGrab); break;
204 case VD_AGENT_CLIPBOARD_REQUEST:
205 min_size = sizeof(VDAgentClipboardRequest); break;
206 case VD_AGENT_CLIPBOARD:
207 min_size = sizeof(VDAgentClipboard); break;
209 if (message_header->size < min_size)
210 goto size_error;
211 do_clipboard(port, message_header, data);
212 break;
213 default:
214 if (debug)
215 fprintf(stderr, "unknown message type %d\n", message_header->type);
216 break;
219 return 0;
221 size_error:
222 fprintf(stderr, "read: invalid message size: %u for message type: %u\n",
223 message_header->size, message_header->type);
224 return 0;
227 /* vdagent client handling */
228 void do_client_clipboard(struct udscs_connection *conn,
229 struct udscs_message_header *header, const uint8_t *data)
231 if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
232 VD_AGENT_CAP_CLIPBOARD_BY_DEMAND))
233 goto error;
235 /* FIXME check that this client is from the currently active session */
236 if (0)
237 goto error;
239 switch (header->type) {
240 case VDAGENTD_CLIPBOARD_GRAB:
241 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
242 VD_AGENT_CLIPBOARD_GRAB, 0,
243 data, header->size);
244 break;
245 case VDAGENTD_CLIPBOARD_REQUEST: {
246 VDAgentClipboardRequest req = { .type = header->opaque };
247 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
248 VD_AGENT_CLIPBOARD_REQUEST, 0,
249 (uint8_t *)&req, sizeof(req));
250 break;
252 case VDAGENTD_CLIPBOARD_DATA: {
253 VDAgentClipboard *clipboard;
254 uint32_t size = sizeof(*clipboard) + header->size;
256 clipboard = calloc(1, size);
257 if (!clipboard) {
258 fprintf(stderr,
259 "out of memory allocating clipboard (write)\n");
260 return;
262 clipboard->type = header->opaque;
263 memcpy(clipboard->data, data, header->size);
265 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
266 VD_AGENT_CLIPBOARD, 0,
267 (uint8_t *)clipboard, size);
268 free(clipboard);
269 break;
271 case VDAGENTD_CLIPBOARD_RELEASE:
272 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
273 VD_AGENT_CLIPBOARD_RELEASE, 0, NULL, 0);
274 break;
277 return;
279 error:
280 if (header->type == VDAGENTD_CLIPBOARD_REQUEST) {
281 /* Let the agent know no answer is coming */
282 udscs_write(conn, VDAGENTD_CLIPBOARD_DATA,
283 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
287 void client_connect(struct udscs_connection *conn)
289 /* We don't create the tablet until we've gotten the xorg resolution
290 from the vdagent client */
291 connection_count++;
293 if (mon_config)
294 udscs_write(conn, VDAGENTD_MONITORS_CONFIG, 0, (uint8_t *)mon_config,
295 sizeof(VDAgentMonitorsConfig) +
296 mon_config->num_of_monitors * sizeof(VDAgentMonConfig));
299 void client_disconnect(struct udscs_connection *conn)
301 connection_count--;
302 if (connection_count == 0) {
303 uinput_close();
304 vdagent_virtio_port_destroy(&virtio_port);
308 int client_read_complete(struct udscs_connection *conn,
309 struct udscs_message_header *header, const uint8_t *data)
311 switch (header->type) {
312 case VDAGENTD_GUEST_XORG_RESOLUTION: {
313 struct vdagentd_guest_xorg_resolution *res =
314 (struct vdagentd_guest_xorg_resolution *)data;
316 if (header->size != sizeof(*res)) {
317 fprintf(stderr,
318 "guest xorg resolution message has wrong size, disconnecting client\n");
319 return -1;
322 /* Now that we know the xorg resolution setup the uinput device */
323 uinput_setup(uinput, res->width, res->height);
324 /* Now that we have a tablet and thus can forward mouse events,
325 we can open the vdagent virtio port. */
326 if (!virtio_port) {
327 virtio_port = vdagent_virtio_port_create(portdev,
328 virtio_port_read_complete,
329 NULL);
330 if (!virtio_port)
331 exit(1);
333 send_capabilities(virtio_port, 1);
335 break;
337 case VDAGENTD_CLIPBOARD_GRAB:
338 case VDAGENTD_CLIPBOARD_REQUEST:
339 case VDAGENTD_CLIPBOARD_DATA:
340 case VDAGENTD_CLIPBOARD_RELEASE:
341 do_client_clipboard(conn, header, data);
342 break;
343 default:
344 fprintf(stderr, "unknown message from vdagent client: %u, ignoring\n",
345 header->type);
348 return 0;
351 /* main */
353 static void usage(FILE *fp)
355 fprintf(fp,
356 "vdagent -- handle spice agent mouse via uinput\n"
357 "options:\n"
358 " -h print this text\n"
359 " -d print debug messages (and don't daemonize)\n"
360 " -s <port> set virtio serial port [%s]\n"
361 " -u <dev> set uinput device [%s]\n",
362 portdev, uinput);
365 void daemonize(void)
367 /* detach from terminal */
368 switch (fork()) {
369 case -1:
370 perror("fork");
371 exit(1);
372 case 0:
373 close(0); close(1); close(2);
374 setsid();
375 open("/dev/null",O_RDWR); dup(0); dup(0);
376 break;
377 default:
378 exit(0);
382 void main_loop(void)
384 fd_set readfds, writefds;
385 int n, nfds;
387 /* FIXME catch sigquit and set a flag to quit */
388 for (;;) {
389 FD_ZERO(&readfds);
390 FD_ZERO(&writefds);
392 nfds = udscs_server_fill_fds(server, &readfds, &writefds);
393 n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds);
394 if (n >= nfds)
395 nfds = n + 1;
397 n = select(nfds, &readfds, &writefds, NULL, NULL);
398 if (n == -1) {
399 if (errno == EINTR)
400 continue;
401 perror("select");
402 exit(1);
405 udscs_server_handle_fds(server, &readfds, &writefds);
406 vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds);
410 int main(int argc, char *argv[])
412 int c;
414 for (;;) {
415 if (-1 == (c = getopt(argc, argv, "dhx:y:s:u:")))
416 break;
417 switch (c) {
418 case 'd':
419 debug++;
420 break;
421 case 's':
422 portdev = optarg;
423 break;
424 case 'u':
425 uinput = optarg;
426 break;
427 case 'h':
428 usage(stdout);
429 exit(0);
430 default:
431 usage(stderr);
432 exit(1);
436 /* Setup communication with vdagent process(es) */
437 server = udscs_create_server(VDAGENTD_SOCKET, client_connect,
438 client_read_complete, client_disconnect,
439 vdagentd_messages, VDAGENTD_NO_MESSAGES,
440 debug? stderr:NULL, stderr);
441 if (!server)
442 exit(1);
444 if (!debug)
445 daemonize();
447 main_loop();
449 udscs_destroy_server(server);
451 return 0;