blob: a4fe3e4890046378052e028b2761c68d5fd606b8 [file] [log] [blame]
Dan Willemsen18490112018-05-25 16:30:04 -07001// 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
Dan Willemsen18490112018-05-25 16:30:04 -070056 exitCode, err := Main(os.Stdout, os.Stderr, interposer, os.Args, mainOpts{
Dan Willemsen18490112018-05-25 16:30:04 -070057 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 {
Dan Willemsen18490112018-05-25 16:30:04 -070075 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 }
Dan Willemsen347ba752020-05-01 16:29:00 -0700125 if config.Error {
Dan Willemsen18490112018-05-25 16:30:04 -0700126 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}