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