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

📄 pty.c

📁 远程登陆工具软件源码 用于远程登陆unix
💻 C
📖 第 1 页 / 共 2 页
字号:
	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 (fd == pty_signal_pipe[0]) {
	pid_t pid;
	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);
	    if (pid == pty_child_pid &&
		(WIFEXITED(status) || WIFSIGNALED(status))) {
		/*
		 * The primary child process died. We could keep
		 * the terminal open for remaining subprocesses to
		 * output to, but conventional wisdom seems to feel
		 * that that's the Wrong Thing for an xterm-alike,
		 * so we bail out now (though we don't necessarily
		 * _close_ the window, depending on the state of
		 * Close On Exit). This would be easy enough to
		 * change or make configurable if necessary.
		 */
		pty_exit_code = status;
		pty_child_dead = TRUE;
		finished = TRUE;
	    }
	} while(pid > 0);
    }

    if (finished && !pty_finished) {
	uxsel_del(pty_master_fd);
	pty_close();
	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));
	}
    }
    return !finished;
}

static void pty_uxsel_setup(void)
{
    uxsel_set(pty_master_fd, 1, pty_select_result);
    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;
    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
		shellname = shell;
	    execl(getenv("SHELL"), shellname, 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 (pipe(pty_signal_pipe) < 0) {
	perror("pipe");
	exit(1);
    }
    pty_uxsel_setup();

    return NULL;
}

static void pty_reconfig(void *handle, Config *cfg)
{
    /*
     * 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)
{
}

/*
 * Called to send data down the pty.
 */
static int pty_send(void *handle, char *buf, int len)
{
    if (pty_master_fd < 0)
	return 0;		       /* ignore all writes if fd closed */

    while (len > 0) {
	int ret = write(pty_master_fd, buf, len);
	if (ret < 0) {
	    perror("write pty master");
	    exit(1);
	}
	buf += ret;
	len -= ret;
    }
    return 0;
}

static void pty_close(void)
{
    if (pty_master_fd >= 0) {
	close(pty_master_fd);
	pty_master_fd = -1;
    }
    if (pty_utmp_helper_pipe >= 0) {
	close(pty_utmp_helper_pipe);   /* this causes utmp to be cleaned up */
	pty_utmp_helper_pipe = -1;
    }
}

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

/*
 * Called to set the size of the window
 */
static void pty_size(void *handle, int width, int height)
{
    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)
{
    /* 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)
{
    /*
     * 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 Socket pty_socket(void *handle)
{
    return NULL;		       /* shouldn't ever be needed */
}

static int pty_sendok(void *handle)
{
    return 1;
}

static void pty_unthrottle(void *handle, int backlog)
{
    /* do nothing */
}

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

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

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

static int pty_exitcode(void *handle)
{
    if (!pty_finished)
	return -1;		       /* not dead yet */
    else
	return pty_exit_code;
}

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

⌨️ 快捷键说明

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