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