uploaded spice-vdagent
[platform/adaptation/emulator/spice-vdagent.git] / src / vdagentd.c
1 /*  vdagentd.c vdagentd (daemon) code
2
3     Copyright 2010-2013 Red Hat, Inc.
4
5     Red Hat Authors:
6     Hans de Goede <hdegoede@redhat.com>
7     Gerd Hoffmann <kraxel@redhat.com>
8
9     This program is free software: you can redistribute it and/or modify
10     it under the terms of the GNU General Public License as published by
11     the Free Software Foundation, either version 3 of the License, or   
12     (at your option) any later version.
13
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of 
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  
17     GNU General Public License for more details.
18
19     You should have received a copy of the GNU General Public License
20     along with this program.  If not, see <http://www.gnu.org/licenses/>.
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 <unistd.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include <signal.h>
33 #include <syslog.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 "vdagentd-uinput.h"
43 #include "vdagentd-xorg-conf.h"
44 #include "vdagent-virtio-port.h"
45 #include "session-info.h"
46
47 struct agent_data {
48     char *session;
49     int width;
50     int height;
51     struct vdagentd_guest_xorg_resolution *screen_info;
52     int screen_count;
53 };
54
55 /* variables */
56 static const char *pidfilename = "/opt/var/run/spice-vdagentd/spice-vdagentd.pid";
57 static const char *portdev = "/dev/virtio-ports/com.redhat.spice.0";
58 static const char *vdagentd_socket = VDAGENTD_SOCKET;
59 static const char *uinput_device = "/dev/uinput";
60 static int debug = 0;
61 static int uinput_fake = 0;
62 static struct udscs_server *server = NULL;
63 static struct vdagent_virtio_port *virtio_port = NULL;
64 static GHashTable *active_xfers = NULL;
65 static struct session_info *session_info = NULL;
66 static struct vdagentd_uinput *uinput = NULL;
67 static VDAgentMonitorsConfig *mon_config = NULL;
68 static uint32_t *capabilities = NULL;
69 static int capabilities_size = 0;
70 static const char *active_session = NULL;
71 static unsigned int session_count = 0;
72 static struct udscs_connection *active_session_conn = NULL;
73 static int agent_owns_clipboard[256] = { 0, };
74 static int quit = 0;
75 static int retval = 0;
76 static int client_connected = 0;
77
78 /* utility functions */
79 /* vdagentd <-> spice-client communication handling */
80 static void send_capabilities(struct vdagent_virtio_port *vport,
81     uint32_t request)
82 {
83     VDAgentAnnounceCapabilities *caps;
84     uint32_t size;
85
86     size = sizeof(*caps) + VD_AGENT_CAPS_BYTES;
87     caps = calloc(1, size);
88     if (!caps) {
89         syslog(LOG_ERR, "out of memory allocating capabilities array (write)");
90         return;
91     }
92
93     caps->request = request;
94     VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MOUSE_STATE);
95     VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MONITORS_CONFIG);
96     VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_REPLY);
97     VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND);
98     VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_SELECTION);
99     VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_SPARSE_MONITORS_CONFIG);
100     VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_GUEST_LINEEND_LF);
101
102     vdagent_virtio_port_write(vport, VDP_CLIENT_PORT,
103                               VD_AGENT_ANNOUNCE_CAPABILITIES, 0,
104                               (uint8_t *)caps, size);
105     free(caps);
106 }
107
108 static void do_client_disconnect(void)
109 {
110     if (client_connected) {
111         udscs_server_write_all(server, VDAGENTD_CLIENT_DISCONNECTED, 0, 0,
112                                NULL, 0);
113         client_connected = 0;
114     }
115 }
116
117 static void do_client_monitors(struct vdagent_virtio_port *vport, int port_nr,
118     VDAgentMessage *message_header, VDAgentMonitorsConfig *new_monitors)
119 {
120     VDAgentReply reply;
121     uint32_t size;
122
123     /* Store monitor config to send to agents when they connect */
124     size = sizeof(VDAgentMonitorsConfig) +
125            new_monitors->num_of_monitors * sizeof(VDAgentMonConfig);
126     if (message_header->size != size) {
127         syslog(LOG_ERR, "invalid message size for VDAgentMonitorsConfig");
128         return;
129     }
130
131     vdagentd_write_xorg_conf(new_monitors);
132
133     if (!mon_config ||
134             mon_config->num_of_monitors != new_monitors->num_of_monitors) {
135         free(mon_config);
136         mon_config = malloc(size);
137         if (!mon_config) {
138             syslog(LOG_ERR, "out of memory allocating monitors config");
139             return;
140         }
141     }
142     memcpy(mon_config, new_monitors, size);
143
144     /* Send monitor config to currently active agent */
145     if (active_session_conn)
146         udscs_write(active_session_conn, VDAGENTD_MONITORS_CONFIG, 0, 0,
147                     (uint8_t *)mon_config, size);
148
149     /* Acknowledge reception of monitors config to spice server / client */
150     reply.type  = VD_AGENT_MONITORS_CONFIG;
151     reply.error = VD_AGENT_SUCCESS;
152     vdagent_virtio_port_write(vport, port_nr, VD_AGENT_REPLY, 0,
153                               (uint8_t *)&reply, sizeof(reply));
154 }
155
156 static void do_client_capabilities(struct vdagent_virtio_port *vport,
157     VDAgentMessage *message_header,
158     VDAgentAnnounceCapabilities *caps)
159 {
160     int new_size = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(message_header->size);
161
162     if (capabilities_size != new_size) {
163         capabilities_size = new_size;
164         free(capabilities);
165         capabilities = malloc(capabilities_size * sizeof(uint32_t));
166         if (!capabilities) {
167             syslog(LOG_ERR, "oom allocating capabilities array (read)");
168             capabilities_size = 0;
169             return;
170         }
171     }
172     memcpy(capabilities, caps->caps, capabilities_size * sizeof(uint32_t));
173     if (caps->request) {
174         /* Report the previous client has disconneced. */
175         do_client_disconnect();
176         if (debug)
177             syslog(LOG_DEBUG, "New client connected");
178         client_connected = 1;
179         send_capabilities(vport, 0);
180     }
181 }
182
183 static void do_client_clipboard(struct vdagent_virtio_port *vport,
184     VDAgentMessage *message_header, uint8_t *data)
185 {
186     uint32_t msg_type = 0, data_type = 0, size = message_header->size;
187     uint8_t selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;
188
189     if (!active_session_conn) {
190         syslog(LOG_WARNING,
191                "Could not find an agent connection belonging to the "
192                "active session, ignoring client clipboard request");
193         return;
194     }
195
196     if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
197                                 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
198       selection = data[0];
199       data += 4;
200       size -= 4;
201     }
202
203     switch (message_header->type) {
204     case VD_AGENT_CLIPBOARD_GRAB:
205         msg_type = VDAGENTD_CLIPBOARD_GRAB;
206         agent_owns_clipboard[selection] = 0;
207         break;
208     case VD_AGENT_CLIPBOARD_REQUEST: {
209         VDAgentClipboardRequest *req = (VDAgentClipboardRequest *)data;
210         msg_type = VDAGENTD_CLIPBOARD_REQUEST;
211         data_type = req->type;
212         data = NULL;
213         size = 0;
214         break;
215     }
216     case VD_AGENT_CLIPBOARD: {
217         VDAgentClipboard *clipboard = (VDAgentClipboard *)data;
218         msg_type = VDAGENTD_CLIPBOARD_DATA;
219         data_type = clipboard->type;
220         size = size - sizeof(VDAgentClipboard);
221         data = clipboard->data;
222         break;
223     }
224     case VD_AGENT_CLIPBOARD_RELEASE:
225         msg_type = VDAGENTD_CLIPBOARD_RELEASE;
226         data = NULL;
227         size = 0;
228         break;
229     }
230
231     udscs_write(active_session_conn, msg_type, selection, data_type,
232                 data, size);
233 }
234
235 static void cancel_file_xfer(struct vdagent_virtio_port *vport,
236                              const char *msg, uint32_t id)
237 {
238     VDAgentFileXferStatusMessage status = {
239         .id = id,
240         .result = VD_AGENT_FILE_XFER_STATUS_CANCELLED,
241     };
242     syslog(LOG_WARNING, msg, id);
243     if (vport)
244         vdagent_virtio_port_write(vport, VDP_CLIENT_PORT,
245                                   VD_AGENT_FILE_XFER_STATUS, 0,
246                                   (uint8_t *)&status, sizeof(status));
247 }
248
249 static void do_client_file_xfer(struct vdagent_virtio_port *vport,
250                                 VDAgentMessage *message_header,
251                                 uint8_t *data)
252 {
253     uint32_t msg_type, id;
254     struct udscs_connection *conn;
255
256     switch (message_header->type) {
257     case VD_AGENT_FILE_XFER_START: {
258         VDAgentFileXferStartMessage *s = (VDAgentFileXferStartMessage *)data;
259         if (!active_session_conn) {
260             cancel_file_xfer(vport,
261                "Could not find an agent connnection belonging to the "
262                "active session, cancelling client file-xfer request %u",
263                s->id);
264             return;
265         }
266         udscs_write(active_session_conn, VDAGENTD_FILE_XFER_START, 0, 0,
267                     data, message_header->size);
268         return;
269     }
270     case VD_AGENT_FILE_XFER_STATUS: {
271         VDAgentFileXferStatusMessage *s = (VDAgentFileXferStatusMessage *)data;
272         msg_type = VDAGENTD_FILE_XFER_STATUS;
273         id = s->id;
274         break;
275     }
276     case VD_AGENT_FILE_XFER_DATA: {
277         VDAgentFileXferDataMessage *d = (VDAgentFileXferDataMessage *)data;
278         msg_type = VDAGENTD_FILE_XFER_DATA;
279         id = d->id;
280         break;
281     }
282     }
283
284     conn = g_hash_table_lookup(active_xfers, GUINT_TO_POINTER(id));
285     if (!conn) {
286         if (debug)
287             syslog(LOG_DEBUG, "Could not find file-xfer %u (cancelled?)", id);
288         return;
289     }
290     udscs_write(conn, msg_type, 0, 0, data, message_header->size);
291 }
292
293 int virtio_port_read_complete(
294         struct vdagent_virtio_port *vport,
295         int port_nr,
296         VDAgentMessage *message_header,
297         uint8_t *data)
298 {
299     uint32_t min_size = 0;
300
301     if (message_header->protocol != VD_AGENT_PROTOCOL) {
302         syslog(LOG_ERR, "message with wrong protocol version ignoring");
303         return 0;
304     }
305
306     switch (message_header->type) {
307     case VD_AGENT_MOUSE_STATE:
308         if (message_header->size != sizeof(VDAgentMouseState))
309             goto size_error;
310         vdagentd_uinput_do_mouse(&uinput, (VDAgentMouseState *)data);
311         if (!uinput) {
312             /* Try to re-open the tablet */
313             struct agent_data *agent_data =
314                 udscs_get_user_data(active_session_conn);
315             if (agent_data)
316                 uinput = vdagentd_uinput_create(uinput_device,
317                                                 agent_data->width,
318                                                 agent_data->height,
319                                                 agent_data->screen_info,
320                                                 agent_data->screen_count,
321                                                 debug > 1,
322                                                 uinput_fake);
323             if (!uinput) {
324                 syslog(LOG_CRIT, "Fatal uinput error");
325                 retval = 1;
326                 quit = 1;
327             }
328         }
329         break;
330     case VD_AGENT_MONITORS_CONFIG:
331         if (message_header->size < sizeof(VDAgentMonitorsConfig))
332             goto size_error;
333         do_client_monitors(vport, port_nr, message_header,
334                     (VDAgentMonitorsConfig *)data);
335         break;
336     case VD_AGENT_ANNOUNCE_CAPABILITIES:
337         if (message_header->size < sizeof(VDAgentAnnounceCapabilities))
338             goto size_error;
339         do_client_capabilities(vport, message_header,
340                         (VDAgentAnnounceCapabilities *)data);
341         break;
342     case VD_AGENT_CLIPBOARD_GRAB:
343     case VD_AGENT_CLIPBOARD_REQUEST:
344     case VD_AGENT_CLIPBOARD:
345     case VD_AGENT_CLIPBOARD_RELEASE:
346         switch (message_header->type) {
347         case VD_AGENT_CLIPBOARD_GRAB:
348             min_size = sizeof(VDAgentClipboardGrab); break;
349         case VD_AGENT_CLIPBOARD_REQUEST:
350             min_size = sizeof(VDAgentClipboardRequest); break;
351         case VD_AGENT_CLIPBOARD:
352             min_size = sizeof(VDAgentClipboard); break;
353         }
354         if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
355                                     VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
356             min_size += 4;
357         }
358         if (message_header->size < min_size) {
359             goto size_error;
360         }
361         do_client_clipboard(vport, message_header, data);
362         break;
363     case VD_AGENT_FILE_XFER_START:
364     case VD_AGENT_FILE_XFER_STATUS:
365     case VD_AGENT_FILE_XFER_DATA:
366         do_client_file_xfer(vport, message_header, data);
367         break;
368     case VD_AGENT_CLIENT_DISCONNECTED:
369         vdagent_virtio_port_reset(vport, VDP_CLIENT_PORT);
370         do_client_disconnect();
371         break;
372     default:
373         syslog(LOG_WARNING, "unknown message type %d, ignoring",
374                message_header->type);
375     }
376
377     return 0;
378
379 size_error:
380     syslog(LOG_ERR, "read: invalid message size: %u for message type: %u",
381            message_header->size, message_header->type);
382     return 0;
383 }
384
385 /* vdagentd <-> vdagent communication handling */
386 int do_agent_clipboard(struct udscs_connection *conn,
387         struct udscs_message_header *header, const uint8_t *data)
388 {
389     uint8_t selection = header->arg1;
390     uint32_t msg_type = 0, data_type = -1, size = header->size;
391
392     if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
393                                  VD_AGENT_CAP_CLIPBOARD_BY_DEMAND))
394         goto error;
395
396     /* Check that this agent is from the currently active session */
397     if (conn != active_session_conn) {
398         if (debug)
399             syslog(LOG_DEBUG, "%p clipboard req from agent which is not in "
400                               "the active session?", conn);
401         goto error;
402     }
403
404     if (!virtio_port) {
405         syslog(LOG_ERR, "Clipboard req from agent but no client connection");
406         goto error;
407     }
408
409     if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
410                                  VD_AGENT_CAP_CLIPBOARD_SELECTION) &&
411             selection != VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) {
412         goto error;
413     }
414
415     switch (header->type) {
416     case VDAGENTD_CLIPBOARD_GRAB:
417         msg_type = VD_AGENT_CLIPBOARD_GRAB;
418         agent_owns_clipboard[selection] = 1;
419         break;
420     case VDAGENTD_CLIPBOARD_REQUEST:
421         msg_type = VD_AGENT_CLIPBOARD_REQUEST;
422         data_type = header->arg2;
423         size = 0;
424         break;
425     case VDAGENTD_CLIPBOARD_DATA:
426         msg_type = VD_AGENT_CLIPBOARD;
427         data_type = header->arg2;
428         break;
429     case VDAGENTD_CLIPBOARD_RELEASE:
430         msg_type = VD_AGENT_CLIPBOARD_RELEASE;
431         size = 0;
432         agent_owns_clipboard[selection] = 0;
433         break;
434     }
435
436     if (size != header->size) {
437         syslog(LOG_ERR,
438                "unexpected extra data in clipboard msg, disconnecting agent");
439         return -1;
440     }
441
442     if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
443                                 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
444         size += 4;
445     }
446     if (data_type != -1) {
447         size += 4;
448     }
449
450     vdagent_virtio_port_write_start(virtio_port, VDP_CLIENT_PORT, msg_type,
451                                     0, size);
452
453     if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
454                                 VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
455         uint8_t sel[4] = { selection, 0, 0, 0 };
456         vdagent_virtio_port_write_append(virtio_port, sel, 4);
457     }
458     if (data_type != -1) {
459         vdagent_virtio_port_write_append(virtio_port, (uint8_t*)&data_type, 4);
460     }
461
462     vdagent_virtio_port_write_append(virtio_port, data, header->size);
463
464     return 0;
465
466 error:
467     if (header->type == VDAGENTD_CLIPBOARD_REQUEST) {
468         /* Let the agent know no answer is coming */
469         udscs_write(conn, VDAGENTD_CLIPBOARD_DATA,
470                     selection, VD_AGENT_CLIPBOARD_NONE, NULL, 0);
471     }
472     return 0;
473 }
474
475 /* When we open the vdagent virtio channel, the server automatically goes into
476    client mouse mode, so we can only have the channel open when we know the
477    active session resolution. This function checks that we have an agent in the
478    active session, and that it has told us its resolution. If these conditions
479    are met it sets the uinput tablet device's resolution and opens the virtio
480    channel (if it is not already open). If these conditions are not met, it
481    closes both. */
482 static void check_xorg_resolution(void)
483 {
484     struct agent_data *agent_data = udscs_get_user_data(active_session_conn);
485
486     if (agent_data && agent_data->screen_info) {
487         if (!uinput)
488             uinput = vdagentd_uinput_create(uinput_device,
489                                             agent_data->width,
490                                             agent_data->height,
491                                             agent_data->screen_info,
492                                             agent_data->screen_count,
493                                             debug > 1,
494                                             uinput_fake);
495         else
496             vdagentd_uinput_update_size(&uinput,
497                                         agent_data->width,
498                                         agent_data->height,
499                                         agent_data->screen_info,
500                                         agent_data->screen_count);
501         if (!uinput) {
502             syslog(LOG_CRIT, "Fatal uinput error");
503             retval = 1;
504             quit = 1;
505             return;
506         }
507
508         if (!virtio_port) {
509             syslog(LOG_INFO, "opening vdagent virtio channel");
510             virtio_port = vdagent_virtio_port_create(portdev,
511                                                      virtio_port_read_complete,
512                                                      NULL);
513             if (!virtio_port) {
514                 syslog(LOG_CRIT, "Fatal error opening vdagent virtio channel");
515                 retval = 1;
516                 quit = 1;
517                 return;
518             }
519             send_capabilities(virtio_port, 1);
520         }
521     } else {
522 #ifndef WITH_STATIC_UINPUT
523         vdagentd_uinput_destroy(&uinput);
524 #endif
525         if (virtio_port) {
526             vdagent_virtio_port_flush(&virtio_port);
527             vdagent_virtio_port_destroy(&virtio_port);
528             syslog(LOG_INFO, "closed vdagent virtio channel");
529         }
530     }
531 }
532
533 static int connection_matches_active_session(struct udscs_connection **connp,
534     void *priv)
535 {
536     struct udscs_connection **conn_ret = (struct udscs_connection **)priv;
537     struct agent_data *agent_data = udscs_get_user_data(*connp);
538
539     /* Check if this connection matches the currently active session */
540     if (!agent_data->session || !active_session)
541         return 0;
542     if (strcmp(agent_data->session, active_session))
543         return 0;
544
545     *conn_ret = *connp;
546     return 1;
547 }
548
549 void release_clipboards(void)
550 {
551     uint8_t sel;
552
553     for (sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; ++sel) {
554         if (agent_owns_clipboard[sel] && virtio_port) {
555             vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
556                                       VD_AGENT_CLIPBOARD_RELEASE, 0, &sel, 1);
557         }
558         agent_owns_clipboard[sel] = 0;
559     }
560 }
561
562 void update_active_session_connection(struct udscs_connection *new_conn)
563 {
564     if (session_info) {
565         new_conn = NULL;
566         if (!active_session)
567             active_session = session_info_get_active_session(session_info);
568         session_count = udscs_server_for_all_clients(server,
569                                          connection_matches_active_session,
570                                          (void*)&new_conn);
571     } else {
572         if (new_conn)
573             session_count++;
574         else
575             session_count--;
576     }
577
578     if (new_conn && session_count != 1) {
579         syslog(LOG_ERR, "multiple agents in one session, "
580                "disabling agent to avoid potential information leak");
581         new_conn = NULL;
582     }
583
584     if (new_conn == active_session_conn)
585         return;
586
587     active_session_conn = new_conn;
588     if (debug)
589         syslog(LOG_DEBUG, "%p is now the active session", new_conn);
590     if (active_session_conn && mon_config)
591         udscs_write(active_session_conn, VDAGENTD_MONITORS_CONFIG, 0, 0,
592                     (uint8_t *)mon_config, sizeof(VDAgentMonitorsConfig) +
593                     mon_config->num_of_monitors * sizeof(VDAgentMonConfig));
594
595     release_clipboards();
596
597     check_xorg_resolution();    
598 }
599
600 gboolean remove_active_xfers(gpointer key, gpointer value, gpointer conn)
601 {
602     if (value == conn) {
603         cancel_file_xfer(virtio_port, "Agent disc; cancelling file-xfer %u",
604                          GPOINTER_TO_UINT(key));
605         return 1;
606     } else
607         return 0;
608 }
609
610 void agent_connect(struct udscs_connection *conn)
611 {
612     struct agent_data *agent_data;
613
614     agent_data = calloc(1, sizeof(*agent_data));
615     if (!agent_data) {
616         syslog(LOG_ERR, "Out of memory allocating agent data, disconnecting");
617         udscs_destroy_connection(&conn);
618         return;
619     }
620
621     if (session_info) {
622         uint32_t pid = udscs_get_peer_cred(conn).pid;
623         agent_data->session = session_info_session_for_pid(session_info, pid);
624     }
625
626     udscs_set_user_data(conn, (void *)agent_data);
627     udscs_write(conn, VDAGENTD_VERSION, 0, 0,
628                 (uint8_t *)VERSION, strlen(VERSION) + 1);
629     update_active_session_connection(conn);
630 }
631
632 void agent_disconnect(struct udscs_connection *conn)
633 {
634     struct agent_data *agent_data = udscs_get_user_data(conn);
635
636     g_hash_table_foreach_remove(active_xfers, remove_active_xfers, conn);
637
638     free(agent_data->session);
639     agent_data->session = NULL;
640     update_active_session_connection(NULL);
641
642     free(agent_data);
643 }
644
645 void agent_read_complete(struct udscs_connection **connp,
646     struct udscs_message_header *header, uint8_t *data)
647 {
648     struct agent_data *agent_data = udscs_get_user_data(*connp);
649
650     switch (header->type) {
651     case VDAGENTD_GUEST_XORG_RESOLUTION: {
652         struct vdagentd_guest_xorg_resolution *res;
653         int n = header->size / sizeof(*res);
654
655         /* Detect older version session agent, but don't disconnect, as
656            that stops it from getting the VDAGENTD_VERSION message, and then
657            it will never re-exec the new version... */
658         if (header->arg1 == 0 && header->arg2 == 0) {
659             syslog(LOG_INFO, "got old session agent xorg resolution message, "
660                              "ignoring");
661             free(data);
662             return;
663         }
664
665         if (header->size != n * sizeof(*res)) {
666             syslog(LOG_ERR, "guest xorg resolution message has wrong size, "
667                             "disconnecting agent");
668             udscs_destroy_connection(connp);
669             free(data);
670             return;
671         }
672
673         free(agent_data->screen_info);
674         res = malloc(n * sizeof(*res));
675         if (!res) {
676             syslog(LOG_ERR, "out of memory allocating screen info");
677             n = 0;
678         }
679         memcpy(res, data, n * sizeof(*res));
680         agent_data->width  = header->arg1;
681         agent_data->height = header->arg2;
682         agent_data->screen_info  = res;
683         agent_data->screen_count = n;
684
685         check_xorg_resolution();
686         break;
687     }
688     case VDAGENTD_CLIPBOARD_GRAB:
689     case VDAGENTD_CLIPBOARD_REQUEST:
690     case VDAGENTD_CLIPBOARD_DATA:
691     case VDAGENTD_CLIPBOARD_RELEASE:
692         if (do_agent_clipboard(*connp, header, data)) {
693             udscs_destroy_connection(connp);
694             free(data);
695             return;
696         }
697         break;
698     case VDAGENTD_FILE_XFER_STATUS:{
699         VDAgentFileXferStatusMessage status;
700         status.id = header->arg1;
701         status.result = header->arg2;
702         vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
703                                   VD_AGENT_FILE_XFER_STATUS, 0,
704                                   (uint8_t *)&status, sizeof(status));
705         if (status.result == VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA)
706             g_hash_table_insert(active_xfers, GUINT_TO_POINTER(status.id),
707                                 *connp);
708         else
709             g_hash_table_remove(active_xfers, GUINT_TO_POINTER(status.id));
710         break;
711     }
712
713     default:
714         syslog(LOG_ERR, "unknown message from vdagent: %u, ignoring",
715                header->type);
716     }
717     free(data);
718 }
719
720 /* main */
721
722 static void usage(FILE *fp)
723 {
724     fprintf(fp,
725             "Usage: spice-vdagentd [OPTIONS]\n\n"
726             "Spice guest agent daemon, version %s.\n\n"
727             "Options:\n"
728             "  -h             print this text\n"
729             "  -d             log debug messages (use twice for extra info)\n"
730             "  -s <port>      set virtio serial port  [%s]\n"
731             "  -S <filename>  set udcs socket [%s]\n"
732             "  -u <dev>       set uinput device       [%s]\n"
733             "  -x             don't daemonize\n"
734 #ifdef HAVE_CONSOLE_KIT
735             "  -X             Disable console kit integration\n"
736 #endif
737 #ifdef HAVE_LIBSYSTEMD_LOGIN
738             "  -X         Disable systemd-logind integration\n"
739 #endif
740             ,VERSION, portdev, vdagentd_socket, uinput_device);
741 }
742
743 void daemonize(void)
744 {
745     int x;
746     FILE *pidfile;
747
748     /* detach from terminal */
749     switch (fork()) {
750     case 0:
751         close(0); close(1); close(2);
752         setsid();
753         x = open("/dev/null", O_RDWR); x = dup(x); x = dup(x);
754         pidfile = fopen(pidfilename, "w");
755         if (pidfile) {
756             fprintf(pidfile, "%d\n", (int)getpid());
757             fclose(pidfile);
758         }
759         break;
760     case -1:
761         syslog(LOG_ERR, "fork: %m");
762         retval = 1;
763     default:
764         udscs_destroy_server(server);
765         exit(retval);
766     }
767 }
768
769 void main_loop(void)
770 {
771     fd_set readfds, writefds;
772     int n, nfds;
773     int ck_fd = 0;
774
775     while (!quit) {
776         FD_ZERO(&readfds);
777         FD_ZERO(&writefds);
778
779         nfds = udscs_server_fill_fds(server, &readfds, &writefds);
780         n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds);
781         if (n >= nfds)
782             nfds = n + 1;
783
784         if (session_info) {
785             ck_fd = session_info_get_fd(session_info);
786             FD_SET(ck_fd, &readfds);
787             if (ck_fd >= nfds)
788                 nfds = ck_fd + 1;
789         }
790
791         n = select(nfds, &readfds, &writefds, NULL, NULL);
792         if (n == -1) {
793             if (errno == EINTR)
794                 continue;
795             syslog(LOG_CRIT, "Fatal error select: %m");
796             retval = 1;
797             break;
798         }
799
800         udscs_server_handle_fds(server, &readfds, &writefds);
801
802         if (virtio_port) {
803             vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds);
804             if (!virtio_port) {
805                 int old_client_connected = client_connected;
806                 syslog(LOG_CRIT,
807                        "AIIEEE lost spice client connection, reconnecting");
808                 virtio_port = vdagent_virtio_port_create(portdev,
809                                                      virtio_port_read_complete,
810                                                      NULL);
811                 if (!virtio_port) {
812                     syslog(LOG_CRIT,
813                            "Fatal error opening vdagent virtio channel");
814                     retval = 1;
815                     break;
816                 }
817                 do_client_disconnect();
818                 client_connected = old_client_connected;
819             }
820         }
821
822         if (session_info && FD_ISSET(ck_fd, &readfds)) {
823             active_session = session_info_get_active_session(session_info);
824             update_active_session_connection(NULL);
825         }
826     }
827 }
828
829 static void quit_handler(int sig)
830 {
831     quit = 1;
832 }
833
834 int main(int argc, char *argv[])
835 {
836     int c;
837     int do_daemonize = 1;
838     int want_session_info = 1;
839     struct sigaction act;
840
841     for (;;) {
842         if (-1 == (c = getopt(argc, argv, "-dhxXs:u:S:")))
843             break;
844         switch (c) {
845         case 'd':
846             debug++;
847             break;
848         case 's':
849             portdev = optarg;
850             break;
851         case 'S':
852             vdagentd_socket = optarg;
853             break;
854         case 'u':
855             uinput_device = optarg;
856             break;
857         case 'x':
858             do_daemonize = 0;
859             break;
860         case 'X':
861             want_session_info = 0;
862             break;
863         case 'h':
864             usage(stdout);
865             return 0;
866         default:
867             fputs("\n", stderr);
868             usage(stderr);
869             return 1;
870         }
871     }
872
873     if (strncmp(uinput_device, "/dev", 4) != 0) {
874         syslog(LOG_INFO, "using fake uinput");
875         uinput_fake = 1;
876     }
877
878     memset(&act, 0, sizeof(act));
879     act.sa_flags = SA_RESTART;
880     act.sa_handler = quit_handler;
881     sigaction(SIGINT, &act, NULL);
882     sigaction(SIGHUP, &act, NULL);
883     sigaction(SIGTERM, &act, NULL);
884     sigaction(SIGQUIT, &act, NULL);
885
886     openlog("spice-vdagentd", do_daemonize ? 0 : LOG_PERROR, LOG_USER);
887
888     /* Setup communication with vdagent process(es) */
889     server = udscs_create_server(vdagentd_socket, agent_connect,
890                                  agent_read_complete, agent_disconnect,
891                                  vdagentd_messages, VDAGENTD_NO_MESSAGES,
892                                  debug);
893     if (!server) {
894         syslog(LOG_CRIT, "Fatal could not create server socket %s",
895                vdagentd_socket);
896         return 1;
897     }
898     if (chmod(vdagentd_socket, 0666)) {
899         syslog(LOG_CRIT, "Fatal could not change permissions on %s: %m",
900                vdagentd_socket);
901         udscs_destroy_server(server);
902         return 1;
903     }
904
905     if (do_daemonize)
906         daemonize();
907
908 #ifdef WITH_STATIC_UINPUT
909     uinput = vdagentd_uinput_create(uinput_device, 1024, 768, NULL, 0,
910                                     debug > 1, uinput_fake);
911     if (!uinput) {
912         udscs_destroy_server(server);
913         return 1;
914     }
915 #endif
916
917     if (want_session_info)
918         session_info = session_info_create(debug);
919     if (!session_info)
920         syslog(LOG_WARNING, "no session info, max 1 session agent allowed");
921
922     active_xfers = g_hash_table_new(g_direct_hash, g_direct_equal);
923     main_loop();
924
925     release_clipboards();
926
927     vdagentd_uinput_destroy(&uinput);
928     vdagent_virtio_port_flush(&virtio_port);
929     vdagent_virtio_port_destroy(&virtio_port);
930     session_info_destroy(session_info);
931     udscs_destroy_server(server);
932     if (unlink(vdagentd_socket) != 0)
933         syslog(LOG_ERR, "unlink %s: %s", vdagentd_socket, strerror(errno));
934     syslog(LOG_INFO, "vdagentd quiting, returning status %d", retval);
935
936     if (do_daemonize)
937         unlink(pidfilename);
938
939     return retval;
940 }