Files
cabletest/fatal.go
T

113 lines
2.3 KiB
Go
Raw Normal View History

package main
import (
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"time"
"golang.org/x/sys/unix"
)
const fatalRebootDelay = 5 * time.Minute
var fatalCh = make(chan any, 1)
// Deferred in every goroutine that can panic: as PID 1 the message is shown by
// the run loop instead of killing init; anywhere else the panic keeps its stack.
func holdPanic() {
p := recover()
if p == nil {
return
}
if os.Getpid() != 1 {
panic(p)
}
select {
case fatalCh <- p:
default:
}
}
// Exiting would take init with it and the kernel's panic screen would cover
// the cause, so it is drawn and held instead, with a reboot to retry.
func fatal(cause error) {
if os.Getpid() != 1 {
panic(cause)
}
if err := drawFatal(cause); err != nil {
panic(fmt.Sprintf("%v; drawing it: %v", cause, err))
}
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
select {
case <-sig:
case <-time.After(fatalRebootDelay):
}
if err := unix.Reboot(unix.LINUX_REBOOT_CMD_RESTART); err != nil {
panic(err)
}
}
func drawFatal(cause error) error {
fb, err := openFramebuffer()
if err != nil {
return err
}
title, err := loadFace(true, 40)
if err != nil {
return err
}
body, err := loadFace(false, 34)
if err != nil {
return err
}
fb.fill(uiBg)
noteY := fb.h - spaceRow - body.lineH
y := spaceRow
title.draw(fb, spaceRow, y-title.capTop, "FATAL", uiRed)
y += title.lineH + spaceRow
for _, line := range wrapWords(cause.Error(), (fb.w-2*spaceRow)/body.cellW) {
if y+body.lineH > noteY-spaceRow {
body.draw(fb, spaceRow, y-body.capTop, "...", uiFg)
break
}
body.draw(fb, spaceRow, y-body.capTop, line, uiFg)
y += body.lineH + spaceTight
}
note := fmt.Sprintf("rebooting in %d minutes", int(fatalRebootDelay.Minutes()))
body.draw(fb, spaceRow, noteY-body.capTop, note, uiDim)
return fb.flush()
}
func wrapWords(s string, cols int) []string {
var lines []string
line := ""
for _, w := range strings.Fields(s) {
for len(w) > cols {
if line != "" {
lines = append(lines, line)
line = ""
}
lines = append(lines, w[:cols])
w = w[cols:]
}
switch {
case line == "":
line = w
case len(line)+1+len(w) <= cols:
line += " " + w
default:
lines = append(lines, line)
line = w
}
}
if line != "" {
lines = append(lines, line)
}
return lines
}