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