package nsenter
import (
"fmt"
"os"
"os/exec"
"strconv"
"golang.org/x/sys/unix"
"golang.org/x/xerrors"
)
type Namespace int
const (
NamespaceMount = iota
NamespaceNet
NamespacePID
)
func Run(pid int, args []string, addFD []*os.File, enterNamespace ...Namespace) error {
nss := []struct {
Env string
Source string
Flags int
NS Namespace
}{
{"_LIBNSENTER_ROOTFD", fmt.Sprintf("/proc/%d/root", pid), unix.O_PATH, -1},
{"_LIBNSENTER_CWDFD", fmt.Sprintf("/proc/%d/cwd", pid), unix.O_PATH, -1},
{"_LIBNSENTER_MNTNSFD", fmt.Sprintf("/proc/%d/ns/mnt", pid), os.O_RDONLY, NamespaceMount},
{"_LIBNSENTER_NETNSFD", fmt.Sprintf("/proc/%d/ns/net", pid), os.O_RDONLY, NamespaceNet},
{"_LIBNSENTER_PIDNSFD", fmt.Sprintf("/proc/%d/ns/pid", pid), os.O_RDONLY, NamespacePID},
}
stdioFdCount := 3
cmd := exec.Command("/proc/self/exe", append([]string{"handler"}, args...)...)
cmd.ExtraFiles = append(cmd.ExtraFiles, addFD...)
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, "_LIBNSENTER_INIT=1")
for _, ns := range nss {
var enter bool
if ns.NS == -1 {
enter = true
} else {
for _, s := range enterNamespace {
if ns.NS == s {
enter = true
break
}
}
}
if !enter {
continue
}
f, err := os.OpenFile(ns.Source, ns.Flags, 0)
if err != nil {
return xerrors.Errorf("cannot open %s: %w", ns.Source, err)
}
defer f.Close()
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", ns.Env, stdioFdCount+len(cmd.ExtraFiles)))
cmd.ExtraFiles = append(cmd.ExtraFiles, f)
}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
err := cmd.Run()
if err != nil {
return xerrors.Errorf("cannot run handler: %w", err)
}
return nil
}
func Mount(pid int, source, target string, fstype string, flags int, data string) error {
args := []string{"mount",
"--source", source,
"--target", target,
"--flags", strconv.Itoa(flags),
}
if fstype != "" {
args = append(args, "--fstype", fstype)
}
if data != "" {
args = append(args, "--data", data)
}
return Run(pid, args, nil, NamespaceMount)
}
func MoveMount(pid int, fromFD *os.File, target string) error {
args := []string{"move-mount",
"--fd", "3",
"--dest", target,
}
return Run(pid, args, []*os.File{fromFD}, NamespaceMount)
}