[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