|  | // 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) | 
|  | } | 
|  |  | 
|  | if fi, err := os.Lstat(interposer); err == nil { | 
|  | if fi.Mode()&os.ModeSymlink != 0 { | 
|  | link, err := os.Readlink(interposer) | 
|  | if err != nil { | 
|  | fmt.Fprintln(os.Stderr, "Unable to read link to interposer executable:", err) | 
|  | os.Exit(1) | 
|  | } | 
|  | if filepath.IsAbs(link) { | 
|  | interposer = link | 
|  | } else { | 
|  | interposer = filepath.Join(filepath.Dir(interposer), link) | 
|  | } | 
|  | } | 
|  | } else { | 
|  | fmt.Fprintln(os.Stderr, "Unable to stat interposer executable:", err) | 
|  | os.Exit(1) | 
|  | } | 
|  |  | 
|  | exitCode, err := Main(os.Stdout, os.Stderr, interposer, os.Args, mainOpts{ | 
|  | 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 { | 
|  | 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 { | 
|  | 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 | 
|  | } |