ó
½-'Nc           @   sˆ  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 m Z d d l m	 Z	 m
 Z
 m Z d d l m Z d d l m Z d d l m Z m Z m Z e ƒ  Z d Z d	 Z d
 Z d e f d „  ƒ  YZ d d „ Z d e f d „  ƒ  YZ d e f d „  ƒ  YZ d e f d „  ƒ  YZ d e f d „  ƒ  YZ  d e e  f d „  ƒ  YZ! d e f d „  ƒ  YZ" d e f d „  ƒ  YZ# d S(   su   
Functionality for running arbitrary shell scripts.

@var ALL_USERS: A token indicating all users should be allowed.
iÿÿÿÿN(   t   ProcessProtocol(   t   Deferredt   failt   maybeDeferred(   t   ProcessDone(   t   build_script(   t   ManagerPlugint	   SUCCEEDEDt   FAILEDif   ig   s<   /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bint   UnknownUserErrorc           B   s   e  Z RS(    (   t   __name__t
   __module__(    (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR	      s   c         C   sª   d  } d  } d  } |  d  k	 r |  j d ƒ } y t j | ƒ } Wn! t k
 rc t d |  ƒ ‚ n X| j } | j } | j } t	 j
 j | ƒ s d } q n  | | | f S(   Ns   utf-8u   Unknown user '%s't   /(   t   Nonet   encodet   pwdt   getpwnamt   KeyErrorR	   t   pw_uidt   pw_gidt   pw_dirt   ost   patht   exists(   t   usernamet   uidt   gidR   t   username_strt   info(    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyt   get_user_info    s    			t   ProcessTimeLimitReachedErrorc           B   s   e  Z d  Z d „  Z RS(   s™   
    Raised when a process has been running for too long.

    @ivar data: The data that the process printed before reaching the time
        limit.
    c         C   s   | |  _  d  S(   N(   t   data(   t   selfR   (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyt   __init__:   s    (   R
   R   t   __doc__R!   (    (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR   2   s   t   ProcessFailedErrorc           B   s   e  Z d  Z d „  Z RS(   s“   Raised when a process exits with a non-0 exit code.

    @ivar data: The data that the process printed before reaching the time
        limit.
    c         C   s   | |  _  | |  _ d  S(   N(   R   t	   exit_code(   R    R   R$   (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR!   E   s    	(   R
   R   R"   R!   (    (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR#   >   s   t   UnknownInterpreterErrorc           B   s    e  Z d  Z d „  Z d „  Z RS(   sˆ   Raised when the interpreter specified to run a script is invalid.

    @ivar interpreter: the interpreter specified for the script.
    c         C   s#   | |  _  t j |  |  j ƒ  ƒ d  S(   N(   t   interpretert	   ExceptionR!   t   _get_message(   R    R&   (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR!   P   s    	c         C   s   d |  j  S(   Ns   Unknown interpreter: '%s'(   R&   (   R    (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR(   T   s    (   R
   R   R"   R!   R(   (    (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR%   J   s   	t   ScriptRunnerMixinc           B   s/   e  Z d d  „ Z d „  Z d „  Z d „  Z RS(   c         C   s,   | d k r d d l m } n  | |  _ d S(   sn   
        @param process_factory: The L{IReactorProcess} provider to run the
            process with.
        iÿÿÿÿ(   t   reactorN(   R   t   twisted.internetR*   t   process_factory(   R    R,   (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR!   Z   s    c         C   s(   |  j  j j ƒ  } | t k p' | | k S(   N(   t   registryt   configt   get_allowed_script_userst	   ALL_USERS(   R    t   usert   allowed_users(    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyt   is_user_allowedc   s    c         C   sV   t  j | d ƒ | d  k	 r2 t  j | | | ƒ n  | j t | | ƒ ƒ | j ƒ  d  S(   NiÀ  (   R   t   chmodR   t   chownt   writeR   t   close(   R    t   script_filet   filenamet   shellt   codeR   R   (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyt   write_script_fileg   s
    c         C   sf   t  |  j j |  j ƒ } |  j j | | d | d | d | d | ƒ| d  k	 r_ | j | ƒ n  | j S(   NR   R   R   t   env(	   t   ProcessAccumulationProtocolR-   R*   t
   size_limitR,   t   spawnProcessR   t   schedule_cancelt   result_deferred(   R    R9   R   R   R   R=   t
   time_limitt   pp(    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyt   _run_scripts   s    	"N(   R
   R   R   R!   R3   R<   RE   (    (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR)   X   s   			t   ScriptExecutionPluginc           B   sh   e  Z d  Z d Z d „  Z d
 d „ Z d „  Z d „  Z d „  Z	 d „  Z
 d
 d
 d
 d „ Z d	 „  Z RS(   s   A plugin which allows execution of arbitrary shell scripts.

    @ivar size_limit: The number of bytes at which to truncate process output.
    i ¡ c         C   s-   t  t |  ƒ j | ƒ | j d |  j ƒ d  S(   Ns   execute-script(   t   superRF   t   registert   register_messaget   _handle_execute_script(   R    R-   (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyRH   …   s    c         C   so   t  | t ƒ s$ | j d d ƒ } n  i d d 6| d 6| d 6| d 6} | rY | | d <n  |  j j j | t ƒ S(	   Ns   utf-8t   replaces   operation-resultt   typet   statuss   result-texts   operation-ids   result-code(   t
   isinstancet   unicodet   decodeR-   t   brokert   send_messaget   True(   R    RM   R   t   opidt   result_codet   message(    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyt   _respondŠ   s    

c      
   C   sØ   | d } y• | d } |  j  | ƒ s@ |  j t d | f | ƒ S|  j | d | d d | d d | d	 | d	 ƒ} | j |  j | ƒ | j |  j | ƒ | SWn2 t k
 rÓ } |  j t |  j	 | ƒ | ƒ ‚  n Xd  S(
   Ns   operation-idR   u!   Scripts cannot be run as user %s.R&   R;   RC   s
   time-limitR1   t   attachments(
   R3   RW   R   t
   run_scriptt   addCallbackt   _respond_successt
   addErrbackt   _respond_failureR'   t   _format_exception(   R    RV   RT   R1   t   dt   e(    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyRJ   —   s"    



c         C   s   d | j  j | j d f S(   Nu   %s: %si    (   t	   __class__R
   t   args(   R    R`   (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR^   «   s    c         C   s   |  j  t | | ƒ S(   N(   RW   R   (   R    R   RT   (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR[   ®   s    c         C   s{   d  } | j t ƒ r t } n | j t ƒ r6 t } n  | d  k	 r^ |  j t | j j	 | | ƒ S|  j t t
 | ƒ | ƒ Sd  S(   N(   R   t   checkR   t   TIMEOUT_RESULTR#   t   PROCESS_FAILED_RESULTRW   R   t   valueR   t   str(   R    t   failureRT   R;   (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR]   ±   s    		c      
      s   t  j j | j ƒ  d ƒ s, t t | ƒ ƒ St | ƒ \ ‰ ‰ ‰ t j ƒ  \ } ‰ t  j	 | d ƒ } ˆ j
 | ˆ | | ˆ ˆ ƒ i t d 6| p” d d 6ˆ p¡ d d 6‰ t  j d ƒ } ‡  ‡ ‡ ‡ ‡ ‡ ‡ ‡ f d †  }	 t |	 ƒ }
 |
 j ˆ j ˆ ˆ | ƒ S(	   s8  
        Run a script based on a shell and the code.

        A file will be written with #!<shell> as the first line, as executable,
        and run as the given user.

        XXX: Handle the 'reboot' and 'killall landscape-client' commands
        gracefully.

        @param shell: The interpreter to use.
        @param code: The code to run.
        @param user: The username to run the process as.
        @param time_limit: The number of seconds to allow the process to run
            before killing it and failing the returned Deferred with a
            L{ProcessTimeLimitReachedError}.
        @param attachments: C{dict} of filename/data attached to the script.

        @return: A deferred that will fire with the data printed by the process
            or fail with a L{ProcessTimeLimitReachedError}.
        i    t   wt   PATHt    t   USERt   HOMEi   c             s÷   ˆ rÛ t  j ƒ  }  |  ˆ d <x‡ ˆ j ƒ  D]y \ } } t j j |  | ƒ } t | d ƒ } t j | d ƒ ˆ d  k	 r‹ t j	 | ˆ ˆ ƒ n  | j
 | ƒ | j ƒ  q) Wt j |  d ƒ ˆ d  k	 rÛ t j	 |  ˆ ˆ ƒ qÛ n  ˆ j ˆ ˆ ˆ ˆ ˆ ˆ  ƒ S(   Nt   LANDSCAPE_ATTACHMENTSt   wbi€  iÀ  (   t   tempfilet   mkdtempt	   iteritemsR   R   t   joint   fileR4   R   R5   R6   R7   RE   (   t   attachment_dirt   attachment_filenameR   t   full_filenamet
   attachment(   RC   RX   R    R9   R   R=   R   R   (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyt   run_with_attachmentsá   s"    
	(   R   R   R   t   splitR   R%   R   Rp   t   mkstempt   fdopenR<   t   UBUNTU_PATHt   umaskR   t   addBotht   _cleanup(   R    R:   R;   R1   RC   RX   t   fdR8   t	   old_umaskRy   t   result(    (   RC   RX   R   R    R9   R   R=   R   sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyRY   ¼   s    $c         C   sZ   y t  j | ƒ Wn n Xd | k rI y t j | d ƒ WqI qI Xn  t  j | ƒ | S(   NRn   (   R   t   unlinkt   shutilt   rmtreeR~   (   R    Rƒ   R9   R=   R‚   (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR€   ø   s    N(   R
   R   R"   R?   RH   R   RW   RJ   R^   R[   R]   RY   R€   (    (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyRF   }   s   					;R>   c           B   s;   e  Z d  Z d „  Z d „  Z d „  Z d „  Z d „  Z RS(   sx   A ProcessProtocol which accumulates output.

    @ivar size_limit: The number of bytes at which to truncate output.
    c         C   s=   g  |  _  t ƒ  |  _ t |  _ | |  _ | |  _ d  |  _ d  S(   N(	   R   R   RB   t   Falset
   _cancelledR?   R*   R   t   _scheduled_cancel(   R    R*   R?   (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR!     s    				c         C   s   |  j  j | |  j ƒ |  _ d  S(   N(   R*   t
   call_latert   _cancelR‰   (   R    RC   (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyRA     s    	c         C   s@   t  t j t t |  j ƒ d ƒ } |  j j | |  j |  ƒ d S(   s‰   Some data was received from the child.

        Add it to our buffer, as long as it doesn't go over L{size_limit}
        bytes.
        i    N(   t   reducet   operatort   addt   mapt   lenR   t   appendR?   (   R    R   R   t   current_size(    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyt   childDataReceived  s    !c         C   s³   | j  j } d j |  j ƒ } |  j r@ |  j j t | ƒ ƒ no |  j d k	 rt |  j } d |  _ |  j
 j | ƒ n  | j t ƒ r– |  j j | ƒ n |  j j t | | ƒ ƒ d S(   s  Fire back the deferred.

        The deferred will be fired with the string of data received from the
        subprocess, or if the subprocess was cancelled, a
        L{ProcessTimeLimitReachedError} will be fired with data accumulated so
        far.
        Rk   N(   Rf   t   exitCodeRs   R   Rˆ   RB   t   errbackR   R‰   R   R*   t   cancel_callRc   R   t   callbackR#   (   R    t   reasonR$   R   t	   scheduled(    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyt   processEnded!  s    			c         C   s>   x d D] } |  j  j | ƒ q W|  j  j d ƒ t |  _ d S(   s—   
        Close filedescriptors, kill the process, and indicate that a
        L{ProcessTimeLimitReachedError} should be fired on the deferred.
        i    i   i   t   KILLN(   i    i   i   (   t	   transportt   closeChildFDt   signalProcessRS   Rˆ   (   R    t   i(    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR‹   9  s    
(   R
   R   R"   R!   RA   R“   Rš   R‹   (    (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR>     s   					t   ScriptExecutionc           B   s,   e  Z d  Z d „  Z d „  Z e d „ Z RS(   sK   
    Meta-plugin wrapping ScriptExecutionPlugin and CustomGraphPlugin.
    c         C   s,   d d l  m } t ƒ  |  _ | ƒ  |  _ d  S(   Niÿÿÿÿ(   t   CustomGraphPlugin(   t   landscape.manager.customgraphR¡   RF   t   _script_executiont   _custom_graph(   R    R¡   (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR!   N  s    c         C   s:   t  t |  ƒ j | ƒ |  j j | ƒ |  j j | ƒ d  S(   N(   RG   R    RH   R£   R¤   (   R    R-   (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyRH   S  s    c         C   s   |  j  j | ƒ d  S(   N(   R¤   t   exchange(   R    t   urgent(    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR¥   X  s    (   R
   R   R"   R!   RH   R‡   R¥   (    (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyR    I  s   		($   R"   R   R   Rp   R   R…   t   twisted.internet.protocolR    t   twisted.internet.deferR   R   R   t   twisted.internet.errorR   t   landscape.lib.scriptcontentR   t   landscape.manager.pluginR   R   R   t   objectR0   Rd   Re   R}   R'   R	   R   R   R   R#   R%   R)   RF   R>   R    (    (    (    sE   /usr/lib/python2.7/dist-packages/landscape/manager/scriptexecution.pyt   <module>   s.   	%‰C