root/src/subshell/common.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. write_all
  2. init_subshell_child
  3. check_sid
  4. init_raw_mode
  5. synchronize
  6. feed_subshell
  7. pty_open_master
  8. pty_open_slave
  9. pty_open_master
  10. pty_open_slave
  11. init_subshell_precmd
  12. subshell_name_quote
  13. init_subshell
  14. invoke_subshell
  15. read_subshell_prompt
  16. do_update_prompt
  17. exit_subshell
  18. do_subshell_chdir
  19. subshell_get_console_attributes
  20. sigchld_handler

   1 /*
   2    Concurrent shell support for the Midnight Commander
   3 
   4    Copyright (C) 1994-2019
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Alexander Kriegisch <Alexander@Kriegisch.name>
   9    Aliaksey Kandratsenka <alk@tut.by>
  10    Andreas Mohr <and@gmx.li>
  11    Andrew Borodin <aborodin@vmail.ru>
  12    Andrew Borodin <borodin@borodin.zarya>
  13    Andrew V. Samoilov <sav@bcs.zp.ua>
  14    Chris Owen <chris@candu.co.uk>
  15    Claes Nästén <me@pekdon.net>
  16    Egmont Koblinger <egmont@gmail.com>
  17    Enrico Weigelt, metux IT service <weigelt@metux.de>
  18    Igor Urazov <z0rc3r@gmail.com>
  19    Ilia Maslakov <il.smind@gmail.com>
  20    Leonard den Ottolander <leonard@den.ottolander.nl>
  21    Miguel de Icaza <miguel@novell.com>
  22    Mikhail S. Pobolovets <styx.mp@gmail.com>
  23    Norbert Warmuth <nwarmuth@privat.circular.de>
  24    Patrick Winnertz <winnie@debian.org>
  25    Pavel Machek <pavel@suse.cz>
  26    Pavel Roskin <proski@gnu.org>
  27    Pavel Tsekov <ptsekov@gmx.net>
  28    Roland Illig <roland.illig@gmx.de>
  29    Sergei Trofimovich <slyfox@inbox.ru>
  30    Slava Zanko <slavazanko@gmail.com>, 2013,2015.
  31    Timur Bakeyev <mc@bat.ru>
  32    Vit Rosin <vit_r@list.ru>
  33 
  34    This file is part of the Midnight Commander.
  35 
  36    The Midnight Commander is free software: you can redistribute it
  37    and/or modify it under the terms of the GNU General Public License as
  38    published by the Free Software Foundation, either version 3 of the License,
  39    or (at your option) any later version.
  40 
  41    The Midnight Commander is distributed in the hope that it will be useful,
  42    but WITHOUT ANY WARRANTY; without even the implied warranty of
  43    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  44    GNU General Public License for more details.
  45 
  46    You should have received a copy of the GNU General Public License
  47    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  48  */
  49 
  50 /** \file subshell.c
  51  *  \brief Source: concurrent shell support
  52  */
  53 
  54 #include <config.h>
  55 
  56 #ifndef _GNU_SOURCE
  57 #define _GNU_SOURCE 1
  58 #endif
  59 
  60 #include <ctype.h>
  61 #include <stdio.h>
  62 #include <stdlib.h>
  63 #include <errno.h>
  64 #include <string.h>
  65 #include <signal.h>
  66 #ifdef HAVE_SYS_SELECT_H
  67 #include <sys/select.h>
  68 #else
  69 #include <sys/time.h>
  70 #include <unistd.h>
  71 #endif
  72 #include <sys/types.h>
  73 #include <sys/wait.h>
  74 #ifdef HAVE_SYS_IOCTL_H
  75 #include <sys/ioctl.h>
  76 #endif
  77 #include <termios.h>
  78 
  79 #ifdef HAVE_STROPTS_H
  80 #include <stropts.h>            /* For I_PUSH */
  81 #endif /* HAVE_STROPTS_H */
  82 
  83 #ifdef HAVE_OPENPTY
  84 /* includes for openpty() */
  85 #ifdef HAVE_PTY_H
  86 #include <pty.h>
  87 #endif
  88 #ifdef HAVE_UTIL_H
  89 #include <util.h>
  90 #endif
  91 /* <sys/types.h> is a prerequisite of <libutil.h> on FreeBSD 8.0.  */
  92 #ifdef HAVE_LIBUTIL_H
  93 #include <libutil.h>
  94 #endif
  95 #endif /* HAVE_OPENPTY */
  96 
  97 #include "lib/global.h"
  98 
  99 #include "lib/unixcompat.h"
 100 #include "lib/tty/tty.h"        /* LINES */
 101 #include "lib/tty/key.h"        /* XCTRL */
 102 #include "lib/vfs/vfs.h"
 103 #include "lib/strutil.h"
 104 #include "lib/mcconfig.h"
 105 #include "lib/util.h"
 106 #include "lib/widget.h"
 107 
 108 #include "subshell.h"
 109 #include "internal.h"
 110 
 111 /*** global variables ****************************************************************************/
 112 
 113 /* State of the subshell:
 114  * INACTIVE: the default state; awaiting a command
 115  * ACTIVE: remain in the shell until the user hits 'subshell_switch_key'
 116  * RUNNING_COMMAND: return to MC when the current command finishes */
 117 enum subshell_state_enum subshell_state;
 118 
 119 /* Holds the latest prompt captured from the subshell */
 120 GString *subshell_prompt = NULL;
 121 
 122 /* Subshell: if set, then the prompt was not saved on CONSOLE_SAVE */
 123 /* We need to paint it after CONSOLE_RESTORE, see: load_prompt */
 124 gboolean update_subshell_prompt = FALSE;
 125 
 126 /*** file scope macro definitions ****************************************************************/
 127 
 128 #ifndef WEXITSTATUS
 129 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
 130 #endif
 131 
 132 #ifndef WIFEXITED
 133 #define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
 134 #endif
 135 
 136 /* Initial length of the buffer for the subshell's prompt */
 137 #define INITIAL_PROMPT_SIZE 10
 138 
 139 /* Used by the child process to indicate failure to start the subshell */
 140 #define FORK_FAILURE 69         /* Arbitrary */
 141 
 142 /* Length of the buffer for all I/O with the subshell */
 143 #define PTY_BUFFER_SIZE BUF_SMALL       /* Arbitrary; but keep it >= 80 */
 144 
 145 /*** file scope type declarations ****************************************************************/
 146 
 147 /* For pipes */
 148 enum
 149 {
 150     READ = 0,
 151     WRITE = 1
 152 };
 153 
 154 /*** file scope variables ************************************************************************/
 155 
 156 /* tcsh closes all non-standard file descriptors, so we have to use a pipe */
 157 static char tcsh_fifo[128];
 158 
 159 static int subshell_pty_slave = -1;
 160 
 161 /* The key for switching back to MC from the subshell */
 162 /* *INDENT-OFF* */
 163 static const char subshell_switch_key = XCTRL ('o') & 255;
 164 /* *INDENT-ON* */
 165 
 166 /* For reading/writing on the subshell's pty */
 167 static char pty_buffer[PTY_BUFFER_SIZE] = "\0";
 168 
 169 /* To pass CWD info from the subshell to MC */
 170 static int subshell_pipe[2];
 171 
 172 /* The subshell's process ID */
 173 static pid_t subshell_pid = 1;
 174 
 175 /* One extra char for final '\n' */
 176 static char subshell_cwd[MC_MAXPATHLEN + 1];
 177 
 178 /* Flag to indicate whether the subshell is ready for next command */
 179 static int subshell_ready;
 180 
 181 /* The following two flags can be changed by the SIGCHLD handler. This is */
 182 /* OK, because the 'int' type is updated atomically on all known machines */
 183 static volatile int subshell_alive, subshell_stopped;
 184 
 185 /* We store the terminal's initial mode here so that we can configure
 186    the pty similarly, and also so we can restore the real terminal to
 187    sanity if we have to exit abruptly */
 188 static struct termios shell_mode;
 189 
 190 /* This is a transparent mode for the terminal where MC is running on */
 191 /* It is used when the shell is active, so that the control signals */
 192 /* are delivered to the shell pty */
 193 static struct termios raw_mode;
 194 
 195 /* --------------------------------------------------------------------------------------------- */
 196 /*** file scope functions ************************************************************************/
 197 /* --------------------------------------------------------------------------------------------- */
 198 /**
 199  *  Write all data, even if the write() call is interrupted.
 200  */
 201 
 202 static ssize_t
 203 write_all (int fd, const void *buf, size_t count)
     /* [previous][next][first][last][top][bottom][index][help]  */
 204 {
 205     ssize_t written = 0;
 206 
 207     while (count > 0)
 208     {
 209         ssize_t ret;
 210 
 211         ret = write (fd, (const unsigned char *) buf + written, count);
 212         if (ret < 0)
 213         {
 214             if (errno == EINTR)
 215             {
 216                 if (tty_got_winch ())
 217                     tty_change_screen_size ();
 218 
 219                 continue;
 220             }
 221 
 222             return written > 0 ? written : ret;
 223         }
 224         count -= ret;
 225         written += ret;
 226     }
 227     return written;
 228 }
 229 
 230 /* --------------------------------------------------------------------------------------------- */
 231 /**
 232  *  Prepare child process to running the shell and run it.
 233  *
 234  *  Modifies the global variables (in the child process only):
 235  *      shell_mode
 236  *
 237  *  Returns: never.
 238  */
 239 
 240 static void
 241 init_subshell_child (const char *pty_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 242 {
 243     char *init_file = NULL;
 244     char *putenv_str = NULL;
 245     pid_t mc_sid;
 246 
 247     (void) pty_name;
 248     setsid ();                  /* Get a fresh terminal session */
 249 
 250     /* Make sure that it has become our controlling terminal */
 251 
 252     /* Redundant on Linux and probably most systems, but just in case: */
 253 
 254 #ifdef TIOCSCTTY
 255     ioctl (subshell_pty_slave, TIOCSCTTY, 0);
 256 #endif
 257 
 258     /* Configure its terminal modes and window size */
 259 
 260     /* Set up the pty with the same termios flags as our own tty */
 261     if (tcsetattr (subshell_pty_slave, TCSANOW, &shell_mode))
 262     {
 263         fprintf (stderr, "Cannot set pty terminal modes: %s\r\n", unix_error_string (errno));
 264         my_exit (FORK_FAILURE);
 265     }
 266 
 267     /* Set the pty's size (80x25 by default on Linux) according to the */
 268     /* size of the real terminal as calculated by ncurses, if possible */
 269     tty_resize (subshell_pty_slave);
 270 
 271     /* Set up the subshell's environment and init file name */
 272 
 273     /* It simplifies things to change to our home directory here, */
 274     /* and the user's startup file may do a 'cd' command anyway   */
 275     {
 276         int ret;
 277 
 278         ret = chdir (mc_config_get_home_dir ());        /* FIXME? What about when we re-run the subshell? */
 279         (void) ret;
 280     }
 281 
 282     /* Set MC_SID to prevent running one mc from another */
 283     mc_sid = getsid (0);
 284     if (mc_sid != -1)
 285     {
 286         char sid_str[BUF_SMALL];
 287 
 288         g_snprintf (sid_str, sizeof (sid_str), "MC_SID=%ld", (long) mc_sid);
 289         putenv (g_strdup (sid_str));
 290     }
 291 
 292     switch (mc_global.shell->type)
 293     {
 294     case SHELL_BASH:
 295         /* Do we have a custom init file ~/.local/share/mc/bashrc? */
 296         init_file = mc_config_get_full_path ("bashrc");
 297 
 298         /* Otherwise use ~/.bashrc */
 299         if (!exist_file (init_file))
 300         {
 301             g_free (init_file);
 302             init_file = g_strdup (".bashrc");
 303         }
 304 
 305         /* Make MC's special commands not show up in bash's history and also suppress
 306          * consecutive identical commands*/
 307         putenv ((char *) "HISTCONTROL=ignoreboth");
 308 
 309         /* Allow alternative readline settings for MC */
 310         {
 311             char *input_file;
 312 
 313             input_file = mc_config_get_full_path ("inputrc");
 314             if (exist_file (input_file))
 315             {
 316                 putenv_str = g_strconcat ("INPUTRC=", input_file, (char *) NULL);
 317                 putenv (putenv_str);
 318             }
 319             g_free (input_file);
 320         }
 321 
 322         break;
 323 
 324     case SHELL_ASH_BUSYBOX:
 325     case SHELL_DASH:
 326         /* Do we have a custom init file ~/.local/share/mc/ashrc? */
 327         init_file = mc_config_get_full_path ("ashrc");
 328 
 329         /* Otherwise use ~/.profile */
 330         if (!exist_file (init_file))
 331         {
 332             g_free (init_file);
 333             init_file = g_strdup (".profile");
 334         }
 335 
 336         /* Put init file to ENV variable used by ash */
 337         putenv_str = g_strconcat ("ENV=", init_file, (char *) NULL);
 338         putenv (putenv_str);
 339         /* Do not use "g_free (putenv_str)" here, otherwise ENV will be undefined! */
 340 
 341         break;
 342 
 343         /* TODO: Find a way to pass initfile to TCSH, ZSH and FISH */
 344     case SHELL_TCSH:
 345     case SHELL_ZSH:
 346     case SHELL_FISH:
 347         break;
 348 
 349     default:
 350         fprintf (stderr, __FILE__ ": unimplemented subshell type %u\r\n", mc_global.shell->type);
 351         my_exit (FORK_FAILURE);
 352     }
 353 
 354     /* Attach all our standard file descriptors to the pty */
 355 
 356     /* This is done just before the fork, because stderr must still      */
 357     /* be connected to the real tty during the above error messages; */
 358     /* otherwise the user will never see them.                   */
 359 
 360     dup2 (subshell_pty_slave, STDIN_FILENO);
 361     dup2 (subshell_pty_slave, STDOUT_FILENO);
 362     dup2 (subshell_pty_slave, STDERR_FILENO);
 363 
 364     close (subshell_pipe[READ]);
 365     close (subshell_pty_slave); /* These may be FD_CLOEXEC, but just in case... */
 366     /* Close master side of pty.  This is important; apart from */
 367     /* freeing up the descriptor for use in the subshell, it also       */
 368     /* means that when MC exits, the subshell will get a SIGHUP and     */
 369     /* exit too, because there will be no more descriptors pointing     */
 370     /* at the master side of the pty and so it will disappear.  */
 371     close (mc_global.tty.subshell_pty);
 372 
 373     /* Execute the subshell at last */
 374 
 375     switch (mc_global.shell->type)
 376     {
 377     case SHELL_BASH:
 378         execl (mc_global.shell->path, "bash", "-rcfile", init_file, (char *) NULL);
 379         break;
 380 
 381     case SHELL_ZSH:
 382         /* Use -g to exclude cmds beginning with space from history
 383          * and -Z to use the line editor on non-interactive term */
 384         execl (mc_global.shell->path, "zsh", "-Z", "-g", (char *) NULL);
 385 
 386         break;
 387 
 388     case SHELL_ASH_BUSYBOX:
 389     case SHELL_DASH:
 390     case SHELL_TCSH:
 391     case SHELL_FISH:
 392         execl (mc_global.shell->path, mc_global.shell->path, (char *) NULL);
 393         break;
 394 
 395     default:
 396         break;
 397     }
 398 
 399     /* If we get this far, everything failed miserably */
 400     g_free (init_file);
 401     g_free (putenv_str);
 402     my_exit (FORK_FAILURE);
 403 }
 404 
 405 
 406 /* --------------------------------------------------------------------------------------------- */
 407 /**
 408  * Check MC_SID to prevent running one mc from another.
 409  * Return:
 410  * 0 if no parent mc in our session was found,
 411  * 1 if parent mc was found and the user wants to continue,
 412  * 2 if parent mc was found and the user wants to quit mc.
 413  */
 414 
 415 static int
 416 check_sid (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 417 {
 418     pid_t my_sid, old_sid;
 419     const char *sid_str;
 420     int r;
 421 
 422     sid_str = getenv ("MC_SID");
 423     if (sid_str == NULL)
 424         return 0;
 425 
 426     old_sid = (pid_t) strtol (sid_str, NULL, 0);
 427     if (old_sid == 0)
 428         return 0;
 429 
 430     my_sid = getsid (0);
 431     if (my_sid == -1)
 432         return 0;
 433 
 434     /* The parent mc is in a different session, it's OK */
 435     if (old_sid != my_sid)
 436         return 0;
 437 
 438     r = query_dialog (_("Warning"),
 439                       _("GNU Midnight Commander is already\n"
 440                         "running on this terminal.\n"
 441                         "Subshell support will be disabled."), D_ERROR, 2, _("&OK"), _("&Quit"));
 442 
 443     return (r != 0) ? 2 : 1;
 444 }
 445 
 446 /* --------------------------------------------------------------------------------------------- */
 447 
 448 static void
 449 init_raw_mode (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 450 {
 451     static gboolean initialized = FALSE;
 452 
 453     /* MC calls tty_reset_shell_mode() in pre_exec() to set the real tty to its */
 454     /* original settings.  However, here we need to make this tty very raw,     */
 455     /* so that all keyboard signals, XON/XOFF, etc. will get through to the     */
 456     /* pty.  So, instead of changing the code for execute(), pre_exec(),        */
 457     /* etc, we just set up the modes we need here, before each command.         */
 458 
 459     if (!initialized)           /* First time: initialise 'raw_mode' */
 460     {
 461         tcgetattr (STDOUT_FILENO, &raw_mode);
 462         raw_mode.c_lflag &= ~ICANON;    /* Disable line-editing chars, etc.   */
 463         raw_mode.c_lflag &= ~ISIG;      /* Disable intr, quit & suspend chars */
 464         raw_mode.c_lflag &= ~ECHO;      /* Disable input echoing              */
 465         raw_mode.c_iflag &= ~IXON;      /* Pass ^S/^Q to subshell undisturbed */
 466         raw_mode.c_iflag &= ~ICRNL;     /* Don't translate CRs into LFs       */
 467         raw_mode.c_oflag &= ~OPOST;     /* Don't postprocess output           */
 468         raw_mode.c_cc[VTIME] = 0;       /* IE: wait forever, and return as    */
 469         raw_mode.c_cc[VMIN] = 1;        /* soon as a character is available   */
 470         initialized = TRUE;
 471     }
 472 }
 473 
 474 /* --------------------------------------------------------------------------------------------- */
 475 /**
 476  * Wait until the subshell dies or stops.  If it stops, make it resume.
 477  * Possibly modifies the globals 'subshell_alive' and 'subshell_stopped'
 478  */
 479 
 480 static void
 481 synchronize (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 482 {
 483     sigset_t sigchld_mask, old_mask;
 484 
 485     sigemptyset (&sigchld_mask);
 486     sigaddset (&sigchld_mask, SIGCHLD);
 487     sigprocmask (SIG_BLOCK, &sigchld_mask, &old_mask);
 488 
 489     /*
 490      * SIGCHLD should not be blocked, but we unblock it just in case.
 491      * This is known to be useful for cygwin 1.3.12 and older.
 492      */
 493     sigdelset (&old_mask, SIGCHLD);
 494 
 495     /* Wait until the subshell has stopped */
 496     while (subshell_alive && !subshell_stopped)
 497         sigsuspend (&old_mask);
 498 
 499     if (subshell_state != ACTIVE)
 500     {
 501         /* Discard all remaining data from stdin to the subshell */
 502         tcflush (subshell_pty_slave, TCIFLUSH);
 503     }
 504 
 505     subshell_stopped = FALSE;
 506     kill (subshell_pid, SIGCONT);
 507 
 508     sigprocmask (SIG_SETMASK, &old_mask, NULL);
 509     /* We can't do any better without modifying the shell(s) */
 510 }
 511 
 512 /* --------------------------------------------------------------------------------------------- */
 513 /** Feed the subshell our keyboard input until it says it's finished */
 514 
 515 static gboolean
 516 feed_subshell (int how, gboolean fail_on_error)
     /* [previous][next][first][last][top][bottom][index][help]  */
 517 {
 518     fd_set read_set;            /* For 'select' */
 519     int bytes;                  /* For the return value from 'read' */
 520     int i;                      /* Loop counter */
 521 
 522     struct timeval wtime;       /* Maximum time we wait for the subshell */
 523     struct timeval *wptr;
 524 
 525     /* we wait up to 10 seconds if fail_on_error, forever otherwise */
 526     wtime.tv_sec = 10;
 527     wtime.tv_usec = 0;
 528     wptr = fail_on_error ? &wtime : NULL;
 529 
 530     while (TRUE)
 531     {
 532         int maxfdp;
 533 
 534         if (!subshell_alive)
 535             return FALSE;
 536 
 537         /* Prepare the file-descriptor set and call 'select' */
 538 
 539         FD_ZERO (&read_set);
 540         FD_SET (mc_global.tty.subshell_pty, &read_set);
 541         FD_SET (subshell_pipe[READ], &read_set);
 542         maxfdp = MAX (mc_global.tty.subshell_pty, subshell_pipe[READ]);
 543         if (how == VISIBLY)
 544         {
 545             FD_SET (STDIN_FILENO, &read_set);
 546             maxfdp = MAX (maxfdp, STDIN_FILENO);
 547         }
 548 
 549         if (select (maxfdp + 1, &read_set, NULL, NULL, wptr) == -1)
 550         {
 551             /* Despite using SA_RESTART, we still have to check for this */
 552             if (errno == EINTR)
 553             {
 554                 if (tty_got_winch ())
 555                     tty_change_screen_size ();
 556 
 557                 continue;       /* try all over again */
 558             }
 559             tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
 560             fprintf (stderr, "select (FD_SETSIZE, &read_set...): %s\r\n",
 561                      unix_error_string (errno));
 562             exit (EXIT_FAILURE);
 563         }
 564 
 565         if (FD_ISSET (mc_global.tty.subshell_pty, &read_set))
 566             /* Read from the subshell, write to stdout */
 567 
 568             /* This loop improves performance by reducing context switches
 569                by a factor of 20 or so... unfortunately, it also hangs MC
 570                randomly, because of an apparent Linux bug.  Investigate. */
 571             /* for (i=0; i<5; ++i)  * FIXME -- experimental */
 572         {
 573             bytes = read (mc_global.tty.subshell_pty, pty_buffer, sizeof (pty_buffer));
 574 
 575             /* The subshell has died */
 576             if (bytes == -1 && errno == EIO && !subshell_alive)
 577                 return FALSE;
 578 
 579             if (bytes <= 0)
 580             {
 581 #ifdef PTY_ZEROREAD
 582                 /* On IBM i, read(1) can return 0 for a non-closed fd */
 583                 continue;
 584 #else
 585                 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
 586                 fprintf (stderr, "read (subshell_pty...): %s\r\n", unix_error_string (errno));
 587                 exit (EXIT_FAILURE);
 588 #endif
 589             }
 590 
 591             if (how == VISIBLY)
 592                 write_all (STDOUT_FILENO, pty_buffer, bytes);
 593         }
 594 
 595         else if (FD_ISSET (subshell_pipe[READ], &read_set))
 596             /* Read the subshell's CWD and capture its prompt */
 597         {
 598             bytes = read (subshell_pipe[READ], subshell_cwd, sizeof (subshell_cwd));
 599             if (bytes <= 0)
 600             {
 601                 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
 602                 fprintf (stderr, "read (subshell_pipe[READ]...): %s\r\n",
 603                          unix_error_string (errno));
 604                 exit (EXIT_FAILURE);
 605             }
 606 
 607             subshell_cwd[bytes - 1] = 0;        /* Squash the final '\n' */
 608 
 609             synchronize ();
 610 
 611             subshell_ready = TRUE;
 612             if (subshell_state == RUNNING_COMMAND)
 613             {
 614                 subshell_state = INACTIVE;
 615                 return TRUE;
 616             }
 617         }
 618 
 619         else if (FD_ISSET (STDIN_FILENO, &read_set))
 620             /* Read from stdin, write to the subshell */
 621         {
 622             bytes = read (STDIN_FILENO, pty_buffer, sizeof (pty_buffer));
 623             if (bytes <= 0)
 624             {
 625                 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
 626                 fprintf (stderr,
 627                          "read (STDIN_FILENO, pty_buffer...): %s\r\n", unix_error_string (errno));
 628                 exit (EXIT_FAILURE);
 629             }
 630 
 631             for (i = 0; i < bytes; ++i)
 632                 if (pty_buffer[i] == subshell_switch_key)
 633                 {
 634                     write_all (mc_global.tty.subshell_pty, pty_buffer, i);
 635                     if (subshell_ready)
 636                         subshell_state = INACTIVE;
 637                     return TRUE;
 638                 }
 639 
 640             write_all (mc_global.tty.subshell_pty, pty_buffer, bytes);
 641 
 642             if (pty_buffer[bytes - 1] == '\n' || pty_buffer[bytes - 1] == '\r')
 643                 subshell_ready = FALSE;
 644         }
 645         else
 646             return FALSE;
 647     }
 648 }
 649 
 650 /* --------------------------------------------------------------------------------------------- */
 651 /* pty opening functions */
 652 
 653 #ifndef HAVE_OPENPTY
 654 
 655 #ifdef HAVE_GRANTPT
 656 
 657 /* System V version of pty_open_master */
 658 
 659 static int
 660 pty_open_master (char *pty_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 661 {
 662     char *slave_name;
 663     int pty_master;
 664 
 665 #ifdef HAVE_POSIX_OPENPT
 666     pty_master = posix_openpt (O_RDWR);
 667 #elif defined HAVE_GETPT
 668     /* getpt () is a GNU extension (glibc 2.1.x) */
 669     pty_master = getpt ();
 670 #elif defined IS_AIX
 671     strcpy (pty_name, "/dev/ptc");
 672     pty_master = open (pty_name, O_RDWR);
 673 #else
 674     strcpy (pty_name, "/dev/ptmx");
 675     pty_master = open (pty_name, O_RDWR);
 676 #endif
 677 
 678     if (pty_master == -1)
 679         return -1;
 680 
 681     if (grantpt (pty_master) == -1      /* Grant access to slave */
 682         || unlockpt (pty_master) == -1  /* Clear slave's lock flag */
 683         || !(slave_name = ptsname (pty_master)))        /* Get slave's name */
 684     {
 685         close (pty_master);
 686         return -1;
 687     }
 688     strcpy (pty_name, slave_name);
 689     return pty_master;
 690 }
 691 
 692 /* --------------------------------------------------------------------------------------------- */
 693 /** System V version of pty_open_slave */
 694 
 695 static int
 696 pty_open_slave (const char *pty_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 697 {
 698     int pty_slave;
 699 
 700     pty_slave = open (pty_name, O_RDWR);
 701     if (pty_slave == -1)
 702     {
 703         fprintf (stderr, "open (%s, O_RDWR): %s\r\n", pty_name, unix_error_string (errno));
 704         return -1;
 705     }
 706 #if !defined(__osf__) && !defined(__linux__)
 707 #if defined (I_FIND) && defined (I_PUSH)
 708     if (ioctl (pty_slave, I_FIND, "ptem") == 0)
 709         if (ioctl (pty_slave, I_PUSH, "ptem") == -1)
 710         {
 711             fprintf (stderr, "ioctl (%d, I_PUSH, \"ptem\") failed: %s\r\n",
 712                      pty_slave, unix_error_string (errno));
 713             close (pty_slave);
 714             return -1;
 715         }
 716 
 717     if (ioctl (pty_slave, I_FIND, "ldterm") == 0)
 718         if (ioctl (pty_slave, I_PUSH, "ldterm") == -1)
 719         {
 720             fprintf (stderr,
 721                      "ioctl (%d, I_PUSH, \"ldterm\") failed: %s\r\n",
 722                      pty_slave, unix_error_string (errno));
 723             close (pty_slave);
 724             return -1;
 725         }
 726 #if !defined(sgi) && !defined(__sgi)
 727     if (ioctl (pty_slave, I_FIND, "ttcompat") == 0)
 728         if (ioctl (pty_slave, I_PUSH, "ttcompat") == -1)
 729         {
 730             fprintf (stderr,
 731                      "ioctl (%d, I_PUSH, \"ttcompat\") failed: %s\r\n",
 732                      pty_slave, unix_error_string (errno));
 733             close (pty_slave);
 734             return -1;
 735         }
 736 #endif /* sgi || __sgi */
 737 #endif /* I_FIND && I_PUSH */
 738 #endif /* __osf__ || __linux__ */
 739 
 740     fcntl (pty_slave, F_SETFD, FD_CLOEXEC);
 741     return pty_slave;
 742 }
 743 
 744 #else /* !HAVE_GRANTPT */
 745 
 746 /* --------------------------------------------------------------------------------------------- */
 747 /** BSD version of pty_open_master */
 748 static int
 749 pty_open_master (char *pty_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 750 {
 751     int pty_master;
 752     const char *ptr1, *ptr2;
 753 
 754     strcpy (pty_name, "/dev/ptyXX");
 755     for (ptr1 = "pqrstuvwxyzPQRST"; *ptr1; ++ptr1)
 756     {
 757         pty_name[8] = *ptr1;
 758         for (ptr2 = "0123456789abcdef"; *ptr2 != '\0'; ++ptr2)
 759         {
 760             pty_name[9] = *ptr2;
 761 
 762             /* Try to open master */
 763             pty_master = open (pty_name, O_RDWR);
 764             if (pty_master == -1)
 765             {
 766                 if (errno == ENOENT)    /* Different from EIO */
 767                     return -1;  /* Out of pty devices */
 768                 continue;       /* Try next pty device */
 769             }
 770             pty_name[5] = 't';  /* Change "pty" to "tty" */
 771             if (access (pty_name, 6) != 0)
 772             {
 773                 close (pty_master);
 774                 pty_name[5] = 'p';
 775                 continue;
 776             }
 777             return pty_master;
 778         }
 779     }
 780     return -1;                  /* Ran out of pty devices */
 781 }
 782 
 783 /* --------------------------------------------------------------------------------------------- */
 784 /** BSD version of pty_open_slave */
 785 
 786 static int
 787 pty_open_slave (const char *pty_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 788 {
 789     int pty_slave;
 790     struct group *group_info;
 791 
 792     group_info = getgrnam ("tty");
 793     if (group_info != NULL)
 794     {
 795         /* The following two calls will only succeed if we are root */
 796         /* [Commented out while permissions problem is investigated] */
 797         /* chown (pty_name, getuid (), group_info->gr_gid);  FIXME */
 798         /* chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP);   FIXME */
 799     }
 800     pty_slave = open (pty_name, O_RDWR);
 801     if (pty_slave == -1)
 802         fprintf (stderr, "open (pty_name, O_RDWR): %s\r\n", pty_name);
 803     fcntl (pty_slave, F_SETFD, FD_CLOEXEC);
 804     return pty_slave;
 805 }
 806 #endif /* !HAVE_GRANTPT */
 807 
 808 #endif /* !HAVE_OPENPTY */
 809 
 810 /* --------------------------------------------------------------------------------------------- */
 811 /**
 812  * Set up `precmd' or equivalent for reading the subshell's CWD.
 813  *
 814  * Attention! Never forget that these are *one-liners* even though the concatenated
 815  * substrings contain line breaks and indentation for better understanding of the
 816  * shell code. It is vital that each one-liner ends with a line feed character ("\n" ).
 817  *
 818  * @return initialized pre-command string
 819  */
 820 
 821 static void
 822 init_subshell_precmd (char *precmd, size_t buff_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 823 {
 824     switch (mc_global.shell->type)
 825     {
 826     case SHELL_BASH:
 827         g_snprintf (precmd, buff_size,
 828                     " PROMPT_COMMAND=${PROMPT_COMMAND:+$PROMPT_COMMAND\n}'pwd>&%d;kill -STOP $$'\n"
 829                     "PS1='\\u@\\h:\\w\\$ '\n", subshell_pipe[WRITE]);
 830         break;
 831 
 832     case SHELL_ASH_BUSYBOX:
 833         /* BusyBox ash needs a somewhat complicated precmd emulation via PS1, and it is vital
 834          * that BB be built with active CONFIG_ASH_EXPAND_PRMT, but this is the default anyway.
 835          *
 836          * A: This leads to a stopped subshell (=frozen mc) if user calls "ash" command
 837          *    "PS1='$(pwd>&%d; kill -STOP $$)\\u@\\h:\\w\\$ '\n",
 838          *
 839          * B: This leads to "sh: precmd: not found" in sub-subshell if user calls "ash" command
 840          *    "precmd() { pwd>&%d; kill -STOP $$; }; "
 841          *    "PS1='$(precmd)\\u@\\h:\\w\\$ '\n",
 842          *
 843          * C: This works if user calls "ash" command because in sub-subshell
 844          *    PRECMD is unfedined, thus evaluated to empty string - no damage done.
 845          *    Attention: BusyBox must be built with FEATURE_EDITING_FANCY_PROMPT to
 846          *    permit \u, \w, \h, \$ escape sequences. Unfortunately this cannot be guaranteed,
 847          *    especially on embedded systems where people try to save space, so let's use
 848          *    the dash version below. It should work on virtually all systems.
 849          *    "precmd() { pwd>&%d; kill -STOP $$; }; "
 850          *    "PRECMD=precmd; "
 851          *    "PS1='$(eval $PRECMD)\\u@\\h:\\w\\$ '\n",
 852          */
 853     case SHELL_DASH:
 854         /* Debian ash needs a precmd emulation via PS1, similar to BusyBox ash,
 855          * but does not support escape sequences for user, host and cwd in prompt.
 856          * Attention! Make sure that the buffer for precmd is big enough.
 857          *
 858          * We want to have a fancy dynamic prompt with user@host:cwd just like in the BusyBox
 859          * examples above, but because replacing the home directory part of the path by "~" is
 860          * complicated, it bloats the precmd to a size > BUF_SMALL (128).
 861          *
 862          * The following example is a little less fancy (home directory not replaced)
 863          * and shows the basic workings of our prompt for easier understanding:
 864          *
 865          * "precmd() { "
 866          *     "echo \"$USER@$(hostname -s):$PWD\"; "
 867          *     "pwd>&%d; "
 868          *     "kill -STOP $$; "
 869          * "}; "
 870          * "PRECMD=precmd; "
 871          * "PS1='$($PRECMD)$ '\n",
 872          */
 873         g_snprintf (precmd, buff_size,
 874                     "precmd() { "
 875                     "if [ ! \"${PWD##$HOME}\" ]; then "
 876                     "MC_PWD=\"~\"; "
 877                     "else "
 878                     "[ \"${PWD##$HOME/}\" = \"$PWD\" ] && MC_PWD=\"$PWD\" || MC_PWD=\"~/${PWD##$HOME/}\"; "
 879                     "fi; "
 880                     "echo \"$USER@$(hostname -s):$MC_PWD\"; "
 881                     "pwd>&%d; "
 882                     "kill -STOP $$; "
 883                     "}; " "PRECMD=precmd; " "PS1='$($PRECMD)$ '\n", subshell_pipe[WRITE]);
 884         break;
 885 
 886     case SHELL_ZSH:
 887         g_snprintf (precmd, buff_size,
 888                     " _mc_precmd(){ pwd>&%d;kill -STOP $$ }; precmd_functions+=(_mc_precmd)\n"
 889                     "PS1='%%n@%%m:%%~%%# '\n", subshell_pipe[WRITE]);
 890         break;
 891 
 892     case SHELL_TCSH:
 893         g_snprintf (precmd, buff_size,
 894                     "set echo_style=both; "
 895                     "set prompt='%%n@%%m:%%~%%# '; "
 896                     "alias precmd 'echo $cwd:q >>%s; kill -STOP $$'\n", tcsh_fifo);
 897         break;
 898 
 899     case SHELL_FISH:
 900         g_snprintf (precmd, buff_size,
 901                     " if not functions -q fish_prompt_mc;"
 902                     "functions -e fish_right_prompt;"
 903                     "functions -c fish_prompt fish_prompt_mc; end;"
 904                     "function fish_prompt;"
 905                     "echo \"$PWD\">&%d; fish_prompt_mc; kill -STOP %%self; end\n",
 906                     subshell_pipe[WRITE]);
 907         break;
 908 
 909     default:
 910         break;
 911     }
 912 }
 913 
 914 /* --------------------------------------------------------------------------------------------- */
 915 /**
 916  * Carefully quote directory name to allow entering any directory safely,
 917  * no matter what weird characters it may contain in its name.
 918  * NOTE: Treat directory name an untrusted data, don't allow it to cause
 919  * executing any commands in the shell.  Escape all control characters.
 920  * Use following technique:
 921  *
 922  * printf(1) with format string containing a single conversion specifier,
 923  * "b", and an argument which contains a copy of the string passed to 
 924  * subshell_name_quote() with all characters, except digits and letters,
 925  * replaced by the backslash-escape sequence \0nnn, where "nnn" is the
 926  * numeric value of the character converted to octal number.
 927  * 
 928  *   cd "`printf '%b' 'ABC\0nnnDEF\0nnnXYZ'`"
 929  *
 930  * N.B.: Use single quotes for conversion specifier to work around
 931  *       tcsh 6.20+ parser breakage, see ticket #3852 for the details.
 932  */
 933 
 934 static GString *
 935 subshell_name_quote (const char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 936 {
 937     GString *ret;
 938     const char *su, *n;
 939     const char *quote_cmd_start, *quote_cmd_end;
 940 
 941     if (mc_global.shell->type == SHELL_FISH)
 942     {
 943         quote_cmd_start = "(printf '%b' '";
 944         quote_cmd_end = "')";
 945     }
 946     /* TODO: When BusyBox printf is fixed, get rid of this "else if", see
 947        http://lists.busybox.net/pipermail/busybox/2012-March/077460.html */
 948     /* else if (subshell_type == ASH_BUSYBOX)
 949        {
 950        quote_cmd_start = "\"`echo -en '";
 951        quote_cmd_end = "'`\"";
 952        } */
 953     else
 954     {
 955         quote_cmd_start = "\"`printf '%b' '";
 956         quote_cmd_end = "'`\"";
 957     }
 958 
 959     ret = g_string_sized_new (64);
 960 
 961     /* Prevent interpreting leading '-' as a switch for 'cd' */
 962     if (s[0] == '-')
 963         g_string_append (ret, "./");
 964 
 965     /* Copy the beginning of the command to the buffer */
 966     g_string_append (ret, quote_cmd_start);
 967 
 968     /*
 969      * Print every character except digits and letters as a backslash-escape
 970      * sequence of the form \0nnn, where "nnn" is the numeric value of the
 971      * character converted to octal number.
 972      */
 973     for (su = s; su[0] != '\0'; su = n)
 974     {
 975         n = str_cget_next_char_safe (su);
 976 
 977         if (str_isalnum (su))
 978             g_string_append_len (ret, su, n - su);
 979         else
 980         {
 981             int c;
 982 
 983             for (c = 0; c < n - su; c++)
 984                 g_string_append_printf (ret, "\\0%03o", (unsigned char) su[c]);
 985         }
 986     }
 987 
 988     g_string_append (ret, quote_cmd_end);
 989 
 990     return ret;
 991 }
 992 
 993 /* --------------------------------------------------------------------------------------------- */
 994 /*** public functions ****************************************************************************/
 995 /* --------------------------------------------------------------------------------------------- */
 996 
 997 /* --------------------------------------------------------------------------------------------- */
 998 /**
 999  *  Fork the subshell, and set up many, many things.
1000  *
1001  *  Possibly modifies the global variables:
1002  *      subshell_type, subshell_alive, subshell_stopped, subshell_pid
1003  *      mc_global.tty.use_subshell - Is set to FALSE if we can't run the subshell
1004  *      quit - Can be set to SUBSHELL_EXIT by the SIGCHLD handler
1005  */
1006 
1007 void
1008 init_subshell (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1009 {
1010     /* This must be remembered across calls to init_subshell() */
1011     static char pty_name[BUF_SMALL];
1012     /* Must be considerably longer than BUF_SMALL (128) to support fancy shell prompts */
1013     char precmd[BUF_MEDIUM];
1014 
1015     switch (check_sid ())
1016     {
1017     case 1:
1018         mc_global.tty.use_subshell = FALSE;
1019         return;
1020     case 2:
1021         mc_global.tty.use_subshell = FALSE;
1022         mc_global.midnight_shutdown = TRUE;
1023         return;
1024     default:
1025         break;
1026     }
1027 
1028     /* Take the current (hopefully pristine) tty mode and make */
1029     /* a raw mode based on it now, before we do anything else with it */
1030     init_raw_mode ();
1031 
1032     if (mc_global.tty.subshell_pty == 0)
1033     {                           /* First time through */
1034         if (mc_global.shell->type == SHELL_NONE)
1035             return;
1036 
1037         /* Open a pty for talking to the subshell */
1038 
1039         /* FIXME: We may need to open a fresh pty each time on SVR4 */
1040 
1041 #ifdef HAVE_OPENPTY
1042         if (openpty (&mc_global.tty.subshell_pty, &subshell_pty_slave, NULL, NULL, NULL))
1043         {
1044             fprintf (stderr, "Cannot open master and slave sides of pty: %s\n",
1045                      unix_error_string (errno));
1046             mc_global.tty.use_subshell = FALSE;
1047             return;
1048         }
1049 #else
1050         mc_global.tty.subshell_pty = pty_open_master (pty_name);
1051         if (mc_global.tty.subshell_pty == -1)
1052         {
1053             fprintf (stderr, "Cannot open master side of pty: %s\r\n", unix_error_string (errno));
1054             mc_global.tty.use_subshell = FALSE;
1055             return;
1056         }
1057         subshell_pty_slave = pty_open_slave (pty_name);
1058         if (subshell_pty_slave == -1)
1059         {
1060             fprintf (stderr, "Cannot open slave side of pty %s: %s\r\n",
1061                      pty_name, unix_error_string (errno));
1062             mc_global.tty.use_subshell = FALSE;
1063             return;
1064         }
1065 #endif /* HAVE_OPENPTY */
1066 
1067         /* Create a pipe for receiving the subshell's CWD */
1068 
1069         if (mc_global.shell->type == SHELL_TCSH)
1070         {
1071             g_snprintf (tcsh_fifo, sizeof (tcsh_fifo), "%s/mc.pipe.%d",
1072                         mc_tmpdir (), (int) getpid ());
1073             if (mkfifo (tcsh_fifo, 0600) == -1)
1074             {
1075                 fprintf (stderr, "mkfifo(%s) failed: %s\r\n", tcsh_fifo, unix_error_string (errno));
1076                 mc_global.tty.use_subshell = FALSE;
1077                 return;
1078             }
1079 
1080             /* Opening the FIFO as O_RDONLY or O_WRONLY causes deadlock */
1081 
1082             if ((subshell_pipe[READ] = open (tcsh_fifo, O_RDWR)) == -1
1083                 || (subshell_pipe[WRITE] = open (tcsh_fifo, O_RDWR)) == -1)
1084             {
1085                 fprintf (stderr, _("Cannot open named pipe %s\n"), tcsh_fifo);
1086                 perror (__FILE__ ": open");
1087                 mc_global.tty.use_subshell = FALSE;
1088                 return;
1089             }
1090         }
1091         else if (pipe (subshell_pipe))  /* subshell_type is BASH, ASH_BUSYBOX, DASH or ZSH */
1092         {
1093             perror (__FILE__ ": couldn't create pipe");
1094             mc_global.tty.use_subshell = FALSE;
1095             return;
1096         }
1097     }
1098 
1099     /* Fork the subshell */
1100 
1101     subshell_alive = TRUE;
1102     subshell_stopped = FALSE;
1103     subshell_pid = fork ();
1104 
1105     if (subshell_pid == -1)
1106     {
1107         fprintf (stderr, "Cannot spawn the subshell process: %s\r\n", unix_error_string (errno));
1108         /* We exit here because, if the process table is full, the */
1109         /* other method of running user commands won't work either */
1110         exit (EXIT_FAILURE);
1111     }
1112 
1113     if (subshell_pid == 0)
1114     {
1115         /* We are in the child process */
1116         init_subshell_child (pty_name);
1117     }
1118 
1119     init_subshell_precmd (precmd, BUF_MEDIUM);
1120 
1121     write_all (mc_global.tty.subshell_pty, precmd, strlen (precmd));
1122 
1123     /* Wait until the subshell has started up and processed the command */
1124 
1125     subshell_state = RUNNING_COMMAND;
1126     tty_enable_interrupt_key ();
1127     if (!feed_subshell (QUIETLY, TRUE))
1128     {
1129         mc_global.tty.use_subshell = FALSE;
1130     }
1131     tty_disable_interrupt_key ();
1132     if (!subshell_alive)
1133         mc_global.tty.use_subshell = FALSE;     /* Subshell died instantly, so don't use it */
1134 }
1135 
1136 /* --------------------------------------------------------------------------------------------- */
1137 
1138 int
1139 invoke_subshell (const char *command, int how, vfs_path_t ** new_dir_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1140 {
1141     /* Make the MC terminal transparent */
1142     tcsetattr (STDOUT_FILENO, TCSANOW, &raw_mode);
1143 
1144     /* Make the subshell change to MC's working directory */
1145     if (new_dir_vpath != NULL)
1146         do_subshell_chdir (subshell_get_cwd (), TRUE);
1147 
1148     if (command == NULL)        /* The user has done "C-o" from MC */
1149     {
1150         if (subshell_state == INACTIVE)
1151         {
1152             subshell_state = ACTIVE;
1153 
1154             /* FIXME: possibly take out this hack; the user can re-play it by hitting C-hyphen a few times! */
1155             if (subshell_ready && mc_global.mc_run_mode == MC_RUN_FULL)
1156                 write_all (mc_global.tty.subshell_pty, " \b", 2);       /* Hack to make prompt reappear */
1157         }
1158     }
1159     else                        /* MC has passed us a user command */
1160     {
1161         if (how == QUIETLY)
1162             write_all (mc_global.tty.subshell_pty, " ", 1);
1163         /* FIXME: if command is long (>8KB ?) we go comma */
1164         write_all (mc_global.tty.subshell_pty, command, strlen (command));
1165         write_all (mc_global.tty.subshell_pty, "\n", 1);
1166         subshell_state = RUNNING_COMMAND;
1167         subshell_ready = FALSE;
1168     }
1169 
1170     feed_subshell (how, FALSE);
1171 
1172     if (new_dir_vpath != NULL && subshell_alive)
1173     {
1174         const char *pcwd;
1175 
1176         pcwd = vfs_translate_path (vfs_path_as_str (subshell_get_cwd ()));
1177         if (strcmp (subshell_cwd, pcwd) != 0)
1178             *new_dir_vpath = vfs_path_from_str (subshell_cwd);  /* Make MC change to the subshell's CWD */
1179     }
1180 
1181     /* Restart the subshell if it has died by SIGHUP, SIGQUIT, etc. */
1182     while (!subshell_alive && subshell_get_mainloop_quit () == 0 && mc_global.tty.use_subshell)
1183         init_subshell ();
1184 
1185     return subshell_get_mainloop_quit ();
1186 }
1187 
1188 
1189 /* --------------------------------------------------------------------------------------------- */
1190 
1191 gboolean
1192 read_subshell_prompt (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1193 {
1194     int rc = 0;
1195     ssize_t bytes = 0;
1196     struct timeval timeleft = { 0, 0 };
1197     GString *p;
1198     gboolean prompt_was_reset = FALSE;
1199 
1200     fd_set tmp;
1201     FD_ZERO (&tmp);
1202     FD_SET (mc_global.tty.subshell_pty, &tmp);
1203 
1204     /* First time through */
1205     if (subshell_prompt == NULL)
1206         subshell_prompt = g_string_sized_new (INITIAL_PROMPT_SIZE);
1207 
1208     p = g_string_sized_new (INITIAL_PROMPT_SIZE);
1209 
1210     while (subshell_alive
1211            && (rc = select (mc_global.tty.subshell_pty + 1, &tmp, NULL, NULL, &timeleft)) != 0)
1212     {
1213         ssize_t i;
1214 
1215         /* Check for 'select' errors */
1216         if (rc == -1)
1217         {
1218             if (errno == EINTR)
1219             {
1220                 if (tty_got_winch ())
1221                     tty_change_screen_size ();
1222 
1223                 continue;
1224             }
1225 
1226             fprintf (stderr, "select (FD_SETSIZE, &tmp...): %s\r\n", unix_error_string (errno));
1227             exit (EXIT_FAILURE);
1228         }
1229 
1230         bytes = read (mc_global.tty.subshell_pty, pty_buffer, sizeof (pty_buffer));
1231 
1232         /* Extract the prompt from the shell output */
1233         for (i = 0; i < bytes; i++)
1234             if (pty_buffer[i] == '\n' || pty_buffer[i] == '\r')
1235             {
1236                 g_string_set_size (p, 0);
1237                 prompt_was_reset = TRUE;
1238             }
1239             else if (pty_buffer[i] != '\0')
1240                 g_string_append_c (p, pty_buffer[i]);
1241     }
1242 
1243     if (p->len != 0 || prompt_was_reset)
1244         g_string_assign (subshell_prompt, p->str);
1245 
1246     g_string_free (p, TRUE);
1247 
1248     return (rc != 0 || bytes != 0);
1249 }
1250 
1251 /* --------------------------------------------------------------------------------------------- */
1252 
1253 void
1254 do_update_prompt (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1255 {
1256     if (update_subshell_prompt)
1257     {
1258         printf ("\r\n%s", subshell_prompt->str);
1259         fflush (stdout);
1260         update_subshell_prompt = FALSE;
1261     }
1262 }
1263 
1264 /* --------------------------------------------------------------------------------------------- */
1265 
1266 gboolean
1267 exit_subshell (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1268 {
1269     gboolean subshell_quit = TRUE;
1270 
1271     if (subshell_state != INACTIVE && subshell_alive)
1272         subshell_quit =
1273             query_dialog (_("Warning"),
1274                           _("The shell is still active. Quit anyway?"),
1275                           D_NORMAL, 2, _("&Yes"), _("&No")) == 0;
1276 
1277     if (subshell_quit)
1278     {
1279         if (mc_global.shell->type == SHELL_TCSH)
1280         {
1281             if (unlink (tcsh_fifo) == -1)
1282                 fprintf (stderr, "Cannot remove named pipe %s: %s\r\n",
1283                          tcsh_fifo, unix_error_string (errno));
1284         }
1285 
1286         g_string_free (subshell_prompt, TRUE);
1287         subshell_prompt = NULL;
1288         pty_buffer[0] = '\0';
1289     }
1290 
1291     return subshell_quit;
1292 }
1293 
1294 /* --------------------------------------------------------------------------------------------- */
1295 
1296 /** If it actually changed the directory it returns true */
1297 void
1298 do_subshell_chdir (const vfs_path_t * vpath, gboolean update_prompt)
     /* [previous][next][first][last][top][bottom][index][help]  */
1299 {
1300     char *pcwd;
1301 
1302     pcwd = vfs_path_to_str_flags (subshell_get_cwd (), 0, VPF_RECODE);
1303 
1304     if (!(subshell_state == INACTIVE && strcmp (subshell_cwd, pcwd) != 0))
1305     {
1306         /* We have to repaint the subshell prompt if we read it from
1307          * the main program.  Please note that in the code after this
1308          * if, the cd command that is sent will make the subshell
1309          * repaint the prompt, so we don't have to paint it. */
1310         if (update_prompt)
1311             do_update_prompt ();
1312         g_free (pcwd);
1313         return;
1314     }
1315 
1316     /* The initial space keeps this out of the command history (in bash
1317        because we set "HISTCONTROL=ignorespace") */
1318     write_all (mc_global.tty.subshell_pty, " cd ", 4);
1319 
1320     if (vpath != NULL)
1321     {
1322         const char *translate;
1323 
1324         translate = vfs_translate_path (vfs_path_as_str (vpath));
1325         if (translate != NULL)
1326         {
1327             GString *temp;
1328 
1329             temp = subshell_name_quote (translate);
1330             write_all (mc_global.tty.subshell_pty, temp->str, temp->len);
1331             g_string_free (temp, TRUE);
1332         }
1333         else
1334         {
1335             write_all (mc_global.tty.subshell_pty, ".", 1);
1336         }
1337     }
1338     else
1339     {
1340         write_all (mc_global.tty.subshell_pty, "/", 1);
1341     }
1342     write_all (mc_global.tty.subshell_pty, "\n", 1);
1343 
1344     subshell_state = RUNNING_COMMAND;
1345     feed_subshell (QUIETLY, FALSE);
1346 
1347     if (subshell_alive)
1348     {
1349         gboolean bPathNotEq;
1350 
1351         bPathNotEq = strcmp (subshell_cwd, pcwd) != 0;
1352 
1353         if (bPathNotEq && mc_global.shell->type == SHELL_TCSH)
1354         {
1355             char rp_subshell_cwd[PATH_MAX];
1356             char rp_current_panel_cwd[PATH_MAX];
1357             char *p_subshell_cwd, *p_current_panel_cwd;
1358 
1359             p_subshell_cwd = mc_realpath (subshell_cwd, rp_subshell_cwd);
1360             p_current_panel_cwd = mc_realpath (pcwd, rp_current_panel_cwd);
1361 
1362             if (p_subshell_cwd == NULL)
1363                 p_subshell_cwd = subshell_cwd;
1364             if (p_current_panel_cwd == NULL)
1365                 p_current_panel_cwd = pcwd;
1366             bPathNotEq = strcmp (p_subshell_cwd, p_current_panel_cwd) != 0;
1367         }
1368 
1369         if (bPathNotEq && !DIR_IS_DOT (pcwd))
1370         {
1371             char *cwd;
1372 
1373             cwd = vfs_path_to_str_flags (subshell_get_cwd (), 0, VPF_STRIP_PASSWORD);
1374             vfs_print_message (_("Warning: Cannot change to %s.\n"), cwd);
1375             g_free (cwd);
1376         }
1377     }
1378 
1379     /* Really escape Zsh history */
1380     if (mc_global.shell->type == SHELL_ZSH)
1381     {
1382         /* Per Zsh documentation last command prefixed with space lingers in the internal history
1383          * until the next command is entered before it vanishes. To make it vanish right away,
1384          * type a space and press return. */
1385         write_all (mc_global.tty.subshell_pty, " \n", 2);
1386         subshell_state = RUNNING_COMMAND;
1387         feed_subshell (QUIETLY, FALSE);
1388     }
1389 
1390     update_subshell_prompt = FALSE;
1391 
1392     g_free (pcwd);
1393     /* Make sure that MC never stores the CWD in a silly format */
1394     /* like /usr////lib/../bin, or the strcmp() above will fail */
1395 }
1396 
1397 /* --------------------------------------------------------------------------------------------- */
1398 
1399 void
1400 subshell_get_console_attributes (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1401 {
1402     /* Get our current terminal modes */
1403 
1404     if (tcgetattr (STDOUT_FILENO, &shell_mode))
1405     {
1406         fprintf (stderr, "Cannot get terminal settings: %s\r\n", unix_error_string (errno));
1407         mc_global.tty.use_subshell = FALSE;
1408     }
1409 }
1410 
1411 /* --------------------------------------------------------------------------------------------- */
1412 /**
1413  * Figure out whether the subshell has stopped, exited or been killed
1414  * Possibly modifies: 'subshell_alive', 'subshell_stopped' and 'quit' */
1415 
1416 void
1417 sigchld_handler (int sig)
     /* [previous][next][first][last][top][bottom][index][help]  */
1418 {
1419     int status;
1420     pid_t pid;
1421 
1422     (void) sig;
1423 
1424     pid = waitpid (subshell_pid, &status, WUNTRACED | WNOHANG);
1425 
1426     if (pid == subshell_pid)
1427     {
1428         /* Figure out what has happened to the subshell */
1429 
1430         if (WIFSTOPPED (status))
1431         {
1432             if (WSTOPSIG (status) == SIGSTOP)
1433             {
1434                 /* The subshell has received a SIGSTOP signal */
1435                 subshell_stopped = TRUE;
1436             }
1437             else
1438             {
1439                 /* The user has suspended the subshell.  Revive it */
1440                 kill (subshell_pid, SIGCONT);
1441             }
1442         }
1443         else
1444         {
1445             /* The subshell has either exited normally or been killed */
1446             subshell_alive = FALSE;
1447             delete_select_channel (mc_global.tty.subshell_pty);
1448             if (WIFEXITED (status) && WEXITSTATUS (status) != FORK_FAILURE)
1449             {
1450                 int subshell_quit;
1451                 subshell_quit = subshell_get_mainloop_quit () | SUBSHELL_EXIT;  /* Exited normally */
1452                 subshell_set_mainloop_quit (subshell_quit);
1453             }
1454         }
1455     }
1456     subshell_handle_cons_saver ();
1457 
1458     /* If we got here, some other child exited; ignore it */
1459 }
1460 
1461 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */