xwayland: do not use setenv() after fork()
Between fork() and exec() in the child process it is only safe to use async-signal-safe functions. Painfully, setenv() is not listed as such. Therefore we must craft our own custom environment, and we get no help from libc with that. Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
This commit is contained in:
committed by
Pekka Paalanen
parent
d1b01ffb9a
commit
1bd92dac01
+92
-3
@@ -126,6 +126,91 @@ fdstr_close_all(struct fdstr *s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct custom_env {
|
||||||
|
struct wl_array p;
|
||||||
|
bool finalized;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
custom_env_init_from_environ(struct custom_env *env)
|
||||||
|
{
|
||||||
|
char **it;
|
||||||
|
char **ep;
|
||||||
|
|
||||||
|
wl_array_init(&env->p);
|
||||||
|
env->finalized = false;
|
||||||
|
|
||||||
|
for (it = environ; *it; it++) {
|
||||||
|
ep = wl_array_add(&env->p, sizeof *ep);
|
||||||
|
assert(ep);
|
||||||
|
*ep = strdup(*it);
|
||||||
|
assert(*ep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
custom_env_fini(struct custom_env *env)
|
||||||
|
{
|
||||||
|
char **ep;
|
||||||
|
|
||||||
|
wl_array_for_each(ep, &env->p)
|
||||||
|
free(*ep);
|
||||||
|
|
||||||
|
wl_array_release(&env->p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char **
|
||||||
|
custom_env_find_element(struct custom_env *env, const char *name)
|
||||||
|
{
|
||||||
|
char **ep;
|
||||||
|
size_t name_len = strlen(name);
|
||||||
|
|
||||||
|
wl_array_for_each(ep, &env->p) {
|
||||||
|
char *entry = *ep;
|
||||||
|
|
||||||
|
if (strncmp(entry, name, name_len) == 0 &&
|
||||||
|
entry[name_len] == '=') {
|
||||||
|
return ep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
custom_env_set(struct custom_env *env, const char *name, const char *value)
|
||||||
|
{
|
||||||
|
char **ep;
|
||||||
|
|
||||||
|
assert(strchr(name, '=') == NULL);
|
||||||
|
assert(!env->finalized);
|
||||||
|
|
||||||
|
ep = custom_env_find_element(env, name);
|
||||||
|
if (ep)
|
||||||
|
free(*ep);
|
||||||
|
else
|
||||||
|
ep = wl_array_add(&env->p, sizeof *ep);
|
||||||
|
assert(ep);
|
||||||
|
|
||||||
|
str_printf(ep, "%s=%s", name, value);
|
||||||
|
assert(*ep);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *const *
|
||||||
|
custom_env_get_envp(struct custom_env *env)
|
||||||
|
{
|
||||||
|
char **ep;
|
||||||
|
|
||||||
|
/* add terminating NULL */
|
||||||
|
ep = wl_array_add(&env->p, sizeof *ep);
|
||||||
|
assert(ep);
|
||||||
|
*ep = NULL;
|
||||||
|
|
||||||
|
env->finalized = true;
|
||||||
|
|
||||||
|
return env->p.data;
|
||||||
|
}
|
||||||
|
|
||||||
static pid_t
|
static pid_t
|
||||||
spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd)
|
spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd)
|
||||||
{
|
{
|
||||||
@@ -141,6 +226,8 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd
|
|||||||
struct weston_config_section *section;
|
struct weston_config_section *section;
|
||||||
struct wl_event_loop *loop;
|
struct wl_event_loop *loop;
|
||||||
char *exec_failure_msg;
|
char *exec_failure_msg;
|
||||||
|
struct custom_env child_env;
|
||||||
|
char *const *envp;
|
||||||
bool ret;
|
bool ret;
|
||||||
|
|
||||||
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wayland_socket.fds) < 0) {
|
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wayland_socket.fds) < 0) {
|
||||||
@@ -169,6 +256,9 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd
|
|||||||
&xserver, XSERVER_PATH);
|
&xserver, XSERVER_PATH);
|
||||||
str_printf(&exec_failure_msg,
|
str_printf(&exec_failure_msg,
|
||||||
"Error: executing Xwayland as '%s' failed.\n", xserver);
|
"Error: executing Xwayland as '%s' failed.\n", xserver);
|
||||||
|
custom_env_init_from_environ(&child_env);
|
||||||
|
custom_env_set(&child_env, "WAYLAND_SOCKET", wayland_socket.str1);
|
||||||
|
envp = custom_env_get_envp(&child_env);
|
||||||
|
|
||||||
const char *const argv[] = {
|
const char *const argv[] = {
|
||||||
xserver,
|
xserver,
|
||||||
@@ -195,9 +285,7 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd
|
|||||||
if (!ret)
|
if (!ret)
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
|
|
||||||
setenv("WAYLAND_SOCKET", wayland_socket.str1, 1);
|
if (execve(xserver, (char *const *)argv, envp) < 0) {
|
||||||
|
|
||||||
if (execv(xserver, (char *const *)argv) < 0) {
|
|
||||||
if (exec_failure_msg) {
|
if (exec_failure_msg) {
|
||||||
write(STDERR_FILENO, exec_failure_msg,
|
write(STDERR_FILENO, exec_failure_msg,
|
||||||
strlen(exec_failure_msg));
|
strlen(exec_failure_msg));
|
||||||
@@ -237,6 +325,7 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
custom_env_fini(&child_env);
|
||||||
free(exec_failure_msg);
|
free(exec_failure_msg);
|
||||||
free(xserver);
|
free(xserver);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user