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