Пример 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 www.abyss-group.narod.ru
Си в UNIX

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

Hosted by uCoz