pax_global_header00006660000000000000000000000064150041404150014504gustar00rootroot0000000000000052 comment=864cb7298ead40c903b0276cbfa887b5369e037c py-setproctitle-version-1.3.6/000077500000000000000000000000001500414041500163455ustar00rootroot00000000000000py-setproctitle-version-1.3.6/.github/000077500000000000000000000000001500414041500177055ustar00rootroot00000000000000py-setproctitle-version-1.3.6/.github/FUNDING.yml000066400000000000000000000001111500414041500215130ustar00rootroot00000000000000--- github: - dvarrazzo custom: - "https://www.paypal.me/dvarrazzo" py-setproctitle-version-1.3.6/.github/workflows/000077500000000000000000000000001500414041500217425ustar00rootroot00000000000000py-setproctitle-version-1.3.6/.github/workflows/packages.yml000066400000000000000000000107451500414041500242520ustar00rootroot00000000000000--- name: Packages on: - workflow_dispatch jobs: build-sdist: name: Build sdist package runs-on: ubuntu-24.04 steps: - name: Checkout repos uses: actions/checkout@v4 - name: Build sdist run: python setup.py sdist - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: sdist path: ./dist/* build-wheel: runs-on: ${{matrix.os}} strategy: fail-fast: false matrix: os: [ubuntu-24.04, windows-latest, macos-latest] pyver: [cp38, cp39, cp310, cp311, cp312, cp313, cp313t] steps: - name: Checkout repos uses: actions/checkout@v4 - name: Build wheels uses: pypa/cibuildwheel@v2.23.3 env: CIBW_BUILD: ${{matrix.pyver}}-* CIBW_ENABLE: cpython-freethreading CIBW_ARCHS_LINUX: auto CIBW_ARCHS_MACOS: auto universal2 CIBW_ARCHS_WINDOWS: auto CIBW_TEST_EXTRAS: test CIBW_TEST_COMMAND: pytest --color=yes -m 'not embedded' {project}/tests # Passing a space in a param is a b*tch on windows. # However embedded tests are skipped anyway there. CIBW_TEST_COMMAND_WINDOWS: pytest --color=yes {project}/tests # musllinux tests fail with some pid mixup # cross-build macos images can't be tested on this runner. CIBW_TEST_SKIP: >- *-musllinux_* *-macosx_universal2:arm64 pp*-macosx_* - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: wheel-${{ matrix.pyver }}-${{ matrix.os }} path: ./wheelhouse/*.whl build-cross-wheel: runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: pyver: [cp38, cp39, cp310, cp311, cp312, cp313, cp313t] arch: [aarch64, ppc64le] steps: - name: Checkout repos uses: actions/checkout@v4 - name: Set up QEMU for multi-arch build uses: docker/setup-qemu-action@v3 - name: Build wheels uses: pypa/cibuildwheel@v2.23.3 env: CIBW_BUILD: ${{matrix.pyver}}-* CIBW_ENABLE: cpython-freethreading CIBW_ARCHS: ${{matrix.arch}} # Tests mostly fail because of some confusion with the python interpreter - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: wheel-${{ matrix.pyver }}-${{ matrix.arch }} path: ./wheelhouse/*.whl build-wheel-pypy: runs-on: ${{matrix.os}} strategy: fail-fast: false matrix: os: [ubuntu-24.04, windows-latest, macos-latest] steps: - name: Checkout repos uses: actions/checkout@v4 - name: Build wheels uses: pypa/cibuildwheel@v2.21.2 env: CIBW_BUILD: pp* CIBW_SKIP: pp37-* CIBW_TEST_EXTRAS: test CIBW_TEST_COMMAND: pytest --color=yes -m 'not embedded' {project}/tests # Passing a space in a param is a b*tch on windows. # However embedded tests are skipped anyway there. CIBW_TEST_COMMAND_WINDOWS: pytest --color=yes {project}/tests # musllinux tests fail with some pid mixup # cross-build macos images can't be tested on this runner. CIBW_TEST_SKIP: >- *-musllinux_* *-macosx_universal2:arm64 pp*-macosx_* - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: wheels-pp-${{ matrix.os }} path: ./wheelhouse/*.whl build-cross-wheel-pypy: runs-on: ubuntu-24.04 strategy: fail-fast: false steps: - name: Checkout repos uses: actions/checkout@v4 - name: Set up QEMU for multi-arch build uses: docker/setup-qemu-action@v3 - name: Build wheels uses: pypa/cibuildwheel@v2.21.2 env: CIBW_BUILD: pp* CIBW_SKIP: pp37-* # Tests mostly fail because of some confusion with the python interpreter - name: Upload artifacts uses: actions/upload-artifact@v4 with: name: wheels-pp-cross path: ./wheelhouse/*.whl merge: runs-on: ubuntu-latest needs: - build-sdist - build-wheel - build-cross-wheel - build-wheel-pypy - build-cross-wheel-pypy steps: - name: Merge Artifacts uses: actions/upload-artifact/merge@v4 with: name: setproctitle-artifacts delete-merged: true py-setproctitle-version-1.3.6/.github/workflows/test.yml000066400000000000000000000057461500414041500234600ustar00rootroot00000000000000--- name: Tests on: push: # This should disable running the workflow on tags, according to the # on.. GitHub Actions docs. branches: - "*" pull_request: jobs: linux-tests: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: python-version: - "3.8" - "3.9" - "3.10" - "3.11" - "3.12" - "3.13" - "3.13t" - "pypy-3.8" - "pypy-3.9" - "pypy-3.10" os: - ubuntu-24.04 steps: - name: Checkout repos uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{matrix.python-version}} allow-prereleases: true architecture: x64 - name: Install Tox run: pip install tox - name: Run tests run: tox -e ${{matrix.python-version}} -- -vrsx --color=yes windows-tests: runs-on: windows-latest strategy: fail-fast: false matrix: python-version: - 3.8 - 3.9 - "3.10" - "3.11" - "3.12" - "3.13" - "3.13t" architecture: ['x64', 'x86'] steps: - name: Checkout repos uses: actions/checkout@v4 - name: Set up Python ${{matrix.python-version}} uses: actions/setup-python@v5 with: python-version: ${{matrix.python-version}} allow-prereleases: true architecture: ${{matrix.architecture}} - name: Install Tox run: pip install tox - name: Run tests run: tox -e ${{matrix.python-version}} -- -vrsx --color=yes macos-tests: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: python-version: - "3.11" - "3.12" - "3.13" - "3.13t" os: - macos-latest include: - python-version: 3.8 os: macos-13 - python-version: 3.9 os: macos-13 - python-version: "3.10" os: macos-13 steps: - name: Checkout repos uses: actions/checkout@v4 - name: Set up Python ${{matrix.python-version}} uses: actions/setup-python@v5 with: python-version: ${{matrix.python-version}} allow-prereleases: true - name: Install Tox run: pip install tox - name: Run tests run: tox -e ${{matrix.python-version}} -- -vrsx --color=yes macos-native-python-test: runs-on: macos-latest steps: - name: Checkout repos uses: actions/checkout@v4 - name: Install Tox run: xcrun python3 -m pip install tox - name: Run tests run: | export XCODE_PYTHON=`xcrun python3 -c "import sys;print(sys.executable)"` $XCODE_PYTHON -c "import sys; print(sys.version)" $XCODE_PYTHON -m tox -e xcode -- -vrsx --color=yes py-setproctitle-version-1.3.6/.gitignore000066400000000000000000000003041500414041500203320ustar00rootroot00000000000000*.o *.sw[op] *.orig *.pyc *.rej *.html tools/pttest tools/sptdemo tools/prctl_demo tools/ps_status.[ch] build dist py3 tests/pyrun?.? env /setproctitle.egg-info _setproctitle.*.so /.tox /packages py-setproctitle-version-1.3.6/.yamllint.yaml000066400000000000000000000001311500414041500211330ustar00rootroot00000000000000--- extends: default rules: truthy: check-keys: false line-length: max: 85 py-setproctitle-version-1.3.6/COPYRIGHT000066400000000000000000000027141500414041500176440ustar00rootroot00000000000000Copyright (c) 2009-2021, Daniele Varrazzo All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The name of Daniele Varrazzo may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. py-setproctitle-version-1.3.6/HISTORY.rst000066400000000000000000000106241500414041500202430ustar00rootroot00000000000000Releases history ---------------- Version 1.3.6 ------------- - Add support for free-threading (issue #147) Version 1.3.5 ------------- - Fix bouncing Dock icon on macOS (issue #143). - Fix building on C23 compilers (issue #145). Version 1.3.4 ------------- - Add support for Python 3.13 (issue #139). - Drop support for Python 3.7. Version 1.3.3 ------------- - Add support for Python 3.12. - Fix package metadata to include Python 3.11, 3.12. Version 1.3.2 ------------- - Restore import-time initialization of macOS to avoid crash on thread+fork (issue #113). Version 1.3.1 ------------- - Fixed segfault on macOS 12.5 in forked processes (issue #111). Note that, as a workaround, Activity Monitor will show the title of the parent. Version 1.3.0 ------------- - Added fallback no-op implementation if building the extension fails. - Added support for displaying title as the process name in MacOS Activity Monitor (issue #10). - Fixed "Symbol not found: _Py_GetArgcArgv" error when using Xcode provided Python (issues #82, #103). - Fixed FreeBSD support, broken in 1.2 (issue #94). - Added package type annotations (issue #101). - Dropped support for Python 3.6. Version 1.2.3 ------------- - Added Python 3.10 packages (issue #102). - Added Wheel packages for macOS (issue #96). - Package build moved to cibuildwheel, other wheels provided (issue #47). Version 1.2.2 ------------- - Fixed Windows build (issues #89, #90). - Added wheel packages for Windows (issues #47, #90). - Added wheel packages for aarch64 (issue #95). Version 1.2.1 ------------- - Fixed segfault after ``os.environ.clear()`` (issue #88). Version 1.2 ~~~~~~~~~~~ - added ``getthreadtitle()`` and ``setthreadtitle()``. - Initialisation of the module moved to the first usage: importing the module doesn't cause side effects. - Manage much longer command lines (issue #52) - Improved build on BSD, dropped ancient versions (issue #67). - Fixed build for Python 3.8 (issues #66, #72) - Added support for Python 3.9 - Dropped support for Python < 3.6 Version 1.1.10 ~~~~~~~~~~~~~~ - Fixed building with certain ``prctl.h`` implementations (issue #44). - Use ``setuptools`` if available (issue #48). Version 1.1.9 ~~~~~~~~~~~~~ - Fixed build on VC (issues #20, #33). - Added ``MANIFEST.in`` to the source distribution to help with RPM building (issue #30). Version 1.1.8 ~~~~~~~~~~~~~ - Added support for Python "diehard" 2.4 (pull request #3). - Fixed build on Mac OS X 10.9 Maverick (issue #27). Version 1.1.7 ~~~~~~~~~~~~~ - Added PyPy support, courtesy of Ozan Turksever - http://www.logsign.net (pull request #2). Version 1.1.6 ~~~~~~~~~~~~~ - The module can be compiled again on Windows (issue #21). Version 1.1.5 ~~~~~~~~~~~~~ - No module bug, but a packaging issue: files ``README`` and ``HISTORY`` added back into the distribution. Version 1.1.4 ~~~~~~~~~~~~~ - The module works correctly in embedded Python. - ``setproctitle()`` accepts a keyword argument. - Debug output support always compiled in: the variable ``SPT_DEBUG`` can be used to emit debug log. Version 1.1.3 ~~~~~~~~~~~~~ - Don't clobber environ if the variable ``SPT_NOENV`` is set (issue #16). Version 1.1.2 ~~~~~~~~~~~~~ - Find the setproctitle include file on OpenBSD (issue #11). - Skip test with unicode if the file system encoding wouldn't make it pass (issue #13). Version 1.1.1 ~~~~~~~~~~~~~ - Fixed segfault when the module is imported under mod_wsgi (issue #9). Version 1.1 ~~~~~~~~~~~ - The module works correctly with Python 3. Version 1.0.1 ~~~~~~~~~~~~~ - ``setproctitle()`` works even when Python messes up with argv, e.g. when run with the -m option (issue #8). Version 1.0 ~~~~~~~~~~~ No major change since the previous version. The module has been heavily used in production environment without any problem reported, so it's time to declare it stable. Version 0.4 ~~~~~~~~~~~ - Module works on BSD (tested on FreeBSD 7.2). - Module works on Windows. Many thanks to `Develer`_ for providing a neat `GCC package for Windows with Python integration`__ that made the Windows porting painless. .. _Develer: http://www.develer.com/ .. __: http://www.develer.com/oss/GccWinBinaries Version 0.3 ~~~~~~~~~~~ - Module works on Mac OS X 10.2. Reported working on OS X 10.6 too. Version 0.2 ~~~~~~~~~~~ - Added ``prctl()`` call on Linux >= 2.6.9 to update ``/proc/self/status``. Version 0.1 ~~~~~~~~~~~ - Initial public release. py-setproctitle-version-1.3.6/MANIFEST.in000066400000000000000000000002431500414041500201020ustar00rootroot00000000000000include HISTORY.rst include README.rst include COPYRIGHT include MANIFEST.in include Makefile include src/*.c include src/*.h include tests/*.py include tests/*.c py-setproctitle-version-1.3.6/README.rst000066400000000000000000000106421500414041500200370ustar00rootroot00000000000000A Python module to customize the process title ============================================== .. image:: https://github.com/dvarrazzo/py-setproctitle/workflows/Tests/badge.svg :target: https://github.com/dvarrazzo/py-setproctitle/actions?query=workflow%3ATests :alt: Tests :author: Daniele Varrazzo The ``setproctitle`` module allows a process to change its title (as displayed by system tools such as ``ps``, ``top`` or MacOS Activity Monitor). Changing the title is mostly useful in multi-process systems, for example when a master process is forked: changing the children's title allows to identify the task each process is busy with. The technique is used by PostgreSQL_ and the `OpenSSH Server`_ for example. The procedure is hardly portable across different systems. PostgreSQL provides a good `multi-platform implementation`__: this package was born as a wrapper around PostgreSQL code. - `Homepage `__ - `Download `__ - `Bug tracker `__ .. _PostgreSQL: http://www.postgresql.org .. _OpenSSH Server: http://www.openssh.com/ .. __: http://doxygen.postgresql.org/ps__status_8c_source.html Installation ------------ ``setproctitle`` is a C extension: in order to build it you will need a C compiler and the Python development support (the ``python-dev`` or ``python3-dev`` package in most Linux distributions). No further external dependencies are required. You can use ``pip`` to install the module:: pip install setproctitle You can use ``pip -t`` or ``virtualenv`` for local installations, ``sudo pip`` for a system-wide one... the usual stuff. Read pip_ or virtualenv_ docs for all the details. .. _pip: https://pip.readthedocs.org/ .. _virtualenv: https://virtualenv.readthedocs.org/ Usage ----- .. note:: You should import and use the module (even just calling ``getproctitle()``) pretty early in your program lifetime: code writing env vars `may interfere`__ with the module initialisation. .. __: https://github.com/dvarrazzo/py-setproctitle/issues/42 The ``setproctitle`` module exports the following functions: ``setproctitle(title)`` Set *title* as the title for the current process. ``getproctitle()`` Return the current process title. The process title is usually visible in files such as ``/proc/PID/cmdline``, ``/proc/PID/status``, ``/proc/PID/comm``, depending on the operating system and kernel version. These information are used by user-space tools such as ``ps`` and ``top``. ``setthreadtitle(title)`` Set *title* as the title for the current thread. ``getthreadtitle()`` Get the current thread title. The thread title is exposed by some operating systems as the file ``/proc/PID/task/TID/comm``, which is used by certain tools such as ``htop``. Environment variables ~~~~~~~~~~~~~~~~~~~~~ A few environment variables can be used to customize the module behavior: ``SPT_NOENV`` Avoid clobbering ``/proc/PID/environ``. On many platforms, setting the process title will clobber the ``environ`` memory area. ``os.environ`` will work as expected from within the Python process, but the content of the file ``/proc/PID/environ`` will be overwritten. If you require this file not to be broken you can set the ``SPT_NOENV`` environment variable to any non-empty value: in this case the maximum length for the title will be limited to the length of the command line. ``SPT_DEBUG`` Print debug information on ``stderr``. If the module doesn't work as expected you can set this variable to a non-empty value to generate information useful for debugging. Note that the most useful information is printed when the module is imported, not when the functions are called. Module status ------------- The module can be currently compiled and effectively used on the following platforms: - GNU/Linux - BSD - MacOS X - Windows Note that on Windows there is no way to change the process string: what the module does is to create a *Named Object* whose value can be read using a tool such as `Process Explorer`_ (contribution of a more useful tool to be used together with ``setproctitle`` would be well accepted). The module can probably work on HP-UX, but I haven't found any to test with. It is unlikely that it can work on Solaris instead. .. _Process Explorer: http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx py-setproctitle-version-1.3.6/pkg/000077500000000000000000000000001500414041500171265ustar00rootroot00000000000000py-setproctitle-version-1.3.6/pkg/setproctitle/000077500000000000000000000000001500414041500216475ustar00rootroot00000000000000py-setproctitle-version-1.3.6/pkg/setproctitle/__init__.py000066400000000000000000000026011500414041500237570ustar00rootroot00000000000000"""Allow customization of the process title.""" import os import sys import logging logger = logging.getLogger("setproctitle") __version__ = "1.3.6" __all__ = [ "setproctitle", "getproctitle", "setthreadtitle", "getthreadtitle", ] def setproctitle(title: str) -> None: logger.debug("setproctitle C module not available") return None def getproctitle() -> str: logger.debug("setproctitle C module not available") return " ".join(sys.argv) def setthreadtitle(title: str) -> None: logger.debug("setproctitle C module not available") return None def getthreadtitle() -> str: logger.debug("setproctitle C module not available") return "" try: from . import _setproctitle # type: ignore except ImportError as e: # Emulate SPT_DEBUG showing process info in the C module. if os.environ.get("SPT_DEBUG", ""): logging.basicConfig() logger.setLevel(logging.DEBUG) logger.debug("failed to import setproctitle: %s", e) else: setproctitle = _setproctitle.setproctitle # noqa: F811 getproctitle = _setproctitle.getproctitle # noqa: F811 setthreadtitle = _setproctitle.setthreadtitle # noqa: F811 getthreadtitle = _setproctitle.getthreadtitle # noqa: F811 # Call getproctitle to initialize structures and avoid problems caused # by fork() on macOS (see #113). if sys.platform == "darwin": getproctitle() py-setproctitle-version-1.3.6/pkg/setproctitle/py.typed000066400000000000000000000000001500414041500233340ustar00rootroot00000000000000py-setproctitle-version-1.3.6/pyproject.toml000066400000000000000000000000361500414041500212600ustar00rootroot00000000000000[tool.black] line-length = 79 py-setproctitle-version-1.3.6/setup.py000066400000000000000000000075721500414041500200720ustar00rootroot00000000000000#!/usr/bin/env python """ setproctitle setup script. Copyright (c) 2009-2021 Daniele Varrazzo """ import re import sys from setuptools import setup, Extension, find_packages from setuptools.command.build_ext import build_ext with open("pkg/setproctitle/__init__.py") as f: data = f.read() m = re.search(r"""(?m)^__version__\s*=\s*['"]([^'"]+)['"]""", data) if not m: raise Exception(f"cannot find version in {f.name}") VERSION = m.group(1) define_macros = {} define_macros["SPT_VERSION"] = VERSION platform_sources = [] if sys.platform.startswith("linux"): define_macros["HAVE_SYS_PRCTL_H"] = 1 elif sys.platform == "darwin": # __darwin__ symbol is not defined; __APPLE__ is instead. define_macros["__darwin__"] = 1 platform_sources.append("src/darwin_set_process_name.c") elif "bsd" in sys.platform: # OMG, how many of them are? define_macros["BSD"] = 1 define_macros["HAVE_SETPROCTITLE"] = 1 define_macros["HAVE_PS_STRING"] = 1 # NOTE: the module may work on HP-UX using pstat # thus setting define_macros['HAVE_SYS_PSTAT_H'] # see http://www.noc.utoronto.ca/~mikep/unix/HPTRICKS # But I have none handy to test with. mod_spt = Extension( "setproctitle._setproctitle", define_macros=list(define_macros.items()), sources=[ "src/setproctitle.c", "src/spt_debug.c", "src/spt_setup.c", "src/spt_status.c", "src/spt_strlcpy.c", ] + platform_sources, ) # Try to include the long description in the setup kwargs = {} try: kwargs["long_description"] = ( open("README.rst").read() + "\n" + open("HISTORY.rst").read() ) kwargs["long_description_content_type"] = "text/x-rst" except Exception: pass classifiers = """\ Development Status :: 5 - Production/Stable Intended Audience :: Developers License :: OSI Approved :: BSD License Programming Language :: C Programming Language :: Python :: 3 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 Programming Language :: Python :: 3.13 Operating System :: POSIX :: Linux Operating System :: POSIX :: BSD Operating System :: MacOS :: MacOS X Operating System :: Microsoft :: Windows Topic :: Software Development """.splitlines() class BuildError(Exception): pass class setproctitle_build_ext(build_ext): def run(self) -> None: try: super().run() except Exception as e: print(f"Failed to build C module: {e}", file=sys.stderr) raise BuildError(str(e)) def build_extension(self, ext): try: super().build_extension(ext) except Exception as e: print( f"Failed to build extension {ext.name}: {e}", file=sys.stderr ) raise BuildError(str(e)) def do_build(with_extension): ext_modules = [mod_spt] if with_extension else [] setup( name="setproctitle", description="A Python module to customize the process title", version=VERSION, author="Daniele Varrazzo", author_email="daniele.varrazzo@gmail.com", url="https://github.com/dvarrazzo/py-setproctitle", download_url="http://pypi.python.org/pypi/setproctitle/", license="BSD-3-Clause", platforms=["GNU/Linux", "BSD", "MacOS X", "Windows"], python_requires=">=3.8", classifiers=classifiers, packages=["setproctitle"], package_dir={"setproctitle": "pkg/setproctitle"}, ext_modules=ext_modules, package_data={"setproctitle": ["py.typed"]}, extras_require={"test": ["pytest"]}, cmdclass={"build_ext": setproctitle_build_ext}, zip_safe=False, **kwargs, ) try: do_build(with_extension=True) except BuildError: do_build(with_extension=False) py-setproctitle-version-1.3.6/src/000077500000000000000000000000001500414041500171345ustar00rootroot00000000000000py-setproctitle-version-1.3.6/src/c.h000066400000000000000000000013671500414041500175360ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * c.h * A few fundamental C definitions. * * Copyright (c) 2009-2021 Daniele Varrazzo *------------------------------------------------------------------------- */ #ifndef C_H #define C_H #include "spt_config.h" #if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ <= 201710L) #include #endif #include /* Let's use our version of strlcpy to avoid portability problems */ size_t spt_strlcpy(char *dst, const char *src, size_t siz); /* VC defines _WIN32, not WIN32 */ #ifdef _WIN32 #ifndef WIN32 #define WIN32 _WIN32 #endif #endif #ifdef WIN32 #include #endif #endif /* C_H */ py-setproctitle-version-1.3.6/src/darwin_set_process_name.c000066400000000000000000000132311500414041500241750ustar00rootroot00000000000000/* Set process title in a way compatible with Activity Monitor and other MacOS system tools. Idea is borrowed from libuv (used by node.js) See https://github.com/libuv/libuv/blob/v1.x/src/unix/darwin-proctitle.c Implementation rewritten from scratch, fixing various libuv bugs among other things */ #include #include #include #include "darwin_set_process_name.h" #define DONE_IF(cond) if (cond) goto done; /* Undocumented Launch Services functions */ typedef enum { kLSDefaultSessionID = -2, } LSSessionID; CFTypeRef LSGetCurrentApplicationASN(void); OSStatus LSSetApplicationInformationItem(LSSessionID, CFTypeRef, CFStringRef, CFStringRef, CFDictionaryRef*); CFDictionaryRef LSApplicationCheckIn(LSSessionID, CFDictionaryRef); void LSSetApplicationLaunchServicesServerConnectionStatus(uint64_t, void *); typedef struct { void * application_services_handle; CFBundleRef launch_services_bundle; typeof(LSGetCurrentApplicationASN) * pLSGetCurrentApplicationASN; typeof(LSSetApplicationInformationItem) * pLSSetApplicationInformationItem; typeof(LSApplicationCheckIn) * pLSApplicationCheckIn; typeof(LSSetApplicationLaunchServicesServerConnectionStatus) * pLSSetApplicationLaunchServicesServerConnectionStatus; CFStringRef * display_name_key_ptr; } launch_services_t; static bool launch_services_init(launch_services_t * it) { enum { has_nothing, has_application_services_handle } state = has_nothing; bool ret = false; it->application_services_handle = dlopen("/System/Library/Frameworks/" "ApplicationServices.framework/" "Versions/Current/ApplicationServices", RTLD_LAZY | RTLD_LOCAL); DONE_IF(!it->application_services_handle); ++state; it->launch_services_bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.LaunchServices")); DONE_IF(!it->launch_services_bundle); #define LOAD_METHOD(name) \ *(void **)(&it->p ## name ) = \ CFBundleGetFunctionPointerForName(it->launch_services_bundle, CFSTR("_" #name)); \ DONE_IF(!it->p ## name); LOAD_METHOD(LSGetCurrentApplicationASN) LOAD_METHOD(LSSetApplicationInformationItem) LOAD_METHOD(LSApplicationCheckIn) LOAD_METHOD(LSSetApplicationLaunchServicesServerConnectionStatus) #undef LOAD_METHOD it->display_name_key_ptr = CFBundleGetDataPointerForName(it->launch_services_bundle, CFSTR("_kLSDisplayNameKey")); DONE_IF(!it->display_name_key_ptr || !*it->display_name_key_ptr); ret = true; done: switch(state) { case has_application_services_handle: if (!ret) dlclose(it->application_services_handle); case has_nothing: ; } return ret; } static inline void launch_services_destroy(launch_services_t * it) { dlclose(it->application_services_handle); } static bool launch_services_set_process_title(const launch_services_t * it, const char * title) { enum { has_nothing, has_cf_title } state = has_nothing; bool ret = false; static bool checked_in = false; CFTypeRef asn; CFStringRef cf_title; CFDictionaryRef info_dict; CFMutableDictionaryRef mutable_info_dict; CFStringRef LSUIElement_key; if (!checked_in) { it->pLSSetApplicationLaunchServicesServerConnectionStatus(0, NULL); // See https://github.com/dvarrazzo/py-setproctitle/issues/143 // We need to set LSUIElement (https://developer.apple.com/documentation/bundleresources/information-property-list/lsuielement) // key to true to avoid macOS > 15 displaying the Dock icon. info_dict = CFBundleGetInfoDictionary(CFBundleGetMainBundle()); mutable_info_dict = CFDictionaryCreateMutableCopy(NULL, 0, info_dict); LSUIElement_key = CFStringCreateWithCString(NULL, "LSUIElement", kCFStringEncodingUTF8); CFDictionaryAddValue(mutable_info_dict, LSUIElement_key, kCFBooleanTrue); CFRelease(LSUIElement_key); it->pLSApplicationCheckIn(kLSDefaultSessionID, mutable_info_dict); CFRelease(mutable_info_dict); checked_in = true; } asn = it->pLSGetCurrentApplicationASN(); DONE_IF(!asn); cf_title = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); DONE_IF(!cf_title); ++state; DONE_IF(it->pLSSetApplicationInformationItem(kLSDefaultSessionID, asn, *it->display_name_key_ptr, cf_title, NULL) != noErr); ret = true; done: switch(state) { case has_cf_title: CFRelease(cf_title); case has_nothing: ; } return ret; } static bool darwin_pthread_setname_np(const char* name) { char namebuf[64]; /* MAXTHREADNAMESIZE according to libuv */ strncpy(namebuf, name, sizeof(namebuf) - 1); namebuf[sizeof(namebuf) - 1] = '\0'; return (pthread_setname_np(namebuf) != 0); } bool darwin_set_process_title(const char * title) { enum { has_nothing, has_launch_services } state = has_nothing; bool ret = false; launch_services_t launch_services; DONE_IF(!launch_services_init(&launch_services)); ++state; DONE_IF(!launch_services_set_process_title(&launch_services, title)); (void)darwin_pthread_setname_np(title); ret = true; done: switch(state) { case has_launch_services: launch_services_destroy(&launch_services); case has_nothing: ; } return ret; } py-setproctitle-version-1.3.6/src/darwin_set_process_name.h000066400000000000000000000003261500414041500242030ustar00rootroot00000000000000#ifndef HEADER_DARWIN_SET_PROCESS_NAME_H_INCLUDED #define HEADER_DARWIN_SET_PROCESS_NAME_H_INCLUDED #include "spt_config.h" #include HIDDEN bool darwin_set_process_title(const char * title); #endif py-setproctitle-version-1.3.6/src/setproctitle.c000066400000000000000000000076351500414041500220340ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * setproctitle.c * Python extension module to update and read the process title. * * Copyright (c) 2009-2021 Daniele Varrazzo * * The module allows Python code to access the functions get_ps_display() * and set_ps_display(). * *------------------------------------------------------------------------- */ #include "spt.h" #include "spt_setup.h" #include "spt_status.h" #ifndef SPT_VERSION #define SPT_VERSION unknown #endif /* macro trick to stringify a macro expansion */ #define xstr(s) str(s) #define str(s) #s /* ----------------------------------------------------- */ static char spt_setproctitle__doc__[] = "setproctitle(title) -- Change the process title." ; static PyObject * spt_setproctitle(PyObject *self, PyObject *args, PyObject *kwargs) { const char *title = NULL; static char *kwlist[] = {"title", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &title)) { spt_debug("failed to parse tuple and keywords"); return NULL; } if (spt_setup() < 0) { spt_debug("failed to initialize setproctitle"); } /* Initialize the process title */ set_ps_display(title, true); Py_RETURN_NONE; } static char spt_getproctitle__doc__[] = "getproctitle() -- Get the current process title." ; static PyObject * spt_getproctitle(PyObject *self, PyObject *args) { size_t tlen; const char *title; if (spt_setup() < 0) { spt_debug("failed to initialize setproctitle"); } title = get_ps_display(&tlen); return Py_BuildValue("s#", title, (int)tlen); } static char spt_setthreadtitle__doc__[] = "setthreadtitle(title) -- Change the thread title." ; static PyObject * spt_setthreadtitle(PyObject *self, PyObject *args, PyObject *kwargs) { const char *title = NULL; static char *kwlist[] = {"title", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &title)) { spt_debug("failed to parse tuple and keywords"); return NULL; } set_thread_title(title); Py_RETURN_NONE; } static char spt_getthreadtitle__doc__[] = "getthreadtitle() -- Return the thread title." ; static PyObject * spt_getthreadtitle(PyObject *self, PyObject *args) { char title[16] = {'\0'}; get_thread_title(title); return Py_BuildValue("s", title); } /* Module initialization function */ static int spt_exec(PyObject *m) { spt_debug("module init"); return 0; } /* List of slots defined in the module */ static PyModuleDef_Slot spt_slots[] = { {Py_mod_exec, spt_exec}, #if PY_VERSION_HEX >= 0x030c0000 {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, #endif #if PY_VERSION_HEX >= 0x030d0000 {Py_mod_gil, Py_MOD_GIL_NOT_USED}, #endif {0, NULL} }; /* List of methods defined in the module */ static struct PyMethodDef spt_methods[] = { {"setproctitle", (PyCFunction)spt_setproctitle, METH_VARARGS|METH_KEYWORDS, spt_setproctitle__doc__}, {"getproctitle", (PyCFunction)spt_getproctitle, METH_NOARGS, spt_getproctitle__doc__}, {"setthreadtitle", (PyCFunction)spt_setthreadtitle, METH_VARARGS|METH_KEYWORDS, spt_setthreadtitle__doc__}, {"getthreadtitle", (PyCFunction)spt_getthreadtitle, METH_NOARGS, spt_getthreadtitle__doc__}, {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */ }; /* Initialization function for the module (*must* be called initsetproctitle) */ static char setproctitle_module_documentation[] = "Allow customization of the process title." ; static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_setproctitle", setproctitle_module_documentation, 0, spt_methods, spt_slots, NULL, NULL, NULL }; PyMODINIT_FUNC PyInit__setproctitle(void) { return PyModuleDef_Init(&moduledef); } py-setproctitle-version-1.3.6/src/spt.h000066400000000000000000000007501500414041500201150ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * spt.h * Definitions useful throughout all the extension. * * Copyright (c) 2010-2021 Daniele Varrazzo * *------------------------------------------------------------------------- */ #ifndef SPT_H #define SPT_H #include "spt_config.h" #include "spt_python.h" /* expose the debug function to the extension code */ HIDDEN void spt_debug(const char *fmt, ...); #endif py-setproctitle-version-1.3.6/src/spt_config.h000066400000000000000000000012521500414041500214400ustar00rootroot00000000000000/* Stub file: should be created in configuration phase */ /* This configuration was taken from an Ubuntu i386 installation. */ /* Define to 1 if you have the `setproctitle' function. */ /* #undef HAVE_SETPROCTITLE */ /* Define to 1 if the PS_STRINGS thing exists. */ /* #undef HAVE_PS_STRINGS */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_PSTAT_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_PRCTL_H */ /* GCC 4.0 and later have support for specifying symbol visibility */ #if __GNUC__ >= 4 && !defined(__MINGW32__) # define HIDDEN __attribute__((visibility("hidden"))) #else # define HIDDEN #endif py-setproctitle-version-1.3.6/src/spt_debug.c000066400000000000000000000016631500414041500212620ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * spt_python.c * A simple function for the module debugging. * * Copyright (c) 2009-2021 Daniele Varrazzo * * Debug logging is enabled if the environment variable SPT_DEBUG is set to a * non-empty value at runtime. * *------------------------------------------------------------------------- */ #include #include #include #include "spt_config.h" HIDDEN void spt_debug(const char *fmt, ...) { static int enabled = -1; va_list ap; /* check if debug is enabled */ if (-1 == enabled) { char *d = getenv("SPT_DEBUG"); enabled = (d && *d) ? 1 : 0; } /* bail out if debug is not enabled */ if (0 == enabled) { return; } fprintf(stderr, "[SPT]: "); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); } py-setproctitle-version-1.3.6/src/spt_python.h000066400000000000000000000011521500414041500215130ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * spt_python.h * Include and customize Python definitions. * * Copyright (c) 2010-2021 Daniele Varrazzo * *------------------------------------------------------------------------- */ #ifndef SPT_PYTHON_H #define SPT_PYTHON_H #define PY_SSIZE_T_CLEAN #include /* Detect pypy */ #ifdef PYPY_VERSION #define IS_PYPY #endif #ifndef __darwin__ /* defined in Modules/main.c but not publically declared */ void Py_GetArgcArgv(int *argc, wchar_t ***argv); #endif #endif /* SPT_PYTHON_H */ py-setproctitle-version-1.3.6/src/spt_setup.c000066400000000000000000000301661500414041500213340ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * spt_setup.c * Initalization code for the spt_status.c module functions. * * Copyright (c) 2009-2021 Daniele Varrazzo * *------------------------------------------------------------------------- */ #include "spt_setup.h" #include "spt.h" #include "spt_status.h" #include /* Darwin doesn't export environ */ #if defined(__darwin__) #include #define environ (*_NSGetEnviron()) #elif !defined(_WIN32) extern char **environ; #endif #ifndef WIN32 /* Return a concatenated version of a strings vector. * * Return newly allocated heap space: clean it up with free(). * * Return NULL and raise an exception on error. */ static char * join_argv(int argc, char **argv) { int i; size_t len = 0; char *buf; char *src; char *dest; /* Calculate the final string length */ for (i = 0; i < argc; i++) { len += strlen(argv[i]) + 1; } if (!(dest = buf = (char *)malloc(len))) { PyErr_NoMemory(); return NULL; } /* Copy the strings in the buffer joining with spaces */ for (i = 0; i < argc; i++) { src = argv[i]; while (*src) { *dest++ = *src++; } *dest++ = ' '; } *--dest = '\x00'; return buf; } #ifndef __darwin__ /* I don't expect it to be defined: should include limits.h. But then it's * another of those ./configure can of worms to find where it is... */ #ifndef ARG_MAX #define ARG_MAX (96 * 1024) #endif /* Return a copy of argv[0] encoded in the default encoding. * * Return a newly allocated buffer to be released with free(). * * Return NULL in case of error. If the error shouldn't be ignored, also set * a Python exception. */ static char * get_encoded_arg0(wchar_t *argv0) { PyObject *ua = NULL, *ba = NULL; char *rv = NULL; if (!(ua = PyUnicode_FromWideChar(argv0, -1))) { spt_debug("failed to convert argv[0] to unicode"); PyErr_Clear(); goto exit; } if (!(ba = PyUnicode_AsEncodedString( ua, PyUnicode_GetDefaultEncoding(), "strict"))) { spt_debug("failed to encode argv[0]"); PyErr_Clear(); goto exit; } if (!(rv = strdup(PyBytes_AsString(ba)))) { PyErr_NoMemory(); } exit: Py_XDECREF(ua); Py_XDECREF(ba); return rv; } /* Find the original arg buffer starting from the env position. * * Return a malloc'd argv vector, pointing to the original arguments. * * Return NULL in case of error. If the error shouldn't be ignored, also set * a Python exception. * * Required on Python 3 as Py_GetArgcArgv doesn't return pointers to the * original area. It can be used on Python 2 too in case we can't get argv, * such as in embedded environment. */ static char ** find_argv_from_env(int argc, char *arg0) { int i; char **buf = NULL; char **rv = NULL; char *ptr; char *limit; spt_debug("walking from environ to look for the arguments"); if (!(buf = (char **)malloc((argc + 1) * sizeof(char *)))) { spt_debug("can't malloc %d args!", argc); PyErr_NoMemory(); goto exit; } buf[argc] = NULL; /* Walk back from environ until you find argc-1 null-terminated strings. * Don't look for argv[0] as it's probably not preceded by 0. */ ptr = environ[0]; if (!ptr) { /* It happens on os.environ.clear() */ spt_debug("environ pointer is NULL"); goto exit; } spt_debug("found environ at %p", ptr); limit = ptr - ARG_MAX; --ptr; for (i = argc - 1; i >= 1; --i) { if (*ptr) { spt_debug("zero %d not found", i); goto exit; } --ptr; while (*ptr && ptr > limit) { --ptr; } if (ptr <= limit) { spt_debug("failed to found arg %d start", i); goto exit; } buf[i] = (ptr + 1); spt_debug("found argv[%d] at %p: %s", i, buf[i], buf[i]); } /* The first arg has not a zero in front. But what we have is reliable * enough (modulo its encoding). Check if it is exactly what found. * * The check is known to fail on OS X with locale C if there are * non-ascii characters in the executable path. See Python issue #9167 */ ptr -= strlen(arg0); spt_debug("argv[0] should be at %p", ptr); if (ptr <= limit) { spt_debug("failed to find argv[0] start"); goto exit; } if (strcmp(ptr, arg0)) { spt_debug("argv[0] '%s' doesn't match '%s'", ptr, arg0); goto exit; } /* We have all the pieces of the jigsaw. */ buf[0] = ptr; spt_debug("found argv[0]: %s", buf[0]); rv = buf; buf = NULL; exit: if (buf) { free(buf); } return rv; } /* Come on, why is this missing?! this is just cruel! * I guess you club seal pups for hobby. */ PyObject * PyFile_FromString(const char *filename, const char *mode) { PyObject *io = NULL; PyObject *rv = NULL; if (!(io = PyImport_ImportModule("io"))) { spt_debug("failed to import io"); goto exit; } rv = PyObject_CallMethod(io, "open", "ss", filename, mode); exit: Py_XDECREF(io); return rv; } /* Read the number of arguments and the first argument from /proc/pid/cmdline * * Return 0 if found, else -1. Return arg0 in a malloc'd array. * * If the function fails in a way that shouldn't be ignored, also set * a Python exception. */ static int get_args_from_proc(int *argc_o, char **arg0_o) { /* allow /proc/PID/cmdline, with oversize max_pid, and them some. */ #define FNLEN 30 char fn[FNLEN]; PyObject *os = NULL; PyObject *pid_py = NULL; long pid; PyObject *f = NULL; PyObject *cl = NULL; PyObject *tmp = NULL; int rv = -1; spt_debug("looking for args into proc fs"); /* get the pid from os.getpid() */ if (!(os = PyImport_ImportModule("os"))) { spt_debug("failed to import os"); goto exit; } if (!(pid_py = PyObject_CallMethod(os, "getpid", NULL))) { spt_debug("calling os.getpid() failed"); /* os.getpid() may be not available, so ignore this error. */ PyErr_Clear(); goto exit; } if (-1 == (pid = PyLong_AsLong(pid_py))) { spt_debug("os.getpid() returned crap?"); /* Don't bother to check PyErr_Occurred as pid can't just be -1. */ goto exit; } /* get the content of /proc/PID/cmdline */ snprintf(fn, FNLEN, "/proc/%ld/cmdline", pid); if (!(f = PyFile_FromString(fn, "rb"))) { spt_debug("opening '%s' failed", fn); /* That's ok: procfs is easily not available on menomated unices */ PyErr_Clear(); goto exit; } /* the file has been open in binary mode, so we get bytes */ cl = PyObject_CallMethod(f, "read", NULL); if (!(tmp = PyObject_CallMethod(f, "close", NULL))) { spt_debug("closing failed"); } else { Py_DECREF(tmp); } if (!cl) { spt_debug("reading failed"); /* could there be some protected environment where a process cannot * read its own pid? Who knows, better not to risk. */ PyErr_Clear(); goto exit; } /* the cmdline is a buffer of null-terminated strings. We can strdup it to * get a copy of arg0, and count the zeros to get argc */ { char *ccl; Py_ssize_t i; if (!(ccl = PyBytes_AsString(cl))) { spt_debug("failed to get cmdline string"); goto exit; } if (!(*arg0_o = strdup(ccl))) { spt_debug("arg0 strdup failed"); PyErr_NoMemory(); goto exit; } spt_debug("got argv[0] = '%s' from /proc", *arg0_o); *argc_o = 0; for (i = PyBytes_Size(cl) - 1; i >= 0; --i) { if (ccl[i] == '\0') { (*argc_o)++; } } spt_debug("got argc = %d from /proc", *argc_o); } /* success */ rv = 0; exit: Py_XDECREF(cl); Py_XDECREF(f); Py_XDECREF(pid_py); Py_XDECREF(os); return rv; } /* Find the original arg buffer, return 0 if found, else -1. * * If found, set argc to the number of arguments, argv to an array * of pointers to the single arguments. The array is allocated via malloc. * * If the function fails in a way that shouldn't be ignored, also set * a Python exception. * * The function overcomes three Py_GetArgcArgv shortcomings: * - some python parameters mess up with the original argv, e.g. -m * (see issue #8) * - with Python 3, argv is a decoded copy and doesn't point to * the original area. * - If python is embedded, the function doesn't return anything. */ static int get_argc_argv(int *argc_o, char ***argv_o) { int argc = 0; wchar_t **argv_py = NULL; char **argv = NULL; char *arg0 = NULL; int rv = -1; #ifndef IS_PYPY spt_debug("reading argc/argv from Python main"); Py_GetArgcArgv(&argc, &argv_py); #endif if (argc > 0) { spt_debug("found %d arguments", argc); if (!(arg0 = get_encoded_arg0(argv_py[0]))) { spt_debug("couldn't get a copy of argv[0]"); goto exit; } /* we got argv: on py2 it used to pointsto the right place in memory; on * py3 we only got a copy of argv[0]: we will use it to look from env */ } else { spt_debug("no good news from Py_GetArgcArgv"); /* get a copy of argv[0] from /proc, so we get back in the same * situation of Py3 */ if (0 > get_args_from_proc(&argc, &arg0)) { spt_debug("failed to get args from proc fs"); goto exit; } } /* If we don't know argv but we know the content of argv[0], we can walk * backwards from environ and see if we get it. */ if (arg0 && !argv) { if (!(argv = find_argv_from_env(argc, arg0))) { spt_debug("couldn't find argv from environ"); goto exit; } } /* success */ *argc_o = argc; *argv_o = argv; argv = NULL; rv = 0; exit: if (arg0) { free(arg0); } if (argv) { free(argv); } return rv; } #else /* __darwin__ */ static int get_argc_argv(int *argc_o, char ***argv_o) { int * pargc = _NSGetArgc(); if (!pargc) { spt_debug("_NSGetArgc returned NULL"); return -1; } int argc = *pargc; char *** pargv = _NSGetArgv(); if (!pargv) { spt_debug("_NSGetArgv returned NULL"); return -1; } char ** buf = malloc((argc + 1) * sizeof(char *)); if (!buf) { spt_debug("can't malloc %d args!", argc); PyErr_NoMemory(); return -1; } memcpy(buf, *pargv, argc * sizeof(char *)); buf[argc] = NULL; *argc_o = argc; *argv_o = buf; return 0; } #endif /* __darwin__ */ #endif /* !WIN32 */ /* Initialize the module internal functions. * * The function reproduces the initialization performed by PostgreSQL * to be able to call the functions in pg_status.c * * Return 0 in case of success, else -1. In case of failure with an error that * shouldn't be ignored, also set a Python exception. * * The function should be called only once in the process lifetime. * so is called at module initialization. After the function is called, * set_ps_display() can be used. */ int spt_setup(void) { const int not_happened = 3; static int rv = 3; /* Make sure setup happens just once, either successful or failed */ if (rv != not_happened) { spt_debug("setup was called more than once!"); return rv; } rv = -1; #ifndef WIN32 int argc = 0; char **argv = NULL; char *init_title; if (0 > get_argc_argv(&argc, &argv)) { spt_debug("get_argc_argv failed"); goto exit; } save_ps_display_args(argc, argv); /* Set up the first title to fully initialize the code */ if (!(init_title = join_argv(argc, argv))) { goto exit; } init_ps_display(init_title); free(init_title); #else /* On Windows save_ps_display_args is a no-op * This is a good news, because Py_GetArgcArgv seems not usable. */ LPTSTR init_title = GetCommandLine(); init_ps_display(init_title); #endif rv = 0; exit: return rv; } py-setproctitle-version-1.3.6/src/spt_setup.h000066400000000000000000000006441500414041500213370ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * spt_setup.h * Initalization code for the spt_status.c module functions. * * Copyright (c) 2009-2021 Daniele Varrazzo * *------------------------------------------------------------------------- */ #ifndef SPT_SETUP_H #define SPT_SETUP_H #include "spt_config.h" HIDDEN int spt_setup(void); #endif py-setproctitle-version-1.3.6/src/spt_status.c000066400000000000000000000320721500414041500215150ustar00rootroot00000000000000/*-------------------------------------------------------------------- * spt_status.c * * Routines to support changing the ps display of a process. * Mechanism differs wildly across platforms. * * Copyright (c) 2000-2009, PostgreSQL Global Development Group * Copyright (c) 2009-2021 Daniele Varrazzo * various details abducted from various places * * This file was taken from PostgreSQL. The PostgreSQL copyright terms follow. *-------------------------------------------------------------------- */ /* * PostgreSQL Database Management System * (formerly known as Postgres, then as Postgres95) * * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * * Portions Copyright (c) 1994, The Regents of the University of California * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose, without fee, and without a written agreement * is hereby granted, provided that the above copyright notice and this * paragraph and the following two paragraphs appear in all copies. * * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ #include "spt.h" #include "spt_config.h" /* note: VC doesn't have this, but it was working on mingw instead * so check on _WIN32 (defined by VC) instead of WIN32 */ #ifndef _WIN32 #include #endif #ifdef HAVE_SYS_PSTAT_H #include /* for HP-UX */ #endif #ifdef HAVE_PS_STRINGS #include /* for old BSD */ #include #endif #ifdef HAVE_SYS_PRCTL_H #include /* for Linux >= 2.6.9 */ #endif #if defined(__darwin__) #include #include "darwin_set_process_name.h" #endif #include "spt_status.h" #include #include #include /* Darwin doesn't export environ */ #if defined(__darwin__) #define environ (*_NSGetEnviron()) #else extern char **environ; #endif bool update_process_title = true; /* * Alternative ways of updating ps display: * * PS_USE_SETPROCTITLE * use the function setproctitle(const char *, ...) * (newer BSD systems) * PS_USE_PSTAT * use the pstat(PSTAT_SETCMD, ) * (HPUX) * PS_USE_PS_STRINGS * assign PS_STRINGS->ps_argvstr = "string" * (some BSD systems) * PS_USE_CHANGE_ARGV * assign argv[0] = "string" * (some other BSD systems) * PS_USE_PRCTL * use prctl(PR_SET_NAME, ) * (Linux >= 2.6.9) * PS_USE_CLOBBER_ARGV * write over the argv and environment area * (most SysV-like systems) * PS_USE_WIN32 * push the string out as the name of a Windows event * PS_USE_NONE * don't update ps display * (This is the default, as it is safest.) */ #if defined(HAVE_SETPROCTITLE) #define PS_USE_SETPROCTITLE #elif defined(HAVE_PSTAT) && defined(PSTAT_SETCMD) #define PS_USE_PSTAT #elif defined(HAVE_PS_STRINGS) #define PS_USE_PS_STRINGS #elif (defined(BSD) || defined(__bsdi__) || defined(__hurd__)) && !defined(__darwin__) #define PS_USE_CHANGE_ARGV #elif defined(__linux__) || defined(_AIX) || defined(__sgi) || (defined(sun) && !defined(BSD)) || defined(ultrix) || defined(__ksr__) || defined(__osf__) || defined(__svr4__) || defined(__svr5__) #define PS_USE_CLOBBER_ARGV #elif defined(__darwin__) #define PS_USE_CLOBBER_ARGV #define PS_USE_DARWIN #elif defined(WIN32) #define PS_USE_WIN32 #else #define PS_USE_NONE #endif /* we use this strategy together with another one (probably PS_USE_CLOBBER_ARGV) */ #if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_NAME) && !defined(PS_USE_NONE) #define PS_USE_PRCTL #endif /* Different systems want the buffer padded differently */ #if defined(_AIX) || defined(__linux__) || defined(__svr4__) || defined(__darwin__) #define PS_PADDING '\0' #else #define PS_PADDING ' ' #endif #ifndef PS_USE_CLOBBER_ARGV /* all but one options need a buffer to write their ps line in */ #define PS_BUFFER_SIZE 256 static char ps_buffer[PS_BUFFER_SIZE]; static const size_t ps_buffer_size = PS_BUFFER_SIZE; #else /* PS_USE_CLOBBER_ARGV */ static char *ps_buffer; /* will point to argv area */ static size_t ps_buffer_size; /* space determined at run time */ static size_t last_status_len; /* use to minimize length of clobber */ #endif /* PS_USE_CLOBBER_ARGV */ static size_t ps_buffer_fixed_size; /* size of the constant prefix */ /* save the original argv[] location here */ static int save_argc; static char **save_argv; /* * Call this early in startup to save the original argc/argv values. * If needed, we make a copy of the original argv[] array to preserve it * from being clobbered by subsequent ps_display actions. * * (The original argv[] will not be overwritten by this routine, but may be * overwritten during init_ps_display. Also, the physical location of the * environment strings may be moved, so this should be called before any code * that might try to hang onto a getenv() result.) */ char ** save_ps_display_args(int argc, char **argv) { save_argc = argc; save_argv = argv; #if defined(PS_USE_CLOBBER_ARGV) /* * If we're going to overwrite the argv area, count the available space. * Also move the environment to make additional room. */ { char *end_of_area = NULL; char **new_environ; int i; /* * check for contiguous argv strings */ for (i = 0; i < argc; i++) { if (i == 0 || end_of_area + 1 == argv[i]) end_of_area = argv[i] + strlen(argv[i]); } if (end_of_area == NULL) /* probably can't happen? */ { ps_buffer = NULL; ps_buffer_size = 0; return argv; } { /* * Clobbering environ works fine from within the process, but some * external utils use /proc/PID/environ and they would find noting, * or mess, if we clobber it. A user can define SPT_NOENV to limit * clobbering to argv (see ticket #16). */ char *noenv; noenv = getenv("SPT_NOENV"); if (!noenv || !*noenv) { /* * check for contiguous environ strings following argv */ for (i = 0; environ[i] != NULL; i++) { if (end_of_area + 1 == environ[i]) end_of_area = environ[i] + strlen(environ[i]); } /* * move the environment out of the way */ spt_debug("environ has been copied"); new_environ = (char **) malloc((i + 1) * sizeof(char *)); for (i = 0; environ[i] != NULL; i++) new_environ[i] = strdup(environ[i]); new_environ[i] = NULL; environ = new_environ; } } ps_buffer = argv[0]; last_status_len = ps_buffer_size = end_of_area - argv[0]; } #endif /* PS_USE_CLOBBER_ARGV */ #if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV) /* * If we're going to change the original argv[] then make a copy for * argument parsing purposes. * * (NB: do NOT think to remove the copying of argv[], even though * postmaster.c finishes looking at argv[] long before we ever consider * changing the ps display. On some platforms, getopt() keeps pointers * into the argv array, and will get horribly confused when it is * re-called to analyze a subprocess' argument string if the argv storage * has been clobbered meanwhile. Other platforms have other dependencies * on argv[]. */ { char **new_argv; int i; new_argv = (char **) malloc((argc + 1) * sizeof(char *)); for (i = 0; i < argc; i++) new_argv[i] = strdup(argv[i]); new_argv[argc] = NULL; #if defined(__darwin__) /* * Darwin (and perhaps other NeXT-derived platforms?) has a static * copy of the argv pointer, which we may fix like so: */ *_NSGetArgv() = new_argv; #endif argv = new_argv; } #endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */ return argv; } /* * Call this once during subprocess startup to set the identification * values. At this point, the original argv[] array may be overwritten. */ void init_ps_display(const char *initial_str) { #ifndef PS_USE_NONE /* no ps display if you didn't call save_ps_display_args() */ if (!save_argv) return; #ifdef PS_USE_CLOBBER_ARGV /* If ps_buffer is a pointer, it might still be null */ if (!ps_buffer) return; #endif /* * Overwrite argv[] to point at appropriate space, if needed */ #ifdef PS_USE_CHANGE_ARGV save_argv[0] = ps_buffer; save_argv[1] = NULL; #endif /* PS_USE_CHANGE_ARGV */ #ifdef PS_USE_CLOBBER_ARGV { int i; /* make extra argv slots point at end_of_area (a NUL) */ for (i = 1; i < save_argc; i++) save_argv[i] = ps_buffer + ps_buffer_size; } #endif /* PS_USE_CLOBBER_ARGV */ /* * Make fixed prefix of ps display. */ ps_buffer[0] = '\0'; ps_buffer_fixed_size = strlen(ps_buffer); set_ps_display(initial_str, true); #endif /* not PS_USE_NONE */ } /* * Call this to update the ps status display to a fixed prefix plus an * indication of what you're currently doing passed in the argument. */ void set_ps_display(const char *activity, bool force) { if (!force && !update_process_title) return; #ifndef PS_USE_NONE #ifdef PS_USE_CLOBBER_ARGV /* If ps_buffer is a pointer, it might still be null */ if (!ps_buffer) return; #endif /* Update ps_buffer to contain both fixed part and activity */ spt_strlcpy(ps_buffer + ps_buffer_fixed_size, activity, ps_buffer_size - ps_buffer_fixed_size); /* Transmit new setting to kernel, if necessary */ #ifdef PS_USE_DARWIN darwin_set_process_title(ps_buffer); #endif #ifdef PS_USE_SETPROCTITLE setproctitle("%s", ps_buffer); #endif #ifdef PS_USE_PSTAT { union pstun pst; pst.pst_command = ps_buffer; pstat(PSTAT_SETCMD, pst, strlen(ps_buffer), 0, 0); } #endif /* PS_USE_PSTAT */ #ifdef PS_USE_PS_STRINGS PS_STRINGS->ps_nargvstr = 1; PS_STRINGS->ps_argvstr = ps_buffer; #endif /* PS_USE_PS_STRINGS */ #ifdef PS_USE_CLOBBER_ARGV { size_t buflen; /* pad unused memory */ buflen = strlen(ps_buffer); /* clobber remainder of old status string */ if (last_status_len > buflen) memset(ps_buffer + buflen, PS_PADDING, last_status_len - buflen); last_status_len = buflen; } #endif /* PS_USE_CLOBBER_ARGV */ #ifdef PS_USE_PRCTL prctl(PR_SET_NAME, ps_buffer); #endif #ifdef PS_USE_WIN32 { /* * Win32 does not support showing any changed arguments. To make it at * all possible to track which backend is doing what, we create a * named object that can be viewed with for example Process Explorer. */ static HANDLE ident_handle = INVALID_HANDLE_VALUE; char name[PS_BUFFER_SIZE + 32]; if (ident_handle != INVALID_HANDLE_VALUE) CloseHandle(ident_handle); sprintf(name, "python(%d): %s", _getpid(), ps_buffer); ident_handle = CreateEvent(NULL, TRUE, FALSE, name); } #endif /* PS_USE_WIN32 */ #endif /* not PS_USE_NONE */ } /* * Returns what's currently in the ps display, in case someone needs * it. Note that only the activity part is returned. On some platforms * the string will not be null-terminated, so return the effective * length into *displen. */ const char * get_ps_display(size_t *displen) { #ifdef PS_USE_CLOBBER_ARGV size_t offset; /* If ps_buffer is a pointer, it might still be null */ if (!ps_buffer) { *displen = 0; return ""; } /* Remove any trailing spaces to offset the effect of PS_PADDING */ offset = ps_buffer_size; while (offset > ps_buffer_fixed_size && ps_buffer[offset - 1] == PS_PADDING) offset--; *displen = offset - ps_buffer_fixed_size; #else *displen = strlen(ps_buffer + ps_buffer_fixed_size); #endif return ps_buffer + ps_buffer_fixed_size; } void set_thread_title(const char *title) { #ifdef PS_USE_PRCTL prctl(PR_SET_NAME, title); #endif } void get_thread_title(char *title) { #ifdef PS_USE_PRCTL prctl(PR_GET_NAME, title); #endif } py-setproctitle-version-1.3.6/src/spt_status.h000066400000000000000000000013101500414041500215110ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * spt_status.h * * Declarations for spt_status.c * *------------------------------------------------------------------------- */ #ifndef SPT_STATUS_H #define SPT_STATUS_H #include "c.h" HIDDEN extern bool update_process_title; HIDDEN extern char **save_ps_display_args(int argc, char **argv); HIDDEN extern void init_ps_display(const char *initial_str); HIDDEN extern void set_ps_display(const char *activity, bool force); HIDDEN extern const char *get_ps_display(size_t *displen); HIDDEN extern void set_thread_title(const char *title); HIDDEN extern void get_thread_title(char *title); #endif /* SPT_STATUS_H */ py-setproctitle-version-1.3.6/src/spt_strlcpy.c000066400000000000000000000041351500414041500216710ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * strlcpy.c * strncpy done right * * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * * * IDENTIFICATION * $PostgreSQL: pgsql/src/port/strlcpy.c,v 1.4 2007/01/05 22:20:03 momjian Exp $ * * This file was taken from OpenBSD and is used on platforms that don't * provide strlcpy(). The OpenBSD copyright terms follow. *------------------------------------------------------------------------- */ /* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "c.h" /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. * Function creation history: http://www.gratisoft.us/todd/papers/strlcpy.html */ size_t spt_strlcpy(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0) { while (--n != 0) { if ((*d++ = *s++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return (s - src - 1); /* count does not include NUL */ } py-setproctitle-version-1.3.6/tests/000077500000000000000000000000001500414041500175075ustar00rootroot00000000000000py-setproctitle-version-1.3.6/tests/__init__.py000066400000000000000000000000001500414041500216060ustar00rootroot00000000000000py-setproctitle-version-1.3.6/tests/conftest.py000066400000000000000000000101061500414041500217040ustar00rootroot00000000000000import os import sys import subprocess as sp import pytest def pytest_configure(config): config.addinivalue_line( "markers", "embedded: the test create an embedded executable" ) skip_if_win32 = pytest.mark.skipif( "sys.platform == 'win32'", reason="skipping Posix tests on Windows" ) skip_if_macos = pytest.mark.skipif( "sys.platform == 'darwin'", reason="skipping test on macOS" ) skip_if_pypy = pytest.mark.skipif( "'__pypy__' in sys.builtin_module_names", reason="skipping test on pypy" ) skip_if_no_proc_env = pytest.mark.skipif( "not os.path.exists('/proc/self/environ')", reason="'/proc/self/environ' not available", ) skip_if_no_proc_cmdline = pytest.mark.skipif( "not os.path.exists('/proc/%s/cmdline' % os.getpid())", reason="'/proc/PID/cmdline' not available", ) skip_if_no_proc_tasks = pytest.mark.skipif( "not os.path.exists('/proc/self/task')", reason="'/proc/self/task' not available", ) @pytest.fixture(scope="session") def pyrun(pyconfig): """ Build the pyrun executable and return its path """ # poor man's make here = os.path.abspath(os.path.dirname(__file__)) ver2 = "%s.%s" % sys.version_info[:2] source = os.path.join(here, "pyrun.c") target = os.path.join(here, f"pyrun{ver2}") if ( os.path.exists(target) and os.stat(target).st_mtime > os.stat(source).st_mtime ): return target cmdline = ["cc"] # big punt cmdline.extend(pyconfig("includes")) cmdline.extend(["-o", target, source]) cmdline.extend(pyconfig("ldflags")) cmdline.append(f"-L{pyconfig('prefix')[0]}/lib") sp.check_call(cmdline) return target @pytest.fixture(scope="session") def pyconfig(): """Return the result of 'python-config --opt' as a list of strings""" pyexe = os.path.realpath(sys.executable) ver2 = "%s.%s" % sys.version_info[:2] for name in (f"python{ver2}-config", "python3-config", "python-config"): pyconfexe = os.path.join(os.path.dirname(pyexe), name) if os.path.exists(pyconfexe): break else: pytest.fail( f"can't find python-config from executable {sys.executable}" ) # Travis' Python 3.8 is not built with --embed help = sp.check_output([pyconfexe, "--help"]) has_embed = b"--embed" in help def pyconfig_func(opt): cmdline = [pyconfexe, f"--{opt}"] if has_embed: cmdline.append("--embed") bout = sp.check_output(cmdline) out = bout.decode( sys.getfilesystemencoding() # sounds like a good bet ) return out.split() return pyconfig_func @pytest.fixture(scope="session") def spt_directory(): """ Where is the setproctitle module installed? """ rv = run_script( """ import os import setproctitle print(os.path.dirname(os.path.dirname(setproctitle.__file__))) """ ) return rv.rstrip() @pytest.fixture(scope="function") def tmp_pypath(monkeypatch, tmp_path): """ return a tmp directory which has been added to the python path """ monkeypatch.setenv( "PYTHONPATH", str(tmp_path) + os.pathsep + os.environ.get("PYTHONPATH", ""), ) return tmp_path def run_script(script=None, args=None, executable=None, env=None): """run a script in a separate process. if the script completes successfully, return its ``stdout``, else fail the test. """ if executable is None: executable = sys.executable cmdline = str(executable) if args: cmdline = cmdline + " " + args proc = sp.Popen( cmdline, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE, env=env, shell=True, close_fds=True, ) out, err = proc.communicate(script and script.encode()) if 0 != proc.returncode: if out: print(out.decode("utf8", "replace"), file=sys.stdout) if err: print(err.decode("utf8", "replace"), file=sys.stderr) pytest.fail("test script failed") # Py3 subprocess generates bytes strings. out = out.decode() return out py-setproctitle-version-1.3.6/tests/module_test.py000066400000000000000000000016301500414041500224050ustar00rootroot00000000000000from .conftest import run_script, skip_if_win32 @skip_if_win32 def test_no_import_side_effect(): """ Check that importing the module doesn't cause side effects. """ rv = run_script( """ import os def print_stuff(): for fn in "cmdline status comm".split(): if os.path.exists(f"/proc/self/{fn}"): with open(f"/proc/self/{fn}") as f: print(f.readline().rstrip()) print_stuff() print("---") import setproctitle print_stuff() """ ) before, after = rv.split("---\n") assert before == after def test_version(): """Test that the module has a version""" rv = run_script( """ import setproctitle print(setproctitle.__version__) """ ) assert rv def test_c_extension_built(): """Test that the C extension was built""" rv = run_script( """ from setproctitle import _setproctitle """ ) assert rv == "" py-setproctitle-version-1.3.6/tests/pyrun.c000066400000000000000000000011111500414041500210220ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * pyrun.c * Stand-alone program to test with embedded Python. * * Run a Python program read from stdin. In case of error return 1. * * Copyright (c) 2011-2021 Daniele Varrazzo * *------------------------------------------------------------------------- */ #include int main(int argc, char *argv[]) { int rv = 0; Py_Initialize(); if (0 != PyRun_SimpleFile(stdin, "stdin")) { rv = 1; } Py_Finalize(); return rv; } py-setproctitle-version-1.3.6/tests/setproctitle_test.py000066400000000000000000000331611500414041500236450ustar00rootroot00000000000000"""setproctitle module unit test. Use pytest to run this test suite. The tests are executed in external processes: setproctitle should never be imported directly from here. Copyright (c) 2009-2021 Daniele Varrazzo """ import os import re import sys import string import subprocess as sp import pytest from .conftest import run_script, skip_if_no_proc_cmdline, skip_if_no_proc_env from .conftest import skip_if_macos, skip_if_pypy, skip_if_win32 pytestmark = [skip_if_win32] def test_runner(): """Test the script execution method.""" rv = run_script( """ print(10 + 20) """ ) assert rv == "30\n" @pytest.mark.skipif( 'sys.platform == "darwin" and (os.environ.get("CIBW_TEST_COMMAND") or sys.version_info >= (3, 11))', reason="f*cked up binary name", ) def test_init_getproctitle(): """getproctitle() returns a sensible value at initial call.""" rv = run_script( """ import setproctitle print(setproctitle.getproctitle()) """, args="-u", ) assert rv == sys.executable + " -u\n" def test_setproctitle(): """setproctitle() can set the process title, duh.""" rv = run_script( r""" import setproctitle setproctitle.setproctitle('Hello, world!') import os print(os.getpid()) # ps can fail on kfreebsd arch # (http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=460331) print(os.popen("ps -x -o pid,command 2> /dev/null").read()) """ ) lines = [line for line in rv.splitlines() if line] pid = lines.pop(0) pids = dict([r.strip().split(None, 1) for r in lines]) title = _clean_up_title(pids[pid]) assert title == "Hello, world!" @pytest.mark.skipif('sys.platform != "darwin"', reason="Mac only test") def test_setproctitle_darwin(): """Mac Activity monitor shows correct info""" rv = run_script( r""" import setproctitle setproctitle.setproctitle('QwErTyZxCvB') import os print(os.popen("lsappinfo find name=QwErTyZxCvB 2> /dev/null").read()) """ ) m = re.search(r'''ASN:.*"QwErTyZxCvB"''', rv) assert m def test_prctl(): """Check that prctl is called on supported platforms.""" linux_version = [] if sys.platform in ("linux", "linux2"): try: f = os.popen("uname -r") name = f.read() f.close() except Exception: pass else: linux_version = list( map(int, re.search("[.0-9]+", name).group().split(".")[:3]) ) if linux_version < [2, 6, 9]: pytest.skip("syscall not supported") rv = run_script( r""" import setproctitle setproctitle.setproctitle('Hello, prctl!') print(open('/proc/self/status').read()) """ ) status = dict([r.split(":", 1) for r in rv.splitlines() if ":" in r]) assert status["Name"].strip() == "Hello, prctl!" def test_getproctitle(): """getproctitle() can read the process title back.""" rv = run_script( r""" import setproctitle setproctitle.setproctitle('Hello, world!') print(setproctitle.getproctitle()) """ ) assert rv == "Hello, world!\n" def test_kwarg(): """setproctitle() supports keyword args.""" rv = run_script( r""" import setproctitle setproctitle.setproctitle(title='Hello, world!') print(setproctitle.getproctitle()) """ ) assert rv == "Hello, world!\n" def test_environ(): """Check that clobbering environ didn't break env.""" rv = run_script( r""" import setproctitle setproctitle.setproctitle('Hello, world! ' + 'X' * 1024) # set a new env variable, update another one import os os.environ['TEST_SETENV'] = "setenv-value" os.environ['PATH'] = os.environ.get('PATH', '') \ + os.pathsep + "fakepath" # read the environment from a spawned process inheriting the # updated env newenv = dict([r.split("=",1) for r in os.popen("env").read().splitlines() if '=' in r]) print(setproctitle.getproctitle()) print(newenv['TEST_SETENV']) print(newenv['PATH']) """ ) title, test, path = rv.splitlines() assert title.startswith("Hello, world! XXXXX"), title assert test == "setenv-value" assert path.endswith("fakepath"), path def test_issue_8(tmp_pypath): """Test that the module works with 'python -m'.""" module = "spt_issue_8" with open(tmp_pypath / f"{module}.py", "w") as f: f.write( r""" import setproctitle setproctitle.setproctitle("Hello, module!") import os print(os.getpid()) print(os.popen("ps -x -o pid,command 2> /dev/null").read()) """ ) rv = run_script(args="-m " + module) lines = [line for line in rv.splitlines() if line] pid = lines.pop(0) pids = dict([r.strip().split(None, 1) for r in lines]) title = _clean_up_title(pids[pid]) assert title == "Hello, module!" def test_large_cmdline(tmp_pypath): """Test with a 64KB command line.""" module = "longargs" with open(tmp_pypath / f"{module}.py", "w") as f: f.write( r""" import setproctitle setproctitle.setproctitle("Hello, long!") import os print(os.getpid()) print(os.popen("ps -x -o pid,command 2> /dev/null").read()) """ ) rv = run_script(args=f"-m {module} {' '.join(['X' * 1024] * 64)}") lines = [line for line in rv.splitlines() if line] pid = lines.pop(0) pids = dict([r.strip().split(None, 1) for r in lines]) title = _clean_up_title(pids[pid]) assert title == "Hello, long!" def test_unicode(): """Title can contain unicode characters.""" snowman = "\u2603" try: snowman.encode(sys.getdefaultencoding()) except UnicodeEncodeError: pytest.skip( "default encoding '%s' can't deal with snowmen" % sys.getdefaultencoding() ) try: snowman.encode(sys.getfilesystemencoding()) except UnicodeEncodeError: pytest.skip( "file system encoding '%s' can't deal with snowmen" % sys.getfilesystemencoding() ) rv = run_script( r""" snowman = u'\u2603' import setproctitle setproctitle.setproctitle("Hello, " + snowman + "!") import os import locale from subprocess import Popen, PIPE print(os.getpid()) proc = Popen("ps -x -o pid,command 2> /dev/null", shell=True, close_fds=True, stdout=PIPE, stderr=PIPE) buf = proc.stdout.read() print(buf.decode(locale.getpreferredencoding(), 'replace')) """ ) lines = [line for line in rv.splitlines() if line] pid = lines.pop(0) pids = dict([r.strip().split(None, 1) for r in lines]) snowmen = [ "\u2603", # ps supports unicode r"\M-b\M^X\M^C", # ps output on BSD r"M-bM^XM^C", # ps output on some Darwin < 11.2 "\ufffdM^XM^C", # ps output on Darwin 11.2 ] title = _clean_up_title(pids[pid]) for snowman in snowmen: if title == "Hello, " + snowman + "!": break else: pytest.fail("unexpected ps output: %r" % title) def test_weird_args(): """No problem with encoded arguments.""" euro = "\u20ac" snowman = "\u2603" try: rv = run_script( r""" import setproctitle setproctitle.setproctitle("Hello, weird args!") import os print(os.getpid()) print(os.popen("ps -x -o pid,command 2> /dev/null").read()) """, args=" ".join(["-", "hello", euro, snowman]), ) except TypeError: pytest.skip("apparently we can't pass unicode args to a program") lines = [line for line in rv.splitlines() if line] pid = lines.pop(0) pids = dict([r.strip().split(None, 1) for r in lines]) title = _clean_up_title(pids[pid]) assert title == "Hello, weird args!" def test_weird_path(tmp_path, spt_directory): """No problem with encoded argv[0] path.""" _check_4388() euro = "\u20ac" snowman = "\u2603" dir = tmp_path / euro / snowman try: os.makedirs(dir) except UnicodeEncodeError: pytest.skip("file system doesn't support unicode") exc = dir / os.path.basename(sys.executable) os.symlink(sys.executable, exc) rv = run_script( f""" import sys sys.path.insert(0, {repr(spt_directory)}) import setproctitle setproctitle.setproctitle("Hello, weird path!") import os print(os.getpid()) print(os.popen("ps -x -o pid,command 2> /dev/null").read()) """, args=" ".join(["-", "foo", "bar", "baz"]), executable=exc, ) lines = [line for line in rv.splitlines() if line] pid = lines.pop(0) pids = dict([r.strip().split(None, 1) for r in lines]) title = _clean_up_title(pids[pid]) assert title == "Hello, weird path!" @pytest.mark.embedded @skip_if_pypy @skip_if_macos @skip_if_no_proc_cmdline def test_embedded(pyrun, spt_directory): """Check the module works with embedded Python.""" rv = run_script( f""" import sys sys.path.insert(0, {spt_directory!r}) import setproctitle setproctitle.setproctitle("Hello, embedded!") import os print(os.getpid()) print(os.popen("ps -x -o pid,command 2> /dev/null").read()) """, executable=pyrun, ) lines = [line for line in rv.splitlines() if line] pid = lines.pop(0) pids = dict([r.strip().split(None, 1) for r in lines]) title = _clean_up_title(pids[pid]) assert title == "Hello, embedded!" @pytest.mark.embedded @skip_if_pypy @skip_if_macos @skip_if_no_proc_cmdline def test_embedded_many_args(pyrun, spt_directory): """Check more complex cmdlines are handled in embedded env too.""" rv = run_script( f""" import sys sys.path.insert(0, {spt_directory!r}) import setproctitle setproctitle.setproctitle("Hello, embedded!") import os print(os.getpid()) print(os.popen("ps -x -o pid,command 2> /dev/null").read()) """, executable=pyrun, args=" ".join(["foo", "bar", "baz"]), ) lines = [line for line in rv.splitlines() if line] pid = lines.pop(0) pids = dict([r.strip().split(None, 1) for r in lines]) title = _clean_up_title(pids[pid]) assert title == "Hello, embedded!" @skip_if_no_proc_env def test_noenv(): """Check that SPT_NOENV avoids clobbering environ.""" env = os.environ.copy() env["SPT_TESTENV"] = "testenv" rv = run_script( """ import os os.environ['SPT_NOENV'] = "1" cmdline_len = len(open('/proc/self/cmdline').read()) print(cmdline_len) print('SPT_TESTENV=testenv' in open('/proc/self/environ').read()) import setproctitle setproctitle.setproctitle('X' * cmdline_len * 10) title = open('/proc/self/cmdline').read().rstrip() print(title) print(len(title)) print('SPT_TESTENV=testenv' in open('/proc/self/environ').read()) """, env=env, ) lines = rv.splitlines() cmdline_len = int(lines[0]) assert lines[1] == "True", "can't verify testenv" title = lines[2] assert "XXX" in _clean_up_title(title), "title not set as expected" title_len = int(lines[3]) assert lines[4] == "True", "env has been clobbered" assert ( title_len <= cmdline_len ), "title (len {title_len}) not limited to argv (len {cmdline_len})" @skip_if_no_proc_env def test_large_env(monkeypatch): """Check that large environment doesn't get clobbered.""" monkeypatch.setenv("SPT_NOENV", "1") for c in string.ascii_uppercase: monkeypatch.setenv( f"{c}_TEST_ENV", "X" * (ord(c) - ord("A") + 1) * 1024 ) rv = run_script( r"""\ import sys with open("/proc/self/environ", "rb") as f: env1 = f.read() sys.stdout.buffer.write(env1) sys.stdout.buffer.write(b"\n-----8<-----\n") import setproctitle setproctitle.setproctitle("hello") with open("/proc/self/environ", "rb") as f: env2 = f.read() sys.stdout.buffer.write(env2) """ ) parts = rv.split("\n-----8<-----\n") for i, part in enumerate(parts): parts[i] = dict( var.split("=", 1) for var in part.split("\0") if "=" in var ) assert parts[0] == parts[1] def test_clear_segfault(): run_script( r"""\ import os from setproctitle import setproctitle os.environ.clear() setproctitle("Test") """ ) def test_fork_segfault(): run_script( """\ import multiprocessing as mp from setproctitle import setproctitle def foo(): setproctitle('title in child') setproctitle('title in parent') mp.set_start_method("fork") p = mp.Process(target=foo) p.start() p.join() assert p.exitcode == 0, f"p.exitcode is {p.exitcode}" """ ) def test_thread_fork_segfault(): run_script( """\ import multiprocessing as mp from threading import Thread from setproctitle import setproctitle def foo(): setproctitle("title in child") def thread(): global p p = mp.Process(target=foo) p.start() p.join() p = None mp.set_start_method("fork") t = Thread(target=thread) t.start() t.join() assert p.exitcode == 0, f"p.exitcode is {p.exitcode}" """ ) # Support functions def _clean_up_title(title): """Clean up a string from the prefix added by the platform.""" # BSD's setproctitle decorates the title with the process name. if "bsd" in sys.platform: procname = os.path.basename(sys.executable) title = " ".join([t for t in title.split(" ") if procname not in t]) return title def _check_4388(): """Check if the system is affected by bug #4388. If positive, unicode chars in the cmdline are not reliable, so bail out. see: http://bugs.python.org/issue4388 """ if sys.getfilesystemencoding() == "ascii": # in this case the char below would get translated in some # inconsistent way. # I'm not getting why the FS encoding is involved in process # spawning, the whole story just seems a gigantic can of worms. return p = sp.Popen([sys.executable, "-c", "ord('\xe9')"], stderr=sp.PIPE) p.communicate() if p.returncode: pytest.skip("bug #4388 detected") py-setproctitle-version-1.3.6/tests/setthreadtitle_test.py000066400000000000000000000030021500414041500241400ustar00rootroot00000000000000import os # noqa import sys # noqa from .conftest import run_script, skip_if_win32, skip_if_no_proc_tasks pytestmark = [skip_if_win32, skip_if_no_proc_tasks] def test_thread_title_unchanged(): rv = run_script( """ from glob import glob def print_stuff(): for fn in sorted(glob("/proc/self/task/*/comm")): with open(fn) as f: print(f.readline().rstrip()) print_stuff() print("---") import setproctitle print_stuff() print("---") print(setproctitle.getthreadtitle()) """ ) before, after, gtt = rv.split("---\n") assert before == after assert before == gtt def test_set_thread_title(): run_script( """ from glob import glob import setproctitle setproctitle.setthreadtitle("hello" * 10) (fn,) = glob("/proc/self/task/*/comm") with open(fn) as f: assert f.read().rstrip() == "hello" * 3 """ ) def test_set_threads_title(): run_script( """ import time import threading from glob import glob (fn,) = glob("/proc/self/task/*/comm") with open(fn) as f: orig = f.read().rstrip() import setproctitle def worker(title): setproctitle.setthreadtitle(title) while 1: time.sleep(1) t1 = threading.Thread(target=worker, args=('reader',), daemon=True) t2 = threading.Thread(target=worker, args=('writer',), daemon=True) t1.start() t2.start() comms = [] for fn in glob("/proc/self/task/*/comm"): with open(fn) as f: comms.append(f.read().rstrip()) comms.sort() assert comms == sorted([orig, "reader", "writer"]) """ ) py-setproctitle-version-1.3.6/tests/test_win32.py000066400000000000000000000010261500414041500220610ustar00rootroot00000000000000import pytest import setproctitle import sys # noqa skip_if_not_win32 = pytest.mark.skipif( "sys.platform != 'win32'", reason="Windows only test" ) pytestmark = [skip_if_not_win32] def test_setproctitle(): title = "setproctitle_test" setproctitle.setproctitle(title) assert title == setproctitle.getproctitle() def test_setthreadtitle(): title = "setproctitle_test" # This is currently a no-op on Windows. Let's make sure # that at least it doesn't error out. setproctitle.setthreadtitle(title) py-setproctitle-version-1.3.6/tools/000077500000000000000000000000001500414041500175055ustar00rootroot00000000000000py-setproctitle-version-1.3.6/tools/Makefile000066400000000000000000000011521500414041500211440ustar00rootroot00000000000000PTTEST_OBJS = pttest.o SPTDEMO_OBJS = spt_demo.o ../src/spt_status.o ../src/strlcpy.o PS_STATUS_C_URL = "http://git.postgresql.org/gitweb?p=postgresql.git;a=blob_plain;f=src/backend/utils/misc/ps_status.c;hb=HEAD" PS_STATUS_H_URL = "http://git.postgresql.org/gitweb?p=postgresql.git;a=blob_plain;f=src/include/utils/ps_status.h;hb=HEAD" CFLAGS = -g -Wall CURL = wget -O - all: pttest sptdemo prctl_demo pttest: $(PTTEST_OBJS) sptdemo: $(SPTDEMO_OBJS) $(CC) $(LDFLAGS) $^ -o $@ pgsources: ps_status.c ps_status.h ps_status.c : $(CURL) $(PS_STATUS_C_URL) > $@ ps_status.h : $(CURL) $(PS_STATUS_H_URL) > $@ py-setproctitle-version-1.3.6/tools/prctl_demo.c000066400000000000000000000014651500414041500220070ustar00rootroot00000000000000/* A test to check what happens using ``prctl()``. * * The ``prctl()`` call is available in Linux from 2.6.9. * * See http://www.kernel.org/doc/man-pages/online/pages/man2/prctl.2.html */ #include /* for prctl() */ #include /* for PR_SET_NAME */ #include #include int main(int argc, char **argv) { printf("Process PID: %i\n", getpid()); prctl(PR_SET_NAME, "Hello world"); printf("Title changed, press enter\n"); getchar(); /* The string set by prctl can be read in ``/proc/PID/stat`` * and ``/proc/PID/status``. It is displayed by ``ps`` but not by ``ps a`` * (which instead displays the content of ``/proc/PID/cmdline``). ``top`` * toggles between both visualizations pressing ``c``. */ return 0; } py-setproctitle-version-1.3.6/tools/pttest.c000066400000000000000000000010401500414041500211670ustar00rootroot00000000000000/* trying to understand why in order to clobber argv * postgres moves around environ too. */ #include extern char **environ; int main(int argc, char **argv) { char **p; printf("argv: %p\n", argv); printf("environ: %p\n", environ); for (p = argv; *p; ++p) { printf("argv[%i]: %p (%s)\n", p - argv, *p, *p); } for (p = environ; *p; ++p) { printf("environ[%i]: %p (%s)\n", p - environ, *p, *p); } /* My conclusion is that environ is contiguous to argv */ return 0; } py-setproctitle-version-1.3.6/tools/spt_demo.c000066400000000000000000000007411500414041500214650ustar00rootroot00000000000000/* A small demo to show how to use the display change */ #include "../src/spt_status.h" #include #include int main(int argc, char **argv) { printf("Process PID: %i\n", getpid()); argv = save_ps_display_args(argc, argv); init_ps_display("hello, world"); printf("Title changed, press enter\n"); getchar(); set_ps_display("new title!", true); printf("Title changed again, press enter to exit\n"); getchar(); return 0; } py-setproctitle-version-1.3.6/tox.ini000066400000000000000000000011301500414041500176530ustar00rootroot00000000000000[tox] envlist = 3.8, 3.9, 3.10, 3.11, 3.12, 3.13, 3.13t, pypy-3.8, pypy-3.9, pypy-3.10 [testenv] commands = pytest {posargs} extras = test [testenv:3.8] basepython = python3.8 [testenv:3.9] basepython = python3.9 [testenv:3.10] basepython = python3.10 [testenv:3.11] basepython = python3.11 [testenv:3.12] basepython = python3.12 [testenv:3.13] basepython = python3.13 [testenv:3.13t] basepython = python3.13t [testenv:pypy-3.8] basepython = pypy3.8 [testenv:pypy-3.9] basepython = pypy3.9 [testenv:pypy-3.10] basepython = pypy3.10 [testenv:xcode] basepython = {env:XCODE_PYTHON}