o
    tf:                     @   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ZddlZddlZddl	Z	ddl
m
Z
mZmZ daejejfejejfgZdd Zdd Zdd	 Zd
d Zdd Zdd ZG dd deZG dd deZG dd deZdd Zdd Zd ddZdd Z e!dkre   dS dS )!a  A server to hand out network ports to applications running on one host.

Typical usage:
 1) Run one instance of this process on each of your unittest farm hosts.
 2) Set the PORTSERVER_ADDRESS environment variable in your test runner
    environment to let the portpicker library know to use a port server
    rather than attempt to find ports on its own.

$ /path/to/portserver.py &
$ export PORTSERVER_ADDRESS=@unittest-portserver
$ # ... launch a bunch of unittest runners using portpicker ...
    N)datetimetimezone	timedeltac                 C   (   zt |  W S  t jy   Y dS w )N )psutilProcessZcmdlineNoSuchProcesspid r   `/var/www/html/software/conda/envs/catlas/lib/python3.10/site-packages/../../../bin/portserver.py_get_process_command_line/   
   r   c                 C   r   )N        )r   r   Zcreate_timer	   r
   r   r   r   _get_process_start_time6   r   r   c              	   C   s   d}t jt jfD ]W}zt  |||}d}W n
 t jy   Y qw z:z"|t jt jd |d| f |t jkr<|	d |
 d } W n t jyT   Y W |   dS w W |  q|  w |rd| S dS )a  Try to bind to a socket of the specified type, protocol, and port.

    For the port to be considered available, the kernel must support at least
    one of (IPv6, IPv4), and the port must be available on each supported
    family.

    Args:
      port: The port number to bind to, or 0 to have the OS pick a free port.
      socket_type: The type of the socket (ex: socket.SOCK_STREAM).
      socket_proto: The protocol of the socket (ex: socket.IPPROTO_TCP).

    Returns:
      The port number on success or None on failure.
    FT   r   N)socketAF_INET6AF_INETerror
setsockopt
SOL_SOCKETSO_REUSEADDRbindSOCK_STREAMlistengetsocknameclose)portsocket_typeZsocket_protoZ
got_socketfamilysockr   r   r   _bind>   s*   

r#   c                 C   s(   t | gtd R  ot | gtd R  S )zCheck if specified port is free.

    Args:
      port: integer, port to check
    Returns:
      boolean, whether it is free to use for both TCP and UDP
    r   r   )r#   _PROTOSr   r   r   r   _is_port_freea   s   (r&   c                 C   sH   | dkrt d dS | dkrt d dS t| s"t d dS dS )zGDetermine if we should allocate a port for use by the given process id.r   z$Not allocating a port to invalid pidFr   zNot allocating a port to init.z/Not allocating a port to a non-existent processT)loginfor   Z
pid_existsr
   r   r   r   _should_allocate_portl   s   



r)   c                    s2    fdd}t  }|j||dI dH ^}}|S )z.Start the server on Windows using named pipes.c                     s   t  } t |  }|S N)asyncioStreamReaderStreamReaderProtocol)stream_readerZstream_reader_protocolclient_connected_cbr   r   protocol_factory   s
   z/_start_windows_server.<locals>.protocol_factory)addressN)r+   get_event_loopZstart_serving_pipe)r0   pathr1   loopserver_r   r/   r   _start_windows_server}   s
   r8   c                   @   s   e Zd ZdZdZdd ZdS )	_PortInfozContainer class for information about a given port assignment.

    Attributes:
      port: integer port number
      pid: integer process id or 0 if unassigned.
      start_time: Time in seconds since the epoch that the process started.
    r   r   
start_timec                 C   s   || _ d| _d| _d S )Nr   r   r:   )selfr   r   r   r   __init__   s   
z_PortInfo.__init__N)__name__
__module____qualname____doc__	__slots__r=   r   r   r   r   r9      s    r9   c                   @   0   e Zd ZdZdd Zdd Zdd Zdd	 Zd
S )	_PortPoola  Manage available ports for processes.

    Ports are reclaimed when the reserving process exits and the reserved port
    is no longer in use.  Only ports which are free for both TCP and UDP will be
    handed out.  It is easier to not differentiate between protocols.

    The pool must be pre-seeded with add_port_to_free_pool() calls
    after which get_port_for_process() will allocate and reclaim ports.
    The len() of a _PortPool returns the total number of ports being managed.

    Attributes:
      ports_checked_for_last_request: The number of ports examined in order to
          return from the most recent get_port_for_process() request.  A high
          number here likely means the number of available ports with no active
          process using them is getting low.
    c                 C   s   t  | _d| _d S )Nr   )collectionsdeque_port_queueports_checked_for_last_requestr<   r   r   r   r=      s   

z_PortPool.__init__c                 C   s
   t | jS r*   )lenrG   rI   r   r   r   	num_ports   s   
z_PortPool.num_portsc                 C   s   | j stdd}t| j }||k rW| j  }| j | |d7 }|jdks.|jt|jkrSt|j	rJ||_t||_|jsDt
d| || _|j	S t
d|j	|j ||k st
d || _dS )zCAllocates and returns port for pid or 0 if none could be allocated.zNo ports being managed.r   r   r   z!Can't read start time for pid %d.z0Port %d unexpectedly in use, last owning pid %d.zAll ports in use.)rG   RuntimeErrorrJ   pop
appendleftr;   r   r   r&   r   r'   r(   rH   )r<   r   Zcheck_countZmax_ports_to_test	candidater   r   r   get_port_for_process   s2   





z_PortPool.get_port_for_processc                 C   s6   |dk s|dkrt d| t|d}| j| dS )z/Add a new port to the free pool for allocation.r     z-Port must be in the [1, 65535] range, not %d.r%   N)
ValueErrorr9   rG   append)r<   r   Z	port_infor   r   r   add_port_to_free_pool   s   
z_PortPool.add_port_to_free_poolN)r>   r?   r@   rA   r=   rK   rP   rT   r   r   r   r   rD      s    rD   c                   @   rC   )_PortServerRequestHandlera  A class to handle port allocation and status requests.

    Allocates ports to process ids via the dead simple port server protocol
    when the handle_port_request asyncio.coroutine handler has been registered.
    Statistics can be logged using the dump_stats method.
    c                 C   s4   t  | _d| _d| _d| _|D ]}| j| qdS )zInitialize a new port server.

        Args:
          ports_to_serve: A sequence of unique port numbers to test and offer
              up to clients.
        r   N)rD   
_port_pool_total_allocations_denied_allocations_client_request_errorsrT   )r<   ports_to_server   r   r   r   r=      s   z"_PortServerRequestHandler.__init__c                    s*   | dI d H }| || |  d S )Nd   )read_handle_port_requestr   )r<   readerwriterclient_datar   r   r   handle_port_request   s   z-_PortServerRequestHandler.handle_port_requestc              
   C   s   zt |dkrtdt|}W n  ty0 } z|  jd7  _td| W Y d}~dS d}~ww td| tdt| t|sL|  j	d7  _	dS | j
|}|dkrq|  jd7  _|d	|d
 td|| dS |  j	d7  _	dS )zGiven a port request body, parse it and respond appropriately.

        Args:
          client_data: The request bytes from the client.
          writer: The asyncio Writer for the response to be written to.
           z!More than 20 characters in "pid".r   zCould not parse request: %sNzRequest on behalf of pid %d.zcmdline: %sr   z{:d}
zutf-8zAllocated port %d to pid %d)rJ   rR   intrY   r'   warningr(   r   r)   rX   rV   rP   rW   writeformatencodedebug)r<   r`   r_   r   r   r   r   r   r   r]      s*   z._PortServerRequestHandler._handle_port_requestc                 C   s   t d g }|d| j |d| j |d| j  |d| jj |d| j	 |D ]}t | q9dS )z!Logs statistics of our operation.zDumping statistics:zclient-request-errors {}zdenied-allocations {}znum-ports-managed {}z%num-ports-checked-for-last-request {}ztotal-allocations {}N)
r'   r(   rS   rf   rY   rX   rV   rK   rH   rW   )r<   statsstatr   r   r   
dump_stats  s   

z$_PortServerRequestHandler.dump_statsN)r>   r?   r@   rA   r=   ra   r]   rk   r   r   r   r   rU      s    rU   c                  C   sf   t  } | jdtddd | jddtddd | jd	d
ddd | jdd
ddd | tjdd S )z+Configure and parse our command line flags.z--portserver_static_poolz15000-24999z<Comma separated N-P Range(s) of ports to manage (inclusive).)typedefaulthelpz--portserver_addressz --portserver_unix_socket_addressz@unittest-portserverzAddress of AF_UNIX socket on which to listen on Unix (first @ is a NUL) or the name of the pipe on Windows (first @ is the \\.\pipe\ prefix).z	--verbose
store_trueFzEnable verbose messages.)actionrm   rn   z--debugzEnable full debug messages.r   N)argparseArgumentParseradd_argumentstr
parse_argssysargv)parserr   r   r   _parse_command_line&  s2   ry   c              	   C   s   t  }| dD ]@}z|dd\}}t|t|}}W n ty,   td| Y qw |dk s5|dkr<td| q|t t||d  q|S )zCGiven a 'N-P,X-Y' description of port ranges, return a set of ints.,-r   z"Ignoring unparsable port range %r.rQ   z%Ignoring out of bounds port range %r.)setsplitrc   rR   r'   r   updaterange)Zpool_strZportsZ	range_strabstartendr   r   r   _parse_port_rangesA  s   r   Fc                 C   sJ   |rt jnt j}t jddd|d t dat| r t j dS | dS )zAConfigure the log global, message format, and verbosity settings.zM{levelname[0]}{asctime}.{msecs:03.0f} {thread} {filename}:{lineno}] {message}z%m%d %H:%M:%S{)rf   datefmtstylelevelZ
portserverN)loggingDEBUGINFObasicConfig	getLoggerr'   setLevel)verboserh   Zoverall_levelr   r   r   _configure_loggingR  s   
r   c                     s  t  } | jr
dtj_t| j| jd t| j}|s#t	
d td t|}tjdkr3tt  t  tjdkrW fdd d t|j| jd	d
dd}n% tj|j tjdk rhd ini }tj|jfd| jd	ddi|}| j} |}t	d| z   W n t y   t	d Y nw |!  tjdkr |"   #tj  !  |  t	d d S )NT)r   rh   z;No ports.  Invalid port ranges in --portserver_static_pool?r   win32c                      s     d d S )N      ?)
call_laterr   Z
event_looplisten_for_signalr   r   r   w  s   zmain.<locals>.listen_for_signalr   @z	\\.\pipe\)r4   )   
   r5   r4    zServing on %szStopping due to ^C.zGoodbye.)$ry   rh   r+   tasks_DEBUGr   r   r   Zportserver_static_poolr'   r   rv   exitrU   platformset_event_loopZProactorEventLoopr3   r   r8   ra   Zportserver_addressreplaceadd_signal_handlersignalSIGUSR1rk   version_infostart_unix_serverrun_until_completer(   run_foreverKeyboardInterruptr   wait_closedremove_signal_handler)configrZ   Zrequest_handlercoroZold_py_loopserver_addressr6   r   r   r   mainb  sZ   






r   __main__)FF)"rA   rq   r+   rE   r   r   r   rv   r   
subprocessr   r   r   r'   r   IPPROTO_TCP
SOCK_DGRAMIPPROTO_UDPr$   r   r   r#   r&   r)   r8   objectr9   rD   rU   ry   r   r   r   r>   r   r   r   r   <module>   s>   

#AI
<
