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