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 *portdev
= "/dev/virtio-ports/com.redhat.spice.0";
48 static const char *uinput
= "/dev/uinput";
50 static struct udscs_server
*server
= NULL
;
51 static struct vdagent_virtio_port
*virtio_port
= NULL
;
52 static struct console_kit
*console_kit
= NULL
;
53 static VDAgentMonitorsConfig
*mon_config
= NULL
;
54 static uint32_t *capabilities
= NULL
;
55 static int capabilities_size
= 0;
56 static int uinput_width
= 0;
57 static int uinput_height
= 0;
59 /* utility functions */
60 static int connection_matches_active_session(struct udscs_connection
**connp
,
63 struct udscs_connection
**conn_ret
= (struct udscs_connection
**)priv
;
64 struct agent_data
*agent_data
= udscs_get_user_data(*connp
);
65 const char *active_session
;
67 /* Check if this connection matches the currently active session */
68 active_session
= console_kit_get_active_session(console_kit
);
69 if (!agent_data
->session
|| !active_session
)
71 if (strcmp(agent_data
->session
, active_session
))
78 struct udscs_connection
*get_active_session_connection(void)
80 struct udscs_connection
*conn
= NULL
;
83 n
= udscs_server_for_all_clients(server
, connection_matches_active_session
,
91 /* vdagentd <-> spice-client communication handling */
92 static void send_capabilities(struct vdagent_virtio_port
*port
,
95 VDAgentAnnounceCapabilities
*caps
;
98 size
= sizeof(*caps
) + VD_AGENT_CAPS_BYTES
;
99 caps
= calloc(1, size
);
102 "out of memory allocating capabilities array (write)\n");
106 caps
->request
= request
;
107 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MOUSE_STATE
);
108 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MONITORS_CONFIG
);
109 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_REPLY
);
111 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
);
113 vdagent_virtio_port_write(port
, VDP_CLIENT_PORT
,
114 VD_AGENT_ANNOUNCE_CAPABILITIES
, 0,
115 (uint8_t *)caps
, size
);
119 static void do_monitors(struct vdagent_virtio_port
*port
, int port_nr
,
120 VDAgentMessage
*message_header
, VDAgentMonitorsConfig
*new_monitors
)
125 /* Store monitor config to send to agents when they connect */
126 size
= sizeof(VDAgentMonitorsConfig
) +
127 new_monitors
->num_of_monitors
* sizeof(VDAgentMonConfig
);
128 if (message_header
->size
!= size
) {
129 fprintf(stderr
, "invalid message size for VDAgentMonitorsConfig\n");
134 mon_config
->num_of_monitors
!= new_monitors
->num_of_monitors
) {
136 mon_config
= malloc(size
);
138 fprintf(stderr
, "out of memory allocating monitors config\n");
142 memcpy(mon_config
, new_monitors
, size
);
144 /* Send monitor config to currently connected agents */
145 udscs_server_write_all(server
, VDAGENTD_MONITORS_CONFIG
, 0,
146 (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(port
, port_nr
, VD_AGENT_REPLY
, 0,
152 (uint8_t *)&reply
, sizeof(reply
));
155 static void do_capabilities(struct vdagent_virtio_port
*port
,
156 VDAgentMessage
*message_header
,
157 VDAgentAnnounceCapabilities
*caps
)
159 capabilities_size
= VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header
->size
);
162 capabilities
= malloc(capabilities_size
* sizeof(uint32_t));
165 "out of memory allocating capabilities array (read)\n");
166 capabilities_size
= 0;
169 memcpy(capabilities
, caps
->caps
, capabilities_size
* sizeof(uint32_t));
171 send_capabilities(port
, 0);
174 static void do_clipboard(struct vdagent_virtio_port
*port
,
175 VDAgentMessage
*message_header
, uint8_t *message_data
)
177 uint32_t type
= 0, opaque
= 0, size
= 0;
178 uint8_t *data
= NULL
;
179 struct udscs_connection
*conn
;
181 switch (message_header
->type
) {
182 case VD_AGENT_CLIPBOARD_GRAB
:
183 type
= VDAGENTD_CLIPBOARD_GRAB
;
185 size
= message_header
->size
;
187 case VD_AGENT_CLIPBOARD_REQUEST
: {
188 VDAgentClipboardRequest
*req
= (VDAgentClipboardRequest
*)message_data
;
189 type
= VDAGENTD_CLIPBOARD_REQUEST
;
193 case VD_AGENT_CLIPBOARD
: {
194 VDAgentClipboard
*clipboard
= (VDAgentClipboard
*)message_data
;
195 type
= VDAGENTD_CLIPBOARD_DATA
;
196 opaque
= clipboard
->type
;
197 size
= message_header
->size
- sizeof(VDAgentClipboard
);
198 data
= clipboard
->data
;
201 case VD_AGENT_CLIPBOARD_RELEASE
:
202 type
= VDAGENTD_CLIPBOARD_RELEASE
;
206 conn
= get_active_session_connection();
209 "Could not find an agent connnection belonging to the "
210 "active session, ignoring client clipboard request\n");
214 udscs_write(conn
, type
, opaque
, data
, size
);
217 int virtio_port_read_complete(
218 struct vdagent_virtio_port
*port
,
219 VDIChunkHeader
*chunk_header
,
220 VDAgentMessage
*message_header
,
223 uint32_t min_size
= 0;
225 if (message_header
->protocol
!= VD_AGENT_PROTOCOL
) {
226 fprintf(stderr
, "message with wrong protocol version ignoring\n");
230 switch (message_header
->type
) {
231 case VD_AGENT_MOUSE_STATE
:
232 if (message_header
->size
!= sizeof(VDAgentMouseState
))
234 uinput_do_mouse((VDAgentMouseState
*)data
, debug
> 1);
236 case VD_AGENT_MONITORS_CONFIG
:
237 if (message_header
->size
< sizeof(VDAgentMonitorsConfig
))
239 do_monitors(port
, chunk_header
->port
, message_header
,
240 (VDAgentMonitorsConfig
*)data
);
242 case VD_AGENT_ANNOUNCE_CAPABILITIES
:
243 if (message_header
->size
< sizeof(VDAgentAnnounceCapabilities
))
245 do_capabilities(port
, message_header
,
246 (VDAgentAnnounceCapabilities
*)data
);
248 case VD_AGENT_CLIPBOARD_GRAB
:
249 case VD_AGENT_CLIPBOARD_REQUEST
:
250 case VD_AGENT_CLIPBOARD
:
251 case VD_AGENT_CLIPBOARD_RELEASE
:
252 switch (message_header
->type
) {
253 case VD_AGENT_CLIPBOARD_GRAB
:
254 min_size
= sizeof(VDAgentClipboardGrab
); break;
255 case VD_AGENT_CLIPBOARD_REQUEST
:
256 min_size
= sizeof(VDAgentClipboardRequest
); break;
257 case VD_AGENT_CLIPBOARD
:
258 min_size
= sizeof(VDAgentClipboard
); break;
260 if (message_header
->size
< min_size
)
262 do_clipboard(port
, message_header
, data
);
266 fprintf(stderr
, "unknown message type %d\n", message_header
->type
);
273 fprintf(stderr
, "read: invalid message size: %u for message type: %u\n",
274 message_header
->size
, message_header
->type
);
278 /* vdagentd <-> vdagent communication handling */
279 void do_agent_clipboard(struct udscs_connection
*conn
,
280 struct udscs_message_header
*header
, const uint8_t *data
)
282 const char *active_session
;
283 struct agent_data
*agent_data
= udscs_get_user_data(conn
);
285 if (!VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
286 VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
))
289 /* Check that this agent is from the currently active session */
290 active_session
= console_kit_get_active_session(console_kit
);
291 if (!agent_data
->session
|| !active_session
) {
292 fprintf(stderr
, "Could not get session info, ignoring agent clipboard request\n");
295 if (strcmp(agent_data
->session
, active_session
)) {
296 fprintf(stderr
, "Clipboard request from agent which is not in the active session?\n");
300 switch (header
->type
) {
301 case VDAGENTD_CLIPBOARD_GRAB
:
302 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
303 VD_AGENT_CLIPBOARD_GRAB
, 0,
306 case VDAGENTD_CLIPBOARD_REQUEST
: {
307 VDAgentClipboardRequest req
= { .type
= header
->opaque
};
308 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
309 VD_AGENT_CLIPBOARD_REQUEST
, 0,
310 (uint8_t *)&req
, sizeof(req
));
313 case VDAGENTD_CLIPBOARD_DATA
: {
314 VDAgentClipboard
*clipboard
;
315 uint32_t size
= sizeof(*clipboard
) + header
->size
;
317 clipboard
= calloc(1, size
);
320 "out of memory allocating clipboard (write)\n");
323 clipboard
->type
= header
->opaque
;
324 memcpy(clipboard
->data
, data
, header
->size
);
326 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
327 VD_AGENT_CLIPBOARD
, 0,
328 (uint8_t *)clipboard
, size
);
332 case VDAGENTD_CLIPBOARD_RELEASE
:
333 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
334 VD_AGENT_CLIPBOARD_RELEASE
, 0, NULL
, 0);
341 if (header
->type
== VDAGENTD_CLIPBOARD_REQUEST
) {
342 /* Let the agent know no answer is coming */
343 udscs_write(conn
, VDAGENTD_CLIPBOARD_DATA
,
344 VD_AGENT_CLIPBOARD_NONE
, NULL
, 0);
348 /* When we open the vdagent virtio channel, the server automatically goes into
349 client mouse mode, so we can only have the channel open when we know the
350 active session resolution. This function checks that we have an agent in the
351 active session, and that it has told us its resolution. If these conditions
352 are met it sets the uinput tablet device's resolution and opens the virtio
353 channel (if it is not already open). If these conditions are not met, it
355 static void check_xorg_resolution(void) {
356 struct udscs_connection
*conn
= get_active_session_connection();
357 struct agent_data
*agent_data
;
359 if (conn
&& (agent_data
= udscs_get_user_data(conn
)) && agent_data
->width
){
360 /* FIXME objectify uinput and let it handle all this */
361 if (agent_data
->width
!= uinput_width
||
362 agent_data
->height
!= uinput_height
) {
365 uinput_setup(uinput
, agent_data
->width
, agent_data
->height
);
366 uinput_width
= agent_data
->width
;
367 uinput_height
= agent_data
->height
;
370 fprintf(stderr
, "opening vdagent virtio channel\n");
371 virtio_port
= vdagent_virtio_port_create(portdev
,
372 virtio_port_read_complete
,
377 send_capabilities(virtio_port
, 1);
382 uinput_width
= uinput_height
= 0;
384 vdagent_virtio_port_destroy(&virtio_port
);
385 fprintf(stderr
, "closed vdagent virtio channel\n");
389 void agent_connect(struct udscs_connection
*conn
)
392 struct agent_data
*agent_data
;
394 agent_data
= calloc(1, sizeof(*agent_data
));
396 fprintf(stderr
, "Out of memory allocating agent data, disconnecting\n");
397 udscs_destroy_connection(&conn
);
401 pid
= udscs_get_peer_cred(conn
).pid
;
402 agent_data
->session
= console_kit_session_for_pid(console_kit
, pid
);
403 udscs_set_user_data(conn
, (void *)agent_data
);
406 udscs_write(conn
, VDAGENTD_MONITORS_CONFIG
, 0, (uint8_t *)mon_config
,
407 sizeof(VDAgentMonitorsConfig
) +
408 mon_config
->num_of_monitors
* sizeof(VDAgentMonConfig
));
411 void agent_disconnect(struct udscs_connection
*conn
)
413 struct agent_data
*agent_data
= udscs_get_user_data(conn
);
415 free(agent_data
->session
);
419 void agent_read_complete(struct udscs_connection
**connp
,
420 struct udscs_message_header
*header
, const uint8_t *data
)
422 struct agent_data
*agent_data
= udscs_get_user_data(*connp
);
424 switch (header
->type
) {
425 case VDAGENTD_GUEST_XORG_RESOLUTION
: {
426 struct vdagentd_guest_xorg_resolution
*res
=
427 (struct vdagentd_guest_xorg_resolution
*)data
;
429 if (header
->size
!= sizeof(*res
)) {
431 "guest xorg resolution message has wrong size, disconnecting agent\n");
432 udscs_destroy_connection(connp
);
436 agent_data
->width
= res
->width
;
437 agent_data
->height
= res
->height
;
438 check_xorg_resolution();
441 case VDAGENTD_CLIPBOARD_GRAB
:
442 case VDAGENTD_CLIPBOARD_REQUEST
:
443 case VDAGENTD_CLIPBOARD_DATA
:
444 case VDAGENTD_CLIPBOARD_RELEASE
:
445 do_agent_clipboard(*connp
, header
, data
);
448 fprintf(stderr
, "unknown message from vdagent: %u, ignoring\n",
455 static void usage(FILE *fp
)
460 " -h print this text\n"
461 " -d print debug messages (and don't daemonize)\n"
462 " -s <port> set virtio serial port [%s]\n"
463 " -u <dev> set uinput device [%s]\n",
469 /* detach from terminal */
475 close(0); close(1); close(2);
477 open("/dev/null",O_RDWR
); dup(0); dup(0);
486 fd_set readfds
, writefds
;
487 int n
, nfds
, ck_fd
= 0;
489 /* FIXME catch sigquit and set a flag to quit */
494 nfds
= udscs_server_fill_fds(server
, &readfds
, &writefds
);
495 n
= vdagent_virtio_port_fill_fds(virtio_port
, &readfds
, &writefds
);
499 ck_fd
= console_kit_get_fd(console_kit
);
500 FD_SET(ck_fd
, &readfds
);
505 n
= select(nfds
, &readfds
, &writefds
, NULL
, NULL
);
513 udscs_server_handle_fds(server
, &readfds
, &writefds
);
514 vdagent_virtio_port_handle_fds(&virtio_port
, &readfds
, &writefds
);
515 if (FD_ISSET(ck_fd
, &readfds
))
516 check_xorg_resolution();
520 int main(int argc
, char *argv
[])
525 if (-1 == (c
= getopt(argc
, argv
, "dhx:y:s:u:")))
546 /* Setup communication with vdagent process(es) */
547 server
= udscs_create_server(VDAGENTD_SOCKET
, agent_connect
,
548 agent_read_complete
, agent_disconnect
,
549 vdagentd_messages
, VDAGENTD_NO_MESSAGES
,
550 debug
? stderr
:NULL
, stderr
);
553 if (chmod(VDAGENTD_SOCKET
, 0666)) {
554 fprintf(stderr
, "could not change permissions on %s: %s\n",
555 VDAGENTD_SOCKET
, strerror(errno
));
558 console_kit
= console_kit_create(stderr
);
560 fprintf(stderr
, "Could not connect to console kit, disabling copy and paste support\n");
567 udscs_destroy_server(server
);