vdagentd: Fix a gcc 4.6 warning
[vd_agent.git] / vdagentd.c
blob9f9843ac7da3a054e39ff41295bb2e837e27cef2
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/spice-vdagentd.log";
49 static const char *pidfilename = "/var/run/spice-vdagentd/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;
365 return;
368 if (!virtio_port) {
369 fprintf(logfile, "opening vdagent virtio channel\n");
370 virtio_port = vdagent_virtio_port_create(portdev,
371 virtio_port_read_complete,
372 NULL, logfile);
373 if (!virtio_port) {
374 fprintf(logfile,
375 "Fatal error opening vdagent virtio channel\n");
376 retval = 1;
377 quit = 1;
378 return;
380 send_capabilities(virtio_port, 1);
382 } else {
383 vdagentd_uinput_destroy(&uinput);
384 if (virtio_port) {
385 vdagent_virtio_port_flush(&virtio_port);
386 vdagent_virtio_port_destroy(&virtio_port);
387 fprintf(logfile, "closed vdagent virtio channel\n");
392 static int connection_matches_active_session(struct udscs_connection **connp,
393 void *priv)
395 struct udscs_connection **conn_ret = (struct udscs_connection **)priv;
396 struct agent_data *agent_data = udscs_get_user_data(*connp);
398 /* Check if this connection matches the currently active session */
399 if (!agent_data->session || !active_session)
400 return 0;
401 if (strcmp(agent_data->session, active_session))
402 return 0;
404 *conn_ret = *connp;
405 return 1;
408 void update_active_session_connection(void)
410 struct udscs_connection *new_conn = NULL;
411 int n;
413 if (!active_session)
414 active_session = console_kit_get_active_session(console_kit);
416 n = udscs_server_for_all_clients(server, connection_matches_active_session,
417 (void*)&new_conn);
418 if (n != 1)
419 new_conn = NULL;
421 if (new_conn == active_session_conn)
422 return;
424 active_session_conn = new_conn;
426 if (agent_owns_clipboard && virtio_port)
427 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
428 VD_AGENT_CLIPBOARD_RELEASE, 0, NULL, 0);
429 agent_owns_clipboard = 0;
431 check_xorg_resolution();
434 void agent_connect(struct udscs_connection *conn)
436 uint32_t pid;
437 struct agent_data *agent_data;
439 agent_data = calloc(1, sizeof(*agent_data));
440 if (!agent_data) {
441 fprintf(logfile, "Out of memory allocating agent data, disconnecting\n");
442 udscs_destroy_connection(&conn);
443 return;
446 pid = udscs_get_peer_cred(conn).pid;
447 agent_data->session = console_kit_session_for_pid(console_kit, pid);
448 udscs_set_user_data(conn, (void *)agent_data);
449 update_active_session_connection();
451 if (mon_config)
452 udscs_write(conn, VDAGENTD_MONITORS_CONFIG, 0, (uint8_t *)mon_config,
453 sizeof(VDAgentMonitorsConfig) +
454 mon_config->num_of_monitors * sizeof(VDAgentMonConfig));
457 void agent_disconnect(struct udscs_connection *conn)
459 struct agent_data *agent_data = udscs_get_user_data(conn);
461 free(agent_data->session);
462 agent_data->session = NULL;
463 update_active_session_connection();
465 free(agent_data);
468 void agent_read_complete(struct udscs_connection **connp,
469 struct udscs_message_header *header, const uint8_t *data)
471 struct agent_data *agent_data = udscs_get_user_data(*connp);
473 switch (header->type) {
474 case VDAGENTD_GUEST_XORG_RESOLUTION: {
475 struct vdagentd_guest_xorg_resolution *res =
476 (struct vdagentd_guest_xorg_resolution *)data;
478 if (header->size != sizeof(*res)) {
479 fprintf(logfile,
480 "guest xorg resolution message has wrong size, disconnecting agent\n");
481 udscs_destroy_connection(connp);
482 return;
485 agent_data->width = res->width;
486 agent_data->height = res->height;
487 check_xorg_resolution();
488 break;
490 case VDAGENTD_CLIPBOARD_GRAB:
491 case VDAGENTD_CLIPBOARD_REQUEST:
492 case VDAGENTD_CLIPBOARD_DATA:
493 case VDAGENTD_CLIPBOARD_RELEASE:
494 do_agent_clipboard(*connp, header, data);
495 break;
496 default:
497 fprintf(logfile, "unknown message from vdagent: %u, ignoring\n",
498 header->type);
502 /* main */
504 static void usage(FILE *fp)
506 fprintf(fp,
507 "vdagentd\n"
508 "options:\n"
509 " -h print this text\n"
510 " -d log debug messages (use twice for extra info)\n"
511 " -s <port> set virtio serial port [%s]\n"
512 " -u <dev> set uinput device [%s]\n"
513 " -x don't daemonize (and log to logfile)\n",
514 portdev, uinput_device);
517 void daemonize(void)
519 int x;
520 FILE *pidfile;
522 /* detach from terminal */
523 switch (fork()) {
524 case 0:
525 close(0); close(1); close(2);
526 setsid();
527 x = open("/dev/null", O_RDWR); dup(x); dup(x);
528 pidfile = fopen(pidfilename, "w");
529 if (pidfile) {
530 fprintf(pidfile, "%d\n", (int)getpid());
531 fclose(pidfile);
533 break;
534 case -1:
535 fprintf(logfile, "fork: %s\n", strerror(errno));
536 retval = 1;
537 default:
538 udscs_destroy_server(server);
539 if (logfile != stderr)
540 fclose(logfile);
541 exit(retval);
545 void main_loop(void)
547 fd_set readfds, writefds;
548 int n, nfds, ck_fd = 0;
550 while (!quit) {
551 FD_ZERO(&readfds);
552 FD_ZERO(&writefds);
554 nfds = udscs_server_fill_fds(server, &readfds, &writefds);
555 n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds);
556 if (n >= nfds)
557 nfds = n + 1;
559 ck_fd = console_kit_get_fd(console_kit);
560 FD_SET(ck_fd, &readfds);
561 if (ck_fd >= nfds)
562 nfds = ck_fd + 1;
564 n = select(nfds, &readfds, &writefds, NULL, NULL);
565 if (n == -1) {
566 if (errno == EINTR)
567 continue;
568 fprintf(logfile, "Fatal error select: %s\n", strerror(errno));
569 retval = 1;
570 break;
573 udscs_server_handle_fds(server, &readfds, &writefds);
575 if (virtio_port) {
576 vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds);
577 if (!virtio_port) {
578 fprintf(logfile,
579 "AIIEEE lost spice client connection, reconnecting\n");
580 virtio_port = vdagent_virtio_port_create(portdev,
581 virtio_port_read_complete,
582 NULL, logfile);
584 if (!virtio_port) {
585 fprintf(logfile,
586 "Fatal error opening vdagent virtio channel\n");
587 retval = 1;
588 break;
592 if (FD_ISSET(ck_fd, &readfds)) {
593 active_session = console_kit_get_active_session(console_kit);
594 update_active_session_connection();
596 fflush(logfile);
600 static void quit_handler(int sig)
602 quit = 1;
605 int main(int argc, char *argv[])
607 int c;
608 int do_daemonize = 1;
609 struct sigaction act;
611 for (;;) {
612 if (-1 == (c = getopt(argc, argv, "-dhxs:u:")))
613 break;
614 switch (c) {
615 case 'd':
616 debug++;
617 break;
618 case 's':
619 portdev = optarg;
620 break;
621 case 'u':
622 uinput_device = optarg;
623 break;
624 case 'x':
625 do_daemonize = 0;
626 break;
627 case 'h':
628 usage(stdout);
629 return 0;
630 default:
631 usage(stderr);
632 return 1;
636 memset(&act, 0, sizeof(act));
637 act.sa_flags = SA_RESTART;
638 act.sa_handler = quit_handler;
639 sigaction(SIGINT, &act, NULL);
640 sigaction(SIGHUP, &act, NULL);
641 sigaction(SIGTERM, &act, NULL);
642 sigaction(SIGQUIT, &act, NULL);
644 if (do_daemonize) {
645 logfile = fopen(logfilename, "a");
646 if (!logfile) {
647 fprintf(stderr, "Error opening %s: %s\n", logfilename,
648 strerror(errno));
649 logfile = stderr;
651 } else
652 logfile = stderr;
654 /* Setup communication with vdagent process(es) */
655 server = udscs_create_server(VDAGENTD_SOCKET, agent_connect,
656 agent_read_complete, agent_disconnect,
657 vdagentd_messages, VDAGENTD_NO_MESSAGES,
658 debug? logfile:NULL, logfile);
659 if (!server) {
660 fprintf(logfile, "Fatal could not create server socket %s\n",
661 VDAGENTD_SOCKET);
662 if (logfile != stderr)
663 fclose(logfile);
664 return 1;
666 if (chmod(VDAGENTD_SOCKET, 0666)) {
667 fprintf(logfile, "Fatal could not change permissions on %s: %s\n",
668 VDAGENTD_SOCKET, strerror(errno));
669 udscs_destroy_server(server);
670 if (logfile != stderr)
671 fclose(logfile);
672 return 1;
675 if (do_daemonize)
676 daemonize();
678 console_kit = console_kit_create(logfile);
679 if (!console_kit) {
680 fprintf(logfile, "Fatal could not connect to console kit\n");
681 udscs_destroy_server(server);
682 if (logfile != stderr)
683 fclose(logfile);
684 return 1;
687 main_loop();
689 if (agent_owns_clipboard && virtio_port)
690 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
691 VD_AGENT_CLIPBOARD_RELEASE, 0, NULL, 0);
693 vdagentd_uinput_destroy(&uinput);
694 vdagent_virtio_port_flush(&virtio_port);
695 vdagent_virtio_port_destroy(&virtio_port);
696 console_kit_destroy(console_kit);
697 udscs_destroy_server(server);
698 if (unlink(VDAGENTD_SOCKET) != 0)
699 fprintf(logfile, "unlink %s: %s\n", VDAGENTD_SOCKET, strerror(errno));
700 fprintf(logfile, "vdagentd quiting, returning status %d\n", retval);
701 if (logfile != stderr)
702 fclose(logfile);
704 if (do_daemonize)
705 unlink(pidfilename);
707 return retval;