internal/pkg: exec prefix verbose output
All checks were successful
Test / Create distribution (push) Successful in 47s
Test / Sandbox (push) Successful in 2m52s
Test / ShareFS (push) Successful in 4m50s
Test / Hpkg (push) Successful in 5m10s
Test / Sandbox (race detector) (push) Successful in 5m18s
Test / Hakurei (push) Successful in 5m25s
Test / Hakurei (race detector) (push) Successful in 7m32s
Test / Flake checks (push) Successful in 1m41s

This proxies program output through msg with a name and fd prefix. This also avoids introducing additional information to the container via process stdout/stderr.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2026-01-12 22:15:01 +09:00
parent 41a8d03dd2
commit 5fe166a4a7

View File

@@ -1,11 +1,14 @@
package pkg
import (
"bufio"
"bytes"
"context"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path"
"slices"
"strconv"
@@ -16,6 +19,7 @@ import (
"hakurei.app/container/check"
"hakurei.app/container/fhs"
"hakurei.app/container/std"
"hakurei.app/message"
)
// AbsWork is the container pathname [CureContext.GetWorkDir] is mounted on.
@@ -57,7 +61,8 @@ const (
// Methods of execArtifact does not modify any struct field or underlying arrays
// referred to by slices.
type execArtifact struct {
// Caller-supplied user-facing reporting name.
// Caller-supplied user-facing reporting name, guaranteed to be nonzero
// during initialisation.
name string
// Caller-supplied inner mount points.
paths []ExecPath
@@ -219,6 +224,23 @@ const (
execWaitDelay = time.Nanosecond
)
// scanVerbose prefixes program output for a verbose [message.Msg].
func scanVerbose(
msg message.Msg,
done chan<- struct{},
prefix string,
r io.Reader,
) {
defer close(done)
s := bufio.NewScanner(r)
for s.Scan() {
msg.Verbose(prefix, s.Text())
}
if err := s.Err(); err != nil && !errors.Is(err, os.ErrClosed) {
msg.Verbose("*"+prefix, err)
}
}
// cure is like Cure but allows optional host net namespace. This is used for
// the [KnownChecksum] variant where networking is allowed.
func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
@@ -250,8 +272,26 @@ func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
z.Hostname = "cure-net"
}
z.Uid, z.Gid = (1<<10)-1, (1<<10)-1
if f.GetMessage().IsVerbose() {
z.Stdout, z.Stderr = os.Stdout, os.Stderr
if msg := f.GetMessage(); msg.IsVerbose() {
var stdout, stderr io.ReadCloser
if stdout, err = z.StdoutPipe(); err != nil {
return
}
if stderr, err = z.StderrPipe(); err != nil {
_ = stdout.Close()
return
}
defer func() {
if err != nil && !errors.As(err, new(*exec.ExitError)) {
_ = stdout.Close()
_ = stderr.Close()
}
}()
stdoutDone, stderrDone := make(chan struct{}), make(chan struct{})
go scanVerbose(msg, stdoutDone, "("+a.name+":1)", stdout)
go scanVerbose(msg, stderrDone, "("+a.name+":2)", stderr)
defer func() { <-stdoutDone; <-stderrDone }()
}
z.Dir, z.Env, z.Path, z.Args = a.dir, a.env, a.path, a.args