func (x *cmdBooted) Execute(args []string) error { if len(args) > 0 { return ErrExtraArgs } if release.OnClassic { fmt.Fprintf(Stdout, i18n.G("Ignoring 'booted' on classic")) return nil } bootloader, err := partition.FindBootloader() if err != nil { return fmt.Errorf(i18n.G("cannot mark boot successful: %s"), err) } if err := partition.MarkBootSuccessful(bootloader); err != nil { return err } ovld, err := overlord.New() if err != nil { return err } return boot.UpdateRevisions(ovld) }
// KernelOrOsRebootRequired returns whether a reboot is required to swith to the given OS or kernel snap. func KernelOrOsRebootRequired(s *snap.Info) bool { if s.Type != snap.TypeKernel && s.Type != snap.TypeOS { return false } bootloader, err := partition.FindBootloader() if err != nil { logger.Noticef("cannot get boot settings: %s", err) return false } var nextBoot, goodBoot string switch s.Type { case snap.TypeKernel: nextBoot = "snap_try_kernel" goodBoot = "snap_kernel" case snap.TypeOS: nextBoot = "snap_try_core" goodBoot = "snap_core" } m, err := bootloader.GetBootVars(nextBoot, goodBoot) if err != nil { return false } squashfsName := filepath.Base(s.MountFile()) if m[nextBoot] == squashfsName && m[goodBoot] != m[nextBoot] { return true } return false }
// SetNextBoot will schedule the given OS or kernel snap to be used in // the next boot func SetNextBoot(s *snap.Info) error { if release.OnClassic { return nil } if s.Type != snap.TypeOS && s.Type != snap.TypeKernel { return nil } bootloader, err := partition.FindBootloader() if err != nil { return fmt.Errorf("cannot set next boot: %s", err) } var bootvar string switch s.Type { case snap.TypeOS: bootvar = "snap_try_core" case snap.TypeKernel: bootvar = "snap_try_kernel" } blobName := filepath.Base(s.MountFile()) if err := bootloader.SetBootVar(bootvar, blobName); err != nil { return err } if err := bootloader.SetBootVar("snap_mode", "try"); err != nil { return err } return nil }
// UpdateBootRevisions synchronizes the active kernel and OS snap versions // with the versions that actually booted. This is needed because a // system may install "os=v2" but that fails to boot. The bootloader // fallback logic will revert to "os=v1" but on the filesystem snappy // still has the "active" version set to "v2" which is // misleading. This code will check what kernel/os booted and set // those versions active.To do this it creates a Change and kicks // start it directly. func UpdateBootRevisions(st *state.State) error { const errorPrefix = "cannot update revisions after boot changes: " if release.OnClassic { return nil } bootloader, err := partition.FindBootloader() if err != nil { return fmt.Errorf(errorPrefix+"%s", err) } m, err := bootloader.GetBootVars("snap_kernel", "snap_core") if err != nil { return fmt.Errorf(errorPrefix+"%s", err) } var tsAll []*state.TaskSet for _, snapNameAndRevno := range []string{m["snap_kernel"], m["snap_core"]} { name, rev, err := nameAndRevnoFromSnap(snapNameAndRevno) if err != nil { logger.Noticef("cannot parse %q: %s", snapNameAndRevno, err) continue } info, err := CurrentInfo(st, name) if err != nil { logger.Noticef("cannot get info for %q: %s", name, err) continue } if rev != info.SideInfo.Revision { // FIXME: check that there is no task // for this already in progress ts, err := RevertToRevision(st, name, rev, Flags{}) if err != nil { return err } tsAll = append(tsAll, ts) } } if len(tsAll) == 0 { return nil } msg := fmt.Sprintf("Update kernel and core snap revisions") chg := st.NewChange("update-revisions", msg) for _, ts := range tsAll { chg.AddAll(ts) } st.EnsureBefore(0) return nil }
// RemoveKernelAssets removes the unpacked kernel/initrd for the given // kernel snap. func RemoveKernelAssets(s snap.PlaceInfo) error { bootloader, err := partition.FindBootloader() if err != nil { return fmt.Errorf("no not remove kernel assets: %s", err) } // remove the kernel blob blobName := filepath.Base(s.MountFile()) dstDir := filepath.Join(bootloader.Dir(), blobName) if err := os.RemoveAll(dstDir); err != nil { return err } return nil }
func setBootvars(downloadedSnapsInfo map[string]*snap.Info) error { // Set bootvars for kernel/core snaps so the system boots and // does the first-time initialization. There is also no // mounted kernel/core snap, but just the blobs. bootloader, err := partition.FindBootloader() if err != nil { return fmt.Errorf("cannot set kernel/core boot variables: %s", err) } snaps, err := filepath.Glob(filepath.Join(dirs.SnapBlobDir, "*.snap")) if len(snaps) == 0 || err != nil { return fmt.Errorf("internal error: cannot find core/kernel snap") } m := map[string]string{ "snap_mode": "", "snap_try_core": "", "snap_try_kernel": "", } for _, fn := range snaps { bootvar := "" info := downloadedSnapsInfo[fn] switch info.Type { case snap.TypeOS: bootvar = "snap_core" case snap.TypeKernel: bootvar = "snap_kernel" if err := extractKernelAssets(fn, info); err != nil { return err } } if bootvar != "" { name := filepath.Base(fn) m[bootvar] = name } } if err := bootloader.SetBootVars(m); err != nil { return err } return nil }
func (m *DeviceManager) ensureBootOk() error { m.state.Lock() defer m.state.Unlock() if release.OnClassic { return nil } if !m.bootOkRan { bootloader, err := partition.FindBootloader() if err != nil { return fmt.Errorf(i18n.G("cannot mark boot successful: %s"), err) } if err := partition.MarkBootSuccessful(bootloader); err != nil { return err } m.bootOkRan = true } return snapstate.UpdateBootRevisions(m.state) }
// ExtractKernelAssets extracts kernel/initrd/dtb data from the given // kernel snap, if required, to a versioned bootloader directory so // that the bootloader can use it. func ExtractKernelAssets(s *snap.Info, snapf snap.Container) error { if s.Type != snap.TypeKernel { return fmt.Errorf("cannot extract kernel assets from snap type %q", s.Type) } bootloader, err := partition.FindBootloader() if err != nil { return fmt.Errorf("cannot extract kernel assets: %s", err) } if bootloader.Name() == "grub" { return nil } // now do the kernel specific bits blobName := filepath.Base(s.MountFile()) dstDir := filepath.Join(bootloader.Dir(), blobName) if err := os.MkdirAll(dstDir, 0755); err != nil { return err } dir, err := os.Open(dstDir) if err != nil { return err } defer dir.Close() for _, src := range []string{"kernel.img", "initrd.img"} { if err := snapf.Unpack(src, dstDir); err != nil { return err } if err := dir.Sync(); err != nil { return err } } if err := snapf.Unpack("dtbs/*", dstDir); err != nil { return err } return dir.Sync() }
// InUse checks if the given name/revision is used in the // boot environment func InUse(name string, rev snap.Revision) bool { bootloader, err := partition.FindBootloader() if err != nil { logger.Noticef("cannot get boot settings: %s", err) return false } bootVars, err := bootloader.GetBootVars("snap_kernel", "snap_try_kernel", "snap_core", "snap_try_core") if err != nil { logger.Noticef("cannot get boot vars: %s", err) return false } snapFile := filepath.Base(snap.MountFile(name, rev)) for _, bootVar := range bootVars { if bootVar == snapFile { return true } } return false }
// SetNextBoot will schedule the given OS or kernel snap to be used in // the next boot func SetNextBoot(s *snap.Info) error { if release.OnClassic { return nil } if s.Type != snap.TypeOS && s.Type != snap.TypeKernel { return nil } bootloader, err := partition.FindBootloader() if err != nil { return fmt.Errorf("cannot set next boot: %s", err) } var nextBoot, goodBoot string switch s.Type { case snap.TypeOS: nextBoot = "snap_try_core" goodBoot = "snap_core" case snap.TypeKernel: nextBoot = "snap_try_kernel" goodBoot = "snap_kernel" } blobName := filepath.Base(s.MountFile()) // check if we actually need to do anything, i.e. the exact same // kernel/core revision got installed again (e.g. firstboot) m, err := bootloader.GetBootVars(goodBoot) if err != nil { return err } if m[goodBoot] == blobName { return nil } return bootloader.SetBootVars(map[string]string{ nextBoot: blobName, "snap_mode": "try", }) }
// UpdateRevisions synchronizes the active kernel and OS snap versions with // the versions that actually booted. This is needed because a // system may install "os=v2" but that fails to boot. The bootloader // fallback logic will revert to "os=v1" but on the filesystem snappy // still has the "active" version set to "v2" which is // misleading. This code will check what kernel/os booted and set // those versions active. func UpdateRevisions(ovld *overlord.Overlord) error { const errorPrefix = "cannot update revisions after boot changes: " if release.OnClassic { return nil } bootloader, err := partition.FindBootloader() if err != nil { return fmt.Errorf(errorPrefix+"%s", err) } bv := "snap_kernel" kernelSnap, err := bootloader.GetBootVar(bv) if err != nil { return fmt.Errorf(errorPrefix+"%s", err) } bv = "snap_core" osSnap, err := bootloader.GetBootVar(bv) if err != nil { return fmt.Errorf(errorPrefix+"%s", err) } st := ovld.State() st.Lock() installed, err := snapstate.All(st) if err != nil { return fmt.Errorf(errorPrefix+"%s", err) } var tsAll []*state.TaskSet for _, snapNameAndRevno := range []string{kernelSnap, osSnap} { name, rev, err := nameAndRevnoFromSnap(snapNameAndRevno) if err != nil { logger.Noticef("cannot parse %q: %s", snapNameAndRevno, err) continue } for snapName, snapState := range installed { if name == snapName { if rev != snapState.Current { ts, err := snapstate.RevertToRevision(st, name, rev, snapstate.Flags(0)) if err != nil { return err } tsAll = append(tsAll, ts) } } } } st.Unlock() if len(tsAll) == 0 { return nil } st.Lock() msg := fmt.Sprintf("Update kernel and core snap revisions") chg := st.NewChange("update-revisions", msg) for _, ts := range tsAll { chg.AddAll(ts) } st.Unlock() // do it and wait for ready ovld.Loop() timeoutTime := 10 * time.Second st.EnsureBefore(0) select { case <-chg.Ready(): case <-time.After(timeoutTime): return fmt.Errorf("change did not apply after %s", timeoutTime) } st.Lock() status := chg.Status() err = chg.Err() st.Unlock() if status != state.DoneStatus { ovld.Stop() return fmt.Errorf(errorPrefix+"%s", err) } return ovld.Stop() }