提问人:nagylzs 提问时间:10/16/2023 更新时间:10/16/2023 访问量:32
无法在 Go 中将 stdin 转发到 sudo 子进程
Cannot forward stdin to sudo subprocess in Go
问:
下面是一个最小的工作示例,演示了这个问题。
package main
import (
"bufio"
"fmt"
"io"
"log"
"os"
"os/exec"
)
func main() {
//cmd := exec.Command("sudo", "tail", "-10", "/var/log/syslog")
cmd := exec.Command("cat")
stdin, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
stderr, err := cmd.StderrPipe()
if err != nil {
log.Fatal(err)
}
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
ForwardStdIn(stdin)
go ForwardStdOut(stdout)
go ForwardStdErr(stderr)
err = cmd.Wait()
if err == nil {
os.Exit(0)
}
if ee, ok := err.(*exec.ExitError); ok {
fmt.Println(ee.Error())
os.Exit(ee.ExitCode())
} else {
log.Fatal(err)
}
}
func ForwardStdOut(stdout io.ReadCloser) {
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
line := scanner.Text()
_, err := fmt.Fprint(os.Stdout, line+"\n")
if err != nil {
log.Fatal(err)
}
}
}
func ForwardStdErr(stderr io.ReadCloser) {
scanner := bufio.NewScanner(stderr)
for scanner.Scan() {
line := scanner.Text()
_, err := fmt.Fprint(os.Stderr, line+"\n")
if err != nil {
log.Fatal(err)
}
}
}
func readStdin(ch chan []byte) {
for {
buf := make([]byte, 4096)
n, err := os.Stdin.Read(buf)
if n == 0 && err == io.EOF {
close(ch)
//fmt.Println("stdin closed #1")
break
}
//fmt.Printf("read %v bytes\n", n)
if n > 0 {
ch <- buf[:n]
}
}
}
func writeStdin(stdin io.WriteCloser, ch chan []byte) {
for {
buf, ok := <-ch
if !ok {
err := stdin.Close()
//fmt.Println("stdin closed #2")
if err != nil {
log.Fatal(err)
}
break
}
pos := 0
for pos < len(buf) {
n, err := stdin.Write(buf[pos:])
if err != nil {
log.Fatal(err)
}
//fmt.Printf("wrote %v bytes\n", n)
pos += n
}
}
}
func ForwardStdIn(stdin io.WriteCloser) {
ch := make(chan []byte, 2)
go readStdin(ch)
go writeStdin(stdin, ch)
}
如果我运行这个程序,那么我写到 stdin 中的任何内容都会出现在 stdout 上。
但是如果我替换,那么它就不起作用。首先,它要求我输入密码。我输入密码,按 .调试器显示密码已发送到 的 stdin,但不会发生其他任何事情。没有错误消息,并且不会生成进一步的输出。cat
sudo tail -10 /var/log/syslog
ENTER
sudo
我最初认为可能需要一个功能齐全的 pty(终端)才能工作,但事实并非如此。我可以这样在没有 tty 的情况下运行:sudo
sudo
sudo tail -10 /var/log/syslog </dev/null |& cat
而且效果很好。
问题是这样的:为什么上面的代码对 ?如何更改此代码以使其与(或使用 askpass 的任何其他程序)一起使用。cat
sudo
sudo
答: 暂无答案
评论
ENTER
sudo -S tail -10 /var/log/syslog
也有效,似乎默认的密码读取方法需要终端;但由于某种原因,sudo 无法检测到它没有连接到终端 github.com/sudo-project/sudo/blob/main/src/tgetpass.c#L141???