github.com/ethanhsieh/snapd@v0.0.0-20210615102523-3db9b8e4edc5/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 err = m.backend.StopServices(svcs, stopReason, progress.Null, perfTimings) 1923 if err != nil { 1924 return err 1925 } 1926 1927 return nil 1928 } 1929 1930 func (m *SnapManager) stopSnapServices(t *state.Task, _ *tomb.Tomb) error { 1931 st := t.State() 1932 st.Lock() 1933 defer st.Unlock() 1934 1935 perfTimings := state.TimingsForTask(t) 1936 defer perfTimings.Save(st) 1937 1938 snapsup, snapst, err := snapSetupAndState(t) 1939 if err != nil { 1940 return err 1941 } 1942 1943 currentInfo, err := snapst.CurrentInfo() 1944 if err != nil { 1945 return err 1946 } 1947 svcs := currentInfo.Services() 1948 if len(svcs) == 0 { 1949 return nil 1950 } 1951 1952 var stopReason snap.ServiceStopReason 1953 if err := t.Get("stop-reason", &stopReason); err != nil && err != state.ErrNoState { 1954 return err 1955 } 1956 1957 pb := NewTaskProgressAdapterUnlocked(t) 1958 st.Unlock() 1959 defer st.Lock() 1960 1961 // stop the services 1962 err = m.backend.StopServices(svcs, stopReason, pb, perfTimings) 1963 if err != nil { 1964 return err 1965 } 1966 1967 // get the disabled services after we stopped all the services. 1968 // this list is not meant to save what services are disabled at any given 1969 // time, specifically just what services are disabled while systemd loses 1970 // track of the services. this list is also used to determine what services are enabled 1971 // when we start services of a new revision of the snap in 1972 // start-snap-services handler. 1973 disabledServices, err := m.queryDisabledServices(currentInfo, pb) 1974 if err != nil { 1975 return err 1976 } 1977 1978 st.Lock() 1979 defer st.Unlock() 1980 1981 // for undo 1982 t.Set("old-last-active-disabled-services", snapst.LastActiveDisabledServices) 1983 // undo could queryDisabledServices, but this avoids it 1984 t.Set("disabled-services", disabledServices) 1985 1986 // add to the disabled services list in snapst services which were disabled 1987 // for usage across changes like in reverting and enabling after being 1988 // disabled. 1989 // we keep what's already in the list in snapst because that list is 1990 // services which were previously present in the snap and disabled, but are 1991 // no longer present. 1992 snapst.LastActiveDisabledServices = append( 1993 snapst.LastActiveDisabledServices, 1994 disabledServices..., 1995 ) 1996 1997 // reset services tracked by operations from hooks 1998 snapst.ServicesDisabledByHooks = nil 1999 snapst.ServicesEnabledByHooks = nil 2000 2001 Set(st, snapsup.InstanceName(), snapst) 2002 2003 return nil 2004 } 2005 2006 func (m *SnapManager) undoStopSnapServices(t *state.Task, _ *tomb.Tomb) error { 2007 st := t.State() 2008 st.Lock() 2009 defer st.Unlock() 2010 2011 perfTimings := state.TimingsForTask(t) 2012 defer perfTimings.Save(st) 2013 2014 snapsup, snapst, err := snapSetupAndState(t) 2015 if err != nil { 2016 return err 2017 } 2018 currentInfo, err := snapst.CurrentInfo() 2019 if err != nil { 2020 return err 2021 } 2022 2023 svcs := currentInfo.Services() 2024 if len(svcs) == 0 { 2025 return nil 2026 } 2027 2028 startupOrdered, err := snap.SortServices(svcs) 2029 if err != nil { 2030 return err 2031 } 2032 2033 var lastActiveDisabled []string 2034 if err := t.Get("old-last-active-disabled-services", &lastActiveDisabled); err != nil && err != state.ErrNoState { 2035 return err 2036 } 2037 snapst.LastActiveDisabledServices = lastActiveDisabled 2038 Set(st, snapsup.InstanceName(), snapst) 2039 2040 var disabledServices []string 2041 if err := t.Get("disabled-services", &disabledServices); err != nil && err != state.ErrNoState { 2042 return err 2043 } 2044 2045 st.Unlock() 2046 err = m.backend.StartServices(startupOrdered, disabledServices, progress.Null, perfTimings) 2047 st.Lock() 2048 if err != nil { 2049 return err 2050 } 2051 2052 return nil 2053 } 2054 2055 func (m *SnapManager) doUnlinkSnap(t *state.Task, _ *tomb.Tomb) error { 2056 // invoked only if snap has a current active revision, during remove or 2057 // disable 2058 // in case of the snapd snap, we only reach here if disabling or removal 2059 // was deemed ok by earlier checks 2060 2061 st := t.State() 2062 st.Lock() 2063 defer st.Unlock() 2064 2065 snapsup, snapst, err := snapSetupAndState(t) 2066 if err != nil { 2067 return err 2068 } 2069 2070 info, err := Info(t.State(), snapsup.InstanceName(), snapsup.Revision()) 2071 if err != nil { 2072 return err 2073 } 2074 2075 // do the final unlink 2076 unlinkCtx := backend.LinkContext{ 2077 FirstInstall: false, 2078 } 2079 err = m.backend.UnlinkSnap(info, unlinkCtx, NewTaskProgressAdapterLocked(t)) 2080 if err != nil { 2081 return err 2082 } 2083 2084 // mark as inactive 2085 snapst.Active = false 2086 Set(st, snapsup.InstanceName(), snapst) 2087 2088 // Notify link snap participants about link changes. 2089 notifyLinkParticipants(t, snapsup.InstanceName()) 2090 2091 return err 2092 } 2093 2094 func (m *SnapManager) undoUnlinkSnap(t *state.Task, _ *tomb.Tomb) error { 2095 st := t.State() 2096 st.Lock() 2097 defer st.Unlock() 2098 2099 perfTimings := state.TimingsForTask(t) 2100 defer perfTimings.Save(st) 2101 2102 snapsup, snapst, err := snapSetupAndState(t) 2103 if err != nil { 2104 return err 2105 } 2106 2107 isInstalled := snapst.IsInstalled() 2108 if !isInstalled { 2109 return fmt.Errorf("internal error: snap %q not installed anymore", snapsup.InstanceName()) 2110 } 2111 2112 info, err := snapst.CurrentInfo() 2113 if err != nil { 2114 return err 2115 } 2116 2117 deviceCtx, err := DeviceCtx(st, t, nil) 2118 if err != nil { 2119 return err 2120 } 2121 2122 // undo here may be part of failed snap remove change, in which case a later 2123 // "clear-snap" task could have been executed and some or all of the 2124 // data of this snap could be lost. If that's the case, then we should not 2125 // enable the snap back. 2126 // XXX: should make an exception for snapd/core? 2127 place := snapsup.placeInfo() 2128 for _, dir := range []string{place.DataDir(), place.CommonDataDir()} { 2129 if exists, _, _ := osutil.DirExists(dir); !exists { 2130 t.Logf("cannot link snap %q back, some of its data has already been removed", snapsup.InstanceName()) 2131 // TODO: mark the snap broken at the SnapState level when we have 2132 // such concept. 2133 return nil 2134 } 2135 } 2136 2137 snapst.Active = true 2138 Set(st, snapsup.InstanceName(), snapst) 2139 2140 opts, err := SnapServiceOptions(st, snapsup.InstanceName(), nil) 2141 if err != nil { 2142 return err 2143 } 2144 linkCtx := backend.LinkContext{ 2145 FirstInstall: false, 2146 ServiceOptions: opts, 2147 } 2148 reboot, err := m.backend.LinkSnap(info, deviceCtx, linkCtx, perfTimings) 2149 if err != nil { 2150 return err 2151 } 2152 2153 // Notify link snap participants about link changes. 2154 notifyLinkParticipants(t, snapsup.InstanceName()) 2155 2156 // if we just linked back a core snap, request a restart 2157 // so that we switch executing its snapd. 2158 m.maybeRestart(t, info, reboot, deviceCtx) 2159 2160 return nil 2161 } 2162 2163 func (m *SnapManager) doClearSnapData(t *state.Task, _ *tomb.Tomb) error { 2164 st := t.State() 2165 st.Lock() 2166 snapsup, snapst, err := snapSetupAndState(t) 2167 st.Unlock() 2168 if err != nil { 2169 return err 2170 } 2171 2172 st.Lock() 2173 info, err := Info(t.State(), snapsup.InstanceName(), snapsup.Revision()) 2174 st.Unlock() 2175 if err != nil { 2176 return err 2177 } 2178 2179 if err = m.backend.RemoveSnapData(info); err != nil { 2180 return err 2181 } 2182 2183 if len(snapst.Sequence) == 1 { 2184 // Only remove data common between versions if this is the last version 2185 if err = m.backend.RemoveSnapCommonData(info); err != nil { 2186 return err 2187 } 2188 2189 st.Lock() 2190 defer st.Unlock() 2191 2192 otherInstances, err := hasOtherInstances(st, snapsup.InstanceName()) 2193 if err != nil { 2194 return err 2195 } 2196 // Snap data directory can be removed now too 2197 if err := m.backend.RemoveSnapDataDir(info, otherInstances); err != nil { 2198 return err 2199 } 2200 } 2201 2202 return nil 2203 } 2204 2205 func (m *SnapManager) doDiscardSnap(t *state.Task, _ *tomb.Tomb) error { 2206 st := t.State() 2207 st.Lock() 2208 defer st.Unlock() 2209 2210 snapsup, snapst, err := snapSetupAndState(t) 2211 if err != nil { 2212 return err 2213 } 2214 2215 deviceCtx, err := DeviceCtx(st, t, nil) 2216 if err != nil { 2217 return err 2218 } 2219 2220 if snapst.Current == snapsup.Revision() && snapst.Active { 2221 return fmt.Errorf("internal error: cannot discard snap %q: still active", snapsup.InstanceName()) 2222 } 2223 2224 if len(snapst.Sequence) == 1 { 2225 snapst.Sequence = nil 2226 snapst.Current = snap.Revision{} 2227 } else { 2228 newSeq := make([]*snap.SideInfo, 0, len(snapst.Sequence)) 2229 for _, si := range snapst.Sequence { 2230 if si.Revision == snapsup.Revision() { 2231 // leave out 2232 continue 2233 } 2234 newSeq = append(newSeq, si) 2235 } 2236 snapst.Sequence = newSeq 2237 if snapst.Current == snapsup.Revision() { 2238 snapst.Current = newSeq[len(newSeq)-1].Revision 2239 } 2240 } 2241 2242 pb := NewTaskProgressAdapterLocked(t) 2243 typ, err := snapst.Type() 2244 if err != nil { 2245 return err 2246 } 2247 err = m.backend.RemoveSnapFiles(snapsup.placeInfo(), typ, nil, deviceCtx, pb) 2248 if err != nil { 2249 t.Errorf("cannot remove snap file %q, will retry in 3 mins: %s", snapsup.InstanceName(), err) 2250 return &state.Retry{After: 3 * time.Minute} 2251 } 2252 if len(snapst.Sequence) == 0 { 2253 // Remove configuration associated with this snap. 2254 err = config.DeleteSnapConfig(st, snapsup.InstanceName()) 2255 if err != nil { 2256 return err 2257 } 2258 err = m.backend.DiscardSnapNamespace(snapsup.InstanceName()) 2259 if err != nil { 2260 t.Errorf("cannot discard snap namespace %q, will retry in 3 mins: %s", snapsup.InstanceName(), err) 2261 return &state.Retry{After: 3 * time.Minute} 2262 } 2263 err = m.backend.RemoveSnapInhibitLock(snapsup.InstanceName()) 2264 if err != nil { 2265 return err 2266 } 2267 if err := m.removeSnapCookie(st, snapsup.InstanceName()); err != nil { 2268 return fmt.Errorf("cannot remove snap cookie: %v", err) 2269 } 2270 2271 otherInstances, err := hasOtherInstances(st, snapsup.InstanceName()) 2272 if err != nil { 2273 return err 2274 } 2275 2276 if err := m.backend.RemoveSnapDir(snapsup.placeInfo(), otherInstances); err != nil { 2277 return fmt.Errorf("cannot remove snap directory: %v", err) 2278 } 2279 2280 // try to remove the auxiliary store info 2281 if err := discardAuxStoreInfo(snapsup.SideInfo.SnapID); err != nil { 2282 logger.Noticef("Cannot remove auxiliary store info for %q: %v", snapsup.InstanceName(), err) 2283 } 2284 2285 // XXX: also remove sequence files? 2286 2287 // remove the snap from any quota groups it may have been in, otherwise 2288 // that quota group may get into an inconsistent state 2289 if err := EnsureSnapAbsentFromQuotaGroup(st, snapsup.InstanceName()); err != nil { 2290 return err 2291 } 2292 } 2293 if err = config.DiscardRevisionConfig(st, snapsup.InstanceName(), snapsup.Revision()); err != nil { 2294 return err 2295 } 2296 if err = SecurityProfilesRemoveLate(snapsup.InstanceName(), snapsup.Revision(), snapsup.Type); err != nil { 2297 return err 2298 } 2299 Set(st, snapsup.InstanceName(), snapst) 2300 return nil 2301 } 2302 2303 /* aliases v2 2304 2305 aliases v2 implementation uses the following tasks: 2306 2307 * for install/refresh/remove/enable/disable etc 2308 2309 - remove-aliases: remove aliases of a snap from disk and mark them pending 2310 2311 - setup-aliases: (re)creates aliases from snap state, mark them as 2312 not pending 2313 2314 - set-auto-aliases: updates aliases snap state based on the 2315 snap-declaration and current revision info of the snap 2316 2317 * for refresh & when the snap-declaration aliases change without a 2318 new revision 2319 2320 - refresh-aliases: updates aliases snap state and updates them on disk too; 2321 its undo is used generically by other tasks as well 2322 2323 - prune-auto-aliases: used for the special case of automatic 2324 aliases transferred from one snap to another to prune them from 2325 the source snaps to avoid conflicts in later operations 2326 2327 * for alias/unalias/prefer: 2328 2329 - alias: creates a manual alias 2330 2331 - unalias: removes a manual alias 2332 2333 - disable-aliases: disable the automatic aliases of a snap and 2334 removes all manual ones as well 2335 2336 - prefer-aliases: enables the automatic aliases of a snap after 2337 disabling any other snap conflicting aliases 2338 2339 */ 2340 2341 func (m *SnapManager) doSetAutoAliases(t *state.Task, _ *tomb.Tomb) error { 2342 st := t.State() 2343 st.Lock() 2344 defer st.Unlock() 2345 snapsup, snapst, err := snapSetupAndState(t) 2346 if err != nil { 2347 return err 2348 } 2349 snapName := snapsup.InstanceName() 2350 curInfo, err := snapst.CurrentInfo() 2351 if err != nil { 2352 return err 2353 } 2354 2355 // --unaliased 2356 if snapsup.Unaliased { 2357 t.Set("old-auto-aliases-disabled", snapst.AutoAliasesDisabled) 2358 snapst.AutoAliasesDisabled = true 2359 } 2360 2361 curAliases := snapst.Aliases 2362 // TODO: implement --prefer logic 2363 newAliases, err := refreshAliases(st, curInfo, curAliases) 2364 if err != nil { 2365 return err 2366 } 2367 _, err = checkAliasesConflicts(st, snapName, snapst.AutoAliasesDisabled, newAliases, nil) 2368 if err != nil { 2369 return err 2370 } 2371 2372 t.Set("old-aliases-v2", curAliases) 2373 // noop, except on first install where we need to set this here 2374 snapst.AliasesPending = true 2375 snapst.Aliases = newAliases 2376 Set(st, snapName, snapst) 2377 return nil 2378 } 2379 2380 func (m *SnapManager) doRemoveAliases(t *state.Task, _ *tomb.Tomb) error { 2381 st := t.State() 2382 st.Lock() 2383 defer st.Unlock() 2384 snapsup, snapst, err := snapSetupAndState(t) 2385 if err != nil { 2386 return err 2387 } 2388 snapName := snapsup.InstanceName() 2389 2390 err = m.backend.RemoveSnapAliases(snapName) 2391 if err != nil { 2392 return err 2393 } 2394 2395 snapst.AliasesPending = true 2396 Set(st, snapName, snapst) 2397 return nil 2398 } 2399 2400 func (m *SnapManager) doSetupAliases(t *state.Task, _ *tomb.Tomb) error { 2401 st := t.State() 2402 st.Lock() 2403 defer st.Unlock() 2404 snapsup, snapst, err := snapSetupAndState(t) 2405 if err != nil { 2406 return err 2407 } 2408 snapName := snapsup.InstanceName() 2409 curAliases := snapst.Aliases 2410 2411 _, _, err = applyAliasesChange(snapName, autoDis, nil, snapst.AutoAliasesDisabled, curAliases, m.backend, doApply) 2412 if err != nil { 2413 return err 2414 } 2415 2416 snapst.AliasesPending = false 2417 Set(st, snapName, snapst) 2418 return nil 2419 } 2420 2421 func (m *SnapManager) doRefreshAliases(t *state.Task, _ *tomb.Tomb) error { 2422 st := t.State() 2423 st.Lock() 2424 defer st.Unlock() 2425 snapsup, snapst, err := snapSetupAndState(t) 2426 if err != nil { 2427 return err 2428 } 2429 snapName := snapsup.InstanceName() 2430 curInfo, err := snapst.CurrentInfo() 2431 if err != nil { 2432 return err 2433 } 2434 2435 autoDisabled := snapst.AutoAliasesDisabled 2436 curAliases := snapst.Aliases 2437 newAliases, err := refreshAliases(st, curInfo, curAliases) 2438 if err != nil { 2439 return err 2440 } 2441 _, err = checkAliasesConflicts(st, snapName, autoDisabled, newAliases, nil) 2442 if err != nil { 2443 return err 2444 } 2445 2446 if !snapst.AliasesPending { 2447 if _, _, err := applyAliasesChange(snapName, autoDisabled, curAliases, autoDisabled, newAliases, m.backend, doApply); err != nil { 2448 return err 2449 } 2450 } 2451 2452 t.Set("old-aliases-v2", curAliases) 2453 snapst.Aliases = newAliases 2454 Set(st, snapName, snapst) 2455 return nil 2456 } 2457 2458 func (m *SnapManager) undoRefreshAliases(t *state.Task, _ *tomb.Tomb) error { 2459 st := t.State() 2460 st.Lock() 2461 defer st.Unlock() 2462 var oldAliases map[string]*AliasTarget 2463 err := t.Get("old-aliases-v2", &oldAliases) 2464 if err == state.ErrNoState { 2465 // nothing to do 2466 return nil 2467 } 2468 if err != nil { 2469 return err 2470 } 2471 snapsup, snapst, err := snapSetupAndState(t) 2472 if err != nil { 2473 return err 2474 } 2475 snapName := snapsup.InstanceName() 2476 curAutoDisabled := snapst.AutoAliasesDisabled 2477 autoDisabled := curAutoDisabled 2478 if err = t.Get("old-auto-aliases-disabled", &autoDisabled); err != nil && err != state.ErrNoState { 2479 return err 2480 } 2481 2482 var otherSnapDisabled map[string]*otherDisabledAliases 2483 if err = t.Get("other-disabled-aliases", &otherSnapDisabled); err != nil && err != state.ErrNoState { 2484 return err 2485 } 2486 2487 // check if the old states creates conflicts now 2488 _, err = checkAliasesConflicts(st, snapName, autoDisabled, oldAliases, nil) 2489 if _, ok := err.(*AliasConflictError); ok { 2490 // best we can do is reinstate with all aliases disabled 2491 t.Errorf("cannot reinstate alias state because of conflicts, disabling: %v", err) 2492 oldAliases, _ = disableAliases(oldAliases) 2493 autoDisabled = true 2494 } else if err != nil { 2495 return err 2496 } 2497 2498 if !snapst.AliasesPending { 2499 curAliases := snapst.Aliases 2500 if _, _, err := applyAliasesChange(snapName, curAutoDisabled, curAliases, autoDisabled, oldAliases, m.backend, doApply); err != nil { 2501 return err 2502 } 2503 } 2504 2505 snapst.AutoAliasesDisabled = autoDisabled 2506 snapst.Aliases = oldAliases 2507 newSnapStates := make(map[string]*SnapState, 1+len(otherSnapDisabled)) 2508 newSnapStates[snapName] = snapst 2509 2510 // if we disabled other snap aliases try to undo that 2511 conflicting := make(map[string]bool, len(otherSnapDisabled)) 2512 otherCurSnapStates := make(map[string]*SnapState, len(otherSnapDisabled)) 2513 for otherSnap, otherDisabled := range otherSnapDisabled { 2514 var otherSnapState SnapState 2515 err := Get(st, otherSnap, &otherSnapState) 2516 if err != nil { 2517 return err 2518 } 2519 otherCurInfo, err := otherSnapState.CurrentInfo() 2520 if err != nil { 2521 return err 2522 } 2523 2524 otherCurSnapStates[otherSnap] = &otherSnapState 2525 2526 autoDisabled := otherSnapState.AutoAliasesDisabled 2527 if otherDisabled.Auto { 2528 // automatic aliases of other were disabled, undo that 2529 autoDisabled = false 2530 } 2531 otherAliases := reenableAliases(otherCurInfo, otherSnapState.Aliases, otherDisabled.Manual) 2532 // check for conflicts taking into account 2533 // re-enabled aliases 2534 conflicts, err := checkAliasesConflicts(st, otherSnap, autoDisabled, otherAliases, newSnapStates) 2535 if _, ok := err.(*AliasConflictError); ok { 2536 conflicting[otherSnap] = true 2537 for conflictSnap := range conflicts { 2538 conflicting[conflictSnap] = true 2539 } 2540 } else if err != nil { 2541 return err 2542 } 2543 2544 newSnapState := otherSnapState 2545 newSnapState.Aliases = otherAliases 2546 newSnapState.AutoAliasesDisabled = autoDisabled 2547 newSnapStates[otherSnap] = &newSnapState 2548 } 2549 2550 // apply non-conflicting other 2551 for otherSnap, otherSnapState := range otherCurSnapStates { 2552 if conflicting[otherSnap] { 2553 // keep as it was 2554 continue 2555 } 2556 newSnapSt := newSnapStates[otherSnap] 2557 if !otherSnapState.AliasesPending { 2558 if _, _, err := applyAliasesChange(otherSnap, otherSnapState.AutoAliasesDisabled, otherSnapState.Aliases, newSnapSt.AutoAliasesDisabled, newSnapSt.Aliases, m.backend, doApply); err != nil { 2559 return err 2560 } 2561 } 2562 } 2563 2564 for instanceName, snapst := range newSnapStates { 2565 if conflicting[instanceName] { 2566 // keep as it was 2567 continue 2568 } 2569 Set(st, instanceName, snapst) 2570 } 2571 return nil 2572 } 2573 2574 func (m *SnapManager) doPruneAutoAliases(t *state.Task, _ *tomb.Tomb) error { 2575 st := t.State() 2576 st.Lock() 2577 defer st.Unlock() 2578 snapsup, snapst, err := snapSetupAndState(t) 2579 if err != nil { 2580 return err 2581 } 2582 var which []string 2583 err = t.Get("aliases", &which) 2584 if err != nil { 2585 return err 2586 } 2587 snapName := snapsup.InstanceName() 2588 autoDisabled := snapst.AutoAliasesDisabled 2589 curAliases := snapst.Aliases 2590 2591 newAliases := pruneAutoAliases(curAliases, which) 2592 2593 if !snapst.AliasesPending { 2594 if _, _, err := applyAliasesChange(snapName, autoDisabled, curAliases, autoDisabled, newAliases, m.backend, doApply); err != nil { 2595 return err 2596 } 2597 } 2598 2599 t.Set("old-aliases-v2", curAliases) 2600 snapst.Aliases = newAliases 2601 Set(st, snapName, snapst) 2602 return nil 2603 } 2604 2605 type changedAlias struct { 2606 Snap string `json:"snap"` 2607 App string `json:"app"` 2608 Alias string `json:"alias"` 2609 } 2610 2611 func aliasesTrace(t *state.Task, added, removed []*backend.Alias) error { 2612 chg := t.Change() 2613 var data map[string]interface{} 2614 err := chg.Get("api-data", &data) 2615 if err != nil && err != state.ErrNoState { 2616 return err 2617 } 2618 if len(data) == 0 { 2619 data = make(map[string]interface{}) 2620 } 2621 2622 curAdded, _ := data["aliases-added"].([]interface{}) 2623 for _, a := range added { 2624 snap, app := snap.SplitSnapApp(a.Target) 2625 curAdded = append(curAdded, &changedAlias{ 2626 Snap: snap, 2627 App: app, 2628 Alias: a.Name, 2629 }) 2630 } 2631 data["aliases-added"] = curAdded 2632 2633 curRemoved, _ := data["aliases-removed"].([]interface{}) 2634 for _, a := range removed { 2635 snap, app := snap.SplitSnapApp(a.Target) 2636 curRemoved = append(curRemoved, &changedAlias{ 2637 Snap: snap, 2638 App: app, 2639 Alias: a.Name, 2640 }) 2641 } 2642 data["aliases-removed"] = curRemoved 2643 2644 chg.Set("api-data", data) 2645 return nil 2646 } 2647 2648 func (m *SnapManager) doAlias(t *state.Task, _ *tomb.Tomb) error { 2649 st := t.State() 2650 st.Lock() 2651 defer st.Unlock() 2652 snapsup, snapst, err := snapSetupAndState(t) 2653 if err != nil { 2654 return err 2655 } 2656 var target, alias string 2657 err = t.Get("target", &target) 2658 if err != nil { 2659 return err 2660 } 2661 err = t.Get("alias", &alias) 2662 if err != nil { 2663 return err 2664 } 2665 2666 snapName := snapsup.InstanceName() 2667 curInfo, err := snapst.CurrentInfo() 2668 if err != nil { 2669 return err 2670 } 2671 2672 autoDisabled := snapst.AutoAliasesDisabled 2673 curAliases := snapst.Aliases 2674 newAliases, err := manualAlias(curInfo, curAliases, target, alias) 2675 if err != nil { 2676 return err 2677 } 2678 _, err = checkAliasesConflicts(st, snapName, autoDisabled, newAliases, nil) 2679 if err != nil { 2680 return err 2681 } 2682 2683 added, removed, err := applyAliasesChange(snapName, autoDisabled, curAliases, autoDisabled, newAliases, m.backend, snapst.AliasesPending) 2684 if err != nil { 2685 return err 2686 } 2687 if err := aliasesTrace(t, added, removed); err != nil { 2688 return err 2689 } 2690 2691 t.Set("old-aliases-v2", curAliases) 2692 snapst.Aliases = newAliases 2693 Set(st, snapName, snapst) 2694 return nil 2695 } 2696 2697 func (m *SnapManager) doDisableAliases(t *state.Task, _ *tomb.Tomb) error { 2698 st := t.State() 2699 st.Lock() 2700 defer st.Unlock() 2701 snapsup, snapst, err := snapSetupAndState(t) 2702 if err != nil { 2703 return err 2704 } 2705 snapName := snapsup.InstanceName() 2706 2707 oldAutoDisabled := snapst.AutoAliasesDisabled 2708 oldAliases := snapst.Aliases 2709 newAliases, _ := disableAliases(oldAliases) 2710 2711 added, removed, err := applyAliasesChange(snapName, oldAutoDisabled, oldAliases, autoDis, newAliases, m.backend, snapst.AliasesPending) 2712 if err != nil { 2713 return err 2714 } 2715 if err := aliasesTrace(t, added, removed); err != nil { 2716 return err 2717 } 2718 2719 t.Set("old-auto-aliases-disabled", oldAutoDisabled) 2720 snapst.AutoAliasesDisabled = true 2721 t.Set("old-aliases-v2", oldAliases) 2722 snapst.Aliases = newAliases 2723 Set(st, snapName, snapst) 2724 return nil 2725 } 2726 2727 func (m *SnapManager) doUnalias(t *state.Task, _ *tomb.Tomb) error { 2728 st := t.State() 2729 st.Lock() 2730 defer st.Unlock() 2731 snapsup, snapst, err := snapSetupAndState(t) 2732 if err != nil { 2733 return err 2734 } 2735 var alias string 2736 err = t.Get("alias", &alias) 2737 if err != nil { 2738 return err 2739 } 2740 snapName := snapsup.InstanceName() 2741 2742 autoDisabled := snapst.AutoAliasesDisabled 2743 oldAliases := snapst.Aliases 2744 newAliases, err := manualUnalias(oldAliases, alias) 2745 if err != nil { 2746 return err 2747 } 2748 2749 added, removed, err := applyAliasesChange(snapName, autoDisabled, oldAliases, autoDisabled, newAliases, m.backend, snapst.AliasesPending) 2750 if err != nil { 2751 return err 2752 } 2753 if err := aliasesTrace(t, added, removed); err != nil { 2754 return err 2755 } 2756 2757 t.Set("old-aliases-v2", oldAliases) 2758 snapst.Aliases = newAliases 2759 Set(st, snapName, snapst) 2760 return nil 2761 } 2762 2763 // otherDisabledAliases is used to track for the benefit of undo what 2764 // changes were made aka what aliases were disabled of another 2765 // conflicting snap by prefer logic 2766 type otherDisabledAliases struct { 2767 // Auto records whether prefer had to disable automatic aliases 2768 Auto bool `json:"auto,omitempty"` 2769 // Manual records which manual aliases were removed by prefer 2770 Manual map[string]string `json:"manual,omitempty"` 2771 } 2772 2773 func (m *SnapManager) doPreferAliases(t *state.Task, _ *tomb.Tomb) error { 2774 st := t.State() 2775 st.Lock() 2776 defer st.Unlock() 2777 snapsup, snapst, err := snapSetupAndState(t) 2778 if err != nil { 2779 return err 2780 } 2781 instanceName := snapsup.InstanceName() 2782 2783 if !snapst.AutoAliasesDisabled { 2784 // already enabled, nothing to do 2785 return nil 2786 } 2787 2788 curAliases := snapst.Aliases 2789 aliasConflicts, err := checkAliasesConflicts(st, instanceName, autoEn, curAliases, nil) 2790 conflErr, isConflErr := err.(*AliasConflictError) 2791 if err != nil && !isConflErr { 2792 return err 2793 } 2794 if isConflErr && conflErr.Conflicts == nil { 2795 // it's a snap command namespace conflict, we cannot remedy it 2796 return conflErr 2797 } 2798 // proceed to disable conflicting aliases as needed 2799 // before re-enabling instanceName aliases 2800 2801 otherSnapStates := make(map[string]*SnapState, len(aliasConflicts)) 2802 otherSnapDisabled := make(map[string]*otherDisabledAliases, len(aliasConflicts)) 2803 for otherSnap := range aliasConflicts { 2804 var otherSnapState SnapState 2805 err := Get(st, otherSnap, &otherSnapState) 2806 if err != nil { 2807 return err 2808 } 2809 2810 otherAliases, disabledManual := disableAliases(otherSnapState.Aliases) 2811 2812 added, removed, err := applyAliasesChange(otherSnap, otherSnapState.AutoAliasesDisabled, otherSnapState.Aliases, autoDis, otherAliases, m.backend, otherSnapState.AliasesPending) 2813 if err != nil { 2814 return err 2815 } 2816 if err := aliasesTrace(t, added, removed); err != nil { 2817 return err 2818 } 2819 2820 var otherDisabled otherDisabledAliases 2821 otherDisabled.Manual = disabledManual 2822 otherSnapState.Aliases = otherAliases 2823 // disable automatic aliases as needed 2824 if !otherSnapState.AutoAliasesDisabled && len(otherAliases) != 0 { 2825 // record that we did disable automatic aliases 2826 otherDisabled.Auto = true 2827 otherSnapState.AutoAliasesDisabled = true 2828 } 2829 otherSnapDisabled[otherSnap] = &otherDisabled 2830 otherSnapStates[otherSnap] = &otherSnapState 2831 } 2832 2833 added, removed, err := applyAliasesChange(instanceName, autoDis, curAliases, autoEn, curAliases, m.backend, snapst.AliasesPending) 2834 if err != nil { 2835 return err 2836 } 2837 if err := aliasesTrace(t, added, removed); err != nil { 2838 return err 2839 } 2840 2841 for otherSnap, otherSnapState := range otherSnapStates { 2842 Set(st, otherSnap, otherSnapState) 2843 } 2844 if len(otherSnapDisabled) != 0 { 2845 t.Set("other-disabled-aliases", otherSnapDisabled) 2846 } 2847 t.Set("old-auto-aliases-disabled", true) 2848 t.Set("old-aliases-v2", curAliases) 2849 snapst.AutoAliasesDisabled = false 2850 Set(st, instanceName, snapst) 2851 return nil 2852 } 2853 2854 // changeReadyUpToTask returns whether all other change's tasks are Ready. 2855 func changeReadyUpToTask(task *state.Task) bool { 2856 me := task.ID() 2857 change := task.Change() 2858 for _, task := range change.Tasks() { 2859 if me == task.ID() { 2860 // ignore self 2861 continue 2862 } 2863 if !task.Status().Ready() { 2864 return false 2865 } 2866 } 2867 return true 2868 } 2869 2870 // refreshedSnaps returns the instance names of the snaps successfully refreshed 2871 // in the last batch of refreshes before the given (re-refresh) task. 2872 // 2873 // It does this by advancing through the given task's change's tasks, keeping 2874 // track of the instance names from the first SnapSetup in every lane, stopping 2875 // when finding the given task, and resetting things when finding a different 2876 // re-refresh task (that indicates the end of a batch that isn't the given one). 2877 func refreshedSnaps(reTask *state.Task) []string { 2878 // NOTE nothing requires reTask to be a check-rerefresh task, nor even to be in 2879 // a refresh-ish change, but it doesn't make much sense to call this otherwise. 2880 tid := reTask.ID() 2881 laneSnaps := map[int]string{} 2882 // change.Tasks() preserves the order tasks were added, otherwise it all falls apart 2883 for _, task := range reTask.Change().Tasks() { 2884 if task.ID() == tid { 2885 // we've reached ourselves; we don't care about anything beyond this 2886 break 2887 } 2888 if task.Kind() == "check-rerefresh" { 2889 // we've reached a previous check-rerefresh (but not ourselves). 2890 // Only snaps in tasks after this point are of interest. 2891 laneSnaps = map[int]string{} 2892 } 2893 lanes := task.Lanes() 2894 if len(lanes) != 1 { 2895 // can't happen, really 2896 continue 2897 } 2898 lane := lanes[0] 2899 if lane == 0 { 2900 // not really a lane 2901 continue 2902 } 2903 if task.Status() != state.DoneStatus { 2904 // ignore non-successful lane (1) 2905 laneSnaps[lane] = "" 2906 continue 2907 } 2908 if _, ok := laneSnaps[lane]; ok { 2909 // ignore lanes we've already seen (including ones explicitly ignored in (1)) 2910 continue 2911 } 2912 var snapsup SnapSetup 2913 if err := task.Get("snap-setup", &snapsup); err != nil { 2914 continue 2915 } 2916 laneSnaps[lane] = snapsup.InstanceName() 2917 } 2918 2919 snapNames := make([]string, 0, len(laneSnaps)) 2920 for _, name := range laneSnaps { 2921 if name == "" { 2922 // the lane was unsuccessful 2923 continue 2924 } 2925 snapNames = append(snapNames, name) 2926 } 2927 return snapNames 2928 } 2929 2930 // reRefreshSetup holds the necessary details to re-refresh snaps that need it 2931 type reRefreshSetup struct { 2932 UserID int `json:"user-id,omitempty"` 2933 *Flags 2934 } 2935 2936 // reRefreshUpdateMany exists just to make testing simpler 2937 var reRefreshUpdateMany = updateManyFiltered 2938 2939 // reRefreshFilter is an updateFilter that returns whether the given update 2940 // needs a re-refresh because of further epoch transitions available. 2941 func reRefreshFilter(update *snap.Info, snapst *SnapState) bool { 2942 cur, err := snapst.CurrentInfo() 2943 if err != nil { 2944 return false 2945 } 2946 return !update.Epoch.Equal(&cur.Epoch) 2947 } 2948 2949 var reRefreshRetryTimeout = time.Second / 2 2950 2951 func (m *SnapManager) doCheckReRefresh(t *state.Task, tomb *tomb.Tomb) error { 2952 st := t.State() 2953 st.Lock() 2954 defer st.Unlock() 2955 2956 if numHaltTasks := t.NumHaltTasks(); numHaltTasks > 0 { 2957 logger.Panicf("Re-refresh task has %d tasks waiting for it.", numHaltTasks) 2958 } 2959 2960 if !changeReadyUpToTask(t) { 2961 return &state.Retry{After: reRefreshRetryTimeout, Reason: "pending refreshes"} 2962 } 2963 snaps := refreshedSnaps(t) 2964 if len(snaps) == 0 { 2965 // nothing to do (maybe everything failed) 2966 return nil 2967 } 2968 2969 var re reRefreshSetup 2970 if err := t.Get("rerefresh-setup", &re); err != nil { 2971 return err 2972 } 2973 chg := t.Change() 2974 updated, tasksets, err := reRefreshUpdateMany(tomb.Context(nil), st, snaps, re.UserID, reRefreshFilter, re.Flags, chg.ID()) 2975 if err != nil { 2976 return err 2977 } 2978 2979 if len(updated) == 0 { 2980 t.Logf("No re-refreshes found.") 2981 } else { 2982 t.Logf("Found re-refresh for %s.", strutil.Quoted(updated)) 2983 2984 for _, taskset := range tasksets { 2985 chg.AddAll(taskset) 2986 } 2987 st.EnsureBefore(0) 2988 } 2989 t.SetStatus(state.DoneStatus) 2990 2991 return nil 2992 } 2993 2994 func (m *SnapManager) doConditionalAutoRefresh(t *state.Task, tomb *tomb.Tomb) error { 2995 st := t.State() 2996 st.Lock() 2997 defer st.Unlock() 2998 2999 snaps, err := snapsToRefresh(t) 3000 if err != nil { 3001 return err 3002 } 3003 3004 if len(snaps) == 0 { 3005 logger.Debugf("refresh gating: no snaps to refresh") 3006 return nil 3007 } 3008 3009 tss, err := autoRefreshPhase2(context.TODO(), st, snaps) 3010 if err != nil { 3011 return err 3012 } 3013 3014 // update original auto-refresh change 3015 chg := t.Change() 3016 for _, ts := range tss { 3017 ts.WaitFor(t) 3018 chg.AddAll(ts) 3019 } 3020 t.SetStatus(state.DoneStatus) 3021 3022 st.EnsureBefore(0) 3023 return nil 3024 } 3025 3026 // InjectTasks makes all the halt tasks of the mainTask wait for extraTasks; 3027 // extraTasks join the same lane and change as the mainTask. 3028 func InjectTasks(mainTask *state.Task, extraTasks *state.TaskSet) { 3029 lanes := mainTask.Lanes() 3030 if len(lanes) == 1 && lanes[0] == 0 { 3031 lanes = nil 3032 } 3033 for _, l := range lanes { 3034 extraTasks.JoinLane(l) 3035 } 3036 3037 chg := mainTask.Change() 3038 // Change shouldn't normally be nil, except for cases where 3039 // this helper is used before tasks are added to a change. 3040 if chg != nil { 3041 chg.AddAll(extraTasks) 3042 } 3043 3044 // make all halt tasks of the mainTask wait on extraTasks 3045 ht := mainTask.HaltTasks() 3046 for _, t := range ht { 3047 t.WaitAll(extraTasks) 3048 } 3049 3050 // make the extra tasks wait for main task 3051 extraTasks.WaitFor(mainTask) 3052 } 3053 3054 func InjectAutoConnect(mainTask *state.Task, snapsup *SnapSetup) { 3055 st := mainTask.State() 3056 autoConnect := st.NewTask("auto-connect", fmt.Sprintf(i18n.G("Automatically connect eligible plugs and slots of snap %q"), snapsup.InstanceName())) 3057 autoConnect.Set("snap-setup", snapsup) 3058 InjectTasks(mainTask, state.NewTaskSet(autoConnect)) 3059 mainTask.Logf("added auto-connect task") 3060 }