github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/overlord/snapstate/booted.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2015 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  	"fmt"
    24  
    25  	"github.com/snapcore/snapd/boot"
    26  	"github.com/snapcore/snapd/logger"
    27  	"github.com/snapcore/snapd/overlord/state"
    28  	"github.com/snapcore/snapd/release"
    29  	"github.com/snapcore/snapd/snap"
    30  )
    31  
    32  // UpdateBootRevisions synchronizes the active kernel and OS snap versions
    33  // with the versions that actually booted. This is needed because a
    34  // system may install "os=v2" but that fails to boot. The bootloader
    35  // fallback logic will revert to "os=v1" but on the filesystem snappy
    36  // still has the "active" version set to "v2" which is
    37  // misleading. This code will check what kernel/os booted and set
    38  // those versions active.To do this it creates a Change and kicks
    39  // start it directly.
    40  func UpdateBootRevisions(st *state.State) error {
    41  	const errorPrefix = "cannot update revisions after boot changes: "
    42  
    43  	if release.OnClassic {
    44  		return nil
    45  	}
    46  
    47  	// nothing to check if there's no kernel
    48  	ok, err := HasSnapOfType(st, snap.TypeKernel)
    49  	if err != nil {
    50  		return fmt.Errorf(errorPrefix+"%s", err)
    51  	}
    52  	if !ok {
    53  		return nil
    54  	}
    55  
    56  	deviceCtx, err := DeviceCtx(st, nil, nil)
    57  	if err != nil {
    58  		// if we have a kernel, we should have a model
    59  		return err
    60  	}
    61  
    62  	kernel, err := boot.GetCurrentBoot(snap.TypeKernel, deviceCtx)
    63  	if err != nil {
    64  		return fmt.Errorf(errorPrefix+"%s", err)
    65  	}
    66  	base, err := boot.GetCurrentBoot(snap.TypeBase, deviceCtx)
    67  	if err != nil {
    68  		return fmt.Errorf(errorPrefix+"%s", err)
    69  	}
    70  
    71  	var tsAll []*state.TaskSet
    72  	for _, actual := range []snap.PlaceInfo{kernel, base} {
    73  		info, err := CurrentInfo(st, actual.SnapName())
    74  		if err != nil {
    75  			logger.Noticef("cannot get info for %q: %s", actual.SnapName(), err)
    76  			continue
    77  		}
    78  		if actual.SnapRevision() != info.SideInfo.Revision {
    79  			// FIXME: check that there is no task
    80  			//        for this already in progress
    81  			ts, err := RevertToRevision(st, actual.SnapName(), actual.SnapRevision(), Flags{})
    82  			if err != nil {
    83  				return err
    84  			}
    85  			tsAll = append(tsAll, ts)
    86  		}
    87  	}
    88  
    89  	if len(tsAll) == 0 {
    90  		return nil
    91  	}
    92  
    93  	msg := "Update kernel and core snap revisions"
    94  	chg := st.NewChange("update-revisions", msg)
    95  	for _, ts := range tsAll {
    96  		chg.AddAll(ts)
    97  	}
    98  	st.EnsureBefore(0)
    99  
   100  	return nil
   101  }