o
    u]E                     @   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ZddlmZ zddlZdZW n eyC   dZY nw ej rPddlmZ nddlmZ z
eejd ZW n eyk   dZY nw ejdkZi Zejjd	d
ddZ e dd Z!e dd Z"e dd Z#dd Z$dd Z%dd Z&dd Z'dd Z(d-ddZ)dd  Z*d!d" Z+d#d$ Z,d%d& Z-d'd( Z.d)d* Z/d+d, Z0e"Z1e#Z2e$Z3e%Z4e&Z5e'Z6e)Z7dS ).a7  
Helper functions for querying process and system information from the /proc
contents. Fetching information this way provides huge performance benefits
over lookups via system utilities (ps, netstat, etc). For instance, resolving
connections this way cuts the runtime by around 90% verses the alternatives.
These functions may not work on all platforms (only Linux?).

The method for reading these files (and a little code) are borrowed from
`psutil <https://code.google.com/p/psutil/>`_, which was written by Jay Loden,
Dave Daeschler, Giampaolo Rodola' and is under the BSD license.

**These functions are not being vended to stem users. They may change in the
future, use them at your own risk.**

.. versionchanged:: 1.3.0
   Dropped the get_* prefix from several function names. The old names still
   work, but are deprecated aliases.

**Module Overview:**

::

  is_available - checks if proc utilities can be used on this system
  system_start_time - unix timestamp for when the system started
  physical_memory - memory available on this system
  cwd - provides the current working directory for a process
  uid - provides the user id a process is running under
  memory_usage - provides the memory usage of a process
  stats - queries statistics about a process
  file_descriptors_used - number of file descriptors used by a process
  connections - provides the connections made by a process

.. data:: Stat (enum)

  Types of data available via the :func:`~stem.util.proc.stats` function.

  ============== ===========
  Stat           Description
  ============== ===========
  **COMMAND**    command name under which the process is running
  **CPU_UTIME**  total user time spent on the process
  **CPU_STIME**  total system time spent on the process
  **START_TIME** when this process began, in unix time
  ============== ===========
    N)logTF)	lru_cache
SC_CLK_TCKlittle)COMMANDZcommand)	CPU_UTIMEutime)	CPU_STIMEZstime)
START_TIMEz
start timec                  C   s4   t  dkrdS d} | D ]}tj|s dS qdS )z
  Checks if proc information is available on this platform.

  :returns: **True** if proc contents exist on this platform, **False** otherwise
  ZLinuxF)
/proc/stat/proc/meminfo/proc/net/tcp/proc/net/udpT)platformsystemospathexists)Z
proc_pathsr    r   0/usr/lib/python3/dist-packages/stem/util/proc.pyis_available[   s   r   c                  C   sb   t   d} }tdd|}zt|  d }t|d|  |W S    td| }t|| |)z
  Provides the unix time (seconds since epoch) when the system started.

  :returns: **float** for the unix time of when the system started

  :raises: **IOError** if it can't be determined
  zsystem start timer   Zbtime   z/proc/stat[btime]z.unable to parse the /proc/stat btime entry: %s)time	_get_linefloatstripsplit_log_runtimeIOError_log_failure)
start_time	parameterZ
btime_lineresultexcr   r   r   system_start_timep      

r$   c                  C   sb   t   d} }tdd|}zt| d d }t|d|  |W S    td| }t|| |)z
  Provides the total physical memory on the system in bytes.

  :returns: **int** for the bytes of physical memory this system has

  :raises: **IOError** if it can't be determined
  zsystem physical memoryr   z	MemTotal:r      z/proc/meminfo[MemTotal]z4unable to parse the /proc/meminfo MemTotal entry: %sr   r   intr   r   r   r   )r    r!   Zmem_total_liner"   r#   r   r   r   physical_memory   r%   r)   c                 C   sl   t   d}}d|  }| dkrd}nzt|}W n ty-   td| }t|| |w t||| |S )z
  Provides the current working directory for the given process.

  :param int pid: process id of the process to be queried

  :returns: **str** with the path of the working directory for the process

  :raises: **IOError** if it can't be determined
  cwdz/proc/%s/cwdr    zunable to read %s)r   r   readlinkOSErrorr   r   r   )pidr    r!   Zproc_cwd_linkr*   r#   r   r   r   r*      s   
r*   c                 C   sn   t   d}}d|  }t|d|}zt| d }t|d| | |W S    td||f }t|| |)z
  Provides the user ID the given process is running under.

  :param int pid: process id of the process to be queried

  :returns: **int** with the user id for the owner of the process

  :raises: **IOError** if it can't be determined
  uid/proc/%s/statuszUid:r   z%s[Uid]z$unable to parse the %s Uid entry: %sr'   )r.   r    r!   status_pathZuid_liner"   r#   r   r   r   r/      s   
r/   c              	   C   s   | dkrdS t   d}}d|  }t|d|}z%t|d  d d }t|d	  d d }t|d
| | ||fW S    td|d|f }t|| |)a'  
  Provides the memory usage in bytes for the given process.

  :param int pid: process id of the process to be queried

  :returns: **tuple** of two ints with the memory usage of the process, of the
    form **(resident_size, virtual_size)**

  :raises: **IOError** if it can't be determined
  r   )r   r   zmemory usager0   )VmRSS:VmSize:r2   r   r&   r3   z%s[VmRSS|VmSize]z3unable to parse the %s VmRSS and VmSize entries: %s, )r   
_get_linesr(   r   r   r   joinr   )r.   r    r!   r1   Z	mem_linesZresidentSizeZvirtualSizer#   r   r   r   memory_usage   s   

r7   c                 G   s  t du rtdt dd| }}d|  }t|t| |}g }|d|d}}|dkrU|dkrU||d|  |||d	 |  |||d	 d  7 }t	|d
k rtt
|d |d |d rttd| }	t||	 |	g }
|D ]s}|tjkr| dkr|
d qx|
|d	  qx|tjkr| dkr|
d qx|
tt|d t   qx|tjkr| dkr|
d qx|
tt|d t   qx|tjkr| dkrt   S t|d t  }|
t|t   qxt||| t|
S )aY  
  Provides process specific information. See the :data:`~stem.util.proc.Stat`
  enum for valid options.

  :param int pid: process id of the process to be queried
  :param Stat stat_types: information to be provided back

  :returns: **tuple** with all of the requested statistics as strings

  :raises: **IOError** if it can't be determined
  NzUnable to look up SC_CLK_TCKz
process %sr4   z/proc/%s/stat()r   ,            z&stat file had an unexpected format: %sr   Zsched0)CLOCK_TICKSr   r   r6   r   strfindappendr   len	_is_floatr   Statr   r   r   r	   r
   r$   r   tuple)r.   Z
stat_typesr    r!   Z	stat_pathZ	stat_lineZ	stat_compZ	cmd_startZcmd_endr#   resultsZ	stat_typeZp_start_timer   r   r   stats   sJ   $





rI   c              
   C   sz   zt | } | dk rtd|  W n ttfy   td|  w z
ttd|  W S  ty< } ztd| d}~ww )a  
  Provides the number of file descriptors currently being used by a process.

  .. versionadded:: 1.3.0

  :param int pid: process id of the process to be queried

  :returns: **int** of the number of file descriptors used

  :raises: **IOError** if it can't be determined
  r   "Process pids can't be negative: %sProcess pid was non-numeric: %sz/proc/%i/fdz3Unable to check number of file descriptors used: %sN)r(   r   
ValueError	TypeErrorrD   r   listdir	Exception)r.   r#   r   r   r   file_descriptors_used8  s   rP   c                 C   s  t   g }}| r-d|  }zt| } | dk rtd|  W n ttfy,   td|  w |r4d| }nd}zts>td| rDt| nt }|rVtj	j
tt|jnd}d	D ]}|d
rhtj|shqZ|dd d
}|d
}	zt|d}
|
  |
D ]|}| dd \
}}}}}}}}}}|r||vrq|r||krq|dkr|dkrq|d}t|d| }t||d d d}|d}t|d| }t||d d d}|dks|dkrq|dks|dkrq|tj	j||||||	 qW d   n	1 sw   Y  W qZ ty& } ztd||f d}~w ty: } ztd||f d}~ww t|d| |W S  tyV } zt||  d}~ww )a  
  Queries connections from the proc contents. This matches netstat, lsof, and
  friends but is much faster. If no **pid** or **user** are provided this
  provides all present connections.

  :param int pid: pid to provide connections for
  :param str user: username to look up connections for

  :returns: **list** of :class:`~stem.util.connection.Connection` instances

  :raises: **IOError** if it can't be determined
  zconnections for pid %sr   rJ   rK   zconnections for user %szall connectionszCThis requires python's pwd module, which is unavailable on Windows.N)r   z/proc/net/tcp6r   z/proc/net/udp66
   rbZtcps   01   :r      z0.0.0.0z0000:0000:0000:0000:0000:0000zunable to read '%s': %szunable to parse '%s': %sz/proc/net/[tcp|udp]) r   r(   r   rL   rM   IS_PWD_AVAILABLE_inodes_for_socketssetstemutil	str_tools	_to_bytesrA   pwdgetpwnamZpw_uidendswithr   r   r   rstripopenreadliner   rB   _unpack_addrrC   
connectionZ
ConnectionrO   r   r   )r.   userr    Zconnr!   inodesZprocess_uidZproc_file_pathZprotocolZis_ipv6	proc_fileline_Zl_dstZr_dststatusr/   inodeZdivZl_addrZl_portZr_addrZr_portr#   r   r   r   connectionsS  s~   
"
$

 
rl   c                 C   s   t  }z	td|  }W n ty } ztd| d}~ww |D ]B}d| |f }zt|}|drB|tj	j
|dd  W q! tyc } ztj|sWW Y d}~q!td||f d}~ww |S )	z
  Provides inodes in use by a process for its sockets.

  :param int pid: process id of the process to be queried

  :returns: **set** with inodes for its sockets

  :raises: **IOError** if it can't be determined
  z/proc/%s/fdz'Unable to read our file descriptors: %sNz/proc/%s/fd/%szsocket:[   r:   z8unable to determine file descriptor destination (%s): %s)rX   r   rN   r-   r   r,   
startswithaddrY   rZ   r[   r\   r   r   )r.   rf   Zfd_contentsr#   fdZfd_pathZfd_namer   r   r   rW     s*   

rW   c                    s   | t vrjt| dkr(trt| ddd nt| }ttj|t | < t |  S trWg }tdD ] }| d| d|d    | fddtdD ddd 7 }q0d	|}n| }t
jjttjt|t | < t |  S )	a  
  Translates an address entry in the /proc/net/* contents to a human readable
  form (`reference <http://linuxdevcenter.com/pub/a/linux/2000/11/16/LinuxAdmin.html>`_,
  for instance:

  ::

    "0500000A" -> "10.0.0.5"
    "F804012A4A5190010000000002000000" -> "2a01:4f8:190:514a::2"

  :param str addr: proc address entry to be decoded

  :returns: **str** of the decoded address
  rm   Nr:      r   c                    s$   g | ]} d | d |d   qS )   r   r   ).0igroupingr   r   
<listcomp>  s   $ z _unpack_addr.<locals>.<listcomp>    )ENCODED_ADDRrD   IS_LITTLE_ENDIANbase64Z	b16decodesocketZ	inet_ntopZAF_INETranger6   rY   rZ   rd   Zexpand_ipv6_addressZAF_INET6)ZaddrZdecodedinvertedrt   Zencodedr   ru   r   rc     s   "&"rc   c                  G   s.   z| D ]}t | qW dS  ty   Y dS w )NTF)r   rL   )valuevr   r   r   rE     s   
rE   c                 C   s   t | |f|| S )N)r5   )	file_pathZline_prefixr!   r   r   r   r     s   r   c           
   
   C   s   zQt |}t| i }}|D ]}|s n|D ]}||r(|||< ||  nqq|  |rOt|dkrBd| |d f }t|d| d|f }t||W S  tyc }	 zt||	  d}	~	ww )a  
  Fetches lines with the given prefixes from a file. This only provides back
  the first instance of each prefix.

  :param str file_path: path of the file to read
  :param tuple line_prefixes: string prefixes of the lines to return
  :param str parameter: description of the proc attribute being fetch

  :returns: mapping of prefixes to the matching line

  :raises: **IOError** if unable to read the file or can't find all of the prefixes
  r   z%s did not contain a %s entryr   z%s did not contain %s entriesr4   N)	listra   rn   removecloserD   r6   r   r   )
r   Zline_prefixesr!   Zremaining_prefixesrg   rH   rh   prefixmsgr#   r   r   r   r5     s4   


r5   c                 C   s$   t   | }td| ||f  dS )z
  Logs a message indicating a successful proc query.

  :param str parameter: description of the proc attribute being fetch
  :param str proc_location: proc files we were querying
  :param int start_time: unix time for when this query was started
  z#proc call (%s): %s (runtime: %0.4f)N)r   r   debug)r!   Zproc_locationr    Zruntimer   r   r   r   4  s   	r   c                 C   s   t d| |f  dS )z
  Logs a message indicating that the proc query failed.

  :param str parameter: description of the proc attribute being fetch
  :param Exception exc: exception that we're raising
  zproc call failed (%s): %sN)r   r   )r!   r#   r   r   r   r   A  s   r   )NN)8__doc__r{   r   r   r|   sysr   Zstem.prereqrY   Zstem.util.connectionZstem.util.enumZstem.util.str_toolsZ	stem.utilr   r]   rV   ImportErrorZprereqZ_is_lru_cache_available	functoolsr   Zstem.util.lru_cachesysconfsysconf_namesr@   AttributeError	byteorderrz   ry   rZ   enumEnumrF   r   r$   r)   r*   r/   r7   rI   rP   rl   rW   rc   rE   r   r5   r   r   Zget_system_start_timeZget_physical_memoryZget_cwdZget_uidZget_memory_usageZ	get_statsZget_connectionsr   r   r   r   <module>   st   .




!D
U&,
,