这可能是一个 XY 问题,但我想将一个串行终端(例如 /dev/ttyUSB0)克隆到两个终端中。来自任一终端的输入被复用到真实终端,并且输出被馈送到两个终端。有没有一个工具可以实现这一点/这是否可能?
我经常使用小型终端仿真器通过串行方式处理嵌入式硬件术语。我想始终保持交互式终端打开,但也使用运行命令序列预计在上面。例如,重新启动并在 U-Boot 提示符处停止。
我想象一个这样使用的工具:
$ teeterm /dev/ttyUSB0
Clone successful: /dev/pts/3 and /dev/pts/4 available.
$ dterm /dev/pts/3 115200
在另一个外壳中:
$ ./uboot (which spawns and interacts with dterm /dev/pts/4 115200)
答案1
使用 dirkt 发布的示例代码作为基础,我为此构建了一个真正的实用程序。这是可以在 Github 上找到。
我最终使用了:
teeterm dterm /dev/ttyUSB0 115200 (in another shell) dterm pty0 (in another shell) dterm pty1
两个伪终端都可以访问命令进程 I/O。该实用程序最重要的方面是叉子通过伪终端(用户不可用)调用来控制子进程,以及开放式和选择调用以创建两个伪终端并监视所有三个伪终端的输入。
答案2
根据要求,一个小型 C 程序在两个新创建的伪终端之间进行双向管道传输。不,这并不能回答问题,但可以很容易地对其进行调整来回答问题。这不是很长,所以这是代码。许可证是“用它做任何你想做的事”。
/* ptycat (ptypipe? ptypair?)
*
* create a pair of pseudo-terminal slaves connected to each other
*
* Link with -lutil
*
* Alternative: socat PTY,link=COM8 PTY,link=COM9
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include <pty.h>
#undef max
#define max(x,y) ((x) > (y) ? (x) : (y))
/*
(void)ioctl(STDIN_FILENO, TIOCGWINSZ, &win);
*/
/* TODO: make symlinks, unlink on atexit */
static uint8_t buf[BUFSIZ]; /* BUFSIZ from stdio.h, at least 256 */
static char *log_dir = NULL;
void logdata (char *dir, uint8_t *data, int n) {
if (dir != log_dir) fprintf (stdout, "\n%s", dir);
log_dir = dir;
for (; n > 0; n--, data++) fprintf (stdout, " %02x", *data);
fflush (stdout);
}
int main (int argc, char* argv[])
{
char name[256]; /* max namelen = 255 for most fs. */
fd_set rfd;
struct termios tt;
struct winsize ws;
int master[2], slave[2];
int n, nfds, cc;
if (tcgetattr (STDIN_FILENO, &tt) < 0)
{
perror("Cannot get terminal attributes of stdin");
exit(1);
}
cfmakeraw (&tt);
for (int i = 0; i < 2; i++)
{
if (openpty (&master[i], &slave[i], name, &tt, NULL /*ws*/) < 0)
{
perror("Cannot open pty");
exit(1);
}
puts(name);
}
for (;;) {
FD_ZERO(&rfd);
FD_SET(master[0], &rfd);
FD_SET(master[1], &rfd);
nfds = max(master[0], master[1]) + 1;
n = select(nfds, &rfd, 0, 0, NULL);
if (n > 0 || errno == EINTR)
{
if (FD_ISSET(master[0], &rfd))
{
if ((cc = read(master[0], buf, sizeof(buf))) > 0)
{
(void) write(master[1], buf, cc);
logdata (">>>", buf, cc);
}
}
if (FD_ISSET(master[1], &rfd))
{
if ((cc = read(master[1], buf, sizeof(buf))) > 0)
{
(void) write(master[0], buf, cc);
logdata ("<<<", buf, cc);
}
}
}
}
/* This is never reached */
return 0;
}