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.
EINVALInvalid arguments were passed to one of the functions.
ENOTTYopenpty(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.
ENOENTThe old BSD-style pty device search failed to locate an available pseudo terminal.
ENOSPCThe 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).
ENXIOpty_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>