| Dan Willemsen | 1849011 | 2018-05-25 16:30:04 -0700 | [diff] [blame] | 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 |  | 
|  | 15 | package main | 
|  | 16 |  | 
|  | 17 | import ( | 
|  | 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 |  | 
|  | 31 | func 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 |  | 
| Dan Willemsen | 1849011 | 2018-05-25 16:30:04 -0700 | [diff] [blame] | 56 | exitCode, err := Main(os.Stdout, os.Stderr, interposer, os.Args, mainOpts{ | 
| Dan Willemsen | 1849011 | 2018-05-25 16:30:04 -0700 | [diff] [blame] | 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 |  | 
|  | 67 | var 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 |  | 
|  | 71 | If a tool isn't in the allowed list, a log will be posted to the unix domain | 
|  | 72 | socket at <interposer>_log.`) | 
|  | 73 |  | 
|  | 74 | type mainOpts struct { | 
| Dan Willemsen | 1849011 | 2018-05-25 16:30:04 -0700 | [diff] [blame] | 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 |  | 
|  | 80 | func 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 | } | 
| Dan Willemsen | 347ba75 | 2020-05-01 16:29:00 -0700 | [diff] [blame] | 125 | if config.Error { | 
| Dan Willemsen | 1849011 | 2018-05-25 16:30:04 -0700 | [diff] [blame] | 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 |  | 
|  | 155 | type procEntry struct { | 
|  | 156 | Pid     int | 
|  | 157 | Ppid    int | 
|  | 158 | Command string | 
|  | 159 | } | 
|  | 160 |  | 
|  | 161 | func 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 |  | 
|  | 171 | func 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 |  | 
|  | 213 | func 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 | } |