123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 | package sandbox
import (
"errors"
"fmt"
"net"
"os"
"os/exec"
"os/signal"
"strconv"
"syscall"
"blinker/internal/proxy"
)
// BridgeCommand is the hidden subcommand that runs inside a Linux sandbox
// to give a command its proxy: "blinker __bridge <socket> <port> -- argv...".
// ApplyCommand (apply.go) is another, run in place of a command.
const BridgeCommand = "__bridge"
// RunHidden runs the hidden subcommand args (a program's os.Args) name, if
// they name one, and reports whether they did, with its exit status. Each
// program that can be the sandbox's, blinker's and the tests', calls it
// first thing.
func RunHidden(args []string) (int, bool) {
if len(args) < 2 {
return 0, false
}
switch args[1] {
case BridgeCommand:
return runBridge(args[2:]), true
case ApplyCommand:
return runApply(args[2:]), true
}
return 0, false
}
// runBridge listens on 127.0.0.1:<port>, forwards each connection to the
// Unix socket, and runs argv until it exits, returning its exit status.
func runBridge(args []string) int {
if len(args) < 4 || args[2] != "--" {
fmt.Fprintln(os.Stderr, "usage: blinker __bridge <socket> <port> -- command...")
return 2
}
socket, argv := args[0], args[3:]
port, err := strconv.Atoi(args[1])
if err != nil {
fmt.Fprintln(os.Stderr, "blinker bridge: bad port:", args[1])
return 2
}
l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
if err != nil {
fmt.Fprintln(os.Stderr, "blinker bridge:", err)
return 1
}
go func() {
for {
c, err := l.Accept()
if err != nil {
return
}
go forward(c, socket)
}
}()
cmd := exec.Command(argv[0], argv[1:]...)
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
sigs := make(chan os.Signal, 4)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
if err := cmd.Start(); err != nil {
fmt.Fprintln(os.Stderr, "blinker bridge:", err)
return 127
}
go func() {
for s := range sigs {
// Fails only once the command has exited, when there is
// nothing left to signal.
if err := cmd.Process.Signal(s); err != nil && !errors.Is(err, os.ErrProcessDone) {
fmt.Fprintln(os.Stderr, "blinker bridge:", err)
}
}
}()
err = cmd.Wait()
if ee := (*exec.ExitError)(nil); errors.As(err, &ee) {
return exitCode(ee)
}
if err != nil {
return 1
}
return 0
}
func forward(c net.Conn, socket string) {
defer c.Close()
u, err := net.Dial("unix", socket)
if err != nil {
return
}
defer u.Close()
// Either side failing just ends the connection: there is no one to
// tell.
proxy.Splice(c, c, u)
}
|