aboutsummaryrefslogtreecommitdiffhomepage
path: root/sandbox/wl/conn.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-03-25 05:26:37 +0900
committerOphestra <cat@gensokyo.uk>2025-03-25 05:26:37 +0900
commit61dbfeffe77df9c1a11413b3cb8954933e79b45f (patch)
tree7f7f6bde9a8662848ad9392621316cbbc454a9f0 /sandbox/wl/conn.go
parent532feb4bfaa84a57dc8de97f36b13a89610a0584 (diff)
sandbox/wl: move into sandbox
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'sandbox/wl/conn.go')
-rw-r--r--sandbox/wl/conn.go121
1 files changed, 121 insertions, 0 deletions
diff --git a/sandbox/wl/conn.go b/sandbox/wl/conn.go
new file mode 100644
index 00000000..9baa5abe
--- /dev/null
+++ b/sandbox/wl/conn.go
@@ -0,0 +1,121 @@
+// Package wl implements Wayland security_context_v1 protocol.
+package wl
+
+import (
+ "errors"
+ "net"
+ "os"
+ "runtime"
+ "sync"
+ "syscall"
+)
+
+type Conn struct {
+ conn *net.UnixConn
+
+ done chan struct{}
+ doneOnce sync.Once
+
+ mu sync.Mutex
+}
+
+// Attach connects Conn to a wayland socket.
+func (c *Conn) Attach(p string) (err error) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ if c.conn != nil {
+ return errors.New("attached")
+ }
+
+ c.conn, err = net.DialUnix("unix", nil, &net.UnixAddr{Name: p, Net: "unix"})
+ return
+}
+
+// Close releases resources and closes the connection to the wayland compositor.
+func (c *Conn) Close() error {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ if c.done == nil {
+ return errors.New("no socket bound")
+ }
+
+ c.doneOnce.Do(func() {
+ c.done <- struct{}{}
+ <-c.done
+ })
+
+ // closed by wayland
+ runtime.SetFinalizer(c.conn, nil)
+ return nil
+}
+
+func (c *Conn) Bind(p, appID, instanceID string) (*os.File, error) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ if c.conn == nil {
+ return nil, errors.New("not attached")
+ }
+ if c.done != nil {
+ return nil, errors.New("bound")
+ }
+
+ if rc, err := c.conn.SyscallConn(); err != nil {
+ // unreachable
+ return nil, err
+ } else {
+ c.done = make(chan struct{})
+ return bindRawConn(c.done, rc, p, appID, instanceID)
+ }
+}
+
+func bindRawConn(done chan struct{}, rc syscall.RawConn, p, appID, instanceID string) (*os.File, error) {
+ var syncPipe [2]*os.File
+
+ if r, w, err := os.Pipe(); err != nil {
+ return nil, err
+ } else {
+ syncPipe[0] = r
+ syncPipe[1] = w
+ }
+
+ setupDone := make(chan error, 1) // does not block with c.done
+
+ go func() {
+ if err := rc.Control(func(fd uintptr) {
+ // prevent runtime from closing the read end of sync fd
+ runtime.SetFinalizer(syncPipe[0], nil)
+
+ // allow the Bind method to return after setup
+ setupDone <- bind(fd, p, appID, instanceID, syncPipe[0].Fd())
+ close(setupDone)
+
+ // keep socket alive until done is requested
+ <-done
+ runtime.KeepAlive(syncPipe[1])
+ }); err != nil {
+ setupDone <- err
+ }
+
+ // notify Close that rc.Control has returned
+ close(done)
+ }()
+
+ // return write end of the pipe
+ return syncPipe[1], <-setupDone
+}
+
+func bind(fd uintptr, p, appID, instanceID string, syncFd uintptr) error {
+ // ensure p is available
+ if f, err := os.Create(p); err != nil {
+ return err
+ } else if err = f.Close(); err != nil {
+ return err
+ } else if err = os.Remove(p); err != nil {
+ return err
+ }
+
+ return bindWaylandFd(p, fd, appID, instanceID, syncFd)
+}