1 /* vdagentd.c vdagentd (daemon) code
3 Copyright 2010 Red Hat, Inc.
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/>.
33 #include <sys/select.h>
35 #include <spice/vd_agent.h>
38 #include "vdagentd-proto.h"
39 #include "vdagentd-proto-strings.h"
40 #include "vdagentd-uinput.h"
41 #include "vdagentd-xorg-conf.h"
42 #include "vdagent-virtio-port.h"
43 #include "console-kit.h"
49 struct vdagentd_guest_xorg_resolution
*screen_info
;
54 static const char *logfilename
= "/var/log/spice-vdagentd/spice-vdagentd.log";
55 static const char *pidfilename
= "/var/run/spice-vdagentd/spice-vdagentd.pid";
56 static const char *portdev
= "/dev/virtio-ports/com.redhat.spice.0";
57 static const char *uinput_device
= "/dev/uinput";
59 static struct udscs_server
*server
= NULL
;
60 static struct vdagent_virtio_port
*virtio_port
= NULL
;
61 #ifdef HAVE_CONSOLE_KIT
62 static struct console_kit
*console_kit
= NULL
;
64 static struct vdagentd_uinput
*uinput
= NULL
;
65 static VDAgentMonitorsConfig
*mon_config
= NULL
;
66 static uint32_t *capabilities
= NULL
;
67 static int capabilities_size
= 0;
68 #ifdef HAVE_CONSOLE_KIT
69 static const char *active_session
= NULL
;
71 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 FILE *logfile
= NULL
;
77 static int retval
= 0;
79 /* utility functions */
80 /* vdagentd <-> spice-client communication handling */
81 static void send_capabilities(struct vdagent_virtio_port
*vport
,
84 VDAgentAnnounceCapabilities
*caps
;
87 size
= sizeof(*caps
) + VD_AGENT_CAPS_BYTES
;
88 caps
= calloc(1, size
);
91 "out of memory allocating capabilities array (write)\n");
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
);
102 vdagent_virtio_port_write(vport
, VDP_CLIENT_PORT
,
103 VD_AGENT_ANNOUNCE_CAPABILITIES
, 0,
104 (uint8_t *)caps
, size
);
108 static void do_client_monitors(struct vdagent_virtio_port
*vport
, int port_nr
,
109 VDAgentMessage
*message_header
, VDAgentMonitorsConfig
*new_monitors
)
114 /* Store monitor config to send to agents when they connect */
115 size
= sizeof(VDAgentMonitorsConfig
) +
116 new_monitors
->num_of_monitors
* sizeof(VDAgentMonConfig
);
117 if (message_header
->size
!= size
) {
118 fprintf(logfile
, "invalid message size for VDAgentMonitorsConfig\n");
122 vdagentd_write_xorg_conf(new_monitors
, logfile
);
124 if (new_monitors
->num_of_monitors
!= 1) {
125 /* No use in sending this to the session agent it cannot handle it
133 mon_config
->num_of_monitors
!= new_monitors
->num_of_monitors
) {
135 mon_config
= malloc(size
);
137 fprintf(logfile
, "out of memory allocating monitors config\n");
141 memcpy(mon_config
, new_monitors
, size
);
143 /* Send monitor config to currently connected agents */
144 udscs_server_write_all(server
, VDAGENTD_MONITORS_CONFIG
, 0, 0,
145 (uint8_t *)mon_config
, size
);
148 /* Acknowledge reception of monitors config to spice server / client */
149 reply
.type
= VD_AGENT_MONITORS_CONFIG
;
150 reply
.error
= VD_AGENT_SUCCESS
;
151 vdagent_virtio_port_write(vport
, port_nr
, VD_AGENT_REPLY
, 0,
152 (uint8_t *)&reply
, sizeof(reply
));
155 static void do_client_capabilities(struct vdagent_virtio_port
*vport
,
156 VDAgentMessage
*message_header
,
157 VDAgentAnnounceCapabilities
*caps
)
159 int new_size
= VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header
->size
);
161 if (capabilities_size
!= new_size
) {
162 capabilities_size
= new_size
;
164 capabilities
= malloc(capabilities_size
* sizeof(uint32_t));
167 "out of memory allocating capabilities array (read)\n");
168 capabilities_size
= 0;
172 memcpy(capabilities
, caps
->caps
, capabilities_size
* sizeof(uint32_t));
174 send_capabilities(vport
, 0);
177 static void do_client_clipboard(struct vdagent_virtio_port
*vport
,
178 VDAgentMessage
*message_header
, uint8_t *data
)
180 uint32_t msg_type
= 0, data_type
= 0, size
= message_header
->size
;
181 uint8_t selection
= VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD
;
183 if (!active_session_conn
) {
185 "Could not find an agent connnection belonging to the "
186 "active session, ignoring client clipboard request\n");
190 if (VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
191 VD_AGENT_CAP_CLIPBOARD_SELECTION
)) {
197 switch (message_header
->type
) {
198 case VD_AGENT_CLIPBOARD_GRAB
:
199 msg_type
= VDAGENTD_CLIPBOARD_GRAB
;
200 agent_owns_clipboard
[selection
] = 0;
202 case VD_AGENT_CLIPBOARD_REQUEST
: {
203 VDAgentClipboardRequest
*req
= (VDAgentClipboardRequest
*)data
;
204 msg_type
= VDAGENTD_CLIPBOARD_REQUEST
;
205 data_type
= req
->type
;
210 case VD_AGENT_CLIPBOARD
: {
211 VDAgentClipboard
*clipboard
= (VDAgentClipboard
*)data
;
212 msg_type
= VDAGENTD_CLIPBOARD_DATA
;
213 data_type
= clipboard
->type
;
214 size
= size
- sizeof(VDAgentClipboard
);
215 data
= clipboard
->data
;
218 case VD_AGENT_CLIPBOARD_RELEASE
:
219 msg_type
= VDAGENTD_CLIPBOARD_RELEASE
;
225 udscs_write(active_session_conn
, msg_type
, selection
, data_type
,
229 int virtio_port_read_complete(
230 struct vdagent_virtio_port
*vport
,
232 VDAgentMessage
*message_header
,
235 uint32_t min_size
= 0;
237 if (message_header
->protocol
!= VD_AGENT_PROTOCOL
) {
238 fprintf(logfile
, "message with wrong protocol version ignoring\n");
242 switch (message_header
->type
) {
243 case VD_AGENT_MOUSE_STATE
:
244 if (message_header
->size
!= sizeof(VDAgentMouseState
))
246 vdagentd_uinput_do_mouse(&uinput
, (VDAgentMouseState
*)data
);
248 /* Try to re-open the tablet */
249 struct agent_data
*agent_data
=
250 udscs_get_user_data(active_session_conn
);
252 uinput
= vdagentd_uinput_create(uinput_device
,
255 agent_data
->screen_info
,
256 agent_data
->screen_count
,
259 fprintf(logfile
, "Fatal uinput error\n");
265 case VD_AGENT_MONITORS_CONFIG
:
266 if (message_header
->size
< sizeof(VDAgentMonitorsConfig
))
268 do_client_monitors(vport
, port_nr
, message_header
,
269 (VDAgentMonitorsConfig
*)data
);
271 case VD_AGENT_ANNOUNCE_CAPABILITIES
:
272 if (message_header
->size
< sizeof(VDAgentAnnounceCapabilities
))
274 do_client_capabilities(vport
, message_header
,
275 (VDAgentAnnounceCapabilities
*)data
);
277 case VD_AGENT_CLIPBOARD_GRAB
:
278 case VD_AGENT_CLIPBOARD_REQUEST
:
279 case VD_AGENT_CLIPBOARD
:
280 case VD_AGENT_CLIPBOARD_RELEASE
:
281 switch (message_header
->type
) {
282 case VD_AGENT_CLIPBOARD_GRAB
:
283 min_size
= sizeof(VDAgentClipboardGrab
); break;
284 case VD_AGENT_CLIPBOARD_REQUEST
:
285 min_size
= sizeof(VDAgentClipboardRequest
); break;
286 case VD_AGENT_CLIPBOARD
:
287 min_size
= sizeof(VDAgentClipboard
); break;
289 if (VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
290 VD_AGENT_CAP_CLIPBOARD_SELECTION
)) {
293 if (message_header
->size
< min_size
) {
296 do_client_clipboard(vport
, message_header
, data
);
300 fprintf(logfile
, "unknown message type %d\n", message_header
->type
);
307 fprintf(logfile
, "read: invalid message size: %u for message type: %u\n",
308 message_header
->size
, message_header
->type
);
312 /* vdagentd <-> vdagent communication handling */
313 int do_agent_clipboard(struct udscs_connection
*conn
,
314 struct udscs_message_header
*header
, const uint8_t *data
)
316 uint8_t selection
= header
->arg1
;
317 uint32_t msg_type
= 0, data_type
= -1, size
= header
->size
;
319 if (!VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
320 VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
))
323 /* Check that this agent is from the currently active session */
324 if (conn
!= active_session_conn
) {
325 fprintf(logfile
, "Clipboard request from agent "
326 "which is not in the active session?\n");
332 "Clipboard request from agent but no client connection\n");
336 if (!VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
337 VD_AGENT_CAP_CLIPBOARD_SELECTION
) &&
338 selection
!= VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD
) {
342 switch (header
->type
) {
343 case VDAGENTD_CLIPBOARD_GRAB
:
344 msg_type
= VD_AGENT_CLIPBOARD_GRAB
;
345 agent_owns_clipboard
[selection
] = 1;
347 case VDAGENTD_CLIPBOARD_REQUEST
:
348 msg_type
= VD_AGENT_CLIPBOARD_REQUEST
;
349 data_type
= header
->arg2
;
352 case VDAGENTD_CLIPBOARD_DATA
:
353 msg_type
= VD_AGENT_CLIPBOARD
;
354 data_type
= header
->arg2
;
356 case VDAGENTD_CLIPBOARD_RELEASE
:
357 msg_type
= VD_AGENT_CLIPBOARD_RELEASE
;
359 agent_owns_clipboard
[selection
] = 0;
363 if (size
!= header
->size
) {
365 "unexpected extra data in clipboard msg, disconnecting agent\n");
369 if (VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
370 VD_AGENT_CAP_CLIPBOARD_SELECTION
)) {
373 if (data_type
!= -1) {
377 vdagent_virtio_port_write_start(virtio_port
, VDP_CLIENT_PORT
, msg_type
,
380 if (VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
381 VD_AGENT_CAP_CLIPBOARD_SELECTION
)) {
382 uint8_t sel
[4] = { selection
, 0, 0, 0 };
383 vdagent_virtio_port_write_append(virtio_port
, sel
, 4);
385 if (data_type
!= -1) {
386 vdagent_virtio_port_write_append(virtio_port
, (uint8_t*)&data_type
, 4);
389 vdagent_virtio_port_write_append(virtio_port
, data
, header
->size
);
394 if (header
->type
== VDAGENTD_CLIPBOARD_REQUEST
) {
395 /* Let the agent know no answer is coming */
396 udscs_write(conn
, VDAGENTD_CLIPBOARD_DATA
,
397 selection
, VD_AGENT_CLIPBOARD_NONE
, NULL
, 0);
402 /* When we open the vdagent virtio channel, the server automatically goes into
403 client mouse mode, so we can only have the channel open when we know the
404 active session resolution. This function checks that we have an agent in the
405 active session, and that it has told us its resolution. If these conditions
406 are met it sets the uinput tablet device's resolution and opens the virtio
407 channel (if it is not already open). If these conditions are not met, it
409 static void check_xorg_resolution(void)
411 struct agent_data
*agent_data
= udscs_get_user_data(active_session_conn
);
413 if (agent_data
&& agent_data
->screen_info
) {
415 uinput
= vdagentd_uinput_create(uinput_device
,
418 agent_data
->screen_info
,
419 agent_data
->screen_count
,
422 vdagentd_uinput_update_size(&uinput
,
425 agent_data
->screen_info
,
426 agent_data
->screen_count
);
428 fprintf(logfile
, "Fatal uinput error\n");
435 fprintf(logfile
, "opening vdagent virtio channel\n");
436 virtio_port
= vdagent_virtio_port_create(portdev
,
437 virtio_port_read_complete
,
441 "Fatal error opening vdagent virtio channel\n");
446 send_capabilities(virtio_port
, 1);
449 vdagentd_uinput_destroy(&uinput
);
451 vdagent_virtio_port_flush(&virtio_port
);
452 vdagent_virtio_port_destroy(&virtio_port
);
453 fprintf(logfile
, "closed vdagent virtio channel\n");
458 #ifdef HAVE_CONSOLE_KIT
459 static int connection_matches_active_session(struct udscs_connection
**connp
,
462 struct udscs_connection
**conn_ret
= (struct udscs_connection
**)priv
;
463 struct agent_data
*agent_data
= udscs_get_user_data(*connp
);
465 /* Check if this connection matches the currently active session */
466 if (!agent_data
->session
|| !active_session
)
468 if (strcmp(agent_data
->session
, active_session
))
476 void release_clipboards(void)
480 for (sel
= 0; sel
< VD_AGENT_CLIPBOARD_SELECTION_SECONDARY
; ++sel
) {
481 if (agent_owns_clipboard
[sel
] && virtio_port
) {
482 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
483 VD_AGENT_CLIPBOARD_RELEASE
, 0, &sel
, 1);
485 agent_owns_clipboard
[sel
] = 0;
489 void update_active_session_connection(void)
491 struct udscs_connection
*new_conn
= NULL
;
494 #ifdef HAVE_CONSOLE_KIT
496 active_session
= console_kit_get_active_session(console_kit
);
498 n
= udscs_server_for_all_clients(server
, connection_matches_active_session
,
503 if (new_conn
== active_session_conn
)
506 active_session_conn
= new_conn
;
509 release_clipboards();
511 check_xorg_resolution();
514 void agent_connect(struct udscs_connection
*conn
)
517 struct agent_data
*agent_data
;
519 agent_data
= calloc(1, sizeof(*agent_data
));
521 fprintf(logfile
, "Out of memory allocating agent data, disconnecting\n");
522 udscs_destroy_connection(&conn
);
525 #ifdef HAVE_CONSOLE_KIT
526 pid
= udscs_get_peer_cred(conn
).pid
;
527 agent_data
->session
= console_kit_session_for_pid(console_kit
, pid
);
530 if (session_count
== 1) {
531 active_session_conn
= conn
;
533 /* disable communication with agents when we've got multiple
534 * connections to the vdagentd and no consolekit since we can't
535 * know to which one we should send data
537 fprintf(logfile
, "Trying to use multiple vdagent without ConsoleKit support, "
538 "disabling vdagent to avoid potential information leak\n");
539 active_session_conn
= NULL
;
543 udscs_set_user_data(conn
, (void *)agent_data
);
544 update_active_session_connection();
546 udscs_write(conn
, VDAGENTD_VERSION
, 0, 0,
547 (uint8_t *)VERSION
, strlen(VERSION
) + 1);
550 udscs_write(conn
, VDAGENTD_MONITORS_CONFIG
, 0, 0,
551 (uint8_t *)mon_config
, sizeof(VDAgentMonitorsConfig
) +
552 mon_config
->num_of_monitors
* sizeof(VDAgentMonConfig
));
555 void agent_disconnect(struct udscs_connection
*conn
)
557 struct agent_data
*agent_data
= udscs_get_user_data(conn
);
559 free(agent_data
->session
);
560 agent_data
->session
= NULL
;
561 update_active_session_connection();
564 #ifndef HAVE_CONSOLE_KIT
569 void agent_read_complete(struct udscs_connection
**connp
,
570 struct udscs_message_header
*header
, uint8_t *data
)
572 struct agent_data
*agent_data
= udscs_get_user_data(*connp
);
574 switch (header
->type
) {
575 case VDAGENTD_GUEST_XORG_RESOLUTION
: {
576 struct vdagentd_guest_xorg_resolution
*res
;
577 int n
= header
->size
/ sizeof(*res
);
579 /* Detect older version session agent, but don't disconnect, as
580 that stops it from getting the VDAGENTD_VERSION message, and then
581 it will never re-exec the new version... */
582 if (header
->arg1
== 0 && header
->arg2
== 0) {
583 fprintf(logfile
, "got old session agent xorg resolution message, ignoring\n");
588 if (header
->size
!= n
* sizeof(*res
)) {
590 "guest xorg resolution message has wrong size, disconnecting agent\n");
591 udscs_destroy_connection(connp
);
596 free(agent_data
->screen_info
);
597 res
= malloc(n
* sizeof(*res
));
599 fprintf(logfile
, "out of memory allocating screen info\n");
602 memcpy(res
, data
, n
* sizeof(*res
));
603 agent_data
->width
= header
->arg1
;
604 agent_data
->height
= header
->arg2
;
605 agent_data
->screen_info
= res
;
606 agent_data
->screen_count
= n
;
608 check_xorg_resolution();
611 case VDAGENTD_CLIPBOARD_GRAB
:
612 case VDAGENTD_CLIPBOARD_REQUEST
:
613 case VDAGENTD_CLIPBOARD_DATA
:
614 case VDAGENTD_CLIPBOARD_RELEASE
:
615 if (do_agent_clipboard(*connp
, header
, data
)) {
616 udscs_destroy_connection(connp
);
622 fprintf(logfile
, "unknown message from vdagent: %u, ignoring\n",
630 static void usage(FILE *fp
)
635 " -h print this text\n"
636 " -d log debug messages (use twice for extra info)\n"
637 " -s <port> set virtio serial port [%s]\n"
638 " -u <dev> set uinput device [%s]\n"
639 " -x don't daemonize (and log to logfile)\n",
640 portdev
, uinput_device
);
648 /* detach from terminal */
651 close(0); close(1); close(2);
653 x
= open("/dev/null", O_RDWR
); x
= dup(x
); x
= dup(x
);
654 pidfile
= fopen(pidfilename
, "w");
656 fprintf(pidfile
, "%d\n", (int)getpid());
661 fprintf(logfile
, "fork: %s\n", strerror(errno
));
664 udscs_destroy_server(server
);
665 if (logfile
!= stderr
)
673 fd_set readfds
, writefds
;
674 int n
, nfds
, ck_fd
= 0;
680 nfds
= udscs_server_fill_fds(server
, &readfds
, &writefds
);
681 n
= vdagent_virtio_port_fill_fds(virtio_port
, &readfds
, &writefds
);
685 #ifdef HAVE_CONSOLE_KIT
686 ck_fd
= console_kit_get_fd(console_kit
);
687 FD_SET(ck_fd
, &readfds
);
692 n
= select(nfds
, &readfds
, &writefds
, NULL
, NULL
);
696 fprintf(logfile
, "Fatal error select: %s\n", strerror(errno
));
701 udscs_server_handle_fds(server
, &readfds
, &writefds
);
704 vdagent_virtio_port_handle_fds(&virtio_port
, &readfds
, &writefds
);
707 "AIIEEE lost spice client connection, reconnecting\n");
708 virtio_port
= vdagent_virtio_port_create(portdev
,
709 virtio_port_read_complete
,
714 "Fatal error opening vdagent virtio channel\n");
720 #ifdef HAVE_CONSOLE_KIT
721 if (FD_ISSET(ck_fd
, &readfds
)) {
722 active_session
= console_kit_get_active_session(console_kit
);
723 update_active_session_connection();
730 static void quit_handler(int sig
)
735 int main(int argc
, char *argv
[])
738 int do_daemonize
= 1;
739 struct sigaction act
;
742 if (-1 == (c
= getopt(argc
, argv
, "-dhxs:u:")))
752 uinput_device
= optarg
;
766 memset(&act
, 0, sizeof(act
));
767 act
.sa_flags
= SA_RESTART
;
768 act
.sa_handler
= quit_handler
;
769 sigaction(SIGINT
, &act
, NULL
);
770 sigaction(SIGHUP
, &act
, NULL
);
771 sigaction(SIGTERM
, &act
, NULL
);
772 sigaction(SIGQUIT
, &act
, NULL
);
775 logfile
= fopen(logfilename
, "a");
777 fprintf(stderr
, "Error opening %s: %s\n", logfilename
,
784 /* Setup communication with vdagent process(es) */
785 server
= udscs_create_server(VDAGENTD_SOCKET
, agent_connect
,
786 agent_read_complete
, agent_disconnect
,
787 vdagentd_messages
, VDAGENTD_NO_MESSAGES
,
788 debug
? logfile
:NULL
, logfile
);
790 fprintf(logfile
, "Fatal could not create server socket %s\n",
792 if (logfile
!= stderr
)
796 if (chmod(VDAGENTD_SOCKET
, 0666)) {
797 fprintf(logfile
, "Fatal could not change permissions on %s: %s\n",
798 VDAGENTD_SOCKET
, strerror(errno
));
799 udscs_destroy_server(server
);
800 if (logfile
!= stderr
)
808 #ifdef HAVE_CONSOLE_KIT
809 console_kit
= console_kit_create(logfile
);
811 fprintf(logfile
, "Fatal could not connect to console kit\n");
812 udscs_destroy_server(server
);
813 if (logfile
!= stderr
)
821 release_clipboards();
823 vdagentd_uinput_destroy(&uinput
);
824 vdagent_virtio_port_flush(&virtio_port
);
825 vdagent_virtio_port_destroy(&virtio_port
);
826 #ifdef HAVE_CONSOLE_KIT
827 console_kit_destroy(console_kit
);
829 udscs_destroy_server(server
);
830 if (unlink(VDAGENTD_SOCKET
) != 0)
831 fprintf(logfile
, "unlink %s: %s\n", VDAGENTD_SOCKET
, strerror(errno
));
832 fprintf(logfile
, "vdagentd quiting, returning status %d\n", retval
);
833 if (logfile
!= stderr
)