github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/snapstate/snapstate.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2018 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/overlord/auth" 42 "github.com/snapcore/snapd/overlord/configstate/config" 43 "github.com/snapcore/snapd/overlord/ifacestate/ifacerepo" 44 "github.com/snapcore/snapd/overlord/snapstate/backend" 45 "github.com/snapcore/snapd/overlord/state" 46 "github.com/snapcore/snapd/release" 47 "github.com/snapcore/snapd/snap" 48 "github.com/snapcore/snapd/snap/channel" 49 "github.com/snapcore/snapd/store" 50 "github.com/snapcore/snapd/strutil" 51 ) 52 53 // control flags for doInstall 54 const ( 55 skipConfigure = 1 << iota 56 ) 57 58 // control flags for "Configure()" 59 const ( 60 IgnoreHookError = 1 << iota 61 TrackHookError 62 UseConfigDefaults 63 ) 64 65 const ( 66 DownloadAndChecksDoneEdge = state.TaskSetEdge("download-and-checks-done") 67 ) 68 69 var ErrNothingToDo = errors.New("nothing to do") 70 71 func isParallelInstallable(snapsup *SnapSetup) error { 72 if snapsup.InstanceKey == "" { 73 return nil 74 } 75 if snapsup.Type == snap.TypeApp { 76 return nil 77 } 78 return fmt.Errorf("cannot install snap of type %v as %q", snapsup.Type, snapsup.InstanceName()) 79 } 80 81 func optedIntoSnapdSnap(st *state.State) (bool, error) { 82 tr := config.NewTransaction(st) 83 experimentalAllowSnapd, err := config.GetFeatureFlag(tr, features.SnapdSnap) 84 if err != nil && !config.IsNoOption(err) { 85 return false, err 86 } 87 return experimentalAllowSnapd, nil 88 } 89 90 func doInstall(st *state.State, snapst *SnapState, snapsup *SnapSetup, flags int, fromChange string) (*state.TaskSet, error) { 91 // NB: we should strive not to need or propagate deviceCtx 92 // here, the resulting effects/changes were not pleasant at 93 // one point 94 tr := config.NewTransaction(st) 95 experimentalRefreshAppAwareness, err := config.GetFeatureFlag(tr, features.RefreshAppAwareness) 96 if err != nil && !config.IsNoOption(err) { 97 return nil, err 98 } 99 100 if snapsup.InstanceName() == "system" { 101 return nil, fmt.Errorf("cannot install reserved snap name 'system'") 102 } 103 if snapst.IsInstalled() && !snapst.Active { 104 return nil, fmt.Errorf("cannot update disabled snap %q", snapsup.InstanceName()) 105 } 106 107 if snapsup.Flags.Classic { 108 if !release.OnClassic { 109 return nil, fmt.Errorf("classic confinement is only supported on classic systems") 110 } else if !dirs.SupportsClassicConfinement() { 111 return nil, fmt.Errorf(i18n.G("classic confinement requires snaps under /snap or symlink from /snap to %s"), dirs.SnapMountDir) 112 } 113 } 114 if !snapst.IsInstalled() { // install? 115 // check that the snap command namespace doesn't conflict with an enabled alias 116 if err := checkSnapAliasConflict(st, snapsup.InstanceName()); err != nil { 117 return nil, err 118 } 119 } 120 121 if err := isParallelInstallable(snapsup); err != nil { 122 return nil, err 123 } 124 125 if err := checkChangeConflictIgnoringOneChange(st, snapsup.InstanceName(), snapst, fromChange); err != nil { 126 return nil, err 127 } 128 129 if snapst.IsInstalled() { 130 // consider also the current revision to set plugs-only hint 131 info, err := snapst.CurrentInfo() 132 if err != nil { 133 return nil, err 134 } 135 snapsup.PlugsOnly = snapsup.PlugsOnly && (len(info.Slots) == 0) 136 137 if experimentalRefreshAppAwareness { 138 // Note that because we are modifying the snap state this block 139 // must be located after the conflict check done above. 140 if err := inhibitRefresh(st, snapst, info, SoftNothingRunningRefreshCheck); err != nil { 141 return nil, err 142 } 143 } 144 } 145 146 ts := state.NewTaskSet() 147 148 targetRevision := snapsup.Revision() 149 revisionStr := "" 150 if snapsup.SideInfo != nil { 151 revisionStr = fmt.Sprintf(" (%s)", targetRevision) 152 } 153 154 // check if we already have the revision locally (alters tasks) 155 revisionIsLocal := snapst.LastIndex(targetRevision) >= 0 156 157 prereq := st.NewTask("prerequisites", fmt.Sprintf(i18n.G("Ensure prerequisites for %q are available"), snapsup.InstanceName())) 158 prereq.Set("snap-setup", snapsup) 159 160 var prepare, prev *state.Task 161 fromStore := false 162 // if we have a local revision here we go back to that 163 if snapsup.SnapPath != "" || revisionIsLocal { 164 prepare = st.NewTask("prepare-snap", fmt.Sprintf(i18n.G("Prepare snap %q%s"), snapsup.SnapPath, revisionStr)) 165 } else { 166 fromStore = true 167 prepare = st.NewTask("download-snap", fmt.Sprintf(i18n.G("Download snap %q%s from channel %q"), snapsup.InstanceName(), revisionStr, snapsup.Channel)) 168 } 169 prepare.Set("snap-setup", snapsup) 170 prepare.WaitFor(prereq) 171 172 tasks := []*state.Task{prereq, prepare} 173 addTask := func(t *state.Task) { 174 t.Set("snap-setup-task", prepare.ID()) 175 t.WaitFor(prev) 176 tasks = append(tasks, t) 177 } 178 prev = prepare 179 180 var checkAsserts *state.Task 181 if fromStore { 182 // fetch and check assertions 183 checkAsserts = st.NewTask("validate-snap", fmt.Sprintf(i18n.G("Fetch and check assertions for snap %q%s"), snapsup.InstanceName(), revisionStr)) 184 addTask(checkAsserts) 185 prev = checkAsserts 186 } 187 188 // mount 189 if !revisionIsLocal { 190 mount := st.NewTask("mount-snap", fmt.Sprintf(i18n.G("Mount snap %q%s"), snapsup.InstanceName(), revisionStr)) 191 addTask(mount) 192 prev = mount 193 } 194 195 // run refresh hooks when updating existing snap, otherwise run install hook further down. 196 runRefreshHooks := (snapst.IsInstalled() && !snapsup.Flags.Revert) 197 if runRefreshHooks { 198 preRefreshHook := SetupPreRefreshHook(st, snapsup.InstanceName()) 199 addTask(preRefreshHook) 200 prev = preRefreshHook 201 } 202 203 if snapst.IsInstalled() { 204 // unlink-current-snap (will stop services for copy-data) 205 stop := st.NewTask("stop-snap-services", fmt.Sprintf(i18n.G("Stop snap %q services"), snapsup.InstanceName())) 206 stop.Set("stop-reason", snap.StopReasonRefresh) 207 addTask(stop) 208 prev = stop 209 210 removeAliases := st.NewTask("remove-aliases", fmt.Sprintf(i18n.G("Remove aliases for snap %q"), snapsup.InstanceName())) 211 addTask(removeAliases) 212 prev = removeAliases 213 214 unlink := st.NewTask("unlink-current-snap", fmt.Sprintf(i18n.G("Make current revision for snap %q unavailable"), snapsup.InstanceName())) 215 addTask(unlink) 216 prev = unlink 217 } 218 219 if !release.OnClassic && snapsup.Type == snap.TypeGadget { 220 // XXX: gadget update currently for core systems only 221 gadgetUpdate := st.NewTask("update-gadget-assets", fmt.Sprintf(i18n.G("Update assets from gadget %q%s"), snapsup.InstanceName(), revisionStr)) 222 addTask(gadgetUpdate) 223 prev = gadgetUpdate 224 } 225 226 // copy-data (needs stopped services by unlink) 227 if !snapsup.Flags.Revert { 228 copyData := st.NewTask("copy-snap-data", fmt.Sprintf(i18n.G("Copy snap %q data"), snapsup.InstanceName())) 229 addTask(copyData) 230 prev = copyData 231 } 232 233 // security 234 setupSecurity := st.NewTask("setup-profiles", fmt.Sprintf(i18n.G("Setup snap %q%s security profiles"), snapsup.InstanceName(), revisionStr)) 235 addTask(setupSecurity) 236 prev = setupSecurity 237 238 // finalize (wrappers+current symlink) 239 linkSnap := st.NewTask("link-snap", fmt.Sprintf(i18n.G("Make snap %q%s available to the system"), snapsup.InstanceName(), revisionStr)) 240 addTask(linkSnap) 241 prev = linkSnap 242 243 // auto-connections 244 autoConnect := st.NewTask("auto-connect", fmt.Sprintf(i18n.G("Automatically connect eligible plugs and slots of snap %q"), snapsup.InstanceName())) 245 addTask(autoConnect) 246 prev = autoConnect 247 248 // setup aliases 249 setAutoAliases := st.NewTask("set-auto-aliases", fmt.Sprintf(i18n.G("Set automatic aliases for snap %q"), snapsup.InstanceName())) 250 addTask(setAutoAliases) 251 prev = setAutoAliases 252 253 setupAliases := st.NewTask("setup-aliases", fmt.Sprintf(i18n.G("Setup snap %q aliases"), snapsup.InstanceName())) 254 addTask(setupAliases) 255 prev = setupAliases 256 257 if runRefreshHooks { 258 postRefreshHook := SetupPostRefreshHook(st, snapsup.InstanceName()) 259 addTask(postRefreshHook) 260 prev = postRefreshHook 261 } 262 263 // only run install hook if installing the snap for the first time 264 if !snapst.IsInstalled() { 265 installHook := SetupInstallHook(st, snapsup.InstanceName()) 266 addTask(installHook) 267 prev = installHook 268 } 269 270 // run new services 271 startSnapServices := st.NewTask("start-snap-services", fmt.Sprintf(i18n.G("Start snap %q%s services"), snapsup.InstanceName(), revisionStr)) 272 addTask(startSnapServices) 273 prev = startSnapServices 274 275 // Do not do that if we are reverting to a local revision 276 if snapst.IsInstalled() && !snapsup.Flags.Revert { 277 var retain int 278 if err := config.NewTransaction(st).Get("core", "refresh.retain", &retain); err != nil { 279 // on classic we only keep 2 copies by default 280 if release.OnClassic { 281 retain = 2 282 } else { 283 retain = 3 284 } 285 } 286 retain-- // we're adding one 287 288 seq := snapst.Sequence 289 currentIndex := snapst.LastIndex(snapst.Current) 290 291 // discard everything after "current" (we may have reverted to 292 // a previous versions earlier) 293 for i := currentIndex + 1; i < len(seq); i++ { 294 si := seq[i] 295 if si.Revision == targetRevision { 296 // but don't discard this one; its' the thing we're switching to! 297 continue 298 } 299 ts := removeInactiveRevision(st, snapsup.InstanceName(), si.SnapID, si.Revision) 300 ts.WaitFor(prev) 301 tasks = append(tasks, ts.Tasks()...) 302 prev = tasks[len(tasks)-1] 303 } 304 305 // make sure we're not scheduling the removal of the target 306 // revision in the case where the target revision is already in 307 // the sequence. 308 for i := 0; i < currentIndex; i++ { 309 si := seq[i] 310 if si.Revision == targetRevision { 311 // we do *not* want to removeInactiveRevision of this one 312 copy(seq[i:], seq[i+1:]) 313 seq = seq[:len(seq)-1] 314 currentIndex-- 315 } 316 } 317 318 // normal garbage collect 319 for i := 0; i <= currentIndex-retain; i++ { 320 si := seq[i] 321 if boot.InUse(snapsup.InstanceName(), si.Revision) { 322 continue 323 } 324 ts := removeInactiveRevision(st, snapsup.InstanceName(), si.SnapID, si.Revision) 325 ts.WaitFor(prev) 326 tasks = append(tasks, ts.Tasks()...) 327 prev = tasks[len(tasks)-1] 328 } 329 330 addTask(st.NewTask("cleanup", fmt.Sprintf("Clean up %q%s install", snapsup.InstanceName(), revisionStr))) 331 } 332 333 installSet := state.NewTaskSet(tasks...) 334 installSet.WaitAll(ts) 335 ts.AddAll(installSet) 336 if checkAsserts != nil { 337 ts.MarkEdge(checkAsserts, DownloadAndChecksDoneEdge) 338 } 339 340 if flags&skipConfigure != 0 { 341 return installSet, nil 342 } 343 344 // we do not support configuration for bases or the "snapd" snap yet 345 if snapsup.Type != snap.TypeBase && snapsup.Type != snap.TypeSnapd { 346 confFlags := 0 347 notCore := snapsup.InstanceName() != "core" 348 hasSnapID := snapsup.SideInfo != nil && snapsup.SideInfo.SnapID != "" 349 if !snapst.IsInstalled() && hasSnapID && notCore { 350 // installation, run configure using the gadget defaults 351 // if available, system config defaults (attached to 352 // "core") are consumed only during seeding, via an 353 // explicit configure step separate from installing 354 confFlags |= UseConfigDefaults 355 } 356 configSet := ConfigureSnap(st, snapsup.InstanceName(), confFlags) 357 configSet.WaitAll(ts) 358 ts.AddAll(configSet) 359 } 360 361 healthCheck := CheckHealthHook(st, snapsup.InstanceName(), snapsup.Revision()) 362 healthCheck.WaitAll(ts) 363 ts.AddTask(healthCheck) 364 365 return ts, nil 366 } 367 368 // ConfigureSnap returns a set of tasks to configure snapName as done during installation/refresh. 369 func ConfigureSnap(st *state.State, snapName string, confFlags int) *state.TaskSet { 370 // This is slightly ugly, ideally we would check the type instead 371 // of hardcoding the name here. Unfortunately we do not have the 372 // type until we actually run the change. 373 if snapName == defaultCoreSnapName { 374 confFlags |= IgnoreHookError 375 confFlags |= TrackHookError 376 } 377 return Configure(st, snapName, nil, confFlags) 378 } 379 380 var Configure = func(st *state.State, snapName string, patch map[string]interface{}, flags int) *state.TaskSet { 381 panic("internal error: snapstate.Configure is unset") 382 } 383 384 var SetupInstallHook = func(st *state.State, snapName string) *state.Task { 385 panic("internal error: snapstate.SetupInstallHook is unset") 386 } 387 388 var SetupPreRefreshHook = func(st *state.State, snapName string) *state.Task { 389 panic("internal error: snapstate.SetupPreRefreshHook is unset") 390 } 391 392 var SetupPostRefreshHook = func(st *state.State, snapName string) *state.Task { 393 panic("internal error: snapstate.SetupPostRefreshHook is unset") 394 } 395 396 var SetupRemoveHook = func(st *state.State, snapName string) *state.Task { 397 panic("internal error: snapstate.SetupRemoveHook is unset") 398 } 399 400 var CheckHealthHook = func(st *state.State, snapName string, rev snap.Revision) *state.Task { 401 panic("internal error: snapstate.CheckHealthHook is unset") 402 } 403 404 // WaitRestart will return a Retry error if there is a pending restart 405 // and a real error if anything went wrong (like a rollback across 406 // restarts) 407 func WaitRestart(task *state.Task, snapsup *SnapSetup) (err error) { 408 if ok, _ := task.State().Restarting(); ok { 409 // don't continue until we are in the restarted snapd 410 task.Logf("Waiting for restart...") 411 return &state.Retry{} 412 } 413 414 snapInfo, err := snap.ReadInfo(snapsup.InstanceName(), snapsup.SideInfo) 415 if err != nil { 416 return err 417 } 418 419 if snapsup.Type == snap.TypeSnapd && os.Getenv("SNAPD_REVERT_TO_REV") != "" { 420 return fmt.Errorf("there was a snapd rollback across the restart") 421 } 422 423 // If not on classic check there was no rollback. A reboot 424 // can be triggered by: 425 // - core (old core16 world, system-reboot) 426 // - bootable base snap (new core18 world, system-reboot) 427 // 428 // TODO: Detect "snapd" snap daemon-restarts here that 429 // fallback into the old version (once we have 430 // better snapd rollback support in core18). 431 if !release.OnClassic { 432 // TODO: double check that we really rebooted 433 // otherwise this could be just a spurious restart 434 // of snapd 435 436 model, err := ModelFromTask(task) 437 if err != nil { 438 return err 439 } 440 bootName := "core" 441 typ := snap.TypeOS 442 if model.Base() != "" { 443 bootName = model.Base() 444 typ = snap.TypeBase 445 } 446 // if it is not a bootable snap we are not interested 447 if snapsup.InstanceName() != bootName { 448 return nil 449 } 450 451 current, err := boot.GetCurrentBoot(typ) 452 if err == boot.ErrBootNameAndRevisionNotReady { 453 return &state.Retry{After: 5 * time.Second} 454 } 455 if err != nil { 456 return err 457 } 458 459 if snapsup.InstanceName() != current.Name || snapInfo.Revision != current.Revision { 460 // TODO: make sure this revision gets ignored for 461 // automatic refreshes 462 return fmt.Errorf("cannot finish %s installation, there was a rollback across reboot", snapsup.InstanceName()) 463 } 464 } 465 466 return nil 467 } 468 469 func contentAttr(attrer interfaces.Attrer) string { 470 var s string 471 err := attrer.Attr("content", &s) 472 if err != nil { 473 return "" 474 } 475 return s 476 } 477 478 func contentIfaceAvailable(st *state.State, contentTag string) bool { 479 repo := ifacerepo.Get(st) 480 for _, slot := range repo.AllSlots("content") { 481 if contentAttr(slot) == "" { 482 continue 483 } 484 if contentAttr(slot) == contentTag { 485 return true 486 } 487 } 488 return false 489 } 490 491 // defaultContentPlugProviders takes a snap.Info and returns what 492 // default providers there are. 493 func defaultContentPlugProviders(st *state.State, info *snap.Info) []string { 494 out := []string{} 495 seen := map[string]bool{} 496 for _, plug := range info.Plugs { 497 if plug.Interface == "content" { 498 if contentAttr(plug) == "" { 499 continue 500 } 501 if !contentIfaceAvailable(st, contentAttr(plug)) { 502 var dprovider string 503 err := plug.Attr("default-provider", &dprovider) 504 if err != nil || dprovider == "" { 505 continue 506 } 507 // The default-provider is a name. However old 508 // documentation said it is "snapname:ifname", 509 // we deal with this gracefully by just 510 // stripping of the part after the ":" 511 if name := strings.SplitN(dprovider, ":", 2)[0]; !seen[name] { 512 out = append(out, name) 513 seen[name] = true 514 } 515 } 516 } 517 } 518 return out 519 } 520 521 // validateFeatureFlags validates the given snap only uses experimental 522 // features that are enabled by the user. 523 func validateFeatureFlags(st *state.State, info *snap.Info) error { 524 tr := config.NewTransaction(st) 525 526 if len(info.Layout) > 0 { 527 flag, err := config.GetFeatureFlag(tr, features.Layouts) 528 if err != nil { 529 return err 530 } 531 if !flag { 532 return fmt.Errorf("experimental feature disabled - test it by setting 'experimental.layouts' to true") 533 } 534 } 535 536 if info.InstanceKey != "" { 537 flag, err := config.GetFeatureFlag(tr, features.ParallelInstances) 538 if err != nil { 539 return err 540 } 541 if !flag { 542 return fmt.Errorf("experimental feature disabled - test it by setting 'experimental.parallel-instances' to true") 543 } 544 } 545 546 return nil 547 } 548 549 func checkInstallPreconditions(st *state.State, info *snap.Info, flags Flags, snapst *SnapState, deviceCtx DeviceContext) error { 550 // Check if the snapd can be installed on Ubuntu Core systems, it is 551 // always ok to install on classic 552 if info.GetType() == snap.TypeSnapd && !release.OnClassic { 553 if deviceCtx.Model().Base() == "" { 554 return fmt.Errorf("cannot install snapd snap on a model without a base snap yet") 555 } 556 } 557 558 if err := validateInfoAndFlags(info, snapst, flags); err != nil { 559 return err 560 } 561 if err := validateFeatureFlags(st, info); err != nil { 562 return err 563 } 564 return nil 565 } 566 567 // InstallPath returns a set of tasks for installing a snap from a file path 568 // and the snap.Info for the given snap. 569 // 570 // Note that the state must be locked by the caller. 571 // The provided SideInfo can contain just a name which results in a 572 // local revision and sideloading, or full metadata in which case it 573 // the snap will appear as installed from the store. 574 func InstallPath(st *state.State, si *snap.SideInfo, path, instanceName, channel string, flags Flags) (*state.TaskSet, *snap.Info, error) { 575 if si.RealName == "" { 576 return nil, nil, fmt.Errorf("internal error: snap name to install %q not provided", path) 577 } 578 579 if instanceName == "" { 580 instanceName = si.RealName 581 } 582 583 deviceCtx, err := DeviceCtxFromState(st, nil) 584 if err != nil { 585 return nil, nil, err 586 } 587 588 var snapst SnapState 589 err = Get(st, instanceName, &snapst) 590 if err != nil && err != state.ErrNoState { 591 return nil, nil, err 592 } 593 594 if si.SnapID != "" { 595 if si.Revision.Unset() { 596 return nil, nil, fmt.Errorf("internal error: snap id set to install %q but revision is unset", path) 597 } 598 } 599 600 channel, err = resolveChannel(st, instanceName, channel, deviceCtx) 601 if err != nil { 602 return nil, nil, err 603 } 604 605 var instFlags int 606 if flags.SkipConfigure { 607 // extract it as a doInstall flag, this is not passed 608 // into SnapSetup 609 instFlags |= skipConfigure 610 } 611 612 // It is ok do open the snap file here because we either 613 // have side info or the user passed --dangerous 614 info, container, err := backend.OpenSnapFile(path, si) 615 if err != nil { 616 return nil, nil, err 617 } 618 619 if err := validateContainer(container, info, logger.Noticef); err != nil { 620 return nil, nil, err 621 } 622 if err := snap.ValidateInstanceName(instanceName); err != nil { 623 return nil, nil, fmt.Errorf("invalid instance name: %v", err) 624 } 625 626 snapName, instanceKey := snap.SplitInstanceName(instanceName) 627 if info.SnapName() != snapName { 628 return nil, nil, fmt.Errorf("cannot install snap %q, the name does not match the metadata %q", instanceName, info.SnapName()) 629 } 630 info.InstanceKey = instanceKey 631 632 if flags.Classic && !info.NeedsClassic() { 633 // snap does not require classic confinement, silently drop the flag 634 flags.Classic = false 635 } 636 // TODO: integrate classic override with the helper 637 if err := checkInstallPreconditions(st, info, flags, &snapst, deviceCtx); err != nil { 638 return nil, nil, err 639 } 640 // this might be a refresh; check the epoch before proceeding 641 if err := earlyEpochCheck(info, &snapst); err != nil { 642 return nil, nil, err 643 } 644 645 snapsup := &SnapSetup{ 646 Base: info.Base, 647 Prereq: defaultContentPlugProviders(st, info), 648 SideInfo: si, 649 SnapPath: path, 650 Channel: channel, 651 Flags: flags.ForSnapSetup(), 652 Type: info.GetType(), 653 PlugsOnly: len(info.Slots) == 0, 654 InstanceKey: info.InstanceKey, 655 } 656 657 ts, err := doInstall(st, &snapst, snapsup, instFlags, "") 658 return ts, info, err 659 } 660 661 // TryPath returns a set of tasks for trying a snap from a file path. 662 // Note that the state must be locked by the caller. 663 func TryPath(st *state.State, name, path string, flags Flags) (*state.TaskSet, error) { 664 flags.TryMode = true 665 666 ts, _, err := InstallPath(st, &snap.SideInfo{RealName: name}, path, "", "", flags) 667 return ts, err 668 } 669 670 // Install returns a set of tasks for installing a snap. 671 // Note that the state must be locked by the caller. 672 // 673 // The returned TaskSet will contain a DownloadAndChecksDoneEdge. 674 func Install(ctx context.Context, st *state.State, name string, opts *RevisionOptions, userID int, flags Flags) (*state.TaskSet, error) { 675 return InstallWithDeviceContext(ctx, st, name, opts, userID, flags, nil, "") 676 } 677 678 // InstallWithDeviceContext returns a set of tasks for installing a snap. 679 // It will query for the snap with the given deviceCtx. 680 // Note that the state must be locked by the caller. 681 // 682 // The returned TaskSet will contain a DownloadAndChecksDoneEdge. 683 func InstallWithDeviceContext(ctx context.Context, st *state.State, name string, opts *RevisionOptions, userID int, flags Flags, deviceCtx DeviceContext, fromChange string) (*state.TaskSet, error) { 684 if opts == nil { 685 opts = &RevisionOptions{} 686 } 687 if opts.CohortKey != "" && !opts.Revision.Unset() { 688 return nil, errors.New("cannot specify revision and cohort") 689 } 690 691 if opts.Channel == "" { 692 opts.Channel = "stable" 693 } 694 695 var snapst SnapState 696 err := Get(st, name, &snapst) 697 if err != nil && err != state.ErrNoState { 698 return nil, err 699 } 700 if snapst.IsInstalled() { 701 return nil, &snap.AlreadyInstalledError{Snap: name} 702 } 703 // need to have a model set before trying to talk the store 704 deviceCtx, err = DevicePastSeeding(st, deviceCtx) 705 if err != nil { 706 return nil, err 707 } 708 709 if err := snap.ValidateInstanceName(name); err != nil { 710 return nil, fmt.Errorf("invalid instance name: %v", err) 711 } 712 713 info, err := installInfo(ctx, st, name, opts, userID, deviceCtx) 714 if err != nil { 715 return nil, err 716 } 717 718 if flags.RequireTypeBase && info.GetType() != snap.TypeBase && info.GetType() != snap.TypeOS { 719 return nil, fmt.Errorf("declared snap base %q has unexpected type %q, instead of 'base'", name, info.GetType()) 720 } 721 722 if flags.Classic && !info.NeedsClassic() { 723 // snap does not require classic confinement, silently drop the flag 724 flags.Classic = false 725 } 726 // TODO: integrate classic override with the helper 727 if err := checkInstallPreconditions(st, info, flags, &snapst, deviceCtx); err != nil { 728 return nil, err 729 } 730 731 snapsup := &SnapSetup{ 732 Channel: opts.Channel, 733 Base: info.Base, 734 Prereq: defaultContentPlugProviders(st, info), 735 UserID: userID, 736 Flags: flags.ForSnapSetup(), 737 DownloadInfo: &info.DownloadInfo, 738 SideInfo: &info.SideInfo, 739 Type: info.GetType(), 740 PlugsOnly: len(info.Slots) == 0, 741 InstanceKey: info.InstanceKey, 742 auxStoreInfo: auxStoreInfo{ 743 Media: info.Media, 744 }, 745 CohortKey: opts.CohortKey, 746 } 747 748 return doInstall(st, &snapst, snapsup, 0, fromChange) 749 } 750 751 // InstallMany installs everything from the given list of names. 752 // Note that the state must be locked by the caller. 753 func InstallMany(st *state.State, names []string, userID int) ([]string, []*state.TaskSet, error) { 754 // need to have a model set before trying to talk the store 755 deviceCtx, err := DevicePastSeeding(st, nil) 756 if err != nil { 757 return nil, nil, err 758 } 759 760 toInstall := make([]string, 0, len(names)) 761 for _, name := range names { 762 var snapst SnapState 763 err := Get(st, name, &snapst) 764 if err != nil && err != state.ErrNoState { 765 return nil, nil, err 766 } 767 if snapst.IsInstalled() { 768 continue 769 } 770 771 if err := snap.ValidateInstanceName(name); err != nil { 772 return nil, nil, fmt.Errorf("invalid instance name: %v", err) 773 } 774 775 toInstall = append(toInstall, name) 776 } 777 778 user, err := userFromUserID(st, userID) 779 if err != nil { 780 return nil, nil, err 781 } 782 783 installs, err := installCandidates(st, toInstall, "stable", user) 784 if err != nil { 785 return nil, nil, err 786 } 787 788 tasksets := make([]*state.TaskSet, 0, len(installs)) 789 for _, info := range installs { 790 var snapst SnapState 791 var flags Flags 792 793 if err := checkInstallPreconditions(st, info, flags, &snapst, deviceCtx); err != nil { 794 return nil, nil, err 795 } 796 797 snapsup := &SnapSetup{ 798 Channel: "stable", 799 Base: info.Base, 800 Prereq: defaultContentPlugProviders(st, info), 801 UserID: userID, 802 Flags: flags.ForSnapSetup(), 803 DownloadInfo: &info.DownloadInfo, 804 SideInfo: &info.SideInfo, 805 Type: info.GetType(), 806 PlugsOnly: len(info.Slots) == 0, 807 InstanceKey: info.InstanceKey, 808 } 809 810 ts, err := doInstall(st, &snapst, snapsup, 0, "") 811 if err != nil { 812 return nil, nil, err 813 } 814 ts.JoinLane(st.NewLane()) 815 tasksets = append(tasksets, ts) 816 } 817 818 return toInstall, tasksets, nil 819 } 820 821 // RefreshCandidates gets a list of candidates for update 822 // Note that the state must be locked by the caller. 823 func RefreshCandidates(st *state.State, user *auth.UserState) ([]*snap.Info, error) { 824 updates, _, _, err := refreshCandidates(context.TODO(), st, nil, user, nil) 825 return updates, err 826 } 827 828 // ValidateRefreshes allows to hook validation into the handling of refresh candidates. 829 var ValidateRefreshes func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx DeviceContext) (validated []*snap.Info, err error) 830 831 // UpdateMany updates everything from the given list of names that the 832 // store says is updateable. If the list is empty, update everything. 833 // Note that the state must be locked by the caller. 834 func UpdateMany(ctx context.Context, st *state.State, names []string, userID int, flags *Flags) ([]string, []*state.TaskSet, error) { 835 return updateManyFiltered(ctx, st, names, userID, nil, flags, "") 836 } 837 838 // updateFilter is the type of function that can be passed to 839 // updateManyFromChange so it filters the updates. 840 // 841 // If the filter returns true, the update for that snap proceeds. If 842 // it returns false, the snap is removed from the list of updates to 843 // consider. 844 type updateFilter func(*snap.Info, *SnapState) bool 845 846 func updateManyFiltered(ctx context.Context, st *state.State, names []string, userID int, filter updateFilter, flags *Flags, fromChange string) ([]string, []*state.TaskSet, error) { 847 if flags == nil { 848 flags = &Flags{} 849 } 850 user, err := userFromUserID(st, userID) 851 if err != nil { 852 return nil, nil, err 853 } 854 855 // need to have a model set before trying to talk the store 856 deviceCtx, err := DevicePastSeeding(st, nil) 857 if err != nil { 858 return nil, nil, err 859 } 860 861 refreshOpts := &store.RefreshOptions{IsAutoRefresh: flags.IsAutoRefresh} 862 updates, stateByInstanceName, ignoreValidation, err := refreshCandidates(ctx, st, names, user, refreshOpts) 863 if err != nil { 864 return nil, nil, err 865 } 866 867 if filter != nil { 868 actual := updates[:0] 869 for _, update := range updates { 870 if filter(update, stateByInstanceName[update.InstanceName()]) { 871 actual = append(actual, update) 872 } 873 } 874 updates = actual 875 } 876 877 if ValidateRefreshes != nil && len(updates) != 0 { 878 updates, err = ValidateRefreshes(st, updates, ignoreValidation, userID, deviceCtx) 879 if err != nil { 880 // not doing "refresh all" report the error 881 if len(names) != 0 { 882 return nil, nil, err 883 } 884 // doing "refresh all", log the problems 885 logger.Noticef("cannot refresh some snaps: %v", err) 886 } 887 } 888 889 params := func(update *snap.Info) (*RevisionOptions, Flags, *SnapState) { 890 snapst := stateByInstanceName[update.InstanceName()] 891 updateFlags := snapst.Flags 892 if !update.NeedsClassic() && updateFlags.Classic { 893 // allow updating from classic to strict 894 updateFlags.Classic = false 895 } 896 // setting options to what's in state as multi-refresh doesn't let you change these 897 opts := &RevisionOptions{ 898 Channel: snapst.Channel, 899 CohortKey: snapst.CohortKey, 900 } 901 return opts, snapst.Flags, snapst 902 903 } 904 905 return doUpdate(ctx, st, names, updates, params, userID, flags, deviceCtx, fromChange) 906 } 907 908 func doUpdate(ctx context.Context, st *state.State, names []string, updates []*snap.Info, params func(*snap.Info) (*RevisionOptions, Flags, *SnapState), userID int, globalFlags *Flags, deviceCtx DeviceContext, fromChange string) ([]string, []*state.TaskSet, error) { 909 if globalFlags == nil { 910 globalFlags = &Flags{} 911 } 912 913 tasksets := make([]*state.TaskSet, 0, len(updates)+2) // 1 for auto-aliases, 1 for re-refresh 914 915 refreshAll := len(names) == 0 916 var nameSet map[string]bool 917 if len(names) != 0 { 918 nameSet = make(map[string]bool, len(names)) 919 for _, name := range names { 920 nameSet[name] = true 921 } 922 } 923 924 newAutoAliases, mustPruneAutoAliases, transferTargets, err := autoAliasesUpdate(st, names, updates) 925 if err != nil { 926 return nil, nil, err 927 } 928 929 reportUpdated := make(map[string]bool, len(updates)) 930 var pruningAutoAliasesTs *state.TaskSet 931 932 if len(mustPruneAutoAliases) != 0 { 933 var err error 934 pruningAutoAliasesTs, err = applyAutoAliasesDelta(st, mustPruneAutoAliases, "prune", refreshAll, fromChange, func(snapName string, _ *state.TaskSet) { 935 if nameSet[snapName] { 936 reportUpdated[snapName] = true 937 } 938 }) 939 if err != nil { 940 return nil, nil, err 941 } 942 tasksets = append(tasksets, pruningAutoAliasesTs) 943 } 944 945 // wait for the auto-alias prune tasks as needed 946 scheduleUpdate := func(snapName string, ts *state.TaskSet) { 947 if pruningAutoAliasesTs != nil && (mustPruneAutoAliases[snapName] != nil || transferTargets[snapName]) { 948 ts.WaitAll(pruningAutoAliasesTs) 949 } 950 reportUpdated[snapName] = true 951 } 952 953 // first snapd, core, bases, then rest 954 sort.Stable(snap.ByType(updates)) 955 prereqs := make(map[string]*state.TaskSet) 956 waitPrereq := func(ts *state.TaskSet, prereqName string) { 957 preTs := prereqs[prereqName] 958 if preTs != nil { 959 ts.WaitAll(preTs) 960 } 961 } 962 963 // updates is sorted by kind so this will process first core 964 // and bases and then other snaps 965 for _, update := range updates { 966 revnoOpts, flags, snapst := params(update) 967 flags.IsAutoRefresh = globalFlags.IsAutoRefresh 968 969 if err := checkInstallPreconditions(st, update, flags, snapst, deviceCtx); err != nil { 970 if refreshAll { 971 logger.Noticef("cannot update %q: %v", update.InstanceName(), err) 972 continue 973 } 974 return nil, nil, err 975 } 976 977 if err := earlyEpochCheck(update, snapst); err != nil { 978 if refreshAll { 979 logger.Noticef("cannot update %q: %v", update.InstanceName(), err) 980 continue 981 } 982 return nil, nil, err 983 } 984 985 snapUserID, err := userIDForSnap(st, snapst, userID) 986 if err != nil { 987 return nil, nil, err 988 } 989 990 snapsup := &SnapSetup{ 991 Base: update.Base, 992 Prereq: defaultContentPlugProviders(st, update), 993 Channel: revnoOpts.Channel, 994 CohortKey: revnoOpts.CohortKey, 995 UserID: snapUserID, 996 Flags: flags.ForSnapSetup(), 997 DownloadInfo: &update.DownloadInfo, 998 SideInfo: &update.SideInfo, 999 Type: update.GetType(), 1000 PlugsOnly: len(update.Slots) == 0, 1001 InstanceKey: update.InstanceKey, 1002 auxStoreInfo: auxStoreInfo{ 1003 Media: update.Media, 1004 }, 1005 } 1006 1007 ts, err := doInstall(st, snapst, snapsup, 0, fromChange) 1008 if err != nil { 1009 if refreshAll { 1010 // doing "refresh all", just skip this snap 1011 logger.Noticef("cannot refresh snap %q: %v", update.InstanceName(), err) 1012 continue 1013 } 1014 return nil, nil, err 1015 } 1016 ts.JoinLane(st.NewLane()) 1017 1018 // because of the sorting of updates we fill prereqs 1019 // first (if branch) and only then use it to setup 1020 // waits (else branch) 1021 if t := update.GetType(); t == snap.TypeOS || t == snap.TypeBase || t == snap.TypeSnapd { 1022 // prereq types come first in updates, we 1023 // also assume bases don't have hooks, otherwise 1024 // they would need to wait on core or snapd 1025 prereqs[update.InstanceName()] = ts 1026 } else { 1027 // prereqs were processed already, wait for 1028 // them as necessary for the other kind of 1029 // snaps 1030 waitPrereq(ts, defaultCoreSnapName) 1031 waitPrereq(ts, "snapd") 1032 if update.Base != "" { 1033 waitPrereq(ts, update.Base) 1034 } 1035 } 1036 1037 scheduleUpdate(update.InstanceName(), ts) 1038 tasksets = append(tasksets, ts) 1039 } 1040 1041 if len(newAutoAliases) != 0 { 1042 addAutoAliasesTs, err := applyAutoAliasesDelta(st, newAutoAliases, "refresh", refreshAll, fromChange, scheduleUpdate) 1043 if err != nil { 1044 return nil, nil, err 1045 } 1046 tasksets = append(tasksets, addAutoAliasesTs) 1047 } 1048 1049 updated := make([]string, 0, len(reportUpdated)) 1050 for name := range reportUpdated { 1051 updated = append(updated, name) 1052 } 1053 1054 if len(updated) > 0 && !globalFlags.NoReRefresh { 1055 // re-refresh will check the lanes to decide what to 1056 // _actually_ re-refresh, but it'll be a subset of updated 1057 // (and equal to updated if nothing goes wrong) 1058 rerefresh := st.NewTask("check-rerefresh", fmt.Sprintf("Consider re-refresh of %s", strutil.Quoted(updated))) 1059 rerefresh.Set("rerefresh-setup", reRefreshSetup{ 1060 UserID: userID, 1061 Flags: globalFlags, 1062 }) 1063 tasksets = append(tasksets, state.NewTaskSet(rerefresh)) 1064 } 1065 1066 return updated, tasksets, nil 1067 } 1068 1069 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) { 1070 applyTs := state.NewTaskSet() 1071 kind := "refresh-aliases" 1072 msg := i18n.G("Refresh aliases for snap %q") 1073 if op == "prune" { 1074 kind = "prune-auto-aliases" 1075 msg = i18n.G("Prune automatic aliases for snap %q") 1076 } 1077 for instanceName, aliases := range delta { 1078 if err := checkChangeConflictIgnoringOneChange(st, instanceName, nil, fromChange); err != nil { 1079 if refreshAll { 1080 // doing "refresh all", just skip this snap 1081 logger.Noticef("cannot %s automatic aliases for snap %q: %v", op, instanceName, err) 1082 continue 1083 } 1084 return nil, err 1085 } 1086 1087 snapName, instanceKey := snap.SplitInstanceName(instanceName) 1088 snapsup := &SnapSetup{ 1089 SideInfo: &snap.SideInfo{RealName: snapName}, 1090 InstanceKey: instanceKey, 1091 } 1092 alias := st.NewTask(kind, fmt.Sprintf(msg, snapsup.InstanceName())) 1093 alias.Set("snap-setup", &snapsup) 1094 if op == "prune" { 1095 alias.Set("aliases", aliases) 1096 } 1097 ts := state.NewTaskSet(alias) 1098 linkTs(instanceName, ts) 1099 applyTs.AddAll(ts) 1100 } 1101 return applyTs, nil 1102 } 1103 1104 func autoAliasesUpdate(st *state.State, names []string, updates []*snap.Info) (changed map[string][]string, mustPrune map[string][]string, transferTargets map[string]bool, err error) { 1105 changed, dropped, err := autoAliasesDelta(st, nil) 1106 if err != nil { 1107 if len(names) != 0 { 1108 // not "refresh all", error 1109 return nil, nil, nil, err 1110 } 1111 // log and continue 1112 logger.Noticef("cannot find the delta for automatic aliases for some snaps: %v", err) 1113 } 1114 1115 refreshAll := len(names) == 0 1116 1117 // dropped alias -> snapName 1118 droppedAliases := make(map[string][]string, len(dropped)) 1119 for instanceName, aliases := range dropped { 1120 for _, alias := range aliases { 1121 droppedAliases[alias] = append(droppedAliases[alias], instanceName) 1122 } 1123 } 1124 1125 // filter changed considering only names if set: 1126 // we add auto-aliases only for mentioned snaps 1127 if !refreshAll && len(changed) != 0 { 1128 filteredChanged := make(map[string][]string, len(changed)) 1129 for _, name := range names { 1130 if changed[name] != nil { 1131 filteredChanged[name] = changed[name] 1132 } 1133 } 1134 changed = filteredChanged 1135 } 1136 1137 // mark snaps that are sources or target of transfers 1138 transferSources := make(map[string]bool, len(dropped)) 1139 transferTargets = make(map[string]bool, len(changed)) 1140 for instanceName, aliases := range changed { 1141 for _, alias := range aliases { 1142 if sources := droppedAliases[alias]; len(sources) != 0 { 1143 transferTargets[instanceName] = true 1144 for _, source := range sources { 1145 transferSources[source] = true 1146 } 1147 } 1148 } 1149 } 1150 1151 // snaps with updates 1152 updating := make(map[string]bool, len(updates)) 1153 for _, info := range updates { 1154 updating[info.InstanceName()] = true 1155 } 1156 1157 // add explicitly auto-aliases only for snaps that are not updated 1158 for instanceName := range changed { 1159 if updating[instanceName] { 1160 delete(changed, instanceName) 1161 } 1162 } 1163 1164 // prune explicitly auto-aliases only for snaps that are mentioned 1165 // and not updated OR the source of transfers 1166 mustPrune = make(map[string][]string, len(dropped)) 1167 for instanceName := range transferSources { 1168 mustPrune[instanceName] = dropped[instanceName] 1169 } 1170 if refreshAll { 1171 for instanceName, aliases := range dropped { 1172 if !updating[instanceName] { 1173 mustPrune[instanceName] = aliases 1174 } 1175 } 1176 } else { 1177 for _, name := range names { 1178 if !updating[name] && dropped[name] != nil { 1179 mustPrune[name] = dropped[name] 1180 } 1181 } 1182 } 1183 1184 return changed, mustPrune, transferTargets, nil 1185 } 1186 1187 // resolveChannel returns the effective channel to use, based on the requested 1188 // channel and constrains set by device model, or an error if switching to 1189 // requested channel is forbidden. 1190 func resolveChannel(st *state.State, snapName, newChannel string, deviceCtx DeviceContext) (effectiveChannel string, err error) { 1191 // nothing to do 1192 if newChannel == "" { 1193 return "", nil 1194 } 1195 1196 // ensure we do not switch away from the kernel-track in the model 1197 model := deviceCtx.Model() 1198 1199 var pinnedTrack, which string 1200 if snapName == model.Kernel() && model.KernelTrack() != "" { 1201 pinnedTrack, which = model.KernelTrack(), "kernel" 1202 } 1203 if snapName == model.Gadget() && model.GadgetTrack() != "" { 1204 pinnedTrack, which = model.GadgetTrack(), "gadget" 1205 } 1206 1207 if pinnedTrack == "" { 1208 // no pinned track 1209 return newChannel, nil 1210 } 1211 1212 // channel name is valid and consist of risk level or 1213 // risk/branch only, do the right thing and default to risk (or 1214 // risk/branch) within the pinned track 1215 resChannel, err := channel.ResolveLocked(pinnedTrack, newChannel) 1216 if err == channel.ErrLockedTrackSwitch { 1217 // switching to a different track is not allowed 1218 return "", fmt.Errorf("cannot switch from %s track %q as specified for the (device) model to %q", which, pinnedTrack, newChannel) 1219 1220 } 1221 if err != nil { 1222 return "", err 1223 } 1224 return resChannel, nil 1225 } 1226 1227 var errRevisionSwitch = errors.New("cannot switch revision") 1228 1229 func switchSummary(snap, chanFrom, chanTo, cohFrom, cohTo string) string { 1230 if cohFrom != cohTo { 1231 if cohTo == "" { 1232 // leave cohort 1233 if chanFrom == chanTo { 1234 return fmt.Sprintf(i18n.G("Switch snap %q away from cohort %q"), 1235 snap, strutil.ElliptLeft(cohFrom, 10)) 1236 } 1237 if chanFrom == "" { 1238 return fmt.Sprintf(i18n.G("Switch snap %q to channel %q and away from cohort %q"), 1239 snap, chanTo, strutil.ElliptLeft(cohFrom, 10), 1240 ) 1241 } 1242 return fmt.Sprintf(i18n.G("Switch snap %q from channel %q to %q and away from cohort %q"), 1243 snap, chanFrom, chanTo, strutil.ElliptLeft(cohFrom, 10), 1244 ) 1245 } 1246 if cohFrom == "" { 1247 // moving into a cohort 1248 if chanFrom == chanTo { 1249 return fmt.Sprintf(i18n.G("Switch snap %q from no cohort to %q"), 1250 snap, strutil.ElliptLeft(cohTo, 10)) 1251 } 1252 if chanFrom == "" { 1253 return fmt.Sprintf(i18n.G("Switch snap %q to channel %q and from no cohort to %q"), 1254 snap, chanTo, strutil.ElliptLeft(cohTo, 10), 1255 ) 1256 } 1257 // chanTo == "" is not interesting 1258 return fmt.Sprintf(i18n.G("Switch snap %q from channel %q to %q and from no cohort to %q"), 1259 snap, chanFrom, chanTo, strutil.ElliptLeft(cohTo, 10), 1260 ) 1261 } 1262 if chanFrom == chanTo { 1263 return fmt.Sprintf(i18n.G("Switch snap %q from cohort %q to %q"), 1264 snap, strutil.ElliptLeft(cohFrom, 10), strutil.ElliptLeft(cohTo, 10)) 1265 } 1266 if chanFrom == "" { 1267 return fmt.Sprintf(i18n.G("Switch snap %q to channel %q and from cohort %q to %q"), 1268 snap, chanTo, strutil.ElliptLeft(cohFrom, 10), strutil.ElliptLeft(cohTo, 10), 1269 ) 1270 } 1271 return fmt.Sprintf(i18n.G("Switch snap %q from channel %q to %q and from cohort %q to %q"), 1272 snap, chanFrom, chanTo, 1273 strutil.ElliptLeft(cohFrom, 10), strutil.ElliptLeft(cohTo, 10), 1274 ) 1275 } 1276 1277 if chanFrom == "" { 1278 return fmt.Sprintf(i18n.G("Switch snap %q to channel %q"), 1279 snap, chanTo) 1280 } 1281 if chanFrom != chanTo { 1282 return fmt.Sprintf(i18n.G("Switch snap %q from channel %q to %q"), 1283 snap, chanFrom, chanTo) 1284 } 1285 // a no-change switch is accepted for idempotency 1286 return "No change switch (no-op)" 1287 } 1288 1289 // Switch switches a snap to a new channel and/or cohort 1290 func Switch(st *state.State, name string, opts *RevisionOptions) (*state.TaskSet, error) { 1291 if opts == nil { 1292 opts = &RevisionOptions{} 1293 } 1294 if !opts.Revision.Unset() { 1295 return nil, errRevisionSwitch 1296 } 1297 var snapst SnapState 1298 err := Get(st, name, &snapst) 1299 if err != nil && err != state.ErrNoState { 1300 return nil, err 1301 } 1302 if !snapst.IsInstalled() { 1303 return nil, &snap.NotInstalledError{Snap: name} 1304 } 1305 1306 if err := CheckChangeConflict(st, name, nil); err != nil { 1307 return nil, err 1308 } 1309 1310 deviceCtx, err := DeviceCtxFromState(st, nil) 1311 if err != nil { 1312 return nil, err 1313 } 1314 1315 opts.Channel, err = resolveChannel(st, name, opts.Channel, deviceCtx) 1316 if err != nil { 1317 return nil, err 1318 } 1319 1320 snapsup := &SnapSetup{ 1321 SideInfo: snapst.CurrentSideInfo(), 1322 InstanceKey: snapst.InstanceKey, 1323 // set the from state (i.e. no change), they are overridden from opts as needed below 1324 CohortKey: snapst.CohortKey, 1325 Channel: snapst.Channel, 1326 } 1327 1328 if opts.Channel != "" { 1329 snapsup.Channel = opts.Channel 1330 } 1331 if opts.CohortKey != "" { 1332 snapsup.CohortKey = opts.CohortKey 1333 } 1334 if opts.LeaveCohort { 1335 snapsup.CohortKey = "" 1336 } 1337 1338 summary := switchSummary(snapsup.InstanceName(), snapst.Channel, snapsup.Channel, snapst.CohortKey, snapsup.CohortKey) 1339 switchSnap := st.NewTask("switch-snap", summary) 1340 switchSnap.Set("snap-setup", &snapsup) 1341 1342 return state.NewTaskSet(switchSnap), nil 1343 } 1344 1345 // RevisionOptions control the selection of a snap revision. 1346 type RevisionOptions struct { 1347 Channel string 1348 Revision snap.Revision 1349 CohortKey string 1350 LeaveCohort bool 1351 } 1352 1353 // Update initiates a change updating a snap. 1354 // Note that the state must be locked by the caller. 1355 // 1356 // The returned TaskSet will contain a DownloadAndChecksDoneEdge. 1357 func Update(st *state.State, name string, opts *RevisionOptions, userID int, flags Flags) (*state.TaskSet, error) { 1358 return UpdateWithDeviceContext(st, name, opts, userID, flags, nil, "") 1359 } 1360 1361 // UpdateWithDeviceContext initiates a change updating a snap. 1362 // It will query for the snap with the given deviceCtx. 1363 // Note that the state must be locked by the caller. 1364 // 1365 // The returned TaskSet will contain a DownloadAndChecksDoneEdge. 1366 func UpdateWithDeviceContext(st *state.State, name string, opts *RevisionOptions, userID int, flags Flags, deviceCtx DeviceContext, fromChange string) (*state.TaskSet, error) { 1367 if opts == nil { 1368 opts = &RevisionOptions{} 1369 } 1370 var snapst SnapState 1371 err := Get(st, name, &snapst) 1372 if err != nil && err != state.ErrNoState { 1373 return nil, err 1374 } 1375 if !snapst.IsInstalled() { 1376 return nil, &snap.NotInstalledError{Snap: name} 1377 } 1378 1379 // FIXME: snaps that are not active are skipped for now 1380 // until we know what we want to do 1381 if !snapst.Active { 1382 return nil, fmt.Errorf("refreshing disabled snap %q not supported", name) 1383 } 1384 1385 // need to have a model set before trying to talk the store 1386 deviceCtx, err = DevicePastSeeding(st, deviceCtx) 1387 if err != nil { 1388 return nil, err 1389 } 1390 1391 opts.Channel, err = resolveChannel(st, name, opts.Channel, deviceCtx) 1392 if err != nil { 1393 return nil, err 1394 } 1395 1396 if opts.Channel == "" { 1397 // default to tracking the same channel 1398 opts.Channel = snapst.Channel 1399 } 1400 if opts.CohortKey == "" { 1401 // default to being in the same cohort 1402 opts.CohortKey = snapst.CohortKey 1403 } 1404 if opts.LeaveCohort { 1405 opts.CohortKey = "" 1406 } 1407 1408 // TODO: make flags be per revision to avoid this logic (that 1409 // leaves corner cases all over the place) 1410 if !(flags.JailMode || flags.DevMode) { 1411 flags.Classic = flags.Classic || snapst.Flags.Classic 1412 } 1413 1414 var updates []*snap.Info 1415 info, infoErr := infoForUpdate(st, &snapst, name, opts, userID, flags, deviceCtx) 1416 switch infoErr { 1417 case nil: 1418 updates = append(updates, info) 1419 case store.ErrNoUpdateAvailable: 1420 // there may be some new auto-aliases 1421 default: 1422 return nil, infoErr 1423 } 1424 1425 params := func(update *snap.Info) (*RevisionOptions, Flags, *SnapState) { 1426 updateFlags := flags 1427 if !update.NeedsClassic() && updateFlags.Classic { 1428 // allow updating from classic to strict 1429 updateFlags.Classic = false 1430 } 1431 return opts, updateFlags, &snapst 1432 } 1433 1434 _, tts, err := doUpdate(context.TODO(), st, []string{name}, updates, params, userID, &flags, deviceCtx, fromChange) 1435 if err != nil { 1436 return nil, err 1437 } 1438 1439 // see if we need to switch the channel or cohort, or toggle ignore-validation 1440 switchChannel := snapst.Channel != opts.Channel 1441 switchCohortKey := snapst.CohortKey != opts.CohortKey 1442 toggleIgnoreValidation := snapst.IgnoreValidation != flags.IgnoreValidation 1443 if infoErr == store.ErrNoUpdateAvailable && (switchChannel || switchCohortKey || toggleIgnoreValidation) { 1444 // NOTE: if we are in here, len(updates) == 0 1445 // (so we're free to add tasks because there's no rerefresh) 1446 1447 if err := checkChangeConflictIgnoringOneChange(st, name, nil, fromChange); err != nil { 1448 return nil, err 1449 } 1450 1451 snapsup := &SnapSetup{ 1452 SideInfo: snapst.CurrentSideInfo(), 1453 Flags: snapst.Flags.ForSnapSetup(), 1454 InstanceKey: snapst.InstanceKey, 1455 CohortKey: opts.CohortKey, 1456 } 1457 1458 if switchChannel || switchCohortKey { 1459 // update the tracked channel and cohort 1460 snapsup.Channel = opts.Channel 1461 snapsup.CohortKey = opts.CohortKey 1462 // Update the current snap channel as well. This ensures that 1463 // the UI displays the right values. 1464 snapsup.SideInfo.Channel = opts.Channel 1465 1466 summary := switchSummary(snapsup.InstanceName(), snapst.Channel, opts.Channel, snapst.CohortKey, opts.CohortKey) 1467 switchSnap := st.NewTask("switch-snap-channel", summary) 1468 switchSnap.Set("snap-setup", &snapsup) 1469 1470 switchSnapTs := state.NewTaskSet(switchSnap) 1471 for _, ts := range tts { 1472 switchSnapTs.WaitAll(ts) 1473 } 1474 tts = append(tts, switchSnapTs) 1475 } 1476 1477 if toggleIgnoreValidation { 1478 snapsup.IgnoreValidation = flags.IgnoreValidation 1479 toggle := st.NewTask("toggle-snap-flags", fmt.Sprintf(i18n.G("Toggle snap %q flags"), snapsup.InstanceName())) 1480 toggle.Set("snap-setup", &snapsup) 1481 1482 toggleTs := state.NewTaskSet(toggle) 1483 for _, ts := range tts { 1484 toggleTs.WaitAll(ts) 1485 } 1486 tts = append(tts, toggleTs) 1487 } 1488 } 1489 1490 if len(tts) == 0 && len(updates) == 0 { 1491 // really nothing to do, return the original no-update-available error 1492 return nil, infoErr 1493 } 1494 flat := state.NewTaskSet() 1495 for _, ts := range tts { 1496 // The tasksets we get from "doUpdate" contain important 1497 // "TaskEdge" information that is needed for "Remodel". 1498 // To preserve those we need to use "AddAllWithEdges()". 1499 if err := flat.AddAllWithEdges(ts); err != nil { 1500 return nil, err 1501 } 1502 } 1503 return flat, nil 1504 } 1505 1506 func infoForUpdate(st *state.State, snapst *SnapState, name string, opts *RevisionOptions, userID int, flags Flags, deviceCtx DeviceContext) (*snap.Info, error) { 1507 if opts.Revision.Unset() { 1508 // good ol' refresh 1509 info, err := updateInfo(st, snapst, opts, userID, flags, deviceCtx) 1510 if err != nil { 1511 return nil, err 1512 } 1513 if ValidateRefreshes != nil && !flags.IgnoreValidation { 1514 _, err := ValidateRefreshes(st, []*snap.Info{info}, nil, userID, deviceCtx) 1515 if err != nil { 1516 return nil, err 1517 } 1518 } 1519 return info, nil 1520 } 1521 var sideInfo *snap.SideInfo 1522 for _, si := range snapst.Sequence { 1523 if si.Revision == opts.Revision { 1524 sideInfo = si 1525 break 1526 } 1527 } 1528 if sideInfo == nil { 1529 // refresh from given revision from store 1530 return updateToRevisionInfo(st, snapst, opts.Revision, userID, deviceCtx) 1531 } 1532 1533 // refresh-to-local, this assumes the snap revision is mounted 1534 return readInfo(name, sideInfo, errorOnBroken) 1535 } 1536 1537 // AutoRefreshAssertions allows to hook fetching of important assertions 1538 // into the Autorefresh function. 1539 var AutoRefreshAssertions func(st *state.State, userID int) error 1540 1541 // AutoRefresh is the wrapper that will do a refresh of all the installed 1542 // snaps on the system. In addition to that it will also refresh important 1543 // assertions. 1544 func AutoRefresh(ctx context.Context, st *state.State) ([]string, []*state.TaskSet, error) { 1545 userID := 0 1546 1547 if AutoRefreshAssertions != nil { 1548 if err := AutoRefreshAssertions(st, userID); err != nil { 1549 return nil, nil, err 1550 } 1551 } 1552 1553 return UpdateMany(ctx, st, nil, userID, &Flags{IsAutoRefresh: true}) 1554 } 1555 1556 // Enable sets a snap to the active state 1557 func Enable(st *state.State, name string) (*state.TaskSet, error) { 1558 var snapst SnapState 1559 err := Get(st, name, &snapst) 1560 if err == state.ErrNoState { 1561 return nil, &snap.NotInstalledError{Snap: name} 1562 } 1563 if err != nil { 1564 return nil, err 1565 } 1566 1567 if snapst.Active { 1568 return nil, fmt.Errorf("snap %q already enabled", name) 1569 } 1570 1571 if err := CheckChangeConflict(st, name, nil); err != nil { 1572 return nil, err 1573 } 1574 1575 info, err := snapst.CurrentInfo() 1576 if err != nil { 1577 return nil, err 1578 } 1579 1580 snapsup := &SnapSetup{ 1581 SideInfo: snapst.CurrentSideInfo(), 1582 Flags: snapst.Flags.ForSnapSetup(), 1583 Type: info.GetType(), 1584 PlugsOnly: len(info.Slots) == 0, 1585 InstanceKey: snapst.InstanceKey, 1586 } 1587 1588 prepareSnap := st.NewTask("prepare-snap", fmt.Sprintf(i18n.G("Prepare snap %q (%s)"), snapsup.InstanceName(), snapst.Current)) 1589 prepareSnap.Set("snap-setup", &snapsup) 1590 1591 setupProfiles := st.NewTask("setup-profiles", fmt.Sprintf(i18n.G("Setup snap %q (%s) security profiles"), snapsup.InstanceName(), snapst.Current)) 1592 setupProfiles.Set("snap-setup-task", prepareSnap.ID()) 1593 setupProfiles.WaitFor(prepareSnap) 1594 1595 linkSnap := st.NewTask("link-snap", fmt.Sprintf(i18n.G("Make snap %q (%s) available to the system"), snapsup.InstanceName(), snapst.Current)) 1596 linkSnap.Set("snap-setup-task", prepareSnap.ID()) 1597 linkSnap.WaitFor(setupProfiles) 1598 1599 // setup aliases 1600 setupAliases := st.NewTask("setup-aliases", fmt.Sprintf(i18n.G("Setup snap %q aliases"), snapsup.InstanceName())) 1601 setupAliases.Set("snap-setup-task", prepareSnap.ID()) 1602 setupAliases.WaitFor(linkSnap) 1603 1604 startSnapServices := st.NewTask("start-snap-services", fmt.Sprintf(i18n.G("Start snap %q (%s) services"), snapsup.InstanceName(), snapst.Current)) 1605 startSnapServices.Set("snap-setup-task", prepareSnap.ID()) 1606 startSnapServices.WaitFor(setupAliases) 1607 1608 return state.NewTaskSet(prepareSnap, setupProfiles, linkSnap, setupAliases, startSnapServices), nil 1609 } 1610 1611 // Disable sets a snap to the inactive state 1612 func Disable(st *state.State, name string) (*state.TaskSet, error) { 1613 var snapst SnapState 1614 err := Get(st, name, &snapst) 1615 if err == state.ErrNoState { 1616 return nil, &snap.NotInstalledError{Snap: name} 1617 } 1618 if err != nil { 1619 return nil, err 1620 } 1621 if !snapst.Active { 1622 return nil, fmt.Errorf("snap %q already disabled", name) 1623 } 1624 1625 info, err := Info(st, name, snapst.Current) 1626 if err != nil { 1627 return nil, err 1628 } 1629 if !canDisable(info) { 1630 return nil, fmt.Errorf("snap %q cannot be disabled", name) 1631 } 1632 1633 if err := CheckChangeConflict(st, name, nil); err != nil { 1634 return nil, err 1635 } 1636 1637 snapsup := &SnapSetup{ 1638 SideInfo: &snap.SideInfo{ 1639 RealName: snap.InstanceSnap(name), 1640 Revision: snapst.Current, 1641 }, 1642 Type: info.GetType(), 1643 PlugsOnly: len(info.Slots) == 0, 1644 InstanceKey: snapst.InstanceKey, 1645 } 1646 1647 stopSnapServices := st.NewTask("stop-snap-services", fmt.Sprintf(i18n.G("Stop snap %q (%s) services"), snapsup.InstanceName(), snapst.Current)) 1648 stopSnapServices.Set("snap-setup", &snapsup) 1649 stopSnapServices.Set("stop-reason", snap.StopReasonDisable) 1650 1651 removeAliases := st.NewTask("remove-aliases", fmt.Sprintf(i18n.G("Remove aliases for snap %q"), snapsup.InstanceName())) 1652 removeAliases.Set("snap-setup-task", stopSnapServices.ID()) 1653 removeAliases.WaitFor(stopSnapServices) 1654 1655 unlinkSnap := st.NewTask("unlink-snap", fmt.Sprintf(i18n.G("Make snap %q (%s) unavailable to the system"), snapsup.InstanceName(), snapst.Current)) 1656 unlinkSnap.Set("snap-setup-task", stopSnapServices.ID()) 1657 unlinkSnap.WaitFor(removeAliases) 1658 1659 removeProfiles := st.NewTask("remove-profiles", fmt.Sprintf(i18n.G("Remove security profiles of snap %q"), snapsup.InstanceName())) 1660 removeProfiles.Set("snap-setup-task", stopSnapServices.ID()) 1661 removeProfiles.WaitFor(unlinkSnap) 1662 1663 return state.NewTaskSet(stopSnapServices, removeAliases, unlinkSnap, removeProfiles), nil 1664 } 1665 1666 // canDisable verifies that a snap can be deactivated. 1667 func canDisable(si *snap.Info) bool { 1668 for _, importantSnapType := range []snap.Type{snap.TypeGadget, snap.TypeKernel, snap.TypeOS} { 1669 if importantSnapType == si.GetType() { 1670 return false 1671 } 1672 } 1673 1674 return true 1675 } 1676 1677 // baseInUse returns true if the given base is needed by another snap 1678 func baseInUse(st *state.State, base *snap.Info) bool { 1679 snapStates, err := All(st) 1680 if err != nil { 1681 return false 1682 } 1683 for name, snapst := range snapStates { 1684 for _, si := range snapst.Sequence { 1685 if snapInfo, err := snap.ReadInfo(name, si); err == nil { 1686 if snapInfo.GetType() != snap.TypeApp { 1687 continue 1688 } 1689 if snapInfo.Base == base.SnapName() { 1690 return true 1691 } 1692 } 1693 } 1694 } 1695 return false 1696 } 1697 1698 // coreInUse returns true if any snap uses "core" (i.e. does not 1699 // declare a base 1700 func coreInUse(st *state.State) bool { 1701 snapStates, err := All(st) 1702 if err != nil { 1703 return false 1704 } 1705 for name, snapst := range snapStates { 1706 for _, si := range snapst.Sequence { 1707 if snapInfo, err := snap.ReadInfo(name, si); err == nil { 1708 if snapInfo.GetType() != snap.TypeApp || snapInfo.GetType() == snap.TypeSnapd { 1709 continue 1710 } 1711 if snapInfo.Base == "" { 1712 return true 1713 } 1714 } 1715 } 1716 } 1717 return false 1718 } 1719 1720 // canRemove verifies that a snap can be removed. 1721 // 1722 // TODO: canRemove should also return the reason why the snap cannot 1723 // be removed to the user 1724 func canRemove(st *state.State, si *snap.Info, snapst *SnapState, removeAll bool, deviceCtx DeviceContext) bool { 1725 // never remove anything that is used for booting 1726 if boot.InUse(si.InstanceName(), si.Revision) { 1727 return false 1728 } 1729 1730 // removing single revisions is generally allowed 1731 if !removeAll { 1732 return true 1733 } 1734 1735 // required snaps cannot be removed 1736 if snapst.Required { 1737 return false 1738 } 1739 1740 // TODO: use Required for these too 1741 1742 // Gadget snaps should not be removed as they are a key 1743 // building block for Gadgets. Do not remove their last 1744 // revision left. 1745 if si.GetType() == snap.TypeGadget { 1746 return false 1747 } 1748 1749 // Allow "ubuntu-core" removals here because we might have two 1750 // core snaps installed (ubuntu-core and core). Note that 1751 // ideally we would only allow the removal of "ubuntu-core" if 1752 // we know that "core" is installed too and if we are part of 1753 // the "ubuntu-core->core" transition. But this transition 1754 // starts automatically on startup so the window of a user 1755 // triggering this manually is very small. 1756 // 1757 // Once the ubuntu-core -> core transition has landed for some 1758 // time we can remove the two lines below. 1759 if si.InstanceName() == "ubuntu-core" && si.GetType() == snap.TypeOS { 1760 return true 1761 } 1762 1763 // do not allow removal of bases that are in use 1764 if si.GetType() == snap.TypeBase && baseInUse(st, si) { 1765 return false 1766 } 1767 1768 // Allow snap.TypeOS removals if a different base is in use 1769 // 1770 // Note that removal of the boot base itself is prevented 1771 // via the snapst.Required flag that is set on firstboot. 1772 model := deviceCtx.Model() 1773 if si.GetType() == snap.TypeOS { 1774 if model.Base() != "" && !coreInUse(st) { 1775 return true 1776 } 1777 return false 1778 } 1779 1780 // Because of a Remodel() we may have multiple kernels. Allow 1781 // removals of kernel(s) that are not model kernels (anymore). 1782 if si.GetType() == snap.TypeKernel { 1783 if model.Kernel() != si.InstanceName() { 1784 return true 1785 } 1786 return false 1787 } 1788 1789 // TODO: on classic likely let remove core even if active if it's only snap left. 1790 1791 return true 1792 } 1793 1794 // RemoveFlags are used to pass additional flags to the Remove operation. 1795 type RemoveFlags struct { 1796 // Remove the snap without creating snapshot data 1797 Purge bool 1798 } 1799 1800 // Remove returns a set of tasks for removing snap. 1801 // Note that the state must be locked by the caller. 1802 func Remove(st *state.State, name string, revision snap.Revision, flags *RemoveFlags) (*state.TaskSet, error) { 1803 var snapst SnapState 1804 err := Get(st, name, &snapst) 1805 if err != nil && err != state.ErrNoState { 1806 return nil, err 1807 } 1808 1809 if !snapst.IsInstalled() { 1810 return nil, &snap.NotInstalledError{Snap: name, Rev: snap.R(0)} 1811 } 1812 1813 if err := CheckChangeConflict(st, name, nil); err != nil { 1814 return nil, err 1815 } 1816 1817 deviceCtx, err := DeviceCtxFromState(st, nil) 1818 if err != nil { 1819 return nil, err 1820 } 1821 1822 active := snapst.Active 1823 var removeAll bool 1824 if revision.Unset() { 1825 revision = snapst.Current 1826 removeAll = true 1827 } else { 1828 if active { 1829 if revision == snapst.Current { 1830 msg := "cannot remove active revision %s of snap %q" 1831 if len(snapst.Sequence) > 1 { 1832 msg += " (revert first?)" 1833 } 1834 return nil, fmt.Errorf(msg, revision, name) 1835 } 1836 active = false 1837 } 1838 1839 if !revisionInSequence(&snapst, revision) { 1840 return nil, &snap.NotInstalledError{Snap: name, Rev: revision} 1841 } 1842 1843 removeAll = len(snapst.Sequence) == 1 1844 } 1845 1846 info, err := Info(st, name, revision) 1847 if err != nil { 1848 return nil, err 1849 } 1850 1851 // check if this is something that can be removed 1852 if !canRemove(st, info, &snapst, removeAll, deviceCtx) { 1853 return nil, fmt.Errorf("snap %q is not removable", name) 1854 } 1855 1856 // main/current SnapSetup 1857 snapsup := SnapSetup{ 1858 SideInfo: &snap.SideInfo{ 1859 SnapID: info.SnapID, 1860 RealName: snap.InstanceSnap(name), 1861 Revision: revision, 1862 }, 1863 Type: info.GetType(), 1864 PlugsOnly: len(info.Slots) == 0, 1865 InstanceKey: snapst.InstanceKey, 1866 } 1867 1868 // trigger remove 1869 1870 full := state.NewTaskSet() 1871 var chain *state.TaskSet 1872 1873 addNext := func(ts *state.TaskSet) { 1874 if chain != nil { 1875 ts.WaitAll(chain) 1876 } 1877 full.AddAll(ts) 1878 chain = ts 1879 } 1880 1881 var prev *state.Task 1882 var stopSnapServices *state.Task 1883 if active { 1884 stopSnapServices = st.NewTask("stop-snap-services", fmt.Sprintf(i18n.G("Stop snap %q services"), name)) 1885 stopSnapServices.Set("snap-setup", snapsup) 1886 stopSnapServices.Set("stop-reason", snap.StopReasonRemove) 1887 addNext(state.NewTaskSet(stopSnapServices)) 1888 prev = stopSnapServices 1889 } 1890 1891 // only run remove hook if uninstalling the snap completely 1892 if removeAll { 1893 removeHook := SetupRemoveHook(st, snapsup.InstanceName()) 1894 addNext(state.NewTaskSet(removeHook)) 1895 prev = removeHook 1896 } 1897 1898 if removeAll { 1899 // run disconnect hooks 1900 disconnect := st.NewTask("auto-disconnect", fmt.Sprintf(i18n.G("Disconnect interfaces of snap %q"), snapsup.InstanceName())) 1901 disconnect.Set("snap-setup", snapsup) 1902 if prev != nil { 1903 disconnect.WaitFor(prev) 1904 } 1905 addNext(state.NewTaskSet(disconnect)) 1906 prev = disconnect 1907 } 1908 1909 // 'purge' flag disables automatic snapshot for given remove op 1910 if flags == nil || !flags.Purge { 1911 if tp, _ := snapst.Type(); tp == snap.TypeApp && removeAll { 1912 ts, err := AutomaticSnapshot(st, name) 1913 if err == nil { 1914 addNext(ts) 1915 } else { 1916 if err != ErrNothingToDo { 1917 return nil, err 1918 } 1919 } 1920 } 1921 } 1922 1923 if active { // unlink 1924 var tasks []*state.Task 1925 1926 removeAliases := st.NewTask("remove-aliases", fmt.Sprintf(i18n.G("Remove aliases for snap %q"), name)) 1927 removeAliases.WaitFor(prev) // prev is not needed beyond here 1928 removeAliases.Set("snap-setup-task", stopSnapServices.ID()) 1929 1930 unlink := st.NewTask("unlink-snap", fmt.Sprintf(i18n.G("Make snap %q unavailable to the system"), name)) 1931 unlink.Set("snap-setup-task", stopSnapServices.ID()) 1932 unlink.WaitFor(removeAliases) 1933 1934 removeSecurity := st.NewTask("remove-profiles", fmt.Sprintf(i18n.G("Remove security profile for snap %q (%s)"), name, revision)) 1935 removeSecurity.WaitFor(unlink) 1936 removeSecurity.Set("snap-setup-task", stopSnapServices.ID()) 1937 1938 tasks = append(tasks, removeAliases, unlink, removeSecurity) 1939 addNext(state.NewTaskSet(tasks...)) 1940 } 1941 1942 if removeAll { 1943 seq := snapst.Sequence 1944 for i := len(seq) - 1; i >= 0; i-- { 1945 si := seq[i] 1946 addNext(removeInactiveRevision(st, name, info.SnapID, si.Revision)) 1947 } 1948 } else { 1949 addNext(removeInactiveRevision(st, name, info.SnapID, revision)) 1950 } 1951 1952 return full, nil 1953 } 1954 1955 func removeInactiveRevision(st *state.State, name, snapID string, revision snap.Revision) *state.TaskSet { 1956 snapName, instanceKey := snap.SplitInstanceName(name) 1957 snapsup := SnapSetup{ 1958 SideInfo: &snap.SideInfo{ 1959 RealName: snapName, 1960 SnapID: snapID, 1961 Revision: revision, 1962 }, 1963 InstanceKey: instanceKey, 1964 } 1965 1966 clearData := st.NewTask("clear-snap", fmt.Sprintf(i18n.G("Remove data for snap %q (%s)"), name, revision)) 1967 clearData.Set("snap-setup", snapsup) 1968 1969 discardSnap := st.NewTask("discard-snap", fmt.Sprintf(i18n.G("Remove snap %q (%s) from the system"), name, revision)) 1970 discardSnap.WaitFor(clearData) 1971 discardSnap.Set("snap-setup-task", clearData.ID()) 1972 1973 return state.NewTaskSet(clearData, discardSnap) 1974 } 1975 1976 // RemoveMany removes everything from the given list of names. 1977 // Note that the state must be locked by the caller. 1978 func RemoveMany(st *state.State, names []string) ([]string, []*state.TaskSet, error) { 1979 removed := make([]string, 0, len(names)) 1980 tasksets := make([]*state.TaskSet, 0, len(names)) 1981 for _, name := range names { 1982 ts, err := Remove(st, name, snap.R(0), nil) 1983 // FIXME: is this expected behavior? 1984 if _, ok := err.(*snap.NotInstalledError); ok { 1985 continue 1986 } 1987 if err != nil { 1988 return nil, nil, err 1989 } 1990 removed = append(removed, name) 1991 ts.JoinLane(st.NewLane()) 1992 tasksets = append(tasksets, ts) 1993 } 1994 1995 return removed, tasksets, nil 1996 } 1997 1998 // Revert returns a set of tasks for reverting to the previous version of the snap. 1999 // Note that the state must be locked by the caller. 2000 func Revert(st *state.State, name string, flags Flags) (*state.TaskSet, error) { 2001 var snapst SnapState 2002 err := Get(st, name, &snapst) 2003 if err != nil && err != state.ErrNoState { 2004 return nil, err 2005 } 2006 2007 pi := snapst.previousSideInfo() 2008 if pi == nil { 2009 return nil, fmt.Errorf("no revision to revert to") 2010 } 2011 2012 return RevertToRevision(st, name, pi.Revision, flags) 2013 } 2014 2015 func RevertToRevision(st *state.State, name string, rev snap.Revision, flags Flags) (*state.TaskSet, error) { 2016 var snapst SnapState 2017 err := Get(st, name, &snapst) 2018 if err != nil && err != state.ErrNoState { 2019 return nil, err 2020 } 2021 2022 if snapst.Current == rev { 2023 return nil, fmt.Errorf("already on requested revision") 2024 } 2025 2026 if !snapst.Active { 2027 return nil, fmt.Errorf("cannot revert inactive snaps") 2028 } 2029 i := snapst.LastIndex(rev) 2030 if i < 0 { 2031 return nil, fmt.Errorf("cannot find revision %s for snap %q", rev, name) 2032 } 2033 2034 flags.Revert = true 2035 // TODO: make flags be per revision to avoid this logic (that 2036 // leaves corner cases all over the place) 2037 if !(flags.JailMode || flags.DevMode || flags.Classic) { 2038 if snapst.Flags.DevMode { 2039 flags.DevMode = true 2040 } 2041 if snapst.Flags.JailMode { 2042 flags.JailMode = true 2043 } 2044 if snapst.Flags.Classic { 2045 flags.Classic = true 2046 } 2047 } 2048 2049 info, err := Info(st, name, rev) 2050 if err != nil { 2051 return nil, err 2052 } 2053 2054 snapsup := &SnapSetup{ 2055 SideInfo: snapst.Sequence[i], 2056 Flags: flags.ForSnapSetup(), 2057 Type: info.GetType(), 2058 PlugsOnly: len(info.Slots) == 0, 2059 InstanceKey: snapst.InstanceKey, 2060 } 2061 return doInstall(st, &snapst, snapsup, 0, "") 2062 } 2063 2064 // TransitionCore transitions from an old core snap name to a new core 2065 // snap name. It is used for the ubuntu-core -> core transition (that 2066 // is not just a rename because the two snaps have different snapIDs) 2067 // 2068 // Note that this function makes some assumptions like: 2069 // - no aliases setup for both snaps 2070 // - no data needs to be copied 2071 // - all interfaces are absolutely identical on both new and old 2072 // Do not use this as a general way to transition from snap A to snap B. 2073 func TransitionCore(st *state.State, oldName, newName string) ([]*state.TaskSet, error) { 2074 var oldSnapst, newSnapst SnapState 2075 err := Get(st, oldName, &oldSnapst) 2076 if err != nil && err != state.ErrNoState { 2077 return nil, err 2078 } 2079 if !oldSnapst.IsInstalled() { 2080 return nil, fmt.Errorf("cannot transition snap %q: not installed", oldName) 2081 } 2082 2083 var all []*state.TaskSet 2084 // install new core (if not already installed) 2085 err = Get(st, newName, &newSnapst) 2086 if err != nil && err != state.ErrNoState { 2087 return nil, err 2088 } 2089 if !newSnapst.IsInstalled() { 2090 var userID int 2091 newInfo, err := installInfo(context.TODO(), st, newName, &RevisionOptions{Channel: oldSnapst.Channel}, userID, nil) 2092 if err != nil { 2093 return nil, err 2094 } 2095 2096 // start by installing the new snap 2097 tsInst, err := doInstall(st, &newSnapst, &SnapSetup{ 2098 Channel: oldSnapst.Channel, 2099 DownloadInfo: &newInfo.DownloadInfo, 2100 SideInfo: &newInfo.SideInfo, 2101 Type: newInfo.GetType(), 2102 }, 0, "") 2103 if err != nil { 2104 return nil, err 2105 } 2106 all = append(all, tsInst) 2107 } 2108 2109 // then transition the interface connections over 2110 transIf := st.NewTask("transition-ubuntu-core", fmt.Sprintf(i18n.G("Transition security profiles from %q to %q"), oldName, newName)) 2111 transIf.Set("old-name", oldName) 2112 transIf.Set("new-name", newName) 2113 if len(all) > 0 { 2114 transIf.WaitAll(all[0]) 2115 } 2116 tsTrans := state.NewTaskSet(transIf) 2117 all = append(all, tsTrans) 2118 2119 // FIXME: this is just here for the tests 2120 transIf.Set("snap-setup", &SnapSetup{ 2121 SideInfo: &snap.SideInfo{ 2122 RealName: oldName, 2123 }, 2124 }) 2125 2126 // then remove the old snap 2127 tsRm, err := Remove(st, oldName, snap.R(0), nil) 2128 if err != nil { 2129 return nil, err 2130 } 2131 tsRm.WaitFor(transIf) 2132 all = append(all, tsRm) 2133 2134 return all, nil 2135 } 2136 2137 // State/info accessors 2138 2139 // Installing returns whether there's an in-progress installation. 2140 func Installing(st *state.State) bool { 2141 for _, task := range st.Tasks() { 2142 k := task.Kind() 2143 chg := task.Change() 2144 if k == "mount-snap" && chg != nil && !chg.Status().Ready() { 2145 return true 2146 } 2147 } 2148 return false 2149 } 2150 2151 // Info returns the information about the snap with given name and revision. 2152 // Works also for a mounted candidate snap in the process of being installed. 2153 func Info(st *state.State, name string, revision snap.Revision) (*snap.Info, error) { 2154 var snapst SnapState 2155 err := Get(st, name, &snapst) 2156 if err == state.ErrNoState { 2157 return nil, &snap.NotInstalledError{Snap: name} 2158 } 2159 if err != nil { 2160 return nil, err 2161 } 2162 2163 for i := len(snapst.Sequence) - 1; i >= 0; i-- { 2164 if si := snapst.Sequence[i]; si.Revision == revision { 2165 return readInfo(name, si, 0) 2166 } 2167 } 2168 2169 return nil, fmt.Errorf("cannot find snap %q at revision %s", name, revision.String()) 2170 } 2171 2172 // CurrentInfo returns the information about the current revision of a snap with the given name. 2173 func CurrentInfo(st *state.State, name string) (*snap.Info, error) { 2174 var snapst SnapState 2175 err := Get(st, name, &snapst) 2176 if err != nil && err != state.ErrNoState { 2177 return nil, err 2178 } 2179 info, err := snapst.CurrentInfo() 2180 if err == ErrNoCurrent { 2181 return nil, &snap.NotInstalledError{Snap: name} 2182 } 2183 return info, err 2184 } 2185 2186 // Get retrieves the SnapState of the given snap. 2187 func Get(st *state.State, name string, snapst *SnapState) error { 2188 if snapst == nil { 2189 return fmt.Errorf("internal error: snapst is nil") 2190 } 2191 // SnapState is (un-)marshalled from/to JSON, fields having omitempty 2192 // tag will not appear in the output (if empty) and subsequently will 2193 // not be unmarshalled to (or cleared); if the caller reuses the same 2194 // struct though subsequent calls, it is possible that they end up with 2195 // garbage inside, clear the destination struct so that we always 2196 // unmarshal to a clean state 2197 *snapst = SnapState{} 2198 2199 var snaps map[string]*json.RawMessage 2200 err := st.Get("snaps", &snaps) 2201 if err != nil { 2202 return err 2203 } 2204 raw, ok := snaps[name] 2205 if !ok { 2206 return state.ErrNoState 2207 } 2208 err = json.Unmarshal([]byte(*raw), &snapst) 2209 if err != nil { 2210 return fmt.Errorf("cannot unmarshal snap state: %v", err) 2211 } 2212 return nil 2213 } 2214 2215 // All retrieves return a map from name to SnapState for all current snaps in the system state. 2216 func All(st *state.State) (map[string]*SnapState, error) { 2217 // XXX: result is a map because sideloaded snaps carry no name 2218 // atm in their sideinfos 2219 var stateMap map[string]*SnapState 2220 if err := st.Get("snaps", &stateMap); err != nil && err != state.ErrNoState { 2221 return nil, err 2222 } 2223 curStates := make(map[string]*SnapState, len(stateMap)) 2224 for instanceName, snapst := range stateMap { 2225 curStates[instanceName] = snapst 2226 } 2227 return curStates, nil 2228 } 2229 2230 // NumSnaps returns the number of installed snaps. 2231 func NumSnaps(st *state.State) (int, error) { 2232 var snaps map[string]*json.RawMessage 2233 if err := st.Get("snaps", &snaps); err != nil && err != state.ErrNoState { 2234 return -1, err 2235 } 2236 return len(snaps), nil 2237 } 2238 2239 // Set sets the SnapState of the given snap, overwriting any earlier state. 2240 func Set(st *state.State, name string, snapst *SnapState) { 2241 var snaps map[string]*json.RawMessage 2242 err := st.Get("snaps", &snaps) 2243 if err != nil && err != state.ErrNoState { 2244 panic("internal error: cannot unmarshal snaps state: " + err.Error()) 2245 } 2246 if snaps == nil { 2247 snaps = make(map[string]*json.RawMessage) 2248 } 2249 if snapst == nil || (len(snapst.Sequence) == 0) { 2250 delete(snaps, name) 2251 } else { 2252 data, err := json.Marshal(snapst) 2253 if err != nil { 2254 panic("internal error: cannot marshal snap state: " + err.Error()) 2255 } 2256 raw := json.RawMessage(data) 2257 snaps[name] = &raw 2258 } 2259 st.Set("snaps", snaps) 2260 } 2261 2262 // ActiveInfos returns information about all active snaps. 2263 func ActiveInfos(st *state.State) ([]*snap.Info, error) { 2264 var stateMap map[string]*SnapState 2265 var infos []*snap.Info 2266 if err := st.Get("snaps", &stateMap); err != nil && err != state.ErrNoState { 2267 return nil, err 2268 } 2269 for instanceName, snapst := range stateMap { 2270 if !snapst.Active { 2271 continue 2272 } 2273 snapInfo, err := snapst.CurrentInfo() 2274 if err != nil { 2275 logger.Noticef("cannot retrieve info for snap %q: %s", instanceName, err) 2276 continue 2277 } 2278 infos = append(infos, snapInfo) 2279 } 2280 return infos, nil 2281 } 2282 2283 func HasSnapOfType(st *state.State, snapType snap.Type) (bool, error) { 2284 var stateMap map[string]*SnapState 2285 if err := st.Get("snaps", &stateMap); err != nil && err != state.ErrNoState { 2286 return false, err 2287 } 2288 2289 for _, snapst := range stateMap { 2290 typ, err := snapst.Type() 2291 if err != nil { 2292 return false, err 2293 } 2294 if typ == snapType { 2295 return true, nil 2296 } 2297 } 2298 2299 return false, nil 2300 } 2301 2302 func infosForType(st *state.State, snapType snap.Type) ([]*snap.Info, error) { 2303 var stateMap map[string]*SnapState 2304 if err := st.Get("snaps", &stateMap); err != nil && err != state.ErrNoState { 2305 return nil, err 2306 } 2307 2308 var res []*snap.Info 2309 for _, snapst := range stateMap { 2310 if !snapst.IsInstalled() { 2311 continue 2312 } 2313 typ, err := snapst.Type() 2314 if err != nil { 2315 return nil, err 2316 } 2317 if typ != snapType { 2318 continue 2319 } 2320 si, err := snapst.CurrentInfo() 2321 if err != nil { 2322 return nil, err 2323 } 2324 res = append(res, si) 2325 } 2326 2327 if len(res) == 0 { 2328 return nil, state.ErrNoState 2329 } 2330 2331 return res, nil 2332 } 2333 2334 func infoForDeviceSnap(st *state.State, deviceCtx DeviceContext, which string, whichName func(*asserts.Model) string) (*snap.Info, error) { 2335 if deviceCtx == nil { 2336 return nil, fmt.Errorf("internal error: unset deviceCtx") 2337 } 2338 model := deviceCtx.Model() 2339 snapName := whichName(model) 2340 if snapName == "" { 2341 return nil, state.ErrNoState 2342 } 2343 var snapst SnapState 2344 err := Get(st, snapName, &snapst) 2345 if err != nil { 2346 return nil, err 2347 } 2348 return snapst.CurrentInfo() 2349 } 2350 2351 // GadgetInfo finds the gadget snap's info for the given device context. 2352 func GadgetInfo(st *state.State, deviceCtx DeviceContext) (*snap.Info, error) { 2353 return infoForDeviceSnap(st, deviceCtx, "gadget", (*asserts.Model).Gadget) 2354 } 2355 2356 // TODO: reintroduce a KernelInfo(state.State, DeviceContext) if needed 2357 // KernelInfo finds the current kernel snap's info. 2358 2359 // coreInfo finds the current OS snap's info. If both 2360 // "core" and "ubuntu-core" is installed then "core" 2361 // is preferred. Different core names are not supported 2362 // currently and will result in an error. 2363 func coreInfo(st *state.State) (*snap.Info, error) { 2364 res, err := infosForType(st, snap.TypeOS) 2365 if err != nil { 2366 return nil, err 2367 } 2368 2369 // a single core: just return it 2370 if len(res) == 1 { 2371 return res[0], nil 2372 } 2373 2374 // some systems have two cores: ubuntu-core/core 2375 // we always return "core" in this case 2376 if len(res) == 2 { 2377 if res[0].InstanceName() == defaultCoreSnapName && res[1].InstanceName() == "ubuntu-core" { 2378 return res[0], nil 2379 } 2380 if res[0].InstanceName() == "ubuntu-core" && res[1].InstanceName() == defaultCoreSnapName { 2381 return res[1], nil 2382 } 2383 return nil, fmt.Errorf("unexpected cores %q and %q", res[0].InstanceName(), res[1].InstanceName()) 2384 } 2385 2386 return nil, fmt.Errorf("unexpected number of cores, got %d", len(res)) 2387 } 2388 2389 // ConfigDefaults returns the configuration defaults for the snap as 2390 // specified in the gadget for the given device context. 2391 // If gadget is absent or the snap has no snap-id it returns 2392 // ErrNoState. 2393 func ConfigDefaults(st *state.State, deviceCtx DeviceContext, snapName string) (map[string]interface{}, error) { 2394 info, err := GadgetInfo(st, deviceCtx) 2395 if err != nil { 2396 return nil, err 2397 } 2398 2399 // system configuration is kept under "core" so apply its defaults when 2400 // configuring "core" 2401 isSystemDefaults := snapName == defaultCoreSnapName 2402 var snapst SnapState 2403 if err := Get(st, snapName, &snapst); err != nil && err != state.ErrNoState { 2404 return nil, err 2405 } 2406 2407 var snapID string 2408 if snapst.IsInstalled() { 2409 snapID = snapst.CurrentSideInfo().SnapID 2410 } 2411 // system snaps (core and snapd) snaps can be addressed even without a 2412 // snap-id via the special "system" value in the config; first-boot 2413 // always configures the core snap with UseConfigDefaults 2414 if snapID == "" && !isSystemDefaults { 2415 return nil, state.ErrNoState 2416 } 2417 2418 gadgetInfo, err := gadget.ReadInfo(info.MountDir(), release.OnClassic) 2419 if err != nil { 2420 return nil, err 2421 } 2422 2423 // we support setting core defaults via "system" 2424 if isSystemDefaults { 2425 if defaults, ok := gadgetInfo.Defaults["system"]; ok { 2426 if _, ok := gadgetInfo.Defaults[snapID]; ok && snapID != "" { 2427 logger.Noticef("core snap configuration defaults found under both 'system' key and core-snap-id, preferring 'system'") 2428 } 2429 2430 return defaults, nil 2431 } 2432 } 2433 2434 defaults, ok := gadgetInfo.Defaults[snapID] 2435 if !ok { 2436 return nil, state.ErrNoState 2437 } 2438 2439 return defaults, nil 2440 } 2441 2442 // GadgetConnections returns the interface connection instructions 2443 // specified in the gadget for the given device context. 2444 // If gadget is absent it returns ErrNoState. 2445 func GadgetConnections(st *state.State, deviceCtx DeviceContext) ([]gadget.Connection, error) { 2446 info, err := GadgetInfo(st, deviceCtx) 2447 if err != nil { 2448 return nil, err 2449 } 2450 2451 gadgetInfo, err := gadget.ReadInfo(info.MountDir(), release.OnClassic) 2452 if err != nil { 2453 return nil, err 2454 } 2455 2456 return gadgetInfo.Connections, nil 2457 }