6.11.2. В качестве самостоятельной работы предлагаем вам пример программы, ведущей протокол сеанса работы. Информацию о псевдотерминалах изучите самостоятельно.


    /*

     *              script.c

     *      Программа получения трассировки работы других программ.

     *      Используется системный вызов опроса готовности каналов

     *      ввода/вывода select() и псевдотерминал (пара ttyp+ptyp).

     */

    #include <stdio.h>

    #include <stdlib.h>

    #include <fcntl.h>

    #include <sys/types.h>

    #include <sys/stat.h>

    #include <signal.h>

    #include <sys/param.h>  /* NOFILE */

    #include <sys/times.h>

    #include <sys/wait.h>

    #include <errno.h>

    #ifdef TERMIOS

    # include <termios.h>

    # define TERMIO struct termios

    # define GTTY(fd, tadr)  tcgetattr(fd, tadr)

    # define STTY(fd, tadr)  tcsetattr(fd, TCSADRAIN, tadr)

    #else

    # include <termio.h>

    # define TERMIO struct termio

    # define GTTY(fd, tadr) ioctl(fd, TCGETA,  tadr)

    # define STTY(fd, tadr) ioctl(fd, TCSETAW, tadr)

    #endif

    #ifdef __SVR4

    # include <stropts.h>   /* STREAMS i/o */

    extern char *ptsname();

    #endif

    #if defined(ISC2_2)

    # include <sys/bsdtypes.h>

    #else

    # include <sys/select.h>

    #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);

            }

    }

    /* Описатель трассируемого процесса */

    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

            /* 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;

            }

    # 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;

    }

    /* Ведение статистики по вызовам 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);

    }

    /* Запуск трассируемого процесса на псевдотерминале */

    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);

            } else {                /* FATHER */

                    PP.pid = child;

                    PP.command = av[0];

                    time( &PP.t_start ); PP.t_stop = PP.t_start;

                    signal( SIGHUP,  wm_done );

                    signal( SIGINT,  wm_done );

                    signal( SIGQUIT, wm_done );

                    signal( SIGTERM, wm_done );

                    signal( SIGILL,  wm_done );

                    signal( SIGBUS,  wm_done );

                    signal( SIGSEGV, wm_done );

            }

    }

    char buf[ BSIZE ];   /* буфер для передачи данных */

    /*                               /dev/pty?    /dev/ttyp?

         экран         *--------*          *--------*

         /|||          |        |  PP.pfd  |        |

        |||||<-STDOUT--|  мой   |<---------| псевдо |<-STDOUT---|

         \|||          |терминал|          |терминал|<-STDERR---|трассируемый

                       |(базовый)          |        |           |процесс

        -------        |        |  STDIN   |        |           |

        |.....|-STDIN-->        |---------->        |--STDIN--->|

        |_____|        |        |          |        |

        клавиатура     *--------*          *--------*

                                        master     slave

    */

    /* Опрос дескрипторов */

    void wm_select(){

            int nready;

            int nfds;

            int maxfd;

            int nopen;  /* число опрашиваемых дескрипторов */

            register f;

            fd_set set, rset;       /* маски */

            struct timeval timeout, rtimeout;

            FD_ZERO(&set); nopen = 0;        /* очистка маски */

            FD_SET (PP.pfd, &set); nopen++;  /* учесть в маске */

            FD_SET (STDIN,  &set); nopen++;

            maxfd = max(PP.pfd, STDIN);

            timeout.tv_sec = 3600;      /* секунд */

            timeout.tv_usec = 0;        /* миллисекунд */

            nfds =  maxfd + 1;

            while( nopen ){

               rset = set;

               rtimeout = timeout;

               /* опросить дескрипторы */

               if((nready = select( nfds, &rset, NULL, NULL, &rtimeout )) <= 0)

                    continue;

               for(f=0; f < nfds; f++ )

                    if( FD_ISSET(f, &rset)){  /* дескриптор f готов */

                            int n;

                            if((n = read(f, buf, sizeof buf)) <= 0 ){

                                FD_CLR(f, &set); nopen--; /* исключить */

                                close(f);

                            } else {

                                int fdout;

                                /* учет и контроль */

                                if( f == PP.pfd ){

                                    fdout = STDOUT;

                                    PP.out_bytes += n;

                                    if( fpscript )

                                        fwrite(buf, 1, n, fpscript);

                                } else if( f == STDIN  ) {

                                    fdout = PP.pfd;

                                    PP.in_bytes += n;

                                    if( halfflag && fpscript )

                                        fwrite(buf, 1, n, fpscript);

                                    if( autoecho )

                                        write(STDOUT, buf, n);

                                }

                                write(fdout, buf, n);

                            }

                    }

            }

    }

    int main(ac, av) char **av;

    {

            while( ac > 1 && *av[1] == '-' ){

                    switch(av[1][1]){

                    case 's':

                            scriptflg++;

                            break;

                    case 'f':

                            av++; ac--;

                            protocol = av[1];

                            scriptflg++;

                            break;

                    case 'h':

                            halfflag++;

                            break;

                    case 'a':

                            autoecho++;

                            break;

                    default:

                            fprintf(stderr, "Bad key %s\n", av[1]);

                            break;

                    }

                    ac--; av++;

            }

            if( scriptflg ){

                    fpscript = fopen( protocol, "w" );

            }

            ac--; av++;

            wm_init();

            PP = wm_ptypair();

            if( PP.pfd < 0 ){

                    fprintf(stderr, "Cannot get pty. Please wait and try again.\n");

                    return 1;

            }

            wm_fixtty();

            wm_startshell(ac, av);

            go++;

            wm_select();

            wm_done(0);

            /* NOTREACHED */

            return 0;

    }

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

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