diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-02-13 19:57:24 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-02-13 19:57:24 +0900 |
| commit | 58eb8f971ddc22941d6d32dd28e050f8c6f68048 (patch) | |
| tree | d44ef30f791c6dbffd96983bccf324d2a9085970 /helper | |
| parent | 0a1d7c01cd00ee1be7fbeda6048381041dd33262 (diff) | |
proc/pipe: implement args and stat file
This is a generic implementation of helper/pipe.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'helper')
| -rw-r--r-- | helper/proc/pipe.go | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/helper/proc/pipe.go b/helper/proc/pipe.go new file mode 100644 index 00000000..92ffe693 --- /dev/null +++ b/helper/proc/pipe.go @@ -0,0 +1,100 @@ +package proc + +import ( + "context" + "errors" + "io" + "os" +) + +// NewWriterTo returns a [File] that receives content from wt on fulfillment. +func NewWriterTo(wt io.WriterTo) File { return &writeToFile{wt: wt} } + +// writeToFile exports the read end of a pipe with data written by an [io.WriterTo]. +type writeToFile struct { + wt io.WriterTo + BaseFile +} + +func (f *writeToFile) ErrCount() int { return 3 } +func (f *writeToFile) Fulfill(ctx context.Context, dispatchErr func(error)) error { + r, w, err := os.Pipe() + if err != nil { + return err + } + f.Set(r) + + done := make(chan struct{}) + go func() { _, err = f.wt.WriteTo(w); dispatchErr(err); dispatchErr(w.Close()); close(done) }() + go func() { + select { + case <-done: + dispatchErr(nil) + case <-ctx.Done(): + dispatchErr(w.Close()) // this aborts WriteTo with file already closed + } + }() + + return nil +} + +// NewStat returns a [File] implementing the behaviour +// of the receiving end of xdg-dbus-proxy stat fd. +func NewStat(s *io.Closer) File { return &statFile{s: s} } + +var ( + ErrStatFault = errors.New("generic stat fd fault") + ErrStatRead = errors.New("unexpected stat behaviour") +) + +// statFile implements xdg-dbus-proxy stat fd behaviour. +type statFile struct { + s *io.Closer + BaseFile +} + +func (f *statFile) ErrCount() int { return 2 } +func (f *statFile) Fulfill(ctx context.Context, dispatchErr func(error)) error { + r, w, err := os.Pipe() + if err != nil { + return err + } + f.Set(w) + + done := make(chan struct{}) + go func() { + defer close(done) + var n int + + n, err = r.Read(make([]byte, 1)) + switch n { + case -1: + if err == nil { + err = ErrStatFault + } + dispatchErr(err) + case 0: + if err == nil { + err = ErrStatRead + } + dispatchErr(err) + case 1: + dispatchErr(err) + default: + panic("unreachable") + } + }() + + go func() { + select { + case <-done: + dispatchErr(nil) + case <-ctx.Done(): + dispatchErr(r.Close()) // this aborts Read with file already closed + } + }() + + // this gets closed by the caller + *f.s = r + return nil +} |
