metropolis: prevent printk console pollution

This implements two separate approaches to limit printk pollution of the
new tconsole:

 1. Sets the minimum printk level to EMERG. Everything lower than this
    level will not get blasted to tty0.
 2. Jut in case something does a spurious EMERG printk (or something
    just writes to tty0), we redraw the console. This makes it
    self-healing.

Change-Id: I69370ebf6c3cb3cacc8b6ea1ad3703e758bbf50c
Reviewed-on: https://review.monogon.dev/c/monogon/+/3398
Tested-by: Jenkins CI
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
diff --git a/metropolis/node/core/main.go b/metropolis/node/core/main.go
index babee28..eba6773 100644
--- a/metropolis/node/core/main.go
+++ b/metropolis/node/core/main.go
@@ -40,6 +40,7 @@
 	"source.monogon.dev/osbase/logtree"
 	"source.monogon.dev/osbase/net/dns"
 	"source.monogon.dev/osbase/supervisor"
+	"source.monogon.dev/osbase/sysctl"
 	"source.monogon.dev/osbase/tpm"
 	"source.monogon.dev/version"
 )
@@ -224,6 +225,16 @@
 			}
 		}
 
+		// Now that we have consoles, set console logging level to 1 (KERNEL_EMERG,
+		// minimum possible). This prevents the TUI console from being polluted by
+		// random printks.
+		opts := sysctl.Options{
+			"kernel.printk": "1",
+		}
+		if err := opts.Apply(); err != nil {
+			logger.Errorf("Failed to configure printk logging: %v", err)
+		}
+
 		// Start cluster manager. This kicks off cluster membership machinery,
 		// which will either start a new cluster, enroll into one or join one.
 		m := cluster.NewManager(root, networkSvc, rs, updateSvc, nodeParams, haveTPM)
diff --git a/metropolis/node/core/tconsole/tconsole.go b/metropolis/node/core/tconsole/tconsole.go
index bbdca82..bd4bd08 100644
--- a/metropolis/node/core/tconsole/tconsole.go
+++ b/metropolis/node/core/tconsole/tconsole.go
@@ -159,8 +159,13 @@
 
 	// Ticker used to maintain redraws at minimum 10Hz, to eg. update the clock in
 	// the status bar.
-	ticker := time.NewTicker(time.Second / 10)
-	defer ticker.Stop()
+	tickerDraw := time.NewTicker(time.Second / 10)
+	defer tickerDraw.Stop()
+
+	// Ticker used to fully resync the screen every 10 seconds, in case something
+	// scribbled over the TTY.
+	tickerSync := time.NewTicker(time.Second * 10)
+	defer tickerSync.Stop()
 
 	for {
 		// Draw active page.
@@ -174,7 +179,9 @@
 		c.screen.Show()
 
 		select {
-		case <-ticker.C:
+		case <-tickerDraw.C:
+		case <-tickerSync.C:
+			c.screen.Sync()
 		case <-ctx.Done():
 			return ctx.Err()
 		case ev := <-evC: