Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1910727
  • 博文数量: 376
  • 博客积分: 2147
  • 博客等级: 大尉
  • 技术积分: 3642
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-06 10:47
文章分类

全部博文(376)

文章存档

2019年(3)

2017年(28)

2016年(15)

2015年(17)

2014年(182)

2013年(16)

2012年(115)

我的朋友

分类: LINUX

2012-03-09 11:39:00

【自动修改 Linux 用户密码】今天学习了一下如何使用伪终端和交互式应用程序通信

其实主要就是 openpty/forkpty/login_tty 三个函数
以下内容为程序代码:
flw@debian:~/study$ ./tpasswd chinaunix perlchina
pty name: /dev/pts/1
Changing password for flw.
(当前)UNIX 口令:chinaunix
输入新的 UNIX 口令:perlchina
重新输入新的 UNIX 口令:perlchina
passwd:已成功更新密码
flw@debian:~/study$ ./tpasswd perlchina abcdef
pty name: /dev/pts/1
Changing password for flw.
(当前)UNIX 口令:perlchina
输入新的 UNIX 口令:abcdef
重新输入新的 UNIX 口令:abcdef
Bad: new password is too simple
输入新的 UNIX 口令:abcdef
重新输入新的 UNIX 口令:abcdef
Bad: new password is too simple
输入新的 UNIX 口令:abcdef
重新输入新的 UNIX 口令:abcdef
Bad: new password is too simple
passwd:鉴定令牌操作错误
passwd: password unchanged


[code]flw@debian:~/study$ cat tpasswd.c
# include
# include
# include
# include
# include
# include
# include

void do_passwd( int pty, const char *old_passwd, const char *new_passwd );

int main( int argc, char *argv[] )
{
    int pty, slave;
    char pty_name[PATH_MAX];
    int ret;
    pid_t child;

    if ( argc < 3 ){
        fprintf( stderr, "Usage: %s \n", argv[0] );
        exit( EXIT_FAILURE );
    }

    ret = openpty( &pty, &slave, pty_name, NULL, NULL );//打开一个伪终端
    if ( ret == -1 ){
        perror( "openpty" );
        exit( EXIT_FAILURE );
    }

    child = fork();
    if ( child == -1 ){
        perror( "fork" );
        exit( EXIT_FAILURE );
    }
    else if ( child == 0 ){
        close( pty );
        login_tty( slave );//把slave复制到0,1,2,并且关闭slave,这样子进程标准输入输出都为伪终端
        execl( "/usr/bin/passwd", "passwd", NULL );
    }

    close( slave );
    printf( "pty name: %s\n", pty_name );

    do_passwd( pty, argv[1], argv[2] );
    exit( EXIT_SUCCESS );
}

void do_passwd( int pty, const char *old_passwd, const char *new_passwd )
{
    char buffer[80];
    int ret;
    fd_set reads;

    int input_old_passwd = 0;

    while(1){
        FD_ZERO( &reads );
        FD_SET( pty, &reads );
        ret = select( pty+1, &reads, NULL, NULL, NULL );
        if ( ret == -1 ){
            perror( "select" );
            break;
        }

        ret = read( pty, buffer, sizeof(buffer) );
        if ( ret <= 0 ){
            break;
        }

        write( fileno(stdout), buffer, ret );
        fflush( stdout );

        if ( buffer[ret-1] == '\n' ){
            continue;
        }

        if ( !input_old_passwd ){
            write( pty, old_passwd, strlen(old_passwd) );
            write( pty, "\n", 1 );
            write( fileno(stdout), old_passwd, strlen(old_passwd) );
            input_old_passwd = 1;
        }
        else{
            write( pty, new_passwd, strlen(new_passwd) );
            write( pty, "\n", 1 );
            write( fileno(stdout), new_passwd, strlen(new_passwd) );
        }
    }

    return;
}
flw@debian:~/study$[/code]

2008-4-14 18:55LinuxKen
学习,代码写得好漂亮.:)

2008-4-14 18:58flw
因为 forkpty 实际上是 openpty/fork/login_tty 三个函数的组合,因此主函数也可以简写成这样:
[code]int main( int argc, char *argv[] )
{
    int pty, slave;
    char pty_name[PATH_MAX];
    int ret;
    pid_t child;

    if ( argc < 3 ){
        fprintf( stderr, "Usage: %s \n", argv[0] );
        exit( EXIT_FAILURE );
    }

    child = forkpty( &pty, pty_name, NULL, NULL );
    if ( child == -1 ){
        perror( "fork" );
        exit( EXIT_FAILURE );
    }
    else if ( child == 0 ){
        execl( "/usr/bin/passwd", "passwd", NULL );
    }

    printf( "pty name: %s\n", pty_name );

    do_passwd( pty, argv[1], argv[2] );
    exit( EXIT_SUCCESS );
}[/code]


如同标题所说,本程序旨在演示如何操纵伪终端和交互式应用程序通信,
因此对于 passwd 实用工具的输出解析并不多,只是做了一些简单的判断。

事实上,根据输入内容的不同,passwd 实用工具的输出信息多达十多种,PAM 的到来使得 passwd 的输出更加复杂,
因此如果希望能够自动控制 passwd,那么还是用 expect/perl 等工具较为方便。

另外需要说明的一点是,自动控制交互式应用程序运行时,尽量运行其英文版本,这样信息会规范一些从而有利于捕获并匹配。
中文或者其它语种的信息不仅要考虑到编码的问题,而且信息用语经常会有变化。

通过设定环境变量 LANG=C,可以使 passwd 实用工具的输出变成英文。
阅读(2579) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~