⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 uxpty.c

📁 putty
💻 C
📖 第 1 页 / 共 2 页
字号:
	    pty->child_dead = TRUE;
	    del234(ptys_by_pid, pty);
	    finished = TRUE;
	}
    } else {
	if (event == 1) {

	    ret = read(pty->master_fd, buf, sizeof(buf));

	    /*
	     * Clean termination condition is that either ret == 0, or ret
	     * < 0 and errno == EIO. Not sure why the latter, but it seems
	     * to happen. Boo.
	     */
	    if (ret == 0 || (ret < 0 && errno == EIO)) {
		/*
		 * We assume a clean exit if the pty has closed but the
		 * actual child process hasn't. The only way I can
		 * imagine this happening is if it detaches itself from
		 * the pty and goes daemonic - in which case the
		 * expected usage model would precisely _not_ be for
		 * the pterm window to hang around!
		 */
		finished = TRUE;
		if (!pty->child_dead)
		    pty->exit_code = 0;
	    } else if (ret < 0) {
		perror("read pty master");
		exit(1);
	    } else if (ret > 0) {
		from_backend(pty->frontend, 0, buf, ret);
	    }
	} else if (event == 2) {
            /*
             * Attempt to send data down the pty.
             */
            pty_try_write(pty);
        }
    }

    if (finished && !pty->finished) {
	uxsel_del(pty->master_fd);
	pty_close(pty);
	pty->master_fd = -1;

	pty->finished = TRUE;

	/*
	 * This is a slight layering-violation sort of hack: only
	 * if we're not closing on exit (COE is set to Never, or to
	 * Only On Clean and it wasn't a clean exit) do we output a
	 * `terminated' message.
	 */
	if (pty->cfg.close_on_exit == FORCE_OFF ||
	    (pty->cfg.close_on_exit == AUTO && pty->exit_code != 0)) {
	    char message[512];
	    if (WIFEXITED(pty->exit_code))
		sprintf(message, "\r\n[pterm: process terminated with exit"
			" code %d]\r\n", WEXITSTATUS(pty->exit_code));
	    else if (WIFSIGNALED(pty->exit_code))
#ifdef HAVE_NO_STRSIGNAL
		sprintf(message, "\r\n[pterm: process terminated on signal"
			" %d]\r\n", WTERMSIG(pty->exit_code));
#else
		sprintf(message, "\r\n[pterm: process terminated on signal"
			" %d (%.400s)]\r\n", WTERMSIG(pty->exit_code),
			strsignal(WTERMSIG(pty->exit_code)));
#endif
	    from_backend(pty->frontend, 0, message, strlen(message));
	}

	notify_remote_exit(pty->frontend);
    }

    return !finished;
}

int pty_select_result(int fd, int event)
{
    int ret = TRUE;
    Pty pty;

    if (fd == pty_signal_pipe[0]) {
	pid_t pid;
	int ipid;
	int status;
	char c[1];

	read(pty_signal_pipe[0], c, 1); /* ignore its value; it'll be `x' */

	do {
	    pid = waitpid(-1, &status, WNOHANG);

	    ipid = pid;
	    pty = find234(ptys_by_pid, &pid, pty_find_by_pid);

	    if (pty)
		ret = ret && pty_real_select_result(pty, -1, status);
	} while (pid > 0);
    } else {
	pty = find234(ptys_by_fd, &fd, pty_find_by_fd);

	if (pty)
	    ret = ret && pty_real_select_result(pty, event, 0);
    }

    return ret;
}

static void pty_uxsel_setup(Pty pty)
{
    int rwx;

    rwx = 1;                           /* always want to read from pty */
    if (bufchain_size(&pty->output_data))
        rwx |= 2;                      /* might also want to write to it */
    uxsel_set(pty->master_fd, rwx, pty_select_result);

    /*
     * In principle this only needs calling once for all pty
     * backend instances, but it's simplest just to call it every
     * time; uxsel won't mind.
     */
    uxsel_set(pty_signal_pipe[0], 1, pty_select_result);
}

/*
 * 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 and SIGQUIT may have been set to ignored by our
	 * parent, particularly by things like sh -c 'pterm &' and
	 * some window 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);
	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
		shellname = shell;
	    execl(getenv("SHELL"), shellname, (void *)NULL);
	}

	/*
	 * If we're here, exec has gone badly foom.
	 */
	perror("exec");
	_exit(127);
    } else {
	pty->child_pid = pid;
	pty->child_dead = FALSE;
	pty->finished = FALSE;
	if (pty->slave_fd > 0)
	    close(pty->slave_fd);
	if (!ptys_by_pid)
	    ptys_by_pid = newtree234(pty_compare_by_pid);
	add234(ptys_by_pid, pty);
    }

    if (pty_signal_pipe[0] < 0) {
	if (pipe(pty_signal_pipe) < 0) {
	    perror("pipe");
	    exit(1);
	}
	cloexec(pty_signal_pipe[0]);
	cloexec(pty_signal_pipe[1]);
    }
    pty_uxsel_setup(pty);

    *backend_handle = pty;

    *realhost = dupprintf("\0");

    return NULL;
}

static void pty_reconfig(void *handle, Config *cfg)
{
    Pty pty = (Pty)handle;
    /*
     * We don't have much need to reconfigure this backend, but
     * unfortunately we do need to pick up the setting of Close On
     * Exit so we know whether to give a `terminated' message.
     */
    pty->cfg = *cfg;		       /* structure copy */
}

/*
 * Stub routine (never called in pterm).
 */
static void pty_free(void *handle)
{
    Pty pty = (Pty)handle;

    /* Either of these may fail `not found'. That's fine with us. */
    del234(ptys_by_pid, pty);
    del234(ptys_by_fd, pty);

    sfree(pty);
}

static void pty_try_write(Pty pty)
{
    void *data;
    int len, ret;

    assert(pty->master_fd >= 0);

    while (bufchain_size(&pty->output_data) > 0) {
        bufchain_prefix(&pty->output_data, &data, &len);
	ret = write(pty->master_fd, data, len);

        if (ret < 0 && (errno == EWOULDBLOCK)) {
            /*
             * We've sent all we can for the moment.
             */
            break;
        }
	if (ret < 0) {
	    perror("write pty master");
	    exit(1);
	}
	bufchain_consume(&pty->output_data, ret);
    }

    pty_uxsel_setup(pty);
}

/*
 * Called to send data down the pty.
 */
static int pty_send(void *handle, char *buf, int len)
{
    Pty pty = (Pty)handle;

    if (pty->master_fd < 0)
	return 0;                      /* ignore all writes if fd closed */

    bufchain_add(&pty->output_data, buf, len);
    pty_try_write(pty);

    return bufchain_size(&pty->output_data);
}

static void pty_close(Pty pty)
{
    if (pty->master_fd >= 0) {
	close(pty->master_fd);
	pty->master_fd = -1;
    }
#ifndef OMIT_UTMP
    if (pty_utmp_helper_pipe >= 0) {
	close(pty_utmp_helper_pipe);   /* this causes utmp to be cleaned up */
	pty_utmp_helper_pipe = -1;
    }
#endif
}

/*
 * Called to query the current socket sendability status.
 */
static int pty_sendbuffer(void *handle)
{
    /* Pty pty = (Pty)handle; */
    return 0;
}

/*
 * Called to set the size of the window
 */
static void pty_size(void *handle, int width, int height)
{
    Pty pty = (Pty)handle;
    struct winsize size;

    pty->term_width = width;
    pty->term_height = height;

    size.ws_row = (unsigned short)pty->term_height;
    size.ws_col = (unsigned short)pty->term_width;
    size.ws_xpixel = (unsigned short) pty->term_width *
	font_dimension(pty->frontend, 0);
    size.ws_ypixel = (unsigned short) pty->term_height *
	font_dimension(pty->frontend, 1);
    ioctl(pty->master_fd, TIOCSWINSZ, (void *)&size);
    return;
}

/*
 * Send special codes.
 */
static void pty_special(void *handle, Telnet_Special code)
{
    /* Pty pty = (Pty)handle; */
    /* Do nothing! */
    return;
}

/*
 * Return a list of the special codes that make sense in this
 * protocol.
 */
static const struct telnet_special *pty_get_specials(void *handle)
{
    /* Pty pty = (Pty)handle; */
    /*
     * Hmm. When I get round to having this actually usable, it
     * might be quite nice to have the ability to deliver a few
     * well chosen signals to the child process - SIGINT, SIGTERM,
     * SIGKILL at least.
     */
    return NULL;
}

static int pty_connected(void *handle)
{
    /* Pty pty = (Pty)handle; */
    return TRUE;
}

static int pty_sendok(void *handle)
{
    /* Pty pty = (Pty)handle; */
    return 1;
}

static void pty_unthrottle(void *handle, int backlog)
{
    /* Pty pty = (Pty)handle; */
    /* do nothing */
}

static int pty_ldisc(void *handle, int option)
{
    /* Pty pty = (Pty)handle; */
    return 0;			       /* neither editing nor echoing */
}

static void pty_provide_ldisc(void *handle, void *ldisc)
{
    /* Pty pty = (Pty)handle; */
    /* This is a stub. */
}

static void pty_provide_logctx(void *handle, void *logctx)
{
    /* Pty pty = (Pty)handle; */
    /* This is a stub. */
}

static int pty_exitcode(void *handle)
{
    Pty pty = (Pty)handle;
    if (!pty->finished)
	return -1;		       /* not dead yet */
    else
	return pty->exit_code;
}

static int pty_cfg_info(void *handle)
{
    /* Pty pty = (Pty)handle; */
    return 0;
}

Backend pty_backend = {
    pty_init,
    pty_free,
    pty_reconfig,
    pty_send,
    pty_sendbuffer,
    pty_size,
    pty_special,
    pty_get_specials,
    pty_connected,
    pty_exitcode,
    pty_sendok,
    pty_ldisc,
    pty_provide_ldisc,
    pty_provide_logctx,
    pty_unthrottle,
    pty_cfg_info,
    1
};

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -