1 /* Concurrent shell support for the Midnight Commander
2 Copyright (C) 1994, 1995 Dugan Porter
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of Version 2 of the GNU General Public
6 License, as published by the Free Software Foundation.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #ifdef HAVE_SUBSHELL_SUPPORT
23 # define _GNU_SOURCE 1
33 #include <sys/types.h>
34 #ifdef HAVE_SYS_IOCTL_H
35 # include <sys/ioctl.h>
43 # include <stropts.h> /* For I_PUSH */
44 #endif /* HAVE_STROPTS_H */
47 #include "tty.h" /* LINES */
48 #include "panel.h" /* current_panel */
49 #include "wtools.h" /* query_dialog() */
50 #include "main.h" /* do_update_prompt() */
51 #include "cons.saver.h" /* handle_console() */
52 #include "key.h" /* XCTRL */
56 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
60 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
63 /* tcsh closes all non-standard file descriptors, so we have to use a pipe */
64 static char tcsh_fifo
[128];
67 static void init_raw_mode (void);
68 static int feed_subshell (int how
, int fail_on_error
);
69 static void synchronize (void);
70 static int pty_open_master (char *pty_name
);
71 static int pty_open_slave (const char *pty_name
);
72 static int resize_tty (int fd
);
75 # define STDIN_FILENO 0
79 # define STDOUT_FILENO 1
83 # define STDERR_FILENO 2
86 /* If using a subshell for evaluating commands this is true */
88 #ifdef SUBSHELL_OPTIONAL
94 /* File descriptor of the pseudoterminal used by the subshell */
97 /* The key for switching back to MC from the subshell */
98 static const char subshell_switch_key
= XCTRL('o') & 255;
100 /* State of the subshell:
101 * INACTIVE: the default state; awaiting a command
102 * ACTIVE: remain in the shell until the user hits `subshell_switch_key'
103 * RUNNING_COMMAND: return to MC when the current command finishes */
104 enum subshell_state_enum subshell_state
;
106 /* Holds the latest prompt captured from the subshell */
107 char *subshell_prompt
= NULL
;
109 /* Initial length of the buffer for the subshell's prompt */
110 #define INITIAL_PROMPT_SIZE 10
112 /* Used by the child process to indicate failure to start the subshell */
113 #define FORK_FAILURE 69 /* Arbitrary */
115 /* Initial length of the buffer for all I/O with the subshell */
116 #define INITIAL_PTY_BUFFER_SIZE 100 /* Arbitrary; but keep it >= 80 */
119 enum {READ
=0, WRITE
=1};
121 static char *pty_buffer
; /* For reading/writing on the subshell's pty */
122 static int pty_buffer_size
; /* The buffer grows as needed */
123 static int subshell_pipe
[2]; /* To pass CWD info from the subshell to MC */
124 static pid_t subshell_pid
= 1; /* The subshell's process ID */
125 static char subshell_cwd
[MC_MAXPATHLEN
+1]; /* One extra char for final '\n' */
127 /* Subshell type (gleaned from the SHELL environment variable, if available) */
128 static enum {BASH
, TCSH
, ZSH
} subshell_type
;
130 /* Flag to indicate whether the subshell is ready for next command */
131 static int subshell_ready
;
133 /* The following two flags can be changed by the SIGCHLD handler. This is */
134 /* OK, because the `int' type is updated atomically on all known machines */
135 static volatile int subshell_alive
, subshell_stopped
;
137 /* We store the terminal's initial mode here so that we can configure
138 the pty similarly, and also so we can restore the real terminal to
139 sanity if we have to exit abruptly */
140 static struct termios shell_mode
;
142 /* This is a transparent mode for the terminal where MC is running on */
143 /* It is used when the shell is active, so that the control signals */
144 /* are delivered to the shell pty */
145 static struct termios raw_mode
;
147 /* This counter indicates how many characters of prompt we have read */
148 /* FIXME: try to figure out why this had to become global */
149 static int prompt_pos
;
153 * Prepare child process to running the shell and run it.
155 * Modifies the global variables (in the child process only):
161 init_subshell_child (const char *pty_name
)
164 const char *init_file
= NULL
;
167 #endif /* HAVE_GETSID */
169 setsid (); /* Get a fresh terminal session */
171 /* Open the slave side of the pty: again */
172 pty_slave
= pty_open_slave (pty_name
);
174 /* This must be done before closing the master side of the pty, */
175 /* or it will fail on certain idiotic systems, such as Solaris. */
177 /* Close master side of pty. This is important; apart from */
178 /* freeing up the descriptor for use in the subshell, it also */
179 /* means that when MC exits, the subshell will get a SIGHUP and */
180 /* exit too, because there will be no more descriptors pointing */
181 /* at the master side of the pty and so it will disappear. */
183 close (subshell_pty
);
185 /* Make sure that it has become our controlling terminal */
187 /* Redundant on Linux and probably most systems, but just in case: */
190 ioctl (pty_slave
, TIOCSCTTY
, 0);
193 /* Configure its terminal modes and window size */
195 /* Set up the pty with the same termios flags as our own tty, plus */
196 /* TOSTOP, which keeps background processes from writing to the pty */
198 shell_mode
.c_lflag
|= TOSTOP
; /* So background writers get SIGTTOU */
199 if (tcsetattr (pty_slave
, TCSANOW
, &shell_mode
)) {
200 fprintf (stderr
, "Cannot set pty terminal modes: %s\r\n",
201 unix_error_string (errno
));
202 _exit (FORK_FAILURE
);
205 /* Set the pty's size (80x25 by default on Linux) according to the */
206 /* size of the real terminal as calculated by ncurses, if possible */
207 resize_tty (pty_slave
);
209 /* Set up the subshell's environment and init file name */
211 /* It simplifies things to change to our home directory here, */
212 /* and the user's startup file may do a `cd' command anyway */
213 chdir (home_dir
); /* FIXME? What about when we re-run the subshell? */
216 /* Set MC_SID to prevent running one mc from another */
219 char sid_str
[BUF_SMALL
];
220 g_snprintf (sid_str
, sizeof (sid_str
), "MC_SID=%ld",
222 putenv (g_strdup (sid_str
));
224 #endif /* HAVE_GETSID */
226 switch (subshell_type
) {
228 init_file
= ".mc/bashrc";
229 if (access (init_file
, R_OK
) == -1)
230 init_file
= ".bashrc";
232 /* Make MC's special commands not show up in bash's history */
233 putenv ("HISTCONTROL=ignorespace");
235 /* Allow alternative readline settings for MC */
236 if (access (".mc/inputrc", R_OK
) == 0)
237 putenv ("INPUTRC=.mc/inputrc");
241 /* TODO: Find a way to pass initfile to TCSH and ZSH */
247 fprintf (stderr
, __FILE__
": unimplemented subshell type %d\r\n",
249 _exit (FORK_FAILURE
);
252 /* Attach all our standard file descriptors to the pty */
254 /* This is done just before the fork, because stderr must still */
255 /* be connected to the real tty during the above error messages; */
256 /* otherwise the user will never see them. */
258 dup2 (pty_slave
, STDIN_FILENO
);
259 dup2 (pty_slave
, STDOUT_FILENO
);
260 dup2 (pty_slave
, STDERR_FILENO
);
262 /* Execute the subshell at last */
264 close (subshell_pipe
[READ
]);
265 close (pty_slave
); /* These may be FD_CLOEXEC, but just in case... */
267 switch (subshell_type
) {
269 execl (shell
, "bash", "-rcfile", init_file
, (char *) NULL
);
273 execl (shell
, "tcsh", (char *) NULL
);
277 /* Use -g to exclude cmds beginning with space from history
278 * and -Z to use the line editor on non-interactive term */
279 execl (shell
, "zsh", "-Z", "-g", (char *) NULL
);
284 /* If we get this far, everything failed miserably */
285 _exit (FORK_FAILURE
);
291 * Check MC_SID to prevent running one mc from another.
293 * 0 if no parent mc in our session was found,
294 * 1 if parent mc was found and the user wants to continue,
295 * 2 if parent mc was found and the user wants to quit mc.
300 pid_t my_sid
, old_sid
;
304 sid_str
= getenv ("MC_SID");
308 old_sid
= (pid_t
) strtol (sid_str
, NULL
, 0);
316 /* The parent mc is in a different session, it's OK */
317 if (old_sid
!= my_sid
)
320 r
= query_dialog (_("Warning"),
321 _("GNU Midnight Commander is already\n"
322 "running on this terminal.\n"
323 "Subshell support will be disabled."), D_ERROR
, 2,
324 _("&OK"), _("&Quit"));
331 #endif /* HAVE_GETSID */
335 * Fork the subshell, and set up many, many things.
337 * Possibly modifies the global variables:
338 * subshell_type, subshell_alive, subshell_stopped, subshell_pid
339 * use_subshell - Is set to FALSE if we can't run the subshell
340 * quit - Can be set to SUBSHELL_EXIT by the SIGCHLD handler
346 /* This must be remembered across calls to init_subshell() */
347 static char pty_name
[BUF_SMALL
];
348 char precmd
[BUF_SMALL
];
352 switch (check_sid ()) {
354 use_subshell
= FALSE
;
357 use_subshell
= FALSE
;
358 midnight_shutdown
= 1;
361 #endif /* HAVE_GETSID */
363 /* Take the current (hopefully pristine) tty mode and make */
364 /* a raw mode based on it now, before we do anything else with it */
367 if (subshell_pty
== 0) { /* First time through */
368 /* Find out what type of shell we have */
370 if (strstr (shell
, "/zsh") || getenv ("ZSH_VERSION"))
372 else if (strstr (shell
, "/tcsh"))
373 subshell_type
= TCSH
;
374 else if (strstr (shell
, "/bash") || getenv ("BASH"))
375 subshell_type
= BASH
;
377 use_subshell
= FALSE
;
381 /* Open a pty for talking to the subshell */
383 /* FIXME: We may need to open a fresh pty each time on SVR4 */
385 subshell_pty
= pty_open_master (pty_name
);
386 if (subshell_pty
== -1) {
387 fprintf (stderr
, "Cannot open master side of pty: %s\r\n",
388 unix_error_string (errno
));
389 use_subshell
= FALSE
;
392 pty_slave
= pty_open_slave (pty_name
);
393 if (pty_slave
== -1) {
394 fprintf (stderr
, "Cannot open slave side of pty %s: %s\r\n",
395 pty_name
, unix_error_string (errno
));
396 use_subshell
= FALSE
;
400 /* Initialise the pty's I/O buffer */
402 pty_buffer_size
= INITIAL_PTY_BUFFER_SIZE
;
403 pty_buffer
= g_malloc (pty_buffer_size
);
405 /* Create a pipe for receiving the subshell's CWD */
407 if (subshell_type
== TCSH
) {
408 g_snprintf (tcsh_fifo
, sizeof (tcsh_fifo
), "%s/mc.pipe.%d",
409 mc_tmpdir (), (int) getpid ());
410 if (mkfifo (tcsh_fifo
, 0600) == -1) {
411 fprintf (stderr
, "mkfifo(%s) failed: %s\r\n", tcsh_fifo
,
412 unix_error_string (errno
));
413 use_subshell
= FALSE
;
417 /* Opening the FIFO as O_RDONLY or O_WRONLY causes deadlock */
419 if ((subshell_pipe
[READ
] = open (tcsh_fifo
, O_RDWR
)) == -1
420 || (subshell_pipe
[WRITE
] =
421 open (tcsh_fifo
, O_RDWR
)) == -1) {
422 fprintf (stderr
, _("Cannot open named pipe %s\n"), tcsh_fifo
);
423 perror (__FILE__
": open");
424 use_subshell
= FALSE
;
427 } else /* subshell_type is BASH or ZSH */ if (pipe (subshell_pipe
)) {
428 perror (__FILE__
": couldn't create pipe");
429 use_subshell
= FALSE
;
434 /* Fork the subshell */
436 subshell_alive
= TRUE
;
437 subshell_stopped
= FALSE
;
438 subshell_pid
= fork ();
440 if (subshell_pid
== -1) {
441 fprintf (stderr
, "Cannot spawn the subshell process: %s\r\n",
442 unix_error_string (errno
));
443 /* We exit here because, if the process table is full, the */
444 /* other method of running user commands won't work either */
448 if (subshell_pid
== 0) { /* We are in the child process */
449 init_subshell_child (pty_name
);
452 /* pty_slave is only opened when called the first time */
453 if (pty_slave
!= -1) {
457 /* Set up `precmd' or equivalent for reading the subshell's CWD */
459 switch (subshell_type
) {
461 g_snprintf (precmd
, sizeof (precmd
),
462 " PROMPT_COMMAND='pwd>&%d;kill -STOP $$'\n",
463 subshell_pipe
[WRITE
]);
467 g_snprintf (precmd
, sizeof (precmd
),
468 " precmd(){ pwd>&%d;kill -STOP $$ }\n",
469 subshell_pipe
[WRITE
]);
473 g_snprintf (precmd
, sizeof (precmd
),
474 "set echo_style=both;"
475 "alias precmd 'echo $cwd:q >>%s;kill -STOP $$'\n",
479 write (subshell_pty
, precmd
, strlen (precmd
));
481 /* Wait until the subshell has started up and processed the command */
483 subshell_state
= RUNNING_COMMAND
;
484 enable_interrupt_key ();
485 if (!feed_subshell (QUIETLY
, TRUE
)) {
486 use_subshell
= FALSE
;
488 disable_interrupt_key ();
490 use_subshell
= FALSE
; /* Subshell died instantly, so don't use it */
494 static void init_raw_mode ()
496 static int initialized
= 0;
498 /* MC calls reset_shell_mode() in pre_exec() to set the real tty to its */
499 /* original settings. However, here we need to make this tty very raw, */
500 /* so that all keyboard signals, XON/XOFF, etc. will get through to the */
501 /* pty. So, instead of changing the code for execute(), pre_exec(), */
502 /* etc, we just set up the modes we need here, before each command. */
504 if (initialized
== 0) /* First time: initialise `raw_mode' */
506 tcgetattr (STDOUT_FILENO
, &raw_mode
);
507 raw_mode
.c_lflag
&= ~ICANON
; /* Disable line-editing chars, etc. */
508 raw_mode
.c_lflag
&= ~ISIG
; /* Disable intr, quit & suspend chars */
509 raw_mode
.c_lflag
&= ~ECHO
; /* Disable input echoing */
510 raw_mode
.c_iflag
&= ~IXON
; /* Pass ^S/^Q to subshell undisturbed */
511 raw_mode
.c_iflag
&= ~ICRNL
; /* Don't translate CRs into LFs */
512 raw_mode
.c_oflag
&= ~OPOST
; /* Don't postprocess output */
513 raw_mode
.c_cc
[VTIME
] = 0; /* IE: wait forever, and return as */
514 raw_mode
.c_cc
[VMIN
] = 1; /* soon as a character is available */
520 int invoke_subshell (const char *command
, int how
, char **new_dir
)
522 /* Make the MC terminal transparent */
523 tcsetattr (STDOUT_FILENO
, TCSANOW
, &raw_mode
);
525 /* Make the subshell change to MC's working directory */
527 do_subshell_chdir (current_panel
->cwd
, TRUE
, 1);
529 if (command
== NULL
) /* The user has done "C-o" from MC */
531 if (subshell_state
== INACTIVE
)
533 subshell_state
= ACTIVE
;
534 /* FIXME: possibly take out this hack; the user can
535 re-play it by hitting C-hyphen a few times! */
536 write (subshell_pty
, " \b", 2); /* Hack to make prompt reappear */
539 else /* MC has passed us a user command */
542 write (subshell_pty
, " ", 1);
543 /* FIXME: if command is long (>8KB ?) we go comma */
544 write (subshell_pty
, command
, strlen (command
));
545 write (subshell_pty
, "\n", 1);
546 subshell_state
= RUNNING_COMMAND
;
547 subshell_ready
= FALSE
;
550 feed_subshell (how
, FALSE
);
552 if (new_dir
&& subshell_alive
&& strcmp (subshell_cwd
, current_panel
->cwd
))
553 *new_dir
= subshell_cwd
; /* Make MC change to the subshell's CWD */
555 /* Restart the subshell if it has died by SIGHUP, SIGQUIT, etc. */
556 while (!subshell_alive
&& !quit
&& use_subshell
)
566 read_subshell_prompt (void)
568 static int prompt_size
= INITIAL_PROMPT_SIZE
;
569 int bytes
= 0, i
, rc
= 0;
570 struct timeval timeleft
= { 0, 0 };
574 FD_SET (subshell_pty
, &tmp
);
576 if (subshell_prompt
== NULL
) { /* First time through */
577 subshell_prompt
= g_malloc (prompt_size
);
578 *subshell_prompt
= '\0';
582 while (subshell_alive
584 select (subshell_pty
+ 1, &tmp
, NULL
, NULL
, &timeleft
))) {
585 /* Check for `select' errors */
590 fprintf (stderr
, "select (FD_SETSIZE, &tmp...): %s\r\n",
591 unix_error_string (errno
));
596 bytes
= read (subshell_pty
, pty_buffer
, pty_buffer_size
);
598 /* Extract the prompt from the shell output */
600 for (i
= 0; i
< bytes
; ++i
)
601 if (pty_buffer
[i
] == '\n' || pty_buffer
[i
] == '\r') {
607 subshell_prompt
[prompt_pos
++] = pty_buffer
[i
];
608 if (prompt_pos
== prompt_size
)
610 g_realloc (subshell_prompt
, prompt_size
*= 2);
613 subshell_prompt
[prompt_pos
] = '\0';
615 if (rc
== 0 && bytes
== 0)
620 /* Resize given terminal using TIOCSWINSZ, return ioctl() result */
621 static int resize_tty (int fd
)
623 #if defined TIOCSWINSZ
624 struct winsize tty_size
;
626 tty_size
.ws_row
= LINES
;
627 tty_size
.ws_col
= COLS
;
628 tty_size
.ws_xpixel
= tty_size
.ws_ypixel
= 0;
630 return ioctl (fd
, TIOCSWINSZ
, &tty_size
);
636 /* Resize subshell_pty */
637 void resize_subshell (void)
639 resize_tty (subshell_pty
);
647 if (subshell_state
!= INACTIVE
&& subshell_alive
)
649 !query_dialog (_("Warning"),
650 _(" The shell is still active. Quit anyway? "),
651 0, 2, _("&Yes"), _("&No"));
654 if (subshell_type
== TCSH
) {
655 if (unlink (tcsh_fifo
) == -1)
656 fprintf (stderr
, "Cannot remove named pipe %s: %s\r\n",
657 tcsh_fifo
, unix_error_string (errno
));
660 g_free (subshell_prompt
);
662 subshell_prompt
= NULL
;
671 * Carefully quote directory name to allow entering any directory safely,
672 * no matter what weird characters it may contain in its name.
673 * NOTE: Treat directory name an untrusted data, don't allow it to cause
674 * executing any commands in the shell. Escape all control characters.
675 * Use following technique:
677 * for bash - echo with `-e', 3-digit octal numbers:
678 * cd "`echo -e '\ooo...\ooo'`"
680 * for zsh - echo with `-e', 4-digit octal numbers:
681 * cd "`echo '\oooo...\oooo'`"
683 * for tcsh - echo without `-e', 4-digit octal numbers:
684 * cd "`echo '\oooo...\oooo'`"
687 subshell_name_quote (const char *s
)
690 const char echo_cmd
[] = "\"`echo '";
691 const char echo_e_cmd
[] = "\"`echo -e '";
692 const char common_end
[] = "'`\"";
693 const char *cmd_start
;
697 * Factor 5 because we need \, 0 and 3 other digits per character
698 * in the worst case (tcsh and zsh).
700 d
= ret
= g_malloc (5 * strlen (s
) + 16);
704 /* Prevent interpreting leading `-' as a switch for `cd' */
710 /* echo in tcsh doesn't understand the "-e" option */
711 if (subshell_type
== TCSH
)
712 cmd_start
= echo_cmd
;
714 cmd_start
= echo_e_cmd
;
716 /* Copy the beginning of the command to the buffer */
717 len
= strlen (cmd_start
);
718 memcpy (d
, cmd_start
, len
);
722 * Print every character in octal format with the leading backslash.
723 * tcsh and zsh may require 4-digit octals, bash < 2.05b doesn't like them.
725 if (subshell_type
== BASH
) {
727 /* Must quote numbers, so that they are not glued to octals */
728 if (isalpha ((unsigned char) *s
)) {
729 *d
++ = (unsigned char) *s
;
731 sprintf (d
, "\\%03o", (unsigned char) *s
);
737 if (isalnum ((unsigned char) *s
)) {
738 *d
++ = (unsigned char) *s
;
740 sprintf (d
, "\\0%03o", (unsigned char) *s
);
746 memcpy (d
, common_end
, sizeof (common_end
));
752 /* If it actually changed the directory it returns true */
754 do_subshell_chdir (const char *directory
, int do_update
, int reset_prompt
)
757 (subshell_state
== INACTIVE
758 && strcmp (subshell_cwd
, current_panel
->cwd
))) {
759 /* We have to repaint the subshell prompt if we read it from
760 * the main program. Please note that in the code after this
761 * if, the cd command that is sent will make the subshell
762 * repaint the prompt, so we don't have to paint it. */
768 /* The initial space keeps this out of the command history (in bash
769 because we set "HISTCONTROL=ignorespace") */
770 write (subshell_pty
, " cd ", 4);
772 char *temp
= subshell_name_quote (directory
);
774 write (subshell_pty
, temp
, strlen (temp
));
777 /* Should not happen unless the directory name is so long
778 that we don't have memory to quote it. */
779 write (subshell_pty
, ".", 1);
782 write (subshell_pty
, "/", 1);
784 write (subshell_pty
, "\n", 1);
786 subshell_state
= RUNNING_COMMAND
;
787 feed_subshell (QUIETLY
, FALSE
);
789 if (subshell_alive
) {
790 int bPathNotEq
= strcmp (subshell_cwd
, current_panel
->cwd
);
792 if (bPathNotEq
&& subshell_type
== TCSH
) {
793 char rp_subshell_cwd
[PATH_MAX
];
794 char rp_current_panel_cwd
[PATH_MAX
];
796 char *p_subshell_cwd
=
797 mc_realpath (subshell_cwd
, rp_subshell_cwd
);
798 char *p_current_panel_cwd
=
799 mc_realpath (current_panel
->cwd
, rp_current_panel_cwd
);
801 if (p_subshell_cwd
== NULL
)
802 p_subshell_cwd
= subshell_cwd
;
803 if (p_current_panel_cwd
== NULL
)
804 p_current_panel_cwd
= current_panel
->cwd
;
805 bPathNotEq
= strcmp (p_subshell_cwd
, p_current_panel_cwd
);
808 if (bPathNotEq
&& strcmp (current_panel
->cwd
, ".")) {
809 char *cwd
= strip_password (g_strdup (current_panel
->cwd
), 1);
810 fprintf (stderr
, _("Warning: Cannot change to %s.\n"), cwd
);
817 update_prompt
= FALSE
;
818 /* Make sure that MC never stores the CWD in a silly format */
819 /* like /usr////lib/../bin, or the strcmp() above will fail */
824 subshell_get_console_attributes (void)
826 /* Get our current terminal modes */
828 if (tcgetattr (STDOUT_FILENO
, &shell_mode
)) {
829 fprintf (stderr
, "Cannot get terminal settings: %s\r\n",
830 unix_error_string (errno
));
831 use_subshell
= FALSE
;
837 /* Figure out whether the subshell has stopped, exited or been killed */
838 /* Possibly modifies: `subshell_alive', `subshell_stopped' and `quit' */
840 sigchld_handler (int sig
)
847 pid
= waitpid (subshell_pid
, &status
, WUNTRACED
| WNOHANG
);
849 if (pid
== subshell_pid
) {
850 /* Figure out what has happened to the subshell */
852 if (WIFSTOPPED (status
)) {
853 if (WSTOPSIG (status
) == SIGSTOP
) {
854 /* The subshell has received a SIGSTOP signal */
855 subshell_stopped
= TRUE
;
857 /* The user has suspended the subshell. Revive it */
858 kill (subshell_pid
, SIGCONT
);
861 /* The subshell has either exited normally or been killed */
862 subshell_alive
= FALSE
;
863 delete_select_channel (subshell_pty
);
864 if (WIFEXITED (status
) && WEXITSTATUS (status
) != FORK_FAILURE
)
865 quit
|= SUBSHELL_EXIT
; /* Exited normally */
869 pid
= waitpid (cons_saver_pid
, &status
, WUNTRACED
| WNOHANG
);
871 if (pid
== cons_saver_pid
) {
873 if (WIFSTOPPED (status
))
874 /* Someone has stopped cons.saver - restart it */
877 /* cons.saver has died - disable confole saving */
878 handle_console (CONSOLE_DONE
);
883 #endif /* __linux__ */
885 /* If we got here, some other child exited; ignore it */
886 #ifdef __EMX__ /* Need to report */
887 pid
= wait (&status
);
892 /* Feed the subshell our keyboard input until it says it's finished */
894 feed_subshell (int how
, int fail_on_error
)
896 fd_set read_set
; /* For `select' */
898 int bytes
; /* For the return value from `read' */
899 int i
; /* Loop counter */
901 struct timeval wtime
; /* Maximum time we wait for the subshell */
902 struct timeval
*wptr
;
904 /* we wait up to 10 seconds if fail_on_error, forever otherwise */
907 wptr
= fail_on_error
? &wtime
: NULL
;
913 /* Prepare the file-descriptor set and call `select' */
916 FD_SET (subshell_pty
, &read_set
);
917 FD_SET (subshell_pipe
[READ
], &read_set
);
918 maxfdp
= max (subshell_pty
, subshell_pipe
[READ
]);
919 if (how
== VISIBLY
) {
920 FD_SET (STDIN_FILENO
, &read_set
);
921 maxfdp
= max (maxfdp
, STDIN_FILENO
);
924 if (select (maxfdp
+ 1, &read_set
, NULL
, NULL
, wptr
) == -1) {
926 /* Despite using SA_RESTART, we still have to check for this */
928 continue; /* try all over again */
929 tcsetattr (STDOUT_FILENO
, TCSANOW
, &shell_mode
);
930 fprintf (stderr
, "select (FD_SETSIZE, &read_set...): %s\r\n",
931 unix_error_string (errno
));
935 if (FD_ISSET (subshell_pty
, &read_set
))
936 /* Read from the subshell, write to stdout */
938 /* This loop improves performance by reducing context switches
939 by a factor of 20 or so... unfortunately, it also hangs MC
940 randomly, because of an apparent Linux bug. Investigate. */
941 /* for (i=0; i<5; ++i) * FIXME -- experimental */
943 bytes
= read (subshell_pty
, pty_buffer
, pty_buffer_size
);
945 /* The subshell has died */
946 if (bytes
== -1 && errno
== EIO
&& !subshell_alive
)
950 tcsetattr (STDOUT_FILENO
, TCSANOW
, &shell_mode
);
951 fprintf (stderr
, "read (subshell_pty...): %s\r\n",
952 unix_error_string (errno
));
957 write (STDOUT_FILENO
, pty_buffer
, bytes
);
960 else if (FD_ISSET (subshell_pipe
[READ
], &read_set
))
961 /* Read the subshell's CWD and capture its prompt */
965 read (subshell_pipe
[READ
], subshell_cwd
,
968 tcsetattr (STDOUT_FILENO
, TCSANOW
, &shell_mode
);
969 fprintf (stderr
, "read (subshell_pipe[READ]...): %s\r\n",
970 unix_error_string (errno
));
974 subshell_cwd
[bytes
- 1] = 0; /* Squash the final '\n' */
978 subshell_ready
= TRUE
;
979 if (subshell_state
== RUNNING_COMMAND
) {
980 subshell_state
= INACTIVE
;
985 else if (FD_ISSET (STDIN_FILENO
, &read_set
))
986 /* Read from stdin, write to the subshell */
988 bytes
= read (STDIN_FILENO
, pty_buffer
, pty_buffer_size
);
990 tcsetattr (STDOUT_FILENO
, TCSANOW
, &shell_mode
);
992 "read (STDIN_FILENO, pty_buffer...): %s\r\n",
993 unix_error_string (errno
));
997 for (i
= 0; i
< bytes
; ++i
)
998 if (pty_buffer
[i
] == subshell_switch_key
) {
999 write (subshell_pty
, pty_buffer
, i
);
1001 subshell_state
= INACTIVE
;
1005 write (subshell_pty
, pty_buffer
, bytes
);
1006 subshell_ready
= FALSE
;
1014 /* Wait until the subshell dies or stops. If it stops, make it resume. */
1015 /* Possibly modifies the globals `subshell_alive' and `subshell_stopped' */
1016 static void synchronize (void)
1018 sigset_t sigchld_mask
, old_mask
;
1020 sigemptyset (&sigchld_mask
);
1021 sigaddset (&sigchld_mask
, SIGCHLD
);
1022 sigprocmask (SIG_BLOCK
, &sigchld_mask
, &old_mask
);
1025 * SIGCHLD should not be blocked, but we unblock it just in case.
1026 * This is known to be useful for cygwin 1.3.12 and older.
1028 sigdelset (&old_mask
, SIGCHLD
);
1030 /* Wait until the subshell has stopped */
1031 while (subshell_alive
&& !subshell_stopped
)
1032 sigsuspend (&old_mask
);
1034 /* Discard all remaining data from stdin to the subshell */
1035 tcflush (subshell_pty
, TCOFLUSH
);
1037 subshell_stopped
= FALSE
;
1038 kill (subshell_pid
, SIGCONT
);
1040 sigprocmask (SIG_SETMASK
, &old_mask
, NULL
);
1041 /* We can't do any better without modifying the shell(s) */
1044 /* pty opening functions */
1048 /* System V version of pty_open_master */
1050 static int pty_open_master (char *pty_name
)
1055 #ifdef HAVE_POSIX_OPENPT
1056 pty_master
= posix_openpt(O_RDWR
);
1058 /* getpt () is a GNU extension (glibc 2.1.x) */
1059 pty_master
= getpt ();
1061 strcpy (pty_name
, "/dev/ptc");
1062 pty_master
= open (pty_name
, O_RDWR
);
1064 strcpy (pty_name
, "/dev/ptmx");
1065 pty_master
= open (pty_name
, O_RDWR
);
1068 if (pty_master
== -1)
1071 if (grantpt (pty_master
) == -1 /* Grant access to slave */
1072 || unlockpt (pty_master
) == -1 /* Clear slave's lock flag */
1073 || !(slave_name
= ptsname (pty_master
))) /* Get slave's name */
1078 strcpy (pty_name
, slave_name
);
1082 /* System V version of pty_open_slave */
1084 pty_open_slave (const char *pty_name
)
1086 int pty_slave
= open (pty_name
, O_RDWR
);
1088 if (pty_slave
== -1) {
1089 fprintf (stderr
, "open (%s, O_RDWR): %s\r\n", pty_name
,
1090 unix_error_string (errno
));
1093 #if !defined(__osf__) && !defined(__linux__)
1094 #if defined (I_FIND) && defined (I_PUSH)
1095 if (!ioctl (pty_slave
, I_FIND
, "ptem"))
1096 if (ioctl (pty_slave
, I_PUSH
, "ptem") == -1) {
1097 fprintf (stderr
, "ioctl (%d, I_PUSH, \"ptem\") failed: %s\r\n",
1098 pty_slave
, unix_error_string (errno
));
1103 if (!ioctl (pty_slave
, I_FIND
, "ldterm"))
1104 if (ioctl (pty_slave
, I_PUSH
, "ldterm") == -1) {
1106 "ioctl (%d, I_PUSH, \"ldterm\") failed: %s\r\n",
1107 pty_slave
, unix_error_string (errno
));
1111 #if !defined(sgi) && !defined(__sgi)
1112 if (!ioctl (pty_slave
, I_FIND
, "ttcompat"))
1113 if (ioctl (pty_slave
, I_PUSH
, "ttcompat") == -1) {
1115 "ioctl (%d, I_PUSH, \"ttcompat\") failed: %s\r\n",
1116 pty_slave
, unix_error_string (errno
));
1120 #endif /* sgi || __sgi */
1121 #endif /* I_FIND && I_PUSH */
1122 #endif /* __osf__ || __linux__ */
1127 #else /* !HAVE_GRANTPT */
1129 /* BSD version of pty_open_master */
1130 static int pty_open_master (char *pty_name
)
1133 const char *ptr1
, *ptr2
;
1135 strcpy (pty_name
, "/dev/ptyXX");
1136 for (ptr1
= "pqrstuvwxyzPQRST"; *ptr1
; ++ptr1
)
1138 pty_name
[8] = *ptr1
;
1139 for (ptr2
= "0123456789abcdef"; *ptr2
; ++ptr2
)
1141 pty_name
[9] = *ptr2
;
1143 /* Try to open master */
1144 if ((pty_master
= open (pty_name
, O_RDWR
)) == -1) {
1145 if (errno
== ENOENT
) /* Different from EIO */
1146 return -1; /* Out of pty devices */
1148 continue; /* Try next pty device */
1150 pty_name
[5] = 't'; /* Change "pty" to "tty" */
1151 if (access (pty_name
, 6)){
1159 return -1; /* Ran out of pty devices */
1162 /* BSD version of pty_open_slave */
1164 pty_open_slave (const char *pty_name
)
1167 struct group
*group_info
= getgrnam ("tty");
1169 if (group_info
!= NULL
) {
1170 /* The following two calls will only succeed if we are root */
1171 /* [Commented out while permissions problem is investigated] */
1172 /* chown (pty_name, getuid (), group_info->gr_gid); FIXME */
1173 /* chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP); FIXME */
1175 if ((pty_slave
= open (pty_name
, O_RDWR
)) == -1)
1176 fprintf (stderr
, "open (pty_name, O_RDWR): %s\r\n", pty_name
);
1180 #endif /* !HAVE_GRANTPT */
1181 #endif /* HAVE_SUBSHELL_SUPPORT */