m/node: minit: fix logging

This makes minit log into the same console devices as the Go core
itself.

Change-Id: I4fedd92d6f86ac224759a67ffd9704ece552b73c
Reviewed-on: https://review.monogon.dev/c/monogon/+/660
Reviewed-by: Mateusz Zalega <mateusz@monogon.tech>
diff --git a/metropolis/node/core/minit/main.c b/metropolis/node/core/minit/main.c
index f2611b4..cee0878 100644
--- a/metropolis/node/core/minit/main.c
+++ b/metropolis/node/core/minit/main.c
@@ -19,6 +19,7 @@
 #include <errno.h>
 #include <linux/reboot.h>
 #include <signal.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -28,24 +29,66 @@
 
 void handle_signal(pid_t child_pid, int signum);
 
+#define NUM_CONSOLES 3
+FILE *consoles[NUM_CONSOLES] = {};
+
+// open_consoles populates the consoles array with FILE pointers to opened
+// character devices that should receive log messages. Some of these pointers
+// are likely to be null, meaning that particular console is not available.
+void open_consoles() {
+    consoles[0] = fopen("/dev/console", "w");
+    consoles[1] = fopen("/dev/tty0", "w");
+    consoles[2] = fopen("/dev/ttyS0", "w");
+
+    // Set all open consoles to be line-buffered.
+    for (int i = 0; i < NUM_CONSOLES; i++) {
+        if (consoles[i] == NULL) {
+            continue;
+        }
+        setvbuf(consoles[i], NULL, _IOLBF, BUFSIZ);
+    }
+
+    // TODO(q3k): disable hardware and software flow control on TTYs. This
+    // shouldn't be necessary on our current platform, but should be ensured
+    // regardless, to make sure we never block writing to any console.
+}
+
+// cprintf emits a format string to all opened consoles.
+void cprintf(const char *fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+
+    for (int i = 0; i < NUM_CONSOLES; i++) {
+        FILE *console = consoles[i];
+        if (console == NULL) {
+            continue;
+        }
+        vfprintf(console, fmt, args);
+    }
+
+    va_end(args);
+}
+
 int main() {
     // Block all signals. We'll unblock them in the child.
     sigset_t all_signals;
     sigfillset(&all_signals);
     sigprocmask(SIG_BLOCK, &all_signals, NULL);
 
+    open_consoles();
+
     // Say hello.
-    fprintf(stderr,
+    cprintf(
         "\n"
         "  Metropolis Cluster Operating System\n"
-        "  Copyright 2020-2021 The Monogon Project Authors\n"
+        "  Copyright 2020-2022 The Monogon Project Authors\n"
         "\n"
     );
 
 
     pid_t pid = fork();
     if (pid < 0) {
-        fprintf(stderr, "fork(): %s\n", strerror(errno));
+        cprintf("fork(): %s\n", strerror(errno));
         return 1;
     }
 
@@ -53,7 +96,7 @@
         // In the child. Unblock all signals.
         sigprocmask(SIG_UNBLOCK, &all_signals, NULL);
         if (setsid() == -1) {
-            fprintf(stderr, "setsid: %s\n", strerror(errno));
+            cprintf("setsid: %s\n", strerror(errno));
             return 1;
         }
 
@@ -63,7 +106,7 @@
             NULL,
         };
         execvp(argv[0], argv);
-        fprintf(stderr, "execvpe(/core) failed: %s\n", strerror(errno));
+        cprintf("execvpe(/core) failed: %s\n", strerror(errno));
         return 1;
     }
 
@@ -111,21 +154,21 @@
         } else {
             // Something unexpected happened. Attempt to handle this gracefully,
             // but complain.
-            fprintf(stderr, "child status not EXITED nor SIGNALED: %d\n", status);
+            cprintf("child status not EXITED nor SIGNALED: %d\n", status);
             exit_status = 1;
         }
     }
 
     // Direct child exited, let's also exit.
     if (exit_status >= 0) {
-        fprintf(stderr, "\n  Metropolis core exited with status: %d\n", exit_status);
+        cprintf("\n  Metropolis core exited with status: %d\n", exit_status);
         sync();
         if (exit_status != 0) {
-            fprintf(stderr, "  Disks synced, rebooting in 30 seconds...\n", exit_status);
+            cprintf("  Disks synced, rebooting in 30 seconds...\n", exit_status);
             sleep(30);
-            fprintf(stderr, "  Rebooting...\n\n", exit_status);
+            cprintf("  Rebooting...\n\n", exit_status);
         } else {
-            fprintf(stderr, "  Disks synced, rebooting...\n\n");
+            cprintf("  Disks synced, rebooting...\n\n");
         }
         reboot(LINUX_REBOOT_CMD_RESTART);
     }