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 exitCode, err := Main(os.Stdout, os.Stderr, interposer, os.Args, mainOpts{ 57 sendLog: paths.SendLog, 58 config: paths.GetConfig, 59 lookupParents: lookupParents, 60 }) 61 if err != nil { 62 fmt.Fprintln(os.Stderr, err.Error()) 63 } 64 os.Exit(exitCode) 65} 66 67var usage = fmt.Errorf(`To use the PATH interposer: 68 * Write the original PATH variable to <interposer>_origpath 69 * Set up a directory of symlinks to the PATH interposer, and use that in PATH 70 71If a tool isn't in the allowed list, a log will be posted to the unix domain 72socket at <interposer>_log.`) 73 74type mainOpts struct { 75 sendLog func(logSocket string, entry *paths.LogEntry, done chan interface{}) 76 config func(name string) paths.PathConfig 77 lookupParents func() []paths.LogProcess 78} 79 80func Main(stdout, stderr io.Writer, interposer string, args []string, opts mainOpts) (int, error) { 81 base := filepath.Base(args[0]) 82 83 origPathFile := interposer + "_origpath" 84 if base == filepath.Base(interposer) { 85 return 1, usage 86 } 87 88 origPath, err := ioutil.ReadFile(origPathFile) 89 if err != nil { 90 if os.IsNotExist(err) { 91 return 1, usage 92 } else { 93 return 1, fmt.Errorf("Failed to read original PATH: %v", err) 94 } 95 } 96 97 cmd := &exec.Cmd{ 98 Args: args, 99 Env: os.Environ(), 100 101 Stdin: os.Stdin, 102 Stdout: stdout, 103 Stderr: stderr, 104 } 105 106 if err := os.Setenv("PATH", string(origPath)); err != nil { 107 return 1, fmt.Errorf("Failed to set PATH env: %v", err) 108 } 109 110 if config := opts.config(base); config.Log || config.Error { 111 var procs []paths.LogProcess 112 if opts.lookupParents != nil { 113 procs = opts.lookupParents() 114 } 115 116 if opts.sendLog != nil { 117 waitForLog := make(chan interface{}) 118 opts.sendLog(interposer+"_log", &paths.LogEntry{ 119 Basename: base, 120 Args: args, 121 Parents: procs, 122 }, waitForLog) 123 defer func() { <-waitForLog }() 124 } 125 if config.Error { 126 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) 127 } 128 } 129 130 cmd.Path, err = exec.LookPath(base) 131 if err != nil { 132 return 1, err 133 } 134 135 if err = cmd.Run(); err != nil { 136 if exitErr, ok := err.(*exec.ExitError); ok { 137 if status, ok := exitErr.Sys().(syscall.WaitStatus); ok { 138 if status.Exited() { 139 return status.ExitStatus(), nil 140 } else if status.Signaled() { 141 exitCode := 128 + int(status.Signal()) 142 return exitCode, nil 143 } else { 144 return 1, exitErr 145 } 146 } else { 147 return 1, nil 148 } 149 } 150 } 151 152 return 0, nil 153} 154 155type procEntry struct { 156 Pid int 157 Ppid int 158 Command string 159} 160 161func readProcs() map[int]procEntry { 162 cmd := exec.Command("ps", "-o", "pid,ppid,command") 163 data, err := cmd.Output() 164 if err != nil { 165 return nil 166 } 167 168 return parseProcs(data) 169} 170 171func parseProcs(data []byte) map[int]procEntry { 172 lines := bytes.Split(data, []byte("\n")) 173 if len(lines) < 2 { 174 return nil 175 } 176 // Remove the header 177 lines = lines[1:] 178 179 ret := make(map[int]procEntry, len(lines)) 180 for _, line := range lines { 181 fields := bytes.SplitN(line, []byte(" "), 2) 182 if len(fields) != 2 { 183 continue 184 } 185 186 pid, err := strconv.Atoi(string(fields[0])) 187 if err != nil { 188 continue 189 } 190 191 line = bytes.TrimLeft(fields[1], " ") 192 193 fields = bytes.SplitN(line, []byte(" "), 2) 194 if len(fields) != 2 { 195 continue 196 } 197 198 ppid, err := strconv.Atoi(string(fields[0])) 199 if err != nil { 200 continue 201 } 202 203 ret[pid] = procEntry{ 204 Pid: pid, 205 Ppid: ppid, 206 Command: string(bytes.TrimLeft(fields[1], " ")), 207 } 208 } 209 210 return ret 211} 212 213func lookupParents() []paths.LogProcess { 214 procs := readProcs() 215 if procs == nil { 216 return nil 217 } 218 219 list := []paths.LogProcess{} 220 pid := os.Getpid() 221 for { 222 entry, ok := procs[pid] 223 if !ok { 224 break 225 } 226 227 list = append([]paths.LogProcess{ 228 { 229 Pid: pid, 230 Command: entry.Command, 231 }, 232 }, list...) 233 234 pid = entry.Ppid 235 } 236 237 return list 238} 239