1998-02-27 07:54:42 +03:00
|
|
|
/* Concurrent shell support for the Midnight Commander
|
|
|
|
Copyright (C) 1994, 1995 Dugan Porter
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of Version 2 of the GNU General Public
|
|
|
|
License, as published by the Free Software Foundation.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
2000-08-23 02:50:00 +04:00
|
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
1998-02-27 07:54:42 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#ifdef HAVE_SUBSHELL_SUPPORT
|
|
|
|
|
2001-05-17 04:47:51 +04:00
|
|
|
#ifndef _GNU_SOURCE
|
|
|
|
# define _GNU_SOURCE 1
|
|
|
|
#endif
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h> /* For errno, putenv, etc. */
|
|
|
|
#include <errno.h> /* For errno on SunOS systems */
|
|
|
|
#include <termios.h> /* tcgetattr(), struct termios, etc. */
|
|
|
|
#include <sys/types.h> /* Required by unistd.h below */
|
2001-09-11 06:18:02 +04:00
|
|
|
#ifdef HAVE_SYS_IOCTL_H
|
|
|
|
# include <sys/ioctl.h> /* For ioctl() (surprise, surprise) */
|
1998-02-27 07:54:42 +03:00
|
|
|
#endif
|
|
|
|
#include <string.h> /* strstr(), strcpy(), etc. */
|
|
|
|
#include <signal.h> /* sigaction(), sigprocmask(), etc. */
|
|
|
|
#include <sys/stat.h> /* Required by dir.h & panel.h below */
|
2002-10-31 07:01:19 +03:00
|
|
|
#include <ctype.h> /* isalnum() */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
# include <unistd.h> /* For pipe, fork, setsid, access etc */
|
|
|
|
#endif
|
|
|
|
|
2001-08-24 08:51:11 +04:00
|
|
|
#ifdef HAVE_STROPTS_H
|
|
|
|
# include <stropts.h> /* For I_PUSH */
|
|
|
|
#endif /* HAVE_STROPTS_H */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2002-11-14 20:08:56 +03:00
|
|
|
#include "global.h"
|
|
|
|
#include "tty.h" /* LINES */
|
2003-10-26 09:45:59 +03:00
|
|
|
#include "panel.h" /* current_panel */
|
2002-11-14 20:08:56 +03:00
|
|
|
#include "wtools.h" /* query_dialog() */
|
2003-03-22 08:15:46 +03:00
|
|
|
#include "main.h" /* do_update_prompt() */
|
2002-11-14 20:08:56 +03:00
|
|
|
#include "cons.saver.h" /* handle_console() */
|
|
|
|
#include "key.h" /* XCTRL */
|
1998-02-27 07:54:42 +03:00
|
|
|
#include "subshell.h"
|
|
|
|
|
2001-09-08 09:36:23 +04:00
|
|
|
#ifndef WEXITSTATUS
|
|
|
|
# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef WIFEXITED
|
|
|
|
# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
|
|
|
|
#endif
|
|
|
|
|
2002-09-11 09:23:18 +04:00
|
|
|
/* tcsh closes all non-standard file descriptors, so we have to use a pipe */
|
|
|
|
static char tcsh_fifo[128];
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
/* Local functions */
|
2001-04-17 10:51:18 +04:00
|
|
|
static void init_raw_mode (void);
|
1998-02-27 07:54:42 +03:00
|
|
|
static int feed_subshell (int how, int fail_on_error);
|
|
|
|
static void synchronize (void);
|
|
|
|
static int pty_open_master (char *pty_name);
|
|
|
|
static int pty_open_slave (const char *pty_name);
|
2002-07-11 02:49:08 +04:00
|
|
|
static int resize_tty (int fd);
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
#ifndef STDIN_FILENO
|
|
|
|
# define STDIN_FILENO 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef STDOUT_FILENO
|
|
|
|
# define STDOUT_FILENO 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef STDERR_FILENO
|
|
|
|
# define STDERR_FILENO 2
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* If using a subshell for evaluating commands this is true */
|
|
|
|
int use_subshell =
|
|
|
|
#ifdef SUBSHELL_OPTIONAL
|
|
|
|
FALSE;
|
|
|
|
#else
|
|
|
|
TRUE;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* File descriptor of the pseudoterminal used by the subshell */
|
|
|
|
int subshell_pty = 0;
|
|
|
|
|
|
|
|
/* The key for switching back to MC from the subshell */
|
2002-12-24 06:05:09 +03:00
|
|
|
static const char subshell_switch_key = XCTRL('o') & 255;
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
/* State of the subshell:
|
|
|
|
* INACTIVE: the default state; awaiting a command
|
|
|
|
* ACTIVE: remain in the shell until the user hits `subshell_switch_key'
|
|
|
|
* RUNNING_COMMAND: return to MC when the current command finishes */
|
|
|
|
enum subshell_state_enum subshell_state;
|
|
|
|
|
|
|
|
/* Holds the latest prompt captured from the subshell */
|
|
|
|
char *subshell_prompt = NULL;
|
|
|
|
|
|
|
|
/* Initial length of the buffer for the subshell's prompt */
|
|
|
|
#define INITIAL_PROMPT_SIZE 10
|
|
|
|
|
|
|
|
/* Used by the child process to indicate failure to start the subshell */
|
|
|
|
#define FORK_FAILURE 69 /* Arbitrary */
|
|
|
|
|
|
|
|
/* Initial length of the buffer for all I/O with the subshell */
|
|
|
|
#define INITIAL_PTY_BUFFER_SIZE 100 /* Arbitrary; but keep it >= 80 */
|
|
|
|
|
|
|
|
/* For pipes */
|
|
|
|
enum {READ=0, WRITE=1};
|
|
|
|
|
|
|
|
static char *pty_buffer; /* For reading/writing on the subshell's pty */
|
|
|
|
static int pty_buffer_size; /* The buffer grows as needed */
|
|
|
|
static int subshell_pipe[2]; /* To pass CWD info from the subshell to MC */
|
|
|
|
static pid_t subshell_pid = 1; /* The subshell's process ID */
|
|
|
|
static char subshell_cwd[MC_MAXPATHLEN+1]; /* One extra char for final '\n' */
|
|
|
|
|
|
|
|
/* Subshell type (gleaned from the SHELL environment variable, if available) */
|
|
|
|
static enum {BASH, TCSH, ZSH} subshell_type;
|
|
|
|
|
|
|
|
/* Flag to indicate whether the subshell is ready for next command */
|
|
|
|
static int subshell_ready;
|
|
|
|
|
|
|
|
/* The following two flags can be changed by the SIGCHLD handler. This is */
|
|
|
|
/* OK, because the `int' type is updated atomically on all known machines */
|
|
|
|
static volatile int subshell_alive, subshell_stopped;
|
|
|
|
|
|
|
|
/* We store the terminal's initial mode here so that we can configure
|
|
|
|
the pty similarly, and also so we can restore the real terminal to
|
|
|
|
sanity if we have to exit abruptly */
|
|
|
|
static struct termios shell_mode;
|
|
|
|
|
2001-04-17 10:51:18 +04:00
|
|
|
/* This is a transparent mode for the terminal where MC is running on */
|
|
|
|
/* It is used when the shell is active, so that the control signals */
|
|
|
|
/* are delivered to the shell pty */
|
|
|
|
static struct termios raw_mode;
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
/* This counter indicates how many characters of prompt we have read */
|
|
|
|
/* FIXME: try to figure out why this had to become global */
|
|
|
|
static int prompt_pos;
|
|
|
|
|
|
|
|
|
2001-05-17 04:40:18 +04:00
|
|
|
/*
|
|
|
|
* Prepare child process to running the shell and run it.
|
|
|
|
*
|
|
|
|
* Modifies the global variables (in the child process only):
|
|
|
|
* shell_mode
|
|
|
|
*
|
|
|
|
* Returns: never.
|
|
|
|
*/
|
2004-02-10 03:11:09 +03:00
|
|
|
static void
|
|
|
|
init_subshell_child (const char *pty_name)
|
2001-05-17 04:40:18 +04:00
|
|
|
{
|
|
|
|
int pty_slave;
|
2004-08-30 03:59:17 +04:00
|
|
|
const char *init_file = NULL;
|
2003-03-05 23:18:11 +03:00
|
|
|
#ifdef HAVE_GETSID
|
|
|
|
pid_t mc_sid;
|
2004-02-10 03:11:09 +03:00
|
|
|
#endif /* HAVE_GETSID */
|
|
|
|
|
|
|
|
setsid (); /* Get a fresh terminal session */
|
2001-05-17 04:40:18 +04:00
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Open the slave side of the pty: again */
|
2001-05-17 04:40:18 +04:00
|
|
|
pty_slave = pty_open_slave (pty_name);
|
|
|
|
|
|
|
|
/* This must be done before closing the master side of the pty, */
|
2004-02-10 03:11:09 +03:00
|
|
|
/* or it will fail on certain idiotic systems, such as Solaris. */
|
2001-05-17 04:40:18 +04:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
/* Close master side of pty. This is important; apart from */
|
|
|
|
/* freeing up the descriptor for use in the subshell, it also */
|
|
|
|
/* means that when MC exits, the subshell will get a SIGHUP and */
|
|
|
|
/* exit too, because there will be no more descriptors pointing */
|
|
|
|
/* at the master side of the pty and so it will disappear. */
|
2001-05-17 04:40:18 +04:00
|
|
|
|
|
|
|
close (subshell_pty);
|
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Make sure that it has become our controlling terminal */
|
2001-05-17 04:40:18 +04:00
|
|
|
|
|
|
|
/* Redundant on Linux and probably most systems, but just in case: */
|
|
|
|
|
|
|
|
#ifdef TIOCSCTTY
|
|
|
|
ioctl (pty_slave, TIOCSCTTY, 0);
|
|
|
|
#endif
|
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Configure its terminal modes and window size */
|
2001-05-17 04:40:18 +04:00
|
|
|
|
|
|
|
/* Set up the pty with the same termios flags as our own tty, plus */
|
|
|
|
/* TOSTOP, which keeps background processes from writing to the pty */
|
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
shell_mode.c_lflag |= TOSTOP; /* So background writers get SIGTTOU */
|
|
|
|
if (tcsetattr (pty_slave, TCSANOW, &shell_mode)) {
|
|
|
|
fprintf (stderr, "Cannot set pty terminal modes: %s\r\n",
|
|
|
|
unix_error_string (errno));
|
|
|
|
_exit (FORK_FAILURE);
|
2001-05-17 04:40:18 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the pty's size (80x25 by default on Linux) according to the */
|
|
|
|
/* size of the real terminal as calculated by ncurses, if possible */
|
2002-07-11 02:49:08 +04:00
|
|
|
resize_tty (pty_slave);
|
2001-05-17 04:40:18 +04:00
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Set up the subshell's environment and init file name */
|
2001-05-17 04:40:18 +04:00
|
|
|
|
|
|
|
/* It simplifies things to change to our home directory here, */
|
|
|
|
/* and the user's startup file may do a `cd' command anyway */
|
2004-02-10 03:11:09 +03:00
|
|
|
chdir (home_dir); /* FIXME? What about when we re-run the subshell? */
|
2001-05-17 04:40:18 +04:00
|
|
|
|
2003-03-05 23:18:11 +03:00
|
|
|
#ifdef HAVE_GETSID
|
|
|
|
/* Set MC_SID to prevent running one mc from another */
|
|
|
|
mc_sid = getsid (0);
|
|
|
|
if (mc_sid != -1) {
|
|
|
|
char sid_str[BUF_SMALL];
|
2004-02-10 03:11:09 +03:00
|
|
|
g_snprintf (sid_str, sizeof (sid_str), "MC_SID=%ld",
|
|
|
|
(long) mc_sid);
|
2004-08-30 03:59:17 +04:00
|
|
|
putenv (g_strdup (sid_str));
|
2003-03-05 23:18:11 +03:00
|
|
|
}
|
2004-02-10 03:11:09 +03:00
|
|
|
#endif /* HAVE_GETSID */
|
2003-03-05 23:18:11 +03:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
switch (subshell_type) {
|
|
|
|
case BASH:
|
|
|
|
init_file = ".mc/bashrc";
|
|
|
|
if (access (init_file, R_OK) == -1)
|
|
|
|
init_file = ".bashrc";
|
2001-05-17 04:40:18 +04:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
/* Make MC's special commands not show up in bash's history */
|
2004-12-08 16:07:53 +03:00
|
|
|
putenv ("HISTCONTROL=ignorespace");
|
2001-05-17 04:40:18 +04:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
/* Allow alternative readline settings for MC */
|
|
|
|
if (access (".mc/inputrc", R_OK) == 0)
|
2004-12-08 16:07:53 +03:00
|
|
|
putenv ("INPUTRC=.mc/inputrc");
|
2001-05-17 04:40:18 +04:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
break;
|
2001-05-17 04:40:18 +04:00
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* TODO: Find a way to pass initfile to TCSH and ZSH */
|
2004-02-10 03:11:09 +03:00
|
|
|
case TCSH:
|
|
|
|
case ZSH:
|
|
|
|
break;
|
2001-05-17 04:40:18 +04:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
default:
|
|
|
|
fprintf (stderr, __FILE__ ": unimplemented subshell type %d\r\n",
|
|
|
|
subshell_type);
|
|
|
|
_exit (FORK_FAILURE);
|
2001-05-17 04:40:18 +04:00
|
|
|
}
|
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Attach all our standard file descriptors to the pty */
|
2001-05-17 04:40:18 +04:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
/* This is done just before the fork, because stderr must still */
|
2001-05-17 04:40:18 +04:00
|
|
|
/* be connected to the real tty during the above error messages; */
|
2004-02-10 03:11:09 +03:00
|
|
|
/* otherwise the user will never see them. */
|
2001-05-17 04:40:18 +04:00
|
|
|
|
|
|
|
dup2 (pty_slave, STDIN_FILENO);
|
|
|
|
dup2 (pty_slave, STDOUT_FILENO);
|
|
|
|
dup2 (pty_slave, STDERR_FILENO);
|
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Execute the subshell at last */
|
2001-05-17 04:40:18 +04:00
|
|
|
|
|
|
|
close (subshell_pipe[READ]);
|
2004-02-10 03:11:09 +03:00
|
|
|
close (pty_slave); /* These may be FD_CLOEXEC, but just in case... */
|
2001-05-17 04:40:18 +04:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
switch (subshell_type) {
|
|
|
|
case BASH:
|
2004-09-24 18:02:39 +04:00
|
|
|
execl (shell, "bash", "-rcfile", init_file, (char *) NULL);
|
2004-02-10 03:11:09 +03:00
|
|
|
break;
|
2001-05-17 04:40:18 +04:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
case TCSH:
|
2004-09-24 18:02:39 +04:00
|
|
|
execl (shell, "tcsh", (char *) NULL);
|
2004-02-10 03:11:09 +03:00
|
|
|
break;
|
2001-05-17 04:40:18 +04:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
case ZSH:
|
|
|
|
/* Use -g to exclude cmds beginning with space from history
|
|
|
|
* and -Z to use the line editor on non-interactive term */
|
2004-09-24 18:02:39 +04:00
|
|
|
execl (shell, "zsh", "-Z", "-g", (char *) NULL);
|
2001-05-17 04:40:18 +04:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
break;
|
2001-05-17 04:40:18 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If we get this far, everything failed miserably */
|
|
|
|
_exit (FORK_FAILURE);
|
|
|
|
}
|
|
|
|
|
2003-03-05 23:18:11 +03:00
|
|
|
|
|
|
|
#ifdef HAVE_GETSID
|
|
|
|
/*
|
|
|
|
* Check MC_SID to prevent running one mc from another.
|
|
|
|
* Return:
|
|
|
|
* 0 if no parent mc in our session was found,
|
|
|
|
* 1 if parent mc was found and the user wants to continue,
|
|
|
|
* 2 if parent mc was found and the user wants to quit mc.
|
|
|
|
*/
|
|
|
|
static int
|
2004-08-16 07:24:49 +04:00
|
|
|
check_sid (void)
|
2003-03-05 23:18:11 +03:00
|
|
|
{
|
|
|
|
pid_t my_sid, old_sid;
|
2004-09-25 17:46:23 +04:00
|
|
|
const char *sid_str;
|
2003-03-05 23:18:11 +03:00
|
|
|
int r;
|
|
|
|
|
|
|
|
sid_str = getenv ("MC_SID");
|
|
|
|
if (!sid_str)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
old_sid = (pid_t) strtol (sid_str, NULL, 0);
|
|
|
|
if (!old_sid)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
my_sid = getsid (0);
|
|
|
|
if (my_sid == -1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* The parent mc is in a different session, it's OK */
|
|
|
|
if (old_sid != my_sid)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
r = query_dialog (_("Warning"),
|
|
|
|
_("GNU Midnight Commander is already\n"
|
|
|
|
"running on this terminal.\n"
|
|
|
|
"Subshell support will be disabled."), D_ERROR, 2,
|
|
|
|
_("&OK"), _("&Quit"));
|
|
|
|
if (r != 0) {
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif /* HAVE_GETSID */
|
|
|
|
|
|
|
|
|
2001-05-17 04:40:18 +04:00
|
|
|
/*
|
|
|
|
* Fork the subshell, and set up many, many things.
|
|
|
|
*
|
|
|
|
* Possibly modifies the global variables:
|
|
|
|
* subshell_type, subshell_alive, subshell_stopped, subshell_pid
|
|
|
|
* use_subshell - Is set to FALSE if we can't run the subshell
|
|
|
|
* quit - Can be set to SUBSHELL_EXIT by the SIGCHLD handler
|
|
|
|
*/
|
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
void
|
|
|
|
init_subshell (void)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
/* This must be remembered across calls to init_subshell() */
|
Glibing..... (2)
Wed Jan 27 03:17:44 1999 Timur Bakeyev <mc@bat.ru>
* Converted memory managment to Glib. Now we use g_new()/g_malloc()/
g_strdup()/g_free() routings. Also, copy_strings() replaced by
g_strconcat(), strcasecmp() -> g_strcasecmp(),and sprintf() by
g_snprintf().
* Some sequences of malloc()/sprintf() changed to g_strdup_printf().
* mad.[ch]: Modified, to work with new GLib's memory managment. Fixed
a missing #undef for tempnam, which caused dead loop. Add several new
functions to emulate GLib memory managment.
*main.c, mad.[ch]: Add a new switch "-M", which allows to redirect MAD
messages to the file.
* util.[ch], utilunix.c: Modified, deleted our variants of strcasecmp()
and strdup() - we have g_ equivalences. Remove get_full_name() - it is
similar to concat_dir_and_file(). Some other tricks with g_* functions.
* global.h: Modified, extended. Now it is main memory mangment include -
i.e. all inclusions of <stdlib.h>, <malloc.h>, <glib.h>, "fs.h", "mem.h",
"util.h" and "mad.h" done there. This elimanates problem with proper or-
der of #include's.
* All around the source - changed order of #include's, most of them gone
to global.h (see above), minor changes, like "0" -> NULL in string func-
tions.
1999-01-27 04:08:30 +03:00
|
|
|
static char pty_name[BUF_SMALL];
|
2003-03-22 08:15:46 +03:00
|
|
|
char precmd[BUF_SMALL];
|
2001-05-17 04:13:19 +04:00
|
|
|
int pty_slave = -1;
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2003-03-05 23:23:50 +03:00
|
|
|
#ifdef HAVE_GETSID
|
2003-03-05 23:18:11 +03:00
|
|
|
switch (check_sid ()) {
|
|
|
|
case 1:
|
|
|
|
use_subshell = FALSE;
|
|
|
|
return;
|
|
|
|
case 2:
|
|
|
|
use_subshell = FALSE;
|
|
|
|
midnight_shutdown = 1;
|
|
|
|
return;
|
|
|
|
}
|
2003-03-05 23:23:50 +03:00
|
|
|
#endif /* HAVE_GETSID */
|
2004-02-10 03:11:09 +03:00
|
|
|
|
2001-04-17 10:51:18 +04:00
|
|
|
/* Take the current (hopefully pristine) tty mode and make */
|
|
|
|
/* a raw mode based on it now, before we do anything else with it */
|
|
|
|
init_raw_mode ();
|
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
if (subshell_pty == 0) { /* First time through */
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Find out what type of shell we have */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
if (strstr (shell, "/zsh") || getenv ("ZSH_VERSION"))
|
1998-02-27 07:54:42 +03:00
|
|
|
subshell_type = ZSH;
|
|
|
|
else if (strstr (shell, "/tcsh"))
|
|
|
|
subshell_type = TCSH;
|
|
|
|
else if (strstr (shell, "/bash") || getenv ("BASH"))
|
|
|
|
subshell_type = BASH;
|
2004-02-10 03:11:09 +03:00
|
|
|
else {
|
1998-02-27 07:54:42 +03:00
|
|
|
use_subshell = FALSE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Open a pty for talking to the subshell */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
/* FIXME: We may need to open a fresh pty each time on SVR4 */
|
|
|
|
|
|
|
|
subshell_pty = pty_open_master (pty_name);
|
2004-02-10 03:11:09 +03:00
|
|
|
if (subshell_pty == -1) {
|
2004-02-10 03:16:17 +03:00
|
|
|
fprintf (stderr, "Cannot open master side of pty: %s\r\n",
|
|
|
|
unix_error_string (errno));
|
1998-02-27 07:54:42 +03:00
|
|
|
use_subshell = FALSE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pty_slave = pty_open_slave (pty_name);
|
2004-02-10 03:11:09 +03:00
|
|
|
if (pty_slave == -1) {
|
|
|
|
fprintf (stderr, "Cannot open slave side of pty %s: %s\r\n",
|
|
|
|
pty_name, unix_error_string (errno));
|
1998-02-27 07:54:42 +03:00
|
|
|
use_subshell = FALSE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Initialise the pty's I/O buffer */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
pty_buffer_size = INITIAL_PTY_BUFFER_SIZE;
|
2004-08-16 20:22:14 +04:00
|
|
|
pty_buffer = g_malloc (pty_buffer_size);
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Create a pipe for receiving the subshell's CWD */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
if (subshell_type == TCSH) {
|
2002-09-11 09:23:18 +04:00
|
|
|
g_snprintf (tcsh_fifo, sizeof (tcsh_fifo), "%s/mc.pipe.%d",
|
2004-08-16 08:57:07 +04:00
|
|
|
mc_tmpdir (), (int) getpid ());
|
2004-02-10 03:11:09 +03:00
|
|
|
if (mkfifo (tcsh_fifo, 0600) == -1) {
|
|
|
|
fprintf (stderr, "mkfifo(%s) failed: %s\r\n", tcsh_fifo,
|
|
|
|
unix_error_string (errno));
|
1998-02-27 07:54:42 +03:00
|
|
|
use_subshell = FALSE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Opening the FIFO as O_RDONLY or O_WRONLY causes deadlock */
|
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
if ((subshell_pipe[READ] = open (tcsh_fifo, O_RDWR)) == -1
|
|
|
|
|| (subshell_pipe[WRITE] =
|
|
|
|
open (tcsh_fifo, O_RDWR)) == -1) {
|
2004-03-05 11:08:13 +03:00
|
|
|
fprintf (stderr, _("Cannot open named pipe %s\n"), tcsh_fifo);
|
|
|
|
perror (__FILE__": open");
|
1998-02-27 07:54:42 +03:00
|
|
|
use_subshell = FALSE;
|
|
|
|
return;
|
|
|
|
}
|
2004-02-10 03:11:09 +03:00
|
|
|
} else /* subshell_type is BASH or ZSH */ if (pipe (subshell_pipe)) {
|
2004-03-05 11:08:13 +03:00
|
|
|
perror (__FILE__": couldn't create pipe");
|
2004-02-10 03:11:09 +03:00
|
|
|
use_subshell = FALSE;
|
|
|
|
return;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Fork the subshell */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
subshell_alive = TRUE;
|
|
|
|
subshell_stopped = FALSE;
|
|
|
|
subshell_pid = fork ();
|
2004-02-10 03:11:09 +03:00
|
|
|
|
|
|
|
if (subshell_pid == -1) {
|
|
|
|
fprintf (stderr, "Cannot spawn the subshell process: %s\r\n",
|
|
|
|
unix_error_string (errno));
|
1998-02-27 07:54:42 +03:00
|
|
|
/* We exit here because, if the process table is full, the */
|
|
|
|
/* other method of running user commands won't work either */
|
|
|
|
exit (1);
|
|
|
|
}
|
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
if (subshell_pid == 0) { /* We are in the child process */
|
2001-05-17 04:40:18 +04:00
|
|
|
init_subshell_child (pty_name);
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
|
2001-05-17 04:13:19 +04:00
|
|
|
/* pty_slave is only opened when called the first time */
|
|
|
|
if (pty_slave != -1) {
|
2004-02-10 03:11:09 +03:00
|
|
|
close (pty_slave);
|
2001-05-17 04:13:19 +04:00
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Set up `precmd' or equivalent for reading the subshell's CWD */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
switch (subshell_type) {
|
2003-03-22 08:15:46 +03:00
|
|
|
case BASH:
|
2004-02-10 03:11:09 +03:00
|
|
|
g_snprintf (precmd, sizeof (precmd),
|
|
|
|
" PROMPT_COMMAND='pwd>&%d;kill -STOP $$'\n",
|
2003-03-22 08:15:46 +03:00
|
|
|
subshell_pipe[WRITE]);
|
|
|
|
break;
|
2004-02-10 03:11:09 +03:00
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
case ZSH:
|
2004-02-10 03:11:09 +03:00
|
|
|
g_snprintf (precmd, sizeof (precmd),
|
|
|
|
" precmd(){ pwd>&%d;kill -STOP $$ }\n",
|
2003-03-22 08:15:46 +03:00
|
|
|
subshell_pipe[WRITE]);
|
|
|
|
break;
|
2004-02-10 03:11:09 +03:00
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
case TCSH:
|
2001-07-13 04:24:50 +04:00
|
|
|
g_snprintf (precmd, sizeof (precmd),
|
|
|
|
"set echo_style=both;"
|
|
|
|
"alias precmd 'echo $cwd:q >>%s;kill -STOP $$'\n",
|
|
|
|
tcsh_fifo);
|
2003-03-22 08:15:46 +03:00
|
|
|
break;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
2003-03-22 08:15:46 +03:00
|
|
|
write (subshell_pty, precmd, strlen (precmd));
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Wait until the subshell has started up and processed the command */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
subshell_state = RUNNING_COMMAND;
|
|
|
|
enable_interrupt_key ();
|
2004-02-10 03:11:09 +03:00
|
|
|
if (!feed_subshell (QUIETLY, TRUE)) {
|
1998-02-27 07:54:42 +03:00
|
|
|
use_subshell = FALSE;
|
|
|
|
}
|
|
|
|
disable_interrupt_key ();
|
|
|
|
if (!subshell_alive)
|
2004-02-10 03:11:09 +03:00
|
|
|
use_subshell = FALSE; /* Subshell died instantly, so don't use it */
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-04-17 10:51:18 +04:00
|
|
|
static void init_raw_mode ()
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
2001-04-17 10:51:18 +04:00
|
|
|
static int initialized = 0;
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
/* MC calls reset_shell_mode() in pre_exec() to set the real tty to its */
|
|
|
|
/* original settings. However, here we need to make this tty very raw, */
|
|
|
|
/* so that all keyboard signals, XON/XOFF, etc. will get through to the */
|
|
|
|
/* pty. So, instead of changing the code for execute(), pre_exec(), */
|
|
|
|
/* etc, we just set up the modes we need here, before each command. */
|
|
|
|
|
2001-04-17 10:51:18 +04:00
|
|
|
if (initialized == 0) /* First time: initialise `raw_mode' */
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
tcgetattr (STDOUT_FILENO, &raw_mode);
|
|
|
|
raw_mode.c_lflag &= ~ICANON; /* Disable line-editing chars, etc. */
|
|
|
|
raw_mode.c_lflag &= ~ISIG; /* Disable intr, quit & suspend chars */
|
|
|
|
raw_mode.c_lflag &= ~ECHO; /* Disable input echoing */
|
|
|
|
raw_mode.c_iflag &= ~IXON; /* Pass ^S/^Q to subshell undisturbed */
|
|
|
|
raw_mode.c_iflag &= ~ICRNL; /* Don't translate CRs into LFs */
|
|
|
|
raw_mode.c_oflag &= ~OPOST; /* Don't postprocess output */
|
|
|
|
raw_mode.c_cc[VTIME] = 0; /* IE: wait forever, and return as */
|
|
|
|
raw_mode.c_cc[VMIN] = 1; /* soon as a character is available */
|
2001-04-17 10:51:18 +04:00
|
|
|
initialized = 1;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
2001-04-17 10:51:18 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int invoke_subshell (const char *command, int how, char **new_dir)
|
|
|
|
{
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Make the MC terminal transparent */
|
1998-02-27 07:54:42 +03:00
|
|
|
tcsetattr (STDOUT_FILENO, TCSANOW, &raw_mode);
|
|
|
|
|
|
|
|
/* Make the subshell change to MC's working directory */
|
|
|
|
if (new_dir)
|
2003-10-26 09:45:59 +03:00
|
|
|
do_subshell_chdir (current_panel->cwd, TRUE, 1);
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
if (command == NULL) /* The user has done "C-o" from MC */
|
|
|
|
{
|
|
|
|
if (subshell_state == INACTIVE)
|
|
|
|
{
|
|
|
|
subshell_state = ACTIVE;
|
|
|
|
/* FIXME: possibly take out this hack; the user can
|
|
|
|
re-play it by hitting C-hyphen a few times! */
|
|
|
|
write (subshell_pty, " \b", 2); /* Hack to make prompt reappear */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else /* MC has passed us a user command */
|
|
|
|
{
|
|
|
|
if (how == QUIETLY)
|
|
|
|
write (subshell_pty, " ", 1);
|
2001-01-21 13:26:09 +03:00
|
|
|
/* FIXME: if command is long (>8KB ?) we go comma */
|
1998-02-27 07:54:42 +03:00
|
|
|
write (subshell_pty, command, strlen (command));
|
|
|
|
write (subshell_pty, "\n", 1);
|
|
|
|
subshell_state = RUNNING_COMMAND;
|
|
|
|
subshell_ready = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
feed_subshell (how, FALSE);
|
|
|
|
|
2003-10-26 09:45:59 +03:00
|
|
|
if (new_dir && subshell_alive && strcmp (subshell_cwd, current_panel->cwd))
|
1998-02-27 07:54:42 +03:00
|
|
|
*new_dir = subshell_cwd; /* Make MC change to the subshell's CWD */
|
|
|
|
|
|
|
|
/* Restart the subshell if it has died by SIGHUP, SIGQUIT, etc. */
|
|
|
|
while (!subshell_alive && !quit && use_subshell)
|
|
|
|
init_subshell ();
|
|
|
|
|
|
|
|
prompt_pos = 0;
|
|
|
|
|
|
|
|
return quit;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
int
|
|
|
|
read_subshell_prompt (void)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
static int prompt_size = INITIAL_PROMPT_SIZE;
|
|
|
|
int bytes = 0, i, rc = 0;
|
2004-02-10 03:11:09 +03:00
|
|
|
struct timeval timeleft = { 0, 0 };
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
fd_set tmp;
|
|
|
|
FD_ZERO (&tmp);
|
|
|
|
FD_SET (subshell_pty, &tmp);
|
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
if (subshell_prompt == NULL) { /* First time through */
|
2004-08-16 20:22:14 +04:00
|
|
|
subshell_prompt = g_malloc (prompt_size);
|
1998-02-27 07:54:42 +03:00
|
|
|
*subshell_prompt = '\0';
|
|
|
|
prompt_pos = 0;
|
|
|
|
}
|
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
while (subshell_alive
|
|
|
|
&& (rc =
|
|
|
|
select (subshell_pty + 1, &tmp, NULL, NULL, &timeleft))) {
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Check for `select' errors */
|
1999-05-03 22:57:48 +04:00
|
|
|
if (rc == -1) {
|
1998-02-27 07:54:42 +03:00
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
1999-05-03 22:57:48 +04:00
|
|
|
else {
|
2004-02-10 03:11:09 +03:00
|
|
|
fprintf (stderr, "select (FD_SETSIZE, &tmp...): %s\r\n",
|
|
|
|
unix_error_string (errno));
|
1998-02-27 07:54:42 +03:00
|
|
|
exit (1);
|
|
|
|
}
|
1999-05-03 22:57:48 +04:00
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
bytes = read (subshell_pty, pty_buffer, pty_buffer_size);
|
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Extract the prompt from the shell output */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
for (i = 0; i < bytes; ++i)
|
|
|
|
if (pty_buffer[i] == '\n' || pty_buffer[i] == '\r') {
|
1998-02-27 07:54:42 +03:00
|
|
|
prompt_pos = 0;
|
|
|
|
} else {
|
2004-02-10 03:11:09 +03:00
|
|
|
if (!pty_buffer[i])
|
1998-02-27 07:54:42 +03:00
|
|
|
continue;
|
2004-02-10 03:11:09 +03:00
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
subshell_prompt[prompt_pos++] = pty_buffer[i];
|
|
|
|
if (prompt_pos == prompt_size)
|
2004-02-10 03:11:09 +03:00
|
|
|
subshell_prompt =
|
2004-08-16 20:22:14 +04:00
|
|
|
g_realloc (subshell_prompt, prompt_size *= 2);
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
|
2002-08-15 10:53:44 +04:00
|
|
|
subshell_prompt[prompt_pos] = '\0';
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
if (rc == 0 && bytes == 0)
|
|
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2002-07-11 02:49:08 +04:00
|
|
|
/* Resize given terminal using TIOCSWINSZ, return ioctl() result */
|
|
|
|
static int resize_tty (int fd)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
2003-06-18 08:47:17 +04:00
|
|
|
#if defined TIOCSWINSZ
|
1998-02-27 07:54:42 +03:00
|
|
|
struct winsize tty_size;
|
|
|
|
|
|
|
|
tty_size.ws_row = LINES;
|
|
|
|
tty_size.ws_col = COLS;
|
|
|
|
tty_size.ws_xpixel = tty_size.ws_ypixel = 0;
|
|
|
|
|
2002-07-11 02:49:08 +04:00
|
|
|
return ioctl (fd, TIOCSWINSZ, &tty_size);
|
2003-01-25 00:00:49 +03:00
|
|
|
#else
|
|
|
|
return 0;
|
1998-02-27 07:54:42 +03:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2002-07-11 02:49:08 +04:00
|
|
|
/* Resize subshell_pty */
|
|
|
|
void resize_subshell (void)
|
|
|
|
{
|
|
|
|
resize_tty (subshell_pty);
|
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
int
|
|
|
|
exit_subshell (void)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
int quit = TRUE;
|
2004-02-10 03:11:09 +03:00
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
if (subshell_state != INACTIVE && subshell_alive)
|
2004-02-10 03:11:09 +03:00
|
|
|
quit =
|
|
|
|
!query_dialog (_("Warning"),
|
|
|
|
_(" The shell is still active. Quit anyway? "),
|
|
|
|
0, 2, _("&Yes"), _("&No"));
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
if (quit && subshell_type == TCSH) {
|
2002-09-11 09:23:18 +04:00
|
|
|
if (unlink (tcsh_fifo) == -1)
|
2004-02-10 03:11:09 +03:00
|
|
|
fprintf (stderr, "Cannot remove named pipe %s: %s\r\n",
|
|
|
|
tcsh_fifo, unix_error_string (errno));
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
2002-08-02 00:31:52 +04:00
|
|
|
|
|
|
|
g_free (subshell_prompt);
|
2003-06-06 19:52:13 +04:00
|
|
|
g_free (pty_buffer);
|
2002-08-02 00:31:52 +04:00
|
|
|
subshell_prompt = NULL;
|
2004-09-04 02:00:27 +04:00
|
|
|
pty_buffer = NULL;
|
2002-08-02 00:31:52 +04:00
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
return quit;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-07-12 18:18:39 +04:00
|
|
|
/*
|
|
|
|
* Carefully quote directory name to allow entering any directory safely,
|
|
|
|
* no matter what weird characters it may contain in its name.
|
|
|
|
* NOTE: Treat directory name an untrusted data, don't allow it to cause
|
2002-09-07 04:27:53 +04:00
|
|
|
* executing any commands in the shell. Escape all control characters.
|
2001-07-12 18:18:39 +04:00
|
|
|
* Use following technique:
|
|
|
|
*
|
|
|
|
* for bash - echo with `-e', 3-digit octal numbers:
|
|
|
|
* cd "`echo -e '\ooo...\ooo'`"
|
|
|
|
*
|
2002-09-07 04:27:53 +04:00
|
|
|
* for zsh - echo with `-e', 4-digit octal numbers:
|
|
|
|
* cd "`echo '\oooo...\oooo'`"
|
|
|
|
*
|
|
|
|
* for tcsh - echo without `-e', 4-digit octal numbers:
|
2001-07-12 18:18:39 +04:00
|
|
|
* cd "`echo '\oooo...\oooo'`"
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
subshell_name_quote (const char *s)
|
|
|
|
{
|
|
|
|
char *ret, *d;
|
2002-09-07 04:27:53 +04:00
|
|
|
const char echo_cmd[] = "\"`echo '";
|
|
|
|
const char echo_e_cmd[] = "\"`echo -e '";
|
2001-07-12 18:18:39 +04:00
|
|
|
const char common_end[] = "'`\"";
|
2002-09-07 04:27:53 +04:00
|
|
|
const char *cmd_start;
|
|
|
|
int len;
|
2001-07-12 18:18:39 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Factor 5 because we need \, 0 and 3 other digits per character
|
|
|
|
* in the worst case (tcsh and zsh).
|
|
|
|
*/
|
|
|
|
d = ret = g_malloc (5 * strlen (s) + 16);
|
2002-09-07 04:27:53 +04:00
|
|
|
if (!d)
|
|
|
|
return NULL;
|
2001-07-12 18:18:39 +04:00
|
|
|
|
|
|
|
/* Prevent interpreting leading `-' as a switch for `cd' */
|
|
|
|
if (*s == '-') {
|
2002-10-31 07:01:19 +03:00
|
|
|
*d++ = '.';
|
|
|
|
*d++ = '/';
|
2001-07-12 18:18:39 +04:00
|
|
|
}
|
|
|
|
|
2002-09-07 04:27:53 +04:00
|
|
|
/* echo in tcsh doesn't understand the "-e" option */
|
|
|
|
if (subshell_type == TCSH)
|
|
|
|
cmd_start = echo_cmd;
|
|
|
|
else
|
|
|
|
cmd_start = echo_e_cmd;
|
|
|
|
|
|
|
|
/* Copy the beginning of the command to the buffer */
|
|
|
|
len = strlen (cmd_start);
|
|
|
|
memcpy (d, cmd_start, len);
|
|
|
|
d += len;
|
|
|
|
|
2001-07-12 18:18:39 +04:00
|
|
|
/*
|
|
|
|
* Print every character in octal format with the leading backslash.
|
2002-10-31 07:01:19 +03:00
|
|
|
* tcsh and zsh may require 4-digit octals, bash < 2.05b doesn't like them.
|
2001-07-12 18:18:39 +04:00
|
|
|
*/
|
|
|
|
if (subshell_type == BASH) {
|
|
|
|
for (; *s; s++) {
|
2002-10-31 07:01:19 +03:00
|
|
|
/* Must quote numbers, so that they are not glued to octals */
|
|
|
|
if (isalpha ((unsigned char) *s)) {
|
2005-01-14 13:00:29 +03:00
|
|
|
*d++ = (unsigned char) *s;
|
2002-10-31 07:01:19 +03:00
|
|
|
} else {
|
|
|
|
sprintf (d, "\\%03o", (unsigned char) *s);
|
|
|
|
d += 4;
|
|
|
|
}
|
2001-07-12 18:18:39 +04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (; *s; s++) {
|
2002-10-31 07:01:19 +03:00
|
|
|
if (isalnum ((unsigned char) *s)) {
|
2005-01-14 13:00:29 +03:00
|
|
|
*d++ = (unsigned char) *s;
|
2002-10-31 07:01:19 +03:00
|
|
|
} else {
|
|
|
|
sprintf (d, "\\0%03o", (unsigned char) *s);
|
|
|
|
d += 5;
|
|
|
|
}
|
2001-07-12 18:18:39 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy (d, common_end, sizeof (common_end));
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
/* If it actually changed the directory it returns true */
|
2004-02-10 03:11:09 +03:00
|
|
|
void
|
|
|
|
do_subshell_chdir (const char *directory, int do_update, int reset_prompt)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
2004-02-10 03:11:09 +03:00
|
|
|
if (!
|
|
|
|
(subshell_state == INACTIVE
|
|
|
|
&& strcmp (subshell_cwd, current_panel->cwd))) {
|
1998-02-27 07:54:42 +03:00
|
|
|
/* We have to repaint the subshell prompt if we read it from
|
|
|
|
* the main program. Please note that in the code after this
|
|
|
|
* if, the cd command that is sent will make the subshell
|
|
|
|
* repaint the prompt, so we don't have to paint it. */
|
|
|
|
if (do_update)
|
|
|
|
do_update_prompt ();
|
|
|
|
return;
|
|
|
|
}
|
2004-02-10 03:11:09 +03:00
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
/* The initial space keeps this out of the command history (in bash
|
|
|
|
because we set "HISTCONTROL=ignorespace") */
|
|
|
|
write (subshell_pty, " cd ", 4);
|
|
|
|
if (*directory) {
|
2005-01-14 13:00:29 +03:00
|
|
|
char *temp = subshell_name_quote (directory);
|
2002-09-07 04:27:53 +04:00
|
|
|
if (temp) {
|
|
|
|
write (subshell_pty, temp, strlen (temp));
|
|
|
|
g_free (temp);
|
|
|
|
} else {
|
|
|
|
/* Should not happen unless the directory name is so long
|
|
|
|
that we don't have memory to quote it. */
|
|
|
|
write (subshell_pty, ".", 1);
|
|
|
|
}
|
1998-04-15 10:05:34 +04:00
|
|
|
} else {
|
1998-02-27 07:54:42 +03:00
|
|
|
write (subshell_pty, "/", 1);
|
1998-04-15 10:05:34 +04:00
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
write (subshell_pty, "\n", 1);
|
2004-02-10 03:11:09 +03:00
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
subshell_state = RUNNING_COMMAND;
|
|
|
|
feed_subshell (QUIETLY, FALSE);
|
2004-02-10 03:11:09 +03:00
|
|
|
|
2005-01-14 13:00:29 +03:00
|
|
|
if (subshell_alive) {
|
|
|
|
int bPathNotEq = strcmp (subshell_cwd, current_panel->cwd);
|
2005-01-13 22:37:46 +03:00
|
|
|
|
2005-01-14 13:00:29 +03:00
|
|
|
if (bPathNotEq && subshell_type == TCSH) {
|
|
|
|
char rp_subshell_cwd[PATH_MAX];
|
|
|
|
char rp_current_panel_cwd[PATH_MAX];
|
2005-01-13 22:37:46 +03:00
|
|
|
|
2005-01-14 13:00:29 +03:00
|
|
|
char *p_subshell_cwd =
|
|
|
|
mc_realpath (subshell_cwd, rp_subshell_cwd);
|
|
|
|
char *p_current_panel_cwd =
|
|
|
|
mc_realpath (current_panel->cwd, rp_current_panel_cwd);
|
2005-01-13 22:37:46 +03:00
|
|
|
|
2005-01-14 13:00:29 +03:00
|
|
|
if (p_subshell_cwd == NULL)
|
|
|
|
p_subshell_cwd = subshell_cwd;
|
|
|
|
if (p_current_panel_cwd == NULL)
|
|
|
|
p_current_panel_cwd = current_panel->cwd;
|
|
|
|
bPathNotEq = strcmp (p_subshell_cwd, p_current_panel_cwd);
|
|
|
|
}
|
2005-01-13 22:37:46 +03:00
|
|
|
|
2005-01-14 13:00:29 +03:00
|
|
|
if (bPathNotEq && strcmp (current_panel->cwd, ".")) {
|
|
|
|
char *cwd = strip_password (g_strdup (current_panel->cwd), 1);
|
|
|
|
fprintf (stderr, _("Warning: Cannot change to %s.\n"), cwd);
|
|
|
|
g_free (cwd);
|
|
|
|
}
|
2004-11-03 22:43:17 +03:00
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
if (reset_prompt)
|
|
|
|
prompt_pos = 0;
|
|
|
|
update_prompt = FALSE;
|
|
|
|
/* Make sure that MC never stores the CWD in a silly format */
|
|
|
|
/* like /usr////lib/../bin, or the strcmp() above will fail */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
void
|
|
|
|
subshell_get_console_attributes (void)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Get our current terminal modes */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
if (tcgetattr (STDOUT_FILENO, &shell_mode)) {
|
|
|
|
fprintf (stderr, "Cannot get terminal settings: %s\r\n",
|
|
|
|
unix_error_string (errno));
|
1998-02-27 07:54:42 +03:00
|
|
|
use_subshell = FALSE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Figure out whether the subshell has stopped, exited or been killed */
|
|
|
|
/* Possibly modifies: `subshell_alive', `subshell_stopped' and `quit' */
|
2003-02-27 08:09:35 +03:00
|
|
|
void
|
|
|
|
sigchld_handler (int sig)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
1999-03-26 23:37:48 +03:00
|
|
|
int status;
|
|
|
|
pid_t pid;
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
pid = waitpid (subshell_pid, &status, WUNTRACED | WNOHANG);
|
|
|
|
|
|
|
|
if (pid == subshell_pid) {
|
2003-02-27 08:09:35 +03:00
|
|
|
/* Figure out what has happened to the subshell */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2003-02-27 08:09:35 +03:00
|
|
|
if (WIFSTOPPED (status)) {
|
2002-08-10 03:36:06 +04:00
|
|
|
if (WSTOPSIG (status) == SIGSTOP) {
|
1998-02-27 07:54:42 +03:00
|
|
|
/* The subshell has received a SIGSTOP signal */
|
|
|
|
subshell_stopped = TRUE;
|
2002-08-10 03:36:06 +04:00
|
|
|
} else {
|
|
|
|
/* The user has suspended the subshell. Revive it */
|
|
|
|
kill (subshell_pid, SIGCONT);
|
|
|
|
}
|
2003-02-27 08:09:35 +03:00
|
|
|
} else {
|
|
|
|
/* The subshell has either exited normally or been killed */
|
1998-02-27 07:54:42 +03:00
|
|
|
subshell_alive = FALSE;
|
2002-07-03 20:35:42 +04:00
|
|
|
delete_select_channel (subshell_pty);
|
1998-02-27 07:54:42 +03:00
|
|
|
if (WIFEXITED (status) && WEXITSTATUS (status) != FORK_FAILURE)
|
2003-02-27 08:09:35 +03:00
|
|
|
quit |= SUBSHELL_EXIT; /* Exited normally */
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
}
|
2003-02-27 08:09:35 +03:00
|
|
|
#ifdef __linux__
|
1998-02-27 07:54:42 +03:00
|
|
|
pid = waitpid (cons_saver_pid, &status, WUNTRACED | WNOHANG);
|
2003-02-27 08:09:35 +03:00
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
if (pid == cons_saver_pid) {
|
|
|
|
|
|
|
|
if (WIFSTOPPED (status))
|
2001-07-06 21:29:20 +04:00
|
|
|
/* Someone has stopped cons.saver - restart it */
|
1998-02-27 07:54:42 +03:00
|
|
|
kill (pid, SIGCONT);
|
2003-02-27 08:09:35 +03:00
|
|
|
else {
|
2001-07-06 21:29:20 +04:00
|
|
|
/* cons.saver has died - disable confole saving */
|
1998-02-27 07:54:42 +03:00
|
|
|
handle_console (CONSOLE_DONE);
|
2001-07-06 21:29:20 +04:00
|
|
|
console_flag = 0;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2003-02-27 08:09:35 +03:00
|
|
|
#endif /* __linux__ */
|
|
|
|
|
|
|
|
/* If we got here, some other child exited; ignore it */
|
|
|
|
#ifdef __EMX__ /* Need to report */
|
|
|
|
pid = wait (&status);
|
|
|
|
#endif
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Feed the subshell our keyboard input until it says it's finished */
|
2004-02-10 03:11:09 +03:00
|
|
|
static int
|
|
|
|
feed_subshell (int how, int fail_on_error)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
2004-02-10 03:11:09 +03:00
|
|
|
fd_set read_set; /* For `select' */
|
2002-08-25 09:44:57 +04:00
|
|
|
int maxfdp;
|
2004-02-10 03:11:09 +03:00
|
|
|
int bytes; /* For the return value from `read' */
|
|
|
|
int i; /* Loop counter */
|
|
|
|
|
|
|
|
struct timeval wtime; /* Maximum time we wait for the subshell */
|
1998-02-27 07:54:42 +03:00
|
|
|
struct timeval *wptr;
|
|
|
|
|
2001-08-24 06:17:15 +04:00
|
|
|
/* we wait up to 10 seconds if fail_on_error, forever otherwise */
|
1998-02-27 07:54:42 +03:00
|
|
|
wtime.tv_sec = 10;
|
|
|
|
wtime.tv_usec = 0;
|
2001-08-24 06:17:15 +04:00
|
|
|
wptr = fail_on_error ? &wtime : NULL;
|
2004-02-10 03:11:09 +03:00
|
|
|
|
2001-08-24 06:17:15 +04:00
|
|
|
while (1) {
|
1998-02-27 07:54:42 +03:00
|
|
|
if (!subshell_alive)
|
|
|
|
return FALSE;
|
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Prepare the file-descriptor set and call `select' */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
FD_ZERO (&read_set);
|
|
|
|
FD_SET (subshell_pty, &read_set);
|
|
|
|
FD_SET (subshell_pipe[READ], &read_set);
|
2002-08-25 09:44:57 +04:00
|
|
|
maxfdp = max (subshell_pty, subshell_pipe[READ]);
|
|
|
|
if (how == VISIBLY) {
|
1998-02-27 07:54:42 +03:00
|
|
|
FD_SET (STDIN_FILENO, &read_set);
|
2002-08-25 09:44:57 +04:00
|
|
|
maxfdp = max (maxfdp, STDIN_FILENO);
|
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
if (select (maxfdp + 1, &read_set, NULL, NULL, wptr) == -1) {
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
/* Despite using SA_RESTART, we still have to check for this */
|
|
|
|
if (errno == EINTR)
|
|
|
|
continue; /* try all over again */
|
|
|
|
tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
|
2004-02-10 03:11:09 +03:00
|
|
|
fprintf (stderr, "select (FD_SETSIZE, &read_set...): %s\r\n",
|
|
|
|
unix_error_string (errno));
|
1998-02-27 07:54:42 +03:00
|
|
|
exit (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (FD_ISSET (subshell_pty, &read_set))
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Read from the subshell, write to stdout */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
/* This loop improves performance by reducing context switches
|
2004-02-10 03:11:09 +03:00
|
|
|
by a factor of 20 or so... unfortunately, it also hangs MC
|
|
|
|
randomly, because of an apparent Linux bug. Investigate. */
|
1998-02-27 07:54:42 +03:00
|
|
|
/* for (i=0; i<5; ++i) * FIXME -- experimental */
|
2004-02-10 03:11:09 +03:00
|
|
|
{
|
|
|
|
bytes = read (subshell_pty, pty_buffer, pty_buffer_size);
|
|
|
|
|
|
|
|
/* The subshell has died */
|
|
|
|
if (bytes == -1 && errno == EIO && !subshell_alive)
|
|
|
|
return FALSE;
|
2002-07-30 04:03:26 +04:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
if (bytes <= 0) {
|
|
|
|
tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
|
|
|
|
fprintf (stderr, "read (subshell_pty...): %s\r\n",
|
|
|
|
unix_error_string (errno));
|
|
|
|
exit (1);
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
if (how == VISIBLY)
|
|
|
|
write (STDOUT_FILENO, pty_buffer, bytes);
|
|
|
|
}
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
else if (FD_ISSET (subshell_pipe[READ], &read_set))
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Read the subshell's CWD and capture its prompt */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
{
|
|
|
|
bytes =
|
|
|
|
read (subshell_pipe[READ], subshell_cwd,
|
|
|
|
MC_MAXPATHLEN + 1);
|
|
|
|
if (bytes <= 0) {
|
|
|
|
tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
|
|
|
|
fprintf (stderr, "read (subshell_pipe[READ]...): %s\r\n",
|
|
|
|
unix_error_string (errno));
|
|
|
|
exit (1);
|
|
|
|
}
|
2002-07-30 04:03:26 +04:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
subshell_cwd[bytes - 1] = 0; /* Squash the final '\n' */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
synchronize ();
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
subshell_ready = TRUE;
|
|
|
|
if (subshell_state == RUNNING_COMMAND) {
|
|
|
|
subshell_state = INACTIVE;
|
|
|
|
return 1;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
2004-02-10 03:11:09 +03:00
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
else if (FD_ISSET (STDIN_FILENO, &read_set))
|
2003-03-22 08:15:46 +03:00
|
|
|
/* Read from stdin, write to the subshell */
|
2004-02-10 03:11:09 +03:00
|
|
|
{
|
|
|
|
bytes = read (STDIN_FILENO, pty_buffer, pty_buffer_size);
|
|
|
|
if (bytes <= 0) {
|
|
|
|
tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
|
|
|
|
fprintf (stderr,
|
|
|
|
"read (STDIN_FILENO, pty_buffer...): %s\r\n",
|
|
|
|
unix_error_string (errno));
|
|
|
|
exit (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < bytes; ++i)
|
|
|
|
if (pty_buffer[i] == subshell_switch_key) {
|
|
|
|
write (subshell_pty, pty_buffer, i);
|
|
|
|
if (subshell_ready)
|
|
|
|
subshell_state = INACTIVE;
|
|
|
|
return TRUE;
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
write (subshell_pty, pty_buffer, bytes);
|
|
|
|
subshell_ready = FALSE;
|
|
|
|
} else {
|
|
|
|
return FALSE;
|
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Wait until the subshell dies or stops. If it stops, make it resume. */
|
|
|
|
/* Possibly modifies the globals `subshell_alive' and `subshell_stopped' */
|
|
|
|
static void synchronize (void)
|
|
|
|
{
|
|
|
|
sigset_t sigchld_mask, old_mask;
|
|
|
|
|
|
|
|
sigemptyset (&sigchld_mask);
|
|
|
|
sigaddset (&sigchld_mask, SIGCHLD);
|
|
|
|
sigprocmask (SIG_BLOCK, &sigchld_mask, &old_mask);
|
|
|
|
|
2002-08-20 02:28:00 +04:00
|
|
|
/*
|
|
|
|
* SIGCHLD should not be blocked, but we unblock it just in case.
|
|
|
|
* This is known to be useful for cygwin 1.3.12 and older.
|
|
|
|
*/
|
|
|
|
sigdelset (&old_mask, SIGCHLD);
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
/* Wait until the subshell has stopped */
|
|
|
|
while (subshell_alive && !subshell_stopped)
|
|
|
|
sigsuspend (&old_mask);
|
2002-08-19 06:06:01 +04:00
|
|
|
|
|
|
|
/* Discard all remaining data from stdin to the subshell */
|
|
|
|
tcflush (subshell_pty, TCOFLUSH);
|
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
subshell_stopped = FALSE;
|
|
|
|
kill (subshell_pid, SIGCONT);
|
|
|
|
|
|
|
|
sigprocmask (SIG_SETMASK, &old_mask, NULL);
|
|
|
|
/* We can't do any better without modifying the shell(s) */
|
|
|
|
}
|
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* pty opening functions */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2003-06-18 08:47:17 +04:00
|
|
|
#ifdef HAVE_GRANTPT
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* System V version of pty_open_master */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
static int pty_open_master (char *pty_name)
|
|
|
|
{
|
|
|
|
char *slave_name;
|
|
|
|
int pty_master;
|
|
|
|
|
2003-07-22 03:33:34 +04:00
|
|
|
#ifdef HAVE_POSIX_OPENPT
|
|
|
|
pty_master = posix_openpt(O_RDWR);
|
|
|
|
#elif HAVE_GETPT
|
1999-05-17 09:51:44 +04:00
|
|
|
/* getpt () is a GNU extension (glibc 2.1.x) */
|
|
|
|
pty_master = getpt ();
|
2002-09-25 08:38:29 +04:00
|
|
|
#elif IS_AIX
|
|
|
|
strcpy (pty_name, "/dev/ptc");
|
|
|
|
pty_master = open (pty_name, O_RDWR);
|
1999-05-17 09:51:44 +04:00
|
|
|
#else
|
1998-02-27 07:54:42 +03:00
|
|
|
strcpy (pty_name, "/dev/ptmx");
|
1999-05-25 22:15:27 +04:00
|
|
|
pty_master = open (pty_name, O_RDWR);
|
1999-05-17 09:51:44 +04:00
|
|
|
#endif
|
2003-07-22 03:33:34 +04:00
|
|
|
|
2001-05-29 11:30:00 +04:00
|
|
|
if (pty_master == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (grantpt (pty_master) == -1 /* Grant access to slave */
|
1998-02-27 07:54:42 +03:00
|
|
|
|| unlockpt (pty_master) == -1 /* Clear slave's lock flag */
|
|
|
|
|| !(slave_name = ptsname (pty_master))) /* Get slave's name */
|
|
|
|
{
|
|
|
|
close (pty_master);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
strcpy (pty_name, slave_name);
|
|
|
|
return pty_master;
|
|
|
|
}
|
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* System V version of pty_open_slave */
|
2004-02-10 03:11:09 +03:00
|
|
|
static int
|
|
|
|
pty_open_slave (const char *pty_name)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
int pty_slave = open (pty_name, O_RDWR);
|
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
if (pty_slave == -1) {
|
|
|
|
fprintf (stderr, "open (%s, O_RDWR): %s\r\n", pty_name,
|
|
|
|
unix_error_string (errno));
|
1998-02-27 07:54:42 +03:00
|
|
|
return -1;
|
|
|
|
}
|
2003-02-27 08:09:35 +03:00
|
|
|
#if !defined(__osf__) && !defined(__linux__)
|
1999-03-12 21:46:33 +03:00
|
|
|
#if defined (I_FIND) && defined (I_PUSH)
|
1998-02-27 07:54:42 +03:00
|
|
|
if (!ioctl (pty_slave, I_FIND, "ptem"))
|
2004-02-10 03:11:09 +03:00
|
|
|
if (ioctl (pty_slave, I_PUSH, "ptem") == -1) {
|
2004-08-16 09:17:49 +04:00
|
|
|
fprintf (stderr, "ioctl (%d, I_PUSH, \"ptem\") failed: %s\r\n",
|
2004-02-10 03:11:09 +03:00
|
|
|
pty_slave, unix_error_string (errno));
|
1998-02-27 07:54:42 +03:00
|
|
|
close (pty_slave);
|
|
|
|
return -1;
|
|
|
|
}
|
2004-02-10 03:11:09 +03:00
|
|
|
|
1998-02-27 07:54:42 +03:00
|
|
|
if (!ioctl (pty_slave, I_FIND, "ldterm"))
|
2004-02-10 03:11:09 +03:00
|
|
|
if (ioctl (pty_slave, I_PUSH, "ldterm") == -1) {
|
|
|
|
fprintf (stderr,
|
2004-08-16 09:17:49 +04:00
|
|
|
"ioctl (%d, I_PUSH, \"ldterm\") failed: %s\r\n",
|
2004-02-10 03:11:09 +03:00
|
|
|
pty_slave, unix_error_string (errno));
|
1998-02-27 07:54:42 +03:00
|
|
|
close (pty_slave);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#if !defined(sgi) && !defined(__sgi)
|
|
|
|
if (!ioctl (pty_slave, I_FIND, "ttcompat"))
|
2004-02-10 03:11:09 +03:00
|
|
|
if (ioctl (pty_slave, I_PUSH, "ttcompat") == -1) {
|
|
|
|
fprintf (stderr,
|
2004-08-16 09:17:49 +04:00
|
|
|
"ioctl (%d, I_PUSH, \"ttcompat\") failed: %s\r\n",
|
2004-02-10 03:11:09 +03:00
|
|
|
pty_slave, unix_error_string (errno));
|
1998-02-27 07:54:42 +03:00
|
|
|
close (pty_slave);
|
|
|
|
return -1;
|
|
|
|
}
|
2004-02-10 03:11:09 +03:00
|
|
|
#endif /* sgi || __sgi */
|
|
|
|
#endif /* I_FIND && I_PUSH */
|
|
|
|
#endif /* __osf__ || __linux__ */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
|
|
|
return pty_slave;
|
|
|
|
}
|
|
|
|
|
2003-06-18 08:47:17 +04:00
|
|
|
#else /* !HAVE_GRANTPT */
|
1998-02-27 07:54:42 +03:00
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* BSD version of pty_open_master */
|
1998-02-27 07:54:42 +03:00
|
|
|
static int pty_open_master (char *pty_name)
|
|
|
|
{
|
|
|
|
int pty_master;
|
|
|
|
char *ptr1, *ptr2;
|
|
|
|
|
|
|
|
strcpy (pty_name, "/dev/ptyXX");
|
|
|
|
for (ptr1 = "pqrstuvwxyzPQRST"; *ptr1; ++ptr1)
|
|
|
|
{
|
|
|
|
pty_name [8] = *ptr1;
|
|
|
|
for (ptr2 = "0123456789abcdef"; *ptr2; ++ptr2)
|
|
|
|
{
|
|
|
|
pty_name [9] = *ptr2;
|
|
|
|
|
|
|
|
/* Try to open master */
|
2001-05-17 20:53:35 +04:00
|
|
|
if ((pty_master = open (pty_name, O_RDWR)) == -1) {
|
1998-02-27 07:54:42 +03:00
|
|
|
if (errno == ENOENT) /* Different from EIO */
|
|
|
|
return -1; /* Out of pty devices */
|
|
|
|
else
|
|
|
|
continue; /* Try next pty device */
|
2001-05-17 20:53:35 +04:00
|
|
|
}
|
1998-02-27 07:54:42 +03:00
|
|
|
pty_name [5] = 't'; /* Change "pty" to "tty" */
|
|
|
|
if (access (pty_name, 6)){
|
|
|
|
close (pty_master);
|
|
|
|
pty_name [5] = 'p';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
return pty_master;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1; /* Ran out of pty devices */
|
|
|
|
}
|
|
|
|
|
2003-03-22 08:15:46 +03:00
|
|
|
/* BSD version of pty_open_slave */
|
2004-02-10 03:11:09 +03:00
|
|
|
static int
|
|
|
|
pty_open_slave (const char *pty_name)
|
1998-02-27 07:54:42 +03:00
|
|
|
{
|
|
|
|
int pty_slave;
|
|
|
|
struct group *group_info = getgrnam ("tty");
|
|
|
|
|
2004-02-10 03:11:09 +03:00
|
|
|
if (group_info != NULL) {
|
1998-02-27 07:54:42 +03:00
|
|
|
/* The following two calls will only succeed if we are root */
|
|
|
|
/* [Commented out while permissions problem is investigated] */
|
|
|
|
/* chown (pty_name, getuid (), group_info->gr_gid); FIXME */
|
|
|
|
/* chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP); FIXME */
|
|
|
|
}
|
|
|
|
if ((pty_slave = open (pty_name, O_RDWR)) == -1)
|
2004-02-10 03:11:09 +03:00
|
|
|
fprintf (stderr, "open (pty_name, O_RDWR): %s\r\n", pty_name);
|
1998-02-27 07:54:42 +03:00
|
|
|
return pty_slave;
|
|
|
|
}
|
|
|
|
|
2003-06-18 08:47:17 +04:00
|
|
|
#endif /* !HAVE_GRANTPT */
|
1998-02-27 07:54:42 +03:00
|
|
|
#endif /* HAVE_SUBSHELL_SUPPORT */
|