blob: cd28b9608cabd35aa7d52c8d5d46462cdf56db4d [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
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}