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

}

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

Используются технологии uCoz