- closes trac#949

Signed-off-by: Jakub Filak <[email protected]>
---
 src/cli/run-command.c | 83 +++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 71 insertions(+), 12 deletions(-)

diff --git a/src/cli/run-command.c b/src/cli/run-command.c
index ee74566..1755d19 100644
--- a/src/cli/run-command.c
+++ b/src/cli/run-command.c
@@ -23,40 +23,98 @@
   http://git.kernel.org/?p=git/git.git;a=blob;f=run-command.c;hb=HEAD
 */
 
-static pid_t start_command(char **argv)
+struct command {
+    char **argv;
+    pid_t pid;
+    int tty_fd;
+    pid_t bck_tcgrp;            //<< old foreground process group
+    struct termios bck_tmodes;  //<< old configuration
+    sighandler_t bck_sigttou;   //<< old SIGTTOU handler
+};
+
+static void start_command(struct command *cmd)
 {
+  if ((cmd->tty_fd = open("/dev/tty", O_RDWR)) < 0)
+      /* do we want to die if we're in a script and have no controlling tty?
+       *
+       * YES, they can always call 'report-cli -y' from script and it should
+       * not try to open an editor
+       */
+      perror_msg_and_die("open(\"/dev/tty\", O_RDWR)");
+
+  /* save current foreground process group id */
+  if ((cmd->bck_tcgrp = tcgetpgrp(cmd->tty_fd)) < 0 )
+      perror_msg_and_die("tcgetpgrp()");
+
+  /* save current foreground process group attrs */
+  if (tcgetattr(cmd->tty_fd, &(cmd->bck_tmodes)) < 0)
+      perror_msg_and_die("tcsetattr()");
+
+  /* have to ignore the SIGTTOU because of tcsetpgrp() below
+   * otherwise the process will be stopped */
+  cmd->bck_sigttou = signal(SIGTTOU, SIG_IGN);
+
   fflush(NULL);
 
-  pid_t pid = vfork();
-  if (pid < 0)
+  if ((cmd->pid = vfork()) < 0)
   {
     perror_msg_and_die("vfork");
   }
-  if (pid == 0)
+  if (cmd->pid == 0)
   {
+    xdup2(cmd->tty_fd, 0);
+    xdup2(cmd->tty_fd, 1);
+    xdup2(cmd->tty_fd, 2);
+
+    pid_t pgrp = getpgrp();
+    if (pgrp < 0) {
+      perror_msg("getpgrp()");
+      _exit(127);
+    }
+
+    /* sends SIGTTOU to process group */
+    if (tcsetpgrp(cmd->tty_fd, pgrp) < 0) {
+      perror_msg("tcsetpgrp()");
+      _exit(127);
+    }
+
+    signal(SIGTTOU, SIG_DFL);
+
     /* Child */
-    execvp(argv[0], argv);
+    execvp(cmd->argv[0], cmd->argv);
     /* Better to use _exit (not exit) after vfork:
      * we don't want to mess up parent's memory state
      * by running libc cleanup routines.
      */
     _exit(127);
   }
-  return pid;
 }
 
-static int finish_command(pid_t pid, char **argv)
+static int finish_command(struct command *cmd)
 {
   int status;
-  pid_t waiting = safe_waitpid(pid, &status, 0);
+  pid_t waiting = safe_waitpid(cmd->pid, &status, 0);
   if (waiting < 0)
     perror_msg_and_die("waitpid");
 
+  /* reset foreground process group */
+  if (tcsetpgrp(cmd->tty_fd, cmd->bck_tcgrp) < 0)
+      perror_msg_and_die("tcsetpgrp()");
+
+  /* restore old foreground process group's attrs */
+  if (tcsetattr(cmd->tty_fd, TCSADRAIN, &(cmd->bck_tmodes)) < 0)
+      perror_msg_and_die("tcsetattr()");
+
+  close(cmd->tty_fd);
+
+  /* SIGTTOU were ignored because of changing foreground process group. */
+  signal(SIGTTOU, cmd->bck_sigttou);
+
   int code;
   if (WIFSIGNALED(status))
   {
     code = WTERMSIG(status);
-    error_msg("'%s' killed by signal %d", argv[0], code);
+    error_msg("'%s' killed by signal %d", cmd->argv[0], code);
     code += 128; /* shells use this convention for deaths by signal */
   }
   else /* if (WIFEXITED(status)) */
@@ -64,7 +122,7 @@ static int finish_command(pid_t pid, char **argv)
     code = WEXITSTATUS(status);
     if (code == 127)
     {
-      error_msg_and_die("Can't run '%s'", argv[0]);
+      error_msg_and_die("Can't run '%s'", cmd->argv[0]);
     }
   }
 
@@ -73,6 +131,7 @@ static int finish_command(pid_t pid, char **argv)
 
 int run_command(char **argv)
 {
-  pid_t pid = start_command(argv);
-  return finish_command(pid, argv);
+  struct command cmd = { argv, 0 };
+  start_command(&cmd);
+  return finish_command(&cmd);
 }
-- 
1.7.11.7

Reply via email to