sshpass引出的终端的知识整理

事情的起因:
    我们的生产系统有18台IBM的aix5.3主机,相应的发布代码,执行命令,如果登陆到每一台上去做,工作量大且冗余多,原本想使用expect,编写起来也有点麻烦,在网上查资料找到一个轻量级的sshpass,可以过程到其他主机上执行命令。源码可以在http://sourceforge.net/projects/sshpass/下载。
    看了下源码,实现是这样的:
    2个进程,主进程打开一个伪终端:
masterpt=posix_openpt(O_RDWR);//可以直接open /dev/ptmx,后面谈这个

    子进程将自己和控制终端断开:setsid();,然后打开从伪终端:
  const char *name=ptsname(masterpt);
  int slavept=open(name, O_RDWR,0 );

    这样,在没有控制终端的子进程中,第一个打开的终端设备,当成了控制终端,然后exec执行ssh(exec后继承pcb,终端不变),ssh在获取密码时,是open /dev/tty来获取控制终端,然后来输出password:和获得用户输入的密码(这样可以无视重定向,密码不回显),变成了对从伪终端的读写。

    主进程监听主伪终端,判断是否有password:输出,有的话将密码写入从伪终端。

相关知识:
    前面其实就涉及了很多的终端问题,所以在网上查了很多的资料,参考的太多,就不在这一一列举了。
终端:
    TTY是TeleTYpe的一个老缩写,现在用来统称各种终端设备,包括终端、串行接口、伪终端等。
    终端理解起来,就是一个人机接口,更像无盘工作站的一个个屏幕和键盘,使用的主机资源是相同的。一台主机可以有多个终端。

伪终端:
    顾名思义,就是一个虚拟终端,多用于ssh/telnet等网络登陆。伪终端是成对出现的,APUE中说system v的unix系统可以使用/dev/ptmx来打开下一个可用的伪终端的主设备,linux也是支持的,不过不是STREAMS设备(?)。/dev/ptmx是一个克隆设备,每次打开都是返回下一个可用的伪终端主设备。使用ptsname可以获取相应的从伪终端名,然后打开从伪终端(/dev/pts/*)。

控制终端:
    这个涉及一个会话(session)的概念,会话指的是一组进程组,会话中的所有进程具有相同的会话标识符,会话首进程(session leader),指的是创建会话的进程,其进程ID会成为会话ID。
    通常,会话都与某个控制终端相关。如果会话没有控制终端,会话首进程在第一次打开一个终端设备时(没有加O_NOCTTY参数),这个终端设备会变为这个会话的控制终端(SVR4/Linux)。对于交互式shell创建的会话,控制终端就是用户的登陆终端。一个终端最多成为一个会话的控制终端。

一些特性:
    1、一个会话可以有一个控制终端(controlling terminal)。通常是登陆到主机上的终端设备(终端登陆)或伪终端设备(网络登陆);
    2、建立与控制终端联系的会话首进程称为控制进程(controlling process);
    3、一个会话中的几个进程组可以分成一个前台进程组(foreground process group)以及一个或多个后台进程组(background process group);
    4、输入终端的中断键(通常是DELETE或CTRL+C),会将中断信号发送给前台进程组的所有进程;
    5、输入终端的退出键(通常是CTRL+\),会将退出信号发送级前台进程组的所有进程;
    6、终端上的连接断开(网络等),会将挂起信号发送给控制进程;
    7、前台进程组可以从控制终端获取输入、输出,后台进程组只能输出,不能获取输入(尝试获取时会收到SIGTTIN信号,默认处理是停止);
下面的资料,来自:http://learn.akae.cn/media/ch34s02.html
      $ cat &
      [1] 9386
      $ (再次回车)

      [1]+  Stopped                 cat

    可以用任务处理命令进行操作:jobs命令可以查看当前有哪些作业。fg命令可以将某个作业提至前台运行,如果该作业的进程组正在后台运行则提至前台运行,如果该作业处于停止状态,则给进程组的每个进程发SIGCONT信号使它继续运行。如果输入Ctrl-Z则向所有前台进程发SIGTSTP信号,该信号的默认动作是使进程停止。
        $ jobs
        [1]+  Stopped                 cat
        $ fg %1
        cat
        hello(回车)
        hello
        ^Z
        [1]+  Stopped                 cat

    bg命令可以让某个停止的作业在后台继续运行,也需要给该作业的进程组的每个进程发SIGCONT信号。cat进程继续运行,又要读终端输入,然而它在后台不能读终端输入,所以又收到SIGTTIN信号而停止。
        $ bg %1
        [1]+ cat &
        
        [1]+  Stopped                 cat

用kill命令给一个停止的进程发SIGTERM信号,这个信号并不会立刻处理,而要等进程准备继续运行之前处理,默认动作是终止进程。但如果给一个停止的进程发SIGKILL信号就不同了。
        $ ps
          PID TTY          TIME CMD
         6994 pts/0    00:00:05 bash
        11022 pts/0    00:00:00 cat
        11023 pts/0    00:00:00 ps
        $ kill 11022
        $ ps
          PID TTY          TIME CMD
         6994 pts/0    00:00:05 bash
        11022 pts/0    00:00:00 cat
        11024 pts/0    00:00:00 ps
        $ fg %1
        cat
        Terminated

SIGKILL信号既不能被阻塞也不能被忽略,也不能用自定义函数捕捉,只能按系统的默认动作立刻处理。与此类似的还有SIGSTOP信号,给一个进程发SIGSTOP信号会使进程停止,这个默认的处理动作不能改变。这样保证了不管什么样的进程都能用SIGKILL终止或者用SIGSTOP停止,当系统出现异常时管理员总是有办法杀掉有问题的进程或者暂时停掉怀疑有问题的进程。
        $ cat &
        [1] 11121
        $ ps
          PID TTY          TIME CMD
         6994 pts/0    00:00:05 bash
        11121 pts/0    00:00:00 cat
        11122 pts/0    00:00:00 ps
        
        [1]+  Stopped                 cat
        $ kill -KILL 11121
        [1]+  Killed                  cat

上面讲了如果后台进程试图从控制终端读,会收到SIGTTIN信号而停止,如果试图向控制终端写呢?通常是允许写的。如果觉得后台进程向控制终端输出信息干扰了用户使用终端,可以设置一个终端选项禁止后台进程写。
        $ cat testfile &
        [1] 11426
        $ hello
        
        [1]+  Done                    cat testfile
        $ stty tostop
        $ cat testfile &
        [1] 11428
        
        [1]+  Stopped                 cat testfile
        $ fg %1
        cat testfile
        hello

首先用stty命令设置终端选项,禁止后台进程写,然后启动一个后台进程准备往终端写,这时进程收到一个SIGTTOU信号,默认处理动作也是停止进程。

    8、stdin,stdout,stderr默认是挂在控制终端上的,但可以重定向。即使被重定向,可以open("/dev/tty", O_RDWR )来打开控制终端(如果有的话,setsid后,没有设置控制终端的话,会话没有控制终端),来进行输入输出。/dev/tty就是指向会话的控制终端,如果打开失败,说明会话没有控制终端。
    9、终端设备有输入队列和输出队列缓冲区。终端可以设置为回显(Echo)模式,在这种模式下,输入队列的每个字符既送给用户程序也送给输出队列,所以程序会收到字符,我们也可以在屏幕上看到该字符的回显。直接打开/dev/tty,写入的话,不会有回显,所以输入密码多直接用/dev/tty来直接读取数据,同时屏幕上不会显示密码明文。shell中也可以用read </dev/tty的方式来实现。这也是为什么ssh的密码不能用重定向来指定。 sshpass就是利用了ssh的这个特性,修改了程序的控制终端为一个从伪终端,通过读写主伪终端,实现自动输入密码。
    10、会话可以没有控制终端,setsid后,没有设置控制终端的话,就没有控制终端。没有控制终端只是不能接收控制字符,输入输出还是挂在原来终端上的,所以执行setsid后,printf等还是可以在原终端输出。想要修改输入输出的话,是使用dup2函数,dup2(fd,STDIN_FILENO)之类的操作,可以将STDIN_FILENO修改为fd,telnetd网络登陆是使用这种方法:
        打开一个伪终端主设备,fork一个子进程,执行setsid新建会话断开控制终端,打开从伪终端设备当成新控制终端,同时用dup2,将STDIN_FILENO/STDOUT_FILENO/STDERR_FILENO挂在从伪终端设备上,然后exec执行login,这样login所有的输入输出就都挂在了伪终端上,telnetd进程就可以和login程序进行交互。验证成功后,login会exec /bin/bash,同样的,交互会通过伪终端进行。
    11、用户网络登陆是通过伪终端来做为控制终端的,用who命令可以看到现在登陆的用户和使用的从伪终端:
        csibma01:/data02/home/wangfeng/rta2/test/sshpass> who
        buildall    pts/1       Jan 14 23:35     (10.70.97.153)  
        test        pts/2       Jan 23 10:32     (10.70.97.99)

可以使用这个从伪终端来进行通信,也就是说echo "How are you?" > /dev/pts/2,则test用户就会在屏幕上显示相应字符。cat </dev/pts/2则test用户的输入就重定向到了我们的cat上,test用户的命令不会被shell接收到。

你可能感兴趣的:(ssh,终端,sshpass,UNIXLINUX)