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 有事件发生。