我们线上的机器登录都是通过每5秒钟获取一次密码的方式登录的,今天要登录线上的一组机器,发现去不到root的password了,一直显示错误,后通过console进去获取到密码才得以登录进去,因为系统里用的是/usr/bin/mkpasswd的方式来生成密码的,手工执行mkpasswd root, 发现没有返回,也没有报错,打开文件看看源码是怎么写的,发现是一个shell脚本,是通过系统的passwd,/dev/random获取随机数字,和expect来实现每次请求的时候都更改用户的密码并回显到调用的网页上。抄家伙,看看他是哪里出问题了

strace -c -T -f /usr/bin/mkpasswd root

Process 26921 attached
Process 26922 attached (waiting for parent)
Process 26922 resumed (parent 26921 ready)
Process 26923 attached
Process 26923 detached
Process 26922 detached
^CProcess 26921 detached
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00    0.000736           5       141           read
  0.00    0.000000           0         4           write
  0.00    0.000000           0       136        16 open
  0.00    0.000000           0       140           close
  0.00    0.000000           0        53        28 stat
  0.00    0.000000           0        92           fstat
  0.00    0.000000           0        16           lstat
  0.00    0.000000           0         9         6 lseek
  0.00    0.000000           0       172           mmap
  0.00    0.000000           0        84           mprotect
  0.00    0.000000           0        34           munmap
  0.00    0.000000           0        19           brk
  0.00    0.000000           0       100         2 rt_sigaction
  0.00    0.000000           0        24           rt_sigprocmask
  0.00    0.000000           0         1           rt_sigreturn
  0.00    0.000000           0        52        23 ioctl
  0.00    0.000000           0        27        17 access
  0.00    0.000000           0         3           pipe
  0.00    0.000000           0         3           select
  0.00    0.000000           0         2           dup2
  0.00    0.000000           0         3           getpid
  0.00    0.000000           0         7           socket
  0.00    0.000000           0         6         6 connect
  0.00    0.000000           0         3           clone
  0.00    0.000000           0        14         9 execve
  0.00    0.000000           0         3         1 wait4
  0.00    0.000000           0         3           uname
  0.00    0.000000           0        41         1 fcntl
  0.00    0.000000           0         2           readlink
  0.00    0.000000           0         4           getrlimit
  0.00    0.000000           0        14           getuid
  0.00    0.000000           0        10           getgid
  0.00    0.000000           0        12           geteuid
  0.00    0.000000           0        10           getegid
  0.00    0.000000           0         2           getppid
  0.00    0.000000           0         2           getpgrp
  0.00    0.000000           0         1           setsid
  0.00    0.000000           0         2           statfs
  0.00    0.000000           0         5           arch_prctl
------ ----------- ----------- --------- --------- ----------------

100.00    0.000736                  1256       109 total

-c表示打印出统计信息,-T显示没个调用所消耗的时间,-f表示跟着子进程,从上面结果可以看到到了read的地方就一直卡在那里不动,一开始以为是没有读取到用户名,后来代码上加上echo $1,是可以打印出来的,那strace显示是在read的地方卡住的,那问题到底出在哪里了呢,在继续往下看代码,突然想到既然用的expect,那肯定是执行passwd的时候read屏幕回显的字符,代码段在这里

 spawn $prog $user

197      expect {

198          "assword*: " {

199              # some systems say "Password (again):"

200              send "$password \r "

201              exp_continue

202          }

203      }

赶紧回shell手工执行以下passwd,发现显示乱码了,echo $LANG,nnd,竟然是zh_CN, 我们线上环境的服务器都是en_US.UTF-8的呀,赶紧加上一条export到.bashrc里面,至此问题解决,就是由于系统语言是中文,导致expect执行的之后无法读取想要匹配的字符导致没法获取到密码。

文章涉及的脚本文件请到这里下载https://github.com/sangrealest/shell/blob/master/mkpasswd