reboot: use kexec where possible
This doesn’t work on the Raspberry Pi yet (due to missing kexec_file_load), but I tested it in qemu.
This commit is contained in:
parent
91da7026f8
commit
4f0e427025
9
reboot.go
Normal file
9
reboot.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// +build !amd64
|
||||||
|
|
||||||
|
package gokrazy
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
func reboot() error {
|
||||||
|
return unix.Reboot(unix.LINUX_REBOOT_CMD_RESTART)
|
||||||
|
}
|
65
reboot_amd64.go
Normal file
65
reboot_amd64.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package gokrazy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: get these constants into sys/unix
|
||||||
|
const (
|
||||||
|
KEXEC_ARCH_DEFAULT = (0 << 16)
|
||||||
|
KEXEC_FILE_NO_INITRAMFS = 0x00000004
|
||||||
|
)
|
||||||
|
|
||||||
|
func kexecReboot() error {
|
||||||
|
tmpdir, err := ioutil.TempDir("", "kexec")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := syscall.Mount(mustFindRootDevice()+"1", tmpdir, "vfat", 0, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel, err := os.Open(filepath.Join(tmpdir, "vmlinuz"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer kernel.Close()
|
||||||
|
cmdline, err := ioutil.ReadFile("/proc/cmdline")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rep := rootRe.ReplaceAllLiteral(cmdline, []byte("root="+mustFindRootDevice()+inactiveRootPartition))
|
||||||
|
// NUL-terminate cmdline
|
||||||
|
cmdlinebuf := make([]byte, len(rep)+1)
|
||||||
|
copy(cmdlinebuf, rep)
|
||||||
|
_, _, errno := unix.Syscall6(
|
||||||
|
unix.SYS_KEXEC_FILE_LOAD,
|
||||||
|
uintptr(kernel.Fd()), // kernel_fd
|
||||||
|
0, // initrd_fd
|
||||||
|
uintptr(len(cmdline)+1), // cmdline_len
|
||||||
|
uintptr(unsafe.Pointer(&cmdlinebuf[0])), // cmdline
|
||||||
|
KEXEC_ARCH_DEFAULT|KEXEC_FILE_NO_INITRAMFS, // flags
|
||||||
|
0)
|
||||||
|
if errno != 0 {
|
||||||
|
// errno is syscall.ENOSYS on kernels without CONFIG_KEXEC_FILE_LOAD=y
|
||||||
|
return errno
|
||||||
|
}
|
||||||
|
|
||||||
|
return unix.Reboot(unix.LINUX_REBOOT_CMD_KEXEC)
|
||||||
|
}
|
||||||
|
|
||||||
|
func reboot() error {
|
||||||
|
if err := kexecReboot(); err != nil {
|
||||||
|
log.Printf("kexec reboot failed: %v", err)
|
||||||
|
return unix.Reboot(unix.LINUX_REBOOT_CMD_RESTART)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -21,6 +21,8 @@ import (
|
|||||||
var (
|
var (
|
||||||
rootRe = regexp.MustCompile(`root=/dev/(?:mmcblk0p|sda)([2-3])`)
|
rootRe = regexp.MustCompile(`root=/dev/(?:mmcblk0p|sda)([2-3])`)
|
||||||
rootDeviceRe = regexp.MustCompile(`root=(/dev/(?:mmcblk0p|sda))`)
|
rootDeviceRe = regexp.MustCompile(`root=(/dev/(?:mmcblk0p|sda))`)
|
||||||
|
|
||||||
|
inactiveRootPartition string
|
||||||
)
|
)
|
||||||
|
|
||||||
// mustFindRootDevice returns the device from which gokrazy was booted. It is
|
// mustFindRootDevice returns the device from which gokrazy was booted. It is
|
||||||
@ -136,7 +138,6 @@ func initUpdate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rootPartition := matches[1]
|
rootPartition := matches[1]
|
||||||
var inactiveRootPartition string
|
|
||||||
switch rootPartition {
|
switch rootPartition {
|
||||||
case "2":
|
case "2":
|
||||||
inactiveRootPartition = "3"
|
inactiveRootPartition = "3"
|
||||||
@ -176,7 +177,7 @@ func initUpdate() error {
|
|||||||
log.Printf("unmounting /perm failed: %v", err)
|
log.Printf("unmounting /perm failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := unix.Reboot(unix.LINUX_REBOOT_CMD_RESTART); err != nil {
|
if err := reboot(); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user