My interactive (using `read`) Bash-script application still suspends itself after executing second instance of a player. I've only seen this happen in the newest development version of Bash.
I tried to debug calls to functions in shtty.c and changes in attributes made through them but I haven't seen anything unusual as compared to the behavior in 4.3. I also tried examining stuffs in sig.c, nosig.c et al but I'm still unfamiliar with signal handling and some conditionals are just too confusing for me. Not sure where to start or which significant part I should try to tackle. The application executes player processes in an input pipe using `exec N>` and process substitution. No extra (sub)shell is also left running as I use `exec` inside the process substitution command. Code: http://sourceforge.net/p/playshell/code/ci/master/tree/ Bash version: 4.4-alpha to 20151106 # ./bash $(which playshell) PlayShell 0.2-AfterRC1 Checking players. Loading playlist. Video output is disabled. Repeating is enabled. Shuffling is enabled. Pause between plays is 5-10 seconds. Restoring commands history. : x Playing |8| John Mayer - My Stupid Mouth.flac. Waiting for 8 seconds. Playing |3| John Mayer - Clarity.flac. : [1]+ Stopped ./bash $(which playshell)
/* * shtty.c -- abstract interface to the terminal, focusing on capabilities. */ /* Copyright (C) 1999 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. Bash is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Bash is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Bash. If not, see <http://www.gnu.org/licenses/>. */ #ifdef HAVE_CONFIG_H # include <config.h> #endif #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #include <shtty.h> #include <stdio.h> #include <string.h> static TTYSTRUCT ttin, ttout; static int ttsaved = 0; void shttyvariables_strvec_to_string (vec, buffer, sep) char** vec; char* buffer; char sep; { char* p; char** it; int i = 0; size_t l = 0; p = vec[0]; if (p) { i = strlen (p); memcpy (buffer, p, i); for (it = vec + 1; (p = *it); ++it) { buffer[i++] = sep; l = strlen (p); memcpy (buffer + i, p, l); i += l; } } buffer[i] = '\0'; } char* shtty_attr_to_string(ttp) TTYSTRUCT *ttp; { char** vec; int i; size_t alloc_size; char* ret; vec = strvec_create (60); #define A(M, F) \ if (ttp->M & F) { \ vec[i++] = #F; \ alloc_size += sizeof (#F); \ } i = 0; alloc_size = 0; A(c_iflag, IGNBRK) A(c_iflag, BRKINT) A(c_iflag, IGNPAR) A(c_iflag, PARMRK) A(c_iflag, INPCK) A(c_iflag, ISTRIP) A(c_iflag, INLCR) A(c_iflag, IGNCR) A(c_iflag, ICRNL) A(c_iflag, IUCLC) A(c_iflag, IXON) A(c_iflag, IXANY) A(c_iflag, IXOFF) A(c_iflag, IMAXBEL) A(c_iflag, IUTF8) A(c_oflag, OPOST) A(c_oflag, OLCUC) A(c_oflag, ONLCR) A(c_oflag, OCRNL) A(c_oflag, ONOCR) A(c_oflag, ONLRET) A(c_oflag, OFILL) A(c_oflag, OFDEL) A(c_oflag, NLDLY) A(c_oflag, CRDLY) A(c_oflag, TABDLY) A(c_oflag, BSDLY) A(c_oflag, VTDLY) A(c_oflag, FFDLY) A(c_cflag, CBAUD) A(c_cflag, CBAUDEX) A(c_cflag, CSIZE) A(c_cflag, CSTOPB) A(c_cflag, CREAD) A(c_cflag, PARENB) A(c_cflag, PARODD) A(c_cflag, HUPCL) A(c_cflag, CLOCAL) // A(c_cflag, LOBLK) A(c_cflag, CIBAUD) A(c_cflag, CMSPAR) A(c_cflag, CRTSCTS) A(c_lflag, ICANON) A(c_lflag, XCASE) A(c_lflag, ECHO) A(c_lflag, ECHOE) A(c_lflag, ECHOK) A(c_lflag, ECHONL) A(c_lflag, ECHOCTL) A(c_lflag, ECHOPRT) A(c_lflag, ECHOKE) // A(c_lflag, DEFECHO) A(c_lflag, FLUSHO) A(c_lflag, NOFLSH) A(c_lflag, TOSTOP) A(c_lflag, PENDIN) A(c_lflag, IEXTEN) if (i == 0) ret = savestring(""); else if (i == 1) ret = savestring(vec[0]); else { vec[i] = 0; strvec_sort (vec); ret = xmalloc (alloc_size); shttyvariables_strvec_to_string (vec, ret, ' '); } free(vec); #undef A return ret; } void shtty_print_attr(prefix, ttp) char* prefix; TTYSTRUCT *ttp; { char* str; str = shtty_attr_to_string (ttp); fprintf(stderr, "%s: %s\n", prefix, str); free(str); } int ttgetattr(fd, ttp) int fd; TTYSTRUCT *ttp; { fprintf(stderr, "ttgetattr(%d, %p)\n", fd, ttp); int r; #ifdef TERMIOS_TTY_DRIVER r = tcgetattr(fd, ttp); #else # ifdef TERMIO_TTY_DRIVER r = ioctl(fd, TCGETA, ttp); # else r = ioctl(fd, TIOCGETP, ttp); # endif #endif shtty_print_attr("ttgetattr", ttp); fprintf(stderr, "ttgetattr: ret: %d\n", r); return r; } int ttsetattr(fd, ttp) int fd; TTYSTRUCT *ttp; { fprintf(stderr, "ttsetattr(%d, %p)\n", fd, ttp); shtty_print_attr("ttsetattr", ttp); int r; #ifdef TERMIOS_TTY_DRIVER r = tcsetattr(fd, TCSADRAIN, ttp); #else # ifdef TERMIO_TTY_DRIVER r = ioctl(fd, TCSETAW, ttp); # else r = ioctl(fd, TIOCSETN, ttp); # endif #endif fprintf(stderr, "ttsetattr: ret: %d\n", r); return r; } void ttsave() { fprintf(stderr, "ttysave()\n"); if (ttsaved) { fprintf(stderr, "ttysave: ttsaved\n"); } else { ttgetattr (0, &ttin); ttgetattr (1, &ttout); ttsaved = 1; } fprintf(stderr, "ttysave: returns\n"); } void ttrestore() { fprintf(stderr, "ttrestore()\n"); if (ttsaved) { fprintf(stderr, "ttrestore: ttsaved\n"); ttsetattr (0, &ttin); ttsetattr (1, &ttout); ttsaved = 0; } fprintf(stderr, "ttrestore: returns\n"); } /* Retrieve the internally-saved attributes associated with tty fd FD. */ TTYSTRUCT * ttattr (fd) int fd; { fprintf(stderr, "ttattr(%d)\n", fd); TTYSTRUCT* r; if (ttsaved == 0) r = ((TTYSTRUCT *)0); else if (fd == 0) r = &ttin; else if (fd == 1) r = &ttout; else r = ((TTYSTRUCT *)0); fprintf(stderr, "ttattr: ret: %p\n", r); return r; } /* * Change attributes in ttp so that when it is installed using * ttsetattr, the terminal will be in one-char-at-a-time mode. */ int tt_setonechar(ttp) TTYSTRUCT *ttp; { fprintf(stderr, "tt_setonechar(%p)\n", ttp); #if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER) /* XXX - might not want this -- it disables erase and kill processing. */ ttp->c_lflag &= ~ICANON; ttp->c_lflag |= ISIG; # ifdef IEXTEN ttp->c_lflag |= IEXTEN; # endif ttp->c_iflag |= ICRNL; /* make sure we get CR->NL on input */ ttp->c_iflag &= ~INLCR; /* but no NL->CR */ # ifdef OPOST ttp->c_oflag |= OPOST; # endif # ifdef ONLCR ttp->c_oflag |= ONLCR; # endif # ifdef OCRNL ttp->c_oflag &= ~OCRNL; # endif # ifdef ONOCR ttp->c_oflag &= ~ONOCR; # endif # ifdef ONLRET ttp->c_oflag &= ~ONLRET; # endif ttp->c_cc[VMIN] = 1; ttp->c_cc[VTIME] = 0; #else ttp->sg_flags |= CBREAK; #endif fprintf(stderr, "ttattr: tt_setonechar: ret: 0 (default)\n"); return 0; } /* Set the tty associated with FD and TTP into one-character-at-a-time mode */ int ttfd_onechar (fd, ttp) int fd; TTYSTRUCT *ttp; { fprintf(stderr, "ttfd_onechar()\n"); int r; r = tt_setonechar(ttp) < 0 ? -1 : ttsetattr (fd, ttp); fprintf(stderr, "ttfd_onechar: ret: %d\n", r); return r; } /* Set the terminal into one-character-at-a-time mode */ int ttonechar () { fprintf(stderr, "ttonechar()\n"); TTYSTRUCT tt; int r; if (ttsaved == 0) r = -1; else { tt = ttin; r = ttfd_onechar(0, &tt); } fprintf(stderr, "ttonechar: ret: %d\n", r); return r; } /* * Change attributes in ttp so that when it is installed using * ttsetattr, the terminal will be in no-echo mode. */ int tt_setnoecho(ttp) TTYSTRUCT *ttp; { fprintf(stderr, "tt_setnoecho(%p)\n", ttp); #if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER) ttp->c_lflag &= ~(ECHO|ECHOK|ECHONL); #else ttp->sg_flags &= ~ECHO; #endif fprintf(stderr, "tt_setnoecho: ret: 0 (default)\n"); return 0; } /* Set the tty associated with FD and TTP into no-echo mode */ int ttfd_noecho (fd, ttp) int fd; TTYSTRUCT *ttp; { fprintf(stderr, "ttfd_noecho(%d, %p)\n", fd, ttp); int r; r = tt_setnoecho(ttp) < 0 ? -1 : ttsetattr(fd, ttp); fprintf(stderr, "ttfd_noecho: ret: %d\n", r); return r; } /* Set the terminal into no-echo mode */ int ttnoecho () { fprintf(stderr, "ttnoecho()\n"); TTYSTRUCT tt; int r; if (ttsaved == 0) r = -1; else { tt = ttin; r = (ttfd_noecho (0, &tt)); } fprintf(stderr, "ttnoecho: ret: %d\n", r); return r; } /* * Change attributes in ttp so that when it is installed using * ttsetattr, the terminal will be in eight-bit mode (pass8). */ int tt_seteightbit (ttp) TTYSTRUCT *ttp; { fprintf(stderr, "tt_seteightbit(%p)\n", ttp); #if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER) ttp->c_iflag &= ~ISTRIP; ttp->c_cflag |= CS8; ttp->c_cflag &= ~PARENB; #else ttp->sg_flags |= ANYP; #endif fprintf(stderr, "tt_seteightbit: ret: 0 (default)\n"); return 0; } /* Set the tty associated with FD and TTP into eight-bit mode */ int ttfd_eightbit (fd, ttp) int fd; TTYSTRUCT *ttp; { int r; fprintf(stderr, "ttfd_eightbit(%d, %p)\n", fd, ttp); r = tt_seteightbit (ttp) < 0 ? -1 : ttsetattr (fd, ttp); fprintf(stderr, "ttfd_eightbit: ret: %d\n", r); return r; } /* Set the terminal into eight-bit mode */ int tteightbit () { TTYSTRUCT tt; fprintf(stderr, "tteightbit()\n"); int r; if (ttsaved == 0) r = -1; else { tt = ttin; r = (ttfd_eightbit (0, &tt)); } fprintf(stderr, "tteightbit: ret: %d\n", r); return r; } /* * Change attributes in ttp so that when it is installed using * ttsetattr, the terminal will be in non-canonical input mode. */ int tt_setnocanon (ttp) TTYSTRUCT *ttp; { fprintf(stderr, "tt_setnocanon(%p)\n", ttp); #if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER) ttp->c_lflag &= ~ICANON; #endif fprintf(stderr, "tt_setnocanon: ret: 0 (default)\n"); return 0; } /* Set the tty associated with FD and TTP into non-canonical mode */ int ttfd_nocanon (fd, ttp) int fd; TTYSTRUCT *ttp; { int r; fprintf(stderr, "ttfd_nocanon(%d, %p)\n", fd, ttp); r = (tt_setnocanon (ttp) < 0) ? -1 : (ttsetattr (fd, ttp)); fprintf(stderr, "ttfd_nocanon: ret: %d\n", r); return r; } /* Set the terminal into non-canonical mode */ int ttnocanon () { fprintf(stderr, "ttnocanon()\n"); TTYSTRUCT tt; int r; if (ttsaved == 0) r = -1; else { tt = ttin; r = (ttfd_nocanon (0, &tt)); } fprintf(stderr, "ttnocanon: ret: %d\n", r); return r; } /* * Change attributes in ttp so that when it is installed using * ttsetattr, the terminal will be in cbreak, no-echo mode. */ int tt_setcbreak(ttp) TTYSTRUCT *ttp; { fprintf(stderr, "tt_setcbreak(%p)\n", ttp); int r; r = (tt_setonechar (ttp) < 0) ? -1 : (tt_setnoecho (ttp)); fprintf(stderr, "tt_setcbreak: ret: %d\n", r); return r; } /* Set the tty associated with FD and TTP into cbreak (no-echo, one-character-at-a-time) mode */ int ttfd_cbreak (fd, ttp) int fd; TTYSTRUCT *ttp; { fprintf(stderr, "ttfd_cbreak(%d, %p)\n", fd, ttp); int r; r = (tt_setcbreak (ttp) < 0) ? -1 : (ttsetattr (fd, ttp)); fprintf(stderr, "ttfd_cbreak: ret: %d\n", r); return r; } /* Set the terminal into cbreak (no-echo, one-character-at-a-time) mode */ int ttcbreak () { TTYSTRUCT tt; fprintf(stderr, "ttcbreak()\n"); int r; if (ttsaved == 0) r = -1; else { tt = ttin; r = (ttfd_cbreak (0, &tt)); } fprintf(stderr, "ttcbreak: ret: %d\n", r); }