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/>.
29 #include <sys/select.h>
31 #include <spice/vd_agent.h>
34 #include "vdagentd-proto.h"
35 #include "vdagentd-proto-strings.h"
36 #include "vdagentd-uinput.h"
37 #include "vdagent-virtio-port.h"
38 #include "console-kit.h"
47 static const char *logfilename
= "/var/log/vdagentd.log";
48 static const char *portdev
= "/dev/virtio-ports/com.redhat.spice.0";
49 static const char *uinput_device
= "/dev/uinput";
51 static struct udscs_server
*server
= NULL
;
52 static struct vdagent_virtio_port
*virtio_port
= NULL
;
53 static struct console_kit
*console_kit
= NULL
;
54 static struct vdagentd_uinput
*uinput
= NULL
;
55 static VDAgentMonitorsConfig
*mon_config
= NULL
;
56 static uint32_t *capabilities
= NULL
;
57 static int capabilities_size
= 0;
58 static const char *active_session
= NULL
;
59 static struct udscs_connection
*active_session_conn
= NULL
;
60 static int agent_owns_clipboard
= 0;
61 static FILE *logfile
= NULL
;
63 static int retval
= 0;
65 /* utility functions */
66 /* vdagentd <-> spice-client communication handling */
67 static void send_capabilities(struct vdagent_virtio_port
*port
,
70 VDAgentAnnounceCapabilities
*caps
;
73 size
= sizeof(*caps
) + VD_AGENT_CAPS_BYTES
;
74 caps
= calloc(1, size
);
77 "out of memory allocating capabilities array (write)\n");
81 caps
->request
= request
;
82 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MOUSE_STATE
);
83 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MONITORS_CONFIG
);
84 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_REPLY
);
85 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
);
87 vdagent_virtio_port_write(port
, VDP_CLIENT_PORT
,
88 VD_AGENT_ANNOUNCE_CAPABILITIES
, 0,
89 (uint8_t *)caps
, size
);
93 static void do_client_monitors(struct vdagent_virtio_port
*port
, int port_nr
,
94 VDAgentMessage
*message_header
, VDAgentMonitorsConfig
*new_monitors
)
99 /* Store monitor config to send to agents when they connect */
100 size
= sizeof(VDAgentMonitorsConfig
) +
101 new_monitors
->num_of_monitors
* sizeof(VDAgentMonConfig
);
102 if (message_header
->size
!= size
) {
103 fprintf(logfile
, "invalid message size for VDAgentMonitorsConfig\n");
108 mon_config
->num_of_monitors
!= new_monitors
->num_of_monitors
) {
110 mon_config
= malloc(size
);
112 fprintf(logfile
, "out of memory allocating monitors config\n");
116 memcpy(mon_config
, new_monitors
, size
);
118 /* Send monitor config to currently connected agents */
119 udscs_server_write_all(server
, VDAGENTD_MONITORS_CONFIG
, 0,
120 (uint8_t *)mon_config
, size
);
122 /* Acknowledge reception of monitors config to spice server / client */
123 reply
.type
= VD_AGENT_MONITORS_CONFIG
;
124 reply
.error
= VD_AGENT_SUCCESS
;
125 vdagent_virtio_port_write(port
, port_nr
, VD_AGENT_REPLY
, 0,
126 (uint8_t *)&reply
, sizeof(reply
));
129 static void do_client_capabilities(struct vdagent_virtio_port
*port
,
130 VDAgentMessage
*message_header
,
131 VDAgentAnnounceCapabilities
*caps
)
133 capabilities_size
= VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header
->size
);
136 capabilities
= malloc(capabilities_size
* sizeof(uint32_t));
139 "out of memory allocating capabilities array (read)\n");
140 capabilities_size
= 0;
143 memcpy(capabilities
, caps
->caps
, capabilities_size
* sizeof(uint32_t));
145 send_capabilities(port
, 0);
148 static void do_client_clipboard(struct vdagent_virtio_port
*port
,
149 VDAgentMessage
*message_header
, uint8_t *message_data
)
151 uint32_t type
= 0, opaque
= 0, size
= 0;
152 uint8_t *data
= NULL
;
154 if (!active_session_conn
) {
156 "Could not find an agent connnection belonging to the "
157 "active session, ignoring client clipboard request\n");
161 switch (message_header
->type
) {
162 case VD_AGENT_CLIPBOARD_GRAB
:
163 type
= VDAGENTD_CLIPBOARD_GRAB
;
165 size
= message_header
->size
;
166 agent_owns_clipboard
= 0;
168 case VD_AGENT_CLIPBOARD_REQUEST
: {
169 VDAgentClipboardRequest
*req
= (VDAgentClipboardRequest
*)message_data
;
170 type
= VDAGENTD_CLIPBOARD_REQUEST
;
174 case VD_AGENT_CLIPBOARD
: {
175 VDAgentClipboard
*clipboard
= (VDAgentClipboard
*)message_data
;
176 type
= VDAGENTD_CLIPBOARD_DATA
;
177 opaque
= clipboard
->type
;
178 size
= message_header
->size
- sizeof(VDAgentClipboard
);
179 data
= clipboard
->data
;
182 case VD_AGENT_CLIPBOARD_RELEASE
:
183 type
= VDAGENTD_CLIPBOARD_RELEASE
;
187 udscs_write(active_session_conn
, type
, opaque
, data
, size
);
190 int virtio_port_read_complete(
191 struct vdagent_virtio_port
*port
,
192 VDIChunkHeader
*chunk_header
,
193 VDAgentMessage
*message_header
,
196 uint32_t min_size
= 0;
198 if (message_header
->protocol
!= VD_AGENT_PROTOCOL
) {
199 fprintf(logfile
, "message with wrong protocol version ignoring\n");
203 switch (message_header
->type
) {
204 case VD_AGENT_MOUSE_STATE
:
205 if (message_header
->size
!= sizeof(VDAgentMouseState
))
207 vdagentd_uinput_do_mouse(&uinput
, (VDAgentMouseState
*)data
);
209 /* Try to re-open the tablet */
210 struct agent_data
*agent_data
=
211 udscs_get_user_data(active_session_conn
);
213 uinput
= vdagentd_uinput_create(uinput_device
,
218 fprintf(logfile
, "Fatal uinput error\n");
224 case VD_AGENT_MONITORS_CONFIG
:
225 if (message_header
->size
< sizeof(VDAgentMonitorsConfig
))
227 do_client_monitors(port
, chunk_header
->port
, message_header
,
228 (VDAgentMonitorsConfig
*)data
);
230 case VD_AGENT_ANNOUNCE_CAPABILITIES
:
231 if (message_header
->size
< sizeof(VDAgentAnnounceCapabilities
))
233 do_client_capabilities(port
, message_header
,
234 (VDAgentAnnounceCapabilities
*)data
);
236 case VD_AGENT_CLIPBOARD_GRAB
:
237 case VD_AGENT_CLIPBOARD_REQUEST
:
238 case VD_AGENT_CLIPBOARD
:
239 case VD_AGENT_CLIPBOARD_RELEASE
:
240 switch (message_header
->type
) {
241 case VD_AGENT_CLIPBOARD_GRAB
:
242 min_size
= sizeof(VDAgentClipboardGrab
); break;
243 case VD_AGENT_CLIPBOARD_REQUEST
:
244 min_size
= sizeof(VDAgentClipboardRequest
); break;
245 case VD_AGENT_CLIPBOARD
:
246 min_size
= sizeof(VDAgentClipboard
); break;
248 if (message_header
->size
< min_size
)
250 do_client_clipboard(port
, message_header
, data
);
254 fprintf(logfile
, "unknown message type %d\n", message_header
->type
);
261 fprintf(logfile
, "read: invalid message size: %u for message type: %u\n",
262 message_header
->size
, message_header
->type
);
266 /* vdagentd <-> vdagent communication handling */
267 void do_agent_clipboard(struct udscs_connection
*conn
,
268 struct udscs_message_header
*header
, const uint8_t *data
)
270 if (!VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
271 VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
))
274 /* Check that this agent is from the currently active session */
275 if (conn
!= active_session_conn
) {
276 fprintf(logfile
, "Clipboard request from agent "
277 "which is not in the active session?\n");
283 "Clipboard request from agent but no client connection\n");
287 switch (header
->type
) {
288 case VDAGENTD_CLIPBOARD_GRAB
:
289 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
290 VD_AGENT_CLIPBOARD_GRAB
, 0,
292 agent_owns_clipboard
= 1;
294 case VDAGENTD_CLIPBOARD_REQUEST
: {
295 VDAgentClipboardRequest req
= { .type
= header
->opaque
};
296 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
297 VD_AGENT_CLIPBOARD_REQUEST
, 0,
298 (uint8_t *)&req
, sizeof(req
));
301 case VDAGENTD_CLIPBOARD_DATA
: {
302 VDAgentClipboard
*clipboard
;
303 uint32_t size
= sizeof(*clipboard
) + header
->size
;
305 clipboard
= calloc(1, size
);
308 "out of memory allocating clipboard (write)\n");
311 clipboard
->type
= header
->opaque
;
312 memcpy(clipboard
->data
, data
, header
->size
);
314 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
315 VD_AGENT_CLIPBOARD
, 0,
316 (uint8_t *)clipboard
, size
);
320 case VDAGENTD_CLIPBOARD_RELEASE
:
321 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
322 VD_AGENT_CLIPBOARD_RELEASE
, 0, NULL
, 0);
323 agent_owns_clipboard
= 0;
330 if (header
->type
== VDAGENTD_CLIPBOARD_REQUEST
) {
331 /* Let the agent know no answer is coming */
332 udscs_write(conn
, VDAGENTD_CLIPBOARD_DATA
,
333 VD_AGENT_CLIPBOARD_NONE
, NULL
, 0);
337 /* When we open the vdagent virtio channel, the server automatically goes into
338 client mouse mode, so we can only have the channel open when we know the
339 active session resolution. This function checks that we have an agent in the
340 active session, and that it has told us its resolution. If these conditions
341 are met it sets the uinput tablet device's resolution and opens the virtio
342 channel (if it is not already open). If these conditions are not met, it
344 static void check_xorg_resolution(void) {
345 struct agent_data
*agent_data
= udscs_get_user_data(active_session_conn
);
347 if (agent_data
&& agent_data
->width
) {
349 uinput
= vdagentd_uinput_create(uinput_device
,
354 vdagentd_uinput_update_size(&uinput
, agent_data
->width
,
357 fprintf(logfile
, "Fatal uinput error\n");
363 fprintf(logfile
, "opening vdagent virtio channel\n");
364 virtio_port
= vdagent_virtio_port_create(portdev
,
365 virtio_port_read_complete
,
369 "Fatal error opening vdagent virtio channel\n");
374 send_capabilities(virtio_port
, 1);
377 vdagentd_uinput_destroy(&uinput
);
379 vdagent_virtio_port_flush(&virtio_port
);
380 vdagent_virtio_port_destroy(&virtio_port
);
381 fprintf(logfile
, "closed vdagent virtio channel\n");
386 static int connection_matches_active_session(struct udscs_connection
**connp
,
389 struct udscs_connection
**conn_ret
= (struct udscs_connection
**)priv
;
390 struct agent_data
*agent_data
= udscs_get_user_data(*connp
);
392 /* Check if this connection matches the currently active session */
393 if (!agent_data
->session
|| !active_session
)
395 if (strcmp(agent_data
->session
, active_session
))
402 void update_active_session_connection(void)
404 struct udscs_connection
*new_conn
= NULL
;
407 n
= udscs_server_for_all_clients(server
, connection_matches_active_session
,
412 if (new_conn
== active_session_conn
)
415 active_session_conn
= new_conn
;
417 if (agent_owns_clipboard
&& virtio_port
)
418 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
419 VD_AGENT_CLIPBOARD_RELEASE
, 0, NULL
, 0);
420 agent_owns_clipboard
= 0;
422 check_xorg_resolution();
425 void agent_connect(struct udscs_connection
*conn
)
428 struct agent_data
*agent_data
;
430 agent_data
= calloc(1, sizeof(*agent_data
));
432 fprintf(logfile
, "Out of memory allocating agent data, disconnecting\n");
433 udscs_destroy_connection(&conn
);
437 pid
= udscs_get_peer_cred(conn
).pid
;
438 agent_data
->session
= console_kit_session_for_pid(console_kit
, pid
);
439 udscs_set_user_data(conn
, (void *)agent_data
);
440 update_active_session_connection();
443 udscs_write(conn
, VDAGENTD_MONITORS_CONFIG
, 0, (uint8_t *)mon_config
,
444 sizeof(VDAgentMonitorsConfig
) +
445 mon_config
->num_of_monitors
* sizeof(VDAgentMonConfig
));
448 void agent_disconnect(struct udscs_connection
*conn
)
450 struct agent_data
*agent_data
= udscs_get_user_data(conn
);
452 free(agent_data
->session
);
453 agent_data
->session
= NULL
;
454 update_active_session_connection();
459 void agent_read_complete(struct udscs_connection
**connp
,
460 struct udscs_message_header
*header
, const uint8_t *data
)
462 struct agent_data
*agent_data
= udscs_get_user_data(*connp
);
464 switch (header
->type
) {
465 case VDAGENTD_GUEST_XORG_RESOLUTION
: {
466 struct vdagentd_guest_xorg_resolution
*res
=
467 (struct vdagentd_guest_xorg_resolution
*)data
;
469 if (header
->size
!= sizeof(*res
)) {
471 "guest xorg resolution message has wrong size, disconnecting agent\n");
472 udscs_destroy_connection(connp
);
476 agent_data
->width
= res
->width
;
477 agent_data
->height
= res
->height
;
478 check_xorg_resolution();
481 case VDAGENTD_CLIPBOARD_GRAB
:
482 case VDAGENTD_CLIPBOARD_REQUEST
:
483 case VDAGENTD_CLIPBOARD_DATA
:
484 case VDAGENTD_CLIPBOARD_RELEASE
:
485 do_agent_clipboard(*connp
, header
, data
);
488 fprintf(logfile
, "unknown message from vdagent: %u, ignoring\n",
495 static void usage(FILE *fp
)
500 " -h print this text\n"
501 " -d log debug messages (use twice for extra info)\n"
502 " -s <port> set virtio serial port [%s]\n"
503 " -u <dev> set uinput device [%s]\n"
504 " -x don't daemonize (and log to logfile)\n",
505 portdev
, uinput_device
);
510 /* detach from terminal */
513 close(0); close(1); close(2);
515 open("/dev/null",O_RDWR
); dup(0); dup(0);
518 fprintf(logfile
, "fork: %s\n", strerror(errno
));
527 fd_set readfds
, writefds
;
528 int n
, nfds
, ck_fd
= 0;
530 /* FIXME catch sigquit and set a flag to quit */
535 nfds
= udscs_server_fill_fds(server
, &readfds
, &writefds
);
536 n
= vdagent_virtio_port_fill_fds(virtio_port
, &readfds
, &writefds
);
540 ck_fd
= console_kit_get_fd(console_kit
);
541 FD_SET(ck_fd
, &readfds
);
545 n
= select(nfds
, &readfds
, &writefds
, NULL
, NULL
);
549 fprintf(logfile
, "Fatal error select: %s\n", strerror(errno
));
554 udscs_server_handle_fds(server
, &readfds
, &writefds
);
557 vdagent_virtio_port_handle_fds(&virtio_port
, &readfds
, &writefds
);
560 "AIIEEE lost spice client connection, reconnecting\n");
561 virtio_port
= vdagent_virtio_port_create(portdev
,
562 virtio_port_read_complete
,
567 "Fatal error opening vdagent virtio channel\n");
573 if (FD_ISSET(ck_fd
, &readfds
)) {
574 active_session
= console_kit_get_active_session(console_kit
);
575 update_active_session_connection();
576 if (!active_session
) {
577 fprintf(logfile
, "Fatal error: could not get active session\n");
586 int main(int argc
, char *argv
[])
589 int do_daemonize
= 1;
592 if (-1 == (c
= getopt(argc
, argv
, "-dhxs:u:")))
602 uinput_device
= optarg
;
617 logfile
= fopen(logfilename
, "a");
619 fprintf(stderr
, "Error opening %s: %s\n", logfilename
,
626 /* Setup communication with vdagent process(es) */
627 server
= udscs_create_server(VDAGENTD_SOCKET
, agent_connect
,
628 agent_read_complete
, agent_disconnect
,
629 vdagentd_messages
, VDAGENTD_NO_MESSAGES
,
630 debug
? logfile
:NULL
, logfile
);
632 fprintf(logfile
, "Fatal could not create server socket %s\n",
636 if (chmod(VDAGENTD_SOCKET
, 0666)) {
637 fprintf(logfile
, "Fatal could not change permissions on %s: %s\n",
638 VDAGENTD_SOCKET
, strerror(errno
));
639 udscs_destroy_server(server
);
643 console_kit
= console_kit_create(logfile
);
645 fprintf(logfile
, "Fatal could not connect to console kit\n");
646 udscs_destroy_server(server
);
649 active_session
= console_kit_get_active_session(console_kit
);
650 if (!active_session
) {
651 fprintf(logfile
, "Fatal could not get active session\n");
652 console_kit_destroy(console_kit
);
653 udscs_destroy_server(server
);
662 if (agent_owns_clipboard
&& virtio_port
)
663 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
664 VD_AGENT_CLIPBOARD_RELEASE
, 0, NULL
, 0);
666 vdagentd_uinput_destroy(&uinput
);
667 vdagent_virtio_port_flush(&virtio_port
);
668 vdagent_virtio_port_destroy(&virtio_port
);
669 console_kit_destroy(console_kit
);
670 udscs_destroy_server(server
);
671 fprintf(logfile
, "vdagentd quiting, returning status %d\n", retval
);
672 if (logfile
!= stderr
)