Exemple #1
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 slavefd;
    pid_t pid, pgrp;
    long windowid;

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

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

    /*
     * 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;
	}
    }

    windowid = get_windowid(pty_frontend);

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

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

	slavefd = open(pty_name, O_RDWR);
	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);
	setsid();
	ioctl(slavefd, TIOCSCTTY, 1);
	pgrp = getpid();
	tcsetpgrp(slavefd, pgrp);
	setpgrp();
	close(open(pty_name, O_WRONLY, 0));
	setpgrp();
	/* Close everything _else_, for tidiness. */
	for (i = 3; i < 1024; i++)
	    close(i);
	{
	    char term_env_var[10 + sizeof(cfg->termtype)];
	    sprintf(term_env_var, "TERM=%s", cfg->termtype);
	    putenv(term_env_var);
	}
	{
	    char windowid_env_var[40];
	    sprintf(windowid_env_var, "WINDOWID=%ld", windowid);
	    putenv(windowid_env_var);
	}
	/*
	 * SIGINT and SIGQUIT may have been set to ignored by our
	 * parent, particularly by things like sh -c 'pterm &' and
	 * some window managers. Reverse this for our child process.
	 */
	putty_signal(SIGINT, SIG_DFL);
	putty_signal(SIGQUIT, SIG_DFL);
	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
Exemple #2
0
void
init_subshell (void)
{
    /* This must be remembered across calls to init_subshell() */
    static char pty_name[BUF_SMALL];
    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 */
        /* 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=${PROMPT_COMMAND:+$PROMPT_COMMAND\n}'pwd>&%d;kill -STOP $$'\n",
                    subshell_pipe[WRITE]);
        break;

    case ZSH:
        g_snprintf (precmd, sizeof (precmd),
                    " _mc_precmd(){ pwd>&%d;kill -STOP $$ }; precmd_functions+=(_mc_precmd)\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:
        /* Use fish_prompt_mc function for prompt, if not present then copy fish_prompt to it. */
        g_snprintf (precmd, sizeof (precmd),
                    "if not functions -q fish_prompt_mc;"
                    "functions -c fish_prompt fish_prompt_mc; end;"
                    "function fish_prompt; echo $PWD>&%d; fish_prompt_mc; kill -STOP %%self; end\n",
                    subshell_pipe[WRITE]);
        break;

    default:
        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 */
}
Exemple #3
0
/*
 * Pre-initialisation. This is here to get around the fact that GTK
 * doesn't like being run in setuid/setgid programs (probably
 * sensibly). So before we initialise GTK - and therefore before we
 * even process the command line - we check to see if we're running
 * set[ug]id. If so, we open our pty master _now_, chown it as
 * necessary, and drop privileges. We can always close it again
 * later. If we're potentially going to be doing utmp as well, we
 * also fork off a utmp helper process and communicate with it by
 * means of a pipe; the utmp helper will keep privileges in order
 * to clean up utmp when we exit (i.e. when its end of our pipe
 * closes).
 */
void pty_pre_init(void)
{
    pid_t pid;
    int pipefd[2];

    /* set the child signal handler straight away; it needs to be set
     * before we ever fork. */
    putty_signal(SIGCHLD, sigchld_handler);
    pty_master_fd = -1;

    if (geteuid() != getuid() || getegid() != getgid()) {
	pty_open_master();
    }

#ifndef OMIT_UTMP
    /*
     * Fork off the utmp helper.
     */
    if (pipe(pipefd) < 0) {
	perror("pterm: pipe");
	exit(1);
    }
    pid = fork();
    if (pid < 0) {
	perror("pterm: fork");
	exit(1);
    } else if (pid == 0) {
	char display[128], buffer[128];
	int dlen, ret;

	close(pipefd[1]);
	/*
	 * Now sit here until we receive a display name from the
	 * other end of the pipe, and then stamp utmp. Unstamp utmp
	 * again, and exit, when the pipe closes.
	 */

	dlen = 0;
	while (1) {
	    
	    ret = read(pipefd[0], buffer, lenof(buffer));
	    if (ret <= 0) {
		cleanup_utmp();
		_exit(0);
	    } else if (!pty_stamped_utmp) {
		if (dlen < lenof(display))
		    memcpy(display+dlen, buffer,
			   min(ret, lenof(display)-dlen));
		if (buffer[ret-1] == '\0') {
		    /*
		     * Now we have a display name. NUL-terminate
		     * it, and stamp utmp.
		     */
		    display[lenof(display)-1] = '\0';
		    /*
		     * Trap as many fatal signals as we can in the
		     * hope of having the best possible chance to
		     * clean up utmp before termination. We are
		     * unfortunately unprotected against SIGKILL,
		     * but that's life.
		     */
		    putty_signal(SIGHUP, fatal_sig_handler);
		    putty_signal(SIGINT, fatal_sig_handler);
		    putty_signal(SIGQUIT, fatal_sig_handler);
		    putty_signal(SIGILL, fatal_sig_handler);
		    putty_signal(SIGABRT, fatal_sig_handler);
		    putty_signal(SIGFPE, fatal_sig_handler);
		    putty_signal(SIGPIPE, fatal_sig_handler);
		    putty_signal(SIGALRM, fatal_sig_handler);
		    putty_signal(SIGTERM, fatal_sig_handler);
		    putty_signal(SIGSEGV, fatal_sig_handler);
		    putty_signal(SIGUSR1, fatal_sig_handler);
		    putty_signal(SIGUSR2, fatal_sig_handler);
#ifdef SIGBUS
		    putty_signal(SIGBUS, fatal_sig_handler);
#endif
#ifdef SIGPOLL
		    putty_signal(SIGPOLL, fatal_sig_handler);
#endif
#ifdef SIGPROF
		    putty_signal(SIGPROF, fatal_sig_handler);
#endif
#ifdef SIGSYS
		    putty_signal(SIGSYS, fatal_sig_handler);
#endif
#ifdef SIGTRAP
		    putty_signal(SIGTRAP, fatal_sig_handler);
#endif
#ifdef SIGVTALRM
		    putty_signal(SIGVTALRM, fatal_sig_handler);
#endif
#ifdef SIGXCPU
		    putty_signal(SIGXCPU, fatal_sig_handler);
#endif
#ifdef SIGXFSZ
		    putty_signal(SIGXFSZ, fatal_sig_handler);
#endif
#ifdef SIGIO
		    putty_signal(SIGIO, fatal_sig_handler);
#endif
		    setup_utmp(pty_name, display);
		}
	    }
	}
    } else {
	close(pipefd[0]);
	pty_utmp_helper_pid = pid;
	pty_utmp_helper_pipe = pipefd[1];
    }
#endif

    /* Drop privs. */
    {
	int gid = getgid(), uid = getuid();
#ifndef HAVE_NO_SETRESUID
	int setresgid(gid_t, gid_t, gid_t);
	int setresuid(uid_t, uid_t, uid_t);
	setresgid(gid, gid, gid);
	setresuid(uid, uid, uid);
#else
	setgid(getgid());
	setuid(getuid());
#endif
    }
}