1 /* vdagent.c xorg-client to vdagentd (daemon).
3 Copyright 2010-2013 Red Hat, Inc.
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/>.
34 #include <sys/select.h>
36 #include <spice/vd_agent.h>
41 #include "vdagentd-proto.h"
42 #include "vdagentd-proto-strings.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
;
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
;
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);
65 case VDAGENTD_CLIPBOARD_REQUEST
:
66 vdagent_x11_clipboard_request(x11
, header
->arg1
, header
->arg2
);
68 case VDAGENTD_CLIPBOARD_GRAB
:
69 vdagent_x11_clipboard_grab(x11
, header
->arg1
, (uint32_t *)data
,
70 header
->size
/ sizeof(uint32_t));
72 case VDAGENTD_CLIPBOARD_DATA
:
73 vdagent_x11_clipboard_data(x11
, header
->arg1
, header
->arg2
,
75 /* vdagent_x11_clipboard_data takes ownership of the data (or frees
78 case VDAGENTD_CLIPBOARD_RELEASE
:
79 vdagent_x11_clipboard_release(x11
, header
->arg1
);
81 case VDAGENTD_VERSION
:
82 if (strcmp((char *)data
, VERSION
) != 0) {
83 syslog(LOG_INFO
, "vdagentd version mismatch: got %s expected %s",
85 udscs_destroy_connection(connp
);
89 case VDAGENTD_FILE_XFER_START
:
90 if (vdagent_file_xfers
!= NULL
) {
91 vdagent_file_xfers_start(vdagent_file_xfers
,
92 (VDAgentFileXferStartMessage
*)data
);
94 vdagent_file_xfers_error(*connp
,
95 ((VDAgentFileXferStartMessage
*)data
)->id
);
98 case VDAGENTD_FILE_XFER_STATUS
:
99 if (vdagent_file_xfers
!= NULL
) {
100 vdagent_file_xfers_status(vdagent_file_xfers
,
101 (VDAgentFileXferStatusMessage
*)data
);
103 vdagent_file_xfers_error(*connp
,
104 ((VDAgentFileXferStatusMessage
*)data
)->id
);
107 case VDAGENTD_FILE_XFER_DISABLE
:
109 syslog(LOG_DEBUG
, "Disabling file-xfers");
111 if (vdagent_file_xfers
!= NULL
) {
112 vdagent_file_xfers_destroy(vdagent_file_xfers
);
113 vdagent_file_xfers
= NULL
;
116 case VDAGENTD_AUDIO_VOLUME_SYNC
: {
117 VDAgentAudioVolumeSync
*avs
= (VDAgentAudioVolumeSync
*)data
;
118 if (avs
->is_playback
) {
119 vdagent_audio_playback_sync(avs
->mute
, avs
->nchannels
, avs
->volume
);
121 vdagent_audio_record_sync(avs
->mute
, avs
->nchannels
, avs
->volume
);
125 case VDAGENTD_FILE_XFER_DATA
:
126 if (vdagent_file_xfers
!= NULL
) {
127 vdagent_file_xfers_data(vdagent_file_xfers
,
128 (VDAgentFileXferDataMessage
*)data
);
130 vdagent_file_xfers_error(*connp
,
131 ((VDAgentFileXferDataMessage
*)data
)->id
);
134 case VDAGENTD_CLIENT_DISCONNECTED
:
135 vdagent_x11_client_disconnected(x11
);
136 if (vdagent_file_xfers
!= NULL
) {
137 vdagent_file_xfers_destroy(vdagent_file_xfers
);
138 vdagent_file_xfers
= vdagent_file_xfers_create(client
, fx_dir
,
143 syslog(LOG_ERR
, "Unknown message from vdagentd type: %d, ignoring",
148 static int client_setup(int reconnect
)
151 client
= udscs_connect(vdagentd_socket
, daemon_read_complete
, NULL
,
152 vdagentd_messages
, VDAGENTD_NO_MESSAGES
,
154 if (client
|| !reconnect
|| quit
) {
159 return client
== NULL
;
162 static void usage(FILE *fp
)
165 "Usage: spice-vdagent [OPTIONS]\n\n"
166 "Spice guest agent X11 session agent, version %s.\n\n"
168 " -h print this text\n"
169 " -d log debug messages\n"
170 " -s <port> set virtio serial port\n"
171 " -S <filename> set udcs socket\n"
172 " -x don't daemonize\n"
173 " -f <dir|xdg-desktop|xdg-download> file xfer save dir\n"
174 " -o <0|1> open dir on file xfer completion\n",
178 static void quit_handler(int sig
)
183 /* When we daemonize, it is useful to have the main process
184 wait to make sure the X connection worked. We wait up
185 to 10 seconds to get an 'all clear' from the child
186 before we exit. If we don't, we're able to exit with a
187 status that indicates an error occured */
188 static void wait_and_exit(int s
)
195 if (poll(&p
, 1, 10000) > 0)
196 if (read(s
, buf
, sizeof(buf
)) > 0)
202 static int daemonize(void)
207 if (socketpair(PF_LOCAL
, SOCK_STREAM
, 0, fd
)) {
208 syslog(LOG_ERR
, "socketpair : %s", strerror(errno
));
212 /* detach from terminal */
215 close(0); close(1); close(2);
217 x
= open("/dev/null", O_RDWR
); x
= dup(x
); x
= dup(x
);
221 syslog(LOG_ERR
, "fork: %s", strerror(errno
));
225 wait_and_exit(fd
[0]);
231 static int file_test(const char *path
)
235 return stat(path
, &buffer
);
238 int main(int argc
, char *argv
[])
240 fd_set readfds
, writefds
;
241 int c
, n
, nfds
, x11_fd
;
242 int do_daemonize
= 1;
243 int parent_socket
= 0;
245 struct sigaction act
;
248 if (-1 == (c
= getopt(argc
, argv
, "-dxhys:f:o:S:")))
270 fx_open_dir
= atoi(optarg
);
273 vdagentd_socket
= optarg
;
282 memset(&act
, 0, sizeof(act
));
283 act
.sa_flags
= SA_RESTART
;
284 act
.sa_handler
= quit_handler
;
285 sigaction(SIGINT
, &act
, NULL
);
286 sigaction(SIGHUP
, &act
, NULL
);
287 sigaction(SIGTERM
, &act
, NULL
);
288 sigaction(SIGQUIT
, &act
, NULL
);
290 openlog("spice-vdagent", do_daemonize
? LOG_PID
: (LOG_PID
| LOG_PERROR
),
293 if (file_test(portdev
) != 0) {
294 syslog(LOG_ERR
, "Cannot access vdagent virtio channel %s", portdev
);
299 parent_socket
= daemonize();
302 if (version_mismatch
) {
303 syslog(LOG_INFO
, "Version mismatch, restarting");
305 execvp(argv
[0], argv
);
308 if (client_setup(do_daemonize
)) {
312 x11
= vdagent_x11_create(client
, debug
, x11_sync
);
314 udscs_destroy_connection(&client
);
319 if (vdagent_x11_has_icons_on_desktop(x11
))
320 fx_dir
= "xdg-desktop";
322 fx_dir
= "xdg-download";
324 if (fx_open_dir
== -1)
325 fx_open_dir
= !vdagent_x11_has_icons_on_desktop(x11
);
326 if (!strcmp(fx_dir
, "xdg-desktop"))
327 fx_dir
= g_get_user_special_dir(G_USER_DIRECTORY_DESKTOP
);
328 else if (!strcmp(fx_dir
, "xdg-download"))
329 fx_dir
= g_get_user_special_dir(G_USER_DIRECTORY_DOWNLOAD
);
331 vdagent_file_xfers
= vdagent_file_xfers_create(client
, fx_dir
,
335 "warning could not get file xfer save dir, file transfers will be disabled");
336 vdagent_file_xfers
= NULL
;
340 if (write(parent_socket
, "OK", 2) != 2)
341 syslog(LOG_WARNING
, "Parent already gone.");
342 close(parent_socket
);
346 while (client
&& !quit
) {
350 nfds
= udscs_client_fill_fds(client
, &readfds
, &writefds
);
351 x11_fd
= vdagent_x11_get_fd(x11
);
352 FD_SET(x11_fd
, &readfds
);
356 n
= select(nfds
, &readfds
, &writefds
, NULL
, NULL
);
360 syslog(LOG_ERR
, "Fatal error select: %s", strerror(errno
));
364 if (FD_ISSET(x11_fd
, &readfds
))
365 vdagent_x11_do_read(x11
);
366 udscs_client_handle_fds(&client
, &readfds
, &writefds
);
369 if (vdagent_file_xfers
!= NULL
) {
370 vdagent_file_xfers_destroy(vdagent_file_xfers
);
372 vdagent_x11_destroy(x11
, client
== NULL
);
373 udscs_destroy_connection(&client
);
374 if (!quit
&& do_daemonize
)