github.com/Cloud-Foundations/Dominator@v0.3.4/imagebuilder/builder/target.go (about) 1 package builder 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "sort" 10 "strings" 11 "syscall" 12 13 "github.com/Cloud-Foundations/Dominator/lib/goroutine" 14 "github.com/Cloud-Foundations/Dominator/lib/wsyscall" 15 ) 16 17 // newNamespaceTarget will create a goroutine which is locked to an OS 18 // thread with a separate mount namespace. 19 func newNamespaceTarget() (*goroutine.Goroutine, error) { 20 g := goroutine.New() 21 var err error 22 g.Run(func() { err = wsyscall.UnshareMountNamespace() }) 23 if err != nil { 24 return nil, err 25 } 26 return g, nil 27 } 28 29 // newNamespaceTargetWithMounts will create a goroutine which is locked to an OS 30 // thread with a separate mount namespace. The directories specified by 31 // bindMounts will be mounted into the new namespace. 32 func newNamespaceTargetWithMounts(rootDir string, bindMounts []string) ( 33 *goroutine.Goroutine, error) { 34 g, err := newNamespaceTarget() 35 if err != nil { 36 return nil, err 37 } 38 g.Run(func() { err = setupMounts(rootDir, bindMounts) }) 39 if err != nil { 40 return nil, err 41 } 42 return g, nil 43 } 44 45 // Run a command in the target root directory in the specified namespace and 46 // with a new PID namespace. 47 func runInTarget(g *goroutine.Goroutine, input io.Reader, output io.Writer, 48 rootDir string, envGetter environmentGetter, 49 prog string, args ...string) error { 50 var environmentToInject map[string]string 51 if envGetter != nil { 52 environmentToInject = envGetter.getenv() 53 } 54 cmd := exec.Command(prog, args...) 55 cmd.Env = stripVariables(os.Environ(), environmentToCopy, environmentToSet, 56 environmentToInject) 57 cmd.Dir = "/" 58 cmd.Stdin = input 59 cmd.Stdout = output 60 cmd.Stderr = output 61 cmd.SysProcAttr = &syscall.SysProcAttr{ 62 Chroot: rootDir, 63 Setsid: true, 64 Cloneflags: syscall.CLONE_NEWPID, 65 } 66 var err error 67 g.Run(func() { err = cmd.Start() }) 68 if err != nil { 69 return err 70 } 71 return cmd.Wait() 72 } 73 74 // setupMounts will mutate the current namespace. 75 func setupMounts(rootDir string, bindMounts []string) error { 76 err := wsyscall.Mount("none", filepath.Join(rootDir, "proc"), "proc", 0, "") 77 if err != nil { 78 return err 79 } 80 err = wsyscall.Mount("none", filepath.Join(rootDir, "sys"), "sysfs", 0, "") 81 if err != nil { 82 return err 83 } 84 for _, bindMount := range bindMounts { 85 err := wsyscall.Mount(bindMount, 86 filepath.Join(rootDir, bindMount), "", 87 wsyscall.MS_BIND|wsyscall.MS_RDONLY, "") 88 if err != nil { 89 return fmt.Errorf("error bind mounting: %s: %s", bindMount, err) 90 } 91 } 92 return nil 93 } 94 95 func stripVariables(input []string, varsToCopy map[string]struct{}, 96 varsToSet ...map[string]string) []string { 97 output := make([]string, 0) 98 for _, nameValue := range input { 99 split := strings.SplitN(nameValue, "=", 2) 100 if len(split) == 2 { 101 if _, ok := varsToCopy[split[0]]; ok { 102 output = append(output, nameValue) 103 } 104 } 105 } 106 for _, varTable := range varsToSet { 107 for name, value := range varTable { 108 output = append(output, name+"="+value) 109 } 110 } 111 sort.Strings(output) 112 return output 113 }