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