8 Commits

Author SHA1 Message Date
ccc0d98bd7 release: 0.3.2
All checks were successful
Release / Create release (push) Successful in 48s
Test / Create distribution (push) Successful in 28s
Test / Sandbox (push) Successful in 42s
Test / Sandbox (race detector) (push) Successful in 41s
Test / Hpkg (push) Successful in 44s
Test / Hakurei (race detector) (push) Successful in 7m4s
Test / Hakurei (push) Successful in 4m2s
Test / Flake checks (push) Successful in 1m40s
Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-12-09 08:12:52 +09:00
a3fd05765e container: load initial process started before syscall
All checks were successful
Test / Create distribution (push) Successful in 27s
Test / Sandbox (push) Successful in 2m50s
Test / Sandbox (race detector) (push) Successful in 5m3s
Test / Hpkg (push) Successful in 5m21s
Test / Hakurei (push) Successful in 5m47s
Test / Hakurei (race detector) (push) Successful in 7m7s
Test / Flake checks (push) Successful in 1m39s
This avoids a race between returning from syscall and checking the state.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-12-09 08:12:22 +09:00
c538df7daa internal/pipewire: expose connection props
All checks were successful
Test / Create distribution (push) Successful in 29s
Test / Sandbox (push) Successful in 2m53s
Test / Sandbox (race detector) (push) Successful in 4m47s
Test / Hpkg (push) Successful in 5m10s
Test / Hakurei (race detector) (push) Successful in 6m29s
Test / Hakurei (push) Successful in 45s
Test / Flake checks (push) Successful in 1m37s
Unused in hakurei but could be useful when the package is moved out of internal.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-12-09 06:51:12 +09:00
44e5aa1a36 internal/pipewire: include remaining size in recvmsg wrapper
All checks were successful
Test / Create distribution (push) Successful in 35s
Test / Sandbox (push) Successful in 2m36s
Test / Sandbox (race detector) (push) Successful in 4m44s
Test / Hpkg (push) Successful in 4m44s
Test / Hakurei (race detector) (push) Successful in 6m20s
Test / Hakurei (push) Successful in 3m35s
Test / Flake checks (push) Successful in 1m22s
This otherwise truncates the received data by len(remaining) bytes.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-12-09 06:36:46 +09:00
cf0e7d8c27 internal/pipewire: reset per-roundtrip state once per call
All checks were successful
Test / Create distribution (push) Successful in 36s
Test / Sandbox (push) Successful in 2m38s
Test / Sandbox (race detector) (push) Successful in 4m40s
Test / Hakurei (push) Successful in 5m1s
Test / Hpkg (push) Successful in 4m58s
Test / Hakurei (race detector) (push) Successful in 6m22s
Test / Flake checks (push) Successful in 1m21s
This was left in consume when relocating per-roundtrip code out of the per-receive consume method.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-12-09 06:28:33 +09:00
130add21e5 internal/pipewire: increment remote sequence after establishing bounds
All checks were successful
Test / Create distribution (push) Successful in 35s
Test / Sandbox (push) Successful in 2m39s
Test / Hakurei (push) Successful in 4m45s
Test / Sandbox (race detector) (push) Successful in 4m47s
Test / Hpkg (push) Successful in 4m53s
Test / Hakurei (race detector) (push) Successful in 6m41s
Test / Flake checks (push) Successful in 1m39s
This avoids incrementing it twice proceeding from a partial message.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-12-09 06:21:41 +09:00
5ec4045e24 internal/pipewire: do not clobber error parsing SCMs
All checks were successful
Test / Create distribution (push) Successful in 35s
Test / Sandbox (push) Successful in 2m35s
Test / Sandbox (race detector) (push) Successful in 4m43s
Test / Hakurei (push) Successful in 4m50s
Test / Hpkg (push) Successful in 4m54s
Test / Hakurei (race detector) (push) Successful in 6m29s
Test / Flake checks (push) Successful in 1m21s
The error is handled later, clobbering it here breaks error handling when SCMs are present.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-12-09 06:02:15 +09:00
be2075f169 Revert "internal/pipewire: work around remote sequence quirk"
All checks were successful
Test / Create distribution (push) Successful in 37s
Test / Sandbox (push) Successful in 2m38s
Test / Hakurei (push) Successful in 4m44s
Test / Sandbox (race detector) (push) Successful in 4m50s
Test / Hpkg (push) Successful in 4m52s
Test / Hakurei (race detector) (push) Successful in 6m28s
Test / Flake checks (push) Successful in 1m21s
This reverts commit 564db6863b.
2025-12-09 05:25:41 +09:00
3 changed files with 25 additions and 20 deletions

View File

@@ -380,6 +380,9 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
err error err error
wpid = -2 wpid = -2
wstatus WaitStatus wstatus WaitStatus
// whether initial process has started
started bool
) )
// keep going until no child process is left // keep going until no child process is left
@@ -406,6 +409,10 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
} }
} }
if !started {
started = initialProcessStarted.Load()
}
err = EINTR err = EINTR
for errors.Is(err, EINTR) { for errors.Is(err, EINTR) {
wpid, err = k.wait4(-1, &wstatus, 0, nil) wpid, err = k.wait4(-1, &wstatus, 0, nil)
@@ -414,7 +421,7 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
if !errors.Is(err, ECHILD) { if !errors.Is(err, ECHILD) {
k.printf(msg, "unexpected wait4 response: %v", err) k.printf(msg, "unexpected wait4 response: %v", err)
} else if !initialProcessStarted.Load() { } else if !started {
// initial process has not yet been reached and all daemons // initial process has not yet been reached and all daemons
// terminated or none were started in the first place // terminated or none were started in the first place
time.Sleep(500 * time.Microsecond) time.Sleep(500 * time.Microsecond)

View File

@@ -63,7 +63,7 @@ type Context struct {
generation Long generation Long
// Pending file descriptors to be sent with the next message. // Pending file descriptors to be sent with the next message.
pendingFiles []int pendingFiles []int
// File count kept track of in [Header]. // File count already kept track of in [Header].
headerFiles int headerFiles int
// Files from the server. This is discarded on every Roundtrip so eventProxy // Files from the server. This is discarded on every Roundtrip so eventProxy
// implementations must make sure to close them to avoid leaking fds. // implementations must make sure to close them to avoid leaking fds.
@@ -271,16 +271,20 @@ func (ctx *Context) recvmsg(remaining []byte) (payload []byte, err error) {
n, oobn, recvflags, err = ctx.conn.Recvmsg(ctx.iovecBuf[len(remaining):], ctx.oobBuf[:], recvmsgFlags) n, oobn, recvflags, err = ctx.conn.Recvmsg(ctx.iovecBuf[len(remaining):], ctx.oobBuf[:], recvmsgFlags)
if oob := ctx.oobBuf[:oobn]; len(oob) > 0 { if oob := ctx.oobBuf[:oobn]; len(oob) > 0 {
var oobErr error
var scm []syscall.SocketControlMessage var scm []syscall.SocketControlMessage
if scm, err = syscall.ParseSocketControlMessage(oob); err != nil { if scm, oobErr = syscall.ParseSocketControlMessage(oob); oobErr != nil {
ctx.closeReceivedFiles() ctx.closeReceivedFiles()
err = oobErr
return return
} }
var fds []int var fds []int
for i := range scm { for i := range scm {
if fds, err = syscall.ParseUnixRights(&scm[i]); err != nil { if fds, oobErr = syscall.ParseUnixRights(&scm[i]); oobErr != nil {
ctx.closeReceivedFiles() ctx.closeReceivedFiles()
err = oobErr
return return
} }
ctx.receivedFiles = append(ctx.receivedFiles, fds...) ctx.receivedFiles = append(ctx.receivedFiles, fds...)
@@ -310,7 +314,7 @@ func (ctx *Context) recvmsg(remaining []byte) (payload []byte, err error) {
err = syscall.EPIPE // not wrapped as it did not come from the syscall err = syscall.EPIPE // not wrapped as it did not come from the syscall
} }
if n > 0 { if n > 0 {
payload = ctx.iovecBuf[:n] payload = ctx.iovecBuf[:len(remaining)+n]
} }
return return
} }
@@ -581,6 +585,9 @@ func (ctx *Context) roundtrip() (err error) {
if err = ctx.sendmsg(ctx.buf, ctx.pendingFiles...); err != nil { if err = ctx.sendmsg(ctx.buf, ctx.pendingFiles...); err != nil {
return return
} }
ctx.buf = ctx.buf[:0]
ctx.pendingFiles = ctx.pendingFiles[:0]
ctx.headerFiles = 0
defer func() { defer func() {
var danglingFiles DanglingFilesError var danglingFiles DanglingFilesError
@@ -657,10 +664,6 @@ func (ctx *Context) consume(receiveRemaining []byte) (remaining []byte, err erro
return return
}() }()
ctx.buf = ctx.buf[:0]
ctx.pendingFiles = ctx.pendingFiles[:0]
ctx.headerFiles = 0
if remaining, err = ctx.recvmsg(receiveRemaining); err != nil { if remaining, err = ctx.recvmsg(receiveRemaining); err != nil {
return return
} }
@@ -674,20 +677,14 @@ func (ctx *Context) consume(receiveRemaining []byte) (remaining []byte, err erro
if err = header.UnmarshalBinary(remaining[:SizeHeader]); err != nil { if err = header.UnmarshalBinary(remaining[:SizeHeader]); err != nil {
return return
} }
// remote sequence sometimes do not start with 0
if ctx.remoteSequence == 0 {
ctx.remoteSequence = header.Sequence
}
if header.Sequence != ctx.remoteSequence { if header.Sequence != ctx.remoteSequence {
return remaining, UnexpectedSequenceError(header.Sequence) return remaining, UnexpectedSequenceError(header.Sequence)
} }
ctx.remoteSequence++
if len(remaining) < int(SizeHeader+header.Size) { if len(remaining) < int(SizeHeader+header.Size) {
return return
} }
ctx.remoteSequence++
proxy, ok := ctx.proxy[header.ID] proxy, ok := ctx.proxy[header.ID]
if !ok { if !ok {
@@ -823,8 +820,7 @@ func connectName(name string, manager bool) (conn *net.UnixConn, err error) {
} }
// ConnectName connects to a PipeWire remote by name. // ConnectName connects to a PipeWire remote by name.
func ConnectName(name string, manager bool) (ctx *Context, err error) { func ConnectName(name string, manager bool, props SPADict) (ctx *Context, err error) {
var props SPADict
if manager { if manager {
props = append(props, SPADictItem{Key: PW_KEY_REMOTE_INTENTION, Value: "manager"}) props = append(props, SPADictItem{Key: PW_KEY_REMOTE_INTENTION, Value: "manager"})
} }
@@ -850,4 +846,6 @@ func ConnectName(name string, manager bool) (ctx *Context, err error) {
} }
// Connect connects to the PipeWire remote. // Connect connects to the PipeWire remote.
func Connect(manager bool) (ctx *Context, err error) { return ConnectName("", manager) } func Connect(manager bool, props SPADict) (ctx *Context, err error) {
return ConnectName("", manager, props)
}

View File

@@ -89,7 +89,7 @@ func (k direct) waylandNew(displayPath, bindPath *check.Absolute, appID, instanc
return wayland.New(displayPath, bindPath, appID, instanceID) return wayland.New(displayPath, bindPath, appID, instanceID)
} }
func (k direct) pipewireConnect() (*pipewire.Context, error) { return pipewire.Connect(true) } func (k direct) pipewireConnect() (*pipewire.Context, error) { return pipewire.Connect(true, nil) }
func (k direct) xcbChangeHosts(mode xcb.HostMode, family xcb.Family, address string) error { func (k direct) xcbChangeHosts(mode xcb.HostMode, family xcb.Family, address string) error {
return xcb.ChangeHosts(mode, family, address) return xcb.ChangeHosts(mode, family, address)