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