diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-11-16 01:23:16 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-11-16 01:23:16 +0900 |
| commit | 61972d61f650be2fb86eb2ead452b74c02b3e00f (patch) | |
| tree | ca86e8190a1de3eb64ed367acaa428521bbc5b87 /internal/wayland/conn.go | |
| parent | fe40af7b7e95356b7d88e35941097edc9700f093 (diff) | |
internal/wayland: reimplement connect/bind code
The old implementation is relocated to system/wayland/deprecated.go.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/wayland/conn.go')
| -rw-r--r-- | internal/wayland/conn.go | 100 |
1 files changed, 69 insertions, 31 deletions
diff --git a/internal/wayland/conn.go b/internal/wayland/conn.go index addbc0d3..c861c05f 100644 --- a/internal/wayland/conn.go +++ b/internal/wayland/conn.go @@ -1,47 +1,85 @@ package wayland import ( + "errors" "os" "syscall" + + "hakurei.app/container/check" ) -func bindRawConn(done chan struct{}, rc syscall.RawConn, p, appID, instanceID string) ([2]int, error) { - var closeFds [2]int - if err := syscall.Pipe2(closeFds[0:], syscall.O_CLOEXEC); err != nil { - return closeFds, err - } +// SecurityContext holds resources associated with a Wayland security_context. +type SecurityContext struct { + // Pipe with its write end passed to security-context-v1. + closeFds [2]int +} - setupDone := make(chan error, 1) // does not block with c.done +// Close releases any resources held by [SecurityContext], and prevents further +// connections to its associated socket. +func (sc *SecurityContext) Close() error { + if sc == nil { + return os.ErrInvalid + } + return errors.Join( + syscall.Close(sc.closeFds[1]), + syscall.Close(sc.closeFds[0]), + ) +} - go func() { - if err := rc.Control(func(fd uintptr) { - // allow the Bind method to return after setup - setupDone <- bind(fd, p, appID, instanceID, uintptr(closeFds[1])) - close(setupDone) +// New creates a new security context on the Wayland display at displayPath +// and associates it with a new socket bound to bindPath. +// +// New does not attach a finalizer to the resulting [SecurityContext] struct. +// The caller is responsible for calling [SecurityContext.Close]. +// +// A non-nil error unwraps to concrete type [Error]. +func New(displayPath, bindPath *check.Absolute, appID, instanceID string) (*SecurityContext, error) { + // ensure bindPath is available + if f, err := os.Create(bindPath.String()); err != nil { + return nil, &Error{Cause: RHostCreate, Errno: err} + } else if err = f.Close(); err != nil { + return nil, &Error{Cause: RHostCreate, Errno: err} + } else if err = os.Remove(bindPath.String()); err != nil { + return nil, &Error{Cause: RHostCreate, Errno: err} + } - // keep socket alive until done is requested - <-done - }); err != nil { - setupDone <- err + if fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0); err != nil { + return nil, &Error{RHostSocket, err} + } else if err = syscall.Connect(fd, &syscall.SockaddrUnix{Name: displayPath.String()}); err != nil { + _ = syscall.Close(fd) + return nil, &Error{RHostConnect, err} + } else { + closeFds, bindErr := bindSecurityContext(fd, bindPath, appID, instanceID) + if bindErr != nil { + // do not leak the pipe and socket + err = errors.Join(bindErr, // already wrapped + syscall.Close(closeFds[1]), + syscall.Close(closeFds[0]), + syscall.Close(fd), + ) } - - // notify Close that rc.Control has returned - close(done) - }() - - // return write end of the pipe - return closeFds, <-setupDone + return &SecurityContext{closeFds}, err + } } -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 +// bindSecurityContext binds a socket associated to a security context created on serverFd, +// returning the pipe file descriptors used for security-context-v1 close_fd. +// +// A non-nil error unwraps to concrete type [Error]. +func bindSecurityContext(serverFd int, bindPath *check.Absolute, appID, instanceID string) ([2]int, error) { + // write end passed to security-context-v1 close_fd + var closeFds [2]int + if err := syscall.Pipe2(closeFds[0:], syscall.O_CLOEXEC); err != nil { + return closeFds, err } - return bindWaylandFd(p, fd, appID, instanceID, syncFd) + // returned error is already wrapped + if err := bindWaylandFd(bindPath.String(), uintptr(serverFd), appID, instanceID, uintptr(closeFds[1])); err != nil { + return closeFds, errors.Join(err, + syscall.Close(closeFds[1]), + syscall.Close(closeFds[0]), + ) + } else { + return closeFds, nil + } } |
