• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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	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}
248