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