Here is my main.go, and I use go run main.go run sh
to create a process which runs shell in it.
package main
import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strconv"
"syscall"
"github.com/sirupsen/logrus"
)
func main() {
if len(os.Args) < 2 {
logrus.Errorf("missing commands")
return
}
switch os.Args[1] {
case "run":
run()
case "child":
child()
default:
logrus.Errorf("wrong command")
return
}
}
func run() {
logrus.Info("Setting up...")
cmd := exec.Command("/proc/self/exe", append([]string{"child"}, os.Args[2:]...)...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
}
check(cmd.Run())
}
func child() {
logrus.Infof("Running %v", os.Args[2:])
cmd := exec.Command(os.Args[2], os.Args[3:]...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
check(syscall.Sethostname([]byte("newhost")))
check(syscall.Chroot("/root/busybox"))
check(os.Chdir("/"))
check(syscall.Mount("proc", "proc", "proc", 0, ""))
check(syscall.Mount("tempdir", "temp", "tmpfs", 0, ""))
check(cmd.Run())
check(syscall.Unmount("proc", 0))
check(syscall.Unmount("temp", 0))
}
func check(err error) {
if err != nil {
logrus.Errorln(err)
}
}
When I run mount
in the new shell, it returns
proc on /proc type proc (rw,relatime)
tempdir on /temp type tmpfs (rw,relatime)
that just works fine.
But when I change the child function into
func child() {
...
check(os.Chdir("/"))
check(syscall.Mount("proc", "proc", "proc", 0, ""))
check(syscall.Mount("tempdir", "temp", "tmpfs", 0, ""))
defer check(syscall.Unmount("proc", 0))
defer check(syscall.Unmount("temp", 0))
check(cmd.Run())
}
and run mount
again, it returns mount: no /proc/mounts
.
defer
is illustrated to defer the function after it to the end of the outer function. But it seems that syscall.Umount()
is invoked before cmd.Run()
. Thanks for your help.