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"
41 static const char *portdev
= "/dev/virtio-ports/com.redhat.spice.0";
42 static const char *uinput
= "/dev/uinput";
43 static int connection_count
= 0;
45 static struct udscs_server
*server
= NULL
;
46 static struct vdagent_virtio_port
*virtio_port
= NULL
;
47 static struct console_kit
*console_kit
= NULL
;
48 static VDAgentMonitorsConfig
*mon_config
= NULL
;
49 static uint32_t *capabilities
= NULL
;
50 static int capabilities_size
= 0;
52 /* vdagent virtio port handling */
53 static void send_capabilities(struct vdagent_virtio_port
*port
,
56 VDAgentAnnounceCapabilities
*caps
;
59 size
= sizeof(*caps
) + VD_AGENT_CAPS_BYTES
;
60 caps
= calloc(1, size
);
63 "out of memory allocating capabilities array (write)\n");
67 caps
->request
= request
;
68 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MOUSE_STATE
);
69 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MONITORS_CONFIG
);
70 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_REPLY
);
72 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
);
74 vdagent_virtio_port_write(port
, VDP_CLIENT_PORT
,
75 VD_AGENT_ANNOUNCE_CAPABILITIES
, 0,
76 (uint8_t *)caps
, size
);
80 static void do_monitors(struct vdagent_virtio_port
*port
, int port_nr
,
81 VDAgentMessage
*message_header
, VDAgentMonitorsConfig
*new_monitors
)
86 /* Store monitor config to send to agents when they connect */
87 size
= sizeof(VDAgentMonitorsConfig
) +
88 new_monitors
->num_of_monitors
* sizeof(VDAgentMonConfig
);
89 if (message_header
->size
!= size
) {
90 fprintf(stderr
, "invalid message size for VDAgentMonitorsConfig\n");
95 mon_config
->num_of_monitors
!= new_monitors
->num_of_monitors
) {
97 mon_config
= malloc(size
);
99 fprintf(stderr
, "out of memory allocating monitors config\n");
103 memcpy(mon_config
, new_monitors
, size
);
105 /* Send monitor config to currently connected agents */
106 udscs_server_write_all(server
, VDAGENTD_MONITORS_CONFIG
, 0,
107 (uint8_t *)mon_config
, size
);
109 /* Acknowledge reception of monitors config to spice server / client */
110 reply
.type
= VD_AGENT_MONITORS_CONFIG
;
111 reply
.error
= VD_AGENT_SUCCESS
;
112 vdagent_virtio_port_write(port
, port_nr
, VD_AGENT_REPLY
, 0,
113 (uint8_t *)&reply
, sizeof(reply
));
116 static void do_capabilities(struct vdagent_virtio_port
*port
,
117 VDAgentMessage
*message_header
,
118 VDAgentAnnounceCapabilities
*caps
)
120 capabilities_size
= VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header
->size
);
123 capabilities
= malloc(capabilities_size
* sizeof(uint32_t));
126 "out of memory allocating capabilities array (read)\n");
127 capabilities_size
= 0;
130 memcpy(capabilities
, caps
->caps
, capabilities_size
* sizeof(uint32_t));
132 send_capabilities(port
, 0);
135 static int connection_matches_active_session(struct udscs_connection
**connp
,
138 struct udscs_connection
**conn_ret
= (struct udscs_connection
**)priv
;
139 const char *conn_session
, *active_session
;
141 /* Check if this connection matches the currently active session */
142 conn_session
= (const char *)udscs_get_user_data(*connp
);
143 active_session
= console_kit_get_active_session(console_kit
);
144 if (!conn_session
|| !active_session
)
146 if (strcmp(conn_session
, active_session
))
153 static void do_clipboard(struct vdagent_virtio_port
*port
,
154 VDAgentMessage
*message_header
, uint8_t *message_data
)
156 uint32_t type
= 0, opaque
= 0, size
= 0;
157 uint8_t *data
= NULL
;
158 struct udscs_connection
*conn
= NULL
;
161 switch (message_header
->type
) {
162 case VD_AGENT_CLIPBOARD_GRAB
:
163 type
= VDAGENTD_CLIPBOARD_GRAB
;
165 size
= message_header
->size
;
167 case VD_AGENT_CLIPBOARD_REQUEST
: {
168 VDAgentClipboardRequest
*req
= (VDAgentClipboardRequest
*)message_data
;
169 type
= VDAGENTD_CLIPBOARD_REQUEST
;
173 case VD_AGENT_CLIPBOARD
: {
174 VDAgentClipboard
*clipboard
= (VDAgentClipboard
*)message_data
;
175 type
= VDAGENTD_CLIPBOARD_DATA
;
176 opaque
= clipboard
->type
;
177 size
= message_header
->size
- sizeof(VDAgentClipboard
);
178 data
= clipboard
->data
;
181 case VD_AGENT_CLIPBOARD_RELEASE
:
182 type
= VDAGENTD_CLIPBOARD_RELEASE
;
186 n
= udscs_server_for_all_clients(server
, connection_matches_active_session
,
188 if (n
!= 1 || conn
== NULL
) {
190 "Could not find an agent connnection belonging to the "
191 "active session, ignoring client clipboard request\n");
195 udscs_write(conn
, type
, opaque
, data
, size
);
198 int virtio_port_read_complete(
199 struct vdagent_virtio_port
*port
,
200 VDIChunkHeader
*chunk_header
,
201 VDAgentMessage
*message_header
,
204 uint32_t min_size
= 0;
206 if (message_header
->protocol
!= VD_AGENT_PROTOCOL
) {
207 fprintf(stderr
, "message with wrong protocol version ignoring\n");
211 switch (message_header
->type
) {
212 case VD_AGENT_MOUSE_STATE
:
213 if (message_header
->size
!= sizeof(VDAgentMouseState
))
215 uinput_do_mouse((VDAgentMouseState
*)data
, debug
> 1);
217 case VD_AGENT_MONITORS_CONFIG
:
218 if (message_header
->size
< sizeof(VDAgentMonitorsConfig
))
220 do_monitors(port
, chunk_header
->port
, message_header
,
221 (VDAgentMonitorsConfig
*)data
);
223 case VD_AGENT_ANNOUNCE_CAPABILITIES
:
224 if (message_header
->size
< sizeof(VDAgentAnnounceCapabilities
))
226 do_capabilities(port
, message_header
,
227 (VDAgentAnnounceCapabilities
*)data
);
229 case VD_AGENT_CLIPBOARD_GRAB
:
230 case VD_AGENT_CLIPBOARD_REQUEST
:
231 case VD_AGENT_CLIPBOARD
:
232 case VD_AGENT_CLIPBOARD_RELEASE
:
233 switch (message_header
->type
) {
234 case VD_AGENT_CLIPBOARD_GRAB
:
235 min_size
= sizeof(VDAgentClipboardGrab
); break;
236 case VD_AGENT_CLIPBOARD_REQUEST
:
237 min_size
= sizeof(VDAgentClipboardRequest
); break;
238 case VD_AGENT_CLIPBOARD
:
239 min_size
= sizeof(VDAgentClipboard
); break;
241 if (message_header
->size
< min_size
)
243 do_clipboard(port
, message_header
, data
);
247 fprintf(stderr
, "unknown message type %d\n", message_header
->type
);
254 fprintf(stderr
, "read: invalid message size: %u for message type: %u\n",
255 message_header
->size
, message_header
->type
);
259 /* vdagent client handling */
260 void do_client_clipboard(struct udscs_connection
*conn
,
261 struct udscs_message_header
*header
, const uint8_t *data
)
263 const char *conn_session
, *active_session
;
265 if (!VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
266 VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
))
269 /* Check that this client is from the currently active session */
270 conn_session
= (const char *)udscs_get_user_data(conn
);
271 active_session
= console_kit_get_active_session(console_kit
);
272 if (!conn_session
|| !active_session
) {
273 fprintf(stderr
, "Could not get session info, ignoring agent clipboard request\n");
276 if (strcmp(conn_session
, active_session
)) {
277 fprintf(stderr
, "Clipboard request from agent which is not in the active session?\n");
281 switch (header
->type
) {
282 case VDAGENTD_CLIPBOARD_GRAB
:
283 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
284 VD_AGENT_CLIPBOARD_GRAB
, 0,
287 case VDAGENTD_CLIPBOARD_REQUEST
: {
288 VDAgentClipboardRequest req
= { .type
= header
->opaque
};
289 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
290 VD_AGENT_CLIPBOARD_REQUEST
, 0,
291 (uint8_t *)&req
, sizeof(req
));
294 case VDAGENTD_CLIPBOARD_DATA
: {
295 VDAgentClipboard
*clipboard
;
296 uint32_t size
= sizeof(*clipboard
) + header
->size
;
298 clipboard
= calloc(1, size
);
301 "out of memory allocating clipboard (write)\n");
304 clipboard
->type
= header
->opaque
;
305 memcpy(clipboard
->data
, data
, header
->size
);
307 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
308 VD_AGENT_CLIPBOARD
, 0,
309 (uint8_t *)clipboard
, size
);
313 case VDAGENTD_CLIPBOARD_RELEASE
:
314 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
315 VD_AGENT_CLIPBOARD_RELEASE
, 0, NULL
, 0);
322 if (header
->type
== VDAGENTD_CLIPBOARD_REQUEST
) {
323 /* Let the agent know no answer is coming */
324 udscs_write(conn
, VDAGENTD_CLIPBOARD_DATA
,
325 VD_AGENT_CLIPBOARD_NONE
, NULL
, 0);
329 void client_connect(struct udscs_connection
*conn
)
334 pid
= udscs_get_peer_cred(conn
).pid
;
335 session
= console_kit_session_for_pid(console_kit
, pid
);
336 udscs_set_user_data(conn
, (void *)session
);
338 /* We don't create the tablet until we've gotten the xorg resolution
339 from the vdagent client */
343 udscs_write(conn
, VDAGENTD_MONITORS_CONFIG
, 0, (uint8_t *)mon_config
,
344 sizeof(VDAgentMonitorsConfig
) +
345 mon_config
->num_of_monitors
* sizeof(VDAgentMonConfig
));
348 void client_disconnect(struct udscs_connection
*conn
)
351 if (connection_count
== 0) {
353 vdagent_virtio_port_destroy(&virtio_port
);
355 free(udscs_get_user_data(conn
));
358 void client_read_complete(struct udscs_connection
**connp
,
359 struct udscs_message_header
*header
, const uint8_t *data
)
361 switch (header
->type
) {
362 case VDAGENTD_GUEST_XORG_RESOLUTION
: {
363 struct vdagentd_guest_xorg_resolution
*res
=
364 (struct vdagentd_guest_xorg_resolution
*)data
;
366 if (header
->size
!= sizeof(*res
)) {
368 "guest xorg resolution message has wrong size, disconnecting client\n");
369 udscs_destroy_connection(connp
);
373 /* Now that we know the xorg resolution setup the uinput device */
374 uinput_setup(uinput
, res
->width
, res
->height
);
375 /* Now that we have a tablet and thus can forward mouse events,
376 we can open the vdagent virtio port. */
378 virtio_port
= vdagent_virtio_port_create(portdev
,
379 virtio_port_read_complete
,
384 send_capabilities(virtio_port
, 1);
388 case VDAGENTD_CLIPBOARD_GRAB
:
389 case VDAGENTD_CLIPBOARD_REQUEST
:
390 case VDAGENTD_CLIPBOARD_DATA
:
391 case VDAGENTD_CLIPBOARD_RELEASE
:
392 do_client_clipboard(*connp
, header
, data
);
395 fprintf(stderr
, "unknown message from vdagent client: %u, ignoring\n",
402 static void usage(FILE *fp
)
405 "vdagent -- handle spice agent mouse via uinput\n"
407 " -h print this text\n"
408 " -d print debug messages (and don't daemonize)\n"
409 " -s <port> set virtio serial port [%s]\n"
410 " -u <dev> set uinput device [%s]\n",
416 /* detach from terminal */
422 close(0); close(1); close(2);
424 open("/dev/null",O_RDWR
); dup(0); dup(0);
433 fd_set readfds
, writefds
;
436 /* FIXME catch sigquit and set a flag to quit */
441 nfds
= udscs_server_fill_fds(server
, &readfds
, &writefds
);
442 n
= vdagent_virtio_port_fill_fds(virtio_port
, &readfds
, &writefds
);
446 n
= select(nfds
, &readfds
, &writefds
, NULL
, NULL
);
454 udscs_server_handle_fds(server
, &readfds
, &writefds
);
455 vdagent_virtio_port_handle_fds(&virtio_port
, &readfds
, &writefds
);
459 int main(int argc
, char *argv
[])
464 if (-1 == (c
= getopt(argc
, argv
, "dhx:y:s:u:")))
485 /* Setup communication with vdagent process(es) */
486 server
= udscs_create_server(VDAGENTD_SOCKET
, client_connect
,
487 client_read_complete
, client_disconnect
,
488 vdagentd_messages
, VDAGENTD_NO_MESSAGES
,
489 debug
? stderr
:NULL
, stderr
);
492 if (chmod(VDAGENTD_SOCKET
, 0666)) {
493 fprintf(stderr
, "could not change permissions on %s: %s\n",
494 VDAGENTD_SOCKET
, strerror(errno
));
497 console_kit
= console_kit_create(stderr
);
499 fprintf(stderr
, "Could not connect to console kit, disabling copy and paste support\n");
506 udscs_destroy_server(server
);