Move mouse-specific handling out of virtio_port_read_complete
[vd_agent.git] / src / vdagentd / vdagentd.c
blobe934f3fbe6847b728679839e953486b7c83680c8
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 "uinput.h"
43 #include "xorg-conf.h"
44 #include "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 int only_once = 0;
63 static struct udscs_server *server = NULL;
64 static struct vdagent_virtio_port *virtio_port = NULL;
65 static GHashTable *active_xfers = NULL;
66 static struct session_info *session_info = NULL;
67 static struct vdagentd_uinput *uinput = NULL;
68 static VDAgentMonitorsConfig *mon_config = NULL;
69 static uint32_t *capabilities = NULL;
70 static int capabilities_size = 0;
71 static const char *active_session = NULL;
72 static unsigned int session_count = 0;
73 static struct udscs_connection *active_session_conn = NULL;
74 static int agent_owns_clipboard[256] = { 0, };
75 static int quit = 0;
76 static int retval = 0;
77 static int client_connected = 0;
78 static int max_clipboard = -1;
80 /* utility functions */
81 /* vdagentd <-> spice-client communication handling */
82 static void send_capabilities(struct vdagent_virtio_port *vport,
83 uint32_t request)
85 VDAgentAnnounceCapabilities *caps;
86 uint32_t size;
88 size = sizeof(*caps) + VD_AGENT_CAPS_BYTES;
89 caps = calloc(1, size);
90 if (!caps) {
91 syslog(LOG_ERR, "out of memory allocating capabilities array (write)");
92 return;
95 caps->request = request;
96 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MOUSE_STATE);
97 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MONITORS_CONFIG);
98 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_REPLY);
99 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND);
100 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_SELECTION);
101 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_SPARSE_MONITORS_CONFIG);
102 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_GUEST_LINEEND_LF);
103 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MAX_CLIPBOARD);
104 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_AUDIO_VOLUME_SYNC);
106 vdagent_virtio_port_write(vport, VDP_CLIENT_PORT,
107 VD_AGENT_ANNOUNCE_CAPABILITIES, 0,
108 (uint8_t *)caps, size);
109 free(caps);
112 static void do_client_disconnect(void)
114 if (client_connected) {
115 udscs_server_write_all(server, VDAGENTD_CLIENT_DISCONNECTED, 0, 0,
116 NULL, 0);
117 client_connected = 0;
121 void do_client_mouse(struct vdagentd_uinput **uinputp, VDAgentMouseState *mouse)
123 vdagentd_uinput_do_mouse(uinputp, mouse);
124 if (!*uinputp) {
125 /* Try to re-open the tablet */
126 struct agent_data *agent_data =
127 udscs_get_user_data(active_session_conn);
128 if (agent_data)
129 *uinputp = vdagentd_uinput_create(uinput_device,
130 agent_data->width,
131 agent_data->height,
132 agent_data->screen_info,
133 agent_data->screen_count,
134 debug > 1,
135 uinput_fake);
136 if (!*uinputp) {
137 syslog(LOG_CRIT, "Fatal uinput error");
138 retval = 1;
139 quit = 1;
144 static void do_client_monitors(struct vdagent_virtio_port *vport, int port_nr,
145 VDAgentMessage *message_header, VDAgentMonitorsConfig *new_monitors)
147 VDAgentReply reply;
148 uint32_t size;
150 /* Store monitor config to send to agents when they connect */
151 size = sizeof(VDAgentMonitorsConfig) +
152 new_monitors->num_of_monitors * sizeof(VDAgentMonConfig);
153 if (message_header->size != size) {
154 syslog(LOG_ERR, "invalid message size for VDAgentMonitorsConfig");
155 return;
158 vdagentd_write_xorg_conf(new_monitors);
160 if (!mon_config ||
161 mon_config->num_of_monitors != new_monitors->num_of_monitors) {
162 free(mon_config);
163 mon_config = malloc(size);
164 if (!mon_config) {
165 syslog(LOG_ERR, "out of memory allocating monitors config");
166 return;
169 memcpy(mon_config, new_monitors, size);
171 /* Send monitor config to currently active agent */
172 if (active_session_conn)
173 udscs_write(active_session_conn, VDAGENTD_MONITORS_CONFIG, 0, 0,
174 (uint8_t *)mon_config, size);
176 /* Acknowledge reception of monitors config to spice server / client */
177 reply.type = VD_AGENT_MONITORS_CONFIG;
178 reply.error = VD_AGENT_SUCCESS;
179 vdagent_virtio_port_write(vport, port_nr, VD_AGENT_REPLY, 0,
180 (uint8_t *)&reply, sizeof(reply));
183 static void do_client_volume_sync(struct vdagent_virtio_port *vport, int port_nr,
184 VDAgentMessage *message_header,
185 VDAgentAudioVolumeSync *avs)
187 if (active_session_conn == NULL) {
188 syslog(LOG_DEBUG, "No active session - Can't volume-sync");
189 return;
192 udscs_write(active_session_conn, VDAGENTD_AUDIO_VOLUME_SYNC, 0, 0,
193 (uint8_t *)avs, message_header->size);
196 static void do_client_capabilities(struct vdagent_virtio_port *vport,
197 VDAgentMessage *message_header,
198 VDAgentAnnounceCapabilities *caps)
200 int new_size = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header->size);
202 if (capabilities_size != new_size) {
203 capabilities_size = new_size;
204 free(capabilities);
205 capabilities = malloc(capabilities_size * sizeof(uint32_t));
206 if (!capabilities) {
207 syslog(LOG_ERR, "oom allocating capabilities array (read)");
208 capabilities_size = 0;
209 return;
212 memcpy(capabilities, caps->caps, capabilities_size * sizeof(uint32_t));
213 if (caps->request) {
214 /* Report the previous client has disconneced. */
215 do_client_disconnect();
216 if (debug)
217 syslog(LOG_DEBUG, "New client connected");
218 client_connected = 1;
219 send_capabilities(vport, 0);
223 static void do_client_clipboard(struct vdagent_virtio_port *vport,
224 VDAgentMessage *message_header, uint8_t *data)
226 uint32_t msg_type = 0, data_type = 0, size = message_header->size;
227 uint8_t selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;
229 if (!active_session_conn) {
230 syslog(LOG_WARNING,
231 "Could not find an agent connection belonging to the "
232 "active session, ignoring client clipboard request");
233 return;
236 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
237 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
238 selection = data[0];
239 data += 4;
240 size -= 4;
243 switch (message_header->type) {
244 case VD_AGENT_CLIPBOARD_GRAB:
245 msg_type = VDAGENTD_CLIPBOARD_GRAB;
246 agent_owns_clipboard[selection] = 0;
247 break;
248 case VD_AGENT_CLIPBOARD_REQUEST: {
249 VDAgentClipboardRequest *req = (VDAgentClipboardRequest *)data;
250 msg_type = VDAGENTD_CLIPBOARD_REQUEST;
251 data_type = req->type;
252 data = NULL;
253 size = 0;
254 break;
256 case VD_AGENT_CLIPBOARD: {
257 VDAgentClipboard *clipboard = (VDAgentClipboard *)data;
258 msg_type = VDAGENTD_CLIPBOARD_DATA;
259 data_type = clipboard->type;
260 size = size - sizeof(VDAgentClipboard);
261 data = clipboard->data;
262 break;
264 case VD_AGENT_CLIPBOARD_RELEASE:
265 msg_type = VDAGENTD_CLIPBOARD_RELEASE;
266 data = NULL;
267 size = 0;
268 break;
271 udscs_write(active_session_conn, msg_type, selection, data_type,
272 data, size);
275 /* To be used by vdagentd for failures in file-xfer such as when file-xfer was
276 * cancelled or an error happened */
277 static void send_file_xfer_status(struct vdagent_virtio_port *vport,
278 const char *msg, uint32_t id, uint32_t xfer_status)
280 VDAgentFileXferStatusMessage status = {
281 .id = id,
282 .result = xfer_status,
284 syslog(LOG_WARNING, msg, id);
285 if (vport)
286 vdagent_virtio_port_write(vport, VDP_CLIENT_PORT,
287 VD_AGENT_FILE_XFER_STATUS, 0,
288 (uint8_t *)&status, sizeof(status));
291 static void do_client_file_xfer(struct vdagent_virtio_port *vport,
292 VDAgentMessage *message_header,
293 uint8_t *data)
295 uint32_t msg_type, id;
296 struct udscs_connection *conn;
298 switch (message_header->type) {
299 case VD_AGENT_FILE_XFER_START: {
300 VDAgentFileXferStartMessage *s = (VDAgentFileXferStartMessage *)data;
301 if (!active_session_conn) {
302 send_file_xfer_status(vport,
303 "Could not find an agent connnection belonging to the "
304 "active session, cancelling client file-xfer request %u",
305 s->id, VD_AGENT_FILE_XFER_STATUS_CANCELLED);
306 return;
307 } else if (session_info_session_is_locked(session_info)) {
308 syslog(LOG_DEBUG, "Session is locked, skipping file-xfer-start");
309 send_file_xfer_status(vport,
310 "User's session is locked and cannot start file transfer. "
311 "Cancelling client file-xfer request %u",
312 s->id, VD_AGENT_FILE_XFER_STATUS_ERROR);
313 return;
315 udscs_write(active_session_conn, VDAGENTD_FILE_XFER_START, 0, 0,
316 data, message_header->size);
317 return;
319 case VD_AGENT_FILE_XFER_STATUS: {
320 VDAgentFileXferStatusMessage *s = (VDAgentFileXferStatusMessage *)data;
321 msg_type = VDAGENTD_FILE_XFER_STATUS;
322 id = s->id;
323 break;
325 case VD_AGENT_FILE_XFER_DATA: {
326 VDAgentFileXferDataMessage *d = (VDAgentFileXferDataMessage *)data;
327 msg_type = VDAGENTD_FILE_XFER_DATA;
328 id = d->id;
329 break;
333 conn = g_hash_table_lookup(active_xfers, GUINT_TO_POINTER(id));
334 if (!conn) {
335 if (debug)
336 syslog(LOG_DEBUG, "Could not find file-xfer %u (cancelled?)", id);
337 return;
339 udscs_write(conn, msg_type, 0, 0, data, message_header->size);
342 static int virtio_port_read_complete(
343 struct vdagent_virtio_port *vport,
344 int port_nr,
345 VDAgentMessage *message_header,
346 uint8_t *data)
348 uint32_t min_size = 0;
350 if (message_header->protocol != VD_AGENT_PROTOCOL) {
351 syslog(LOG_ERR, "message with wrong protocol version ignoring");
352 return 0;
355 switch (message_header->type) {
356 case VD_AGENT_MOUSE_STATE:
357 if (message_header->size != sizeof(VDAgentMouseState))
358 goto size_error;
359 do_client_mouse(&uinput, (VDAgentMouseState *)data);
360 break;
361 case VD_AGENT_MONITORS_CONFIG:
362 if (message_header->size < sizeof(VDAgentMonitorsConfig))
363 goto size_error;
364 do_client_monitors(vport, port_nr, message_header,
365 (VDAgentMonitorsConfig *)data);
366 break;
367 case VD_AGENT_ANNOUNCE_CAPABILITIES:
368 if (message_header->size < sizeof(VDAgentAnnounceCapabilities))
369 goto size_error;
370 do_client_capabilities(vport, message_header,
371 (VDAgentAnnounceCapabilities *)data);
372 break;
373 case VD_AGENT_CLIPBOARD_GRAB:
374 case VD_AGENT_CLIPBOARD_REQUEST:
375 case VD_AGENT_CLIPBOARD:
376 case VD_AGENT_CLIPBOARD_RELEASE:
377 switch (message_header->type) {
378 case VD_AGENT_CLIPBOARD_GRAB:
379 min_size = sizeof(VDAgentClipboardGrab); break;
380 case VD_AGENT_CLIPBOARD_REQUEST:
381 min_size = sizeof(VDAgentClipboardRequest); break;
382 case VD_AGENT_CLIPBOARD:
383 min_size = sizeof(VDAgentClipboard); break;
385 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
386 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
387 min_size += 4;
389 if (message_header->size < min_size) {
390 goto size_error;
392 do_client_clipboard(vport, message_header, data);
393 break;
394 case VD_AGENT_FILE_XFER_START:
395 case VD_AGENT_FILE_XFER_STATUS:
396 case VD_AGENT_FILE_XFER_DATA:
397 do_client_file_xfer(vport, message_header, data);
398 break;
399 case VD_AGENT_CLIENT_DISCONNECTED:
400 vdagent_virtio_port_reset(vport, VDP_CLIENT_PORT);
401 do_client_disconnect();
402 break;
403 case VD_AGENT_MAX_CLIPBOARD:
404 if (message_header->size != sizeof(VDAgentMaxClipboard))
405 goto size_error;
406 VDAgentMaxClipboard *msg = (VDAgentMaxClipboard *)data;
407 syslog(LOG_DEBUG, "Set max clipboard: %d", msg->max);
408 max_clipboard = msg->max;
409 break;
410 case VD_AGENT_AUDIO_VOLUME_SYNC:
411 if (message_header->size < sizeof(VDAgentAudioVolumeSync))
412 goto size_error;
414 do_client_volume_sync(vport, port_nr, message_header,
415 (VDAgentAudioVolumeSync *)data);
416 break;
417 default:
418 syslog(LOG_WARNING, "unknown message type %d, ignoring",
419 message_header->type);
422 return 0;
424 size_error:
425 syslog(LOG_ERR, "read: invalid message size: %u for message type: %u",
426 message_header->size, message_header->type);
427 return 0;
430 static void virtio_write_clipboard(uint8_t selection, uint32_t msg_type,
431 uint32_t data_type, const uint8_t *data, uint32_t data_size)
433 uint32_t size = data_size;
435 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
436 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
437 size += 4;
439 if (data_type != -1) {
440 size += 4;
443 vdagent_virtio_port_write_start(virtio_port, VDP_CLIENT_PORT, msg_type,
444 0, size);
446 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
447 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
448 uint8_t sel[4] = { selection, 0, 0, 0 };
449 vdagent_virtio_port_write_append(virtio_port, sel, 4);
451 if (data_type != -1) {
452 vdagent_virtio_port_write_append(virtio_port, (uint8_t*)&data_type, 4);
455 vdagent_virtio_port_write_append(virtio_port, data, data_size);
458 /* vdagentd <-> vdagent communication handling */
459 static int do_agent_clipboard(struct udscs_connection *conn,
460 struct udscs_message_header *header, const uint8_t *data)
462 uint8_t selection = header->arg1;
463 uint32_t msg_type = 0, data_type = -1, size = header->size;
465 if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
466 VD_AGENT_CAP_CLIPBOARD_BY_DEMAND))
467 goto error;
469 /* Check that this agent is from the currently active session */
470 if (conn != active_session_conn) {
471 if (debug)
472 syslog(LOG_DEBUG, "%p clipboard req from agent which is not in "
473 "the active session?", conn);
474 goto error;
477 if (!virtio_port) {
478 syslog(LOG_ERR, "Clipboard req from agent but no client connection");
479 goto error;
482 if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
483 VD_AGENT_CAP_CLIPBOARD_SELECTION) &&
484 selection != VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) {
485 goto error;
488 switch (header->type) {
489 case VDAGENTD_CLIPBOARD_GRAB:
490 msg_type = VD_AGENT_CLIPBOARD_GRAB;
491 agent_owns_clipboard[selection] = 1;
492 break;
493 case VDAGENTD_CLIPBOARD_REQUEST:
494 msg_type = VD_AGENT_CLIPBOARD_REQUEST;
495 data_type = header->arg2;
496 size = 0;
497 break;
498 case VDAGENTD_CLIPBOARD_DATA:
499 msg_type = VD_AGENT_CLIPBOARD;
500 data_type = header->arg2;
501 if (max_clipboard != -1 && size > max_clipboard) {
502 syslog(LOG_WARNING, "clipboard is too large (%d > %d), discarding",
503 size, max_clipboard);
504 virtio_write_clipboard(selection, msg_type, data_type, NULL, 0);
505 return 0;
507 break;
508 case VDAGENTD_CLIPBOARD_RELEASE:
509 msg_type = VD_AGENT_CLIPBOARD_RELEASE;
510 size = 0;
511 agent_owns_clipboard[selection] = 0;
512 break;
513 default:
514 syslog(LOG_WARNING, "unexpected clipboard message type");
515 goto error;
518 if (size != header->size) {
519 syslog(LOG_ERR,
520 "unexpected extra data in clipboard msg, disconnecting agent");
521 return -1;
524 virtio_write_clipboard(selection, msg_type, data_type, data, header->size);
526 return 0;
528 error:
529 if (header->type == VDAGENTD_CLIPBOARD_REQUEST) {
530 /* Let the agent know no answer is coming */
531 udscs_write(conn, VDAGENTD_CLIPBOARD_DATA,
532 selection, VD_AGENT_CLIPBOARD_NONE, NULL, 0);
534 return 0;
537 /* When we open the vdagent virtio channel, the server automatically goes into
538 client mouse mode, so we can only have the channel open when we know the
539 active session resolution. This function checks that we have an agent in the
540 active session, and that it has told us its resolution. If these conditions
541 are met it sets the uinput tablet device's resolution and opens the virtio
542 channel (if it is not already open). If these conditions are not met, it
543 closes both. */
544 static void check_xorg_resolution(void)
546 struct agent_data *agent_data = udscs_get_user_data(active_session_conn);
548 if (agent_data && agent_data->screen_info) {
549 if (!uinput)
550 uinput = vdagentd_uinput_create(uinput_device,
551 agent_data->width,
552 agent_data->height,
553 agent_data->screen_info,
554 agent_data->screen_count,
555 debug > 1,
556 uinput_fake);
557 else
558 vdagentd_uinput_update_size(&uinput,
559 agent_data->width,
560 agent_data->height,
561 agent_data->screen_info,
562 agent_data->screen_count);
563 if (!uinput) {
564 syslog(LOG_CRIT, "Fatal uinput error");
565 retval = 1;
566 quit = 1;
567 return;
570 if (!virtio_port) {
571 syslog(LOG_INFO, "opening vdagent virtio channel");
572 virtio_port = vdagent_virtio_port_create(portdev,
573 virtio_port_read_complete,
574 NULL);
575 if (!virtio_port) {
576 syslog(LOG_CRIT, "Fatal error opening vdagent virtio channel");
577 retval = 1;
578 quit = 1;
579 return;
581 send_capabilities(virtio_port, 1);
583 } else {
584 #ifndef WITH_STATIC_UINPUT
585 vdagentd_uinput_destroy(&uinput);
586 #endif
587 if (virtio_port) {
588 vdagent_virtio_port_flush(&virtio_port);
589 vdagent_virtio_port_destroy(&virtio_port);
590 syslog(LOG_INFO, "closed vdagent virtio channel");
595 static int connection_matches_active_session(struct udscs_connection **connp,
596 void *priv)
598 struct udscs_connection **conn_ret = (struct udscs_connection **)priv;
599 struct agent_data *agent_data = udscs_get_user_data(*connp);
601 /* Check if this connection matches the currently active session */
602 if (!agent_data->session || !active_session)
603 return 0;
604 if (strcmp(agent_data->session, active_session))
605 return 0;
607 *conn_ret = *connp;
608 return 1;
611 static void release_clipboards(void)
613 uint8_t sel;
615 for (sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; ++sel) {
616 if (agent_owns_clipboard[sel] && virtio_port) {
617 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
618 VD_AGENT_CLIPBOARD_RELEASE, 0, &sel, 1);
620 agent_owns_clipboard[sel] = 0;
624 static void update_active_session_connection(struct udscs_connection *new_conn)
626 if (session_info) {
627 new_conn = NULL;
628 if (!active_session)
629 active_session = session_info_get_active_session(session_info);
630 session_count = udscs_server_for_all_clients(server,
631 connection_matches_active_session,
632 (void*)&new_conn);
633 } else {
634 if (new_conn)
635 session_count++;
636 else
637 session_count--;
640 if (new_conn && session_count != 1) {
641 syslog(LOG_ERR, "multiple agents in one session, "
642 "disabling agent to avoid potential information leak");
643 new_conn = NULL;
646 if (new_conn == active_session_conn)
647 return;
649 active_session_conn = new_conn;
650 if (debug)
651 syslog(LOG_DEBUG, "%p is now the active session", new_conn);
653 if (active_session_conn && !session_info_is_user(session_info)) {
654 if (debug)
655 syslog(LOG_DEBUG, "New session agent does not belong to user: "
656 "disabling file-xfer");
657 udscs_write(active_session_conn, VDAGENTD_FILE_XFER_DISABLE, 0, 0,
658 NULL, 0);
661 if (active_session_conn && mon_config)
662 udscs_write(active_session_conn, VDAGENTD_MONITORS_CONFIG, 0, 0,
663 (uint8_t *)mon_config, sizeof(VDAgentMonitorsConfig) +
664 mon_config->num_of_monitors * sizeof(VDAgentMonConfig));
666 release_clipboards();
668 check_xorg_resolution();
671 static gboolean remove_active_xfers(gpointer key, gpointer value, gpointer conn)
673 if (value == conn) {
674 send_file_xfer_status(virtio_port,
675 "Agent disc; cancelling file-xfer %u",
676 GPOINTER_TO_UINT(key),
677 VD_AGENT_FILE_XFER_STATUS_CANCELLED);
678 return 1;
679 } else
680 return 0;
683 static void agent_connect(struct udscs_connection *conn)
685 struct agent_data *agent_data;
687 agent_data = calloc(1, sizeof(*agent_data));
688 if (!agent_data) {
689 syslog(LOG_ERR, "Out of memory allocating agent data, disconnecting");
690 udscs_destroy_connection(&conn);
691 return;
694 if (session_info) {
695 uint32_t pid = udscs_get_peer_cred(conn).pid;
696 agent_data->session = session_info_session_for_pid(session_info, pid);
699 udscs_set_user_data(conn, (void *)agent_data);
700 udscs_write(conn, VDAGENTD_VERSION, 0, 0,
701 (uint8_t *)VERSION, strlen(VERSION) + 1);
702 update_active_session_connection(conn);
705 static void agent_disconnect(struct udscs_connection *conn)
707 struct agent_data *agent_data = udscs_get_user_data(conn);
709 g_hash_table_foreach_remove(active_xfers, remove_active_xfers, conn);
711 free(agent_data->session);
712 agent_data->session = NULL;
713 update_active_session_connection(NULL);
715 free(agent_data->screen_info);
716 free(agent_data);
719 static void agent_read_complete(struct udscs_connection **connp,
720 struct udscs_message_header *header, uint8_t *data)
722 struct agent_data *agent_data = udscs_get_user_data(*connp);
724 switch (header->type) {
725 case VDAGENTD_GUEST_XORG_RESOLUTION: {
726 struct vdagentd_guest_xorg_resolution *res;
727 int n = header->size / sizeof(*res);
729 /* Detect older version session agent, but don't disconnect, as
730 that stops it from getting the VDAGENTD_VERSION message, and then
731 it will never re-exec the new version... */
732 if (header->arg1 == 0 && header->arg2 == 0) {
733 syslog(LOG_INFO, "got old session agent xorg resolution message, "
734 "ignoring");
735 return;
738 if (header->size != n * sizeof(*res)) {
739 syslog(LOG_ERR, "guest xorg resolution message has wrong size, "
740 "disconnecting agent");
741 udscs_destroy_connection(connp);
742 return;
745 free(agent_data->screen_info);
746 res = malloc(n * sizeof(*res));
747 if (!res) {
748 syslog(LOG_ERR, "out of memory allocating screen info");
749 n = 0;
751 memcpy(res, data, n * sizeof(*res));
752 agent_data->width = header->arg1;
753 agent_data->height = header->arg2;
754 agent_data->screen_info = res;
755 agent_data->screen_count = n;
757 check_xorg_resolution();
758 break;
760 case VDAGENTD_CLIPBOARD_GRAB:
761 case VDAGENTD_CLIPBOARD_REQUEST:
762 case VDAGENTD_CLIPBOARD_DATA:
763 case VDAGENTD_CLIPBOARD_RELEASE:
764 if (do_agent_clipboard(*connp, header, data)) {
765 udscs_destroy_connection(connp);
766 return;
768 break;
769 case VDAGENTD_FILE_XFER_STATUS:{
770 VDAgentFileXferStatusMessage status;
771 status.id = header->arg1;
772 status.result = header->arg2;
773 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
774 VD_AGENT_FILE_XFER_STATUS, 0,
775 (uint8_t *)&status, sizeof(status));
776 if (status.result == VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA)
777 g_hash_table_insert(active_xfers, GUINT_TO_POINTER(status.id),
778 *connp);
779 else
780 g_hash_table_remove(active_xfers, GUINT_TO_POINTER(status.id));
781 break;
784 default:
785 syslog(LOG_ERR, "unknown message from vdagent: %u, ignoring",
786 header->type);
790 /* main */
792 static void usage(FILE *fp)
794 fprintf(fp,
795 "Usage: spice-vdagentd [OPTIONS]\n\n"
796 "Spice guest agent daemon, version %s.\n\n"
797 "Options:\n"
798 " -h print this text\n"
799 " -d log debug messages (use twice for extra info)\n"
800 " -s <port> set virtio serial port [%s]\n"
801 " -S <filename> set vdagent Unix domain socket [%s]\n"
802 " -u <dev> set uinput device [%s]\n"
803 " -f treat uinput device as fake; no ioctls\n"
804 " -x don't daemonize\n"
805 " -o only handle one virtio serial session\n"
806 #ifdef HAVE_CONSOLE_KIT
807 " -X disable console kit integration\n"
808 #endif
809 #ifdef HAVE_LIBSYSTEMD_LOGIN
810 " -X disable systemd-logind integration\n"
811 #endif
812 ,VERSION, portdev, vdagentd_socket, uinput_device);
815 static void daemonize(void)
817 int x;
818 FILE *pidfile;
820 /* detach from terminal */
821 switch (fork()) {
822 case 0:
823 close(0); close(1); close(2);
824 setsid();
825 x = open("/dev/null", O_RDWR); x = dup(x); x = dup(x);
826 pidfile = fopen(pidfilename, "w");
827 if (pidfile) {
828 fprintf(pidfile, "%d\n", (int)getpid());
829 fclose(pidfile);
831 break;
832 case -1:
833 syslog(LOG_ERR, "fork: %m");
834 retval = 1;
835 default:
836 udscs_destroy_server(server);
837 exit(retval);
841 static void main_loop(void)
843 fd_set readfds, writefds;
844 int n, nfds;
845 int ck_fd = 0;
846 int once = 0;
848 while (!quit) {
849 FD_ZERO(&readfds);
850 FD_ZERO(&writefds);
852 nfds = udscs_server_fill_fds(server, &readfds, &writefds);
853 n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds);
854 if (n >= nfds)
855 nfds = n + 1;
857 if (session_info) {
858 ck_fd = session_info_get_fd(session_info);
859 FD_SET(ck_fd, &readfds);
860 if (ck_fd >= nfds)
861 nfds = ck_fd + 1;
864 n = select(nfds, &readfds, &writefds, NULL, NULL);
865 if (n == -1) {
866 if (errno == EINTR)
867 continue;
868 syslog(LOG_CRIT, "Fatal error select: %m");
869 retval = 1;
870 break;
873 udscs_server_handle_fds(server, &readfds, &writefds);
875 if (virtio_port) {
876 once = 1;
877 vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds);
878 if (!virtio_port) {
879 int old_client_connected = client_connected;
880 syslog(LOG_CRIT,
881 "AIIEEE lost spice client connection, reconnecting");
882 virtio_port = vdagent_virtio_port_create(portdev,
883 virtio_port_read_complete,
884 NULL);
885 if (!virtio_port) {
886 syslog(LOG_CRIT,
887 "Fatal error opening vdagent virtio channel");
888 retval = 1;
889 break;
891 do_client_disconnect();
892 client_connected = old_client_connected;
895 else if (only_once && once)
897 syslog(LOG_INFO, "Exiting after one client session.");
898 break;
901 if (session_info && FD_ISSET(ck_fd, &readfds)) {
902 active_session = session_info_get_active_session(session_info);
903 update_active_session_connection(NULL);
908 static void quit_handler(int sig)
910 quit = 1;
913 int main(int argc, char *argv[])
915 int c;
916 int do_daemonize = 1;
917 int want_session_info = 1;
918 struct sigaction act;
920 for (;;) {
921 if (-1 == (c = getopt(argc, argv, "-dhxXfos:u:S:")))
922 break;
923 switch (c) {
924 case 'd':
925 debug++;
926 break;
927 case 's':
928 portdev = optarg;
929 break;
930 case 'S':
931 vdagentd_socket = optarg;
932 break;
933 case 'u':
934 uinput_device = optarg;
935 break;
936 case 'f':
937 uinput_fake = 1;
938 break;
939 case 'o':
940 only_once = 1;
941 break;
942 case 'x':
943 do_daemonize = 0;
944 break;
945 case 'X':
946 want_session_info = 0;
947 break;
948 case 'h':
949 usage(stdout);
950 return 0;
951 default:
952 fputs("\n", stderr);
953 usage(stderr);
954 return 1;
958 memset(&act, 0, sizeof(act));
959 act.sa_flags = SA_RESTART;
960 act.sa_handler = quit_handler;
961 sigaction(SIGINT, &act, NULL);
962 sigaction(SIGHUP, &act, NULL);
963 sigaction(SIGTERM, &act, NULL);
964 sigaction(SIGQUIT, &act, NULL);
966 openlog("spice-vdagentd", do_daemonize ? 0 : LOG_PERROR, LOG_USER);
968 /* Setup communication with vdagent process(es) */
969 server = udscs_create_server(vdagentd_socket, agent_connect,
970 agent_read_complete, agent_disconnect,
971 vdagentd_messages, VDAGENTD_NO_MESSAGES,
972 debug);
973 if (!server) {
974 if (errno == EADDRINUSE) {
975 syslog(LOG_CRIT, "Fatal the server socket %s exists already. Delete it?",
976 vdagentd_socket);
977 } else {
978 syslog(LOG_CRIT, "Fatal could not create the server socket %s: %m",
979 vdagentd_socket);
981 return 1;
983 if (chmod(vdagentd_socket, 0666)) {
984 syslog(LOG_CRIT, "Fatal could not change permissions on %s: %m",
985 vdagentd_socket);
986 udscs_destroy_server(server);
987 return 1;
990 if (do_daemonize)
991 daemonize();
993 #ifdef WITH_STATIC_UINPUT
994 uinput = vdagentd_uinput_create(uinput_device, 1024, 768, NULL, 0,
995 debug > 1, uinput_fake);
996 if (!uinput) {
997 udscs_destroy_server(server);
998 return 1;
1000 #endif
1002 if (want_session_info)
1003 session_info = session_info_create(debug);
1004 if (!session_info)
1005 syslog(LOG_WARNING, "no session info, max 1 session agent allowed");
1007 active_xfers = g_hash_table_new(g_direct_hash, g_direct_equal);
1008 main_loop();
1010 release_clipboards();
1012 vdagentd_uinput_destroy(&uinput);
1013 vdagent_virtio_port_flush(&virtio_port);
1014 vdagent_virtio_port_destroy(&virtio_port);
1015 session_info_destroy(session_info);
1016 udscs_destroy_server(server);
1017 if (unlink(vdagentd_socket) != 0)
1018 syslog(LOG_ERR, "unlink %s: %s", vdagentd_socket, strerror(errno));
1019 syslog(LOG_INFO, "vdagentd quiting, returning status %d", retval);
1021 if (do_daemonize)
1022 unlink(pidfilename);
1024 return retval;