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 "vdagent-virtio-port.h"
42 #include "console-kit.h"
51 static const char *logfilename
= "/var/log/spice-vdagentd/spice-vdagentd.log";
52 static const char *pidfilename
= "/var/run/spice-vdagentd/spice-vdagentd.pid";
53 static const char *portdev
= "/dev/virtio-ports/com.redhat.spice.0";
54 static const char *uinput_device
= "/dev/uinput";
56 static struct udscs_server
*server
= NULL
;
57 static struct vdagent_virtio_port
*virtio_port
= NULL
;
58 #ifdef HAVE_CONSOLE_KIT
59 static struct console_kit
*console_kit
= NULL
;
61 static struct vdagentd_uinput
*uinput
= NULL
;
62 static VDAgentMonitorsConfig
*mon_config
= NULL
;
63 static uint32_t *capabilities
= NULL
;
64 static int capabilities_size
= 0;
65 #ifdef HAVE_CONSOLE_KIT
66 static const char *active_session
= NULL
;
68 static unsigned int session_count
= 0;
70 static struct udscs_connection
*active_session_conn
= NULL
;
71 static int agent_owns_clipboard
[256] = { 0, };
72 static FILE *logfile
= NULL
;
74 static int retval
= 0;
76 /* utility functions */
77 /* vdagentd <-> spice-client communication handling */
78 static void send_capabilities(struct vdagent_virtio_port
*vport
,
81 VDAgentAnnounceCapabilities
*caps
;
84 size
= sizeof(*caps
) + VD_AGENT_CAPS_BYTES
;
85 caps
= calloc(1, size
);
88 "out of memory allocating capabilities array (write)\n");
92 caps
->request
= request
;
93 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MOUSE_STATE
);
94 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MONITORS_CONFIG
);
95 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_REPLY
);
96 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
);
97 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_CLIPBOARD_SELECTION
);
99 vdagent_virtio_port_write(vport
, VDP_CLIENT_PORT
,
100 VD_AGENT_ANNOUNCE_CAPABILITIES
, 0,
101 (uint8_t *)caps
, size
);
105 static void do_client_monitors(struct vdagent_virtio_port
*vport
, int port_nr
,
106 VDAgentMessage
*message_header
, VDAgentMonitorsConfig
*new_monitors
)
111 /* Store monitor config to send to agents when they connect */
112 size
= sizeof(VDAgentMonitorsConfig
) +
113 new_monitors
->num_of_monitors
* sizeof(VDAgentMonConfig
);
114 if (message_header
->size
!= size
) {
115 fprintf(logfile
, "invalid message size for VDAgentMonitorsConfig\n");
120 mon_config
->num_of_monitors
!= new_monitors
->num_of_monitors
) {
122 mon_config
= malloc(size
);
124 fprintf(logfile
, "out of memory allocating monitors config\n");
128 memcpy(mon_config
, new_monitors
, size
);
130 /* Send monitor config to currently connected agents */
131 udscs_server_write_all(server
, VDAGENTD_MONITORS_CONFIG
, 0, 0,
132 (uint8_t *)mon_config
, size
);
134 /* Acknowledge reception of monitors config to spice server / client */
135 reply
.type
= VD_AGENT_MONITORS_CONFIG
;
136 reply
.error
= VD_AGENT_SUCCESS
;
137 vdagent_virtio_port_write(vport
, port_nr
, VD_AGENT_REPLY
, 0,
138 (uint8_t *)&reply
, sizeof(reply
));
141 static void do_client_capabilities(struct vdagent_virtio_port
*vport
,
142 VDAgentMessage
*message_header
,
143 VDAgentAnnounceCapabilities
*caps
)
145 int new_size
= VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header
->size
);
147 if (capabilities_size
!= new_size
) {
148 capabilities_size
= new_size
;
150 capabilities
= malloc(capabilities_size
* sizeof(uint32_t));
153 "out of memory allocating capabilities array (read)\n");
154 capabilities_size
= 0;
158 memcpy(capabilities
, caps
->caps
, capabilities_size
* sizeof(uint32_t));
160 send_capabilities(vport
, 0);
163 static void do_client_clipboard(struct vdagent_virtio_port
*vport
,
164 VDAgentMessage
*message_header
, uint8_t *data
)
166 uint32_t msg_type
= 0, data_type
= 0, size
= message_header
->size
;
167 uint8_t selection
= VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD
;
169 if (!active_session_conn
) {
171 "Could not find an agent connnection belonging to the "
172 "active session, ignoring client clipboard request\n");
176 if (VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
177 VD_AGENT_CAP_CLIPBOARD_SELECTION
)) {
183 switch (message_header
->type
) {
184 case VD_AGENT_CLIPBOARD_GRAB
:
185 msg_type
= VDAGENTD_CLIPBOARD_GRAB
;
186 agent_owns_clipboard
[selection
] = 0;
188 case VD_AGENT_CLIPBOARD_REQUEST
: {
189 VDAgentClipboardRequest
*req
= (VDAgentClipboardRequest
*)data
;
190 msg_type
= VDAGENTD_CLIPBOARD_REQUEST
;
191 data_type
= req
->type
;
196 case VD_AGENT_CLIPBOARD
: {
197 VDAgentClipboard
*clipboard
= (VDAgentClipboard
*)data
;
198 msg_type
= VDAGENTD_CLIPBOARD_DATA
;
199 data_type
= clipboard
->type
;
200 size
= size
- sizeof(VDAgentClipboard
);
201 data
= clipboard
->data
;
204 case VD_AGENT_CLIPBOARD_RELEASE
:
205 msg_type
= VDAGENTD_CLIPBOARD_RELEASE
;
211 udscs_write(active_session_conn
, msg_type
, selection
, data_type
,
215 int virtio_port_read_complete(
216 struct vdagent_virtio_port
*vport
,
218 VDAgentMessage
*message_header
,
221 uint32_t min_size
= 0;
223 if (message_header
->protocol
!= VD_AGENT_PROTOCOL
) {
224 fprintf(logfile
, "message with wrong protocol version ignoring\n");
228 switch (message_header
->type
) {
229 case VD_AGENT_MOUSE_STATE
:
230 if (message_header
->size
!= sizeof(VDAgentMouseState
))
232 vdagentd_uinput_do_mouse(&uinput
, (VDAgentMouseState
*)data
);
234 /* Try to re-open the tablet */
235 struct agent_data
*agent_data
=
236 udscs_get_user_data(active_session_conn
);
238 uinput
= vdagentd_uinput_create(uinput_device
,
243 fprintf(logfile
, "Fatal uinput error\n");
249 case VD_AGENT_MONITORS_CONFIG
:
250 if (message_header
->size
< sizeof(VDAgentMonitorsConfig
))
252 do_client_monitors(vport
, port_nr
, message_header
,
253 (VDAgentMonitorsConfig
*)data
);
255 case VD_AGENT_ANNOUNCE_CAPABILITIES
:
256 if (message_header
->size
< sizeof(VDAgentAnnounceCapabilities
))
258 do_client_capabilities(vport
, message_header
,
259 (VDAgentAnnounceCapabilities
*)data
);
261 case VD_AGENT_CLIPBOARD_GRAB
:
262 case VD_AGENT_CLIPBOARD_REQUEST
:
263 case VD_AGENT_CLIPBOARD
:
264 case VD_AGENT_CLIPBOARD_RELEASE
:
265 switch (message_header
->type
) {
266 case VD_AGENT_CLIPBOARD_GRAB
:
267 min_size
= sizeof(VDAgentClipboardGrab
); break;
268 case VD_AGENT_CLIPBOARD_REQUEST
:
269 min_size
= sizeof(VDAgentClipboardRequest
); break;
270 case VD_AGENT_CLIPBOARD
:
271 min_size
= sizeof(VDAgentClipboard
); break;
273 if (VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
274 VD_AGENT_CAP_CLIPBOARD_SELECTION
)) {
277 if (message_header
->size
< min_size
) {
280 do_client_clipboard(vport
, message_header
, data
);
284 fprintf(logfile
, "unknown message type %d\n", message_header
->type
);
291 fprintf(logfile
, "read: invalid message size: %u for message type: %u\n",
292 message_header
->size
, message_header
->type
);
296 /* vdagentd <-> vdagent communication handling */
297 int do_agent_clipboard(struct udscs_connection
*conn
,
298 struct udscs_message_header
*header
, const uint8_t *data
)
300 uint8_t selection
= header
->arg1
;
301 uint32_t msg_type
= 0, data_type
= -1, size
= header
->size
;
303 if (!VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
304 VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
))
307 /* Check that this agent is from the currently active session */
308 if (conn
!= active_session_conn
) {
309 fprintf(logfile
, "Clipboard request from agent "
310 "which is not in the active session?\n");
316 "Clipboard request from agent but no client connection\n");
320 if (!VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
321 VD_AGENT_CAP_CLIPBOARD_SELECTION
) &&
322 selection
!= VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD
) {
326 switch (header
->type
) {
327 case VDAGENTD_CLIPBOARD_GRAB
:
328 msg_type
= VD_AGENT_CLIPBOARD_GRAB
;
329 agent_owns_clipboard
[selection
] = 1;
331 case VDAGENTD_CLIPBOARD_REQUEST
:
332 msg_type
= VD_AGENT_CLIPBOARD_REQUEST
;
333 data_type
= header
->arg2
;
336 case VDAGENTD_CLIPBOARD_DATA
:
337 msg_type
= VD_AGENT_CLIPBOARD
;
338 data_type
= header
->arg2
;
340 case VDAGENTD_CLIPBOARD_RELEASE
:
341 msg_type
= VD_AGENT_CLIPBOARD_RELEASE
;
343 agent_owns_clipboard
[selection
] = 0;
347 if (size
!= header
->size
) {
349 "unexpected extra data in clipboard msg, disconnecting agent\n");
353 if (VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
354 VD_AGENT_CAP_CLIPBOARD_SELECTION
)) {
357 if (data_type
!= -1) {
361 vdagent_virtio_port_write_start(virtio_port
, VDP_CLIENT_PORT
, msg_type
,
364 if (VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
365 VD_AGENT_CAP_CLIPBOARD_SELECTION
)) {
366 uint8_t sel
[4] = { selection
, 0, 0, 0 };
367 vdagent_virtio_port_write_append(virtio_port
, sel
, 4);
369 if (data_type
!= -1) {
370 vdagent_virtio_port_write_append(virtio_port
, (uint8_t*)&data_type
, 4);
373 vdagent_virtio_port_write_append(virtio_port
, data
, header
->size
);
378 if (header
->type
== VDAGENTD_CLIPBOARD_REQUEST
) {
379 /* Let the agent know no answer is coming */
380 udscs_write(conn
, VDAGENTD_CLIPBOARD_DATA
,
381 selection
, VD_AGENT_CLIPBOARD_NONE
, NULL
, 0);
386 /* When we open the vdagent virtio channel, the server automatically goes into
387 client mouse mode, so we can only have the channel open when we know the
388 active session resolution. This function checks that we have an agent in the
389 active session, and that it has told us its resolution. If these conditions
390 are met it sets the uinput tablet device's resolution and opens the virtio
391 channel (if it is not already open). If these conditions are not met, it
393 static void check_xorg_resolution(void)
395 struct agent_data
*agent_data
= udscs_get_user_data(active_session_conn
);
397 if (agent_data
&& agent_data
->width
) {
399 uinput
= vdagentd_uinput_create(uinput_device
,
404 vdagentd_uinput_update_size(&uinput
, agent_data
->width
,
407 fprintf(logfile
, "Fatal uinput error\n");
414 fprintf(logfile
, "opening vdagent virtio channel\n");
415 virtio_port
= vdagent_virtio_port_create(portdev
,
416 virtio_port_read_complete
,
420 "Fatal error opening vdagent virtio channel\n");
425 send_capabilities(virtio_port
, 1);
428 vdagentd_uinput_destroy(&uinput
);
430 vdagent_virtio_port_flush(&virtio_port
);
431 vdagent_virtio_port_destroy(&virtio_port
);
432 fprintf(logfile
, "closed vdagent virtio channel\n");
437 #ifdef HAVE_CONSOLE_KIT
438 static int connection_matches_active_session(struct udscs_connection
**connp
,
441 struct udscs_connection
**conn_ret
= (struct udscs_connection
**)priv
;
442 struct agent_data
*agent_data
= udscs_get_user_data(*connp
);
444 /* Check if this connection matches the currently active session */
445 if (!agent_data
->session
|| !active_session
)
447 if (strcmp(agent_data
->session
, active_session
))
455 void release_clipboards(void)
459 for (sel
= 0; sel
< VD_AGENT_CLIPBOARD_SELECTION_SECONDARY
; ++sel
) {
460 if (agent_owns_clipboard
[sel
] && virtio_port
) {
461 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
462 VD_AGENT_CLIPBOARD_RELEASE
, 0, &sel
, 1);
464 agent_owns_clipboard
[sel
] = 0;
468 void update_active_session_connection(void)
470 struct udscs_connection
*new_conn
= NULL
;
473 #ifdef HAVE_CONSOLE_KIT
475 active_session
= console_kit_get_active_session(console_kit
);
477 n
= udscs_server_for_all_clients(server
, connection_matches_active_session
,
482 if (new_conn
== active_session_conn
)
485 active_session_conn
= new_conn
;
488 release_clipboards();
490 check_xorg_resolution();
493 void agent_connect(struct udscs_connection
*conn
)
496 struct agent_data
*agent_data
;
498 agent_data
= calloc(1, sizeof(*agent_data
));
500 fprintf(logfile
, "Out of memory allocating agent data, disconnecting\n");
501 udscs_destroy_connection(&conn
);
504 #ifdef HAVE_CONSOLE_KIT
505 pid
= udscs_get_peer_cred(conn
).pid
;
506 agent_data
->session
= console_kit_session_for_pid(console_kit
, pid
);
509 if (session_count
== 1)
510 active_session_conn
= conn
;
512 /* disable communication with agents when we've got multiple
513 * connections to the vdagentd and no consolekit since we can't
514 * know to which one we should send data
516 fprintf(logfile
, "Trying to use multiple vdagent without ConsoleKit support, "
517 "disabling vdagent to avoid potential information leak\n");
518 active_session_conn
= NULL
;
522 udscs_set_user_data(conn
, (void *)agent_data
);
523 update_active_session_connection();
525 udscs_write(conn
, VDAGENTD_VERSION
, 0, 0,
526 (uint8_t *)VERSION
, strlen(VERSION
) + 1);
529 udscs_write(conn
, VDAGENTD_MONITORS_CONFIG
, 0, 0,
530 (uint8_t *)mon_config
, sizeof(VDAgentMonitorsConfig
) +
531 mon_config
->num_of_monitors
* sizeof(VDAgentMonConfig
));
534 void agent_disconnect(struct udscs_connection
*conn
)
536 struct agent_data
*agent_data
= udscs_get_user_data(conn
);
538 free(agent_data
->session
);
539 agent_data
->session
= NULL
;
540 update_active_session_connection();
543 #ifndef HAVE_CONSOLE_KIT
548 void agent_read_complete(struct udscs_connection
**connp
,
549 struct udscs_message_header
*header
, uint8_t *data
)
551 struct agent_data
*agent_data
= udscs_get_user_data(*connp
);
553 switch (header
->type
) {
554 case VDAGENTD_GUEST_XORG_RESOLUTION
: {
555 struct vdagentd_guest_xorg_resolution
*res
=
556 (struct vdagentd_guest_xorg_resolution
*)data
;
558 if (header
->size
!= sizeof(*res
)) {
560 "guest xorg resolution message has wrong size, disconnecting agent\n");
561 udscs_destroy_connection(connp
);
565 agent_data
->width
= res
->width
;
566 agent_data
->height
= res
->height
;
567 check_xorg_resolution();
570 case VDAGENTD_CLIPBOARD_GRAB
:
571 case VDAGENTD_CLIPBOARD_REQUEST
:
572 case VDAGENTD_CLIPBOARD_DATA
:
573 case VDAGENTD_CLIPBOARD_RELEASE
:
574 if (do_agent_clipboard(*connp
, header
, data
)) {
575 udscs_destroy_connection(connp
);
580 fprintf(logfile
, "unknown message from vdagent: %u, ignoring\n",
588 static void usage(FILE *fp
)
593 " -h print this text\n"
594 " -d log debug messages (use twice for extra info)\n"
595 " -s <port> set virtio serial port [%s]\n"
596 " -u <dev> set uinput device [%s]\n"
597 " -x don't daemonize (and log to logfile)\n",
598 portdev
, uinput_device
);
606 /* detach from terminal */
609 close(0); close(1); close(2);
611 x
= open("/dev/null", O_RDWR
); dup(x
); dup(x
);
612 pidfile
= fopen(pidfilename
, "w");
614 fprintf(pidfile
, "%d\n", (int)getpid());
619 fprintf(logfile
, "fork: %s\n", strerror(errno
));
622 udscs_destroy_server(server
);
623 if (logfile
!= stderr
)
631 fd_set readfds
, writefds
;
632 int n
, nfds
, ck_fd
= 0;
638 nfds
= udscs_server_fill_fds(server
, &readfds
, &writefds
);
639 n
= vdagent_virtio_port_fill_fds(virtio_port
, &readfds
, &writefds
);
643 #ifdef HAVE_CONSOLE_KIT
644 ck_fd
= console_kit_get_fd(console_kit
);
645 FD_SET(ck_fd
, &readfds
);
650 n
= select(nfds
, &readfds
, &writefds
, NULL
, NULL
);
654 fprintf(logfile
, "Fatal error select: %s\n", strerror(errno
));
659 udscs_server_handle_fds(server
, &readfds
, &writefds
);
662 vdagent_virtio_port_handle_fds(&virtio_port
, &readfds
, &writefds
);
665 "AIIEEE lost spice client connection, reconnecting\n");
666 virtio_port
= vdagent_virtio_port_create(portdev
,
667 virtio_port_read_complete
,
672 "Fatal error opening vdagent virtio channel\n");
678 #ifdef HAVE_CONSOLE_KIT
679 if (FD_ISSET(ck_fd
, &readfds
)) {
680 active_session
= console_kit_get_active_session(console_kit
);
681 update_active_session_connection();
688 static void quit_handler(int sig
)
693 int main(int argc
, char *argv
[])
696 int do_daemonize
= 1;
697 struct sigaction act
;
700 if (-1 == (c
= getopt(argc
, argv
, "-dhxs:u:")))
710 uinput_device
= optarg
;
724 memset(&act
, 0, sizeof(act
));
725 act
.sa_flags
= SA_RESTART
;
726 act
.sa_handler
= quit_handler
;
727 sigaction(SIGINT
, &act
, NULL
);
728 sigaction(SIGHUP
, &act
, NULL
);
729 sigaction(SIGTERM
, &act
, NULL
);
730 sigaction(SIGQUIT
, &act
, NULL
);
733 logfile
= fopen(logfilename
, "a");
735 fprintf(stderr
, "Error opening %s: %s\n", logfilename
,
742 /* Setup communication with vdagent process(es) */
743 server
= udscs_create_server(VDAGENTD_SOCKET
, agent_connect
,
744 agent_read_complete
, agent_disconnect
,
745 vdagentd_messages
, VDAGENTD_NO_MESSAGES
,
746 debug
? logfile
:NULL
, logfile
);
748 fprintf(logfile
, "Fatal could not create server socket %s\n",
750 if (logfile
!= stderr
)
754 if (chmod(VDAGENTD_SOCKET
, 0666)) {
755 fprintf(logfile
, "Fatal could not change permissions on %s: %s\n",
756 VDAGENTD_SOCKET
, strerror(errno
));
757 udscs_destroy_server(server
);
758 if (logfile
!= stderr
)
766 #ifdef HAVE_CONSOLE_KIT
767 console_kit
= console_kit_create(logfile
);
769 fprintf(logfile
, "Fatal could not connect to console kit\n");
770 udscs_destroy_server(server
);
771 if (logfile
!= stderr
)
779 release_clipboards();
781 vdagentd_uinput_destroy(&uinput
);
782 vdagent_virtio_port_flush(&virtio_port
);
783 vdagent_virtio_port_destroy(&virtio_port
);
784 #ifdef HAVE_CONSOLE_KIT
785 console_kit_destroy(console_kit
);
787 udscs_destroy_server(server
);
788 if (unlink(VDAGENTD_SOCKET
) != 0)
789 fprintf(logfile
, "unlink %s: %s\n", VDAGENTD_SOCKET
, strerror(errno
));
790 fprintf(logfile
, "vdagentd quiting, returning status %d\n", retval
);
791 if (logfile
!= stderr
)