免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3882 | 回复: 8
打印 上一主题 下一主题

[C] 在unix环境用C语言实现操作的多屏幕同步输出 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-05-07 00:05 |只看该作者 |倒序浏览
根据高人《在UNIX系统上记录进程输入/输出的方法》(注:见[url]http://bbs.chinaunix.net/thread-1090836-1-3.html[/url])

在UNIX环境下用C语言和伪终端方法,实现同一操作的多屏幕输出(相当于现场转播功能)

命令用法:

vtp 空闲的伪终端号 转播终端1 转播终端2 ...

例:vtp ttyp4 tty1a tty1b tty1c tty1d ... (其中ttyp4必须是空闲和有效的)

源程序如下:
/code

/* vtp.c */

#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "fcntl.h"
#include "signal.h"
#include "tinfo.h"
#include "termio.h"
#include "sys/stream.h"

#define BUFSIZE 4096

int console_fd,pty_fd;
struct termio t_attr;
char pttyname[15]="/dev/pty";

/* 退出时,恢复原来的终端属性 */
void exitproc(int i){
        ioctl(console_fd,TCSETAF,&t_attr);
        exit(0);
}

void HELP(char *str){
        printf("\n用法: %s 伪终端号 [转播终端1 转播终端2...]\n\n", str);
        exit(0);
        }

/* 设置终端的低层特性 */
static void set_console_raw(int fd){
        struct termios buf;
        if(tcgetattr(fd,&buf)<0) exit(-1);
        buf.c_lflag&=~(ECHO|ICANON|IEXTEN|ISIG);
        buf.c_iflag&=~(BRKINT|ICRNL|INPCK|ISTRIP|IXON);
        buf.c_cflag&=~(CSIZE|PARENB);
        buf.c_cflag|=CS8;
        buf.c_oflag&=~(OPOST);
        buf.c_cc[VMIN]=1;
        buf.c_cc[VTIME]=0;
        if(tcsetattr(fd,TCSAFLUSH,&buf)<0) exit(-1);
}

main(int argc,char *argv[]){
        int fd_in,fd_out,nread,slave_fd,i;
        int MAX,fd[50]; /* 允许的最大"转播"终端数为50个 */
        char str[100];
        fd_set rset,rrset;
        static unsigned char buf[BUFSIZE];

        if(argc<2) HELP(argv[0]);
        /* 判断第一个参数是不是伪终端号 */
        if(strncmp("ttyp",argv[1],4)!=0) HELP(argv[0]);
        strcat(pttyname,argv[1]+3);
        for(MAX=0;MAX<argc-2;MAX++){ /* 打开所有要转播的终端 */
                sprintf(str,"/dev/%s",argv[MAX+2]);
                fd[MAX]=open(str,O_WRONLY);
        }
        signal(SIGTERM,exitproc);
        signal(SIGKILL,exitproc);
        signal(SIGCLD,exitproc);
        console_fd=open(ttyname(0),O_RDWR);
        ioctl(console_fd,TCGETA,&t_attr);
        set_console_raw(console_fd);
        pty_fd=open(pttyname,O_RDWR);

        /* 打开用于保存输入输出信息用的文件 */
        fd_in=open("./vtp.in",O_CREAT|O_RDWR|O_TRUNC,0660);
        fd_out=open("./vtp.out",O_CREAT|O_RDWR|O_TRUNC,0660);
       
        switch(fork()){
                case -1: exit(1);
                case 0:
                        setsid();
                        /* 让子进程以指定的伪终端号运行 */
                        sprintf(str,"/dev/%s",argv[1]);
                        slave_fd=open(str,O_RDWR);
                        if(slave_fd==-1){
                                printf("打开%s出错\n\n",pttyname);
                                exit(-1);
                        }
                        ioctl(slave_fd,TCSETAF,&t_attr);
                        dup2(slave_fd,0);
                        dup2(slave_fd,1);
                        dup2(slave_fd,2);
                        close(slave_fd);
                        execlp("/bin/sh",NULL);
        }
        FD_ZERO(&rrset);
        FD_SET(console_fd,&rrset);
        FD_SET(pty_fd,&rrset);
        while(1){
                rset=rrset;
                if(select(FD_SETSIZE,&rset,0,0,0)==-1) continue;
                /* 通过伪终端,向子进程转递输入信息,并保存 */
                if(FD_ISSET(console_fd,&rset)){
                        nread=read(console_fd,buf,BUFSIZE);
                        buf[nread]='\0';
                        if(nread>0){
                                write(pty_fd,buf,strlen(buf));
                                write(fd_in,buf,nread);
                        }
                }
                /* 向伪终端读取输出信息,并保存 */
                if(FD_ISSET(pty_fd,&rset)){
                        nread=read(pty_fd,buf,BUFSIZE);
                        /* 向终端转播输出信息 */
                        for(i=0;i<MAX;i++) write(fd[i],buf,nread);
                        buf[nread]='\0';
                        if(nread>0){
                                write(console_fd,buf,strlen(buf));
                                write(fd_out,buf,nread);
                        }
                }
        }
}


/code

转播时子进程以伪终端号运行,并在当前目录下保存输入和输出信息文件 vtp.in   vtp.out

要回放时只需cat vtp.out即可。

如果喜欢请回贴一下。谢谢。

[[i] 本帖最后由 melove 于 2008-5-7 00:11 编辑 [/i]]

论坛徽章:
0
2 [报告]
发表于 2008-05-07 00:10 |只看该作者
代码如何包围呀,怎么又有问题呀。唉。

论坛徽章:
0
3 [报告]
发表于 2008-05-07 00:13 |只看该作者
[code]
代码
[/code]

论坛徽章:
0
4 [报告]
发表于 2008-05-07 00:18 |只看该作者
源程序在sco openserver 5.0.5的开发系统下编译通过

cc vtp.c -lc  -o vtp

论坛徽章:
0
5 [报告]
发表于 2008-05-08 12:10 |只看该作者
真没人感兴趣?

论坛徽章:
0
6 [报告]
发表于 2008-05-15 10:20 |只看该作者

回复 #5 melove 的帖子

我感兴趣!最近研究远程控制!也是一头雾水,如果兄台愿意,我们可以交流一下啊!邮箱hyp19861016@sina.com

论坛徽章:
0
7 [报告]
发表于 2008-05-15 12:57 |只看该作者

回复 #4 melove 的帖子

我用的是fedora 8,我的电脑里面怎么没有/dev/pty阿

论坛徽章:
3
2015年迎新春徽章
日期:2015-03-04 09:48:31平安夜徽章
日期:2015-12-26 00:06:30C
日期:2016-10-25 16:26:25
8 [报告]
发表于 2008-05-16 15:01 |只看该作者
原帖由 melove 于 2008-5-8 12:10 发表
真没人感兴趣?


我感兴趣,但目前对于伪终端编程还不了解,还在看APUE,学习下!

论坛徽章:
0
9 [报告]
发表于 2008-05-24 17:50 |只看该作者
程序做了些轻微的改进,主要是自动打开一个伪终端,不用手工指定。

使用命令改为: relay 终端号1 终端号2 ...

编译命令为: cc relay.c -lc -o relay

测试环境仍为: SCO Openserver 5.0.5



  1. /* 向指定终端转播当前操作 relay.c */
  2. #include "stdio.h"
  3. #include "stdlib.h"
  4. #include "unistd.h"
  5. #include "fcntl.h"
  6. #include "signal.h"
  7. #include "tinfo.h"
  8. #include "termio.h"
  9. #include "sys/stream.h"

  10. #define BUFSIZE 1024

  11. int console_fd,pty_fd;
  12. struct termio t_attr;

  13. /* 退出时,恢复原来的终端属性 */
  14. void exitproc(int i){
  15.         ioctl(console_fd,TCSETAF,&t_attr);
  16.         printf("\n所有操作已被记录,如要回放请 cat relay.out \n\n");
  17.         exit(0);
  18. }

  19. /* 设置终端的低层特性 */
  20. static void set_console_raw(int fd){
  21.         struct termios buf;
  22.         if(tcgetattr(fd,&buf)<0) exit(-1);
  23.         buf.c_lflag&=~(ECHO|ICANON|IEXTEN|ISIG);
  24.         buf.c_iflag&=~(BRKINT|ICRNL|INPCK|ISTRIP|IXON);
  25.         buf.c_cflag&=~(CSIZE|PARENB);
  26.         buf.c_cflag|=CS8;
  27.         buf.c_oflag&=~(OPOST);
  28.         buf.c_cc[VMIN]=1;
  29.         buf.c_cc[VTIME]=0;
  30.         if(tcsetattr(fd,TCSAFLUSH,&buf)<0) exit(-1);
  31. }

  32. main(int argc,char *argv[]){
  33.         int fd_in,fd_out,nread,slave_fd,i;
  34.         int MAX,fd[50]; /* 允许的最大"转播"屏幕数为50个 */
  35.         char *ptr1,*ptr2,ter_name[20];
  36.         char *ptm_name="/dev/ptypXX",*pts_name="/dev/ttypXX";
  37.         fd_set rset,rrset;
  38.         static unsigned char buf[BUFSIZE];

  39.         /* 打开一个空闲的伪终端号 */
  40.         for(ptr1="0123456789abcdef";*ptr1!='\0';ptr1++){
  41.            ptm_name[9]=*ptr1;
  42.            pts_name[9]=*ptr1;
  43.            for(ptr2="0123456789abcdef";*ptr2!='\0';ptr2++){
  44.                 ptm_name[10]=*ptr2;
  45.                 pty_fd=open(ptm_name,O_RDWR);
  46.                    if(pty_fd<=0) continue;
  47.                    else{
  48.                         pts_name[10]=*ptr2;
  49.                         break;
  50.                    }
  51.               }
  52.            if(pty_fd>0) break;else ptr2-=16;
  53.         }
  54.         if(pty_fd<=0){
  55.                 printf("\n打开伪终端失败,程序无法运行!\n");
  56.                 exit(-1);
  57.         }

  58.         /* 打开用于保存输入输出信息用的文件 */
  59.         fd_in=open("./relay.in",O_CREAT|O_RDWR|O_TRUNC,0660);
  60.         fd_out=open("./relay.out",O_CREAT|O_RDWR|O_TRUNC,0660);
  61.         if(fd_out<=0){
  62.                 printf("\n无法在当前目录保存操作记录!\n");
  63.                 exit(-1);
  64.         }

  65.         printf("\n正在向以下终端\"转播\"操作 ...\n\n");
  66.         for(i=0,MAX=0;MAX<argc-1;MAX++){
  67.                 sprintf(ter_name,"/dev/%s",argv[MAX+1]);
  68.                 fd[MAX]=open(ter_name,O_WRONLY);
  69.                 if(fd[MAX]>0){
  70.                    printf("%s ",argv[MAX+1]);
  71.                    i++;
  72.                 }
  73.         }
  74.         printf("\n\n共 %d 台终端.\n\n",i);

  75.         signal(SIGTERM,exitproc);
  76.         signal(SIGKILL,exitproc);
  77.         signal(SIGCLD,exitproc);
  78.         console_fd=open(ttyname(0),O_RDWR);
  79.         ioctl(console_fd,TCGETA,&t_attr);
  80.         set_console_raw(console_fd);

  81.         switch(fork()){
  82.                 case -1: exit(1);
  83.                 case 0:
  84.                         setsid();
  85.                         /* 让子进程以指定的伪终端号运行 */
  86.                         slave_fd=open(pts_name,O_RDWR);
  87.                         if(slave_fd==-1){
  88.                                 printf("打开%s出错\n\n",pts_name);
  89.                                 exit(-1);
  90.                         }
  91.                         ioctl(slave_fd,TCSETAF,&t_attr);
  92.                         dup2(slave_fd,0);
  93.                         dup2(slave_fd,1);
  94.                         dup2(slave_fd,2);
  95.                         close(slave_fd);
  96.                         execlp("/bin/sh",NULL);
  97.         }
  98.         FD_ZERO(&rrset);
  99.         FD_SET(console_fd,&rrset);
  100.         FD_SET(pty_fd,&rrset);
  101.         while(1){
  102.                 rset=rrset;
  103.                 if(select(FD_SETSIZE,&rset,0,0,0)==-1) continue;
  104.                 /* 通过伪终端,向子进程转递输入信息,并保存 */
  105.                 if(FD_ISSET(console_fd,&rset)){
  106.                         nread=read(console_fd,buf,BUFSIZE);
  107.                         buf[nread]='\0';
  108.                         if(nread>0){
  109.                                 write(pty_fd,buf,strlen(buf));
  110.                                 write(fd_in,buf,nread);
  111.                         }
  112.                 }
  113.                 /* 向伪终端读取输出信息,并保存 */
  114.                 if(FD_ISSET(pty_fd,&rset)){
  115.                         nread=read(pty_fd,buf,BUFSIZE);
  116.                         /* 向终端转播输出信息 */
  117.                         for(i=0;i<MAX;i++) write(fd[i],buf,nread);
  118.                         buf[nread]='\0';
  119.                         if(nread>0){
  120.                                 write(console_fd,buf,strlen(buf));
  121.                                 write(fd_out,buf,nread);
  122.                         }
  123.                 }
  124.         }
  125. }

复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP