vdagentd: send file-xfer status, generic version
[vd_agent.git] / src / vdagentd.c
blob66afeaa24ac51a56f531cd60177fc69f8c4fa302
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;
285 udscs_write(active_session_conn, VDAGENTD_FILE_XFER_START, 0, 0,
286 data, message_header->size);
287 return;
289 case VD_AGENT_FILE_XFER_STATUS: {
290 VDAgentFileXferStatusMessage *s = (VDAgentFileXferStatusMessage *)data;
291 msg_type = VDAGENTD_FILE_XFER_STATUS;
292 id = s->id;
293 break;
295 case VD_AGENT_FILE_XFER_DATA: {
296 VDAgentFileXferDataMessage *d = (VDAgentFileXferDataMessage *)data;
297 msg_type = VDAGENTD_FILE_XFER_DATA;
298 id = d->id;
299 break;
303 conn = g_hash_table_lookup(active_xfers, GUINT_TO_POINTER(id));
304 if (!conn) {
305 if (debug)
306 syslog(LOG_DEBUG, "Could not find file-xfer %u (cancelled?)", id);
307 return;
309 udscs_write(conn, msg_type, 0, 0, data, message_header->size);
312 static int virtio_port_read_complete(
313 struct vdagent_virtio_port *vport,
314 int port_nr,
315 VDAgentMessage *message_header,
316 uint8_t *data)
318 uint32_t min_size = 0;
320 if (message_header->protocol != VD_AGENT_PROTOCOL) {
321 syslog(LOG_ERR, "message with wrong protocol version ignoring");
322 return 0;
325 switch (message_header->type) {
326 case VD_AGENT_MOUSE_STATE:
327 if (message_header->size != sizeof(VDAgentMouseState))
328 goto size_error;
329 vdagentd_uinput_do_mouse(&uinput, (VDAgentMouseState *)data);
330 if (!uinput) {
331 /* Try to re-open the tablet */
332 struct agent_data *agent_data =
333 udscs_get_user_data(active_session_conn);
334 if (agent_data)
335 uinput = vdagentd_uinput_create(uinput_device,
336 agent_data->width,
337 agent_data->height,
338 agent_data->screen_info,
339 agent_data->screen_count,
340 debug > 1,
341 uinput_fake);
342 if (!uinput) {
343 syslog(LOG_CRIT, "Fatal uinput error");
344 retval = 1;
345 quit = 1;
348 break;
349 case VD_AGENT_MONITORS_CONFIG:
350 if (message_header->size < sizeof(VDAgentMonitorsConfig))
351 goto size_error;
352 do_client_monitors(vport, port_nr, message_header,
353 (VDAgentMonitorsConfig *)data);
354 break;
355 case VD_AGENT_ANNOUNCE_CAPABILITIES:
356 if (message_header->size < sizeof(VDAgentAnnounceCapabilities))
357 goto size_error;
358 do_client_capabilities(vport, message_header,
359 (VDAgentAnnounceCapabilities *)data);
360 break;
361 case VD_AGENT_CLIPBOARD_GRAB:
362 case VD_AGENT_CLIPBOARD_REQUEST:
363 case VD_AGENT_CLIPBOARD:
364 case VD_AGENT_CLIPBOARD_RELEASE:
365 switch (message_header->type) {
366 case VD_AGENT_CLIPBOARD_GRAB:
367 min_size = sizeof(VDAgentClipboardGrab); break;
368 case VD_AGENT_CLIPBOARD_REQUEST:
369 min_size = sizeof(VDAgentClipboardRequest); break;
370 case VD_AGENT_CLIPBOARD:
371 min_size = sizeof(VDAgentClipboard); break;
373 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
374 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
375 min_size += 4;
377 if (message_header->size < min_size) {
378 goto size_error;
380 do_client_clipboard(vport, message_header, data);
381 break;
382 case VD_AGENT_FILE_XFER_START:
383 case VD_AGENT_FILE_XFER_STATUS:
384 case VD_AGENT_FILE_XFER_DATA:
385 do_client_file_xfer(vport, message_header, data);
386 break;
387 case VD_AGENT_CLIENT_DISCONNECTED:
388 vdagent_virtio_port_reset(vport, VDP_CLIENT_PORT);
389 do_client_disconnect();
390 break;
391 case VD_AGENT_MAX_CLIPBOARD:
392 if (message_header->size != sizeof(VDAgentMaxClipboard))
393 goto size_error;
394 VDAgentMaxClipboard *msg = (VDAgentMaxClipboard *)data;
395 syslog(LOG_DEBUG, "Set max clipboard: %d", msg->max);
396 max_clipboard = msg->max;
397 break;
398 case VD_AGENT_AUDIO_VOLUME_SYNC:
399 if (message_header->size < sizeof(VDAgentAudioVolumeSync))
400 goto size_error;
402 do_client_volume_sync(vport, port_nr, message_header,
403 (VDAgentAudioVolumeSync *)data);
404 break;
405 default:
406 syslog(LOG_WARNING, "unknown message type %d, ignoring",
407 message_header->type);
410 return 0;
412 size_error:
413 syslog(LOG_ERR, "read: invalid message size: %u for message type: %u",
414 message_header->size, message_header->type);
415 return 0;
418 static void virtio_write_clipboard(uint8_t selection, uint32_t msg_type,
419 uint32_t data_type, const uint8_t *data, uint32_t data_size)
421 uint32_t size = data_size;
423 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
424 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
425 size += 4;
427 if (data_type != -1) {
428 size += 4;
431 vdagent_virtio_port_write_start(virtio_port, VDP_CLIENT_PORT, msg_type,
432 0, size);
434 if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
435 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
436 uint8_t sel[4] = { selection, 0, 0, 0 };
437 vdagent_virtio_port_write_append(virtio_port, sel, 4);
439 if (data_type != -1) {
440 vdagent_virtio_port_write_append(virtio_port, (uint8_t*)&data_type, 4);
443 vdagent_virtio_port_write_append(virtio_port, data, data_size);
446 /* vdagentd <-> vdagent communication handling */
447 static int do_agent_clipboard(struct udscs_connection *conn,
448 struct udscs_message_header *header, const uint8_t *data)
450 uint8_t selection = header->arg1;
451 uint32_t msg_type = 0, data_type = -1, size = header->size;
453 if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
454 VD_AGENT_CAP_CLIPBOARD_BY_DEMAND))
455 goto error;
457 /* Check that this agent is from the currently active session */
458 if (conn != active_session_conn) {
459 if (debug)
460 syslog(LOG_DEBUG, "%p clipboard req from agent which is not in "
461 "the active session?", conn);
462 goto error;
465 if (!virtio_port) {
466 syslog(LOG_ERR, "Clipboard req from agent but no client connection");
467 goto error;
470 if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
471 VD_AGENT_CAP_CLIPBOARD_SELECTION) &&
472 selection != VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) {
473 goto error;
476 switch (header->type) {
477 case VDAGENTD_CLIPBOARD_GRAB:
478 msg_type = VD_AGENT_CLIPBOARD_GRAB;
479 agent_owns_clipboard[selection] = 1;
480 break;
481 case VDAGENTD_CLIPBOARD_REQUEST:
482 msg_type = VD_AGENT_CLIPBOARD_REQUEST;
483 data_type = header->arg2;
484 size = 0;
485 break;
486 case VDAGENTD_CLIPBOARD_DATA:
487 msg_type = VD_AGENT_CLIPBOARD;
488 data_type = header->arg2;
489 if (max_clipboard != -1 && size > max_clipboard) {
490 syslog(LOG_WARNING, "clipboard is too large (%d > %d), discarding",
491 size, max_clipboard);
492 virtio_write_clipboard(selection, msg_type, data_type, NULL, 0);
493 return 0;
495 break;
496 case VDAGENTD_CLIPBOARD_RELEASE:
497 msg_type = VD_AGENT_CLIPBOARD_RELEASE;
498 size = 0;
499 agent_owns_clipboard[selection] = 0;
500 break;
501 default:
502 syslog(LOG_WARNING, "unexpected clipboard message type");
503 goto error;
506 if (size != header->size) {
507 syslog(LOG_ERR,
508 "unexpected extra data in clipboard msg, disconnecting agent");
509 return -1;
512 virtio_write_clipboard(selection, msg_type, data_type, data, header->size);
514 return 0;
516 error:
517 if (header->type == VDAGENTD_CLIPBOARD_REQUEST) {
518 /* Let the agent know no answer is coming */
519 udscs_write(conn, VDAGENTD_CLIPBOARD_DATA,
520 selection, VD_AGENT_CLIPBOARD_NONE, NULL, 0);
522 return 0;
525 /* When we open the vdagent virtio channel, the server automatically goes into
526 client mouse mode, so we can only have the channel open when we know the
527 active session resolution. This function checks that we have an agent in the
528 active session, and that it has told us its resolution. If these conditions
529 are met it sets the uinput tablet device's resolution and opens the virtio
530 channel (if it is not already open). If these conditions are not met, it
531 closes both. */
532 static void check_xorg_resolution(void)
534 struct agent_data *agent_data = udscs_get_user_data(active_session_conn);
536 if (agent_data && agent_data->screen_info) {
537 if (!uinput)
538 uinput = vdagentd_uinput_create(uinput_device,
539 agent_data->width,
540 agent_data->height,
541 agent_data->screen_info,
542 agent_data->screen_count,
543 debug > 1,
544 uinput_fake);
545 else
546 vdagentd_uinput_update_size(&uinput,
547 agent_data->width,
548 agent_data->height,
549 agent_data->screen_info,
550 agent_data->screen_count);
551 if (!uinput) {
552 syslog(LOG_CRIT, "Fatal uinput error");
553 retval = 1;
554 quit = 1;
555 return;
558 if (!virtio_port) {
559 syslog(LOG_INFO, "opening vdagent virtio channel");
560 virtio_port = vdagent_virtio_port_create(portdev,
561 virtio_port_read_complete,
562 NULL);
563 if (!virtio_port) {
564 syslog(LOG_CRIT, "Fatal error opening vdagent virtio channel");
565 retval = 1;
566 quit = 1;
567 return;
569 send_capabilities(virtio_port, 1);
571 } else {
572 #ifndef WITH_STATIC_UINPUT
573 vdagentd_uinput_destroy(&uinput);
574 #endif
575 if (virtio_port) {
576 vdagent_virtio_port_flush(&virtio_port);
577 vdagent_virtio_port_destroy(&virtio_port);
578 syslog(LOG_INFO, "closed vdagent virtio channel");
583 static int connection_matches_active_session(struct udscs_connection **connp,
584 void *priv)
586 struct udscs_connection **conn_ret = (struct udscs_connection **)priv;
587 struct agent_data *agent_data = udscs_get_user_data(*connp);
589 /* Check if this connection matches the currently active session */
590 if (!agent_data->session || !active_session)
591 return 0;
592 if (strcmp(agent_data->session, active_session))
593 return 0;
595 *conn_ret = *connp;
596 return 1;
599 static void release_clipboards(void)
601 uint8_t sel;
603 for (sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; ++sel) {
604 if (agent_owns_clipboard[sel] && virtio_port) {
605 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
606 VD_AGENT_CLIPBOARD_RELEASE, 0, &sel, 1);
608 agent_owns_clipboard[sel] = 0;
612 static void update_active_session_connection(struct udscs_connection *new_conn)
614 if (session_info) {
615 new_conn = NULL;
616 if (!active_session)
617 active_session = session_info_get_active_session(session_info);
618 session_count = udscs_server_for_all_clients(server,
619 connection_matches_active_session,
620 (void*)&new_conn);
621 } else {
622 if (new_conn)
623 session_count++;
624 else
625 session_count--;
628 if (new_conn && session_count != 1) {
629 syslog(LOG_ERR, "multiple agents in one session, "
630 "disabling agent to avoid potential information leak");
631 new_conn = NULL;
634 if (new_conn == active_session_conn)
635 return;
637 active_session_conn = new_conn;
638 if (debug)
639 syslog(LOG_DEBUG, "%p is now the active session", new_conn);
640 if (active_session_conn && mon_config)
641 udscs_write(active_session_conn, VDAGENTD_MONITORS_CONFIG, 0, 0,
642 (uint8_t *)mon_config, sizeof(VDAgentMonitorsConfig) +
643 mon_config->num_of_monitors * sizeof(VDAgentMonConfig));
645 release_clipboards();
647 check_xorg_resolution();
650 static gboolean remove_active_xfers(gpointer key, gpointer value, gpointer conn)
652 if (value == conn) {
653 send_file_xfer_status(virtio_port,
654 "Agent disc; cancelling file-xfer %u",
655 GPOINTER_TO_UINT(key),
656 VD_AGENT_FILE_XFER_STATUS_CANCELLED);
657 return 1;
658 } else
659 return 0;
662 static void agent_connect(struct udscs_connection *conn)
664 struct agent_data *agent_data;
666 agent_data = calloc(1, sizeof(*agent_data));
667 if (!agent_data) {
668 syslog(LOG_ERR, "Out of memory allocating agent data, disconnecting");
669 udscs_destroy_connection(&conn);
670 return;
673 if (session_info) {
674 uint32_t pid = udscs_get_peer_cred(conn).pid;
675 agent_data->session = session_info_session_for_pid(session_info, pid);
678 udscs_set_user_data(conn, (void *)agent_data);
679 udscs_write(conn, VDAGENTD_VERSION, 0, 0,
680 (uint8_t *)VERSION, strlen(VERSION) + 1);
681 update_active_session_connection(conn);
684 static void agent_disconnect(struct udscs_connection *conn)
686 struct agent_data *agent_data = udscs_get_user_data(conn);
688 g_hash_table_foreach_remove(active_xfers, remove_active_xfers, conn);
690 free(agent_data->session);
691 agent_data->session = NULL;
692 update_active_session_connection(NULL);
694 free(agent_data->screen_info);
695 free(agent_data);
698 static void agent_read_complete(struct udscs_connection **connp,
699 struct udscs_message_header *header, uint8_t *data)
701 struct agent_data *agent_data = udscs_get_user_data(*connp);
703 switch (header->type) {
704 case VDAGENTD_GUEST_XORG_RESOLUTION: {
705 struct vdagentd_guest_xorg_resolution *res;
706 int n = header->size / sizeof(*res);
708 /* Detect older version session agent, but don't disconnect, as
709 that stops it from getting the VDAGENTD_VERSION message, and then
710 it will never re-exec the new version... */
711 if (header->arg1 == 0 && header->arg2 == 0) {
712 syslog(LOG_INFO, "got old session agent xorg resolution message, "
713 "ignoring");
714 free(data);
715 return;
718 if (header->size != n * sizeof(*res)) {
719 syslog(LOG_ERR, "guest xorg resolution message has wrong size, "
720 "disconnecting agent");
721 udscs_destroy_connection(connp);
722 free(data);
723 return;
726 free(agent_data->screen_info);
727 res = malloc(n * sizeof(*res));
728 if (!res) {
729 syslog(LOG_ERR, "out of memory allocating screen info");
730 n = 0;
732 memcpy(res, data, n * sizeof(*res));
733 agent_data->width = header->arg1;
734 agent_data->height = header->arg2;
735 agent_data->screen_info = res;
736 agent_data->screen_count = n;
738 check_xorg_resolution();
739 break;
741 case VDAGENTD_CLIPBOARD_GRAB:
742 case VDAGENTD_CLIPBOARD_REQUEST:
743 case VDAGENTD_CLIPBOARD_DATA:
744 case VDAGENTD_CLIPBOARD_RELEASE:
745 if (do_agent_clipboard(*connp, header, data)) {
746 udscs_destroy_connection(connp);
747 free(data);
748 return;
750 break;
751 case VDAGENTD_FILE_XFER_STATUS:{
752 VDAgentFileXferStatusMessage status;
753 status.id = header->arg1;
754 status.result = header->arg2;
755 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
756 VD_AGENT_FILE_XFER_STATUS, 0,
757 (uint8_t *)&status, sizeof(status));
758 if (status.result == VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA)
759 g_hash_table_insert(active_xfers, GUINT_TO_POINTER(status.id),
760 *connp);
761 else
762 g_hash_table_remove(active_xfers, GUINT_TO_POINTER(status.id));
763 break;
766 default:
767 syslog(LOG_ERR, "unknown message from vdagent: %u, ignoring",
768 header->type);
770 free(data);
773 /* main */
775 static void usage(FILE *fp)
777 fprintf(fp,
778 "Usage: spice-vdagentd [OPTIONS]\n\n"
779 "Spice guest agent daemon, version %s.\n\n"
780 "Options:\n"
781 " -h print this text\n"
782 " -d log debug messages (use twice for extra info)\n"
783 " -s <port> set virtio serial port [%s]\n"
784 " -S <filename> set udcs socket [%s]\n"
785 " -u <dev> set uinput device [%s]\n"
786 " -f treat uinput device as fake; no ioctls\n"
787 " -x don't daemonize\n"
788 " -o Only handle one virtio serial session.\n"
789 #ifdef HAVE_CONSOLE_KIT
790 " -X Disable console kit integration\n"
791 #endif
792 #ifdef HAVE_LIBSYSTEMD_LOGIN
793 " -X Disable systemd-logind integration\n"
794 #endif
795 ,VERSION, portdev, vdagentd_socket, uinput_device);
798 static void daemonize(void)
800 int x;
801 FILE *pidfile;
803 /* detach from terminal */
804 switch (fork()) {
805 case 0:
806 close(0); close(1); close(2);
807 setsid();
808 x = open("/dev/null", O_RDWR); x = dup(x); x = dup(x);
809 pidfile = fopen(pidfilename, "w");
810 if (pidfile) {
811 fprintf(pidfile, "%d\n", (int)getpid());
812 fclose(pidfile);
814 break;
815 case -1:
816 syslog(LOG_ERR, "fork: %m");
817 retval = 1;
818 default:
819 udscs_destroy_server(server);
820 exit(retval);
824 static void main_loop(void)
826 fd_set readfds, writefds;
827 int n, nfds;
828 int ck_fd = 0;
829 int once = 0;
831 while (!quit) {
832 FD_ZERO(&readfds);
833 FD_ZERO(&writefds);
835 nfds = udscs_server_fill_fds(server, &readfds, &writefds);
836 n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds);
837 if (n >= nfds)
838 nfds = n + 1;
840 if (session_info) {
841 ck_fd = session_info_get_fd(session_info);
842 FD_SET(ck_fd, &readfds);
843 if (ck_fd >= nfds)
844 nfds = ck_fd + 1;
847 n = select(nfds, &readfds, &writefds, NULL, NULL);
848 if (n == -1) {
849 if (errno == EINTR)
850 continue;
851 syslog(LOG_CRIT, "Fatal error select: %m");
852 retval = 1;
853 break;
856 udscs_server_handle_fds(server, &readfds, &writefds);
858 if (virtio_port) {
859 once = 1;
860 vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds);
861 if (!virtio_port) {
862 int old_client_connected = client_connected;
863 syslog(LOG_CRIT,
864 "AIIEEE lost spice client connection, reconnecting");
865 virtio_port = vdagent_virtio_port_create(portdev,
866 virtio_port_read_complete,
867 NULL);
868 if (!virtio_port) {
869 syslog(LOG_CRIT,
870 "Fatal error opening vdagent virtio channel");
871 retval = 1;
872 break;
874 do_client_disconnect();
875 client_connected = old_client_connected;
878 else if (only_once && once)
880 syslog(LOG_INFO, "Exiting after one client session.");
881 break;
884 if (session_info && FD_ISSET(ck_fd, &readfds)) {
885 active_session = session_info_get_active_session(session_info);
886 update_active_session_connection(NULL);
891 static void quit_handler(int sig)
893 quit = 1;
896 int main(int argc, char *argv[])
898 int c;
899 int do_daemonize = 1;
900 int want_session_info = 1;
901 struct sigaction act;
903 for (;;) {
904 if (-1 == (c = getopt(argc, argv, "-dhxXfos:u:S:")))
905 break;
906 switch (c) {
907 case 'd':
908 debug++;
909 break;
910 case 's':
911 portdev = optarg;
912 break;
913 case 'S':
914 vdagentd_socket = optarg;
915 break;
916 case 'u':
917 uinput_device = optarg;
918 break;
919 case 'f':
920 uinput_fake = 1;
921 break;
922 case 'o':
923 only_once = 1;
924 break;
925 case 'x':
926 do_daemonize = 0;
927 break;
928 case 'X':
929 want_session_info = 0;
930 break;
931 case 'h':
932 usage(stdout);
933 return 0;
934 default:
935 fputs("\n", stderr);
936 usage(stderr);
937 return 1;
941 memset(&act, 0, sizeof(act));
942 act.sa_flags = SA_RESTART;
943 act.sa_handler = quit_handler;
944 sigaction(SIGINT, &act, NULL);
945 sigaction(SIGHUP, &act, NULL);
946 sigaction(SIGTERM, &act, NULL);
947 sigaction(SIGQUIT, &act, NULL);
949 openlog("spice-vdagentd", do_daemonize ? 0 : LOG_PERROR, LOG_USER);
951 /* Setup communication with vdagent process(es) */
952 server = udscs_create_server(vdagentd_socket, agent_connect,
953 agent_read_complete, agent_disconnect,
954 vdagentd_messages, VDAGENTD_NO_MESSAGES,
955 debug);
956 if (!server) {
957 syslog(LOG_CRIT, "Fatal could not create server socket %s",
958 vdagentd_socket);
959 return 1;
961 if (chmod(vdagentd_socket, 0666)) {
962 syslog(LOG_CRIT, "Fatal could not change permissions on %s: %m",
963 vdagentd_socket);
964 udscs_destroy_server(server);
965 return 1;
968 if (do_daemonize)
969 daemonize();
971 #ifdef WITH_STATIC_UINPUT
972 uinput = vdagentd_uinput_create(uinput_device, 1024, 768, NULL, 0,
973 debug > 1, uinput_fake);
974 if (!uinput) {
975 udscs_destroy_server(server);
976 return 1;
978 #endif
980 if (want_session_info)
981 session_info = session_info_create(debug);
982 if (!session_info)
983 syslog(LOG_WARNING, "no session info, max 1 session agent allowed");
985 active_xfers = g_hash_table_new(g_direct_hash, g_direct_equal);
986 main_loop();
988 release_clipboards();
990 vdagentd_uinput_destroy(&uinput);
991 vdagent_virtio_port_flush(&virtio_port);
992 vdagent_virtio_port_destroy(&virtio_port);
993 session_info_destroy(session_info);
994 udscs_destroy_server(server);
995 if (unlink(vdagentd_socket) != 0)
996 syslog(LOG_ERR, "unlink %s: %s", vdagentd_socket, strerror(errno));
997 syslog(LOG_INFO, "vdagentd quiting, returning status %d", retval);
999 if (do_daemonize)
1000 unlink(pidfilename);
1002 return retval;