
    gT                     @   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Zddl	Z
ddlZddlZddlZddlmZ  ej                          Z G d de      Z G d de      Z e       Zej/                         e_        ej/                         e_        ej/                         e_        ej/                         e_        ej/                         e_        	 ddlZ	  ej<                  d      Z G d
 de      Z! e!       Z"y# e$ r 	 dZY w xY w# e $ r d	ZY *w xY w# e$ r dZY 7w xY w)a
  An implementation of the Web Site Process Bus.

This module is completely standalone, depending only on the stdlib.

Web Site Process Bus
--------------------

A Bus object is used to contain and manage site-wide behavior:
daemonization, HTTP server start/stop, process reload, signal handling,
drop privileges, PID file management, logging for all of these,
and many more.

In addition, a Bus object provides a place for each web framework
to register code that runs in response to site-wide events (like
process start and stop), or which controls or otherwise interacts with
the site-wide components mentioned above. For example, a framework which
uses file-based templates would add known template filenames to an
autoreload component.

Ideally, a Bus object will be flexible enough to be useful in a variety
of invocation scenarios:

 1. The deployer starts a site from the command line via a
    framework-neutral deployment script; applications from multiple frameworks
    are mixed in a single site. Command-line arguments and configuration
    files are used to define site-wide components such as the HTTP server,
    WSGI component graph, autoreload behavior, signal handling, etc.
 2. The deployer starts a site via some other process, such as Apache;
    applications from multiple frameworks are mixed in a single site.
    Autoreload and signal handling (from Python at least) are disabled.
 3. The deployer starts a site via a framework-specific mechanism;
    for example, when running tests, exploring tutorials, or deploying
    single applications from a single framework. The framework controls
    which site-wide components are enabled as it sees fit.

The Bus object in this package uses topic-based publish-subscribe
messaging to accomplish all this. A few topic channels are built in
('start', 'stop', 'exit', 'graceful', 'log', and 'main'). Frameworks and
site containers are free to define their own. If a message is sent to a
channel that has not been defined or has no listeners, there is no effect.

In general, there should only ever be a single Bus object per process.
Frameworks and site containers share a single Bus object by publishing
messages and subscribing listeners.

The Bus object works as a finite state machine which models the current
state of the process. Bus methods move it from one state to another;
those methods then publish to subscribed listeners on the channel for
the new state.::

                        O
                        |
                        V
       STOPPING --> STOPPED --> EXITING -> X
          A   A         |
          |    \___     |
          |        \    |
          |         V   V
        STARTED <-- STARTING
    N)always_iterablec                   F     e Zd ZdZdZ fdZd Zd Zd ZeZ	d Z
e
Z xZS )ChannelFailuresz0Exception raised during errors on Bus.publish().
c                 J    t        t        | 
  |i | t               | _        y)z*Initialize ChannelFailures errors wrapper.N)superr   __init__list_exceptions)selfargskwargs	__class__s      ,/opt/Tautulli/lib/cherrypy/process/wspbus.pyr	   zChannelFailures.__init__d   s!    ot-t>v>6    c                 d    | j                   j                  t        j                         d          y)z%Append the current exception to self.   N)r   appendsysexc_infor   s    r   handle_exceptionz ChannelFailures.handle_exceptioni   s!    q 12r   c                      | j                   dd S )z*Return a list of seen exception instances.N)r   r   s    r   get_instanceszChannelFailures.get_instancesm   s    ""r   c                 t    t        t        | j                               }| j                  j	                  |      S )z5Render the list of errors, which happened in channel.)mapreprr   	delimiterjoin)r   exception_stringss     r   __str__zChannelFailures.__str__q   s.    d&8&8&:;~~""#455r   c                 ,    t        | j                        S )z0Determine whether any error happened in channel.)boolr   r   s    r   __bool__zChannelFailures.__bool__x   s    D$$%%r   )__name__
__module____qualname____doc__r   r	   r   r   r!   __repr__r$   __nonzero____classcell__)r   s   @r   r   r   _   s2    :I"
3#6
 H& Kr   r   c                   (    e Zd Z G d de      Zd Zy)
_StateEnumc                       e Zd ZdZd Zy)_StateEnum.StateNc                      d| j                   z  S )Nz	states.%s)namer   s    r   r)   z_StateEnum.State.__repr__   s    **r   )r%   r&   r'   r1   r)    r   r   Stater/      s    	+r   r3   c                 l    t        || j                        r||_        t        j	                  | ||       y N)
isinstancer3   r1   object__setattr__)r   keyvalues      r   r8   z_StateEnum.__setattr__   s)    eTZZ(EJ4e,r   N)r%   r&   r'   r7   r3   r8   r2   r   r   r-   r-      s    + +-r   r-   SC_OPEN_MAXi   c                       e Zd ZdZeZej
                  ZdZeZ	d Z
ddZd Zd Zd Zd	 Zd
 Zd Zd ZddZddZd Zed        Zed        Zed        Zd Zd ZddZddZy)Busas  Process state-machine and messenger for HTTP site deployment.

    All listeners for a given channel are guaranteed to be called even
    if others at the same channel fail. Each failure is logged, but
    execution proceeds on to the next listener. The only way to stop all
    processing from inside a listener is to raise SystemExit and stop
    the whole server.
    Fc                 |    d| _         t        j                  | _        d}t	        d |D              | _        i | _        y)zInitialize pub/sub bus.F)startstopexitgracefullogmainc              3   4   K   | ]  }|t               f  y wr5   )set).0channels     r   	<genexpr>zBus.__init__.<locals>.<genexpr>   s      
 ce
s   N)execvstatesSTOPPEDstatedict	listeners_priorities)r   channelss     r   r	   zBus.__init__   s<    
^^
E 
#
 
 r   Nc                     |"t        j                  | j                  ||      S | j                  j	                  |t                     }|j                  |       |t        |dd      }|| j                  ||f<   y)zAdd the given callback at the given channel (if not present).

        If callback is None, return a partial suitable for decorating
        the callback.
        N)priorityrS   2   )		functoolspartial	subscriberO   
setdefaultrF   addgetattrrP   )r   rH   callbackrS   ch_listenerss        r   rW   zBus.subscribe   s{     $$!  ~~00#%@"xR8H08'8,-r   c                     | j                   j                  |      }|r&||v r!|j                  |       | j                  ||f= yyy)z(Discard the given callback (if present).N)rO   getdiscardrP   )r   rH   r[   rO   s       r   unsubscribezBus.unsubscribe   sH    NN&&w/	Y.h'  '8!45 /9r   c                       j                   vrg S t               }g } fd j                      D        }t        |t        j                  d            }|D ]  \  }}		 |j                   |	|i |        |r||S # t        $ r  t        $ r1 t        j                         d   }
|r|
j                  dk(  rd|
_
         t        $ r3 |j                          dk(  rn j                  dd|	dd	
       Y w xY w)z7Return output of all subscribers for the given channel.c              3   D   K   | ]  }j                   |f   |f  y wr5   )rP   )rG   listenerrH   r   s     r   rI   zBus.publish.<locals>.<genexpr>   s.      
 w12H=
s    r   )r9   r   rC   z	Error in z
 listener (   Tlevel	traceback)rO   r   sortedoperator
itemgetterr   KeyboardInterrupt
SystemExitr   r   code	Exceptionr   rC   )r   rH   r   r   excoutput	raw_itemsitemsrS   rc   es   ``         r   publishzBus.publish   s   $..(I
 NN73
	 yh&9&9!&<="' 	7Hh7h778	7& I% %  LLN1%166Q;AF 7$$&e#HH'8L#%  77s   !B  A:C=<C=c                     | j                   t        j                  k7  r8t        j                  d| j                   z  t
               | j                          yy)z>Assert that the Bus is not running in atexit handler callback.zThe main thread is exiting, but the Bus is in the %r state; shutting it down automatically now. You must either call bus.block() after start(), or call bus.exit() before the main thread exits.N)rM   rK   EXITINGwarningswarnRuntimeWarningrA   r   s    r   _clean_exitzBus._clean_exit   sH    ::'MM% (,zz2 4B	C
 IIK (r   c                    t        j                  | j                         t        j                  | _        | j                  d       	 | j                  d       t        j                  | _        | j                  d       y	# t        t        f$ r  t        $ rO | j                  ddd       t        j                         d   }	 | j                          |# t        $ r Y |w xY ww xY w)
zStart all services.zBus STARTINGr?   zBus STARTEDz-Shutting down due to error in start listener:rd   Tre   r   N)atexitregisterrz   rK   STARTINGrM   rC   rt   STARTEDrk   rl   rn   r   r   rA   )r   e_infos     r   r?   z	Bus.start  s    (()__
 	LL!DJHH]#!:. 	 
	HHD  /\\^A&F		
 L	  L	
	s1   7A? ?AC&CC&	C"C&!C""C&c                 l   | j                   }d}	 | j                          t        j                  | _         | j	                  d       | j                  d       | j	                  d       |t        j                  k(  rt        j                  |       yy# t        $ r t        j                  |       Y Jw xY w)z2Stop all services and prepare to exit the process.F   zBus EXITINGrA   z
Bus EXITEDN)
rM   r@   rK   rv   rC   rt   rn   os_exitr~   )r   	exitstateEX_SOFTWAREs      r   rA   zBus.exit  s    JJ		"IIKDJHH]#LL  HH\" '
 HH[! (  	"
 HH[!	"s   AB B32B3c                 2    d| _         | j                          y)zRestart the process (may close connections).

        This method does not restart the process from the calling
        thread; instead, it stops the bus and asks the main thread to
        call execv.
        TN)rJ   rA   r   s    r   restartzBus.restart7  s     
		r   c                 H    | j                  d       | j                  d       y)zAdvise all services to reload.zBus gracefulrB   N)rC   rt   r   s    r   rB   zBus.gracefulA  s     Z r   c                 t   	 | j                  t        j                  |d       | j                  d       t        j                         D ]p  }|t        j                         k7  st        |t        j                        r6|j                  rC| j                  d|j                  z         |j                          r | j                   r| j#                          yy# t        t        f$ r$ | j                  d       | j                          Y t        $ r# | j                  d       | j                           w xY w)a  Wait for the EXITING state, KeyboardInterrupt or SystemExit.

        This function is intended to be called only by the main thread.
        After waiting for the EXITING state, it also waits for all
        threads to terminate, and then calls os.execv if self.execv is
        True. This design allows another thread to call bus.restart, yet
        have the main thread perform the actual execv call (required on
        some platforms).
        rD   )intervalrH   z%Keyboard Interrupt: shutting down busz$SystemExit raised: shutting down busz)Waiting for child threads to terminate...zWaiting for thread %s.N)waitrK   rv   rk   IOErrorrC   rA   rl   	threading	enumeratecurrent_threadr6   _MainThreaddaemonr1   r   rJ   	_do_execv)r   r   ts      r   blockz	Bus.blockF  s    
	IIfnnxIH  	<=$$& 	A 1133"1i&;&;< 1AFF:;	 ::NN ? "7+ 	 HH<=IIK 	HH;<IIK	s   "C 0D7+D7c                     t        t        |            }| j                  |vr6t        j                  |       | j                  |       | j                  |vr5yy)z=Poll for the given state(s) at intervals; publish to channel.N)rF   r   rM   timesleeprt   )r   rM   r   rH   rK   s        r   r   zBus.waitt  sC    _U+,jj&JJx LL! jj&r   c                 t   	 | j                         }| j                  ddj                  |      z         | j                  t        j                         t        j                  dd dk(  rddlm} |t        j                  dk(  r|D cg c]  }d	|z  	 }}t        j                  t               | j                   r| j#                          t        j$                  t        j                  |       y# t        $ r8 	 t        j                  g| j	                         z   t        j
                  z   }Y w xY wc c}w )
zRe-execute the current process.

        This must be called from the main thread, because certain
        platforms (OS X) don't allow execv to be called in a child
        thread very well.
        zRe-spawning %s N   javar   )SystemRestartwin32z"%s")_get_true_argvNotImplementedErrorr   
executable_get_interpreter_argvargvrC   r   _extend_pythonpathr   environplatform_systemrestartr   chdir_startup_cwdmax_cloexec_files_set_cloexecrJ   )r   r   r   args       r   r   zBus._do_execv|  s    	N&&(D
 	!CHHTN23

+<<v%4||w&04555HH\"%%!!#HHS^^T*% # 	N-NN#d&@&@&BBSXXMD	N 6s   C1 	D51=D21D2c                  P    t        t        dd      rg S t        j                         S )a  Retrieve current Python interpreter's arguments.

        Returns empty tuple in case of frozen mode, uses built-in arguments
        reproduction function otherwise.

        Frozen mode is possible for the app has been packaged into a binary
        executable using py2exe. In this case the interpreter's arguments are
        already built-in into that executable.

        :seealso: https://github.com/cherrypy/cherrypy/issues/1526
        Ref: https://pythonhosted.org/PyInstaller/runtime-information.html
        frozenF)rZ   r   
subprocess_args_from_interpreter_flagsr2   r   r   r   zBus._get_interpreter_argv  s-     3%0  	@<<>	@r   c                     	 t         j                  }  t        j                  |              }t        j                         }t         j                  j                  t        j                  |      t        j                  |             |d|j                   }t        |      dd}}}	 |j                  d      }||dz
  k  r||dz      dv r	 d}	 |j                  d      }||dz
  k  r||dz      dk(  rd}|r	 |r||k  r	 t        d      t        j                  d	   }	t        j                   |	t        j"                        s	 t%        d
j'                  |	            |||dz   = |j)                  ||	       |S |r	 t        d      	 |S # t        t        f$ r d}Y w xY w# t        t        f$ r d}Y w xY w# t$        $ r 	 t*        w xY w)a(  Retrieve all real arguments of the python interpreter.

        ...even those not listed in ``sys.argv``

        :seealso: http://stackoverflow.com/a/28338254/595220
        :seealso: http://stackoverflow.com/a/6683222/595220
        :seealso: http://stackoverflow.com/a/28414807/595220
        NF-mr   )-cr   Tr   z[Cannot reconstruct command from '-c'. Ref: https://github.com/cherrypy/cherrypy/issues/1545r   z9{} doesn't seem to be a module accessible by current user   )ctypes	c_wchar_pPOINTERc_int	pythonapiPy_GetArgcArgvbyrefr:   lenindex
IndexError
ValueErrorRuntimeErrorr   r   r   accessR_OKAttributeErrorformatinsertr   )
char_pr   argc_argvargv_len
is_command	is_modulem_indc_indoriginal_modules
             r   r   zBus._get_true_argv  s   I	%%F)6>>&)+D<<>D++T"T"
 $**%E /2%j%)jH	D)8a<'E%!),<,L !%ID)8a<'E%!),<,D!%J D%%-5&KL L #&((1+yy"'':7(55;VO5LN N %	/*UO4$ L# I"LM M " LY 
+  
+ 2  
	& &%
	&sU   B"F5 %&F &F 2BF5 6F5 FF5 FF5 F2/F5 1F22F5 5Gc                     dt         j                  z   }| j                  dd      }t        j                  d   dk(  xr |j                  |       }|r	||z   | d<   yy)a;  Prepend current working dir to PATH environment variable if needed.

        If sys.path[0] is an empty string, the interpreter was likely
        invoked with -m and the effective path is about to change on re-
        exec.  Add the current directory to $PYTHONPATH to ensure that
        the new process sees the same path.

        This issue cannot be addressed in the general case because
        Python cannot reliably reconstruct the original command line (
        http://bugs.python.org/issue14208).

        (This idea filched from tornado.autoreload)
        .
PYTHONPATH r   N)r   pathsepr^   r   path
startswith)envpath_prefixexisting_pathneeds_patchs       r   r   zBus._extend_pythonpath   sf     BJJ&b1HHQK2 6((55 	
  +m ;C r   c                    t        d| j                        D ]]  }	 t        j                  |t        j                        }t        j                  |t        j
                  |t        j                  z         _ y# t        $ r Y lw xY w)a  Set the CLOEXEC flag on all open files (except stdin/out/err).

        If self.max_cloexec_files is an integer (the default), then on
        platforms which support it, it represents the max open files
        setting for the operating system. This function will be called
        just before the process is restarted via os.execv() to prevent
        open files from persisting into the new process.

        Set self.max_cloexec_files to 0 to disable this behavior.
           N)ranger   fcntlF_GETFDr   F_SETFD
FD_CLOEXEC)r   fdflagss      r   r   zBus._set_cloexec  sp     4112 	EBB6 KKEMM553C3C+CD	E  s   $A88	BBc                     t         j                  | _        | j                  d       | j	                  d       t         j
                  | _        | j                  d       y)zStop all services.zBus STOPPINGr@   zBus STOPPEDN)rK   STOPPINGrM   rC   rt   rL   r   s    r   r@   zBus.stop+  s<    __
 V^^
r   c                      |d}|i }|f|z   } fd}t        j                  |||      }d|j                  z   |_        |j                           j                          |S )z?Start 'func' in a new thread T, then start self (and return T).r2   c                 T    j                  t        j                          | |i | y r5   )r   rK   r   )funcakwr   s      r   	_callbackz*Bus.start_with_callback.<locals>._callback;  s    IIfnn%!NrNr   )targetr   r   zBus Callback )r   Threadr1   r?   )r   r   r   r   r   r   s   `     r   start_with_callbackzBus.start_with_callback3  sd    <D>Fw~	 IDH 166)		

r   c                     |r9|ddj                  t        j                  t        j                                z   z  }| j                  d||       y)zPLog the given message.

        Append the last traceback if requested.
        r   r   rC   N)r   
_tracebackformat_exceptionr   r   rt   )r   msgrf   rg   s       r   rC   zBus.logF  s@    
 4"''*"="=s||~"NOOOCUC'r   )NN)皙?)r   N)r      F)r%   r&   r'   r(   rK   rL   rM   rJ   	max_filesr   r	   rW   r`   rt   rz   r?   rA   r   rB   r   r   r   staticmethodr   r   r   r   r@   r   rC   r2   r   r   r=   r=      s     FNNEE!	9(6"H0"6!
,\"+: @ @" R Rh < <0E$ &(r   r=   )#r(   r|   r   ImportErrorri   r   r   r   r   rg   r   rw   r   rU   more_itertoolsr   getcwdr   rn   r   r7   r-   rK   r3   rL   r~   r   r   rv   r   sysconfr   r   r=   busr2   r   r   <module>r      s(  ;z   	 
       * ryy{i B- - 
,,.,,.BJJ}-	
l(& l(^ 
e]   Fv  	  Is5   C8 D D 8DDDDDD