mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
tools: improve error reporting
Add better error reporting in the security context and pw-container. Add manpage for pw-container.
This commit is contained in:
parent
81181e255f
commit
69cc3832d4
5 changed files with 121 additions and 21 deletions
|
|
@ -7,6 +7,7 @@ Manual pages:
|
||||||
- \subpage page_man_pw-cat_1
|
- \subpage page_man_pw-cat_1
|
||||||
- \subpage page_man_pw-cli_1
|
- \subpage page_man_pw-cli_1
|
||||||
- \subpage page_man_pw-config_1
|
- \subpage page_man_pw-config_1
|
||||||
|
- \subpage page_man_pw-container_1
|
||||||
- \subpage page_man_pw-dot_1
|
- \subpage page_man_pw-dot_1
|
||||||
- \subpage page_man_pw-dump_1
|
- \subpage page_man_pw-dump_1
|
||||||
- \subpage page_man_pw-jack_1
|
- \subpage page_man_pw-jack_1
|
||||||
|
|
|
||||||
71
doc/dox/programs/pw-container.1.md
Normal file
71
doc/dox/programs/pw-container.1.md
Normal file
|
|
@ -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=<socket-address>` 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
|
||||||
|
|
@ -97,6 +97,7 @@ manpage_docs = [
|
||||||
'dox/programs/pw-cat.1.md',
|
'dox/programs/pw-cat.1.md',
|
||||||
'dox/programs/pw-cli.1.md',
|
'dox/programs/pw-cli.1.md',
|
||||||
'dox/programs/pw-config.1.md',
|
'dox/programs/pw-config.1.md',
|
||||||
|
'dox/programs/pw-container.1.md',
|
||||||
'dox/programs/pw-dot.1.md',
|
'dox/programs/pw-dot.1.md',
|
||||||
'dox/programs/pw-dump.1.md',
|
'dox/programs/pw-dump.1.md',
|
||||||
'dox/programs/pw-jack.1.md',
|
'dox/programs/pw-jack.1.md',
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,9 @@ static int security_context_create(void *object,
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
if ((client = impl->context->current_client) == NULL)
|
if ((client = impl->context->current_client) == NULL)
|
||||||
goto not_allowed;
|
goto invalid_state;
|
||||||
if (client->protocol != impl->protocol)
|
if (client->protocol != impl->protocol)
|
||||||
goto not_allowed;
|
goto invalid_state;
|
||||||
|
|
||||||
/* we can't make a nested security context */
|
/* we can't make a nested security context */
|
||||||
p = pw_impl_client_get_properties(client);
|
p = pw_impl_client_get_properties(client);
|
||||||
|
|
@ -49,13 +49,17 @@ static int security_context_create(void *object,
|
||||||
goto not_allowed;
|
goto not_allowed;
|
||||||
|
|
||||||
if (pw_protocol_add_fd_server(impl->protocol, impl->context->core,
|
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;
|
res = -errno;
|
||||||
|
pw_resource_errorf(d->resource, res, "can't add fd server: %m");
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
invalid_state:
|
||||||
|
pw_resource_errorf(d->resource, -EIO, "invalid client protocol");
|
||||||
|
return -EIO;
|
||||||
not_allowed:
|
not_allowed:
|
||||||
pw_log_warn("can't make security context");
|
pw_resource_errorf(d->resource, -EPERM, "Nested security context is not allowed");
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,12 @@ struct data {
|
||||||
struct pw_registry *registry;
|
struct pw_registry *registry;
|
||||||
struct spa_hook registry_listener;
|
struct spa_hook registry_listener;
|
||||||
|
|
||||||
|
struct pw_properties *props;
|
||||||
|
|
||||||
struct pw_security_context *sec;
|
struct pw_security_context *sec;
|
||||||
|
|
||||||
|
int pending_create;
|
||||||
|
int create_result;
|
||||||
int pending;
|
int pending;
|
||||||
int done;
|
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",
|
pw_log_error("error id:%u seq:%d res:%d (%s): %s",
|
||||||
id, seq, res, spa_strerror(res), message);
|
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) {
|
if (id == PW_ID_CORE && res == -EPIPE) {
|
||||||
data->done = true;
|
data->done = true;
|
||||||
pw_main_loop_quit(data->loop);
|
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);
|
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"
|
" -h, --help Show this help\n"
|
||||||
" --version Show version\n"
|
" --version Show version\n"
|
||||||
" -r, --remote Remote daemon name\n"
|
" -r, --remote Remote daemon name\n"
|
||||||
" -P, --properties Context properties\n",
|
" -P, --properties Context properties\n",
|
||||||
name);
|
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[])
|
int main(int argc, char *argv[])
|
||||||
|
|
@ -138,9 +150,8 @@ int main(int argc, char *argv[])
|
||||||
int c, res, listen_fd, close_fd[2];
|
int c, res, listen_fd, close_fd[2];
|
||||||
char temp[PATH_MAX] = "/tmp/pipewire-XXXXXX";
|
char temp[PATH_MAX] = "/tmp/pipewire-XXXXXX";
|
||||||
struct sockaddr_un sockaddr = {0};
|
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_SEC_ENGINE, "org.flatpak",
|
||||||
PW_KEY_ACCESS, "restricted",
|
PW_KEY_ACCESS, "restricted",
|
||||||
NULL);
|
NULL);
|
||||||
|
|
@ -151,7 +162,7 @@ int main(int argc, char *argv[])
|
||||||
while ((c = getopt_long(argc, argv, "hVr:P:", long_options, NULL)) != -1) {
|
while ((c = getopt_long(argc, argv, "hVr:P:", long_options, NULL)) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
show_help(argv[0], false);
|
show_help(&data, argv[0], false);
|
||||||
return 0;
|
return 0;
|
||||||
case 'V':
|
case 'V':
|
||||||
printf("%s\n"
|
printf("%s\n"
|
||||||
|
|
@ -165,10 +176,10 @@ int main(int argc, char *argv[])
|
||||||
opt_remote = optarg;
|
opt_remote = optarg;
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
pw_properties_update_string(props, optarg, strlen(optarg));
|
pw_properties_update_string(data.props, optarg, strlen(optarg));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
show_help(argv[0], true);
|
show_help(&data, argv[0], true);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -212,13 +223,13 @@ int main(int argc, char *argv[])
|
||||||
roundtrip(&data);
|
roundtrip(&data);
|
||||||
|
|
||||||
if (data.sec == NULL) {
|
if (data.sec == NULL) {
|
||||||
fprintf(stderr, "no security context object found");
|
fprintf(stderr, "no security context object found\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = mkstemp(temp);
|
res = mkstemp(temp);
|
||||||
if (res < 0) {
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
close(res);
|
close(res);
|
||||||
|
|
@ -226,31 +237,43 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
if (listen_fd < 0) {
|
if (listen_fd < 0) {
|
||||||
fprintf(stderr, "can't make unix socket: %m");
|
fprintf(stderr, "can't make unix socket: %m\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sockaddr.sun_family = AF_UNIX;
|
sockaddr.sun_family = AF_UNIX;
|
||||||
snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s", temp);
|
snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s", temp);
|
||||||
if (bind(listen_fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr)) != 0) {
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
if (listen(listen_fd, 0) != 0) {
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = pipe2(close_fd, O_CLOEXEC);
|
res = pipe2(close_fd, O_CLOEXEC);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
fprintf(stderr, "can't create pipe: %m");
|
fprintf(stderr, "can't create pipe: %m\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
setenv("PIPEWIRE_REMOTE", temp, 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) {
|
if (optind < argc) {
|
||||||
system(argv[optind++]);
|
system(argv[optind++]);
|
||||||
|
|
@ -266,7 +289,7 @@ int main(int argc, char *argv[])
|
||||||
spa_hook_remove(&data.core_listener);
|
spa_hook_remove(&data.core_listener);
|
||||||
pw_context_destroy(data.context);
|
pw_context_destroy(data.context);
|
||||||
pw_main_loop_destroy(data.loop);
|
pw_main_loop_destroy(data.loop);
|
||||||
pw_properties_free(props);
|
pw_properties_free(data.props);
|
||||||
pw_deinit();
|
pw_deinit();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue