1/*
2 * Copyright 2014 Jeremy White for CodeWeavers Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include <errno.h>
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <fcntl.h>
31#include <unistd.h>
32#include <sys/socket.h>
33#include <sys/un.h>
34
35
36#include "spiceqxl_smartcard.h"
37
38typedef struct XSpiceSmartcardCharDeviceInstance {
39    SpiceCharDeviceInstance base;
40    qxl_screen_t *qxl;
41    int listen_fd;
42    int fd;
43    SpiceWatch *listen_watch;
44    SpiceWatch *watch;
45} XSpiceSmartcardCharDeviceInstance;
46
47static XSpiceSmartcardCharDeviceInstance smartcard_sin = {
48    .base = {
49        .subtype = "smartcard"
50    }
51};
52
53static int smartcard_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
54{
55    int written;
56
57    if (smartcard_sin.fd == -1)
58        return 0;
59
60    written = write(smartcard_sin.fd, buf, len);
61    if (written != len)
62        ErrorF("%s: ERROR: short write to smartcard socket - TODO buffering\n", __FUNCTION__);
63
64    return written;
65}
66
67static int smartcard_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
68{
69    int rc;
70
71    if (smartcard_sin.fd == -1)
72        return 0;
73
74    rc = read(smartcard_sin.fd, buf, len);
75    if (rc <= 0) {
76        if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
77            return 0;
78        }
79        ErrorF("smartcard socket died: %s\n", strerror(errno));
80
81        smartcard_sin.qxl->core->watch_remove(smartcard_sin.watch);
82        close(smartcard_sin.fd);
83        smartcard_sin.fd = -1;
84        smartcard_sin.watch = NULL;
85    }
86
87    return rc;
88}
89
90static void on_read_available(int fd, int event, void *opaque)
91{
92    spice_server_char_device_wakeup(&smartcard_sin.base);
93}
94
95static void on_accept_available(int fd, int event, void *opaque)
96{
97    qxl_screen_t *qxl = (qxl_screen_t *) opaque;
98    int flags;
99    int client_fd;
100
101    client_fd = accept(fd, NULL, NULL);
102    if (client_fd < 0)
103        return;
104
105    if (smartcard_sin.fd != -1) {
106        ErrorF("smartcard error: a new connection came in while an old one was active.\n");
107        close(client_fd);
108        return;
109    }
110
111    flags = fcntl(client_fd, F_GETFL, 0);
112    if (flags < 0)
113        flags = 0;
114    flags |= O_NONBLOCK;
115    fcntl(client_fd, F_SETFL, flags);
116
117    smartcard_sin.fd = client_fd;
118    smartcard_sin.watch = qxl->core->watch_add(smartcard_sin.fd, SPICE_WATCH_EVENT_READ, on_read_available, qxl);
119
120}
121
122
123#if SPICE_SERVER_VERSION >= 0x000c02
124static void smartcard_event(SpiceCharDeviceInstance *sin, uint8_t event)
125{
126    ErrorF("%s: unimplemented; event is %d\n", __FUNCTION__, event);
127}
128#endif
129
130static void smartcard_state(SpiceCharDeviceInstance *sin, int connected)
131{
132    ErrorF("%s: unimplemented; connected is %d\n", __FUNCTION__, connected);
133}
134
135static SpiceCharDeviceInterface smartcard_interface = {
136    .base.type          = SPICE_INTERFACE_CHAR_DEVICE,
137    .base.description   = "Xspice virtual channel char device",
138    .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
139    .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
140    .state              = smartcard_state,
141    .write              = smartcard_write,
142    .read               = smartcard_read,
143#if SPICE_SERVER_VERSION >= 0x000c02
144    .event              = smartcard_event,
145#endif
146};
147
148int
149qxl_add_spice_smartcard_interface (qxl_screen_t *qxl)
150{
151    int rc;
152    struct sockaddr_un addr;
153
154    if (qxl->smartcard_file[0] == 0) {
155        xf86DrvMsg(qxl->pScrn->scrnIndex, X_INFO, "smartcard: no file given, smartcard is disabled\n");
156        return 0;
157    }
158
159    smartcard_sin.fd = -1;
160    smartcard_sin.listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
161    if (smartcard_sin.listen_fd < 0) {
162        ErrorF("smartcard: unable to open socket: %s\n", strerror(errno));
163        return errno;
164    }
165
166    memset(&addr, 0, sizeof(addr));
167    addr.sun_family = AF_UNIX;
168    strncpy(addr.sun_path, qxl->smartcard_file, sizeof(addr.sun_path) - 1);
169    unlink(qxl->smartcard_file);
170
171    if (bind(smartcard_sin.listen_fd, (struct sockaddr *) &addr, sizeof(addr))) {
172        ErrorF("smartcard: unable to bind to unix domain %s: %s\n", qxl->smartcard_file, strerror(errno));
173        close(smartcard_sin.listen_fd);
174        return errno;
175    }
176
177    if (listen(smartcard_sin.listen_fd, 1)) {
178        ErrorF("smartcard: unable to listen to unix domain %s: %s\n", qxl->smartcard_file, strerror(errno));
179        close(smartcard_sin.listen_fd);
180        return errno;
181    }
182
183    smartcard_sin.listen_watch = qxl->core->watch_add(smartcard_sin.listen_fd, SPICE_WATCH_EVENT_READ, on_accept_available, qxl);
184
185    smartcard_sin.base.base.sif = &smartcard_interface.base;
186    smartcard_sin.qxl = qxl;
187
188    rc = spice_server_add_interface(qxl->spice_server, &smartcard_sin.base.base);
189    if (rc < 0)
190        return errno;
191
192    return 0;
193}
194