| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package rt |
| |
| import ( |
| "encoding/base64" |
| "time" |
| |
| "v.io/v23" |
| "v.io/v23/context" |
| "v.io/v23/rpc" |
| "v.io/v23/security" |
| "v.io/v23/verror" |
| "v.io/v23/vom" |
| |
| "v.io/x/ref/lib/exec" |
| "v.io/x/ref/lib/mgmt" |
| vsecurity "v.io/x/ref/lib/security" |
| ) |
| |
| const pkgPath = "v.io/x/ref/option/internal/rt" |
| |
| var ( |
| errInvalidConfig = verror.Register(pkgPath+".errInvalidConfig", verror.NoRetry, "{1:}{2:} failed to decode config from the OS environment {:_}") |
| errConfigKeyNotSet = verror.Register(pkgPath+".errConfigKeyNotSet", verror.NoRetry, "{1:}{2:} {3} is not set{:_}") |
| ) |
| |
| func (rt *Runtime) initMgmt(ctx *context.T) error { |
| config, err := exec.ReadConfigFromOSEnv() |
| if err != nil { |
| return verror.New(errInvalidConfig, ctx, err) |
| } |
| if config == nil { |
| // Config was not set. |
| return nil |
| } |
| |
| parentName, err := config.Get(mgmt.ParentNameConfigKey) |
| if ctx, err = setListenSpec(ctx, rt, config); err != nil { |
| return err |
| } |
| var blessings security.Blessings |
| if b64blessings, err := config.Get(mgmt.AppCycleBlessingsKey); err == nil { |
| vombytes, err := base64.URLEncoding.DecodeString(b64blessings) |
| if err == nil { |
| err = vom.Decode(vombytes, &blessings) |
| } |
| if err != nil { |
| return err |
| } |
| } else { |
| // TODO(caprita,ashankar): This case should only happen when |
| // the device manager starts itself up as a child process for |
| // testing (see services/device/deviced/internal/impl/device_service.go, |
| // specifically testDeviceManager function). |
| // FIx that up so it is no longer the case? |
| blessings, _ = rt.GetPrincipal(ctx).BlessingStore().Default() |
| } |
| // Arguably could use the same principal with a different blessing store |
| // and blessing roots. However, at the time of this writing there wasn't |
| // a particularly convenient way to do so, and this alternative scheme |
| // works just as well. |
| // |
| // Instantiate a principal that will be used by the AppCycleManager |
| // which will communicate with (both as a client and a server) only the |
| // device manager. |
| principal, err := vsecurity.NewPrincipal() |
| if err != nil { |
| return err |
| } |
| blessings, err = rt.GetPrincipal(ctx).Bless(principal.PublicKey(), blessings, "instance", security.UnconstrainedUse()) |
| if err != nil { |
| return err |
| } |
| if err := security.AddToRoots(principal, blessings); err != nil { |
| return err |
| } |
| if err := principal.BlessingStore().SetDefault(blessings); err != nil { |
| return err |
| } |
| if _, err := principal.BlessingStore().Set(blessings, security.AllPrincipals); err != nil { |
| return err |
| } |
| if ctx, err = rt.WithPrincipal(ctx, principal); err != nil { |
| return err |
| } |
| // Fire up the server to receive calls from the parent and also ping the parent. |
| ctx, cancel := context.WithCancel(ctx) |
| _, server, err := rt.WithNewServer(ctx, "", v23.GetAppCycle(ctx).Remote(), nil) |
| if err != nil { |
| return err |
| } |
| if err = rt.callbackToParent(ctx, parentName, server.Status().Endpoints[0].Name()); err != nil { |
| cancel() |
| <-server.Closed() |
| return err |
| } |
| return nil |
| } |
| |
| func setListenSpec(ctx *context.T, rt *Runtime, config exec.Config) (*context.T, error) { |
| protocol, err := config.Get(mgmt.ProtocolConfigKey) |
| if err != nil { |
| return nil, err |
| } |
| if protocol == "" { |
| return nil, verror.New(errConfigKeyNotSet, ctx, mgmt.ProtocolConfigKey) |
| } |
| |
| address, err := config.Get(mgmt.AddressConfigKey) |
| if err != nil { |
| return nil, err |
| } |
| if address == "" { |
| return nil, verror.New(errConfigKeyNotSet, ctx, mgmt.AddressConfigKey) |
| } |
| return rt.WithListenSpec(ctx, rpc.ListenSpec{Addrs: rpc.ListenAddrs{{protocol, address}}}), nil |
| } |
| |
| func (rt *Runtime) callbackToParent(ctx *context.T, parentName, myName string) error { |
| ctx, _ = context.WithTimeout(ctx, time.Minute) |
| return rt.GetClient(ctx).Call(ctx, parentName, "Set", []interface{}{mgmt.AppCycleManagerConfigKey, myName}, nil) |
| } |