github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/snapstate/handlers.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 21 22 import ( 23 "context" 24 "encoding/json" 25 "fmt" 26 "os" 27 "path/filepath" 28 "sort" 29 "strconv" 30 "strings" 31 "time" 32 33 "gopkg.in/tomb.v2" 34 35 "github.com/snapcore/snapd/boot" 36 "github.com/snapcore/snapd/dirs" 37 "github.com/snapcore/snapd/features" 38 "github.com/snapcore/snapd/i18n" 39 "github.com/snapcore/snapd/logger" 40 "github.com/snapcore/snapd/osutil" 41 "github.com/snapcore/snapd/overlord/auth" 42 "github.com/snapcore/snapd/overlord/configstate/config" 43 "github.com/snapcore/snapd/overlord/configstate/settings" 44 "github.com/snapcore/snapd/overlord/snapstate/backend" 45 "github.com/snapcore/snapd/overlord/state" 46 "github.com/snapcore/snapd/progress" 47 "github.com/snapcore/snapd/release" 48 "github.com/snapcore/snapd/snap" 49 "github.com/snapcore/snapd/store" 50 "github.com/snapcore/snapd/strutil" 51 "github.com/snapcore/snapd/timings" 52 ) 53 54 // TaskSnapSetup returns the SnapSetup with task params hold by or referred to by the task. 55 func TaskSnapSetup(t *state.Task) (*SnapSetup, error) { 56 var snapsup SnapSetup 57 58 err := t.Get("snap-setup", &snapsup) 59 if err != nil && err != state.ErrNoState { 60 return nil, err 61 } 62 if err == nil { 63 return &snapsup, nil 64 } 65 66 var id string 67 err = t.Get("snap-setup-task", &id) 68 if err != nil { 69 return nil, err 70 } 71 72 ts := t.State().Task(id) 73 if ts == nil { 74 return nil, fmt.Errorf("internal error: tasks are being pruned") 75 } 76 if err := ts.Get("snap-setup", &snapsup); err != nil { 77 return nil, err 78 } 79 return &snapsup, nil 80 } 81 82 // SetTaskSnapSetup writes the given SnapSetup to the provided task's 83 // snap-setup-task Task, or to the task itself if the task does not have a 84 // snap-setup-task (i.e. it _is_ the snap-setup-task) 85 func SetTaskSnapSetup(t *state.Task, snapsup *SnapSetup) error { 86 if t.Has("snap-setup") { 87 // this is the snap-setup-task so just write to the task directly 88 t.Set("snap-setup", snapsup) 89 } else { 90 // this task isn't the snap-setup-task, so go get that and write to that 91 // one 92 var id string 93 err := t.Get("snap-setup-task", &id) 94 if err != nil { 95 return err 96 } 97 98 ts := t.State().Task(id) 99 if ts == nil { 100 return fmt.Errorf("internal error: tasks are being pruned") 101 } 102 ts.Set("snap-setup", snapsup) 103 } 104 105 return nil 106 } 107 108 func snapSetupAndState(t *state.Task) (*SnapSetup, *SnapState, error) { 109 snapsup, err := TaskSnapSetup(t) 110 if err != nil { 111 return nil, nil, err 112 } 113 var snapst SnapState 114 err = Get(t.State(), snapsup.InstanceName(), &snapst) 115 if err != nil && err != state.ErrNoState { 116 return nil, nil, err 117 } 118 return snapsup, &snapst, nil 119 } 120 121 /* State Locking 122 123 do* / undo* handlers should usually lock the state just once with: 124 125 st.Lock() 126 defer st.Unlock() 127 128 For tasks doing slow operations (long i/o, networking operations) it's OK 129 to unlock the state temporarily: 130 131 st.Unlock() 132 err := slowIOOp() 133 st.Lock() 134 if err != nil { 135 ... 136 } 137 138 but if a task Get and then Set the SnapState of a snap it must avoid 139 releasing the state lock in between, other tasks might have 140 reasons to update the SnapState independently: 141 142 // DO NOT DO THIS!: 143 snapst := ... 144 snapst.Attr = ... 145 st.Unlock() 146 ... 147 st.Lock() 148 Set(st, snapName, snapst) 149 150 if a task really needs to mix mutating a SnapState and releasing the state 151 lock it should be serialized at the task runner level, see 152 SnapManger.blockedTask and TaskRunner.SetBlocked 153 154 */ 155 156 const defaultCoreSnapName = "core" 157 158 func defaultBaseSnapsChannel() string { 159 channel := os.Getenv("SNAPD_BASES_CHANNEL") 160 if channel == "" { 161 return "stable" 162 } 163 return channel 164 } 165 166 func defaultSnapdSnapsChannel() string { 167 channel := os.Getenv("SNAPD_SNAPD_CHANNEL") 168 if channel == "" { 169 return "stable" 170 } 171 return channel 172 } 173 174 func defaultPrereqSnapsChannel() string { 175 channel := os.Getenv("SNAPD_PREREQS_CHANNEL") 176 if channel == "" { 177 return "stable" 178 } 179 return channel 180 } 181 182 func linkSnapInFlight(st *state.State, snapName string) (bool, error) { 183 for _, chg := range st.Changes() { 184 if chg.Status().Ready() { 185 continue 186 } 187 for _, tc := range chg.Tasks() { 188 if tc.Status().Ready() { 189 continue 190 } 191 if tc.Kind() == "link-snap" { 192 snapsup, err := TaskSnapSetup(tc) 193 if err != nil { 194 return false, err 195 } 196 if snapsup.InstanceName() == snapName { 197 return true, nil 198 } 199 } 200 } 201 } 202 203 return false, nil 204 } 205 206 func isInstalled(st *state.State, snapName string) (bool, error) { 207 var snapState SnapState 208 err := Get(st, snapName, &snapState) 209 if err != nil && err != state.ErrNoState { 210 return false, err 211 } 212 return snapState.IsInstalled(), nil 213 } 214 215 // timeout for tasks to check if the prerequisites are ready 216 var prerequisitesRetryTimeout = 30 * time.Second 217 218 func (m *SnapManager) doPrerequisites(t *state.Task, _ *tomb.Tomb) error { 219 st := t.State() 220 st.Lock() 221 defer st.Unlock() 222 223 perfTimings := state.TimingsForTask(t) 224 defer perfTimings.Save(st) 225 226 // check if we need to inject tasks to install core 227 snapsup, _, err := snapSetupAndState(t) 228 if err != nil { 229 return err 230 } 231 232 // os/base/kernel/gadget cannot have prerequisites other 233 // than the models default base (or core) which is installed anyway 234 switch snapsup.Type { 235 case snap.TypeOS, snap.TypeBase, snap.TypeKernel, snap.TypeGadget: 236 return nil 237 } 238 // snapd is special and has no prereqs 239 if snapsup.Type == snap.TypeSnapd { 240 return nil 241 } 242 243 // we need to make sure we install all prereqs together in one 244 // operation 245 base := defaultCoreSnapName 246 if snapsup.Base != "" { 247 base = snapsup.Base 248 } 249 250 if err := m.installPrereqs(t, base, snapsup.Prereq, snapsup.UserID, perfTimings); err != nil { 251 return err 252 } 253 254 return nil 255 } 256 257 func (m *SnapManager) installOneBaseOrRequired(st *state.State, snapName string, requireTypeBase bool, channel string, onInFlight error, userID int) (*state.TaskSet, error) { 258 // The core snap provides everything we need for core16. 259 coreInstalled, err := isInstalled(st, "core") 260 if err != nil { 261 return nil, err 262 } 263 if snapName == "core16" && coreInstalled { 264 return nil, nil 265 } 266 267 // installed already? 268 isInstalled, err := isInstalled(st, snapName) 269 if err != nil { 270 return nil, err 271 } 272 if isInstalled { 273 return nil, nil 274 } 275 // in progress? 276 inFlight, err := linkSnapInFlight(st, snapName) 277 if err != nil { 278 return nil, err 279 } 280 if inFlight { 281 return nil, onInFlight 282 } 283 284 // not installed, nor queued for install -> install it 285 ts, err := Install(context.TODO(), st, snapName, &RevisionOptions{Channel: channel}, userID, Flags{RequireTypeBase: requireTypeBase}) 286 287 // something might have triggered an explicit install while 288 // the state was unlocked -> deal with that here by simply 289 // retrying the operation. 290 if _, ok := err.(*ChangeConflictError); ok { 291 return nil, &state.Retry{After: prerequisitesRetryTimeout} 292 } 293 return ts, err 294 } 295 296 func (m *SnapManager) installPrereqs(t *state.Task, base string, prereq []string, userID int, tm timings.Measurer) error { 297 st := t.State() 298 299 // We try to install all wanted snaps. If one snap cannot be installed 300 // because of change conflicts or similar we retry. Only if all snaps 301 // can be installed together we add the tasks to the change. 302 var tss []*state.TaskSet 303 for _, prereqName := range prereq { 304 var onInFlightErr error = nil 305 var err error 306 var ts *state.TaskSet 307 timings.Run(tm, "install-prereq", fmt.Sprintf("install %q", prereqName), func(timings.Measurer) { 308 noTypeBaseCheck := false 309 ts, err = m.installOneBaseOrRequired(st, prereqName, noTypeBaseCheck, defaultPrereqSnapsChannel(), onInFlightErr, userID) 310 }) 311 if err != nil { 312 return prereqError("prerequisite", prereqName, err) 313 } 314 if ts == nil { 315 continue 316 } 317 tss = append(tss, ts) 318 } 319 320 // for base snaps we need to wait until the change is done 321 // (either finished or failed) 322 onInFlightErr := &state.Retry{After: prerequisitesRetryTimeout} 323 324 var tsBase *state.TaskSet 325 var err error 326 if base != "none" { 327 timings.Run(tm, "install-prereq", fmt.Sprintf("install base %q", base), func(timings.Measurer) { 328 requireTypeBase := true 329 tsBase, err = m.installOneBaseOrRequired(st, base, requireTypeBase, defaultBaseSnapsChannel(), onInFlightErr, userID) 330 }) 331 if err != nil { 332 return prereqError("snap base", base, err) 333 } 334 } 335 336 // on systems without core or snapd need to install snapd to 337 // make interfaces work - LP: 1819318 338 var tsSnapd *state.TaskSet 339 snapdSnapInstalled, err := isInstalled(st, "snapd") 340 if err != nil { 341 return err 342 } 343 coreSnapInstalled, err := isInstalled(st, "core") 344 if err != nil { 345 return err 346 } 347 if base != "core" && !snapdSnapInstalled && !coreSnapInstalled { 348 timings.Run(tm, "install-prereq", "install snapd", func(timings.Measurer) { 349 noTypeBaseCheck := false 350 tsSnapd, err = m.installOneBaseOrRequired(st, "snapd", noTypeBaseCheck, defaultSnapdSnapsChannel(), onInFlightErr, userID) 351 }) 352 if err != nil { 353 return prereqError("system snap", "snapd", err) 354 } 355 } 356 357 chg := t.Change() 358 // add all required snaps, no ordering, this will be done in the 359 // auto-connect task handler 360 for _, ts := range tss { 361 ts.JoinLane(st.NewLane()) 362 chg.AddAll(ts) 363 } 364 // add the base if needed, prereqs else must wait on this 365 if tsBase != nil { 366 tsBase.JoinLane(st.NewLane()) 367 for _, t := range chg.Tasks() { 368 t.WaitAll(tsBase) 369 } 370 chg.AddAll(tsBase) 371 } 372 // add snapd if needed, everything must wait on this 373 if tsSnapd != nil { 374 tsSnapd.JoinLane(st.NewLane()) 375 for _, t := range chg.Tasks() { 376 t.WaitAll(tsSnapd) 377 } 378 chg.AddAll(tsSnapd) 379 } 380 381 // make sure that the new change is committed to the state 382 // together with marking this task done 383 t.SetStatus(state.DoneStatus) 384 385 return nil 386 } 387 388 func prereqError(what, snapName string, err error) error { 389 if _, ok := err.(*state.Retry); ok { 390 return err 391 } 392 return fmt.Errorf("cannot install %s %q: %v", what, snapName, err) 393 } 394 395 func (m *SnapManager) doPrepareSnap(t *state.Task, _ *tomb.Tomb) error { 396 st := t.State() 397 st.Lock() 398 defer st.Unlock() 399 snapsup, snapst, err := snapSetupAndState(t) 400 if err != nil { 401 return err 402 } 403 404 if snapsup.Revision().Unset() { 405 // Local revisions start at -1 and go down. 406 revision := snapst.LocalRevision() 407 if revision.Unset() || revision.N > 0 { 408 revision = snap.R(-1) 409 } else { 410 revision.N-- 411 } 412 if !revision.Local() { 413 panic("internal error: invalid local revision built: " + revision.String()) 414 } 415 snapsup.SideInfo.Revision = revision 416 } 417 418 t.Set("snap-setup", snapsup) 419 return nil 420 } 421 422 func (m *SnapManager) undoPrepareSnap(t *state.Task, _ *tomb.Tomb) error { 423 st := t.State() 424 st.Lock() 425 defer st.Unlock() 426 427 snapsup, err := TaskSnapSetup(t) 428 if err != nil { 429 return err 430 } 431 432 if snapsup.SideInfo == nil || snapsup.SideInfo.RealName == "" { 433 return nil 434 } 435 436 var logMsg []string 437 var snapSetup string 438 dupSig := []string{"snap-install:"} 439 chg := t.Change() 440 logMsg = append(logMsg, fmt.Sprintf("change %q: %q", chg.Kind(), chg.Summary())) 441 for _, t := range chg.Tasks() { 442 // TODO: report only tasks in intersecting lanes? 443 tintro := fmt.Sprintf("%s: %s", t.Kind(), t.Status()) 444 logMsg = append(logMsg, tintro) 445 dupSig = append(dupSig, tintro) 446 if snapsup, err := TaskSnapSetup(t); err == nil && snapsup.SideInfo != nil { 447 snapSetup1 := fmt.Sprintf(" snap-setup: %q (%v) %q", snapsup.SideInfo.RealName, snapsup.SideInfo.Revision, snapsup.SideInfo.Channel) 448 if snapSetup1 != snapSetup { 449 snapSetup = snapSetup1 450 logMsg = append(logMsg, snapSetup) 451 dupSig = append(dupSig, fmt.Sprintf(" snap-setup: %q", snapsup.SideInfo.RealName)) 452 } 453 } 454 for _, l := range t.Log() { 455 // cut of the rfc339 timestamp to ensure duplicate 456 // detection works in daisy 457 tStampLen := strings.Index(l, " ") 458 if tStampLen < 0 { 459 continue 460 } 461 // not tStampLen+1 because the indent is nice 462 entry := l[tStampLen:] 463 logMsg = append(logMsg, entry) 464 dupSig = append(dupSig, entry) 465 } 466 } 467 468 var ubuntuCoreTransitionCount int 469 err = st.Get("ubuntu-core-transition-retry", &ubuntuCoreTransitionCount) 470 if err != nil && err != state.ErrNoState { 471 return err 472 } 473 extra := map[string]string{ 474 "Channel": snapsup.Channel, 475 "Revision": snapsup.SideInfo.Revision.String(), 476 } 477 if ubuntuCoreTransitionCount > 0 { 478 extra["UbuntuCoreTransitionCount"] = strconv.Itoa(ubuntuCoreTransitionCount) 479 } 480 481 // Only report and error if there is an actual error in the change, 482 // we could undo things because the user canceled the change. 483 var isErr bool 484 for _, tt := range t.Change().Tasks() { 485 if tt.Status() == state.ErrorStatus { 486 isErr = true 487 break 488 } 489 } 490 if isErr && !settings.ProblemReportsDisabled(st) { 491 st.Unlock() 492 oopsid, err := errtrackerReport(snapsup.SideInfo.RealName, strings.Join(logMsg, "\n"), strings.Join(dupSig, "\n"), extra) 493 st.Lock() 494 if err == nil { 495 logger.Noticef("Reported install problem for %q as %s", snapsup.SideInfo.RealName, oopsid) 496 } else { 497 logger.Debugf("Cannot report problem: %s", err) 498 } 499 } 500 501 return nil 502 } 503 504 func installInfoUnlocked(st *state.State, snapsup *SnapSetup, deviceCtx DeviceContext) (store.SnapActionResult, error) { 505 st.Lock() 506 defer st.Unlock() 507 opts := &RevisionOptions{Channel: snapsup.Channel, CohortKey: snapsup.CohortKey, Revision: snapsup.Revision()} 508 return installInfo(context.TODO(), st, snapsup.InstanceName(), opts, snapsup.UserID, deviceCtx) 509 } 510 511 // autoRefreshRateLimited returns the rate limit of auto-refreshes or 0 if 512 // there is no limit. 513 func autoRefreshRateLimited(st *state.State) (rate int64) { 514 tr := config.NewTransaction(st) 515 516 var rateLimit string 517 err := tr.Get("core", "refresh.rate-limit", &rateLimit) 518 if err != nil { 519 return 0 520 } 521 // NOTE ParseByteSize errors on negative rates 522 val, err := strutil.ParseByteSize(rateLimit) 523 if err != nil { 524 return 0 525 } 526 return val 527 } 528 529 func downloadSnapParams(st *state.State, t *state.Task) (*SnapSetup, StoreService, *auth.UserState, error) { 530 snapsup, err := TaskSnapSetup(t) 531 if err != nil { 532 return nil, nil, nil, err 533 } 534 535 deviceCtx, err := DeviceCtx(st, t, nil) 536 if err != nil { 537 return nil, nil, nil, err 538 } 539 540 sto := Store(st, deviceCtx) 541 542 user, err := userFromUserID(st, snapsup.UserID) 543 if err != nil { 544 return nil, nil, nil, err 545 } 546 547 return snapsup, sto, user, nil 548 } 549 550 func (m *SnapManager) doDownloadSnap(t *state.Task, tomb *tomb.Tomb) error { 551 st := t.State() 552 var rate int64 553 554 st.Lock() 555 perfTimings := state.TimingsForTask(t) 556 snapsup, theStore, user, err := downloadSnapParams(st, t) 557 if snapsup != nil && snapsup.IsAutoRefresh { 558 // NOTE rate is never negative 559 rate = autoRefreshRateLimited(st) 560 } 561 st.Unlock() 562 if err != nil { 563 return err 564 } 565 566 meter := NewTaskProgressAdapterUnlocked(t) 567 targetFn := snapsup.MountFile() 568 569 dlOpts := &store.DownloadOptions{ 570 IsAutoRefresh: snapsup.IsAutoRefresh, 571 RateLimit: rate, 572 } 573 if snapsup.DownloadInfo == nil { 574 var storeInfo store.SnapActionResult 575 // COMPATIBILITY - this task was created from an older version 576 // of snapd that did not store the DownloadInfo in the state 577 // yet. Therefore do not worry about DeviceContext. 578 storeInfo, err = installInfoUnlocked(st, snapsup, nil) 579 if err != nil { 580 return err 581 } 582 timings.Run(perfTimings, "download", fmt.Sprintf("download snap %q", snapsup.SnapName()), func(timings.Measurer) { 583 err = theStore.Download(tomb.Context(nil), snapsup.SnapName(), targetFn, &storeInfo.DownloadInfo, meter, user, dlOpts) 584 }) 585 snapsup.SideInfo = &storeInfo.SideInfo 586 } else { 587 timings.Run(perfTimings, "download", fmt.Sprintf("download snap %q", snapsup.SnapName()), func(timings.Measurer) { 588 err = theStore.Download(tomb.Context(nil), snapsup.SnapName(), targetFn, snapsup.DownloadInfo, meter, user, dlOpts) 589 }) 590 } 591 if err != nil { 592 return err 593 } 594 595 snapsup.SnapPath = targetFn 596 597 // update the snap setup for the follow up tasks 598 st.Lock() 599 t.Set("snap-setup", snapsup) 600 perfTimings.Save(st) 601 st.Unlock() 602 603 return nil 604 } 605 606 var ( 607 mountPollInterval = 1 * time.Second 608 ) 609 610 // hasOtherInstances checks whether there are other instances of the snap, be it 611 // instance keyed or not 612 func hasOtherInstances(st *state.State, instanceName string) (bool, error) { 613 snapName, _ := snap.SplitInstanceName(instanceName) 614 var all map[string]*json.RawMessage 615 if err := st.Get("snaps", &all); err != nil && err != state.ErrNoState { 616 return false, err 617 } 618 for otherName := range all { 619 if otherName == instanceName { 620 continue 621 } 622 if otherSnapName, _ := snap.SplitInstanceName(otherName); otherSnapName == snapName { 623 return true, nil 624 } 625 } 626 return false, nil 627 } 628 629 func (m *SnapManager) doMountSnap(t *state.Task, _ *tomb.Tomb) error { 630 st := t.State() 631 st.Lock() 632 perfTimings := state.TimingsForTask(t) 633 snapsup, snapst, err := snapSetupAndState(t) 634 st.Unlock() 635 if err != nil { 636 return err 637 } 638 639 curInfo, err := snapst.CurrentInfo() 640 if err != nil && err != ErrNoCurrent { 641 return err 642 } 643 644 m.backend.CurrentInfo(curInfo) 645 646 st.Lock() 647 deviceCtx, err := DeviceCtx(t.State(), t, nil) 648 st.Unlock() 649 if err != nil { 650 return err 651 } 652 653 timings.Run(perfTimings, "check-snap", fmt.Sprintf("check snap %q", snapsup.InstanceName()), func(timings.Measurer) { 654 err = checkSnap(st, snapsup.SnapPath, snapsup.InstanceName(), snapsup.SideInfo, curInfo, snapsup.Flags, deviceCtx) 655 }) 656 if err != nil { 657 return err 658 } 659 660 cleanup := func() { 661 st.Lock() 662 defer st.Unlock() 663 664 otherInstances, err := hasOtherInstances(st, snapsup.InstanceName()) 665 if err != nil { 666 t.Errorf("cannot cleanup partial setup snap %q: %v", snapsup.InstanceName(), err) 667 return 668 } 669 670 // remove snap dir is idempotent so it's ok to always call it in the cleanup path 671 if err := m.backend.RemoveSnapDir(snapsup.placeInfo(), otherInstances); err != nil { 672 t.Errorf("cannot cleanup partial setup snap %q: %v", snapsup.InstanceName(), err) 673 } 674 675 } 676 677 pb := NewTaskProgressAdapterUnlocked(t) 678 // TODO Use snapsup.Revision() to obtain the right info to mount 679 // instead of assuming the candidate is the right one. 680 var snapType snap.Type 681 var installRecord *backend.InstallRecord 682 timings.Run(perfTimings, "setup-snap", fmt.Sprintf("setup snap %q", snapsup.InstanceName()), func(timings.Measurer) { 683 snapType, installRecord, err = m.backend.SetupSnap(snapsup.SnapPath, snapsup.InstanceName(), snapsup.SideInfo, deviceCtx, pb) 684 }) 685 if err != nil { 686 cleanup() 687 return err 688 } 689 690 // double check that the snap is mounted 691 var readInfoErr error 692 for i := 0; i < 10; i++ { 693 _, readInfoErr = readInfo(snapsup.InstanceName(), snapsup.SideInfo, errorOnBroken) 694 if readInfoErr == nil { 695 break 696 } 697 if _, ok := readInfoErr.(*snap.NotFoundError); !ok { 698 break 699 } 700 // snap not found, seems is not mounted yet 701 msg := fmt.Sprintf("expected snap %q revision %v to be mounted but is not", snapsup.InstanceName(), snapsup.Revision()) 702 readInfoErr = fmt.Errorf("cannot proceed, %s", msg) 703 if i == 0 { 704 logger.Noticef(msg) 705 } 706 time.Sleep(mountPollInterval) 707 } 708 if readInfoErr != nil { 709 timings.Run(perfTimings, "undo-setup-snap", fmt.Sprintf("Undo setup of snap %q", snapsup.InstanceName()), func(timings.Measurer) { 710 err = m.backend.UndoSetupSnap(snapsup.placeInfo(), snapType, installRecord, deviceCtx, pb) 711 }) 712 if err != nil { 713 st.Lock() 714 t.Errorf("cannot undo partial setup snap %q: %v", snapsup.InstanceName(), err) 715 st.Unlock() 716 } 717 718 cleanup() 719 return readInfoErr 720 } 721 722 st.Lock() 723 // set snapst type for undoMountSnap 724 t.Set("snap-type", snapType) 725 if installRecord != nil { 726 t.Set("install-record", installRecord) 727 } 728 st.Unlock() 729 730 if snapsup.Flags.RemoveSnapPath { 731 if err := os.Remove(snapsup.SnapPath); err != nil { 732 logger.Noticef("Failed to cleanup %s: %s", snapsup.SnapPath, err) 733 } 734 } 735 736 st.Lock() 737 perfTimings.Save(st) 738 st.Unlock() 739 740 return nil 741 } 742 743 func (m *SnapManager) undoMountSnap(t *state.Task, _ *tomb.Tomb) error { 744 st := t.State() 745 st.Lock() 746 snapsup, err := TaskSnapSetup(t) 747 st.Unlock() 748 if err != nil { 749 return err 750 } 751 752 st.Lock() 753 deviceCtx, err := DeviceCtx(t.State(), t, nil) 754 st.Unlock() 755 if err != nil { 756 return err 757 } 758 759 st.Lock() 760 var typ snap.Type 761 err = t.Get("snap-type", &typ) 762 st.Unlock() 763 // backward compatibility 764 if err == state.ErrNoState { 765 typ = "app" 766 } else if err != nil { 767 return err 768 } 769 770 var installRecord backend.InstallRecord 771 st.Lock() 772 // install-record is optional (e.g. not present in tasks from older snapd) 773 err = t.Get("install-record", &installRecord) 774 st.Unlock() 775 if err != nil && err != state.ErrNoState { 776 return err 777 } 778 779 pb := NewTaskProgressAdapterUnlocked(t) 780 if err := m.backend.UndoSetupSnap(snapsup.placeInfo(), typ, &installRecord, deviceCtx, pb); err != nil { 781 return err 782 } 783 784 st.Lock() 785 defer st.Unlock() 786 787 otherInstances, err := hasOtherInstances(st, snapsup.InstanceName()) 788 if err != nil { 789 return err 790 } 791 792 return m.backend.RemoveSnapDir(snapsup.placeInfo(), otherInstances) 793 } 794 795 // queryDisabledServices uses wrappers.QueryDisabledServices() 796 // 797 // Note this function takes a snap info rather than snapst because there are 798 // situations where we want to call this on non-current snap infos, i.e. in the 799 // undo handlers, see undoLinkSnap for an example. 800 func (m *SnapManager) queryDisabledServices(info *snap.Info, pb progress.Meter) ([]string, error) { 801 return m.backend.QueryDisabledServices(info, pb) 802 } 803 804 func (m *SnapManager) doUnlinkCurrentSnap(t *state.Task, _ *tomb.Tomb) error { 805 st := t.State() 806 st.Lock() 807 defer st.Unlock() 808 809 snapsup, snapst, err := snapSetupAndState(t) 810 if err != nil { 811 return err 812 } 813 814 oldInfo, err := snapst.CurrentInfo() 815 if err != nil { 816 return err 817 } 818 819 // add to the disabled services list in snapst services which were disabled 820 // when stop-snap-services ran, for usage across changes like in reverting 821 // and enabling after being disabled. 822 // we keep what's already in the list in snapst because that list is 823 // services which were previously present in the snap and disabled, but are 824 // no longer present. 825 snapst.LastActiveDisabledServices = append( 826 snapst.LastActiveDisabledServices, 827 snapsup.LastActiveDisabledServices..., 828 ) 829 830 tr := config.NewTransaction(st) 831 experimentalRefreshAppAwareness, err := features.Flag(tr, features.RefreshAppAwareness) 832 if err != nil && !config.IsNoOption(err) { 833 return err 834 } 835 836 if experimentalRefreshAppAwareness { 837 // A process may be created after the soft refresh done upon 838 // the request to refresh a snap. If such process is alive by 839 // the time this code is reached the refresh process is stopped. 840 // In case of failure the snap state is modified to indicate 841 // when the refresh was first inhibited. If the first 842 // inhibition is outside of a grace period then refresh 843 // proceeds regardless of the existing processes. 844 if err := inhibitRefresh(st, snapst, oldInfo, HardNothingRunningRefreshCheck); err != nil { 845 return err 846 } 847 } 848 849 snapst.Active = false 850 851 // do the final unlink 852 linkCtx := backend.LinkContext{ 853 FirstInstall: false, 854 } 855 err = m.backend.UnlinkSnap(oldInfo, linkCtx, NewTaskProgressAdapterLocked(t)) 856 if err != nil { 857 return err 858 } 859 860 // mark as inactive 861 Set(st, snapsup.InstanceName(), snapst) 862 return nil 863 } 864 865 func (m *SnapManager) undoUnlinkCurrentSnap(t *state.Task, _ *tomb.Tomb) error { 866 st := t.State() 867 st.Lock() 868 defer st.Unlock() 869 870 perfTimings := state.TimingsForTask(t) 871 defer perfTimings.Save(st) 872 873 snapsup, snapst, err := snapSetupAndState(t) 874 if err != nil { 875 return err 876 } 877 878 oldInfo, err := snapst.CurrentInfo() 879 if err != nil { 880 return err 881 } 882 883 deviceCtx, err := DeviceCtx(st, t, nil) 884 if err != nil { 885 return err 886 } 887 888 // get the services which LinkSnap should disable when generating wrappers, 889 // as well as the services which are not present in this revision, but were 890 // present and disabled in a previous one and as such should be kept inside 891 // snapst for persistent storage. 892 svcsToSave, svcsToDisable, err := missingDisabledServices(snapst.LastActiveDisabledServices, oldInfo) 893 if err != nil { 894 return err 895 } 896 897 snapst.Active = true 898 vitalityRank, err := vitalityRank(st, snapsup.InstanceName()) 899 if err != nil { 900 return err 901 } 902 linkCtx := backend.LinkContext{ 903 PrevDisabledServices: svcsToDisable, 904 FirstInstall: false, 905 VitalityRank: vitalityRank, 906 } 907 reboot, err := m.backend.LinkSnap(oldInfo, deviceCtx, linkCtx, perfTimings) 908 if err != nil { 909 return err 910 } 911 912 // re-save the missing services so when we unlink this revision and go to a 913 // different revision with potentially different service names, the 914 // currently missing service names will be re-disabled if they exist later 915 snapst.LastActiveDisabledServices = svcsToSave 916 917 // mark as active again 918 Set(st, snapsup.InstanceName(), snapst) 919 920 // if we just put back a previous a core snap, request a restart 921 // so that we switch executing its snapd 922 m.maybeRestart(t, oldInfo, reboot, deviceCtx) 923 924 return nil 925 } 926 927 func (m *SnapManager) doCopySnapData(t *state.Task, _ *tomb.Tomb) error { 928 st := t.State() 929 st.Lock() 930 snapsup, snapst, err := snapSetupAndState(t) 931 st.Unlock() 932 if err != nil { 933 return err 934 } 935 936 newInfo, err := readInfo(snapsup.InstanceName(), snapsup.SideInfo, 0) 937 if err != nil { 938 return err 939 } 940 941 oldInfo, err := snapst.CurrentInfo() 942 if err != nil && err != ErrNoCurrent { 943 return err 944 } 945 946 pb := NewTaskProgressAdapterUnlocked(t) 947 if copyDataErr := m.backend.CopySnapData(newInfo, oldInfo, pb); copyDataErr != nil { 948 if oldInfo != nil { 949 // there is another revision of the snap, cannot remove 950 // shared data directory 951 return copyDataErr 952 } 953 954 // cleanup shared snap data directory 955 st.Lock() 956 defer st.Unlock() 957 958 otherInstances, err := hasOtherInstances(st, snapsup.InstanceName()) 959 if err != nil { 960 t.Errorf("cannot undo partial snap %q data copy: %v", snapsup.InstanceName(), err) 961 return copyDataErr 962 } 963 // no other instances of this snap, shared data directory can be 964 // removed now too 965 if err := m.backend.RemoveSnapDataDir(newInfo, otherInstances); err != nil { 966 t.Errorf("cannot undo partial snap %q data copy, failed removing shared directory: %v", snapsup.InstanceName(), err) 967 } 968 return copyDataErr 969 } 970 return nil 971 } 972 973 func (m *SnapManager) undoCopySnapData(t *state.Task, _ *tomb.Tomb) error { 974 st := t.State() 975 st.Lock() 976 snapsup, snapst, err := snapSetupAndState(t) 977 st.Unlock() 978 if err != nil { 979 return err 980 } 981 982 newInfo, err := readInfo(snapsup.InstanceName(), snapsup.SideInfo, 0) 983 if err != nil { 984 return err 985 } 986 987 oldInfo, err := snapst.CurrentInfo() 988 if err != nil && err != ErrNoCurrent { 989 return err 990 } 991 992 pb := NewTaskProgressAdapterUnlocked(t) 993 if err := m.backend.UndoCopySnapData(newInfo, oldInfo, pb); err != nil { 994 return err 995 } 996 997 if oldInfo != nil { 998 // there is other revision of this snap, cannot remove shared 999 // directory anyway 1000 return nil 1001 } 1002 1003 st.Lock() 1004 defer st.Unlock() 1005 1006 otherInstances, err := hasOtherInstances(st, snapsup.InstanceName()) 1007 if err != nil { 1008 return err 1009 } 1010 // no other instances of this snap and no other revisions, shared data 1011 // directory can be removed 1012 if err := m.backend.RemoveSnapDataDir(newInfo, otherInstances); err != nil { 1013 return err 1014 } 1015 return nil 1016 } 1017 1018 func (m *SnapManager) cleanupCopySnapData(t *state.Task, _ *tomb.Tomb) error { 1019 st := t.State() 1020 st.Lock() 1021 defer st.Unlock() 1022 1023 if t.Status() != state.DoneStatus { 1024 // it failed 1025 return nil 1026 } 1027 1028 _, snapst, err := snapSetupAndState(t) 1029 if err != nil { 1030 return err 1031 } 1032 1033 info, err := snapst.CurrentInfo() 1034 if err != nil { 1035 return err 1036 } 1037 1038 m.backend.ClearTrashedData(info) 1039 1040 return nil 1041 } 1042 1043 // writeSeqFile writes the sequence file for failover handling 1044 func writeSeqFile(name string, snapst *SnapState) error { 1045 p := filepath.Join(dirs.SnapSeqDir, name+".json") 1046 if err := os.MkdirAll(filepath.Dir(p), 0755); err != nil { 1047 return err 1048 } 1049 1050 b, err := json.Marshal(&struct { 1051 Sequence []*snap.SideInfo `json:"sequence"` 1052 Current string `json:"current"` 1053 }{ 1054 Sequence: snapst.Sequence, 1055 Current: snapst.Current.String(), 1056 }) 1057 if err != nil { 1058 return err 1059 } 1060 1061 return osutil.AtomicWriteFile(p, b, 0644, 0) 1062 } 1063 1064 // missingDisabledServices returns a list of services that were disabled 1065 // that are currently missing from the specific snap info (i.e. they were 1066 // renamed in this snap info), as well as a list of disabled services that are 1067 // present in this snap info. 1068 // the first arg is the disabled services when the snap was last active 1069 func missingDisabledServices(svcs []string, info *snap.Info) ([]string, []string, error) { 1070 // make a copy of all the previously disabled services that we will remove 1071 // from, as well as an empty list to add to for the found services 1072 missingSvcs := []string{} 1073 foundSvcs := []string{} 1074 1075 // for all the previously disabled services, check if they are in the 1076 // current snap info revision as services or not 1077 for _, disabledSvcName := range svcs { 1078 // check if the service is an app _and_ is a service 1079 if app, ok := info.Apps[disabledSvcName]; ok && app.IsService() { 1080 foundSvcs = append(foundSvcs, disabledSvcName) 1081 } else { 1082 missingSvcs = append(missingSvcs, disabledSvcName) 1083 } 1084 } 1085 1086 // sort the lists for easier testing 1087 sort.Strings(missingSvcs) 1088 sort.Strings(foundSvcs) 1089 1090 return missingSvcs, foundSvcs, nil 1091 } 1092 1093 func vitalityRank(st *state.State, instanceName string) (rank int, err error) { 1094 tr := config.NewTransaction(st) 1095 1096 var vitalityStr string 1097 err = tr.GetMaybe("core", "resilience.vitality-hint", &vitalityStr) 1098 if err != nil { 1099 return 0, err 1100 } 1101 for i, s := range strings.Split(vitalityStr, ",") { 1102 if s == instanceName { 1103 return i + 1, nil 1104 } 1105 } 1106 return 0, nil 1107 } 1108 1109 func (m *SnapManager) doLinkSnap(t *state.Task, _ *tomb.Tomb) (err error) { 1110 st := t.State() 1111 st.Lock() 1112 defer st.Unlock() 1113 1114 perfTimings := state.TimingsForTask(t) 1115 defer perfTimings.Save(st) 1116 1117 snapsup, snapst, err := snapSetupAndState(t) 1118 if err != nil { 1119 return err 1120 } 1121 1122 deviceCtx, err := DeviceCtx(st, t, nil) 1123 if err != nil { 1124 return err 1125 } 1126 1127 // find if the snap is already installed before we modify snapst below 1128 isInstalled := snapst.IsInstalled() 1129 1130 cand := snapsup.SideInfo 1131 m.backend.Candidate(cand) 1132 1133 oldCandidateIndex := snapst.LastIndex(cand.Revision) 1134 1135 if oldCandidateIndex < 0 { 1136 snapst.Sequence = append(snapst.Sequence, cand) 1137 } else if !snapsup.Revert { 1138 // remove the old candidate from the sequence, add it at the end 1139 copy(snapst.Sequence[oldCandidateIndex:len(snapst.Sequence)-1], snapst.Sequence[oldCandidateIndex+1:]) 1140 snapst.Sequence[len(snapst.Sequence)-1] = cand 1141 } 1142 1143 oldCurrent := snapst.Current 1144 snapst.Current = cand.Revision 1145 snapst.Active = true 1146 oldChannel := snapst.TrackingChannel 1147 if snapsup.Channel != "" { 1148 err := snapst.SetTrackingChannel(snapsup.Channel) 1149 if err != nil { 1150 return err 1151 } 1152 } 1153 oldIgnoreValidation := snapst.IgnoreValidation 1154 snapst.IgnoreValidation = snapsup.IgnoreValidation 1155 oldTryMode := snapst.TryMode 1156 snapst.TryMode = snapsup.TryMode 1157 oldDevMode := snapst.DevMode 1158 snapst.DevMode = snapsup.DevMode 1159 oldJailMode := snapst.JailMode 1160 snapst.JailMode = snapsup.JailMode 1161 oldClassic := snapst.Classic 1162 snapst.Classic = snapsup.Classic 1163 oldCohortKey := snapst.CohortKey 1164 snapst.CohortKey = snapsup.CohortKey 1165 if snapsup.Required { // set only on install and left alone on refresh 1166 snapst.Required = true 1167 } 1168 oldRefreshInhibitedTime := snapst.RefreshInhibitedTime 1169 // only set userID if unset or logged out in snapst and if we 1170 // actually have an associated user 1171 if snapsup.UserID > 0 { 1172 var user *auth.UserState 1173 if snapst.UserID != 0 { 1174 user, err = auth.User(st, snapst.UserID) 1175 if err != nil && err != auth.ErrInvalidUser { 1176 return err 1177 } 1178 } 1179 if user == nil { 1180 // if the original user installing the snap is 1181 // no longer available transfer to user who 1182 // triggered this change 1183 snapst.UserID = snapsup.UserID 1184 } 1185 } 1186 // keep instance key 1187 snapst.InstanceKey = snapsup.InstanceKey 1188 1189 newInfo, err := readInfo(snapsup.InstanceName(), cand, 0) 1190 if err != nil { 1191 return err 1192 } 1193 1194 // record type 1195 snapst.SetType(newInfo.Type()) 1196 1197 pb := NewTaskProgressAdapterLocked(t) 1198 1199 // Check for D-Bus service conflicts a second time to detect 1200 // conflicts within a transaction. 1201 if err := checkDBusServiceConflicts(st, newInfo); err != nil { 1202 return err 1203 } 1204 1205 // get the services which LinkSnap should disable when generating wrappers, 1206 // as well as the services which are not present in this revision, but were 1207 // present and disabled in a previous one and as such should be kept inside 1208 // snapst for persistent storage 1209 svcsToSave, svcsToDisable, err := missingDisabledServices(snapst.LastActiveDisabledServices, newInfo) 1210 if err != nil { 1211 return err 1212 } 1213 1214 vitalityRank, err := vitalityRank(st, snapsup.InstanceName()) 1215 if err != nil { 1216 return err 1217 } 1218 linkCtx := backend.LinkContext{ 1219 FirstInstall: oldCurrent.Unset(), 1220 PrevDisabledServices: svcsToDisable, 1221 VitalityRank: vitalityRank, 1222 } 1223 reboot, err := m.backend.LinkSnap(newInfo, deviceCtx, linkCtx, perfTimings) 1224 // defer a cleanup helper which will unlink the snap if anything fails after 1225 // this point 1226 defer func() { 1227 if err == nil { 1228 return 1229 } 1230 // err is not nil, we need to try and unlink the snap to cleanup after 1231 // ourselves 1232 var unlinkErr error 1233 unlinkErr = m.backend.UnlinkSnap(newInfo, linkCtx, pb) 1234 if unlinkErr != nil { 1235 t.Errorf("cannot cleanup failed attempt at making snap %q available to the system: %v", snapsup.InstanceName(), unlinkErr) 1236 } 1237 }() 1238 if err != nil { 1239 return err 1240 } 1241 1242 // commit the missing services to state so when we unlink this revision and 1243 // go to a different revision with potentially different service names, the 1244 // currently missing service names will be re-disabled if they exist later 1245 snapst.LastActiveDisabledServices = svcsToSave 1246 1247 // Restore configuration of the target revision (if available) on revert 1248 if isInstalled { 1249 // Make a copy of configuration of current snap revision 1250 if err = config.SaveRevisionConfig(st, snapsup.InstanceName(), oldCurrent); err != nil { 1251 return err 1252 } 1253 } 1254 1255 // Restore configuration of the target revision (if available; nothing happens if it's not). 1256 // We only do this on reverts (and not on refreshes). 1257 if snapsup.Revert { 1258 if err = config.RestoreRevisionConfig(st, snapsup.InstanceName(), snapsup.Revision()); err != nil { 1259 return err 1260 } 1261 } 1262 1263 if len(snapst.Sequence) == 1 { 1264 if err := m.createSnapCookie(st, snapsup.InstanceName()); err != nil { 1265 return fmt.Errorf("cannot create snap cookie: %v", err) 1266 } 1267 } 1268 // save for undoLinkSnap 1269 t.Set("old-trymode", oldTryMode) 1270 t.Set("old-devmode", oldDevMode) 1271 t.Set("old-jailmode", oldJailMode) 1272 t.Set("old-classic", oldClassic) 1273 t.Set("old-last-active-disabled-services", svcsToSave) 1274 t.Set("old-ignore-validation", oldIgnoreValidation) 1275 t.Set("old-channel", oldChannel) 1276 t.Set("old-current", oldCurrent) 1277 t.Set("old-candidate-index", oldCandidateIndex) 1278 t.Set("old-refresh-inhibited-time", oldRefreshInhibitedTime) 1279 t.Set("old-cohort-key", oldCohortKey) 1280 1281 // Record the fact that the snap was refreshed successfully. 1282 snapst.RefreshInhibitedTime = nil 1283 1284 // Do at the end so we only preserve the new state if it worked. 1285 Set(st, snapsup.InstanceName(), snapst) 1286 1287 if cand.SnapID != "" { 1288 // write the auxiliary store info 1289 aux := &auxStoreInfo{ 1290 Media: snapsup.Media, 1291 Website: snapsup.Website, 1292 } 1293 if err := keepAuxStoreInfo(cand.SnapID, aux); err != nil { 1294 return err 1295 } 1296 if len(snapst.Sequence) == 1 { 1297 defer func() { 1298 if err != nil { 1299 // the install is getting undone, and there are no more of this snap 1300 // try to remove the aux info we just created 1301 discardAuxStoreInfo(cand.SnapID) 1302 } 1303 }() 1304 } 1305 } 1306 1307 // write sequence file for failover helpers 1308 if err := writeSeqFile(snapsup.InstanceName(), snapst); err != nil { 1309 return err 1310 } 1311 1312 // Compatibility with old snapd: check if we have auto-connect task and 1313 // if not, inject it after self (link-snap) for snaps that are not core 1314 if newInfo.Type() != snap.TypeOS { 1315 var hasAutoConnect, hasSetupProfiles bool 1316 for _, other := range t.Change().Tasks() { 1317 // Check if this is auto-connect task for same snap and we it's part of the change with setup-profiles task 1318 if other.Kind() == "auto-connect" || other.Kind() == "setup-profiles" { 1319 otherSnapsup, err := TaskSnapSetup(other) 1320 if err != nil { 1321 return err 1322 } 1323 if snapsup.InstanceName() == otherSnapsup.InstanceName() { 1324 if other.Kind() == "auto-connect" { 1325 hasAutoConnect = true 1326 } else { 1327 hasSetupProfiles = true 1328 } 1329 } 1330 } 1331 } 1332 if !hasAutoConnect && hasSetupProfiles { 1333 InjectAutoConnect(t, snapsup) 1334 } 1335 } 1336 1337 // Make sure if state commits and snapst is mutated we won't be rerun 1338 t.SetStatus(state.DoneStatus) 1339 1340 // if we just installed a core snap, request a restart 1341 // so that we switch executing its snapd. 1342 m.maybeRestart(t, newInfo, reboot, deviceCtx) 1343 1344 return nil 1345 } 1346 1347 // maybeRestart will schedule a reboot or restart as needed for the 1348 // just linked snap with info if it's a core or snapd or kernel snap. 1349 func (m *SnapManager) maybeRestart(t *state.Task, info *snap.Info, rebootRequired bool, deviceCtx DeviceContext) { 1350 // Don't restart when preseeding - we will switch to new snapd on 1351 // first boot. 1352 if m.preseed { 1353 return 1354 } 1355 1356 st := t.State() 1357 1358 if rebootRequired { 1359 t.Logf("Requested system restart.") 1360 st.RequestRestart(state.RestartSystem) 1361 return 1362 } 1363 1364 typ := info.Type() 1365 1366 // if bp is non-trivial then either we're not on classic, or the snap is 1367 // snapd. So daemonRestartReason will always return "" which is what we 1368 // want. If that combination stops being true and there's a situation 1369 // where a non-trivial bp could return a non-empty reason, use IsTrivial 1370 // to check and bail before reaching this far. 1371 1372 restartReason := daemonRestartReason(st, typ) 1373 if restartReason == "" { 1374 // no message -> no restart 1375 return 1376 } 1377 1378 t.Logf(restartReason) 1379 st.RequestRestart(state.RestartDaemon) 1380 } 1381 1382 func daemonRestartReason(st *state.State, typ snap.Type) string { 1383 if !((release.OnClassic && typ == snap.TypeOS) || typ == snap.TypeSnapd) { 1384 // not interesting 1385 return "" 1386 } 1387 1388 if typ == snap.TypeOS { 1389 // ignore error here as we have no way to return to caller 1390 snapdSnapInstalled, _ := isInstalled(st, "snapd") 1391 if snapdSnapInstalled { 1392 // this snap is the base, but snapd is running from the snapd snap 1393 return "" 1394 } 1395 return "Requested daemon restart." 1396 } 1397 1398 return "Requested daemon restart (snapd snap)." 1399 } 1400 1401 // maybeUndoRemodelBootChanges will check if an undo needs to update the 1402 // bootloader. This can happen if e.g. a new kernel gets installed. This 1403 // will switch the bootloader to the new kernel but if the change is later 1404 // undone we need to switch back to the kernel of the old model. 1405 func (m *SnapManager) maybeUndoRemodelBootChanges(t *state.Task) error { 1406 // get the new and the old model 1407 deviceCtx, err := DeviceCtx(t.State(), t, nil) 1408 if err != nil { 1409 return err 1410 } 1411 // we only have an old model if we are in a remodel situation 1412 if !deviceCtx.ForRemodeling() { 1413 return nil 1414 } 1415 groundDeviceCtx := deviceCtx.GroundContext() 1416 oldModel := groundDeviceCtx.Model() 1417 newModel := deviceCtx.Model() 1418 1419 // check type of the snap we are undoing, only kernel/base/core are 1420 // relevant 1421 snapsup, _, err := snapSetupAndState(t) 1422 if err != nil { 1423 return err 1424 } 1425 var newSnapName, snapName string 1426 switch snapsup.Type { 1427 case snap.TypeKernel: 1428 snapName = oldModel.Kernel() 1429 newSnapName = newModel.Kernel() 1430 case snap.TypeOS, snap.TypeBase: 1431 // XXX: add support for "core" 1432 snapName = oldModel.Base() 1433 newSnapName = newModel.Base() 1434 default: 1435 return nil 1436 } 1437 // we can stop if the kernel/base has not changed 1438 if snapName == newSnapName { 1439 return nil 1440 } 1441 // we can stop if the snap we are looking at is not a kernel/base 1442 // of the new model 1443 if snapsup.InstanceName() != newSnapName { 1444 return nil 1445 } 1446 // get info for *old* kernel/base/core and see if we need to reboot 1447 // TODO: we may need something like infoForDeviceSnap here 1448 var snapst SnapState 1449 if err = Get(t.State(), snapName, &snapst); err != nil { 1450 return err 1451 } 1452 info, err := snapst.CurrentInfo() 1453 if err != nil && err != ErrNoCurrent { 1454 return err 1455 } 1456 bp := boot.Participant(info, info.Type(), groundDeviceCtx) 1457 reboot, err := bp.SetNextBoot() 1458 if err != nil { 1459 return err 1460 } 1461 1462 // we may just have switch back to the old kernel/base/core so 1463 // we may need to restart 1464 m.maybeRestart(t, info, reboot, groundDeviceCtx) 1465 1466 return nil 1467 } 1468 1469 func (m *SnapManager) undoLinkSnap(t *state.Task, _ *tomb.Tomb) error { 1470 st := t.State() 1471 st.Lock() 1472 defer st.Unlock() 1473 1474 perfTimings := state.TimingsForTask(t) 1475 defer perfTimings.Save(st) 1476 1477 snapsup, snapst, err := snapSetupAndState(t) 1478 if err != nil { 1479 return err 1480 } 1481 1482 var oldChannel string 1483 err = t.Get("old-channel", &oldChannel) 1484 if err != nil { 1485 return err 1486 } 1487 var oldIgnoreValidation bool 1488 err = t.Get("old-ignore-validation", &oldIgnoreValidation) 1489 if err != nil && err != state.ErrNoState { 1490 return err 1491 } 1492 var oldTryMode bool 1493 err = t.Get("old-trymode", &oldTryMode) 1494 if err != nil { 1495 return err 1496 } 1497 var oldDevMode bool 1498 err = t.Get("old-devmode", &oldDevMode) 1499 if err != nil { 1500 return err 1501 } 1502 var oldJailMode bool 1503 err = t.Get("old-jailmode", &oldJailMode) 1504 if err != nil { 1505 return err 1506 } 1507 var oldClassic bool 1508 err = t.Get("old-classic", &oldClassic) 1509 if err != nil { 1510 return err 1511 } 1512 var oldCurrent snap.Revision 1513 err = t.Get("old-current", &oldCurrent) 1514 if err != nil { 1515 return err 1516 } 1517 var oldCandidateIndex int 1518 if err := t.Get("old-candidate-index", &oldCandidateIndex); err != nil { 1519 return err 1520 } 1521 var oldRefreshInhibitedTime *time.Time 1522 if err := t.Get("old-refresh-inhibited-time", &oldRefreshInhibitedTime); err != nil && err != state.ErrNoState { 1523 return err 1524 } 1525 var oldCohortKey string 1526 if err := t.Get("old-cohort-key", &oldCohortKey); err != nil && err != state.ErrNoState { 1527 return err 1528 } 1529 1530 var oldLastActiveDisabledServices []string 1531 if err := t.Get("old-last-active-disabled-services", &oldLastActiveDisabledServices); err != nil && err != state.ErrNoState { 1532 return err 1533 } 1534 1535 if len(snapst.Sequence) == 1 { 1536 // XXX: shouldn't these two just log and carry on? this is an undo handler... 1537 timings.Run(perfTimings, "discard-snap-namespace", fmt.Sprintf("discard the namespace of snap %q", snapsup.InstanceName()), func(tm timings.Measurer) { 1538 err = m.backend.DiscardSnapNamespace(snapsup.InstanceName()) 1539 }) 1540 if err != nil { 1541 t.Errorf("cannot discard snap namespace %q, will retry in 3 mins: %s", snapsup.InstanceName(), err) 1542 return &state.Retry{After: 3 * time.Minute} 1543 } 1544 if err := m.removeSnapCookie(st, snapsup.InstanceName()); err != nil { 1545 return fmt.Errorf("cannot remove snap cookie: %v", err) 1546 } 1547 // try to remove the auxiliary store info 1548 if err := discardAuxStoreInfo(snapsup.SideInfo.SnapID); err != nil { 1549 return fmt.Errorf("cannot remove auxiliary store info: %v", err) 1550 } 1551 } 1552 1553 isRevert := snapsup.Revert 1554 1555 // relinking of the old snap is done in the undo of unlink-current-snap 1556 currentIndex := snapst.LastIndex(snapst.Current) 1557 if currentIndex < 0 { 1558 return fmt.Errorf("internal error: cannot find revision %d in %v for undoing the added revision", snapsup.SideInfo.Revision, snapst.Sequence) 1559 } 1560 1561 if oldCandidateIndex < 0 { 1562 snapst.Sequence = append(snapst.Sequence[:currentIndex], snapst.Sequence[currentIndex+1:]...) 1563 } else if !isRevert { 1564 oldCand := snapst.Sequence[currentIndex] 1565 copy(snapst.Sequence[oldCandidateIndex+1:], snapst.Sequence[oldCandidateIndex:]) 1566 snapst.Sequence[oldCandidateIndex] = oldCand 1567 } 1568 snapst.Current = oldCurrent 1569 snapst.Active = false 1570 snapst.TrackingChannel = oldChannel 1571 snapst.IgnoreValidation = oldIgnoreValidation 1572 snapst.TryMode = oldTryMode 1573 snapst.DevMode = oldDevMode 1574 snapst.JailMode = oldJailMode 1575 snapst.Classic = oldClassic 1576 snapst.RefreshInhibitedTime = oldRefreshInhibitedTime 1577 snapst.CohortKey = oldCohortKey 1578 snapst.LastActiveDisabledServices = oldLastActiveDisabledServices 1579 1580 newInfo, err := readInfo(snapsup.InstanceName(), snapsup.SideInfo, 0) 1581 if err != nil { 1582 return err 1583 } 1584 1585 // we need to undo potential changes to current snap configuration (e.g. if 1586 // modified by post-refresh/install/configure hooks as part of failed 1587 // refresh/install) by restoring the configuration of "old current". 1588 // similarly, we need to re-save the disabled services if there is a 1589 // revision for us to go back to, see comment below for full explanation 1590 if len(snapst.Sequence) > 0 { 1591 if err = config.RestoreRevisionConfig(st, snapsup.InstanceName(), oldCurrent); err != nil { 1592 return err 1593 } 1594 1595 // unlock state while we talk to systemd 1596 st.Unlock() 1597 defer st.Lock() 1598 1599 // get the currently disabled services and add them to 1600 // snapst.LastActiveDisabledServices because if we completed a successful 1601 // doLinkSnap (hence we are in the undo handler), then we already disabled 1602 // the services and deleted currently existing services from the state 1603 // during doLinkSnap, but now we will need that information again when we go 1604 // to link the old version to prevent accidental enabling of disabled 1605 // services on a failed revert/refresh 1606 disabledServices, err := m.queryDisabledServices(newInfo, NewTaskProgressAdapterUnlocked(t)) 1607 if err != nil { 1608 return err 1609 } 1610 1611 st.Lock() 1612 defer st.Unlock() 1613 1614 snapst.LastActiveDisabledServices = append( 1615 snapst.LastActiveDisabledServices, 1616 disabledServices..., 1617 ) 1618 } else { 1619 // in the case of an install we need to clear any config 1620 err = config.DeleteSnapConfig(st, snapsup.InstanceName()) 1621 if err != nil { 1622 return err 1623 } 1624 } 1625 1626 pb := NewTaskProgressAdapterLocked(t) 1627 linkCtx := backend.LinkContext{ 1628 FirstInstall: oldCurrent.Unset(), 1629 } 1630 err = m.backend.UnlinkSnap(newInfo, linkCtx, pb) 1631 if err != nil { 1632 return err 1633 } 1634 1635 if err := m.maybeUndoRemodelBootChanges(t); err != nil { 1636 return err 1637 } 1638 1639 // restart only when snapd was installed for the first time and the rest of 1640 // the cleanup is performed by snapd from core; 1641 // when reverting a subsequent snapd revision, the restart happens in 1642 // undoLinkCurrentSnap() instead 1643 if linkCtx.FirstInstall && newInfo.Type() == snap.TypeSnapd { 1644 // only way to get 1645 deviceCtx, err := DeviceCtx(st, t, nil) 1646 if err != nil { 1647 return err 1648 } 1649 const rebootRequired = false 1650 m.maybeRestart(t, newInfo, rebootRequired, deviceCtx) 1651 } 1652 1653 // mark as inactive 1654 Set(st, snapsup.InstanceName(), snapst) 1655 // write sequence file for failover helpers 1656 if err := writeSeqFile(snapsup.InstanceName(), snapst); err != nil { 1657 return err 1658 } 1659 // Make sure if state commits and snapst is mutated we won't be rerun 1660 t.SetStatus(state.UndoneStatus) 1661 1662 // If we are on classic and have no previous version of core 1663 // we may have restarted from a distro package into the core 1664 // snap. We need to undo that restart here. Instead of in 1665 // doUnlinkCurrentSnap() like we usually do when going from 1666 // core snap -> next core snap 1667 if release.OnClassic && newInfo.Type() == snap.TypeOS && oldCurrent.Unset() { 1668 t.Logf("Requested daemon restart (undo classic initial core install)") 1669 st.RequestRestart(state.RestartDaemon) 1670 } 1671 return nil 1672 } 1673 1674 type doSwitchFlags struct { 1675 switchCurrentChannel bool 1676 } 1677 1678 // doSwitchSnapChannel switches the snap's tracking channel and/or cohort. It 1679 // also switches the current channel if appropriate. For use from 'Update'. 1680 func (m *SnapManager) doSwitchSnapChannel(t *state.Task, _ *tomb.Tomb) error { 1681 return m.genericDoSwitchSnap(t, doSwitchFlags{switchCurrentChannel: true}) 1682 } 1683 1684 // doSwitchSnap switches the snap's tracking channel and/or cohort, *without* 1685 // switching the current snap channel. For use from 'Switch'. 1686 func (m *SnapManager) doSwitchSnap(t *state.Task, _ *tomb.Tomb) error { 1687 return m.genericDoSwitchSnap(t, doSwitchFlags{}) 1688 } 1689 1690 func (m *SnapManager) genericDoSwitchSnap(t *state.Task, flags doSwitchFlags) error { 1691 st := t.State() 1692 st.Lock() 1693 defer st.Unlock() 1694 1695 snapsup, snapst, err := snapSetupAndState(t) 1696 if err != nil { 1697 return err 1698 } 1699 1700 // switched the tracked channel 1701 if err := snapst.SetTrackingChannel(snapsup.Channel); err != nil { 1702 return err 1703 } 1704 snapst.CohortKey = snapsup.CohortKey 1705 if flags.switchCurrentChannel { 1706 // optionally support switching the current snap channel too, e.g. 1707 // if a snap is in both stable and candidate with the same revision 1708 // we can update it here and it will be displayed correctly in the UI 1709 if snapsup.SideInfo.Channel != "" { 1710 snapst.CurrentSideInfo().Channel = snapsup.Channel 1711 } 1712 } 1713 1714 Set(st, snapsup.InstanceName(), snapst) 1715 return nil 1716 } 1717 1718 func (m *SnapManager) doToggleSnapFlags(t *state.Task, _ *tomb.Tomb) error { 1719 st := t.State() 1720 st.Lock() 1721 defer st.Unlock() 1722 1723 snapsup, snapst, err := snapSetupAndState(t) 1724 if err != nil { 1725 return err 1726 } 1727 1728 // for now we support toggling only ignore-validation 1729 snapst.IgnoreValidation = snapsup.IgnoreValidation 1730 1731 Set(st, snapsup.InstanceName(), snapst) 1732 return nil 1733 } 1734 1735 func (m *SnapManager) startSnapServices(t *state.Task, _ *tomb.Tomb) error { 1736 st := t.State() 1737 st.Lock() 1738 defer st.Unlock() 1739 1740 perfTimings := state.TimingsForTask(t) 1741 defer perfTimings.Save(st) 1742 1743 _, snapst, err := snapSetupAndState(t) 1744 if err != nil { 1745 return err 1746 } 1747 1748 currentInfo, err := snapst.CurrentInfo() 1749 if err != nil { 1750 return err 1751 } 1752 svcs := currentInfo.Services() 1753 if len(svcs) == 0 { 1754 return nil 1755 } 1756 1757 startupOrdered, err := snap.SortServices(svcs) 1758 if err != nil { 1759 return err 1760 } 1761 1762 pb := NewTaskProgressAdapterUnlocked(t) 1763 st.Unlock() 1764 err = m.backend.StartServices(startupOrdered, pb, perfTimings) 1765 st.Lock() 1766 return err 1767 } 1768 1769 func (m *SnapManager) stopSnapServices(t *state.Task, _ *tomb.Tomb) error { 1770 st := t.State() 1771 st.Lock() 1772 defer st.Unlock() 1773 1774 perfTimings := state.TimingsForTask(t) 1775 defer perfTimings.Save(st) 1776 1777 snapsup, snapst, err := snapSetupAndState(t) 1778 if err != nil { 1779 return err 1780 } 1781 1782 currentInfo, err := snapst.CurrentInfo() 1783 if err != nil { 1784 return err 1785 } 1786 svcs := currentInfo.Services() 1787 if len(svcs) == 0 { 1788 return nil 1789 } 1790 1791 var stopReason snap.ServiceStopReason 1792 if err := t.Get("stop-reason", &stopReason); err != nil && err != state.ErrNoState { 1793 return err 1794 } 1795 1796 pb := NewTaskProgressAdapterUnlocked(t) 1797 st.Unlock() 1798 defer st.Lock() 1799 1800 // stop the services 1801 err = m.backend.StopServices(svcs, stopReason, pb, perfTimings) 1802 if err != nil { 1803 return err 1804 } 1805 1806 // get the disabled services after we stopped all the services. 1807 // NOTE: we could probably do this before we stopped all the services (or 1808 // later in a different task from this entirely), but the important ordering 1809 // for saving the disabled services is that we save the list before we 1810 // unlink the snap (and hence destroy systemd's state of what services are 1811 // disabled). 1812 // this list is not meant to save what services are disabled at any given 1813 // time, specifically just what services are disabled while systemd loses 1814 // track of the services because we need to delete and re-generate the 1815 // service units. 1816 disabledServices, err := m.queryDisabledServices(currentInfo, pb) 1817 if err != nil { 1818 return err 1819 } 1820 1821 st.Lock() 1822 defer st.Unlock() 1823 1824 // finally commit the disabled services to snapsetup 1825 snapsup.LastActiveDisabledServices = disabledServices 1826 1827 err = SetTaskSnapSetup(t, snapsup) 1828 if err != nil { 1829 return err 1830 } 1831 1832 return nil 1833 } 1834 1835 func (m *SnapManager) doUnlinkSnap(t *state.Task, _ *tomb.Tomb) error { 1836 // invoked only if snap has a current active revision 1837 st := t.State() 1838 st.Lock() 1839 defer st.Unlock() 1840 1841 snapsup, snapst, err := snapSetupAndState(t) 1842 if err != nil { 1843 return err 1844 } 1845 1846 info, err := Info(t.State(), snapsup.InstanceName(), snapsup.Revision()) 1847 if err != nil { 1848 return err 1849 } 1850 1851 // do the final unlink 1852 linkCtx := backend.LinkContext{ 1853 FirstInstall: false, 1854 } 1855 err = m.backend.UnlinkSnap(info, linkCtx, NewTaskProgressAdapterLocked(t)) 1856 if err != nil { 1857 return err 1858 } 1859 1860 // add to the disabled services list in snapst services which were disabled 1861 // when stop-snap-services ran, for usage across changes like in reverting 1862 // and enabling after being disabled. 1863 // we keep what's already in the list in snapst because that list is 1864 // services which were previously present in the snap and disabled, but are 1865 // no longer present. 1866 snapst.LastActiveDisabledServices = append( 1867 snapst.LastActiveDisabledServices, 1868 snapsup.LastActiveDisabledServices..., 1869 ) 1870 1871 // mark as inactive 1872 snapst.Active = false 1873 Set(st, snapsup.InstanceName(), snapst) 1874 1875 return err 1876 } 1877 1878 func (m *SnapManager) doClearSnapData(t *state.Task, _ *tomb.Tomb) error { 1879 st := t.State() 1880 st.Lock() 1881 snapsup, snapst, err := snapSetupAndState(t) 1882 st.Unlock() 1883 if err != nil { 1884 return err 1885 } 1886 1887 st.Lock() 1888 info, err := Info(t.State(), snapsup.InstanceName(), snapsup.Revision()) 1889 st.Unlock() 1890 if err != nil { 1891 return err 1892 } 1893 1894 if err = m.backend.RemoveSnapData(info); err != nil { 1895 return err 1896 } 1897 1898 if len(snapst.Sequence) == 1 { 1899 // Only remove data common between versions if this is the last version 1900 if err = m.backend.RemoveSnapCommonData(info); err != nil { 1901 return err 1902 } 1903 1904 st.Lock() 1905 defer st.Unlock() 1906 1907 otherInstances, err := hasOtherInstances(st, snapsup.InstanceName()) 1908 if err != nil { 1909 return err 1910 } 1911 // Snap data directory can be removed now too 1912 if err := m.backend.RemoveSnapDataDir(info, otherInstances); err != nil { 1913 return err 1914 } 1915 } 1916 1917 return nil 1918 } 1919 1920 func (m *SnapManager) doDiscardSnap(t *state.Task, _ *tomb.Tomb) error { 1921 st := t.State() 1922 st.Lock() 1923 defer st.Unlock() 1924 1925 snapsup, snapst, err := snapSetupAndState(t) 1926 if err != nil { 1927 return err 1928 } 1929 1930 deviceCtx, err := DeviceCtx(st, t, nil) 1931 if err != nil { 1932 return err 1933 } 1934 1935 if snapst.Current == snapsup.Revision() && snapst.Active { 1936 return fmt.Errorf("internal error: cannot discard snap %q: still active", snapsup.InstanceName()) 1937 } 1938 1939 if len(snapst.Sequence) == 1 { 1940 snapst.Sequence = nil 1941 snapst.Current = snap.Revision{} 1942 } else { 1943 newSeq := make([]*snap.SideInfo, 0, len(snapst.Sequence)) 1944 for _, si := range snapst.Sequence { 1945 if si.Revision == snapsup.Revision() { 1946 // leave out 1947 continue 1948 } 1949 newSeq = append(newSeq, si) 1950 } 1951 snapst.Sequence = newSeq 1952 if snapst.Current == snapsup.Revision() { 1953 snapst.Current = newSeq[len(newSeq)-1].Revision 1954 } 1955 } 1956 1957 pb := NewTaskProgressAdapterLocked(t) 1958 typ, err := snapst.Type() 1959 if err != nil { 1960 return err 1961 } 1962 err = m.backend.RemoveSnapFiles(snapsup.placeInfo(), typ, nil, deviceCtx, pb) 1963 if err != nil { 1964 t.Errorf("cannot remove snap file %q, will retry in 3 mins: %s", snapsup.InstanceName(), err) 1965 return &state.Retry{After: 3 * time.Minute} 1966 } 1967 if len(snapst.Sequence) == 0 { 1968 // Remove configuration associated with this snap. 1969 err = config.DeleteSnapConfig(st, snapsup.InstanceName()) 1970 if err != nil { 1971 return err 1972 } 1973 err = m.backend.DiscardSnapNamespace(snapsup.InstanceName()) 1974 if err != nil { 1975 t.Errorf("cannot discard snap namespace %q, will retry in 3 mins: %s", snapsup.InstanceName(), err) 1976 return &state.Retry{After: 3 * time.Minute} 1977 } 1978 if err := m.removeSnapCookie(st, snapsup.InstanceName()); err != nil { 1979 return fmt.Errorf("cannot remove snap cookie: %v", err) 1980 } 1981 1982 otherInstances, err := hasOtherInstances(st, snapsup.InstanceName()) 1983 if err != nil { 1984 return err 1985 } 1986 1987 if err := m.backend.RemoveSnapDir(snapsup.placeInfo(), otherInstances); err != nil { 1988 return fmt.Errorf("cannot remove snap directory: %v", err) 1989 } 1990 1991 // try to remove the auxiliary store info 1992 if err := discardAuxStoreInfo(snapsup.SideInfo.SnapID); err != nil { 1993 logger.Noticef("Cannot remove auxiliary store info for %q: %v", snapsup.InstanceName(), err) 1994 } 1995 1996 // XXX: also remove sequence files? 1997 } 1998 if err = config.DiscardRevisionConfig(st, snapsup.InstanceName(), snapsup.Revision()); err != nil { 1999 return err 2000 } 2001 Set(st, snapsup.InstanceName(), snapst) 2002 return nil 2003 } 2004 2005 /* aliases v2 2006 2007 aliases v2 implementation uses the following tasks: 2008 2009 * for install/refresh/remove/enable/disable etc 2010 2011 - remove-aliases: remove aliases of a snap from disk and mark them pending 2012 2013 - setup-aliases: (re)creates aliases from snap state, mark them as 2014 not pending 2015 2016 - set-auto-aliases: updates aliases snap state based on the 2017 snap-declaration and current revision info of the snap 2018 2019 * for refresh & when the snap-declaration aliases change without a 2020 new revision 2021 2022 - refresh-aliases: updates aliases snap state and updates them on disk too; 2023 its undo is used generically by other tasks as well 2024 2025 - prune-auto-aliases: used for the special case of automatic 2026 aliases transferred from one snap to another to prune them from 2027 the source snaps to avoid conflicts in later operations 2028 2029 * for alias/unalias/prefer: 2030 2031 - alias: creates a manual alias 2032 2033 - unalias: removes a manual alias 2034 2035 - disable-aliases: disable the automatic aliases of a snap and 2036 removes all manual ones as well 2037 2038 - prefer-aliases: enables the automatic aliases of a snap after 2039 disabling any other snap conflicting aliases 2040 2041 */ 2042 2043 func (m *SnapManager) doSetAutoAliases(t *state.Task, _ *tomb.Tomb) error { 2044 st := t.State() 2045 st.Lock() 2046 defer st.Unlock() 2047 snapsup, snapst, err := snapSetupAndState(t) 2048 if err != nil { 2049 return err 2050 } 2051 snapName := snapsup.InstanceName() 2052 curInfo, err := snapst.CurrentInfo() 2053 if err != nil { 2054 return err 2055 } 2056 2057 // --unaliased 2058 if snapsup.Unaliased { 2059 t.Set("old-auto-aliases-disabled", snapst.AutoAliasesDisabled) 2060 snapst.AutoAliasesDisabled = true 2061 } 2062 2063 curAliases := snapst.Aliases 2064 // TODO: implement --prefer logic 2065 newAliases, err := refreshAliases(st, curInfo, curAliases) 2066 if err != nil { 2067 return err 2068 } 2069 _, err = checkAliasesConflicts(st, snapName, snapst.AutoAliasesDisabled, newAliases, nil) 2070 if err != nil { 2071 return err 2072 } 2073 2074 t.Set("old-aliases-v2", curAliases) 2075 // noop, except on first install where we need to set this here 2076 snapst.AliasesPending = true 2077 snapst.Aliases = newAliases 2078 Set(st, snapName, snapst) 2079 return nil 2080 } 2081 2082 func (m *SnapManager) doRemoveAliases(t *state.Task, _ *tomb.Tomb) error { 2083 st := t.State() 2084 st.Lock() 2085 defer st.Unlock() 2086 snapsup, snapst, err := snapSetupAndState(t) 2087 if err != nil { 2088 return err 2089 } 2090 snapName := snapsup.InstanceName() 2091 2092 err = m.backend.RemoveSnapAliases(snapName) 2093 if err != nil { 2094 return err 2095 } 2096 2097 snapst.AliasesPending = true 2098 Set(st, snapName, snapst) 2099 return nil 2100 } 2101 2102 func (m *SnapManager) doSetupAliases(t *state.Task, _ *tomb.Tomb) error { 2103 st := t.State() 2104 st.Lock() 2105 defer st.Unlock() 2106 snapsup, snapst, err := snapSetupAndState(t) 2107 if err != nil { 2108 return err 2109 } 2110 snapName := snapsup.InstanceName() 2111 curAliases := snapst.Aliases 2112 2113 _, _, err = applyAliasesChange(snapName, autoDis, nil, snapst.AutoAliasesDisabled, curAliases, m.backend, doApply) 2114 if err != nil { 2115 return err 2116 } 2117 2118 snapst.AliasesPending = false 2119 Set(st, snapName, snapst) 2120 return nil 2121 } 2122 2123 func (m *SnapManager) doRefreshAliases(t *state.Task, _ *tomb.Tomb) error { 2124 st := t.State() 2125 st.Lock() 2126 defer st.Unlock() 2127 snapsup, snapst, err := snapSetupAndState(t) 2128 if err != nil { 2129 return err 2130 } 2131 snapName := snapsup.InstanceName() 2132 curInfo, err := snapst.CurrentInfo() 2133 if err != nil { 2134 return err 2135 } 2136 2137 autoDisabled := snapst.AutoAliasesDisabled 2138 curAliases := snapst.Aliases 2139 newAliases, err := refreshAliases(st, curInfo, curAliases) 2140 if err != nil { 2141 return err 2142 } 2143 _, err = checkAliasesConflicts(st, snapName, autoDisabled, newAliases, nil) 2144 if err != nil { 2145 return err 2146 } 2147 2148 if !snapst.AliasesPending { 2149 if _, _, err := applyAliasesChange(snapName, autoDisabled, curAliases, autoDisabled, newAliases, m.backend, doApply); err != nil { 2150 return err 2151 } 2152 } 2153 2154 t.Set("old-aliases-v2", curAliases) 2155 snapst.Aliases = newAliases 2156 Set(st, snapName, snapst) 2157 return nil 2158 } 2159 2160 func (m *SnapManager) undoRefreshAliases(t *state.Task, _ *tomb.Tomb) error { 2161 st := t.State() 2162 st.Lock() 2163 defer st.Unlock() 2164 var oldAliases map[string]*AliasTarget 2165 err := t.Get("old-aliases-v2", &oldAliases) 2166 if err == state.ErrNoState { 2167 // nothing to do 2168 return nil 2169 } 2170 if err != nil { 2171 return err 2172 } 2173 snapsup, snapst, err := snapSetupAndState(t) 2174 if err != nil { 2175 return err 2176 } 2177 snapName := snapsup.InstanceName() 2178 curAutoDisabled := snapst.AutoAliasesDisabled 2179 autoDisabled := curAutoDisabled 2180 if err = t.Get("old-auto-aliases-disabled", &autoDisabled); err != nil && err != state.ErrNoState { 2181 return err 2182 } 2183 2184 var otherSnapDisabled map[string]*otherDisabledAliases 2185 if err = t.Get("other-disabled-aliases", &otherSnapDisabled); err != nil && err != state.ErrNoState { 2186 return err 2187 } 2188 2189 // check if the old states creates conflicts now 2190 _, err = checkAliasesConflicts(st, snapName, autoDisabled, oldAliases, nil) 2191 if _, ok := err.(*AliasConflictError); ok { 2192 // best we can do is reinstate with all aliases disabled 2193 t.Errorf("cannot reinstate alias state because of conflicts, disabling: %v", err) 2194 oldAliases, _ = disableAliases(oldAliases) 2195 autoDisabled = true 2196 } else if err != nil { 2197 return err 2198 } 2199 2200 if !snapst.AliasesPending { 2201 curAliases := snapst.Aliases 2202 if _, _, err := applyAliasesChange(snapName, curAutoDisabled, curAliases, autoDisabled, oldAliases, m.backend, doApply); err != nil { 2203 return err 2204 } 2205 } 2206 2207 snapst.AutoAliasesDisabled = autoDisabled 2208 snapst.Aliases = oldAliases 2209 newSnapStates := make(map[string]*SnapState, 1+len(otherSnapDisabled)) 2210 newSnapStates[snapName] = snapst 2211 2212 // if we disabled other snap aliases try to undo that 2213 conflicting := make(map[string]bool, len(otherSnapDisabled)) 2214 otherCurSnapStates := make(map[string]*SnapState, len(otherSnapDisabled)) 2215 for otherSnap, otherDisabled := range otherSnapDisabled { 2216 var otherSnapState SnapState 2217 err := Get(st, otherSnap, &otherSnapState) 2218 if err != nil { 2219 return err 2220 } 2221 otherCurInfo, err := otherSnapState.CurrentInfo() 2222 if err != nil { 2223 return err 2224 } 2225 2226 otherCurSnapStates[otherSnap] = &otherSnapState 2227 2228 autoDisabled := otherSnapState.AutoAliasesDisabled 2229 if otherDisabled.Auto { 2230 // automatic aliases of other were disabled, undo that 2231 autoDisabled = false 2232 } 2233 otherAliases := reenableAliases(otherCurInfo, otherSnapState.Aliases, otherDisabled.Manual) 2234 // check for conflicts taking into account 2235 // re-enabled aliases 2236 conflicts, err := checkAliasesConflicts(st, otherSnap, autoDisabled, otherAliases, newSnapStates) 2237 if _, ok := err.(*AliasConflictError); ok { 2238 conflicting[otherSnap] = true 2239 for conflictSnap := range conflicts { 2240 conflicting[conflictSnap] = true 2241 } 2242 } else if err != nil { 2243 return err 2244 } 2245 2246 newSnapState := otherSnapState 2247 newSnapState.Aliases = otherAliases 2248 newSnapState.AutoAliasesDisabled = autoDisabled 2249 newSnapStates[otherSnap] = &newSnapState 2250 } 2251 2252 // apply non-conflicting other 2253 for otherSnap, otherSnapState := range otherCurSnapStates { 2254 if conflicting[otherSnap] { 2255 // keep as it was 2256 continue 2257 } 2258 newSnapSt := newSnapStates[otherSnap] 2259 if !otherSnapState.AliasesPending { 2260 if _, _, err := applyAliasesChange(otherSnap, otherSnapState.AutoAliasesDisabled, otherSnapState.Aliases, newSnapSt.AutoAliasesDisabled, newSnapSt.Aliases, m.backend, doApply); err != nil { 2261 return err 2262 } 2263 } 2264 } 2265 2266 for instanceName, snapst := range newSnapStates { 2267 if conflicting[instanceName] { 2268 // keep as it was 2269 continue 2270 } 2271 Set(st, instanceName, snapst) 2272 } 2273 return nil 2274 } 2275 2276 func (m *SnapManager) doPruneAutoAliases(t *state.Task, _ *tomb.Tomb) error { 2277 st := t.State() 2278 st.Lock() 2279 defer st.Unlock() 2280 snapsup, snapst, err := snapSetupAndState(t) 2281 if err != nil { 2282 return err 2283 } 2284 var which []string 2285 err = t.Get("aliases", &which) 2286 if err != nil { 2287 return err 2288 } 2289 snapName := snapsup.InstanceName() 2290 autoDisabled := snapst.AutoAliasesDisabled 2291 curAliases := snapst.Aliases 2292 2293 newAliases := pruneAutoAliases(curAliases, which) 2294 2295 if !snapst.AliasesPending { 2296 if _, _, err := applyAliasesChange(snapName, autoDisabled, curAliases, autoDisabled, newAliases, m.backend, doApply); err != nil { 2297 return err 2298 } 2299 } 2300 2301 t.Set("old-aliases-v2", curAliases) 2302 snapst.Aliases = newAliases 2303 Set(st, snapName, snapst) 2304 return nil 2305 } 2306 2307 type changedAlias struct { 2308 Snap string `json:"snap"` 2309 App string `json:"app"` 2310 Alias string `json:"alias"` 2311 } 2312 2313 func aliasesTrace(t *state.Task, added, removed []*backend.Alias) error { 2314 chg := t.Change() 2315 var data map[string]interface{} 2316 err := chg.Get("api-data", &data) 2317 if err != nil && err != state.ErrNoState { 2318 return err 2319 } 2320 if len(data) == 0 { 2321 data = make(map[string]interface{}) 2322 } 2323 2324 curAdded, _ := data["aliases-added"].([]interface{}) 2325 for _, a := range added { 2326 snap, app := snap.SplitSnapApp(a.Target) 2327 curAdded = append(curAdded, &changedAlias{ 2328 Snap: snap, 2329 App: app, 2330 Alias: a.Name, 2331 }) 2332 } 2333 data["aliases-added"] = curAdded 2334 2335 curRemoved, _ := data["aliases-removed"].([]interface{}) 2336 for _, a := range removed { 2337 snap, app := snap.SplitSnapApp(a.Target) 2338 curRemoved = append(curRemoved, &changedAlias{ 2339 Snap: snap, 2340 App: app, 2341 Alias: a.Name, 2342 }) 2343 } 2344 data["aliases-removed"] = curRemoved 2345 2346 chg.Set("api-data", data) 2347 return nil 2348 } 2349 2350 func (m *SnapManager) doAlias(t *state.Task, _ *tomb.Tomb) error { 2351 st := t.State() 2352 st.Lock() 2353 defer st.Unlock() 2354 snapsup, snapst, err := snapSetupAndState(t) 2355 if err != nil { 2356 return err 2357 } 2358 var target, alias string 2359 err = t.Get("target", &target) 2360 if err != nil { 2361 return err 2362 } 2363 err = t.Get("alias", &alias) 2364 if err != nil { 2365 return err 2366 } 2367 2368 snapName := snapsup.InstanceName() 2369 curInfo, err := snapst.CurrentInfo() 2370 if err != nil { 2371 return err 2372 } 2373 2374 autoDisabled := snapst.AutoAliasesDisabled 2375 curAliases := snapst.Aliases 2376 newAliases, err := manualAlias(curInfo, curAliases, target, alias) 2377 if err != nil { 2378 return err 2379 } 2380 _, err = checkAliasesConflicts(st, snapName, autoDisabled, newAliases, nil) 2381 if err != nil { 2382 return err 2383 } 2384 2385 added, removed, err := applyAliasesChange(snapName, autoDisabled, curAliases, autoDisabled, newAliases, m.backend, snapst.AliasesPending) 2386 if err != nil { 2387 return err 2388 } 2389 if err := aliasesTrace(t, added, removed); err != nil { 2390 return err 2391 } 2392 2393 t.Set("old-aliases-v2", curAliases) 2394 snapst.Aliases = newAliases 2395 Set(st, snapName, snapst) 2396 return nil 2397 } 2398 2399 func (m *SnapManager) doDisableAliases(t *state.Task, _ *tomb.Tomb) error { 2400 st := t.State() 2401 st.Lock() 2402 defer st.Unlock() 2403 snapsup, snapst, err := snapSetupAndState(t) 2404 if err != nil { 2405 return err 2406 } 2407 snapName := snapsup.InstanceName() 2408 2409 oldAutoDisabled := snapst.AutoAliasesDisabled 2410 oldAliases := snapst.Aliases 2411 newAliases, _ := disableAliases(oldAliases) 2412 2413 added, removed, err := applyAliasesChange(snapName, oldAutoDisabled, oldAliases, autoDis, newAliases, m.backend, snapst.AliasesPending) 2414 if err != nil { 2415 return err 2416 } 2417 if err := aliasesTrace(t, added, removed); err != nil { 2418 return err 2419 } 2420 2421 t.Set("old-auto-aliases-disabled", oldAutoDisabled) 2422 snapst.AutoAliasesDisabled = true 2423 t.Set("old-aliases-v2", oldAliases) 2424 snapst.Aliases = newAliases 2425 Set(st, snapName, snapst) 2426 return nil 2427 } 2428 2429 func (m *SnapManager) doUnalias(t *state.Task, _ *tomb.Tomb) error { 2430 st := t.State() 2431 st.Lock() 2432 defer st.Unlock() 2433 snapsup, snapst, err := snapSetupAndState(t) 2434 if err != nil { 2435 return err 2436 } 2437 var alias string 2438 err = t.Get("alias", &alias) 2439 if err != nil { 2440 return err 2441 } 2442 snapName := snapsup.InstanceName() 2443 2444 autoDisabled := snapst.AutoAliasesDisabled 2445 oldAliases := snapst.Aliases 2446 newAliases, err := manualUnalias(oldAliases, alias) 2447 if err != nil { 2448 return err 2449 } 2450 2451 added, removed, err := applyAliasesChange(snapName, autoDisabled, oldAliases, autoDisabled, newAliases, m.backend, snapst.AliasesPending) 2452 if err != nil { 2453 return err 2454 } 2455 if err := aliasesTrace(t, added, removed); err != nil { 2456 return err 2457 } 2458 2459 t.Set("old-aliases-v2", oldAliases) 2460 snapst.Aliases = newAliases 2461 Set(st, snapName, snapst) 2462 return nil 2463 } 2464 2465 // otherDisabledAliases is used to track for the benefit of undo what 2466 // changes were made aka what aliases were disabled of another 2467 // conflicting snap by prefer logic 2468 type otherDisabledAliases struct { 2469 // Auto records whether prefer had to disable automatic aliases 2470 Auto bool `json:"auto,omitempty"` 2471 // Manual records which manual aliases were removed by prefer 2472 Manual map[string]string `json:"manual,omitempty"` 2473 } 2474 2475 func (m *SnapManager) doPreferAliases(t *state.Task, _ *tomb.Tomb) error { 2476 st := t.State() 2477 st.Lock() 2478 defer st.Unlock() 2479 snapsup, snapst, err := snapSetupAndState(t) 2480 if err != nil { 2481 return err 2482 } 2483 instanceName := snapsup.InstanceName() 2484 2485 if !snapst.AutoAliasesDisabled { 2486 // already enabled, nothing to do 2487 return nil 2488 } 2489 2490 curAliases := snapst.Aliases 2491 aliasConflicts, err := checkAliasesConflicts(st, instanceName, autoEn, curAliases, nil) 2492 conflErr, isConflErr := err.(*AliasConflictError) 2493 if err != nil && !isConflErr { 2494 return err 2495 } 2496 if isConflErr && conflErr.Conflicts == nil { 2497 // it's a snap command namespace conflict, we cannot remedy it 2498 return conflErr 2499 } 2500 // proceed to disable conflicting aliases as needed 2501 // before re-enabling instanceName aliases 2502 2503 otherSnapStates := make(map[string]*SnapState, len(aliasConflicts)) 2504 otherSnapDisabled := make(map[string]*otherDisabledAliases, len(aliasConflicts)) 2505 for otherSnap := range aliasConflicts { 2506 var otherSnapState SnapState 2507 err := Get(st, otherSnap, &otherSnapState) 2508 if err != nil { 2509 return err 2510 } 2511 2512 otherAliases, disabledManual := disableAliases(otherSnapState.Aliases) 2513 2514 added, removed, err := applyAliasesChange(otherSnap, otherSnapState.AutoAliasesDisabled, otherSnapState.Aliases, autoDis, otherAliases, m.backend, otherSnapState.AliasesPending) 2515 if err != nil { 2516 return err 2517 } 2518 if err := aliasesTrace(t, added, removed); err != nil { 2519 return err 2520 } 2521 2522 var otherDisabled otherDisabledAliases 2523 otherDisabled.Manual = disabledManual 2524 otherSnapState.Aliases = otherAliases 2525 // disable automatic aliases as needed 2526 if !otherSnapState.AutoAliasesDisabled && len(otherAliases) != 0 { 2527 // record that we did disable automatic aliases 2528 otherDisabled.Auto = true 2529 otherSnapState.AutoAliasesDisabled = true 2530 } 2531 otherSnapDisabled[otherSnap] = &otherDisabled 2532 otherSnapStates[otherSnap] = &otherSnapState 2533 } 2534 2535 added, removed, err := applyAliasesChange(instanceName, autoDis, curAliases, autoEn, curAliases, m.backend, snapst.AliasesPending) 2536 if err != nil { 2537 return err 2538 } 2539 if err := aliasesTrace(t, added, removed); err != nil { 2540 return err 2541 } 2542 2543 for otherSnap, otherSnapState := range otherSnapStates { 2544 Set(st, otherSnap, otherSnapState) 2545 } 2546 if len(otherSnapDisabled) != 0 { 2547 t.Set("other-disabled-aliases", otherSnapDisabled) 2548 } 2549 t.Set("old-auto-aliases-disabled", true) 2550 t.Set("old-aliases-v2", curAliases) 2551 snapst.AutoAliasesDisabled = false 2552 Set(st, instanceName, snapst) 2553 return nil 2554 } 2555 2556 // changeReadyUpToTask returns whether all other change's tasks are Ready. 2557 func changeReadyUpToTask(task *state.Task) bool { 2558 me := task.ID() 2559 change := task.Change() 2560 for _, task := range change.Tasks() { 2561 if me == task.ID() { 2562 // ignore self 2563 continue 2564 } 2565 if !task.Status().Ready() { 2566 return false 2567 } 2568 } 2569 return true 2570 } 2571 2572 // refreshedSnaps returns the instance names of the snaps successfully refreshed 2573 // in the last batch of refreshes before the given (re-refresh) task. 2574 // 2575 // It does this by advancing through the given task's change's tasks, keeping 2576 // track of the instance names from the first SnapSetup in every lane, stopping 2577 // when finding the given task, and resetting things when finding a different 2578 // re-refresh task (that indicates the end of a batch that isn't the given one). 2579 func refreshedSnaps(reTask *state.Task) []string { 2580 // NOTE nothing requires reTask to be a check-rerefresh task, nor even to be in 2581 // a refresh-ish change, but it doesn't make much sense to call this otherwise. 2582 tid := reTask.ID() 2583 laneSnaps := map[int]string{} 2584 // change.Tasks() preserves the order tasks were added, otherwise it all falls apart 2585 for _, task := range reTask.Change().Tasks() { 2586 if task.ID() == tid { 2587 // we've reached ourselves; we don't care about anything beyond this 2588 break 2589 } 2590 if task.Kind() == "check-rerefresh" { 2591 // we've reached a previous check-rerefresh (but not ourselves). 2592 // Only snaps in tasks after this point are of interest. 2593 laneSnaps = map[int]string{} 2594 } 2595 lanes := task.Lanes() 2596 if len(lanes) != 1 { 2597 // can't happen, really 2598 continue 2599 } 2600 lane := lanes[0] 2601 if lane == 0 { 2602 // not really a lane 2603 continue 2604 } 2605 if task.Status() != state.DoneStatus { 2606 // ignore non-successful lane (1) 2607 laneSnaps[lane] = "" 2608 continue 2609 } 2610 if _, ok := laneSnaps[lane]; ok { 2611 // ignore lanes we've already seen (including ones explicitly ignored in (1)) 2612 continue 2613 } 2614 var snapsup SnapSetup 2615 if err := task.Get("snap-setup", &snapsup); err != nil { 2616 continue 2617 } 2618 laneSnaps[lane] = snapsup.InstanceName() 2619 } 2620 2621 snapNames := make([]string, 0, len(laneSnaps)) 2622 for _, name := range laneSnaps { 2623 if name == "" { 2624 // the lane was unsuccessful 2625 continue 2626 } 2627 snapNames = append(snapNames, name) 2628 } 2629 return snapNames 2630 } 2631 2632 // reRefreshSetup holds the necessary details to re-refresh snaps that need it 2633 type reRefreshSetup struct { 2634 UserID int `json:"user-id,omitempty"` 2635 *Flags 2636 } 2637 2638 // reRefreshUpdateMany exists just to make testing simpler 2639 var reRefreshUpdateMany = updateManyFiltered 2640 2641 // reRefreshFilter is an updateFilter that returns whether the given update 2642 // needs a re-refresh because of further epoch transitions available. 2643 func reRefreshFilter(update *snap.Info, snapst *SnapState) bool { 2644 cur, err := snapst.CurrentInfo() 2645 if err != nil { 2646 return false 2647 } 2648 return !update.Epoch.Equal(&cur.Epoch) 2649 } 2650 2651 var reRefreshRetryTimeout = time.Second / 10 2652 2653 func (m *SnapManager) doCheckReRefresh(t *state.Task, tomb *tomb.Tomb) error { 2654 st := t.State() 2655 st.Lock() 2656 defer st.Unlock() 2657 2658 if numHaltTasks := t.NumHaltTasks(); numHaltTasks > 0 { 2659 logger.Panicf("Re-refresh task has %d tasks waiting for it.", numHaltTasks) 2660 } 2661 2662 if !changeReadyUpToTask(t) { 2663 return &state.Retry{After: reRefreshRetryTimeout, Reason: "pending refreshes"} 2664 } 2665 snaps := refreshedSnaps(t) 2666 if len(snaps) == 0 { 2667 // nothing to do (maybe everything failed) 2668 return nil 2669 } 2670 2671 var re reRefreshSetup 2672 if err := t.Get("rerefresh-setup", &re); err != nil { 2673 return err 2674 } 2675 chg := t.Change() 2676 updated, tasksets, err := reRefreshUpdateMany(tomb.Context(nil), st, snaps, re.UserID, reRefreshFilter, re.Flags, chg.ID()) 2677 if err != nil { 2678 return err 2679 } 2680 2681 if len(updated) == 0 { 2682 t.Logf("No re-refreshes found.") 2683 } else { 2684 t.Logf("Found re-refresh for %s.", strutil.Quoted(updated)) 2685 2686 for _, taskset := range tasksets { 2687 chg.AddAll(taskset) 2688 } 2689 st.EnsureBefore(0) 2690 } 2691 t.SetStatus(state.DoneStatus) 2692 2693 return nil 2694 } 2695 2696 // InjectTasks makes all the halt tasks of the mainTask wait for extraTasks; 2697 // extraTasks join the same lane and change as the mainTask. 2698 func InjectTasks(mainTask *state.Task, extraTasks *state.TaskSet) { 2699 lanes := mainTask.Lanes() 2700 if len(lanes) == 1 && lanes[0] == 0 { 2701 lanes = nil 2702 } 2703 for _, l := range lanes { 2704 extraTasks.JoinLane(l) 2705 } 2706 2707 chg := mainTask.Change() 2708 // Change shouldn't normally be nil, except for cases where 2709 // this helper is used before tasks are added to a change. 2710 if chg != nil { 2711 chg.AddAll(extraTasks) 2712 } 2713 2714 // make all halt tasks of the mainTask wait on extraTasks 2715 ht := mainTask.HaltTasks() 2716 for _, t := range ht { 2717 t.WaitAll(extraTasks) 2718 } 2719 2720 // make the extra tasks wait for main task 2721 extraTasks.WaitFor(mainTask) 2722 } 2723 2724 func InjectAutoConnect(mainTask *state.Task, snapsup *SnapSetup) { 2725 st := mainTask.State() 2726 autoConnect := st.NewTask("auto-connect", fmt.Sprintf(i18n.G("Automatically connect eligible plugs and slots of snap %q"), snapsup.InstanceName())) 2727 autoConnect.Set("snap-setup", snapsup) 2728 InjectTasks(mainTask, state.NewTaskSet(autoConnect)) 2729 mainTask.Logf("added auto-connect task") 2730 }