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"
43 #include "vdagent-audio.h"
44 #include "vdagent-x11.h"
45 #include "vdagent-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 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);
66 case VDAGENTD_CLIPBOARD_REQUEST
:
67 vdagent_x11_clipboard_request(x11
, header
->arg1
, header
->arg2
);
70 case VDAGENTD_CLIPBOARD_GRAB
:
71 vdagent_x11_clipboard_grab(x11
, header
->arg1
, (uint32_t *)data
,
72 header
->size
/ sizeof(uint32_t));
75 case VDAGENTD_CLIPBOARD_DATA
:
76 vdagent_x11_clipboard_data(x11
, header
->arg1
, header
->arg2
,
78 /* vdagent_x11_clipboard_data takes ownership of the data (or frees
81 case VDAGENTD_CLIPBOARD_RELEASE
:
82 vdagent_x11_clipboard_release(x11
, header
->arg1
);
85 case VDAGENTD_VERSION
:
86 if (strcmp((char *)data
, VERSION
) != 0) {
87 syslog(LOG_INFO
, "vdagentd version mismatch: got %s expected %s",
89 udscs_destroy_connection(connp
);
93 case VDAGENTD_FILE_XFER_START
:
94 if (vdagent_file_xfers
!= NULL
) {
95 vdagent_file_xfers_start(vdagent_file_xfers
,
96 (VDAgentFileXferStartMessage
*)data
);
98 vdagent_file_xfers_error(*connp
,
99 ((VDAgentFileXferStartMessage
*)data
)->id
);
103 case VDAGENTD_FILE_XFER_STATUS
:
104 if (vdagent_file_xfers
!= NULL
) {
105 vdagent_file_xfers_status(vdagent_file_xfers
,
106 (VDAgentFileXferStatusMessage
*)data
);
108 vdagent_file_xfers_error(*connp
,
109 ((VDAgentFileXferStatusMessage
*)data
)->id
);
113 case VDAGENTD_AUDIO_VOLUME_SYNC
: {
114 VDAgentAudioVolumeSync
*avs
= (VDAgentAudioVolumeSync
*)data
;
115 if (avs
->is_playback
) {
116 vdagent_audio_playback_sync(avs
->mute
, avs
->nchannels
, avs
->volume
);
118 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
);
133 case VDAGENTD_CLIENT_DISCONNECTED
:
134 vdagent_x11_client_disconnected(x11
);
135 if (vdagent_file_xfers
!= NULL
) {
136 vdagent_file_xfers_destroy(vdagent_file_xfers
);
137 vdagent_file_xfers
= vdagent_file_xfers_create(client
, fx_dir
,
142 syslog(LOG_ERR
, "Unknown message from vdagentd type: %d, ignoring",
148 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 void wait_and_exit(int s
)
195 if (poll(&p
, 1, 10000) > 0)
196 if (read(s
, buf
, sizeof(buf
)) > 0)
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) {
298 parent_socket
= daemonize();
301 if (version_mismatch
) {
302 syslog(LOG_INFO
, "Version mismatch, restarting");
304 execvp(argv
[0], argv
);
307 if (client_setup(do_daemonize
)) {
311 x11
= vdagent_x11_create(client
, debug
, x11_sync
);
313 udscs_destroy_connection(&client
);
318 if (vdagent_x11_has_icons_on_desktop(x11
))
319 fx_dir
= "xdg-desktop";
321 fx_dir
= "xdg-download";
323 if (fx_open_dir
== -1)
324 fx_open_dir
= !vdagent_x11_has_icons_on_desktop(x11
);
325 if (!strcmp(fx_dir
, "xdg-desktop"))
326 fx_dir
= g_get_user_special_dir(G_USER_DIRECTORY_DESKTOP
);
327 else if (!strcmp(fx_dir
, "xdg-download"))
328 fx_dir
= g_get_user_special_dir(G_USER_DIRECTORY_DOWNLOAD
);
330 vdagent_file_xfers
= vdagent_file_xfers_create(client
, fx_dir
,
334 "warning could not get file xfer save dir, file transfers will be disabled");
335 vdagent_file_xfers
= NULL
;
339 if (write(parent_socket
, "OK", 2) != 2)
340 syslog(LOG_WARNING
, "Parent already gone.");
341 close(parent_socket
);
345 while (client
&& !quit
) {
349 nfds
= udscs_client_fill_fds(client
, &readfds
, &writefds
);
350 x11_fd
= vdagent_x11_get_fd(x11
);
351 FD_SET(x11_fd
, &readfds
);
355 n
= select(nfds
, &readfds
, &writefds
, NULL
, NULL
);
359 syslog(LOG_ERR
, "Fatal error select: %s", strerror(errno
));
363 if (FD_ISSET(x11_fd
, &readfds
))
364 vdagent_x11_do_read(x11
);
365 udscs_client_handle_fds(&client
, &readfds
, &writefds
);
368 if (vdagent_file_xfers
!= NULL
) {
369 vdagent_file_xfers_destroy(vdagent_file_xfers
);
371 vdagent_x11_destroy(x11
, client
== NULL
);
372 udscs_destroy_connection(&client
);
373 if (!quit
&& do_daemonize
)