
    Ki[0                         d Z ddlZddlZddlZddlZddlZddlZddlmZ ddl	m
Z
 ddlmZ ddlmZ  ej        e          ZddZ G d	 d
ee
          ZdS )zKSSH remote execution environment with ControlMaster connection persistence.    N)Path)BaseEnvironment)PersistentShellMixin)is_interruptedreturnc                  L    t          j        d          st          d          dS )z@Fail fast with a clear error when the SSH client is unavailable.sshzWSSH is not installed or not in PATH. Install OpenSSH client: apt install openssh-clientN)shutilwhichRuntimeError     3/home/ubuntu/hermes-agent/tools/environments/ssh.py_ensure_ssh_availabler      s3    < 
e
 
 	

 
r   c                   V    e Zd ZU dZ	 	 	 d$deded	ed
edededef fdZd%dedz  defdZ	d Z
defdZd&dZd'dddded	ed
edz  dedz  def
 fdZdZeed<   edefd            Zdej        fdZdedee         fdZd  Zd! Zd'dddded	ed
edz  dedz  def
d"Z fd#Z xZS )(SSHEnvironmenta  Run commands on a remote machine over SSH.

    Uses SSH ControlMaster for connection persistence so subsequent
    commands are fast. Security benefit: the agent cannot modify its
    own code since execution happens on a separate machine.

    Foreground commands are interruptible: the local ssh process is killed
    and a remote kill is attempted over the ControlMaster socket.

    When ``persistent=True``, a single long-lived bash shell is kept alive
    over SSH and state (cwd, env vars, shell variables) persists across
    ``execute()`` calls.  Output capture uses file-based IPC on the remote
    host (stdout/stderr/exit-code written to temp files, polled via fast
    ControlMaster one-shot reads).
    ~<       Fhostusercwdtimeoutportkey_path
persistentc                 $   t                                          ||           || _        || _        || _        || _        || _        t          t          j	                              dz  | _
        | j
                            dd           | j
        | d| d| dz  | _        t                       |                                  |                                 | _        |                                  | j        r|                                  d S d S )N)r   r   z
hermes-sshT)parentsexist_ok@:z.sock)super__init__r   r   r   r   r   r   tempfile
gettempdircontrol_dirmkdircontrol_socketr   _establish_connection_detect_remote_home_remote_home_sync_skills_and_credentials_init_persistent_shell)	selfr   r   r   r   r   r   r   	__class__s	           r   r$   zSSHEnvironment.__init__+   s    	S'222			 $ 3 5 566Etd;;;".D1M1M41M1M$1M1M1MM""$$$ 4466))+++? 	*'')))))	* 	*r   N
extra_argsr   c                 T   dg}|                     dd| j         g           |                     ddg           |                     ddg           |                     ddg           |                     ddg           |                     ddg           | j        d	k    r)|                     d
t          | j                  g           | j        r|                     d| j        g           |r|                     |           |                    | j         d| j                    |S )Nr	   -oControlPath=zControlMaster=autozControlPersist=300zBatchMode=yesz StrictHostKeyChecking=accept-newzConnectTimeout=10r   z-pz-ir!   )extendr)   r   strr   appendr   r   )r/   r1   cmds      r   _build_ssh_commandz!SSHEnvironment._build_ssh_command@   s3   g

D>)<>>?@@@

D./000

D./000

D/*+++

D<=>>>

D-.///9??JJc$)nn-...= 	.JJdm,--- 	#JJz"""

di--$)--...
r   c                    |                                  }|                    d           	 t          j        |ddd          }|j        dk    rD|j                                        p|j                                        }t          d|           d S # t          j	        $ r! t          d| j
         d| j         d	          w xY w)
Nz!echo 'SSH connection established'T   capture_outputtextr   r   zSSH connection failed: zSSH connection to r!   z
 timed out)r9   r7   
subprocessrun
returncodestderrstripstdoutr   TimeoutExpiredr   r   )r/   r8   result	error_msgs       r   r*   z$SSHEnvironment._establish_connectionQ   s    %%''

6777	W^C4QSTTTF A%%"M//11JV]5H5H5J5J	"#HY#H#HIII &% ( 	W 	W 	WUDIUU	UUUVVV	Ws   A'B 0Cc                 \   	 |                                  }|                    d           t          j        |ddd          }|j                                        }|r(|j        dk    rt                              d|           |S n# t          $ r Y nw xY w| j
        dk    rdS d	| j
         S )
z(Detect the remote user's home directory.z
echo $HOMET
   r<   r   zSSH: remote home = %srootz/rootz/home/)r9   r7   r?   r@   rD   rC   rA   loggerdebug	Exceptionr   )r/   r8   rF   homes       r   r+   z"SSHEnvironment._detect_remote_home\   s    		))++CJJ|$$$^C4QSTTTF=&&((D )Q..4d;;; 	 	 	D	 97#	###s   BB 
BBc                    	 | j          d}ddlm}m} g d}d| j         d}| j        dk    r|d| j         z  }| j        r|d	| j         z  }|                    d
|g           | j         d| j	         } |            D ]}|d         
                    d|d          }t          t          |          j                  }	|                                 }
|
                    d|	            t!          j        |
ddd           ||d         | d| gz   }t!          j        |ddd          }|j        dk    r#t&                              d|d         |           t&                              d|j                                                    ||          }|r|d         }|                                 }
|
                    d|            t!          j        |
ddd           ||d                             d          dz   | d| dgz   }t!          j        |ddd          }|j        dk    r$t&                              d|d         |           dS t&                              d|j                                                   dS dS # t2          $ r&}t&                              d|           Y d}~dS d}~ww xY w)z?Rsync skills directory and credential files to the remote host.z/.hermesr   )get_credential_file_mountsget_skills_directory_mount)rsyncz-azz--timeout=30z--safe-linkszssh -o ControlPath=z -o ControlMaster=autor   z -p z -i z-er!   container_pathz/root/.hermes   z	mkdir -p TrI   r<   	host_pathr"      zSSH: synced credential %s -> %sz SSH: rsync credential failed: %s)container_base/r   zSSH: synced skills dir %s -> %sz SSH: rsync skills dir failed: %sz*SSH: could not sync skills/credentials: %sN)r,   tools.credential_filesrP   rQ   r)   r   r   r5   r   r   replacer6   r   parentr9   r7   r?   r@   rA   rK   inforL   rB   rC   rstriprM   )r/   rW   rP   rQ   
rsync_basessh_optsdest_prefixmount_entryremote_path
parent_dir	mkdir_cmdr8   rF   skills_mountes                  r   r-   z+SSHEnvironment._sync_skills_and_credentialsm   se   ,	J $ 1;;;NeeeeeeeeIIIJXT-@XXXHyB.49...} 324=222tX.///!Y4444K  :9;; \ \)*:;CCOUcefgg k!2!2!9::
 3355	  !9Z!9!9:::yDRTUUUU K$<>\>\{>\>\#]]#DtUWXXX$))KK A;{C[]hiiiiLL!CV]EXEXEZEZ[[[[ 65^TTTL \*+;< 3355	  !:[!:!:;;;yDRTUUUU  -44S99C?"33[333$  $DtUWXXX$))KK A<P[C\^ijjjjjLL!CV]EXEXEZEZ[[[[[\ \  	J 	J 	JLLEqIIIIIIIII	Js   IJ 2J 
KJ==Kr   
stdin_datacommandrh   c                v    |                                   t                                          ||||          S )Nrg   )r-   r#   execute)r/   ri   r   r   rh   r0   s        r   rk   zSSHEnvironment.execute   s5    
 	))+++wwwWTTTr   g333333?_poll_interval_startc                     d| j          S )Nz/tmp/hermes-ssh-)_session_id)r/   s    r   _temp_prefixzSSHEnvironment._temp_prefix   s    4$"2444r   c                     |                                  }|                    d           t          j        |t          j        t          j        t          j        d          S )Nzbash -lT)stdinrD   rB   r>   )r9   r7   r?   PopenPIPEDEVNULLr/   r8   s     r   _spawn_shell_processz#SSHEnvironment._spawn_shell_process   sU    %%''

9/?%
 
 
 	
r   pathsc                    t          |          dk    rs|                                 }|                    d|d          d           	 t          j        |ddd          }|j        gS # t          j        t          f$ r dgcY S w xY wd	| j         d
d	                    fd|D                       }|                                 }|                    |           	 t          j        |ddd          }|j        
                    dz             fdt          t          |                    D             S # t          j        t          f$ r dgt          |          z  cY S w xY w)NrT   cat r   z 2>/dev/nullTrI   r<   r   __HERMES_SEP___z; c              3   *   K   | ]}d | d dV  dS )ry   z 2>/dev/null; echo ''Nr   ).0pdelims     r   	<genexpr>z2SSHEnvironment._read_temp_files.<locals>.<genexpr>   sH       
 
782122%222
 
 
 
 
 
r   
c                 J    g | ]}|t                    k     r|         nd  S r   )len)r~   ipartss     r   
<listcomp>z3SSHEnvironment._read_temp_files.<locals>.<listcomp>   s/    RRR1CJJE!HHBRRRr   )r   r9   r7   r?   r@   rD   rE   OSErrorrn   joinsplitrange)r/   rw   r8   rF   scriptr   r   s        @@r   _read_temp_fileszSSHEnvironment._read_temp_files   s   u::??))++CJJ4eAh444555#4   &-w7   t 5 0444 
 
 
 
<A
 
 
 
 
 %%''

6	%^DtR  F M''55ERRRRc%jj@Q@QRRRR)73 	% 	% 	%4#e**$$$$	%s%   
A* *BBAD< <)E('E(c                     | j         d S |                                 }|                    d| j          d           	 t          j        |dd           d S # t          j        t          f$ r Y d S w xY w)Nz	pkill -P z 2>/dev/null; trueT   r=   r   )
_shell_pidr9   r7   r?   r@   rE   r   ru   s     r   _kill_shell_childrenz#SSHEnvironment._kill_shell_children   s    ?"F%%''

BtBBBCCC	N3tQ??????)73 	 	 	DD	s   A A0/A0c                     |                                  }|                    d| j         d           	 t          j        |dd           d S # t          j        t          f$ r Y d S w xY w)Nzrm -f z-*Tr   r   )r9   r7   ro   r?   r@   rE   r   ru   s     r   _cleanup_temp_filesz"SSHEnvironment._cleanup_temp_files   s    %%''

1D-111222	N3tQ??????)73 	 	 	DD	s   A A'&A'c                   |p| j         }|                     |          \  }}d| d| }|p| j        }	||||z   }
n||}
n|}
|                                 }|                    |           |                     ||
          }|                    dd            g t          j        |t          j	        t          j
        |
rt          j	        nt          j        d          |
rL	 j                            |
           j                                         n# t          t           f$ r Y nw xY wfd}t#          j        |d          }|                                 t)          j                    |	z   }                                t/                      r                                 	                     d	           n)# t          j        $ r                                  Y nw xY w|                    d
	           d                              dz   ddS t)          j                    |k    r?                                 |                    d
	           |                     |	          S t)          j        d                                           |                    d	           d                              j        dS )Nzcd z && r   T)rD   rB   rq   r>   c                  j    	 j         D ]}                     |            d S # t          $ r Y d S w xY wN)rD   r7   rM   )line_output_chunksprocs    r   _drainz/SSHEnvironment._execute_oneshot.<locals>._drain  s\     K 0 0D"))$////0 0   s   $ 
22)targetdaemonrT   )r      r   z
[Command interrupted]   )outputrA   g?r   ) r   _prepare_commandr   r9   r7   _build_run_kwargspopr?   rr   rs   STDOUTrt   rq   writecloseBrokenPipeErrorr   	threadingThreadstarttime	monotonicpollr   	terminatewaitrE   killr   _timeout_resultsleeprA   )r/   ri   r   r   rh   work_direxec_command
sudo_stdinwrappedeffective_timeouteffective_stdinr8   kwargsr   readerdeadliner   r   s                   @@r   _execute_oneshotzSSHEnvironment._execute_oneshot   s     ?$(#'#8#8#A#A j444l44#3t|!j&<(:5OO#(OO(O%%''

7''AA

9d###?$%4L*//*:L
 
 
  	
  111
  """"#W-   	 	 	 	 	 	 !===>##&77iikk! 
    IIaI((((!0      IIKKKKK A&&& ggn558QQ"%   ~(**		A&&&++,=>>>JsOOO! iikk!$ 	A''.11QQQs$   %3D D-,D-.G #G+*G+c                 |   t                                                       | j                                        r	 ddd| j         dd| j         d| j         g}t          j        |dd	           n# t          t          j	        f$ r Y nw xY w	 | j        
                                 d S # t          $ r Y d S w xY wd S )
Nr	   r3   r4   z-Oexitr!   Tr   r   )r#   cleanupr)   existsr   r   r?   r@   r   SubprocessErrorunlink)r/   r8   r0   s     r   r   zSSHEnvironment.cleanup'  s    %%'' 
	d$H43F$H$HV	%?%?DI%?%?As4CCCCCZ78   #**,,,,,   
	 
	s#   6A3 3BBB+ +
B98B9)r   r   r   r   Fr   r   Nr   )__name__
__module____qualname____doc__r6   intboolr$   listr9   r*   r+   r-   dictrk   rl   float__annotations__propertyro   r?   rr   rv   r   r   r   r   r   __classcell__)r0   s   @r   r   r      s           9<DF$)* *S * *# ***-*>A*!* * * * * ** TD[ D    "	W 	W 	W$S $ $ $ $".J .J .J .J`U&*)-U U Us U UtU$JU26U U U U U U #'%&&&5c 5 5 5 X5	
j&6 	
 	
 	
 	
%s %tCy % % % %6    BR/326BR BR BR BR# BR"%*BR%(4ZBR;?BR BR BR BRH        r   r   r   )r   loggingr
   r?   r%   r   r   pathlibr   tools.environments.baser   #tools.environments.persistent_shellr   tools.interruptr   	getLoggerr   rK   r   r   r   r   r   <module>r      s    Q Q                   3 3 3 3 3 3 D D D D D D * * * * * *		8	$	$
 
 
 
Y Y Y Y Y)? Y Y Y Y Yr   