如何安全地将net.Listener()的FD传递给子进程?

huangapple go评论84阅读模式
英文:

How to pass net.Listener()'s FD to child process safely?

问题

我已经被这个问题困扰了几个小时:

我有一个作为TCP服务器的主进程,主进程调用Fork(),将其net.Listener()的FD传递给子进程。然后子进程可以使用net.Filelistener()来继承这个FD。

我通过许多开源代码进行了研究,也进行了一些实验。但不幸的是,目前这些解决方案都不满足我,因为它们不具备可移植性,还需要许多危险的低级工作。

如果有任何安全地将net.Listener()的FD传递给子进程的解决方案,我会很高兴知道。

我目前尝试过的方法:

  1. 环境变量,不具备可移植性,会导致许多FD混乱,不安全,因为可以从外部进行更改。

  2. 复制FD并清除FD_CLOEXEC然后执行/分叉,具备可移植性,但不受Go API支持,一个关于syscall.NoCloseOnExec()的更改提交给了开发团队,但被拒绝了,因为他们希望保持syscall的清洁。

  3. 设置SO_REUSEADDR,这样子进程可以立即监听端口,在此之前关闭父进程的监听器。失败了,不具备可移植性,不受Go API支持,也不安全。

  4. exec.Command.ExtraFiles(),不知道如何从子进程获取继承的FD,我需要一个配置文件来保存FD和名称吗?这个解决方案还有一个bug,请阅读exec的文档以获取更多细节。

好了,伙计们,我已经编写了一个简单的测试用例来解决这个问题(使用解决方案4):

https://github.com/reckhou/go-fd-pass-test

还包括了OS X和Linux上的2个可执行文件。我尝试过Go 1.1和Go 1.1.1,但这个问题仍然存在。

英文:

I've been stucked with this issue for hours:

I have a main process served as a TCP server, the main process call Fork(), pass its net.Listener()'s FD to child process. Then child process can use net.Filelistener() to inherit this FD.

I have researched this issue through many open-sourced codes, also did some experiments. But unfortunately none of these solutions satisfy me for now since they are not portable, you also need many low-level jobs which are dangerous.

If there's any solution to pass net.Listener()'s FD to child process SAFELY, I'd be glad to know.

What I've tried for now:

  1. Environment values, not portable, will cause chaos with many FDs, not safe since can be changed from outside.

  2. Dup FD & Clear FD_CLOEXEC then exec/fork, portable but not supported by Go API, a syscall.NoCloseOnExec() change submitted to dev team was rejected since they want to keep syscall clean.

  3. Set SO_REUSEADDR so child process can listen to port instantly, close parent's listener before that. Failed, not portable, not supported by Go API, also unsafe.

  4. exec.Command.ExtraFiles(), have no idea how to get inherited FDs from child process, do I need a config file to save FD & names? This solution also have a bug, read exec's document for more detail.

All right guys, I've written a simple test case of this issue(with solution 4):

https://github.com/reckhou/go-fd-pass-test

Also include 2 executables on OS X & Linux. I tried Go 1.1 & Go 1.1.1 but this issue still remains.

答案1

得分: 5

最简单的方法是将监听器传递给exec.Cmd的ExtraFiles字段。

父进程的示例:

var l *net.TCPListener
cmd := exec.Command(...)
f, err := l.File()
cmd.ExtraFiles = []*os.File{f}

子进程的示例:

l, err := net.FileListener(os.NewFile(3, "listener"))

您还可以将此通用化,并使子进程接受PROGRAMNAME_LISTENER_FD作为环境变量。然后,父进程在启动子进程之前将环境变量设置为3。

英文:

The easiest way is to pass the listener in the ExtraFiles field of exec.Cmd.

Example of parent:

var l *net.TCPListener
cmd := exec.Command(...)
f, err := l.File()
cmd.ExtraFiles = []*os.File{f}

Example of child:

l, err := net.FileListener(os.NewFile(3, "listener"))

You may also want to generalize this and have the child accept PROGRAMNAME_LISTENER_FD as an environment variable. Then the parent would set the environment variable to 3 before starting the child.

huangapple
  • 本文由 发表于 2013年6月19日 21:52:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/17193086.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定