github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/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) *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 var updates []*snap.Info 1927 1928 // check conflicts 1929 fromChange := "" 1930 for _, up := range candidates { 1931 if _, ok := hints[up.InstanceName()]; !ok { 1932 // filtered out by refreshHintsFromCandidates 1933 continue 1934 } 1935 snapst := snapstateByInstance[up.InstanceName()] 1936 if err := checkChangeConflictIgnoringOneChange(st, up.InstanceName(), snapst, fromChange); err != nil { 1937 logger.Noticef("cannot refresh snap %q: %v", up.InstanceName(), err) 1938 } else { 1939 updates = append(updates, up) 1940 } 1941 } 1942 1943 if len(updates) == 0 { 1944 return nil, nil, nil 1945 } 1946 1947 // all snaps in updates are now considered to be operated on and should 1948 // provoke conflicts until updated or until we know (after running 1949 // gate-auto-refresh hooks) that some are not going to be updated 1950 // and can stop conflicting. 1951 1952 affectedSnaps, err := affectedByRefresh(st, updates) 1953 if err != nil { 1954 return nil, nil, err 1955 } 1956 1957 // affectedByRefresh only considers snaps affected by updates, add 1958 // updates themselves as long as they have gate-auto-refresh hooks. 1959 for _, up := range updates { 1960 snapst := snapstateByInstance[up.InstanceName()] 1961 inf, err := snapst.CurrentInfo() 1962 if err != nil { 1963 return nil, nil, err 1964 } 1965 if affectedSnaps[up.InstanceName()] == nil && inf.Hooks[gateAutoRefreshHookName] != nil { 1966 affectedSnaps[up.InstanceName()] = &affectedSnapInfo{} 1967 } 1968 } 1969 1970 var hooks *state.TaskSet 1971 if len(affectedSnaps) > 0 { 1972 hooks = createGateAutoRefreshHooks(st, affectedSnaps) 1973 } 1974 1975 // gate-auto-refresh hooks, followed by conditional-auto-refresh task waiting 1976 // for all hooks. 1977 ar := st.NewTask("conditional-auto-refresh", "Run auto-refresh for ready snaps") 1978 tss := []*state.TaskSet{state.NewTaskSet(ar)} 1979 if hooks != nil { 1980 ar.WaitAll(hooks) 1981 tss = append(tss, hooks) 1982 } 1983 1984 // return all names as potentially getting updated even though some may be 1985 // held. 1986 names := make([]string, len(updates)) 1987 toUpdate := make(map[string]*refreshCandidate, len(updates)) 1988 for i, up := range updates { 1989 names[i] = up.InstanceName() 1990 toUpdate[up.InstanceName()] = hints[up.InstanceName()] 1991 } 1992 sort.Strings(names) 1993 1994 // store the list of snaps to update on the conditional-auto-refresh task 1995 // (this may be a subset of refresh-candidates due to conflicts). 1996 ar.Set("snaps", toUpdate) 1997 1998 return names, tss, nil 1999 } 2000 2001 // autoRefreshPhase2 creates tasks for refreshing snaps from updates. 2002 func autoRefreshPhase2(ctx context.Context, st *state.State, updates []*refreshCandidate) ([]*state.TaskSet, error) { 2003 fromChange := "" 2004 flags := &Flags{IsAutoRefresh: true} 2005 userID := 0 2006 2007 deviceCtx, err := DeviceCtx(st, nil, nil) 2008 if err != nil { 2009 return nil, err 2010 } 2011 2012 toUpdate := make([]minimalInstallInfo, len(updates)) 2013 for i, up := range updates { 2014 toUpdate[i] = up 2015 } 2016 2017 tr := config.NewTransaction(st) 2018 checkDiskSpaceRefresh, err := features.Flag(tr, features.CheckDiskSpaceRefresh) 2019 if err != nil && !config.IsNoOption(err) { 2020 return nil, err 2021 } 2022 if checkDiskSpaceRefresh { 2023 // check if there is enough disk space for requested snaps and their 2024 // prerequisites. 2025 totalSize, err := installSize(st, toUpdate, 0) 2026 if err != nil { 2027 return nil, err 2028 } 2029 requiredSpace := safetyMarginDiskSpace(totalSize) 2030 path := dirs.SnapdStateDir(dirs.GlobalRootDir) 2031 if err := osutilCheckFreeSpace(path, requiredSpace); err != nil { 2032 snaps := make([]string, len(updates)) 2033 for i, up := range updates { 2034 snaps[i] = up.InstanceName() 2035 } 2036 if _, ok := err.(*osutil.NotEnoughDiskSpaceError); ok { 2037 return nil, &InsufficientSpaceError{ 2038 Path: path, 2039 Snaps: snaps, 2040 ChangeKind: "refresh", 2041 } 2042 } 2043 return nil, err 2044 } 2045 } 2046 2047 _, tasksets, err := doUpdate(ctx, st, nil, toUpdate, nil, userID, flags, deviceCtx, fromChange) 2048 if err != nil { 2049 return nil, err 2050 } 2051 2052 tasksets = finalizeUpdate(st, tasksets, len(updates) > 0, nil, userID, flags) 2053 return tasksets, nil 2054 } 2055 2056 // LinkNewBaseOrKernel will create prepare/link-snap tasks for a remodel 2057 func LinkNewBaseOrKernel(st *state.State, name string) (*state.TaskSet, error) { 2058 var snapst SnapState 2059 err := Get(st, name, &snapst) 2060 if err == state.ErrNoState { 2061 return nil, &snap.NotInstalledError{Snap: name} 2062 } 2063 if err != nil { 2064 return nil, err 2065 } 2066 2067 if err := CheckChangeConflict(st, name, nil); err != nil { 2068 return nil, err 2069 } 2070 2071 info, err := snapst.CurrentInfo() 2072 if err != nil { 2073 return nil, err 2074 } 2075 2076 switch info.Type() { 2077 case snap.TypeOS, snap.TypeBase, snap.TypeKernel: 2078 // good 2079 default: 2080 // bad 2081 return nil, fmt.Errorf("cannot link type %v", info.Type()) 2082 } 2083 2084 snapsup := &SnapSetup{ 2085 SideInfo: snapst.CurrentSideInfo(), 2086 Flags: snapst.Flags.ForSnapSetup(), 2087 Type: info.Type(), 2088 PlugsOnly: len(info.Slots) == 0, 2089 InstanceKey: snapst.InstanceKey, 2090 } 2091 2092 prepareSnap := st.NewTask("prepare-snap", fmt.Sprintf(i18n.G("Prepare snap %q (%s) for remodel"), snapsup.InstanceName(), snapst.Current)) 2093 prepareSnap.Set("snap-setup", &snapsup) 2094 2095 linkSnap := st.NewTask("link-snap", fmt.Sprintf(i18n.G("Make snap %q (%s) available to the system during remodel"), snapsup.InstanceName(), snapst.Current)) 2096 linkSnap.Set("snap-setup-task", prepareSnap.ID()) 2097 linkSnap.WaitFor(prepareSnap) 2098 2099 // we need this for remodel 2100 ts := state.NewTaskSet(prepareSnap, linkSnap) 2101 ts.MarkEdge(prepareSnap, DownloadAndChecksDoneEdge) 2102 return ts, nil 2103 } 2104 2105 // Enable sets a snap to the active state 2106 func Enable(st *state.State, name string) (*state.TaskSet, error) { 2107 var snapst SnapState 2108 err := Get(st, name, &snapst) 2109 if err == state.ErrNoState { 2110 return nil, &snap.NotInstalledError{Snap: name} 2111 } 2112 if err != nil { 2113 return nil, err 2114 } 2115 2116 if snapst.Active { 2117 return nil, fmt.Errorf("snap %q already enabled", name) 2118 } 2119 2120 if err := CheckChangeConflict(st, name, nil); err != nil { 2121 return nil, err 2122 } 2123 2124 info, err := snapst.CurrentInfo() 2125 if err != nil { 2126 return nil, err 2127 } 2128 2129 snapsup := &SnapSetup{ 2130 SideInfo: snapst.CurrentSideInfo(), 2131 Flags: snapst.Flags.ForSnapSetup(), 2132 Type: info.Type(), 2133 PlugsOnly: len(info.Slots) == 0, 2134 InstanceKey: snapst.InstanceKey, 2135 } 2136 2137 prepareSnap := st.NewTask("prepare-snap", fmt.Sprintf(i18n.G("Prepare snap %q (%s)"), snapsup.InstanceName(), snapst.Current)) 2138 prepareSnap.Set("snap-setup", &snapsup) 2139 2140 setupProfiles := st.NewTask("setup-profiles", fmt.Sprintf(i18n.G("Setup snap %q (%s) security profiles"), snapsup.InstanceName(), snapst.Current)) 2141 setupProfiles.Set("snap-setup-task", prepareSnap.ID()) 2142 setupProfiles.WaitFor(prepareSnap) 2143 2144 linkSnap := st.NewTask("link-snap", fmt.Sprintf(i18n.G("Make snap %q (%s) available to the system"), snapsup.InstanceName(), snapst.Current)) 2145 linkSnap.Set("snap-setup-task", prepareSnap.ID()) 2146 linkSnap.WaitFor(setupProfiles) 2147 2148 // setup aliases 2149 setupAliases := st.NewTask("setup-aliases", fmt.Sprintf(i18n.G("Setup snap %q aliases"), snapsup.InstanceName())) 2150 setupAliases.Set("snap-setup-task", prepareSnap.ID()) 2151 setupAliases.WaitFor(linkSnap) 2152 2153 startSnapServices := st.NewTask("start-snap-services", fmt.Sprintf(i18n.G("Start snap %q (%s) services"), snapsup.InstanceName(), snapst.Current)) 2154 startSnapServices.Set("snap-setup-task", prepareSnap.ID()) 2155 startSnapServices.WaitFor(setupAliases) 2156 2157 return state.NewTaskSet(prepareSnap, setupProfiles, linkSnap, setupAliases, startSnapServices), nil 2158 } 2159 2160 // Disable sets a snap to the inactive state 2161 func Disable(st *state.State, name string) (*state.TaskSet, error) { 2162 var snapst SnapState 2163 err := Get(st, name, &snapst) 2164 if err == state.ErrNoState { 2165 return nil, &snap.NotInstalledError{Snap: name} 2166 } 2167 if err != nil { 2168 return nil, err 2169 } 2170 if !snapst.Active { 2171 return nil, fmt.Errorf("snap %q already disabled", name) 2172 } 2173 2174 info, err := Info(st, name, snapst.Current) 2175 if err != nil { 2176 return nil, err 2177 } 2178 if !canDisable(info) { 2179 return nil, fmt.Errorf("snap %q cannot be disabled", name) 2180 } 2181 2182 if err := CheckChangeConflict(st, name, nil); err != nil { 2183 return nil, err 2184 } 2185 2186 snapsup := &SnapSetup{ 2187 SideInfo: &snap.SideInfo{ 2188 RealName: snap.InstanceSnap(name), 2189 Revision: snapst.Current, 2190 }, 2191 Type: info.Type(), 2192 PlugsOnly: len(info.Slots) == 0, 2193 InstanceKey: snapst.InstanceKey, 2194 } 2195 2196 stopSnapServices := st.NewTask("stop-snap-services", fmt.Sprintf(i18n.G("Stop snap %q (%s) services"), snapsup.InstanceName(), snapst.Current)) 2197 stopSnapServices.Set("snap-setup", &snapsup) 2198 stopSnapServices.Set("stop-reason", snap.StopReasonDisable) 2199 2200 removeAliases := st.NewTask("remove-aliases", fmt.Sprintf(i18n.G("Remove aliases for snap %q"), snapsup.InstanceName())) 2201 removeAliases.Set("snap-setup-task", stopSnapServices.ID()) 2202 removeAliases.WaitFor(stopSnapServices) 2203 2204 unlinkSnap := st.NewTask("unlink-snap", fmt.Sprintf(i18n.G("Make snap %q (%s) unavailable to the system"), snapsup.InstanceName(), snapst.Current)) 2205 unlinkSnap.Set("snap-setup-task", stopSnapServices.ID()) 2206 unlinkSnap.WaitFor(removeAliases) 2207 2208 removeProfiles := st.NewTask("remove-profiles", fmt.Sprintf(i18n.G("Remove security profiles of snap %q"), snapsup.InstanceName())) 2209 removeProfiles.Set("snap-setup-task", stopSnapServices.ID()) 2210 removeProfiles.WaitFor(unlinkSnap) 2211 2212 return state.NewTaskSet(stopSnapServices, removeAliases, unlinkSnap, removeProfiles), nil 2213 } 2214 2215 // canDisable verifies that a snap can be deactivated. 2216 func canDisable(si *snap.Info) bool { 2217 for _, importantSnapType := range []snap.Type{snap.TypeGadget, snap.TypeKernel, snap.TypeOS} { 2218 if importantSnapType == si.Type() { 2219 return false 2220 } 2221 } 2222 2223 return true 2224 } 2225 2226 // canRemove verifies that a snap can be removed. 2227 func canRemove(st *state.State, si *snap.Info, snapst *SnapState, removeAll bool, deviceCtx DeviceContext) error { 2228 rev := snap.Revision{} 2229 if !removeAll { 2230 rev = si.Revision 2231 } 2232 2233 return PolicyFor(si.Type(), deviceCtx.Model()).CanRemove(st, snapst, rev, deviceCtx) 2234 } 2235 2236 // RemoveFlags are used to pass additional flags to the Remove operation. 2237 type RemoveFlags struct { 2238 // Remove the snap without creating snapshot data 2239 Purge bool 2240 } 2241 2242 // Remove returns a set of tasks for removing snap. 2243 // Note that the state must be locked by the caller. 2244 func Remove(st *state.State, name string, revision snap.Revision, flags *RemoveFlags) (*state.TaskSet, error) { 2245 ts, snapshotSize, err := removeTasks(st, name, revision, flags) 2246 // removeTasks() checks check-disk-space-remove feature flag, so snapshotSize 2247 // will only be greater than 0 if the feature is enabled. 2248 if snapshotSize > 0 { 2249 requiredSpace := safetyMarginDiskSpace(snapshotSize) 2250 path := dirs.SnapdStateDir(dirs.GlobalRootDir) 2251 if err := osutilCheckFreeSpace(path, requiredSpace); err != nil { 2252 if _, ok := err.(*osutil.NotEnoughDiskSpaceError); ok { 2253 return nil, &InsufficientSpaceError{ 2254 Path: path, 2255 Snaps: []string{name}, 2256 ChangeKind: "remove", 2257 Message: fmt.Sprintf("cannot create automatic snapshot when removing last revision of the snap: %v", err)} 2258 } 2259 return nil, err 2260 } 2261 } 2262 return ts, err 2263 } 2264 2265 // removeTasks provides the task set to remove snap name after taking a snapshot 2266 // if flags.Purge is not true, it also computes an estimate of the latter size. 2267 func removeTasks(st *state.State, name string, revision snap.Revision, flags *RemoveFlags) (removeTs *state.TaskSet, snapshotSize uint64, err error) { 2268 var snapst SnapState 2269 err = Get(st, name, &snapst) 2270 if err != nil && err != state.ErrNoState { 2271 return nil, 0, err 2272 } 2273 2274 if !snapst.IsInstalled() { 2275 return nil, 0, &snap.NotInstalledError{Snap: name, Rev: snap.R(0)} 2276 } 2277 2278 if err := CheckChangeConflict(st, name, nil); err != nil { 2279 return nil, 0, err 2280 } 2281 2282 deviceCtx, err := DeviceCtxFromState(st, nil) 2283 if err != nil { 2284 return nil, 0, err 2285 } 2286 2287 active := snapst.Active 2288 var removeAll bool 2289 if revision.Unset() { 2290 revision = snapst.Current 2291 removeAll = true 2292 } else { 2293 if active { 2294 if revision == snapst.Current { 2295 msg := "cannot remove active revision %s of snap %q" 2296 if len(snapst.Sequence) > 1 { 2297 msg += " (revert first?)" 2298 } 2299 return nil, 0, fmt.Errorf(msg, revision, name) 2300 } 2301 active = false 2302 } 2303 2304 if !revisionInSequence(&snapst, revision) { 2305 return nil, 0, &snap.NotInstalledError{Snap: name, Rev: revision} 2306 } 2307 2308 removeAll = len(snapst.Sequence) == 1 2309 } 2310 2311 info, err := Info(st, name, revision) 2312 if err != nil { 2313 return nil, 0, err 2314 } 2315 2316 // check if this is something that can be removed 2317 if err := canRemove(st, info, &snapst, removeAll, deviceCtx); err != nil { 2318 return nil, 0, fmt.Errorf("snap %q is not removable: %v", name, err) 2319 } 2320 2321 // main/current SnapSetup 2322 snapsup := SnapSetup{ 2323 SideInfo: &snap.SideInfo{ 2324 SnapID: info.SnapID, 2325 RealName: snap.InstanceSnap(name), 2326 Revision: revision, 2327 }, 2328 Type: info.Type(), 2329 PlugsOnly: len(info.Slots) == 0, 2330 InstanceKey: snapst.InstanceKey, 2331 } 2332 2333 // trigger remove 2334 2335 removeTs = state.NewTaskSet() 2336 var chain *state.TaskSet 2337 2338 addNext := func(ts *state.TaskSet) { 2339 if chain != nil { 2340 ts.WaitAll(chain) 2341 } 2342 removeTs.AddAll(ts) 2343 chain = ts 2344 } 2345 2346 var prev *state.Task 2347 var stopSnapServices *state.Task 2348 if active { 2349 stopSnapServices = st.NewTask("stop-snap-services", fmt.Sprintf(i18n.G("Stop snap %q services"), name)) 2350 stopSnapServices.Set("snap-setup", snapsup) 2351 stopSnapServices.Set("stop-reason", snap.StopReasonRemove) 2352 addNext(state.NewTaskSet(stopSnapServices)) 2353 prev = stopSnapServices 2354 } 2355 2356 // only run remove hook if uninstalling the snap completely 2357 if removeAll { 2358 removeHook := SetupRemoveHook(st, snapsup.InstanceName()) 2359 addNext(state.NewTaskSet(removeHook)) 2360 prev = removeHook 2361 2362 // run disconnect hooks 2363 disconnect := st.NewTask("auto-disconnect", fmt.Sprintf(i18n.G("Disconnect interfaces of snap %q"), snapsup.InstanceName())) 2364 disconnect.Set("snap-setup", snapsup) 2365 if prev != nil { 2366 disconnect.WaitFor(prev) 2367 } 2368 addNext(state.NewTaskSet(disconnect)) 2369 prev = disconnect 2370 } 2371 2372 // 'purge' flag disables automatic snapshot for given remove op 2373 if flags == nil || !flags.Purge { 2374 if tp, _ := snapst.Type(); tp == snap.TypeApp && removeAll { 2375 ts, err := AutomaticSnapshot(st, name) 2376 if err == nil { 2377 tr := config.NewTransaction(st) 2378 checkDiskSpaceRemove, err := features.Flag(tr, features.CheckDiskSpaceRemove) 2379 if err != nil && !config.IsNoOption(err) { 2380 return nil, 0, err 2381 } 2382 if checkDiskSpaceRemove { 2383 snapshotSize, err = EstimateSnapshotSize(st, name, nil) 2384 if err != nil { 2385 return nil, 0, err 2386 } 2387 } 2388 addNext(ts) 2389 } else { 2390 if err != ErrNothingToDo { 2391 return nil, 0, err 2392 } 2393 } 2394 } 2395 } 2396 2397 if active { // unlink 2398 var tasks []*state.Task 2399 2400 removeAliases := st.NewTask("remove-aliases", fmt.Sprintf(i18n.G("Remove aliases for snap %q"), name)) 2401 removeAliases.WaitFor(prev) // prev is not needed beyond here 2402 removeAliases.Set("snap-setup-task", stopSnapServices.ID()) 2403 2404 unlink := st.NewTask("unlink-snap", fmt.Sprintf(i18n.G("Make snap %q unavailable to the system"), name)) 2405 unlink.Set("snap-setup-task", stopSnapServices.ID()) 2406 unlink.WaitFor(removeAliases) 2407 2408 removeSecurity := st.NewTask("remove-profiles", fmt.Sprintf(i18n.G("Remove security profile for snap %q (%s)"), name, revision)) 2409 removeSecurity.WaitFor(unlink) 2410 removeSecurity.Set("snap-setup-task", stopSnapServices.ID()) 2411 2412 tasks = append(tasks, removeAliases, unlink, removeSecurity) 2413 addNext(state.NewTaskSet(tasks...)) 2414 } 2415 2416 if removeAll { 2417 seq := snapst.Sequence 2418 currentIndex := snapst.LastIndex(snapst.Current) 2419 for i := len(seq) - 1; i >= 0; i-- { 2420 if i != currentIndex { 2421 si := seq[i] 2422 addNext(removeInactiveRevision(st, name, info.SnapID, si.Revision, snapsup.Type)) 2423 } 2424 } 2425 // add tasks for removing the current revision last, 2426 // this is then also when common data will be removed 2427 if currentIndex >= 0 { 2428 addNext(removeInactiveRevision(st, name, info.SnapID, seq[currentIndex].Revision, snapsup.Type)) 2429 } 2430 } else { 2431 addNext(removeInactiveRevision(st, name, info.SnapID, revision, snapsup.Type)) 2432 } 2433 2434 return removeTs, snapshotSize, nil 2435 } 2436 2437 func removeInactiveRevision(st *state.State, name, snapID string, revision snap.Revision, typ snap.Type) *state.TaskSet { 2438 snapName, instanceKey := snap.SplitInstanceName(name) 2439 snapsup := SnapSetup{ 2440 SideInfo: &snap.SideInfo{ 2441 RealName: snapName, 2442 SnapID: snapID, 2443 Revision: revision, 2444 }, 2445 InstanceKey: instanceKey, 2446 Type: typ, 2447 } 2448 2449 clearData := st.NewTask("clear-snap", fmt.Sprintf(i18n.G("Remove data for snap %q (%s)"), name, revision)) 2450 clearData.Set("snap-setup", snapsup) 2451 2452 discardSnap := st.NewTask("discard-snap", fmt.Sprintf(i18n.G("Remove snap %q (%s) from the system"), name, revision)) 2453 discardSnap.WaitFor(clearData) 2454 discardSnap.Set("snap-setup-task", clearData.ID()) 2455 2456 return state.NewTaskSet(clearData, discardSnap) 2457 } 2458 2459 // RemoveMany removes everything from the given list of names. 2460 // Note that the state must be locked by the caller. 2461 func RemoveMany(st *state.State, names []string) ([]string, []*state.TaskSet, error) { 2462 removed := make([]string, 0, len(names)) 2463 tasksets := make([]*state.TaskSet, 0, len(names)) 2464 2465 var totalSnapshotsSize uint64 2466 path := dirs.SnapdStateDir(dirs.GlobalRootDir) 2467 2468 for _, name := range names { 2469 ts, snapshotSize, err := removeTasks(st, name, snap.R(0), nil) 2470 // FIXME: is this expected behavior? 2471 if _, ok := err.(*snap.NotInstalledError); ok { 2472 continue 2473 } 2474 if err != nil { 2475 return nil, nil, err 2476 } 2477 totalSnapshotsSize += snapshotSize 2478 removed = append(removed, name) 2479 ts.JoinLane(st.NewLane()) 2480 tasksets = append(tasksets, ts) 2481 } 2482 2483 // removeTasks() checks check-disk-space-remove feature flag, so totalSnapshotsSize 2484 // will only be greater than 0 if the feature is enabled. 2485 if totalSnapshotsSize > 0 { 2486 requiredSpace := safetyMarginDiskSpace(totalSnapshotsSize) 2487 if err := osutilCheckFreeSpace(path, requiredSpace); err != nil { 2488 if _, ok := err.(*osutil.NotEnoughDiskSpaceError); ok { 2489 return nil, nil, &InsufficientSpaceError{ 2490 Path: path, 2491 Snaps: names, 2492 ChangeKind: "remove", 2493 } 2494 } 2495 return nil, nil, err 2496 } 2497 } 2498 2499 return removed, tasksets, nil 2500 } 2501 2502 // Revert returns a set of tasks for reverting to the previous version of the snap. 2503 // Note that the state must be locked by the caller. 2504 func Revert(st *state.State, name string, flags Flags) (*state.TaskSet, error) { 2505 var snapst SnapState 2506 err := Get(st, name, &snapst) 2507 if err != nil && err != state.ErrNoState { 2508 return nil, err 2509 } 2510 2511 pi := snapst.previousSideInfo() 2512 if pi == nil { 2513 return nil, fmt.Errorf("no revision to revert to") 2514 } 2515 2516 return RevertToRevision(st, name, pi.Revision, flags) 2517 } 2518 2519 func RevertToRevision(st *state.State, name string, rev snap.Revision, flags Flags) (*state.TaskSet, error) { 2520 var snapst SnapState 2521 err := Get(st, name, &snapst) 2522 if err != nil && err != state.ErrNoState { 2523 return nil, err 2524 } 2525 2526 if snapst.Current == rev { 2527 return nil, fmt.Errorf("already on requested revision") 2528 } 2529 2530 if !snapst.Active { 2531 return nil, fmt.Errorf("cannot revert inactive snaps") 2532 } 2533 i := snapst.LastIndex(rev) 2534 if i < 0 { 2535 return nil, fmt.Errorf("cannot find revision %s for snap %q", rev, name) 2536 } 2537 2538 flags.Revert = true 2539 // TODO: make flags be per revision to avoid this logic (that 2540 // leaves corner cases all over the place) 2541 if !(flags.JailMode || flags.DevMode || flags.Classic) { 2542 if snapst.Flags.DevMode { 2543 flags.DevMode = true 2544 } 2545 if snapst.Flags.JailMode { 2546 flags.JailMode = true 2547 } 2548 if snapst.Flags.Classic { 2549 flags.Classic = true 2550 } 2551 } 2552 2553 info, err := Info(st, name, rev) 2554 if err != nil { 2555 return nil, err 2556 } 2557 2558 snapsup := &SnapSetup{ 2559 Base: info.Base, 2560 SideInfo: snapst.Sequence[i], 2561 Flags: flags.ForSnapSetup(), 2562 Type: info.Type(), 2563 PlugsOnly: len(info.Slots) == 0, 2564 InstanceKey: snapst.InstanceKey, 2565 } 2566 return doInstall(st, &snapst, snapsup, 0, "", nil) 2567 } 2568 2569 // TransitionCore transitions from an old core snap name to a new core 2570 // snap name. It is used for the ubuntu-core -> core transition (that 2571 // is not just a rename because the two snaps have different snapIDs) 2572 // 2573 // Note that this function makes some assumptions like: 2574 // - no aliases setup for both snaps 2575 // - no data needs to be copied 2576 // - all interfaces are absolutely identical on both new and old 2577 // Do not use this as a general way to transition from snap A to snap B. 2578 func TransitionCore(st *state.State, oldName, newName string) ([]*state.TaskSet, error) { 2579 var oldSnapst, newSnapst SnapState 2580 err := Get(st, oldName, &oldSnapst) 2581 if err != nil && err != state.ErrNoState { 2582 return nil, err 2583 } 2584 if !oldSnapst.IsInstalled() { 2585 return nil, fmt.Errorf("cannot transition snap %q: not installed", oldName) 2586 } 2587 2588 var all []*state.TaskSet 2589 // install new core (if not already installed) 2590 err = Get(st, newName, &newSnapst) 2591 if err != nil && err != state.ErrNoState { 2592 return nil, err 2593 } 2594 if !newSnapst.IsInstalled() { 2595 var userID int 2596 newInfo, err := installInfo(context.TODO(), st, newName, &RevisionOptions{Channel: oldSnapst.TrackingChannel}, userID, nil) 2597 if err != nil { 2598 return nil, err 2599 } 2600 2601 // start by installing the new snap 2602 tsInst, err := doInstall(st, &newSnapst, &SnapSetup{ 2603 Channel: oldSnapst.TrackingChannel, 2604 DownloadInfo: &newInfo.DownloadInfo, 2605 SideInfo: &newInfo.SideInfo, 2606 Type: newInfo.Type(), 2607 }, 0, "", nil) 2608 if err != nil { 2609 return nil, err 2610 } 2611 all = append(all, tsInst) 2612 } 2613 2614 // then transition the interface connections over 2615 transIf := st.NewTask("transition-ubuntu-core", fmt.Sprintf(i18n.G("Transition security profiles from %q to %q"), oldName, newName)) 2616 transIf.Set("old-name", oldName) 2617 transIf.Set("new-name", newName) 2618 if len(all) > 0 { 2619 transIf.WaitAll(all[0]) 2620 } 2621 tsTrans := state.NewTaskSet(transIf) 2622 all = append(all, tsTrans) 2623 2624 // FIXME: this is just here for the tests 2625 transIf.Set("snap-setup", &SnapSetup{ 2626 SideInfo: &snap.SideInfo{ 2627 RealName: oldName, 2628 }, 2629 }) 2630 2631 // then remove the old snap 2632 tsRm, err := Remove(st, oldName, snap.R(0), nil) 2633 if err != nil { 2634 return nil, err 2635 } 2636 tsRm.WaitFor(transIf) 2637 all = append(all, tsRm) 2638 2639 return all, nil 2640 } 2641 2642 // State/info accessors 2643 2644 // Installing returns whether there's an in-progress installation. 2645 func Installing(st *state.State) bool { 2646 for _, task := range st.Tasks() { 2647 k := task.Kind() 2648 chg := task.Change() 2649 if k == "mount-snap" && chg != nil && !chg.Status().Ready() { 2650 return true 2651 } 2652 } 2653 return false 2654 } 2655 2656 // Info returns the information about the snap with given name and revision. 2657 // Works also for a mounted candidate snap in the process of being installed. 2658 func Info(st *state.State, name string, revision snap.Revision) (*snap.Info, error) { 2659 var snapst SnapState 2660 err := Get(st, name, &snapst) 2661 if err == state.ErrNoState { 2662 return nil, &snap.NotInstalledError{Snap: name} 2663 } 2664 if err != nil { 2665 return nil, err 2666 } 2667 2668 for i := len(snapst.Sequence) - 1; i >= 0; i-- { 2669 if si := snapst.Sequence[i]; si.Revision == revision { 2670 return readInfo(name, si, 0) 2671 } 2672 } 2673 2674 return nil, fmt.Errorf("cannot find snap %q at revision %s", name, revision.String()) 2675 } 2676 2677 // CurrentInfo returns the information about the current revision of a snap with the given name. 2678 func CurrentInfo(st *state.State, name string) (*snap.Info, error) { 2679 var snapst SnapState 2680 err := Get(st, name, &snapst) 2681 if err != nil && err != state.ErrNoState { 2682 return nil, err 2683 } 2684 info, err := snapst.CurrentInfo() 2685 if err == ErrNoCurrent { 2686 return nil, &snap.NotInstalledError{Snap: name} 2687 } 2688 return info, err 2689 } 2690 2691 // Get retrieves the SnapState of the given snap. 2692 func Get(st *state.State, name string, snapst *SnapState) error { 2693 if snapst == nil { 2694 return fmt.Errorf("internal error: snapst is nil") 2695 } 2696 // SnapState is (un-)marshalled from/to JSON, fields having omitempty 2697 // tag will not appear in the output (if empty) and subsequently will 2698 // not be unmarshalled to (or cleared); if the caller reuses the same 2699 // struct though subsequent calls, it is possible that they end up with 2700 // garbage inside, clear the destination struct so that we always 2701 // unmarshal to a clean state 2702 *snapst = SnapState{} 2703 2704 var snaps map[string]*json.RawMessage 2705 err := st.Get("snaps", &snaps) 2706 if err != nil { 2707 return err 2708 } 2709 raw, ok := snaps[name] 2710 if !ok { 2711 return state.ErrNoState 2712 } 2713 2714 // XXX: &snapst pointer isn't needed here but it is likely historical 2715 // (a bug in old JSON marshaling probably). 2716 err = json.Unmarshal([]byte(*raw), &snapst) 2717 if err != nil { 2718 return fmt.Errorf("cannot unmarshal snap state: %v", err) 2719 } 2720 return nil 2721 } 2722 2723 // All retrieves return a map from name to SnapState for all current snaps in the system state. 2724 func All(st *state.State) (map[string]*SnapState, error) { 2725 // XXX: result is a map because sideloaded snaps carry no name 2726 // atm in their sideinfos 2727 var stateMap map[string]*SnapState 2728 if err := st.Get("snaps", &stateMap); err != nil && err != state.ErrNoState { 2729 return nil, err 2730 } 2731 curStates := make(map[string]*SnapState, len(stateMap)) 2732 for instanceName, snapst := range stateMap { 2733 curStates[instanceName] = snapst 2734 } 2735 return curStates, nil 2736 } 2737 2738 // NumSnaps returns the number of installed snaps. 2739 func NumSnaps(st *state.State) (int, error) { 2740 var snaps map[string]*json.RawMessage 2741 if err := st.Get("snaps", &snaps); err != nil && err != state.ErrNoState { 2742 return -1, err 2743 } 2744 return len(snaps), nil 2745 } 2746 2747 // Set sets the SnapState of the given snap, overwriting any earlier state. 2748 // Note that a SnapState with an empty Sequence will be treated as if snapst was 2749 // nil and name will be deleted from the state. 2750 func Set(st *state.State, name string, snapst *SnapState) { 2751 var snaps map[string]*json.RawMessage 2752 err := st.Get("snaps", &snaps) 2753 if err != nil && err != state.ErrNoState { 2754 panic("internal error: cannot unmarshal snaps state: " + err.Error()) 2755 } 2756 if snaps == nil { 2757 snaps = make(map[string]*json.RawMessage) 2758 } 2759 if snapst == nil || (len(snapst.Sequence) == 0) { 2760 delete(snaps, name) 2761 } else { 2762 data, err := json.Marshal(snapst) 2763 if err != nil { 2764 panic("internal error: cannot marshal snap state: " + err.Error()) 2765 } 2766 raw := json.RawMessage(data) 2767 snaps[name] = &raw 2768 } 2769 st.Set("snaps", snaps) 2770 } 2771 2772 // ActiveInfos returns information about all active snaps. 2773 func ActiveInfos(st *state.State) ([]*snap.Info, error) { 2774 var stateMap map[string]*SnapState 2775 var infos []*snap.Info 2776 if err := st.Get("snaps", &stateMap); err != nil && err != state.ErrNoState { 2777 return nil, err 2778 } 2779 for instanceName, snapst := range stateMap { 2780 if !snapst.Active { 2781 continue 2782 } 2783 snapInfo, err := snapst.CurrentInfo() 2784 if err != nil { 2785 logger.Noticef("cannot retrieve info for snap %q: %s", instanceName, err) 2786 continue 2787 } 2788 infos = append(infos, snapInfo) 2789 } 2790 return infos, nil 2791 } 2792 2793 func HasSnapOfType(st *state.State, snapType snap.Type) (bool, error) { 2794 var stateMap map[string]*SnapState 2795 if err := st.Get("snaps", &stateMap); err != nil && err != state.ErrNoState { 2796 return false, err 2797 } 2798 2799 for _, snapst := range stateMap { 2800 typ, err := snapst.Type() 2801 if err != nil { 2802 return false, err 2803 } 2804 if typ == snapType { 2805 return true, nil 2806 } 2807 } 2808 2809 return false, nil 2810 } 2811 2812 func infosForType(st *state.State, snapType snap.Type) ([]*snap.Info, error) { 2813 var stateMap map[string]*SnapState 2814 if err := st.Get("snaps", &stateMap); err != nil && err != state.ErrNoState { 2815 return nil, err 2816 } 2817 2818 var res []*snap.Info 2819 for _, snapst := range stateMap { 2820 if !snapst.IsInstalled() { 2821 continue 2822 } 2823 typ, err := snapst.Type() 2824 if err != nil { 2825 return nil, err 2826 } 2827 if typ != snapType { 2828 continue 2829 } 2830 si, err := snapst.CurrentInfo() 2831 if err != nil { 2832 return nil, err 2833 } 2834 res = append(res, si) 2835 } 2836 2837 if len(res) == 0 { 2838 return nil, state.ErrNoState 2839 } 2840 2841 return res, nil 2842 } 2843 2844 func infoForDeviceSnap(st *state.State, deviceCtx DeviceContext, which string, whichName func(*asserts.Model) string) (*snap.Info, error) { 2845 if deviceCtx == nil { 2846 return nil, fmt.Errorf("internal error: unset deviceCtx") 2847 } 2848 model := deviceCtx.Model() 2849 snapName := whichName(model) 2850 if snapName == "" { 2851 return nil, state.ErrNoState 2852 } 2853 var snapst SnapState 2854 err := Get(st, snapName, &snapst) 2855 if err != nil { 2856 return nil, err 2857 } 2858 return snapst.CurrentInfo() 2859 } 2860 2861 // GadgetInfo finds the gadget snap's info for the given device context. 2862 func GadgetInfo(st *state.State, deviceCtx DeviceContext) (*snap.Info, error) { 2863 return infoForDeviceSnap(st, deviceCtx, "gadget", (*asserts.Model).Gadget) 2864 } 2865 2866 // KernelInfo finds the kernel snap's info for the given device context. 2867 func KernelInfo(st *state.State, deviceCtx DeviceContext) (*snap.Info, error) { 2868 return infoForDeviceSnap(st, deviceCtx, "kernel", (*asserts.Model).Kernel) 2869 } 2870 2871 // BootBaseInfo finds the boot base snap's info for the given device context. 2872 func BootBaseInfo(st *state.State, deviceCtx DeviceContext) (*snap.Info, error) { 2873 baseName := func(mod *asserts.Model) string { 2874 base := mod.Base() 2875 if base == "" { 2876 return "core" 2877 } 2878 return base 2879 } 2880 return infoForDeviceSnap(st, deviceCtx, "boot base", baseName) 2881 } 2882 2883 // TODO: reintroduce a KernelInfo(state.State, DeviceContext) if needed 2884 // KernelInfo finds the current kernel snap's info. 2885 2886 // coreInfo finds the current OS snap's info. If both 2887 // "core" and "ubuntu-core" is installed then "core" 2888 // is preferred. Different core names are not supported 2889 // currently and will result in an error. 2890 func coreInfo(st *state.State) (*snap.Info, error) { 2891 res, err := infosForType(st, snap.TypeOS) 2892 if err != nil { 2893 return nil, err 2894 } 2895 2896 // a single core: just return it 2897 if len(res) == 1 { 2898 return res[0], nil 2899 } 2900 2901 // some systems have two cores: ubuntu-core/core 2902 // we always return "core" in this case 2903 if len(res) == 2 { 2904 if res[0].InstanceName() == defaultCoreSnapName && res[1].InstanceName() == "ubuntu-core" { 2905 return res[0], nil 2906 } 2907 if res[0].InstanceName() == "ubuntu-core" && res[1].InstanceName() == defaultCoreSnapName { 2908 return res[1], nil 2909 } 2910 return nil, fmt.Errorf("unexpected cores %q and %q", res[0].InstanceName(), res[1].InstanceName()) 2911 } 2912 2913 return nil, fmt.Errorf("unexpected number of cores, got %d", len(res)) 2914 } 2915 2916 // ConfigDefaults returns the configuration defaults for the snap as 2917 // specified in the gadget for the given device context. 2918 // If gadget is absent or the snap has no snap-id it returns 2919 // ErrNoState. 2920 func ConfigDefaults(st *state.State, deviceCtx DeviceContext, snapName string) (map[string]interface{}, error) { 2921 info, err := GadgetInfo(st, deviceCtx) 2922 if err != nil { 2923 return nil, err 2924 } 2925 2926 // system configuration is kept under "core" so apply its defaults when 2927 // configuring "core" 2928 isSystemDefaults := snapName == defaultCoreSnapName 2929 var snapst SnapState 2930 if err := Get(st, snapName, &snapst); err != nil && err != state.ErrNoState { 2931 return nil, err 2932 } 2933 2934 var snapID string 2935 if snapst.IsInstalled() { 2936 snapID = snapst.CurrentSideInfo().SnapID 2937 } 2938 // system snaps (core and snapd) snaps can be addressed even without a 2939 // snap-id via the special "system" value in the config; first-boot 2940 // always configures the core snap with UseConfigDefaults 2941 if snapID == "" && !isSystemDefaults { 2942 return nil, state.ErrNoState 2943 } 2944 2945 // no constraints enforced: those should have been checked before already 2946 gadgetInfo, err := gadget.ReadInfo(info.MountDir(), nil) 2947 if err != nil { 2948 return nil, err 2949 } 2950 2951 // we support setting core defaults via "system" 2952 if isSystemDefaults { 2953 if defaults, ok := gadgetInfo.Defaults["system"]; ok { 2954 if _, ok := gadgetInfo.Defaults[snapID]; ok && snapID != "" { 2955 logger.Noticef("core snap configuration defaults found under both 'system' key and core-snap-id, preferring 'system'") 2956 } 2957 2958 return defaults, nil 2959 } 2960 } 2961 2962 defaults, ok := gadgetInfo.Defaults[snapID] 2963 if !ok { 2964 return nil, state.ErrNoState 2965 } 2966 2967 return defaults, nil 2968 } 2969 2970 // GadgetConnections returns the interface connection instructions 2971 // specified in the gadget for the given device context. 2972 // If gadget is absent it returns ErrNoState. 2973 func GadgetConnections(st *state.State, deviceCtx DeviceContext) ([]gadget.Connection, error) { 2974 info, err := GadgetInfo(st, deviceCtx) 2975 if err != nil { 2976 return nil, err 2977 } 2978 2979 // no constraints enforced: those should have been checked before already 2980 gadgetInfo, err := gadget.ReadInfo(info.MountDir(), nil) 2981 if err != nil { 2982 return nil, err 2983 } 2984 2985 return gadgetInfo.Connections, nil 2986 } 2987 2988 func MockOsutilCheckFreeSpace(mock func(path string, minSize uint64) error) (restore func()) { 2989 old := osutilCheckFreeSpace 2990 osutilCheckFreeSpace = mock 2991 return func() { osutilCheckFreeSpace = old } 2992 }