PCSC那事儿(二十八--PCSCD)

 

Info.plist 这个文件以后再说。

 

所以呢, serial PCMCIA 类型的读卡器在 /etc/reader.conf 配置。

usb 类型的读卡器在 /usr/local/pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist 配置。

 

307~312 行,表示出现了不在上面提到的选项。则程序直接退出,而不是忽略。

313

314         /*

315          * test the presence of /var/run/pcscd/pcsc.pub

316           */

 

317

318         rv = SYS_Stat(PCSCLITE_PUBSHM_FILE, &fStatBuf);

319

 

317~319 行,这个好像在哪里见过,当然了前面讲解客户端的时候,说过了。

PCSCLITE_PUBSHM_FILE ,就是共享内存所在的文件路径。

PCSCLITE_PUBSHM_FILE 在前面说过了,是 /var/run/pcscd/pcscd.pub

315 行,实际上应该改成 *test the presence of /var/run/pcscd/pcscd.pub

检测该文件是否存在,存在则返回0 .

 

 

320         if (rv == 0)

321         {

322                 pid_t pid;

323

324                 /* read the pid file to get the old pid and test if the old pcscd is

325                  * still running

326                   */

327                 pid = GetDaemonPid();

328

329                 if (pid != -1)

330                 {

331                         if (HotPlug)

332                                 return SendHotplugSignal();

333

334                          if (kill(pid, 0) == 0)

335                         {

336                                 Log1(PCSC_LOG_CRITICAL,

337                                         "file " PCSCLITE_PUBSHM_FILE " already exists.");

338                                 Log2(PCSC_LOG_CRITICAL,

339                                         "Another pcscd (pid: %d) seems to be running.", pid);

340                                 return EXIT_FAILURE;

341                         }

342                         else

343                                  /* the old pcscd is dead. make some cleanup */

344                                 clean_temp_files();

345                 }

346                 else

 

320 行,如果共享内存文件存在,则开始进入判断体。

327 行, pid = GetDaemonPid();

GetDaemonPid 定义在 utils.c

实现如下:

  31 pid_t GetDaemonPid(void)

  32 {

  33         FILE *f;

  34         pid_t pid;

  35

  36         /* pids are only 15 bits but 4294967296

  37          * (32 bits in case of a new system use it) is on 10 bytes

  38          */

  39         if ((f = fopen(PCSCLITE_RUN_PID, "rb")) != NULL)

  40         {

  41                 char pid_ascii[PID_ASCII_SIZE];

  42

  43                 (void)fgets(pid_ascii, PID_ASCII_SIZE, f);

  44                 (void)fclose(f);

  45

  46                 pid = atoi(pid_ascii);

  47         }

  48         else

  49         {

  50                 Log2(PCSC_LOG_CRITICAL, "Can't open " PCSCLITE_RUN_PID ": %s",

  51                         strerror(errno));

  52                 return -1;

  53         }

  54

  55         return pid;

  56 } /* GetDaemonPid */

 

PCSCLITE_RUN_PID          /var/run/pcscd /pcscd.pid

首次检查这个文件是存在,存在则从中读取 pid 号。否则直接退出。

 

329 行,说明存 id 的文件存在。

331~332 行,说明如果 pcscd 传入的命令行选项有 -H --hotplug

SendHotplugSignal

SendHotplugSignal 定义在 utils.c

实现如下:

  58 int SendHotplugSignal(void)

  59 {

  60         pid_t pid;

  61

  62         pid = GetDaemonPid();

  63

  64         if (pid != -1)

  65         {

  66                 Log2(PCSC_LOG_INFO, "Send hotplug signal to pcscd (pid=%d)", pid);

  67                 if (kill(pid, SIGUSR1) < 0)

  68                 {

  69                         Log3(PCSC_LOG_CRITICAL, "Can't signal pcscd (pid=%d): %s",

  70                                 pid, strerror(errno));

  71                         return EXIT_FAILURE ;

  72                 }

  73                 (void)SYS_Sleep(1);

  74         }

  75

  76         return EXIT_SUCCESS;

  77 } /* SendHotplugSignal */

 

 

  原来是这样。从上面提到的那个文件中读取 pid, 然后真正检测该 pid 进程是否存在于系统

中。这个检测出现在 67 行。

if (kill(pid, SIGUSR1) < 0)

 

kill 做什么?找 man 看看

man

  On success (at least one signal was sent), zero is returned.  On error,

       -1 is returned, and errno is set appropriately.

也就是说如果 kill 返回值 <0 则该 pid 对应的进程不存在。

所以 SendHotplugSignal 真正检测 pcscd 这个进程是否存在。如果存在则程序且能响应信号直接返回,目的是为了重新 scan 所有的设备。

 

如果旧 pcscd 进程不存在呢?

那么意味着,每次键入如下命令新的 pcscd 进程都会直接退出。

# pcscd -H

都会出现执行如下片段,而退出。

 

348                         if (HotPlug)

349                         {

350                                  Log1(PCSC_LOG_CRITICAL, "file " PCSCLITE_RUN_PID " do not exist");

351                                 Log1(PCSC_LOG_CRITICAL, "Hotplug failed");

352                                 return EXIT_FAILURE;

353                         }

 

 

这是个用户定义的 Signal ,看看 SIGUSR1 挂接了什么例程。

527         (void)signal(SIGUSR1, signal_reload);

 

571 static void signal_reload(/*@unused@*/ int sig)

572 {

573         (void)sig;

574

575         if (AraKiri)

576                 return;

577

578         HPReCheckSerialReaders();

579 } /* signal_reload */

571~579 行,没有做什么大事,仅仅检测 serial 类型的读卡器。

 

总的来说

331                         if (HotPlug)

332                                 return SendHotplugSignal();

用来给原先存在的还正常运行的 pcscd 发信号,重新检测系统中的 serial 版本的读卡器 .

无论成功,失败,新的 pcscd 都不再运行。

 

 

 

 

334 行,又做什么?再次 kill.

334                         if (kill(pid, 0) == 0)

那是没有 HotPlug 时候的故事。

 

看看 man 如何解释。

  #include <sys/types.h>

  #include <signal.h>

  int kill(pid_t pid, int sig);

If sig is 0, then no signal is sent, but error checking is  still  per-

       formed.

不发送信号,仅仅检测原先的 pid 是否还存在于系统中。

如果原先的 pid 存在,则返回。不再启动新的 pid.

不存在则,

344                                 clean_temp_files();

 

 

clean_temp_files 定义在同一个文件 pcscdaemon.c

 

实现如下:

544 static void clean_temp_files(void)

545 {

546         int rv;

547

548         rv = SYS_RemoveFile(PCSCLITE_PUBSHM_FILE);

549         if (rv != 0)

550                 Log2(PCSC_LOG_ERROR, "Cannot remove " PCSCLITE_PUBSHM_FILE ": %s",

551                         strerror(errno));

552

553         rv = SYS_RemoveFile(PCSCLITE_CSOCK_NAME);

554         if (rv != 0)

555                 Log2(PCSC_LOG_ERROR, "Cannot remove " PCSCLITE_CSOCK_NAME ": %s",

556                         strerror(errno));

557

558         rv = SYS_RemoveFile(PCSCLITE_RUN_PID);

559         if (rv != 0)

560                 Log2(PCSC_LOG_ERROR, "Cannot remove " PCSCLITE_RUN_PID ": %s",

561                         strerror(errno));

562

563         (void)StatSynchronize(NULL);

564         SYS_Sleep(1);

565         rv = SYS_RemoveFile(PCSCLITE_EVENTS_DIR);

566         if (rv != 0)

567                 Log2(PCSC_LOG_ERROR, "Cannot remove " PCSCLITE_EVENTS_DIR ": %s",

568                         strerror(errno));

569 }

clean_temp_files 要干不少活?

具体做哪些?

一个一个来。

 

要删除 4 个文件。

4 个文件分别是:

PCSCLITE_PUBSHM_FILE  /var/run/pcscd/pcscd.pub

共享内存文件,在 APPLICATION pcscd 之间共享内存。

PCSCLITE_CSOCK_NAME /var/run/pcscd/pcscd.comm

本地 socket 通讯文件。

PCSCLITE_RUN_PID /var/run/pcscd/pcscd.pid

这个文件的存在目的,上面提过了。

PCSCLITE_EVENTS_DIR /var/run/pcscd/pcscd.events 这个是目录。

这个是 ... 正要解说。实际上关系到很久以前提到的问题 ( 64 页提到的 fifo)

563 行, StatSynchronize 定义在 utils.c

实现如下:

  87 int StatSynchronize(struct pubReaderStatesList *readerState)

  88 {

  89         DIR *dir_fd;

  90         struct dirent *dir;

  91

  92         if (readerState)

  93                 (void)SYS_MMapSynchronize((void *)readerState, SYS_GetPageSize() );

  94

  95         dir_fd = opendir(PCSCLITE_EVENTS_DIR);

  96         if (NULL == dir_fd)

  97         {

  98                  Log2(PCSC_LOG_ERROR, "Can't opendir " PCSCLITE_EVENTS_DIR ": %s",

  99                         strerror(errno));

100                 return -1;

101         }

102

103         while ((dir = readdir(dir_fd)) != NULL)

104         {

105                  char filename[FILENAME_MAX];

106                 int fd;

107                 char buf[] = { '/0' };

108                 struct stat fstat_buf;

109

110                 if ('.' == dir->d_name[0])

111                         continue;

112

113                  (void)snprintf(filename, sizeof(filename), "%s/%s", PCSCLITE_EVENTS_DIR,

114                         dir->d_name);

115                 Log2(PCSC_LOG_DEBUG, "status file: %s", filename);

116

117                 fd = SYS_OpenFile(filename, O_WRONLY | O_APPEND | O_NONBLOCK, 0);

118                 if (fd < 0)

119                 {

120                         /* ENXIO "No such device or address" is a normal error

121                          * if the client is no more listening the pipe */

122                         Log3(ENXIO == errno ? PCSC_LOG_DEBUG : PCSC_LOG_ERROR,

123                                 "Can't open %s: %s", filename, strerror(errno));

124                 }

125                 else

126                 {

127                          if (fstat(fd, &fstat_buf))

128                         {

129                                 Log3(PCSC_LOG_ERROR, "Can't fstat %s: %s", filename,

130                                         strerror(errno));

131                          }

132                         else

133                         {

134                                 /* check that the file is a FIFO */

135                                 if (!S_ISFIFO(fstat_buf.st_mode))

136                                          Log2(PCSC_LOG_ERROR, "%s is not a fifo", filename);

137                                 else

138                                         (void)SYS_WriteFile(fd, buf, sizeof(buf));

139                         }

140

141                         (void)SYS_CloseFile(fd);

142                 }

143

144                 /* remove files older than 60 seconds */

145                 if ((difftime(time(NULL), fstat_buf.st_atime) > 60) && unlink(filename))

146                         Log3(PCSC_LOG_ERROR, "Can't remove %s: %s", filename,

147                         strerror(errno));

148         }

149         (void)closedir(dir_fd);

150

151         return 0;

152 } /* StatSynchronize */

92~94 行,同步 readerState 共享内存。 SYS_MMapSynchronize msync 的一个 wrapper

pcscd 用了很多系统调用的 wrapper, 都集中在 sys_unix.c. 对于这个文件的理解,实际上也不困难。最后的系统 api 调用,不熟悉或者忘记了,也可以再参考 man.

 

 

95~101 行,打开这个目录。

103~117 行,遍历。逐个打开这个目录的目录项。

127 行, fstat 获取每个目录项的属性,如果是 FIFO ,则 138 行写入一个字节 '/0'

144~147 行,删除已经在系统中存在超过 60s fifo 文件。因为 fifo 文件是由 APPLICATION

mkfifo 创建的,如果 APPLICATION 自己在删除 fifo 文件之前崩溃,则这个文件就留在系统中了。所以这样做的目的是保持系统的干净。可以再回头看看 winscard_clnt.c 1872 行的

SCardGetStatusChange 实现。服务端只是更新了一个读卡器的状态 (StatSynchronize) 却需要遍历所有的目录项并发出事件通知所有的 APPLICATIONs 。而 SCardGetStatusChange, 却要不停 ( 可以采用超时机制 ) 地遍历比较参数传入的 rgReaderStates(1 个或多个读卡器状态 ),

来最终确定是否是感兴趣的读卡器状态发生了变化。效率是比较低的。也只能如此。

 

回到 clean_temp_files   563 行,现在情况很清楚了。就是删除这四类文件。同时简单地往 fifo 写入 '/0', 告知 APPLICATION 有事件发生。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最新的PCSC、CCID以及测试源码。 贴一段源码: LONG SCardEstablishContext(DWORD dwScope, /*@unused@*/ LPCVOID pvReserved1, /*@unused@*/ LPCVOID pvReserved2, LPSCARDCONTEXT phContext) { (void)pvReserved1; (void)pvReserved2; if (dwScope != SCARD_SCOPE_USER && dwScope != SCARD_SCOPE_TERMINAL && dwScope != SCARD_SCOPE_SYSTEM && dwScope != SCARD_SCOPE_GLOBAL) { *phContext = 0; return SCARD_E_INVALID_VALUE; } /* * Unique identifier for this server so that it can uniquely be * identified by clients and distinguished from others */ *phContext = (PCSCLITE_SVC_IDENTITY + SYS_RandomInt(1, 65535)); Log2(PCSC_LOG_DEBUG, "Establishing Context: 0x%X", *phContext); return SCARD_S_SUCCESS; } LONG SCardReleaseContext(SCARDCONTEXT hContext) { /* * Nothing to do here RPC layer will handle this */ Log2(PCSC_LOG_DEBUG, "Releasing Context: 0x%X", hContext); return SCARD_S_SUCCESS; } LONG SCardConnect(/*@unused@*/ SCARDCONTEXT hContext, LPCSTR szReader, DWORD dwShareMode, DWORD dwPreferredProtocols, LPSCARDHANDLE phCard, LPDWORD pdwActiveProtocol) { LONG rv; READER_CONTEXT * rContext = NULL; uint32_t readerState; (void)hContext; PROFILE_START *phCard = 0; if ((dwShareMode != SCARD_SHARE_DIRECT) && !(dwPreferredProtocols & SCARD_PROTOCOL_T0) && !(dwPreferredProtocols & SCARD_PROTOCOL_T1) && !(dwPreferredProtocols & SCARD_PROTOCOL_RAW) && !(dwPreferredProtocols & SCARD_PROTOCOL_ANY_OLD)) return SCARD_E_PROTO_MISMATCH; if (dwShareMode != SCARD_SHARE_EXCLUSIVE && dwShareMode != SCARD_SHARE_SHARED && dwShareMode != SCARD_SHARE_DIRECT) return SCARD_E_INVALID_VALUE; Log3(PCSC_LOG_DEBUG, "Attempting Connect to %s using protocol: %d", szReader, dwPreferredProtocols); rv = RFReaderInfo((LPSTR) szReader, &rContext); if (rv != SCARD_S_SUCCESS) { Log2(PCSC_LOG_ERROR, "Reader %s Not Found", szReader); return rv; } /* * Make sure the reader is working properly */ rv = RFCheckReaderStatus(rContext); if (rv != SCARD_S_SUCCESS) re

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值