8

私は bash または ssh でいくつかのプロセスを自動化する必要があるプロジェクトで忙しいので、libexpect.so ライブラリを使用することにしました。libexpect が何かわからない場合は、C++ プログラムで使用できる expect 拡張機能が提供されます。expect は、ssh などの自動化されたスクリプトを実行できる単なるプログラムです。そのため、どこかで ssh を試行するスクリプトを実行できます...パスワード プロンプトが見つかれば、パスワードを送信することを期待できます。

私の問題は、プログラムを実行すると、非常に単純なプログラムであっても、セグメンテーション違反が発生し、gdb を使用して、exp_spawnv と呼ばれる libexpect.so の関数に絞り込むことです。

ライブラリを正しくリンクしたことはわかっています。コンパイルは正常に行われ、ubuntuでコンパイルして実行すると問題全体が存在しませんが、arch linuxのインストールで後で詳しく説明するセグメンテーション違反が発生します。Arch でビルドする理由は、最終的にはほとんどのディストリビューションでプロジェクトをビルドできるようにしたいからです。

私の考えでは、アーチのインストールには、おそらくパイプ、フォークなど、exp_spawnv 関数が呼び出されたときに失敗するパーミッションがあると思います。

私が変なことをしていないことを証明するために、説明のために簡単な main.cpp を示します。

#include <tcl8.5/expect.h>

int main()
{
  FILE* file = exp_popen("bash");
}

したがって、これはこれまでに作成された最も単純な期待プログラムです。ここに私がコンパイルしてリンクしています。

$ g++ -ggdb -c main.cpp

main.cpp: 関数 'int main()' 内:

main.cpp:5:32: 警告: 文字列定数から 'char*' への非推奨の変換 [-Wwrite-strings]

$ g++ main.o -lexpect -o mainprog

それで、実行可能なmainprogを取得しました...実行しているだけで、セグメンテーション違反が発生し、他には何もありません。

gdb で mainprog を実行すると、exp_spawnv にセグメント障害があることがわかります。最後にバックトレースを使用してgdbで行ったことを次に示します。

(gdb) 実行

プログラムの開始: /home/user/testlibexpect/mainprog

警告: linux-vdso.so.1 の共有ライブラリ シンボルを読み込めませんでした。

「set solib-search-path」または「set sysroot」が必要ですか?

プログラム受信信号 SIGSEGV、セグメンテーション違反。

/usr/lib/libexpect.so からの exp_spawnv () の 0x00007ffff7bc8836

(gdb) バックトレース

0 0x00007ffff7bc8836 in exp_spawnv () from /usr/lib/libexpect.so

1 0x00007ffff7bc8cb4 in exp_spawnl () from /usr/lib/libexpect.so

2 0x00007ffff7bc8d01 in exp_popen () from /usr/lib/libexpect.so

3 0x000000000040069e in main () at main.cpp:5

2 つのことが気になります。

  1. libexpect のマンページを見ると、exp_spawnv が新しいプロセスをフォークし、FILE* を介して通信できることがわかります。フォークで何か悪いことが起こったので、SIGSEGV シグナルが受信されたと思いますか?

  2. バックトレースのその行 (警告: linux-vdso.so.1 の共有ライブラリ シンボルをロードできませんでした。) は怪しいですか?

要約すると、私の質問は、この問題を解決するために何を調べる必要があるかということです。ソースからexpectライブラリをビルドしようとしましたが、archパッケージマネージャーpacmanで取得しました...問題が解決しないので、意味がわかればライブラリビルドが破損しているとは思いません。

編集:私の懸念のポイント2は、私が行った調査によると問題ではなく、単なる表面的なものです。

日食からの分解は以下のとおりです。

00007ffff7bc87c6:   mov 0x20c68b(%rip),%rax        # 0x7ffff7dd4e58
00007ffff7bc87cd:   mov (%rax),%rax
00007ffff7bc87d0:   test %rax,%rax
00007ffff7bc87d3:   je 0x7ffff7bc87d7 <exp_spawnv+935>
00007ffff7bc87d5:   callq *%rax
00007ffff7bc87d7:   mov %r12,%rsi
00007ffff7bc87da:   mov %rbp,%rdi
00007ffff7bc87dd:   callq 0x7ffff7bb2330 <execvp@plt>
00007ffff7bc87e2:   callq 0x7ffff7bb1720 <__errno_location@plt>
00007ffff7bc87e7:   mov 0x24(%rsp),%edi
00007ffff7bc87eb:   mov %rax,%rsi
00007ffff7bc87ee:   mov $0x4,%edx
00007ffff7bc87f3:   xor %eax,%eax
00007ffff7bc87f5:   callq 0x7ffff7bb1910 <write@plt>
00007ffff7bc87fa:   mov $0xffffffff,%edi
00007ffff7bc87ff:   callq 0x7ffff7bb23d0 <exit@plt>
00007ffff7bc8804:   nopl 0x0(%rax)
00007ffff7bc8808:   xor %eax,%eax
00007ffff7bc880a:   movl $0x0,0x20dd3c(%rip)        # 0x7ffff7dd6550
00007ffff7bc8814:   callq 0x7ffff7bb1700 <exp_init_pty@plt>
00007ffff7bc8819:   xor %eax,%eax
00007ffff7bc881b:   callq 0x7ffff7bb2460 <exp_init_tty@plt>
00007ffff7bc8820:   lea -0x1c97(%rip),%rdi        # 0x7ffff7bc6b90
00007ffff7bc8827:   callq 0x7ffff7bb2540 <expDiagLogPtrSet@plt>
00007ffff7bc882c:   mov 0x20c555(%rip),%rax        # 0x7ffff7dd4d88
00007ffff7bc8833:   mov (%rax),%rax
00007ffff7bc8836:   mov 0x410(%rax),%rdi

私が思いついた答え

これが私が最終的に思いついた解決策です.szxの答えを受け入れました。なぜなら、私が探しているものを知っていれば些細なことだったからです。

//do not use TCL stubs as this is a main
#undef USE_TCL_STUBS


#include <iostream>
using std::cout;
using std::endl;

//headers that must be included when using expectTcl as an extension to c++ program
#include <stdio.h>
#include <stdlib.h>
#include <expectTcl/tcl.h>
#include <expectTcl/expect_tcl.h>
#include <expectTcl/expect.h>

//enums representing cases of what expect found in loop
enum{FOUNDSEARCH, PROMPT};

int main()
{
  /* initialise expect and tcl */
  Tcl_Interp *interp = Tcl_CreateInterp();

  if(Tcl_Init(interp) == TCL_ERROR)
    {
      cout << "TCL failed to initialize." << endl;
    }
  if(Expect_Init(interp) == TCL_ERROR)
    {
      cout << "Expect failed to initialize." << endl;
    }

  /* end of intialisation procedure */

  //open a shell with a pipe
  char shellType[] = "sh";
  FILE* fp = exp_popen(shellType);

  //should we exit from the loop which is studying sh output
  bool shouldBreak = false;
  //did we find the pwd
  bool foundSearch = false;
  //does it look like expect is working
  bool expectWorking = false;
  //did we receive a prompt...therefore we should send a command
  bool receivedPrompt = false;

  while(shouldBreak == false)
    {
      switch(exp_fexpectl(fp,
              exp_glob, "/tools/test*", FOUNDSEARCH,  //different
              exp_glob,"# ", PROMPT, //cases are shown here
              exp_end))  //that the expect loop could encounter
    {
    case FOUNDSEARCH:
      foundSearch = true;
      break;
    case PROMPT:
      if (receivedPrompt)
        {
          shouldBreak = true;
          expectWorking = true;
        }
      else
        {
          receivedPrompt = true;
          fprintf(fp, "%s\r", "pwd");
        }
      break;
    case EXP_TIMEOUT:
      shouldBreak = true;
      break;
    case EXP_EOF:
      shouldBreak = true;
      break;
    }

      //cout << "exp_match : " << exp_match << endl;
    }

  cout << endl;
  if (foundSearch)
    {
      cout << "Expect found output of pwd" << endl;
    }
  else
    {
      cout << "Expect failed to find output of pwd" << endl;
    }
  if(expectWorking)
    {
      cout << "The expect interface is working" << endl;
    }
  else
    {
      cout << "The expect interface is not working" << endl;
    }


  cout << "The test program successfully reached the end" << endl;
}

ここで行ったのは、expect/tcl を初期化して、szx が話していた問題を回避する方法を示したことだけです。次に、シェルが入力を求めるプロンプトが表示された場合に pwd を送信するという、典型的な期待のような問題を実行しました。次に、現在のディレクトリが機能していることを期待している場合。この種の構造は、ssh などで非常に便利です。どこかで sshing を自動化したい場合、何かをしてからそこから抜け出すとします。特に、数百回実行する必要があり、すべてのホストの信頼性を確認して毎回パスワードを入力したくない場合.

何らかの理由で、 ubuntuでこれを行う必要がなかったことに注意してください...おそらく、ソースからビルドせずに を使用したためapt-getです。ただし、私のプロジェクトではソースからビルドする必要があるため、http://www.linuxfromscratch.org/lfs/view/development/chapter05/tcl.html および http://www でそれを行うための非常に適切できちんとした方法を見つけまし。 .linuxfromscratch.org/lfs/view/development/chapter05/expect.html ... 実際、ウェブサイト全体が本当に便利に見えます。

今後ともszxをよろしくお願いいたします。

4

2 に答える 2

4

TclStubs *tclStubsPtrTcl 内で定義されているグローバル変数は、その構造体のメンバーとして定義されているものにアクセスしようとするとNULL発生します ( を参照)。exp_spawnvTcl_ErrnoMsgtcl.h

#ifndef Tcl_ErrnoMsg
#define Tcl_ErrnoMsg \
    (tclStubsPtr->tcl_ErrnoMsg) /* 128 */
#endif

私は、expect にも Tcl にも精通していませんが、上記は、初期化サブルーチン (存在する場合) を呼び出すか、手動で設定する必要があることを示唆しています。

于 2012-12-05T02:50:53.607 に答える
1

コンパイル時の警告が最も心配です。このインターフェイスでは、書き込み可能な文字列を渡す必要があるように見えますが、文字列定数を渡します。実際に書き込みを行うと、セグメンテーション違反が発生します。したがって、問題の良い候補のように見えます。

代わりに書き込み可能なバッファを作成してそれを渡そうとするとどうなりますか:

char name[] = "bash";
FILE* file = exp_popen(name);

更新: 私はあなたのプログラムをテストしました (上記の変更と、最後に「return 0;」を使用)。ライブラリが半分インストールされているなど、システムに何か問題があるのではないでしょうか? -static でリンクしたときにも失敗するかどうかを確認できます。これを行うと、コンパイル時にリンクされたライブラリが実行時に使用されるライブラリと同じであることが確実になります (コンパイル時に実行可能ファイルに含まれるため)。

于 2012-12-04T15:16:19.363 に答える