libslack(pseudo) - pseudo terminal module
#include <slack/std.h> #include <slack/pseudo.h> int pty_open(int *masterfd, int *slavefd, char *slavename, size_t slavenamesize, const struct termios *slave_termios, const struct winsize *slave_winsize); int pty_release(const char *slavename); int pty_set_owner(const char *slavename, uid_t uid); int pty_make_controlling_tty(int *slavefd, const char *slavename); int pty_change_window_size(int masterfd, int row, int col, int xpixel, int ypixel); pid_t pty_fork(int *masterfd, char *slavename, size_t slavenamesize, const struct termios *slave_termios, const struct winsize *slave_winsize);
This module provides functions for opening pseudo terminals, changing their ownership, making them the controlling terminal, changing their window size and forking a new process whose standard input, output and error and attached to a pseudo terminal which is made the controlling terminal.
int pty_open(int *masterfd, int *slavefd, char *slavename, size_t slavenamesize, const struct termios *slave_termios, const struct winsize *slave_winsize)
A safe version of openpty(3). Allocates and opens a pseudo terminal. The
new descriptor for the master side of the pseudo terminal is stored in
*masterfd
. The new descriptor for the slave side of the pseudo terminal
is stored in *slavefd
. The device name of the slave side of the pseudo
terminal is stored in the buffer pointed to by slavename
which must be
able to hold at least 64 characters. slavenamesize
is the size of the
buffer pointed to by slavename
. No more than slavenamesize
bytes will
be written into the buffer pointed to by slavename
, including the
terminating nul
byte. If slave_termios
is not null, it is passed to
tcsetattr(3) with the command TCSANOW
to set the terminal attributes
of the slave device. If slave_winsize
is not null, it is passed to
ioctl(2) with the command TIOCSWINSZ
to set the window size of the
slave device. On success, returns 0
. On error, returns -1
with
errno
set appropriately.
int pty_release(const char *slavename)
Releases the slave tty device whose name is in slavename
. Its ownership
is returned to root, and its permissions set to rw-rw-rw-
. Note that only
root can execute this function successfully on most systems. On success,
returns 0
. On error, returns -1
with errno
set appropriately.
int pty_set_owner(const char *slavename, uid_t uid)
Changes the ownership of the slave pty device referred to by slavename
to
the user id, uid
. Group ownership of the slave pty device will be changed
to the tty
group if it exists. Otherwise, it will be changed to the given
user's primary group. The slave pty device's permissions are set to
rw--w----
. Note that only root can execute this function successfully on
most systems. Also note that the ownership of the device is automatically
set to the real uid of the process by pty_open(3) and pty_fork(3). The
permissions are also set automatically by these functions. So
pty_set_owner(3) is only needed when the device needs to be owned by some
user other than the real user. On success, returns 0
. On error, returns
-1
with errno
set appropriately.
int pty_make_controlling_tty(int *slavefd, const char *slavename)
Makes the slave pty the controlling terminal. *slavefd
contains the
descriptor for the slave side of a pseudo terminal. The descriptor of the
resulting controlling terminal will be stored in *slavefd
. slavename
is the device name of the slave side of the pseudo terminal. On success,
returns 0
. On error, returns -1
with errno
set appropriately.
int pty_change_window_size(int masterfd, int row, int col, int xpixel, int ypixel)
Changes the window size associated with the pseudo terminal referred to by
masterfd
. The row
, col
, xpixel
and ypixel
specify the new
window size. On success, returns 0
. On error, returns -1
with errno
set appropriately.
pid_t pty_fork(int *masterfd, char *slavename, size_t slavenamesize, const struct termios *slave_termios, const struct winsize *slave_winsize)
A safe version of forkpty(3). Creates a pseudo terminal and then calls
fork(2). In the parent process, the slave side of the pseudo terminal is
closed. In the child process, the master side of the pseudo terminal is
closed and the slave side is made the controlling terminal. It is duplicated
onto standard input, output and error and then closed. The master side of
the pseudo terminal is stored in *masterfd
for the parent process. The
device name of the slave side of the pseudo terminal is stored in the buffer
pointed to by slavename
which must be able to hold at least 64 bytes.
slavenamesize
is the size of the buffer pointed to by slavename
. No
more than slavenamesize
bytes will be written to slavename
, including
the terminating nul
byte. If slave_termios
is not null, it is passed
to tcsetattr(3) with the command TCSANOW
to set the terminal
attributes of the slave device. If slave_winsize
is not null, it is
passed to ioctl(2) with the command TIOCSWINSZ
to set the window size
of the slave device. On success, returns 0
to the child process and
returns the process id of the child process to the parent process. On error,
returns -1
with errno
set appropriately.
Additional errors may be generated and returned from the underlying system calls. See their manual pages.
EINVAL
Invalid arguments were passed to one of the functions.
ENOTTY
openpty(3) or open("/dev/ptc") returned a slave descriptor for which ttyname(3) failed to return the slave device name. open("/dev/ptmx") returned a master descriptor for which ptsname(3) failed to return the slave device name.
ENOENT
The old BSD-style pty device search failed to locate an available pseudo terminal.
ENOSPC
The device name of the slave side of the pseudo terminal was too large to
fit in the slavename
buffer passed to pty_open(3) or pty_fork(3).
ENXIO
pty_make_controlling_tty(3) failed to disconnect from the controlling terminal.
MT-Safe if and only if ttyname_r(3) or ptsname_r(3) are available when
needed. On systems that have openpty(3) or "/dev/ptc"
, ttyname_r(3)
is required, otherwise the unsafe ttyname(3) will be used. On systems
that have "/dev/ptmx"
, ptsname_r(3) is required, otherwise the unsafe
ptsname(3) will be used. On systems that have _getpty(2),
pty_open(3) is unsafe because _getpty(2) is unsafe. In short, it's
MT-Safe under Linux, Unsafe under Solaris and OpenBSD.
A very simple pty program:
#include <slack/std.h> #include <slack/pseudo.h> #include <slack/sig.h> #include <sys/select.h> #include <sys/wait.h> struct termios stdin_termios; struct winsize stdin_winsize; int havewin = 0; char slavename[64]; int masterfd; pid_t pid; int tty_raw(int fd) { struct termios attr[1]; if (tcgetattr(fd, attr) == -1) return -1; attr->c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); attr->c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); attr->c_cflag &= ~(CSIZE | PARENB); attr->c_cflag |= (CS8); attr->c_oflag &= ~(OPOST); attr->c_cc[VMIN] = 1; attr->c_cc[VTIME] = 0; return tcsetattr(fd, TCSANOW, attr); } void restore_stdin(void) { if (tcsetattr(STDIN_FILENO, TCSANOW, &stdin_termios) == -1) errorsys("failed to restore stdin terminal attributes"); } void winch(int signo) { struct winsize winsize; if (ioctl(STDIN_FILENO, TIOCGWINSZ, &winsize) != -1) ioctl(masterfd, TIOCSWINSZ, &winsize); } int main(int ac, char **av) { if (ac == 1) { fprintf(stderr, "usage: pty command [arg...]\n"); return EXIT_FAILURE; } if (isatty(STDIN_FILENO)) havewin = ioctl(STDIN_FILENO, TIOCGWINSZ, &stdin_winsize) != -1; switch (pid = pty_fork(&masterfd, slavename, sizeof slavename, NULL, havewin ? &stdin_winsize : NULL)) { case -1: fprintf(stderr, "pty: pty_fork() failed (%s)\n", strerror(errno)); pty_release(slavename); return EXIT_FAILURE; case 0: execvp(av[1], av + 1); return EXIT_FAILURE; default: { int infd = STDIN_FILENO; int status; if (isatty(STDIN_FILENO)) { if (tcgetattr(STDIN_FILENO, &stdin_termios) != -1) atexit((void (*)(void))restore_stdin); tty_raw(STDIN_FILENO); signal_set_handler(SIGWINCH, 0, winch); } while (masterfd != -1) { fd_set readfds[1]; int maxfd; char buf[BUFSIZ]; ssize_t bytes; int n; FD_ZERO(readfds); if (infd != -1) FD_SET(infd, readfds); if (masterfd != -1) FD_SET(masterfd, readfds); maxfd = (masterfd > infd) ? masterfd : infd; signal_handle_all(); if ((n = select(maxfd + 1, readfds, NULL, NULL, NULL)) == -1 && errno != EINTR) break; if (n == -1 && errno == EINTR) continue; if (infd != -1 && FD_ISSET(infd, readfds)) { if ((bytes = read(infd, buf, BUFSIZ)) > 0) { if (masterfd != -1 && write(masterfd, buf, bytes) == -1) break; } else if (n == -1 && errno == EINTR) { continue; } else { infd = -1; continue; } } if (masterfd != -1 && FD_ISSET(masterfd, readfds)) { if ((bytes = read(masterfd, buf, BUFSIZ)) > 0) { if (write(STDOUT_FILENO, buf, bytes) == -1) break; } else if (n == -1 && errno == EINTR) { continue; } else { close(masterfd); masterfd = -1; continue; } } } if (waitpid(pid, &status, 0) == -1) { fprintf(stderr, "pty: waitpid(%d) failed (%s)\n", (int)pid, strerror(errno)); pty_release(slavename); return EXIT_FAILURE; } } } pty_release(slavename); if (masterfd != -1) close(masterfd); return EXIT_SUCCESS; }
libslack(3), openpty(3), forkpty(3) open(2), close(2), grantpt(3), unlockpt(3), ioctl(2), ttyname(3), ttyname_r(3), ptsname(3), ptsname_r(3), setpgrp(2), vhangup(2), setsid(2), _getpty(2), chown(2), chmod(2), tcsetattr(3), setpgrp(2), fork(2), dup2(2)
1995 Tatu Ylonen <ylo@cs.hut.fi> 2001-2010 raf <raf@raf.org>