パスワードを忘れた? アカウント作成
74610 journal

novaの日記: えくすぺくと

日記 by nova

 expect を利用して、cron 経由で scp コマンドを発行する際のメモです。 本来なら expect なんぞ使わずに、パスフレーズ無しの鍵を作るべきなのでしょうが、 送り先のサーバが鍵交換に対応してないので。私の権限が及ぶモノでは無いので、設定変えられないし。

 まずは expect が動かないとハナシになりません。fedora は default でインストールされていますが、 今回使う RHEL ES 4 にはインストールされていません。up2date で入れようかと思ったのですが…… そういや認証が必要なんだっけ。……資料探すのメンドイのでソースで入れよう。

 うにっくすさんの覚え書き - Expect - インストール を参考にインストールしました。

 expect を使用する際には、使いたいコマンドがどんなメッセージでどんな入力を要求するのか事前に調べておく必要が有ります。 「この文字列が現れたらコレを送れ」という処理の繰り返しになりますので。 手動で scp を実行した場合は以下のような対応になります。

 初めての接続の時:

  # scp ./zzz.utf nova@172.16.5.10:/home/nova
  The authenticity of host '172.16.5.10 (172.16.5.10)' can't be established.
  RSA key fingerprint is **:**:**:**:**:**:**:**:**:**:**:**:**:**:**:**.
  Are you sure you want to continue connecting (yes/no)? yes
  Warning: Permanently added '172.16.5.10' (RSA) to the list of known hosts.
  nova@172.16.5.10's password:
  zzz.utf                                                 100%  115     0.1KB/s   00:00

 二回目以降の接続の時:

  # scp ./zzz.utf nova@172.16.5.10:/home/nova
  nova@172.16.5.10's password:
  zzz.utf                                                 100%  115     0.1KB/s   00:00

 以上の結果から、必要な処理は以下の二点。

  • 「Are you sure you want to continue connecting (yes/no)?」って聞かれたら、「yes」と答える。
  • 「password:」って聞かれたら、パスワードを入力。

 コレを expect に答えさせれば自動化出来るワケですね。 google 先生に聞いて、以下のシェルスクリプトを書きました。 nova アカウントのパスワードは "hahaha" と仮定します。

  #!/bin/sh

  expect -c "
  spawn scp /home/nova/zzz.utf nova@172.16.5.10:/home/nova
  expect {
    \"Are you sure you want to continue connecting (yes/no)?\" {
      send \"yes\r\"
      expect \"password:\"
      send \"hahaha\r\"
    } \"password:\" {
      send \"hahaha\r\"
    }
  }
  interact
  "

 実行してみると……ちゃんと送れますね。cron に仕込んでみると……送れない。 ……やっぱパクって切り貼りじゃダメか。 Manpage of EXPECT を読んで、各コマンドの役割を確認。

  • spawn

    コマンドを実行するプロセスを生成する。

  • expect

    プロセスの出力をパターンマッチングする。

  • send

    文字列をプロセスに送る。

  • interact

    プロセスの制御をユーザに渡す。

 ……プロセスの制御をユーザに渡す?それは端末に制御を引き継がせるというコト? ……そりゃ cron では使えないわな。誰も引き継いでくれないもの。

 んで、改良したのがコレ。

  #!/bin/sh

  expect -c "
  spawn scp /home/nova/zzz.utf nova@172.16.5.10:/home/nova
  expect {
    \"Are you sure you want to continue connecting (yes/no)?\" {
      send \"yes\r\"
      expect \"password:\"
      send \"hahaha\r\"
    } \"password:\" {
      send \"hahaha\r\"
    }
  }
  expect {
    \"denied\" { exit 0 } \
    \"100%\" { exit 1 }
  }
  "

 パスワードの入力後に更に判定を入れました。コレで「denied」か「100%」が出るまで続きます。

 転送中は転送率が表示されるので、この条件なら転送終了時に "exit 1" を実行して抜けます。 尚、当然ですがこの条件ではファイル名に「100%」って文字列を含んではイケマセン。 ファイル名が表示された瞬間に抜けますので。「denied」もダメだぞ。

 認証に失敗した場合は「denied」にマッチし、"exit 0" を実行して抜けます。 因みに手動での認証失敗時の対応はこんな感じ。

  # scp ./zzz.utf nova@172.16.5.10:/home/nova
  nova@172.16.5.10's password:
  Permission denied, please try again.
  nova@172.16.5.10's password:

 やっとこ動くようになったので、1.2GB のファイルでテスト。…………途中で止まるんですが? 何で?「denied」も「100%」も出てないダロ?……ログを取ろう。 log_file コマンドでログが取れます。書式は "log_file [ログファイル名]" です。因みに常に追記モードです。 …………「denied」も「100%」も出てないナ。何で落ちるんだろ? 因みに戻り値は 0 だが……「denied」にマッチしてるとは思えない。 「denied」にマッチした時は "exit 2" を実行するようにしてみると……やっぱ 0 だ。 「denied」も「100%」もマッチしてない。 …………兎に角、片っ端から調べるしかないな。


 ………………分かりました。spawn で生成されるプロセスの寿命が default で 20 秒なんです。 ……そんなのアリですか。まぁ、無尽蔵にプロセスを増殖させない為の処置なんでしょうケド。 プロセスの寿命は、"set timeout [生かしたい秒数]" で指定出来ます。

 そんなワケで最終的には以下の形に成りました。

  #! /bin/sh

  item=/home/nova/zzz.utf
  targetPath=@172.16.5.10:/home/nova
  user=nova
  passwd=hahaha
  alive_time=3600
  logFile=/home/nova/scp.log

  echo `date` >> $logFile

  expect -c "
    log_file $logFile
    set timeout $alive_time
    spawn scp $item $user$targetPath
    sleep 1
    expect {
      \"Are you sure you want to continue connecting (yes/no)?\" {
        send \"yes\r\"
        expect \"password:\"
        send \"$passwd\r\"
      } \"password:\" {
        send \"$passwd\r\"
      }
    }
    expect {
      \"denied\" { exit 2 } \
      \"100%\" { exit 1 }
    }
  "

  ret=$?

  if [ $ret -eq 0 ]
    then
      echo "abort" >> $logFile
    else
      if [ $ret -eq 1 ]
        then
          echo "done" >> $logFile
        else
          if [ $ret -eq 2 ]
            then
              echo "fail" >> $logFile
          fi
      fi
  fi

  exit $ret

 ……転送が一時間で終わらないとアボートしますがね。 "set timeout -1" で不死身のプロセスを作るコトも出来るんですが……それもビミョー。 要件に因りますが、戻り値見て異常が有ればアラートメールでも送れば十分でしょう。

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
typodupeerror

ソースを見ろ -- ある4桁UID

読み込み中...