-static int spawn (char **argv, pid_t *child_pointer)
-{
- pid_t child, child_got;
- int status;
-
- switch (child = fork ()) {
- case -1:
- perror ("fork(2)");
- exit (EXIT_FAILURE);
- case 0:
- /* Do not setpgrp(2) in the parent process as the process-group
- is shared for the whole sh(1) pipeline we could be a part
- of. The process-group is set according to PID of the first
- command in the pipeline.
- We would rip even vi(1) in the case of:
- ./orphanripper sh -c 'sleep 1&' | vi -
- */
- /* Do not setpgrp(2) as our pty would not be ours and we would
- get `SIGSTOP' later, particularly after spawning gdb(1). */
- if (getpid() != setsid ()) {
- perror ("setsid");
- exit (EXIT_FAILURE);
- }
- execvp (argv[1], argv + 1);
- perror ("execvp(2)");
- exit (EXIT_FAILURE);
- default:
- break;
+static volatile int signal_child_hit = 0;
+
+/* We use it to race-safely emulate ppoll(2) by poll(2). */
+static int pipefd[2];
+
+static void signal_child (int signo)
+{
+ int i;
+
+ signal_child_hit = 1;
+
+ assert (pipefd[1] != -1);
+ i = close (pipefd[1]);
+ assert (i == 0);
+ pipefd[1] = -1;
+}
+
+static char childptyname[LINE_MAX];
+static pid_t child;
+
+static int spawn (char **argv)
+{
+ pid_t child_got;
+ int status, amaster, i;
+ struct sigaction act;
+ struct termios termios;
+
+ i = pipe (pipefd);
+ assert (i == 0);
+
+ /* We do not use signal(2) to be sure we have SA_RESTART set. */
+ memset (&act, 0, sizeof (act));
+ act.sa_handler = signal_child;
+ i = sigemptyset (&act.sa_mask);
+ assert (i == 0);
+ act.sa_flags = SA_RESTART;
+ i = sigaction (SIGCHLD, &act, NULL);
+ assert (i == 0);
+
+ /* With TERMP passed as NULL we get "\n" -> "\r\n". */
+ cfmakeraw (&termios);
+#ifdef FLUSHO
+ /* Workaround a readline deadlock bug in _get_tty_settings(). */
+ termios.c_lflag &= ~FLUSHO;
+#endif
+ child = forkpty (&amaster, childptyname, &termios, NULL);
+ switch (child)
+ {
+ case -1:
+ perror ("forkpty(3)");
+ exit (EXIT_FAILURE);
+ case 0:
+ i = close (pipefd[0]);
+ assert (i == 0);
+ i = close (pipefd[1]);
+ assert (i == 0);
+
+ /* Do not replace STDIN as inferiors query its termios. */
+#if 0
+ i = close (STDIN_FILENO);
+ assert (i == 0);
+ i = open ("/dev/null", O_RDONLY);
+ assert (i == STDIN_FILENO);
+#endif
+
+ /* Do not setpgrp(2) in the parent process as the process-group
+ is shared for the whole sh(1) pipeline we could be a part
+ of. The process-group is set according to PID of the first
+ command in the pipeline.
+ We would rip even vi(1) in the case of:
+ ./orphanripper sh -c 'sleep 1&' | vi -
+ */
+ /* Do not setpgrp(2) as our pty would not be ours and we would
+ get `SIGSTOP' later, particularly after spawning gdb(1).
+ setsid(3) was already executed by forkpty(3) and it would fail if
+ executed again. */
+ if (getpid() != getpgrp ())
+ {
+ perror ("getpgrp(2)");
+ exit (EXIT_FAILURE);
+ }
+ execvp (argv[1], argv + 1);
+ perror ("execvp(2)");
+ exit (EXIT_FAILURE);
+ default:
+ break;
+ }
+ i = fcntl (amaster, F_SETFL, O_RDWR | O_NONBLOCK);
+ if (i != 0)
+ {
+ perror ("fcntl (amaster, F_SETFL, O_NONBLOCK)");
+ exit (EXIT_FAILURE);
+ }
+ for (;;)
+ {
+ struct pollfd pollfd[2];
+ char buf[LINE_MAX];
+ ssize_t buf_got;
+
+ pollfd[0].fd = amaster;
+ pollfd[0].events = POLLIN;
+ pollfd[1].fd = pipefd[0];
+ pollfd[1].events = POLLIN;
+ i = poll (pollfd, LENGTH (pollfd), -1);
+ if (i == -1 && errno == EINTR)
+ {
+ /* Weird but SA_RESTART sometimes does not work. */
+ continue;