1// Copyright 2018 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package main 16 17import ( 18 "bytes" 19 "fmt" 20 "io" 21 "io/ioutil" 22 "os" 23 "os/exec" 24 "path/filepath" 25 "strconv" 26 "syscall" 27 28 "android/soong/ui/build/paths" 29) 30 31func main() { 32 interposer, err := os.Executable() 33 if err != nil { 34 fmt.Fprintln(os.Stderr, "Unable to locate interposer executable:", err) 35 os.Exit(1) 36 } 37 38 if fi, err := os.Lstat(interposer); err == nil { 39 if fi.Mode()&os.ModeSymlink != 0 { 40 link, err := os.Readlink(interposer) 41 if err != nil { 42 fmt.Fprintln(os.Stderr, "Unable to read link to interposer executable:", err) 43 os.Exit(1) 44 } 45 if filepath.IsAbs(link) { 46 interposer = link 47 } else { 48 interposer = filepath.Join(filepath.Dir(interposer), link) 49 } 50 } 51 } else { 52 fmt.Fprintln(os.Stderr, "Unable to stat interposer executable:", err) 53 os.Exit(1) 54 } 55 56 disableError := false 57 if e, ok := os.LookupEnv("TEMPORARY_DISABLE_PATH_RESTRICTIONS"); ok { 58 disableError = e == "1" || e == "y" || e == "yes" || e == "on" || e == "true" 59 } 60 61 exitCode, err := Main(os.Stdout, os.Stderr, interposer, os.Args, mainOpts{ 62 disableError: disableError, 63 64 sendLog: paths.SendLog, 65 config: paths.GetConfig, 66 lookupParents: lookupParents, 67 }) 68 if err != nil { 69 fmt.Fprintln(os.Stderr, err.Error()) 70 } 71 os.Exit(exitCode) 72} 73 74var usage = fmt.Errorf(`To use the PATH interposer: 75 * Write the original PATH variable to <interposer>_origpath 76 * Set up a directory of symlinks to the PATH interposer, and use that in PATH 77 78If a tool isn't in the allowed list, a log will be posted to the unix domain 79socket at <interposer>_log.`) 80 81type mainOpts struct { 82 disableError bool 83 84 sendLog func(logSocket string, entry *paths.LogEntry, done chan interface{}) 85 config func(name string) paths.PathConfig 86 lookupParents func() []paths.LogProcess 87} 88 89func Main(stdout, stderr io.Writer, interposer string, args []string, opts mainOpts) (int, error) { 90 base := filepath.Base(args[0]) 91 92 origPathFile := interposer + "_origpath" 93 if base == filepath.Base(interposer) { 94 return 1, usage 95 } 96 97 origPath, err := ioutil.ReadFile(origPathFile) 98 if err != nil { 99 if os.IsNotExist(err) { 100 return 1, usage 101 } else { 102 return 1, fmt.Errorf("Failed to read original PATH: %v", err) 103 } 104 } 105 106 cmd := &exec.Cmd{ 107 Args: args, 108 Env: os.Environ(), 109 110 Stdin: os.Stdin, 111 Stdout: stdout, 112 Stderr: stderr, 113 } 114 115 if err := os.Setenv("PATH", string(origPath)); err != nil { 116 return 1, fmt.Errorf("Failed to set PATH env: %v", err) 117 } 118 119 if config := opts.config(base); config.Log || config.Error { 120 var procs []paths.LogProcess 121 if opts.lookupParents != nil { 122 procs = opts.lookupParents() 123 } 124 125 if opts.sendLog != nil { 126 waitForLog := make(chan interface{}) 127 opts.sendLog(interposer+"_log", &paths.LogEntry{ 128 Basename: base, 129 Args: args, 130 Parents: procs, 131 }, waitForLog) 132 defer func() { <-waitForLog }() 133 } 134 if config.Error && !opts.disableError { 135 return 1, fmt.Errorf("%q is not allowed to be used. See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.", base) 136 } 137 } 138 139 cmd.Path, err = exec.LookPath(base) 140 if err != nil { 141 return 1, err 142 } 143 144 if err = cmd.Run(); err != nil { 145 if exitErr, ok := err.(*exec.ExitError); ok { 146 if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { 147 if status.Exited() { 148 return status.ExitStatus(), nil 149 } else if status.Signaled() { 150 exitCode := 128 + int(status.Signal()) 151 return exitCode, nil 152 } else { 153 return 1, exitErr 154 } 155 } else { 156 return 1, nil 157 } 158 } 159 } 160 161 return 0, nil 162} 163 164type procEntry struct { 165 Pid int 166 Ppid int 167 Command string 168} 169 170func readProcs() map[int]procEntry { 171 cmd := exec.Command("ps", "-o", "pid,ppid,command") 172 data, err := cmd.Output() 173 if err != nil { 174 return nil 175 } 176 177 return parseProcs(data) 178} 179 180func parseProcs(data []byte) map[int]procEntry { 181 lines := bytes.Split(data, []byte("\n")) 182 if len(lines) < 2 { 183 return nil 184 } 185 // Remove the header 186 lines = lines[1:] 187 188 ret := make(map[int]procEntry, len(lines)) 189 for _, line := range lines { 190 fields := bytes.SplitN(line, []byte(" "), 2) 191 if len(fields) != 2 { 192 continue 193 } 194 195 pid, err := strconv.Atoi(string(fields[0])) 196 if err != nil { 197 continue 198 } 199 200 line = bytes.TrimLeft(fields[1], " ") 201 202 fields = bytes.SplitN(line, []byte(" "), 2) 203 if len(fields) != 2 { 204 continue 205 } 206 207 ppid, err := strconv.Atoi(string(fields[0])) 208 if err != nil { 209 continue 210 } 211 212 ret[pid] = procEntry{ 213 Pid: pid, 214 Ppid: ppid, 215 Command: string(bytes.TrimLeft(fields[1], " ")), 216 } 217 } 218 219 return ret 220} 221 222func lookupParents() []paths.LogProcess { 223 procs := readProcs() 224 if procs == nil { 225 return nil 226 } 227 228 list := []paths.LogProcess{} 229 pid := os.Getpid() 230 for { 231 entry, ok := procs[pid] 232 if !ok { 233 break 234 } 235 236 list = append([]paths.LogProcess{ 237 { 238 Pid: pid, 239 Command: entry.Command, 240 }, 241 }, list...) 242 243 pid = entry.Ppid 244 } 245 246 return list 247} 248