Home | History | Annotate | Line # | Download | only in src
      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 
     38 typedef 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 
     47 static XSpiceSmartcardCharDeviceInstance smartcard_sin = {
     48     .base = {
     49         .subtype = "smartcard"
     50     }
     51 };
     52 
     53 static 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 
     67 static 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 
     90 static void on_read_available(int fd, int event, void *opaque)
     91 {
     92     spice_server_char_device_wakeup(&smartcard_sin.base);
     93 }
     94 
     95 static 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
    124 static void smartcard_event(SpiceCharDeviceInstance *sin, uint8_t event)
    125 {
    126     ErrorF("%s: unimplemented; event is %d\n", __FUNCTION__, event);
    127 }
    128 #endif
    129 
    130 static void smartcard_state(SpiceCharDeviceInstance *sin, int connected)
    131 {
    132     ErrorF("%s: unimplemented; connected is %d\n", __FUNCTION__, connected);
    133 }
    134 
    135 static 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 
    148 int
    149 qxl_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