- 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
