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:
parent
d1b01ffb9a
commit
1bd92dac01
|
@ -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
|
||||
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 wl_event_loop *loop;
|
||||
char *exec_failure_msg;
|
||||
struct custom_env child_env;
|
||||
char *const *envp;
|
||||
bool ret;
|
||||
|
||||
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);
|
||||
str_printf(&exec_failure_msg,
|
||||
"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[] = {
|
||||
xserver,
|
||||
|
@ -195,9 +285,7 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd
|
|||
if (!ret)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
setenv("WAYLAND_SOCKET", wayland_socket.str1, 1);
|
||||
|
||||
if (execv(xserver, (char *const *)argv) < 0) {
|
||||
if (execve(xserver, (char *const *)argv, envp) < 0) {
|
||||
if (exec_failure_msg) {
|
||||
write(STDERR_FILENO, 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;
|
||||
}
|
||||
|
||||
custom_env_fini(&child_env);
|
||||
free(exec_failure_msg);
|
||||
free(xserver);
|
||||
|
||||
|
|
Loading…
Reference in New Issue