diff --git a/cmd/path_interposer/Android.bp b/cmd/path_interposer/Android.bp deleted file mode 100644 index 41a219f9c..000000000 --- a/cmd/path_interposer/Android.bp +++ /dev/null @@ -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"], -} diff --git a/cmd/path_interposer/main.go b/cmd/path_interposer/main.go deleted file mode 100644 index 01fe5d4b4..000000000 --- a/cmd/path_interposer/main.go +++ /dev/null @@ -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 _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 _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 -} diff --git a/cmd/path_interposer/main_test.go b/cmd/path_interposer/main_test.go deleted file mode 100644 index 4b25c4468..000000000 --- a/cmd/path_interposer/main_test.go +++ /dev/null @@ -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) - } -} diff --git a/python/scripts/stub_template_host.txt b/python/scripts/stub_template_host.txt index 386298ebc..b90a28b56 100644 --- a/python/scripts/stub_template_host.txt +++ b/python/scripts/stub_template_host.txt @@ -18,6 +18,8 @@ def SearchPathEnv(name): for directory in search_path: if directory == '': continue path = os.path.join(directory, name) + if os.path.islink(path): + path = os.path.realpath(path) # Check if path is actual executable file. if os.path.isfile(path) and os.access(path, os.X_OK): return path diff --git a/ui/build/Android.bp b/ui/build/Android.bp index 1fe5b6f20..580989436 100644 --- a/ui/build/Android.bp +++ b/ui/build/Android.bp @@ -12,23 +12,10 @@ // See the License for the specific language governing permissions and // 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 { name: "soong-ui-build", pkgPath: "android/soong/ui/build", deps: [ - "soong-ui-build-paths", "soong-ui-logger", "soong-ui-tracer", "soong-shared", @@ -46,7 +33,6 @@ bootstrap_go_package { "finder.go", "kati.go", "ninja.go", - "path.go", "proc_sync.go", "signal.go", "soong.go", diff --git a/ui/build/build.go b/ui/build/build.go index 66dbf03f9..78eb6a327 100644 --- a/ui/build/build.go +++ b/ui/build/build.go @@ -140,8 +140,6 @@ func Build(ctx Context, config Config, what int) { ensureEmptyDirectoriesExist(ctx, config.TempDir()) - SetupPath(ctx, config) - if what&BuildProductConfig != 0 { // Run make for product config runMakeProductConfig(ctx, config) diff --git a/ui/build/config.go b/ui/build/config.go index 6f2d24a55..5622dff64 100644 --- a/ui/build/config.go +++ b/ui/build/config.go @@ -51,8 +51,6 @@ type configImpl struct { targetDeviceDir string brokenDupRules bool - - pathReplaced bool } const srcDirFileCheck = "build/soong/root.bp" diff --git a/ui/build/path.go b/ui/build/path.go deleted file mode 100644 index 52658ef12..000000000 --- a/ui/build/path.go +++ /dev/null @@ -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 -} diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go deleted file mode 100644 index ed44cedd5..000000000 --- a/ui/build/paths/config.go +++ /dev/null @@ -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, -} diff --git a/ui/build/paths/logs.go b/ui/build/paths/logs.go deleted file mode 100644 index a44994cd2..000000000 --- a/ui/build/paths/logs.go +++ /dev/null @@ -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 -} diff --git a/ui/build/paths/logs_test.go b/ui/build/paths/logs_test.go deleted file mode 100644 index 1ec040d62..000000000 --- a/ui/build/paths/logs_test.go +++ /dev/null @@ -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") - } - }) -}