Merge "veyron/runtimes/google/ipc: Pass client to server to allow discharge client to use client from context."
diff --git a/lib/modules/shell.go b/lib/modules/shell.go
index c6cd898..cc14137 100644
--- a/lib/modules/shell.go
+++ b/lib/modules/shell.go
@@ -136,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
}
@@ -146,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 {
@@ -161,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())
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/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/blessingroots_test.go b/security/blessingroots_test.go
index 49b599f..9e06948 100644
--- a/security/blessingroots_test.go
+++ b/security/blessingroots_test.go
@@ -29,7 +29,7 @@
}{
{t[0], "veyron/..."},
{t[1], "google/foo/..."},
- {t[0], "google"},
+ {t[0], "google/$"},
}
for _, d := range testdata {
if err := br.Add(d.root, d.pattern); err != nil {
diff --git a/security/blessingstore_test.go b/security/blessingstore_test.go
index d328da4..a9b2906 100644
--- a/security/blessingstore_test.go
+++ b/security/blessingstore_test.go
@@ -22,13 +22,12 @@
wantErr string
}{
{t.forAll, "...", ""},
+ {t.forAll, "$", ""},
{t.forFoo, "foo/...", ""},
- {t.forBar, "bar", ""},
+ {t.forBar, "bar/$", ""},
{t.other, "...", "public key does not match"},
{t.forAll, "", "invalid BlessingPattern"},
- {t.forAll, "foo...", "invalid BlessingPattern"},
- {t.forAll, "...foo", "invalid BlessingPattern"},
- {t.forAll, "foo/.../bar", "invalid BlessingPattern"},
+ {t.forAll, "foo/$/bar", "invalid BlessingPattern"},
}
added := make(map[security.BlessingPattern]security.Blessings)
for _, d := range testdata {
@@ -193,7 +192,7 @@
// Set(alice, "alice")
// Set(bob, "alice/...")
// So, {alice, bob} is shared with "alice", whilst {bob} is shared with "alice/tv"
- if _, err := s.Set(alice, "alice"); err != nil {
+ if _, err := s.Set(alice, "alice/$"); err != nil {
t.Fatal(err)
}
if _, err := s.Set(bob, "alice/..."); err != nil {
@@ -208,7 +207,7 @@
// Clear out the blessing associated with "alice".
// Now, bob should be shared with both alice and alice/friend.
- if _, err := s.Set(nil, "alice"); err != nil {
+ if _, err := s.Set(nil, "alice/$"); err != nil {
t.Fatal(err)
}
if got, want := s.ForPeer("alice"), bob; !reflect.DeepEqual(got, want) {
@@ -219,7 +218,7 @@
}
// Clearing out an association that doesn't exist should have no effect.
- if _, err := s.Set(nil, "alice/enemy"); err != nil {
+ if _, err := s.Set(nil, "alice/enemy/$"); err != nil {
t.Fatal(err)
}
if got, want := s.ForPeer("alice"), bob; !reflect.DeepEqual(got, want) {
diff --git a/security/flag/flag_test.go b/security/flag/flag_test.go
index fbf46bc..2416001 100644
--- a/security/flag/flag_test.go
+++ b/security/flag/flag_test.go
@@ -25,10 +25,10 @@
acl1 = access.TaggedACLMap{}
acl2 = access.TaggedACLMap{
string(access.Read): access.ACL{
- In: []security.BlessingPattern{"veyron/alice", "veyron/bob"},
+ In: []security.BlessingPattern{"veyron/alice/$", "veyron/bob"},
},
string(access.Write): access.ACL{
- In: []security.BlessingPattern{"veyron/alice"},
+ In: []security.BlessingPattern{"veyron/alice/$"},
},
}
@@ -94,7 +94,7 @@
},
{
cmd: "tamFromFlag",
- flags: []string{"--veyron.acl.literal", `{"Read": {"In":["veyron/alice", "veyron/bob"]}, "Write": {"In":["veyron/alice"]}}`},
+ flags: []string{"--veyron.acl.literal", `{"Read": {"In":["veyron/alice/$", "veyron/bob"]}, "Write": {"In":["veyron/alice/$"]}}`},
auth: "acl2",
},
}
diff --git a/services/mgmt/binary/impl/acl_test.go b/services/mgmt/binary/impl/acl_test.go
index 8962d52..6c16291 100644
--- a/services/mgmt/binary/impl/acl_test.go
+++ b/services/mgmt/binary/impl/acl_test.go
@@ -20,7 +20,6 @@
"v.io/core/veyron/lib/signals"
"v.io/core/veyron/lib/testutil"
tsecurity "v.io/core/veyron/lib/testutil/security"
- vsecurity "v.io/core/veyron/security"
"v.io/core/veyron/services/mgmt/binary/impl"
mgmttest "v.io/core/veyron/services/mgmt/lib/testutil"
)
@@ -95,27 +94,15 @@
defer cleanup()
prepDirectory(t, storedir)
- otherPrincipal, err := vsecurity.NewPrincipal()
- if err != nil {
- t.Fatalf("NewPrincipal() failed: %v", err)
+ otherPrincipal := tsecurity.NewPrincipal("other")
+ if err := otherPrincipal.AddToRoots(selfPrincipal.BlessingStore().Default()); err != nil {
+ t.Fatalf("otherPrincipal.AddToRoots() failed: %v", err)
}
otherCtx, err := veyron2.SetPrincipal(selfCtx, otherPrincipal)
if err != nil {
t.Fatalf("SetPrincipal() failed: %v", err)
}
- selfBlessing := selfPrincipal.BlessingStore().Default()
- otherBlessing, err := selfPrincipal.Bless(otherPrincipal.PublicKey(), selfBlessing, "other", security.UnconstrainedUse())
- if err != nil {
- t.Fatalf("selfPrincipal.Bless() failed: %v", err)
- }
- if _, err := otherPrincipal.BlessingStore().Set(otherBlessing, "self/child"); err != nil {
- t.Fatalf("otherPrincipal.BlessingStore() failed: %v", err)
- }
- if err := otherPrincipal.AddToRoots(otherBlessing); err != nil {
- t.Fatalf("otherPrincipal.AddToRoots() failed: %v", err)
- }
-
_, nms := mgmttest.RunShellCommand(t, sh, nil, binaryCmd, "bini", storedir)
pid := mgmttest.ReadPID(t, nms)
defer syscall.Kill(pid, syscall.SIGINT)
@@ -139,7 +126,11 @@
t.Fatalf("invokeUpload() failed %v, %v", err, streamErr)
}
- vlog.VI(2).Infof("Verify that other can't access bini/private")
+ vlog.VI(2).Infof("Verify that in the beginning other can't access bini/private or bini/shared")
+ binary = repository.BinaryClient("bini/private")
+ if _, _, err := binary.Stat(otherCtx); !verror.Is(err, verror.NoAccess) {
+ t.Fatalf("Stat() should have failed but didn't: %v", err)
+ }
binary = repository.BinaryClient("bini/shared")
if _, _, err := binary.Stat(otherCtx); !verror.Is(err, verror.NoAccess) {
t.Fatalf("Stat() should have failed but didn't: %v", err)
@@ -153,36 +144,47 @@
}
expected := access.TaggedACLMap{"Admin": access.ACL{In: []security.BlessingPattern{"self"}, NotIn: []string{}}, "Read": access.ACL{In: []security.BlessingPattern{"self"}, NotIn: []string{}}, "Write": access.ACL{In: []security.BlessingPattern{"self"}, NotIn: []string{}}, "Debug": access.ACL{In: []security.BlessingPattern{"self"}, NotIn: []string{}}, "Resolve": access.ACL{In: []security.BlessingPattern{"self"}, NotIn: []string{}}}
if got, want := acl.Normalize(), expected.Normalize(); !reflect.DeepEqual(got, want) {
- t.Errorf("got %#v, exected %#v ", got, want)
+ t.Errorf("got %#v, expected %#v ", got, want)
}
- vlog.VI(2).Infof("Validate the ACL file on bini/shared.")
- binary = repository.BinaryClient("bini/shared")
+ vlog.VI(2).Infof("Validate the ACL file on bini/private.")
+ binary = repository.BinaryClient("bini/private")
acl, etag, err := binary.GetACL(selfCtx)
if err != nil {
t.Fatalf("GetACL failed: %v", err)
}
if got, want := acl.Normalize(), expected.Normalize(); !reflect.DeepEqual(got, want) {
- t.Errorf("got %#v, exected %#v ", got, want)
+ t.Errorf("got %#v, expected %#v ", got, want)
}
- vlog.VI(2).Infof("Self modifies the ACL file on bini/shared.")
+ vlog.VI(2).Infof("self blesses other as self/other and locks the bini/private binary to itself.")
+ selfBlessing := selfPrincipal.BlessingStore().Default()
+ otherBlessing, err := selfPrincipal.Bless(otherPrincipal.PublicKey(), selfBlessing, "other", security.UnconstrainedUse())
+ if err != nil {
+ t.Fatalf("selfPrincipal.Bless() failed: %v", err)
+ }
+ if _, err := otherPrincipal.BlessingStore().Set(otherBlessing, security.AllPrincipals); err != nil {
+ t.Fatalf("otherPrincipal.BlessingStore() failed: %v", err)
+ }
+
+ vlog.VI(2).Infof("Self modifies the ACL file on bini/private.")
for _, tag := range access.AllTypicalTags() {
- acl.Add("self/other", string(tag))
+ acl.Clear("self", string(tag))
+ acl.Add("self/$", string(tag))
}
if err := binary.SetACL(selfCtx, acl, etag); err != nil {
t.Fatalf("SetACL failed: %v", err)
}
- vlog.VI(2).Infof(" Verify that bini/shared's acls are updated.")
- binary = repository.BinaryClient("bini/shared")
- updated := access.TaggedACLMap{"Admin": access.ACL{In: []security.BlessingPattern{"self", "self/other"}, NotIn: []string{}}, "Read": access.ACL{In: []security.BlessingPattern{"self", "self/other"}, NotIn: []string{}}, "Write": access.ACL{In: []security.BlessingPattern{"self", "self/other"}, NotIn: []string{}}, "Debug": access.ACL{In: []security.BlessingPattern{"self", "self/other"}, NotIn: []string{}}, "Resolve": access.ACL{In: []security.BlessingPattern{"self", "self/other"}, NotIn: []string{}}}
+ vlog.VI(2).Infof(" Verify that bini/private's acls are updated.")
+ binary = repository.BinaryClient("bini/private")
+ updated := access.TaggedACLMap{"Admin": access.ACL{In: []security.BlessingPattern{"self/$"}, NotIn: []string{}}, "Read": access.ACL{In: []security.BlessingPattern{"self/$"}, NotIn: []string{}}, "Write": access.ACL{In: []security.BlessingPattern{"self/$"}, NotIn: []string{}}, "Debug": access.ACL{In: []security.BlessingPattern{"self/$"}, NotIn: []string{}}, "Resolve": access.ACL{In: []security.BlessingPattern{"self/$"}, NotIn: []string{}}}
acl, _, err = binary.GetACL(selfCtx)
if err != nil {
t.Fatalf("GetACL failed: %v", err)
}
if got, want := acl.Normalize(), updated.Normalize(); !reflect.DeepEqual(got, want) {
- t.Errorf("got %#v, exected %#v ", got, want)
+ t.Errorf("got %#v, expected %#v ", got, want)
}
// Other still can't access bini/shared because there's no ACL file at the
@@ -198,7 +200,7 @@
binary = repository.BinaryClient("bini")
newRootACL := make(access.TaggedACLMap)
for _, tag := range access.AllTypicalTags() {
- newRootACL.Add("self", string(tag))
+ newRootACL.Add("self/$", string(tag))
}
if err := binary.SetACL(selfCtx, newRootACL, ""); err != nil {
t.Fatalf("SetACL failed: %v", err)
@@ -220,7 +222,7 @@
if err != nil {
t.Fatalf("GetACL() failed: %v", err)
}
- acl.Add("self/other", string("Write"))
+ acl.Add("self", string("Write"))
err = binary.SetACL(selfCtx, acl, tag)
if err != nil {
t.Fatalf("SetACL() failed: %v", err)
@@ -256,7 +258,7 @@
t.Fatalf("GetACL failed: %v", err)
}
if got, want := acl.Normalize(), updated.Normalize(); !reflect.DeepEqual(want, got) {
- t.Errorf("got %#v, exected %#v ", got, want)
+ t.Errorf("got %#v, expected %#v ", got, want)
}
vlog.VI(2).Infof("Other tries to exclude self by adding self to Read's notin")
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 1538c98..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"
@@ -124,7 +125,7 @@
"reflect"
"strconv"
"strings"
- "sync"
+ "text/template"
"time"
"v.io/core/veyron2"
@@ -189,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.
@@ -472,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
@@ -483,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.
@@ -771,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
@@ -1254,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/dispatcher.go b/services/mgmt/device/impl/dispatcher.go
index 30cf651..1ad57b6 100644
--- a/services/mgmt/device/impl/dispatcher.go
+++ b/services/mgmt/device/impl/dispatcher.go
@@ -152,7 +152,9 @@
acl := make(access.TaggedACLMap)
for _, n := range names {
for _, tag := range access.AllTypicalTags() {
- acl.Add(security.BlessingPattern(n), string(tag))
+ // TODO(caprita, ataly, ashankar): Do we really need the NonExtendable restriction
+ // below?
+ acl.Add(security.BlessingPattern(n).MakeNonExtendable(), string(tag))
}
}
if err := d.locks.SetPathACL(principal, d.getACLDir(), acl, ""); err != nil {
diff --git a/services/mgmt/device/impl/impl_test.go b/services/mgmt/device/impl/impl_test.go
index 4a6fee9..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")
@@ -917,7 +935,7 @@
}
expectedACL := make(access.TaggedACLMap)
for _, tag := range access.AllTypicalTags() {
- expectedACL[string(tag)] = access.ACL{In: []security.BlessingPattern{"root/self/mydevice"}}
+ expectedACL[string(tag)] = access.ACL{In: []security.BlessingPattern{"root/self/mydevice/$"}}
}
var b bytes.Buffer
if err := expectedACL.WriteTo(&b); err != nil {
@@ -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/system.go b/services/mgmt/suidhelper/impl/system.go
index 8a93d91..c634b59 100644
--- a/services/mgmt/suidhelper/impl/system.go
+++ b/services/mgmt/suidhelper/impl/system.go
@@ -43,11 +43,12 @@
}
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 {
- attr.Sys = new(syscall.SysProcAttr)
attr.Sys.Credential = new(syscall.Credential)
attr.Sys.Credential.Gid = uint32(hw.gid)
attr.Sys.Credential.Uid = uint32(hw.uid)
diff --git a/tools/mgmt/device/impl/devicemanager_mock_test.go b/tools/mgmt/device/impl/devicemanager_mock_test.go
index 3777043..62cf94a 100644
--- a/tools/mgmt/device/impl/devicemanager_mock_test.go
+++ b/tools/mgmt/device/impl/devicemanager_mock_test.go
@@ -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/impl.go b/tools/mgmt/device/impl/impl.go
index 3a60782..8762b8a 100644
--- a/tools/mgmt/device/impl/impl.go
+++ b/tools/mgmt/device/impl/impl.go
@@ -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 {
@@ -135,3 +135,72 @@
}
return nil
}
+
+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/impl_test.go b/tools/mgmt/device/impl/impl_test.go
index dc53e15..b841ee6 100644
--- a/tools/mgmt/device/impl/impl_test.go
+++ b/tools/mgmt/device/impl/impl_test.go
@@ -418,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/impl/root.go b/tools/mgmt/device/impl/root.go
index 9228247..db04609 100644
--- a/tools/mgmt/device/impl/root.go
+++ b/tools/mgmt/device/impl/root.go
@@ -19,6 +19,6 @@
Long: `
The device tool facilitates interaction with the veyron device manager.
`,
- Children: []*cmdline.Command{cmdInstall, cmdStart, associateRoot(), cmdDescribe, cmdClaim, cmdStop, cmdSuspend, cmdResume, aclRoot()},
+ Children: []*cmdline.Command{cmdInstall, cmdStart, associateRoot(), cmdDescribe, cmdClaim, cmdStop, cmdSuspend, cmdResume, cmdRevert, cmdUpdate, cmdDebug, aclRoot()},
}
}
diff --git a/tools/mgmt/test.sh b/tools/mgmt/test.sh
index 0628c47..6441eb7 100755
--- a/tools/mgmt/test.sh
+++ b/tools/mgmt/test.sh
@@ -129,7 +129,7 @@
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).
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 {