#!/usr/bin/env ruby
require "rubygems"
require "net/ssh"
require "logger"
# IP、ユーザー名、ユーザーのパスは適宜変更のこと
@ssh = Net::SSH.start("192.168.0.100", "tester", {:password => "tester_pass"})
#@ssh.logger = Logger.new($stdout, Logger::INFO)
def su_exec(command)
# ch.request_pty(:modes => disable_echoback) での出力:
# ---------------------------------------------------
# root@machine:~# root
# root@machine:~# [入力待ち、Channel#eof! は効かない]
# ---------------------------------------------------
#
# ch.request_pty (引数なし)での出力:
# ---------------------------------------------------
# whoami
# root@machine:~# whoami
# root
# root@machine:~# [入力待ち、Channel#eof! 効かず]
# ---------------------------------------------------
#
# 何とかしてrootで実行したコマンドの結果(標準出力・エラー出力・戻り値)だけを得て、SSHのチャンネルを閉じたい
#
# 出入力が多段状態?
# user(stdin) -> rootのプロセスに"whoami"の文言伝わる(stdoutにも出る) -> コマンド実行 -> rootにコマンド実行結果出る -> rootプロセス入力待ち みたいな
# よくわかってない orz
# rootのパスは適当
expect("su -", ["パスワード:", "root_pass"]) {|root_channel|
root_channel.send_data "#{command}" + "\n"
root_channel.eof!
}
end
# expect "su - ", ["xxxx", "yyyy"] {|root_channel| ch.send_data "whoami" + "\n"} # エンター必須、ターミナル状態なので
# expect "passwd", ["ssword:", "flour"], ["word:", "tenpura-ko"], ["rd:", "tenpura-ko"]
def expect(first_command, *expect_reaction_list, &expect_passed_block)
expected = true
yielded = false
disable_echoback = [[Net::SSH::Connection::Term::ECHO, 0]]
@ssh.open_channel "session" do |ch, success|
#ch.request_pty(:modes => disable_echoback)
ch.request_pty
# callback、(リモートのstdoutが出切ったあとじゃなくて)受信パケットが一定量溜まると発生する
ch.on_data do |ignore, stdout_data|
# LANG=ja_JP su -
# パスワード: ← この出力でincompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (Encoding::CompatibilityError)
#
# LANG=Cならforce_encoding不要
converted_stdout = stdout_data.force_encoding("UTF-8")
if expected
# passwdなど、2回以上の入力が必要なものもあるから
expect_reaction = expect_reaction_list[0]
expect = expect_reaction[0]
reaction = expect_reaction[1]
if stdout_data =~ /#{expect}/
ch.send_data reaction + "\n"
expect_reaction_list.shift
end
expected = false if expect_reaction_list.count == 0
else
# 出力の長さ次第で2周以上しえるけど、2回以上yieldされてもこまる
if block_given? && !yielded
yield ch
yielded = true
else
print stdout_data
end
end
end
ch.on_extended_data do |ignore, stderr_data|
$stderr.print stderr_data
end
ch.send_channel_request "shell" do |ignore, success|
raise "failed to execute shell" unless success
ch.send_data first_command + "\n"
end
end
@ssh.loop
end
# (まずは)こういう書式を想定、まだそこまで行けてない
# 可能なら、cifsとかnfsマウントして、ffmpegとgst-launch-1.0をぶったたいて分散エンコをキメてアニメ生活を快適にする
# SSHのDSLにしたいんだけど……
#
# wrapped = SSHWrapper.new(ip, user, {:password => "xxx", :root_password => "yyy"}
#
# # rootで実行
# wrapped.su do
# wrapped.exec "echo VDPAU_DRIVER=va_gl >> /etc/environment"
# wrapped.expect "passwd test", ["password:", "aaa"], ["password:", "bbb"]
# end
#
# # 一般ユーザー
# wrapped.exec "ls /etc"
#
# # sudo
# wrapped.sudo "smbpasswd ebiten"
#
# su叩いたらフラグを立てて、裏で "su -c \"#{Shellwords.espace(command)\"" に切り替える手はあるが
# 入力されたコマンドの \ や " がおかしくなりそうなので避けたい
su_exec("whoami")
@ssh.close