どうやら、同じシェルが同じサーバーへの複数のssh接続を起動した場合、与えられたコマンドを実行した後、戻りませんが、ハング(Stopped (tty input)
)します。説明する:
_#!/bin/bash
ssh localhost sleep 2
echo "$$ DONE!"
_
上記のスクリプトをバックグラウンドで複数回実行すると、スクリプトが終了することはありません。
_$ for i in {1..3}; do foo.sh & done
[1] 28695
[2] 28696
[3] 28697
$ ## Hit enter
[1] Stopped foo.sh
[2]- Stopped foo.sh
[3]+ Stopped foo.sh
$ ## Hit enter again
$ jobs -l
[1] 28695 Stopped (tty input) foo.sh
[2]- 28696 Stopped (tty input) foo.sh
[3]+ 28697 Stopped (tty input) foo.sh
_
system()
呼び出しを使用してssh
を起動した場合も同じ動作が発生します。system()
の代わりにPerlモジュールを使用する場合にも同じ問題が発生します。 _Net::SSH::Perl
_、_Net:SSH2
_、_Net::OpenSSH
_を試しました。Ssh接続のデバッグ情報に明らかに役立つものはありません。
_OpenSSH_7.5p1, OpenSSL 1.1.0f 25 May 2017
debug1: Reading configuration data /home/terdon/.ssh/config
debug1: Reading configuration data /etc/ssh/ssh_config
debug2: resolving "localhost" port 22
debug2: ssh_connect_direct: needpriv 0
debug1: Connecting to localhost [::1] port 22.
debug1: Connection established.
debug1: identity file /home/terdon/.ssh/id_rsa type 1
debug1: key_load_public: No such file or directory
debug1: identity file /home/terdon/.ssh/id_rsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/terdon/.ssh/id_dsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/terdon/.ssh/id_dsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/terdon/.ssh/id_ecdsa type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/terdon/.ssh/id_ecdsa-cert type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/terdon/.ssh/id_ed25519 type -1
debug1: key_load_public: No such file or directory
debug1: identity file /home/terdon/.ssh/id_ed25519-cert type -1
debug1: Enabling compatibility mode for protocol 2.0
debug1: Local version string SSH-2.0-OpenSSH_7.5
debug1: Remote protocol version 2.0, remote software version OpenSSH_7.5
debug1: match: OpenSSH_7.5 pat OpenSSH* compat 0x04000000
debug2: fd 3 setting O_NONBLOCK
debug1: Authenticating to localhost:22 as 'terdon'
debug3: hostkeys_foreach: reading file "/home/terdon/.ssh/known_hosts"
debug3: record_hostkey: found key type ECDSA in file /home/terdon/.ssh/known_hosts:47
debug3: load_hostkeys: loaded 1 keys from localhost
debug3: order_hostkeyalgs: prefer hostkeyalgs: [email protected],[email protected],[email protected],ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521
debug3: send packet: type 20
debug1: SSH2_MSG_KEXINIT sent
debug3: receive packet: type 20
debug1: SSH2_MSG_KEXINIT received
debug2: local client KEXINIT proposal
debug2: KEX algorithms: curve25519-sha256,[email protected],ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1,ext-info-c
debug2: Host key algorithms: [email protected],[email protected],[email protected],ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,[email protected],[email protected],ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
debug2: ciphers ctos: [email protected],aes128-ctr,aes192-ctr,aes256-ctr,[email protected],[email protected],aes128-cbc,aes192-cbc,aes256-cbc
debug2: ciphers stoc: [email protected],aes128-ctr,aes192-ctr,aes256-ctr,[email protected],[email protected],aes128-cbc,aes192-cbc,aes256-cbc
debug2: MACs ctos: [email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected],hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: MACs stoc: [email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected],hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: compression ctos: none,[email protected],zlib
debug2: compression stoc: none,[email protected],zlib
debug2: languages ctos:
debug2: languages stoc:
debug2: first_kex_follows 0
debug2: reserved 0
debug2: peer server KEXINIT proposal
debug2: KEX algorithms: curve25519-sha256,[email protected],ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1
debug2: Host key algorithms: ssh-rsa,rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp256,ssh-ed25519
debug2: ciphers ctos: [email protected],aes128-ctr,aes192-ctr,aes256-ctr,[email protected],[email protected]
debug2: ciphers stoc: [email protected],aes128-ctr,aes192-ctr,aes256-ctr,[email protected],[email protected]
debug2: MACs ctos: [email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected],hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: MACs stoc: [email protected],[email protected],[email protected],[email protected],[email protected],[email protected],[email protected],hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: compression ctos: none,[email protected]
debug2: compression stoc: none,[email protected]
debug2: languages ctos:
debug2: languages stoc:
debug2: first_kex_follows 0
debug2: reserved 0
debug1: kex: algorithm: curve25519-sha256
debug1: kex: Host key algorithm: ecdsa-sha2-nistp256
debug1: kex: server->client cipher: [email protected] MAC: <implicit> compression: none
debug1: kex: client->server cipher: [email protected] MAC: <implicit> compression: none
debug3: send packet: type 30
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug3: receive packet: type 31
debug1: Server Host key: ecdsa-sha2-nistp256 SHA256:uxhkh+gGPiCJQPaP024WXHth382h3BTs7QdGMokB9VM
debug3: hostkeys_foreach: reading file "/home/terdon/.ssh/known_hosts"
debug3: record_hostkey: found key type ECDSA in file /home/terdon/.ssh/known_hosts:47
debug3: load_hostkeys: loaded 1 keys from localhost
debug1: Host 'localhost' is known and matches the ECDSA Host key.
debug1: Found key in /home/terdon/.ssh/known_hosts:47
debug3: send packet: type 21
debug2: set_newkeys: mode 1
debug1: rekey after 134217728 blocks
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug3: receive packet: type 21
debug1: SSH2_MSG_NEWKEYS received
debug2: set_newkeys: mode 0
debug1: rekey after 134217728 blocks
debug2: key: /home/terdon/.ssh/id_rsa (0x555a5e4b5060)
debug2: key: /home/terdon/.ssh/id_dsa ((nil))
debug2: key: /home/terdon/.ssh/id_ecdsa ((nil))
debug2: key: /home/terdon/.ssh/id_ed25519 ((nil))
debug3: send packet: type 5
debug3: receive packet: type 7
debug1: SSH2_MSG_EXT_INFO received
debug1: kex_input_ext_info: server-sig-algs=<ssh-ed25519,ssh-rsa,rsa-sha2-256,rsa-sha2-512,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521>
debug3: receive packet: type 6
debug2: service_accept: ssh-userauth
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug3: send packet: type 50
debug3: receive packet: type 51
debug1: Authentications that can continue: publickey,password
debug3: start over, passed a different list publickey,password
debug3: preferred publickey,keyboard-interactive,password
debug3: authmethod_lookup publickey
debug3: remaining preferred: keyboard-interactive,password
debug3: authmethod_is_enabled publickey
debug1: Next authentication method: publickey
debug1: Offering RSA public key: /home/terdon/.ssh/id_rsa
debug3: send_pubkey_test
debug3: send packet: type 50
debug2: we sent a publickey packet, wait for reply
debug3: receive packet: type 60
debug1: Server accepts key: pkalg rsa-sha2-512 blen 279
debug2: input_userauth_pk_ok: fp SHA256:OGvtyUIFJw426w/FK/RvIhsykeP8kIEAtAeZwYBIzok
debug3: sign_and_send_pubkey: RSA SHA256:OGvtyUIFJw426w/FK/RvIhsykeP8kIEAtAeZwYBIzok
debug3: send packet: type 50
debug3: receive packet: type 52
debug1: Authentication succeeded (publickey).
Authenticated to localhost ([::1]:22).
debug2: fd 6 setting O_NONBLOCK
debug1: channel 0: new [client-session]
debug3: ssh_session2_open: channel_new: 0
debug2: channel 0: send open
debug3: send packet: type 90
debug1: Requesting [email protected]
debug3: send packet: type 80
debug1: Entering interactive session.
debug1: pledge: network
debug3: receive packet: type 80
debug1: client_input_global_request: rtype [email protected] want_reply 0
debug3: receive packet: type 91
debug2: callback start
debug2: fd 3 setting TCP_NODELAY
debug3: ssh_packet_set_tos: set IPV6_TCLASS 0x08
debug2: client_session2_setup: id 0
debug1: Sending command: sleep 2
debug2: channel 0: request exec confirm 1
debug3: send packet: type 98
debug2: callback done
debug2: channel 0: open confirm rwindow 0 rmax 32768
debug2: channel 0: rcvd adjust 2097152
debug3: receive packet: type 99
debug2: channel_input_status_confirm: type 99 id 0
debug2: exec request accepted on channel 0
_
これは私の_~/.ssh/config
_の設定には依存しません。ファイルの名前を変更しても何も変わりません。
sleep
ですが、実際にはかなり複雑なもの)は正常に終了し、本来の動作を実行します。これは、実行しているコマンドに依存しません。sshの問題です。~/.bashrc
_の名前を変更しても違いはありません。また、Ubuntu(デフォルトのログインシェルdash
)とArch(デフォルトのログインシェルbash
、sh
と呼ばれる)を実行しているマシンでこれを実行しました。どうしたの?これはsshのバグですか?設定する必要のあるオプションはありますか?同じシェルからSSH経由でコマンドを実行するスクリプトの複数のインスタンスを起動するにはどうすればよいですか?
何が起こっているのかを理解するには、端末の共有について少し知っておく必要があります。 2つのプログラムが同じ端末から同時に読み取ろうとするとどうなりますか?各入力バイトはランダムにプログラムの1つに送られます。 (カーネルのようにランダムではなく、RNGを使用して決定します。実際には予測できない場合のようにランダムです。)2つのプログラムがパイプ、または1つの場所から移動されるバイトのストリームである他のファイルタイプから読み取る場合も同じことが起こります。任意のバイトを複数回読み取ることができるバイト配列(通常のファイル、ブロックデバイス)ではなく、別のバイト(ソケット、文字デバイスなど)に。たとえば、ターミナルでシェルを実行し、ターミナルの名前を見つけて、cat
を実行します。
$ tty
/dev/pts/18
$ cat
次に、別の端末からcat /dev/pts/18
を実行します。次に、ターミナルに入力します。ラインがcat
プロセスの1つに行く場合と、他のプロセスに行く場合があります。端末がクックドモードの場合、行は全体としてディスパッチされます。端末をrawモードにすると、各バイトは個別にディスパッチされます。
それは厄介です。確かに、1つのプログラムが端末を取得し、他のプログラムは取得しないことを決定するメカニズムが必要です。さて、あります!通常のケースでトリガーされますが、上記で設定したシナリオではトリガーされません。 cat /dev/pts/18
が/dev/pts/18
から開始されていないため、このシナリオは異常です。この端末内で起動されなかったプログラムから端末にアクセスするのは珍しいことです。通常の場合、ターミナルでシェルを実行し、そのシェルからプログラムを実行します。その場合のルールは、フォアグラウンドのプログラムはターミナルを取得し、バックグラウンドのプログラムは取得しないことです。これは 端末アクセス制御 として知られています。それが機能する方法は次のとおりです。
tcsetpgrp
を呼び出して、誰がフォアグラウンドにいる必要があるかをカーネルに通知します。これは通常のケースで機能します。シェルでプログラムを実行すると、そのプログラムがフォアグラウンドプロセスになります。 (&
を使用して)バックグラウンドでプログラムを実行すると、プログラムがフォアグラウンドになりません。シェルがプロンプトを表示しているとき、シェルはそれ自体をフォアグラウンドに置きます。中断されたジョブをfg
で再開すると、ジョブはフォアグラウンドになります。 bg
では、そうではありません。
バックグラウンドプロセスが端末から読み取ろうとすると、カーネルはそれにSIGTTIN信号を送信します。シグナルのデフォルトのアクションは、プロセスを一時停止することです(SIGSTOPなど)。プロセスの親は、waitpid
フラグを指定して WSTOPPED
を呼び出すことにより、これを知ることができます。子プロセスがそれを一時停止するシグナルを受信すると、親のwaitpid
呼び出しが返され、シグナルが何であったかを親に知らせます。これは、シェルが「停止(tty入力)」を出力する方法です。これは、このジョブがSIGTTINのために中断されていることを示しています。
プロセスは中断されているため、再開または終了されるまで何も起こりません(プロセスがシグナルハンドラーを設定している場合、プロセスが中断されているため、プロセスは実行されないため、プロセスがキャッチしないシグナルを使用します)。 SIGCONTを送信することでプロセスを再開できますが、プロセスが端末から読み取っている場合は何も達成されず、すぐに別のSIGTTINを受け取ります。 fg
を使用してプロセスを再開すると、プロセスはフォアグラウンドに移動するため、読み取りは成功します。
これで、バックグラウンドでcat
を実行するとどうなるかがわかりました。
$ cat &
$
[1] + Stopped (tty input) cat
$
次に、SSHで同じことをしましょう。
$ ssh localhost sleep 999999 &
$
$
$
[1] + Stopped (tty input) ssh localhost sleep 999999
$
押す Enter シェル(フォアグラウンドにある)に行くこともあれば、SSHプロセス(その時点でSIGTTINによって停止される)に行くこともあります。どうして? ssh
が端末から読み取っていた場合、すぐにSIGTTINを受信するはずですが、そうでない場合は、なぜSIGTTINを受信するのでしょうか。
何が起こっているのかというと、SSHプロセスが select
システムコールを呼び出して、関心のあるファイルのいずれかで入力が利用可能になる時期を知る(または出力ファイルがより多くのデータを受信する準備ができているかどうか) )。入力ソースには、少なくとも端末とネットワークソケットが含まれます。 read
とは異なり、select
はバックグラウンドプロセスで禁止されておらず、ssh
はselect
を呼び出してもSIGTTINを受け取りません。 select
の目的は、何も中断することなく、データが利用可能かどうかを確認することです。理想的にはselect
はシステム状態をまったく変更しませんが、実際にはこれは完全には真実ではありません。 select
がSSHプロセスに、入力が端末ファイル記述子で利用可能であることを通知する場合、プロセスが後でread
を呼び出すと、カーネルは入力の送信をコミットする必要があります。 (そうでなく、プロセスがread
を呼び出した場合、この時点で使用可能な入力がない可能性があるため、select
からの戻り値は嘘でした。)したがって、カーネルは、入力をSSHプロセスにルーティングすることを決定し、select
システムコールが戻るまでに決定します。次に、SSHはread
を呼び出し、その時点でカーネルは、バックグラウンドプロセスが端末から読み取ろうとしたことを確認し、SIGTTINでそれを中断します。
同じサーバーに複数の接続を起動する必要がないことに注意してください。 1つで十分です。複数の接続は、問題が発生する可能性を高めるだけです。
ターミナルから読み取るためにSSHセッションが必要な場合は、フォアグラウンドで実行します。
端末からの読み取りにSSHセッションが必要ない場合は、その入力が端末からのものでないことを確認してください。これを行うには2つの方法があります。
入力をリダイレクトできます。
ssh … </dev/null
-n
または-f
で端末接続を転送しないようにSSHに指示できます。 (-n
は</dev/null
と同等です。-f
を使用すると、SSH自体が端末から読み取ることができます(パスワードの読み取りなど)が、コマンド自体では端末が開かれません。)
ssh -n …
端末とSSHの間の切断はクライアントで発生する必要があることに注意してください。サーバーで実行されているsleep
プロセスが端末から読み取ることはありませんが、SSHはそれを知る方法がありません。クライアントが標準入力で入力を受け取った場合、それをサーバーに転送する必要があります。これにより、アプリケーションがデータを読み取ることを決定した場合に備えて、データをバッファーで使用できるようになります(アプリケーションがselect
を呼び出す場合は、データが利用可能であることが通知されます)。
マニュアルページでヘルプを見つけることができます:
-n Redirects stdin from /dev/null (actually, prevents reading from stdin). This must be used when ssh is run in the
background. A common trick is to use this to run X11 programs on a remote machine. For example, ssh -n
shadows.cs.hut.fi emacs & will start an emacs on shadows.cs.hut.fi, and the X11 connection will be automatically
forwarded over an encrypted channel. The ssh program will be put in the background. (This does not work if ssh
needs to ask for a password or passphrase; see also the -f option.)
それでも問題が解決しない場合は、気まぐれで-T
(疑似tty割り当てを無効にする)を試してみます。
どうやら、同じシェルが同じサーバーへの複数のssh接続を起動した場合、与えられたコマンドを実行した後、戻りませんが、永遠にハングします(Stopped(tty input))。
これは、TTYへの同時アクセスの一般的な動作です。プロセス全体はすでにバックグラウンドで処理されており、出力を書き込もうとすると、TTYへのアクセスが許可されず、シグナル(SIGTTOU
)を受信します。これは、bash
プロセスによってキャッチされないため、デフォルトのアクションが実行されます(Stop
)。
その他の回答 で説明されている-n
オプション、またはIO)を一部のファイルにリダイレクトすると、役に立ちます。他に説明するものがあるかどうかはわかりません。 、ただし、その場合は明確にしてください。