log open/close of vdagent virtio channel
[vd_agent/hramrach.git] / vdagentd.c
blob5bb364e37f456d649dbefe1acfdf6c44ed7b0494
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 <sys/stat.h>
31 #include <spice/vd_agent.h>
33 #include "udscs.h"
34 #include "vdagentd-proto.h"
35 #include "vdagentd-proto-strings.h"
36 #include "vdagentd-uinput.h"
37 #include "vdagent-virtio-port.h"
38 #include "console-kit.h"
40 struct agent_data {
41 char *session;
42 int width;
43 int height;
46 /* variables */
47 static const char *portdev = "/dev/virtio-ports/com.redhat.spice.0";
48 static const char *uinput = "/dev/uinput";
49 static int debug = 0;
50 static struct udscs_server *server = NULL;
51 static struct vdagent_virtio_port *virtio_port = NULL;
52 static struct console_kit *console_kit = NULL;
53 static VDAgentMonitorsConfig *mon_config = NULL;
54 static uint32_t *capabilities = NULL;
55 static int capabilities_size = 0;
56 static int uinput_width = 0;
57 static int uinput_height = 0;
59 /* utility functions */
60 static int connection_matches_active_session(struct udscs_connection **connp,
61 void *priv)
63 struct udscs_connection **conn_ret = (struct udscs_connection **)priv;
64 struct agent_data *agent_data = udscs_get_user_data(*connp);
65 const char *active_session;
67 /* Check if this connection matches the currently active session */
68 active_session = console_kit_get_active_session(console_kit);
69 if (!agent_data->session || !active_session)
70 return 0;
71 if (strcmp(agent_data->session, active_session))
72 return 0;
74 *conn_ret = *connp;
75 return 1;
78 struct udscs_connection *get_active_session_connection(void)
80 struct udscs_connection *conn = NULL;
81 int n;
83 n = udscs_server_for_all_clients(server, connection_matches_active_session,
84 (void*)&conn);
85 if (n != 1)
86 return NULL;
88 return conn;
91 /* vdagentd <-> spice-client communication handling */
92 static void send_capabilities(struct vdagent_virtio_port *port,
93 uint32_t request)
95 VDAgentAnnounceCapabilities *caps;
96 uint32_t size;
98 size = sizeof(*caps) + VD_AGENT_CAPS_BYTES;
99 caps = calloc(1, size);
100 if (!caps) {
101 fprintf(stderr,
102 "out of memory allocating capabilities array (write)\n");
103 return;
106 caps->request = request;
107 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MOUSE_STATE);
108 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MONITORS_CONFIG);
109 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_REPLY);
110 if (console_kit)
111 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND);
113 vdagent_virtio_port_write(port, VDP_CLIENT_PORT,
114 VD_AGENT_ANNOUNCE_CAPABILITIES, 0,
115 (uint8_t *)caps, size);
116 free(caps);
119 static void do_monitors(struct vdagent_virtio_port *port, int port_nr,
120 VDAgentMessage *message_header, VDAgentMonitorsConfig *new_monitors)
122 VDAgentReply reply;
123 uint32_t size;
125 /* Store monitor config to send to agents when they connect */
126 size = sizeof(VDAgentMonitorsConfig) +
127 new_monitors->num_of_monitors * sizeof(VDAgentMonConfig);
128 if (message_header->size != size) {
129 fprintf(stderr, "invalid message size for VDAgentMonitorsConfig\n");
130 return;
133 if (!mon_config ||
134 mon_config->num_of_monitors != new_monitors->num_of_monitors) {
135 free(mon_config);
136 mon_config = malloc(size);
137 if (!mon_config) {
138 fprintf(stderr, "out of memory allocating monitors config\n");
139 return;
142 memcpy(mon_config, new_monitors, size);
144 /* Send monitor config to currently connected agents */
145 udscs_server_write_all(server, VDAGENTD_MONITORS_CONFIG, 0,
146 (uint8_t *)mon_config, size);
148 /* Acknowledge reception of monitors config to spice server / client */
149 reply.type = VD_AGENT_MONITORS_CONFIG;
150 reply.error = VD_AGENT_SUCCESS;
151 vdagent_virtio_port_write(port, port_nr, VD_AGENT_REPLY, 0,
152 (uint8_t *)&reply, sizeof(reply));
155 static void do_capabilities(struct vdagent_virtio_port *port,
156 VDAgentMessage *message_header,
157 VDAgentAnnounceCapabilities *caps)
159 capabilities_size = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header->size);
161 free(capabilities);
162 capabilities = malloc(capabilities_size * sizeof(uint32_t));
163 if (!capabilities) {
164 fprintf(stderr,
165 "out of memory allocating capabilities array (read)\n");
166 capabilities_size = 0;
167 return;
169 memcpy(capabilities, caps->caps, capabilities_size * sizeof(uint32_t));
170 if (caps->request)
171 send_capabilities(port, 0);
174 static void do_clipboard(struct vdagent_virtio_port *port,
175 VDAgentMessage *message_header, uint8_t *message_data)
177 uint32_t type = 0, opaque = 0, size = 0;
178 uint8_t *data = NULL;
179 struct udscs_connection *conn;
181 switch (message_header->type) {
182 case VD_AGENT_CLIPBOARD_GRAB:
183 type = VDAGENTD_CLIPBOARD_GRAB;
184 data = message_data;
185 size = message_header->size;
186 break;
187 case VD_AGENT_CLIPBOARD_REQUEST: {
188 VDAgentClipboardRequest *req = (VDAgentClipboardRequest *)message_data;
189 type = VDAGENTD_CLIPBOARD_REQUEST;
190 opaque = req->type;
191 break;
193 case VD_AGENT_CLIPBOARD: {
194 VDAgentClipboard *clipboard = (VDAgentClipboard *)message_data;
195 type = VDAGENTD_CLIPBOARD_DATA;
196 opaque = clipboard->type;
197 size = message_header->size - sizeof(VDAgentClipboard);
198 data = clipboard->data;
199 break;
201 case VD_AGENT_CLIPBOARD_RELEASE:
202 type = VDAGENTD_CLIPBOARD_RELEASE;
203 break;
206 conn = get_active_session_connection();
207 if (!conn) {
208 fprintf(stderr,
209 "Could not find an agent connnection belonging to the "
210 "active session, ignoring client clipboard request\n");
211 return;
214 udscs_write(conn, type, opaque, data, size);
217 int virtio_port_read_complete(
218 struct vdagent_virtio_port *port,
219 VDIChunkHeader *chunk_header,
220 VDAgentMessage *message_header,
221 uint8_t *data)
223 uint32_t min_size = 0;
225 if (message_header->protocol != VD_AGENT_PROTOCOL) {
226 fprintf(stderr, "message with wrong protocol version ignoring\n");
227 return 0;
230 switch (message_header->type) {
231 case VD_AGENT_MOUSE_STATE:
232 if (message_header->size != sizeof(VDAgentMouseState))
233 goto size_error;
234 uinput_do_mouse((VDAgentMouseState *)data, debug > 1);
235 break;
236 case VD_AGENT_MONITORS_CONFIG:
237 if (message_header->size < sizeof(VDAgentMonitorsConfig))
238 goto size_error;
239 do_monitors(port, chunk_header->port, message_header,
240 (VDAgentMonitorsConfig *)data);
241 break;
242 case VD_AGENT_ANNOUNCE_CAPABILITIES:
243 if (message_header->size < sizeof(VDAgentAnnounceCapabilities))
244 goto size_error;
245 do_capabilities(port, message_header,
246 (VDAgentAnnounceCapabilities *)data);
247 break;
248 case VD_AGENT_CLIPBOARD_GRAB:
249 case VD_AGENT_CLIPBOARD_REQUEST:
250 case VD_AGENT_CLIPBOARD:
251 case VD_AGENT_CLIPBOARD_RELEASE:
252 switch (message_header->type) {
253 case VD_AGENT_CLIPBOARD_GRAB:
254 min_size = sizeof(VDAgentClipboardGrab); break;
255 case VD_AGENT_CLIPBOARD_REQUEST:
256 min_size = sizeof(VDAgentClipboardRequest); break;
257 case VD_AGENT_CLIPBOARD:
258 min_size = sizeof(VDAgentClipboard); break;
260 if (message_header->size < min_size)
261 goto size_error;
262 do_clipboard(port, message_header, data);
263 break;
264 default:
265 if (debug)
266 fprintf(stderr, "unknown message type %d\n", message_header->type);
267 break;
270 return 0;
272 size_error:
273 fprintf(stderr, "read: invalid message size: %u for message type: %u\n",
274 message_header->size, message_header->type);
275 return 0;
278 /* vdagentd <-> vdagent communication handling */
279 void do_agent_clipboard(struct udscs_connection *conn,
280 struct udscs_message_header *header, const uint8_t *data)
282 const char *active_session;
283 struct agent_data *agent_data = udscs_get_user_data(conn);
285 if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
286 VD_AGENT_CAP_CLIPBOARD_BY_DEMAND))
287 goto error;
289 /* Check that this agent is from the currently active session */
290 active_session = console_kit_get_active_session(console_kit);
291 if (!agent_data->session || !active_session) {
292 fprintf(stderr, "Could not get session info, ignoring agent clipboard request\n");
293 goto error;
295 if (strcmp(agent_data->session, active_session)) {
296 fprintf(stderr, "Clipboard request from agent which is not in the active session?\n");
297 goto error;
300 switch (header->type) {
301 case VDAGENTD_CLIPBOARD_GRAB:
302 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
303 VD_AGENT_CLIPBOARD_GRAB, 0,
304 data, header->size);
305 break;
306 case VDAGENTD_CLIPBOARD_REQUEST: {
307 VDAgentClipboardRequest req = { .type = header->opaque };
308 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
309 VD_AGENT_CLIPBOARD_REQUEST, 0,
310 (uint8_t *)&req, sizeof(req));
311 break;
313 case VDAGENTD_CLIPBOARD_DATA: {
314 VDAgentClipboard *clipboard;
315 uint32_t size = sizeof(*clipboard) + header->size;
317 clipboard = calloc(1, size);
318 if (!clipboard) {
319 fprintf(stderr,
320 "out of memory allocating clipboard (write)\n");
321 return;
323 clipboard->type = header->opaque;
324 memcpy(clipboard->data, data, header->size);
326 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
327 VD_AGENT_CLIPBOARD, 0,
328 (uint8_t *)clipboard, size);
329 free(clipboard);
330 break;
332 case VDAGENTD_CLIPBOARD_RELEASE:
333 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
334 VD_AGENT_CLIPBOARD_RELEASE, 0, NULL, 0);
335 break;
338 return;
340 error:
341 if (header->type == VDAGENTD_CLIPBOARD_REQUEST) {
342 /* Let the agent know no answer is coming */
343 udscs_write(conn, VDAGENTD_CLIPBOARD_DATA,
344 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
348 /* When we open the vdagent virtio channel, the server automatically goes into
349 client mouse mode, so we can only have the channel open when we know the
350 active session resolution. This function checks that we have an agent in the
351 active session, and that it has told us its resolution. If these conditions
352 are met it sets the uinput tablet device's resolution and opens the virtio
353 channel (if it is not already open). If these conditions are not met, it
354 closes both. */
355 static void check_xorg_resolution(void) {
356 struct udscs_connection *conn = get_active_session_connection();
357 struct agent_data *agent_data;
359 if (conn && (agent_data = udscs_get_user_data(conn)) && agent_data->width){
360 /* FIXME objectify uinput and let it handle all this */
361 if (agent_data->width != uinput_width ||
362 agent_data->height != uinput_height) {
363 if (uinput_width)
364 uinput_close();
365 uinput_setup(uinput, agent_data->width, agent_data->height);
366 uinput_width = agent_data->width;
367 uinput_height = agent_data->height;
369 if (!virtio_port) {
370 fprintf(stderr, "opening vdagent virtio channel\n");
371 virtio_port = vdagent_virtio_port_create(portdev,
372 virtio_port_read_complete,
373 NULL);
374 if (!virtio_port)
375 exit(1);
377 send_capabilities(virtio_port, 1);
379 } else {
380 if (uinput_width) {
381 uinput_close();
382 uinput_width = uinput_height = 0;
384 vdagent_virtio_port_destroy(&virtio_port);
385 fprintf(stderr, "closed vdagent virtio channel\n");
389 void agent_connect(struct udscs_connection *conn)
391 uint32_t pid;
392 struct agent_data *agent_data;
394 agent_data = calloc(1, sizeof(*agent_data));
395 if (!agent_data) {
396 fprintf(stderr, "Out of memory allocating agent data, disconnecting\n");
397 udscs_destroy_connection(&conn);
398 return;
401 pid = udscs_get_peer_cred(conn).pid;
402 agent_data->session = console_kit_session_for_pid(console_kit, pid);
403 udscs_set_user_data(conn, (void *)agent_data);
405 if (mon_config)
406 udscs_write(conn, VDAGENTD_MONITORS_CONFIG, 0, (uint8_t *)mon_config,
407 sizeof(VDAgentMonitorsConfig) +
408 mon_config->num_of_monitors * sizeof(VDAgentMonConfig));
411 void agent_disconnect(struct udscs_connection *conn)
413 struct agent_data *agent_data = udscs_get_user_data(conn);
415 free(agent_data->session);
416 free(agent_data);
419 void agent_read_complete(struct udscs_connection **connp,
420 struct udscs_message_header *header, const uint8_t *data)
422 struct agent_data *agent_data = udscs_get_user_data(*connp);
424 switch (header->type) {
425 case VDAGENTD_GUEST_XORG_RESOLUTION: {
426 struct vdagentd_guest_xorg_resolution *res =
427 (struct vdagentd_guest_xorg_resolution *)data;
429 if (header->size != sizeof(*res)) {
430 fprintf(stderr,
431 "guest xorg resolution message has wrong size, disconnecting agent\n");
432 udscs_destroy_connection(connp);
433 return;
436 agent_data->width = res->width;
437 agent_data->height = res->height;
438 check_xorg_resolution();
439 break;
441 case VDAGENTD_CLIPBOARD_GRAB:
442 case VDAGENTD_CLIPBOARD_REQUEST:
443 case VDAGENTD_CLIPBOARD_DATA:
444 case VDAGENTD_CLIPBOARD_RELEASE:
445 do_agent_clipboard(*connp, header, data);
446 break;
447 default:
448 fprintf(stderr, "unknown message from vdagent: %u, ignoring\n",
449 header->type);
453 /* main */
455 static void usage(FILE *fp)
457 fprintf(fp,
458 "vdagentd\n"
459 "options:\n"
460 " -h print this text\n"
461 " -d print debug messages (and don't daemonize)\n"
462 " -s <port> set virtio serial port [%s]\n"
463 " -u <dev> set uinput device [%s]\n",
464 portdev, uinput);
467 void daemonize(void)
469 /* detach from terminal */
470 switch (fork()) {
471 case -1:
472 perror("fork");
473 exit(1);
474 case 0:
475 close(0); close(1); close(2);
476 setsid();
477 open("/dev/null",O_RDWR); dup(0); dup(0);
478 break;
479 default:
480 exit(0);
484 void main_loop(void)
486 fd_set readfds, writefds;
487 int n, nfds, ck_fd = 0;
489 /* FIXME catch sigquit and set a flag to quit */
490 for (;;) {
491 FD_ZERO(&readfds);
492 FD_ZERO(&writefds);
494 nfds = udscs_server_fill_fds(server, &readfds, &writefds);
495 n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds);
496 if (n >= nfds)
497 nfds = n + 1;
498 if (console_kit) {
499 ck_fd = console_kit_get_fd(console_kit);
500 FD_SET(ck_fd, &readfds);
501 if (ck_fd >= nfds)
502 nfds = ck_fd + 1;
505 n = select(nfds, &readfds, &writefds, NULL, NULL);
506 if (n == -1) {
507 if (errno == EINTR)
508 continue;
509 perror("select");
510 exit(1);
513 udscs_server_handle_fds(server, &readfds, &writefds);
514 vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds);
515 if (FD_ISSET(ck_fd, &readfds))
516 check_xorg_resolution();
520 int main(int argc, char *argv[])
522 int c;
524 for (;;) {
525 if (-1 == (c = getopt(argc, argv, "dhx:y:s:u:")))
526 break;
527 switch (c) {
528 case 'd':
529 debug++;
530 break;
531 case 's':
532 portdev = optarg;
533 break;
534 case 'u':
535 uinput = optarg;
536 break;
537 case 'h':
538 usage(stdout);
539 exit(0);
540 default:
541 usage(stderr);
542 exit(1);
546 /* Setup communication with vdagent process(es) */
547 server = udscs_create_server(VDAGENTD_SOCKET, agent_connect,
548 agent_read_complete, agent_disconnect,
549 vdagentd_messages, VDAGENTD_NO_MESSAGES,
550 debug? stderr:NULL, stderr);
551 if (!server)
552 exit(1);
553 if (chmod(VDAGENTD_SOCKET, 0666)) {
554 fprintf(stderr, "could not change permissions on %s: %s\n",
555 VDAGENTD_SOCKET, strerror(errno));
558 console_kit = console_kit_create(stderr);
559 if (!console_kit)
560 fprintf(stderr, "Could not connect to console kit, disabling copy and paste support\n");
562 if (!debug)
563 daemonize();
565 main_loop();
567 udscs_destroy_server(server);
569 return 0;