Hi,
I wrote a small program to test the card insertion and removal events. It is linked into libpcsclite.so. I discovered a SCardCancel() hang problem in libpcsclite when running this program. Attached are simple programs to reproduce this on Redhat Linux with smartcard daemon pcscd running.
gcc -o scmon scmon_main.c smartcardmonitor.c -I /usr/local/include/PCSC -L/usr/local/lib -lpcsclite
It requires PC/SC header files which on my system were installed under /usr/local/include/PCSC. It also requires the libpcsclite.so. It was installed under /usr/local/lib on my system.
After compiling, start the pcscd daemon in the background.
Then run the scmon as regular user. You will see it monitors the smartcard insertion and removal events and print out the info. It actually starts a seperate thread which listens on the smartcard event while the main thread is in a while(1) loop. It also installs a signal handler. So when you hit ctrl c on keyboard, the main thread will call killCardEventMonitor() to kill the event monitor thread and then exit.
The problem is when I hit ctrl c, the program hangs. Further investigation shows the siganl handler calls killCardEventMonitor(), which in turns call SCardCancel() into libpcsclite.so. It just hangs in SCardCancel() and never returns back.
I am using the pcsc-lite 1.3.0 and wondering if anyone has reported this before or already has a fix.
Thanks in advance for your help,
Wei
#include <stdio.h> #include <signal.h> #include <stdlib.h> #include "smartcardmonitor.h"
typedef struct mond_data {
int eventNumber;
} mond_data_t;
static mond_data_t mdp;
void card_event_cb(const char *readerName, int cardEvent,
int numberOfReaders, void *appDatap)
{
mond_data_t *a;
a = (mond_data_t *)appDatap;
fprintf(stderr, "Card event happened, number of readers is %d,"
" eventNumber = %d\n", numberOfReaders, a->eventNumber);
a->eventNumber++;
switch (cardEvent) {
case SMARTCARD_INSERTION_EVENT:
fprintf(stderr, "Card inserted at reader %s\n", readerName);
break;
case SMARTCARD_REMOVAL_EVENT:
fprintf(stderr, "Card removed at reader %s\n", readerName);
break;
default:
fprintf(stderr, "Unknown event %d at reader %s\n",
cardEvent, readerName);
break;
}
}
void
sighdl_sigterm(int sig)
{
fprintf(stderr, "Recieve termination signal, exiting\n");
killCardEventMonitor();
exit(0);
}
int
main()
{
mdp.eventNumber = 0;
/* Install singal handler for SIGTERM */
signal(SIGTERM, &sighdl_sigterm);
signal(SIGINT, &sighdl_sigterm);
setCardEventCallback((CARDEVENTCALLBACK)&card_event_cb);
if (startCardEventMonitor((void *)&mdp) != 1) {
fprintf(stderr, "Cannot start event monitor\n");
exit(-1);
}
/* Endless loop waiting for card event */
while(1) ;
}
#include "smartcardmonitor.h"
#include <winscard.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
static pthread_t g_ThreadHandle = (pthread_t) 0;
static int g_fExitThread = 0;
static SCARDCONTEXT g_hContext = 0;
static CARDEVENTCALLBACK pCardEventCallback = NULL;
void setCardEventCallback( CARDEVENTCALLBACK pCallback)
{
if( pCallback )
{
pCardEventCallback = pCallback;
}
}
int
GetReaderListInfo (char **pReaderList,
unsigned long *pReaderListSize,
int *pReaderCount,
SCARD_READERSTATE **pStates)
{
char *szReaderList = NULL;
unsigned long ReaderListSize = 0;
int iReaderCount = 0;
SCARD_READERSTATE *ReaderStates = NULL;
long lRetVal = 0;
char *TempReaderList = NULL;
int i;
if (g_hContext == 0)
return -1;
/* waiting for reader availability */
lRetVal = SCardGetStatusChange (g_hContext, INFINITE , NULL, 0);
if ( lRetVal != SCARD_S_SUCCESS )
return -1;
/* Timed out */
if (g_fExitThread != 0)
{
/* Don't worry, we are exiting in the caller */
return -2;
}
/* We have reader. Get reader list size now. */
ReaderListSize = 0;
lRetVal = SCardListReaders (g_hContext, NULL, NULL, &ReaderListSize);
if(lRetVal != SCARD_S_SUCCESS)
return -1;
szReaderList = malloc (ReaderListSize * sizeof (char));
memset (szReaderList, 0, (ReaderListSize * sizeof (char)));
lRetVal = SCardListReaders (g_hContext, NULL, szReaderList, &ReaderListSize);
if (lRetVal != SCARD_S_SUCCESS)
{
/* hmm. readers list has changed too fast ?! */
free (szReaderList);
return -1;
}
TempReaderList = szReaderList;
iReaderCount = 0;
while (TempReaderList[0] != '\0')
{
iReaderCount++;
TempReaderList += strlen (TempReaderList) + 1;
}
ReaderStates = (SCARD_READERSTATE *) malloc (sizeof (SCARD_READERSTATE) *
iReaderCount);
memset (ReaderStates, 0, sizeof (SCARD_READERSTATE) * iReaderCount);
TempReaderList = szReaderList;
for (i = 0;
i < iReaderCount;
i++, TempReaderList += strlen (TempReaderList) + 1)
{
ReaderStates[i].szReader = TempReaderList;
ReaderStates[i].dwCurrentState = SCARD_STATE_UNAWARE;
ReaderStates[i].dwEventState = SCARD_STATE_UNAWARE;
}
*pReaderList = szReaderList;
*pReaderListSize = ReaderListSize;
*pReaderCount = iReaderCount;
*pStates = ReaderStates;
return 0;
}
int
CompareReaderList ( char *szReaderList1,
int nLength1,
char *szReaderList2,
int nLength2 )
{
if(nLength1 == 0 || nLength2 == 0 || nLength1 != nLength2 )
return -1;
if (szReaderList1 == NULL && szReaderList2 == NULL)
return 0;
if (szReaderList1 != NULL && szReaderList2 == NULL)
return 1;
if (szReaderList1 == NULL && szReaderList2 != NULL)
return -1;
return memcmp( szReaderList1, szReaderList2, nLength1 );
}
int
UpdateReaderStates (SCARD_READERSTATE *pReaderStates,
int ReaderCount,
SCARD_READERSTATE *pCurrentReaderStates,
int CurrentReaderCount)
{
int i = 0;
int j = 0;
if (pReaderStates == NULL || pCurrentReaderStates == NULL)
return -1;
for (i = 0; i < ReaderCount; i++)
{
for (j = 0; j < CurrentReaderCount; j++)
{
if (strcmp (pReaderStates[i].szReader, pCurrentReaderStates[j].szReader) == 0)
{
pReaderStates[i].dwCurrentState = pCurrentReaderStates[j].dwCurrentState;
pReaderStates[i].dwEventState = pCurrentReaderStates[j].dwEventState;
break;
}
}
}
return 0;
}
/** Check if the event state of the given reader has changed
* and has the given flag set.
*
* @param reader the reader whose event state is to be checked. Must
* not be NULL.
*
* @param flag the state flag to be checked. Must be one of
* SCARD_STATE_EMPTY, SCARD_STATE_PRESENT, SCARD_STATE_UNKNOWN, etc.
*
* @return 0 if the event state of the reader does not have
* (flag & SCARD_STATE_CHANGED) set; !0 otherwise.
*/
static int state_changed(const SCARD_READERSTATE *reader, DWORD flag)
{
return (reader->dwEventState & (flag | SCARD_STATE_CHANGED))
== (flag | SCARD_STATE_CHANGED);
}
void *
EventMonitorThread (LPVOID lpParameter)
{
char *szMasterReaderList = NULL;
unsigned long MasterReaderListSize = 0;
int iMasterReaderCount = 0;
SCARD_READERSTATE *pMasterStates = NULL;
char *szReaderList = NULL;
unsigned long ReaderListSize = 0;
int iReaderCount = 0;
SCARD_READERSTATE *pStates = NULL;
int i;
long lRetVal;
int ExitVal = 0;
g_fExitThread = 0;
lRetVal = SCardEstablishContext (SCARD_SCOPE_USER, NULL, NULL, &g_hContext);
if (lRetVal != SCARD_S_SUCCESS) {
g_fExitThread = 1;
pthread_exit ((void *) 1);
}
if (GetReaderListInfo (&szMasterReaderList,
&MasterReaderListSize,
&iMasterReaderCount,
&pMasterStates) != 0)
{
SCardReleaseContext (g_hContext);
pthread_exit ((void *) 2);
}
while (g_fExitThread == 0)
{
lRetVal = SCardGetStatusChange (g_hContext, INFINITE ,
pMasterStates, iMasterReaderCount);
if (lRetVal == SCARD_E_INVALID_VALUE ||
lRetVal == SCARD_E_INVALID_HANDLE ||
lRetVal == SCARD_E_READER_UNAVAILABLE)
{
ExitVal = 4;
break;
}
if (lRetVal == SCARD_S_SUCCESS)
{
for (i = 0; i < iMasterReaderCount; i++)
{
SCARD_READERSTATE *reader = &pMasterStates[i];
/* Check if state has changed to 'card present' */
if (state_changed(reader, SCARD_STATE_PRESENT))
{
if (pCardEventCallback)
(pCardEventCallback) (pMasterStates[i].szReader,
SMARTCARD_INSERTION_EVENT,
iMasterReaderCount,
lpParameter);
}
/* Check if state has changed to 'card unavailable'. This will
occur if the card is removed or the reader is detached (in
which case it becomes 'unknown' to PC/SC) */
else if (state_changed(reader, SCARD_STATE_EMPTY) ||
state_changed(reader, SCARD_STATE_UNKNOWN))
{
if (pCardEventCallback)
(pCardEventCallback) (pMasterStates[i].szReader,
SMARTCARD_REMOVAL_EVENT,
iMasterReaderCount,
lpParameter);
}
/* Update the current state of the reader */
reader->dwCurrentState = reader->dwEventState;
}
}
if (GetReaderListInfo (&szReaderList,
&ReaderListSize,
&iReaderCount,
&pStates) != 0)
{
ExitVal = 2;
break;
}
if (CompareReaderList (szMasterReaderList,MasterReaderListSize , szReaderList,ReaderListSize) == 0)
{
free (pStates);
free (szReaderList);
}
else
{
UpdateReaderStates (pStates, iReaderCount, pMasterStates, iMasterReaderCount);
free (pMasterStates);
free (szMasterReaderList);
szMasterReaderList = szReaderList;
MasterReaderListSize = ReaderListSize;
iMasterReaderCount = iReaderCount;
pMasterStates = pStates;
}
szReaderList = NULL;
ReaderListSize = 0;
iReaderCount = 0;
pStates = NULL;
}
SCardReleaseContext (g_hContext);
g_fExitThread = 1;
if (pMasterStates != NULL)
free (pMasterStates);
if (szMasterReaderList != NULL)
free (szMasterReaderList);
pthread_exit ((void *) ExitVal);
return (NULL);
}
void killCardEventMonitor( )
{
if((pthread_t) 0 != g_ThreadHandle)
{
g_fExitThread = 1;
/* Cancel the SCardGetStatusChange INFINITE loop wait*/
#if 1
SCardCancel( g_hContext );
#endif
#if 1
pthread_cancel(g_ThreadHandle );
pthread_join( g_ThreadHandle, NULL);
#endif
#if 1
SCardReleaseContext(g_hContext);
#endif
}
}
int startCardEventMonitor( void *pVoid )
{
int nRet = 0;
static int thread_running = 0;
int thread_return = -1;
pthread_attr_t attr;
if(!thread_running)
{
pthread_attr_init(&attr);
thread_return = pthread_create( &g_ThreadHandle, &attr, EventMonitorThread, pVoid );
if(0 == thread_return)
nRet = 1;
else
nRet = -1;
}
else
{
nRet = 1;
}
return nRet;
}
int isThreadRunning(void)
{
return ((g_fExitThread == 0) ? 1 : 0);
}
#ifndef __SMARTCARDMONITOR_H #define __SMARTCARDMONITOR_H #define SMARTCARD_INSERTION_EVENT 1 #define SMARTCARD_REMOVAL_EVENT 2 typedef void (*CARDEVENTCALLBACK)(const char *, /*readerName*/ int, /*Card insertion/removal Event*/ int, /*Number of Readers in System*/ void * ); /* App specific data*/ int startCardEventMonitor( void *pVoid ); void killCardEventMonitor( void ); void setCardEventCallback( CARDEVENTCALLBACK ); int isThreadRunning(void); #endif /*#ifndef __SMARTCARDMONITOR_H*/
_______________________________________________ Muscle mailing list [email protected] http://lists.drizzle.com/mailman/listinfo/muscle
