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