github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/overlord/devicestate/handlers.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  /*
     3   * Copyright (C) 2016-2020 Canonical Ltd
     4   *
     5   * This program is free software: you can redistribute it and/or modify
     6   * it under the terms of the GNU General Public License version 3 as
     7   * published by the Free Software Foundation.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   *
    17   */
    18  
    19  package devicestate
    20  
    21  import (
    22  	"fmt"
    23  	"os/exec"
    24  	"time"
    25  
    26  	"gopkg.in/tomb.v2"
    27  
    28  	"github.com/snapcore/snapd/interfaces"
    29  	"github.com/snapcore/snapd/logger"
    30  	"github.com/snapcore/snapd/overlord/snapstate"
    31  	"github.com/snapcore/snapd/overlord/state"
    32  )
    33  
    34  func (m *DeviceManager) doMarkPreseeded(t *state.Task, _ *tomb.Tomb) error {
    35  	st := t.State()
    36  	st.Lock()
    37  	defer st.Unlock()
    38  
    39  	snaps, err := snapstate.All(st)
    40  	if err != nil {
    41  		return err
    42  	}
    43  
    44  	systemKey, err := interfaces.RecordedSystemKey()
    45  	if err != nil {
    46  		return fmt.Errorf("cannot get recorded system key: %v", err)
    47  	}
    48  
    49  	if m.preseed {
    50  		var preseeded bool
    51  		// the "preseeded" flag on this task is set to allow skipping the logic
    52  		// below in case this handler is retried in preseeding mode due to an
    53  		// EnsureBefore(0) done somewhere else.
    54  		// XXX: we should probably drop the flag from the task now that we have
    55  		// one on the state.
    56  		if err := t.Get("preseeded", &preseeded); err != nil && err != state.ErrNoState {
    57  			return err
    58  		}
    59  		if !preseeded {
    60  			preseeded = true
    61  			t.Set("preseeded", preseeded)
    62  			// unmount all snaps
    63  			// TODO: move to snapstate.UnmountAllSnaps.
    64  			for _, snapSt := range snaps {
    65  				info, err := snapSt.CurrentInfo()
    66  				if err != nil {
    67  					return err
    68  				}
    69  				logger.Debugf("unmounting snap %s at %s", info.InstanceName(), info.MountDir())
    70  				if _, err := exec.Command("umount", "-d", "-l", info.MountDir()).CombinedOutput(); err != nil {
    71  					return err
    72  				}
    73  			}
    74  
    75  			st.Set("preseeded", preseeded)
    76  			st.Set("preseed-system-key", systemKey)
    77  			st.Set("preseed-time", timeNow())
    78  
    79  			// do not mark this task done as this makes it racy against taskrunner tear down (the next task
    80  			// could start). Let this task finish after snapd restart when preseed mode is off.
    81  			st.RequestRestart(state.StopDaemon)
    82  		}
    83  
    84  		return &state.Retry{Reason: "mark-preseeded will be marked done when snapd is executed in normal mode"}
    85  	}
    86  
    87  	// normal snapd run after snapd restart (not in preseed mode anymore)
    88  
    89  	st.Set("seed-restart-system-key", systemKey)
    90  	if err := m.setTimeOnce("seed-restart-time", startTime); err != nil {
    91  		return err
    92  	}
    93  
    94  	return nil
    95  }
    96  
    97  type seededSystem struct {
    98  	// System carries the recovery system label that was used to seed the
    99  	// current system
   100  	System string `json:"system"`
   101  	Model  string `json:"model"`
   102  	// BrandID is the brand account ID
   103  	BrandID string `json:"brand-id"`
   104  	// Revision of the model assertion
   105  	Revision int `json:"revision"`
   106  	// Timestamp of model assertion
   107  	Timestamp time.Time `json:"timestamp"`
   108  	// SeedTime holds the timestamp when the system was seeded
   109  	SeedTime time.Time `json:"seed-time"`
   110  }
   111  
   112  func (m *DeviceManager) recordSeededSystem(st *state.State, whatSeeded *seededSystem) error {
   113  	var seeded []seededSystem
   114  	if err := st.Get("seeded-systems", &seeded); err != nil && err != state.ErrNoState {
   115  		return err
   116  	}
   117  	seeded = append([]seededSystem{*whatSeeded}, seeded...)
   118  	st.Set("seeded-systems", seeded)
   119  	return nil
   120  }
   121  
   122  func (m *DeviceManager) doMarkSeeded(t *state.Task, _ *tomb.Tomb) error {
   123  	st := t.State()
   124  	st.Lock()
   125  	defer st.Unlock()
   126  
   127  	if m.preseed {
   128  		return fmt.Errorf("internal error: mark-seeded task not expected in pre-seeding mode")
   129  	}
   130  
   131  	deviceCtx, err := DeviceCtx(st, t, nil)
   132  	if err != nil {
   133  		return fmt.Errorf("cannot get device context: %v", err)
   134  	}
   135  
   136  	if deviceCtx.HasModeenv() && deviceCtx.RunMode() {
   137  		modeEnv, err := maybeReadModeenv()
   138  		if err != nil {
   139  			return err
   140  		}
   141  		if modeEnv == nil {
   142  			return fmt.Errorf("missing modeenv, cannot proceed")
   143  		}
   144  		// unset recovery_system because that is only needed during install mode
   145  		modeEnv.RecoverySystem = ""
   146  		err = modeEnv.Write()
   147  		if err != nil {
   148  			return err
   149  		}
   150  	}
   151  
   152  	now := time.Now()
   153  	var whatSeeded *seededSystem
   154  	if err := t.Get("seed-system", &whatSeeded); err != nil && err != state.ErrNoState {
   155  		return err
   156  	}
   157  	if whatSeeded != nil && deviceCtx.RunMode() {
   158  		// record what seeded in the state only when in run mode
   159  
   160  		whatSeeded.SeedTime = now
   161  		// TODO:UC20 what about remodels?
   162  		if err := m.recordSeededSystem(st, whatSeeded); err != nil {
   163  			return fmt.Errorf("cannot record the seeded system: %v", err)
   164  		}
   165  	}
   166  	st.Set("seed-time", now)
   167  	st.Set("seeded", true)
   168  	// avoid possibly recording the same system multiple times etc.
   169  	t.SetStatus(state.DoneStatus)
   170  	// make sure we setup a fallback model/consider the next phase
   171  	// (registration) timely
   172  	st.EnsureBefore(0)
   173  	return nil
   174  }