vdagent: volume synchronization from client.
[vd_agent.git] / src / vdagentd.c
blob14c5be1a524fcdd253f3c54aac0c5121b91d1be3
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 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 static void do_client_monitors(struct vdagent_virtio_port *vport, int port_nr,
122 VDAgentMessage *message_header, VDAgentMonitorsConfig *new_monitors)
124 VDAgentReply reply;
125 uint32_t size;
127 /* Store monitor config to send to agents when they connect */
128 size = sizeof(VDAgentMonitorsConfig) +
129 new_monitors->num_of_monitors * sizeof(VDAgentMonConfig);
130 if (message_header->size != size) {
131 syslog(LOG_ERR, "invalid message size for VDAgentMonitorsConfig");
132 return;
135 vdagentd_write_xorg_conf(new_monitors);
137 if (!mon_config ||
138 mon_config->num_of_monitors != new_monitors->num_of_monitors) {
139 free(mon_config);
140 mon_config = malloc(size);
141 if (!mon_config) {
142 syslog(LOG_ERR, "out of memory allocating monitors config");
143 return;
146 memcpy(mon_config, new_monitors, size);
148 /* Send monitor config to currently active agent */
149 if (active_session_conn)
150 udscs_write(active_session_conn, VDAGENTD_MONITORS_CONFIG, 0, 0,
151 (uint8_t *)mon_config, size);
153 /* Acknowledge reception of monitors config to spice server / client */
154 reply.type = VD_AGENT_MONITORS_CONFIG;
155 reply.error = VD_AGENT_SUCCESS;
156 vdagent_virtio_port_write(vport, port_nr, VD_AGENT_REPLY, 0,
157 (uint8_t *)&reply, sizeof(reply));
160 static void do_client_volume_sync(struct vdagent_virtio_port *vport, int port_nr,
161 VDAgentMessage *message_header,
162 VDAgentAudioVolumeSync *avs)
164 if (active_session_conn == NULL) {
165 syslog(LOG_DEBUG, "No active session - Can't volume-sync");
166 return;
169 udscs_write(active_session_conn, VDAGENTD_AUDIO_VOLUME_SYNC, 0, 0,
170 (uint8_t *)avs, message_header->size);
173 static void do_client_capabilities(struct vdagent_virtio_port *vport,
174 VDAgentMessage *message_header,
175 VDAgentAnnounceCapabilities *caps)
177 int new_size = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header->size);
179 if (capabilities_size != new_size) {
180 capabilities_size = new_size;
181 free(capabilities);
182 capabilities = malloc(capabilities_size * sizeof(uint32_t));
183 if (!capabilities) {
184 syslog(LOG_ERR, "oom allocating capabilities array (read)");
185 capabilities_size = 0;
186 return;
189 memcpy(capabilities, caps->caps, capabilities_size * sizeof(uint32_t));
190 if (caps->request) {
191 /* Report the previous client has disconneced. */
192 do_client_disconnect();
193 if (debug)
194 syslog(LOG_DEBUG, "New client connected");
195 client_connected = 1;
196 send_capabilities(vport, 0);
200 static void do_client_clipboard(struct vdagent_virtio_port *vport,
201 VDAgentMessage *message_header, uint8_t *data)
203 uint32_t msg_type = 0, data_type = 0, size = message_header->size;
204 uint8_t selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;
206 if (!active_session_conn) {
207 syslog(LOG_WARNING,
208 "Could not find an agent connection belonging to the "
209 "active session, ignoring client clipboard request");
210 return;
213 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
214 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
215 selection = data[0];
216 data += 4;
217 size -= 4;
220 switch (message_header->type) {
221 case VD_AGENT_CLIPBOARD_GRAB:
222 msg_type = VDAGENTD_CLIPBOARD_GRAB;
223 agent_owns_clipboard[selection] = 0;
224 break;
225 case VD_AGENT_CLIPBOARD_REQUEST: {
226 VDAgentClipboardRequest *req = (VDAgentClipboardRequest *)data;
227 msg_type = VDAGENTD_CLIPBOARD_REQUEST;
228 data_type = req->type;
229 data = NULL;
230 size = 0;
231 break;
233 case VD_AGENT_CLIPBOARD: {
234 VDAgentClipboard *clipboard = (VDAgentClipboard *)data;
235 msg_type = VDAGENTD_CLIPBOARD_DATA;
236 data_type = clipboard->type;
237 size = size - sizeof(VDAgentClipboard);
238 data = clipboard->data;
239 break;
241 case VD_AGENT_CLIPBOARD_RELEASE:
242 msg_type = VDAGENTD_CLIPBOARD_RELEASE;
243 data = NULL;
244 size = 0;
245 break;
248 udscs_write(active_session_conn, msg_type, selection, data_type,
249 data, size);
252 static void cancel_file_xfer(struct vdagent_virtio_port *vport,
253 const char *msg, uint32_t id)
255 VDAgentFileXferStatusMessage status = {
256 .id = id,
257 .result = VD_AGENT_FILE_XFER_STATUS_CANCELLED,
259 syslog(LOG_WARNING, msg, id);
260 if (vport)
261 vdagent_virtio_port_write(vport, VDP_CLIENT_PORT,
262 VD_AGENT_FILE_XFER_STATUS, 0,
263 (uint8_t *)&status, sizeof(status));
266 static void do_client_file_xfer(struct vdagent_virtio_port *vport,
267 VDAgentMessage *message_header,
268 uint8_t *data)
270 uint32_t msg_type, id;
271 struct udscs_connection *conn;
273 switch (message_header->type) {
274 case VD_AGENT_FILE_XFER_START: {
275 VDAgentFileXferStartMessage *s = (VDAgentFileXferStartMessage *)data;
276 if (!active_session_conn) {
277 cancel_file_xfer(vport,
278 "Could not find an agent connnection belonging to the "
279 "active session, cancelling client file-xfer request %u",
280 s->id);
281 return;
283 udscs_write(active_session_conn, VDAGENTD_FILE_XFER_START, 0, 0,
284 data, message_header->size);
285 return;
287 case VD_AGENT_FILE_XFER_STATUS: {
288 VDAgentFileXferStatusMessage *s = (VDAgentFileXferStatusMessage *)data;
289 msg_type = VDAGENTD_FILE_XFER_STATUS;
290 id = s->id;
291 break;
293 case VD_AGENT_FILE_XFER_DATA: {
294 VDAgentFileXferDataMessage *d = (VDAgentFileXferDataMessage *)data;
295 msg_type = VDAGENTD_FILE_XFER_DATA;
296 id = d->id;
297 break;
301 conn = g_hash_table_lookup(active_xfers, GUINT_TO_POINTER(id));
302 if (!conn) {
303 if (debug)
304 syslog(LOG_DEBUG, "Could not find file-xfer %u (cancelled?)", id);
305 return;
307 udscs_write(conn, msg_type, 0, 0, data, message_header->size);
310 int virtio_port_read_complete(
311 struct vdagent_virtio_port *vport,
312 int port_nr,
313 VDAgentMessage *message_header,
314 uint8_t *data)
316 uint32_t min_size = 0;
318 if (message_header->protocol != VD_AGENT_PROTOCOL) {
319 syslog(LOG_ERR, "message with wrong protocol version ignoring");
320 return 0;
323 switch (message_header->type) {
324 case VD_AGENT_MOUSE_STATE:
325 if (message_header->size != sizeof(VDAgentMouseState))
326 goto size_error;
327 vdagentd_uinput_do_mouse(&uinput, (VDAgentMouseState *)data);
328 if (!uinput) {
329 /* Try to re-open the tablet */
330 struct agent_data *agent_data =
331 udscs_get_user_data(active_session_conn);
332 if (agent_data)
333 uinput = vdagentd_uinput_create(uinput_device,
334 agent_data->width,
335 agent_data->height,
336 agent_data->screen_info,
337 agent_data->screen_count,
338 debug > 1,
339 uinput_fake);
340 if (!uinput) {
341 syslog(LOG_CRIT, "Fatal uinput error");
342 retval = 1;
343 quit = 1;
346 break;
347 case VD_AGENT_MONITORS_CONFIG:
348 if (message_header->size < sizeof(VDAgentMonitorsConfig))
349 goto size_error;
350 do_client_monitors(vport, port_nr, message_header,
351 (VDAgentMonitorsConfig *)data);
352 break;
353 case VD_AGENT_ANNOUNCE_CAPABILITIES:
354 if (message_header->size < sizeof(VDAgentAnnounceCapabilities))
355 goto size_error;
356 do_client_capabilities(vport, message_header,
357 (VDAgentAnnounceCapabilities *)data);
358 break;
359 case VD_AGENT_CLIPBOARD_GRAB:
360 case VD_AGENT_CLIPBOARD_REQUEST:
361 case VD_AGENT_CLIPBOARD:
362 case VD_AGENT_CLIPBOARD_RELEASE:
363 switch (message_header->type) {
364 case VD_AGENT_CLIPBOARD_GRAB:
365 min_size = sizeof(VDAgentClipboardGrab); break;
366 case VD_AGENT_CLIPBOARD_REQUEST:
367 min_size = sizeof(VDAgentClipboardRequest); break;
368 case VD_AGENT_CLIPBOARD:
369 min_size = sizeof(VDAgentClipboard); break;
371 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
372 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
373 min_size += 4;
375 if (message_header->size < min_size) {
376 goto size_error;
378 do_client_clipboard(vport, message_header, data);
379 break;
380 case VD_AGENT_FILE_XFER_START:
381 case VD_AGENT_FILE_XFER_STATUS:
382 case VD_AGENT_FILE_XFER_DATA:
383 do_client_file_xfer(vport, message_header, data);
384 break;
385 case VD_AGENT_CLIENT_DISCONNECTED:
386 vdagent_virtio_port_reset(vport, VDP_CLIENT_PORT);
387 do_client_disconnect();
388 break;
389 case VD_AGENT_MAX_CLIPBOARD:
390 if (message_header->size != sizeof(VDAgentMaxClipboard))
391 goto size_error;
392 VDAgentMaxClipboard *msg = (VDAgentMaxClipboard *)data;
393 syslog(LOG_DEBUG, "Set max clipboard: %d", msg->max);
394 max_clipboard = msg->max;
395 break;
396 case VD_AGENT_AUDIO_VOLUME_SYNC:
397 if (message_header->size < sizeof(VDAgentAudioVolumeSync))
398 goto size_error;
400 do_client_volume_sync(vport, port_nr, message_header,
401 (VDAgentAudioVolumeSync *)data);
402 break;
403 default:
404 syslog(LOG_WARNING, "unknown message type %d, ignoring",
405 message_header->type);
408 return 0;
410 size_error:
411 syslog(LOG_ERR, "read: invalid message size: %u for message type: %u",
412 message_header->size, message_header->type);
413 return 0;
416 static void virtio_write_clipboard(uint8_t selection, uint32_t msg_type,
417 uint32_t data_type, const uint8_t *data, uint32_t data_size)
419 uint32_t size = data_size;
421 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
422 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
423 size += 4;
425 if (data_type != -1) {
426 size += 4;
429 vdagent_virtio_port_write_start(virtio_port, VDP_CLIENT_PORT, msg_type,
430 0, size);
432 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
433 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
434 uint8_t sel[4] = { selection, 0, 0, 0 };
435 vdagent_virtio_port_write_append(virtio_port, sel, 4);
437 if (data_type != -1) {
438 vdagent_virtio_port_write_append(virtio_port, (uint8_t*)&data_type, 4);
441 vdagent_virtio_port_write_append(virtio_port, data, data_size);
444 /* vdagentd <-> vdagent communication handling */
445 int do_agent_clipboard(struct udscs_connection *conn,
446 struct udscs_message_header *header, const uint8_t *data)
448 uint8_t selection = header->arg1;
449 uint32_t msg_type = 0, data_type = -1, size = header->size;
451 if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
452 VD_AGENT_CAP_CLIPBOARD_BY_DEMAND))
453 goto error;
455 /* Check that this agent is from the currently active session */
456 if (conn != active_session_conn) {
457 if (debug)
458 syslog(LOG_DEBUG, "%p clipboard req from agent which is not in "
459 "the active session?", conn);
460 goto error;
463 if (!virtio_port) {
464 syslog(LOG_ERR, "Clipboard req from agent but no client connection");
465 goto error;
468 if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
469 VD_AGENT_CAP_CLIPBOARD_SELECTION) &&
470 selection != VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) {
471 goto error;
474 switch (header->type) {
475 case VDAGENTD_CLIPBOARD_GRAB:
476 msg_type = VD_AGENT_CLIPBOARD_GRAB;
477 agent_owns_clipboard[selection] = 1;
478 break;
479 case VDAGENTD_CLIPBOARD_REQUEST:
480 msg_type = VD_AGENT_CLIPBOARD_REQUEST;
481 data_type = header->arg2;
482 size = 0;
483 break;
484 case VDAGENTD_CLIPBOARD_DATA:
485 msg_type = VD_AGENT_CLIPBOARD;
486 data_type = header->arg2;
487 if (max_clipboard != -1 && size > max_clipboard) {
488 syslog(LOG_WARNING, "clipboard is too large (%d > %d), discarding",
489 size, max_clipboard);
490 virtio_write_clipboard(selection, msg_type, data_type, NULL, 0);
491 return 0;
493 break;
494 case VDAGENTD_CLIPBOARD_RELEASE:
495 msg_type = VD_AGENT_CLIPBOARD_RELEASE;
496 size = 0;
497 agent_owns_clipboard[selection] = 0;
498 break;
499 default:
500 syslog(LOG_WARNING, "unexpected clipboard message type");
501 goto error;
504 if (size != header->size) {
505 syslog(LOG_ERR,
506 "unexpected extra data in clipboard msg, disconnecting agent");
507 return -1;
510 virtio_write_clipboard(selection, msg_type, data_type, data, header->size);
512 return 0;
514 error:
515 if (header->type == VDAGENTD_CLIPBOARD_REQUEST) {
516 /* Let the agent know no answer is coming */
517 udscs_write(conn, VDAGENTD_CLIPBOARD_DATA,
518 selection, VD_AGENT_CLIPBOARD_NONE, NULL, 0);
520 return 0;
523 /* When we open the vdagent virtio channel, the server automatically goes into
524 client mouse mode, so we can only have the channel open when we know the
525 active session resolution. This function checks that we have an agent in the
526 active session, and that it has told us its resolution. If these conditions
527 are met it sets the uinput tablet device's resolution and opens the virtio
528 channel (if it is not already open). If these conditions are not met, it
529 closes both. */
530 static void check_xorg_resolution(void)
532 struct agent_data *agent_data = udscs_get_user_data(active_session_conn);
534 if (agent_data && agent_data->screen_info) {
535 if (!uinput)
536 uinput = vdagentd_uinput_create(uinput_device,
537 agent_data->width,
538 agent_data->height,
539 agent_data->screen_info,
540 agent_data->screen_count,
541 debug > 1,
542 uinput_fake);
543 else
544 vdagentd_uinput_update_size(&uinput,
545 agent_data->width,
546 agent_data->height,
547 agent_data->screen_info,
548 agent_data->screen_count);
549 if (!uinput) {
550 syslog(LOG_CRIT, "Fatal uinput error");
551 retval = 1;
552 quit = 1;
553 return;
556 if (!virtio_port) {
557 syslog(LOG_INFO, "opening vdagent virtio channel");
558 virtio_port = vdagent_virtio_port_create(portdev,
559 virtio_port_read_complete,
560 NULL);
561 if (!virtio_port) {
562 syslog(LOG_CRIT, "Fatal error opening vdagent virtio channel");
563 retval = 1;
564 quit = 1;
565 return;
567 send_capabilities(virtio_port, 1);
569 } else {
570 #ifndef WITH_STATIC_UINPUT
571 vdagentd_uinput_destroy(&uinput);
572 #endif
573 if (virtio_port) {
574 vdagent_virtio_port_flush(&virtio_port);
575 vdagent_virtio_port_destroy(&virtio_port);
576 syslog(LOG_INFO, "closed vdagent virtio channel");
581 static int connection_matches_active_session(struct udscs_connection **connp,
582 void *priv)
584 struct udscs_connection **conn_ret = (struct udscs_connection **)priv;
585 struct agent_data *agent_data = udscs_get_user_data(*connp);
587 /* Check if this connection matches the currently active session */
588 if (!agent_data->session || !active_session)
589 return 0;
590 if (strcmp(agent_data->session, active_session))
591 return 0;
593 *conn_ret = *connp;
594 return 1;
597 void release_clipboards(void)
599 uint8_t sel;
601 for (sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; ++sel) {
602 if (agent_owns_clipboard[sel] && virtio_port) {
603 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
604 VD_AGENT_CLIPBOARD_RELEASE, 0, &sel, 1);
606 agent_owns_clipboard[sel] = 0;
610 void update_active_session_connection(struct udscs_connection *new_conn)
612 if (session_info) {
613 new_conn = NULL;
614 if (!active_session)
615 active_session = session_info_get_active_session(session_info);
616 session_count = udscs_server_for_all_clients(server,
617 connection_matches_active_session,
618 (void*)&new_conn);
619 } else {
620 if (new_conn)
621 session_count++;
622 else
623 session_count--;
626 if (new_conn && session_count != 1) {
627 syslog(LOG_ERR, "multiple agents in one session, "
628 "disabling agent to avoid potential information leak");
629 new_conn = NULL;
632 if (new_conn == active_session_conn)
633 return;
635 active_session_conn = new_conn;
636 if (debug)
637 syslog(LOG_DEBUG, "%p is now the active session", new_conn);
638 if (active_session_conn && mon_config)
639 udscs_write(active_session_conn, VDAGENTD_MONITORS_CONFIG, 0, 0,
640 (uint8_t *)mon_config, sizeof(VDAgentMonitorsConfig) +
641 mon_config->num_of_monitors * sizeof(VDAgentMonConfig));
643 release_clipboards();
645 check_xorg_resolution();
648 gboolean remove_active_xfers(gpointer key, gpointer value, gpointer conn)
650 if (value == conn) {
651 cancel_file_xfer(virtio_port, "Agent disc; cancelling file-xfer %u",
652 GPOINTER_TO_UINT(key));
653 return 1;
654 } else
655 return 0;
658 void agent_connect(struct udscs_connection *conn)
660 struct agent_data *agent_data;
662 agent_data = calloc(1, sizeof(*agent_data));
663 if (!agent_data) {
664 syslog(LOG_ERR, "Out of memory allocating agent data, disconnecting");
665 udscs_destroy_connection(&conn);
666 return;
669 if (session_info) {
670 uint32_t pid = udscs_get_peer_cred(conn).pid;
671 agent_data->session = session_info_session_for_pid(session_info, pid);
674 udscs_set_user_data(conn, (void *)agent_data);
675 udscs_write(conn, VDAGENTD_VERSION, 0, 0,
676 (uint8_t *)VERSION, strlen(VERSION) + 1);
677 update_active_session_connection(conn);
680 void agent_disconnect(struct udscs_connection *conn)
682 struct agent_data *agent_data = udscs_get_user_data(conn);
684 g_hash_table_foreach_remove(active_xfers, remove_active_xfers, conn);
686 free(agent_data->session);
687 agent_data->session = NULL;
688 update_active_session_connection(NULL);
690 free(agent_data);
693 void agent_read_complete(struct udscs_connection **connp,
694 struct udscs_message_header *header, uint8_t *data)
696 struct agent_data *agent_data = udscs_get_user_data(*connp);
698 switch (header->type) {
699 case VDAGENTD_GUEST_XORG_RESOLUTION: {
700 struct vdagentd_guest_xorg_resolution *res;
701 int n = header->size / sizeof(*res);
703 /* Detect older version session agent, but don't disconnect, as
704 that stops it from getting the VDAGENTD_VERSION message, and then
705 it will never re-exec the new version... */
706 if (header->arg1 == 0 && header->arg2 == 0) {
707 syslog(LOG_INFO, "got old session agent xorg resolution message, "
708 "ignoring");
709 free(data);
710 return;
713 if (header->size != n * sizeof(*res)) {
714 syslog(LOG_ERR, "guest xorg resolution message has wrong size, "
715 "disconnecting agent");
716 udscs_destroy_connection(connp);
717 free(data);
718 return;
721 free(agent_data->screen_info);
722 res = malloc(n * sizeof(*res));
723 if (!res) {
724 syslog(LOG_ERR, "out of memory allocating screen info");
725 n = 0;
727 memcpy(res, data, n * sizeof(*res));
728 agent_data->width = header->arg1;
729 agent_data->height = header->arg2;
730 agent_data->screen_info = res;
731 agent_data->screen_count = n;
733 check_xorg_resolution();
734 break;
736 case VDAGENTD_CLIPBOARD_GRAB:
737 case VDAGENTD_CLIPBOARD_REQUEST:
738 case VDAGENTD_CLIPBOARD_DATA:
739 case VDAGENTD_CLIPBOARD_RELEASE:
740 if (do_agent_clipboard(*connp, header, data)) {
741 udscs_destroy_connection(connp);
742 free(data);
743 return;
745 break;
746 case VDAGENTD_FILE_XFER_STATUS:{
747 VDAgentFileXferStatusMessage status;
748 status.id = header->arg1;
749 status.result = header->arg2;
750 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
751 VD_AGENT_FILE_XFER_STATUS, 0,
752 (uint8_t *)&status, sizeof(status));
753 if (status.result == VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA)
754 g_hash_table_insert(active_xfers, GUINT_TO_POINTER(status.id),
755 *connp);
756 else
757 g_hash_table_remove(active_xfers, GUINT_TO_POINTER(status.id));
758 break;
761 default:
762 syslog(LOG_ERR, "unknown message from vdagent: %u, ignoring",
763 header->type);
765 free(data);
768 /* main */
770 static void usage(FILE *fp)
772 fprintf(fp,
773 "Usage: spice-vdagentd [OPTIONS]\n\n"
774 "Spice guest agent daemon, version %s.\n\n"
775 "Options:\n"
776 " -h print this text\n"
777 " -d log debug messages (use twice for extra info)\n"
778 " -s <port> set virtio serial port [%s]\n"
779 " -S <filename> set udcs socket [%s]\n"
780 " -u <dev> set uinput device [%s]\n"
781 " -f treat uinput device as fake; no ioctls\n"
782 " -x don't daemonize\n"
783 " -o Only handle one virtio serial session.\n"
784 #ifdef HAVE_CONSOLE_KIT
785 " -X Disable console kit integration\n"
786 #endif
787 #ifdef HAVE_LIBSYSTEMD_LOGIN
788 " -X Disable systemd-logind integration\n"
789 #endif
790 ,VERSION, portdev, vdagentd_socket, uinput_device);
793 void daemonize(void)
795 int x;
796 FILE *pidfile;
798 /* detach from terminal */
799 switch (fork()) {
800 case 0:
801 close(0); close(1); close(2);
802 setsid();
803 x = open("/dev/null", O_RDWR); x = dup(x); x = dup(x);
804 pidfile = fopen(pidfilename, "w");
805 if (pidfile) {
806 fprintf(pidfile, "%d\n", (int)getpid());
807 fclose(pidfile);
809 break;
810 case -1:
811 syslog(LOG_ERR, "fork: %m");
812 retval = 1;
813 default:
814 udscs_destroy_server(server);
815 exit(retval);
819 void main_loop(void)
821 fd_set readfds, writefds;
822 int n, nfds;
823 int ck_fd = 0;
824 int once = 0;
826 while (!quit) {
827 FD_ZERO(&readfds);
828 FD_ZERO(&writefds);
830 nfds = udscs_server_fill_fds(server, &readfds, &writefds);
831 n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds);
832 if (n >= nfds)
833 nfds = n + 1;
835 if (session_info) {
836 ck_fd = session_info_get_fd(session_info);
837 FD_SET(ck_fd, &readfds);
838 if (ck_fd >= nfds)
839 nfds = ck_fd + 1;
842 n = select(nfds, &readfds, &writefds, NULL, NULL);
843 if (n == -1) {
844 if (errno == EINTR)
845 continue;
846 syslog(LOG_CRIT, "Fatal error select: %m");
847 retval = 1;
848 break;
851 udscs_server_handle_fds(server, &readfds, &writefds);
853 if (virtio_port) {
854 once = 1;
855 vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds);
856 if (!virtio_port) {
857 int old_client_connected = client_connected;
858 syslog(LOG_CRIT,
859 "AIIEEE lost spice client connection, reconnecting");
860 virtio_port = vdagent_virtio_port_create(portdev,
861 virtio_port_read_complete,
862 NULL);
863 if (!virtio_port) {
864 syslog(LOG_CRIT,
865 "Fatal error opening vdagent virtio channel");
866 retval = 1;
867 break;
869 do_client_disconnect();
870 client_connected = old_client_connected;
873 else if (only_once && once)
875 syslog(LOG_INFO, "Exiting after one client session.");
876 break;
879 if (session_info && FD_ISSET(ck_fd, &readfds)) {
880 active_session = session_info_get_active_session(session_info);
881 update_active_session_connection(NULL);
886 static void quit_handler(int sig)
888 quit = 1;
891 int main(int argc, char *argv[])
893 int c;
894 int do_daemonize = 1;
895 int want_session_info = 1;
896 struct sigaction act;
898 for (;;) {
899 if (-1 == (c = getopt(argc, argv, "-dhxXfos:u:S:")))
900 break;
901 switch (c) {
902 case 'd':
903 debug++;
904 break;
905 case 's':
906 portdev = optarg;
907 break;
908 case 'S':
909 vdagentd_socket = optarg;
910 break;
911 case 'u':
912 uinput_device = optarg;
913 break;
914 case 'f':
915 uinput_fake = 1;
916 break;
917 case 'o':
918 only_once = 1;
919 break;
920 case 'x':
921 do_daemonize = 0;
922 break;
923 case 'X':
924 want_session_info = 0;
925 break;
926 case 'h':
927 usage(stdout);
928 return 0;
929 default:
930 fputs("\n", stderr);
931 usage(stderr);
932 return 1;
936 memset(&act, 0, sizeof(act));
937 act.sa_flags = SA_RESTART;
938 act.sa_handler = quit_handler;
939 sigaction(SIGINT, &act, NULL);
940 sigaction(SIGHUP, &act, NULL);
941 sigaction(SIGTERM, &act, NULL);
942 sigaction(SIGQUIT, &act, NULL);
944 openlog("spice-vdagentd", do_daemonize ? 0 : LOG_PERROR, LOG_USER);
946 /* Setup communication with vdagent process(es) */
947 server = udscs_create_server(vdagentd_socket, agent_connect,
948 agent_read_complete, agent_disconnect,
949 vdagentd_messages, VDAGENTD_NO_MESSAGES,
950 debug);
951 if (!server) {
952 syslog(LOG_CRIT, "Fatal could not create server socket %s",
953 vdagentd_socket);
954 return 1;
956 if (chmod(vdagentd_socket, 0666)) {
957 syslog(LOG_CRIT, "Fatal could not change permissions on %s: %m",
958 vdagentd_socket);
959 udscs_destroy_server(server);
960 return 1;
963 if (do_daemonize)
964 daemonize();
966 #ifdef WITH_STATIC_UINPUT
967 uinput = vdagentd_uinput_create(uinput_device, 1024, 768, NULL, 0,
968 debug > 1, uinput_fake);
969 if (!uinput) {
970 udscs_destroy_server(server);
971 return 1;
973 #endif
975 if (want_session_info)
976 session_info = session_info_create(debug);
977 if (!session_info)
978 syslog(LOG_WARNING, "no session info, max 1 session agent allowed");
980 active_xfers = g_hash_table_new(g_direct_hash, g_direct_equal);
981 main_loop();
983 release_clipboards();
985 vdagentd_uinput_destroy(&uinput);
986 vdagent_virtio_port_flush(&virtio_port);
987 vdagent_virtio_port_destroy(&virtio_port);
988 session_info_destroy(session_info);
989 udscs_destroy_server(server);
990 if (unlink(vdagentd_socket) != 0)
991 syslog(LOG_ERR, "unlink %s: %s", vdagentd_socket, strerror(errno));
992 syslog(LOG_INFO, "vdagentd quiting, returning status %d", retval);
994 if (do_daemonize)
995 unlink(pidfilename);
997 return retval;