[Pkg-shadow-devel] Bug#663200: Bug#663200: Bug#628843: Bug#659878: cannot set terminal process group (-1): Inappropriate ioctl for device
Wolfgang Zarre
lkdev at essax.com
Sun Sep 15 10:34:09 UTC 2013
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
More information about the Pkg-shadow-devel
mailing list