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