1 /* Concurrent shell support for the Midnight Commander
2 Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
3 2005, 2006, 2007 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of Version 2 of the GNU General Public
7 License, as published by the Free Software Foundation.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * \brief Source: concurrent shell support
25 #ifdef HAVE_SUBSHELL_SUPPORT
28 # define _GNU_SOURCE 1
38 #include <sys/types.h>
39 #ifdef HAVE_SYS_IOCTL_H
40 # include <sys/ioctl.h>
48 # include <stropts.h> /* For I_PUSH */
49 #endif /* HAVE_STROPTS_H */
52 #include "tty.h" /* LINES */
53 #include "panel.h" /* current_panel */
54 #include "wtools.h" /* query_dialog() */
55 #include "main.h" /* do_update_prompt() */
56 #include "cons.saver.h" /* handle_console() */
57 #include "key.h" /* XCTRL */
62 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
66 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
69 /* tcsh closes all non-standard file descriptors, so we have to use a pipe */
70 static char tcsh_fifo
[128];
73 static void init_raw_mode (void);
74 static int feed_subshell (int how
, int fail_on_error
);
75 static void synchronize (void);
76 static int pty_open_master (char *pty_name
);
77 static int pty_open_slave (const char *pty_name
);
78 static int resize_tty (int fd
);
81 # define STDIN_FILENO 0
85 # define STDOUT_FILENO 1
89 # define STDERR_FILENO 2
92 /* If using a subshell for evaluating commands this is true */
94 #ifdef SUBSHELL_OPTIONAL
100 /* File descriptors of the pseudoterminal used by the subshell */
101 int subshell_pty
= 0;
102 static int subshell_pty_slave
= -1;
104 /* The key for switching back to MC from the subshell */
105 static const char subshell_switch_key
= XCTRL('o') & 255;
107 /* State of the subshell:
108 * INACTIVE: the default state; awaiting a command
109 * ACTIVE: remain in the shell until the user hits `subshell_switch_key'
110 * RUNNING_COMMAND: return to MC when the current command finishes */
111 enum subshell_state_enum subshell_state
;
113 /* Holds the latest prompt captured from the subshell */
114 char *subshell_prompt
= NULL
;
116 /* Initial length of the buffer for the subshell's prompt */
117 #define INITIAL_PROMPT_SIZE 10
119 /* Used by the child process to indicate failure to start the subshell */
120 #define FORK_FAILURE 69 /* Arbitrary */
122 /* Initial length of the buffer for all I/O with the subshell */
123 #define INITIAL_PTY_BUFFER_SIZE 100 /* Arbitrary; but keep it >= 80 */
126 enum {READ
=0, WRITE
=1};
128 static char *pty_buffer
; /* For reading/writing on the subshell's pty */
129 static int pty_buffer_size
; /* The buffer grows as needed */
130 static int subshell_pipe
[2]; /* To pass CWD info from the subshell to MC */
131 static pid_t subshell_pid
= 1; /* The subshell's process ID */
132 static char subshell_cwd
[MC_MAXPATHLEN
+1]; /* One extra char for final '\n' */
134 /* Subshell type (gleaned from the SHELL environment variable, if available) */
135 static enum {BASH
, TCSH
, ZSH
} subshell_type
;
137 /* Flag to indicate whether the subshell is ready for next command */
138 static int subshell_ready
;
140 /* The following two flags can be changed by the SIGCHLD handler. This is */
141 /* OK, because the `int' type is updated atomically on all known machines */
142 static volatile int subshell_alive
, subshell_stopped
;
144 /* We store the terminal's initial mode here so that we can configure
145 the pty similarly, and also so we can restore the real terminal to
146 sanity if we have to exit abruptly */
147 static struct termios shell_mode
;
149 /* This is a transparent mode for the terminal where MC is running on */
150 /* It is used when the shell is active, so that the control signals */
151 /* are delivered to the shell pty */
152 static struct termios raw_mode
;
154 /* This counter indicates how many characters of prompt we have read */
155 /* FIXME: try to figure out why this had to become global */
156 static int prompt_pos
;
160 * Write all data, even if the write() call is interrupted.
163 write_all (int fd
, const void *buf
, size_t count
)
168 ret
= write (fd
, (const unsigned char *) buf
+ written
, count
);
170 if (errno
== EINTR
) {
173 return written
> 0 ? written
: ret
;
183 * Prepare child process to running the shell and run it.
185 * Modifies the global variables (in the child process only):
191 init_subshell_child (const char *pty_name
)
193 const char *init_file
= NULL
;
196 #endif /* HAVE_GETSID */
199 setsid (); /* Get a fresh terminal session */
201 /* Make sure that it has become our controlling terminal */
203 /* Redundant on Linux and probably most systems, but just in case: */
206 ioctl (subshell_pty_slave
, TIOCSCTTY
, 0);
209 /* Configure its terminal modes and window size */
211 /* Set up the pty with the same termios flags as our own tty, plus */
212 /* TOSTOP, which keeps background processes from writing to the pty */
214 shell_mode
.c_lflag
|= TOSTOP
; /* So background writers get SIGTTOU */
215 if (tcsetattr (subshell_pty_slave
, TCSANOW
, &shell_mode
)) {
216 fprintf (stderr
, "Cannot set pty terminal modes: %s\r\n",
217 unix_error_string (errno
));
218 _exit (FORK_FAILURE
);
221 /* Set the pty's size (80x25 by default on Linux) according to the */
222 /* size of the real terminal as calculated by ncurses, if possible */
223 resize_tty (subshell_pty_slave
);
225 /* Set up the subshell's environment and init file name */
227 /* It simplifies things to change to our home directory here, */
228 /* and the user's startup file may do a `cd' command anyway */
229 chdir (home_dir
); /* FIXME? What about when we re-run the subshell? */
232 /* Set MC_SID to prevent running one mc from another */
235 char sid_str
[BUF_SMALL
];
236 g_snprintf (sid_str
, sizeof (sid_str
), "MC_SID=%ld",
238 putenv (g_strdup (sid_str
));
240 #endif /* HAVE_GETSID */
242 switch (subshell_type
) {
244 init_file
= ".mc/bashrc";
245 if (access (init_file
, R_OK
) == -1)
246 init_file
= ".bashrc";
248 /* Make MC's special commands not show up in bash's history */
249 putenv ("HISTCONTROL=ignorespace");
251 /* Allow alternative readline settings for MC */
252 if (access (".mc/inputrc", R_OK
) == 0)
253 putenv ("INPUTRC=.mc/inputrc");
257 /* TODO: Find a way to pass initfile to TCSH and ZSH */
263 fprintf (stderr
, __FILE__
": unimplemented subshell type %d\r\n",
265 _exit (FORK_FAILURE
);
268 /* Attach all our standard file descriptors to the pty */
270 /* This is done just before the fork, because stderr must still */
271 /* be connected to the real tty during the above error messages; */
272 /* otherwise the user will never see them. */
274 dup2 (subshell_pty_slave
, STDIN_FILENO
);
275 dup2 (subshell_pty_slave
, STDOUT_FILENO
);
276 dup2 (subshell_pty_slave
, STDERR_FILENO
);
278 close (subshell_pipe
[READ
]);
279 close (subshell_pty_slave
); /* These may be FD_CLOEXEC, but just in case... */
280 /* Close master side of pty. This is important; apart from */
281 /* freeing up the descriptor for use in the subshell, it also */
282 /* means that when MC exits, the subshell will get a SIGHUP and */
283 /* exit too, because there will be no more descriptors pointing */
284 /* at the master side of the pty and so it will disappear. */
285 close (subshell_pty
);
287 /* Execute the subshell at last */
289 switch (subshell_type
) {
291 execl (shell
, "bash", "-rcfile", init_file
, (char *) NULL
);
295 execl (shell
, "tcsh", (char *) NULL
);
299 /* Use -g to exclude cmds beginning with space from history
300 * and -Z to use the line editor on non-interactive term */
301 execl (shell
, "zsh", "-Z", "-g", (char *) NULL
);
306 /* If we get this far, everything failed miserably */
307 _exit (FORK_FAILURE
);
313 * Check MC_SID to prevent running one mc from another.
315 * 0 if no parent mc in our session was found,
316 * 1 if parent mc was found and the user wants to continue,
317 * 2 if parent mc was found and the user wants to quit mc.
322 pid_t my_sid
, old_sid
;
326 sid_str
= getenv ("MC_SID");
330 old_sid
= (pid_t
) strtol (sid_str
, NULL
, 0);
338 /* The parent mc is in a different session, it's OK */
339 if (old_sid
!= my_sid
)
342 r
= query_dialog (_("Warning"),
343 _("GNU Midnight Commander is already\n"
344 "running on this terminal.\n"
345 "Subshell support will be disabled."), D_ERROR
, 2,
346 _("&OK"), _("&Quit"));
353 #endif /* HAVE_GETSID */
357 * Fork the subshell, and set up many, many things.
359 * Possibly modifies the global variables:
360 * subshell_type, subshell_alive, subshell_stopped, subshell_pid
361 * use_subshell - Is set to FALSE if we can't run the subshell
362 * quit - Can be set to SUBSHELL_EXIT by the SIGCHLD handler
368 /* This must be remembered across calls to init_subshell() */
369 static char pty_name
[BUF_SMALL
];
370 char precmd
[BUF_SMALL
];
373 switch (check_sid ()) {
375 use_subshell
= FALSE
;
378 use_subshell
= FALSE
;
379 midnight_shutdown
= 1;
382 #endif /* HAVE_GETSID */
384 /* Take the current (hopefully pristine) tty mode and make */
385 /* a raw mode based on it now, before we do anything else with it */
388 if (subshell_pty
== 0) { /* First time through */
389 /* Find out what type of shell we have */
391 if (strstr (shell
, "/zsh") || getenv ("ZSH_VERSION"))
393 else if (strstr (shell
, "/tcsh"))
394 subshell_type
= TCSH
;
395 else if (strstr (shell
, "/bash") || getenv ("BASH"))
396 subshell_type
= BASH
;
398 use_subshell
= FALSE
;
402 /* Open a pty for talking to the subshell */
404 /* FIXME: We may need to open a fresh pty each time on SVR4 */
406 subshell_pty
= pty_open_master (pty_name
);
407 if (subshell_pty
== -1) {
408 fprintf (stderr
, "Cannot open master side of pty: %s\r\n",
409 unix_error_string (errno
));
410 use_subshell
= FALSE
;
413 subshell_pty_slave
= pty_open_slave (pty_name
);
414 if (subshell_pty_slave
== -1) {
415 fprintf (stderr
, "Cannot open slave side of pty %s: %s\r\n",
416 pty_name
, unix_error_string (errno
));
417 use_subshell
= FALSE
;
421 /* Initialise the pty's I/O buffer */
423 pty_buffer_size
= INITIAL_PTY_BUFFER_SIZE
;
424 pty_buffer
= g_malloc (pty_buffer_size
);
426 /* Create a pipe for receiving the subshell's CWD */
428 if (subshell_type
== TCSH
) {
429 g_snprintf (tcsh_fifo
, sizeof (tcsh_fifo
), "%s/mc.pipe.%d",
430 mc_tmpdir (), (int) getpid ());
431 if (mkfifo (tcsh_fifo
, 0600) == -1) {
432 fprintf (stderr
, "mkfifo(%s) failed: %s\r\n", tcsh_fifo
,
433 unix_error_string (errno
));
434 use_subshell
= FALSE
;
438 /* Opening the FIFO as O_RDONLY or O_WRONLY causes deadlock */
440 if ((subshell_pipe
[READ
] = open (tcsh_fifo
, O_RDWR
)) == -1
441 || (subshell_pipe
[WRITE
] =
442 open (tcsh_fifo
, O_RDWR
)) == -1) {
443 fprintf (stderr
, _("Cannot open named pipe %s\n"), tcsh_fifo
);
444 perror (__FILE__
": open");
445 use_subshell
= FALSE
;
448 } else /* subshell_type is BASH or ZSH */ if (pipe (subshell_pipe
)) {
449 perror (__FILE__
": couldn't create pipe");
450 use_subshell
= FALSE
;
455 /* Fork the subshell */
457 subshell_alive
= TRUE
;
458 subshell_stopped
= FALSE
;
459 subshell_pid
= fork ();
461 if (subshell_pid
== -1) {
462 fprintf (stderr
, "Cannot spawn the subshell process: %s\r\n",
463 unix_error_string (errno
));
464 /* We exit here because, if the process table is full, the */
465 /* other method of running user commands won't work either */
469 if (subshell_pid
== 0) { /* We are in the child process */
470 init_subshell_child (pty_name
);
473 /* Set up `precmd' or equivalent for reading the subshell's CWD */
475 switch (subshell_type
) {
477 g_snprintf (precmd
, sizeof (precmd
),
478 " PROMPT_COMMAND='pwd>&%d;kill -STOP $$'\n",
479 subshell_pipe
[WRITE
]);
483 g_snprintf (precmd
, sizeof (precmd
),
484 " precmd(){ pwd>&%d;kill -STOP $$ }\n",
485 subshell_pipe
[WRITE
]);
489 g_snprintf (precmd
, sizeof (precmd
),
490 "set echo_style=both;"
491 "alias precmd 'echo $cwd:q >>%s;kill -STOP $$'\n",
495 write_all (subshell_pty
, precmd
, strlen (precmd
));
497 /* Wait until the subshell has started up and processed the command */
499 subshell_state
= RUNNING_COMMAND
;
500 enable_interrupt_key ();
501 if (!feed_subshell (QUIETLY
, TRUE
)) {
502 use_subshell
= FALSE
;
504 disable_interrupt_key ();
506 use_subshell
= FALSE
; /* Subshell died instantly, so don't use it */
510 static void init_raw_mode ()
512 static int initialized
= 0;
514 /* MC calls reset_shell_mode() in pre_exec() to set the real tty to its */
515 /* original settings. However, here we need to make this tty very raw, */
516 /* so that all keyboard signals, XON/XOFF, etc. will get through to the */
517 /* pty. So, instead of changing the code for execute(), pre_exec(), */
518 /* etc, we just set up the modes we need here, before each command. */
520 if (initialized
== 0) /* First time: initialise `raw_mode' */
522 tcgetattr (STDOUT_FILENO
, &raw_mode
);
523 raw_mode
.c_lflag
&= ~ICANON
; /* Disable line-editing chars, etc. */
524 raw_mode
.c_lflag
&= ~ISIG
; /* Disable intr, quit & suspend chars */
525 raw_mode
.c_lflag
&= ~ECHO
; /* Disable input echoing */
526 raw_mode
.c_iflag
&= ~IXON
; /* Pass ^S/^Q to subshell undisturbed */
527 raw_mode
.c_iflag
&= ~ICRNL
; /* Don't translate CRs into LFs */
528 raw_mode
.c_oflag
&= ~OPOST
; /* Don't postprocess output */
529 raw_mode
.c_cc
[VTIME
] = 0; /* IE: wait forever, and return as */
530 raw_mode
.c_cc
[VMIN
] = 1; /* soon as a character is available */
536 int invoke_subshell (const char *command
, int how
, char **new_dir
)
540 /* Make the MC terminal transparent */
541 tcsetattr (STDOUT_FILENO
, TCSANOW
, &raw_mode
);
543 /* Make the subshell change to MC's working directory */
545 do_subshell_chdir (current_panel
->cwd
, TRUE
, 1);
547 if (command
== NULL
) /* The user has done "C-o" from MC */
549 if (subshell_state
== INACTIVE
)
551 subshell_state
= ACTIVE
;
552 /* FIXME: possibly take out this hack; the user can
553 re-play it by hitting C-hyphen a few times! */
554 write_all (subshell_pty
, " \b", 2); /* Hack to make prompt reappear */
557 else /* MC has passed us a user command */
560 write_all (subshell_pty
, " ", 1);
561 /* FIXME: if command is long (>8KB ?) we go comma */
562 write_all (subshell_pty
, command
, strlen (command
));
563 write_all (subshell_pty
, "\n", 1);
564 subshell_state
= RUNNING_COMMAND
;
565 subshell_ready
= FALSE
;
568 feed_subshell (how
, FALSE
);
570 pcwd
= vfs_translate_path_n (current_panel
->cwd
);
571 if (new_dir
&& subshell_alive
&& strcmp (subshell_cwd
, pcwd
))
572 *new_dir
= subshell_cwd
; /* Make MC change to the subshell's CWD */
575 /* Restart the subshell if it has died by SIGHUP, SIGQUIT, etc. */
576 while (!subshell_alive
&& !quit
&& use_subshell
)
586 read_subshell_prompt (void)
588 static int prompt_size
= INITIAL_PROMPT_SIZE
;
589 int bytes
= 0, i
, rc
= 0;
590 struct timeval timeleft
= { 0, 0 };
594 FD_SET (subshell_pty
, &tmp
);
596 if (subshell_prompt
== NULL
) { /* First time through */
597 subshell_prompt
= g_malloc (prompt_size
);
598 *subshell_prompt
= '\0';
602 while (subshell_alive
604 select (subshell_pty
+ 1, &tmp
, NULL
, NULL
, &timeleft
))) {
605 /* Check for `select' errors */
610 fprintf (stderr
, "select (FD_SETSIZE, &tmp...): %s\r\n",
611 unix_error_string (errno
));
616 bytes
= read (subshell_pty
, pty_buffer
, pty_buffer_size
);
618 /* Extract the prompt from the shell output */
620 for (i
= 0; i
< bytes
; ++i
)
621 if (pty_buffer
[i
] == '\n' || pty_buffer
[i
] == '\r') {
627 subshell_prompt
[prompt_pos
++] = pty_buffer
[i
];
628 if (prompt_pos
== prompt_size
)
630 g_realloc (subshell_prompt
, prompt_size
*= 2);
633 subshell_prompt
[prompt_pos
] = '\0';
635 if (rc
== 0 && bytes
== 0)
640 /* Resize given terminal using TIOCSWINSZ, return ioctl() result */
641 static int resize_tty (int fd
)
643 #if defined TIOCSWINSZ
644 struct winsize tty_size
;
646 tty_size
.ws_row
= LINES
;
647 tty_size
.ws_col
= COLS
;
648 tty_size
.ws_xpixel
= tty_size
.ws_ypixel
= 0;
650 return ioctl (fd
, TIOCSWINSZ
, &tty_size
);
656 /* Resize subshell_pty */
657 void resize_subshell (void)
659 if (use_subshell
== 0)
662 resize_tty (subshell_pty
);
668 int subshell_quit
= TRUE
;
670 if (subshell_state
!= INACTIVE
&& subshell_alive
)
672 !query_dialog (_("Warning"),
673 _(" The shell is still active. Quit anyway? "),
674 D_NORMAL
, 2, _("&Yes"), _("&No"));
677 if (subshell_type
== TCSH
) {
678 if (unlink (tcsh_fifo
) == -1)
679 fprintf (stderr
, "Cannot remove named pipe %s: %s\r\n",
680 tcsh_fifo
, unix_error_string (errno
));
683 g_free (subshell_prompt
);
685 subshell_prompt
= NULL
;
689 return subshell_quit
;
694 * Carefully quote directory name to allow entering any directory safely,
695 * no matter what weird characters it may contain in its name.
696 * NOTE: Treat directory name an untrusted data, don't allow it to cause
697 * executing any commands in the shell. Escape all control characters.
698 * Use following technique:
700 * printf(1) with format string containing a single conversion specifier,
701 * "b", and an argument which contains a copy of the string passed to
702 * subshell_name_quote() with all characters, except digits and letters,
703 * replaced by the backslash-escape sequence \0nnn, where "nnn" is the
704 * numeric value of the character converted to octal number.
706 * cd "`printf "%b" 'ABC\0nnnDEF\0nnnXYZ'`"
710 subshell_name_quote (const char *s
)
714 const char quote_cmd_start
[] = "\"`printf \"%b\" '";
715 const char quote_cmd_end
[] = "'`\"";
718 /* Factor 5 because we need \, 0 and 3 other digits per character. */
719 d
= ret
= g_malloc (1 + (5 * strlen (s
)) + (sizeof(quote_cmd_start
) - 1)
720 + (sizeof(quote_cmd_end
) - 1));
724 /* Prevent interpreting leading `-' as a switch for `cd' */
730 /* Copy the beginning of the command to the buffer */
731 strcpy (d
, quote_cmd_start
);
732 d
+= sizeof(quote_cmd_start
) - 1;
735 * Print every character except digits and letters as a backslash-escape
736 * sequence of the form \0nnn, where "nnn" is the numeric value of the
737 * character converted to octal number.
740 for (; su
[0] != '\0'; ) {
741 n
= str_cget_next_char_safe (su
);
742 if (str_isalnum (su
)) {
743 memcpy (d
, su
, n
- su
);
746 for (c
= 0; c
< n
- su
; c
++) {
747 sprintf (d
, "\\0%03o", (unsigned char) su
[c
]);
754 strcpy (d
, quote_cmd_end
);
760 /* If it actually changed the directory it returns true */
762 do_subshell_chdir (const char *directory
, int do_update
, int reset_prompt
)
768 pcwd
= vfs_translate_path_n (current_panel
->cwd
);
771 (subshell_state
== INACTIVE
772 && strcmp (subshell_cwd
, pcwd
))) {
773 /* We have to repaint the subshell prompt if we read it from
774 * the main program. Please note that in the code after this
775 * if, the cd command that is sent will make the subshell
776 * repaint the prompt, so we don't have to paint it. */
783 /* The initial space keeps this out of the command history (in bash
784 because we set "HISTCONTROL=ignorespace") */
785 write_all (subshell_pty
, " cd ", 4);
787 translate
= vfs_translate_path_n (directory
);
789 temp
= subshell_name_quote (translate
);
791 write_all (subshell_pty
, temp
, strlen (temp
));
794 /* Should not happen unless the directory name is so long
795 that we don't have memory to quote it. */
796 write_all (subshell_pty
, ".", 1);
800 write_all (subshell_pty
, ".", 1);
803 write_all (subshell_pty
, "/", 1);
805 write_all (subshell_pty
, "\n", 1);
807 subshell_state
= RUNNING_COMMAND
;
808 feed_subshell (QUIETLY
, FALSE
);
810 if (subshell_alive
) {
811 int bPathNotEq
= strcmp (subshell_cwd
, pcwd
);
813 if (bPathNotEq
&& subshell_type
== TCSH
) {
814 char rp_subshell_cwd
[PATH_MAX
];
815 char rp_current_panel_cwd
[PATH_MAX
];
817 char *p_subshell_cwd
=
818 mc_realpath (subshell_cwd
, rp_subshell_cwd
);
819 char *p_current_panel_cwd
=
820 mc_realpath (pcwd
, rp_current_panel_cwd
);
822 if (p_subshell_cwd
== NULL
)
823 p_subshell_cwd
= subshell_cwd
;
824 if (p_current_panel_cwd
== NULL
)
825 p_current_panel_cwd
= pcwd
;
826 bPathNotEq
= strcmp (p_subshell_cwd
, p_current_panel_cwd
);
829 if (bPathNotEq
&& strcmp (pcwd
, ".")) {
830 char *cwd
= strip_password (g_strdup (pcwd
), 1);
831 fprintf (stderr
, _("Warning: Cannot change to %s.\n"), cwd
);
838 update_prompt
= FALSE
;
841 /* Make sure that MC never stores the CWD in a silly format */
842 /* like /usr////lib/../bin, or the strcmp() above will fail */
847 subshell_get_console_attributes (void)
849 /* Get our current terminal modes */
851 if (tcgetattr (STDOUT_FILENO
, &shell_mode
)) {
852 fprintf (stderr
, "Cannot get terminal settings: %s\r\n",
853 unix_error_string (errno
));
854 use_subshell
= FALSE
;
860 /* Figure out whether the subshell has stopped, exited or been killed */
861 /* Possibly modifies: `subshell_alive', `subshell_stopped' and `quit' */
863 sigchld_handler (int sig
)
870 pid
= waitpid (subshell_pid
, &status
, WUNTRACED
| WNOHANG
);
872 if (pid
== subshell_pid
) {
873 /* Figure out what has happened to the subshell */
875 if (WIFSTOPPED (status
)) {
876 if (WSTOPSIG (status
) == SIGSTOP
) {
877 /* The subshell has received a SIGSTOP signal */
878 subshell_stopped
= TRUE
;
880 /* The user has suspended the subshell. Revive it */
881 kill (subshell_pid
, SIGCONT
);
884 /* The subshell has either exited normally or been killed */
885 subshell_alive
= FALSE
;
886 delete_select_channel (subshell_pty
);
887 if (WIFEXITED (status
) && WEXITSTATUS (status
) != FORK_FAILURE
)
888 quit
|= SUBSHELL_EXIT
; /* Exited normally */
892 pid
= waitpid (cons_saver_pid
, &status
, WUNTRACED
| WNOHANG
);
894 if (pid
== cons_saver_pid
) {
896 if (WIFSTOPPED (status
))
897 /* Someone has stopped cons.saver - restart it */
900 /* cons.saver has died - disable confole saving */
901 handle_console (CONSOLE_DONE
);
906 #endif /* __linux__ */
908 /* If we got here, some other child exited; ignore it */
912 /* Feed the subshell our keyboard input until it says it's finished */
914 feed_subshell (int how
, int fail_on_error
)
916 fd_set read_set
; /* For `select' */
918 int bytes
; /* For the return value from `read' */
919 int i
; /* Loop counter */
921 struct timeval wtime
; /* Maximum time we wait for the subshell */
922 struct timeval
*wptr
;
924 /* we wait up to 10 seconds if fail_on_error, forever otherwise */
927 wptr
= fail_on_error
? &wtime
: NULL
;
933 /* Prepare the file-descriptor set and call `select' */
936 FD_SET (subshell_pty
, &read_set
);
937 FD_SET (subshell_pipe
[READ
], &read_set
);
938 maxfdp
= max (subshell_pty
, subshell_pipe
[READ
]);
939 if (how
== VISIBLY
) {
940 FD_SET (STDIN_FILENO
, &read_set
);
941 maxfdp
= max (maxfdp
, STDIN_FILENO
);
944 if (select (maxfdp
+ 1, &read_set
, NULL
, NULL
, wptr
) == -1) {
946 /* Despite using SA_RESTART, we still have to check for this */
948 continue; /* try all over again */
949 tcsetattr (STDOUT_FILENO
, TCSANOW
, &shell_mode
);
950 fprintf (stderr
, "select (FD_SETSIZE, &read_set...): %s\r\n",
951 unix_error_string (errno
));
955 if (FD_ISSET (subshell_pty
, &read_set
))
956 /* Read from the subshell, write to stdout */
958 /* This loop improves performance by reducing context switches
959 by a factor of 20 or so... unfortunately, it also hangs MC
960 randomly, because of an apparent Linux bug. Investigate. */
961 /* for (i=0; i<5; ++i) * FIXME -- experimental */
963 bytes
= read (subshell_pty
, pty_buffer
, pty_buffer_size
);
965 /* The subshell has died */
966 if (bytes
== -1 && errno
== EIO
&& !subshell_alive
)
970 tcsetattr (STDOUT_FILENO
, TCSANOW
, &shell_mode
);
971 fprintf (stderr
, "read (subshell_pty...): %s\r\n",
972 unix_error_string (errno
));
977 write_all (STDOUT_FILENO
, pty_buffer
, bytes
);
980 else if (FD_ISSET (subshell_pipe
[READ
], &read_set
))
981 /* Read the subshell's CWD and capture its prompt */
985 read (subshell_pipe
[READ
], subshell_cwd
,
988 tcsetattr (STDOUT_FILENO
, TCSANOW
, &shell_mode
);
989 fprintf (stderr
, "read (subshell_pipe[READ]...): %s\r\n",
990 unix_error_string (errno
));
994 subshell_cwd
[bytes
- 1] = 0; /* Squash the final '\n' */
998 subshell_ready
= TRUE
;
999 if (subshell_state
== RUNNING_COMMAND
) {
1000 subshell_state
= INACTIVE
;
1005 else if (FD_ISSET (STDIN_FILENO
, &read_set
))
1006 /* Read from stdin, write to the subshell */
1008 bytes
= read (STDIN_FILENO
, pty_buffer
, pty_buffer_size
);
1010 tcsetattr (STDOUT_FILENO
, TCSANOW
, &shell_mode
);
1012 "read (STDIN_FILENO, pty_buffer...): %s\r\n",
1013 unix_error_string (errno
));
1017 for (i
= 0; i
< bytes
; ++i
)
1018 if (pty_buffer
[i
] == subshell_switch_key
) {
1019 write_all (subshell_pty
, pty_buffer
, i
);
1021 subshell_state
= INACTIVE
;
1025 write_all (subshell_pty
, pty_buffer
, bytes
);
1026 subshell_ready
= FALSE
;
1034 /* Wait until the subshell dies or stops. If it stops, make it resume. */
1035 /* Possibly modifies the globals `subshell_alive' and `subshell_stopped' */
1036 static void synchronize (void)
1038 sigset_t sigchld_mask
, old_mask
;
1040 sigemptyset (&sigchld_mask
);
1041 sigaddset (&sigchld_mask
, SIGCHLD
);
1042 sigprocmask (SIG_BLOCK
, &sigchld_mask
, &old_mask
);
1045 * SIGCHLD should not be blocked, but we unblock it just in case.
1046 * This is known to be useful for cygwin 1.3.12 and older.
1048 sigdelset (&old_mask
, SIGCHLD
);
1050 /* Wait until the subshell has stopped */
1051 while (subshell_alive
&& !subshell_stopped
)
1052 sigsuspend (&old_mask
);
1054 if (subshell_state
!= ACTIVE
) {
1055 /* Discard all remaining data from stdin to the subshell */
1056 tcflush (subshell_pty_slave
, TCIFLUSH
);
1059 subshell_stopped
= FALSE
;
1060 kill (subshell_pid
, SIGCONT
);
1062 sigprocmask (SIG_SETMASK
, &old_mask
, NULL
);
1063 /* We can't do any better without modifying the shell(s) */
1066 /* pty opening functions */
1070 /* System V version of pty_open_master */
1072 static int pty_open_master (char *pty_name
)
1077 #ifdef HAVE_POSIX_OPENPT
1078 pty_master
= posix_openpt(O_RDWR
);
1080 /* getpt () is a GNU extension (glibc 2.1.x) */
1081 pty_master
= getpt ();
1083 strcpy (pty_name
, "/dev/ptc");
1084 pty_master
= open (pty_name
, O_RDWR
);
1086 strcpy (pty_name
, "/dev/ptmx");
1087 pty_master
= open (pty_name
, O_RDWR
);
1090 if (pty_master
== -1)
1093 if (grantpt (pty_master
) == -1 /* Grant access to slave */
1094 || unlockpt (pty_master
) == -1 /* Clear slave's lock flag */
1095 || !(slave_name
= ptsname (pty_master
))) /* Get slave's name */
1100 strcpy (pty_name
, slave_name
);
1104 /* System V version of pty_open_slave */
1106 pty_open_slave (const char *pty_name
)
1108 int pty_slave
= open (pty_name
, O_RDWR
);
1110 if (pty_slave
== -1) {
1111 fprintf (stderr
, "open (%s, O_RDWR): %s\r\n", pty_name
,
1112 unix_error_string (errno
));
1115 #if !defined(__osf__) && !defined(__linux__)
1116 #if defined (I_FIND) && defined (I_PUSH)
1117 if (!ioctl (pty_slave
, I_FIND
, "ptem"))
1118 if (ioctl (pty_slave
, I_PUSH
, "ptem") == -1) {
1119 fprintf (stderr
, "ioctl (%d, I_PUSH, \"ptem\") failed: %s\r\n",
1120 pty_slave
, unix_error_string (errno
));
1125 if (!ioctl (pty_slave
, I_FIND
, "ldterm"))
1126 if (ioctl (pty_slave
, I_PUSH
, "ldterm") == -1) {
1128 "ioctl (%d, I_PUSH, \"ldterm\") failed: %s\r\n",
1129 pty_slave
, unix_error_string (errno
));
1133 #if !defined(sgi) && !defined(__sgi)
1134 if (!ioctl (pty_slave
, I_FIND
, "ttcompat"))
1135 if (ioctl (pty_slave
, I_PUSH
, "ttcompat") == -1) {
1137 "ioctl (%d, I_PUSH, \"ttcompat\") failed: %s\r\n",
1138 pty_slave
, unix_error_string (errno
));
1142 #endif /* sgi || __sgi */
1143 #endif /* I_FIND && I_PUSH */
1144 #endif /* __osf__ || __linux__ */
1146 fcntl(pty_slave
, F_SETFD
, FD_CLOEXEC
);
1150 #else /* !HAVE_GRANTPT */
1152 /* BSD version of pty_open_master */
1153 static int pty_open_master (char *pty_name
)
1156 const char *ptr1
, *ptr2
;
1158 strcpy (pty_name
, "/dev/ptyXX");
1159 for (ptr1
= "pqrstuvwxyzPQRST"; *ptr1
; ++ptr1
)
1161 pty_name
[8] = *ptr1
;
1162 for (ptr2
= "0123456789abcdef"; *ptr2
; ++ptr2
)
1164 pty_name
[9] = *ptr2
;
1166 /* Try to open master */
1167 if ((pty_master
= open (pty_name
, O_RDWR
)) == -1) {
1168 if (errno
== ENOENT
) /* Different from EIO */
1169 return -1; /* Out of pty devices */
1171 continue; /* Try next pty device */
1173 pty_name
[5] = 't'; /* Change "pty" to "tty" */
1174 if (access (pty_name
, 6)){
1182 return -1; /* Ran out of pty devices */
1185 /* BSD version of pty_open_slave */
1187 pty_open_slave (const char *pty_name
)
1190 struct group
*group_info
= getgrnam ("tty");
1192 if (group_info
!= NULL
) {
1193 /* The following two calls will only succeed if we are root */
1194 /* [Commented out while permissions problem is investigated] */
1195 /* chown (pty_name, getuid (), group_info->gr_gid); FIXME */
1196 /* chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP); FIXME */
1198 if ((pty_slave
= open (pty_name
, O_RDWR
)) == -1)
1199 fprintf (stderr
, "open (pty_name, O_RDWR): %s\r\n", pty_name
);
1200 fcntl(pty_slave
, F_SETFD
, FD_CLOEXEC
);
1204 #endif /* !HAVE_GRANTPT */
1205 #endif /* HAVE_SUBSHELL_SUPPORT */