今日任务
init 传参改用匿名管道
init 传参改用匿名管道
为什么要使用匿名管道
原方案通过 /proc/self/exe init [args...] 将所有参数直接塞进 exec.Command() 调用中,如果参数过长或者有特殊符号就会发生错误。
匿名管道的读和写直接走文件io,可以加强对参数的控制和校验
// run
func NewParentProcess(interactive, tty bool) (*exec.Cmd, *os.File) { // 返回 Cmd 对象和 管道的写对象
fr, fw, err := os.Pipe() // 创建匿名管道
if err != nil {
l.Error("failed to create pipe", "detail", err)
return nil, nil
}
cmd := exec.Command("/proc/self/exe", "init") // 不需要在此处将 args 传入
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID |
syscall.CLONE_NEWNS | syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC,
}
if interactive {
cmd.Stdin = os.Stdin
}
if tty {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
}
cmd.ExtraFiles = append(cmd.ExtraFiles, fr) // 将管道的读文件描述符放进新创建进程的文件描述符数组中, 新的进程通过这个fd可以打开对应的管道文件
return cmd, fw
}
// init
const readingFd uintptr = 3 // 不固定
func Init() error {
l.Info("initiating")
l.Info("opening pipe")
f := os.NewFile(readingFd, "pipe") // 打开对应的管道
if f == nil {
l.Error("failed to create FILE with the given fd", "fd", readingFd)
return fmt.Errorf("failed to create FILE with the given fd: %d", readingFd)
}
l.Info("reading from pipe")
data, err := io.ReadAll(f) // 直接走文件io
if err != nil {
l.Error("failed to read from pipe", "detail", err)
return err
}
// 将参数反序列化
//
// 用 NUL 分隔符还原 cmds,避免含空格的参数(如 sh -c "echo hi")被腰斩
// 使用管道传参可以完成更多的操作
args := strings.Split(strings.TrimRight(string(data), "\x00"), "\x00")
if len(args) < 1 || args[0] == "" {
return fmt.Errorf("command is nil")
}
l.Info("remounting `/`")
if err := syscall.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil {
l.Error("failed to remount `/` with `MS_PRIVATE`", "detail", err)
return err
}
mountFlags := syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
if err := syscall.Mount("proc", "/proc", "proc", uintptr(mountFlags), ""); err != nil {
l.Error("failed to mount proc", "detail", err)
return err
}
l.Info("executing", "argv0", args[0], "argv", args)
if err := syscall.Exec(args[0], args, os.Environ()); err != nil {
l.Error("failed to init", "cmds", args, "detail", err)
return err
}
return nil
}