m/installer: log to tty0 and ttyS0

Without this we only log to whatever the default system console is.

Change-Id: I64b43f8617f3b8752332209b511ea470848c4481
Reviewed-on: https://review.monogon.dev/c/monogon/+/1381
Tested-by: Jenkins CI
Reviewed-by: Leopold Schabel <leo@monogon.tech>
diff --git a/metropolis/installer/log.go b/metropolis/installer/log.go
new file mode 100644
index 0000000..cb7dee6
--- /dev/null
+++ b/metropolis/installer/log.go
@@ -0,0 +1,42 @@
+package main
+
+import (
+	"fmt"
+	"os"
+)
+
+var logC = make(chan string)
+
+// logPiper pipes log entries submitted via logf and panicf into whatever
+// consoles are available to the system.
+func logPiper() {
+	var consoles []*os.File
+	for _, p := range []string{"/dev/tty0", "/dev/ttyS0"} {
+		f, err := os.OpenFile(p, os.O_WRONLY, 0)
+		if err != nil {
+			continue
+		}
+		consoles = append(consoles, f)
+	}
+
+	for {
+		s := <-logC
+		for _, c := range consoles {
+			fmt.Fprintf(c, "%s\n", s)
+		}
+	}
+}
+
+// logf logs some format/args into the active consoles.
+func logf(format string, args ...any) {
+	s := fmt.Sprintf(format, args...)
+	logC <- s
+}
+
+// panicf aborts the installation process with a given format/args.
+func panicf(format string, args ...any) {
+	s := fmt.Sprintf(format, args...)
+	// We don't need to print `s` here, as it's gonna get printed by the recovery
+	// code in main.
+	panic(s)
+}