Install spice-vdagent under /usr/bin
[vd_agent/hramrach.git] / vdagentd.c
blob444bbcfe09bf640c38f0d15e590974671fc4abad
1 /* vdagentd.c vdagentd (daemon) code
3 Copyright 2010 Red Hat, Inc.
5 Red Hat Authors:
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/>.
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <signal.h>
30 #include <sys/select.h>
31 #include <sys/stat.h>
32 #include <spice/vd_agent.h>
34 #include "udscs.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"
41 struct agent_data {
42 char *session;
43 int width;
44 int height;
47 /* variables */
48 static const char *logfilename = "/var/log/spice-vdagentd.log";
49 static const char *portdev = "/dev/virtio-ports/com.redhat.spice.0";
50 static const char *uinput_device = "/dev/uinput";
51 static int debug = 0;
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;
63 static int quit = 0;
64 static int retval = 0;
66 /* utility functions */
67 /* vdagentd <-> spice-client communication handling */
68 static void send_capabilities(struct vdagent_virtio_port *port,
69 uint32_t request)
71 VDAgentAnnounceCapabilities *caps;
72 uint32_t size;
74 size = sizeof(*caps) + VD_AGENT_CAPS_BYTES;
75 caps = calloc(1, size);
76 if (!caps) {
77 fprintf(logfile,
78 "out of memory allocating capabilities array (write)\n");
79 return;
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);
91 free(caps);
94 static void do_client_monitors(struct vdagent_virtio_port *port, int port_nr,
95 VDAgentMessage *message_header, VDAgentMonitorsConfig *new_monitors)
97 VDAgentReply reply;
98 uint32_t size;
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");
105 return;
108 if (!mon_config ||
109 mon_config->num_of_monitors != new_monitors->num_of_monitors) {
110 free(mon_config);
111 mon_config = malloc(size);
112 if (!mon_config) {
113 fprintf(logfile, "out of memory allocating monitors config\n");
114 return;
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);
136 free(capabilities);
137 capabilities = malloc(capabilities_size * sizeof(uint32_t));
138 if (!capabilities) {
139 fprintf(logfile,
140 "out of memory allocating capabilities array (read)\n");
141 capabilities_size = 0;
142 return;
144 memcpy(capabilities, caps->caps, capabilities_size * sizeof(uint32_t));
145 if (caps->request)
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) {
156 fprintf(logfile,
157 "Could not find an agent connnection belonging to the "
158 "active session, ignoring client clipboard request\n");
159 return;
162 switch (message_header->type) {
163 case VD_AGENT_CLIPBOARD_GRAB:
164 type = VDAGENTD_CLIPBOARD_GRAB;
165 data = message_data;
166 size = message_header->size;
167 agent_owns_clipboard = 0;
168 break;
169 case VD_AGENT_CLIPBOARD_REQUEST: {
170 VDAgentClipboardRequest *req = (VDAgentClipboardRequest *)message_data;
171 type = VDAGENTD_CLIPBOARD_REQUEST;
172 opaque = req->type;
173 break;
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;
181 break;
183 case VD_AGENT_CLIPBOARD_RELEASE:
184 type = VDAGENTD_CLIPBOARD_RELEASE;
185 break;
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,
195 uint8_t *data)
197 uint32_t min_size = 0;
199 if (message_header->protocol != VD_AGENT_PROTOCOL) {
200 fprintf(logfile, "message with wrong protocol version ignoring\n");
201 return 0;
204 switch (message_header->type) {
205 case VD_AGENT_MOUSE_STATE:
206 if (message_header->size != sizeof(VDAgentMouseState))
207 goto size_error;
208 vdagentd_uinput_do_mouse(&uinput, (VDAgentMouseState *)data);
209 if (!uinput) {
210 /* Try to re-open the tablet */
211 struct agent_data *agent_data =
212 udscs_get_user_data(active_session_conn);
213 if (agent_data)
214 uinput = vdagentd_uinput_create(uinput_device,
215 agent_data->width,
216 agent_data->height,
217 logfile, debug > 1);
218 if (!uinput) {
219 fprintf(logfile, "Fatal uinput error\n");
220 retval = 1;
221 quit = 1;
224 break;
225 case VD_AGENT_MONITORS_CONFIG:
226 if (message_header->size < sizeof(VDAgentMonitorsConfig))
227 goto size_error;
228 do_client_monitors(port, chunk_header->port, message_header,
229 (VDAgentMonitorsConfig *)data);
230 break;
231 case VD_AGENT_ANNOUNCE_CAPABILITIES:
232 if (message_header->size < sizeof(VDAgentAnnounceCapabilities))
233 goto size_error;
234 do_client_capabilities(port, message_header,
235 (VDAgentAnnounceCapabilities *)data);
236 break;
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)
250 goto size_error;
251 do_client_clipboard(port, message_header, data);
252 break;
253 default:
254 if (debug)
255 fprintf(logfile, "unknown message type %d\n", message_header->type);
256 break;
259 return 0;
261 size_error:
262 fprintf(logfile, "read: invalid message size: %u for message type: %u\n",
263 message_header->size, message_header->type);
264 return 0;
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))
273 goto error;
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");
279 goto error;
282 if (!virtio_port) {
283 fprintf(logfile,
284 "Clipboard request from agent but no client connection\n");
285 goto error;
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,
292 data, header->size);
293 agent_owns_clipboard = 1;
294 break;
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));
300 break;
302 case VDAGENTD_CLIPBOARD_DATA: {
303 VDAgentClipboard *clipboard;
304 uint32_t size = sizeof(*clipboard) + header->size;
306 clipboard = calloc(1, size);
307 if (!clipboard) {
308 fprintf(logfile,
309 "out of memory allocating clipboard (write)\n");
310 return;
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);
318 free(clipboard);
319 break;
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;
325 break;
328 return;
330 error:
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
344 closes both. */
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) {
349 if (!uinput)
350 uinput = vdagentd_uinput_create(uinput_device,
351 agent_data->width,
352 agent_data->height,
353 logfile, debug > 1);
354 else
355 vdagentd_uinput_update_size(&uinput, agent_data->width,
356 agent_data->height);
357 if (!uinput) {
358 fprintf(logfile, "Fatal uinput error\n");
359 retval = 1;
360 quit = 1;
363 if (!virtio_port) {
364 fprintf(logfile, "opening vdagent virtio channel\n");
365 virtio_port = vdagent_virtio_port_create(portdev,
366 virtio_port_read_complete,
367 NULL, logfile);
368 if (!virtio_port) {
369 fprintf(logfile,
370 "Fatal error opening vdagent virtio channel\n");
371 retval = 1;
372 quit = 1;
373 return;
375 send_capabilities(virtio_port, 1);
377 } else {
378 vdagentd_uinput_destroy(&uinput);
379 if (virtio_port) {
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,
388 void *priv)
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)
395 return 0;
396 if (strcmp(agent_data->session, active_session))
397 return 0;
399 *conn_ret = *connp;
400 return 1;
403 void update_active_session_connection(void)
405 struct udscs_connection *new_conn = NULL;
406 int n;
408 n = udscs_server_for_all_clients(server, connection_matches_active_session,
409 (void*)&new_conn);
410 if (n != 1)
411 new_conn = NULL;
413 if (new_conn == active_session_conn)
414 return;
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)
428 uint32_t pid;
429 struct agent_data *agent_data;
431 agent_data = calloc(1, sizeof(*agent_data));
432 if (!agent_data) {
433 fprintf(logfile, "Out of memory allocating agent data, disconnecting\n");
434 udscs_destroy_connection(&conn);
435 return;
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();
443 if (mon_config)
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();
457 free(agent_data);
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)) {
471 fprintf(logfile,
472 "guest xorg resolution message has wrong size, disconnecting agent\n");
473 udscs_destroy_connection(connp);
474 return;
477 agent_data->width = res->width;
478 agent_data->height = res->height;
479 check_xorg_resolution();
480 break;
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);
487 break;
488 default:
489 fprintf(logfile, "unknown message from vdagent: %u, ignoring\n",
490 header->type);
494 /* main */
496 static void usage(FILE *fp)
498 fprintf(fp,
499 "vdagentd\n"
500 "options:\n"
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);
509 void daemonize(void)
511 /* detach from terminal */
512 switch (fork()) {
513 case 0:
514 close(0); close(1); close(2);
515 setsid();
516 open("/dev/null",O_RDWR); dup(0); dup(0);
517 break;
518 case -1:
519 fprintf(logfile, "fork: %s\n", strerror(errno));
520 retval = 1;
521 default:
522 udscs_destroy_server(server);
523 if (logfile != stderr)
524 fclose(logfile);
525 exit(0);
529 void main_loop(void)
531 fd_set readfds, writefds;
532 int n, nfds, ck_fd = 0;
534 while (!quit) {
535 FD_ZERO(&readfds);
536 FD_ZERO(&writefds);
538 nfds = udscs_server_fill_fds(server, &readfds, &writefds);
539 n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds);
540 if (n >= nfds)
541 nfds = n + 1;
543 ck_fd = console_kit_get_fd(console_kit);
544 FD_SET(ck_fd, &readfds);
545 if (ck_fd >= nfds)
546 nfds = ck_fd + 1;
548 n = select(nfds, &readfds, &writefds, NULL, NULL);
549 if (n == -1) {
550 if (errno == EINTR)
551 continue;
552 fprintf(logfile, "Fatal error select: %s\n", strerror(errno));
553 retval = 1;
554 break;
557 udscs_server_handle_fds(server, &readfds, &writefds);
559 if (virtio_port) {
560 vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds);
561 if (!virtio_port) {
562 fprintf(logfile,
563 "AIIEEE lost spice client connection, reconnecting\n");
564 virtio_port = vdagent_virtio_port_create(portdev,
565 virtio_port_read_complete,
566 NULL, logfile);
568 if (!virtio_port) {
569 fprintf(logfile,
570 "Fatal error opening vdagent virtio channel\n");
571 retval = 1;
572 break;
576 if (FD_ISSET(ck_fd, &readfds)) {
577 active_session = console_kit_get_active_session(console_kit);
578 update_active_session_connection();
579 if (!active_session) {
580 fprintf(logfile, "Fatal error: could not get active session\n");
581 retval = 1;
582 break;
585 fflush(logfile);
589 static void quit_handler(int sig)
591 quit = 1;
594 int main(int argc, char *argv[])
596 int c;
597 int do_daemonize = 1;
598 struct sigaction act;
600 for (;;) {
601 if (-1 == (c = getopt(argc, argv, "-dhxs:u:")))
602 break;
603 switch (c) {
604 case 'd':
605 debug++;
606 break;
607 case 's':
608 portdev = optarg;
609 break;
610 case 'u':
611 uinput_device = optarg;
612 break;
613 case 'x':
614 do_daemonize = 0;
615 break;
616 case 'h':
617 usage(stdout);
618 return 0;
619 default:
620 usage(stderr);
621 return 1;
625 memset(&act, 0, sizeof(act));
626 act.sa_flags = SA_RESTART;
627 act.sa_handler = quit_handler;
628 sigaction(SIGINT, &act, NULL);
629 sigaction(SIGHUP, &act, NULL);
630 sigaction(SIGTERM, &act, NULL);
631 sigaction(SIGQUIT, &act, NULL);
633 if (do_daemonize) {
634 logfile = fopen(logfilename, "a");
635 if (!logfile) {
636 fprintf(stderr, "Error opening %s: %s\n", logfilename,
637 strerror(errno));
638 logfile = stderr;
640 } else
641 logfile = stderr;
643 /* Setup communication with vdagent process(es) */
644 server = udscs_create_server(VDAGENTD_SOCKET, agent_connect,
645 agent_read_complete, agent_disconnect,
646 vdagentd_messages, VDAGENTD_NO_MESSAGES,
647 debug? logfile:NULL, logfile);
648 if (!server) {
649 fprintf(logfile, "Fatal could not create server socket %s\n",
650 VDAGENTD_SOCKET);
651 return 1;
653 if (chmod(VDAGENTD_SOCKET, 0666)) {
654 fprintf(logfile, "Fatal could not change permissions on %s: %s\n",
655 VDAGENTD_SOCKET, strerror(errno));
656 udscs_destroy_server(server);
657 return 1;
660 if (do_daemonize)
661 daemonize();
663 console_kit = console_kit_create(logfile);
664 if (!console_kit) {
665 fprintf(logfile, "Fatal could not connect to console kit\n");
666 udscs_destroy_server(server);
667 return 1;
669 active_session = console_kit_get_active_session(console_kit);
670 if (!active_session) {
671 fprintf(logfile, "Fatal could not get active session\n");
672 console_kit_destroy(console_kit);
673 udscs_destroy_server(server);
674 return 1;
677 main_loop();
679 if (agent_owns_clipboard && virtio_port)
680 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
681 VD_AGENT_CLIPBOARD_RELEASE, 0, NULL, 0);
683 vdagentd_uinput_destroy(&uinput);
684 vdagent_virtio_port_flush(&virtio_port);
685 vdagent_virtio_port_destroy(&virtio_port);
686 console_kit_destroy(console_kit);
687 udscs_destroy_server(server);
688 fprintf(logfile, "vdagentd quiting, returning status %d\n", retval);
689 if (logfile != stderr)
690 fclose(logfile);
692 return retval;