英文:
How to pass net.Listener()'s FD to child process safely?
问题
我已经被这个问题困扰了几个小时:
我有一个作为TCP服务器的主进程,主进程调用Fork(),将其net.Listener()的FD传递给子进程。然后子进程可以使用net.Filelistener()来继承这个FD。
我通过许多开源代码进行了研究,也进行了一些实验。但不幸的是,目前这些解决方案都不满足我,因为它们不具备可移植性,还需要许多危险的低级工作。
如果有任何安全地将net.Listener()的FD传递给子进程的解决方案,我会很高兴知道。
我目前尝试过的方法:
-
环境变量,不具备可移植性,会导致许多FD混乱,不安全,因为可以从外部进行更改。
-
复制FD并清除
FD_CLOEXEC
然后执行/分叉,具备可移植性,但不受Go API支持,一个关于syscall.NoCloseOnExec()
的更改提交给了开发团队,但被拒绝了,因为他们希望保持syscall的清洁。 -
设置
SO_REUSEADDR
,这样子进程可以立即监听端口,在此之前关闭父进程的监听器。失败了,不具备可移植性,不受Go API支持,也不安全。 -
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:
-
Environment values, not portable, will cause chaos with many FDs, not safe since can be changed from outside.
-
Dup FD & Clear
FD_CLOEXEC
then exec/fork, portable but not supported by Go API, asyscall.NoCloseOnExec()
change submitted to dev team was rejected since they want to keep syscall clean. -
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. -
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论