Add VD_AGENT_CAP_MAX_CLIPBOARD support
[vd_agent.git] / src / vdagentd.c
blob1098fb62e8eddc5635f1909ac5dff45776ca1a78
1 /* vdagentd.c vdagentd (daemon) code
3 Copyright 2010-2013 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/>.
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include <signal.h>
33 #include <syslog.h>
34 #include <sys/select.h>
35 #include <sys/stat.h>
36 #include <spice/vd_agent.h>
37 #include <glib.h>
39 #include "udscs.h"
40 #include "vdagentd-proto.h"
41 #include "vdagentd-proto-strings.h"
42 #include "vdagentd-uinput.h"
43 #include "vdagentd-xorg-conf.h"
44 #include "vdagent-virtio-port.h"
45 #include "session-info.h"
47 struct agent_data {
48 char *session;
49 int width;
50 int height;
51 struct vdagentd_guest_xorg_resolution *screen_info;
52 int screen_count;
55 /* variables */
56 static const char *pidfilename = "/var/run/spice-vdagentd/spice-vdagentd.pid";
57 static const char *portdev = "/dev/virtio-ports/com.redhat.spice.0";
58 static const char *vdagentd_socket = VDAGENTD_SOCKET;
59 static const char *uinput_device = "/dev/uinput";
60 static int debug = 0;
61 static int uinput_fake = 0;
62 static struct udscs_server *server = NULL;
63 static struct vdagent_virtio_port *virtio_port = NULL;
64 static GHashTable *active_xfers = NULL;
65 static struct session_info *session_info = NULL;
66 static struct vdagentd_uinput *uinput = NULL;
67 static VDAgentMonitorsConfig *mon_config = NULL;
68 static uint32_t *capabilities = NULL;
69 static int capabilities_size = 0;
70 static const char *active_session = NULL;
71 static unsigned int session_count = 0;
72 static struct udscs_connection *active_session_conn = NULL;
73 static int agent_owns_clipboard[256] = { 0, };
74 static int quit = 0;
75 static int retval = 0;
76 static int client_connected = 0;
77 static int max_clipboard = -1;
79 /* utility functions */
80 /* vdagentd <-> spice-client communication handling */
81 static void send_capabilities(struct vdagent_virtio_port *vport,
82 uint32_t request)
84 VDAgentAnnounceCapabilities *caps;
85 uint32_t size;
87 size = sizeof(*caps) + VD_AGENT_CAPS_BYTES;
88 caps = calloc(1, size);
89 if (!caps) {
90 syslog(LOG_ERR, "out of memory allocating capabilities array (write)");
91 return;
94 caps->request = request;
95 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MOUSE_STATE);
96 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MONITORS_CONFIG);
97 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_REPLY);
98 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND);
99 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_SELECTION);
100 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_SPARSE_MONITORS_CONFIG);
101 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_GUEST_LINEEND_LF);
102 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MAX_CLIPBOARD);
104 vdagent_virtio_port_write(vport, VDP_CLIENT_PORT,
105 VD_AGENT_ANNOUNCE_CAPABILITIES, 0,
106 (uint8_t *)caps, size);
107 free(caps);
110 static void do_client_disconnect(void)
112 if (client_connected) {
113 udscs_server_write_all(server, VDAGENTD_CLIENT_DISCONNECTED, 0, 0,
114 NULL, 0);
115 client_connected = 0;
119 static void do_client_monitors(struct vdagent_virtio_port *vport, 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 syslog(LOG_ERR, "invalid message size for VDAgentMonitorsConfig");
130 return;
133 vdagentd_write_xorg_conf(new_monitors);
135 if (!mon_config ||
136 mon_config->num_of_monitors != new_monitors->num_of_monitors) {
137 free(mon_config);
138 mon_config = malloc(size);
139 if (!mon_config) {
140 syslog(LOG_ERR, "out of memory allocating monitors config");
141 return;
144 memcpy(mon_config, new_monitors, size);
146 /* Send monitor config to currently active agent */
147 if (active_session_conn)
148 udscs_write(active_session_conn, VDAGENTD_MONITORS_CONFIG, 0, 0,
149 (uint8_t *)mon_config, size);
151 /* Acknowledge reception of monitors config to spice server / client */
152 reply.type = VD_AGENT_MONITORS_CONFIG;
153 reply.error = VD_AGENT_SUCCESS;
154 vdagent_virtio_port_write(vport, port_nr, VD_AGENT_REPLY, 0,
155 (uint8_t *)&reply, sizeof(reply));
158 static void do_client_capabilities(struct vdagent_virtio_port *vport,
159 VDAgentMessage *message_header,
160 VDAgentAnnounceCapabilities *caps)
162 int new_size = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header->size);
164 if (capabilities_size != new_size) {
165 capabilities_size = new_size;
166 free(capabilities);
167 capabilities = malloc(capabilities_size * sizeof(uint32_t));
168 if (!capabilities) {
169 syslog(LOG_ERR, "oom allocating capabilities array (read)");
170 capabilities_size = 0;
171 return;
174 memcpy(capabilities, caps->caps, capabilities_size * sizeof(uint32_t));
175 if (caps->request) {
176 /* Report the previous client has disconneced. */
177 do_client_disconnect();
178 if (debug)
179 syslog(LOG_DEBUG, "New client connected");
180 client_connected = 1;
181 send_capabilities(vport, 0);
185 static void do_client_clipboard(struct vdagent_virtio_port *vport,
186 VDAgentMessage *message_header, uint8_t *data)
188 uint32_t msg_type = 0, data_type = 0, size = message_header->size;
189 uint8_t selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;
191 if (!active_session_conn) {
192 syslog(LOG_WARNING,
193 "Could not find an agent connection belonging to the "
194 "active session, ignoring client clipboard request");
195 return;
198 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
199 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
200 selection = data[0];
201 data += 4;
202 size -= 4;
205 switch (message_header->type) {
206 case VD_AGENT_CLIPBOARD_GRAB:
207 msg_type = VDAGENTD_CLIPBOARD_GRAB;
208 agent_owns_clipboard[selection] = 0;
209 break;
210 case VD_AGENT_CLIPBOARD_REQUEST: {
211 VDAgentClipboardRequest *req = (VDAgentClipboardRequest *)data;
212 msg_type = VDAGENTD_CLIPBOARD_REQUEST;
213 data_type = req->type;
214 data = NULL;
215 size = 0;
216 break;
218 case VD_AGENT_CLIPBOARD: {
219 VDAgentClipboard *clipboard = (VDAgentClipboard *)data;
220 msg_type = VDAGENTD_CLIPBOARD_DATA;
221 data_type = clipboard->type;
222 size = size - sizeof(VDAgentClipboard);
223 data = clipboard->data;
224 break;
226 case VD_AGENT_CLIPBOARD_RELEASE:
227 msg_type = VDAGENTD_CLIPBOARD_RELEASE;
228 data = NULL;
229 size = 0;
230 break;
233 udscs_write(active_session_conn, msg_type, selection, data_type,
234 data, size);
237 static void cancel_file_xfer(struct vdagent_virtio_port *vport,
238 const char *msg, uint32_t id)
240 VDAgentFileXferStatusMessage status = {
241 .id = id,
242 .result = VD_AGENT_FILE_XFER_STATUS_CANCELLED,
244 syslog(LOG_WARNING, msg, id);
245 if (vport)
246 vdagent_virtio_port_write(vport, VDP_CLIENT_PORT,
247 VD_AGENT_FILE_XFER_STATUS, 0,
248 (uint8_t *)&status, sizeof(status));
251 static void do_client_file_xfer(struct vdagent_virtio_port *vport,
252 VDAgentMessage *message_header,
253 uint8_t *data)
255 uint32_t msg_type, id;
256 struct udscs_connection *conn;
258 switch (message_header->type) {
259 case VD_AGENT_FILE_XFER_START: {
260 VDAgentFileXferStartMessage *s = (VDAgentFileXferStartMessage *)data;
261 if (!active_session_conn) {
262 cancel_file_xfer(vport,
263 "Could not find an agent connnection belonging to the "
264 "active session, cancelling client file-xfer request %u",
265 s->id);
266 return;
268 udscs_write(active_session_conn, VDAGENTD_FILE_XFER_START, 0, 0,
269 data, message_header->size);
270 return;
272 case VD_AGENT_FILE_XFER_STATUS: {
273 VDAgentFileXferStatusMessage *s = (VDAgentFileXferStatusMessage *)data;
274 msg_type = VDAGENTD_FILE_XFER_STATUS;
275 id = s->id;
276 break;
278 case VD_AGENT_FILE_XFER_DATA: {
279 VDAgentFileXferDataMessage *d = (VDAgentFileXferDataMessage *)data;
280 msg_type = VDAGENTD_FILE_XFER_DATA;
281 id = d->id;
282 break;
286 conn = g_hash_table_lookup(active_xfers, GUINT_TO_POINTER(id));
287 if (!conn) {
288 if (debug)
289 syslog(LOG_DEBUG, "Could not find file-xfer %u (cancelled?)", id);
290 return;
292 udscs_write(conn, msg_type, 0, 0, data, message_header->size);
295 int virtio_port_read_complete(
296 struct vdagent_virtio_port *vport,
297 int port_nr,
298 VDAgentMessage *message_header,
299 uint8_t *data)
301 uint32_t min_size = 0;
303 if (message_header->protocol != VD_AGENT_PROTOCOL) {
304 syslog(LOG_ERR, "message with wrong protocol version ignoring");
305 return 0;
308 switch (message_header->type) {
309 case VD_AGENT_MOUSE_STATE:
310 if (message_header->size != sizeof(VDAgentMouseState))
311 goto size_error;
312 vdagentd_uinput_do_mouse(&uinput, (VDAgentMouseState *)data);
313 if (!uinput) {
314 /* Try to re-open the tablet */
315 struct agent_data *agent_data =
316 udscs_get_user_data(active_session_conn);
317 if (agent_data)
318 uinput = vdagentd_uinput_create(uinput_device,
319 agent_data->width,
320 agent_data->height,
321 agent_data->screen_info,
322 agent_data->screen_count,
323 debug > 1,
324 uinput_fake);
325 if (!uinput) {
326 syslog(LOG_CRIT, "Fatal uinput error");
327 retval = 1;
328 quit = 1;
331 break;
332 case VD_AGENT_MONITORS_CONFIG:
333 if (message_header->size < sizeof(VDAgentMonitorsConfig))
334 goto size_error;
335 do_client_monitors(vport, port_nr, message_header,
336 (VDAgentMonitorsConfig *)data);
337 break;
338 case VD_AGENT_ANNOUNCE_CAPABILITIES:
339 if (message_header->size < sizeof(VDAgentAnnounceCapabilities))
340 goto size_error;
341 do_client_capabilities(vport, message_header,
342 (VDAgentAnnounceCapabilities *)data);
343 break;
344 case VD_AGENT_CLIPBOARD_GRAB:
345 case VD_AGENT_CLIPBOARD_REQUEST:
346 case VD_AGENT_CLIPBOARD:
347 case VD_AGENT_CLIPBOARD_RELEASE:
348 switch (message_header->type) {
349 case VD_AGENT_CLIPBOARD_GRAB:
350 min_size = sizeof(VDAgentClipboardGrab); break;
351 case VD_AGENT_CLIPBOARD_REQUEST:
352 min_size = sizeof(VDAgentClipboardRequest); break;
353 case VD_AGENT_CLIPBOARD:
354 min_size = sizeof(VDAgentClipboard); break;
356 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
357 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
358 min_size += 4;
360 if (message_header->size < min_size) {
361 goto size_error;
363 do_client_clipboard(vport, message_header, data);
364 break;
365 case VD_AGENT_FILE_XFER_START:
366 case VD_AGENT_FILE_XFER_STATUS:
367 case VD_AGENT_FILE_XFER_DATA:
368 do_client_file_xfer(vport, message_header, data);
369 break;
370 case VD_AGENT_CLIENT_DISCONNECTED:
371 vdagent_virtio_port_reset(vport, VDP_CLIENT_PORT);
372 do_client_disconnect();
373 break;
374 case VD_AGENT_MAX_CLIPBOARD:
375 if (message_header->size != sizeof(VDAgentMaxClipboard))
376 goto size_error;
377 VDAgentMaxClipboard *msg = (VDAgentMaxClipboard *)data;
378 syslog(LOG_DEBUG, "Set max clipboard: %d", msg->max);
379 max_clipboard = msg->max;
380 break;
381 default:
382 syslog(LOG_WARNING, "unknown message type %d, ignoring",
383 message_header->type);
386 return 0;
388 size_error:
389 syslog(LOG_ERR, "read: invalid message size: %u for message type: %u",
390 message_header->size, message_header->type);
391 return 0;
394 static void virtio_write_clipboard(uint8_t selection, uint32_t msg_type,
395 uint32_t data_type, const uint8_t *data, uint32_t data_size)
397 uint32_t size = data_size;
399 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
400 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
401 size += 4;
403 if (data_type != -1) {
404 size += 4;
407 vdagent_virtio_port_write_start(virtio_port, VDP_CLIENT_PORT, msg_type,
408 0, size);
410 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
411 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
412 uint8_t sel[4] = { selection, 0, 0, 0 };
413 vdagent_virtio_port_write_append(virtio_port, sel, 4);
415 if (data_type != -1) {
416 vdagent_virtio_port_write_append(virtio_port, (uint8_t*)&data_type, 4);
419 vdagent_virtio_port_write_append(virtio_port, data, data_size);
422 /* vdagentd <-> vdagent communication handling */
423 int do_agent_clipboard(struct udscs_connection *conn,
424 struct udscs_message_header *header, const uint8_t *data)
426 uint8_t selection = header->arg1;
427 uint32_t msg_type = 0, data_type = -1, size = header->size;
429 if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
430 VD_AGENT_CAP_CLIPBOARD_BY_DEMAND))
431 goto error;
433 /* Check that this agent is from the currently active session */
434 if (conn != active_session_conn) {
435 if (debug)
436 syslog(LOG_DEBUG, "%p clipboard req from agent which is not in "
437 "the active session?", conn);
438 goto error;
441 if (!virtio_port) {
442 syslog(LOG_ERR, "Clipboard req from agent but no client connection");
443 goto error;
446 if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
447 VD_AGENT_CAP_CLIPBOARD_SELECTION) &&
448 selection != VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) {
449 goto error;
452 switch (header->type) {
453 case VDAGENTD_CLIPBOARD_GRAB:
454 msg_type = VD_AGENT_CLIPBOARD_GRAB;
455 agent_owns_clipboard[selection] = 1;
456 break;
457 case VDAGENTD_CLIPBOARD_REQUEST:
458 msg_type = VD_AGENT_CLIPBOARD_REQUEST;
459 data_type = header->arg2;
460 size = 0;
461 break;
462 case VDAGENTD_CLIPBOARD_DATA:
463 msg_type = VD_AGENT_CLIPBOARD;
464 data_type = header->arg2;
465 if (max_clipboard != -1 && size > max_clipboard) {
466 syslog(LOG_WARNING, "clipboard is too large (%d > %d), discarding",
467 size, max_clipboard);
468 virtio_write_clipboard(selection, msg_type, data_type, NULL, 0);
469 return 0;
471 break;
472 case VDAGENTD_CLIPBOARD_RELEASE:
473 msg_type = VD_AGENT_CLIPBOARD_RELEASE;
474 size = 0;
475 agent_owns_clipboard[selection] = 0;
476 break;
477 default:
478 syslog(LOG_WARNING, "unexpected clipboard message type");
479 goto error;
482 if (size != header->size) {
483 syslog(LOG_ERR,
484 "unexpected extra data in clipboard msg, disconnecting agent");
485 return -1;
488 virtio_write_clipboard(selection, msg_type, data_type, data, header->size);
490 return 0;
492 error:
493 if (header->type == VDAGENTD_CLIPBOARD_REQUEST) {
494 /* Let the agent know no answer is coming */
495 udscs_write(conn, VDAGENTD_CLIPBOARD_DATA,
496 selection, VD_AGENT_CLIPBOARD_NONE, NULL, 0);
498 return 0;
501 /* When we open the vdagent virtio channel, the server automatically goes into
502 client mouse mode, so we can only have the channel open when we know the
503 active session resolution. This function checks that we have an agent in the
504 active session, and that it has told us its resolution. If these conditions
505 are met it sets the uinput tablet device's resolution and opens the virtio
506 channel (if it is not already open). If these conditions are not met, it
507 closes both. */
508 static void check_xorg_resolution(void)
510 struct agent_data *agent_data = udscs_get_user_data(active_session_conn);
512 if (agent_data && agent_data->screen_info) {
513 if (!uinput)
514 uinput = vdagentd_uinput_create(uinput_device,
515 agent_data->width,
516 agent_data->height,
517 agent_data->screen_info,
518 agent_data->screen_count,
519 debug > 1,
520 uinput_fake);
521 else
522 vdagentd_uinput_update_size(&uinput,
523 agent_data->width,
524 agent_data->height,
525 agent_data->screen_info,
526 agent_data->screen_count);
527 if (!uinput) {
528 syslog(LOG_CRIT, "Fatal uinput error");
529 retval = 1;
530 quit = 1;
531 return;
534 if (!virtio_port) {
535 syslog(LOG_INFO, "opening vdagent virtio channel");
536 virtio_port = vdagent_virtio_port_create(portdev,
537 virtio_port_read_complete,
538 NULL);
539 if (!virtio_port) {
540 syslog(LOG_CRIT, "Fatal error opening vdagent virtio channel");
541 retval = 1;
542 quit = 1;
543 return;
545 send_capabilities(virtio_port, 1);
547 } else {
548 #ifndef WITH_STATIC_UINPUT
549 vdagentd_uinput_destroy(&uinput);
550 #endif
551 if (virtio_port) {
552 vdagent_virtio_port_flush(&virtio_port);
553 vdagent_virtio_port_destroy(&virtio_port);
554 syslog(LOG_INFO, "closed vdagent virtio channel");
559 static int connection_matches_active_session(struct udscs_connection **connp,
560 void *priv)
562 struct udscs_connection **conn_ret = (struct udscs_connection **)priv;
563 struct agent_data *agent_data = udscs_get_user_data(*connp);
565 /* Check if this connection matches the currently active session */
566 if (!agent_data->session || !active_session)
567 return 0;
568 if (strcmp(agent_data->session, active_session))
569 return 0;
571 *conn_ret = *connp;
572 return 1;
575 void release_clipboards(void)
577 uint8_t sel;
579 for (sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; ++sel) {
580 if (agent_owns_clipboard[sel] && virtio_port) {
581 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
582 VD_AGENT_CLIPBOARD_RELEASE, 0, &sel, 1);
584 agent_owns_clipboard[sel] = 0;
588 void update_active_session_connection(struct udscs_connection *new_conn)
590 if (session_info) {
591 new_conn = NULL;
592 if (!active_session)
593 active_session = session_info_get_active_session(session_info);
594 session_count = udscs_server_for_all_clients(server,
595 connection_matches_active_session,
596 (void*)&new_conn);
597 } else {
598 if (new_conn)
599 session_count++;
600 else
601 session_count--;
604 if (new_conn && session_count != 1) {
605 syslog(LOG_ERR, "multiple agents in one session, "
606 "disabling agent to avoid potential information leak");
607 new_conn = NULL;
610 if (new_conn == active_session_conn)
611 return;
613 active_session_conn = new_conn;
614 if (debug)
615 syslog(LOG_DEBUG, "%p is now the active session", new_conn);
616 if (active_session_conn && mon_config)
617 udscs_write(active_session_conn, VDAGENTD_MONITORS_CONFIG, 0, 0,
618 (uint8_t *)mon_config, sizeof(VDAgentMonitorsConfig) +
619 mon_config->num_of_monitors * sizeof(VDAgentMonConfig));
621 release_clipboards();
623 check_xorg_resolution();
626 gboolean remove_active_xfers(gpointer key, gpointer value, gpointer conn)
628 if (value == conn) {
629 cancel_file_xfer(virtio_port, "Agent disc; cancelling file-xfer %u",
630 GPOINTER_TO_UINT(key));
631 return 1;
632 } else
633 return 0;
636 void agent_connect(struct udscs_connection *conn)
638 struct agent_data *agent_data;
640 agent_data = calloc(1, sizeof(*agent_data));
641 if (!agent_data) {
642 syslog(LOG_ERR, "Out of memory allocating agent data, disconnecting");
643 udscs_destroy_connection(&conn);
644 return;
647 if (session_info) {
648 uint32_t pid = udscs_get_peer_cred(conn).pid;
649 agent_data->session = session_info_session_for_pid(session_info, pid);
652 udscs_set_user_data(conn, (void *)agent_data);
653 udscs_write(conn, VDAGENTD_VERSION, 0, 0,
654 (uint8_t *)VERSION, strlen(VERSION) + 1);
655 update_active_session_connection(conn);
658 void agent_disconnect(struct udscs_connection *conn)
660 struct agent_data *agent_data = udscs_get_user_data(conn);
662 g_hash_table_foreach_remove(active_xfers, remove_active_xfers, conn);
664 free(agent_data->session);
665 agent_data->session = NULL;
666 update_active_session_connection(NULL);
668 free(agent_data);
671 void agent_read_complete(struct udscs_connection **connp,
672 struct udscs_message_header *header, uint8_t *data)
674 struct agent_data *agent_data = udscs_get_user_data(*connp);
676 switch (header->type) {
677 case VDAGENTD_GUEST_XORG_RESOLUTION: {
678 struct vdagentd_guest_xorg_resolution *res;
679 int n = header->size / sizeof(*res);
681 /* Detect older version session agent, but don't disconnect, as
682 that stops it from getting the VDAGENTD_VERSION message, and then
683 it will never re-exec the new version... */
684 if (header->arg1 == 0 && header->arg2 == 0) {
685 syslog(LOG_INFO, "got old session agent xorg resolution message, "
686 "ignoring");
687 free(data);
688 return;
691 if (header->size != n * sizeof(*res)) {
692 syslog(LOG_ERR, "guest xorg resolution message has wrong size, "
693 "disconnecting agent");
694 udscs_destroy_connection(connp);
695 free(data);
696 return;
699 free(agent_data->screen_info);
700 res = malloc(n * sizeof(*res));
701 if (!res) {
702 syslog(LOG_ERR, "out of memory allocating screen info");
703 n = 0;
705 memcpy(res, data, n * sizeof(*res));
706 agent_data->width = header->arg1;
707 agent_data->height = header->arg2;
708 agent_data->screen_info = res;
709 agent_data->screen_count = n;
711 check_xorg_resolution();
712 break;
714 case VDAGENTD_CLIPBOARD_GRAB:
715 case VDAGENTD_CLIPBOARD_REQUEST:
716 case VDAGENTD_CLIPBOARD_DATA:
717 case VDAGENTD_CLIPBOARD_RELEASE:
718 if (do_agent_clipboard(*connp, header, data)) {
719 udscs_destroy_connection(connp);
720 free(data);
721 return;
723 break;
724 case VDAGENTD_FILE_XFER_STATUS:{
725 VDAgentFileXferStatusMessage status;
726 status.id = header->arg1;
727 status.result = header->arg2;
728 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
729 VD_AGENT_FILE_XFER_STATUS, 0,
730 (uint8_t *)&status, sizeof(status));
731 if (status.result == VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA)
732 g_hash_table_insert(active_xfers, GUINT_TO_POINTER(status.id),
733 *connp);
734 else
735 g_hash_table_remove(active_xfers, GUINT_TO_POINTER(status.id));
736 break;
739 default:
740 syslog(LOG_ERR, "unknown message from vdagent: %u, ignoring",
741 header->type);
743 free(data);
746 /* main */
748 static void usage(FILE *fp)
750 fprintf(fp,
751 "Usage: spice-vdagentd [OPTIONS]\n\n"
752 "Spice guest agent daemon, version %s.\n\n"
753 "Options:\n"
754 " -h print this text\n"
755 " -d log debug messages (use twice for extra info)\n"
756 " -s <port> set virtio serial port [%s]\n"
757 " -S <filename> set udcs socket [%s]\n"
758 " -u <dev> set uinput device [%s]\n"
759 " -x don't daemonize\n"
760 #ifdef HAVE_CONSOLE_KIT
761 " -X Disable console kit integration\n"
762 #endif
763 #ifdef HAVE_LIBSYSTEMD_LOGIN
764 " -X Disable systemd-logind integration\n"
765 #endif
766 ,VERSION, portdev, vdagentd_socket, uinput_device);
769 void daemonize(void)
771 int x;
772 FILE *pidfile;
774 /* detach from terminal */
775 switch (fork()) {
776 case 0:
777 close(0); close(1); close(2);
778 setsid();
779 x = open("/dev/null", O_RDWR); x = dup(x); x = dup(x);
780 pidfile = fopen(pidfilename, "w");
781 if (pidfile) {
782 fprintf(pidfile, "%d\n", (int)getpid());
783 fclose(pidfile);
785 break;
786 case -1:
787 syslog(LOG_ERR, "fork: %m");
788 retval = 1;
789 default:
790 udscs_destroy_server(server);
791 exit(retval);
795 void main_loop(void)
797 fd_set readfds, writefds;
798 int n, nfds;
799 int ck_fd = 0;
801 while (!quit) {
802 FD_ZERO(&readfds);
803 FD_ZERO(&writefds);
805 nfds = udscs_server_fill_fds(server, &readfds, &writefds);
806 n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds);
807 if (n >= nfds)
808 nfds = n + 1;
810 if (session_info) {
811 ck_fd = session_info_get_fd(session_info);
812 FD_SET(ck_fd, &readfds);
813 if (ck_fd >= nfds)
814 nfds = ck_fd + 1;
817 n = select(nfds, &readfds, &writefds, NULL, NULL);
818 if (n == -1) {
819 if (errno == EINTR)
820 continue;
821 syslog(LOG_CRIT, "Fatal error select: %m");
822 retval = 1;
823 break;
826 udscs_server_handle_fds(server, &readfds, &writefds);
828 if (virtio_port) {
829 vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds);
830 if (!virtio_port) {
831 int old_client_connected = client_connected;
832 syslog(LOG_CRIT,
833 "AIIEEE lost spice client connection, reconnecting");
834 virtio_port = vdagent_virtio_port_create(portdev,
835 virtio_port_read_complete,
836 NULL);
837 if (!virtio_port) {
838 syslog(LOG_CRIT,
839 "Fatal error opening vdagent virtio channel");
840 retval = 1;
841 break;
843 do_client_disconnect();
844 client_connected = old_client_connected;
848 if (session_info && FD_ISSET(ck_fd, &readfds)) {
849 active_session = session_info_get_active_session(session_info);
850 update_active_session_connection(NULL);
855 static void quit_handler(int sig)
857 quit = 1;
860 int main(int argc, char *argv[])
862 int c;
863 int do_daemonize = 1;
864 int want_session_info = 1;
865 struct sigaction act;
867 for (;;) {
868 if (-1 == (c = getopt(argc, argv, "-dhxXs:u:S:")))
869 break;
870 switch (c) {
871 case 'd':
872 debug++;
873 break;
874 case 's':
875 portdev = optarg;
876 break;
877 case 'S':
878 vdagentd_socket = optarg;
879 break;
880 case 'u':
881 uinput_device = optarg;
882 break;
883 case 'x':
884 do_daemonize = 0;
885 break;
886 case 'X':
887 want_session_info = 0;
888 break;
889 case 'h':
890 usage(stdout);
891 return 0;
892 default:
893 fputs("\n", stderr);
894 usage(stderr);
895 return 1;
899 if (strncmp(uinput_device, "/dev", 4) != 0) {
900 syslog(LOG_INFO, "using fake uinput");
901 uinput_fake = 1;
904 memset(&act, 0, sizeof(act));
905 act.sa_flags = SA_RESTART;
906 act.sa_handler = quit_handler;
907 sigaction(SIGINT, &act, NULL);
908 sigaction(SIGHUP, &act, NULL);
909 sigaction(SIGTERM, &act, NULL);
910 sigaction(SIGQUIT, &act, NULL);
912 openlog("spice-vdagentd", do_daemonize ? 0 : LOG_PERROR, LOG_USER);
914 /* Setup communication with vdagent process(es) */
915 server = udscs_create_server(vdagentd_socket, agent_connect,
916 agent_read_complete, agent_disconnect,
917 vdagentd_messages, VDAGENTD_NO_MESSAGES,
918 debug);
919 if (!server) {
920 syslog(LOG_CRIT, "Fatal could not create server socket %s",
921 vdagentd_socket);
922 return 1;
924 if (chmod(vdagentd_socket, 0666)) {
925 syslog(LOG_CRIT, "Fatal could not change permissions on %s: %m",
926 vdagentd_socket);
927 udscs_destroy_server(server);
928 return 1;
931 if (do_daemonize)
932 daemonize();
934 #ifdef WITH_STATIC_UINPUT
935 uinput = vdagentd_uinput_create(uinput_device, 1024, 768, NULL, 0,
936 debug > 1, uinput_fake);
937 if (!uinput) {
938 udscs_destroy_server(server);
939 return 1;
941 #endif
943 if (want_session_info)
944 session_info = session_info_create(debug);
945 if (!session_info)
946 syslog(LOG_WARNING, "no session info, max 1 session agent allowed");
948 active_xfers = g_hash_table_new(g_direct_hash, g_direct_equal);
949 main_loop();
951 release_clipboards();
953 vdagentd_uinput_destroy(&uinput);
954 vdagent_virtio_port_flush(&virtio_port);
955 vdagent_virtio_port_destroy(&virtio_port);
956 session_info_destroy(session_info);
957 udscs_destroy_server(server);
958 if (unlink(vdagentd_socket) != 0)
959 syslog(LOG_ERR, "unlink %s: %s", vdagentd_socket, strerror(errno));
960 syslog(LOG_INFO, "vdagentd quiting, returning status %d", retval);
962 if (do_daemonize)
963 unlink(pidfilename);
965 return retval;