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