Skip to content

Commit 04d209c

Browse files
committed
serial: allow the device to be set to null
It's useful to be able to log the serial console output to a ring buffer (or even ASL) without necessarily having a real host console attached to it (i.e. no TTY or `stdio`). The code requires an open file descriptor so add a new type `null` which opens `/dev/null`. Signed-off-by: David Scott <dave@recoil.org>
1 parent 02dfe99 commit 04d209c

File tree

3 files changed

+61
-13
lines changed

3 files changed

+61
-13
lines changed

go/hyperkit.go

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"os"
3232
"os/exec"
3333
"os/user"
34+
"path"
3435
"path/filepath"
3536
"strconv"
3637
"strings"
@@ -237,12 +238,19 @@ func (h *HyperKit) checkLegacyConsole() error {
237238
}
238239

239240
func (h *HyperKit) checkSerials() error {
240-
for _, serial := range h.Serials {
241+
stdioConsole := -1
242+
for i, serial := range h.Serials {
241243
if serial.LogToRingBuffer && h.StateDir == "" {
242244
return fmt.Errorf("If VM is to log to a ring buffer, StateDir must be specified")
243245
}
244-
if serial.InteractiveConsole == StdioInteractiveConsole && !isTerminal(os.Stdout) {
245-
return fmt.Errorf("If StdioInteractiveConsole is set, stdio must be a TTY")
246+
if serial.InteractiveConsole == StdioInteractiveConsole {
247+
if isTerminal(os.Stdout) {
248+
return fmt.Errorf("If StdioInteractiveConsole is set, stdio must be a TTY")
249+
}
250+
if stdioConsole != -1 {
251+
return fmt.Errorf("Only one serial port can be nominated as the stdio interactive console")
252+
}
253+
stdioConsole = i
246254
}
247255
if serial.InteractiveConsole == TTYInteractiveConsole && h.StateDir == "" {
248256
return fmt.Errorf("If TTYInteractiveConsole is set, StateDir must be specified ")
@@ -442,10 +450,12 @@ func (h *HyperKit) serialArgs() []string {
442450
for i, serial := range h.Serials {
443451
cfg := fmt.Sprintf("com%d", i+1)
444452
switch serial.InteractiveConsole {
453+
case NoInteractiveConsole:
454+
cfg += ",null"
445455
case StdioInteractiveConsole:
446456
cfg += fmt.Sprintf(",stdio")
447457
case TTYInteractiveConsole:
448-
cfg += fmt.Sprintf(",autopty=%s/tty", h.StateDir)
458+
cfg += fmt.Sprintf(",autopty=%s/tty%d", h.StateDir, i+1)
449459
}
450460
if serial.LogToASL {
451461
cfg += fmt.Sprintf(",asl")
@@ -543,8 +553,8 @@ func (h *HyperKit) buildArgs(cmdline string) {
543553
}
544554

545555
// openTTY opens the tty files for reading, and returns it.
546-
func (h *HyperKit) openTTY() *os.File {
547-
path := fmt.Sprintf("%s/tty", h.StateDir)
556+
func (h *HyperKit) openTTY(filename string) *os.File {
557+
path := path.Join(h.StateDir, filename)
548558
for {
549559
if res, err := os.OpenFile(path, os.O_RDONLY, 0); err != nil {
550560
log.Infof("hyperkit: openTTY: %v, retrying", err)
@@ -558,6 +568,21 @@ func (h *HyperKit) openTTY() *os.File {
558568
}
559569
}
560570

571+
func (h *HyperKit) findStdioTTY() string {
572+
if h.Serials != nil {
573+
for i, serial := range h.Serials {
574+
if serial.InteractiveConsole == StdioInteractiveConsole {
575+
return fmt.Sprintf("tty%d", i)
576+
}
577+
}
578+
return ""
579+
}
580+
if h.Console == ConsoleStdio {
581+
return "tty"
582+
}
583+
return ""
584+
}
585+
561586
// execute forges the command to run hyperkit, runs and returns it.
562587
// It also plumbs stdin/stdout/stderr.
563588
func (h *HyperKit) execute() (*exec.Cmd, error) {
@@ -580,19 +605,21 @@ func (h *HyperKit) execute() (*exec.Cmd, error) {
580605
//
581606
// If a logger is specified, use it for stdout/stderr
582607
// logging. Otherwise use the default /dev/null.
583-
if h.Console == ConsoleStdio {
608+
filename := h.findStdioTTY()
609+
if filename != "" {
584610
if isTerminal(os.Stdout) {
585611
cmd.Stdin = os.Stdin
586612
cmd.Stdout = os.Stdout
587613
cmd.Stderr = os.Stderr
588614
} else {
589615
go func() {
590-
tty := h.openTTY()
616+
tty := h.openTTY(filename)
591617
defer tty.Close()
592618
io.Copy(os.Stdout, tty)
593619
}()
594620
}
595-
} else if log != nil {
621+
}
622+
if log != nil {
596623
log.Debugf("hyperkit: Redirecting stdout/stderr to logger")
597624
stdout, err := cmd.StdoutPipe()
598625
if err != nil {

go/hyperkit_test.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
)
99

1010
func TestLegacyConsole(t *testing.T) {
11-
h, err := New("hyperkit", "", "state-dir")
11+
h, err := New("sh", "", "state-dir")
1212
require.Nil(t, err)
1313

1414
h.Console = ConsoleFile
@@ -17,7 +17,7 @@ func TestLegacyConsole(t *testing.T) {
1717
}
1818

1919
func TestNewSerial(t *testing.T) {
20-
h, err := New("hyperkit", "", "state-dir")
20+
h, err := New("sh", "", "state-dir")
2121
require.Nil(t, err)
2222

2323
h.Serials = []Serial{
@@ -27,5 +27,18 @@ func TestNewSerial(t *testing.T) {
2727
},
2828
}
2929
h.buildArgs("")
30-
assert.EqualValues(t, []string{"-A", "-u", "-F", "state-dir/hyperkit.pid", "-c", "1", "-m", "1024M", "-s", "0:0,hostbridge", "-s", "31,lpc", "-s", "1,virtio-rnd", "-l", "com1,autopty=state-dir/tty,log=state-dir/console-ring", "-f", "kexec,,,earlyprintk=serial "}, h.Arguments)
30+
assert.EqualValues(t, []string{"-A", "-u", "-F", "state-dir/hyperkit.pid", "-c", "1", "-m", "1024M", "-s", "0:0,hostbridge", "-s", "31,lpc", "-s", "1,virtio-rnd", "-l", "com1,autopty=state-dir/tty1,log=state-dir/console-ring", "-f", "kexec,,,earlyprintk=serial "}, h.Arguments)
31+
}
32+
33+
func TestNullSerial(t *testing.T) {
34+
h, err := New("sh", "", "state-dir")
35+
require.Nil(t, err)
36+
37+
h.Serials = []Serial{
38+
{
39+
LogToRingBuffer: true,
40+
},
41+
}
42+
h.buildArgs("")
43+
assert.EqualValues(t, []string{"-A", "-u", "-F", "state-dir/hyperkit.pid", "-c", "1", "-m", "1024M", "-s", "0:0,hostbridge", "-s", "31,lpc", "-s", "1,virtio-rnd", "-l", "com1,null,log=state-dir/console-ring", "-f", "kexec,,,earlyprintk=serial "}, h.Arguments)
3144
}

src/lib/uart_emul.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,15 @@ uart_set_backend(struct uart_softc *sc, const char *backend, const char *devname
722722
if (next)
723723
next[0] = '\0';
724724

725-
if (strcmp("stdio", backend) == 0 && !uart_stdio) {
725+
if (strcmp("null", backend) == 0) {
726+
sc->tty.fd = open("/dev/null", O_RDWR | O_NONBLOCK);
727+
if (sc->tty.fd == -1) {
728+
fprintf(stderr, "error opening /dev/null\n");
729+
goto err;
730+
}
731+
sc->tty.opened = true;
732+
retval = 0;
733+
} else if (strcmp("stdio", backend) == 0 && !uart_stdio) {
726734
sc->tty.fd = STDIN_FILENO;
727735
sc->tty.opened = true;
728736
uart_stdio = true;

0 commit comments

Comments
 (0)