Just a little correction at the it.po file.
[midnight-commander.git] / src / subshell.c
blob07b5c8562e178c940808027994dbf128f9a988c7
1 /* Concurrent shell support for the Midnight Commander
2 Copyright (C) 1994, 1995 Dugan Porter
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of Version 2 of the GNU General Public
6 License, as published by the Free Software Foundation.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 #include <config.h>
19 #ifdef HAVE_SUBSHELL_SUPPORT
21 #ifndef _GNU_SOURCE
22 # define _GNU_SOURCE 1
23 #endif
25 #include <stdio.h>
26 #include <stdlib.h> /* For errno, putenv, etc. */
27 #include <errno.h> /* For errno on SunOS systems */
28 #include <termios.h> /* tcgetattr(), struct termios, etc. */
29 #include <sys/types.h> /* Required by unistd.h below */
30 #ifdef HAVE_SYS_IOCTL_H
31 # include <sys/ioctl.h> /* For ioctl() (surprise, surprise) */
32 #endif
33 #include <string.h> /* strstr(), strcpy(), etc. */
34 #include <signal.h> /* sigaction(), sigprocmask(), etc. */
35 #include <sys/stat.h> /* Required by dir.h & panel.h below */
36 #include <ctype.h> /* isalnum() */
38 #ifdef HAVE_UNISTD_H
39 # include <unistd.h> /* For pipe, fork, setsid, access etc */
40 #endif
42 #ifdef HAVE_STROPTS_H
43 # include <stropts.h> /* For I_PUSH */
44 #endif /* HAVE_STROPTS_H */
46 #include "global.h"
47 #include "tty.h" /* LINES */
48 #include "panel.h" /* cpanel */
49 #include "wtools.h" /* query_dialog() */
50 #include "main.h" /* do_update_prompt() */
51 #include "cons.saver.h" /* handle_console() */
52 #include "key.h" /* XCTRL */
53 #include "subshell.h"
55 #ifndef WEXITSTATUS
56 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
57 #endif
59 #ifndef WIFEXITED
60 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
61 #endif
63 /* tcsh closes all non-standard file descriptors, so we have to use a pipe */
64 static char tcsh_fifo[128];
66 /* Local functions */
67 static void init_raw_mode (void);
68 static int feed_subshell (int how, int fail_on_error);
69 static void synchronize (void);
70 static int pty_open_master (char *pty_name);
71 static int pty_open_slave (const char *pty_name);
72 static int resize_tty (int fd);
74 #ifndef STDIN_FILENO
75 # define STDIN_FILENO 0
76 #endif
78 #ifndef STDOUT_FILENO
79 # define STDOUT_FILENO 1
80 #endif
82 #ifndef STDERR_FILENO
83 # define STDERR_FILENO 2
84 #endif
86 /* If using a subshell for evaluating commands this is true */
87 int use_subshell =
88 #ifdef SUBSHELL_OPTIONAL
89 FALSE;
90 #else
91 TRUE;
92 #endif
94 /* File descriptor of the pseudoterminal used by the subshell */
95 int subshell_pty = 0;
97 /* The key for switching back to MC from the subshell */
98 static const char subshell_switch_key = XCTRL('o') & 255;
100 /* State of the subshell:
101 * INACTIVE: the default state; awaiting a command
102 * ACTIVE: remain in the shell until the user hits `subshell_switch_key'
103 * RUNNING_COMMAND: return to MC when the current command finishes */
104 enum subshell_state_enum subshell_state;
106 /* Holds the latest prompt captured from the subshell */
107 char *subshell_prompt = NULL;
109 /* Initial length of the buffer for the subshell's prompt */
110 #define INITIAL_PROMPT_SIZE 10
112 /* Used by the child process to indicate failure to start the subshell */
113 #define FORK_FAILURE 69 /* Arbitrary */
115 /* Initial length of the buffer for all I/O with the subshell */
116 #define INITIAL_PTY_BUFFER_SIZE 100 /* Arbitrary; but keep it >= 80 */
118 /* For pipes */
119 enum {READ=0, WRITE=1};
121 static char *pty_buffer; /* For reading/writing on the subshell's pty */
122 static int pty_buffer_size; /* The buffer grows as needed */
123 static int subshell_pipe[2]; /* To pass CWD info from the subshell to MC */
124 static pid_t subshell_pid = 1; /* The subshell's process ID */
125 static char subshell_cwd[MC_MAXPATHLEN+1]; /* One extra char for final '\n' */
127 /* Subshell type (gleaned from the SHELL environment variable, if available) */
128 static enum {BASH, TCSH, ZSH} subshell_type;
130 /* Flag to indicate whether the subshell is ready for next command */
131 static int subshell_ready;
133 /* The following two flags can be changed by the SIGCHLD handler. This is */
134 /* OK, because the `int' type is updated atomically on all known machines */
135 static volatile int subshell_alive, subshell_stopped;
137 /* We store the terminal's initial mode here so that we can configure
138 the pty similarly, and also so we can restore the real terminal to
139 sanity if we have to exit abruptly */
140 static struct termios shell_mode;
142 /* This is a transparent mode for the terminal where MC is running on */
143 /* It is used when the shell is active, so that the control signals */
144 /* are delivered to the shell pty */
145 static struct termios raw_mode;
147 /* This counter indicates how many characters of prompt we have read */
148 /* FIXME: try to figure out why this had to become global */
149 static int prompt_pos;
153 * Prepare child process to running the shell and run it.
155 * Modifies the global variables (in the child process only):
156 * shell_mode
158 * Returns: never.
160 static void init_subshell_child (const char *pty_name)
162 int pty_slave;
163 char *init_file = NULL;
164 #ifdef HAVE_GETSID
165 pid_t mc_sid;
166 #endif /* HAVE_GETSID */
168 setsid (); /* Get a fresh terminal session */
170 /* Open the slave side of the pty: again */
171 pty_slave = pty_open_slave (pty_name);
173 /* This must be done before closing the master side of the pty, */
174 /* or it will fail on certain idiotic systems, such as Solaris. */
176 /* Close master side of pty. This is important; apart from */
177 /* freeing up the descriptor for use in the subshell, it also */
178 /* means that when MC exits, the subshell will get a SIGHUP and */
179 /* exit too, because there will be no more descriptors pointing */
180 /* at the master side of the pty and so it will disappear. */
182 close (subshell_pty);
184 /* Make sure that it has become our controlling terminal */
186 /* Redundant on Linux and probably most systems, but just in case: */
188 #ifdef TIOCSCTTY
189 ioctl (pty_slave, TIOCSCTTY, 0);
190 #endif
192 /* Configure its terminal modes and window size */
194 /* Set up the pty with the same termios flags as our own tty, plus */
195 /* TOSTOP, which keeps background processes from writing to the pty */
197 shell_mode.c_lflag |= TOSTOP; /* So background writers get SIGTTOU */
198 if (tcsetattr (pty_slave, TCSANOW, &shell_mode))
200 perror (__FILE__": couldn't set pty terminal modes");
201 _exit (FORK_FAILURE);
204 /* Set the pty's size (80x25 by default on Linux) according to the */
205 /* size of the real terminal as calculated by ncurses, if possible */
206 resize_tty (pty_slave);
208 /* Set up the subshell's environment and init file name */
210 /* It simplifies things to change to our home directory here, */
211 /* and the user's startup file may do a `cd' command anyway */
212 chdir (home_dir); /* FIXME? What about when we re-run the subshell? */
214 #ifdef HAVE_GETSID
215 /* Set MC_SID to prevent running one mc from another */
216 mc_sid = getsid (0);
217 if (mc_sid != -1) {
218 char sid_str[BUF_SMALL];
219 g_snprintf (sid_str, sizeof (sid_str), "MC_SID=%ld", (long) mc_sid);
220 putenv (sid_str);
222 #endif /* HAVE_GETSID */
224 switch (subshell_type)
226 case BASH:
227 init_file = ".mc/bashrc";
228 if (access (init_file, R_OK) == -1)
229 init_file = ".bashrc";
231 /* Make MC's special commands not show up in bash's history */
232 putenv ("HISTCONTROL=ignorespace");
234 /* Allow alternative readline settings for MC */
235 if (access (".mc/inputrc", R_OK) == 0)
236 putenv ("INPUTRC=.mc/inputrc");
238 break;
240 /* TODO: Find a way to pass initfile to TCSH and ZSH */
241 case TCSH: case ZSH:
242 break;
244 default:
245 fprintf (stderr, __FILE__": unimplemented subshell type %d\n",
246 subshell_type);
247 _exit (FORK_FAILURE);
250 /* Attach all our standard file descriptors to the pty */
252 /* This is done just before the fork, because stderr must still */
253 /* be connected to the real tty during the above error messages; */
254 /* otherwise the user will never see them. */
256 dup2 (pty_slave, STDIN_FILENO);
257 dup2 (pty_slave, STDOUT_FILENO);
258 dup2 (pty_slave, STDERR_FILENO);
260 /* Execute the subshell at last */
262 close (subshell_pipe[READ]);
263 close (pty_slave); /* These may be FD_CLOEXEC, but just in case... */
265 switch (subshell_type)
267 case BASH:
268 execl (shell, "bash", "-rcfile", init_file, NULL);
269 break;
271 case TCSH:
272 execl (shell, "tcsh", NULL);
273 break;
275 case ZSH:
276 /* Use -g to exclude cmds beginning with space from history
277 * and -Z to use the line editor on non-interactive term */
278 execl (shell, "zsh", "-Z", "-g", NULL);
280 break;
283 /* If we get this far, everything failed miserably */
284 _exit (FORK_FAILURE);
288 #ifdef HAVE_GETSID
290 * Check MC_SID to prevent running one mc from another.
291 * Return:
292 * 0 if no parent mc in our session was found,
293 * 1 if parent mc was found and the user wants to continue,
294 * 2 if parent mc was found and the user wants to quit mc.
296 static int
297 check_sid ()
299 pid_t my_sid, old_sid;
300 char *sid_str;
301 int r;
303 sid_str = getenv ("MC_SID");
304 if (!sid_str)
305 return 0;
307 old_sid = (pid_t) strtol (sid_str, NULL, 0);
308 if (!old_sid)
309 return 0;
311 my_sid = getsid (0);
312 if (my_sid == -1)
313 return 0;
315 /* The parent mc is in a different session, it's OK */
316 if (old_sid != my_sid)
317 return 0;
319 r = query_dialog (_("Warning"),
320 _("GNU Midnight Commander is already\n"
321 "running on this terminal.\n"
322 "Subshell support will be disabled."), D_ERROR, 2,
323 _("&OK"), _("&Quit"));
324 if (r != 0) {
325 return 2;
328 return 1;
330 #endif /* HAVE_GETSID */
334 * Fork the subshell, and set up many, many things.
336 * Possibly modifies the global variables:
337 * subshell_type, subshell_alive, subshell_stopped, subshell_pid
338 * use_subshell - Is set to FALSE if we can't run the subshell
339 * quit - Can be set to SUBSHELL_EXIT by the SIGCHLD handler
342 void init_subshell (void)
344 /* This must be remembered across calls to init_subshell() */
345 static char pty_name[BUF_SMALL];
346 char precmd[BUF_SMALL];
347 int pty_slave = -1;
349 #ifdef HAVE_GETSID
350 switch (check_sid ()) {
351 case 1:
352 use_subshell = FALSE;
353 return;
354 case 2:
355 use_subshell = FALSE;
356 midnight_shutdown = 1;
357 return;
359 #endif /* HAVE_GETSID */
361 /* Take the current (hopefully pristine) tty mode and make */
362 /* a raw mode based on it now, before we do anything else with it */
363 init_raw_mode ();
365 if (subshell_pty == 0) /* First time through */
367 /* Find out what type of shell we have */
369 if (strstr (shell, "/zsh") || getenv("ZSH_VERSION"))
370 subshell_type = ZSH;
371 else if (strstr (shell, "/tcsh"))
372 subshell_type = TCSH;
373 else if (strstr (shell, "/bash") || getenv ("BASH"))
374 subshell_type = BASH;
375 else
377 use_subshell = FALSE;
378 return;
381 /* Open a pty for talking to the subshell */
383 /* FIXME: We may need to open a fresh pty each time on SVR4 */
385 subshell_pty = pty_open_master (pty_name);
386 if (subshell_pty == -1)
388 fputs (__FILE__": couldn't open master side of pty\n", stderr);
389 perror ("pty_open_master");
390 use_subshell = FALSE;
391 return;
393 pty_slave = pty_open_slave (pty_name);
394 if (pty_slave == -1)
396 fprintf (stderr, "couldn't open slave side of pty (%s)\n\r",
397 pty_name);
398 use_subshell = FALSE;
399 return;
402 /* Initialise the pty's I/O buffer */
404 pty_buffer_size = INITIAL_PTY_BUFFER_SIZE;
405 pty_buffer = (char *) g_malloc (pty_buffer_size);
407 /* Create a pipe for receiving the subshell's CWD */
409 if (subshell_type == TCSH)
411 g_snprintf (tcsh_fifo, sizeof (tcsh_fifo), "%s/mc.pipe.%d",
412 mc_tmpdir(), getpid ());
413 if (mkfifo (tcsh_fifo, 0600) == -1)
415 perror (__FILE__": mkfifo");
416 use_subshell = FALSE;
417 return;
420 /* Opening the FIFO as O_RDONLY or O_WRONLY causes deadlock */
422 if ((subshell_pipe[READ] = open (tcsh_fifo, O_RDWR)) == -1 ||
423 (subshell_pipe[WRITE] = open (tcsh_fifo, O_RDWR)) == -1)
425 fprintf (stderr, _("Cannot open named pipe %s\n"), tcsh_fifo);
426 perror (__FILE__": open");
427 use_subshell = FALSE;
428 return;
431 else /* subshell_type is BASH or ZSH */
432 if (pipe (subshell_pipe))
434 perror (__FILE__": couldn't create pipe");
435 use_subshell = FALSE;
436 return;
440 /* Fork the subshell */
442 subshell_alive = TRUE;
443 subshell_stopped = FALSE;
444 subshell_pid = fork ();
446 if (subshell_pid == -1)
448 perror (__FILE__": couldn't spawn the subshell process");
449 /* We exit here because, if the process table is full, the */
450 /* other method of running user commands won't work either */
451 exit (1);
454 if (subshell_pid == 0) /* We are in the child process */
456 init_subshell_child (pty_name);
459 /* pty_slave is only opened when called the first time */
460 if (pty_slave != -1) {
461 close(pty_slave);
464 /* Set up `precmd' or equivalent for reading the subshell's CWD */
466 switch (subshell_type)
468 case BASH:
469 g_snprintf (precmd, sizeof (precmd), " PROMPT_COMMAND='pwd>&%d;kill -STOP $$'\n",
470 subshell_pipe[WRITE]);
471 break;
473 case ZSH:
474 g_snprintf (precmd, sizeof (precmd), " precmd(){ pwd>&%d;kill -STOP $$ }\n",
475 subshell_pipe[WRITE]);
476 break;
478 case TCSH:
479 g_snprintf (precmd, sizeof (precmd),
480 "set echo_style=both;"
481 "alias precmd 'echo $cwd:q >>%s;kill -STOP $$'\n",
482 tcsh_fifo);
483 break;
485 write (subshell_pty, precmd, strlen (precmd));
487 /* Wait until the subshell has started up and processed the command */
489 subshell_state = RUNNING_COMMAND;
490 enable_interrupt_key ();
491 if (!feed_subshell (QUIETLY, TRUE)){
492 use_subshell = FALSE;
494 disable_interrupt_key ();
495 if (!subshell_alive)
496 use_subshell = FALSE; /* Subshell died instantly, so don't use it */
500 static void init_raw_mode ()
502 static int initialized = 0;
504 /* MC calls reset_shell_mode() in pre_exec() to set the real tty to its */
505 /* original settings. However, here we need to make this tty very raw, */
506 /* so that all keyboard signals, XON/XOFF, etc. will get through to the */
507 /* pty. So, instead of changing the code for execute(), pre_exec(), */
508 /* etc, we just set up the modes we need here, before each command. */
510 if (initialized == 0) /* First time: initialise `raw_mode' */
512 tcgetattr (STDOUT_FILENO, &raw_mode);
513 raw_mode.c_lflag &= ~ICANON; /* Disable line-editing chars, etc. */
514 raw_mode.c_lflag &= ~ISIG; /* Disable intr, quit & suspend chars */
515 raw_mode.c_lflag &= ~ECHO; /* Disable input echoing */
516 raw_mode.c_iflag &= ~IXON; /* Pass ^S/^Q to subshell undisturbed */
517 raw_mode.c_iflag &= ~ICRNL; /* Don't translate CRs into LFs */
518 raw_mode.c_oflag &= ~OPOST; /* Don't postprocess output */
519 raw_mode.c_cc[VTIME] = 0; /* IE: wait forever, and return as */
520 raw_mode.c_cc[VMIN] = 1; /* soon as a character is available */
521 initialized = 1;
526 int invoke_subshell (const char *command, int how, char **new_dir)
528 /* Make the MC terminal transparent */
529 tcsetattr (STDOUT_FILENO, TCSANOW, &raw_mode);
531 /* Make the subshell change to MC's working directory */
532 if (new_dir)
533 do_subshell_chdir (cpanel->cwd, TRUE, 1);
535 if (command == NULL) /* The user has done "C-o" from MC */
537 if (subshell_state == INACTIVE)
539 subshell_state = ACTIVE;
540 /* FIXME: possibly take out this hack; the user can
541 re-play it by hitting C-hyphen a few times! */
542 write (subshell_pty, " \b", 2); /* Hack to make prompt reappear */
545 else /* MC has passed us a user command */
547 if (how == QUIETLY)
548 write (subshell_pty, " ", 1);
549 /* FIXME: if command is long (>8KB ?) we go comma */
550 write (subshell_pty, command, strlen (command));
551 write (subshell_pty, "\n", 1);
552 subshell_state = RUNNING_COMMAND;
553 subshell_ready = FALSE;
556 feed_subshell (how, FALSE);
558 if (new_dir && subshell_alive && strcmp (subshell_cwd, cpanel->cwd))
559 *new_dir = subshell_cwd; /* Make MC change to the subshell's CWD */
561 /* Restart the subshell if it has died by SIGHUP, SIGQUIT, etc. */
562 while (!subshell_alive && !quit && use_subshell)
563 init_subshell ();
565 prompt_pos = 0;
567 return quit;
571 int read_subshell_prompt (void)
573 static int prompt_size = INITIAL_PROMPT_SIZE;
574 int bytes = 0, i, rc = 0;
575 struct timeval timeleft = {0, 0};
577 fd_set tmp;
578 FD_ZERO (&tmp);
579 FD_SET (subshell_pty, &tmp);
581 if (subshell_prompt == NULL) /* First time through */
583 subshell_prompt = (char *) g_malloc (prompt_size);
584 *subshell_prompt = '\0';
585 prompt_pos = 0;
588 while (subshell_alive &&
589 (rc = select (subshell_pty + 1, &tmp, NULL, NULL, &timeleft)))
591 /* Check for `select' errors */
592 if (rc == -1) {
593 if (errno == EINTR)
594 continue;
595 else {
596 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
597 perror ("\n"__FILE__": select (FD_SETSIZE, &tmp...)");
598 exit (1);
602 bytes = read (subshell_pty, pty_buffer, pty_buffer_size);
604 /* Extract the prompt from the shell output */
606 for (i=0; i<bytes; ++i)
607 if (pty_buffer[i] == '\n' || pty_buffer[i] == '\r'){
608 prompt_pos = 0;
609 } else {
610 if (!pty_buffer [i])
611 continue;
613 subshell_prompt[prompt_pos++] = pty_buffer[i];
614 if (prompt_pos == prompt_size)
615 subshell_prompt = (char *) g_realloc (subshell_prompt,
616 prompt_size *= 2);
619 subshell_prompt[prompt_pos] = '\0';
621 if (rc == 0 && bytes == 0)
622 return FALSE;
623 return TRUE;
626 /* Resize given terminal using TIOCSWINSZ, return ioctl() result */
627 static int resize_tty (int fd)
629 #if defined TIOCSWINSZ && !defined SCO_FLAVOR
630 struct winsize tty_size;
632 tty_size.ws_row = LINES;
633 tty_size.ws_col = COLS;
634 tty_size.ws_xpixel = tty_size.ws_ypixel = 0;
636 return ioctl (fd, TIOCSWINSZ, &tty_size);
637 #else
638 return 0;
639 #endif
642 /* Resize subshell_pty */
643 void resize_subshell (void)
645 resize_tty (subshell_pty);
648 int exit_subshell (void)
650 int quit = TRUE;
652 if (subshell_state != INACTIVE && subshell_alive)
653 quit = !query_dialog (_("Warning"), _(" The shell is still active. Quit anyway? "),
654 0, 2, _("&Yes"), _("&No"));
656 if (quit && subshell_type == TCSH)
658 if (unlink (tcsh_fifo) == -1)
659 perror (__FILE__": couldn't remove named pipe /tmp/mc.pipe.NNN");
662 g_free (subshell_prompt);
663 subshell_prompt = NULL;
665 return quit;
670 * Carefully quote directory name to allow entering any directory safely,
671 * no matter what weird characters it may contain in its name.
672 * NOTE: Treat directory name an untrusted data, don't allow it to cause
673 * executing any commands in the shell. Escape all control characters.
674 * Use following technique:
676 * for bash - echo with `-e', 3-digit octal numbers:
677 * cd "`echo -e '\ooo...\ooo'`"
679 * for zsh - echo with `-e', 4-digit octal numbers:
680 * cd "`echo '\oooo...\oooo'`"
682 * for tcsh - echo without `-e', 4-digit octal numbers:
683 * cd "`echo '\oooo...\oooo'`"
685 static char *
686 subshell_name_quote (const char *s)
688 char *ret, *d;
689 const char echo_cmd[] = "\"`echo '";
690 const char echo_e_cmd[] = "\"`echo -e '";
691 const char common_end[] = "'`\"";
692 const char *cmd_start;
693 int len;
696 * Factor 5 because we need \, 0 and 3 other digits per character
697 * in the worst case (tcsh and zsh).
699 d = ret = g_malloc (5 * strlen (s) + 16);
700 if (!d)
701 return NULL;
703 /* Prevent interpreting leading `-' as a switch for `cd' */
704 if (*s == '-') {
705 *d++ = '.';
706 *d++ = '/';
709 /* echo in tcsh doesn't understand the "-e" option */
710 if (subshell_type == TCSH)
711 cmd_start = echo_cmd;
712 else
713 cmd_start = echo_e_cmd;
715 /* Copy the beginning of the command to the buffer */
716 len = strlen (cmd_start);
717 memcpy (d, cmd_start, len);
718 d += len;
721 * Print every character in octal format with the leading backslash.
722 * tcsh and zsh may require 4-digit octals, bash < 2.05b doesn't like them.
724 if (subshell_type == BASH) {
725 for (; *s; s++) {
726 /* Must quote numbers, so that they are not glued to octals */
727 if (isalpha ((unsigned char) *s)) {
728 sprintf (d, "%c", (unsigned char) *s);
729 d += 1;
730 } else {
731 sprintf (d, "\\%03o", (unsigned char) *s);
732 d += 4;
735 } else {
736 for (; *s; s++) {
737 if (isalnum ((unsigned char) *s)) {
738 sprintf (d, "%c", (unsigned char) *s);
739 d += 1;
740 } else {
741 sprintf (d, "\\0%03o", (unsigned char) *s);
742 d += 5;
747 memcpy (d, common_end, sizeof (common_end));
749 return ret;
753 /* If it actually changed the directory it returns true */
754 void do_subshell_chdir (const char *directory, int do_update, int reset_prompt)
756 if (!(subshell_state == INACTIVE && strcmp (subshell_cwd, cpanel->cwd))){
757 /* We have to repaint the subshell prompt if we read it from
758 * the main program. Please note that in the code after this
759 * if, the cd command that is sent will make the subshell
760 * repaint the prompt, so we don't have to paint it. */
761 if (do_update)
762 do_update_prompt ();
763 return;
766 /* The initial space keeps this out of the command history (in bash
767 because we set "HISTCONTROL=ignorespace") */
768 write (subshell_pty, " cd ", 4);
769 if (*directory) {
770 char *temp;
771 temp = subshell_name_quote (directory);
772 if (temp) {
773 write (subshell_pty, temp, strlen (temp));
774 g_free (temp);
775 } else {
776 /* Should not happen unless the directory name is so long
777 that we don't have memory to quote it. */
778 write (subshell_pty, ".", 1);
780 } else {
781 write (subshell_pty, "/", 1);
783 write (subshell_pty, "\n", 1);
785 subshell_state = RUNNING_COMMAND;
786 feed_subshell (QUIETLY, FALSE);
788 if (subshell_alive && strcmp (subshell_cwd, cpanel->cwd) && strcmp (cpanel->cwd, "."))
789 fprintf (stderr, _("Warning: Cannot change to %s.\n"), cpanel->cwd);
791 if (reset_prompt)
792 prompt_pos = 0;
793 update_prompt = FALSE;
794 /* Make sure that MC never stores the CWD in a silly format */
795 /* like /usr////lib/../bin, or the strcmp() above will fail */
799 void subshell_get_console_attributes (void)
801 /* Get our current terminal modes */
803 if (tcgetattr (STDOUT_FILENO, &shell_mode))
805 perror (__FILE__": couldn't get terminal settings");
806 use_subshell = FALSE;
807 return;
812 /* Figure out whether the subshell has stopped, exited or been killed */
813 /* Possibly modifies: `subshell_alive', `subshell_stopped' and `quit' */
814 void
815 sigchld_handler (int sig)
817 int status;
818 pid_t pid;
820 pid = waitpid (subshell_pid, &status, WUNTRACED | WNOHANG);
822 if (pid == subshell_pid) {
823 /* Figure out what has happened to the subshell */
825 if (WIFSTOPPED (status)) {
826 if (WSTOPSIG (status) == SIGSTOP) {
827 /* The subshell has received a SIGSTOP signal */
828 subshell_stopped = TRUE;
829 } else {
830 /* The user has suspended the subshell. Revive it */
831 kill (subshell_pid, SIGCONT);
833 } else {
834 /* The subshell has either exited normally or been killed */
835 subshell_alive = FALSE;
836 delete_select_channel (subshell_pty);
837 if (WIFEXITED (status) && WEXITSTATUS (status) != FORK_FAILURE)
838 quit |= SUBSHELL_EXIT; /* Exited normally */
841 #ifdef __linux__
842 pid = waitpid (cons_saver_pid, &status, WUNTRACED | WNOHANG);
844 if (pid == cons_saver_pid) {
846 if (WIFSTOPPED (status))
847 /* Someone has stopped cons.saver - restart it */
848 kill (pid, SIGCONT);
849 else {
850 /* cons.saver has died - disable confole saving */
851 handle_console (CONSOLE_DONE);
852 console_flag = 0;
856 #endif /* __linux__ */
858 /* If we got here, some other child exited; ignore it */
859 #ifdef __EMX__ /* Need to report */
860 pid = wait (&status);
861 #endif
865 /* Feed the subshell our keyboard input until it says it's finished */
866 static int feed_subshell (int how, int fail_on_error)
868 fd_set read_set; /* For `select' */
869 int maxfdp;
870 int bytes; /* For the return value from `read' */
871 int i; /* Loop counter */
873 struct timeval wtime; /* Maximum time we wait for the subshell */
874 struct timeval *wptr;
876 /* we wait up to 10 seconds if fail_on_error, forever otherwise */
877 wtime.tv_sec = 10;
878 wtime.tv_usec = 0;
879 wptr = fail_on_error ? &wtime : NULL;
881 while (1) {
882 if (!subshell_alive)
883 return FALSE;
885 /* Prepare the file-descriptor set and call `select' */
887 FD_ZERO (&read_set);
888 FD_SET (subshell_pty, &read_set);
889 FD_SET (subshell_pipe[READ], &read_set);
890 maxfdp = max (subshell_pty, subshell_pipe[READ]);
891 if (how == VISIBLY) {
892 FD_SET (STDIN_FILENO, &read_set);
893 maxfdp = max (maxfdp, STDIN_FILENO);
896 if (select (maxfdp + 1, &read_set, NULL, NULL, wptr) == -1){
898 /* Despite using SA_RESTART, we still have to check for this */
899 if (errno == EINTR)
900 continue; /* try all over again */
901 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
902 perror ("\n"__FILE__": select (FD_SETSIZE, &read_set...)");
903 exit (1);
906 if (FD_ISSET (subshell_pty, &read_set))
907 /* Read from the subshell, write to stdout */
909 /* This loop improves performance by reducing context switches
910 by a factor of 20 or so... unfortunately, it also hangs MC
911 randomly, because of an apparent Linux bug. Investigate. */
912 /* for (i=0; i<5; ++i) * FIXME -- experimental */
914 bytes = read (subshell_pty, pty_buffer, pty_buffer_size);
916 /* The subshell has died */
917 if (bytes == -1 && errno == EIO && !subshell_alive)
918 return FALSE;
920 if (bytes <= 0)
922 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
923 perror ("\n"__FILE__": read (subshell_pty...)");
924 exit (1);
927 if (how == VISIBLY)
928 write (STDOUT_FILENO, pty_buffer, bytes);
931 else if (FD_ISSET (subshell_pipe[READ], &read_set))
932 /* Read the subshell's CWD and capture its prompt */
935 bytes = read (subshell_pipe[READ], subshell_cwd, MC_MAXPATHLEN+1);
936 if (bytes <= 0)
938 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
939 perror ("\n"__FILE__": read (subshell_pipe[READ]...)");
940 exit (1);
943 subshell_cwd[bytes-1] = 0; /* Squash the final '\n' */
945 synchronize ();
947 subshell_ready = TRUE;
948 if (subshell_state == RUNNING_COMMAND)
950 subshell_state = INACTIVE;
951 return 1;
955 else if (FD_ISSET (STDIN_FILENO, &read_set))
956 /* Read from stdin, write to the subshell */
958 bytes = read (STDIN_FILENO, pty_buffer, pty_buffer_size);
959 if (bytes <= 0)
961 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
962 perror ("\n"__FILE__": read (STDIN_FILENO, pty_buffer...)");
963 exit (1);
966 for (i=0; i<bytes; ++i)
967 if (pty_buffer[i] == subshell_switch_key)
969 write (subshell_pty, pty_buffer, i);
970 if (subshell_ready)
971 subshell_state = INACTIVE;
972 return TRUE;
975 write (subshell_pty, pty_buffer, bytes);
976 subshell_ready = FALSE;
977 } else {
978 return FALSE;
984 /* Wait until the subshell dies or stops. If it stops, make it resume. */
985 /* Possibly modifies the globals `subshell_alive' and `subshell_stopped' */
986 static void synchronize (void)
988 sigset_t sigchld_mask, old_mask;
990 sigemptyset (&sigchld_mask);
991 sigaddset (&sigchld_mask, SIGCHLD);
992 sigprocmask (SIG_BLOCK, &sigchld_mask, &old_mask);
995 * SIGCHLD should not be blocked, but we unblock it just in case.
996 * This is known to be useful for cygwin 1.3.12 and older.
998 sigdelset (&old_mask, SIGCHLD);
1000 /* Wait until the subshell has stopped */
1001 while (subshell_alive && !subshell_stopped)
1002 sigsuspend (&old_mask);
1004 /* Discard all remaining data from stdin to the subshell */
1005 tcflush (subshell_pty, TCOFLUSH);
1007 subshell_stopped = FALSE;
1008 kill (subshell_pid, SIGCONT);
1010 sigprocmask (SIG_SETMASK, &old_mask, NULL);
1011 /* We can't do any better without modifying the shell(s) */
1014 /* pty opening functions */
1016 #ifdef SCO_FLAVOR
1018 /* SCO version of pty_open_master */
1020 static int pty_open_master (char *pty_name)
1022 int pty_master;
1023 int num;
1024 char *ptr;
1026 strcpy (pty_name, "/dev/ptyp");
1027 ptr = pty_name+9;
1028 for (num=0;;num++)
1030 g_snprintf(ptr, 9, "%d",num); /* surpriiise ... SCO lacks itoa() */
1031 /* Try to open master */
1032 if ((pty_master = open (pty_name, O_RDWR)) == -1) {
1033 if (errno == ENOENT) /* Different from EIO */
1034 return -1; /* Out of pty devices */
1035 else
1036 continue; /* Try next pty device */
1038 pty_name [5] = 't'; /* Change "pty" to "tty" */
1039 if (access (pty_name, 6)){
1040 close (pty_master);
1041 pty_name [5] = 'p';
1042 continue;
1044 return pty_master;
1046 return -1; /* Ran out of pty devices */
1049 /* SCO version of pty_open_slave */
1051 static int pty_open_slave (const char *pty_name)
1053 int pty_slave;
1054 struct group *group_info = getgrnam ("terminal");
1056 if (group_info != NULL)
1058 /* The following two calls will only succeed if we are root */
1059 /* [Commented out while permissions problem is investigated] */
1060 /* chown (pty_name, getuid (), group_info->gr_gid); FIXME */
1061 /* chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP); FIXME */
1063 if ((pty_slave = open (pty_name, O_RDWR)) == -1)
1064 perror ("open (pty_name, O_RDWR)");
1065 return pty_slave;
1068 #elif HAVE_GRANTPT /* !HAVE_SCO */
1070 /* System V version of pty_open_master */
1072 static int pty_open_master (char *pty_name)
1074 char *slave_name;
1075 int pty_master;
1078 #ifdef HAVE_GETPT
1079 /* getpt () is a GNU extension (glibc 2.1.x) */
1080 pty_master = getpt ();
1081 #elif IS_AIX
1082 strcpy (pty_name, "/dev/ptc");
1083 pty_master = open (pty_name, O_RDWR);
1084 #else
1085 strcpy (pty_name, "/dev/ptmx");
1086 pty_master = open (pty_name, O_RDWR);
1087 #endif
1088 if (pty_master == -1)
1089 return -1;
1091 if (grantpt (pty_master) == -1 /* Grant access to slave */
1092 || unlockpt (pty_master) == -1 /* Clear slave's lock flag */
1093 || !(slave_name = ptsname (pty_master))) /* Get slave's name */
1095 close (pty_master);
1096 return -1;
1098 strcpy (pty_name, slave_name);
1099 return pty_master;
1102 /* System V version of pty_open_slave */
1103 static int pty_open_slave (const char *pty_name)
1105 int pty_slave = open (pty_name, O_RDWR);
1107 if (pty_slave == -1)
1109 perror ("open (pty_name, O_RDWR)");
1110 return -1;
1113 #if !defined(__osf__) && !defined(__linux__)
1114 #if defined (I_FIND) && defined (I_PUSH)
1115 if (!ioctl (pty_slave, I_FIND, "ptem"))
1116 if (ioctl (pty_slave, I_PUSH, "ptem") == -1)
1118 fprintf (stderr, "ioctl (pty_slave, I_PUSH, \"ptem\") failed\n");
1119 close (pty_slave);
1120 return -1;
1123 if (!ioctl (pty_slave, I_FIND, "ldterm"))
1124 if (ioctl (pty_slave, I_PUSH, "ldterm") == -1)
1126 fprintf (stderr, "ioctl (pty_slave, I_PUSH, \"ldterm\") failed\n");
1127 close (pty_slave);
1128 return -1;
1131 #if !defined(sgi) && !defined(__sgi)
1132 if (!ioctl (pty_slave, I_FIND, "ttcompat"))
1133 if (ioctl (pty_slave, I_PUSH, "ttcompat") == -1)
1135 fprintf (stderr, "ioctl (pty_slave, I_PUSH, \"ttcompat\") failed\n");
1136 close (pty_slave);
1137 return -1;
1139 #endif /* sgi || __sgi */
1140 #endif /* I_FIND && I_PUSH */
1141 #endif /* __osf__ || __linux__ */
1143 return pty_slave;
1146 #else /* !HAVE_SCO && !HAVE_GRANTPT */
1148 /* BSD version of pty_open_master */
1149 static int pty_open_master (char *pty_name)
1151 int pty_master;
1152 char *ptr1, *ptr2;
1154 strcpy (pty_name, "/dev/ptyXX");
1155 for (ptr1 = "pqrstuvwxyzPQRST"; *ptr1; ++ptr1)
1157 pty_name [8] = *ptr1;
1158 for (ptr2 = "0123456789abcdef"; *ptr2; ++ptr2)
1160 pty_name [9] = *ptr2;
1162 /* Try to open master */
1163 if ((pty_master = open (pty_name, O_RDWR)) == -1) {
1164 if (errno == ENOENT) /* Different from EIO */
1165 return -1; /* Out of pty devices */
1166 else
1167 continue; /* Try next pty device */
1169 pty_name [5] = 't'; /* Change "pty" to "tty" */
1170 if (access (pty_name, 6)){
1171 close (pty_master);
1172 pty_name [5] = 'p';
1173 continue;
1175 return pty_master;
1178 return -1; /* Ran out of pty devices */
1181 /* BSD version of pty_open_slave */
1182 static int pty_open_slave (const char *pty_name)
1184 int pty_slave;
1185 struct group *group_info = getgrnam ("tty");
1187 if (group_info != NULL)
1189 /* The following two calls will only succeed if we are root */
1190 /* [Commented out while permissions problem is investigated] */
1191 /* chown (pty_name, getuid (), group_info->gr_gid); FIXME */
1192 /* chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP); FIXME */
1194 if ((pty_slave = open (pty_name, O_RDWR)) == -1)
1195 perror ("open (pty_name, O_RDWR)");
1196 return pty_slave;
1199 #endif /* !HAVE_SCO && !HAVE_GRANTPT */
1200 #endif /* HAVE_SUBSHELL_SUPPORT */