1package houdini 2 3import ( 4 "fmt" 5 "os" 6 "os/exec" 7 "path/filepath" 8 "strings" 9 "syscall" 10 11 "code.cloudfoundry.org/garden" 12) 13 14func (container *container) setup() error { 15 if container.hasRootfs { 16 for _, dir := range []string{"/proc", "/dev", "/sys"} { 17 dest := filepath.Join(container.workDir, dir) 18 19 err := os.MkdirAll(dest, 0755) 20 if err != nil { 21 return fmt.Errorf("failed to create target for bind mount: %s", err) 22 } 23 24 err = syscall.Mount(dir, dest, "none", syscall.MS_BIND|syscall.MS_RDONLY, "") 25 if err != nil { 26 return err 27 } 28 } 29 30 for _, file := range []string{"/etc/resolv.conf", "/etc/hosts"} { 31 dest := filepath.Join(container.workDir, file) 32 33 f, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, 0644) 34 if err != nil { 35 return fmt.Errorf("failed to create target for bind mount: %s", err) 36 } 37 38 err = f.Close() 39 if err != nil { 40 return err 41 } 42 43 err = syscall.Mount(file, dest, "none", syscall.MS_BIND|syscall.MS_RDONLY, "") 44 if err != nil { 45 return err 46 } 47 } 48 } 49 50 for _, bm := range container.spec.BindMounts { 51 dest := filepath.Join(container.workDir, bm.DstPath) 52 53 err := os.MkdirAll(dest, 0755) 54 if err != nil { 55 return fmt.Errorf("failed to create target for bind mount: %s", err) 56 } 57 58 flags := uintptr(syscall.MS_BIND) 59 if bm.Mode == garden.BindMountModeRO { 60 flags |= syscall.MS_RDONLY 61 } 62 63 err = syscall.Mount(bm.SrcPath, dest, "none", flags, "") 64 if err != nil { 65 return err 66 } 67 } 68 69 return nil 70} 71 72const defaultRootPath = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 73// const defaultPath = "/usr/local/bin:/usr/bin:/bin" 74 75func (container *container) path() string { 76 var path string 77 for _, env := range container.env { 78 segs := strings.SplitN(env, "=", 2) 79 if len(segs) < 2 { 80 continue 81 } 82 83 if segs[0] == "PATH" { 84 path = segs[1] 85 } 86 } 87 88 if !container.hasRootfs { 89 if path == "" { 90 path = os.Getenv("PATH") 91 } 92 93 return path 94 } 95 96 if path == "" { 97 // assume running as root for now, since Houdini doesn't currently support 98 // running as a user 99 path = defaultRootPath 100 } 101 102 var scopedPath string 103 for _, dir := range filepath.SplitList(path) { 104 if scopedPath != "" { 105 scopedPath += string(filepath.ListSeparator) 106 } 107 108 scopedPath += container.workDir + dir 109 } 110 111 return scopedPath 112} 113 114func (container *container) cmd(spec garden.ProcessSpec) (*exec.Cmd, error) { 115 var cmd *exec.Cmd 116 117 if container.hasRootfs { 118 path := spec.Path 119 120 if !strings.Contains(path, "/") { 121 // find executable within container's $PATH 122 123 absPath, err := lookPath(path, container.path()) 124 if err != nil { 125 return nil, garden.ExecutableNotFoundError{ 126 Message: err.Error(), 127 } 128 } 129 130 // correct path so that it's absolute from the rootfs 131 path = strings.TrimPrefix(absPath, container.workDir) 132 } 133 134 cmd = exec.Command(path, spec.Args...) 135 136 if spec.Dir != "" { 137 cmd.Dir = spec.Dir 138 } else { 139 cmd.Dir = "/" 140 } 141 142 cmd.SysProcAttr = &syscall.SysProcAttr{ 143 Chroot: container.workDir, 144 } 145 } else { 146 cmd = exec.Command(spec.Path, spec.Args...) 147 cmd.Dir = filepath.Join(container.workDir, spec.Dir) 148 } 149 150 cmd.Env = append(os.Environ(), append(container.env, spec.Env...)...) 151 152 return cmd, nil 153} 154 155func findExecutable(file string) error { 156 d, err := os.Stat(file) 157 if err != nil { 158 return err 159 } 160 if m := d.Mode(); !m.IsDir() && m&0111 != 0 { 161 return nil 162 } 163 return os.ErrPermission 164} 165 166// based on exec.LookPath from stdlib 167func lookPath(file string, path string) (string, error) { 168 if strings.Contains(file, "/") { 169 err := findExecutable(file) 170 if err == nil { 171 return file, nil 172 } 173 return "", &exec.Error{Name: file, Err: err} 174 } 175 176 for _, dir := range filepath.SplitList(path) { 177 if dir == "" { 178 // Unix shell semantics: path element "" means "." 179 dir = "." 180 } 181 path := filepath.Join(dir, file) 182 if err := findExecutable(path); err == nil { 183 return path, nil 184 } 185 } 186 187 return "", &exec.Error{Name: file, Err: exec.ErrNotFound} 188} 189