o
    u]ʧ                     @   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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 ddlmZ ejjddddZeeeeejeed	d
 e ee!eiZ"i Z#dgZ$dZ%dZ&dZ'dZ(dZ)dZ*dZ+dZ,dZ-dZ.dZ/dZ0dZ1dZ2dZ3dZ4dZ5dZ6dZ7dZ8e9ej:Z;zej<j=Z=de=_>e9ej?e9e;ge=_@W n   dZ=Y daAd aBd!aCeD ZEG d"d# d#eFZGG d$d% d%eGZHG d&d' d'eIZJd(d) ZKd*d+ ZLd,d- ZMd.d/ ZNd0d1 ZOdbd3d4ZPd5d6 ZQdcd7d8ZRd9d: ZSddd<d=ZTd>d? ZUd@dA ZVdBdC ZWdDdE ZXdFdG ZYdHdI ZZdcdJdKZ[dLdM Z\dNdO Z]dPdQ Z^dcdRdSZ_dTdU Z`ed;dddfdVdWZadXdY ZbdZd[ Zcd\d] Zdd^d_ Zed`da ZfeSZgeTZheUZieVZjeXZkeYZleZZme\Zne]ZodS )eaA	  
Helper functions for working with the underlying system. These are mostly os
dependent, only working on linux, osx, and bsd. In almost all cases they're
best-effort, providing **None** if the lookup fails.

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

.. versionchanged:: 1.5.0
   Added the **SYSTEM_CALL_TIME** global, which tracks total time spent making
   system commands.

**Module Overview:**

::

  is_windows - checks if we're running on windows
  is_mac - checks if we're running on a mac
  is_gentoo - checks if we're running on gentoo
  is_slackware - checks if we're running on slackware
  is_bsd - checks if we're running on the bsd family of operating systems

  is_available - determines if a command is available on this system
  is_running - determines if a given process is running
  size_of - provides the memory usage of an object
  call - runs the given system command and provides back the results

  name_by_pid - gets the name for a process by the given pid
  pid_by_name - gets the pid for a process by the given name
  pid_by_port - gets the pid for a process listening to a given port
  pid_by_open_file - gets the pid for the process with an open file
  pids_by_user - provides processes owned by a user
  cwd - provides the current working directory for a given process
  user - provides the user a process is running under
  start_time - provides the unix timestamp when the process started
  tail - provides lines from the end of a file
  bsd_jail_id - provides the BSD jail id a given process is running within
  bsd_jail_path - provides the path of the given BSD jail

  is_tarfile - checks if the given path is a tarball
  expand_path - expands relative paths and ~ entries
  files_with_suffix - provides files with the given suffix

  get_process_name - provides our process' name
  set_process_name - changes our process' name

.. data:: Status (enum)

  State of a subprocess.

  .. versionadded:: 1.6.0

  ====================  ===========
  Status                Description
  ====================  ===========
  PENDING               not yet started
  RUNNING               currently being performed
  DONE                  completed successfully
  FAILED                failed with an exception
  ====================  ===========
    N)	UNDEFINED)logPENDINGRUNNINGDONEFAILEDc                 C   s   t j|  S N)	itertoolschainfrom_iterableitems)d r   2/usr/lib/python3/dist-packages/stem/util/system.py<lambda>f   s    r   Zulimitzps -A co commandzps -ao ucomm=zps -p %s -o commzpgrep -x %szpidof %szps -o pid -C %szps axczlsof -tc %sznetstat -npltuzsockstat -4l -P tcp -p %szlsof -wnP -iTCP -sTCP:LISTENzlsof -tw %szps -o pid -u %szps -o pid -U %szpwdx %szlsof -a -p %s -d cwd -Fnzps -p %s -o jidz	jls -j %si      g        c                   @   s    e Zd ZdZdd Zdd ZdS )	CallErrora  
  Error response when making a system call. This is an **OSError** subclass
  with additional information about the process. Depending on the nature of the
  error not all of these attributes will be available.

  :var str msg: exception string
  :var str command: command that was ran
  :var int exit_status: exit code of the process
  :var float runtime: time the command took to run
  :var str stdout: stdout of the process
  :var str stderr: stderr of the process
  c                 C   s(   || _ || _|| _|| _|| _|| _d S r   )msgcommandexit_statusruntimestdoutstderr)selfr   r   r   r   r   r   r   r   r   __init__   s   
zCallError.__init__c                 C   s   | j S r   )r   )r   r   r   r   __str__   s   zCallError.__str__N)__name__
__module____qualname____doc__r   r   r   r   r   r   r      s    r   c                       s    e Zd ZdZ fddZ  ZS )CallTimeoutErrorz
  Error response when making a system call that has timed out.

  .. versionadded:: 1.6.0

  :var float timeout: time we waited
  c                    s$   t t| |||||| || _d S r   )superr!   r   timeout)r   r   r   r   r   r   r   r#   	__class__r   r   r      s   
zCallTimeoutError.__init__)r   r   r   r    r   __classcell__r   r   r$   r   r!      s    r!   c                   @   s6   e Zd ZdZdddZdd Zd	d
 Zedd ZdS )
DaemonTaska  
  Invokes the given function in a subprocess, returning the value.

  .. versionadded:: 1.6.0

  :var function runner: function to be invoked by the subprocess
  :var tuple args: arguments to provide to the subprocess
  :var int priority: subprocess nice priority

  :var stem.util.system.State status: state of the subprocess
  :var float runtime: seconds subprocess took to complete
  :var object result: return value of subprocess if successful
  :var exception error: exception raised by subprocess if it failed
  Nr   Fc                 C   sL   || _ || _|| _tj| _d | _d | _d | _d | _	d | _
|r$|   d S d S r   )runnerargspriorityStater   statusr   resulterror_process_piperun)r   r(   r)   r*   startr   r   r   r      s   zDaemonTask.__init__c                 C   sT   | j tjkr(t \| _}tjtj|| j	| j
| jfd| _| j  tj| _ dS dS )z\
    Invokes the task if it hasn't already been started. If it has this is a
    no-op.
    )targetr)   N)r,   r+   r   multiprocessingZPiper0   ZProcessr'   _run_wrapperr*   r(   r)   r/   r2   r   )r   Z
child_piper   r   r   r1      s    
zDaemonTask.runc                 C   s   | j tjkr
|   | j tjkr;| j  | j }|d | _ |d | _	| j tj
kr0|d | _n| j tjkr;|d | _| j tj
krD| jS | j tjkrM| jtd| j  )z
    Provides the result of the daemon task. If still running this blocks until
    the task is completed.

    :returns: response of the function we ran

    :raises: exception raised by the function if it failed with one
    r         z+BUG: unexpected status from daemon task, %s)r,   r+   r   r1   r   r/   joinr0   Zrecvr   r   r-   r   r.   RuntimeError)r   Zresponser   r   r   r8     s    





zDaemonTask.joinc              
   C   s   t   }t| zFz|r|| n| }| tjt   | |f W n tyA } z| tjt   | |f W Y d }~nd }~ww W |   d S W |   d S |   w r   )	timeosnicesendr+   r   	Exceptionr   close)Zconnr*   r(   r)   
start_timer-   excr   r   r   r5   +  s   
&zDaemonTask._run_wrapper)Nr   F)	r   r   r   r    r   r1   r8   staticmethodr5   r   r   r   r   r'      s    
 r'   c                   C      t  dkS )z`
  Checks if we are running on Windows.

  :returns: **bool** to indicate if we're on Windows
  ZWindowsplatformsystemr   r   r   r   
is_windows9     rG   c                   C   rC   )z^
  Checks if we are running on Mac OSX.

  :returns: **bool** to indicate if we're on a Mac
  DarwinrD   r   r   r   r   is_macC  rH   rJ   c                   C      t jdS )z]
  Checks if we're running on Gentoo.

  :returns: **bool** to indicate if we're on Gentoo
  z/etc/gentoo-releaser;   pathexistsr   r   r   r   	is_gentooM  rH   rO   c                   C   rK   )zv
  Checks if we are running on a Slackware system.

  :returns: **bool** to indicate if we're on a Slackware system
  z/etc/slackware-versionrL   r   r   r   r   is_slackwareW  rH   rP   c                   C   s   t  dv S )z
  Checks if we are within the BSD family of operating systems. This currently
  recognizes Macs, FreeBSD, and OpenBSD but may be expanded later.

  :returns: **bool** to indicate if we're on a BSD OS
  )rI   FreeBSDOpenBSDZNetBSDrD   r   r   r   r   is_bsda  s   rS   Tc                 C   s   d| v r|  dd } | tv rdS |r| tv rt|  S dtjvr"dS d}tjd  tjD ]!}tj|| }t r=|d7 }tj	|rNt
|tjrNd} nq-|t| < |S )aS  
  Checks the current PATH to see if a command is available or not. If more
  than one command is present (for instance "ls -a | grep foo") then this
  just checks the first.

  Note that shell (like cd and ulimit) aren't in the PATH so this lookup will
  try to assume that it's available. This only happends for recognized shell
  commands (those in SHELL_COMMANDS).

  :param str command: command to search for
  :param bool cached: makes use of available cached results if **True**

  :returns: **True** if an executable we can use by that name exists in the
    PATH, **False** otherwise
   r   TPATHF.exe)splitSHELL_COMMANDSCMD_AVAILABLE_CACHEr;   environpathseprM   r8   rG   rN   accessX_OK)r   cachedZ
cmd_existsrM   Zcmd_pathr   r   r   is_availablel  s&   
r_   c                 C   s   t | trz	t| d W dS  ty   Y dS w tdrUt r%t}t}nt}t}t	|d}|s5t	|d}|rUdd |D }t
j| rG| g} | D ]	}||v rR dS qIdS dS )a  
  Checks for if a process with a given name or pid is running.

  .. versionchanged:: 1.6.0
     Added support for list and pid arguments.

  :param str,list,int command: process name if a str, multiple process names if
    a list, or pid if an int to be checked

  :returns: **True** if the process is running, **False** if it's not among ps
    results, and **None** if ps can't be queried
  r   TFpsNc                 S   s   g | ]}|  qS r   )strip).0cr   r   r   
<listcomp>  s    zis_running.<locals>.<listcomp>)
isinstanceintr;   killOSErrorr_   rS   IS_RUNNING_PS_BSDIS_RUNNING_PS_LINUXcallstemutilZ_is_str)r   Zprimary_resolverZsecondary_resolverZcommand_listingcmdr   r   r   
is_running  s4   


ro   c                 C   s   t j r	td|du rt }nt| |v rdS zt| }W n ty.   td}Y nw |	t|  t
| tv rNtt
|  | D ]	}|t||7 }qD|S )aF  
  Provides the `approximate memory usage of an object
  <https://code.activestate.com/recipes/577504/>`_. This can recurse tuples,
  lists, deques, dicts, and sets. To teach this function to inspect additional
  object types expand SIZE_RECURSES...

  ::

    stem.util.system.SIZE_RECURSES[SomeClass] = SomeClass.get_elements

  .. versionadded:: 1.6.0

  :param object obj: object to provide the size of
  :param set exclude: object ids to exclude from size estimation

  :returns: **int** with the size of the object in bytes

  :raises: **NotImplementedError** if using PyPy
  z'PyPy does not implement sys.getsizeof()Nr   )rl   ZprereqZis_pypyNotImplementedErrorsetidsys	getsizeof	TypeErroraddtypeSIZE_RECURSESsize_of)objZexcludesizeentryr   r   r   ry     s    
ry   c                 C   s   d}t jj r"zt jj| t jjjjd }W n	 ty!   Y nw |sLztt	|  }W n t
y7   d}Y nw |rLt|dkrL|d dkrL|d  }|S )a<  
  Attempts to determine the name a given process is running under (not
  including arguments). This uses...

  ::

    1. Information from /proc
    2. ps -p <pid> -o command

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

  :returns: **str** with the process name, **None** if it can't be determined
  Nr   r7   COMMANDr6   )rl   rm   procr_   statsStatr}   IOErrorrk   GET_NAME_BY_PID_PSrh   lenra   )pidprocess_nameresultsr   r   r   name_by_pid   s     r   Fc                    s  t dr0tt  d}|r0zttt|}|r|W S t|dkr%|d W S W n	 ty/   Y nw t drjtt  d}|rjt|dkrjzttt|d 	 }|rT|W S t|dkr_|d W S W n	 tyi   Y nw t drt
 stt  d}|rzttt|dd }|r|W S t|dkr|d W S W n	 ty   Y nw t
 rttd}|rׇ fdd|D }zttt|}|r|W S t|dkr|d W S W n	 ty   Y nw t d	r
tt  d}|r
zttt|}|r|W S t|dkr|d W S W n
 ty	   Y nw t d
rYt rY ds d  g }tjjd
d}|rYtd  }|D ]}||}|rG|t|d q3|rN|S t|dkrY|d S td   |reg S dS )a  
  Attempts to determine the process id for a running process, using...

  ::

    1. pgrep -x <name>
    2. pidof <name>
    3. ps -o pid -C <name> (linux)
       ps axc | egrep " <name>$" (bsd)
    4. lsof -tc <name>
    5. tasklist | str <name>.exe

  :param str process_name: process name for which to fetch the pid
  :param bool multiple: provides a list of all pids if **True**, otherwise
    results with multiple processes are discarded

  :returns:
    Response depends upon the 'multiple' argument as follows...

    * if **False** then this provides an **int** with the process id or **None** if it can't be determined
    * if **True** then this provides a **list** of all **int** process ids, and an empty list if it can't be determined
  ZpgrepNr6   r   pidofr`   c                    s&   g | ]}| d   r| d qS )z %sr   )endswithrW   rb   rr   r   r   rd     s   & zpid_by_name.<locals>.<listcomp>lsofZtasklistrV   z^\s*%s\s+(?P<pid>[0-9]*)r   z failed to resolve a pid for '%s')r_   rk   GET_PID_BY_NAME_PGREPlistmaprf   r   
ValueErrorGET_PID_BY_NAME_PIDOFrW   rS   GET_PID_BY_NAME_PS_LINUXGET_PID_BY_NAME_PS_BSDGET_PID_BY_NAME_LSOFrG   r   rl   rm   rF   recompilesearchappendgroupr   debug)r   Zmultipler   ZpidsZprocess_idsZtasklist_regexlinematchr   r   r   pid_by_name+  s    








r   c                    s*  t dr=ttd}|r= fdd|D }t|dkr=t|d  dkr=|d  d }|d|d	 }| r=t|S t d
ritt  d}|ri fdd|D }t|dkri|d  d }| rit|S t drtt	d}|r fdd|D }t|dkr|d  d }| rt|S dS )a  
  Attempts to determine the process id for a process with the given port,
  using...

  ::

    1. netstat -npltu | grep 127.0.0.1:<port>
    2. sockstat -4l -P tcp -p <port>
    3. lsof -wnP -iTCP -sTCP:LISTEN | grep ":<port>"

  Most queries limit results to listening TCP connections. This function likely
  won't work on Mac OSX.

  :param int port: port where the process we're looking for is listening

  :returns: **int** with the process id, **None** if it can't be determined
  ZnetstatNc                    s   g | ]
}d   |v r|qS )z127.0.0.1:%sr   r   portr   r   rd     s    zpid_by_port.<locals>.<listcomp>r6   r         /Zsockstatc                    4   g | ]}t | d krd  | d v r|qS )r   :%s   r   rW   r   r   r   r   rd   #     4 r7   r   c                    r   )
   r      r   r   r   r   r   rd   C  r   )
r_   rk   GET_PID_BY_PORT_NETSTATr   rW   findisdigitrf   GET_PID_BY_PORT_SOCKSTATGET_PID_BY_PORT_LSOF)r   r   r   r   r   r   pid_by_port  s4   '
 
r   c                 C   sB   t drtt|  g }t|dkr|d  }| rt|S dS )a  
  Attempts to determine the process id for a process with the given open file,
  using...

  ::

    lsof -w <path>

  :param str path: location of the socket file to query against

  :returns: **int** with the process id, **None** if it can't be determined
  r   r6   r   N)r_   rk   GET_PID_BY_FILE_LSOFr   ra   r   rf   )rM   r   r   r   r   r   pid_by_open_fileN  s   r   c                 C   sb   t dr/t rtt|  d}ntt|  d}|r/zttt|dd W S  ty.   Y dS w dS )z
  Provides processes owned by a given user.

  .. versionadded:: 1.5.0

  :param str user: user to look up processes for

  :returns: **list** with the process ids, **None** if it can't be determined
  r`   Nr6   )	r_   rS   rk   GET_PIDS_BY_USER_BSDGET_PIDS_BY_USER_LINUXr   r   rf   r   )userr   r   r   r   pids_by_usert  s   r   c                 C   sF  t jj rzt jj| W S  ty   Y nw d|  }tdrntt|  d}|s1t	d|  n=|d 
dr@t	d|  n.t|dksX|d d	dksX|d d
|  sbt	d||f  n|d d	dd  S tdrtt|  g }t|dkr|d dr|d dd  }d	|vr|S dS t	d||f  dS )z
  Provides the working directory of the given process.

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

  :returns: **str** with the absolute path for the process' present working
    directory, **None** if it can't be determined
  zcwd(%s):ZpwdxNz!%s pwdx didn't return any resultsr   zNo such processz'%s pwdx processes reported for this pidr6   rT   z%s: z)%s we got unexpected output from pwdx: %sr   r7   r   zn/z)%s we got unexpected output from lsof: %s)rl   rm   r~   r_   cwdr   rk   GET_CWD_PWDXr   r   r   r   count
startswithrW   ra   GET_CWD_LSOF)r   Zlogging_prefixr   Zlsof_resultr   r   r   r     s2   0r   c                 C   s   t | tr	| dk rdS tjj r2zddl}tjj| }|r,| r,|	t|j
W S W n   Y tdrItd|  g }t|dkrI|d  S dS )z
  Provides the user a process is running under.

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

  :returns: **str** with the username a process is running under, **None** if
    it can't be determined
  r   Nr`   zps -o user %sr7   r6   )re   rf   rl   rm   r~   r_   pwduidr   getpwuidZpw_namerk   r   ra   )r   r   r   r   r   r   r   r     s    
r   c                 C   s   t | tr	| dk rdS tjj r,zttjj| tjjjj	d W S  t
y+   Y nw z"td|  g }t|dkrL|d  }t tjj| W S W dS    Y dS )z
  Provides the unix timestamp when the given process started.

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

  :returns: **float** for the unix timestamp when the process began, **None**
    if it can't be determined
  r   Nzps -p %s -o etimer7   r6   )re   rf   rl   rm   r~   r_   floatr   r   Z
START_TIMEr   rk   r   ra   r:   	str_toolsZparse_short_time_label)r   Z
ps_resultsZetimer   r   r   r@     s$   
"r@   c                 c   sP   t | tr(t| d}t||D ]}|V  q	 W d   dS 1 s#w   Y  | dd |  }d}d}|du s>|dkr|dkr|t dkr^| |t d | t| dd\}}n| dd | || }t	|
 D ]}|du s{|dkr|dur|d8 }tjj|V  qq|t8 }|d8 }|du s|dkr|dksBdS dS dS dS )	a  
  Provides lines of a file starting with the end. For instance,
  'tail -n 50 /tmp/my_log' could be done with...

  ::

    reversed(list(tail('/tmp/my_log', 50)))

  :param str,file target: path or file object to read from
  :param int lines: number of lines to read

  :returns: **generator** that reads lines, starting with the end

  :raises: **IOError** if unable to read the file
  rbNr   r7   r          
r6   )re   stropentailseektell
BLOCK_SIZEreadrW   reversed
splitlinesrl   rm   r   Z_to_unicode)r3   linesZtarget_filer   Zblock_end_byteZblock_numberZcontentZcompleted_linesr   r   r   r     s4   
 	(r   c                 C   s   t t|  g }t|dkr%t|d  dkr%|d  }| r%t|S t }|dkr6t	
d|   dS t	d| |f  dS )a  
  Gets the jail id for a process. These seem to only exist for FreeBSD (this
  style for jails does not exist on Linux, OSX, or OpenBSD).

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

  :returns: **int** for the jail id, zero if this can't be determined
  r7   r6   rQ   z)Unable to get the jail id for process %s.z,bsd_jail_id(%s): jail ids do not exist on %sr   )rk   GET_BSD_JAIL_ID_PSr   rW   ra   r   rf   rE   rF   r   warnr   )r   	ps_outputjidZos_namer   r   r   bsd_jail_idN  s    r   c                 C   sJ   | dkr#t t|  g }t|dkr#t|d  dkr#|d  d S dS )z
  Provides the path of the given FreeBSD jail.

  :param int jid: jail id to be queried

  :returns: **str** of the path prefix, **None** if this can't be determined
  r   r7   r6         N)rk   GET_BSD_JAIL_PATHr   rW   )r   Z
jls_outputr   r   r   bsd_jail_pathq  s
   	 r   c              	   C   s6   zt | W S  ttfy   t| d dk Y S w )z
  Returns if the path belongs to a tarfile or not.

  .. versionadded:: 1.2.0

  :param str path: path to be checked

  :returns: **True** if the path belongs to a tarball, **False** otherwise
  r   zapplication/x-tar)tarfile
is_tarfiler   AttributeError	mimetypesZ
guess_type)rM   r   r   r   r     s
   r   c                 C   s   t  r| ddd}n| d}|rtj|r	 |S |dr*tj|}|S |s0t }|ds:|drA|dd }n|dkrGd	}|d	krO|}|S tj	||}|S )
a  
  Provides an absolute path, expanding tildes with the user's home and
  appending a current working directory if the path was relative.

  :param str path: path to be expanded
  :param str cwd: current working directory to expand relative paths with, our
    process' if this is **None**

  :returns: **str** of the path expanded to be an absolute path, never with an
    ending slash
  r   \~z./z.\r7   N. )
rG   replacerstripr;   rM   isabsr   
expandusergetcwdr8   )rM   r   Zrelative_pathr   r   r   expand_path  s(   

r   c                 c   sd    t j| r| |r| V  dS dS t | D ]\}}}|D ]}||r.t j||V  qqdS )a3  
  Iterates over files in a given directory, providing filenames with a certain
  suffix.

  .. versionadded:: 1.2.0

  :param str base_path: directory to be iterated over
  :param str suffix: filename suffix to look for

  :returns: iterator that yields the absolute path for files with the given suffix
  N)r;   rM   isfiler   walkr8   )	base_pathsuffixroot_filesfilenamer   r   r   files_with_suffix  s   


r   c                 C   s<  t | tr| d}nttt| }d\}}}	}
t }zdz|d tv }tj|tj	tj	|||d}|r\|
 du r\t | |krQtd| d|d|dd|td |
 du s:| \}	}
|	 |
 }	}
t | }td	| |f  t rd
|  }|	r|
rt|d|	|
f   n|	rt|d|	   n|
rt|d|
   |
 }|s|dkrtd| |f |	r|	dd W W t tt | 7 aW d   S 1 sw   Y  S g W W t tt | 7 aW d   S 1 sw   Y  S  ty5   td| |f  |tkr4| Y W t tt | 7 aW d   S 1 s.w   Y  S   ty } z@td| |f  |tkro|W  Y d}~W t tt | 7 aW d   S 1 siw   Y  S tt|d||||	|
d}~ww t tt | 7 aW d   w 1 sw   Y  w )a5  
  call(command, default = UNDEFINED, ignore_exit_status = False)

  Issues a command in a subprocess, blocking until completion and returning the
  results. This is not actually ran in a shell so pipes and other shell syntax
  are not permitted.

  .. versionchanged:: 1.5.0
     Providing additional information upon failure by raising a CallError. This
     is a subclass of OSError, providing backward compatibility.

  .. versionchanged:: 1.5.0
     Added env argument.

  .. versionchanged:: 1.6.0
     Added timeout and cwd arguments.

  :param str,list command: command to be issued
  :param object default: response if the query fails
  :param bool ignore_exit_status: reports failure if our command's exit status
    was non-zero
  :param float timeout: maximum seconds to wait, blocks indefinitely if
    **None**
  :param dict env: environment variables

  :returns: **list** with the lines of output from the command

  :raises:
    * **CallError** if this fails and no default was provided
    * **CallTimeoutError** if the timeout is reached without a default
  rT   )NNNNr   )r   r   shellr   envNz)Process didn't finish after %0.1f secondsr   gMbP?z System call: %s (runtime: %0.2f)zReceived from system (%s)z, stdout:
%s
stderr:
%sz, stdout:
%sz, stderr:
%sz%s returned exit status %izutf-8r   z(System call (timeout): %s (after %0.4fs)z$System call (failed): %s (error: %s))re   r   rW   r   r   r:   rX   
subprocessPopenPIPEZpollr!   r8   sleepZcommunicatera   r   r   
is_tracingZtracerh   decoder   SYSTEM_CALL_TIME_LOCKSYSTEM_CALL_TIMEr   r   )r   defaultZignore_exit_statusr#   r   r   Zcommand_listr   r   r   r   r@   Zis_shell_commandZprocessZtrace_prefixrA   r   r   r   rk     sv   
%

 
  rk   c               	   C   s   t du rStdt  g } t| dkr| d dv r| d a n1g t }}tdD ]!}z|| du r4W  nW n
 ty?   Y  nw |t	||  q(d
|a tt at S )	zi
  Provides the present name of our process.

  :returns: **str** with the present name of our process
  Nzps -p %i -o argsr7   r   )r}   ZARGSr6   d   rT   )_PROCESS_NAMErk   r;   getpidr   argc_tranger   r   r   r8   _MAX_NAME_LENGTH)r   r)   argcir   r   r   get_process_nameJ  s"   	

r  c                 C   s<   t |  t dkrt|  dS t dv rt|  dS dS )z
  Renames our current process from "python <args>" to a custom name. This is
  best-effort, not necessarily working on all platforms.

  :param str process_name: new name for our process

  :raises: **IOError** if the process cannot be renamed
  ZLinux)rI   rQ   rR   N)	_set_argvrE   rF   _set_prctl_name_set_proc_titler   r   r   r   set_process_name~  s   r  c                 C   s   t du rdS t }tdt }}t |t| t| tkr$tdt	t|t| }t
|jd|d  | d}t|j|t|  | adS )zi
  Overwrites our argv in a similar fashion to how it's done in C with:
  strcpy(argv[0], 'new_name');
  Nr   zmCan't rename process to something longer than our initial name (this would overwrite memory used for the env)r6   utf8)Py_GetArgcArgvr  ctypesc_intr   Zpointerr   r   r   maxZmemsetcontentsencodeZmemmover   )r   Zcurrent_nameargvr   Z	zero_sizeZprocess_name_encodedr   r   r   r    s   
r  c                 C   sP   t t jd}t t| d }tjj| |_	|
tt |ddd dS )a	  
  Sets the prctl name, which is used by top and killall. This appears to be
  Linux specific and has the max of 15 characters.

  This is from...
  http://stackoverflow.com/questions/564695/is-there-a-way-to-change-effective-process-name-in-python/923034#923034
  rc   r6   r   N)r  CDLLrm   find_librarycreate_string_bufferr   rl   r   Z	_to_bytesvalueZprctlPR_SET_NAMEbyrefr   ZlibcZname_bufferr   r   r   r    s   	r  c                 C   sZ   t t jd}t t| d }|  |_z|t 	| W dS  t
y,   Y dS w )z
  BSD specific calls (should be compataible with both FreeBSD and OpenBSD:
  http://fxr.watson.org/fxr/source/gen/setproctitle.c?v=FREEBSD-LIBC
  http://www.rootr.net/man/man/setproctitle/3
  rc   r6   N)r  r  rm   r  r  r   r  r  Zsetproctitler  r   r  r   r   r   r    s   
r  )Tr   )F)pr    collectionsr  Zctypes.utilr	   r   r4   r;   rE   r   r   rs   r   Z	threadingr:   Zstem.prereqrl   Z	stem.utilZstem.util.enumZstem.util.procZstem.util.str_toolsr   r   rm   enumZUppercaseEnumr+   tupleiterr   dequedictrq   	frozensetrx   rY   rX   rj   ri   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  ZPOINTERZc_char_pr   Z	pythonapir  Zrestyper	  argtypesr   r   r   RLockr   rh   r   r!   objectr'   rG   rJ   rO   rP   rS   r_   ro   ry   r   r   r   r   r   r   r   r@   r   r   r   r   r   r   rk   r  r  r  r  r  Zget_name_by_pidZget_pid_by_nameZget_pid_by_portZget_pid_by_open_fileZget_cwdZget_userZget_start_timeZget_bsd_jail_idZget_bsd_jail_pathr   r   r   r   <module>   s   ?	


Z




+
>+
+ ,x& E!
5#
-e4"