fork download
  1. #!/usr/bin/env ruby
  2.  
  3. require "rubygems"
  4. require "net/ssh"
  5. require "logger"
  6.  
  7. # IP、ユーザー名、ユーザーのパスは適宜変更のこと
  8. @ssh = Net::SSH.start("192.168.0.100", "tester", {:password => "tester_pass"})
  9. #@ssh.logger = Logger.new($stdout, Logger::INFO)
  10.  
  11. def su_exec(command)
  12.  
  13. # ch.request_pty(:modes => disable_echoback) での出力:
  14. # ---------------------------------------------------
  15. # root@machine:~# root
  16. # root@machine:~# [入力待ち、Channel#eof! は効かない]
  17. # ---------------------------------------------------
  18. #
  19. # ch.request_pty (引数なし)での出力:
  20. # ---------------------------------------------------
  21. # whoami
  22. # root@machine:~# whoami
  23. # root
  24. # root@machine:~# [入力待ち、Channel#eof! 効かず]
  25. # ---------------------------------------------------
  26. #
  27. # 何とかしてrootで実行したコマンドの結果(標準出力・エラー出力・戻り値)だけを得て、SSHのチャンネルを閉じたい
  28. #
  29. # 出入力が多段状態?
  30. # user(stdin) -> rootのプロセスに"whoami"の文言伝わる(stdoutにも出る) -> コマンド実行 -> rootにコマンド実行結果出る -> rootプロセス入力待ち みたいな
  31. # よくわかってない orz
  32.  
  33. # rootのパスは適当
  34. expect("su -", ["パスワード:", "root_pass"]) {|root_channel|
  35. root_channel.send_data "#{command}" + "\n"
  36. root_channel.eof!
  37. }
  38. end
  39.  
  40. # expect "su - ", ["xxxx", "yyyy"] {|root_channel| ch.send_data "whoami" + "\n"} # エンター必須、ターミナル状態なので
  41. # expect "passwd", ["ssword:", "flour"], ["word:", "tenpura-ko"], ["rd:", "tenpura-ko"]
  42. def expect(first_command, *expect_reaction_list, &expect_passed_block)
  43. expected = true
  44. yielded = false
  45.  
  46. disable_echoback = [[Net::SSH::Connection::Term::ECHO, 0]]
  47.  
  48. @ssh.open_channel "session" do |ch, success|
  49. #ch.request_pty(:modes => disable_echoback)
  50. ch.request_pty
  51.  
  52. # callback、(リモートのstdoutが出切ったあとじゃなくて)受信パケットが一定量溜まると発生する
  53. ch.on_data do |ignore, stdout_data|
  54. # LANG=ja_JP su -
  55. # パスワード: ← この出力でincompatible encoding regexp match (UTF-8 regexp with ASCII-8BIT string) (Encoding::CompatibilityError)
  56. #
  57. # LANG=Cならforce_encoding不要
  58. converted_stdout = stdout_data.force_encoding("UTF-8")
  59.  
  60. if expected
  61. # passwdなど、2回以上の入力が必要なものもあるから
  62. expect_reaction = expect_reaction_list[0]
  63. expect = expect_reaction[0]
  64. reaction = expect_reaction[1]
  65.  
  66. if stdout_data =~ /#{expect}/
  67. ch.send_data reaction + "\n"
  68. expect_reaction_list.shift
  69. end
  70.  
  71. expected = false if expect_reaction_list.count == 0
  72. else
  73. # 出力の長さ次第で2周以上しえるけど、2回以上yieldされてもこまる
  74. if block_given? && !yielded
  75. yield ch
  76. yielded = true
  77. else
  78. print stdout_data
  79. end
  80. end
  81. end
  82.  
  83. ch.on_extended_data do |ignore, stderr_data|
  84. $stderr.print stderr_data
  85. end
  86.  
  87. ch.send_channel_request "shell" do |ignore, success|
  88. raise "failed to execute shell" unless success
  89. ch.send_data first_command + "\n"
  90. end
  91. end
  92.  
  93. @ssh.loop
  94. end
  95.  
  96. # (まずは)こういう書式を想定、まだそこまで行けてない
  97. # 可能なら、cifsとかnfsマウントして、ffmpegとgst-launch-1.0をぶったたいて分散エンコをキメてアニメ生活を快適にする
  98. # SSHのDSLにしたいんだけど……
  99. #
  100. # wrapped = SSHWrapper.new(ip, user, {:password => "xxx", :root_password => "yyy"}
  101. #
  102. # # rootで実行
  103. # wrapped.su do
  104. # wrapped.exec "echo VDPAU_DRIVER=va_gl >> /etc/environment"
  105. # wrapped.expect "passwd test", ["password:", "aaa"], ["password:", "bbb"]
  106. # end
  107. #
  108. # # 一般ユーザー
  109. # wrapped.exec "ls /etc"
  110. #
  111. # # sudo
  112. # wrapped.sudo "smbpasswd ebiten"
  113. #
  114. # su叩いたらフラグを立てて、裏で "su -c \"#{Shellwords.espace(command)\"" に切り替える手はあるが
  115. # 入力されたコマンドの \ や " がおかしくなりそうなので避けたい
  116.  
  117. su_exec("whoami")
  118. @ssh.close
  119.  
Runtime error #stdin #stdout #stderr 0.03s 33304KB
stdin
Standard input is empty
stdout
Standard output is empty
stderr
/usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- net/ssh (LoadError)
	from /usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require'
	from prog.rb:4:in `<main>'