containers-from-scratch/main.go

79 lines
1.7 KiB
Go

package main
import (
"fmt"
"os"
"os/exec"
"strconv"
"syscall"
"time"
)
// docker run <container-name> <command> <args>
// go main.go run <command> <args>
func main() {
if len(os.Args) < 3 {
panic("found too few arguments for the program.")
}
switch os.Args[1] {
case "run":
executeRun()
case "child":
executeChild()
default:
panic(fmt.Sprintf("found an unsupported command: %s", os.Args[0]))
}
}
func executeRun() {
fmt.Printf("starting: 'run' in PID: %d\n", os.Getpid())
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,
Unshareflags: syscall.CLONE_NEWNS,
}
must(cmd.Run())
}
func executeChild() {
fmt.Printf("starting: %s in PID: %d\n", os.Args[2], os.Getpid())
setupCgroup()
syscall.Sethostname([]byte("container"))
syscall.Chroot("./container-root")
os.Chdir("/")
syscall.Mount("proc", "proc", "proc", 0, "")
defer syscall.Unmount("/proc", 0)
cmd := exec.Command(os.Args[2])
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
must(cmd.Run())
}
func setupCgroup() {
cgroupDir := fmt.Sprintf("/sys/fs/cgroup/pids/%d", time.Now().Unix())
must(os.Mkdir(cgroupDir, 0755))
os.WriteFile(fmt.Sprintf("%s/pids.max", cgroupDir), []byte("20"), 0644)
os.WriteFile(fmt.Sprintf("%s/notify_on_release", cgroupDir), []byte("1"), 0644)
os.WriteFile(fmt.Sprintf("%s/cgroup.procs", cgroupDir), []byte(strconv.Itoa(os.Getpid())), 0644)
}
func must(err error) {
if err != nil {
panic(err)
}
}