vdagent: do not use exit()
[vd_agent/hramrach.git] / vdagentd.c
blobf5950674897ae36bdcf9065c601d594d2ae76a97
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 <sys/select.h>
30 #include <sys/stat.h>
31 #include <spice/vd_agent.h>
33 #include "udscs.h"
34 #include "vdagentd-proto.h"
35 #include "vdagentd-proto-strings.h"
36 #include "vdagentd-uinput.h"
37 #include "vdagent-virtio-port.h"
38 #include "console-kit.h"
40 struct agent_data {
41 char *session;
42 int width;
43 int height;
46 /* variables */
47 static const char *logfilename = "/var/log/vdagentd.log";
48 static const char *portdev = "/dev/virtio-ports/com.redhat.spice.0";
49 static const char *uinput_device = "/dev/uinput";
50 static int debug = 0;
51 static struct udscs_server *server = NULL;
52 static struct vdagent_virtio_port *virtio_port = NULL;
53 static struct console_kit *console_kit = NULL;
54 static struct vdagentd_uinput *uinput = NULL;
55 static VDAgentMonitorsConfig *mon_config = NULL;
56 static uint32_t *capabilities = NULL;
57 static int capabilities_size = 0;
58 static const char *active_session = NULL;
59 static struct udscs_connection *active_session_conn = NULL;
60 static int agent_owns_clipboard = 0;
61 static FILE *logfile = NULL;
62 static int quit = 0;
63 static int retval = 0;
65 /* utility functions */
66 /* vdagentd <-> spice-client communication handling */
67 static void send_capabilities(struct vdagent_virtio_port *port,
68 uint32_t request)
70 VDAgentAnnounceCapabilities *caps;
71 uint32_t size;
73 size = sizeof(*caps) + VD_AGENT_CAPS_BYTES;
74 caps = calloc(1, size);
75 if (!caps) {
76 fprintf(logfile,
77 "out of memory allocating capabilities array (write)\n");
78 return;
81 caps->request = request;
82 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MOUSE_STATE);
83 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MONITORS_CONFIG);
84 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_REPLY);
85 VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND);
87 vdagent_virtio_port_write(port, VDP_CLIENT_PORT,
88 VD_AGENT_ANNOUNCE_CAPABILITIES, 0,
89 (uint8_t *)caps, size);
90 free(caps);
93 static void do_client_monitors(struct vdagent_virtio_port *port, int port_nr,
94 VDAgentMessage *message_header, VDAgentMonitorsConfig *new_monitors)
96 VDAgentReply reply;
97 uint32_t size;
99 /* Store monitor config to send to agents when they connect */
100 size = sizeof(VDAgentMonitorsConfig) +
101 new_monitors->num_of_monitors * sizeof(VDAgentMonConfig);
102 if (message_header->size != size) {
103 fprintf(logfile, "invalid message size for VDAgentMonitorsConfig\n");
104 return;
107 if (!mon_config ||
108 mon_config->num_of_monitors != new_monitors->num_of_monitors) {
109 free(mon_config);
110 mon_config = malloc(size);
111 if (!mon_config) {
112 fprintf(logfile, "out of memory allocating monitors config\n");
113 return;
116 memcpy(mon_config, new_monitors, size);
118 /* Send monitor config to currently connected agents */
119 udscs_server_write_all(server, VDAGENTD_MONITORS_CONFIG, 0,
120 (uint8_t *)mon_config, size);
122 /* Acknowledge reception of monitors config to spice server / client */
123 reply.type = VD_AGENT_MONITORS_CONFIG;
124 reply.error = VD_AGENT_SUCCESS;
125 vdagent_virtio_port_write(port, port_nr, VD_AGENT_REPLY, 0,
126 (uint8_t *)&reply, sizeof(reply));
129 static void do_client_capabilities(struct vdagent_virtio_port *port,
130 VDAgentMessage *message_header,
131 VDAgentAnnounceCapabilities *caps)
133 capabilities_size = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header->size);
135 free(capabilities);
136 capabilities = malloc(capabilities_size * sizeof(uint32_t));
137 if (!capabilities) {
138 fprintf(logfile,
139 "out of memory allocating capabilities array (read)\n");
140 capabilities_size = 0;
141 return;
143 memcpy(capabilities, caps->caps, capabilities_size * sizeof(uint32_t));
144 if (caps->request)
145 send_capabilities(port, 0);
148 static void do_client_clipboard(struct vdagent_virtio_port *port,
149 VDAgentMessage *message_header, uint8_t *message_data)
151 uint32_t type = 0, opaque = 0, size = 0;
152 uint8_t *data = NULL;
154 if (!active_session_conn) {
155 fprintf(logfile,
156 "Could not find an agent connnection belonging to the "
157 "active session, ignoring client clipboard request\n");
158 return;
161 switch (message_header->type) {
162 case VD_AGENT_CLIPBOARD_GRAB:
163 type = VDAGENTD_CLIPBOARD_GRAB;
164 data = message_data;
165 size = message_header->size;
166 agent_owns_clipboard = 0;
167 break;
168 case VD_AGENT_CLIPBOARD_REQUEST: {
169 VDAgentClipboardRequest *req = (VDAgentClipboardRequest *)message_data;
170 type = VDAGENTD_CLIPBOARD_REQUEST;
171 opaque = req->type;
172 break;
174 case VD_AGENT_CLIPBOARD: {
175 VDAgentClipboard *clipboard = (VDAgentClipboard *)message_data;
176 type = VDAGENTD_CLIPBOARD_DATA;
177 opaque = clipboard->type;
178 size = message_header->size - sizeof(VDAgentClipboard);
179 data = clipboard->data;
180 break;
182 case VD_AGENT_CLIPBOARD_RELEASE:
183 type = VDAGENTD_CLIPBOARD_RELEASE;
184 break;
187 udscs_write(active_session_conn, type, opaque, data, size);
190 int virtio_port_read_complete(
191 struct vdagent_virtio_port *port,
192 VDIChunkHeader *chunk_header,
193 VDAgentMessage *message_header,
194 uint8_t *data)
196 uint32_t min_size = 0;
198 if (message_header->protocol != VD_AGENT_PROTOCOL) {
199 fprintf(logfile, "message with wrong protocol version ignoring\n");
200 return 0;
203 switch (message_header->type) {
204 case VD_AGENT_MOUSE_STATE:
205 if (message_header->size != sizeof(VDAgentMouseState))
206 goto size_error;
207 vdagentd_uinput_do_mouse(&uinput, (VDAgentMouseState *)data);
208 if (!uinput) {
209 /* Try to re-open the tablet */
210 struct agent_data *agent_data =
211 udscs_get_user_data(active_session_conn);
212 if (agent_data)
213 uinput = vdagentd_uinput_create(uinput_device,
214 agent_data->width,
215 agent_data->height,
216 logfile, debug > 1);
217 if (!uinput) {
218 fprintf(logfile, "Fatal uinput error\n");
219 retval = 1;
220 quit = 1;
223 break;
224 case VD_AGENT_MONITORS_CONFIG:
225 if (message_header->size < sizeof(VDAgentMonitorsConfig))
226 goto size_error;
227 do_client_monitors(port, chunk_header->port, message_header,
228 (VDAgentMonitorsConfig *)data);
229 break;
230 case VD_AGENT_ANNOUNCE_CAPABILITIES:
231 if (message_header->size < sizeof(VDAgentAnnounceCapabilities))
232 goto size_error;
233 do_client_capabilities(port, message_header,
234 (VDAgentAnnounceCapabilities *)data);
235 break;
236 case VD_AGENT_CLIPBOARD_GRAB:
237 case VD_AGENT_CLIPBOARD_REQUEST:
238 case VD_AGENT_CLIPBOARD:
239 case VD_AGENT_CLIPBOARD_RELEASE:
240 switch (message_header->type) {
241 case VD_AGENT_CLIPBOARD_GRAB:
242 min_size = sizeof(VDAgentClipboardGrab); break;
243 case VD_AGENT_CLIPBOARD_REQUEST:
244 min_size = sizeof(VDAgentClipboardRequest); break;
245 case VD_AGENT_CLIPBOARD:
246 min_size = sizeof(VDAgentClipboard); break;
248 if (message_header->size < min_size)
249 goto size_error;
250 do_client_clipboard(port, message_header, data);
251 break;
252 default:
253 if (debug)
254 fprintf(logfile, "unknown message type %d\n", message_header->type);
255 break;
258 return 0;
260 size_error:
261 fprintf(logfile, "read: invalid message size: %u for message type: %u\n",
262 message_header->size, message_header->type);
263 return 0;
266 /* vdagentd <-> vdagent communication handling */
267 void do_agent_clipboard(struct udscs_connection *conn,
268 struct udscs_message_header *header, const uint8_t *data)
270 if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
271 VD_AGENT_CAP_CLIPBOARD_BY_DEMAND))
272 goto error;
274 /* Check that this agent is from the currently active session */
275 if (conn != active_session_conn) {
276 fprintf(logfile, "Clipboard request from agent "
277 "which is not in the active session?\n");
278 goto error;
281 if (!virtio_port) {
282 fprintf(logfile,
283 "Clipboard request from agent but no client connection\n");
284 goto error;
287 switch (header->type) {
288 case VDAGENTD_CLIPBOARD_GRAB:
289 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
290 VD_AGENT_CLIPBOARD_GRAB, 0,
291 data, header->size);
292 agent_owns_clipboard = 1;
293 break;
294 case VDAGENTD_CLIPBOARD_REQUEST: {
295 VDAgentClipboardRequest req = { .type = header->opaque };
296 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
297 VD_AGENT_CLIPBOARD_REQUEST, 0,
298 (uint8_t *)&req, sizeof(req));
299 break;
301 case VDAGENTD_CLIPBOARD_DATA: {
302 VDAgentClipboard *clipboard;
303 uint32_t size = sizeof(*clipboard) + header->size;
305 clipboard = calloc(1, size);
306 if (!clipboard) {
307 fprintf(logfile,
308 "out of memory allocating clipboard (write)\n");
309 return;
311 clipboard->type = header->opaque;
312 memcpy(clipboard->data, data, header->size);
314 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
315 VD_AGENT_CLIPBOARD, 0,
316 (uint8_t *)clipboard, size);
317 free(clipboard);
318 break;
320 case VDAGENTD_CLIPBOARD_RELEASE:
321 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
322 VD_AGENT_CLIPBOARD_RELEASE, 0, NULL, 0);
323 agent_owns_clipboard = 0;
324 break;
327 return;
329 error:
330 if (header->type == VDAGENTD_CLIPBOARD_REQUEST) {
331 /* Let the agent know no answer is coming */
332 udscs_write(conn, VDAGENTD_CLIPBOARD_DATA,
333 VD_AGENT_CLIPBOARD_NONE, NULL, 0);
337 /* When we open the vdagent virtio channel, the server automatically goes into
338 client mouse mode, so we can only have the channel open when we know the
339 active session resolution. This function checks that we have an agent in the
340 active session, and that it has told us its resolution. If these conditions
341 are met it sets the uinput tablet device's resolution and opens the virtio
342 channel (if it is not already open). If these conditions are not met, it
343 closes both. */
344 static void check_xorg_resolution(void) {
345 struct agent_data *agent_data = udscs_get_user_data(active_session_conn);
347 if (agent_data && agent_data->width) {
348 if (!uinput)
349 uinput = vdagentd_uinput_create(uinput_device,
350 agent_data->width,
351 agent_data->height,
352 logfile, debug > 1);
353 else
354 vdagentd_uinput_update_size(&uinput, agent_data->width,
355 agent_data->height);
356 if (!uinput) {
357 fprintf(logfile, "Fatal uinput error\n");
358 retval = 1;
359 quit = 1;
362 if (!virtio_port) {
363 fprintf(logfile, "opening vdagent virtio channel\n");
364 virtio_port = vdagent_virtio_port_create(portdev,
365 virtio_port_read_complete,
366 NULL, logfile);
367 if (!virtio_port) {
368 fprintf(logfile,
369 "Fatal error opening vdagent virtio channel\n");
370 retval = 1;
371 quit = 1;
372 return;
374 send_capabilities(virtio_port, 1);
376 } else {
377 vdagentd_uinput_destroy(&uinput);
378 if (virtio_port) {
379 vdagent_virtio_port_flush(&virtio_port);
380 vdagent_virtio_port_destroy(&virtio_port);
381 fprintf(logfile, "closed vdagent virtio channel\n");
386 static int connection_matches_active_session(struct udscs_connection **connp,
387 void *priv)
389 struct udscs_connection **conn_ret = (struct udscs_connection **)priv;
390 struct agent_data *agent_data = udscs_get_user_data(*connp);
392 /* Check if this connection matches the currently active session */
393 if (!agent_data->session || !active_session)
394 return 0;
395 if (strcmp(agent_data->session, active_session))
396 return 0;
398 *conn_ret = *connp;
399 return 1;
402 void update_active_session_connection(void)
404 struct udscs_connection *new_conn = NULL;
405 int n;
407 n = udscs_server_for_all_clients(server, connection_matches_active_session,
408 (void*)&new_conn);
409 if (n != 1)
410 new_conn = NULL;
412 if (new_conn == active_session_conn)
413 return;
415 active_session_conn = new_conn;
417 if (agent_owns_clipboard && virtio_port)
418 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
419 VD_AGENT_CLIPBOARD_RELEASE, 0, NULL, 0);
420 agent_owns_clipboard = 0;
422 check_xorg_resolution();
425 void agent_connect(struct udscs_connection *conn)
427 uint32_t pid;
428 struct agent_data *agent_data;
430 agent_data = calloc(1, sizeof(*agent_data));
431 if (!agent_data) {
432 fprintf(logfile, "Out of memory allocating agent data, disconnecting\n");
433 udscs_destroy_connection(&conn);
434 return;
437 pid = udscs_get_peer_cred(conn).pid;
438 agent_data->session = console_kit_session_for_pid(console_kit, pid);
439 udscs_set_user_data(conn, (void *)agent_data);
440 update_active_session_connection();
442 if (mon_config)
443 udscs_write(conn, VDAGENTD_MONITORS_CONFIG, 0, (uint8_t *)mon_config,
444 sizeof(VDAgentMonitorsConfig) +
445 mon_config->num_of_monitors * sizeof(VDAgentMonConfig));
448 void agent_disconnect(struct udscs_connection *conn)
450 struct agent_data *agent_data = udscs_get_user_data(conn);
452 free(agent_data->session);
453 agent_data->session = NULL;
454 update_active_session_connection();
456 free(agent_data);
459 void agent_read_complete(struct udscs_connection **connp,
460 struct udscs_message_header *header, const uint8_t *data)
462 struct agent_data *agent_data = udscs_get_user_data(*connp);
464 switch (header->type) {
465 case VDAGENTD_GUEST_XORG_RESOLUTION: {
466 struct vdagentd_guest_xorg_resolution *res =
467 (struct vdagentd_guest_xorg_resolution *)data;
469 if (header->size != sizeof(*res)) {
470 fprintf(logfile,
471 "guest xorg resolution message has wrong size, disconnecting agent\n");
472 udscs_destroy_connection(connp);
473 return;
476 agent_data->width = res->width;
477 agent_data->height = res->height;
478 check_xorg_resolution();
479 break;
481 case VDAGENTD_CLIPBOARD_GRAB:
482 case VDAGENTD_CLIPBOARD_REQUEST:
483 case VDAGENTD_CLIPBOARD_DATA:
484 case VDAGENTD_CLIPBOARD_RELEASE:
485 do_agent_clipboard(*connp, header, data);
486 break;
487 default:
488 fprintf(logfile, "unknown message from vdagent: %u, ignoring\n",
489 header->type);
493 /* main */
495 static void usage(FILE *fp)
497 fprintf(fp,
498 "vdagentd\n"
499 "options:\n"
500 " -h print this text\n"
501 " -d log debug messages (use twice for extra info)\n"
502 " -s <port> set virtio serial port [%s]\n"
503 " -u <dev> set uinput device [%s]\n"
504 " -x don't daemonize (and log to logfile)\n",
505 portdev, uinput_device);
508 void daemonize(void)
510 /* detach from terminal */
511 switch (fork()) {
512 case 0:
513 close(0); close(1); close(2);
514 setsid();
515 open("/dev/null",O_RDWR); dup(0); dup(0);
516 break;
517 case -1:
518 fprintf(logfile, "fork: %s\n", strerror(errno));
519 retval = 1;
520 default:
521 quit = 1;
525 void main_loop(void)
527 fd_set readfds, writefds;
528 int n, nfds, ck_fd = 0;
530 /* FIXME catch sigquit and set a flag to quit */
531 while (!quit) {
532 FD_ZERO(&readfds);
533 FD_ZERO(&writefds);
535 nfds = udscs_server_fill_fds(server, &readfds, &writefds);
536 n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds);
537 if (n >= nfds)
538 nfds = n + 1;
540 ck_fd = console_kit_get_fd(console_kit);
541 FD_SET(ck_fd, &readfds);
542 if (ck_fd >= nfds)
543 nfds = ck_fd + 1;
545 n = select(nfds, &readfds, &writefds, NULL, NULL);
546 if (n == -1) {
547 if (errno == EINTR)
548 continue;
549 fprintf(logfile, "Fatal error select: %s\n", strerror(errno));
550 retval = 1;
551 break;
554 udscs_server_handle_fds(server, &readfds, &writefds);
556 if (virtio_port) {
557 vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds);
558 if (!virtio_port) {
559 fprintf(logfile,
560 "AIIEEE lost spice client connection, reconnecting\n");
561 virtio_port = vdagent_virtio_port_create(portdev,
562 virtio_port_read_complete,
563 NULL, logfile);
565 if (!virtio_port) {
566 fprintf(logfile,
567 "Fatal error opening vdagent virtio channel\n");
568 retval = 1;
569 break;
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");
578 retval = 1;
579 break;
582 fflush(logfile);
586 int main(int argc, char *argv[])
588 int c;
589 int do_daemonize = 1;
591 for (;;) {
592 if (-1 == (c = getopt(argc, argv, "-dhxs:u:")))
593 break;
594 switch (c) {
595 case 'd':
596 debug++;
597 break;
598 case 's':
599 portdev = optarg;
600 break;
601 case 'u':
602 uinput_device = optarg;
603 break;
604 case 'x':
605 do_daemonize = 0;
606 break;
607 case 'h':
608 usage(stdout);
609 return 0;
610 default:
611 usage(stderr);
612 return 1;
616 if (do_daemonize) {
617 logfile = fopen(logfilename, "a");
618 if (!logfile) {
619 fprintf(stderr, "Error opening %s: %s\n", logfilename,
620 strerror(errno));
621 logfile = stderr;
623 } else
624 logfile = stderr;
626 /* Setup communication with vdagent process(es) */
627 server = udscs_create_server(VDAGENTD_SOCKET, agent_connect,
628 agent_read_complete, agent_disconnect,
629 vdagentd_messages, VDAGENTD_NO_MESSAGES,
630 debug? logfile:NULL, logfile);
631 if (!server) {
632 fprintf(logfile, "Fatal could not create server socket %s\n",
633 VDAGENTD_SOCKET);
634 return 1;
636 if (chmod(VDAGENTD_SOCKET, 0666)) {
637 fprintf(logfile, "Fatal could not change permissions on %s: %s\n",
638 VDAGENTD_SOCKET, strerror(errno));
639 udscs_destroy_server(server);
640 return 1;
643 console_kit = console_kit_create(logfile);
644 if (!console_kit) {
645 fprintf(logfile, "Fatal could not connect to console kit\n");
646 udscs_destroy_server(server);
647 return 1;
649 active_session = console_kit_get_active_session(console_kit);
650 if (!active_session) {
651 fprintf(logfile, "Fatal could not get active session\n");
652 console_kit_destroy(console_kit);
653 udscs_destroy_server(server);
654 return 1;
657 if (do_daemonize)
658 daemonize();
660 main_loop();
662 if (agent_owns_clipboard && virtio_port)
663 vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
664 VD_AGENT_CLIPBOARD_RELEASE, 0, NULL, 0);
666 vdagentd_uinput_destroy(&uinput);
667 vdagent_virtio_port_flush(&virtio_port);
668 vdagent_virtio_port_destroy(&virtio_port);
669 console_kit_destroy(console_kit);
670 udscs_destroy_server(server);
671 fprintf(logfile, "vdagentd quiting, returning status %d\n", retval);
672 if (logfile != stderr)
673 fclose(logfile);
675 return retval;