Пример 27
/* Коммуникация процессов при помощи псевдо-терминала.
* Данная программа позволяет сохранять полный протокол работы
* экранной программы в файл.
* Не экранные программы данная версия НЕ трассирует,
* поскольку сама работает в "прозрачном" режиме.
*
* Вариацией данной программы может служить использование
* системного вызова select() вместо запуска нескольких процессов.
*
* Программа также иллюстрирует "дерево" из 5 процессов.
* Данная версия написана для UNIX System V.
* TRACE__
* \ \ master slave
* |экран<======\(Reader)=======!~!<====(целевая )
* / <==\ | ! !====>(программа)
* \ | !P! |
* | | !T! |
* . . . . | | !Y! (Slave)-->Управляет
* клавиатура=|===|=>(Writer)=>!_! | \ семафором
* | | | | \
* | #####starter################## \
* |...................................|
* ftty
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <termio.h>
#include <sys/stat.h>
#include <fcntl.h>
extern int exit ();
extern char *ttyname ();
extern FILE * fopen ();
extern errno;
#define SEMAPHORE "/tmp/+++" /* семафорный файл */
#define TRACE "./TRACE" /* файл с протоколом */
/* псевдотерминал связи */
/* master - это часть, которая ведет себя как ФАЙЛ и умеет
* реагировать на некоторые специальные ioctl()-и */
#define PTY "/dev/ptyp0" /* master */
/* slave - это часть, которая ведет себя как драйвер терминалов */
#define TTYP "/dev/ttyp0" /* slave */
int ptyfd;
FILE * ftrace = NULL;
/* при прерывании завершить работу процесса "писателя" */
onintr () {
closeVisual ();
fprintf (stderr, "\rwriter finished\r\n");
exit (0);
}
/* завершение работы процесса-"читателя" */
bye () {
if (ftrace)
fclose (ftrace);
fprintf (stderr, "\rreader finished\r\n");
exit (0);
}
int visual = 0;
struct termio old,
new;
/* настроить режимы работы терминала на "прозрачный" режим */
initVisual () {
ioctl (0, TCGETA, &old);
new = old;
new.c_iflag &= ~ICRNL;
new.c_lflag &= ~(ECHO | ICANON);
new.c_oflag &= ~(TAB3 | ONLCR);
new.c_cc[VMIN] = 1;
new.c_cc[VTIME] = 0;
/* new.c_cc[VINTR] = ctrl('C'); */
new.c_cc[VQUIT] = 0;
new.c_cc[VERASE] = 0;
new.c_cc[VKILL] = 0;
}
/* включить прозрачный режим */
openVisual () {
if (visual) return;
visual = 1;
ioctl (0, TCSETAW, &new);
}
/* выключить прозрачный режим */
closeVisual () {
if (!visual) return;
visual = 0;
ioctl (0, TCSETAW, &old);
}
struct stat st;
main (argc, argv) char **argv; {
int r, /* pid процесса-"читателя" */
w; /* pid процесса-"писателя" */
if (argc == 1) {
fprintf (stderr, "pty CMD ...\n");
exit (1);
}
initVisual ();
if((ptyfd = open ( PTY , O_RDWR)) < 0){
fprintf(stderr, "Cannot open pty\n"); exit(2);
}
/* запустить процесс чтения с псевдодисплея */
r = startReader ();
/* запустить процесс чтения с клавиатуры */
w = startWriter ();
sleep (2);
/* запустить протоколируемый процесс */
startSlave (argv + 1, r, w);
/* дождаться окончания всех потомков */
while (wait (NULL) > 0);
exit (0);
}
/* запуск протоколируемого процесса */
startSlave (argv, r, w) char **argv; {
FILE * ftty;
int pid;
int tfd;
char *tty = ttyname (1); /* полное имя нашего терминала */
if (!(pid = fork ())) {
/* PTY SLAVE process */
ftty = fopen (tty, "w"); /* Для выдачи сообщений */
setpgrp (); /* образовать новую группу процессов ;
* лишиться управляющего терминала */
/* закрыть стандартные ввод, вывод, вывод ошибок */
close (0);
close (1);
close (2);
/* первый открытый терминал станет управляющим для процесса,
* не имеющего управляющего терминала.
* Открываем псевдотерминал (slave) в качестве стандартных
* ввода, вывода и вывода ошибок
*/
open ( TTYP, O_RDWR);
open ( TTYP, O_RDWR);
tfd = open ( TTYP, O_RDWR);
if (tfd < 0) {
fprintf (ftty, "\rSlave: can't read/write pty\r\n");
kill(r, SIGKILL); kill(w, SIGKILL); exit (1);
}
/* запускаем целевую программу */
if (!(pid = fork ())) {
fprintf (ftty, "\rCreating %s\r\n", SEMAPHORE);
fflush (ftty);
/* создаем семафорный файл */
close (creat (SEMAPHORE, 0644));
fprintf (ftty, "\rStart %s\r\n", argv[0]);
fclose(ftty);
/* заменить ответвившийся процесс программой,
* указанной в аргументах
*/
execvp (argv[0], argv);
exit (errno);
}
/* дожидаться окончания целевой программы */
while (wait (NULL) != pid);
/* уничтожить семафор, что является признаком завершения
* для процессов чтения и записи
*/
unlink (SEMAPHORE);
fprintf (ftty, "\rDied.\r\n");
fflush (ftty);
/* убить процессы чтения и записи */
/* terminate reader & writer */
kill (r, SIGINT); kill (w, SIGINT);
exit (0);
}
return pid;
}
/* Пара master-процессов чтения и записи */
/* запуск процесса чтения с псевдотерминала (из master-части) */
startReader () {
char c[512];
int pid;
int n;
if (!(pid = fork ())) {
/* читать данные с ptyp на экран и в файл трассировки */
signal (SIGINT, bye);
/* ожидать появления семафора */
while (stat (SEMAPHORE, &st) < 0);
fprintf (stderr, "\rReader: Hello\r\n");
ftrace = fopen (TRACE, "w");
/* работать, пока существует семафорный файл */
while (stat (SEMAPHORE, &st) >= 0) {
/* прочесть очередные данные */
n = read (ptyfd, c, 512);
if( n > 0 ) {
/* записать их на настоящий терминал */
fwrite( c, sizeof(char), n, stdout );
/* и в файл протокола */
fwrite( c, sizeof(char), n, ftrace );
fflush (stdout);
}
}
bye ();
}
return pid;
}
/* запуск процесса чтения данных с клавиатуры и записи
* их на "псевдоклавиатуру". Эти данные протоколировать не надо,
* так как их эхо-отобразит сам псевдотерминал
*/
startWriter () {
char c;
int pid;
if (!(pid = fork ())) {
/* читать клавиатуру моего терминала и выдавать это в ptyp */
openVisual (); /* наш терминал - в прозрачный режим */
signal (SIGINT, onintr);
while (stat (SEMAPHORE, &st) < 0);
fprintf (stderr, "\rWriter: Hello\r\n");
/* работать, пока существует семафорный файл */
while (stat (SEMAPHORE, &st) >= 0) {
read (0, &c, 1); /* читать букву с клавиатуры */
write (ptyfd, &c, 1); /* записать ее на master-pty */
}
onintr (); /* завершиться */
}
return pid;
}
© Copyright А. Богатырев, 1992-95
Си в UNIX
Назад | Содержание | Вперед