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 <der...@osg.samsung.com>
Reviewed-by: Quentin Glidic <sardemff7+...@sardemff7.net>

---
Changes from revision 1:
 - don't forget to close the other half of the pipe in the child

 clients/terminal.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/clients/terminal.c b/clients/terminal.c
index c5531790..274ced09 100644
--- a/clients/terminal.c
+++ b/clients/terminal.c
@@ -38,6 +38,7 @@
 #include <sys/epoll.h>
 #include <wchar.h>
 #include <locale.h>
+#include <errno.h>
 
 #include <linux/input.h>
 
@@ -481,6 +482,7 @@ struct terminal {
        int selection_start_row, selection_start_col;
        int selection_end_row, selection_end_col;
        struct wl_list link;
+       int pace_pipe;
 };
 
 /* Create default tab stops, every 8 characters */
@@ -860,6 +862,10 @@ resize_handler(struct widget *widget,
        struct terminal *terminal = data;
        int32_t columns, rows, m;
 
+       if (terminal->pace_pipe >= 0) {
+               close(terminal->pace_pipe);
+               terminal->pace_pipe = -1;
+       }
        m = 2 * terminal->margin;
        columns = (width - m) / (int32_t) terminal->average_width;
        rows = (height - m) / (int32_t) terminal->extents.height;
@@ -3027,9 +3033,34 @@ terminal_run(struct terminal *terminal, const char *path)
 {
        int master;
        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);
        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("COLORTERM", option_term, 1);
                if (execl(path, path, NULL)) {
@@ -3041,7 +3072,9 @@ terminal_run(struct terminal *terminal, const char *path)
                return -1;
        }
 
+       close(pipes[0]);
        terminal->master = master;
+       terminal->pace_pipe = pipes[1];
        fcntl(master, F_SETFL, O_NONBLOCK);
        terminal->io_task.run = io_handler;
        display_watch_fd(terminal->display, terminal->master,
-- 
2.11.0

_______________________________________________
wayland-devel mailing list
wayland-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to