github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/overlord/snapstate/snapstate.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2018 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 snapstate implements the manager and state aspects responsible for the installation and removal of snaps. 21 package snapstate 22 23 import ( 24 "context" 25 "encoding/json" 26 "errors" 27 "fmt" 28 "os" 29 "sort" 30 "strings" 31 "time" 32 33 "github.com/snapcore/snapd/asserts" 34 "github.com/snapcore/snapd/boot" 35 "github.com/snapcore/snapd/dirs" 36 "github.com/snapcore/snapd/features" 37 "github.com/snapcore/snapd/gadget" 38 "github.com/snapcore/snapd/i18n" 39 "github.com/snapcore/snapd/interfaces" 40 "github.com/snapcore/snapd/logger" 41 "github.com/snapcore/snapd/osutil" 42 "github.com/snapcore/snapd/overlord/auth" 43 "github.com/snapcore/snapd/overlord/configstate/config" 44 "github.com/snapcore/snapd/overlord/ifacestate/ifacerepo" 45 "github.com/snapcore/snapd/overlord/snapstate/backend" 46 "github.com/snapcore/snapd/overlord/state" 47 "github.com/snapcore/snapd/release" 48 "github.com/snapcore/snapd/snap" 49 "github.com/snapcore/snapd/snap/channel" 50 "github.com/snapcore/snapd/store" 51 "github.com/snapcore/snapd/strutil" 52 ) 53 54 // control flags for doInstall 55 const ( 56 skipConfigure = 1 << iota 57 ) 58 59 // control flags for "Configure()" 60 const ( 61 IgnoreHookError = 1 << iota 62 TrackHookError 63 UseConfigDefaults 64 ) 65 66 const ( 67 DownloadAndChecksDoneEdge = state.TaskSetEdge("download-and-checks-done") 68 BeginEdge = state.TaskSetEdge("begin") 69 BeforeHooksEdge = state.TaskSetEdge("before-hooks") 70 HooksEdge = state.TaskSetEdge("hooks") 71 ) 72 73 var ErrNothingToDo = errors.New("nothing to do") 74 75 var osutilCheckFreeSpace = osutil.CheckFreeSpace 76 77 // InsufficientSpaceError represents an error where there is not enough disk 78 // space to perform an operation. 79 type InsufficientSpaceError struct { 80 // Path is the filesystem path checked for available disk space 81 Path string 82 // Snaps affected by the failing operation 83 Snaps []string 84 // Kind of the change that failed 85 ChangeKind string 86 // Message is optional, otherwise one is composed from the other information 87 Message string 88 } 89 90 func (e *InsufficientSpaceError) Error() string { 91 if e.Message != "" { 92 return e.Message 93 } 94 if len(e.Snaps) > 0 { 95 snaps := strings.Join(e.Snaps, ", ") 96 return fmt.Sprintf("insufficient space in %q to perform %q change for the following snaps: %s", e.Path, e.ChangeKind, snaps) 97 } 98 return fmt.Sprintf("insufficient space in %q", e.Path) 99 } 100 101 // safetyMarginDiskSpace returns size plus a safety margin (5Mb) 102 func safetyMarginDiskSpace(size uint64) uint64 { 103 return size + 5*1024*1024 104 } 105 106 func isParallelInstallable(snapsup *SnapSetup) error { 107 if snapsup.InstanceKey == "" { 108 return nil 109 } 110 if snapsup.Type == snap.TypeApp { 111 return nil 112 } 113 return fmt.Errorf("cannot install snap of type %v as %q", snapsup.Type, snapsup.InstanceName()) 114 } 115 116 func optedIntoSnapdSnap(st *state.State) (bool, error) { 117 tr := config.NewTransaction(st) 118 experimentalAllowSnapd, err := features.Flag(tr, features.SnapdSnap) 119 if err != nil && !config.IsNoOption(err) { 120 return false, err 121 } 122 return experimentalAllowSnapd, nil 123 } 124 125 func doInstall(st *state.State, snapst *SnapState, snapsup *SnapSetup, flags int, fromChange string, inUseCheck func(snap.Type) (boot.InUseFunc, error)) (*state.TaskSet, error) { 126 // NB: we should strive not to need or propagate deviceCtx 127 // here, the resulting effects/changes were not pleasant at 128 // one point 129 tr := config.NewTransaction(st) 130 experimentalRefreshAppAwareness, err := features.Flag(tr, features.RefreshAppAwareness) 131 if err != nil && !config.IsNoOption(err) { 132 return nil, err 133 } 134 135 if snapsup.InstanceName() == "system" { 136 return nil, fmt.Errorf("cannot install reserved snap name 'system'") 137 } 138 if snapst.IsInstalled() && !snapst.Active { 139 return nil, fmt.Errorf("cannot update disabled snap %q", snapsup.InstanceName()) 140 } 141 if snapst.IsInstalled() && !snapsup.Flags.Revert { 142 if inUseCheck == nil { 143 return nil, fmt.Errorf("internal error: doInstall: inUseCheck not provided for refresh") 144 } 145 } 146 147 if snapsup.Flags.Classic { 148 if !release.OnClassic { 149 return nil, fmt.Errorf("classic confinement is only supported on classic systems") 150 } else if !dirs.SupportsClassicConfinement() { 151 return nil, fmt.Errorf(i18n.G("classic confinement requires snaps under /snap or symlink from /snap to %s"), dirs.SnapMountDir) 152 } 153 } 154 if !snapst.IsInstalled() { // install? 155 // check that the snap command namespace doesn't conflict with an enabled alias 156 if err := checkSnapAliasConflict(st, snapsup.InstanceName()); err != nil { 157 return nil, err 158 } 159 } 160 161 if err := isParallelInstallable(snapsup); err != nil { 162 return nil, err 163 } 164 165 if err := checkChangeConflictIgnoringOneChange(st, snapsup.InstanceName(), snapst, fromChange); err != nil { 166 return nil, err 167 } 168 169 if snapst.IsInstalled() { 170 // consider also the current revision to set plugs-only hint 171 info, err := snapst.CurrentInfo() 172 if err != nil { 173 return nil, err 174 } 175 snapsup.PlugsOnly = snapsup.PlugsOnly && (len(info.Slots) == 0) 176 177 if experimentalRefreshAppAwareness && !snapsup.Flags.IgnoreRunning { 178 // Note that because we are modifying the snap state inside 179 // softCheckNothingRunningForRefresh, this block must be located 180 // after the conflict check done above. 181 if err := softCheckNothingRunningForRefresh(st, snapst, info); err != nil { 182 return nil, err 183 } 184 } 185 } 186 187 ts := state.NewTaskSet() 188 189 targetRevision := snapsup.Revision() 190 revisionStr := "" 191 if snapsup.SideInfo != nil { 192 revisionStr = fmt.Sprintf(" (%s)", targetRevision) 193 } 194 195 // check if we already have the revision locally (alters tasks) 196 revisionIsLocal := snapst.LastIndex(targetRevision) >= 0 197 198 prereq := st.NewTask("prerequisites", fmt.Sprintf(i18n.G("Ensure prerequisites for %q are available"), snapsup.InstanceName())) 199 prereq.Set("snap-setup", snapsup) 200 201 var prepare, prev *state.Task 202 fromStore := false 203 // if we have a local revision here we go back to that 204 if snapsup.SnapPath != "" || revisionIsLocal { 205 prepare = st.NewTask("prepare-snap", fmt.Sprintf(i18n.G("Prepare snap %q%s"), snapsup.SnapPath, revisionStr)) 206 } else { 207 fromStore = true 208 prepare = st.NewTask("download-snap", fmt.Sprintf(i18n.G("Download snap %q%s from channel %q"), snapsup.InstanceName(), revisionStr, snapsup.Channel)) 209 } 210 prepare.Set("snap-setup", snapsup) 211 prepare.WaitFor(prereq) 212 213 tasks := []*state.Task{prereq, prepare} 214 addTask := func(t *state.Task) { 215 t.Set("snap-setup-task", prepare.ID()) 216 t.WaitFor(prev) 217 tasks = append(tasks, t) 218 } 219 prev = prepare 220 221 var checkAsserts *state.Task 222 if fromStore { 223 // fetch and check assertions 224 checkAsserts = st.NewTask("validate-snap", fmt.Sprintf(i18n.G("Fetch and check assertions for snap %q%s"), snapsup.InstanceName(), revisionStr)) 225 addTask(checkAsserts) 226 prev = checkAsserts 227 } 228 229 // mount 230 if !revisionIsLocal { 231 mount := st.NewTask("mount-snap", fmt.Sprintf(i18n.G("Mount snap %q%s"), snapsup.InstanceName(), revisionStr)) 232 addTask(mount) 233 prev = mount 234 } 235 236 // run refresh hooks when updating existing snap, otherwise run install hook further down. 237 runRefreshHooks := (snapst.IsInstalled() && !snapsup.Flags.Revert) 238 if runRefreshHooks { 239 preRefreshHook := SetupPreRefreshHook(st, snapsup.InstanceName()) 240 addTask(preRefreshHook) 241 prev = preRefreshHook 242 } 243 244 if snapst.IsInstalled() { 245 // unlink-current-snap (will stop services for copy-data) 246 stop := st.NewTask("stop-snap-services", fmt.Sprintf(i18n.G("Stop snap %q services"), snapsup.InstanceName())) 247 stop.Set("stop-reason", snap.StopReasonRefresh) 248 addTask(stop) 249 prev = stop 250 251 removeAliases := st.NewTask("remove-aliases", fmt.Sprintf(i18n.G("Remove aliases for snap %q"), snapsup.InstanceName())) 252 addTask(removeAliases) 253 prev = removeAliases 254 255 unlink := st.NewTask("unlink-current-snap", fmt.Sprintf(i18n.G("Make current revision for snap %q unavailable"), snapsup.InstanceName())) 256 addTask(unlink) 257 prev = unlink 258 } 259 260 if !release.OnClassic && snapsup.Type == snap.TypeGadget { 261 // XXX: gadget update currently for core systems only 262 gadgetUpdate := st.NewTask("update-gadget-assets", fmt.Sprintf(i18n.G("Update assets from gadget %q%s"), snapsup.InstanceName(), revisionStr)) 263 addTask(gadgetUpdate) 264 prev = gadgetUpdate 265 } 266 267 // copy-data (needs stopped services by unlink) 268 if !snapsup.Flags.Revert { 269 copyData := st.NewTask("copy-snap-data", fmt.Sprintf(i18n.G("Copy snap %q data"), snapsup.InstanceName())) 270 addTask(copyData) 271 prev = copyData 272 } 273 274 // security 275 setupSecurity := st.NewTask("setup-profiles", fmt.Sprintf(i18n.G("Setup snap %q%s security profiles"), snapsup.InstanceName(), revisionStr)) 276 addTask(setupSecurity) 277 prev = setupSecurity 278 279 // finalize (wrappers+current symlink) 280 linkSnap := st.NewTask("link-snap", fmt.Sprintf(i18n.G("Make snap %q%s available to the system"), snapsup.InstanceName(), revisionStr)) 281 addTask(linkSnap) 282 prev = linkSnap 283 284 // auto-connections 285 autoConnect := st.NewTask("auto-connect", fmt.Sprintf(i18n.G("Automatically connect eligible plugs and slots of snap %q"), snapsup.InstanceName())) 286 addTask(autoConnect) 287 prev = autoConnect 288 289 // setup aliases 290 setAutoAliases := st.NewTask("set-auto-aliases", fmt.Sprintf(i18n.G("Set automatic aliases for snap %q"), snapsup.InstanceName())) 291 addTask(setAutoAliases) 292 prev = setAutoAliases 293 294 setupAliases := st.NewTask("setup-aliases", fmt.Sprintf(i18n.G("Setup snap %q aliases"), snapsup.InstanceName())) 295 addTask(setupAliases) 296 prev = setupAliases 297 298 if runRefreshHooks { 299 postRefreshHook := SetupPostRefreshHook(st, snapsup.InstanceName()) 300 addTask(postRefreshHook) 301 prev = postRefreshHook 302 } 303 304 var installHook *state.Task 305 // only run install hook if installing the snap for the first time 306 if !snapst.IsInstalled() { 307 installHook = SetupInstallHook(st, snapsup.InstanceName()) 308 addTask(installHook) 309 prev = installHook 310 } 311 312 // run new services 313 startSnapServices := st.NewTask("start-snap-services", fmt.Sprintf(i18n.G("Start snap %q%s services"), snapsup.InstanceName(), revisionStr)) 314 addTask(startSnapServices) 315 prev = startSnapServices 316 317 // Do not do that if we are reverting to a local revision 318 if snapst.IsInstalled() && !snapsup.Flags.Revert { 319 var retain int 320 if err := config.NewTransaction(st).Get("core", "refresh.retain", &retain); err != nil { 321 // on classic we only keep 2 copies by default 322 if release.OnClassic { 323 retain = 2 324 } else { 325 retain = 3 326 } 327 } 328 retain-- // we're adding one 329 330 seq := snapst.Sequence 331 currentIndex := snapst.LastIndex(snapst.Current) 332 333 // discard everything after "current" (we may have reverted to 334 // a previous versions earlier) 335 for i := currentIndex + 1; i < len(seq); i++ { 336 si := seq[i] 337 if si.Revision == targetRevision { 338 // but don't discard this one; its' the thing we're switching to! 339 continue 340 } 341 ts := removeInactiveRevision(st, snapsup.InstanceName(), si.SnapID, si.Revision) 342 ts.WaitFor(prev) 343 tasks = append(tasks, ts.Tasks()...) 344 prev = tasks[len(tasks)-1] 345 } 346 347 // make sure we're not scheduling the removal of the target 348 // revision in the case where the target revision is already in 349 // the sequence. 350 for i := 0; i < currentIndex; i++ { 351 si := seq[i] 352 if si.Revision == targetRevision { 353 // we do *not* want to removeInactiveRevision of this one 354 copy(seq[i:], seq[i+1:]) 355 seq = seq[:len(seq)-1] 356 currentIndex-- 357 } 358 } 359 360 // normal garbage collect 361 var inUse boot.InUseFunc 362 for i := 0; i <= currentIndex-retain; i++ { 363 if inUse == nil { 364 var err error 365 inUse, err = inUseCheck(snapsup.Type) 366 if err != nil { 367 return nil, err 368 } 369 } 370 371 si := seq[i] 372 if inUse(snapsup.InstanceName(), si.Revision) { 373 continue 374 } 375 ts := removeInactiveRevision(st, snapsup.InstanceName(), si.SnapID, si.Revision) 376 ts.WaitFor(prev) 377 tasks = append(tasks, ts.Tasks()...) 378 prev = tasks[len(tasks)-1] 379 } 380 381 addTask(st.NewTask("cleanup", fmt.Sprintf("Clean up %q%s install", snapsup.InstanceName(), revisionStr))) 382 } 383 384 installSet := state.NewTaskSet(tasks...) 385 installSet.WaitAll(ts) 386 installSet.MarkEdge(prereq, BeginEdge) 387 installSet.MarkEdge(setupAliases, BeforeHooksEdge) 388 if installHook != nil { 389 installSet.MarkEdge(installHook, HooksEdge) 390 } 391 ts.AddAllWithEdges(installSet) 392 if checkAsserts != nil { 393 ts.MarkEdge(checkAsserts, DownloadAndChecksDoneEdge) 394 } 395 396 if flags&skipConfigure != 0 { 397 return installSet, nil 398 } 399 400 // we do not support configuration for bases or the "snapd" snap yet 401 if snapsup.Type != snap.TypeBase && snapsup.Type != snap.TypeSnapd { 402 confFlags := 0 403 notCore := snapsup.InstanceName() != "core" 404 hasSnapID := snapsup.SideInfo != nil && snapsup.SideInfo.SnapID != "" 405 if !snapst.IsInstalled() && hasSnapID && notCore { 406 // installation, run configure using the gadget defaults 407 // if available, system config defaults (attached to 408 // "core") are consumed only during seeding, via an 409 // explicit configure step separate from installing 410 confFlags |= UseConfigDefaults 411 } 412 configSet := ConfigureSnap(st, snapsup.InstanceName(), confFlags) 413 configSet.WaitAll(ts) 414 ts.AddAll(configSet) 415 } 416 417 healthCheck := CheckHealthHook(st, snapsup.InstanceName(), snapsup.Revision()) 418 healthCheck.WaitAll(ts) 419 ts.AddTask(healthCheck) 420 421 return ts, nil 422 } 423 424 // ConfigureSnap returns a set of tasks to configure snapName as done during installation/refresh. 425 func ConfigureSnap(st *state.State, snapName string, confFlags int) *state.TaskSet { 426 // This is slightly ugly, ideally we would check the type instead 427 // of hardcoding the name here. Unfortunately we do not have the 428 // type until we actually run the change. 429 if snapName == defaultCoreSnapName { 430 confFlags |= IgnoreHookError 431 confFlags |= TrackHookError 432 } 433 return Configure(st, snapName, nil, confFlags) 434 } 435 436 var Configure = func(st *state.State, snapName string, patch map[string]interface{}, flags int) *state.TaskSet { 437 panic("internal error: snapstate.Configure is unset") 438 } 439 440 var SetupInstallHook = func(st *state.State, snapName string) *state.Task { 441 panic("internal error: snapstate.SetupInstallHook is unset") 442 } 443 444 var SetupPreRefreshHook = func(st *state.State, snapName string) *state.Task { 445 panic("internal error: snapstate.SetupPreRefreshHook is unset") 446 } 447 448 var SetupPostRefreshHook = func(st *state.State, snapName string) *state.Task { 449 panic("internal error: snapstate.SetupPostRefreshHook is unset") 450 } 451 452 var SetupRemoveHook = func(st *state.State, snapName string) *state.Task { 453 panic("internal error: snapstate.SetupRemoveHook is unset") 454 } 455 456 var CheckHealthHook = func(st *state.State, snapName string, rev snap.Revision) *state.Task { 457 panic("internal error: snapstate.CheckHealthHook is unset") 458 } 459 460 var generateSnapdWrappers = backend.GenerateSnapdWrappers 461 462 // FinishRestart will return a Retry error if there is a pending restart 463 // and a real error if anything went wrong (like a rollback across 464 // restarts). 465 // For snapd snap updates this will also rerun wrappers generation to fully 466 // catch up with any change. 467 func FinishRestart(task *state.Task, snapsup *SnapSetup) (err error) { 468 if ok, _ := task.State().Restarting(); ok { 469 // don't continue until we are in the restarted snapd 470 task.Logf("Waiting for automatic snapd restart...") 471 return &state.Retry{} 472 } 473 474 if snapsup.Type == snap.TypeSnapd { 475 if os.Getenv("SNAPD_REVERT_TO_REV") != "" { 476 return fmt.Errorf("there was a snapd rollback across the restart") 477 } 478 479 // if we have restarted and snapd was refreshed, then we need to generate 480 // snapd wrappers again with current snapd, as the logic of generating 481 // wrappers may have changed between previous and new snapd code. 482 if !release.OnClassic { 483 snapdInfo, err := snap.ReadCurrentInfo(snapsup.SnapName()) 484 if err != nil { 485 return fmt.Errorf("cannot get current snapd snap info: %v", err) 486 } 487 // TODO: if future changes to wrappers need one more snapd restart, 488 // then it should be handled here as well. 489 if err := generateSnapdWrappers(snapdInfo); err != nil { 490 return err 491 } 492 } 493 } 494 495 // consider kernel and base 496 497 deviceCtx, err := DeviceCtx(task.State(), task, nil) 498 if err != nil { 499 return err 500 } 501 502 // Check if there was a rollback. A reboot can be triggered by: 503 // - core (old core16 world, system-reboot) 504 // - bootable base snap (new core18 world, system-reboot) 505 // - kernel 506 // 507 // On classic and in ephemeral run modes (like install, recover) 508 // there can never be a rollback so we can skip the check there. 509 // 510 // TODO: Detect "snapd" snap daemon-restarts here that 511 // fallback into the old version (once we have 512 // better snapd rollback support in core18). 513 if deviceCtx.RunMode() && !release.OnClassic { 514 // get the name of the name relevant for booting 515 // based on the given type 516 model := deviceCtx.Model() 517 var bootName string 518 switch snapsup.Type { 519 case snap.TypeKernel: 520 bootName = model.Kernel() 521 case snap.TypeOS, snap.TypeBase: 522 bootName = "core" 523 if model.Base() != "" { 524 bootName = model.Base() 525 } 526 default: 527 return nil 528 } 529 // if it is not a snap related to our booting we are not 530 // interested 531 if snapsup.InstanceName() != bootName { 532 return nil 533 } 534 535 // compare what we think is "current" for snapd with what 536 // actually booted. The bootloader may revert on a failed 537 // boot from a bad os/base/kernel to a good one and in this 538 // case we need to catch this and error accordingly 539 current, err := boot.GetCurrentBoot(snapsup.Type, deviceCtx) 540 if err == boot.ErrBootNameAndRevisionNotReady { 541 return &state.Retry{After: 5 * time.Second} 542 } 543 if err != nil { 544 return err 545 } 546 if snapsup.InstanceName() != current.SnapName() || snapsup.SideInfo.Revision != current.SnapRevision() { 547 // TODO: make sure this revision gets ignored for 548 // automatic refreshes 549 return fmt.Errorf("cannot finish %s installation, there was a rollback across reboot", snapsup.InstanceName()) 550 } 551 } 552 553 return nil 554 } 555 556 func contentAttr(attrer interfaces.Attrer) string { 557 var s string 558 err := attrer.Attr("content", &s) 559 if err != nil { 560 return "" 561 } 562 return s 563 } 564 565 func contentIfaceAvailable(st *state.State) map[string]bool { 566 repo := ifacerepo.Get(st) 567 contentSlots := repo.AllSlots("content") 568 avail := make(map[string]bool, len(contentSlots)) 569 for _, slot := range contentSlots { 570 contentTag := contentAttr(slot) 571 if contentTag == "" { 572 continue 573 } 574 avail[contentTag] = true 575 } 576 return avail 577 } 578 579 // defaultContentPlugProviders takes a snap.Info and returns what 580 // default providers there are. 581 func defaultContentPlugProviders(st *state.State, info *snap.Info) []string { 582 needed := snap.NeededDefaultProviders(info) 583 if len(needed) == 0 { 584 return nil 585 } 586 avail := contentIfaceAvailable(st) 587 out := []string{} 588 for snapInstance, contentTags := range needed { 589 for _, contentTag := range contentTags { 590 if !avail[contentTag] { 591 out = append(out, snapInstance) 592 break 593 } 594 } 595 } 596 return out 597 } 598 599 // validateFeatureFlags validates the given snap only uses experimental 600 // features that are enabled by the user. 601 func validateFeatureFlags(st *state.State, info *snap.Info) error { 602 tr := config.NewTransaction(st) 603 604 if len(info.Layout) > 0 { 605 flag, err := features.Flag(tr, features.Layouts) 606 if err != nil { 607 return err 608 } 609 if !flag { 610 return fmt.Errorf("experimental feature disabled - test it by setting 'experimental.layouts' to true") 611 } 612 } 613 614 if info.InstanceKey != "" { 615 flag, err := features.Flag(tr, features.ParallelInstances) 616 if err != nil { 617 return err 618 } 619 if !flag { 620 return fmt.Errorf("experimental feature disabled - test it by setting 'experimental.parallel-instances' to true") 621 } 622 } 623 624 var hasUserService, usesDbusActivation bool 625 for _, app := range info.Apps { 626 if app.IsService() && app.DaemonScope == snap.UserDaemon { 627 hasUserService = true 628 } 629 if len(app.ActivatesOn) != 0 { 630 usesDbusActivation = true 631 } 632 } 633 634 if hasUserService { 635 flag, err := features.Flag(tr, features.UserDaemons) 636 if err != nil { 637 return err 638 } 639 if !flag { 640 return fmt.Errorf("experimental feature disabled - test it by setting 'experimental.user-daemons' to true") 641 } 642 if !release.SystemctlSupportsUserUnits() { 643 return fmt.Errorf("user session daemons are not supported on this release") 644 } 645 } 646 647 if usesDbusActivation { 648 flag, err := features.Flag(tr, features.DbusActivation) 649 if err != nil { 650 return err 651 } 652 if !flag { 653 return fmt.Errorf("experimental feature disabled - test it by setting 'experimental.dbus-activation' to true") 654 } 655 } 656 657 return nil 658 } 659 660 func ensureInstallPreconditions(st *state.State, info *snap.Info, flags Flags, snapst *SnapState, deviceCtx DeviceContext) (Flags, error) { 661 // if snap is allowed to be devmode via the dangerous model and it's 662 // confinement is indeed devmode, promote the flags.DevMode to true 663 if flags.ApplySnapDevMode && info.NeedsDevMode() { 664 // TODO: what about jail-mode? will we also allow putting devmode 665 // snaps (i.e. snaps with snap.yaml with confinement: devmode) into 666 // strict confinement via the model assertion? 667 flags.DevMode = true 668 } 669 670 if flags.Classic && !info.NeedsClassic() { 671 // snap does not require classic confinement, silently drop the flag 672 flags.Classic = false 673 } 674 675 if err := validateInfoAndFlags(info, snapst, flags); err != nil { 676 return flags, err 677 } 678 if err := validateFeatureFlags(st, info); err != nil { 679 return flags, err 680 } 681 if err := checkDBusServiceConflicts(st, info); err != nil { 682 return flags, err 683 } 684 return flags, nil 685 } 686 687 // InstallPath returns a set of tasks for installing a snap from a file path 688 // and the snap.Info for the given snap. 689 // 690 // Note that the state must be locked by the caller. 691 // The provided SideInfo can contain just a name which results in a 692 // local revision and sideloading, or full metadata in which case it 693 // the snap will appear as installed from the store. 694 func InstallPath(st *state.State, si *snap.SideInfo, path, instanceName, channel string, flags Flags) (*state.TaskSet, *snap.Info, error) { 695 if si.RealName == "" { 696 return nil, nil, fmt.Errorf("internal error: snap name to install %q not provided", path) 697 } 698 699 if instanceName == "" { 700 instanceName = si.RealName 701 } 702 703 deviceCtx, err := DeviceCtxFromState(st, nil) 704 if err != nil { 705 return nil, nil, err 706 } 707 708 var snapst SnapState 709 err = Get(st, instanceName, &snapst) 710 if err != nil && err != state.ErrNoState { 711 return nil, nil, err 712 } 713 714 if si.SnapID != "" { 715 if si.Revision.Unset() { 716 return nil, nil, fmt.Errorf("internal error: snap id set to install %q but revision is unset", path) 717 } 718 } 719 720 channel, err = resolveChannel(st, instanceName, snapst.TrackingChannel, channel, deviceCtx) 721 if err != nil { 722 return nil, nil, err 723 } 724 725 var instFlags int 726 if flags.SkipConfigure { 727 // extract it as a doInstall flag, this is not passed 728 // into SnapSetup 729 instFlags |= skipConfigure 730 } 731 732 // It is ok do open the snap file here because we either 733 // have side info or the user passed --dangerous 734 info, container, err := backend.OpenSnapFile(path, si) 735 if err != nil { 736 return nil, nil, err 737 } 738 739 if err := validateContainer(container, info, logger.Noticef); err != nil { 740 return nil, nil, err 741 } 742 if err := snap.ValidateInstanceName(instanceName); err != nil { 743 return nil, nil, fmt.Errorf("invalid instance name: %v", err) 744 } 745 746 snapName, instanceKey := snap.SplitInstanceName(instanceName) 747 if info.SnapName() != snapName { 748 return nil, nil, fmt.Errorf("cannot install snap %q, the name does not match the metadata %q", instanceName, info.SnapName()) 749 } 750 info.InstanceKey = instanceKey 751 752 flags, err = ensureInstallPreconditions(st, info, flags, &snapst, deviceCtx) 753 if err != nil { 754 return nil, nil, err 755 } 756 // this might be a refresh; check the epoch before proceeding 757 if err := earlyEpochCheck(info, &snapst); err != nil { 758 return nil, nil, err 759 } 760 761 snapsup := &SnapSetup{ 762 Base: info.Base, 763 Prereq: defaultContentPlugProviders(st, info), 764 SideInfo: si, 765 SnapPath: path, 766 Channel: channel, 767 Flags: flags.ForSnapSetup(), 768 Type: info.Type(), 769 PlugsOnly: len(info.Slots) == 0, 770 InstanceKey: info.InstanceKey, 771 } 772 773 ts, err := doInstall(st, &snapst, snapsup, instFlags, "", inUseFor(deviceCtx)) 774 return ts, info, err 775 } 776 777 // TryPath returns a set of tasks for trying a snap from a file path. 778 // Note that the state must be locked by the caller. 779 func TryPath(st *state.State, name, path string, flags Flags) (*state.TaskSet, error) { 780 flags.TryMode = true 781 782 ts, _, err := InstallPath(st, &snap.SideInfo{RealName: name}, path, "", "", flags) 783 return ts, err 784 } 785 786 // Install returns a set of tasks for installing a snap. 787 // Note that the state must be locked by the caller. 788 // 789 // The returned TaskSet will contain a DownloadAndChecksDoneEdge. 790 func Install(ctx context.Context, st *state.State, name string, opts *RevisionOptions, userID int, flags Flags) (*state.TaskSet, error) { 791 return InstallWithDeviceContext(ctx, st, name, opts, userID, flags, nil, "") 792 } 793 794 // InstallWithDeviceContext returns a set of tasks for installing a snap. 795 // It will query for the snap with the given deviceCtx. 796 // Note that the state must be locked by the caller. 797 // 798 // The returned TaskSet will contain a DownloadAndChecksDoneEdge. 799 func InstallWithDeviceContext(ctx context.Context, st *state.State, name string, opts *RevisionOptions, userID int, flags Flags, deviceCtx DeviceContext, fromChange string) (*state.TaskSet, error) { 800 if opts == nil { 801 opts = &RevisionOptions{} 802 } 803 if opts.CohortKey != "" && !opts.Revision.Unset() { 804 return nil, errors.New("cannot specify revision and cohort") 805 } 806 807 if opts.Channel == "" { 808 opts.Channel = "stable" 809 } 810 811 var snapst SnapState 812 err := Get(st, name, &snapst) 813 if err != nil && err != state.ErrNoState { 814 return nil, err 815 } 816 if snapst.IsInstalled() { 817 return nil, &snap.AlreadyInstalledError{Snap: name} 818 } 819 // need to have a model set before trying to talk the store 820 deviceCtx, err = DevicePastSeeding(st, deviceCtx) 821 if err != nil { 822 return nil, err 823 } 824 825 if err := snap.ValidateInstanceName(name); err != nil { 826 return nil, fmt.Errorf("invalid instance name: %v", err) 827 } 828 829 sar, err := installInfo(ctx, st, name, opts, userID, deviceCtx) 830 if err != nil { 831 return nil, err 832 } 833 info := sar.Info 834 835 if flags.RequireTypeBase && info.Type() != snap.TypeBase && info.Type() != snap.TypeOS { 836 return nil, fmt.Errorf("unexpected snap type %q, instead of 'base'", info.Type()) 837 } 838 839 flags, err = ensureInstallPreconditions(st, info, flags, &snapst, deviceCtx) 840 if err != nil { 841 return nil, err 842 } 843 844 tr := config.NewTransaction(st) 845 checkDiskSpaceInstall, err := features.Flag(tr, features.CheckDiskSpaceInstall) 846 if err != nil && !config.IsNoOption(err) { 847 return nil, err 848 } 849 if checkDiskSpaceInstall { 850 // check if there is enough disk space for requested snap and its 851 // prerequisites. 852 totalSize, err := installSize(st, []*snap.Info{info}, userID) 853 if err != nil { 854 return nil, err 855 } 856 requiredSpace := safetyMarginDiskSpace(totalSize) 857 path := dirs.SnapdStateDir(dirs.GlobalRootDir) 858 if err := osutilCheckFreeSpace(path, requiredSpace); err != nil { 859 if _, ok := err.(*osutil.NotEnoughDiskSpaceError); ok { 860 return nil, &InsufficientSpaceError{ 861 Path: path, 862 Snaps: []string{info.InstanceName()}, 863 ChangeKind: "install", 864 } 865 } 866 return nil, err 867 } 868 } 869 870 snapsup := &SnapSetup{ 871 Channel: opts.Channel, 872 Base: info.Base, 873 Prereq: defaultContentPlugProviders(st, info), 874 UserID: userID, 875 Flags: flags.ForSnapSetup(), 876 DownloadInfo: &info.DownloadInfo, 877 SideInfo: &info.SideInfo, 878 Type: info.Type(), 879 PlugsOnly: len(info.Slots) == 0, 880 InstanceKey: info.InstanceKey, 881 auxStoreInfo: auxStoreInfo{ 882 Media: info.Media, 883 Website: info.Website, 884 }, 885 CohortKey: opts.CohortKey, 886 } 887 888 if sar.RedirectChannel != "" { 889 snapsup.Channel = sar.RedirectChannel 890 } 891 892 return doInstall(st, &snapst, snapsup, 0, fromChange, nil) 893 } 894 895 // InstallMany installs everything from the given list of names. 896 // Note that the state must be locked by the caller. 897 func InstallMany(st *state.State, names []string, userID int) ([]string, []*state.TaskSet, error) { 898 // need to have a model set before trying to talk the store 899 deviceCtx, err := DevicePastSeeding(st, nil) 900 if err != nil { 901 return nil, nil, err 902 } 903 904 toInstall := make([]string, 0, len(names)) 905 for _, name := range names { 906 var snapst SnapState 907 err := Get(st, name, &snapst) 908 if err != nil && err != state.ErrNoState { 909 return nil, nil, err 910 } 911 if snapst.IsInstalled() { 912 continue 913 } 914 915 if err := snap.ValidateInstanceName(name); err != nil { 916 return nil, nil, fmt.Errorf("invalid instance name: %v", err) 917 } 918 919 toInstall = append(toInstall, name) 920 } 921 922 user, err := userFromUserID(st, userID) 923 if err != nil { 924 return nil, nil, err 925 } 926 927 installs, err := installCandidates(st, toInstall, "stable", user) 928 if err != nil { 929 return nil, nil, err 930 } 931 932 tr := config.NewTransaction(st) 933 checkDiskSpaceInstall, err := features.Flag(tr, features.CheckDiskSpaceInstall) 934 if err != nil && !config.IsNoOption(err) { 935 return nil, nil, err 936 } 937 if checkDiskSpaceInstall { 938 // check if there is enough disk space for requested snaps and their 939 // prerequisites. 940 snapInfos := make([]*snap.Info, len(installs)) 941 for i, sar := range installs { 942 snapInfos[i] = sar.Info 943 } 944 totalSize, err := installSize(st, snapInfos, userID) 945 if err != nil { 946 return nil, nil, err 947 } 948 949 requiredSpace := safetyMarginDiskSpace(totalSize) 950 path := dirs.SnapdStateDir(dirs.GlobalRootDir) 951 if err := osutilCheckFreeSpace(path, requiredSpace); err != nil { 952 if _, ok := err.(*osutil.NotEnoughDiskSpaceError); ok { 953 return nil, nil, &InsufficientSpaceError{ 954 Path: path, 955 Snaps: toInstall, 956 ChangeKind: "install", 957 } 958 } 959 return nil, nil, err 960 } 961 } 962 963 tasksets := make([]*state.TaskSet, 0, len(installs)) 964 for _, sar := range installs { 965 info := sar.Info 966 var snapst SnapState 967 var flags Flags 968 969 flags, err := ensureInstallPreconditions(st, info, flags, &snapst, deviceCtx) 970 if err != nil { 971 return nil, nil, err 972 } 973 974 channel := "stable" 975 if sar.RedirectChannel != "" { 976 channel = sar.RedirectChannel 977 } 978 979 snapsup := &SnapSetup{ 980 Channel: channel, 981 Base: info.Base, 982 Prereq: defaultContentPlugProviders(st, info), 983 UserID: userID, 984 Flags: flags.ForSnapSetup(), 985 DownloadInfo: &info.DownloadInfo, 986 SideInfo: &info.SideInfo, 987 Type: info.Type(), 988 PlugsOnly: len(info.Slots) == 0, 989 InstanceKey: info.InstanceKey, 990 } 991 992 ts, err := doInstall(st, &snapst, snapsup, 0, "", inUseFor(deviceCtx)) 993 if err != nil { 994 return nil, nil, err 995 } 996 ts.JoinLane(st.NewLane()) 997 tasksets = append(tasksets, ts) 998 } 999 1000 return toInstall, tasksets, nil 1001 } 1002 1003 // RefreshCandidates gets a list of candidates for update 1004 // Note that the state must be locked by the caller. 1005 func RefreshCandidates(st *state.State, user *auth.UserState) ([]*snap.Info, error) { 1006 updates, _, _, err := refreshCandidates(context.TODO(), st, nil, user, nil) 1007 return updates, err 1008 } 1009 1010 // ValidateRefreshes allows to hook validation into the handling of refresh candidates. 1011 var ValidateRefreshes func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx DeviceContext) (validated []*snap.Info, err error) 1012 1013 // UpdateMany updates everything from the given list of names that the 1014 // store says is updateable. If the list is empty, update everything. 1015 // Note that the state must be locked by the caller. 1016 func UpdateMany(ctx context.Context, st *state.State, names []string, userID int, flags *Flags) ([]string, []*state.TaskSet, error) { 1017 return updateManyFiltered(ctx, st, names, userID, nil, flags, "") 1018 } 1019 1020 // updateFilter is the type of function that can be passed to 1021 // updateManyFromChange so it filters the updates. 1022 // 1023 // If the filter returns true, the update for that snap proceeds. If 1024 // it returns false, the snap is removed from the list of updates to 1025 // consider. 1026 type updateFilter func(*snap.Info, *SnapState) bool 1027 1028 func updateManyFiltered(ctx context.Context, st *state.State, names []string, userID int, filter updateFilter, flags *Flags, fromChange string) ([]string, []*state.TaskSet, error) { 1029 if flags == nil { 1030 flags = &Flags{} 1031 } 1032 user, err := userFromUserID(st, userID) 1033 if err != nil { 1034 return nil, nil, err 1035 } 1036 1037 // need to have a model set before trying to talk the store 1038 deviceCtx, err := DevicePastSeeding(st, nil) 1039 if err != nil { 1040 return nil, nil, err 1041 } 1042 1043 refreshOpts := &store.RefreshOptions{IsAutoRefresh: flags.IsAutoRefresh} 1044 updates, stateByInstanceName, ignoreValidation, err := refreshCandidates(ctx, st, names, user, refreshOpts) 1045 if err != nil { 1046 return nil, nil, err 1047 } 1048 1049 if filter != nil { 1050 actual := updates[:0] 1051 for _, update := range updates { 1052 if filter(update, stateByInstanceName[update.InstanceName()]) { 1053 actual = append(actual, update) 1054 } 1055 } 1056 updates = actual 1057 } 1058 1059 if ValidateRefreshes != nil && len(updates) != 0 { 1060 updates, err = ValidateRefreshes(st, updates, ignoreValidation, userID, deviceCtx) 1061 if err != nil { 1062 // not doing "refresh all" report the error 1063 if len(names) != 0 { 1064 return nil, nil, err 1065 } 1066 // doing "refresh all", log the problems 1067 logger.Noticef("cannot refresh some snaps: %v", err) 1068 } 1069 } 1070 1071 params := func(update *snap.Info) (*RevisionOptions, Flags, *SnapState) { 1072 snapst := stateByInstanceName[update.InstanceName()] 1073 // setting options to what's in state as multi-refresh doesn't let you change these 1074 opts := &RevisionOptions{ 1075 Channel: snapst.TrackingChannel, 1076 CohortKey: snapst.CohortKey, 1077 } 1078 return opts, snapst.Flags, snapst 1079 1080 } 1081 1082 tr := config.NewTransaction(st) 1083 checkDiskSpaceRefresh, err := features.Flag(tr, features.CheckDiskSpaceRefresh) 1084 if err != nil && !config.IsNoOption(err) { 1085 return nil, nil, err 1086 } 1087 if checkDiskSpaceRefresh { 1088 // check if there is enough disk space for requested snap and its 1089 // prerequisites. 1090 totalSize, err := installSize(st, updates, userID) 1091 if err != nil { 1092 return nil, nil, err 1093 } 1094 requiredSpace := safetyMarginDiskSpace(totalSize) 1095 path := dirs.SnapdStateDir(dirs.GlobalRootDir) 1096 if err := osutilCheckFreeSpace(path, requiredSpace); err != nil { 1097 snaps := make([]string, len(updates)) 1098 for i, up := range updates { 1099 snaps[i] = up.InstanceName() 1100 } 1101 if _, ok := err.(*osutil.NotEnoughDiskSpaceError); ok { 1102 return nil, nil, &InsufficientSpaceError{ 1103 Path: path, 1104 Snaps: snaps, 1105 ChangeKind: "refresh", 1106 } 1107 } 1108 return nil, nil, err 1109 } 1110 } 1111 1112 updated, tasksets, err := doUpdate(ctx, st, names, updates, params, userID, flags, deviceCtx, fromChange) 1113 if err != nil { 1114 return nil, nil, err 1115 } 1116 tasksets = finalizeUpdate(st, tasksets, len(updates) > 0, updated, userID, flags) 1117 return updated, tasksets, nil 1118 } 1119 1120 func doUpdate(ctx context.Context, st *state.State, names []string, updates []*snap.Info, params func(*snap.Info) (*RevisionOptions, Flags, *SnapState), userID int, globalFlags *Flags, deviceCtx DeviceContext, fromChange string) ([]string, []*state.TaskSet, error) { 1121 if globalFlags == nil { 1122 globalFlags = &Flags{} 1123 } 1124 1125 tasksets := make([]*state.TaskSet, 0, len(updates)+2) // 1 for auto-aliases, 1 for re-refresh 1126 1127 refreshAll := len(names) == 0 1128 var nameSet map[string]bool 1129 if len(names) != 0 { 1130 nameSet = make(map[string]bool, len(names)) 1131 for _, name := range names { 1132 nameSet[name] = true 1133 } 1134 } 1135 1136 newAutoAliases, mustPruneAutoAliases, transferTargets, err := autoAliasesUpdate(st, names, updates) 1137 if err != nil { 1138 return nil, nil, err 1139 } 1140 1141 reportUpdated := make(map[string]bool, len(updates)) 1142 var pruningAutoAliasesTs *state.TaskSet 1143 1144 if len(mustPruneAutoAliases) != 0 { 1145 var err error 1146 pruningAutoAliasesTs, err = applyAutoAliasesDelta(st, mustPruneAutoAliases, "prune", refreshAll, fromChange, func(snapName string, _ *state.TaskSet) { 1147 if nameSet[snapName] { 1148 reportUpdated[snapName] = true 1149 } 1150 }) 1151 if err != nil { 1152 return nil, nil, err 1153 } 1154 tasksets = append(tasksets, pruningAutoAliasesTs) 1155 } 1156 1157 // wait for the auto-alias prune tasks as needed 1158 scheduleUpdate := func(snapName string, ts *state.TaskSet) { 1159 if pruningAutoAliasesTs != nil && (mustPruneAutoAliases[snapName] != nil || transferTargets[snapName]) { 1160 ts.WaitAll(pruningAutoAliasesTs) 1161 } 1162 reportUpdated[snapName] = true 1163 } 1164 1165 // first snapd, core, bases, then rest 1166 sort.Stable(snap.ByType(updates)) 1167 prereqs := make(map[string]*state.TaskSet) 1168 waitPrereq := func(ts *state.TaskSet, prereqName string) { 1169 preTs := prereqs[prereqName] 1170 if preTs != nil { 1171 ts.WaitAll(preTs) 1172 } 1173 } 1174 1175 // updates is sorted by kind so this will process first core 1176 // and bases and then other snaps 1177 for _, update := range updates { 1178 revnoOpts, flags, snapst := params(update) 1179 flags.IsAutoRefresh = globalFlags.IsAutoRefresh 1180 1181 flags, err := ensureInstallPreconditions(st, update, flags, snapst, deviceCtx) 1182 if err != nil { 1183 if refreshAll { 1184 logger.Noticef("cannot update %q: %v", update.InstanceName(), err) 1185 continue 1186 } 1187 return nil, nil, err 1188 } 1189 1190 if err := earlyEpochCheck(update, snapst); err != nil { 1191 if refreshAll { 1192 logger.Noticef("cannot update %q: %v", update.InstanceName(), err) 1193 continue 1194 } 1195 return nil, nil, err 1196 } 1197 1198 snapUserID, err := userIDForSnap(st, snapst, userID) 1199 if err != nil { 1200 return nil, nil, err 1201 } 1202 1203 snapsup := &SnapSetup{ 1204 Base: update.Base, 1205 Prereq: defaultContentPlugProviders(st, update), 1206 Channel: revnoOpts.Channel, 1207 CohortKey: revnoOpts.CohortKey, 1208 UserID: snapUserID, 1209 Flags: flags.ForSnapSetup(), 1210 DownloadInfo: &update.DownloadInfo, 1211 SideInfo: &update.SideInfo, 1212 Type: update.Type(), 1213 PlugsOnly: len(update.Slots) == 0, 1214 InstanceKey: update.InstanceKey, 1215 auxStoreInfo: auxStoreInfo{ 1216 Website: update.Website, 1217 Media: update.Media, 1218 }, 1219 } 1220 1221 ts, err := doInstall(st, snapst, snapsup, 0, fromChange, inUseFor(deviceCtx)) 1222 if err != nil { 1223 if refreshAll { 1224 // doing "refresh all", just skip this snap 1225 logger.Noticef("cannot refresh snap %q: %v", update.InstanceName(), err) 1226 continue 1227 } 1228 return nil, nil, err 1229 } 1230 ts.JoinLane(st.NewLane()) 1231 1232 // because of the sorting of updates we fill prereqs 1233 // first (if branch) and only then use it to setup 1234 // waits (else branch) 1235 if t := update.Type(); t == snap.TypeOS || t == snap.TypeBase || t == snap.TypeSnapd { 1236 // prereq types come first in updates, we 1237 // also assume bases don't have hooks, otherwise 1238 // they would need to wait on core or snapd 1239 prereqs[update.InstanceName()] = ts 1240 } else { 1241 // prereqs were processed already, wait for 1242 // them as necessary for the other kind of 1243 // snaps 1244 waitPrereq(ts, defaultCoreSnapName) 1245 waitPrereq(ts, "snapd") 1246 if update.Base != "" { 1247 waitPrereq(ts, update.Base) 1248 } 1249 } 1250 1251 scheduleUpdate(update.InstanceName(), ts) 1252 tasksets = append(tasksets, ts) 1253 } 1254 1255 if len(newAutoAliases) != 0 { 1256 addAutoAliasesTs, err := applyAutoAliasesDelta(st, newAutoAliases, "refresh", refreshAll, fromChange, scheduleUpdate) 1257 if err != nil { 1258 return nil, nil, err 1259 } 1260 tasksets = append(tasksets, addAutoAliasesTs) 1261 } 1262 1263 updated := make([]string, 0, len(reportUpdated)) 1264 for name := range reportUpdated { 1265 updated = append(updated, name) 1266 } 1267 1268 return updated, tasksets, nil 1269 } 1270 1271 func finalizeUpdate(st *state.State, tasksets []*state.TaskSet, hasUpdates bool, updated []string, userID int, globalFlags *Flags) []*state.TaskSet { 1272 if hasUpdates && !globalFlags.NoReRefresh { 1273 // re-refresh will check the lanes to decide what to 1274 // _actually_ re-refresh, but it'll be a subset of updated 1275 // (and equal to updated if nothing goes wrong) 1276 rerefresh := st.NewTask("check-rerefresh", fmt.Sprintf("Consider re-refresh of %s", strutil.Quoted(updated))) 1277 rerefresh.Set("rerefresh-setup", reRefreshSetup{ 1278 UserID: userID, 1279 Flags: globalFlags, 1280 }) 1281 tasksets = append(tasksets, state.NewTaskSet(rerefresh)) 1282 } 1283 return tasksets 1284 } 1285 1286 func applyAutoAliasesDelta(st *state.State, delta map[string][]string, op string, refreshAll bool, fromChange string, linkTs func(instanceName string, ts *state.TaskSet)) (*state.TaskSet, error) { 1287 applyTs := state.NewTaskSet() 1288 kind := "refresh-aliases" 1289 msg := i18n.G("Refresh aliases for snap %q") 1290 if op == "prune" { 1291 kind = "prune-auto-aliases" 1292 msg = i18n.G("Prune automatic aliases for snap %q") 1293 } 1294 for instanceName, aliases := range delta { 1295 if err := checkChangeConflictIgnoringOneChange(st, instanceName, nil, fromChange); err != nil { 1296 if refreshAll { 1297 // doing "refresh all", just skip this snap 1298 logger.Noticef("cannot %s automatic aliases for snap %q: %v", op, instanceName, err) 1299 continue 1300 } 1301 return nil, err 1302 } 1303 1304 snapName, instanceKey := snap.SplitInstanceName(instanceName) 1305 snapsup := &SnapSetup{ 1306 SideInfo: &snap.SideInfo{RealName: snapName}, 1307 InstanceKey: instanceKey, 1308 } 1309 alias := st.NewTask(kind, fmt.Sprintf(msg, snapsup.InstanceName())) 1310 alias.Set("snap-setup", &snapsup) 1311 if op == "prune" { 1312 alias.Set("aliases", aliases) 1313 } 1314 ts := state.NewTaskSet(alias) 1315 linkTs(instanceName, ts) 1316 applyTs.AddAll(ts) 1317 } 1318 return applyTs, nil 1319 } 1320 1321 func autoAliasesUpdate(st *state.State, names []string, updates []*snap.Info) (changed map[string][]string, mustPrune map[string][]string, transferTargets map[string]bool, err error) { 1322 changed, dropped, err := autoAliasesDelta(st, nil) 1323 if err != nil { 1324 if len(names) != 0 { 1325 // not "refresh all", error 1326 return nil, nil, nil, err 1327 } 1328 // log and continue 1329 logger.Noticef("cannot find the delta for automatic aliases for some snaps: %v", err) 1330 } 1331 1332 refreshAll := len(names) == 0 1333 1334 // dropped alias -> snapName 1335 droppedAliases := make(map[string][]string, len(dropped)) 1336 for instanceName, aliases := range dropped { 1337 for _, alias := range aliases { 1338 droppedAliases[alias] = append(droppedAliases[alias], instanceName) 1339 } 1340 } 1341 1342 // filter changed considering only names if set: 1343 // we add auto-aliases only for mentioned snaps 1344 if !refreshAll && len(changed) != 0 { 1345 filteredChanged := make(map[string][]string, len(changed)) 1346 for _, name := range names { 1347 if changed[name] != nil { 1348 filteredChanged[name] = changed[name] 1349 } 1350 } 1351 changed = filteredChanged 1352 } 1353 1354 // mark snaps that are sources or target of transfers 1355 transferSources := make(map[string]bool, len(dropped)) 1356 transferTargets = make(map[string]bool, len(changed)) 1357 for instanceName, aliases := range changed { 1358 for _, alias := range aliases { 1359 if sources := droppedAliases[alias]; len(sources) != 0 { 1360 transferTargets[instanceName] = true 1361 for _, source := range sources { 1362 transferSources[source] = true 1363 } 1364 } 1365 } 1366 } 1367 1368 // snaps with updates 1369 updating := make(map[string]bool, len(updates)) 1370 for _, info := range updates { 1371 updating[info.InstanceName()] = true 1372 } 1373 1374 // add explicitly auto-aliases only for snaps that are not updated 1375 for instanceName := range changed { 1376 if updating[instanceName] { 1377 delete(changed, instanceName) 1378 } 1379 } 1380 1381 // prune explicitly auto-aliases only for snaps that are mentioned 1382 // and not updated OR the source of transfers 1383 mustPrune = make(map[string][]string, len(dropped)) 1384 for instanceName := range transferSources { 1385 mustPrune[instanceName] = dropped[instanceName] 1386 } 1387 if refreshAll { 1388 for instanceName, aliases := range dropped { 1389 if !updating[instanceName] { 1390 mustPrune[instanceName] = aliases 1391 } 1392 } 1393 } else { 1394 for _, name := range names { 1395 if !updating[name] && dropped[name] != nil { 1396 mustPrune[name] = dropped[name] 1397 } 1398 } 1399 } 1400 1401 return changed, mustPrune, transferTargets, nil 1402 } 1403 1404 // resolveChannel returns the effective channel to use, based on the requested 1405 // channel and constrains set by device model, or an error if switching to 1406 // requested channel is forbidden. 1407 func resolveChannel(st *state.State, snapName, oldChannel, newChannel string, deviceCtx DeviceContext) (effectiveChannel string, err error) { 1408 if newChannel == "" { 1409 return "", nil 1410 } 1411 1412 // ensure we do not switch away from the kernel-track in the model 1413 model := deviceCtx.Model() 1414 1415 var pinnedTrack, which string 1416 if snapName == model.Kernel() && model.KernelTrack() != "" { 1417 pinnedTrack, which = model.KernelTrack(), "kernel" 1418 } 1419 if snapName == model.Gadget() && model.GadgetTrack() != "" { 1420 pinnedTrack, which = model.GadgetTrack(), "gadget" 1421 } 1422 1423 if pinnedTrack == "" { 1424 // no pinned track 1425 return channel.Resolve(oldChannel, newChannel) 1426 } 1427 1428 // channel name is valid and consist of risk level or 1429 // risk/branch only, do the right thing and default to risk (or 1430 // risk/branch) within the pinned track 1431 resChannel, err := channel.ResolvePinned(pinnedTrack, newChannel) 1432 if err == channel.ErrPinnedTrackSwitch { 1433 // switching to a different track is not allowed 1434 return "", fmt.Errorf("cannot switch from %s track %q as specified for the (device) model to %q", which, pinnedTrack, newChannel) 1435 1436 } 1437 if err != nil { 1438 return "", err 1439 } 1440 return resChannel, nil 1441 } 1442 1443 var errRevisionSwitch = errors.New("cannot switch revision") 1444 1445 func switchSummary(snap, chanFrom, chanTo, cohFrom, cohTo string) string { 1446 if cohFrom != cohTo { 1447 if cohTo == "" { 1448 // leave cohort 1449 if chanFrom == chanTo { 1450 return fmt.Sprintf(i18n.G("Switch snap %q away from cohort %q"), 1451 snap, strutil.ElliptLeft(cohFrom, 10)) 1452 } 1453 if chanFrom == "" { 1454 return fmt.Sprintf(i18n.G("Switch snap %q to channel %q and away from cohort %q"), 1455 snap, chanTo, strutil.ElliptLeft(cohFrom, 10), 1456 ) 1457 } 1458 return fmt.Sprintf(i18n.G("Switch snap %q from channel %q to %q and away from cohort %q"), 1459 snap, chanFrom, chanTo, strutil.ElliptLeft(cohFrom, 10), 1460 ) 1461 } 1462 if cohFrom == "" { 1463 // moving into a cohort 1464 if chanFrom == chanTo { 1465 return fmt.Sprintf(i18n.G("Switch snap %q from no cohort to %q"), 1466 snap, strutil.ElliptLeft(cohTo, 10)) 1467 } 1468 if chanFrom == "" { 1469 return fmt.Sprintf(i18n.G("Switch snap %q to channel %q and from no cohort to %q"), 1470 snap, chanTo, strutil.ElliptLeft(cohTo, 10), 1471 ) 1472 } 1473 // chanTo == "" is not interesting 1474 return fmt.Sprintf(i18n.G("Switch snap %q from channel %q to %q and from no cohort to %q"), 1475 snap, chanFrom, chanTo, strutil.ElliptLeft(cohTo, 10), 1476 ) 1477 } 1478 if chanFrom == chanTo { 1479 return fmt.Sprintf(i18n.G("Switch snap %q from cohort %q to %q"), 1480 snap, strutil.ElliptLeft(cohFrom, 10), strutil.ElliptLeft(cohTo, 10)) 1481 } 1482 if chanFrom == "" { 1483 return fmt.Sprintf(i18n.G("Switch snap %q to channel %q and from cohort %q to %q"), 1484 snap, chanTo, strutil.ElliptLeft(cohFrom, 10), strutil.ElliptLeft(cohTo, 10), 1485 ) 1486 } 1487 return fmt.Sprintf(i18n.G("Switch snap %q from channel %q to %q and from cohort %q to %q"), 1488 snap, chanFrom, chanTo, 1489 strutil.ElliptLeft(cohFrom, 10), strutil.ElliptLeft(cohTo, 10), 1490 ) 1491 } 1492 1493 if chanFrom == "" { 1494 return fmt.Sprintf(i18n.G("Switch snap %q to channel %q"), 1495 snap, chanTo) 1496 } 1497 if chanFrom != chanTo { 1498 return fmt.Sprintf(i18n.G("Switch snap %q from channel %q to %q"), 1499 snap, chanFrom, chanTo) 1500 } 1501 // a no-change switch is accepted for idempotency 1502 return "No change switch (no-op)" 1503 } 1504 1505 // Switch switches a snap to a new channel and/or cohort 1506 func Switch(st *state.State, name string, opts *RevisionOptions) (*state.TaskSet, error) { 1507 if opts == nil { 1508 opts = &RevisionOptions{} 1509 } 1510 if !opts.Revision.Unset() { 1511 return nil, errRevisionSwitch 1512 } 1513 var snapst SnapState 1514 err := Get(st, name, &snapst) 1515 if err != nil && err != state.ErrNoState { 1516 return nil, err 1517 } 1518 if !snapst.IsInstalled() { 1519 return nil, &snap.NotInstalledError{Snap: name} 1520 } 1521 1522 if err := CheckChangeConflict(st, name, nil); err != nil { 1523 return nil, err 1524 } 1525 1526 deviceCtx, err := DeviceCtxFromState(st, nil) 1527 if err != nil { 1528 return nil, err 1529 } 1530 1531 opts.Channel, err = resolveChannel(st, name, snapst.TrackingChannel, opts.Channel, deviceCtx) 1532 if err != nil { 1533 return nil, err 1534 } 1535 1536 snapsup := &SnapSetup{ 1537 SideInfo: snapst.CurrentSideInfo(), 1538 InstanceKey: snapst.InstanceKey, 1539 // set the from state (i.e. no change), they are overridden from opts as needed below 1540 CohortKey: snapst.CohortKey, 1541 Channel: snapst.TrackingChannel, 1542 } 1543 1544 if opts.Channel != "" { 1545 snapsup.Channel = opts.Channel 1546 } 1547 if opts.CohortKey != "" { 1548 snapsup.CohortKey = opts.CohortKey 1549 } 1550 if opts.LeaveCohort { 1551 snapsup.CohortKey = "" 1552 } 1553 1554 summary := switchSummary(snapsup.InstanceName(), snapst.TrackingChannel, snapsup.Channel, snapst.CohortKey, snapsup.CohortKey) 1555 switchSnap := st.NewTask("switch-snap", summary) 1556 switchSnap.Set("snap-setup", &snapsup) 1557 1558 return state.NewTaskSet(switchSnap), nil 1559 } 1560 1561 // RevisionOptions control the selection of a snap revision. 1562 type RevisionOptions struct { 1563 Channel string 1564 Revision snap.Revision 1565 CohortKey string 1566 LeaveCohort bool 1567 } 1568 1569 // Update initiates a change updating a snap. 1570 // Note that the state must be locked by the caller. 1571 // 1572 // The returned TaskSet will contain a DownloadAndChecksDoneEdge. 1573 func Update(st *state.State, name string, opts *RevisionOptions, userID int, flags Flags) (*state.TaskSet, error) { 1574 return UpdateWithDeviceContext(st, name, opts, userID, flags, nil, "") 1575 } 1576 1577 // UpdateWithDeviceContext initiates a change updating a snap. 1578 // It will query for the snap with the given deviceCtx. 1579 // Note that the state must be locked by the caller. 1580 // 1581 // The returned TaskSet will contain a DownloadAndChecksDoneEdge. 1582 func UpdateWithDeviceContext(st *state.State, name string, opts *RevisionOptions, userID int, flags Flags, deviceCtx DeviceContext, fromChange string) (*state.TaskSet, error) { 1583 if opts == nil { 1584 opts = &RevisionOptions{} 1585 } 1586 var snapst SnapState 1587 err := Get(st, name, &snapst) 1588 if err != nil && err != state.ErrNoState { 1589 return nil, err 1590 } 1591 if !snapst.IsInstalled() { 1592 return nil, &snap.NotInstalledError{Snap: name} 1593 } 1594 1595 // FIXME: snaps that are not active are skipped for now 1596 // until we know what we want to do 1597 if !snapst.Active { 1598 return nil, fmt.Errorf("refreshing disabled snap %q not supported", name) 1599 } 1600 1601 // need to have a model set before trying to talk the store 1602 deviceCtx, err = DevicePastSeeding(st, deviceCtx) 1603 if err != nil { 1604 return nil, err 1605 } 1606 1607 opts.Channel, err = resolveChannel(st, name, snapst.TrackingChannel, opts.Channel, deviceCtx) 1608 if err != nil { 1609 return nil, err 1610 } 1611 1612 if opts.Channel == "" { 1613 // default to tracking the same channel 1614 opts.Channel = snapst.TrackingChannel 1615 } 1616 if opts.CohortKey == "" { 1617 // default to being in the same cohort 1618 opts.CohortKey = snapst.CohortKey 1619 } 1620 if opts.LeaveCohort { 1621 opts.CohortKey = "" 1622 } 1623 1624 // TODO: make flags be per revision to avoid this logic (that 1625 // leaves corner cases all over the place) 1626 if !(flags.JailMode || flags.DevMode) { 1627 flags.Classic = flags.Classic || snapst.Flags.Classic 1628 } 1629 1630 var updates []*snap.Info 1631 info, infoErr := infoForUpdate(st, &snapst, name, opts, userID, flags, deviceCtx) 1632 switch infoErr { 1633 case nil: 1634 updates = append(updates, info) 1635 case store.ErrNoUpdateAvailable: 1636 // there may be some new auto-aliases 1637 default: 1638 return nil, infoErr 1639 } 1640 1641 tr := config.NewTransaction(st) 1642 checkDiskSpaceRefresh, err := features.Flag(tr, features.CheckDiskSpaceRefresh) 1643 if err != nil && !config.IsNoOption(err) { 1644 return nil, err 1645 } 1646 if checkDiskSpaceRefresh { 1647 // check if there is enough disk space for requested snap and its 1648 // prerequisites. 1649 totalSize, err := installSize(st, updates, userID) 1650 if err != nil { 1651 return nil, err 1652 } 1653 requiredSpace := safetyMarginDiskSpace(totalSize) 1654 path := dirs.SnapdStateDir(dirs.GlobalRootDir) 1655 if err := osutilCheckFreeSpace(path, requiredSpace); err != nil { 1656 snaps := make([]string, len(updates)) 1657 for i, up := range updates { 1658 snaps[i] = up.InstanceName() 1659 } 1660 if _, ok := err.(*osutil.NotEnoughDiskSpaceError); ok { 1661 return nil, &InsufficientSpaceError{ 1662 Path: path, 1663 Snaps: snaps, 1664 ChangeKind: "refresh", 1665 } 1666 } 1667 return nil, err 1668 } 1669 } 1670 1671 params := func(update *snap.Info) (*RevisionOptions, Flags, *SnapState) { 1672 return opts, flags, &snapst 1673 } 1674 1675 _, tts, err := doUpdate(context.TODO(), st, []string{name}, updates, params, userID, &flags, deviceCtx, fromChange) 1676 if err != nil { 1677 return nil, err 1678 } 1679 1680 // see if we need to switch the channel or cohort, or toggle ignore-validation 1681 switchChannel := snapst.TrackingChannel != opts.Channel 1682 switchCohortKey := snapst.CohortKey != opts.CohortKey 1683 toggleIgnoreValidation := snapst.IgnoreValidation != flags.IgnoreValidation 1684 if infoErr == store.ErrNoUpdateAvailable && (switchChannel || switchCohortKey || toggleIgnoreValidation) { 1685 if err := checkChangeConflictIgnoringOneChange(st, name, nil, fromChange); err != nil { 1686 return nil, err 1687 } 1688 1689 snapsup := &SnapSetup{ 1690 SideInfo: snapst.CurrentSideInfo(), 1691 Flags: snapst.Flags.ForSnapSetup(), 1692 InstanceKey: snapst.InstanceKey, 1693 CohortKey: opts.CohortKey, 1694 } 1695 1696 if switchChannel || switchCohortKey { 1697 // update the tracked channel and cohort 1698 snapsup.Channel = opts.Channel 1699 snapsup.CohortKey = opts.CohortKey 1700 // Update the current snap channel as well. This ensures that 1701 // the UI displays the right values. 1702 snapsup.SideInfo.Channel = opts.Channel 1703 1704 summary := switchSummary(snapsup.InstanceName(), snapst.TrackingChannel, opts.Channel, snapst.CohortKey, opts.CohortKey) 1705 switchSnap := st.NewTask("switch-snap-channel", summary) 1706 switchSnap.Set("snap-setup", &snapsup) 1707 1708 switchSnapTs := state.NewTaskSet(switchSnap) 1709 for _, ts := range tts { 1710 switchSnapTs.WaitAll(ts) 1711 } 1712 tts = append(tts, switchSnapTs) 1713 } 1714 1715 if toggleIgnoreValidation { 1716 snapsup.IgnoreValidation = flags.IgnoreValidation 1717 toggle := st.NewTask("toggle-snap-flags", fmt.Sprintf(i18n.G("Toggle snap %q flags"), snapsup.InstanceName())) 1718 toggle.Set("snap-setup", &snapsup) 1719 1720 toggleTs := state.NewTaskSet(toggle) 1721 for _, ts := range tts { 1722 toggleTs.WaitAll(ts) 1723 } 1724 tts = append(tts, toggleTs) 1725 } 1726 } 1727 1728 if len(tts) == 0 && len(updates) == 0 { 1729 // really nothing to do, return the original no-update-available error 1730 return nil, infoErr 1731 } 1732 1733 tts = finalizeUpdate(st, tts, len(updates) > 0, []string{name}, userID, &flags) 1734 1735 flat := state.NewTaskSet() 1736 for _, ts := range tts { 1737 // The tasksets we get from "doUpdate" contain important 1738 // "TaskEdge" information that is needed for "Remodel". 1739 // To preserve those we need to use "AddAllWithEdges()". 1740 if err := flat.AddAllWithEdges(ts); err != nil { 1741 return nil, err 1742 } 1743 } 1744 return flat, nil 1745 } 1746 1747 func infoForUpdate(st *state.State, snapst *SnapState, name string, opts *RevisionOptions, userID int, flags Flags, deviceCtx DeviceContext) (*snap.Info, error) { 1748 if opts.Revision.Unset() { 1749 // good ol' refresh 1750 info, err := updateInfo(st, snapst, opts, userID, flags, deviceCtx) 1751 if err != nil { 1752 return nil, err 1753 } 1754 if ValidateRefreshes != nil && !flags.IgnoreValidation { 1755 _, err := ValidateRefreshes(st, []*snap.Info{info}, nil, userID, deviceCtx) 1756 if err != nil { 1757 return nil, err 1758 } 1759 } 1760 return info, nil 1761 } 1762 var sideInfo *snap.SideInfo 1763 for _, si := range snapst.Sequence { 1764 if si.Revision == opts.Revision { 1765 sideInfo = si 1766 break 1767 } 1768 } 1769 if sideInfo == nil { 1770 // refresh from given revision from store 1771 return updateToRevisionInfo(st, snapst, opts.Revision, userID, deviceCtx) 1772 } 1773 1774 // refresh-to-local, this assumes the snap revision is mounted 1775 return readInfo(name, sideInfo, errorOnBroken) 1776 } 1777 1778 // AutoRefreshAssertions allows to hook fetching of important assertions 1779 // into the Autorefresh function. 1780 var AutoRefreshAssertions func(st *state.State, userID int) error 1781 1782 // AutoRefresh is the wrapper that will do a refresh of all the installed 1783 // snaps on the system. In addition to that it will also refresh important 1784 // assertions. 1785 func AutoRefresh(ctx context.Context, st *state.State) ([]string, []*state.TaskSet, error) { 1786 userID := 0 1787 1788 if AutoRefreshAssertions != nil { 1789 if err := AutoRefreshAssertions(st, userID); err != nil { 1790 return nil, nil, err 1791 } 1792 } 1793 1794 return UpdateMany(ctx, st, nil, userID, &Flags{IsAutoRefresh: true}) 1795 } 1796 1797 // LinkNewBaseOrKernel will create prepare/link-snap tasks for a remodel 1798 func LinkNewBaseOrKernel(st *state.State, name string) (*state.TaskSet, error) { 1799 var snapst SnapState 1800 err := Get(st, name, &snapst) 1801 if err == state.ErrNoState { 1802 return nil, &snap.NotInstalledError{Snap: name} 1803 } 1804 if err != nil { 1805 return nil, err 1806 } 1807 1808 if err := CheckChangeConflict(st, name, nil); err != nil { 1809 return nil, err 1810 } 1811 1812 info, err := snapst.CurrentInfo() 1813 if err != nil { 1814 return nil, err 1815 } 1816 1817 switch info.Type() { 1818 case snap.TypeOS, snap.TypeBase, snap.TypeKernel: 1819 // good 1820 default: 1821 // bad 1822 return nil, fmt.Errorf("cannot link type %v", info.Type()) 1823 } 1824 1825 snapsup := &SnapSetup{ 1826 SideInfo: snapst.CurrentSideInfo(), 1827 Flags: snapst.Flags.ForSnapSetup(), 1828 Type: info.Type(), 1829 PlugsOnly: len(info.Slots) == 0, 1830 InstanceKey: snapst.InstanceKey, 1831 } 1832 1833 prepareSnap := st.NewTask("prepare-snap", fmt.Sprintf(i18n.G("Prepare snap %q (%s) for remodel"), snapsup.InstanceName(), snapst.Current)) 1834 prepareSnap.Set("snap-setup", &snapsup) 1835 1836 linkSnap := st.NewTask("link-snap", fmt.Sprintf(i18n.G("Make snap %q (%s) available to the system during remodel"), snapsup.InstanceName(), snapst.Current)) 1837 linkSnap.Set("snap-setup-task", prepareSnap.ID()) 1838 linkSnap.WaitFor(prepareSnap) 1839 1840 // we need this for remodel 1841 ts := state.NewTaskSet(prepareSnap, linkSnap) 1842 ts.MarkEdge(prepareSnap, DownloadAndChecksDoneEdge) 1843 return ts, nil 1844 } 1845 1846 // Enable sets a snap to the active state 1847 func Enable(st *state.State, name string) (*state.TaskSet, error) { 1848 var snapst SnapState 1849 err := Get(st, name, &snapst) 1850 if err == state.ErrNoState { 1851 return nil, &snap.NotInstalledError{Snap: name} 1852 } 1853 if err != nil { 1854 return nil, err 1855 } 1856 1857 if snapst.Active { 1858 return nil, fmt.Errorf("snap %q already enabled", name) 1859 } 1860 1861 if err := CheckChangeConflict(st, name, nil); err != nil { 1862 return nil, err 1863 } 1864 1865 info, err := snapst.CurrentInfo() 1866 if err != nil { 1867 return nil, err 1868 } 1869 1870 snapsup := &SnapSetup{ 1871 SideInfo: snapst.CurrentSideInfo(), 1872 Flags: snapst.Flags.ForSnapSetup(), 1873 Type: info.Type(), 1874 PlugsOnly: len(info.Slots) == 0, 1875 InstanceKey: snapst.InstanceKey, 1876 } 1877 1878 prepareSnap := st.NewTask("prepare-snap", fmt.Sprintf(i18n.G("Prepare snap %q (%s)"), snapsup.InstanceName(), snapst.Current)) 1879 prepareSnap.Set("snap-setup", &snapsup) 1880 1881 setupProfiles := st.NewTask("setup-profiles", fmt.Sprintf(i18n.G("Setup snap %q (%s) security profiles"), snapsup.InstanceName(), snapst.Current)) 1882 setupProfiles.Set("snap-setup-task", prepareSnap.ID()) 1883 setupProfiles.WaitFor(prepareSnap) 1884 1885 linkSnap := st.NewTask("link-snap", fmt.Sprintf(i18n.G("Make snap %q (%s) available to the system"), snapsup.InstanceName(), snapst.Current)) 1886 linkSnap.Set("snap-setup-task", prepareSnap.ID()) 1887 linkSnap.WaitFor(setupProfiles) 1888 1889 // setup aliases 1890 setupAliases := st.NewTask("setup-aliases", fmt.Sprintf(i18n.G("Setup snap %q aliases"), snapsup.InstanceName())) 1891 setupAliases.Set("snap-setup-task", prepareSnap.ID()) 1892 setupAliases.WaitFor(linkSnap) 1893 1894 startSnapServices := st.NewTask("start-snap-services", fmt.Sprintf(i18n.G("Start snap %q (%s) services"), snapsup.InstanceName(), snapst.Current)) 1895 startSnapServices.Set("snap-setup-task", prepareSnap.ID()) 1896 startSnapServices.WaitFor(setupAliases) 1897 1898 return state.NewTaskSet(prepareSnap, setupProfiles, linkSnap, setupAliases, startSnapServices), nil 1899 } 1900 1901 // Disable sets a snap to the inactive state 1902 func Disable(st *state.State, name string) (*state.TaskSet, error) { 1903 var snapst SnapState 1904 err := Get(st, name, &snapst) 1905 if err == state.ErrNoState { 1906 return nil, &snap.NotInstalledError{Snap: name} 1907 } 1908 if err != nil { 1909 return nil, err 1910 } 1911 if !snapst.Active { 1912 return nil, fmt.Errorf("snap %q already disabled", name) 1913 } 1914 1915 info, err := Info(st, name, snapst.Current) 1916 if err != nil { 1917 return nil, err 1918 } 1919 if !canDisable(info) { 1920 return nil, fmt.Errorf("snap %q cannot be disabled", name) 1921 } 1922 1923 if err := CheckChangeConflict(st, name, nil); err != nil { 1924 return nil, err 1925 } 1926 1927 snapsup := &SnapSetup{ 1928 SideInfo: &snap.SideInfo{ 1929 RealName: snap.InstanceSnap(name), 1930 Revision: snapst.Current, 1931 }, 1932 Type: info.Type(), 1933 PlugsOnly: len(info.Slots) == 0, 1934 InstanceKey: snapst.InstanceKey, 1935 } 1936 1937 stopSnapServices := st.NewTask("stop-snap-services", fmt.Sprintf(i18n.G("Stop snap %q (%s) services"), snapsup.InstanceName(), snapst.Current)) 1938 stopSnapServices.Set("snap-setup", &snapsup) 1939 stopSnapServices.Set("stop-reason", snap.StopReasonDisable) 1940 1941 removeAliases := st.NewTask("remove-aliases", fmt.Sprintf(i18n.G("Remove aliases for snap %q"), snapsup.InstanceName())) 1942 removeAliases.Set("snap-setup-task", stopSnapServices.ID()) 1943 removeAliases.WaitFor(stopSnapServices) 1944 1945 unlinkSnap := st.NewTask("unlink-snap", fmt.Sprintf(i18n.G("Make snap %q (%s) unavailable to the system"), snapsup.InstanceName(), snapst.Current)) 1946 unlinkSnap.Set("snap-setup-task", stopSnapServices.ID()) 1947 unlinkSnap.WaitFor(removeAliases) 1948 1949 removeProfiles := st.NewTask("remove-profiles", fmt.Sprintf(i18n.G("Remove security profiles of snap %q"), snapsup.InstanceName())) 1950 removeProfiles.Set("snap-setup-task", stopSnapServices.ID()) 1951 removeProfiles.WaitFor(unlinkSnap) 1952 1953 return state.NewTaskSet(stopSnapServices, removeAliases, unlinkSnap, removeProfiles), nil 1954 } 1955 1956 // canDisable verifies that a snap can be deactivated. 1957 func canDisable(si *snap.Info) bool { 1958 for _, importantSnapType := range []snap.Type{snap.TypeGadget, snap.TypeKernel, snap.TypeOS} { 1959 if importantSnapType == si.Type() { 1960 return false 1961 } 1962 } 1963 1964 return true 1965 } 1966 1967 // canRemove verifies that a snap can be removed. 1968 func canRemove(st *state.State, si *snap.Info, snapst *SnapState, removeAll bool, deviceCtx DeviceContext) error { 1969 rev := snap.Revision{} 1970 if !removeAll { 1971 rev = si.Revision 1972 } 1973 1974 return PolicyFor(si.Type(), deviceCtx.Model()).CanRemove(st, snapst, rev, deviceCtx) 1975 } 1976 1977 // RemoveFlags are used to pass additional flags to the Remove operation. 1978 type RemoveFlags struct { 1979 // Remove the snap without creating snapshot data 1980 Purge bool 1981 } 1982 1983 // Remove returns a set of tasks for removing snap. 1984 // Note that the state must be locked by the caller. 1985 func Remove(st *state.State, name string, revision snap.Revision, flags *RemoveFlags) (*state.TaskSet, error) { 1986 ts, snapshotSize, err := removeTasks(st, name, revision, flags) 1987 // removeTasks() checks check-disk-space-remove feature flag, so snapshotSize 1988 // will only be greater than 0 if the feature is enabled. 1989 if snapshotSize > 0 { 1990 requiredSpace := safetyMarginDiskSpace(snapshotSize) 1991 path := dirs.SnapdStateDir(dirs.GlobalRootDir) 1992 if err := osutilCheckFreeSpace(path, requiredSpace); err != nil { 1993 if _, ok := err.(*osutil.NotEnoughDiskSpaceError); ok { 1994 return nil, &InsufficientSpaceError{ 1995 Path: path, 1996 Snaps: []string{name}, 1997 ChangeKind: "remove", 1998 Message: fmt.Sprintf("cannot create automatic snapshot when removing last revision of the snap: %v", err)} 1999 } 2000 return nil, err 2001 } 2002 } 2003 return ts, err 2004 } 2005 2006 // removeTasks provides the task set to remove snap name after taking a snapshot 2007 // if flags.Purge is not true, it also computes an estimate of the latter size. 2008 func removeTasks(st *state.State, name string, revision snap.Revision, flags *RemoveFlags) (removeTs *state.TaskSet, snapshotSize uint64, err error) { 2009 var snapst SnapState 2010 err = Get(st, name, &snapst) 2011 if err != nil && err != state.ErrNoState { 2012 return nil, 0, err 2013 } 2014 2015 if !snapst.IsInstalled() { 2016 return nil, 0, &snap.NotInstalledError{Snap: name, Rev: snap.R(0)} 2017 } 2018 2019 if err := CheckChangeConflict(st, name, nil); err != nil { 2020 return nil, 0, err 2021 } 2022 2023 deviceCtx, err := DeviceCtxFromState(st, nil) 2024 if err != nil { 2025 return nil, 0, err 2026 } 2027 2028 active := snapst.Active 2029 var removeAll bool 2030 if revision.Unset() { 2031 revision = snapst.Current 2032 removeAll = true 2033 } else { 2034 if active { 2035 if revision == snapst.Current { 2036 msg := "cannot remove active revision %s of snap %q" 2037 if len(snapst.Sequence) > 1 { 2038 msg += " (revert first?)" 2039 } 2040 return nil, 0, fmt.Errorf(msg, revision, name) 2041 } 2042 active = false 2043 } 2044 2045 if !revisionInSequence(&snapst, revision) { 2046 return nil, 0, &snap.NotInstalledError{Snap: name, Rev: revision} 2047 } 2048 2049 removeAll = len(snapst.Sequence) == 1 2050 } 2051 2052 info, err := Info(st, name, revision) 2053 if err != nil { 2054 return nil, 0, err 2055 } 2056 2057 // check if this is something that can be removed 2058 if err := canRemove(st, info, &snapst, removeAll, deviceCtx); err != nil { 2059 return nil, 0, fmt.Errorf("snap %q is not removable: %v", name, err) 2060 } 2061 2062 // main/current SnapSetup 2063 snapsup := SnapSetup{ 2064 SideInfo: &snap.SideInfo{ 2065 SnapID: info.SnapID, 2066 RealName: snap.InstanceSnap(name), 2067 Revision: revision, 2068 }, 2069 Type: info.Type(), 2070 PlugsOnly: len(info.Slots) == 0, 2071 InstanceKey: snapst.InstanceKey, 2072 } 2073 2074 // trigger remove 2075 2076 removeTs = state.NewTaskSet() 2077 var chain *state.TaskSet 2078 2079 addNext := func(ts *state.TaskSet) { 2080 if chain != nil { 2081 ts.WaitAll(chain) 2082 } 2083 removeTs.AddAll(ts) 2084 chain = ts 2085 } 2086 2087 var prev *state.Task 2088 var stopSnapServices *state.Task 2089 if active { 2090 stopSnapServices = st.NewTask("stop-snap-services", fmt.Sprintf(i18n.G("Stop snap %q services"), name)) 2091 stopSnapServices.Set("snap-setup", snapsup) 2092 stopSnapServices.Set("stop-reason", snap.StopReasonRemove) 2093 addNext(state.NewTaskSet(stopSnapServices)) 2094 prev = stopSnapServices 2095 } 2096 2097 // only run remove hook if uninstalling the snap completely 2098 if removeAll { 2099 removeHook := SetupRemoveHook(st, snapsup.InstanceName()) 2100 addNext(state.NewTaskSet(removeHook)) 2101 prev = removeHook 2102 2103 // run disconnect hooks 2104 disconnect := st.NewTask("auto-disconnect", fmt.Sprintf(i18n.G("Disconnect interfaces of snap %q"), snapsup.InstanceName())) 2105 disconnect.Set("snap-setup", snapsup) 2106 if prev != nil { 2107 disconnect.WaitFor(prev) 2108 } 2109 addNext(state.NewTaskSet(disconnect)) 2110 prev = disconnect 2111 } 2112 2113 // 'purge' flag disables automatic snapshot for given remove op 2114 if flags == nil || !flags.Purge { 2115 if tp, _ := snapst.Type(); tp == snap.TypeApp && removeAll { 2116 ts, err := AutomaticSnapshot(st, name) 2117 if err == nil { 2118 tr := config.NewTransaction(st) 2119 checkDiskSpaceRemove, err := features.Flag(tr, features.CheckDiskSpaceRemove) 2120 if err != nil && !config.IsNoOption(err) { 2121 return nil, 0, err 2122 } 2123 if checkDiskSpaceRemove { 2124 snapshotSize, err = EstimateSnapshotSize(st, name, nil) 2125 if err != nil { 2126 return nil, 0, err 2127 } 2128 } 2129 addNext(ts) 2130 } else { 2131 if err != ErrNothingToDo { 2132 return nil, 0, err 2133 } 2134 } 2135 } 2136 } 2137 2138 if active { // unlink 2139 var tasks []*state.Task 2140 2141 removeAliases := st.NewTask("remove-aliases", fmt.Sprintf(i18n.G("Remove aliases for snap %q"), name)) 2142 removeAliases.WaitFor(prev) // prev is not needed beyond here 2143 removeAliases.Set("snap-setup-task", stopSnapServices.ID()) 2144 2145 unlink := st.NewTask("unlink-snap", fmt.Sprintf(i18n.G("Make snap %q unavailable to the system"), name)) 2146 unlink.Set("snap-setup-task", stopSnapServices.ID()) 2147 unlink.WaitFor(removeAliases) 2148 2149 removeSecurity := st.NewTask("remove-profiles", fmt.Sprintf(i18n.G("Remove security profile for snap %q (%s)"), name, revision)) 2150 removeSecurity.WaitFor(unlink) 2151 removeSecurity.Set("snap-setup-task", stopSnapServices.ID()) 2152 2153 tasks = append(tasks, removeAliases, unlink, removeSecurity) 2154 addNext(state.NewTaskSet(tasks...)) 2155 } 2156 2157 if removeAll { 2158 seq := snapst.Sequence 2159 currentIndex := snapst.LastIndex(snapst.Current) 2160 for i := len(seq) - 1; i >= 0; i-- { 2161 if i != currentIndex { 2162 si := seq[i] 2163 addNext(removeInactiveRevision(st, name, info.SnapID, si.Revision)) 2164 } 2165 } 2166 // add tasks for removing the current revision last, 2167 // this is then also when common data will be removed 2168 if currentIndex >= 0 { 2169 addNext(removeInactiveRevision(st, name, info.SnapID, seq[currentIndex].Revision)) 2170 } 2171 } else { 2172 addNext(removeInactiveRevision(st, name, info.SnapID, revision)) 2173 } 2174 2175 return removeTs, snapshotSize, nil 2176 } 2177 2178 func removeInactiveRevision(st *state.State, name, snapID string, revision snap.Revision) *state.TaskSet { 2179 snapName, instanceKey := snap.SplitInstanceName(name) 2180 snapsup := SnapSetup{ 2181 SideInfo: &snap.SideInfo{ 2182 RealName: snapName, 2183 SnapID: snapID, 2184 Revision: revision, 2185 }, 2186 InstanceKey: instanceKey, 2187 } 2188 2189 clearData := st.NewTask("clear-snap", fmt.Sprintf(i18n.G("Remove data for snap %q (%s)"), name, revision)) 2190 clearData.Set("snap-setup", snapsup) 2191 2192 discardSnap := st.NewTask("discard-snap", fmt.Sprintf(i18n.G("Remove snap %q (%s) from the system"), name, revision)) 2193 discardSnap.WaitFor(clearData) 2194 discardSnap.Set("snap-setup-task", clearData.ID()) 2195 2196 return state.NewTaskSet(clearData, discardSnap) 2197 } 2198 2199 // RemoveMany removes everything from the given list of names. 2200 // Note that the state must be locked by the caller. 2201 func RemoveMany(st *state.State, names []string) ([]string, []*state.TaskSet, error) { 2202 removed := make([]string, 0, len(names)) 2203 tasksets := make([]*state.TaskSet, 0, len(names)) 2204 2205 var totalSnapshotsSize uint64 2206 path := dirs.SnapdStateDir(dirs.GlobalRootDir) 2207 2208 for _, name := range names { 2209 ts, snapshotSize, err := removeTasks(st, name, snap.R(0), nil) 2210 // FIXME: is this expected behavior? 2211 if _, ok := err.(*snap.NotInstalledError); ok { 2212 continue 2213 } 2214 if err != nil { 2215 return nil, nil, err 2216 } 2217 totalSnapshotsSize += snapshotSize 2218 removed = append(removed, name) 2219 ts.JoinLane(st.NewLane()) 2220 tasksets = append(tasksets, ts) 2221 } 2222 2223 // removeTasks() checks check-disk-space-remove feature flag, so totalSnapshotsSize 2224 // will only be greater than 0 if the feature is enabled. 2225 if totalSnapshotsSize > 0 { 2226 requiredSpace := safetyMarginDiskSpace(totalSnapshotsSize) 2227 if err := osutilCheckFreeSpace(path, requiredSpace); err != nil { 2228 if _, ok := err.(*osutil.NotEnoughDiskSpaceError); ok { 2229 return nil, nil, &InsufficientSpaceError{ 2230 Path: path, 2231 Snaps: names, 2232 ChangeKind: "remove", 2233 } 2234 } 2235 return nil, nil, err 2236 } 2237 } 2238 2239 return removed, tasksets, nil 2240 } 2241 2242 // Revert returns a set of tasks for reverting to the previous version of the snap. 2243 // Note that the state must be locked by the caller. 2244 func Revert(st *state.State, name string, flags Flags) (*state.TaskSet, error) { 2245 var snapst SnapState 2246 err := Get(st, name, &snapst) 2247 if err != nil && err != state.ErrNoState { 2248 return nil, err 2249 } 2250 2251 pi := snapst.previousSideInfo() 2252 if pi == nil { 2253 return nil, fmt.Errorf("no revision to revert to") 2254 } 2255 2256 return RevertToRevision(st, name, pi.Revision, flags) 2257 } 2258 2259 func RevertToRevision(st *state.State, name string, rev snap.Revision, flags Flags) (*state.TaskSet, error) { 2260 var snapst SnapState 2261 err := Get(st, name, &snapst) 2262 if err != nil && err != state.ErrNoState { 2263 return nil, err 2264 } 2265 2266 if snapst.Current == rev { 2267 return nil, fmt.Errorf("already on requested revision") 2268 } 2269 2270 if !snapst.Active { 2271 return nil, fmt.Errorf("cannot revert inactive snaps") 2272 } 2273 i := snapst.LastIndex(rev) 2274 if i < 0 { 2275 return nil, fmt.Errorf("cannot find revision %s for snap %q", rev, name) 2276 } 2277 2278 flags.Revert = true 2279 // TODO: make flags be per revision to avoid this logic (that 2280 // leaves corner cases all over the place) 2281 if !(flags.JailMode || flags.DevMode || flags.Classic) { 2282 if snapst.Flags.DevMode { 2283 flags.DevMode = true 2284 } 2285 if snapst.Flags.JailMode { 2286 flags.JailMode = true 2287 } 2288 if snapst.Flags.Classic { 2289 flags.Classic = true 2290 } 2291 } 2292 2293 info, err := Info(st, name, rev) 2294 if err != nil { 2295 return nil, err 2296 } 2297 2298 snapsup := &SnapSetup{ 2299 Base: info.Base, 2300 SideInfo: snapst.Sequence[i], 2301 Flags: flags.ForSnapSetup(), 2302 Type: info.Type(), 2303 PlugsOnly: len(info.Slots) == 0, 2304 InstanceKey: snapst.InstanceKey, 2305 } 2306 return doInstall(st, &snapst, snapsup, 0, "", nil) 2307 } 2308 2309 // TransitionCore transitions from an old core snap name to a new core 2310 // snap name. It is used for the ubuntu-core -> core transition (that 2311 // is not just a rename because the two snaps have different snapIDs) 2312 // 2313 // Note that this function makes some assumptions like: 2314 // - no aliases setup for both snaps 2315 // - no data needs to be copied 2316 // - all interfaces are absolutely identical on both new and old 2317 // Do not use this as a general way to transition from snap A to snap B. 2318 func TransitionCore(st *state.State, oldName, newName string) ([]*state.TaskSet, error) { 2319 var oldSnapst, newSnapst SnapState 2320 err := Get(st, oldName, &oldSnapst) 2321 if err != nil && err != state.ErrNoState { 2322 return nil, err 2323 } 2324 if !oldSnapst.IsInstalled() { 2325 return nil, fmt.Errorf("cannot transition snap %q: not installed", oldName) 2326 } 2327 2328 var all []*state.TaskSet 2329 // install new core (if not already installed) 2330 err = Get(st, newName, &newSnapst) 2331 if err != nil && err != state.ErrNoState { 2332 return nil, err 2333 } 2334 if !newSnapst.IsInstalled() { 2335 var userID int 2336 newInfo, err := installInfo(context.TODO(), st, newName, &RevisionOptions{Channel: oldSnapst.TrackingChannel}, userID, nil) 2337 if err != nil { 2338 return nil, err 2339 } 2340 2341 // start by installing the new snap 2342 tsInst, err := doInstall(st, &newSnapst, &SnapSetup{ 2343 Channel: oldSnapst.TrackingChannel, 2344 DownloadInfo: &newInfo.DownloadInfo, 2345 SideInfo: &newInfo.SideInfo, 2346 Type: newInfo.Type(), 2347 }, 0, "", nil) 2348 if err != nil { 2349 return nil, err 2350 } 2351 all = append(all, tsInst) 2352 } 2353 2354 // then transition the interface connections over 2355 transIf := st.NewTask("transition-ubuntu-core", fmt.Sprintf(i18n.G("Transition security profiles from %q to %q"), oldName, newName)) 2356 transIf.Set("old-name", oldName) 2357 transIf.Set("new-name", newName) 2358 if len(all) > 0 { 2359 transIf.WaitAll(all[0]) 2360 } 2361 tsTrans := state.NewTaskSet(transIf) 2362 all = append(all, tsTrans) 2363 2364 // FIXME: this is just here for the tests 2365 transIf.Set("snap-setup", &SnapSetup{ 2366 SideInfo: &snap.SideInfo{ 2367 RealName: oldName, 2368 }, 2369 }) 2370 2371 // then remove the old snap 2372 tsRm, err := Remove(st, oldName, snap.R(0), nil) 2373 if err != nil { 2374 return nil, err 2375 } 2376 tsRm.WaitFor(transIf) 2377 all = append(all, tsRm) 2378 2379 return all, nil 2380 } 2381 2382 // State/info accessors 2383 2384 // Installing returns whether there's an in-progress installation. 2385 func Installing(st *state.State) bool { 2386 for _, task := range st.Tasks() { 2387 k := task.Kind() 2388 chg := task.Change() 2389 if k == "mount-snap" && chg != nil && !chg.Status().Ready() { 2390 return true 2391 } 2392 } 2393 return false 2394 } 2395 2396 // Info returns the information about the snap with given name and revision. 2397 // Works also for a mounted candidate snap in the process of being installed. 2398 func Info(st *state.State, name string, revision snap.Revision) (*snap.Info, error) { 2399 var snapst SnapState 2400 err := Get(st, name, &snapst) 2401 if err == state.ErrNoState { 2402 return nil, &snap.NotInstalledError{Snap: name} 2403 } 2404 if err != nil { 2405 return nil, err 2406 } 2407 2408 for i := len(snapst.Sequence) - 1; i >= 0; i-- { 2409 if si := snapst.Sequence[i]; si.Revision == revision { 2410 return readInfo(name, si, 0) 2411 } 2412 } 2413 2414 return nil, fmt.Errorf("cannot find snap %q at revision %s", name, revision.String()) 2415 } 2416 2417 // CurrentInfo returns the information about the current revision of a snap with the given name. 2418 func CurrentInfo(st *state.State, name string) (*snap.Info, error) { 2419 var snapst SnapState 2420 err := Get(st, name, &snapst) 2421 if err != nil && err != state.ErrNoState { 2422 return nil, err 2423 } 2424 info, err := snapst.CurrentInfo() 2425 if err == ErrNoCurrent { 2426 return nil, &snap.NotInstalledError{Snap: name} 2427 } 2428 return info, err 2429 } 2430 2431 // Get retrieves the SnapState of the given snap. 2432 func Get(st *state.State, name string, snapst *SnapState) error { 2433 if snapst == nil { 2434 return fmt.Errorf("internal error: snapst is nil") 2435 } 2436 // SnapState is (un-)marshalled from/to JSON, fields having omitempty 2437 // tag will not appear in the output (if empty) and subsequently will 2438 // not be unmarshalled to (or cleared); if the caller reuses the same 2439 // struct though subsequent calls, it is possible that they end up with 2440 // garbage inside, clear the destination struct so that we always 2441 // unmarshal to a clean state 2442 *snapst = SnapState{} 2443 2444 var snaps map[string]*json.RawMessage 2445 err := st.Get("snaps", &snaps) 2446 if err != nil { 2447 return err 2448 } 2449 raw, ok := snaps[name] 2450 if !ok { 2451 return state.ErrNoState 2452 } 2453 2454 // XXX: &snapst pointer isn't needed here but it is likely historical 2455 // (a bug in old JSON marshaling probably). 2456 err = json.Unmarshal([]byte(*raw), &snapst) 2457 if err != nil { 2458 return fmt.Errorf("cannot unmarshal snap state: %v", err) 2459 } 2460 return nil 2461 } 2462 2463 // All retrieves return a map from name to SnapState for all current snaps in the system state. 2464 func All(st *state.State) (map[string]*SnapState, error) { 2465 // XXX: result is a map because sideloaded snaps carry no name 2466 // atm in their sideinfos 2467 var stateMap map[string]*SnapState 2468 if err := st.Get("snaps", &stateMap); err != nil && err != state.ErrNoState { 2469 return nil, err 2470 } 2471 curStates := make(map[string]*SnapState, len(stateMap)) 2472 for instanceName, snapst := range stateMap { 2473 curStates[instanceName] = snapst 2474 } 2475 return curStates, nil 2476 } 2477 2478 // NumSnaps returns the number of installed snaps. 2479 func NumSnaps(st *state.State) (int, error) { 2480 var snaps map[string]*json.RawMessage 2481 if err := st.Get("snaps", &snaps); err != nil && err != state.ErrNoState { 2482 return -1, err 2483 } 2484 return len(snaps), nil 2485 } 2486 2487 // Set sets the SnapState of the given snap, overwriting any earlier state. 2488 // Note that a SnapState with an empty Sequence will be treated as if snapst was 2489 // nil and name will be deleted from the state. 2490 func Set(st *state.State, name string, snapst *SnapState) { 2491 var snaps map[string]*json.RawMessage 2492 err := st.Get("snaps", &snaps) 2493 if err != nil && err != state.ErrNoState { 2494 panic("internal error: cannot unmarshal snaps state: " + err.Error()) 2495 } 2496 if snaps == nil { 2497 snaps = make(map[string]*json.RawMessage) 2498 } 2499 if snapst == nil || (len(snapst.Sequence) == 0) { 2500 delete(snaps, name) 2501 } else { 2502 data, err := json.Marshal(snapst) 2503 if err != nil { 2504 panic("internal error: cannot marshal snap state: " + err.Error()) 2505 } 2506 raw := json.RawMessage(data) 2507 snaps[name] = &raw 2508 } 2509 st.Set("snaps", snaps) 2510 } 2511 2512 // ActiveInfos returns information about all active snaps. 2513 func ActiveInfos(st *state.State) ([]*snap.Info, error) { 2514 var stateMap map[string]*SnapState 2515 var infos []*snap.Info 2516 if err := st.Get("snaps", &stateMap); err != nil && err != state.ErrNoState { 2517 return nil, err 2518 } 2519 for instanceName, snapst := range stateMap { 2520 if !snapst.Active { 2521 continue 2522 } 2523 snapInfo, err := snapst.CurrentInfo() 2524 if err != nil { 2525 logger.Noticef("cannot retrieve info for snap %q: %s", instanceName, err) 2526 continue 2527 } 2528 infos = append(infos, snapInfo) 2529 } 2530 return infos, nil 2531 } 2532 2533 func HasSnapOfType(st *state.State, snapType snap.Type) (bool, error) { 2534 var stateMap map[string]*SnapState 2535 if err := st.Get("snaps", &stateMap); err != nil && err != state.ErrNoState { 2536 return false, err 2537 } 2538 2539 for _, snapst := range stateMap { 2540 typ, err := snapst.Type() 2541 if err != nil { 2542 return false, err 2543 } 2544 if typ == snapType { 2545 return true, nil 2546 } 2547 } 2548 2549 return false, nil 2550 } 2551 2552 func infosForType(st *state.State, snapType snap.Type) ([]*snap.Info, error) { 2553 var stateMap map[string]*SnapState 2554 if err := st.Get("snaps", &stateMap); err != nil && err != state.ErrNoState { 2555 return nil, err 2556 } 2557 2558 var res []*snap.Info 2559 for _, snapst := range stateMap { 2560 if !snapst.IsInstalled() { 2561 continue 2562 } 2563 typ, err := snapst.Type() 2564 if err != nil { 2565 return nil, err 2566 } 2567 if typ != snapType { 2568 continue 2569 } 2570 si, err := snapst.CurrentInfo() 2571 if err != nil { 2572 return nil, err 2573 } 2574 res = append(res, si) 2575 } 2576 2577 if len(res) == 0 { 2578 return nil, state.ErrNoState 2579 } 2580 2581 return res, nil 2582 } 2583 2584 func infoForDeviceSnap(st *state.State, deviceCtx DeviceContext, which string, whichName func(*asserts.Model) string) (*snap.Info, error) { 2585 if deviceCtx == nil { 2586 return nil, fmt.Errorf("internal error: unset deviceCtx") 2587 } 2588 model := deviceCtx.Model() 2589 snapName := whichName(model) 2590 if snapName == "" { 2591 return nil, state.ErrNoState 2592 } 2593 var snapst SnapState 2594 err := Get(st, snapName, &snapst) 2595 if err != nil { 2596 return nil, err 2597 } 2598 return snapst.CurrentInfo() 2599 } 2600 2601 // GadgetInfo finds the gadget snap's info for the given device context. 2602 func GadgetInfo(st *state.State, deviceCtx DeviceContext) (*snap.Info, error) { 2603 return infoForDeviceSnap(st, deviceCtx, "gadget", (*asserts.Model).Gadget) 2604 } 2605 2606 // KernelInfo finds the kernel snap's info for the given device context. 2607 func KernelInfo(st *state.State, deviceCtx DeviceContext) (*snap.Info, error) { 2608 return infoForDeviceSnap(st, deviceCtx, "kernel", (*asserts.Model).Kernel) 2609 } 2610 2611 // BootBaseInfo finds the boot base snap's info for the given device context. 2612 func BootBaseInfo(st *state.State, deviceCtx DeviceContext) (*snap.Info, error) { 2613 baseName := func(mod *asserts.Model) string { 2614 base := mod.Base() 2615 if base == "" { 2616 return "core" 2617 } 2618 return base 2619 } 2620 return infoForDeviceSnap(st, deviceCtx, "boot base", baseName) 2621 } 2622 2623 // TODO: reintroduce a KernelInfo(state.State, DeviceContext) if needed 2624 // KernelInfo finds the current kernel snap's info. 2625 2626 // coreInfo finds the current OS snap's info. If both 2627 // "core" and "ubuntu-core" is installed then "core" 2628 // is preferred. Different core names are not supported 2629 // currently and will result in an error. 2630 func coreInfo(st *state.State) (*snap.Info, error) { 2631 res, err := infosForType(st, snap.TypeOS) 2632 if err != nil { 2633 return nil, err 2634 } 2635 2636 // a single core: just return it 2637 if len(res) == 1 { 2638 return res[0], nil 2639 } 2640 2641 // some systems have two cores: ubuntu-core/core 2642 // we always return "core" in this case 2643 if len(res) == 2 { 2644 if res[0].InstanceName() == defaultCoreSnapName && res[1].InstanceName() == "ubuntu-core" { 2645 return res[0], nil 2646 } 2647 if res[0].InstanceName() == "ubuntu-core" && res[1].InstanceName() == defaultCoreSnapName { 2648 return res[1], nil 2649 } 2650 return nil, fmt.Errorf("unexpected cores %q and %q", res[0].InstanceName(), res[1].InstanceName()) 2651 } 2652 2653 return nil, fmt.Errorf("unexpected number of cores, got %d", len(res)) 2654 } 2655 2656 // ConfigDefaults returns the configuration defaults for the snap as 2657 // specified in the gadget for the given device context. 2658 // If gadget is absent or the snap has no snap-id it returns 2659 // ErrNoState. 2660 func ConfigDefaults(st *state.State, deviceCtx DeviceContext, snapName string) (map[string]interface{}, error) { 2661 info, err := GadgetInfo(st, deviceCtx) 2662 if err != nil { 2663 return nil, err 2664 } 2665 2666 // system configuration is kept under "core" so apply its defaults when 2667 // configuring "core" 2668 isSystemDefaults := snapName == defaultCoreSnapName 2669 var snapst SnapState 2670 if err := Get(st, snapName, &snapst); err != nil && err != state.ErrNoState { 2671 return nil, err 2672 } 2673 2674 var snapID string 2675 if snapst.IsInstalled() { 2676 snapID = snapst.CurrentSideInfo().SnapID 2677 } 2678 // system snaps (core and snapd) snaps can be addressed even without a 2679 // snap-id via the special "system" value in the config; first-boot 2680 // always configures the core snap with UseConfigDefaults 2681 if snapID == "" && !isSystemDefaults { 2682 return nil, state.ErrNoState 2683 } 2684 2685 // no constraints enforced: those should have been checked before already 2686 gadgetInfo, err := gadget.ReadInfo(info.MountDir(), nil) 2687 if err != nil { 2688 return nil, err 2689 } 2690 2691 // we support setting core defaults via "system" 2692 if isSystemDefaults { 2693 if defaults, ok := gadgetInfo.Defaults["system"]; ok { 2694 if _, ok := gadgetInfo.Defaults[snapID]; ok && snapID != "" { 2695 logger.Noticef("core snap configuration defaults found under both 'system' key and core-snap-id, preferring 'system'") 2696 } 2697 2698 return defaults, nil 2699 } 2700 } 2701 2702 defaults, ok := gadgetInfo.Defaults[snapID] 2703 if !ok { 2704 return nil, state.ErrNoState 2705 } 2706 2707 return defaults, nil 2708 } 2709 2710 // GadgetConnections returns the interface connection instructions 2711 // specified in the gadget for the given device context. 2712 // If gadget is absent it returns ErrNoState. 2713 func GadgetConnections(st *state.State, deviceCtx DeviceContext) ([]gadget.Connection, error) { 2714 info, err := GadgetInfo(st, deviceCtx) 2715 if err != nil { 2716 return nil, err 2717 } 2718 2719 // no constraints enforced: those should have been checked before already 2720 gadgetInfo, err := gadget.ReadInfo(info.MountDir(), nil) 2721 if err != nil { 2722 return nil, err 2723 } 2724 2725 return gadgetInfo.Connections, nil 2726 } 2727 2728 func MockOsutilCheckFreeSpace(mock func(path string, minSize uint64) error) (restore func()) { 2729 old := osutilCheckFreeSpace 2730 osutilCheckFreeSpace = mock 2731 return func() { osutilCheckFreeSpace = old } 2732 }