Пример 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

Назад | Содержание | Вперед