aboutsummaryrefslogtreecommitdiffhomepage
path: root/helper/proc
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-11-12 23:12:38 +0900
committerOphestra <cat@gensokyo.uk>2025-11-12 23:16:13 +0900
commitf347d44c2281f507e3786aa498f634bfa4c92d84 (patch)
treefe85b41d5f6a5e308b87585f46efd64f90221e2b /helper/proc
parentb5630f6883c874d458d9ae3c666cfb7ae5e3121d (diff)
internal/helper: relocate from helper
This package is ugly and is pending removal only kept alive by xdg-dbus-proxy. Its exported symbols are made available until v0.4.0 where it will be removed for #24. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'helper/proc')
-rw-r--r--helper/proc/deprecated.go63
-rw-r--r--helper/proc/files.go162
-rw-r--r--helper/proc/pipe.go110
3 files changed, 63 insertions, 272 deletions
diff --git a/helper/proc/deprecated.go b/helper/proc/deprecated.go
new file mode 100644
index 00000000..91897526
--- /dev/null
+++ b/helper/proc/deprecated.go
@@ -0,0 +1,63 @@
+// Deprecated: This package will be removed in 0.4.
+package proc
+
+import (
+ "context"
+ "io"
+ "os"
+ "os/exec"
+ "time"
+ _ "unsafe" // for go:linkname
+
+ "hakurei.app/internal/helper/proc"
+)
+
+//go:linkname FulfillmentTimeout hakurei.app/internal/helper/proc.FulfillmentTimeout
+var FulfillmentTimeout time.Duration
+
+// A File is an extra file with deferred initialisation.
+type File = proc.File
+
+// ExtraFilesPre is a linked list storing addresses of [os.File].
+type ExtraFilesPre = proc.ExtraFilesPre
+
+// Fulfill calls the [File.Fulfill] method on all files, starts cmd and blocks until all fulfillment completes.
+//
+//go:linkname Fulfill hakurei.app/internal/helper/proc.Fulfill
+func Fulfill(ctx context.Context,
+ v *[]*os.File, start func() error,
+ files []File, extraFiles *ExtraFilesPre,
+) (err error)
+
+// InitFile initialises f as part of the slice extraFiles points to,
+// and returns its final fd value.
+//
+//go:linkname InitFile hakurei.app/internal/helper/proc.InitFile
+func InitFile(f File, extraFiles *ExtraFilesPre) (fd uintptr)
+
+// BaseFile implements the Init method of the File interface and provides indirect access to extra file state.
+type BaseFile = proc.BaseFile
+
+//go:linkname ExtraFile hakurei.app/internal/helper/proc.ExtraFile
+func ExtraFile(cmd *exec.Cmd, f *os.File) (fd uintptr)
+
+//go:linkname ExtraFileSlice hakurei.app/internal/helper/proc.ExtraFileSlice
+func ExtraFileSlice(extraFiles *[]*os.File, f *os.File) (fd uintptr)
+
+// NewWriterTo returns a [File] that receives content from wt on fulfillment.
+//
+//go:linkname NewWriterTo hakurei.app/internal/helper/proc.NewWriterTo
+func NewWriterTo(wt io.WriterTo) File
+
+// NewStat returns a [File] implementing the behaviour
+// of the receiving end of xdg-dbus-proxy stat fd.
+//
+//go:linkname NewStat hakurei.app/internal/helper/proc.NewStat
+func NewStat(s *io.Closer) File
+
+var (
+ //go:linkname ErrStatFault hakurei.app/internal/helper/proc.ErrStatFault
+ ErrStatFault error
+ //go:linkname ErrStatRead hakurei.app/internal/helper/proc.ErrStatRead
+ ErrStatRead error
+)
diff --git a/helper/proc/files.go b/helper/proc/files.go
deleted file mode 100644
index 0612718e..00000000
--- a/helper/proc/files.go
+++ /dev/null
@@ -1,162 +0,0 @@
-package proc
-
-import (
- "context"
- "os"
- "os/exec"
- "sync/atomic"
- "syscall"
- "testing"
- "time"
-)
-
-var FulfillmentTimeout = 2 * time.Second
-
-func init() {
- if testing.Testing() {
- FulfillmentTimeout *= 10
- }
-}
-
-// A File is an extra file with deferred initialisation.
-type File interface {
- // Init initialises File state. Init must not be called more than once.
- Init(fd uintptr, v **os.File) uintptr
- // Fd returns the fd value set on initialisation.
- Fd() uintptr
- // ErrCount returns count of error values emitted during fulfillment.
- ErrCount() int
- // Fulfill is called prior to process creation and must populate its corresponding file address.
- // Calls to dispatchErr must match the return value of ErrCount.
- // Fulfill must not be called more than once.
- Fulfill(ctx context.Context, dispatchErr func(error)) error
-}
-
-// ExtraFilesPre is a linked list storing addresses of [os.File].
-type ExtraFilesPre struct {
- n *ExtraFilesPre
- v *os.File
-}
-
-// Append grows the list by one entry and returns an address of the address of [os.File] stored in the new entry.
-func (f *ExtraFilesPre) Append() (uintptr, **os.File) { return f.append(3) }
-
-// Files returns a slice pointing to a continuous segment of memory containing all addresses stored in f in order.
-func (f *ExtraFilesPre) Files() []*os.File { return f.copy(make([]*os.File, 0, f.len())) }
-
-func (f *ExtraFilesPre) append(i uintptr) (uintptr, **os.File) {
- if f.n == nil {
- f.n = new(ExtraFilesPre)
- return i, &f.v
- }
- return f.n.append(i + 1)
-}
-func (f *ExtraFilesPre) len() uintptr {
- if f == nil {
- return 0
- }
- return f.n.len() + 1
-}
-func (f *ExtraFilesPre) copy(e []*os.File) []*os.File {
- if f == nil {
- // the public methods ensure the first call is never nil;
- // the last element is unused, slice it off here
- return e[:len(e)-1]
- }
- return f.n.copy(append(e, f.v))
-}
-
-// Fulfill calls the [File.Fulfill] method on all files, starts cmd and blocks until all fulfillment completes.
-func Fulfill(ctx context.Context,
- v *[]*os.File, start func() error,
- files []File, extraFiles *ExtraFilesPre,
-) (err error) {
- var ecs int
- for _, o := range files {
- ecs += o.ErrCount()
- }
- ec := make(chan error, ecs)
-
- c, cancel := context.WithTimeout(ctx, FulfillmentTimeout)
- defer cancel()
-
- for _, f := range files {
- err = f.Fulfill(c, makeDispatchErr(f, ec))
- if err != nil {
- return
- }
- }
-
- *v = extraFiles.Files()
- if err = start(); err != nil {
- return
- }
-
- for ecs > 0 {
- select {
- case err = <-ec:
- ecs--
- if err != nil {
- break
- }
- case <-ctx.Done():
- err = syscall.ECANCELED
- break
- }
- }
- return
-}
-
-// InitFile initialises f as part of the slice extraFiles points to,
-// and returns its final fd value.
-func InitFile(f File, extraFiles *ExtraFilesPre) (fd uintptr) { return f.Init(extraFiles.Append()) }
-
-// BaseFile implements the Init method of the File interface and provides indirect access to extra file state.
-type BaseFile struct {
- fd uintptr
- v **os.File
-}
-
-func (f *BaseFile) Init(fd uintptr, v **os.File) uintptr {
- if v == nil || fd < 3 {
- panic("invalid extra file initial state")
- }
- if f.v != nil {
- panic("extra file initialised twice")
- }
- f.fd, f.v = fd, v
- return fd
-}
-
-func (f *BaseFile) Fd() uintptr {
- if f.v == nil {
- panic("use of uninitialised extra file")
- }
- return f.fd
-}
-
-func (f *BaseFile) Set(v *os.File) {
- *f.v = v // runtime guards against use before init
-}
-
-func makeDispatchErr(f File, ec chan<- error) func(error) {
- c := new(atomic.Int32)
- c.Store(int32(f.ErrCount()))
- return func(err error) {
- if c.Add(-1) < 0 {
- panic("unexpected error dispatches")
- }
- ec <- err
- }
-}
-
-func ExtraFile(cmd *exec.Cmd, f *os.File) (fd uintptr) {
- return ExtraFileSlice(&cmd.ExtraFiles, f)
-}
-
-func ExtraFileSlice(extraFiles *[]*os.File, f *os.File) (fd uintptr) {
- // ExtraFiles: If non-nil, entry i becomes file descriptor 3+i.
- fd = uintptr(3 + len(*extraFiles))
- *extraFiles = append(*extraFiles, f)
- return
-}
diff --git a/helper/proc/pipe.go b/helper/proc/pipe.go
deleted file mode 100644
index 838c4ae4..00000000
--- a/helper/proc/pipe.go
+++ /dev/null
@@ -1,110 +0,0 @@
-package proc
-
-import (
- "context"
- "errors"
- "io"
- "os"
- "runtime"
-)
-
-// 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)
- runtime.KeepAlive(r)
- }()
- go func() {
- select {
- case <-done:
- dispatchErr(nil)
- case <-ctx.Done():
- dispatchErr(w.Close()) // this aborts WriteTo with file already closed
- runtime.KeepAlive(r)
- }
- }()
-
- 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")
- }
- runtime.KeepAlive(w)
- }()
-
- go func() {
- select {
- case <-done:
- dispatchErr(nil)
- case <-ctx.Done():
- dispatchErr(r.Close()) // this aborts Read with file already closed
- runtime.KeepAlive(w)
- }
- }()
-
- // this gets closed by the caller
- *f.s = r
- return nil
-}