Help, please, with pseudo tty
Larry McVoy
lm at snafu.Sun.COM
Tue Nov 29 10:38:18 AEST 1988
In article <926 at dlhpedg.co.uk> cl at datlog.co.uk (Charles Lambert) writes:
>
>I'm struggling with my first attempt to use a pseudo-tty on AIX 2.2.
[ Someone else wanted this as well, enjoy ]
This is some code that basically gives you a shell (or an su, I don't
remember) and logs all the commands that you type. Last I remember, it
more or less worked - no promises. At any rate, it certainly gives all
those pty hackers a running start. This source was generated on and for
Sun machines.
# This is a shell archive. Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file". (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# Makefile log.awk log.c mode.c pty.c ptypair.c
echo x - Makefile
cat > "Makefile" << '//E*O*F Makefile//'
CFLAGS = -DLOGFILE=\"./log\"
O = pty.o ptypair.o log.o
pty: $O
cc $(CFLAGS) $O -o pty
clobber:
/bin/rm -f $O pty log
//E*O*F Makefile//
echo x - log.awk
cat > "log.awk" << '//E*O*F log.awk//'
BEGIN { FS=":" }
NF == 3 && $2 == "END" { IN=0; }
NF == 6 && $1 == "START" { IN = 1; printf("user=%s time=%s\n", $3, $2); getline; }
IN == 1 { print; }
//E*O*F log.awk//
echo x - log.c
cat > "log.c" << '//E*O*F log.c//'
# include <stdio.h>
# include <sys/file.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <pwd.h>
# ifndef LOGFILE
# define LOGFILE "/usr/adm/sulog"
# endif
static logfd;
static opened;
static char *tmp = "/tmp/ReXXXXXX";
logstartup()
{
struct stat st;
struct passwd *p;
struct passwd *getpwuid();
struct passwd *getpwnam();
char *date();
char *getwd();
char *getlogin();
char buf[200];
char wd[100];
if (stat(LOGFILE, &st))
close(creat(LOGFILE, 0600));
logfd = open(mktemp(tmp), O_RDWR | O_CREAT, 0600);
opened = 1;
setpwent();
if (!(p = getpwnam(getlogin())) && !(p = getpwuid(getuid()))) {
printf("Who are you?\n");
exit(1);
}
endpwent();
sprintf(buf, "START:%s:%s:%d:%d:%s\n",
date(), p->pw_name, getuid(), getgid(), getwd(wd));
logadd(buf, strlen(buf));
}
char *
date()
{
long t;
register char *s;
char *ctime();
time(&t);
s = ctime(&t);
s[13] = '.';
s[16] = 0;
return s;
}
logadd(buf, n)
char *buf;
{
register i;
if (!opened) {
logstartup();
opened = 1;
}
for (i = 0; i < n; i++)
if (buf[i] == '\r')
buf[i] = '\n';
write(logfd, buf, n);
}
logclose()
{
register trys = 0, nread, log;
char buf[BUFSIZ];
while (trys++ < 4 && (log = open(LOGFILE, O_RDWR | O_EXCL | O_APPEND)) < 0)
sleep(1);
logadd(":END:\n", 8);
close(logfd);
logfd = open(tmp, O_RDONLY);
sleep(1);
while ((nread = read(logfd, buf, sizeof(buf))) > 0)
write(log, buf, nread);
close(logfd);
close(log);
unlink(tmp);
}
//E*O*F log.c//
echo x - mode.c
cat > "mode.c" << '//E*O*F mode.c//'
# include <stdio.h>
# include <sgtty.h>
static struct sgttyb old;
static struct sgttyb new;
static int done;
m_init()
{
ioctl(fileno(stdin), TIOCGETP, &old);
new = old;
}
m_normal(fd)
{
if (!done++)
m_init();
ioctl(fd, TIOCSETP, &old);
}
m_raw(fd)
{
if (!done++)
m_init();
new.sg_flags = RAW;
ioctl(fd, TIOCSETP, &new);
}
//E*O*F mode.c//
echo x - pty.c
cat > "pty.c" << '//E*O*F pty.c//'
# include <stdio.h>
# include <signal.h>
# include <errno.h>
# include <sgtty.h>
# include <assert.h>
# include <sys/types.h>
/*
* pty filter - trap all keyboard input in a logfile
*/
# define debug(x) /* fprintf x /* */
# define BSIZ 256
# define BAD_READ 1
# define BAD_SELECT 2
int cleanup(); /* called to quit */
int kidid; /* child pid & std{*} */
struct sgttyb restore; /* tty modes of stdin */
main(ac, av, ev)
char **av;
char **ev;
{
int fds[2]; /* master/slave */
/* tidy cleanup, thank you. */
signal(SIGHUP, cleanup);
signal(SIGCLD, cleanup);
/* save the modes for exit... */
ioctl(0, TIOCGETP, &restore);
if (ptypair(fds) == -1)
perror("ptypair");
if (!(kidid = fork())) {
close(fds[0]);
kid(fds[1], av, ev);
}
close(fds[1]);
pass(fds[0]);
/* NOTREACHED */
}
/* kid - exec a sh
*/
kid(fd, av, ev)
char **av;
char **ev;
{
int pgrp;
/*
* dup the slave side to std{in,out,err}, make a new process group, grab
* the pty, set the pty modes to be "normal" for a shell, and exec.
*/
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
ioctl(fd, TIOCSETP, &restore);
setpgrp(0, pgrp = getpid());
ioctl(fd, TIOCSPGRP, &pgrp);
av[0] = "/bin/sh";
execve(av[0], av, ev);
syserr("exec");
/* NOTREACHED */
}
/*
* pass - pass through filter
*
* take any input from stdin and pass to fd
* take any output from fd and pass to stdout
*/
# define STDIN 1
# define SLAVE (1<<fd)
pass(fd)
{
extern errno;
struct sgttyb p;
static u_char buf[BSIZ];
u_long orgmask, mask;
register nread;
/* input is raw (no signal processing & fast */
ioctl(0, TIOCGETP, &p);
p.sg_flags = RAW;
ioctl(0, TIOCSETP, &p);
orgmask = STDIN | SLAVE;
logstartup();
for (;;) {
mask = orgmask;
/*
* could check for EINTR except we're in RAW mode so no signals, and
* there are no itimers either... pretty safe.
*/
if (select(32, &mask, 0, 0, 0) == -1)
cleanup(BAD_SELECT);
debug((stderr, "mask=%#x\n", mask));
/*
* input from the "keyboard"
*/
if (mask & STDIN) {
if ((nread = read(0, buf, sizeof(buf))) <= 0)
cleanup(BAD_READ);
debug((stderr, "Line: %d bytes\n\r", nread));
write(fd, buf, nread);
logadd(buf, nread);
}
/*
* output from the process
*/
if (mask & SLAVE) {
if ((nread = read(fd, buf, sizeof(buf))) <= 0)
cleanup(BAD_READ);
debug((stderr, "pty: %d bytes\n\r", nread));
write(1, buf, nread);
}
}
/* NOTREACHED */
}
syserr(msg)
char *msg;
{
extern int errno;
psyserr(msg);
exit(errno ? errno : 100);
}
psyserr(msg)
register char *msg;
{
extern int errno, sys_nerr;
extern char *sys_errlist[];
fprintf(stderr, "ERROR: %s (%d", msg, errno);
if (errno > 0 && errno < sys_nerr)
fprintf(stderr, "; %s)\n", sys_errlist[errno]);
else
fprintf(stderr, ")\n");
}
cleanup(sig)
{
kill(kid, SIGHUP);
ioctl(0, TIOCSETP, &restore);
logclose();
exit(0);
}
//E*O*F pty.c//
echo x - ptypair.c
cat > "ptypair.c" << '//E*O*F ptypair.c//'
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
/*
* ptypair
* works like pipe() or socketpair(), but returns a master/slave
* pty pair. The master is fd[0], the slave is fd[1]. The slave
* end is the end that actually looks like a tty. The pty is
* returned in RAW/NO-PARITY mode and both ends are open read/write.
*
* returns 0 if it found a pty, -1 if not.
*/
int
ptypair(fd)
int fd[2];
{
register i;
register char *c, *line;
struct stat stb;
struct sgttyb sb;
extern int errno;
char ptymask[10];
#define PTYMASK_LEN 10
strcpy(ptymask, "/dev/ptyXX");
for (c = "pqrs"; *c != 0; c++) {
line = ptymask;
line[PTYMASK_LEN - 2] = *c;
line[PTYMASK_LEN - 1] = '0';
if (stat(line, &stb) < 0) /* see if the block of ptys exists */
break;
for (i = 0; i < 16; i++) {
line[PTYMASK_LEN - 1] = "0123456789abcdef"[i];
fd[0] = open(line, O_RDWR);
if (fd[0] > 0) {
line[PTYMASK_LEN - 5] = 't';
fd[1] = open(line, O_RDWR);
if (fd[1] < 0) {/* if tty open fails, try another */
(void) close(fd[0]);
continue;
}
/* now, make it sane */
(void) ioctl(fd[1], (int) TIOCGETP, (char *) &sb);
sb.sg_ispeed = EXTA;
sb.sg_ospeed = EXTA;
sb.sg_flags = RAW | ANYP;
(void) ioctl(fd[1], (int) TIOCSETP, (char *) &sb);
return 0;
}
}
}
errno = ENOSPC; /* no space left on device -- it's close */
return -1;
}
//E*O*F ptypair.c//
echo Possible errors detected by \'wc\' [hopefully none]:
temp=/tmp/shar$$
trap "rm -f $temp; exit" 0 1 2 3 15
cat > $temp <<\!!!
8 21 121 Makefile
7 37 156 log.awk
85 205 1741 log.c
29 47 363 mode.c
150 447 3210 pty.c
63 238 1524 ptypair.c
342 995 7115 total
!!!
wc Makefile log.awk log.c mode.c pty.c ptypair.c | sed 's=[^ ]*/==' | diff -b $temp -
exit 0
Larry McVoy (lm%snafu at sun.com)
More information about the Comp.unix.wizards
mailing list