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>
30 #include <spice/vd_agent.h>
33 #include "vdagentd-proto.h"
34 #include "vdagentd-proto-strings.h"
35 #include "vdagentd-uinput.h"
36 #include "vdagent-virtio-port.h"
39 static const char *portdev
= "/dev/virtio-ports/com.redhat.spice.0";
40 static const char *uinput
= "/dev/uinput";
41 static int connection_count
= 0;
43 static struct udscs_server
*server
= NULL
;
44 static struct vdagent_virtio_port
*virtio_port
= NULL
;
45 static VDAgentMonitorsConfig
*mon_config
= NULL
;
46 static uint32_t *capabilities
= NULL
;
47 static int capabilities_size
= 0;
49 /* vdagent virtio port handling */
50 static void send_capabilities(struct vdagent_virtio_port
*port
,
53 VDAgentAnnounceCapabilities
*caps
;
56 size
= sizeof(*caps
) + VD_AGENT_CAPS_BYTES
;
57 caps
= calloc(1, size
);
60 "out of memory allocating capabilities array (write)\n");
64 caps
->request
= request
;
65 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MOUSE_STATE
);
66 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MONITORS_CONFIG
);
67 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_REPLY
);
68 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
);
70 vdagent_virtio_port_write(port
, VDP_CLIENT_PORT
,
71 VD_AGENT_ANNOUNCE_CAPABILITIES
, 0,
72 (uint8_t *)caps
, size
);
76 static void do_monitors(struct vdagent_virtio_port
*port
, int port_nr
,
77 VDAgentMessage
*message_header
, VDAgentMonitorsConfig
*new_monitors
)
82 /* Store monitor config to send to agents when they connect */
83 size
= sizeof(VDAgentMonitorsConfig
) +
84 new_monitors
->num_of_monitors
* sizeof(VDAgentMonConfig
);
85 if (message_header
->size
!= size
) {
86 fprintf(stderr
, "invalid message size for VDAgentMonitorsConfig\n");
91 mon_config
->num_of_monitors
!= new_monitors
->num_of_monitors
) {
93 mon_config
= malloc(size
);
95 fprintf(stderr
, "out of memory allocating monitors config\n");
99 memcpy(mon_config
, new_monitors
, size
);
101 /* Send monitor config to currently connected agents */
102 udscs_server_write_all(server
, VDAGENTD_MONITORS_CONFIG
, 0,
103 (uint8_t *)mon_config
, size
);
105 /* Acknowledge reception of monitors config to spice server / client */
106 reply
.type
= VD_AGENT_MONITORS_CONFIG
;
107 reply
.error
= VD_AGENT_SUCCESS
;
108 vdagent_virtio_port_write(port
, port_nr
, VD_AGENT_REPLY
, 0,
109 (uint8_t *)&reply
, sizeof(reply
));
112 static void do_capabilities(struct vdagent_virtio_port
*port
,
113 VDAgentMessage
*message_header
,
114 VDAgentAnnounceCapabilities
*caps
)
116 capabilities_size
= VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header
->size
);
119 capabilities
= malloc(capabilities_size
* sizeof(uint32_t));
122 "out of memory allocating capabilities array (read)\n");
123 capabilities_size
= 0;
126 memcpy(capabilities
, caps
->caps
, capabilities_size
* sizeof(uint32_t));
128 send_capabilities(port
, 0);
131 static void do_clipboard(struct vdagent_virtio_port
*port
,
132 VDAgentMessage
*message_header
, uint8_t *message_data
)
134 uint32_t type
= 0, opaque
= 0, size
= 0;
135 uint8_t *data
= NULL
;
137 switch (message_header
->type
) {
138 case VD_AGENT_CLIPBOARD_GRAB
:
139 type
= VDAGENTD_CLIPBOARD_GRAB
;
141 size
= message_header
->size
;
143 case VD_AGENT_CLIPBOARD_REQUEST
: {
144 VDAgentClipboardRequest
*req
= (VDAgentClipboardRequest
*)message_data
;
145 type
= VDAGENTD_CLIPBOARD_REQUEST
;
149 case VD_AGENT_CLIPBOARD
: {
150 VDAgentClipboard
*clipboard
= (VDAgentClipboard
*)message_data
;
151 type
= VDAGENTD_CLIPBOARD_DATA
;
152 opaque
= clipboard
->type
;
153 size
= message_header
->size
- sizeof(VDAgentClipboard
);
154 data
= clipboard
->data
;
157 case VD_AGENT_CLIPBOARD_RELEASE
:
158 type
= VDAGENTD_CLIPBOARD_RELEASE
;
162 /* FIXME send only to agent in active session */
163 udscs_server_write_all(server
, type
, opaque
, data
, size
);
166 int virtio_port_read_complete(
167 struct vdagent_virtio_port
*port
,
168 VDIChunkHeader
*chunk_header
,
169 VDAgentMessage
*message_header
,
172 uint32_t min_size
= 0;
174 if (message_header
->protocol
!= VD_AGENT_PROTOCOL
) {
175 fprintf(stderr
, "message with wrong protocol version ignoring\n");
179 switch (message_header
->type
) {
180 case VD_AGENT_MOUSE_STATE
:
181 if (message_header
->size
!= sizeof(VDAgentMouseState
))
183 uinput_do_mouse((VDAgentMouseState
*)data
, debug
> 1);
185 case VD_AGENT_MONITORS_CONFIG
:
186 if (message_header
->size
< sizeof(VDAgentMonitorsConfig
))
188 do_monitors(port
, chunk_header
->port
, message_header
,
189 (VDAgentMonitorsConfig
*)data
);
191 case VD_AGENT_ANNOUNCE_CAPABILITIES
:
192 if (message_header
->size
< sizeof(VDAgentAnnounceCapabilities
))
194 do_capabilities(port
, message_header
,
195 (VDAgentAnnounceCapabilities
*)data
);
197 case VD_AGENT_CLIPBOARD_GRAB
:
198 case VD_AGENT_CLIPBOARD_REQUEST
:
199 case VD_AGENT_CLIPBOARD
:
200 case VD_AGENT_CLIPBOARD_RELEASE
:
201 switch (message_header
->type
) {
202 case VD_AGENT_CLIPBOARD_GRAB
:
203 min_size
= sizeof(VDAgentClipboardGrab
); break;
204 case VD_AGENT_CLIPBOARD_REQUEST
:
205 min_size
= sizeof(VDAgentClipboardRequest
); break;
206 case VD_AGENT_CLIPBOARD
:
207 min_size
= sizeof(VDAgentClipboard
); break;
209 if (message_header
->size
< min_size
)
211 do_clipboard(port
, message_header
, data
);
215 fprintf(stderr
, "unknown message type %d\n", message_header
->type
);
222 fprintf(stderr
, "read: invalid message size: %u for message type: %u\n",
223 message_header
->size
, message_header
->type
);
227 /* vdagent client handling */
228 void do_client_clipboard(struct udscs_connection
*conn
,
229 struct udscs_message_header
*header
, const uint8_t *data
)
231 if (!VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
232 VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
))
235 /* FIXME check that this client is from the currently active session */
239 switch (header
->type
) {
240 case VDAGENTD_CLIPBOARD_GRAB
:
241 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
242 VD_AGENT_CLIPBOARD_GRAB
, 0,
245 case VDAGENTD_CLIPBOARD_REQUEST
: {
246 VDAgentClipboardRequest req
= { .type
= header
->opaque
};
247 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
248 VD_AGENT_CLIPBOARD_REQUEST
, 0,
249 (uint8_t *)&req
, sizeof(req
));
252 case VDAGENTD_CLIPBOARD_DATA
: {
253 VDAgentClipboard
*clipboard
;
254 uint32_t size
= sizeof(*clipboard
) + header
->size
;
256 clipboard
= calloc(1, size
);
259 "out of memory allocating clipboard (write)\n");
262 clipboard
->type
= header
->opaque
;
263 memcpy(clipboard
->data
, data
, header
->size
);
265 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
266 VD_AGENT_CLIPBOARD
, 0,
267 (uint8_t *)clipboard
, size
);
271 case VDAGENTD_CLIPBOARD_RELEASE
:
272 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
273 VD_AGENT_CLIPBOARD_RELEASE
, 0, NULL
, 0);
280 if (header
->type
== VDAGENTD_CLIPBOARD_REQUEST
) {
281 /* Let the agent know no answer is coming */
282 udscs_write(conn
, VDAGENTD_CLIPBOARD_DATA
,
283 VD_AGENT_CLIPBOARD_NONE
, NULL
, 0);
287 void client_connect(struct udscs_connection
*conn
)
289 /* We don't create the tablet until we've gotten the xorg resolution
290 from the vdagent client */
294 udscs_write(conn
, VDAGENTD_MONITORS_CONFIG
, 0, (uint8_t *)mon_config
,
295 sizeof(VDAgentMonitorsConfig
) +
296 mon_config
->num_of_monitors
* sizeof(VDAgentMonConfig
));
299 void client_disconnect(struct udscs_connection
*conn
)
302 if (connection_count
== 0) {
304 vdagent_virtio_port_destroy(&virtio_port
);
308 int client_read_complete(struct udscs_connection
*conn
,
309 struct udscs_message_header
*header
, const uint8_t *data
)
311 switch (header
->type
) {
312 case VDAGENTD_GUEST_XORG_RESOLUTION
: {
313 struct vdagentd_guest_xorg_resolution
*res
=
314 (struct vdagentd_guest_xorg_resolution
*)data
;
316 if (header
->size
!= sizeof(*res
)) {
318 "guest xorg resolution message has wrong size, disconnecting client\n");
322 /* Now that we know the xorg resolution setup the uinput device */
323 uinput_setup(uinput
, res
->width
, res
->height
);
324 /* Now that we have a tablet and thus can forward mouse events,
325 we can open the vdagent virtio port. */
327 virtio_port
= vdagent_virtio_port_create(portdev
,
328 virtio_port_read_complete
,
333 send_capabilities(virtio_port
, 1);
337 case VDAGENTD_CLIPBOARD_GRAB
:
338 case VDAGENTD_CLIPBOARD_REQUEST
:
339 case VDAGENTD_CLIPBOARD_DATA
:
340 case VDAGENTD_CLIPBOARD_RELEASE
:
341 do_client_clipboard(conn
, header
, data
);
344 fprintf(stderr
, "unknown message from vdagent client: %u, ignoring\n",
353 static void usage(FILE *fp
)
356 "vdagent -- handle spice agent mouse via uinput\n"
358 " -h print this text\n"
359 " -d print debug messages (and don't daemonize)\n"
360 " -s <port> set virtio serial port [%s]\n"
361 " -u <dev> set uinput device [%s]\n",
367 /* detach from terminal */
373 close(0); close(1); close(2);
375 open("/dev/null",O_RDWR
); dup(0); dup(0);
384 fd_set readfds
, writefds
;
387 /* FIXME catch sigquit and set a flag to quit */
392 nfds
= udscs_server_fill_fds(server
, &readfds
, &writefds
);
393 n
= vdagent_virtio_port_fill_fds(virtio_port
, &readfds
, &writefds
);
397 n
= select(nfds
, &readfds
, &writefds
, NULL
, NULL
);
405 udscs_server_handle_fds(server
, &readfds
, &writefds
);
406 vdagent_virtio_port_handle_fds(&virtio_port
, &readfds
, &writefds
);
410 int main(int argc
, char *argv
[])
415 if (-1 == (c
= getopt(argc
, argv
, "dhx:y:s:u:")))
436 /* Setup communication with vdagent process(es) */
437 server
= udscs_create_server(VDAGENTD_SOCKET
, client_connect
,
438 client_read_complete
, client_disconnect
,
439 vdagentd_messages
, VDAGENTD_NO_MESSAGES
,
440 debug
? stderr
:NULL
, stderr
);
449 udscs_destroy_server(server
);