Skip to content
Commits on Source (10)
......@@ -355,10 +355,9 @@ sigchld_handler(int signal_number, void *data)
break;
}
if (&p->link == &wet->child_process_list) {
weston_log("unknown child process exited\n");
/* An unknown child process exited. Oh well. */
if (&p->link == &wet->child_process_list)
continue;
}
wl_list_remove(&p->link);
wl_list_init(&p->link);
......@@ -388,44 +387,30 @@ cleanup_for_child_process() {
sigprocmask(SIG_UNBLOCK, &allsigs, NULL);
}
WL_EXPORT struct wl_client *
WL_EXPORT bool
weston_client_launch(struct weston_compositor *compositor,
struct weston_process *proc,
const char *path,
struct custom_env *child_env,
int *no_cloexec_fds,
size_t num_no_cloexec_fds,
weston_process_cleanup_func_t cleanup)
{
struct wl_client *client = NULL;
struct custom_env child_env;
struct fdstr wayland_socket;
const char *fail_cloexec = "Couldn't unset CLOEXEC on client socket";
const char *fail_cloexec = "Couldn't unset CLOEXEC on child FDs";
const char *fail_seteuid = "Couldn't call seteuid";
char *fail_exec;
char * const *argp;
char * const *envp;
pid_t pid;
int err;
bool ret;
size_t i;
size_t written __attribute__((unused));
weston_log("launching '%s'\n", path);
str_printf(&fail_exec, "Error: Couldn't launch client '%s'\n", path);
argp = custom_env_get_argp(child_env);
envp = custom_env_get_envp(child_env);
custom_env_init_from_environ(&child_env);
custom_env_add_from_exec_string(&child_env, path);
if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0,
wayland_socket.fds) < 0) {
weston_log("weston_client_launch: "
"socketpair failed while launching '%s': %s\n",
path, strerror(errno));
custom_env_fini(&child_env);
return NULL;
}
fdstr_update_str1(&wayland_socket);
custom_env_set_env_var(&child_env, "WAYLAND_SOCKET",
wayland_socket.str1);
argp = custom_env_get_argp(&child_env);
envp = custom_env_get_envp(&child_env);
weston_log("launching '%s'\n", argp[0]);
str_printf(&fail_exec, "Error: Couldn't launch client '%s'\n", argp[0]);
pid = fork();
switch (pid) {
......@@ -439,11 +424,13 @@ weston_client_launch(struct weston_compositor *compositor,
_exit(EXIT_FAILURE);
}
ret = fdstr_clear_cloexec_fd1(&wayland_socket);
if (!ret) {
written = write(STDERR_FILENO, fail_cloexec,
strlen(fail_cloexec));
_exit(EXIT_FAILURE);
for (i = 0; i < num_no_cloexec_fds; i++) {
err = os_fd_clear_cloexec(no_cloexec_fds[i]);
if (err < 0) {
written = write(STDERR_FILENO, fail_cloexec,
strlen(fail_cloexec));
_exit(EXIT_FAILURE);
}
}
execve(argp[0], argp, envp);
......@@ -454,36 +441,23 @@ weston_client_launch(struct weston_compositor *compositor,
_exit(EXIT_FAILURE);
default:
close(wayland_socket.fds[1]);
client = wl_client_create(compositor->wl_display,
wayland_socket.fds[0]);
if (!client) {
custom_env_fini(&child_env);
close(wayland_socket.fds[0]);
free(fail_exec);
weston_log("weston_client_launch: "
"wl_client_create failed while launching '%s'.\n",
path);
return NULL;
}
proc->pid = pid;
proc->cleanup = cleanup;
wet_watch_process(compositor, proc);
ret = true;
break;
case -1:
fdstr_close_all(&wayland_socket);
weston_log("weston_client_launch: "
"fork failed while launching '%s': %s\n", path,
"fork failed while launching '%s': %s\n", argp[0],
strerror(errno));
ret = false;
break;
}
custom_env_fini(&child_env);
custom_env_fini(child_env);
free(fail_exec);
return client;
return ret;
}
WL_EXPORT void
......@@ -529,6 +503,11 @@ weston_client_start(struct weston_compositor *compositor, const char *path)
{
struct process_info *pinfo;
struct wl_client *client;
struct custom_env child_env;
struct fdstr wayland_socket = FDSTR_INIT;
int no_cloexec_fds[1];
size_t num_no_cloexec_fds = 0;
bool ret;
pinfo = zalloc(sizeof *pinfo);
if (!pinfo)
......@@ -538,18 +517,51 @@ weston_client_start(struct weston_compositor *compositor, const char *path)
if (!pinfo->path)
goto out_free;
client = weston_client_launch(compositor, &pinfo->proc, path,
process_handle_sigchld);
if (!client)
goto out_str;
if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0,
wayland_socket.fds) < 0) {
weston_log("weston_client_start: "
"socketpair failed while launching '%s': %s\n",
path, strerror(errno));
goto out_path;
}
custom_env_init_from_environ(&child_env);
custom_env_add_from_exec_string(&child_env, path);
fdstr_update_str1(&wayland_socket);
no_cloexec_fds[num_no_cloexec_fds++] = wayland_socket.fds[1];
custom_env_set_env_var(&child_env, "WAYLAND_SOCKET",
wayland_socket.str1);
assert(num_no_cloexec_fds <= ARRAY_LENGTH(no_cloexec_fds));
ret = weston_client_launch(compositor, &pinfo->proc, &child_env,
no_cloexec_fds, num_no_cloexec_fds,
process_handle_sigchld);
if (!ret)
goto out_path;
client = wl_client_create(compositor->wl_display,
wayland_socket.fds[0]);
if (!client) {
weston_log("weston_client_start: "
"wl_client_create failed while launching '%s'.\n",
path);
/* We have no way of killing the process, so leave it hanging */
goto out_sock;
}
/* Close the child end of our socket which we no longer need */
close(wayland_socket.fds[1]);
return client;
out_str:
out_path:
free(pinfo->path);
out_free:
free(pinfo);
out_sock:
fdstr_close_all(&wayland_socket);
return NULL;
}
......
......@@ -36,17 +36,18 @@
struct screenshooter {
struct weston_compositor *ec;
struct wl_client *client;
struct weston_process process;
struct wl_listener destroy_listener;
struct wl_listener client_destroy_listener;
struct wl_listener compositor_destroy_listener;
struct weston_recorder *recorder;
struct wl_listener authorization;
};
static void
screenshooter_sigchld(struct weston_process *process, int status)
screenshooter_client_destroy(struct wl_listener *listener, void *data)
{
struct screenshooter *shooter =
container_of(process, struct screenshooter, process);
container_of(listener, struct screenshooter,
client_destroy_listener);
shooter->client = NULL;
}
......@@ -58,6 +59,9 @@ screenshooter_binding(struct weston_keyboard *keyboard,
struct screenshooter *shooter = data;
char *screenshooter_exe;
/* Don't start a screenshot whilst we already have one in progress */
if (shooter->client)
return;
screenshooter_exe = wet_get_bindir_path("weston-screenshooter");
if (!screenshooter_exe) {
......@@ -65,11 +69,16 @@ screenshooter_binding(struct weston_keyboard *keyboard,
return;
}
if (!shooter->client)
shooter->client = weston_client_launch(shooter->ec,
&shooter->process,
screenshooter_exe, screenshooter_sigchld);
shooter->client = weston_client_start(shooter->ec,
screenshooter_exe);
free(screenshooter_exe);
if (!shooter->client)
return;
shooter->client_destroy_listener.notify = screenshooter_client_destroy;
wl_client_add_destroy_listener(shooter->client,
&shooter->client_destroy_listener);
}
static void
......@@ -111,9 +120,10 @@ static void
screenshooter_destroy(struct wl_listener *listener, void *data)
{
struct screenshooter *shooter =
container_of(listener, struct screenshooter, destroy_listener);
container_of(listener, struct screenshooter,
compositor_destroy_listener);
wl_list_remove(&shooter->destroy_listener.link);
wl_list_remove(&shooter->compositor_destroy_listener.link);
wl_list_remove(&shooter->authorization.link);
free(shooter);
......@@ -135,8 +145,9 @@ screenshooter_create(struct weston_compositor *ec)
weston_compositor_add_key_binding(ec, KEY_R, MODIFIER_SUPER,
recorder_binding, shooter);
shooter->destroy_listener.notify = screenshooter_destroy;
wl_signal_add(&ec->destroy_signal, &shooter->destroy_listener);
shooter->compositor_destroy_listener.notify = screenshooter_destroy;
wl_signal_add(&ec->destroy_signal,
&shooter->compositor_destroy_listener);
weston_compositor_add_screenshot_authority(ec, &shooter->authorization,
authorize_screenshooter);
......
......@@ -46,10 +46,14 @@ struct weston_process {
struct wl_list link;
};
struct wl_client *
struct custom_env;
bool
weston_client_launch(struct weston_compositor *compositor,
struct weston_process *proc,
const char *path,
struct custom_env *custom_env,
int *fds_no_cloexec,
size_t num_fds_no_cloexec,
weston_process_cleanup_func_t cleanup);
struct wl_client *
......
......@@ -91,53 +91,67 @@ out:
return 0;
}
static void
xserver_cleanup(struct weston_process *process, int status)
{
struct wet_xwayland *wxw =
container_of(process, struct wet_xwayland, process);
wxw->api->xserver_exited(wxw->xwayland, status);
wxw->client = NULL;
}
static pid_t
spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd)
{
struct wet_xwayland *wxw = user_data;
pid_t pid;
struct fdstr wayland_socket;
struct fdstr x11_abstract_socket;
struct fdstr x11_unix_socket;
struct fdstr x11_wm_socket;
struct fdstr display_pipe;
struct fdstr wayland_socket = FDSTR_INIT;
struct fdstr x11_abstract_socket = FDSTR_INIT;
struct fdstr x11_unix_socket = FDSTR_INIT;
struct fdstr x11_wm_socket = FDSTR_INIT;
struct fdstr display_pipe = FDSTR_INIT;
char *xserver = NULL;
struct weston_config *config = wet_get_config(wxw->compositor);
struct weston_config_section *section;
struct wl_event_loop *loop;
char *exec_failure_msg;
struct custom_env child_env;
char *const *envp;
char *const *argp;
bool ret;
int no_cloexec_fds[5];
size_t num_no_cloexec_fds = 0;
int ret;
size_t written __attribute__ ((unused));
if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, wayland_socket.fds) < 0) {
weston_log("wl connection socketpair failed\n");
return 1;
goto err;
}
fdstr_update_str1(&wayland_socket);
no_cloexec_fds[num_no_cloexec_fds++] = wayland_socket.fds[1];
if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, x11_wm_socket.fds) < 0) {
weston_log("X wm connection socketpair failed\n");
return 1;
goto err;
}
fdstr_update_str1(&x11_wm_socket);
no_cloexec_fds[num_no_cloexec_fds++] = x11_wm_socket.fds[1];
if (pipe2(display_pipe.fds, O_CLOEXEC) < 0) {
weston_log("pipe creation for displayfd failed\n");
return 1;
goto err;
}
fdstr_update_str1(&display_pipe);
no_cloexec_fds[num_no_cloexec_fds++] = display_pipe.fds[1];
fdstr_set_fd1(&x11_abstract_socket, abstract_fd);
no_cloexec_fds[num_no_cloexec_fds++] = abstract_fd;
fdstr_set_fd1(&x11_unix_socket, unix_fd);
no_cloexec_fds[num_no_cloexec_fds++] = unix_fd;
assert(num_no_cloexec_fds <= ARRAY_LENGTH(no_cloexec_fds));
section = weston_config_get_section(config, "xwayland", NULL, NULL);
weston_config_section_get_string(section, "path",
&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_env_var(&child_env, "WAYLAND_SOCKET", wayland_socket.str1);
......@@ -154,79 +168,48 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd
custom_env_add_arg(&child_env, x11_wm_socket.str1);
custom_env_add_arg(&child_env, "-terminate");
envp = custom_env_get_envp(&child_env);
argp = custom_env_get_argp(&child_env);
pid = fork();
switch (pid) {
case 0:
setsid();
/* SOCK_CLOEXEC closes both ends, so we need to unset
* the flag on the client fd. */
ret = fdstr_clear_cloexec_fd1(&wayland_socket);
ret &= fdstr_clear_cloexec_fd1(&x11_abstract_socket);
ret &= fdstr_clear_cloexec_fd1(&x11_unix_socket);
ret &= fdstr_clear_cloexec_fd1(&x11_wm_socket);
ret &= fdstr_clear_cloexec_fd1(&display_pipe);
if (!ret)
_exit(EXIT_FAILURE);
execve(xserver, argp, envp);
/* execve does not return on success, so it failed */
if (exec_failure_msg) {
written = write(STDERR_FILENO, exec_failure_msg,
strlen(exec_failure_msg));
}
_exit(EXIT_FAILURE);
default:
close(wayland_socket.fds[1]);
wxw->client = wl_client_create(wxw->compositor->wl_display,
wayland_socket.fds[0]);
close(x11_wm_socket.fds[1]);
wxw->wm_fd = x11_wm_socket.fds[0];
/* During initialization the X server will round trip
* and block on the wayland compositor, so avoid making
* blocking requests (like xcb_connect_to_fd) until
* it's done with that. */
close(display_pipe.fds[1]);
loop = wl_display_get_event_loop(wxw->compositor->wl_display);
wxw->display_fd_source =
wl_event_loop_add_fd(loop, display_pipe.fds[0],
WL_EVENT_READABLE,
handle_display_fd, wxw);
wxw->process.pid = pid;
wet_watch_process(wxw->compositor, &wxw->process);
break;
case -1:
weston_log("Failed to fork to spawn xserver process\n");
fdstr_close_all(&wayland_socket);
fdstr_close_all(&x11_wm_socket);
fdstr_close_all(&display_pipe);
break;
ret = weston_client_launch(wxw->compositor, &wxw->process, &child_env,
no_cloexec_fds, num_no_cloexec_fds,
xserver_cleanup);
if (!ret) {
weston_log("Couldn't start Xwayland\n");
goto err;
}
custom_env_fini(&child_env);
free(exec_failure_msg);
free(xserver);
wxw->client = wl_client_create(wxw->compositor->wl_display,
wayland_socket.fds[0]);
if (!wxw->client) {
weston_log("Couldn't create client for Xwayland\n");
goto err;
}
return pid;
}
wxw->wm_fd = x11_wm_socket.fds[0];
static void
xserver_cleanup(struct weston_process *process, int status)
{
struct wet_xwayland *wxw =
container_of(process, struct wet_xwayland, process);
/* Now we can no longer fail, close the child end of our sockets */
close(wayland_socket.fds[1]);
close(x11_wm_socket.fds[1]);
close(display_pipe.fds[1]);
wxw->api->xserver_exited(wxw->xwayland, status);
wxw->client = NULL;
/* During initialization the X server will round trip
* and block on the wayland compositor, so avoid making
* blocking requests (like xcb_connect_to_fd) until
* it's done with that. */
loop = wl_display_get_event_loop(wxw->compositor->wl_display);
wxw->display_fd_source =
wl_event_loop_add_fd(loop, display_pipe.fds[0],
WL_EVENT_READABLE,
handle_display_fd, wxw);
free(xserver);
return wxw->process.pid;
err:
free(xserver);
fdstr_close_all(&display_pipe);
fdstr_close_all(&x11_wm_socket);
fdstr_close_all(&wayland_socket);
return -1;
}
static void
......
......@@ -68,7 +68,8 @@ fdstr_close_all(struct fdstr *s)
unsigned i;
for (i = 0; i < ARRAY_LENGTH(s->fds); i++) {
close(s->fds[i]);
if (s->fds[i] >= 0)
close(s->fds[i]);
s->fds[i] = -1;
}
}
......
......@@ -59,6 +59,7 @@ fdstr_clear_cloexec_fd1(struct fdstr *s);
void
fdstr_close_all(struct fdstr *s);
#define FDSTR_INIT ((struct fdstr){ { 0 }, { -1, -1 }})
/**
* A container for environment variables and/or process arguments, designed to
......