internal/pipewire: implement SecurityContext::Create
All checks were successful
Test / Create distribution (push) Successful in 36s
Test / Sandbox (push) Successful in 2m26s
Test / Hakurei (push) Successful in 3m18s
Test / Hpkg (push) Successful in 4m16s
Test / Sandbox (race detector) (push) Successful in 4m22s
Test / Hakurei (race detector) (push) Successful in 5m12s
Test / Flake checks (push) Successful in 1m27s

This is finally the thing we are after.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-11-29 16:08:31 +09:00
parent 5a5c4705dd
commit de3fc7ba38
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
6 changed files with 157 additions and 52 deletions

View File

@ -10,6 +10,7 @@ func TestFooterCoreGeneration(t *testing.T) {
t.Parallel()
encodingTestCases[pipewire.Footer[pipewire.FooterCoreGeneration], *pipewire.Footer[pipewire.FooterCoreGeneration]]{
/* recvmsg 0 */
{"sample0", samplePWContainer[1][0][2], pipewire.Footer[pipewire.FooterCoreGeneration]{
@ -29,6 +30,7 @@ func TestFooterCoreGeneration(t *testing.T) {
}.run(t)
encodingTestCases[pipewire.Footer[pipewire.FooterClientGeneration], *pipewire.Footer[pipewire.FooterClientGeneration]]{
/* sendmsg 1 */
{"sample0", samplePWContainer[3][0][2], pipewire.Footer[pipewire.FooterClientGeneration]{
@ -36,6 +38,13 @@ func TestFooterCoreGeneration(t *testing.T) {
// why does this not match FooterCoreGeneration sample2?
Payload: pipewire.FooterClientGeneration{ClientGeneration: 0x23},
}, nil},
/* sendmsg 2 */
{"sample1", samplePWContainer[6][0][2], pipewire.Footer[pipewire.FooterClientGeneration]{
Opcode: pipewire.FOOTER_CORE_OPCODE_GENERATION,
Payload: pipewire.FooterClientGeneration{ClientGeneration: 0x24},
}, nil},
}.run(t)
}
@ -101,6 +110,12 @@ func TestCoreDone(t *testing.T) {
ID: 0,
Sequence: pipewire.CoreSyncSequenceOffset + 3,
}, nil},
// matches the second Core::Sync sample
{"sample2", samplePWContainer[7][0][1], pipewire.CoreDone{
ID: 0,
Sequence: pipewire.CoreSyncSequenceOffset + 6,
}, nil},
}.run(t)
}
@ -150,10 +165,15 @@ func TestCoreSync(t *testing.T) {
t.Parallel()
encodingTestCases[pipewire.CoreSync, *pipewire.CoreSync]{
{"sample", samplePWContainer[0][3][1], pipewire.CoreSync{
{"sample0", samplePWContainer[0][3][1], pipewire.CoreSync{
ID: 0,
Sequence: pipewire.CoreSyncSequenceOffset + 3,
}, nil},
{"sample1", samplePWContainer[6][1][1], pipewire.CoreSync{
ID: 0,
Sequence: pipewire.CoreSyncSequenceOffset + 6,
}, nil},
}.run(t)
}

View File

@ -32,7 +32,7 @@ func TestHeader(t *testing.T) {
Size: 0x28, Sequence: 2, FileCount: 0,
}, nil},
{"PW_CORE_METHOD_SYNC", samplePWContainer[0][3][0], pipewire.Header{
{"PW_CORE_METHOD_SYNC 0", samplePWContainer[0][3][0], pipewire.Header{
ID: pipewire.PW_ID_CORE,
Opcode: pipewire.PW_CORE_METHOD_SYNC,
Size: 0x28, Sequence: 3, FileCount: 0,
@ -314,33 +314,27 @@ func TestHeader(t *testing.T) {
Size: 0x68, Sequence: 43, FileCount: 0,
}, nil},
{"PW_SECURITY_CONTEXT_METHOD_CREATE", []byte{
// Id
3, 0, 0, 0,
// size
0xd8, 0, 0,
// opcode
1,
// seq
5, 0, 0, 0,
// n_fds
2, 0, 0, 0,
}, pipewire.Header{ID: 3, Opcode: pipewire.PW_SECURITY_CONTEXT_METHOD_CREATE,
Size: 0xd8, Sequence: 5, FileCount: 2}, nil},
/* sendmsg 2 */
{"PW_SECURITY_CONTEXT_METHOD_NUM", []byte{
// Id
0, 0, 0, 0,
// size
0x28, 0, 0,
// opcode
2,
// seq
6, 0, 0, 0,
// n_fds
0, 0, 0, 0,
}, pipewire.Header{ID: 0, Opcode: pipewire.PW_SECURITY_CONTEXT_METHOD_NUM,
Size: 0x28, Sequence: 6, FileCount: 0}, nil},
{"PW_SECURITY_CONTEXT_METHOD_CREATE", samplePWContainer[6][0][0], pipewire.Header{
ID: 3,
Opcode: pipewire.PW_SECURITY_CONTEXT_METHOD_CREATE,
Size: 0xd8, Sequence: 5, FileCount: 2,
}, nil},
{"PW_CORE_METHOD_SYNC 1", samplePWContainer[6][1][0], pipewire.Header{
ID: pipewire.PW_ID_CORE,
Opcode: pipewire.PW_CORE_METHOD_SYNC,
Size: 0x28, Sequence: 6, FileCount: 0,
}, nil},
/* recvmsg 2 */
{"PW_CORE_EVENT_DONE 2", samplePWContainer[7][0][0], pipewire.Header{
ID: pipewire.PW_ID_CORE,
Opcode: pipewire.PW_CORE_EVENT_DONE,
Size: 0x28, Sequence: 44, FileCount: 0,
}, nil},
}.run(t)
t.Run("size range", func(t *testing.T) {

View File

@ -356,30 +356,6 @@ const (
PW_KEY_PROFILER_NAME = "profiler.name"
)
/* pipewire/extensions/security-context.h */
const (
PW_TYPE_INTERFACE_SecurityContext = PW_TYPE_INFO_INTERFACE_BASE + "SecurityContext"
PW_SECURITY_CONTEXT_PERM_MASK = PW_PERM_RWX
PW_VERSION_SECURITY_CONTEXT = 3
PW_EXTENSION_MODULE_SECURITY_CONTEXT = PIPEWIRE_MODULE_PREFIX + "module-security-context"
)
const (
PW_SECURITY_CONTEXT_EVENT_NUM = iota
PW_VERSION_SECURITY_CONTEXT_EVENTS = 0
)
const (
PW_SECURITY_CONTEXT_METHOD_ADD_LISTENER = iota
PW_SECURITY_CONTEXT_METHOD_CREATE
PW_SECURITY_CONTEXT_METHOD_NUM
PW_VERSION_SECURITY_CONTEXT_METHODS = 0
)
/* pipewire/type.h */
const (

View File

@ -30,6 +30,9 @@ type (
String = string
// Bytes is a byte slice representing SPA_TYPE_Bytes.
Bytes = []byte
// A Fd is a signed integer value representing SPA_TYPE_Fd.
Fd Long
)
const (
@ -49,6 +52,9 @@ const (
SizeInt Word = 4
// SizeLong is the fixed, unpadded size of a [SPA_TYPE_Long] value.
SizeLong Word = 8
// SizeFd is the fixed, unpadded size of a [SPA_TYPE_Fd] value.
SizeFd = SizeLong
)
/* Basic types */
@ -176,6 +182,15 @@ func marshalValueAppend(data []byte, v reflect.Value) ([]byte, error) {
// marshalValueAppendRaw implements [MarshalAppend] on [reflect.Value] without the size prefix.
func marshalValueAppendRaw(data []byte, v reflect.Value) ([]byte, error) {
if v.CanInterface() {
switch c := v.Interface().(type) {
case Fd:
data = binary.NativeEndian.AppendUint32(data, SPA_TYPE_Fd)
data = binary.NativeEndian.AppendUint64(data, uint64(c))
return data, nil
}
}
switch v.Kind() {
case reflect.Uint32:
data = binary.NativeEndian.AppendUint32(data, SPA_TYPE_Id)
@ -312,6 +327,16 @@ func unmarshalValue(data []byte, v reflect.Value, wireSizeP *Word) error {
*wireSizeP, err = u.UnmarshalPOD(data)
return err
}
switch v.Interface().(type) {
case Fd:
*wireSizeP = SizeFd
if err := unmarshalCheckTypeBounds(&data, SPA_TYPE_Fd, wireSizeP); err != nil {
return err
}
v.SetInt(int64(binary.NativeEndian.Uint64(data)))
return nil
}
}
switch v.Kind() {

View File

@ -0,0 +1,69 @@
package pipewire
/* pipewire/extensions/security-context.h */
const (
PW_TYPE_INTERFACE_SecurityContext = PW_TYPE_INFO_INTERFACE_BASE + "SecurityContext"
PW_SECURITY_CONTEXT_PERM_MASK = PW_PERM_RWX
PW_VERSION_SECURITY_CONTEXT = 3
PW_EXTENSION_MODULE_SECURITY_CONTEXT = PIPEWIRE_MODULE_PREFIX + "module-security-context"
)
const (
PW_SECURITY_CONTEXT_EVENT_NUM = iota
PW_VERSION_SECURITY_CONTEXT_EVENTS = 0
)
const (
PW_SECURITY_CONTEXT_METHOD_ADD_LISTENER = iota
PW_SECURITY_CONTEXT_METHOD_CREATE
PW_SECURITY_CONTEXT_METHOD_NUM
PW_VERSION_SECURITY_CONTEXT_METHODS = 0
)
// SecurityContextCreate is sent to create a new security context.
//
// Creates a new security context with a socket listening FD.
// PipeWire will accept new client connections on listen_fd.
//
// listen_fd must be ready to accept new connections when this
// request is sent by the client. In other words, the client must
// call bind(2) and listen(2) before sending the FD.
//
// close_fd is a FD closed by the client when PipeWire should stop
// accepting new connections on listen_fd.
//
// PipeWire must continue to accept connections on listen_fd when
// the client which created the security context disconnects.
//
// After sending this request, closing listen_fd and close_fd
// remains the only valid operation on them.
type SecurityContextCreate struct {
// The offset in the SCM_RIGHTS msg_control message to
// the fd to listen on for new connections.
ListenFd Fd
// The offset in the SCM_RIGHTS msg_control message to
// the fd used to stop listening.
CloseFd Fd
// Extra properties. These will be copied on the client
// that connects through this context.
Properties *SPADict `json:"props"`
}
// Size satisfies [KnownSize] with a value computed at runtime.
func (c *SecurityContextCreate) Size() Word {
return SizePrefix +
Size(SizeFd) +
Size(SizeFd) +
c.Properties.Size()
}
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
func (c *SecurityContextCreate) MarshalBinary() ([]byte, error) { return Marshal(c) }
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
func (c *SecurityContextCreate) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }

View File

@ -0,0 +1,21 @@
package pipewire_test
import (
"testing"
"hakurei.app/internal/pipewire"
)
func TestSecurityContextCreate(t *testing.T) {
t.Parallel()
encodingTestCases[pipewire.SecurityContextCreate, *pipewire.SecurityContextCreate]{
{"sample", samplePWContainer[6][0][1], pipewire.SecurityContextCreate{
ListenFd: 1 /* 21: duplicated from listen_fd */, CloseFd: 0, /* 20: duplicated from close_fd */
Properties: &pipewire.SPADict{
{Key: "pipewire.sec.engine", Value: "org.flatpak"},
{Key: "pipewire.access", Value: "restricted"},
},
}, nil},
}.run(t)
}