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/>.
30 #include <sys/select.h>
32 #include <spice/vd_agent.h>
35 #include "vdagentd-proto.h"
36 #include "vdagentd-proto-strings.h"
37 #include "vdagentd-uinput.h"
38 #include "vdagent-virtio-port.h"
39 #include "console-kit.h"
48 static const char *logfilename
= "/var/log/vdagentd.log";
49 static const char *portdev
= "/dev/virtio-ports/com.redhat.spice.0";
50 static const char *uinput_device
= "/dev/uinput";
52 static struct udscs_server
*server
= NULL
;
53 static struct vdagent_virtio_port
*virtio_port
= NULL
;
54 static struct console_kit
*console_kit
= NULL
;
55 static struct vdagentd_uinput
*uinput
= NULL
;
56 static VDAgentMonitorsConfig
*mon_config
= NULL
;
57 static uint32_t *capabilities
= NULL
;
58 static int capabilities_size
= 0;
59 static const char *active_session
= NULL
;
60 static struct udscs_connection
*active_session_conn
= NULL
;
61 static int agent_owns_clipboard
= 0;
62 static FILE *logfile
= NULL
;
64 static int retval
= 0;
66 /* utility functions */
67 /* vdagentd <-> spice-client communication handling */
68 static void send_capabilities(struct vdagent_virtio_port
*port
,
71 VDAgentAnnounceCapabilities
*caps
;
74 size
= sizeof(*caps
) + VD_AGENT_CAPS_BYTES
;
75 caps
= calloc(1, size
);
78 "out of memory allocating capabilities array (write)\n");
82 caps
->request
= request
;
83 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MOUSE_STATE
);
84 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_MONITORS_CONFIG
);
85 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_REPLY
);
86 VD_AGENT_SET_CAPABILITY(caps
->caps
, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
);
88 vdagent_virtio_port_write(port
, VDP_CLIENT_PORT
,
89 VD_AGENT_ANNOUNCE_CAPABILITIES
, 0,
90 (uint8_t *)caps
, size
);
94 static void do_client_monitors(struct vdagent_virtio_port
*port
, int port_nr
,
95 VDAgentMessage
*message_header
, VDAgentMonitorsConfig
*new_monitors
)
100 /* Store monitor config to send to agents when they connect */
101 size
= sizeof(VDAgentMonitorsConfig
) +
102 new_monitors
->num_of_monitors
* sizeof(VDAgentMonConfig
);
103 if (message_header
->size
!= size
) {
104 fprintf(logfile
, "invalid message size for VDAgentMonitorsConfig\n");
109 mon_config
->num_of_monitors
!= new_monitors
->num_of_monitors
) {
111 mon_config
= malloc(size
);
113 fprintf(logfile
, "out of memory allocating monitors config\n");
117 memcpy(mon_config
, new_monitors
, size
);
119 /* Send monitor config to currently connected agents */
120 udscs_server_write_all(server
, VDAGENTD_MONITORS_CONFIG
, 0,
121 (uint8_t *)mon_config
, size
);
123 /* Acknowledge reception of monitors config to spice server / client */
124 reply
.type
= VD_AGENT_MONITORS_CONFIG
;
125 reply
.error
= VD_AGENT_SUCCESS
;
126 vdagent_virtio_port_write(port
, port_nr
, VD_AGENT_REPLY
, 0,
127 (uint8_t *)&reply
, sizeof(reply
));
130 static void do_client_capabilities(struct vdagent_virtio_port
*port
,
131 VDAgentMessage
*message_header
,
132 VDAgentAnnounceCapabilities
*caps
)
134 capabilities_size
= VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header
->size
);
137 capabilities
= malloc(capabilities_size
* sizeof(uint32_t));
140 "out of memory allocating capabilities array (read)\n");
141 capabilities_size
= 0;
144 memcpy(capabilities
, caps
->caps
, capabilities_size
* sizeof(uint32_t));
146 send_capabilities(port
, 0);
149 static void do_client_clipboard(struct vdagent_virtio_port
*port
,
150 VDAgentMessage
*message_header
, uint8_t *message_data
)
152 uint32_t type
= 0, opaque
= 0, size
= 0;
153 uint8_t *data
= NULL
;
155 if (!active_session_conn
) {
157 "Could not find an agent connnection belonging to the "
158 "active session, ignoring client clipboard request\n");
162 switch (message_header
->type
) {
163 case VD_AGENT_CLIPBOARD_GRAB
:
164 type
= VDAGENTD_CLIPBOARD_GRAB
;
166 size
= message_header
->size
;
167 agent_owns_clipboard
= 0;
169 case VD_AGENT_CLIPBOARD_REQUEST
: {
170 VDAgentClipboardRequest
*req
= (VDAgentClipboardRequest
*)message_data
;
171 type
= VDAGENTD_CLIPBOARD_REQUEST
;
175 case VD_AGENT_CLIPBOARD
: {
176 VDAgentClipboard
*clipboard
= (VDAgentClipboard
*)message_data
;
177 type
= VDAGENTD_CLIPBOARD_DATA
;
178 opaque
= clipboard
->type
;
179 size
= message_header
->size
- sizeof(VDAgentClipboard
);
180 data
= clipboard
->data
;
183 case VD_AGENT_CLIPBOARD_RELEASE
:
184 type
= VDAGENTD_CLIPBOARD_RELEASE
;
188 udscs_write(active_session_conn
, type
, opaque
, data
, size
);
191 int virtio_port_read_complete(
192 struct vdagent_virtio_port
*port
,
193 VDIChunkHeader
*chunk_header
,
194 VDAgentMessage
*message_header
,
197 uint32_t min_size
= 0;
199 if (message_header
->protocol
!= VD_AGENT_PROTOCOL
) {
200 fprintf(logfile
, "message with wrong protocol version ignoring\n");
204 switch (message_header
->type
) {
205 case VD_AGENT_MOUSE_STATE
:
206 if (message_header
->size
!= sizeof(VDAgentMouseState
))
208 vdagentd_uinput_do_mouse(&uinput
, (VDAgentMouseState
*)data
);
210 /* Try to re-open the tablet */
211 struct agent_data
*agent_data
=
212 udscs_get_user_data(active_session_conn
);
214 uinput
= vdagentd_uinput_create(uinput_device
,
219 fprintf(logfile
, "Fatal uinput error\n");
225 case VD_AGENT_MONITORS_CONFIG
:
226 if (message_header
->size
< sizeof(VDAgentMonitorsConfig
))
228 do_client_monitors(port
, chunk_header
->port
, message_header
,
229 (VDAgentMonitorsConfig
*)data
);
231 case VD_AGENT_ANNOUNCE_CAPABILITIES
:
232 if (message_header
->size
< sizeof(VDAgentAnnounceCapabilities
))
234 do_client_capabilities(port
, message_header
,
235 (VDAgentAnnounceCapabilities
*)data
);
237 case VD_AGENT_CLIPBOARD_GRAB
:
238 case VD_AGENT_CLIPBOARD_REQUEST
:
239 case VD_AGENT_CLIPBOARD
:
240 case VD_AGENT_CLIPBOARD_RELEASE
:
241 switch (message_header
->type
) {
242 case VD_AGENT_CLIPBOARD_GRAB
:
243 min_size
= sizeof(VDAgentClipboardGrab
); break;
244 case VD_AGENT_CLIPBOARD_REQUEST
:
245 min_size
= sizeof(VDAgentClipboardRequest
); break;
246 case VD_AGENT_CLIPBOARD
:
247 min_size
= sizeof(VDAgentClipboard
); break;
249 if (message_header
->size
< min_size
)
251 do_client_clipboard(port
, message_header
, data
);
255 fprintf(logfile
, "unknown message type %d\n", message_header
->type
);
262 fprintf(logfile
, "read: invalid message size: %u for message type: %u\n",
263 message_header
->size
, message_header
->type
);
267 /* vdagentd <-> vdagent communication handling */
268 void do_agent_clipboard(struct udscs_connection
*conn
,
269 struct udscs_message_header
*header
, const uint8_t *data
)
271 if (!VD_AGENT_HAS_CAPABILITY(capabilities
, capabilities_size
,
272 VD_AGENT_CAP_CLIPBOARD_BY_DEMAND
))
275 /* Check that this agent is from the currently active session */
276 if (conn
!= active_session_conn
) {
277 fprintf(logfile
, "Clipboard request from agent "
278 "which is not in the active session?\n");
284 "Clipboard request from agent but no client connection\n");
288 switch (header
->type
) {
289 case VDAGENTD_CLIPBOARD_GRAB
:
290 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
291 VD_AGENT_CLIPBOARD_GRAB
, 0,
293 agent_owns_clipboard
= 1;
295 case VDAGENTD_CLIPBOARD_REQUEST
: {
296 VDAgentClipboardRequest req
= { .type
= header
->opaque
};
297 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
298 VD_AGENT_CLIPBOARD_REQUEST
, 0,
299 (uint8_t *)&req
, sizeof(req
));
302 case VDAGENTD_CLIPBOARD_DATA
: {
303 VDAgentClipboard
*clipboard
;
304 uint32_t size
= sizeof(*clipboard
) + header
->size
;
306 clipboard
= calloc(1, size
);
309 "out of memory allocating clipboard (write)\n");
312 clipboard
->type
= header
->opaque
;
313 memcpy(clipboard
->data
, data
, header
->size
);
315 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
316 VD_AGENT_CLIPBOARD
, 0,
317 (uint8_t *)clipboard
, size
);
321 case VDAGENTD_CLIPBOARD_RELEASE
:
322 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
323 VD_AGENT_CLIPBOARD_RELEASE
, 0, NULL
, 0);
324 agent_owns_clipboard
= 0;
331 if (header
->type
== VDAGENTD_CLIPBOARD_REQUEST
) {
332 /* Let the agent know no answer is coming */
333 udscs_write(conn
, VDAGENTD_CLIPBOARD_DATA
,
334 VD_AGENT_CLIPBOARD_NONE
, NULL
, 0);
338 /* When we open the vdagent virtio channel, the server automatically goes into
339 client mouse mode, so we can only have the channel open when we know the
340 active session resolution. This function checks that we have an agent in the
341 active session, and that it has told us its resolution. If these conditions
342 are met it sets the uinput tablet device's resolution and opens the virtio
343 channel (if it is not already open). If these conditions are not met, it
345 static void check_xorg_resolution(void) {
346 struct agent_data
*agent_data
= udscs_get_user_data(active_session_conn
);
348 if (agent_data
&& agent_data
->width
) {
350 uinput
= vdagentd_uinput_create(uinput_device
,
355 vdagentd_uinput_update_size(&uinput
, agent_data
->width
,
358 fprintf(logfile
, "Fatal uinput error\n");
364 fprintf(logfile
, "opening vdagent virtio channel\n");
365 virtio_port
= vdagent_virtio_port_create(portdev
,
366 virtio_port_read_complete
,
370 "Fatal error opening vdagent virtio channel\n");
375 send_capabilities(virtio_port
, 1);
378 vdagentd_uinput_destroy(&uinput
);
380 vdagent_virtio_port_flush(&virtio_port
);
381 vdagent_virtio_port_destroy(&virtio_port
);
382 fprintf(logfile
, "closed vdagent virtio channel\n");
387 static int connection_matches_active_session(struct udscs_connection
**connp
,
390 struct udscs_connection
**conn_ret
= (struct udscs_connection
**)priv
;
391 struct agent_data
*agent_data
= udscs_get_user_data(*connp
);
393 /* Check if this connection matches the currently active session */
394 if (!agent_data
->session
|| !active_session
)
396 if (strcmp(agent_data
->session
, active_session
))
403 void update_active_session_connection(void)
405 struct udscs_connection
*new_conn
= NULL
;
408 n
= udscs_server_for_all_clients(server
, connection_matches_active_session
,
413 if (new_conn
== active_session_conn
)
416 active_session_conn
= new_conn
;
418 if (agent_owns_clipboard
&& virtio_port
)
419 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
420 VD_AGENT_CLIPBOARD_RELEASE
, 0, NULL
, 0);
421 agent_owns_clipboard
= 0;
423 check_xorg_resolution();
426 void agent_connect(struct udscs_connection
*conn
)
429 struct agent_data
*agent_data
;
431 agent_data
= calloc(1, sizeof(*agent_data
));
433 fprintf(logfile
, "Out of memory allocating agent data, disconnecting\n");
434 udscs_destroy_connection(&conn
);
438 pid
= udscs_get_peer_cred(conn
).pid
;
439 agent_data
->session
= console_kit_session_for_pid(console_kit
, pid
);
440 udscs_set_user_data(conn
, (void *)agent_data
);
441 update_active_session_connection();
444 udscs_write(conn
, VDAGENTD_MONITORS_CONFIG
, 0, (uint8_t *)mon_config
,
445 sizeof(VDAgentMonitorsConfig
) +
446 mon_config
->num_of_monitors
* sizeof(VDAgentMonConfig
));
449 void agent_disconnect(struct udscs_connection
*conn
)
451 struct agent_data
*agent_data
= udscs_get_user_data(conn
);
453 free(agent_data
->session
);
454 agent_data
->session
= NULL
;
455 update_active_session_connection();
460 void agent_read_complete(struct udscs_connection
**connp
,
461 struct udscs_message_header
*header
, const uint8_t *data
)
463 struct agent_data
*agent_data
= udscs_get_user_data(*connp
);
465 switch (header
->type
) {
466 case VDAGENTD_GUEST_XORG_RESOLUTION
: {
467 struct vdagentd_guest_xorg_resolution
*res
=
468 (struct vdagentd_guest_xorg_resolution
*)data
;
470 if (header
->size
!= sizeof(*res
)) {
472 "guest xorg resolution message has wrong size, disconnecting agent\n");
473 udscs_destroy_connection(connp
);
477 agent_data
->width
= res
->width
;
478 agent_data
->height
= res
->height
;
479 check_xorg_resolution();
482 case VDAGENTD_CLIPBOARD_GRAB
:
483 case VDAGENTD_CLIPBOARD_REQUEST
:
484 case VDAGENTD_CLIPBOARD_DATA
:
485 case VDAGENTD_CLIPBOARD_RELEASE
:
486 do_agent_clipboard(*connp
, header
, data
);
489 fprintf(logfile
, "unknown message from vdagent: %u, ignoring\n",
496 static void usage(FILE *fp
)
501 " -h print this text\n"
502 " -d log debug messages (use twice for extra info)\n"
503 " -s <port> set virtio serial port [%s]\n"
504 " -u <dev> set uinput device [%s]\n"
505 " -x don't daemonize (and log to logfile)\n",
506 portdev
, uinput_device
);
511 /* detach from terminal */
514 close(0); close(1); close(2);
516 open("/dev/null",O_RDWR
); dup(0); dup(0);
519 fprintf(logfile
, "fork: %s\n", strerror(errno
));
528 fd_set readfds
, writefds
;
529 int n
, nfds
, ck_fd
= 0;
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 static void quit_handler(int sig
)
591 int main(int argc
, char *argv
[])
594 int do_daemonize
= 1;
595 struct sigaction act
;
598 if (-1 == (c
= getopt(argc
, argv
, "-dhxs:u:")))
608 uinput_device
= optarg
;
622 memset(&act
, 0, sizeof(act
));
623 act
.sa_flags
= SA_RESTART
;
624 act
.sa_handler
= quit_handler
;
625 sigaction(SIGINT
, &act
, NULL
);
626 sigaction(SIGHUP
, &act
, NULL
);
627 sigaction(SIGTERM
, &act
, NULL
);
628 sigaction(SIGQUIT
, &act
, NULL
);
631 logfile
= fopen(logfilename
, "a");
633 fprintf(stderr
, "Error opening %s: %s\n", logfilename
,
640 /* Setup communication with vdagent process(es) */
641 server
= udscs_create_server(VDAGENTD_SOCKET
, agent_connect
,
642 agent_read_complete
, agent_disconnect
,
643 vdagentd_messages
, VDAGENTD_NO_MESSAGES
,
644 debug
? logfile
:NULL
, logfile
);
646 fprintf(logfile
, "Fatal could not create server socket %s\n",
650 if (chmod(VDAGENTD_SOCKET
, 0666)) {
651 fprintf(logfile
, "Fatal could not change permissions on %s: %s\n",
652 VDAGENTD_SOCKET
, strerror(errno
));
653 udscs_destroy_server(server
);
657 console_kit
= console_kit_create(logfile
);
659 fprintf(logfile
, "Fatal could not connect to console kit\n");
660 udscs_destroy_server(server
);
663 active_session
= console_kit_get_active_session(console_kit
);
664 if (!active_session
) {
665 fprintf(logfile
, "Fatal could not get active session\n");
666 console_kit_destroy(console_kit
);
667 udscs_destroy_server(server
);
676 if (agent_owns_clipboard
&& virtio_port
)
677 vdagent_virtio_port_write(virtio_port
, VDP_CLIENT_PORT
,
678 VD_AGENT_CLIPBOARD_RELEASE
, 0, NULL
, 0);
680 vdagentd_uinput_destroy(&uinput
);
681 vdagent_virtio_port_flush(&virtio_port
);
682 vdagent_virtio_port_destroy(&virtio_port
);
683 console_kit_destroy(console_kit
);
684 udscs_destroy_server(server
);
685 fprintf(logfile
, "vdagentd quiting, returning status %d\n", retval
);
686 if (logfile
!= stderr
)