Linux的终端里,模拟实现一个带口令保护的屏保程序遇到的输入输出的问题,简化的用于说明意图的代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#define TERM_ATTR_RESET 0
#define TERM_TEXT_YELLOW 3
#define MAX_NAMELEN 64
#define MAX_PWLEN 32
#define MAX_ARGCS 64
#define MAX_CMDLEN 1024
#define printfcolorbuff(attr, color, buff) printf("\033[%d;%dm%s\033[0m", attr, color + 30, buff)
static int gworkmode = 0;
#define WORKMODE_LOCAL_MAIN 0
#define WORKMODE_LOCAL_SAVE 6
bool bscreensaver = true;
unsigned long lMasterInputTime = 0;
timer_t screensavertimer;
struct sigevent screensavertimersev;
struct itimerspec screensavertimeritv;
unsigned long gettickcount()
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
}
void printfusleep(const char *fmt, int usleepsec)
{
uint32_t i;
uint32_t fmtlen = strlen(fmt);
for (i = 0; i < fmtlen; i++)
{
fwrite(&fmt[i], 1, 1, stdout);
fflush(stdout);
usleep(usleepsec);
}
}
void getargcargv(char *parm, int &count, char *out[])
{
count = 0;
char seq[] = " ";
char *p;
if (p = strtok(parm, seq))
{
out[count] = p;
while (p != NULL)
{
if (count >= MAX_ARGCS)
break;
p = strtok(NULL, seq);
count++;
if (p)
out[count] = p;
}
}
}
bool myscanf(int color, char *title, char *input, int inputlen)
{
int i = 0;
int chr;
struct termios term, initial_term;
tcgetattr(0, &initial_term);
term = initial_term;
term.c_lflag &= ~ICANON;
term.c_lflag &= ~ECHO;
tcsetattr(0, TCSANOW, &term);
if (strlen(title))
{
printfcolorbuff(TERM_ATTR_RESET, color, title);
fflush(stdout);
}
while (1)
{
chr = getchar();
if (chr == 27) // ESC
{
tcsetattr(STDIN_FILENO, TCSADRAIN, &initial_term);
putchar('\n');
return false;
}
if (chr == '\n' || i == inputlen)
{
input[i] = '\0';
break;
}
else if (chr == 3)
{
input[0] = '\0';
break;
}
else if (chr == 127)
{
if (i > 0)
{
input[--i] = '\0';
putchar('\b');
putchar(' ');
putchar('\b');
}
else
putchar(7);
}
else
{
input[i] = chr;
i++;
putchar(chr);
}
}
tcsetattr(STDIN_FILENO, TCSADRAIN, &initial_term);
return true;
}
bool sceensaver_pwd_verify(const char *changedpwd, const unsigned int maxtrytimes)
{
char inputpwd[MAX_PWLEN] = {0};
unsigned int trytimes = 0;
begin:
if (trytimes >= maxtrytimes)
{
return false;
}
if (!myscanf(TERM_TEXT_YELLOW, (char *)"\npassword: ", inputpwd, sizeof(inputpwd)))
goto begin;
else
{
if (strcmp(inputpwd, changedpwd) == 0)
{
lMasterInputTime = gettickcount();
gworkmode = WORKMODE_LOCAL_MAIN;
return true;
}
else
{
trytimes++;
goto begin;
}
}
return false;
}
void stopscreensavertimer()
{
timer_delete(screensavertimer);
}
void screensaver_timer_handler(int signo)
{
switch (signo)
{
case SIGUSR1:
if (gettickcount() - lMasterInputTime > 5 * 1000 && gworkmode == WORKMODE_LOCAL_MAIN)
{
stopscreensavertimer();
gworkmode = WORKMODE_LOCAL_SAVE;
if (sceensaver_pwd_verify("abc123", 3))
{
printf("\nOK");
fflush(stdout);
}
else
{
exit(-1);
}
signal(SIGUSR1, screensaver_timer_handler);
screensavertimersev.sigev_notify = SIGEV_SIGNAL; // 表示通过信号来通知进程计时器到时
screensavertimersev.sigev_signo = SIGUSR1; // 设定信号的名称
timer_create(CLOCK_REALTIME, &screensavertimersev, &screensavertimer);
timer_settime(screensavertimer, ITIMER_REAL, &screensavertimeritv, NULL);
}
break;
}
}
void startscreensavertimer()
{
lMasterInputTime = gettickcount();
screensavertimeritv.it_value.tv_sec = 1;
screensavertimeritv.it_value.tv_nsec = 0;
screensavertimeritv.it_interval.tv_sec = 1;
screensavertimeritv.it_interval.tv_nsec = 0;
signal(SIGUSR1, screensaver_timer_handler);
screensavertimersev.sigev_notify = SIGEV_SIGNAL; // 表示通过信号来通知进程计时器到时
screensavertimersev.sigev_signo = SIGUSR1; // 设定信号的名称
timer_create(CLOCK_REALTIME, &screensavertimersev, &screensavertimer);
timer_settime(screensavertimer, ITIMER_REAL, &screensavertimeritv, NULL);
}
int main()
{
int argc = 0;
char prompt[MAX_NAMELEN] = {0};
char *argv[MAX_ARGCS] = {0};
char command[MAX_CMDLEN] = {0};
if (bscreensaver)
startscreensavertimer();
sprintf(prompt, "\nMain%s ", getuid() == 0 ? "#" : "$");
gworkmode = WORKMODE_LOCAL_MAIN;
while (1)
{
memset(command, 0, MAX_CMDLEN);
if (gworkmode == WORKMODE_LOCAL_MAIN)
{
myscanf(TERM_TEXT_YELLOW, prompt, command, sizeof(command));
lMasterInputTime = gettickcount();
begin:
getargcargv(command, argc, argv);
if (argc == 0)
{
continue;
}
if (strncasecmp(argv[0], "ss", 2) == 0 && strlen(argv[0]) == 2)
{
if (bscreensaver)
stopscreensavertimer();
gworkmode = WORKMODE_LOCAL_SAVE;
if (sceensaver_pwd_verify("abc123", 3))
{
printf("\nOK");
fflush(stdout);
}
if (bscreensaver)
startscreensavertimer();
}
else if (strncasecmp(argv[0], "quit", 4) == 0 && strlen(argv[0]) == 4)
{
printf("\n");
break;
}
}
}
}
编译:gcc -o timer timer.cpp -lrt
运行环境:ubuntu server 20.04 ARM64
运行不同的操作步骤展示问题:
进入Main# 提示符后,如果不等待Timer机制激活,而是直接手动输入'ss'来激活密码验证过程, 提示输入密码,输入正确的密码'abc123'(按回车结束)后,则直接回到Main# 提示符下。在这个操作和运行逻辑下,密码正确,只需要按一次回车键,就可以回到‘Main#’ 提示符下
进入Main# 提示符后,等待5秒后由Timer机制激活密码验证过程,提示输入密码,输入正确的'abc123'(按第一次回车结束)后,需要再连续按两次回车键,才能回到Main# 提示符下。这种由Timer机制激活的操作和运行逻辑,即便密码正确,需要按三次回车键,才能回到‘Main#’ 提示符下
我的疑问和需求:
- 操作1的流程是我想要的运行逻辑和效果,但同时我希望保留timer运行机制。而该机制下,却要连续回车三次才能回到‘Main#’ 提示符下。
- 操作2里的三次回车,第一次用于验证密码,第二次是因为main()循环里还处于myscanf()获取输入的状态。但是为啥要三次回车才能跳出?
- 有没有什么方法能在timer运行机制(操作2)下,在输入正确的密码后,和操作1一样,一次回车就直接回到Main# 提示符下,而不是要多回车两次?
- Timer回调函数里的输入输出是有什么特别处理?这里包含了什么知识点需要学习?
不要用AI说大道理,代码都贴这里了,怎么修改代码能实现我的需求,直接贴出代码吧。