禁止 GPG“从文件描述符 0 读取密码”消息

Pau*_*jan 2 gpg stdin stdout

简单地说,我怎样才能让 GPG 不打印该消息?以下是我正在使用的命令:

echo "test input" > test.in
echo "test" | gpg -q -c --passphrase-fd 0 --output test.enc --yes --force-mdc test.in
echo "test" | gpg -q -d --passphrase-fd 0 test.enc > test.out
Run Code Online (Sandbox Code Playgroud)

并运行它:

$ echo "test input" > test.in 
$ echo "test" | gpg -q -c --passphrase-fd 0 --output test.enc --yes --force-mdc test.in
Reading passphrase from file descriptor 0    
$ echo "test" | gpg -q -d --passphrase-fd 0 test.enc > test.out
Reading passphrase from file descriptor 0
Run Code Online (Sandbox Code Playgroud)

编辑:重定向 stderr 似乎不起作用

$ echo "test" | gpg -q -c --passphrase-fd 0 --output test.enc --yes --force-mdc test.in 2> /dev/null
Reading passphrase from file descriptor 0    
Run Code Online (Sandbox Code Playgroud)

use*_*579 8

查看正在发生的事情的一种方法是跟踪所涉及的系统调用。执行此操作的实用程序因平台而异。在 Solaris 上,您将使用truss。在 Linux 上(如我的示例中),您将使用strace


为了跟踪,我们更改了使用的命令:

echo "test" | gpg -q -c --passphrase-fd 0 --output test.enc --yes --force-mdc test.in 2> /dev/null

到:

echo "test" | strace gpg -q -c --passphrase-fd 0 --output test.enc --yes --force-mdc test.in 2>trace_output.txt .


第一件有趣的事情(如果有点不相关)是 gpg 在从 stdin 获取输入密码时会重复读取单字节。这有时是代码效率低下的明显迹象 - 但在这种情况下,这可能没什么大不了的:

read(0, "t", 1)                         = 1
read(0, "e", 1)                         = 1
read(0, "s", 1)                         = 1
read(0, "t", 1)                         = 1
read(0, "\n", 1)                        = 1
Run Code Online (Sandbox Code Playgroud)

关于日志消息输出的更多相关内容都在这里:

open("/dev/tty", O_RDWR)                = 3
fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(5, 0), ...}) = 0
ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 opost isig icanon echo ...}) = 0
write(3, "Reading passphrase from file des"..., 45) = 45
write(3, "\10\10\10   \n", 7)           = 7
Run Code Online (Sandbox Code Playgroud)

这就是我们在退出之前听到的关于文件描述符 3 的全部内容(它没有明确关闭)。

依次查看每一个:

  • open("/dev/tty", O_RDWR) = 3

    这是打开文件 /dev/tty,用于读取和写入。返回值(供以后使用的新文件描述符)为 3。

    /dev/tty 是当前控制终端的同义词。您可以通过运行来查看此特殊文件有效引用的设备$ tty

  • fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(5, 0), ...}) = 0

    gpg 使用它来查找刚刚打开的文件描述符为 3 的文件。大括号中的内容是返回的内容(填充的 struct stat,其中 5, 0 表示这是一个特别特殊的文件)。

  • ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 opost isig icanon echo ...}) = 0

    这是在输出之前操纵控制终端的属性。

  • write(3, "Reading passphrase from file des"..., 45) = 45

    write(3, "\10\10\10 \n", 7) = 7

    这些更直接。gpg 成功地将该文本(其中一些在 strace 输出中缩写)写入终端。


所以 - 这就是你的答案.. gpg 正在将此日志消息直接写入 /dev/tty(控制终端的同义词),因此您将无法以与 stdout 或 stderr 相同的方式重定向它。

还有就是解决的办法。您可以在执行 gpg 之前断开控制终端的连接。

这是一个简短的程序,它就是这样做的:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

int main(int argc, char* argv[])
{
  int rc, fd;
  if (argc != 2)
  {
    fprintf(stderr, 
     "Provide command line arg to execute after TIOCNOTTY\n");
    return EXIT_FAILURE;
  }
  fd = open("/dev/tty", O_RDWR);
  if (fd < 0)
  {
    fprintf(stderr, 
     "Failed to open controlling terminal: %s\n",
     strerror(errno));
    return EXIT_FAILURE;
  }
  rc = ioctl(fd, TIOCNOTTY);
  if (rc == -1)
  {
    fprintf(stderr,
     "Failed TIOCNOTTY ioctrl: %s\b",
     strerror(errno));
    return EXIT_FAILURE;
  }
  return system(argv[1]);
}
Run Code Online (Sandbox Code Playgroud)

应该有一个现有的实用程序来执行上述操作,但我找不到。

如果您要编译该代码,请调用生成的可执行文件notty,然后您可以这样做:

echo "test" | notty "gpg -q -c --passphrase-fd 0 --output test.enc --yes --force-mdc test.in"

这应该会抑制消息,但保持 stdout 和 stderr 完好无损。目前尚不清楚还有什么会被抑制(您需要查看 gpg 源以查看以这种方式输出的其他内容)。

  • 哇,非常深入和翔实。谢谢你。接受,即使 `--batch` 也能工作,你教会了我很多。为什么 gpg 会做 /dev/tty 而不是 stdout? (2认同)

Pau*_*jan 7

--batch 是答案,但我不知道它是如何输出的,即使 stderr 被重定向...