| Tim Windelschmidt | 6d33a43 | 2025-02-04 14:34:25 +0100 | [diff] [blame] | 1 | // Copyright The Monogon Project Authors. |
| 2 | // SPDX-License-Identifier: Apache-2.0 |
| 3 | |
| Tim Windelschmidt | 7a1b27d | 2024-02-22 23:54:58 +0100 | [diff] [blame] | 4 | package main |
| 5 | |
| 6 | import ( |
| 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 |
| 21 | const launchModeEnv = "TAKEOVER_LAUNCH_MODE" |
| 22 | |
| 23 | const ( |
| 24 | launchModeTakeover = "" |
| 25 | launchModeDetached = "DETACHED" |
| 26 | launchModeInit = "INIT" |
| 27 | ) |
| 28 | |
| 29 | func 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 | |
| 42 | func 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 |
| 84 | func 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 | |
| 106 | func launchInit() { |
| 107 | bringup.Runnable(takeoverRunnable).Run() |
| 108 | } |