aboutsummaryrefslogtreecommitdiffhomepage
path: root/helper/pipe.go
diff options
context:
space:
mode:
authorOphestra Umiker <cat@ophivana.moe>2024-10-07 12:48:20 +0900
committerOphestra Umiker <cat@ophivana.moe>2024-10-07 12:48:20 +0900
commit9647eb6a6b5607e9aac1e2c113e80f2e966ec0ac (patch)
tree2f314f8669742a817df0057ceab79891b36bea9d /helper/pipe.go
parent18d9ce733e19c500ca04b4e744e6cdc5c6faf137 (diff)
helper: separate pipes from Helper
Upcoming bwrap helper implementation requires two sets of pipes to be managed, fd will also no longer be constant. Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
Diffstat (limited to 'helper/pipe.go')
-rw-r--r--helper/pipe.go150
1 files changed, 150 insertions, 0 deletions
diff --git a/helper/pipe.go b/helper/pipe.go
new file mode 100644
index 00000000..e6013283
--- /dev/null
+++ b/helper/pipe.go
@@ -0,0 +1,150 @@
+package helper
+
+import (
+ "errors"
+ "io"
+ "os"
+ "os/exec"
+)
+
+type pipes struct {
+ args io.WriterTo
+
+ statP [2]*os.File
+ argsP [2]*os.File
+
+ ready chan error
+
+ cmd *exec.Cmd
+}
+
+func (p *pipes) pipe() error {
+ if p.statP[0] != nil || p.statP[1] != nil ||
+ p.argsP[0] != nil || p.argsP[1] != nil {
+ panic("attempted to pipe twice")
+ }
+ if p.args == nil {
+ panic("attempted to pipe without args")
+ }
+
+ // create pipes
+ if pr, pw, err := os.Pipe(); err != nil {
+ return err
+ } else {
+ p.argsP[0], p.argsP[1] = pr, pw
+ }
+
+ // create status pipes if ready signal is requested
+ if p.ready != nil {
+ if pr, pw, err := os.Pipe(); err != nil {
+ return err
+ } else {
+ p.statP[0], p.statP[1] = pr, pw
+ }
+ }
+
+ return nil
+}
+
+// calls pipe to create pipes and sets them up as ExtraFiles, returning their fd
+func (p *pipes) prepareCmd(cmd *exec.Cmd) (int, int, error) {
+ if err := p.pipe(); err != nil {
+ return -1, -1, err
+ }
+
+ // save a reference of cmd for future use
+ p.cmd = cmd
+
+ // ExtraFiles: If non-nil, entry i becomes file descriptor 3+i.
+ argsFd := 3 + len(cmd.ExtraFiles)
+ cmd.ExtraFiles = append(cmd.ExtraFiles, p.argsP[0])
+
+ if p.ready != nil {
+ cmd.ExtraFiles = append(cmd.ExtraFiles, p.statP[1])
+ return argsFd, argsFd + 1, nil
+ } else {
+ return argsFd, -1, nil
+ }
+}
+
+func (p *pipes) readyWriteArgs() error {
+ statsP, argsP := p.statP[0], p.argsP[1]
+
+ // write arguments and close args pipe
+ if _, err := p.args.WriteTo(argsP); err != nil {
+ if err1 := p.cmd.Process.Kill(); err1 != nil {
+ // should be unreachable
+ panic(err1.Error())
+ }
+ return err
+ } else {
+ if err = argsP.Close(); err != nil {
+ if err1 := p.cmd.Process.Kill(); err1 != nil {
+ // should be unreachable
+ panic(err1.Error())
+ }
+ return err
+ }
+ }
+
+ if p.ready != nil {
+ // monitor stat pipe
+ go func() {
+ n, err := statsP.Read(make([]byte, 1))
+ switch n {
+ case -1:
+ if err1 := p.cmd.Process.Kill(); err1 != nil {
+ // should be unreachable
+ panic(err1.Error())
+ }
+ // ensure error is not nil
+ if err == nil {
+ err = ErrStatusFault
+ }
+ p.ready <- err
+ case 0:
+ // ensure error is not nil
+ if err == nil {
+ err = ErrStatusRead
+ }
+ p.ready <- err
+ case 1:
+ p.ready <- nil
+ default:
+ panic("unreachable") // unexpected read count
+ }
+ }()
+ }
+
+ return nil
+}
+
+func (p *pipes) mustClosePipes() {
+ if err := p.argsP[0].Close(); err != nil && !errors.Is(err, os.ErrClosed) {
+ // unreachable
+ panic(err.Error())
+ }
+ if err := p.argsP[1].Close(); err != nil && !errors.Is(err, os.ErrClosed) {
+ // unreachable
+ panic(err.Error())
+ }
+
+ if p.ready != nil {
+ if err := p.statP[0].Close(); err != nil && !errors.Is(err, os.ErrClosed) {
+ // unreachable
+ panic(err.Error())
+ }
+ if err := p.statP[1].Close(); err != nil && !errors.Is(err, os.ErrClosed) {
+ // unreachable
+ panic(err.Error())
+ }
+ }
+}
+
+func (p *pipes) closeStatus() error {
+ if p.ready == nil {
+ panic("attempted to close helper with no status pipe")
+ }
+
+ return p.statP[0].Close()
+}