diff --git a/internal/system/wayland.go b/internal/system/wayland.go index c423fe2..bbecde1 100644 --- a/internal/system/wayland.go +++ b/internal/system/wayland.go @@ -8,7 +8,7 @@ import ( "hakurei.app/container/check" "hakurei.app/hst" "hakurei.app/internal/acl" - "hakurei.app/internal/wayland" + "hakurei.app/system/wayland" ) type waylandConn interface { diff --git a/internal/system/wayland_test.go b/internal/system/wayland_test.go index 4e7db51..c70603b 100644 --- a/internal/system/wayland_test.go +++ b/internal/system/wayland_test.go @@ -7,7 +7,7 @@ import ( "hakurei.app/container/stub" "hakurei.app/internal/acl" - "hakurei.app/internal/wayland" + "hakurei.app/system/wayland" ) type stubWaylandConn struct { diff --git a/internal/wayland/conn.go b/internal/wayland/conn.go index c55fe70..addbc0d 100644 --- a/internal/wayland/conn.go +++ b/internal/wayland/conn.go @@ -1,101 +1,26 @@ package wayland import ( - "errors" - "net" "os" - "runtime" - "sync" "syscall" ) -// Conn represents a connection to the wayland display server. -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("socket already 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 -} - -// Bind binds the new socket to pathname. -func (c *Conn) Bind(pathname, appID, instanceID string) (*os.File, error) { - c.mu.Lock() - defer c.mu.Unlock() - - if c.conn == nil { - return nil, errors.New("socket not attached") - } - if c.done != nil { - return nil, errors.New("socket already bound") - } - - if rc, err := c.conn.SyscallConn(); err != nil { - // unreachable - return nil, err - } else { - c.done = make(chan struct{}) - return bindRawConn(c.done, rc, pathname, 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 +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 } 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()) + setupDone <- bind(fd, p, appID, instanceID, uintptr(closeFds[1])) close(setupDone) // keep socket alive until done is requested <-done - runtime.KeepAlive(syncPipe[1]) }); err != nil { setupDone <- err } @@ -105,7 +30,7 @@ func bindRawConn(done chan struct{}, rc syscall.RawConn, p, appID, instanceID st }() // return write end of the pipe - return syncPipe[1], <-setupDone + return closeFds, <-setupDone } func bind(fd uintptr, p, appID, instanceID string, syncFd uintptr) error { diff --git a/internal/wayland/wayland-client-helper.c b/internal/wayland/wayland-client-helper.c index a6a824b..ccfbaf6 100644 --- a/internal/wayland/wayland-client-helper.c +++ b/internal/wayland/wayland-client-helper.c @@ -36,7 +36,7 @@ hakurei_wayland_res hakurei_bind_wayland_fd( int fd, const char *app_id, const char *instance_id, - int sync_fd) { + int close_fd) { hakurei_wayland_res res = HAKUREI_WAYLAND_SUCCESS; /* see wayland.go for handling */ struct wl_display *display = NULL; @@ -88,7 +88,7 @@ hakurei_wayland_res hakurei_bind_wayland_fd( goto out; } - security_context = wp_security_context_manager_v1_create_listener(security_context_manager, listen_fd, sync_fd); + security_context = wp_security_context_manager_v1_create_listener(security_context_manager, listen_fd, close_fd); if (security_context == NULL) { /* not reached */ res = HAKUREI_WAYLAND_NOT_AVAIL; goto out; diff --git a/internal/wayland/wayland-client-helper.h b/internal/wayland/wayland-client-helper.h index dc456c3..9dd7c90 100644 --- a/internal/wayland/wayland-client-helper.h +++ b/internal/wayland/wayland-client-helper.h @@ -21,4 +21,4 @@ hakurei_wayland_res hakurei_bind_wayland_fd( int fd, const char *app_id, const char *instance_id, - int sync_fd); + int close_fd); diff --git a/system/wayland/deprecated.go b/system/wayland/deprecated.go index e83c910..e544597 100644 --- a/system/wayland/deprecated.go +++ b/system/wayland/deprecated.go @@ -4,13 +4,89 @@ package wayland import ( + "errors" + "net" + "os" + "runtime" + "sync" + "syscall" _ "unsafe" // for go:linkname "hakurei.app/internal/wayland" ) // Conn represents a connection to the wayland display server. -type Conn = wayland.Conn +// +// Deprecated: this interface is being replaced. +// Additionally, the package it belongs to will be removed in 0.4. +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("socket already 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 +} + +//go:linkname bindRawConn hakurei.app/internal/wayland.bindRawConn +func bindRawConn(done chan struct{}, rc syscall.RawConn, p, appID, instanceID string) ([2]int, error) + +// Bind binds the new socket to pathname. +func (c *Conn) Bind(pathname, appID, instanceID string) (*os.File, error) { + c.mu.Lock() + defer c.mu.Unlock() + + if c.conn == nil { + return nil, errors.New("socket not attached") + } + if c.done != nil { + return nil, errors.New("socket already bound") + } + + if rc, err := c.conn.SyscallConn(); err != nil { + // unreachable + return nil, err + } else { + c.done = make(chan struct{}) + if closeFds, err := bindRawConn(c.done, rc, pathname, appID, instanceID); err != nil { + return nil, err + } else { + return os.NewFile(uintptr(closeFds[1]), "close_fd"), nil + } + } +} const ( // WaylandDisplay contains the name of the server socket