From 8a6bd21baf126f91bb331f37733eb7203675da30 Mon Sep 17 00:00:00 2001 From: Avery Pennarun Date: Thu, 28 May 2020 04:28:04 -0400 Subject: [PATCH] router_linux: extract process runner routines into runner.go. These will probably be useful across platforms. They're not really Linux-specific at all. Signed-off-by: Avery Pennarun --- wgengine/router/router_linux.go | 76 ---------------------------- wgengine/router/runner.go | 88 +++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 76 deletions(-) create mode 100644 wgengine/router/runner.go diff --git a/wgengine/router/router_linux.go b/wgengine/router/router_linux.go index baa979167..4a72c487d 100644 --- a/wgengine/router/router_linux.go +++ b/wgengine/router/router_linux.go @@ -6,13 +6,11 @@ package router import ( "bytes" - "errors" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" - "strconv" "strings" "github.com/coreos/go-iptables/iptables" @@ -68,14 +66,6 @@ type netfilterRunner interface { DeleteChain(table, chain string) error } -// commandRunner abstracts helpers to run OS commands. It exists -// purely to swap out osCommandRunner (below) with a fake runner in -// tests. -type commandRunner interface { - run(...string) error - output(...string) ([]byte, error) -} - type linuxRouter struct { logf func(fmt string, args ...interface{}) tunname string @@ -113,72 +103,6 @@ func newUserspaceRouterAdvanced(logf logger.Logf, tunname string, netfilter netf }, nil } -type osCommandRunner struct{} - -func errCode(err error) int { - if err == nil { - return 0 - } - var e *exec.ExitError - if ok := errors.As(err, &e); ok { - return e.ExitCode() - } - s := err.Error() - if strings.HasPrefix(s, "exitcode:") { - code, err := strconv.Atoi(s[9:]) - if err == nil { - return code - } - } - return -42 -} - -func (o osCommandRunner) run(args ...string) error { - _, err := o.output(args...) - return err -} - -func (o osCommandRunner) output(args ...string) ([]byte, error) { - if len(args) == 0 { - return nil, errors.New("cmd: no argv[0]") - } - - out, err := exec.Command(args[0], args[1:]...).CombinedOutput() - if err != nil { - return nil, fmt.Errorf("running %q failed: %w\n%s", strings.Join(args, " "), err, out) - } - - return out, nil -} - -type runGroup struct { - OkCode int // an error code that is acceptable, other than 0, if any - Runner commandRunner // the runner that actually runs our commands - ErrAcc error // first error encountered, if any -} - -func newRunGroup(okCode int, runner commandRunner) *runGroup { - return &runGroup{ - OkCode: okCode, - Runner: runner, - } -} - -func (rg *runGroup) Output(args ...string) []byte { - b, err := rg.Runner.output(args...) - if rg.ErrAcc == nil && err != nil && errCode(err) != rg.OkCode { - rg.ErrAcc = err - } - return b -} - -func (rg *runGroup) Run(args ...string) { - err := rg.Runner.run(args...) - if rg.ErrAcc == nil && err != nil && errCode(err) != rg.OkCode { - rg.ErrAcc = err - } -} - func (r *linuxRouter) Up() error { if err := r.delLegacyNetfilter(); err != nil { return err diff --git a/wgengine/router/runner.go b/wgengine/router/runner.go new file mode 100644 index 000000000..4b83a352c --- /dev/null +++ b/wgengine/router/runner.go @@ -0,0 +1,88 @@ +// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package router + +import ( + "errors" + "fmt" + "os/exec" + "strconv" + "strings" +) + +// commandRunner abstracts helpers to run OS commands. It exists +// purely to swap out osCommandRunner (below) with a fake runner in +// tests. +type commandRunner interface { + run(...string) error + output(...string) ([]byte, error) +} + +type osCommandRunner struct{} + +func errCode(err error) int { + if err == nil { + return 0 + } + var e *exec.ExitError + if ok := errors.As(err, &e); ok { + return e.ExitCode() + } + s := err.Error() + if strings.HasPrefix(s, "exitcode:") { + code, err := strconv.Atoi(s[9:]) + if err == nil { + return code + } + } + return -42 +} + +func (o osCommandRunner) run(args ...string) error { + _, err := o.output(args...) + return err +} + +func (o osCommandRunner) output(args ...string) ([]byte, error) { + if len(args) == 0 { + return nil, errors.New("cmd: no argv[0]") + } + + out, err := exec.Command(args[0], args[1:]...).CombinedOutput() + if err != nil { + return nil, fmt.Errorf("running %q failed: %w\n%s", strings.Join(args, " "), err, out) + } + + return out, nil +} + +type runGroup struct { + OkCode int // an error code that is acceptable, other than 0, if any + Runner commandRunner // the runner that actually runs our commands + ErrAcc error // first error encountered, if any +} + +func newRunGroup(okCode int, runner commandRunner) *runGroup { + return &runGroup{ + OkCode: okCode, + Runner: runner, + } +} + +func (rg *runGroup) Output(args ...string) []byte { + b, err := rg.Runner.output(args...) + if rg.ErrAcc == nil && err != nil && errCode(err) != rg.OkCode { + rg.ErrAcc = err + } + return b +} + +func (rg *runGroup) Run(args ...string) { + err := rg.Runner.run(args...) + if rg.ErrAcc == nil && err != nil && errCode(err) != rg.OkCode { + rg.ErrAcc = err + } +} +