github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/overlord/hookstate/ctlcmd/reboot.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2021 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 ctlcmd 21 22 import ( 23 "fmt" 24 25 "github.com/snapcore/snapd/i18n" 26 "github.com/snapcore/snapd/overlord/devicestate" 27 ) 28 29 var ( 30 shortRebootHelp = i18n.G("Control the reboot behavior of the system") 31 longRebootHelp = i18n.G(` 32 The reboot command can used from allowed hooks to control the reboot behavior of the system. 33 34 Currently it can only be invoked from gadget install-device during UC20 install mode. After invoking it from install-device with --halt or --poweroff the device will not reboot into run mode after finishing install mode but will instead either halt or power off. From install-device the effect is therefore not immediate but delayed until the end of installation itself. 35 `) 36 ) 37 38 func init() { 39 addCommand("reboot", shortRebootHelp, longRebootHelp, func() command { return &rebootCommand{} }) 40 } 41 42 type rebootCommand struct { 43 baseCommand 44 45 Halt bool `long:"halt"` 46 Poweroff bool `long:"poweroff"` 47 } 48 49 func (c *rebootCommand) Execute([]string) error { 50 ctx := c.context() 51 if ctx == nil { 52 return fmt.Errorf(i18n.G("cannot use reboot command without a context")) 53 } 54 if ctx.HookName() != "install-device" { 55 return fmt.Errorf("cannot use reboot command outside of gadget install-device hook") 56 } 57 task, ok := ctx.Task() 58 if !ok { 59 return fmt.Errorf("internal error: inside gadget install-device hook but no task") 60 } 61 if !c.Halt && !c.Poweroff { 62 return fmt.Errorf("either --halt or --poweroff must be specified") 63 } 64 if c.Halt && c.Poweroff { 65 return fmt.Errorf("cannot specify both --halt and --poweroff") 66 } 67 68 ctx.Lock() 69 defer ctx.Unlock() 70 st := ctx.State() 71 72 var restartTaskID string 73 err := task.Get("restart-task", &restartTaskID) 74 if err != nil { 75 return fmt.Errorf("internal error: cannot get restart-task following install-device hook: %v", err) 76 } 77 restartTask := st.Task(restartTaskID) 78 if restartTask == nil { 79 // same error as TaskSnapSetup 80 return fmt.Errorf("internal error: tasks are being pruned") 81 } 82 83 var op string 84 if c.Halt { 85 op = devicestate.RebootHaltOp 86 } else if c.Poweroff { 87 op = devicestate.RebootPoweroffOp 88 } 89 90 restartTask.Set("reboot", devicestate.RebootOptions{ 91 Op: op, 92 }) 93 94 return nil 95 }