libslack(pseudo) - pseudo terminal module
#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() and pty_fork(). The permissions are also set automatically by these functions. So
pty_set_owner() 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.
Invalid arguments were passed to one of the functions.
openpty() or open("/dev/ptc") returned a slave descriptor for which ttyname() failed to return the slave device name. open("/dev/ptmx") returned a master descriptor for which ptsname() failed to return the the slave device name.
The old BSD-style pty device search failed to locate an available pseudo terminal.
The device name of the slave side of the pseudo terminal was too large to
fit in the slavename
buffer passed to pty_open() or pty_fork().
pty_make_controlling_tty() failed to disconnect from the controlling terminal.
A very simple pty program:
#include <slack/std.h> #include <slack/pseudo.h>
#include <sys/select.h> #include <sys/wait.h>
int main(int ac, char **av) { char slavename[64]; int masterfd; pid_t pid;
if (ac == 1) { fprintf(stderr, "usage: pty command [arg...]\n"); return EXIT_FAILURE; }
switch (pid = pty_fork(&masterfd, slavename, sizeof slavename, NULL, 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;
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;
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 { 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); close(masterfd);
return EXIT_SUCCESS; }
MT-Safe if and only if ttyname_r() or ptsname_r() are available when needed. On systems that have openpty() or "/dev/ptc"
, ttyname_r()
is required, otherwise the unsafe ttyname() will be used. On systems that have "/dev/ptmx"
, ptsname_r() is required, otherwise the unsafe
ptsname() will be used. On systems that have _getpty(), pty_open()
is unsafe because _getpty() is unsafe. In short, it's MT-Safe under Linux, Unsafe under Solaris and
OpenBSD.
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 raf <raf@raf.org>