Merge ""core": Make glob-patterns the default"
diff --git a/lib/modules/core/core.go b/lib/modules/core/core.go
index c2919fa..c937287 100644
--- a/lib/modules/core/core.go
+++ b/lib/modules/core/core.go
@@ -47,9 +47,6 @@
// echoClient <name> <text>
// invoke <name>.Echo(<text>)
//
-// exec <command> [args...]
-// executes the given command with the given arguments
-//
// proxyd <names>...
// runs a proxy server
package core
@@ -67,5 +64,4 @@
ProxyServerCommand = "proxyd"
WSPRCommand = "wsprd"
TestIdentitydCommand = "test_identityd"
- ExecCommand = "exec"
)
diff --git a/lib/modules/core/core_test.go b/lib/modules/core/core_test.go
index e5071c2..15a7c6a 100644
--- a/lib/modules/core/core_test.go
+++ b/lib/modules/core/core_test.go
@@ -202,7 +202,7 @@
func TestExec(t *testing.T) {
sh, cleanup := newShell(t)
defer cleanup()
- h, err := sh.Start(core.ExecCommand, nil, []string{"/bin/sh", "-c", "echo hello world"}...)
+ h, err := sh.StartExternalCommand(nil, []string{"/bin/sh", "-c", "echo hello world"}...)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -218,7 +218,7 @@
func TestExecWithEnv(t *testing.T) {
sh, cleanup := newShell(t)
defer cleanup()
- h, err := sh.Start(core.ExecCommand, []string{"BLAH=hello world"}, "/bin/sh", "-c", "printenv BLAH")
+ h, err := sh.StartExternalCommand([]string{"BLAH=hello world"}, "/bin/sh", "-c", "printenv BLAH")
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
diff --git a/lib/modules/core/exec.go b/lib/modules/core/exec.go
deleted file mode 100644
index be41d0d..0000000
--- a/lib/modules/core/exec.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package core
-
-import (
- "io"
- "syscall"
-
- "v.io/core/veyron/lib/modules"
-)
-
-func init() {
- modules.RegisterChild(ExecCommand, "", execCommand)
-}
-
-func execCommand(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
- envSlice := []string{}
- for key, value := range env {
- envSlice = append(envSlice, key+"="+value)
- }
- return syscall.Exec(args[1], args[1:], envSlice)
-}
diff --git a/lib/modules/exec.go b/lib/modules/exec.go
index b7e77c0..547ac0d 100644
--- a/lib/modules/exec.go
+++ b/lib/modules/exec.go
@@ -70,6 +70,10 @@
return &execHandle{name: name, entryPoint: shellEntryPoint + "=" + name, procErrCh: make(chan error, 1)}
}
+func newExecHandleForExternalCommand(name string) command {
+ return &execHandle{name: name, procErrCh: make(chan error, 1)}
+}
+
func (eh *execHandle) Stdout() io.Reader {
eh.mu.Lock()
defer eh.mu.Unlock()
@@ -114,8 +118,16 @@
eh.mu.Lock()
defer eh.mu.Unlock()
eh.sh = sh
- newargs, newenv := eh.envelope(sh, env, args[1:]...)
- cmd := exec.Command(os.Args[0], newargs[1:]...)
+ cmdPath := args[0]
+ newargs, newenv := args, env
+
+ // If an entry point is specified, use the envelope execution environment.
+ if eh.entryPoint != "" {
+ cmdPath = os.Args[0]
+ newargs, newenv = eh.envelope(sh, env, args[1:]...)
+ }
+
+ cmd := exec.Command(cmdPath, newargs[1:]...)
cmd.Env = newenv
stderr, err := newLogfile("stderr", eh.name)
if err != nil {
@@ -160,7 +172,6 @@
return nil, err
}
vlog.VI(1).Infof("Started: %q, pid %d", eh.name, cmd.Process.Pid)
- err = handle.WaitForReady(sh.startTimeout)
go func() {
eh.procErrCh <- eh.handle.Wait(0)
// It's now safe to close eh.stdout, since Wait only returns
@@ -170,7 +181,7 @@
eh.stdout.Close()
}()
- return eh, err
+ return eh, nil
}
func (eh *execHandle) Pid() int {
@@ -215,3 +226,7 @@
return procErr
}
+
+func (eh *execHandle) WaitForReady(timeout time.Duration) error {
+ return eh.handle.WaitForReady(timeout)
+}
diff --git a/lib/modules/func.go b/lib/modules/func.go
index e7372e6..5c5bc4c 100644
--- a/lib/modules/func.go
+++ b/lib/modules/func.go
@@ -5,6 +5,7 @@
"io"
"os"
"sync"
+ "time"
"v.io/core/veyron2/vlog"
)
@@ -143,3 +144,7 @@
fh.mu.Unlock()
return funcErr
}
+
+func (fh *functionHandle) WaitForReady(time.Duration) error {
+ return nil
+}
diff --git a/lib/modules/registry.go b/lib/modules/registry.go
index 7b01a1e..8331f9c 100644
--- a/lib/modules/registry.go
+++ b/lib/modules/registry.go
@@ -39,7 +39,7 @@
return r.cmds[name]
}
-// RegisterChild adds a new command to the registery that will be run
+// RegisterChild adds a new command to the registry that will be run
// as a subprocess. It must be called before Dispatch or DispatchInTest is
// called, typically from an init function.
func RegisterChild(name, help string, main Main) {
diff --git a/lib/modules/shell.go b/lib/modules/shell.go
index c80aae7..cc14137 100644
--- a/lib/modules/shell.go
+++ b/lib/modules/shell.go
@@ -37,6 +37,7 @@
package modules
import (
+ "errors"
"fmt"
"io"
"io/ioutil"
@@ -135,7 +136,7 @@
return sh, nil
}
-func (sh *Shell) getChildCredentials() (*os.File, error) {
+func (sh *Shell) getChildCredentials() (c *os.File, err error) {
if sh.ctx == nil {
return nil, nil
}
@@ -145,11 +146,16 @@
if err != nil {
return nil, err
}
+ defer func() {
+ if err != nil {
+ conn.Close()
+ }
+ }()
ctx, cancel := context.WithCancel(sh.ctx)
+ defer cancel()
if ctx, _, err = veyron2.SetNewStreamManager(ctx); err != nil {
return nil, err
}
- defer cancel()
syscall.ForkLock.RLock()
fd, err := syscall.Dup(int(conn.Fd()))
if err != nil {
@@ -160,6 +166,7 @@
syscall.ForkLock.RUnlock()
p, err := agent.NewAgentPrincipal(ctx, fd, veyron2.GetClient(ctx))
if err != nil {
+ syscall.Close(fd)
return nil, err
}
blessingForChild, err := root.Bless(p.PublicKey(), rootBlessing, childBlessingExtension, security.UnconstrainedUse())
@@ -213,6 +220,14 @@
return registry.help(command)
}
+func (sh *Shell) StartExternalCommand(env []string, args ...string) (Handle, error) {
+ if len(args) == 0 {
+ return nil, errors.New("no arguments specified to StartExternalCommand")
+ }
+ c := newExecHandleForExternalCommand(args[0])
+ return sh.startCommand(c, env, args...)
+}
+
// Start starts the specified command, it returns a Handle which can be
// used for interacting with that command.
//
@@ -236,6 +251,26 @@
// Commands must have already been registered using RegisterFunction
// or RegisterChild.
func (sh *Shell) Start(name string, env []string, args ...string) (Handle, error) {
+ cmd := registry.getCommand(name)
+ if cmd == nil {
+ return nil, fmt.Errorf("%s: not registered", name)
+ }
+ expanded := append([]string{name}, sh.expand(args...)...)
+ c := cmd.factory()
+ h, err := sh.startCommand(c, env, expanded...)
+ if err != nil {
+ // If the error is a timeout, then h can be used to recover
+ // any output from the process.
+ return h, err
+ }
+
+ if err := h.WaitForReady(sh.waitTimeout); err != nil {
+ return h, err
+ }
+ return h, nil
+}
+
+func (sh *Shell) startCommand(c command, env []string, args ...string) (Handle, error) {
cenv, err := sh.setupCommandEnv(env)
if err != nil {
return nil, err
@@ -244,16 +279,10 @@
if err != nil {
return nil, err
}
- cmd := registry.getCommand(name)
- if cmd == nil {
- return nil, fmt.Errorf("%s: not registered", name)
- }
- expanded := append([]string{name}, sh.expand(args...)...)
- h, err := cmd.factory().start(sh, p, cenv, expanded...)
+
+ h, err := c.start(sh, p, cenv, args...)
if err != nil {
- // If the error is a timeout, then h can be used to recover
- // any output from the process.
- return h, err
+ return nil, err
}
sh.mu.Lock()
sh.handles[h] = struct{}{}
@@ -459,6 +488,11 @@
// Pid returns the pid of the process running the command
Pid() int
+
+ // WaitForReady waits until the child process signals to us that it is
+ // ready. If this does not occur within the given timeout duration, a
+ // timeout error is returned.
+ WaitForReady(timeout time.Duration) error
}
// command is used to abstract the implementations of inprocess and subprocess
diff --git a/lib/stats/sysstats/sysstats.go b/lib/stats/sysstats/sysstats.go
index 75c0c17..9579280 100644
--- a/lib/stats/sysstats/sysstats.go
+++ b/lib/stats/sysstats/sysstats.go
@@ -21,6 +21,7 @@
stats.NewInteger("system/num-cpu").Set(int64(runtime.NumCPU()))
stats.NewIntegerFunc("system/num-goroutine", func() int64 { return int64(runtime.NumGoroutine()) })
stats.NewString("system/version").Set(runtime.Version())
+ stats.NewInteger("system/pid").Set(int64(os.Getpid()))
if hostname, err := os.Hostname(); err == nil {
stats.NewString("system/hostname").Set(hostname)
}
diff --git a/lib/stats/sysstats/sysstats_test.go b/lib/stats/sysstats/sysstats_test.go
index ee2f835..d157b5e 100644
--- a/lib/stats/sysstats/sysstats_test.go
+++ b/lib/stats/sysstats/sysstats_test.go
@@ -31,3 +31,14 @@
t.Errorf("unexpected Alloc value. Got %v, want != 0", v)
}
}
+
+func TestPid(t *testing.T) {
+ obj, err := stats.GetStatsObject("system/pid")
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ expected := int64(os.Getpid())
+ if got := obj.Value(); got != expected {
+ t.Errorf("unexpected result. Got %q, want %q", got, expected)
+ }
+}
diff --git a/lib/testutil/integration/util.go b/lib/testutil/integration/util.go
index 46a2a6b..208fad1 100644
--- a/lib/testutil/integration/util.go
+++ b/lib/testutil/integration/util.go
@@ -262,9 +262,9 @@
locationString = fmt.Sprintf("(requested at %s:%d) ", filepath.Base(file), line)
}
b.env.t.Logf("%sstarting %s %s", locationString, b.Path(), strings.Join(args, " "))
- handle, err := b.env.shell.Start("exec", b.envVars, append([]string{b.Path()}, args...)...)
+ handle, err := b.env.shell.StartExternalCommand(b.envVars, append([]string{b.Path()}, args...)...)
if err != nil {
- b.env.t.Fatalf("Start(%v, %v) failed: %v", b.Path(), strings.Join(args, ", "), err)
+ b.env.t.Fatalf("StartExternalCommand(%v, %v) failed: %v", b.Path(), strings.Join(args, ", "), err)
}
b.env.t.Logf("started PID %d\n", handle.Pid())
return &integrationTestBinaryInvocation{
diff --git a/lib/unixfd/unixfd.go b/lib/unixfd/unixfd.go
index fd17a93..fa0fe85 100644
--- a/lib/unixfd/unixfd.go
+++ b/lib/unixfd/unixfd.go
@@ -231,12 +231,16 @@
// This is to work around a race on OS X where it appears we can close
// the file descriptor before it gets transfered over the socket.
f := local.releaseFile()
+ syscall.ForkLock.Lock()
fd, err := syscall.Dup(int(f.Fd()))
if err != nil {
+ syscall.ForkLock.Unlock()
f.Close()
rfile.Close()
return nil, err
}
+ syscall.CloseOnExec(fd)
+ syscall.ForkLock.Unlock()
newConn, err := net.FileConn(f)
f.Close()
if err != nil {
@@ -290,11 +294,15 @@
return nil, n, nil, nil
}
result := Addr(uintptr(fd))
+ syscall.ForkLock.Lock()
fd, err = syscall.Dup(fd)
if err != nil {
+ syscall.ForkLock.Unlock()
CloseUnixAddr(result)
return nil, n, nil, err
}
+ syscall.CloseOnExec(fd)
+ syscall.ForkLock.Unlock()
file := os.NewFile(uintptr(fd), "newconn")
newconn, err := net.FileConn(file)
file.Close()
diff --git a/profiles/chrome/chromeinit.go b/profiles/chrome/chromeinit.go
index 9f4ace2..1019de9 100644
--- a/profiles/chrome/chromeinit.go
+++ b/profiles/chrome/chromeinit.go
@@ -7,6 +7,7 @@
"v.io/core/veyron2"
"v.io/core/veyron2/context"
+ "v.io/core/veyron2/ipc"
"v.io/core/veyron2/ipc/stream"
"v.io/core/veyron2/vlog"
@@ -31,7 +32,9 @@
return nil, nil, nil, err
}
- runtime, ctx, shutdown, err := grt.Init(ctx, nil, nil, nil, commonFlags.RuntimeFlags(), nil)
+ protocols := []string{"wsh", "ws"}
+ listenSpec := ipc.ListenSpec{Addrs: ipc.ListenAddrs{{Protocol: "ws", Address: ""}}}
+ runtime, ctx, shutdown, err := grt.Init(ctx, nil, protocols, &listenSpec, commonFlags.RuntimeFlags(), nil)
if err != nil {
return nil, nil, shutdown, err
}
diff --git a/profiles/roaming/roaming_server.go b/profiles/roaming/roaming_server.go
index e85f22e..30874be 100644
--- a/profiles/roaming/roaming_server.go
+++ b/profiles/roaming/roaming_server.go
@@ -7,6 +7,7 @@
"v.io/core/veyron2"
"v.io/core/veyron2/ipc"
+ "v.io/core/veyron2/vlog"
"v.io/core/veyron/profiles/roaming"
)
@@ -14,11 +15,10 @@
func main() {
ctx, shutdown := veyron2.Init()
defer shutdown()
- log := veyron2.GetLogger(ctx)
server, err := veyron2.NewServer(ctx)
if err != nil {
- log.Fatalf("unexpected error: %q", err)
+ vlog.Fatalf("unexpected error: %q", err)
}
listenSpec := veyron2.GetListenSpec(ctx)
@@ -26,13 +26,13 @@
fmt.Printf("listen spec: %v\n", listenSpec)
ep, err := server.Listen(listenSpec)
if err != nil {
- log.Fatalf("unexpected error: %q", err)
+ vlog.Fatalf("unexpected error: %q", err)
}
if ep != nil {
fmt.Println(ep)
}
if err := server.Serve("roamer", &receiver{}, nil); err != nil {
- log.Fatalf("unexpected error: %q", err)
+ vlog.Fatalf("unexpected error: %q", err)
}
done := make(chan struct{})
diff --git a/profiles/roaming/roaminginit.go b/profiles/roaming/roaminginit.go
index 48350e4..45a5f56 100644
--- a/profiles/roaming/roaminginit.go
+++ b/profiles/roaming/roaminginit.go
@@ -139,7 +139,6 @@
ch chan<- config.Setting) {
defer close(ch)
- log := runtime.GetLogger(ctx)
listenSpec := runtime.GetListenSpec(ctx)
// TODO(cnicolaou): add support for listening on multiple network addresses.
@@ -150,21 +149,21 @@
case <-watcher.Channel():
cur, err := netstate.GetAccessibleIPs()
if err != nil {
- log.Errorf("failed to read network state: %s", err)
+ vlog.Errorf("failed to read network state: %s", err)
continue
}
removed := netstate.FindRemoved(prev, cur)
added := netstate.FindAdded(prev, cur)
- log.VI(2).Infof("Previous: %d: %s", len(prev), prev)
- log.VI(2).Infof("Current : %d: %s", len(cur), cur)
- log.VI(2).Infof("Added : %d: %s", len(added), added)
- log.VI(2).Infof("Removed : %d: %s", len(removed), removed)
+ vlog.VI(2).Infof("Previous: %d: %s", len(prev), prev)
+ vlog.VI(2).Infof("Current : %d: %s", len(cur), cur)
+ vlog.VI(2).Infof("Added : %d: %s", len(added), added)
+ vlog.VI(2).Infof("Removed : %d: %s", len(removed), removed)
if len(removed) == 0 && len(added) == 0 {
- log.VI(2).Infof("Network event that lead to no address changes since our last 'baseline'")
+ vlog.VI(2).Infof("Network event that lead to no address changes since our last 'baseline'")
continue
}
if len(removed) > 0 {
- log.VI(2).Infof("Sending removed: %s", removed)
+ vlog.VI(2).Infof("Sending removed: %s", removed)
ch <- ipc.NewRmAddrsSetting(removed)
}
// We will always send the best currently available address
diff --git a/runtimes/fake/runtime.go b/runtimes/fake/runtime.go
index baa6386..4f5bd29 100644
--- a/runtimes/fake/runtime.go
+++ b/runtimes/fake/runtime.go
@@ -7,7 +7,6 @@
"v.io/core/veyron2"
"v.io/core/veyron2/context"
"v.io/core/veyron2/security"
- "v.io/core/veyron2/vlog"
tsecurity "v.io/core/veyron/lib/testutil/security"
)
@@ -28,6 +27,10 @@
return &Runtime{}, ctx, func() {}, nil
}
+func (r *Runtime) Init(ctx *context.T) error {
+ return nil
+}
+
func (r *Runtime) SetPrincipal(ctx *context.T, principal security.Principal) (*context.T, error) {
return context.WithValue(ctx, principalKey, principal), nil
}
@@ -37,19 +40,6 @@
return p
}
-func (r *Runtime) SetNewLogger(ctx *context.T, name string, opts ...vlog.LoggingOpts) (*context.T, vlog.Logger, error) {
- logger, err := vlog.NewLogger(name, opts...)
- if err != nil {
- return context.WithValue(ctx, loggerKey, logger), logger, nil
- }
- return ctx, nil, err
-}
-
-func (r *Runtime) GetLogger(ctx *context.T) vlog.Logger {
- l, _ := ctx.Value(loggerKey).(vlog.Logger)
- return l
-}
-
func (r *Runtime) GetAppCycle(ctx *context.T) veyron2.AppCycle {
panic("unimplemented")
}
diff --git a/runtimes/google/ipc/client.go b/runtimes/google/ipc/client.go
index de89583..c0d41df 100644
--- a/runtimes/google/ipc/client.go
+++ b/runtimes/google/ipc/client.go
@@ -79,6 +79,9 @@
errAuthNoPatternMatch = verror.Register(pkgPath+".authNoPatternMatch",
verror.NoRetry, "server blessings {3} do not match pattern {4}")
+ errAuthServerNotAllowed = verror.Register(pkgPath+".authServerNotAllowed",
+ verror.NoRetry, "set of allowed servers {3} not matched by server blessings {4}")
+
errDefaultAuthDenied = verror.Register(pkgPath+".defaultAuthDenied", verror.NoRetry, "default authorization precludes talking to server with blessings{:3}")
errBlessingGrant = verror.Register(pkgPath+".blessingGrantFailed", verror.NoRetry, "failed to grant blessing to server with blessings {3}{:4}")
@@ -122,6 +125,13 @@
encrypted bool
}
+// PreferredProtocols instructs the Runtime implementation to select
+// endpoints with the specified protocols and to order them in the
+// specified order.
+type PreferredProtocols []string
+
+func (PreferredProtocols) IPCClientOpt() {}
+
func InternalNewClient(streamMgr stream.Manager, ns naming.Namespace, opts ...ipc.ClientOpt) (ipc.Client, error) {
c := &client{
streamMgr: streamMgr,
@@ -135,7 +145,7 @@
switch v := opt.(type) {
case stream.VCOpt:
c.vcOpts = append(c.vcOpts, v)
- case options.PreferredProtocols:
+ case PreferredProtocols:
c.preferredProtocols = v
}
}
@@ -654,6 +664,17 @@
}
for _, o := range opts {
switch v := o.(type) {
+ case options.AllowedServersPolicy:
+ allowed := false
+ for _, p := range v {
+ if p.MatchedBy(serverBlessings...) {
+ allowed = true
+ break
+ }
+ }
+ if !allowed {
+ return nil, nil, verror.Make(errAuthServerNotAllowed, ctx, v, serverBlessings)
+ }
case ipc.Granter:
if b, err := v.Grant(flow.RemoteBlessings()); err != nil {
return nil, nil, verror.Make(errBlessingGrant, ctx, serverBlessings, err)
diff --git a/runtimes/google/ipc/debug_test.go b/runtimes/google/ipc/debug_test.go
index 7290cd5..547d350 100644
--- a/runtimes/google/ipc/debug_test.go
+++ b/runtimes/google/ipc/debug_test.go
@@ -36,7 +36,7 @@
defer sm.Shutdown()
ns := tnaming.NewSimpleNamespace()
ctx := testContext()
- server, err := InternalNewServer(ctx, sm, ns, nil, options.ReservedNameDispatcher{debugDisp}, vc.LocalPrincipal{pserver})
+ server, err := InternalNewServer(ctx, sm, ns, nil, ReservedNameDispatcher{debugDisp}, vc.LocalPrincipal{pserver})
if err != nil {
t.Fatalf("InternalNewServer failed: %v", err)
}
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index 45f3a61..f46b0b7 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -6,7 +6,6 @@
"fmt"
"io"
"net"
- "os"
"path/filepath"
"reflect"
"runtime"
@@ -352,10 +351,8 @@
}
func matchesErrorPattern(err error, id verror.IDAction, pattern string) bool {
- if len(pattern) > 0 && err != nil {
- if strings.Index(err.Error(), pattern) < 0 {
- fmt.Fprintf(os.Stderr, "got error msg: %q, expected: %q\n", err, pattern)
- }
+ if len(pattern) > 0 && err != nil && strings.Index(err.Error(), pattern) < 0 {
+ return false
}
if err == nil && id.ID == "" {
return true
@@ -427,73 +424,91 @@
func TestRPCServerAuthorization(t *testing.T) {
const (
- vcErr = "VC handshake failed"
- nameErr = "do not match pattern"
+ vcErr = "VC handshake failed"
+ nameErr = "do not match pattern"
+ allowedErr = "set of allowed servers"
)
var (
pprovider, pclient, pserver = tsecurity.NewPrincipal("root"), tsecurity.NewPrincipal(), tsecurity.NewPrincipal()
pdischarger = pprovider
-
- now = time.Now()
-
- serverName = "mountpoint/server"
- dischargeServerName = "mountpoint/dischargeserver"
+ now = time.Now()
+ noErrID verror.IDAction
// Third-party caveats on blessings presented by server.
- cavTPValid = mkThirdPartyCaveat(pdischarger.PublicKey(), dischargeServerName, mkCaveat(security.ExpiryCaveat(now.Add(24*time.Hour))))
- cavTPExpired = mkThirdPartyCaveat(pdischarger.PublicKey(), dischargeServerName, mkCaveat(security.ExpiryCaveat(now.Add(-1*time.Second))))
+ cavTPValid = mkThirdPartyCaveat(pdischarger.PublicKey(), "mountpoint/dischargeserver", mkCaveat(security.ExpiryCaveat(now.Add(24*time.Hour))))
+ cavTPExpired = mkThirdPartyCaveat(pdischarger.PublicKey(), "mountpoint/dischargeserver", mkCaveat(security.ExpiryCaveat(now.Add(-1*time.Second))))
// Server blessings.
bServer = bless(pprovider, pserver, "server")
bServerExpired = bless(pprovider, pserver, "server", mkCaveat(security.ExpiryCaveat(time.Now().Add(-1*time.Second))))
bServerTPValid = bless(pprovider, pserver, "serverWithTPCaveats", cavTPValid)
bServerTPExpired = bless(pprovider, pserver, "serverWithTPCaveats", cavTPExpired)
+ bTwoBlessings, _ = security.UnionOfBlessings(bServer, bServerTPValid)
mgr = imanager.InternalNew(naming.FixedRoutingID(0x1111111))
ns = tnaming.NewSimpleNamespace()
tests = []struct {
- server security.Blessings // blessings presented by the server to the client.
- pattern security.BlessingPattern // pattern on the server identity expected by the client.
+ server security.Blessings // blessings presented by the server to the client.
+ name string // name provided by the client to StartCall
+ allowed options.AllowedServersPolicy // option provided to StartCall.
errID verror.IDAction
err string
}{
- // Client accepts talking to the server only if the server's blessings match the provided pattern
- {bServer, security.AllPrincipals, verror.IDAction{}, ""},
- {bServer, "root/server", verror.IDAction{}, ""},
- {bServer, "root/otherserver", verror.NotTrusted, nameErr},
- {bServer, "otherroot/server", verror.NotTrusted, nameErr},
+ // Client accepts talking to the server only if the
+ // server's blessings match the provided pattern
+ {bServer, "mountpoint/server", nil, noErrID, ""},
+ {bServer, "[root/server]mountpoint/server", nil, noErrID, ""},
+ {bServer, "[root/otherserver]mountpoint/server", nil, verror.NotTrusted, nameErr},
+ {bServer, "[otherroot/server]mountpoint/server", nil, verror.NotTrusted, nameErr},
- // and, if the server's blessing has third-party caveats then the server provides
- // appropriate discharges.
- {bServerTPValid, security.AllPrincipals, verror.IDAction{}, ""},
- {bServerTPValid, "root/serverWithTPCaveats", verror.IDAction{}, ""},
- {bServerTPValid, "root/otherserver", verror.NotTrusted, nameErr},
- {bServerTPValid, "otherroot/server", verror.NotTrusted, nameErr},
+ // and, if the server's blessing has third-party
+ // caveats then the server provides appropriate
+ // discharges.
+ {bServerTPValid, "mountpoint/server", nil, noErrID, ""},
+ {bServerTPValid, "[root/serverWithTPCaveats]mountpoint/server", nil, noErrID, ""},
+ {bServerTPValid, "[root/otherserver]mountpoint/server", nil, verror.NotTrusted, nameErr},
+ {bServerTPValid, "[otherroot/server]mountpoint/server", nil, verror.NotTrusted, nameErr},
- // Client does not talk to a server that presents expired blessings.
- {bServerExpired, security.AllPrincipals, verror.NotTrusted, vcErr},
+ // Client does not talk to a server that presents
+ // expired blessings (because the blessing store is
+ // configured to only talk to root/...).
+ {bServerExpired, "mountpoint/server", nil, verror.NotTrusted, vcErr},
- // Client does not talk to a server that fails to provide discharges for
- // third-party caveats on the blessings presented by it.
- {bServerTPExpired, security.AllPrincipals, verror.NotTrusted, vcErr},
+ // Client does not talk to a server that fails to
+ // provide discharges for third-party caveats on the
+ // blessings presented by it.
+ {bServerTPExpired, "mountpoint/server", nil, verror.NotTrusted, vcErr},
+
+ // Testing the AllowedServersPolicy option.
+ {bServer, "mountpoint/server", options.AllowedServersPolicy{"otherroot/..."}, verror.NotTrusted, allowedErr},
+ {bServer, "[root/server]mountpoint/server", options.AllowedServersPolicy{"otherroot/..."}, verror.NotTrusted, allowedErr},
+ {bServer, "[otherroot/server]mountpoint/server", options.AllowedServersPolicy{"root/server"}, verror.NotTrusted, nameErr},
+ {bServer, "[root/server]mountpoint/server", options.AllowedServersPolicy{"root/..."}, noErrID, ""},
+ // Server presents two blessings: One that satisfies
+ // the pattern provided to StartCall and one that
+ // satisfies the AllowedServersPolicy, so the server is
+ // authorized.
+ {bTwoBlessings, "[root/serverWithTPCaveats]mountpoint/server", options.AllowedServersPolicy{"root/server"}, noErrID, ""},
}
)
- _, server := startServer(t, pserver, mgr, ns, serverName, testServerDisp{&testServer{}})
- defer stopServer(t, server, ns, serverName)
+ _, server := startServer(t, pserver, mgr, ns, "mountpoint/server", testServerDisp{&testServer{}})
+ defer stopServer(t, server, ns, "mountpoint/server")
// Start the discharge server.
- _, dischargeServer := startServer(t, pdischarger, mgr, ns, dischargeServerName, testutil.LeafDispatcher(&dischargeServer{}, &acceptAllAuthorizer{}))
- defer stopServer(t, dischargeServer, ns, dischargeServerName)
+ _, dischargeServer := startServer(t, pdischarger, mgr, ns, "mountpoint/dischargeserver", testutil.LeafDispatcher(&dischargeServer{}, &acceptAllAuthorizer{}))
+ defer stopServer(t, dischargeServer, ns, "mountpoint/dischargeserver")
- // Make the client and server principals trust root certificates from pprovider
+ // Make the client and server principals trust root certificates from
+ // pprovider
pclient.AddToRoots(pprovider.BlessingStore().Default())
pserver.AddToRoots(pprovider.BlessingStore().Default())
- // Set a blessing that the client is willing to share with servers with blessings
- // from pprovider.
+ // Set a blessing that the client is willing to share with servers with
+ // blessings from pprovider.
pclient.BlessingStore().Set(bless(pprovider, pclient, "client"), "root/...")
+
for i, test := range tests {
- name := fmt.Sprintf("(%q@%q)", test.pattern, test.server)
+ name := fmt.Sprintf("(Name:%q, Server:%q, Allowed:%v)", test.name, test.server, test.allowed)
if err := pserver.BlessingStore().SetDefault(test.server); err != nil {
t.Fatalf("SetDefault failed on server's BlessingStore: %v", err)
}
@@ -506,9 +521,12 @@
t.Errorf("%s: failed to create client: %v", name, err)
continue
}
- ctx := testContextWithoutDeadline()
- dctx, cancel := context.WithTimeout(ctx, 10*time.Second)
- call, err := client.StartCall(dctx, fmt.Sprintf("[%s]%s/suffix", test.pattern, serverName), "Method", nil)
+ ctx, cancel := context.WithTimeout(testContextWithoutDeadline(), 10*time.Second)
+ var opts []ipc.CallOpt
+ if test.allowed != nil {
+ opts = append(opts, test.allowed)
+ }
+ call, err := client.StartCall(ctx, test.name, "Method", nil, opts...)
if !matchesErrorPattern(err, test.errID, test.err) {
t.Errorf(`%d: %s: client.StartCall: got error "%v", want to match "%v"`, i, name, err, test.err)
} else if call != nil {
@@ -516,8 +534,12 @@
if proof == nil {
t.Errorf("%s: Returned nil for remote blessings", name)
}
- if !test.pattern.MatchedBy(blessings...) {
- t.Errorf("%s: %q.MatchedBy(%v) failed", name, test.pattern, blessings)
+ // Currently all tests are configured so that the only
+ // blessings presented by the server that are
+ // recognized by the client match the pattern
+ // "root/..."
+ if len(blessings) < 1 || !security.BlessingPattern("root/...").MatchedBy(blessings...) {
+ t.Errorf("%s: Client sees server as %v, expected a single blessing matching root/...", name, blessings)
}
}
cancel()
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index 26fd9f0..12eeba0 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -138,6 +138,14 @@
func (PreferredServerResolveProtocols) IPCServerOpt() {}
+// ReservedNameDispatcher specifies the dispatcher that controls access
+// to framework managed portion of the namespace.
+type ReservedNameDispatcher struct {
+ Dispatcher ipc.Dispatcher
+}
+
+func (ReservedNameDispatcher) IPCServerOpt() {}
+
func InternalNewServer(ctx *context.T, streamMgr stream.Manager, ns naming.Namespace, opts ...ipc.ServerOpt) (ipc.Server, error) {
ctx, _ = vtrace.SetNewSpan(ctx, "NewServer")
statsPrefix := naming.Join("ipc", "server", "routing-id", streamMgr.RoutingID().String())
@@ -170,7 +178,7 @@
}
case options.ServesMountTable:
s.servesMountTable = bool(opt)
- case options.ReservedNameDispatcher:
+ case ReservedNameDispatcher:
s.dispReserved = opt.Dispatcher
case PreferredServerResolveProtocols:
s.preferredProtocols = []string(opt)
diff --git a/runtimes/google/ipc/server_test.go b/runtimes/google/ipc/server_test.go
index 14548b3..8502416 100644
--- a/runtimes/google/ipc/server_test.go
+++ b/runtimes/google/ipc/server_test.go
@@ -408,10 +408,19 @@
if err := <-progress; err != nil {
t.Fatalf(err.Error())
}
- // Now that the the RPC is done the server should be able to stop.
- status = server.Status()
- if got, want := status.State, ipc.ServerStopped; got != want {
- t.Fatalf("got %s, want %s", got, want)
+
+ // Now that the RPC is done, the server should be able to stop.
+ then = time.Now()
+ for {
+ status = server.Status()
+ if got, want := status.State, ipc.ServerStopped; got != want {
+ if time.Now().Sub(then) > time.Minute {
+ t.Fatalf("got %s, want %s", got, want)
+ }
+ } else {
+ break
+ }
+ time.Sleep(100 * time.Millisecond)
}
}
diff --git a/runtimes/google/rt/mgmt.go b/runtimes/google/rt/mgmt.go
index ca0f7e3..ccaad62 100644
--- a/runtimes/google/rt/mgmt.go
+++ b/runtimes/google/rt/mgmt.go
@@ -14,13 +14,17 @@
"v.io/core/veyron/lib/exec"
)
-func (rt *Runtime) initMgmt(ctx *context.T, appCycle veyron2.AppCycle, handle *exec.ChildHandle) error {
- // Do not initialize the mgmt runtime if the process has not
- // been started through the veyron exec library by a device
- // manager.
- if handle == nil {
+func (rt *Runtime) initMgmt(ctx *context.T) error {
+ handle, err := exec.GetChildHandle()
+ if err == exec.ErrNoVersion {
+ // Do not initialize the mgmt runtime if the process has not
+ // been started through the veyron exec library by a device
+ // manager.
return nil
+ } else if err != nil {
+ return err
}
+
parentName, err := handle.Config.Get(mgmt.ParentNameConfigKey)
if err != nil {
return nil
@@ -45,7 +49,7 @@
if err != nil {
return err
}
- if err := server.Serve("", appCycle.Remote(), nil); err != nil {
+ if err := server.Serve("", veyron2.GetAppCycle(ctx).Remote(), nil); err != nil {
server.Stop()
return err
}
@@ -55,6 +59,13 @@
return err
}
+ // Note(mattr): that we ignore the error here.
+ // As far as I can tell this is because the modules framework actually calls
+ // SetReady(), so we would then call it here a second time and get
+ // an error.
+ // TODO(mattr): We should change the modules framework so we can properly check
+ // errors here.
+ handle.SetReady()
return nil
}
@@ -79,7 +90,7 @@
func (rt *Runtime) callbackToParent(ctx *context.T, parentName, myName string) error {
ctx, _ = context.WithTimeout(ctx, time.Minute)
- call, err := rt.GetClient(ctx).StartCall(ctx, parentName, "Set", []interface{}{mgmt.AppCycleManagerConfigKey, myName}, options.NoResolve{})
+ call, err := rt.GetClient(ctx).StartCall(ctx, parentName, "Set", []interface{}{mgmt.AppCycleManagerConfigKey, myName})
if err != nil {
return err
diff --git a/runtimes/google/rt/rt_test.go b/runtimes/google/rt/rt_test.go
index ce3a6a4..a4e473d 100644
--- a/runtimes/google/rt/rt_test.go
+++ b/runtimes/google/rt/rt_test.go
@@ -36,7 +36,7 @@
ctx, shutdown := veyron2.Init()
defer shutdown()
- l := veyron2.GetLogger(ctx)
+ l := vlog.Log
fmt.Println(l)
args := fmt.Sprintf("%s", l)
expected := regexp.MustCompile("name=veyron logdirs=\\[/tmp\\] logtostderr=true|false alsologtostderr=false|true max_stack_buf_size=4292608 v=[0-9] stderrthreshold=2 vmodule= log_backtrace_at=:0")
@@ -59,10 +59,10 @@
}
func child(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
- ctx, shutdown := veyron2.Init()
+ _, shutdown := veyron2.Init()
defer shutdown()
- logger := veyron2.GetLogger(ctx)
+ logger := vlog.Log
vlog.Infof("%s\n", logger)
fmt.Fprintf(stdout, "%s\n", logger)
modules.WaitForEOF(stdin)
diff --git a/runtimes/google/rt/runtime.go b/runtimes/google/rt/runtime.go
index b74bc10..4a5d3da 100644
--- a/runtimes/google/rt/runtime.go
+++ b/runtimes/google/rt/runtime.go
@@ -15,13 +15,11 @@
"v.io/core/veyron2/ipc"
"v.io/core/veyron2/ipc/stream"
"v.io/core/veyron2/naming"
- "v.io/core/veyron2/options"
"v.io/core/veyron2/security"
"v.io/core/veyron2/verror2"
"v.io/core/veyron2/vlog"
"v.io/core/veyron2/vtrace"
- "v.io/core/veyron/lib/exec"
"v.io/core/veyron/lib/flags"
_ "v.io/core/veyron/lib/stats/sysstats"
iipc "v.io/core/veyron/runtimes/google/ipc"
@@ -39,7 +37,6 @@
streamManagerKey = contextKey(iota)
clientKey
namespaceKey
- loggerKey
principalKey
reservedNameKey
profileKey
@@ -63,26 +60,16 @@
opts []ipc.ServerOpt
}
-// TODO(mattr,suharshs): Decide if ROpts would be better than this.
+// TODO(mattr,suharshs): Decide if Options would be better than this.
func Init(ctx *context.T, appCycle veyron2.AppCycle, protocols []string, listenSpec *ipc.ListenSpec, flags flags.RuntimeFlags,
reservedDispatcher ipc.Dispatcher, dispatcherOpts ...ipc.ServerOpt) (*Runtime, *context.T, veyron2.Shutdown, error) {
r := &Runtime{deps: dependency.NewGraph()}
- handle, err := exec.GetChildHandle()
- switch err {
- case exec.ErrNoVersion:
- // The process has not been started through the veyron exec
- // library. No further action is needed.
- case nil:
- // The process has been started through the veyron exec
- // library.
- default:
+ err := vlog.ConfigureLibraryLoggerFromFlags()
+ if err != nil {
return nil, nil, nil, err
}
- r.initLogging(ctx)
- ctx = context.WithValue(ctx, loggerKey, vlog.Log)
-
// Setup the initial trace.
ctx, err = ivtrace.Init(ctx, flags.Vtrace)
if err != nil {
@@ -148,7 +135,7 @@
}
// Initialize security.
- principal, err := initSecurity(ctx, handle, flags.Credentials, client)
+ principal, err := initSecurity(ctx, flags.Credentials, client)
if err != nil {
return nil, nil, nil, err
}
@@ -160,20 +147,11 @@
return nil, nil, nil, err
}
- // Initialize management.
- if err := r.initMgmt(ctx, r.GetAppCycle(ctx), handle); err != nil {
- return nil, nil, nil, err
- }
-
ctx = r.SetBackgroundContext(ctx)
// TODO(suharshs,mattr): Go through the rt.Cleanup function and make sure everything
// gets cleaned up.
- if handle != nil {
- handle.SetReady()
- }
-
return r, ctx, r.shutdown, nil
}
@@ -192,17 +170,15 @@
return nil
}
+func (r *Runtime) Init(ctx *context.T) error {
+ return r.initMgmt(ctx)
+}
+
func (r *Runtime) shutdown() {
r.deps.CloseAndWaitForAll()
vlog.FlushLog()
}
-// initLogging configures logging for the runtime. It needs to be called after
-// flag.Parse and after signal handling has been initialized.
-func (r *Runtime) initLogging(ctx *context.T) error {
- return vlog.ConfigureLibraryLoggerFromFlags()
-}
-
func (r *Runtime) initSignalHandling(ctx *context.T) {
// TODO(caprita): Given that our device manager implementation is to
// kill all child apps when the device manager dies, we should
@@ -246,7 +222,7 @@
otherOpts := append([]ipc.ServerOpt{}, opts...)
otherOpts = append(otherOpts, vc.LocalPrincipal{principal})
if reserved, ok := ctx.Value(reservedNameKey).(*reservedNameDispatcher); ok {
- otherOpts = append(otherOpts, options.ReservedNameDispatcher{reserved.dispatcher})
+ otherOpts = append(otherOpts, iipc.ReservedNameDispatcher{reserved.dispatcher})
otherOpts = append(otherOpts, reserved.opts...)
}
if protocols, ok := ctx.Value(protocolsKey).([]string); ok {
@@ -346,7 +322,7 @@
otherOpts = append(otherOpts, vc.LocalPrincipal{p}, &imanager.DialTimeout{5 * time.Minute})
if protocols, ok := ctx.Value(protocolsKey).([]string); ok {
- otherOpts = append(otherOpts, options.PreferredProtocols(protocols))
+ otherOpts = append(otherOpts, iipc.PreferredProtocols(protocols))
}
client, err := iipc.InternalNewClient(sm, ns, otherOpts...)
@@ -394,19 +370,6 @@
return ns
}
-func (*Runtime) SetNewLogger(ctx *context.T, name string, opts ...vlog.LoggingOpts) (*context.T, vlog.Logger, error) {
- logger, err := vlog.NewLogger(name, opts...)
- if err == nil {
- ctx = context.WithValue(ctx, loggerKey, logger)
- }
- return ctx, logger, err
-}
-
-func (*Runtime) GetLogger(ctx *context.T) vlog.Logger {
- logger, _ := ctx.Value(loggerKey).(vlog.Logger)
- return logger
-}
-
func (*Runtime) GetAppCycle(ctx *context.T) veyron2.AppCycle {
appCycle, _ := ctx.Value(appCycleKey).(veyron2.AppCycle)
return appCycle
diff --git a/runtimes/google/rt/security.go b/runtimes/google/rt/security.go
index ab640df..b4a3aef 100644
--- a/runtimes/google/rt/security.go
+++ b/runtimes/google/rt/security.go
@@ -18,8 +18,8 @@
"v.io/core/veyron/security/agent"
)
-func initSecurity(ctx *context.T, handle *exec.ChildHandle, credentials string, client ipc.Client) (security.Principal, error) {
- principal, err := setupPrincipal(ctx, handle, credentials, client)
+func initSecurity(ctx *context.T, credentials string, client ipc.Client) (security.Principal, error) {
+ principal, err := setupPrincipal(ctx, credentials, client)
if err != nil {
return nil, err
}
@@ -31,13 +31,13 @@
return principal, nil
}
-func setupPrincipal(ctx *context.T, handle *exec.ChildHandle, credentials string, client ipc.Client) (security.Principal, error) {
+func setupPrincipal(ctx *context.T, credentials string, client ipc.Client) (security.Principal, error) {
var err error
var principal security.Principal
if principal, _ = ctx.Value(principalKey).(security.Principal); principal != nil {
return principal, nil
}
- if fd, err := agentFD(handle); err != nil {
+ if fd, err := agentFD(); err != nil {
return nil, err
} else if fd >= 0 {
return connectToAgent(ctx, fd, client)
@@ -66,7 +66,11 @@
// agentFD returns a non-negative file descriptor to be used to communicate with
// the security agent if the current process has been configured to use the
// agent.
-func agentFD(handle *exec.ChildHandle) (int, error) {
+func agentFD() (int, error) {
+ handle, err := exec.GetChildHandle()
+ if err != nil && err != exec.ErrNoVersion {
+ return -1, err
+ }
var fd string
if handle != nil {
// We were started by a parent (presumably, device manager).
diff --git a/security/agent/agentd/main.go b/security/agent/agentd/main.go
index c011d8a..ec7babd 100644
--- a/security/agent/agentd/main.go
+++ b/security/agent/agentd/main.go
@@ -81,13 +81,11 @@
vlog.Panic("failed to set principal for ctx: %v", err)
}
- log := veyron2.GetLogger(ctx)
-
if err = os.Setenv(agent.FdVarName, "3"); err != nil {
- log.Fatalf("setenv: %v", err)
+ vlog.Fatalf("setenv: %v", err)
}
if err = os.Setenv(consts.VeyronCredentials, ""); err != nil {
- log.Fatalf("setenv: %v", err)
+ vlog.Fatalf("setenv: %v", err)
}
if *keypath == "" && passphrase != nil {
@@ -101,11 +99,11 @@
// Start running our server.
var sock, mgrSock *os.File
if sock, err = server.RunAnonymousAgent(ctx, p); err != nil {
- log.Fatalf("RunAnonymousAgent: %v", err)
+ vlog.Fatalf("RunAnonymousAgent: %v", err)
}
if *keypath != "" {
if mgrSock, err = server.RunKeyManager(ctx, *keypath, passphrase); err != nil {
- log.Fatalf("RunKeyManager: %v", err)
+ vlog.Fatalf("RunKeyManager: %v", err)
}
}
@@ -123,7 +121,7 @@
err = cmd.Start()
if err != nil {
- log.Fatalf("Error starting child: %v", err)
+ vlog.Fatalf("Error starting child: %v", err)
}
shutdown := make(chan struct{})
go func() {
diff --git a/security/agent/client.go b/security/agent/client.go
index f3128f9..77c6dde 100644
--- a/security/agent/client.go
+++ b/security/agent/client.go
@@ -57,7 +57,8 @@
// NewAgentPrincipal returns a security.Pricipal using the PrivateKey held in a remote agent process.
// 'fd' is the socket for connecting to the agent, typically obtained from
// os.GetEnv(agent.FdVarName).
-// 'ctx' should not have a deadline, and should never be cancelled.
+// 'ctx' should not have a deadline, and should never be cancelled while the
+// principal is in use.
func NewAgentPrincipal(ctx *context.T, fd int, insecureClient ipc.Client) (security.Principal, error) {
f := os.NewFile(uintptr(fd), "agent_client")
defer f.Close()
diff --git a/security/agent/pingpong/main.go b/security/agent/pingpong/main.go
index 532edd9..4b8c428 100644
--- a/security/agent/pingpong/main.go
+++ b/security/agent/pingpong/main.go
@@ -8,6 +8,7 @@
"v.io/core/veyron2/context"
"v.io/core/veyron2/ipc"
"v.io/core/veyron2/security"
+ "v.io/core/veyron2/vlog"
"v.io/core/veyron/lib/signals"
_ "v.io/core/veyron/profiles"
@@ -22,24 +23,22 @@
}
func clientMain(ctx *context.T) {
- log := veyron2.GetLogger(ctx)
- log.Info("Pinging...")
+ vlog.Info("Pinging...")
s := PingPongClient("pingpong")
pong, err := s.Ping(ctx, "ping")
if err != nil {
- log.Fatal("error pinging: ", err)
+ vlog.Fatal("error pinging: ", err)
}
fmt.Println(pong)
}
func serverMain(ctx *context.T) {
- log := veyron2.GetLogger(ctx)
s, err := veyron2.NewServer(ctx)
if err != nil {
- log.Fatal("failure creating server: ", err)
+ vlog.Fatal("failure creating server: ", err)
}
- log.Info("Waiting for ping")
+ vlog.Info("Waiting for ping")
serverPong := PingPongServer(&pongd{})
@@ -47,11 +46,11 @@
if endpoint, err := s.Listen(spec); err == nil {
fmt.Printf("Listening at: %v\n", endpoint)
} else {
- log.Fatal("error listening to service: ", err)
+ vlog.Fatal("error listening to service: ", err)
}
if err := s.Serve("pingpong", serverPong, allowEveryone{}); err != nil {
- log.Fatal("error serving service: ", err)
+ vlog.Fatal("error serving service: ", err)
}
// Wait forever.
@@ -63,8 +62,6 @@
func (allowEveryone) Authorize(security.Context) error { return nil }
func main() {
- flag.Parse()
-
ctx, shutdown := veyron2.Init()
defer shutdown()
diff --git a/services/mgmt/device/deviced/commands.go b/services/mgmt/device/deviced/commands.go
index 90372cc..aeb3b5b 100644
--- a/services/mgmt/device/deviced/commands.go
+++ b/services/mgmt/device/deviced/commands.go
@@ -16,6 +16,7 @@
suidHelper string
agent string
initHelper string
+ origin string
singleUser bool
sessionMode bool
initMode bool
@@ -50,6 +51,7 @@
cmdInstall.Flags.StringVar(&suidHelper, "suid_helper", "", "path to suid helper")
cmdInstall.Flags.StringVar(&agent, "agent", "", "path to security agent")
cmdInstall.Flags.StringVar(&initHelper, "init_helper", "", "path to sysinit helper")
+ cmdInstall.Flags.StringVar(&origin, "origin", "", "if specified, self-updates will use this origin")
cmdInstall.Flags.BoolVar(&singleUser, "single_user", false, "if set, performs the installation assuming a single-user system")
cmdInstall.Flags.BoolVar(&sessionMode, "session_mode", false, "if set, installs the device manager to run a single session. Otherwise, the device manager is configured to get restarted upon exit")
cmdInstall.Flags.BoolVar(&initMode, "init_mode", false, "if set, installs the device manager with the system init service manager")
@@ -73,7 +75,7 @@
if initMode && initHelper == "" {
return cmd.UsageErrorf("--init_helper must be set")
}
- if err := impl.SelfInstall(installationDir(), suidHelper, agent, initHelper, singleUser, sessionMode, initMode, args, os.Environ(), cmd.Stderr(), cmd.Stdout()); err != nil {
+ if err := impl.SelfInstall(installationDir(), suidHelper, agent, initHelper, origin, singleUser, sessionMode, initMode, args, os.Environ(), cmd.Stderr(), cmd.Stdout()); err != nil {
vlog.Errorf("SelfInstall failed: %v", err)
return err
}
diff --git a/services/mgmt/device/impl/app_service.go b/services/mgmt/device/impl/app_service.go
index 940df40..2d40769 100644
--- a/services/mgmt/device/impl/app_service.go
+++ b/services/mgmt/device/impl/app_service.go
@@ -108,6 +108,7 @@
// refine that later.
import (
+ "bytes"
"crypto/md5"
"crypto/rand"
"encoding/base64"
@@ -119,13 +120,12 @@
"io/ioutil"
"os"
"os/exec"
- "os/user"
"path"
"path/filepath"
"reflect"
"strconv"
"strings"
- "sync"
+ "text/template"
"time"
"v.io/core/veyron2"
@@ -190,12 +190,6 @@
type securityAgentState struct {
// Security agent key manager client.
keyMgrAgent *keymgr.Agent
- // Ensures only one security agent connection socket is created
- // at any time, preventing fork/exec from potentially passing
- // down sockets meant for other children (as per ribrdb@, Go's
- // exec implementation does not prune the set of files passed
- // down to only include those specified in cmd.ExtraFiles).
- startLock sync.Mutex
}
// appService implements the Device manager's Application interface.
@@ -473,6 +467,29 @@
return installationDir, nil
}
+// agentPrincipal creates a Principal backed by the given agent connection,
+// taking ownership of the connection. The returned cancel function is to be
+// called when the Principal is no longer in use.
+func agentPrincipal(ctx *context.T, conn *os.File) (security.Principal, func(), error) {
+ agentctx, cancel := context.WithCancel(ctx)
+ var err error
+ if agentctx, _, err = veyron2.SetNewStreamManager(agentctx); err != nil {
+ cancel()
+ conn.Close()
+ return nil, nil, err
+ }
+ p, err := agent.NewAgentPrincipal(agentctx, int(conn.Fd()), veyron2.GetClient(agentctx))
+ if err != nil {
+ cancel()
+ conn.Close()
+ return nil, nil, err
+ }
+ // conn will be closed when the connection to the agent is shut down, as
+ // a result of cancel shutting down the stream manager. No need to
+ // explicitly call conn.Close() with cancel.
+ return p, cancel, nil
+}
+
// setupPrincipal sets up the instance's principal, with the right blessings.
func setupPrincipal(ctx *context.T, instanceDir, versionDir string, call ipc.ServerContext, securityAgent *securityAgentState, info *instanceInfo) error {
var p security.Principal
@@ -484,14 +501,16 @@
vlog.Errorf("NewPrincipal() failed %v", err)
return verror2.Make(ErrOperationFailed, nil)
}
- defer conn.Close()
-
- // TODO(caprita): release the socket created by NewAgentPrincipal.
- if p, err = agent.NewAgentPrincipal(ctx, int(conn.Fd()), veyron2.GetClient(ctx)); err != nil {
- vlog.Errorf("NewAgentPrincipal() failed: %v", err)
+ var cancel func()
+ if p, cancel, err = agentPrincipal(ctx, conn); err != nil {
+ vlog.Errorf("agentPrincipal failed: %v", err)
return verror2.Make(ErrOperationFailed, nil)
}
+ defer cancel()
info.SecurityAgentHandle = handle
+ // conn will be closed when the connection to the agent is shut
+ // down, as a result of cancel() shutting down the stream
+ // manager. No need to call conn.Close().
} else {
credentialsDir := filepath.Join(instanceDir, "credentials")
// TODO(caprita): The app's system user id needs access to this dir.
@@ -677,56 +696,6 @@
return instanceDir, instanceID, nil
}
-// isSetuid is defined like this so we can override its
-// implementation for tests.
-var isSetuid = func(fileStat os.FileInfo) bool {
- vlog.VI(2).Infof("running the original isSetuid")
- return fileStat.Mode()&os.ModeSetuid == os.ModeSetuid
-}
-
-// systemAccountForHelper returns the uname that the helper uses to invoke the
-// application. If the helper exists and is setuid, the device manager
-// requires that there is a uname associated with the Veyron
-// identity that requested starting an application.
-// TODO(rjkroege): This function assumes a desktop installation target
-// and is probably not a good fit in other contexts. Revisit the design
-// as appropriate. This function also internalizes a decision as to when
-// it is possible to start an application that needs to be made explicit.
-func systemAccountForHelper(ctx ipc.ServerContext, helperPath string, uat BlessingSystemAssociationStore) (systemName string, err error) {
- identityNames := ctx.RemoteBlessings().ForContext(ctx)
- helperStat, err := os.Stat(helperPath)
- if err != nil {
- vlog.Errorf("Stat(%v) failed: %v. helper is required.", helperPath, err)
- return "", verror2.Make(ErrOperationFailed, ctx.Context())
- }
- haveHelper := isSetuid(helperStat)
- systemName, present := uat.SystemAccountForBlessings(identityNames)
-
- switch {
- case haveHelper && present:
- return systemName, nil
- case haveHelper && !present:
- // The helper is owned by the device manager and installed as
- // setuid root. Therefore, the device manager must never run an
- // app as itself to prevent an app trivially granting itself
- // root permissions. There must be an associated uname for the
- // account in this case.
- return "", verror2.Make(verror2.NoAccess, ctx.Context(), "use of setuid helper requires an associated uname.")
- case !haveHelper:
- // When the helper is not setuid, the helper can't change the
- // app's uid so just run the app as the device manager's uname
- // whether or not there is an association.
- vlog.VI(1).Infof("helper not setuid. Device manager will invoke app with its own userid")
- user, err := user.Current()
- if err != nil {
- vlog.Errorf("user.Current() failed: %v", err)
- return "", verror2.Make(ErrOperationFailed, ctx.Context())
- }
- return user.Username, nil
- }
- return "", verror2.Make(ErrOperationFailed, ctx.Context())
-}
-
func genCmd(instanceDir, helperPath, systemName string, nsRoots []string) (*exec.Cmd, error) {
versionLink := filepath.Join(instanceDir, "version")
versionDir, err := filepath.EvalSymlinks(versionLink)
@@ -745,7 +714,15 @@
}
cmd := exec.Command(helperPath)
- cmd.Args = append(cmd.Args, "--username", systemName)
+
+ switch yes, err := suidHelper.suidhelperEnabled(systemName, helperPath); {
+ case err != nil:
+ return nil, err
+ case yes:
+ cmd.Args = append(cmd.Args, "--username", systemName)
+ case !yes:
+ cmd.Args = append(cmd.Args, "--username", systemName, "--dryrun")
+ }
var nsRootEnvs []string
for i, r := range nsRoots {
@@ -814,24 +791,16 @@
cfg.Set(mgmt.ParentBlessingConfigKey, info.DeviceManagerPeerPattern)
// Set up any agent-specific state.
- // NOTE(caprita): This ought to belong in genCmd, but we do it here
- // to avoid holding on to the lock for too long.
- //
- // TODO(caprita): We need to take care to grab/release the lock
- // excluding concurrent start operations. See if we can make this more
- // robust.
+ // NOTE(caprita): This ought to belong in genCmd.
var agentCleaner func()
if sa := i.securityAgent; sa != nil {
- sa.startLock.Lock()
file, err := sa.keyMgrAgent.NewConnection(info.SecurityAgentHandle)
if err != nil {
- sa.startLock.Unlock()
vlog.Errorf("NewConnection(%v) failed: %v", info.SecurityAgentHandle, err)
return err
}
agentCleaner = func() {
file.Close()
- sa.startLock.Unlock()
}
// We need to account for the file descriptors corresponding to
// std{err|out|in} as well as the implementation-specific pipes
@@ -908,12 +877,7 @@
return nil, err
}
- systemName, err := systemAccountForHelper(call, helper, i.uat)
- if err != nil {
- cleanupDir(instanceDir, helper)
- return nil, err
- }
-
+ systemName := suidHelper.usernameForPrincipal(call, i.uat)
if err := saveSystemNameForInstance(instanceDir, systemName); err != nil {
cleanupDir(instanceDir, helper)
return nil, err
@@ -956,11 +920,7 @@
return err
}
- systemName, err := systemAccountForHelper(call, i.config.Helper, i.uat)
- if err != nil {
- return err
- }
-
+ systemName := suidHelper.usernameForPrincipal(call, i.uat)
startSystemName, err := readSystemNameForInstance(instanceDir)
if err != nil {
return err
@@ -1306,3 +1266,142 @@
}
return i.locks.GetPathACL(ctx.LocalPrincipal(), path.Join(dir, "acls"))
}
+
+func (i *appService) Debug(ctx ipc.ServerContext) (string, error) {
+ switch len(i.suffix) {
+ case 2:
+ return i.installationDebug(ctx)
+ case 3:
+ return i.instanceDebug(ctx)
+ default:
+ return "", verror2.Make(ErrInvalidSuffix, nil)
+ }
+}
+
+func (i *appService) installationDebug(ctx ipc.ServerContext) (string, error) {
+ const installationDebug = `Installation dir: {{.InstallationDir}}
+
+Origin: {{.Origin}}
+
+Envelope: {{printf "%+v" .Envelope}}
+
+Config: {{printf "%+v" .Config}}
+`
+ installationDebugTemplate, err := template.New("installation-debug").Parse(installationDebug)
+ if err != nil {
+ return "", err
+ }
+
+ installationDir, err := i.installationDir()
+ if err != nil {
+ return "", err
+ }
+ debugInfo := struct {
+ InstallationDir, Origin string
+ Envelope *application.Envelope
+ Config device.Config
+ }{}
+ debugInfo.InstallationDir = installationDir
+
+ if origin, err := loadOrigin(installationDir); err != nil {
+ return "", err
+ } else {
+ debugInfo.Origin = origin
+ }
+
+ currLink := filepath.Join(installationDir, "current")
+ if envelope, err := loadEnvelope(currLink); err != nil {
+ return "", err
+ } else {
+ debugInfo.Envelope = envelope
+ }
+
+ if config, err := loadConfig(installationDir); err != nil {
+ return "", err
+ } else {
+ debugInfo.Config = config
+ }
+
+ var buf bytes.Buffer
+ if err := installationDebugTemplate.Execute(&buf, debugInfo); err != nil {
+ return "", err
+ }
+ return buf.String(), nil
+
+}
+
+func (i *appService) instanceDebug(ctx ipc.ServerContext) (string, error) {
+ const instanceDebug = `Instance dir: {{.InstanceDir}}
+
+System name / start system name: {{.SystemName}} / {{.StartSystemName}}
+
+Cmd: {{printf "%+v" .Cmd}}
+
+Info: {{printf "%+v" .Info}}
+
+Principal: {{.PrincipalType}}
+Public Key: {{.Principal.PublicKey}}
+Blessing Store: {{.Principal.BlessingStore.DebugString}}
+Roots: {{.Principal.Roots.DebugString}}
+`
+ instanceDebugTemplate, err := template.New("instance-debug").Parse(instanceDebug)
+ if err != nil {
+ return "", err
+ }
+
+ instanceDir, err := i.instanceDir()
+ if err != nil {
+ return "", err
+ }
+ debugInfo := struct {
+ InstanceDir, SystemName, StartSystemName string
+ Cmd *exec.Cmd
+ Info *instanceInfo
+ Principal security.Principal
+ PrincipalType string
+ }{}
+ debugInfo.InstanceDir = instanceDir
+
+ debugInfo.SystemName = suidHelper.usernameForPrincipal(ctx, i.uat)
+ if startSystemName, err := readSystemNameForInstance(instanceDir); err != nil {
+ return "", err
+ } else {
+ debugInfo.StartSystemName = startSystemName
+ }
+ if cmd, err := genCmd(instanceDir, i.config.Helper, debugInfo.SystemName, veyron2.GetNamespace(ctx.Context()).Roots()); err != nil {
+ return "", err
+ } else {
+ debugInfo.Cmd = cmd
+ }
+ if info, err := loadInstanceInfo(instanceDir); err != nil {
+ return "", err
+ } else {
+ debugInfo.Info = info
+ }
+
+ if sa := i.securityAgent; sa != nil {
+ file, err := sa.keyMgrAgent.NewConnection(debugInfo.Info.SecurityAgentHandle)
+ if err != nil {
+ vlog.Errorf("NewConnection(%v) failed: %v", debugInfo.Info.SecurityAgentHandle, err)
+ return "", err
+ }
+ var cancel func()
+ if debugInfo.Principal, cancel, err = agentPrincipal(ctx.Context(), file); err != nil {
+ return "", err
+ }
+ defer cancel()
+ debugInfo.PrincipalType = "Agent-based"
+ } else {
+ credentialsDir := filepath.Join(instanceDir, "credentials")
+ var err error
+ if debugInfo.Principal, err = vsecurity.LoadPersistentPrincipal(credentialsDir, nil); err != nil {
+ return "", err
+ }
+ debugInfo.PrincipalType = fmt.Sprintf("Credentials dir-based (%v)", credentialsDir)
+ }
+ var buf bytes.Buffer
+ if err := instanceDebugTemplate.Execute(&buf, debugInfo); err != nil {
+ return "", err
+ }
+ return buf.String(), nil
+}
diff --git a/services/mgmt/device/impl/device_installer.go b/services/mgmt/device/impl/device_installer.go
index 1057bfe..ee59788 100644
--- a/services/mgmt/device/impl/device_installer.go
+++ b/services/mgmt/device/impl/device_installer.go
@@ -39,6 +39,7 @@
"strings"
"v.io/core/veyron2/context"
+ "v.io/core/veyron2/naming"
"v.io/core/veyron2/services/mgmt/application"
"v.io/core/veyron2/services/mgmt/device"
@@ -119,7 +120,7 @@
// SelfInstall installs the device manager and configures it using the
// environment and the supplied command-line flags.
-func SelfInstall(installDir, suidHelper, agent, initHelper string, singleUser, sessionMode, init bool, args, env []string, stderr, stdout io.Writer) error {
+func SelfInstall(installDir, suidHelper, agent, initHelper, origin string, singleUser, sessionMode, init bool, args, env []string, stderr, stdout io.Writer) error {
root := filepath.Join(installDir, dmRoot)
if _, err := os.Stat(root); err == nil || !os.IsNotExist(err) {
return fmt.Errorf("%v already exists", root)
@@ -133,6 +134,7 @@
configState := &config.State{
Name: "dummy", // So that Validate passes.
Root: root,
+ Origin: origin,
CurrentLink: currLink,
Helper: suidHelper,
}
@@ -141,7 +143,7 @@
}
var extraArgs []string
if name, err := os.Hostname(); err == nil {
- extraArgs = append(extraArgs, fmt.Sprintf("--name=%q", name))
+ extraArgs = append(extraArgs, fmt.Sprintf("--name=%q", naming.Join("devices", name)))
}
if !sessionMode {
extraArgs = append(extraArgs, fmt.Sprintf("--restart_exit_code=%d", restartExitCode))
diff --git a/services/mgmt/device/impl/device_service.go b/services/mgmt/device/impl/device_service.go
index d53a10d..d570d3e 100644
--- a/services/mgmt/device/impl/device_service.go
+++ b/services/mgmt/device/impl/device_service.go
@@ -352,6 +352,7 @@
return err
}
if envelope.Title != application.DeviceManagerTitle {
+ vlog.Errorf("app title mismatch. Got %q, expected %q.", envelope.Title, application.DeviceManagerTitle)
return verror2.Make(ErrAppTitleMismatch, ctx)
}
if s.config.Envelope != nil && reflect.DeepEqual(envelope, s.config.Envelope) {
@@ -399,9 +400,11 @@
return err
}
- if err := s.testDeviceManager(ctx, workspace, envelope); err != nil {
- return err
- }
+ // TODO(rthellend): testDeviceManager always fails due to https://github.com/veyron/release-issues/issues/714
+ // Uncomment when the bug is fixed.
+ //if err := s.testDeviceManager(ctx, workspace, envelope); err != nil {
+ // return err
+ //}
if err := updateLink(filepath.Join(workspace, "deviced.sh"), s.config.CurrentLink); err != nil {
return err
@@ -535,3 +538,7 @@
}
return s.uat.AllBlessingSystemAssociations()
}
+
+func (*deviceService) Debug(ipc.ServerContext) (string, error) {
+ return "Not implemented", nil
+}
diff --git a/services/mgmt/device/impl/helper_manager.go b/services/mgmt/device/impl/helper_manager.go
new file mode 100644
index 0000000..9e935d2
--- /dev/null
+++ b/services/mgmt/device/impl/helper_manager.go
@@ -0,0 +1,69 @@
+package impl
+
+import (
+ "os"
+ "os/user"
+
+ "v.io/core/veyron2/ipc"
+ "v.io/core/veyron2/verror2"
+ "v.io/core/veyron2/vlog"
+)
+
+type suidHelperState string
+
+var suidHelper suidHelperState
+
+func init() {
+ u, err := user.Current()
+ if err != nil {
+ vlog.Panicf("devicemanager has no current user: %v", err)
+ }
+ suidHelper = suidHelperState(u.Username)
+}
+
+// isSetuid is defined like this so we can override its
+// implementation for tests.
+var isSetuid = func(fileStat os.FileInfo) bool {
+ vlog.VI(2).Infof("running the original isSetuid")
+ return fileStat.Mode()&os.ModeSetuid == os.ModeSetuid
+}
+
+// unameRequiresSuidhelper determines if the the suidhelper must exist
+// and be setuid to run an application as system user un. If false, the
+// setuidhelper must be invoked with the --dryrun flag to skip making
+// system calls that will fail or provide apps with a trivial
+// priviledge escalation.
+func (dn suidHelperState) suidhelperEnabled(un, helperPath string) (bool, error) {
+ helperStat, err := os.Stat(helperPath)
+ if err != nil {
+ vlog.Errorf("Stat(%v) failed: %v. helper is required.", helperPath, err)
+ return false, verror2.Make(ErrOperationFailed, nil)
+ }
+ haveHelper := isSetuid(helperStat)
+
+ switch {
+ case haveHelper && string(dn) != un:
+ return true, nil
+ case haveHelper && string(dn) == un:
+ return false, verror2.Make(verror2.NoAccess, nil)
+ default:
+ return false, nil
+ }
+ // Keep the compiler happy.
+ return false, nil
+}
+
+// usernameForVanadiumPrincipal returns the system name that the
+// devicemanager will use to invoke apps.
+// TODO(rjkroege): This code assumes a desktop target and will need
+// to be reconsidered for embedded contexts.
+func (i suidHelperState) usernameForPrincipal(ctx ipc.ServerContext, uat BlessingSystemAssociationStore) string {
+ identityNames := ctx.RemoteBlessings().ForContext(ctx)
+ systemName, present := uat.SystemAccountForBlessings(identityNames)
+
+ if present {
+ return systemName
+ } else {
+ return string(i)
+ }
+}
diff --git a/services/mgmt/device/impl/impl_test.go b/services/mgmt/device/impl/impl_test.go
index 8b9a926..7ec36ed 100644
--- a/services/mgmt/device/impl/impl_test.go
+++ b/services/mgmt/device/impl/impl_test.go
@@ -593,6 +593,19 @@
// Install the app. The config-specified flag value for testFlagName
// should override the value specified in the envelope above.
appID := installApp(t, ctx, device.Config{testFlagName: "flag-val-install"})
+ installationDebug := debug(t, ctx, appID)
+ // We spot-check a couple pieces of information we expect in the debug
+ // output.
+ // TODO(caprita): Is there a way to verify more without adding brittle
+ // logic that assumes too much about the format? This may be one
+ // argument in favor of making the output of Debug a struct instead of
+ // free-form string.
+ if !strings.Contains(installationDebug, "Origin: ar") {
+ t.Fatalf("debug response doesn't contain expected info: %v", installationDebug)
+ }
+ if !strings.Contains(installationDebug, "Config: map[random_test_flag:flag-val-install]") {
+ t.Fatalf("debug response doesn't contain expected info: %v", installationDebug)
+ }
// Start requires the caller to grant a blessing for the app instance.
if _, err := startAppImpl(t, ctx, appID, ""); err == nil || !verror.Is(err, impl.ErrInvalidBlessing.ID) {
@@ -602,6 +615,11 @@
// Start an instance of the app.
instance1ID := startApp(t, ctx, appID)
+ instanceDebug := debug(t, ctx, appID, instance1ID)
+ if !strings.Contains(instanceDebug, "Blessing Store: Default blessings: test-principal/forapp/google naps") {
+ t.Fatalf("debug response doesn't contain expected info: %v", instanceDebug)
+ }
+
// Wait until the app pings us that it's ready.
verifyPingArgs(t, pingCh, userName(t), "flag-val-install", "env-val-envelope")
@@ -993,7 +1011,7 @@
dmDir := filepath.Join(testDir, "dm")
// TODO(caprita): Add test logic when initMode = true.
singleUser, sessionMode, initMode := true, true, false
- if err := impl.SelfInstall(dmDir, suidHelperPath, agentPath, initHelperPath, singleUser, sessionMode, initMode, dmargs[1:], dmenv, os.Stderr, os.Stdout); err != nil {
+ if err := impl.SelfInstall(dmDir, suidHelperPath, agentPath, initHelperPath, "", singleUser, sessionMode, initMode, dmargs[1:], dmenv, os.Stderr, os.Stdout); err != nil {
t.Fatalf("SelfInstall failed: %v", err)
}
diff --git a/services/mgmt/device/impl/util_test.go b/services/mgmt/device/impl/util_test.go
index 0ac22f6..732ca47 100644
--- a/services/mgmt/device/impl/util_test.go
+++ b/services/mgmt/device/impl/util_test.go
@@ -241,6 +241,14 @@
}
}
+func debug(t *testing.T, ctx *context.T, nameComponents ...string) string {
+ dbg, err := appStub(nameComponents...).Debug(ctx)
+ if err != nil {
+ t.Fatalf(testutil.FormatLogLine(2, "Debug(%v) failed: %v", nameComponents, err))
+ }
+ return dbg
+}
+
// Code to make Association lists sortable.
type byIdentity []device.Association
diff --git a/services/mgmt/suidhelper/impl/args.go b/services/mgmt/suidhelper/impl/args.go
index 67003a3..185bae8 100644
--- a/services/mgmt/suidhelper/impl/args.go
+++ b/services/mgmt/suidhelper/impl/args.go
@@ -36,7 +36,7 @@
var (
flagUsername, flagWorkspace, flagLogDir, flagRun *string
flagMinimumUid *int64
- flagRemove *bool
+ flagRemove, flagDryrun *bool
)
func init() {
@@ -53,6 +53,7 @@
flagRun = sflag.Run
flagMinimumUid = sflag.MinimumUid
flagRemove = sflag.Remove
+ flagDryrun = sflag.Dryrun
}
// ParseArguments populates the WorkParameter object from the provided args
@@ -88,6 +89,8 @@
return fmt.Errorf("suidhelper does not permit uids less than %d", *flagMinimumUid)
}
+ wp.dryrun = *flagDryrun
+
// Preserve the arguments for examination by the test harness if executed
// in the course of a test.
if os.Getenv("VEYRON_SUIDHELPER_TEST") != "" {
diff --git a/services/mgmt/suidhelper/impl/args_test.go b/services/mgmt/suidhelper/impl/args_test.go
index 9358e9f..9341fc7 100644
--- a/services/mgmt/suidhelper/impl/args_test.go
+++ b/services/mgmt/suidhelper/impl/args_test.go
@@ -85,6 +85,24 @@
fmt.Errorf("suidhelper does not permit uids less than 501"),
WorkParameters{},
},
+
+ {
+ []string{"setuidhelper", "--minuid", "1", "--username", testUserName, "--workspace", "/hello",
+ "--logdir", "/logging", "--run", "/bin/veyron", "--dryrun", "--", "one", "two"},
+ []string{"A=B"},
+ nil,
+ WorkParameters{
+ uid: testUid,
+ gid: testGid,
+ workspace: "/hello",
+ logDir: "/logging",
+ argv0: "/bin/veyron",
+ argv: []string{"/bin/veyron", "one", "two"},
+ envv: []string{"A=B"},
+ dryrun: true,
+ remove: false,
+ },
+ },
}
for _, c := range cases {
diff --git a/services/mgmt/suidhelper/impl/flag/flag.go b/services/mgmt/suidhelper/impl/flag/flag.go
index f8b53e1..273f56b 100644
--- a/services/mgmt/suidhelper/impl/flag/flag.go
+++ b/services/mgmt/suidhelper/impl/flag/flag.go
@@ -15,7 +15,7 @@
var (
Username, Workspace, LogDir, Run *string
MinimumUid *int64
- Remove *bool
+ Remove, Dryrun *bool
)
func init() {
@@ -29,6 +29,7 @@
Run = fs.String("run", "", "Path to the application to exec.")
MinimumUid = fs.Int64("minuid", uidThreshold, "UIDs cannot be less than this number.")
Remove = fs.Bool("rm", false, "Remove the file trees given as command-line arguments.")
+ Dryrun = fs.Bool("dryrun", false, "Elides root-requiring systemcalls.")
}
const uidThreshold = 501
diff --git a/services/mgmt/suidhelper/impl/system.go b/services/mgmt/suidhelper/impl/system.go
index 60a7b92..c634b59 100644
--- a/services/mgmt/suidhelper/impl/system.go
+++ b/services/mgmt/suidhelper/impl/system.go
@@ -34,22 +34,38 @@
}
func (hw *WorkParameters) Exec() error {
+ attr := new(syscall.ProcAttr)
+
+ if dir, err := os.Getwd(); err != nil {
+ log.Printf("error Getwd(): %v\n", err)
+ return fmt.Errorf("os.Getwd failed: %v", err)
+ attr.Dir = dir
+ }
+ attr.Env = hw.envv
+
+ attr.Sys = new(syscall.SysProcAttr)
+ attr.Sys.Setsid = true
if hw.dryrun {
log.Printf("[dryrun] syscall.Setgid(%d)", hw.gid)
log.Printf("[dryrun] syscall.Setuid(%d)", hw.uid)
} else {
- // NOTE(caprita): Commenting this out since it's broken with go
- // 1.4, to make the integration test pass. go/vcl/8240 will fix
- // it properly.
-
- // if err := syscall.Setgid(hw.gid); err != nil {
- // return fmt.Errorf("syscall.Setgid(%d) failed: %v", hw.gid, err)
- // }
- // if err := syscall.Setuid(hw.uid); err != nil {
- // return fmt.Errorf("syscall.Setuid(%d) failed: %v", hw.uid, err)
- // }
+ attr.Sys.Credential = new(syscall.Credential)
+ attr.Sys.Credential.Gid = uint32(hw.gid)
+ attr.Sys.Credential.Uid = uint32(hw.uid)
}
- return syscall.Exec(hw.argv0, hw.argv, hw.envv)
+
+ _, _, err := syscall.StartProcess(hw.argv0, hw.argv, attr)
+ if err != nil {
+ if !hw.dryrun {
+ log.Printf("StartProcess failed: attr: %#v, attr.Sys: %#v, attr.Sys.Cred: %#v error: %v\n", attr, attr.Sys, attr.Sys.Credential, err)
+ } else {
+ log.Printf("StartProcess failed: %v", err)
+ }
+ return fmt.Errorf("syscall.StartProcess(%s) failed: %v", hw.argv0, err)
+ }
+ // TODO(rjkroege): Return the pid to the node manager.
+ os.Exit(0)
+ return nil // Not reached.
}
func (hw *WorkParameters) Remove() error {
diff --git a/tools/application/impl.go b/tools/application/impl.go
index 026f2c3..ef0bf9e 100644
--- a/tools/application/impl.go
+++ b/tools/application/impl.go
@@ -17,7 +17,9 @@
"v.io/lib/cmdline"
)
-func getEnvelopeJSON(ctx *context.T, app repository.ApplicationClientMethods, profiles string) ([]byte, error) {
+func getEnvelopeJSON(app repository.ApplicationClientMethods, profiles string) ([]byte, error) {
+ ctx, cancel := context.WithTimeout(gctx, time.Minute)
+ defer cancel()
env, err := app.Match(ctx, strings.Split(profiles, ","))
if err != nil {
return nil, err
@@ -29,11 +31,13 @@
return j, nil
}
-func putEnvelopeJSON(ctx *context.T, app repository.ApplicationClientMethods, profiles string, j []byte) error {
+func putEnvelopeJSON(app repository.ApplicationClientMethods, profiles string, j []byte) error {
var env application.Envelope
if err := json.Unmarshal(j, &env); err != nil {
return fmt.Errorf("Unmarshal(%v) failed: %v", string(j), err)
}
+ ctx, cancel := context.WithTimeout(gctx, time.Minute)
+ defer cancel()
if err := app.Put(ctx, strings.Split(profiles, ","), env); err != nil {
return err
}
@@ -66,9 +70,7 @@
}
name, profiles := args[0], args[1]
app := repository.ApplicationClient(name)
- ctx, cancel := context.WithTimeout(gctx, time.Minute)
- defer cancel()
- j, err := getEnvelopeJSON(ctx, app, profiles)
+ j, err := getEnvelopeJSON(app, profiles)
if err != nil {
return err
}
@@ -81,29 +83,40 @@
Name: "put",
Short: "Add the given envelope to the application for the given profiles.",
Long: "Add the given envelope to the application for the given profiles.",
- ArgsName: "<application> <profiles> <envelope>",
+ ArgsName: "<application> <profiles> [<envelope>]",
ArgsLong: `
<application> is the full name of the application.
<profiles> is a comma-separated list of profiles.
-<envelope> is the file that contains a JSON-encoded envelope.`,
+<envelope> is the file that contains a JSON-encoded envelope. If this file is
+not provided, the user will be prompted to enter the data manually.`,
}
func runPut(cmd *cmdline.Command, args []string) error {
- if expected, got := 3, len(args); expected != got {
- return cmd.UsageErrorf("put: incorrect number of arguments, expected %d, got %d", expected, got)
+ if got := len(args); got != 2 && got != 3 {
+ return cmd.UsageErrorf("put: incorrect number of arguments, expected 2 or 3, got %d", got)
}
- name, profiles, envelope := args[0], args[1], args[2]
+ name, profiles := args[0], args[1]
app := repository.ApplicationClient(name)
- j, err := ioutil.ReadFile(envelope)
- if err != nil {
- return fmt.Errorf("ReadFile(%v): %v", envelope, err)
+ if len(args) == 3 {
+ envelope := args[2]
+ j, err := ioutil.ReadFile(envelope)
+ if err != nil {
+ return fmt.Errorf("ReadFile(%v): %v", envelope, err)
+ }
+ if err = putEnvelopeJSON(app, profiles, j); err != nil {
+ return err
+ }
+ fmt.Fprintln(cmd.Stdout(), "Application envelope added successfully.")
+ return nil
}
- ctx, cancel := context.WithTimeout(gctx, time.Minute)
- defer cancel()
- if err = putEnvelopeJSON(ctx, app, profiles, j); err != nil {
+ env := application.Envelope{Args: []string{}, Env: []string{}, Packages: map[string]string{}}
+ j, err := json.MarshalIndent(env, "", " ")
+ if err != nil {
+ return fmt.Errorf("MarshalIndent() failed: %v", err)
+ }
+ if err := editAndPutEnvelopeJSON(cmd, app, profiles, j); err != nil {
return err
}
- fmt.Fprintln(cmd.Stdout(), "Application envelope added successfully.")
return nil
}
@@ -150,6 +163,18 @@
}
name, profile := args[0], args[1]
app := repository.ApplicationClient(name)
+
+ envData, err := getEnvelopeJSON(app, profile)
+ if err != nil {
+ return err
+ }
+ if err := editAndPutEnvelopeJSON(cmd, app, profile, envData); err != nil {
+ return err
+ }
+ return nil
+}
+
+func editAndPutEnvelopeJSON(cmd *cmdline.Command, app repository.ApplicationClientMethods, profile string, envData []byte) error {
f, err := ioutil.TempFile("", "application-edit-")
if err != nil {
return fmt.Errorf("TempFile() failed: %v", err)
@@ -157,13 +182,6 @@
fileName := f.Name()
f.Close()
defer os.Remove(fileName)
-
- ctx, cancel := context.WithTimeout(gctx, time.Minute)
- defer cancel()
- envData, err := getEnvelopeJSON(ctx, app, profile)
- if err != nil {
- return err
- }
if err = ioutil.WriteFile(fileName, envData, os.FileMode(0644)); err != nil {
return err
}
@@ -191,7 +209,7 @@
fmt.Fprintln(cmd.Stdout(), "Nothing changed")
return nil
}
- if err = putEnvelopeJSON(ctx, app, profile, newData); err != nil {
+ if err = putEnvelopeJSON(app, profile, newData); err != nil {
fmt.Fprintf(cmd.Stdout(), "Error: %v\n", err)
if ans := promptUser(cmd, "Try again? [y/N] "); strings.ToUpper(ans) == "Y" {
continue
diff --git a/tools/binary/impl.go b/tools/binary/impl.go
index b51f7fd..3f8ebd3 100644
--- a/tools/binary/impl.go
+++ b/tools/binary/impl.go
@@ -2,6 +2,7 @@
import (
"fmt"
+ "os"
"v.io/core/veyron/services/mgmt/lib/binary"
"v.io/lib/cmdline"
@@ -71,11 +72,21 @@
}
func runUpload(cmd *cmdline.Command, args []string) error {
- // TODO(rthellend): Add support for creating packages on the fly.
if expected, got := 2, len(args); expected != got {
return cmd.UsageErrorf("upload: incorrect number of arguments, expected %d, got %d", expected, got)
}
von, filename := args[0], args[1]
+ fi, err := os.Stat(filename)
+ if err != nil {
+ return err
+ }
+ if fi.IsDir() {
+ if err := binary.UploadFromDir(gctx, von, filename); err != nil {
+ return err
+ }
+ fmt.Fprintf(cmd.Stdout(), "Binary package uploaded from directory %s\n", filename)
+ return nil
+ }
if err := binary.UploadFromFile(gctx, von, filename); err != nil {
return err
}
diff --git a/tools/mgmt/device/acl_fmt.go b/tools/mgmt/device/impl/acl_fmt.go
similarity index 99%
rename from tools/mgmt/device/acl_fmt.go
rename to tools/mgmt/device/impl/acl_fmt.go
index 24091bc..05a6b05 100644
--- a/tools/mgmt/device/acl_fmt.go
+++ b/tools/mgmt/device/impl/acl_fmt.go
@@ -1,4 +1,4 @@
-package main
+package impl
import (
"fmt"
diff --git a/tools/mgmt/device/acl_impl.go b/tools/mgmt/device/impl/acl_impl.go
similarity index 99%
rename from tools/mgmt/device/acl_impl.go
rename to tools/mgmt/device/impl/acl_impl.go
index e39f68e..dfa0cab 100644
--- a/tools/mgmt/device/acl_impl.go
+++ b/tools/mgmt/device/impl/acl_impl.go
@@ -1,4 +1,4 @@
-package main
+package impl
// Commands to get/set ACLs.
diff --git a/tools/mgmt/device/acl_test.go b/tools/mgmt/device/impl/acl_test.go
similarity index 98%
rename from tools/mgmt/device/acl_test.go
rename to tools/mgmt/device/impl/acl_test.go
index a146e82..d195afa 100644
--- a/tools/mgmt/device/acl_test.go
+++ b/tools/mgmt/device/impl/acl_test.go
@@ -1,4 +1,4 @@
-package main
+package impl_test
import (
"bytes"
@@ -10,6 +10,8 @@
"v.io/core/veyron2/security"
"v.io/core/veyron2/services/security/access"
verror "v.io/core/veyron2/verror2"
+
+ "v.io/core/veyron/tools/mgmt/device/impl"
)
const pkgPath = "v.io/core/veyron/tools/mgmt/device/main"
@@ -31,7 +33,7 @@
defer stopServer(t, server)
// Setup the command-line.
- cmd := root()
+ cmd := impl.Root()
var stdout, stderr bytes.Buffer
cmd.Init(nil, &stdout, &stderr)
deviceName := endpoint.Name()
@@ -80,7 +82,7 @@
defer stopServer(t, server)
// Setup the command-line.
- cmd := root()
+ cmd := impl.Root()
var stdout, stderr bytes.Buffer
cmd.Init(nil, &stdout, &stderr)
deviceName := endpoint.Name()
diff --git a/tools/mgmt/device/associate_impl.go b/tools/mgmt/device/impl/associate_impl.go
similarity index 99%
rename from tools/mgmt/device/associate_impl.go
rename to tools/mgmt/device/impl/associate_impl.go
index 1f21191..08d68f8 100644
--- a/tools/mgmt/device/associate_impl.go
+++ b/tools/mgmt/device/impl/associate_impl.go
@@ -1,4 +1,4 @@
-package main
+package impl
import (
"fmt"
diff --git a/tools/mgmt/device/devicemanager_mock_test.go b/tools/mgmt/device/impl/devicemanager_mock_test.go
similarity index 93%
rename from tools/mgmt/device/devicemanager_mock_test.go
rename to tools/mgmt/device/impl/devicemanager_mock_test.go
index 7501383..62cf94a 100644
--- a/tools/mgmt/device/devicemanager_mock_test.go
+++ b/tools/mgmt/device/impl/devicemanager_mock_test.go
@@ -1,4 +1,4 @@
-package main
+package impl_test
import (
"log"
@@ -68,9 +68,11 @@
func (*mockDeviceInvoker) Describe(ipc.ServerContext) (device.Description, error) {
return device.Description{}, nil
}
+
func (*mockDeviceInvoker) IsRunnable(_ ipc.ServerContext, description binary.Description) (bool, error) {
return false, nil
}
+
func (*mockDeviceInvoker) Reset(call ipc.ServerContext, deadline uint64) error { return nil }
// Mock Install
@@ -92,11 +94,13 @@
}
func (*mockDeviceInvoker) Refresh(ipc.ServerContext) error { return nil }
+
func (*mockDeviceInvoker) Restart(ipc.ServerContext) error { return nil }
func (mni *mockDeviceInvoker) Resume(_ ipc.ServerContext) error {
return mni.simpleCore("Resume", "Resume")
}
+
func (i *mockDeviceInvoker) Revert(call ipc.ServerContext) error { return nil }
type StartResponse struct {
@@ -122,8 +126,11 @@
func (mni *mockDeviceInvoker) Suspend(_ ipc.ServerContext) error {
return mni.simpleCore("Suspend", "Suspend")
}
-func (*mockDeviceInvoker) Uninstall(ipc.ServerContext) error { return nil }
-func (i *mockDeviceInvoker) Update(ipc.ServerContext) error { return nil }
+
+func (*mockDeviceInvoker) Uninstall(ipc.ServerContext) error { return nil }
+
+func (i *mockDeviceInvoker) Update(ipc.ServerContext) error { return nil }
+
func (*mockDeviceInvoker) UpdateTo(ipc.ServerContext, string) error { return nil }
// Mock ACL getting and setting
@@ -149,6 +156,12 @@
return r.acl, r.etag, r.err
}
+func (mni *mockDeviceInvoker) Debug(ipc.ServerContext) (string, error) {
+ ir := mni.tape.Record("Debug")
+ r := ir.(string)
+ return r, nil
+}
+
type dispatcher struct {
tape *Tape
t *testing.T
diff --git a/tools/mgmt/device/impl.go b/tools/mgmt/device/impl/impl.go
similarity index 65%
rename from tools/mgmt/device/impl.go
rename to tools/mgmt/device/impl/impl.go
index b62fb96..8762b8a 100644
--- a/tools/mgmt/device/impl.go
+++ b/tools/mgmt/device/impl/impl.go
@@ -1,4 +1,4 @@
-package main
+package impl
import (
"encoding/json"
@@ -94,7 +94,7 @@
Long: "Claim the device.",
ArgsName: "<device> <grant extension>",
ArgsLong: `
-<device> is the veyron object name of the device manager's app service.
+<device> is the veyron object name of the device manager's device service.
<grant extension> is used to extend the default blessing of the
current principal when blessing the app instance.`,
@@ -120,7 +120,7 @@
Long: "Describe the device.",
ArgsName: "<device>",
ArgsLong: `
-<device> is the veyron object name of the device manager's app service.`,
+<device> is the veyron object name of the device manager's device service.`,
}
func runDescribe(cmd *cmdline.Command, args []string) error {
@@ -136,13 +136,71 @@
return nil
}
-func root() *cmdline.Command {
- return &cmdline.Command{
- Name: "device",
- Short: "Tool for interacting with the veyron device manager",
- Long: `
-The device tool facilitates interaction with the veyron device manager.
-`,
- Children: []*cmdline.Command{cmdInstall, cmdStart, associateRoot(), cmdDescribe, cmdClaim, cmdStop, cmdSuspend, cmdResume, aclRoot()},
+var cmdUpdate = &cmdline.Command{
+ Run: runUpdate,
+ Name: "update",
+ Short: "Update the device manager or application",
+ Long: "Update the device manager or application",
+ ArgsName: "<object>",
+ ArgsLong: `
+<object> is the veyron object name of the device manager or application
+installation to update.`,
+}
+
+func runUpdate(cmd *cmdline.Command, args []string) error {
+ if expected, got := 1, len(args); expected != got {
+ return cmd.UsageErrorf("update: incorrect number of arguments, expected %d, got %d", expected, got)
}
+ deviceName := args[0]
+ if err := device.ApplicationClient(deviceName).Update(gctx); err != nil {
+ return err
+ }
+ fmt.Fprintln(cmd.Stdout(), "Update successful.")
+ return nil
+}
+
+var cmdRevert = &cmdline.Command{
+ Run: runRevert,
+ Name: "revert",
+ Short: "Revert the device manager or application",
+ Long: "Revert the device manager or application to its previous version",
+ ArgsName: "<object>",
+ ArgsLong: `
+<object> is the veyron object name of the device manager or application
+installation to revert.`,
+}
+
+func runRevert(cmd *cmdline.Command, args []string) error {
+ if expected, got := 1, len(args); expected != got {
+ return cmd.UsageErrorf("revert: incorrect number of arguments, expected %d, got %d", expected, got)
+ }
+ deviceName := args[0]
+ if err := device.ApplicationClient(deviceName).Revert(gctx); err != nil {
+ return err
+ }
+ fmt.Fprintln(cmd.Stdout(), "Revert successful.")
+ return nil
+}
+
+var cmdDebug = &cmdline.Command{
+ Run: runDebug,
+ Name: "debug",
+ Short: "Debug the device.",
+ Long: "Debug the device.",
+ ArgsName: "<device>",
+ ArgsLong: `
+<device> is the veyron object name of an app installation or instance.`,
+}
+
+func runDebug(cmd *cmdline.Command, args []string) error {
+ if expected, got := 1, len(args); expected != got {
+ return cmd.UsageErrorf("debug: incorrect number of arguments, expected %d, got %d", expected, got)
+ }
+ deviceName := args[0]
+ if description, err := device.DeviceClient(deviceName).Debug(gctx); err != nil {
+ return fmt.Errorf("Debug failed: %v", err)
+ } else {
+ fmt.Fprintf(cmd.Stdout(), "%v\n", description)
+ }
+ return nil
}
diff --git a/tools/mgmt/device/impl_test.go b/tools/mgmt/device/impl/impl_test.go
similarity index 91%
rename from tools/mgmt/device/impl_test.go
rename to tools/mgmt/device/impl/impl_test.go
index 065574c..b841ee6 100644
--- a/tools/mgmt/device/impl_test.go
+++ b/tools/mgmt/device/impl/impl_test.go
@@ -1,4 +1,4 @@
-package main
+package impl_test
import (
"bytes"
@@ -8,22 +8,12 @@
"strings"
"testing"
- tsecurity "v.io/core/veyron/lib/testutil/security"
-
- "v.io/core/veyron2"
"v.io/core/veyron2/naming"
"v.io/core/veyron2/services/mgmt/device"
verror "v.io/core/veyron2/verror2"
-)
-func initTest() (shutdown veyron2.Shutdown) {
- var err error
- gctx, shutdown = veyron2.Init()
- if gctx, err = veyron2.SetPrincipal(gctx, tsecurity.NewPrincipal("test-blessing")); err != nil {
- panic(err)
- }
- return shutdown
-}
+ "v.io/core/veyron/tools/mgmt/device/impl"
+)
func TestListCommand(t *testing.T) {
shutdown := initTest()
@@ -37,7 +27,7 @@
defer stopServer(t, server)
// Setup the command-line.
- cmd := root()
+ cmd := impl.Root()
var stdout, stderr bytes.Buffer
cmd.Init(nil, &stdout, &stderr)
deviceName := naming.JoinAddressName(endpoint.String(), "")
@@ -92,7 +82,7 @@
defer stopServer(t, server)
// Setup the command-line.
- cmd := root()
+ cmd := impl.Root()
var stdout, stderr bytes.Buffer
cmd.Init(nil, &stdout, &stderr)
deviceName := naming.JoinAddressName(endpoint.String(), "/myapp/1")
@@ -145,7 +135,7 @@
defer stopServer(t, server)
// Setup the command-line.
- cmd := root()
+ cmd := impl.Root()
var stdout, stderr bytes.Buffer
cmd.Init(nil, &stdout, &stderr)
deviceName := naming.JoinAddressName(endpoint.String(), "")
@@ -185,7 +175,7 @@
defer stopServer(t, server)
// Setup the command-line.
- cmd := root()
+ cmd := impl.Root()
var stdout, stderr bytes.Buffer
cmd.Init(nil, &stdout, &stderr)
deviceName := naming.JoinAddressName(endpoint.String(), "")
@@ -278,7 +268,7 @@
defer stopServer(t, server)
// Setup the command-line.
- cmd := root()
+ cmd := impl.Root()
var stdout, stderr bytes.Buffer
cmd.Init(nil, &stdout, &stderr)
deviceName := naming.JoinAddressName(endpoint.String(), "")
@@ -358,7 +348,7 @@
defer stopServer(t, server)
// Setup the command-line.
- cmd := root()
+ cmd := impl.Root()
var stdout, stderr bytes.Buffer
cmd.Init(nil, &stdout, &stderr)
appName := naming.JoinAddressName(endpoint.String(), "")
@@ -428,3 +418,31 @@
stdout.Reset()
stderr.Reset()
}
+
+func TestDebugCommand(t *testing.T) {
+ shutdown := initTest()
+ defer shutdown()
+ tape := NewTape()
+ server, endpoint, err := startServer(t, gctx, tape)
+ if err != nil {
+ return
+ }
+ defer stopServer(t, server)
+ // Setup the command-line.
+ cmd := impl.Root()
+ var stdout, stderr bytes.Buffer
+ cmd.Init(nil, &stdout, &stderr)
+ appName := naming.JoinAddressName(endpoint.String(), "")
+
+ debugMessage := "the secrets of the universe, revealed"
+ tape.SetResponses([]interface{}{debugMessage})
+ if err := cmd.Execute([]string{"debug", appName}); err != nil {
+ t.Fatalf("%v", err)
+ }
+ if expected, got := debugMessage, strings.TrimSpace(stdout.String()); got != expected {
+ t.Fatalf("Unexpected output from debug. Got %q, expected %q", got, expected)
+ }
+ if got, expected := tape.Play(), []interface{}{"Debug"}; !reflect.DeepEqual(expected, got) {
+ t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+ }
+}
diff --git a/tools/mgmt/device/instance_impl.go b/tools/mgmt/device/impl/instance_impl.go
similarity index 99%
rename from tools/mgmt/device/instance_impl.go
rename to tools/mgmt/device/impl/instance_impl.go
index b7a1fc4..3210ad4 100644
--- a/tools/mgmt/device/instance_impl.go
+++ b/tools/mgmt/device/impl/instance_impl.go
@@ -1,4 +1,4 @@
-package main
+package impl
// Commands to modify instance.
diff --git a/tools/mgmt/device/instance_impl_test.go b/tools/mgmt/device/impl/instance_impl_test.go
similarity index 97%
rename from tools/mgmt/device/instance_impl_test.go
rename to tools/mgmt/device/impl/instance_impl_test.go
index 10f6702..08daed4 100644
--- a/tools/mgmt/device/instance_impl_test.go
+++ b/tools/mgmt/device/impl/instance_impl_test.go
@@ -1,4 +1,4 @@
-package main
+package impl_test
import (
"bytes"
@@ -8,6 +8,8 @@
"v.io/core/veyron2/naming"
verror "v.io/core/veyron2/verror2"
+
+ "v.io/core/veyron/tools/mgmt/device/impl"
)
func TestStopCommand(t *testing.T) {
@@ -22,7 +24,7 @@
defer stopServer(t, server)
// Setup the command-line.
- cmd := root()
+ cmd := impl.Root()
var stdout, stderr bytes.Buffer
cmd.Init(nil, &stdout, &stderr)
appName := naming.JoinAddressName(endpoint.String(), "")
@@ -97,7 +99,7 @@
defer stopServer(t, server)
// Setup the command-line.
- cmd := root()
+ cmd := impl.Root()
var stdout, stderr bytes.Buffer
cmd.Init(nil, &stdout, &stderr)
appName := naming.JoinAddressName(endpoint.String(), "")
diff --git a/tools/mgmt/device/mock_test.go b/tools/mgmt/device/impl/mock_test.go
similarity index 97%
rename from tools/mgmt/device/mock_test.go
rename to tools/mgmt/device/impl/mock_test.go
index 936fc45..f08b44c 100644
--- a/tools/mgmt/device/mock_test.go
+++ b/tools/mgmt/device/impl/mock_test.go
@@ -1,4 +1,4 @@
-package main
+package impl_test
import (
"fmt"
diff --git a/tools/mgmt/device/impl/root.go b/tools/mgmt/device/impl/root.go
new file mode 100644
index 0000000..db04609
--- /dev/null
+++ b/tools/mgmt/device/impl/root.go
@@ -0,0 +1,24 @@
+package impl
+
+import (
+ "v.io/core/veyron2/context"
+
+ "v.io/lib/cmdline"
+)
+
+var gctx *context.T
+
+func SetGlobalContext(ctx *context.T) {
+ gctx = ctx
+}
+
+func Root() *cmdline.Command {
+ return &cmdline.Command{
+ Name: "device",
+ Short: "Tool for interacting with the veyron device manager",
+ Long: `
+The device tool facilitates interaction with the veyron device manager.
+`,
+ Children: []*cmdline.Command{cmdInstall, cmdStart, associateRoot(), cmdDescribe, cmdClaim, cmdStop, cmdSuspend, cmdResume, cmdRevert, cmdUpdate, cmdDebug, aclRoot()},
+ }
+}
diff --git a/tools/mgmt/device/impl/util_test.go b/tools/mgmt/device/impl/util_test.go
new file mode 100644
index 0000000..e108e49
--- /dev/null
+++ b/tools/mgmt/device/impl/util_test.go
@@ -0,0 +1,27 @@
+package impl_test
+
+import (
+ "v.io/core/veyron2"
+ "v.io/core/veyron2/context"
+
+ "v.io/core/veyron/lib/testutil/security"
+ _ "v.io/core/veyron/profiles"
+ "v.io/core/veyron/tools/mgmt/device/impl"
+)
+
+var gctx *context.T
+
+func initTest() veyron2.Shutdown {
+ ctx, shutdown := veyron2.Init()
+ var err error
+ if ctx, err = veyron2.SetPrincipal(ctx, security.NewPrincipal("test-blessing")); err != nil {
+ panic(err)
+ }
+ gctx = ctx
+ impl.SetGlobalContext(gctx)
+ return func() {
+ shutdown()
+ impl.SetGlobalContext(nil)
+ gctx = nil
+ }
+}
diff --git a/tools/mgmt/device/main.go b/tools/mgmt/device/main.go
index 65f7187..fc24f06 100644
--- a/tools/mgmt/device/main.go
+++ b/tools/mgmt/device/main.go
@@ -7,17 +7,15 @@
"os"
"v.io/core/veyron2"
- "v.io/core/veyron2/context"
_ "v.io/core/veyron/profiles"
+ "v.io/core/veyron/tools/mgmt/device/impl"
)
-var gctx *context.T
-
func main() {
- var shutdown veyron2.Shutdown
- gctx, shutdown = veyron2.Init()
- exitCode := root().Main()
+ gctx, shutdown := veyron2.Init()
+ impl.SetGlobalContext(gctx)
+ exitCode := impl.Root().Main()
shutdown()
os.Exit(exitCode)
}
diff --git a/tools/mgmt/test.sh b/tools/mgmt/test.sh
index c42fa1d..6441eb7 100755
--- a/tools/mgmt/test.sh
+++ b/tools/mgmt/test.sh
@@ -1,8 +1,29 @@
#!/bin/bash
# Test the device manager and related services and tools.
+#
+#
+# By default, this script tests the device manager in a fashion amenable
+# to automatic testing: the --single_user is passed to the device
+# manager so that all device manager components run as the same user and
+# no user input (such as an agent pass phrase) is needed.
+#
+# When this script is invoked with the --with_suid <user> flag, it
+# installs the device manager in its more secure multi-account
+# configuration where the device manager runs under the account of the
+# invoker and test apps will be executed as <user>. This mode will
+# require root permisisons to install and may require configuring an
+# agent passphrase.
+#
+# For exanple:
+#
+# ./test.sh --with_suid vanaguest
+#
+# to test a device manager with multi-account support enabled for app
+# account vanaguest.
+#
-source "$(go list -f {{.Dir}} v.io/core/shell/lib)/shell_test.sh"
+ source "$(go list -f {{.Dir}} v.io/core/shell/lib)/shell_test.sh"
# Run the test under the security agent.
shell_test::enable_agent "$@"
@@ -83,6 +104,12 @@
}
main() {
+ local -r WITH_SUID="${1:-no}"
+ if [[ "${WITH_SUID}" == "--with_suid" ]]; then
+ local -r SUID_USER="$2"
+ SUDO_USER="root"
+ fi
+
cd "${WORKDIR}"
build
@@ -90,16 +117,19 @@
cp "${AGENTD_BIN}" "${SUIDHELPER_BIN}" "${INITHELPER_BIN}" "${DEVICEMANAGER_BIN}" "${BIN_STAGING_DIR}"
shell_test::setup_server_test
- # TODO(caprita): Expose an option to turn --single_user off, so we can run
- # test.sh by hand and exercise the code that requires root privileges.
-
# Install and start device manager.
DM_INSTALL_DIR=$(shell::tmp_dir)
export VANADIUM_DEVICE_DIR="${DM_INSTALL_DIR}/dm"
- "${DEVICE_SCRIPT}" install "${BIN_STAGING_DIR}" --single_user -- --veyron.tcp.address=127.0.0.1:0
+
+ if [[ "${WITH_SUID}" == "--with_suid" ]]; then
+ "${DEVICE_SCRIPT}" install "${BIN_STAGING_DIR}" --veyron.tcp.address=127.0.0.1:0
+ else
+ "${DEVICE_SCRIPT}" install "${BIN_STAGING_DIR}" --single_user -- --veyron.tcp.address=127.0.0.1:0
+ fi
+
"${VRUN}" "${DEVICE_SCRIPT}" start
- local -r DM_NAME=$(hostname)
+ local -r DM_NAME=devices/$(hostname)
DM_EP=$(wait_for_mountentry "${NAMESPACE_BIN}" 5 "${DM_NAME}")
# Verify that device manager is published under the expected name (hostname).
@@ -119,6 +149,12 @@
# Claim the device as "alice/myworkstation".
"${DEVICE_BIN}" claim "${DM_NAME}/device" myworkstation
+ if [[ "${WITH_SUID}" == "--with_suid" ]]; then
+ "${DEVICE_BIN}" associate add "${DM_NAME}/device" "${SUID_USER}" "alice"
+ shell_test::assert_eq "$("${DEVICE_BIN}" associate list "${DM_NAME}/device")" \
+ "alice ${SUID_USER}" "${LINENO}"
+ fi
+
# Verify the device's default blessing is as expected.
shell_test::assert_eq "$("${DEBUG_BIN}" stats read "${DM_NAME}/__debug/stats/security/principal/blessingstore" | head -1 | sed -e 's/^.*Default blessings: '//)" \
"alice/myworkstation" "${LINENO}"
@@ -173,6 +209,8 @@
# Verify that the instance shows up when globbing the device manager.
shell_test::assert_eq "$("${NAMESPACE_BIN}" glob "${DM_NAME}/apps/BINARYD/*/*")" "${INSTANCE_NAME}" "${LINENO}"
+ # TODO(rjkroege): Verify that the app is actually running as ${SUID_USER}
+
# Verify the app's default blessing.
shell_test::assert_eq "$("${DEBUG_BIN}" stats read "${INSTANCE_NAME}/stats/security/principal/blessingstore" | head -1 | sed -e 's/^.*Default blessings: '//)" \
"alice/myapp/BINARYD" "${LINENO}"
diff --git a/tools/servicerunner/main.go b/tools/servicerunner/main.go
index 225e19a..3e0b345 100644
--- a/tools/servicerunner/main.go
+++ b/tools/servicerunner/main.go
@@ -33,7 +33,7 @@
}
numLeft := len(varsToAdd)
- s := expect.NewSession(nil, h.Stdout(), 10*time.Second)
+ s := expect.NewSession(nil, h.Stdout(), 30*time.Second)
for {
l := s.ReadLine()
if err := s.OriginalError(); err != nil {