m/n/core: verbose logging to tty0 and ttyS0

https://review.monogon.dev/517 broke console logging in Nodes, this
re-enables it by explicitly logging to all available consoles instead of
just using whatever we get as stderr.

Change-Id: I3ffde421f1ac07492a1bc3293c31f934f602aefb
Reviewed-on: https://review.monogon.dev/c/monogon/+/523
Reviewed-by: Mateusz Zalega <mateusz@monogon.tech>
diff --git a/metropolis/node/core/main.go b/metropolis/node/core/main.go
index fef5f81..f14b589 100644
--- a/metropolis/node/core/main.go
+++ b/metropolis/node/core/main.go
@@ -19,6 +19,7 @@
 import (
 	"context"
 	"fmt"
+	"io"
 	"net"
 	"os"
 	"runtime/debug"
@@ -59,28 +60,37 @@
 		}
 	}()
 
-	// Set up logger for Metropolis. Currently logs everything to stderr.
-	lt := logtree.New()
-	reader, err := lt.Read("", logtree.WithChildren(), logtree.WithStream())
-	if err != nil {
-		panic(fmt.Errorf("could not set up root log reader: %v", err))
+	// Set up basic mounts (like /dev, /sys...).
+	if err := setupMounts(); err != nil {
+		panic(fmt.Errorf("could not set up basic mounts: %w", err))
 	}
-	go func() {
-		for {
-			p := <-reader.Stream
-			fmt.Fprintf(os.Stderr, "%s\n", p.String())
+
+	// Set up logger for Metropolis. Currently logs everything to /dev/tty0 and
+	// /dev/ttyS0.
+	lt := logtree.New()
+	for _, p := range []string{
+		"/dev/tty0", "/dev/ttyS0",
+	} {
+		f, err := os.OpenFile(p, os.O_WRONLY, 0)
+		if err != nil {
+			continue
 		}
-	}()
+		reader, err := lt.Read("", logtree.WithChildren(), logtree.WithStream())
+		if err != nil {
+			panic(fmt.Errorf("could not set up root log reader: %v", err))
+		}
+		go func(path string, f io.Writer) {
+			fmt.Fprintf(f, "\nMetropolis: this is %s. Verbose node logs follow.\n\n", path)
+			for {
+				p := <-reader.Stream
+				fmt.Fprintf(f, "%s\n", p.String())
+			}
+		}(p, f)
+	}
 
 	// Initial logger. Used until we get to a supervisor.
 	logger := lt.MustLeveledFor("init")
 
-	// Set up basic mounts
-	err = setupMounts(logger)
-	if err != nil {
-		panic(fmt.Errorf("could not set up basic mounts: %w", err))
-	}
-
 	// Linux kernel default is 4096 which is far too low. Raise it to 1M which
 	// is what gVisor suggests.
 	if err := unix.Setrlimit(unix.RLIMIT_NOFILE, &unix.Rlimit{Cur: 1048576, Max: 1048576}); err != nil {
diff --git a/metropolis/node/core/mounts.go b/metropolis/node/core/mounts.go
index a54331d..797d892 100644
--- a/metropolis/node/core/mounts.go
+++ b/metropolis/node/core/mounts.go
@@ -22,14 +22,12 @@
 	"strings"
 
 	"golang.org/x/sys/unix"
-
-	"source.monogon.dev/metropolis/pkg/logtree"
 )
 
 // setupMounts sets up basic mounts like sysfs, procfs, devtmpfs and cgroups.
 // This should be called early during init as a lot of processes depend on this
 // being available.
-func setupMounts(log logtree.LeveledLogger) error {
+func setupMounts() error {
 	// Set up target filesystems.
 	for _, el := range []struct {
 		dir   string