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