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

来源:互联网 发布:明天教室网络课好吗 编辑:程序博客网 时间:2024/05/22 04:50

 

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

 

所以呢,serialPCMCIA类型的读卡器在/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 setappropriately.

也就是说如果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 is0, 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

共享内存文件,在APPLICATIONpcscd之间共享内存。

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行,删除已经在系统中存在超过60sfifo文件。因为fifo文件是由APPLICATION

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

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

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

 

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

原创粉丝点击