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