All checks were successful
Test / Create distribution (push) Successful in 35s
Test / Sandbox (push) Successful in 2m14s
Test / Hakurei (push) Successful in 3m16s
Test / Hpkg (push) Successful in 4m5s
Test / Sandbox (race detector) (push) Successful in 4m18s
Test / Hakurei (race detector) (push) Successful in 5m8s
Test / Flake checks (push) Successful in 1m21s
This includes targeted paths in the returned errors. Signed-off-by: Ophestra <cat@gensokyo.uk>
159 lines
4.9 KiB
Go
159 lines
4.9 KiB
Go
// Package wayland implements Wayland security_context_v1 protocol.
|
|
package wayland
|
|
|
|
//go:generate sh -c "wayland-scanner client-header `pkg-config --variable=datarootdir wayland-protocols`/wayland-protocols/staging/security-context/security-context-v1.xml security-context-v1-protocol.h"
|
|
//go:generate sh -c "wayland-scanner private-code `pkg-config --variable=datarootdir wayland-protocols`/wayland-protocols/staging/security-context/security-context-v1.xml security-context-v1-protocol.c"
|
|
|
|
/*
|
|
#cgo linux pkg-config: --static wayland-client
|
|
#cgo freebsd openbsd LDFLAGS: -lwayland-client
|
|
|
|
#include "wayland-client-helper.h"
|
|
*/
|
|
import "C"
|
|
import (
|
|
"strings"
|
|
"syscall"
|
|
)
|
|
|
|
const (
|
|
// Display contains the name of the server socket
|
|
// (https://gitlab.freedesktop.org/wayland/wayland/-/blob/1.23.1/src/wayland-client.c#L1147)
|
|
// which is concatenated with XDG_RUNTIME_DIR
|
|
// (https://gitlab.freedesktop.org/wayland/wayland/-/blob/1.23.1/src/wayland-client.c#L1171)
|
|
// or used as-is if absolute
|
|
// (https://gitlab.freedesktop.org/wayland/wayland/-/blob/1.23.1/src/wayland-client.c#L1176).
|
|
Display = "WAYLAND_DISPLAY"
|
|
|
|
// FallbackName is used as the wayland socket name if WAYLAND_DISPLAY is unset
|
|
// (https://gitlab.freedesktop.org/wayland/wayland/-/blob/1.23.1/src/wayland-client.c#L1149).
|
|
FallbackName = "wayland-0"
|
|
)
|
|
|
|
type (
|
|
// Res is the outcome of a call to [New].
|
|
Res = C.hakurei_wayland_res
|
|
|
|
// An Error represents a failure during [New].
|
|
Error struct {
|
|
// Where the failure occurred.
|
|
Cause Res
|
|
// Attempted pathname socket.
|
|
Path string
|
|
// Pathname socket to host server. Omitted for libwayland errors.
|
|
Host string
|
|
// Global errno value set during the fault.
|
|
Errno error
|
|
}
|
|
)
|
|
|
|
// withPrefix returns prefix suffixed with errno description if available.
|
|
func (e *Error) withPrefix(prefix string) string {
|
|
if e.Errno == nil {
|
|
return prefix
|
|
}
|
|
return prefix + ": " + e.Errno.Error()
|
|
}
|
|
|
|
const (
|
|
// RSuccess is returned on a successful call.
|
|
RSuccess Res = C.HAKUREI_WAYLAND_SUCCESS
|
|
// RConnect is returned if wl_display_connect_to_fd failed. The global errno is set.
|
|
RConnect Res = C.HAKUREI_WAYLAND_CONNECT
|
|
// RListener is returned if wl_registry_add_listener failed. The global errno is set.
|
|
RListener Res = C.HAKUREI_WAYLAND_LISTENER
|
|
// RRoundtrip is returned if wl_display_roundtrip failed. The global errno is set.
|
|
RRoundtrip Res = C.HAKUREI_WAYLAND_ROUNDTRIP
|
|
// RNotAvail is returned if compositor does not implement wp_security_context_v1.
|
|
RNotAvail Res = C.HAKUREI_WAYLAND_NOT_AVAIL
|
|
// RSocket is returned if socket failed. The global errno is set.
|
|
RSocket Res = C.HAKUREI_WAYLAND_SOCKET
|
|
// RBind is returned if bind failed. The global errno is set.
|
|
RBind Res = C.HAKUREI_WAYLAND_BIND
|
|
// RListen is returned if listen failed. The global errno is set.
|
|
RListen Res = C.HAKUREI_WAYLAND_LISTEN
|
|
|
|
// RCreate is returned if ensuring pathname availability failed. Returned by [New].
|
|
RCreate Res = C.HAKUREI_WAYLAND_CREAT
|
|
// RHostSocket is returned if socket failed for host server. Returned by [New].
|
|
RHostSocket Res = C.HAKUREI_WAYLAND_HOST_SOCKET
|
|
// RHostConnect is returned if connect failed for host server. Returned by [New].
|
|
RHostConnect Res = C.HAKUREI_WAYLAND_HOST_CONNECT
|
|
)
|
|
|
|
func (e *Error) Unwrap() error { return e.Errno }
|
|
func (e *Error) Message() string { return e.Error() }
|
|
func (e *Error) Error() string {
|
|
switch e.Cause {
|
|
case RSuccess:
|
|
if e.Errno == nil {
|
|
return "success"
|
|
}
|
|
return e.Errno.Error()
|
|
|
|
case RConnect:
|
|
return e.withPrefix("wl_display_connect_to_fd failed")
|
|
case RListener:
|
|
return e.withPrefix("wl_registry_add_listener failed")
|
|
case RRoundtrip:
|
|
return e.withPrefix("wl_display_roundtrip failed")
|
|
case RNotAvail:
|
|
return "compositor does not implement security_context_v1"
|
|
|
|
case RSocket:
|
|
if e.Errno == nil {
|
|
return "socket operation failed"
|
|
}
|
|
return "socket: " + e.Errno.Error()
|
|
case RBind:
|
|
return e.withPrefix("cannot bind " + e.Path)
|
|
case RListen:
|
|
return e.withPrefix("cannot listen on " + e.Path)
|
|
|
|
case RCreate:
|
|
if e.Errno == nil {
|
|
return "cannot ensure wayland pathname socket"
|
|
}
|
|
return e.Errno.Error()
|
|
case RHostSocket:
|
|
return e.withPrefix("socket")
|
|
case RHostConnect:
|
|
return e.withPrefix("cannot connect to " + e.Host)
|
|
|
|
default:
|
|
return e.withPrefix("impossible outcome") /* not reached */
|
|
}
|
|
}
|
|
|
|
// securityContextBind calls hakurei_security_context_bind.
|
|
//
|
|
// A non-nil error has concrete type [Error].
|
|
func securityContextBind(
|
|
socketPath string,
|
|
serverFd int,
|
|
appID, instanceID string,
|
|
closeFd int,
|
|
) error {
|
|
if hasNull(appID) || hasNull(instanceID) {
|
|
return syscall.EINVAL
|
|
}
|
|
|
|
var e Error
|
|
e.Cause, e.Errno = C.hakurei_security_context_bind(
|
|
C.CString(socketPath),
|
|
C.int(serverFd),
|
|
C.CString(appID),
|
|
C.CString(instanceID),
|
|
C.int(closeFd),
|
|
)
|
|
|
|
if e.Cause == RSuccess {
|
|
return nil
|
|
}
|
|
e.Path = socketPath
|
|
return &e
|
|
}
|
|
|
|
// hasNull returns whether s contains the NUL character.
|
|
func hasNull(s string) bool { return strings.IndexByte(s, 0) > -1 }
|