Exemplo n.º 1
0
/*.......................................................................
 * Run a program with enhanced terminal editing facilities.
 *
 * Usage:
 *  enhance program [args...]
 */
int main(int argc, char *argv[])
{
  int cntrl = -1;  /* The fd of the pseudo-terminal controller device */
  int slave = -1;  /* The fd of the pseudo-terminal slave device */
  pid_t pid;       /* The return value of fork() */
  int status;      /* The return statuses of the parent and child functions */
  char slave_name[PTY_MAX_NAME]; /* The filename of the slave end of the */
                                 /*  pseudo-terminal. */
  char *prog;      /* The name of the program (ie. argv[0]) */
/*
 * Check the arguments.
 */
  if(argc < 2) {
    fprintf(stderr, "Usage: %s <program> [arguments...]\n", argv[0]);
    return 1;
  };
/*
 * Get the name of the program.
 */
  prog = argv[0];
/*
 * If the user has the LC_CTYPE or LC_ALL environment variables set,
 * enable display of characters corresponding to the specified locale.
 */
  (void) setlocale(LC_CTYPE, "");
/*
 * If the program is taking its input from a pipe or a file, or
 * sending its output to something other than a terminal, run the
 * program without tecla.
 */
  if(!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) {
    if(execvp(argv[1], argv + 1) < 0) {
      fprintf(stderr, "%s: Unable to execute %s (%s).\n", prog, argv[1],
	      strerror(errno));
      fflush(stderr);
      _exit(1);
    };
  };
/*
 * Open the master side of a pseudo-terminal pair, and return
 * the corresponding file descriptor and the filename of the
 * slave end of the pseudo-terminal.
 */
  if(pty_open_master(prog, &cntrl, slave_name))
    return 1;
/*
 * Set up a signal handler to watch for the child process exiting.
 */
  signal(SIGCHLD, pty_child_exited);
/*
 * The above signal handler sends the parent process a SIGINT signal.
 * This signal is caught by gl_get_line(), which resets the terminal
 * settings, and if the application signal handler for this signal
 * doesn't abort the process, gl_get_line() returns NULL with errno
 * set to EINTR. Arrange to ignore the signal, so that gl_get_line()
 * returns and we have a chance to cleanup.
 */
  signal(SIGINT, SIG_IGN);
/*
 * We will read user input in one process, and run the user's program
 * in a child process.
 */
  pid = fork();
  if(pid < 0) {
    fprintf(stderr, "%s: Unable to fork child process (%s).\n", prog,
	    strerror(errno));
    return 1;
  };
/*
 * Are we the parent?
 */
  if(pid!=0) {
    status = pty_parent(prog, cntrl);
    close(cntrl);
  } else {
    close(cntrl); /* The child doesn't use the slave device */
    signal(SIGCHLD, pty_child_exited);
    if((slave = pty_open_slave(prog, slave_name)) >= 0) {
      status = pty_child(prog, slave, argv + 1);
      close(slave);
    } else {
      status = 1;
    };
  };
  return status;
}
Exemplo n.º 2
0
void
init_subshell (void)
{
    /* This must be remembered across calls to init_subshell() */
    static char pty_name[BUF_SMALL];
    char precmd[BUF_SMALL];

    switch (check_sid ())
    {
    case 1:
        mc_global.tty.use_subshell = FALSE;
        return;
    case 2:
        mc_global.tty.use_subshell = FALSE;
        mc_global.midnight_shutdown = TRUE;
        return;
    }

    /* 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 ();

    if (mc_global.tty.subshell_pty == 0)
    {   /* First time through */
        /* Find out what type of shell we have */

        if (strstr (mc_global.tty.shell, "/zsh") || getenv ("ZSH_VERSION"))
            subshell_type = ZSH;
        else if (strstr (mc_global.tty.shell, "/tcsh"))
            subshell_type = TCSH;
        else if (strstr (mc_global.tty.shell, "/csh"))
            subshell_type = TCSH;
        else if (strstr (mc_global.tty.shell, "/bash") || getenv ("BASH"))
            subshell_type = BASH;
        else if (strstr (mc_global.tty.shell, "/fish"))
            subshell_type = FISH;
        else
        {
            mc_global.tty.use_subshell = FALSE;
            return;
        }

        /* Open a pty for talking to the subshell */

        /* FIXME: We may need to open a fresh pty each time on SVR4 */

        mc_global.tty.subshell_pty = pty_open_master (pty_name);
        if (mc_global.tty.subshell_pty == -1)
        {
            fprintf (stderr, "Cannot open master side of pty: %s\r\n", unix_error_string (errno));
            mc_global.tty.use_subshell = FALSE;
            return;
        }
        subshell_pty_slave = pty_open_slave (pty_name);
        if (subshell_pty_slave == -1)
        {
            fprintf (stderr, "Cannot open slave side of pty %s: %s\r\n",
                     pty_name, unix_error_string (errno));
            mc_global.tty.use_subshell = FALSE;
            return;
        }

        /* Create a pipe for receiving the subshell's CWD */

        if (subshell_type == TCSH)
        {
            g_snprintf (tcsh_fifo, sizeof (tcsh_fifo), "%s/mc.pipe.%d",
                        mc_tmpdir (), (int) getpid ());
            if (mkfifo (tcsh_fifo, 0600) == -1)
            {
                fprintf (stderr, "mkfifo(%s) failed: %s\r\n", tcsh_fifo, unix_error_string (errno));
                mc_global.tty.use_subshell = FALSE;
                return;
            }

            /* Opening the FIFO as O_RDONLY or O_WRONLY causes deadlock */

            if ((subshell_pipe[READ] = open (tcsh_fifo, O_RDWR)) == -1
                    || (subshell_pipe[WRITE] = open (tcsh_fifo, O_RDWR)) == -1)
            {
                fprintf (stderr, _("Cannot open named pipe %s\n"), tcsh_fifo);
                perror (__FILE__ ": open");
                mc_global.tty.use_subshell = FALSE;
                return;
            }
        }
        else /* subshell_type is BASH or ZSH */ if (pipe (subshell_pipe))
        {
            perror (__FILE__ ": couldn't create pipe");
            mc_global.tty.use_subshell = FALSE;
            return;
        }
    }

    /* Fork the subshell */

    subshell_alive = TRUE;
    subshell_stopped = FALSE;
    subshell_pid = fork ();

    if (subshell_pid == -1)
    {
        fprintf (stderr, "Cannot spawn the subshell process: %s\r\n", unix_error_string (errno));
        /* We exit here because, if the process table is full, the */
        /* other method of running user commands won't work either */
        exit (EXIT_FAILURE);
    }

    if (subshell_pid == 0)
    {
        /* We are in the child process */
        init_subshell_child (pty_name);
    }

    /* Set up 'precmd' or equivalent for reading the subshell's CWD */

    switch (subshell_type)
    {
    case BASH:
        g_snprintf (precmd, sizeof (precmd),
                    " PROMPT_COMMAND='pwd>&%d;kill -STOP $$'\n", subshell_pipe[WRITE]);
        break;

    case ZSH:
        g_snprintf (precmd, sizeof (precmd),
                    " precmd(){ pwd>&%d;kill -STOP $$ }\n", subshell_pipe[WRITE]);
        break;

    case TCSH:
        g_snprintf (precmd, sizeof (precmd),
                    "set echo_style=both;"
                    "alias precmd 'echo $cwd:q >>%s;kill -STOP $$'\n", tcsh_fifo);
        break;
    case FISH:
        g_snprintf (precmd, sizeof (precmd),
                    "function fish_prompt ; pwd>&%d;kill -STOP %%self; end\n",
                    subshell_pipe[WRITE]);
        break;

    }
    write_all (mc_global.tty.subshell_pty, precmd, strlen (precmd));

    /* Wait until the subshell has started up and processed the command */

    subshell_state = RUNNING_COMMAND;
    tty_enable_interrupt_key ();
    if (!feed_subshell (QUIETLY, TRUE))
    {
        mc_global.tty.use_subshell = FALSE;
    }
    tty_disable_interrupt_key ();
    if (!subshell_alive)
        mc_global.tty.use_subshell = FALSE;     /* Subshell died instantly, so don't use it */
}
Exemplo n.º 3
0
/*
 * Called to set up the pty.
 * 
 * Returns an error message, or NULL on success.
 *
 * Also places the canonical host name into `realhost'. It must be
 * freed by the caller.
 */
static const char *pty_init(void *frontend, void **backend_handle, Conf *conf,
			    char *host, int port, char **realhost, int nodelay,
			    int keepalive)
{
    int slavefd;
    pid_t pid, pgrp;
#ifndef NOT_X_WINDOWS		       /* for Mac OS X native compilation */
    long windowid;
#endif
    Pty pty;

    if (single_pty) {
	pty = single_pty;
        assert(pty->conf == NULL);
    } else {
	pty = snew(struct pty_tag);
	pty->master_fd = pty->slave_fd = -1;
#ifndef OMIT_UTMP
	pty_stamped_utmp = FALSE;
#endif
    }

    pty->frontend = frontend;
    *backend_handle = NULL;	       /* we can't sensibly use this, sadly */

    pty->conf = conf_copy(conf);
    pty->term_width = conf_get_int(conf, CONF_width);
    pty->term_height = conf_get_int(conf, CONF_height);

    if (pty->master_fd < 0)
	pty_open_master(pty);

    /*
     * Set the backspace character to be whichever of ^H and ^? is
     * specified by bksp_is_delete.
     */
    {
	struct termios attrs;
	tcgetattr(pty->master_fd, &attrs);
	attrs.c_cc[VERASE] = conf_get_int(conf, CONF_bksp_is_delete)
	    ? '\177' : '\010';
	tcsetattr(pty->master_fd, TCSANOW, &attrs);
    }

#ifndef OMIT_UTMP
    /*
     * Stamp utmp (that is, tell the utmp helper process to do so),
     * or not.
     */
    if (pty_utmp_helper_pipe >= 0) {   /* if it's < 0, we can't anyway */
        if (!conf_get_int(conf, CONF_stamp_utmp)) {
            close(pty_utmp_helper_pipe);   /* just let the child process die */
            pty_utmp_helper_pipe = -1;
        } else {
            char *location = get_x_display(pty->frontend);
            int len = strlen(location)+1, pos = 0;   /* +1 to include NUL */
            while (pos < len) {
                int ret = write(pty_utmp_helper_pipe, location+pos, len - pos);
                if (ret < 0) {
                    perror("pterm: writing to utmp helper process");
                    close(pty_utmp_helper_pipe);   /* arrgh, just give up */
                    pty_utmp_helper_pipe = -1;
                    break;
                }
                pos += ret;
            }
	}
    }
#endif

#ifndef NOT_X_WINDOWS		       /* for Mac OS X native compilation */
    windowid = get_windowid(pty->frontend);
#endif

    /*
     * Fork and execute the command.
     */
    pid = fork();
    if (pid < 0) {
	perror("fork");
	exit(1);
    }

    if (pid == 0) {
	/*
	 * We are the child.
	 */

	slavefd = pty_open_slave(pty);
	if (slavefd < 0) {
	    perror("slave pty: open");
	    _exit(1);
	}

	close(pty->master_fd);
	noncloexec(slavefd);
	dup2(slavefd, 0);
	dup2(slavefd, 1);
	dup2(slavefd, 2);
	close(slavefd);
	setsid();
#ifdef TIOCSCTTY
	ioctl(0, TIOCSCTTY, 1);
#endif
	pgrp = getpid();
	tcsetpgrp(0, pgrp);
	setpgid(pgrp, pgrp);
        {
            int ptyfd = open(pty->name, O_WRONLY, 0);
            if (ptyfd >= 0)
                close(ptyfd);
        }
	setpgid(pgrp, pgrp);
	{
	    char *term_env_var = dupprintf("TERM=%s",
					   conf_get_str(conf, CONF_termtype));
	    putenv(term_env_var);
	    /* We mustn't free term_env_var, as putenv links it into the
	     * environment in place.
	     */
	}
#ifndef NOT_X_WINDOWS		       /* for Mac OS X native compilation */
	{
	    char *windowid_env_var = dupprintf("WINDOWID=%ld", windowid);
	    putenv(windowid_env_var);
	    /* We mustn't free windowid_env_var, as putenv links it into the
	     * environment in place.
	     */
	}
#endif
	{
	    char *key, *val;

	    for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key);
		 val != NULL;
		 val = conf_get_str_strs(conf, CONF_environmt, key, &key)) {
		char *varval = dupcat(key, "=", val, NULL);
		putenv(varval);
		/*
		 * We must not free varval, since putenv links it
		 * into the environment _in place_. Weird, but
		 * there we go. Memory usage will be rationalised
		 * as soon as we exec anyway.
		 */
	    }
	}

	/*
	 * SIGINT, SIGQUIT and SIGPIPE may have been set to ignored by
	 * our parent, particularly by things like sh -c 'pterm &' and
	 * some window or session managers. SIGCHLD, meanwhile, was
	 * blocked during pt_main() startup. Reverse all this for our
	 * child process.
	 */
	putty_signal(SIGINT, SIG_DFL);
	putty_signal(SIGQUIT, SIG_DFL);
	putty_signal(SIGPIPE, SIG_DFL);
	block_signal(SIGCHLD, 0);
	if (pty_argv) {
            /*
             * Exec the exact argument list we were given.
             */
	    execvp(pty_argv[0], pty_argv);
            /*
             * If that fails, and if we had exactly one argument, pass
             * that argument to $SHELL -c.
             *
             * This arranges that we can _either_ follow 'pterm -e'
             * with a list of argv elements to be fed directly to
             * exec, _or_ with a single argument containing a command
             * to be parsed by a shell (but, in cases of doubt, the
             * former is more reliable).
             *
             * A quick survey of other terminal emulators' -e options
             * (as of Debian squeeze) suggests that:
             *
             *  - xterm supports both modes, more or less like this
             *  - gnome-terminal will only accept a one-string shell command
             *  - Eterm, kterm and rxvt will only accept a list of
             *    argv elements (as did older versions of pterm).
             *
             * It therefore seems important to support both usage
             * modes in order to be a drop-in replacement for either
             * xterm or gnome-terminal, and hence for anyone's
             * plausible uses of the Debian-style alias
             * 'x-terminal-emulator'...
             */
            if (pty_argv[1] == NULL) {
                char *shell = getenv("SHELL");
                if (shell)
                    execl(shell, shell, "-c", pty_argv[0], (void *)NULL);
            }
        } else {
	    char *shell = getenv("SHELL");
	    char *shellname;
	    if (conf_get_int(conf, CONF_login_shell)) {
		char *p = strrchr(shell, '/');
		shellname = snewn(2+strlen(shell), char);
		p = p ? p+1 : shell;
		sprintf(shellname, "-%s", p);
	    } else
		shellname = shell;
	    execl(getenv("SHELL"), shellname, (void *)NULL);
	}
Exemplo n.º 4
0
static void pty_open_master(Pty pty)
{
#ifdef BSD_PTYS
    const char chars1[] = "pqrstuvwxyz";
    const char chars2[] = "0123456789abcdef";
    const char *p1, *p2;
    char master_name[20];
    struct group *gp;

    for (p1 = chars1; *p1; p1++)
	for (p2 = chars2; *p2; p2++) {
	    sprintf(master_name, "/dev/pty%c%c", *p1, *p2);
	    pty->master_fd = open(master_name, O_RDWR);
	    if (pty->master_fd >= 0) {
		if (geteuid() == 0 ||
		    access(master_name, R_OK | W_OK) == 0) {
		    /*
		     * We must also check at this point that we are
		     * able to open the slave side of the pty. We
		     * wouldn't want to allocate the wrong master,
		     * get all the way down to forking, and _then_
		     * find we're unable to open the slave.
		     */
		    strcpy(pty->name, master_name);
		    pty->name[5] = 't'; /* /dev/ptyXX -> /dev/ttyXX */

                    cloexec(pty->master_fd);

		    if (pty_open_slave(pty) >= 0 &&
			access(pty->name, R_OK | W_OK) == 0)
			goto got_one;
		    if (pty->slave_fd > 0)
			close(pty->slave_fd);
		    pty->slave_fd = -1;
		}
		close(pty->master_fd);
	    }
	}

    /* If we get here, we couldn't get a tty at all. */
    fprintf(stderr, "pterm: unable to open a pseudo-terminal device\n");
    exit(1);

    got_one:

    /* We need to chown/chmod the /dev/ttyXX device. */
    gp = getgrnam("tty");
    chown(pty->name, getuid(), gp ? gp->gr_gid : -1);
    chmod(pty->name, 0600);
#else

    const int flags = O_RDWR
#ifdef O_NOCTTY
        | O_NOCTTY
#endif
        ;

#ifdef HAVE_POSIX_OPENPT
    pty->master_fd = posix_openpt(flags);

    if (pty->master_fd < 0) {
	perror("posix_openpt");
	exit(1);
    }
#else
    pty->master_fd = open("/dev/ptmx", flags);

    if (pty->master_fd < 0) {
	perror("/dev/ptmx: open");
	exit(1);
    }
#endif

    if (grantpt(pty->master_fd) < 0) {
	perror("grantpt");
	exit(1);
    }
    
    if (unlockpt(pty->master_fd) < 0) {
	perror("unlockpt");
	exit(1);
    }

    cloexec(pty->master_fd);

    pty->name[FILENAME_MAX-1] = '\0';
    strncpy(pty->name, ptsname(pty->master_fd), FILENAME_MAX-1);
#endif

    nonblock(pty->master_fd);

    if (!ptys_by_fd)
	ptys_by_fd = newtree234(pty_compare_by_fd);
    add234(ptys_by_fd, pty);
}
Exemplo n.º 5
0
/*
 * Called to set up the pty.
 * 
 * Returns an error message, or NULL on success.
 *
 * Also places the canonical host name into `realhost'. It must be
 * freed by the caller.
 */
static const char *pty_init(void *frontend, void **backend_handle, Config *cfg,
			    char *host, int port, char **realhost, int nodelay,
			    int keepalive)
{
    int slavefd;
    pid_t pid, pgrp;
#ifndef NOT_X_WINDOWS		       /* for Mac OS X native compilation */
    long windowid;
#endif
    Pty pty;

    if (single_pty) {
	pty = single_pty;
    } else {
	pty = snew(struct pty_tag);
	pty->master_fd = pty->slave_fd = -1;
#ifndef OMIT_UTMP
	pty_stamped_utmp = FALSE;
#endif
    }

    pty->frontend = frontend;
    *backend_handle = NULL;	       /* we can't sensibly use this, sadly */

    pty->cfg = *cfg;		       /* structure copy */
    pty->term_width = cfg->width;
    pty->term_height = cfg->height;

    if (pty->master_fd < 0)
	pty_open_master(pty);

    /*
     * Set the backspace character to be whichever of ^H and ^? is
     * specified by bksp_is_delete.
     */
    {
	struct termios attrs;
	tcgetattr(pty->master_fd, &attrs);
	attrs.c_cc[VERASE] = cfg->bksp_is_delete ? '\177' : '\010';
	tcsetattr(pty->master_fd, TCSANOW, &attrs);
    }

#ifndef OMIT_UTMP
    /*
     * Stamp utmp (that is, tell the utmp helper process to do so),
     * or not.
     */
    if (!cfg->stamp_utmp) {
	close(pty_utmp_helper_pipe);   /* just let the child process die */
	pty_utmp_helper_pipe = -1;
    } else {
	char *location = get_x_display(pty->frontend);
	int len = strlen(location)+1, pos = 0;   /* +1 to include NUL */
	while (pos < len) {
	    int ret = write(pty_utmp_helper_pipe, location+pos, len - pos);
	    if (ret < 0) {
		perror("pterm: writing to utmp helper process");
		close(pty_utmp_helper_pipe);   /* arrgh, just give up */
		pty_utmp_helper_pipe = -1;
		break;
	    }
	    pos += ret;
	}
    }
#endif

#ifndef NOT_X_WINDOWS		       /* for Mac OS X native compilation */
    windowid = get_windowid(pty->frontend);
#endif

    /*
     * Fork and execute the command.
     */
    pid = fork();
    if (pid < 0) {
	perror("fork");
	exit(1);
    }

    if (pid == 0) {
	/*
	 * We are the child.
	 */

	slavefd = pty_open_slave(pty);
	if (slavefd < 0) {
	    perror("slave pty: open");
	    _exit(1);
	}

	close(pty->master_fd);
	fcntl(slavefd, F_SETFD, 0);    /* don't close on exec */
	dup2(slavefd, 0);
	dup2(slavefd, 1);
	dup2(slavefd, 2);
	close(slavefd);
	setsid();
#ifdef TIOCSCTTY
	ioctl(0, TIOCSCTTY, 1);
#endif
	pgrp = getpid();
	tcsetpgrp(0, pgrp);
	setpgid(pgrp, pgrp);
	close(open(pty->name, O_WRONLY, 0));
	setpgid(pgrp, pgrp);
	{
	    char *term_env_var = dupprintf("TERM=%s", cfg->termtype);
	    putenv(term_env_var);
	    /* We mustn't free term_env_var, as putenv links it into the
	     * environment in place.
	     */
	}
#ifndef NOT_X_WINDOWS		       /* for Mac OS X native compilation */
	{
	    char *windowid_env_var = dupprintf("WINDOWID=%ld", windowid);
	    putenv(windowid_env_var);
	    /* We mustn't free windowid_env_var, as putenv links it into the
	     * environment in place.
	     */
	}
#endif
	{
	    char *e = cfg->environmt;
	    char *var, *varend, *val, *varval;
	    while (*e) {
		var = e;
		while (*e && *e != '\t') e++;
		varend = e;
		if (*e == '\t') e++;
		val = e;
		while (*e) e++;
		e++;

		varval = dupprintf("%.*s=%s", varend-var, var, val);
		putenv(varval);
		/*
		 * We must not free varval, since putenv links it
		 * into the environment _in place_. Weird, but
		 * there we go. Memory usage will be rationalised
		 * as soon as we exec anyway.
		 */
	    }
	}

	/*
	 * SIGINT, SIGQUIT and SIGPIPE may have been set to ignored by
	 * our parent, particularly by things like sh -c 'pterm &' and
	 * some window or session managers. SIGCHLD, meanwhile, was
	 * blocked during pt_main() startup. Reverse all this for our
	 * child process.
	 */
	putty_signal(SIGINT, SIG_DFL);
	putty_signal(SIGQUIT, SIG_DFL);
	putty_signal(SIGPIPE, SIG_DFL);
	block_signal(SIGCHLD, 0);
	if (pty_argv)
	    execvp(pty_argv[0], pty_argv);
	else {
	    char *shell = getenv("SHELL");
	    char *shellname;
	    if (cfg->login_shell) {
		char *p = strrchr(shell, '/');
		shellname = snewn(2+strlen(shell), char);
		p = p ? p+1 : shell;
		sprintf(shellname, "-%s", p);
	    } else
Exemplo n.º 6
0
void
init_subshell (void)
{
    /* This must be remembered across calls to init_subshell() */
    static char pty_name[BUF_SMALL];
    /* Must be considerably longer than BUF_SMALL (128) to support fancy shell prompts */
    char precmd[BUF_MEDIUM];

    switch (check_sid ())
    {
    case 1:
        mc_global.tty.use_subshell = FALSE;
        return;
    case 2:
        mc_global.tty.use_subshell = FALSE;
        mc_global.midnight_shutdown = TRUE;
        return;
    default:
        break;
    }

    /* 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 ();

    if (mc_global.tty.subshell_pty == 0)
    {                           /* First time through */
        if (mc_global.shell->type == SHELL_NONE)
            return;

        /* Open a pty for talking to the subshell */

        /* FIXME: We may need to open a fresh pty each time on SVR4 */

        mc_global.tty.subshell_pty = pty_open_master (pty_name);
        if (mc_global.tty.subshell_pty == -1)
        {
            fprintf (stderr, "Cannot open master side of pty: %s\r\n", unix_error_string (errno));
            mc_global.tty.use_subshell = FALSE;
            return;
        }
        subshell_pty_slave = pty_open_slave (pty_name);
        if (subshell_pty_slave == -1)
        {
            fprintf (stderr, "Cannot open slave side of pty %s: %s\r\n",
                     pty_name, unix_error_string (errno));
            mc_global.tty.use_subshell = FALSE;
            return;
        }

        /* Create a pipe for receiving the subshell's CWD */

        if (mc_global.shell->type == SHELL_TCSH)
        {
            g_snprintf (tcsh_fifo, sizeof (tcsh_fifo), "%s/mc.pipe.%d",
                        mc_tmpdir (), (int) getpid ());
            if (mkfifo (tcsh_fifo, 0600) == -1)
            {
                fprintf (stderr, "mkfifo(%s) failed: %s\r\n", tcsh_fifo, unix_error_string (errno));
                mc_global.tty.use_subshell = FALSE;
                return;
            }

            /* Opening the FIFO as O_RDONLY or O_WRONLY causes deadlock */

            if ((subshell_pipe[READ] = open (tcsh_fifo, O_RDWR)) == -1
                || (subshell_pipe[WRITE] = open (tcsh_fifo, O_RDWR)) == -1)
            {
                fprintf (stderr, _("Cannot open named pipe %s\n"), tcsh_fifo);
                perror (__FILE__ ": open");
                mc_global.tty.use_subshell = FALSE;
                return;
            }
        }
        else if (pipe (subshell_pipe))  /* subshell_type is BASH, ASH_BUSYBOX, DASH or ZSH */
        {
            perror (__FILE__ ": couldn't create pipe");
            mc_global.tty.use_subshell = FALSE;
            return;
        }
    }

    /* Fork the subshell */

    subshell_alive = TRUE;
    subshell_stopped = FALSE;
    subshell_pid = fork ();

    if (subshell_pid == -1)
    {
        fprintf (stderr, "Cannot spawn the subshell process: %s\r\n", unix_error_string (errno));
        /* We exit here because, if the process table is full, the */
        /* other method of running user commands won't work either */
        exit (EXIT_FAILURE);
    }

    if (subshell_pid == 0)
    {
        /* We are in the child process */
        init_subshell_child (pty_name);
    }

    init_subshell_precmd (precmd, BUF_MEDIUM);

    write_all (mc_global.tty.subshell_pty, precmd, strlen (precmd));

    /* Wait until the subshell has started up and processed the command */

    subshell_state = RUNNING_COMMAND;
    tty_enable_interrupt_key ();
    if (!feed_subshell (QUIETLY, TRUE))
    {
        mc_global.tty.use_subshell = FALSE;
    }
    tty_disable_interrupt_key ();
    if (!subshell_alive)
        mc_global.tty.use_subshell = FALSE;     /* Subshell died instantly, so don't use it */
}