Update .gitignore
[vd_agent.git] / vdagentd.c
blob47ec963bda5a71733649af98643f1ce6fe6a5302
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 *pidfilename = "/var/run/spice-vdagentd.pid";
50 static const char *portdev = "/dev/virtio-ports/com.redhat.spice.0";
51 static const char *uinput_device = "/dev/uinput";
52 static int debug = 0;
53 static struct udscs_server *server = NULL;
54 static struct vdagent_virtio_port *virtio_port = NULL;
55 static struct console_kit *console_kit = NULL;
56 static struct vdagentd_uinput *uinput = NULL;
57 static VDAgentMonitorsConfig *mon_config = NULL;
58 static uint32_t *capabilities = NULL;
59 static int capabilities_size = 0;
60 static const char *active_session = NULL;
61 static struct udscs_connection *active_session_conn = NULL;
62 static int agent_owns_clipboard = 0;
63 static FILE *logfile = NULL;
64 static int quit = 0;
65 static int retval = 0;
67 /* utility functions */
68 /* vdagentd <-> spice-client communication handling */
69 static void send_capabilities(struct vdagent_virtio_port *port,
70 uint32_t request)
72 VDAgentAnnounceCapabilities *caps;
73 uint32_t size;
75 size = sizeof(*caps) + VD_AGENT_CAPS_BYTES;
76 caps = calloc(1, size);
77 if (!caps) {
78 fprintf(logfile,
79 "out of memory allocating capabilities array (write)\n");
80 return;
83 caps->request = request;
84 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MOUSE_STATE);
85 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MONITORS_CONFIG);
86 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_REPLY);
87 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND);
89 vdagent_virtio_port_write(port, VDP_CLIENT_PORT,
90 VD_AGENT_ANNOUNCE_CAPABILITIES, 0,
91 (uint8_t *)caps, size);
92 free(caps);
95 static void do_client_monitors(struct vdagent_virtio_port *port, int port_nr,
96 VDAgentMessage *message_header, VDAgentMonitorsConfig *new_monitors)
98 VDAgentReply reply;
99 uint32_t size;
101 /* Store monitor config to send to agents when they connect */
102 size = sizeof(VDAgentMonitorsConfig) +
103 new_monitors->num_of_monitors * sizeof(VDAgentMonConfig);
104 if (message_header->size != size) {
105 fprintf(logfile, "invalid message size for VDAgentMonitorsConfig\n");
106 return;
109 if (!mon_config ||
110 mon_config->num_of_monitors != new_monitors->num_of_monitors) {
111 free(mon_config);
112 mon_config = malloc(size);
113 if (!mon_config) {
114 fprintf(logfile, "out of memory allocating monitors config\n");
115 return;
118 memcpy(mon_config, new_monitors, size);
120 /* Send monitor config to currently connected agents */
121 udscs_server_write_all(server, VDAGENTD_MONITORS_CONFIG, 0,
122 (uint8_t *)mon_config, size);
124 /* Acknowledge reception of monitors config to spice server / client */
125 reply.type = VD_AGENT_MONITORS_CONFIG;
126 reply.error = VD_AGENT_SUCCESS;
127 vdagent_virtio_port_write(port, port_nr, VD_AGENT_REPLY, 0,
128 (uint8_t *)&reply, sizeof(reply));
131 static void do_client_capabilities(struct vdagent_virtio_port *port,
132 VDAgentMessage *message_header,
133 VDAgentAnnounceCapabilities *caps)
135 int new_size = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header->size);
137 if (capabilities_size != new_size) {
138 capabilities_size = new_size;
139 free(capabilities);
140 capabilities = malloc(capabilities_size * sizeof(uint32_t));
141 if (!capabilities) {
142 fprintf(logfile,
143 "out of memory allocating capabilities array (read)\n");
144 capabilities_size = 0;
145 return;
148 memcpy(capabilities, caps->caps, capabilities_size * sizeof(uint32_t));
149 if (caps->request)
150 send_capabilities(port, 0);
153 static void do_client_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;
159 if (!active_session_conn) {
160 fprintf(logfile,
161 "Could not find an agent connnection belonging to the "
162 "active session, ignoring client clipboard request\n");
163 return;
166 switch (message_header->type) {
167 case VD_AGENT_CLIPBOARD_GRAB:
168 type = VDAGENTD_CLIPBOARD_GRAB;
169 data = message_data;
170 size = message_header->size;
171 agent_owns_clipboard = 0;
172 break;
173 case VD_AGENT_CLIPBOARD_REQUEST: {
174 VDAgentClipboardRequest *req = (VDAgentClipboardRequest *)message_data;
175 type = VDAGENTD_CLIPBOARD_REQUEST;
176 opaque = req->type;
177 break;
179 case VD_AGENT_CLIPBOARD: {
180 VDAgentClipboard *clipboard = (VDAgentClipboard *)message_data;
181 type = VDAGENTD_CLIPBOARD_DATA;
182 opaque = clipboard->type;
183 size = message_header->size - sizeof(VDAgentClipboard);
184 data = clipboard->data;
185 break;
187 case VD_AGENT_CLIPBOARD_RELEASE:
188 type = VDAGENTD_CLIPBOARD_RELEASE;
189 break;
192 udscs_write(active_session_conn, type, opaque, data, size);
195 int virtio_port_read_complete(
196 struct vdagent_virtio_port *port,
197 VDIChunkHeader *chunk_header,
198 VDAgentMessage *message_header,
199 uint8_t *data)
201 uint32_t min_size = 0;
203 if (message_header->protocol != VD_AGENT_PROTOCOL) {
204 fprintf(logfile, "message with wrong protocol version ignoring\n");
205 return 0;
208 switch (message_header->type) {
209 case VD_AGENT_MOUSE_STATE:
210 if (message_header->size != sizeof(VDAgentMouseState))
211 goto size_error;
212 vdagentd_uinput_do_mouse(&uinput, (VDAgentMouseState *)data);
213 if (!uinput) {
214 /* Try to re-open the tablet */
215 struct agent_data *agent_data =
216 udscs_get_user_data(active_session_conn);
217 if (agent_data)
218 uinput = vdagentd_uinput_create(uinput_device,
219 agent_data->width,
220 agent_data->height,
221 logfile, debug > 1);
222 if (!uinput) {
223 fprintf(logfile, "Fatal uinput error\n");
224 retval = 1;
225 quit = 1;
228 break;
229 case VD_AGENT_MONITORS_CONFIG:
230 if (message_header->size < sizeof(VDAgentMonitorsConfig))
231 goto size_error;
232 do_client_monitors(port, chunk_header->port, message_header,
233 (VDAgentMonitorsConfig *)data);
234 break;
235 case VD_AGENT_ANNOUNCE_CAPABILITIES:
236 if (message_header->size < sizeof(VDAgentAnnounceCapabilities))
237 goto size_error;
238 do_client_capabilities(port, message_header,
239 (VDAgentAnnounceCapabilities *)data);
240 break;
241 case VD_AGENT_CLIPBOARD_GRAB:
242 case VD_AGENT_CLIPBOARD_REQUEST:
243 case VD_AGENT_CLIPBOARD:
244 case VD_AGENT_CLIPBOARD_RELEASE:
245 switch (message_header->type) {
246 case VD_AGENT_CLIPBOARD_GRAB:
247 min_size = sizeof(VDAgentClipboardGrab); break;
248 case VD_AGENT_CLIPBOARD_REQUEST:
249 min_size = sizeof(VDAgentClipboardRequest); break;
250 case VD_AGENT_CLIPBOARD:
251 min_size = sizeof(VDAgentClipboard); break;
253 if (message_header->size < min_size)
254 goto size_error;
255 do_client_clipboard(port, message_header, data);
256 break;
257 default:
258 if (debug)
259 fprintf(logfile, "unknown message type %d\n", message_header->type);
260 break;
263 return 0;
265 size_error:
266 fprintf(logfile, "read: invalid message size: %u for message type: %u\n",
267 message_header->size, message_header->type);
268 return 0;
271 /* vdagentd <-> vdagent communication handling */
272 void do_agent_clipboard(struct udscs_connection *conn,
273 struct udscs_message_header *header, const uint8_t *data)
275 if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
276 VD_AGENT_CAP_CLIPBOARD_BY_DEMAND))
277 goto error;
279 /* Check that this agent is from the currently active session */
280 if (conn != active_session_conn) {
281 fprintf(logfile, "Clipboard request from agent "
282 "which is not in the active session?\n");
283 goto error;
286 if (!virtio_port) {
287 fprintf(logfile,
288 "Clipboard request from agent but no client connection\n");
289 goto error;
292 switch (header->type) {
293 case VDAGENTD_CLIPBOARD_GRAB:
294 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
295 VD_AGENT_CLIPBOARD_GRAB, 0,
296 data, header->size);
297 agent_owns_clipboard = 1;
298 break;
299 case VDAGENTD_CLIPBOARD_REQUEST: {
300 VDAgentClipboardRequest req = { .type = header->opaque };
301 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
302 VD_AGENT_CLIPBOARD_REQUEST, 0,
303 (uint8_t *)&req, sizeof(req));
304 break;
306 case VDAGENTD_CLIPBOARD_DATA: {
307 VDAgentClipboard *clipboard;
308 uint32_t size = sizeof(*clipboard) + header->size;
310 clipboard = calloc(1, size);
311 if (!clipboard) {
312 fprintf(logfile,
313 "out of memory allocating clipboard (write)\n");
314 return;
316 clipboard->type = header->opaque;
317 memcpy(clipboard->data, data, header->size);
319 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
320 VD_AGENT_CLIPBOARD, 0,
321 (uint8_t *)clipboard, size);
322 free(clipboard);
323 break;
325 case VDAGENTD_CLIPBOARD_RELEASE:
326 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
327 VD_AGENT_CLIPBOARD_RELEASE, 0, NULL, 0);
328 agent_owns_clipboard = 0;
329 break;
332 return;
334 error:
335 if (header->type == VDAGENTD_CLIPBOARD_REQUEST) {
336 /* Let the agent know no answer is coming */
337 udscs_write(conn, VDAGENTD_CLIPBOARD_DATA,
338 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
342 /* When we open the vdagent virtio channel, the server automatically goes into
343 client mouse mode, so we can only have the channel open when we know the
344 active session resolution. This function checks that we have an agent in the
345 active session, and that it has told us its resolution. If these conditions
346 are met it sets the uinput tablet device's resolution and opens the virtio
347 channel (if it is not already open). If these conditions are not met, it
348 closes both. */
349 static void check_xorg_resolution(void) {
350 struct agent_data *agent_data = udscs_get_user_data(active_session_conn);
352 if (agent_data && agent_data->width) {
353 if (!uinput)
354 uinput = vdagentd_uinput_create(uinput_device,
355 agent_data->width,
356 agent_data->height,
357 logfile, debug > 1);
358 else
359 vdagentd_uinput_update_size(&uinput, agent_data->width,
360 agent_data->height);
361 if (!uinput) {
362 fprintf(logfile, "Fatal uinput error\n");
363 retval = 1;
364 quit = 1;
367 if (!virtio_port) {
368 fprintf(logfile, "opening vdagent virtio channel\n");
369 virtio_port = vdagent_virtio_port_create(portdev,
370 virtio_port_read_complete,
371 NULL, logfile);
372 if (!virtio_port) {
373 fprintf(logfile,
374 "Fatal error opening vdagent virtio channel\n");
375 retval = 1;
376 quit = 1;
377 return;
379 send_capabilities(virtio_port, 1);
381 } else {
382 vdagentd_uinput_destroy(&uinput);
383 if (virtio_port) {
384 vdagent_virtio_port_flush(&virtio_port);
385 vdagent_virtio_port_destroy(&virtio_port);
386 fprintf(logfile, "closed vdagent virtio channel\n");
391 static int connection_matches_active_session(struct udscs_connection **connp,
392 void *priv)
394 struct udscs_connection **conn_ret = (struct udscs_connection **)priv;
395 struct agent_data *agent_data = udscs_get_user_data(*connp);
397 /* Check if this connection matches the currently active session */
398 if (!agent_data->session || !active_session)
399 return 0;
400 if (strcmp(agent_data->session, active_session))
401 return 0;
403 *conn_ret = *connp;
404 return 1;
407 void update_active_session_connection(void)
409 struct udscs_connection *new_conn = NULL;
410 int n;
412 if (!active_session)
413 active_session = console_kit_get_active_session(console_kit);
415 n = udscs_server_for_all_clients(server, connection_matches_active_session,
416 (void*)&new_conn);
417 if (n != 1)
418 new_conn = NULL;
420 if (new_conn == active_session_conn)
421 return;
423 active_session_conn = new_conn;
425 if (agent_owns_clipboard && virtio_port)
426 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
427 VD_AGENT_CLIPBOARD_RELEASE, 0, NULL, 0);
428 agent_owns_clipboard = 0;
430 check_xorg_resolution();
433 void agent_connect(struct udscs_connection *conn)
435 uint32_t pid;
436 struct agent_data *agent_data;
438 agent_data = calloc(1, sizeof(*agent_data));
439 if (!agent_data) {
440 fprintf(logfile, "Out of memory allocating agent data, disconnecting\n");
441 udscs_destroy_connection(&conn);
442 return;
445 pid = udscs_get_peer_cred(conn).pid;
446 agent_data->session = console_kit_session_for_pid(console_kit, pid);
447 udscs_set_user_data(conn, (void *)agent_data);
448 update_active_session_connection();
450 if (mon_config)
451 udscs_write(conn, VDAGENTD_MONITORS_CONFIG, 0, (uint8_t *)mon_config,
452 sizeof(VDAgentMonitorsConfig) +
453 mon_config->num_of_monitors * sizeof(VDAgentMonConfig));
456 void agent_disconnect(struct udscs_connection *conn)
458 struct agent_data *agent_data = udscs_get_user_data(conn);
460 free(agent_data->session);
461 agent_data->session = NULL;
462 update_active_session_connection();
464 free(agent_data);
467 void agent_read_complete(struct udscs_connection **connp,
468 struct udscs_message_header *header, const uint8_t *data)
470 struct agent_data *agent_data = udscs_get_user_data(*connp);
472 switch (header->type) {
473 case VDAGENTD_GUEST_XORG_RESOLUTION: {
474 struct vdagentd_guest_xorg_resolution *res =
475 (struct vdagentd_guest_xorg_resolution *)data;
477 if (header->size != sizeof(*res)) {
478 fprintf(logfile,
479 "guest xorg resolution message has wrong size, disconnecting agent\n");
480 udscs_destroy_connection(connp);
481 return;
484 agent_data->width = res->width;
485 agent_data->height = res->height;
486 check_xorg_resolution();
487 break;
489 case VDAGENTD_CLIPBOARD_GRAB:
490 case VDAGENTD_CLIPBOARD_REQUEST:
491 case VDAGENTD_CLIPBOARD_DATA:
492 case VDAGENTD_CLIPBOARD_RELEASE:
493 do_agent_clipboard(*connp, header, data);
494 break;
495 default:
496 fprintf(logfile, "unknown message from vdagent: %u, ignoring\n",
497 header->type);
501 /* main */
503 static void usage(FILE *fp)
505 fprintf(fp,
506 "vdagentd\n"
507 "options:\n"
508 " -h print this text\n"
509 " -d log debug messages (use twice for extra info)\n"
510 " -s <port> set virtio serial port [%s]\n"
511 " -u <dev> set uinput device [%s]\n"
512 " -x don't daemonize (and log to logfile)\n",
513 portdev, uinput_device);
516 void daemonize(void)
518 int x;
519 FILE *pidfile;
521 /* detach from terminal */
522 switch (fork()) {
523 case 0:
524 close(0); close(1); close(2);
525 setsid();
526 x = open("/dev/null", O_RDWR); x = dup(0); x = dup(0);
527 pidfile = fopen(pidfilename, "w");
528 if (pidfile) {
529 fprintf(pidfile, "%d\n", (int)getpid());
530 fclose(pidfile);
532 break;
533 case -1:
534 fprintf(logfile, "fork: %s\n", strerror(errno));
535 retval = 1;
536 default:
537 udscs_destroy_server(server);
538 if (logfile != stderr)
539 fclose(logfile);
540 exit(0);
544 void main_loop(void)
546 fd_set readfds, writefds;
547 int n, nfds, ck_fd = 0;
549 while (!quit) {
550 FD_ZERO(&readfds);
551 FD_ZERO(&writefds);
553 nfds = udscs_server_fill_fds(server, &readfds, &writefds);
554 n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds);
555 if (n >= nfds)
556 nfds = n + 1;
558 ck_fd = console_kit_get_fd(console_kit);
559 FD_SET(ck_fd, &readfds);
560 if (ck_fd >= nfds)
561 nfds = ck_fd + 1;
563 n = select(nfds, &readfds, &writefds, NULL, NULL);
564 if (n == -1) {
565 if (errno == EINTR)
566 continue;
567 fprintf(logfile, "Fatal error select: %s\n", strerror(errno));
568 retval = 1;
569 break;
572 udscs_server_handle_fds(server, &readfds, &writefds);
574 if (virtio_port) {
575 vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds);
576 if (!virtio_port) {
577 fprintf(logfile,
578 "AIIEEE lost spice client connection, reconnecting\n");
579 virtio_port = vdagent_virtio_port_create(portdev,
580 virtio_port_read_complete,
581 NULL, logfile);
583 if (!virtio_port) {
584 fprintf(logfile,
585 "Fatal error opening vdagent virtio channel\n");
586 retval = 1;
587 break;
591 if (FD_ISSET(ck_fd, &readfds)) {
592 active_session = console_kit_get_active_session(console_kit);
593 update_active_session_connection();
595 fflush(logfile);
599 static void quit_handler(int sig)
601 quit = 1;
604 int main(int argc, char *argv[])
606 int c;
607 int do_daemonize = 1;
608 struct sigaction act;
610 for (;;) {
611 if (-1 == (c = getopt(argc, argv, "-dhxs:u:")))
612 break;
613 switch (c) {
614 case 'd':
615 debug++;
616 break;
617 case 's':
618 portdev = optarg;
619 break;
620 case 'u':
621 uinput_device = optarg;
622 break;
623 case 'x':
624 do_daemonize = 0;
625 break;
626 case 'h':
627 usage(stdout);
628 return 0;
629 default:
630 usage(stderr);
631 return 1;
635 memset(&act, 0, sizeof(act));
636 act.sa_flags = SA_RESTART;
637 act.sa_handler = quit_handler;
638 sigaction(SIGINT, &act, NULL);
639 sigaction(SIGHUP, &act, NULL);
640 sigaction(SIGTERM, &act, NULL);
641 sigaction(SIGQUIT, &act, NULL);
643 if (do_daemonize) {
644 logfile = fopen(logfilename, "a");
645 if (!logfile) {
646 fprintf(stderr, "Error opening %s: %s\n", logfilename,
647 strerror(errno));
648 logfile = stderr;
650 } else
651 logfile = stderr;
653 /* Setup communication with vdagent process(es) */
654 server = udscs_create_server(VDAGENTD_SOCKET, agent_connect,
655 agent_read_complete, agent_disconnect,
656 vdagentd_messages, VDAGENTD_NO_MESSAGES,
657 debug? logfile:NULL, logfile);
658 if (!server) {
659 fprintf(logfile, "Fatal could not create server socket %s\n",
660 VDAGENTD_SOCKET);
661 return 1;
663 if (chmod(VDAGENTD_SOCKET, 0666)) {
664 fprintf(logfile, "Fatal could not change permissions on %s: %s\n",
665 VDAGENTD_SOCKET, strerror(errno));
666 udscs_destroy_server(server);
667 return 1;
670 if (do_daemonize)
671 daemonize();
673 console_kit = console_kit_create(logfile);
674 if (!console_kit) {
675 fprintf(logfile, "Fatal could not connect to console kit\n");
676 udscs_destroy_server(server);
677 return 1;
680 main_loop();
682 if (agent_owns_clipboard && virtio_port)
683 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
684 VD_AGENT_CLIPBOARD_RELEASE, 0, NULL, 0);
686 vdagentd_uinput_destroy(&uinput);
687 vdagent_virtio_port_flush(&virtio_port);
688 vdagent_virtio_port_destroy(&virtio_port);
689 console_kit_destroy(console_kit);
690 udscs_destroy_server(server);
691 if (unlink(VDAGENTD_SOCKET) != 0)
692 fprintf(logfile, "unlink %s: %s\n", VDAGENTD_SOCKET, strerror(errno));
693 fprintf(logfile, "vdagentd quiting, returning status %d\n", retval);
694 if (logfile != stderr)
695 fclose(logfile);
697 if (do_daemonize)
698 unlink(pidfilename);
700 return retval;