Add init debugging support

This adds Delve into the initramfs and a conditional hook which attaches Delve to our init
after the network is up. This allows for breakpoint-debugging the init itself, at least after the
very early node bringup.

Test Plan:
`bazel run -c dbg //:launch`, then use IDEA's Go Remote target to connect to localhost:2345
and set a breakpoint.

Bug: T786

X-Origin-Diff: phab/D581
GitOrigin-RevId: f6b32e7b7f4d36c8492df3e11ee97588817dbd8e
diff --git a/core/cmd/init/BUILD.bazel b/core/cmd/init/BUILD.bazel
index 1e5db46..af37ea6 100644
--- a/core/cmd/init/BUILD.bazel
+++ b/core/cmd/init/BUILD.bazel
@@ -2,13 +2,18 @@
 
 go_library(
     name = "go_default_library",
+    # keep
     srcs = [
         "main.go",
         "switchroot.go",
-    ],
+    ] + select({
+        "//core:debug_build": ["debug_enabled.go"],
+        "//conditions:default": ["debug_disabled.go"],
+    }),
     importpath = "git.monogon.dev/source/nexantic.git/core/cmd/init",
     visibility = ["//visibility:private"],
     deps = [
+        "//core/internal/common:go_default_library",  # keep
         "//core/internal/common/supervisor:go_default_library",
         "//core/internal/network:go_default_library",
         "//core/internal/node:go_default_library",
diff --git a/core/cmd/init/debug_disabled.go b/core/cmd/init/debug_disabled.go
new file mode 100644
index 0000000..06bcad0
--- /dev/null
+++ b/core/cmd/init/debug_disabled.go
@@ -0,0 +1,23 @@
+// Copyright 2020 The Monogon Project Authors.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import "git.monogon.dev/source/nexantic.git/core/internal/network"
+
+// initializeDebugger does nothing in a non-debug build
+func initializeDebugger(*network.Service) {
+}
diff --git a/core/cmd/init/debug_enabled.go b/core/cmd/init/debug_enabled.go
new file mode 100644
index 0000000..1c2af00
--- /dev/null
+++ b/core/cmd/init/debug_enabled.go
@@ -0,0 +1,42 @@
+// Copyright 2020 The Monogon Project Authors.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"context"
+	"fmt"
+	"os/exec"
+
+	"git.monogon.dev/source/nexantic.git/core/internal/common"
+	"git.monogon.dev/source/nexantic.git/core/internal/network"
+)
+
+// initializeDebugger attaches Delve to ourselves and exposes it on common.DebuggerPort
+// This is coupled to compilation_mode=dbg because otherwise Delve doesn't have the necessary DWARF debug info
+func initializeDebugger(networkSvc *network.Service) {
+	go func() {
+		// This is intentionally delayed until network becomes available since Delve for some reason connects to itself
+		// and in early-boot no network interface is available to do that through. Also external access isn't possible
+		// early on anyways.
+		networkSvc.GetIP(context.Background(), true)
+		dlvCmd := exec.Command("/dlv", "--headless=true", fmt.Sprintf("--listen=:%v", common.DebuggerPort),
+			"--accept-multiclient", "--only-same-user=false", "attach", "--continue", "1", "/init")
+		if err := dlvCmd.Start(); err != nil {
+			panic(err)
+		}
+	}()
+}
diff --git a/core/cmd/init/main.go b/core/cmd/init/main.go
index 4fc949d..f5b09f8 100644
--- a/core/cmd/init/main.go
+++ b/core/cmd/init/main.go
@@ -86,6 +86,9 @@
 		panic(err)
 	}
 
+	// This function initializes a headless Delve if this is a debug build or does nothing if it's not
+	initializeDebugger(networkSvc)
+
 	supervisor.New(context.Background(), logger, func(ctx context.Context) error {
 		if err := supervisor.Run(ctx, "network", networkSvc.Run); err != nil {
 			return err