build-sys: get rid of noinst_HEADERS
[vd_agent.git] / src / vdagentd.c
bloba699681fd18b3a472b9bfc3d23c1232fa39793b7
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 /* To be used by vdagentd for failures in file-xfer such as when file-xfer was
253 * cancelled or an error happened */
254 static void send_file_xfer_status(struct vdagent_virtio_port *vport,
255 const char *msg, uint32_t id, uint32_t xfer_status)
257 VDAgentFileXferStatusMessage status = {
258 .id = id,
259 .result = xfer_status,
261 syslog(LOG_WARNING, msg, id);
262 if (vport)
263 vdagent_virtio_port_write(vport, VDP_CLIENT_PORT,
264 VD_AGENT_FILE_XFER_STATUS, 0,
265 (uint8_t *)&status, sizeof(status));
268 static void do_client_file_xfer(struct vdagent_virtio_port *vport,
269 VDAgentMessage *message_header,
270 uint8_t *data)
272 uint32_t msg_type, id;
273 struct udscs_connection *conn;
275 switch (message_header->type) {
276 case VD_AGENT_FILE_XFER_START: {
277 VDAgentFileXferStartMessage *s = (VDAgentFileXferStartMessage *)data;
278 if (!active_session_conn) {
279 send_file_xfer_status(vport,
280 "Could not find an agent connnection belonging to the "
281 "active session, cancelling client file-xfer request %u",
282 s->id, VD_AGENT_FILE_XFER_STATUS_CANCELLED);
283 return;
284 } else if (session_info_session_is_locked(session_info)) {
285 syslog(LOG_DEBUG, "Session is locked, skipping file-xfer-start");
286 send_file_xfer_status(vport,
287 "User's session is locked and cannot start file transfer. "
288 "Cancelling client file-xfer request %u",
289 s->id, VD_AGENT_FILE_XFER_STATUS_ERROR);
290 return;
292 udscs_write(active_session_conn, VDAGENTD_FILE_XFER_START, 0, 0,
293 data, message_header->size);
294 return;
296 case VD_AGENT_FILE_XFER_STATUS: {
297 VDAgentFileXferStatusMessage *s = (VDAgentFileXferStatusMessage *)data;
298 msg_type = VDAGENTD_FILE_XFER_STATUS;
299 id = s->id;
300 break;
302 case VD_AGENT_FILE_XFER_DATA: {
303 VDAgentFileXferDataMessage *d = (VDAgentFileXferDataMessage *)data;
304 msg_type = VDAGENTD_FILE_XFER_DATA;
305 id = d->id;
306 break;
310 conn = g_hash_table_lookup(active_xfers, GUINT_TO_POINTER(id));
311 if (!conn) {
312 if (debug)
313 syslog(LOG_DEBUG, "Could not find file-xfer %u (cancelled?)", id);
314 return;
316 udscs_write(conn, msg_type, 0, 0, data, message_header->size);
319 static int virtio_port_read_complete(
320 struct vdagent_virtio_port *vport,
321 int port_nr,
322 VDAgentMessage *message_header,
323 uint8_t *data)
325 uint32_t min_size = 0;
327 if (message_header->protocol != VD_AGENT_PROTOCOL) {
328 syslog(LOG_ERR, "message with wrong protocol version ignoring");
329 return 0;
332 switch (message_header->type) {
333 case VD_AGENT_MOUSE_STATE:
334 if (message_header->size != sizeof(VDAgentMouseState))
335 goto size_error;
336 vdagentd_uinput_do_mouse(&uinput, (VDAgentMouseState *)data);
337 if (!uinput) {
338 /* Try to re-open the tablet */
339 struct agent_data *agent_data =
340 udscs_get_user_data(active_session_conn);
341 if (agent_data)
342 uinput = vdagentd_uinput_create(uinput_device,
343 agent_data->width,
344 agent_data->height,
345 agent_data->screen_info,
346 agent_data->screen_count,
347 debug > 1,
348 uinput_fake);
349 if (!uinput) {
350 syslog(LOG_CRIT, "Fatal uinput error");
351 retval = 1;
352 quit = 1;
355 break;
356 case VD_AGENT_MONITORS_CONFIG:
357 if (message_header->size < sizeof(VDAgentMonitorsConfig))
358 goto size_error;
359 do_client_monitors(vport, port_nr, message_header,
360 (VDAgentMonitorsConfig *)data);
361 break;
362 case VD_AGENT_ANNOUNCE_CAPABILITIES:
363 if (message_header->size < sizeof(VDAgentAnnounceCapabilities))
364 goto size_error;
365 do_client_capabilities(vport, message_header,
366 (VDAgentAnnounceCapabilities *)data);
367 break;
368 case VD_AGENT_CLIPBOARD_GRAB:
369 case VD_AGENT_CLIPBOARD_REQUEST:
370 case VD_AGENT_CLIPBOARD:
371 case VD_AGENT_CLIPBOARD_RELEASE:
372 switch (message_header->type) {
373 case VD_AGENT_CLIPBOARD_GRAB:
374 min_size = sizeof(VDAgentClipboardGrab); break;
375 case VD_AGENT_CLIPBOARD_REQUEST:
376 min_size = sizeof(VDAgentClipboardRequest); break;
377 case VD_AGENT_CLIPBOARD:
378 min_size = sizeof(VDAgentClipboard); break;
380 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
381 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
382 min_size += 4;
384 if (message_header->size < min_size) {
385 goto size_error;
387 do_client_clipboard(vport, message_header, data);
388 break;
389 case VD_AGENT_FILE_XFER_START:
390 case VD_AGENT_FILE_XFER_STATUS:
391 case VD_AGENT_FILE_XFER_DATA:
392 do_client_file_xfer(vport, message_header, data);
393 break;
394 case VD_AGENT_CLIENT_DISCONNECTED:
395 vdagent_virtio_port_reset(vport, VDP_CLIENT_PORT);
396 do_client_disconnect();
397 break;
398 case VD_AGENT_MAX_CLIPBOARD:
399 if (message_header->size != sizeof(VDAgentMaxClipboard))
400 goto size_error;
401 VDAgentMaxClipboard *msg = (VDAgentMaxClipboard *)data;
402 syslog(LOG_DEBUG, "Set max clipboard: %d", msg->max);
403 max_clipboard = msg->max;
404 break;
405 case VD_AGENT_AUDIO_VOLUME_SYNC:
406 if (message_header->size < sizeof(VDAgentAudioVolumeSync))
407 goto size_error;
409 do_client_volume_sync(vport, port_nr, message_header,
410 (VDAgentAudioVolumeSync *)data);
411 break;
412 default:
413 syslog(LOG_WARNING, "unknown message type %d, ignoring",
414 message_header->type);
417 return 0;
419 size_error:
420 syslog(LOG_ERR, "read: invalid message size: %u for message type: %u",
421 message_header->size, message_header->type);
422 return 0;
425 static void virtio_write_clipboard(uint8_t selection, uint32_t msg_type,
426 uint32_t data_type, const uint8_t *data, uint32_t data_size)
428 uint32_t size = data_size;
430 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
431 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
432 size += 4;
434 if (data_type != -1) {
435 size += 4;
438 vdagent_virtio_port_write_start(virtio_port, VDP_CLIENT_PORT, msg_type,
439 0, size);
441 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
442 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
443 uint8_t sel[4] = { selection, 0, 0, 0 };
444 vdagent_virtio_port_write_append(virtio_port, sel, 4);
446 if (data_type != -1) {
447 vdagent_virtio_port_write_append(virtio_port, (uint8_t*)&data_type, 4);
450 vdagent_virtio_port_write_append(virtio_port, data, data_size);
453 /* vdagentd <-> vdagent communication handling */
454 static int do_agent_clipboard(struct udscs_connection *conn,
455 struct udscs_message_header *header, const uint8_t *data)
457 uint8_t selection = header->arg1;
458 uint32_t msg_type = 0, data_type = -1, size = header->size;
460 if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
461 VD_AGENT_CAP_CLIPBOARD_BY_DEMAND))
462 goto error;
464 /* Check that this agent is from the currently active session */
465 if (conn != active_session_conn) {
466 if (debug)
467 syslog(LOG_DEBUG, "%p clipboard req from agent which is not in "
468 "the active session?", conn);
469 goto error;
472 if (!virtio_port) {
473 syslog(LOG_ERR, "Clipboard req from agent but no client connection");
474 goto error;
477 if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
478 VD_AGENT_CAP_CLIPBOARD_SELECTION) &&
479 selection != VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) {
480 goto error;
483 switch (header->type) {
484 case VDAGENTD_CLIPBOARD_GRAB:
485 msg_type = VD_AGENT_CLIPBOARD_GRAB;
486 agent_owns_clipboard[selection] = 1;
487 break;
488 case VDAGENTD_CLIPBOARD_REQUEST:
489 msg_type = VD_AGENT_CLIPBOARD_REQUEST;
490 data_type = header->arg2;
491 size = 0;
492 break;
493 case VDAGENTD_CLIPBOARD_DATA:
494 msg_type = VD_AGENT_CLIPBOARD;
495 data_type = header->arg2;
496 if (max_clipboard != -1 && size > max_clipboard) {
497 syslog(LOG_WARNING, "clipboard is too large (%d > %d), discarding",
498 size, max_clipboard);
499 virtio_write_clipboard(selection, msg_type, data_type, NULL, 0);
500 return 0;
502 break;
503 case VDAGENTD_CLIPBOARD_RELEASE:
504 msg_type = VD_AGENT_CLIPBOARD_RELEASE;
505 size = 0;
506 agent_owns_clipboard[selection] = 0;
507 break;
508 default:
509 syslog(LOG_WARNING, "unexpected clipboard message type");
510 goto error;
513 if (size != header->size) {
514 syslog(LOG_ERR,
515 "unexpected extra data in clipboard msg, disconnecting agent");
516 return -1;
519 virtio_write_clipboard(selection, msg_type, data_type, data, header->size);
521 return 0;
523 error:
524 if (header->type == VDAGENTD_CLIPBOARD_REQUEST) {
525 /* Let the agent know no answer is coming */
526 udscs_write(conn, VDAGENTD_CLIPBOARD_DATA,
527 selection, VD_AGENT_CLIPBOARD_NONE, NULL, 0);
529 return 0;
532 /* When we open the vdagent virtio channel, the server automatically goes into
533 client mouse mode, so we can only have the channel open when we know the
534 active session resolution. This function checks that we have an agent in the
535 active session, and that it has told us its resolution. If these conditions
536 are met it sets the uinput tablet device's resolution and opens the virtio
537 channel (if it is not already open). If these conditions are not met, it
538 closes both. */
539 static void check_xorg_resolution(void)
541 struct agent_data *agent_data = udscs_get_user_data(active_session_conn);
543 if (agent_data && agent_data->screen_info) {
544 if (!uinput)
545 uinput = vdagentd_uinput_create(uinput_device,
546 agent_data->width,
547 agent_data->height,
548 agent_data->screen_info,
549 agent_data->screen_count,
550 debug > 1,
551 uinput_fake);
552 else
553 vdagentd_uinput_update_size(&uinput,
554 agent_data->width,
555 agent_data->height,
556 agent_data->screen_info,
557 agent_data->screen_count);
558 if (!uinput) {
559 syslog(LOG_CRIT, "Fatal uinput error");
560 retval = 1;
561 quit = 1;
562 return;
565 if (!virtio_port) {
566 syslog(LOG_INFO, "opening vdagent virtio channel");
567 virtio_port = vdagent_virtio_port_create(portdev,
568 virtio_port_read_complete,
569 NULL);
570 if (!virtio_port) {
571 syslog(LOG_CRIT, "Fatal error opening vdagent virtio channel");
572 retval = 1;
573 quit = 1;
574 return;
576 send_capabilities(virtio_port, 1);
578 } else {
579 #ifndef WITH_STATIC_UINPUT
580 vdagentd_uinput_destroy(&uinput);
581 #endif
582 if (virtio_port) {
583 vdagent_virtio_port_flush(&virtio_port);
584 vdagent_virtio_port_destroy(&virtio_port);
585 syslog(LOG_INFO, "closed vdagent virtio channel");
590 static int connection_matches_active_session(struct udscs_connection **connp,
591 void *priv)
593 struct udscs_connection **conn_ret = (struct udscs_connection **)priv;
594 struct agent_data *agent_data = udscs_get_user_data(*connp);
596 /* Check if this connection matches the currently active session */
597 if (!agent_data->session || !active_session)
598 return 0;
599 if (strcmp(agent_data->session, active_session))
600 return 0;
602 *conn_ret = *connp;
603 return 1;
606 static void release_clipboards(void)
608 uint8_t sel;
610 for (sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; ++sel) {
611 if (agent_owns_clipboard[sel] && virtio_port) {
612 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
613 VD_AGENT_CLIPBOARD_RELEASE, 0, &sel, 1);
615 agent_owns_clipboard[sel] = 0;
619 static void update_active_session_connection(struct udscs_connection *new_conn)
621 if (session_info) {
622 new_conn = NULL;
623 if (!active_session)
624 active_session = session_info_get_active_session(session_info);
625 session_count = udscs_server_for_all_clients(server,
626 connection_matches_active_session,
627 (void*)&new_conn);
628 } else {
629 if (new_conn)
630 session_count++;
631 else
632 session_count--;
635 if (new_conn && session_count != 1) {
636 syslog(LOG_ERR, "multiple agents in one session, "
637 "disabling agent to avoid potential information leak");
638 new_conn = NULL;
641 if (new_conn == active_session_conn)
642 return;
644 active_session_conn = new_conn;
645 if (debug)
646 syslog(LOG_DEBUG, "%p is now the active session", new_conn);
648 if (active_session_conn && !session_info_is_user(session_info)) {
649 if (debug)
650 syslog(LOG_DEBUG, "New session agent does not belong to user: "
651 "disabling file-xfer");
652 udscs_write(active_session_conn, VDAGENTD_FILE_XFER_DISABLE, 0, 0,
653 NULL, 0);
656 if (active_session_conn && mon_config)
657 udscs_write(active_session_conn, VDAGENTD_MONITORS_CONFIG, 0, 0,
658 (uint8_t *)mon_config, sizeof(VDAgentMonitorsConfig) +
659 mon_config->num_of_monitors * sizeof(VDAgentMonConfig));
661 release_clipboards();
663 check_xorg_resolution();
666 static gboolean remove_active_xfers(gpointer key, gpointer value, gpointer conn)
668 if (value == conn) {
669 send_file_xfer_status(virtio_port,
670 "Agent disc; cancelling file-xfer %u",
671 GPOINTER_TO_UINT(key),
672 VD_AGENT_FILE_XFER_STATUS_CANCELLED);
673 return 1;
674 } else
675 return 0;
678 static void agent_connect(struct udscs_connection *conn)
680 struct agent_data *agent_data;
682 agent_data = calloc(1, sizeof(*agent_data));
683 if (!agent_data) {
684 syslog(LOG_ERR, "Out of memory allocating agent data, disconnecting");
685 udscs_destroy_connection(&conn);
686 return;
689 if (session_info) {
690 uint32_t pid = udscs_get_peer_cred(conn).pid;
691 agent_data->session = session_info_session_for_pid(session_info, pid);
694 udscs_set_user_data(conn, (void *)agent_data);
695 udscs_write(conn, VDAGENTD_VERSION, 0, 0,
696 (uint8_t *)VERSION, strlen(VERSION) + 1);
697 update_active_session_connection(conn);
700 static void agent_disconnect(struct udscs_connection *conn)
702 struct agent_data *agent_data = udscs_get_user_data(conn);
704 g_hash_table_foreach_remove(active_xfers, remove_active_xfers, conn);
706 free(agent_data->session);
707 agent_data->session = NULL;
708 update_active_session_connection(NULL);
710 free(agent_data->screen_info);
711 free(agent_data);
714 static void agent_read_complete(struct udscs_connection **connp,
715 struct udscs_message_header *header, uint8_t *data)
717 struct agent_data *agent_data = udscs_get_user_data(*connp);
719 switch (header->type) {
720 case VDAGENTD_GUEST_XORG_RESOLUTION: {
721 struct vdagentd_guest_xorg_resolution *res;
722 int n = header->size / sizeof(*res);
724 /* Detect older version session agent, but don't disconnect, as
725 that stops it from getting the VDAGENTD_VERSION message, and then
726 it will never re-exec the new version... */
727 if (header->arg1 == 0 && header->arg2 == 0) {
728 syslog(LOG_INFO, "got old session agent xorg resolution message, "
729 "ignoring");
730 free(data);
731 return;
734 if (header->size != n * sizeof(*res)) {
735 syslog(LOG_ERR, "guest xorg resolution message has wrong size, "
736 "disconnecting agent");
737 udscs_destroy_connection(connp);
738 free(data);
739 return;
742 free(agent_data->screen_info);
743 res = malloc(n * sizeof(*res));
744 if (!res) {
745 syslog(LOG_ERR, "out of memory allocating screen info");
746 n = 0;
748 memcpy(res, data, n * sizeof(*res));
749 agent_data->width = header->arg1;
750 agent_data->height = header->arg2;
751 agent_data->screen_info = res;
752 agent_data->screen_count = n;
754 check_xorg_resolution();
755 break;
757 case VDAGENTD_CLIPBOARD_GRAB:
758 case VDAGENTD_CLIPBOARD_REQUEST:
759 case VDAGENTD_CLIPBOARD_DATA:
760 case VDAGENTD_CLIPBOARD_RELEASE:
761 if (do_agent_clipboard(*connp, header, data)) {
762 udscs_destroy_connection(connp);
763 free(data);
764 return;
766 break;
767 case VDAGENTD_FILE_XFER_STATUS:{
768 VDAgentFileXferStatusMessage status;
769 status.id = header->arg1;
770 status.result = header->arg2;
771 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
772 VD_AGENT_FILE_XFER_STATUS, 0,
773 (uint8_t *)&status, sizeof(status));
774 if (status.result == VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA)
775 g_hash_table_insert(active_xfers, GUINT_TO_POINTER(status.id),
776 *connp);
777 else
778 g_hash_table_remove(active_xfers, GUINT_TO_POINTER(status.id));
779 break;
782 default:
783 syslog(LOG_ERR, "unknown message from vdagent: %u, ignoring",
784 header->type);
786 free(data);
789 /* main */
791 static void usage(FILE *fp)
793 fprintf(fp,
794 "Usage: spice-vdagentd [OPTIONS]\n\n"
795 "Spice guest agent daemon, version %s.\n\n"
796 "Options:\n"
797 " -h print this text\n"
798 " -d log debug messages (use twice for extra info)\n"
799 " -s <port> set virtio serial port [%s]\n"
800 " -S <filename> set udcs socket [%s]\n"
801 " -u <dev> set uinput device [%s]\n"
802 " -f treat uinput device as fake; no ioctls\n"
803 " -x don't daemonize\n"
804 " -o Only handle one virtio serial session.\n"
805 #ifdef HAVE_CONSOLE_KIT
806 " -X Disable console kit integration\n"
807 #endif
808 #ifdef HAVE_LIBSYSTEMD_LOGIN
809 " -X Disable systemd-logind integration\n"
810 #endif
811 ,VERSION, portdev, vdagentd_socket, uinput_device);
814 static void daemonize(void)
816 int x;
817 FILE *pidfile;
819 /* detach from terminal */
820 switch (fork()) {
821 case 0:
822 close(0); close(1); close(2);
823 setsid();
824 x = open("/dev/null", O_RDWR); x = dup(x); x = dup(x);
825 pidfile = fopen(pidfilename, "w");
826 if (pidfile) {
827 fprintf(pidfile, "%d\n", (int)getpid());
828 fclose(pidfile);
830 break;
831 case -1:
832 syslog(LOG_ERR, "fork: %m");
833 retval = 1;
834 default:
835 udscs_destroy_server(server);
836 exit(retval);
840 static void main_loop(void)
842 fd_set readfds, writefds;
843 int n, nfds;
844 int ck_fd = 0;
845 int once = 0;
847 while (!quit) {
848 FD_ZERO(&readfds);
849 FD_ZERO(&writefds);
851 nfds = udscs_server_fill_fds(server, &readfds, &writefds);
852 n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds);
853 if (n >= nfds)
854 nfds = n + 1;
856 if (session_info) {
857 ck_fd = session_info_get_fd(session_info);
858 FD_SET(ck_fd, &readfds);
859 if (ck_fd >= nfds)
860 nfds = ck_fd + 1;
863 n = select(nfds, &readfds, &writefds, NULL, NULL);
864 if (n == -1) {
865 if (errno == EINTR)
866 continue;
867 syslog(LOG_CRIT, "Fatal error select: %m");
868 retval = 1;
869 break;
872 udscs_server_handle_fds(server, &readfds, &writefds);
874 if (virtio_port) {
875 once = 1;
876 vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds);
877 if (!virtio_port) {
878 int old_client_connected = client_connected;
879 syslog(LOG_CRIT,
880 "AIIEEE lost spice client connection, reconnecting");
881 virtio_port = vdagent_virtio_port_create(portdev,
882 virtio_port_read_complete,
883 NULL);
884 if (!virtio_port) {
885 syslog(LOG_CRIT,
886 "Fatal error opening vdagent virtio channel");
887 retval = 1;
888 break;
890 do_client_disconnect();
891 client_connected = old_client_connected;
894 else if (only_once && once)
896 syslog(LOG_INFO, "Exiting after one client session.");
897 break;
900 if (session_info && FD_ISSET(ck_fd, &readfds)) {
901 active_session = session_info_get_active_session(session_info);
902 update_active_session_connection(NULL);
907 static void quit_handler(int sig)
909 quit = 1;
912 int main(int argc, char *argv[])
914 int c;
915 int do_daemonize = 1;
916 int want_session_info = 1;
917 struct sigaction act;
919 for (;;) {
920 if (-1 == (c = getopt(argc, argv, "-dhxXfos:u:S:")))
921 break;
922 switch (c) {
923 case 'd':
924 debug++;
925 break;
926 case 's':
927 portdev = optarg;
928 break;
929 case 'S':
930 vdagentd_socket = optarg;
931 break;
932 case 'u':
933 uinput_device = optarg;
934 break;
935 case 'f':
936 uinput_fake = 1;
937 break;
938 case 'o':
939 only_once = 1;
940 break;
941 case 'x':
942 do_daemonize = 0;
943 break;
944 case 'X':
945 want_session_info = 0;
946 break;
947 case 'h':
948 usage(stdout);
949 return 0;
950 default:
951 fputs("\n", stderr);
952 usage(stderr);
953 return 1;
957 memset(&act, 0, sizeof(act));
958 act.sa_flags = SA_RESTART;
959 act.sa_handler = quit_handler;
960 sigaction(SIGINT, &act, NULL);
961 sigaction(SIGHUP, &act, NULL);
962 sigaction(SIGTERM, &act, NULL);
963 sigaction(SIGQUIT, &act, NULL);
965 openlog("spice-vdagentd", do_daemonize ? 0 : LOG_PERROR, LOG_USER);
967 /* Setup communication with vdagent process(es) */
968 server = udscs_create_server(vdagentd_socket, agent_connect,
969 agent_read_complete, agent_disconnect,
970 vdagentd_messages, VDAGENTD_NO_MESSAGES,
971 debug);
972 if (!server) {
973 syslog(LOG_CRIT, "Fatal could not create server socket %s",
974 vdagentd_socket);
975 return 1;
977 if (chmod(vdagentd_socket, 0666)) {
978 syslog(LOG_CRIT, "Fatal could not change permissions on %s: %m",
979 vdagentd_socket);
980 udscs_destroy_server(server);
981 return 1;
984 if (do_daemonize)
985 daemonize();
987 #ifdef WITH_STATIC_UINPUT
988 uinput = vdagentd_uinput_create(uinput_device, 1024, 768, NULL, 0,
989 debug > 1, uinput_fake);
990 if (!uinput) {
991 udscs_destroy_server(server);
992 return 1;
994 #endif
996 if (want_session_info)
997 session_info = session_info_create(debug);
998 if (!session_info)
999 syslog(LOG_WARNING, "no session info, max 1 session agent allowed");
1001 active_xfers = g_hash_table_new(g_direct_hash, g_direct_equal);
1002 main_loop();
1004 release_clipboards();
1006 vdagentd_uinput_destroy(&uinput);
1007 vdagent_virtio_port_flush(&virtio_port);
1008 vdagent_virtio_port_destroy(&virtio_port);
1009 session_info_destroy(session_info);
1010 udscs_destroy_server(server);
1011 if (unlink(vdagentd_socket) != 0)
1012 syslog(LOG_ERR, "unlink %s: %s", vdagentd_socket, strerror(errno));
1013 syslog(LOG_INFO, "vdagentd quiting, returning status %d", retval);
1015 if (do_daemonize)
1016 unlink(pidfilename);
1018 return retval;