Jiri Simsa | d7616c9 | 2015-03-24 23:44:30 -0700 | [diff] [blame] | 1 | // Copyright 2015 The Vanadium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 5 | package impl |
| 6 | |
| 7 | import ( |
Bogdan Caprita | c87a914 | 2014-07-21 10:38:13 -0700 | [diff] [blame] | 8 | "fmt" |
Gautham | 82bb995 | 2014-08-28 14:11:51 -0700 | [diff] [blame] | 9 | "os" |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 10 | "path" |
Gautham | 82bb995 | 2014-08-28 14:11:51 -0700 | [diff] [blame] | 11 | "path/filepath" |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 12 | "strings" |
Gautham | 6fe61e5 | 2014-09-16 13:58:17 -0700 | [diff] [blame] | 13 | "sync" |
Jiri Simsa | 24e87aa | 2014-06-09 09:27:34 -0700 | [diff] [blame] | 14 | |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 15 | "v.io/v23/context" |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 16 | "v.io/v23/naming" |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 17 | "v.io/v23/rpc" |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 18 | "v.io/v23/security" |
Todd Wang | 387d8a4 | 2015-03-30 17:09:05 -0700 | [diff] [blame] | 19 | "v.io/v23/security/access" |
Todd Wang | 94c9d0b | 2015-04-01 14:27:00 -0700 | [diff] [blame] | 20 | "v.io/v23/services/device" |
| 21 | "v.io/v23/services/pprof" |
| 22 | "v.io/v23/services/stats" |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 23 | "v.io/v23/vdl" |
| 24 | "v.io/v23/vdlroot/signature" |
Jiri Simsa | 6ac9522 | 2015-02-23 16:11:49 -0800 | [diff] [blame] | 25 | "v.io/v23/verror" |
Jiri Simsa | 337af23 | 2015-02-27 14:36:46 -0800 | [diff] [blame] | 26 | "v.io/x/lib/vlog" |
Todd Wang | 8123b5e | 2015-05-14 18:44:43 -0700 | [diff] [blame] | 27 | "v.io/x/ref" |
Todd Wang | b351149 | 2015-04-07 23:32:34 -0700 | [diff] [blame] | 28 | "v.io/x/ref/services/agent/keymgr" |
| 29 | s_device "v.io/x/ref/services/device" |
| 30 | "v.io/x/ref/services/device/internal/config" |
Bogdan Caprita | 040603b | 2015-06-23 18:19:30 -0700 | [diff] [blame^] | 31 | "v.io/x/ref/services/device/internal/errors" |
Todd Wang | fb93903 | 2015-04-08 16:42:44 -0700 | [diff] [blame] | 32 | "v.io/x/ref/services/internal/logreaderlib" |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 33 | "v.io/x/ref/services/internal/pathperms" |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 34 | ) |
| 35 | |
Bogdan Caprita | 2b21936 | 2014-12-09 17:03:33 -0800 | [diff] [blame] | 36 | // internalState wraps state shared between different device manager |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 37 | // invocations. |
| 38 | type internalState struct { |
Bogdan Caprita | 29a3b35 | 2015-01-16 16:28:49 -0800 | [diff] [blame] | 39 | callback *callbackState |
| 40 | updating *updatingState |
| 41 | securityAgent *securityAgentState |
| 42 | restartHandler func() |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 43 | testMode bool |
Robert Kroeger | cc9f55d | 2015-05-20 16:25:36 -0700 | [diff] [blame] | 44 | // reap is the app process monitoring subsystem. |
| 45 | reap *reaper |
Robert Kroeger | 1f6b891 | 2015-06-18 10:29:39 -0700 | [diff] [blame] | 46 | // tidying is the automatic state tidying subsystem. |
| 47 | tidying chan<- tidyRequests |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 48 | } |
| 49 | |
Bogdan Caprita | 2b21936 | 2014-12-09 17:03:33 -0800 | [diff] [blame] | 50 | // dispatcher holds the state of the device manager dispatcher. |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 51 | type dispatcher struct { |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 52 | // internal holds the state that persists across RPC method invocations. |
Bogdan Caprita | c87a914 | 2014-07-21 10:38:13 -0700 | [diff] [blame] | 53 | internal *internalState |
Bogdan Caprita | 2b21936 | 2014-12-09 17:03:33 -0800 | [diff] [blame] | 54 | // config holds the device manager's (immutable) configuration state. |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 55 | config *config.State |
Gautham | 6fe61e5 | 2014-09-16 13:58:17 -0700 | [diff] [blame] | 56 | // dispatcherMutex is a lock for coordinating concurrent access to some |
| 57 | // dispatcher methods. |
Bogdan Caprita | 7f49167 | 2014-11-13 14:51:08 -0800 | [diff] [blame] | 58 | mu sync.RWMutex |
| 59 | // TODO(rjkroege): Consider moving this inside internal. |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 60 | uat BlessingSystemAssociationStore |
| 61 | permsStore *pathperms.PathStore |
Robin Thellend | acaa432 | 2015-02-05 11:00:28 -0800 | [diff] [blame] | 62 | // Namespace |
| 63 | mtAddress string // The address of the local mounttable. |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 64 | } |
| 65 | |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 66 | var _ rpc.Dispatcher = (*dispatcher)(nil) |
Benjamin Prosnitz | fdfbf7b | 2014-10-08 09:47:21 -0700 | [diff] [blame] | 67 | |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 68 | const ( |
| 69 | appsSuffix = "apps" |
Bogdan Caprita | 9c4aa22 | 2014-12-10 14:46:30 -0800 | [diff] [blame] | 70 | deviceSuffix = "device" |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 71 | configSuffix = "cfg" |
Todd Wang | 34ed4c6 | 2014-11-26 15:15:52 -0800 | [diff] [blame] | 72 | |
Todd Wang | cd4b3cc | 2015-04-06 16:42:02 -0700 | [diff] [blame] | 73 | pkgPath = "v.io/x/ref/services/device/internal/impl" |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 74 | ) |
| 75 | |
| 76 | var ( |
Mike Burrows | 39bbaaf | 2015-03-24 11:27:32 -0700 | [diff] [blame] | 77 | errInvalidConfig = verror.Register(pkgPath+".errInvalidConfig", verror.NoRetry, "{1:}{2:} invalid config {3}{:_}") |
| 78 | errCantCreateAccountStore = verror.Register(pkgPath+".errCantCreateAccountStore", verror.NoRetry, "{1:}{2:} cannot create persistent store for identity to system account associations{:_}") |
| 79 | errCantCreateAppWatcher = verror.Register(pkgPath+".errCantCreateAppWatcher", verror.NoRetry, "{1:}{2:} cannot create app status watcher{:_}") |
| 80 | errNewAgentFailed = verror.Register(pkgPath+".errNewAgentFailed", verror.NoRetry, "{1:}{2:} NewAgent() failed{:_}") |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 81 | ) |
| 82 | |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 83 | // NewClaimableDispatcher returns an rpc.Dispatcher that allows the device to |
Asim Shankar | 23dac32 | 2015-02-14 12:42:26 -0800 | [diff] [blame] | 84 | // be Claimed if it hasn't been already and a channel that will be closed once |
| 85 | // the device has been claimed. |
| 86 | // |
| 87 | // It returns (nil, nil) if the device is no longer claimable. |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 88 | func NewClaimableDispatcher(ctx *context.T, config *config.State, pairingToken string) (rpc.Dispatcher, <-chan struct{}) { |
Asim Shankar | 23dac32 | 2015-02-14 12:42:26 -0800 | [diff] [blame] | 89 | var ( |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 90 | permsDir = PermsDir(config) |
Cosmos Nicolaou | 7a4221f | 2015-06-21 08:02:23 -0700 | [diff] [blame] | 91 | permsStore = pathperms.NewPathStore(ctx) |
Asim Shankar | 23dac32 | 2015-02-14 12:42:26 -0800 | [diff] [blame] | 92 | ) |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 93 | if _, _, err := permsStore.Get(permsDir); !os.IsNotExist(err) { |
Asim Shankar | 23dac32 | 2015-02-14 12:42:26 -0800 | [diff] [blame] | 94 | return nil, nil |
| 95 | } |
| 96 | // The device is claimable only if Claim hasn't been called before. The |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 97 | // existence of the Permissions file is an indication of a successful prior |
Asim Shankar | 23dac32 | 2015-02-14 12:42:26 -0800 | [diff] [blame] | 98 | // call to Claim. |
| 99 | notify := make(chan struct{}) |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 100 | return &claimable{token: pairingToken, permsStore: permsStore, permsDir: permsDir, notify: notify}, notify |
Asim Shankar | 23dac32 | 2015-02-14 12:42:26 -0800 | [diff] [blame] | 101 | } |
| 102 | |
Bogdan Caprita | 2b21936 | 2014-12-09 17:03:33 -0800 | [diff] [blame] | 103 | // NewDispatcher is the device manager dispatcher factory. |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 104 | func NewDispatcher(ctx *context.T, config *config.State, mtAddress string, testMode bool, restartHandler func(), permStore *pathperms.PathStore) (rpc.Dispatcher, error) { |
Bogdan Caprita | c87a914 | 2014-07-21 10:38:13 -0700 | [diff] [blame] | 105 | if err := config.Validate(); err != nil { |
Mike Burrows | 39bbaaf | 2015-03-24 11:27:32 -0700 | [diff] [blame] | 106 | return nil, verror.New(errInvalidConfig, ctx, config, err) |
Bogdan Caprita | c87a914 | 2014-07-21 10:38:13 -0700 | [diff] [blame] | 107 | } |
Robert Kroeger | 1cb4a0d | 2014-10-20 11:55:38 -0700 | [diff] [blame] | 108 | uat, err := NewBlessingSystemAssociationStore(config.Root) |
| 109 | if err != nil { |
Mike Burrows | 39bbaaf | 2015-03-24 11:27:32 -0700 | [diff] [blame] | 110 | return nil, verror.New(errCantCreateAccountStore, ctx, err) |
Robert Kroeger | 1cb4a0d | 2014-10-20 11:55:38 -0700 | [diff] [blame] | 111 | } |
Arup Mukherjee | 746444f | 2015-04-16 19:13:24 -0700 | [diff] [blame] | 112 | initSuidHelper(config.Helper) |
Gautham | 82bb995 | 2014-08-28 14:11:51 -0700 | [diff] [blame] | 113 | d := &dispatcher{ |
Bogdan Caprita | c87a914 | 2014-07-21 10:38:13 -0700 | [diff] [blame] | 114 | internal: &internalState{ |
Bogdan Caprita | 29a3b35 | 2015-01-16 16:28:49 -0800 | [diff] [blame] | 115 | callback: newCallbackState(config.Name), |
| 116 | updating: newUpdatingState(), |
| 117 | restartHandler: restartHandler, |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 118 | testMode: testMode, |
Robert Kroeger | 1f6b891 | 2015-06-18 10:29:39 -0700 | [diff] [blame] | 119 | tidying: newTidyingDaemon(config.Root), |
Jiri Simsa | 70c3205 | 2014-06-18 11:38:21 -0700 | [diff] [blame] | 120 | }, |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 121 | config: config, |
| 122 | uat: uat, |
| 123 | permsStore: permStore, |
| 124 | mtAddress: mtAddress, |
Gautham | 82bb995 | 2014-08-28 14:11:51 -0700 | [diff] [blame] | 125 | } |
Robert Kroeger | e95ed6d | 2015-01-14 17:41:04 -0800 | [diff] [blame] | 126 | |
Bogdan Caprita | 7f49167 | 2014-11-13 14:51:08 -0800 | [diff] [blame] | 127 | // If we're in 'security agent mode', set up the key manager agent. |
Todd Wang | 8123b5e | 2015-05-14 18:44:43 -0700 | [diff] [blame] | 128 | if len(os.Getenv(ref.EnvAgentEndpoint)) > 0 { |
Bogdan Caprita | 7f49167 | 2014-11-13 14:51:08 -0800 | [diff] [blame] | 129 | if keyMgrAgent, err := keymgr.NewAgent(); err != nil { |
Mike Burrows | 39bbaaf | 2015-03-24 11:27:32 -0700 | [diff] [blame] | 130 | return nil, verror.New(errNewAgentFailed, ctx, err) |
Bogdan Caprita | 7f49167 | 2014-11-13 14:51:08 -0800 | [diff] [blame] | 131 | } else { |
| 132 | d.internal.securityAgent = &securityAgentState{ |
| 133 | keyMgrAgent: keyMgrAgent, |
| 134 | } |
| 135 | } |
| 136 | } |
Robert Kroeger | cc9f55d | 2015-05-20 16:25:36 -0700 | [diff] [blame] | 137 | reap, err := newReaper(ctx, config.Root, &appRunner{ |
| 138 | callback: d.internal.callback, |
| 139 | securityAgent: d.internal.securityAgent, |
| 140 | }) |
| 141 | if err != nil { |
| 142 | return nil, verror.New(errCantCreateAppWatcher, ctx, err) |
| 143 | } |
| 144 | d.internal.reap = reap |
| 145 | |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 146 | if testMode { |
| 147 | return &testModeDispatcher{d}, nil |
| 148 | } |
Gautham | 82bb995 | 2014-08-28 14:11:51 -0700 | [diff] [blame] | 149 | return d, nil |
| 150 | } |
| 151 | |
Robert Kroeger | 936853a | 2015-01-28 17:42:55 -0800 | [diff] [blame] | 152 | // Shutdown the dispatcher. |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 153 | func Shutdown(rpcd rpc.Dispatcher) { |
| 154 | switch d := rpcd.(type) { |
Robert Kroeger | 936853a | 2015-01-28 17:42:55 -0800 | [diff] [blame] | 155 | case *dispatcher: |
Robert Kroeger | cc9f55d | 2015-05-20 16:25:36 -0700 | [diff] [blame] | 156 | d.internal.reap.shutdown() |
Robert Kroeger | 936853a | 2015-01-28 17:42:55 -0800 | [diff] [blame] | 157 | case *testModeDispatcher: |
| 158 | Shutdown(d.realDispatcher) |
| 159 | default: |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 160 | vlog.Panicf("%v not a supported dispatcher type.", rpcd) |
Robert Kroeger | 936853a | 2015-01-28 17:42:55 -0800 | [diff] [blame] | 161 | } |
| 162 | } |
| 163 | |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 164 | // Logging invoker that logs any error messages before returning. |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 165 | func newLoggingInvoker(obj interface{}) (rpc.Invoker, error) { |
| 166 | if invoker, ok := obj.(rpc.Invoker); ok { |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 167 | return &loggingInvoker{invoker}, nil |
| 168 | } |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 169 | invoker, err := rpc.ReflectInvoker(obj) |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 170 | if err != nil { |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 171 | vlog.Errorf("rpc.ReflectInvoker returned error: %v", err) |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 172 | return nil, err |
| 173 | } |
| 174 | return &loggingInvoker{invoker}, nil |
| 175 | } |
| 176 | |
| 177 | type loggingInvoker struct { |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 178 | invoker rpc.Invoker |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 179 | } |
| 180 | |
| 181 | func (l *loggingInvoker) Prepare(method string, numArgs int) (argptrs []interface{}, tags []*vdl.Value, err error) { |
| 182 | argptrs, tags, err = l.invoker.Prepare(method, numArgs) |
| 183 | if err != nil { |
| 184 | vlog.Errorf("Prepare(%s %d) returned error: %v", method, numArgs, err) |
| 185 | } |
| 186 | return |
| 187 | } |
| 188 | |
Todd Wang | 54feabe | 2015-04-15 23:38:26 -0700 | [diff] [blame] | 189 | func (l *loggingInvoker) Invoke(ctx *context.T, call rpc.StreamServerCall, method string, argptrs []interface{}) (results []interface{}, err error) { |
| 190 | results, err = l.invoker.Invoke(ctx, call, method, argptrs) |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 191 | if err != nil { |
| 192 | vlog.Errorf("Invoke(method:%s argptrs:%v) returned error: %v", method, argptrs, err) |
| 193 | } |
| 194 | return |
| 195 | } |
| 196 | |
Todd Wang | 54feabe | 2015-04-15 23:38:26 -0700 | [diff] [blame] | 197 | func (l *loggingInvoker) Signature(ctx *context.T, call rpc.ServerCall) ([]signature.Interface, error) { |
| 198 | sig, err := l.invoker.Signature(ctx, call) |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 199 | if err != nil { |
| 200 | vlog.Errorf("Signature returned error: %v", err) |
| 201 | } |
| 202 | return sig, err |
| 203 | } |
| 204 | |
Todd Wang | 54feabe | 2015-04-15 23:38:26 -0700 | [diff] [blame] | 205 | func (l *loggingInvoker) MethodSignature(ctx *context.T, call rpc.ServerCall, method string) (signature.Method, error) { |
| 206 | methodSig, err := l.invoker.MethodSignature(ctx, call, method) |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 207 | if err != nil { |
| 208 | vlog.Errorf("MethodSignature(%s) returned error: %v", method, err) |
| 209 | } |
| 210 | return methodSig, err |
| 211 | } |
| 212 | |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 213 | func (l *loggingInvoker) Globber() *rpc.GlobState { |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 214 | return l.invoker.Globber() |
| 215 | } |
| 216 | |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 217 | // DISPATCHER INTERFACE IMPLEMENTATION |
Robin Thellend | a02fe8f | 2014-11-19 09:58:29 -0800 | [diff] [blame] | 218 | func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) { |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 219 | invoker, auth, err := d.internalLookup(suffix) |
| 220 | if err != nil { |
| 221 | return nil, nil, err |
| 222 | } |
| 223 | loggingInvoker, err := newLoggingInvoker(invoker) |
| 224 | if err != nil { |
| 225 | return nil, nil, err |
| 226 | } |
| 227 | return loggingInvoker, auth, nil |
| 228 | } |
| 229 | |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 230 | func newTestableHierarchicalAuth(testMode bool, rootDir, childDir string, get pathperms.PermsGetter) (security.Authorizer, error) { |
Robert Kroeger | 7a8b222 | 2015-03-06 07:58:57 -0800 | [diff] [blame] | 231 | if testMode { |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 232 | // In test mode, the device manager will not be able to read the |
| 233 | // Permissions, because they were signed with the key of the real device |
| 234 | // manager. It's not a problem because the testModeDispatcher overrides the |
| 235 | // authorizer anyway. |
Robert Kroeger | 7a8b222 | 2015-03-06 07:58:57 -0800 | [diff] [blame] | 236 | return nil, nil |
| 237 | } |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 238 | return pathperms.NewHierarchicalAuthorizer(rootDir, childDir, get) |
Robert Kroeger | 7a8b222 | 2015-03-06 07:58:57 -0800 | [diff] [blame] | 239 | } |
| 240 | |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 241 | func (d *dispatcher) internalLookup(suffix string) (interface{}, security.Authorizer, error) { |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 242 | components := strings.Split(suffix, "/") |
| 243 | for i := 0; i < len(components); i++ { |
| 244 | if len(components[i]) == 0 { |
| 245 | components = append(components[:i], components[i+1:]...) |
| 246 | i-- |
| 247 | } |
| 248 | } |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 249 | |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 250 | // TODO(rjkroege): Permit the root Permissions to diverge for the device and |
| 251 | // app sub-namespaces of the device manager after claiming. |
| 252 | auth, err := newTestableHierarchicalAuth(d.internal.testMode, PermsDir(d.config), PermsDir(d.config), d.permsStore) |
Robert Kroeger | e95ed6d | 2015-01-14 17:41:04 -0800 | [diff] [blame] | 253 | if err != nil { |
| 254 | return nil, nil, err |
| 255 | } |
| 256 | |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 257 | if len(components) == 0 { |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 258 | return rpc.ChildrenGlobberInvoker(deviceSuffix, appsSuffix), auth, nil |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 259 | } |
Bogdan Caprita | 2b21936 | 2014-12-09 17:03:33 -0800 | [diff] [blame] | 260 | // The implementation of the device manager is split up into several |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 261 | // invokers, which are instantiated depending on the receiver name |
| 262 | // prefix. |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 263 | switch components[0] { |
Bogdan Caprita | 2b21936 | 2014-12-09 17:03:33 -0800 | [diff] [blame] | 264 | case deviceSuffix: |
Bogdan Caprita | a456f47 | 2014-12-10 10:18:03 -0800 | [diff] [blame] | 265 | receiver := device.DeviceServer(&deviceService{ |
Bogdan Caprita | 29a3b35 | 2015-01-16 16:28:49 -0800 | [diff] [blame] | 266 | callback: d.internal.callback, |
| 267 | updating: d.internal.updating, |
| 268 | restartHandler: d.internal.restartHandler, |
| 269 | config: d.config, |
| 270 | disp: d, |
| 271 | uat: d.uat, |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 272 | securityAgent: d.internal.securityAgent, |
Robert Kroeger | 1f6b891 | 2015-06-18 10:29:39 -0700 | [diff] [blame] | 273 | tidying: d.internal.tidying, |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 274 | }) |
Robert Kroeger | e95ed6d | 2015-01-14 17:41:04 -0800 | [diff] [blame] | 275 | return receiver, auth, nil |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 276 | case appsSuffix: |
Robin Thellend | 8a0f04f | 2014-11-17 10:40:24 -0800 | [diff] [blame] | 277 | // Requests to apps/*/*/*/logs are handled locally by LogFileService. |
Robin Thellend | b9dd9bb | 2014-10-29 13:54:08 -0700 | [diff] [blame] | 278 | // Requests to apps/*/*/*/pprof are proxied to the apps' __debug/pprof object. |
| 279 | // Requests to apps/*/*/*/stats are proxied to the apps' __debug/stats object. |
Robin Thellend | 9bc8fcb | 2014-11-17 10:23:04 -0800 | [diff] [blame] | 280 | // Everything else is handled by the Application server. |
Robin Thellend | ac7128c | 2014-11-11 09:58:28 -0800 | [diff] [blame] | 281 | if len(components) >= 5 { |
Robin Thellend | 4c5266e | 2014-10-27 13:19:29 -0700 | [diff] [blame] | 282 | appInstanceDir, err := instanceDir(d.config.Root, components[1:4]) |
| 283 | if err != nil { |
| 284 | return nil, nil, err |
| 285 | } |
Robin Thellend | b9dd9bb | 2014-10-29 13:54:08 -0700 | [diff] [blame] | 286 | switch kind := components[4]; kind { |
| 287 | case "logs": |
| 288 | logsDir := filepath.Join(appInstanceDir, "logs") |
| 289 | suffix := naming.Join(components[5:]...) |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 290 | appSpecificAuthorizer, err := newAppSpecificAuthorizer(auth, d.config, components[1:], d.permsStore) |
Robert Kroeger | 8f914be | 2015-03-14 16:32:37 -0700 | [diff] [blame] | 291 | if err != nil { |
| 292 | return nil, nil, err |
| 293 | } |
Todd Wang | 1ea8f19 | 2015-04-03 17:31:51 -0700 | [diff] [blame] | 294 | return logreaderlib.NewLogFileService(logsDir, suffix), appSpecificAuthorizer, nil |
Robin Thellend | b9dd9bb | 2014-10-29 13:54:08 -0700 | [diff] [blame] | 295 | case "pprof", "stats": |
gauthamt | fd1e34e | 2015-03-05 15:30:52 -0800 | [diff] [blame] | 296 | info, err := loadInstanceInfo(nil, appInstanceDir) |
Robin Thellend | b9dd9bb | 2014-10-29 13:54:08 -0700 | [diff] [blame] | 297 | if err != nil { |
| 298 | return nil, nil, err |
| 299 | } |
Bogdan Caprita | 2b05032 | 2015-04-17 09:04:03 -0700 | [diff] [blame] | 300 | if !instanceStateIs(appInstanceDir, device.InstanceStateRunning) { |
Bogdan Caprita | 040603b | 2015-06-23 18:19:30 -0700 | [diff] [blame^] | 301 | return nil, nil, verror.New(errors.ErrInvalidSuffix, nil) |
Robin Thellend | b9dd9bb | 2014-10-29 13:54:08 -0700 | [diff] [blame] | 302 | } |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 303 | var desc []rpc.InterfaceDesc |
Todd Wang | f519f8f | 2015-01-21 10:07:41 -0800 | [diff] [blame] | 304 | switch kind { |
| 305 | case "pprof": |
| 306 | desc = pprof.PProfServer(nil).Describe__() |
| 307 | case "stats": |
| 308 | desc = stats.StatsServer(nil).Describe__() |
Robin Thellend | b9dd9bb | 2014-10-29 13:54:08 -0700 | [diff] [blame] | 309 | } |
| 310 | suffix := naming.Join("__debug", naming.Join(components[4:]...)) |
| 311 | remote := naming.JoinAddressName(info.AppCycleMgrName, suffix) |
Robert Kroeger | 16ee22b | 2015-03-12 14:57:09 -0700 | [diff] [blame] | 312 | |
| 313 | // Use hierarchical auth with debugacls under debug access. |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 314 | appSpecificAuthorizer, err := newAppSpecificAuthorizer(auth, d.config, components[1:], d.permsStore) |
Robert Kroeger | 16ee22b | 2015-03-12 14:57:09 -0700 | [diff] [blame] | 315 | if err != nil { |
| 316 | return nil, nil, err |
| 317 | } |
| 318 | return newProxyInvoker(remote, access.Debug, desc), appSpecificAuthorizer, nil |
Robin Thellend | b9dd9bb | 2014-10-29 13:54:08 -0700 | [diff] [blame] | 319 | } |
Robin Thellend | 4c5266e | 2014-10-27 13:19:29 -0700 | [diff] [blame] | 320 | } |
Bogdan Caprita | a456f47 | 2014-12-10 10:18:03 -0800 | [diff] [blame] | 321 | receiver := device.ApplicationServer(&appService{ |
Robert Kroeger | 450fdf1 | 2015-05-19 14:40:42 -0700 | [diff] [blame] | 322 | config: d.config, |
| 323 | suffix: components[1:], |
| 324 | uat: d.uat, |
| 325 | permsStore: d.permsStore, |
Robert Kroeger | cc9f55d | 2015-05-20 16:25:36 -0700 | [diff] [blame] | 326 | runner: &appRunner{ |
| 327 | reap: d.internal.reap, |
Robert Kroeger | 450fdf1 | 2015-05-19 14:40:42 -0700 | [diff] [blame] | 328 | callback: d.internal.callback, |
| 329 | securityAgent: d.internal.securityAgent, |
| 330 | mtAddress: d.mtAddress, |
| 331 | }, |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 332 | }) |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 333 | appSpecificAuthorizer, err := newAppSpecificAuthorizer(auth, d.config, components[1:], d.permsStore) |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 334 | if err != nil { |
| 335 | return nil, nil, err |
| 336 | } |
Cosmos Nicolaou | 710daa2 | 2014-11-11 19:39:18 -0800 | [diff] [blame] | 337 | return receiver, appSpecificAuthorizer, nil |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 338 | case configSuffix: |
| 339 | if len(components) != 2 { |
Bogdan Caprita | 040603b | 2015-06-23 18:19:30 -0700 | [diff] [blame^] | 340 | return nil, nil, verror.New(errors.ErrInvalidSuffix, nil) |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 341 | } |
Todd Wang | cd4b3cc | 2015-04-06 16:42:02 -0700 | [diff] [blame] | 342 | receiver := s_device.ConfigServer(&configService{ |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 343 | callback: d.internal.callback, |
| 344 | suffix: components[1], |
| 345 | }) |
Bogdan Caprita | b9501d1 | 2014-10-10 15:02:03 -0700 | [diff] [blame] | 346 | // The nil authorizer ensures that only principals blessed by |
Bogdan Caprita | 2b21936 | 2014-12-09 17:03:33 -0800 | [diff] [blame] | 347 | // the device manager can talk back to it. All apps started by |
| 348 | // the device manager should fall in that category. |
Bogdan Caprita | b9501d1 | 2014-10-10 15:02:03 -0700 | [diff] [blame] | 349 | // |
| 350 | // TODO(caprita,rjkroege): We should further refine this, by |
| 351 | // only allowing the app to update state referring to itself |
| 352 | // (and not other apps). |
Cosmos Nicolaou | 710daa2 | 2014-11-11 19:39:18 -0800 | [diff] [blame] | 353 | return receiver, nil, nil |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 354 | default: |
Bogdan Caprita | 040603b | 2015-06-23 18:19:30 -0700 | [diff] [blame^] | 355 | return nil, nil, verror.New(errors.ErrInvalidSuffix, nil) |
Bogdan Caprita | 4d67c04 | 2014-08-19 10:41:19 -0700 | [diff] [blame] | 356 | } |
Jiri Simsa | 24e87aa | 2014-06-09 09:27:34 -0700 | [diff] [blame] | 357 | } |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 358 | |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 359 | // testModeDispatcher is a wrapper around the real dispatcher. It returns the |
| 360 | // exact same object as the real dispatcher, but the authorizer only allows |
Bogdan Caprita | 2b05032 | 2015-04-17 09:04:03 -0700 | [diff] [blame] | 361 | // calls to "device".Delete(). |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 362 | type testModeDispatcher struct { |
Matt Rosencrantz | 94502cf | 2015-03-18 09:43:44 -0700 | [diff] [blame] | 363 | realDispatcher rpc.Dispatcher |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 364 | } |
| 365 | |
| 366 | func (d *testModeDispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) { |
| 367 | obj, _, err := d.realDispatcher.Lookup(suffix) |
| 368 | return obj, d, err |
| 369 | } |
| 370 | |
Todd Wang | 4264e4b | 2015-04-16 22:43:40 -0700 | [diff] [blame] | 371 | func (testModeDispatcher) Authorize(ctx *context.T, call security.Call) error { |
Bogdan Caprita | 2b05032 | 2015-04-17 09:04:03 -0700 | [diff] [blame] | 372 | if call.Suffix() == deviceSuffix && call.Method() == "Delete" { |
Matt Rosencrantz | 9dce9b2 | 2015-03-02 10:48:37 -0800 | [diff] [blame] | 373 | vlog.Infof("testModeDispatcher.Authorize: Allow %q.%s()", call.Suffix(), call.Method()) |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 374 | return nil |
| 375 | } |
Matt Rosencrantz | 9dce9b2 | 2015-03-02 10:48:37 -0800 | [diff] [blame] | 376 | vlog.Infof("testModeDispatcher.Authorize: Reject %q.%s()", call.Suffix(), call.Method()) |
Bogdan Caprita | 040603b | 2015-06-23 18:19:30 -0700 | [diff] [blame^] | 377 | return verror.New(errors.ErrInvalidSuffix, nil) |
Robin Thellend | 9299b78 | 2015-02-03 08:42:46 -0800 | [diff] [blame] | 378 | } |
| 379 | |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 380 | func newAppSpecificAuthorizer(sec security.Authorizer, config *config.State, suffix []string, getter pathperms.PermsGetter) (security.Authorizer, error) { |
Bogdan Caprita | 2b21936 | 2014-12-09 17:03:33 -0800 | [diff] [blame] | 381 | // TODO(rjkroege): This does not support <appname>.Start() to start all |
| 382 | // instances. Correct this. |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 383 | |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 384 | // If we are attempting a method invocation against "apps/", we use the root |
| 385 | // Permissions. |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 386 | if len(suffix) == 0 || len(suffix) == 1 { |
| 387 | return sec, nil |
| 388 | } |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 389 | // Otherwise, we require a per-installation and per-instance Permissions file. |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 390 | if len(suffix) == 2 { |
| 391 | p, err := installationDirCore(suffix, config.Root) |
| 392 | if err != nil { |
Bogdan Caprita | 040603b | 2015-06-23 18:19:30 -0700 | [diff] [blame^] | 393 | return nil, verror.New(errors.ErrOperationFailed, nil, fmt.Sprintf("newAppSpecificAuthorizer failed: %v", err)) |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 394 | } |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 395 | return pathperms.NewHierarchicalAuthorizer(PermsDir(config), path.Join(p, "acls"), getter) |
Asim Shankar | 6888519 | 2014-11-26 12:48:35 -0800 | [diff] [blame] | 396 | } |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 397 | // Use the special debugacls for instance/logs, instance/pprof, instance/stats. |
Robert Kroeger | 16ee22b | 2015-03-12 14:57:09 -0700 | [diff] [blame] | 398 | if len(suffix) > 3 && (suffix[3] == "logs" || suffix[3] == "pprof" || suffix[3] == "stats") { |
| 399 | p, err := instanceDir(config.Root, suffix[0:3]) |
| 400 | if err != nil { |
Bogdan Caprita | 040603b | 2015-06-23 18:19:30 -0700 | [diff] [blame^] | 401 | return nil, verror.New(errors.ErrOperationFailed, nil, fmt.Sprintf("newAppSpecificAuthorizer failed: %v", err)) |
Robert Kroeger | 16ee22b | 2015-03-12 14:57:09 -0700 | [diff] [blame] | 402 | } |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 403 | return pathperms.NewHierarchicalAuthorizer(PermsDir(config), path.Join(p, "debugacls"), getter) |
Robert Kroeger | 16ee22b | 2015-03-12 14:57:09 -0700 | [diff] [blame] | 404 | } |
| 405 | |
Robert Kroeger | 7a8b222 | 2015-03-06 07:58:57 -0800 | [diff] [blame] | 406 | p, err := instanceDir(config.Root, suffix[0:3]) |
| 407 | if err != nil { |
Bogdan Caprita | 040603b | 2015-06-23 18:19:30 -0700 | [diff] [blame^] | 408 | return nil, verror.New(errors.ErrOperationFailed, nil, fmt.Sprintf("newAppSpecificAuthorizer failed: %v", err)) |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 409 | } |
Adam Sadovsky | a4d4a69 | 2015-04-20 11:36:49 -0700 | [diff] [blame] | 410 | return pathperms.NewHierarchicalAuthorizer(PermsDir(config), path.Join(p, "acls"), getter) |
Robert Kroeger | acc778b | 2014-11-03 17:17:21 -0800 | [diff] [blame] | 411 | } |