PK¥”4G©Â›œ®"®" libproc.py# encoding: utf-8 # # This file is part of libproc. # # Copyright 2015 Zygmunt Krynicki. # Written by: # Zygmunt Krynicki # # libproc is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3, # as published by the Free Software Foundation. # # libproc is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with libproc. If not, see . """ Low-level bindings to libproc.dylib. This module exposes only one function, proc_info(). Please refer to the documentation directory for explanation on how to use it correctly. .. note:: That the only low-level binding is the `proc_info()` function itself. All of the other functions are pure-python wrappers around it that don't require the caller to handle ctypes. """ from __future__ import ( absolute_import, division, print_function, unicode_literals, ) import ctypes import ctypes.util import os import sys import unittest __version__ = '0.2' __all__ = ( 'proc_info', 'PROC_CALLNUM_LISTPIDS', 'PROC_CALLNUM_PIDINFO', 'PROC_CALLNUM_PIDFDINFO', 'PROC_CALLNUM_KERNMSGBUF', 'PROC_CALLNUM_SETCONTROL', 'PROC_CALLNUM_PIDFILEPORTINFO', 'PROC_ALL_PIDS', 'PROC_PGRP_ONLY', 'PROC_TTY_ONLY', 'PROC_UID_ONLY', 'PROC_RUID_ONLY', 'PROC_PPID_ONLY', # Pure-python wrappers: 'get_all_pids', 'get_pids_for_uid', 'get_pids_for_ruid', 'get_pids_for_ppid', 'get_pids_for_pgrp', 'get_pids_for_tty', ) if sys.platform != 'darwin': # NOTE: This mainly is here so that readthedocs can import # and build the documentation of this module. def __proc_info(callnum, pid, flavor, arg, buffer, buf_size): """Fake function available on non-darwin systems.""" raise NotImplementedError("__proc_info() is only supported on OS X") else: def __proc_info_errcheck(result, func, arguments): """Error checker for __proc_info().""" proc_errno = ctypes.get_errno() if proc_errno != 0: raise OSError(proc_errno, os.strerror(proc_errno)) return result # This is the libproc.dylib library. It is also linked into libc and # libSystem. If you already hold a reference to either of those, you can # use that as well. _libproc_path = ctypes.util.find_library("libproc.dylib") _libproc = ctypes.CDLL(_libproc_path, use_errno=True) __proc_info = _libproc['__proc_info'] __proc_info.restype = ctypes.c_int __proc_info.argtypes = [ ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_uint64, ctypes.c_void_p, ctypes.c_int] __proc_info.errcheck = __proc_info_errcheck #: The proc_info() low-level system call. #: #: The python3-style signature of proc_info() is:: #: #: __proc_info( #: callnum: ctypes.c_int, #: pid: ctypes.c_int, #: flavor: ctypes.c_int, #: arg: ctypes.c_uint64, #: buffer: ctypes.c_void_p, #: buf_size: ctypes.c_int #: ) -> ctypes.c_int #: #: There is also an error checker that raises :class:`python:OSError` if #: the underlying call fails. This is easy to do if the buffer is handled #: incorrectly or *callnum* or *pid* are invalid. #: #: This function uses multiplexing on *callnum* to invoke distinct kernel #: functions. Please look at the *PROC_CALLNUM_xxx* family of constants #: for details. proc_info = __proc_info # NOTE: Those are found in xnu source code in proc_info_internal() #: Value of __proc_info(callnum, ...), returns a list of PIDs. PROC_CALLNUM_LISTPIDS = 1 #: Undocumented. PROC_CALLNUM_PIDINFO = 2 #: Undocumented. PROC_CALLNUM_PIDFDINFO = 3 #: Undocumented. PROC_CALLNUM_KERNMSGBUF = 4 #: Undocumented. PROC_CALLNUM_SETCONTROL = 5 #: Undocumented. PROC_CALLNUM_PIDFILEPORTINFO = 6 # NOTE: Those can be found in #: When called with callnum 1, return all processes PROC_ALL_PIDS = 1 #: When called with callnum 1, return all processes in a given group PROC_PGRP_ONLY = 2 #: When called with callnum 1, return all processes attached to a given tty PROC_TTY_ONLY = 3 #: When called with callnum 1, return all processes with the given UID PROC_UID_ONLY = 4 #: When called with callnum 1, return all processes with the given RUID PROC_RUID_ONLY = 5 #: When called with callnum 1, return all processes with the given PPID PROC_PPID_ONLY = 6 _size_of_c_int = ctypes.sizeof(ctypes.c_int) def _get_pids(filter_type, filter_arg): """ Get a list of PIDs (process IDs) with specific libproc filter. :param filter_type: One of :data:`PROC_ALL_PIDS`, :data:`PROC_PGRP_ONLY`, :data:`PROC_TTY_ONLY`, :data:`PROC_UID_ONLY` :data:`PROC_RUID_ONLY` or :data:`PROC_PPID_ONLY`. This argument describes the type of filtering to apply. :param filter_arg: The specific process property filter value to look for. For :data:`PROC_ALL_PIDS` this value is ignored. """ buf_size = proc_info( PROC_CALLNUM_LISTPIDS, filter_type, filter_arg, 0, None, 0) pid_list = (ctypes.c_int * (buf_size // _size_of_c_int))() assert ctypes.sizeof(pid_list) == buf_size retval = proc_info( PROC_CALLNUM_LISTPIDS, filter_type, filter_arg, 0, pid_list, buf_size) return pid_list[:retval // _size_of_c_int] def get_all_pids(): """ Get a list of all process IDs. :returns: A list of all the process IDs on this system. :raises OSError: If the underlying system call fails. """ return _get_pids(PROC_ALL_PIDS, 0) def get_pids_for_uid(uid): """ Get a list of PIDs (process IDs) with the specific user ID. :param uid: The UID to look for. :returns: A list of matching PIDs. :raises OSError: If the underlying system call fails. """ return _get_pids(PROC_UID_ONLY, uid) def get_pids_for_ruid(ruid): """ Get a list of PIDs (process IDs) with the specific real user ID. :param ruid: The RUID to look for. :returns: A list of matching PIDs. :raises OSError: If the underlying system call fails. """ return _get_pids(PROC_RUID_ONLY, ruid) def get_pids_for_ppid(ppid): """ Get a list of PIDs (process IDs) with the specific parent PID. :param ppid: The parent process ID to look for. :returns: A list of matching PIDs. :raises OSError: If the underlying system call fails. """ return _get_pids(PROC_PPID_ONLY, ppid) def get_pids_for_pgrp(pgrp): """ Get a list of PIDs (process IDs) with the specific process group. :param pgrp: The process group ID to look for. :returns: A list of matching PIDs. :raises OSError: If the underlying system call fails. """ return _get_pids(PROC_PGRP_ONLY, pgrp) def get_pids_for_tty(tty): """ Get a list of PIDs (process IDs) with the specific TTY. :param tty: The TTY to look for. :returns: A list of matching PIDs. :raises OSError: If the underlying system call fails. """ return _get_pids(PROC_TTY_ONLY, tty) class TestSmoke(unittest.TestCase): """Smoke tests for the higher-level functions.""" def setUp(self): """Common set-up code.""" self.uid = os.getuid() self.pid = os.getpid() self.ppid = os.getppid() def test_get_all_pids(self): """Smoke test for get_all_pids().""" pids = get_all_pids() self.assertNotEqual(pids, []) self.assertIn(self.pid, pids) def test_get_pids_for_uid(self): """Smoke test for get_pids_for_uid().""" pids = get_pids_for_uid(self.uid) self.assertNotEqual(pids, []) self.assertIn(self.pid, pids) def test_get_pids_for_ruid(self): """Smoke test for get_pids_for_ruid().""" pids = get_pids_for_ruid(self.uid) self.assertNotEqual(pids, []) self.assertIn(self.pid, pids) def test_get_pids_for_ppid(self): """Smoke test for get_pids_for_ppid().""" pids = get_pids_for_ppid(self.ppid) self.assertNotEqual(pids, []) self.assertIn(self.pid, pids) def test_get_pids_for_pgrp(self): """Smoke test for get_pids_for_pgrp().""" # Move this process to a dedicated process group os.setpgid(0, self.pid) pids = get_pids_for_pgrp(self.pid) self.assertNotEqual(pids, []) self.assertIn(self.pid, pids) if __name__ == '__main__': import unittest unittest.main() PK²”4Gx>ÏÏ%libproc-0.2.dist-info/DESCRIPTION.rst=================================== Low-level bindings to libproc.dylib =================================== Introduction ============ This module contains descriptions and wrappers around interesting parts of the libproc.dylib shared library. This library exposes internal kernel data about processes of OS X. It was developed and tested on OS X 10.7.5, using i386 architecture. Many aspects were traced manually from xnu source code as this library is severely undocumented. Low-level API ============= The entire action of this library revolves around the function ``__proc_info()`` which is a simple wrapper around a system call. The signature of the function is as follows (Internal private prototype taken from ``libproc.c``):: int __proc_info( int callnum, // One of _PROC_CALLNUM_xxx constants below. int pid, // Varies per callnum, see below. int flavor, // Ditto. uint64_t arg, // Ditto, sometimes unused. void *buffer, // Output buffer. int buffersize // Size of output buffer. ); The only low level API is ``__proc_info()`` itself. The behavior of this function depends on the first argument, *callnum*, which is typical of many kernel interfaces. Unfortunately distinct values of *callnum* do not have any official names (in the source code they are simply hard-coded constants. I have used a convention *_PROC_CALLNUM_xxx* where *xxx* is derived from the name of the kernel function multiplexed by that value. The values I have made are:: PROC_CALLNUM_LISTPIDS = 1 PROC_CALLNUM_PIDINFO = 2 PROC_CALLNUM_PIDFDINFO = 3 PROC_CALLNUM_KERNMSGBUF = 4 PROC_CALLNUM_SETCONTROL = 5 PROC_CALLNUM_PIDFILEPORTINFO = 6 You can verify them by looking at ``proc_info_internal()`` in the xnu source code: ``xnu/bsd/kern/proc_info.c``. Whenever this function is called with NULL value for *buffer*, it will compute and return the correct size of the buffer to pass. Looking at the source code of the system call it makes some conservative estimates but I suspect it is still racy (a fork bomb might make the value invalid between the first and second calls). Callnum 1 --------- .. note:: This *callnum* has a liproc-only alias of *PROC_CALLNUM_LISTPIDS* When *callnum* is :data:`~libproc.PROC_CALLNUM_LISTPIDS` then the function obtains a list of process identifiers that match some criteria. The remaining arguments have the following meaning: *pid*: Contains the type of process list to obtain. The possible values are one of *PROC_xxx* constants listed below. :data:`~libproc.PROC_ALL_PIDS`: Return the full process table. :data:`~libproc.PROC_PGRP_ONLY`: Return a list of processes that have a given process group ID :data:`~libproc.PROC_TTY_ONLY`: Return a list of processes that are attached to a given TTY :data:`~libproc.PROC_UID_ONLY`: Return a list of processes that have a given user ID. :data:`~libproc.PROC_RUID_ONLY`: Return a list of processes that have a given real user ID. :data:`~libproc.PROC_PPID_ONLY`: Return a list of processes that are children of a given process. *flavor*: Contains the optional filtering argument for the processes that are returned. The value passed here is compared against the desired property of each process. The only exception is *PROC_ALL_PIDS* where no filtering takes place. *arg*: This parameter is unused. *buffer*: This parameter is the pointer to the output buffer. The buffer is an array of :class:`python:ctypes.c_int` of appropriate size (as determined by the size of the process table). As a convention, you can pass a None value (which maps to a *NULL* pointer) to ask the kernel for the size of the buffer. Correct buffer size in bytes is then returned by the call. *buf_size*: Size of the buffer, in bytes. The return value is either the number of bytes needed or the number of bytes written to the buffer (see the discussion of *buffer* argument above). Callnum 2 --------- .. note:: This *callnum* has a liproc-only alias of *PROC_CALLNUM_PIDINFO* This *callnum* is currently undocumented. Callnum 3 --------- .. note:: This *callnum* has a liproc-only alias of *PROC_CALLNUM_PIDFDINFO* This *callnum* is currently undocumented. Callnum 4 --------- .. note:: This *callnum* has a liproc-only alias of *PROC_CALLNUM_KERNMSGBUF* This *callnum* is currently undocumented. Callnum 5 --------- .. note:: This *callnum* has a liproc-only alias of *PROC_CALLNUM_SETCONTROL* This *callnum* is currently undocumented. Callnum 6 --------- .. note:: This *callnum* has a liproc-only alias of *PROC_CALLNUM_PIDFILEPORTINFO* This *callnum* is currently undocumented. Higher-level APIs ================= This library contains a number of higher-level functions that call ``__proc_info()`` with appropriate arguments, handle buffer allocation and return friendly pythonic values. You can find them below: Callnum 1 --------- The following wrappers exist for this *callnum*: - :func:`~libproc.get_all_pids()`. - :func:`~libproc.get_pids_for_uid()`. - :func:`~libproc.get_pids_for_ruid()`. - :func:`~libproc.get_pids_for_ppid()`. - :func:`~libproc.get_pids_for_pgrp()`. - :func:`~libproc.get_pids_for_tty()`. History ======= 0.2 (2015-09-20) ---------------- * Initial version exposing just *callnum* 1 (:data:`~libproc.PROC_CALLNUM_LISTPIDS`) along with pythonic wrappers and comprehensive demonstration tool (``exampes/listpids.py``) PK²”4GæˆD~~~#libproc-0.2.dist-info/metadata.json{"classifiers": ["Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", "Natural Language :: English", "Operating System :: MacOS :: MacOS X", "Topic :: Software Development", "Topic :: Software Development :: Libraries", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy"], "extensions": {"python.details": {"contacts": [{"email": "me@zygoon.pl", "name": "Zygmunt Krynicki", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/zyga/libproc"}}}, "generator": "bdist_wheel (0.24.0)", "keywords": ["libproc.dylib", "libproc", "proc", "bindings"], "license": "LGPLv3", "metadata_version": "2.0", "name": "libproc", "summary": "Low-level bindings to libproc.dylib", "version": "0.2"}PK²”4GEl#libproc-0.2.dist-info/top_level.txtlibproc PK™’4G“×2libproc-0.2.dist-info/zip-safe PK²”4G‡3onnlibproc-0.2.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.24.0) Root-Is-Purelib: true Tag: py2-none-any Tag: py3-none-any PK²”4Gç¢'Ûùùlibproc-0.2.dist-info/METADATAMetadata-Version: 2.0 Name: libproc Version: 0.2 Summary: Low-level bindings to libproc.dylib Home-page: https://github.com/zyga/libproc Author: Zygmunt Krynicki Author-email: me@zygoon.pl License: LGPLv3 Keywords: libproc.dylib libproc proc bindings Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3) Classifier: Natural Language :: English Classifier: Operating System :: MacOS :: MacOS X Classifier: Topic :: Software Development Classifier: Topic :: Software Development :: Libraries Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy =================================== Low-level bindings to libproc.dylib =================================== Introduction ============ This module contains descriptions and wrappers around interesting parts of the libproc.dylib shared library. This library exposes internal kernel data about processes of OS X. It was developed and tested on OS X 10.7.5, using i386 architecture. Many aspects were traced manually from xnu source code as this library is severely undocumented. Low-level API ============= The entire action of this library revolves around the function ``__proc_info()`` which is a simple wrapper around a system call. The signature of the function is as follows (Internal private prototype taken from ``libproc.c``):: int __proc_info( int callnum, // One of _PROC_CALLNUM_xxx constants below. int pid, // Varies per callnum, see below. int flavor, // Ditto. uint64_t arg, // Ditto, sometimes unused. void *buffer, // Output buffer. int buffersize // Size of output buffer. ); The only low level API is ``__proc_info()`` itself. The behavior of this function depends on the first argument, *callnum*, which is typical of many kernel interfaces. Unfortunately distinct values of *callnum* do not have any official names (in the source code they are simply hard-coded constants. I have used a convention *_PROC_CALLNUM_xxx* where *xxx* is derived from the name of the kernel function multiplexed by that value. The values I have made are:: PROC_CALLNUM_LISTPIDS = 1 PROC_CALLNUM_PIDINFO = 2 PROC_CALLNUM_PIDFDINFO = 3 PROC_CALLNUM_KERNMSGBUF = 4 PROC_CALLNUM_SETCONTROL = 5 PROC_CALLNUM_PIDFILEPORTINFO = 6 You can verify them by looking at ``proc_info_internal()`` in the xnu source code: ``xnu/bsd/kern/proc_info.c``. Whenever this function is called with NULL value for *buffer*, it will compute and return the correct size of the buffer to pass. Looking at the source code of the system call it makes some conservative estimates but I suspect it is still racy (a fork bomb might make the value invalid between the first and second calls). Callnum 1 --------- .. note:: This *callnum* has a liproc-only alias of *PROC_CALLNUM_LISTPIDS* When *callnum* is :data:`~libproc.PROC_CALLNUM_LISTPIDS` then the function obtains a list of process identifiers that match some criteria. The remaining arguments have the following meaning: *pid*: Contains the type of process list to obtain. The possible values are one of *PROC_xxx* constants listed below. :data:`~libproc.PROC_ALL_PIDS`: Return the full process table. :data:`~libproc.PROC_PGRP_ONLY`: Return a list of processes that have a given process group ID :data:`~libproc.PROC_TTY_ONLY`: Return a list of processes that are attached to a given TTY :data:`~libproc.PROC_UID_ONLY`: Return a list of processes that have a given user ID. :data:`~libproc.PROC_RUID_ONLY`: Return a list of processes that have a given real user ID. :data:`~libproc.PROC_PPID_ONLY`: Return a list of processes that are children of a given process. *flavor*: Contains the optional filtering argument for the processes that are returned. The value passed here is compared against the desired property of each process. The only exception is *PROC_ALL_PIDS* where no filtering takes place. *arg*: This parameter is unused. *buffer*: This parameter is the pointer to the output buffer. The buffer is an array of :class:`python:ctypes.c_int` of appropriate size (as determined by the size of the process table). As a convention, you can pass a None value (which maps to a *NULL* pointer) to ask the kernel for the size of the buffer. Correct buffer size in bytes is then returned by the call. *buf_size*: Size of the buffer, in bytes. The return value is either the number of bytes needed or the number of bytes written to the buffer (see the discussion of *buffer* argument above). Callnum 2 --------- .. note:: This *callnum* has a liproc-only alias of *PROC_CALLNUM_PIDINFO* This *callnum* is currently undocumented. Callnum 3 --------- .. note:: This *callnum* has a liproc-only alias of *PROC_CALLNUM_PIDFDINFO* This *callnum* is currently undocumented. Callnum 4 --------- .. note:: This *callnum* has a liproc-only alias of *PROC_CALLNUM_KERNMSGBUF* This *callnum* is currently undocumented. Callnum 5 --------- .. note:: This *callnum* has a liproc-only alias of *PROC_CALLNUM_SETCONTROL* This *callnum* is currently undocumented. Callnum 6 --------- .. note:: This *callnum* has a liproc-only alias of *PROC_CALLNUM_PIDFILEPORTINFO* This *callnum* is currently undocumented. Higher-level APIs ================= This library contains a number of higher-level functions that call ``__proc_info()`` with appropriate arguments, handle buffer allocation and return friendly pythonic values. You can find them below: Callnum 1 --------- The following wrappers exist for this *callnum*: - :func:`~libproc.get_all_pids()`. - :func:`~libproc.get_pids_for_uid()`. - :func:`~libproc.get_pids_for_ruid()`. - :func:`~libproc.get_pids_for_ppid()`. - :func:`~libproc.get_pids_for_pgrp()`. - :func:`~libproc.get_pids_for_tty()`. History ======= 0.2 (2015-09-20) ---------------- * Initial version exposing just *callnum* 1 (:data:`~libproc.PROC_CALLNUM_LISTPIDS`) along with pythonic wrappers and comprehensive demonstration tool (``exampes/listpids.py``) PK²”4G'ñ½{{libproc-0.2.dist-info/RECORDlibproc.py,sha256=8QOPOAkaXe8IVsMuedwP_W3_IyFm68KcAhWeziCVKUA,8878 libproc-0.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 libproc-0.2.dist-info/metadata.json,sha256=O25NPCQ_Fk5nLztRa23NqUG9ibK7Yc81J2G3c0Z-ojQ,1150 libproc-0.2.dist-info/RECORD,, libproc-0.2.dist-info/top_level.txt,sha256=IGQ3ehkqswZALIh1UPnE6B5P8Qaf_M9J-c3uAGunos4,8 libproc-0.2.dist-info/DESCRIPTION.rst,sha256=Kfr1RNTm0k5T8GyombfygSn0xlHSUKBFm1sjsPkswkQ,5583 libproc-0.2.dist-info/WHEEL,sha256=AvR0WeTpDaxT645bl5FQxUK6NPsTls2ttpcGJg3j1Xg,110 libproc-0.2.dist-info/METADATA,sha256=V8ZQvU3nzdvsoA4MUtI7Vwebx-aY6bVqYmCu3mPhrlU,6649 PK¥”4G©Â›œ®"®" libproc.pyPK²”4Gx>ÏÏ%Ö"libproc-0.2.dist-info/DESCRIPTION.rstPK²”4GæˆD~~~#è8libproc-0.2.dist-info/metadata.jsonPK²”4GEl#§=libproc-0.2.dist-info/top_level.txtPK™’4G“×2ð=libproc-0.2.dist-info/zip-safePK²”4G‡3onn->libproc-0.2.dist-info/WHEELPK²”4Gç¢'ÛùùÔ>libproc-0.2.dist-info/METADATAPK²”4G'ñ½{{ Ylibproc-0.2.dist-info/RECORDPKX¾[