aboutsummaryrefslogtreecommitdiffhomepage
path: root/helper
diff options
context:
space:
mode:
authorOphestra Umiker <cat@ophivana.moe>2024-09-24 16:11:08 +0900
committerOphestra Umiker <cat@ophivana.moe>2024-09-24 16:11:08 +0900
commit000607da5fca0e101cb43db9b1c1b466e3292d35 (patch)
tree3dcf1cdbf086bca5883964324442de42e17776bb /helper
parent1cb90c0840e6d2bdef9ed8b9cb5ae54fc1b6c91c (diff)
helper: separate helper args fd builder from dbus
This method of passing arguments is used in bubblewrap as well as other tools, this commit separates the argument builder/writer to the helper package and generalise it as an interface. Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
Diffstat (limited to 'helper')
-rw-r--r--helper/args.go97
-rw-r--r--helper/helper.go4
2 files changed, 101 insertions, 0 deletions
diff --git a/helper/args.go b/helper/args.go
new file mode 100644
index 00000000..bbb66f0c
--- /dev/null
+++ b/helper/args.go
@@ -0,0 +1,97 @@
+package helper
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+ "sync"
+)
+
+var (
+ ErrContainsNull = errors.New("argument contains null character")
+)
+
+// Args is sealed with a slice of arguments for writing to the helper args FD.
+// The sealing args is checked to not contain null characters.
+// Attempting to seal an instance twice will cause a panic.
+type Args interface {
+ Seal(args []string) error
+ io.WriterTo
+ fmt.Stringer
+}
+
+// argsFD implements Args for helpers expecting null terminated arguments to a file descriptor.
+// argsFD must not be copied after first use.
+type argsFD struct {
+ seal []byte
+ sync.RWMutex
+}
+
+func (a *argsFD) Seal(args []string) error {
+ a.Lock()
+ defer a.Unlock()
+
+ if a.seal != nil {
+ panic("args sealed twice")
+ }
+
+ seal := bytes.Buffer{}
+
+ n := 0
+ for _, arg := range args {
+ // reject argument strings containing null
+ if hasNull(arg) {
+ return ErrContainsNull
+ }
+
+ // accumulate buffer size
+ n += len(arg) + 1
+ }
+ seal.Grow(n)
+
+ // write null terminated arguments
+ for _, arg := range args {
+ seal.WriteString(arg)
+ seal.WriteByte('\x00')
+ }
+
+ a.seal = seal.Bytes()
+ return nil
+}
+
+func (a *argsFD) WriteTo(w io.Writer) (int64, error) {
+ if a.seal == nil {
+ panic("attempted to activate unsealed args")
+ }
+
+ n, err := w.Write(a.seal)
+ return int64(n), err
+}
+
+func (a *argsFD) String() string {
+ if a == nil {
+ return "(invalid helper args)"
+ }
+
+ if a.seal == nil {
+ return "(unsealed helper args)"
+ }
+
+ return strings.ReplaceAll(string(a.seal), "\x00", " ")
+}
+
+func hasNull(s string) bool {
+ for _, b := range s {
+ if b == '\x00' {
+ return true
+ }
+ }
+ return false
+}
+
+// NewArgs returns a new instance of Args
+func NewArgs() Args {
+ return new(argsFD)
+}
diff --git a/helper/helper.go b/helper/helper.go
new file mode 100644
index 00000000..d9bcc7f4
--- /dev/null
+++ b/helper/helper.go
@@ -0,0 +1,4 @@
+/*
+Package helper runs external helpers and manages their status and args FDs.
+*/
+package helper