发送SIGINT到过程通过发送CTRL-C至标准输入
我在寻找一种方法来模拟天生一些自动化测试的端子:即启动一个进程,然后通过将数据发送到stdin和从标准输出读取与它进行交互。例如。向stdin发送一些输入行,包括ctrl-c
和ctrl-\
,这应该导致发送信号给进程。发送SIGINT到过程通过发送CTRL-C至标准输入
使用std::process::Commannd
我能够将输入发送给例如cat
,我也看到了在标准输出上输出,但发送ctrl-c
(如I understand that is 3
)不会导致SIGINT
发送到外壳。例如。这个计划应当终止:
use std::process::{Command, Stdio};
use std::io::Write;
fn main() {
let mut child = Command::new("sh")
.arg("-c").arg("-i").arg("cat")
.stdin(Stdio::piped())
.spawn().unwrap();
let mut stdin = child.stdin.take().unwrap();
stdin.write(&[3]).expect("cannot send ctrl-c");
child.wait();
}
我怀疑的问题是,发送ctrl-c
需要一些TTY并通过sh -i
这只是在“交互模式”。
更新:我在原来的问题混淆外壳和终端。我现在清除了这个。我还提到ssh
应该是sh
。
了大量的研究后,我想通了,这不是太多的工作要做PTY叉自己。有pty-rs,但它有错误,似乎没有维护。
下面的代码需要pty
module of nix
这是尚未在crates.io,所以Cargo.toml
需要这个现在:
[dependencies]
nix = {git = "https://github.com/nix-rust/nix.git"}
下面的代码运行在一个tty猫,然后写从中/读取和发送按Ctrl -C(3
):
extern crate nix;
use std::path::Path;
use nix::pty::{posix_openpt, grantpt, unlockpt, ptsname};
use nix::fcntl::{O_RDWR, open};
use nix::sys::stat;
use nix::unistd::{fork, ForkResult, setsid, dup2};
use nix::libc::{STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::io::prelude::*;
use std::io::{BufReader, LineWriter};
fn run() -> std::io::Result<()> {
// Open a new PTY master
let master_fd = posix_openpt(O_RDWR)?;
// Allow a slave to be generated for it
grantpt(&master_fd)?;
unlockpt(&master_fd)?;
// Get the name of the slave
let slave_name = ptsname(&master_fd)?;
match fork() {
Ok(ForkResult::Child) => {
setsid()?; // create new session with child as session leader
let slave_fd = open(Path::new(&slave_name), O_RDWR, stat::Mode::empty())?;
// assign stdin, stdout, stderr to the tty, just like a terminal does
dup2(slave_fd, STDIN_FILENO)?;
dup2(slave_fd, STDOUT_FILENO)?;
dup2(slave_fd, STDERR_FILENO)?;
std::process::Command::new("cat").status()?;
}
Ok(ForkResult::Parent { child: _ }) => {
let f = unsafe { std::fs::File::from_raw_fd(master_fd.as_raw_fd()) };
let mut reader = BufReader::new(&f);
let mut writer = LineWriter::new(&f);
writer.write_all(b"hello world\n")?;
let mut s = String::new();
reader.read_line(&mut s)?; // what we just wrote in
reader.read_line(&mut s)?; // what cat wrote out
writer.write(&[3])?; // send ^C
writer.flush()?;
let mut buf = [0; 2]; // needs bytewise read as ^C has no newline
reader.read(&mut buf)?;
s += &String::from_utf8_lossy(&buf).to_string();
println!("{}", s);
println!("cat exit code: {:?}", wait::wait()?); // make sure cat really exited
}
Err(_) => println!("error"),
}
Ok(())
}
fn main() {
run().expect("could not execute command");
}
输出:
hello world
hello world
^C
cat exit code: Signaled(2906, SIGINT, false)
尝试添加-t选项TWICE来强制伪tty分配。即
klar (16:14) ~>echo foo | ssh [email protected] tty
not a tty
klar (16:14) ~>echo foo | ssh -t -t [email protected] tty
/dev/pts/0
当你有一个伪tty,我认为它应该将其转换为SIGINT,如你所愿。
在您简单的例子,你也可以写,经过短短接近标准输入在这种情况下,服务器应该退出。对于这种特殊情况,它会更优雅,可能更可靠。
但首先我想使用'cat'(或其他命令)而不是'ssh',然后我想用生锈来控制过程。国际海事组织我真的需要一个tty分支,否则Ctrl-C和类似的将永远不会工作,请参阅下面的答案 – hansaplast
如果您按下Ctrl-C键,那么这些按键从不会输入到应用程序中。它们由终端处理,终端通过发送SIGINT来响应该终端。所以你想发送SIGINT到进程。 – sepp2k
https://stackoverflow.com/questions/6108953/how-does-ctrl-c-terminate-a-child-process –
@ sepp2k感谢您的问题。我意识到shell将ctrl-c转换为SIGINT,但不知何故忘了在问题中加入这一点。这个问题现在应该更清楚了。我还添加了'-i'选项,它应该在交互模式下运行sh,但它仍然不起作用 – hansaplast