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;
58 static const char *active_session
= NULL
;
59 static struct udscs_connection
*active_session_conn
= NULL
;
60 static int agent_owns_clipboard
= 0;
62 /* utility functions */
63 /* vdagentd <-> spice-client communication handling */
64 static void send_capabilities(struct vdagent_virtio_port
*port
,
67 VDAgentAnnounceCapabilities
*caps
;
70 size
= sizeof(*caps
) + VD_AGENT_CAPS_BYTES
;
71 caps
= calloc(1, size
);
74 "out of memory allocating capabilities array (write)\n");
78 caps
->request
= request
;
79 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MOUSE_STATE
);
80 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MONITORS_CONFIG
);
81 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_REPLY
);
82 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
);
84 vdagent_virtio_port_write(port
, VDP_CLIENT_PORT
,
85 VD_AGENT_ANNOUNCE_CAPABILITIES
, 0,
86 (uint8_t *)caps
, size
);
90 static void do_client_monitors(struct vdagent_virtio_port
*port
, int port_nr
,
91 VDAgentMessage
*message_header
, VDAgentMonitorsConfig
*new_monitors
)
96 /* Store monitor config to send to agents when they connect */
97 size
= sizeof(VDAgentMonitorsConfig
) +
98 new_monitors
->num_of_monitors
* sizeof(VDAgentMonConfig
);
99 if (message_header
->size
!= size
) {
100 fprintf(stderr
, "invalid message size for VDAgentMonitorsConfig\n");
105 mon_config
->num_of_monitors
!= new_monitors
->num_of_monitors
) {
107 mon_config
= malloc(size
);
109 fprintf(stderr
, "out of memory allocating monitors config\n");
113 memcpy(mon_config
, new_monitors
, size
);
115 /* Send monitor config to currently connected agents */
116 udscs_server_write_all(server
, VDAGENTD_MONITORS_CONFIG
, 0,
117 (uint8_t *)mon_config
, size
);
119 /* Acknowledge reception of monitors config to spice server / client */
120 reply
.type
= VD_AGENT_MONITORS_CONFIG
;
121 reply
.error
= VD_AGENT_SUCCESS
;
122 vdagent_virtio_port_write(port
, port_nr
, VD_AGENT_REPLY
, 0,
123 (uint8_t *)&reply
, sizeof(reply
));
126 static void do_client_capabilities(struct vdagent_virtio_port
*port
,
127 VDAgentMessage
*message_header
,
128 VDAgentAnnounceCapabilities
*caps
)
130 capabilities_size
= VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header
->size
);
133 capabilities
= malloc(capabilities_size
* sizeof(uint32_t));
136 "out of memory allocating capabilities array (read)\n");
137 capabilities_size
= 0;
140 memcpy(capabilities
, caps
->caps
, capabilities_size
* sizeof(uint32_t));
142 send_capabilities(port
, 0);
145 static void do_client_clipboard(struct vdagent_virtio_port
*port
,
146 VDAgentMessage
*message_header
, uint8_t *message_data
)
148 uint32_t type
= 0, opaque
= 0, size
= 0;
149 uint8_t *data
= NULL
;
151 if (!active_session_conn
) {
153 "Could not find an agent connnection belonging to the "
154 "active session, ignoring client clipboard request\n");
158 switch (message_header
->type
) {
159 case VD_AGENT_CLIPBOARD_GRAB
:
160 type
= VDAGENTD_CLIPBOARD_GRAB
;
162 size
= message_header
->size
;
163 agent_owns_clipboard
= 0;
165 case VD_AGENT_CLIPBOARD_REQUEST
: {
166 VDAgentClipboardRequest
*req
= (VDAgentClipboardRequest
*)message_data
;
167 type
= VDAGENTD_CLIPBOARD_REQUEST
;
171 case VD_AGENT_CLIPBOARD
: {
172 VDAgentClipboard
*clipboard
= (VDAgentClipboard
*)message_data
;
173 type
= VDAGENTD_CLIPBOARD_DATA
;
174 opaque
= clipboard
->type
;
175 size
= message_header
->size
- sizeof(VDAgentClipboard
);
176 data
= clipboard
->data
;
179 case VD_AGENT_CLIPBOARD_RELEASE
:
180 type
= VDAGENTD_CLIPBOARD_RELEASE
;
184 udscs_write(active_session_conn
, type
, opaque
, data
, size
);
187 int virtio_port_read_complete(
188 struct vdagent_virtio_port
*port
,
189 VDIChunkHeader
*chunk_header
,
190 VDAgentMessage
*message_header
,
193 uint32_t min_size
= 0;
195 if (message_header
->protocol
!= VD_AGENT_PROTOCOL
) {
196 fprintf(stderr
, "message with wrong protocol version ignoring\n");
200 switch (message_header
->type
) {
201 case VD_AGENT_MOUSE_STATE
:
202 if (message_header
->size
!= sizeof(VDAgentMouseState
))
204 uinput_do_mouse((VDAgentMouseState
*)data
, debug
> 1);
206 case VD_AGENT_MONITORS_CONFIG
:
207 if (message_header
->size
< sizeof(VDAgentMonitorsConfig
))
209 do_client_monitors(port
, chunk_header
->port
, message_header
,
210 (VDAgentMonitorsConfig
*)data
);
212 case VD_AGENT_ANNOUNCE_CAPABILITIES
:
213 if (message_header
->size
< sizeof(VDAgentAnnounceCapabilities
))
215 do_client_capabilities(port
, message_header
,
216 (VDAgentAnnounceCapabilities
*)data
);
218 case VD_AGENT_CLIPBOARD_GRAB
:
219 case VD_AGENT_CLIPBOARD_REQUEST
:
220 case VD_AGENT_CLIPBOARD
:
221 case VD_AGENT_CLIPBOARD_RELEASE
:
222 switch (message_header
->type
) {
223 case VD_AGENT_CLIPBOARD_GRAB
:
224 min_size
= sizeof(VDAgentClipboardGrab
); break;
225 case VD_AGENT_CLIPBOARD_REQUEST
:
226 min_size
= sizeof(VDAgentClipboardRequest
); break;
227 case VD_AGENT_CLIPBOARD
:
228 min_size
= sizeof(VDAgentClipboard
); break;
230 if (message_header
->size
< min_size
)
232 do_client_clipboard(port
, message_header
, data
);
236 fprintf(stderr
, "unknown message type %d\n", message_header
->type
);
243 fprintf(stderr
, "read: invalid message size: %u for message type: %u\n",
244 message_header
->size
, message_header
->type
);
248 /* vdagentd <-> vdagent communication handling */
249 void do_agent_clipboard(struct udscs_connection
*conn
,
250 struct udscs_message_header
*header
, const uint8_t *data
)
252 if (!VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
253 VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
))
256 /* Check that this agent is from the currently active session */
257 if (conn
!= active_session_conn
) {
258 fprintf(stderr
, "Clipboard request from agent which is not in the active session?\n");
262 switch (header
->type
) {
263 case VDAGENTD_CLIPBOARD_GRAB
:
264 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
265 VD_AGENT_CLIPBOARD_GRAB
, 0,
267 agent_owns_clipboard
= 1;
269 case VDAGENTD_CLIPBOARD_REQUEST
: {
270 VDAgentClipboardRequest req
= { .type
= header
->opaque
};
271 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
272 VD_AGENT_CLIPBOARD_REQUEST
, 0,
273 (uint8_t *)&req
, sizeof(req
));
276 case VDAGENTD_CLIPBOARD_DATA
: {
277 VDAgentClipboard
*clipboard
;
278 uint32_t size
= sizeof(*clipboard
) + header
->size
;
280 clipboard
= calloc(1, size
);
283 "out of memory allocating clipboard (write)\n");
286 clipboard
->type
= header
->opaque
;
287 memcpy(clipboard
->data
, data
, header
->size
);
289 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
290 VD_AGENT_CLIPBOARD
, 0,
291 (uint8_t *)clipboard
, size
);
295 case VDAGENTD_CLIPBOARD_RELEASE
:
296 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
297 VD_AGENT_CLIPBOARD_RELEASE
, 0, NULL
, 0);
298 agent_owns_clipboard
= 0;
305 if (header
->type
== VDAGENTD_CLIPBOARD_REQUEST
) {
306 /* Let the agent know no answer is coming */
307 udscs_write(conn
, VDAGENTD_CLIPBOARD_DATA
,
308 VD_AGENT_CLIPBOARD_NONE
, NULL
, 0);
312 /* When we open the vdagent virtio channel, the server automatically goes into
313 client mouse mode, so we can only have the channel open when we know the
314 active session resolution. This function checks that we have an agent in the
315 active session, and that it has told us its resolution. If these conditions
316 are met it sets the uinput tablet device's resolution and opens the virtio
317 channel (if it is not already open). If these conditions are not met, it
319 static void check_xorg_resolution(void) {
320 struct agent_data
*agent_data
= udscs_get_user_data(active_session_conn
);
322 if (agent_data
&& agent_data
->width
) {
323 /* FIXME objectify uinput and let it handle all this */
324 if (agent_data
->width
!= uinput_width
||
325 agent_data
->height
!= uinput_height
) {
328 uinput_setup(uinput
, agent_data
->width
, agent_data
->height
);
329 uinput_width
= agent_data
->width
;
330 uinput_height
= agent_data
->height
;
333 fprintf(stderr
, "opening vdagent virtio channel\n");
334 virtio_port
= vdagent_virtio_port_create(portdev
,
335 virtio_port_read_complete
,
340 send_capabilities(virtio_port
, 1);
345 uinput_width
= uinput_height
= 0;
347 vdagent_virtio_port_flush(&virtio_port
);
348 vdagent_virtio_port_destroy(&virtio_port
);
349 fprintf(stderr
, "closed vdagent virtio channel\n");
353 static int connection_matches_active_session(struct udscs_connection
**connp
,
356 struct udscs_connection
**conn_ret
= (struct udscs_connection
**)priv
;
357 struct agent_data
*agent_data
= udscs_get_user_data(*connp
);
359 /* Check if this connection matches the currently active session */
360 if (!agent_data
->session
|| !active_session
)
362 if (strcmp(agent_data
->session
, active_session
))
369 void update_active_session_connection(void)
371 struct udscs_connection
*new_conn
= NULL
;
374 n
= udscs_server_for_all_clients(server
, connection_matches_active_session
,
379 if (new_conn
== active_session_conn
)
382 active_session_conn
= new_conn
;
384 if (agent_owns_clipboard
) {
385 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
386 VD_AGENT_CLIPBOARD_RELEASE
, 0, NULL
, 0);
387 agent_owns_clipboard
= 0;
390 check_xorg_resolution();
393 void agent_connect(struct udscs_connection
*conn
)
396 struct agent_data
*agent_data
;
398 agent_data
= calloc(1, sizeof(*agent_data
));
400 fprintf(stderr
, "Out of memory allocating agent data, disconnecting\n");
401 udscs_destroy_connection(&conn
);
405 pid
= udscs_get_peer_cred(conn
).pid
;
406 agent_data
->session
= console_kit_session_for_pid(console_kit
, pid
);
407 udscs_set_user_data(conn
, (void *)agent_data
);
408 update_active_session_connection();
411 udscs_write(conn
, VDAGENTD_MONITORS_CONFIG
, 0, (uint8_t *)mon_config
,
412 sizeof(VDAgentMonitorsConfig
) +
413 mon_config
->num_of_monitors
* sizeof(VDAgentMonConfig
));
416 void agent_disconnect(struct udscs_connection
*conn
)
418 struct agent_data
*agent_data
= udscs_get_user_data(conn
);
420 free(agent_data
->session
);
421 agent_data
->session
= NULL
;
422 update_active_session_connection();
427 void agent_read_complete(struct udscs_connection
**connp
,
428 struct udscs_message_header
*header
, const uint8_t *data
)
430 struct agent_data
*agent_data
= udscs_get_user_data(*connp
);
432 switch (header
->type
) {
433 case VDAGENTD_GUEST_XORG_RESOLUTION
: {
434 struct vdagentd_guest_xorg_resolution
*res
=
435 (struct vdagentd_guest_xorg_resolution
*)data
;
437 if (header
->size
!= sizeof(*res
)) {
439 "guest xorg resolution message has wrong size, disconnecting agent\n");
440 udscs_destroy_connection(connp
);
444 agent_data
->width
= res
->width
;
445 agent_data
->height
= res
->height
;
446 check_xorg_resolution();
449 case VDAGENTD_CLIPBOARD_GRAB
:
450 case VDAGENTD_CLIPBOARD_REQUEST
:
451 case VDAGENTD_CLIPBOARD_DATA
:
452 case VDAGENTD_CLIPBOARD_RELEASE
:
453 do_agent_clipboard(*connp
, header
, data
);
456 fprintf(stderr
, "unknown message from vdagent: %u, ignoring\n",
463 static void usage(FILE *fp
)
468 " -h print this text\n"
469 " -d print debug messages (and don't daemonize)\n"
470 " -s <port> set virtio serial port [%s]\n"
471 " -u <dev> set uinput device [%s]\n",
477 /* detach from terminal */
483 close(0); close(1); close(2);
485 open("/dev/null",O_RDWR
); dup(0); dup(0);
494 fd_set readfds
, writefds
;
495 int n
, nfds
, ck_fd
= 0;
497 /* FIXME catch sigquit and set a flag to quit */
502 nfds
= udscs_server_fill_fds(server
, &readfds
, &writefds
);
503 n
= vdagent_virtio_port_fill_fds(virtio_port
, &readfds
, &writefds
);
507 ck_fd
= console_kit_get_fd(console_kit
);
508 FD_SET(ck_fd
, &readfds
);
512 n
= select(nfds
, &readfds
, &writefds
, NULL
, NULL
);
520 udscs_server_handle_fds(server
, &readfds
, &writefds
);
521 vdagent_virtio_port_handle_fds(&virtio_port
, &readfds
, &writefds
);
522 if (FD_ISSET(ck_fd
, &readfds
)) {
523 active_session
= console_kit_get_active_session(console_kit
);
524 update_active_session_connection();
525 check_xorg_resolution();
526 if (!active_session
) {
527 fprintf(stderr
, "Fatal error: could not get active session\n");
534 int main(int argc
, char *argv
[])
539 if (-1 == (c
= getopt(argc
, argv
, "-dhx:y:s:u:")))
560 /* Setup communication with vdagent process(es) */
561 server
= udscs_create_server(VDAGENTD_SOCKET
, agent_connect
,
562 agent_read_complete
, agent_disconnect
,
563 vdagentd_messages
, VDAGENTD_NO_MESSAGES
,
564 debug
? stderr
:NULL
, stderr
);
567 if (chmod(VDAGENTD_SOCKET
, 0666)) {
568 fprintf(stderr
, "could not change permissions on %s: %s\n",
569 VDAGENTD_SOCKET
, strerror(errno
));
572 console_kit
= console_kit_create(stderr
);
575 active_session
= console_kit_get_active_session(console_kit
);
584 udscs_destroy_server(server
);