diff --git a/man/busctl.xml b/man/busctl.xml index a5e3d92cf0..04f05294f9 100644 --- a/man/busctl.xml +++ b/man/busctl.xml @@ -187,7 +187,7 @@ BOOL - When used with the call command, specifies + When used with the call or emit command, specifies whether the method call should implicitly activate the called service, should it not be running yet but is configured to be auto-started. Defaults to @@ -247,6 +247,15 @@ + + SERVICE + + + Takes a service name. When used with the emit command, a signal is + emitted to the specified service. + + + @@ -341,6 +350,15 @@ option. + + emit OBJECT INTERFACE SIGNAL SIGNATURE ARGUMENT + + Emit a signal. Takes a object path, interface name and method name. If parameters + shall be passed, a signature string is required, followed by the arguments, individually formatted as + strings. For details on the formatting used, see below. To specify the destination of the signal, + use the option. + + get-property SERVICE OBJECT INTERFACE PROPERTY diff --git a/shell-completion/bash/busctl b/shell-completion/bash/busctl index 476101c24c..63a7644cbf 100644 --- a/shell-completion/bash/busctl +++ b/shell-completion/bash/busctl @@ -86,7 +86,8 @@ _busctl() { -q --quiet --verbose --expect-reply=no --auto-start=no --allow-interactive-authorization=no --augment-creds=no --watch-bind=yes -j' - [ARG]='--address -H --host -M --machine --match --timeout --size --json' + [ARG]='--address -H --host -M --machine --match --timeout --size --json + --destination' ) if __contains_word "--user" ${COMP_WORDS[*]}; then @@ -106,6 +107,9 @@ _busctl() { --json) comps=$( busctl --json=help 2>/dev/null ) ;; + --destination) + comps=$( __get_busnames $mode ) + ;; esac COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) return 0 @@ -121,6 +125,7 @@ _busctl() { [BUSNAME]='status monitor capture tree' [OBJECT]='introspect' [METHOD]='call' + [EMIT]='emit' [PROPERTY_GET]='get-property' [PROPERTY_SET]='set-property' ) @@ -165,6 +170,8 @@ _busctl() { else comps='' fi + elif __contains_word "$verb" ${VERBS[EMIT]}; then + comps='' elif __contains_word "$verb" ${VERBS[PROPERTY_GET]}; then if [[ $n -eq 1 ]] ; then comps=$( __get_busnames $mode) diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index 08d9e70c96..a61fd2e706 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -57,6 +57,7 @@ static bool arg_allow_interactive_authorization = true; static bool arg_augment_creds = true; static bool arg_watch_bind = false; static usec_t arg_timeout = 0; +static const char *arg_destination = NULL; STATIC_DESTRUCTOR_REGISTER(arg_matches, strv_freep); @@ -795,10 +796,8 @@ static int on_interface(const char *interface, uint64_t flags, void *userdata) { return log_oom(); r = set_put(members, m); - if (r <= 0) { - log_error("Duplicate interface"); - return -EINVAL; - } + if (r <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate interface"); m = NULL; return 0; @@ -836,10 +835,8 @@ static int on_method(const char *interface, const char *name, const char *signat return log_oom(); r = set_put(members, m); - if (r <= 0) { - log_error("Duplicate method"); - return -EINVAL; - } + if (r <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate method"); m = NULL; return 0; @@ -873,10 +870,8 @@ static int on_signal(const char *interface, const char *name, const char *signat return log_oom(); r = set_put(members, m); - if (r <= 0) { - log_error("Duplicate signal"); - return -EINVAL; - } + if (r <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate signal"); m = NULL; return 0; @@ -911,10 +906,8 @@ static int on_property(const char *interface, const char *name, const char *sign return log_oom(); r = set_put(members, m); - if (r <= 0) { - log_error("Duplicate property"); - return -EINVAL; - } + if (r <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate property"); m = NULL; return 0; @@ -1195,10 +1188,8 @@ static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f STRV_FOREACH(i, argv+1) { _cleanup_free_ char *m = NULL; - if (!service_name_is_valid(*i)) { - log_error("Invalid service name '%s'", *i); - return -EINVAL; - } + if (!service_name_is_valid(*i)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid service name '%s'", *i); m = strjoin("sender='", *i, "'"); if (!m) @@ -1978,10 +1969,8 @@ static int call(int argc, char **argv, void *userdata) { if (r < 0) return r; - if (*p) { - log_error("Too many parameters for signature."); - return -EINVAL; - } + if (*p) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many parameters for signature."); } if (!arg_expect_reply) { @@ -2036,6 +2025,49 @@ static int call(int argc, char **argv, void *userdata) { return 0; } +static int emit_signal(int argc, char **argv, void *userdata) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + r = acquire_bus(false, &bus); + if (r < 0) + return r; + + r = sd_bus_message_new_signal(bus, &m, argv[1], argv[2], argv[3]); + if (r < 0) + return bus_log_create_error(r); + + if (arg_destination) { + r = sd_bus_message_set_destination(m, arg_destination); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_set_auto_start(m, arg_auto_start); + if (r < 0) + return bus_log_create_error(r); + + if (!isempty(argv[4])) { + char **p; + + p = argv+5; + + r = message_append_cmdline(m, argv[4], &p); + if (r < 0) + return r; + + if (*p) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many parameters for signature."); + } + + r = sd_bus_send(bus, m, NULL); + if (r < 0) + return log_error_errno(r, "Failed to send signal: %m"); + + return 0; +} + static int get_property(int argc, char **argv, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -2132,10 +2164,8 @@ static int set_property(int argc, char **argv, void *userdata) { if (r < 0) return bus_log_create_error(r); - if (*p) { - log_error("Too many parameters for signature."); - return -EINVAL; - } + if (*p) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many parameters for signature."); r = sd_bus_call(bus, m, arg_timeout, &error, NULL); if (r < 0) @@ -2154,48 +2184,51 @@ static int help(void) { printf("%s [OPTIONS...] {COMMAND} ...\n\n" "Introspect the bus.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --no-pager Do not pipe output into a pager\n" - " --no-legend Do not show the headers and footers\n" - " --system Connect to system bus\n" - " --user Connect to user bus\n" - " -H --host=[USER@]HOST Operate on remote host\n" - " -M --machine=CONTAINER Operate on local container\n" - " --address=ADDRESS Connect to bus specified by address\n" - " --show-machine Show machine ID column in list\n" - " --unique Only show unique names\n" - " --acquired Only show acquired names\n" - " --activatable Only show activatable names\n" - " --match=MATCH Only show matching messages\n" - " --size=SIZE Maximum length of captured packet\n" - " --list Don't show tree, but simple object path list\n" - " -q --quiet Don't show method call reply\n" - " --verbose Show result values in long format\n" - " --json=MODE Output as JSON\n" - " -j Same as --json=pretty on tty, --json=short otherwise\n" - " --expect-reply=BOOL Expect a method call reply\n" - " --auto-start=BOOL Auto-start destination service\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " --no-legend Do not show the headers and footers\n" + " --system Connect to system bus\n" + " --user Connect to user bus\n" + " -H --host=[USER@]HOST Operate on remote host\n" + " -M --machine=CONTAINER Operate on local container\n" + " --address=ADDRESS Connect to bus specified by address\n" + " --show-machine Show machine ID column in list\n" + " --unique Only show unique names\n" + " --acquired Only show acquired names\n" + " --activatable Only show activatable names\n" + " --match=MATCH Only show matching messages\n" + " --size=SIZE Maximum length of captured packet\n" + " --list Don't show tree, but simple object path list\n" + " -q --quiet Don't show method call reply\n" + " --verbose Show result values in long format\n" + " --json=MODE Output as JSON\n" + " -j Same as --json=pretty on tty, --json=short otherwise\n" + " --expect-reply=BOOL Expect a method call reply\n" + " --auto-start=BOOL Auto-start destination service\n" " --allow-interactive-authorization=BOOL\n" - " Allow interactive authorization for operation\n" - " --timeout=SECS Maximum time to wait for method call completion\n" - " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n" - " --watch-bind=BOOL Wait for bus AF_UNIX socket to be bound in the file\n" - " system\n\n" - "Commands:\n" - " list List bus names\n" - " status [SERVICE] Show bus service, process or bus owner credentials\n" - " monitor [SERVICE...] Show bus traffic\n" - " capture [SERVICE...] Capture bus traffic as pcap\n" - " tree [SERVICE...] Show object tree of service\n" + " Allow interactive authorization for operation\n" + " --timeout=SECS Maximum time to wait for method call completion\n" + " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n" + " --watch-bind=BOOL Wait for bus AF_UNIX socket to be bound in the file\n" + " system\n" + " --destination=SERVICE Destination service of a signal\n" + "\nCommands:\n" + " list List bus names\n" + " status [SERVICE] Show bus service, process or bus owner credentials\n" + " monitor [SERVICE...] Show bus traffic\n" + " capture [SERVICE...] Capture bus traffic as pcap\n" + " tree [SERVICE...] Show object tree of service\n" " introspect SERVICE OBJECT [INTERFACE]\n" " call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n" - " Call a method\n" + " Call a method\n" + " emit OBJECT INTERFACE SIGNAL [SIGNATURE [ARGUMENT...]]\n" + " Emit a signal\n" " get-property SERVICE OBJECT INTERFACE PROPERTY...\n" - " Get property value\n" + " Get property value\n" " set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n" - " Set property value\n" - " help Show this help\n" + " Set property value\n" + " help Show this help\n" "\nSee the %s for details.\n" , program_invocation_short_name , link @@ -2232,6 +2265,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_AUGMENT_CREDS, ARG_WATCH_BIND, ARG_JSON, + ARG_DESTINATION, }; static const struct option options[] = { @@ -2260,6 +2294,7 @@ static int parse_argv(int argc, char *argv[]) { { "augment-creds", required_argument, NULL, ARG_AUGMENT_CREDS }, { "watch-bind", required_argument, NULL, ARG_WATCH_BIND }, { "json", required_argument, NULL, ARG_JSON }, + { "destination", required_argument, NULL, ARG_DESTINATION }, {}, }; @@ -2426,6 +2461,10 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_DESTINATION: + arg_destination = optarg; + break; + case '?': return -EINVAL; @@ -2446,6 +2485,7 @@ static int busctl_main(int argc, char *argv[]) { { "tree", VERB_ANY, VERB_ANY, 0, tree }, { "introspect", 3, 4, 0, introspect }, { "call", 5, VERB_ANY, 0, call }, + { "emit", 4, VERB_ANY, 0, emit_signal }, { "get-property", 5, VERB_ANY, 0, get_property }, { "set-property", 6, VERB_ANY, 0, set_property }, { "help", VERB_ANY, VERB_ANY, 0, verb_help },