blob: a609a634780c5169bd2a08902b5d2014620a2bb3 [file] [log] [blame]
Tim Windelschmidt7a1b27d2024-02-22 23:54:58 +01001package main
2
3import (
4 "flag"
5 "io"
6 "log"
7 "os"
8 "os/exec"
9 "strings"
10 "time"
11
12 "golang.org/x/sys/unix"
13
14 "source.monogon.dev/osbase/bringup"
15)
16
17// Environment variable which tells the takeover binary to run the correct stage
18const launchModeEnv = "TAKEOVER_LAUNCH_MODE"
19
20const (
21 launchModeTakeover = ""
22 launchModeDetached = "DETACHED"
23 launchModeInit = "INIT"
24)
25
26func main() {
27 switch m := os.Getenv(launchModeEnv); m {
28 case launchModeTakeover:
29 launchTakeover()
30 case launchModeDetached:
31 launchDetached()
32 case launchModeInit:
33 launchInit()
34 default:
35 panic("unknown launch mode: " + m)
36 }
37}
38
39func launchTakeover() {
40 disk := flag.String("disk", "", "disk to install to without /dev/")
41 flag.Parse()
42 if disk == nil || *disk == "" {
43 log.Fatal("missing target disk")
44 }
45
46 nodeParamsRaw, err := io.ReadAll(os.Stdin)
47 if err != nil {
48 log.Fatal(err)
49 }
50
51 // try removing /dev/ just to be safe
52 diskName := strings.ReplaceAll(*disk, "/dev/", "")
53 warns, err := setupTakeover(nodeParamsRaw, diskName)
54 if err != nil {
55 log.Fatal(err)
56 }
57
58 if len(warns) != 0 {
59 for _, s := range warns {
60 os.Stdout.WriteString(s)
61 }
62 }
63
64 // Close stdout, we're done responding
65 os.Stdout.Close()
66
67 // Start second stage which waits for 5 seconds while performing
68 // final cleanup.
69 detachedCmd := exec.Command("/proc/self/exe")
70 detachedCmd.Env = []string{launchModeEnv + "=" + launchModeDetached}
71 if err := detachedCmd.Start(); err != nil {
72 log.Fatalf("failed to launch final stage: %v", err)
73 }
74 // Release the second stage so that the first stage can cleanly terminate.
75 if err := detachedCmd.Process.Release(); err != nil {
76 log.Fatalf("error releasing final stage process: %v", err)
77 }
78}
79
80// launchDetached executes the second stage
81func launchDetached() {
82 // Wait 5 seconds for data to be sent, connections to be closed and
83 // syncs to be executed
84 time.Sleep(5 * time.Second)
85 // Perform kexec, this will not return unless it fails
86 err := unix.Reboot(unix.LINUX_REBOOT_CMD_KEXEC)
87 msg := "takeover: reboot succeeded, but we're still runing??"
88 if err != nil {
89 msg = err.Error()
90 }
91 // We have no standard output/error anymore, if this fails it's
92 // just borked. Attempt to dump the error into kmesg for manual
93 // debugging.
94 kmsg, err := os.OpenFile("/dev/kmsg", os.O_WRONLY, 0)
95 if err != nil {
96 os.Exit(2)
97 }
98 kmsg.WriteString(msg)
99 kmsg.Close()
100 os.Exit(1)
101}
102
103func launchInit() {
104 bringup.Runnable(takeoverRunnable).Run()
105}