2016-06-12 3 views
4

이것은 SuperUser/ServerFault와는 반대로 StackOverflow와 syscd 및 sshd에 의해 수행되는 OS 상호 작용과 관련이 있기 때문에, 나는 을 사용하여 SSH를 사용하고있는 문제가 아닙니다.).왜 서버의 'sshd'에서 양쪽 끝이 열린 파이프의 출력을 기다리는 SSH 명령을 걸고 있습니까?

는 문맥 :

나는 SSH, 예를 통해 스크립트의 복잡한 일련의 호출 ssh [email protected] -- /my/command. 원격 명령은 많은 복잡한 분기 및 실행을 수행하며 결과적으로 원격 호스트에서 백그라운드에서 실행되는 데몬 프로세스를 실행합니다. 가끔 (안정적인 재생 조건을 찾기 위해 서서히 노력하고 있습니다) ssh 명령은 절대로 클라이언트 셸에 제어권을 반환하지 않습니다. 이러한 상황에서 대상 호스트로 이동하여 자식이 무한정 걸어 다니지 않고 sshd: [email protected] 프로세스를 볼 수 있습니다.

이 문제의 해결은이 질문의 내용과 관련이 없습니다. 이 질문은 그 sshd 프로세스가 일 때 일을합니다.

SSH 구현은 OpenSSH이며 버전 버전은 5.3p1-112.el6_7입니다.

문제 :

내가 갇혀 그 중 하나 찾을 경우 sshd들과 strace가, 내가,이 두 개의 손잡이에 선택을하고있어 예를 볼 수 있습니다 select(12, [3 6], [], NULL, NULL 또는 이와 유사한 것. lsof은 그 핸들 중 하나가 SSH 클라이언트에 다시 연결되는 TCP 소켓임을 알려줍니다. 다른 하나는 파이프이며, 다른 쪽 파이프는 동일한 sshd 프로세스에만 열려 있습니다. this SuperUser question에 대한 응답을 사용하여 해당 파이프를 ID로 검색하면 해당 파이프에 대한 참조를 포함하는 유일한 프로세스가 동일한 프로세스입니다. lsof은 이것을 확인합니다. 파이프의 읽기 및 쓰기 끝이 모두 동일한 프로세스에서 열려 있습니다. (파이프 788,422,703 및 sshd PID 22744에 대한) :

sshd 22744 user 6r FIFO    0,8  0t0 788422703 pipe 
sshd 22744 user 7w FIFO    0,8  0t0 788422703 pipe 

질문 : SSH 기다리고 무엇

? 파이프가 아무 것도 연결되어 있지 않고 하위 프로세스가없는 경우 예상 할 수있는 이벤트가 무엇인지 상상할 수 없습니다.

"루프 된"파이프 란 무엇입니까/무엇을 나타 냅니까? 내 유일한 이론은 STDIN이 SSH 클라이언트에 제공되지 않는다면 대상 호스트 sshd은 더미 STDIN 파이프를 열어 일부 내부 자식 관리 코드가 더 균일 할 수 있다는 것입니다. 그러나 그것은 꽤 약해 보인다.

어떻게 이런 상황에 처하게 되나요? 나는 무엇을 시도했다

/추가 정보 :

  • 처음에 나는이 데몬에 핸들 누수 생각했다. 자신을 배경으로하는 명령을 실행하여 대기 중이며 어린이가없는 sshd 프로세스를 만들 수 있습니다 (예 : ssh [email protected] -- 'sleep 60 &'; sshd은 스트림이 데몬 프로세스에 닫힐 때까지 대기합니다. 직계 아동의 퇴거 만이 아닙니다.문제가되는 스크립트는 결국 데몬이 시작될 때 (프로세스 트리 아래로) 결과로 시작하기 때문에 처음에는 데몬이 핸들을 잡고있을 가능성이있는 것처럼 보였습니다. 그러나, 예를 들어 sleep 60 & 명령을 사용하면, sshd 프로세스가 데몬과 통신하고 개의 열린 파이프가 두 개가 아닌 개의 파이프로 구성되며 두 개 이상이 sshd에서 연결됩니다. 루프되지 않은 데몬 프로세스. 추적 할 수있는 방법이 없다면 (그리고 내가 모르는 경우) (예를 들어, dup 파일 핸들이 close() 세마포어 대기 또는 파이핑으로 어떻게 작동하는지 알지 못한다. pipe-to-self 상황은 데몬 대기 상태를 나타냅니다.
  • sshd은 주기적으로 (strace에서 SIGCHLD를 차단하는 것으로 표시되는) select s에서 깨어나서 TCP 소켓/ssh 연결 자체에서 통신을 수신 한 다음 같은 FD들.
  • this race condition (커널이 파이프에서 데이터를 사용할 수있게 만들기 전에 SIGCHLD가 전달됨)의 영향을받을 수 있습니다. 그러나이 상태가 나타나는 속도와 대상 호스트에서 실행되는 프로세스가 Perl 스크립트 및 Perl runtime closes and flushes open file descriptors on shutdown이라는 사실을 감안할 때 거의 불가능합니다.
+2

디버깅을 켜고 로그를보고 sshd를 다시 시작해 보셨습니까? – xxfelixxx

+0

질문에 키워드는 * Open * SSH입니다. 블랙 박스가 내부적으로 수행하는 작업을 추측하는 대신 [코드 자체를 읽으십시오] (https://github.com/openssh/openssh-portable/blob/master/sshd.c#L1242). 그것은 꽤 잘 쓰여졌습니다. – msw

+0

깊이있는 설명 주셔서 감사합니다 - 간결하게 내가 가진 문제를 요약합니다. 제 경우에는 ssh 명령이 [packer] (https://packer.io)를 사용하여 VirtualBox VM을 시작했습니다. Packer는 빌드가 실패한 경우 VM을 실행중인 상태로 두도록 구성되었습니다. 게시 한 경쟁 조건 링크를 읽은 후에 나는 주위가 멍청했고 VirtualBox가 패커의 STDOUT/STDERR 파이프 중 하나를 상속 받고 패커가 종료 된 후에도 계속 열어 둡니다. 이렇게하면 sshd가 파이프가 닫힐 때까지 기다리게됩니다. 버추얼 박스를 죽이면 ssh 연결이 제대로 종료됩니다. – Matt

답변

3

알림 파이프를 설명하는 것으로 보입니다. OpenSSH sshd main 루프는 select()을 호출하여 할 일이있을 때까지 대기합니다. 폴링되는 파일 설명자에는 클라이언트에 대한 TCP 연결과 활성 채널을 서비스하는 데 사용되는 설명자가 포함됩니다.

sshd는 SIGCHLD 신호가 수신 될 때 select() 호출을 인터럽트 할 수 있기를 원합니다. 그렇게하기 위해 sshd는 SIGCHLD를위한 시그널 핸들러를 설치하고 파이프를 생성한다. SIGCHLD 신호가 수신되면 신호 처리기는 파이프에 바이트를 씁니다. 파이프의 읽기 끝은 select()에 의해 폴링 된 파일 설명자 목록에 포함됩니다. 파이프에 쓰는 작업은 select() 호출이 통지 파이프가 읽을 수 있다는 표시와 함께 반환되게합니다.

코드의 모든 serverloop.c에 있습니다

/* 
* we write to this pipe if a SIGCHLD is caught in order to avoid 
* the race between select() and child_terminated 
*/ 
static int notify_pipe[2]; 
static void 
notify_setup(void) 
{ 
     if (pipe(notify_pipe) < 0) { 
       error("pipe(notify_pipe) failed %s", strerror(errno)); 
     } else if ((fcntl(notify_pipe[0], F_SETFD, 1) == -1) || 
      (fcntl(notify_pipe[1], F_SETFD, 1) == -1)) { 
       error("fcntl(notify_pipe, F_SETFD) failed %s", strerror(errno)); 
       close(notify_pipe[0]); 
       close(notify_pipe[1]); 
     } else { 
       set_nonblock(notify_pipe[0]); 
       set_nonblock(notify_pipe[1]); 
       return; 
     } 
     notify_pipe[0] = -1; /* read end */ 
     notify_pipe[1] = -1; /* write end */ 
} 
static void 
notify_parent(void) 
{ 
     if (notify_pipe[1] != -1) 
       write(notify_pipe[1], "", 1); 
} 
[...] 

/*ARGSUSED*/ 
static void 
sigchld_handler(int sig) 
{ 
     int save_errno = errno; 
     child_terminated = 1; 
#ifndef _UNICOS 
     mysignal(SIGCHLD, sigchld_handler); 
#endif 
     notify_parent(); 
     errno = save_errno; 
} 

설정하고 선택 호출이 wait_until_can_do_something()라는 또 다른 기능에 수행하는 코드입니다. 상당히 길기 때문에 여기에 포함시키지 않을 것입니다. OpenSSH는 오픈 소스이며 this page은 소스 코드를 다운로드하는 방법을 설명합니다.