あけましておめでとうございます。
元旦といえば、そうプログラミングです。
ということで、Python で sshpass を再現の話。
os.forkpty というものがあるようだ。
これだよコレ、似たようなことを考える人っているもんだ。
がんばって sshpass と完全に同じコードにしなくてもよかった。
というか ptmx では上手くいかない、擬似端末ならこっちでもいいはず。
Python3 に書き換えしなきゃね。
色々問題はあるけどなんとかなったコードをとりあえず。
#!/usr/bin/env python3
import os, sys, signal, time
# var
HOSTNAME = 'username@hostname'
PASSWORD = '********'
# Ctrl+C
signal.signal(signal.SIGINT, signal.SIG_DFL)
pid, fd = os.forkpty()
def cmd(s):
'''
長い出力の場合分割される場合がある
os.read はこの場合ループにすると値を戻さずフリーズする
しかたがないので空打ちの場合は残りの読み込みにしている
'''
if s:
os.write(fd, f'{s}\n'.encode('utf-8'))
time.sleep(1)
res = os.read(fd, 1024).decode()
# 一行目を取り除いて表示
sys.stdout.write(res[res.find('\n')+1 : ])
else:
res = os.read(fd, 1024).decode()
sys.stdout.write(res)
sys.stdout.flush()
if pid == 0:
#os.setsid() # Error
os.execvp('ssh',['ssh', HOSTNAME])
# 親プロセスに切り替わるので以下は実行されない
print('execvp Error!')
# レスポンスが遅いと表示されないけどログインは可能
output = os.read(fd, 1024)
sys.stdout.write(output.decode())
sys.stdout.flush()
# パスいワード
os.write(fd, f'{PASSWORD}\n'.encode('utf-8'))
# 只の時間稼ぎ
time.sleep(1)
res = os.read(fd, 1024).decode()
# 最初のプロンプトが表示できないので仮プロンプト
print(f'{res}First Command > ', end='')
# exit で終了
while True:
s = input()
if s == 'exit':
break
cmd(s)
os.write(fd, 'exit\n'.encode('utf-8'))
print('__DONE__')
よしこれで sshpass 不要でパスワード入力ができる。
しかし time.sleep を駆使するしかないのかな?
今のところこんな感じ。
調子こいて pysshpass みたいなものを作ろうと思ったけど…
os.forkpty では setsid できないし ptmx だと暴走するしetc…
我が Macbook Air の CPU ファン全開音なんて初めて聞いたよ。
ま、必要ないか。
しかし思っていたより Fedora と macOS では使える関数が違う。
os.fsync は macOS で問題ないけど Fedora は何をやってもエラー。
fcntl でノンブロッキングI/O は macOS ではエラー出まくり。
他の Linux や BSD では、なんて考えたくないな。
