x11: More udscs_read_callback memory ownership issues
[vd_agent/hramrach.git] / src / vdagent / vdagent.c
blob3d195b11d8bb334e933259352e56bcc2c96ea415
1 /* vdagent.c xorg-client to vdagentd (daemon).
3 Copyright 2010-2013 Red Hat, Inc.
5 Red Hat Authors:
6 Hans de Goede <hdegoede@redhat.com>
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <syslog.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <signal.h>
34 #include <sys/select.h>
35 #include <sys/stat.h>
36 #include <spice/vd_agent.h>
37 #include <glib.h>
38 #include <poll.h>
40 #include "udscs.h"
41 #include "vdagentd-proto.h"
42 #include "vdagentd-proto-strings.h"
43 #include "audio.h"
44 #include "x11.h"
45 #include "file-xfers.h"
47 static const char *portdev = "/dev/virtio-ports/com.redhat.spice.0";
48 static const char *vdagentd_socket = VDAGENTD_SOCKET;
49 static int debug = 0;
50 static const char *fx_dir = NULL;
51 static int fx_open_dir = -1;
52 static struct vdagent_x11 *x11 = NULL;
53 static struct vdagent_file_xfers *vdagent_file_xfers = NULL;
54 static struct udscs_connection *client = NULL;
55 static int quit = 0;
56 static int version_mismatch = 0;
58 static void daemon_read_complete(struct udscs_connection **connp,
59 struct udscs_message_header *header, uint8_t *data)
61 switch (header->type) {
62 case VDAGENTD_MONITORS_CONFIG:
63 vdagent_x11_set_monitor_config(x11, (VDAgentMonitorsConfig *)data, 0);
64 break;
65 case VDAGENTD_CLIPBOARD_REQUEST:
66 vdagent_x11_clipboard_request(x11, header->arg1, header->arg2);
67 break;
68 case VDAGENTD_CLIPBOARD_GRAB:
69 vdagent_x11_clipboard_grab(x11, header->arg1, (uint32_t *)data,
70 header->size / sizeof(uint32_t));
71 break;
72 case VDAGENTD_CLIPBOARD_DATA:
73 vdagent_x11_clipboard_data(x11, header->arg1, header->arg2,
74 data, header->size);
75 break;
76 case VDAGENTD_CLIPBOARD_RELEASE:
77 vdagent_x11_clipboard_release(x11, header->arg1);
78 break;
79 case VDAGENTD_VERSION:
80 if (strcmp((char *)data, VERSION) != 0) {
81 syslog(LOG_INFO, "vdagentd version mismatch: got %s expected %s",
82 data, VERSION);
83 udscs_destroy_connection(connp);
84 version_mismatch = 1;
86 break;
87 case VDAGENTD_FILE_XFER_START:
88 if (vdagent_file_xfers != NULL) {
89 vdagent_file_xfers_start(vdagent_file_xfers,
90 (VDAgentFileXferStartMessage *)data);
91 } else {
92 vdagent_file_xfers_error(*connp,
93 ((VDAgentFileXferStartMessage *)data)->id);
95 break;
96 case VDAGENTD_FILE_XFER_STATUS:
97 if (vdagent_file_xfers != NULL) {
98 vdagent_file_xfers_status(vdagent_file_xfers,
99 (VDAgentFileXferStatusMessage *)data);
100 } else {
101 vdagent_file_xfers_error(*connp,
102 ((VDAgentFileXferStatusMessage *)data)->id);
104 break;
105 case VDAGENTD_FILE_XFER_DISABLE:
106 if (debug)
107 syslog(LOG_DEBUG, "Disabling file-xfers");
109 if (vdagent_file_xfers != NULL) {
110 vdagent_file_xfers_destroy(vdagent_file_xfers);
111 vdagent_file_xfers = NULL;
113 break;
114 case VDAGENTD_AUDIO_VOLUME_SYNC: {
115 VDAgentAudioVolumeSync *avs = (VDAgentAudioVolumeSync *)data;
116 if (avs->is_playback) {
117 vdagent_audio_playback_sync(avs->mute, avs->nchannels, avs->volume);
118 } else {
119 vdagent_audio_record_sync(avs->mute, avs->nchannels, avs->volume);
121 break;
123 case VDAGENTD_FILE_XFER_DATA:
124 if (vdagent_file_xfers != NULL) {
125 vdagent_file_xfers_data(vdagent_file_xfers,
126 (VDAgentFileXferDataMessage *)data);
127 } else {
128 vdagent_file_xfers_error(*connp,
129 ((VDAgentFileXferDataMessage *)data)->id);
131 break;
132 case VDAGENTD_CLIENT_DISCONNECTED:
133 vdagent_x11_client_disconnected(x11);
134 if (vdagent_file_xfers != NULL) {
135 vdagent_file_xfers_destroy(vdagent_file_xfers);
136 vdagent_file_xfers = vdagent_file_xfers_create(client, fx_dir,
137 fx_open_dir, debug);
139 break;
140 default:
141 syslog(LOG_ERR, "Unknown message from vdagentd type: %d, ignoring",
142 header->type);
146 static int client_setup(int reconnect)
148 while (!quit) {
149 client = udscs_connect(vdagentd_socket, daemon_read_complete, NULL,
150 vdagentd_messages, VDAGENTD_NO_MESSAGES,
151 debug);
152 if (client || !reconnect || quit) {
153 break;
155 sleep(1);
157 return client == NULL;
160 static void usage(FILE *fp)
162 fprintf(fp,
163 "Usage: spice-vdagent [OPTIONS]\n\n"
164 "Spice guest agent X11 session agent, version %s.\n\n"
165 "Options:\n"
166 " -h print this text\n"
167 " -d log debug messages\n"
168 " -s <port> set virtio serial port\n"
169 " -S <filename> set udcs socket\n"
170 " -x don't daemonize\n"
171 " -f <dir|xdg-desktop|xdg-download> file xfer save dir\n"
172 " -o <0|1> open dir on file xfer completion\n",
173 VERSION);
176 static void quit_handler(int sig)
178 quit = 1;
181 /* When we daemonize, it is useful to have the main process
182 wait to make sure the X connection worked. We wait up
183 to 10 seconds to get an 'all clear' from the child
184 before we exit. If we don't, we're able to exit with a
185 status that indicates an error occured */
186 static void wait_and_exit(int s)
188 char buf[4];
189 struct pollfd p;
190 p.fd = s;
191 p.events = POLLIN;
193 if (poll(&p, 1, 10000) > 0)
194 if (read(s, buf, sizeof(buf)) > 0)
195 exit(0);
197 exit(1);
200 static int daemonize(void)
202 int x;
203 int fd[2];
205 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fd)) {
206 syslog(LOG_ERR, "socketpair : %s", strerror(errno));
207 exit(1);
210 /* detach from terminal */
211 switch (fork()) {
212 case 0:
213 close(0); close(1); close(2);
214 setsid();
215 x = open("/dev/null", O_RDWR); x = dup(x); x = dup(x);
216 close(fd[0]);
217 return fd[1];
218 case -1:
219 syslog(LOG_ERR, "fork: %s", strerror(errno));
220 exit(1);
221 default:
222 close(fd[1]);
223 wait_and_exit(fd[0]);
226 return 0;
229 static int file_test(const char *path)
231 struct stat buffer;
233 return stat(path, &buffer);
236 int main(int argc, char *argv[])
238 fd_set readfds, writefds;
239 int c, n, nfds, x11_fd;
240 int do_daemonize = 1;
241 int parent_socket = 0;
242 int x11_sync = 0;
243 struct sigaction act;
245 for (;;) {
246 if (-1 == (c = getopt(argc, argv, "-dxhys:f:o:S:")))
247 break;
248 switch (c) {
249 case 'd':
250 debug++;
251 break;
252 case 's':
253 portdev = optarg;
254 break;
255 case 'x':
256 do_daemonize = 0;
257 break;
258 case 'y':
259 x11_sync = 1;
260 break;
261 case 'h':
262 usage(stdout);
263 return 0;
264 case 'f':
265 fx_dir = optarg;
266 break;
267 case 'o':
268 fx_open_dir = atoi(optarg);
269 break;
270 case 'S':
271 vdagentd_socket = optarg;
272 break;
273 default:
274 fputs("\n", stderr);
275 usage(stderr);
276 return 1;
280 memset(&act, 0, sizeof(act));
281 act.sa_flags = SA_RESTART;
282 act.sa_handler = quit_handler;
283 sigaction(SIGINT, &act, NULL);
284 sigaction(SIGHUP, &act, NULL);
285 sigaction(SIGTERM, &act, NULL);
286 sigaction(SIGQUIT, &act, NULL);
288 openlog("spice-vdagent", do_daemonize ? LOG_PID : (LOG_PID | LOG_PERROR),
289 LOG_USER);
291 if (file_test(portdev) != 0) {
292 syslog(LOG_ERR, "Cannot access vdagent virtio channel %s", portdev);
293 return 1;
296 if (do_daemonize)
297 parent_socket = daemonize();
299 reconnect:
300 if (version_mismatch) {
301 syslog(LOG_INFO, "Version mismatch, restarting");
302 sleep(1);
303 execvp(argv[0], argv);
306 if (client_setup(do_daemonize)) {
307 return 1;
310 x11 = vdagent_x11_create(client, debug, x11_sync);
311 if (!x11) {
312 udscs_destroy_connection(&client);
313 return 1;
316 if (!fx_dir) {
317 if (vdagent_x11_has_icons_on_desktop(x11))
318 fx_dir = "xdg-desktop";
319 else
320 fx_dir = "xdg-download";
322 if (fx_open_dir == -1)
323 fx_open_dir = !vdagent_x11_has_icons_on_desktop(x11);
324 if (!strcmp(fx_dir, "xdg-desktop"))
325 fx_dir = g_get_user_special_dir(G_USER_DIRECTORY_DESKTOP);
326 else if (!strcmp(fx_dir, "xdg-download"))
327 fx_dir = g_get_user_special_dir(G_USER_DIRECTORY_DOWNLOAD);
328 if (fx_dir) {
329 vdagent_file_xfers = vdagent_file_xfers_create(client, fx_dir,
330 fx_open_dir, debug);
331 } else {
332 syslog(LOG_WARNING,
333 "warning could not get file xfer save dir, file transfers will be disabled");
334 vdagent_file_xfers = NULL;
337 if (parent_socket) {
338 if (write(parent_socket, "OK", 2) != 2)
339 syslog(LOG_WARNING, "Parent already gone.");
340 close(parent_socket);
341 parent_socket = 0;
344 while (client && !quit) {
345 FD_ZERO(&readfds);
346 FD_ZERO(&writefds);
348 nfds = udscs_client_fill_fds(client, &readfds, &writefds);
349 x11_fd = vdagent_x11_get_fd(x11);
350 FD_SET(x11_fd, &readfds);
351 if (x11_fd >= nfds)
352 nfds = x11_fd + 1;
354 n = select(nfds, &readfds, &writefds, NULL, NULL);
355 if (n == -1) {
356 if (errno == EINTR)
357 continue;
358 syslog(LOG_ERR, "Fatal error select: %s", strerror(errno));
359 break;
362 if (FD_ISSET(x11_fd, &readfds))
363 vdagent_x11_do_read(x11);
364 udscs_client_handle_fds(&client, &readfds, &writefds);
367 if (vdagent_file_xfers != NULL) {
368 vdagent_file_xfers_destroy(vdagent_file_xfers);
370 vdagent_x11_destroy(x11, client == NULL);
371 udscs_destroy_connection(&client);
372 if (!quit && do_daemonize)
373 goto reconnect;
375 return 0;