blob: a609a634780c5169bd2a08902b5d2014620a2bb3 [file] [log] [blame]
package main
import (
"flag"
"io"
"log"
"os"
"os/exec"
"strings"
"time"
"golang.org/x/sys/unix"
"source.monogon.dev/osbase/bringup"
)
// Environment variable which tells the takeover binary to run the correct stage
const launchModeEnv = "TAKEOVER_LAUNCH_MODE"
const (
launchModeTakeover = ""
launchModeDetached = "DETACHED"
launchModeInit = "INIT"
)
func main() {
switch m := os.Getenv(launchModeEnv); m {
case launchModeTakeover:
launchTakeover()
case launchModeDetached:
launchDetached()
case launchModeInit:
launchInit()
default:
panic("unknown launch mode: " + m)
}
}
func launchTakeover() {
disk := flag.String("disk", "", "disk to install to without /dev/")
flag.Parse()
if disk == nil || *disk == "" {
log.Fatal("missing target disk")
}
nodeParamsRaw, err := io.ReadAll(os.Stdin)
if err != nil {
log.Fatal(err)
}
// try removing /dev/ just to be safe
diskName := strings.ReplaceAll(*disk, "/dev/", "")
warns, err := setupTakeover(nodeParamsRaw, diskName)
if err != nil {
log.Fatal(err)
}
if len(warns) != 0 {
for _, s := range warns {
os.Stdout.WriteString(s)
}
}
// Close stdout, we're done responding
os.Stdout.Close()
// Start second stage which waits for 5 seconds while performing
// final cleanup.
detachedCmd := exec.Command("/proc/self/exe")
detachedCmd.Env = []string{launchModeEnv + "=" + launchModeDetached}
if err := detachedCmd.Start(); err != nil {
log.Fatalf("failed to launch final stage: %v", err)
}
// Release the second stage so that the first stage can cleanly terminate.
if err := detachedCmd.Process.Release(); err != nil {
log.Fatalf("error releasing final stage process: %v", err)
}
}
// launchDetached executes the second stage
func launchDetached() {
// Wait 5 seconds for data to be sent, connections to be closed and
// syncs to be executed
time.Sleep(5 * time.Second)
// Perform kexec, this will not return unless it fails
err := unix.Reboot(unix.LINUX_REBOOT_CMD_KEXEC)
msg := "takeover: reboot succeeded, but we're still runing??"
if err != nil {
msg = err.Error()
}
// We have no standard output/error anymore, if this fails it's
// just borked. Attempt to dump the error into kmesg for manual
// debugging.
kmsg, err := os.OpenFile("/dev/kmsg", os.O_WRONLY, 0)
if err != nil {
os.Exit(2)
}
kmsg.WriteString(msg)
kmsg.Close()
os.Exit(1)
}
func launchInit() {
bringup.Runnable(takeoverRunnable).Run()
}