diff options
Diffstat (limited to 'server/smartcard.c')
-rw-r--r-- | server/smartcard.c | 377 |
1 files changed, 63 insertions, 314 deletions
diff --git a/server/smartcard.c b/server/smartcard.c index e544f195..ec42960d 100644 --- a/server/smartcard.c +++ b/server/smartcard.c @@ -30,8 +30,8 @@ #include "reds.h" #include "char-device.h" -#include "red-channel-client-private.h" #include "smartcard.h" +#include "smartcard-channel-client.h" #include "migration-protocol.h" /* @@ -49,17 +49,6 @@ // Maximal length of APDU #define APDUBufSize 270 -typedef struct SmartCardChannelClient { - RedChannelClient base; - RedCharDeviceSmartcard *smartcard; - - /* read_from_client/write_to_device buffer. - * The beginning of the buffer should always be VSCMsgHeader*/ - RedCharDeviceWriteBuffer *write_buf; - int msg_in_write_buf; /* was the client msg received into a RedCharDeviceWriteBuffer - * or was it explicitly malloced */ -} SmartCardChannelClient; - G_DEFINE_TYPE(RedCharDeviceSmartcard, red_char_device_smartcard, RED_TYPE_CHAR_DEVICE) #define RED_CHAR_DEVICE_SMARTCARD_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RED_TYPE_CHAR_DEVICE_SMARTCARD, RedCharDeviceSmartcardPrivate)) @@ -76,18 +65,6 @@ struct RedCharDeviceSmartcardPrivate { int reader_added; // has reader_add been sent to the device }; -enum { - RED_PIPE_ITEM_TYPE_ERROR = RED_PIPE_ITEM_TYPE_CHANNEL_BASE, - RED_PIPE_ITEM_TYPE_SMARTCARD_DATA, - RED_PIPE_ITEM_TYPE_SMARTCARD_MIGRATE_DATA, -}; - -typedef struct RedErrorItem { - RedPipeItem base; - VSCMsgHeader vheader; - VSCMsgError error; -} RedErrorItem; - typedef struct RedMsgItem { RedPipeItem base; @@ -106,12 +83,7 @@ static struct Readers { SpiceCharDeviceInstance* sin[SMARTCARD_MAX_READERS]; } g_smartcard_readers = {0, {NULL}}; -static SpiceCharDeviceInstance* smartcard_readers_get_unattached(void); -static SpiceCharDeviceInstance* smartcard_readers_get(uint32_t reader_id); static int smartcard_char_device_add_to_readers(RedsState *reds, SpiceCharDeviceInstance *sin); -static void smartcard_char_device_attach_client( - SpiceCharDeviceInstance *char_device, SmartCardChannelClient *scc); -static void smartcard_channel_write_to_reader(RedCharDeviceWriteBuffer *write_buf); static RedMsgItem *smartcard_char_device_on_message_from_device( RedCharDeviceSmartcard *dev, VSCMsgHeader *header); @@ -171,9 +143,11 @@ static void smartcard_send_msg_to_client(RedPipeItem *msg, void *opaque) { RedCharDeviceSmartcard *dev = opaque; - spice_assert(dev->priv->scc && dev->priv->scc->base.client == client); - smartcard_channel_client_pipe_add_push(&dev->priv->scc->base, (RedPipeItem *)msg); + RedClient *this_client = red_channel_client_get_client(RED_CHANNEL_CLIENT(dev->priv->scc)); + spice_assert(dev->priv->scc && this_client == client); + smartcard_channel_client_pipe_add_push(RED_CHANNEL_CLIENT(dev->priv->scc), + (RedPipeItem *)msg); } static void smartcard_send_tokens_to_client(RedClient *client, uint32_t tokens, void *opaque) @@ -184,10 +158,11 @@ static void smartcard_send_tokens_to_client(RedClient *client, uint32_t tokens, static void smartcard_remove_client(RedClient *client, void *opaque) { RedCharDeviceSmartcard *dev = opaque; + RedClient *this_client = red_channel_client_get_client(RED_CHANNEL_CLIENT(dev->priv->scc)); spice_printerr("smartcard dev %p, client %p", dev, client); - spice_assert(dev->priv->scc && dev->priv->scc->base.client == client); - red_channel_client_shutdown(&dev->priv->scc->base); + spice_assert(dev->priv->scc && this_client == client); + red_channel_client_shutdown(RED_CHANNEL_CLIENT(dev->priv->scc)); } RedMsgItem *smartcard_char_device_on_message_from_device(RedCharDeviceSmartcard *dev, @@ -214,7 +189,8 @@ RedMsgItem *smartcard_char_device_on_message_from_device(RedCharDeviceSmartcard /* We patch the reader_id, since the device only knows about itself, and * we know about the sum of readers. */ sent_header->reader_id = dev->priv->reader_id; - return smartcard_get_vsc_msg_item(&dev->priv->scc->base, sent_header); + return smartcard_get_vsc_msg_item(RED_CHANNEL_CLIENT(dev->priv->scc), + sent_header); } return NULL; } @@ -232,7 +208,7 @@ static int smartcard_char_device_add_to_readers(RedsState *reds, SpiceCharDevice return 0; } -static SpiceCharDeviceInstance *smartcard_readers_get(uint32_t reader_id) +SpiceCharDeviceInstance *smartcard_readers_get(uint32_t reader_id) { spice_assert(reader_id < g_smartcard_readers.num); return g_smartcard_readers.sin[reader_id]; @@ -240,7 +216,7 @@ static SpiceCharDeviceInstance *smartcard_readers_get(uint32_t reader_id) /* TODO: fix implementation for multiple readers. Each reader should have a separated * channel */ -static SpiceCharDeviceInstance *smartcard_readers_get_unattached(void) +SpiceCharDeviceInstance *smartcard_readers_get_unattached(void) { int i; RedCharDeviceSmartcard* dev; @@ -289,7 +265,7 @@ RedCharDevice *smartcard_device_connect(RedsState *reds, SpiceCharDeviceInstance return RED_CHAR_DEVICE(dev); } -static void smartcard_char_device_notify_reader_add(RedCharDeviceSmartcard *dev) +void smartcard_char_device_notify_reader_add(RedCharDeviceSmartcard *dev) { RedCharDeviceWriteBuffer *write_buf; VSCMsgHeader *vheader; @@ -307,44 +283,43 @@ static void smartcard_char_device_notify_reader_add(RedCharDeviceSmartcard *dev) smartcard_channel_write_to_reader(write_buf); } -static void smartcard_char_device_attach_client(SpiceCharDeviceInstance *char_device, - SmartCardChannelClient *scc) +void smartcard_char_device_attach_client(SpiceCharDeviceInstance *char_device, + SmartCardChannelClient *scc) { RedCharDeviceSmartcard *dev = red_char_device_opaque_get(char_device->st); int client_added; - spice_assert(!scc->smartcard && !dev->priv->scc); + spice_assert(!smartcard_channel_client_get_char_device(scc) && !dev->priv->scc); dev->priv->scc = scc; - scc->smartcard = dev; + smartcard_channel_client_set_char_device(scc, dev); client_added = red_char_device_client_add(RED_CHAR_DEVICE(dev), - scc->base.client, + red_channel_client_get_client(RED_CHANNEL_CLIENT(scc)), FALSE, /* no flow control yet */ 0, /* send queue size */ ~0, ~0, - red_channel_client_is_waiting_for_migrate_data( - &scc->base)); + red_channel_client_is_waiting_for_migrate_data(RED_CHANNEL_CLIENT(scc))); if (!client_added) { spice_warning("failed"); dev->priv->scc = NULL; - scc->smartcard = NULL; - red_channel_client_disconnect(&scc->base); + smartcard_channel_client_set_char_device(scc, NULL); + red_channel_client_disconnect(RED_CHANNEL_CLIENT(scc)); } } -static void smartcard_char_device_notify_reader_remove(RedCharDeviceSmartcard *dev) +gboolean smartcard_char_device_notify_reader_remove(RedCharDeviceSmartcard *dev) { RedCharDeviceWriteBuffer *write_buf; VSCMsgHeader *vheader; if (!dev->priv->reader_added) { spice_debug("reader add was never sent to the device"); - return; + return FALSE; } write_buf = red_char_device_write_buffer_get(RED_CHAR_DEVICE(dev), NULL, sizeof(vheader)); if (!write_buf) { spice_error("failed to allocate write buffer"); - return; + return FALSE; } dev->priv->reader_added = FALSE; vheader = (VSCMsgHeader *)write_buf->buf; @@ -352,98 +327,28 @@ static void smartcard_char_device_notify_reader_remove(RedCharDeviceSmartcard *d vheader->reader_id = dev->priv->reader_id; vheader->length = 0; smartcard_channel_write_to_reader(write_buf); -} -static void smartcard_char_device_detach_client(SmartCardChannelClient *scc) -{ - RedCharDeviceSmartcard *dev; - - if (!scc->smartcard) { - return; - } - dev = scc->smartcard; - spice_assert(dev->priv->scc == scc); - red_char_device_client_remove(RED_CHAR_DEVICE(dev), scc->base.client); - scc->smartcard = NULL; - dev->priv->scc = NULL; -} - -static int smartcard_channel_client_config_socket(RedChannelClient *rcc) -{ return TRUE; } -static uint8_t *smartcard_channel_alloc_msg_rcv_buf(RedChannelClient *rcc, - uint16_t type, - uint32_t size) +void smartcard_char_device_detach_client(RedCharDeviceSmartcard *smartcard, + SmartCardChannelClient *scc) { - SmartCardChannelClient *scc = SPICE_CONTAINEROF(rcc, SmartCardChannelClient, base); - - /* todo: only one reader is actually supported. When we fix the code to support - * multiple readers, we will porbably associate different devices to - * differenc channels */ - if (!scc->smartcard) { - scc->msg_in_write_buf = FALSE; - return spice_malloc(size); - } else { - RedCharDeviceSmartcard *dev; - - spice_assert(g_smartcard_readers.num == 1); - dev = scc->smartcard; - spice_assert(dev->priv->scc || scc->smartcard); - spice_assert(!scc->write_buf); - scc->write_buf = red_char_device_write_buffer_get(RED_CHAR_DEVICE(dev), rcc->client, size); - - if (!scc->write_buf) { - spice_error("failed to allocate write buffer"); - return NULL; - } - scc->msg_in_write_buf = TRUE; - return scc->write_buf->buf; - } + spice_assert(smartcard->priv->scc == scc); + red_char_device_client_remove(RED_CHAR_DEVICE(smartcard), + red_channel_client_get_client(RED_CHANNEL_CLIENT(scc))); + smartcard_channel_client_set_char_device(scc, NULL); + smartcard->priv->scc = NULL; } -static void smartcard_channel_release_msg_rcv_buf(RedChannelClient *rcc, - uint16_t type, - uint32_t size, - uint8_t *msg) -{ - SmartCardChannelClient *scc = SPICE_CONTAINEROF(rcc, SmartCardChannelClient, base); - - /* todo: only one reader is actually supported. When we fix the code to support - * multiple readers, we will porbably associate different devices to - * differenc channels */ - - if (!scc->msg_in_write_buf) { - spice_assert(!scc->write_buf); - free(msg); - } else { - if (scc->write_buf) { /* msg hasn't been pushed to the guest */ - spice_assert(scc->write_buf->buf == msg); - red_char_device_write_buffer_release(RED_CHAR_DEVICE(scc->smartcard), scc->write_buf); - scc->write_buf = NULL; - } - } -} - -static void smartcard_channel_send_data(RedChannelClient *rcc, SpiceMarshaller *m, - RedPipeItem *item, VSCMsgHeader *vheader) +static int smartcard_channel_client_config_socket(RedChannelClient *rcc) { - spice_assert(rcc); - spice_assert(vheader); - red_channel_client_init_send_data(rcc, SPICE_MSG_SMARTCARD_DATA, item); - spice_marshaller_add_ref(m, (uint8_t*)vheader, sizeof(VSCMsgHeader)); - if (vheader->length > 0) { - spice_marshaller_add_ref(m, (uint8_t*)(vheader+1), vheader->length); - } + return TRUE; } -static void smartcard_channel_send_error( - RedChannelClient *rcc, SpiceMarshaller *m, RedPipeItem *item) +SmartCardChannelClient* smartcard_char_device_get_client(RedCharDeviceSmartcard *smartcard) { - RedErrorItem* error_item = (RedErrorItem*)item; - - smartcard_channel_send_data(rcc, m, item, &error_item->vheader); + return smartcard->priv->scc; } static void smartcard_channel_send_msg(RedChannelClient *rcc, @@ -451,7 +356,7 @@ static void smartcard_channel_send_msg(RedChannelClient *rcc, { RedMsgItem* msg_item = (RedMsgItem*)item; - smartcard_channel_send_data(rcc, m, item, msg_item->vheader); + smartcard_channel_client_send_data(rcc, m, item, msg_item->vheader); } static void smartcard_channel_send_migrate_data(RedChannelClient *rcc, @@ -461,8 +366,8 @@ static void smartcard_channel_send_migrate_data(RedChannelClient *rcc, RedCharDeviceSmartcard *dev; SpiceMarshaller *m2; - scc = SPICE_CONTAINEROF(rcc, SmartCardChannelClient, base); - dev = scc->smartcard; + scc = SMARTCARD_CHANNEL_CLIENT(rcc); + dev = smartcard_channel_client_get_char_device(scc); red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item); spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_SMARTCARD_MAGIC); spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_SMARTCARD_VERSION); @@ -489,7 +394,7 @@ static void smartcard_channel_send_item(RedChannelClient *rcc, RedPipeItem *item switch (item->type) { case RED_PIPE_ITEM_TYPE_ERROR: - smartcard_channel_send_error(rcc, m, item); + smartcard_channel_client_send_error(rcc, m, item); break; case RED_PIPE_ITEM_TYPE_SMARTCARD_DATA: smartcard_channel_send_msg(rcc, m, item); @@ -515,18 +420,6 @@ static void smartcard_channel_release_pipe_item(RedChannelClient *rcc, } } -static void smartcard_channel_on_disconnect(RedChannelClient *rcc) -{ - SmartCardChannelClient *scc = SPICE_CONTAINEROF(rcc, SmartCardChannelClient, base); - - if (scc->smartcard) { - RedCharDeviceSmartcard *dev = scc->smartcard; - - smartcard_char_device_detach_client(scc); - smartcard_char_device_notify_reader_remove(dev); - } -} - /* this is called from both device input and client input. since the device is * a usb device, the context is still the main thread (kvm_main_loop, timers) * so no mutex is required. */ @@ -536,20 +429,6 @@ static void smartcard_channel_client_pipe_add_push(RedChannelClient *rcc, red_channel_client_pipe_add_push(rcc, item); } -static void smartcard_push_error(RedChannelClient *rcc, uint32_t reader_id, VSCErrorCode error) -{ - RedErrorItem *error_item = spice_new0(RedErrorItem, 1); - - red_pipe_item_init(&error_item->base, RED_PIPE_ITEM_TYPE_ERROR); - - error_item->base.type = RED_PIPE_ITEM_TYPE_ERROR; - error_item->vheader.reader_id = reader_id; - error_item->vheader.type = VSC_Error; - error_item->vheader.length = sizeof(error_item->error); - error_item->error.code = error; - smartcard_channel_client_pipe_add_push(rcc, &error_item->base); -} - static void smartcard_free_vsc_msg_item(RedMsgItem *item) { free(item->vheader); @@ -567,47 +446,7 @@ static RedMsgItem *smartcard_get_vsc_msg_item(RedChannelClient *rcc, return msg_item; } -static void smartcard_remove_reader(SmartCardChannelClient *scc, uint32_t reader_id) -{ - SpiceCharDeviceInstance *char_device = smartcard_readers_get(reader_id); - RedCharDeviceSmartcard *dev; - - if (char_device == NULL) { - smartcard_push_error(&scc->base, reader_id, - VSC_GENERAL_ERROR); - return; - } - - dev = red_char_device_opaque_get(char_device->st); - if (dev->priv->reader_added == FALSE) { - smartcard_push_error(&scc->base, reader_id, - VSC_GENERAL_ERROR); - return; - } - spice_assert(scc->smartcard == dev); - smartcard_char_device_notify_reader_remove(dev); -} - -static void smartcard_add_reader(SmartCardChannelClient *scc, uint8_t *name) -{ - if (!scc->smartcard) { /* we already tried to attach a reader to the client - when it connected */ - SpiceCharDeviceInstance *char_device = smartcard_readers_get_unattached(); - - if (!char_device) { - smartcard_push_error(&scc->base, VSCARD_UNDEFINED_READER_ID, - VSC_CANNOT_ADD_MORE_READERS); - return; - } - smartcard_char_device_attach_client(char_device, scc); - } - smartcard_char_device_notify_reader_add(scc->smartcard); - // The device sends a VSC_Error message, we will let it through, no - // need to send our own. We already set the correct reader_id, from - // our RedCharDeviceSmartcard. -} - -static void smartcard_channel_write_to_reader(RedCharDeviceWriteBuffer *write_buf) +void smartcard_channel_write_to_reader(RedCharDeviceWriteBuffer *write_buf) { SpiceCharDeviceInstance *sin; RedCharDeviceSmartcard *dev; @@ -620,7 +459,8 @@ static void smartcard_channel_write_to_reader(RedCharDeviceWriteBuffer *write_bu spice_assert(vheader->reader_id <= g_smartcard_readers.num); sin = g_smartcard_readers.sin[vheader->reader_id]; dev = (RedCharDeviceSmartcard *)red_char_device_opaque_get(sin->st); - spice_assert(!dev->priv->scc || dev == dev->priv->scc->smartcard); + spice_assert(!dev->priv->scc || + dev == smartcard_channel_client_get_device(dev->priv->scc)); /* protocol requires messages to be in network endianess */ vheader->type = htonl(vheader->type); vheader->length = htonl(vheader->length); @@ -629,15 +469,6 @@ static void smartcard_channel_write_to_reader(RedCharDeviceWriteBuffer *write_bu /* pushing the buffer to the write queue; It will be released * when it will be fully consumed by the device */ red_char_device_write_buffer_add(sin->st, write_buf); - if (dev->priv->scc && write_buf == dev->priv->scc->write_buf) { - dev->priv->scc->write_buf = NULL; - } -} - -static int smartcard_channel_client_handle_migrate_flush_mark(RedChannelClient *rcc) -{ - red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_SMARTCARD_MIGRATE_DATA); - return TRUE; } static void smartcard_device_restore_partial_read(RedCharDeviceSmartcard *dev, @@ -657,96 +488,13 @@ static void smartcard_device_restore_partial_read(RedCharDeviceSmartcard *dev, dev->priv->buf_pos = dev->priv->buf + mig_data->read_size; } -static int smartcard_channel_client_handle_migrate_data(RedChannelClient *rcc, - uint32_t size, void *message) +int smartcard_char_device_handle_migrate_data(RedCharDeviceSmartcard *smartcard, + SpiceMigrateDataSmartcard *mig_data) { - SmartCardChannelClient *scc; - SpiceMigrateDataHeader *header; - SpiceMigrateDataSmartcard *mig_data; - - scc = SPICE_CONTAINEROF(rcc, SmartCardChannelClient, base); - header = (SpiceMigrateDataHeader *)message; - mig_data = (SpiceMigrateDataSmartcard *)(header + 1); - if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataSmartcard)) { - spice_error("bad message size"); - return FALSE; - } - if (!migration_protocol_validate_header(header, - SPICE_MIGRATE_DATA_SMARTCARD_MAGIC, - SPICE_MIGRATE_DATA_SMARTCARD_VERSION)) { - spice_error("bad header"); - return FALSE; - } - - if (!mig_data->base.connected) { /* client wasn't attached to a smartcard */ - return TRUE; - } + smartcard->priv->reader_added = mig_data->reader_added; - if (!scc->smartcard) { - SpiceCharDeviceInstance *char_device = smartcard_readers_get_unattached(); - - if (!char_device) { - spice_warning("no unattached device available"); - return TRUE; - } else { - smartcard_char_device_attach_client(char_device, scc); - } - } - spice_debug("reader added %d partial read_size %u", mig_data->reader_added, mig_data->read_size); - scc->smartcard->priv->reader_added = mig_data->reader_added; - - smartcard_device_restore_partial_read(scc->smartcard, mig_data); - return red_char_device_restore(RED_CHAR_DEVICE(scc->smartcard), &mig_data->base); -} - -static int smartcard_channel_handle_message(RedChannelClient *rcc, - uint16_t type, - uint32_t size, - uint8_t *msg) -{ - VSCMsgHeader* vheader = (VSCMsgHeader*)msg; - SmartCardChannelClient *scc = SPICE_CONTAINEROF(rcc, SmartCardChannelClient, base); - - if (type != SPICE_MSGC_SMARTCARD_DATA) { - /* Handles seamless migration protocol. Also handles ack's, - * spicy sends them while spicec does not */ - return red_channel_client_handle_message(rcc, size, type, msg); - } - - spice_assert(size == vheader->length + sizeof(VSCMsgHeader)); - switch (vheader->type) { - case VSC_ReaderAdd: - smartcard_add_reader(scc, msg + sizeof(VSCMsgHeader)); - return TRUE; - break; - case VSC_ReaderRemove: - smartcard_remove_reader(scc, vheader->reader_id); - return TRUE; - break; - case VSC_Init: - // ignore - we should never get this anyway - return TRUE; - break; - case VSC_Error: - case VSC_ATR: - case VSC_CardRemove: - case VSC_APDU: - break; // passed on to device - default: - printf("ERROR: unexpected message on smartcard channel\n"); - return TRUE; - } - - /* todo: fix */ - if (vheader->reader_id >= g_smartcard_readers.num) { - spice_printerr("ERROR: received message for non existing reader: %d, %d, %d", vheader->reader_id, - vheader->type, vheader->length); - return FALSE; - } - spice_assert(scc->write_buf->buf == msg); - smartcard_channel_write_to_reader(scc->write_buf); - - return TRUE; + smartcard_device_restore_partial_read(smartcard, mig_data); + return red_char_device_restore(RED_CHAR_DEVICE(smartcard), &mig_data->base); } static void smartcard_channel_hold_pipe_item(RedChannelClient *rcc, @@ -764,18 +512,17 @@ static void smartcard_connect_client(RedChannel *channel, RedClient *client, SmartCardChannelClient *scc; - scc = (SmartCardChannelClient *)red_channel_client_create(sizeof(SmartCardChannelClient), - channel, - client, - stream, - FALSE, - num_common_caps, common_caps, - num_caps, caps); + scc = smartcard_channel_client_create(channel, + client, + stream, + FALSE, + num_common_caps, common_caps, + num_caps, caps); if (!scc) { return; } - red_channel_client_ack_zero_messages_window(&scc->base); + red_channel_client_ack_zero_messages_window(RED_CHANNEL_CLIENT(scc)); if (char_device) { smartcard_char_device_attach_client(char_device, scc); @@ -795,12 +542,12 @@ static void smartcard_init(RedsState *reds) spice_assert(!g_smartcard_channel); channel_cbs.config_socket = smartcard_channel_client_config_socket; - channel_cbs.on_disconnect = smartcard_channel_on_disconnect; + channel_cbs.on_disconnect = smartcard_channel_client_on_disconnect; channel_cbs.send_item = smartcard_channel_send_item; channel_cbs.hold_item = smartcard_channel_hold_pipe_item; channel_cbs.release_item = smartcard_channel_release_pipe_item; - channel_cbs.alloc_recv_buf = smartcard_channel_alloc_msg_rcv_buf; - channel_cbs.release_recv_buf = smartcard_channel_release_msg_rcv_buf; + channel_cbs.alloc_recv_buf = smartcard_channel_client_alloc_msg_rcv_buf; + channel_cbs.release_recv_buf = smartcard_channel_client_release_msg_rcv_buf; channel_cbs.handle_migrate_flush_mark = smartcard_channel_client_handle_migrate_flush_mark; channel_cbs.handle_migrate_data = smartcard_channel_client_handle_migrate_data; @@ -809,7 +556,7 @@ static void smartcard_init(RedsState *reds) reds_get_core_interface(reds), SPICE_CHANNEL_SMARTCARD, 0, FALSE /* handle_acks */, - smartcard_channel_handle_message, + smartcard_channel_client_handle_message, &channel_cbs, migration_flags); @@ -830,9 +577,6 @@ red_char_device_smartcard_finalize(GObject *object) RedCharDeviceSmartcard *self = RED_CHAR_DEVICE_SMARTCARD(object); free(self->priv->buf); - if (self->priv->scc) { - self->priv->scc->smartcard = NULL; - } G_OBJECT_CLASS(red_char_device_smartcard_parent_class)->finalize(object); } @@ -863,3 +607,8 @@ red_char_device_smartcard_init(RedCharDeviceSmartcard *self) self->priv->buf = spice_malloc(self->priv->buf_size); self->priv->buf_pos = self->priv->buf; } + +uint32_t smartcard_get_n_readers(void) +{ + return g_smartcard_readers.num; +} |