uploaded spice-vdagent
[platform/adaptation/emulator/spice-vdagent.git] / src / vdagent.c
1 /*  vdagent.c xorg-client to vdagentd (daemon).
2
3     Copyright 2010-2013 Red Hat, Inc.
4
5     Red Hat Authors:
6     Hans de Goede <hdegoede@redhat.com>
7
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.
12
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.
17
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/>.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
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
39 #include "udscs.h"
40 #include "vdagentd-proto.h"
41 #include "vdagentd-proto-strings.h"
42 #include "vdagent-x11.h"
43 #include "vdagent-file-xfers.h"
44
45 static const char *portdev = "/dev/virtio-ports/com.redhat.spice.0";
46 static const char *vdagentd_socket = VDAGENTD_SOCKET;
47 static int debug = 0;
48 static const char *fx_dir = NULL;
49 static int fx_open_dir = -1;
50 static struct vdagent_x11 *x11 = NULL;
51 static struct vdagent_file_xfers *vdagent_file_xfers = NULL;
52 static struct udscs_connection *client = NULL;
53 static int quit = 0;
54 static int version_mismatch = 0;
55
56 void daemon_read_complete(struct udscs_connection **connp,
57     struct udscs_message_header *header, uint8_t *data)
58 {
59     switch (header->type) {
60     case VDAGENTD_MONITORS_CONFIG:
61         vdagent_x11_set_monitor_config(x11, (VDAgentMonitorsConfig *)data, 0);
62         free(data);
63         break;
64     case VDAGENTD_CLIPBOARD_REQUEST:
65         vdagent_x11_clipboard_request(x11, header->arg1, header->arg2);
66         free(data);
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         free(data);
72         break;
73     case VDAGENTD_CLIPBOARD_DATA:
74         vdagent_x11_clipboard_data(x11, header->arg1, header->arg2,
75                                    data, header->size);
76         /* vdagent_x11_clipboard_data takes ownership of the data (or frees
77            it immediately) */
78         break;
79     case VDAGENTD_CLIPBOARD_RELEASE:
80         vdagent_x11_clipboard_release(x11, header->arg1);
81         free(data);
82         break;
83     case VDAGENTD_VERSION:
84         if (strcmp((char *)data, VERSION) != 0) {
85             syslog(LOG_INFO, "vdagentd version mismatch: got %s expected %s",
86                    data, VERSION);
87             udscs_destroy_connection(connp);
88             version_mismatch = 1;
89         }
90         break;
91     case VDAGENTD_FILE_XFER_START:
92         vdagent_file_xfers_start(vdagent_file_xfers,
93                                  (VDAgentFileXferStartMessage *)data);
94         free(data);
95         break;
96     case VDAGENTD_FILE_XFER_STATUS:
97         vdagent_file_xfers_status(vdagent_file_xfers,
98                                   (VDAgentFileXferStatusMessage *)data);
99         free(data);
100         break;
101     case VDAGENTD_FILE_XFER_DATA:
102         vdagent_file_xfers_data(vdagent_file_xfers,
103                                 (VDAgentFileXferDataMessage *)data);
104         free(data);
105         break;
106     case VDAGENTD_CLIENT_DISCONNECTED:
107         vdagent_x11_client_disconnected(x11);
108         vdagent_file_xfers_destroy(vdagent_file_xfers);
109         vdagent_file_xfers = vdagent_file_xfers_create(client, fx_dir,
110                                                        fx_open_dir, debug);
111         break;
112     default:
113         syslog(LOG_ERR, "Unknown message from vdagentd type: %d, ignoring",
114                header->type);
115         free(data);
116     }
117 }
118
119 int client_setup(int reconnect)
120 {
121     while (!quit) {
122         client = udscs_connect(vdagentd_socket, daemon_read_complete, NULL,
123                                vdagentd_messages, VDAGENTD_NO_MESSAGES,
124                                debug);
125         if (client || !reconnect || quit) {
126             break;
127         }
128         sleep(1);
129     }
130     return client == NULL;
131 }
132
133 static void usage(FILE *fp)
134 {
135     fprintf(fp,
136       "Usage: spice-vdagent [OPTIONS]\n\n"
137       "Spice guest agent X11 session agent, version %s.\n\n"
138       "Options:\n"
139       "  -h                                print this text\n"
140       "  -d                                log debug messages\n"
141       "  -s <port>                         set virtio serial port\n"
142       "  -S <filename>                     set udcs socket\n"
143       "  -x                                don't daemonize\n"
144       "  -f <dir|xdg-desktop|xdg-download> file xfer save dir\n"
145       "  -o <0|1>                          open dir on file xfer completion\n",
146       VERSION);
147 }
148
149 static void quit_handler(int sig)
150 {
151     quit = 1;
152 }
153
154 void daemonize(void)
155 {
156     int x, retval = 0;
157
158     /* detach from terminal */
159     switch (fork()) {
160     case 0:
161         close(0); close(1); close(2);
162         setsid();
163         x = open("/dev/null", O_RDWR); x = dup(x); x = dup(x);
164         break;
165     case -1:
166         syslog(LOG_ERR, "fork: %s", strerror(errno));
167         retval = 1;
168     default:
169         exit(retval);
170     }
171 }
172
173 static int file_test(const char *path)
174 {
175     struct stat buffer;
176
177     return stat(path, &buffer);
178 }
179
180 int main(int argc, char *argv[])
181 {
182     fd_set readfds, writefds;
183     int c, n, nfds, x11_fd;
184     int do_daemonize = 1;
185     int x11_sync = 0;
186     struct sigaction act;
187
188     for (;;) {
189         if (-1 == (c = getopt(argc, argv, "-dxhys:f:o:S:")))
190             break;
191         switch (c) {
192         case 'd':
193             debug++;
194             break;
195         case 's':
196             portdev = optarg;
197             break;
198         case 'x':
199             do_daemonize = 0;
200             break;
201         case 'y':
202             x11_sync = 1;
203             break;
204         case 'h':
205             usage(stdout);
206             return 0;
207         case 'f':
208             fx_dir = optarg;
209             break;
210         case 'o':
211             fx_open_dir = atoi(optarg);
212             break;
213         case 'S':
214             vdagentd_socket = optarg;
215             break;
216         default:
217             fputs("\n", stderr);
218             usage(stderr);
219             return 1;
220         }
221     }
222
223     memset(&act, 0, sizeof(act));
224     act.sa_flags = SA_RESTART;
225     act.sa_handler = quit_handler;
226     sigaction(SIGINT, &act, NULL);
227     sigaction(SIGHUP, &act, NULL);
228     sigaction(SIGTERM, &act, NULL);
229     sigaction(SIGQUIT, &act, NULL);
230
231     openlog("spice-vdagent", do_daemonize ? LOG_PID : (LOG_PID | LOG_PERROR),
232             LOG_USER);
233
234     if (file_test(portdev) != 0) {
235         return 0;
236     }
237
238     if (do_daemonize)
239         daemonize();
240
241 reconnect:
242     if (version_mismatch) {
243         syslog(LOG_INFO, "Version mismatch, restarting");
244         sleep(1);
245         execvp(argv[0], argv);
246     }
247
248     if (client_setup(do_daemonize)) {
249         return 1;
250     }
251
252     x11 = vdagent_x11_create(client, debug, x11_sync);
253     if (!x11) {
254         udscs_destroy_connection(&client);
255         return 1;
256     }
257
258     if (!fx_dir) {
259         if (vdagent_x11_has_icons_on_desktop(x11))
260             fx_dir = "xdg-desktop";
261         else
262             fx_dir = "xdg-download";
263     }
264     if (fx_open_dir == -1)
265         fx_open_dir = !vdagent_x11_has_icons_on_desktop(x11);
266     if (!strcmp(fx_dir, "xdg-desktop"))
267         fx_dir = g_get_user_special_dir(G_USER_DIRECTORY_DESKTOP);
268     else if (!strcmp(fx_dir, "xdg-download"))
269         fx_dir = g_get_user_special_dir(G_USER_DIRECTORY_DOWNLOAD);
270     if (!fx_dir) {
271         syslog(LOG_WARNING,
272                "warning could not get file xfer save dir, using cwd");
273         fx_dir = ".";
274     }
275     vdagent_file_xfers = vdagent_file_xfers_create(client, fx_dir,
276                                                    fx_open_dir, debug);
277
278     while (client && !quit) {
279         FD_ZERO(&readfds);
280         FD_ZERO(&writefds);
281
282         nfds = udscs_client_fill_fds(client, &readfds, &writefds);
283         x11_fd = vdagent_x11_get_fd(x11);
284         FD_SET(x11_fd, &readfds);
285         if (x11_fd >= nfds)
286             nfds = x11_fd + 1;
287
288         n = select(nfds, &readfds, &writefds, NULL, NULL);
289         if (n == -1) {
290             if (errno == EINTR)
291                 continue;
292             syslog(LOG_ERR, "Fatal error select: %s", strerror(errno));
293             break;
294         }
295
296         if (FD_ISSET(x11_fd, &readfds))
297             vdagent_x11_do_read(x11);
298         udscs_client_handle_fds(&client, &readfds, &writefds);
299     }
300
301     vdagent_file_xfers_destroy(vdagent_file_xfers);
302     vdagent_x11_destroy(x11, client == NULL);
303     udscs_destroy_connection(&client);
304     if (!quit && do_daemonize)
305         goto reconnect;
306
307     return 0;
308 }