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