opensc-notify: handle reader insertion/removal events
- If readers are attatched, the new reader is probed for a card to check if a notification needs to be sent - removal of readers are not notified to the user, we assume that PC/SC sends the correct card removal event - The list of readers to be monitored is adjusted once a reader (dis)appears - On macOS, without PnP notification, we always check for new/removed readers with SCardListReaders - fixes interrupt handling in opensc-notify on Unix fixes https://github.com/OpenSC/OpenSC/issues/1874
This commit is contained in:
parent
7893d2860c
commit
2c26b7392d
|
@ -45,8 +45,8 @@ const char *sc_strerror(int error)
|
|||
"Message too long (keypad)",
|
||||
"Timeout while waiting for event from card reader",
|
||||
"Unresponsive card (correctly inserted?)",
|
||||
"Reader detached (hotplug device?)",
|
||||
"Reader reattached (hotplug device?)",
|
||||
"Reader detached",
|
||||
"Reader reattached",
|
||||
"Reader in use by another application"
|
||||
};
|
||||
const int rdr_base = -SC_ERROR_READER;
|
||||
|
|
|
@ -100,6 +100,7 @@ typedef unsigned __int8 uint8_t;
|
|||
#define SCARD_E_NOT_TRANSACTED 0x80100016 /**< An attempt was made to end a non-existent transaction. */
|
||||
#define SCARD_E_READER_UNAVAILABLE 0x80100017 /**< The specified reader is not currently available for use. */
|
||||
#define SCARD_E_NO_SERVICE 0x8010001D /**< The Smart card resource manager is not running. */
|
||||
#define SCARD_E_SERVICE_STOPPED 0x8010001E /**< The smart card resource manager has shut down. */
|
||||
#define SCARD_E_NO_READERS_AVAILABLE 0x8010002E /**< Cannot find a smart card reader. */
|
||||
#define SCARD_W_UNRESPONSIVE_CARD 0x80100066 /**< The smart card is not responding to a reset. */
|
||||
#define SCARD_W_UNPOWERED_CARD 0x80100067 /**< Power has been removed from the smart card, so that further communication is not possible. */
|
||||
|
|
|
@ -213,10 +213,10 @@ extern "C" {
|
|||
/* Event masks for sc_wait_for_event() */
|
||||
#define SC_EVENT_CARD_INSERTED 0x0001
|
||||
#define SC_EVENT_CARD_REMOVED 0x0002
|
||||
#define SC_EVENT_CARD_EVENTS SC_EVENT_CARD_INSERTED|SC_EVENT_CARD_REMOVED
|
||||
#define SC_EVENT_CARD_EVENTS (SC_EVENT_CARD_INSERTED|SC_EVENT_CARD_REMOVED)
|
||||
#define SC_EVENT_READER_ATTACHED 0x0004
|
||||
#define SC_EVENT_READER_DETACHED 0x0008
|
||||
#define SC_EVENT_READER_EVENTS SC_EVENT_READER_ATTACHED|SC_EVENT_READER_DETACHED
|
||||
#define SC_EVENT_READER_EVENTS (SC_EVENT_READER_ATTACHED|SC_EVENT_READER_DETACHED)
|
||||
|
||||
#define MAX_FILE_SIZE 65535
|
||||
|
||||
|
@ -1024,18 +1024,25 @@ int sc_disconnect_card(struct sc_card *card);
|
|||
int sc_detect_card_presence(sc_reader_t *reader);
|
||||
|
||||
/**
|
||||
* Waits for an event on readers. Note: only the event is detected,
|
||||
* there is no update of any card or other info.
|
||||
* NOTE: Only PC/SC backend implements this.
|
||||
* @param ctx pointer to a Context structure
|
||||
* @param event_mask The types of events to wait for; this should
|
||||
* be ORed from one of the following
|
||||
* SC_EVENT_CARD_REMOVED
|
||||
* SC_EVENT_CARD_INSERTED
|
||||
* SC_EVENT_READER_ATTACHED
|
||||
* @param event_reader (OUT) the reader on which the event was detected, or NULL if new reader
|
||||
* Waits for an event on readers.
|
||||
*
|
||||
* In case of a reader event (attached/detached), the list of reader is
|
||||
* adjusted accordingly. This means that a subsequent call to
|
||||
* `sc_ctx_detect_readers()` is not needed.
|
||||
*
|
||||
* @note Only PC/SC backend implements this. An infinite timeout on macOS does
|
||||
* not detect reader events (use a limited timeout instead if needed).
|
||||
*
|
||||
* @param ctx (IN) pointer to a Context structure
|
||||
* @param event_mask (IN) The types of events to wait for; this should
|
||||
* be ORed from one of the following:
|
||||
* - SC_EVENT_CARD_REMOVED
|
||||
* - SC_EVENT_CARD_INSERTED
|
||||
* - SC_EVENT_READER_ATTACHED
|
||||
* - SC_EVENT_READER_DETACHED
|
||||
* @param event_reader (OUT) the reader on which the event was detected
|
||||
* @param event (OUT) the events that occurred. This is also ORed
|
||||
* from the SC_EVENT_CARD_* constants listed above.
|
||||
* from the constants listed above.
|
||||
* @param timeout Amount of millisecs to wait; -1 means forever
|
||||
* @retval < 0 if an error occurred
|
||||
* @retval = 0 if a an event happened
|
||||
|
|
|
@ -127,6 +127,9 @@ struct pcsc_global_private_data {
|
|||
SCardTransmit_t SCardTransmit;
|
||||
SCardListReaders_t SCardListReaders;
|
||||
SCardGetAttrib_t SCardGetAttrib;
|
||||
|
||||
sc_reader_t *attached_reader;
|
||||
sc_reader_t *removed_reader;
|
||||
};
|
||||
|
||||
struct pcsc_private_data {
|
||||
|
@ -185,6 +188,7 @@ static int pcsc_to_opensc_error(LONG rv)
|
|||
return SC_ERROR_READER_DETACHED;
|
||||
|
||||
case SCARD_E_NO_SERVICE:
|
||||
case SCARD_E_SERVICE_STOPPED:
|
||||
/* If the service is (auto)started, there could be readers later */
|
||||
return SC_ERROR_NO_READERS_FOUND;
|
||||
case SCARD_E_NO_SMARTCARD:
|
||||
|
@ -358,7 +362,7 @@ static int refresh_attributes(sc_reader_t *reader)
|
|||
if (reader->ctx->flags & SC_CTX_FLAG_TERMINATE)
|
||||
return SC_ERROR_NOT_ALLOWED;
|
||||
|
||||
if (priv->reader_state.szReader == NULL) {
|
||||
if (priv->reader_state.szReader == NULL || reader->ctx->flags & SC_READER_REMOVED) {
|
||||
priv->reader_state.szReader = reader->name;
|
||||
priv->reader_state.dwCurrentState = SCARD_STATE_UNAWARE;
|
||||
priv->reader_state.dwEventState = SCARD_STATE_UNAWARE;
|
||||
|
@ -382,12 +386,11 @@ static int refresh_attributes(sc_reader_t *reader)
|
|||
}
|
||||
|
||||
/* the system could not detect the reader. It means, the prevoiusly attached reader is disconnected. */
|
||||
if (
|
||||
if (rv == (LONG)SCARD_E_UNKNOWN_READER
|
||||
#ifdef SCARD_E_NO_READERS_AVAILABLE
|
||||
(rv == (LONG)SCARD_E_NO_READERS_AVAILABLE) ||
|
||||
|| rv == (LONG)SCARD_E_NO_READERS_AVAILABLE
|
||||
#endif
|
||||
(rv == (LONG)SCARD_E_UNKNOWN_READER) || (rv == (LONG)SCARD_E_SERVICE_STOPPED)) {
|
||||
|
||||
|| rv == (LONG)SCARD_E_SERVICE_STOPPED) {
|
||||
if (old_flags & SC_READER_CARD_PRESENT) {
|
||||
reader->flags |= SC_READER_CARD_CHANGED;
|
||||
}
|
||||
|
@ -1143,7 +1146,7 @@ static void detect_reader_features(sc_reader_t *reader, SCARDHANDLE card_handle)
|
|||
return;
|
||||
|
||||
rv = gpriv->SCardControl(card_handle, CM_IOCTL_GET_FEATURE_REQUEST, NULL, 0, feature_buf, sizeof(feature_buf), &feature_len);
|
||||
if (rv != (LONG)SCARD_S_SUCCESS) {
|
||||
if (rv != SCARD_S_SUCCESS) {
|
||||
PCSC_TRACE(reader, "SCardControl failed", rv);
|
||||
return;
|
||||
}
|
||||
|
@ -1382,6 +1385,8 @@ static int pcsc_detect_readers(sc_context_t *ctx)
|
|||
}
|
||||
|
||||
sc_log(ctx, "Probing PC/SC readers");
|
||||
gpriv->attached_reader = NULL;
|
||||
gpriv->removed_reader = NULL;
|
||||
|
||||
do {
|
||||
if (gpriv->pcsc_ctx == (SCARDCONTEXT)-1) {
|
||||
|
@ -1399,12 +1404,11 @@ static int pcsc_detect_readers(sc_context_t *ctx)
|
|||
* All readers have disappeared, so mark them as
|
||||
* such so we don't keep polling them over and over.
|
||||
*/
|
||||
if (
|
||||
if (rv == (LONG)SCARD_E_NO_SERVICE
|
||||
#ifdef SCARD_E_NO_READERS_AVAILABLE
|
||||
(rv == (LONG)SCARD_E_NO_READERS_AVAILABLE) ||
|
||||
|| rv == (LONG)SCARD_E_NO_READERS_AVAILABLE
|
||||
#endif
|
||||
(rv == (LONG)SCARD_E_NO_SERVICE) || (rv == (LONG)SCARD_E_SERVICE_STOPPED)) {
|
||||
|
||||
|| rv == (LONG)SCARD_E_SERVICE_STOPPED) {
|
||||
for (i = 0; i < sc_ctx_get_reader_count(ctx); i++) {
|
||||
sc_reader_t *reader = sc_ctx_get_reader(ctx, i);
|
||||
|
||||
|
@ -1414,6 +1418,7 @@ static int pcsc_detect_readers(sc_context_t *ctx)
|
|||
}
|
||||
|
||||
reader->flags |= SC_READER_REMOVED;
|
||||
gpriv->removed_reader = reader;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1473,6 +1478,7 @@ static int pcsc_detect_readers(sc_context_t *ctx)
|
|||
if (!strcmp(reader->name, reader_name)) {
|
||||
if (reader->flags & SC_READER_REMOVED) {
|
||||
reader->flags &= ~SC_READER_REMOVED;
|
||||
gpriv->attached_reader = reader;
|
||||
refresh_attributes(reader);
|
||||
}
|
||||
break;
|
||||
|
@ -1487,8 +1493,11 @@ static int pcsc_detect_readers(sc_context_t *ctx)
|
|||
(reader_buf + reader_buf_size) - next_reader_name);
|
||||
reader_buf_size -= (next_reader_name - reader_name);
|
||||
} else {
|
||||
/* existing reader not found */
|
||||
reader->flags |= SC_READER_REMOVED;
|
||||
if (!(reader->flags & SC_READER_REMOVED)) {
|
||||
/* existing reader not found */
|
||||
reader->flags |= SC_READER_REMOVED;
|
||||
gpriv->removed_reader = reader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1503,6 +1512,7 @@ static int pcsc_detect_readers(sc_context_t *ctx)
|
|||
_sc_delete_reader(ctx, reader);
|
||||
continue;
|
||||
}
|
||||
gpriv->attached_reader = reader;
|
||||
|
||||
/* check for pinpad support early, to allow opensc-tool -l display accurate information */
|
||||
priv = reader->drv_data;
|
||||
|
@ -1555,7 +1565,7 @@ static int pcsc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_re
|
|||
SCARD_READERSTATE *rgReaderStates;
|
||||
size_t i;
|
||||
unsigned int num_watch, count;
|
||||
int r = SC_ERROR_INTERNAL;
|
||||
int r = SC_ERROR_INTERNAL, detect_readers = 0, detected_hotplug = 0;
|
||||
DWORD dwtimeout;
|
||||
|
||||
LOG_FUNC_CALLED(ctx);
|
||||
|
@ -1579,8 +1589,13 @@ static int pcsc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_re
|
|||
sc_reader_t *reader = sc_ctx_get_reader(ctx, i);
|
||||
if (reader->flags & SC_READER_REMOVED)
|
||||
continue;
|
||||
struct pcsc_private_data *priv = reader->drv_data;
|
||||
rgReaderStates[num_watch].szReader = reader->name;
|
||||
rgReaderStates[num_watch].dwCurrentState = SCARD_STATE_UNAWARE;
|
||||
if (priv->reader_state.szReader == NULL) {
|
||||
rgReaderStates[num_watch].dwCurrentState = SCARD_STATE_UNAWARE;
|
||||
} else {
|
||||
rgReaderStates[num_watch].dwCurrentState = priv->reader_state.dwEventState;
|
||||
}
|
||||
rgReaderStates[num_watch].dwEventState = SCARD_STATE_UNAWARE;
|
||||
num_watch++;
|
||||
}
|
||||
|
@ -1589,6 +1604,12 @@ static int pcsc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_re
|
|||
#ifdef __APPLE__
|
||||
/* OS X 10.6.2 - 10.12.6 do not support PnP notification */
|
||||
sc_log(ctx, "PnP notification not supported");
|
||||
/* Always check on new readers as if a hotplug
|
||||
* event was detected. This overwrites a
|
||||
* SC_ERROR_EVENT_TIMEOUT if a new reader is
|
||||
* detected with SC_SUCCESS. */
|
||||
detect_readers = 1;
|
||||
detected_hotplug = 1;
|
||||
#else
|
||||
rgReaderStates[num_watch].szReader = "\\\\?PnP?\\Notification";
|
||||
rgReaderStates[num_watch].dwCurrentState = SCARD_STATE_UNAWARE;
|
||||
|
@ -1622,9 +1643,11 @@ static int pcsc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_re
|
|||
goto out;
|
||||
}
|
||||
|
||||
*event_reader = NULL;
|
||||
*event = 0;
|
||||
|
||||
if (num_watch == 0) {
|
||||
sc_log(ctx, "No readers available to be watched");
|
||||
*event_reader = NULL;
|
||||
r = SC_ERROR_NO_READERS_FOUND;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1646,7 +1669,6 @@ static int pcsc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_re
|
|||
|
||||
/* Scan the current state of all readers to see if they
|
||||
* match any of the events we're polling for */
|
||||
*event = 0;
|
||||
for (i = 0, rsp = rgReaderStates; i < num_watch; i++, rsp++) {
|
||||
DWORD state, prev_state;
|
||||
sc_log(ctx, "'%s' before=0x%08X now=0x%08X",
|
||||
|
@ -1657,51 +1679,72 @@ static int pcsc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_re
|
|||
state = rsp->dwEventState;
|
||||
rsp->dwCurrentState = rsp->dwEventState;
|
||||
if (state & SCARD_STATE_CHANGED) {
|
||||
|
||||
/* check for hotplug events */
|
||||
if (!strcmp(rgReaderStates[i].szReader, "\\\\?PnP?\\Notification")) {
|
||||
if (!strcmp(rsp->szReader, "\\\\?PnP?\\Notification")) {
|
||||
sc_log(ctx, "detected hotplug event");
|
||||
*event |= SC_EVENT_READER_ATTACHED;
|
||||
*event_reader = NULL;
|
||||
}
|
||||
/* Windows sends hotplug event on both, attaching and
|
||||
* detaching a reader. pcscd only sends it in case of
|
||||
* attaching a reader. We'll detect later in which case we
|
||||
* are. */
|
||||
detect_readers = 1;
|
||||
detected_hotplug = 1;
|
||||
|
||||
if ((state & SCARD_STATE_PRESENT) && !(prev_state & SCARD_STATE_PRESENT)) {
|
||||
sc_log(ctx, "card inserted event");
|
||||
*event |= SC_EVENT_CARD_INSERTED;
|
||||
}
|
||||
/* Windows wants us to manually reset the changed state */
|
||||
rsp->dwEventState &= ~SCARD_STATE_CHANGED;
|
||||
|
||||
if ((prev_state & SCARD_STATE_PRESENT) && !(state & SCARD_STATE_PRESENT)) {
|
||||
sc_log(ctx, "card removed event");
|
||||
*event |= SC_EVENT_CARD_REMOVED;
|
||||
}
|
||||
/* By default, ignore a hotplug event as if a timout
|
||||
* occurred, since it may be an unrequested removal or
|
||||
* false alarm. Just continue to loop and check at the end
|
||||
* of this function whether we need to return the attached
|
||||
* reader or not. */
|
||||
r = SC_ERROR_EVENT_TIMEOUT;
|
||||
} else {
|
||||
sc_reader_t *reader = sc_ctx_get_reader_by_name(ctx, rsp->szReader);
|
||||
if (reader) {
|
||||
/* copy the state so we know what to watch out for */
|
||||
struct pcsc_private_data *priv = reader->drv_data;
|
||||
priv->reader_state.dwEventState = state;
|
||||
priv->reader_state.dwCurrentState = prev_state;
|
||||
}
|
||||
|
||||
if ((state & SCARD_STATE_UNKNOWN) && !(prev_state & SCARD_STATE_UNKNOWN)) {
|
||||
sc_log(ctx, "reader detached event");
|
||||
*event |= SC_EVENT_READER_DETACHED;
|
||||
}
|
||||
if ((state & SCARD_STATE_PRESENT) && !(prev_state & SCARD_STATE_PRESENT)) {
|
||||
sc_log(ctx, "card inserted event");
|
||||
*event |= SC_EVENT_CARD_INSERTED;
|
||||
}
|
||||
|
||||
if ((prev_state & SCARD_STATE_UNKNOWN) && !(state & SCARD_STATE_UNKNOWN)) {
|
||||
sc_log(ctx, "reader re-attached event");
|
||||
*event |= SC_EVENT_READER_ATTACHED;
|
||||
}
|
||||
if ((prev_state & SCARD_STATE_PRESENT) && !(state & SCARD_STATE_PRESENT)) {
|
||||
sc_log(ctx, "card removed event");
|
||||
*event |= SC_EVENT_CARD_REMOVED;
|
||||
}
|
||||
|
||||
if (*event & event_mask) {
|
||||
sc_log(ctx, "Matching event 0x%02X in reader %s", *event, rsp->szReader);
|
||||
*event_reader = sc_ctx_get_reader_by_name(ctx, rsp->szReader);
|
||||
r = SC_SUCCESS;
|
||||
goto out;
|
||||
}
|
||||
if ((state & SCARD_STATE_UNKNOWN) && !(prev_state & SCARD_STATE_UNKNOWN)) {
|
||||
sc_log(ctx, "reader detached event");
|
||||
*event |= SC_EVENT_READER_DETACHED;
|
||||
detect_readers = 1;
|
||||
}
|
||||
|
||||
if ((state & SCARD_STATE_IGNORE) && !(prev_state & SCARD_STATE_IGNORE)) {
|
||||
sc_log(ctx, "reader detached event");
|
||||
*event |= SC_EVENT_READER_DETACHED;
|
||||
detect_readers = 1;
|
||||
}
|
||||
|
||||
if ((prev_state & SCARD_STATE_UNKNOWN) && !(state & SCARD_STATE_UNKNOWN)) {
|
||||
sc_log(ctx, "reader re-attached event");
|
||||
*event |= SC_EVENT_READER_ATTACHED;
|
||||
detect_readers = 1;
|
||||
}
|
||||
|
||||
if (*event & event_mask) {
|
||||
sc_log(ctx, "Matching event 0x%02X in reader %s", *event, rsp->szReader);
|
||||
*event_reader = reader;
|
||||
r = SC_SUCCESS;
|
||||
goto out;
|
||||
} else {
|
||||
*event = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* No match - copy the state so pcscd knows
|
||||
* what to watch out for */
|
||||
/* rsp->dwCurrentState = rsp->dwEventState; */
|
||||
}
|
||||
|
||||
if (timeout == 0) {
|
||||
r = SC_ERROR_EVENT_TIMEOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Set the timeout if caller wants to time out */
|
||||
|
@ -1713,13 +1756,13 @@ static int pcsc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_re
|
|||
|
||||
rv = gpriv->SCardGetStatusChange(gpriv->pcsc_wait_ctx, dwtimeout, rgReaderStates, num_watch);
|
||||
|
||||
if (rv == (LONG) SCARD_E_CANCELLED) {
|
||||
if (rv == (LONG)SCARD_E_CANCELLED) {
|
||||
/* C_Finalize was called, events don't matter */
|
||||
r = SC_ERROR_EVENT_TIMEOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (rv == (LONG) SCARD_E_TIMEOUT) {
|
||||
if (rv == (LONG)SCARD_E_TIMEOUT) {
|
||||
r = SC_ERROR_EVENT_TIMEOUT;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1731,12 +1774,54 @@ static int pcsc_wait_for_event(sc_context_t *ctx, unsigned int event_mask, sc_re
|
|||
}
|
||||
}
|
||||
out:
|
||||
if (!reader_states) {
|
||||
free(rgReaderStates);
|
||||
/* in case of an error re-detect all readers */
|
||||
if (r < 0 && r != SC_ERROR_EVENT_TIMEOUT)
|
||||
detect_readers = 1;
|
||||
|
||||
if (detect_readers) {
|
||||
pcsc_detect_readers(ctx);
|
||||
}
|
||||
else if (*reader_states == NULL) {
|
||||
sc_log(ctx, "return allocated 'reader states'");
|
||||
*reader_states = rgReaderStates;
|
||||
|
||||
if (detected_hotplug) {
|
||||
if (gpriv->attached_reader) {
|
||||
if (event_reader && event && !*event) {
|
||||
/* no other event has been detected, yet */
|
||||
*event_reader = gpriv->attached_reader;
|
||||
*event = SC_EVENT_READER_ATTACHED;
|
||||
r = SC_SUCCESS;
|
||||
}
|
||||
gpriv->attached_reader = NULL;
|
||||
} else if (gpriv->removed_reader) {
|
||||
/* Normally, we only check the hotplug event for attached readers.
|
||||
* However, Windows also notifies on removal. Check, if the latter
|
||||
* was requested by the caller. */
|
||||
if (event_mask & SC_EVENT_READER_DETACHED
|
||||
&& event_reader && event && !*event) {
|
||||
/* no other event has been detected, yet */
|
||||
*event_reader = gpriv->removed_reader;
|
||||
*event = SC_EVENT_READER_DETACHED;
|
||||
r = SC_SUCCESS;
|
||||
}
|
||||
gpriv->removed_reader = NULL;
|
||||
} else {
|
||||
/* false alarm, there was no reader attached or removed,
|
||||
* avoid re-initialize the reader states by resetting detect_readers */
|
||||
detect_readers = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (detect_readers) {
|
||||
free(rgReaderStates);
|
||||
if (reader_states && *reader_states)
|
||||
*reader_states = NULL;
|
||||
} else {
|
||||
if (!reader_states) {
|
||||
free(rgReaderStates);
|
||||
}
|
||||
else if (*reader_states == NULL) {
|
||||
sc_log(ctx, "return allocated reader states");
|
||||
*reader_states = rgReaderStates;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_FUNC_RETURN(ctx, r);
|
||||
|
@ -2449,6 +2534,7 @@ int pcsc_use_reader(sc_context_t *ctx, void * pcsc_context_handle, void * pcsc_c
|
|||
}
|
||||
|
||||
sc_log(ctx, "Probing PC/SC reader");
|
||||
gpriv->attached_reader = NULL;
|
||||
|
||||
gpriv->pcsc_ctx = *(SCARDCONTEXT *)pcsc_context_handle;
|
||||
card_handle = *(SCARDHANDLE *)pcsc_card_handle;
|
||||
|
@ -2467,6 +2553,7 @@ int pcsc_use_reader(sc_context_t *ctx, void * pcsc_context_handle, void * pcsc_c
|
|||
} else {
|
||||
_sc_delete_reader(ctx, reader);
|
||||
}
|
||||
gpriv->attached_reader = reader;
|
||||
}
|
||||
|
||||
out:
|
||||
|
|
|
@ -49,26 +49,21 @@ void Sleep(unsigned int Milliseconds)
|
|||
}
|
||||
#endif
|
||||
|
||||
void stop_daemon()
|
||||
{
|
||||
#ifdef PCSCLITE_GOOD
|
||||
sc_cancel(ctx);
|
||||
#endif
|
||||
run_daemon = 0;
|
||||
}
|
||||
|
||||
void notify_daemon()
|
||||
{
|
||||
int r;
|
||||
const unsigned int event_mask = SC_EVENT_CARD_EVENTS;
|
||||
const unsigned int event_mask = SC_EVENT_CARD_EVENTS|SC_EVENT_READER_EVENTS;
|
||||
unsigned int event;
|
||||
struct sc_reader *event_reader = NULL;
|
||||
size_t error_count = 0;
|
||||
void *reader_states = NULL;
|
||||
#ifndef __APPLE__
|
||||
/* timeout adjusted to the maximum response time for WM_CLOSE in case
|
||||
* canceling doesn't work */
|
||||
const int timeout = 20000;
|
||||
struct sc_atr old_atr;
|
||||
void *reader_states = NULL;
|
||||
#else
|
||||
/* lower timeout, because Apple doesn't support hotplug events */
|
||||
const int timeout = 2000;
|
||||
#endif
|
||||
|
||||
r = sc_establish_context(&ctx, "opensc-notify");
|
||||
if (r < 0 || !ctx) {
|
||||
|
@ -76,50 +71,51 @@ void notify_daemon()
|
|||
return;
|
||||
}
|
||||
|
||||
while (run_daemon && error_count < 1000) {
|
||||
while (run_daemon) {
|
||||
|
||||
r = sc_wait_for_event(ctx, event_mask,
|
||||
&event_reader, &event, timeout, &reader_states);
|
||||
if (r < 0) {
|
||||
if (r == SC_ERROR_NO_READERS_FOUND) {
|
||||
/* No readers available, PnP notification not supported */
|
||||
Sleep(200);
|
||||
} else {
|
||||
error_count++;
|
||||
Sleep(timeout);
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
error_count = 0;
|
||||
|
||||
if (event & SC_EVENT_CARD_REMOVED) {
|
||||
sc_notify_id(ctx, &old_atr, NULL, NOTIFY_CARD_REMOVED);
|
||||
}
|
||||
if (event & SC_EVENT_CARD_INSERTED) {
|
||||
if (event_reader) {
|
||||
/* FIXME `pcsc_wait_for_event` has all the information that's
|
||||
* requested again with `pcsc_detect_card_presence`, but it
|
||||
* doesn't use the ATR, for example, to refresh the reader's
|
||||
* attributes. To get the ATR we need to call
|
||||
* sc_detect_card_presence. Eventually this should be fixed. */
|
||||
sc_detect_card_presence(event_reader);
|
||||
memcpy(old_atr.value, event_reader->atr.value,
|
||||
event_reader->atr.len);
|
||||
old_atr.len = event_reader->atr.len;
|
||||
} else {
|
||||
old_atr.len = 0;
|
||||
if (event_reader) {
|
||||
if (event & SC_EVENT_CARD_REMOVED
|
||||
|| (event & SC_EVENT_READER_DETACHED
|
||||
&& event_reader->flags & SC_READER_CARD_PRESENT)) {
|
||||
/* sc_notify_id uses only the reader's name for displaying on
|
||||
* removal, so use a dummy card here to get that information
|
||||
* into the notification */
|
||||
struct sc_pkcs15_card p15card;
|
||||
sc_card_t card;
|
||||
memset(&card, 0, sizeof card);
|
||||
card.reader = event_reader;
|
||||
memset(&p15card, 0, sizeof p15card);
|
||||
p15card.card = &card;
|
||||
sc_notify_id(ctx, &event_reader->atr, &p15card, NOTIFY_CARD_REMOVED);
|
||||
} else if (event & SC_EVENT_CARD_INSERTED
|
||||
|| (event & SC_EVENT_READER_ATTACHED
|
||||
&& event_reader->flags & SC_READER_CARD_PRESENT)) {
|
||||
/* sc_notify_id prevers the reader's name for displaying on
|
||||
* insertion, so use a dummy card here to get that information
|
||||
* into the notification */
|
||||
struct sc_pkcs15_card p15card;
|
||||
sc_card_t card;
|
||||
memset(&card, 0, sizeof card);
|
||||
card.reader = event_reader;
|
||||
memset(&p15card, 0, sizeof p15card);
|
||||
p15card.card = &card;
|
||||
sc_notify_id(ctx, &event_reader->atr, &p15card, NOTIFY_CARD_INSERTED);
|
||||
}
|
||||
sc_notify_id(ctx, old_atr.len ? &old_atr : NULL, NULL,
|
||||
NOTIFY_CARD_INSERTED);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx) {
|
||||
if (error_count >= 1000) {
|
||||
sc_log(ctx, "Too many errors; aborting.");
|
||||
}
|
||||
/* free `reader_states` */
|
||||
sc_wait_for_event(ctx, 0, NULL, NULL, 0, &reader_states);
|
||||
reader_states = NULL;
|
||||
sc_release_context(ctx);
|
||||
ctx = NULL;
|
||||
}
|
||||
|
@ -132,7 +128,8 @@ void notify_daemon()
|
|||
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (message == WM_CLOSE || message == WM_QUIT) {
|
||||
stop_daemon();
|
||||
run_daemon = 0;
|
||||
sc_cancel(ctx);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -182,32 +179,54 @@ WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPSTR lpCmdLine, int nShowC
|
|||
|
||||
#else
|
||||
|
||||
#ifdef HAVE_SIGACTION
|
||||
#if defined(HAVE_SIGACTION) && defined(HAVE_PTHREAD)
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int cancellation_fd[] = {-1, -1};
|
||||
|
||||
void sig_handler(int sig) {
|
||||
stop_daemon();
|
||||
run_daemon = 0;
|
||||
write(cancellation_fd[1], &sig, sizeof sig);
|
||||
}
|
||||
|
||||
void set_sa_handler(void)
|
||||
static void *cancellation_proc(void *arg)
|
||||
{
|
||||
struct sigaction new_sig, old_sig;
|
||||
(void)arg;
|
||||
while (run_daemon) {
|
||||
int sig;
|
||||
if (sizeof sig == read(cancellation_fd[0], &sig, sizeof sig)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
sc_cancel(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Register signal handlers */
|
||||
void setup_cancellation(void)
|
||||
{
|
||||
pthread_t cancellation_thread;
|
||||
struct sigaction new_sig, old_sig;
|
||||
new_sig.sa_handler = sig_handler;
|
||||
sigemptyset(&new_sig.sa_mask);
|
||||
new_sig.sa_flags = SA_RESTART;
|
||||
if ((sigaction(SIGINT, &new_sig, &old_sig) < 0)
|
||||
|| (sigaction(SIGTERM, &new_sig, &old_sig) < 0)) {
|
||||
fprintf(stderr, "Failed to create signal handler: %s", strerror(errno));
|
||||
|
||||
if (pipe(cancellation_fd) != 0
|
||||
|| (errno = pthread_create(&cancellation_thread, NULL, cancellation_proc, NULL)) != 0
|
||||
|| sigaction(SIGINT, &new_sig, &old_sig) != 0
|
||||
|| sigaction(SIGTERM, &new_sig, &old_sig) != 0) {
|
||||
fprintf(stderr, "Failed to setup cancellation: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void set_sa_handler(void)
|
||||
void setup_cancellation(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#include "opensc-notify-cmdline.h"
|
||||
|
@ -244,8 +263,8 @@ main (int argc, char **argv)
|
|||
|
||||
if ((!cmdline.customized_mode_counter && !cmdline.standard_mode_counter)
|
||||
|| cmdline.daemon_mode_counter) {
|
||||
set_sa_handler();
|
||||
run_daemon = 1;
|
||||
setup_cancellation();
|
||||
notify_daemon();
|
||||
} else {
|
||||
/* give the notification process some time to spawn */
|
||||
|
|
|
@ -34,8 +34,12 @@ static const char *get_inserted_text(struct sc_pkcs15_card *p15card, struct sc_a
|
|||
static char text[3*SC_MAX_ATR_SIZE] = {0};
|
||||
const char prefix[] = "ATR: ";
|
||||
|
||||
if (p15card && p15card->card && p15card->card->name) {
|
||||
if (p15card && p15card->card
|
||||
&& p15card->card->name) {
|
||||
return p15card->card->name;
|
||||
} else if (p15card && p15card->card
|
||||
&& p15card->card->reader && p15card->card->reader->name) {
|
||||
return p15card->card->reader->name;
|
||||
}
|
||||
|
||||
if (!atr)
|
||||
|
@ -192,7 +196,7 @@ const char *ui_get_str(struct sc_context *ctx, struct sc_atr *atr,
|
|||
str = "Dieses Fenster wird automatisch geschlossen, wenn die PIN am PIN-Pad eingegeben wurde (Timeout typischerweise nach 30 Sekunden).";
|
||||
break;
|
||||
case NOTIFY_CARD_INSERTED:
|
||||
if (p15card) {
|
||||
if (p15card && p15card->card && p15card->card->name) {
|
||||
str = "Smartcard kann jetzt verwendet werden";
|
||||
} else {
|
||||
str = "Smartcard erkannt";
|
||||
|
@ -260,7 +264,7 @@ const char *ui_get_str(struct sc_context *ctx, struct sc_atr *atr,
|
|||
str = "This window will be closed automatically after the PIN has been submitted on the PIN pad (timeout typically after 30 seconds).";
|
||||
break;
|
||||
case NOTIFY_CARD_INSERTED:
|
||||
if (p15card) {
|
||||
if (p15card && p15card->card && p15card->card->name) {
|
||||
str = "Smart card is ready to use";
|
||||
} else {
|
||||
str = "Smart card detected";
|
||||
|
|
Loading…
Reference in New Issue