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