Beispiel #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, 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);
	}
Beispiel #2
0
Socket platform_new_connection(SockAddr addr, char *hostname,
			       int port, int privport,
			       int oobinline, int nodelay, int keepalive,
			       Plug plug, Conf *conf)
{
    char *cmd;

    static const struct socket_function_table socket_fn_table = {
	sk_localproxy_plug,
	sk_localproxy_close,
	sk_localproxy_write,
	sk_localproxy_write_oob,
	sk_localproxy_write_eof,
	sk_localproxy_flush,
	sk_localproxy_set_private_ptr,
	sk_localproxy_get_private_ptr,
	sk_localproxy_set_frozen,
	sk_localproxy_socket_error
    };

    Local_Proxy_Socket ret;
    int to_cmd_pipe[2], from_cmd_pipe[2], pid;

    if (conf_get_int(conf, CONF_proxy_type) != PROXY_CMD)
	return NULL;

    cmd = format_telnet_command(addr, port, conf);

    ret = snew(struct Socket_localproxy_tag);
    ret->fn = &socket_fn_table;
    ret->plug = plug;
    ret->error = NULL;
    ret->outgoingeof = EOF_NO;

    bufchain_init(&ret->pending_input_data);
    bufchain_init(&ret->pending_output_data);

    /*
     * Create the pipes to the proxy command, and spawn the proxy
     * command process.
     */
    if (pipe(to_cmd_pipe) < 0 ||
	pipe(from_cmd_pipe) < 0) {
	ret->error = dupprintf("pipe: %s", strerror(errno));
        sfree(cmd);
	return (Socket)ret;
    }
    cloexec(to_cmd_pipe[1]);
    cloexec(from_cmd_pipe[0]);

    pid = fork();

    if (pid < 0) {
	ret->error = dupprintf("fork: %s", strerror(errno));
        sfree(cmd);
	return (Socket)ret;
    } else if (pid == 0) {
	close(0);
	close(1);
	dup2(to_cmd_pipe[0], 0);
	dup2(from_cmd_pipe[1], 1);
	close(to_cmd_pipe[0]);
	close(from_cmd_pipe[1]);
	noncloexec(0);
	noncloexec(1);
	execl("/bin/sh", "sh", "-c", cmd, (void *)NULL);
	_exit(255);
    }

    sfree(cmd);

    close(to_cmd_pipe[0]);
    close(from_cmd_pipe[1]);

    ret->to_cmd = to_cmd_pipe[1];
    ret->from_cmd = from_cmd_pipe[0];

    if (!localproxy_by_fromfd)
	localproxy_by_fromfd = newtree234(localproxy_fromfd_cmp);
    if (!localproxy_by_tofd)
	localproxy_by_tofd = newtree234(localproxy_tofd_cmp);

    add234(localproxy_by_fromfd, ret);
    add234(localproxy_by_tofd, ret);

    uxsel_set(ret->from_cmd, 1, localproxy_select_result);

    /* We are responsible for this and don't need it any more */
    sk_addr_free(addr);

    return (Socket) ret;
}
Beispiel #3
0
Socket platform_new_connection(SockAddr addr, const char *hostname,
			       int port, int privport,
			       int oobinline, int nodelay, int keepalive,
			       Plug plug, Conf *conf)
{
    char *cmd;

    static const struct socket_function_table socket_fn_table = {
	sk_localproxy_plug,
	sk_localproxy_close,
	sk_localproxy_write,
	sk_localproxy_write_oob,
	sk_localproxy_write_eof,
	sk_localproxy_flush,
	sk_localproxy_set_frozen,
	sk_localproxy_socket_error,
        NULL, /* peer_info */
    };

    Local_Proxy_Socket ret;
    int to_cmd_pipe[2], from_cmd_pipe[2], cmd_err_pipe[2], pid, proxytype;

    proxytype = conf_get_int(conf, CONF_proxy_type);
    if (proxytype != PROXY_CMD && proxytype != PROXY_FUZZ)
	return NULL;

    ret = snew(struct Socket_localproxy_tag);
    ret->fn = &socket_fn_table;
    ret->plug = plug;
    ret->error = NULL;
    ret->outgoingeof = EOF_NO;

    bufchain_init(&ret->pending_input_data);
    bufchain_init(&ret->pending_output_data);
    bufchain_init(&ret->pending_error_data);

    if (proxytype == PROXY_CMD) {
	cmd = format_telnet_command(addr, port, conf);

        if (flags & FLAG_STDERR) {
            /* If we have a sensible stderr, the proxy command can
             * send its own standard error there, so we won't
             * interfere. */
            cmd_err_pipe[0] = cmd_err_pipe[1] = -1;
        } else {
            /* If we don't have a sensible stderr, we should catch the
             * proxy command's standard error to put in our event
             * log. */
            cmd_err_pipe[0] = cmd_err_pipe[1] = 0;
        }

        {
            char *logmsg = dupprintf("Starting local proxy command: %s", cmd);
            plug_log(plug, 2, NULL, 0, logmsg, 0);
            sfree(logmsg);
        }

	/*
	 * Create the pipes to the proxy command, and spawn the proxy
	 * command process.
	 */
	if (pipe(to_cmd_pipe) < 0 ||
	    pipe(from_cmd_pipe) < 0 ||
            (cmd_err_pipe[0] == 0 && pipe(cmd_err_pipe) < 0)) {
	    ret->error = dupprintf("pipe: %s", strerror(errno));
	    sfree(cmd);
	    return (Socket)ret;
	}
	cloexec(to_cmd_pipe[1]);
	cloexec(from_cmd_pipe[0]);
	if (cmd_err_pipe[0] >= 0)
            cloexec(cmd_err_pipe[0]);

	pid = fork();

	if (pid < 0) {
	    ret->error = dupprintf("fork: %s", strerror(errno));
	    sfree(cmd);
	    return (Socket)ret;
	} else if (pid == 0) {
	    close(0);
	    close(1);
	    dup2(to_cmd_pipe[0], 0);
	    dup2(from_cmd_pipe[1], 1);
	    close(to_cmd_pipe[0]);
	    close(from_cmd_pipe[1]);
	    if (cmd_err_pipe[0] >= 0) {
                dup2(cmd_err_pipe[1], 2);
                close(cmd_err_pipe[1]);
            }
	    noncloexec(0);
	    noncloexec(1);
	    execl("/bin/sh", "sh", "-c", cmd, (void *)NULL);
	    _exit(255);
	}

	sfree(cmd);

	close(to_cmd_pipe[0]);
	close(from_cmd_pipe[1]);
        if (cmd_err_pipe[0] >= 0)
            close(cmd_err_pipe[1]);

	ret->to_cmd = to_cmd_pipe[1];
	ret->from_cmd = from_cmd_pipe[0];
	ret->cmd_err = cmd_err_pipe[0];
    } else {
	cmd = format_telnet_command(addr, port, conf);
	ret->to_cmd = open("/dev/null", O_WRONLY);
	if (ret->to_cmd == -1) {
	    ret->error = dupprintf("/dev/null: %s", strerror(errno));
	    sfree(cmd);
	    return (Socket)ret;
	}
	ret->from_cmd = open(cmd, O_RDONLY);
	if (ret->from_cmd == -1) {
	    ret->error = dupprintf("%s: %s", cmd, strerror(errno));
	    sfree(cmd);
	    return (Socket)ret;
	}
	sfree(cmd);
	ret->cmd_err = -1;
    }

    if (!localproxy_by_fromfd)
	localproxy_by_fromfd = newtree234(localproxy_fromfd_cmp);
    if (!localproxy_by_tofd)
	localproxy_by_tofd = newtree234(localproxy_tofd_cmp);
    if (!localproxy_by_errfd)
	localproxy_by_errfd = newtree234(localproxy_errfd_cmp);

    add234(localproxy_by_fromfd, ret);
    add234(localproxy_by_tofd, ret);
    if (ret->cmd_err >= 0)
        add234(localproxy_by_errfd, ret);

    uxsel_set(ret->from_cmd, 1, localproxy_select_result);
    if (ret->cmd_err >= 0)
        uxsel_set(ret->cmd_err, 1, localproxy_select_result);

    /* We are responsible for this and don't need it any more */
    sk_addr_free(addr);

    return (Socket) ret;
}