Merge "Revert "Add path interposer""
This commit is contained in:
commit
4fc3ea6048
|
@ -1,20 +0,0 @@
|
||||||
// Copyright 2018 Google Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
blueprint_go_binary {
|
|
||||||
name: "path_interposer",
|
|
||||||
deps: ["soong-ui-build-paths"],
|
|
||||||
srcs: ["main.go"],
|
|
||||||
testSrcs: ["main_test.go"],
|
|
||||||
}
|
|
|
@ -1,229 +0,0 @@
|
||||||
// Copyright 2018 Google Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"android/soong/ui/build/paths"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
interposer, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "Unable to locate interposer executable:", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
disableError := false
|
|
||||||
if e, ok := os.LookupEnv("TEMPORARY_DISABLE_PATH_RESTRICTIONS"); ok {
|
|
||||||
disableError = e == "1" || e == "y" || e == "yes" || e == "on" || e == "true"
|
|
||||||
}
|
|
||||||
|
|
||||||
exitCode, err := Main(os.Stdout, os.Stderr, interposer, os.Args, mainOpts{
|
|
||||||
disableError: disableError,
|
|
||||||
|
|
||||||
sendLog: paths.SendLog,
|
|
||||||
config: paths.GetConfig,
|
|
||||||
lookupParents: lookupParents,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err.Error())
|
|
||||||
}
|
|
||||||
os.Exit(exitCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
var usage = fmt.Errorf(`To use the PATH interposer:
|
|
||||||
* Write the original PATH variable to <interposer>_origpath
|
|
||||||
* Set up a directory of symlinks to the PATH interposer, and use that in PATH
|
|
||||||
|
|
||||||
If a tool isn't in the allowed list, a log will be posted to the unix domain
|
|
||||||
socket at <interposer>_log.`)
|
|
||||||
|
|
||||||
type mainOpts struct {
|
|
||||||
disableError bool
|
|
||||||
|
|
||||||
sendLog func(logSocket string, entry *paths.LogEntry, done chan interface{})
|
|
||||||
config func(name string) paths.PathConfig
|
|
||||||
lookupParents func() []paths.LogProcess
|
|
||||||
}
|
|
||||||
|
|
||||||
func Main(stdout, stderr io.Writer, interposer string, args []string, opts mainOpts) (int, error) {
|
|
||||||
base := filepath.Base(args[0])
|
|
||||||
|
|
||||||
origPathFile := interposer + "_origpath"
|
|
||||||
if base == filepath.Base(interposer) {
|
|
||||||
return 1, usage
|
|
||||||
}
|
|
||||||
|
|
||||||
origPath, err := ioutil.ReadFile(origPathFile)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return 1, usage
|
|
||||||
} else {
|
|
||||||
return 1, fmt.Errorf("Failed to read original PATH: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := &exec.Cmd{
|
|
||||||
Args: args,
|
|
||||||
Env: os.Environ(),
|
|
||||||
|
|
||||||
Stdin: os.Stdin,
|
|
||||||
Stdout: stdout,
|
|
||||||
Stderr: stderr,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.Setenv("PATH", string(origPath)); err != nil {
|
|
||||||
return 1, fmt.Errorf("Failed to set PATH env: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if config := opts.config(base); config.Log || config.Error {
|
|
||||||
var procs []paths.LogProcess
|
|
||||||
if opts.lookupParents != nil {
|
|
||||||
procs = opts.lookupParents()
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.sendLog != nil {
|
|
||||||
waitForLog := make(chan interface{})
|
|
||||||
opts.sendLog(interposer+"_log", &paths.LogEntry{
|
|
||||||
Basename: base,
|
|
||||||
Args: args,
|
|
||||||
Parents: procs,
|
|
||||||
}, waitForLog)
|
|
||||||
defer func() { <-waitForLog }()
|
|
||||||
}
|
|
||||||
if config.Error && !opts.disableError {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Path, err = exec.LookPath(base)
|
|
||||||
if err != nil {
|
|
||||||
return 1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = cmd.Run(); err != nil {
|
|
||||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
|
||||||
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
|
|
||||||
if status.Exited() {
|
|
||||||
return status.ExitStatus(), nil
|
|
||||||
} else if status.Signaled() {
|
|
||||||
exitCode := 128 + int(status.Signal())
|
|
||||||
return exitCode, nil
|
|
||||||
} else {
|
|
||||||
return 1, exitErr
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return 1, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type procEntry struct {
|
|
||||||
Pid int
|
|
||||||
Ppid int
|
|
||||||
Command string
|
|
||||||
}
|
|
||||||
|
|
||||||
func readProcs() map[int]procEntry {
|
|
||||||
cmd := exec.Command("ps", "-o", "pid,ppid,command")
|
|
||||||
data, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return parseProcs(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseProcs(data []byte) map[int]procEntry {
|
|
||||||
lines := bytes.Split(data, []byte("\n"))
|
|
||||||
if len(lines) < 2 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Remove the header
|
|
||||||
lines = lines[1:]
|
|
||||||
|
|
||||||
ret := make(map[int]procEntry, len(lines))
|
|
||||||
for _, line := range lines {
|
|
||||||
fields := bytes.SplitN(line, []byte(" "), 2)
|
|
||||||
if len(fields) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pid, err := strconv.Atoi(string(fields[0]))
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
line = bytes.TrimLeft(fields[1], " ")
|
|
||||||
|
|
||||||
fields = bytes.SplitN(line, []byte(" "), 2)
|
|
||||||
if len(fields) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ppid, err := strconv.Atoi(string(fields[0]))
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ret[pid] = procEntry{
|
|
||||||
Pid: pid,
|
|
||||||
Ppid: ppid,
|
|
||||||
Command: string(bytes.TrimLeft(fields[1], " ")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupParents() []paths.LogProcess {
|
|
||||||
procs := readProcs()
|
|
||||||
if procs == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
list := []paths.LogProcess{}
|
|
||||||
pid := os.Getpid()
|
|
||||||
for {
|
|
||||||
entry, ok := procs[pid]
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
list = append([]paths.LogProcess{
|
|
||||||
{
|
|
||||||
Pid: pid,
|
|
||||||
Command: entry.Command,
|
|
||||||
},
|
|
||||||
}, list...)
|
|
||||||
|
|
||||||
pid = entry.Ppid
|
|
||||||
}
|
|
||||||
|
|
||||||
return list
|
|
||||||
}
|
|
|
@ -1,196 +0,0 @@
|
||||||
// Copyright 2018 Google Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"android/soong/ui/build/paths"
|
|
||||||
)
|
|
||||||
|
|
||||||
var tmpDir string
|
|
||||||
var origPATH string
|
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
|
||||||
os.Exit(func() int {
|
|
||||||
var err error
|
|
||||||
tmpDir, err = ioutil.TempDir("", "interposer_test")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmpDir)
|
|
||||||
|
|
||||||
origPATH = os.Getenv("PATH")
|
|
||||||
err = os.Setenv("PATH", "")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.Run()
|
|
||||||
}())
|
|
||||||
}
|
|
||||||
|
|
||||||
func setup(t *testing.T) string {
|
|
||||||
f, err := ioutil.TempFile(tmpDir, "interposer")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
err = ioutil.WriteFile(f.Name()+"_origpath", []byte(origPATH), 0666)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
return f.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInterposer(t *testing.T) {
|
|
||||||
interposer := setup(t)
|
|
||||||
|
|
||||||
logConfig := func(name string) paths.PathConfig {
|
|
||||||
if name == "true" {
|
|
||||||
return paths.PathConfig{
|
|
||||||
Log: false,
|
|
||||||
Error: false,
|
|
||||||
}
|
|
||||||
} else if name == "path_interposer_test_not_allowed" {
|
|
||||||
return paths.PathConfig{
|
|
||||||
Log: false,
|
|
||||||
Error: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return paths.PathConfig{
|
|
||||||
Log: true,
|
|
||||||
Error: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
args []string
|
|
||||||
|
|
||||||
exitCode int
|
|
||||||
err error
|
|
||||||
logEntry string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "direct call",
|
|
||||||
args: []string{interposer},
|
|
||||||
|
|
||||||
exitCode: 1,
|
|
||||||
err: usage,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "relative call",
|
|
||||||
args: []string{filepath.Base(interposer)},
|
|
||||||
|
|
||||||
exitCode: 1,
|
|
||||||
err: usage,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "true",
|
|
||||||
args: []string{"/my/path/true"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "relative true",
|
|
||||||
args: []string{"true"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "exit code",
|
|
||||||
args: []string{"bash", "-c", "exit 42"},
|
|
||||||
|
|
||||||
exitCode: 42,
|
|
||||||
logEntry: "bash",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "signal",
|
|
||||||
args: []string{"bash", "-c", "kill -9 $$"},
|
|
||||||
|
|
||||||
exitCode: 137,
|
|
||||||
logEntry: "bash",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "does not exist",
|
|
||||||
args: []string{"path_interposer_test_does_not_exist"},
|
|
||||||
|
|
||||||
exitCode: 1,
|
|
||||||
err: fmt.Errorf(`exec: "path_interposer_test_does_not_exist": executable file not found in $PATH`),
|
|
||||||
logEntry: "path_interposer_test_does_not_exist",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not allowed",
|
|
||||||
args: []string{"path_interposer_test_not_allowed"},
|
|
||||||
|
|
||||||
exitCode: 1,
|
|
||||||
err: fmt.Errorf(`"path_interposer_test_not_allowed" is not allowed to be used. See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.`),
|
|
||||||
logEntry: "path_interposer_test_not_allowed",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
|
||||||
logged := false
|
|
||||||
logFunc := func(logSocket string, entry *paths.LogEntry, done chan interface{}) {
|
|
||||||
defer close(done)
|
|
||||||
|
|
||||||
logged = true
|
|
||||||
if entry.Basename != testCase.logEntry {
|
|
||||||
t.Errorf("unexpected log entry:\nwant: %q\n got: %q", testCase.logEntry, entry.Basename)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exitCode, err := Main(ioutil.Discard, ioutil.Discard, interposer, testCase.args, mainOpts{
|
|
||||||
sendLog: logFunc,
|
|
||||||
config: logConfig,
|
|
||||||
})
|
|
||||||
|
|
||||||
errstr := func(err error) string {
|
|
||||||
if err == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return err.Error()
|
|
||||||
}
|
|
||||||
if errstr(testCase.err) != errstr(err) {
|
|
||||||
t.Errorf("unexpected error:\nwant: %v\n got: %v", testCase.err, err)
|
|
||||||
}
|
|
||||||
if testCase.exitCode != exitCode {
|
|
||||||
t.Errorf("expected exit code %d, got %d", testCase.exitCode, exitCode)
|
|
||||||
}
|
|
||||||
if !logged && testCase.logEntry != "" {
|
|
||||||
t.Errorf("no log entry, but expected %q", testCase.logEntry)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMissingPath(t *testing.T) {
|
|
||||||
interposer := setup(t)
|
|
||||||
err := os.Remove(interposer + "_origpath")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to remove:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
exitCode, err := Main(ioutil.Discard, ioutil.Discard, interposer, []string{"true"}, mainOpts{})
|
|
||||||
if err != usage {
|
|
||||||
t.Errorf("Unexpected error:\n got: %v\nwant: %v", err, usage)
|
|
||||||
}
|
|
||||||
if exitCode != 1 {
|
|
||||||
t.Errorf("expected exit code %d, got %d", 1, exitCode)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -18,6 +18,8 @@ def SearchPathEnv(name):
|
||||||
for directory in search_path:
|
for directory in search_path:
|
||||||
if directory == '': continue
|
if directory == '': continue
|
||||||
path = os.path.join(directory, name)
|
path = os.path.join(directory, name)
|
||||||
|
if os.path.islink(path):
|
||||||
|
path = os.path.realpath(path)
|
||||||
# Check if path is actual executable file.
|
# Check if path is actual executable file.
|
||||||
if os.path.isfile(path) and os.access(path, os.X_OK):
|
if os.path.isfile(path) and os.access(path, os.X_OK):
|
||||||
return path
|
return path
|
||||||
|
|
|
@ -12,23 +12,10 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
bootstrap_go_package {
|
|
||||||
name: "soong-ui-build-paths",
|
|
||||||
pkgPath: "android/soong/ui/build/paths",
|
|
||||||
srcs: [
|
|
||||||
"paths/config.go",
|
|
||||||
"paths/logs.go",
|
|
||||||
],
|
|
||||||
testSrcs: [
|
|
||||||
"paths/logs_test.go",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
bootstrap_go_package {
|
bootstrap_go_package {
|
||||||
name: "soong-ui-build",
|
name: "soong-ui-build",
|
||||||
pkgPath: "android/soong/ui/build",
|
pkgPath: "android/soong/ui/build",
|
||||||
deps: [
|
deps: [
|
||||||
"soong-ui-build-paths",
|
|
||||||
"soong-ui-logger",
|
"soong-ui-logger",
|
||||||
"soong-ui-tracer",
|
"soong-ui-tracer",
|
||||||
"soong-shared",
|
"soong-shared",
|
||||||
|
@ -46,7 +33,6 @@ bootstrap_go_package {
|
||||||
"finder.go",
|
"finder.go",
|
||||||
"kati.go",
|
"kati.go",
|
||||||
"ninja.go",
|
"ninja.go",
|
||||||
"path.go",
|
|
||||||
"proc_sync.go",
|
"proc_sync.go",
|
||||||
"signal.go",
|
"signal.go",
|
||||||
"soong.go",
|
"soong.go",
|
||||||
|
|
|
@ -140,8 +140,6 @@ func Build(ctx Context, config Config, what int) {
|
||||||
|
|
||||||
ensureEmptyDirectoriesExist(ctx, config.TempDir())
|
ensureEmptyDirectoriesExist(ctx, config.TempDir())
|
||||||
|
|
||||||
SetupPath(ctx, config)
|
|
||||||
|
|
||||||
if what&BuildProductConfig != 0 {
|
if what&BuildProductConfig != 0 {
|
||||||
// Run make for product config
|
// Run make for product config
|
||||||
runMakeProductConfig(ctx, config)
|
runMakeProductConfig(ctx, config)
|
||||||
|
|
|
@ -51,8 +51,6 @@ type configImpl struct {
|
||||||
targetDeviceDir string
|
targetDeviceDir string
|
||||||
|
|
||||||
brokenDupRules bool
|
brokenDupRules bool
|
||||||
|
|
||||||
pathReplaced bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const srcDirFileCheck = "build/soong/root.bp"
|
const srcDirFileCheck = "build/soong/root.bp"
|
||||||
|
|
149
ui/build/path.go
149
ui/build/path.go
|
@ -1,149 +0,0 @@
|
||||||
// Copyright 2018 Google Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package build
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/google/blueprint/microfactory"
|
|
||||||
|
|
||||||
"android/soong/ui/build/paths"
|
|
||||||
)
|
|
||||||
|
|
||||||
func parsePathDir(dir string) []string {
|
|
||||||
f, err := os.Open(dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
if s, err := f.Stat(); err != nil || !s.IsDir() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
infos, err := f.Readdir(-1)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := make([]string, 0, len(infos))
|
|
||||||
for _, info := range infos {
|
|
||||||
if m := info.Mode(); !m.IsDir() && m&0111 != 0 {
|
|
||||||
ret = append(ret, info.Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetupPath(ctx Context, config Config) {
|
|
||||||
if config.pathReplaced {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.BeginTrace("path")
|
|
||||||
defer ctx.EndTrace()
|
|
||||||
|
|
||||||
origPath, _ := config.Environment().Get("PATH")
|
|
||||||
myPath := filepath.Join(config.OutDir(), ".path")
|
|
||||||
interposer := myPath + "_interposer"
|
|
||||||
|
|
||||||
var cfg microfactory.Config
|
|
||||||
cfg.Map("android/soong", "build/soong")
|
|
||||||
cfg.TrimPath, _ = filepath.Abs(".")
|
|
||||||
if _, err := microfactory.Build(&cfg, interposer, "android/soong/cmd/path_interposer"); err != nil {
|
|
||||||
ctx.Fatalln("Failed to build path interposer:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ioutil.WriteFile(interposer+"_origpath", []byte(origPath), 0777); err != nil {
|
|
||||||
ctx.Fatalln("Failed to write original path:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
entries, err := paths.LogListener(ctx.Context, interposer+"_log")
|
|
||||||
if err != nil {
|
|
||||||
ctx.Fatalln("Failed to listen for path logs:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for log := range entries {
|
|
||||||
curPid := os.Getpid()
|
|
||||||
for i, proc := range log.Parents {
|
|
||||||
if proc.Pid == curPid {
|
|
||||||
log.Parents = log.Parents[i:]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
procPrints := []string{
|
|
||||||
"See https://android.googlesource.com/platform/build/+/master/Changes.md#PATH_Tools for more information.",
|
|
||||||
}
|
|
||||||
if len(log.Parents) > 0 {
|
|
||||||
procPrints = append(procPrints, "Process tree:")
|
|
||||||
for i, proc := range log.Parents {
|
|
||||||
procPrints = append(procPrints, fmt.Sprintf("%s→ %s", strings.Repeat(" ", i), proc.Command))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config := paths.GetConfig(log.Basename)
|
|
||||||
if config.Error {
|
|
||||||
ctx.Printf("Disallowed PATH tool %q used: %#v", log.Basename, log.Args)
|
|
||||||
for _, line := range procPrints {
|
|
||||||
ctx.Println(line)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ctx.Verbosef("Unknown PATH tool %q used: %#v", log.Basename, log.Args)
|
|
||||||
for _, line := range procPrints {
|
|
||||||
ctx.Verboseln(line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
ensureEmptyDirectoriesExist(ctx, myPath)
|
|
||||||
|
|
||||||
var execs []string
|
|
||||||
for _, pathEntry := range filepath.SplitList(origPath) {
|
|
||||||
if pathEntry == "" {
|
|
||||||
// Ignore the current directory
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// TODO(dwillemsen): remove path entries under TOP? or anything
|
|
||||||
// that looks like an android source dir? They won't exist on
|
|
||||||
// the build servers, since they're added by envsetup.sh.
|
|
||||||
// (Except for the JDK, which is configured in ui/build/config.go)
|
|
||||||
|
|
||||||
execs = append(execs, parsePathDir(pathEntry)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
allowAllSymlinks := config.Environment().IsEnvTrue("TEMPORARY_DISABLE_PATH_RESTRICTIONS")
|
|
||||||
for _, name := range execs {
|
|
||||||
if !paths.GetConfig(name).Symlink && !allowAllSymlinks {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
err := os.Symlink("../.path_interposer", filepath.Join(myPath, name))
|
|
||||||
// Intentionally ignore existing files -- that means that we
|
|
||||||
// just created it, and the first one should win.
|
|
||||||
if err != nil && !os.IsExist(err) {
|
|
||||||
ctx.Fatalln("Failed to create symlink:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
myPath, _ = filepath.Abs(myPath)
|
|
||||||
config.Environment().Set("PATH", myPath)
|
|
||||||
config.pathReplaced = true
|
|
||||||
}
|
|
|
@ -1,150 +0,0 @@
|
||||||
// Copyright 2018 Google Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package paths
|
|
||||||
|
|
||||||
type PathConfig struct {
|
|
||||||
// Whether to create the symlink in the new PATH for this tool.
|
|
||||||
Symlink bool
|
|
||||||
|
|
||||||
// Whether to log about usages of this tool to the soong.log
|
|
||||||
Log bool
|
|
||||||
|
|
||||||
// Whether to exit with an error instead of invoking the underlying tool.
|
|
||||||
Error bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var Allowed = PathConfig{
|
|
||||||
Symlink: true,
|
|
||||||
Log: false,
|
|
||||||
Error: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
var Forbidden = PathConfig{
|
|
||||||
Symlink: false,
|
|
||||||
Log: true,
|
|
||||||
Error: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// The configuration used if the tool is not listed in the config below.
|
|
||||||
// Currently this will create the symlink, but log a warning. In the future,
|
|
||||||
// I expect this to move closer to Forbidden.
|
|
||||||
var Missing = PathConfig{
|
|
||||||
Symlink: true,
|
|
||||||
Log: true,
|
|
||||||
Error: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetConfig(name string) PathConfig {
|
|
||||||
if config, ok := Configuration[name]; ok {
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
return Missing
|
|
||||||
}
|
|
||||||
|
|
||||||
var Configuration = map[string]PathConfig{
|
|
||||||
"awk": Allowed,
|
|
||||||
"basename": Allowed,
|
|
||||||
"bash": Allowed,
|
|
||||||
"bzip2": Allowed,
|
|
||||||
"cat": Allowed,
|
|
||||||
"chmod": Allowed,
|
|
||||||
"cmp": Allowed,
|
|
||||||
"comm": Allowed,
|
|
||||||
"cp": Allowed,
|
|
||||||
"cut": Allowed,
|
|
||||||
"date": Allowed,
|
|
||||||
"dd": Allowed,
|
|
||||||
"diff": Allowed,
|
|
||||||
"dirname": Allowed,
|
|
||||||
"echo": Allowed,
|
|
||||||
"egrep": Allowed,
|
|
||||||
"env": Allowed,
|
|
||||||
"expr": Allowed,
|
|
||||||
"find": Allowed,
|
|
||||||
"getconf": Allowed,
|
|
||||||
"getopt": Allowed,
|
|
||||||
"git": Allowed,
|
|
||||||
"grep": Allowed,
|
|
||||||
"gzip": Allowed,
|
|
||||||
"head": Allowed,
|
|
||||||
"hexdump": Allowed,
|
|
||||||
"hostname": Allowed,
|
|
||||||
"jar": Allowed,
|
|
||||||
"java": Allowed,
|
|
||||||
"javap": Allowed,
|
|
||||||
"ln": Allowed,
|
|
||||||
"ls": Allowed,
|
|
||||||
"m4": Allowed,
|
|
||||||
"make": Allowed,
|
|
||||||
"md5sum": Allowed,
|
|
||||||
"mkdir": Allowed,
|
|
||||||
"mktemp": Allowed,
|
|
||||||
"mv": Allowed,
|
|
||||||
"openssl": Allowed,
|
|
||||||
"patch": Allowed,
|
|
||||||
"perl": Allowed,
|
|
||||||
"pstree": Allowed,
|
|
||||||
"python": Allowed,
|
|
||||||
"python2.7": Allowed,
|
|
||||||
"python3": Allowed,
|
|
||||||
"readlink": Allowed,
|
|
||||||
"realpath": Allowed,
|
|
||||||
"rm": Allowed,
|
|
||||||
"rsync": Allowed,
|
|
||||||
"runalarm": Allowed,
|
|
||||||
"sed": Allowed,
|
|
||||||
"setsid": Allowed,
|
|
||||||
"sh": Allowed,
|
|
||||||
"sha256sum": Allowed,
|
|
||||||
"sha512sum": Allowed,
|
|
||||||
"sort": Allowed,
|
|
||||||
"stat": Allowed,
|
|
||||||
"sum": Allowed,
|
|
||||||
"tar": Allowed,
|
|
||||||
"tail": Allowed,
|
|
||||||
"touch": Allowed,
|
|
||||||
"tr": Allowed,
|
|
||||||
"true": Allowed,
|
|
||||||
"uname": Allowed,
|
|
||||||
"uniq": Allowed,
|
|
||||||
"unzip": Allowed,
|
|
||||||
"wc": Allowed,
|
|
||||||
"which": Allowed,
|
|
||||||
"whoami": Allowed,
|
|
||||||
"xargs": Allowed,
|
|
||||||
"xmllint": Allowed,
|
|
||||||
"xz": Allowed,
|
|
||||||
"zip": Allowed,
|
|
||||||
"zipinfo": Allowed,
|
|
||||||
|
|
||||||
// Host toolchain is removed. In-tree toolchain should be used instead.
|
|
||||||
// GCC also can't find cc1 with this implementation.
|
|
||||||
"ar": Forbidden,
|
|
||||||
"as": Forbidden,
|
|
||||||
"cc": Forbidden,
|
|
||||||
"clang": Forbidden,
|
|
||||||
"clang++": Forbidden,
|
|
||||||
"gcc": Forbidden,
|
|
||||||
"g++": Forbidden,
|
|
||||||
"ld": Forbidden,
|
|
||||||
"ld.bfd": Forbidden,
|
|
||||||
"ld.gold": Forbidden,
|
|
||||||
"pkg-config": Forbidden,
|
|
||||||
|
|
||||||
// We've got prebuilts of these
|
|
||||||
//"dtc": Forbidden,
|
|
||||||
//"lz4": Forbidden,
|
|
||||||
//"lz4c": Forbidden,
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
// Copyright 2018 Google Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package paths
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/gob"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LogProcess struct {
|
|
||||||
Pid int
|
|
||||||
Command string
|
|
||||||
}
|
|
||||||
|
|
||||||
type LogEntry struct {
|
|
||||||
Basename string
|
|
||||||
Args []string
|
|
||||||
Parents []LogProcess
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeoutDuration = time.Duration(250) * time.Millisecond
|
|
||||||
|
|
||||||
func SendLog(logSocket string, entry *LogEntry, done chan interface{}) {
|
|
||||||
defer close(done)
|
|
||||||
|
|
||||||
dialer := &net.Dialer{}
|
|
||||||
conn, err := dialer.Dial("unix", logSocket)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
if err := conn.SetDeadline(dialer.Deadline); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
enc := gob.NewEncoder(conn)
|
|
||||||
enc.Encode(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
func LogListener(ctx context.Context, logSocket string) (chan *LogEntry, error) {
|
|
||||||
ret := make(chan *LogEntry, 5)
|
|
||||||
|
|
||||||
if err := os.Remove(logSocket); err != nil && !os.IsNotExist(err) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ln, err := net.Listen("unix", logSocket)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
ln.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer close(ret)
|
|
||||||
|
|
||||||
for {
|
|
||||||
conn, err := ln.Accept()
|
|
||||||
if err != nil {
|
|
||||||
ln.Close()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
conn.SetDeadline(time.Now().Add(timeoutDuration))
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
dec := gob.NewDecoder(conn)
|
|
||||||
entry := &LogEntry{}
|
|
||||||
if err := dec.Decode(entry); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ret <- entry
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
|
@ -1,134 +0,0 @@
|
||||||
// Copyright 2018 Google Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package paths
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSendLog(t *testing.T) {
|
|
||||||
d, err := ioutil.TempDir("", "log_socket")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(d)
|
|
||||||
f := filepath.Join(d, "sock")
|
|
||||||
|
|
||||||
ctx, _ := context.WithTimeout(context.Background(), 2*timeoutDuration)
|
|
||||||
|
|
||||||
recv, err := LogListener(ctx, f)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
SendLog(f, &LogEntry{
|
|
||||||
Basename: "test",
|
|
||||||
Args: []string{"foo", "bar"},
|
|
||||||
}, make(chan interface{}))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
count := 0
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case entry := <-recv:
|
|
||||||
ref := LogEntry{
|
|
||||||
Basename: "test",
|
|
||||||
Args: []string{"foo", "bar"},
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(ref, *entry) {
|
|
||||||
t.Fatalf("Bad log entry: %v", entry)
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
|
|
||||||
if count == 10 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case <-ctx.Done():
|
|
||||||
t.Error("Hit timeout before receiving all logs")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSendLogError(t *testing.T) {
|
|
||||||
d, err := ioutil.TempDir("", "log_socket")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(d)
|
|
||||||
|
|
||||||
t.Run("Missing file", func(t *testing.T) {
|
|
||||||
start := time.Now()
|
|
||||||
SendLog(filepath.Join(d, "missing"), &LogEntry{}, make(chan interface{}))
|
|
||||||
elapsed := time.Since(start)
|
|
||||||
if elapsed > timeoutDuration {
|
|
||||||
t.Errorf("Should have been << timeout (%s), but was %s", timeoutDuration, elapsed)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Regular file", func(t *testing.T) {
|
|
||||||
f := filepath.Join(d, "file")
|
|
||||||
if fp, err := os.Create(f); err == nil {
|
|
||||||
fp.Close()
|
|
||||||
} else {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
start := time.Now()
|
|
||||||
SendLog(f, &LogEntry{}, make(chan interface{}))
|
|
||||||
elapsed := time.Since(start)
|
|
||||||
if elapsed > timeoutDuration {
|
|
||||||
t.Errorf("Should have been << timeout (%s), but was %s", timeoutDuration, elapsed)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Reader not reading", func(t *testing.T) {
|
|
||||||
f := filepath.Join(d, "sock1")
|
|
||||||
|
|
||||||
ln, err := net.Listen("unix", f)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer ln.Close()
|
|
||||||
|
|
||||||
done := make(chan bool, 1)
|
|
||||||
go func() {
|
|
||||||
for i := 0; i < 1000; i++ {
|
|
||||||
SendLog(f, &LogEntry{
|
|
||||||
// Ensure a relatively large payload
|
|
||||||
Basename: strings.Repeat(" ", 100000),
|
|
||||||
}, make(chan interface{}))
|
|
||||||
}
|
|
||||||
done <- true
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-done:
|
|
||||||
break
|
|
||||||
case <-time.After(10 * timeoutDuration):
|
|
||||||
t.Error("Should have finished")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
Loading…
Reference in New Issue