gpt4 book ai didi

Python select() 在 forkpty() 之后不等待终端输入

转载 作者:太空狗 更新时间:2023-10-29 12:28:44 25 4
gpt4 key购买 nike

我正在尝试编写一个 python 脚本,它将通过 ssh 自动登录到远程主机并更新用户密码。由于 ssh 要求它从终端获取输入,因此我使用 os.forkpty(),在子进程中运行 ssh 并使用父进程通过伪终端发送命令输入。这是我目前所拥有的:

import os, sys, time, getpass, select, termios

# Time in seconds between commands sent to tty
SLEEP_TIME = 1
NETWORK_TIMEOUT = 15

#------------------------------Get Passwords------------------------------------

# get username
login = getpass.getuser()

# get current password
current_pass = getpass.getpass("Enter current password: ")

# get new password, retry if same as current password
new_pass = current_pass
first_try = True

while new_pass == current_pass:
if first_try:
first_try = False
else:
# New password equal to old password
print("New password must differ from current password.")

# Get new password
new_pass = getpass.getpass("Enter new password: ")
new_pass_confirm = getpass.getpass("Confirm new password: ")
while new_pass != new_pass_confirm:
# Passwords do not match
print("Passwords do not match")
new_pass = getpass.getpass("Enter new password: ")
new_pass_confirm = getpass.getpass("Confirm new password: ")

#------------------------------End Get Passwords--------------------------------

ssh = "/usr/bin/ssh" # ssh bin location
args = ["ssh", login + "@localhost", "-o StrictHostKeyChecking=no"]

#fork
pid, master = os.forkpty()
if pid == 0:
# Turn off echo so master does not need to read back its own input
attrs = termios.tcgetattr(sys.stdin.fileno())
attrs[3] = attrs[3] & ~termios.ECHO
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, attrs)
os.execv(ssh, args)
else:
select.select([master], [], [])
os.write(master, current_pass + "\n")
select.select([master], [], [])
os.write(master, "passwd\n")
select.select([master], [], [])
os.write(master, current_pass + "\n")
select.select([master], [], [])
os.write(master, new_pass + "\n")
select.select([master], [], [])
os.write(master, new_pass + "\n")
select.select([master], [], [])
os.write(master, "id\n")
select.select([master], [], [])
sys.stdout.write(os.read(master, 2048))
os.wait()

该脚本会提示用户输入他/她的当前密码和新密码,然后 fork 并向 ssh 登录提示和 passwd 提示发送适当的响应。

我遇到的问题是选择系统调用的行为与我预期的不同。他们似乎根本没有阻止。我在想我误解了 select 与 pty 的主端一起工作的方式。

如果我用 time.sleep(1) 替换它们,脚本工作正常,但我不想依赖那个解决方案,因为我不能总是保证网络会在短时间内响应,而且我不想让它变得荒谬到永远(我打算用它来以编程方式登录到多个服务器以更新密码)

有没有办法可靠地轮询 pty 的主端以等待从属端的输出?

注意:我意识到使用 sshpass 和 chpasswd 之类的东西可以更好地解决这个问题,但我所处的环境不能以 root 身份运行,而且可用的实用程序很少。值得庆幸的是 python 是。

最佳答案

select 不读取任何数据;它只是阻塞,直到数据可供读取。

因为在第一个select之后你没有读取任何数据,缓冲区中仍然会有数据供你读取,所以任何后续的select都不会 block 。

在再次调用select 之前,您需要读取缓冲区中的数据。在不阻塞的情况下执行此操作意味着您可能必须将文件设置为非阻塞模式(我不知道如何在 Python 中执行此操作)。


通过 SSH 提供密码的更好方法是 use the --stdin flag if your passwd supports it ,并直接通过 SSH 而不是通过创建的 shell 运行命令。

handle = subprocess.Popen(["ssh", login + "@localhost", "-o StrictHostKeyChecking=no", "passwd --stdin"])
handle.communicate("\n".join([oldpass, newpass, newpass, ""]))

关于Python select() 在 forkpty() 之后不等待终端输入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33711160/

25 4 0
文章推荐: Javascript UI 渲染技术
文章推荐: jquery - 如果为空,如何发布空的