当 stty 设置更改时监视并提醒用户?

当 stty 设置更改时监视并提醒用户?

一个特别大的(~10^6 LOC)程序导致我的 stty 设置从 更改为echo ixon icanon-echo -ixon -icanon我想在这个大型程序中找到导致此更改的函数。

我显然不想通过这些混乱的 OOP 意大利面条代码来跟踪执行情况。

如何监视 stty 设置并记录更改它们的内容?我想stracewithawk可能可以为我提供所需的信息,但我不知道要过滤哪些系统调用。

答案1

如果您认为可以通过特定操作或交互来引发事件,那么到目前为止最简单的方法如下:

 watch -d -n1 "stty -F /dev/pts/106 -a | grep -Eo '.(icanon|ixon)'"

在新终端上运行它,选项是-F您将运行程序的终端(tty在启动之前运行以查看它是什么)。| grep ..如果您想观察完整的终端状态,请省略。

如果您使用的是 Linux,下一个选项是使用ltrace跟踪库调用,这类似于strace(并且它包含一些strace功能),但它适用于用户空间库,而不仅仅是内核系统调用:

ltrace -tt -e tcgetattr+tcsetattr myprogram ...

这将显示对libc 函数 和 的调用tcgetattr()并添加时间戳,以获取和设置终端属性。tcsetattr()

最终,这些 libc 调用将使用ioctl()系统调用,您可以使用strace或进行跟踪,以下是在 Linux 上truss使用的方法:strace

strace -tt -e trace=ioctl myprogram [...]

这里的一个很大的优点是,它strace会很乐意为您将各种参数解码为系统调用。

上面的内容都不会从逻辑上告诉您问题可能发生在程序的哪个位置,为此您有两个选择:调试器或 DLL 注入。

您可以轻松地在上gdb设置断点tcsetattr(),然后检查调用堆栈,但是如果有很多调用,这可能会很乏味(并且需要调试 libc 的构建或符号以获得最佳结果)。

最全面的选项(假设目标程序是动态链接的)是注入您自己的 DLL,它拦截或包装您需要跟踪的函数,在本例中为tcsetattr().

#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <dlfcn.h>
#include <unistd.h>
#include <termios.h>

/* 
 * gcc -nostartfiles -shared -ldl -Wl,-soname,tcsetattr -o tc.so wraptc.c
 * LD_PRELOAD=./tc.so stty -icanon ixon
 *
 */

#define DEBUG 1
#define dfprintf(fmt, ...) \
    do { if (DEBUG) fprintf(stderr, "[%14s#%04d:%8s()] " fmt, \
          __FILE__, __LINE__, __func__, __VA_ARGS__); } while (0)


typedef int tcsetattr_fp(int fd, int optional_actions, 
                     const struct termios *termios_p);
static tcsetattr_fp    *real_tcsetattr;

void _init()
{
    dfprintf("It's alive!\n","");
    real_tcsetattr = dlsym(RTLD_NEXT, "tcsetattr");
    dfprintf("Hooked %p tcsetattr()\n",(void *)real_tcsetattr);
}

int tcsetattr(int fd, int optional_actions, const struct termios *termios_p)
{
     void *bt[20];
     size_t btsz;

     int rc,stacktr=0;
     dfprintf("Caught tcsetattr(%i,%04x,...)\n",fd,optional_actions);

     if ( (fd==0) && !((termios_p->c_lflag) & ICANON)) {
         dfprintf("ICANON off!\n","");
         stacktr=1;
     }
     if ( (fd==0) && !((termios_p->c_iflag) & IXON)) {
         dfprintf("IXON off!\n","");
         stacktr=1;
     }
     if (stacktr) {
         btsz=backtrace(bt,sizeof(bt));
         backtrace_symbols_fd(bt,btsz,STDERR_FILENO);
     }

     rc=real_tcsetattr(fd,optional_actions, termios_p);
     return rc;
}

按照注释中的指示编译和调用。此代码找到了真正的 libctcsetattr()函数,并包含一个替代版本。此代码backtrace()在 FD 0 上看到可能有趣的活动时调用,然后调用真正的 libc 版本。可能需要进行细微调整。

相关内容