Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
fd4c5f6
Update package manifest to Swift 6 and PureSwift/Socket
colemancda Aug 3, 2026
e8b9ff1
Update resolved dependencies
colemancda Aug 3, 2026
b1f5ffd
Remove dbus_bool_t conversions
colemancda Aug 3, 2026
ce68d60
Remove libdbus timeout wrapper
colemancda Aug 3, 2026
a138e49
Remove libdbus dispatch status
colemancda Aug 3, 2026
d844896
Remove unused libdbus handler result
colemancda Aug 3, 2026
962038d
Remove libdbus message iterator
colemancda Aug 3, 2026
95ba9e3
Remove libdbus pending call
colemancda Aug 3, 2026
d763f1b
Remove LinuxMain.swift
colemancda Aug 3, 2026
1a4f7ad
Implement type code properties in Swift
colemancda Aug 3, 2026
2792145
Implement signature parsing without libdbus
colemancda Aug 3, 2026
34ec3ae
Implement object path parsing without libdbus
colemancda Aug 3, 2026
1a14d7b
Implement interface name parsing without libdbus
colemancda Aug 3, 2026
cbac818
Implement member name parsing without libdbus
colemancda Aug 3, 2026
cf294de
Implement bus name parsing without libdbus
colemancda Aug 3, 2026
bf05af8
Replace libdbus error reference with Swift error types
colemancda Aug 3, 2026
6b2d8d1
Add variant and dictionary argument types
colemancda Aug 3, 2026
e48b7b6
Use UInt8 raw values for message type
colemancda Aug 3, 2026
ab662ca
Add byte order and type alignment
colemancda Aug 3, 2026
58a8612
Add wire format encoder
colemancda Aug 3, 2026
9b6e65f
Add wire format decoder
colemancda Aug 3, 2026
32faeb0
Add message header encoding and framing
colemancda Aug 3, 2026
760cb57
Rewrite message as a value type
colemancda Aug 3, 2026
4eba7d5
Rewrite connection as an actor
colemancda Aug 3, 2026
9c4ef73
Add environment and credential access
colemancda Aug 3, 2026
f9d9b6b
Add bus address parsing
colemancda Aug 3, 2026
0f58f59
Add Unix socket address with abstract namespace support
colemancda Aug 3, 2026
749071f
Add SASL authentication
colemancda Aug 3, 2026
df35cb4
Add match rules
colemancda Aug 3, 2026
178231a
Add bus daemon API
colemancda Aug 3, 2026
5d64bee
Add signal subscriptions
colemancda Aug 3, 2026
c14388d
Add exported object model
colemancda Aug 3, 2026
6b384d4
Add object export and method dispatch
colemancda Aug 3, 2026
863a157
Add Peer, Introspectable and Properties
colemancda Aug 3, 2026
24b0f8a
Add test support helpers
colemancda Aug 3, 2026
52b10ab
Migrate signature tests to Swift Testing
colemancda Aug 3, 2026
a820ccc
Migrate object path tests to Swift Testing
colemancda Aug 3, 2026
b5713c5
Migrate interface tests to Swift Testing
colemancda Aug 3, 2026
e1e349b
Add member and bus name tests
colemancda Aug 3, 2026
f84801f
Add wire format tests
colemancda Aug 3, 2026
384d904
Rewrite message tests for the new API
colemancda Aug 3, 2026
a53d18d
Add bus address tests
colemancda Aug 3, 2026
9bda751
Add SASL handshake tests
colemancda Aug 3, 2026
467262b
Add match rule tests
colemancda Aug 3, 2026
1ab2e24
Add live connection tests
colemancda Aug 3, 2026
b2e482c
Add introspection and object server tests
colemancda Aug 3, 2026
0d72436
Add live signal tests
colemancda Aug 3, 2026
595ef40
Update README
colemancda Aug 3, 2026
a4c1b82
Depend on the Socket branch with descriptor passing
colemancda Aug 3, 2026
9bce141
Update resolved dependencies
colemancda Aug 3, 2026
83a68eb
Add SHA-1
colemancda Aug 3, 2026
66dc372
Add keyring and cookie challenge handling
colemancda Aug 3, 2026
bc3f3fd
Add DBUS_COOKIE_SHA1 authentication
colemancda Aug 3, 2026
beafcb0
Add user name and home directory lookup
colemancda Aug 3, 2026
8c87495
Add TCP and nonce-TCP transports
colemancda Aug 3, 2026
aad8636
Marshal file descriptors as indices
colemancda Aug 3, 2026
3be41a9
Resolve file descriptor indices when decoding
colemancda Aug 3, 2026
5884300
Carry file descriptors alongside encoded messages
colemancda Aug 3, 2026
98999d2
Connect over TCP and pass file descriptors
colemancda Aug 3, 2026
6b56987
Add a minimal XML reader
colemancda Aug 3, 2026
1fe000b
Parse introspection XML into a node model
colemancda Aug 3, 2026
379ed90
Add SHA-1, keyring and cookie authentication tests
colemancda Aug 3, 2026
1c76048
Add TCP transport tests
colemancda Aug 3, 2026
f300b1b
Add file descriptor passing tests
colemancda Aug 3, 2026
601c708
Add introspection parsing tests
colemancda Aug 3, 2026
674f3c8
Thread file descriptors through the marshalling tests
colemancda Aug 3, 2026
41daf11
Serialize the tests that open sockets
colemancda Aug 3, 2026
194f905
Update README
colemancda Aug 3, 2026
91012b8
Depend on the Socket release with descriptor passing
colemancda Aug 7, 2026
a93c690
Update resolved dependencies
colemancda Aug 7, 2026
a4c60ba
Merge branch 'master' into pure-swift
colemancda Aug 7, 2026
9dc6bcb
Resolve TCP addresses on Darwin
colemancda Aug 7, 2026
d6c2fee
Import the platform C library portably in the descriptor tests
colemancda Aug 7, 2026
c3e70eb
Spawn the TCP test daemon portably
colemancda Aug 7, 2026
3ba5cda
Add macOS and Linux continuous integration
colemancda Aug 7, 2026
6f20bc7
Add portable UTF-8 validation
colemancda Aug 7, 2026
4319dee
Validate decoded strings portably
colemancda Aug 7, 2026
6e2496b
Validate authentication lines portably
colemancda Aug 7, 2026
bb77e85
Validate hex digests portably
colemancda Aug 7, 2026
aafa8d7
Validate keyring contents portably
colemancda Aug 7, 2026
a0d627a
Validate address components portably
colemancda Aug 7, 2026
0eb0876
Validate property strings portably
colemancda Aug 7, 2026
528236b
Run continuous integration on push only
colemancda Aug 7, 2026
0922c3a
Depend on the Socket branch with the socket teardown fixes
colemancda Aug 7, 2026
dc13645
Update resolved dependencies
colemancda Aug 7, 2026
acd1a50
Give macOS a session bus on a Unix socket
colemancda Aug 7, 2026
a3bb0bd
Build the test daemon environment portably
colemancda Aug 7, 2026
179be1d
Build for macOS and run the tests on Linux
colemancda Aug 7, 2026
d5c017e
Depend on the Socket branch with the readiness fix
colemancda Aug 7, 2026
fd3f7a6
Update resolved dependencies
colemancda Aug 7, 2026
2844207
Enable testing when building the tests in release
colemancda Aug 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Swift
on: [push]
jobs:

# macOS is a compile target only, on both architectures: macos-15 is Apple
# Silicon and macos-15-intel is the x86_64 image that replaced the retired
# macos-13. The test suite needs a session bus and D-Bus is not part of macOS,
# so the suite is run on Linux, where a real bus is available. The tests are
# still *built* here, so they cannot rot out of compiling on Darwin.
macos:
name: macOS (${{ matrix.arch }}, ${{ matrix.config }})
strategy:
# One failing cell should not hide the others: knowing which
# architecture and configuration combinations fail is the whole
# point of the matrix when diagnosing platform-specific behavior.
fail-fast: false
matrix:
arch: ["arm64", "x86_64"]
config: ["debug", "release"]
include:
- arch: arm64
runner: macos-15
- arch: x86_64
runner: macos-15-intel
runs-on: ${{ matrix.runner }}
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Swift Version
run: swift --version
- name: Build
run: swift build -c ${{ matrix.config }}
- name: Build tests
# `-enable-testing` is on by default only for debug. The tests use `@testable import`,
# and `swift test` passes the flag itself, but a plain build in release does not.
run: swift build -c ${{ matrix.config }} --build-tests -Xswiftc -enable-testing

# x86_64 on the standard hosted runner, arm64 on ubuntu-24.04-arm, which is a
# real ARM64 machine rather than a cross-compilation target, so the suite runs
# there too. Docker pulls the matching architecture of the same Swift images.
linux:
name: Linux (${{ matrix.arch }}, ${{ matrix.container }}, ${{ matrix.config }}, ${{ matrix.options || 'static' }})
strategy:
fail-fast: false
matrix:
arch: ["x86_64", "arm64"]
container: ["swift:6.2.3", "swift:6.3.3"]
config: ["debug", "release"]
options: ["", "SWIFT_BUILD_DYNAMIC_LIBRARY=1"]
include:
- arch: x86_64
runner: ubuntu-latest
- arch: arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
container: ${{ matrix.container }}-jammy
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Swift Version
run: swift --version
- name: Install D-Bus
run: apt-get update -qq && apt-get install -y --no-install-recommends dbus
- name: Build
run: ${{ matrix.options }} swift build -c ${{ matrix.config }}
- name: Test
run: dbus-run-session -- env ${{ matrix.options }} swift test -c ${{ matrix.config }} --no-parallel
34 changes: 21 additions & 13 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 15 additions & 9 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
// swift-tools-version:4.1
// swift-tools-version:6.0
import PackageDescription
import class Foundation.ProcessInfo

// force building as dynamic library
let dynamicLibrary = ProcessInfo.processInfo.environment["SWIFT_BUILD_DYNAMIC_LIBRARY"] != nil
let libraryType: PackageDescription.Product.Library.LibraryType? = dynamicLibrary ? .dynamic : nil

let package = Package(
name: "DBus",
platforms: [
.macOS(.v13),
],
products: [
.library(
name: "DBus",
targets: [
"DBus"
]
type: libraryType,
targets: ["DBus"]
)
],
dependencies: [
.package(
url: "https://github.com/PureSwift/CDBus.git",
.branch("master")
url: "https://github.com/PureSwift/Socket.git",
branch: "fix/stale-readiness"
)
],
targets: [
.target(
name: "DBus",
dependencies: [
//"CDBus"
"Socket"
]
),
.testTarget(
Expand All @@ -30,6 +37,5 @@ let package = Package(
"DBus"
]
)
],
swiftLanguageVersions: [5]
]
)
152 changes: 151 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,152 @@
# DBus
Swift library for D-Bus

Pure Swift library for D-Bus. No `libdbus`, no C implementation — the wire format, the SASL
handshake and the transport are all implemented in Swift.

## Requirements

- Swift 6.0+
- Linux (or any platform with a D-Bus daemon reachable over a Unix socket)

Depends on [PureSwift/Socket](https://github.com/PureSwift/Socket) for async sockets.

> The manifest currently points at a local `../Socket` checkout, which carries three changes not
> yet upstream: `SCM_RIGHTS` ancillary data support that file-descriptor passing is built on, and
> two socket-monitor fixes — deferred poll results being applied to a reused descriptor, and a
> never-connected socket being torn down because an unconnected socket polls as `POLLHUP`.

## Usage

```swift
import DBus

let connection = try await DBusConnection.connect(to: .session)

let reply = try await connection.send(
DBusMessage(methodCall: DBusMessage.MethodCall(
destination: DBusBusName(rawValue: "org.freedesktop.DBus")!,
path: DBusObjectPath(rawValue: "/org/freedesktop/DBus")!,
interface: DBusInterface(rawValue: "org.freedesktop.DBus")!,
method: DBusMember(rawValue: "ListNames")!
))
)

if case let .array(names)? = reply.arguments.first {
for name in names.compactMap({ $0.stringValue }) {
print(name)
}
}

await connection.close()
```

An error reply is thrown as a `DBusError` carrying its `org.freedesktop.DBus.Error.*` name;
framing and marshalling failures are thrown as `DBusProtocolError`.

### File descriptors

A `UNIX_FD` argument carries a real descriptor. On the wire it is marshalled as an index into
the descriptors sent out of band, so the value you pass is the descriptor itself:

```swift
try await connection.callMethod(
destination: name,
path: path,
interface: interface,
method: DBusMember(rawValue: "Accept")!,
arguments: [.fileDescriptor(.init(rawValue: myFileDescriptor))]
)
```

The peer receives its own descriptor referring to the same open file, and **owns it**: close it
when finished. Sending requires the peer to have agreed to `NEGOTIATE_UNIX_FD`, which
`unixFileDescriptorsSupported` reports.

### Signals

A connection receives no broadcast signals until it installs a match rule. `signals(matching:)`
installs one and yields matching messages until the stream is dropped, which removes it again.

```swift
let signals = try await connection.signals(matching: .nameOwnerChanged())

for await signal in signals {
print(signal.arguments)
}
```

### Exporting an object

```swift
let counter = DBusInterfaceImplementation(
name: DBusInterface(rawValue: "com.example.Counter")!,
methods: [
.init(name: DBusMember(rawValue: "Increment")!,
outputSignature: DBusSignature(rawValue: "u")!,
handler: { _ in [.uint32(await state.increment())] })
],
properties: [
.init(name: "Total", type: .uint32, access: .read,
get: { .uint32(await state.total) })
]
)

await connection.export(DBusExportedObject([counter]),
at: DBusObjectPath(rawValue: "/com/example/Counter")!)

try await connection.requestName(DBusBusName(rawValue: "com.example.Counter")!)
```

`org.freedesktop.DBus.Peer`, `.Introspectable` and `.Properties` are answered automatically:
introspection XML is generated from the declared methods, properties and signals.

## Design

`DBusMessage` and every value type are `Sendable` structs. `DBusConnection` is an actor that owns
the socket, runs the read loop and matches replies to calls by serial. Nothing wraps a C pointer,
so there is no reference counting to get wrong.

Values are modelled by `DBusMessageArgument`, which covers every D-Bus type including `variant`
and `dict`. Array element types are stored explicitly rather than inferred, so an empty array
still marshals with the right signature.

Names are validated on construction by hand-written parsers: `DBusObjectPath`, `DBusInterface`,
`DBusMember`, `DBusBusName` and `DBusSignature`.

## Status

Implemented:

- Message marshalling and unmarshalling, both byte orders
- Bus address parsing (`unix:path=`, `unix:abstract=`, `unix:runtime=yes`, percent escaping)
- Unix socket transport, including the Linux abstract namespace
- SASL `EXTERNAL` and `ANONYMOUS`, plus `NEGOTIATE_UNIX_FD`
- Method calls and replies, with timeouts
- Bus daemon API: name registration and queries, `AddMatch` / `RemoveMatch`
- Match rules, with spec-correct encoding and local matching
- Signal subscriptions as `AsyncStream`
- Server side: object export, method dispatch, signal emission
- `org.freedesktop.DBus.Peer`, `.Introspectable` and `.Properties`
- SASL `DBUS_COOKIE_SHA1`, with a pure-Swift SHA-1
- `tcp:` and `nonce-tcp:` transports, IPv4 and IPv6
- Unix file descriptor passing, via `SCM_RIGHTS`

- Parsing introspection XML into a typed node model

Not yet implemented:

- Code generation from introspection XML

## Tests

```sh
swift test
```

Written with [Swift Testing](https://github.com/swiftlang/swift-testing). The suites that need a
live bus are marked `.enabled(if: hasSessionBus)`, so they report as skipped rather than passing
when no bus socket is present.

## License

MIT. See [LICENSE](LICENSE).
Loading
Loading