Fly2Free2 2023-12-08 00:13 采纳率: 0%
浏览 8
已结题

关于Linux的终端里,模拟实现一个带口令保护的屏保程序遇到的输入输出的问题!(语言-c语言)

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

运行不同的操作步骤展示问题:

  1. 进入Main# 提示符后,如果不等待Timer机制激活,而是直接手动输入'ss'来激活密码验证过程, 提示输入密码,输入正确的密码'abc123'(按回车结束)后,则直接回到Main# 提示符下。在这个操作和运行逻辑下,密码正确,只需要按一次回车键,就可以回到‘Main#’ 提示符下

  2. 进入Main# 提示符后,等待5秒后由Timer机制激活密码验证过程,提示输入密码,输入正确的'abc123'(按第一次回车结束)后,需要再连续按两次回车键,才能回到Main# 提示符下。这种由Timer机制激活的操作和运行逻辑,即便密码正确,需要按三次回车键,才能回到‘Main#’ 提示符下

我的疑问和需求:

  1. 操作1的流程是我想要的运行逻辑和效果,但同时我希望保留timer运行机制。而该机制下,却要连续回车三次才能回到‘Main#’ 提示符下。
  2. 操作2里的三次回车,第一次用于验证密码,第二次是因为main()循环里还处于myscanf()获取输入的状态。但是为啥要三次回车才能跳出?
  3. 有没有什么方法能在timer运行机制(操作2)下,在输入正确的密码后,和操作1一样,一次回车就直接回到Main# 提示符下,而不是要多回车两次?
  4. Timer回调函数里的输入输出是有什么特别处理?这里包含了什么知识点需要学习?

不要用AI说大道理,代码都贴这里了,怎么修改代码能实现我的需求,直接贴出代码吧。

  • 写回答

18条回答 默认 最新

  • 专家-赤兔[在线] 全栈领域优质创作者 2023-12-08 00:14
    关注
    获得0.45元问题酬金

    引自强大而免费的微信小程序:皆我百晓生

    1. 操作1中,输入'ss'后,会进入屏幕保护模式,并提示输入密码。输入正确的密码后,会回到Main#提示符下。这是因为在screensaver_timer_handler函数中,当接收到SIGUSR1信号时,会执行停止屏幕保护计时器的操作(timer_delete),然后重新设置计时器(timer_create)并启动计时器(timer_settime)。因此,屏幕保护模式结束后会回到Main#提示符下。

    2. 操作2中,等待5秒后Timer机制激活,进入屏幕保护模式。此时,输入密码后,不会立即回到Main#提示符下,而是继续等待输入。需要按两次回车键才能退出。这是因为在screensaver_timer_handler函数中,如果接收到SIGUSR1信号,且屏幕保护模式结束后,会重新设置计时器并启动计时器。然后,输入的第一次回车键会被mycasnf函数读取并结束输入,但是程序仍会继续等待下一次输入的回车键,直到接收到下一个回车键才会回到Main#提示符下。

    3. 要实现在timer运行机制下,输入正确的密码后直接回到Main#提示符下,可以在screensaver_timer_handler函数中,在屏幕保护模式结束后添加一个跳出输入的逻辑。例如,可以在输入密码正确后使用一个标志变量来标识屏保解锁,然后在while循环中检查该标志变量,如果为真,则直接跳出输入。
      更改sceensaver_pwd_verify函数如下:

    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;
       }
    

    在screensaver_timer_handler函数中添加标志变量:

    volatile bool screensaver_unlocked = false;
    
       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);
                            screensaver_unlocked = true;  // 标志屏保解锁
                        }
                        else
                        {
                            exit(-1);
                        }
    
                        // 跳出输入
                        while (1)
                        {
                            if (screensaver_unlocked)
                                break;
                        }
                        
                        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;
            }
        }
    
    在main函数中修改while循环:
    
    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);
                        screensaver_unlocked = true;  // 标志屏保解锁
                    }
    
                    // 跳出输入
                    while (1)
                    {
                        if (screensaver_unlocked)
                            break;
                    }
    
                    if (bscreensaver)
                        startscreensavertimer();
                }
                else if (strncasecmp(argv[0], "quit", 2) == 0 && strlen(argv[0]) == 4)
                {
                    printf("\n");
                    break;
                }
            }
        }
    

    4.在回调函数screensaver_timer_handler中,输入输出主要涉及以下部分:

    • 使用printf函数输出提示信息,例如在输入密码前输出"password: "。
    • 使用getchar函数获取输入字符,通过检查输入字符的ASCII值进行处理,如回车键对应的ASCII值为13。
    • 使用putchar函数进行输出,通过输出特定的字符如'\n'来换行,'\b'来退格等。
    • 使用fflush函数刷新输出缓冲区,使得输出结果及时显示。
    • 使用fprintf函数和stdout来将内容输出到标准输出流。
    • 使用tcgetattr和tcsetattr函数来设置和恢复终端属性。
    • 使用readline函数和相关库函数来实现在终端中获取输入,并支持命令历史记录。
    评论 编辑记录

报告相同问题?

问题事件

  • 系统已结题 12月16日
  • 修改了问题 12月8日
  • 修改了问题 12月8日
  • 修改了问题 12月8日
  • 展开全部

悬赏问题

  • ¥15 在若依框架下实现人脸识别
  • ¥15 网络科学导论,网络控制
  • ¥100 安卓tv程序连接SQLSERVER2008问题
  • ¥15 利用Sentinel-2和Landsat8做一个水库的长时序NDVI的对比,为什么Snetinel-2计算的结果最小值特别小,而Lansat8就很平均
  • ¥15 metadata提取的PDF元数据,如何转换为一个Excel
  • ¥15 关于arduino编程toCharArray()函数的使用
  • ¥100 vc++混合CEF采用CLR方式编译报错
  • ¥15 coze 的插件输入飞书多维表格 app_token 后一直显示错误,如何解决?
  • ¥15 vite+vue3+plyr播放本地public文件夹下视频无法加载
  • ¥15 c#逐行读取txt文本,但是每一行里面数据之间空格数量不同