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)
*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)
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)
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)
*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)
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)
*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.
slavename
buffer passed to pty_open(3) or pty_fork(3).
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(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.
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>