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>
40 #ifdef HAVE_SYS_IOCTL_H
41 # include <sys/ioctl.h>
47 # include <stropts.h> /* For I_PUSH */
48 #endif /* HAVE_STROPTS_H */
50 #include "lib/global.h"
51 #include "lib/tty/tty.h" /* LINES */
52 #include "lib/tty/key.h" /* XCTRL */
53 #include "lib/vfs/mc-vfs/vfs.h"
54 #include "lib/strutil.h"
55 #include "lib/fileloc.h"
57 #include "panel.h" /* current_panel */
58 #include "wtools.h" /* query_dialog() */
59 #include "main.h" /* do_update_prompt() */
60 #include "consaver/cons.saver.h" /* handle_console() */
64 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
68 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
71 /* tcsh closes all non-standard file descriptors, so we have to use a pipe */
72 static char tcsh_fifo
[128];
75 static void init_raw_mode (void);
76 static int feed_subshell (int how
, int fail_on_error
);
77 static void synchronize (void);
78 static int pty_open_master (char *pty_name
);
79 static int pty_open_slave (const char *pty_name
);
80 static int resize_tty (int fd
);
83 # define STDIN_FILENO 0
87 # define STDOUT_FILENO 1
91 # define STDERR_FILENO 2
94 /* If using a subshell for evaluating commands this is true */
96 #ifdef SUBSHELL_OPTIONAL
102 /* File descriptors of the pseudoterminal used by the subshell */
103 int subshell_pty
= 0;
104 static int subshell_pty_slave
= -1;
106 /* The key for switching back to MC from the subshell */
107 static const char subshell_switch_key
= XCTRL('o') & 255;
109 /* State of the subshell:
110 * INACTIVE: the default state; awaiting a command
111 * ACTIVE: remain in the shell until the user hits `subshell_switch_key'
112 * RUNNING_COMMAND: return to MC when the current command finishes */
113 enum subshell_state_enum subshell_state
;
115 /* Holds the latest prompt captured from the subshell */
116 char *subshell_prompt
= NULL
;
118 /* Initial length of the buffer for the subshell's prompt */
119 #define INITIAL_PROMPT_SIZE 10
121 /* Used by the child process to indicate failure to start the subshell */
122 #define FORK_FAILURE 69 /* Arbitrary */
124 /* Initial length of the buffer for all I/O with the subshell */
125 #define INITIAL_PTY_BUFFER_SIZE 100 /* Arbitrary; but keep it >= 80 */
128 enum {READ
=0, WRITE
=1};
130 static char *pty_buffer
; /* For reading/writing on the subshell's pty */
131 static int pty_buffer_size
; /* The buffer grows as needed */
132 static int subshell_pipe
[2]; /* To pass CWD info from the subshell to MC */
133 static pid_t subshell_pid
= 1; /* The subshell's process ID */
134 static char subshell_cwd
[MC_MAXPATHLEN
+1]; /* One extra char for final '\n' */
136 /* Subshell type (gleaned from the SHELL environment variable, if available) */
144 /* Flag to indicate whether the subshell is ready for next command */
145 static int subshell_ready
;
147 /* The following two flags can be changed by the SIGCHLD handler. This is */
148 /* OK, because the `int' type is updated atomically on all known machines */
149 static volatile int subshell_alive
, subshell_stopped
;
151 /* We store the terminal's initial mode here so that we can configure
152 the pty similarly, and also so we can restore the real terminal to
153 sanity if we have to exit abruptly */
154 static struct termios shell_mode
;
156 /* This is a transparent mode for the terminal where MC is running on */
157 /* It is used when the shell is active, so that the control signals */
158 /* are delivered to the shell pty */
159 static struct termios raw_mode
;
161 /* This counter indicates how many characters of prompt we have read */
162 /* FIXME: try to figure out why this had to become global */
163 static int prompt_pos
;
167 * Write all data, even if the write() call is interrupted.
170 write_all (int fd
, const void *buf
, size_t count
)
175 ret
= write (fd
, (const unsigned char *) buf
+ written
, count
);
177 if (errno
== EINTR
) {
180 return written
> 0 ? written
: ret
;
190 * Prepare child process to running the shell and run it.
192 * Modifies the global variables (in the child process only):
198 init_subshell_child (const char *pty_name
)
200 const char *init_file
= NULL
;
204 setsid (); /* Get a fresh terminal session */
206 /* Make sure that it has become our controlling terminal */
208 /* Redundant on Linux and probably most systems, but just in case: */
211 ioctl (subshell_pty_slave
, TIOCSCTTY
, 0);
214 /* Configure its terminal modes and window size */
216 /* Set up the pty with the same termios flags as our own tty */
217 if (tcsetattr (subshell_pty_slave
, TCSANOW
, &shell_mode
)) {
218 fprintf (stderr
, "Cannot set pty terminal modes: %s\r\n",
219 unix_error_string (errno
));
220 _exit (FORK_FAILURE
);
223 /* Set the pty's size (80x25 by default on Linux) according to the */
224 /* size of the real terminal as calculated by ncurses, if possible */
225 resize_tty (subshell_pty_slave
);
227 /* Set up the subshell's environment and init file name */
229 /* It simplifies things to change to our home directory here, */
230 /* and the user's startup file may do a `cd' command anyway */
231 chdir (home_dir
); /* FIXME? What about when we re-run the subshell? */
233 /* Set MC_SID to prevent running one mc from another */
236 char sid_str
[BUF_SMALL
];
237 g_snprintf (sid_str
, sizeof (sid_str
), "MC_SID=%ld",
239 putenv (g_strdup (sid_str
));
242 switch (subshell_type
) {
244 init_file
= MC_USERCONF_DIR PATH_SEP_STR
"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 ((char*)"HISTCONTROL=ignorespace");
251 /* Allow alternative readline settings for MC */
252 if (access (MC_USERCONF_DIR PATH_SEP_STR
"inputrc", R_OK
) == 0)
253 putenv ((char*)"INPUTRC=" MC_USERCONF_DIR PATH_SEP_STR
"/inputrc");
257 /* TODO: Find a way to pass initfile to TCSH and ZSH */
264 fprintf (stderr
, __FILE__
": unimplemented subshell type %d\r\n",
266 _exit (FORK_FAILURE
);
269 /* Attach all our standard file descriptors to the pty */
271 /* This is done just before the fork, because stderr must still */
272 /* be connected to the real tty during the above error messages; */
273 /* otherwise the user will never see them. */
275 dup2 (subshell_pty_slave
, STDIN_FILENO
);
276 dup2 (subshell_pty_slave
, STDOUT_FILENO
);
277 dup2 (subshell_pty_slave
, STDERR_FILENO
);
279 close (subshell_pipe
[READ
]);
280 close (subshell_pty_slave
); /* These may be FD_CLOEXEC, but just in case... */
281 /* Close master side of pty. This is important; apart from */
282 /* freeing up the descriptor for use in the subshell, it also */
283 /* means that when MC exits, the subshell will get a SIGHUP and */
284 /* exit too, because there will be no more descriptors pointing */
285 /* at the master side of the pty and so it will disappear. */
286 close (subshell_pty
);
288 /* Execute the subshell at last */
290 switch (subshell_type
) {
292 execl (shell
, "bash", "-rcfile", init_file
, (char *) NULL
);
296 execl (shell
, "tcsh", (char *) NULL
);
300 /* Use -g to exclude cmds beginning with space from history
301 * and -Z to use the line editor on non-interactive term */
302 execl (shell
, "zsh", "-Z", "-g", (char *) NULL
);
307 execl (shell
, "fish", (char *) NULL
);
311 /* If we get this far, everything failed miserably */
312 _exit (FORK_FAILURE
);
317 * Check MC_SID to prevent running one mc from another.
319 * 0 if no parent mc in our session was found,
320 * 1 if parent mc was found and the user wants to continue,
321 * 2 if parent mc was found and the user wants to quit mc.
326 pid_t my_sid
, old_sid
;
330 sid_str
= getenv ("MC_SID");
334 old_sid
= (pid_t
) strtol (sid_str
, NULL
, 0);
342 /* The parent mc is in a different session, it's OK */
343 if (old_sid
!= my_sid
)
346 r
= query_dialog (_("Warning"),
347 _("GNU Midnight Commander is already\n"
348 "running on this terminal.\n"
349 "Subshell support will be disabled."), D_ERROR
, 2,
350 _("&OK"), _("&Quit"));
360 * Fork the subshell, and set up many, many things.
362 * Possibly modifies the global variables:
363 * subshell_type, subshell_alive, subshell_stopped, subshell_pid
364 * use_subshell - Is set to FALSE if we can't run the subshell
365 * quit - Can be set to SUBSHELL_EXIT by the SIGCHLD handler
371 /* This must be remembered across calls to init_subshell() */
372 static char pty_name
[BUF_SMALL
];
373 char precmd
[BUF_SMALL
];
375 switch (check_sid ()) {
377 use_subshell
= FALSE
;
380 use_subshell
= FALSE
;
381 midnight_shutdown
= 1;
385 /* Take the current (hopefully pristine) tty mode and make */
386 /* a raw mode based on it now, before we do anything else with it */
389 if (subshell_pty
== 0) { /* First time through */
390 /* Find out what type of shell we have */
392 if (strstr (shell
, "/zsh") || getenv ("ZSH_VERSION"))
394 else if (strstr (shell
, "/tcsh"))
395 subshell_type
= TCSH
;
396 else if (strstr (shell
, "/csh"))
397 subshell_type
= TCSH
;
398 else if (strstr (shell
, "/bash") || getenv ("BASH"))
399 subshell_type
= BASH
;
400 else if (strstr (shell
, "/fish"))
401 subshell_type
= FISH
;
403 use_subshell
= FALSE
;
407 /* Open a pty for talking to the subshell */
409 /* FIXME: We may need to open a fresh pty each time on SVR4 */
411 subshell_pty
= pty_open_master (pty_name
);
412 if (subshell_pty
== -1) {
413 fprintf (stderr
, "Cannot open master side of pty: %s\r\n",
414 unix_error_string (errno
));
415 use_subshell
= FALSE
;
418 subshell_pty_slave
= pty_open_slave (pty_name
);
419 if (subshell_pty_slave
== -1) {
420 fprintf (stderr
, "Cannot open slave side of pty %s: %s\r\n",
421 pty_name
, unix_error_string (errno
));
422 use_subshell
= FALSE
;
426 /* Initialise the pty's I/O buffer */
428 pty_buffer_size
= INITIAL_PTY_BUFFER_SIZE
;
429 pty_buffer
= g_malloc (pty_buffer_size
);
431 /* Create a pipe for receiving the subshell's CWD */
433 if (subshell_type
== TCSH
) {
434 g_snprintf (tcsh_fifo
, sizeof (tcsh_fifo
), "%s/mc.pipe.%d",
435 mc_tmpdir (), (int) getpid ());
436 if (mkfifo (tcsh_fifo
, 0600) == -1) {
437 fprintf (stderr
, "mkfifo(%s) failed: %s\r\n", tcsh_fifo
,
438 unix_error_string (errno
));
439 use_subshell
= FALSE
;
443 /* Opening the FIFO as O_RDONLY or O_WRONLY causes deadlock */
445 if ((subshell_pipe
[READ
] = open (tcsh_fifo
, O_RDWR
)) == -1
446 || (subshell_pipe
[WRITE
] =
447 open (tcsh_fifo
, O_RDWR
)) == -1) {
448 fprintf (stderr
, _("Cannot open named pipe %s\n"), tcsh_fifo
);
449 perror (__FILE__
": open");
450 use_subshell
= FALSE
;
453 } else /* subshell_type is BASH or ZSH */ if (pipe (subshell_pipe
)) {
454 perror (__FILE__
": couldn't create pipe");
455 use_subshell
= FALSE
;
460 /* Fork the subshell */
462 subshell_alive
= TRUE
;
463 subshell_stopped
= FALSE
;
464 subshell_pid
= fork ();
466 if (subshell_pid
== -1) {
467 fprintf (stderr
, "Cannot spawn the subshell process: %s\r\n",
468 unix_error_string (errno
));
469 /* We exit here because, if the process table is full, the */
470 /* other method of running user commands won't work either */
474 if (subshell_pid
== 0) { /* We are in the child process */
475 init_subshell_child (pty_name
);
478 /* Set up `precmd' or equivalent for reading the subshell's CWD */
480 switch (subshell_type
) {
482 g_snprintf (precmd
, sizeof (precmd
),
483 " PROMPT_COMMAND='pwd>&%d;kill -STOP $$'\n",
484 subshell_pipe
[WRITE
]);
488 g_snprintf (precmd
, sizeof (precmd
),
489 " precmd(){ pwd>&%d;kill -STOP $$ }\n",
490 subshell_pipe
[WRITE
]);
494 g_snprintf (precmd
, sizeof (precmd
),
495 "set echo_style=both;"
496 "alias precmd 'echo $cwd:q >>%s;kill -STOP $$'\n",
500 g_snprintf (precmd
, sizeof (precmd
),
501 "function fish_prompt ; pwd>&%d;kill -STOP %%self; end\n",
502 subshell_pipe
[WRITE
]);
506 write_all (subshell_pty
, precmd
, strlen (precmd
));
508 /* Wait until the subshell has started up and processed the command */
510 subshell_state
= RUNNING_COMMAND
;
511 tty_enable_interrupt_key ();
512 if (!feed_subshell (QUIETLY
, TRUE
)) {
513 use_subshell
= FALSE
;
515 tty_disable_interrupt_key ();
517 use_subshell
= FALSE
; /* Subshell died instantly, so don't use it */
521 static void init_raw_mode ()
523 static int initialized
= 0;
525 /* MC calls tty_reset_shell_mode() in pre_exec() to set the real tty to its */
526 /* original settings. However, here we need to make this tty very raw, */
527 /* so that all keyboard signals, XON/XOFF, etc. will get through to the */
528 /* pty. So, instead of changing the code for execute(), pre_exec(), */
529 /* etc, we just set up the modes we need here, before each command. */
531 if (initialized
== 0) /* First time: initialise `raw_mode' */
533 tcgetattr (STDOUT_FILENO
, &raw_mode
);
534 raw_mode
.c_lflag
&= ~ICANON
; /* Disable line-editing chars, etc. */
535 raw_mode
.c_lflag
&= ~ISIG
; /* Disable intr, quit & suspend chars */
536 raw_mode
.c_lflag
&= ~ECHO
; /* Disable input echoing */
537 raw_mode
.c_iflag
&= ~IXON
; /* Pass ^S/^Q to subshell undisturbed */
538 raw_mode
.c_iflag
&= ~ICRNL
; /* Don't translate CRs into LFs */
539 raw_mode
.c_oflag
&= ~OPOST
; /* Don't postprocess output */
540 raw_mode
.c_cc
[VTIME
] = 0; /* IE: wait forever, and return as */
541 raw_mode
.c_cc
[VMIN
] = 1; /* soon as a character is available */
547 int invoke_subshell (const char *command
, int how
, char **new_dir
)
551 /* Make the MC terminal transparent */
552 tcsetattr (STDOUT_FILENO
, TCSANOW
, &raw_mode
);
554 /* Make the subshell change to MC's working directory */
556 do_subshell_chdir (current_panel
->cwd
, TRUE
, 1);
558 if (command
== NULL
) /* The user has done "C-o" from MC */
560 if (subshell_state
== INACTIVE
)
562 subshell_state
= ACTIVE
;
563 /* FIXME: possibly take out this hack; the user can
564 re-play it by hitting C-hyphen a few times! */
566 write_all (subshell_pty
, " \b", 2); /* Hack to make prompt reappear */
569 else /* MC has passed us a user command */
572 write_all (subshell_pty
, " ", 1);
573 /* FIXME: if command is long (>8KB ?) we go comma */
574 write_all (subshell_pty
, command
, strlen (command
));
575 write_all (subshell_pty
, "\n", 1);
576 subshell_state
= RUNNING_COMMAND
;
577 subshell_ready
= FALSE
;
580 feed_subshell (how
, FALSE
);
582 pcwd
= vfs_translate_path_n (current_panel
->cwd
);
583 if (new_dir
&& subshell_alive
&& strcmp (subshell_cwd
, pcwd
))
584 *new_dir
= subshell_cwd
; /* Make MC change to the subshell's CWD */
587 /* Restart the subshell if it has died by SIGHUP, SIGQUIT, etc. */
588 while (!subshell_alive
&& !quit
&& use_subshell
)
598 read_subshell_prompt (void)
600 static int prompt_size
= INITIAL_PROMPT_SIZE
;
601 int bytes
= 0, i
, rc
= 0;
602 struct timeval timeleft
= { 0, 0 };
606 FD_SET (subshell_pty
, &tmp
);
608 if (subshell_prompt
== NULL
) { /* First time through */
609 subshell_prompt
= g_malloc (prompt_size
);
610 *subshell_prompt
= '\0';
614 while (subshell_alive
616 select (subshell_pty
+ 1, &tmp
, NULL
, NULL
, &timeleft
))) {
617 /* Check for `select' errors */
622 fprintf (stderr
, "select (FD_SETSIZE, &tmp...): %s\r\n",
623 unix_error_string (errno
));
628 bytes
= read (subshell_pty
, pty_buffer
, pty_buffer_size
);
630 /* Extract the prompt from the shell output */
632 for (i
= 0; i
< bytes
; ++i
)
633 if (pty_buffer
[i
] == '\n' || pty_buffer
[i
] == '\r') {
639 subshell_prompt
[prompt_pos
++] = pty_buffer
[i
];
640 if (prompt_pos
== prompt_size
)
642 g_realloc (subshell_prompt
, prompt_size
*= 2);
645 subshell_prompt
[prompt_pos
] = '\0';
647 if (rc
== 0 && bytes
== 0)
652 /* Resize given terminal using TIOCSWINSZ, return ioctl() result */
653 static int resize_tty (int fd
)
655 #if defined TIOCSWINSZ
656 struct winsize tty_size
;
658 tty_size
.ws_row
= LINES
;
659 tty_size
.ws_col
= COLS
;
660 tty_size
.ws_xpixel
= tty_size
.ws_ypixel
= 0;
662 return ioctl (fd
, TIOCSWINSZ
, &tty_size
);
668 /* Resize subshell_pty */
669 void resize_subshell (void)
671 if (use_subshell
== 0)
674 resize_tty (subshell_pty
);
680 int subshell_quit
= TRUE
;
682 if (subshell_state
!= INACTIVE
&& subshell_alive
)
684 !query_dialog (_("Warning"),
685 _(" The shell is still active. Quit anyway? "),
686 D_NORMAL
, 2, _("&Yes"), _("&No"));
689 if (subshell_type
== TCSH
) {
690 if (unlink (tcsh_fifo
) == -1)
691 fprintf (stderr
, "Cannot remove named pipe %s: %s\r\n",
692 tcsh_fifo
, unix_error_string (errno
));
695 g_free (subshell_prompt
);
697 subshell_prompt
= NULL
;
701 return subshell_quit
;
706 * Carefully quote directory name to allow entering any directory safely,
707 * no matter what weird characters it may contain in its name.
708 * NOTE: Treat directory name an untrusted data, don't allow it to cause
709 * executing any commands in the shell. Escape all control characters.
710 * Use following technique:
712 * printf(1) with format string containing a single conversion specifier,
713 * "b", and an argument which contains a copy of the string passed to
714 * subshell_name_quote() with all characters, except digits and letters,
715 * replaced by the backslash-escape sequence \0nnn, where "nnn" is the
716 * numeric value of the character converted to octal number.
718 * cd "`printf "%b" 'ABC\0nnnDEF\0nnnXYZ'`"
722 subshell_name_quote (const char *s
)
726 const char *quote_cmd_start
, *quote_cmd_end
;
729 if (subshell_type
== FISH
) {
730 quote_cmd_start
= "(printf \"%b\" '";
731 quote_cmd_end
= "')";
733 quote_cmd_start
= "\"`printf \"%b\" '";
734 quote_cmd_end
= "'`\"";
737 /* Factor 5 because we need \, 0 and 3 other digits per character. */
738 d
= ret
= g_try_malloc (1 + (5 * strlen (s
)) + (strlen(quote_cmd_start
))
739 + (strlen(quote_cmd_end
)));
743 /* Prevent interpreting leading `-' as a switch for `cd' */
749 /* Copy the beginning of the command to the buffer */
750 strcpy (d
, quote_cmd_start
);
751 d
+= strlen(quote_cmd_start
);
754 * Print every character except digits and letters as a backslash-escape
755 * sequence of the form \0nnn, where "nnn" is the numeric value of the
756 * character converted to octal number.
759 for (; su
[0] != '\0'; ) {
760 n
= str_cget_next_char_safe (su
);
761 if (str_isalnum (su
)) {
762 memcpy (d
, su
, n
- su
);
765 for (c
= 0; c
< n
- su
; c
++) {
766 sprintf (d
, "\\0%03o", (unsigned char) su
[c
]);
773 strcpy (d
, quote_cmd_end
);
779 /* If it actually changed the directory it returns true */
781 do_subshell_chdir (const char *directory
, int do_update
, int reset_prompt
)
787 pcwd
= vfs_translate_path_n (current_panel
->cwd
);
790 (subshell_state
== INACTIVE
791 && strcmp (subshell_cwd
, pcwd
))) {
792 /* We have to repaint the subshell prompt if we read it from
793 * the main program. Please note that in the code after this
794 * if, the cd command that is sent will make the subshell
795 * repaint the prompt, so we don't have to paint it. */
802 /* The initial space keeps this out of the command history (in bash
803 because we set "HISTCONTROL=ignorespace") */
804 write_all (subshell_pty
, " cd ", 4);
806 translate
= vfs_translate_path_n (directory
);
808 temp
= subshell_name_quote (translate
);
810 write_all (subshell_pty
, temp
, strlen (temp
));
813 /* Should not happen unless the directory name is so long
814 that we don't have memory to quote it. */
815 write_all (subshell_pty
, ".", 1);
819 write_all (subshell_pty
, ".", 1);
822 write_all (subshell_pty
, "/", 1);
824 write_all (subshell_pty
, "\n", 1);
826 subshell_state
= RUNNING_COMMAND
;
827 feed_subshell (QUIETLY
, FALSE
);
829 if (subshell_alive
) {
830 int bPathNotEq
= strcmp (subshell_cwd
, pcwd
);
832 if (bPathNotEq
&& subshell_type
== TCSH
) {
833 char rp_subshell_cwd
[PATH_MAX
];
834 char rp_current_panel_cwd
[PATH_MAX
];
836 char *p_subshell_cwd
=
837 mc_realpath (subshell_cwd
, rp_subshell_cwd
);
838 char *p_current_panel_cwd
=
839 mc_realpath (pcwd
, rp_current_panel_cwd
);
841 if (p_subshell_cwd
== NULL
)
842 p_subshell_cwd
= subshell_cwd
;
843 if (p_current_panel_cwd
== NULL
)
844 p_current_panel_cwd
= pcwd
;
845 bPathNotEq
= strcmp (p_subshell_cwd
, p_current_panel_cwd
);
848 if (bPathNotEq
&& strcmp (pcwd
, ".")) {
849 char *cwd
= strip_password (g_strdup (pcwd
), 1);
850 fprintf (stderr
, _("Warning: Cannot change to %s.\n"), cwd
);
857 update_prompt
= FALSE
;
860 /* Make sure that MC never stores the CWD in a silly format */
861 /* like /usr////lib/../bin, or the strcmp() above will fail */
866 subshell_get_console_attributes (void)
868 /* Get our current terminal modes */
870 if (tcgetattr (STDOUT_FILENO
, &shell_mode
)) {
871 fprintf (stderr
, "Cannot get terminal settings: %s\r\n",
872 unix_error_string (errno
));
873 use_subshell
= FALSE
;
879 /* Figure out whether the subshell has stopped, exited or been killed */
880 /* Possibly modifies: `subshell_alive', `subshell_stopped' and `quit' */
882 sigchld_handler (int sig
)
889 pid
= waitpid (subshell_pid
, &status
, WUNTRACED
| WNOHANG
);
891 if (pid
== subshell_pid
) {
892 /* Figure out what has happened to the subshell */
894 if (WIFSTOPPED (status
)) {
895 if (WSTOPSIG (status
) == SIGSTOP
) {
896 /* The subshell has received a SIGSTOP signal */
897 subshell_stopped
= TRUE
;
899 /* The user has suspended the subshell. Revive it */
900 kill (subshell_pid
, SIGCONT
);
903 /* The subshell has either exited normally or been killed */
904 subshell_alive
= FALSE
;
905 delete_select_channel (subshell_pty
);
906 if (WIFEXITED (status
) && WEXITSTATUS (status
) != FORK_FAILURE
)
907 quit
|= SUBSHELL_EXIT
; /* Exited normally */
911 pid
= waitpid (cons_saver_pid
, &status
, WUNTRACED
| WNOHANG
);
913 if (pid
== cons_saver_pid
) {
915 if (WIFSTOPPED (status
))
916 /* Someone has stopped cons.saver - restart it */
919 /* cons.saver has died - disable confole saving */
920 handle_console (CONSOLE_DONE
);
925 #endif /* __linux__ */
927 /* If we got here, some other child exited; ignore it */
931 /* Feed the subshell our keyboard input until it says it's finished */
933 feed_subshell (int how
, int fail_on_error
)
935 fd_set read_set
; /* For `select' */
937 int bytes
; /* For the return value from `read' */
938 int i
; /* Loop counter */
940 struct timeval wtime
; /* Maximum time we wait for the subshell */
941 struct timeval
*wptr
;
943 /* we wait up to 10 seconds if fail_on_error, forever otherwise */
946 wptr
= fail_on_error
? &wtime
: NULL
;
952 /* Prepare the file-descriptor set and call `select' */
955 FD_SET (subshell_pty
, &read_set
);
956 FD_SET (subshell_pipe
[READ
], &read_set
);
957 maxfdp
= max (subshell_pty
, subshell_pipe
[READ
]);
958 if (how
== VISIBLY
) {
959 FD_SET (STDIN_FILENO
, &read_set
);
960 maxfdp
= max (maxfdp
, STDIN_FILENO
);
963 if (select (maxfdp
+ 1, &read_set
, NULL
, NULL
, wptr
) == -1) {
965 /* Despite using SA_RESTART, we still have to check for this */
967 continue; /* try all over again */
968 tcsetattr (STDOUT_FILENO
, TCSANOW
, &shell_mode
);
969 fprintf (stderr
, "select (FD_SETSIZE, &read_set...): %s\r\n",
970 unix_error_string (errno
));
974 if (FD_ISSET (subshell_pty
, &read_set
))
975 /* Read from the subshell, write to stdout */
977 /* This loop improves performance by reducing context switches
978 by a factor of 20 or so... unfortunately, it also hangs MC
979 randomly, because of an apparent Linux bug. Investigate. */
980 /* for (i=0; i<5; ++i) * FIXME -- experimental */
982 bytes
= read (subshell_pty
, pty_buffer
, pty_buffer_size
);
984 /* The subshell has died */
985 if (bytes
== -1 && errno
== EIO
&& !subshell_alive
)
989 tcsetattr (STDOUT_FILENO
, TCSANOW
, &shell_mode
);
990 fprintf (stderr
, "read (subshell_pty...): %s\r\n",
991 unix_error_string (errno
));
996 write_all (STDOUT_FILENO
, pty_buffer
, bytes
);
999 else if (FD_ISSET (subshell_pipe
[READ
], &read_set
))
1000 /* Read the subshell's CWD and capture its prompt */
1004 read (subshell_pipe
[READ
], subshell_cwd
,
1007 tcsetattr (STDOUT_FILENO
, TCSANOW
, &shell_mode
);
1008 fprintf (stderr
, "read (subshell_pipe[READ]...): %s\r\n",
1009 unix_error_string (errno
));
1013 subshell_cwd
[bytes
- 1] = 0; /* Squash the final '\n' */
1017 subshell_ready
= TRUE
;
1018 if (subshell_state
== RUNNING_COMMAND
) {
1019 subshell_state
= INACTIVE
;
1024 else if (FD_ISSET (STDIN_FILENO
, &read_set
))
1025 /* Read from stdin, write to the subshell */
1027 bytes
= read (STDIN_FILENO
, pty_buffer
, pty_buffer_size
);
1029 tcsetattr (STDOUT_FILENO
, TCSANOW
, &shell_mode
);
1031 "read (STDIN_FILENO, pty_buffer...): %s\r\n",
1032 unix_error_string (errno
));
1036 for (i
= 0; i
< bytes
; ++i
)
1037 if (pty_buffer
[i
] == subshell_switch_key
) {
1038 write_all (subshell_pty
, pty_buffer
, i
);
1040 subshell_state
= INACTIVE
;
1044 write_all (subshell_pty
, pty_buffer
, bytes
);
1046 if (pty_buffer
[bytes
-1] == '\n' || pty_buffer
[bytes
-1] == '\r')
1047 subshell_ready
= FALSE
;
1055 /* Wait until the subshell dies or stops. If it stops, make it resume. */
1056 /* Possibly modifies the globals `subshell_alive' and `subshell_stopped' */
1057 static void synchronize (void)
1059 sigset_t sigchld_mask
, old_mask
;
1061 sigemptyset (&sigchld_mask
);
1062 sigaddset (&sigchld_mask
, SIGCHLD
);
1063 sigprocmask (SIG_BLOCK
, &sigchld_mask
, &old_mask
);
1066 * SIGCHLD should not be blocked, but we unblock it just in case.
1067 * This is known to be useful for cygwin 1.3.12 and older.
1069 sigdelset (&old_mask
, SIGCHLD
);
1071 /* Wait until the subshell has stopped */
1072 while (subshell_alive
&& !subshell_stopped
)
1073 sigsuspend (&old_mask
);
1075 if (subshell_state
!= ACTIVE
) {
1076 /* Discard all remaining data from stdin to the subshell */
1077 tcflush (subshell_pty_slave
, TCIFLUSH
);
1080 subshell_stopped
= FALSE
;
1081 kill (subshell_pid
, SIGCONT
);
1083 sigprocmask (SIG_SETMASK
, &old_mask
, NULL
);
1084 /* We can't do any better without modifying the shell(s) */
1087 /* pty opening functions */
1091 /* System V version of pty_open_master */
1093 static int pty_open_master (char *pty_name
)
1098 #ifdef HAVE_POSIX_OPENPT
1099 pty_master
= posix_openpt(O_RDWR
);
1101 /* getpt () is a GNU extension (glibc 2.1.x) */
1102 pty_master
= getpt ();
1104 strcpy (pty_name
, "/dev/ptc");
1105 pty_master
= open (pty_name
, O_RDWR
);
1107 strcpy (pty_name
, "/dev/ptmx");
1108 pty_master
= open (pty_name
, O_RDWR
);
1111 if (pty_master
== -1)
1114 if (grantpt (pty_master
) == -1 /* Grant access to slave */
1115 || unlockpt (pty_master
) == -1 /* Clear slave's lock flag */
1116 || !(slave_name
= ptsname (pty_master
))) /* Get slave's name */
1121 strcpy (pty_name
, slave_name
);
1125 /* System V version of pty_open_slave */
1127 pty_open_slave (const char *pty_name
)
1129 int pty_slave
= open (pty_name
, O_RDWR
);
1131 if (pty_slave
== -1) {
1132 fprintf (stderr
, "open (%s, O_RDWR): %s\r\n", pty_name
,
1133 unix_error_string (errno
));
1136 #if !defined(__osf__) && !defined(__linux__)
1137 #if defined (I_FIND) && defined (I_PUSH)
1138 if (!ioctl (pty_slave
, I_FIND
, "ptem"))
1139 if (ioctl (pty_slave
, I_PUSH
, "ptem") == -1) {
1140 fprintf (stderr
, "ioctl (%d, I_PUSH, \"ptem\") failed: %s\r\n",
1141 pty_slave
, unix_error_string (errno
));
1146 if (!ioctl (pty_slave
, I_FIND
, "ldterm"))
1147 if (ioctl (pty_slave
, I_PUSH
, "ldterm") == -1) {
1149 "ioctl (%d, I_PUSH, \"ldterm\") failed: %s\r\n",
1150 pty_slave
, unix_error_string (errno
));
1154 #if !defined(sgi) && !defined(__sgi)
1155 if (!ioctl (pty_slave
, I_FIND
, "ttcompat"))
1156 if (ioctl (pty_slave
, I_PUSH
, "ttcompat") == -1) {
1158 "ioctl (%d, I_PUSH, \"ttcompat\") failed: %s\r\n",
1159 pty_slave
, unix_error_string (errno
));
1163 #endif /* sgi || __sgi */
1164 #endif /* I_FIND && I_PUSH */
1165 #endif /* __osf__ || __linux__ */
1167 fcntl(pty_slave
, F_SETFD
, FD_CLOEXEC
);
1171 #else /* !HAVE_GRANTPT */
1173 /* BSD version of pty_open_master */
1174 static int pty_open_master (char *pty_name
)
1177 const char *ptr1
, *ptr2
;
1179 strcpy (pty_name
, "/dev/ptyXX");
1180 for (ptr1
= "pqrstuvwxyzPQRST"; *ptr1
; ++ptr1
)
1182 pty_name
[8] = *ptr1
;
1183 for (ptr2
= "0123456789abcdef"; *ptr2
; ++ptr2
)
1185 pty_name
[9] = *ptr2
;
1187 /* Try to open master */
1188 if ((pty_master
= open (pty_name
, O_RDWR
)) == -1) {
1189 if (errno
== ENOENT
) /* Different from EIO */
1190 return -1; /* Out of pty devices */
1192 continue; /* Try next pty device */
1194 pty_name
[5] = 't'; /* Change "pty" to "tty" */
1195 if (access (pty_name
, 6)){
1203 return -1; /* Ran out of pty devices */
1206 /* BSD version of pty_open_slave */
1208 pty_open_slave (const char *pty_name
)
1211 struct group
*group_info
= getgrnam ("tty");
1213 if (group_info
!= NULL
) {
1214 /* The following two calls will only succeed if we are root */
1215 /* [Commented out while permissions problem is investigated] */
1216 /* chown (pty_name, getuid (), group_info->gr_gid); FIXME */
1217 /* chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP); FIXME */
1219 if ((pty_slave
= open (pty_name
, O_RDWR
)) == -1)
1220 fprintf (stderr
, "open (pty_name, O_RDWR): %s\r\n", pty_name
);
1221 fcntl(pty_slave
, F_SETFD
, FD_CLOEXEC
);
1225 #endif /* !HAVE_GRANTPT */
1226 #endif /* HAVE_SUBSHELL_SUPPORT */