weston-terminal: Fix race at startup

If anything is printed for the terminal window to display before the
window has been initially sized we end up with a segfault.

This defers the exec() of the shell child process until after the
window is sized so this can't happen anymore.

Signed-off-by: Derek Foreman <derekf@osg.samsung.com>
Reviewed-by: Quentin Glidic <sardemff7+git@sardemff7.net>
This commit is contained in:
Derek Foreman 2017-03-24 16:29:31 -05:00 committed by Quentin Glidic
parent 091c801705
commit 88353ddad7
No known key found for this signature in database
GPG Key ID: AC203F96E2C34BB7

View File

@ -38,6 +38,7 @@
#include <sys/epoll.h> #include <sys/epoll.h>
#include <wchar.h> #include <wchar.h>
#include <locale.h> #include <locale.h>
#include <errno.h>
#include <linux/input.h> #include <linux/input.h>
@ -481,6 +482,7 @@ struct terminal {
int selection_start_row, selection_start_col; int selection_start_row, selection_start_col;
int selection_end_row, selection_end_col; int selection_end_row, selection_end_col;
struct wl_list link; struct wl_list link;
int pace_pipe;
}; };
/* Create default tab stops, every 8 characters */ /* Create default tab stops, every 8 characters */
@ -860,6 +862,10 @@ resize_handler(struct widget *widget,
struct terminal *terminal = data; struct terminal *terminal = data;
int32_t columns, rows, m; int32_t columns, rows, m;
if (terminal->pace_pipe >= 0) {
close(terminal->pace_pipe);
terminal->pace_pipe = -1;
}
m = 2 * terminal->margin; m = 2 * terminal->margin;
columns = (width - m) / (int32_t) terminal->average_width; columns = (width - m) / (int32_t) terminal->average_width;
rows = (height - m) / (int32_t) terminal->extents.height; rows = (height - m) / (int32_t) terminal->extents.height;
@ -3027,9 +3033,34 @@ terminal_run(struct terminal *terminal, const char *path)
{ {
int master; int master;
pid_t pid; pid_t pid;
int pipes[2];
/* Awkwardness: There's a sticky race condition here. If
* anything prints after the forkpty() but before the window has
* a size then we'll segfault. So we make a pipe and wait on
* it before actually exec()ing the terminal. The resize
* handler closes it in the parent process and the child continues
* on to launch a shell.
*
* The reason we don't just do terminal_run() after the window
* has a size is that we'd prefer to perform the fork() before
* the process opens a wayland connection.
*/
if (pipe(pipes) == -1) {
fprintf(stderr, "Can't create pipe for pacing.\n");
exit(EXIT_FAILURE);
}
pid = forkpty(&master, NULL, NULL, NULL); pid = forkpty(&master, NULL, NULL, NULL);
if (pid == 0) { if (pid == 0) {
int ret;
close(pipes[1]);
do {
char tmp;
ret = read(pipes[0], &tmp, 1);
} while (ret == -1 && errno == EINTR);
close(pipes[0]);
setenv("TERM", option_term, 1); setenv("TERM", option_term, 1);
setenv("COLORTERM", option_term, 1); setenv("COLORTERM", option_term, 1);
if (execl(path, path, NULL)) { if (execl(path, path, NULL)) {
@ -3041,7 +3072,9 @@ terminal_run(struct terminal *terminal, const char *path)
return -1; return -1;
} }
close(pipes[0]);
terminal->master = master; terminal->master = master;
terminal->pace_pipe = pipes[1];
fcntl(master, F_SETFL, O_NONBLOCK); fcntl(master, F_SETFL, O_NONBLOCK);
terminal->io_task.run = io_handler; terminal->io_task.run = io_handler;
display_watch_fd(terminal->display, terminal->master, display_watch_fd(terminal->display, terminal->master,