Hi again, Sorry, but I was submitting the wrong patch by mistake but here now the right one:
___BEGIN_PATCH___ diff --git a/src/su.c b/src/su.c index 34f6771..8053225 100644 --- a/src/su.c +++ b/src/su.c @@ -60,7 +60,6 @@ #include <pwd.h> #include <signal.h> #include <stdio.h> -#include <sys/time.h> #include <sys/types.h> #include <unistd.h> #include <sys/ioctl.h> @@ -220,6 +219,46 @@ static /*@noreturn@*/void su_failure (const char *tty, bool su_to_root) exit (1); } +static bool term_setattr( int fd, const struct termios *termset, bool hndl_sig) { + + struct termios termset_new; + struct termios termset_check; + + termset_new = *termset; + /* Set RAW mode */ + cfmakeraw( &termset_new); + + if( hndl_sig) + termset_new.c_lflag = ISIG; + + if( tcsetattr( fd, TCSANOW, &termset_new) == -1) { + fprintf( stderr, + _("%s: Cannot set raw mode\n"), + Prog); + return false; + } + + if( tcgetattr( fd, &termset_check) == -1) { + fprintf( stderr, + _("%s: Cannot get terminal attributes\n"), + Prog); + return false; + } + + if( termset_new.c_iflag != termset_check.c_iflag || + termset_new.c_oflag != termset_check.c_oflag || + termset_new.c_cflag != termset_check.c_cflag || + termset_new.c_lflag != termset_check.c_lflag || + memcmp( &termset_new.c_cc, &termset_check.c_cc, NCCS) != 0) { + + fprintf( stderr, + _("%s: Could not set terminal attributes correctly\n"), + Prog); + return false; + } + return true; +} + /* * execve_shell - Execute a shell with execve, or interpret it with * /bin/sh @@ -280,19 +319,22 @@ static void handle_session (const struct passwd *pw) #endif /* USE_PAM */ int fd_ptmx = -1; int fd_pts = -1; - char *pts_name = NULL; + char *pts_name = NULL; struct termios termset_save; - struct termios termset_new; fd_set inp_fds; struct timeval sel_to; char trbuf[BUFSIZ]; ssize_t bytes_r; struct winsize winsz; bool winsz_set = false; + pid_t pg_pid = 0; + pid_t pg_pid_cmp = 0; + pid_t pg_pid_tmp = 0; + pg_pid = getpid(); - if (isatty (0) == 1) { + if (isatty ( STDIN_FILENO) == 1) { have_tty = true; if (tcgetattr (STDIN_FILENO, &termset_save) == -1) { @@ -360,14 +402,6 @@ static void handle_session (const struct passwd *pw) if (have_tty) { close (fd_ptmx); - if (tcsetattr (fd_pts, TCSANOW, &termset_save) == -1) { - fprintf (stderr, - _("%s: Cannot set termios attributes of session\n"), - Prog); - (void) close (fd_pts); - exit (1); - } - if ( winsz_set && (ioctl (fd_pts, TIOCSWINSZ, &winsz) == -1)) { fprintf (stderr, @@ -423,7 +457,7 @@ static void handle_session (const struct passwd *pw) (void) fprintf (stderr, _("%s: signal malfunction\n"), Prog); - caught = SIGTERM; + caught = SIGHUP; } if (0 == caught) { struct sigaction action; @@ -434,31 +468,39 @@ static void handle_session (const struct passwd *pw) sigemptyset (&ourset); if ( (sigaddset (&ourset, SIGTERM) != 0) + || (sigaddset (&ourset, SIGINT) != 0) || (sigaddset (&ourset, SIGALRM) != 0) || (sigaddset (&ourset, SIGWINCH) != 0) + || (sigaddset (&ourset, SIGCONT) != 0) + || (sigaddset (&ourset, SIGTSTP) != 0) || (sigaction (SIGTERM, &action, NULL) != 0) + || (sigaction (SIGINT, &action, NULL) != 0) || (sigaction (SIGWINCH, &action, NULL) != 0) - || (sigprocmask (SIG_UNBLOCK, &ourset, NULL) != 0)) { + || (sigaction (SIGCONT, &action, NULL) != 0) + || (sigaction (SIGTSTP, &action, NULL) != 0) + || (sigprocmask (SIG_UNBLOCK, &ourset, NULL) != 0) + ) { fprintf (stderr, _("%s: signal masking malfunction\n"), Prog); - caught = SIGTERM; + caught = SIGHUP; } } if ((0 == caught) && have_tty) { - /* Set RAW mode */ - termset_new = termset_save; - cfmakeraw (&termset_new); - if (tcsetattr (STDIN_FILENO, TCSANOW, &termset_new) != 0) { - /* FIXME: At least one change was successful. - * Success should be checked with tcsetattr */ - fprintf (stderr, - _("%s: Cannot set terminal attributes: %s\n"), - Prog, strerror (errno)); - caught = -1; - } - } + if( (pg_pid_tmp = tcgetpgrp( STDIN_FILENO)) == -1) { + fprintf( stderr, _("%s: Cannot get process group id\n"), Prog); + caught = SIGHUP; + } else { + /* Set raw mode if running in foreground */ + if( pg_pid_tmp == pg_pid) { + /* Set RAW mode */ + if( term_setattr( STDIN_FILENO, &termset_save, !doshell) == false) + caught = SIGHUP; + } + pg_pid_cmp = pg_pid_tmp; + } + } if (0 == caught) { bool stop = true; @@ -466,6 +508,7 @@ static void handle_session (const struct passwd *pw) do { pid_t pid; stop = true; + errno = 0; if (have_tty) { pid = waitpid (-1, &status, WUNTRACED | WNOHANG); @@ -473,16 +516,31 @@ static void handle_session (const struct passwd *pw) pid = waitpid (-1, &status, WUNTRACED); } - if ( ((pid_t)-1 != pid && !have_tty) + /* When interrupted by signal, the signal will be + * forwarded to the child, and termination will be + * forced later. + */ + if ( (((pid_t)-1 == pid) && (EINTR == errno) && (SIGTSTP == caught)) || + ((pid == (pid_t)0) && (SIGTSTP == caught))) { + /* Except for SIGTSTP, which request to + * stop the child. + * We will SIGSTOP ourself on the next + * waitpid round. + */ + kill (pid_child, SIGSTOP); + stop = false; + } else if ( ((pid_t)0 < pid) && (0 != WIFSTOPPED (status))) { /* The child (shell) was suspended. * Suspend su. */ - kill (getpid (), WSTOPSIG (status)); + kill (pg_pid, WSTOPSIG (status)); /* wake child when resumed */ kill (pid, SIGCONT); stop = false; - } else if (pid == (pid_t)0 && have_tty) { - stop = false; + } else if ( pid >= (pid_t)0 && have_tty) { + + if( pid == (pid_t)0) + stop = false; if (caught == SIGWINCH) { caught = 0; @@ -491,76 +549,146 @@ static void handle_session (const struct passwd *pw) } } + /* Reset 'process group pid compare' to set STDIN to 'raw' again */ + if( caught == SIGCONT) { + caught = 0; + pg_pid_cmp = 0; + } + + /* Terminate the child with SIGHUP to be able + * to terminate a shell running as command + */ + if( caught == SIGINT) + kill( -pid_child, SIGHUP); + + /* If caught by any other signal then stop */ + if( caught != 0) + stop = true; + FD_ZERO (&inp_fds); FD_SET (STDIN_FILENO, &inp_fds); FD_SET (fd_ptmx, &inp_fds); sel_to = (struct timeval){ 0, 10000}; if (select (fd_ptmx + 1, &inp_fds, NULL, NULL, &sel_to) == -1) { - if (errno == EINTR) { + if (errno == EINTR && stop == false) { continue; } stop = true; + errno = 0; } if (FD_ISSET (STDIN_FILENO, &inp_fds)) { - bytes_r = read (STDIN_FILENO, trbuf, BUFSIZ); - if (bytes_r <= 0) { - if (errno == EINTR) { - continue; - } - fprintf (stderr, - _("%s: Failure in reading from stdin\r\n"), - Prog); + /* Get process group pid to compare with last run */ + if ( (pg_pid_tmp = tcgetpgrp( STDIN_FILENO)) == -1) { + fprintf( stderr, + _("%s: Cannot get process group id: %s\n"), + Prog, strerror( errno)); stop = true; + errno = 0; } - - if ( (bytes_r > 0) - && (write (fd_ptmx, trbuf, bytes_r) != bytes_r)) { - if (errno == EINTR || errno == EIO) { - /* FIXME: are we - * loosing some - * bytes here? */ - continue; + else { + + /* Running in foreground if equal */ + if( pg_pid_tmp == pg_pid) { + + /* Set raw mode again if last run was in background */ + if( pg_pid_cmp != pg_pid_tmp) { + + /* Fetch term settings again because parent might have changed settings */ + if( tcgetattr( STDIN_FILENO, &termset_save) == -1) { + fprintf( stderr, + _("%s: Cannot get termios attributes: %s\n"), + Prog, strerror( errno)); + stop = true; + errno = 0; + } + else { + /* Set RAW mode */ + if( term_setattr( STDIN_FILENO, &termset_save, !doshell) == false) { + stop = true; + errno = 0; + } + } + pg_pid_cmp = pg_pid_tmp; + } } - fprintf (stderr, _("%s: Failure in writing to session\r\n"), Prog); - stop = true; } + + + bytes_r = read( STDIN_FILENO, trbuf, BUFSIZ); + if( bytes_r <= 0) { + if( errno != EINTR && errno != EIO && errno != 0) { + fprintf( stderr, + _("%s: Failure in reading from stdin: %s\r\n"), + Prog, strerror( errno)); + stop = true; + errno = 0; + } + } + + while( bytes_r > 0) { + ret = write( fd_ptmx, trbuf, bytes_r); + if( ret < 0) { + if( errno != EINTR && errno != 0) { + fprintf( stderr, + _("%s: Failure in writing to session: %s\r\n"), + Prog, strerror( errno)); + stop = true; + bytes_r = 0; + } + errno = 0; + } + else + bytes_r -= ret; + } } if (FD_ISSET (fd_ptmx, &inp_fds)) { bytes_r = read (fd_ptmx, trbuf, BUFSIZ); if (bytes_r <= 0) { - if (errno == EINTR || errno == EIO) { - continue; - } - fprintf (stderr, - _("%s: Failure in reading from session: %s\r\n"), - Prog, strerror (errno)); - stop = true; + if (errno != EINTR && errno != EIO && errno != 0) { + fprintf (stderr, + _("%s: Failure in reading from session: %s\r\n"), + Prog, strerror (errno)); + stop = true; + errno = 0; + } } - if (bytes_r > 0 && write (STDOUT_FILENO, trbuf, bytes_r) != bytes_r) { - fprintf (stderr, - _("%s: Failure in writing to stdout\r\n"), - Prog); - stop = true; - } ___END_PATCH___ Best regards Wolf -- To UNSUBSCRIBE, email to debian-bugs-rc-requ...@lists.debian.org with a subject of "unsubscribe". Trouble? Contact listmas...@lists.debian.org