pcsc-lite 2.3.0
eventhandler.c
Go to the documentation of this file.
1/*
2 * MUSCLE SmartCard Development ( https://pcsclite.apdu.fr/ )
3 *
4 * Copyright (C) 2000-2002
5 * David Corcoran <corcoran@musclecard.com>
6 * Copyright (C) 2002-2023
7 * Ludovic Rousseau <ludovic.rousseau@free.fr>
8 *
9Redistribution and use in source and binary forms, with or without
10modification, are permitted provided that the following conditions
11are met:
12
131. Redistributions of source code must retain the above copyright
14 notice, this list of conditions and the following disclaimer.
152. Redistributions in binary form must reproduce the above copyright
16 notice, this list of conditions and the following disclaimer in the
17 documentation and/or other materials provided with the distribution.
183. The name of the author may not be used to endorse or promote products
19 derived from this software without specific prior written permission.
20
21THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
39#include "config.h"
40#include <sys/types.h>
41#include <sys/stat.h>
42#include <errno.h>
43#include <fcntl.h>
44#include <string.h>
45#include <stdlib.h>
46#include <pthread.h>
47
48#include "misc.h"
49#include "pcscd.h"
50#include "debuglog.h"
51#include "readerfactory.h"
52#include "eventhandler.h"
53#include "dyn_generic.h"
54#include "sys_generic.h"
55#include "ifdwrapper.h"
56#include "prothandler.h"
57#include "utils.h"
58#include "winscard_svc.h"
59#include "simclist.h"
60
64static void * EHStatusHandlerThread(READER_CONTEXT *);
65
66LONG EHRegisterClientForEvent(int32_t filedes)
67{
68 (void)pthread_mutex_lock(&ClientsWaitingForEvent_lock);
69
70 (void)list_append(&ClientsWaitingForEvent, &filedes);
71
72 (void)MSGSendReaderStates(filedes);
73
74 (void)pthread_mutex_unlock(&ClientsWaitingForEvent_lock);
75
76 return SCARD_S_SUCCESS;
77} /* EHRegisterClientForEvent */
78
84{
85 LONG rv = SCARD_S_SUCCESS;
86 int ret;
87
88 (void)pthread_mutex_lock(&ClientsWaitingForEvent_lock);
89
90 ret = list_delete(&ClientsWaitingForEvent, &filedes);
91
92 (void)pthread_mutex_unlock(&ClientsWaitingForEvent_lock);
93
94 if (ret < 0)
96
97 return rv;
98} /* EHTryToUnregisterClientForEvent */
99
103LONG EHUnregisterClientForEvent(int32_t filedes)
104{
105 LONG rv = EHTryToUnregisterClientForEvent(filedes);
106
107 if (rv != SCARD_S_SUCCESS)
108 Log2(PCSC_LOG_ERROR, "Can't remove client: %d", filedes);
109
110 return rv;
111} /* EHUnregisterClientForEvent */
112
117{
118 int32_t filedes;
119
120 (void)pthread_mutex_lock(&ClientsWaitingForEvent_lock);
121
122 (void)list_iterator_start(&ClientsWaitingForEvent);
123 while (list_iterator_hasnext(&ClientsWaitingForEvent))
124 {
125 filedes = *(int32_t *)list_iterator_next(&ClientsWaitingForEvent);
126 MSGSignalClient(filedes, SCARD_S_SUCCESS);
127 }
128 (void)list_iterator_stop(&ClientsWaitingForEvent);
129
130 (void)list_clear(&ClientsWaitingForEvent);
131
132 (void)pthread_mutex_unlock(&ClientsWaitingForEvent_lock);
133} /* EHSignalEventToClients */
134
135LONG EHInitializeEventStructures(void)
136{
137 (void)list_init(&ClientsWaitingForEvent);
138
139 /* request to store copies, and provide the metric function */
140 (void)list_attributes_copy(&ClientsWaitingForEvent, list_meter_int32_t, 1);
141
142 /* setting the comparator, so the list can sort, find the min, max etc */
143 (void)list_attributes_comparator(&ClientsWaitingForEvent, list_comparator_int32_t);
144
145 (void)pthread_mutex_init(&ClientsWaitingForEvent_lock, NULL);
146
147 return SCARD_S_SUCCESS;
148}
149
150LONG EHDeinitializeEventStructures(void)
151{
152 list_destroy(&ClientsWaitingForEvent);
153 pthread_mutex_destroy(&ClientsWaitingForEvent_lock);
154
155 return SCARD_S_SUCCESS;
156}
157
158void EHDestroyEventHandler(READER_CONTEXT * rContext)
159{
160 int rv;
161 DWORD dwGetSize;
162 UCHAR ucGetData[1];
163
164 if ('\0' == rContext->readerState->readerName[0])
165 {
166 Log1(PCSC_LOG_INFO, "Thread already stomped.");
167 return;
168 }
169
170 /*
171 * Set the thread to 0 to exit thread
172 */
173 rContext->hLockId = 0xFFFF;
174
175 Log1(PCSC_LOG_INFO, "Stomping thread.");
176
177 /* kill the "polling" thread */
178 dwGetSize = sizeof(ucGetData);
180 &dwGetSize, ucGetData);
181
182 if ((IFD_SUCCESS == rv) && (1 == dwGetSize) && ucGetData[0])
183 {
184 Log1(PCSC_LOG_INFO, "Killing polling thread");
185 (void)pthread_cancel(rContext->pthThread);
186 }
187 else
188 {
189 /* ask to stop the "polling" thread */
190 RESPONSECODE (*fct)(DWORD) = NULL;
191
192 dwGetSize = sizeof(fct);
194 &dwGetSize, (PUCHAR)&fct);
195
196 if ((IFD_SUCCESS == rv) && (dwGetSize == sizeof(fct)))
197 {
198 Log1(PCSC_LOG_INFO, "Request stopping of polling thread");
199 fct(rContext->slot);
200 }
201 else
202 Log1(PCSC_LOG_INFO, "Waiting polling thread");
203 }
204
205 /* wait for the thread to finish */
206 rv = pthread_join(rContext->pthThread, NULL);
207 if (rv)
208 Log2(PCSC_LOG_ERROR, "pthread_join failed: %s", strerror(rv));
209
210 /* Zero the thread */
211 rContext->pthThread = 0;
212
213 Log1(PCSC_LOG_INFO, "Thread stomped.");
214
215 return;
216}
217
218LONG EHSpawnEventHandler(READER_CONTEXT * rContext)
219{
220 LONG rv;
221 DWORD dwStatus = 0;
222
223 rv = IFDStatusICC(rContext, &dwStatus);
224 if (rv != SCARD_S_SUCCESS)
225 {
226 Log2(PCSC_LOG_ERROR, "Initial Check Failed on %s",
227 rContext->readerState->readerName);
229 }
230
231 rv = ThreadCreate(&rContext->pthThread, 0,
232 (PCSCLITE_THREAD_FUNCTION( ))EHStatusHandlerThread, (LPVOID) rContext);
233 if (rv)
234 {
235 Log2(PCSC_LOG_ERROR, "ThreadCreate failed: %s", strerror(rv));
236 return SCARD_E_NO_MEMORY;
237 }
238 else
239 return SCARD_S_SUCCESS;
240}
241
242static void * EHStatusHandlerThread(READER_CONTEXT * rContext)
243{
244 LONG rv;
245#ifndef NO_LOG
246 const char *readerName;
247#endif
248 DWORD dwStatus;
249 uint32_t readerState;
250 int32_t readerSharing;
251 DWORD dwCurrentState;
252#ifndef DISABLE_AUTO_POWER_ON
253 DWORD dwAtrLen;
254#endif
255
256 /*
257 * Zero out everything
258 */
259 dwStatus = 0;
260
261#ifndef NO_LOG
262 readerName = rContext->readerState->readerName;
263#endif
264
265 rv = IFDStatusICC(rContext, &dwStatus);
266
267 if ((SCARD_S_SUCCESS == rv) && (dwStatus & SCARD_PRESENT))
268 {
269#ifdef DISABLE_AUTO_POWER_ON
270 rContext->readerState->cardAtrLength = 0;
272 readerState = SCARD_PRESENT;
273 Log1(PCSC_LOG_INFO, "Skip card power on");
274#else
275 dwAtrLen = sizeof(rContext->readerState->cardAtr);
276 rv = IFDPowerICC(rContext, IFD_POWER_UP,
277 rContext->readerState->cardAtr, &dwAtrLen);
278 rContext->readerState->cardAtrLength = dwAtrLen;
279
280 /* the protocol is unset after a power on */
282
283 if (rv == IFD_SUCCESS)
284 {
286 RFSetPowerState(rContext, POWER_STATE_POWERED);
287 Log1(PCSC_LOG_DEBUG, "powerState: POWER_STATE_POWERED");
288
289 if (rContext->readerState->cardAtrLength > 0)
290 {
291 LogXxd(PCSC_LOG_INFO, "Card ATR: ",
292 rContext->readerState->cardAtr,
293 rContext->readerState->cardAtrLength);
294 }
295 else
296 Log1(PCSC_LOG_INFO, "Card ATR: (NULL)");
297 }
298 else
299 {
300 readerState = SCARD_PRESENT | SCARD_SWALLOWED;
301 RFSetPowerState(rContext, POWER_STATE_UNPOWERED);
302 Log1(PCSC_LOG_DEBUG, "powerState: POWER_STATE_UNPOWERED");
303 Log2(PCSC_LOG_ERROR, "Error powering up card: %s", rv2text(rv));
304 }
305#endif
306
307 dwCurrentState = SCARD_PRESENT;
308 }
309 else
310 {
311 readerState = SCARD_ABSENT;
312 rContext->readerState->cardAtrLength = 0;
314
315 dwCurrentState = SCARD_ABSENT;
316 }
317
318 /*
319 * Set all the public attributes to this reader
320 */
321 rContext->readerState->readerState = readerState;
322 rContext->readerState->readerSharing = readerSharing = rContext->contexts;
323
325
326 while (1)
327 {
328 dwStatus = 0;
329
330 rv = IFDStatusICC(rContext, &dwStatus);
331
332 if (rv != SCARD_S_SUCCESS)
333 {
334 Log2(PCSC_LOG_ERROR, "Error communicating to: %s", readerName);
335
336 /*
337 * Set error status on this reader while errors occur
338 */
340 rContext->readerState->cardAtrLength = 0;
342
343 dwCurrentState = SCARD_UNKNOWN;
344
346 }
347
348 if (dwStatus & SCARD_ABSENT)
349 {
350 if (dwCurrentState == SCARD_PRESENT ||
351 dwCurrentState == SCARD_UNKNOWN)
352 {
353 /*
354 * Change the status structure
355 */
356 Log2(PCSC_LOG_INFO, "Card Removed From %s", readerName);
357 /*
358 * Notify the card has been removed
359 */
360 (void)RFSetReaderEventState(rContext, SCARD_REMOVED);
361
362 rContext->readerState->cardAtrLength = 0;
365 dwCurrentState = SCARD_ABSENT;
366
367 rContext->readerState->eventCounter++;
368 if (rContext->readerState->eventCounter > 0xFFFF)
369 rContext->readerState->eventCounter = 0;
370
372 }
373
374 }
375 else if (dwStatus & SCARD_PRESENT)
376 {
377 if (dwCurrentState == SCARD_ABSENT ||
378 dwCurrentState == SCARD_UNKNOWN)
379 {
380#ifdef DISABLE_AUTO_POWER_ON
381 rContext->readerState->cardAtrLength = 0;
384 RFSetPowerState(rContext, POWER_STATE_UNPOWERED);
385 Log1(PCSC_LOG_DEBUG, "powerState: POWER_STATE_UNPOWERED");
386 rv = IFD_SUCCESS;
387 Log1(PCSC_LOG_INFO, "Skip card power on");
388#else
389 /*
390 * Power and reset the card
391 */
392 dwAtrLen = sizeof(rContext->readerState->cardAtr);
393 rv = IFDPowerICC(rContext, IFD_POWER_UP,
394 rContext->readerState->cardAtr, &dwAtrLen);
395 rContext->readerState->cardAtrLength = dwAtrLen;
396
397 /* the protocol is unset after a power on */
399
400 if (rv == IFD_SUCCESS)
401 {
403 RFSetPowerState(rContext, POWER_STATE_POWERED);
404 Log1(PCSC_LOG_DEBUG, "powerState: POWER_STATE_POWERED");
405 }
406 else
407 {
409 RFSetPowerState(rContext, POWER_STATE_UNPOWERED);
410 Log1(PCSC_LOG_DEBUG, "powerState: POWER_STATE_UNPOWERED");
411 rContext->readerState->cardAtrLength = 0;
412 }
413#endif
414
415 dwCurrentState = SCARD_PRESENT;
416
417 rContext->readerState->eventCounter++;
418 if (rContext->readerState->eventCounter > 0xFFFF)
419 rContext->readerState->eventCounter = 0;
420
421 Log2(PCSC_LOG_INFO, "Card inserted into %s", readerName);
422
424
425 if (rv == IFD_SUCCESS)
426 {
427 if (rContext->readerState->cardAtrLength > 0)
428 {
429 LogXxd(PCSC_LOG_INFO, "Card ATR: ",
430 rContext->readerState->cardAtr,
431 rContext->readerState->cardAtrLength);
432 }
433 else
434 Log1(PCSC_LOG_INFO, "Card ATR: (NULL)");
435 }
436 else
437 Log1(PCSC_LOG_ERROR,"Error powering up card.");
438 }
439 }
440
441 /*
442 * Sharing may change w/o an event pass it on
443 */
444 if (readerSharing != rContext->contexts)
445 {
446 readerSharing = rContext->contexts;
447 rContext->readerState->readerSharing = readerSharing;
449 }
450
451 if (rContext->pthCardEvent)
452 {
453 int ret;
454 int timeout;
455
456#ifndef DISABLE_ON_DEMAND_POWER_ON
457 if (POWER_STATE_POWERED == RFGetPowerState(rContext))
458 /* The card is powered but not yet used */
459 timeout = PCSCLITE_POWER_OFF_GRACE_PERIOD;
460 else
461 /* The card is already in use or not used at all */
462#endif
463 timeout = PCSCLITE_STATUS_EVENT_TIMEOUT;
464
465 ret = rContext->pthCardEvent(rContext->slot, timeout);
466 if (IFD_SUCCESS != ret)
467 (void)SYS_USleep(PCSCLITE_STATUS_POLL_RATE);
468 }
469 else
470 (void)SYS_USleep(PCSCLITE_STATUS_POLL_RATE);
471
472#ifndef DISABLE_ON_DEMAND_POWER_ON
473 /* the card is powered but not used */
474 (void)pthread_mutex_lock(&rContext->powerState_lock);
475 if (POWER_STATE_POWERED == rContext->powerState)
476 {
477 /* power down */
478 IFDPowerICC(rContext, IFD_POWER_DOWN, NULL, NULL);
479 rContext->powerState = POWER_STATE_UNPOWERED;
480 Log1(PCSC_LOG_DEBUG, "powerState: POWER_STATE_UNPOWERED");
481
482 /* the protocol is unset after a power down */
484 }
485
486 /* the card was in use */
487 if (POWER_STATE_GRACE_PERIOD == rContext->powerState)
488 {
489 /* the next state should be UNPOWERED unless the
490 * card is used again */
491 rContext->powerState = POWER_STATE_POWERED;
492 Log1(PCSC_LOG_DEBUG, "powerState: POWER_STATE_POWERED");
493 }
494 (void)pthread_mutex_unlock(&rContext->powerState_lock);
495#endif
496
497 if (rContext->hLockId == 0xFFFF)
498 {
499 /*
500 * Exit and notify the caller
501 */
502 Log1(PCSC_LOG_INFO, "Die");
503 rContext->hLockId = 0;
504 (void)pthread_exit(NULL);
505 }
506 }
507}
508
This handles debugging.
This abstracts dynamic library loading functions.
static list_t ClientsWaitingForEvent
list of client file descriptors
LONG EHTryToUnregisterClientForEvent(int32_t filedes)
Try to unregister a client If no client is found then do not log an error.
void EHSignalEventToClients(void)
Sends an asynchronous event to any waiting client.
pthread_mutex_t ClientsWaitingForEvent_lock
lock for the above list
LONG EHUnregisterClientForEvent(int32_t filedes)
Unregister a client and log an error if the client is not found.
This handles card insertion/removal events, updates ATR, protocol, and status information.
#define SCARD_F_INTERNAL_ERROR
An internal consistency check failed.
Definition pcsclite.h:109
#define SCARD_F_UNKNOWN_ERROR
An internal error has been detected, but the source is unknown.
Definition pcsclite.h:147
#define SCARD_S_SUCCESS
No error was encountered.
Definition pcsclite.h:107
#define SCARD_E_NO_MEMORY
Not enough memory available to complete this command.
Definition pcsclite.h:119
#define IFD_POWER_UP
power up the card
Definition ifdhandler.h:343
#define TAG_IFD_STOP_POLLING_THREAD
method used to stop the polling thread (instead of just pthread_kill())
Definition ifdhandler.h:329
#define IFD_POWER_DOWN
power down the card
Definition ifdhandler.h:344
#define TAG_IFD_POLLING_THREAD_KILLABLE
the polling thread can be killed
Definition ifdhandler.h:328
#define IFD_SUCCESS
no error
Definition ifdhandler.h:351
RESPONSECODE IFDGetCapabilities(READER_CONTEXT *rContext, DWORD dwTag, PDWORD pdwLength, PUCHAR pucValue)
Gets capabilities in the reader.
Definition ifdwrapper.c:240
LONG IFDStatusICC(READER_CONTEXT *rContext, PDWORD pdwStatus)
Provide statistical information about the IFD and ICC including insertions, atr, powering status/etc.
Definition ifdwrapper.c:339
RESPONSECODE IFDPowerICC(READER_CONTEXT *rContext, DWORD dwAction, PUCHAR pucAtr, PDWORD pdwAtrLen)
Power up/down or reset's an ICC located in the IFD.
Definition ifdwrapper.c:270
This wraps the dynamic ifdhandler functions.
#define SCARD_SWALLOWED
Card not powered.
Definition pcsclite.h:261
#define SCARD_PROTOCOL_UNDEFINED
protocol not set
Definition pcsclite.h:240
#define SCARD_PRESENT
Card is present.
Definition pcsclite.h:260
#define SCARD_POWERED
Card is powered.
Definition pcsclite.h:262
#define SCARD_ABSENT
Card is absent.
Definition pcsclite.h:259
#define SCARD_UNKNOWN
Unknown state.
Definition pcsclite.h:258
#define SCARD_NEGOTIABLE
Ready for PTS.
Definition pcsclite.h:263
This handles protocol defaults, PTS, etc.
int RFGetPowerState(READER_CONTEXT *rContext)
Wait until all connected readers have a chance to power up a possibly inserted card.
This keeps track of a list of currently available reader structures.
RESPONSECODE(* pthCardEvent)(DWORD, int)
Card Event sync.
pthread_mutex_t powerState_lock
powerState mutex
pthread_t pthThread
Event polling thread.
_Atomic int32_t contexts
Number of open contexts.
int slot
Current Reader Slot.
_Atomic SCARDHANDLE hLockId
Lock Id.
struct pubReaderStatesList * readerState
link to the reader state
int powerState
auto power off state
list object
Definition simclist.h:181
_Atomic int32_t readerSharing
PCSCLITE_SHARING_* sharing status.
char readerName[MAX_READERNAME]
reader name
uint32_t cardProtocol
SCARD_PROTOCOL_* value.
UCHAR cardAtr[MAX_ATR_SIZE]
ATR.
uint32_t eventCounter
number of card events
_Atomic uint32_t cardAtrLength
ATR length.
uint32_t readerState
SCARD_* bit field.
This handles abstract system level calls.
int SYS_USleep(int)
Makes the current process sleep for some microseconds.
Definition sys_unix.c:80
This demarshalls functions over the message queue and keeps track of clients and their handles.