opennx/LibOpenSC.cpp
2025-08-08 20:34:09 +02:00

388 lines
12 KiB
C++

// $Id: LibOpenSC.cpp 451 2010-01-27 12:24:56Z felfert $
//
// Copyright (C) 2006 The OpenNX Team
// Author: Fritz Elfert
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU Library General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this program; if not, write to the
// Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#if defined(__GNUG__) && !defined(__APPLE__)
#pragma implementation "LibOpenSC.h"
#endif
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include <wx/event.h>
#include <wx/thread.h>
#include <wx/process.h>
#include <wx/config.h>
#include "MyDynlib.h"
#include "LibOpenSC.h"
#ifdef APP_OPENNX
# include "opennxApp.h"
# include "CardWaiterDialog.h"
#endif
#ifdef APP_WATCHREADER
# include "watchReaderApp.h"
#endif
#include "trace.h"
ENABLE_TRACE;
#ifdef __WXMAC__
// Current SCA implementation on MacOSX does not
// allow multiple applications. So we use the same
// context like nxssh.
# define OPENSC_CTXNAME "openssh"
#else
# define OPENSC_CTXNAME "opennx"
#endif
#include <opensc/opensc.h>
typedef int (*Tsc_establish_context)(sc_context_t **ctx, const char *app_name);
typedef int (*Tsc_release_context)(sc_context_t *ctx);
typedef int (*Tsc_detect_card_presence)(sc_reader_t *reader, int slot_id);
#ifdef APP_OPENNX
DEFINE_LOCAL_EVENT_TYPE(wxEVT_CARDINSERTED);
class CardWaitThread : public wxThreadHelper
{
public:
CardWaitThread(wxEvtHandler *handler = NULL);
virtual ~CardWaitThread();
virtual wxThread::ExitCode Entry();
bool IsOk() { return m_bOk; }
int GetReader() { return m_iFoundID; }
void SetHandler(wxEvtHandler *handler) { m_pEvtHandler = handler; }
private:
wxEvtHandler *m_pEvtHandler;
bool m_bOk;
bool m_bTerminate;
bool m_bFirstLoopDone;
int m_iFoundID;
};
CardWaitThread::CardWaitThread(wxEvtHandler *handler)
: wxThreadHelper()
, m_pEvtHandler(handler)
, m_bOk(false)
, m_bTerminate(false)
, m_bFirstLoopDone(false)
, m_iFoundID(-1)
{
if (CreateThread(
#ifdef __OPENBSD__
32768
#endif
) == wxTHREAD_NO_ERROR) {
GetThread()->Run();
while ((!m_bFirstLoopDone) && GetThread()->IsRunning())
wxThread::Sleep(100);
myLogTrace(MYTRACETAG, wxT("opensc API is %savailable"), m_bOk ? wxT("") : wxT("un"));
if (m_bOk)
myLogTrace(MYTRACETAG, wxT("reader-ID: %d"), (int)m_iFoundID);
} else
myLogTrace(MYTRACETAG, wxT("could not create waiter thread"));
}
CardWaitThread::~CardWaitThread()
{
m_pEvtHandler = NULL;
if (m_bOk) {
m_bTerminate = true;
GetThread()->Delete();
while (m_bOk)
wxThread::Sleep(100);
}
}
wxThread::ExitCode
CardWaitThread::Entry()
{
#ifdef __WXMAC__
m_bOk = true;
wxString cmd;
wxConfigBase::Get()->Read(wxT("Config/SystemNxDir"), &cmd);
cmd << wxFileName::GetPathSeparator() << wxT("bin")
<< wxFileName::GetPathSeparator() << wxT("macfindreader");
while (!m_thread->TestDestroy()) {
int res = system(cmd.mb_str());
if ((res >= 0) && (res < 255)) {
if (res != m_iFoundID) {
m_iFoundID = res;
if (m_pEvtHandler) {
wxCommandEvent ev(wxEVT_CARDINSERTED, wxID_ANY);
ev.SetInt(m_iFoundID);
m_pEvtHandler->AddPendingEvent(ev);
}
}
}
if (m_bFirstLoopDone)
wxThread::Sleep(500);
m_bFirstLoopDone = true;
}
#else
sc_context *ctx;
MyDynamicLibrary dll;
{
wxLogNull ignoreErrors;
if (!dll.Load(wxT("libopensc")))
return 0;
}
wxDYNLIB_FUNCTION(Tsc_establish_context, sc_establish_context, dll);
if (!pfnsc_establish_context)
return 0;
if (SC_SUCCESS != pfnsc_establish_context(&ctx, OPENSC_CTXNAME))
return 0;
wxDYNLIB_FUNCTION(Tsc_release_context, sc_release_context, dll);
if (!pfnsc_release_context)
return 0;
wxDYNLIB_FUNCTION(Tsc_detect_card_presence, sc_detect_card_presence, dll);
if (!pfnsc_detect_card_presence)
return 0;
m_bOk = true;
while (!m_thread->TestDestroy()) {
if (m_bTerminate)
break;
int found_id = -1;
int r, j;
unsigned int i;
unsigned int rc = ctx->reader_count;
if (rc > 0) {
unsigned int errc = 0;
for (i = 0; i < rc; i++) {
sc_reader_t *reader = ctx->reader[i];
if (!reader)
continue;
myLogTrace(MYTRACETAG, wxT("Trying reader %d"), i);
for (j = 0; j < reader->slot_count; j++) {
r = pfnsc_detect_card_presence(reader, j);
if (r > 0) {
found_id = i;
break;
}
if (r < 0) {
errc++;
myLogTrace(MYTRACETAG, wxT("error %d during sc_detect_card_presence"), r);
}
}
if (found_id != -1)
break;
}
if (errc >= rc) {
myLogTrace(MYTRACETAG, wxT("All readers returned an error"));
pfnsc_release_context(ctx);
m_bOk = false;
return 0;
}
} else
myLogTrace(MYTRACETAG, wxT("no readers found"));
if (found_id == -1)
myLogTrace(MYTRACETAG, wxT("no cards found"));
else {
myLogTrace(MYTRACETAG, wxT("found card in reader %d"), found_id);
if (found_id != m_iFoundID) {
m_iFoundID = found_id;
if (m_pEvtHandler) {
wxCommandEvent ev(wxEVT_CARDINSERTED, wxID_ANY);
ev.SetInt(m_iFoundID);
m_pEvtHandler->AddPendingEvent(ev);
}
}
}
if (m_bFirstLoopDone)
wxThread::Sleep(500);
m_bFirstLoopDone = true;
}
pfnsc_release_context(ctx);
#endif
m_bOk = false;
myLogTrace(MYTRACETAG, wxT("terminating waiter thread"));
return 0;
}
#endif // APP_OPENNX
LibOpenSC::LibOpenSC()
{
myLogTrace(MYTRACETAG, wxT("LibOpenSC()"));
}
LibOpenSC::~LibOpenSC()
{
myLogTrace(MYTRACETAG, wxT("~LibOpenSC()"));
}
bool LibOpenSC::HasOpenSC() {
wxLogNull ignoreErrors;
MyDynamicLibrary dll;
if (!dll.Load(wxT("libopensc")))
return false;
wxDYNLIB_FUNCTION(Tsc_establish_context, sc_establish_context, dll);
if (!pfnsc_establish_context)
return false;
wxDYNLIB_FUNCTION(Tsc_release_context, sc_release_context, dll);
if (!pfnsc_release_context)
return false;
wxDYNLIB_FUNCTION(Tsc_detect_card_presence, sc_detect_card_presence, dll);
if (!pfnsc_detect_card_presence)
return false;
return true;
}
#ifdef APP_OPENNX
int LibOpenSC::WaitForCard(CardWaiterDialog *d) {
CardWaitThread t(d);
if (t.IsOk() && (t.GetReader() != -1))
return t.GetReader();
d->ShowModal();
return d->GetReader();
}
#endif
bool LibOpenSC::WatchHotRemove(unsigned int ridx, long sshpid) {
MyDynamicLibrary dll;
myLogTrace(MYTRACETAG, wxT("WatchHotRemove loading OpenSC"));
{
wxLogNull ignoreErrors;
if (!dll.Load(wxT("libopensc")))
return false;
}
myLogTrace(MYTRACETAG, wxT("WatchHotRemove checking OpenSC functions"));
wxDYNLIB_FUNCTION(Tsc_establish_context, sc_establish_context, dll);
if (!pfnsc_establish_context)
return false;
wxDYNLIB_FUNCTION(Tsc_release_context, sc_release_context, dll);
if (!pfnsc_release_context)
return false;
wxDYNLIB_FUNCTION(Tsc_detect_card_presence, sc_detect_card_presence, dll);
if (!pfnsc_detect_card_presence)
return false;
sc_context *ctx = NULL;
myLogTrace(MYTRACETAG, wxT("WatchHotRemove waiting for card removal"));
myLogTrace(MYTRACETAG, wxT("WatchHotRemove trying to establish context"));
if (SC_SUCCESS == pfnsc_establish_context(&ctx, OPENSC_CTXNAME)) {
while (true) {
if (sshpid != 0) {
if (!wxProcess::Exists(sshpid)) {
myLogTrace(MYTRACETAG, wxT("nxssh pid %d has terminated"), (int)sshpid);
return false;
}
}
unsigned int rc = ctx->reader_count;
if (rc <= ridx) {
// reader is gone
myLogTrace(MYTRACETAG, wxT("reader is gone"));
break;
}
sc_reader_t *reader = ctx->reader[ridx];
if (reader) {
int r = 1;
int j;
for (j = 0; j < reader->slot_count; j++) {
myLogTrace(MYTRACETAG, wxT("WatchHotRemove checking for card in reader %d, slot %d"), ridx, j);
r = pfnsc_detect_card_presence(reader, j);
if (r == 0) {
// card is gone
myLogTrace(MYTRACETAG, wxT("card is gone"));
break;
}
if (r < 0) {
myLogTrace(MYTRACETAG, wxT("error %d during sc_detect_card_presence"), r);
break;
}
}
if (r <= 0)
break;
} else {
myLogTrace(MYTRACETAG, wxT("no readers found"));
break;
}
while (wxGetApp().Pending())
wxGetApp().Dispatch();
wxThread::Sleep(1000);
}
} else
myLogTrace(MYTRACETAG, wxT("could not establish context"));
if (ctx)
pfnsc_release_context(ctx);
if (sshpid != 0) {
myLogTrace(MYTRACETAG, wxT("Sending HUP to nxssh pid %d"), (int)sshpid);
int trycount = 10;
while (wxProcess::Exists(sshpid) && (0 < trycount)) {
wxProcess::Kill(sshpid, wxSIGHUP);
while (::wxGetApp().Pending())
::wxGetApp().Dispatch();
wxThread::Sleep(500);
trycount--;
}
if (!wxProcess::Exists(sshpid))
return true;
// Again, this time SIGTERM
myLogTrace(MYTRACETAG, wxT("Sending TERM to nxssh pid %d"), (int)sshpid);
trycount = 10;
while (wxProcess::Exists(sshpid) && (0 < trycount)) {
wxProcess::Kill(sshpid, wxSIGTERM);
while (::wxGetApp().Pending())
::wxGetApp().Dispatch();
wxThread::Sleep(500);
trycount--;
}
if (!wxProcess::Exists(sshpid))
return true;
// Finally, use brute force
myLogTrace(MYTRACETAG, wxT("Sending KILL to nxssh pid %d"), (int)sshpid);
trycount = 10;
while (wxProcess::Exists(sshpid) && (0 < trycount)) {
wxProcess::Kill(sshpid, wxSIGKILL);
while (::wxGetApp().Pending())
::wxGetApp().Dispatch();
wxThread::Sleep(500);
trycount--;
}
if (!wxProcess::Exists(sshpid))
return true;
wxLogError(_("Could not terminate nxssh"));
} else
return true;
return false;
}