Skip to content

Commit 7068e75

Browse files
committed
Add dbus list command for easily finding connections on the bus
1 parent bc3d152 commit 7068e75

4 files changed

Lines changed: 514 additions & 6 deletions

File tree

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Then add `register ~/.cargo/bin/nu_plugin_dbus` to your `~/.config/nushell/confi
2020
dbus get - Get a D-Bus property
2121
dbus get-all - Get all D-Bus property for the given objects
2222
dbus introspect - Introspect a D-Bus object
23+
dbus list - List all available connection names on the bus
2324
dbus set - Get all D-Bus property for the given objects
2425

2526
Flags:
@@ -196,3 +197,44 @@ Then add `register ~/.cargo/bin/nu_plugin_dbus` to your `~/.config/nushell/confi
196197
Set the volume of Spotify to 50%
197198
> dbus set --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player Volume 0.5
198199

200+
## `dbus list`
201+
202+
List all available connection names on the bus
203+
204+
These can be used as arguments for --dest on any of the other commands.
205+
206+
Search terms: dbus
207+
208+
Usage:
209+
> dbus list {flags} (pattern)
210+
211+
Flags:
212+
-h, --help - Display the help message for this command
213+
--session - Send to the session message bus (default)
214+
--system - Send to the system message bus
215+
--started - Send to the bus that started this process, if applicable
216+
--bus <String> - Send to the bus server at the given address
217+
--peer <String> - Send to a non-bus D-Bus server at the given address. Will not call the Hello method on initialization.
218+
--timeout <Duration> - How long to wait for a response
219+
220+
Parameters:
221+
pattern <string>: An optional glob-like pattern to filter the result by (optional)
222+
223+
Examples:
224+
List all names available on the bus
225+
> dbus list
226+
227+
List top-level freedesktop.org names on the bus (e.g. matches `org.freedesktop.PowerManagement`, but not `org.freedesktop.Management.Inhibit`)
228+
> dbus list org.freedesktop.*
229+
╭───┬───────────────────────────────╮
230+
│ 0 │ org.freedesktop.DBus │
231+
│ 1 │ org.freedesktop.Flatpak │
232+
│ 2 │ org.freedesktop.Notifications │
233+
╰───┴───────────────────────────────╯
234+
235+
List all MPRIS2 media players on the bus
236+
> dbus list org.mpris.MediaPlayer2.**
237+
╭───┬────────────────────────────────────────────────╮
238+
│ 0 │ org.mpris.MediaPlayer2.spotify │
239+
│ 1 │ org.mpris.MediaPlayer2.kdeconnect.mpris_000001 │
240+
╰───┴────────────────────────────────────────────────╯

src/client.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use dbus::{channel::{Channel, BusType}, Message, arg::messageitem::MessageItem};
22
use nu_plugin::LabeledError;
33
use nu_protocol::{Spanned, Value};
44

5-
use crate::{config::{DbusClientConfig, DbusBusChoice}, dbus_type::DbusType, convert::to_message_item, introspection::Node};
5+
use crate::{config::{DbusClientConfig, DbusBusChoice}, dbus_type::DbusType, convert::to_message_item, introspection::Node, pattern::Pattern};
66

77
/// Executes D-Bus actions on a connection, handling nushell types
88
pub struct DbusClient {
@@ -318,4 +318,30 @@ impl DbusClient {
318318

319319
Ok(())
320320
}
321+
322+
pub fn list(&self, pattern: Option<&Pattern>)
323+
-> Result<Vec<String>, LabeledError>
324+
{
325+
let context = "while listing D-Bus connection names";
326+
327+
let message = Message::new_method_call(
328+
"org.freedesktop.DBus",
329+
"/org/freedesktop/DBus",
330+
"org.freedesktop.DBus",
331+
"ListNames"
332+
).map_err(|err| self.error(err, context))?;
333+
334+
self.conn.send_with_reply_and_block(message, self.config.timeout.item)
335+
.map_err(|err| self.error(err, context))
336+
.and_then(|reply| reply.read1().map_err(|err| self.error(err, context)))
337+
.map(|names: Vec<String>| {
338+
// Filter the names by the pattern
339+
if let Some(pattern) = pattern {
340+
eprintln!("pattern: {:?}", pattern);
341+
names.into_iter().filter(|name| pattern.is_match(name)).collect()
342+
} else {
343+
names
344+
}
345+
})
346+
}
321347
}

src/main.rs

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ mod client;
66
mod convert;
77
mod dbus_type;
88
mod introspection;
9+
mod pattern;
910

1011
use config::*;
1112
use client::*;
1213

14+
use crate::pattern::Pattern;
15+
1316
fn main() {
1417
serve_plugin(&mut NuPluginDbus, MsgPackSerializer)
1518
}
@@ -29,10 +32,10 @@ impl Plugin for NuPluginDbus {
2932
PluginSignature::build("dbus introspect")
3033
.is_dbus_command()
3134
.accepts_dbus_client_options()
35+
.accepts_timeout()
3236
.usage("Introspect a D-Bus object")
3337
.extra_usage("Returns information about available nodes, interfaces, methods, \
3438
signals, and properties on the given object path")
35-
.named("timeout", SyntaxShape::Duration, "How long to wait for a response", None)
3639
.required_named("dest", SyntaxShape::String,
3740
"The name of the connection that owns the object",
3841
None)
@@ -63,9 +66,9 @@ impl Plugin for NuPluginDbus {
6366
PluginSignature::build("dbus call")
6467
.is_dbus_command()
6568
.accepts_dbus_client_options()
69+
.accepts_timeout()
6670
.usage("Call a method and get its response")
6771
.extra_usage("Returns an array if the method call returns more than one value.")
68-
.named("timeout", SyntaxShape::Duration, "How long to wait for a response", None)
6972
.named("signature", SyntaxShape::String,
7073
"Signature of the arguments to send, in D-Bus format.\n \
7174
If not provided, they will be determined from introspection.\n \
@@ -105,8 +108,8 @@ impl Plugin for NuPluginDbus {
105108
PluginSignature::build("dbus get")
106109
.is_dbus_command()
107110
.accepts_dbus_client_options()
111+
.accepts_timeout()
108112
.usage("Get a D-Bus property")
109-
.named("timeout", SyntaxShape::Duration, "How long to wait for a response", None)
110113
.required_named("dest", SyntaxShape::String,
111114
"The name of the connection to read the property from",
112115
None)
@@ -135,8 +138,8 @@ impl Plugin for NuPluginDbus {
135138
PluginSignature::build("dbus get-all")
136139
.is_dbus_command()
137140
.accepts_dbus_client_options()
141+
.accepts_timeout()
138142
.usage("Get all D-Bus property for the given objects")
139-
.named("timeout", SyntaxShape::Duration, "How long to wait for a response", None)
140143
.required_named("dest", SyntaxShape::String,
141144
"The name of the connection to read the property from",
142145
None)
@@ -160,8 +163,8 @@ impl Plugin for NuPluginDbus {
160163
PluginSignature::build("dbus set")
161164
.is_dbus_command()
162165
.accepts_dbus_client_options()
166+
.accepts_timeout()
163167
.usage("Get all D-Bus property for the given objects")
164-
.named("timeout", SyntaxShape::Duration, "How long to wait for a response", None)
165168
.named("signature", SyntaxShape::String,
166169
"Signature of the value to set, in D-Bus format.\n \
167170
If not provided, it will be determined from introspection.\n \
@@ -187,6 +190,40 @@ impl Plugin for NuPluginDbus {
187190
result: None,
188191
},
189192
]),
193+
PluginSignature::build("dbus list")
194+
.is_dbus_command()
195+
.accepts_dbus_client_options()
196+
.accepts_timeout()
197+
.usage("List all available connection names on the bus")
198+
.extra_usage("These can be used as arguments for --dest on any of the other commands.")
199+
.optional("pattern", SyntaxShape::String,
200+
"An optional glob-like pattern to filter the result by")
201+
.plugin_examples(vec![
202+
PluginExample {
203+
example: "dbus list".into(),
204+
description: "List all names available on the bus".into(),
205+
result: None,
206+
},
207+
PluginExample {
208+
example: "dbus list org.freedesktop.*".into(),
209+
description: "List top-level freedesktop.org names on the bus \
210+
(e.g. matches `org.freedesktop.PowerManagement`, \
211+
but not `org.freedesktop.Management.Inhibit`)".into(),
212+
result: Some(Value::list(vec![
213+
str!("org.freedesktop.DBus"),
214+
str!("org.freedesktop.Flatpak"),
215+
str!("org.freedesktop.Notifications"),
216+
], Span::unknown())),
217+
},
218+
PluginExample {
219+
example: "dbus list org.mpris.MediaPlayer2.**".into(),
220+
description: "List all MPRIS2 media players on the bus".into(),
221+
result: Some(Value::list(vec![
222+
str!("org.mpris.MediaPlayer2.spotify"),
223+
str!("org.mpris.MediaPlayer2.kdeconnect.mpris_000001"),
224+
], Span::unknown())),
225+
},
226+
])
190227
]
191228
}
192229

@@ -208,6 +245,7 @@ impl Plugin for NuPluginDbus {
208245
"dbus get" => self.get(call),
209246
"dbus get-all" => self.get_all(call),
210247
"dbus set" => self.set(call),
248+
"dbus list" => self.list(call),
211249

212250
_ => Err(LabeledError {
213251
label: "Plugin invoked with unknown command name".into(),
@@ -222,6 +260,7 @@ impl Plugin for NuPluginDbus {
222260
trait DbusSignatureUtilExt {
223261
fn is_dbus_command(self) -> Self;
224262
fn accepts_dbus_client_options(self) -> Self;
263+
fn accepts_timeout(self) -> Self;
225264
}
226265

227266
impl DbusSignatureUtilExt for PluginSignature {
@@ -240,6 +279,10 @@ impl DbusSignatureUtilExt for PluginSignature {
240279
Will not call the Hello method on initialization.",
241280
None)
242281
}
282+
283+
fn accepts_timeout(self) -> Self {
284+
self.named("timeout", SyntaxShape::Duration, "How long to wait for a response", None)
285+
}
243286
}
244287

245288
impl NuPluginDbus {
@@ -310,4 +353,14 @@ impl NuPluginDbus {
310353
)?;
311354
Ok(Value::nothing(call.head))
312355
}
356+
357+
fn list(&self, call: &EvaluatedCall) -> Result<Value, LabeledError> {
358+
let config = DbusClientConfig::try_from(call)?;
359+
let dbus = DbusClient::new(config)?;
360+
let pattern = call.opt::<String>(0)?.map(|pat| Pattern::new(&pat, Some('.')));
361+
let result = dbus.list(pattern.as_ref())?;
362+
Ok(Value::list(
363+
result.into_iter().map(|s| Value::string(s, call.head)).collect(),
364+
call.head))
365+
}
313366
}

0 commit comments

Comments
 (0)