diff --git a/man/busctl.xml b/man/busctl.xml
index 47a20e8c4c..0034c15b73 100644
--- a/man/busctl.xml
+++ b/man/busctl.xml
@@ -75,7 +75,7 @@
to or from this peer, identified by its well-known or unique
name. Otherwise, show all messages on the bus. Use
CtrlC
- to terminate the dump.
+ to terminate the dump or limit it with the option.
@@ -147,9 +147,10 @@
waitSERVICEOBJECTINTERFACESIGNAL
- Wait for a signal. Takes an object path, interface name, and signal name. To suppress
- output of the returned data, use the option. The service name may be
- omitted, in which case busctl will match signals from any sender.
+ Wait for a signal. Takes an object path, interface name, and signal name. Use the
+ option to wait for more than just one signal before exiting. To
+ suppress output of the returned data, use the option. The service name may
+ be omitted, in which case busctl will match signals from any sender.
@@ -398,7 +399,14 @@
When used with the monitor command, if enabled will make
busctl exit when the specified number of messages have been received and
printed. This is useful in combination with , to wait for the specified
- number of occurrences of specific D-Bus messages.
+ number of occurrences of specific D-Bus messages. While if not specified or is set to special value
+ infinity, the default is to keep monitoring without limitations.
+
+ When used with the wait command, it will make busctl
+ exit when the specified number of DBus signals have been received and printed. Setting it to
+ special value infinity will sustain busctl to receive and
+ print signals continuously without limitations. When nothing is specified, the default is to exit
+ immediately after receiving and printing the first signal.
diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c
index 62ee0a1484..8c523dc02b 100644
--- a/src/busctl/busctl.c
+++ b/src/busctl/busctl.c
@@ -72,6 +72,7 @@ static bool arg_watch_bind = false;
static usec_t arg_timeout = 0;
static const char *arg_destination = NULL;
static uint64_t arg_limit_messages = UINT64_MAX;
+static uint64_t arg_limit_signals = 1;
STATIC_DESTRUCTOR_REGISTER(arg_matches, strv_freep);
@@ -1931,8 +1932,24 @@ static int get_property(int argc, char **argv, void *userdata) {
}
static int on_bus_signal(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error) {
- return sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(ASSERT_PTR(msg))),
- bus_message_dump(msg, /* flags= */ 0));
+ int r;
+
+ r = bus_message_dump(msg, /* flags= */ 0);
+ if (r < 0)
+ return sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(ASSERT_PTR(msg))), r);
+
+ if (arg_limit_signals != UINT64_MAX) {
+ arg_limit_signals--;
+
+ if (arg_limit_signals == 0) {
+ if (!arg_quiet && !sd_json_format_enabled(arg_json_format_flags))
+ log_info("Received requested maximum number of signals, exiting.");
+
+ return sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(ASSERT_PTR(msg))), 0);
+ }
+ }
+
+ return 0;
}
static int wait_signal(int argc, char **argv, void *userdata) {
@@ -2353,7 +2370,14 @@ static int parse_argv(int argc, char *argv[]) {
case 'N':
if (isempty(optarg)) {
- arg_limit_messages = UINT64_MAX; /* Reset to default */
+ /* Reset to default */
+ arg_limit_messages = UINT64_MAX;
+ arg_limit_signals = 1;
+ break;
+ }
+
+ if (streq(optarg, "infinity")) {
+ arg_limit_signals = arg_limit_messages = UINT64_MAX;
break;
}
@@ -2363,6 +2387,7 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_limit_messages == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--limit-messages= parameter cannot be 0");
+ arg_limit_signals = arg_limit_messages;
break;
case '?':
diff --git a/test/units/TEST-74-AUX-UTILS.busctl.sh b/test/units/TEST-74-AUX-UTILS.busctl.sh
index 60e27d3f87..b8ae8da568 100755
--- a/test/units/TEST-74-AUX-UTILS.busctl.sh
+++ b/test/units/TEST-74-AUX-UTILS.busctl.sh
@@ -57,10 +57,27 @@ busctl emit --auto-start=no --destination=systemd-logind.service \
/org/freedesktop/login1 org.freedesktop.login1.Manager \
PrepareForShutdown b false
+signal_emit_command() {
+ echo "for i in \$(seq 1 ${1:?}); do busctl emit /test org.freedesktop.fake1 TestSignal s success; done"
+}
+
systemd-run --quiet --service-type=notify --unit=test-busctl-wait --pty \
-p Environment=SYSTEMD_LOG_LEVEL=debug \
- -p ExecStartPost="busctl emit /test org.freedesktop.fake1 TestSignal s success" \
- busctl --timeout=30 wait /test org.freedesktop.fake1 TestSignal | grep -F 's "success"' >/dev/null
+ -p ExecStartPost="sh -c '$(signal_emit_command 1)'" \
+ busctl --timeout=30 wait /test org.freedesktop.fake1 TestSignal |
+ grep -Fc 's "success"' | xargs test 1 -eq
+
+systemd-run --quiet --service-type=notify --unit=test-busctl-wait-limited --pty \
+ -p Environment=SYSTEMD_LOG_LEVEL=debug \
+ -p ExecStartPost="sh -c '$(signal_emit_command 5)'" \
+ busctl --timeout=30 --limit-messages=3 wait /test org.freedesktop.fake1 TestSignal |
+ grep -Fc 's "success"' | xargs test 3 -eq
+
+systemd-run --quiet --service-type=notify --unit=test-busctl-wait-unlimited --pty \
+ -p Environment=SYSTEMD_LOG_LEVEL=debug \
+ -p ExecStartPost="sh -c '$(signal_emit_command 2)'" \
+ busctl --timeout=3 --limit-messages=infinity wait /test org.freedesktop.fake1 TestSignal |
+ grep -Fc 's "success"' | xargs test 2 -eq
busctl get-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager \
Version