Хрестоматия по программированию на Си в Unix
* ввода/вывода select() и псевдотерминал (пара ttyp+ptyp).
*/
#include
#include
#include
#include
#include
#include
#include /* NOFILE */
#include
#include
#include
#ifdef TERMIOS
# include
# define TERMIO struct termios
# define GTTY(fd, tadr) tcgetattr(fd, tadr)
# define STTY(fd, tadr) tcsetattr(fd, TCSADRAIN, tadr)
#else
# include
# define TERMIO struct termio
# define GTTY(fd, tadr) ioctl(fd, TCGETA, tadr)
# define STTY(fd, tadr) ioctl(fd, TCSETAW, tadr)
#endif
А. Богатырев, 1992-95 - 263 - Си в UNIX
#ifdef __SVR4
# include /* STREAMS i/o */
extern char *ptsname();
#endif
#if defined(ISC2_2)
# include
#else
# include
#endif
#ifndef BSIZE
# define BSIZE 512
#endif
#define LOGFILE "/usr/spool/scriptlog"
#define max(a,b) ((a) > (b) ? (a) : (b))
extern int errno;
TERMIO told, tnew, ttypmodes;
FILE *fpscript = NULL; /* файл с трассировкой (если надо) */
int go = 0;
int scriptflg = 0;
int halfflag = 0; /* HALF DUPLEX */
int autoecho = 0;
char *protocol = "typescript";
#define STDIN 0 /* fileno(stdin) */
#define STDOUT 1 /* fileno(stdout) */
#define STDERR 2 /* fileno(stderr) */
/* какие каналы связаны с терминалом? */
int tty_stdin, tty_stdout, tty_stderr;
int TTYFD;
void wm_checkttys(){
TERMIO t;
tty_stdin = ( GTTY(STDIN, &t) >= 0 );
tty_stdout = ( GTTY(STDOUT, &t) >= 0 );
tty_stderr = ( GTTY(STDERR, &t) >= 0 );
if ( tty_stdin ) TTYFD = STDIN;
else if( tty_stdout ) TTYFD = STDOUT;
else if( tty_stderr ) TTYFD = STDERR;
else {
fprintf(stderr, "Cannot access tty\n");
exit(7);
}
}
А. Богатырев, 1992-95 - 264 - Си в UNIX
/* Описатель трассируемого процесса */
struct ptypair {
char line[25]; /* терминальная линия: /dev/ttyp? */
int pfd; /* дескриптор master pty */
long in_bytes; /* прочтено байт с клавиатуры */
long out_bytes; /* послано байт на экран */
int pid; /* идентификатор процесса */
time_t t_start, t_stop; /* время запуска и окончания */
char *command; /* запущенная команда */
} PP;
/* Эта функция вызывается при окончании трассируемого процесса -
* по сигналу SIGCLD
*/
char Reason[128];
void ondeath(sig){
int pid;
extern void wm_done();
int status;
int fd;
/* выявить причину окончания процесса */
while((pid = wait(&status)) > 0 ){
if( WIFEXITED(status))
sprintf( Reason, "Pid %d died with retcode %d",
pid, WEXITSTATUS(status));
else if( WIFSIGNALED(status)) {
sprintf( Reason, "Pid %d killed by signal #%d",
pid, WTERMSIG(status));
#ifdef WCOREDUMP
if(WCOREDUMP(status)) strcat( Reason, " Core dumped" );
#endif
} else if( WIFSTOPPED(status))
sprintf( Reason, "Pid %d suspended by signal #%d",
pid, WSTOPSIG(status));
}
wm_done(0);
}
void wm_init(){
wm_checkttys();
GTTY(TTYFD, &told);
/* Сконструировать "сырой" режим для нашего _базового_ терминала */
tnew = told;
tnew.c_cc[VINTR] = '\0';
tnew.c_cc[VQUIT] = '\0';
tnew.c_cc[VERASE] = '\0';
tnew.c_cc[VKILL] = '\0';
#ifdef VSUSP
tnew.c_cc[VSUSP] = '\0';
#endif
А. Богатырев, 1992-95 - 265 - Си в UNIX
/* CBREAK */
tnew.c_cc[VMIN] = 1;
tnew.c_cc[VTIME] = 0;
tnew.c_cflag &= ~(PARENB|CSIZE);
tnew.c_cflag |= CS8;
tnew.c_iflag &= ~(ISTRIP|ICRNL);
tnew.c_lflag &= ~(ICANON|ECHO|ECHOK|ECHOE|XCASE);
tnew.c_oflag &= ~OLCUC;
/* но оставить c_oflag ONLCR и TAB3, если они были */
/* моды для псевдотерминала */
ttypmodes = told;
/* не выполнять преобразования на выводе:
* ONLCR: \n --> \r\n
* TAB3: \t --> пробелы
*/
ttypmodes.c_oflag &= ~(ONLCR|TAB3);
(void) signal(SIGCLD, ondeath);
}
void wm_fixtty(){
STTY(TTYFD, &tnew);
}
void wm_resettty(){
STTY(TTYFD, &told);
}
/* Подобрать свободный псевдотерминал для трассируемого процесса */
struct ptypair wm_ptypair(){
struct ptypair p;
#ifdef __SVR4
p.pfd = (-1); p.pid = 0;
p.in_bytes = p.out_bytes = 0;
/* Открыть master side пары pty (еще есть slave) */
if((p.pfd = open( "/dev/ptmx", O_RDWR)) < 0 ){
/* Это клонируемый STREAMS driver.
* Поскольку он клонируемый, то есть создающий новое псевдоустройство
* при каждом открытии, то на master-стороне может быть только
* единственный процесс!
*/
perror( "Open /dev/ptmx" );
goto err;
}
А. Богатырев, 1992-95 - 266 - Си в UNIX
# ifdef notdef
/* Сделать права доступа к slave-стороне моими. */
if( grantpt (p.pfd) < 0 ){
perror( "grantpt");
exit(errno);
}
# endif
/* Разблокировать slave-сторону псевдотерминала:
позволить первый open() для нее */
if( unlockpt(p.pfd) < 0 ){
perror( "unlockpt");
exit(errno);
}
/* Получить и записать имя нового slave-устройства-файла. */
strcpy( p.line, ptsname(p.pfd));
#else
register i;
char c;
struct stat st;
p.pfd = (-1); p.pid = 0;
p.in_bytes = p.out_bytes = 0;
strcpy( p.line, "/dev/ptyXX" );
for( c = 'p'; c <= 's'; c++ ){
p.line[ strlen("/dev/pty") ] = c;
p.line[ strlen("/dev/ptyp")] = '0';
if( stat(p.line, &st) < 0 )
goto err;
for(i=0; i < 16; i++){
p.line[ strlen("/dev/ptyp") ] =
"0123456789abcdef" [i] ;
if((p.pfd = open( p.line, O_RDWR )) >= 0 ){
p.line[ strlen("/dev/") ] = 't';
return p;
}
}
}
#endif
err: return p;
}
А. Богатырев, 1992-95 - 267 - Си в UNIX
/* Ведение статистики по вызовам script */
void write_stat( in_bytes, out_bytes, time_here , name, line, at )
long in_bytes, out_bytes;
time_t time_here;
char *name;
char *line;
char *at;
{
FILE *fplog;
struct flock lock;
if((fplog = fopen( LOGFILE, "a" )) == NULL )
return;
lock.l_type = F_WRLCK;
lock.l_whence = 0;
lock.l_start = 0;
lock.l_len = 0; /* заблокировать весь файл */
fcntl ( fileno(fplog), F_SETLKW, &lock );
fprintf( fplog, "%s (%s) %ld bytes_in %ld bytes_out %ld secs %s %s %s",
PP.command, Reason, in_bytes, out_bytes,
time_here, name, line, at );
fflush ( fplog );
lock.l_type = F_UNLCK;
lock.l_whence = 0;
lock.l_start = 0;
lock.l_len = 0; /* разблокировать весь файл */
fcntl ( fileno(fplog), F_SETLK, &lock );
fclose ( fplog );
}
void wm_done(sig){
char *getlogin(), *getenv(), *logname = getlogin();
time( &PP.t_stop ); /* запомнить время окончания */
wm_resettty(); /* восстановить режим базового терминала */
if( fpscript )
fclose(fpscript);
if( PP.pid > 0 ) kill( SIGHUP, PP.pid ); /* "обрыв связи" */
if( go ) write_stat( PP.in_bytes, PP.out_bytes,
PP.t_stop - PP.t_start,
logname ? logname : getenv("LOGNAME"),
PP.line, ctime(&PP.t_stop) );
printf( "\n" );
exit(0);
}
А. Богатырев, 1992-95 - 268 - Си в UNIX
/* Запуск трассируемого процесса на псевдотерминале */
void wm_startshell (ac, av)
char **av;
{
int child, fd, sig;
if( ac == 0 ){
static char *avshell[] = { "/bin/sh", "-i", NULL };
av = avshell;
}
if((child = fork()) < 0 ){
perror("fork");
wm_done(errno);
}
if( child == 0 ){ /* SON */
if( tty_stdin )
setpgrp(); /* отказ от управляющего терминала */
/* получить новый управляющий терминал */
if((fd = open( PP.line, O_RDWR )) < 0 ){
exit(errno);
}
/* закрыть лишние каналы */
if( fpscript )
fclose(fpscript);
close( PP.pfd );
#ifdef __SVR4
/* Push pty compatibility modules onto stream */
ioctl(fd, I_PUSH, "ptem"); /* pseudo tty module */
ioctl(fd, I_PUSH, "ldterm"); /* line discipline module */
ioctl(fd, I_PUSH, "ttcompat"); /* BSD ioctls module */
#endif
/* перенаправить каналы, связанные с терминалом */
if( fd != STDIN && tty_stdin ) dup2(fd, STDIN);
if( fd != STDOUT && tty_stdout ) dup2(fd, STDOUT);
if( fd != STDERR && tty_stderr ) dup2(fd, STDERR);
if( fd > STDERR )
(void) close(fd);
/* установить моды терминала */
STTY(TTYFD, &ttypmodes);
/* восстановить реакции на сигналы */
for(sig=1; sig < NSIG; sig++)
signal( sig, SIG_DFL );
execvp(av[0], av);
system( "echo OBLOM > HELP.ME");
perror("execl");
exit(errno);
А. Богатырев, 1992-95 - 269 - Си в UNIX
} else { /* FATHER */