github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/ifacestate/helpers.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package ifacestate 21 22 import ( 23 "bytes" 24 "encoding/json" 25 "fmt" 26 "os" 27 "sort" 28 "strings" 29 30 "github.com/snapcore/snapd/asserts" 31 "github.com/snapcore/snapd/dirs" 32 "github.com/snapcore/snapd/interfaces" 33 "github.com/snapcore/snapd/interfaces/backends" 34 "github.com/snapcore/snapd/interfaces/builtin" 35 "github.com/snapcore/snapd/interfaces/policy" 36 "github.com/snapcore/snapd/interfaces/utils" 37 "github.com/snapcore/snapd/jsonutil" 38 "github.com/snapcore/snapd/logger" 39 "github.com/snapcore/snapd/overlord/assertstate" 40 "github.com/snapcore/snapd/overlord/snapstate" 41 "github.com/snapcore/snapd/overlord/state" 42 "github.com/snapcore/snapd/snap" 43 "github.com/snapcore/snapd/timings" 44 ) 45 46 func (m *InterfaceManager) selectInterfaceMapper(snaps []*snap.Info) { 47 for _, snapInfo := range snaps { 48 if snapInfo.GetType() == snap.TypeSnapd { 49 mapper = &CoreSnapdSystemMapper{} 50 break 51 } 52 } 53 } 54 55 func (m *InterfaceManager) addInterfaces(extra []interfaces.Interface) error { 56 for _, iface := range builtin.Interfaces() { 57 if err := m.repo.AddInterface(iface); err != nil { 58 return err 59 } 60 } 61 for _, iface := range extra { 62 if err := m.repo.AddInterface(iface); err != nil { 63 return err 64 } 65 } 66 return nil 67 } 68 69 func (m *InterfaceManager) addBackends(extra []interfaces.SecurityBackend) error { 70 for _, backend := range backends.All { 71 if err := backend.Initialize(); err != nil { 72 return err 73 } 74 if err := m.repo.AddBackend(backend); err != nil { 75 return err 76 } 77 } 78 for _, backend := range extra { 79 if err := backend.Initialize(); err != nil { 80 return err 81 } 82 if err := m.repo.AddBackend(backend); err != nil { 83 return err 84 } 85 } 86 return nil 87 } 88 89 func (m *InterfaceManager) addSnaps(snaps []*snap.Info) error { 90 for _, snapInfo := range snaps { 91 if err := addImplicitSlots(m.state, snapInfo); err != nil { 92 return err 93 } 94 if err := m.repo.AddSnap(snapInfo); err != nil { 95 logger.Noticef("cannot add snap %q to interface repository: %s", snapInfo.InstanceName(), err) 96 } 97 } 98 return nil 99 } 100 101 func profilesNeedRegenerationImpl() bool { 102 mismatch, err := interfaces.SystemKeyMismatch() 103 if err != nil { 104 logger.Noticef("error trying to compare the snap system key: %v", err) 105 return true 106 } 107 return mismatch 108 } 109 110 var profilesNeedRegeneration = profilesNeedRegenerationImpl 111 var writeSystemKey = interfaces.WriteSystemKey 112 113 // regenerateAllSecurityProfiles will regenerate all security profiles. 114 func (m *InterfaceManager) regenerateAllSecurityProfiles(tm timings.Measurer) error { 115 // Get all the security backends 116 securityBackends := m.repo.Backends() 117 118 // Get all the snap infos 119 snaps, err := snapsWithSecurityProfiles(m.state) 120 if err != nil { 121 return err 122 } 123 124 // Add implicit slots to all snaps 125 for _, snapInfo := range snaps { 126 if err := addImplicitSlots(m.state, snapInfo); err != nil { 127 return err 128 } 129 } 130 131 // The reason the system key is unlinked is to prevent snapd from believing 132 // that an old system key is valid and represents security setup 133 // established in the system. If snapd is reverted following a failed 134 // startup then system key may match the system key that used to be on disk 135 // but some of the system security may have been changed by the new snapd, 136 // the one that was reverted. Unlinking avoids such possibility, forcing 137 // old snapd to re-establish proper security view. 138 shouldWriteSystemKey := true 139 os.Remove(dirs.SnapSystemKeyFile) 140 141 // For each snap: 142 for _, snapInfo := range snaps { 143 snapName := snapInfo.InstanceName() 144 // Get the state of the snap so we can compute the confinement option 145 var snapst snapstate.SnapState 146 if err := snapstate.Get(m.state, snapName, &snapst); err != nil { 147 logger.Noticef("cannot get state of snap %q: %s", snapName, err) 148 } 149 150 // Compute confinement options 151 opts := confinementOptions(snapst.Flags) 152 153 // For each backend: 154 for _, backend := range securityBackends { 155 if backend.Name() == "" { 156 continue // Test backends have no name, skip them to simplify testing. 157 } 158 // Refresh security of this snap and backend 159 timings.Run(tm, "setup-security-backend", fmt.Sprintf("setup security backend %q for snap %q", backend.Name(), snapInfo.InstanceName()), func(nesttm timings.Measurer) { 160 if err := backend.Setup(snapInfo, opts, m.repo, nesttm); err != nil { 161 // Let's log this but carry on without writing the system key. 162 logger.Noticef("cannot regenerate %s profile for snap %q: %s", 163 backend.Name(), snapName, err) 164 shouldWriteSystemKey = false 165 } 166 }) 167 } 168 } 169 170 if shouldWriteSystemKey { 171 if err := writeSystemKey(); err != nil { 172 logger.Noticef("cannot write system key: %v", err) 173 } 174 } 175 return nil 176 } 177 178 // renameCorePlugConnection renames one connection from "core-support" plug to 179 // slot so that the plug name is "core-support-plug" while the slot is 180 // unchanged. This matches a change introduced in 2.24, where the core snap no 181 // longer has the "core-support" plug as that was clashing with the slot with 182 // the same name. 183 func (m *InterfaceManager) renameCorePlugConnection() error { 184 conns, err := getConns(m.state) 185 if err != nil { 186 return err 187 } 188 const oldPlugName = "core-support" 189 const newPlugName = "core-support-plug" 190 // old connection, note that slotRef is the same in both 191 slotRef := interfaces.SlotRef{Snap: "core", Name: oldPlugName} 192 oldPlugRef := interfaces.PlugRef{Snap: "core", Name: oldPlugName} 193 oldConnRef := interfaces.ConnRef{PlugRef: oldPlugRef, SlotRef: slotRef} 194 oldID := oldConnRef.ID() 195 // if the old connection is saved, replace it with the new connection 196 if cState, ok := conns[oldID]; ok { 197 newPlugRef := interfaces.PlugRef{Snap: "core", Name: newPlugName} 198 newConnRef := interfaces.ConnRef{PlugRef: newPlugRef, SlotRef: slotRef} 199 newID := newConnRef.ID() 200 delete(conns, oldID) 201 conns[newID] = cState 202 setConns(m.state, conns) 203 } 204 return nil 205 } 206 207 // removeStaleConnections removes stale connections left by some older versions of snapd. 208 // Connection is considered stale if the snap on either end of the connection doesn't exist anymore. 209 // XXX: this code should eventually go away. 210 var removeStaleConnections = func(st *state.State) error { 211 conns, err := getConns(st) 212 if err != nil { 213 return err 214 } 215 var staleConns []string 216 for id := range conns { 217 connRef, err := interfaces.ParseConnRef(id) 218 if err != nil { 219 return err 220 } 221 var snapst snapstate.SnapState 222 if err := snapstate.Get(st, connRef.PlugRef.Snap, &snapst); err != nil { 223 if err != state.ErrNoState { 224 return err 225 } 226 staleConns = append(staleConns, id) 227 continue 228 } 229 if err := snapstate.Get(st, connRef.SlotRef.Snap, &snapst); err != nil { 230 if err != state.ErrNoState { 231 return err 232 } 233 staleConns = append(staleConns, id) 234 continue 235 } 236 } 237 if len(staleConns) > 0 { 238 for _, id := range staleConns { 239 delete(conns, id) 240 } 241 setConns(st, conns) 242 logger.Noticef("removed stale connections: %s", strings.Join(staleConns, ", ")) 243 } 244 return nil 245 } 246 247 // reloadConnections reloads connections stored in the state in the repository. 248 // Using non-empty snapName the operation can be scoped to connections 249 // affecting a given snap. 250 // 251 // The return value is the list of affected snap names. 252 func (m *InterfaceManager) reloadConnections(snapName string) ([]string, error) { 253 conns, err := getConns(m.state) 254 if err != nil { 255 return nil, err 256 } 257 258 connStateChanged := false 259 affected := make(map[string]bool) 260 for connId, connState := range conns { 261 // Skip entries that just mark a connection as undesired. Those don't 262 // carry attributes that can go stale. In the same spirit, skip 263 // information about hotplug connections that don't have the associated 264 // hotplug hardware. 265 if connState.Undesired || connState.HotplugGone { 266 continue 267 } 268 connRef, err := interfaces.ParseConnRef(connId) 269 if err != nil { 270 return nil, err 271 } 272 // Apply filtering, this allows us to reload only a subset of 273 // connections (and similarly, refresh the static attributes of only a 274 // subset of connections). 275 if snapName != "" && connRef.PlugRef.Snap != snapName && connRef.SlotRef.Snap != snapName { 276 continue 277 } 278 279 // Some versions of snapd may have left stray connections that don't 280 // have the corresponding plug or slot anymore. Before we choose how to 281 // deal with this data we want to silently ignore that error not to 282 // worry the users. 283 plugInfo := m.repo.Plug(connRef.PlugRef.Snap, connRef.PlugRef.Name) 284 slotInfo := m.repo.Slot(connRef.SlotRef.Snap, connRef.SlotRef.Name) 285 if plugInfo == nil || slotInfo == nil { 286 continue 287 } 288 289 var updateStaticAttrs bool 290 staticPlugAttrs := connState.StaticPlugAttrs 291 staticSlotAttrs := connState.StaticSlotAttrs 292 293 // XXX: Refresh the copy of the static connection attributes for "content" interface as long 294 // as its "content" attribute 295 // This is a partial and temporary solution to https://bugs.launchpad.net/snapd/+bug/1825883 296 if plugInfo.Interface == "content" { 297 var plugContent, slotContent string 298 plugInfo.Attr("content", &plugContent) 299 slotInfo.Attr("content", &slotContent) 300 301 if plugContent != "" && plugContent == slotContent { 302 staticPlugAttrs = utils.NormalizeInterfaceAttributes(plugInfo.Attrs).(map[string]interface{}) 303 staticSlotAttrs = utils.NormalizeInterfaceAttributes(slotInfo.Attrs).(map[string]interface{}) 304 updateStaticAttrs = true 305 } else { 306 logger.Noticef("cannot refresh static attributes of the connection %q", connId) 307 } 308 } 309 310 // Note: reloaded connections are not checked against policy again, and also we don't call BeforeConnect* methods on them. 311 if _, err := m.repo.Connect(connRef, staticPlugAttrs, connState.DynamicPlugAttrs, staticSlotAttrs, connState.DynamicSlotAttrs, nil); err != nil { 312 logger.Noticef("%s", err) 313 } else { 314 // If the connection succeeded update the connection state and keep 315 // track of the snaps that were affected. 316 affected[connRef.PlugRef.Snap] = true 317 affected[connRef.SlotRef.Snap] = true 318 319 if updateStaticAttrs { 320 connState.StaticPlugAttrs = staticPlugAttrs 321 connState.StaticSlotAttrs = staticSlotAttrs 322 connStateChanged = true 323 } 324 } 325 } 326 if connStateChanged { 327 setConns(m.state, conns) 328 } 329 330 result := make([]string, 0, len(affected)) 331 for name := range affected { 332 result = append(result, name) 333 } 334 return result, nil 335 } 336 337 // removeConnections disconnects all connections of the snap in the repo. It should only be used if the snap 338 // has no connections in the state. State must be locked by the caller. 339 func (m *InterfaceManager) removeConnections(snapName string) error { 340 conns, err := getConns(m.state) 341 if err != nil { 342 return err 343 } 344 for id := range conns { 345 connRef, err := interfaces.ParseConnRef(id) 346 if err != nil { 347 return err 348 } 349 if connRef.PlugRef.Snap == snapName || connRef.SlotRef.Snap == snapName { 350 return fmt.Errorf("internal error: cannot remove connections of snap %s from the repository while its connections are present in the state", snapName) 351 } 352 } 353 354 repoConns, err := m.repo.Connections(snapName) 355 if err != nil { 356 return fmt.Errorf("internal error: %v", err) 357 } 358 for _, conn := range repoConns { 359 if err := m.repo.Disconnect(conn.PlugRef.Snap, conn.PlugRef.Name, conn.SlotRef.Snap, conn.SlotRef.Name); err != nil { 360 return fmt.Errorf("internal error: %v", err) 361 } 362 } 363 return nil 364 } 365 366 func (m *InterfaceManager) setupSecurityByBackend(task *state.Task, snaps []*snap.Info, opts []interfaces.ConfinementOptions, tm timings.Measurer) error { 367 st := task.State() 368 369 // Setup all affected snaps, start with the most important security 370 // backend and run it for all snaps. See LP: 1802581 371 for _, backend := range m.repo.Backends() { 372 for i, snapInfo := range snaps { 373 st.Unlock() 374 var err error 375 timings.Run(tm, "setup-security-backend", fmt.Sprintf("setup security backend %q for snap %q", backend.Name(), snapInfo.InstanceName()), func(nesttm timings.Measurer) { 376 err = backend.Setup(snapInfo, opts[i], m.repo, nesttm) 377 }) 378 st.Lock() 379 if err != nil { 380 task.Errorf("cannot setup %s for snap %q: %s", backend.Name(), snapInfo.InstanceName(), err) 381 return err 382 } 383 } 384 } 385 386 return nil 387 } 388 389 func (m *InterfaceManager) setupSnapSecurity(task *state.Task, snapInfo *snap.Info, opts interfaces.ConfinementOptions, tm timings.Measurer) error { 390 return m.setupSecurityByBackend(task, []*snap.Info{snapInfo}, []interfaces.ConfinementOptions{opts}, tm) 391 } 392 393 func (m *InterfaceManager) removeSnapSecurity(task *state.Task, instanceName string) error { 394 st := task.State() 395 for _, backend := range m.repo.Backends() { 396 st.Unlock() 397 err := backend.Remove(instanceName) 398 st.Lock() 399 if err != nil { 400 task.Errorf("cannot setup %s for snap %q: %s", backend.Name(), instanceName, err) 401 return err 402 } 403 } 404 return nil 405 } 406 407 func addHotplugSlot(st *state.State, repo *interfaces.Repository, stateSlots map[string]*HotplugSlotInfo, iface interfaces.Interface, slot *snap.SlotInfo) error { 408 if slot.HotplugKey == "" { 409 return fmt.Errorf("internal error: cannot store slot %q, not a hotplug slot", slot.Name) 410 } 411 if iface, ok := iface.(interfaces.SlotSanitizer); ok { 412 if err := iface.BeforePrepareSlot(slot); err != nil { 413 return fmt.Errorf("cannot sanitize hotplug slot %q for interface %s: %s", slot.Name, slot.Interface, err) 414 } 415 } 416 417 if err := repo.AddSlot(slot); err != nil { 418 return fmt.Errorf("cannot add hotplug slot %q for interface %s: %s", slot.Name, slot.Interface, err) 419 } 420 421 stateSlots[slot.Name] = &HotplugSlotInfo{ 422 Name: slot.Name, 423 Interface: slot.Interface, 424 StaticAttrs: slot.Attrs, 425 HotplugKey: slot.HotplugKey, 426 HotplugGone: false, 427 } 428 setHotplugSlots(st, stateSlots) 429 logger.Debugf("added hotplug slot %s:%s of interface %s, hotplug key %q", slot.Snap.InstanceName(), slot.Name, slot.Interface, slot.HotplugKey) 430 return nil 431 } 432 433 type connState struct { 434 Auto bool `json:"auto,omitempty"` 435 ByGadget bool `json:"by-gadget,omitempty"` 436 Interface string `json:"interface,omitempty"` 437 // Undesired tracks connections that were manually disconnected after being auto-connected, 438 // so that they are not automatically reconnected again in the future. 439 Undesired bool `json:"undesired,omitempty"` 440 StaticPlugAttrs map[string]interface{} `json:"plug-static,omitempty"` 441 DynamicPlugAttrs map[string]interface{} `json:"plug-dynamic,omitempty"` 442 StaticSlotAttrs map[string]interface{} `json:"slot-static,omitempty"` 443 DynamicSlotAttrs map[string]interface{} `json:"slot-dynamic,omitempty"` 444 // Hotplug-related attributes: HotplugGone indicates a connection that 445 // disappeared because the device was removed, but may potentially be 446 // restored in the future if we see the device again. HotplugKey is the 447 // key of the associated device; it's empty for connections of regular 448 // slots. 449 HotplugGone bool `json:"hotplug-gone,omitempty"` 450 HotplugKey snap.HotplugKey `json:"hotplug-key,omitempty"` 451 } 452 453 type autoConnectChecker struct { 454 st *state.State 455 deviceCtx snapstate.DeviceContext 456 cache map[string]*asserts.SnapDeclaration 457 baseDecl *asserts.BaseDeclaration 458 } 459 460 func newAutoConnectChecker(s *state.State, deviceCtx snapstate.DeviceContext) (*autoConnectChecker, error) { 461 baseDecl, err := assertstate.BaseDeclaration(s) 462 if err != nil { 463 return nil, fmt.Errorf("internal error: cannot find base declaration: %v", err) 464 } 465 return &autoConnectChecker{ 466 st: s, 467 deviceCtx: deviceCtx, 468 cache: make(map[string]*asserts.SnapDeclaration), 469 baseDecl: baseDecl, 470 }, nil 471 } 472 473 func (c *autoConnectChecker) snapDeclaration(snapID string) (*asserts.SnapDeclaration, error) { 474 snapDecl := c.cache[snapID] 475 if snapDecl != nil { 476 return snapDecl, nil 477 } 478 snapDecl, err := assertstate.SnapDeclaration(c.st, snapID) 479 if err != nil { 480 return nil, err 481 } 482 c.cache[snapID] = snapDecl 483 return snapDecl, nil 484 } 485 486 func (c *autoConnectChecker) check(plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) (bool, error) { 487 modelAs := c.deviceCtx.Model() 488 489 var storeAs *asserts.Store 490 if modelAs.Store() != "" { 491 var err error 492 storeAs, err = assertstate.Store(c.st, modelAs.Store()) 493 if err != nil && !asserts.IsNotFound(err) { 494 return false, err 495 } 496 } 497 498 var plugDecl *asserts.SnapDeclaration 499 if plug.Snap().SnapID != "" { 500 var err error 501 plugDecl, err = c.snapDeclaration(plug.Snap().SnapID) 502 if err != nil { 503 logger.Noticef("error: cannot find snap declaration for %q: %v", plug.Snap().InstanceName(), err) 504 return false, nil 505 } 506 } 507 508 var slotDecl *asserts.SnapDeclaration 509 if slot.Snap().SnapID != "" { 510 var err error 511 slotDecl, err = c.snapDeclaration(slot.Snap().SnapID) 512 if err != nil { 513 logger.Noticef("error: cannot find snap declaration for %q: %v", slot.Snap().InstanceName(), err) 514 return false, nil 515 } 516 } 517 518 // check the connection against the declarations' rules 519 ic := policy.ConnectCandidate{ 520 Plug: plug, 521 PlugSnapDeclaration: plugDecl, 522 Slot: slot, 523 SlotSnapDeclaration: slotDecl, 524 BaseDeclaration: c.baseDecl, 525 Model: modelAs, 526 Store: storeAs, 527 } 528 529 return ic.CheckAutoConnect() == nil, nil 530 } 531 532 type connectChecker struct { 533 st *state.State 534 deviceCtx snapstate.DeviceContext 535 baseDecl *asserts.BaseDeclaration 536 } 537 538 func newConnectChecker(s *state.State, deviceCtx snapstate.DeviceContext) (*connectChecker, error) { 539 baseDecl, err := assertstate.BaseDeclaration(s) 540 if err != nil { 541 return nil, fmt.Errorf("internal error: cannot find base declaration: %v", err) 542 } 543 return &connectChecker{ 544 st: s, 545 deviceCtx: deviceCtx, 546 baseDecl: baseDecl, 547 }, nil 548 } 549 550 func (c *connectChecker) check(plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) (bool, error) { 551 modelAs := c.deviceCtx.Model() 552 553 var storeAs *asserts.Store 554 if modelAs.Store() != "" { 555 var err error 556 storeAs, err = assertstate.Store(c.st, modelAs.Store()) 557 if err != nil && !asserts.IsNotFound(err) { 558 return false, err 559 } 560 } 561 562 var plugDecl *asserts.SnapDeclaration 563 if plug.Snap().SnapID != "" { 564 var err error 565 plugDecl, err = assertstate.SnapDeclaration(c.st, plug.Snap().SnapID) 566 if err != nil { 567 return false, fmt.Errorf("cannot find snap declaration for %q: %v", plug.Snap().InstanceName(), err) 568 } 569 } 570 571 var slotDecl *asserts.SnapDeclaration 572 if slot.Snap().SnapID != "" { 573 var err error 574 slotDecl, err = assertstate.SnapDeclaration(c.st, slot.Snap().SnapID) 575 if err != nil { 576 return false, fmt.Errorf("cannot find snap declaration for %q: %v", slot.Snap().InstanceName(), err) 577 } 578 } 579 580 // check the connection against the declarations' rules 581 ic := policy.ConnectCandidate{ 582 Plug: plug, 583 PlugSnapDeclaration: plugDecl, 584 Slot: slot, 585 SlotSnapDeclaration: slotDecl, 586 BaseDeclaration: c.baseDecl, 587 Model: modelAs, 588 Store: storeAs, 589 } 590 591 // if either of plug or slot snaps don't have a declaration it 592 // means they were installed with "dangerous", so the security 593 // check should be skipped at this point. 594 if plugDecl != nil && slotDecl != nil { 595 if err := ic.Check(); err != nil { 596 return false, err 597 } 598 } 599 return true, nil 600 } 601 602 func getPlugAndSlotRefs(task *state.Task) (interfaces.PlugRef, interfaces.SlotRef, error) { 603 var plugRef interfaces.PlugRef 604 var slotRef interfaces.SlotRef 605 if err := task.Get("plug", &plugRef); err != nil { 606 return plugRef, slotRef, err 607 } 608 if err := task.Get("slot", &slotRef); err != nil { 609 return plugRef, slotRef, err 610 } 611 return plugRef, slotRef, nil 612 } 613 614 // getConns returns information about connections from the state. 615 // 616 // Connections are transparently re-mapped according to remapIncomingConnRef 617 func getConns(st *state.State) (conns map[string]*connState, err error) { 618 var raw *json.RawMessage 619 err = st.Get("conns", &raw) 620 if err != nil && err != state.ErrNoState { 621 return nil, fmt.Errorf("cannot obtain raw data about existing connections: %s", err) 622 } 623 if raw != nil { 624 err = jsonutil.DecodeWithNumber(bytes.NewReader(*raw), &conns) 625 if err != nil { 626 return nil, fmt.Errorf("cannot decode data about existing connections: %s", err) 627 } 628 } 629 if conns == nil { 630 conns = make(map[string]*connState) 631 } 632 remapped := make(map[string]*connState, len(conns)) 633 for id, cstate := range conns { 634 cref, err := interfaces.ParseConnRef(id) 635 if err != nil { 636 return nil, err 637 } 638 cref.PlugRef.Snap = RemapSnapFromState(cref.PlugRef.Snap) 639 cref.SlotRef.Snap = RemapSnapFromState(cref.SlotRef.Snap) 640 cstate.StaticSlotAttrs = utils.NormalizeInterfaceAttributes(cstate.StaticSlotAttrs).(map[string]interface{}) 641 cstate.DynamicSlotAttrs = utils.NormalizeInterfaceAttributes(cstate.DynamicSlotAttrs).(map[string]interface{}) 642 cstate.StaticPlugAttrs = utils.NormalizeInterfaceAttributes(cstate.StaticPlugAttrs).(map[string]interface{}) 643 cstate.DynamicPlugAttrs = utils.NormalizeInterfaceAttributes(cstate.DynamicPlugAttrs).(map[string]interface{}) 644 remapped[cref.ID()] = cstate 645 } 646 return remapped, nil 647 } 648 649 // setConns sets information about connections in the state. 650 // 651 // Connections are transparently re-mapped according to remapOutgoingConnRef 652 func setConns(st *state.State, conns map[string]*connState) { 653 remapped := make(map[string]*connState, len(conns)) 654 for id, cstate := range conns { 655 cref, err := interfaces.ParseConnRef(id) 656 if err != nil { 657 // We cannot fail here 658 panic(err) 659 } 660 cref.PlugRef.Snap = RemapSnapToState(cref.PlugRef.Snap) 661 cref.SlotRef.Snap = RemapSnapToState(cref.SlotRef.Snap) 662 remapped[cref.ID()] = cstate 663 } 664 st.Set("conns", remapped) 665 } 666 667 // snapsWithSecurityProfiles returns all snaps that have active 668 // security profiles: these are either snaps that are active, or about 669 // to be active (pending link-snap) with a done setup-profiles 670 func snapsWithSecurityProfiles(st *state.State) ([]*snap.Info, error) { 671 infos, err := snapstate.ActiveInfos(st) 672 if err != nil { 673 return nil, err 674 } 675 seen := make(map[string]bool, len(infos)) 676 for _, info := range infos { 677 seen[info.InstanceName()] = true 678 } 679 for _, t := range st.Tasks() { 680 if t.Kind() != "link-snap" || t.Status().Ready() { 681 continue 682 } 683 snapsup, err := snapstate.TaskSnapSetup(t) 684 if err != nil { 685 return nil, err 686 } 687 instanceName := snapsup.InstanceName() 688 if seen[instanceName] { 689 continue 690 } 691 692 doneProfiles := false 693 for _, t1 := range t.WaitTasks() { 694 if t1.Kind() == "setup-profiles" && t1.Status() == state.DoneStatus { 695 snapsup1, err := snapstate.TaskSnapSetup(t) 696 if err != nil { 697 return nil, err 698 } 699 if snapsup1.InstanceName() == instanceName { 700 doneProfiles = true 701 break 702 } 703 } 704 } 705 if !doneProfiles { 706 continue 707 } 708 709 seen[instanceName] = true 710 snapInfo, err := snap.ReadInfo(instanceName, snapsup.SideInfo) 711 if err != nil { 712 logger.Noticef("cannot retrieve info for snap %q: %s", instanceName, err) 713 continue 714 } 715 infos = append(infos, snapInfo) 716 } 717 718 return infos, nil 719 } 720 721 func resolveSnapIDToName(st *state.State, snapID string) (name string, err error) { 722 if snapID == "system" { 723 // Resolve the system nickname to a concrete snap. 724 return mapper.RemapSnapFromRequest(snapID), nil 725 } 726 decl, err := assertstate.SnapDeclaration(st, snapID) 727 if asserts.IsNotFound(err) { 728 return "", nil 729 } 730 if err != nil { 731 return "", err 732 } 733 return decl.SnapName(), nil 734 } 735 736 // SnapMapper offers APIs for re-mapping snap names in interfaces and the 737 // configuration system. The mapper is designed to apply transformations around 738 // the edges of snapd (state interactions and API interactions) to offer one 739 // view on the inside of snapd and another view on the outside. 740 type SnapMapper interface { 741 // re-map functions for loading and saving objects in the state. 742 RemapSnapFromState(snapName string) string 743 RemapSnapToState(snapName string) string 744 // RamapSnapFromRequest can replace snap names in API requests. 745 // There is no corresponding mapping function for API responses anymore. 746 // The API responses always reflect the real system state. 747 RemapSnapFromRequest(snapName string) string 748 // Returns actual name of the system snap. 749 SystemSnapName() string 750 } 751 752 // IdentityMapper implements SnapMapper and performs no transformations at all. 753 type IdentityMapper struct{} 754 755 // RemapSnapFromState doesn't change the snap name in any way. 756 func (m *IdentityMapper) RemapSnapFromState(snapName string) string { 757 return snapName 758 } 759 760 // RemapSnapToState doesn't change the snap name in any way. 761 func (m *IdentityMapper) RemapSnapToState(snapName string) string { 762 return snapName 763 } 764 765 // RemapSnapFromRequest doesn't change the snap name in any way. 766 func (m *IdentityMapper) RemapSnapFromRequest(snapName string) string { 767 return snapName 768 } 769 770 // CoreCoreSystemMapper implements SnapMapper and makes implicit slots 771 // appear to be on "core" in the state and in memory but as "system" in the API. 772 // 773 // NOTE: This mapper can be used to prepare, as an intermediate step, for the 774 // transition to "snapd" mapper. Using it the state and API layer will look 775 // exactly the same as with the "snapd" mapper. This can be used to make any 776 // necessary adjustments the test suite. 777 type CoreCoreSystemMapper struct { 778 IdentityMapper // Embedding the identity mapper allows us to cut on boilerplate. 779 } 780 781 // RemapSnapFromRequest renames the "system" snap to the "core" snap. 782 // 783 // This allows us to accept connection and disconnection requests that 784 // explicitly refer to "core" or using the "system" nickname. 785 func (m *CoreCoreSystemMapper) RemapSnapFromRequest(snapName string) string { 786 if snapName == "system" { 787 return m.SystemSnapName() 788 } 789 return snapName 790 } 791 792 // SystemSnapName returns actual name of the system snap. 793 func (m *CoreCoreSystemMapper) SystemSnapName() string { 794 return "core" 795 } 796 797 // CoreSnapdSystemMapper implements SnapMapper and makes implicit slots 798 // appear to be on "core" in the state and on "system" in the API while they 799 // are on "snapd" in memory. 800 type CoreSnapdSystemMapper struct { 801 IdentityMapper // Embedding the identity mapper allows us to cut on boilerplate. 802 } 803 804 // RemapSnapFromState renames the "core" snap to the "snapd" snap. 805 // 806 // This allows modern snapd to load an old state that remembers connections 807 // between slots on the "core" snap and other snaps. In memory we are actually 808 // using "snapd" snap for hosting those slots and this lets us stay compatible. 809 func (m *CoreSnapdSystemMapper) RemapSnapFromState(snapName string) string { 810 if snapName == "core" { 811 return m.SystemSnapName() 812 } 813 return snapName 814 } 815 816 // RemapSnapToState renames the "snapd" snap to the "core" snap. 817 // 818 // This allows the state to stay backwards compatible as all the connections 819 // seem to refer to the "core" snap, as in pre core{16,18} days where there was 820 // only one core snap. 821 func (m *CoreSnapdSystemMapper) RemapSnapToState(snapName string) string { 822 if snapName == m.SystemSnapName() { 823 return "core" 824 } 825 return snapName 826 } 827 828 // RemapSnapFromRequest renames the "core" or "system" snaps to the "snapd" snap. 829 // 830 // This allows us to accept connection and disconnection requests that 831 // explicitly refer to "core" or "system" even though we really want them to 832 // refer to "snapd". Note that this is not fully symmetric with 833 // RemapSnapToResponse as we explicitly always talk about "system" snap, 834 // even if the request used "core". 835 func (m *CoreSnapdSystemMapper) RemapSnapFromRequest(snapName string) string { 836 if snapName == "system" || snapName == "core" { 837 return m.SystemSnapName() 838 } 839 return snapName 840 } 841 842 // SystemSnapName returns actual name of the system snap. 843 func (m *CoreSnapdSystemMapper) SystemSnapName() string { 844 return "snapd" 845 } 846 847 // mapper contains the currently active snap mapper. 848 var mapper SnapMapper = &CoreCoreSystemMapper{} 849 850 // MockSnapMapper mocks the currently used snap mapper. 851 func MockSnapMapper(new SnapMapper) (restore func()) { 852 old := mapper 853 mapper = new 854 return func() { mapper = old } 855 } 856 857 // RemapSnapFromState renames a snap when loaded from state according to the current mapper. 858 func RemapSnapFromState(snapName string) string { 859 return mapper.RemapSnapFromState(snapName) 860 } 861 862 // RemapSnapToState renames a snap when saving to state according to the current mapper. 863 func RemapSnapToState(snapName string) string { 864 return mapper.RemapSnapToState(snapName) 865 } 866 867 // RemapSnapFromRequest renames a snap as received from an API request according to the current mapper. 868 func RemapSnapFromRequest(snapName string) string { 869 return mapper.RemapSnapFromRequest(snapName) 870 } 871 872 // SystemSnapName returns actual name of the system snap. 873 func SystemSnapName() string { 874 return mapper.SystemSnapName() 875 } 876 877 // systemSnapInfo returns current info for system snap. 878 func systemSnapInfo(st *state.State) (*snap.Info, error) { 879 return snapstate.CurrentInfo(st, SystemSnapName()) 880 } 881 882 func connectDisconnectAffectedSnaps(t *state.Task) ([]string, error) { 883 plugRef, slotRef, err := getPlugAndSlotRefs(t) 884 if err != nil { 885 return nil, fmt.Errorf("internal error: cannot obtain plug/slot data from task: %s", t.Summary()) 886 } 887 return []string{plugRef.Snap, slotRef.Snap}, nil 888 } 889 890 func checkSystemSnapIsPresent(st *state.State) bool { 891 st.Lock() 892 defer st.Unlock() 893 _, err := systemSnapInfo(st) 894 return err == nil 895 } 896 897 func setHotplugAttrs(task *state.Task, ifaceName string, hotplugKey snap.HotplugKey) { 898 task.Set("interface", ifaceName) 899 task.Set("hotplug-key", hotplugKey) 900 } 901 902 func getHotplugAttrs(task *state.Task) (ifaceName string, hotplugKey snap.HotplugKey, err error) { 903 if err = task.Get("interface", &ifaceName); err != nil { 904 return "", "", fmt.Errorf("internal error: cannot get interface name from hotplug task: %s", err) 905 } 906 if err = task.Get("hotplug-key", &hotplugKey); err != nil { 907 return "", "", fmt.Errorf("internal error: cannot get hotplug key from hotplug task: %s", err) 908 } 909 return ifaceName, hotplugKey, err 910 } 911 912 func allocHotplugSeq(st *state.State) (int, error) { 913 var seq int 914 if err := st.Get("hotplug-seq", &seq); err != nil && err != state.ErrNoState { 915 return 0, fmt.Errorf("internal error: cannot allocate hotplug sequence number: %s", err) 916 } 917 seq++ 918 st.Set("hotplug-seq", seq) 919 return seq, nil 920 } 921 922 func isHotplugChange(chg *state.Change) bool { 923 return strings.HasPrefix(chg.Kind(), "hotplug-") 924 } 925 926 func getHotplugChangeAttrs(chg *state.Change) (seq int, hotplugKey snap.HotplugKey, err error) { 927 if err = chg.Get("hotplug-key", &hotplugKey); err != nil { 928 return 0, "", fmt.Errorf("internal error: hotplug-key not set on change %q", chg.Kind()) 929 } 930 if err = chg.Get("hotplug-seq", &seq); err != nil { 931 return 0, "", fmt.Errorf("internal error: hotplug-seq not set on change %q", chg.Kind()) 932 } 933 return seq, hotplugKey, nil 934 } 935 936 func setHotplugChangeAttrs(chg *state.Change, seq int, hotplugKey snap.HotplugKey) { 937 chg.Set("hotplug-seq", seq) 938 chg.Set("hotplug-key", hotplugKey) 939 } 940 941 // addHotplugSeqWaitTask sets mandatory hotplug attributes on the hotplug change, adds "hotplug-seq-wait" task 942 // and makes all existing tasks of the change wait for it. 943 func addHotplugSeqWaitTask(hotplugChange *state.Change, hotplugKey snap.HotplugKey, hotplugSeq int) { 944 st := hotplugChange.State() 945 setHotplugChangeAttrs(hotplugChange, hotplugSeq, hotplugKey) 946 seqControl := st.NewTask("hotplug-seq-wait", fmt.Sprintf("Serialize hotplug change for hotplug key %q", hotplugKey)) 947 tss := state.NewTaskSet(hotplugChange.Tasks()...) 948 tss.WaitFor(seqControl) 949 hotplugChange.AddTask(seqControl) 950 } 951 952 type HotplugSlotInfo struct { 953 Name string `json:"name"` 954 Interface string `json:"interface"` 955 StaticAttrs map[string]interface{} `json:"static-attrs,omitempty"` 956 HotplugKey snap.HotplugKey `json:"hotplug-key"` 957 958 // device was unplugged but has connections, so slot is remembered 959 HotplugGone bool `json:"hotplug-gone"` 960 } 961 962 func getHotplugSlots(st *state.State) (map[string]*HotplugSlotInfo, error) { 963 var slots map[string]*HotplugSlotInfo 964 err := st.Get("hotplug-slots", &slots) 965 if err != nil { 966 if err != state.ErrNoState { 967 return nil, err 968 } 969 slots = make(map[string]*HotplugSlotInfo) 970 } 971 return slots, nil 972 } 973 974 func setHotplugSlots(st *state.State, slots map[string]*HotplugSlotInfo) { 975 st.Set("hotplug-slots", slots) 976 } 977 978 func findHotplugSlot(stateSlots map[string]*HotplugSlotInfo, ifaceName string, hotplugKey snap.HotplugKey) *HotplugSlotInfo { 979 for _, slot := range stateSlots { 980 if slot.HotplugKey == hotplugKey && slot.Interface == ifaceName { 981 return slot 982 } 983 } 984 return nil 985 } 986 987 func findConnsForHotplugKey(conns map[string]*connState, ifaceName string, hotplugKey snap.HotplugKey) []string { 988 var connsForDevice []string 989 for id, connSt := range conns { 990 if connSt.Interface != ifaceName || connSt.HotplugKey != hotplugKey { 991 continue 992 } 993 connsForDevice = append(connsForDevice, id) 994 } 995 sort.Strings(connsForDevice) 996 return connsForDevice 997 }