私は 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 つのことが気になります。
libexpect のマンページを見ると、exp_spawnv が新しいプロセスをフォークし、FILE* を介して通信できることがわかります。フォークで何か悪いことが起こったので、SIGSEGV シグナルが受信されたと思いますか?
バックトレースのその行 (警告: 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をよろしくお願いいたします。