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
,
76 case VDAGENTD_CLIPBOARD_RELEASE
:
77 vdagent_x11_clipboard_release(x11
, header
->arg1
);
79 case VDAGENTD_VERSION
:
80 if (strcmp((char *)data
, VERSION
) != 0) {
81 syslog(LOG_INFO
, "vdagentd version mismatch: got %s expected %s",
83 udscs_destroy_connection(connp
);
87 case VDAGENTD_FILE_XFER_START
:
88 if (vdagent_file_xfers
!= NULL
) {
89 vdagent_file_xfers_start(vdagent_file_xfers
,
90 (VDAgentFileXferStartMessage
*)data
);
92 vdagent_file_xfers_error(*connp
,
93 ((VDAgentFileXferStartMessage
*)data
)->id
);
96 case VDAGENTD_FILE_XFER_STATUS
:
97 if (vdagent_file_xfers
!= NULL
) {
98 vdagent_file_xfers_status(vdagent_file_xfers
,
99 (VDAgentFileXferStatusMessage
*)data
);
101 vdagent_file_xfers_error(*connp
,
102 ((VDAgentFileXferStatusMessage
*)data
)->id
);
105 case VDAGENTD_FILE_XFER_DISABLE
:
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
;
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
);
119 vdagent_audio_record_sync(avs
->mute
, avs
->nchannels
, avs
->volume
);
123 case VDAGENTD_FILE_XFER_DATA
:
124 if (vdagent_file_xfers
!= NULL
) {
125 vdagent_file_xfers_data(vdagent_file_xfers
,
126 (VDAgentFileXferDataMessage
*)data
);
128 vdagent_file_xfers_error(*connp
,
129 ((VDAgentFileXferDataMessage
*)data
)->id
);
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
,
141 syslog(LOG_ERR
, "Unknown message from vdagentd type: %d, ignoring",
146 static int client_setup(int reconnect
)
149 client
= udscs_connect(vdagentd_socket
, daemon_read_complete
, NULL
,
150 vdagentd_messages
, VDAGENTD_NO_MESSAGES
,
152 if (client
|| !reconnect
|| quit
) {
157 return client
== NULL
;
160 static void usage(FILE *fp
)
163 "Usage: spice-vdagent [OPTIONS]\n\n"
164 "Spice guest agent X11 session agent, version %s.\n\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",
176 static void quit_handler(int sig
)
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
)
193 if (poll(&p
, 1, 10000) > 0)
194 if (read(s
, buf
, sizeof(buf
)) > 0)
200 static int daemonize(void)
205 if (socketpair(PF_LOCAL
, SOCK_STREAM
, 0, fd
)) {
206 syslog(LOG_ERR
, "socketpair : %s", strerror(errno
));
210 /* detach from terminal */
213 close(0); close(1); close(2);
215 x
= open("/dev/null", O_RDWR
); x
= dup(x
); x
= dup(x
);
219 syslog(LOG_ERR
, "fork: %s", strerror(errno
));
223 wait_and_exit(fd
[0]);
229 static int file_test(const char *path
)
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;
243 struct sigaction act
;
246 if (-1 == (c
= getopt(argc
, argv
, "-dxhys:f:o:S:")))
268 fx_open_dir
= atoi(optarg
);
271 vdagentd_socket
= optarg
;
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
),
291 if (file_test(portdev
) != 0) {
292 syslog(LOG_ERR
, "Cannot access vdagent virtio channel %s", portdev
);
297 parent_socket
= daemonize();
300 if (version_mismatch
) {
301 syslog(LOG_INFO
, "Version mismatch, restarting");
303 execvp(argv
[0], argv
);
306 if (client_setup(do_daemonize
)) {
310 x11
= vdagent_x11_create(client
, debug
, x11_sync
);
312 udscs_destroy_connection(&client
);
317 if (vdagent_x11_has_icons_on_desktop(x11
))
318 fx_dir
= "xdg-desktop";
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
);
329 vdagent_file_xfers
= vdagent_file_xfers_create(client
, fx_dir
,
333 "warning could not get file xfer save dir, file transfers will be disabled");
334 vdagent_file_xfers
= NULL
;
338 if (write(parent_socket
, "OK", 2) != 2)
339 syslog(LOG_WARNING
, "Parent already gone.");
340 close(parent_socket
);
344 while (client
&& !quit
) {
348 nfds
= udscs_client_fill_fds(client
, &readfds
, &writefds
);
349 x11_fd
= vdagent_x11_get_fd(x11
);
350 FD_SET(x11_fd
, &readfds
);
354 n
= select(nfds
, &readfds
, &writefds
, NULL
, NULL
);
358 syslog(LOG_ERR
, "Fatal error select: %s", strerror(errno
));
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
)