aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/pkg/exec.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-01-12 22:15:01 +0900
committerOphestra <cat@gensokyo.uk>2026-01-12 22:15:01 +0900
commit5fe166a4a74339440647b09d416c5d0784a6c3ee (patch)
treedca1ff4f61015e08c41c228bf56c28517d70d655 /internal/pkg/exec.go
parent41a8d03dd2ff1675e9ab5f0c0134e2b4132e28bf (diff)
internal/pkg: exec prefix verbose output
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>
Diffstat (limited to 'internal/pkg/exec.go')
-rw-r--r--internal/pkg/exec.go46
1 files changed, 43 insertions, 3 deletions
diff --git a/internal/pkg/exec.go b/internal/pkg/exec.go
index 8fd6898f..8d27d07d 100644
--- a/internal/pkg/exec.go
+++ b/internal/pkg/exec.go
@@ -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