From 69cc3832d46ce7053543d4b7455984f67bcf1d15 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 9 Feb 2024 10:23:03 +0100 Subject: [PATCH] tools: improve error reporting Add better error reporting in the security context and pw-container. Add manpage for pw-container. --- doc/dox/programs/index.md | 1 + doc/dox/programs/pw-container.1.md | 71 +++++++++++++++++++ doc/meson.build | 1 + .../module-protocol-native/security-context.c | 14 ++-- src/tools/pw-container.c | 55 +++++++++----- 5 files changed, 121 insertions(+), 21 deletions(-) create mode 100644 doc/dox/programs/pw-container.1.md diff --git a/doc/dox/programs/index.md b/doc/dox/programs/index.md index 0f7a3aa25..991ec7263 100644 --- a/doc/dox/programs/index.md +++ b/doc/dox/programs/index.md @@ -7,6 +7,7 @@ Manual pages: - \subpage page_man_pw-cat_1 - \subpage page_man_pw-cli_1 - \subpage page_man_pw-config_1 +- \subpage page_man_pw-container_1 - \subpage page_man_pw-dot_1 - \subpage page_man_pw-dump_1 - \subpage page_man_pw-jack_1 diff --git a/doc/dox/programs/pw-container.1.md b/doc/dox/programs/pw-container.1.md new file mode 100644 index 000000000..ab6e77056 --- /dev/null +++ b/doc/dox/programs/pw-container.1.md @@ -0,0 +1,71 @@ +\page page_man_pw-container_1 pw-container + +The PipeWire container utility + +# SYNOPSIS + +**pw-container** \[*options*\] \[*PROGRAM*\] + +# DESCRIPTION + +Run a program in a new security context [1]. + +**pw-container** will create a new temporary unix socket and uses the +SecurityContext extension API to create a server on this socket with +the given properties. Clients created from this server socket will have +the security properties attached to them. + +This can be used to simulate the behaviour of Flatpak or other containers. + +Without any arguments, **pw-container** simply creates the new socket +and prints the address on stdout. Other PipeWire programs can then be run +with `PIPEWIRE_REMOTE=` to connect through this security +context. + +When *PROGRAM* is given, the `PIPEWIRE_REMOTE` env variable will be set +and *PROGRAM* will be passed to system(). Argument to *PROGRAM* need to be +properly quoted. + +# OPTIONS + +\par -P | \--properties=VALUE +Set extra context properties as a JSON object. + +\par -r | \--remote=NAME +The name the *remote* instance to connect to. If left unspecified, a +connection is made to the default PipeWire instance. + +\par -h | \--help +Show help. + +\par \--version +Show version information. + +# EXIT STATUS + +If the security context was successfuly created, **pw-container** does +not exit until terminated with a signal. It exits with status 0 if terminated by +SIGINT or SIGTERM in this case. + +Otherwise, it exits with nonzero exit status. + +# EXAMPLES + +**pw-container** 'pw-dump i 0' + +Run pw-dump of the Core object. Note the difference in the object permissions +when running pw-dump with and without **pw-container**. + +**pw-container** 'pw-dump pw-dump' + +Run pw-dump of itself. Note the difference in the Client security tokens when +running pw-dump with and without **pw-container**. + +# AUTHORS + +The PipeWire Developers <$(PACKAGE_BUGREPORT)>; +PipeWire is available from <$(PACKAGE_URL)> + +# SEE ALSO + +[1] https://gitlab.freedesktop.org/wayland/wayland-protocols/-/blob/main/staging/security-context/security-context-v1.xml - Creating a security context diff --git a/doc/meson.build b/doc/meson.build index d00d3cde3..5fc4349e7 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -97,6 +97,7 @@ manpage_docs = [ 'dox/programs/pw-cat.1.md', 'dox/programs/pw-cli.1.md', 'dox/programs/pw-config.1.md', + 'dox/programs/pw-container.1.md', 'dox/programs/pw-dot.1.md', 'dox/programs/pw-dump.1.md', 'dox/programs/pw-jack.1.md', diff --git a/src/modules/module-protocol-native/security-context.c b/src/modules/module-protocol-native/security-context.c index a7e958dd8..adf4b227b 100644 --- a/src/modules/module-protocol-native/security-context.c +++ b/src/modules/module-protocol-native/security-context.c @@ -39,9 +39,9 @@ static int security_context_create(void *object, int res = 0; if ((client = impl->context->current_client) == NULL) - goto not_allowed; + goto invalid_state; if (client->protocol != impl->protocol) - goto not_allowed; + goto invalid_state; /* we can't make a nested security context */ p = pw_impl_client_get_properties(client); @@ -49,13 +49,17 @@ static int security_context_create(void *object, goto not_allowed; if (pw_protocol_add_fd_server(impl->protocol, impl->context->core, - listen_fd, close_fd, props) == NULL) + listen_fd, close_fd, props) == NULL) { res = -errno; - + pw_resource_errorf(d->resource, res, "can't add fd server: %m"); + } return res; +invalid_state: + pw_resource_errorf(d->resource, -EIO, "invalid client protocol"); + return -EIO; not_allowed: - pw_log_warn("can't make security context"); + pw_resource_errorf(d->resource, -EPERM, "Nested security context is not allowed"); return -EPERM; } diff --git a/src/tools/pw-container.c b/src/tools/pw-container.c index c1c57e8e6..ac1cb06a6 100644 --- a/src/tools/pw-container.c +++ b/src/tools/pw-container.c @@ -32,8 +32,12 @@ struct data { struct pw_registry *registry; struct spa_hook registry_listener; + struct pw_properties *props; + struct pw_security_context *sec; + int pending_create; + int create_result; int pending; int done; }; @@ -65,6 +69,9 @@ static void on_core_error(void *_data, uint32_t id, int seq, int res, const char pw_log_error("error id:%u seq:%d res:%d (%s): %s", id, seq, res, spa_strerror(res), message); + if (seq == SPA_RESULT_ASYNC_SEQ(data->pending_create)) + data->create_result = res; + if (id == PW_ID_CORE && res == -EPIPE) { data->done = true; pw_main_loop_quit(data->loop); @@ -113,14 +120,19 @@ static void do_quit(void *data, int signal_number) pw_main_loop_quit(d->loop); } -static void show_help(const char *name, bool error) +static void show_help(struct data *d, const char *name, bool error) { - fprintf(error ? stderr : stdout, "%s [options] [application]\n" + FILE *out = error ? stderr : stdout; + fprintf(out, "%s [options] [application]\n" " -h, --help Show this help\n" " --version Show version\n" " -r, --remote Remote daemon name\n" " -P, --properties Context properties\n", name); + fprintf(out, "\nDefault Context properties:\n"); + pw_properties_serialize_dict(out, &d->props->dict, + PW_PROPERTIES_FLAG_NL | PW_PROPERTIES_FLAG_ENCLOSE); + fprintf(out, "\n"); } int main(int argc, char *argv[]) @@ -138,9 +150,8 @@ int main(int argc, char *argv[]) int c, res, listen_fd, close_fd[2]; char temp[PATH_MAX] = "/tmp/pipewire-XXXXXX"; struct sockaddr_un sockaddr = {0}; - struct pw_properties *props; - props = pw_properties_new( + data.props = pw_properties_new( PW_KEY_SEC_ENGINE, "org.flatpak", PW_KEY_ACCESS, "restricted", NULL); @@ -151,7 +162,7 @@ int main(int argc, char *argv[]) while ((c = getopt_long(argc, argv, "hVr:P:", long_options, NULL)) != -1) { switch (c) { case 'h': - show_help(argv[0], false); + show_help(&data, argv[0], false); return 0; case 'V': printf("%s\n" @@ -165,10 +176,10 @@ int main(int argc, char *argv[]) opt_remote = optarg; break; case 'P': - pw_properties_update_string(props, optarg, strlen(optarg)); + pw_properties_update_string(data.props, optarg, strlen(optarg)); break; default: - show_help(argv[0], true); + show_help(&data, argv[0], true); return -1; } } @@ -212,13 +223,13 @@ int main(int argc, char *argv[]) roundtrip(&data); if (data.sec == NULL) { - fprintf(stderr, "no security context object found"); + fprintf(stderr, "no security context object found\n"); return -1; } res = mkstemp(temp); if (res < 0) { - fprintf(stderr, "can't make temp file with template %s: %m", temp); + fprintf(stderr, "can't make temp file with template %s: %m\n", temp); return -1; } close(res); @@ -226,31 +237,43 @@ int main(int argc, char *argv[]) listen_fd = socket(AF_UNIX, SOCK_STREAM, 0); if (listen_fd < 0) { - fprintf(stderr, "can't make unix socket: %m"); + fprintf(stderr, "can't make unix socket: %m\n"); return -1; } sockaddr.sun_family = AF_UNIX; snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s", temp); if (bind(listen_fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr)) != 0) { - fprintf(stderr, "can't bind unix socket to %s: %m", temp); + fprintf(stderr, "can't bind unix socket to %s: %m\n", temp); return -1; } if (listen(listen_fd, 0) != 0) { - fprintf(stderr, "can't listen unix socket: %m"); + fprintf(stderr, "can't listen unix socket: %m\n"); return -1; } res = pipe2(close_fd, O_CLOEXEC); if (res < 0) { - fprintf(stderr, "can't create pipe: %m"); + fprintf(stderr, "can't create pipe: %m\n"); return -1; } setenv("PIPEWIRE_REMOTE", temp, 1); - pw_security_context_create(data.sec, listen_fd, close_fd[1], &props->dict); + data.create_result = 0; + data.pending_create = pw_security_context_create(data.sec, + listen_fd, close_fd[1], &data.props->dict); - roundtrip(&data); + if (SPA_RESULT_IS_ASYNC(data.pending_create)) { + pw_log_debug("create: %d", data.pending_create); + roundtrip(&data); + } + pw_log_debug("create result: %d", data.create_result); + + if (data.create_result < 0) { + fprintf(stderr, "can't create security context: %s\n", + spa_strerror(data.create_result)); + return -1; + } if (optind < argc) { system(argv[optind++]); @@ -266,7 +289,7 @@ int main(int argc, char *argv[]) spa_hook_remove(&data.core_listener); pw_context_destroy(data.context); pw_main_loop_destroy(data.loop); - pw_properties_free(props); + pw_properties_free(data.props); pw_deinit(); return 0;