Ticket #1981: configure.ac: add AM_PROG_CC_C_O as we use compiler, that accepts ...
[midnight-commander.git] / src / subshell.c
bloba23b1060137079feb8259fcf9bf5f1946b4596c1
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.
19 /** \file subshell.c
20 * \brief Source: concurrent shell support
23 #include <config.h>
25 #ifdef HAVE_SUBSHELL_SUPPORT
27 #ifndef _GNU_SOURCE
28 # define _GNU_SOURCE 1
29 #endif
31 #include <ctype.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <fcntl.h>
38 #include <sys/types.h>
39 #include <sys/wait.h>
40 #ifdef HAVE_SYS_IOCTL_H
41 # include <sys/ioctl.h>
42 #endif
43 #include <termios.h>
44 #include <unistd.h>
46 #ifdef HAVE_STROPTS_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() */
61 #include "subshell.h"
63 #ifndef WEXITSTATUS
64 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
65 #endif
67 #ifndef WIFEXITED
68 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
69 #endif
71 /* tcsh closes all non-standard file descriptors, so we have to use a pipe */
72 static char tcsh_fifo[128];
74 /* Local functions */
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);
82 #ifndef STDIN_FILENO
83 # define STDIN_FILENO 0
84 #endif
86 #ifndef STDOUT_FILENO
87 # define STDOUT_FILENO 1
88 #endif
90 #ifndef STDERR_FILENO
91 # define STDERR_FILENO 2
92 #endif
94 /* If using a subshell for evaluating commands this is true */
95 int use_subshell =
96 #ifdef SUBSHELL_OPTIONAL
97 FALSE;
98 #else
99 TRUE;
100 #endif
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 */
127 /* For pipes */
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) */
137 static enum {
138 BASH,
139 TCSH,
140 ZSH,
141 FISH
142 } subshell_type;
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.
169 static ssize_t
170 write_all (int fd, const void *buf, size_t count)
172 ssize_t ret;
173 ssize_t written = 0;
174 while (count > 0) {
175 ret = write (fd, (const unsigned char *) buf + written, count);
176 if (ret < 0) {
177 if (errno == EINTR) {
178 continue;
179 } else {
180 return written > 0 ? written : ret;
183 count -= ret;
184 written += ret;
186 return written;
190 * Prepare child process to running the shell and run it.
192 * Modifies the global variables (in the child process only):
193 * shell_mode
195 * Returns: never.
197 static void
198 init_subshell_child (const char *pty_name)
200 const char *init_file = NULL;
201 pid_t mc_sid;
203 (void) pty_name;
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: */
210 #ifdef TIOCSCTTY
211 ioctl (subshell_pty_slave, TIOCSCTTY, 0);
212 #endif
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 */
234 mc_sid = getsid (0);
235 if (mc_sid != -1) {
236 char sid_str[BUF_SMALL];
237 g_snprintf (sid_str, sizeof (sid_str), "MC_SID=%ld",
238 (long) mc_sid);
239 putenv (g_strdup (sid_str));
242 switch (subshell_type) {
243 case BASH:
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");
255 break;
257 /* TODO: Find a way to pass initfile to TCSH and ZSH */
258 case TCSH:
259 case ZSH:
260 case FISH:
261 break;
263 default:
264 fprintf (stderr, __FILE__ ": unimplemented subshell type %d\r\n",
265 subshell_type);
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) {
291 case BASH:
292 execl (shell, "bash", "-rcfile", init_file, (char *) NULL);
293 break;
295 case TCSH:
296 execl (shell, "tcsh", (char *) NULL);
297 break;
299 case ZSH:
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);
304 break;
306 case FISH:
307 execl (shell, "fish", (char *) NULL);
308 break;
311 /* If we get this far, everything failed miserably */
312 _exit (FORK_FAILURE);
317 * Check MC_SID to prevent running one mc from another.
318 * Return:
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.
323 static int
324 check_sid (void)
326 pid_t my_sid, old_sid;
327 const char *sid_str;
328 int r;
330 sid_str = getenv ("MC_SID");
331 if (!sid_str)
332 return 0;
334 old_sid = (pid_t) strtol (sid_str, NULL, 0);
335 if (!old_sid)
336 return 0;
338 my_sid = getsid (0);
339 if (my_sid == -1)
340 return 0;
342 /* The parent mc is in a different session, it's OK */
343 if (old_sid != my_sid)
344 return 0;
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"));
351 if (r != 0) {
352 return 2;
355 return 1;
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
368 void
369 init_subshell (void)
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 ()) {
376 case 1:
377 use_subshell = FALSE;
378 return;
379 case 2:
380 use_subshell = FALSE;
381 midnight_shutdown = 1;
382 return;
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 */
387 init_raw_mode ();
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"))
393 subshell_type = ZSH;
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;
402 else {
403 use_subshell = FALSE;
404 return;
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;
416 return;
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;
423 return;
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;
440 return;
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;
451 return;
453 } else /* subshell_type is BASH or ZSH */ if (pipe (subshell_pipe)) {
454 perror (__FILE__": couldn't create pipe");
455 use_subshell = FALSE;
456 return;
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 */
471 exit (1);
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) {
481 case BASH:
482 g_snprintf (precmd, sizeof (precmd),
483 " PROMPT_COMMAND='pwd>&%d;kill -STOP $$'\n",
484 subshell_pipe[WRITE]);
485 break;
487 case ZSH:
488 g_snprintf (precmd, sizeof (precmd),
489 " precmd(){ pwd>&%d;kill -STOP $$ }\n",
490 subshell_pipe[WRITE]);
491 break;
493 case TCSH:
494 g_snprintf (precmd, sizeof (precmd),
495 "set echo_style=both;"
496 "alias precmd 'echo $cwd:q >>%s;kill -STOP $$'\n",
497 tcsh_fifo);
498 break;
499 case FISH:
500 g_snprintf (precmd, sizeof (precmd),
501 "function fish_prompt ; pwd>&%d;kill -STOP %%self; end\n",
502 subshell_pipe[WRITE]);
503 break;
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 ();
516 if (!subshell_alive)
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 */
542 initialized = 1;
547 int invoke_subshell (const char *command, int how, char **new_dir)
549 char *pcwd;
551 /* Make the MC terminal transparent */
552 tcsetattr (STDOUT_FILENO, TCSANOW, &raw_mode);
554 /* Make the subshell change to MC's working directory */
555 if (new_dir)
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! */
565 if (subshell_ready)
566 write_all (subshell_pty, " \b", 2); /* Hack to make prompt reappear */
569 else /* MC has passed us a user command */
571 if (how == QUIETLY)
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 */
585 g_free (pcwd);
587 /* Restart the subshell if it has died by SIGHUP, SIGQUIT, etc. */
588 while (!subshell_alive && !quit && use_subshell)
589 init_subshell ();
591 prompt_pos = 0;
593 return quit;
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 };
604 fd_set tmp;
605 FD_ZERO (&tmp);
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';
611 prompt_pos = 0;
614 while (subshell_alive
615 && (rc =
616 select (subshell_pty + 1, &tmp, NULL, NULL, &timeleft))) {
617 /* Check for `select' errors */
618 if (rc == -1) {
619 if (errno == EINTR)
620 continue;
621 else {
622 fprintf (stderr, "select (FD_SETSIZE, &tmp...): %s\r\n",
623 unix_error_string (errno));
624 exit (1);
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') {
634 prompt_pos = 0;
635 } else {
636 if (!pty_buffer[i])
637 continue;
639 subshell_prompt[prompt_pos++] = pty_buffer[i];
640 if (prompt_pos == prompt_size)
641 subshell_prompt =
642 g_realloc (subshell_prompt, prompt_size *= 2);
645 subshell_prompt[prompt_pos] = '\0';
647 if (rc == 0 && bytes == 0)
648 return FALSE;
649 return TRUE;
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);
663 #else
664 return 0;
665 #endif
668 /* Resize subshell_pty */
669 void resize_subshell (void)
671 if (use_subshell == 0)
672 return;
674 resize_tty (subshell_pty);
678 exit_subshell (void)
680 int subshell_quit = TRUE;
682 if (subshell_state != INACTIVE && subshell_alive)
683 subshell_quit =
684 !query_dialog (_("Warning"),
685 _(" The shell is still active. Quit anyway? "),
686 D_NORMAL, 2, _("&Yes"), _("&No"));
688 if (subshell_quit) {
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);
696 g_free (pty_buffer);
697 subshell_prompt = NULL;
698 pty_buffer = 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'`"
721 static char *
722 subshell_name_quote (const char *s)
724 char *ret, *d;
725 const char *su, *n;
726 const char *quote_cmd_start, *quote_cmd_end;
727 int c;
729 if (subshell_type == FISH) {
730 quote_cmd_start = "(printf \"%b\" '";
731 quote_cmd_end = "')";
732 } else {
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)));
740 if (d == NULL)
741 return NULL;
743 /* Prevent interpreting leading `-' as a switch for `cd' */
744 if (*s == '-') {
745 *d++ = '.';
746 *d++ = '/';
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.
758 su = s;
759 for (; su[0] != '\0'; ) {
760 n = str_cget_next_char_safe (su);
761 if (str_isalnum (su)) {
762 memcpy (d, su, n - su);
763 d+= n - su;
764 } else {
765 for (c = 0; c < n - su; c++) {
766 sprintf (d, "\\0%03o", (unsigned char) su[c]);
767 d += 5;
770 su = n;
773 strcpy (d, quote_cmd_end);
775 return ret;
779 /* If it actually changed the directory it returns true */
780 void
781 do_subshell_chdir (const char *directory, int do_update, int reset_prompt)
783 char *pcwd;
784 char *temp;
785 char *translate;
787 pcwd = vfs_translate_path_n (current_panel->cwd);
789 if (!
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. */
796 if (do_update)
797 do_update_prompt ();
798 g_free (pcwd);
799 return;
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);
805 if (*directory) {
806 translate = vfs_translate_path_n (directory);
807 if (translate) {
808 temp = subshell_name_quote (translate);
809 if (temp) {
810 write_all (subshell_pty, temp, strlen (temp));
811 g_free (temp);
812 } else {
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);
817 g_free (translate);
818 } else {
819 write_all (subshell_pty, ".", 1);
821 } else {
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);
851 g_free (cwd);
855 if (reset_prompt)
856 prompt_pos = 0;
857 update_prompt = FALSE;
859 g_free (pcwd);
860 /* Make sure that MC never stores the CWD in a silly format */
861 /* like /usr////lib/../bin, or the strcmp() above will fail */
865 void
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;
874 return;
879 /* Figure out whether the subshell has stopped, exited or been killed */
880 /* Possibly modifies: `subshell_alive', `subshell_stopped' and `quit' */
881 void
882 sigchld_handler (int sig)
884 int status;
885 pid_t pid;
887 (void) 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;
898 } else {
899 /* The user has suspended the subshell. Revive it */
900 kill (subshell_pid, SIGCONT);
902 } else {
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 */
910 #ifdef __linux__
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 */
917 kill (pid, SIGCONT);
918 else {
919 /* cons.saver has died - disable confole saving */
920 handle_console (CONSOLE_DONE);
921 console_flag = 0;
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 */
932 static int
933 feed_subshell (int how, int fail_on_error)
935 fd_set read_set; /* For `select' */
936 int maxfdp;
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 */
944 wtime.tv_sec = 10;
945 wtime.tv_usec = 0;
946 wptr = fail_on_error ? &wtime : NULL;
948 while (1) {
949 if (!subshell_alive)
950 return FALSE;
952 /* Prepare the file-descriptor set and call `select' */
954 FD_ZERO (&read_set);
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 */
966 if (errno == EINTR)
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));
971 exit (1);
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)
986 return FALSE;
988 if (bytes <= 0) {
989 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
990 fprintf (stderr, "read (subshell_pty...): %s\r\n",
991 unix_error_string (errno));
992 exit (1);
995 if (how == VISIBLY)
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 */
1003 bytes =
1004 read (subshell_pipe[READ], subshell_cwd,
1005 MC_MAXPATHLEN + 1);
1006 if (bytes <= 0) {
1007 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
1008 fprintf (stderr, "read (subshell_pipe[READ]...): %s\r\n",
1009 unix_error_string (errno));
1010 exit (1);
1013 subshell_cwd[bytes - 1] = 0; /* Squash the final '\n' */
1015 synchronize ();
1017 subshell_ready = TRUE;
1018 if (subshell_state == RUNNING_COMMAND) {
1019 subshell_state = INACTIVE;
1020 return 1;
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);
1028 if (bytes <= 0) {
1029 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
1030 fprintf (stderr,
1031 "read (STDIN_FILENO, pty_buffer...): %s\r\n",
1032 unix_error_string (errno));
1033 exit (1);
1036 for (i = 0; i < bytes; ++i)
1037 if (pty_buffer[i] == subshell_switch_key) {
1038 write_all (subshell_pty, pty_buffer, i);
1039 if (subshell_ready)
1040 subshell_state = INACTIVE;
1041 return TRUE;
1044 write_all (subshell_pty, pty_buffer, bytes);
1046 if (pty_buffer[bytes-1] == '\n' || pty_buffer[bytes-1] == '\r')
1047 subshell_ready = FALSE;
1048 } else {
1049 return 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 */
1089 #ifdef HAVE_GRANTPT
1091 /* System V version of pty_open_master */
1093 static int pty_open_master (char *pty_name)
1095 char *slave_name;
1096 int pty_master;
1098 #ifdef HAVE_POSIX_OPENPT
1099 pty_master = posix_openpt(O_RDWR);
1100 #elif HAVE_GETPT
1101 /* getpt () is a GNU extension (glibc 2.1.x) */
1102 pty_master = getpt ();
1103 #elif IS_AIX
1104 strcpy (pty_name, "/dev/ptc");
1105 pty_master = open (pty_name, O_RDWR);
1106 #else
1107 strcpy (pty_name, "/dev/ptmx");
1108 pty_master = open (pty_name, O_RDWR);
1109 #endif
1111 if (pty_master == -1)
1112 return -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 */
1118 close (pty_master);
1119 return -1;
1121 strcpy (pty_name, slave_name);
1122 return pty_master;
1125 /* System V version of pty_open_slave */
1126 static int
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));
1134 return -1;
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));
1142 close (pty_slave);
1143 return -1;
1146 if (!ioctl (pty_slave, I_FIND, "ldterm"))
1147 if (ioctl (pty_slave, I_PUSH, "ldterm") == -1) {
1148 fprintf (stderr,
1149 "ioctl (%d, I_PUSH, \"ldterm\") failed: %s\r\n",
1150 pty_slave, unix_error_string (errno));
1151 close (pty_slave);
1152 return -1;
1154 #if !defined(sgi) && !defined(__sgi)
1155 if (!ioctl (pty_slave, I_FIND, "ttcompat"))
1156 if (ioctl (pty_slave, I_PUSH, "ttcompat") == -1) {
1157 fprintf (stderr,
1158 "ioctl (%d, I_PUSH, \"ttcompat\") failed: %s\r\n",
1159 pty_slave, unix_error_string (errno));
1160 close (pty_slave);
1161 return -1;
1163 #endif /* sgi || __sgi */
1164 #endif /* I_FIND && I_PUSH */
1165 #endif /* __osf__ || __linux__ */
1167 fcntl(pty_slave, F_SETFD, FD_CLOEXEC);
1168 return pty_slave;
1171 #else /* !HAVE_GRANTPT */
1173 /* BSD version of pty_open_master */
1174 static int pty_open_master (char *pty_name)
1176 int pty_master;
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 */
1191 else
1192 continue; /* Try next pty device */
1194 pty_name [5] = 't'; /* Change "pty" to "tty" */
1195 if (access (pty_name, 6)){
1196 close (pty_master);
1197 pty_name [5] = 'p';
1198 continue;
1200 return pty_master;
1203 return -1; /* Ran out of pty devices */
1206 /* BSD version of pty_open_slave */
1207 static int
1208 pty_open_slave (const char *pty_name)
1210 int pty_slave;
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);
1222 return pty_slave;
1225 #endif /* !HAVE_GRANTPT */
1226 #endif /* HAVE_SUBSHELL_SUPPORT */