diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml new file mode 100644 index 00000000000000..29c28ba415d870 --- /dev/null +++ b/.github/workflows/mingw.yml @@ -0,0 +1,297 @@ +name: Build +on: [push, pull_request, workflow_dispatch] + +jobs: + build: + runs-on: windows-2022 + strategy: + fail-fast: false + matrix: + msystem: ['MINGW64','MINGW32','UCRT64','CLANG64'] + include: + - msystem: MINGW64 + prefix: mingw-w64-x86_64 + - msystem: MINGW32 + prefix: mingw-w64-i686 + - msystem: UCRT64 + prefix: mingw-w64-ucrt-x86_64 + - msystem: CLANG64 + prefix: mingw-w64-clang-x86_64 + steps: + - name: Setup git + run: | + git config --global core.autocrlf false + git config --global core.eol lf + - uses: actions/checkout@v3 + - uses: msys2/setup-msys2@v2 + with: + msystem: ${{ matrix.msystem }} + release: false + update: true + install: >- + make + binutils + autoconf + autoconf-archive + automake-wrapper + tar + gzip + ${{ matrix.prefix }}-toolchain + ${{ matrix.prefix }}-expat + ${{ matrix.prefix }}-bzip2 + ${{ matrix.prefix }}-libffi + ${{ matrix.prefix }}-mpdecimal + ${{ matrix.prefix }}-ncurses + ${{ matrix.prefix }}-openssl + ${{ matrix.prefix }}-sqlite3 + ${{ matrix.prefix }}-tcl + ${{ matrix.prefix }}-tk + ${{ matrix.prefix }}-zlib + ${{ matrix.prefix }}-xz + ${{ matrix.prefix }}-tzdata + + - name: Build Python + shell: msys2 {0} + run: | + set -ex + + if [[ "${{ matrix.msystem }}" == CLANG* ]]; then + export CC=clang + export CXX=clang++ + fi + autoreconf -vfi + + rm -Rf _build && mkdir _build && cd _build + + ../configure \ + --prefix=${MINGW_PREFIX} \ + --host=${MINGW_CHOST} \ + --build=${MINGW_CHOST} \ + --enable-shared \ + --with-system-expat \ + --with-system-libmpdec \ + --without-ensurepip \ + --enable-loadable-sqlite-extensions \ + --with-tzpath=${MINGW_PREFIX}/share/zoneinfo \ + --enable-optimizations + + make -j8 + + - name: Run Smoke Test (build) + shell: msys2 {0} + run: | + SMOKETESTS="$(pwd)/mingw_smoketests.py" + cd _build + ./python.exe "$SMOKETESTS" + MSYSTEM= ./python.exe "$SMOKETESTS" + + - name: Run tests + shell: msys2 {0} + run: | + IGNOREFILE="$(pwd)/mingw_ignorefile.txt" + IGNOREFILE_EXTRA="$IGNOREFILE" + if [[ "${{ matrix.msystem }}" == "MINGW32" ]] || [[ "${{ matrix.msystem }}" == "MINGW64" ]]; then + IGNOREFILE_EXTRA="$(pwd)/mingw_ignorefile_msvcrt.txt" + fi + cd _build + MSYSTEM= ./python.exe -m test -j8 --ignorefile "$IGNOREFILE" --ignorefile "$IGNOREFILE_EXTRA" -W + + - name: Run broken tests + continue-on-error: true + shell: msys2 {0} + run: | + IGNOREFILE="$(pwd)/mingw_ignorefile.txt" + IGNOREFILE_EXTRA="$IGNOREFILE" + if [[ "${{ matrix.msystem }}" == "MINGW32" ]] || [[ "${{ matrix.msystem }}" == "MINGW64" ]]; then + IGNOREFILE_EXTRA="$(pwd)/mingw_ignorefile_msvcrt.txt" + fi + cd _build + MSYSTEM= ./python.exe -m test -j8 --matchfile "$IGNOREFILE" --matchfile "$IGNOREFILE_EXTRA" -W + + - name: Install + shell: msys2 {0} + run: | + set -ex + cd _build + + pkgdir=python_pkgdir + + make -j1 install DESTDIR="${pkgdir}" + + # Fix shebangs + _pybasever=$(./python.exe -c "import sys; print(f'{sys.version_info[0]}.{sys.version_info[1]}');") + for fscripts in 2to3 2to3-${_pybasever} idle3 idle${_pybasever} pydoc3 pydoc${_pybasever}; do + sed -i "s|$(cygpath -w ${MINGW_PREFIX} | sed 's|\\|\\\\|g')/bin/python${_pybasever}.exe|/usr/bin/env python${_pybasever}.exe|g" "${pkgdir}${MINGW_PREFIX}"/bin/${fscripts} + done + sed -i "s|#!${MINGW_PREFIX}/bin/python${_pybasever}.exe|#!/usr/bin/env python${_pybasever}.exe|" "${pkgdir}${MINGW_PREFIX}"/lib/python${_pybasever}/config-${_pybasever}/python-config.py + + # Create version-less aliases + cp "${pkgdir}${MINGW_PREFIX}"/bin/python3.exe "${pkgdir}${MINGW_PREFIX}"/bin/python.exe + cp "${pkgdir}${MINGW_PREFIX}"/bin/python3w.exe "${pkgdir}${MINGW_PREFIX}"/bin/pythonw.exe + cp "${pkgdir}${MINGW_PREFIX}"/bin/python3-config "${pkgdir}${MINGW_PREFIX}"/bin/python-config + cp "${pkgdir}${MINGW_PREFIX}"/bin/idle3 "${pkgdir}${MINGW_PREFIX}"/bin/idle + cp "${pkgdir}${MINGW_PREFIX}"/bin/pydoc3 "${pkgdir}${MINGW_PREFIX}"/bin/pydoc + + # copy to / + cp -rf "${pkgdir}"/* / + + - name: Run Smoke Test (installed) + shell: msys2 {0} + run: | + SMOKETESTS="$(pwd)/mingw_smoketests.py" + ${MINGW_PREFIX}/bin/python.exe "$SMOKETESTS" + MSYSTEM= ${MINGW_PREFIX}/bin/python.exe "$SMOKETESTS" + + - name: Compress + if: always() + shell: msys2 {0} + run: | + cd _build + tar -zcf python.tar.gz python_pkgdir/ + + - name: Upload + uses: actions/upload-artifact@v3 + if: always() + with: + name: build-${{ matrix.msystem }} + path: _build/python.tar.gz + + cross-gcc-x86_64: + runs-on: ubuntu-latest + container: + image: ubuntu:24.10 + steps: + - uses: actions/checkout@v3 + - name: Install deps + run: | + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + gcc-mingw-w64-x86-64 \ + g++-mingw-w64-x86-64 \ + autoconf-archive \ + autoconf \ + automake \ + zip \ + make \ + pkg-config + + - uses: actions/setup-python@v4 + with: + python-version: '3.13' + + - name: Check Python Version + run: | + which python + python --version + + - name: Build + run: | + autoreconf -vfi + + mkdir _build && cd _build + + ../configure \ + --host=x86_64-w64-mingw32 \ + --build=x86_64-pc-linux-gnu \ + --enable-shared \ + --without-ensurepip \ + --enable-loadable-sqlite-extensions \ + --with-build-python=yes + + make -j8 + + make install DESTDIR="$(pwd)/install" + + - name: 'Zip files' + run: | + zip -r install.zip _build/install + + - name: Upload + uses: actions/upload-artifact@v3 + with: + name: build-cross-gcc-x86_64 + path: install.zip + + cross-gcc-x86_64-test: + needs: [cross-gcc-x86_64] + runs-on: windows-latest + steps: + - uses: actions/download-artifact@v3 + with: + name: build-cross-gcc-x86_64 + + - name: 'Run tests' + run: | + 7z x install.zip + ./_build/install/usr/local/bin/python3.exe -c "import sysconfig, pprint; pprint.pprint(sysconfig.get_config_vars())" + + + cross-llvm-mingw: + runs-on: ubuntu-latest + container: + image: mstorsjo/llvm-mingw:latest + strategy: + fail-fast: false + matrix: + arch: ['x86_64', 'i686', 'aarch64', 'armv7'] + steps: + - uses: actions/checkout@v3 + + - name: Install deps + run: | + export DEBIAN_FRONTEND=noninteractive + apt-get update -qq + apt-get install -qqy software-properties-common + add-apt-repository --yes ppa:deadsnakes/ppa + apt-get update -qq + apt-get install -qqy autoconf-archive python3.13-dev python3.13 + + - name: Build + run: | + autoreconf -vfi + + mkdir _build && cd _build + + export CC=${{ matrix.arch }}-w64-mingw32-clang + export CXX=${CC}++ + ../configure \ + --host=${{ matrix.arch }}-w64-mingw32 \ + --build=x86_64-pc-linux-gnu \ + --enable-shared \ + --without-ensurepip \ + --without-c-locale-coercion \ + --enable-loadable-sqlite-extensions \ + --with-build-python=yes + + make -j8 + + make install DESTDIR="$(pwd)/install" + + - name: 'Zip files' + run: | + zip -r install.zip _build/install + + - name: Upload + uses: actions/upload-artifact@v3 + with: + name: build-cross-llvm-mingw-${{ matrix.arch }} + path: install.zip + + cross-llvm-mingw-test: + needs: [cross-llvm-mingw] + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + arch: ['x86_64', 'i686'] + steps: + - uses: actions/download-artifact@v3 + with: + name: build-cross-llvm-mingw-${{ matrix.arch }} + + - name: 'Run tests' + run: | + 7z x install.zip + ./_build/install/usr/local/bin/python3.exe -c "import sysconfig, pprint; pprint.pprint(sysconfig.get_config_vars())" + + diff --git a/Include/bytesobject.h b/Include/bytesobject.h index c5a24195be6bc3..cc7e52973702bf 100644 --- a/Include/bytesobject.h +++ b/Include/bytesobject.h @@ -32,9 +32,9 @@ PyAPI_FUNC(PyObject *) PyBytes_FromStringAndSize(const char *, Py_ssize_t); PyAPI_FUNC(PyObject *) PyBytes_FromString(const char *); PyAPI_FUNC(PyObject *) PyBytes_FromObject(PyObject *); PyAPI_FUNC(PyObject *) PyBytes_FromFormatV(const char*, va_list) - Py_GCC_ATTRIBUTE((format(printf, 1, 0))); + Py_PRINTF(1, 0); PyAPI_FUNC(PyObject *) PyBytes_FromFormat(const char*, ...) - Py_GCC_ATTRIBUTE((format(printf, 1, 2))); + Py_PRINTF(1, 2); PyAPI_FUNC(Py_ssize_t) PyBytes_Size(PyObject *); PyAPI_FUNC(char *) PyBytes_AsString(PyObject *); PyAPI_FUNC(PyObject *) PyBytes_Repr(PyObject *, int); diff --git a/Include/exports.h b/Include/exports.h index ce601216f17156..f3aa722574ffc4 100644 --- a/Include/exports.h +++ b/Include/exports.h @@ -15,12 +15,12 @@ */ /* - All windows ports, except cygwin, are handled in PC/pyconfig.h. + Only MSVC windows port is handled in PC/pyconfig.h. - Cygwin is the only other autoconf platform requiring special + Cygwin and Mingw is the other autoconf platforms requiring special linkage handling and it uses __declspec(). */ -#if defined(__CYGWIN__) +#if defined(__CYGWIN__) || defined(__MINGW32__) # define HAVE_DECLSPEC_DLL #endif @@ -63,21 +63,23 @@ # define PyAPI_FUNC(RTYPE) Py_EXPORTED_SYMBOL RTYPE # define PyAPI_DATA(RTYPE) extern Py_EXPORTED_SYMBOL RTYPE /* module init functions inside the core need no external linkage */ - /* except for Cygwin to handle embedding */ -# if defined(__CYGWIN__) + /* except for Cygwin/Mingw to handle embedding */ +# if defined(__CYGWIN__) || defined(__MINGW32__) # define PyMODINIT_FUNC Py_EXPORTED_SYMBOL PyObject* -# else /* __CYGWIN__ */ +# else /* __CYGWIN__ || __MINGW32__ */ # define PyMODINIT_FUNC PyObject* -# endif /* __CYGWIN__ */ +# endif /* __CYGWIN__ || __MINGW32__ */ # else /* Py_BUILD_CORE */ /* Building an extension module, or an embedded situation */ /* public Python functions and data are imported */ /* Under Cygwin, auto-import functions to prevent compilation */ /* failures similar to those described at the bottom of 4.1: */ /* http://docs.python.org/extending/windows.html#a-cookbook-approach */ -# if !defined(__CYGWIN__) +# if defined(__CYGWIN__) || defined(__MINGW32__) +# define PyAPI_FUNC(RTYPE) RTYPE +# else # define PyAPI_FUNC(RTYPE) Py_IMPORTED_SYMBOL RTYPE -# endif /* !__CYGWIN__ */ +# endif /* __CYGWIN__ || __MINGW32__ */ # define PyAPI_DATA(RTYPE) extern Py_IMPORTED_SYMBOL RTYPE /* module init functions outside the core must be exported */ # if defined(__cplusplus) diff --git a/Include/internal/pycore_condvar.h b/Include/internal/pycore_condvar.h index ee9533484e8048..9b38ee6af463e6 100644 --- a/Include/internal/pycore_condvar.h +++ b/Include/internal/pycore_condvar.h @@ -34,6 +34,11 @@ #define WIN32_LEAN_AND_MEAN #include // CRITICAL_SECTION +/* winpthreads are involved via windows header, so need undef _POSIX_THREADS after header include */ +#if defined(_POSIX_THREADS) +# undef _POSIX_THREADS +#endif + /* options */ /* emulated condition variables are provided for those that want * to target Windows XP or earlier. Modify this macro to enable them. diff --git a/Include/internal/pycore_fileutils.h b/Include/internal/pycore_fileutils.h index 13f86b01bbfe8f..51386b0454aad5 100644 --- a/Include/internal/pycore_fileutils.h +++ b/Include/internal/pycore_fileutils.h @@ -12,7 +12,7 @@ extern "C" { /* A routine to check if a file descriptor can be select()-ed. */ -#ifdef _MSC_VER +#ifdef MS_WINDOWS /* On Windows, any socket fd can be select()-ed, no matter how high */ #define _PyIsSelectable_fd(FD) (1) #else diff --git a/Include/internal/pycore_pythread.h b/Include/internal/pycore_pythread.h index 3610c6254db6af..f9f1aa29b52ea1 100644 --- a/Include/internal/pycore_pythread.h +++ b/Include/internal/pycore_pythread.h @@ -11,6 +11,12 @@ extern "C" { #include "dynamic_annotations.h" // _Py_ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX #include "pycore_llist.h" // struct llist_node +#ifdef __MINGW32__ +# if !defined(HAVE_PTHREAD_H) || defined(NT_THREADS) +# undef _POSIX_THREADS +# endif +#endif + // Get _POSIX_THREADS and _POSIX_SEMAPHORES macros if available #if (defined(HAVE_UNISTD_H) && !defined(_POSIX_THREADS) \ && !defined(_POSIX_SEMAPHORES)) diff --git a/Include/iscygpty.h b/Include/iscygpty.h new file mode 100644 index 00000000000000..82fd0affbd4875 --- /dev/null +++ b/Include/iscygpty.h @@ -0,0 +1,41 @@ +/* + * iscygpty.h -- part of ptycheck + * https://github.com/k-takata/ptycheck + * + * Copyright (c) 2015-2017 K.Takata + * + * You can redistribute it and/or modify it under the terms of either + * the MIT license (as described below) or the Vim license. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _ISCYGPTY_H +#define _ISCYGPTY_H + +#ifdef _WIN32 +int is_cygpty(int fd); +int is_cygpty_used(void); +#else +#define is_cygpty(fd) 0 +#define is_cygpty_used() 0 +#endif + +#endif /* _ISCYGPTY_H */ diff --git a/Include/osdefs.h b/Include/osdefs.h index 2599e87a9d7c4b..8c0682702748dd 100644 --- a/Include/osdefs.h +++ b/Include/osdefs.h @@ -16,7 +16,6 @@ extern "C" { #ifdef MS_WINDOWS # define SEP L'\\' # define ALTSEP L'/' -# define MAXPATHLEN 256 # define DELIM L';' #endif diff --git a/Include/py_curses.h b/Include/py_curses.h index 79b1b01fcfa594..794116fff246c6 100644 --- a/Include/py_curses.h +++ b/Include/py_curses.h @@ -50,6 +50,13 @@ # include #endif +#if defined(__MINGW32__) +# include +# if !defined(_ISPAD) +# define _ISPAD 0x10 +# endif +#endif + #ifdef NCURSES_VERSION /* configure was checking , but we will use , which has some or all these features. */ diff --git a/Include/pyerrors.h b/Include/pyerrors.h index 5d0028c116e2d8..cae6979447d33c 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -319,9 +319,9 @@ PyAPI_FUNC(int) PyUnicodeTranslateError_SetReason( ); PyAPI_FUNC(int) PyOS_snprintf(char *str, size_t size, const char *format, ...) - Py_GCC_ATTRIBUTE((format(printf, 3, 4))); + Py_PRINTF(3, 4); PyAPI_FUNC(int) PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) - Py_GCC_ATTRIBUTE((format(printf, 3, 0))); + Py_PRINTF(3, 0); #ifndef Py_LIMITED_API # define Py_CPYTHON_ERRORS_H diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h index de1bcb1d2cb632..29632398332887 100644 --- a/Include/pylifecycle.h +++ b/Include/pylifecycle.h @@ -21,6 +21,15 @@ PyAPI_FUNC(int) Py_IsInitialized(void); PyAPI_FUNC(PyThreadState *) Py_NewInterpreter(void); PyAPI_FUNC(void) Py_EndInterpreter(PyThreadState *); +PyAPI_FUNC(wchar_t) Py_GetAltSepW(const wchar_t *); +PyAPI_FUNC(wchar_t) Py_GetSepW(const wchar_t *); +PyAPI_FUNC(char) Py_GetSepA(const char *); + +PyAPI_FUNC(void) Py_NormalizeSepsW(wchar_t *); +PyAPI_FUNC(void) Py_NormalizeSepsA(char *); + +PyAPI_FUNC(void) Py_NormalizeSepsPathcchW(wchar_t *); + /* Py_PyAtExit is for the atexit module, Py_AtExit is for low-level * exit functions. diff --git a/Include/pyport.h b/Include/pyport.h index 2ba81a4be42822..a50347d097f189 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -49,6 +49,44 @@ #endif +#ifdef __MINGW32__ +/* Translate GCC[mingw*] platform specific defines to those + * used in python code. + */ +#if !defined(MS_WIN64) && defined(_WIN64) +# define MS_WIN64 +#endif +#if !defined(MS_WIN32) && defined(_WIN32) +# define MS_WIN32 +#endif +#if !defined(MS_WINDOWS) && defined(MS_WIN32) +# define MS_WINDOWS +#endif + +#if defined(Py_BUILD_CORE) || defined(Py_BUILD_CORE_BUILTIN) || defined(Py_BUILD_CORE_MODULE) +#include + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define MS_WINDOWS_DESKTOP +#endif +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) +#define MS_WINDOWS_APP +#endif +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_SYSTEM) +#define MS_WINDOWS_SYSTEM +#endif +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_GAMES) +#define MS_WINDOWS_GAMES +#endif + +/* Define to 1 if you support windows console io */ +#if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_APP) || defined(MS_WINDOWS_SYSTEM) +#define HAVE_WINDOWS_CONSOLE_IO 1 +#endif +#endif /* Py_BUILD_CORE || Py_BUILD_CORE_BUILTIN || Py_BUILD_CORE_MODULE */ + +#endif /* __MINGW32__*/ + /************************************************************************** Symbols and macros to supply platform-independent interfaces to basic C language & library operations whose spellings vary across platforms. @@ -439,6 +477,12 @@ extern "C" { #define Py_VA_COPY va_copy +#if defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__>= 4) || __GNUC__ > 4) +# define Py_PRINTF(X,Y) Py_GCC_ATTRIBUTE((format(gnu_printf,X,Y))) +#else +# define Py_PRINTF(X,Y) Py_GCC_ATTRIBUTE((format(printf,X,Y))) +#endif + /* * Convenient macros to deal with endianness of the platform. WORDS_BIGENDIAN is * detected by configure and defined in pyconfig.h. The code in pyconfig.h diff --git a/Include/pythread.h b/Include/pythread.h index a3216c51d66165..9b4d2aefdd6a47 100644 --- a/Include/pythread.h +++ b/Include/pythread.h @@ -7,6 +7,12 @@ typedef void *PyThread_type_lock; extern "C" { #endif +#ifdef __MINGW32__ +# if !defined(HAVE_PTHREAD_H) || defined(NT_THREADS) +# undef _POSIX_THREADS +# endif +#endif + /* Return status codes for Python lock acquisition. Chosen for maximum * backwards compatibility, ie failure -> 0, success -> 1. */ typedef enum PyLockStatus { diff --git a/Include/sysmodule.h b/Include/sysmodule.h index 5a0af2e1578eb7..259677a69dddb5 100644 --- a/Include/sysmodule.h +++ b/Include/sysmodule.h @@ -11,9 +11,9 @@ Py_DEPRECATED(3.11) PyAPI_FUNC(void) PySys_SetArgv(int, wchar_t **); Py_DEPRECATED(3.11) PyAPI_FUNC(void) PySys_SetArgvEx(int, wchar_t **, int); PyAPI_FUNC(void) PySys_WriteStdout(const char *format, ...) - Py_GCC_ATTRIBUTE((format(printf, 1, 2))); + Py_PRINTF(1, 2); PyAPI_FUNC(void) PySys_WriteStderr(const char *format, ...) - Py_GCC_ATTRIBUTE((format(printf, 1, 2))); + Py_PRINTF(1, 2); PyAPI_FUNC(void) PySys_FormatStdout(const char *format, ...); PyAPI_FUNC(void) PySys_FormatStderr(const char *format, ...); diff --git a/Lib/compileall.py b/Lib/compileall.py index 47e2446356e7d7..8e02d02c2fca0f 100644 --- a/Lib/compileall.py +++ b/Lib/compileall.py @@ -38,6 +38,8 @@ def _walk_dir(dir, maxlevels, quiet=0): if name == '__pycache__': continue fullname = os.path.join(dir, name) + if sys.platform == "win32" and sys.version.find("GCC") >= 0: + fullname = fullname.replace('\\','/') if not os.path.isdir(fullname): yield fullname elif (maxlevels > 0 and name != os.curdir and name != os.pardir and diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 8261773cef9830..a17c9e13d96ccb 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -475,7 +475,9 @@ def LoadLibrary(self, name): cdll = LibraryLoader(CDLL) pydll = LibraryLoader(PyDLL) -if _os.name == "nt": +if _os.name == "nt" and _sys.version.find('GCC') >= 0: + pythonapi = PyDLL("libpython%d.%d%s.dll" % (_sys.version_info[:2] + (_sys.abiflags,)), None) +elif _os.name == "nt": pythonapi = PyDLL("python dll", None, _sys.dllhandle) elif _sys.platform == "android": pythonapi = PyDLL("libpython%d.%d.so" % _sys.version_info[:2]) diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index 117bf06cb01013..a89907399e8d62 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -31,6 +31,12 @@ def _get_build_version(): # else we don't know what version of the compiler this is return None + def find_msvcrt_mingw(): + is_ucrt = 'clang' in sys.version.lower() or 'ucrt' in sys.version.lower() + if is_ucrt: + return None + return 'msvcrt.dll' + def find_msvcrt(): """Return the name of the VC runtime dll""" version = _get_build_version() @@ -54,6 +60,9 @@ def find_msvcrt(): def find_library(name): if name in ('c', 'm'): + import sysconfig + if sysconfig.get_platform().startswith('mingw'): + return find_msvcrt_mingw() return find_msvcrt() # See MSDN for the REAL search order. for directory in os.environ['PATH'].split(os.pathsep): diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index eb8fea1aad0919..520025e7aa2d11 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -42,6 +42,10 @@ path_separators = ['\\', '/'] else: path_separators = ['/'] + +if _os.environ.get('MSYSTEM', ''): + path_separators = path_separators[::-1] + # Assumption made in _path_join() assert all(len(sep) == 1 for sep in path_separators) path_sep = path_separators[0] diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 5481bb8888ef59..added25ebd4360 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -11,9 +11,7 @@ curdir = '.' pardir = '..' extsep = '.' -sep = '\\' pathsep = ';' -altsep = '/' defpath = '.;C:\\bin' devnull = 'nul' @@ -22,6 +20,15 @@ import genericpath from genericpath import * +if sys.platform == "win32" and os.environ.get("MSYSTEM", ""): + sep = '/' + altsep = '\\' +else: + sep = '\\' + altsep = '/' +bsep = str.encode(sep) +baltsep = str.encode(altsep) + __all__ = ["normcase","isabs","join","splitdrive","splitroot","split","splitext", "basename","dirname","commonprefix","getsize","getmtime", "getatime","getctime", "islink","exists","lexists","isdir","isfile", @@ -33,9 +40,39 @@ def _get_bothseps(path): if isinstance(path, bytes): - return b'\\/' + return bsep + baltsep + else: + return sep + altsep + +def _get_sep(path): + if isinstance(path, bytes): + return bsep + else: + return sep + +def _get_altsep(path): + if isinstance(path, bytes): + return baltsep else: - return '\\/' + return altsep + +def _get_colon(path): + if isinstance(path, bytes): + return b':' + else: + return ':' + +def _get_empty(path): + if isinstance(path, bytes): + return b'' + else: + return '' + +def _get_unc_prefix(path): + if isinstance(path, bytes): + return b'\\\\?\\UNC\\' + else: + return '\\\\?\\UNC\\' # Normalize the case of a pathname and map slashes to backslashes. # Other normalizations (such as optimizing '../' away) are not done @@ -57,14 +94,14 @@ def normcase(s): return s if isinstance(s, bytes): encoding = sys.getfilesystemencoding() - s = s.decode(encoding, 'surrogateescape').replace('/', '\\') + s = s.decode(encoding, 'surrogateescape').replace(altsep, sep) s = _LCMapStringEx(_LOCALE_NAME_INVARIANT, _LCMAP_LOWERCASE, s) return s.encode(encoding, 'surrogateescape') else: return _LCMapStringEx(_LOCALE_NAME_INVARIANT, _LCMAP_LOWERCASE, - s.replace('/', '\\')) + s.replace(altsep, sep)) except ImportError: def normcase(s): """Normalize case of pathname. @@ -73,23 +110,17 @@ def normcase(s): """ s = os.fspath(s) if isinstance(s, bytes): - return os.fsencode(os.fsdecode(s).replace('/', '\\').lower()) - return s.replace('/', '\\').lower() + return os.fsencode(os.fsdecode(s).replace(altsep, sep).lower()) + return s.replace(altsep, sep).lower() def isabs(s): """Test whether a path is absolute""" s = os.fspath(s) - if isinstance(s, bytes): - sep = b'\\' - altsep = b'/' - colon_sep = b':\\' - double_sep = b'\\\\' - else: - sep = '\\' - altsep = '/' - colon_sep = ':\\' - double_sep = '\\\\' + sep = _get_sep(s) + altsep = _get_altsep(s) + colon_sep = _get_colon(s) + sep + double_sep = sep + sep s = s[:3].replace(altsep, sep) # Absolute: UNC, device, and paths with a drive and root. return s.startswith(colon_sep, 1) or s.startswith(double_sep) @@ -98,14 +129,9 @@ def isabs(s): # Join two (or more) paths. def join(path, *paths): path = os.fspath(path) - if isinstance(path, bytes): - sep = b'\\' - seps = b'\\/' - colon_seps = b':\\/' - else: - sep = '\\' - seps = '\\/' - colon_seps = ':\\/' + sep = _get_sep(path) + seps = _get_bothseps(path) + colon_seps = _get_colon(path) + seps try: result_drive, result_root, result_path = splitroot(path) for p in paths: @@ -174,18 +200,12 @@ def splitroot(p): The tail contains anything after the root.""" p = os.fspath(p) - if isinstance(p, bytes): - sep = b'\\' - altsep = b'/' - colon = b':' - unc_prefix = b'\\\\?\\UNC\\' - empty = b'' - else: - sep = '\\' - altsep = '/' - colon = ':' - unc_prefix = '\\\\?\\UNC\\' - empty = '' + sep = _get_sep(p) + altsep = _get_altsep(p) + colon = _get_colon(p) + unc_prefix = _get_unc_prefix(p) + empty = _get_empty(p) + normp = p.replace(altsep, sep) if normp[:1] == sep: if normp[1:2] == sep: @@ -243,9 +263,9 @@ def split(p): def splitext(p): p = os.fspath(p) if isinstance(p, bytes): - return genericpath._splitext(p, b'\\', b'/', b'.') + return genericpath._splitext(p, bsep, baltsep, b'.') else: - return genericpath._splitext(p, '\\', '/', '.') + return genericpath._splitext(p, sep, altsep, '.') splitext.__doc__ = genericpath._splitext.__doc__ @@ -360,7 +380,7 @@ def expanduser(path): if 'USERPROFILE' in os.environ: userhome = os.environ['USERPROFILE'] elif 'HOMEPATH' not in os.environ: - return path + return os.path.normpath(path) else: drive = os.environ.get('HOMEDRIVE', '') userhome = join(drive, os.environ['HOMEPATH']) @@ -384,7 +404,7 @@ def expanduser(path): if isinstance(path, bytes): userhome = os.fsencode(userhome) - return userhome + path[i:] + return os.path.normpath(userhome) + path[i:] # Expand paths containing shell variable substitutions. @@ -519,14 +539,12 @@ def expandvars(path): def normpath(path): """Normalize path, eliminating double slashes, etc.""" path = os.fspath(path) + sep = _get_sep(path) + altsep = _get_altsep(path) if isinstance(path, bytes): - sep = b'\\' - altsep = b'/' curdir = b'.' pardir = b'..' else: - sep = '\\' - altsep = '/' curdir = '.' pardir = '..' path = path.replace(altsep, sep) @@ -573,7 +591,7 @@ def abspath(path): def abspath(path): """Return the absolute version of a path.""" try: - return _getfullpathname(normpath(path)) + return normpath(_getfullpathname(normpath(path))) except (OSError, ValueError): # See gh-75230, handle outside for cleaner traceback pass @@ -761,6 +779,7 @@ def realpath(path, *, strict=False): # strip the prefix anyway. if ex.winerror == initial_winerror: path = spath + path = normpath(path) return path @@ -772,13 +791,11 @@ def relpath(path, start=None): path = os.fspath(path) if not path: raise ValueError("no path specified") - + sep = _get_sep(path) if isinstance(path, bytes): - sep = b'\\' curdir = b'.' pardir = b'..' else: - sep = '\\' curdir = '.' pardir = '..' @@ -829,14 +846,11 @@ def commonpath(paths): paths = tuple(map(os.fspath, paths)) if not paths: raise ValueError('commonpath() arg is an empty iterable') - + sep = _get_sep(paths[0]) + altsep = _get_altsep(paths[0]) if isinstance(paths[0], bytes): - sep = b'\\' - altsep = b'/' curdir = b'.' else: - sep = '\\' - altsep = '/' curdir = '.' try: diff --git a/Lib/site.py b/Lib/site.py index 89a81c55cb63dd..c216d485b8f872 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -89,6 +89,12 @@ USER_BASE = None +# Same as defined in Lib/sysconfig.py +# redeclared since sysconfig is large for site. +# GCC[mingw*] use posix build system +_POSIX_BUILD = os.name == 'posix' or \ + (os.name == "nt" and 'GCC' in sys.version) + def _trace(message): if sys.flags.verbose: print(message, file=sys.stderr) @@ -298,7 +304,7 @@ def _getuserbase(): def joinuser(*args): return os.path.expanduser(os.path.join(*args)) - if os.name == "nt": + if os.name == "nt" and not _POSIX_BUILD: base = os.environ.get("APPDATA") or "~" return joinuser(base, _get_implementation()) @@ -308,6 +314,32 @@ def joinuser(*args): return joinuser("~", ".local") +# Copy of sysconfig.get_platform() but only for MinGW +def _get_platform(): + if os.name == 'nt': + if 'gcc' in sys.version.lower(): + platform = 'mingw' + if 'amd64' in sys.version.lower(): + platform += '_x86_64' + elif 'arm64' in sys.version.lower(): + platform += '_aarch64' + elif 'arm' in sys.version.lower(): + platform += '_armv7' + else: + platform += '_i686' + + if 'ucrt' in sys.version.lower(): + platform += '_ucrt' + else: + platform += "_msvcrt" + + if 'clang' in sys.version.lower(): + platform += "_llvm" + else: + platform += "_gnu" + + return platform + return sys.platform # Same to sysconfig.get_path('purelib', os.name+'_user') def _get_path(userbase): @@ -320,8 +352,10 @@ def _get_path(userbase): implementation = _get_implementation() implementation_lower = implementation.lower() if os.name == 'nt': - ver_nodot = sys.winver.replace('.', '') - return f'{userbase}\\{implementation}{ver_nodot}\\site-packages' + if not _POSIX_BUILD: + ver_nodot = sys.winver.replace('.', '') + return f'{userbase}\\{implementation}{ver_nodot}\\site-packages' + return f'{userbase}/lib/{implementation_lower}{version[0]}.{version[1]}-{_get_platform()}{abi_thread}/site-packages' if sys.platform == 'darwin' and sys._framework: return f'{userbase}/lib/{implementation_lower}/site-packages' @@ -398,7 +432,7 @@ def getsitepackages(prefixes=None): abi_thread = 't' else: abi_thread = '' - if os.sep == '/': + if _POSIX_BUILD: libdirs = [sys.platlibdir] if sys.platlibdir != "lib": libdirs.append("lib") @@ -429,7 +463,7 @@ def setquit(): The repr of each object contains a hint at how it works. """ - if os.sep == '\\': + if sys.platform == 'win32': eof = 'Ctrl-Z plus Return' else: eof = 'Ctrl-D (i.e. EOF)' diff --git a/Lib/ssl.py b/Lib/ssl.py index c8703b046cfd4b..67119b220ef9f1 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -254,7 +254,7 @@ class _TLSMessageType: CHANGE_CIPHER_SPEC = 0x0101 -if sys.platform == "win32": +if sys.platform == "win32" and sys.version.find("GCC") == -1: from _ssl import enum_certificates, enum_crls from socket import socket, SOCK_STREAM, create_connection @@ -528,7 +528,7 @@ def _load_windows_store_certs(self, storename, purpose): def load_default_certs(self, purpose=Purpose.SERVER_AUTH): if not isinstance(purpose, _ASN1Object): raise TypeError(purpose) - if sys.platform == "win32": + if sys.platform == "win32" and sys.version.find("GCC") == -1: for storename in self._windows_cert_stores: self._load_windows_store_certs(storename, purpose) self.set_default_verify_paths() diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index ec3b638f00766d..19ca3bfd7fbc5e 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -49,13 +49,13 @@ 'data': '{base}', }, 'nt': { - 'stdlib': '{installed_base}/Lib', - 'platstdlib': '{base}/Lib', - 'purelib': '{base}/Lib/site-packages', - 'platlib': '{base}/Lib/site-packages', - 'include': '{installed_base}/Include', - 'platinclude': '{installed_base}/Include', - 'scripts': '{base}/Scripts', + 'stdlib': '{installed_base}/lib/{implementation_lower}{py_version_short}{abi_thread}', + 'platstdlib': '{base}/lib/{implementation_lower}{py_version_short}{abi_thread}', + 'purelib': '{base}/lib/{implementation_lower}{py_version_short}{abi_thread}/site-packages', + 'platlib': '{base}/lib/{implementation_lower}{py_version_short}{abi_thread}/site-packages', + 'include': '{installed_base}/include/{implementation_lower}{py_version_short}{abiflags}', + 'platinclude': '{installed_base}/include/{implementation_lower}{py_version_short}{abiflags}', + 'scripts': '{base}/bin', 'data': '{base}', }, @@ -100,8 +100,12 @@ }, } +# GCC[mingw*] use posix build system +_POSIX_BUILD = os.name == 'posix' or \ + (os.name == "nt" and 'GCC' in sys.version) + # For the OS-native venv scheme, we essentially provide an alias: -if os.name == 'nt': +if os.name == 'nt' and not _POSIX_BUILD: _INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['nt_venv'] else: _INSTALL_SCHEMES['venv'] = _INSTALL_SCHEMES['posix_venv'] @@ -109,6 +113,7 @@ def _get_implementation(): return 'Python' + # NOTE: site.py has copy of this function. # Sync it when modify this function. def _getuserbase(): @@ -123,7 +128,7 @@ def _getuserbase(): def joinuser(*args): return os.path.expanduser(os.path.join(*args)) - if os.name == "nt": + if os.name == "nt" and not _POSIX_BUILD: base = os.environ.get("APPDATA") or "~" return joinuser(base, _get_implementation()) @@ -139,20 +144,20 @@ def joinuser(*args): _INSTALL_SCHEMES |= { # NOTE: When modifying "purelib" scheme, update site._get_path() too. 'nt_user': { - 'stdlib': '{userbase}/{implementation}{py_version_nodot_plat}', - 'platstdlib': '{userbase}/{implementation}{py_version_nodot_plat}', - 'purelib': '{userbase}/{implementation}{py_version_nodot_plat}/site-packages', - 'platlib': '{userbase}/{implementation}{py_version_nodot_plat}/site-packages', - 'include': '{userbase}/{implementation}{py_version_nodot_plat}/Include', - 'scripts': '{userbase}/{implementation}{py_version_nodot_plat}/Scripts', + 'stdlib': '{userbase}/lib/{implementation}{py_version_short_plat}{abi_thread}', + 'platstdlib': '{userbase}/lib/{implementation}{py_version_short_plat}{abi_thread}', + 'purelib': '{userbase}/lib/{implementation}{py_version_short_plat}{abi_thread}/site-packages', + 'platlib': '{userbase}/lib/{implementation}{py_version_short_plat}{abi_thread}/site-packages', + 'include': '{userbase}/include/{implementation}{py_version_short_plat}{abi_thread}', + 'scripts': '{userbase}/bin', 'data': '{userbase}', }, 'posix_user': { - 'stdlib': '{userbase}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}', - 'platstdlib': '{userbase}/{platlibdir}/{implementation_lower}{py_version_short}{abi_thread}', - 'purelib': '{userbase}/lib/{implementation_lower}{py_version_short}{abi_thread}/site-packages', - 'platlib': '{userbase}/lib/{implementation_lower}{py_version_short}{abi_thread}/site-packages', - 'include': '{userbase}/include/{implementation_lower}{py_version_short}{abi_thread}', + 'stdlib': '{userbase}/{platlibdir}/{implementation_lower}{py_version_short_plat}{abi_thread}', + 'platstdlib': '{userbase}/{platlibdir}/{implementation_lower}{py_version_short_plat}{abi_thread}', + 'purelib': '{userbase}/lib/{implementation_lower}{py_version_short_plat}{abi_thread}/site-packages', + 'platlib': '{userbase}/lib/{implementation_lower}{py_version_short_plat}{abi_thread}/site-packages', + 'include': '{userbase}/include/{implementation_lower}{py_version_short_plat}{abi_thread}', 'scripts': '{userbase}/bin', 'data': '{userbase}', }, @@ -278,7 +283,7 @@ def _expand_vars(scheme, vars): def _get_preferred_schemes(): - if os.name == 'nt': + if os.name == 'nt' and not _POSIX_BUILD: return { 'prefix': 'nt', 'home': 'posix_home', @@ -373,7 +378,7 @@ def _init_non_posix(vars): vars['LIBRARY'] = os.path.basename(_safe_realpath(dllhandle)) vars['LDLIBRARY'] = vars['LIBRARY'] vars['EXE'] = '.exe' - vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT + vars['VERSION'] = _PY_VERSION_SHORT vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable)) vars['TZPATH'] = '' @@ -419,7 +424,7 @@ def parse_config_h(fp, vars=None): def get_config_h_filename(): """Return the path of pyconfig.h.""" if _PYTHON_BUILD: - if os.name == "nt": + if os.name == "nt" and not _POSIX_BUILD: inc_dir = os.path.dirname(sys._base_executable) else: inc_dir = _PROJECT_BASE @@ -488,11 +493,15 @@ def _init_config_vars(): _CONFIG_VARS['py_version_nodot_plat'] = sys.winver.replace('.', '') except AttributeError: _CONFIG_VARS['py_version_nodot_plat'] = '' + if os.name == 'nt' and _POSIX_BUILD: + _CONFIG_VARS['py_version_short_plat'] = f'{_PY_VERSION_SHORT}-{get_platform()}' + else: + _CONFIG_VARS['py_version_short_plat'] = _PY_VERSION_SHORT - if os.name == 'nt': + if os.name == 'nt' and not _POSIX_BUILD: _init_non_posix(_CONFIG_VARS) _CONFIG_VARS['VPATH'] = sys._vpath - if os.name == 'posix': + if _POSIX_BUILD: _init_posix(_CONFIG_VARS) if _HAS_USER_BASE: # Setting 'userbase' is done below the call to the @@ -505,7 +514,7 @@ def _init_config_vars(): # Always convert srcdir to an absolute path srcdir = _CONFIG_VARS.get('srcdir', _PROJECT_BASE) - if os.name == 'posix': + if _POSIX_BUILD: if _PYTHON_BUILD: # If srcdir is a relative path (typically '.' or '..') # then it should be interpreted relative to the directory @@ -578,7 +587,7 @@ def get_config_var(name): """ return get_config_vars().get(name) - +# make sure to change site._get_platform() while changing this function def get_platform(): """Return a string that identifies the current platform. @@ -601,6 +610,28 @@ def get_platform(): """ if os.name == 'nt': + if 'gcc' in sys.version.lower(): + platform = 'mingw' + if 'amd64' in sys.version.lower(): + platform += '_x86_64' + elif 'arm64' in sys.version.lower(): + platform += '_aarch64' + elif 'arm' in sys.version.lower(): + platform += '_armv7' + else: + platform += '_i686' + + if 'ucrt' in sys.version.lower(): + platform += '_ucrt' + else: + platform += "_msvcrt" + + if 'clang' in sys.version.lower(): + platform += "_llvm" + else: + platform += "_gnu" + + return platform if 'amd64' in sys.version.lower(): return 'win-amd64' if '(arm)' in sys.version.lower(): diff --git a/Lib/sysconfig/__main__.py b/Lib/sysconfig/__main__.py index d7257b9d2d00db..dad8f903c7df49 100644 --- a/Lib/sysconfig/__main__.py +++ b/Lib/sysconfig/__main__.py @@ -1,5 +1,6 @@ import os import sys +import textwrap from sysconfig import ( _ALWAYS_STR, _PYTHON_BUILD, @@ -145,6 +146,14 @@ def _parse_makefile(filename, vars=None, keep_unresolved=True): if isinstance(v, str): done[k] = v.strip() + # any keys that have one with the same name suffixed with _b2h + # need to be replaced with the value of the _b2h key. + # This converts from MSYS*/Cygwin paths to Windows paths. + for k, v in dict(done).items(): + if isinstance(k, str): + if k.endswith("_b2h"): + done[k[:-4]]=v + # save the results in the global dictionary vars.update(done) return vars @@ -209,11 +218,30 @@ def _generate_posix_vars(): os.makedirs(pybuilddir, exist_ok=True) destfile = os.path.join(pybuilddir, name + '.py') + replacement = """ + keys_to_replace = [ + 'BINDIR', 'BINLIBDEST', 'CONFINCLUDEDIR', + 'CONFINCLUDEPY', 'DESTDIRS', 'DESTLIB', 'DESTSHARED', + 'INCLDIRSTOMAKE', 'INCLUDEDIR', 'INCLUDEPY', + 'LIBDEST', 'LIBDIR', 'LIBPC', 'LIBPL', 'MACHDESTLIB', + 'MANDIR', 'SCRIPTDIR', 'datarootdir', 'exec_prefix', + 'TZPATH', + ] + + prefix = build_time_vars['BINDIR'][:-4] + + for key in keys_to_replace: + value = build_time_vars[key] + build_time_vars[key] = value.replace(prefix, sys.base_prefix) + """ + with open(destfile, 'w', encoding='utf8') as f: + f.write('import sys\n') f.write('# system configuration generated and used by' ' the sysconfig module\n') f.write('build_time_vars = ') _print_config_dict(vars, stream=f) + f.write('\n%s' % textwrap.dedent(replacement)) # Create file used for sys.path fixup -- see Modules/getpath.c with open('pybuilddir.txt', 'w', encoding='utf8') as f: diff --git a/Lib/test/__main__.py b/Lib/test/__main__.py index 82b50ad2c6e777..25bac39e24af20 100644 --- a/Lib/test/__main__.py +++ b/Lib/test/__main__.py @@ -1,2 +1,10 @@ +import os +import sys + from test.libregrtest.main import main + +if sys.platform == "win32": + # Enable DLL loading from PATH. + os.environ["PYTHONLEGACYWINDOWSDLLLOADING"] = "1" + main(_add_python_opts=True) diff --git a/Lib/test/test_bytes.py b/Lib/test/test_bytes.py index 9e1985bb3a7639..492ab350116f89 100644 --- a/Lib/test/test_bytes.py +++ b/Lib/test/test_bytes.py @@ -1136,7 +1136,7 @@ def test_from_format(self): if os.name == 'nt': # Windows (MSCRT) - ptr_format = '0x%0{}X'.format(2 * sizeof_ptr) + ptr_format = '0x%0{}x'.format(2 * sizeof_ptr) def ptr_formatter(ptr): return (ptr_format % ptr) else: diff --git a/Lib/test/test_getpath.py b/Lib/test/test_getpath.py index d5dcdad9614ecc..f43877b59aa541 100644 --- a/Lib/test/test_getpath.py +++ b/Lib/test/test_getpath.py @@ -852,6 +852,7 @@ def test_explicitly_set_stdlib_dir(self): ENV_PYTHONHOME="", ENV_PYTHONEXECUTABLE="", ENV___PYVENV_LAUNCHER__="", + ENV_MSYSTEM="", argv0="", py_setpath="", real_executable="", @@ -891,6 +892,7 @@ def __init__(self, *a, argv0=None, config=None, **kw): self.update(DEFAULT_NAMESPACE) self["config"] = DEFAULT_CONFIG.copy() self["os_name"] = "nt" + self["is_mingw"] = 0 self["PLATLIBDIR"] = "DLLs" self["PYWINVER"] = "9.8-XY" self["VPATH"] = r"..\.." @@ -926,6 +928,9 @@ def __missing__(self, key): except AttributeError: raise KeyError(key) from None + def normpath(self, path): + return ntpath.normpath(path) + def abspath(self, path): if self.isabs(path): return path @@ -1067,6 +1072,7 @@ def __init__(self, *a, argv0=None, config=None, **kw): self.update(DEFAULT_NAMESPACE) self["config"] = DEFAULT_CONFIG.copy() self["os_name"] = "posix" + self["is_mingw"] = 0 self["PLATLIBDIR"] = "lib" self["WITH_NEXT_FRAMEWORK"] = 0 super().__init__(*a, **kw) @@ -1103,6 +1109,9 @@ def __missing__(self, key): except AttributeError: raise KeyError(key) from None + def normpath(self, path): + return path + def abspath(self, path): if self.isabs(path): return path diff --git a/Lib/test/test_importlib/test_windows.py b/Lib/test/test_importlib/test_windows.py index 8a9a8fffcd10d4..03be17b0c4c087 100644 --- a/Lib/test/test_importlib/test_windows.py +++ b/Lib/test/test_importlib/test_windows.py @@ -24,6 +24,29 @@ def get_platform(): 'x64' : 'win-amd64', 'arm' : 'win-arm32', } + if os.name == 'nt': + if "gcc" in sys.version.lower(): + platform = "mingw" + if "amd64" in sys.version.lower(): + platform += "_x86_64" + elif "arm64" in sys.version.lower(): + platform += "_aarch64" + elif "arm" in sys.version.lower(): + platform += "_armv7" + else: + platform += "_i686" + + if "ucrt" in sys.version.lower(): + platform += "_ucrt" + else: + platform += "_msvcrt" + + if "clang" in sys.version.lower(): + platform += "_llvm" + else: + platform += "_gnu" + + return platform if ('VSCMD_ARG_TGT_ARCH' in os.environ and os.environ['VSCMD_ARG_TGT_ARCH'] in TARGET_TO_PLAT): return TARGET_TO_PLAT[os.environ['VSCMD_ARG_TGT_ARCH']] diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py index bf534130212482..4a28a8ef115717 100644 --- a/Lib/test/test_sysconfig.py +++ b/Lib/test/test_sysconfig.py @@ -25,7 +25,7 @@ from sysconfig import (get_paths, get_platform, get_config_vars, get_path, get_path_names, _INSTALL_SCHEMES, get_default_scheme, get_scheme_names, get_config_var, - _expand_vars, _get_preferred_schemes) + _expand_vars, _get_preferred_schemes, _POSIX_BUILD) from sysconfig.__main__ import _main, _parse_makefile import _imp import _osx_support @@ -208,7 +208,7 @@ def test_nt_venv_scheme(self): self.assertEqual(libpath, sysconfig.get_path('purelib', scheme='nt_venv', vars=vars)) def test_venv_scheme(self): - if sys.platform == 'win32': + if not _POSIX_BUILD and sys.platform == 'win32': self.assertEqual( sysconfig.get_path('scripts', scheme='venv'), sysconfig.get_path('scripts', scheme='nt_venv') @@ -421,6 +421,10 @@ def test_user_similar(self): if HAS_USER_BASE: user_path = get_path(name, 'posix_user') expected = os.path.normpath(global_path.replace(base, user, 1)) + if os.name == 'nt' and _POSIX_BUILD: + expected = expected.replace( + f'python{sysconfig.get_python_version()}', + f'python{sysconfig.get_python_version()}-{get_platform()}') # bpo-44860: platlib of posix_user doesn't use sys.platlibdir, # whereas posix_prefix does. if name == 'platlib': diff --git a/Lib/test/test_tools/test_makefile.py b/Lib/test/test_tools/test_makefile.py index 4c7588d4d93fc3..4d54e6e91aca18 100644 --- a/Lib/test/test_tools/test_makefile.py +++ b/Lib/test/test_tools/test_makefile.py @@ -33,12 +33,13 @@ def list_test_dirs(self): if '\t' not in line: break result.append(line.replace('\\', '').strip()) + result = [os.path.normpath(d) for d in result if d] return result @unittest.skipUnless(support.TEST_MODULES_ENABLED, "requires test modules") def test_makefile_test_folders(self): test_dirs = self.list_test_dirs() - idle_test = 'idlelib/idle_test' + idle_test = os.path.join('idlelib', 'idle_test') self.assertIn(idle_test, test_dirs) used = set([idle_test]) diff --git a/Lib/test/test_wmi.py b/Lib/test/test_wmi.py index f667926d1f8ddf..f1490a361a343e 100644 --- a/Lib/test/test_wmi.py +++ b/Lib/test/test_wmi.py @@ -7,7 +7,7 @@ # Do this first so test will be skipped if module doesn't exist -_wmi = import_helper.import_module('_wmi', required_on=['win']) +_wmi = import_helper.import_module('_wmi') def wmi_exec_query(query): diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index f7a6d2614018c5..92c22c49001ecd 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -13,6 +13,7 @@ import types import shlex +from sysconfig import _POSIX_BUILD CORE_VENV_DEPS = ('pip',) logger = logging.getLogger(__name__) @@ -377,7 +378,7 @@ def setup_python(self, context): } do_copies = True - if self.symlinks: + if self.symlinks and not _POSIX_BUILD: do_copies = False # For symlinking, we need all the DLLs to be available alongside # the executables. @@ -413,6 +414,12 @@ def setup_python(self, context): except OSError: logger.warning('Unable to copy %r to %r', src, dest) + if _POSIX_BUILD: + # copy from python/pythonw so the venvlauncher magic in symlink_or_copy triggers + shutil.copy2(os.path.join(dirname, 'python.exe'), os.path.join(binpath, 'python3.exe')) + shutil.copy2(os.path.join(dirname, 'python.exe'), os.path.join(binpath, 'python%d.%d.exe' % sys.version_info[:2])) + shutil.copy2(os.path.join(dirname, 'pythonw.exe'), os.path.join(binpath, 'python3w.exe')) + if sysconfig.is_python_build(): # copy init.tcl for root, dirs, files in os.walk(context.python_dir): @@ -437,6 +444,7 @@ def _call_new_python(self, context, *py_args, **kwargs): env['VIRTUAL_ENV'] = context.env_dir env.pop('PYTHONHOME', None) env.pop('PYTHONPATH', None) + env.pop("MSYSTEM", None) kwargs['cwd'] = context.env_dir kwargs['executable'] = context.env_exec_cmd subprocess.check_output(args, **kwargs) diff --git a/Makefile.pre.in b/Makefile.pre.in index 03ca4cb635bd38..30d17142bd2086 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -38,6 +38,7 @@ CC= @CC@ CXX= @CXX@ LINKCC= @LINKCC@ AR= @AR@ +WINDRES= @WINDRES@ READELF= @READELF@ SOABI= @SOABI@ ABIFLAGS= @ABIFLAGS@ @@ -126,6 +127,7 @@ PY_CORE_CFLAGS= $(PY_STDMODULE_CFLAGS) -DPy_BUILD_CORE PY_CORE_LDFLAGS=$(PY_LDFLAGS) $(PY_LDFLAGS_NODIST) # Strict or non-strict aliasing flags used to compile dtoa.c, see above CFLAGS_ALIASING=@CFLAGS_ALIASING@ +RCFLAGS=@RCFLAGS@ # Machine-dependent subdirectories @@ -144,6 +146,12 @@ exec_prefix= @exec_prefix@ # Install prefix for data files datarootdir= @datarootdir@ +# Locations needed for semi-native fixup of sysconfig. +srcdir_b2h= @srcdir_b2h@ +abs_srcdir_b2h= @abs_srcdir_b2h@ +abs_builddir_b2h= @abs_builddir_b2h@ +prefix_b2h= @prefix_b2h@ + # Expanded directories BINDIR= @bindir@ LIBDIR= @libdir@ @@ -162,10 +170,12 @@ BINLIBDEST= @BINLIBDEST@ LIBDEST= $(SCRIPTDIR)/python$(VERSION)$(ABI_THREAD) INCLUDEPY= $(INCLUDEDIR)/python$(LDVERSION) CONFINCLUDEPY= $(CONFINCLUDEDIR)/python$(LDVERSION) +VENVLAUNCHERDIR= $(BINLIBDEST)/venv/scripts/nt # Symbols used for using shared libraries SHLIB_SUFFIX= @SHLIB_SUFFIX@ EXT_SUFFIX= @EXT_SUFFIX@ +PYD_PLATFORM_TAG = @PYD_PLATFORM_TAG@ LDSHARED= @LDSHARED@ $(PY_LDFLAGS) BLDSHARED= @BLDSHARED@ $(PY_CORE_LDFLAGS) LDCXXSHARED= @LDCXXSHARED@ $(PY_LDFLAGS) @@ -278,6 +288,8 @@ LIBRARY_DEPS= @LIBRARY_DEPS@ LINK_PYTHON_DEPS=@LINK_PYTHON_DEPS@ PY_ENABLE_SHARED= @PY_ENABLE_SHARED@ STATIC_LIBPYTHON= @STATIC_LIBPYTHON@ +ABI3DLLLIBRARY= libpython3.dll +ABI3LDLIBRARY= libpython3.dll.a LIBS= @LIBS@ @@ -294,6 +306,9 @@ LIBOBJS= @LIBOBJS@ PYTHON= python$(EXE) BUILDPYTHON= python$(BUILDEXE) +BUILDPYTHONW= pythonw$(BUILDEXE) +BUILDVENVLAUNCHER= venvlauncher$(BUILDEXE) +BUILDVENVWLAUNCHER= venvwlauncher$(BUILDEXE) HOSTRUNNER= @HOSTRUNNER@ @@ -431,7 +446,7 @@ PYTHON_OBJS= \ Python/errors.o \ Python/flowgraph.o \ Python/frame.o \ - Python/frozenmain.o \ + @PYTHON_OBJS_FROZENMAIN@ \ Python/future.o \ Python/gc.o \ Python/gc_free_threading.o \ @@ -451,6 +466,7 @@ PYTHON_OBJS= \ Python/instrumentation.o \ Python/instruction_sequence.o \ Python/intrinsics.o \ + Python/iscygpty.o \ Python/jit.o \ Python/legacy_tracing.o \ Python/lock.o \ @@ -695,8 +711,8 @@ list-targets: @grep -E '^[A-Za-z][-A-Za-z0-9]+:' Makefile | awk -F : '{print $$1}' .PHONY: build_all -build_all: check-clean-src check-app-store-compliance $(BUILDPYTHON) platform sharedmods \ - gdbhooks Programs/_testembed scripts checksharedmods rundsymutil +build_all: check-clean-src check-app-store-compliance $(BUILDPYTHON) $(BUILDPYTHONW) $(BUILDVENVLAUNCHER) $(BUILDVENVWLAUNCHER) platform sharedmods \ + gdbhooks Programs/_testembed scripts checksharedmods rundsymutil $(ABI3DLLLIBRARY) $(ABI3LDLIBRARY) .PHONY: build_wasm build_wasm: check-clean-src $(BUILDPYTHON) platform sharedmods \ @@ -752,7 +768,7 @@ profile-run-stamp: $(MAKE) profile-gen-stamp # Next, run the profile task to generate the profile information. @ # FIXME: can't run for a cross build - $(LLVM_PROF_FILE) $(RUNSHARED) ./$(BUILDPYTHON) $(PROFILE_TASK) + $(LLVM_PROF_FILE) $(RUNSHARED) ./$(BUILDPYTHON) $(PROFILE_TASK) || true $(LLVM_PROF_MERGER) # Remove profile generation binary since we are done with it. $(MAKE) clean-retain-profile @@ -801,7 +817,7 @@ profile-bolt-stamp: $(BUILDPYTHON) mv "$${bin}.bolt_inst" "$${bin}"; \ done # Run instrumented binaries to collect data. - $(RUNSHARED) ./$(BUILDPYTHON) $(PROFILE_TASK) + $(RUNSHARED) ./$(BUILDPYTHON) $(PROFILE_TASK) || true # Merge all the data files together. for bin in $(BOLT_BINARIES); do \ @MERGE_FDATA@ $${bin}.*.fdata > "$${bin}.fdata"; \ @@ -876,9 +892,36 @@ clinic: check-clean-src $(srcdir)/Modules/_blake2/blake2s_impl.c clinic-tests: check-clean-src $(srcdir)/Lib/test/clinic.test.c $(PYTHON_FOR_REGEN) $(srcdir)/Tools/clinic/clinic.py -f $(srcdir)/Lib/test/clinic.test.c +python_exe.o: $(srcdir)/PC/python_exe.rc + $(WINDRES) $(RCFLAGS) -I$(srcdir)/Include -I$(srcdir)/PC -I. $(srcdir)/PC/python_exe.rc $@ + +pythonw_exe.o: $(srcdir)/PC/pythonw_exe.rc + $(WINDRES) $(RCFLAGS) -I$(srcdir)/Include -I$(srcdir)/PC -I. $(srcdir)/PC/pythonw_exe.rc $@ + +python_nt.o: $(srcdir)/PC/python_nt.rc + $(WINDRES) $(RCFLAGS) -DORIGINAL_FILENAME=\\\"$(DLLLIBRARY)\\\" -I$(srcdir)/Include -I$(srcdir)/PC -I. $(srcdir)/PC/python_nt.rc $@ + +python3dll_nt.o: $(srcdir)/PC/python_nt.rc + $(WINDRES) $(RCFLAGS) -DORIGINAL_FILENAME=\\\"$(ABI3DLLLIBRARY)\\\" -I$(srcdir)/Include -I$(srcdir)/PC -I. $(srcdir)/PC/python_nt.rc $@ + +venvlauncher.o: $(srcdir)/PC/pylauncher.rc + $(WINDRES) $(RCFLAGS) -DPY_ICON -I$(srcdir)/Include -I$(srcdir)/PC -I. $(srcdir)/PC/pylauncher.rc $@ + +venvwlauncher.o: $(srcdir)/PC/pylauncher.rc + $(WINDRES) $(RCFLAGS) -DPYW_ICON -I$(srcdir)/Include -I$(srcdir)/PC -I. $(srcdir)/PC/pylauncher.rc $@ + +$(BUILDPYTHONW): Programs/python.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) pythonw_exe.o + $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -municode -mwindows -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) pythonw_exe.o + # Build the interpreter -$(BUILDPYTHON): Programs/python.o $(LINK_PYTHON_DEPS) - $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS) +$(BUILDPYTHON): Programs/python.o $(LINK_PYTHON_DEPS) python_exe.o + $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -municode -o $@ Programs/python.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS) python_exe.o + +$(BUILDVENVLAUNCHER): $(BUILDPYTHON) venvlauncher.o $(srcdir)/PC/launcher.c + $(LINKCC) -D_CONSOLE -DVENV_REDIRECT -DPYTHON_EXECUTABLE_WITH_VERSION="L\"python$(LDVERSION)$(EXE)\"" $(PY_STDMODULE_CFLAGS) -municode -static -static-libgcc -static-libstdc++ venvlauncher.o $(srcdir)/PC/launcher.c -o $@ -lversion + +$(BUILDVENVWLAUNCHER): $(BUILDPYTHONW) venvwlauncher.o $(srcdir)/PC/launcher.c + $(LINKCC) -D_WINDOWS -DVENV_REDIRECT -DPYTHON_EXECUTABLE_WITH_VERSION="L\"pythonw$(LDVERSION)$(EXE)\"" $(PY_STDMODULE_CFLAGS) -mwindows -municode -static -static-libgcc -static-libstdc++ venvwlauncher.o $(srcdir)/PC/launcher.c -o $@ -lversion platform: $(PYTHON_FOR_BUILD_DEPS) pybuilddir.txt $(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import sys ; from sysconfig import get_platform ; print("%s-%d.%d" % (get_platform(), *sys.version_info[:2]))' >platform @@ -979,13 +1022,17 @@ $(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK): \ # This rule builds the Cygwin Python DLL and import library if configured # for a shared core library; otherwise, this rule is a noop. -$(DLLLIBRARY) libpython$(LDVERSION).dll.a: $(LIBRARY_OBJS) +$(DLLLIBRARY) libpython$(LDVERSION).dll.a: $(LIBRARY_OBJS) python_nt.o if test -n "$(DLLLIBRARY)"; then \ $(LDSHARED) -Wl,--out-implib=$@ -o $(DLLLIBRARY) $^ \ - $(LIBS) $(MODLIBS) $(SYSLIBS); \ + $(LIBS) $(LDFLAGS_NODIST) $(MODLIBS) $(SYSLIBS) python_nt.o; \ else true; \ fi +$(ABI3DLLLIBRARY) $(ABI3LDLIBRARY): python3dll_nt.o $(srcdir)/PC/launcher.c + $(LDSHARED) -DPYTHON_DLL_NAME=\"$(DLLLIBRARY)\" $(srcdir)/PC/python3dll.c -Wl,--out-implib=$(ABI3LDLIBRARY) -o $(ABI3DLLLIBRARY) python3dll_nt.o \ + $(LDFLAGS_NODIST); + # wasm32-emscripten browser build # wasm assets directory is relative to current build dir, e.g. "./usr/local". # --preload-file turns a relative asset path into an absolute path. @@ -1032,6 +1079,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/genericaliasobject.h \ $(srcdir)/Include/import.h \ $(srcdir)/Include/intrcheck.h \ + $(srcdir)/Include/iscygpty.h \ $(srcdir)/Include/iterobject.h \ $(srcdir)/Include/listobject.h \ $(srcdir)/Include/lock.h \ @@ -1445,7 +1493,7 @@ BOOTSTRAP_HEADERS = \ Programs/_bootstrap_python.o: Programs/_bootstrap_python.c $(BOOTSTRAP_HEADERS) $(PYTHON_HEADERS) _bootstrap_python: $(LIBRARY_OBJS_OMIT_FROZEN) Programs/_bootstrap_python.o Modules/getpath.o Modules/Setup.local - $(LINKCC) $(PY_LDFLAGS_NOLTO) -o $@ $(LIBRARY_OBJS_OMIT_FROZEN) \ + $(LINKCC) $(PY_LDFLAGS_NOLTO) -o $@ -municode -mwindows $(LIBRARY_OBJS_OMIT_FROZEN) \ Programs/_bootstrap_python.o Modules/getpath.o $(LIBS) $(MODLIBS) $(SYSLIBS) @@ -1678,7 +1726,7 @@ Modules/getbuildinfo.o: $(PARSER_OBJS) \ $(MODOBJS) \ $(DTRACE_OBJS) \ $(srcdir)/Modules/getbuildinfo.c - $(CC) -c $(PY_CORE_CFLAGS) \ + $(CC) -c $(PY_BUILTIN_MODULE_CFLAGS) \ -DGITVERSION="\"`LC_ALL=C $(GITVERSION)`\"" \ -DGITTAG="\"`LC_ALL=C $(GITTAG)`\"" \ -DGITBRANCH="\"`LC_ALL=C $(GITBRANCH)`\"" \ @@ -1732,9 +1780,15 @@ Python/dynload_hpux.o: $(srcdir)/Python/dynload_hpux.c Makefile -DSHLIB_EXT='"$(EXT_SUFFIX)"' \ -o $@ $(srcdir)/Python/dynload_hpux.c +Python/dynload_win.o: $(srcdir)/Python/dynload_win.c Makefile + $(CC) -c $(PY_CORE_CFLAGS) \ + -DPYD_PLATFORM_TAG='"$(PYD_PLATFORM_TAG)"' \ + -o $@ $(srcdir)/Python/dynload_win.c + Python/sysmodule.o: $(srcdir)/Python/sysmodule.c Makefile $(srcdir)/Include/pydtrace.h $(CC) -c $(PY_CORE_CFLAGS) \ -DABIFLAGS='"$(ABIFLAGS)"' \ + -DVPATH='"$(VPATH)"' \ $(MULTIARCH_CPPFLAGS) \ -o $@ $(srcdir)/Python/sysmodule.c @@ -2197,7 +2251,7 @@ sharedinstall: all # This goes into $(exec_prefix) .PHONY: altbininstall altbininstall: $(BUILDPYTHON) @FRAMEWORKPYTHONW@ - @for i in $(BINDIR) $(LIBDIR); \ + @for i in $(BINDIR) $(LIBDIR) $(VENVLAUNCHERDIR); \ do \ if test ! -d $(DESTDIR)$$i; then \ echo "Creating directory $$i"; \ @@ -2207,6 +2261,9 @@ altbininstall: $(BUILDPYTHON) @FRAMEWORKPYTHONW@ done if test "$(PYTHONFRAMEWORKDIR)" = "no-framework" ; then \ $(INSTALL_PROGRAM) $(BUILDPYTHON) $(DESTDIR)$(BINDIR)/python$(LDVERSION)$(EXE); \ + $(INSTALL_PROGRAM) $(BUILDPYTHONW) $(DESTDIR)$(BINDIR)/python3w$(EXE); \ + $(INSTALL_PROGRAM) $(BUILDVENVLAUNCHER) $(DESTDIR)$(VENVLAUNCHERDIR)/python$(EXE); \ + $(INSTALL_PROGRAM) $(BUILDVENVWLAUNCHER) $(DESTDIR)$(VENVLAUNCHERDIR)/pythonw$(EXE); \ else \ $(INSTALL_PROGRAM) $(STRIPFLAG) Mac/pythonw $(DESTDIR)$(BINDIR)/python$(LDVERSION)$(EXE); \ fi @@ -2220,6 +2277,7 @@ altbininstall: $(BUILDPYTHON) @FRAMEWORKPYTHONW@ if test -f $(LDLIBRARY) && test "$(PYTHONFRAMEWORKDIR)" = "no-framework" ; then \ if test -n "$(DLLLIBRARY)" ; then \ $(INSTALL_SHARED) $(DLLLIBRARY) $(DESTDIR)$(BINDIR); \ + $(INSTALL_SHARED) $(ABI3DLLLIBRARY) $(DESTDIR)$(BINDIR); \ else \ $(INSTALL_SHARED) $(LDLIBRARY) $(DESTDIR)$(LIBDIR)/$(INSTSONAME); \ if test $(LDLIBRARY) != $(INSTSONAME); then \ @@ -2701,8 +2759,9 @@ libainstall: all scripts @if test "$(STATIC_LIBPYTHON)" = 1; then \ if test -d $(LIBRARY); then :; else \ if test "$(PYTHONFRAMEWORKDIR)" = no-framework; then \ - if test "$(SHLIB_SUFFIX)" = .dll; then \ - $(INSTALL_DATA) $(LDLIBRARY) $(DESTDIR)$(LIBPL) ; \ + if test "$(SHLIB_SUFFIX)" = .dll -o "$(SHLIB_SUFFIX)" = .pyd; then \ + $(INSTALL_DATA) $(LDLIBRARY) $(DESTDIR)$(LIBDIR) ; \ + $(INSTALL_DATA) $(ABI3LDLIBRARY) $(DESTDIR)$(LIBDIR) ; \ else \ $(INSTALL_DATA) $(LIBRARY) $(DESTDIR)$(LIBPL)/$(LIBRARY) ; \ fi; \ @@ -3011,7 +3070,7 @@ clean: clean-retain-profile clean-bolt .PHONY: clobber clobber: clean - -rm -f $(BUILDPYTHON) $(LIBRARY) $(LDLIBRARY) $(DLLLIBRARY) \ + -rm -f $(BUILDPYTHON) $(LIBRARY) $(LDLIBRARY) $(DLLLIBRARY)$(ABI3LDLIBRARY) $(ABI3DLLLIBRARY) \ tags TAGS \ config.cache config.log pyconfig.h Modules/config.c -rm -rf build platform diff --git a/Misc/config_mingw b/Misc/config_mingw new file mode 100644 index 00000000000000..9be43fd3a11407 --- /dev/null +++ b/Misc/config_mingw @@ -0,0 +1,15 @@ +# configure defaults for mingw* hosts + +# mingw functions to ignore +ac_cv_func_ftruncate=ignore # implement it as _chsize + +# mingw-w64 functions to ignore +ac_cv_func_truncate=ignore +ac_cv_func_alarm=ignore + +# files to ignore +ac_cv_file__dev_ptmx=ignore #NOTE: under MSYS environment device exist +ac_cv_file__dev_ptc=no + +# force detection of winsock2 functionality - require wxp or newer +ac_cv_func_getpeername=yes diff --git a/Misc/cross_mingw32 b/Misc/cross_mingw32 new file mode 100644 index 00000000000000..03fde9e6af3ad7 --- /dev/null +++ b/Misc/cross_mingw32 @@ -0,0 +1,11 @@ +# configure defaults for mingw32 host if cross-build + +ac_cv_little_endian_double=yes +ac_cv_big_endian_double=no +ac_cv_mixed_endian_double=no + +ac_cv_tanh_preserves_zero_sign=yes + +ac_cv_wchar_t_signed=no + +ac_cv_have_size_t_format=no diff --git a/Misc/python-config.sh.in b/Misc/python-config.sh.in index 555b0cb6ba2a48..1eb78afaba463a 100644 --- a/Misc/python-config.sh.in +++ b/Misc/python-config.sh.in @@ -1,7 +1,5 @@ #!/bin/sh -# Keep this script in sync with python-config.in - exit_with_usage () { local usage @@ -11,28 +9,42 @@ exit_with_usage () else echo "$usage" >&2 fi - exit $1 + exit 1 } +# Really, python-config.py (and thus .sh) should be called directly, but +# sometimes software (e.g. GDB) calls python-config.sh as if it were the +# Python executable, passing python-config.py as the first argument. +# Work around that oddness by ignoring any .py passed as first arg. +case "$1" in + *.py) + shift + ;; +esac + if [ "$1" = "" ] ; then - exit_with_usage 1 + exit_with_usage fi # Returns the actual prefix where this script was installed to. installed_prefix () { - RESULT=$(dirname $(cd $(dirname "$1") && pwd -P)) - if which readlink >/dev/null 2>&1 ; then - if readlink -f "$RESULT" >/dev/null 2>&1; then - RESULT=$(readlink -f "$RESULT") - fi + local RESULT=$(dirname $(cd $(dirname "$1") && pwd -P)) + if [ $(which readlink) ] ; then + RESULT=$(readlink -f "$RESULT") + fi + # Since we don't know where the output from this script will end up + # we keep all paths in Windows-land since MSYS2 can handle that + # while native tools can't handle paths in MSYS2-land. + if [ "$OSTYPE" = "msys" ]; then + RESULT=$(cd "$RESULT" && pwd -W) fi echo $RESULT } prefix_real=$(installed_prefix "$0") -# Use sed to fix paths from their built-to locations to their installed-to +# Use sed to fix paths from their built-to locations to their installed to # locations. Keep prefix & exec_prefix using their original values in case # they are referenced in other configure variables, to prevent double # substitution, issue #22140. @@ -47,13 +59,17 @@ LIBM="@LIBM@" LIBC="@LIBC@" SYSLIBS="$LIBM $LIBC" ABIFLAGS="@ABIFLAGS@" +# Protect against lack of substitution. +if [ "$ABIFLAGS" = "@""ABIFLAGS""@" ] ; then + ABIFLAGS= +fi LIBS="@LIBPYTHON@ @LIBS@ $SYSLIBS" LIBS_EMBED="-lpython${VERSION}${ABIFLAGS} @LIBS@ $SYSLIBS" BASECFLAGS="@BASECFLAGS@" -LDLIBRARY="@LDLIBRARY@" OPT="@OPT@" PY_ENABLE_SHARED="@PY_ENABLE_SHARED@" LDVERSION="@LDVERSION@" +LDLIBRARY="@LDLIBRARY@" LIBDEST=${prefix_real}/lib/python${VERSION} LIBPL=$(echo "@LIBPL@" | sed "s#$prefix#$prefix_real#") SO="@EXT_SUFFIX@" @@ -67,7 +83,7 @@ for ARG in $* do case $ARG in --help) - exit_with_usage 0 + exit_with_usage ;; --embed) PY_EMBED=1 @@ -75,7 +91,7 @@ do --prefix|--exec-prefix|--includes|--libs|--cflags|--ldflags|--extension-suffix|--abiflags|--configdir) ;; *) - exit_with_usage 1 + exit_with_usage ;; esac done @@ -86,37 +102,37 @@ fi for ARG in "$@" do - case "$ARG" in + case $ARG in --prefix) - echo "$prefix_real" + echo -ne "$prefix_real" ;; --exec-prefix) - echo "$exec_prefix_real" + echo -ne "$exec_prefix_real " ;; --includes) - echo "$INCDIR $PLATINCDIR" + echo -ne "$INCDIR $PLATINCDIR" ;; --cflags) - echo "$INCDIR $PLATINCDIR $BASECFLAGS $CFLAGS $OPT" + echo -ne "$INCDIR $PLATINCDIR $BASECFLAGS $CFLAGS $OPT" ;; --libs) - echo "$LIBS" + echo -ne "$LIBS" ;; --ldflags) LIBPLUSED= if [ "$PY_ENABLE_SHARED" = "0" ] ; then LIBPLUSED="-L$LIBPL" fi - echo "$LIBPLUSED -L$libdir $LIBS" + echo -ne "$LIBPLUSED -L$libdir $LIBS " ;; --extension-suffix) - echo "$SO" + echo -ne "$SO " ;; --abiflags) - echo "$ABIFLAGS" + echo -ne "$ABIFLAGS " ;; --configdir) - echo "$LIBPL" + echo -ne "$LIBPL " ;; esac done diff --git a/Modules/Setup.bootstrap.in b/Modules/Setup.bootstrap.in index aa4e60e272653b..0b53035c6787c9 100644 --- a/Modules/Setup.bootstrap.in +++ b/Modules/Setup.bootstrap.in @@ -8,8 +8,8 @@ # module C APIs are used in core atexit atexitmodule.c faulthandler faulthandler.c -posix posixmodule.c -_signal signalmodule.c +@INITSYS@ posixmodule.c +_signal signalmodule.c -lws2_32 _tracemalloc _tracemalloc.c _suggestions _suggestions.c @@ -17,7 +17,7 @@ _suggestions _suggestions.c _codecs _codecsmodule.c _collections _collectionsmodule.c errno errnomodule.c -_io _io/_iomodule.c _io/iobase.c _io/fileio.c _io/bytesio.c _io/bufferedio.c _io/textio.c _io/stringio.c +_io _io/_iomodule.c _io/iobase.c _io/fileio.c _io/bytesio.c _io/bufferedio.c _io/textio.c _io/stringio.c _io/winconsoleio.c itertools itertoolsmodule.c _sre _sre/sre.c _sysconfig _sysconfig.c @@ -36,3 +36,8 @@ _symtable symtablemodule.c # for systems without $HOME env, used by site._getuserbase() @MODULE_PWD_TRUE@pwd pwdmodule.c + +# build-in modules for windows platform: +@USE_WIN32_MODULE@winreg ../PC/winreg.c +@MODULE_MSVCRT_TRUE@msvcrt -DPy_BUILD_CORE ../PC/msvcrtmodule.c +@MODULE__WINAPI_TRUE@_winapi _winapi.c diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 06b30feef43e40..78471f1dcf09c7 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -155,6 +155,12 @@ # _scproxy needs SystemConfiguration and CoreFoundation framework @MODULE__SCPROXY_TRUE@_scproxy _scproxy.c +############################################################################ +# Windows specific modules + +@MODULE__OVERLAPPED_TRUE@_overlapped overlapped.c +@MODULE_WINSOUND_TRUE@winsound ../PC/winsound.c + ############################################################################ # Test modules @@ -175,6 +181,7 @@ @MODULE__TESTSINGLEPHASE_TRUE@_testsinglephase _testsinglephase.c @MODULE__TESTEXTERNALINSPECTION_TRUE@_testexternalinspection _testexternalinspection.c @MODULE__CTYPES_TEST_TRUE@_ctypes_test _ctypes/_ctypes_test.c +@MODULE__TESTCONSOLE_TRUE@_testconsole ../PC/_testconsole.c # Limited API template modules; must be built as shared modules. @MODULE_XXLIMITED_TRUE@xxlimited xxlimited.c diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 0ac5458ea2d349..082be3328ca0e3 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -3557,6 +3557,18 @@ static PPROC FindAddress(void *handle, const char *name, PyObject *type) mangled_name = alloca(strlen(name) + 1 + 1 + 1 + 3); /* \0 _ @ %d */ if (!mangled_name) return NULL; + /* Issue: for stdcall decorated export functions MSVC compiler adds + * underscore, but GCC compiler create them without. This is + * visible by example for _ctypes_test.pyd module. + * As well functions from system libraries are without underscore. + * Solutions: + * - If a python module is build with gcc option --add-stdcall-alias + * the module will contain XXX as alias for function XXX@ as result + * first search in this method will succeed. + * - Distutil may use compiler to create def-file, to modify it as + * add underscore alias and with new def file to create module. + * - Or may be just to search for function without underscore. + */ for (i = 0; i < 32; ++i) { sprintf(mangled_name, "_%s@%d", name, i*4); Py_BEGIN_ALLOW_THREADS @@ -3564,6 +3576,13 @@ static PPROC FindAddress(void *handle, const char *name, PyObject *type) Py_END_ALLOW_THREADS if (address) return address; + /* search for function without underscore as weel */ + sprintf(mangled_name, "%s@%d", name, i*4); + Py_BEGIN_ALLOW_THREADS + address = (PPROC)GetProcAddress(handle, mangled_name); + Py_END_ALLOW_THREADS + if (address) + return address; } return NULL; #endif diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index df7fba67810ed0..ee6f01a658deb8 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -15,7 +15,7 @@ #include #include -#if defined(WIN32) && !defined(__CYGWIN__) +#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) #include "gdbmerrno.h" extern const char * gdbm_strerror(gdbm_error); #endif diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index b5129ffcbffdcf..6cf6bd6c5beeb9 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -23,6 +23,7 @@ #endif #include "_iomodule.h" +#include "iscygpty.h" /* * Known likely problems: @@ -1155,7 +1156,7 @@ _io_FileIO_isatty_impl(fileio *self) return err_closed(); Py_BEGIN_ALLOW_THREADS _Py_BEGIN_SUPPRESS_IPH - res = isatty(self->fd); + res = isatty(self->fd) || is_cygpty(self->fd); _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS return PyBool_FromLong(res); diff --git a/Modules/_localemodule.c b/Modules/_localemodule.c index d1d3ce9cc91239..f559440f8f0c6e 100644 --- a/Modules/_localemodule.c +++ b/Modules/_localemodule.c @@ -13,6 +13,14 @@ This software comes with no warranty. Use at your own risk. #include "pycore_fileutils.h" // _Py_GetLocaleconvNumeric() #include "pycore_pymem.h" // _PyMem_Strdup() +#ifdef __MINGW32__ +/* The header libintl.h and library libintl may exist on mingw host. + * To be compatible with MSVC build we has to undef some defines. + */ +#undef HAVE_LIBINTL_H +#undef HAVE_BIND_TEXTDOMAIN_CODESET +#endif + #include // setlocale() #include // strlen() #ifdef HAVE_ERRNO_H diff --git a/Modules/_multiprocessing/multiprocessing.c b/Modules/_multiprocessing/multiprocessing.c index cee8cf7b9a83c0..8e09cea70abb4b 100644 --- a/Modules/_multiprocessing/multiprocessing.c +++ b/Modules/_multiprocessing/multiprocessing.c @@ -181,7 +181,7 @@ static PyMethodDef module_methods[] = { _MULTIPROCESSING_RECV_METHODDEF _MULTIPROCESSING_SEND_METHODDEF #endif -#if !defined(POSIX_SEMAPHORES_NOT_ENABLED) +#if defined(MS_WINDOWS) || !defined(POSIX_SEMAPHORES_NOT_ENABLED) _MULTIPROCESSING_SEM_UNLINK_METHODDEF #endif {NULL} diff --git a/Modules/_multiprocessing/multiprocessing.h b/Modules/_multiprocessing/multiprocessing.h index 099004b437828e..a31443b9c830d2 100644 --- a/Modules/_multiprocessing/multiprocessing.h +++ b/Modules/_multiprocessing/multiprocessing.h @@ -30,7 +30,10 @@ # endif # define SEM_HANDLE HANDLE # define SEM_VALUE_MAX LONG_MAX -# define HAVE_MP_SEMAPHORE +# define HAVE_MP_SEMAPHORE +# if defined(HAVE_SEM_OPEN) && defined(_POSIX_THREADS) +# include +# endif #else # include /* O_CREAT and O_EXCL */ # if defined(HAVE_SEM_OPEN) && !defined(POSIX_SEMAPHORES_NOT_ENABLED) diff --git a/Modules/_winapi.c b/Modules/_winapi.c index bd80c5c94fe36d..af40e07b33aa72 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -46,7 +46,9 @@ #endif #include "windows.h" #include +#if defined(Py_DEBUG) #include +#endif #include "winreparse.h" #if defined(MS_WIN32) && !defined(MS_WIN64) @@ -1227,7 +1229,7 @@ getattributelist(PyObject *obj, const char *name, AttributeList *attribute_list) DWORD err; BOOL result; PyObject *value; - Py_ssize_t handle_list_size; + Py_ssize_t handle_list_size = 0; DWORD attribute_count = 0; SIZE_T attribute_list_size = 0; diff --git a/Modules/getbuildinfo.c b/Modules/getbuildinfo.c index 8d553d106c6ab5..cb8cc329506a31 100644 --- a/Modules/getbuildinfo.c +++ b/Modules/getbuildinfo.c @@ -41,7 +41,7 @@ static char buildinfo[50 + sizeof(GITVERSION) + ((sizeof(GITTAG) > sizeof(GITBRANCH)) ? sizeof(GITTAG) : sizeof(GITBRANCH))]; -const char * +PyAPI_FUNC(const char *) Py_GetBuildInfo(void) { if (initialized) { diff --git a/Modules/getpath.c b/Modules/getpath.c index d0128b20faeeae..cf3fbdd0989ec0 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -57,6 +57,25 @@ /* HELPER FUNCTIONS for getpath.py */ +static PyObject * +getpath_normpath(PyObject *Py_UNUSED(self), PyObject *args) +{ + PyObject *r = NULL; + PyObject *pathobj; + wchar_t *path; + if (!PyArg_ParseTuple(args, "U", &pathobj)) { + return NULL; + } + Py_ssize_t len; + wchar_t *buffer = PyUnicode_AsWideCharString(pathobj, &len); + if (!buffer) { + return NULL; + } + r = PyUnicode_FromWideChar(_Py_normpath(buffer, len), -1); + PyMem_Free(buffer); + return r; +} + static PyObject * getpath_abspath(PyObject *Py_UNUSED(self), PyObject *args) { @@ -91,6 +110,12 @@ getpath_basename(PyObject *Py_UNUSED(self), PyObject *args) } Py_ssize_t end = PyUnicode_GET_LENGTH(path); Py_ssize_t pos = PyUnicode_FindChar(path, SEP, 0, end, -1); +#ifdef ALTSEP + if (pos < 0) { + // try using altsep + pos = PyUnicode_FindChar(path, ALTSEP, 0, end, -1); + } +#endif if (pos < 0) { return Py_NewRef(path); } @@ -107,6 +132,12 @@ getpath_dirname(PyObject *Py_UNUSED(self), PyObject *args) } Py_ssize_t end = PyUnicode_GET_LENGTH(path); Py_ssize_t pos = PyUnicode_FindChar(path, SEP, 0, end, -1); +#ifdef ALTSEP + if (pos < 0) { + // try using altsep + pos = PyUnicode_FindChar(path, ALTSEP, 0, end, -1); + } +#endif if (pos < 0) { return PyUnicode_FromStringAndSize(NULL, 0); } @@ -561,6 +592,7 @@ getpath_realpath(PyObject *Py_UNUSED(self) , PyObject *args) static PyMethodDef getpath_methods[] = { + {"normpath", getpath_normpath, METH_VARARGS, NULL}, {"abspath", getpath_abspath, METH_VARARGS, NULL}, {"basename", getpath_basename, METH_VARARGS, NULL}, {"dirname", getpath_dirname, METH_VARARGS, NULL}, @@ -927,6 +959,11 @@ _PyConfig_InitPathConfig(PyConfig *config, int compute_path_config) #else !decode_to_dict(dict, "os_name", "posix") || #endif +#ifdef __MINGW32__ + !int_to_dict(dict, "is_mingw", 1) || +#else + !int_to_dict(dict, "is_mingw", 0) || +#endif #ifdef WITH_NEXT_FRAMEWORK !int_to_dict(dict, "WITH_NEXT_FRAMEWORK", 1) || #else @@ -958,6 +995,9 @@ _PyConfig_InitPathConfig(PyConfig *config, int compute_path_config) #endif #ifndef MS_WINDOWS PyDict_SetItemString(dict, "winreg", Py_None) < 0 || +#endif +#ifdef __MINGW32__ + !env_to_dict(dict, "ENV_MSYSTEM", 0) || #endif PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins()) < 0 ) { diff --git a/Modules/getpath.py b/Modules/getpath.py index 1f1bfcb4f64dd4..e743161eed3501 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -30,6 +30,7 @@ # ** Values known at compile time ** # os_name -- [in] one of 'nt', 'posix', 'darwin' +# is_mingw -- [in] True if targeting MinGW # PREFIX -- [in] sysconfig.get_config_var(...) # EXEC_PREFIX -- [in] sysconfig.get_config_var(...) # PYTHONPATH -- [in] sysconfig.get_config_var(...) @@ -52,6 +53,7 @@ # ENV_PYTHONHOME -- [in] getenv(...) # ENV_PYTHONEXECUTABLE -- [in] getenv(...) # ENV___PYVENV_LAUNCHER__ -- [in] getenv(...) +# ENV_MSYSTEM -- [in] getenv(...) # ** Values calculated at runtime ** # config -- [in/out] dict of the PyConfig structure @@ -187,8 +189,27 @@ ZIP_LANDMARK = f'{platlibdir}/python{VERSION_MAJOR}{VERSION_MINOR}{ABI_THREAD}.zip' DELIM = ':' SEP = '/' + ALTSEP = None -elif os_name == 'nt': +elif os_name == 'nt' and is_mingw: + BUILDDIR_TXT = 'pybuilddir.txt' + BUILD_LANDMARK = 'Modules/Setup.local' + DEFAULT_PROGRAM_NAME = f'python{VERSION_MAJOR}' + STDLIB_SUBDIR = f'{platlibdir}/python{VERSION_MAJOR}.{VERSION_MINOR}' + STDLIB_LANDMARKS = [f'{STDLIB_SUBDIR}/os.py', f'{STDLIB_SUBDIR}/os.pyc'] + PLATSTDLIB_LANDMARK = f'{platlibdir}/python{VERSION_MAJOR}.{VERSION_MINOR}/lib-dynload' + BUILDSTDLIB_LANDMARKS = ['Lib/os.py'] + VENV_LANDMARK = 'pyvenv.cfg' + ZIP_LANDMARK = f'{platlibdir}/python{VERSION_MAJOR}{VERSION_MINOR}.zip' + DELIM = ';' + if ENV_MSYSTEM: + SEP = '/' + ALTSEP = '\\' + else: + SEP = '\\' + ALTSEP = '/' + +elif os_name == 'nt': # MSVC BUILDDIR_TXT = 'pybuilddir.txt' BUILD_LANDMARK = f'{VPATH}\\Modules\\Setup.local' DEFAULT_PROGRAM_NAME = f'python' @@ -201,6 +222,7 @@ WINREG_KEY = f'SOFTWARE\\Python\\PythonCore\\{PYWINVER}\\PythonPath' DELIM = ';' SEP = '\\' + ALTSEP = '/' # ****************************************************************************** @@ -213,6 +235,8 @@ def search_up(prefix, *landmarks, test=isfile): return prefix prefix = dirname(prefix) +def _normpath(p): + return normpath(p) if p is not None else None # ****************************************************************************** # READ VARIABLES FROM config @@ -264,10 +288,10 @@ def search_up(prefix, *landmarks, test=isfile): if not executable: executable = real_executable -if not executable and SEP in program_name: +if not executable and (SEP in program_name or + (ALTSEP and ALTSEP in program_name)): # Resolve partial path program_name against current directory executable = abspath(program_name) - if not executable: # All platforms default to real_executable if known at this # stage. POSIX does not set this value. @@ -502,15 +526,15 @@ def search_up(prefix, *landmarks, test=isfile): except (FileNotFoundError, PermissionError): if isfile(joinpath(real_executable_dir, BUILD_LANDMARK)): build_prefix = joinpath(real_executable_dir, VPATH) - if os_name == 'nt': + if os_name == 'nt' and not is_mingw: # QUIRK: Windows builds need platstdlib_dir to be the executable # dir. Normally the builddir marker handles this, but in this # case we need to correct manually. platstdlib_dir = real_executable_dir if build_prefix: - if os_name == 'nt': - # QUIRK: No searching for more landmarks on Windows + if os_name == 'nt' and not is_mingw: + # QUIRK: No searching for more landmarks on MSVC build_stdlib_prefix = build_prefix else: build_stdlib_prefix = search_up(build_prefix, *BUILDSTDLIB_LANDMARKS) @@ -559,6 +583,9 @@ def search_up(prefix, *landmarks, test=isfile): # First try to detect prefix by looking alongside our runtime library, if known if library and not prefix: library_dir = dirname(library) + if os_name == 'nt' and is_mingw: + # QUIRK: On Windows, mingw Python DLLs are in the bin directory + library_dir = joinpath(library_dir, '..') if ZIP_LANDMARK: if os_name == 'nt': # QUIRK: Windows does not search up for ZIP file @@ -605,7 +632,7 @@ def search_up(prefix, *landmarks, test=isfile): # Detect exec_prefix by searching from executable for the platstdlib_dir if PLATSTDLIB_LANDMARK and not exec_prefix: - if os_name == 'nt': + if os_name == 'nt' and (not is_mingw): # QUIRK: Windows always assumed these were the same # gh-100320: Our PYDs are assumed to be relative to the Lib directory # (that is, prefix) rather than the executable (that is, executable_dir) @@ -615,7 +642,7 @@ def search_up(prefix, *landmarks, test=isfile): if not exec_prefix and EXEC_PREFIX: exec_prefix = EXEC_PREFIX if not exec_prefix or not isdir(joinpath(exec_prefix, PLATSTDLIB_LANDMARK)): - if os_name == 'nt': + if os_name == 'nt' and (not is_mingw): # QUIRK: If DLLs is missing on Windows, don't warn, just assume # that they're in exec_prefix if not platstdlib_dir: @@ -653,7 +680,7 @@ def search_up(prefix, *landmarks, test=isfile): if py_setpath: # If Py_SetPath was called then it overrides any existing search path - config['module_search_paths'] = py_setpath.split(DELIM) + config['module_search_paths'] = [_normpath(p) for p in py_setpath.split(DELIM)] config['module_search_paths_set'] = 1 elif not pythonpath_was_set: @@ -668,7 +695,7 @@ def search_up(prefix, *landmarks, test=isfile): pythonpath.append(abspath(p)) # Then add the default zip file - if os_name == 'nt': + if os_name == 'nt' and (not is_mingw): # QUIRK: Windows uses the library directory rather than the prefix if library: library_dir = dirname(library) @@ -681,7 +708,7 @@ def search_up(prefix, *landmarks, test=isfile): else: pythonpath.append(joinpath(prefix, ZIP_LANDMARK)) - if os_name == 'nt' and use_environment and winreg: + if (not is_mingw) and os_name == 'nt' and use_environment and winreg: # QUIRK: Windows also lists paths in the registry. Paths are stored # as the default value of each subkey of # {HKCU,HKLM}\Software\Python\PythonCore\{winver}\PythonPath @@ -722,7 +749,7 @@ def search_up(prefix, *landmarks, test=isfile): if not platstdlib_dir and exec_prefix: platstdlib_dir = joinpath(exec_prefix, PLATSTDLIB_LANDMARK) - if os_name == 'nt': + if os_name == 'nt' and (not is_mingw): # QUIRK: Windows generates paths differently if platstdlib_dir: pythonpath.append(platstdlib_dir) @@ -740,7 +767,7 @@ def search_up(prefix, *landmarks, test=isfile): if platstdlib_dir: pythonpath.append(platstdlib_dir) - config['module_search_paths'] = pythonpath + config['module_search_paths'] = [_normpath(p) for p in pythonpath] config['module_search_paths_set'] = 1 @@ -750,8 +777,8 @@ def search_up(prefix, *landmarks, test=isfile): # QUIRK: Non-Windows replaces prefix/exec_prefix with defaults when running # in build directory. This happens after pythonpath calculation. -if os_name != 'nt' and build_prefix: - prefix = config.get('prefix') or PREFIX +if (os_name != 'nt' or is_mingw) and build_prefix: + prefix = config.get('prefix') or abspath(PREFIX) exec_prefix = config.get('exec_prefix') or EXEC_PREFIX or prefix @@ -775,23 +802,23 @@ def search_up(prefix, *landmarks, test=isfile): warn("unsupported 'import' line in ._pth file") else: pythonpath.append(joinpath(pth_dir, line)) - config['module_search_paths'] = pythonpath + config['module_search_paths'] = [_normpath(p) for p in pythonpath] config['module_search_paths_set'] = 1 # ****************************************************************************** # UPDATE config FROM CALCULATED VALUES # ****************************************************************************** -config['program_name'] = program_name -config['home'] = home -config['executable'] = executable -config['base_executable'] = base_executable -config['prefix'] = prefix -config['exec_prefix'] = exec_prefix -config['base_prefix'] = base_prefix or prefix -config['base_exec_prefix'] = base_exec_prefix or exec_prefix +config['program_name'] = _normpath(program_name) +config['home'] = _normpath(home) +config['executable'] = _normpath(executable) +config['base_executable'] = _normpath(base_executable) +config['prefix'] = _normpath(prefix) +config['exec_prefix'] = _normpath(exec_prefix) +config['base_prefix'] = _normpath(base_prefix or prefix) +config['base_exec_prefix'] = _normpath(base_exec_prefix or exec_prefix) -config['platlibdir'] = platlibdir +config['platlibdir'] = _normpath(platlibdir) # test_embed expects empty strings, not None -config['stdlib_dir'] = stdlib_dir or '' -config['platstdlib_dir'] = platstdlib_dir or '' +config['stdlib_dir'] = _normpath(stdlib_dir or '') +config['platstdlib_dir'] = _normpath(platstdlib_dir or '') diff --git a/Modules/main.c b/Modules/main.c index 15ea49a1bad19e..800fbb4b3f6d64 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -9,6 +9,7 @@ #include "pycore_pylifecycle.h" // _Py_PreInitializeFromPyArgv() #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_pythonrun.h" // _PyRun_AnyFileObject() +#include "iscygpty.h" /* Includes for exit_sigint() */ #include // perror() @@ -90,7 +91,7 @@ static inline int config_run_code(const PyConfig *config) static int stdin_is_interactive(const PyConfig *config) { - return (isatty(fileno(stdin)) || config->interactive); + return (isatty(fileno(stdin)) || config->interactive || is_cygpty(fileno(stdin))); } diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 99a85e9e49ad47..31dd628b1efcb8 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -256,7 +256,7 @@ do { \ } while (0) #endif /* UNIX */ -#if defined(MS_WINDOWS) && !defined(DONT_USE_SEH) +#if defined(_MSC_VER) && !defined(DONT_USE_SEH) static DWORD filter_page_exception(EXCEPTION_POINTERS *ptrs, EXCEPTION_RECORD *record) { @@ -289,7 +289,7 @@ filter_page_exception_method(mmap_object *self, EXCEPTION_POINTERS *ptrs, } #endif -#if defined(MS_WINDOWS) && !defined(DONT_USE_SEH) +#if defined(_MSC_VER) && !defined(DONT_USE_SEH) #define HANDLE_INVALID_MEM(sourcecode) \ do { \ EXCEPTION_RECORD record; \ @@ -317,7 +317,7 @@ do { \ } while (0) #endif -#if defined(MS_WINDOWS) && !defined(DONT_USE_SEH) +#if defined(_MSC_VER) && !defined(DONT_USE_SEH) #define HANDLE_INVALID_MEM_METHOD(self, sourcecode) \ do { \ EXCEPTION_RECORD record; \ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index a09305c91ecbba..fa14d4199fb04f 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -1,18108 +1,18137 @@ -/* POSIX module implementation */ - -/* This file is also used for Windows NT/MS-Win. In that case the - module actually calls itself 'nt', not 'posix', and a few - functions are either unimplemented or implemented differently. The source - assumes that for Windows NT, the macro 'MS_WINDOWS' is defined independent - of the compiler used. Different compilers define their own feature - test macro, e.g. '_MSC_VER'. */ - -#include "Python.h" - -#ifdef __VXWORKS__ -# include "pycore_bitutils.h" // _Py_popcount32() -#endif -#include "pycore_abstract.h" // _PyNumber_Index() -#include "pycore_call.h" // _PyObject_CallNoArgs() -#include "pycore_ceval.h" // _PyEval_ReInitThreads() -#include "pycore_fileutils.h" // _Py_closerange() -#include "pycore_initconfig.h" // _PyStatus_EXCEPTION() -#include "pycore_long.h" // _PyLong_IsNegative() -#include "pycore_moduleobject.h" // _PyModule_GetState() -#include "pycore_object.h" // _PyObject_LookupSpecial() -#include "pycore_pylifecycle.h" // _PyOS_URandom() -#include "pycore_pystate.h" // _PyInterpreterState_GET() -#include "pycore_signal.h" // Py_NSIG -#include "pycore_time.h" // _PyLong_FromTime_t() -#include "pycore_typeobject.h" // _PyType_AddMethod() - -#ifdef HAVE_UNISTD_H -# include // symlink() -#endif - -#ifdef MS_WINDOWS -# include -# if !defined(MS_WINDOWS_GAMES) || defined(MS_WINDOWS_DESKTOP) -# include -# endif -# include -# include // UNLEN -# include "osdefs.h" // SEP -# include // SetEntriesInAcl -# include // SDDL_REVISION_1 -# if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) -# define HAVE_SYMLINK -# endif /* MS_WINDOWS_DESKTOP | MS_WINDOWS_SYSTEM */ -#endif - -#ifndef MS_WINDOWS -# include "posixmodule.h" -#else -# include "pycore_fileutils_windows.h" -# include "winreparse.h" -#endif - -#if !defined(EX_OK) && defined(EXIT_SUCCESS) -# define EX_OK EXIT_SUCCESS -#endif - -#ifdef __APPLE__ - /* Needed for the implementation of os.statvfs */ -# include -# include -#endif - -/* On android API level 21, 'AT_EACCESS' is not declared although - * HAVE_FACCESSAT is defined. */ -#ifdef __ANDROID__ -# undef HAVE_FACCESSAT -#endif - -#include // ctermid() -#include // system() -#ifdef HAVE_SYS_TIME_H -# include // futimes() -#endif - - -// SGI apparently needs this forward declaration -#ifdef HAVE__GETPTY -# include // mode_t - extern char * _getpty(int *, int, mode_t, int); -#endif - - -/* - * A number of APIs are available on macOS from a certain macOS version. - * To support building with a new SDK while deploying to older versions - * the availability test is split into two: - * - HAVE_: The configure check for compile time availability - * - HAVE__RUNTIME: Runtime check for availability - * - * The latter is always true when not on macOS, or when using a compiler - * that does not support __has_builtin (older versions of Xcode). - * - * Due to compiler restrictions there is one valid use of HAVE__RUNTIME: - * if (HAVE__RUNTIME) { ... } - * - * In mixing the test with other tests or using negations will result in compile - * errors. - */ -#if defined(__APPLE__) - -#include - -#if defined(__has_builtin) -#if __has_builtin(__builtin_available) -#define HAVE_BUILTIN_AVAILABLE 1 -#endif -#endif - -#ifdef HAVE_BUILTIN_AVAILABLE -# define HAVE_FSTATAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_FACCESSAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_FCHMODAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_FCHOWNAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_LINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_FDOPENDIR_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_MKDIRAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_RENAMEAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_UNLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_OPENAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_READLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_SYMLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) -# define HAVE_FUTIMENS_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) -# define HAVE_UTIMENSAT_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) -# define HAVE_PWRITEV_RUNTIME __builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) -# define HAVE_MKFIFOAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) -# define HAVE_MKNODAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) -# define HAVE_PTSNAME_R_RUNTIME __builtin_available(macOS 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.3, *) - -# define HAVE_POSIX_SPAWN_SETSID_RUNTIME __builtin_available(macOS 10.15, *) - -#else /* Xcode 8 or earlier */ - - /* __builtin_available is not present in these compilers, but - * some of the symbols might be weak linked (10.10 SDK or later - * deploying on 10.9. - * - * Fall back to the older style of availability checking for - * symbols introduced in macOS 10.10. - */ - -# ifdef HAVE_FSTATAT -# define HAVE_FSTATAT_RUNTIME (fstatat != NULL) -# endif - -# ifdef HAVE_FACCESSAT -# define HAVE_FACCESSAT_RUNTIME (faccessat != NULL) -# endif - -# ifdef HAVE_FCHMODAT -# define HAVE_FCHMODAT_RUNTIME (fchmodat != NULL) -# endif - -# ifdef HAVE_FCHOWNAT -# define HAVE_FCHOWNAT_RUNTIME (fchownat != NULL) -# endif - -# ifdef HAVE_LINKAT -# define HAVE_LINKAT_RUNTIME (linkat != NULL) -# endif - -# ifdef HAVE_FDOPENDIR -# define HAVE_FDOPENDIR_RUNTIME (fdopendir != NULL) -# endif - -# ifdef HAVE_MKDIRAT -# define HAVE_MKDIRAT_RUNTIME (mkdirat != NULL) -# endif - -# ifdef HAVE_RENAMEAT -# define HAVE_RENAMEAT_RUNTIME (renameat != NULL) -# endif - -# ifdef HAVE_UNLINKAT -# define HAVE_UNLINKAT_RUNTIME (unlinkat != NULL) -# endif - -# ifdef HAVE_OPENAT -# define HAVE_OPENAT_RUNTIME (openat != NULL) -# endif - -# ifdef HAVE_READLINKAT -# define HAVE_READLINKAT_RUNTIME (readlinkat != NULL) -# endif - -# ifdef HAVE_SYMLINKAT -# define HAVE_SYMLINKAT_RUNTIME (symlinkat != NULL) -# endif - -# ifdef HAVE_UTIMENSAT -# define HAVE_UTIMENSAT_RUNTIME (utimensat != NULL) -# endif - -# ifdef HAVE_FUTIMENS -# define HAVE_FUTIMENS_RUNTIME (futimens != NULL) -# endif - -# ifdef HAVE_PWRITEV -# define HAVE_PWRITEV_RUNTIME (pwritev != NULL) -# endif - -# ifdef HAVE_MKFIFOAT -# define HAVE_MKFIFOAT_RUNTIME (mkfifoat != NULL) -# endif - -# ifdef HAVE_MKNODAT -# define HAVE_MKNODAT_RUNTIME (mknodat != NULL) -# endif - -# ifdef HAVE_PTSNAME_R -# define HAVE_PTSNAME_R_RUNTIME (ptsname_r != NULL) -# endif - -#endif - -#ifdef HAVE_FUTIMESAT -/* Some of the logic for weak linking depends on this assertion */ -# error "HAVE_FUTIMESAT unexpectedly defined" -#endif - -#else -# define HAVE_FSTATAT_RUNTIME 1 -# define HAVE_FACCESSAT_RUNTIME 1 -# define HAVE_FCHMODAT_RUNTIME 1 -# define HAVE_FCHOWNAT_RUNTIME 1 -# define HAVE_LINKAT_RUNTIME 1 -# define HAVE_FDOPENDIR_RUNTIME 1 -# define HAVE_MKDIRAT_RUNTIME 1 -# define HAVE_RENAMEAT_RUNTIME 1 -# define HAVE_UNLINKAT_RUNTIME 1 -# define HAVE_OPENAT_RUNTIME 1 -# define HAVE_READLINKAT_RUNTIME 1 -# define HAVE_SYMLINKAT_RUNTIME 1 -# define HAVE_FUTIMENS_RUNTIME 1 -# define HAVE_UTIMENSAT_RUNTIME 1 -# define HAVE_PWRITEV_RUNTIME 1 -# define HAVE_MKFIFOAT_RUNTIME 1 -# define HAVE_MKNODAT_RUNTIME 1 -# define HAVE_PTSNAME_R_RUNTIME 1 -#endif - - -PyDoc_STRVAR(posix__doc__, -"This module provides access to operating system functionality that is\n\ -standardized by the C Standard and the POSIX standard (a thinly\n\ -disguised Unix interface). Refer to the library manual and\n\ -corresponding Unix manual entries for more information on calls."); - - -#ifdef HAVE_SYS_UIO_H -# include -#endif - -#ifdef HAVE_SYS_TYPES_H -/* Should be included before on HP-UX v3 */ -# include -#endif /* HAVE_SYS_TYPES_H */ - -#ifdef HAVE_SYS_SYSMACROS_H -/* GNU C Library: major(), minor(), makedev() */ -# include -#endif - -#ifdef HAVE_SYS_STAT_H -# include -#endif /* HAVE_SYS_STAT_H */ - -#ifdef HAVE_SYS_WAIT_H -# include // WNOHANG -#endif -#ifdef HAVE_LINUX_WAIT_H -# include // P_PIDFD -#endif - -#ifdef HAVE_SIGNAL_H -# include -#endif - -#ifdef HAVE_FCNTL_H -# include -#endif - -#ifdef HAVE_GRP_H -# include -#endif - -#ifdef HAVE_SYSEXITS_H -# include -#endif - -#ifdef HAVE_SYS_LOADAVG_H -# include -#endif - -#ifdef HAVE_SYS_SENDFILE_H -# include -#endif - -#if defined(__APPLE__) -# include -#endif - -#ifdef HAVE_SCHED_H -# include -#endif - -#if !defined(CPU_ALLOC) && defined(HAVE_SCHED_SETAFFINITY) -# undef HAVE_SCHED_SETAFFINITY -#endif - -#if defined(HAVE_SYS_XATTR_H) -# if defined(HAVE_LINUX_LIMITS_H) && !defined(__FreeBSD_kernel__) && !defined(__GNU__) -# define USE_XATTRS -# include // Needed for XATTR_SIZE_MAX on musl libc. -# endif -# if defined(__CYGWIN__) -# define USE_XATTRS -# include // Needed for XATTR_SIZE_MAX and XATTR_LIST_MAX. -# endif -#endif - -#ifdef USE_XATTRS -# include -#endif - -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__) -# ifdef HAVE_SYS_SOCKET_H -# include -# endif -#endif - -#ifdef HAVE_DLFCN_H -# include -#endif - -#ifdef __hpux -# include -#endif - -#if defined(__DragonFly__) || \ - defined(__OpenBSD__) || \ - defined(__FreeBSD__) || \ - defined(__NetBSD__) || \ - defined(__APPLE__) -# include -#endif - -#ifdef HAVE_LINUX_RANDOM_H -# include -#endif -#ifdef HAVE_GETRANDOM_SYSCALL -# include -#endif - -#ifdef HAVE_WINDOWS_CONSOLE_IO -# define TERMSIZE_USE_CONIO -#elif defined(HAVE_SYS_IOCTL_H) -# include -# if defined(HAVE_TERMIOS_H) -# include -# endif -# if defined(TIOCGWINSZ) -# define TERMSIZE_USE_IOCTL -# endif -#endif /* HAVE_WINDOWS_CONSOLE_IO */ - -/* Various compilers have only certain posix functions */ -/* XXX Gosh I wish these were all moved into pyconfig.h */ -#if defined(__WATCOMC__) && !defined(__QNX__) /* Watcom compiler */ -# define HAVE_OPENDIR 1 -# define HAVE_SYSTEM 1 -# include -#elif defined( _MSC_VER) - /* Microsoft compiler */ -# if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_APP) || defined(MS_WINDOWS_SYSTEM) -# define HAVE_GETPPID 1 -# endif /* MS_WINDOWS_DESKTOP | MS_WINDOWS_APP | MS_WINDOWS_SYSTEM */ -# if defined(MS_WINDOWS_DESKTOP) -# define HAVE_GETLOGIN 1 -# endif /* MS_WINDOWS_DESKTOP */ -# if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) -# define HAVE_SPAWNV 1 -# define HAVE_EXECV 1 -# define HAVE_WSPAWNV 1 -# define HAVE_WEXECV 1 -# define HAVE_SYSTEM 1 -# define HAVE_CWAIT 1 -# endif /* MS_WINDOWS_DESKTOP | MS_WINDOWS_SYSTEM */ -# define HAVE_PIPE 1 -# define HAVE_FSYNC 1 -# define fsync _commit -#endif /* ! __WATCOMC__ || __QNX__ */ - -/*[clinic input] -# one of the few times we lie about this name! -module os -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=94a0f0f978acae17]*/ - -#ifndef _MSC_VER - -#if defined(__sgi)&&_COMPILER_VERSION>=700 -/* declare ctermid_r if compiling with MIPSPro 7.x in ANSI C mode - (default) */ -extern char *ctermid_r(char *); -#endif - -#endif /* !_MSC_VER */ - -#if defined(__VXWORKS__) -# include -# include -# include -# include -# ifndef _P_WAIT -# define _P_WAIT 0 -# define _P_NOWAIT 1 -# define _P_NOWAITO 1 -# endif -#endif /* __VXWORKS__ */ - -#ifdef HAVE_POSIX_SPAWN -# include -#endif - -#ifdef HAVE_UTIME_H -# include -#endif /* HAVE_UTIME_H */ - -#ifdef HAVE_SYS_UTIME_H -# include -# define HAVE_UTIME_H /* pretend we do for the rest of this file */ -#endif /* HAVE_SYS_UTIME_H */ - -#ifdef HAVE_SYS_TIMES_H -# include -#endif /* HAVE_SYS_TIMES_H */ - -#ifdef HAVE_SYS_PARAM_H -# include -#endif /* HAVE_SYS_PARAM_H */ - -#ifdef HAVE_SYS_UTSNAME_H -# include -#endif /* HAVE_SYS_UTSNAME_H */ - -#ifdef HAVE_DIRENT_H -# include -# define NAMLEN(dirent) strlen((dirent)->d_name) -#else -# if defined(__WATCOMC__) && !defined(__QNX__) -# include -# define NAMLEN(dirent) strlen((dirent)->d_name) -# else -# define dirent direct -# define NAMLEN(dirent) (dirent)->d_namlen -# endif -# ifdef HAVE_SYS_NDIR_H -# include -# endif -# ifdef HAVE_SYS_DIR_H -# include -# endif -# ifdef HAVE_NDIR_H -# include -# endif -#endif - -#ifdef _MSC_VER -# ifdef HAVE_DIRECT_H -# include -# endif -# ifdef HAVE_IO_H -# include -# endif -# ifdef HAVE_PROCESS_H -# include -# endif -# include -#endif /* _MSC_VER */ - -#ifndef MAXPATHLEN -# if defined(PATH_MAX) && PATH_MAX > 1024 -# define MAXPATHLEN PATH_MAX -# else -# define MAXPATHLEN 1024 -# endif -#endif /* MAXPATHLEN */ - -#ifdef UNION_WAIT - /* Emulate some macros on systems that have a union instead of macros */ -# ifndef WIFEXITED -# define WIFEXITED(u_wait) (!(u_wait).w_termsig && !(u_wait).w_coredump) -# endif -# ifndef WEXITSTATUS -# define WEXITSTATUS(u_wait) (WIFEXITED(u_wait)?((u_wait).w_retcode):-1) -# endif -# ifndef WTERMSIG -# define WTERMSIG(u_wait) ((u_wait).w_termsig) -# endif -# define WAIT_TYPE union wait -# define WAIT_STATUS_INT(s) (s.w_status) -#else - /* !UNION_WAIT */ -# define WAIT_TYPE int -# define WAIT_STATUS_INT(s) (s) -#endif /* UNION_WAIT */ - -/* Don't use the "_r" form if we don't need it (also, won't have a - prototype for it, at least on Solaris -- maybe others as well?). */ -#if defined(HAVE_CTERMID_R) -# define USE_CTERMID_R -#endif - -/* choose the appropriate stat and fstat functions and return structs */ -#undef STAT -#undef FSTAT -#undef STRUCT_STAT -#ifdef MS_WINDOWS -# define STAT win32_stat -# define LSTAT win32_lstat -# define FSTAT _Py_fstat_noraise -# define STRUCT_STAT struct _Py_stat_struct -#else -# define STAT stat -# define LSTAT lstat -# define FSTAT fstat -# define STRUCT_STAT struct stat -#endif - -#if defined(MAJOR_IN_MKDEV) -# include -#else -# if defined(MAJOR_IN_SYSMACROS) -# include -# endif -# if defined(HAVE_MKNOD) && defined(HAVE_SYS_MKDEV_H) -# include -# endif -#endif - -#ifdef MS_WINDOWS -# define INITFUNC PyInit_nt -# define MODNAME "nt" -# define MODNAME_OBJ &_Py_ID(nt) -#else -# define INITFUNC PyInit_posix -# define MODNAME "posix" -# define MODNAME_OBJ &_Py_ID(posix) -#endif - -#if defined(__sun) -/* Something to implement in autoconf, not present in autoconf 2.69 */ -# define HAVE_STRUCT_STAT_ST_FSTYPE 1 -#endif - -/* memfd_create is either defined in sys/mman.h or sys/memfd.h - * linux/memfd.h defines additional flags - */ -#ifdef HAVE_SYS_MMAN_H -# include -#endif -#ifdef HAVE_SYS_MEMFD_H -# include -#endif -#ifdef HAVE_LINUX_MEMFD_H -# include -#endif - -/* eventfd() */ -#ifdef HAVE_SYS_EVENTFD_H -# include -#endif - -/* timerfd_create() */ -#ifdef HAVE_SYS_TIMERFD_H -# include -#endif - -#ifdef _Py_MEMORY_SANITIZER -# include -#endif - -#ifdef HAVE_FORK -static void -run_at_forkers(PyObject *lst, int reverse) -{ - Py_ssize_t i; - PyObject *cpy; - - if (lst != NULL) { - assert(PyList_CheckExact(lst)); - - /* Use a list copy in case register_at_fork() is called from - * one of the callbacks. - */ - cpy = PyList_GetSlice(lst, 0, PyList_GET_SIZE(lst)); - if (cpy == NULL) - PyErr_WriteUnraisable(lst); - else { - if (reverse) - PyList_Reverse(cpy); - for (i = 0; i < PyList_GET_SIZE(cpy); i++) { - PyObject *func, *res; - func = PyList_GET_ITEM(cpy, i); - res = _PyObject_CallNoArgs(func); - if (res == NULL) - PyErr_WriteUnraisable(func); - else - Py_DECREF(res); - } - Py_DECREF(cpy); - } - } -} - -void -PyOS_BeforeFork(void) -{ - PyInterpreterState *interp = _PyInterpreterState_GET(); - run_at_forkers(interp->before_forkers, 1); - - _PyImport_AcquireLock(interp); - _PyEval_StopTheWorldAll(&_PyRuntime); - HEAD_LOCK(&_PyRuntime); -} - -void -PyOS_AfterFork_Parent(void) -{ - HEAD_UNLOCK(&_PyRuntime); - _PyEval_StartTheWorldAll(&_PyRuntime); - - PyInterpreterState *interp = _PyInterpreterState_GET(); - _PyImport_ReleaseLock(interp); - run_at_forkers(interp->after_forkers_parent, 0); -} - -void -PyOS_AfterFork_Child(void) -{ - PyStatus status; - _PyRuntimeState *runtime = &_PyRuntime; - - // re-creates runtime->interpreters.mutex (HEAD_UNLOCK) - status = _PyRuntimeState_ReInitThreads(runtime); - if (_PyStatus_EXCEPTION(status)) { - goto fatal_error; - } - - PyThreadState *tstate = _PyThreadState_GET(); - _Py_EnsureTstateNotNULL(tstate); - - assert(tstate->thread_id == PyThread_get_thread_ident()); -#ifdef PY_HAVE_THREAD_NATIVE_ID - tstate->native_thread_id = PyThread_get_thread_native_id(); -#endif - -#ifdef Py_GIL_DISABLED - _Py_brc_after_fork(tstate->interp); - _Py_qsbr_after_fork((_PyThreadStateImpl *)tstate); -#endif - - // Ideally we could guarantee tstate is running main. - _PyInterpreterState_ReinitRunningMain(tstate); - - status = _PyEval_ReInitThreads(tstate); - if (_PyStatus_EXCEPTION(status)) { - goto fatal_error; - } - - // Remove the dead thread states. We "start the world" once we are the only - // thread state left to undo the stop the world call in `PyOS_BeforeFork`. - // That needs to happen before `_PyThreadState_DeleteList`, because that - // may call destructors. - PyThreadState *list = _PyThreadState_RemoveExcept(tstate); - _PyEval_StartTheWorldAll(&_PyRuntime); - _PyThreadState_DeleteList(list); - - _PyImport_ReInitLock(tstate->interp); - _PyImport_ReleaseLock(tstate->interp); - - _PySignal_AfterFork(); - - status = _PyInterpreterState_DeleteExceptMain(runtime); - if (_PyStatus_EXCEPTION(status)) { - goto fatal_error; - } - assert(_PyThreadState_GET() == tstate); - - status = _PyPerfTrampoline_AfterFork_Child(); - if (_PyStatus_EXCEPTION(status)) { - goto fatal_error; - } - - run_at_forkers(tstate->interp->after_forkers_child, 0); - return; - -fatal_error: - Py_ExitStatusException(status); -} - -static int -register_at_forker(PyObject **lst, PyObject *func) -{ - if (func == NULL) /* nothing to register? do nothing. */ - return 0; - if (*lst == NULL) { - *lst = PyList_New(0); - if (*lst == NULL) - return -1; - } - return PyList_Append(*lst, func); -} -#endif /* HAVE_FORK */ - - -/* Legacy wrapper */ -void -PyOS_AfterFork(void) -{ -#ifdef HAVE_FORK - PyOS_AfterFork_Child(); -#endif -} - - -#ifdef MS_WINDOWS -/* defined in fileutils.c */ -void _Py_time_t_to_FILE_TIME(time_t, int, FILETIME *); -void _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *, ULONG, - FILE_BASIC_INFO *, FILE_ID_INFO *, - struct _Py_stat_struct *); -void _Py_stat_basic_info_to_stat(FILE_STAT_BASIC_INFORMATION *, - struct _Py_stat_struct *); -#endif - - -#ifndef MS_WINDOWS -PyObject * -_PyLong_FromUid(uid_t uid) -{ - if (uid == (uid_t)-1) - return PyLong_FromLong(-1); - return PyLong_FromUnsignedLong(uid); -} - -PyObject * -_PyLong_FromGid(gid_t gid) -{ - if (gid == (gid_t)-1) - return PyLong_FromLong(-1); - return PyLong_FromUnsignedLong(gid); -} - -int -_Py_Uid_Converter(PyObject *obj, uid_t *p) -{ - uid_t uid; - PyObject *index; - int overflow; - long result; - unsigned long uresult; - - index = _PyNumber_Index(obj); - if (index == NULL) { - PyErr_Format(PyExc_TypeError, - "uid should be integer, not %.200s", - _PyType_Name(Py_TYPE(obj))); - return 0; - } - - /* - * Handling uid_t is complicated for two reasons: - * * Although uid_t is (always?) unsigned, it still - * accepts -1. - * * We don't know its size in advance--it may be - * bigger than an int, or it may be smaller than - * a long. - * - * So a bit of defensive programming is in order. - * Start with interpreting the value passed - * in as a signed long and see if it works. - */ - - result = PyLong_AsLongAndOverflow(index, &overflow); - - if (!overflow) { - uid = (uid_t)result; - - if (result == -1) { - if (PyErr_Occurred()) - goto fail; - /* It's a legitimate -1, we're done. */ - goto success; - } - - /* Any other negative number is disallowed. */ - if (result < 0) - goto underflow; - - /* Ensure the value wasn't truncated. */ - if (sizeof(uid_t) < sizeof(long) && - (long)uid != result) - goto underflow; - goto success; - } - - if (overflow < 0) - goto underflow; - - /* - * Okay, the value overflowed a signed long. If it - * fits in an *unsigned* long, it may still be okay, - * as uid_t may be unsigned long on this platform. - */ - uresult = PyLong_AsUnsignedLong(index); - if (PyErr_Occurred()) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) - goto overflow; - goto fail; - } - - uid = (uid_t)uresult; - - /* - * If uid == (uid_t)-1, the user actually passed in ULONG_MAX, - * but this value would get interpreted as (uid_t)-1 by chown - * and its siblings. That's not what the user meant! So we - * throw an overflow exception instead. (We already - * handled a real -1 with PyLong_AsLongAndOverflow() above.) - */ - if (uid == (uid_t)-1) - goto overflow; - - /* Ensure the value wasn't truncated. */ - if (sizeof(uid_t) < sizeof(long) && - (unsigned long)uid != uresult) - goto overflow; - /* fallthrough */ - -success: - Py_DECREF(index); - *p = uid; - return 1; - -underflow: - PyErr_SetString(PyExc_OverflowError, - "uid is less than minimum"); - goto fail; - -overflow: - PyErr_SetString(PyExc_OverflowError, - "uid is greater than maximum"); - /* fallthrough */ - -fail: - Py_DECREF(index); - return 0; -} - -int -_Py_Gid_Converter(PyObject *obj, gid_t *p) -{ - gid_t gid; - PyObject *index; - int overflow; - long result; - unsigned long uresult; - - index = _PyNumber_Index(obj); - if (index == NULL) { - PyErr_Format(PyExc_TypeError, - "gid should be integer, not %.200s", - _PyType_Name(Py_TYPE(obj))); - return 0; - } - - /* - * Handling gid_t is complicated for two reasons: - * * Although gid_t is (always?) unsigned, it still - * accepts -1. - * * We don't know its size in advance--it may be - * bigger than an int, or it may be smaller than - * a long. - * - * So a bit of defensive programming is in order. - * Start with interpreting the value passed - * in as a signed long and see if it works. - */ - - result = PyLong_AsLongAndOverflow(index, &overflow); - - if (!overflow) { - gid = (gid_t)result; - - if (result == -1) { - if (PyErr_Occurred()) - goto fail; - /* It's a legitimate -1, we're done. */ - goto success; - } - - /* Any other negative number is disallowed. */ - if (result < 0) { - goto underflow; - } - - /* Ensure the value wasn't truncated. */ - if (sizeof(gid_t) < sizeof(long) && - (long)gid != result) - goto underflow; - goto success; - } - - if (overflow < 0) - goto underflow; - - /* - * Okay, the value overflowed a signed long. If it - * fits in an *unsigned* long, it may still be okay, - * as gid_t may be unsigned long on this platform. - */ - uresult = PyLong_AsUnsignedLong(index); - if (PyErr_Occurred()) { - if (PyErr_ExceptionMatches(PyExc_OverflowError)) - goto overflow; - goto fail; - } - - gid = (gid_t)uresult; - - /* - * If gid == (gid_t)-1, the user actually passed in ULONG_MAX, - * but this value would get interpreted as (gid_t)-1 by chown - * and its siblings. That's not what the user meant! So we - * throw an overflow exception instead. (We already - * handled a real -1 with PyLong_AsLongAndOverflow() above.) - */ - if (gid == (gid_t)-1) - goto overflow; - - /* Ensure the value wasn't truncated. */ - if (sizeof(gid_t) < sizeof(long) && - (unsigned long)gid != uresult) - goto overflow; - /* fallthrough */ - -success: - Py_DECREF(index); - *p = gid; - return 1; - -underflow: - PyErr_SetString(PyExc_OverflowError, - "gid is less than minimum"); - goto fail; - -overflow: - PyErr_SetString(PyExc_OverflowError, - "gid is greater than maximum"); - /* fallthrough */ - -fail: - Py_DECREF(index); - return 0; -} -#endif /* MS_WINDOWS */ - - -static PyObject * -_PyLong_FromDev(dev_t dev) -{ -#ifdef NODEV - if (dev == NODEV) { - return PyLong_FromLongLong((long long)dev); - } -#endif - return PyLong_FromUnsignedLongLong((unsigned long long)dev); -} - - -#if (defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV)) || defined(HAVE_DEVICE_MACROS) -static int -_Py_Dev_Converter(PyObject *obj, void *p) -{ -#ifdef NODEV - if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) { - int overflow; - long long result = PyLong_AsLongLongAndOverflow(obj, &overflow); - if (result == -1 && PyErr_Occurred()) { - return 0; - } - if (!overflow && result == (long long)NODEV) { - *((dev_t *)p) = NODEV; - return 1; - } - } -#endif - - unsigned long long result = PyLong_AsUnsignedLongLong(obj); - if (result == (unsigned long long)-1 && PyErr_Occurred()) { - return 0; - } - if ((unsigned long long)(dev_t)result != result) { - PyErr_SetString(PyExc_OverflowError, - "Python int too large to convert to C dev_t"); - return 0; - } - *((dev_t *)p) = (dev_t)result; - return 1; -} -#endif /* (HAVE_MKNOD && HAVE_MAKEDEV) || HAVE_DEVICE_MACROS */ - - -#ifdef AT_FDCWD -/* - * Why the (int) cast? Solaris 10 defines AT_FDCWD as 0xffd19553 (-3041965); - * without the int cast, the value gets interpreted as uint (4291925331), - * which doesn't play nicely with all the initializer lines in this file that - * look like this: - * int dir_fd = DEFAULT_DIR_FD; - */ -#define DEFAULT_DIR_FD (int)AT_FDCWD -#else -#define DEFAULT_DIR_FD (-100) -#endif - -static int -_fd_converter(PyObject *o, int *p) -{ - int overflow; - long long_value; - - if (PyBool_Check(o)) { - if (PyErr_WarnEx(PyExc_RuntimeWarning, - "bool is used as a file descriptor", 1)) - { - return 0; - } - } - PyObject *index = _PyNumber_Index(o); - if (index == NULL) { - return 0; - } - - assert(PyLong_Check(index)); - long_value = PyLong_AsLongAndOverflow(index, &overflow); - Py_DECREF(index); - assert(!PyErr_Occurred()); - if (overflow > 0 || long_value > INT_MAX) { - PyErr_SetString(PyExc_OverflowError, - "fd is greater than maximum"); - return 0; - } - if (overflow < 0 || long_value < INT_MIN) { - PyErr_SetString(PyExc_OverflowError, - "fd is less than minimum"); - return 0; - } - - *p = (int)long_value; - return 1; -} - -static int -dir_fd_converter(PyObject *o, void *p) -{ - if (o == Py_None) { - *(int *)p = DEFAULT_DIR_FD; - return 1; - } - else if (PyIndex_Check(o)) { - return _fd_converter(o, (int *)p); - } - else { - PyErr_Format(PyExc_TypeError, - "argument should be integer or None, not %.200s", - _PyType_Name(Py_TYPE(o))); - return 0; - } -} - -typedef struct { - PyObject *billion; - PyObject *DirEntryType; - PyObject *ScandirIteratorType; -#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) - PyObject *SchedParamType; -#endif - newfunc statresult_new_orig; - PyObject *StatResultType; - PyObject *StatVFSResultType; - PyObject *TerminalSizeType; - PyObject *TimesResultType; - PyObject *UnameResultType; -#if defined(HAVE_WAITID) - PyObject *WaitidResultType; -#endif -#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4) - PyObject *struct_rusage; -#endif - PyObject *st_mode; -#ifndef MS_WINDOWS - // times() clock frequency in hertz; used by os.times() - long ticks_per_second; -#endif -} _posixstate; - - -static inline _posixstate* -get_posix_state(PyObject *module) -{ - void *state = _PyModule_GetState(module); - assert(state != NULL); - return (_posixstate *)state; -} - -/* - * A PyArg_ParseTuple "converter" function - * that handles filesystem paths in the manner - * preferred by the os module. - * - * path_converter accepts (Unicode) strings and their - * subclasses, and bytes and their subclasses. What - * it does with the argument depends on path.make_wide: - * - * * If path.make_wide is nonzero, if we get a (Unicode) - * string we extract the wchar_t * and return it; if we - * get bytes we decode to wchar_t * and return that. - * - * * If path.make_wide is zero, if we get bytes we extract - * the char_t * and return it; if we get a (Unicode) - * string we encode to char_t * and return that. - * - * path_converter also optionally accepts signed - * integers (representing open file descriptors) instead - * of path strings. - * - * Input fields: - * path.nullable - * If nonzero, the path is permitted to be None. - * path.nonstrict - * If nonzero, the path is permitted to contain - * embedded null characters and have any length. - * path.make_wide - * If nonzero, the converter always uses wide, decoding if necessary, else - * it always uses narrow, encoding if necessary. The default value is - * nonzero on Windows, else zero. - * path.suppress_value_error - * If nonzero, raising ValueError is suppressed. - * path.allow_fd - * If nonzero, the path is permitted to be a file handle - * (a signed int) instead of a string. - * path.function_name - * If non-NULL, path_converter will use that as the name - * of the function in error messages. - * (If path.function_name is NULL it omits the function name.) - * path.argument_name - * If non-NULL, path_converter will use that as the name - * of the parameter in error messages. - * (If path.argument_name is NULL it uses "path".) - * - * Output fields: - * path.wide - * Points to the path if it was expressed as Unicode - * or if it was bytes and decoded to Unicode. - * path.narrow - * Points to the path if it was expressed as bytes, - * or if it was Unicode and encoded to bytes. - * path.fd - * Contains a file descriptor if path.accept_fd was true - * and the caller provided a signed integer instead of any - * sort of string. - * - * WARNING: if your "path" parameter is optional, and is - * unspecified, path_converter will never get called. - * So if you set allow_fd, you *MUST* initialize path.fd = -1 - * yourself! - * path.value_error - * If nonzero, then suppress_value_error was specified and a ValueError - * occurred. - * path.length - * The length of the path in characters, if specified as - * a string. - * path.object - * The original object passed in (if get a PathLike object, - * the result of PyOS_FSPath() is treated as the original object). - * Own a reference to the object. - * path.cleanup - * For internal use only. May point to a temporary object. - * (Pay no attention to the man behind the curtain.) - * - * At most one of path.wide or path.narrow will be non-NULL. - * If path was None and path.nullable was set, - * or if path was an integer and path.allow_fd was set, - * both path.wide and path.narrow will be NULL - * and path.length will be 0. - * - * path_converter takes care to not write to the path_t - * unless it's successful. However it must reset the - * "cleanup" field each time it's called. - * - * Use as follows: - * path_t path; - * memset(&path, 0, sizeof(path)); - * PyArg_ParseTuple(args, "O&", path_converter, &path); - * // ... use values from path ... - * path_cleanup(&path); - * - * (Note that if PyArg_Parse fails you don't need to call - * path_cleanup(). However it is safe to do so.) - */ -typedef struct { - // Input fields - const char *function_name; - const char *argument_name; - int nullable; - int nonstrict; - int make_wide; - int suppress_value_error; - int allow_fd; - // Output fields - const wchar_t *wide; - const char *narrow; - int fd; - int value_error; - Py_ssize_t length; - PyObject *object; - PyObject *cleanup; -} path_t; - -#define PATH_T_INITIALIZE(function_name, argument_name, nullable, nonstrict, \ - make_wide, suppress_value_error, allow_fd) \ - {function_name, argument_name, nullable, nonstrict, make_wide, \ - suppress_value_error, allow_fd, NULL, NULL, -1, 0, 0, NULL, NULL} -#ifdef MS_WINDOWS -#define PATH_T_INITIALIZE_P(function_name, argument_name, nullable, \ - nonstrict, suppress_value_error, allow_fd) \ - PATH_T_INITIALIZE(function_name, argument_name, nullable, nonstrict, 1, \ - suppress_value_error, allow_fd) -#else -#define PATH_T_INITIALIZE_P(function_name, argument_name, nullable, \ - nonstrict, suppress_value_error, allow_fd) \ - PATH_T_INITIALIZE(function_name, argument_name, nullable, nonstrict, 0, \ - suppress_value_error, allow_fd) -#endif - -static void -path_cleanup(path_t *path) -{ - wchar_t *wide = (wchar_t *)path->wide; - path->wide = NULL; - PyMem_Free(wide); - Py_CLEAR(path->object); - Py_CLEAR(path->cleanup); -} - -static int -path_converter(PyObject *o, void *p) -{ - path_t *path = (path_t *)p; - PyObject *bytes = NULL; - Py_ssize_t length = 0; - int is_index, is_bytes, is_unicode; - const char *narrow; - PyObject *wo = NULL; - wchar_t *wide = NULL; - -#define FORMAT_EXCEPTION(exc, fmt) \ - PyErr_Format(exc, "%s%s" fmt, \ - path->function_name ? path->function_name : "", \ - path->function_name ? ": " : "", \ - path->argument_name ? path->argument_name : "path") - - /* Py_CLEANUP_SUPPORTED support */ - if (o == NULL) { - path_cleanup(path); - return 1; - } - - /* Ensure it's always safe to call path_cleanup(). */ - path->object = path->cleanup = NULL; - /* path->object owns a reference to the original object */ - Py_INCREF(o); - - if ((o == Py_None) && path->nullable) { - path->wide = NULL; - path->narrow = NULL; - path->fd = -1; - goto success_exit; - } - - /* Only call this here so that we don't treat the return value of - os.fspath() as an fd or buffer. */ - is_index = path->allow_fd && PyIndex_Check(o); - is_bytes = PyBytes_Check(o); - is_unicode = PyUnicode_Check(o); - - if (!is_index && !is_unicode && !is_bytes) { - /* Inline PyOS_FSPath() for better error messages. */ - PyObject *func, *res; - - func = _PyObject_LookupSpecial(o, &_Py_ID(__fspath__)); - if ((NULL == func) || (func == Py_None)) { - goto error_format; - } - res = _PyObject_CallNoArgs(func); - Py_DECREF(func); - if (NULL == res) { - goto error_exit; - } - else if (PyUnicode_Check(res)) { - is_unicode = 1; - } - else if (PyBytes_Check(res)) { - is_bytes = 1; - } - else { - PyErr_Format(PyExc_TypeError, - "expected %.200s.__fspath__() to return str or bytes, " - "not %.200s", _PyType_Name(Py_TYPE(o)), - _PyType_Name(Py_TYPE(res))); - Py_DECREF(res); - goto error_exit; - } - - /* still owns a reference to the original object */ - Py_SETREF(o, res); - } - - if (is_unicode) { - if (path->make_wide) { - wide = PyUnicode_AsWideCharString(o, &length); - if (!wide) { - goto error_exit; - } -#ifdef MS_WINDOWS - if (!path->nonstrict && length > 32767) { - FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows"); - goto error_exit; - } -#endif - if (!path->nonstrict && wcslen(wide) != (size_t)length) { - FORMAT_EXCEPTION(PyExc_ValueError, - "embedded null character in %s"); - goto error_exit; - } - - path->wide = wide; - path->narrow = NULL; - path->fd = -1; - wide = NULL; - goto success_exit; - } - bytes = PyUnicode_EncodeFSDefault(o); - if (!bytes) { - goto error_exit; - } - } - else if (is_bytes) { - bytes = Py_NewRef(o); - } - else if (is_index) { - if (!_fd_converter(o, &path->fd)) { - goto error_exit; - } - path->wide = NULL; - path->narrow = NULL; - goto success_exit; - } - else { - error_format: - PyErr_Format(PyExc_TypeError, "%s%s%s should be %s, not %.200s", - path->function_name ? path->function_name : "", - path->function_name ? ": " : "", - path->argument_name ? path->argument_name : "path", - path->allow_fd && path->nullable ? "string, bytes, os.PathLike, " - "integer or None" : - path->allow_fd ? "string, bytes, os.PathLike or integer" : - path->nullable ? "string, bytes, os.PathLike or None" : - "string, bytes or os.PathLike", - _PyType_Name(Py_TYPE(o))); - goto error_exit; - } - - length = PyBytes_GET_SIZE(bytes); - narrow = PyBytes_AS_STRING(bytes); - if (!path->nonstrict && strlen(narrow) != (size_t)length) { - FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s"); - goto error_exit; - } - - if (path->make_wide) { - wo = PyUnicode_DecodeFSDefaultAndSize(narrow, length); - if (!wo) { - goto error_exit; - } - - wide = PyUnicode_AsWideCharString(wo, &length); - Py_DECREF(wo); - if (!wide) { - goto error_exit; - } -#ifdef MS_WINDOWS - if (!path->nonstrict && length > 32767) { - FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows"); - goto error_exit; - } -#endif - if (!path->nonstrict && wcslen(wide) != (size_t)length) { - FORMAT_EXCEPTION(PyExc_ValueError, - "embedded null character in %s"); - goto error_exit; - } - path->wide = wide; - path->narrow = NULL; - Py_DECREF(bytes); - wide = NULL; - } - else { - path->wide = NULL; - path->narrow = narrow; - if (bytes == o) { - /* Still a reference owned by path->object, don't have to - worry about path->narrow is used after free. */ - Py_DECREF(bytes); - } - else { - path->cleanup = bytes; - } - } - path->fd = -1; - - success_exit: - path->value_error = 0; - path->length = length; - path->object = o; - return Py_CLEANUP_SUPPORTED; - - error_exit: - Py_XDECREF(o); - Py_XDECREF(bytes); - PyMem_Free(wide); - if (!path->suppress_value_error || - !PyErr_ExceptionMatches(PyExc_ValueError)) - { - return 0; - } - PyErr_Clear(); - path->wide = NULL; - path->narrow = NULL; - path->fd = -1; - path->value_error = 1; - path->length = 0; - path->object = NULL; - return Py_CLEANUP_SUPPORTED; -} - -static void -argument_unavailable_error(const char *function_name, const char *argument_name) -{ - PyErr_Format(PyExc_NotImplementedError, - "%s%s%s unavailable on this platform", - (function_name != NULL) ? function_name : "", - (function_name != NULL) ? ": ": "", - argument_name); -} - -static int -dir_fd_unavailable(PyObject *o, void *p) -{ - int dir_fd; - if (!dir_fd_converter(o, &dir_fd)) - return 0; - if (dir_fd != DEFAULT_DIR_FD) { - argument_unavailable_error(NULL, "dir_fd"); - return 0; - } - *(int *)p = dir_fd; - return 1; -} - -static int -fd_specified(const char *function_name, int fd) -{ - if (fd == -1) - return 0; - - argument_unavailable_error(function_name, "fd"); - return 1; -} - -static int -follow_symlinks_specified(const char *function_name, int follow_symlinks) -{ - if (follow_symlinks) - return 0; - - argument_unavailable_error(function_name, "follow_symlinks"); - return 1; -} - -static int -path_and_dir_fd_invalid(const char *function_name, path_t *path, int dir_fd) -{ - if (!path->wide && (dir_fd != DEFAULT_DIR_FD) && !path->narrow) { - PyErr_Format(PyExc_ValueError, - "%s: can't specify dir_fd without matching path", - function_name); - return 1; - } - return 0; -} - -static int -dir_fd_and_fd_invalid(const char *function_name, int dir_fd, int fd) -{ - if ((dir_fd != DEFAULT_DIR_FD) && (fd != -1)) { - PyErr_Format(PyExc_ValueError, - "%s: can't specify both dir_fd and fd", - function_name); - return 1; - } - return 0; -} - -static int -fd_and_follow_symlinks_invalid(const char *function_name, int fd, - int follow_symlinks) -{ - if ((fd > 0) && (!follow_symlinks)) { - PyErr_Format(PyExc_ValueError, - "%s: cannot use fd and follow_symlinks together", - function_name); - return 1; - } - return 0; -} - -static int -dir_fd_and_follow_symlinks_invalid(const char *function_name, int dir_fd, - int follow_symlinks) -{ - if ((dir_fd != DEFAULT_DIR_FD) && (!follow_symlinks)) { - PyErr_Format(PyExc_ValueError, - "%s: cannot use dir_fd and follow_symlinks together", - function_name); - return 1; - } - return 0; -} - -#ifdef MS_WINDOWS - typedef long long Py_off_t; -#else - typedef off_t Py_off_t; -#endif - -static int -Py_off_t_converter(PyObject *arg, void *addr) -{ -#ifdef HAVE_LARGEFILE_SUPPORT - *((Py_off_t *)addr) = PyLong_AsLongLong(arg); -#else - *((Py_off_t *)addr) = PyLong_AsLong(arg); -#endif - if (PyErr_Occurred()) - return 0; - return 1; -} - -static PyObject * -PyLong_FromPy_off_t(Py_off_t offset) -{ -#ifdef HAVE_LARGEFILE_SUPPORT - return PyLong_FromLongLong(offset); -#else - return PyLong_FromLong(offset); -#endif -} - -#ifdef HAVE_SIGSET_T -/* Convert an iterable of integers to a sigset. - Return 1 on success, return 0 and raise an exception on error. */ -int -_Py_Sigset_Converter(PyObject *obj, void *addr) -{ - sigset_t *mask = (sigset_t *)addr; - PyObject *iterator, *item; - long signum; - int overflow; - - // The extra parens suppress the unreachable-code warning with clang on MacOS - if (sigemptyset(mask) < (0)) { - /* Probably only if mask == NULL. */ - PyErr_SetFromErrno(PyExc_OSError); - return 0; - } - - iterator = PyObject_GetIter(obj); - if (iterator == NULL) { - return 0; - } - - while ((item = PyIter_Next(iterator)) != NULL) { - signum = PyLong_AsLongAndOverflow(item, &overflow); - Py_DECREF(item); - if (signum <= 0 || signum >= Py_NSIG) { - if (overflow || signum != -1 || !PyErr_Occurred()) { - PyErr_Format(PyExc_ValueError, - "signal number %ld out of range [1; %i]", - signum, Py_NSIG - 1); - } - goto error; - } - if (sigaddset(mask, (int)signum)) { - if (errno != EINVAL) { - /* Probably impossible */ - PyErr_SetFromErrno(PyExc_OSError); - goto error; - } - /* For backwards compatibility, allow idioms such as - * `range(1, NSIG)` but warn about invalid signal numbers - */ - const char msg[] = - "invalid signal number %ld, please use valid_signals()"; - if (PyErr_WarnFormat(PyExc_RuntimeWarning, 1, msg, signum)) { - goto error; - } - } - } - if (!PyErr_Occurred()) { - Py_DECREF(iterator); - return 1; - } - -error: - Py_DECREF(iterator); - return 0; -} -#endif /* HAVE_SIGSET_T */ - -/* Return a dictionary corresponding to the POSIX environment table */ -#if defined(WITH_NEXT_FRAMEWORK) || (defined(__APPLE__) && defined(Py_ENABLE_SHARED)) -/* On Darwin/MacOSX a shared library or framework has no access to -** environ directly, we must obtain it with _NSGetEnviron(). See also -** man environ(7). -*/ -#include -#define USE_DARWIN_NS_GET_ENVIRON 1 -#elif !defined(_MSC_VER) && (!defined(__WATCOMC__) || defined(__QNX__) || defined(__VXWORKS__)) -extern char **environ; -#endif /* !_MSC_VER */ - -static PyObject * -convertenviron(void) -{ - PyObject *d; -#ifdef MS_WINDOWS - wchar_t **e; -#else - char **e; -#endif - - d = PyDict_New(); - if (d == NULL) - return NULL; -#ifdef MS_WINDOWS - /* _wenviron must be initialized in this way if the program is started - through main() instead of wmain(). */ - (void)_wgetenv(L""); - e = _wenviron; -#elif defined(USE_DARWIN_NS_GET_ENVIRON) - /* environ is not accessible as an extern in a shared object on OSX; use - _NSGetEnviron to resolve it. The value changes if you add environment - variables between calls to Py_Initialize, so don't cache the value. */ - e = *_NSGetEnviron(); -#else - e = environ; -#endif - if (e == NULL) - return d; - for (; *e != NULL; e++) { - PyObject *k; - PyObject *v; -#ifdef MS_WINDOWS - const wchar_t *p = wcschr(*e, L'='); -#else - const char *p = strchr(*e, '='); -#endif - if (p == NULL) - continue; -#ifdef MS_WINDOWS - k = PyUnicode_FromWideChar(*e, (Py_ssize_t)(p-*e)); -#else - k = PyBytes_FromStringAndSize(*e, (int)(p-*e)); -#endif - if (k == NULL) { - Py_DECREF(d); - return NULL; - } -#ifdef MS_WINDOWS - v = PyUnicode_FromWideChar(p+1, wcslen(p+1)); -#else - v = PyBytes_FromStringAndSize(p+1, strlen(p+1)); -#endif - if (v == NULL) { - Py_DECREF(k); - Py_DECREF(d); - return NULL; - } - if (PyDict_SetDefaultRef(d, k, v, NULL) < 0) { - Py_DECREF(v); - Py_DECREF(k); - Py_DECREF(d); - return NULL; - } - Py_DECREF(k); - Py_DECREF(v); - } - return d; -} - -/* Set a POSIX-specific error from errno, and return NULL */ - -static PyObject * -posix_error(void) -{ - return PyErr_SetFromErrno(PyExc_OSError); -} - -#ifdef MS_WINDOWS -static PyObject * -win32_error(const char* function, const char* filename) -{ - /* XXX We should pass the function name along in the future. - (winreg.c also wants to pass the function name.) - This would however require an additional param to the - Windows error object, which is non-trivial. - */ - errno = GetLastError(); - if (filename) - return PyErr_SetFromWindowsErrWithFilename(errno, filename); - else - return PyErr_SetFromWindowsErr(errno); -} - -static PyObject * -win32_error_object_err(const char* function, PyObject* filename, DWORD err) -{ - /* XXX - see win32_error for comments on 'function' */ - if (filename) - return PyErr_SetExcFromWindowsErrWithFilenameObject( - PyExc_OSError, - err, - filename); - else - return PyErr_SetFromWindowsErr(err); -} - -static PyObject * -win32_error_object(const char* function, PyObject* filename) -{ - errno = GetLastError(); - return win32_error_object_err(function, filename, errno); -} - -#endif /* MS_WINDOWS */ - -static PyObject * -posix_path_object_error(PyObject *path) -{ - return PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path); -} - -static PyObject * -path_object_error(PyObject *path) -{ -#ifdef MS_WINDOWS - return PyErr_SetExcFromWindowsErrWithFilenameObject( - PyExc_OSError, 0, path); -#else - return posix_path_object_error(path); -#endif -} - -static PyObject * -path_object_error2(PyObject *path, PyObject *path2) -{ -#ifdef MS_WINDOWS - return PyErr_SetExcFromWindowsErrWithFilenameObjects( - PyExc_OSError, 0, path, path2); -#else - return PyErr_SetFromErrnoWithFilenameObjects(PyExc_OSError, path, path2); -#endif -} - -static PyObject * -path_error(path_t *path) -{ - return path_object_error(path->object); -} - -static PyObject * -posix_path_error(path_t *path) -{ - return posix_path_object_error(path->object); -} - -static PyObject * -path_error2(path_t *path, path_t *path2) -{ - return path_object_error2(path->object, path2->object); -} - - -/* POSIX generic methods */ - -static PyObject * -posix_fildes_fd(int fd, int (*func)(int)) -{ - int res; - int async_err = 0; - - do { - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH - res = (*func)(fd); - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - } while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - if (res != 0) - return (!async_err) ? posix_error() : NULL; - Py_RETURN_NONE; -} - - -#ifdef MS_WINDOWS -/* This is a reimplementation of the C library's chdir function, - but one that produces Win32 errors instead of DOS error codes. - chdir is essentially a wrapper around SetCurrentDirectory; however, - it also needs to set "magic" environment variables indicating - the per-drive current directory, which are of the form =: */ -static BOOL __stdcall -win32_wchdir(LPCWSTR path) -{ - wchar_t path_buf[MAX_PATH], *new_path = path_buf; - int result; - wchar_t env[4] = L"=x:"; - - if(!SetCurrentDirectoryW(path)) - return FALSE; - result = GetCurrentDirectoryW(Py_ARRAY_LENGTH(path_buf), new_path); - if (!result) - return FALSE; - if (result > Py_ARRAY_LENGTH(path_buf)) { - new_path = PyMem_RawMalloc(result * sizeof(wchar_t)); - if (!new_path) { - SetLastError(ERROR_OUTOFMEMORY); - return FALSE; - } - result = GetCurrentDirectoryW(result, new_path); - if (!result) { - PyMem_RawFree(new_path); - return FALSE; - } - } - int is_unc_like_path = (wcsncmp(new_path, L"\\\\", 2) == 0 || - wcsncmp(new_path, L"//", 2) == 0); - if (!is_unc_like_path) { - env[1] = new_path[0]; - result = SetEnvironmentVariableW(env, new_path); - } - if (new_path != path_buf) - PyMem_RawFree(new_path); - return result ? TRUE : FALSE; -} -#endif - -#ifdef MS_WINDOWS -/* The CRT of Windows has a number of flaws wrt. its stat() implementation: - - time stamps are restricted to second resolution - - file modification times suffer from forth-and-back conversions between - UTC and local time - Therefore, we implement our own stat, based on the Win32 API directly. -*/ -#define HAVE_STAT_NSEC 1 -#define HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES 1 -#define HAVE_STRUCT_STAT_ST_REPARSE_TAG 1 - -static void -find_data_to_file_info(WIN32_FIND_DATAW *pFileData, - BY_HANDLE_FILE_INFORMATION *info, - ULONG *reparse_tag) -{ - memset(info, 0, sizeof(*info)); - info->dwFileAttributes = pFileData->dwFileAttributes; - info->ftCreationTime = pFileData->ftCreationTime; - info->ftLastAccessTime = pFileData->ftLastAccessTime; - info->ftLastWriteTime = pFileData->ftLastWriteTime; - info->nFileSizeHigh = pFileData->nFileSizeHigh; - info->nFileSizeLow = pFileData->nFileSizeLow; -/* info->nNumberOfLinks = 1; */ - if (pFileData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) - *reparse_tag = pFileData->dwReserved0; - else - *reparse_tag = 0; -} - -static BOOL -attributes_from_dir(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION *info, ULONG *reparse_tag) -{ - HANDLE hFindFile; - WIN32_FIND_DATAW FileData; - LPCWSTR filename = pszFile; - size_t n = wcslen(pszFile); - if (n && (pszFile[n - 1] == L'\\' || pszFile[n - 1] == L'/')) { - // cannot use PyMem_Malloc here because we do not hold the GIL - filename = (LPCWSTR)malloc((n + 1) * sizeof(filename[0])); - if(!filename) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return FALSE; - } - wcsncpy_s((LPWSTR)filename, n + 1, pszFile, n); - while (--n > 0 && (filename[n] == L'\\' || filename[n] == L'/')) { - ((LPWSTR)filename)[n] = L'\0'; - } - if (!n || (n == 1 && filename[1] == L':')) { - // Nothing left to query - free((void *)filename); - return FALSE; - } - } - hFindFile = FindFirstFileW(filename, &FileData); - if (pszFile != filename) { - free((void *)filename); - } - if (hFindFile == INVALID_HANDLE_VALUE) { - return FALSE; - } - FindClose(hFindFile); - find_data_to_file_info(&FileData, info, reparse_tag); - return TRUE; -} - - -static void -update_st_mode_from_path(const wchar_t *path, DWORD attr, - struct _Py_stat_struct *result) -{ - if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) { - /* Fix the file execute permissions. This hack sets S_IEXEC if - the filename has an extension that is commonly used by files - that CreateProcessW can execute. A real implementation calls - GetSecurityInfo, OpenThreadToken/OpenProcessToken, and - AccessCheck to check for generic read, write, and execute - access. */ - const wchar_t *fileExtension = wcsrchr(path, '.'); - if (fileExtension) { - if (_wcsicmp(fileExtension, L".exe") == 0 || - _wcsicmp(fileExtension, L".bat") == 0 || - _wcsicmp(fileExtension, L".cmd") == 0 || - _wcsicmp(fileExtension, L".com") == 0) { - result->st_mode |= 0111; - } - } - } -} - - -static int -win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result, - BOOL traverse) -{ - HANDLE hFile; - BY_HANDLE_FILE_INFORMATION fileInfo; - FILE_BASIC_INFO basicInfo; - FILE_BASIC_INFO *pBasicInfo = NULL; - FILE_ID_INFO idInfo; - FILE_ID_INFO *pIdInfo = NULL; - FILE_ATTRIBUTE_TAG_INFO tagInfo = { 0 }; - DWORD fileType, error; - BOOL isUnhandledTag = FALSE; - int retval = 0; - - DWORD access = FILE_READ_ATTRIBUTES; - DWORD flags = FILE_FLAG_BACKUP_SEMANTICS; /* Allow opening directories. */ - if (!traverse) { - flags |= FILE_FLAG_OPEN_REPARSE_POINT; - } - - hFile = CreateFileW(path, access, 0, NULL, OPEN_EXISTING, flags, NULL); - if (hFile == INVALID_HANDLE_VALUE) { - /* Either the path doesn't exist, or the caller lacks access. */ - error = GetLastError(); - switch (error) { - case ERROR_ACCESS_DENIED: /* Cannot sync or read attributes. */ - case ERROR_SHARING_VIOLATION: /* It's a paging file. */ - /* Try reading the parent directory. */ - if (!attributes_from_dir(path, &fileInfo, &tagInfo.ReparseTag)) { - /* Cannot read the parent directory. */ - switch (GetLastError()) { - case ERROR_FILE_NOT_FOUND: /* File cannot be found */ - case ERROR_PATH_NOT_FOUND: /* File parent directory cannot be found */ - case ERROR_NOT_READY: /* Drive exists but unavailable */ - case ERROR_BAD_NET_NAME: /* Remote drive unavailable */ - break; - /* Restore the error from CreateFileW(). */ - default: - SetLastError(error); - } - - return -1; - } - if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - if (traverse || - !IsReparseTagNameSurrogate(tagInfo.ReparseTag)) { - /* The stat call has to traverse but cannot, so fail. */ - SetLastError(error); - return -1; - } - } - break; - - case ERROR_INVALID_PARAMETER: - /* \\.\con requires read or write access. */ - hFile = CreateFileW(path, access | GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, - OPEN_EXISTING, flags, NULL); - if (hFile == INVALID_HANDLE_VALUE) { - SetLastError(error); - return -1; - } - break; - - case ERROR_CANT_ACCESS_FILE: - /* bpo37834: open unhandled reparse points if traverse fails. */ - if (traverse) { - traverse = FALSE; - isUnhandledTag = TRUE; - hFile = CreateFileW(path, access, 0, NULL, OPEN_EXISTING, - flags | FILE_FLAG_OPEN_REPARSE_POINT, NULL); - } - if (hFile == INVALID_HANDLE_VALUE) { - SetLastError(error); - return -1; - } - break; - - default: - return -1; - } - } - - if (hFile != INVALID_HANDLE_VALUE) { - /* Handle types other than files on disk. */ - fileType = GetFileType(hFile); - if (fileType != FILE_TYPE_DISK) { - if (fileType == FILE_TYPE_UNKNOWN && GetLastError() != 0) { - retval = -1; - goto cleanup; - } - DWORD fileAttributes = GetFileAttributesW(path); - memset(result, 0, sizeof(*result)); - if (fileAttributes != INVALID_FILE_ATTRIBUTES && - fileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - /* \\.\pipe\ or \\.\mailslot\ */ - result->st_mode = _S_IFDIR; - } else if (fileType == FILE_TYPE_CHAR) { - /* \\.\nul */ - result->st_mode = _S_IFCHR; - } else if (fileType == FILE_TYPE_PIPE) { - /* \\.\pipe\spam */ - result->st_mode = _S_IFIFO; - } - /* FILE_TYPE_UNKNOWN, e.g. \\.\mailslot\waitfor.exe\spam */ - goto cleanup; - } - - /* Query the reparse tag, and traverse a non-link. */ - if (!traverse) { - if (!GetFileInformationByHandleEx(hFile, FileAttributeTagInfo, - &tagInfo, sizeof(tagInfo))) { - /* Allow devices that do not support FileAttributeTagInfo. */ - switch (GetLastError()) { - case ERROR_INVALID_PARAMETER: - case ERROR_INVALID_FUNCTION: - case ERROR_NOT_SUPPORTED: - tagInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; - tagInfo.ReparseTag = 0; - break; - default: - retval = -1; - goto cleanup; - } - } else if (tagInfo.FileAttributes & - FILE_ATTRIBUTE_REPARSE_POINT) { - if (IsReparseTagNameSurrogate(tagInfo.ReparseTag)) { - if (isUnhandledTag) { - /* Traversing previously failed for either this link - or its target. */ - SetLastError(ERROR_CANT_ACCESS_FILE); - retval = -1; - goto cleanup; - } - /* Traverse a non-link, but not if traversing already failed - for an unhandled tag. */ - } else if (!isUnhandledTag) { - CloseHandle(hFile); - return win32_xstat_slow_impl(path, result, TRUE); - } - } - } - - if (!GetFileInformationByHandle(hFile, &fileInfo) || - !GetFileInformationByHandleEx(hFile, FileBasicInfo, - &basicInfo, sizeof(basicInfo))) { - switch (GetLastError()) { - case ERROR_INVALID_PARAMETER: - case ERROR_INVALID_FUNCTION: - case ERROR_NOT_SUPPORTED: - /* Volumes and physical disks are block devices, e.g. - \\.\C: and \\.\PhysicalDrive0. */ - memset(result, 0, sizeof(*result)); - result->st_mode = 0x6000; /* S_IFBLK */ - goto cleanup; - } - retval = -1; - goto cleanup; - } - - /* Successfully got FileBasicInfo, so we'll pass it along */ - pBasicInfo = &basicInfo; - - if (GetFileInformationByHandleEx(hFile, FileIdInfo, &idInfo, sizeof(idInfo))) { - /* Successfully got FileIdInfo, so pass it along */ - pIdInfo = &idInfo; - } - } - - _Py_attribute_data_to_stat(&fileInfo, tagInfo.ReparseTag, pBasicInfo, pIdInfo, result); - update_st_mode_from_path(path, fileInfo.dwFileAttributes, result); - -cleanup: - if (hFile != INVALID_HANDLE_VALUE) { - /* Preserve last error if we are failing */ - error = retval ? GetLastError() : 0; - if (!CloseHandle(hFile)) { - retval = -1; - } else if (retval) { - /* Restore last error */ - SetLastError(error); - } - } - - return retval; -} - -static int -win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result, - BOOL traverse) -{ - FILE_STAT_BASIC_INFORMATION statInfo; - if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo, - &statInfo, sizeof(statInfo))) { - if (// Cannot use fast path for reparse points ... - !(statInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) - // ... unless it's a name surrogate (symlink) and we're not following - || (!traverse && IsReparseTagNameSurrogate(statInfo.ReparseTag)) - ) { - _Py_stat_basic_info_to_stat(&statInfo, result); - update_st_mode_from_path(path, statInfo.FileAttributes, result); - return 0; - } - } else { - switch(GetLastError()) { - case ERROR_FILE_NOT_FOUND: - case ERROR_PATH_NOT_FOUND: - case ERROR_NOT_READY: - case ERROR_BAD_NET_NAME: - /* These errors aren't worth retrying with the slow path */ - return -1; - case ERROR_NOT_SUPPORTED: - /* indicates the API couldn't be loaded */ - break; - } - } - - return win32_xstat_slow_impl(path, result, traverse); -} - -static int -win32_xstat(const wchar_t *path, struct _Py_stat_struct *result, BOOL traverse) -{ - /* Protocol violation: we explicitly clear errno, instead of - setting it to a POSIX error. Callers should use GetLastError. */ - int code = win32_xstat_impl(path, result, traverse); - errno = 0; - - /* ctime is only deprecated from 3.12, so we copy birthtime across */ - result->st_ctime = result->st_birthtime; - result->st_ctime_nsec = result->st_birthtime_nsec; - return code; -} -/* About the following functions: win32_lstat_w, win32_stat, win32_stat_w - - In Posix, stat automatically traverses symlinks and returns the stat - structure for the target. In Windows, the equivalent GetFileAttributes by - default does not traverse symlinks and instead returns attributes for - the symlink. - - Instead, we will open the file (which *does* traverse symlinks by default) - and GetFileInformationByHandle(). */ - -static int -win32_lstat(const wchar_t* path, struct _Py_stat_struct *result) -{ - return win32_xstat(path, result, FALSE); -} - -static int -win32_stat(const wchar_t* path, struct _Py_stat_struct *result) -{ - return win32_xstat(path, result, TRUE); -} - -#endif /* MS_WINDOWS */ - -PyDoc_STRVAR(stat_result__doc__, -"stat_result: Result from stat, fstat, or lstat.\n\n\ -This object may be accessed either as a tuple of\n\ - (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)\n\ -or via the attributes st_mode, st_ino, st_dev, st_nlink, st_uid, and so on.\n\ -\n\ -Posix/windows: If your platform supports st_blksize, st_blocks, st_rdev,\n\ -or st_flags, they are available as attributes only.\n\ -\n\ -See os.stat for more information."); - -static PyStructSequence_Field stat_result_fields[] = { - {"st_mode", "protection bits"}, - {"st_ino", "inode"}, - {"st_dev", "device"}, - {"st_nlink", "number of hard links"}, - {"st_uid", "user ID of owner"}, - {"st_gid", "group ID of owner"}, - {"st_size", "total size, in bytes"}, - /* The NULL is replaced with PyStructSequence_UnnamedField later. */ - {NULL, "integer time of last access"}, - {NULL, "integer time of last modification"}, - {NULL, "integer time of last change"}, - {"st_atime", "time of last access"}, - {"st_mtime", "time of last modification"}, - {"st_ctime", "time of last change"}, - {"st_atime_ns", "time of last access in nanoseconds"}, - {"st_mtime_ns", "time of last modification in nanoseconds"}, - {"st_ctime_ns", "time of last change in nanoseconds"}, -#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE - {"st_blksize", "blocksize for filesystem I/O"}, -#endif -#ifdef HAVE_STRUCT_STAT_ST_BLOCKS - {"st_blocks", "number of blocks allocated"}, -#endif -#ifdef HAVE_STRUCT_STAT_ST_RDEV - {"st_rdev", "device type (if inode device)"}, -#endif -#ifdef HAVE_STRUCT_STAT_ST_FLAGS - {"st_flags", "user defined flags for file"}, -#endif -#ifdef HAVE_STRUCT_STAT_ST_GEN - {"st_gen", "generation number"}, -#endif -#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) || defined(MS_WINDOWS) - {"st_birthtime", "time of creation"}, -#endif -#ifdef MS_WINDOWS - {"st_birthtime_ns", "time of creation in nanoseconds"}, -#endif -#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES - {"st_file_attributes", "Windows file attribute bits"}, -#endif -#ifdef HAVE_STRUCT_STAT_ST_FSTYPE - {"st_fstype", "Type of filesystem"}, -#endif -#ifdef HAVE_STRUCT_STAT_ST_REPARSE_TAG - {"st_reparse_tag", "Windows reparse tag"}, -#endif - {0} -}; - -#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE -#define ST_BLKSIZE_IDX 16 -#else -#define ST_BLKSIZE_IDX 15 -#endif - -#ifdef HAVE_STRUCT_STAT_ST_BLOCKS -#define ST_BLOCKS_IDX (ST_BLKSIZE_IDX+1) -#else -#define ST_BLOCKS_IDX ST_BLKSIZE_IDX -#endif - -#ifdef HAVE_STRUCT_STAT_ST_RDEV -#define ST_RDEV_IDX (ST_BLOCKS_IDX+1) -#else -#define ST_RDEV_IDX ST_BLOCKS_IDX -#endif - -#ifdef HAVE_STRUCT_STAT_ST_FLAGS -#define ST_FLAGS_IDX (ST_RDEV_IDX+1) -#else -#define ST_FLAGS_IDX ST_RDEV_IDX -#endif - -#ifdef HAVE_STRUCT_STAT_ST_GEN -#define ST_GEN_IDX (ST_FLAGS_IDX+1) -#else -#define ST_GEN_IDX ST_FLAGS_IDX -#endif - -#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) || defined(MS_WINDOWS) -#define ST_BIRTHTIME_IDX (ST_GEN_IDX+1) -#else -#define ST_BIRTHTIME_IDX ST_GEN_IDX -#endif - -#ifdef MS_WINDOWS -#define ST_BIRTHTIME_NS_IDX (ST_BIRTHTIME_IDX+1) -#else -#define ST_BIRTHTIME_NS_IDX ST_BIRTHTIME_IDX -#endif - -#if defined(HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES) || defined(MS_WINDOWS) -#define ST_FILE_ATTRIBUTES_IDX (ST_BIRTHTIME_NS_IDX+1) -#else -#define ST_FILE_ATTRIBUTES_IDX ST_BIRTHTIME_NS_IDX -#endif - -#ifdef HAVE_STRUCT_STAT_ST_FSTYPE -#define ST_FSTYPE_IDX (ST_FILE_ATTRIBUTES_IDX+1) -#else -#define ST_FSTYPE_IDX ST_FILE_ATTRIBUTES_IDX -#endif - -#ifdef HAVE_STRUCT_STAT_ST_REPARSE_TAG -#define ST_REPARSE_TAG_IDX (ST_FSTYPE_IDX+1) -#else -#define ST_REPARSE_TAG_IDX ST_FSTYPE_IDX -#endif - -static PyStructSequence_Desc stat_result_desc = { - "stat_result", /* name */ - stat_result__doc__, /* doc */ - stat_result_fields, - 10 -}; - -PyDoc_STRVAR(statvfs_result__doc__, -"statvfs_result: Result from statvfs or fstatvfs.\n\n\ -This object may be accessed either as a tuple of\n\ - (bsize, frsize, blocks, bfree, bavail, files, ffree, favail, flag, namemax),\n\ -or via the attributes f_bsize, f_frsize, f_blocks, f_bfree, and so on.\n\ -\n\ -See os.statvfs for more information."); - -static PyStructSequence_Field statvfs_result_fields[] = { - {"f_bsize", }, - {"f_frsize", }, - {"f_blocks", }, - {"f_bfree", }, - {"f_bavail", }, - {"f_files", }, - {"f_ffree", }, - {"f_favail", }, - {"f_flag", }, - {"f_namemax",}, - {"f_fsid", }, - {0} -}; - -static PyStructSequence_Desc statvfs_result_desc = { - "statvfs_result", /* name */ - statvfs_result__doc__, /* doc */ - statvfs_result_fields, - 10 -}; - -#if defined(HAVE_WAITID) -PyDoc_STRVAR(waitid_result__doc__, -"waitid_result: Result from waitid.\n\n\ -This object may be accessed either as a tuple of\n\ - (si_pid, si_uid, si_signo, si_status, si_code),\n\ -or via the attributes si_pid, si_uid, and so on.\n\ -\n\ -See os.waitid for more information."); - -static PyStructSequence_Field waitid_result_fields[] = { - {"si_pid", }, - {"si_uid", }, - {"si_signo", }, - {"si_status", }, - {"si_code", }, - {0} -}; - -static PyStructSequence_Desc waitid_result_desc = { - "waitid_result", /* name */ - waitid_result__doc__, /* doc */ - waitid_result_fields, - 5 -}; -#endif - -static PyObject * -statresult_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyStructSequence *result; - int i; - - // ht_module doesn't get set in PyStructSequence_NewType(), - // so we can't use PyType_GetModule(). - PyObject *mod = PyImport_GetModule(MODNAME_OBJ); - if (mod == NULL) { - return NULL; - } - _posixstate *state = get_posix_state(mod); - Py_DECREF(mod); - if (state == NULL) { - return NULL; - } -#define structseq_new state->statresult_new_orig - - result = (PyStructSequence*)structseq_new(type, args, kwds); - if (!result) - return NULL; - /* If we have been initialized from a tuple, - st_?time might be set to None. Initialize it - from the int slots. */ - for (i = 7; i <= 9; i++) { - if (result->ob_item[i+3] == Py_None) { - Py_DECREF(Py_None); - result->ob_item[i+3] = Py_NewRef(result->ob_item[i]); - } - } - return (PyObject*)result; -} - -static int -_posix_clear(PyObject *module) -{ - _posixstate *state = get_posix_state(module); - Py_CLEAR(state->billion); - Py_CLEAR(state->DirEntryType); - Py_CLEAR(state->ScandirIteratorType); -#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) - Py_CLEAR(state->SchedParamType); -#endif - Py_CLEAR(state->StatResultType); - Py_CLEAR(state->StatVFSResultType); - Py_CLEAR(state->TerminalSizeType); - Py_CLEAR(state->TimesResultType); - Py_CLEAR(state->UnameResultType); -#if defined(HAVE_WAITID) - Py_CLEAR(state->WaitidResultType); -#endif -#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4) - Py_CLEAR(state->struct_rusage); -#endif - Py_CLEAR(state->st_mode); - return 0; -} - -static int -_posix_traverse(PyObject *module, visitproc visit, void *arg) -{ - _posixstate *state = get_posix_state(module); - Py_VISIT(state->billion); - Py_VISIT(state->DirEntryType); - Py_VISIT(state->ScandirIteratorType); -#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) - Py_VISIT(state->SchedParamType); -#endif - Py_VISIT(state->StatResultType); - Py_VISIT(state->StatVFSResultType); - Py_VISIT(state->TerminalSizeType); - Py_VISIT(state->TimesResultType); - Py_VISIT(state->UnameResultType); -#if defined(HAVE_WAITID) - Py_VISIT(state->WaitidResultType); -#endif -#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4) - Py_VISIT(state->struct_rusage); -#endif - Py_VISIT(state->st_mode); - return 0; -} - -static void -_posix_free(void *module) -{ - _posix_clear((PyObject *)module); -} - -static int -fill_time(PyObject *module, PyObject *v, int s_index, int f_index, int ns_index, time_t sec, unsigned long nsec) -{ - assert(!PyErr_Occurred()); - - int res = -1; - PyObject *s_in_ns = NULL; - PyObject *ns_total = NULL; - PyObject *float_s = NULL; - - PyObject *s = _PyLong_FromTime_t(sec); - PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec); - if (!(s && ns_fractional)) { - goto exit; - } - - s_in_ns = PyNumber_Multiply(s, get_posix_state(module)->billion); - if (!s_in_ns) { - goto exit; - } - - ns_total = PyNumber_Add(s_in_ns, ns_fractional); - if (!ns_total) - goto exit; - - float_s = PyFloat_FromDouble(sec + 1e-9*nsec); - if (!float_s) { - goto exit; - } - - if (s_index >= 0) { - PyStructSequence_SET_ITEM(v, s_index, s); - s = NULL; - } - if (f_index >= 0) { - PyStructSequence_SET_ITEM(v, f_index, float_s); - float_s = NULL; - } - if (ns_index >= 0) { - PyStructSequence_SET_ITEM(v, ns_index, ns_total); - ns_total = NULL; - } - - assert(!PyErr_Occurred()); - res = 0; - -exit: - Py_XDECREF(s); - Py_XDECREF(ns_fractional); - Py_XDECREF(s_in_ns); - Py_XDECREF(ns_total); - Py_XDECREF(float_s); - return res; -} - -#ifdef MS_WINDOWS -static PyObject* -_pystat_l128_from_l64_l64(uint64_t low, uint64_t high) -{ - PyObject *o_low = PyLong_FromUnsignedLongLong(low); - if (!o_low || !high) { - return o_low; - } - PyObject *o_high = PyLong_FromUnsignedLongLong(high); - PyObject *l64 = o_high ? PyLong_FromLong(64) : NULL; - if (!l64) { - Py_XDECREF(o_high); - Py_DECREF(o_low); - return NULL; - } - Py_SETREF(o_high, PyNumber_Lshift(o_high, l64)); - Py_DECREF(l64); - if (!o_high) { - Py_DECREF(o_low); - return NULL; - } - Py_SETREF(o_low, PyNumber_Add(o_low, o_high)); - Py_DECREF(o_high); - return o_low; -} -#endif - -/* pack a system stat C structure into the Python stat tuple - (used by posix_stat() and posix_fstat()) */ -static PyObject* -_pystat_fromstructstat(PyObject *module, STRUCT_STAT *st) -{ - assert(!PyErr_Occurred()); - - PyObject *StatResultType = get_posix_state(module)->StatResultType; - PyObject *v = PyStructSequence_New((PyTypeObject *)StatResultType); - if (v == NULL) { - return NULL; - } - -#define SET_ITEM(pos, expr) \ - do { \ - PyObject *obj = (expr); \ - if (obj == NULL) { \ - goto error; \ - } \ - PyStructSequence_SET_ITEM(v, (pos), obj); \ - } while (0) - - SET_ITEM(0, PyLong_FromLong((long)st->st_mode)); -#ifdef MS_WINDOWS - SET_ITEM(1, _pystat_l128_from_l64_l64(st->st_ino, st->st_ino_high)); - SET_ITEM(2, PyLong_FromUnsignedLongLong(st->st_dev)); -#else - static_assert(sizeof(unsigned long long) >= sizeof(st->st_ino), - "stat.st_ino is larger than unsigned long long"); - SET_ITEM(1, PyLong_FromUnsignedLongLong(st->st_ino)); - SET_ITEM(2, _PyLong_FromDev(st->st_dev)); -#endif - SET_ITEM(3, PyLong_FromLong((long)st->st_nlink)); -#if defined(MS_WINDOWS) - SET_ITEM(4, PyLong_FromLong(0)); - SET_ITEM(5, PyLong_FromLong(0)); -#else - SET_ITEM(4, _PyLong_FromUid(st->st_uid)); - SET_ITEM(5, _PyLong_FromGid(st->st_gid)); -#endif - static_assert(sizeof(long long) >= sizeof(st->st_size), - "stat.st_size is larger than long long"); - SET_ITEM(6, PyLong_FromLongLong(st->st_size)); - - // Set st_atime, st_mtime and st_ctime - unsigned long ansec, mnsec, cnsec; -#if defined(HAVE_STAT_TV_NSEC) - ansec = st->st_atim.tv_nsec; - mnsec = st->st_mtim.tv_nsec; - cnsec = st->st_ctim.tv_nsec; -#elif defined(HAVE_STAT_TV_NSEC2) - ansec = st->st_atimespec.tv_nsec; - mnsec = st->st_mtimespec.tv_nsec; - cnsec = st->st_ctimespec.tv_nsec; -#elif defined(HAVE_STAT_NSEC) - ansec = st->st_atime_nsec; - mnsec = st->st_mtime_nsec; - cnsec = st->st_ctime_nsec; -#else - ansec = mnsec = cnsec = 0; -#endif - if (fill_time(module, v, 7, 10, 13, st->st_atime, ansec) < 0) { - goto error; - } - if (fill_time(module, v, 8, 11, 14, st->st_mtime, mnsec) < 0) { - goto error; - } - if (fill_time(module, v, 9, 12, 15, st->st_ctime, cnsec) < 0) { - goto error; - } - -#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE - SET_ITEM(ST_BLKSIZE_IDX, PyLong_FromLong((long)st->st_blksize)); -#endif -#ifdef HAVE_STRUCT_STAT_ST_BLOCKS - SET_ITEM(ST_BLOCKS_IDX, PyLong_FromLong((long)st->st_blocks)); -#endif -#ifdef HAVE_STRUCT_STAT_ST_RDEV - SET_ITEM(ST_RDEV_IDX, PyLong_FromLong((long)st->st_rdev)); -#endif -#ifdef HAVE_STRUCT_STAT_ST_GEN - SET_ITEM(ST_GEN_IDX, PyLong_FromLong((long)st->st_gen)); -#endif -#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) - { - unsigned long bsec, bnsec; - bsec = (long)st->st_birthtime; -#ifdef HAVE_STAT_TV_NSEC2 - bnsec = st->st_birthtimespec.tv_nsec; -#else - bnsec = 0; -#endif - SET_ITEM(ST_BIRTHTIME_IDX, PyFloat_FromDouble(bsec + bnsec * 1e-9)); - } -#elif defined(MS_WINDOWS) - if (fill_time(module, v, -1, ST_BIRTHTIME_IDX, ST_BIRTHTIME_NS_IDX, - st->st_birthtime, st->st_birthtime_nsec) < 0) { - goto error; - } -#endif -#ifdef HAVE_STRUCT_STAT_ST_FLAGS - SET_ITEM(ST_FLAGS_IDX, PyLong_FromLong((long)st->st_flags)); -#endif -#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES - SET_ITEM(ST_FILE_ATTRIBUTES_IDX, - PyLong_FromUnsignedLong(st->st_file_attributes)); -#endif -#ifdef HAVE_STRUCT_STAT_ST_FSTYPE - SET_ITEM(ST_FSTYPE_IDX, PyUnicode_FromString(st->st_fstype)); -#endif -#ifdef HAVE_STRUCT_STAT_ST_REPARSE_TAG - SET_ITEM(ST_REPARSE_TAG_IDX, PyLong_FromUnsignedLong(st->st_reparse_tag)); -#endif - - assert(!PyErr_Occurred()); - return v; - -error: - Py_DECREF(v); - return NULL; - -#undef SET_ITEM -} - -/* POSIX methods */ - - -static PyObject * -posix_do_stat(PyObject *module, const char *function_name, path_t *path, - int dir_fd, int follow_symlinks) -{ - STRUCT_STAT st; - int result; - -#ifdef HAVE_FSTATAT - int fstatat_unavailable = 0; -#endif - -#if !defined(MS_WINDOWS) && !defined(HAVE_FSTATAT) && !defined(HAVE_LSTAT) - if (follow_symlinks_specified(function_name, follow_symlinks)) - return NULL; -#endif - - if (path_and_dir_fd_invalid("stat", path, dir_fd) || - dir_fd_and_fd_invalid("stat", dir_fd, path->fd) || - fd_and_follow_symlinks_invalid("stat", path->fd, follow_symlinks)) - return NULL; - - Py_BEGIN_ALLOW_THREADS - if (path->fd != -1) - result = FSTAT(path->fd, &st); -#ifdef MS_WINDOWS - else if (follow_symlinks) - result = win32_stat(path->wide, &st); - else - result = win32_lstat(path->wide, &st); -#else - else -#if defined(HAVE_LSTAT) - if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD)) - result = LSTAT(path->narrow, &st); - else -#endif /* HAVE_LSTAT */ -#ifdef HAVE_FSTATAT - if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks) { - if (HAVE_FSTATAT_RUNTIME) { - result = fstatat(dir_fd, path->narrow, &st, - follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW); - - } else { - fstatat_unavailable = 1; - } - } else -#endif /* HAVE_FSTATAT */ - result = STAT(path->narrow, &st); -#endif /* MS_WINDOWS */ - Py_END_ALLOW_THREADS - -#ifdef HAVE_FSTATAT - if (fstatat_unavailable) { - argument_unavailable_error("stat", "dir_fd"); - return NULL; - } -#endif - - if (result != 0) { - return path_error(path); - } - - return _pystat_fromstructstat(module, &st); -} - -/*[python input] - -for s in """ - -FACCESSAT -FCHMODAT -FCHOWNAT -FSTATAT -LINKAT -MKDIRAT -MKFIFOAT -MKNODAT -OPENAT -READLINKAT -SYMLINKAT -UNLINKAT - -""".strip().split(): - s = s.strip() - print(""" -#ifdef HAVE_{s} - #define {s}_DIR_FD_CONVERTER dir_fd_converter -#else - #define {s}_DIR_FD_CONVERTER dir_fd_unavailable -#endif -""".rstrip().format(s=s)) - -for s in """ - -FCHDIR -FCHMOD -FCHOWN -FDOPENDIR -FEXECVE -FPATHCONF -FSTATVFS -FTRUNCATE - -""".strip().split(): - s = s.strip() - print(""" -#ifdef HAVE_{s} - #define PATH_HAVE_{s} 1 -#else - #define PATH_HAVE_{s} 0 -#endif - -""".rstrip().format(s=s)) -[python start generated code]*/ - -#ifdef HAVE_FACCESSAT - #define FACCESSAT_DIR_FD_CONVERTER dir_fd_converter -#else - #define FACCESSAT_DIR_FD_CONVERTER dir_fd_unavailable -#endif - -#ifdef HAVE_FCHMODAT - #define FCHMODAT_DIR_FD_CONVERTER dir_fd_converter -#else - #define FCHMODAT_DIR_FD_CONVERTER dir_fd_unavailable -#endif - -#ifdef HAVE_FCHOWNAT - #define FCHOWNAT_DIR_FD_CONVERTER dir_fd_converter -#else - #define FCHOWNAT_DIR_FD_CONVERTER dir_fd_unavailable -#endif - -#ifdef HAVE_FSTATAT - #define FSTATAT_DIR_FD_CONVERTER dir_fd_converter -#else - #define FSTATAT_DIR_FD_CONVERTER dir_fd_unavailable -#endif - -#ifdef HAVE_LINKAT - #define LINKAT_DIR_FD_CONVERTER dir_fd_converter -#else - #define LINKAT_DIR_FD_CONVERTER dir_fd_unavailable -#endif - -#ifdef HAVE_MKDIRAT - #define MKDIRAT_DIR_FD_CONVERTER dir_fd_converter -#else - #define MKDIRAT_DIR_FD_CONVERTER dir_fd_unavailable -#endif - -#ifdef HAVE_MKFIFOAT - #define MKFIFOAT_DIR_FD_CONVERTER dir_fd_converter -#else - #define MKFIFOAT_DIR_FD_CONVERTER dir_fd_unavailable -#endif - -#ifdef HAVE_MKNODAT - #define MKNODAT_DIR_FD_CONVERTER dir_fd_converter -#else - #define MKNODAT_DIR_FD_CONVERTER dir_fd_unavailable -#endif - -#ifdef HAVE_OPENAT - #define OPENAT_DIR_FD_CONVERTER dir_fd_converter -#else - #define OPENAT_DIR_FD_CONVERTER dir_fd_unavailable -#endif - -#ifdef HAVE_READLINKAT - #define READLINKAT_DIR_FD_CONVERTER dir_fd_converter -#else - #define READLINKAT_DIR_FD_CONVERTER dir_fd_unavailable -#endif - -#ifdef HAVE_SYMLINKAT - #define SYMLINKAT_DIR_FD_CONVERTER dir_fd_converter -#else - #define SYMLINKAT_DIR_FD_CONVERTER dir_fd_unavailable -#endif - -#ifdef HAVE_UNLINKAT - #define UNLINKAT_DIR_FD_CONVERTER dir_fd_converter -#else - #define UNLINKAT_DIR_FD_CONVERTER dir_fd_unavailable -#endif - -#ifdef HAVE_FCHDIR - #define PATH_HAVE_FCHDIR 1 -#else - #define PATH_HAVE_FCHDIR 0 -#endif - -#ifdef HAVE_FCHMOD - #define PATH_HAVE_FCHMOD 1 -#else - #define PATH_HAVE_FCHMOD 0 -#endif - -#ifdef HAVE_FCHOWN - #define PATH_HAVE_FCHOWN 1 -#else - #define PATH_HAVE_FCHOWN 0 -#endif - -#ifdef HAVE_FDOPENDIR - #define PATH_HAVE_FDOPENDIR 1 -#else - #define PATH_HAVE_FDOPENDIR 0 -#endif - -#ifdef HAVE_FEXECVE - #define PATH_HAVE_FEXECVE 1 -#else - #define PATH_HAVE_FEXECVE 0 -#endif - -#ifdef HAVE_FPATHCONF - #define PATH_HAVE_FPATHCONF 1 -#else - #define PATH_HAVE_FPATHCONF 0 -#endif - -#ifdef HAVE_FSTATVFS - #define PATH_HAVE_FSTATVFS 1 -#else - #define PATH_HAVE_FSTATVFS 0 -#endif - -#ifdef HAVE_FTRUNCATE - #define PATH_HAVE_FTRUNCATE 1 -#else - #define PATH_HAVE_FTRUNCATE 0 -#endif -/*[python end generated code: output=4bd4f6f7d41267f1 input=80b4c890b6774ea5]*/ - -#ifdef MS_WINDOWS - #undef PATH_HAVE_FTRUNCATE - #define PATH_HAVE_FTRUNCATE 1 - #undef PATH_HAVE_FCHMOD - #define PATH_HAVE_FCHMOD 1 -#endif - -/*[python input] - -class path_t_converter(CConverter): - - type = "path_t" - impl_by_reference = True - parse_by_reference = True - - converter = 'path_converter' - - def converter_init(self, *, allow_fd=False, make_wide=None, - nonstrict=False, nullable=False, - suppress_value_error=False): - # right now path_t doesn't support default values. - # to support a default value, you'll need to override initialize(). - if self.default not in (unspecified, None): - fail("Can't specify a default to the path_t converter!") - - if self.c_default not in (None, 'Py_None'): - raise RuntimeError("Can't specify a c_default to the path_t converter!") - - self.nullable = nullable - self.nonstrict = nonstrict - self.make_wide = make_wide - self.suppress_value_error = suppress_value_error - self.allow_fd = allow_fd - - def pre_render(self): - def strify(value): - if isinstance(value, str): - return value - return str(int(bool(value))) - - # add self.py_name here when merging with posixmodule conversion - if self.make_wide is None: - self.c_default = 'PATH_T_INITIALIZE_P("{}", "{}", {}, {}, {}, {})'.format( - self.function.name, - self.name, - strify(self.nullable), - strify(self.nonstrict), - strify(self.suppress_value_error), - strify(self.allow_fd), - ) - else: - self.c_default = 'PATH_T_INITIALIZE("{}", "{}", {}, {}, {}, {}, {})'.format( - self.function.name, - self.name, - strify(self.nullable), - strify(self.nonstrict), - strify(self.make_wide), - strify(self.suppress_value_error), - strify(self.allow_fd), - ) - - def cleanup(self): - return "path_cleanup(&" + self.name + ");\n" - - -class dir_fd_converter(CConverter): - type = 'int' - - def converter_init(self, requires=None): - if self.default in (unspecified, None): - self.c_default = 'DEFAULT_DIR_FD' - if isinstance(requires, str): - self.converter = requires.upper() + '_DIR_FD_CONVERTER' - else: - self.converter = 'dir_fd_converter' - -class uid_t_converter(CConverter): - type = "uid_t" - converter = '_Py_Uid_Converter' - -class gid_t_converter(CConverter): - type = "gid_t" - converter = '_Py_Gid_Converter' - -class dev_t_converter(CConverter): - type = 'dev_t' - converter = '_Py_Dev_Converter' - -class dev_t_return_converter(unsigned_long_return_converter): - type = 'dev_t' - conversion_fn = '_PyLong_FromDev' - unsigned_cast = '(dev_t)' - -class FSConverter_converter(CConverter): - type = 'PyObject *' - converter = 'PyUnicode_FSConverter' - def converter_init(self): - if self.default is not unspecified: - fail("FSConverter_converter does not support default values") - self.c_default = 'NULL' - - def cleanup(self): - return "Py_XDECREF(" + self.name + ");\n" - -class pid_t_converter(CConverter): - type = 'pid_t' - format_unit = '" _Py_PARSE_PID "' - -class idtype_t_converter(int_converter): - type = 'idtype_t' - -class id_t_converter(CConverter): - type = 'id_t' - format_unit = '" _Py_PARSE_PID "' - -class intptr_t_converter(CConverter): - type = 'intptr_t' - format_unit = '" _Py_PARSE_INTPTR "' - -class Py_off_t_converter(CConverter): - type = 'Py_off_t' - converter = 'Py_off_t_converter' - -class Py_off_t_return_converter(long_return_converter): - type = 'Py_off_t' - conversion_fn = 'PyLong_FromPy_off_t' - -class path_confname_converter(CConverter): - type="int" - converter="conv_path_confname" - -class confstr_confname_converter(path_confname_converter): - converter='conv_confstr_confname' - -class sysconf_confname_converter(path_confname_converter): - converter="conv_sysconf_confname" - -[python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=577cb476e5d64960]*/ - -/*[clinic input] - -os.stat - - path : path_t(allow_fd=True) - Path to be examined; can be string, bytes, a path-like object or - open-file-descriptor int. - - * - - dir_fd : dir_fd(requires='fstatat') = None - If not None, it should be a file descriptor open to a directory, - and path should be a relative string; path will then be relative to - that directory. - - follow_symlinks: bool = True - If False, and the last element of the path is a symbolic link, - stat will examine the symbolic link itself instead of the file - the link points to. - -Perform a stat system call on the given path. - -dir_fd and follow_symlinks may not be implemented - on your platform. If they are unavailable, using them will raise a - NotImplementedError. - -It's an error to use dir_fd or follow_symlinks when specifying path as - an open file descriptor. - -[clinic start generated code]*/ - -static PyObject * -os_stat_impl(PyObject *module, path_t *path, int dir_fd, int follow_symlinks) -/*[clinic end generated code: output=7d4976e6f18a59c5 input=01d362ebcc06996b]*/ -{ - return posix_do_stat(module, "stat", path, dir_fd, follow_symlinks); -} - - -/*[clinic input] -os.lstat - - path : path_t - - * - - dir_fd : dir_fd(requires='fstatat') = None - -Perform a stat system call on the given path, without following symbolic links. - -Like stat(), but do not follow symbolic links. -Equivalent to stat(path, follow_symlinks=False). -[clinic start generated code]*/ - -static PyObject * -os_lstat_impl(PyObject *module, path_t *path, int dir_fd) -/*[clinic end generated code: output=ef82a5d35ce8ab37 input=0b7474765927b925]*/ -{ - int follow_symlinks = 0; - return posix_do_stat(module, "lstat", path, dir_fd, follow_symlinks); -} - - -/*[clinic input] -os.access -> bool - - path: path_t - Path to be tested; can be string, bytes, or a path-like object. - - mode: int - Operating-system mode bitfield. Can be F_OK to test existence, - or the inclusive-OR of R_OK, W_OK, and X_OK. - - * - - dir_fd : dir_fd(requires='faccessat') = None - If not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that - directory. - - effective_ids: bool = False - If True, access will use the effective uid/gid instead of - the real uid/gid. - - follow_symlinks: bool = True - If False, and the last element of the path is a symbolic link, - access will examine the symbolic link itself instead of the file - the link points to. - -Use the real uid/gid to test for access to a path. - -{parameters} -dir_fd, effective_ids, and follow_symlinks may not be implemented - on your platform. If they are unavailable, using them will raise a - NotImplementedError. - -Note that most operations will use the effective uid/gid, therefore this - routine can be used in a suid/sgid environment to test if the invoking user - has the specified access to the path. - -[clinic start generated code]*/ - -static int -os_access_impl(PyObject *module, path_t *path, int mode, int dir_fd, - int effective_ids, int follow_symlinks) -/*[clinic end generated code: output=cf84158bc90b1a77 input=3ffe4e650ee3bf20]*/ -{ - int return_value; - -#ifdef MS_WINDOWS - DWORD attr; -#else - int result; -#endif - -#ifdef HAVE_FACCESSAT - int faccessat_unavailable = 0; -#endif - -#ifndef HAVE_FACCESSAT - if (follow_symlinks_specified("access", follow_symlinks)) - return -1; - - if (effective_ids) { - argument_unavailable_error("access", "effective_ids"); - return -1; - } -#endif - -#ifdef MS_WINDOWS - Py_BEGIN_ALLOW_THREADS - attr = GetFileAttributesW(path->wide); - Py_END_ALLOW_THREADS - - /* - * Access is possible if - * * we didn't get a -1, and - * * write access wasn't requested, - * * or the file isn't read-only, - * * or it's a directory. - * (Directories cannot be read-only on Windows.) - */ - return_value = (attr != INVALID_FILE_ATTRIBUTES) && - (!(mode & 2) || - !(attr & FILE_ATTRIBUTE_READONLY) || - (attr & FILE_ATTRIBUTE_DIRECTORY)); -#else - - Py_BEGIN_ALLOW_THREADS -#ifdef HAVE_FACCESSAT - if ((dir_fd != DEFAULT_DIR_FD) || - effective_ids || - !follow_symlinks) { - - if (HAVE_FACCESSAT_RUNTIME) { - int flags = 0; - if (!follow_symlinks) - flags |= AT_SYMLINK_NOFOLLOW; - if (effective_ids) - flags |= AT_EACCESS; - result = faccessat(dir_fd, path->narrow, mode, flags); - } else { - faccessat_unavailable = 1; - } - } - else -#endif - result = access(path->narrow, mode); - Py_END_ALLOW_THREADS - -#ifdef HAVE_FACCESSAT - if (faccessat_unavailable) { - if (dir_fd != DEFAULT_DIR_FD) { - argument_unavailable_error("access", "dir_fd"); - return -1; - } - if (follow_symlinks_specified("access", follow_symlinks)) - return -1; - - if (effective_ids) { - argument_unavailable_error("access", "effective_ids"); - return -1; - } - /* should be unreachable */ - return -1; - } -#endif - return_value = !result; -#endif - - return return_value; -} - -#ifndef F_OK -#define F_OK 0 -#endif -#ifndef R_OK -#define R_OK 4 -#endif -#ifndef W_OK -#define W_OK 2 -#endif -#ifndef X_OK -#define X_OK 1 -#endif - - -#ifdef HAVE_TTYNAME -/*[clinic input] -os.ttyname - - fd: int - Integer file descriptor handle. - - / - -Return the name of the terminal device connected to 'fd'. -[clinic start generated code]*/ - -static PyObject * -os_ttyname_impl(PyObject *module, int fd) -/*[clinic end generated code: output=c424d2e9d1cd636a input=9ff5a58b08115c55]*/ -{ - - long size = sysconf(_SC_TTY_NAME_MAX); - if (size == -1) { - return posix_error(); - } - char *buffer = (char *)PyMem_RawMalloc(size); - if (buffer == NULL) { - return PyErr_NoMemory(); - } - int ret = ttyname_r(fd, buffer, size); - if (ret != 0) { - PyMem_RawFree(buffer); - errno = ret; - return posix_error(); - } - PyObject *res = PyUnicode_DecodeFSDefault(buffer); - PyMem_RawFree(buffer); - return res; -} -#endif - -#ifdef HAVE_CTERMID -/*[clinic input] -os.ctermid - -Return the name of the controlling terminal for this process. -[clinic start generated code]*/ - -static PyObject * -os_ctermid_impl(PyObject *module) -/*[clinic end generated code: output=02f017e6c9e620db input=3b87fdd52556382d]*/ -{ - char *ret; - char buffer[L_ctermid]; - -#ifdef USE_CTERMID_R - ret = ctermid_r(buffer); -#else - ret = ctermid(buffer); -#endif - if (ret == NULL) - return posix_error(); - return PyUnicode_DecodeFSDefault(buffer); -} -#endif /* HAVE_CTERMID */ - - -/*[clinic input] -os.chdir - - path: path_t(allow_fd='PATH_HAVE_FCHDIR') - -Change the current working directory to the specified path. - -path may always be specified as a string. -On some platforms, path may also be specified as an open file descriptor. - If this functionality is unavailable, using it raises an exception. -[clinic start generated code]*/ - -static PyObject * -os_chdir_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=3be6400eee26eaae input=1a4a15b4d12cb15d]*/ -{ - int result; - - if (PySys_Audit("os.chdir", "(O)", path->object) < 0) { - return NULL; - } - - Py_BEGIN_ALLOW_THREADS -#ifdef MS_WINDOWS - /* on unix, success = 0, on windows, success = !0 */ - result = !win32_wchdir(path->wide); -#else -#ifdef HAVE_FCHDIR - if (path->fd != -1) - result = fchdir(path->fd); - else -#endif - result = chdir(path->narrow); -#endif - Py_END_ALLOW_THREADS - - if (result) { - return path_error(path); - } - - Py_RETURN_NONE; -} - - -#ifdef HAVE_FCHDIR -/*[clinic input] -os.fchdir - - fd: fildes - -Change to the directory of the given file descriptor. - -fd must be opened on a directory, not a file. -Equivalent to os.chdir(fd). - -[clinic start generated code]*/ - -static PyObject * -os_fchdir_impl(PyObject *module, int fd) -/*[clinic end generated code: output=42e064ec4dc00ab0 input=18e816479a2fa985]*/ -{ - if (PySys_Audit("os.chdir", "(i)", fd) < 0) { - return NULL; - } - return posix_fildes_fd(fd, fchdir); -} -#endif /* HAVE_FCHDIR */ - -#ifdef MS_WINDOWS -# define CHMOD_DEFAULT_FOLLOW_SYMLINKS 0 -#else -# define CHMOD_DEFAULT_FOLLOW_SYMLINKS 1 -#endif - -#ifdef MS_WINDOWS -static int -win32_lchmod(LPCWSTR path, int mode) -{ - DWORD attr = GetFileAttributesW(path); - if (attr == INVALID_FILE_ATTRIBUTES) { - return 0; - } - if (mode & _S_IWRITE) { - attr &= ~FILE_ATTRIBUTE_READONLY; - } - else { - attr |= FILE_ATTRIBUTE_READONLY; - } - return SetFileAttributesW(path, attr); -} - -static int -win32_hchmod(HANDLE hfile, int mode) -{ - FILE_BASIC_INFO info; - if (!GetFileInformationByHandleEx(hfile, FileBasicInfo, - &info, sizeof(info))) - { - return 0; - } - if (mode & _S_IWRITE) { - info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY; - } - else { - info.FileAttributes |= FILE_ATTRIBUTE_READONLY; - } - return SetFileInformationByHandle(hfile, FileBasicInfo, - &info, sizeof(info)); -} - -static int -win32_fchmod(int fd, int mode) -{ - HANDLE hfile = _Py_get_osfhandle_noraise(fd); - if (hfile == INVALID_HANDLE_VALUE) { - SetLastError(ERROR_INVALID_HANDLE); - return 0; - } - return win32_hchmod(hfile, mode); -} - -#endif /* MS_WINDOWS */ - -/*[clinic input] -os.chmod - - path: path_t(allow_fd='PATH_HAVE_FCHMOD') - Path to be modified. May always be specified as a str, bytes, or a path-like object. - On some platforms, path may also be specified as an open file descriptor. - If this functionality is unavailable, using it raises an exception. - - mode: int - Operating-system mode bitfield. - Be careful when using number literals for *mode*. The conventional UNIX notation for - numeric modes uses an octal base, which needs to be indicated with a ``0o`` prefix in - Python. - - * - - dir_fd : dir_fd(requires='fchmodat') = None - If not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that - directory. - - follow_symlinks: bool(c_default="CHMOD_DEFAULT_FOLLOW_SYMLINKS", \ - py_default="(os.name != 'nt')") = CHMOD_DEFAULT_FOLLOW_SYMLINKS - If False, and the last element of the path is a symbolic link, - chmod will modify the symbolic link itself instead of the file - the link points to. - -Change the access permissions of a file. - -It is an error to use dir_fd or follow_symlinks when specifying path as - an open file descriptor. -dir_fd and follow_symlinks may not be implemented on your platform. - If they are unavailable, using them will raise a NotImplementedError. - -[clinic start generated code]*/ - -static PyObject * -os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd, - int follow_symlinks) -/*[clinic end generated code: output=5cf6a94915cc7bff input=fcf115d174b9f3d8]*/ -{ - int result; - -#ifdef HAVE_FCHMODAT - int fchmodat_nofollow_unsupported = 0; - int fchmodat_unsupported = 0; -#endif - -#if !(defined(HAVE_FCHMODAT) || defined(HAVE_LCHMOD) || defined(MS_WINDOWS)) - if (follow_symlinks_specified("chmod", follow_symlinks)) - return NULL; -#endif - - if (PySys_Audit("os.chmod", "Oii", path->object, mode, - dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) { - return NULL; - } - -#ifdef MS_WINDOWS - result = 0; - Py_BEGIN_ALLOW_THREADS - if (path->fd != -1) { - result = win32_fchmod(path->fd, mode); - } - else if (follow_symlinks) { - HANDLE hfile = CreateFileW(path->wide, - FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES, - 0, NULL, - OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (hfile != INVALID_HANDLE_VALUE) { - result = win32_hchmod(hfile, mode); - (void)CloseHandle(hfile); - } - } - else { - result = win32_lchmod(path->wide, mode); - } - Py_END_ALLOW_THREADS - if (!result) { - return path_error(path); - } -#else /* MS_WINDOWS */ - Py_BEGIN_ALLOW_THREADS -#ifdef HAVE_FCHMOD - if (path->fd != -1) - result = fchmod(path->fd, mode); - else -#endif /* HAVE_CHMOD */ -#ifdef HAVE_LCHMOD - if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD)) - result = lchmod(path->narrow, mode); - else -#endif /* HAVE_LCHMOD */ -#ifdef HAVE_FCHMODAT - if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks) { - if (HAVE_FCHMODAT_RUNTIME) { - /* - * fchmodat() doesn't currently support AT_SYMLINK_NOFOLLOW! - * The documentation specifically shows how to use it, - * and then says it isn't implemented yet. - * (true on linux with glibc 2.15, and openindiana 3.x) - * - * Once it is supported, os.chmod will automatically - * support dir_fd and follow_symlinks=False. (Hopefully.) - * Until then, we need to be careful what exception we raise. - */ - result = fchmodat(dir_fd, path->narrow, mode, - follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW); - /* - * But wait! We can't throw the exception without allowing threads, - * and we can't do that in this nested scope. (Macro trickery, sigh.) - */ - fchmodat_nofollow_unsupported = - result && - ((errno == ENOTSUP) || (errno == EOPNOTSUPP)) && - !follow_symlinks; - } else { - fchmodat_unsupported = 1; - fchmodat_nofollow_unsupported = 1; - - result = -1; - } - } - else -#endif /* HAVE_FHCMODAT */ - { -#ifdef HAVE_CHMOD - result = chmod(path->narrow, mode); -#elif defined(__wasi__) - // WASI SDK 15.0 does not support chmod. - // Ignore missing syscall for now. - result = 0; -#else - result = -1; - errno = ENOSYS; -#endif - } - Py_END_ALLOW_THREADS - - if (result) { -#ifdef HAVE_FCHMODAT - if (fchmodat_unsupported) { - if (dir_fd != DEFAULT_DIR_FD) { - argument_unavailable_error("chmod", "dir_fd"); - return NULL; - } - } - - if (fchmodat_nofollow_unsupported) { - if (dir_fd != DEFAULT_DIR_FD) - dir_fd_and_follow_symlinks_invalid("chmod", - dir_fd, follow_symlinks); - else - follow_symlinks_specified("chmod", follow_symlinks); - return NULL; - } - else -#endif /* HAVE_FCHMODAT */ - return path_error(path); - } -#endif /* MS_WINDOWS */ - - Py_RETURN_NONE; -} - - -#if defined(HAVE_FCHMOD) || defined(MS_WINDOWS) -/*[clinic input] -os.fchmod - - fd: int - The file descriptor of the file to be modified. - mode: int - Operating-system mode bitfield. - Be careful when using number literals for *mode*. The conventional UNIX notation for - numeric modes uses an octal base, which needs to be indicated with a ``0o`` prefix in - Python. - -Change the access permissions of the file given by file descriptor fd. - -Equivalent to os.chmod(fd, mode). -[clinic start generated code]*/ - -static PyObject * -os_fchmod_impl(PyObject *module, int fd, int mode) -/*[clinic end generated code: output=afd9bc05b4e426b3 input=b5594618bbbc22df]*/ -{ - int res; - - if (PySys_Audit("os.chmod", "iii", fd, mode, -1) < 0) { - return NULL; - } - -#ifdef MS_WINDOWS - res = 0; - Py_BEGIN_ALLOW_THREADS - res = win32_fchmod(fd, mode); - Py_END_ALLOW_THREADS - if (!res) { - return PyErr_SetFromWindowsErr(0); - } -#else /* MS_WINDOWS */ - int async_err = 0; - do { - Py_BEGIN_ALLOW_THREADS - res = fchmod(fd, mode); - Py_END_ALLOW_THREADS - } while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - if (res != 0) - return (!async_err) ? posix_error() : NULL; -#endif /* MS_WINDOWS */ - - Py_RETURN_NONE; -} -#endif /* HAVE_FCHMOD || MS_WINDOWS */ - - -#if defined(HAVE_LCHMOD) || defined(MS_WINDOWS) -/*[clinic input] -os.lchmod - - path: path_t - mode: int - -Change the access permissions of a file, without following symbolic links. - -If path is a symlink, this affects the link itself rather than the target. -Equivalent to chmod(path, mode, follow_symlinks=False)." -[clinic start generated code]*/ - -static PyObject * -os_lchmod_impl(PyObject *module, path_t *path, int mode) -/*[clinic end generated code: output=082344022b51a1d5 input=90c5663c7465d24f]*/ -{ - int res; - if (PySys_Audit("os.chmod", "Oii", path->object, mode, -1) < 0) { - return NULL; - } -#ifdef MS_WINDOWS - Py_BEGIN_ALLOW_THREADS - res = win32_lchmod(path->wide, mode); - Py_END_ALLOW_THREADS - if (!res) { - path_error(path); - return NULL; - } -#else /* MS_WINDOWS */ - Py_BEGIN_ALLOW_THREADS - res = lchmod(path->narrow, mode); - Py_END_ALLOW_THREADS - if (res < 0) { - path_error(path); - return NULL; - } -#endif /* MS_WINDOWS */ - Py_RETURN_NONE; -} -#endif /* HAVE_LCHMOD || MS_WINDOWS */ - - -#ifdef HAVE_CHFLAGS -/*[clinic input] -os.chflags - - path: path_t - flags: unsigned_long(bitwise=True) - follow_symlinks: bool=True - -Set file flags. - -If follow_symlinks is False, and the last element of the path is a symbolic - link, chflags will change flags on the symbolic link itself instead of the - file the link points to. -follow_symlinks may not be implemented on your platform. If it is -unavailable, using it will raise a NotImplementedError. - -[clinic start generated code]*/ - -static PyObject * -os_chflags_impl(PyObject *module, path_t *path, unsigned long flags, - int follow_symlinks) -/*[clinic end generated code: output=85571c6737661ce9 input=0327e29feb876236]*/ -{ - int result; - -#ifndef HAVE_LCHFLAGS - if (follow_symlinks_specified("chflags", follow_symlinks)) - return NULL; -#endif - - if (PySys_Audit("os.chflags", "Ok", path->object, flags) < 0) { - return NULL; - } - - Py_BEGIN_ALLOW_THREADS -#ifdef HAVE_LCHFLAGS - if (!follow_symlinks) - result = lchflags(path->narrow, flags); - else -#endif - result = chflags(path->narrow, flags); - Py_END_ALLOW_THREADS - - if (result) - return path_error(path); - - Py_RETURN_NONE; -} -#endif /* HAVE_CHFLAGS */ - - -#ifdef HAVE_LCHFLAGS -/*[clinic input] -os.lchflags - - path: path_t - flags: unsigned_long(bitwise=True) - -Set file flags. - -This function will not follow symbolic links. -Equivalent to chflags(path, flags, follow_symlinks=False). -[clinic start generated code]*/ - -static PyObject * -os_lchflags_impl(PyObject *module, path_t *path, unsigned long flags) -/*[clinic end generated code: output=30ae958695c07316 input=f9f82ea8b585ca9d]*/ -{ - int res; - if (PySys_Audit("os.chflags", "Ok", path->object, flags) < 0) { - return NULL; - } - Py_BEGIN_ALLOW_THREADS - res = lchflags(path->narrow, flags); - Py_END_ALLOW_THREADS - if (res < 0) { - return path_error(path); - } - Py_RETURN_NONE; -} -#endif /* HAVE_LCHFLAGS */ - - -#ifdef HAVE_CHROOT -/*[clinic input] -os.chroot - path: path_t - -Change root directory to path. - -[clinic start generated code]*/ - -static PyObject * -os_chroot_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=de80befc763a4475 input=14822965652c3dc3]*/ -{ - int res; - Py_BEGIN_ALLOW_THREADS - res = chroot(path->narrow); - Py_END_ALLOW_THREADS - if (res < 0) - return path_error(path); - Py_RETURN_NONE; -} -#endif /* HAVE_CHROOT */ - - -#ifdef HAVE_FSYNC -/*[clinic input] -os.fsync - - fd: fildes - -Force write of fd to disk. -[clinic start generated code]*/ - -static PyObject * -os_fsync_impl(PyObject *module, int fd) -/*[clinic end generated code: output=4a10d773f52b3584 input=21c3645c056967f2]*/ -{ - return posix_fildes_fd(fd, fsync); -} -#endif /* HAVE_FSYNC */ - - -#ifdef HAVE_SYNC -/*[clinic input] -os.sync - -Force write of everything to disk. -[clinic start generated code]*/ - -static PyObject * -os_sync_impl(PyObject *module) -/*[clinic end generated code: output=2796b1f0818cd71c input=84749fe5e9b404ff]*/ -{ - Py_BEGIN_ALLOW_THREADS - sync(); - Py_END_ALLOW_THREADS - Py_RETURN_NONE; -} -#endif /* HAVE_SYNC */ - - -#ifdef HAVE_FDATASYNC -#ifdef __hpux -extern int fdatasync(int); /* On HP-UX, in libc but not in unistd.h */ -#endif - -/*[clinic input] -os.fdatasync - - fd: fildes - -Force write of fd to disk without forcing update of metadata. -[clinic start generated code]*/ - -static PyObject * -os_fdatasync_impl(PyObject *module, int fd) -/*[clinic end generated code: output=b4b9698b5d7e26dd input=bc74791ee54dd291]*/ -{ - return posix_fildes_fd(fd, fdatasync); -} -#endif /* HAVE_FDATASYNC */ - - -#ifdef HAVE_CHOWN -/*[clinic input] -os.chown - - path : path_t(allow_fd='PATH_HAVE_FCHOWN') - Path to be examined; can be string, bytes, a path-like object, or open-file-descriptor int. - - uid: uid_t - - gid: gid_t - - * - - dir_fd : dir_fd(requires='fchownat') = None - If not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that - directory. - - follow_symlinks: bool = True - If False, and the last element of the path is a symbolic link, - stat will examine the symbolic link itself instead of the file - the link points to. - -Change the owner and group id of path to the numeric uid and gid.\ - -path may always be specified as a string. -On some platforms, path may also be specified as an open file descriptor. - If this functionality is unavailable, using it raises an exception. -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. -If follow_symlinks is False, and the last element of the path is a symbolic - link, chown will modify the symbolic link itself instead of the file the - link points to. -It is an error to use dir_fd or follow_symlinks when specifying path as - an open file descriptor. -dir_fd and follow_symlinks may not be implemented on your platform. - If they are unavailable, using them will raise a NotImplementedError. - -[clinic start generated code]*/ - -static PyObject * -os_chown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid, - int dir_fd, int follow_symlinks) -/*[clinic end generated code: output=4beadab0db5f70cd input=b08c5ec67996a97d]*/ -{ - int result; - -#if defined(HAVE_FCHOWNAT) - int fchownat_unsupported = 0; -#endif - -#if !(defined(HAVE_LCHOWN) || defined(HAVE_FCHOWNAT)) - if (follow_symlinks_specified("chown", follow_symlinks)) - return NULL; -#endif - if (dir_fd_and_fd_invalid("chown", dir_fd, path->fd) || - fd_and_follow_symlinks_invalid("chown", path->fd, follow_symlinks)) - return NULL; - - if (PySys_Audit("os.chown", "OIIi", path->object, uid, gid, - dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) { - return NULL; - } - - Py_BEGIN_ALLOW_THREADS -#ifdef HAVE_FCHOWN - if (path->fd != -1) - result = fchown(path->fd, uid, gid); - else -#endif -#ifdef HAVE_LCHOWN - if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD)) - result = lchown(path->narrow, uid, gid); - else -#endif -#ifdef HAVE_FCHOWNAT - if ((dir_fd != DEFAULT_DIR_FD) || (!follow_symlinks)) { - if (HAVE_FCHOWNAT_RUNTIME) { - result = fchownat(dir_fd, path->narrow, uid, gid, - follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW); - } else { - fchownat_unsupported = 1; - } - } else -#endif - result = chown(path->narrow, uid, gid); - Py_END_ALLOW_THREADS - -#ifdef HAVE_FCHOWNAT - if (fchownat_unsupported) { - /* This would be incorrect if the current platform - * doesn't support lchown. - */ - argument_unavailable_error(NULL, "dir_fd"); - return NULL; - } -#endif - - if (result) - return path_error(path); - - Py_RETURN_NONE; -} -#endif /* HAVE_CHOWN */ - - -#ifdef HAVE_FCHOWN -/*[clinic input] -os.fchown - - fd: int - uid: uid_t - gid: gid_t - -Change the owner and group id of the file specified by file descriptor. - -Equivalent to os.chown(fd, uid, gid). - -[clinic start generated code]*/ - -static PyObject * -os_fchown_impl(PyObject *module, int fd, uid_t uid, gid_t gid) -/*[clinic end generated code: output=97d21cbd5a4350a6 input=3af544ba1b13a0d7]*/ -{ - int res; - int async_err = 0; - - if (PySys_Audit("os.chown", "iIIi", fd, uid, gid, -1) < 0) { - return NULL; - } - - do { - Py_BEGIN_ALLOW_THREADS - res = fchown(fd, uid, gid); - Py_END_ALLOW_THREADS - } while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - if (res != 0) - return (!async_err) ? posix_error() : NULL; - - Py_RETURN_NONE; -} -#endif /* HAVE_FCHOWN */ - - -#ifdef HAVE_LCHOWN -/*[clinic input] -os.lchown - - path : path_t - uid: uid_t - gid: gid_t - -Change the owner and group id of path to the numeric uid and gid. - -This function will not follow symbolic links. -Equivalent to os.chown(path, uid, gid, follow_symlinks=False). -[clinic start generated code]*/ - -static PyObject * -os_lchown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid) -/*[clinic end generated code: output=25eaf6af412fdf2f input=b1c6014d563a7161]*/ -{ - int res; - if (PySys_Audit("os.chown", "OIIi", path->object, uid, gid, -1) < 0) { - return NULL; - } - Py_BEGIN_ALLOW_THREADS - res = lchown(path->narrow, uid, gid); - Py_END_ALLOW_THREADS - if (res < 0) { - return path_error(path); - } - Py_RETURN_NONE; -} -#endif /* HAVE_LCHOWN */ - - -static PyObject * -posix_getcwd(int use_bytes) -{ -#ifdef MS_WINDOWS - wchar_t wbuf[MAXPATHLEN]; - wchar_t *wbuf2 = wbuf; - DWORD len; - - Py_BEGIN_ALLOW_THREADS - len = GetCurrentDirectoryW(Py_ARRAY_LENGTH(wbuf), wbuf); - /* If the buffer is large enough, len does not include the - terminating \0. If the buffer is too small, len includes - the space needed for the terminator. */ - if (len >= Py_ARRAY_LENGTH(wbuf)) { - if (len <= PY_SSIZE_T_MAX / sizeof(wchar_t)) { - wbuf2 = PyMem_RawMalloc(len * sizeof(wchar_t)); - } - else { - wbuf2 = NULL; - } - if (wbuf2) { - len = GetCurrentDirectoryW(len, wbuf2); - } - } - Py_END_ALLOW_THREADS - - if (!wbuf2) { - PyErr_NoMemory(); - return NULL; - } - if (!len) { - PyErr_SetFromWindowsErr(0); - if (wbuf2 != wbuf) - PyMem_RawFree(wbuf2); - return NULL; - } - - PyObject *resobj = PyUnicode_FromWideChar(wbuf2, len); - if (wbuf2 != wbuf) { - PyMem_RawFree(wbuf2); - } - - if (use_bytes) { - if (resobj == NULL) { - return NULL; - } - Py_SETREF(resobj, PyUnicode_EncodeFSDefault(resobj)); - } - - return resobj; -#else - const size_t chunk = 1024; - - char *buf = NULL; - char *cwd = NULL; - size_t buflen = 0; - - Py_BEGIN_ALLOW_THREADS - do { - char *newbuf; - if (buflen <= PY_SSIZE_T_MAX - chunk) { - buflen += chunk; - newbuf = PyMem_RawRealloc(buf, buflen); - } - else { - newbuf = NULL; - } - if (newbuf == NULL) { - PyMem_RawFree(buf); - buf = NULL; - break; - } - buf = newbuf; - - cwd = getcwd(buf, buflen); - } while (cwd == NULL && errno == ERANGE); - Py_END_ALLOW_THREADS - - if (buf == NULL) { - return PyErr_NoMemory(); - } - if (cwd == NULL) { - posix_error(); - PyMem_RawFree(buf); - return NULL; - } - - PyObject *obj; - if (use_bytes) { - obj = PyBytes_FromStringAndSize(buf, strlen(buf)); - } - else { - obj = PyUnicode_DecodeFSDefault(buf); - } -#ifdef __linux__ - if (buf[0] != '/') { - /* - * On Linux >= 2.6.36 with glibc < 2.27, getcwd() can return a - * relative pathname starting with '(unreachable)'. We detect this - * and fail with ENOENT, matching newer glibc behaviour. - */ - errno = ENOENT; - path_object_error(obj); - PyMem_RawFree(buf); - return NULL; - } -#endif - assert(buf[0] == '/'); - PyMem_RawFree(buf); - - return obj; -#endif /* !MS_WINDOWS */ -} - - -/*[clinic input] -os.getcwd - -Return a unicode string representing the current working directory. -[clinic start generated code]*/ - -static PyObject * -os_getcwd_impl(PyObject *module) -/*[clinic end generated code: output=21badfae2ea99ddc input=f069211bb70e3d39]*/ -{ - return posix_getcwd(0); -} - - -/*[clinic input] -os.getcwdb - -Return a bytes string representing the current working directory. -[clinic start generated code]*/ - -static PyObject * -os_getcwdb_impl(PyObject *module) -/*[clinic end generated code: output=3dd47909480e4824 input=f6f6a378dad3d9cb]*/ -{ - return posix_getcwd(1); -} - - -#if ((!defined(HAVE_LINK)) && defined(MS_WINDOWS)) -#define HAVE_LINK 1 -#endif - -#ifdef HAVE_LINK -/*[clinic input] - -os.link - - src : path_t - dst : path_t - * - src_dir_fd : dir_fd = None - dst_dir_fd : dir_fd = None - follow_symlinks: bool = True - -Create a hard link to a file. - -If either src_dir_fd or dst_dir_fd is not None, it should be a file - descriptor open to a directory, and the respective path string (src or dst) - should be relative; the path will then be relative to that directory. -If follow_symlinks is False, and the last element of src is a symbolic - link, link will create a link to the symbolic link itself instead of the - file the link points to. -src_dir_fd, dst_dir_fd, and follow_symlinks may not be implemented on your - platform. If they are unavailable, using them will raise a - NotImplementedError. -[clinic start generated code]*/ - -static PyObject * -os_link_impl(PyObject *module, path_t *src, path_t *dst, int src_dir_fd, - int dst_dir_fd, int follow_symlinks) -/*[clinic end generated code: output=7f00f6007fd5269a input=b0095ebbcbaa7e04]*/ -{ -#ifdef MS_WINDOWS - BOOL result = FALSE; -#else - int result; -#endif -#if defined(HAVE_LINKAT) - int linkat_unavailable = 0; -#endif - -#ifndef HAVE_LINKAT - if ((src_dir_fd != DEFAULT_DIR_FD) || (dst_dir_fd != DEFAULT_DIR_FD)) { - argument_unavailable_error("link", "src_dir_fd and dst_dir_fd"); - return NULL; - } -#endif - -#ifndef MS_WINDOWS - if ((src->narrow && dst->wide) || (src->wide && dst->narrow)) { - PyErr_SetString(PyExc_NotImplementedError, - "link: src and dst must be the same type"); - return NULL; - } -#endif - - if (PySys_Audit("os.link", "OOii", src->object, dst->object, - src_dir_fd == DEFAULT_DIR_FD ? -1 : src_dir_fd, - dst_dir_fd == DEFAULT_DIR_FD ? -1 : dst_dir_fd) < 0) { - return NULL; - } - -#ifdef MS_WINDOWS - Py_BEGIN_ALLOW_THREADS - result = CreateHardLinkW(dst->wide, src->wide, NULL); - Py_END_ALLOW_THREADS - - if (!result) - return path_error2(src, dst); -#else - Py_BEGIN_ALLOW_THREADS -#ifdef HAVE_LINKAT - if ((src_dir_fd != DEFAULT_DIR_FD) || - (dst_dir_fd != DEFAULT_DIR_FD) || - (!follow_symlinks)) { - - if (HAVE_LINKAT_RUNTIME) { - - result = linkat(src_dir_fd, src->narrow, - dst_dir_fd, dst->narrow, - follow_symlinks ? AT_SYMLINK_FOLLOW : 0); - - } -#ifdef __APPLE__ - else { - if (src_dir_fd == DEFAULT_DIR_FD && dst_dir_fd == DEFAULT_DIR_FD) { - /* See issue 41355: This matches the behaviour of !HAVE_LINKAT */ - result = link(src->narrow, dst->narrow); - } else { - linkat_unavailable = 1; - } - } -#endif - } - else -#endif /* HAVE_LINKAT */ - result = link(src->narrow, dst->narrow); - Py_END_ALLOW_THREADS - -#ifdef HAVE_LINKAT - if (linkat_unavailable) { - /* Either or both dir_fd arguments were specified */ - if (src_dir_fd != DEFAULT_DIR_FD) { - argument_unavailable_error("link", "src_dir_fd"); - } else { - argument_unavailable_error("link", "dst_dir_fd"); - } - return NULL; - } -#endif - - if (result) - return path_error2(src, dst); -#endif /* MS_WINDOWS */ - - Py_RETURN_NONE; -} -#endif - - -#if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR) -static PyObject * -_listdir_windows_no_opendir(path_t *path, PyObject *list) -{ - PyObject *v; - HANDLE hFindFile = INVALID_HANDLE_VALUE; - BOOL result, return_bytes; - wchar_t namebuf[MAX_PATH+4]; /* Overallocate for "\*.*" */ - /* only claim to have space for MAX_PATH */ - Py_ssize_t len = Py_ARRAY_LENGTH(namebuf)-4; - wchar_t *wnamebuf = NULL; - - WIN32_FIND_DATAW wFileData; - const wchar_t *po_wchars; - - if (!path->wide) { /* Default arg: "." */ - po_wchars = L"."; - len = 1; - return_bytes = 0; - } else { - po_wchars = path->wide; - len = wcslen(path->wide); - return_bytes = PyBytes_Check(path->object); - } - /* The +5 is so we can append "\\*.*\0" */ - wnamebuf = PyMem_New(wchar_t, len + 5); - if (!wnamebuf) { - PyErr_NoMemory(); - goto exit; - } - wcscpy(wnamebuf, po_wchars); - if (len > 0) { - wchar_t wch = wnamebuf[len-1]; - if (wch != SEP && wch != ALTSEP && wch != L':') - wnamebuf[len++] = SEP; - wcscpy(wnamebuf + len, L"*.*"); - } - if ((list = PyList_New(0)) == NULL) { - goto exit; - } - Py_BEGIN_ALLOW_THREADS - hFindFile = FindFirstFileW(wnamebuf, &wFileData); - Py_END_ALLOW_THREADS - if (hFindFile == INVALID_HANDLE_VALUE) { - int error = GetLastError(); - if (error == ERROR_FILE_NOT_FOUND) - goto exit; - path_error(path); - Py_CLEAR(list); - goto exit; - } - do { - /* Skip over . and .. */ - if (wcscmp(wFileData.cFileName, L".") != 0 && - wcscmp(wFileData.cFileName, L"..") != 0) { - v = PyUnicode_FromWideChar(wFileData.cFileName, - wcslen(wFileData.cFileName)); - if (return_bytes && v) { - Py_SETREF(v, PyUnicode_EncodeFSDefault(v)); - } - if (v == NULL) { - Py_CLEAR(list); - break; - } - if (PyList_Append(list, v) != 0) { - Py_DECREF(v); - Py_CLEAR(list); - break; - } - Py_DECREF(v); - } - Py_BEGIN_ALLOW_THREADS - result = FindNextFileW(hFindFile, &wFileData); - Py_END_ALLOW_THREADS - /* FindNextFile sets error to ERROR_NO_MORE_FILES if - it got to the end of the directory. */ - if (!result && GetLastError() != ERROR_NO_MORE_FILES) { - path_error(path); - Py_CLEAR(list); - goto exit; - } - } while (result == TRUE); - -exit: - if (hFindFile != INVALID_HANDLE_VALUE) { - if (FindClose(hFindFile) == FALSE) { - if (list != NULL) { - path_error(path); - Py_CLEAR(list); - } - } - } - PyMem_Free(wnamebuf); - - return list; -} /* end of _listdir_windows_no_opendir */ - -#else /* thus POSIX, ie: not (MS_WINDOWS and not HAVE_OPENDIR) */ - -static PyObject * -_posix_listdir(path_t *path, PyObject *list) -{ - PyObject *v; - DIR *dirp = NULL; - struct dirent *ep; - int return_str; /* if false, return bytes */ -#ifdef HAVE_FDOPENDIR - int fd = -1; -#endif - - errno = 0; -#ifdef HAVE_FDOPENDIR - if (path->fd != -1) { - if (HAVE_FDOPENDIR_RUNTIME) { - /* closedir() closes the FD, so we duplicate it */ - fd = _Py_dup(path->fd); - if (fd == -1) - return NULL; - - return_str = 1; - - Py_BEGIN_ALLOW_THREADS - dirp = fdopendir(fd); - Py_END_ALLOW_THREADS - } else { - PyErr_SetString(PyExc_TypeError, - "listdir: path should be string, bytes, os.PathLike or None, not int"); - return NULL; - } - } - else -#endif - { - const char *name; - if (path->narrow) { - name = path->narrow; - /* only return bytes if they specified a bytes object */ - return_str = !PyBytes_Check(path->object); - } - else { - name = "."; - return_str = 1; - } - - Py_BEGIN_ALLOW_THREADS - dirp = opendir(name); - Py_END_ALLOW_THREADS - } - - if (dirp == NULL) { - path_error(path); - list = NULL; -#ifdef HAVE_FDOPENDIR - if (fd != -1) { - Py_BEGIN_ALLOW_THREADS - close(fd); - Py_END_ALLOW_THREADS - } -#endif - goto exit; - } - if ((list = PyList_New(0)) == NULL) { - goto exit; - } - for (;;) { - errno = 0; - Py_BEGIN_ALLOW_THREADS - ep = readdir(dirp); - Py_END_ALLOW_THREADS - if (ep == NULL) { - if (errno == 0) { - break; - } else { - path_error(path); - Py_CLEAR(list); - goto exit; - } - } - if (ep->d_name[0] == '.' && - (NAMLEN(ep) == 1 || - (ep->d_name[1] == '.' && NAMLEN(ep) == 2))) - continue; - if (return_str) - v = PyUnicode_DecodeFSDefaultAndSize(ep->d_name, NAMLEN(ep)); - else - v = PyBytes_FromStringAndSize(ep->d_name, NAMLEN(ep)); - if (v == NULL) { - Py_CLEAR(list); - break; - } - if (PyList_Append(list, v) != 0) { - Py_DECREF(v); - Py_CLEAR(list); - break; - } - Py_DECREF(v); - } - -exit: - if (dirp != NULL) { - Py_BEGIN_ALLOW_THREADS -#ifdef HAVE_FDOPENDIR - if (fd > -1) - rewinddir(dirp); -#endif - closedir(dirp); - Py_END_ALLOW_THREADS - } - - return list; -} /* end of _posix_listdir */ -#endif /* which OS */ - - -/*[clinic input] -os.listdir - - path : path_t(nullable=True, allow_fd='PATH_HAVE_FDOPENDIR') = None - -Return a list containing the names of the files in the directory. - -path can be specified as either str, bytes, or a path-like object. If path is bytes, - the filenames returned will also be bytes; in all other circumstances - the filenames returned will be str. -If path is None, uses the path='.'. -On some platforms, path may also be specified as an open file descriptor;\ - the file descriptor must refer to a directory. - If this functionality is unavailable, using it raises NotImplementedError. - -The list is in arbitrary order. It does not include the special -entries '.' and '..' even if they are present in the directory. - - -[clinic start generated code]*/ - -static PyObject * -os_listdir_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=293045673fcd1a75 input=e3f58030f538295d]*/ -{ - if (PySys_Audit("os.listdir", "O", - path->object ? path->object : Py_None) < 0) { - return NULL; - } -#if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR) - return _listdir_windows_no_opendir(path, NULL); -#else - return _posix_listdir(path, NULL); -#endif -} - - -#ifdef MS_WINDOWS - -/*[clinic input] -os.listdrives - -Return a list containing the names of drives in the system. - -A drive name typically looks like 'C:\\'. - -[clinic start generated code]*/ - -static PyObject * -os_listdrives_impl(PyObject *module) -/*[clinic end generated code: output=aaece9dacdf682b5 input=1af9ccc9e583798e]*/ -{ - /* Number of possible drives is limited, so 256 should always be enough. - On the day when it is not, listmounts() will have to be used. */ - wchar_t buffer[256]; - DWORD buflen = Py_ARRAY_LENGTH(buffer); - PyObject *result = NULL; - if (PySys_Audit("os.listdrives", NULL) < 0) { - return NULL; - } - - Py_BEGIN_ALLOW_THREADS; - buflen = GetLogicalDriveStringsW(buflen, buffer); - Py_END_ALLOW_THREADS; - - if (!buflen) { - PyErr_SetFromWindowsErr(0); - return NULL; - } else if (buflen >= Py_ARRAY_LENGTH(buffer)) { - PyErr_SetFromWindowsErr(ERROR_MORE_DATA); - return NULL; - } - - /* buflen includes a null terminator, so remove it */ - PyObject *str = PyUnicode_FromWideChar(buffer, buflen - 1); - if (str) { - PyObject *nullchar = PyUnicode_FromStringAndSize("\0", 1); - if (nullchar) { - result = PyUnicode_Split(str, nullchar, -1); - Py_DECREF(nullchar); - } - Py_DECREF(str); - } - return result; -} - -/*[clinic input] -os.listvolumes - -Return a list containing the volumes in the system. - -Volumes are typically represented as a GUID path. - -[clinic start generated code]*/ - -static PyObject * -os_listvolumes_impl(PyObject *module) -/*[clinic end generated code: output=534e10ea2bf9d386 input=f6e4e70371f11e99]*/ -{ - PyObject *result = PyList_New(0); - HANDLE find = INVALID_HANDLE_VALUE; - wchar_t buffer[MAX_PATH + 1]; - if (!result) { - return NULL; - } - if (PySys_Audit("os.listvolumes", NULL) < 0) { - Py_DECREF(result); - return NULL; - } - - int err = 0; - Py_BEGIN_ALLOW_THREADS; - find = FindFirstVolumeW(buffer, Py_ARRAY_LENGTH(buffer)); - if (find == INVALID_HANDLE_VALUE) { - err = GetLastError(); - } - Py_END_ALLOW_THREADS; - - while (!err) { - PyObject *s = PyUnicode_FromWideChar(buffer, -1); - if (!s || PyList_Append(result, s) < 0) { - Py_XDECREF(s); - Py_CLEAR(result); - break; - } - Py_DECREF(s); - - Py_BEGIN_ALLOW_THREADS; - if (!FindNextVolumeW(find, buffer, Py_ARRAY_LENGTH(buffer))) { - err = GetLastError(); - } - Py_END_ALLOW_THREADS; - } - - if (find != INVALID_HANDLE_VALUE) { - Py_BEGIN_ALLOW_THREADS; - FindVolumeClose(find); - Py_END_ALLOW_THREADS; - } - if (err && err != ERROR_NO_MORE_FILES) { - PyErr_SetFromWindowsErr(err); - Py_XDECREF(result); - result = NULL; - } - return result; -} - - -/*[clinic input] -os.listmounts - - volume: path_t - -Return a list containing mount points for a particular volume. - -'volume' should be a GUID path as returned from os.listvolumes. - -[clinic start generated code]*/ - -static PyObject * -os_listmounts_impl(PyObject *module, path_t *volume) -/*[clinic end generated code: output=06da49679de4512e input=a8a27178e3f67845]*/ -{ - wchar_t default_buffer[MAX_PATH + 1]; - DWORD buflen = Py_ARRAY_LENGTH(default_buffer); - LPWSTR buffer = default_buffer; - DWORD attributes; - PyObject *str = NULL; - PyObject *nullchar = NULL; - PyObject *result = NULL; - - /* Ensure we have a valid volume path before continuing */ - Py_BEGIN_ALLOW_THREADS - attributes = GetFileAttributesW(volume->wide); - Py_END_ALLOW_THREADS - if (attributes == INVALID_FILE_ATTRIBUTES && - GetLastError() == ERROR_UNRECOGNIZED_VOLUME) - { - return PyErr_SetFromWindowsErr(ERROR_UNRECOGNIZED_VOLUME); - } - - if (PySys_Audit("os.listmounts", "O", volume->object) < 0) { - return NULL; - } - - while (1) { - BOOL success; - Py_BEGIN_ALLOW_THREADS - success = GetVolumePathNamesForVolumeNameW(volume->wide, buffer, - buflen, &buflen); - Py_END_ALLOW_THREADS - if (success) { - break; - } - if (GetLastError() != ERROR_MORE_DATA) { - PyErr_SetFromWindowsErr(0); - goto exit; - } - if (buffer != default_buffer) { - PyMem_Free((void *)buffer); - } - buffer = (wchar_t*)PyMem_Malloc(sizeof(wchar_t) * buflen); - if (!buffer) { - PyErr_NoMemory(); - goto exit; - } - } - if (buflen < 2) { - result = PyList_New(0); - goto exit; - } - // buflen includes two null terminators, one for the last string - // and one for the array of strings. - str = PyUnicode_FromWideChar(buffer, buflen - 2); - nullchar = PyUnicode_FromStringAndSize("\0", 1); - if (str && nullchar) { - result = PyUnicode_Split(str, nullchar, -1); - } -exit: - if (buffer != default_buffer) { - PyMem_Free(buffer); - } - Py_XDECREF(nullchar); - Py_XDECREF(str); - return result; -} - - -/*[clinic input] -os._path_isdevdrive - - path: path_t - -Determines whether the specified path is on a Windows Dev Drive. - -[clinic start generated code]*/ - -static PyObject * -os__path_isdevdrive_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=1f437ea6677433a2 input=ee83e4996a48e23d]*/ -{ -#ifndef PERSISTENT_VOLUME_STATE_DEV_VOLUME - /* This flag will be documented at - https://learn.microsoft.com/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_fs_persistent_volume_information - after release, and will be available in the latest WinSDK. - We include the flag to avoid a specific version dependency - on the latest WinSDK. */ - const int PERSISTENT_VOLUME_STATE_DEV_VOLUME = 0x00002000; -#endif - int err = 0; - PyObject *r = NULL; - wchar_t volume[MAX_PATH]; - - Py_BEGIN_ALLOW_THREADS - if (!GetVolumePathNameW(path->wide, volume, MAX_PATH)) { - /* invalid path of some kind */ - /* Note that this also includes the case where a volume is mounted - in a path longer than 260 characters. This is likely to be rare - and problematic for other reasons, so a (soft) failure in this - check seems okay. */ - err = GetLastError(); - } else if (GetDriveTypeW(volume) != DRIVE_FIXED) { - /* only care about local dev drives */ - r = Py_False; - } else { - HANDLE hVolume = CreateFileW( - volume, - FILE_READ_ATTRIBUTES, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, - NULL - ); - if (hVolume == INVALID_HANDLE_VALUE) { - err = GetLastError(); - } else { - FILE_FS_PERSISTENT_VOLUME_INFORMATION volumeState = {0}; - volumeState.Version = 1; - volumeState.FlagMask = PERSISTENT_VOLUME_STATE_DEV_VOLUME; - if (!DeviceIoControl( - hVolume, - FSCTL_QUERY_PERSISTENT_VOLUME_STATE, - &volumeState, - sizeof(volumeState), - &volumeState, - sizeof(volumeState), - NULL, - NULL - )) { - err = GetLastError(); - } - CloseHandle(hVolume); - if (err == ERROR_INVALID_PARAMETER) { - /* not supported on this platform */ - r = Py_False; - } else if (!err) { - r = (volumeState.VolumeFlags & PERSISTENT_VOLUME_STATE_DEV_VOLUME) - ? Py_True : Py_False; - } - } - } - Py_END_ALLOW_THREADS - - if (err) { - PyErr_SetFromWindowsErr(err); - return NULL; - } - - if (r) { - return Py_NewRef(r); - } - - return NULL; -} - - -int -_PyOS_getfullpathname(const wchar_t *path, wchar_t **abspath_p) -{ - wchar_t woutbuf[MAX_PATH], *woutbufp = woutbuf; - DWORD result; - - result = GetFullPathNameW(path, - Py_ARRAY_LENGTH(woutbuf), woutbuf, - NULL); - if (!result) { - return -1; - } - - if (result >= Py_ARRAY_LENGTH(woutbuf)) { - if ((size_t)result <= (size_t)PY_SSIZE_T_MAX / sizeof(wchar_t)) { - woutbufp = PyMem_RawMalloc((size_t)result * sizeof(wchar_t)); - } - else { - woutbufp = NULL; - } - if (!woutbufp) { - *abspath_p = NULL; - return 0; - } - - result = GetFullPathNameW(path, result, woutbufp, NULL); - if (!result) { - PyMem_RawFree(woutbufp); - return -1; - } - } - - if (woutbufp != woutbuf) { - *abspath_p = woutbufp; - return 0; - } - - *abspath_p = _PyMem_RawWcsdup(woutbufp); - return 0; -} - - -/* A helper function for abspath on win32 */ -/*[clinic input] -os._getfullpathname - - path: path_t - / - -[clinic start generated code]*/ - -static PyObject * -os__getfullpathname_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=bb8679d56845bc9b input=332ed537c29d0a3e]*/ -{ - wchar_t *abspath; - - if (_PyOS_getfullpathname(path->wide, &abspath) < 0) { - return win32_error_object("GetFullPathNameW", path->object); - } - if (abspath == NULL) { - return PyErr_NoMemory(); - } - - PyObject *str = PyUnicode_FromWideChar(abspath, wcslen(abspath)); - PyMem_RawFree(abspath); - if (str == NULL) { - return NULL; - } - if (PyBytes_Check(path->object)) { - Py_SETREF(str, PyUnicode_EncodeFSDefault(str)); - } - return str; -} - - -/*[clinic input] -os._getfinalpathname - - path: path_t - / - -A helper function for samepath on windows. -[clinic start generated code]*/ - -static PyObject * -os__getfinalpathname_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=621a3c79bc29ebfa input=2b6b6c7cbad5fb84]*/ -{ - HANDLE hFile; - wchar_t buf[MAXPATHLEN], *target_path = buf; - int buf_size = Py_ARRAY_LENGTH(buf); - int result_length; - PyObject *result; - - Py_BEGIN_ALLOW_THREADS - hFile = CreateFileW( - path->wide, - 0, /* desired access */ - 0, /* share mode */ - NULL, /* security attributes */ - OPEN_EXISTING, - /* FILE_FLAG_BACKUP_SEMANTICS is required to open a directory */ - FILE_FLAG_BACKUP_SEMANTICS, - NULL); - Py_END_ALLOW_THREADS - - if (hFile == INVALID_HANDLE_VALUE) { - return win32_error_object("CreateFileW", path->object); - } - - /* We have a good handle to the target, use it to determine the - target path name. */ - while (1) { - Py_BEGIN_ALLOW_THREADS - result_length = GetFinalPathNameByHandleW(hFile, target_path, - buf_size, VOLUME_NAME_DOS); - Py_END_ALLOW_THREADS - - if (!result_length) { - result = win32_error_object("GetFinalPathNameByHandleW", - path->object); - goto cleanup; - } - - if (result_length < buf_size) { - break; - } - - wchar_t *tmp; - tmp = PyMem_Realloc(target_path != buf ? target_path : NULL, - result_length * sizeof(*tmp)); - if (!tmp) { - result = PyErr_NoMemory(); - goto cleanup; - } - - buf_size = result_length; - target_path = tmp; - } - - result = PyUnicode_FromWideChar(target_path, result_length); - if (result && PyBytes_Check(path->object)) { - Py_SETREF(result, PyUnicode_EncodeFSDefault(result)); - } - -cleanup: - if (target_path != buf) { - PyMem_Free(target_path); - } - CloseHandle(hFile); - return result; -} - -/*[clinic input] -os._findfirstfile - path: path_t - / -A function to get the real file name without accessing the file in Windows. -[clinic start generated code]*/ - -static PyObject * -os__findfirstfile_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=106dd3f0779c83dd input=0734dff70f60e1a8]*/ -{ - PyObject *result; - HANDLE hFindFile; - WIN32_FIND_DATAW wFileData; - WCHAR *wRealFileName; - - Py_BEGIN_ALLOW_THREADS - hFindFile = FindFirstFileW(path->wide, &wFileData); - Py_END_ALLOW_THREADS - - if (hFindFile == INVALID_HANDLE_VALUE) { - path_error(path); - return NULL; - } - - wRealFileName = wFileData.cFileName; - result = PyUnicode_FromWideChar(wRealFileName, wcslen(wRealFileName)); - FindClose(hFindFile); - return result; -} - - -/*[clinic input] -os._getvolumepathname - - path: path_t - -A helper function for ismount on Win32. -[clinic start generated code]*/ - -static PyObject * -os__getvolumepathname_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=804c63fd13a1330b input=722b40565fa21552]*/ -{ - PyObject *result; - wchar_t *mountpath=NULL; - size_t buflen; - BOOL ret; - - /* Volume path should be shorter than entire path */ - buflen = Py_MAX(path->length, MAX_PATH); - - if (buflen > PY_DWORD_MAX) { - PyErr_SetString(PyExc_OverflowError, "path too long"); - return NULL; - } - - mountpath = PyMem_New(wchar_t, buflen); - if (mountpath == NULL) - return PyErr_NoMemory(); - - Py_BEGIN_ALLOW_THREADS - ret = GetVolumePathNameW(path->wide, mountpath, - Py_SAFE_DOWNCAST(buflen, size_t, DWORD)); - Py_END_ALLOW_THREADS - - if (!ret) { - result = win32_error_object("_getvolumepathname", path->object); - goto exit; - } - result = PyUnicode_FromWideChar(mountpath, wcslen(mountpath)); - if (PyBytes_Check(path->object)) - Py_SETREF(result, PyUnicode_EncodeFSDefault(result)); - -exit: - PyMem_Free(mountpath); - return result; -} - - -/*[clinic input] -os._path_splitroot - - path: path_t - -Removes everything after the root on Win32. -[clinic start generated code]*/ - -static PyObject * -os__path_splitroot_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=ab7f1a88b654581c input=dc93b1d3984cffb6]*/ -{ - wchar_t *buffer; - wchar_t *end; - PyObject *result = NULL; - HRESULT ret; - - buffer = (wchar_t*)PyMem_Malloc(sizeof(wchar_t) * (wcslen(path->wide) + 1)); - if (!buffer) { - return NULL; - } - wcscpy(buffer, path->wide); - for (wchar_t *p = wcschr(buffer, L'/'); p; p = wcschr(p, L'/')) { - *p = L'\\'; - } - - Py_BEGIN_ALLOW_THREADS - ret = PathCchSkipRoot(buffer, &end); - Py_END_ALLOW_THREADS - if (FAILED(ret)) { - result = Py_BuildValue("sO", "", path->object); - } else if (end != buffer) { - size_t rootLen = (size_t)(end - buffer); - result = Py_BuildValue("NN", - PyUnicode_FromWideChar(path->wide, rootLen), - PyUnicode_FromWideChar(path->wide + rootLen, -1) - ); - } else { - result = Py_BuildValue("Os", path->object, ""); - } - PyMem_Free(buffer); - - return result; -} - - -#define PY_IFREG 1 // Regular file -#define PY_IFDIR 2 // Directory -#define PY_IFLNK 4 // Symlink -#define PY_IFMNT 8 // Mount Point (junction) -#define PY_IFLRP 16 // Link Reparse Point (name-surrogate, symlink, junction) -#define PY_IFRRP 32 // Regular Reparse Point - -static inline BOOL -_testInfo(DWORD attributes, DWORD reparseTag, BOOL diskDevice, int testedType) -{ - switch (testedType) { - case PY_IFREG: - return diskDevice && attributes && - !(attributes & FILE_ATTRIBUTE_DIRECTORY); - case PY_IFDIR: - return attributes & FILE_ATTRIBUTE_DIRECTORY; - case PY_IFLNK: - return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) && - reparseTag == IO_REPARSE_TAG_SYMLINK; - case PY_IFMNT: - return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) && - reparseTag == IO_REPARSE_TAG_MOUNT_POINT; - case PY_IFLRP: - return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) && - IsReparseTagNameSurrogate(reparseTag); - case PY_IFRRP: - return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) && - reparseTag && !IsReparseTagNameSurrogate(reparseTag); - } - - return FALSE; -} - -static BOOL -_testFileTypeByHandle(HANDLE hfile, int testedType, BOOL diskOnly) -{ - assert(testedType == PY_IFREG || testedType == PY_IFDIR || - testedType == PY_IFLNK || testedType == PY_IFMNT || - testedType == PY_IFLRP || testedType == PY_IFRRP); - - BOOL diskDevice = GetFileType(hfile) == FILE_TYPE_DISK; - if (diskOnly && !diskDevice) { - return FALSE; - } - if (testedType != PY_IFREG && testedType != PY_IFDIR) { - FILE_ATTRIBUTE_TAG_INFO info; - return GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &info, - sizeof(info)) && - _testInfo(info.FileAttributes, info.ReparseTag, diskDevice, - testedType); - } - FILE_BASIC_INFO info; - return GetFileInformationByHandleEx(hfile, FileBasicInfo, &info, - sizeof(info)) && - _testInfo(info.FileAttributes, 0, diskDevice, testedType); -} - -static BOOL -_testFileTypeByName(LPCWSTR path, int testedType) -{ - assert(testedType == PY_IFREG || testedType == PY_IFDIR || - testedType == PY_IFLNK || testedType == PY_IFMNT || - testedType == PY_IFLRP || testedType == PY_IFRRP); - - FILE_STAT_BASIC_INFORMATION info; - if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo, &info, - sizeof(info))) - { - BOOL diskDevice = info.DeviceType == FILE_DEVICE_DISK || - info.DeviceType == FILE_DEVICE_VIRTUAL_DISK || - info.DeviceType == FILE_DEVICE_CD_ROM; - BOOL result = _testInfo(info.FileAttributes, info.ReparseTag, - diskDevice, testedType); - if (!result || (testedType != PY_IFREG && testedType != PY_IFDIR) || - !(info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) - { - return result; - } - } - else if (_Py_GetFileInformationByName_ErrorIsTrustworthy( - GetLastError())) - { - return FALSE; - } - - DWORD flags = FILE_FLAG_BACKUP_SEMANTICS; - if (testedType != PY_IFREG && testedType != PY_IFDIR) { - flags |= FILE_FLAG_OPEN_REPARSE_POINT; - } - HANDLE hfile = CreateFileW(path, FILE_READ_ATTRIBUTES, 0, NULL, - OPEN_EXISTING, flags, NULL); - if (hfile != INVALID_HANDLE_VALUE) { - BOOL result = _testFileTypeByHandle(hfile, testedType, FALSE); - CloseHandle(hfile); - return result; - } - - switch (GetLastError()) { - case ERROR_ACCESS_DENIED: - case ERROR_SHARING_VIOLATION: - case ERROR_CANT_ACCESS_FILE: - case ERROR_INVALID_PARAMETER: - int rc; - STRUCT_STAT st; - if (testedType == PY_IFREG || testedType == PY_IFDIR) { - rc = STAT(path, &st); - } - else { - // PY_IFRRP is not generally supported in this case, except for - // unhandled reparse points such as IO_REPARSE_TAG_APPEXECLINK. - rc = LSTAT(path, &st); - } - if (!rc) { - return _testInfo(st.st_file_attributes, st.st_reparse_tag, - st.st_mode & S_IFREG, testedType); - } - } - - return FALSE; -} - - -static BOOL -_testFileExistsByName(LPCWSTR path, BOOL followLinks) -{ - FILE_STAT_BASIC_INFORMATION info; - if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo, &info, - sizeof(info))) - { - if (!(info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) || - !followLinks && IsReparseTagNameSurrogate(info.ReparseTag)) - { - return TRUE; - } - } - else if (_Py_GetFileInformationByName_ErrorIsTrustworthy( - GetLastError())) - { - return FALSE; - } - - DWORD flags = FILE_FLAG_BACKUP_SEMANTICS; - if (!followLinks) { - flags |= FILE_FLAG_OPEN_REPARSE_POINT; - } - HANDLE hfile = CreateFileW(path, FILE_READ_ATTRIBUTES, 0, NULL, - OPEN_EXISTING, flags, NULL); - if (hfile != INVALID_HANDLE_VALUE) { - if (followLinks) { - CloseHandle(hfile); - return TRUE; - } - // Regular Reparse Points (PY_IFRRP) have to be traversed. - BOOL result = _testFileTypeByHandle(hfile, PY_IFRRP, FALSE); - CloseHandle(hfile); - if (!result) { - return TRUE; - } - hfile = CreateFileW(path, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (hfile != INVALID_HANDLE_VALUE) { - CloseHandle(hfile); - return TRUE; - } - } - - switch (GetLastError()) { - case ERROR_ACCESS_DENIED: - case ERROR_SHARING_VIOLATION: - case ERROR_CANT_ACCESS_FILE: - case ERROR_INVALID_PARAMETER: - STRUCT_STAT _st; - return followLinks ? !STAT(path, &_st): !LSTAT(path, &_st); - } - - return FALSE; -} - - -static BOOL -_testFileExists(path_t *path, BOOL followLinks) -{ - BOOL result = FALSE; - if (path->value_error) { - return FALSE; - } - - Py_BEGIN_ALLOW_THREADS - if (path->fd != -1) { - HANDLE hfile = _Py_get_osfhandle_noraise(path->fd); - if (hfile != INVALID_HANDLE_VALUE) { - if (GetFileType(hfile) != FILE_TYPE_UNKNOWN || !GetLastError()) { - result = TRUE; - } - } - } - else if (path->wide) { - result = _testFileExistsByName(path->wide, followLinks); - } - Py_END_ALLOW_THREADS - - return result; -} - - -static BOOL -_testFileType(path_t *path, int testedType) -{ - BOOL result = FALSE; - if (path->value_error) { - return FALSE; - } - - Py_BEGIN_ALLOW_THREADS - if (path->fd != -1) { - HANDLE hfile = _Py_get_osfhandle_noraise(path->fd); - if (hfile != INVALID_HANDLE_VALUE) { - result = _testFileTypeByHandle(hfile, testedType, TRUE); - } - } - else if (path->wide) { - result = _testFileTypeByName(path->wide, testedType); - } - Py_END_ALLOW_THREADS - - return result; -} - - -/*[clinic input] -os._path_exists -> bool - - path: path_t(allow_fd=True, suppress_value_error=True) - -Test whether a path exists. Returns False for broken symbolic links. - -[clinic start generated code]*/ - -static int -os__path_exists_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=8da13acf666e16ba input=142beabfc66783eb]*/ -{ - return _testFileExists(path, TRUE); -} - - -/*[clinic input] -os._path_lexists -> bool - - path: path_t(allow_fd=True, suppress_value_error=True) - -Test whether a path exists. Returns True for broken symbolic links. - -[clinic start generated code]*/ - -static int -os__path_lexists_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=e7240ed5fc45bff3 input=208205112a3cc1ed]*/ -{ - return _testFileExists(path, FALSE); -} - - -/*[clinic input] -os._path_isdir -> bool - - s as path: path_t(allow_fd=True, suppress_value_error=True) - -Return true if the pathname refers to an existing directory. - -[clinic start generated code]*/ - -static int -os__path_isdir_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=d5786196f9e2fa7a input=132a3b5301aecf79]*/ -{ - return _testFileType(path, PY_IFDIR); -} - - -/*[clinic input] -os._path_isfile -> bool - - path: path_t(allow_fd=True, suppress_value_error=True) - -Test whether a path is a regular file - -[clinic start generated code]*/ - -static int -os__path_isfile_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=5c3073bc212b9863 input=4ac1fd350b30a39e]*/ -{ - return _testFileType(path, PY_IFREG); -} - - -/*[clinic input] -os._path_islink -> bool - - path: path_t(allow_fd=True, suppress_value_error=True) - -Test whether a path is a symbolic link - -[clinic start generated code]*/ - -static int -os__path_islink_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=30da7bda8296adcc input=7510ce05b547debb]*/ -{ - return _testFileType(path, PY_IFLNK); -} - - -/*[clinic input] -os._path_isjunction -> bool - - path: path_t(allow_fd=True, suppress_value_error=True) - -Test whether a path is a junction - -[clinic start generated code]*/ - -static int -os__path_isjunction_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=e1d17a9dd18a9945 input=7dcb8bc4e972fcaf]*/ -{ - return _testFileType(path, PY_IFMNT); -} - -#undef PY_IFREG -#undef PY_IFDIR -#undef PY_IFLNK -#undef PY_IFMNT -#undef PY_IFLRP -#undef PY_IFRRP - -#endif /* MS_WINDOWS */ - - -/*[clinic input] -os._path_splitroot_ex - - p as path: path_t(make_wide=True, nonstrict=True) - -Split a pathname into drive, root and tail. - -The tail contains anything after the root. -[clinic start generated code]*/ - -static PyObject * -os__path_splitroot_ex_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=4b0072b6cdf4b611 input=4556b615c7cc13f2]*/ -{ - Py_ssize_t drvsize, rootsize; - PyObject *drv = NULL, *root = NULL, *tail = NULL, *result = NULL; - - const wchar_t *buffer = path->wide; - _Py_skiproot(buffer, path->length, &drvsize, &rootsize); - drv = PyUnicode_FromWideChar(buffer, drvsize); - if (drv == NULL) { - goto exit; - } - root = PyUnicode_FromWideChar(&buffer[drvsize], rootsize); - if (root == NULL) { - goto exit; - } - tail = PyUnicode_FromWideChar(&buffer[drvsize + rootsize], - path->length - drvsize - rootsize); - if (tail == NULL) { - goto exit; - } - if (PyBytes_Check(path->object)) { - Py_SETREF(drv, PyUnicode_EncodeFSDefault(drv)); - if (drv == NULL) { - goto exit; - } - Py_SETREF(root, PyUnicode_EncodeFSDefault(root)); - if (root == NULL) { - goto exit; - } - Py_SETREF(tail, PyUnicode_EncodeFSDefault(tail)); - if (tail == NULL) { - goto exit; - } - } - result = PyTuple_Pack(3, drv, root, tail); -exit: - Py_XDECREF(drv); - Py_XDECREF(root); - Py_XDECREF(tail); - return result; -} - - -/*[clinic input] -os._path_normpath - - path: path_t(make_wide=True, nonstrict=True) - -Normalize path, eliminating double slashes, etc. -[clinic start generated code]*/ - -static PyObject * -os__path_normpath_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=d353e7ed9410c044 input=3d4ac23b06332dcb]*/ -{ - PyObject *result; - Py_ssize_t norm_len; - wchar_t *norm_path = _Py_normpath_and_size((wchar_t *)path->wide, - path->length, &norm_len); - if (!norm_len) { - result = PyUnicode_FromOrdinal('.'); - } - else { - result = PyUnicode_FromWideChar(norm_path, norm_len); - } - if (PyBytes_Check(path->object)) { - Py_SETREF(result, PyUnicode_EncodeFSDefault(result)); - } - return result; -} - -/*[clinic input] -os.mkdir - - path : path_t - - mode: int = 0o777 - - * - - dir_fd : dir_fd(requires='mkdirat') = None - -# "mkdir(path, mode=0o777, *, dir_fd=None)\n\n\ - -Create a directory. - -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. -dir_fd may not be implemented on your platform. - If it is unavailable, using it will raise a NotImplementedError. - -The mode argument is ignored on Windows. Where it is used, the current umask -value is first masked out. -[clinic start generated code]*/ - -static PyObject * -os_mkdir_impl(PyObject *module, path_t *path, int mode, int dir_fd) -/*[clinic end generated code: output=a70446903abe821f input=a61722e1576fab03]*/ -{ - int result; -#ifdef MS_WINDOWS - int error = 0; - int pathError = 0; - SECURITY_ATTRIBUTES secAttr = { sizeof(secAttr) }; - SECURITY_ATTRIBUTES *pSecAttr = NULL; -#endif -#ifdef HAVE_MKDIRAT - int mkdirat_unavailable = 0; -#endif - - if (PySys_Audit("os.mkdir", "Oii", path->object, mode, - dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) { - return NULL; - } - -#ifdef MS_WINDOWS - Py_BEGIN_ALLOW_THREADS - if (mode == 0700 /* 0o700 */) { - ULONG sdSize; - pSecAttr = &secAttr; - // Set a discretionary ACL (D) that is protected (P) and includes - // inheritable (OICI) entries that allow (A) full control (FA) to - // SYSTEM (SY), Administrators (BA), and the owner (OW). - if (!ConvertStringSecurityDescriptorToSecurityDescriptorW( - L"D:P(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;FA;;;OW)", - SDDL_REVISION_1, - &secAttr.lpSecurityDescriptor, - &sdSize - )) { - error = GetLastError(); - } - } - if (!error) { - result = CreateDirectoryW(path->wide, pSecAttr); - if (secAttr.lpSecurityDescriptor && - // uncommonly, LocalFree returns non-zero on error, but still uses - // GetLastError() to see what the error code is - LocalFree(secAttr.lpSecurityDescriptor)) { - error = GetLastError(); - } - } - Py_END_ALLOW_THREADS - - if (error) { - return PyErr_SetFromWindowsErr(error); - } - if (!result) { - return path_error(path); - } -#else - Py_BEGIN_ALLOW_THREADS -#if HAVE_MKDIRAT - if (dir_fd != DEFAULT_DIR_FD) { - if (HAVE_MKDIRAT_RUNTIME) { - result = mkdirat(dir_fd, path->narrow, mode); - - } else { - mkdirat_unavailable = 1; - } - } else -#endif -#if defined(__WATCOMC__) && !defined(__QNX__) - result = mkdir(path->narrow); -#else - result = mkdir(path->narrow, mode); -#endif - Py_END_ALLOW_THREADS - -#if HAVE_MKDIRAT - if (mkdirat_unavailable) { - argument_unavailable_error(NULL, "dir_fd"); - return NULL; - } -#endif - - if (result < 0) - return path_error(path); -#endif /* MS_WINDOWS */ - Py_RETURN_NONE; -} - - -/* sys/resource.h is needed for at least: wait3(), wait4(), broken nice. */ -#if defined(HAVE_SYS_RESOURCE_H) -#include -#endif - - -#ifdef HAVE_NICE -/*[clinic input] -os.nice - - increment: int - / - -Add increment to the priority of process and return the new priority. -[clinic start generated code]*/ - -static PyObject * -os_nice_impl(PyObject *module, int increment) -/*[clinic end generated code: output=9dad8a9da8109943 input=864be2d402a21da2]*/ -{ - int value; - - /* There are two flavours of 'nice': one that returns the new - priority (as required by almost all standards out there) and the - Linux/FreeBSD one, which returns '0' on success and advices - the use of getpriority() to get the new priority. - - If we are of the nice family that returns the new priority, we - need to clear errno before the call, and check if errno is filled - before calling posix_error() on a returnvalue of -1, because the - -1 may be the actual new priority! */ - - errno = 0; - value = nice(increment); -#if defined(HAVE_BROKEN_NICE) && defined(HAVE_GETPRIORITY) - if (value == 0) - value = getpriority(PRIO_PROCESS, 0); -#endif - if (value == -1 && errno != 0) - /* either nice() or getpriority() returned an error */ - return posix_error(); - return PyLong_FromLong((long) value); -} -#endif /* HAVE_NICE */ - - -#ifdef HAVE_GETPRIORITY -/*[clinic input] -os.getpriority - - which: int - who: int - -Return program scheduling priority. -[clinic start generated code]*/ - -static PyObject * -os_getpriority_impl(PyObject *module, int which, int who) -/*[clinic end generated code: output=c41b7b63c7420228 input=9be615d40e2544ef]*/ -{ - int retval; - - errno = 0; - retval = getpriority(which, who); - if (errno != 0) - return posix_error(); - return PyLong_FromLong((long)retval); -} -#endif /* HAVE_GETPRIORITY */ - - -#ifdef HAVE_SETPRIORITY -/*[clinic input] -os.setpriority - - which: int - who: int - priority: int - -Set program scheduling priority. -[clinic start generated code]*/ - -static PyObject * -os_setpriority_impl(PyObject *module, int which, int who, int priority) -/*[clinic end generated code: output=3d910d95a7771eb2 input=710ccbf65b9dc513]*/ -{ - int retval; - - retval = setpriority(which, who, priority); - if (retval == -1) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_SETPRIORITY */ - - -static PyObject * -internal_rename(path_t *src, path_t *dst, int src_dir_fd, int dst_dir_fd, int is_replace) -{ - const char *function_name = is_replace ? "replace" : "rename"; - int dir_fd_specified; - -#ifdef HAVE_RENAMEAT - int renameat_unavailable = 0; -#endif - -#ifdef MS_WINDOWS - BOOL result; - int flags = is_replace ? MOVEFILE_REPLACE_EXISTING : 0; -#else - int result; -#endif - - dir_fd_specified = (src_dir_fd != DEFAULT_DIR_FD) || - (dst_dir_fd != DEFAULT_DIR_FD); -#ifndef HAVE_RENAMEAT - if (dir_fd_specified) { - argument_unavailable_error(function_name, "src_dir_fd and dst_dir_fd"); - return NULL; - } -#endif - - if (PySys_Audit("os.rename", "OOii", src->object, dst->object, - src_dir_fd == DEFAULT_DIR_FD ? -1 : src_dir_fd, - dst_dir_fd == DEFAULT_DIR_FD ? -1 : dst_dir_fd) < 0) { - return NULL; - } - -#ifdef MS_WINDOWS - Py_BEGIN_ALLOW_THREADS - result = MoveFileExW(src->wide, dst->wide, flags); - Py_END_ALLOW_THREADS - - if (!result) - return path_error2(src, dst); - -#else - if ((src->narrow && dst->wide) || (src->wide && dst->narrow)) { - PyErr_Format(PyExc_ValueError, - "%s: src and dst must be the same type", function_name); - return NULL; - } - - Py_BEGIN_ALLOW_THREADS -#ifdef HAVE_RENAMEAT - if (dir_fd_specified) { - if (HAVE_RENAMEAT_RUNTIME) { - result = renameat(src_dir_fd, src->narrow, dst_dir_fd, dst->narrow); - } else { - renameat_unavailable = 1; - } - } else -#endif - result = rename(src->narrow, dst->narrow); - Py_END_ALLOW_THREADS - - -#ifdef HAVE_RENAMEAT - if (renameat_unavailable) { - argument_unavailable_error(function_name, "src_dir_fd and dst_dir_fd"); - return NULL; - } -#endif - - if (result) - return path_error2(src, dst); -#endif - Py_RETURN_NONE; -} - - -/*[clinic input] -os.rename - - src : path_t - dst : path_t - * - src_dir_fd : dir_fd = None - dst_dir_fd : dir_fd = None - -Rename a file or directory. - -If either src_dir_fd or dst_dir_fd is not None, it should be a file - descriptor open to a directory, and the respective path string (src or dst) - should be relative; the path will then be relative to that directory. -src_dir_fd and dst_dir_fd, may not be implemented on your platform. - If they are unavailable, using them will raise a NotImplementedError. -[clinic start generated code]*/ - -static PyObject * -os_rename_impl(PyObject *module, path_t *src, path_t *dst, int src_dir_fd, - int dst_dir_fd) -/*[clinic end generated code: output=59e803072cf41230 input=faa61c847912c850]*/ -{ - return internal_rename(src, dst, src_dir_fd, dst_dir_fd, 0); -} - - -/*[clinic input] -os.replace = os.rename - -Rename a file or directory, overwriting the destination. - -If either src_dir_fd or dst_dir_fd is not None, it should be a file - descriptor open to a directory, and the respective path string (src or dst) - should be relative; the path will then be relative to that directory. -src_dir_fd and dst_dir_fd, may not be implemented on your platform. - If they are unavailable, using them will raise a NotImplementedError. -[clinic start generated code]*/ - -static PyObject * -os_replace_impl(PyObject *module, path_t *src, path_t *dst, int src_dir_fd, - int dst_dir_fd) -/*[clinic end generated code: output=1968c02e7857422b input=c003f0def43378ef]*/ -{ - return internal_rename(src, dst, src_dir_fd, dst_dir_fd, 1); -} - - -/*[clinic input] -os.rmdir - - path: path_t - * - dir_fd: dir_fd(requires='unlinkat') = None - -Remove a directory. - -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. -dir_fd may not be implemented on your platform. - If it is unavailable, using it will raise a NotImplementedError. -[clinic start generated code]*/ - -static PyObject * -os_rmdir_impl(PyObject *module, path_t *path, int dir_fd) -/*[clinic end generated code: output=080eb54f506e8301 input=38c8b375ca34a7e2]*/ -{ - int result; -#ifdef HAVE_UNLINKAT - int unlinkat_unavailable = 0; -#endif - - if (PySys_Audit("os.rmdir", "Oi", path->object, - dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) { - return NULL; - } - - Py_BEGIN_ALLOW_THREADS -#ifdef MS_WINDOWS - /* Windows, success=1, UNIX, success=0 */ - result = !RemoveDirectoryW(path->wide); -#else -#ifdef HAVE_UNLINKAT - if (dir_fd != DEFAULT_DIR_FD) { - if (HAVE_UNLINKAT_RUNTIME) { - result = unlinkat(dir_fd, path->narrow, AT_REMOVEDIR); - } else { - unlinkat_unavailable = 1; - result = -1; - } - } else -#endif - result = rmdir(path->narrow); -#endif - Py_END_ALLOW_THREADS - -#ifdef HAVE_UNLINKAT - if (unlinkat_unavailable) { - argument_unavailable_error("rmdir", "dir_fd"); - return NULL; - } -#endif - - if (result) - return path_error(path); - - Py_RETURN_NONE; -} - - -#ifdef HAVE_SYSTEM -#ifdef MS_WINDOWS -/*[clinic input] -os.system -> long - - command: Py_UNICODE - -Execute the command in a subshell. -[clinic start generated code]*/ - -static long -os_system_impl(PyObject *module, const wchar_t *command) -/*[clinic end generated code: output=dd528cbd5943a679 input=303f5ce97df606b0]*/ -{ - long result; - - if (PySys_Audit("os.system", "(u)", command) < 0) { - return -1; - } - - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH - result = _wsystem(command); - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - return result; -} -#else /* MS_WINDOWS */ -/*[clinic input] -os.system -> long - - command: FSConverter - -Execute the command in a subshell. -[clinic start generated code]*/ - -static long -os_system_impl(PyObject *module, PyObject *command) -/*[clinic end generated code: output=290fc437dd4f33a0 input=86a58554ba6094af]*/ -{ - long result; - const char *bytes = PyBytes_AsString(command); - - if (PySys_Audit("os.system", "(O)", command) < 0) { - return -1; - } - - Py_BEGIN_ALLOW_THREADS - result = system(bytes); - Py_END_ALLOW_THREADS - return result; -} -#endif -#endif /* HAVE_SYSTEM */ - - -#ifdef HAVE_UMASK -/*[clinic input] -os.umask - - mask: int - / - -Set the current numeric umask and return the previous umask. -[clinic start generated code]*/ - -static PyObject * -os_umask_impl(PyObject *module, int mask) -/*[clinic end generated code: output=a2e33ce3bc1a6e33 input=ab6bfd9b24d8a7e8]*/ -{ - int i = (int)umask(mask); - if (i < 0) - return posix_error(); - return PyLong_FromLong((long)i); -} -#endif - -#ifdef MS_WINDOWS - -/* override the default DeleteFileW behavior so that directory -symlinks can be removed with this function, the same as with -Unix symlinks */ -BOOL WINAPI Py_DeleteFileW(LPCWSTR lpFileName) -{ - WIN32_FILE_ATTRIBUTE_DATA info; - WIN32_FIND_DATAW find_data; - HANDLE find_data_handle; - int is_directory = 0; - int is_link = 0; - - if (GetFileAttributesExW(lpFileName, GetFileExInfoStandard, &info)) { - is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; - - /* Get WIN32_FIND_DATA structure for the path to determine if - it is a symlink */ - if(is_directory && - info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - find_data_handle = FindFirstFileW(lpFileName, &find_data); - - if(find_data_handle != INVALID_HANDLE_VALUE) { - /* IO_REPARSE_TAG_SYMLINK if it is a symlink and - IO_REPARSE_TAG_MOUNT_POINT if it is a junction point. */ - is_link = find_data.dwReserved0 == IO_REPARSE_TAG_SYMLINK || - find_data.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT; - FindClose(find_data_handle); - } - } - } - - if (is_directory && is_link) - return RemoveDirectoryW(lpFileName); - - return DeleteFileW(lpFileName); -} -#endif /* MS_WINDOWS */ - - -/*[clinic input] -os.unlink - - path: path_t - * - dir_fd: dir_fd(requires='unlinkat')=None - -Remove a file (same as remove()). - -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. -dir_fd may not be implemented on your platform. - If it is unavailable, using it will raise a NotImplementedError. - -[clinic start generated code]*/ - -static PyObject * -os_unlink_impl(PyObject *module, path_t *path, int dir_fd) -/*[clinic end generated code: output=621797807b9963b1 input=d7bcde2b1b2a2552]*/ -{ - int result; -#ifdef HAVE_UNLINKAT - int unlinkat_unavailable = 0; -#endif - - if (PySys_Audit("os.remove", "Oi", path->object, - dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) { - return NULL; - } - - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH -#ifdef MS_WINDOWS - /* Windows, success=1, UNIX, success=0 */ - result = !Py_DeleteFileW(path->wide); -#else -#ifdef HAVE_UNLINKAT - if (dir_fd != DEFAULT_DIR_FD) { - if (HAVE_UNLINKAT_RUNTIME) { - - result = unlinkat(dir_fd, path->narrow, 0); - } else { - unlinkat_unavailable = 1; - } - } else -#endif /* HAVE_UNLINKAT */ - result = unlink(path->narrow); -#endif - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - -#ifdef HAVE_UNLINKAT - if (unlinkat_unavailable) { - argument_unavailable_error(NULL, "dir_fd"); - return NULL; - } -#endif - - if (result) - return path_error(path); - - Py_RETURN_NONE; -} - - -/*[clinic input] -os.remove = os.unlink - -Remove a file (same as unlink()). - -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. -dir_fd may not be implemented on your platform. - If it is unavailable, using it will raise a NotImplementedError. -[clinic start generated code]*/ - -static PyObject * -os_remove_impl(PyObject *module, path_t *path, int dir_fd) -/*[clinic end generated code: output=a8535b28f0068883 input=e05c5ab55cd30983]*/ -{ - return os_unlink_impl(module, path, dir_fd); -} - - -static PyStructSequence_Field uname_result_fields[] = { - {"sysname", "operating system name"}, - {"nodename", "name of machine on network (implementation-defined)"}, - {"release", "operating system release"}, - {"version", "operating system version"}, - {"machine", "hardware identifier"}, - {NULL} -}; - -PyDoc_STRVAR(uname_result__doc__, -"uname_result: Result from os.uname().\n\n\ -This object may be accessed either as a tuple of\n\ - (sysname, nodename, release, version, machine),\n\ -or via the attributes sysname, nodename, release, version, and machine.\n\ -\n\ -See os.uname for more information."); - -static PyStructSequence_Desc uname_result_desc = { - MODNAME ".uname_result", /* name */ - uname_result__doc__, /* doc */ - uname_result_fields, - 5 -}; - -#ifdef HAVE_UNAME -/*[clinic input] -os.uname - -Return an object identifying the current operating system. - -The object behaves like a named tuple with the following fields: - (sysname, nodename, release, version, machine) - -[clinic start generated code]*/ - -static PyObject * -os_uname_impl(PyObject *module) -/*[clinic end generated code: output=e6a49cf1a1508a19 input=e68bd246db3043ed]*/ -{ - struct utsname u; - int res; - PyObject *value; - - Py_BEGIN_ALLOW_THREADS - res = uname(&u); - Py_END_ALLOW_THREADS - if (res < 0) - return posix_error(); - - PyObject *UnameResultType = get_posix_state(module)->UnameResultType; - value = PyStructSequence_New((PyTypeObject *)UnameResultType); - if (value == NULL) - return NULL; - -#define SET(i, field) \ - { \ - PyObject *o = PyUnicode_DecodeFSDefault(field); \ - if (!o) { \ - Py_DECREF(value); \ - return NULL; \ - } \ - PyStructSequence_SET_ITEM(value, i, o); \ - } \ - - SET(0, u.sysname); - SET(1, u.nodename); - SET(2, u.release); - SET(3, u.version); - SET(4, u.machine); - -#undef SET - - return value; -} -#endif /* HAVE_UNAME */ - - - -typedef struct { - int now; - time_t atime_s; - long atime_ns; - time_t mtime_s; - long mtime_ns; -} utime_t; - -/* - * these macros assume that "ut" is a pointer to a utime_t - * they also intentionally leak the declaration of a pointer named "time" - */ -#define UTIME_TO_TIMESPEC \ - struct timespec ts[2]; \ - struct timespec *time; \ - if (ut->now) \ - time = NULL; \ - else { \ - ts[0].tv_sec = ut->atime_s; \ - ts[0].tv_nsec = ut->atime_ns; \ - ts[1].tv_sec = ut->mtime_s; \ - ts[1].tv_nsec = ut->mtime_ns; \ - time = ts; \ - } \ - -#define UTIME_TO_TIMEVAL \ - struct timeval tv[2]; \ - struct timeval *time; \ - if (ut->now) \ - time = NULL; \ - else { \ - tv[0].tv_sec = ut->atime_s; \ - tv[0].tv_usec = ut->atime_ns / 1000; \ - tv[1].tv_sec = ut->mtime_s; \ - tv[1].tv_usec = ut->mtime_ns / 1000; \ - time = tv; \ - } \ - -#define UTIME_TO_UTIMBUF \ - struct utimbuf u; \ - struct utimbuf *time; \ - if (ut->now) \ - time = NULL; \ - else { \ - u.actime = ut->atime_s; \ - u.modtime = ut->mtime_s; \ - time = &u; \ - } - -#define UTIME_TO_TIME_T \ - time_t timet[2]; \ - time_t *time; \ - if (ut->now) \ - time = NULL; \ - else { \ - timet[0] = ut->atime_s; \ - timet[1] = ut->mtime_s; \ - time = timet; \ - } \ - - -#if defined(HAVE_FUTIMESAT) || defined(HAVE_UTIMENSAT) - -static int -utime_dir_fd(utime_t *ut, int dir_fd, const char *path, int follow_symlinks) -{ -#if defined(__APPLE__) && defined(HAVE_UTIMENSAT) - if (HAVE_UTIMENSAT_RUNTIME) { - int flags = follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW; - UTIME_TO_TIMESPEC; - return utimensat(dir_fd, path, time, flags); - } else { - errno = ENOSYS; - return -1; - } -#elif defined(HAVE_UTIMENSAT) - int flags = follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW; - UTIME_TO_TIMESPEC; - return utimensat(dir_fd, path, time, flags); -#elif defined(HAVE_FUTIMESAT) - UTIME_TO_TIMEVAL; - /* - * follow_symlinks will never be false here; - * we only allow !follow_symlinks and dir_fd together - * if we have utimensat() - */ - assert(follow_symlinks); - return futimesat(dir_fd, path, time); -#endif -} - - #define FUTIMENSAT_DIR_FD_CONVERTER dir_fd_converter -#else - #define FUTIMENSAT_DIR_FD_CONVERTER dir_fd_unavailable -#endif - -#if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMENS) - -static int -utime_fd(utime_t *ut, int fd) -{ -#ifdef HAVE_FUTIMENS - - if (HAVE_FUTIMENS_RUNTIME) { - - UTIME_TO_TIMESPEC; - return futimens(fd, time); - - } else -#ifndef HAVE_FUTIMES - { - /* Not sure if this can happen */ - PyErr_SetString( - PyExc_RuntimeError, - "neither futimens nor futimes are supported" - " on this system"); - return -1; - } -#endif - -#endif -#ifdef HAVE_FUTIMES - { - UTIME_TO_TIMEVAL; - return futimes(fd, time); - } -#endif -} - - #define PATH_UTIME_HAVE_FD 1 -#else - #define PATH_UTIME_HAVE_FD 0 -#endif - -#if defined(HAVE_UTIMENSAT) || defined(HAVE_LUTIMES) -# define UTIME_HAVE_NOFOLLOW_SYMLINKS -#endif - -#ifdef UTIME_HAVE_NOFOLLOW_SYMLINKS - -static int -utime_nofollow_symlinks(utime_t *ut, const char *path) -{ -#ifdef HAVE_UTIMENSAT - if (HAVE_UTIMENSAT_RUNTIME) { - UTIME_TO_TIMESPEC; - return utimensat(DEFAULT_DIR_FD, path, time, AT_SYMLINK_NOFOLLOW); - } else -#ifndef HAVE_LUTIMES - { - /* Not sure if this can happen */ - PyErr_SetString( - PyExc_RuntimeError, - "neither utimensat nor lutimes are supported" - " on this system"); - return -1; - } -#endif -#endif - -#ifdef HAVE_LUTIMES - { - UTIME_TO_TIMEVAL; - return lutimes(path, time); - } -#endif -} - -#endif - -#ifndef MS_WINDOWS - -static int -utime_default(utime_t *ut, const char *path) -{ -#if defined(__APPLE__) && defined(HAVE_UTIMENSAT) - if (HAVE_UTIMENSAT_RUNTIME) { - UTIME_TO_TIMESPEC; - return utimensat(DEFAULT_DIR_FD, path, time, 0); - } else { - UTIME_TO_TIMEVAL; - return utimes(path, time); - } -#elif defined(HAVE_UTIMENSAT) - UTIME_TO_TIMESPEC; - return utimensat(DEFAULT_DIR_FD, path, time, 0); -#elif defined(HAVE_UTIMES) - UTIME_TO_TIMEVAL; - return utimes(path, time); -#elif defined(HAVE_UTIME_H) - UTIME_TO_UTIMBUF; - return utime(path, time); -#else - UTIME_TO_TIME_T; - return utime(path, time); -#endif -} - -#endif - -static int -split_py_long_to_s_and_ns(PyObject *module, PyObject *py_long, time_t *s, long *ns) -{ - int result = 0; - PyObject *divmod; - divmod = PyNumber_Divmod(py_long, get_posix_state(module)->billion); - if (!divmod) - goto exit; - if (!PyTuple_Check(divmod) || PyTuple_GET_SIZE(divmod) != 2) { - PyErr_Format(PyExc_TypeError, - "%.200s.__divmod__() must return a 2-tuple, not %.200s", - _PyType_Name(Py_TYPE(py_long)), _PyType_Name(Py_TYPE(divmod))); - goto exit; - } - *s = _PyLong_AsTime_t(PyTuple_GET_ITEM(divmod, 0)); - if ((*s == -1) && PyErr_Occurred()) - goto exit; - *ns = PyLong_AsLong(PyTuple_GET_ITEM(divmod, 1)); - if ((*ns == -1) && PyErr_Occurred()) - goto exit; - - result = 1; -exit: - Py_XDECREF(divmod); - return result; -} - - -/*[clinic input] -os.utime - - path: path_t(allow_fd='PATH_UTIME_HAVE_FD') - times: object = None - * - ns: object = NULL - dir_fd: dir_fd(requires='futimensat') = None - follow_symlinks: bool=True - -# "utime(path, times=None, *[, ns], dir_fd=None, follow_symlinks=True)\n\ - -Set the access and modified time of path. - -path may always be specified as a string. -On some platforms, path may also be specified as an open file descriptor. - If this functionality is unavailable, using it raises an exception. - -If times is not None, it must be a tuple (atime, mtime); - atime and mtime should be expressed as float seconds since the epoch. -If ns is specified, it must be a tuple (atime_ns, mtime_ns); - atime_ns and mtime_ns should be expressed as integer nanoseconds - since the epoch. -If times is None and ns is unspecified, utime uses the current time. -Specifying tuples for both times and ns is an error. - -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. -If follow_symlinks is False, and the last element of the path is a symbolic - link, utime will modify the symbolic link itself instead of the file the - link points to. -It is an error to use dir_fd or follow_symlinks when specifying path - as an open file descriptor. -dir_fd and follow_symlinks may not be available on your platform. - If they are unavailable, using them will raise a NotImplementedError. - -[clinic start generated code]*/ - -static PyObject * -os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, - int dir_fd, int follow_symlinks) -/*[clinic end generated code: output=cfcac69d027b82cf input=2fbd62a2f228f8f4]*/ -{ -#ifdef MS_WINDOWS - HANDLE hFile; - FILETIME atime, mtime; -#else - int result; -#endif - - utime_t utime; - - memset(&utime, 0, sizeof(utime_t)); - - if (times != Py_None && ns) { - PyErr_SetString(PyExc_ValueError, - "utime: you may specify either 'times'" - " or 'ns' but not both"); - return NULL; - } - - if (times != Py_None) { - time_t a_sec, m_sec; - long a_nsec, m_nsec; - if (!PyTuple_CheckExact(times) || (PyTuple_Size(times) != 2)) { - PyErr_SetString(PyExc_TypeError, - "utime: 'times' must be either" - " a tuple of two ints or None"); - return NULL; - } - utime.now = 0; - if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 0), - &a_sec, &a_nsec, _PyTime_ROUND_FLOOR) == -1 || - _PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 1), - &m_sec, &m_nsec, _PyTime_ROUND_FLOOR) == -1) { - return NULL; - } - utime.atime_s = a_sec; - utime.atime_ns = a_nsec; - utime.mtime_s = m_sec; - utime.mtime_ns = m_nsec; - } - else if (ns) { - if (!PyTuple_CheckExact(ns) || (PyTuple_Size(ns) != 2)) { - PyErr_SetString(PyExc_TypeError, - "utime: 'ns' must be a tuple of two ints"); - return NULL; - } - utime.now = 0; - if (!split_py_long_to_s_and_ns(module, PyTuple_GET_ITEM(ns, 0), - &utime.atime_s, &utime.atime_ns) || - !split_py_long_to_s_and_ns(module, PyTuple_GET_ITEM(ns, 1), - &utime.mtime_s, &utime.mtime_ns)) { - return NULL; - } - } - else { - /* times and ns are both None/unspecified. use "now". */ - utime.now = 1; - } - -#if !defined(UTIME_HAVE_NOFOLLOW_SYMLINKS) - if (follow_symlinks_specified("utime", follow_symlinks)) - return NULL; -#endif - - if (path_and_dir_fd_invalid("utime", path, dir_fd) || - dir_fd_and_fd_invalid("utime", dir_fd, path->fd) || - fd_and_follow_symlinks_invalid("utime", path->fd, follow_symlinks)) - return NULL; - -#if !defined(HAVE_UTIMENSAT) - if ((dir_fd != DEFAULT_DIR_FD) && (!follow_symlinks)) { - PyErr_SetString(PyExc_ValueError, - "utime: cannot use dir_fd and follow_symlinks " - "together on this platform"); - return NULL; - } -#endif - - if (PySys_Audit("os.utime", "OOOi", path->object, times, ns ? ns : Py_None, - dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) { - return NULL; - } - -#ifdef MS_WINDOWS - Py_BEGIN_ALLOW_THREADS - hFile = CreateFileW(path->wide, FILE_WRITE_ATTRIBUTES, 0, - NULL, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, NULL); - Py_END_ALLOW_THREADS - if (hFile == INVALID_HANDLE_VALUE) { - path_error(path); - return NULL; - } - - if (utime.now) { - GetSystemTimeAsFileTime(&mtime); - atime = mtime; - } - else { - _Py_time_t_to_FILE_TIME(utime.atime_s, utime.atime_ns, &atime); - _Py_time_t_to_FILE_TIME(utime.mtime_s, utime.mtime_ns, &mtime); - } - if (!SetFileTime(hFile, NULL, &atime, &mtime)) { - path_error(path); - CloseHandle(hFile); - return NULL; - } - CloseHandle(hFile); -#else /* MS_WINDOWS */ - Py_BEGIN_ALLOW_THREADS - -#ifdef UTIME_HAVE_NOFOLLOW_SYMLINKS - if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD)) - result = utime_nofollow_symlinks(&utime, path->narrow); - else -#endif - -#if defined(HAVE_FUTIMESAT) || defined(HAVE_UTIMENSAT) - if ((dir_fd != DEFAULT_DIR_FD) || (!follow_symlinks)) { - result = utime_dir_fd(&utime, dir_fd, path->narrow, follow_symlinks); - - } else -#endif - -#if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMENS) - if (path->fd != -1) - result = utime_fd(&utime, path->fd); - else -#endif - - result = utime_default(&utime, path->narrow); - - Py_END_ALLOW_THREADS - -#if defined(__APPLE__) && defined(HAVE_UTIMENSAT) - /* See utime_dir_fd implementation */ - if (result == -1 && errno == ENOSYS) { - argument_unavailable_error(NULL, "dir_fd"); - return NULL; - } -#endif - - if (result < 0) { - path_error(path); - return NULL; - } - -#endif /* MS_WINDOWS */ - - Py_RETURN_NONE; -} - -/* Process operations */ - - -/*[clinic input] -os._exit - - status: int - -Exit to the system with specified status, without normal exit processing. -[clinic start generated code]*/ - -static PyObject * -os__exit_impl(PyObject *module, int status) -/*[clinic end generated code: output=116e52d9c2260d54 input=5e6d57556b0c4a62]*/ -{ - _exit(status); - return NULL; /* Make gcc -Wall happy */ -} - -#if defined(HAVE_WEXECV) || defined(HAVE_WSPAWNV) -#define EXECV_CHAR wchar_t -#else -#define EXECV_CHAR char -#endif - -#if defined(HAVE_EXECV) || defined(HAVE_SPAWNV) || defined(HAVE_RTPSPAWN) -static void -free_string_array(EXECV_CHAR **array, Py_ssize_t count) -{ - Py_ssize_t i; - for (i = 0; i < count; i++) - PyMem_Free(array[i]); - PyMem_Free(array); -} - -static int -fsconvert_strdup(PyObject *o, EXECV_CHAR **out) -{ - Py_ssize_t size; - PyObject *ub; - int result = 0; -#if defined(HAVE_WEXECV) || defined(HAVE_WSPAWNV) - if (!PyUnicode_FSDecoder(o, &ub)) - return 0; - *out = PyUnicode_AsWideCharString(ub, &size); - if (*out) - result = 1; -#else - if (!PyUnicode_FSConverter(o, &ub)) - return 0; - size = PyBytes_GET_SIZE(ub); - *out = PyMem_Malloc(size + 1); - if (*out) { - memcpy(*out, PyBytes_AS_STRING(ub), size + 1); - result = 1; - } else - PyErr_NoMemory(); -#endif - Py_DECREF(ub); - return result; -} -#endif - -#if defined(HAVE_EXECV) || defined (HAVE_FEXECVE) || defined(HAVE_RTPSPAWN) -static EXECV_CHAR** -parse_envlist(PyObject* env, Py_ssize_t *envc_ptr) -{ - Py_ssize_t i, pos, envc; - PyObject *keys=NULL, *vals=NULL; - PyObject *key2, *val2, *keyval; - EXECV_CHAR **envlist; - - i = PyMapping_Size(env); - if (i < 0) - return NULL; - envlist = PyMem_NEW(EXECV_CHAR *, i + 1); - if (envlist == NULL) { - PyErr_NoMemory(); - return NULL; - } - envc = 0; - keys = PyMapping_Keys(env); - if (!keys) - goto error; - vals = PyMapping_Values(env); - if (!vals) - goto error; - if (!PyList_Check(keys) || !PyList_Check(vals)) { - PyErr_Format(PyExc_TypeError, - "env.keys() or env.values() is not a list"); - goto error; - } - - for (pos = 0; pos < i; pos++) { - PyObject *key = PyList_GetItem(keys, pos); // Borrowed ref. - if (key == NULL) { - goto error; - } - PyObject *val = PyList_GetItem(vals, pos); // Borrowed ref. - if (val == NULL) { - goto error; - } - -#if defined(HAVE_WEXECV) || defined(HAVE_WSPAWNV) - if (!PyUnicode_FSDecoder(key, &key2)) - goto error; - if (!PyUnicode_FSDecoder(val, &val2)) { - Py_DECREF(key2); - goto error; - } - /* Search from index 1 because on Windows starting '=' is allowed for - defining hidden environment variables. */ - if (PyUnicode_GET_LENGTH(key2) == 0 || - PyUnicode_FindChar(key2, '=', 1, PyUnicode_GET_LENGTH(key2), 1) != -1) - { - PyErr_SetString(PyExc_ValueError, "illegal environment variable name"); - Py_DECREF(key2); - Py_DECREF(val2); - goto error; - } - keyval = PyUnicode_FromFormat("%U=%U", key2, val2); -#else - if (!PyUnicode_FSConverter(key, &key2)) - goto error; - if (!PyUnicode_FSConverter(val, &val2)) { - Py_DECREF(key2); - goto error; - } - if (PyBytes_GET_SIZE(key2) == 0 || - strchr(PyBytes_AS_STRING(key2) + 1, '=') != NULL) - { - PyErr_SetString(PyExc_ValueError, "illegal environment variable name"); - Py_DECREF(key2); - Py_DECREF(val2); - goto error; - } - keyval = PyBytes_FromFormat("%s=%s", PyBytes_AS_STRING(key2), - PyBytes_AS_STRING(val2)); -#endif - Py_DECREF(key2); - Py_DECREF(val2); - if (!keyval) - goto error; - - if (!fsconvert_strdup(keyval, &envlist[envc++])) { - Py_DECREF(keyval); - goto error; - } - - Py_DECREF(keyval); - } - Py_DECREF(vals); - Py_DECREF(keys); - - envlist[envc] = 0; - *envc_ptr = envc; - return envlist; - -error: - Py_XDECREF(keys); - Py_XDECREF(vals); - free_string_array(envlist, envc); - return NULL; -} - -static EXECV_CHAR** -parse_arglist(PyObject* argv, Py_ssize_t *argc) -{ - int i; - EXECV_CHAR **argvlist = PyMem_NEW(EXECV_CHAR *, *argc+1); - if (argvlist == NULL) { - PyErr_NoMemory(); - return NULL; - } - for (i = 0; i < *argc; i++) { - PyObject* item = PySequence_ITEM(argv, i); - if (item == NULL) - goto fail; - if (!fsconvert_strdup(item, &argvlist[i])) { - Py_DECREF(item); - goto fail; - } - Py_DECREF(item); - } - argvlist[*argc] = NULL; - return argvlist; -fail: - *argc = i; - free_string_array(argvlist, *argc); - return NULL; -} - -#endif - - -#ifdef HAVE_EXECV -/*[clinic input] -os.execv - - path: path_t - Path of executable file. - argv: object - Tuple or list of strings. - / - -Execute an executable path with arguments, replacing current process. -[clinic start generated code]*/ - -static PyObject * -os_execv_impl(PyObject *module, path_t *path, PyObject *argv) -/*[clinic end generated code: output=3b52fec34cd0dafd input=9bac31efae07dac7]*/ -{ - EXECV_CHAR **argvlist; - Py_ssize_t argc; - - PyInterpreterState *interp = _PyInterpreterState_GET(); - if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_EXEC)) { - PyErr_SetString(PyExc_RuntimeError, - "exec not supported for isolated subinterpreters"); - return NULL; - } - - /* execv has two arguments: (path, argv), where - argv is a list or tuple of strings. */ - - if (!PyList_Check(argv) && !PyTuple_Check(argv)) { - PyErr_SetString(PyExc_TypeError, - "execv() arg 2 must be a tuple or list"); - return NULL; - } - argc = PySequence_Size(argv); - if (argc < 1) { - PyErr_SetString(PyExc_ValueError, "execv() arg 2 must not be empty"); - return NULL; - } - - argvlist = parse_arglist(argv, &argc); - if (argvlist == NULL) { - return NULL; - } - if (!argvlist[0][0]) { - PyErr_SetString(PyExc_ValueError, - "execv() arg 2 first element cannot be empty"); - free_string_array(argvlist, argc); - return NULL; - } - - if (PySys_Audit("os.exec", "OOO", path->object, argv, Py_None) < 0) { - free_string_array(argvlist, argc); - return NULL; - } - - _Py_BEGIN_SUPPRESS_IPH -#ifdef HAVE_WEXECV - _wexecv(path->wide, argvlist); -#else - execv(path->narrow, argvlist); -#endif - _Py_END_SUPPRESS_IPH - - /* If we get here it's definitely an error */ - - posix_error(); - free_string_array(argvlist, argc); - return NULL; -} - - -/*[clinic input] -os.execve - - path: path_t(allow_fd='PATH_HAVE_FEXECVE') - Path of executable file. - argv: object - Tuple or list of strings. - env: object - Dictionary of strings mapping to strings. - -Execute an executable path with arguments, replacing current process. -[clinic start generated code]*/ - -static PyObject * -os_execve_impl(PyObject *module, path_t *path, PyObject *argv, PyObject *env) -/*[clinic end generated code: output=ff9fa8e4da8bde58 input=626804fa092606d9]*/ -{ - EXECV_CHAR **argvlist = NULL; - EXECV_CHAR **envlist; - Py_ssize_t argc, envc; - - PyInterpreterState *interp = _PyInterpreterState_GET(); - if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_EXEC)) { - PyErr_SetString(PyExc_RuntimeError, - "exec not supported for isolated subinterpreters"); - return NULL; - } - - /* execve has three arguments: (path, argv, env), where - argv is a list or tuple of strings and env is a dictionary - like posix.environ. */ - - if (!PyList_Check(argv) && !PyTuple_Check(argv)) { - PyErr_SetString(PyExc_TypeError, - "execve: argv must be a tuple or list"); - goto fail_0; - } - argc = PySequence_Size(argv); - if (argc < 1) { - PyErr_SetString(PyExc_ValueError, "execve: argv must not be empty"); - return NULL; - } - - if (!PyMapping_Check(env)) { - PyErr_SetString(PyExc_TypeError, - "execve: environment must be a mapping object"); - goto fail_0; - } - - argvlist = parse_arglist(argv, &argc); - if (argvlist == NULL) { - goto fail_0; - } - if (!argvlist[0][0]) { - PyErr_SetString(PyExc_ValueError, - "execve: argv first element cannot be empty"); - goto fail_0; - } - - envlist = parse_envlist(env, &envc); - if (envlist == NULL) - goto fail_0; - - if (PySys_Audit("os.exec", "OOO", path->object, argv, env) < 0) { - goto fail_1; - } - - _Py_BEGIN_SUPPRESS_IPH -#ifdef HAVE_FEXECVE - if (path->fd > -1) - fexecve(path->fd, argvlist, envlist); - else -#endif -#ifdef HAVE_WEXECV - _wexecve(path->wide, argvlist, envlist); -#else - execve(path->narrow, argvlist, envlist); -#endif - _Py_END_SUPPRESS_IPH - - /* If we get here it's definitely an error */ - - posix_path_error(path); - fail_1: - free_string_array(envlist, envc); - fail_0: - if (argvlist) - free_string_array(argvlist, argc); - return NULL; -} - -#endif /* HAVE_EXECV */ - -#ifdef HAVE_POSIX_SPAWN - -enum posix_spawn_file_actions_identifier { - POSIX_SPAWN_OPEN, - POSIX_SPAWN_CLOSE, - POSIX_SPAWN_DUP2 -#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP - ,POSIX_SPAWN_CLOSEFROM -#endif -}; - -#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) -static int -convert_sched_param(PyObject *module, PyObject *param, struct sched_param *res); -#endif - -static int -parse_posix_spawn_flags(PyObject *module, const char *func_name, PyObject *setpgroup, - int resetids, int setsid, PyObject *setsigmask, - PyObject *setsigdef, PyObject *scheduler, - posix_spawnattr_t *attrp) -{ - long all_flags = 0; - - errno = posix_spawnattr_init(attrp); - if (errno) { - posix_error(); - return -1; - } - - if (setpgroup) { - pid_t pgid = PyLong_AsPid(setpgroup); - if (pgid == (pid_t)-1 && PyErr_Occurred()) { - goto fail; - } - errno = posix_spawnattr_setpgroup(attrp, pgid); - if (errno) { - posix_error(); - goto fail; - } - all_flags |= POSIX_SPAWN_SETPGROUP; - } - - if (resetids) { - all_flags |= POSIX_SPAWN_RESETIDS; - } - - if (setsid) { -#ifdef HAVE_POSIX_SPAWN_SETSID_RUNTIME - if (HAVE_POSIX_SPAWN_SETSID_RUNTIME) { -#endif -#ifdef POSIX_SPAWN_SETSID - all_flags |= POSIX_SPAWN_SETSID; -#elif defined(POSIX_SPAWN_SETSID_NP) - all_flags |= POSIX_SPAWN_SETSID_NP; -#else - argument_unavailable_error(func_name, "setsid"); - return -1; -#endif - -#ifdef HAVE_POSIX_SPAWN_SETSID_RUNTIME - } else { - argument_unavailable_error(func_name, "setsid"); - return -1; - } -#endif /* HAVE_POSIX_SPAWN_SETSID_RUNTIME */ - - } - -#ifdef HAVE_SIGSET_T - if (setsigmask) { - sigset_t set; - if (!_Py_Sigset_Converter(setsigmask, &set)) { - goto fail; - } - errno = posix_spawnattr_setsigmask(attrp, &set); - if (errno) { - posix_error(); - goto fail; - } - all_flags |= POSIX_SPAWN_SETSIGMASK; - } - - if (setsigdef) { - sigset_t set; - if (!_Py_Sigset_Converter(setsigdef, &set)) { - goto fail; - } - errno = posix_spawnattr_setsigdefault(attrp, &set); - if (errno) { - posix_error(); - goto fail; - } - all_flags |= POSIX_SPAWN_SETSIGDEF; - } -#else - if (setsigmask || setsigdef) { - PyErr_SetString(PyExc_NotImplementedError, - "sigset is not supported on this platform"); - goto fail; - } -#endif - - if (scheduler) { -#ifdef POSIX_SPAWN_SETSCHEDULER - PyObject *py_schedpolicy; - PyObject *schedparam_obj; - struct sched_param schedparam; - - if (!PyArg_ParseTuple(scheduler, "OO" - ";A scheduler tuple must have two elements", - &py_schedpolicy, &schedparam_obj)) { - goto fail; - } - if (!convert_sched_param(module, schedparam_obj, &schedparam)) { - goto fail; - } - if (py_schedpolicy != Py_None) { - int schedpolicy = PyLong_AsInt(py_schedpolicy); - - if (schedpolicy == -1 && PyErr_Occurred()) { - goto fail; - } - errno = posix_spawnattr_setschedpolicy(attrp, schedpolicy); - if (errno) { - posix_error(); - goto fail; - } - all_flags |= POSIX_SPAWN_SETSCHEDULER; - } - errno = posix_spawnattr_setschedparam(attrp, &schedparam); - if (errno) { - posix_error(); - goto fail; - } - all_flags |= POSIX_SPAWN_SETSCHEDPARAM; -#else - PyErr_SetString(PyExc_NotImplementedError, - "The scheduler option is not supported in this system."); - goto fail; -#endif - } - - errno = posix_spawnattr_setflags(attrp, all_flags); - if (errno) { - posix_error(); - goto fail; - } - - return 0; - -fail: - (void)posix_spawnattr_destroy(attrp); - return -1; -} - -static int -parse_file_actions(PyObject *file_actions, - posix_spawn_file_actions_t *file_actionsp, - PyObject *temp_buffer) -{ - PyObject *seq; - PyObject *file_action = NULL; - PyObject *tag_obj; - - seq = PySequence_Fast(file_actions, - "file_actions must be a sequence or None"); - if (seq == NULL) { - return -1; - } - - errno = posix_spawn_file_actions_init(file_actionsp); - if (errno) { - posix_error(); - Py_DECREF(seq); - return -1; - } - - for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(seq); ++i) { - file_action = PySequence_Fast_GET_ITEM(seq, i); - Py_INCREF(file_action); - if (!PyTuple_Check(file_action) || !PyTuple_GET_SIZE(file_action)) { - PyErr_SetString(PyExc_TypeError, - "Each file_actions element must be a non-empty tuple"); - goto fail; - } - long tag = PyLong_AsLong(PyTuple_GET_ITEM(file_action, 0)); - if (tag == -1 && PyErr_Occurred()) { - goto fail; - } - - /* Populate the file_actions object */ - switch (tag) { - case POSIX_SPAWN_OPEN: { - int fd, oflag; - PyObject *path; - unsigned long mode; - if (!PyArg_ParseTuple(file_action, "OiO&ik" - ";A open file_action tuple must have 5 elements", - &tag_obj, &fd, PyUnicode_FSConverter, &path, - &oflag, &mode)) - { - goto fail; - } - if (PyList_Append(temp_buffer, path)) { - Py_DECREF(path); - goto fail; - } - errno = posix_spawn_file_actions_addopen(file_actionsp, - fd, PyBytes_AS_STRING(path), oflag, (mode_t)mode); - if (errno) { - posix_error(); - Py_DECREF(path); - goto fail; - } - Py_DECREF(path); - break; - } - case POSIX_SPAWN_CLOSE: { - int fd; - if (!PyArg_ParseTuple(file_action, "Oi" - ";A close file_action tuple must have 2 elements", - &tag_obj, &fd)) - { - goto fail; - } - errno = posix_spawn_file_actions_addclose(file_actionsp, fd); - if (errno) { - posix_error(); - goto fail; - } - break; - } - case POSIX_SPAWN_DUP2: { - int fd1, fd2; - if (!PyArg_ParseTuple(file_action, "Oii" - ";A dup2 file_action tuple must have 3 elements", - &tag_obj, &fd1, &fd2)) - { - goto fail; - } - errno = posix_spawn_file_actions_adddup2(file_actionsp, - fd1, fd2); - if (errno) { - posix_error(); - goto fail; - } - break; - } -#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP - case POSIX_SPAWN_CLOSEFROM: { - int fd; - if (!PyArg_ParseTuple(file_action, "Oi" - ";A closefrom file_action tuple must have 2 elements", - &tag_obj, &fd)) - { - goto fail; - } - errno = posix_spawn_file_actions_addclosefrom_np(file_actionsp, - fd); - if (errno) { - posix_error(); - goto fail; - } - break; - } -#endif - default: { - PyErr_SetString(PyExc_TypeError, - "Unknown file_actions identifier"); - goto fail; - } - } - Py_DECREF(file_action); - } - - Py_DECREF(seq); - return 0; - -fail: - Py_DECREF(seq); - Py_DECREF(file_action); - (void)posix_spawn_file_actions_destroy(file_actionsp); - return -1; -} - - -static PyObject * -py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *argv, - PyObject *env, PyObject *file_actions, - PyObject *setpgroup, int resetids, int setsid, PyObject *setsigmask, - PyObject *setsigdef, PyObject *scheduler) -{ - const char *func_name = use_posix_spawnp ? "posix_spawnp" : "posix_spawn"; - EXECV_CHAR **argvlist = NULL; - EXECV_CHAR **envlist = NULL; - posix_spawn_file_actions_t file_actions_buf; - posix_spawn_file_actions_t *file_actionsp = NULL; - posix_spawnattr_t attr; - posix_spawnattr_t *attrp = NULL; - Py_ssize_t argc, envc; - PyObject *result = NULL; - PyObject *temp_buffer = NULL; - pid_t pid; - int err_code; - - /* posix_spawn and posix_spawnp have three arguments: (path, argv, env), where - argv is a list or tuple of strings and env is a dictionary - like posix.environ. */ - - if (!PyList_Check(argv) && !PyTuple_Check(argv)) { - PyErr_Format(PyExc_TypeError, - "%s: argv must be a tuple or list", func_name); - goto exit; - } - argc = PySequence_Size(argv); - if (argc < 1) { - PyErr_Format(PyExc_ValueError, - "%s: argv must not be empty", func_name); - return NULL; - } - - if (!PyMapping_Check(env) && env != Py_None) { - PyErr_Format(PyExc_TypeError, - "%s: environment must be a mapping object or None", func_name); - goto exit; - } - - argvlist = parse_arglist(argv, &argc); - if (argvlist == NULL) { - goto exit; - } - if (!argvlist[0][0]) { - PyErr_Format(PyExc_ValueError, - "%s: argv first element cannot be empty", func_name); - goto exit; - } - -#ifdef USE_DARWIN_NS_GET_ENVIRON - // There is no environ global in this situation. - char **environ = NULL; -#endif - - if (env == Py_None) { -#ifdef USE_DARWIN_NS_GET_ENVIRON - environ = *_NSGetEnviron(); -#endif - envlist = environ; - } else { - envlist = parse_envlist(env, &envc); - if (envlist == NULL) { - goto exit; - } - } - - if (file_actions != NULL && file_actions != Py_None) { - /* There is a bug in old versions of glibc that makes some of the - * helper functions for manipulating file actions not copy the provided - * buffers. The problem is that posix_spawn_file_actions_addopen does not - * copy the value of path for some old versions of glibc (<2.20). - * The use of temp_buffer here is a workaround that keeps the - * python objects that own the buffers alive until posix_spawn gets called. - * Check https://bugs.python.org/issue33630 and - * https://sourceware.org/bugzilla/show_bug.cgi?id=17048 for more info.*/ - temp_buffer = PyList_New(0); - if (!temp_buffer) { - goto exit; - } - if (parse_file_actions(file_actions, &file_actions_buf, temp_buffer)) { - goto exit; - } - file_actionsp = &file_actions_buf; - } - - if (parse_posix_spawn_flags(module, func_name, setpgroup, resetids, setsid, - setsigmask, setsigdef, scheduler, &attr)) { - goto exit; - } - attrp = &attr; - - if (PySys_Audit("os.posix_spawn", "OOO", path->object, argv, env) < 0) { - goto exit; - } - - _Py_BEGIN_SUPPRESS_IPH -#ifdef HAVE_POSIX_SPAWNP - if (use_posix_spawnp) { - err_code = posix_spawnp(&pid, path->narrow, - file_actionsp, attrp, argvlist, envlist); - } - else -#endif /* HAVE_POSIX_SPAWNP */ - { - err_code = posix_spawn(&pid, path->narrow, - file_actionsp, attrp, argvlist, envlist); - } - _Py_END_SUPPRESS_IPH - - if (err_code) { - errno = err_code; - PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object); - goto exit; - } -#ifdef _Py_MEMORY_SANITIZER - __msan_unpoison(&pid, sizeof(pid)); -#endif - result = PyLong_FromPid(pid); - -exit: - if (file_actionsp) { - (void)posix_spawn_file_actions_destroy(file_actionsp); - } - if (attrp) { - (void)posix_spawnattr_destroy(attrp); - } - if (envlist && envlist != environ) { - free_string_array(envlist, envc); - } - if (argvlist) { - free_string_array(argvlist, argc); - } - Py_XDECREF(temp_buffer); - return result; -} - - -/*[clinic input] - -os.posix_spawn - path: path_t - Path of executable file. - argv: object - Tuple or list of strings. - env: object - Dictionary of strings mapping to strings. - / - * - file_actions: object(c_default='NULL') = () - A sequence of file action tuples. - setpgroup: object = NULL - The pgroup to use with the POSIX_SPAWN_SETPGROUP flag. - resetids: bool = False - If the value is `true` the POSIX_SPAWN_RESETIDS will be activated. - setsid: bool = False - If the value is `true` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP will be activated. - setsigmask: object(c_default='NULL') = () - The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag. - setsigdef: object(c_default='NULL') = () - The sigmask to use with the POSIX_SPAWN_SETSIGDEF flag. - scheduler: object = NULL - A tuple with the scheduler policy (optional) and parameters. - -Execute the program specified by path in a new process. -[clinic start generated code]*/ - -static PyObject * -os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv, - PyObject *env, PyObject *file_actions, - PyObject *setpgroup, int resetids, int setsid, - PyObject *setsigmask, PyObject *setsigdef, - PyObject *scheduler) -/*[clinic end generated code: output=14a1098c566bc675 input=808aed1090d84e33]*/ -{ - return py_posix_spawn(0, module, path, argv, env, file_actions, - setpgroup, resetids, setsid, setsigmask, setsigdef, - scheduler); -} - #endif /* HAVE_POSIX_SPAWN */ - - - -#ifdef HAVE_POSIX_SPAWNP -/*[clinic input] - -os.posix_spawnp - path: path_t - Path of executable file. - argv: object - Tuple or list of strings. - env: object - Dictionary of strings mapping to strings. - / - * - file_actions: object(c_default='NULL') = () - A sequence of file action tuples. - setpgroup: object = NULL - The pgroup to use with the POSIX_SPAWN_SETPGROUP flag. - resetids: bool = False - If the value is `True` the POSIX_SPAWN_RESETIDS will be activated. - setsid: bool = False - If the value is `True` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP will be activated. - setsigmask: object(c_default='NULL') = () - The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag. - setsigdef: object(c_default='NULL') = () - The sigmask to use with the POSIX_SPAWN_SETSIGDEF flag. - scheduler: object = NULL - A tuple with the scheduler policy (optional) and parameters. - -Execute the program specified by path in a new process. -[clinic start generated code]*/ - -static PyObject * -os_posix_spawnp_impl(PyObject *module, path_t *path, PyObject *argv, - PyObject *env, PyObject *file_actions, - PyObject *setpgroup, int resetids, int setsid, - PyObject *setsigmask, PyObject *setsigdef, - PyObject *scheduler) -/*[clinic end generated code: output=7b9aaefe3031238d input=9e89e616116752a1]*/ -{ - return py_posix_spawn(1, module, path, argv, env, file_actions, - setpgroup, resetids, setsid, setsigmask, setsigdef, - scheduler); -} -#endif /* HAVE_POSIX_SPAWNP */ - -#ifdef HAVE_RTPSPAWN -static intptr_t -_rtp_spawn(int mode, const char *rtpFileName, const char *argv[], - const char *envp[]) -{ - RTP_ID rtpid; - int status; - pid_t res; - int async_err = 0; - - /* Set priority=100 and uStackSize=16 MiB (0x1000000) for new processes. - uStackSize=0 cannot be used, the default stack size is too small for - Python. */ - if (envp) { - rtpid = rtpSpawn(rtpFileName, argv, envp, - 100, 0x1000000, 0, VX_FP_TASK); - } - else { - rtpid = rtpSpawn(rtpFileName, argv, (const char **)environ, - 100, 0x1000000, 0, VX_FP_TASK); - } - if ((rtpid != RTP_ID_ERROR) && (mode == _P_WAIT)) { - do { - res = waitpid((pid_t)rtpid, &status, 0); - } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - - if (res < 0) - return RTP_ID_ERROR; - return ((intptr_t)status); - } - return ((intptr_t)rtpid); -} -#endif - -#if defined(HAVE_SPAWNV) || defined(HAVE_WSPAWNV) || defined(HAVE_RTPSPAWN) -/*[clinic input] -os.spawnv - - mode: int - Mode of process creation. - path: path_t - Path of executable file. - argv: object - Tuple or list of strings. - / - -Execute the program specified by path in a new process. -[clinic start generated code]*/ - -static PyObject * -os_spawnv_impl(PyObject *module, int mode, path_t *path, PyObject *argv) -/*[clinic end generated code: output=71cd037a9d96b816 input=43224242303291be]*/ -{ - EXECV_CHAR **argvlist; - int i; - Py_ssize_t argc; - intptr_t spawnval; - PyObject *(*getitem)(PyObject *, Py_ssize_t); - - /* spawnv has three arguments: (mode, path, argv), where - argv is a list or tuple of strings. */ - - if (PyList_Check(argv)) { - argc = PyList_Size(argv); - getitem = PyList_GetItem; - } - else if (PyTuple_Check(argv)) { - argc = PyTuple_Size(argv); - getitem = PyTuple_GetItem; - } - else { - PyErr_SetString(PyExc_TypeError, - "spawnv() arg 2 must be a tuple or list"); - return NULL; - } - if (argc == 0) { - PyErr_SetString(PyExc_ValueError, - "spawnv() arg 2 cannot be empty"); - return NULL; - } - - argvlist = PyMem_NEW(EXECV_CHAR *, argc+1); - if (argvlist == NULL) { - return PyErr_NoMemory(); - } - for (i = 0; i < argc; i++) { - if (!fsconvert_strdup((*getitem)(argv, i), - &argvlist[i])) { - free_string_array(argvlist, i); - PyErr_SetString( - PyExc_TypeError, - "spawnv() arg 2 must contain only strings"); - return NULL; - } - if (i == 0 && !argvlist[0][0]) { - free_string_array(argvlist, i + 1); - PyErr_SetString( - PyExc_ValueError, - "spawnv() arg 2 first element cannot be empty"); - return NULL; - } - } - argvlist[argc] = NULL; - -#if !defined(HAVE_RTPSPAWN) - if (mode == _OLD_P_OVERLAY) - mode = _P_OVERLAY; -#endif - - if (PySys_Audit("os.spawn", "iOOO", mode, path->object, argv, - Py_None) < 0) { - free_string_array(argvlist, argc); - return NULL; - } - - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH -#ifdef HAVE_WSPAWNV - spawnval = _wspawnv(mode, path->wide, argvlist); -#elif defined(HAVE_RTPSPAWN) - spawnval = _rtp_spawn(mode, path->narrow, (const char **)argvlist, NULL); -#else - spawnval = _spawnv(mode, path->narrow, argvlist); -#endif - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - - int saved_errno = errno; - free_string_array(argvlist, argc); - - if (spawnval == -1) { - errno = saved_errno; - posix_error(); - return NULL; - } - return Py_BuildValue(_Py_PARSE_INTPTR, spawnval); -} - -/*[clinic input] -os.spawnve - - mode: int - Mode of process creation. - path: path_t - Path of executable file. - argv: object - Tuple or list of strings. - env: object - Dictionary of strings mapping to strings. - / - -Execute the program specified by path in a new process. -[clinic start generated code]*/ - -static PyObject * -os_spawnve_impl(PyObject *module, int mode, path_t *path, PyObject *argv, - PyObject *env) -/*[clinic end generated code: output=30fe85be56fe37ad input=3e40803ee7c4c586]*/ -{ - EXECV_CHAR **argvlist; - EXECV_CHAR **envlist; - PyObject *res = NULL; - Py_ssize_t argc, i, envc; - intptr_t spawnval; - PyObject *(*getitem)(PyObject *, Py_ssize_t); - Py_ssize_t lastarg = 0; - - /* spawnve has four arguments: (mode, path, argv, env), where - argv is a list or tuple of strings and env is a dictionary - like posix.environ. */ - - if (PyList_Check(argv)) { - argc = PyList_Size(argv); - getitem = PyList_GetItem; - } - else if (PyTuple_Check(argv)) { - argc = PyTuple_Size(argv); - getitem = PyTuple_GetItem; - } - else { - PyErr_SetString(PyExc_TypeError, - "spawnve() arg 2 must be a tuple or list"); - goto fail_0; - } - if (argc == 0) { - PyErr_SetString(PyExc_ValueError, - "spawnve() arg 2 cannot be empty"); - goto fail_0; - } - if (!PyMapping_Check(env)) { - PyErr_SetString(PyExc_TypeError, - "spawnve() arg 3 must be a mapping object"); - goto fail_0; - } - - argvlist = PyMem_NEW(EXECV_CHAR *, argc+1); - if (argvlist == NULL) { - PyErr_NoMemory(); - goto fail_0; - } - for (i = 0; i < argc; i++) { - if (!fsconvert_strdup((*getitem)(argv, i), - &argvlist[i])) - { - lastarg = i; - goto fail_1; - } - if (i == 0 && !argvlist[0][0]) { - lastarg = i + 1; - PyErr_SetString( - PyExc_ValueError, - "spawnv() arg 2 first element cannot be empty"); - goto fail_1; - } - } - lastarg = argc; - argvlist[argc] = NULL; - - envlist = parse_envlist(env, &envc); - if (envlist == NULL) - goto fail_1; - -#if !defined(HAVE_RTPSPAWN) - if (mode == _OLD_P_OVERLAY) - mode = _P_OVERLAY; -#endif - - if (PySys_Audit("os.spawn", "iOOO", mode, path->object, argv, env) < 0) { - goto fail_2; - } - - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH -#ifdef HAVE_WSPAWNV - spawnval = _wspawnve(mode, path->wide, argvlist, envlist); -#elif defined(HAVE_RTPSPAWN) - spawnval = _rtp_spawn(mode, path->narrow, (const char **)argvlist, - (const char **)envlist); -#else - spawnval = _spawnve(mode, path->narrow, argvlist, envlist); -#endif - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - - if (spawnval == -1) - (void) posix_error(); - else - res = Py_BuildValue(_Py_PARSE_INTPTR, spawnval); - - fail_2: - while (--envc >= 0) { - PyMem_Free(envlist[envc]); - } - PyMem_Free(envlist); - fail_1: - free_string_array(argvlist, lastarg); - fail_0: - return res; -} - -#endif /* HAVE_SPAWNV */ - -#ifdef HAVE_FORK - -/* Helper function to validate arguments. - Returns 0 on success. non-zero on failure with a TypeError raised. - If obj is non-NULL it must be callable. */ -static int -check_null_or_callable(PyObject *obj, const char* obj_name) -{ - if (obj && !PyCallable_Check(obj)) { - PyErr_Format(PyExc_TypeError, "'%s' must be callable, not %s", - obj_name, _PyType_Name(Py_TYPE(obj))); - return -1; - } - return 0; -} - -/*[clinic input] -os.register_at_fork - - * - before: object=NULL - A callable to be called in the parent before the fork() syscall. - after_in_child: object=NULL - A callable to be called in the child after fork(). - after_in_parent: object=NULL - A callable to be called in the parent after fork(). - -Register callables to be called when forking a new process. - -'before' callbacks are called in reverse order. -'after_in_child' and 'after_in_parent' callbacks are called in order. - -[clinic start generated code]*/ - -static PyObject * -os_register_at_fork_impl(PyObject *module, PyObject *before, - PyObject *after_in_child, PyObject *after_in_parent) -/*[clinic end generated code: output=5398ac75e8e97625 input=cd1187aa85d2312e]*/ -{ - PyInterpreterState *interp; - - if (!before && !after_in_child && !after_in_parent) { - PyErr_SetString(PyExc_TypeError, "At least one argument is required."); - return NULL; - } - if (check_null_or_callable(before, "before") || - check_null_or_callable(after_in_child, "after_in_child") || - check_null_or_callable(after_in_parent, "after_in_parent")) { - return NULL; - } - interp = _PyInterpreterState_GET(); - - if (register_at_forker(&interp->before_forkers, before)) { - return NULL; - } - if (register_at_forker(&interp->after_forkers_child, after_in_child)) { - return NULL; - } - if (register_at_forker(&interp->after_forkers_parent, after_in_parent)) { - return NULL; - } - Py_RETURN_NONE; -} -#endif /* HAVE_FORK */ - -#if defined(HAVE_FORK1) || defined(HAVE_FORKPTY) || defined(HAVE_FORK) -// Common code to raise a warning if we detect there is more than one thread -// running in the process. Best effort, silent if unable to count threads. -// Constraint: Quick. Never overcounts. Never leaves an error set. -// -// This should only be called from the parent process after -// PyOS_AfterFork_Parent(). -static void -warn_about_fork_with_threads(const char* name) -{ - // It's not safe to issue the warning while the world is stopped, because - // other threads might be holding locks that we need, which would deadlock. - assert(!_PyRuntime.stoptheworld.world_stopped); - - // TODO: Consider making an `os` module API to return the current number - // of threads in the process. That'd presumably use this platform code but - // raise an error rather than using the inaccurate fallback. - Py_ssize_t num_python_threads = 0; -#if defined(__APPLE__) && defined(HAVE_GETPID) - mach_port_t macos_self = mach_task_self(); - mach_port_t macos_task; - if (task_for_pid(macos_self, getpid(), &macos_task) == KERN_SUCCESS) { - thread_array_t macos_threads; - mach_msg_type_number_t macos_n_threads; - if (task_threads(macos_task, &macos_threads, - &macos_n_threads) == KERN_SUCCESS) { - num_python_threads = macos_n_threads; - } - } -#elif defined(__linux__) - // Linux /proc/self/stat 20th field is the number of threads. - FILE* proc_stat = fopen("/proc/self/stat", "r"); - if (proc_stat) { - size_t n; - // Size chosen arbitrarily. ~60% more bytes than a 20th column index - // observed on the author's workstation. - char stat_line[160]; - n = fread(&stat_line, 1, 159, proc_stat); - stat_line[n] = '\0'; - fclose(proc_stat); - - char *saveptr = NULL; - char *field = strtok_r(stat_line, " ", &saveptr); - unsigned int idx; - for (idx = 19; idx && field; --idx) { - field = strtok_r(NULL, " ", &saveptr); - } - if (idx == 0 && field) { // found the 20th field - num_python_threads = atoi(field); // 0 on error - } - } -#endif - if (num_python_threads <= 0) { - // Fall back to just the number our threading module knows about. - // An incomplete view of the world, but better than nothing. - PyObject *threading = PyImport_GetModule(&_Py_ID(threading)); - if (!threading) { - PyErr_Clear(); - return; - } - PyObject *threading_active = - PyObject_GetAttr(threading, &_Py_ID(_active)); - if (!threading_active) { - PyErr_Clear(); - Py_DECREF(threading); - return; - } - PyObject *threading_limbo = - PyObject_GetAttr(threading, &_Py_ID(_limbo)); - if (!threading_limbo) { - PyErr_Clear(); - Py_DECREF(threading); - Py_DECREF(threading_active); - return; - } - Py_DECREF(threading); - // Duplicating what threading.active_count() does but without holding - // threading._active_limbo_lock so our count could be inaccurate if - // these dicts are mid-update from another thread. Not a big deal. - // Worst case if someone replaced threading._active or threading._limbo - // with non-dicts, we get -1 from *Length() below and undercount. - // Nobody should, but we're best effort so we clear errors and move on. - num_python_threads = (PyMapping_Length(threading_active) - + PyMapping_Length(threading_limbo)); - PyErr_Clear(); - Py_DECREF(threading_active); - Py_DECREF(threading_limbo); - } - if (num_python_threads > 1) { - PyErr_WarnFormat( - PyExc_DeprecationWarning, 1, -#ifdef HAVE_GETPID - "This process (pid=%d) is multi-threaded, " -#else - "This process is multi-threaded, " -#endif - "use of %s() may lead to deadlocks in the child.", -#ifdef HAVE_GETPID - getpid(), -#endif - name); - PyErr_Clear(); - } -} -#endif // HAVE_FORK1 || HAVE_FORKPTY || HAVE_FORK - -#ifdef HAVE_FORK1 -/*[clinic input] -os.fork1 - -Fork a child process with a single multiplexed (i.e., not bound) thread. - -Return 0 to child process and PID of child to parent process. -[clinic start generated code]*/ - -static PyObject * -os_fork1_impl(PyObject *module) -/*[clinic end generated code: output=0de8e67ce2a310bc input=12db02167893926e]*/ -{ - pid_t pid; - - PyInterpreterState *interp = _PyInterpreterState_GET(); - if (_PyInterpreterState_GetFinalizing(interp) != NULL) { - PyErr_SetString(PyExc_PythonFinalizationError, - "can't fork at interpreter shutdown"); - return NULL; - } - if (!_Py_IsMainInterpreter(interp)) { - PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters"); - return NULL; - } - PyOS_BeforeFork(); - pid = fork1(); - int saved_errno = errno; - if (pid == 0) { - /* child: this clobbers and resets the import lock. */ - PyOS_AfterFork_Child(); - } else { - /* parent: release the import lock. */ - PyOS_AfterFork_Parent(); - // After PyOS_AfterFork_Parent() starts the world to avoid deadlock. - warn_about_fork_with_threads("fork1"); - } - if (pid == -1) { - errno = saved_errno; - return posix_error(); - } - return PyLong_FromPid(pid); -} -#endif /* HAVE_FORK1 */ - - -#ifdef HAVE_FORK -/*[clinic input] -os.fork - -Fork a child process. - -Return 0 to child process and PID of child to parent process. -[clinic start generated code]*/ - -static PyObject * -os_fork_impl(PyObject *module) -/*[clinic end generated code: output=3626c81f98985d49 input=13c956413110eeaa]*/ -{ - pid_t pid; - PyInterpreterState *interp = _PyInterpreterState_GET(); - if (_PyInterpreterState_GetFinalizing(interp) != NULL) { - PyErr_SetString(PyExc_PythonFinalizationError, - "can't fork at interpreter shutdown"); - return NULL; - } - if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_FORK)) { - PyErr_SetString(PyExc_RuntimeError, - "fork not supported for isolated subinterpreters"); - return NULL; - } - if (PySys_Audit("os.fork", NULL) < 0) { - return NULL; - } - PyOS_BeforeFork(); - pid = fork(); - int saved_errno = errno; - if (pid == 0) { - /* child: this clobbers and resets the import lock. */ - PyOS_AfterFork_Child(); - } else { - /* parent: release the import lock. */ - PyOS_AfterFork_Parent(); - // After PyOS_AfterFork_Parent() starts the world to avoid deadlock. - warn_about_fork_with_threads("fork"); - } - if (pid == -1) { - errno = saved_errno; - return posix_error(); - } - return PyLong_FromPid(pid); -} -#endif /* HAVE_FORK */ - - -#ifdef HAVE_SCHED_H -#ifdef HAVE_SCHED_GET_PRIORITY_MAX -/*[clinic input] -os.sched_get_priority_max - - policy: int - -Get the maximum scheduling priority for policy. -[clinic start generated code]*/ - -static PyObject * -os_sched_get_priority_max_impl(PyObject *module, int policy) -/*[clinic end generated code: output=9e465c6e43130521 input=2097b7998eca6874]*/ -{ - int max; - - max = sched_get_priority_max(policy); - if (max < 0) - return posix_error(); - return PyLong_FromLong(max); -} - - -/*[clinic input] -os.sched_get_priority_min - - policy: int - -Get the minimum scheduling priority for policy. -[clinic start generated code]*/ - -static PyObject * -os_sched_get_priority_min_impl(PyObject *module, int policy) -/*[clinic end generated code: output=7595c1138cc47a6d input=21bc8fa0d70983bf]*/ -{ - int min = sched_get_priority_min(policy); - if (min < 0) - return posix_error(); - return PyLong_FromLong(min); -} -#endif /* HAVE_SCHED_GET_PRIORITY_MAX */ - - -#ifdef HAVE_SCHED_SETSCHEDULER -/*[clinic input] -os.sched_getscheduler - pid: pid_t - / - -Get the scheduling policy for the process identified by pid. - -Passing 0 for pid returns the scheduling policy for the calling process. -[clinic start generated code]*/ - -static PyObject * -os_sched_getscheduler_impl(PyObject *module, pid_t pid) -/*[clinic end generated code: output=dce4c0bd3f1b34c8 input=8d99dac505485ac8]*/ -{ - int policy; - - policy = sched_getscheduler(pid); - if (policy < 0) - return posix_error(); - return PyLong_FromLong(policy); -} -#endif /* HAVE_SCHED_SETSCHEDULER */ - - -#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) -/*[clinic input] -class os.sched_param "PyObject *" "SchedParamType" - -@classmethod -os.sched_param.__new__ - - sched_priority: object - A scheduling parameter. - -Currently has only one field: sched_priority -[clinic start generated code]*/ - -static PyObject * -os_sched_param_impl(PyTypeObject *type, PyObject *sched_priority) -/*[clinic end generated code: output=48f4067d60f48c13 input=eb42909a2c0e3e6c]*/ -{ - PyObject *res; - - res = PyStructSequence_New(type); - if (!res) - return NULL; - PyStructSequence_SET_ITEM(res, 0, Py_NewRef(sched_priority)); - return res; -} - -static PyObject * -os_sched_param_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - return Py_BuildValue("(O(N))", Py_TYPE(self), PyStructSequence_GetItem(self, 0)); -} - -static PyMethodDef os_sched_param_reduce_method = { - "__reduce__", (PyCFunction)os_sched_param_reduce, METH_NOARGS|METH_COEXIST, NULL, -}; - -PyDoc_VAR(os_sched_param__doc__); - -static PyStructSequence_Field sched_param_fields[] = { - {"sched_priority", "the scheduling priority"}, - {0} -}; - -static PyStructSequence_Desc sched_param_desc = { - "sched_param", /* name */ - os_sched_param__doc__, /* doc */ - sched_param_fields, - 1 -}; - -static int -convert_sched_param(PyObject *module, PyObject *param, struct sched_param *res) -{ - long priority; - - if (!Py_IS_TYPE(param, (PyTypeObject *)get_posix_state(module)->SchedParamType)) { - PyErr_SetString(PyExc_TypeError, "must have a sched_param object"); - return 0; - } - priority = PyLong_AsLong(PyStructSequence_GET_ITEM(param, 0)); - if (priority == -1 && PyErr_Occurred()) - return 0; - if (priority > INT_MAX || priority < INT_MIN) { - PyErr_SetString(PyExc_OverflowError, "sched_priority out of range"); - return 0; - } - res->sched_priority = Py_SAFE_DOWNCAST(priority, long, int); - return 1; -} -#endif /* defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) */ - - -#ifdef HAVE_SCHED_SETSCHEDULER -/*[clinic input] -os.sched_setscheduler - - pid: pid_t - policy: int - param as param_obj: object - / - -Set the scheduling policy for the process identified by pid. - -If pid is 0, the calling process is changed. -param is an instance of sched_param. -[clinic start generated code]*/ - -static PyObject * -os_sched_setscheduler_impl(PyObject *module, pid_t pid, int policy, - PyObject *param_obj) -/*[clinic end generated code: output=cde27faa55dc993e input=73013d731bd8fbe9]*/ -{ - struct sched_param param; - if (!convert_sched_param(module, param_obj, ¶m)) { - return NULL; - } - - /* - ** sched_setscheduler() returns 0 in Linux, but the previous - ** scheduling policy under Solaris/Illumos, and others. - ** On error, -1 is returned in all Operating Systems. - */ - if (sched_setscheduler(pid, policy, ¶m) == -1) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_SCHED_SETSCHEDULER*/ - - -#ifdef HAVE_SCHED_SETPARAM -/*[clinic input] -os.sched_getparam - pid: pid_t - / - -Returns scheduling parameters for the process identified by pid. - -If pid is 0, returns parameters for the calling process. -Return value is an instance of sched_param. -[clinic start generated code]*/ - -static PyObject * -os_sched_getparam_impl(PyObject *module, pid_t pid) -/*[clinic end generated code: output=b194e8708dcf2db8 input=18a1ef9c2efae296]*/ -{ - struct sched_param param; - PyObject *result; - PyObject *priority; - - if (sched_getparam(pid, ¶m)) - return posix_error(); - PyObject *SchedParamType = get_posix_state(module)->SchedParamType; - result = PyStructSequence_New((PyTypeObject *)SchedParamType); - if (!result) - return NULL; - priority = PyLong_FromLong(param.sched_priority); - if (!priority) { - Py_DECREF(result); - return NULL; - } - PyStructSequence_SET_ITEM(result, 0, priority); - return result; -} - - -/*[clinic input] -os.sched_setparam - pid: pid_t - param as param_obj: object - / - -Set scheduling parameters for the process identified by pid. - -If pid is 0, sets parameters for the calling process. -param should be an instance of sched_param. -[clinic start generated code]*/ - -static PyObject * -os_sched_setparam_impl(PyObject *module, pid_t pid, PyObject *param_obj) -/*[clinic end generated code: output=f19fe020a53741c1 input=27b98337c8b2dcc7]*/ -{ - struct sched_param param; - if (!convert_sched_param(module, param_obj, ¶m)) { - return NULL; - } - - if (sched_setparam(pid, ¶m)) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_SCHED_SETPARAM */ - - -#ifdef HAVE_SCHED_RR_GET_INTERVAL -/*[clinic input] -os.sched_rr_get_interval -> double - pid: pid_t - / - -Return the round-robin quantum for the process identified by pid, in seconds. - -Value returned is a float. -[clinic start generated code]*/ - -static double -os_sched_rr_get_interval_impl(PyObject *module, pid_t pid) -/*[clinic end generated code: output=7e2d935833ab47dc input=2a973da15cca6fae]*/ -{ - struct timespec interval; - if (sched_rr_get_interval(pid, &interval)) { - posix_error(); - return -1.0; - } -#ifdef _Py_MEMORY_SANITIZER - __msan_unpoison(&interval, sizeof(interval)); -#endif - return (double)interval.tv_sec + 1e-9*interval.tv_nsec; -} -#endif /* HAVE_SCHED_RR_GET_INTERVAL */ - - -/*[clinic input] -os.sched_yield - -Voluntarily relinquish the CPU. -[clinic start generated code]*/ - -static PyObject * -os_sched_yield_impl(PyObject *module) -/*[clinic end generated code: output=902323500f222cac input=e54d6f98189391d4]*/ -{ - int result; - Py_BEGIN_ALLOW_THREADS - result = sched_yield(); - Py_END_ALLOW_THREADS - if (result < 0) { - return posix_error(); - } - Py_RETURN_NONE; -} - -#ifdef HAVE_SCHED_SETAFFINITY -/* The minimum number of CPUs allocated in a cpu_set_t */ -static const int NCPUS_START = sizeof(unsigned long) * CHAR_BIT; - -/*[clinic input] -os.sched_setaffinity - pid: pid_t - mask : object - / - -Set the CPU affinity of the process identified by pid to mask. - -mask should be an iterable of integers identifying CPUs. -[clinic start generated code]*/ - -static PyObject * -os_sched_setaffinity_impl(PyObject *module, pid_t pid, PyObject *mask) -/*[clinic end generated code: output=882d7dd9a229335b input=a0791a597c7085ba]*/ -{ - int ncpus; - size_t setsize; - cpu_set_t *cpu_set = NULL; - PyObject *iterator = NULL, *item; - - iterator = PyObject_GetIter(mask); - if (iterator == NULL) - return NULL; - - ncpus = NCPUS_START; - setsize = CPU_ALLOC_SIZE(ncpus); - cpu_set = CPU_ALLOC(ncpus); - if (cpu_set == NULL) { - PyErr_NoMemory(); - goto error; - } - CPU_ZERO_S(setsize, cpu_set); - - while ((item = PyIter_Next(iterator))) { - long cpu; - if (!PyLong_Check(item)) { - PyErr_Format(PyExc_TypeError, - "expected an iterator of ints, " - "but iterator yielded %R", - Py_TYPE(item)); - Py_DECREF(item); - goto error; - } - cpu = PyLong_AsLong(item); - Py_DECREF(item); - if (cpu < 0) { - if (!PyErr_Occurred()) - PyErr_SetString(PyExc_ValueError, "negative CPU number"); - goto error; - } - if (cpu > INT_MAX - 1) { - PyErr_SetString(PyExc_OverflowError, "CPU number too large"); - goto error; - } - if (cpu >= ncpus) { - /* Grow CPU mask to fit the CPU number */ - int newncpus = ncpus; - cpu_set_t *newmask; - size_t newsetsize; - while (newncpus <= cpu) { - if (newncpus > INT_MAX / 2) - newncpus = cpu + 1; - else - newncpus = newncpus * 2; - } - newmask = CPU_ALLOC(newncpus); - if (newmask == NULL) { - PyErr_NoMemory(); - goto error; - } - newsetsize = CPU_ALLOC_SIZE(newncpus); - CPU_ZERO_S(newsetsize, newmask); - memcpy(newmask, cpu_set, setsize); - CPU_FREE(cpu_set); - setsize = newsetsize; - cpu_set = newmask; - ncpus = newncpus; - } - CPU_SET_S(cpu, setsize, cpu_set); - } - if (PyErr_Occurred()) { - goto error; - } - Py_CLEAR(iterator); - - if (sched_setaffinity(pid, setsize, cpu_set)) { - posix_error(); - goto error; - } - CPU_FREE(cpu_set); - Py_RETURN_NONE; - -error: - if (cpu_set) - CPU_FREE(cpu_set); - Py_XDECREF(iterator); - return NULL; -} - - -/*[clinic input] -os.sched_getaffinity - pid: pid_t - / - -Return the affinity of the process identified by pid (or the current process if zero). - -The affinity is returned as a set of CPU identifiers. -[clinic start generated code]*/ - -static PyObject * -os_sched_getaffinity_impl(PyObject *module, pid_t pid) -/*[clinic end generated code: output=f726f2c193c17a4f input=983ce7cb4a565980]*/ -{ - int ncpus = NCPUS_START; - size_t setsize; - cpu_set_t *mask; - - while (1) { - setsize = CPU_ALLOC_SIZE(ncpus); - mask = CPU_ALLOC(ncpus); - if (mask == NULL) { - return PyErr_NoMemory(); - } - if (sched_getaffinity(pid, setsize, mask) == 0) { - break; - } - CPU_FREE(mask); - if (errno != EINVAL) { - return posix_error(); - } - if (ncpus > INT_MAX / 2) { - PyErr_SetString(PyExc_OverflowError, - "could not allocate a large enough CPU set"); - return NULL; - } - ncpus *= 2; - } - - PyObject *res = PySet_New(NULL); - if (res == NULL) { - goto error; - } - - int cpu = 0; - int count = CPU_COUNT_S(setsize, mask); - for (; count; cpu++) { - if (CPU_ISSET_S(cpu, setsize, mask)) { - PyObject *cpu_num = PyLong_FromLong(cpu); - --count; - if (cpu_num == NULL) { - goto error; - } - if (PySet_Add(res, cpu_num)) { - Py_DECREF(cpu_num); - goto error; - } - Py_DECREF(cpu_num); - } - } - CPU_FREE(mask); - return res; - -error: - if (mask) { - CPU_FREE(mask); - } - Py_XDECREF(res); - return NULL; -} -#endif /* HAVE_SCHED_SETAFFINITY */ - -#endif /* HAVE_SCHED_H */ - - -#ifdef HAVE_POSIX_OPENPT -/*[clinic input] -os.posix_openpt -> int - - oflag: int - / - -Open and return a file descriptor for a master pseudo-terminal device. - -Performs a posix_openpt() C function call. The oflag argument is used to -set file status flags and file access modes as specified in the manual page -of posix_openpt() of your system. -[clinic start generated code]*/ - -static int -os_posix_openpt_impl(PyObject *module, int oflag) -/*[clinic end generated code: output=ee0bc2624305fc79 input=0de33d0e29693caa]*/ -{ - int fd; - -#if defined(O_CLOEXEC) - oflag |= O_CLOEXEC; -#endif - - fd = posix_openpt(oflag); - if (fd == -1) { - posix_error(); - return -1; - } - - // Just in case, likely a no-op given O_CLOEXEC above. - if (_Py_set_inheritable(fd, 0, NULL) < 0) { - close(fd); - return -1; - } - - return fd; -} -#endif /* HAVE_POSIX_OPENPT */ - -#ifdef HAVE_GRANTPT -/*[clinic input] -os.grantpt - - fd: fildes - File descriptor of a master pseudo-terminal device. - / - -Grant access to the slave pseudo-terminal device. - -Performs a grantpt() C function call. -[clinic start generated code]*/ - -static PyObject * -os_grantpt_impl(PyObject *module, int fd) -/*[clinic end generated code: output=dfd580015cf548ab input=0668e3b96760e849]*/ -{ - int ret; - int saved_errno; - PyOS_sighandler_t sig_saved; - - sig_saved = PyOS_setsig(SIGCHLD, SIG_DFL); - - ret = grantpt(fd); - if (ret == -1) - saved_errno = errno; - - PyOS_setsig(SIGCHLD, sig_saved); - - if (ret == -1) { - errno = saved_errno; - return posix_error(); - } - - Py_RETURN_NONE; -} -#endif /* HAVE_GRANTPT */ - -#ifdef HAVE_UNLOCKPT -/*[clinic input] -os.unlockpt - - fd: fildes - File descriptor of a master pseudo-terminal device. - / - -Unlock a pseudo-terminal master/slave pair. - -Performs an unlockpt() C function call. -[clinic start generated code]*/ - -static PyObject * -os_unlockpt_impl(PyObject *module, int fd) -/*[clinic end generated code: output=e08d354dec12d30c input=de7ab1f59f69a2b4]*/ -{ - if (unlockpt(fd) == -1) - return posix_error(); - - Py_RETURN_NONE; -} -#endif /* HAVE_UNLOCKPT */ - -#if defined(HAVE_PTSNAME) || defined(HAVE_PTSNAME_R) -static PyObject * -py_ptsname(int fd) -{ - // POSIX manpage: Upon failure, ptsname() shall return a null pointer - // and may set errno. Always initialize errno to avoid undefined behavior. - errno = 0; - char *name = ptsname(fd); - if (name == NULL) { - return posix_error(); - } - return PyUnicode_DecodeFSDefault(name); -} - -/*[clinic input] -os.ptsname - - fd: fildes - File descriptor of a master pseudo-terminal device. - / - -Return the name of the slave pseudo-terminal device. - -If the ptsname_r() C function is available, it is called; -otherwise, performs a ptsname() C function call. -[clinic start generated code]*/ - -static PyObject * -os_ptsname_impl(PyObject *module, int fd) -/*[clinic end generated code: output=ef300fadc5675872 input=1369ccc0546f3130]*/ -{ -#ifdef HAVE_PTSNAME_R - int ret; - char name[MAXPATHLEN+1]; - - if (HAVE_PTSNAME_R_RUNTIME) { - ret = ptsname_r(fd, name, sizeof(name)); - } - else { - // fallback to ptsname() if ptsname_r() is not available in runtime. - return py_ptsname(fd); - } - if (ret != 0) { - errno = ret; - return posix_error(); - } - - return PyUnicode_DecodeFSDefault(name); -#else - return py_ptsname(fd); -#endif /* HAVE_PTSNAME_R */ -} -#endif /* defined(HAVE_PTSNAME) || defined(HAVE_PTSNAME_R) */ - -/* AIX uses /dev/ptc but is otherwise the same as /dev/ptmx */ -#if defined(HAVE_DEV_PTC) && !defined(HAVE_DEV_PTMX) -# define DEV_PTY_FILE "/dev/ptc" -# define HAVE_DEV_PTMX -#else -# define DEV_PTY_FILE "/dev/ptmx" -#endif - -#if defined(HAVE_OPENPTY) || defined(HAVE_FORKPTY) || defined(HAVE_LOGIN_TTY) || defined(HAVE_DEV_PTMX) -#ifdef HAVE_PTY_H -#include -#ifdef HAVE_UTMP_H -#include -#endif /* HAVE_UTMP_H */ -#elif defined(HAVE_LIBUTIL_H) -#include -#elif defined(HAVE_UTIL_H) -#include -#endif /* HAVE_PTY_H */ -#ifdef HAVE_STROPTS_H -#include -#endif -#endif /* defined(HAVE_OPENPTY) || defined(HAVE_FORKPTY) || defined(HAVE_LOGIN_TTY) || defined(HAVE_DEV_PTMX) */ - - -#if defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) -/*[clinic input] -os.openpty - -Open a pseudo-terminal. - -Return a tuple of (master_fd, slave_fd) containing open file descriptors -for both the master and slave ends. -[clinic start generated code]*/ - -static PyObject * -os_openpty_impl(PyObject *module) -/*[clinic end generated code: output=98841ce5ec9cef3c input=f3d99fd99e762907]*/ -{ - int master_fd = -1, slave_fd = -1; -#ifndef HAVE_OPENPTY - char * slave_name; -#endif -#if defined(HAVE_DEV_PTMX) && !defined(HAVE_OPENPTY) && !defined(HAVE__GETPTY) - PyOS_sighandler_t sig_saved; -#if defined(__sun) && defined(__SVR4) - extern char *ptsname(int fildes); -#endif -#endif - -#ifdef HAVE_OPENPTY - if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) != 0) - goto posix_error; - - if (_Py_set_inheritable(master_fd, 0, NULL) < 0) - goto error; - if (_Py_set_inheritable(slave_fd, 0, NULL) < 0) - goto error; - -#elif defined(HAVE__GETPTY) - slave_name = _getpty(&master_fd, O_RDWR, 0666, 0); - if (slave_name == NULL) - goto posix_error; - if (_Py_set_inheritable(master_fd, 0, NULL) < 0) - goto error; - - slave_fd = _Py_open(slave_name, O_RDWR); - if (slave_fd < 0) - goto error; - -#else - master_fd = open(DEV_PTY_FILE, O_RDWR | O_NOCTTY); /* open master */ - if (master_fd < 0) - goto posix_error; - - sig_saved = PyOS_setsig(SIGCHLD, SIG_DFL); - - /* change permission of slave */ - if (grantpt(master_fd) < 0) { - int saved_errno = errno; - PyOS_setsig(SIGCHLD, sig_saved); - errno = saved_errno; - goto posix_error; - } - - /* unlock slave */ - if (unlockpt(master_fd) < 0) { - int saved_errno = errno; - PyOS_setsig(SIGCHLD, sig_saved); - errno = saved_errno; - goto posix_error; - } - - PyOS_setsig(SIGCHLD, sig_saved); - - slave_name = ptsname(master_fd); /* get name of slave */ - if (slave_name == NULL) - goto posix_error; - - slave_fd = _Py_open(slave_name, O_RDWR | O_NOCTTY); /* open slave */ - if (slave_fd == -1) - goto error; - - if (_Py_set_inheritable(master_fd, 0, NULL) < 0) - goto posix_error; - -#if !defined(__CYGWIN__) && !defined(__ANDROID__) && !defined(HAVE_DEV_PTC) - ioctl(slave_fd, I_PUSH, "ptem"); /* push ptem */ - ioctl(slave_fd, I_PUSH, "ldterm"); /* push ldterm */ -#ifndef __hpux - ioctl(slave_fd, I_PUSH, "ttcompat"); /* push ttcompat */ -#endif /* __hpux */ -#endif /* HAVE_CYGWIN */ -#endif /* HAVE_OPENPTY */ - - return Py_BuildValue("(ii)", master_fd, slave_fd); - -posix_error: - posix_error(); -error: - if (master_fd != -1) - close(master_fd); - if (slave_fd != -1) - close(slave_fd); - return NULL; -} -#endif /* defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) */ - - -#if defined(HAVE_SETSID) && defined(TIOCSCTTY) -#define HAVE_FALLBACK_LOGIN_TTY 1 -#endif /* defined(HAVE_SETSID) && defined(TIOCSCTTY) */ - -#if defined(HAVE_LOGIN_TTY) || defined(HAVE_FALLBACK_LOGIN_TTY) -/*[clinic input] -os.login_tty - - fd: fildes - / - -Prepare the tty of which fd is a file descriptor for a new login session. - -Make the calling process a session leader; make the tty the -controlling tty, the stdin, the stdout, and the stderr of the -calling process; close fd. -[clinic start generated code]*/ - -static PyObject * -os_login_tty_impl(PyObject *module, int fd) -/*[clinic end generated code: output=495a79911b4cc1bc input=5f298565099903a2]*/ -{ -#ifdef HAVE_LOGIN_TTY - if (login_tty(fd) == -1) { - return posix_error(); - } -#else /* defined(HAVE_FALLBACK_LOGIN_TTY) */ - /* Establish a new session. */ - if (setsid() == -1) { - return posix_error(); - } - - /* The tty becomes the controlling terminal. */ - if (ioctl(fd, TIOCSCTTY, (char *)NULL) == -1) { - return posix_error(); - } - - /* The tty becomes stdin/stdout/stderr */ - if (dup2(fd, 0) == -1 || dup2(fd, 1) == -1 || dup2(fd, 2) == -1) { - return posix_error(); - } - if (fd > 2) { - close(fd); - } -#endif /* HAVE_LOGIN_TTY */ - Py_RETURN_NONE; -} -#endif /* defined(HAVE_LOGIN_TTY) || defined(HAVE_FALLBACK_LOGIN_TTY) */ - - -#ifdef HAVE_FORKPTY -/*[clinic input] -os.forkpty - -Fork a new process with a new pseudo-terminal as controlling tty. - -Returns a tuple of (pid, master_fd). -Like fork(), return pid of 0 to the child process, -and pid of child to the parent process. -To both, return fd of newly opened pseudo-terminal. -[clinic start generated code]*/ - -static PyObject * -os_forkpty_impl(PyObject *module) -/*[clinic end generated code: output=60d0a5c7512e4087 input=f1f7f4bae3966010]*/ -{ - int master_fd = -1; - pid_t pid; - - PyInterpreterState *interp = _PyInterpreterState_GET(); - if (_PyInterpreterState_GetFinalizing(interp) != NULL) { - PyErr_SetString(PyExc_PythonFinalizationError, - "can't fork at interpreter shutdown"); - return NULL; - } - if (!_Py_IsMainInterpreter(interp)) { - PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters"); - return NULL; - } - if (PySys_Audit("os.forkpty", NULL) < 0) { - return NULL; - } - PyOS_BeforeFork(); - pid = forkpty(&master_fd, NULL, NULL, NULL); - if (pid == 0) { - /* child: this clobbers and resets the import lock. */ - PyOS_AfterFork_Child(); - } else { - /* parent: release the import lock. */ - PyOS_AfterFork_Parent(); - // After PyOS_AfterFork_Parent() starts the world to avoid deadlock. - warn_about_fork_with_threads("forkpty"); - } - if (pid == -1) { - return posix_error(); - } - return Py_BuildValue("(Ni)", PyLong_FromPid(pid), master_fd); -} -#endif /* HAVE_FORKPTY */ - - -#ifdef HAVE_GETEGID -/*[clinic input] -os.getegid - -Return the current process's effective group id. -[clinic start generated code]*/ - -static PyObject * -os_getegid_impl(PyObject *module) -/*[clinic end generated code: output=67d9be7ac68898a2 input=1596f79ad1107d5d]*/ -{ - return _PyLong_FromGid(getegid()); -} -#endif /* HAVE_GETEGID */ - - -#ifdef HAVE_GETEUID -/*[clinic input] -os.geteuid - -Return the current process's effective user id. -[clinic start generated code]*/ - -static PyObject * -os_geteuid_impl(PyObject *module) -/*[clinic end generated code: output=ea1b60f0d6abb66e input=4644c662d3bd9f19]*/ -{ - return _PyLong_FromUid(geteuid()); -} -#endif /* HAVE_GETEUID */ - - -#ifdef HAVE_GETGID -/*[clinic input] -os.getgid - -Return the current process's group id. -[clinic start generated code]*/ - -static PyObject * -os_getgid_impl(PyObject *module) -/*[clinic end generated code: output=4f28ebc9d3e5dfcf input=58796344cd87c0f6]*/ -{ - return _PyLong_FromGid(getgid()); -} -#endif /* HAVE_GETGID */ - - -#if defined(HAVE_GETPID) -/*[clinic input] -os.getpid - -Return the current process id. -[clinic start generated code]*/ - -static PyObject * -os_getpid_impl(PyObject *module) -/*[clinic end generated code: output=9ea6fdac01ed2b3c input=5a9a00f0ab68aa00]*/ -{ -#if !defined(MS_WINDOWS) || defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) - return PyLong_FromPid(getpid()); -#else - return PyLong_FromUnsignedLong(GetCurrentProcessId()); -#endif -} -#endif /* defined(HAVE_GETPID) */ - -#ifdef NGROUPS_MAX -#define MAX_GROUPS NGROUPS_MAX -#else - /* defined to be 16 on Solaris7, so this should be a small number */ -#define MAX_GROUPS 64 -#endif - -#ifdef HAVE_GETGROUPLIST - -#ifdef __APPLE__ -/*[clinic input] -os.getgrouplist - - user: str - username to lookup - group as basegid: int - base group id of the user - / - -Returns a list of groups to which a user belongs. -[clinic start generated code]*/ - -static PyObject * -os_getgrouplist_impl(PyObject *module, const char *user, int basegid) -/*[clinic end generated code: output=6e734697b8c26de0 input=f8d870374b09a490]*/ -#else -/*[clinic input] -os.getgrouplist - - user: str - username to lookup - group as basegid: gid_t - base group id of the user - / - -Returns a list of groups to which a user belongs. -[clinic start generated code]*/ - -static PyObject * -os_getgrouplist_impl(PyObject *module, const char *user, gid_t basegid) -/*[clinic end generated code: output=0ebd7fb70115575b input=cc61d5c20b08958d]*/ -#endif -{ - int i, ngroups; - PyObject *list; -#ifdef __APPLE__ - int *groups; -#else - gid_t *groups; -#endif - - /* - * NGROUPS_MAX is defined by POSIX.1 as the maximum - * number of supplimental groups a users can belong to. - * We have to increment it by one because - * getgrouplist() returns both the supplemental groups - * and the primary group, i.e. all of the groups the - * user belongs to. - */ - ngroups = 1 + MAX_GROUPS; - - while (1) { -#ifdef __APPLE__ - groups = PyMem_New(int, ngroups); -#else - groups = PyMem_New(gid_t, ngroups); -#endif - if (groups == NULL) { - return PyErr_NoMemory(); - } - - int old_ngroups = ngroups; - if (getgrouplist(user, basegid, groups, &ngroups) != -1) { - /* Success */ - break; - } - - /* getgrouplist() fails if the group list is too small */ - PyMem_Free(groups); - - if (ngroups > old_ngroups) { - /* If the group list is too small, the glibc implementation of - getgrouplist() sets ngroups to the total number of groups and - returns -1. */ - } - else { - /* Double the group list size */ - if (ngroups > INT_MAX / 2) { - return PyErr_NoMemory(); - } - ngroups *= 2; - } - - /* Retry getgrouplist() with a larger group list */ - } - -#ifdef _Py_MEMORY_SANITIZER - /* Clang memory sanitizer libc intercepts don't know getgrouplist. */ - __msan_unpoison(&ngroups, sizeof(ngroups)); - __msan_unpoison(groups, ngroups*sizeof(*groups)); -#endif - - list = PyList_New(ngroups); - if (list == NULL) { - PyMem_Free(groups); - return NULL; - } - - for (i = 0; i < ngroups; i++) { -#ifdef __APPLE__ - PyObject *o = PyLong_FromUnsignedLong((unsigned long)groups[i]); -#else - PyObject *o = _PyLong_FromGid(groups[i]); -#endif - if (o == NULL) { - Py_DECREF(list); - PyMem_Free(groups); - return NULL; - } - PyList_SET_ITEM(list, i, o); - } - - PyMem_Free(groups); - - return list; -} -#endif /* HAVE_GETGROUPLIST */ - - -#ifdef HAVE_GETGROUPS -/*[clinic input] -os.getgroups - -Return list of supplemental group IDs for the process. -[clinic start generated code]*/ - -static PyObject * -os_getgroups_impl(PyObject *module) -/*[clinic end generated code: output=42b0c17758561b56 input=d3f109412e6a155c]*/ -{ - // Call getgroups with length 0 to get the actual number of groups - int n = getgroups(0, NULL); - if (n < 0) { - return posix_error(); - } - - if (n == 0) { - return PyList_New(0); - } - - gid_t *grouplist = PyMem_New(gid_t, n); - if (grouplist == NULL) { - return PyErr_NoMemory(); - } - - n = getgroups(n, grouplist); - if (n == -1) { - posix_error(); - PyMem_Free(grouplist); - return NULL; - } - - PyObject *result = PyList_New(n); - if (result == NULL) { - goto error; - } - - for (int i = 0; i < n; ++i) { - PyObject *group = _PyLong_FromGid(grouplist[i]); - if (group == NULL) { - goto error; - } - PyList_SET_ITEM(result, i, group); - } - PyMem_Free(grouplist); - - return result; - -error: - PyMem_Free(grouplist); - Py_XDECREF(result); - return NULL; -} -#endif /* HAVE_GETGROUPS */ - -#ifdef HAVE_INITGROUPS -#ifdef __APPLE__ -/*[clinic input] -os.initgroups - - username as oname: FSConverter - gid: int - / - -Initialize the group access list. - -Call the system initgroups() to initialize the group access list with all of -the groups of which the specified username is a member, plus the specified -group id. -[clinic start generated code]*/ - -static PyObject * -os_initgroups_impl(PyObject *module, PyObject *oname, int gid) -/*[clinic end generated code: output=7f074d30a425fd3a input=df3d54331b0af204]*/ -#else -/*[clinic input] -os.initgroups - - username as oname: FSConverter - gid: gid_t - / - -Initialize the group access list. - -Call the system initgroups() to initialize the group access list with all of -the groups of which the specified username is a member, plus the specified -group id. -[clinic start generated code]*/ - -static PyObject * -os_initgroups_impl(PyObject *module, PyObject *oname, gid_t gid) -/*[clinic end generated code: output=59341244521a9e3f input=0cb91bdc59a4c564]*/ -#endif -{ - const char *username = PyBytes_AS_STRING(oname); - - if (initgroups(username, gid) == -1) - return PyErr_SetFromErrno(PyExc_OSError); - - Py_RETURN_NONE; -} -#endif /* HAVE_INITGROUPS */ - - -#ifdef HAVE_GETPGID -/*[clinic input] -os.getpgid - - pid: pid_t - -Call the system call getpgid(), and return the result. -[clinic start generated code]*/ - -static PyObject * -os_getpgid_impl(PyObject *module, pid_t pid) -/*[clinic end generated code: output=1db95a97be205d18 input=39d710ae3baaf1c7]*/ -{ - pid_t pgid = getpgid(pid); - if (pgid < 0) - return posix_error(); - return PyLong_FromPid(pgid); -} -#endif /* HAVE_GETPGID */ - - -#ifdef HAVE_GETPGRP -/*[clinic input] -os.getpgrp - -Return the current process group id. -[clinic start generated code]*/ - -static PyObject * -os_getpgrp_impl(PyObject *module) -/*[clinic end generated code: output=c4fc381e51103cf3 input=6846fb2bb9a3705e]*/ -{ -#ifdef GETPGRP_HAVE_ARG - return PyLong_FromPid(getpgrp(0)); -#else /* GETPGRP_HAVE_ARG */ - return PyLong_FromPid(getpgrp()); -#endif /* GETPGRP_HAVE_ARG */ -} -#endif /* HAVE_GETPGRP */ - - -#ifdef HAVE_SETPGRP -/*[clinic input] -os.setpgrp - -Make the current process the leader of its process group. -[clinic start generated code]*/ - -static PyObject * -os_setpgrp_impl(PyObject *module) -/*[clinic end generated code: output=2554735b0a60f0a0 input=1f0619fcb5731e7e]*/ -{ -#ifdef SETPGRP_HAVE_ARG - if (setpgrp(0, 0) < 0) -#else /* SETPGRP_HAVE_ARG */ - if (setpgrp() < 0) -#endif /* SETPGRP_HAVE_ARG */ - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_SETPGRP */ - -#ifdef HAVE_GETPPID - -#ifdef MS_WINDOWS -#include -#include - -// The structure definition in winternl.h may be incomplete. -// This structure is the full version from the MSDN documentation. -typedef struct _PROCESS_BASIC_INFORMATION_FULL { - NTSTATUS ExitStatus; - PVOID PebBaseAddress; - ULONG_PTR AffinityMask; - LONG BasePriority; - ULONG_PTR UniqueProcessId; - ULONG_PTR InheritedFromUniqueProcessId; -} PROCESS_BASIC_INFORMATION_FULL; - -typedef NTSTATUS (NTAPI *PNT_QUERY_INFORMATION_PROCESS) ( - IN HANDLE ProcessHandle, - IN PROCESSINFOCLASS ProcessInformationClass, - OUT PVOID ProcessInformation, - IN ULONG ProcessInformationLength, - OUT PULONG ReturnLength OPTIONAL); - -// This function returns the process ID of the parent process. -// Returns 0 on failure. -static ULONG -win32_getppid_fast(void) -{ - NTSTATUS status; - HMODULE ntdll; - PNT_QUERY_INFORMATION_PROCESS pNtQueryInformationProcess; - PROCESS_BASIC_INFORMATION_FULL basic_information; - static ULONG cached_ppid = 0; - - if (cached_ppid) { - // No need to query the kernel again. - return cached_ppid; - } - - ntdll = GetModuleHandleW(L"ntdll.dll"); - if (!ntdll) { - return 0; - } - - pNtQueryInformationProcess = (PNT_QUERY_INFORMATION_PROCESS) GetProcAddress(ntdll, "NtQueryInformationProcess"); - if (!pNtQueryInformationProcess) { - return 0; - } - - status = pNtQueryInformationProcess(GetCurrentProcess(), - ProcessBasicInformation, - &basic_information, - sizeof(basic_information), - NULL); - - if (!NT_SUCCESS(status)) { - return 0; - } - - // Perform sanity check on the parent process ID we received from NtQueryInformationProcess. - // The check covers values which exceed the 32-bit range (if running on x64) as well as - // zero and (ULONG) -1. - - if (basic_information.InheritedFromUniqueProcessId == 0 || - basic_information.InheritedFromUniqueProcessId >= ULONG_MAX) - { - return 0; - } - - // Now that we have reached this point, the BasicInformation.InheritedFromUniqueProcessId - // structure member contains a ULONG_PTR which represents the process ID of our parent - // process. This process ID will be correctly returned even if the parent process has - // exited or been terminated. - - cached_ppid = (ULONG) basic_information.InheritedFromUniqueProcessId; - return cached_ppid; -} - -static PyObject* -win32_getppid(void) -{ - DWORD error; - PyObject* result = NULL; - HANDLE process = GetCurrentProcess(); - HPSS snapshot = NULL; - ULONG pid; - - pid = win32_getppid_fast(); - if (pid != 0) { - return PyLong_FromUnsignedLong(pid); - } - - // If failure occurs in win32_getppid_fast(), fall back to using the PSS API. - - error = PssCaptureSnapshot(process, PSS_CAPTURE_NONE, 0, &snapshot); - if (error != ERROR_SUCCESS) { - return PyErr_SetFromWindowsErr(error); - } - - PSS_PROCESS_INFORMATION info; - error = PssQuerySnapshot(snapshot, PSS_QUERY_PROCESS_INFORMATION, &info, - sizeof(info)); - if (error == ERROR_SUCCESS) { - result = PyLong_FromUnsignedLong(info.ParentProcessId); - } - else { - result = PyErr_SetFromWindowsErr(error); - } - - PssFreeSnapshot(process, snapshot); - return result; -} -#endif /*MS_WINDOWS*/ - - -/*[clinic input] -os.getppid - -Return the parent's process id. - -If the parent process has already exited, Windows machines will still -return its id; others systems will return the id of the 'init' process (1). -[clinic start generated code]*/ - -static PyObject * -os_getppid_impl(PyObject *module) -/*[clinic end generated code: output=43b2a946a8c603b4 input=e637cb87539c030e]*/ -{ -#ifdef MS_WINDOWS - return win32_getppid(); -#else - return PyLong_FromPid(getppid()); -#endif -} -#endif /* HAVE_GETPPID */ - - -#ifdef HAVE_GETLOGIN -/*[clinic input] -os.getlogin - -Return the actual login name. -[clinic start generated code]*/ - -static PyObject * -os_getlogin_impl(PyObject *module) -/*[clinic end generated code: output=a32e66a7e5715dac input=2a21ab1e917163df]*/ -{ - PyObject *result = NULL; -#ifdef MS_WINDOWS - wchar_t user_name[UNLEN + 1]; - DWORD num_chars = Py_ARRAY_LENGTH(user_name); - - if (GetUserNameW(user_name, &num_chars)) { - /* num_chars is the number of unicode chars plus null terminator */ - result = PyUnicode_FromWideChar(user_name, num_chars - 1); - } - else - result = PyErr_SetFromWindowsErr(GetLastError()); -#else - char *name; - int old_errno = errno; - - errno = 0; - name = getlogin(); - if (name == NULL) { - if (errno) - posix_error(); - else - PyErr_SetString(PyExc_OSError, "unable to determine login name"); - } - else - result = PyUnicode_DecodeFSDefault(name); - errno = old_errno; -#endif - return result; -} -#endif /* HAVE_GETLOGIN */ - - -#ifdef HAVE_GETUID -/*[clinic input] -os.getuid - -Return the current process's user id. -[clinic start generated code]*/ - -static PyObject * -os_getuid_impl(PyObject *module) -/*[clinic end generated code: output=415c0b401ebed11a input=b53c8b35f110a516]*/ -{ - return _PyLong_FromUid(getuid()); -} -#endif /* HAVE_GETUID */ - - -#ifdef MS_WINDOWS -#define HAVE_KILL -#endif /* MS_WINDOWS */ - -#ifdef HAVE_KILL -/*[clinic input] -os.kill - - pid: pid_t - signal: Py_ssize_t - / - -Kill a process with a signal. -[clinic start generated code]*/ - -static PyObject * -os_kill_impl(PyObject *module, pid_t pid, Py_ssize_t signal) -/*[clinic end generated code: output=8e346a6701c88568 input=61a36b86ca275ab9]*/ -{ - if (PySys_Audit("os.kill", "in", pid, signal) < 0) { - return NULL; - } -#ifndef MS_WINDOWS - if (kill(pid, (int)signal) == -1) { - return posix_error(); - } - - // Check immediately if the signal was sent to the current process. - // Don't micro-optimize pid == getpid(), since PyErr_SetString() check - // is cheap. - if (PyErr_CheckSignals()) { - return NULL; - } - - Py_RETURN_NONE; -#else /* !MS_WINDOWS */ - PyObject *result; - DWORD sig = (DWORD)signal; - DWORD err; - HANDLE handle; - -#ifdef HAVE_WINDOWS_CONSOLE_IO - /* Console processes which share a common console can be sent CTRL+C or - CTRL+BREAK events, provided they handle said events. */ - if (sig == CTRL_C_EVENT || sig == CTRL_BREAK_EVENT) { - if (GenerateConsoleCtrlEvent(sig, (DWORD)pid) == 0) { - err = GetLastError(); - PyErr_SetFromWindowsErr(err); - } - else { - Py_RETURN_NONE; - } - } -#endif /* HAVE_WINDOWS_CONSOLE_IO */ - - /* If the signal is outside of what GenerateConsoleCtrlEvent can use, - attempt to open and terminate the process. */ - handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid); - if (handle == NULL) { - err = GetLastError(); - return PyErr_SetFromWindowsErr(err); - } - - if (TerminateProcess(handle, sig) == 0) { - err = GetLastError(); - result = PyErr_SetFromWindowsErr(err); - } else { - result = Py_NewRef(Py_None); - } - - CloseHandle(handle); - return result; -#endif /* !MS_WINDOWS */ -} -#endif /* HAVE_KILL */ - - -#ifdef HAVE_KILLPG -/*[clinic input] -os.killpg - - pgid: pid_t - signal: int - / - -Kill a process group with a signal. -[clinic start generated code]*/ - -static PyObject * -os_killpg_impl(PyObject *module, pid_t pgid, int signal) -/*[clinic end generated code: output=6dbcd2f1fdf5fdba input=38b5449eb8faec19]*/ -{ - if (PySys_Audit("os.killpg", "ii", pgid, signal) < 0) { - return NULL; - } - /* XXX some man pages make the `pgid` parameter an int, others - a pid_t. Since getpgrp() returns a pid_t, we assume killpg should - take the same type. Moreover, pid_t is always at least as wide as - int (else compilation of this module fails), which is safe. */ - if (killpg(pgid, signal) == -1) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_KILLPG */ - - -#ifdef HAVE_PLOCK -#ifdef HAVE_SYS_LOCK_H -#include -#endif - -/*[clinic input] -os.plock - op: int - / - -Lock program segments into memory."); -[clinic start generated code]*/ - -static PyObject * -os_plock_impl(PyObject *module, int op) -/*[clinic end generated code: output=81424167033b168e input=e6e5e348e1525f60]*/ -{ - if (plock(op) == -1) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_PLOCK */ - - -#ifdef HAVE_SETUID -/*[clinic input] -os.setuid - - uid: uid_t - / - -Set the current process's user id. -[clinic start generated code]*/ - -static PyObject * -os_setuid_impl(PyObject *module, uid_t uid) -/*[clinic end generated code: output=a0a41fd0d1ec555f input=c921a3285aa22256]*/ -{ - if (setuid(uid) < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_SETUID */ - - -#ifdef HAVE_SETEUID -/*[clinic input] -os.seteuid - - euid: uid_t - / - -Set the current process's effective user id. -[clinic start generated code]*/ - -static PyObject * -os_seteuid_impl(PyObject *module, uid_t euid) -/*[clinic end generated code: output=102e3ad98361519a input=ba93d927e4781aa9]*/ -{ - if (seteuid(euid) < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_SETEUID */ - - -#ifdef HAVE_SETEGID -/*[clinic input] -os.setegid - - egid: gid_t - / - -Set the current process's effective group id. -[clinic start generated code]*/ - -static PyObject * -os_setegid_impl(PyObject *module, gid_t egid) -/*[clinic end generated code: output=4e4b825a6a10258d input=4080526d0ccd6ce3]*/ -{ - if (setegid(egid) < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_SETEGID */ - - -#ifdef HAVE_SETREUID -/*[clinic input] -os.setreuid - - ruid: uid_t - euid: uid_t - / - -Set the current process's real and effective user ids. -[clinic start generated code]*/ - -static PyObject * -os_setreuid_impl(PyObject *module, uid_t ruid, uid_t euid) -/*[clinic end generated code: output=62d991210006530a input=0ca8978de663880c]*/ -{ - if (setreuid(ruid, euid) < 0) { - return posix_error(); - } else { - Py_RETURN_NONE; - } -} -#endif /* HAVE_SETREUID */ - - -#ifdef HAVE_SETREGID -/*[clinic input] -os.setregid - - rgid: gid_t - egid: gid_t - / - -Set the current process's real and effective group ids. -[clinic start generated code]*/ - -static PyObject * -os_setregid_impl(PyObject *module, gid_t rgid, gid_t egid) -/*[clinic end generated code: output=aa803835cf5342f3 input=c59499f72846db78]*/ -{ - if (setregid(rgid, egid) < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_SETREGID */ - - -#ifdef HAVE_SETGID -/*[clinic input] -os.setgid - gid: gid_t - / - -Set the current process's group id. -[clinic start generated code]*/ - -static PyObject * -os_setgid_impl(PyObject *module, gid_t gid) -/*[clinic end generated code: output=bdccd7403f6ad8c3 input=27d30c4059045dc6]*/ -{ - if (setgid(gid) < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_SETGID */ - - -#ifdef HAVE_SETGROUPS -/*[clinic input] -os.setgroups - - groups: object - / - -Set the groups of the current process to list. -[clinic start generated code]*/ - -static PyObject * -os_setgroups(PyObject *module, PyObject *groups) -/*[clinic end generated code: output=3fcb32aad58c5ecd input=fa742ca3daf85a7e]*/ -{ - if (!PySequence_Check(groups)) { - PyErr_SetString(PyExc_TypeError, "setgroups argument must be a sequence"); - return NULL; - } - Py_ssize_t len = PySequence_Size(groups); - if (len < 0) { - return NULL; - } - if (len > MAX_GROUPS) { - PyErr_SetString(PyExc_ValueError, "too many groups"); - return NULL; - } - - gid_t *grouplist = PyMem_New(gid_t, len); - if (grouplist == NULL) { - PyErr_NoMemory(); - return NULL; - } - for (Py_ssize_t i = 0; i < len; i++) { - PyObject *elem; - elem = PySequence_GetItem(groups, i); - if (!elem) { - PyMem_Free(grouplist); - return NULL; - } - if (!PyLong_Check(elem)) { - PyErr_SetString(PyExc_TypeError, - "groups must be integers"); - Py_DECREF(elem); - PyMem_Free(grouplist); - return NULL; - } else { - if (!_Py_Gid_Converter(elem, &grouplist[i])) { - Py_DECREF(elem); - PyMem_Free(grouplist); - return NULL; - } - } - Py_DECREF(elem); - } - - if (setgroups(len, grouplist) < 0) { - posix_error(); - PyMem_Free(grouplist); - return NULL; - } - PyMem_Free(grouplist); - Py_RETURN_NONE; -} -#endif /* HAVE_SETGROUPS */ - -#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4) -static PyObject * -wait_helper(PyObject *module, pid_t pid, int status, struct rusage *ru) -{ - PyObject *result; - PyObject *struct_rusage; - - if (pid == -1) - return posix_error(); - - // If wait succeeded but no child was ready to report status, ru will not - // have been populated. - if (pid == 0) { - memset(ru, 0, sizeof(*ru)); - } - - struct_rusage = _PyImport_GetModuleAttrString("resource", "struct_rusage"); - if (struct_rusage == NULL) - return NULL; - - /* XXX(nnorwitz): Copied (w/mods) from resource.c, there should be only one. */ - result = PyStructSequence_New((PyTypeObject*) struct_rusage); - Py_DECREF(struct_rusage); - if (!result) - return NULL; - - int pos = 0; - -#ifndef doubletime -#define doubletime(TV) ((double)(TV).tv_sec + (TV).tv_usec * 0.000001) -#endif - -#define SET_RESULT(CALL) \ - do { \ - PyObject *item = (CALL); \ - if (item == NULL) { \ - Py_DECREF(result); \ - return NULL; \ - } \ - PyStructSequence_SET_ITEM(result, pos++, item); \ - } while(0) - - SET_RESULT(PyFloat_FromDouble(doubletime(ru->ru_utime))); - SET_RESULT(PyFloat_FromDouble(doubletime(ru->ru_stime))); - SET_RESULT(PyLong_FromLong(ru->ru_maxrss)); - SET_RESULT(PyLong_FromLong(ru->ru_ixrss)); - SET_RESULT(PyLong_FromLong(ru->ru_idrss)); - SET_RESULT(PyLong_FromLong(ru->ru_isrss)); - SET_RESULT(PyLong_FromLong(ru->ru_minflt)); - SET_RESULT(PyLong_FromLong(ru->ru_majflt)); - SET_RESULT(PyLong_FromLong(ru->ru_nswap)); - SET_RESULT(PyLong_FromLong(ru->ru_inblock)); - SET_RESULT(PyLong_FromLong(ru->ru_oublock)); - SET_RESULT(PyLong_FromLong(ru->ru_msgsnd)); - SET_RESULT(PyLong_FromLong(ru->ru_msgrcv)); - SET_RESULT(PyLong_FromLong(ru->ru_nsignals)); - SET_RESULT(PyLong_FromLong(ru->ru_nvcsw)); - SET_RESULT(PyLong_FromLong(ru->ru_nivcsw)); -#undef SET_RESULT - - return Py_BuildValue("NiN", PyLong_FromPid(pid), status, result); -} -#endif /* HAVE_WAIT3 || HAVE_WAIT4 */ - - -#ifdef HAVE_WAIT3 -/*[clinic input] -os.wait3 - - options: int -Wait for completion of a child process. - -Returns a tuple of information about the child process: - (pid, status, rusage) -[clinic start generated code]*/ - -static PyObject * -os_wait3_impl(PyObject *module, int options) -/*[clinic end generated code: output=92c3224e6f28217a input=8ac4c56956b61710]*/ -{ - pid_t pid; - struct rusage ru; - int async_err = 0; - WAIT_TYPE status; - WAIT_STATUS_INT(status) = 0; - - do { - Py_BEGIN_ALLOW_THREADS - pid = wait3(&status, options, &ru); - Py_END_ALLOW_THREADS - } while (pid < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - if (pid < 0) - return (!async_err) ? posix_error() : NULL; - - return wait_helper(module, pid, WAIT_STATUS_INT(status), &ru); -} -#endif /* HAVE_WAIT3 */ - - -#ifdef HAVE_WAIT4 -/*[clinic input] - -os.wait4 - - pid: pid_t - options: int - -Wait for completion of a specific child process. - -Returns a tuple of information about the child process: - (pid, status, rusage) -[clinic start generated code]*/ - -static PyObject * -os_wait4_impl(PyObject *module, pid_t pid, int options) -/*[clinic end generated code: output=66195aa507b35f70 input=d11deed0750600ba]*/ -{ - pid_t res; - struct rusage ru; - int async_err = 0; - WAIT_TYPE status; - WAIT_STATUS_INT(status) = 0; - - do { - Py_BEGIN_ALLOW_THREADS - res = wait4(pid, &status, options, &ru); - Py_END_ALLOW_THREADS - } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - if (res < 0) - return (!async_err) ? posix_error() : NULL; - - return wait_helper(module, res, WAIT_STATUS_INT(status), &ru); -} -#endif /* HAVE_WAIT4 */ - - -#if defined(HAVE_WAITID) -/*[clinic input] -os.waitid - - idtype: idtype_t - Must be one of be P_PID, P_PGID or P_ALL. - id: id_t - The id to wait on. - options: int - Constructed from the ORing of one or more of WEXITED, WSTOPPED - or WCONTINUED and additionally may be ORed with WNOHANG or WNOWAIT. - / - -Returns the result of waiting for a process or processes. - -Returns either waitid_result or None if WNOHANG is specified and there are -no children in a waitable state. -[clinic start generated code]*/ - -static PyObject * -os_waitid_impl(PyObject *module, idtype_t idtype, id_t id, int options) -/*[clinic end generated code: output=5d2e1c0bde61f4d8 input=d8e7f76e052b7920]*/ -{ - PyObject *result; - int res; - int async_err = 0; - siginfo_t si; - si.si_pid = 0; - - do { - Py_BEGIN_ALLOW_THREADS - res = waitid(idtype, id, &si, options); - Py_END_ALLOW_THREADS - } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - if (res < 0) - return (!async_err) ? posix_error() : NULL; - - if (si.si_pid == 0) - Py_RETURN_NONE; - - PyObject *WaitidResultType = get_posix_state(module)->WaitidResultType; - result = PyStructSequence_New((PyTypeObject *)WaitidResultType); - if (!result) - return NULL; - - int pos = 0; - -#define SET_RESULT(CALL) \ - do { \ - PyObject *item = (CALL); \ - if (item == NULL) { \ - Py_DECREF(result); \ - return NULL; \ - } \ - PyStructSequence_SET_ITEM(result, pos++, item); \ - } while(0) - - SET_RESULT(PyLong_FromPid(si.si_pid)); - SET_RESULT(_PyLong_FromUid(si.si_uid)); - SET_RESULT(PyLong_FromLong((long)(si.si_signo))); - SET_RESULT(PyLong_FromLong((long)(si.si_status))); - SET_RESULT(PyLong_FromLong((long)(si.si_code))); - -#undef SET_RESULT - - return result; -} -#endif /* defined(HAVE_WAITID) */ - - -#if defined(HAVE_WAITPID) -/*[clinic input] -os.waitpid - pid: pid_t - options: int - / - -Wait for completion of a given child process. - -Returns a tuple of information regarding the child process: - (pid, status) - -The options argument is ignored on Windows. -[clinic start generated code]*/ - -static PyObject * -os_waitpid_impl(PyObject *module, pid_t pid, int options) -/*[clinic end generated code: output=5c37c06887a20270 input=0bf1666b8758fda3]*/ -{ - pid_t res; - int async_err = 0; - WAIT_TYPE status; - WAIT_STATUS_INT(status) = 0; - - do { - Py_BEGIN_ALLOW_THREADS - res = waitpid(pid, &status, options); - Py_END_ALLOW_THREADS - } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - if (res < 0) - return (!async_err) ? posix_error() : NULL; - - return Py_BuildValue("Ni", PyLong_FromPid(res), WAIT_STATUS_INT(status)); -} -#elif defined(HAVE_CWAIT) -/* MS C has a variant of waitpid() that's usable for most purposes. */ -/*[clinic input] -os.waitpid - pid: intptr_t - options: int - / - -Wait for completion of a given process. - -Returns a tuple of information regarding the process: - (pid, status << 8) - -The options argument is ignored on Windows. -[clinic start generated code]*/ - -static PyObject * -os_waitpid_impl(PyObject *module, intptr_t pid, int options) -/*[clinic end generated code: output=be836b221271d538 input=40f2440c515410f8]*/ -{ - int status; - intptr_t res; - int async_err = 0; - - do { - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH - res = _cwait(&status, pid, options); - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - if (res < 0) - return (!async_err) ? posix_error() : NULL; - - unsigned long long ustatus = (unsigned int)status; - - /* shift the status left a byte so this is more like the POSIX waitpid */ - return Py_BuildValue(_Py_PARSE_INTPTR "K", res, ustatus << 8); -} -#endif - - -#ifdef HAVE_WAIT -/*[clinic input] -os.wait - -Wait for completion of a child process. - -Returns a tuple of information about the child process: - (pid, status) -[clinic start generated code]*/ - -static PyObject * -os_wait_impl(PyObject *module) -/*[clinic end generated code: output=6bc419ac32fb364b input=03b0182d4a4700ce]*/ -{ - pid_t pid; - int async_err = 0; - WAIT_TYPE status; - WAIT_STATUS_INT(status) = 0; - - do { - Py_BEGIN_ALLOW_THREADS - pid = wait(&status); - Py_END_ALLOW_THREADS - } while (pid < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - if (pid < 0) - return (!async_err) ? posix_error() : NULL; - - return Py_BuildValue("Ni", PyLong_FromPid(pid), WAIT_STATUS_INT(status)); -} -#endif /* HAVE_WAIT */ - - -// This system call always crashes on older Android versions. -#if defined(__linux__) && defined(__NR_pidfd_open) && \ - !(defined(__ANDROID__) && __ANDROID_API__ < 31) -/*[clinic input] -os.pidfd_open - pid: pid_t - flags: unsigned_int = 0 - -Return a file descriptor referring to the process *pid*. - -The descriptor can be used to perform process management without races and -signals. -[clinic start generated code]*/ - -static PyObject * -os_pidfd_open_impl(PyObject *module, pid_t pid, unsigned int flags) -/*[clinic end generated code: output=5c7252698947dc41 input=c3fd99ce947ccfef]*/ -{ - int fd = syscall(__NR_pidfd_open, pid, flags); - if (fd < 0) { - return posix_error(); - } - return PyLong_FromLong(fd); -} -#endif - - -#ifdef HAVE_SETNS -/*[clinic input] -os.setns - fd: fildes - A file descriptor to a namespace. - nstype: int = 0 - Type of namespace. - -Move the calling thread into different namespaces. -[clinic start generated code]*/ - -static PyObject * -os_setns_impl(PyObject *module, int fd, int nstype) -/*[clinic end generated code: output=5dbd055bfb66ecd0 input=42787871226bf3ee]*/ -{ - int res; - - Py_BEGIN_ALLOW_THREADS - res = setns(fd, nstype); - Py_END_ALLOW_THREADS - - if (res != 0) { - return posix_error(); - } - - Py_RETURN_NONE; -} -#endif - - -#ifdef HAVE_UNSHARE -/*[clinic input] -os.unshare - flags: int - Namespaces to be unshared. - -Disassociate parts of a process (or thread) execution context. -[clinic start generated code]*/ - -static PyObject * -os_unshare_impl(PyObject *module, int flags) -/*[clinic end generated code: output=1b3177906dd237ee input=9e065db3232b8b1b]*/ -{ - int res; - - Py_BEGIN_ALLOW_THREADS - res = unshare(flags); - Py_END_ALLOW_THREADS - - if (res != 0) { - return posix_error(); - } - - Py_RETURN_NONE; -} -#endif - - -#if defined(HAVE_READLINK) || defined(MS_WINDOWS) -/*[clinic input] -os.readlink - - path: path_t - * - dir_fd: dir_fd(requires='readlinkat') = None - -Return a string representing the path to which the symbolic link points. - -If dir_fd is not None, it should be a file descriptor open to a directory, -and path should be relative; path will then be relative to that directory. - -dir_fd may not be implemented on your platform. If it is unavailable, -using it will raise a NotImplementedError. -[clinic start generated code]*/ - -static PyObject * -os_readlink_impl(PyObject *module, path_t *path, int dir_fd) -/*[clinic end generated code: output=d21b732a2e814030 input=113c87e0db1ecaf2]*/ -{ -#if defined(HAVE_READLINK) - char buffer[MAXPATHLEN+1]; - ssize_t length; -#ifdef HAVE_READLINKAT - int readlinkat_unavailable = 0; -#endif - - Py_BEGIN_ALLOW_THREADS -#ifdef HAVE_READLINKAT - if (dir_fd != DEFAULT_DIR_FD) { - if (HAVE_READLINKAT_RUNTIME) { - length = readlinkat(dir_fd, path->narrow, buffer, MAXPATHLEN); - } else { - readlinkat_unavailable = 1; - } - } else -#endif - length = readlink(path->narrow, buffer, MAXPATHLEN); - Py_END_ALLOW_THREADS - -#ifdef HAVE_READLINKAT - if (readlinkat_unavailable) { - argument_unavailable_error(NULL, "dir_fd"); - return NULL; - } -#endif - - if (length < 0) { - return path_error(path); - } - buffer[length] = '\0'; - - if (PyUnicode_Check(path->object)) - return PyUnicode_DecodeFSDefaultAndSize(buffer, length); - else - return PyBytes_FromStringAndSize(buffer, length); -#elif defined(MS_WINDOWS) - DWORD n_bytes_returned; - DWORD io_result = 0; - HANDLE reparse_point_handle; - char target_buffer[_Py_MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; - _Py_REPARSE_DATA_BUFFER *rdb = (_Py_REPARSE_DATA_BUFFER *)target_buffer; - PyObject *result = NULL; - - /* First get a handle to the reparse point */ - Py_BEGIN_ALLOW_THREADS - reparse_point_handle = CreateFileW( - path->wide, - 0, - 0, - 0, - OPEN_EXISTING, - FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, - 0); - if (reparse_point_handle != INVALID_HANDLE_VALUE) { - /* New call DeviceIoControl to read the reparse point */ - io_result = DeviceIoControl( - reparse_point_handle, - FSCTL_GET_REPARSE_POINT, - 0, 0, /* in buffer */ - target_buffer, sizeof(target_buffer), - &n_bytes_returned, - 0 /* we're not using OVERLAPPED_IO */ - ); - CloseHandle(reparse_point_handle); - } - Py_END_ALLOW_THREADS - - if (io_result == 0) { - return path_error(path); - } - - wchar_t *name = NULL; - Py_ssize_t nameLen = 0; - if (rdb->ReparseTag == IO_REPARSE_TAG_SYMLINK) - { - name = (wchar_t *)((char*)rdb->SymbolicLinkReparseBuffer.PathBuffer + - rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset); - nameLen = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t); - } - else if (rdb->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) - { - name = (wchar_t *)((char*)rdb->MountPointReparseBuffer.PathBuffer + - rdb->MountPointReparseBuffer.SubstituteNameOffset); - nameLen = rdb->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t); - } - else - { - PyErr_SetString(PyExc_ValueError, "not a symbolic link"); - } - if (name) { - if (nameLen > 4 && wcsncmp(name, L"\\??\\", 4) == 0) { - /* Our buffer is mutable, so this is okay */ - name[1] = L'\\'; - } - result = PyUnicode_FromWideChar(name, nameLen); - if (result && PyBytes_Check(path->object)) { - Py_SETREF(result, PyUnicode_EncodeFSDefault(result)); - } - } - return result; -#endif -} -#endif /* defined(HAVE_READLINK) || defined(MS_WINDOWS) */ - -#if defined(MS_WINDOWS) - -/* Remove the last portion of the path - return 0 on success */ -static int -_dirnameW(WCHAR *path) -{ - WCHAR *ptr; - size_t length = wcsnlen_s(path, MAX_PATH); - if (length == MAX_PATH) { - return -1; - } - - /* walk the path from the end until a backslash is encountered */ - for(ptr = path + length; ptr != path; ptr--) { - if (*ptr == L'\\' || *ptr == L'/') { - break; - } - } - *ptr = 0; - return 0; -} - -#endif - -#ifdef HAVE_SYMLINK - -#if defined(MS_WINDOWS) - -/* Is this path absolute? */ -static int -_is_absW(const WCHAR *path) -{ - return path[0] == L'\\' || path[0] == L'/' || - (path[0] && path[1] == L':'); -} - -/* join root and rest with a backslash - return 0 on success */ -static int -_joinW(WCHAR *dest_path, const WCHAR *root, const WCHAR *rest) -{ - if (_is_absW(rest)) { - return wcscpy_s(dest_path, MAX_PATH, rest); - } - - if (wcscpy_s(dest_path, MAX_PATH, root)) { - return -1; - } - - if (dest_path[0] && wcscat_s(dest_path, MAX_PATH, L"\\")) { - return -1; - } - - return wcscat_s(dest_path, MAX_PATH, rest); -} - -/* Return True if the path at src relative to dest is a directory */ -static int -_check_dirW(LPCWSTR src, LPCWSTR dest) -{ - WIN32_FILE_ATTRIBUTE_DATA src_info; - WCHAR dest_parent[MAX_PATH]; - WCHAR src_resolved[MAX_PATH] = L""; - - /* dest_parent = os.path.dirname(dest) */ - if (wcscpy_s(dest_parent, MAX_PATH, dest) || - _dirnameW(dest_parent)) { - return 0; - } - /* src_resolved = os.path.join(dest_parent, src) */ - if (_joinW(src_resolved, dest_parent, src)) { - return 0; - } - return ( - GetFileAttributesExW(src_resolved, GetFileExInfoStandard, &src_info) - && src_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY - ); -} -#endif - - -/*[clinic input] -os.symlink - src: path_t - dst: path_t - target_is_directory: bool = False - * - dir_fd: dir_fd(requires='symlinkat')=None - -# "symlink(src, dst, target_is_directory=False, *, dir_fd=None)\n\n\ - -Create a symbolic link pointing to src named dst. - -target_is_directory is required on Windows if the target is to be - interpreted as a directory. (On Windows, symlink requires - Windows 6.0 or greater, and raises a NotImplementedError otherwise.) - target_is_directory is ignored on non-Windows platforms. - -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. -dir_fd may not be implemented on your platform. - If it is unavailable, using it will raise a NotImplementedError. - -[clinic start generated code]*/ - -static PyObject * -os_symlink_impl(PyObject *module, path_t *src, path_t *dst, - int target_is_directory, int dir_fd) -/*[clinic end generated code: output=08ca9f3f3cf960f6 input=e820ec4472547bc3]*/ -{ -#ifdef MS_WINDOWS - DWORD result; - DWORD flags = 0; - - /* Assumed true, set to false if detected to not be available. */ - static int windows_has_symlink_unprivileged_flag = TRUE; -#else - int result; -#ifdef HAVE_SYMLINKAT - int symlinkat_unavailable = 0; -#endif -#endif - - if (PySys_Audit("os.symlink", "OOi", src->object, dst->object, - dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) { - return NULL; - } - -#ifdef MS_WINDOWS - - if (windows_has_symlink_unprivileged_flag) { - /* Allow non-admin symlinks if system allows it. */ - flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; - } - - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH - /* if src is a directory, ensure flags==1 (target_is_directory bit) */ - if (target_is_directory || _check_dirW(src->wide, dst->wide)) { - flags |= SYMBOLIC_LINK_FLAG_DIRECTORY; - } - - result = CreateSymbolicLinkW(dst->wide, src->wide, flags); - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - - if (windows_has_symlink_unprivileged_flag && !result && - ERROR_INVALID_PARAMETER == GetLastError()) { - - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH - /* This error might be caused by - SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE not being supported. - Try again, and update windows_has_symlink_unprivileged_flag if we - are successful this time. - - NOTE: There is a risk of a race condition here if there are other - conditions than the flag causing ERROR_INVALID_PARAMETER, and - another process (or thread) changes that condition in between our - calls to CreateSymbolicLink. - */ - flags &= ~(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE); - result = CreateSymbolicLinkW(dst->wide, src->wide, flags); - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - - if (result || ERROR_INVALID_PARAMETER != GetLastError()) { - windows_has_symlink_unprivileged_flag = FALSE; - } - } - - if (!result) - return path_error2(src, dst); - -#else - - if ((src->narrow && dst->wide) || (src->wide && dst->narrow)) { - PyErr_SetString(PyExc_ValueError, - "symlink: src and dst must be the same type"); - return NULL; - } - - Py_BEGIN_ALLOW_THREADS -#ifdef HAVE_SYMLINKAT - if (dir_fd != DEFAULT_DIR_FD) { - if (HAVE_SYMLINKAT_RUNTIME) { - result = symlinkat(src->narrow, dir_fd, dst->narrow); - } else { - symlinkat_unavailable = 1; - } - } else -#endif - result = symlink(src->narrow, dst->narrow); - Py_END_ALLOW_THREADS - -#ifdef HAVE_SYMLINKAT - if (symlinkat_unavailable) { - argument_unavailable_error(NULL, "dir_fd"); - return NULL; - } -#endif - - if (result) - return path_error2(src, dst); -#endif - - Py_RETURN_NONE; -} -#endif /* HAVE_SYMLINK */ - - -static PyStructSequence_Field times_result_fields[] = { - {"user", "user time"}, - {"system", "system time"}, - {"children_user", "user time of children"}, - {"children_system", "system time of children"}, - {"elapsed", "elapsed time since an arbitrary point in the past"}, - {NULL} -}; - -PyDoc_STRVAR(times_result__doc__, -"times_result: Result from os.times().\n\n\ -This object may be accessed either as a tuple of\n\ - (user, system, children_user, children_system, elapsed),\n\ -or via the attributes user, system, children_user, children_system,\n\ -and elapsed.\n\ -\n\ -See os.times for more information."); - -static PyStructSequence_Desc times_result_desc = { - "times_result", /* name */ - times_result__doc__, /* doc */ - times_result_fields, - 5 -}; - -static PyObject * -build_times_result(PyObject *module, double user, double system, - double children_user, double children_system, - double elapsed) -{ - PyObject *TimesResultType = get_posix_state(module)->TimesResultType; - PyObject *value = PyStructSequence_New((PyTypeObject *)TimesResultType); - if (value == NULL) - return NULL; - -#define SET(i, field) \ - { \ - PyObject *o = PyFloat_FromDouble(field); \ - if (!o) { \ - Py_DECREF(value); \ - return NULL; \ - } \ - PyStructSequence_SET_ITEM(value, i, o); \ - } \ - - SET(0, user); - SET(1, system); - SET(2, children_user); - SET(3, children_system); - SET(4, elapsed); - -#undef SET - - return value; -} - - -/*[clinic input] -os.times - -Return a collection containing process timing information. - -The object returned behaves like a named tuple with these fields: - (utime, stime, cutime, cstime, elapsed_time) -All fields are floating-point numbers. -[clinic start generated code]*/ - -static PyObject * -os_times_impl(PyObject *module) -/*[clinic end generated code: output=35f640503557d32a input=8dbfe33a2dcc3df3]*/ -{ -#ifdef MS_WINDOWS - FILETIME create, exit, kernel, user; - HANDLE hProc; - hProc = GetCurrentProcess(); - GetProcessTimes(hProc, &create, &exit, &kernel, &user); - /* The fields of a FILETIME structure are the hi and lo part - of a 64-bit value expressed in 100 nanosecond units. - 1e7 is one second in such units; 1e-7 the inverse. - 429.4967296 is 2**32 / 1e7 or 2**32 * 1e-7. - */ - return build_times_result(module, - (double)(user.dwHighDateTime*429.4967296 + - user.dwLowDateTime*1e-7), - (double)(kernel.dwHighDateTime*429.4967296 + - kernel.dwLowDateTime*1e-7), - (double)0, - (double)0, - (double)0); -#else /* MS_WINDOWS */ - _posixstate *state = get_posix_state(module); - long ticks_per_second = state->ticks_per_second; - - struct tms process; - clock_t elapsed; - errno = 0; - elapsed = times(&process); - if (elapsed == (clock_t) -1) { - return posix_error(); - } - - return build_times_result(module, - (double)process.tms_utime / ticks_per_second, - (double)process.tms_stime / ticks_per_second, - (double)process.tms_cutime / ticks_per_second, - (double)process.tms_cstime / ticks_per_second, - (double)elapsed / ticks_per_second); -#endif /* MS_WINDOWS */ -} - - -#if defined(HAVE_TIMERFD_CREATE) -#define ONE_SECOND_IN_NS (1000 * 1000 * 1000) -#define EXTRACT_NSEC(value) (long)( ( (double)(value) - (time_t)(value) ) * 1e9) -#define CONVERT_SEC_AND_NSEC_TO_DOUBLE(sec, nsec) ( (double)(sec) + (double)(nsec) * 1e-9 ) - -static PyObject * -build_itimerspec(const struct itimerspec* curr_value) -{ - double _value = CONVERT_SEC_AND_NSEC_TO_DOUBLE(curr_value->it_value.tv_sec, - curr_value->it_value.tv_nsec); - PyObject *value = PyFloat_FromDouble(_value); - if (value == NULL) { - return NULL; - } - double _interval = CONVERT_SEC_AND_NSEC_TO_DOUBLE(curr_value->it_interval.tv_sec, - curr_value->it_interval.tv_nsec); - PyObject *interval = PyFloat_FromDouble(_interval); - if (interval == NULL) { - Py_DECREF(value); - return NULL; - } - PyObject *tuple = PyTuple_Pack(2, value, interval); - Py_DECREF(interval); - Py_DECREF(value); - return tuple; -} - -static PyObject * -build_itimerspec_ns(const struct itimerspec* curr_value) -{ - PyTime_t value, interval; - if (_PyTime_FromTimespec(&value, &curr_value->it_value) < 0) { - return NULL; - } - if (_PyTime_FromTimespec(&interval, &curr_value->it_interval) < 0) { - return NULL; - } - return Py_BuildValue("LL", value, interval); -} - -/*[clinic input] -os.timerfd_create - - clockid: int - A valid clock ID constant as timer file descriptor. - - time.CLOCK_REALTIME - time.CLOCK_MONOTONIC - time.CLOCK_BOOTTIME - / - * - flags: int = 0 - 0 or a bit mask of os.TFD_NONBLOCK or os.TFD_CLOEXEC. - - os.TFD_NONBLOCK - If *TFD_NONBLOCK* is set as a flag, read doesn't blocks. - If *TFD_NONBLOCK* is not set as a flag, read block until the timer fires. - - os.TFD_CLOEXEC - If *TFD_CLOEXEC* is set as a flag, enable the close-on-exec flag - -Create and return a timer file descriptor. -[clinic start generated code]*/ - -static PyObject * -os_timerfd_create_impl(PyObject *module, int clockid, int flags) -/*[clinic end generated code: output=1caae80fb168004a input=64b7020c5ac0b8f4]*/ - -{ - int fd; - Py_BEGIN_ALLOW_THREADS - flags |= TFD_CLOEXEC; // PEP 446: always create non-inheritable FD - fd = timerfd_create(clockid, flags); - Py_END_ALLOW_THREADS - if (fd == -1) { - return PyErr_SetFromErrno(PyExc_OSError); - } - return PyLong_FromLong(fd); -} - -/*[clinic input] -os.timerfd_settime - - fd: fildes - A timer file descriptor. - / - * - flags: int = 0 - 0 or a bit mask of TFD_TIMER_ABSTIME or TFD_TIMER_CANCEL_ON_SET. - initial as initial_double: double = 0.0 - The initial expiration time, in seconds. - interval as interval_double: double = 0.0 - The timer's interval, in seconds. - -Alter a timer file descriptor's internal timer in seconds. -[clinic start generated code]*/ - -static PyObject * -os_timerfd_settime_impl(PyObject *module, int fd, int flags, - double initial_double, double interval_double) -/*[clinic end generated code: output=df4c1bce6859224e input=81d2c0d7e936e8a7]*/ -{ - PyTime_t initial, interval; - if (_PyTime_FromSecondsDouble(initial_double, _PyTime_ROUND_FLOOR, - &initial) < 0) { - return NULL; - } - if (_PyTime_FromSecondsDouble(interval_double, _PyTime_ROUND_FLOOR, - &interval) < 0) { - return NULL; - } - - struct itimerspec new_value, old_value; - if (_PyTime_AsTimespec(initial, &new_value.it_value) < 0) { - PyErr_SetString(PyExc_ValueError, "invalid initial value"); - return NULL; - } - if (_PyTime_AsTimespec(interval, &new_value.it_interval) < 0) { - PyErr_SetString(PyExc_ValueError, "invalid interval value"); - return NULL; - } - - int result; - Py_BEGIN_ALLOW_THREADS - result = timerfd_settime(fd, flags, &new_value, &old_value); - Py_END_ALLOW_THREADS - if (result == -1) { - return PyErr_SetFromErrno(PyExc_OSError); - } - return build_itimerspec(&old_value); -} - - -/*[clinic input] -os.timerfd_settime_ns - - fd: fildes - A timer file descriptor. - / - * - flags: int = 0 - 0 or a bit mask of TFD_TIMER_ABSTIME or TFD_TIMER_CANCEL_ON_SET. - initial: long_long = 0 - initial expiration timing in seconds. - interval: long_long = 0 - interval for the timer in seconds. - -Alter a timer file descriptor's internal timer in nanoseconds. -[clinic start generated code]*/ - -static PyObject * -os_timerfd_settime_ns_impl(PyObject *module, int fd, int flags, - long long initial, long long interval) -/*[clinic end generated code: output=6273ec7d7b4cc0b3 input=261e105d6e42f5bc]*/ -{ - struct itimerspec new_value; - struct itimerspec old_value; - int result; - if (_PyTime_AsTimespec(initial, &new_value.it_value) < 0) { - PyErr_SetString(PyExc_ValueError, "invalid initial value"); - return NULL; - } - if (_PyTime_AsTimespec(interval, &new_value.it_interval) < 0) { - PyErr_SetString(PyExc_ValueError, "invalid interval value"); - return NULL; - } - Py_BEGIN_ALLOW_THREADS - result = timerfd_settime(fd, flags, &new_value, &old_value); - Py_END_ALLOW_THREADS - if (result == -1) { - return PyErr_SetFromErrno(PyExc_OSError); - } - return build_itimerspec_ns(&old_value); -} - -/*[clinic input] -os.timerfd_gettime - - fd: fildes - A timer file descriptor. - / - -Return a tuple of a timer file descriptor's (interval, next expiration) in float seconds. -[clinic start generated code]*/ - -static PyObject * -os_timerfd_gettime_impl(PyObject *module, int fd) -/*[clinic end generated code: output=ec5a94a66cfe6ab4 input=8148e3430870da1c]*/ -{ - struct itimerspec curr_value; - int result; - Py_BEGIN_ALLOW_THREADS - result = timerfd_gettime(fd, &curr_value); - Py_END_ALLOW_THREADS - if (result == -1) { - return PyErr_SetFromErrno(PyExc_OSError); - } - return build_itimerspec(&curr_value); -} - - -/*[clinic input] -os.timerfd_gettime_ns - - fd: fildes - A timer file descriptor. - / - -Return a tuple of a timer file descriptor's (interval, next expiration) in nanoseconds. -[clinic start generated code]*/ - -static PyObject * -os_timerfd_gettime_ns_impl(PyObject *module, int fd) -/*[clinic end generated code: output=580633a4465f39fe input=a825443e4c6b40ac]*/ -{ - struct itimerspec curr_value; - int result; - Py_BEGIN_ALLOW_THREADS - result = timerfd_gettime(fd, &curr_value); - Py_END_ALLOW_THREADS - if (result == -1) { - return PyErr_SetFromErrno(PyExc_OSError); - } - return build_itimerspec_ns(&curr_value); -} - -#undef ONE_SECOND_IN_NS -#undef EXTRACT_NSEC - -#endif /* HAVE_TIMERFD_CREATE */ - -#ifdef HAVE_GETSID -/*[clinic input] -os.getsid - - pid: pid_t - / - -Call the system call getsid(pid) and return the result. -[clinic start generated code]*/ - -static PyObject * -os_getsid_impl(PyObject *module, pid_t pid) -/*[clinic end generated code: output=112deae56b306460 input=eeb2b923a30ce04e]*/ -{ - int sid; - sid = getsid(pid); - if (sid < 0) - return posix_error(); - return PyLong_FromLong((long)sid); -} -#endif /* HAVE_GETSID */ - - -#ifdef HAVE_SETSID -/*[clinic input] -os.setsid - -Call the system call setsid(). -[clinic start generated code]*/ - -static PyObject * -os_setsid_impl(PyObject *module) -/*[clinic end generated code: output=e2ddedd517086d77 input=5fff45858e2f0776]*/ -{ - if (setsid() < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_SETSID */ - - -#ifdef HAVE_SETPGID -/*[clinic input] -os.setpgid - - pid: pid_t - pgrp: pid_t - / - -Call the system call setpgid(pid, pgrp). -[clinic start generated code]*/ - -static PyObject * -os_setpgid_impl(PyObject *module, pid_t pid, pid_t pgrp) -/*[clinic end generated code: output=6461160319a43d6a input=fceb395eca572e1a]*/ -{ - if (setpgid(pid, pgrp) < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_SETPGID */ - - -#ifdef HAVE_TCGETPGRP -/*[clinic input] -os.tcgetpgrp - - fd: int - / - -Return the process group associated with the terminal specified by fd. -[clinic start generated code]*/ - -static PyObject * -os_tcgetpgrp_impl(PyObject *module, int fd) -/*[clinic end generated code: output=f865e88be86c272b input=7f6c18eac10ada86]*/ -{ - pid_t pgid = tcgetpgrp(fd); - if (pgid < 0) - return posix_error(); - return PyLong_FromPid(pgid); -} -#endif /* HAVE_TCGETPGRP */ - - -#ifdef HAVE_TCSETPGRP -/*[clinic input] -os.tcsetpgrp - - fd: int - pgid: pid_t - / - -Set the process group associated with the terminal specified by fd. -[clinic start generated code]*/ - -static PyObject * -os_tcsetpgrp_impl(PyObject *module, int fd, pid_t pgid) -/*[clinic end generated code: output=f1821a381b9daa39 input=5bdc997c6a619020]*/ -{ - if (tcsetpgrp(fd, pgid) < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_TCSETPGRP */ - -/* Functions acting on file descriptors */ - -#ifdef O_CLOEXEC -extern int _Py_open_cloexec_works; -#endif - - -/*[clinic input] -os.open -> int - path: path_t - flags: int - mode: int = 0o777 - * - dir_fd: dir_fd(requires='openat') = None - -# "open(path, flags, mode=0o777, *, dir_fd=None)\n\n\ - -Open a file for low level IO. Returns a file descriptor (integer). - -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. -dir_fd may not be implemented on your platform. - If it is unavailable, using it will raise a NotImplementedError. -[clinic start generated code]*/ - -static int -os_open_impl(PyObject *module, path_t *path, int flags, int mode, int dir_fd) -/*[clinic end generated code: output=abc7227888c8bc73 input=ad8623b29acd2934]*/ -{ - int fd; - int async_err = 0; -#ifdef HAVE_OPENAT - int openat_unavailable = 0; -#endif - -#ifdef O_CLOEXEC - int *atomic_flag_works = &_Py_open_cloexec_works; -#elif !defined(MS_WINDOWS) - int *atomic_flag_works = NULL; -#endif - -#ifdef MS_WINDOWS - flags |= O_NOINHERIT; -#elif defined(O_CLOEXEC) - flags |= O_CLOEXEC; -#endif - - if (PySys_Audit("open", "OOi", path->object, Py_None, flags) < 0) { - return -1; - } - - _Py_BEGIN_SUPPRESS_IPH - do { - Py_BEGIN_ALLOW_THREADS -#ifdef MS_WINDOWS - fd = _wopen(path->wide, flags, mode); -#else -#ifdef HAVE_OPENAT - if (dir_fd != DEFAULT_DIR_FD) { - if (HAVE_OPENAT_RUNTIME) { - fd = openat(dir_fd, path->narrow, flags, mode); - - } else { - openat_unavailable = 1; - fd = -1; - } - } else -#endif /* HAVE_OPENAT */ - fd = open(path->narrow, flags, mode); -#endif /* !MS_WINDOWS */ - Py_END_ALLOW_THREADS - } while (fd < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - _Py_END_SUPPRESS_IPH - -#ifdef HAVE_OPENAT - if (openat_unavailable) { - argument_unavailable_error(NULL, "dir_fd"); - return -1; - } -#endif - - if (fd < 0) { - if (!async_err) - PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object); - return -1; - } - -#ifndef MS_WINDOWS - if (_Py_set_inheritable(fd, 0, atomic_flag_works) < 0) { - close(fd); - return -1; - } -#endif - - return fd; -} - - -/*[clinic input] -os.close - - fd: int - -Close a file descriptor. -[clinic start generated code]*/ - -static PyObject * -os_close_impl(PyObject *module, int fd) -/*[clinic end generated code: output=2fe4e93602822c14 input=2bc42451ca5c3223]*/ -{ - int res; - /* We do not want to retry upon EINTR: see http://lwn.net/Articles/576478/ - * and http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html - * for more details. - */ - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH - res = close(fd); - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - if (res < 0) - return posix_error(); - Py_RETURN_NONE; -} - -/*[clinic input] -os.closerange - - fd_low: int - fd_high: int - / - -Closes all file descriptors in [fd_low, fd_high), ignoring errors. -[clinic start generated code]*/ - -static PyObject * -os_closerange_impl(PyObject *module, int fd_low, int fd_high) -/*[clinic end generated code: output=0ce5c20fcda681c2 input=5855a3d053ebd4ec]*/ -{ - Py_BEGIN_ALLOW_THREADS - _Py_closerange(fd_low, fd_high - 1); - Py_END_ALLOW_THREADS - Py_RETURN_NONE; -} - - -/*[clinic input] -os.dup -> int - - fd: int - / - -Return a duplicate of a file descriptor. -[clinic start generated code]*/ - -static int -os_dup_impl(PyObject *module, int fd) -/*[clinic end generated code: output=486f4860636b2a9f input=6f10f7ea97f7852a]*/ -{ - return _Py_dup(fd); -} - -// dup2() is either provided by libc or dup2.c with AC_REPLACE_FUNCS(). -// dup2.c provides working dup2() if and only if F_DUPFD is available. -#if (defined(HAVE_DUP3) || defined(F_DUPFD) || defined(MS_WINDOWS)) -/*[clinic input] -os.dup2 -> int - fd: int - fd2: int - inheritable: bool=True - -Duplicate file descriptor. -[clinic start generated code]*/ - -static int -os_dup2_impl(PyObject *module, int fd, int fd2, int inheritable) -/*[clinic end generated code: output=bc059d34a73404d1 input=c3cddda8922b038d]*/ -{ - int res = 0; -#if defined(HAVE_DUP3) && \ - !(defined(HAVE_FCNTL_H) && defined(F_DUP2FD_CLOEXEC)) - /* dup3() is available on Linux 2.6.27+ and glibc 2.9 */ - static int dup3_works = -1; -#endif - - /* dup2() can fail with EINTR if the target FD is already open, because it - * then has to be closed. See os_close_impl() for why we don't handle EINTR - * upon close(), and therefore below. - */ -#ifdef MS_WINDOWS - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH - res = dup2(fd, fd2); - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - if (res < 0) { - posix_error(); - return -1; - } - res = fd2; // msvcrt dup2 returns 0 on success. - - /* Character files like console cannot be make non-inheritable */ - if (!inheritable && _Py_set_inheritable(fd2, 0, NULL) < 0) { - close(fd2); - return -1; - } - -#elif defined(HAVE_FCNTL_H) && defined(F_DUP2FD_CLOEXEC) - Py_BEGIN_ALLOW_THREADS - if (!inheritable) - res = fcntl(fd, F_DUP2FD_CLOEXEC, fd2); - else - res = dup2(fd, fd2); - Py_END_ALLOW_THREADS - if (res < 0) { - posix_error(); - return -1; - } - -#else - -#ifdef HAVE_DUP3 - if (!inheritable && dup3_works != 0) { - Py_BEGIN_ALLOW_THREADS - res = dup3(fd, fd2, O_CLOEXEC); - Py_END_ALLOW_THREADS - if (res < 0) { - if (dup3_works == -1) - dup3_works = (errno != ENOSYS); - if (dup3_works) { - posix_error(); - return -1; - } - } - } - - if (inheritable || dup3_works == 0) - { -#endif - Py_BEGIN_ALLOW_THREADS - res = dup2(fd, fd2); - Py_END_ALLOW_THREADS - if (res < 0) { - posix_error(); - return -1; - } - - if (!inheritable && _Py_set_inheritable(fd2, 0, NULL) < 0) { - close(fd2); - return -1; - } -#ifdef HAVE_DUP3 - } -#endif - -#endif - - return res; -} -#endif - - -#ifdef HAVE_LOCKF -/*[clinic input] -os.lockf - - fd: int - An open file descriptor. - command: int - One of F_LOCK, F_TLOCK, F_ULOCK or F_TEST. - length: Py_off_t - The number of bytes to lock, starting at the current position. - / - -Apply, test or remove a POSIX lock on an open file descriptor. - -[clinic start generated code]*/ - -static PyObject * -os_lockf_impl(PyObject *module, int fd, int command, Py_off_t length) -/*[clinic end generated code: output=af7051f3e7c29651 input=65da41d2106e9b79]*/ -{ - int res; - - if (PySys_Audit("os.lockf", "iiL", fd, command, length) < 0) { - return NULL; - } - - Py_BEGIN_ALLOW_THREADS - res = lockf(fd, command, length); - Py_END_ALLOW_THREADS - - if (res < 0) - return posix_error(); - - Py_RETURN_NONE; -} -#endif /* HAVE_LOCKF */ - - -/*[clinic input] -os.lseek -> Py_off_t - - fd: int - An open file descriptor, as returned by os.open(). - position: Py_off_t - Position, interpreted relative to 'whence'. - whence as how: int - The relative position to seek from. Valid values are: - - SEEK_SET: seek from the start of the file. - - SEEK_CUR: seek from the current file position. - - SEEK_END: seek from the end of the file. - / - -Set the position of a file descriptor. Return the new position. - -The return value is the number of bytes relative to the beginning of the file. -[clinic start generated code]*/ - -static Py_off_t -os_lseek_impl(PyObject *module, int fd, Py_off_t position, int how) -/*[clinic end generated code: output=971e1efb6b30bd2f input=f096e754c5367504]*/ -{ - Py_off_t result; - -#ifdef SEEK_SET - /* Turn 0, 1, 2 into SEEK_{SET,CUR,END} */ - switch (how) { - case 0: how = SEEK_SET; break; - case 1: how = SEEK_CUR; break; - case 2: how = SEEK_END; break; - } -#endif /* SEEK_END */ - - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH -#ifdef MS_WINDOWS - result = _lseeki64(fd, position, how); -#else - result = lseek(fd, position, how); -#endif - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - if (result < 0) - posix_error(); - - return result; -} - - -/*[clinic input] -os.read - fd: int - length: Py_ssize_t - / - -Read from a file descriptor. Returns a bytes object. -[clinic start generated code]*/ - -static PyObject * -os_read_impl(PyObject *module, int fd, Py_ssize_t length) -/*[clinic end generated code: output=dafbe9a5cddb987b input=1df2eaa27c0bf1d3]*/ -{ - Py_ssize_t n; - PyObject *buffer; - - if (length < 0) { - errno = EINVAL; - return posix_error(); - } - - length = Py_MIN(length, _PY_READ_MAX); - - buffer = PyBytes_FromStringAndSize((char *)NULL, length); - if (buffer == NULL) - return NULL; - - n = _Py_read(fd, PyBytes_AS_STRING(buffer), length); - if (n == -1) { - Py_DECREF(buffer); - return NULL; - } - - if (n != length) - _PyBytes_Resize(&buffer, n); - - return buffer; -} - -#if (defined(HAVE_SENDFILE) && (defined(__FreeBSD__) || defined(__DragonFly__) \ - || defined(__APPLE__))) \ - || defined(HAVE_READV) || defined(HAVE_PREADV) || defined (HAVE_PREADV2) \ - || defined(HAVE_WRITEV) || defined(HAVE_PWRITEV) || defined (HAVE_PWRITEV2) -static int -iov_setup(struct iovec **iov, Py_buffer **buf, PyObject *seq, Py_ssize_t cnt, int type) -{ - Py_ssize_t i, j; - - *iov = PyMem_New(struct iovec, cnt); - if (*iov == NULL) { - PyErr_NoMemory(); - return -1; - } - - *buf = PyMem_New(Py_buffer, cnt); - if (*buf == NULL) { - PyMem_Free(*iov); - PyErr_NoMemory(); - return -1; - } - - for (i = 0; i < cnt; i++) { - PyObject *item = PySequence_GetItem(seq, i); - if (item == NULL) - goto fail; - if (PyObject_GetBuffer(item, &(*buf)[i], type) == -1) { - Py_DECREF(item); - goto fail; - } - Py_DECREF(item); - (*iov)[i].iov_base = (*buf)[i].buf; - (*iov)[i].iov_len = (*buf)[i].len; - } - return 0; - -fail: - PyMem_Free(*iov); - for (j = 0; j < i; j++) { - PyBuffer_Release(&(*buf)[j]); - } - PyMem_Free(*buf); - return -1; -} - -static void -iov_cleanup(struct iovec *iov, Py_buffer *buf, int cnt) -{ - int i; - PyMem_Free(iov); - for (i = 0; i < cnt; i++) { - PyBuffer_Release(&buf[i]); - } - PyMem_Free(buf); -} -#endif - - -#ifdef HAVE_READV -/*[clinic input] -os.readv -> Py_ssize_t - - fd: int - buffers: object - / - -Read from a file descriptor fd into an iterable of buffers. - -The buffers should be mutable buffers accepting bytes. -readv will transfer data into each buffer until it is full -and then move on to the next buffer in the sequence to hold -the rest of the data. - -readv returns the total number of bytes read, -which may be less than the total capacity of all the buffers. -[clinic start generated code]*/ - -static Py_ssize_t -os_readv_impl(PyObject *module, int fd, PyObject *buffers) -/*[clinic end generated code: output=792da062d3fcebdb input=e679eb5dbfa0357d]*/ -{ - Py_ssize_t cnt, n; - int async_err = 0; - struct iovec *iov; - Py_buffer *buf; - - if (!PySequence_Check(buffers)) { - PyErr_SetString(PyExc_TypeError, - "readv() arg 2 must be a sequence"); - return -1; - } - - cnt = PySequence_Size(buffers); - if (cnt < 0) - return -1; - - if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_WRITABLE) < 0) - return -1; - - do { - Py_BEGIN_ALLOW_THREADS - n = readv(fd, iov, cnt); - Py_END_ALLOW_THREADS - } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - - int saved_errno = errno; - iov_cleanup(iov, buf, cnt); - if (n < 0) { - if (!async_err) { - errno = saved_errno; - posix_error(); - } - return -1; - } - - return n; -} -#endif /* HAVE_READV */ - - -#ifdef HAVE_PREAD -/*[clinic input] -os.pread - - fd: int - length: Py_ssize_t - offset: Py_off_t - / - -Read a number of bytes from a file descriptor starting at a particular offset. - -Read length bytes from file descriptor fd, starting at offset bytes from -the beginning of the file. The file offset remains unchanged. -[clinic start generated code]*/ - -static PyObject * -os_pread_impl(PyObject *module, int fd, Py_ssize_t length, Py_off_t offset) -/*[clinic end generated code: output=3f875c1eef82e32f input=85cb4a5589627144]*/ -{ - Py_ssize_t n; - int async_err = 0; - PyObject *buffer; - - if (length < 0) { - errno = EINVAL; - return posix_error(); - } - buffer = PyBytes_FromStringAndSize((char *)NULL, length); - if (buffer == NULL) - return NULL; - - do { - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH - n = pread(fd, PyBytes_AS_STRING(buffer), length, offset); - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - - if (n < 0) { - if (!async_err) { - posix_error(); - } - Py_DECREF(buffer); - return NULL; - } - if (n != length) - _PyBytes_Resize(&buffer, n); - return buffer; -} -#endif /* HAVE_PREAD */ - -#if defined(HAVE_PREADV) || defined (HAVE_PREADV2) -/*[clinic input] -os.preadv -> Py_ssize_t - - fd: int - buffers: object - offset: Py_off_t - flags: int = 0 - / - -Reads from a file descriptor into a number of mutable bytes-like objects. - -Combines the functionality of readv() and pread(). As readv(), it will -transfer data into each buffer until it is full and then move on to the next -buffer in the sequence to hold the rest of the data. Its fourth argument, -specifies the file offset at which the input operation is to be performed. It -will return the total number of bytes read (which can be less than the total -capacity of all the objects). - -The flags argument contains a bitwise OR of zero or more of the following flags: - -- RWF_HIPRI -- RWF_NOWAIT - -Using non-zero flags requires Linux 4.6 or newer. -[clinic start generated code]*/ - -static Py_ssize_t -os_preadv_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset, - int flags) -/*[clinic end generated code: output=26fc9c6e58e7ada5 input=4173919dc1f7ed99]*/ -{ - Py_ssize_t cnt, n; - int async_err = 0; - struct iovec *iov; - Py_buffer *buf; - - if (!PySequence_Check(buffers)) { - PyErr_SetString(PyExc_TypeError, - "preadv2() arg 2 must be a sequence"); - return -1; - } - - cnt = PySequence_Size(buffers); - if (cnt < 0) { - return -1; - } - -#ifndef HAVE_PREADV2 - if(flags != 0) { - argument_unavailable_error("preadv2", "flags"); - return -1; - } -#endif - - if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_WRITABLE) < 0) { - return -1; - } -#ifdef HAVE_PREADV2 - do { - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH - n = preadv2(fd, iov, cnt, offset, flags); - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); -#else - do { -#if defined(__APPLE__) && defined(__clang__) -/* This entire function will be removed from the module dict when the API - * is not available. - */ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunguarded-availability" -#pragma clang diagnostic ignored "-Wunguarded-availability-new" -#endif - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH - n = preadv(fd, iov, cnt, offset); - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - -#if defined(__APPLE__) && defined(__clang__) -#pragma clang diagnostic pop -#endif - -#endif - - int saved_errno = errno; - iov_cleanup(iov, buf, cnt); - if (n < 0) { - if (!async_err) { - errno = saved_errno; - posix_error(); - } - return -1; - } - - return n; -} -#endif /* HAVE_PREADV */ - - -/*[clinic input] -os.write -> Py_ssize_t - - fd: int - data: Py_buffer - / - -Write a bytes object to a file descriptor. -[clinic start generated code]*/ - -static Py_ssize_t -os_write_impl(PyObject *module, int fd, Py_buffer *data) -/*[clinic end generated code: output=e4ef5bc904b58ef9 input=3207e28963234f3c]*/ -{ - return _Py_write(fd, data->buf, data->len); -} - -#ifdef HAVE_SENDFILE -#ifdef __APPLE__ -/*[clinic input] -os.sendfile - - out_fd: int - in_fd: int - offset: Py_off_t - count as sbytes: Py_off_t - headers: object(c_default="NULL") = () - trailers: object(c_default="NULL") = () - flags: int = 0 - -Copy count bytes from file descriptor in_fd to file descriptor out_fd. -[clinic start generated code]*/ - -static PyObject * -os_sendfile_impl(PyObject *module, int out_fd, int in_fd, Py_off_t offset, - Py_off_t sbytes, PyObject *headers, PyObject *trailers, - int flags) -/*[clinic end generated code: output=81c4bcd143f5c82b input=b0d72579d4c69afa]*/ -#elif defined(__FreeBSD__) || defined(__DragonFly__) -/*[clinic input] -os.sendfile - - out_fd: int - in_fd: int - offset: Py_off_t - count: Py_ssize_t - headers: object(c_default="NULL") = () - trailers: object(c_default="NULL") = () - flags: int = 0 - -Copy count bytes from file descriptor in_fd to file descriptor out_fd. -[clinic start generated code]*/ - -static PyObject * -os_sendfile_impl(PyObject *module, int out_fd, int in_fd, Py_off_t offset, - Py_ssize_t count, PyObject *headers, PyObject *trailers, - int flags) -/*[clinic end generated code: output=329ea009bdd55afc input=338adb8ff84ae8cd]*/ -#else -/*[clinic input] -os.sendfile - - out_fd: int - in_fd: int - offset as offobj: object - count: Py_ssize_t - -Copy count bytes from file descriptor in_fd to file descriptor out_fd. -[clinic start generated code]*/ - -static PyObject * -os_sendfile_impl(PyObject *module, int out_fd, int in_fd, PyObject *offobj, - Py_ssize_t count) -/*[clinic end generated code: output=ae81216e40f167d8 input=76d64058c74477ba]*/ -#endif -{ - Py_ssize_t ret; - int async_err = 0; - -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__) -#ifndef __APPLE__ - off_t sbytes; -#endif - Py_buffer *hbuf, *tbuf; - struct sf_hdtr sf; - - sf.headers = NULL; - sf.trailers = NULL; - - if (headers != NULL) { - if (!PySequence_Check(headers)) { - PyErr_SetString(PyExc_TypeError, - "sendfile() headers must be a sequence"); - return NULL; - } else { - Py_ssize_t i = PySequence_Size(headers); - if (i < 0) - return NULL; - if (i > INT_MAX) { - PyErr_SetString(PyExc_OverflowError, - "sendfile() header is too large"); - return NULL; - } - if (i > 0) { - sf.hdr_cnt = (int)i; - if (iov_setup(&(sf.headers), &hbuf, - headers, sf.hdr_cnt, PyBUF_SIMPLE) < 0) - return NULL; -#ifdef __APPLE__ - for (i = 0; i < sf.hdr_cnt; i++) { - Py_ssize_t blen = sf.headers[i].iov_len; -# define OFF_T_MAX 0x7fffffffffffffff - if (sbytes >= OFF_T_MAX - blen) { - PyErr_SetString(PyExc_OverflowError, - "sendfile() header is too large"); - return NULL; - } - sbytes += blen; - } -#endif - } - } - } - if (trailers != NULL) { - if (!PySequence_Check(trailers)) { - PyErr_SetString(PyExc_TypeError, - "sendfile() trailers must be a sequence"); - return NULL; - } else { - Py_ssize_t i = PySequence_Size(trailers); - if (i < 0) - return NULL; - if (i > INT_MAX) { - PyErr_SetString(PyExc_OverflowError, - "sendfile() trailer is too large"); - return NULL; - } - if (i > 0) { - sf.trl_cnt = (int)i; - if (iov_setup(&(sf.trailers), &tbuf, - trailers, sf.trl_cnt, PyBUF_SIMPLE) < 0) - return NULL; - } - } - } - - _Py_BEGIN_SUPPRESS_IPH - do { - Py_BEGIN_ALLOW_THREADS -#ifdef __APPLE__ - ret = sendfile(in_fd, out_fd, offset, &sbytes, &sf, flags); -#else - ret = sendfile(in_fd, out_fd, offset, count, &sf, &sbytes, flags); -#endif - Py_END_ALLOW_THREADS - } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - _Py_END_SUPPRESS_IPH - - int saved_errno = errno; - if (sf.headers != NULL) - iov_cleanup(sf.headers, hbuf, sf.hdr_cnt); - if (sf.trailers != NULL) - iov_cleanup(sf.trailers, tbuf, sf.trl_cnt); - - if (ret < 0) { - if ((saved_errno == EAGAIN) || (saved_errno == EBUSY)) { - if (sbytes != 0) { - // some data has been sent - goto done; - } - // no data has been sent; upper application is supposed - // to retry on EAGAIN or EBUSY - } - if (!async_err) { - errno = saved_errno; - posix_error(); - } - return NULL; - } - goto done; - -done: - #if !defined(HAVE_LARGEFILE_SUPPORT) - return PyLong_FromLong(sbytes); - #else - return PyLong_FromLongLong(sbytes); - #endif - -#else -#ifdef __linux__ - if (offobj == Py_None) { - do { - Py_BEGIN_ALLOW_THREADS - ret = sendfile(out_fd, in_fd, NULL, count); - Py_END_ALLOW_THREADS - } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - if (ret < 0) - return (!async_err) ? posix_error() : NULL; - return PyLong_FromSsize_t(ret); - } -#endif - off_t offset; - if (!Py_off_t_converter(offobj, &offset)) - return NULL; - -#if defined(__sun) && defined(__SVR4) - // On Solaris, sendfile raises EINVAL rather than returning 0 - // when the offset is equal or bigger than the in_fd size. - struct stat st; - - do { - Py_BEGIN_ALLOW_THREADS - ret = fstat(in_fd, &st); - Py_END_ALLOW_THREADS - } while (ret != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - if (ret < 0) - return (!async_err) ? posix_error() : NULL; - - if (offset >= st.st_size) { - return PyLong_FromLong(0); - } - - // On illumos specifically sendfile() may perform a partial write but - // return -1/an error (in one confirmed case the destination socket - // had a 5 second timeout set and errno was EAGAIN) and it's on the client - // code to check if the offset parameter was modified by sendfile(). - // - // We need this variable to track said change. - off_t original_offset = offset; -#endif - - do { - Py_BEGIN_ALLOW_THREADS - ret = sendfile(out_fd, in_fd, &offset, count); -#if defined(__sun) && defined(__SVR4) - // This handles illumos-specific sendfile() partial write behavior, - // see a comment above for more details. - if (ret < 0 && offset != original_offset) { - ret = offset - original_offset; - } -#endif - Py_END_ALLOW_THREADS - } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - if (ret < 0) - return (!async_err) ? posix_error() : NULL; - return PyLong_FromSsize_t(ret); -#endif -} -#endif /* HAVE_SENDFILE */ - - -#if defined(__APPLE__) -/*[clinic input] -os._fcopyfile - - in_fd: int - out_fd: int - flags: int - / - -Efficiently copy content or metadata of 2 regular file descriptors (macOS). -[clinic start generated code]*/ - -static PyObject * -os__fcopyfile_impl(PyObject *module, int in_fd, int out_fd, int flags) -/*[clinic end generated code: output=c9d1a35a992e401b input=1e34638a86948795]*/ -{ - int ret; - - Py_BEGIN_ALLOW_THREADS - ret = fcopyfile(in_fd, out_fd, NULL, flags); - Py_END_ALLOW_THREADS - if (ret < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif - - -/*[clinic input] -os.fstat - - fd : int - -Perform a stat system call on the given file descriptor. - -Like stat(), but for an open file descriptor. -Equivalent to os.stat(fd). -[clinic start generated code]*/ - -static PyObject * -os_fstat_impl(PyObject *module, int fd) -/*[clinic end generated code: output=efc038cb5f654492 input=27e0e0ebbe5600c9]*/ -{ - STRUCT_STAT st; - int res; - int async_err = 0; - - do { - Py_BEGIN_ALLOW_THREADS - res = FSTAT(fd, &st); - Py_END_ALLOW_THREADS - } while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - if (res != 0) { -#ifdef MS_WINDOWS - return PyErr_SetFromWindowsErr(0); -#else - return (!async_err) ? posix_error() : NULL; -#endif - } - - return _pystat_fromstructstat(module, &st); -} - - -/*[clinic input] -os.isatty -> bool - fd: int - / - -Return True if the fd is connected to a terminal. - -Return True if the file descriptor is an open file descriptor -connected to the slave end of a terminal. -[clinic start generated code]*/ - -static int -os_isatty_impl(PyObject *module, int fd) -/*[clinic end generated code: output=6a48c8b4e644ca00 input=08ce94aa1eaf7b5e]*/ -{ - int return_value; - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH - return_value = isatty(fd); - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - return return_value; -} - - -#ifdef HAVE_PIPE -/*[clinic input] -os.pipe - -Create a pipe. - -Returns a tuple of two file descriptors: - (read_fd, write_fd) -[clinic start generated code]*/ - -static PyObject * -os_pipe_impl(PyObject *module) -/*[clinic end generated code: output=ff9b76255793b440 input=02535e8c8fa6c4d4]*/ -{ - int fds[2]; -#ifdef MS_WINDOWS - HANDLE read, write; - SECURITY_ATTRIBUTES attr; - BOOL ok; -#else - int res; -#endif - -#ifdef MS_WINDOWS - attr.nLength = sizeof(attr); - attr.lpSecurityDescriptor = NULL; - attr.bInheritHandle = FALSE; - - Py_BEGIN_ALLOW_THREADS - ok = CreatePipe(&read, &write, &attr, 0); - if (ok) { - fds[0] = _Py_open_osfhandle_noraise(read, _O_RDONLY | _O_NOINHERIT); - fds[1] = _Py_open_osfhandle_noraise(write, _O_WRONLY | _O_NOINHERIT); - if (fds[0] == -1 || fds[1] == -1) { - CloseHandle(read); - CloseHandle(write); - ok = 0; - } - } - Py_END_ALLOW_THREADS - - if (!ok) - return PyErr_SetFromWindowsErr(0); -#else - -#ifdef HAVE_PIPE2 - Py_BEGIN_ALLOW_THREADS - res = pipe2(fds, O_CLOEXEC); - Py_END_ALLOW_THREADS - - if (res != 0 && errno == ENOSYS) - { -#endif - Py_BEGIN_ALLOW_THREADS - res = pipe(fds); - Py_END_ALLOW_THREADS - - if (res == 0) { - if (_Py_set_inheritable(fds[0], 0, NULL) < 0) { - close(fds[0]); - close(fds[1]); - return NULL; - } - if (_Py_set_inheritable(fds[1], 0, NULL) < 0) { - close(fds[0]); - close(fds[1]); - return NULL; - } - } -#ifdef HAVE_PIPE2 - } -#endif - - if (res != 0) - return PyErr_SetFromErrno(PyExc_OSError); -#endif /* !MS_WINDOWS */ - return Py_BuildValue("(ii)", fds[0], fds[1]); -} -#endif /* HAVE_PIPE */ - - -#ifdef HAVE_PIPE2 -/*[clinic input] -os.pipe2 - - flags: int - / - -Create a pipe with flags set atomically. - -Returns a tuple of two file descriptors: - (read_fd, write_fd) - -flags can be constructed by ORing together one or more of these values: -O_NONBLOCK, O_CLOEXEC. -[clinic start generated code]*/ - -static PyObject * -os_pipe2_impl(PyObject *module, int flags) -/*[clinic end generated code: output=25751fb43a45540f input=f261b6e7e63c6817]*/ -{ - int fds[2]; - int res; - - res = pipe2(fds, flags); - if (res != 0) - return posix_error(); - return Py_BuildValue("(ii)", fds[0], fds[1]); -} -#endif /* HAVE_PIPE2 */ - - -#ifdef HAVE_WRITEV -/*[clinic input] -os.writev -> Py_ssize_t - fd: int - buffers: object - / - -Iterate over buffers, and write the contents of each to a file descriptor. - -Returns the total number of bytes written. -buffers must be a sequence of bytes-like objects. -[clinic start generated code]*/ - -static Py_ssize_t -os_writev_impl(PyObject *module, int fd, PyObject *buffers) -/*[clinic end generated code: output=56565cfac3aac15b input=5b8d17fe4189d2fe]*/ -{ - Py_ssize_t cnt; - Py_ssize_t result; - int async_err = 0; - struct iovec *iov; - Py_buffer *buf; - - if (!PySequence_Check(buffers)) { - PyErr_SetString(PyExc_TypeError, - "writev() arg 2 must be a sequence"); - return -1; - } - cnt = PySequence_Size(buffers); - if (cnt < 0) - return -1; - - if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_SIMPLE) < 0) { - return -1; - } - - do { - Py_BEGIN_ALLOW_THREADS - result = writev(fd, iov, cnt); - Py_END_ALLOW_THREADS - } while (result < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - - if (result < 0 && !async_err) - posix_error(); - - iov_cleanup(iov, buf, cnt); - return result; -} -#endif /* HAVE_WRITEV */ - - -#ifdef HAVE_PWRITE -/*[clinic input] -os.pwrite -> Py_ssize_t - - fd: int - buffer: Py_buffer - offset: Py_off_t - / - -Write bytes to a file descriptor starting at a particular offset. - -Write buffer to fd, starting at offset bytes from the beginning of -the file. Returns the number of bytes written. Does not change the -current file offset. -[clinic start generated code]*/ - -static Py_ssize_t -os_pwrite_impl(PyObject *module, int fd, Py_buffer *buffer, Py_off_t offset) -/*[clinic end generated code: output=c74da630758ee925 input=614acbc7e5a0339a]*/ -{ - Py_ssize_t size; - int async_err = 0; - - do { - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH - size = pwrite(fd, buffer->buf, (size_t)buffer->len, offset); - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - } while (size < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - - if (size < 0 && !async_err) - posix_error(); - return size; -} -#endif /* HAVE_PWRITE */ - -#if defined(HAVE_PWRITEV) || defined (HAVE_PWRITEV2) -/*[clinic input] -os.pwritev -> Py_ssize_t - - fd: int - buffers: object - offset: Py_off_t - flags: int = 0 - / - -Writes the contents of bytes-like objects to a file descriptor at a given offset. - -Combines the functionality of writev() and pwrite(). All buffers must be a sequence -of bytes-like objects. Buffers are processed in array order. Entire contents of first -buffer is written before proceeding to second, and so on. The operating system may -set a limit (sysconf() value SC_IOV_MAX) on the number of buffers that can be used. -This function writes the contents of each object to the file descriptor and returns -the total number of bytes written. - -The flags argument contains a bitwise OR of zero or more of the following flags: - -- RWF_DSYNC -- RWF_SYNC -- RWF_APPEND - -Using non-zero flags requires Linux 4.7 or newer. -[clinic start generated code]*/ - -static Py_ssize_t -os_pwritev_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset, - int flags) -/*[clinic end generated code: output=e3dd3e9d11a6a5c7 input=35358c327e1a2a8e]*/ -{ - Py_ssize_t cnt; - Py_ssize_t result; - int async_err = 0; - struct iovec *iov; - Py_buffer *buf; - - if (!PySequence_Check(buffers)) { - PyErr_SetString(PyExc_TypeError, - "pwritev() arg 2 must be a sequence"); - return -1; - } - - cnt = PySequence_Size(buffers); - if (cnt < 0) { - return -1; - } - -#ifndef HAVE_PWRITEV2 - if(flags != 0) { - argument_unavailable_error("pwritev2", "flags"); - return -1; - } -#endif - - if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_SIMPLE) < 0) { - return -1; - } -#ifdef HAVE_PWRITEV2 - do { - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH - result = pwritev2(fd, iov, cnt, offset, flags); - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - } while (result < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); -#else - -#if defined(__APPLE__) && defined(__clang__) -/* This entire function will be removed from the module dict when the API - * is not available. - */ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunguarded-availability" -#pragma clang diagnostic ignored "-Wunguarded-availability-new" -#endif - do { - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH - result = pwritev(fd, iov, cnt, offset); - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - } while (result < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - -#if defined(__APPLE__) && defined(__clang__) -#pragma clang diagnostic pop -#endif - -#endif - - if (result < 0) { - if (!async_err) { - posix_error(); - } - result = -1; - } - iov_cleanup(iov, buf, cnt); - - return result; -} -#endif /* HAVE_PWRITEV */ - -#ifdef HAVE_COPY_FILE_RANGE -/*[clinic input] - -os.copy_file_range - src: int - Source file descriptor. - dst: int - Destination file descriptor. - count: Py_ssize_t - Number of bytes to copy. - offset_src: object = None - Starting offset in src. - offset_dst: object = None - Starting offset in dst. - -Copy count bytes from one file descriptor to another. - -If offset_src is None, then src is read from the current position; -respectively for offset_dst. -[clinic start generated code]*/ - -static PyObject * -os_copy_file_range_impl(PyObject *module, int src, int dst, Py_ssize_t count, - PyObject *offset_src, PyObject *offset_dst) -/*[clinic end generated code: output=1a91713a1d99fc7a input=42fdce72681b25a9]*/ -{ - off_t offset_src_val, offset_dst_val; - off_t *p_offset_src = NULL; - off_t *p_offset_dst = NULL; - Py_ssize_t ret; - int async_err = 0; - /* The flags argument is provided to allow - * for future extensions and currently must be to 0. */ - int flags = 0; - - - if (count < 0) { - PyErr_SetString(PyExc_ValueError, "negative value for 'count' not allowed"); - return NULL; - } - - if (offset_src != Py_None) { - if (!Py_off_t_converter(offset_src, &offset_src_val)) { - return NULL; - } - p_offset_src = &offset_src_val; - } - - if (offset_dst != Py_None) { - if (!Py_off_t_converter(offset_dst, &offset_dst_val)) { - return NULL; - } - p_offset_dst = &offset_dst_val; - } - - do { - Py_BEGIN_ALLOW_THREADS - ret = copy_file_range(src, p_offset_src, dst, p_offset_dst, count, flags); - Py_END_ALLOW_THREADS - } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - - if (ret < 0) { - return (!async_err) ? posix_error() : NULL; - } - - return PyLong_FromSsize_t(ret); -} -#endif /* HAVE_COPY_FILE_RANGE*/ - -#if (defined(HAVE_SPLICE) && !defined(_AIX)) -/*[clinic input] - -os.splice - src: int - Source file descriptor. - dst: int - Destination file descriptor. - count: Py_ssize_t - Number of bytes to copy. - offset_src: object = None - Starting offset in src. - offset_dst: object = None - Starting offset in dst. - flags: unsigned_int = 0 - Flags to modify the semantics of the call. - -Transfer count bytes from one pipe to a descriptor or vice versa. - -If offset_src is None, then src is read from the current position; -respectively for offset_dst. The offset associated to the file -descriptor that refers to a pipe must be None. -[clinic start generated code]*/ - -static PyObject * -os_splice_impl(PyObject *module, int src, int dst, Py_ssize_t count, - PyObject *offset_src, PyObject *offset_dst, - unsigned int flags) -/*[clinic end generated code: output=d0386f25a8519dc5 input=047527c66c6d2e0a]*/ -{ - off_t offset_src_val, offset_dst_val; - off_t *p_offset_src = NULL; - off_t *p_offset_dst = NULL; - Py_ssize_t ret; - int async_err = 0; - - if (count < 0) { - PyErr_SetString(PyExc_ValueError, "negative value for 'count' not allowed"); - return NULL; - } - - if (offset_src != Py_None) { - if (!Py_off_t_converter(offset_src, &offset_src_val)) { - return NULL; - } - p_offset_src = &offset_src_val; - } - - if (offset_dst != Py_None) { - if (!Py_off_t_converter(offset_dst, &offset_dst_val)) { - return NULL; - } - p_offset_dst = &offset_dst_val; - } - - do { - Py_BEGIN_ALLOW_THREADS - ret = splice(src, p_offset_src, dst, p_offset_dst, count, flags); - Py_END_ALLOW_THREADS - } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); - - if (ret < 0) { - return (!async_err) ? posix_error() : NULL; - } - - return PyLong_FromSsize_t(ret); -} -#endif /* HAVE_SPLICE*/ - -#ifdef HAVE_MKFIFO -/*[clinic input] -os.mkfifo - - path: path_t - mode: int=0o666 - * - dir_fd: dir_fd(requires='mkfifoat')=None - -Create a "fifo" (a POSIX named pipe). - -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. -dir_fd may not be implemented on your platform. - If it is unavailable, using it will raise a NotImplementedError. -[clinic start generated code]*/ - -static PyObject * -os_mkfifo_impl(PyObject *module, path_t *path, int mode, int dir_fd) -/*[clinic end generated code: output=ce41cfad0e68c940 input=73032e98a36e0e19]*/ -{ - int result; - int async_err = 0; -#ifdef HAVE_MKFIFOAT - int mkfifoat_unavailable = 0; -#endif - - do { - Py_BEGIN_ALLOW_THREADS -#ifdef HAVE_MKFIFOAT - if (dir_fd != DEFAULT_DIR_FD) { - if (HAVE_MKFIFOAT_RUNTIME) { - result = mkfifoat(dir_fd, path->narrow, mode); - - } else { - mkfifoat_unavailable = 1; - result = 0; - } - } else -#endif - result = mkfifo(path->narrow, mode); - Py_END_ALLOW_THREADS - } while (result != 0 && errno == EINTR && - !(async_err = PyErr_CheckSignals())); - -#ifdef HAVE_MKFIFOAT - if (mkfifoat_unavailable) { - argument_unavailable_error(NULL, "dir_fd"); - return NULL; - } -#endif - - if (result != 0) - return (!async_err) ? posix_error() : NULL; - - Py_RETURN_NONE; -} -#endif /* HAVE_MKFIFO */ - - -#if defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) -/*[clinic input] -os.mknod - - path: path_t - mode: int=0o600 - device: dev_t=0 - * - dir_fd: dir_fd(requires='mknodat')=None - -Create a node in the file system. - -Create a node in the file system (file, device special file or named pipe) -at path. mode specifies both the permissions to use and the -type of node to be created, being combined (bitwise OR) with one of -S_IFREG, S_IFCHR, S_IFBLK, and S_IFIFO. If S_IFCHR or S_IFBLK is set on mode, -device defines the newly created device special file (probably using -os.makedev()). Otherwise device is ignored. - -If dir_fd is not None, it should be a file descriptor open to a directory, - and path should be relative; path will then be relative to that directory. -dir_fd may not be implemented on your platform. - If it is unavailable, using it will raise a NotImplementedError. -[clinic start generated code]*/ - -static PyObject * -os_mknod_impl(PyObject *module, path_t *path, int mode, dev_t device, - int dir_fd) -/*[clinic end generated code: output=92e55d3ca8917461 input=ee44531551a4d83b]*/ -{ - int result; - int async_err = 0; -#ifdef HAVE_MKNODAT - int mknodat_unavailable = 0; -#endif - - do { - Py_BEGIN_ALLOW_THREADS -#ifdef HAVE_MKNODAT - if (dir_fd != DEFAULT_DIR_FD) { - if (HAVE_MKNODAT_RUNTIME) { - result = mknodat(dir_fd, path->narrow, mode, device); - - } else { - mknodat_unavailable = 1; - result = 0; - } - } else -#endif - result = mknod(path->narrow, mode, device); - Py_END_ALLOW_THREADS - } while (result != 0 && errno == EINTR && - !(async_err = PyErr_CheckSignals())); -#ifdef HAVE_MKNODAT - if (mknodat_unavailable) { - argument_unavailable_error(NULL, "dir_fd"); - return NULL; - } -#endif - if (result != 0) - return (!async_err) ? posix_error() : NULL; - - Py_RETURN_NONE; -} -#endif /* defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) */ - - -#ifdef HAVE_DEVICE_MACROS -static PyObject * -major_minor_conv(unsigned int value) -{ -#ifdef NODEV - if (value == (unsigned int)NODEV) { - return PyLong_FromLong((int)NODEV); - } -#endif - return PyLong_FromUnsignedLong(value); -} - -static int -major_minor_check(dev_t value) -{ -#ifdef NODEV - if (value == NODEV) { - return 1; - } -#endif - return (dev_t)(unsigned int)value == value; -} - -/*[clinic input] -os.major - - device: dev_t - / - -Extracts a device major number from a raw device number. -[clinic start generated code]*/ - -static PyObject * -os_major_impl(PyObject *module, dev_t device) -/*[clinic end generated code: output=4071ffee17647891 input=b1a0a14ec9448229]*/ -{ - return major_minor_conv(major(device)); -} - - -/*[clinic input] -os.minor - - device: dev_t - / - -Extracts a device minor number from a raw device number. -[clinic start generated code]*/ - -static PyObject * -os_minor_impl(PyObject *module, dev_t device) -/*[clinic end generated code: output=306cb78e3bc5004f input=2f686e463682a9da]*/ -{ - return major_minor_conv(minor(device)); -} - - -/*[clinic input] -os.makedev -> dev_t - - major: dev_t - minor: dev_t - / - -Composes a raw device number from the major and minor device numbers. -[clinic start generated code]*/ - -static dev_t -os_makedev_impl(PyObject *module, dev_t major, dev_t minor) -/*[clinic end generated code: output=cad6125c51f5af80 input=2146126ec02e55c1]*/ -{ - if (!major_minor_check(major) || !major_minor_check(minor)) { - PyErr_SetString(PyExc_OverflowError, - "Python int too large to convert to C unsigned int"); - return (dev_t)-1; - } - return makedev(major, minor); -} -#endif /* HAVE_DEVICE_MACROS */ - - -#if defined HAVE_FTRUNCATE || defined MS_WINDOWS -/*[clinic input] -os.ftruncate - - fd: int - length: Py_off_t - / - -Truncate a file, specified by file descriptor, to a specific length. -[clinic start generated code]*/ - -static PyObject * -os_ftruncate_impl(PyObject *module, int fd, Py_off_t length) -/*[clinic end generated code: output=fba15523721be7e4 input=63b43641e52818f2]*/ -{ - int result; - int async_err = 0; - - if (PySys_Audit("os.truncate", "in", fd, length) < 0) { - return NULL; - } - - do { - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH -#ifdef MS_WINDOWS - result = _chsize_s(fd, length); -#else - result = ftruncate(fd, length); -#endif - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - } while (result != 0 && errno == EINTR && - !(async_err = PyErr_CheckSignals())); - if (result != 0) - return (!async_err) ? posix_error() : NULL; - Py_RETURN_NONE; -} -#endif /* HAVE_FTRUNCATE || MS_WINDOWS */ - - -#if defined HAVE_TRUNCATE || defined MS_WINDOWS -/*[clinic input] -os.truncate - path: path_t(allow_fd='PATH_HAVE_FTRUNCATE') - length: Py_off_t - -Truncate a file, specified by path, to a specific length. - -On some platforms, path may also be specified as an open file descriptor. - If this functionality is unavailable, using it raises an exception. -[clinic start generated code]*/ - -static PyObject * -os_truncate_impl(PyObject *module, path_t *path, Py_off_t length) -/*[clinic end generated code: output=43009c8df5c0a12b input=77229cf0b50a9b77]*/ -{ - int result; -#ifdef MS_WINDOWS - int fd; -#endif - - if (path->fd != -1) - return os_ftruncate_impl(module, path->fd, length); - - if (PySys_Audit("os.truncate", "On", path->object, length) < 0) { - return NULL; - } - - Py_BEGIN_ALLOW_THREADS - _Py_BEGIN_SUPPRESS_IPH -#ifdef MS_WINDOWS - fd = _wopen(path->wide, _O_WRONLY | _O_BINARY | _O_NOINHERIT); - if (fd < 0) - result = -1; - else { - result = _chsize_s(fd, length); - close(fd); - if (result < 0) - errno = result; - } -#else - result = truncate(path->narrow, length); -#endif - _Py_END_SUPPRESS_IPH - Py_END_ALLOW_THREADS - if (result < 0) - return posix_path_error(path); - - Py_RETURN_NONE; -} -#endif /* HAVE_TRUNCATE || MS_WINDOWS */ - - -/* Issue #22396: On 32-bit AIX platform, the prototypes of os.posix_fadvise() - and os.posix_fallocate() in system headers are wrong if _LARGE_FILES is - defined, which is the case in Python on AIX. AIX bug report: - http://www-01.ibm.com/support/docview.wss?uid=isg1IV56170 */ -#if defined(_AIX) && defined(_LARGE_FILES) && !defined(__64BIT__) -# define POSIX_FADVISE_AIX_BUG -#endif - - -/* GH-111804: Due to posix_fallocate() not having consistent semantics across - OSs, support was dropped in WASI preview2. */ -#if defined(HAVE_POSIX_FALLOCATE) && !defined(POSIX_FADVISE_AIX_BUG) && \ - !defined(__wasi__) -/*[clinic input] -os.posix_fallocate - - fd: int - offset: Py_off_t - length: Py_off_t - / - -Ensure a file has allocated at least a particular number of bytes on disk. - -Ensure that the file specified by fd encompasses a range of bytes -starting at offset bytes from the beginning and continuing for length bytes. -[clinic start generated code]*/ - -static PyObject * -os_posix_fallocate_impl(PyObject *module, int fd, Py_off_t offset, - Py_off_t length) -/*[clinic end generated code: output=73f107139564aa9d input=d7a2ef0ab2ca52fb]*/ -{ - int result; - int async_err = 0; - - do { - Py_BEGIN_ALLOW_THREADS - result = posix_fallocate(fd, offset, length); - Py_END_ALLOW_THREADS - } while (result == EINTR && !(async_err = PyErr_CheckSignals())); - - if (result == 0) - Py_RETURN_NONE; - - if (async_err) - return NULL; - - errno = result; - return posix_error(); -} -#endif /* HAVE_POSIX_FALLOCATE) && !POSIX_FADVISE_AIX_BUG && !defined(__wasi__) */ - - -#if defined(HAVE_POSIX_FADVISE) && !defined(POSIX_FADVISE_AIX_BUG) -/*[clinic input] -os.posix_fadvise - - fd: int - offset: Py_off_t - length: Py_off_t - advice: int - / - -Announce an intention to access data in a specific pattern. - -Announce an intention to access data in a specific pattern, thus allowing -the kernel to make optimizations. -The advice applies to the region of the file specified by fd starting at -offset and continuing for length bytes. -advice is one of POSIX_FADV_NORMAL, POSIX_FADV_SEQUENTIAL, -POSIX_FADV_RANDOM, POSIX_FADV_NOREUSE, POSIX_FADV_WILLNEED, or -POSIX_FADV_DONTNEED. -[clinic start generated code]*/ - -static PyObject * -os_posix_fadvise_impl(PyObject *module, int fd, Py_off_t offset, - Py_off_t length, int advice) -/*[clinic end generated code: output=412ef4aa70c98642 input=0fbe554edc2f04b5]*/ -{ - int result; - int async_err = 0; - - do { - Py_BEGIN_ALLOW_THREADS - result = posix_fadvise(fd, offset, length, advice); - Py_END_ALLOW_THREADS - } while (result == EINTR && !(async_err = PyErr_CheckSignals())); - - if (result == 0) - Py_RETURN_NONE; - - if (async_err) - return NULL; - - errno = result; - return posix_error(); -} -#endif /* HAVE_POSIX_FADVISE && !POSIX_FADVISE_AIX_BUG */ - - -#ifdef MS_WINDOWS -static PyObject* -win32_putenv(PyObject *name, PyObject *value) -{ - /* Search from index 1 because on Windows starting '=' is allowed for - defining hidden environment variables. */ - if (PyUnicode_GET_LENGTH(name) == 0 || - PyUnicode_FindChar(name, '=', 1, PyUnicode_GET_LENGTH(name), 1) != -1) - { - PyErr_SetString(PyExc_ValueError, "illegal environment variable name"); - return NULL; - } - PyObject *unicode; - if (value != NULL) { - unicode = PyUnicode_FromFormat("%U=%U", name, value); - } - else { - unicode = PyUnicode_FromFormat("%U=", name); - } - if (unicode == NULL) { - return NULL; - } - - Py_ssize_t size; - wchar_t *env = PyUnicode_AsWideCharString(unicode, &size); - Py_DECREF(unicode); - - if (env == NULL) { - return NULL; - } - if (size > _MAX_ENV) { - PyErr_Format(PyExc_ValueError, - "the environment variable is longer than %u characters", - _MAX_ENV); - PyMem_Free(env); - return NULL; - } - if (wcslen(env) != (size_t)size) { - PyErr_SetString(PyExc_ValueError, - "embedded null character"); - PyMem_Free(env); - return NULL; - } - - /* _wputenv() and SetEnvironmentVariableW() update the environment in the - Process Environment Block (PEB). _wputenv() also updates CRT 'environ' - and '_wenviron' variables, whereas SetEnvironmentVariableW() does not. - - Prefer _wputenv() to be compatible with C libraries using CRT - variables and CRT functions using these variables (ex: getenv()). */ - int err = _wputenv(env); - - if (err) { - posix_error(); - PyMem_Free(env); - return NULL; - } - PyMem_Free(env); - - Py_RETURN_NONE; -} -#endif - - -#ifdef MS_WINDOWS -/*[clinic input] -os.putenv - - name: unicode - value: unicode - / - -Change or add an environment variable. -[clinic start generated code]*/ - -static PyObject * -os_putenv_impl(PyObject *module, PyObject *name, PyObject *value) -/*[clinic end generated code: output=d29a567d6b2327d2 input=ba586581c2e6105f]*/ -{ - if (PySys_Audit("os.putenv", "OO", name, value) < 0) { - return NULL; - } - return win32_putenv(name, value); -} -#else -/*[clinic input] -os.putenv - - name: FSConverter - value: FSConverter - / - -Change or add an environment variable. -[clinic start generated code]*/ - -static PyObject * -os_putenv_impl(PyObject *module, PyObject *name, PyObject *value) -/*[clinic end generated code: output=d29a567d6b2327d2 input=a97bc6152f688d31]*/ -{ - const char *name_string = PyBytes_AS_STRING(name); - const char *value_string = PyBytes_AS_STRING(value); - - if (strchr(name_string, '=') != NULL) { - PyErr_SetString(PyExc_ValueError, "illegal environment variable name"); - return NULL; - } - - if (PySys_Audit("os.putenv", "OO", name, value) < 0) { - return NULL; - } - - if (setenv(name_string, value_string, 1)) { - return posix_error(); - } - Py_RETURN_NONE; -} -#endif /* !defined(MS_WINDOWS) */ - - -#ifdef MS_WINDOWS -/*[clinic input] -os.unsetenv - name: unicode - / - -Delete an environment variable. -[clinic start generated code]*/ - -static PyObject * -os_unsetenv_impl(PyObject *module, PyObject *name) -/*[clinic end generated code: output=54c4137ab1834f02 input=4d6a1747cc526d2f]*/ -{ - if (PySys_Audit("os.unsetenv", "(O)", name) < 0) { - return NULL; - } - return win32_putenv(name, NULL); -} -#else -/*[clinic input] -os.unsetenv - name: FSConverter - / - -Delete an environment variable. -[clinic start generated code]*/ - -static PyObject * -os_unsetenv_impl(PyObject *module, PyObject *name) -/*[clinic end generated code: output=54c4137ab1834f02 input=2bb5288a599c7107]*/ -{ - if (PySys_Audit("os.unsetenv", "(O)", name) < 0) { - return NULL; - } -#ifdef HAVE_BROKEN_UNSETENV - unsetenv(PyBytes_AS_STRING(name)); -#else - int err = unsetenv(PyBytes_AS_STRING(name)); - if (err) { - return posix_error(); - } -#endif - - Py_RETURN_NONE; -} -#endif /* !MS_WINDOWS */ - - -/*[clinic input] -os.strerror - - code: int - / - -Translate an error code to a message string. -[clinic start generated code]*/ - -static PyObject * -os_strerror_impl(PyObject *module, int code) -/*[clinic end generated code: output=baebf09fa02a78f2 input=75a8673d97915a91]*/ -{ - char *message = strerror(code); - if (message == NULL) { - PyErr_SetString(PyExc_ValueError, - "strerror() argument out of range"); - return NULL; - } - return PyUnicode_DecodeLocale(message, "surrogateescape"); -} - - -#ifdef HAVE_SYS_WAIT_H -#ifdef WCOREDUMP -/*[clinic input] -os.WCOREDUMP -> bool - - status: int - / - -Return True if the process returning status was dumped to a core file. -[clinic start generated code]*/ - -static int -os_WCOREDUMP_impl(PyObject *module, int status) -/*[clinic end generated code: output=1a584b147b16bd18 input=8b05e7ab38528d04]*/ -{ - WAIT_TYPE wait_status; - WAIT_STATUS_INT(wait_status) = status; - return WCOREDUMP(wait_status); -} -#endif /* WCOREDUMP */ - - -#ifdef WIFCONTINUED -/*[clinic input] -os.WIFCONTINUED -> bool - - status: int - -Return True if a particular process was continued from a job control stop. - -Return True if the process returning status was continued from a -job control stop. -[clinic start generated code]*/ - -static int -os_WIFCONTINUED_impl(PyObject *module, int status) -/*[clinic end generated code: output=1e35295d844364bd input=e777e7d38eb25bd9]*/ -{ - WAIT_TYPE wait_status; - WAIT_STATUS_INT(wait_status) = status; - return WIFCONTINUED(wait_status); -} -#endif /* WIFCONTINUED */ - - -#ifdef WIFSTOPPED -/*[clinic input] -os.WIFSTOPPED -> bool - - status: int - -Return True if the process returning status was stopped. -[clinic start generated code]*/ - -static int -os_WIFSTOPPED_impl(PyObject *module, int status) -/*[clinic end generated code: output=fdb57122a5c9b4cb input=043cb7f1289ef904]*/ -{ - WAIT_TYPE wait_status; - WAIT_STATUS_INT(wait_status) = status; - return WIFSTOPPED(wait_status); -} -#endif /* WIFSTOPPED */ - - -#ifdef WIFSIGNALED -/*[clinic input] -os.WIFSIGNALED -> bool - - status: int - -Return True if the process returning status was terminated by a signal. -[clinic start generated code]*/ - -static int -os_WIFSIGNALED_impl(PyObject *module, int status) -/*[clinic end generated code: output=d1dde4dcc819a5f5 input=d55ba7cc9ce5dc43]*/ -{ - WAIT_TYPE wait_status; - WAIT_STATUS_INT(wait_status) = status; - return WIFSIGNALED(wait_status); -} -#endif /* WIFSIGNALED */ - - -#ifdef WIFEXITED -/*[clinic input] -os.WIFEXITED -> bool - - status: int - -Return True if the process returning status exited via the exit() system call. -[clinic start generated code]*/ - -static int -os_WIFEXITED_impl(PyObject *module, int status) -/*[clinic end generated code: output=01c09d6ebfeea397 input=d63775a6791586c0]*/ -{ - WAIT_TYPE wait_status; - WAIT_STATUS_INT(wait_status) = status; - return WIFEXITED(wait_status); -} -#endif /* WIFEXITED */ - - -#ifdef WEXITSTATUS -/*[clinic input] -os.WEXITSTATUS -> int - - status: int - -Return the process return code from status. -[clinic start generated code]*/ - -static int -os_WEXITSTATUS_impl(PyObject *module, int status) -/*[clinic end generated code: output=6e3efbba11f6488d input=e1fb4944e377585b]*/ -{ - WAIT_TYPE wait_status; - WAIT_STATUS_INT(wait_status) = status; - return WEXITSTATUS(wait_status); -} -#endif /* WEXITSTATUS */ - - -#ifdef WTERMSIG -/*[clinic input] -os.WTERMSIG -> int - - status: int - -Return the signal that terminated the process that provided the status value. -[clinic start generated code]*/ - -static int -os_WTERMSIG_impl(PyObject *module, int status) -/*[clinic end generated code: output=172f7dfc8dcfc3ad input=727fd7f84ec3f243]*/ -{ - WAIT_TYPE wait_status; - WAIT_STATUS_INT(wait_status) = status; - return WTERMSIG(wait_status); -} -#endif /* WTERMSIG */ - - -#ifdef WSTOPSIG -/*[clinic input] -os.WSTOPSIG -> int - - status: int - -Return the signal that stopped the process that provided the status value. -[clinic start generated code]*/ - -static int -os_WSTOPSIG_impl(PyObject *module, int status) -/*[clinic end generated code: output=0ab7586396f5d82b input=46ebf1d1b293c5c1]*/ -{ - WAIT_TYPE wait_status; - WAIT_STATUS_INT(wait_status) = status; - return WSTOPSIG(wait_status); -} -#endif /* WSTOPSIG */ -#endif /* HAVE_SYS_WAIT_H */ - - -#if defined(HAVE_FSTATVFS) && defined(HAVE_SYS_STATVFS_H) -#ifdef _SCO_DS -/* SCO OpenServer 5.0 and later requires _SVID3 before it reveals the - needed definitions in sys/statvfs.h */ -#define _SVID3 -#endif -#include - -#ifdef __APPLE__ -/* On macOS struct statvfs uses 32-bit integers for block counts, - * resulting in overflow when filesystems are larger than 4TB. Therefore - * os.statvfs is implemented in terms of statfs(2). - */ - -static PyObject* -_pystatvfs_fromstructstatfs(PyObject *module, struct statfs st) { - PyObject *StatVFSResultType = get_posix_state(module)->StatVFSResultType; - PyObject *v = PyStructSequence_New((PyTypeObject *)StatVFSResultType); - if (v == NULL) { - return NULL; - } - - long flags = 0; - if (st.f_flags & MNT_RDONLY) { - flags |= ST_RDONLY; - } - if (st.f_flags & MNT_NOSUID) { - flags |= ST_NOSUID; - } - - _Static_assert(sizeof(st.f_blocks) == sizeof(long long), "assuming large file"); - -#define SET_ITEM(SEQ, INDEX, EXPR) \ - do { \ - PyObject *obj = (EXPR); \ - if (obj == NULL) { \ - Py_DECREF((SEQ)); \ - return NULL; \ - } \ - PyStructSequence_SET_ITEM((SEQ), (INDEX), obj); \ - } while (0) - - SET_ITEM(v, 0, PyLong_FromLong((long) st.f_iosize)); - SET_ITEM(v, 1, PyLong_FromLong((long) st.f_bsize)); - SET_ITEM(v, 2, PyLong_FromLongLong((long long) st.f_blocks)); - SET_ITEM(v, 3, PyLong_FromLongLong((long long) st.f_bfree)); - SET_ITEM(v, 4, PyLong_FromLongLong((long long) st.f_bavail)); - SET_ITEM(v, 5, PyLong_FromLongLong((long long) st.f_files)); - SET_ITEM(v, 6, PyLong_FromLongLong((long long) st.f_ffree)); - SET_ITEM(v, 7, PyLong_FromLongLong((long long) st.f_ffree)); - SET_ITEM(v, 8, PyLong_FromLong((long) flags)); - - SET_ITEM(v, 9, PyLong_FromLong((long) NAME_MAX)); - SET_ITEM(v, 10, PyLong_FromUnsignedLong(st.f_fsid.val[0])); - -#undef SET_ITEM - - return v; -} - -#else - - - -static PyObject* -_pystatvfs_fromstructstatvfs(PyObject *module, struct statvfs st) { - PyObject *StatVFSResultType = get_posix_state(module)->StatVFSResultType; - PyObject *v = PyStructSequence_New((PyTypeObject *)StatVFSResultType); - if (v == NULL) - return NULL; - - int pos = 0; - -#define SET_RESULT(CALL) \ - do { \ - PyObject *item = (CALL); \ - if (item == NULL) { \ - Py_DECREF(v); \ - return NULL; \ - } \ - PyStructSequence_SET_ITEM(v, pos++, item); \ - } while(0) - -#if !defined(HAVE_LARGEFILE_SUPPORT) - SET_RESULT(PyLong_FromLong((long) st.f_bsize)); - SET_RESULT(PyLong_FromLong((long) st.f_frsize)); - SET_RESULT(PyLong_FromLong((long) st.f_blocks)); - SET_RESULT(PyLong_FromLong((long) st.f_bfree)); - SET_RESULT(PyLong_FromLong((long) st.f_bavail)); - SET_RESULT(PyLong_FromLong((long) st.f_files)); - SET_RESULT(PyLong_FromLong((long) st.f_ffree)); - SET_RESULT(PyLong_FromLong((long) st.f_favail)); - SET_RESULT(PyLong_FromLong((long) st.f_flag)); - SET_RESULT(PyLong_FromLong((long) st.f_namemax)); -#else - SET_RESULT(PyLong_FromLong((long) st.f_bsize)); - SET_RESULT(PyLong_FromLong((long) st.f_frsize)); - SET_RESULT(PyLong_FromLongLong((long long) st.f_blocks)); - SET_RESULT(PyLong_FromLongLong((long long) st.f_bfree)); - SET_RESULT(PyLong_FromLongLong((long long) st.f_bavail)); - SET_RESULT(PyLong_FromLongLong((long long) st.f_files)); - SET_RESULT(PyLong_FromLongLong((long long) st.f_ffree)); - SET_RESULT(PyLong_FromLongLong((long long) st.f_favail)); - SET_RESULT(PyLong_FromLong((long) st.f_flag)); - SET_RESULT(PyLong_FromLong((long) st.f_namemax)); -#endif -/* The _ALL_SOURCE feature test macro defines f_fsid as a structure - * (issue #32390). */ -#if defined(_AIX) && defined(_ALL_SOURCE) - SET_RESULT(PyLong_FromUnsignedLong(st.f_fsid.val[0])); -#else - SET_RESULT(PyLong_FromUnsignedLong(st.f_fsid)); -#endif - -#undef SET_RESULT - - return v; -} - -#endif - - -/*[clinic input] -os.fstatvfs - fd: int - / - -Perform an fstatvfs system call on the given fd. - -Equivalent to statvfs(fd). -[clinic start generated code]*/ - -static PyObject * -os_fstatvfs_impl(PyObject *module, int fd) -/*[clinic end generated code: output=53547cf0cc55e6c5 input=d8122243ac50975e]*/ -{ - int result; - int async_err = 0; -#ifdef __APPLE__ - struct statfs st; - /* On macOS os.fstatvfs is implemented using fstatfs(2) because - * the former uses 32-bit values for block counts. - */ - do { - Py_BEGIN_ALLOW_THREADS - result = fstatfs(fd, &st); - Py_END_ALLOW_THREADS - } while (result != 0 && errno == EINTR && - !(async_err = PyErr_CheckSignals())); - if (result != 0) - return (!async_err) ? posix_error() : NULL; - - return _pystatvfs_fromstructstatfs(module, st); -#else - struct statvfs st; - - do { - Py_BEGIN_ALLOW_THREADS - result = fstatvfs(fd, &st); - Py_END_ALLOW_THREADS - } while (result != 0 && errno == EINTR && - !(async_err = PyErr_CheckSignals())); - if (result != 0) - return (!async_err) ? posix_error() : NULL; - - return _pystatvfs_fromstructstatvfs(module, st); -#endif -} -#endif /* defined(HAVE_FSTATVFS) && defined(HAVE_SYS_STATVFS_H) */ - - -#if defined(HAVE_STATVFS) && defined(HAVE_SYS_STATVFS_H) -#include -/*[clinic input] -os.statvfs - - path: path_t(allow_fd='PATH_HAVE_FSTATVFS') - -Perform a statvfs system call on the given path. - -path may always be specified as a string. -On some platforms, path may also be specified as an open file descriptor. - If this functionality is unavailable, using it raises an exception. -[clinic start generated code]*/ - -static PyObject * -os_statvfs_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=87106dd1beb8556e input=3f5c35791c669bd9]*/ -{ - int result; - -#ifdef __APPLE__ - /* On macOS os.statvfs is implemented using statfs(2)/fstatfs(2) because - * the former uses 32-bit values for block counts. - */ - struct statfs st; - - Py_BEGIN_ALLOW_THREADS - if (path->fd != -1) { - result = fstatfs(path->fd, &st); - } - else - result = statfs(path->narrow, &st); - Py_END_ALLOW_THREADS - - if (result) { - return path_error(path); - } - - return _pystatvfs_fromstructstatfs(module, st); - -#else - struct statvfs st; - - Py_BEGIN_ALLOW_THREADS -#ifdef HAVE_FSTATVFS - if (path->fd != -1) { - result = fstatvfs(path->fd, &st); - } - else -#endif - result = statvfs(path->narrow, &st); - Py_END_ALLOW_THREADS - - if (result) { - return path_error(path); - } - - return _pystatvfs_fromstructstatvfs(module, st); -#endif -} -#endif /* defined(HAVE_STATVFS) && defined(HAVE_SYS_STATVFS_H) */ - - -#ifdef MS_WINDOWS -/*[clinic input] -os._getdiskusage - - path: path_t - -Return disk usage statistics about the given path as a (total, free) tuple. -[clinic start generated code]*/ - -static PyObject * -os__getdiskusage_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=3bd3991f5e5c5dfb input=6af8d1b7781cc042]*/ -{ - BOOL retval; - ULARGE_INTEGER _, total, free; - DWORD err = 0; - - Py_BEGIN_ALLOW_THREADS - retval = GetDiskFreeSpaceExW(path->wide, &_, &total, &free); - Py_END_ALLOW_THREADS - if (retval == 0) { - if (GetLastError() == ERROR_DIRECTORY) { - wchar_t *dir_path = NULL; - - dir_path = PyMem_New(wchar_t, path->length + 1); - if (dir_path == NULL) { - return PyErr_NoMemory(); - } - - wcscpy_s(dir_path, path->length + 1, path->wide); - - if (_dirnameW(dir_path) != -1) { - Py_BEGIN_ALLOW_THREADS - retval = GetDiskFreeSpaceExW(dir_path, &_, &total, &free); - Py_END_ALLOW_THREADS - } - /* Record the last error in case it's modified by PyMem_Free. */ - err = GetLastError(); - PyMem_Free(dir_path); - if (retval) { - goto success; - } - } - return PyErr_SetFromWindowsErr(err); - } - -success: - return Py_BuildValue("(LL)", total.QuadPart, free.QuadPart); -} -#endif /* MS_WINDOWS */ - - -/* This is used for fpathconf(), pathconf(), confstr() and sysconf(). - * It maps strings representing configuration variable names to - * integer values, allowing those functions to be called with the - * magic names instead of polluting the module's namespace with tons of - * rarely-used constants. There are three separate tables that use - * these definitions. - * - * This code is always included, even if none of the interfaces that - * need it are included. The #if hackery needed to avoid it would be - * sufficiently pervasive that it's not worth the loss of readability. - */ -struct constdef { - const char *name; - int value; -}; - -static int -conv_confname(PyObject *arg, int *valuep, struct constdef *table, - size_t tablesize) -{ - if (PyLong_Check(arg)) { - int value = PyLong_AsInt(arg); - if (value == -1 && PyErr_Occurred()) - return 0; - *valuep = value; - return 1; - } - else { - /* look up the value in the table using a binary search */ - size_t lo = 0; - size_t mid; - size_t hi = tablesize; - int cmp; - const char *confname; - if (!PyUnicode_Check(arg)) { - PyErr_SetString(PyExc_TypeError, - "configuration names must be strings or integers"); - return 0; - } - confname = PyUnicode_AsUTF8(arg); - if (confname == NULL) - return 0; - while (lo < hi) { - mid = (lo + hi) / 2; - cmp = strcmp(confname, table[mid].name); - if (cmp < 0) - hi = mid; - else if (cmp > 0) - lo = mid + 1; - else { - *valuep = table[mid].value; - return 1; - } - } - PyErr_SetString(PyExc_ValueError, "unrecognized configuration name"); - return 0; - } -} - - -#if defined(HAVE_FPATHCONF) || defined(HAVE_PATHCONF) -static struct constdef posix_constants_pathconf[] = { -#ifdef _PC_ABI_AIO_XFER_MAX - {"PC_ABI_AIO_XFER_MAX", _PC_ABI_AIO_XFER_MAX}, -#endif -#ifdef _PC_ABI_ASYNC_IO - {"PC_ABI_ASYNC_IO", _PC_ABI_ASYNC_IO}, -#endif -#ifdef _PC_ASYNC_IO - {"PC_ASYNC_IO", _PC_ASYNC_IO}, -#endif -#ifdef _PC_CHOWN_RESTRICTED - {"PC_CHOWN_RESTRICTED", _PC_CHOWN_RESTRICTED}, -#endif -#ifdef _PC_FILESIZEBITS - {"PC_FILESIZEBITS", _PC_FILESIZEBITS}, -#endif -#ifdef _PC_LAST - {"PC_LAST", _PC_LAST}, -#endif -#ifdef _PC_LINK_MAX - {"PC_LINK_MAX", _PC_LINK_MAX}, -#endif -#ifdef _PC_MAX_CANON - {"PC_MAX_CANON", _PC_MAX_CANON}, -#endif -#ifdef _PC_MAX_INPUT - {"PC_MAX_INPUT", _PC_MAX_INPUT}, -#endif -#ifdef _PC_NAME_MAX - {"PC_NAME_MAX", _PC_NAME_MAX}, -#endif -#ifdef _PC_NO_TRUNC - {"PC_NO_TRUNC", _PC_NO_TRUNC}, -#endif -#ifdef _PC_PATH_MAX - {"PC_PATH_MAX", _PC_PATH_MAX}, -#endif -#ifdef _PC_PIPE_BUF - {"PC_PIPE_BUF", _PC_PIPE_BUF}, -#endif -#ifdef _PC_PRIO_IO - {"PC_PRIO_IO", _PC_PRIO_IO}, -#endif -#ifdef _PC_SOCK_MAXBUF - {"PC_SOCK_MAXBUF", _PC_SOCK_MAXBUF}, -#endif -#ifdef _PC_SYNC_IO - {"PC_SYNC_IO", _PC_SYNC_IO}, -#endif -#ifdef _PC_VDISABLE - {"PC_VDISABLE", _PC_VDISABLE}, -#endif -#ifdef _PC_ACL_ENABLED - {"PC_ACL_ENABLED", _PC_ACL_ENABLED}, -#endif -#ifdef _PC_MIN_HOLE_SIZE - {"PC_MIN_HOLE_SIZE", _PC_MIN_HOLE_SIZE}, -#endif -#ifdef _PC_ALLOC_SIZE_MIN - {"PC_ALLOC_SIZE_MIN", _PC_ALLOC_SIZE_MIN}, -#endif -#ifdef _PC_REC_INCR_XFER_SIZE - {"PC_REC_INCR_XFER_SIZE", _PC_REC_INCR_XFER_SIZE}, -#endif -#ifdef _PC_REC_MAX_XFER_SIZE - {"PC_REC_MAX_XFER_SIZE", _PC_REC_MAX_XFER_SIZE}, -#endif -#ifdef _PC_REC_MIN_XFER_SIZE - {"PC_REC_MIN_XFER_SIZE", _PC_REC_MIN_XFER_SIZE}, -#endif -#ifdef _PC_REC_XFER_ALIGN - {"PC_REC_XFER_ALIGN", _PC_REC_XFER_ALIGN}, -#endif -#ifdef _PC_SYMLINK_MAX - {"PC_SYMLINK_MAX", _PC_SYMLINK_MAX}, -#endif -#ifdef _PC_XATTR_ENABLED - {"PC_XATTR_ENABLED", _PC_XATTR_ENABLED}, -#endif -#ifdef _PC_XATTR_EXISTS - {"PC_XATTR_EXISTS", _PC_XATTR_EXISTS}, -#endif -#ifdef _PC_TIMESTAMP_RESOLUTION - {"PC_TIMESTAMP_RESOLUTION", _PC_TIMESTAMP_RESOLUTION}, -#endif -}; - -static int -conv_path_confname(PyObject *arg, int *valuep) -{ - return conv_confname(arg, valuep, posix_constants_pathconf, - sizeof(posix_constants_pathconf) - / sizeof(struct constdef)); -} -#endif - - -#ifdef HAVE_FPATHCONF -/*[clinic input] -os.fpathconf -> long - - fd: fildes - name: path_confname - / - -Return the configuration limit name for the file descriptor fd. - -If there is no limit, return -1. -[clinic start generated code]*/ - -static long -os_fpathconf_impl(PyObject *module, int fd, int name) -/*[clinic end generated code: output=d5b7042425fc3e21 input=5b8d2471cfaae186]*/ -{ - long limit; - - errno = 0; - limit = fpathconf(fd, name); - if (limit == -1 && errno != 0) - posix_error(); - - return limit; -} -#endif /* HAVE_FPATHCONF */ - - -#ifdef HAVE_PATHCONF -/*[clinic input] -os.pathconf -> long - path: path_t(allow_fd='PATH_HAVE_FPATHCONF') - name: path_confname - -Return the configuration limit name for the file or directory path. - -If there is no limit, return -1. -On some platforms, path may also be specified as an open file descriptor. - If this functionality is unavailable, using it raises an exception. -[clinic start generated code]*/ - -static long -os_pathconf_impl(PyObject *module, path_t *path, int name) -/*[clinic end generated code: output=5bedee35b293a089 input=bc3e2a985af27e5e]*/ -{ - long limit; - - errno = 0; -#ifdef HAVE_FPATHCONF - if (path->fd != -1) - limit = fpathconf(path->fd, name); - else -#endif - limit = pathconf(path->narrow, name); - if (limit == -1 && errno != 0) { - if (errno == EINVAL) - /* could be a path or name problem */ - posix_error(); - else - path_error(path); - } - - return limit; -} -#endif /* HAVE_PATHCONF */ - -#ifdef HAVE_CONFSTR -static struct constdef posix_constants_confstr[] = { -#ifdef _CS_ARCHITECTURE - {"CS_ARCHITECTURE", _CS_ARCHITECTURE}, -#endif -#ifdef _CS_GNU_LIBC_VERSION - {"CS_GNU_LIBC_VERSION", _CS_GNU_LIBC_VERSION}, -#endif -#ifdef _CS_GNU_LIBPTHREAD_VERSION - {"CS_GNU_LIBPTHREAD_VERSION", _CS_GNU_LIBPTHREAD_VERSION}, -#endif -#ifdef _CS_HOSTNAME - {"CS_HOSTNAME", _CS_HOSTNAME}, -#endif -#ifdef _CS_HW_PROVIDER - {"CS_HW_PROVIDER", _CS_HW_PROVIDER}, -#endif -#ifdef _CS_HW_SERIAL - {"CS_HW_SERIAL", _CS_HW_SERIAL}, -#endif -#ifdef _CS_INITTAB_NAME - {"CS_INITTAB_NAME", _CS_INITTAB_NAME}, -#endif -#ifdef _CS_LFS64_CFLAGS - {"CS_LFS64_CFLAGS", _CS_LFS64_CFLAGS}, -#endif -#ifdef _CS_LFS64_LDFLAGS - {"CS_LFS64_LDFLAGS", _CS_LFS64_LDFLAGS}, -#endif -#ifdef _CS_LFS64_LIBS - {"CS_LFS64_LIBS", _CS_LFS64_LIBS}, -#endif -#ifdef _CS_LFS64_LINTFLAGS - {"CS_LFS64_LINTFLAGS", _CS_LFS64_LINTFLAGS}, -#endif -#ifdef _CS_LFS_CFLAGS - {"CS_LFS_CFLAGS", _CS_LFS_CFLAGS}, -#endif -#ifdef _CS_LFS_LDFLAGS - {"CS_LFS_LDFLAGS", _CS_LFS_LDFLAGS}, -#endif -#ifdef _CS_LFS_LIBS - {"CS_LFS_LIBS", _CS_LFS_LIBS}, -#endif -#ifdef _CS_LFS_LINTFLAGS - {"CS_LFS_LINTFLAGS", _CS_LFS_LINTFLAGS}, -#endif -#ifdef _CS_MACHINE - {"CS_MACHINE", _CS_MACHINE}, -#endif -#ifdef _CS_PATH - {"CS_PATH", _CS_PATH}, -#endif -#ifdef _CS_RELEASE - {"CS_RELEASE", _CS_RELEASE}, -#endif -#ifdef _CS_SRPC_DOMAIN - {"CS_SRPC_DOMAIN", _CS_SRPC_DOMAIN}, -#endif -#ifdef _CS_SYSNAME - {"CS_SYSNAME", _CS_SYSNAME}, -#endif -#ifdef _CS_VERSION - {"CS_VERSION", _CS_VERSION}, -#endif -#ifdef _CS_XBS5_ILP32_OFF32_CFLAGS - {"CS_XBS5_ILP32_OFF32_CFLAGS", _CS_XBS5_ILP32_OFF32_CFLAGS}, -#endif -#ifdef _CS_XBS5_ILP32_OFF32_LDFLAGS - {"CS_XBS5_ILP32_OFF32_LDFLAGS", _CS_XBS5_ILP32_OFF32_LDFLAGS}, -#endif -#ifdef _CS_XBS5_ILP32_OFF32_LIBS - {"CS_XBS5_ILP32_OFF32_LIBS", _CS_XBS5_ILP32_OFF32_LIBS}, -#endif -#ifdef _CS_XBS5_ILP32_OFF32_LINTFLAGS - {"CS_XBS5_ILP32_OFF32_LINTFLAGS", _CS_XBS5_ILP32_OFF32_LINTFLAGS}, -#endif -#ifdef _CS_XBS5_ILP32_OFFBIG_CFLAGS - {"CS_XBS5_ILP32_OFFBIG_CFLAGS", _CS_XBS5_ILP32_OFFBIG_CFLAGS}, -#endif -#ifdef _CS_XBS5_ILP32_OFFBIG_LDFLAGS - {"CS_XBS5_ILP32_OFFBIG_LDFLAGS", _CS_XBS5_ILP32_OFFBIG_LDFLAGS}, -#endif -#ifdef _CS_XBS5_ILP32_OFFBIG_LIBS - {"CS_XBS5_ILP32_OFFBIG_LIBS", _CS_XBS5_ILP32_OFFBIG_LIBS}, -#endif -#ifdef _CS_XBS5_ILP32_OFFBIG_LINTFLAGS - {"CS_XBS5_ILP32_OFFBIG_LINTFLAGS", _CS_XBS5_ILP32_OFFBIG_LINTFLAGS}, -#endif -#ifdef _CS_XBS5_LP64_OFF64_CFLAGS - {"CS_XBS5_LP64_OFF64_CFLAGS", _CS_XBS5_LP64_OFF64_CFLAGS}, -#endif -#ifdef _CS_XBS5_LP64_OFF64_LDFLAGS - {"CS_XBS5_LP64_OFF64_LDFLAGS", _CS_XBS5_LP64_OFF64_LDFLAGS}, -#endif -#ifdef _CS_XBS5_LP64_OFF64_LIBS - {"CS_XBS5_LP64_OFF64_LIBS", _CS_XBS5_LP64_OFF64_LIBS}, -#endif -#ifdef _CS_XBS5_LP64_OFF64_LINTFLAGS - {"CS_XBS5_LP64_OFF64_LINTFLAGS", _CS_XBS5_LP64_OFF64_LINTFLAGS}, -#endif -#ifdef _CS_XBS5_LPBIG_OFFBIG_CFLAGS - {"CS_XBS5_LPBIG_OFFBIG_CFLAGS", _CS_XBS5_LPBIG_OFFBIG_CFLAGS}, -#endif -#ifdef _CS_XBS5_LPBIG_OFFBIG_LDFLAGS - {"CS_XBS5_LPBIG_OFFBIG_LDFLAGS", _CS_XBS5_LPBIG_OFFBIG_LDFLAGS}, -#endif -#ifdef _CS_XBS5_LPBIG_OFFBIG_LIBS - {"CS_XBS5_LPBIG_OFFBIG_LIBS", _CS_XBS5_LPBIG_OFFBIG_LIBS}, -#endif -#ifdef _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS - {"CS_XBS5_LPBIG_OFFBIG_LINTFLAGS", _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS}, -#endif -#ifdef _MIPS_CS_AVAIL_PROCESSORS - {"MIPS_CS_AVAIL_PROCESSORS", _MIPS_CS_AVAIL_PROCESSORS}, -#endif -#ifdef _MIPS_CS_BASE - {"MIPS_CS_BASE", _MIPS_CS_BASE}, -#endif -#ifdef _MIPS_CS_HOSTID - {"MIPS_CS_HOSTID", _MIPS_CS_HOSTID}, -#endif -#ifdef _MIPS_CS_HW_NAME - {"MIPS_CS_HW_NAME", _MIPS_CS_HW_NAME}, -#endif -#ifdef _MIPS_CS_NUM_PROCESSORS - {"MIPS_CS_NUM_PROCESSORS", _MIPS_CS_NUM_PROCESSORS}, -#endif -#ifdef _MIPS_CS_OSREL_MAJ - {"MIPS_CS_OSREL_MAJ", _MIPS_CS_OSREL_MAJ}, -#endif -#ifdef _MIPS_CS_OSREL_MIN - {"MIPS_CS_OSREL_MIN", _MIPS_CS_OSREL_MIN}, -#endif -#ifdef _MIPS_CS_OSREL_PATCH - {"MIPS_CS_OSREL_PATCH", _MIPS_CS_OSREL_PATCH}, -#endif -#ifdef _MIPS_CS_OS_NAME - {"MIPS_CS_OS_NAME", _MIPS_CS_OS_NAME}, -#endif -#ifdef _MIPS_CS_OS_PROVIDER - {"MIPS_CS_OS_PROVIDER", _MIPS_CS_OS_PROVIDER}, -#endif -#ifdef _MIPS_CS_PROCESSORS - {"MIPS_CS_PROCESSORS", _MIPS_CS_PROCESSORS}, -#endif -#ifdef _MIPS_CS_SERIAL - {"MIPS_CS_SERIAL", _MIPS_CS_SERIAL}, -#endif -#ifdef _MIPS_CS_VENDOR - {"MIPS_CS_VENDOR", _MIPS_CS_VENDOR}, -#endif -}; - -static int -conv_confstr_confname(PyObject *arg, int *valuep) -{ - return conv_confname(arg, valuep, posix_constants_confstr, - sizeof(posix_constants_confstr) - / sizeof(struct constdef)); -} - - -/*[clinic input] -os.confstr - - name: confstr_confname - / - -Return a string-valued system configuration variable. -[clinic start generated code]*/ - -static PyObject * -os_confstr_impl(PyObject *module, int name) -/*[clinic end generated code: output=bfb0b1b1e49b9383 input=18fb4d0567242e65]*/ -{ - PyObject *result = NULL; - char buffer[255]; - size_t len; - - errno = 0; - len = confstr(name, buffer, sizeof(buffer)); - if (len == 0) { - if (errno) { - posix_error(); - return NULL; - } - else { - Py_RETURN_NONE; - } - } - - if (len >= sizeof(buffer)) { - size_t len2; - char *buf = PyMem_Malloc(len); - if (buf == NULL) - return PyErr_NoMemory(); - len2 = confstr(name, buf, len); - assert(len == len2); - result = PyUnicode_DecodeFSDefaultAndSize(buf, len2-1); - PyMem_Free(buf); - } - else - result = PyUnicode_DecodeFSDefaultAndSize(buffer, len-1); - return result; -} -#endif /* HAVE_CONFSTR */ - - -#ifdef HAVE_SYSCONF -static struct constdef posix_constants_sysconf[] = { -#ifdef _SC_2_CHAR_TERM - {"SC_2_CHAR_TERM", _SC_2_CHAR_TERM}, -#endif -#ifdef _SC_2_C_BIND - {"SC_2_C_BIND", _SC_2_C_BIND}, -#endif -#ifdef _SC_2_C_DEV - {"SC_2_C_DEV", _SC_2_C_DEV}, -#endif -#ifdef _SC_2_C_VERSION - {"SC_2_C_VERSION", _SC_2_C_VERSION}, -#endif -#ifdef _SC_2_FORT_DEV - {"SC_2_FORT_DEV", _SC_2_FORT_DEV}, -#endif -#ifdef _SC_2_FORT_RUN - {"SC_2_FORT_RUN", _SC_2_FORT_RUN}, -#endif -#ifdef _SC_2_LOCALEDEF - {"SC_2_LOCALEDEF", _SC_2_LOCALEDEF}, -#endif -#ifdef _SC_2_SW_DEV - {"SC_2_SW_DEV", _SC_2_SW_DEV}, -#endif -#ifdef _SC_2_UPE - {"SC_2_UPE", _SC_2_UPE}, -#endif -#ifdef _SC_2_VERSION - {"SC_2_VERSION", _SC_2_VERSION}, -#endif -#ifdef _SC_ABI_ASYNCHRONOUS_IO - {"SC_ABI_ASYNCHRONOUS_IO", _SC_ABI_ASYNCHRONOUS_IO}, -#endif -#ifdef _SC_ACL - {"SC_ACL", _SC_ACL}, -#endif -#ifdef _SC_AIO_LISTIO_MAX - {"SC_AIO_LISTIO_MAX", _SC_AIO_LISTIO_MAX}, -#endif -#ifdef _SC_AIO_MAX - {"SC_AIO_MAX", _SC_AIO_MAX}, -#endif -#ifdef _SC_AIO_PRIO_DELTA_MAX - {"SC_AIO_PRIO_DELTA_MAX", _SC_AIO_PRIO_DELTA_MAX}, -#endif -#ifdef _SC_ARG_MAX - {"SC_ARG_MAX", _SC_ARG_MAX}, -#endif -#ifdef _SC_ASYNCHRONOUS_IO - {"SC_ASYNCHRONOUS_IO", _SC_ASYNCHRONOUS_IO}, -#endif -#ifdef _SC_ATEXIT_MAX - {"SC_ATEXIT_MAX", _SC_ATEXIT_MAX}, -#endif -#ifdef _SC_AUDIT - {"SC_AUDIT", _SC_AUDIT}, -#endif -#ifdef _SC_AVPHYS_PAGES - {"SC_AVPHYS_PAGES", _SC_AVPHYS_PAGES}, -#endif -#ifdef _SC_BC_BASE_MAX - {"SC_BC_BASE_MAX", _SC_BC_BASE_MAX}, -#endif -#ifdef _SC_BC_DIM_MAX - {"SC_BC_DIM_MAX", _SC_BC_DIM_MAX}, -#endif -#ifdef _SC_BC_SCALE_MAX - {"SC_BC_SCALE_MAX", _SC_BC_SCALE_MAX}, -#endif -#ifdef _SC_BC_STRING_MAX - {"SC_BC_STRING_MAX", _SC_BC_STRING_MAX}, -#endif -#ifdef _SC_CAP - {"SC_CAP", _SC_CAP}, -#endif -#ifdef _SC_CHARCLASS_NAME_MAX - {"SC_CHARCLASS_NAME_MAX", _SC_CHARCLASS_NAME_MAX}, -#endif -#ifdef _SC_CHAR_BIT - {"SC_CHAR_BIT", _SC_CHAR_BIT}, -#endif -#ifdef _SC_CHAR_MAX - {"SC_CHAR_MAX", _SC_CHAR_MAX}, -#endif -#ifdef _SC_CHAR_MIN - {"SC_CHAR_MIN", _SC_CHAR_MIN}, -#endif -#ifdef _SC_CHILD_MAX - {"SC_CHILD_MAX", _SC_CHILD_MAX}, -#endif -#ifdef _SC_CLK_TCK - {"SC_CLK_TCK", _SC_CLK_TCK}, -#endif -#ifdef _SC_COHER_BLKSZ - {"SC_COHER_BLKSZ", _SC_COHER_BLKSZ}, -#endif -#ifdef _SC_COLL_WEIGHTS_MAX - {"SC_COLL_WEIGHTS_MAX", _SC_COLL_WEIGHTS_MAX}, -#endif -#ifdef _SC_DCACHE_ASSOC - {"SC_DCACHE_ASSOC", _SC_DCACHE_ASSOC}, -#endif -#ifdef _SC_DCACHE_BLKSZ - {"SC_DCACHE_BLKSZ", _SC_DCACHE_BLKSZ}, -#endif -#ifdef _SC_DCACHE_LINESZ - {"SC_DCACHE_LINESZ", _SC_DCACHE_LINESZ}, -#endif -#ifdef _SC_DCACHE_SZ - {"SC_DCACHE_SZ", _SC_DCACHE_SZ}, -#endif -#ifdef _SC_DCACHE_TBLKSZ - {"SC_DCACHE_TBLKSZ", _SC_DCACHE_TBLKSZ}, -#endif -#ifdef _SC_DELAYTIMER_MAX - {"SC_DELAYTIMER_MAX", _SC_DELAYTIMER_MAX}, -#endif -#ifdef _SC_EQUIV_CLASS_MAX - {"SC_EQUIV_CLASS_MAX", _SC_EQUIV_CLASS_MAX}, -#endif -#ifdef _SC_EXPR_NEST_MAX - {"SC_EXPR_NEST_MAX", _SC_EXPR_NEST_MAX}, -#endif -#ifdef _SC_FSYNC - {"SC_FSYNC", _SC_FSYNC}, -#endif -#ifdef _SC_GETGR_R_SIZE_MAX - {"SC_GETGR_R_SIZE_MAX", _SC_GETGR_R_SIZE_MAX}, -#endif -#ifdef _SC_GETPW_R_SIZE_MAX - {"SC_GETPW_R_SIZE_MAX", _SC_GETPW_R_SIZE_MAX}, -#endif -#ifdef _SC_ICACHE_ASSOC - {"SC_ICACHE_ASSOC", _SC_ICACHE_ASSOC}, -#endif -#ifdef _SC_ICACHE_BLKSZ - {"SC_ICACHE_BLKSZ", _SC_ICACHE_BLKSZ}, -#endif -#ifdef _SC_ICACHE_LINESZ - {"SC_ICACHE_LINESZ", _SC_ICACHE_LINESZ}, -#endif -#ifdef _SC_ICACHE_SZ - {"SC_ICACHE_SZ", _SC_ICACHE_SZ}, -#endif -#ifdef _SC_INF - {"SC_INF", _SC_INF}, -#endif -#ifdef _SC_INT_MAX - {"SC_INT_MAX", _SC_INT_MAX}, -#endif -#ifdef _SC_INT_MIN - {"SC_INT_MIN", _SC_INT_MIN}, -#endif -#ifdef _SC_IOV_MAX - {"SC_IOV_MAX", _SC_IOV_MAX}, -#endif -#ifdef _SC_IP_SECOPTS - {"SC_IP_SECOPTS", _SC_IP_SECOPTS}, -#endif -#ifdef _SC_JOB_CONTROL - {"SC_JOB_CONTROL", _SC_JOB_CONTROL}, -#endif -#ifdef _SC_KERN_POINTERS - {"SC_KERN_POINTERS", _SC_KERN_POINTERS}, -#endif -#ifdef _SC_KERN_SIM - {"SC_KERN_SIM", _SC_KERN_SIM}, -#endif -#ifdef _SC_LINE_MAX - {"SC_LINE_MAX", _SC_LINE_MAX}, -#endif -#ifdef _SC_LOGIN_NAME_MAX - {"SC_LOGIN_NAME_MAX", _SC_LOGIN_NAME_MAX}, -#endif -#ifdef _SC_LOGNAME_MAX - {"SC_LOGNAME_MAX", _SC_LOGNAME_MAX}, -#endif -#ifdef _SC_LONG_BIT - {"SC_LONG_BIT", _SC_LONG_BIT}, -#endif -#ifdef _SC_MAC - {"SC_MAC", _SC_MAC}, -#endif -#ifdef _SC_MAPPED_FILES - {"SC_MAPPED_FILES", _SC_MAPPED_FILES}, -#endif -#ifdef _SC_MAXPID - {"SC_MAXPID", _SC_MAXPID}, -#endif -#ifdef _SC_MB_LEN_MAX - {"SC_MB_LEN_MAX", _SC_MB_LEN_MAX}, -#endif -#ifdef _SC_MEMLOCK - {"SC_MEMLOCK", _SC_MEMLOCK}, -#endif -#ifdef _SC_MEMLOCK_RANGE - {"SC_MEMLOCK_RANGE", _SC_MEMLOCK_RANGE}, -#endif -#ifdef _SC_MEMORY_PROTECTION - {"SC_MEMORY_PROTECTION", _SC_MEMORY_PROTECTION}, -#endif -#ifdef _SC_MESSAGE_PASSING - {"SC_MESSAGE_PASSING", _SC_MESSAGE_PASSING}, -#endif -#ifdef _SC_MMAP_FIXED_ALIGNMENT - {"SC_MMAP_FIXED_ALIGNMENT", _SC_MMAP_FIXED_ALIGNMENT}, -#endif -#ifdef _SC_MQ_OPEN_MAX - {"SC_MQ_OPEN_MAX", _SC_MQ_OPEN_MAX}, -#endif -#ifdef _SC_MQ_PRIO_MAX - {"SC_MQ_PRIO_MAX", _SC_MQ_PRIO_MAX}, -#endif -#ifdef _SC_NACLS_MAX - {"SC_NACLS_MAX", _SC_NACLS_MAX}, -#endif -#ifdef _SC_NGROUPS_MAX - {"SC_NGROUPS_MAX", _SC_NGROUPS_MAX}, -#endif -#ifdef _SC_NL_ARGMAX - {"SC_NL_ARGMAX", _SC_NL_ARGMAX}, -#endif -#ifdef _SC_NL_LANGMAX - {"SC_NL_LANGMAX", _SC_NL_LANGMAX}, -#endif -#ifdef _SC_NL_MSGMAX - {"SC_NL_MSGMAX", _SC_NL_MSGMAX}, -#endif -#ifdef _SC_NL_NMAX - {"SC_NL_NMAX", _SC_NL_NMAX}, -#endif -#ifdef _SC_NL_SETMAX - {"SC_NL_SETMAX", _SC_NL_SETMAX}, -#endif -#ifdef _SC_NL_TEXTMAX - {"SC_NL_TEXTMAX", _SC_NL_TEXTMAX}, -#endif -#ifdef _SC_NPROCESSORS_CONF - {"SC_NPROCESSORS_CONF", _SC_NPROCESSORS_CONF}, -#endif -#ifdef _SC_NPROCESSORS_ONLN - {"SC_NPROCESSORS_ONLN", _SC_NPROCESSORS_ONLN}, -#endif -#ifdef _SC_NPROC_CONF - {"SC_NPROC_CONF", _SC_NPROC_CONF}, -#endif -#ifdef _SC_NPROC_ONLN - {"SC_NPROC_ONLN", _SC_NPROC_ONLN}, -#endif -#ifdef _SC_NZERO - {"SC_NZERO", _SC_NZERO}, -#endif -#ifdef _SC_OPEN_MAX - {"SC_OPEN_MAX", _SC_OPEN_MAX}, -#endif -#ifdef _SC_PAGESIZE - {"SC_PAGESIZE", _SC_PAGESIZE}, -#endif -#ifdef _SC_PAGE_SIZE - {"SC_PAGE_SIZE", _SC_PAGE_SIZE}, -#endif -#ifdef _SC_AIX_REALMEM - {"SC_AIX_REALMEM", _SC_AIX_REALMEM}, -#endif -#ifdef _SC_PASS_MAX - {"SC_PASS_MAX", _SC_PASS_MAX}, -#endif -#ifdef _SC_PHYS_PAGES - {"SC_PHYS_PAGES", _SC_PHYS_PAGES}, -#endif -#ifdef _SC_PII - {"SC_PII", _SC_PII}, -#endif -#ifdef _SC_PII_INTERNET - {"SC_PII_INTERNET", _SC_PII_INTERNET}, -#endif -#ifdef _SC_PII_INTERNET_DGRAM - {"SC_PII_INTERNET_DGRAM", _SC_PII_INTERNET_DGRAM}, -#endif -#ifdef _SC_PII_INTERNET_STREAM - {"SC_PII_INTERNET_STREAM", _SC_PII_INTERNET_STREAM}, -#endif -#ifdef _SC_PII_OSI - {"SC_PII_OSI", _SC_PII_OSI}, -#endif -#ifdef _SC_PII_OSI_CLTS - {"SC_PII_OSI_CLTS", _SC_PII_OSI_CLTS}, -#endif -#ifdef _SC_PII_OSI_COTS - {"SC_PII_OSI_COTS", _SC_PII_OSI_COTS}, -#endif -#ifdef _SC_PII_OSI_M - {"SC_PII_OSI_M", _SC_PII_OSI_M}, -#endif -#ifdef _SC_PII_SOCKET - {"SC_PII_SOCKET", _SC_PII_SOCKET}, -#endif -#ifdef _SC_PII_XTI - {"SC_PII_XTI", _SC_PII_XTI}, -#endif -#ifdef _SC_POLL - {"SC_POLL", _SC_POLL}, -#endif -#ifdef _SC_PRIORITIZED_IO - {"SC_PRIORITIZED_IO", _SC_PRIORITIZED_IO}, -#endif -#ifdef _SC_PRIORITY_SCHEDULING - {"SC_PRIORITY_SCHEDULING", _SC_PRIORITY_SCHEDULING}, -#endif -#ifdef _SC_REALTIME_SIGNALS - {"SC_REALTIME_SIGNALS", _SC_REALTIME_SIGNALS}, -#endif -#ifdef _SC_RE_DUP_MAX - {"SC_RE_DUP_MAX", _SC_RE_DUP_MAX}, -#endif -#ifdef _SC_RTSIG_MAX - {"SC_RTSIG_MAX", _SC_RTSIG_MAX}, -#endif -#ifdef _SC_SAVED_IDS - {"SC_SAVED_IDS", _SC_SAVED_IDS}, -#endif -#ifdef _SC_SCHAR_MAX - {"SC_SCHAR_MAX", _SC_SCHAR_MAX}, -#endif -#ifdef _SC_SCHAR_MIN - {"SC_SCHAR_MIN", _SC_SCHAR_MIN}, -#endif -#ifdef _SC_SELECT - {"SC_SELECT", _SC_SELECT}, -#endif -#ifdef _SC_SEMAPHORES - {"SC_SEMAPHORES", _SC_SEMAPHORES}, -#endif -#ifdef _SC_SEM_NSEMS_MAX - {"SC_SEM_NSEMS_MAX", _SC_SEM_NSEMS_MAX}, -#endif -#ifdef _SC_SEM_VALUE_MAX - {"SC_SEM_VALUE_MAX", _SC_SEM_VALUE_MAX}, -#endif -#ifdef _SC_SHARED_MEMORY_OBJECTS - {"SC_SHARED_MEMORY_OBJECTS", _SC_SHARED_MEMORY_OBJECTS}, -#endif -#ifdef _SC_SHRT_MAX - {"SC_SHRT_MAX", _SC_SHRT_MAX}, -#endif -#ifdef _SC_SHRT_MIN - {"SC_SHRT_MIN", _SC_SHRT_MIN}, -#endif -#ifdef _SC_SIGQUEUE_MAX - {"SC_SIGQUEUE_MAX", _SC_SIGQUEUE_MAX}, -#endif -#ifdef _SC_SIGRT_MAX - {"SC_SIGRT_MAX", _SC_SIGRT_MAX}, -#endif -#ifdef _SC_SIGRT_MIN - {"SC_SIGRT_MIN", _SC_SIGRT_MIN}, -#endif -#ifdef _SC_SOFTPOWER - {"SC_SOFTPOWER", _SC_SOFTPOWER}, -#endif -#ifdef _SC_SPLIT_CACHE - {"SC_SPLIT_CACHE", _SC_SPLIT_CACHE}, -#endif -#ifdef _SC_SSIZE_MAX - {"SC_SSIZE_MAX", _SC_SSIZE_MAX}, -#endif -#ifdef _SC_STACK_PROT - {"SC_STACK_PROT", _SC_STACK_PROT}, -#endif -#ifdef _SC_STREAM_MAX - {"SC_STREAM_MAX", _SC_STREAM_MAX}, -#endif -#ifdef _SC_SYNCHRONIZED_IO - {"SC_SYNCHRONIZED_IO", _SC_SYNCHRONIZED_IO}, -#endif -#ifdef _SC_THREADS - {"SC_THREADS", _SC_THREADS}, -#endif -#ifdef _SC_THREAD_ATTR_STACKADDR - {"SC_THREAD_ATTR_STACKADDR", _SC_THREAD_ATTR_STACKADDR}, -#endif -#ifdef _SC_THREAD_ATTR_STACKSIZE - {"SC_THREAD_ATTR_STACKSIZE", _SC_THREAD_ATTR_STACKSIZE}, -#endif -#ifdef _SC_THREAD_DESTRUCTOR_ITERATIONS - {"SC_THREAD_DESTRUCTOR_ITERATIONS", _SC_THREAD_DESTRUCTOR_ITERATIONS}, -#endif -#ifdef _SC_THREAD_KEYS_MAX - {"SC_THREAD_KEYS_MAX", _SC_THREAD_KEYS_MAX}, -#endif -#ifdef _SC_THREAD_PRIORITY_SCHEDULING - {"SC_THREAD_PRIORITY_SCHEDULING", _SC_THREAD_PRIORITY_SCHEDULING}, -#endif -#ifdef _SC_THREAD_PRIO_INHERIT - {"SC_THREAD_PRIO_INHERIT", _SC_THREAD_PRIO_INHERIT}, -#endif -#ifdef _SC_THREAD_PRIO_PROTECT - {"SC_THREAD_PRIO_PROTECT", _SC_THREAD_PRIO_PROTECT}, -#endif -#ifdef _SC_THREAD_PROCESS_SHARED - {"SC_THREAD_PROCESS_SHARED", _SC_THREAD_PROCESS_SHARED}, -#endif -#ifdef _SC_THREAD_SAFE_FUNCTIONS - {"SC_THREAD_SAFE_FUNCTIONS", _SC_THREAD_SAFE_FUNCTIONS}, -#endif -#ifdef _SC_THREAD_STACK_MIN - {"SC_THREAD_STACK_MIN", _SC_THREAD_STACK_MIN}, -#endif -#ifdef _SC_THREAD_THREADS_MAX - {"SC_THREAD_THREADS_MAX", _SC_THREAD_THREADS_MAX}, -#endif -#ifdef _SC_TIMERS - {"SC_TIMERS", _SC_TIMERS}, -#endif -#ifdef _SC_TIMER_MAX - {"SC_TIMER_MAX", _SC_TIMER_MAX}, -#endif -#ifdef _SC_TTY_NAME_MAX - {"SC_TTY_NAME_MAX", _SC_TTY_NAME_MAX}, -#endif -#ifdef _SC_TZNAME_MAX - {"SC_TZNAME_MAX", _SC_TZNAME_MAX}, -#endif -#ifdef _SC_T_IOV_MAX - {"SC_T_IOV_MAX", _SC_T_IOV_MAX}, -#endif -#ifdef _SC_UCHAR_MAX - {"SC_UCHAR_MAX", _SC_UCHAR_MAX}, -#endif -#ifdef _SC_UINT_MAX - {"SC_UINT_MAX", _SC_UINT_MAX}, -#endif -#ifdef _SC_UIO_MAXIOV - {"SC_UIO_MAXIOV", _SC_UIO_MAXIOV}, -#endif -#ifdef _SC_ULONG_MAX - {"SC_ULONG_MAX", _SC_ULONG_MAX}, -#endif -#ifdef _SC_USHRT_MAX - {"SC_USHRT_MAX", _SC_USHRT_MAX}, -#endif -#ifdef _SC_VERSION - {"SC_VERSION", _SC_VERSION}, -#endif -#ifdef _SC_WORD_BIT - {"SC_WORD_BIT", _SC_WORD_BIT}, -#endif -#ifdef _SC_XBS5_ILP32_OFF32 - {"SC_XBS5_ILP32_OFF32", _SC_XBS5_ILP32_OFF32}, -#endif -#ifdef _SC_XBS5_ILP32_OFFBIG - {"SC_XBS5_ILP32_OFFBIG", _SC_XBS5_ILP32_OFFBIG}, -#endif -#ifdef _SC_XBS5_LP64_OFF64 - {"SC_XBS5_LP64_OFF64", _SC_XBS5_LP64_OFF64}, -#endif -#ifdef _SC_XBS5_LPBIG_OFFBIG - {"SC_XBS5_LPBIG_OFFBIG", _SC_XBS5_LPBIG_OFFBIG}, -#endif -#ifdef _SC_XOPEN_CRYPT - {"SC_XOPEN_CRYPT", _SC_XOPEN_CRYPT}, -#endif -#ifdef _SC_XOPEN_ENH_I18N - {"SC_XOPEN_ENH_I18N", _SC_XOPEN_ENH_I18N}, -#endif -#ifdef _SC_XOPEN_LEGACY - {"SC_XOPEN_LEGACY", _SC_XOPEN_LEGACY}, -#endif -#ifdef _SC_XOPEN_REALTIME - {"SC_XOPEN_REALTIME", _SC_XOPEN_REALTIME}, -#endif -#ifdef _SC_XOPEN_REALTIME_THREADS - {"SC_XOPEN_REALTIME_THREADS", _SC_XOPEN_REALTIME_THREADS}, -#endif -#ifdef _SC_XOPEN_SHM - {"SC_XOPEN_SHM", _SC_XOPEN_SHM}, -#endif -#ifdef _SC_XOPEN_UNIX - {"SC_XOPEN_UNIX", _SC_XOPEN_UNIX}, -#endif -#ifdef _SC_XOPEN_VERSION - {"SC_XOPEN_VERSION", _SC_XOPEN_VERSION}, -#endif -#ifdef _SC_XOPEN_XCU_VERSION - {"SC_XOPEN_XCU_VERSION", _SC_XOPEN_XCU_VERSION}, -#endif -#ifdef _SC_XOPEN_XPG2 - {"SC_XOPEN_XPG2", _SC_XOPEN_XPG2}, -#endif -#ifdef _SC_XOPEN_XPG3 - {"SC_XOPEN_XPG3", _SC_XOPEN_XPG3}, -#endif -#ifdef _SC_XOPEN_XPG4 - {"SC_XOPEN_XPG4", _SC_XOPEN_XPG4}, -#endif -#ifdef _SC_MINSIGSTKSZ - {"SC_MINSIGSTKSZ", _SC_MINSIGSTKSZ}, -#endif -}; - -static int -conv_sysconf_confname(PyObject *arg, int *valuep) -{ - return conv_confname(arg, valuep, posix_constants_sysconf, - sizeof(posix_constants_sysconf) - / sizeof(struct constdef)); -} - - -/*[clinic input] -os.sysconf -> long - name: sysconf_confname - / - -Return an integer-valued system configuration variable. -[clinic start generated code]*/ - -static long -os_sysconf_impl(PyObject *module, int name) -/*[clinic end generated code: output=3662f945fc0cc756 input=279e3430a33f29e4]*/ -{ - long value; - - errno = 0; - value = sysconf(name); - if (value == -1 && errno != 0) - posix_error(); - return value; -} -#endif /* HAVE_SYSCONF */ - - -/* This code is used to ensure that the tables of configuration value names - * are in sorted order as required by conv_confname(), and also to build - * the exported dictionaries that are used to publish information about the - * names available on the host platform. - * - * Sorting the table at runtime ensures that the table is properly ordered - * when used, even for platforms we're not able to test on. It also makes - * it easier to add additional entries to the tables. - */ - -static int -cmp_constdefs(const void *v1, const void *v2) -{ - const struct constdef *c1 = - (const struct constdef *) v1; - const struct constdef *c2 = - (const struct constdef *) v2; - - return strcmp(c1->name, c2->name); -} - -static int -setup_confname_table(struct constdef *table, size_t tablesize, - const char *tablename, PyObject *module) -{ - PyObject *d = NULL; - size_t i; - - qsort(table, tablesize, sizeof(struct constdef), cmp_constdefs); - d = PyDict_New(); - if (d == NULL) - return -1; - - for (i=0; i < tablesize; ++i) { - PyObject *o = PyLong_FromLong(table[i].value); - if (o == NULL || PyDict_SetItemString(d, table[i].name, o) == -1) { - Py_XDECREF(o); - Py_DECREF(d); - return -1; - } - Py_DECREF(o); - } - return PyModule_Add(module, tablename, d); -} - -/* Return -1 on failure, 0 on success. */ -static int -setup_confname_tables(PyObject *module) -{ -#if defined(HAVE_FPATHCONF) || defined(HAVE_PATHCONF) - if (setup_confname_table(posix_constants_pathconf, - sizeof(posix_constants_pathconf) - / sizeof(struct constdef), - "pathconf_names", module)) - return -1; -#endif -#ifdef HAVE_CONFSTR - if (setup_confname_table(posix_constants_confstr, - sizeof(posix_constants_confstr) - / sizeof(struct constdef), - "confstr_names", module)) - return -1; -#endif -#ifdef HAVE_SYSCONF - if (setup_confname_table(posix_constants_sysconf, - sizeof(posix_constants_sysconf) - / sizeof(struct constdef), - "sysconf_names", module)) - return -1; -#endif - return 0; -} - - -/*[clinic input] -os.abort - -Abort the interpreter immediately. - -This function 'dumps core' or otherwise fails in the hardest way possible -on the hosting operating system. This function never returns. -[clinic start generated code]*/ - -static PyObject * -os_abort_impl(PyObject *module) -/*[clinic end generated code: output=dcf52586dad2467c input=cf2c7d98bc504047]*/ -{ - abort(); - /*NOTREACHED*/ -#ifndef __clang__ - /* Issue #28152: abort() is declared with __attribute__((__noreturn__)). - GCC emits a warning without "return NULL;" (compiler bug?), but Clang - is smarter and emits a warning on the return. */ - Py_FatalError("abort() called from Python code didn't abort!"); - return NULL; -#endif -} - -#ifdef MS_WINDOWS -/* Grab ShellExecute dynamically from shell32 */ -static int has_ShellExecute = -1; -static HINSTANCE (CALLBACK *Py_ShellExecuteW)(HWND, LPCWSTR, LPCWSTR, LPCWSTR, - LPCWSTR, INT); -static int -check_ShellExecute(void) -{ - HINSTANCE hShell32; - - /* only recheck */ - if (-1 == has_ShellExecute) { - Py_BEGIN_ALLOW_THREADS - /* Security note: this call is not vulnerable to "DLL hijacking". - SHELL32 is part of "KnownDLLs" and so Windows always load - the system SHELL32.DLL, even if there is another SHELL32.DLL - in the DLL search path. */ - hShell32 = LoadLibraryW(L"SHELL32"); - if (hShell32) { - *(FARPROC*)&Py_ShellExecuteW = GetProcAddress(hShell32, - "ShellExecuteW"); - has_ShellExecute = Py_ShellExecuteW != NULL; - } else { - has_ShellExecute = 0; - } - Py_END_ALLOW_THREADS - } - return has_ShellExecute; -} - - -/*[clinic input] -os.startfile - filepath: path_t - operation: Py_UNICODE = NULL - arguments: Py_UNICODE = NULL - cwd: path_t(nullable=True) = None - show_cmd: int = 1 - -Start a file with its associated application. - -When "operation" is not specified or "open", this acts like -double-clicking the file in Explorer, or giving the file name as an -argument to the DOS "start" command: the file is opened with whatever -application (if any) its extension is associated. -When another "operation" is given, it specifies what should be done with -the file. A typical operation is "print". - -"arguments" is passed to the application, but should be omitted if the -file is a document. - -"cwd" is the working directory for the operation. If "filepath" is -relative, it will be resolved against this directory. This argument -should usually be an absolute path. - -"show_cmd" can be used to override the recommended visibility option. -See the Windows ShellExecute documentation for values. - -startfile returns as soon as the associated application is launched. -There is no option to wait for the application to close, and no way -to retrieve the application's exit status. - -The filepath is relative to the current directory. If you want to use -an absolute path, make sure the first character is not a slash ("/"); -the underlying Win32 ShellExecute function doesn't work if it is. -[clinic start generated code]*/ - -static PyObject * -os_startfile_impl(PyObject *module, path_t *filepath, - const wchar_t *operation, const wchar_t *arguments, - path_t *cwd, int show_cmd) -/*[clinic end generated code: output=1c6f2f3340e31ffa input=8248997b80669622]*/ -{ - HINSTANCE rc; - - if(!check_ShellExecute()) { - /* If the OS doesn't have ShellExecute, return a - NotImplementedError. */ - return PyErr_Format(PyExc_NotImplementedError, - "startfile not available on this platform"); - } - - if (PySys_Audit("os.startfile", "Ou", filepath->object, operation) < 0) { - return NULL; - } - if (PySys_Audit("os.startfile/2", "OuuOi", filepath->object, operation, - arguments, cwd->object ? cwd->object : Py_None, - show_cmd) < 0) { - return NULL; - } - - Py_BEGIN_ALLOW_THREADS - rc = Py_ShellExecuteW((HWND)0, operation, filepath->wide, - arguments, cwd->wide, show_cmd); - Py_END_ALLOW_THREADS - - if (rc <= (HINSTANCE)32) { - win32_error_object("startfile", filepath->object); - return NULL; - } - Py_RETURN_NONE; -} -#endif /* MS_WINDOWS */ - - -#ifdef HAVE_GETLOADAVG -/*[clinic input] -os.getloadavg - -Return average recent system load information. - -Return the number of processes in the system run queue averaged over -the last 1, 5, and 15 minutes as a tuple of three floats. -Raises OSError if the load average was unobtainable. -[clinic start generated code]*/ - -static PyObject * -os_getloadavg_impl(PyObject *module) -/*[clinic end generated code: output=9ad3a11bfb4f4bd2 input=3d6d826b76d8a34e]*/ -{ - double loadavg[3]; - if (getloadavg(loadavg, 3)!=3) { - PyErr_SetString(PyExc_OSError, "Load averages are unobtainable"); - return NULL; - } else - return Py_BuildValue("ddd", loadavg[0], loadavg[1], loadavg[2]); -} -#endif /* HAVE_GETLOADAVG */ - - -/*[clinic input] -os.device_encoding - fd: int - -Return a string describing the encoding of a terminal's file descriptor. - -The file descriptor must be attached to a terminal. -If the device is not a terminal, return None. -[clinic start generated code]*/ - -static PyObject * -os_device_encoding_impl(PyObject *module, int fd) -/*[clinic end generated code: output=e0d294bbab7e8c2b input=9e1d4a42b66df312]*/ -{ - return _Py_device_encoding(fd); -} - - -#ifdef HAVE_SETRESUID -/*[clinic input] -os.setresuid - - ruid: uid_t - euid: uid_t - suid: uid_t - / - -Set the current process's real, effective, and saved user ids. -[clinic start generated code]*/ - -static PyObject * -os_setresuid_impl(PyObject *module, uid_t ruid, uid_t euid, uid_t suid) -/*[clinic end generated code: output=834a641e15373e97 input=9e33cb79a82792f3]*/ -{ - if (setresuid(ruid, euid, suid) < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_SETRESUID */ - - -#ifdef HAVE_SETRESGID -/*[clinic input] -os.setresgid - - rgid: gid_t - egid: gid_t - sgid: gid_t - / - -Set the current process's real, effective, and saved group ids. -[clinic start generated code]*/ - -static PyObject * -os_setresgid_impl(PyObject *module, gid_t rgid, gid_t egid, gid_t sgid) -/*[clinic end generated code: output=6aa402f3d2e514a9 input=33e9e0785ef426b1]*/ -{ - if (setresgid(rgid, egid, sgid) < 0) - return posix_error(); - Py_RETURN_NONE; -} -#endif /* HAVE_SETRESGID */ - - -#ifdef HAVE_GETRESUID -/*[clinic input] -os.getresuid - -Return a tuple of the current process's real, effective, and saved user ids. -[clinic start generated code]*/ - -static PyObject * -os_getresuid_impl(PyObject *module) -/*[clinic end generated code: output=8e0becff5dece5bf input=41ccfa8e1f6517ad]*/ -{ - uid_t ruid, euid, suid; - if (getresuid(&ruid, &euid, &suid) < 0) - return posix_error(); - return Py_BuildValue("(NNN)", _PyLong_FromUid(ruid), - _PyLong_FromUid(euid), - _PyLong_FromUid(suid)); -} -#endif /* HAVE_GETRESUID */ - - -#ifdef HAVE_GETRESGID -/*[clinic input] -os.getresgid - -Return a tuple of the current process's real, effective, and saved group ids. -[clinic start generated code]*/ - -static PyObject * -os_getresgid_impl(PyObject *module) -/*[clinic end generated code: output=2719c4bfcf27fb9f input=517e68db9ca32df6]*/ -{ - gid_t rgid, egid, sgid; - if (getresgid(&rgid, &egid, &sgid) < 0) - return posix_error(); - return Py_BuildValue("(NNN)", _PyLong_FromGid(rgid), - _PyLong_FromGid(egid), - _PyLong_FromGid(sgid)); -} -#endif /* HAVE_GETRESGID */ - - -#ifdef USE_XATTRS -/*[clinic input] -os.getxattr - - path: path_t(allow_fd=True) - attribute: path_t - * - follow_symlinks: bool = True - -Return the value of extended attribute attribute on path. - -path may be either a string, a path-like object, or an open file descriptor. -If follow_symlinks is False, and the last element of the path is a symbolic - link, getxattr will examine the symbolic link itself instead of the file - the link points to. - -[clinic start generated code]*/ - -static PyObject * -os_getxattr_impl(PyObject *module, path_t *path, path_t *attribute, - int follow_symlinks) -/*[clinic end generated code: output=5f2f44200a43cff2 input=025789491708f7eb]*/ -{ - Py_ssize_t i; - PyObject *buffer = NULL; - - if (fd_and_follow_symlinks_invalid("getxattr", path->fd, follow_symlinks)) - return NULL; - - if (PySys_Audit("os.getxattr", "OO", path->object, attribute->object) < 0) { - return NULL; - } - - for (i = 0; ; i++) { - void *ptr; - ssize_t result; - static const Py_ssize_t buffer_sizes[] = {128, XATTR_SIZE_MAX, 0}; - Py_ssize_t buffer_size = buffer_sizes[i]; - if (!buffer_size) { - path_error(path); - return NULL; - } - buffer = PyBytes_FromStringAndSize(NULL, buffer_size); - if (!buffer) - return NULL; - ptr = PyBytes_AS_STRING(buffer); - - Py_BEGIN_ALLOW_THREADS; - if (path->fd >= 0) - result = fgetxattr(path->fd, attribute->narrow, ptr, buffer_size); - else if (follow_symlinks) - result = getxattr(path->narrow, attribute->narrow, ptr, buffer_size); - else - result = lgetxattr(path->narrow, attribute->narrow, ptr, buffer_size); - Py_END_ALLOW_THREADS; - - if (result < 0) { - if (errno == ERANGE) { - Py_DECREF(buffer); - continue; - } - path_error(path); - Py_DECREF(buffer); - return NULL; - } - - if (result != buffer_size) { - /* Can only shrink. */ - _PyBytes_Resize(&buffer, result); - } - break; - } - - return buffer; -} - - -/*[clinic input] -os.setxattr - - path: path_t(allow_fd=True) - attribute: path_t - value: Py_buffer - flags: int = 0 - * - follow_symlinks: bool = True - -Set extended attribute attribute on path to value. - -path may be either a string, a path-like object, or an open file descriptor. -If follow_symlinks is False, and the last element of the path is a symbolic - link, setxattr will modify the symbolic link itself instead of the file - the link points to. - -[clinic start generated code]*/ - -static PyObject * -os_setxattr_impl(PyObject *module, path_t *path, path_t *attribute, - Py_buffer *value, int flags, int follow_symlinks) -/*[clinic end generated code: output=98b83f63fdde26bb input=c17c0103009042f0]*/ -{ - ssize_t result; - - if (fd_and_follow_symlinks_invalid("setxattr", path->fd, follow_symlinks)) - return NULL; - - if (PySys_Audit("os.setxattr", "OOy#i", path->object, attribute->object, - value->buf, value->len, flags) < 0) { - return NULL; - } - - Py_BEGIN_ALLOW_THREADS; - if (path->fd > -1) - result = fsetxattr(path->fd, attribute->narrow, - value->buf, value->len, flags); - else if (follow_symlinks) - result = setxattr(path->narrow, attribute->narrow, - value->buf, value->len, flags); - else - result = lsetxattr(path->narrow, attribute->narrow, - value->buf, value->len, flags); - Py_END_ALLOW_THREADS; - - if (result) { - path_error(path); - return NULL; - } - - Py_RETURN_NONE; -} - - -/*[clinic input] -os.removexattr - - path: path_t(allow_fd=True) - attribute: path_t - * - follow_symlinks: bool = True - -Remove extended attribute attribute on path. - -path may be either a string, a path-like object, or an open file descriptor. -If follow_symlinks is False, and the last element of the path is a symbolic - link, removexattr will modify the symbolic link itself instead of the file - the link points to. - -[clinic start generated code]*/ - -static PyObject * -os_removexattr_impl(PyObject *module, path_t *path, path_t *attribute, - int follow_symlinks) -/*[clinic end generated code: output=521a51817980cda6 input=3d9a7d36fe2f7c4e]*/ -{ - ssize_t result; - - if (fd_and_follow_symlinks_invalid("removexattr", path->fd, follow_symlinks)) - return NULL; - - if (PySys_Audit("os.removexattr", "OO", path->object, attribute->object) < 0) { - return NULL; - } - - Py_BEGIN_ALLOW_THREADS; - if (path->fd > -1) - result = fremovexattr(path->fd, attribute->narrow); - else if (follow_symlinks) - result = removexattr(path->narrow, attribute->narrow); - else - result = lremovexattr(path->narrow, attribute->narrow); - Py_END_ALLOW_THREADS; - - if (result) { - return path_error(path); - } - - Py_RETURN_NONE; -} - - -/*[clinic input] -os.listxattr - - path: path_t(allow_fd=True, nullable=True) = None - * - follow_symlinks: bool = True - -Return a list of extended attributes on path. - -path may be either None, a string, a path-like object, or an open file descriptor. -if path is None, listxattr will examine the current directory. -If follow_symlinks is False, and the last element of the path is a symbolic - link, listxattr will examine the symbolic link itself instead of the file - the link points to. -[clinic start generated code]*/ - -static PyObject * -os_listxattr_impl(PyObject *module, path_t *path, int follow_symlinks) -/*[clinic end generated code: output=bebdb4e2ad0ce435 input=9826edf9fdb90869]*/ -{ - Py_ssize_t i; - PyObject *result = NULL; - const char *name; - char *buffer = NULL; - - if (fd_and_follow_symlinks_invalid("listxattr", path->fd, follow_symlinks)) - goto exit; - - if (PySys_Audit("os.listxattr", "(O)", - path->object ? path->object : Py_None) < 0) { - return NULL; - } - - name = path->narrow ? path->narrow : "."; - - for (i = 0; ; i++) { - const char *start, *trace, *end; - ssize_t length; - static const Py_ssize_t buffer_sizes[] = { 256, XATTR_LIST_MAX, 0 }; - Py_ssize_t buffer_size = buffer_sizes[i]; - if (!buffer_size) { - /* ERANGE */ - path_error(path); - break; - } - buffer = PyMem_Malloc(buffer_size); - if (!buffer) { - PyErr_NoMemory(); - break; - } - - Py_BEGIN_ALLOW_THREADS; - if (path->fd > -1) - length = flistxattr(path->fd, buffer, buffer_size); - else if (follow_symlinks) - length = listxattr(name, buffer, buffer_size); - else - length = llistxattr(name, buffer, buffer_size); - Py_END_ALLOW_THREADS; - - if (length < 0) { - if (errno == ERANGE) { - PyMem_Free(buffer); - buffer = NULL; - continue; - } - path_error(path); - break; - } - - result = PyList_New(0); - if (!result) { - goto exit; - } - - end = buffer + length; - for (trace = start = buffer; trace != end; trace++) { - if (!*trace) { - int error; - PyObject *attribute = PyUnicode_DecodeFSDefaultAndSize(start, - trace - start); - if (!attribute) { - Py_SETREF(result, NULL); - goto exit; - } - error = PyList_Append(result, attribute); - Py_DECREF(attribute); - if (error) { - Py_SETREF(result, NULL); - goto exit; - } - start = trace + 1; - } - } - break; - } -exit: - if (buffer) - PyMem_Free(buffer); - return result; -} -#endif /* USE_XATTRS */ - - -/*[clinic input] -os.urandom - - size: Py_ssize_t - / - -Return a bytes object containing random bytes suitable for cryptographic use. -[clinic start generated code]*/ - -static PyObject * -os_urandom_impl(PyObject *module, Py_ssize_t size) -/*[clinic end generated code: output=42c5cca9d18068e9 input=4067cdb1b6776c29]*/ -{ - PyObject *bytes; - int result; - - if (size < 0) - return PyErr_Format(PyExc_ValueError, - "negative argument not allowed"); - bytes = PyBytes_FromStringAndSize(NULL, size); - if (bytes == NULL) - return NULL; - - result = _PyOS_URandom(PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes)); - if (result == -1) { - Py_DECREF(bytes); - return NULL; - } - return bytes; -} - -#ifdef HAVE_MEMFD_CREATE -/*[clinic input] -os.memfd_create - - name: FSConverter - flags: unsigned_int(bitwise=True, c_default="MFD_CLOEXEC") = MFD_CLOEXEC - -[clinic start generated code]*/ - -static PyObject * -os_memfd_create_impl(PyObject *module, PyObject *name, unsigned int flags) -/*[clinic end generated code: output=6681ede983bdb9a6 input=a42cfc199bcd56e9]*/ -{ - int fd; - const char *bytes = PyBytes_AS_STRING(name); - Py_BEGIN_ALLOW_THREADS - fd = memfd_create(bytes, flags); - Py_END_ALLOW_THREADS - if (fd == -1) { - return PyErr_SetFromErrno(PyExc_OSError); - } - return PyLong_FromLong(fd); -} -#endif - -#if defined(HAVE_EVENTFD) && defined(EFD_CLOEXEC) -/*[clinic input] -os.eventfd - - initval: unsigned_int - flags: int(c_default="EFD_CLOEXEC") = EFD_CLOEXEC - -Creates and returns an event notification file descriptor. -[clinic start generated code]*/ - -static PyObject * -os_eventfd_impl(PyObject *module, unsigned int initval, int flags) -/*[clinic end generated code: output=ce9c9bbd1446f2de input=66203e3c50c4028b]*/ - -{ - /* initval is limited to uint32_t, internal counter is uint64_t */ - int fd; - Py_BEGIN_ALLOW_THREADS - fd = eventfd(initval, flags); - Py_END_ALLOW_THREADS - if (fd == -1) { - return PyErr_SetFromErrno(PyExc_OSError); - } - return PyLong_FromLong(fd); -} - -/*[clinic input] -os.eventfd_read - - fd: fildes - -Read eventfd value -[clinic start generated code]*/ - -static PyObject * -os_eventfd_read_impl(PyObject *module, int fd) -/*[clinic end generated code: output=8f2c7b59a3521fd1 input=110f8b57fa596afe]*/ -{ - eventfd_t value; - int result; - Py_BEGIN_ALLOW_THREADS - result = eventfd_read(fd, &value); - Py_END_ALLOW_THREADS - if (result == -1) { - return PyErr_SetFromErrno(PyExc_OSError); - } - return PyLong_FromUnsignedLongLong(value); -} - -/*[clinic input] -os.eventfd_write - - fd: fildes - value: unsigned_long_long - -Write eventfd value. -[clinic start generated code]*/ - -static PyObject * -os_eventfd_write_impl(PyObject *module, int fd, unsigned long long value) -/*[clinic end generated code: output=bebd9040bbf987f5 input=156de8555be5a949]*/ -{ - int result; - Py_BEGIN_ALLOW_THREADS - result = eventfd_write(fd, value); - Py_END_ALLOW_THREADS - if (result == -1) { - return PyErr_SetFromErrno(PyExc_OSError); - } - Py_RETURN_NONE; -} -#endif /* HAVE_EVENTFD && EFD_CLOEXEC */ - -/* Terminal size querying */ - -PyDoc_STRVAR(TerminalSize_docstring, - "A tuple of (columns, lines) for holding terminal window size"); - -static PyStructSequence_Field TerminalSize_fields[] = { - {"columns", "width of the terminal window in characters"}, - {"lines", "height of the terminal window in characters"}, - {NULL, NULL} -}; - -static PyStructSequence_Desc TerminalSize_desc = { - "os.terminal_size", - TerminalSize_docstring, - TerminalSize_fields, - 2, -}; - -#if defined(TERMSIZE_USE_CONIO) || defined(TERMSIZE_USE_IOCTL) -/*[clinic input] -os.get_terminal_size - - fd: int(c_default="fileno(stdout)", py_default="") = -1 - / - -Return the size of the terminal window as (columns, lines). - -The optional argument fd (default standard output) specifies -which file descriptor should be queried. - -If the file descriptor is not connected to a terminal, an OSError -is thrown. - -This function will only be defined if an implementation is -available for this system. - -shutil.get_terminal_size is the high-level function which should -normally be used, os.get_terminal_size is the low-level implementation. -[clinic start generated code]*/ - -static PyObject * -os_get_terminal_size_impl(PyObject *module, int fd) -/*[clinic end generated code: output=fbab93acef980508 input=ead5679b82ddb920]*/ -{ - int columns, lines; - PyObject *termsize; - - /* Under some conditions stdout may not be connected and - * fileno(stdout) may point to an invalid file descriptor. For example - * GUI apps don't have valid standard streams by default. - * - * If this happens, and the optional fd argument is not present, - * the ioctl below will fail returning EBADF. This is what we want. - */ - -#ifdef TERMSIZE_USE_IOCTL - { - struct winsize w; - if (ioctl(fd, TIOCGWINSZ, &w)) - return PyErr_SetFromErrno(PyExc_OSError); - columns = w.ws_col; - lines = w.ws_row; - } -#endif /* TERMSIZE_USE_IOCTL */ - -#ifdef TERMSIZE_USE_CONIO - { - HANDLE handle; - CONSOLE_SCREEN_BUFFER_INFO csbi; - handle = _Py_get_osfhandle(fd); - if (handle == INVALID_HANDLE_VALUE) - return NULL; - - if (!GetConsoleScreenBufferInfo(handle, &csbi)) - return PyErr_SetFromWindowsErr(0); - - columns = csbi.srWindow.Right - csbi.srWindow.Left + 1; - lines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; - } -#endif /* TERMSIZE_USE_CONIO */ - - PyObject *TerminalSizeType = get_posix_state(module)->TerminalSizeType; - termsize = PyStructSequence_New((PyTypeObject *)TerminalSizeType); - if (termsize == NULL) - return NULL; - - int pos = 0; - -#define SET_TERMSIZE(CALL) \ - do { \ - PyObject *item = (CALL); \ - if (item == NULL) { \ - Py_DECREF(termsize); \ - return NULL; \ - } \ - PyStructSequence_SET_ITEM(termsize, pos++, item); \ - } while(0) - - SET_TERMSIZE(PyLong_FromLong(columns)); - SET_TERMSIZE(PyLong_FromLong(lines)); -#undef SET_TERMSIZE - - return termsize; -} -#endif /* defined(TERMSIZE_USE_CONIO) || defined(TERMSIZE_USE_IOCTL) */ - -/*[clinic input] -os.cpu_count - -Return the number of logical CPUs in the system. - -Return None if indeterminable. -[clinic start generated code]*/ - -static PyObject * -os_cpu_count_impl(PyObject *module) -/*[clinic end generated code: output=5fc29463c3936a9c input=ba2f6f8980a0e2eb]*/ -{ - const PyConfig *config = _Py_GetConfig(); - if (config->cpu_count > 0) { - return PyLong_FromLong(config->cpu_count); - } - - int ncpu = 0; -#ifdef MS_WINDOWS -# ifdef MS_WINDOWS_DESKTOP - ncpu = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); -# else - ncpu = 0; -# endif - -#elif defined(__hpux) - ncpu = mpctl(MPC_GETNUMSPUS, NULL, NULL); - -#elif defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN) - ncpu = sysconf(_SC_NPROCESSORS_ONLN); - -#elif defined(__VXWORKS__) - ncpu = _Py_popcount32(vxCpuEnabledGet()); - -#elif defined(__DragonFly__) || \ - defined(__OpenBSD__) || \ - defined(__FreeBSD__) || \ - defined(__NetBSD__) || \ - defined(__APPLE__) - ncpu = 0; - size_t len = sizeof(ncpu); - int mib[2] = {CTL_HW, HW_NCPU}; - if (sysctl(mib, 2, &ncpu, &len, NULL, 0) != 0) { - ncpu = 0; - } -#endif - - if (ncpu < 1) { - Py_RETURN_NONE; - } - return PyLong_FromLong(ncpu); -} - - -/*[clinic input] -os.get_inheritable -> bool - - fd: int - / - -Get the close-on-exe flag of the specified file descriptor. -[clinic start generated code]*/ - -static int -os_get_inheritable_impl(PyObject *module, int fd) -/*[clinic end generated code: output=0445e20e149aa5b8 input=89ac008dc9ab6b95]*/ -{ - int return_value; - _Py_BEGIN_SUPPRESS_IPH - return_value = _Py_get_inheritable(fd); - _Py_END_SUPPRESS_IPH - return return_value; -} - - -/*[clinic input] -os.set_inheritable - fd: int - inheritable: int - / - -Set the inheritable flag of the specified file descriptor. -[clinic start generated code]*/ - -static PyObject * -os_set_inheritable_impl(PyObject *module, int fd, int inheritable) -/*[clinic end generated code: output=f1b1918a2f3c38c2 input=9ceaead87a1e2402]*/ -{ - int result; - - _Py_BEGIN_SUPPRESS_IPH - result = _Py_set_inheritable(fd, inheritable, NULL); - _Py_END_SUPPRESS_IPH - if (result < 0) - return NULL; - Py_RETURN_NONE; -} - - -#ifdef MS_WINDOWS -#ifndef HANDLE_FLAG_INHERIT -#define HANDLE_FLAG_INHERIT 0x00000001 -#endif - -/*[clinic input] -os.get_handle_inheritable -> bool - handle: intptr_t - / - -Get the close-on-exe flag of the specified file descriptor. -[clinic start generated code]*/ - -static int -os_get_handle_inheritable_impl(PyObject *module, intptr_t handle) -/*[clinic end generated code: output=36be5afca6ea84d8 input=cfe99f9c05c70ad1]*/ -{ - DWORD flags; - - if (!GetHandleInformation((HANDLE)handle, &flags)) { - PyErr_SetFromWindowsErr(0); - return -1; - } - - return flags & HANDLE_FLAG_INHERIT; -} - - -/*[clinic input] -os.set_handle_inheritable - handle: intptr_t - inheritable: bool - / - -Set the inheritable flag of the specified handle. -[clinic start generated code]*/ - -static PyObject * -os_set_handle_inheritable_impl(PyObject *module, intptr_t handle, - int inheritable) -/*[clinic end generated code: output=021d74fe6c96baa3 input=7a7641390d8364fc]*/ -{ - DWORD flags = inheritable ? HANDLE_FLAG_INHERIT : 0; - if (!SetHandleInformation((HANDLE)handle, HANDLE_FLAG_INHERIT, flags)) { - PyErr_SetFromWindowsErr(0); - return NULL; - } - Py_RETURN_NONE; -} -#endif /* MS_WINDOWS */ - -/*[clinic input] -os.get_blocking -> bool - fd: int - / - -Get the blocking mode of the file descriptor. - -Return False if the O_NONBLOCK flag is set, True if the flag is cleared. -[clinic start generated code]*/ - -static int -os_get_blocking_impl(PyObject *module, int fd) -/*[clinic end generated code: output=336a12ad76a61482 input=f4afb59d51560179]*/ -{ - int blocking; - - _Py_BEGIN_SUPPRESS_IPH - blocking = _Py_get_blocking(fd); - _Py_END_SUPPRESS_IPH - return blocking; -} - -/*[clinic input] -os.set_blocking - fd: int - blocking: bool - / - -Set the blocking mode of the specified file descriptor. - -Set the O_NONBLOCK flag if blocking is False, -clear the O_NONBLOCK flag otherwise. -[clinic start generated code]*/ - -static PyObject * -os_set_blocking_impl(PyObject *module, int fd, int blocking) -/*[clinic end generated code: output=384eb43aa0762a9d input=7e9dfc9b14804dd4]*/ -{ - int result; - - _Py_BEGIN_SUPPRESS_IPH - result = _Py_set_blocking(fd, blocking); - _Py_END_SUPPRESS_IPH - if (result < 0) - return NULL; - Py_RETURN_NONE; -} - - -/*[clinic input] -class os.DirEntry "DirEntry *" "DirEntryType" -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=3c18c7a448247980]*/ - -typedef struct { - PyObject_HEAD - PyObject *name; - PyObject *path; - PyObject *stat; - PyObject *lstat; -#ifdef MS_WINDOWS - struct _Py_stat_struct win32_lstat; - uint64_t win32_file_index; - uint64_t win32_file_index_high; - int got_file_index; -#else /* POSIX */ -#ifdef HAVE_DIRENT_D_TYPE - unsigned char d_type; -#endif - ino_t d_ino; - int dir_fd; -#endif -} DirEntry; - -static void -DirEntry_dealloc(DirEntry *entry) -{ - PyTypeObject *tp = Py_TYPE(entry); - Py_XDECREF(entry->name); - Py_XDECREF(entry->path); - Py_XDECREF(entry->stat); - Py_XDECREF(entry->lstat); - freefunc free_func = PyType_GetSlot(tp, Py_tp_free); - free_func(entry); - Py_DECREF(tp); -} - -/* Forward reference */ -static int -DirEntry_test_mode(PyTypeObject *defining_class, DirEntry *self, - int follow_symlinks, unsigned short mode_bits); - -/*[clinic input] -os.DirEntry.is_symlink -> bool - defining_class: defining_class - / - -Return True if the entry is a symbolic link; cached per entry. -[clinic start generated code]*/ - -static int -os_DirEntry_is_symlink_impl(DirEntry *self, PyTypeObject *defining_class) -/*[clinic end generated code: output=293096d589b6d47c input=e9acc5ee4d511113]*/ -{ -#ifdef MS_WINDOWS - return (self->win32_lstat.st_mode & S_IFMT) == S_IFLNK; -#elif defined(HAVE_DIRENT_D_TYPE) - /* POSIX */ - if (self->d_type != DT_UNKNOWN) - return self->d_type == DT_LNK; - else - return DirEntry_test_mode(defining_class, self, 0, S_IFLNK); -#else - /* POSIX without d_type */ - return DirEntry_test_mode(defining_class, self, 0, S_IFLNK); -#endif -} - -/*[clinic input] -os.DirEntry.is_junction -> bool - -Return True if the entry is a junction; cached per entry. -[clinic start generated code]*/ - -static int -os_DirEntry_is_junction_impl(DirEntry *self) -/*[clinic end generated code: output=97f64d5d99eeccb5 input=4fc8e701eea118a1]*/ -{ -#ifdef MS_WINDOWS - return self->win32_lstat.st_reparse_tag == IO_REPARSE_TAG_MOUNT_POINT; -#else - return 0; -#endif -} - -static PyObject * -DirEntry_fetch_stat(PyObject *module, DirEntry *self, int follow_symlinks) -{ - int result; - STRUCT_STAT st; - PyObject *ub; - -#ifdef MS_WINDOWS - if (!PyUnicode_FSDecoder(self->path, &ub)) - return NULL; - wchar_t *path = PyUnicode_AsWideCharString(ub, NULL); - Py_DECREF(ub); -#else /* POSIX */ - if (!PyUnicode_FSConverter(self->path, &ub)) - return NULL; - const char *path = PyBytes_AS_STRING(ub); - if (self->dir_fd != DEFAULT_DIR_FD) { -#ifdef HAVE_FSTATAT - if (HAVE_FSTATAT_RUNTIME) { - Py_BEGIN_ALLOW_THREADS - result = fstatat(self->dir_fd, path, &st, - follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW); - Py_END_ALLOW_THREADS - } else - -#endif /* HAVE_FSTATAT */ - { - Py_DECREF(ub); - PyErr_SetString(PyExc_NotImplementedError, "can't fetch stat"); - return NULL; - } - } - else -#endif - { - Py_BEGIN_ALLOW_THREADS - if (follow_symlinks) { - result = STAT(path, &st); - } - else { - result = LSTAT(path, &st); - } - Py_END_ALLOW_THREADS - } - - int saved_errno = errno; -#if defined(MS_WINDOWS) - PyMem_Free(path); -#else - Py_DECREF(ub); -#endif - - if (result != 0) { - errno = saved_errno; - path_object_error(self->path); - return NULL; - } - - return _pystat_fromstructstat(module, &st); -} - -static PyObject * -DirEntry_get_lstat(PyTypeObject *defining_class, DirEntry *self) -{ - if (!self->lstat) { - PyObject *module = PyType_GetModule(defining_class); -#ifdef MS_WINDOWS - self->lstat = _pystat_fromstructstat(module, &self->win32_lstat); -#else /* POSIX */ - self->lstat = DirEntry_fetch_stat(module, self, 0); -#endif - } - return Py_XNewRef(self->lstat); -} - -/*[clinic input] -os.DirEntry.stat - defining_class: defining_class - / - * - follow_symlinks: bool = True - -Return stat_result object for the entry; cached per entry. -[clinic start generated code]*/ - -static PyObject * -os_DirEntry_stat_impl(DirEntry *self, PyTypeObject *defining_class, - int follow_symlinks) -/*[clinic end generated code: output=23f803e19c3e780e input=e816273c4e67ee98]*/ -{ - if (!follow_symlinks) { - return DirEntry_get_lstat(defining_class, self); - } - - if (!self->stat) { - int result = os_DirEntry_is_symlink_impl(self, defining_class); - if (result == -1) { - return NULL; - } - if (result) { - PyObject *module = PyType_GetModule(defining_class); - self->stat = DirEntry_fetch_stat(module, self, 1); - } - else { - self->stat = DirEntry_get_lstat(defining_class, self); - } - } - - return Py_XNewRef(self->stat); -} - -/* Set exception and return -1 on error, 0 for False, 1 for True */ -static int -DirEntry_test_mode(PyTypeObject *defining_class, DirEntry *self, - int follow_symlinks, unsigned short mode_bits) -{ - PyObject *stat = NULL; - PyObject *st_mode = NULL; - long mode; - int result; -#if defined(MS_WINDOWS) || defined(HAVE_DIRENT_D_TYPE) - int is_symlink; - int need_stat; -#endif -#ifdef MS_WINDOWS - unsigned long dir_bits; -#endif - -#ifdef MS_WINDOWS - is_symlink = (self->win32_lstat.st_mode & S_IFMT) == S_IFLNK; - need_stat = follow_symlinks && is_symlink; -#elif defined(HAVE_DIRENT_D_TYPE) - is_symlink = self->d_type == DT_LNK; - need_stat = self->d_type == DT_UNKNOWN || (follow_symlinks && is_symlink); -#endif - -#if defined(MS_WINDOWS) || defined(HAVE_DIRENT_D_TYPE) - if (need_stat) { -#endif - stat = os_DirEntry_stat_impl(self, defining_class, follow_symlinks); - if (!stat) { - if (PyErr_ExceptionMatches(PyExc_FileNotFoundError)) { - /* If file doesn't exist (anymore), then return False - (i.e., say it's not a file/directory) */ - PyErr_Clear(); - return 0; - } - goto error; - } - _posixstate* state = get_posix_state(PyType_GetModule(defining_class)); - st_mode = PyObject_GetAttr(stat, state->st_mode); - if (!st_mode) - goto error; - - mode = PyLong_AsLong(st_mode); - if (mode == -1 && PyErr_Occurred()) - goto error; - Py_CLEAR(st_mode); - Py_CLEAR(stat); - result = (mode & S_IFMT) == mode_bits; -#if defined(MS_WINDOWS) || defined(HAVE_DIRENT_D_TYPE) - } - else if (is_symlink) { - assert(mode_bits != S_IFLNK); - result = 0; - } - else { - assert(mode_bits == S_IFDIR || mode_bits == S_IFREG); -#ifdef MS_WINDOWS - dir_bits = self->win32_lstat.st_file_attributes & FILE_ATTRIBUTE_DIRECTORY; - if (mode_bits == S_IFDIR) - result = dir_bits != 0; - else - result = dir_bits == 0; -#else /* POSIX */ - if (mode_bits == S_IFDIR) - result = self->d_type == DT_DIR; - else - result = self->d_type == DT_REG; -#endif - } -#endif - - return result; - -error: - Py_XDECREF(st_mode); - Py_XDECREF(stat); - return -1; -} - -/*[clinic input] -os.DirEntry.is_dir -> bool - defining_class: defining_class - / - * - follow_symlinks: bool = True - -Return True if the entry is a directory; cached per entry. -[clinic start generated code]*/ - -static int -os_DirEntry_is_dir_impl(DirEntry *self, PyTypeObject *defining_class, - int follow_symlinks) -/*[clinic end generated code: output=0cd453b9c0987fdf input=1a4ffd6dec9920cb]*/ -{ - return DirEntry_test_mode(defining_class, self, follow_symlinks, S_IFDIR); -} - -/*[clinic input] -os.DirEntry.is_file -> bool - defining_class: defining_class - / - * - follow_symlinks: bool = True - -Return True if the entry is a file; cached per entry. -[clinic start generated code]*/ - -static int -os_DirEntry_is_file_impl(DirEntry *self, PyTypeObject *defining_class, - int follow_symlinks) -/*[clinic end generated code: output=f7c277ab5ba80908 input=0a64c5a12e802e3b]*/ -{ - return DirEntry_test_mode(defining_class, self, follow_symlinks, S_IFREG); -} - -/*[clinic input] -os.DirEntry.inode - -Return inode of the entry; cached per entry. -[clinic start generated code]*/ - -static PyObject * -os_DirEntry_inode_impl(DirEntry *self) -/*[clinic end generated code: output=156bb3a72162440e input=3ee7b872ae8649f0]*/ -{ -#ifdef MS_WINDOWS - if (!self->got_file_index) { - PyObject *unicode; - STRUCT_STAT stat; - int result; - - if (!PyUnicode_FSDecoder(self->path, &unicode)) - return NULL; - wchar_t *path = PyUnicode_AsWideCharString(unicode, NULL); - Py_DECREF(unicode); - result = LSTAT(path, &stat); - - int saved_errno = errno; - PyMem_Free(path); - - if (result != 0) { - errno = saved_errno; - return path_object_error(self->path); - } - - self->win32_file_index = stat.st_ino; - self->win32_file_index_high = stat.st_ino_high; - self->got_file_index = 1; - } - return _pystat_l128_from_l64_l64(self->win32_file_index, self->win32_file_index_high); -#else /* POSIX */ - static_assert(sizeof(unsigned long long) >= sizeof(self->d_ino), - "DirEntry.d_ino is larger than unsigned long long"); - return PyLong_FromUnsignedLongLong(self->d_ino); -#endif -} - -static PyObject * -DirEntry_repr(DirEntry *self) -{ - return PyUnicode_FromFormat("", self->name); -} - -/*[clinic input] -os.DirEntry.__fspath__ - -Returns the path for the entry. -[clinic start generated code]*/ - -static PyObject * -os_DirEntry___fspath___impl(DirEntry *self) -/*[clinic end generated code: output=6dd7f7ef752e6f4f input=3c49d0cf38df4fac]*/ -{ - return Py_NewRef(self->path); -} - -static PyMemberDef DirEntry_members[] = { - {"name", Py_T_OBJECT_EX, offsetof(DirEntry, name), Py_READONLY, - "the entry's base filename, relative to scandir() \"path\" argument"}, - {"path", Py_T_OBJECT_EX, offsetof(DirEntry, path), Py_READONLY, - "the entry's full path name; equivalent to os.path.join(scandir_path, entry.name)"}, - {NULL} -}; - -#include "clinic/posixmodule.c.h" - -static PyMethodDef DirEntry_methods[] = { - OS_DIRENTRY_IS_DIR_METHODDEF - OS_DIRENTRY_IS_FILE_METHODDEF - OS_DIRENTRY_IS_SYMLINK_METHODDEF - OS_DIRENTRY_IS_JUNCTION_METHODDEF - OS_DIRENTRY_STAT_METHODDEF - OS_DIRENTRY_INODE_METHODDEF - OS_DIRENTRY___FSPATH___METHODDEF - {"__class_getitem__", Py_GenericAlias, - METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, - {NULL} -}; - -static PyType_Slot DirEntryType_slots[] = { - {Py_tp_dealloc, DirEntry_dealloc}, - {Py_tp_repr, DirEntry_repr}, - {Py_tp_methods, DirEntry_methods}, - {Py_tp_members, DirEntry_members}, - {0, 0}, -}; - -static PyType_Spec DirEntryType_spec = { - MODNAME ".DirEntry", - sizeof(DirEntry), - 0, - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, - DirEntryType_slots -}; - - -#ifdef MS_WINDOWS - -static wchar_t * -join_path_filenameW(const wchar_t *path_wide, const wchar_t *filename) -{ - Py_ssize_t path_len; - Py_ssize_t size; - wchar_t *result; - wchar_t ch; - - if (!path_wide) { /* Default arg: "." */ - path_wide = L"."; - path_len = 1; - } - else { - path_len = wcslen(path_wide); - } - - /* The +1's are for the path separator and the NUL */ - size = path_len + 1 + wcslen(filename) + 1; - result = PyMem_New(wchar_t, size); - if (!result) { - PyErr_NoMemory(); - return NULL; - } - wcscpy(result, path_wide); - if (path_len > 0) { - ch = result[path_len - 1]; - if (ch != SEP && ch != ALTSEP && ch != L':') - result[path_len++] = SEP; - wcscpy(result + path_len, filename); - } - return result; -} - -static PyObject * -DirEntry_from_find_data(PyObject *module, path_t *path, WIN32_FIND_DATAW *dataW) -{ - DirEntry *entry; - BY_HANDLE_FILE_INFORMATION file_info; - ULONG reparse_tag; - wchar_t *joined_path; - - PyObject *DirEntryType = get_posix_state(module)->DirEntryType; - entry = PyObject_New(DirEntry, (PyTypeObject *)DirEntryType); - if (!entry) - return NULL; - entry->name = NULL; - entry->path = NULL; - entry->stat = NULL; - entry->lstat = NULL; - entry->got_file_index = 0; - - entry->name = PyUnicode_FromWideChar(dataW->cFileName, -1); - if (!entry->name) - goto error; - int return_bytes = path->wide && PyBytes_Check(path->object); - if (return_bytes) { - Py_SETREF(entry->name, PyUnicode_EncodeFSDefault(entry->name)); - if (!entry->name) - goto error; - } - - joined_path = join_path_filenameW(path->wide, dataW->cFileName); - if (!joined_path) - goto error; - - entry->path = PyUnicode_FromWideChar(joined_path, -1); - PyMem_Free(joined_path); - if (!entry->path) - goto error; - if (return_bytes) { - Py_SETREF(entry->path, PyUnicode_EncodeFSDefault(entry->path)); - if (!entry->path) - goto error; - } - - find_data_to_file_info(dataW, &file_info, &reparse_tag); - _Py_attribute_data_to_stat(&file_info, reparse_tag, NULL, NULL, &entry->win32_lstat); - - /* ctime is only deprecated from 3.12, so we copy birthtime across */ - entry->win32_lstat.st_ctime = entry->win32_lstat.st_birthtime; - entry->win32_lstat.st_ctime_nsec = entry->win32_lstat.st_birthtime_nsec; - - return (PyObject *)entry; - -error: - Py_DECREF(entry); - return NULL; -} - -#else /* POSIX */ - -static char * -join_path_filename(const char *path_narrow, const char* filename, Py_ssize_t filename_len) -{ - Py_ssize_t path_len; - Py_ssize_t size; - char *result; - - if (!path_narrow) { /* Default arg: "." */ - path_narrow = "."; - path_len = 1; - } - else { - path_len = strlen(path_narrow); - } - - if (filename_len == -1) - filename_len = strlen(filename); - - /* The +1's are for the path separator and the NUL */ - size = path_len + 1 + filename_len + 1; - result = PyMem_New(char, size); - if (!result) { - PyErr_NoMemory(); - return NULL; - } - strcpy(result, path_narrow); - if (path_len > 0 && result[path_len - 1] != '/') - result[path_len++] = '/'; - strcpy(result + path_len, filename); - return result; -} - -static PyObject * -DirEntry_from_posix_info(PyObject *module, path_t *path, const char *name, - Py_ssize_t name_len, ino_t d_ino -#ifdef HAVE_DIRENT_D_TYPE - , unsigned char d_type -#endif - ) -{ - DirEntry *entry; - char *joined_path; - - PyObject *DirEntryType = get_posix_state(module)->DirEntryType; - entry = PyObject_New(DirEntry, (PyTypeObject *)DirEntryType); - if (!entry) - return NULL; - entry->name = NULL; - entry->path = NULL; - entry->stat = NULL; - entry->lstat = NULL; - - if (path->fd != -1) { - entry->dir_fd = path->fd; - joined_path = NULL; - } - else { - entry->dir_fd = DEFAULT_DIR_FD; - joined_path = join_path_filename(path->narrow, name, name_len); - if (!joined_path) - goto error; - } - - if (!path->narrow || !PyBytes_Check(path->object)) { - entry->name = PyUnicode_DecodeFSDefaultAndSize(name, name_len); - if (joined_path) - entry->path = PyUnicode_DecodeFSDefault(joined_path); - } - else { - entry->name = PyBytes_FromStringAndSize(name, name_len); - if (joined_path) - entry->path = PyBytes_FromString(joined_path); - } - PyMem_Free(joined_path); - if (!entry->name) - goto error; - - if (path->fd != -1) { - entry->path = Py_NewRef(entry->name); - } - else if (!entry->path) - goto error; - -#ifdef HAVE_DIRENT_D_TYPE - entry->d_type = d_type; -#endif - entry->d_ino = d_ino; - - return (PyObject *)entry; - -error: - Py_XDECREF(entry); - return NULL; -} - -#endif - - -typedef struct { - PyObject_HEAD - path_t path; -#ifdef MS_WINDOWS - HANDLE handle; - WIN32_FIND_DATAW file_data; - int first_time; -#else /* POSIX */ - DIR *dirp; -#endif -#ifdef HAVE_FDOPENDIR - int fd; -#endif -} ScandirIterator; - -#ifdef MS_WINDOWS - -static int -ScandirIterator_is_closed(ScandirIterator *iterator) -{ - return iterator->handle == INVALID_HANDLE_VALUE; -} - -static void -ScandirIterator_closedir(ScandirIterator *iterator) -{ - HANDLE handle = iterator->handle; - - if (handle == INVALID_HANDLE_VALUE) - return; - - iterator->handle = INVALID_HANDLE_VALUE; - Py_BEGIN_ALLOW_THREADS - FindClose(handle); - Py_END_ALLOW_THREADS -} - -static PyObject * -ScandirIterator_iternext(ScandirIterator *iterator) -{ - WIN32_FIND_DATAW *file_data = &iterator->file_data; - BOOL success; - PyObject *entry; - - /* Happens if the iterator is iterated twice, or closed explicitly */ - if (iterator->handle == INVALID_HANDLE_VALUE) - return NULL; - - while (1) { - if (!iterator->first_time) { - Py_BEGIN_ALLOW_THREADS - success = FindNextFileW(iterator->handle, file_data); - Py_END_ALLOW_THREADS - if (!success) { - /* Error or no more files */ - if (GetLastError() != ERROR_NO_MORE_FILES) - path_error(&iterator->path); - break; - } - } - iterator->first_time = 0; - - /* Skip over . and .. */ - if (wcscmp(file_data->cFileName, L".") != 0 && - wcscmp(file_data->cFileName, L"..") != 0) - { - PyObject *module = PyType_GetModule(Py_TYPE(iterator)); - entry = DirEntry_from_find_data(module, &iterator->path, file_data); - if (!entry) - break; - return entry; - } - - /* Loop till we get a non-dot directory or finish iterating */ - } - - /* Error or no more files */ - ScandirIterator_closedir(iterator); - return NULL; -} - -#else /* POSIX */ - -static int -ScandirIterator_is_closed(ScandirIterator *iterator) -{ - return !iterator->dirp; -} - -static void -ScandirIterator_closedir(ScandirIterator *iterator) -{ - DIR *dirp = iterator->dirp; - - if (!dirp) - return; - - iterator->dirp = NULL; - Py_BEGIN_ALLOW_THREADS -#ifdef HAVE_FDOPENDIR - if (iterator->path.fd != -1) - rewinddir(dirp); -#endif - closedir(dirp); - Py_END_ALLOW_THREADS - return; -} - -static PyObject * -ScandirIterator_iternext(ScandirIterator *iterator) -{ - struct dirent *direntp; - Py_ssize_t name_len; - int is_dot; - PyObject *entry; - - /* Happens if the iterator is iterated twice, or closed explicitly */ - if (!iterator->dirp) - return NULL; - - while (1) { - errno = 0; - Py_BEGIN_ALLOW_THREADS - direntp = readdir(iterator->dirp); - Py_END_ALLOW_THREADS - - if (!direntp) { - /* Error or no more files */ - if (errno != 0) - path_error(&iterator->path); - break; - } - - /* Skip over . and .. */ - name_len = NAMLEN(direntp); - is_dot = direntp->d_name[0] == '.' && - (name_len == 1 || (direntp->d_name[1] == '.' && name_len == 2)); - if (!is_dot) { - PyObject *module = PyType_GetModule(Py_TYPE(iterator)); - entry = DirEntry_from_posix_info(module, - &iterator->path, direntp->d_name, - name_len, direntp->d_ino -#ifdef HAVE_DIRENT_D_TYPE - , direntp->d_type -#endif - ); - if (!entry) - break; - return entry; - } - - /* Loop till we get a non-dot directory or finish iterating */ - } - - /* Error or no more files */ - ScandirIterator_closedir(iterator); - return NULL; -} - -#endif - -static PyObject * -ScandirIterator_close(ScandirIterator *self, PyObject *args) -{ - ScandirIterator_closedir(self); - Py_RETURN_NONE; -} - -static PyObject * -ScandirIterator_enter(PyObject *self, PyObject *args) -{ - return Py_NewRef(self); -} - -static PyObject * -ScandirIterator_exit(ScandirIterator *self, PyObject *args) -{ - ScandirIterator_closedir(self); - Py_RETURN_NONE; -} - -static void -ScandirIterator_finalize(ScandirIterator *iterator) -{ - - /* Save the current exception, if any. */ - PyObject *exc = PyErr_GetRaisedException(); - - if (!ScandirIterator_is_closed(iterator)) { - ScandirIterator_closedir(iterator); - - if (PyErr_ResourceWarning((PyObject *)iterator, 1, - "unclosed scandir iterator %R", iterator)) { - /* Spurious errors can appear at shutdown */ - if (PyErr_ExceptionMatches(PyExc_Warning)) { - PyErr_WriteUnraisable((PyObject *) iterator); - } - } - } - - path_cleanup(&iterator->path); - - /* Restore the saved exception. */ - PyErr_SetRaisedException(exc); -} - -static void -ScandirIterator_dealloc(ScandirIterator *iterator) -{ - PyTypeObject *tp = Py_TYPE(iterator); - if (PyObject_CallFinalizerFromDealloc((PyObject *)iterator) < 0) - return; - - freefunc free_func = PyType_GetSlot(tp, Py_tp_free); - free_func(iterator); - Py_DECREF(tp); -} - -static PyMethodDef ScandirIterator_methods[] = { - {"__enter__", (PyCFunction)ScandirIterator_enter, METH_NOARGS}, - {"__exit__", (PyCFunction)ScandirIterator_exit, METH_VARARGS}, - {"close", (PyCFunction)ScandirIterator_close, METH_NOARGS}, - {NULL} -}; - -static PyType_Slot ScandirIteratorType_slots[] = { - {Py_tp_dealloc, ScandirIterator_dealloc}, - {Py_tp_finalize, ScandirIterator_finalize}, - {Py_tp_iter, PyObject_SelfIter}, - {Py_tp_iternext, ScandirIterator_iternext}, - {Py_tp_methods, ScandirIterator_methods}, - {0, 0}, -}; - -static PyType_Spec ScandirIteratorType_spec = { - MODNAME ".ScandirIterator", - sizeof(ScandirIterator), - 0, - // bpo-40549: Py_TPFLAGS_BASETYPE should not be used, since - // PyType_GetModule(Py_TYPE(self)) doesn't work on a subclass instance. - (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_FINALIZE - | Py_TPFLAGS_DISALLOW_INSTANTIATION), - ScandirIteratorType_slots -}; - -/*[clinic input] -os.scandir - - path : path_t(nullable=True, allow_fd='PATH_HAVE_FDOPENDIR') = None - -Return an iterator of DirEntry objects for given path. - -path can be specified as either str, bytes, or a path-like object. If path -is bytes, the names of yielded DirEntry objects will also be bytes; in -all other circumstances they will be str. - -If path is None, uses the path='.'. -[clinic start generated code]*/ - -static PyObject * -os_scandir_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=6eb2668b675ca89e input=6bdd312708fc3bb0]*/ -{ - ScandirIterator *iterator; -#ifdef MS_WINDOWS - wchar_t *path_strW; -#else - const char *path_str; -#ifdef HAVE_FDOPENDIR - int fd = -1; -#endif -#endif - - if (PySys_Audit("os.scandir", "O", - path->object ? path->object : Py_None) < 0) { - return NULL; - } - - PyObject *ScandirIteratorType = get_posix_state(module)->ScandirIteratorType; - iterator = PyObject_New(ScandirIterator, (PyTypeObject *)ScandirIteratorType); - if (!iterator) - return NULL; - -#ifdef MS_WINDOWS - iterator->handle = INVALID_HANDLE_VALUE; -#else - iterator->dirp = NULL; -#endif - - /* Move the ownership to iterator->path */ - memcpy(&iterator->path, path, sizeof(path_t)); - memset(path, 0, sizeof(path_t)); - -#ifdef MS_WINDOWS - iterator->first_time = 1; - - path_strW = join_path_filenameW(iterator->path.wide, L"*.*"); - if (!path_strW) - goto error; - - Py_BEGIN_ALLOW_THREADS - iterator->handle = FindFirstFileW(path_strW, &iterator->file_data); - Py_END_ALLOW_THREADS - - if (iterator->handle == INVALID_HANDLE_VALUE) { - path_error(&iterator->path); - PyMem_Free(path_strW); - goto error; - } - PyMem_Free(path_strW); -#else /* POSIX */ - errno = 0; -#ifdef HAVE_FDOPENDIR - if (iterator->path.fd != -1) { - if (HAVE_FDOPENDIR_RUNTIME) { - /* closedir() closes the FD, so we duplicate it */ - fd = _Py_dup(iterator->path.fd); - if (fd == -1) - goto error; - - Py_BEGIN_ALLOW_THREADS - iterator->dirp = fdopendir(fd); - Py_END_ALLOW_THREADS - } else { - PyErr_SetString(PyExc_TypeError, - "scandir: path should be string, bytes, os.PathLike or None, not int"); - return NULL; - } - } - else -#endif - { - if (iterator->path.narrow) - path_str = iterator->path.narrow; - else - path_str = "."; - - Py_BEGIN_ALLOW_THREADS - iterator->dirp = opendir(path_str); - Py_END_ALLOW_THREADS - } - - if (!iterator->dirp) { - path_error(&iterator->path); -#ifdef HAVE_FDOPENDIR - if (fd != -1) { - Py_BEGIN_ALLOW_THREADS - close(fd); - Py_END_ALLOW_THREADS - } -#endif - goto error; - } -#endif - - return (PyObject *)iterator; - -error: - Py_DECREF(iterator); - return NULL; -} - -/* - Return the file system path representation of the object. - - If the object is str or bytes, then allow it to pass through with - an incremented refcount. If the object defines __fspath__(), then - return the result of that method. All other types raise a TypeError. -*/ -PyObject * -PyOS_FSPath(PyObject *path) -{ - /* For error message reasons, this function is manually inlined in - path_converter(). */ - PyObject *func = NULL; - PyObject *path_repr = NULL; - - if (PyUnicode_Check(path) || PyBytes_Check(path)) { - return Py_NewRef(path); - } - - func = _PyObject_LookupSpecial(path, &_Py_ID(__fspath__)); - if ((NULL == func) || (func == Py_None)) { - return PyErr_Format(PyExc_TypeError, - "expected str, bytes or os.PathLike object, " - "not %.200s", - _PyType_Name(Py_TYPE(path))); - } - - path_repr = _PyObject_CallNoArgs(func); - Py_DECREF(func); - if (NULL == path_repr) { - return NULL; - } - - if (!(PyUnicode_Check(path_repr) || PyBytes_Check(path_repr))) { - PyErr_Format(PyExc_TypeError, - "expected %.200s.__fspath__() to return str or bytes, " - "not %.200s", _PyType_Name(Py_TYPE(path)), - _PyType_Name(Py_TYPE(path_repr))); - Py_DECREF(path_repr); - return NULL; - } - - return path_repr; -} - -/*[clinic input] -os.fspath - - path: object - -Return the file system path representation of the object. - -If the object is str or bytes, then allow it to pass through as-is. If the -object defines __fspath__(), then return the result of that method. All other -types raise a TypeError. -[clinic start generated code]*/ - -static PyObject * -os_fspath_impl(PyObject *module, PyObject *path) -/*[clinic end generated code: output=c3c3b78ecff2914f input=e357165f7b22490f]*/ -{ - return PyOS_FSPath(path); -} - -#ifdef HAVE_GETRANDOM_SYSCALL -/*[clinic input] -os.getrandom - - size: Py_ssize_t - flags: int=0 - -Obtain a series of random bytes. -[clinic start generated code]*/ - -static PyObject * -os_getrandom_impl(PyObject *module, Py_ssize_t size, int flags) -/*[clinic end generated code: output=b3a618196a61409c input=59bafac39c594947]*/ -{ - PyObject *bytes; - Py_ssize_t n; - - if (size < 0) { - errno = EINVAL; - return posix_error(); - } - - bytes = PyBytes_FromStringAndSize(NULL, size); - if (bytes == NULL) { - PyErr_NoMemory(); - return NULL; - } - - while (1) { - n = syscall(SYS_getrandom, - PyBytes_AS_STRING(bytes), - PyBytes_GET_SIZE(bytes), - flags); - if (n < 0 && errno == EINTR) { - if (PyErr_CheckSignals() < 0) { - goto error; - } - - /* getrandom() was interrupted by a signal: retry */ - continue; - } - break; - } - - if (n < 0) { - PyErr_SetFromErrno(PyExc_OSError); - goto error; - } - - if (n != size) { - _PyBytes_Resize(&bytes, n); - } - - return bytes; - -error: - Py_DECREF(bytes); - return NULL; -} -#endif /* HAVE_GETRANDOM_SYSCALL */ - -#if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_APP) || defined(MS_WINDOWS_SYSTEM) - -/* bpo-36085: Helper functions for managing DLL search directories - * on win32 - */ - -/*[clinic input] -os._add_dll_directory - - path: path_t - -Add a path to the DLL search path. - -This search path is used when resolving dependencies for imported -extension modules (the module itself is resolved through sys.path), -and also by ctypes. - -Returns an opaque value that may be passed to os.remove_dll_directory -to remove this directory from the search path. -[clinic start generated code]*/ - -static PyObject * -os__add_dll_directory_impl(PyObject *module, path_t *path) -/*[clinic end generated code: output=80b025daebb5d683 input=1de3e6c13a5808c8]*/ -{ - DLL_DIRECTORY_COOKIE cookie = 0; - DWORD err = 0; - - if (PySys_Audit("os.add_dll_directory", "(O)", path->object) < 0) { - return NULL; - } - - Py_BEGIN_ALLOW_THREADS - if (!(cookie = AddDllDirectory(path->wide))) { - err = GetLastError(); - } - Py_END_ALLOW_THREADS - - if (err) { - return win32_error_object_err("add_dll_directory", - path->object, err); - } - - return PyCapsule_New(cookie, "DLL directory cookie", NULL); -} - -/*[clinic input] -os._remove_dll_directory - - cookie: object - -Removes a path from the DLL search path. - -The parameter is an opaque value that was returned from -os.add_dll_directory. You can only remove directories that you added -yourself. -[clinic start generated code]*/ - -static PyObject * -os__remove_dll_directory_impl(PyObject *module, PyObject *cookie) -/*[clinic end generated code: output=594350433ae535bc input=c1d16a7e7d9dc5dc]*/ -{ - DLL_DIRECTORY_COOKIE cookieValue; - DWORD err = 0; - - if (!PyCapsule_IsValid(cookie, "DLL directory cookie")) { - PyErr_SetString(PyExc_TypeError, - "Provided cookie was not returned from os.add_dll_directory"); - return NULL; - } - - cookieValue = (DLL_DIRECTORY_COOKIE)PyCapsule_GetPointer( - cookie, "DLL directory cookie"); - - Py_BEGIN_ALLOW_THREADS - if (!RemoveDllDirectory(cookieValue)) { - err = GetLastError(); - } - Py_END_ALLOW_THREADS - - if (err) { - return win32_error_object_err("remove_dll_directory", - NULL, err); - } - - if (PyCapsule_SetName(cookie, NULL)) { - return NULL; - } - - Py_RETURN_NONE; -} - -#endif /* MS_WINDOWS_APP || MS_WINDOWS_SYSTEM */ - - -/* Only check if WIFEXITED is available: expect that it comes - with WEXITSTATUS, WIFSIGNALED, etc. - - os.waitstatus_to_exitcode() is implemented in C and not in Python, so - subprocess can safely call it during late Python finalization without - risking that used os attributes were set to None by finalize_modules(). */ -#if defined(WIFEXITED) || defined(MS_WINDOWS) -/*[clinic input] -os.waitstatus_to_exitcode - - status as status_obj: object - -Convert a wait status to an exit code. - -On Unix: - -* If WIFEXITED(status) is true, return WEXITSTATUS(status). -* If WIFSIGNALED(status) is true, return -WTERMSIG(status). -* Otherwise, raise a ValueError. - -On Windows, return status shifted right by 8 bits. - -On Unix, if the process is being traced or if waitpid() was called with -WUNTRACED option, the caller must first check if WIFSTOPPED(status) is true. -This function must not be called if WIFSTOPPED(status) is true. -[clinic start generated code]*/ - -static PyObject * -os_waitstatus_to_exitcode_impl(PyObject *module, PyObject *status_obj) -/*[clinic end generated code: output=db50b1b0ba3c7153 input=7fe2d7fdaea3db42]*/ -{ -#ifndef MS_WINDOWS - int status = PyLong_AsInt(status_obj); - if (status == -1 && PyErr_Occurred()) { - return NULL; - } - - WAIT_TYPE wait_status; - WAIT_STATUS_INT(wait_status) = status; - int exitcode; - if (WIFEXITED(wait_status)) { - exitcode = WEXITSTATUS(wait_status); - /* Sanity check to provide warranty on the function behavior. - It should not occur in practice */ - if (exitcode < 0) { - PyErr_Format(PyExc_ValueError, "invalid WEXITSTATUS: %i", exitcode); - return NULL; - } - } - else if (WIFSIGNALED(wait_status)) { - int signum = WTERMSIG(wait_status); - /* Sanity check to provide warranty on the function behavior. - It should not occurs in practice */ - if (signum <= 0) { - PyErr_Format(PyExc_ValueError, "invalid WTERMSIG: %i", signum); - return NULL; - } - exitcode = -signum; - } else if (WIFSTOPPED(wait_status)) { - /* Status only received if the process is being traced - or if waitpid() was called with WUNTRACED option. */ - int signum = WSTOPSIG(wait_status); - PyErr_Format(PyExc_ValueError, - "process stopped by delivery of signal %i", - signum); - return NULL; - } - else { - PyErr_Format(PyExc_ValueError, "invalid wait status: %i", status); - return NULL; - } - return PyLong_FromLong(exitcode); -#else - /* Windows implementation: see os.waitpid() implementation - which uses _cwait(). */ - unsigned long long status = PyLong_AsUnsignedLongLong(status_obj); - if (status == (unsigned long long)-1 && PyErr_Occurred()) { - return NULL; - } - - unsigned long long exitcode = (status >> 8); - /* ExitProcess() accepts an UINT type: - reject exit code which doesn't fit in an UINT */ - if (exitcode > UINT_MAX) { - PyErr_Format(PyExc_ValueError, "invalid exit code: %llu", exitcode); - return NULL; - } - return PyLong_FromUnsignedLong((unsigned long)exitcode); -#endif -} -#endif - -#if defined(MS_WINDOWS) -/*[clinic input] -os._supports_virtual_terminal - -Checks if virtual terminal is supported in windows -[clinic start generated code]*/ - -static PyObject * -os__supports_virtual_terminal_impl(PyObject *module) -/*[clinic end generated code: output=bd0556a6d9d99fe6 input=0752c98e5d321542]*/ -{ - DWORD mode = 0; - HANDLE handle = GetStdHandle(STD_ERROR_HANDLE); - if (!GetConsoleMode(handle, &mode)) { - Py_RETURN_FALSE; - } - return PyBool_FromLong(mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING); -} -#endif - -/*[clinic input] -os._inputhook - -Calls PyOS_CallInputHook droppong the GIL first -[clinic start generated code]*/ - -static PyObject * -os__inputhook_impl(PyObject *module) -/*[clinic end generated code: output=525aca4ef3c6149f input=fc531701930d064f]*/ -{ - int result = 0; - if (PyOS_InputHook) { - Py_BEGIN_ALLOW_THREADS; - result = PyOS_InputHook(); - Py_END_ALLOW_THREADS; - } - return PyLong_FromLong(result); -} - -/*[clinic input] -os._is_inputhook_installed - -Checks if PyOS_CallInputHook is set -[clinic start generated code]*/ - -static PyObject * -os__is_inputhook_installed_impl(PyObject *module) -/*[clinic end generated code: output=3b3eab4f672c689a input=ff177c9938dd76d8]*/ -{ - return PyBool_FromLong(PyOS_InputHook != NULL); -} - -static PyMethodDef posix_methods[] = { - - OS_STAT_METHODDEF - OS_ACCESS_METHODDEF - OS_TTYNAME_METHODDEF - OS_CHDIR_METHODDEF - OS_CHFLAGS_METHODDEF - OS_CHMOD_METHODDEF - OS_FCHMOD_METHODDEF - OS_LCHMOD_METHODDEF - OS_CHOWN_METHODDEF - OS_FCHOWN_METHODDEF - OS_LCHOWN_METHODDEF - OS_LCHFLAGS_METHODDEF - OS_CHROOT_METHODDEF - OS_CTERMID_METHODDEF - OS_GETCWD_METHODDEF - OS_GETCWDB_METHODDEF - OS_LINK_METHODDEF - OS_LISTDIR_METHODDEF - OS_LISTDRIVES_METHODDEF - OS_LISTMOUNTS_METHODDEF - OS_LISTVOLUMES_METHODDEF - OS_LSTAT_METHODDEF - OS_MKDIR_METHODDEF - OS_NICE_METHODDEF - OS_GETPRIORITY_METHODDEF - OS_SETPRIORITY_METHODDEF - OS_POSIX_SPAWN_METHODDEF - OS_POSIX_SPAWNP_METHODDEF - OS_READLINK_METHODDEF - OS_COPY_FILE_RANGE_METHODDEF - OS_SPLICE_METHODDEF - OS_RENAME_METHODDEF - OS_REPLACE_METHODDEF - OS_RMDIR_METHODDEF - OS_SYMLINK_METHODDEF - OS_SYSTEM_METHODDEF - OS_UMASK_METHODDEF - OS_UNAME_METHODDEF - OS_UNLINK_METHODDEF - OS_REMOVE_METHODDEF - OS_UTIME_METHODDEF - OS_TIMES_METHODDEF - OS__EXIT_METHODDEF - OS__FCOPYFILE_METHODDEF - OS_EXECV_METHODDEF - OS_EXECVE_METHODDEF - OS_SPAWNV_METHODDEF - OS_SPAWNVE_METHODDEF - OS_FORK1_METHODDEF - OS_FORK_METHODDEF - OS_REGISTER_AT_FORK_METHODDEF - OS_SCHED_GET_PRIORITY_MAX_METHODDEF - OS_SCHED_GET_PRIORITY_MIN_METHODDEF - OS_SCHED_GETPARAM_METHODDEF - OS_SCHED_GETSCHEDULER_METHODDEF - OS_SCHED_RR_GET_INTERVAL_METHODDEF - OS_SCHED_SETPARAM_METHODDEF - OS_SCHED_SETSCHEDULER_METHODDEF - OS_SCHED_YIELD_METHODDEF - OS_SCHED_SETAFFINITY_METHODDEF - OS_SCHED_GETAFFINITY_METHODDEF - OS_POSIX_OPENPT_METHODDEF - OS_GRANTPT_METHODDEF - OS_UNLOCKPT_METHODDEF - OS_PTSNAME_METHODDEF - OS_OPENPTY_METHODDEF - OS_LOGIN_TTY_METHODDEF - OS_FORKPTY_METHODDEF - OS_GETEGID_METHODDEF - OS_GETEUID_METHODDEF - OS_GETGID_METHODDEF - OS_GETGROUPLIST_METHODDEF - OS_GETGROUPS_METHODDEF - OS_GETPID_METHODDEF - OS_GETPGRP_METHODDEF - OS_GETPPID_METHODDEF - OS_GETUID_METHODDEF - OS_GETLOGIN_METHODDEF - OS_KILL_METHODDEF - OS_KILLPG_METHODDEF - OS_PLOCK_METHODDEF - OS_STARTFILE_METHODDEF - OS_SETUID_METHODDEF - OS_SETEUID_METHODDEF - OS_SETREUID_METHODDEF - OS_SETGID_METHODDEF - OS_SETEGID_METHODDEF - OS_SETREGID_METHODDEF - OS_SETGROUPS_METHODDEF - OS_INITGROUPS_METHODDEF - OS_GETPGID_METHODDEF - OS_SETPGRP_METHODDEF - OS_WAIT_METHODDEF - OS_WAIT3_METHODDEF - OS_WAIT4_METHODDEF - OS_WAITID_METHODDEF - OS_WAITPID_METHODDEF - OS_PIDFD_OPEN_METHODDEF - OS_GETSID_METHODDEF - OS_SETSID_METHODDEF - OS_SETPGID_METHODDEF - OS_TCGETPGRP_METHODDEF - OS_TCSETPGRP_METHODDEF - OS_OPEN_METHODDEF - OS_CLOSE_METHODDEF - OS_CLOSERANGE_METHODDEF - OS_DEVICE_ENCODING_METHODDEF - OS_DUP_METHODDEF - OS_DUP2_METHODDEF - OS_LOCKF_METHODDEF - OS_LSEEK_METHODDEF - OS_READ_METHODDEF - OS_READV_METHODDEF - OS_PREAD_METHODDEF - OS_PREADV_METHODDEF - OS_WRITE_METHODDEF - OS_WRITEV_METHODDEF - OS_PWRITE_METHODDEF - OS_PWRITEV_METHODDEF - OS_SENDFILE_METHODDEF - OS_FSTAT_METHODDEF - OS_ISATTY_METHODDEF - OS_PIPE_METHODDEF - OS_PIPE2_METHODDEF - OS_MKFIFO_METHODDEF - OS_MKNOD_METHODDEF - OS_MAJOR_METHODDEF - OS_MINOR_METHODDEF - OS_MAKEDEV_METHODDEF - OS_FTRUNCATE_METHODDEF - OS_TRUNCATE_METHODDEF - OS_POSIX_FALLOCATE_METHODDEF - OS_POSIX_FADVISE_METHODDEF - OS_PUTENV_METHODDEF - OS_UNSETENV_METHODDEF - OS_STRERROR_METHODDEF - OS_FCHDIR_METHODDEF - OS_FSYNC_METHODDEF - OS_SYNC_METHODDEF - OS_FDATASYNC_METHODDEF - OS_WCOREDUMP_METHODDEF - OS_WIFCONTINUED_METHODDEF - OS_WIFSTOPPED_METHODDEF - OS_WIFSIGNALED_METHODDEF - OS_WIFEXITED_METHODDEF - OS_WEXITSTATUS_METHODDEF - OS_WTERMSIG_METHODDEF - OS_WSTOPSIG_METHODDEF - OS_FSTATVFS_METHODDEF - OS_STATVFS_METHODDEF - OS_CONFSTR_METHODDEF - OS_SYSCONF_METHODDEF - OS_FPATHCONF_METHODDEF - OS_PATHCONF_METHODDEF - OS_ABORT_METHODDEF - OS__GETFULLPATHNAME_METHODDEF - OS__GETDISKUSAGE_METHODDEF - OS__GETFINALPATHNAME_METHODDEF - OS__FINDFIRSTFILE_METHODDEF - OS__GETVOLUMEPATHNAME_METHODDEF - OS__PATH_SPLITROOT_METHODDEF - OS__PATH_SPLITROOT_EX_METHODDEF - OS__PATH_NORMPATH_METHODDEF - OS_GETLOADAVG_METHODDEF - OS_URANDOM_METHODDEF - OS_SETRESUID_METHODDEF - OS_SETRESGID_METHODDEF - OS_GETRESUID_METHODDEF - OS_GETRESGID_METHODDEF - - OS_GETXATTR_METHODDEF - OS_SETXATTR_METHODDEF - OS_REMOVEXATTR_METHODDEF - OS_LISTXATTR_METHODDEF - - OS_GET_TERMINAL_SIZE_METHODDEF - OS_CPU_COUNT_METHODDEF - OS_GET_INHERITABLE_METHODDEF - OS_SET_INHERITABLE_METHODDEF - OS_GET_HANDLE_INHERITABLE_METHODDEF - OS_SET_HANDLE_INHERITABLE_METHODDEF - OS_GET_BLOCKING_METHODDEF - OS_SET_BLOCKING_METHODDEF - OS_SCANDIR_METHODDEF - OS_FSPATH_METHODDEF - OS_GETRANDOM_METHODDEF - OS_MEMFD_CREATE_METHODDEF - OS_EVENTFD_METHODDEF - OS_EVENTFD_READ_METHODDEF - OS_EVENTFD_WRITE_METHODDEF - OS__ADD_DLL_DIRECTORY_METHODDEF - OS__REMOVE_DLL_DIRECTORY_METHODDEF - OS_WAITSTATUS_TO_EXITCODE_METHODDEF - OS_SETNS_METHODDEF - OS_UNSHARE_METHODDEF - OS_TIMERFD_CREATE_METHODDEF - OS_TIMERFD_SETTIME_METHODDEF - OS_TIMERFD_SETTIME_NS_METHODDEF - OS_TIMERFD_GETTIME_METHODDEF - OS_TIMERFD_GETTIME_NS_METHODDEF - - OS__PATH_ISDEVDRIVE_METHODDEF - OS__PATH_ISDIR_METHODDEF - OS__PATH_ISFILE_METHODDEF - OS__PATH_ISLINK_METHODDEF - OS__PATH_ISJUNCTION_METHODDEF - OS__PATH_EXISTS_METHODDEF - OS__PATH_LEXISTS_METHODDEF - - OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF - OS__INPUTHOOK_METHODDEF - OS__IS_INPUTHOOK_INSTALLED_METHODDEF - {NULL, NULL} /* Sentinel */ -}; - -static int -all_ins(PyObject *m) -{ -#ifdef F_OK - if (PyModule_AddIntMacro(m, F_OK)) return -1; -#endif -#ifdef R_OK - if (PyModule_AddIntMacro(m, R_OK)) return -1; -#endif -#ifdef W_OK - if (PyModule_AddIntMacro(m, W_OK)) return -1; -#endif -#ifdef X_OK - if (PyModule_AddIntMacro(m, X_OK)) return -1; -#endif -#ifdef NGROUPS_MAX - if (PyModule_AddIntMacro(m, NGROUPS_MAX)) return -1; -#endif -#ifdef TMP_MAX - if (PyModule_AddIntMacro(m, TMP_MAX)) return -1; -#endif -#ifdef WCONTINUED - if (PyModule_AddIntMacro(m, WCONTINUED)) return -1; -#endif -#ifdef WNOHANG - if (PyModule_AddIntMacro(m, WNOHANG)) return -1; -#endif -#ifdef WUNTRACED - if (PyModule_AddIntMacro(m, WUNTRACED)) return -1; -#endif -#ifdef O_RDONLY - if (PyModule_AddIntMacro(m, O_RDONLY)) return -1; -#endif -#ifdef O_WRONLY - if (PyModule_AddIntMacro(m, O_WRONLY)) return -1; -#endif -#ifdef O_RDWR - if (PyModule_AddIntMacro(m, O_RDWR)) return -1; -#endif -#ifdef O_NDELAY - if (PyModule_AddIntMacro(m, O_NDELAY)) return -1; -#endif -#ifdef O_NONBLOCK - if (PyModule_AddIntMacro(m, O_NONBLOCK)) return -1; -#endif -#ifdef O_APPEND - if (PyModule_AddIntMacro(m, O_APPEND)) return -1; -#endif -#ifdef O_DSYNC - if (PyModule_AddIntMacro(m, O_DSYNC)) return -1; -#endif -#ifdef O_RSYNC - if (PyModule_AddIntMacro(m, O_RSYNC)) return -1; -#endif -#ifdef O_SYNC - if (PyModule_AddIntMacro(m, O_SYNC)) return -1; -#endif -#ifdef O_NOCTTY - if (PyModule_AddIntMacro(m, O_NOCTTY)) return -1; -#endif -#ifdef O_CREAT - if (PyModule_AddIntMacro(m, O_CREAT)) return -1; -#endif -#ifdef O_EXCL - if (PyModule_AddIntMacro(m, O_EXCL)) return -1; -#endif -#ifdef O_TRUNC - if (PyModule_AddIntMacro(m, O_TRUNC)) return -1; -#endif -#ifdef O_BINARY - if (PyModule_AddIntMacro(m, O_BINARY)) return -1; -#endif -#ifdef O_TEXT - if (PyModule_AddIntMacro(m, O_TEXT)) return -1; -#endif -#ifdef O_XATTR - if (PyModule_AddIntMacro(m, O_XATTR)) return -1; -#endif -#ifdef O_LARGEFILE - if (PyModule_AddIntMacro(m, O_LARGEFILE)) return -1; -#endif -#ifndef __GNU__ -#ifdef O_SHLOCK - if (PyModule_AddIntMacro(m, O_SHLOCK)) return -1; -#endif -#ifdef O_EXLOCK - if (PyModule_AddIntMacro(m, O_EXLOCK)) return -1; -#endif -#endif -#ifdef O_EXEC - if (PyModule_AddIntMacro(m, O_EXEC)) return -1; -#endif -#ifdef O_SEARCH - if (PyModule_AddIntMacro(m, O_SEARCH)) return -1; -#endif -#ifdef O_PATH - if (PyModule_AddIntMacro(m, O_PATH)) return -1; -#endif -#ifdef O_TTY_INIT - if (PyModule_AddIntMacro(m, O_TTY_INIT)) return -1; -#endif -#ifdef O_TMPFILE - if (PyModule_AddIntMacro(m, O_TMPFILE)) return -1; -#endif -#ifdef PRIO_PROCESS - if (PyModule_AddIntMacro(m, PRIO_PROCESS)) return -1; -#endif -#ifdef PRIO_PGRP - if (PyModule_AddIntMacro(m, PRIO_PGRP)) return -1; -#endif -#ifdef PRIO_USER - if (PyModule_AddIntMacro(m, PRIO_USER)) return -1; -#endif -#ifdef PRIO_DARWIN_THREAD - if (PyModule_AddIntMacro(m, PRIO_DARWIN_THREAD)) return -1; -#endif -#ifdef PRIO_DARWIN_PROCESS - if (PyModule_AddIntMacro(m, PRIO_DARWIN_PROCESS)) return -1; -#endif -#ifdef PRIO_DARWIN_BG - if (PyModule_AddIntMacro(m, PRIO_DARWIN_BG)) return -1; -#endif -#ifdef PRIO_DARWIN_NONUI - if (PyModule_AddIntMacro(m, PRIO_DARWIN_NONUI)) return -1; -#endif -#ifdef O_CLOEXEC - if (PyModule_AddIntMacro(m, O_CLOEXEC)) return -1; -#endif -#ifdef O_ACCMODE - if (PyModule_AddIntMacro(m, O_ACCMODE)) return -1; -#endif -#ifdef O_EVTONLY - if (PyModule_AddIntMacro(m, O_EVTONLY)) return -1; -#endif -#ifdef O_FSYNC - if (PyModule_AddIntMacro(m, O_FSYNC)) return -1; -#endif -#ifdef O_SYMLINK - if (PyModule_AddIntMacro(m, O_SYMLINK)) return -1; -#endif - -#ifdef SEEK_HOLE - if (PyModule_AddIntMacro(m, SEEK_HOLE)) return -1; -#endif -#ifdef SEEK_DATA - if (PyModule_AddIntMacro(m, SEEK_DATA)) return -1; -#endif - -/* MS Windows */ -#ifdef O_NOINHERIT - /* Don't inherit in child processes. */ - if (PyModule_AddIntMacro(m, O_NOINHERIT)) return -1; -#endif -#ifdef _O_SHORT_LIVED - /* Optimize for short life (keep in memory). */ - /* MS forgot to define this one with a non-underscore form too. */ - if (PyModule_AddIntConstant(m, "O_SHORT_LIVED", _O_SHORT_LIVED)) return -1; -#endif -#ifdef O_TEMPORARY - /* Automatically delete when last handle is closed. */ - if (PyModule_AddIntMacro(m, O_TEMPORARY)) return -1; -#endif -#ifdef O_RANDOM - /* Optimize for random access. */ - if (PyModule_AddIntMacro(m, O_RANDOM)) return -1; -#endif -#ifdef O_SEQUENTIAL - /* Optimize for sequential access. */ - if (PyModule_AddIntMacro(m, O_SEQUENTIAL)) return -1; -#endif - -/* GNU extensions. */ -#ifdef O_ASYNC - /* Send a SIGIO signal whenever input or output - becomes available on file descriptor */ - if (PyModule_AddIntMacro(m, O_ASYNC)) return -1; -#endif -#ifdef O_DIRECT - /* Direct disk access. */ - if (PyModule_AddIntMacro(m, O_DIRECT)) return -1; -#endif -#ifdef O_DIRECTORY - /* Must be a directory. */ - if (PyModule_AddIntMacro(m, O_DIRECTORY)) return -1; -#endif -#ifdef O_NOFOLLOW - /* Do not follow links. */ - if (PyModule_AddIntMacro(m, O_NOFOLLOW)) return -1; -#endif -#ifdef O_NOFOLLOW_ANY - if (PyModule_AddIntMacro(m, O_NOFOLLOW_ANY)) return -1; -#endif -#ifdef O_NOLINKS - /* Fails if link count of the named file is greater than 1 */ - if (PyModule_AddIntMacro(m, O_NOLINKS)) return -1; -#endif -#ifdef O_NOATIME - /* Do not update the access time. */ - if (PyModule_AddIntMacro(m, O_NOATIME)) return -1; -#endif - - /* These come from sysexits.h */ -#ifdef EX_OK - if (PyModule_AddIntMacro(m, EX_OK)) return -1; -#endif /* EX_OK */ -#ifdef EX_USAGE - if (PyModule_AddIntMacro(m, EX_USAGE)) return -1; -#endif /* EX_USAGE */ -#ifdef EX_DATAERR - if (PyModule_AddIntMacro(m, EX_DATAERR)) return -1; -#endif /* EX_DATAERR */ -#ifdef EX_NOINPUT - if (PyModule_AddIntMacro(m, EX_NOINPUT)) return -1; -#endif /* EX_NOINPUT */ -#ifdef EX_NOUSER - if (PyModule_AddIntMacro(m, EX_NOUSER)) return -1; -#endif /* EX_NOUSER */ -#ifdef EX_NOHOST - if (PyModule_AddIntMacro(m, EX_NOHOST)) return -1; -#endif /* EX_NOHOST */ -#ifdef EX_UNAVAILABLE - if (PyModule_AddIntMacro(m, EX_UNAVAILABLE)) return -1; -#endif /* EX_UNAVAILABLE */ -#ifdef EX_SOFTWARE - if (PyModule_AddIntMacro(m, EX_SOFTWARE)) return -1; -#endif /* EX_SOFTWARE */ -#ifdef EX_OSERR - if (PyModule_AddIntMacro(m, EX_OSERR)) return -1; -#endif /* EX_OSERR */ -#ifdef EX_OSFILE - if (PyModule_AddIntMacro(m, EX_OSFILE)) return -1; -#endif /* EX_OSFILE */ -#ifdef EX_CANTCREAT - if (PyModule_AddIntMacro(m, EX_CANTCREAT)) return -1; -#endif /* EX_CANTCREAT */ -#ifdef EX_IOERR - if (PyModule_AddIntMacro(m, EX_IOERR)) return -1; -#endif /* EX_IOERR */ -#ifdef EX_TEMPFAIL - if (PyModule_AddIntMacro(m, EX_TEMPFAIL)) return -1; -#endif /* EX_TEMPFAIL */ -#ifdef EX_PROTOCOL - if (PyModule_AddIntMacro(m, EX_PROTOCOL)) return -1; -#endif /* EX_PROTOCOL */ -#ifdef EX_NOPERM - if (PyModule_AddIntMacro(m, EX_NOPERM)) return -1; -#endif /* EX_NOPERM */ -#ifdef EX_CONFIG - if (PyModule_AddIntMacro(m, EX_CONFIG)) return -1; -#endif /* EX_CONFIG */ -#ifdef EX_NOTFOUND - if (PyModule_AddIntMacro(m, EX_NOTFOUND)) return -1; -#endif /* EX_NOTFOUND */ - - /* statvfs */ -#ifdef ST_RDONLY - if (PyModule_AddIntMacro(m, ST_RDONLY)) return -1; -#endif /* ST_RDONLY */ -#ifdef ST_NOSUID - if (PyModule_AddIntMacro(m, ST_NOSUID)) return -1; -#endif /* ST_NOSUID */ - - /* GNU extensions */ -#ifdef ST_NODEV - if (PyModule_AddIntMacro(m, ST_NODEV)) return -1; -#endif /* ST_NODEV */ -#ifdef ST_NOEXEC - if (PyModule_AddIntMacro(m, ST_NOEXEC)) return -1; -#endif /* ST_NOEXEC */ -#ifdef ST_SYNCHRONOUS - if (PyModule_AddIntMacro(m, ST_SYNCHRONOUS)) return -1; -#endif /* ST_SYNCHRONOUS */ -#ifdef ST_MANDLOCK - if (PyModule_AddIntMacro(m, ST_MANDLOCK)) return -1; -#endif /* ST_MANDLOCK */ -#ifdef ST_WRITE - if (PyModule_AddIntMacro(m, ST_WRITE)) return -1; -#endif /* ST_WRITE */ -#ifdef ST_APPEND - if (PyModule_AddIntMacro(m, ST_APPEND)) return -1; -#endif /* ST_APPEND */ -#ifdef ST_NOATIME - if (PyModule_AddIntMacro(m, ST_NOATIME)) return -1; -#endif /* ST_NOATIME */ -#ifdef ST_NODIRATIME - if (PyModule_AddIntMacro(m, ST_NODIRATIME)) return -1; -#endif /* ST_NODIRATIME */ -#ifdef ST_RELATIME - if (PyModule_AddIntMacro(m, ST_RELATIME)) return -1; -#endif /* ST_RELATIME */ - - /* FreeBSD sendfile() constants */ -#ifdef SF_NODISKIO - if (PyModule_AddIntMacro(m, SF_NODISKIO)) return -1; -#endif - /* is obsolete since the 11.x release */ -#ifdef SF_MNOWAIT - if (PyModule_AddIntMacro(m, SF_MNOWAIT)) return -1; -#endif -#ifdef SF_SYNC - if (PyModule_AddIntMacro(m, SF_SYNC)) return -1; -#endif -#ifdef SF_NOCACHE - if (PyModule_AddIntMacro(m, SF_NOCACHE)) return -1; -#endif - -#ifdef TFD_NONBLOCK - if (PyModule_AddIntMacro(m, TFD_NONBLOCK)) return -1; -#endif -#ifdef TFD_CLOEXEC - if (PyModule_AddIntMacro(m, TFD_CLOEXEC)) return -1; -#endif -#ifdef TFD_TIMER_ABSTIME - if (PyModule_AddIntMacro(m, TFD_TIMER_ABSTIME)) return -1; -#endif -#ifdef TFD_TIMER_CANCEL_ON_SET - if (PyModule_AddIntMacro(m, TFD_TIMER_CANCEL_ON_SET)) return -1; -#endif - - /* constants for posix_fadvise */ -#ifdef POSIX_FADV_NORMAL - if (PyModule_AddIntMacro(m, POSIX_FADV_NORMAL)) return -1; -#endif -#ifdef POSIX_FADV_SEQUENTIAL - if (PyModule_AddIntMacro(m, POSIX_FADV_SEQUENTIAL)) return -1; -#endif -#ifdef POSIX_FADV_RANDOM - if (PyModule_AddIntMacro(m, POSIX_FADV_RANDOM)) return -1; -#endif -#ifdef POSIX_FADV_NOREUSE - if (PyModule_AddIntMacro(m, POSIX_FADV_NOREUSE)) return -1; -#endif -#ifdef POSIX_FADV_WILLNEED - if (PyModule_AddIntMacro(m, POSIX_FADV_WILLNEED)) return -1; -#endif -#ifdef POSIX_FADV_DONTNEED - if (PyModule_AddIntMacro(m, POSIX_FADV_DONTNEED)) return -1; -#endif - - /* constants for waitid */ -#if defined(HAVE_SYS_WAIT_H) && defined(HAVE_WAITID) - if (PyModule_AddIntMacro(m, P_PID)) return -1; - if (PyModule_AddIntMacro(m, P_PGID)) return -1; - if (PyModule_AddIntMacro(m, P_ALL)) return -1; -#ifdef P_PIDFD - if (PyModule_AddIntMacro(m, P_PIDFD)) return -1; -#endif -#ifdef PIDFD_NONBLOCK - if (PyModule_AddIntMacro(m, PIDFD_NONBLOCK)) return -1; -#endif -#endif -#ifdef WEXITED - if (PyModule_AddIntMacro(m, WEXITED)) return -1; -#endif -#ifdef WNOWAIT - if (PyModule_AddIntMacro(m, WNOWAIT)) return -1; -#endif -#ifdef WSTOPPED - if (PyModule_AddIntMacro(m, WSTOPPED)) return -1; -#endif -#ifdef CLD_EXITED - if (PyModule_AddIntMacro(m, CLD_EXITED)) return -1; -#endif -#ifdef CLD_KILLED - if (PyModule_AddIntMacro(m, CLD_KILLED)) return -1; -#endif -#ifdef CLD_DUMPED - if (PyModule_AddIntMacro(m, CLD_DUMPED)) return -1; -#endif -#ifdef CLD_TRAPPED - if (PyModule_AddIntMacro(m, CLD_TRAPPED)) return -1; -#endif -#ifdef CLD_STOPPED - if (PyModule_AddIntMacro(m, CLD_STOPPED)) return -1; -#endif -#ifdef CLD_CONTINUED - if (PyModule_AddIntMacro(m, CLD_CONTINUED)) return -1; -#endif - - /* constants for lockf */ -#ifdef F_LOCK - if (PyModule_AddIntMacro(m, F_LOCK)) return -1; -#endif -#ifdef F_TLOCK - if (PyModule_AddIntMacro(m, F_TLOCK)) return -1; -#endif -#ifdef F_ULOCK - if (PyModule_AddIntMacro(m, F_ULOCK)) return -1; -#endif -#ifdef F_TEST - if (PyModule_AddIntMacro(m, F_TEST)) return -1; -#endif - -#ifdef RWF_DSYNC - if (PyModule_AddIntConstant(m, "RWF_DSYNC", RWF_DSYNC)) return -1; -#endif -#ifdef RWF_HIPRI - if (PyModule_AddIntConstant(m, "RWF_HIPRI", RWF_HIPRI)) return -1; -#endif -#ifdef RWF_SYNC - if (PyModule_AddIntConstant(m, "RWF_SYNC", RWF_SYNC)) return -1; -#endif -#ifdef RWF_NOWAIT - if (PyModule_AddIntConstant(m, "RWF_NOWAIT", RWF_NOWAIT)) return -1; -#endif -#ifdef RWF_APPEND - if (PyModule_AddIntConstant(m, "RWF_APPEND", RWF_APPEND)) return -1; -#endif - -/* constants for splice */ -#if defined(HAVE_SPLICE) && defined(__linux__) - if (PyModule_AddIntConstant(m, "SPLICE_F_MOVE", SPLICE_F_MOVE)) return -1; - if (PyModule_AddIntConstant(m, "SPLICE_F_NONBLOCK", SPLICE_F_NONBLOCK)) return -1; - if (PyModule_AddIntConstant(m, "SPLICE_F_MORE", SPLICE_F_MORE)) return -1; -#endif - -/* constants for posix_spawn */ -#ifdef HAVE_POSIX_SPAWN - if (PyModule_AddIntConstant(m, "POSIX_SPAWN_OPEN", POSIX_SPAWN_OPEN)) return -1; - if (PyModule_AddIntConstant(m, "POSIX_SPAWN_CLOSE", POSIX_SPAWN_CLOSE)) return -1; - if (PyModule_AddIntConstant(m, "POSIX_SPAWN_DUP2", POSIX_SPAWN_DUP2)) return -1; -#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP - if (PyModule_AddIntMacro(m, POSIX_SPAWN_CLOSEFROM)) return -1; -#endif -#endif - -#if defined(HAVE_SPAWNV) || defined (HAVE_RTPSPAWN) - if (PyModule_AddIntConstant(m, "P_WAIT", _P_WAIT)) return -1; - if (PyModule_AddIntConstant(m, "P_NOWAIT", _P_NOWAIT)) return -1; - if (PyModule_AddIntConstant(m, "P_NOWAITO", _P_NOWAITO)) return -1; -#endif -#ifdef HAVE_SPAWNV - if (PyModule_AddIntConstant(m, "P_OVERLAY", _OLD_P_OVERLAY)) return -1; - if (PyModule_AddIntConstant(m, "P_DETACH", _P_DETACH)) return -1; -#endif - -#ifdef HAVE_SCHED_H -#ifdef SCHED_OTHER - if (PyModule_AddIntMacro(m, SCHED_OTHER)) return -1; -#endif -#ifdef SCHED_FIFO - if (PyModule_AddIntMacro(m, SCHED_FIFO)) return -1; -#endif -#ifdef SCHED_RR - if (PyModule_AddIntMacro(m, SCHED_RR)) return -1; -#endif -#ifdef SCHED_SPORADIC - if (PyModule_AddIntMacro(m, SCHED_SPORADIC)) return -1; -#endif -#ifdef SCHED_BATCH - if (PyModule_AddIntMacro(m, SCHED_BATCH)) return -1; -#endif -#ifdef SCHED_IDLE - if (PyModule_AddIntMacro(m, SCHED_IDLE)) return -1; -#endif -#ifdef SCHED_RESET_ON_FORK - if (PyModule_AddIntMacro(m, SCHED_RESET_ON_FORK)) return -1; -#endif -#ifdef SCHED_SYS - if (PyModule_AddIntMacro(m, SCHED_SYS)) return -1; -#endif -#ifdef SCHED_IA - if (PyModule_AddIntMacro(m, SCHED_IA)) return -1; -#endif -#ifdef SCHED_FSS - if (PyModule_AddIntMacro(m, SCHED_FSS)) return -1; -#endif -#ifdef SCHED_FX - if (PyModule_AddIntConstant(m, "SCHED_FX", SCHED_FSS)) return -1; -#endif - -/* constants for namespaces */ -#if defined(HAVE_SETNS) || defined(HAVE_UNSHARE) -#ifdef CLONE_FS - if (PyModule_AddIntMacro(m, CLONE_FS)) return -1; -#endif -#ifdef CLONE_FILES - if (PyModule_AddIntMacro(m, CLONE_FILES)) return -1; -#endif -#ifdef CLONE_NEWNS - if (PyModule_AddIntMacro(m, CLONE_NEWNS)) return -1; -#endif -#ifdef CLONE_NEWCGROUP - if (PyModule_AddIntMacro(m, CLONE_NEWCGROUP)) return -1; -#endif -#ifdef CLONE_NEWUTS - if (PyModule_AddIntMacro(m, CLONE_NEWUTS)) return -1; -#endif -#ifdef CLONE_NEWIPC - if (PyModule_AddIntMacro(m, CLONE_NEWIPC)) return -1; -#endif -#ifdef CLONE_NEWUSER - if (PyModule_AddIntMacro(m, CLONE_NEWUSER)) return -1; -#endif -#ifdef CLONE_NEWPID - if (PyModule_AddIntMacro(m, CLONE_NEWPID)) return -1; -#endif -#ifdef CLONE_NEWNET - if (PyModule_AddIntMacro(m, CLONE_NEWNET)) return -1; -#endif -#ifdef CLONE_NEWTIME - if (PyModule_AddIntMacro(m, CLONE_NEWTIME)) return -1; -#endif -#ifdef CLONE_SYSVSEM - if (PyModule_AddIntMacro(m, CLONE_SYSVSEM)) return -1; -#endif -#ifdef CLONE_THREAD - if (PyModule_AddIntMacro(m, CLONE_THREAD)) return -1; -#endif -#ifdef CLONE_SIGHAND - if (PyModule_AddIntMacro(m, CLONE_SIGHAND)) return -1; -#endif -#ifdef CLONE_VM - if (PyModule_AddIntMacro(m, CLONE_VM)) return -1; -#endif -#endif - -#endif - -#ifdef USE_XATTRS - if (PyModule_AddIntMacro(m, XATTR_CREATE)) return -1; - if (PyModule_AddIntMacro(m, XATTR_REPLACE)) return -1; - if (PyModule_AddIntMacro(m, XATTR_SIZE_MAX)) return -1; -#endif - -#if HAVE_DECL_RTLD_LAZY - if (PyModule_AddIntMacro(m, RTLD_LAZY)) return -1; -#endif -#if HAVE_DECL_RTLD_NOW - if (PyModule_AddIntMacro(m, RTLD_NOW)) return -1; -#endif -#if HAVE_DECL_RTLD_GLOBAL - if (PyModule_AddIntMacro(m, RTLD_GLOBAL)) return -1; -#endif -#if HAVE_DECL_RTLD_LOCAL - if (PyModule_AddIntMacro(m, RTLD_LOCAL)) return -1; -#endif -#if HAVE_DECL_RTLD_NODELETE - if (PyModule_AddIntMacro(m, RTLD_NODELETE)) return -1; -#endif -#if HAVE_DECL_RTLD_NOLOAD - if (PyModule_AddIntMacro(m, RTLD_NOLOAD)) return -1; -#endif -#if HAVE_DECL_RTLD_DEEPBIND - if (PyModule_AddIntMacro(m, RTLD_DEEPBIND)) return -1; -#endif -#if HAVE_DECL_RTLD_MEMBER - if (PyModule_AddIntMacro(m, RTLD_MEMBER)) return -1; -#endif - -#ifdef HAVE_GETRANDOM_SYSCALL - if (PyModule_AddIntMacro(m, GRND_RANDOM)) return -1; - if (PyModule_AddIntMacro(m, GRND_NONBLOCK)) return -1; -#endif -#ifdef HAVE_MEMFD_CREATE - if (PyModule_AddIntMacro(m, MFD_CLOEXEC)) return -1; - if (PyModule_AddIntMacro(m, MFD_ALLOW_SEALING)) return -1; -#ifdef MFD_HUGETLB - if (PyModule_AddIntMacro(m, MFD_HUGETLB)) return -1; -#endif -#ifdef MFD_HUGE_SHIFT - if (PyModule_AddIntMacro(m, MFD_HUGE_SHIFT)) return -1; -#endif -#ifdef MFD_HUGE_MASK - if (PyModule_AddIntMacro(m, MFD_HUGE_MASK)) return -1; -#endif -#ifdef MFD_HUGE_64KB - if (PyModule_AddIntMacro(m, MFD_HUGE_64KB)) return -1; -#endif -#ifdef MFD_HUGE_512KB - if (PyModule_AddIntMacro(m, MFD_HUGE_512KB)) return -1; -#endif -#ifdef MFD_HUGE_1MB - if (PyModule_AddIntMacro(m, MFD_HUGE_1MB)) return -1; -#endif -#ifdef MFD_HUGE_2MB - if (PyModule_AddIntMacro(m, MFD_HUGE_2MB)) return -1; -#endif -#ifdef MFD_HUGE_8MB - if (PyModule_AddIntMacro(m, MFD_HUGE_8MB)) return -1; -#endif -#ifdef MFD_HUGE_16MB - if (PyModule_AddIntMacro(m, MFD_HUGE_16MB)) return -1; -#endif -#ifdef MFD_HUGE_32MB - if (PyModule_AddIntMacro(m, MFD_HUGE_32MB)) return -1; -#endif -#ifdef MFD_HUGE_256MB - if (PyModule_AddIntMacro(m, MFD_HUGE_256MB)) return -1; -#endif -#ifdef MFD_HUGE_512MB - if (PyModule_AddIntMacro(m, MFD_HUGE_512MB)) return -1; -#endif -#ifdef MFD_HUGE_1GB - if (PyModule_AddIntMacro(m, MFD_HUGE_1GB)) return -1; -#endif -#ifdef MFD_HUGE_2GB - if (PyModule_AddIntMacro(m, MFD_HUGE_2GB)) return -1; -#endif -#ifdef MFD_HUGE_16GB - if (PyModule_AddIntMacro(m, MFD_HUGE_16GB)) return -1; -#endif -#endif /* HAVE_MEMFD_CREATE */ - -#if defined(HAVE_EVENTFD) && defined(EFD_CLOEXEC) - if (PyModule_AddIntMacro(m, EFD_CLOEXEC)) return -1; -#ifdef EFD_NONBLOCK - if (PyModule_AddIntMacro(m, EFD_NONBLOCK)) return -1; -#endif -#ifdef EFD_SEMAPHORE - if (PyModule_AddIntMacro(m, EFD_SEMAPHORE)) return -1; -#endif -#endif /* HAVE_EVENTFD && EFD_CLOEXEC */ - -#if defined(__APPLE__) - if (PyModule_AddIntConstant(m, "_COPYFILE_DATA", COPYFILE_DATA)) return -1; - if (PyModule_AddIntConstant(m, "_COPYFILE_STAT", COPYFILE_STAT)) return -1; - if (PyModule_AddIntConstant(m, "_COPYFILE_ACL", COPYFILE_ACL)) return -1; - if (PyModule_AddIntConstant(m, "_COPYFILE_XATTR", COPYFILE_XATTR)) return -1; -#endif - -#ifdef MS_WINDOWS - if (PyModule_AddIntConstant(m, "_LOAD_LIBRARY_SEARCH_DEFAULT_DIRS", LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)) return -1; - if (PyModule_AddIntConstant(m, "_LOAD_LIBRARY_SEARCH_APPLICATION_DIR", LOAD_LIBRARY_SEARCH_APPLICATION_DIR)) return -1; - if (PyModule_AddIntConstant(m, "_LOAD_LIBRARY_SEARCH_SYSTEM32", LOAD_LIBRARY_SEARCH_SYSTEM32)) return -1; - if (PyModule_AddIntConstant(m, "_LOAD_LIBRARY_SEARCH_USER_DIRS", LOAD_LIBRARY_SEARCH_USER_DIRS)) return -1; - if (PyModule_AddIntConstant(m, "_LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR", LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR)) return -1; -#endif - - return 0; -} - - - -#define PROBE(name, test) \ - static int name(void) \ - { \ - if (test) { \ - return 1; \ - } else { \ - return 0; \ - } \ - } - -#ifdef HAVE_FSTATAT -PROBE(probe_fstatat, HAVE_FSTATAT_RUNTIME) -#endif - -#ifdef HAVE_FACCESSAT -PROBE(probe_faccessat, HAVE_FACCESSAT_RUNTIME) -#endif - -#ifdef HAVE_FCHMODAT -PROBE(probe_fchmodat, HAVE_FCHMODAT_RUNTIME) -#endif - -#ifdef HAVE_FCHOWNAT -PROBE(probe_fchownat, HAVE_FCHOWNAT_RUNTIME) -#endif - -#ifdef HAVE_LINKAT -PROBE(probe_linkat, HAVE_LINKAT_RUNTIME) -#endif - -#ifdef HAVE_FDOPENDIR -PROBE(probe_fdopendir, HAVE_FDOPENDIR_RUNTIME) -#endif - -#ifdef HAVE_MKDIRAT -PROBE(probe_mkdirat, HAVE_MKDIRAT_RUNTIME) -#endif - -#ifdef HAVE_MKFIFOAT -PROBE(probe_mkfifoat, HAVE_MKFIFOAT_RUNTIME) -#endif - -#ifdef HAVE_MKNODAT -PROBE(probe_mknodat, HAVE_MKNODAT_RUNTIME) -#endif - -#ifdef HAVE_RENAMEAT -PROBE(probe_renameat, HAVE_RENAMEAT_RUNTIME) -#endif - -#ifdef HAVE_UNLINKAT -PROBE(probe_unlinkat, HAVE_UNLINKAT_RUNTIME) -#endif - -#ifdef HAVE_OPENAT -PROBE(probe_openat, HAVE_OPENAT_RUNTIME) -#endif - -#ifdef HAVE_READLINKAT -PROBE(probe_readlinkat, HAVE_READLINKAT_RUNTIME) -#endif - -#ifdef HAVE_SYMLINKAT -PROBE(probe_symlinkat, HAVE_SYMLINKAT_RUNTIME) -#endif - -#ifdef HAVE_FUTIMENS -PROBE(probe_futimens, HAVE_FUTIMENS_RUNTIME) -#endif - -#ifdef HAVE_UTIMENSAT -PROBE(probe_utimensat, HAVE_UTIMENSAT_RUNTIME) -#endif - -#ifdef HAVE_PTSNAME_R -PROBE(probe_ptsname_r, HAVE_PTSNAME_R_RUNTIME) -#endif - - - -static const struct have_function { - const char * const label; - int (*probe)(void); -} have_functions[] = { - -#ifdef HAVE_EVENTFD - {"HAVE_EVENTFD", NULL}, -#endif - -#ifdef HAVE_TIMERFD_CREATE - {"HAVE_TIMERFD_CREATE", NULL}, -#endif - -#ifdef HAVE_FACCESSAT - { "HAVE_FACCESSAT", probe_faccessat }, -#endif - -#ifdef HAVE_FCHDIR - { "HAVE_FCHDIR", NULL }, -#endif - -#ifdef HAVE_FCHMOD - { "HAVE_FCHMOD", NULL }, -#endif - -#ifdef HAVE_FCHMODAT - { "HAVE_FCHMODAT", probe_fchmodat }, -#endif - -#ifdef HAVE_FCHOWN - { "HAVE_FCHOWN", NULL }, -#endif - -#ifdef HAVE_FCHOWNAT - { "HAVE_FCHOWNAT", probe_fchownat }, -#endif - -#ifdef HAVE_FEXECVE - { "HAVE_FEXECVE", NULL }, -#endif - -#ifdef HAVE_FDOPENDIR - { "HAVE_FDOPENDIR", probe_fdopendir }, -#endif - -#ifdef HAVE_FPATHCONF - { "HAVE_FPATHCONF", NULL }, -#endif - -#ifdef HAVE_FSTATAT - { "HAVE_FSTATAT", probe_fstatat }, -#endif - -#ifdef HAVE_FSTATVFS - { "HAVE_FSTATVFS", NULL }, -#endif - -#if defined HAVE_FTRUNCATE || defined MS_WINDOWS - { "HAVE_FTRUNCATE", NULL }, -#endif - -#ifdef HAVE_FUTIMENS - { "HAVE_FUTIMENS", probe_futimens }, -#endif - -#ifdef HAVE_FUTIMES - { "HAVE_FUTIMES", NULL }, -#endif - -#ifdef HAVE_FUTIMESAT - { "HAVE_FUTIMESAT", NULL }, -#endif - -#ifdef HAVE_LINKAT - { "HAVE_LINKAT", probe_linkat }, -#endif - -#ifdef HAVE_LCHFLAGS - { "HAVE_LCHFLAGS", NULL }, -#endif - -#ifdef HAVE_LCHMOD - { "HAVE_LCHMOD", NULL }, -#endif - -#ifdef HAVE_LCHOWN - { "HAVE_LCHOWN", NULL }, -#endif - -#ifdef HAVE_LSTAT - { "HAVE_LSTAT", NULL }, -#endif - -#ifdef HAVE_LUTIMES - { "HAVE_LUTIMES", NULL }, -#endif - -#ifdef HAVE_MEMFD_CREATE - { "HAVE_MEMFD_CREATE", NULL }, -#endif - -#ifdef HAVE_MKDIRAT - { "HAVE_MKDIRAT", probe_mkdirat }, -#endif - -#ifdef HAVE_MKFIFOAT - { "HAVE_MKFIFOAT", probe_mkfifoat }, -#endif - -#ifdef HAVE_MKNODAT - { "HAVE_MKNODAT", probe_mknodat }, -#endif - -#ifdef HAVE_OPENAT - { "HAVE_OPENAT", probe_openat }, -#endif - -#ifdef HAVE_READLINKAT - { "HAVE_READLINKAT", probe_readlinkat }, -#endif - -#ifdef HAVE_RENAMEAT - { "HAVE_RENAMEAT", probe_renameat }, -#endif - -#ifdef HAVE_SYMLINKAT - { "HAVE_SYMLINKAT", probe_symlinkat }, -#endif - -#ifdef HAVE_UNLINKAT - { "HAVE_UNLINKAT", probe_unlinkat }, -#endif - -#ifdef HAVE_UTIMENSAT - { "HAVE_UTIMENSAT", probe_utimensat }, -#endif - -#ifdef HAVE_PTSNAME_R - { "HAVE_PTSNAME_R", probe_ptsname_r }, -#endif - -#ifdef MS_WINDOWS - { "MS_WINDOWS", NULL }, -#endif - - { NULL, NULL } -}; - - -static int -posixmodule_exec(PyObject *m) -{ - _posixstate *state = get_posix_state(m); - -#if defined(HAVE_PWRITEV) - if (HAVE_PWRITEV_RUNTIME) {} else { - PyObject* dct = PyModule_GetDict(m); - - if (dct == NULL) { - return -1; - } - - if (PyDict_PopString(dct, "pwritev", NULL) < 0) { - return -1; - } - if (PyDict_PopString(dct, "preadv", NULL) < 0) { - return -1; - } - } -#endif - - /* Initialize environ dictionary */ - if (PyModule_Add(m, "environ", convertenviron()) != 0) { - return -1; - } - - if (all_ins(m)) - return -1; - - if (setup_confname_tables(m)) - return -1; - - if (PyModule_AddObjectRef(m, "error", PyExc_OSError) < 0) { - return -1; - } - -#if defined(HAVE_WAITID) - waitid_result_desc.name = MODNAME ".waitid_result"; - state->WaitidResultType = (PyObject *)PyStructSequence_NewType(&waitid_result_desc); - if (PyModule_AddObjectRef(m, "waitid_result", state->WaitidResultType) < 0) { - return -1; - } -#endif - - stat_result_desc.name = "os.stat_result"; /* see issue #19209 */ - stat_result_desc.fields[7].name = PyStructSequence_UnnamedField; - stat_result_desc.fields[8].name = PyStructSequence_UnnamedField; - stat_result_desc.fields[9].name = PyStructSequence_UnnamedField; - state->StatResultType = (PyObject *)PyStructSequence_NewType(&stat_result_desc); - if (PyModule_AddObjectRef(m, "stat_result", state->StatResultType) < 0) { - return -1; - } - state->statresult_new_orig = ((PyTypeObject *)state->StatResultType)->tp_new; - ((PyTypeObject *)state->StatResultType)->tp_new = statresult_new; - - statvfs_result_desc.name = "os.statvfs_result"; /* see issue #19209 */ - state->StatVFSResultType = (PyObject *)PyStructSequence_NewType(&statvfs_result_desc); - if (PyModule_AddObjectRef(m, "statvfs_result", state->StatVFSResultType) < 0) { - return -1; - } - -#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) - sched_param_desc.name = MODNAME ".sched_param"; - state->SchedParamType = (PyObject *)PyStructSequence_NewType(&sched_param_desc); - if (PyModule_AddObjectRef(m, "sched_param", state->SchedParamType) < 0) { - return -1; - } - ((PyTypeObject *)state->SchedParamType)->tp_new = os_sched_param; - if (_PyType_AddMethod((PyTypeObject *)state->SchedParamType, - &os_sched_param_reduce_method) < 0) - { - return -1; - } - PyType_Modified((PyTypeObject *)state->SchedParamType); -#endif - - /* initialize TerminalSize_info */ - state->TerminalSizeType = (PyObject *)PyStructSequence_NewType(&TerminalSize_desc); - if (PyModule_AddObjectRef(m, "terminal_size", state->TerminalSizeType) < 0) { - return -1; - } - - /* initialize scandir types */ - PyObject *ScandirIteratorType = PyType_FromModuleAndSpec(m, &ScandirIteratorType_spec, NULL); - if (ScandirIteratorType == NULL) { - return -1; - } - state->ScandirIteratorType = ScandirIteratorType; - - state->DirEntryType = PyType_FromModuleAndSpec(m, &DirEntryType_spec, NULL); - if (PyModule_AddObjectRef(m, "DirEntry", state->DirEntryType) < 0) { - return -1; - } - - times_result_desc.name = MODNAME ".times_result"; - state->TimesResultType = (PyObject *)PyStructSequence_NewType(×_result_desc); - if (PyModule_AddObjectRef(m, "times_result", state->TimesResultType) < 0) { - return -1; - } - - state->UnameResultType = (PyObject *)PyStructSequence_NewType(&uname_result_desc); - if (PyModule_AddObjectRef(m, "uname_result", state->UnameResultType) < 0) { - return -1; - } - - if ((state->billion = PyLong_FromLong(1000000000)) == NULL) - return -1; -#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4) - state->struct_rusage = PyUnicode_InternFromString("struct_rusage"); - if (state->struct_rusage == NULL) - return -1; -#endif - state->st_mode = PyUnicode_InternFromString("st_mode"); - if (state->st_mode == NULL) - return -1; - - /* suppress "function not used" warnings */ - { - int ignored; - fd_specified("", -1); - follow_symlinks_specified("", 1); - dir_fd_and_follow_symlinks_invalid("chmod", DEFAULT_DIR_FD, 1); - dir_fd_converter(Py_None, &ignored); - dir_fd_unavailable(Py_None, &ignored); - } - - /* - * provide list of locally available functions - * so os.py can populate support_* lists - */ - PyObject *list = PyList_New(0); - if (!list) { - return -1; - } - for (const struct have_function *trace = have_functions; trace->label; trace++) { - PyObject *unicode; - if (trace->probe && !trace->probe()) continue; - unicode = PyUnicode_DecodeASCII(trace->label, strlen(trace->label), NULL); - if (!unicode) - return -1; - if (PyList_Append(list, unicode)) - return -1; - Py_DECREF(unicode); - } - -#ifndef MS_WINDOWS - if (_Py_GetTicksPerSecond(&state->ticks_per_second) < 0) { - PyErr_SetString(PyExc_RuntimeError, - "cannot read ticks_per_second"); - return -1; - } - assert(state->ticks_per_second >= 1); -#endif - - return PyModule_Add(m, "_have_functions", list); -} - - -static PyModuleDef_Slot posixmodile_slots[] = { - {Py_mod_exec, posixmodule_exec}, - {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, - {Py_mod_gil, Py_MOD_GIL_NOT_USED}, - {0, NULL} -}; - -static struct PyModuleDef posixmodule = { - PyModuleDef_HEAD_INIT, - .m_name = MODNAME, - .m_doc = posix__doc__, - .m_size = sizeof(_posixstate), - .m_methods = posix_methods, - .m_slots = posixmodile_slots, - .m_traverse = _posix_traverse, - .m_clear = _posix_clear, - .m_free = _posix_free, -}; - -PyMODINIT_FUNC -INITFUNC(void) -{ - return PyModuleDef_Init(&posixmodule); -} +/* POSIX module implementation */ + +/* This file is also used for Windows NT/MS-Win. In that case the + module actually calls itself 'nt', not 'posix', and a few + functions are either unimplemented or implemented differently. The source + assumes that for Windows NT, the macro 'MS_WINDOWS' is defined independent + of the compiler used. Different compilers define their own feature + test macro, e.g. '_MSC_VER'. */ + +#include "Python.h" + +#ifdef __VXWORKS__ +# include "pycore_bitutils.h" // _Py_popcount32() +#endif +#include "pycore_abstract.h" // _PyNumber_Index() +#include "pycore_call.h" // _PyObject_CallNoArgs() +#include "pycore_ceval.h" // _PyEval_ReInitThreads() +#include "pycore_fileutils.h" // _Py_closerange() +#include "pycore_initconfig.h" // _PyStatus_EXCEPTION() +#include "pycore_long.h" // _PyLong_IsNegative() +#include "pycore_moduleobject.h" // _PyModule_GetState() +#include "pycore_object.h" // _PyObject_LookupSpecial() +#include "pycore_pylifecycle.h" // _PyOS_URandom() +#include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_signal.h" // Py_NSIG +#include "pycore_time.h" // _PyLong_FromTime_t() +#include "pycore_typeobject.h" // _PyType_AddMethod() + +#ifdef HAVE_UNISTD_H +# include // symlink() +#endif + +#ifdef MS_WINDOWS +# include +# if !defined(MS_WINDOWS_GAMES) || defined(MS_WINDOWS_DESKTOP) +# include +# endif +# include +# include // UNLEN +# include "osdefs.h" // SEP +# include // SetEntriesInAcl +# include // SDDL_REVISION_1 +# if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) +# define HAVE_SYMLINK +# endif /* MS_WINDOWS_DESKTOP | MS_WINDOWS_SYSTEM */ +#endif + +#ifndef MS_WINDOWS +# include "posixmodule.h" +#else +# include "pycore_fileutils_windows.h" +# include "winreparse.h" +#endif + +#if !defined(EX_OK) && defined(EXIT_SUCCESS) +# define EX_OK EXIT_SUCCESS +#endif + +#ifdef __APPLE__ + /* Needed for the implementation of os.statvfs */ +# include +# include +#endif + +/* On android API level 21, 'AT_EACCESS' is not declared although + * HAVE_FACCESSAT is defined. */ +#ifdef __ANDROID__ +# undef HAVE_FACCESSAT +#endif +#include "iscygpty.h" + +#include // ctermid() +#include // system() +#ifdef HAVE_SYS_TIME_H +# include // futimes() +#endif + + +// SGI apparently needs this forward declaration +#ifdef HAVE__GETPTY +# include // mode_t + extern char * _getpty(int *, int, mode_t, int); +#endif + + +/* + * A number of APIs are available on macOS from a certain macOS version. + * To support building with a new SDK while deploying to older versions + * the availability test is split into two: + * - HAVE_: The configure check for compile time availability + * - HAVE__RUNTIME: Runtime check for availability + * + * The latter is always true when not on macOS, or when using a compiler + * that does not support __has_builtin (older versions of Xcode). + * + * Due to compiler restrictions there is one valid use of HAVE__RUNTIME: + * if (HAVE__RUNTIME) { ... } + * + * In mixing the test with other tests or using negations will result in compile + * errors. + */ +#if defined(__APPLE__) + +#include + +#if defined(__has_builtin) +#if __has_builtin(__builtin_available) +#define HAVE_BUILTIN_AVAILABLE 1 +#endif +#endif + +#ifdef HAVE_BUILTIN_AVAILABLE +# define HAVE_FSTATAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_FACCESSAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_FCHMODAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_FCHOWNAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_LINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_FDOPENDIR_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_MKDIRAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_RENAMEAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_UNLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_OPENAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_READLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_SYMLINKAT_RUNTIME __builtin_available(macOS 10.10, iOS 8.0, *) +# define HAVE_FUTIMENS_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) +# define HAVE_UTIMENSAT_RUNTIME __builtin_available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *) +# define HAVE_PWRITEV_RUNTIME __builtin_available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) +# define HAVE_MKFIFOAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) +# define HAVE_MKNODAT_RUNTIME __builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) +# define HAVE_PTSNAME_R_RUNTIME __builtin_available(macOS 10.13.4, iOS 11.3, tvOS 11.3, watchOS 4.3, *) + +# define HAVE_POSIX_SPAWN_SETSID_RUNTIME __builtin_available(macOS 10.15, *) + +#else /* Xcode 8 or earlier */ + + /* __builtin_available is not present in these compilers, but + * some of the symbols might be weak linked (10.10 SDK or later + * deploying on 10.9. + * + * Fall back to the older style of availability checking for + * symbols introduced in macOS 10.10. + */ + +# ifdef HAVE_FSTATAT +# define HAVE_FSTATAT_RUNTIME (fstatat != NULL) +# endif + +# ifdef HAVE_FACCESSAT +# define HAVE_FACCESSAT_RUNTIME (faccessat != NULL) +# endif + +# ifdef HAVE_FCHMODAT +# define HAVE_FCHMODAT_RUNTIME (fchmodat != NULL) +# endif + +# ifdef HAVE_FCHOWNAT +# define HAVE_FCHOWNAT_RUNTIME (fchownat != NULL) +# endif + +# ifdef HAVE_LINKAT +# define HAVE_LINKAT_RUNTIME (linkat != NULL) +# endif + +# ifdef HAVE_FDOPENDIR +# define HAVE_FDOPENDIR_RUNTIME (fdopendir != NULL) +# endif + +# ifdef HAVE_MKDIRAT +# define HAVE_MKDIRAT_RUNTIME (mkdirat != NULL) +# endif + +# ifdef HAVE_RENAMEAT +# define HAVE_RENAMEAT_RUNTIME (renameat != NULL) +# endif + +# ifdef HAVE_UNLINKAT +# define HAVE_UNLINKAT_RUNTIME (unlinkat != NULL) +# endif + +# ifdef HAVE_OPENAT +# define HAVE_OPENAT_RUNTIME (openat != NULL) +# endif + +# ifdef HAVE_READLINKAT +# define HAVE_READLINKAT_RUNTIME (readlinkat != NULL) +# endif + +# ifdef HAVE_SYMLINKAT +# define HAVE_SYMLINKAT_RUNTIME (symlinkat != NULL) +# endif + +# ifdef HAVE_UTIMENSAT +# define HAVE_UTIMENSAT_RUNTIME (utimensat != NULL) +# endif + +# ifdef HAVE_FUTIMENS +# define HAVE_FUTIMENS_RUNTIME (futimens != NULL) +# endif + +# ifdef HAVE_PWRITEV +# define HAVE_PWRITEV_RUNTIME (pwritev != NULL) +# endif + +# ifdef HAVE_MKFIFOAT +# define HAVE_MKFIFOAT_RUNTIME (mkfifoat != NULL) +# endif + +# ifdef HAVE_MKNODAT +# define HAVE_MKNODAT_RUNTIME (mknodat != NULL) +# endif + +# ifdef HAVE_PTSNAME_R +# define HAVE_PTSNAME_R_RUNTIME (ptsname_r != NULL) +# endif + +#endif + +#ifdef HAVE_FUTIMESAT +/* Some of the logic for weak linking depends on this assertion */ +# error "HAVE_FUTIMESAT unexpectedly defined" +#endif + +#else +# define HAVE_FSTATAT_RUNTIME 1 +# define HAVE_FACCESSAT_RUNTIME 1 +# define HAVE_FCHMODAT_RUNTIME 1 +# define HAVE_FCHOWNAT_RUNTIME 1 +# define HAVE_LINKAT_RUNTIME 1 +# define HAVE_FDOPENDIR_RUNTIME 1 +# define HAVE_MKDIRAT_RUNTIME 1 +# define HAVE_RENAMEAT_RUNTIME 1 +# define HAVE_UNLINKAT_RUNTIME 1 +# define HAVE_OPENAT_RUNTIME 1 +# define HAVE_READLINKAT_RUNTIME 1 +# define HAVE_SYMLINKAT_RUNTIME 1 +# define HAVE_FUTIMENS_RUNTIME 1 +# define HAVE_UTIMENSAT_RUNTIME 1 +# define HAVE_PWRITEV_RUNTIME 1 +# define HAVE_MKFIFOAT_RUNTIME 1 +# define HAVE_MKNODAT_RUNTIME 1 +# define HAVE_PTSNAME_R_RUNTIME 1 +#endif + + +PyDoc_STRVAR(posix__doc__, +"This module provides access to operating system functionality that is\n\ +standardized by the C Standard and the POSIX standard (a thinly\n\ +disguised Unix interface). Refer to the library manual and\n\ +corresponding Unix manual entries for more information on calls."); + + +#ifdef HAVE_SYS_UIO_H +# include +#endif + +#ifdef HAVE_SYS_TYPES_H +/* Should be included before on HP-UX v3 */ +# include +#endif /* HAVE_SYS_TYPES_H */ + +#ifdef HAVE_SYS_SYSMACROS_H +/* GNU C Library: major(), minor(), makedev() */ +# include +#endif + +#ifdef HAVE_SYS_STAT_H +# include +#endif /* HAVE_SYS_STAT_H */ + +#ifdef HAVE_SYS_WAIT_H +# include // WNOHANG +#endif +#ifdef HAVE_LINUX_WAIT_H +# include // P_PIDFD +#endif + +#ifdef HAVE_SIGNAL_H +# include +#endif + +#ifdef HAVE_FCNTL_H +# include +#endif + +#ifdef HAVE_GRP_H +# include +#endif + +#ifdef HAVE_SYSEXITS_H +# include +#endif + +#ifdef HAVE_SYS_LOADAVG_H +# include +#endif + +#ifdef HAVE_SYS_SENDFILE_H +# include +#endif + +#if defined(__APPLE__) +# include +#endif + +#ifdef HAVE_SCHED_H +# include +#endif + +#if !defined(CPU_ALLOC) && defined(HAVE_SCHED_SETAFFINITY) +# undef HAVE_SCHED_SETAFFINITY +#endif + +#if defined(HAVE_SYS_XATTR_H) +# if defined(HAVE_LINUX_LIMITS_H) && !defined(__FreeBSD_kernel__) && !defined(__GNU__) +# define USE_XATTRS +# include // Needed for XATTR_SIZE_MAX on musl libc. +# endif +# if defined(__CYGWIN__) +# define USE_XATTRS +# include // Needed for XATTR_SIZE_MAX and XATTR_LIST_MAX. +# endif +#endif + +#ifdef USE_XATTRS +# include +#endif + +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__) +# ifdef HAVE_SYS_SOCKET_H +# include +# endif +#endif + +#ifdef HAVE_DLFCN_H +# include +#endif + +#ifdef __hpux +# include +#endif + +#if defined(__DragonFly__) || \ + defined(__OpenBSD__) || \ + defined(__FreeBSD__) || \ + defined(__NetBSD__) || \ + defined(__APPLE__) +# include +#endif + +#ifdef HAVE_LINUX_RANDOM_H +# include +#endif +#ifdef HAVE_GETRANDOM_SYSCALL +# include +#endif + +#ifdef HAVE_WINDOWS_CONSOLE_IO +# define TERMSIZE_USE_CONIO +#elif defined(HAVE_SYS_IOCTL_H) +# include +# if defined(HAVE_TERMIOS_H) +# include +# endif +# if defined(TIOCGWINSZ) +# define TERMSIZE_USE_IOCTL +# endif +#endif /* HAVE_WINDOWS_CONSOLE_IO */ + +/* Various compilers have only certain posix functions */ +/* XXX Gosh I wish these were all moved into pyconfig.h */ +#if defined(__WATCOMC__) && !defined(__QNX__) /* Watcom compiler */ +# define HAVE_OPENDIR 1 +# define HAVE_SYSTEM 1 +# include +#elif defined( _MSC_VER) + /* Microsoft compiler */ +# if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_APP) || defined(MS_WINDOWS_SYSTEM) +# define HAVE_GETPPID 1 +# endif /* MS_WINDOWS_DESKTOP | MS_WINDOWS_APP | MS_WINDOWS_SYSTEM */ +# if defined(MS_WINDOWS_DESKTOP) +# define HAVE_GETLOGIN 1 +# endif /* MS_WINDOWS_DESKTOP */ +# if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) +# define HAVE_SPAWNV 1 +# define HAVE_EXECV 1 +# define HAVE_WSPAWNV 1 +# define HAVE_WEXECV 1 +# define HAVE_SYSTEM 1 +# define HAVE_CWAIT 1 +# endif /* MS_WINDOWS_DESKTOP | MS_WINDOWS_SYSTEM */ +# define HAVE_PIPE 1 +# define HAVE_FSYNC 1 +# define fsync _commit +#elif defined(__MINGW32__) /* GCC for windows hosts */ +/* getlogin is detected by configure on mingw-w64 */ +# undef HAVE_GETLOGIN +/* opendir is detected by configure on mingw-w64, and for some reason +things don't work as expected. For example, os.listdir always returns +the cwd's directory listing instead of the one specified. By +un-defining, this, os.listdir will use the one which uses native +windows API. */ +# undef HAVE_OPENDIR +/*# define HAVE_GETCWD 1 - detected by configure*/ +# define HAVE_GETPPID 1 +# define HAVE_GETLOGIN 1 +# define HAVE_SPAWNV 1 +# define HAVE_WSPAWNV 1 +# define HAVE_WEXECV 1 +/*# define HAVE_EXECV 1 - detected by configure*/ +# define HAVE_PIPE 1 +# define HAVE_POPEN 1 +# define HAVE_SYSTEM 1 +# define HAVE_CWAIT 1 +# define HAVE_FSYNC 1 +# define fsync _commit +# include +# ifndef _MAX_ENV +# define _MAX_ENV 32767 +# endif +#endif /* ! __WATCOMC__ || __QNX__ */ + +/*[clinic input] +# one of the few times we lie about this name! +module os +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=94a0f0f978acae17]*/ + +#ifndef _MSC_VER + +#if defined(__sgi)&&_COMPILER_VERSION>=700 +/* declare ctermid_r if compiling with MIPSPro 7.x in ANSI C mode + (default) */ +extern char *ctermid_r(char *); +#endif + +#endif /* !_MSC_VER */ + +#if defined(__VXWORKS__) +# include +# include +# include +# include +# ifndef _P_WAIT +# define _P_WAIT 0 +# define _P_NOWAIT 1 +# define _P_NOWAITO 1 +# endif +#endif /* __VXWORKS__ */ + +#ifdef HAVE_POSIX_SPAWN +# include +#endif + +#ifdef HAVE_UTIME_H +# include +#endif /* HAVE_UTIME_H */ + +#ifdef HAVE_SYS_UTIME_H +# include +# define HAVE_UTIME_H /* pretend we do for the rest of this file */ +#endif /* HAVE_SYS_UTIME_H */ + +#ifdef HAVE_SYS_TIMES_H +# include +#endif /* HAVE_SYS_TIMES_H */ + +#ifdef HAVE_SYS_PARAM_H +# include +#endif /* HAVE_SYS_PARAM_H */ + +#ifdef HAVE_SYS_UTSNAME_H +# include +#endif /* HAVE_SYS_UTSNAME_H */ + +#ifdef HAVE_DIRENT_H +# include +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# if defined(__WATCOMC__) && !defined(__QNX__) +# include +# define NAMLEN(dirent) strlen((dirent)->d_name) +# else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# endif +# ifdef HAVE_SYS_NDIR_H +# include +# endif +# ifdef HAVE_SYS_DIR_H +# include +# endif +# ifdef HAVE_NDIR_H +# include +# endif +#endif + +#ifdef MS_WINDOWS +# ifdef HAVE_DIRECT_H +# include +# endif +# ifdef HAVE_IO_H +# include +# endif +# ifdef HAVE_PROCESS_H +# include +# endif +# include +#endif /* MS_WINDOWS */ + +#ifndef MAXPATHLEN +# if defined(PATH_MAX) && PATH_MAX > 1024 +# define MAXPATHLEN PATH_MAX +# else +# define MAXPATHLEN 1024 +# endif +#endif /* MAXPATHLEN */ + +#ifdef UNION_WAIT + /* Emulate some macros on systems that have a union instead of macros */ +# ifndef WIFEXITED +# define WIFEXITED(u_wait) (!(u_wait).w_termsig && !(u_wait).w_coredump) +# endif +# ifndef WEXITSTATUS +# define WEXITSTATUS(u_wait) (WIFEXITED(u_wait)?((u_wait).w_retcode):-1) +# endif +# ifndef WTERMSIG +# define WTERMSIG(u_wait) ((u_wait).w_termsig) +# endif +# define WAIT_TYPE union wait +# define WAIT_STATUS_INT(s) (s.w_status) +#else + /* !UNION_WAIT */ +# define WAIT_TYPE int +# define WAIT_STATUS_INT(s) (s) +#endif /* UNION_WAIT */ + +/* Don't use the "_r" form if we don't need it (also, won't have a + prototype for it, at least on Solaris -- maybe others as well?). */ +#if defined(HAVE_CTERMID_R) +# define USE_CTERMID_R +#endif + +/* choose the appropriate stat and fstat functions and return structs */ +#undef STAT +#undef FSTAT +#undef STRUCT_STAT +#ifdef MS_WINDOWS +# define STAT win32_stat +# define LSTAT win32_lstat +# define FSTAT _Py_fstat_noraise +# define STRUCT_STAT struct _Py_stat_struct +#else +# define STAT stat +# define LSTAT lstat +# define FSTAT fstat +# define STRUCT_STAT struct stat +#endif + +#if defined(MAJOR_IN_MKDEV) +# include +#else +# if defined(MAJOR_IN_SYSMACROS) +# include +# endif +# if defined(HAVE_MKNOD) && defined(HAVE_SYS_MKDEV_H) +# include +# endif +#endif + +#ifdef MS_WINDOWS +# define INITFUNC PyInit_nt +# define MODNAME "nt" +# define MODNAME_OBJ &_Py_ID(nt) +#else +# define INITFUNC PyInit_posix +# define MODNAME "posix" +# define MODNAME_OBJ &_Py_ID(posix) +#endif + +#if defined(__sun) +/* Something to implement in autoconf, not present in autoconf 2.69 */ +# define HAVE_STRUCT_STAT_ST_FSTYPE 1 +#endif + +/* memfd_create is either defined in sys/mman.h or sys/memfd.h + * linux/memfd.h defines additional flags + */ +#ifdef HAVE_SYS_MMAN_H +# include +#endif +#ifdef HAVE_SYS_MEMFD_H +# include +#endif +#ifdef HAVE_LINUX_MEMFD_H +# include +#endif + +/* eventfd() */ +#ifdef HAVE_SYS_EVENTFD_H +# include +#endif + +/* timerfd_create() */ +#ifdef HAVE_SYS_TIMERFD_H +# include +#endif + +#ifdef _Py_MEMORY_SANITIZER +# include +#endif + +#ifdef HAVE_FORK +static void +run_at_forkers(PyObject *lst, int reverse) +{ + Py_ssize_t i; + PyObject *cpy; + + if (lst != NULL) { + assert(PyList_CheckExact(lst)); + + /* Use a list copy in case register_at_fork() is called from + * one of the callbacks. + */ + cpy = PyList_GetSlice(lst, 0, PyList_GET_SIZE(lst)); + if (cpy == NULL) + PyErr_WriteUnraisable(lst); + else { + if (reverse) + PyList_Reverse(cpy); + for (i = 0; i < PyList_GET_SIZE(cpy); i++) { + PyObject *func, *res; + func = PyList_GET_ITEM(cpy, i); + res = _PyObject_CallNoArgs(func); + if (res == NULL) + PyErr_WriteUnraisable(func); + else + Py_DECREF(res); + } + Py_DECREF(cpy); + } + } +} + +void +PyOS_BeforeFork(void) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + run_at_forkers(interp->before_forkers, 1); + + _PyImport_AcquireLock(interp); + _PyEval_StopTheWorldAll(&_PyRuntime); + HEAD_LOCK(&_PyRuntime); +} + +void +PyOS_AfterFork_Parent(void) +{ + HEAD_UNLOCK(&_PyRuntime); + _PyEval_StartTheWorldAll(&_PyRuntime); + + PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyImport_ReleaseLock(interp); + run_at_forkers(interp->after_forkers_parent, 0); +} + +void +PyOS_AfterFork_Child(void) +{ + PyStatus status; + _PyRuntimeState *runtime = &_PyRuntime; + + // re-creates runtime->interpreters.mutex (HEAD_UNLOCK) + status = _PyRuntimeState_ReInitThreads(runtime); + if (_PyStatus_EXCEPTION(status)) { + goto fatal_error; + } + + PyThreadState *tstate = _PyThreadState_GET(); + _Py_EnsureTstateNotNULL(tstate); + + assert(tstate->thread_id == PyThread_get_thread_ident()); +#ifdef PY_HAVE_THREAD_NATIVE_ID + tstate->native_thread_id = PyThread_get_thread_native_id(); +#endif + +#ifdef Py_GIL_DISABLED + _Py_brc_after_fork(tstate->interp); + _Py_qsbr_after_fork((_PyThreadStateImpl *)tstate); +#endif + + // Ideally we could guarantee tstate is running main. + _PyInterpreterState_ReinitRunningMain(tstate); + + status = _PyEval_ReInitThreads(tstate); + if (_PyStatus_EXCEPTION(status)) { + goto fatal_error; + } + + // Remove the dead thread states. We "start the world" once we are the only + // thread state left to undo the stop the world call in `PyOS_BeforeFork`. + // That needs to happen before `_PyThreadState_DeleteList`, because that + // may call destructors. + PyThreadState *list = _PyThreadState_RemoveExcept(tstate); + _PyEval_StartTheWorldAll(&_PyRuntime); + _PyThreadState_DeleteList(list); + + _PyImport_ReInitLock(tstate->interp); + _PyImport_ReleaseLock(tstate->interp); + + _PySignal_AfterFork(); + + status = _PyInterpreterState_DeleteExceptMain(runtime); + if (_PyStatus_EXCEPTION(status)) { + goto fatal_error; + } + assert(_PyThreadState_GET() == tstate); + + status = _PyPerfTrampoline_AfterFork_Child(); + if (_PyStatus_EXCEPTION(status)) { + goto fatal_error; + } + + run_at_forkers(tstate->interp->after_forkers_child, 0); + return; + +fatal_error: + Py_ExitStatusException(status); +} + +static int +register_at_forker(PyObject **lst, PyObject *func) +{ + if (func == NULL) /* nothing to register? do nothing. */ + return 0; + if (*lst == NULL) { + *lst = PyList_New(0); + if (*lst == NULL) + return -1; + } + return PyList_Append(*lst, func); +} +#endif /* HAVE_FORK */ + + +/* Legacy wrapper */ +void +PyOS_AfterFork(void) +{ +#ifdef HAVE_FORK + PyOS_AfterFork_Child(); +#endif +} + + +#ifdef MS_WINDOWS +/* defined in fileutils.c */ +void _Py_time_t_to_FILE_TIME(time_t, int, FILETIME *); +void _Py_attribute_data_to_stat(BY_HANDLE_FILE_INFORMATION *, ULONG, + FILE_BASIC_INFO *, FILE_ID_INFO *, + struct _Py_stat_struct *); +void _Py_stat_basic_info_to_stat(FILE_STAT_BASIC_INFORMATION *, + struct _Py_stat_struct *); +#endif + + +#ifndef MS_WINDOWS +PyObject * +_PyLong_FromUid(uid_t uid) +{ + if (uid == (uid_t)-1) + return PyLong_FromLong(-1); + return PyLong_FromUnsignedLong(uid); +} + +PyObject * +_PyLong_FromGid(gid_t gid) +{ + if (gid == (gid_t)-1) + return PyLong_FromLong(-1); + return PyLong_FromUnsignedLong(gid); +} + +int +_Py_Uid_Converter(PyObject *obj, uid_t *p) +{ + uid_t uid; + PyObject *index; + int overflow; + long result; + unsigned long uresult; + + index = _PyNumber_Index(obj); + if (index == NULL) { + PyErr_Format(PyExc_TypeError, + "uid should be integer, not %.200s", + _PyType_Name(Py_TYPE(obj))); + return 0; + } + + /* + * Handling uid_t is complicated for two reasons: + * * Although uid_t is (always?) unsigned, it still + * accepts -1. + * * We don't know its size in advance--it may be + * bigger than an int, or it may be smaller than + * a long. + * + * So a bit of defensive programming is in order. + * Start with interpreting the value passed + * in as a signed long and see if it works. + */ + + result = PyLong_AsLongAndOverflow(index, &overflow); + + if (!overflow) { + uid = (uid_t)result; + + if (result == -1) { + if (PyErr_Occurred()) + goto fail; + /* It's a legitimate -1, we're done. */ + goto success; + } + + /* Any other negative number is disallowed. */ + if (result < 0) + goto underflow; + + /* Ensure the value wasn't truncated. */ + if (sizeof(uid_t) < sizeof(long) && + (long)uid != result) + goto underflow; + goto success; + } + + if (overflow < 0) + goto underflow; + + /* + * Okay, the value overflowed a signed long. If it + * fits in an *unsigned* long, it may still be okay, + * as uid_t may be unsigned long on this platform. + */ + uresult = PyLong_AsUnsignedLong(index); + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) + goto overflow; + goto fail; + } + + uid = (uid_t)uresult; + + /* + * If uid == (uid_t)-1, the user actually passed in ULONG_MAX, + * but this value would get interpreted as (uid_t)-1 by chown + * and its siblings. That's not what the user meant! So we + * throw an overflow exception instead. (We already + * handled a real -1 with PyLong_AsLongAndOverflow() above.) + */ + if (uid == (uid_t)-1) + goto overflow; + + /* Ensure the value wasn't truncated. */ + if (sizeof(uid_t) < sizeof(long) && + (unsigned long)uid != uresult) + goto overflow; + /* fallthrough */ + +success: + Py_DECREF(index); + *p = uid; + return 1; + +underflow: + PyErr_SetString(PyExc_OverflowError, + "uid is less than minimum"); + goto fail; + +overflow: + PyErr_SetString(PyExc_OverflowError, + "uid is greater than maximum"); + /* fallthrough */ + +fail: + Py_DECREF(index); + return 0; +} + +int +_Py_Gid_Converter(PyObject *obj, gid_t *p) +{ + gid_t gid; + PyObject *index; + int overflow; + long result; + unsigned long uresult; + + index = _PyNumber_Index(obj); + if (index == NULL) { + PyErr_Format(PyExc_TypeError, + "gid should be integer, not %.200s", + _PyType_Name(Py_TYPE(obj))); + return 0; + } + + /* + * Handling gid_t is complicated for two reasons: + * * Although gid_t is (always?) unsigned, it still + * accepts -1. + * * We don't know its size in advance--it may be + * bigger than an int, or it may be smaller than + * a long. + * + * So a bit of defensive programming is in order. + * Start with interpreting the value passed + * in as a signed long and see if it works. + */ + + result = PyLong_AsLongAndOverflow(index, &overflow); + + if (!overflow) { + gid = (gid_t)result; + + if (result == -1) { + if (PyErr_Occurred()) + goto fail; + /* It's a legitimate -1, we're done. */ + goto success; + } + + /* Any other negative number is disallowed. */ + if (result < 0) { + goto underflow; + } + + /* Ensure the value wasn't truncated. */ + if (sizeof(gid_t) < sizeof(long) && + (long)gid != result) + goto underflow; + goto success; + } + + if (overflow < 0) + goto underflow; + + /* + * Okay, the value overflowed a signed long. If it + * fits in an *unsigned* long, it may still be okay, + * as gid_t may be unsigned long on this platform. + */ + uresult = PyLong_AsUnsignedLong(index); + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) + goto overflow; + goto fail; + } + + gid = (gid_t)uresult; + + /* + * If gid == (gid_t)-1, the user actually passed in ULONG_MAX, + * but this value would get interpreted as (gid_t)-1 by chown + * and its siblings. That's not what the user meant! So we + * throw an overflow exception instead. (We already + * handled a real -1 with PyLong_AsLongAndOverflow() above.) + */ + if (gid == (gid_t)-1) + goto overflow; + + /* Ensure the value wasn't truncated. */ + if (sizeof(gid_t) < sizeof(long) && + (unsigned long)gid != uresult) + goto overflow; + /* fallthrough */ + +success: + Py_DECREF(index); + *p = gid; + return 1; + +underflow: + PyErr_SetString(PyExc_OverflowError, + "gid is less than minimum"); + goto fail; + +overflow: + PyErr_SetString(PyExc_OverflowError, + "gid is greater than maximum"); + /* fallthrough */ + +fail: + Py_DECREF(index); + return 0; +} +#endif /* MS_WINDOWS */ + + +static PyObject * +_PyLong_FromDev(dev_t dev) +{ +#ifdef NODEV + if (dev == NODEV) { + return PyLong_FromLongLong((long long)dev); + } +#endif + return PyLong_FromUnsignedLongLong((unsigned long long)dev); +} + + +#if (defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV)) || defined(HAVE_DEVICE_MACROS) +static int +_Py_Dev_Converter(PyObject *obj, void *p) +{ +#ifdef NODEV + if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) { + int overflow; + long long result = PyLong_AsLongLongAndOverflow(obj, &overflow); + if (result == -1 && PyErr_Occurred()) { + return 0; + } + if (!overflow && result == (long long)NODEV) { + *((dev_t *)p) = NODEV; + return 1; + } + } +#endif + + unsigned long long result = PyLong_AsUnsignedLongLong(obj); + if (result == (unsigned long long)-1 && PyErr_Occurred()) { + return 0; + } + if ((unsigned long long)(dev_t)result != result) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C dev_t"); + return 0; + } + *((dev_t *)p) = (dev_t)result; + return 1; +} +#endif /* (HAVE_MKNOD && HAVE_MAKEDEV) || HAVE_DEVICE_MACROS */ + + +#ifdef AT_FDCWD +/* + * Why the (int) cast? Solaris 10 defines AT_FDCWD as 0xffd19553 (-3041965); + * without the int cast, the value gets interpreted as uint (4291925331), + * which doesn't play nicely with all the initializer lines in this file that + * look like this: + * int dir_fd = DEFAULT_DIR_FD; + */ +#define DEFAULT_DIR_FD (int)AT_FDCWD +#else +#define DEFAULT_DIR_FD (-100) +#endif + +static int +_fd_converter(PyObject *o, int *p) +{ + int overflow; + long long_value; + + if (PyBool_Check(o)) { + if (PyErr_WarnEx(PyExc_RuntimeWarning, + "bool is used as a file descriptor", 1)) + { + return 0; + } + } + PyObject *index = _PyNumber_Index(o); + if (index == NULL) { + return 0; + } + + assert(PyLong_Check(index)); + long_value = PyLong_AsLongAndOverflow(index, &overflow); + Py_DECREF(index); + assert(!PyErr_Occurred()); + if (overflow > 0 || long_value > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "fd is greater than maximum"); + return 0; + } + if (overflow < 0 || long_value < INT_MIN) { + PyErr_SetString(PyExc_OverflowError, + "fd is less than minimum"); + return 0; + } + + *p = (int)long_value; + return 1; +} + +static int +dir_fd_converter(PyObject *o, void *p) +{ + if (o == Py_None) { + *(int *)p = DEFAULT_DIR_FD; + return 1; + } + else if (PyIndex_Check(o)) { + return _fd_converter(o, (int *)p); + } + else { + PyErr_Format(PyExc_TypeError, + "argument should be integer or None, not %.200s", + _PyType_Name(Py_TYPE(o))); + return 0; + } +} + +typedef struct { + PyObject *billion; + PyObject *DirEntryType; + PyObject *ScandirIteratorType; +#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) + PyObject *SchedParamType; +#endif + newfunc statresult_new_orig; + PyObject *StatResultType; + PyObject *StatVFSResultType; + PyObject *TerminalSizeType; + PyObject *TimesResultType; + PyObject *UnameResultType; +#if defined(HAVE_WAITID) + PyObject *WaitidResultType; +#endif +#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4) + PyObject *struct_rusage; +#endif + PyObject *st_mode; +#ifndef MS_WINDOWS + // times() clock frequency in hertz; used by os.times() + long ticks_per_second; +#endif +} _posixstate; + + +static inline _posixstate* +get_posix_state(PyObject *module) +{ + void *state = _PyModule_GetState(module); + assert(state != NULL); + return (_posixstate *)state; +} + +/* + * A PyArg_ParseTuple "converter" function + * that handles filesystem paths in the manner + * preferred by the os module. + * + * path_converter accepts (Unicode) strings and their + * subclasses, and bytes and their subclasses. What + * it does with the argument depends on path.make_wide: + * + * * If path.make_wide is nonzero, if we get a (Unicode) + * string we extract the wchar_t * and return it; if we + * get bytes we decode to wchar_t * and return that. + * + * * If path.make_wide is zero, if we get bytes we extract + * the char_t * and return it; if we get a (Unicode) + * string we encode to char_t * and return that. + * + * path_converter also optionally accepts signed + * integers (representing open file descriptors) instead + * of path strings. + * + * Input fields: + * path.nullable + * If nonzero, the path is permitted to be None. + * path.nonstrict + * If nonzero, the path is permitted to contain + * embedded null characters and have any length. + * path.make_wide + * If nonzero, the converter always uses wide, decoding if necessary, else + * it always uses narrow, encoding if necessary. The default value is + * nonzero on Windows, else zero. + * path.suppress_value_error + * If nonzero, raising ValueError is suppressed. + * path.allow_fd + * If nonzero, the path is permitted to be a file handle + * (a signed int) instead of a string. + * path.function_name + * If non-NULL, path_converter will use that as the name + * of the function in error messages. + * (If path.function_name is NULL it omits the function name.) + * path.argument_name + * If non-NULL, path_converter will use that as the name + * of the parameter in error messages. + * (If path.argument_name is NULL it uses "path".) + * + * Output fields: + * path.wide + * Points to the path if it was expressed as Unicode + * or if it was bytes and decoded to Unicode. + * path.narrow + * Points to the path if it was expressed as bytes, + * or if it was Unicode and encoded to bytes. + * path.fd + * Contains a file descriptor if path.accept_fd was true + * and the caller provided a signed integer instead of any + * sort of string. + * + * WARNING: if your "path" parameter is optional, and is + * unspecified, path_converter will never get called. + * So if you set allow_fd, you *MUST* initialize path.fd = -1 + * yourself! + * path.value_error + * If nonzero, then suppress_value_error was specified and a ValueError + * occurred. + * path.length + * The length of the path in characters, if specified as + * a string. + * path.object + * The original object passed in (if get a PathLike object, + * the result of PyOS_FSPath() is treated as the original object). + * Own a reference to the object. + * path.cleanup + * For internal use only. May point to a temporary object. + * (Pay no attention to the man behind the curtain.) + * + * At most one of path.wide or path.narrow will be non-NULL. + * If path was None and path.nullable was set, + * or if path was an integer and path.allow_fd was set, + * both path.wide and path.narrow will be NULL + * and path.length will be 0. + * + * path_converter takes care to not write to the path_t + * unless it's successful. However it must reset the + * "cleanup" field each time it's called. + * + * Use as follows: + * path_t path; + * memset(&path, 0, sizeof(path)); + * PyArg_ParseTuple(args, "O&", path_converter, &path); + * // ... use values from path ... + * path_cleanup(&path); + * + * (Note that if PyArg_Parse fails you don't need to call + * path_cleanup(). However it is safe to do so.) + */ +typedef struct { + // Input fields + const char *function_name; + const char *argument_name; + int nullable; + int nonstrict; + int make_wide; + int suppress_value_error; + int allow_fd; + // Output fields + const wchar_t *wide; + const char *narrow; + int fd; + int value_error; + Py_ssize_t length; + PyObject *object; + PyObject *cleanup; +} path_t; + +#define PATH_T_INITIALIZE(function_name, argument_name, nullable, nonstrict, \ + make_wide, suppress_value_error, allow_fd) \ + {function_name, argument_name, nullable, nonstrict, make_wide, \ + suppress_value_error, allow_fd, NULL, NULL, -1, 0, 0, NULL, NULL} +#ifdef MS_WINDOWS +#define PATH_T_INITIALIZE_P(function_name, argument_name, nullable, \ + nonstrict, suppress_value_error, allow_fd) \ + PATH_T_INITIALIZE(function_name, argument_name, nullable, nonstrict, 1, \ + suppress_value_error, allow_fd) +#else +#define PATH_T_INITIALIZE_P(function_name, argument_name, nullable, \ + nonstrict, suppress_value_error, allow_fd) \ + PATH_T_INITIALIZE(function_name, argument_name, nullable, nonstrict, 0, \ + suppress_value_error, allow_fd) +#endif + +static void +path_cleanup(path_t *path) +{ + wchar_t *wide = (wchar_t *)path->wide; + path->wide = NULL; + PyMem_Free(wide); + Py_CLEAR(path->object); + Py_CLEAR(path->cleanup); +} + +static int +path_converter(PyObject *o, void *p) +{ + path_t *path = (path_t *)p; + PyObject *bytes = NULL; + Py_ssize_t length = 0; + int is_index, is_bytes, is_unicode; + const char *narrow; + PyObject *wo = NULL; + wchar_t *wide = NULL; + +#define FORMAT_EXCEPTION(exc, fmt) \ + PyErr_Format(exc, "%s%s" fmt, \ + path->function_name ? path->function_name : "", \ + path->function_name ? ": " : "", \ + path->argument_name ? path->argument_name : "path") + + /* Py_CLEANUP_SUPPORTED support */ + if (o == NULL) { + path_cleanup(path); + return 1; + } + + /* Ensure it's always safe to call path_cleanup(). */ + path->object = path->cleanup = NULL; + /* path->object owns a reference to the original object */ + Py_INCREF(o); + + if ((o == Py_None) && path->nullable) { + path->wide = NULL; + path->narrow = NULL; + path->fd = -1; + goto success_exit; + } + + /* Only call this here so that we don't treat the return value of + os.fspath() as an fd or buffer. */ + is_index = path->allow_fd && PyIndex_Check(o); + is_bytes = PyBytes_Check(o); + is_unicode = PyUnicode_Check(o); + + if (!is_index && !is_unicode && !is_bytes) { + /* Inline PyOS_FSPath() for better error messages. */ + PyObject *func, *res; + + func = _PyObject_LookupSpecial(o, &_Py_ID(__fspath__)); + if ((NULL == func) || (func == Py_None)) { + goto error_format; + } + res = _PyObject_CallNoArgs(func); + Py_DECREF(func); + if (NULL == res) { + goto error_exit; + } + else if (PyUnicode_Check(res)) { + is_unicode = 1; + } + else if (PyBytes_Check(res)) { + is_bytes = 1; + } + else { + PyErr_Format(PyExc_TypeError, + "expected %.200s.__fspath__() to return str or bytes, " + "not %.200s", _PyType_Name(Py_TYPE(o)), + _PyType_Name(Py_TYPE(res))); + Py_DECREF(res); + goto error_exit; + } + + /* still owns a reference to the original object */ + Py_SETREF(o, res); + } + + if (is_unicode) { + if (path->make_wide) { + wide = PyUnicode_AsWideCharString(o, &length); + if (!wide) { + goto error_exit; + } +#ifdef MS_WINDOWS + if (!path->nonstrict && length > 32767) { + FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows"); + goto error_exit; + } +#endif + if (!path->nonstrict && wcslen(wide) != (size_t)length) { + FORMAT_EXCEPTION(PyExc_ValueError, + "embedded null character in %s"); + goto error_exit; + } + + path->wide = wide; + path->narrow = NULL; + path->fd = -1; + wide = NULL; + goto success_exit; + } + bytes = PyUnicode_EncodeFSDefault(o); + if (!bytes) { + goto error_exit; + } + } + else if (is_bytes) { + bytes = Py_NewRef(o); + } + else if (is_index) { + if (!_fd_converter(o, &path->fd)) { + goto error_exit; + } + path->wide = NULL; + path->narrow = NULL; + goto success_exit; + } + else { + error_format: + PyErr_Format(PyExc_TypeError, "%s%s%s should be %s, not %.200s", + path->function_name ? path->function_name : "", + path->function_name ? ": " : "", + path->argument_name ? path->argument_name : "path", + path->allow_fd && path->nullable ? "string, bytes, os.PathLike, " + "integer or None" : + path->allow_fd ? "string, bytes, os.PathLike or integer" : + path->nullable ? "string, bytes, os.PathLike or None" : + "string, bytes or os.PathLike", + _PyType_Name(Py_TYPE(o))); + goto error_exit; + } + + length = PyBytes_GET_SIZE(bytes); + narrow = PyBytes_AS_STRING(bytes); + if (!path->nonstrict && strlen(narrow) != (size_t)length) { + FORMAT_EXCEPTION(PyExc_ValueError, "embedded null character in %s"); + goto error_exit; + } + + if (path->make_wide) { + wo = PyUnicode_DecodeFSDefaultAndSize(narrow, length); + if (!wo) { + goto error_exit; + } + + wide = PyUnicode_AsWideCharString(wo, &length); + Py_DECREF(wo); + if (!wide) { + goto error_exit; + } +#ifdef MS_WINDOWS + if (!path->nonstrict && length > 32767) { + FORMAT_EXCEPTION(PyExc_ValueError, "%s too long for Windows"); + goto error_exit; + } +#endif + if (!path->nonstrict && wcslen(wide) != (size_t)length) { + FORMAT_EXCEPTION(PyExc_ValueError, + "embedded null character in %s"); + goto error_exit; + } + path->wide = wide; + path->narrow = NULL; + Py_DECREF(bytes); + wide = NULL; + } + else { + path->wide = NULL; + path->narrow = narrow; + if (bytes == o) { + /* Still a reference owned by path->object, don't have to + worry about path->narrow is used after free. */ + Py_DECREF(bytes); + } + else { + path->cleanup = bytes; + } + } + path->fd = -1; + + success_exit: + path->value_error = 0; + path->length = length; + path->object = o; + return Py_CLEANUP_SUPPORTED; + + error_exit: + Py_XDECREF(o); + Py_XDECREF(bytes); + PyMem_Free(wide); + if (!path->suppress_value_error || + !PyErr_ExceptionMatches(PyExc_ValueError)) + { + return 0; + } + PyErr_Clear(); + path->wide = NULL; + path->narrow = NULL; + path->fd = -1; + path->value_error = 1; + path->length = 0; + path->object = NULL; + return Py_CLEANUP_SUPPORTED; +} + +static void +argument_unavailable_error(const char *function_name, const char *argument_name) +{ + PyErr_Format(PyExc_NotImplementedError, + "%s%s%s unavailable on this platform", + (function_name != NULL) ? function_name : "", + (function_name != NULL) ? ": ": "", + argument_name); +} + +static int +dir_fd_unavailable(PyObject *o, void *p) +{ + int dir_fd; + if (!dir_fd_converter(o, &dir_fd)) + return 0; + if (dir_fd != DEFAULT_DIR_FD) { + argument_unavailable_error(NULL, "dir_fd"); + return 0; + } + *(int *)p = dir_fd; + return 1; +} + +static int +fd_specified(const char *function_name, int fd) +{ + if (fd == -1) + return 0; + + argument_unavailable_error(function_name, "fd"); + return 1; +} + +static int +follow_symlinks_specified(const char *function_name, int follow_symlinks) +{ + if (follow_symlinks) + return 0; + + argument_unavailable_error(function_name, "follow_symlinks"); + return 1; +} + +static int +path_and_dir_fd_invalid(const char *function_name, path_t *path, int dir_fd) +{ + if (!path->wide && (dir_fd != DEFAULT_DIR_FD) && !path->narrow) { + PyErr_Format(PyExc_ValueError, + "%s: can't specify dir_fd without matching path", + function_name); + return 1; + } + return 0; +} + +static int +dir_fd_and_fd_invalid(const char *function_name, int dir_fd, int fd) +{ + if ((dir_fd != DEFAULT_DIR_FD) && (fd != -1)) { + PyErr_Format(PyExc_ValueError, + "%s: can't specify both dir_fd and fd", + function_name); + return 1; + } + return 0; +} + +static int +fd_and_follow_symlinks_invalid(const char *function_name, int fd, + int follow_symlinks) +{ + if ((fd > 0) && (!follow_symlinks)) { + PyErr_Format(PyExc_ValueError, + "%s: cannot use fd and follow_symlinks together", + function_name); + return 1; + } + return 0; +} + +static int +dir_fd_and_follow_symlinks_invalid(const char *function_name, int dir_fd, + int follow_symlinks) +{ + if ((dir_fd != DEFAULT_DIR_FD) && (!follow_symlinks)) { + PyErr_Format(PyExc_ValueError, + "%s: cannot use dir_fd and follow_symlinks together", + function_name); + return 1; + } + return 0; +} + +#ifdef MS_WINDOWS + typedef long long Py_off_t; +#else + typedef off_t Py_off_t; +#endif + +static int +Py_off_t_converter(PyObject *arg, void *addr) +{ +#ifdef HAVE_LARGEFILE_SUPPORT + *((Py_off_t *)addr) = PyLong_AsLongLong(arg); +#else + *((Py_off_t *)addr) = PyLong_AsLong(arg); +#endif + if (PyErr_Occurred()) + return 0; + return 1; +} + +static PyObject * +PyLong_FromPy_off_t(Py_off_t offset) +{ +#ifdef HAVE_LARGEFILE_SUPPORT + return PyLong_FromLongLong(offset); +#else + return PyLong_FromLong(offset); +#endif +} + +#ifdef HAVE_SIGSET_T +/* Convert an iterable of integers to a sigset. + Return 1 on success, return 0 and raise an exception on error. */ +int +_Py_Sigset_Converter(PyObject *obj, void *addr) +{ + sigset_t *mask = (sigset_t *)addr; + PyObject *iterator, *item; + long signum; + int overflow; + + // The extra parens suppress the unreachable-code warning with clang on MacOS + if (sigemptyset(mask) < (0)) { + /* Probably only if mask == NULL. */ + PyErr_SetFromErrno(PyExc_OSError); + return 0; + } + + iterator = PyObject_GetIter(obj); + if (iterator == NULL) { + return 0; + } + + while ((item = PyIter_Next(iterator)) != NULL) { + signum = PyLong_AsLongAndOverflow(item, &overflow); + Py_DECREF(item); + if (signum <= 0 || signum >= Py_NSIG) { + if (overflow || signum != -1 || !PyErr_Occurred()) { + PyErr_Format(PyExc_ValueError, + "signal number %ld out of range [1; %i]", + signum, Py_NSIG - 1); + } + goto error; + } + if (sigaddset(mask, (int)signum)) { + if (errno != EINVAL) { + /* Probably impossible */ + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + /* For backwards compatibility, allow idioms such as + * `range(1, NSIG)` but warn about invalid signal numbers + */ + const char msg[] = + "invalid signal number %ld, please use valid_signals()"; + if (PyErr_WarnFormat(PyExc_RuntimeWarning, 1, msg, signum)) { + goto error; + } + } + } + if (!PyErr_Occurred()) { + Py_DECREF(iterator); + return 1; + } + +error: + Py_DECREF(iterator); + return 0; +} +#endif /* HAVE_SIGSET_T */ + +/* Return a dictionary corresponding to the POSIX environment table */ +#if defined(WITH_NEXT_FRAMEWORK) || (defined(__APPLE__) && defined(Py_ENABLE_SHARED)) +/* On Darwin/MacOSX a shared library or framework has no access to +** environ directly, we must obtain it with _NSGetEnviron(). See also +** man environ(7). +*/ +#include +#define USE_DARWIN_NS_GET_ENVIRON 1 +#elif !defined(MS_WINDOWS) && (!defined(__WATCOMC__) || defined(__QNX__) || defined(__VXWORKS__)) +extern char **environ; +#endif /* !MS_WINDOWS */ + +static PyObject * +convertenviron(void) +{ + PyObject *d; +#ifdef MS_WINDOWS + wchar_t **e; +#else + char **e; +#endif + + d = PyDict_New(); + if (d == NULL) + return NULL; +#ifdef MS_WINDOWS + /* _wenviron must be initialized in this way if the program is started + through main() instead of wmain(). */ + (void)_wgetenv(L""); + e = _wenviron; +#elif defined(USE_DARWIN_NS_GET_ENVIRON) + /* environ is not accessible as an extern in a shared object on OSX; use + _NSGetEnviron to resolve it. The value changes if you add environment + variables between calls to Py_Initialize, so don't cache the value. */ + e = *_NSGetEnviron(); +#else + e = environ; +#endif + if (e == NULL) + return d; + for (; *e != NULL; e++) { + PyObject *k; + PyObject *v; +#ifdef MS_WINDOWS + const wchar_t *p = wcschr(*e, L'='); +#else + const char *p = strchr(*e, '='); +#endif + if (p == NULL) + continue; +#ifdef MS_WINDOWS + k = PyUnicode_FromWideChar(*e, (Py_ssize_t)(p-*e)); +#else + k = PyBytes_FromStringAndSize(*e, (int)(p-*e)); +#endif + if (k == NULL) { + Py_DECREF(d); + return NULL; + } +#ifdef MS_WINDOWS + v = PyUnicode_FromWideChar(p+1, wcslen(p+1)); +#else + v = PyBytes_FromStringAndSize(p+1, strlen(p+1)); +#endif + if (v == NULL) { + Py_DECREF(k); + Py_DECREF(d); + return NULL; + } + if (PyDict_SetDefaultRef(d, k, v, NULL) < 0) { + Py_DECREF(v); + Py_DECREF(k); + Py_DECREF(d); + return NULL; + } + Py_DECREF(k); + Py_DECREF(v); + } + return d; +} + +/* Set a POSIX-specific error from errno, and return NULL */ + +static PyObject * +posix_error(void) +{ + return PyErr_SetFromErrno(PyExc_OSError); +} + +#ifdef MS_WINDOWS +static PyObject * +win32_error(const char* function, const char* filename) +{ + /* XXX We should pass the function name along in the future. + (winreg.c also wants to pass the function name.) + This would however require an additional param to the + Windows error object, which is non-trivial. + */ + errno = GetLastError(); + if (filename) + return PyErr_SetFromWindowsErrWithFilename(errno, filename); + else + return PyErr_SetFromWindowsErr(errno); +} + +static PyObject * +win32_error_object_err(const char* function, PyObject* filename, DWORD err) +{ + /* XXX - see win32_error for comments on 'function' */ + if (filename) + return PyErr_SetExcFromWindowsErrWithFilenameObject( + PyExc_OSError, + err, + filename); + else + return PyErr_SetFromWindowsErr(err); +} + +static PyObject * +win32_error_object(const char* function, PyObject* filename) +{ + errno = GetLastError(); + return win32_error_object_err(function, filename, errno); +} + +#endif /* MS_WINDOWS */ + +static PyObject * +posix_path_object_error(PyObject *path) +{ + return PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path); +} + +static PyObject * +path_object_error(PyObject *path) +{ +#ifdef MS_WINDOWS + return PyErr_SetExcFromWindowsErrWithFilenameObject( + PyExc_OSError, 0, path); +#else + return posix_path_object_error(path); +#endif +} + +static PyObject * +path_object_error2(PyObject *path, PyObject *path2) +{ +#ifdef MS_WINDOWS + return PyErr_SetExcFromWindowsErrWithFilenameObjects( + PyExc_OSError, 0, path, path2); +#else + return PyErr_SetFromErrnoWithFilenameObjects(PyExc_OSError, path, path2); +#endif +} + +static PyObject * +path_error(path_t *path) +{ + return path_object_error(path->object); +} + +static PyObject * +posix_path_error(path_t *path) +{ + return posix_path_object_error(path->object); +} + +static PyObject * +path_error2(path_t *path, path_t *path2) +{ + return path_object_error2(path->object, path2->object); +} + + +/* POSIX generic methods */ + +static PyObject * +posix_fildes_fd(int fd, int (*func)(int)) +{ + int res; + int async_err = 0; + + do { + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH + res = (*func)(fd); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + } while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + if (res != 0) + return (!async_err) ? posix_error() : NULL; + Py_RETURN_NONE; +} + + +#ifdef MS_WINDOWS +/* This is a reimplementation of the C library's chdir function, + but one that produces Win32 errors instead of DOS error codes. + chdir is essentially a wrapper around SetCurrentDirectory; however, + it also needs to set "magic" environment variables indicating + the per-drive current directory, which are of the form =: */ +static BOOL __stdcall +win32_wchdir(LPCWSTR path) +{ + wchar_t path_buf[MAX_PATH], *new_path = path_buf; + int result; + wchar_t env[4] = L"=x:"; + + if(!SetCurrentDirectoryW(path)) + return FALSE; + result = GetCurrentDirectoryW(Py_ARRAY_LENGTH(path_buf), new_path); + if (!result) + return FALSE; + if (result > Py_ARRAY_LENGTH(path_buf)) { + new_path = PyMem_RawMalloc(result * sizeof(wchar_t)); + if (!new_path) { + SetLastError(ERROR_OUTOFMEMORY); + return FALSE; + } + result = GetCurrentDirectoryW(result, new_path); + if (!result) { + PyMem_RawFree(new_path); + return FALSE; + } + } + int is_unc_like_path = (wcsncmp(new_path, L"\\\\", 2) == 0 || + wcsncmp(new_path, L"//", 2) == 0); + if (!is_unc_like_path) { + env[1] = new_path[0]; + result = SetEnvironmentVariableW(env, new_path); + } + if (new_path != path_buf) + PyMem_RawFree(new_path); + return result ? TRUE : FALSE; +} +#endif + +#ifdef MS_WINDOWS +/* The CRT of Windows has a number of flaws wrt. its stat() implementation: + - time stamps are restricted to second resolution + - file modification times suffer from forth-and-back conversions between + UTC and local time + Therefore, we implement our own stat, based on the Win32 API directly. +*/ +#define HAVE_STAT_NSEC 1 +#define HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES 1 +#define HAVE_STRUCT_STAT_ST_REPARSE_TAG 1 + +static void +find_data_to_file_info(WIN32_FIND_DATAW *pFileData, + BY_HANDLE_FILE_INFORMATION *info, + ULONG *reparse_tag) +{ + memset(info, 0, sizeof(*info)); + info->dwFileAttributes = pFileData->dwFileAttributes; + info->ftCreationTime = pFileData->ftCreationTime; + info->ftLastAccessTime = pFileData->ftLastAccessTime; + info->ftLastWriteTime = pFileData->ftLastWriteTime; + info->nFileSizeHigh = pFileData->nFileSizeHigh; + info->nFileSizeLow = pFileData->nFileSizeLow; +/* info->nNumberOfLinks = 1; */ + if (pFileData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) + *reparse_tag = pFileData->dwReserved0; + else + *reparse_tag = 0; +} + +static BOOL +attributes_from_dir(LPCWSTR pszFile, BY_HANDLE_FILE_INFORMATION *info, ULONG *reparse_tag) +{ + HANDLE hFindFile; + WIN32_FIND_DATAW FileData; + LPCWSTR filename = pszFile; + size_t n = wcslen(pszFile); + if (n && (pszFile[n - 1] == L'\\' || pszFile[n - 1] == L'/')) { + // cannot use PyMem_Malloc here because we do not hold the GIL + filename = (LPCWSTR)malloc((n + 1) * sizeof(filename[0])); + if(!filename) { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + wcsncpy_s((LPWSTR)filename, n + 1, pszFile, n); + while (--n > 0 && (filename[n] == L'\\' || filename[n] == L'/')) { + ((LPWSTR)filename)[n] = L'\0'; + } + if (!n || (n == 1 && filename[1] == L':')) { + // Nothing left to query + free((void *)filename); + return FALSE; + } + } + hFindFile = FindFirstFileW(filename, &FileData); + if (pszFile != filename) { + free((void *)filename); + } + if (hFindFile == INVALID_HANDLE_VALUE) { + return FALSE; + } + FindClose(hFindFile); + find_data_to_file_info(&FileData, info, reparse_tag); + return TRUE; +} + + +static void +update_st_mode_from_path(const wchar_t *path, DWORD attr, + struct _Py_stat_struct *result) +{ + if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) { + /* Fix the file execute permissions. This hack sets S_IEXEC if + the filename has an extension that is commonly used by files + that CreateProcessW can execute. A real implementation calls + GetSecurityInfo, OpenThreadToken/OpenProcessToken, and + AccessCheck to check for generic read, write, and execute + access. */ + const wchar_t *fileExtension = wcsrchr(path, '.'); + if (fileExtension) { + if (_wcsicmp(fileExtension, L".exe") == 0 || + _wcsicmp(fileExtension, L".bat") == 0 || + _wcsicmp(fileExtension, L".cmd") == 0 || + _wcsicmp(fileExtension, L".com") == 0) { + result->st_mode |= 0111; + } + } + } +} + + +static int +win32_xstat_slow_impl(const wchar_t *path, struct _Py_stat_struct *result, + BOOL traverse) +{ + HANDLE hFile; + BY_HANDLE_FILE_INFORMATION fileInfo; + FILE_BASIC_INFO basicInfo; + FILE_BASIC_INFO *pBasicInfo = NULL; + FILE_ID_INFO idInfo; + FILE_ID_INFO *pIdInfo = NULL; + FILE_ATTRIBUTE_TAG_INFO tagInfo = { 0 }; + DWORD fileType, error; + BOOL isUnhandledTag = FALSE; + int retval = 0; + + DWORD access = FILE_READ_ATTRIBUTES; + DWORD flags = FILE_FLAG_BACKUP_SEMANTICS; /* Allow opening directories. */ + if (!traverse) { + flags |= FILE_FLAG_OPEN_REPARSE_POINT; + } + + hFile = CreateFileW(path, access, 0, NULL, OPEN_EXISTING, flags, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + /* Either the path doesn't exist, or the caller lacks access. */ + error = GetLastError(); + switch (error) { + case ERROR_ACCESS_DENIED: /* Cannot sync or read attributes. */ + case ERROR_SHARING_VIOLATION: /* It's a paging file. */ + /* Try reading the parent directory. */ + if (!attributes_from_dir(path, &fileInfo, &tagInfo.ReparseTag)) { + /* Cannot read the parent directory. */ + switch (GetLastError()) { + case ERROR_FILE_NOT_FOUND: /* File cannot be found */ + case ERROR_PATH_NOT_FOUND: /* File parent directory cannot be found */ + case ERROR_NOT_READY: /* Drive exists but unavailable */ + case ERROR_BAD_NET_NAME: /* Remote drive unavailable */ + break; + /* Restore the error from CreateFileW(). */ + default: + SetLastError(error); + } + + return -1; + } + if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + if (traverse || + !IsReparseTagNameSurrogate(tagInfo.ReparseTag)) { + /* The stat call has to traverse but cannot, so fail. */ + SetLastError(error); + return -1; + } + } + break; + + case ERROR_INVALID_PARAMETER: + /* \\.\con requires read or write access. */ + hFile = CreateFileW(path, access | GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, flags, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + SetLastError(error); + return -1; + } + break; + + case ERROR_CANT_ACCESS_FILE: + /* bpo37834: open unhandled reparse points if traverse fails. */ + if (traverse) { + traverse = FALSE; + isUnhandledTag = TRUE; + hFile = CreateFileW(path, access, 0, NULL, OPEN_EXISTING, + flags | FILE_FLAG_OPEN_REPARSE_POINT, NULL); + } + if (hFile == INVALID_HANDLE_VALUE) { + SetLastError(error); + return -1; + } + break; + + default: + return -1; + } + } + + if (hFile != INVALID_HANDLE_VALUE) { + /* Handle types other than files on disk. */ + fileType = GetFileType(hFile); + if (fileType != FILE_TYPE_DISK) { + if (fileType == FILE_TYPE_UNKNOWN && GetLastError() != 0) { + retval = -1; + goto cleanup; + } + DWORD fileAttributes = GetFileAttributesW(path); + memset(result, 0, sizeof(*result)); + if (fileAttributes != INVALID_FILE_ATTRIBUTES && + fileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + /* \\.\pipe\ or \\.\mailslot\ */ + result->st_mode = _S_IFDIR; + } else if (fileType == FILE_TYPE_CHAR) { + /* \\.\nul */ + result->st_mode = _S_IFCHR; + } else if (fileType == FILE_TYPE_PIPE) { + /* \\.\pipe\spam */ + result->st_mode = _S_IFIFO; + } + /* FILE_TYPE_UNKNOWN, e.g. \\.\mailslot\waitfor.exe\spam */ + goto cleanup; + } + + /* Query the reparse tag, and traverse a non-link. */ + if (!traverse) { + if (!GetFileInformationByHandleEx(hFile, FileAttributeTagInfo, + &tagInfo, sizeof(tagInfo))) { + /* Allow devices that do not support FileAttributeTagInfo. */ + switch (GetLastError()) { + case ERROR_INVALID_PARAMETER: + case ERROR_INVALID_FUNCTION: + case ERROR_NOT_SUPPORTED: + tagInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; + tagInfo.ReparseTag = 0; + break; + default: + retval = -1; + goto cleanup; + } + } else if (tagInfo.FileAttributes & + FILE_ATTRIBUTE_REPARSE_POINT) { + if (IsReparseTagNameSurrogate(tagInfo.ReparseTag)) { + if (isUnhandledTag) { + /* Traversing previously failed for either this link + or its target. */ + SetLastError(ERROR_CANT_ACCESS_FILE); + retval = -1; + goto cleanup; + } + /* Traverse a non-link, but not if traversing already failed + for an unhandled tag. */ + } else if (!isUnhandledTag) { + CloseHandle(hFile); + return win32_xstat_slow_impl(path, result, TRUE); + } + } + } + + if (!GetFileInformationByHandle(hFile, &fileInfo) || + !GetFileInformationByHandleEx(hFile, FileBasicInfo, + &basicInfo, sizeof(basicInfo))) { + switch (GetLastError()) { + case ERROR_INVALID_PARAMETER: + case ERROR_INVALID_FUNCTION: + case ERROR_NOT_SUPPORTED: + /* Volumes and physical disks are block devices, e.g. + \\.\C: and \\.\PhysicalDrive0. */ + memset(result, 0, sizeof(*result)); + result->st_mode = 0x6000; /* S_IFBLK */ + goto cleanup; + } + retval = -1; + goto cleanup; + } + + /* Successfully got FileBasicInfo, so we'll pass it along */ + pBasicInfo = &basicInfo; + + if (GetFileInformationByHandleEx(hFile, FileIdInfo, &idInfo, sizeof(idInfo))) { + /* Successfully got FileIdInfo, so pass it along */ + pIdInfo = &idInfo; + } + } + + _Py_attribute_data_to_stat(&fileInfo, tagInfo.ReparseTag, pBasicInfo, pIdInfo, result); + update_st_mode_from_path(path, fileInfo.dwFileAttributes, result); + +cleanup: + if (hFile != INVALID_HANDLE_VALUE) { + /* Preserve last error if we are failing */ + error = retval ? GetLastError() : 0; + if (!CloseHandle(hFile)) { + retval = -1; + } else if (retval) { + /* Restore last error */ + SetLastError(error); + } + } + + return retval; +} + +static int +win32_xstat_impl(const wchar_t *path, struct _Py_stat_struct *result, + BOOL traverse) +{ + FILE_STAT_BASIC_INFORMATION statInfo; + if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo, + &statInfo, sizeof(statInfo))) { + if (// Cannot use fast path for reparse points ... + !(statInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) + // ... unless it's a name surrogate (symlink) and we're not following + || (!traverse && IsReparseTagNameSurrogate(statInfo.ReparseTag)) + ) { + _Py_stat_basic_info_to_stat(&statInfo, result); + update_st_mode_from_path(path, statInfo.FileAttributes, result); + return 0; + } + } else { + switch(GetLastError()) { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + case ERROR_NOT_READY: + case ERROR_BAD_NET_NAME: + /* These errors aren't worth retrying with the slow path */ + return -1; + case ERROR_NOT_SUPPORTED: + /* indicates the API couldn't be loaded */ + break; + } + } + + return win32_xstat_slow_impl(path, result, traverse); +} + +static int +win32_xstat(const wchar_t *path, struct _Py_stat_struct *result, BOOL traverse) +{ + /* Protocol violation: we explicitly clear errno, instead of + setting it to a POSIX error. Callers should use GetLastError. */ + int code = win32_xstat_impl(path, result, traverse); + errno = 0; + + /* ctime is only deprecated from 3.12, so we copy birthtime across */ + result->st_ctime = result->st_birthtime; + result->st_ctime_nsec = result->st_birthtime_nsec; + return code; +} +/* About the following functions: win32_lstat_w, win32_stat, win32_stat_w + + In Posix, stat automatically traverses symlinks and returns the stat + structure for the target. In Windows, the equivalent GetFileAttributes by + default does not traverse symlinks and instead returns attributes for + the symlink. + + Instead, we will open the file (which *does* traverse symlinks by default) + and GetFileInformationByHandle(). */ + +static int +win32_lstat(const wchar_t* path, struct _Py_stat_struct *result) +{ + return win32_xstat(path, result, FALSE); +} + +static int +win32_stat(const wchar_t* path, struct _Py_stat_struct *result) +{ + return win32_xstat(path, result, TRUE); +} + +#endif /* MS_WINDOWS */ + +PyDoc_STRVAR(stat_result__doc__, +"stat_result: Result from stat, fstat, or lstat.\n\n\ +This object may be accessed either as a tuple of\n\ + (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)\n\ +or via the attributes st_mode, st_ino, st_dev, st_nlink, st_uid, and so on.\n\ +\n\ +Posix/windows: If your platform supports st_blksize, st_blocks, st_rdev,\n\ +or st_flags, they are available as attributes only.\n\ +\n\ +See os.stat for more information."); + +static PyStructSequence_Field stat_result_fields[] = { + {"st_mode", "protection bits"}, + {"st_ino", "inode"}, + {"st_dev", "device"}, + {"st_nlink", "number of hard links"}, + {"st_uid", "user ID of owner"}, + {"st_gid", "group ID of owner"}, + {"st_size", "total size, in bytes"}, + /* The NULL is replaced with PyStructSequence_UnnamedField later. */ + {NULL, "integer time of last access"}, + {NULL, "integer time of last modification"}, + {NULL, "integer time of last change"}, + {"st_atime", "time of last access"}, + {"st_mtime", "time of last modification"}, + {"st_ctime", "time of last change"}, + {"st_atime_ns", "time of last access in nanoseconds"}, + {"st_mtime_ns", "time of last modification in nanoseconds"}, + {"st_ctime_ns", "time of last change in nanoseconds"}, +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + {"st_blksize", "blocksize for filesystem I/O"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS + {"st_blocks", "number of blocks allocated"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_RDEV + {"st_rdev", "device type (if inode device)"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_FLAGS + {"st_flags", "user defined flags for file"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_GEN + {"st_gen", "generation number"}, +#endif +#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) || defined(MS_WINDOWS) + {"st_birthtime", "time of creation"}, +#endif +#ifdef MS_WINDOWS + {"st_birthtime_ns", "time of creation in nanoseconds"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES + {"st_file_attributes", "Windows file attribute bits"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_FSTYPE + {"st_fstype", "Type of filesystem"}, +#endif +#ifdef HAVE_STRUCT_STAT_ST_REPARSE_TAG + {"st_reparse_tag", "Windows reparse tag"}, +#endif + {0} +}; + +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE +#define ST_BLKSIZE_IDX 16 +#else +#define ST_BLKSIZE_IDX 15 +#endif + +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS +#define ST_BLOCKS_IDX (ST_BLKSIZE_IDX+1) +#else +#define ST_BLOCKS_IDX ST_BLKSIZE_IDX +#endif + +#ifdef HAVE_STRUCT_STAT_ST_RDEV +#define ST_RDEV_IDX (ST_BLOCKS_IDX+1) +#else +#define ST_RDEV_IDX ST_BLOCKS_IDX +#endif + +#ifdef HAVE_STRUCT_STAT_ST_FLAGS +#define ST_FLAGS_IDX (ST_RDEV_IDX+1) +#else +#define ST_FLAGS_IDX ST_RDEV_IDX +#endif + +#ifdef HAVE_STRUCT_STAT_ST_GEN +#define ST_GEN_IDX (ST_FLAGS_IDX+1) +#else +#define ST_GEN_IDX ST_FLAGS_IDX +#endif + +#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) || defined(MS_WINDOWS) +#define ST_BIRTHTIME_IDX (ST_GEN_IDX+1) +#else +#define ST_BIRTHTIME_IDX ST_GEN_IDX +#endif + +#ifdef MS_WINDOWS +#define ST_BIRTHTIME_NS_IDX (ST_BIRTHTIME_IDX+1) +#else +#define ST_BIRTHTIME_NS_IDX ST_BIRTHTIME_IDX +#endif + +#if defined(HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES) || defined(MS_WINDOWS) +#define ST_FILE_ATTRIBUTES_IDX (ST_BIRTHTIME_NS_IDX+1) +#else +#define ST_FILE_ATTRIBUTES_IDX ST_BIRTHTIME_NS_IDX +#endif + +#ifdef HAVE_STRUCT_STAT_ST_FSTYPE +#define ST_FSTYPE_IDX (ST_FILE_ATTRIBUTES_IDX+1) +#else +#define ST_FSTYPE_IDX ST_FILE_ATTRIBUTES_IDX +#endif + +#ifdef HAVE_STRUCT_STAT_ST_REPARSE_TAG +#define ST_REPARSE_TAG_IDX (ST_FSTYPE_IDX+1) +#else +#define ST_REPARSE_TAG_IDX ST_FSTYPE_IDX +#endif + +static PyStructSequence_Desc stat_result_desc = { + "stat_result", /* name */ + stat_result__doc__, /* doc */ + stat_result_fields, + 10 +}; + +PyDoc_STRVAR(statvfs_result__doc__, +"statvfs_result: Result from statvfs or fstatvfs.\n\n\ +This object may be accessed either as a tuple of\n\ + (bsize, frsize, blocks, bfree, bavail, files, ffree, favail, flag, namemax),\n\ +or via the attributes f_bsize, f_frsize, f_blocks, f_bfree, and so on.\n\ +\n\ +See os.statvfs for more information."); + +static PyStructSequence_Field statvfs_result_fields[] = { + {"f_bsize", }, + {"f_frsize", }, + {"f_blocks", }, + {"f_bfree", }, + {"f_bavail", }, + {"f_files", }, + {"f_ffree", }, + {"f_favail", }, + {"f_flag", }, + {"f_namemax",}, + {"f_fsid", }, + {0} +}; + +static PyStructSequence_Desc statvfs_result_desc = { + "statvfs_result", /* name */ + statvfs_result__doc__, /* doc */ + statvfs_result_fields, + 10 +}; + +#if defined(HAVE_WAITID) +PyDoc_STRVAR(waitid_result__doc__, +"waitid_result: Result from waitid.\n\n\ +This object may be accessed either as a tuple of\n\ + (si_pid, si_uid, si_signo, si_status, si_code),\n\ +or via the attributes si_pid, si_uid, and so on.\n\ +\n\ +See os.waitid for more information."); + +static PyStructSequence_Field waitid_result_fields[] = { + {"si_pid", }, + {"si_uid", }, + {"si_signo", }, + {"si_status", }, + {"si_code", }, + {0} +}; + +static PyStructSequence_Desc waitid_result_desc = { + "waitid_result", /* name */ + waitid_result__doc__, /* doc */ + waitid_result_fields, + 5 +}; +#endif + +static PyObject * +statresult_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyStructSequence *result; + int i; + + // ht_module doesn't get set in PyStructSequence_NewType(), + // so we can't use PyType_GetModule(). + PyObject *mod = PyImport_GetModule(MODNAME_OBJ); + if (mod == NULL) { + return NULL; + } + _posixstate *state = get_posix_state(mod); + Py_DECREF(mod); + if (state == NULL) { + return NULL; + } +#define structseq_new state->statresult_new_orig + + result = (PyStructSequence*)structseq_new(type, args, kwds); + if (!result) + return NULL; + /* If we have been initialized from a tuple, + st_?time might be set to None. Initialize it + from the int slots. */ + for (i = 7; i <= 9; i++) { + if (result->ob_item[i+3] == Py_None) { + Py_DECREF(Py_None); + result->ob_item[i+3] = Py_NewRef(result->ob_item[i]); + } + } + return (PyObject*)result; +} + +static int +_posix_clear(PyObject *module) +{ + _posixstate *state = get_posix_state(module); + Py_CLEAR(state->billion); + Py_CLEAR(state->DirEntryType); + Py_CLEAR(state->ScandirIteratorType); +#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) + Py_CLEAR(state->SchedParamType); +#endif + Py_CLEAR(state->StatResultType); + Py_CLEAR(state->StatVFSResultType); + Py_CLEAR(state->TerminalSizeType); + Py_CLEAR(state->TimesResultType); + Py_CLEAR(state->UnameResultType); +#if defined(HAVE_WAITID) + Py_CLEAR(state->WaitidResultType); +#endif +#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4) + Py_CLEAR(state->struct_rusage); +#endif + Py_CLEAR(state->st_mode); + return 0; +} + +static int +_posix_traverse(PyObject *module, visitproc visit, void *arg) +{ + _posixstate *state = get_posix_state(module); + Py_VISIT(state->billion); + Py_VISIT(state->DirEntryType); + Py_VISIT(state->ScandirIteratorType); +#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) + Py_VISIT(state->SchedParamType); +#endif + Py_VISIT(state->StatResultType); + Py_VISIT(state->StatVFSResultType); + Py_VISIT(state->TerminalSizeType); + Py_VISIT(state->TimesResultType); + Py_VISIT(state->UnameResultType); +#if defined(HAVE_WAITID) + Py_VISIT(state->WaitidResultType); +#endif +#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4) + Py_VISIT(state->struct_rusage); +#endif + Py_VISIT(state->st_mode); + return 0; +} + +static void +_posix_free(void *module) +{ + _posix_clear((PyObject *)module); +} + +static int +fill_time(PyObject *module, PyObject *v, int s_index, int f_index, int ns_index, time_t sec, unsigned long nsec) +{ + assert(!PyErr_Occurred()); + + int res = -1; + PyObject *s_in_ns = NULL; + PyObject *ns_total = NULL; + PyObject *float_s = NULL; + + PyObject *s = _PyLong_FromTime_t(sec); + PyObject *ns_fractional = PyLong_FromUnsignedLong(nsec); + if (!(s && ns_fractional)) { + goto exit; + } + + s_in_ns = PyNumber_Multiply(s, get_posix_state(module)->billion); + if (!s_in_ns) { + goto exit; + } + + ns_total = PyNumber_Add(s_in_ns, ns_fractional); + if (!ns_total) + goto exit; + + float_s = PyFloat_FromDouble(sec + 1e-9*nsec); + if (!float_s) { + goto exit; + } + + if (s_index >= 0) { + PyStructSequence_SET_ITEM(v, s_index, s); + s = NULL; + } + if (f_index >= 0) { + PyStructSequence_SET_ITEM(v, f_index, float_s); + float_s = NULL; + } + if (ns_index >= 0) { + PyStructSequence_SET_ITEM(v, ns_index, ns_total); + ns_total = NULL; + } + + assert(!PyErr_Occurred()); + res = 0; + +exit: + Py_XDECREF(s); + Py_XDECREF(ns_fractional); + Py_XDECREF(s_in_ns); + Py_XDECREF(ns_total); + Py_XDECREF(float_s); + return res; +} + +#ifdef MS_WINDOWS +static PyObject* +_pystat_l128_from_l64_l64(uint64_t low, uint64_t high) +{ + PyObject *o_low = PyLong_FromUnsignedLongLong(low); + if (!o_low || !high) { + return o_low; + } + PyObject *o_high = PyLong_FromUnsignedLongLong(high); + PyObject *l64 = o_high ? PyLong_FromLong(64) : NULL; + if (!l64) { + Py_XDECREF(o_high); + Py_DECREF(o_low); + return NULL; + } + Py_SETREF(o_high, PyNumber_Lshift(o_high, l64)); + Py_DECREF(l64); + if (!o_high) { + Py_DECREF(o_low); + return NULL; + } + Py_SETREF(o_low, PyNumber_Add(o_low, o_high)); + Py_DECREF(o_high); + return o_low; +} +#endif + +/* pack a system stat C structure into the Python stat tuple + (used by posix_stat() and posix_fstat()) */ +static PyObject* +_pystat_fromstructstat(PyObject *module, STRUCT_STAT *st) +{ + assert(!PyErr_Occurred()); + + PyObject *StatResultType = get_posix_state(module)->StatResultType; + PyObject *v = PyStructSequence_New((PyTypeObject *)StatResultType); + if (v == NULL) { + return NULL; + } + +#define SET_ITEM(pos, expr) \ + do { \ + PyObject *obj = (expr); \ + if (obj == NULL) { \ + goto error; \ + } \ + PyStructSequence_SET_ITEM(v, (pos), obj); \ + } while (0) + + SET_ITEM(0, PyLong_FromLong((long)st->st_mode)); +#ifdef MS_WINDOWS + SET_ITEM(1, _pystat_l128_from_l64_l64(st->st_ino, st->st_ino_high)); + SET_ITEM(2, PyLong_FromUnsignedLongLong(st->st_dev)); +#else + static_assert(sizeof(unsigned long long) >= sizeof(st->st_ino), + "stat.st_ino is larger than unsigned long long"); + SET_ITEM(1, PyLong_FromUnsignedLongLong(st->st_ino)); + SET_ITEM(2, _PyLong_FromDev(st->st_dev)); +#endif + SET_ITEM(3, PyLong_FromLong((long)st->st_nlink)); +#if defined(MS_WINDOWS) + SET_ITEM(4, PyLong_FromLong(0)); + SET_ITEM(5, PyLong_FromLong(0)); +#else + SET_ITEM(4, _PyLong_FromUid(st->st_uid)); + SET_ITEM(5, _PyLong_FromGid(st->st_gid)); +#endif + static_assert(sizeof(long long) >= sizeof(st->st_size), + "stat.st_size is larger than long long"); + SET_ITEM(6, PyLong_FromLongLong(st->st_size)); + + // Set st_atime, st_mtime and st_ctime + unsigned long ansec, mnsec, cnsec; +#if defined(HAVE_STAT_TV_NSEC) + ansec = st->st_atim.tv_nsec; + mnsec = st->st_mtim.tv_nsec; + cnsec = st->st_ctim.tv_nsec; +#elif defined(HAVE_STAT_TV_NSEC2) + ansec = st->st_atimespec.tv_nsec; + mnsec = st->st_mtimespec.tv_nsec; + cnsec = st->st_ctimespec.tv_nsec; +#elif defined(HAVE_STAT_NSEC) + ansec = st->st_atime_nsec; + mnsec = st->st_mtime_nsec; + cnsec = st->st_ctime_nsec; +#else + ansec = mnsec = cnsec = 0; +#endif + if (fill_time(module, v, 7, 10, 13, st->st_atime, ansec) < 0) { + goto error; + } + if (fill_time(module, v, 8, 11, 14, st->st_mtime, mnsec) < 0) { + goto error; + } + if (fill_time(module, v, 9, 12, 15, st->st_ctime, cnsec) < 0) { + goto error; + } + +#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE + SET_ITEM(ST_BLKSIZE_IDX, PyLong_FromLong((long)st->st_blksize)); +#endif +#ifdef HAVE_STRUCT_STAT_ST_BLOCKS + SET_ITEM(ST_BLOCKS_IDX, PyLong_FromLong((long)st->st_blocks)); +#endif +#ifdef HAVE_STRUCT_STAT_ST_RDEV + SET_ITEM(ST_RDEV_IDX, PyLong_FromLong((long)st->st_rdev)); +#endif +#ifdef HAVE_STRUCT_STAT_ST_GEN + SET_ITEM(ST_GEN_IDX, PyLong_FromLong((long)st->st_gen)); +#endif +#if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME) + { + unsigned long bsec, bnsec; + bsec = (long)st->st_birthtime; +#ifdef HAVE_STAT_TV_NSEC2 + bnsec = st->st_birthtimespec.tv_nsec; +#else + bnsec = 0; +#endif + SET_ITEM(ST_BIRTHTIME_IDX, PyFloat_FromDouble(bsec + bnsec * 1e-9)); + } +#elif defined(MS_WINDOWS) + if (fill_time(module, v, -1, ST_BIRTHTIME_IDX, ST_BIRTHTIME_NS_IDX, + st->st_birthtime, st->st_birthtime_nsec) < 0) { + goto error; + } +#endif +#ifdef HAVE_STRUCT_STAT_ST_FLAGS + SET_ITEM(ST_FLAGS_IDX, PyLong_FromLong((long)st->st_flags)); +#endif +#ifdef HAVE_STRUCT_STAT_ST_FILE_ATTRIBUTES + SET_ITEM(ST_FILE_ATTRIBUTES_IDX, + PyLong_FromUnsignedLong(st->st_file_attributes)); +#endif +#ifdef HAVE_STRUCT_STAT_ST_FSTYPE + SET_ITEM(ST_FSTYPE_IDX, PyUnicode_FromString(st->st_fstype)); +#endif +#ifdef HAVE_STRUCT_STAT_ST_REPARSE_TAG + SET_ITEM(ST_REPARSE_TAG_IDX, PyLong_FromUnsignedLong(st->st_reparse_tag)); +#endif + + assert(!PyErr_Occurred()); + return v; + +error: + Py_DECREF(v); + return NULL; + +#undef SET_ITEM +} + +/* POSIX methods */ + + +static PyObject * +posix_do_stat(PyObject *module, const char *function_name, path_t *path, + int dir_fd, int follow_symlinks) +{ + STRUCT_STAT st; + int result; + +#ifdef HAVE_FSTATAT + int fstatat_unavailable = 0; +#endif + +#if !defined(MS_WINDOWS) && !defined(HAVE_FSTATAT) && !defined(HAVE_LSTAT) + if (follow_symlinks_specified(function_name, follow_symlinks)) + return NULL; +#endif + + if (path_and_dir_fd_invalid("stat", path, dir_fd) || + dir_fd_and_fd_invalid("stat", dir_fd, path->fd) || + fd_and_follow_symlinks_invalid("stat", path->fd, follow_symlinks)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + if (path->fd != -1) + result = FSTAT(path->fd, &st); +#ifdef MS_WINDOWS + else if (follow_symlinks) + result = win32_stat(path->wide, &st); + else + result = win32_lstat(path->wide, &st); +#else + else +#if defined(HAVE_LSTAT) + if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD)) + result = LSTAT(path->narrow, &st); + else +#endif /* HAVE_LSTAT */ +#ifdef HAVE_FSTATAT + if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks) { + if (HAVE_FSTATAT_RUNTIME) { + result = fstatat(dir_fd, path->narrow, &st, + follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW); + + } else { + fstatat_unavailable = 1; + } + } else +#endif /* HAVE_FSTATAT */ + result = STAT(path->narrow, &st); +#endif /* MS_WINDOWS */ + Py_END_ALLOW_THREADS + +#ifdef HAVE_FSTATAT + if (fstatat_unavailable) { + argument_unavailable_error("stat", "dir_fd"); + return NULL; + } +#endif + + if (result != 0) { + return path_error(path); + } + + return _pystat_fromstructstat(module, &st); +} + +/*[python input] + +for s in """ + +FACCESSAT +FCHMODAT +FCHOWNAT +FSTATAT +LINKAT +MKDIRAT +MKFIFOAT +MKNODAT +OPENAT +READLINKAT +SYMLINKAT +UNLINKAT + +""".strip().split(): + s = s.strip() + print(""" +#ifdef HAVE_{s} + #define {s}_DIR_FD_CONVERTER dir_fd_converter +#else + #define {s}_DIR_FD_CONVERTER dir_fd_unavailable +#endif +""".rstrip().format(s=s)) + +for s in """ + +FCHDIR +FCHMOD +FCHOWN +FDOPENDIR +FEXECVE +FPATHCONF +FSTATVFS +FTRUNCATE + +""".strip().split(): + s = s.strip() + print(""" +#ifdef HAVE_{s} + #define PATH_HAVE_{s} 1 +#else + #define PATH_HAVE_{s} 0 +#endif + +""".rstrip().format(s=s)) +[python start generated code]*/ + +#ifdef HAVE_FACCESSAT + #define FACCESSAT_DIR_FD_CONVERTER dir_fd_converter +#else + #define FACCESSAT_DIR_FD_CONVERTER dir_fd_unavailable +#endif + +#ifdef HAVE_FCHMODAT + #define FCHMODAT_DIR_FD_CONVERTER dir_fd_converter +#else + #define FCHMODAT_DIR_FD_CONVERTER dir_fd_unavailable +#endif + +#ifdef HAVE_FCHOWNAT + #define FCHOWNAT_DIR_FD_CONVERTER dir_fd_converter +#else + #define FCHOWNAT_DIR_FD_CONVERTER dir_fd_unavailable +#endif + +#ifdef HAVE_FSTATAT + #define FSTATAT_DIR_FD_CONVERTER dir_fd_converter +#else + #define FSTATAT_DIR_FD_CONVERTER dir_fd_unavailable +#endif + +#ifdef HAVE_LINKAT + #define LINKAT_DIR_FD_CONVERTER dir_fd_converter +#else + #define LINKAT_DIR_FD_CONVERTER dir_fd_unavailable +#endif + +#ifdef HAVE_MKDIRAT + #define MKDIRAT_DIR_FD_CONVERTER dir_fd_converter +#else + #define MKDIRAT_DIR_FD_CONVERTER dir_fd_unavailable +#endif + +#ifdef HAVE_MKFIFOAT + #define MKFIFOAT_DIR_FD_CONVERTER dir_fd_converter +#else + #define MKFIFOAT_DIR_FD_CONVERTER dir_fd_unavailable +#endif + +#ifdef HAVE_MKNODAT + #define MKNODAT_DIR_FD_CONVERTER dir_fd_converter +#else + #define MKNODAT_DIR_FD_CONVERTER dir_fd_unavailable +#endif + +#ifdef HAVE_OPENAT + #define OPENAT_DIR_FD_CONVERTER dir_fd_converter +#else + #define OPENAT_DIR_FD_CONVERTER dir_fd_unavailable +#endif + +#ifdef HAVE_READLINKAT + #define READLINKAT_DIR_FD_CONVERTER dir_fd_converter +#else + #define READLINKAT_DIR_FD_CONVERTER dir_fd_unavailable +#endif + +#ifdef HAVE_SYMLINKAT + #define SYMLINKAT_DIR_FD_CONVERTER dir_fd_converter +#else + #define SYMLINKAT_DIR_FD_CONVERTER dir_fd_unavailable +#endif + +#ifdef HAVE_UNLINKAT + #define UNLINKAT_DIR_FD_CONVERTER dir_fd_converter +#else + #define UNLINKAT_DIR_FD_CONVERTER dir_fd_unavailable +#endif + +#ifdef HAVE_FCHDIR + #define PATH_HAVE_FCHDIR 1 +#else + #define PATH_HAVE_FCHDIR 0 +#endif + +#ifdef HAVE_FCHMOD + #define PATH_HAVE_FCHMOD 1 +#else + #define PATH_HAVE_FCHMOD 0 +#endif + +#ifdef HAVE_FCHOWN + #define PATH_HAVE_FCHOWN 1 +#else + #define PATH_HAVE_FCHOWN 0 +#endif + +#ifdef HAVE_FDOPENDIR + #define PATH_HAVE_FDOPENDIR 1 +#else + #define PATH_HAVE_FDOPENDIR 0 +#endif + +#ifdef HAVE_FEXECVE + #define PATH_HAVE_FEXECVE 1 +#else + #define PATH_HAVE_FEXECVE 0 +#endif + +#ifdef HAVE_FPATHCONF + #define PATH_HAVE_FPATHCONF 1 +#else + #define PATH_HAVE_FPATHCONF 0 +#endif + +#ifdef HAVE_FSTATVFS + #define PATH_HAVE_FSTATVFS 1 +#else + #define PATH_HAVE_FSTATVFS 0 +#endif + +#ifdef HAVE_FTRUNCATE + #define PATH_HAVE_FTRUNCATE 1 +#else + #define PATH_HAVE_FTRUNCATE 0 +#endif +/*[python end generated code: output=4bd4f6f7d41267f1 input=80b4c890b6774ea5]*/ + +#ifdef MS_WINDOWS + #undef PATH_HAVE_FTRUNCATE + #define PATH_HAVE_FTRUNCATE 1 + #undef PATH_HAVE_FCHMOD + #define PATH_HAVE_FCHMOD 1 +#endif + +/*[python input] + +class path_t_converter(CConverter): + + type = "path_t" + impl_by_reference = True + parse_by_reference = True + + converter = 'path_converter' + + def converter_init(self, *, allow_fd=False, make_wide=None, + nonstrict=False, nullable=False, + suppress_value_error=False): + # right now path_t doesn't support default values. + # to support a default value, you'll need to override initialize(). + if self.default not in (unspecified, None): + fail("Can't specify a default to the path_t converter!") + + if self.c_default not in (None, 'Py_None'): + raise RuntimeError("Can't specify a c_default to the path_t converter!") + + self.nullable = nullable + self.nonstrict = nonstrict + self.make_wide = make_wide + self.suppress_value_error = suppress_value_error + self.allow_fd = allow_fd + + def pre_render(self): + def strify(value): + if isinstance(value, str): + return value + return str(int(bool(value))) + + # add self.py_name here when merging with posixmodule conversion + if self.make_wide is None: + self.c_default = 'PATH_T_INITIALIZE_P("{}", "{}", {}, {}, {}, {})'.format( + self.function.name, + self.name, + strify(self.nullable), + strify(self.nonstrict), + strify(self.suppress_value_error), + strify(self.allow_fd), + ) + else: + self.c_default = 'PATH_T_INITIALIZE("{}", "{}", {}, {}, {}, {}, {})'.format( + self.function.name, + self.name, + strify(self.nullable), + strify(self.nonstrict), + strify(self.make_wide), + strify(self.suppress_value_error), + strify(self.allow_fd), + ) + + def cleanup(self): + return "path_cleanup(&" + self.name + ");\n" + + +class dir_fd_converter(CConverter): + type = 'int' + + def converter_init(self, requires=None): + if self.default in (unspecified, None): + self.c_default = 'DEFAULT_DIR_FD' + if isinstance(requires, str): + self.converter = requires.upper() + '_DIR_FD_CONVERTER' + else: + self.converter = 'dir_fd_converter' + +class uid_t_converter(CConverter): + type = "uid_t" + converter = '_Py_Uid_Converter' + +class gid_t_converter(CConverter): + type = "gid_t" + converter = '_Py_Gid_Converter' + +class dev_t_converter(CConverter): + type = 'dev_t' + converter = '_Py_Dev_Converter' + +class dev_t_return_converter(unsigned_long_return_converter): + type = 'dev_t' + conversion_fn = '_PyLong_FromDev' + unsigned_cast = '(dev_t)' + +class FSConverter_converter(CConverter): + type = 'PyObject *' + converter = 'PyUnicode_FSConverter' + def converter_init(self): + if self.default is not unspecified: + fail("FSConverter_converter does not support default values") + self.c_default = 'NULL' + + def cleanup(self): + return "Py_XDECREF(" + self.name + ");\n" + +class pid_t_converter(CConverter): + type = 'pid_t' + format_unit = '" _Py_PARSE_PID "' + +class idtype_t_converter(int_converter): + type = 'idtype_t' + +class id_t_converter(CConverter): + type = 'id_t' + format_unit = '" _Py_PARSE_PID "' + +class intptr_t_converter(CConverter): + type = 'intptr_t' + format_unit = '" _Py_PARSE_INTPTR "' + +class Py_off_t_converter(CConverter): + type = 'Py_off_t' + converter = 'Py_off_t_converter' + +class Py_off_t_return_converter(long_return_converter): + type = 'Py_off_t' + conversion_fn = 'PyLong_FromPy_off_t' + +class path_confname_converter(CConverter): + type="int" + converter="conv_path_confname" + +class confstr_confname_converter(path_confname_converter): + converter='conv_confstr_confname' + +class sysconf_confname_converter(path_confname_converter): + converter="conv_sysconf_confname" + +[python start generated code]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=577cb476e5d64960]*/ + +/*[clinic input] + +os.stat + + path : path_t(allow_fd=True) + Path to be examined; can be string, bytes, a path-like object or + open-file-descriptor int. + + * + + dir_fd : dir_fd(requires='fstatat') = None + If not None, it should be a file descriptor open to a directory, + and path should be a relative string; path will then be relative to + that directory. + + follow_symlinks: bool = True + If False, and the last element of the path is a symbolic link, + stat will examine the symbolic link itself instead of the file + the link points to. + +Perform a stat system call on the given path. + +dir_fd and follow_symlinks may not be implemented + on your platform. If they are unavailable, using them will raise a + NotImplementedError. + +It's an error to use dir_fd or follow_symlinks when specifying path as + an open file descriptor. + +[clinic start generated code]*/ + +static PyObject * +os_stat_impl(PyObject *module, path_t *path, int dir_fd, int follow_symlinks) +/*[clinic end generated code: output=7d4976e6f18a59c5 input=01d362ebcc06996b]*/ +{ + return posix_do_stat(module, "stat", path, dir_fd, follow_symlinks); +} + + +/*[clinic input] +os.lstat + + path : path_t + + * + + dir_fd : dir_fd(requires='fstatat') = None + +Perform a stat system call on the given path, without following symbolic links. + +Like stat(), but do not follow symbolic links. +Equivalent to stat(path, follow_symlinks=False). +[clinic start generated code]*/ + +static PyObject * +os_lstat_impl(PyObject *module, path_t *path, int dir_fd) +/*[clinic end generated code: output=ef82a5d35ce8ab37 input=0b7474765927b925]*/ +{ + int follow_symlinks = 0; + return posix_do_stat(module, "lstat", path, dir_fd, follow_symlinks); +} + + +/*[clinic input] +os.access -> bool + + path: path_t + Path to be tested; can be string, bytes, or a path-like object. + + mode: int + Operating-system mode bitfield. Can be F_OK to test existence, + or the inclusive-OR of R_OK, W_OK, and X_OK. + + * + + dir_fd : dir_fd(requires='faccessat') = None + If not None, it should be a file descriptor open to a directory, + and path should be relative; path will then be relative to that + directory. + + effective_ids: bool = False + If True, access will use the effective uid/gid instead of + the real uid/gid. + + follow_symlinks: bool = True + If False, and the last element of the path is a symbolic link, + access will examine the symbolic link itself instead of the file + the link points to. + +Use the real uid/gid to test for access to a path. + +{parameters} +dir_fd, effective_ids, and follow_symlinks may not be implemented + on your platform. If they are unavailable, using them will raise a + NotImplementedError. + +Note that most operations will use the effective uid/gid, therefore this + routine can be used in a suid/sgid environment to test if the invoking user + has the specified access to the path. + +[clinic start generated code]*/ + +static int +os_access_impl(PyObject *module, path_t *path, int mode, int dir_fd, + int effective_ids, int follow_symlinks) +/*[clinic end generated code: output=cf84158bc90b1a77 input=3ffe4e650ee3bf20]*/ +{ + int return_value; + +#ifdef MS_WINDOWS + DWORD attr; +#else + int result; +#endif + +#ifdef HAVE_FACCESSAT + int faccessat_unavailable = 0; +#endif + +#ifndef HAVE_FACCESSAT + if (follow_symlinks_specified("access", follow_symlinks)) + return -1; + + if (effective_ids) { + argument_unavailable_error("access", "effective_ids"); + return -1; + } +#endif + +#ifdef MS_WINDOWS + Py_BEGIN_ALLOW_THREADS + attr = GetFileAttributesW(path->wide); + Py_END_ALLOW_THREADS + + /* + * Access is possible if + * * we didn't get a -1, and + * * write access wasn't requested, + * * or the file isn't read-only, + * * or it's a directory. + * (Directories cannot be read-only on Windows.) + */ + return_value = (attr != INVALID_FILE_ATTRIBUTES) && + (!(mode & 2) || + !(attr & FILE_ATTRIBUTE_READONLY) || + (attr & FILE_ATTRIBUTE_DIRECTORY)); +#else + + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_FACCESSAT + if ((dir_fd != DEFAULT_DIR_FD) || + effective_ids || + !follow_symlinks) { + + if (HAVE_FACCESSAT_RUNTIME) { + int flags = 0; + if (!follow_symlinks) + flags |= AT_SYMLINK_NOFOLLOW; + if (effective_ids) + flags |= AT_EACCESS; + result = faccessat(dir_fd, path->narrow, mode, flags); + } else { + faccessat_unavailable = 1; + } + } + else +#endif + result = access(path->narrow, mode); + Py_END_ALLOW_THREADS + +#ifdef HAVE_FACCESSAT + if (faccessat_unavailable) { + if (dir_fd != DEFAULT_DIR_FD) { + argument_unavailable_error("access", "dir_fd"); + return -1; + } + if (follow_symlinks_specified("access", follow_symlinks)) + return -1; + + if (effective_ids) { + argument_unavailable_error("access", "effective_ids"); + return -1; + } + /* should be unreachable */ + return -1; + } +#endif + return_value = !result; +#endif + + return return_value; +} + +#ifndef F_OK +#define F_OK 0 +#endif +#ifndef R_OK +#define R_OK 4 +#endif +#ifndef W_OK +#define W_OK 2 +#endif +#ifndef X_OK +#define X_OK 1 +#endif + + +#ifdef HAVE_TTYNAME +/*[clinic input] +os.ttyname + + fd: int + Integer file descriptor handle. + + / + +Return the name of the terminal device connected to 'fd'. +[clinic start generated code]*/ + +static PyObject * +os_ttyname_impl(PyObject *module, int fd) +/*[clinic end generated code: output=c424d2e9d1cd636a input=9ff5a58b08115c55]*/ +{ + + long size = sysconf(_SC_TTY_NAME_MAX); + if (size == -1) { + return posix_error(); + } + char *buffer = (char *)PyMem_RawMalloc(size); + if (buffer == NULL) { + return PyErr_NoMemory(); + } + int ret = ttyname_r(fd, buffer, size); + if (ret != 0) { + PyMem_RawFree(buffer); + errno = ret; + return posix_error(); + } + PyObject *res = PyUnicode_DecodeFSDefault(buffer); + PyMem_RawFree(buffer); + return res; +} +#endif + +#ifdef HAVE_CTERMID +/*[clinic input] +os.ctermid + +Return the name of the controlling terminal for this process. +[clinic start generated code]*/ + +static PyObject * +os_ctermid_impl(PyObject *module) +/*[clinic end generated code: output=02f017e6c9e620db input=3b87fdd52556382d]*/ +{ + char *ret; + char buffer[L_ctermid]; + +#ifdef USE_CTERMID_R + ret = ctermid_r(buffer); +#else + ret = ctermid(buffer); +#endif + if (ret == NULL) + return posix_error(); + return PyUnicode_DecodeFSDefault(buffer); +} +#endif /* HAVE_CTERMID */ + + +/*[clinic input] +os.chdir + + path: path_t(allow_fd='PATH_HAVE_FCHDIR') + +Change the current working directory to the specified path. + +path may always be specified as a string. +On some platforms, path may also be specified as an open file descriptor. + If this functionality is unavailable, using it raises an exception. +[clinic start generated code]*/ + +static PyObject * +os_chdir_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=3be6400eee26eaae input=1a4a15b4d12cb15d]*/ +{ + int result; + + if (PySys_Audit("os.chdir", "(O)", path->object) < 0) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS +#ifdef MS_WINDOWS + /* on unix, success = 0, on windows, success = !0 */ + result = !win32_wchdir(path->wide); +#else +#ifdef HAVE_FCHDIR + if (path->fd != -1) + result = fchdir(path->fd); + else +#endif + result = chdir(path->narrow); +#endif + Py_END_ALLOW_THREADS + + if (result) { + return path_error(path); + } + + Py_RETURN_NONE; +} + + +#ifdef HAVE_FCHDIR +/*[clinic input] +os.fchdir + + fd: fildes + +Change to the directory of the given file descriptor. + +fd must be opened on a directory, not a file. +Equivalent to os.chdir(fd). + +[clinic start generated code]*/ + +static PyObject * +os_fchdir_impl(PyObject *module, int fd) +/*[clinic end generated code: output=42e064ec4dc00ab0 input=18e816479a2fa985]*/ +{ + if (PySys_Audit("os.chdir", "(i)", fd) < 0) { + return NULL; + } + return posix_fildes_fd(fd, fchdir); +} +#endif /* HAVE_FCHDIR */ + +#ifdef MS_WINDOWS +# define CHMOD_DEFAULT_FOLLOW_SYMLINKS 0 +#else +# define CHMOD_DEFAULT_FOLLOW_SYMLINKS 1 +#endif + +#ifdef MS_WINDOWS +static int +win32_lchmod(LPCWSTR path, int mode) +{ + DWORD attr = GetFileAttributesW(path); + if (attr == INVALID_FILE_ATTRIBUTES) { + return 0; + } + if (mode & _S_IWRITE) { + attr &= ~FILE_ATTRIBUTE_READONLY; + } + else { + attr |= FILE_ATTRIBUTE_READONLY; + } + return SetFileAttributesW(path, attr); +} + +static int +win32_hchmod(HANDLE hfile, int mode) +{ + FILE_BASIC_INFO info; + if (!GetFileInformationByHandleEx(hfile, FileBasicInfo, + &info, sizeof(info))) + { + return 0; + } + if (mode & _S_IWRITE) { + info.FileAttributes &= ~FILE_ATTRIBUTE_READONLY; + } + else { + info.FileAttributes |= FILE_ATTRIBUTE_READONLY; + } + return SetFileInformationByHandle(hfile, FileBasicInfo, + &info, sizeof(info)); +} + +static int +win32_fchmod(int fd, int mode) +{ + HANDLE hfile = _Py_get_osfhandle_noraise(fd); + if (hfile == INVALID_HANDLE_VALUE) { + SetLastError(ERROR_INVALID_HANDLE); + return 0; + } + return win32_hchmod(hfile, mode); +} + +#endif /* MS_WINDOWS */ + +/*[clinic input] +os.chmod + + path: path_t(allow_fd='PATH_HAVE_FCHMOD') + Path to be modified. May always be specified as a str, bytes, or a path-like object. + On some platforms, path may also be specified as an open file descriptor. + If this functionality is unavailable, using it raises an exception. + + mode: int + Operating-system mode bitfield. + Be careful when using number literals for *mode*. The conventional UNIX notation for + numeric modes uses an octal base, which needs to be indicated with a ``0o`` prefix in + Python. + + * + + dir_fd : dir_fd(requires='fchmodat') = None + If not None, it should be a file descriptor open to a directory, + and path should be relative; path will then be relative to that + directory. + + follow_symlinks: bool(c_default="CHMOD_DEFAULT_FOLLOW_SYMLINKS", \ + py_default="(os.name != 'nt')") = CHMOD_DEFAULT_FOLLOW_SYMLINKS + If False, and the last element of the path is a symbolic link, + chmod will modify the symbolic link itself instead of the file + the link points to. + +Change the access permissions of a file. + +It is an error to use dir_fd or follow_symlinks when specifying path as + an open file descriptor. +dir_fd and follow_symlinks may not be implemented on your platform. + If they are unavailable, using them will raise a NotImplementedError. + +[clinic start generated code]*/ + +static PyObject * +os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd, + int follow_symlinks) +/*[clinic end generated code: output=5cf6a94915cc7bff input=fcf115d174b9f3d8]*/ +{ + int result; + +#ifdef HAVE_FCHMODAT + int fchmodat_nofollow_unsupported = 0; + int fchmodat_unsupported = 0; +#endif + +#if !(defined(HAVE_FCHMODAT) || defined(HAVE_LCHMOD) || defined(MS_WINDOWS)) + if (follow_symlinks_specified("chmod", follow_symlinks)) + return NULL; +#endif + + if (PySys_Audit("os.chmod", "Oii", path->object, mode, + dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) { + return NULL; + } + +#ifdef MS_WINDOWS + result = 0; + Py_BEGIN_ALLOW_THREADS + if (path->fd != -1) { + result = win32_fchmod(path->fd, mode); + } + else if (follow_symlinks) { + HANDLE hfile = CreateFileW(path->wide, + FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES, + 0, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (hfile != INVALID_HANDLE_VALUE) { + result = win32_hchmod(hfile, mode); + (void)CloseHandle(hfile); + } + } + else { + result = win32_lchmod(path->wide, mode); + } + Py_END_ALLOW_THREADS + if (!result) { + return path_error(path); + } +#else /* MS_WINDOWS */ + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_FCHMOD + if (path->fd != -1) + result = fchmod(path->fd, mode); + else +#endif /* HAVE_CHMOD */ +#ifdef HAVE_LCHMOD + if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD)) + result = lchmod(path->narrow, mode); + else +#endif /* HAVE_LCHMOD */ +#ifdef HAVE_FCHMODAT + if ((dir_fd != DEFAULT_DIR_FD) || !follow_symlinks) { + if (HAVE_FCHMODAT_RUNTIME) { + /* + * fchmodat() doesn't currently support AT_SYMLINK_NOFOLLOW! + * The documentation specifically shows how to use it, + * and then says it isn't implemented yet. + * (true on linux with glibc 2.15, and openindiana 3.x) + * + * Once it is supported, os.chmod will automatically + * support dir_fd and follow_symlinks=False. (Hopefully.) + * Until then, we need to be careful what exception we raise. + */ + result = fchmodat(dir_fd, path->narrow, mode, + follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW); + /* + * But wait! We can't throw the exception without allowing threads, + * and we can't do that in this nested scope. (Macro trickery, sigh.) + */ + fchmodat_nofollow_unsupported = + result && + ((errno == ENOTSUP) || (errno == EOPNOTSUPP)) && + !follow_symlinks; + } else { + fchmodat_unsupported = 1; + fchmodat_nofollow_unsupported = 1; + + result = -1; + } + } + else +#endif /* HAVE_FHCMODAT */ + { +#ifdef HAVE_CHMOD + result = chmod(path->narrow, mode); +#elif defined(__wasi__) + // WASI SDK 15.0 does not support chmod. + // Ignore missing syscall for now. + result = 0; +#else + result = -1; + errno = ENOSYS; +#endif + } + Py_END_ALLOW_THREADS + + if (result) { +#ifdef HAVE_FCHMODAT + if (fchmodat_unsupported) { + if (dir_fd != DEFAULT_DIR_FD) { + argument_unavailable_error("chmod", "dir_fd"); + return NULL; + } + } + + if (fchmodat_nofollow_unsupported) { + if (dir_fd != DEFAULT_DIR_FD) + dir_fd_and_follow_symlinks_invalid("chmod", + dir_fd, follow_symlinks); + else + follow_symlinks_specified("chmod", follow_symlinks); + return NULL; + } + else +#endif /* HAVE_FCHMODAT */ + return path_error(path); + } +#endif /* MS_WINDOWS */ + + Py_RETURN_NONE; +} + + +#if defined(HAVE_FCHMOD) || defined(MS_WINDOWS) +/*[clinic input] +os.fchmod + + fd: int + The file descriptor of the file to be modified. + mode: int + Operating-system mode bitfield. + Be careful when using number literals for *mode*. The conventional UNIX notation for + numeric modes uses an octal base, which needs to be indicated with a ``0o`` prefix in + Python. + +Change the access permissions of the file given by file descriptor fd. + +Equivalent to os.chmod(fd, mode). +[clinic start generated code]*/ + +static PyObject * +os_fchmod_impl(PyObject *module, int fd, int mode) +/*[clinic end generated code: output=afd9bc05b4e426b3 input=b5594618bbbc22df]*/ +{ + int res; + + if (PySys_Audit("os.chmod", "iii", fd, mode, -1) < 0) { + return NULL; + } + +#ifdef MS_WINDOWS + res = 0; + Py_BEGIN_ALLOW_THREADS + res = win32_fchmod(fd, mode); + Py_END_ALLOW_THREADS + if (!res) { + return PyErr_SetFromWindowsErr(0); + } +#else /* MS_WINDOWS */ + int async_err = 0; + do { + Py_BEGIN_ALLOW_THREADS + res = fchmod(fd, mode); + Py_END_ALLOW_THREADS + } while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + if (res != 0) + return (!async_err) ? posix_error() : NULL; +#endif /* MS_WINDOWS */ + + Py_RETURN_NONE; +} +#endif /* HAVE_FCHMOD || MS_WINDOWS */ + + +#if defined(HAVE_LCHMOD) || defined(MS_WINDOWS) +/*[clinic input] +os.lchmod + + path: path_t + mode: int + +Change the access permissions of a file, without following symbolic links. + +If path is a symlink, this affects the link itself rather than the target. +Equivalent to chmod(path, mode, follow_symlinks=False)." +[clinic start generated code]*/ + +static PyObject * +os_lchmod_impl(PyObject *module, path_t *path, int mode) +/*[clinic end generated code: output=082344022b51a1d5 input=90c5663c7465d24f]*/ +{ + int res; + if (PySys_Audit("os.chmod", "Oii", path->object, mode, -1) < 0) { + return NULL; + } +#ifdef MS_WINDOWS + Py_BEGIN_ALLOW_THREADS + res = win32_lchmod(path->wide, mode); + Py_END_ALLOW_THREADS + if (!res) { + path_error(path); + return NULL; + } +#else /* MS_WINDOWS */ + Py_BEGIN_ALLOW_THREADS + res = lchmod(path->narrow, mode); + Py_END_ALLOW_THREADS + if (res < 0) { + path_error(path); + return NULL; + } +#endif /* MS_WINDOWS */ + Py_RETURN_NONE; +} +#endif /* HAVE_LCHMOD || MS_WINDOWS */ + + +#ifdef HAVE_CHFLAGS +/*[clinic input] +os.chflags + + path: path_t + flags: unsigned_long(bitwise=True) + follow_symlinks: bool=True + +Set file flags. + +If follow_symlinks is False, and the last element of the path is a symbolic + link, chflags will change flags on the symbolic link itself instead of the + file the link points to. +follow_symlinks may not be implemented on your platform. If it is +unavailable, using it will raise a NotImplementedError. + +[clinic start generated code]*/ + +static PyObject * +os_chflags_impl(PyObject *module, path_t *path, unsigned long flags, + int follow_symlinks) +/*[clinic end generated code: output=85571c6737661ce9 input=0327e29feb876236]*/ +{ + int result; + +#ifndef HAVE_LCHFLAGS + if (follow_symlinks_specified("chflags", follow_symlinks)) + return NULL; +#endif + + if (PySys_Audit("os.chflags", "Ok", path->object, flags) < 0) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_LCHFLAGS + if (!follow_symlinks) + result = lchflags(path->narrow, flags); + else +#endif + result = chflags(path->narrow, flags); + Py_END_ALLOW_THREADS + + if (result) + return path_error(path); + + Py_RETURN_NONE; +} +#endif /* HAVE_CHFLAGS */ + + +#ifdef HAVE_LCHFLAGS +/*[clinic input] +os.lchflags + + path: path_t + flags: unsigned_long(bitwise=True) + +Set file flags. + +This function will not follow symbolic links. +Equivalent to chflags(path, flags, follow_symlinks=False). +[clinic start generated code]*/ + +static PyObject * +os_lchflags_impl(PyObject *module, path_t *path, unsigned long flags) +/*[clinic end generated code: output=30ae958695c07316 input=f9f82ea8b585ca9d]*/ +{ + int res; + if (PySys_Audit("os.chflags", "Ok", path->object, flags) < 0) { + return NULL; + } + Py_BEGIN_ALLOW_THREADS + res = lchflags(path->narrow, flags); + Py_END_ALLOW_THREADS + if (res < 0) { + return path_error(path); + } + Py_RETURN_NONE; +} +#endif /* HAVE_LCHFLAGS */ + + +#ifdef HAVE_CHROOT +/*[clinic input] +os.chroot + path: path_t + +Change root directory to path. + +[clinic start generated code]*/ + +static PyObject * +os_chroot_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=de80befc763a4475 input=14822965652c3dc3]*/ +{ + int res; + Py_BEGIN_ALLOW_THREADS + res = chroot(path->narrow); + Py_END_ALLOW_THREADS + if (res < 0) + return path_error(path); + Py_RETURN_NONE; +} +#endif /* HAVE_CHROOT */ + + +#ifdef HAVE_FSYNC +/*[clinic input] +os.fsync + + fd: fildes + +Force write of fd to disk. +[clinic start generated code]*/ + +static PyObject * +os_fsync_impl(PyObject *module, int fd) +/*[clinic end generated code: output=4a10d773f52b3584 input=21c3645c056967f2]*/ +{ + return posix_fildes_fd(fd, fsync); +} +#endif /* HAVE_FSYNC */ + + +#ifdef HAVE_SYNC +/*[clinic input] +os.sync + +Force write of everything to disk. +[clinic start generated code]*/ + +static PyObject * +os_sync_impl(PyObject *module) +/*[clinic end generated code: output=2796b1f0818cd71c input=84749fe5e9b404ff]*/ +{ + Py_BEGIN_ALLOW_THREADS + sync(); + Py_END_ALLOW_THREADS + Py_RETURN_NONE; +} +#endif /* HAVE_SYNC */ + + +#ifdef HAVE_FDATASYNC +#ifdef __hpux +extern int fdatasync(int); /* On HP-UX, in libc but not in unistd.h */ +#endif + +/*[clinic input] +os.fdatasync + + fd: fildes + +Force write of fd to disk without forcing update of metadata. +[clinic start generated code]*/ + +static PyObject * +os_fdatasync_impl(PyObject *module, int fd) +/*[clinic end generated code: output=b4b9698b5d7e26dd input=bc74791ee54dd291]*/ +{ + return posix_fildes_fd(fd, fdatasync); +} +#endif /* HAVE_FDATASYNC */ + + +#ifdef HAVE_CHOWN +/*[clinic input] +os.chown + + path : path_t(allow_fd='PATH_HAVE_FCHOWN') + Path to be examined; can be string, bytes, a path-like object, or open-file-descriptor int. + + uid: uid_t + + gid: gid_t + + * + + dir_fd : dir_fd(requires='fchownat') = None + If not None, it should be a file descriptor open to a directory, + and path should be relative; path will then be relative to that + directory. + + follow_symlinks: bool = True + If False, and the last element of the path is a symbolic link, + stat will examine the symbolic link itself instead of the file + the link points to. + +Change the owner and group id of path to the numeric uid and gid.\ + +path may always be specified as a string. +On some platforms, path may also be specified as an open file descriptor. + If this functionality is unavailable, using it raises an exception. +If dir_fd is not None, it should be a file descriptor open to a directory, + and path should be relative; path will then be relative to that directory. +If follow_symlinks is False, and the last element of the path is a symbolic + link, chown will modify the symbolic link itself instead of the file the + link points to. +It is an error to use dir_fd or follow_symlinks when specifying path as + an open file descriptor. +dir_fd and follow_symlinks may not be implemented on your platform. + If they are unavailable, using them will raise a NotImplementedError. + +[clinic start generated code]*/ + +static PyObject * +os_chown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid, + int dir_fd, int follow_symlinks) +/*[clinic end generated code: output=4beadab0db5f70cd input=b08c5ec67996a97d]*/ +{ + int result; + +#if defined(HAVE_FCHOWNAT) + int fchownat_unsupported = 0; +#endif + +#if !(defined(HAVE_LCHOWN) || defined(HAVE_FCHOWNAT)) + if (follow_symlinks_specified("chown", follow_symlinks)) + return NULL; +#endif + if (dir_fd_and_fd_invalid("chown", dir_fd, path->fd) || + fd_and_follow_symlinks_invalid("chown", path->fd, follow_symlinks)) + return NULL; + + if (PySys_Audit("os.chown", "OIIi", path->object, uid, gid, + dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_FCHOWN + if (path->fd != -1) + result = fchown(path->fd, uid, gid); + else +#endif +#ifdef HAVE_LCHOWN + if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD)) + result = lchown(path->narrow, uid, gid); + else +#endif +#ifdef HAVE_FCHOWNAT + if ((dir_fd != DEFAULT_DIR_FD) || (!follow_symlinks)) { + if (HAVE_FCHOWNAT_RUNTIME) { + result = fchownat(dir_fd, path->narrow, uid, gid, + follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW); + } else { + fchownat_unsupported = 1; + } + } else +#endif + result = chown(path->narrow, uid, gid); + Py_END_ALLOW_THREADS + +#ifdef HAVE_FCHOWNAT + if (fchownat_unsupported) { + /* This would be incorrect if the current platform + * doesn't support lchown. + */ + argument_unavailable_error(NULL, "dir_fd"); + return NULL; + } +#endif + + if (result) + return path_error(path); + + Py_RETURN_NONE; +} +#endif /* HAVE_CHOWN */ + + +#ifdef HAVE_FCHOWN +/*[clinic input] +os.fchown + + fd: int + uid: uid_t + gid: gid_t + +Change the owner and group id of the file specified by file descriptor. + +Equivalent to os.chown(fd, uid, gid). + +[clinic start generated code]*/ + +static PyObject * +os_fchown_impl(PyObject *module, int fd, uid_t uid, gid_t gid) +/*[clinic end generated code: output=97d21cbd5a4350a6 input=3af544ba1b13a0d7]*/ +{ + int res; + int async_err = 0; + + if (PySys_Audit("os.chown", "iIIi", fd, uid, gid, -1) < 0) { + return NULL; + } + + do { + Py_BEGIN_ALLOW_THREADS + res = fchown(fd, uid, gid); + Py_END_ALLOW_THREADS + } while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + if (res != 0) + return (!async_err) ? posix_error() : NULL; + + Py_RETURN_NONE; +} +#endif /* HAVE_FCHOWN */ + + +#ifdef HAVE_LCHOWN +/*[clinic input] +os.lchown + + path : path_t + uid: uid_t + gid: gid_t + +Change the owner and group id of path to the numeric uid and gid. + +This function will not follow symbolic links. +Equivalent to os.chown(path, uid, gid, follow_symlinks=False). +[clinic start generated code]*/ + +static PyObject * +os_lchown_impl(PyObject *module, path_t *path, uid_t uid, gid_t gid) +/*[clinic end generated code: output=25eaf6af412fdf2f input=b1c6014d563a7161]*/ +{ + int res; + if (PySys_Audit("os.chown", "OIIi", path->object, uid, gid, -1) < 0) { + return NULL; + } + Py_BEGIN_ALLOW_THREADS + res = lchown(path->narrow, uid, gid); + Py_END_ALLOW_THREADS + if (res < 0) { + return path_error(path); + } + Py_RETURN_NONE; +} +#endif /* HAVE_LCHOWN */ + + +static PyObject * +posix_getcwd(int use_bytes) +{ +#ifdef MS_WINDOWS + wchar_t wbuf[MAXPATHLEN]; + wchar_t *wbuf2 = wbuf; + DWORD len; + + Py_BEGIN_ALLOW_THREADS + len = GetCurrentDirectoryW(Py_ARRAY_LENGTH(wbuf), wbuf); + /* If the buffer is large enough, len does not include the + terminating \0. If the buffer is too small, len includes + the space needed for the terminator. */ + if (len >= Py_ARRAY_LENGTH(wbuf)) { + if (len <= PY_SSIZE_T_MAX / sizeof(wchar_t)) { + wbuf2 = PyMem_RawMalloc(len * sizeof(wchar_t)); + } + else { + wbuf2 = NULL; + } + if (wbuf2) { + len = GetCurrentDirectoryW(len, wbuf2); + } + } + Py_END_ALLOW_THREADS + + if (!wbuf2) { + PyErr_NoMemory(); + return NULL; + } + if (!len) { + PyErr_SetFromWindowsErr(0); + if (wbuf2 != wbuf) + PyMem_RawFree(wbuf2); + return NULL; + } + + Py_NormalizeSepsW(wbuf2); + PyObject *resobj = PyUnicode_FromWideChar(wbuf2, len); + if (wbuf2 != wbuf) { + PyMem_RawFree(wbuf2); + } + + if (use_bytes) { + if (resobj == NULL) { + return NULL; + } + Py_SETREF(resobj, PyUnicode_EncodeFSDefault(resobj)); + } + + return resobj; +#else + const size_t chunk = 1024; + + char *buf = NULL; + char *cwd = NULL; + size_t buflen = 0; + + Py_BEGIN_ALLOW_THREADS + do { + char *newbuf; + if (buflen <= PY_SSIZE_T_MAX - chunk) { + buflen += chunk; + newbuf = PyMem_RawRealloc(buf, buflen); + } + else { + newbuf = NULL; + } + if (newbuf == NULL) { + PyMem_RawFree(buf); + buf = NULL; + break; + } + buf = newbuf; + + cwd = getcwd(buf, buflen); + } while (cwd == NULL && errno == ERANGE); + Py_END_ALLOW_THREADS + + if (buf == NULL) { + return PyErr_NoMemory(); + } + if (cwd == NULL) { + posix_error(); + PyMem_RawFree(buf); + return NULL; + } + + PyObject *obj; + if (use_bytes) { + obj = PyBytes_FromStringAndSize(buf, strlen(buf)); + } + else { + obj = PyUnicode_DecodeFSDefault(buf); + } +#ifdef __linux__ + if (buf[0] != '/') { + /* + * On Linux >= 2.6.36 with glibc < 2.27, getcwd() can return a + * relative pathname starting with '(unreachable)'. We detect this + * and fail with ENOENT, matching newer glibc behaviour. + */ + errno = ENOENT; + path_object_error(obj); + PyMem_RawFree(buf); + return NULL; + } +#endif + assert(buf[0] == '/'); + PyMem_RawFree(buf); + + return obj; +#endif /* !MS_WINDOWS */ +} + + +/*[clinic input] +os.getcwd + +Return a unicode string representing the current working directory. +[clinic start generated code]*/ + +static PyObject * +os_getcwd_impl(PyObject *module) +/*[clinic end generated code: output=21badfae2ea99ddc input=f069211bb70e3d39]*/ +{ + return posix_getcwd(0); +} + + +/*[clinic input] +os.getcwdb + +Return a bytes string representing the current working directory. +[clinic start generated code]*/ + +static PyObject * +os_getcwdb_impl(PyObject *module) +/*[clinic end generated code: output=3dd47909480e4824 input=f6f6a378dad3d9cb]*/ +{ + return posix_getcwd(1); +} + + +#if ((!defined(HAVE_LINK)) && defined(MS_WINDOWS)) +#define HAVE_LINK 1 +#endif + +#ifdef HAVE_LINK +/*[clinic input] + +os.link + + src : path_t + dst : path_t + * + src_dir_fd : dir_fd = None + dst_dir_fd : dir_fd = None + follow_symlinks: bool = True + +Create a hard link to a file. + +If either src_dir_fd or dst_dir_fd is not None, it should be a file + descriptor open to a directory, and the respective path string (src or dst) + should be relative; the path will then be relative to that directory. +If follow_symlinks is False, and the last element of src is a symbolic + link, link will create a link to the symbolic link itself instead of the + file the link points to. +src_dir_fd, dst_dir_fd, and follow_symlinks may not be implemented on your + platform. If they are unavailable, using them will raise a + NotImplementedError. +[clinic start generated code]*/ + +static PyObject * +os_link_impl(PyObject *module, path_t *src, path_t *dst, int src_dir_fd, + int dst_dir_fd, int follow_symlinks) +/*[clinic end generated code: output=7f00f6007fd5269a input=b0095ebbcbaa7e04]*/ +{ +#ifdef MS_WINDOWS + BOOL result = FALSE; +#else + int result; +#endif +#if defined(HAVE_LINKAT) + int linkat_unavailable = 0; +#endif + +#ifndef HAVE_LINKAT + if ((src_dir_fd != DEFAULT_DIR_FD) || (dst_dir_fd != DEFAULT_DIR_FD)) { + argument_unavailable_error("link", "src_dir_fd and dst_dir_fd"); + return NULL; + } +#endif + +#ifndef MS_WINDOWS + if ((src->narrow && dst->wide) || (src->wide && dst->narrow)) { + PyErr_SetString(PyExc_NotImplementedError, + "link: src and dst must be the same type"); + return NULL; + } +#endif + + if (PySys_Audit("os.link", "OOii", src->object, dst->object, + src_dir_fd == DEFAULT_DIR_FD ? -1 : src_dir_fd, + dst_dir_fd == DEFAULT_DIR_FD ? -1 : dst_dir_fd) < 0) { + return NULL; + } + +#ifdef MS_WINDOWS + Py_BEGIN_ALLOW_THREADS + result = CreateHardLinkW(dst->wide, src->wide, NULL); + Py_END_ALLOW_THREADS + + if (!result) + return path_error2(src, dst); +#else + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_LINKAT + if ((src_dir_fd != DEFAULT_DIR_FD) || + (dst_dir_fd != DEFAULT_DIR_FD) || + (!follow_symlinks)) { + + if (HAVE_LINKAT_RUNTIME) { + + result = linkat(src_dir_fd, src->narrow, + dst_dir_fd, dst->narrow, + follow_symlinks ? AT_SYMLINK_FOLLOW : 0); + + } +#ifdef __APPLE__ + else { + if (src_dir_fd == DEFAULT_DIR_FD && dst_dir_fd == DEFAULT_DIR_FD) { + /* See issue 41355: This matches the behaviour of !HAVE_LINKAT */ + result = link(src->narrow, dst->narrow); + } else { + linkat_unavailable = 1; + } + } +#endif + } + else +#endif /* HAVE_LINKAT */ + result = link(src->narrow, dst->narrow); + Py_END_ALLOW_THREADS + +#ifdef HAVE_LINKAT + if (linkat_unavailable) { + /* Either or both dir_fd arguments were specified */ + if (src_dir_fd != DEFAULT_DIR_FD) { + argument_unavailable_error("link", "src_dir_fd"); + } else { + argument_unavailable_error("link", "dst_dir_fd"); + } + return NULL; + } +#endif + + if (result) + return path_error2(src, dst); +#endif /* MS_WINDOWS */ + + Py_RETURN_NONE; +} +#endif + + +#if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR) +static PyObject * +_listdir_windows_no_opendir(path_t *path, PyObject *list) +{ + PyObject *v; + HANDLE hFindFile = INVALID_HANDLE_VALUE; + BOOL result, return_bytes; + wchar_t namebuf[MAX_PATH+4]; /* Overallocate for "\*.*" */ + /* only claim to have space for MAX_PATH */ + Py_ssize_t len = Py_ARRAY_LENGTH(namebuf)-4; + wchar_t *wnamebuf = NULL; + + WIN32_FIND_DATAW wFileData; + const wchar_t *po_wchars; + + if (!path->wide) { /* Default arg: "." */ + po_wchars = L"."; + len = 1; + return_bytes = 0; + } else { + po_wchars = path->wide; + len = wcslen(path->wide); + return_bytes = PyBytes_Check(path->object); + } + /* The +5 is so we can append "\\*.*\0" */ + wnamebuf = PyMem_New(wchar_t, len + 5); + if (!wnamebuf) { + PyErr_NoMemory(); + goto exit; + } + wcscpy(wnamebuf, po_wchars); + if (len > 0) { + wchar_t wch = wnamebuf[len-1]; + if (wch != SEP && wch != ALTSEP && wch != L':') + wnamebuf[len++] = SEP; + wcscpy(wnamebuf + len, L"*.*"); + } + if ((list = PyList_New(0)) == NULL) { + goto exit; + } + Py_BEGIN_ALLOW_THREADS + hFindFile = FindFirstFileW(wnamebuf, &wFileData); + Py_END_ALLOW_THREADS + if (hFindFile == INVALID_HANDLE_VALUE) { + int error = GetLastError(); + if (error == ERROR_FILE_NOT_FOUND) + goto exit; + path_error(path); + Py_CLEAR(list); + goto exit; + } + do { + /* Skip over . and .. */ + if (wcscmp(wFileData.cFileName, L".") != 0 && + wcscmp(wFileData.cFileName, L"..") != 0) { + v = PyUnicode_FromWideChar(wFileData.cFileName, + wcslen(wFileData.cFileName)); + if (return_bytes && v) { + Py_SETREF(v, PyUnicode_EncodeFSDefault(v)); + } + if (v == NULL) { + Py_CLEAR(list); + break; + } + if (PyList_Append(list, v) != 0) { + Py_DECREF(v); + Py_CLEAR(list); + break; + } + Py_DECREF(v); + } + Py_BEGIN_ALLOW_THREADS + result = FindNextFileW(hFindFile, &wFileData); + Py_END_ALLOW_THREADS + /* FindNextFile sets error to ERROR_NO_MORE_FILES if + it got to the end of the directory. */ + if (!result && GetLastError() != ERROR_NO_MORE_FILES) { + path_error(path); + Py_CLEAR(list); + goto exit; + } + } while (result == TRUE); + +exit: + if (hFindFile != INVALID_HANDLE_VALUE) { + if (FindClose(hFindFile) == FALSE) { + if (list != NULL) { + path_error(path); + Py_CLEAR(list); + } + } + } + PyMem_Free(wnamebuf); + + return list; +} /* end of _listdir_windows_no_opendir */ + +#else /* thus POSIX, ie: not (MS_WINDOWS and not HAVE_OPENDIR) */ + +static PyObject * +_posix_listdir(path_t *path, PyObject *list) +{ + PyObject *v; + DIR *dirp = NULL; + struct dirent *ep; + int return_str; /* if false, return bytes */ +#ifdef HAVE_FDOPENDIR + int fd = -1; +#endif + + errno = 0; +#ifdef HAVE_FDOPENDIR + if (path->fd != -1) { + if (HAVE_FDOPENDIR_RUNTIME) { + /* closedir() closes the FD, so we duplicate it */ + fd = _Py_dup(path->fd); + if (fd == -1) + return NULL; + + return_str = 1; + + Py_BEGIN_ALLOW_THREADS + dirp = fdopendir(fd); + Py_END_ALLOW_THREADS + } else { + PyErr_SetString(PyExc_TypeError, + "listdir: path should be string, bytes, os.PathLike or None, not int"); + return NULL; + } + } + else +#endif + { + const char *name; + if (path->narrow) { + name = path->narrow; + /* only return bytes if they specified a bytes object */ + return_str = !PyBytes_Check(path->object); + } + else { + name = "."; + return_str = 1; + } + + Py_BEGIN_ALLOW_THREADS + dirp = opendir(name); + Py_END_ALLOW_THREADS + } + + if (dirp == NULL) { + path_error(path); + list = NULL; +#ifdef HAVE_FDOPENDIR + if (fd != -1) { + Py_BEGIN_ALLOW_THREADS + close(fd); + Py_END_ALLOW_THREADS + } +#endif + goto exit; + } + if ((list = PyList_New(0)) == NULL) { + goto exit; + } + for (;;) { + errno = 0; + Py_BEGIN_ALLOW_THREADS + ep = readdir(dirp); + Py_END_ALLOW_THREADS + if (ep == NULL) { + if (errno == 0) { + break; + } else { + path_error(path); + Py_CLEAR(list); + goto exit; + } + } + if (ep->d_name[0] == '.' && + (NAMLEN(ep) == 1 || + (ep->d_name[1] == '.' && NAMLEN(ep) == 2))) + continue; + if (return_str) + v = PyUnicode_DecodeFSDefaultAndSize(ep->d_name, NAMLEN(ep)); + else + v = PyBytes_FromStringAndSize(ep->d_name, NAMLEN(ep)); + if (v == NULL) { + Py_CLEAR(list); + break; + } + if (PyList_Append(list, v) != 0) { + Py_DECREF(v); + Py_CLEAR(list); + break; + } + Py_DECREF(v); + } + +exit: + if (dirp != NULL) { + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_FDOPENDIR + if (fd > -1) + rewinddir(dirp); +#endif + closedir(dirp); + Py_END_ALLOW_THREADS + } + + return list; +} /* end of _posix_listdir */ +#endif /* which OS */ + + +/*[clinic input] +os.listdir + + path : path_t(nullable=True, allow_fd='PATH_HAVE_FDOPENDIR') = None + +Return a list containing the names of the files in the directory. + +path can be specified as either str, bytes, or a path-like object. If path is bytes, + the filenames returned will also be bytes; in all other circumstances + the filenames returned will be str. +If path is None, uses the path='.'. +On some platforms, path may also be specified as an open file descriptor;\ + the file descriptor must refer to a directory. + If this functionality is unavailable, using it raises NotImplementedError. + +The list is in arbitrary order. It does not include the special +entries '.' and '..' even if they are present in the directory. + + +[clinic start generated code]*/ + +static PyObject * +os_listdir_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=293045673fcd1a75 input=e3f58030f538295d]*/ +{ + if (PySys_Audit("os.listdir", "O", + path->object ? path->object : Py_None) < 0) { + return NULL; + } +#if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR) + return _listdir_windows_no_opendir(path, NULL); +#else + return _posix_listdir(path, NULL); +#endif +} + + +#ifdef MS_WINDOWS + +/*[clinic input] +os.listdrives + +Return a list containing the names of drives in the system. + +A drive name typically looks like 'C:\\'. + +[clinic start generated code]*/ + +static PyObject * +os_listdrives_impl(PyObject *module) +/*[clinic end generated code: output=aaece9dacdf682b5 input=1af9ccc9e583798e]*/ +{ + /* Number of possible drives is limited, so 256 should always be enough. + On the day when it is not, listmounts() will have to be used. */ + wchar_t buffer[256]; + DWORD buflen = Py_ARRAY_LENGTH(buffer); + PyObject *result = NULL; + if (PySys_Audit("os.listdrives", NULL) < 0) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS; + buflen = GetLogicalDriveStringsW(buflen, buffer); + Py_END_ALLOW_THREADS; + + if (!buflen) { + PyErr_SetFromWindowsErr(0); + return NULL; + } else if (buflen >= Py_ARRAY_LENGTH(buffer)) { + PyErr_SetFromWindowsErr(ERROR_MORE_DATA); + return NULL; + } + + /* buflen includes a null terminator, so remove it */ + PyObject *str = PyUnicode_FromWideChar(buffer, buflen - 1); + if (str) { + PyObject *nullchar = PyUnicode_FromStringAndSize("\0", 1); + if (nullchar) { + result = PyUnicode_Split(str, nullchar, -1); + Py_DECREF(nullchar); + } + Py_DECREF(str); + } + return result; +} + +/*[clinic input] +os.listvolumes + +Return a list containing the volumes in the system. + +Volumes are typically represented as a GUID path. + +[clinic start generated code]*/ + +static PyObject * +os_listvolumes_impl(PyObject *module) +/*[clinic end generated code: output=534e10ea2bf9d386 input=f6e4e70371f11e99]*/ +{ + PyObject *result = PyList_New(0); + HANDLE find = INVALID_HANDLE_VALUE; + wchar_t buffer[MAX_PATH + 1]; + if (!result) { + return NULL; + } + if (PySys_Audit("os.listvolumes", NULL) < 0) { + Py_DECREF(result); + return NULL; + } + + int err = 0; + Py_BEGIN_ALLOW_THREADS; + find = FindFirstVolumeW(buffer, Py_ARRAY_LENGTH(buffer)); + if (find == INVALID_HANDLE_VALUE) { + err = GetLastError(); + } + Py_END_ALLOW_THREADS; + + while (!err) { + PyObject *s = PyUnicode_FromWideChar(buffer, -1); + if (!s || PyList_Append(result, s) < 0) { + Py_XDECREF(s); + Py_CLEAR(result); + break; + } + Py_DECREF(s); + + Py_BEGIN_ALLOW_THREADS; + if (!FindNextVolumeW(find, buffer, Py_ARRAY_LENGTH(buffer))) { + err = GetLastError(); + } + Py_END_ALLOW_THREADS; + } + + if (find != INVALID_HANDLE_VALUE) { + Py_BEGIN_ALLOW_THREADS; + FindVolumeClose(find); + Py_END_ALLOW_THREADS; + } + if (err && err != ERROR_NO_MORE_FILES) { + PyErr_SetFromWindowsErr(err); + Py_XDECREF(result); + result = NULL; + } + return result; +} + + +/*[clinic input] +os.listmounts + + volume: path_t + +Return a list containing mount points for a particular volume. + +'volume' should be a GUID path as returned from os.listvolumes. + +[clinic start generated code]*/ + +static PyObject * +os_listmounts_impl(PyObject *module, path_t *volume) +/*[clinic end generated code: output=06da49679de4512e input=a8a27178e3f67845]*/ +{ + wchar_t default_buffer[MAX_PATH + 1]; + DWORD buflen = Py_ARRAY_LENGTH(default_buffer); + LPWSTR buffer = default_buffer; + DWORD attributes; + PyObject *str = NULL; + PyObject *nullchar = NULL; + PyObject *result = NULL; + + /* Ensure we have a valid volume path before continuing */ + Py_BEGIN_ALLOW_THREADS + attributes = GetFileAttributesW(volume->wide); + Py_END_ALLOW_THREADS + if (attributes == INVALID_FILE_ATTRIBUTES && + GetLastError() == ERROR_UNRECOGNIZED_VOLUME) + { + return PyErr_SetFromWindowsErr(ERROR_UNRECOGNIZED_VOLUME); + } + + if (PySys_Audit("os.listmounts", "O", volume->object) < 0) { + return NULL; + } + + while (1) { + BOOL success; + Py_BEGIN_ALLOW_THREADS + success = GetVolumePathNamesForVolumeNameW(volume->wide, buffer, + buflen, &buflen); + Py_END_ALLOW_THREADS + if (success) { + break; + } + if (GetLastError() != ERROR_MORE_DATA) { + PyErr_SetFromWindowsErr(0); + goto exit; + } + if (buffer != default_buffer) { + PyMem_Free((void *)buffer); + } + buffer = (wchar_t*)PyMem_Malloc(sizeof(wchar_t) * buflen); + if (!buffer) { + PyErr_NoMemory(); + goto exit; + } + } + if (buflen < 2) { + result = PyList_New(0); + goto exit; + } + // buflen includes two null terminators, one for the last string + // and one for the array of strings. + str = PyUnicode_FromWideChar(buffer, buflen - 2); + nullchar = PyUnicode_FromStringAndSize("\0", 1); + if (str && nullchar) { + result = PyUnicode_Split(str, nullchar, -1); + } +exit: + if (buffer != default_buffer) { + PyMem_Free(buffer); + } + Py_XDECREF(nullchar); + Py_XDECREF(str); + return result; +} + + +/*[clinic input] +os._path_isdevdrive + + path: path_t + +Determines whether the specified path is on a Windows Dev Drive. + +[clinic start generated code]*/ + +static PyObject * +os__path_isdevdrive_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=1f437ea6677433a2 input=ee83e4996a48e23d]*/ +{ +#ifndef PERSISTENT_VOLUME_STATE_DEV_VOLUME + /* This flag will be documented at + https://learn.microsoft.com/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_fs_persistent_volume_information + after release, and will be available in the latest WinSDK. + We include the flag to avoid a specific version dependency + on the latest WinSDK. */ + const int PERSISTENT_VOLUME_STATE_DEV_VOLUME = 0x00002000; +#endif + int err = 0; + PyObject *r = NULL; + wchar_t volume[MAX_PATH]; + + Py_BEGIN_ALLOW_THREADS + if (!GetVolumePathNameW(path->wide, volume, MAX_PATH)) { + /* invalid path of some kind */ + /* Note that this also includes the case where a volume is mounted + in a path longer than 260 characters. This is likely to be rare + and problematic for other reasons, so a (soft) failure in this + check seems okay. */ + err = GetLastError(); + } else if (GetDriveTypeW(volume) != DRIVE_FIXED) { + /* only care about local dev drives */ + r = Py_False; + } else { + HANDLE hVolume = CreateFileW( + volume, + FILE_READ_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL + ); + if (hVolume == INVALID_HANDLE_VALUE) { + err = GetLastError(); + } else { + FILE_FS_PERSISTENT_VOLUME_INFORMATION volumeState = {0}; + volumeState.Version = 1; + volumeState.FlagMask = PERSISTENT_VOLUME_STATE_DEV_VOLUME; + if (!DeviceIoControl( + hVolume, + FSCTL_QUERY_PERSISTENT_VOLUME_STATE, + &volumeState, + sizeof(volumeState), + &volumeState, + sizeof(volumeState), + NULL, + NULL + )) { + err = GetLastError(); + } + CloseHandle(hVolume); + if (err == ERROR_INVALID_PARAMETER) { + /* not supported on this platform */ + r = Py_False; + } else if (!err) { + r = (volumeState.VolumeFlags & PERSISTENT_VOLUME_STATE_DEV_VOLUME) + ? Py_True : Py_False; + } + } + } + Py_END_ALLOW_THREADS + + if (err) { + PyErr_SetFromWindowsErr(err); + return NULL; + } + + if (r) { + return Py_NewRef(r); + } + + return NULL; +} + + +int +_PyOS_getfullpathname(const wchar_t *path, wchar_t **abspath_p) +{ + wchar_t woutbuf[MAX_PATH], *woutbufp = woutbuf; + DWORD result; + + result = GetFullPathNameW(path, + Py_ARRAY_LENGTH(woutbuf), woutbuf, + NULL); + if (!result) { + return -1; + } + + if (result >= Py_ARRAY_LENGTH(woutbuf)) { + if ((size_t)result <= (size_t)PY_SSIZE_T_MAX / sizeof(wchar_t)) { + woutbufp = PyMem_RawMalloc((size_t)result * sizeof(wchar_t)); + } + else { + woutbufp = NULL; + } + if (!woutbufp) { + *abspath_p = NULL; + return 0; + } + + result = GetFullPathNameW(path, result, woutbufp, NULL); + if (!result) { + PyMem_RawFree(woutbufp); + return -1; + } + } + + if (woutbufp != woutbuf) { + *abspath_p = woutbufp; + return 0; + } + + *abspath_p = _PyMem_RawWcsdup(woutbufp); + return 0; +} + + +/* A helper function for abspath on win32 */ +/*[clinic input] +os._getfullpathname + + path: path_t + / + +[clinic start generated code]*/ + +static PyObject * +os__getfullpathname_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=bb8679d56845bc9b input=332ed537c29d0a3e]*/ +{ + wchar_t *abspath; + + if (_PyOS_getfullpathname(path->wide, &abspath) < 0) { + return win32_error_object("GetFullPathNameW", path->object); + } + if (abspath == NULL) { + return PyErr_NoMemory(); + } + + PyObject *str = PyUnicode_FromWideChar(abspath, wcslen(abspath)); + PyMem_RawFree(abspath); + if (str == NULL) { + return NULL; + } + if (PyBytes_Check(path->object)) { + Py_SETREF(str, PyUnicode_EncodeFSDefault(str)); + } + return str; +} + + +/*[clinic input] +os._getfinalpathname + + path: path_t + / + +A helper function for samepath on windows. +[clinic start generated code]*/ + +static PyObject * +os__getfinalpathname_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=621a3c79bc29ebfa input=2b6b6c7cbad5fb84]*/ +{ + HANDLE hFile; + wchar_t buf[MAXPATHLEN], *target_path = buf; + int buf_size = Py_ARRAY_LENGTH(buf); + int result_length; + PyObject *result; + + Py_BEGIN_ALLOW_THREADS + hFile = CreateFileW( + path->wide, + 0, /* desired access */ + 0, /* share mode */ + NULL, /* security attributes */ + OPEN_EXISTING, + /* FILE_FLAG_BACKUP_SEMANTICS is required to open a directory */ + FILE_FLAG_BACKUP_SEMANTICS, + NULL); + Py_END_ALLOW_THREADS + + if (hFile == INVALID_HANDLE_VALUE) { + return win32_error_object("CreateFileW", path->object); + } + + /* We have a good handle to the target, use it to determine the + target path name. */ + while (1) { + Py_BEGIN_ALLOW_THREADS + result_length = GetFinalPathNameByHandleW(hFile, target_path, + buf_size, VOLUME_NAME_DOS); + Py_END_ALLOW_THREADS + + if (!result_length) { + result = win32_error_object("GetFinalPathNameByHandleW", + path->object); + goto cleanup; + } + + if (result_length < buf_size) { + break; + } + + wchar_t *tmp; + tmp = PyMem_Realloc(target_path != buf ? target_path : NULL, + result_length * sizeof(*tmp)); + if (!tmp) { + result = PyErr_NoMemory(); + goto cleanup; + } + + buf_size = result_length; + target_path = tmp; + } + + Py_NormalizeSepsW(target_path); + result = PyUnicode_FromWideChar(target_path, result_length); + if (result && PyBytes_Check(path->object)) { + Py_SETREF(result, PyUnicode_EncodeFSDefault(result)); + } + +cleanup: + if (target_path != buf) { + PyMem_Free(target_path); + } + CloseHandle(hFile); + return result; +} + +/*[clinic input] +os._findfirstfile + path: path_t + / +A function to get the real file name without accessing the file in Windows. +[clinic start generated code]*/ + +static PyObject * +os__findfirstfile_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=106dd3f0779c83dd input=0734dff70f60e1a8]*/ +{ + PyObject *result; + HANDLE hFindFile; + WIN32_FIND_DATAW wFileData; + WCHAR *wRealFileName; + + Py_BEGIN_ALLOW_THREADS + hFindFile = FindFirstFileW(path->wide, &wFileData); + Py_END_ALLOW_THREADS + + if (hFindFile == INVALID_HANDLE_VALUE) { + path_error(path); + return NULL; + } + + wRealFileName = wFileData.cFileName; + result = PyUnicode_FromWideChar(wRealFileName, wcslen(wRealFileName)); + FindClose(hFindFile); + return result; +} + + +/*[clinic input] +os._getvolumepathname + + path: path_t + +A helper function for ismount on Win32. +[clinic start generated code]*/ + +static PyObject * +os__getvolumepathname_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=804c63fd13a1330b input=722b40565fa21552]*/ +{ + PyObject *result; + wchar_t *mountpath=NULL; + size_t buflen; + BOOL ret; + + /* Volume path should be shorter than entire path */ + buflen = Py_MAX(path->length, MAX_PATH); + + if (buflen > PY_DWORD_MAX) { + PyErr_SetString(PyExc_OverflowError, "path too long"); + return NULL; + } + + mountpath = PyMem_New(wchar_t, buflen); + if (mountpath == NULL) + return PyErr_NoMemory(); + + Py_BEGIN_ALLOW_THREADS + ret = GetVolumePathNameW(path->wide, mountpath, + Py_SAFE_DOWNCAST(buflen, size_t, DWORD)); + Py_END_ALLOW_THREADS + + if (!ret) { + result = win32_error_object("_getvolumepathname", path->object); + goto exit; + } + result = PyUnicode_FromWideChar(mountpath, wcslen(mountpath)); + if (PyBytes_Check(path->object)) + Py_SETREF(result, PyUnicode_EncodeFSDefault(result)); + +exit: + PyMem_Free(mountpath); + return result; +} + + +/*[clinic input] +os._path_splitroot + + path: path_t + +Removes everything after the root on Win32. +[clinic start generated code]*/ + +static PyObject * +os__path_splitroot_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=ab7f1a88b654581c input=dc93b1d3984cffb6]*/ +{ + wchar_t *buffer; + wchar_t *end; + PyObject *result = NULL; + HRESULT ret; + + buffer = (wchar_t*)PyMem_Malloc(sizeof(wchar_t) * (wcslen(path->wide) + 1)); + if (!buffer) { + return NULL; + } + wcscpy(buffer, path->wide); + for (wchar_t *p = wcschr(buffer, L'/'); p; p = wcschr(p, L'/')) { + *p = L'\\'; + } + + Py_BEGIN_ALLOW_THREADS + ret = PathCchSkipRoot(buffer, &end); + Py_END_ALLOW_THREADS + if (FAILED(ret)) { + result = Py_BuildValue("sO", "", path->object); + } else if (end != buffer) { + size_t rootLen = (size_t)(end - buffer); + result = Py_BuildValue("NN", + PyUnicode_FromWideChar(path->wide, rootLen), + PyUnicode_FromWideChar(path->wide + rootLen, -1) + ); + } else { + result = Py_BuildValue("Os", path->object, ""); + } + PyMem_Free(buffer); + + return result; +} + + +#define PY_IFREG 1 // Regular file +#define PY_IFDIR 2 // Directory +#define PY_IFLNK 4 // Symlink +#define PY_IFMNT 8 // Mount Point (junction) +#define PY_IFLRP 16 // Link Reparse Point (name-surrogate, symlink, junction) +#define PY_IFRRP 32 // Regular Reparse Point + +static inline BOOL +_testInfo(DWORD attributes, DWORD reparseTag, BOOL diskDevice, int testedType) +{ + switch (testedType) { + case PY_IFREG: + return diskDevice && attributes && + !(attributes & FILE_ATTRIBUTE_DIRECTORY); + case PY_IFDIR: + return attributes & FILE_ATTRIBUTE_DIRECTORY; + case PY_IFLNK: + return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) && + reparseTag == IO_REPARSE_TAG_SYMLINK; + case PY_IFMNT: + return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) && + reparseTag == IO_REPARSE_TAG_MOUNT_POINT; + case PY_IFLRP: + return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) && + IsReparseTagNameSurrogate(reparseTag); + case PY_IFRRP: + return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) && + reparseTag && !IsReparseTagNameSurrogate(reparseTag); + } + + return FALSE; +} + +static BOOL +_testFileTypeByHandle(HANDLE hfile, int testedType, BOOL diskOnly) +{ + assert(testedType == PY_IFREG || testedType == PY_IFDIR || + testedType == PY_IFLNK || testedType == PY_IFMNT || + testedType == PY_IFLRP || testedType == PY_IFRRP); + + BOOL diskDevice = GetFileType(hfile) == FILE_TYPE_DISK; + if (diskOnly && !diskDevice) { + return FALSE; + } + if (testedType != PY_IFREG && testedType != PY_IFDIR) { + FILE_ATTRIBUTE_TAG_INFO info; + return GetFileInformationByHandleEx(hfile, FileAttributeTagInfo, &info, + sizeof(info)) && + _testInfo(info.FileAttributes, info.ReparseTag, diskDevice, + testedType); + } + FILE_BASIC_INFO info; + return GetFileInformationByHandleEx(hfile, FileBasicInfo, &info, + sizeof(info)) && + _testInfo(info.FileAttributes, 0, diskDevice, testedType); +} + +static BOOL +_testFileTypeByName(LPCWSTR path, int testedType) +{ + assert(testedType == PY_IFREG || testedType == PY_IFDIR || + testedType == PY_IFLNK || testedType == PY_IFMNT || + testedType == PY_IFLRP || testedType == PY_IFRRP); + + FILE_STAT_BASIC_INFORMATION info; + if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo, &info, + sizeof(info))) + { + BOOL diskDevice = info.DeviceType == FILE_DEVICE_DISK || + info.DeviceType == FILE_DEVICE_VIRTUAL_DISK || + info.DeviceType == FILE_DEVICE_CD_ROM; + BOOL result = _testInfo(info.FileAttributes, info.ReparseTag, + diskDevice, testedType); + if (!result || (testedType != PY_IFREG && testedType != PY_IFDIR) || + !(info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) + { + return result; + } + } + else if (_Py_GetFileInformationByName_ErrorIsTrustworthy( + GetLastError())) + { + return FALSE; + } + + DWORD flags = FILE_FLAG_BACKUP_SEMANTICS; + if (testedType != PY_IFREG && testedType != PY_IFDIR) { + flags |= FILE_FLAG_OPEN_REPARSE_POINT; + } + HANDLE hfile = CreateFileW(path, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, flags, NULL); + if (hfile != INVALID_HANDLE_VALUE) { + BOOL result = _testFileTypeByHandle(hfile, testedType, FALSE); + CloseHandle(hfile); + return result; + } + + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_CANT_ACCESS_FILE: + case ERROR_INVALID_PARAMETER: + int rc; + STRUCT_STAT st; + if (testedType == PY_IFREG || testedType == PY_IFDIR) { + rc = STAT(path, &st); + } + else { + // PY_IFRRP is not generally supported in this case, except for + // unhandled reparse points such as IO_REPARSE_TAG_APPEXECLINK. + rc = LSTAT(path, &st); + } + if (!rc) { + return _testInfo(st.st_file_attributes, st.st_reparse_tag, + st.st_mode & S_IFREG, testedType); + } + } + + return FALSE; +} + + +static BOOL +_testFileExistsByName(LPCWSTR path, BOOL followLinks) +{ + FILE_STAT_BASIC_INFORMATION info; + if (_Py_GetFileInformationByName(path, FileStatBasicByNameInfo, &info, + sizeof(info))) + { + if (!(info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) || + !followLinks && IsReparseTagNameSurrogate(info.ReparseTag)) + { + return TRUE; + } + } + else if (_Py_GetFileInformationByName_ErrorIsTrustworthy( + GetLastError())) + { + return FALSE; + } + + DWORD flags = FILE_FLAG_BACKUP_SEMANTICS; + if (!followLinks) { + flags |= FILE_FLAG_OPEN_REPARSE_POINT; + } + HANDLE hfile = CreateFileW(path, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, flags, NULL); + if (hfile != INVALID_HANDLE_VALUE) { + if (followLinks) { + CloseHandle(hfile); + return TRUE; + } + // Regular Reparse Points (PY_IFRRP) have to be traversed. + BOOL result = _testFileTypeByHandle(hfile, PY_IFRRP, FALSE); + CloseHandle(hfile); + if (!result) { + return TRUE; + } + hfile = CreateFileW(path, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (hfile != INVALID_HANDLE_VALUE) { + CloseHandle(hfile); + return TRUE; + } + } + + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_CANT_ACCESS_FILE: + case ERROR_INVALID_PARAMETER: + STRUCT_STAT _st; + return followLinks ? !STAT(path, &_st): !LSTAT(path, &_st); + } + + return FALSE; +} + + +static BOOL +_testFileExists(path_t *path, BOOL followLinks) +{ + BOOL result = FALSE; + if (path->value_error) { + return FALSE; + } + + Py_BEGIN_ALLOW_THREADS + if (path->fd != -1) { + HANDLE hfile = _Py_get_osfhandle_noraise(path->fd); + if (hfile != INVALID_HANDLE_VALUE) { + if (GetFileType(hfile) != FILE_TYPE_UNKNOWN || !GetLastError()) { + result = TRUE; + } + } + } + else if (path->wide) { + result = _testFileExistsByName(path->wide, followLinks); + } + Py_END_ALLOW_THREADS + + return result; +} + + +static BOOL +_testFileType(path_t *path, int testedType) +{ + BOOL result = FALSE; + if (path->value_error) { + return FALSE; + } + + Py_BEGIN_ALLOW_THREADS + if (path->fd != -1) { + HANDLE hfile = _Py_get_osfhandle_noraise(path->fd); + if (hfile != INVALID_HANDLE_VALUE) { + result = _testFileTypeByHandle(hfile, testedType, TRUE); + } + } + else if (path->wide) { + result = _testFileTypeByName(path->wide, testedType); + } + Py_END_ALLOW_THREADS + + return result; +} + + +/*[clinic input] +os._path_exists -> bool + + path: path_t(allow_fd=True, suppress_value_error=True) + +Test whether a path exists. Returns False for broken symbolic links. + +[clinic start generated code]*/ + +static int +os__path_exists_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=8da13acf666e16ba input=142beabfc66783eb]*/ +{ + return _testFileExists(path, TRUE); +} + + +/*[clinic input] +os._path_lexists -> bool + + path: path_t(allow_fd=True, suppress_value_error=True) + +Test whether a path exists. Returns True for broken symbolic links. + +[clinic start generated code]*/ + +static int +os__path_lexists_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=e7240ed5fc45bff3 input=208205112a3cc1ed]*/ +{ + return _testFileExists(path, FALSE); +} + + +/*[clinic input] +os._path_isdir -> bool + + s as path: path_t(allow_fd=True, suppress_value_error=True) + +Return true if the pathname refers to an existing directory. + +[clinic start generated code]*/ + +static int +os__path_isdir_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=d5786196f9e2fa7a input=132a3b5301aecf79]*/ +{ + return _testFileType(path, PY_IFDIR); +} + + +/*[clinic input] +os._path_isfile -> bool + + path: path_t(allow_fd=True, suppress_value_error=True) + +Test whether a path is a regular file + +[clinic start generated code]*/ + +static int +os__path_isfile_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=5c3073bc212b9863 input=4ac1fd350b30a39e]*/ +{ + return _testFileType(path, PY_IFREG); +} + + +/*[clinic input] +os._path_islink -> bool + + path: path_t(allow_fd=True, suppress_value_error=True) + +Test whether a path is a symbolic link + +[clinic start generated code]*/ + +static int +os__path_islink_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=30da7bda8296adcc input=7510ce05b547debb]*/ +{ + return _testFileType(path, PY_IFLNK); +} + + +/*[clinic input] +os._path_isjunction -> bool + + path: path_t(allow_fd=True, suppress_value_error=True) + +Test whether a path is a junction + +[clinic start generated code]*/ + +static int +os__path_isjunction_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=e1d17a9dd18a9945 input=7dcb8bc4e972fcaf]*/ +{ + return _testFileType(path, PY_IFMNT); +} + +#undef PY_IFREG +#undef PY_IFDIR +#undef PY_IFLNK +#undef PY_IFMNT +#undef PY_IFLRP +#undef PY_IFRRP + +#endif /* MS_WINDOWS */ + + +/*[clinic input] +os._path_splitroot_ex + + p as path: path_t(make_wide=True, nonstrict=True) + +Split a pathname into drive, root and tail. + +The tail contains anything after the root. +[clinic start generated code]*/ + +static PyObject * +os__path_splitroot_ex_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=4b0072b6cdf4b611 input=4556b615c7cc13f2]*/ +{ + Py_ssize_t drvsize, rootsize; + PyObject *drv = NULL, *root = NULL, *tail = NULL, *result = NULL; + + const wchar_t *buffer = path->wide; + _Py_skiproot(buffer, path->length, &drvsize, &rootsize); + drv = PyUnicode_FromWideChar(buffer, drvsize); + if (drv == NULL) { + goto exit; + } + root = PyUnicode_FromWideChar(&buffer[drvsize], rootsize); + if (root == NULL) { + goto exit; + } + tail = PyUnicode_FromWideChar(&buffer[drvsize + rootsize], + path->length - drvsize - rootsize); + if (tail == NULL) { + goto exit; + } + if (PyBytes_Check(path->object)) { + Py_SETREF(drv, PyUnicode_EncodeFSDefault(drv)); + if (drv == NULL) { + goto exit; + } + Py_SETREF(root, PyUnicode_EncodeFSDefault(root)); + if (root == NULL) { + goto exit; + } + Py_SETREF(tail, PyUnicode_EncodeFSDefault(tail)); + if (tail == NULL) { + goto exit; + } + } + result = PyTuple_Pack(3, drv, root, tail); +exit: + Py_XDECREF(drv); + Py_XDECREF(root); + Py_XDECREF(tail); + return result; +} + + +/*[clinic input] +os._path_normpath + + path: path_t(make_wide=True, nonstrict=True) + +Normalize path, eliminating double slashes, etc. +[clinic start generated code]*/ + +static PyObject * +os__path_normpath_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=d353e7ed9410c044 input=3d4ac23b06332dcb]*/ +{ + PyObject *result; + Py_ssize_t norm_len; + wchar_t *norm_path = _Py_normpath_and_size((wchar_t *)path->wide, + path->length, &norm_len); + if (!norm_len) { + result = PyUnicode_FromOrdinal('.'); + } + else { + result = PyUnicode_FromWideChar(norm_path, norm_len); + } + if (PyBytes_Check(path->object)) { + Py_SETREF(result, PyUnicode_EncodeFSDefault(result)); + } + return result; +} + +/*[clinic input] +os.mkdir + + path : path_t + + mode: int = 0o777 + + * + + dir_fd : dir_fd(requires='mkdirat') = None + +# "mkdir(path, mode=0o777, *, dir_fd=None)\n\n\ + +Create a directory. + +If dir_fd is not None, it should be a file descriptor open to a directory, + and path should be relative; path will then be relative to that directory. +dir_fd may not be implemented on your platform. + If it is unavailable, using it will raise a NotImplementedError. + +The mode argument is ignored on Windows. Where it is used, the current umask +value is first masked out. +[clinic start generated code]*/ + +static PyObject * +os_mkdir_impl(PyObject *module, path_t *path, int mode, int dir_fd) +/*[clinic end generated code: output=a70446903abe821f input=a61722e1576fab03]*/ +{ + int result; +#ifdef MS_WINDOWS + int error = 0; + int pathError = 0; + SECURITY_ATTRIBUTES secAttr = { sizeof(secAttr) }; + SECURITY_ATTRIBUTES *pSecAttr = NULL; +#endif +#ifdef HAVE_MKDIRAT + int mkdirat_unavailable = 0; +#endif + + if (PySys_Audit("os.mkdir", "Oii", path->object, mode, + dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) { + return NULL; + } + +#ifdef MS_WINDOWS + Py_BEGIN_ALLOW_THREADS + if (mode == 0700 /* 0o700 */) { + ULONG sdSize; + pSecAttr = &secAttr; + // Set a discretionary ACL (D) that is protected (P) and includes + // inheritable (OICI) entries that allow (A) full control (FA) to + // SYSTEM (SY), Administrators (BA), and the owner (OW). + if (!ConvertStringSecurityDescriptorToSecurityDescriptorW( + L"D:P(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;FA;;;OW)", + SDDL_REVISION_1, + &secAttr.lpSecurityDescriptor, + &sdSize + )) { + error = GetLastError(); + } + } + if (!error) { + result = CreateDirectoryW(path->wide, pSecAttr); + if (secAttr.lpSecurityDescriptor && + // uncommonly, LocalFree returns non-zero on error, but still uses + // GetLastError() to see what the error code is + LocalFree(secAttr.lpSecurityDescriptor)) { + error = GetLastError(); + } + } + Py_END_ALLOW_THREADS + + if (error) { + return PyErr_SetFromWindowsErr(error); + } + if (!result) { + return path_error(path); + } +#else + Py_BEGIN_ALLOW_THREADS +#if HAVE_MKDIRAT + if (dir_fd != DEFAULT_DIR_FD) { + if (HAVE_MKDIRAT_RUNTIME) { + result = mkdirat(dir_fd, path->narrow, mode); + + } else { + mkdirat_unavailable = 1; + } + } else +#endif +#if defined(__WATCOMC__) && !defined(__QNX__) + result = mkdir(path->narrow); +#else + result = mkdir(path->narrow, mode); +#endif + Py_END_ALLOW_THREADS + +#if HAVE_MKDIRAT + if (mkdirat_unavailable) { + argument_unavailable_error(NULL, "dir_fd"); + return NULL; + } +#endif + + if (result < 0) + return path_error(path); +#endif /* MS_WINDOWS */ + Py_RETURN_NONE; +} + + +/* sys/resource.h is needed for at least: wait3(), wait4(), broken nice. */ +#if defined(HAVE_SYS_RESOURCE_H) +#include +#endif + + +#ifdef HAVE_NICE +/*[clinic input] +os.nice + + increment: int + / + +Add increment to the priority of process and return the new priority. +[clinic start generated code]*/ + +static PyObject * +os_nice_impl(PyObject *module, int increment) +/*[clinic end generated code: output=9dad8a9da8109943 input=864be2d402a21da2]*/ +{ + int value; + + /* There are two flavours of 'nice': one that returns the new + priority (as required by almost all standards out there) and the + Linux/FreeBSD one, which returns '0' on success and advices + the use of getpriority() to get the new priority. + + If we are of the nice family that returns the new priority, we + need to clear errno before the call, and check if errno is filled + before calling posix_error() on a returnvalue of -1, because the + -1 may be the actual new priority! */ + + errno = 0; + value = nice(increment); +#if defined(HAVE_BROKEN_NICE) && defined(HAVE_GETPRIORITY) + if (value == 0) + value = getpriority(PRIO_PROCESS, 0); +#endif + if (value == -1 && errno != 0) + /* either nice() or getpriority() returned an error */ + return posix_error(); + return PyLong_FromLong((long) value); +} +#endif /* HAVE_NICE */ + + +#ifdef HAVE_GETPRIORITY +/*[clinic input] +os.getpriority + + which: int + who: int + +Return program scheduling priority. +[clinic start generated code]*/ + +static PyObject * +os_getpriority_impl(PyObject *module, int which, int who) +/*[clinic end generated code: output=c41b7b63c7420228 input=9be615d40e2544ef]*/ +{ + int retval; + + errno = 0; + retval = getpriority(which, who); + if (errno != 0) + return posix_error(); + return PyLong_FromLong((long)retval); +} +#endif /* HAVE_GETPRIORITY */ + + +#ifdef HAVE_SETPRIORITY +/*[clinic input] +os.setpriority + + which: int + who: int + priority: int + +Set program scheduling priority. +[clinic start generated code]*/ + +static PyObject * +os_setpriority_impl(PyObject *module, int which, int who, int priority) +/*[clinic end generated code: output=3d910d95a7771eb2 input=710ccbf65b9dc513]*/ +{ + int retval; + + retval = setpriority(which, who, priority); + if (retval == -1) + return posix_error(); + Py_RETURN_NONE; +} +#endif /* HAVE_SETPRIORITY */ + + +static PyObject * +internal_rename(path_t *src, path_t *dst, int src_dir_fd, int dst_dir_fd, int is_replace) +{ + const char *function_name = is_replace ? "replace" : "rename"; + int dir_fd_specified; + +#ifdef HAVE_RENAMEAT + int renameat_unavailable = 0; +#endif + +#ifdef MS_WINDOWS + BOOL result; + int flags = is_replace ? MOVEFILE_REPLACE_EXISTING : 0; +#else + int result; +#endif + + dir_fd_specified = (src_dir_fd != DEFAULT_DIR_FD) || + (dst_dir_fd != DEFAULT_DIR_FD); +#ifndef HAVE_RENAMEAT + if (dir_fd_specified) { + argument_unavailable_error(function_name, "src_dir_fd and dst_dir_fd"); + return NULL; + } +#endif + + if (PySys_Audit("os.rename", "OOii", src->object, dst->object, + src_dir_fd == DEFAULT_DIR_FD ? -1 : src_dir_fd, + dst_dir_fd == DEFAULT_DIR_FD ? -1 : dst_dir_fd) < 0) { + return NULL; + } + +#ifdef MS_WINDOWS + Py_BEGIN_ALLOW_THREADS + result = MoveFileExW(src->wide, dst->wide, flags); + Py_END_ALLOW_THREADS + + if (!result) + return path_error2(src, dst); + +#else + if ((src->narrow && dst->wide) || (src->wide && dst->narrow)) { + PyErr_Format(PyExc_ValueError, + "%s: src and dst must be the same type", function_name); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_RENAMEAT + if (dir_fd_specified) { + if (HAVE_RENAMEAT_RUNTIME) { + result = renameat(src_dir_fd, src->narrow, dst_dir_fd, dst->narrow); + } else { + renameat_unavailable = 1; + } + } else +#endif + result = rename(src->narrow, dst->narrow); + Py_END_ALLOW_THREADS + + +#ifdef HAVE_RENAMEAT + if (renameat_unavailable) { + argument_unavailable_error(function_name, "src_dir_fd and dst_dir_fd"); + return NULL; + } +#endif + + if (result) + return path_error2(src, dst); +#endif + Py_RETURN_NONE; +} + + +/*[clinic input] +os.rename + + src : path_t + dst : path_t + * + src_dir_fd : dir_fd = None + dst_dir_fd : dir_fd = None + +Rename a file or directory. + +If either src_dir_fd or dst_dir_fd is not None, it should be a file + descriptor open to a directory, and the respective path string (src or dst) + should be relative; the path will then be relative to that directory. +src_dir_fd and dst_dir_fd, may not be implemented on your platform. + If they are unavailable, using them will raise a NotImplementedError. +[clinic start generated code]*/ + +static PyObject * +os_rename_impl(PyObject *module, path_t *src, path_t *dst, int src_dir_fd, + int dst_dir_fd) +/*[clinic end generated code: output=59e803072cf41230 input=faa61c847912c850]*/ +{ + return internal_rename(src, dst, src_dir_fd, dst_dir_fd, 0); +} + + +/*[clinic input] +os.replace = os.rename + +Rename a file or directory, overwriting the destination. + +If either src_dir_fd or dst_dir_fd is not None, it should be a file + descriptor open to a directory, and the respective path string (src or dst) + should be relative; the path will then be relative to that directory. +src_dir_fd and dst_dir_fd, may not be implemented on your platform. + If they are unavailable, using them will raise a NotImplementedError. +[clinic start generated code]*/ + +static PyObject * +os_replace_impl(PyObject *module, path_t *src, path_t *dst, int src_dir_fd, + int dst_dir_fd) +/*[clinic end generated code: output=1968c02e7857422b input=c003f0def43378ef]*/ +{ + return internal_rename(src, dst, src_dir_fd, dst_dir_fd, 1); +} + + +/*[clinic input] +os.rmdir + + path: path_t + * + dir_fd: dir_fd(requires='unlinkat') = None + +Remove a directory. + +If dir_fd is not None, it should be a file descriptor open to a directory, + and path should be relative; path will then be relative to that directory. +dir_fd may not be implemented on your platform. + If it is unavailable, using it will raise a NotImplementedError. +[clinic start generated code]*/ + +static PyObject * +os_rmdir_impl(PyObject *module, path_t *path, int dir_fd) +/*[clinic end generated code: output=080eb54f506e8301 input=38c8b375ca34a7e2]*/ +{ + int result; +#ifdef HAVE_UNLINKAT + int unlinkat_unavailable = 0; +#endif + + if (PySys_Audit("os.rmdir", "Oi", path->object, + dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS +#ifdef MS_WINDOWS + /* Windows, success=1, UNIX, success=0 */ + result = !RemoveDirectoryW(path->wide); +#else +#ifdef HAVE_UNLINKAT + if (dir_fd != DEFAULT_DIR_FD) { + if (HAVE_UNLINKAT_RUNTIME) { + result = unlinkat(dir_fd, path->narrow, AT_REMOVEDIR); + } else { + unlinkat_unavailable = 1; + result = -1; + } + } else +#endif + result = rmdir(path->narrow); +#endif + Py_END_ALLOW_THREADS + +#ifdef HAVE_UNLINKAT + if (unlinkat_unavailable) { + argument_unavailable_error("rmdir", "dir_fd"); + return NULL; + } +#endif + + if (result) + return path_error(path); + + Py_RETURN_NONE; +} + + +#ifdef HAVE_SYSTEM +#ifdef MS_WINDOWS +/*[clinic input] +os.system -> long + + command: Py_UNICODE + +Execute the command in a subshell. +[clinic start generated code]*/ + +static long +os_system_impl(PyObject *module, const wchar_t *command) +/*[clinic end generated code: output=dd528cbd5943a679 input=303f5ce97df606b0]*/ +{ + long result; + + if (PySys_Audit("os.system", "(u)", command) < 0) { + return -1; + } + + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH + result = _wsystem(command); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + return result; +} +#else /* MS_WINDOWS */ +/*[clinic input] +os.system -> long + + command: FSConverter + +Execute the command in a subshell. +[clinic start generated code]*/ + +static long +os_system_impl(PyObject *module, PyObject *command) +/*[clinic end generated code: output=290fc437dd4f33a0 input=86a58554ba6094af]*/ +{ + long result; + const char *bytes = PyBytes_AsString(command); + + if (PySys_Audit("os.system", "(O)", command) < 0) { + return -1; + } + + Py_BEGIN_ALLOW_THREADS + result = system(bytes); + Py_END_ALLOW_THREADS + return result; +} +#endif +#endif /* HAVE_SYSTEM */ + + +#ifdef HAVE_UMASK +/*[clinic input] +os.umask + + mask: int + / + +Set the current numeric umask and return the previous umask. +[clinic start generated code]*/ + +static PyObject * +os_umask_impl(PyObject *module, int mask) +/*[clinic end generated code: output=a2e33ce3bc1a6e33 input=ab6bfd9b24d8a7e8]*/ +{ + int i = (int)umask(mask); + if (i < 0) + return posix_error(); + return PyLong_FromLong((long)i); +} +#endif + +#ifdef MS_WINDOWS + +/* override the default DeleteFileW behavior so that directory +symlinks can be removed with this function, the same as with +Unix symlinks */ +BOOL WINAPI Py_DeleteFileW(LPCWSTR lpFileName) +{ + WIN32_FILE_ATTRIBUTE_DATA info; + WIN32_FIND_DATAW find_data; + HANDLE find_data_handle; + int is_directory = 0; + int is_link = 0; + + if (GetFileAttributesExW(lpFileName, GetFileExInfoStandard, &info)) { + is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + + /* Get WIN32_FIND_DATA structure for the path to determine if + it is a symlink */ + if(is_directory && + info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + find_data_handle = FindFirstFileW(lpFileName, &find_data); + + if(find_data_handle != INVALID_HANDLE_VALUE) { + /* IO_REPARSE_TAG_SYMLINK if it is a symlink and + IO_REPARSE_TAG_MOUNT_POINT if it is a junction point. */ + is_link = find_data.dwReserved0 == IO_REPARSE_TAG_SYMLINK || + find_data.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT; + FindClose(find_data_handle); + } + } + } + + if (is_directory && is_link) + return RemoveDirectoryW(lpFileName); + + return DeleteFileW(lpFileName); +} +#endif /* MS_WINDOWS */ + + +/*[clinic input] +os.unlink + + path: path_t + * + dir_fd: dir_fd(requires='unlinkat')=None + +Remove a file (same as remove()). + +If dir_fd is not None, it should be a file descriptor open to a directory, + and path should be relative; path will then be relative to that directory. +dir_fd may not be implemented on your platform. + If it is unavailable, using it will raise a NotImplementedError. + +[clinic start generated code]*/ + +static PyObject * +os_unlink_impl(PyObject *module, path_t *path, int dir_fd) +/*[clinic end generated code: output=621797807b9963b1 input=d7bcde2b1b2a2552]*/ +{ + int result; +#ifdef HAVE_UNLINKAT + int unlinkat_unavailable = 0; +#endif + + if (PySys_Audit("os.remove", "Oi", path->object, + dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH +#ifdef MS_WINDOWS + /* Windows, success=1, UNIX, success=0 */ + result = !Py_DeleteFileW(path->wide); +#else +#ifdef HAVE_UNLINKAT + if (dir_fd != DEFAULT_DIR_FD) { + if (HAVE_UNLINKAT_RUNTIME) { + + result = unlinkat(dir_fd, path->narrow, 0); + } else { + unlinkat_unavailable = 1; + } + } else +#endif /* HAVE_UNLINKAT */ + result = unlink(path->narrow); +#endif + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + +#ifdef HAVE_UNLINKAT + if (unlinkat_unavailable) { + argument_unavailable_error(NULL, "dir_fd"); + return NULL; + } +#endif + + if (result) + return path_error(path); + + Py_RETURN_NONE; +} + + +/*[clinic input] +os.remove = os.unlink + +Remove a file (same as unlink()). + +If dir_fd is not None, it should be a file descriptor open to a directory, + and path should be relative; path will then be relative to that directory. +dir_fd may not be implemented on your platform. + If it is unavailable, using it will raise a NotImplementedError. +[clinic start generated code]*/ + +static PyObject * +os_remove_impl(PyObject *module, path_t *path, int dir_fd) +/*[clinic end generated code: output=a8535b28f0068883 input=e05c5ab55cd30983]*/ +{ + return os_unlink_impl(module, path, dir_fd); +} + + +static PyStructSequence_Field uname_result_fields[] = { + {"sysname", "operating system name"}, + {"nodename", "name of machine on network (implementation-defined)"}, + {"release", "operating system release"}, + {"version", "operating system version"}, + {"machine", "hardware identifier"}, + {NULL} +}; + +PyDoc_STRVAR(uname_result__doc__, +"uname_result: Result from os.uname().\n\n\ +This object may be accessed either as a tuple of\n\ + (sysname, nodename, release, version, machine),\n\ +or via the attributes sysname, nodename, release, version, and machine.\n\ +\n\ +See os.uname for more information."); + +static PyStructSequence_Desc uname_result_desc = { + MODNAME ".uname_result", /* name */ + uname_result__doc__, /* doc */ + uname_result_fields, + 5 +}; + +#ifdef HAVE_UNAME +/*[clinic input] +os.uname + +Return an object identifying the current operating system. + +The object behaves like a named tuple with the following fields: + (sysname, nodename, release, version, machine) + +[clinic start generated code]*/ + +static PyObject * +os_uname_impl(PyObject *module) +/*[clinic end generated code: output=e6a49cf1a1508a19 input=e68bd246db3043ed]*/ +{ + struct utsname u; + int res; + PyObject *value; + + Py_BEGIN_ALLOW_THREADS + res = uname(&u); + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + + PyObject *UnameResultType = get_posix_state(module)->UnameResultType; + value = PyStructSequence_New((PyTypeObject *)UnameResultType); + if (value == NULL) + return NULL; + +#define SET(i, field) \ + { \ + PyObject *o = PyUnicode_DecodeFSDefault(field); \ + if (!o) { \ + Py_DECREF(value); \ + return NULL; \ + } \ + PyStructSequence_SET_ITEM(value, i, o); \ + } \ + + SET(0, u.sysname); + SET(1, u.nodename); + SET(2, u.release); + SET(3, u.version); + SET(4, u.machine); + +#undef SET + + return value; +} +#endif /* HAVE_UNAME */ + + + +typedef struct { + int now; + time_t atime_s; + long atime_ns; + time_t mtime_s; + long mtime_ns; +} utime_t; + +/* + * these macros assume that "ut" is a pointer to a utime_t + * they also intentionally leak the declaration of a pointer named "time" + */ +#define UTIME_TO_TIMESPEC \ + struct timespec ts[2]; \ + struct timespec *time; \ + if (ut->now) \ + time = NULL; \ + else { \ + ts[0].tv_sec = ut->atime_s; \ + ts[0].tv_nsec = ut->atime_ns; \ + ts[1].tv_sec = ut->mtime_s; \ + ts[1].tv_nsec = ut->mtime_ns; \ + time = ts; \ + } \ + +#define UTIME_TO_TIMEVAL \ + struct timeval tv[2]; \ + struct timeval *time; \ + if (ut->now) \ + time = NULL; \ + else { \ + tv[0].tv_sec = ut->atime_s; \ + tv[0].tv_usec = ut->atime_ns / 1000; \ + tv[1].tv_sec = ut->mtime_s; \ + tv[1].tv_usec = ut->mtime_ns / 1000; \ + time = tv; \ + } \ + +#define UTIME_TO_UTIMBUF \ + struct utimbuf u; \ + struct utimbuf *time; \ + if (ut->now) \ + time = NULL; \ + else { \ + u.actime = ut->atime_s; \ + u.modtime = ut->mtime_s; \ + time = &u; \ + } + +#define UTIME_TO_TIME_T \ + time_t timet[2]; \ + time_t *time; \ + if (ut->now) \ + time = NULL; \ + else { \ + timet[0] = ut->atime_s; \ + timet[1] = ut->mtime_s; \ + time = timet; \ + } \ + + +#if defined(HAVE_FUTIMESAT) || defined(HAVE_UTIMENSAT) + +static int +utime_dir_fd(utime_t *ut, int dir_fd, const char *path, int follow_symlinks) +{ +#if defined(__APPLE__) && defined(HAVE_UTIMENSAT) + if (HAVE_UTIMENSAT_RUNTIME) { + int flags = follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW; + UTIME_TO_TIMESPEC; + return utimensat(dir_fd, path, time, flags); + } else { + errno = ENOSYS; + return -1; + } +#elif defined(HAVE_UTIMENSAT) + int flags = follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW; + UTIME_TO_TIMESPEC; + return utimensat(dir_fd, path, time, flags); +#elif defined(HAVE_FUTIMESAT) + UTIME_TO_TIMEVAL; + /* + * follow_symlinks will never be false here; + * we only allow !follow_symlinks and dir_fd together + * if we have utimensat() + */ + assert(follow_symlinks); + return futimesat(dir_fd, path, time); +#endif +} + + #define FUTIMENSAT_DIR_FD_CONVERTER dir_fd_converter +#else + #define FUTIMENSAT_DIR_FD_CONVERTER dir_fd_unavailable +#endif + +#if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMENS) + +static int +utime_fd(utime_t *ut, int fd) +{ +#ifdef HAVE_FUTIMENS + + if (HAVE_FUTIMENS_RUNTIME) { + + UTIME_TO_TIMESPEC; + return futimens(fd, time); + + } else +#ifndef HAVE_FUTIMES + { + /* Not sure if this can happen */ + PyErr_SetString( + PyExc_RuntimeError, + "neither futimens nor futimes are supported" + " on this system"); + return -1; + } +#endif + +#endif +#ifdef HAVE_FUTIMES + { + UTIME_TO_TIMEVAL; + return futimes(fd, time); + } +#endif +} + + #define PATH_UTIME_HAVE_FD 1 +#else + #define PATH_UTIME_HAVE_FD 0 +#endif + +#if defined(HAVE_UTIMENSAT) || defined(HAVE_LUTIMES) +# define UTIME_HAVE_NOFOLLOW_SYMLINKS +#endif + +#ifdef UTIME_HAVE_NOFOLLOW_SYMLINKS + +static int +utime_nofollow_symlinks(utime_t *ut, const char *path) +{ +#ifdef HAVE_UTIMENSAT + if (HAVE_UTIMENSAT_RUNTIME) { + UTIME_TO_TIMESPEC; + return utimensat(DEFAULT_DIR_FD, path, time, AT_SYMLINK_NOFOLLOW); + } else +#ifndef HAVE_LUTIMES + { + /* Not sure if this can happen */ + PyErr_SetString( + PyExc_RuntimeError, + "neither utimensat nor lutimes are supported" + " on this system"); + return -1; + } +#endif +#endif + +#ifdef HAVE_LUTIMES + { + UTIME_TO_TIMEVAL; + return lutimes(path, time); + } +#endif +} + +#endif + +#ifndef MS_WINDOWS + +static int +utime_default(utime_t *ut, const char *path) +{ +#if defined(__APPLE__) && defined(HAVE_UTIMENSAT) + if (HAVE_UTIMENSAT_RUNTIME) { + UTIME_TO_TIMESPEC; + return utimensat(DEFAULT_DIR_FD, path, time, 0); + } else { + UTIME_TO_TIMEVAL; + return utimes(path, time); + } +#elif defined(HAVE_UTIMENSAT) + UTIME_TO_TIMESPEC; + return utimensat(DEFAULT_DIR_FD, path, time, 0); +#elif defined(HAVE_UTIMES) + UTIME_TO_TIMEVAL; + return utimes(path, time); +#elif defined(HAVE_UTIME_H) + UTIME_TO_UTIMBUF; + return utime(path, time); +#else + UTIME_TO_TIME_T; + return utime(path, time); +#endif +} + +#endif + +static int +split_py_long_to_s_and_ns(PyObject *module, PyObject *py_long, time_t *s, long *ns) +{ + int result = 0; + PyObject *divmod; + divmod = PyNumber_Divmod(py_long, get_posix_state(module)->billion); + if (!divmod) + goto exit; + if (!PyTuple_Check(divmod) || PyTuple_GET_SIZE(divmod) != 2) { + PyErr_Format(PyExc_TypeError, + "%.200s.__divmod__() must return a 2-tuple, not %.200s", + _PyType_Name(Py_TYPE(py_long)), _PyType_Name(Py_TYPE(divmod))); + goto exit; + } + *s = _PyLong_AsTime_t(PyTuple_GET_ITEM(divmod, 0)); + if ((*s == -1) && PyErr_Occurred()) + goto exit; + *ns = PyLong_AsLong(PyTuple_GET_ITEM(divmod, 1)); + if ((*ns == -1) && PyErr_Occurred()) + goto exit; + + result = 1; +exit: + Py_XDECREF(divmod); + return result; +} + + +/*[clinic input] +os.utime + + path: path_t(allow_fd='PATH_UTIME_HAVE_FD') + times: object = None + * + ns: object = NULL + dir_fd: dir_fd(requires='futimensat') = None + follow_symlinks: bool=True + +# "utime(path, times=None, *[, ns], dir_fd=None, follow_symlinks=True)\n\ + +Set the access and modified time of path. + +path may always be specified as a string. +On some platforms, path may also be specified as an open file descriptor. + If this functionality is unavailable, using it raises an exception. + +If times is not None, it must be a tuple (atime, mtime); + atime and mtime should be expressed as float seconds since the epoch. +If ns is specified, it must be a tuple (atime_ns, mtime_ns); + atime_ns and mtime_ns should be expressed as integer nanoseconds + since the epoch. +If times is None and ns is unspecified, utime uses the current time. +Specifying tuples for both times and ns is an error. + +If dir_fd is not None, it should be a file descriptor open to a directory, + and path should be relative; path will then be relative to that directory. +If follow_symlinks is False, and the last element of the path is a symbolic + link, utime will modify the symbolic link itself instead of the file the + link points to. +It is an error to use dir_fd or follow_symlinks when specifying path + as an open file descriptor. +dir_fd and follow_symlinks may not be available on your platform. + If they are unavailable, using them will raise a NotImplementedError. + +[clinic start generated code]*/ + +static PyObject * +os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, + int dir_fd, int follow_symlinks) +/*[clinic end generated code: output=cfcac69d027b82cf input=2fbd62a2f228f8f4]*/ +{ +#ifdef MS_WINDOWS + HANDLE hFile = 0; + FILETIME atime, mtime; +#else + int result; +#endif + + utime_t utime; + + memset(&utime, 0, sizeof(utime_t)); + + if (times != Py_None && ns) { + PyErr_SetString(PyExc_ValueError, + "utime: you may specify either 'times'" + " or 'ns' but not both"); + return NULL; + } + + if (times != Py_None) { + time_t a_sec, m_sec; + long a_nsec, m_nsec; + if (!PyTuple_CheckExact(times) || (PyTuple_Size(times) != 2)) { + PyErr_SetString(PyExc_TypeError, + "utime: 'times' must be either" + " a tuple of two ints or None"); + return NULL; + } + utime.now = 0; + if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 0), + &a_sec, &a_nsec, _PyTime_ROUND_FLOOR) == -1 || + _PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 1), + &m_sec, &m_nsec, _PyTime_ROUND_FLOOR) == -1) { + return NULL; + } + utime.atime_s = a_sec; + utime.atime_ns = a_nsec; + utime.mtime_s = m_sec; + utime.mtime_ns = m_nsec; + } + else if (ns) { + if (!PyTuple_CheckExact(ns) || (PyTuple_Size(ns) != 2)) { + PyErr_SetString(PyExc_TypeError, + "utime: 'ns' must be a tuple of two ints"); + return NULL; + } + utime.now = 0; + if (!split_py_long_to_s_and_ns(module, PyTuple_GET_ITEM(ns, 0), + &utime.atime_s, &utime.atime_ns) || + !split_py_long_to_s_and_ns(module, PyTuple_GET_ITEM(ns, 1), + &utime.mtime_s, &utime.mtime_ns)) { + return NULL; + } + } + else { + /* times and ns are both None/unspecified. use "now". */ + utime.now = 1; + } + +#if !defined(UTIME_HAVE_NOFOLLOW_SYMLINKS) + if (follow_symlinks_specified("utime", follow_symlinks)) + return NULL; +#endif + + if (path_and_dir_fd_invalid("utime", path, dir_fd) || + dir_fd_and_fd_invalid("utime", dir_fd, path->fd) || + fd_and_follow_symlinks_invalid("utime", path->fd, follow_symlinks)) + return NULL; + +#if !defined(HAVE_UTIMENSAT) + if ((dir_fd != DEFAULT_DIR_FD) && (!follow_symlinks)) { + PyErr_SetString(PyExc_ValueError, + "utime: cannot use dir_fd and follow_symlinks " + "together on this platform"); + return NULL; + } +#endif + + if (PySys_Audit("os.utime", "OOOi", path->object, times, ns ? ns : Py_None, + dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) { + return NULL; + } + +#ifdef MS_WINDOWS + Py_BEGIN_ALLOW_THREADS + hFile = CreateFileW(path->wide, FILE_WRITE_ATTRIBUTES, 0, + NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, NULL); + Py_END_ALLOW_THREADS + if (hFile == INVALID_HANDLE_VALUE) { + path_error(path); + return NULL; + } + + if (utime.now) { + GetSystemTimeAsFileTime(&mtime); + atime = mtime; + } + else { + _Py_time_t_to_FILE_TIME(utime.atime_s, utime.atime_ns, &atime); + _Py_time_t_to_FILE_TIME(utime.mtime_s, utime.mtime_ns, &mtime); + } + if (!SetFileTime(hFile, NULL, &atime, &mtime)) { + path_error(path); + CloseHandle(hFile); + return NULL; + } + CloseHandle(hFile); +#else /* MS_WINDOWS */ + Py_BEGIN_ALLOW_THREADS + +#ifdef UTIME_HAVE_NOFOLLOW_SYMLINKS + if ((!follow_symlinks) && (dir_fd == DEFAULT_DIR_FD)) + result = utime_nofollow_symlinks(&utime, path->narrow); + else +#endif + +#if defined(HAVE_FUTIMESAT) || defined(HAVE_UTIMENSAT) + if ((dir_fd != DEFAULT_DIR_FD) || (!follow_symlinks)) { + result = utime_dir_fd(&utime, dir_fd, path->narrow, follow_symlinks); + + } else +#endif + +#if defined(HAVE_FUTIMES) || defined(HAVE_FUTIMENS) + if (path->fd != -1) + result = utime_fd(&utime, path->fd); + else +#endif + + result = utime_default(&utime, path->narrow); + + Py_END_ALLOW_THREADS + +#if defined(__APPLE__) && defined(HAVE_UTIMENSAT) + /* See utime_dir_fd implementation */ + if (result == -1 && errno == ENOSYS) { + argument_unavailable_error(NULL, "dir_fd"); + return NULL; + } +#endif + + if (result < 0) { + path_error(path); + return NULL; + } + +#endif /* MS_WINDOWS */ + + Py_RETURN_NONE; +} + +/* Process operations */ + + +/*[clinic input] +os._exit + + status: int + +Exit to the system with specified status, without normal exit processing. +[clinic start generated code]*/ + +static PyObject * +os__exit_impl(PyObject *module, int status) +/*[clinic end generated code: output=116e52d9c2260d54 input=5e6d57556b0c4a62]*/ +{ + _exit(status); + return NULL; /* Make gcc -Wall happy */ +} + +#if defined(HAVE_WEXECV) || defined(HAVE_WSPAWNV) +#define EXECV_CHAR wchar_t +#else +#define EXECV_CHAR char +#endif + +#if defined(HAVE_EXECV) || defined(HAVE_SPAWNV) || defined(HAVE_RTPSPAWN) +static void +free_string_array(EXECV_CHAR **array, Py_ssize_t count) +{ + Py_ssize_t i; + for (i = 0; i < count; i++) + PyMem_Free(array[i]); + PyMem_Free(array); +} + +static int +fsconvert_strdup(PyObject *o, EXECV_CHAR **out) +{ + Py_ssize_t size; + PyObject *ub; + int result = 0; +#if defined(HAVE_WEXECV) || defined(HAVE_WSPAWNV) + if (!PyUnicode_FSDecoder(o, &ub)) + return 0; + *out = PyUnicode_AsWideCharString(ub, &size); + if (*out) + result = 1; +#else + if (!PyUnicode_FSConverter(o, &ub)) + return 0; + size = PyBytes_GET_SIZE(ub); + *out = PyMem_Malloc(size + 1); + if (*out) { + memcpy(*out, PyBytes_AS_STRING(ub), size + 1); + result = 1; + } else + PyErr_NoMemory(); +#endif + Py_DECREF(ub); + return result; +} +#endif + +#if defined(HAVE_EXECV) || defined (HAVE_FEXECVE) || defined(HAVE_RTPSPAWN) +static EXECV_CHAR** +parse_envlist(PyObject* env, Py_ssize_t *envc_ptr) +{ + Py_ssize_t i, pos, envc; + PyObject *keys=NULL, *vals=NULL; + PyObject *key2, *val2, *keyval; + EXECV_CHAR **envlist; + + i = PyMapping_Size(env); + if (i < 0) + return NULL; + envlist = PyMem_NEW(EXECV_CHAR *, i + 1); + if (envlist == NULL) { + PyErr_NoMemory(); + return NULL; + } + envc = 0; + keys = PyMapping_Keys(env); + if (!keys) + goto error; + vals = PyMapping_Values(env); + if (!vals) + goto error; + if (!PyList_Check(keys) || !PyList_Check(vals)) { + PyErr_Format(PyExc_TypeError, + "env.keys() or env.values() is not a list"); + goto error; + } + + for (pos = 0; pos < i; pos++) { + PyObject *key = PyList_GetItem(keys, pos); // Borrowed ref. + if (key == NULL) { + goto error; + } + PyObject *val = PyList_GetItem(vals, pos); // Borrowed ref. + if (val == NULL) { + goto error; + } + +#if defined(HAVE_WEXECV) || defined(HAVE_WSPAWNV) + if (!PyUnicode_FSDecoder(key, &key2)) + goto error; + if (!PyUnicode_FSDecoder(val, &val2)) { + Py_DECREF(key2); + goto error; + } + /* Search from index 1 because on Windows starting '=' is allowed for + defining hidden environment variables. */ + if (PyUnicode_GET_LENGTH(key2) == 0 || + PyUnicode_FindChar(key2, '=', 1, PyUnicode_GET_LENGTH(key2), 1) != -1) + { + PyErr_SetString(PyExc_ValueError, "illegal environment variable name"); + Py_DECREF(key2); + Py_DECREF(val2); + goto error; + } + keyval = PyUnicode_FromFormat("%U=%U", key2, val2); +#else + if (!PyUnicode_FSConverter(key, &key2)) + goto error; + if (!PyUnicode_FSConverter(val, &val2)) { + Py_DECREF(key2); + goto error; + } + if (PyBytes_GET_SIZE(key2) == 0 || + strchr(PyBytes_AS_STRING(key2) + 1, '=') != NULL) + { + PyErr_SetString(PyExc_ValueError, "illegal environment variable name"); + Py_DECREF(key2); + Py_DECREF(val2); + goto error; + } + keyval = PyBytes_FromFormat("%s=%s", PyBytes_AS_STRING(key2), + PyBytes_AS_STRING(val2)); +#endif + Py_DECREF(key2); + Py_DECREF(val2); + if (!keyval) + goto error; + + if (!fsconvert_strdup(keyval, &envlist[envc++])) { + Py_DECREF(keyval); + goto error; + } + + Py_DECREF(keyval); + } + Py_DECREF(vals); + Py_DECREF(keys); + + envlist[envc] = 0; + *envc_ptr = envc; + return envlist; + +error: + Py_XDECREF(keys); + Py_XDECREF(vals); + free_string_array(envlist, envc); + return NULL; +} + +static EXECV_CHAR** +parse_arglist(PyObject* argv, Py_ssize_t *argc) +{ + int i; + EXECV_CHAR **argvlist = PyMem_NEW(EXECV_CHAR *, *argc+1); + if (argvlist == NULL) { + PyErr_NoMemory(); + return NULL; + } + for (i = 0; i < *argc; i++) { + PyObject* item = PySequence_ITEM(argv, i); + if (item == NULL) + goto fail; + if (!fsconvert_strdup(item, &argvlist[i])) { + Py_DECREF(item); + goto fail; + } + Py_DECREF(item); + } + argvlist[*argc] = NULL; + return argvlist; +fail: + *argc = i; + free_string_array(argvlist, *argc); + return NULL; +} + +#endif + + +#ifdef HAVE_EXECV +/*[clinic input] +os.execv + + path: path_t + Path of executable file. + argv: object + Tuple or list of strings. + / + +Execute an executable path with arguments, replacing current process. +[clinic start generated code]*/ + +static PyObject * +os_execv_impl(PyObject *module, path_t *path, PyObject *argv) +/*[clinic end generated code: output=3b52fec34cd0dafd input=9bac31efae07dac7]*/ +{ + EXECV_CHAR **argvlist; + Py_ssize_t argc; + + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_EXEC)) { + PyErr_SetString(PyExc_RuntimeError, + "exec not supported for isolated subinterpreters"); + return NULL; + } + + /* execv has two arguments: (path, argv), where + argv is a list or tuple of strings. */ + + if (!PyList_Check(argv) && !PyTuple_Check(argv)) { + PyErr_SetString(PyExc_TypeError, + "execv() arg 2 must be a tuple or list"); + return NULL; + } + argc = PySequence_Size(argv); + if (argc < 1) { + PyErr_SetString(PyExc_ValueError, "execv() arg 2 must not be empty"); + return NULL; + } + + argvlist = parse_arglist(argv, &argc); + if (argvlist == NULL) { + return NULL; + } + if (!argvlist[0][0]) { + PyErr_SetString(PyExc_ValueError, + "execv() arg 2 first element cannot be empty"); + free_string_array(argvlist, argc); + return NULL; + } + + if (PySys_Audit("os.exec", "OOO", path->object, argv, Py_None) < 0) { + free_string_array(argvlist, argc); + return NULL; + } + + _Py_BEGIN_SUPPRESS_IPH +#ifdef HAVE_WEXECV + _wexecv(path->wide, argvlist); +#else + execv(path->narrow, argvlist); +#endif + _Py_END_SUPPRESS_IPH + + /* If we get here it's definitely an error */ + + posix_error(); + free_string_array(argvlist, argc); + return NULL; +} + + +/*[clinic input] +os.execve + + path: path_t(allow_fd='PATH_HAVE_FEXECVE') + Path of executable file. + argv: object + Tuple or list of strings. + env: object + Dictionary of strings mapping to strings. + +Execute an executable path with arguments, replacing current process. +[clinic start generated code]*/ + +static PyObject * +os_execve_impl(PyObject *module, path_t *path, PyObject *argv, PyObject *env) +/*[clinic end generated code: output=ff9fa8e4da8bde58 input=626804fa092606d9]*/ +{ + EXECV_CHAR **argvlist = NULL; + EXECV_CHAR **envlist; + Py_ssize_t argc, envc; + + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_EXEC)) { + PyErr_SetString(PyExc_RuntimeError, + "exec not supported for isolated subinterpreters"); + return NULL; + } + + /* execve has three arguments: (path, argv, env), where + argv is a list or tuple of strings and env is a dictionary + like posix.environ. */ + + if (!PyList_Check(argv) && !PyTuple_Check(argv)) { + PyErr_SetString(PyExc_TypeError, + "execve: argv must be a tuple or list"); + goto fail_0; + } + argc = PySequence_Size(argv); + if (argc < 1) { + PyErr_SetString(PyExc_ValueError, "execve: argv must not be empty"); + return NULL; + } + + if (!PyMapping_Check(env)) { + PyErr_SetString(PyExc_TypeError, + "execve: environment must be a mapping object"); + goto fail_0; + } + + argvlist = parse_arglist(argv, &argc); + if (argvlist == NULL) { + goto fail_0; + } + if (!argvlist[0][0]) { + PyErr_SetString(PyExc_ValueError, + "execve: argv first element cannot be empty"); + goto fail_0; + } + + envlist = parse_envlist(env, &envc); + if (envlist == NULL) + goto fail_0; + + if (PySys_Audit("os.exec", "OOO", path->object, argv, env) < 0) { + goto fail_1; + } + + _Py_BEGIN_SUPPRESS_IPH +#ifdef HAVE_FEXECVE + if (path->fd > -1) + fexecve(path->fd, argvlist, envlist); + else +#endif +#ifdef HAVE_WEXECV + _wexecve(path->wide, argvlist, envlist); +#else + execve(path->narrow, argvlist, envlist); +#endif + _Py_END_SUPPRESS_IPH + + /* If we get here it's definitely an error */ + + posix_path_error(path); + fail_1: + free_string_array(envlist, envc); + fail_0: + if (argvlist) + free_string_array(argvlist, argc); + return NULL; +} + +#endif /* HAVE_EXECV */ + +#ifdef HAVE_POSIX_SPAWN + +enum posix_spawn_file_actions_identifier { + POSIX_SPAWN_OPEN, + POSIX_SPAWN_CLOSE, + POSIX_SPAWN_DUP2 +#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP + ,POSIX_SPAWN_CLOSEFROM +#endif +}; + +#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) +static int +convert_sched_param(PyObject *module, PyObject *param, struct sched_param *res); +#endif + +static int +parse_posix_spawn_flags(PyObject *module, const char *func_name, PyObject *setpgroup, + int resetids, int setsid, PyObject *setsigmask, + PyObject *setsigdef, PyObject *scheduler, + posix_spawnattr_t *attrp) +{ + long all_flags = 0; + + errno = posix_spawnattr_init(attrp); + if (errno) { + posix_error(); + return -1; + } + + if (setpgroup) { + pid_t pgid = PyLong_AsPid(setpgroup); + if (pgid == (pid_t)-1 && PyErr_Occurred()) { + goto fail; + } + errno = posix_spawnattr_setpgroup(attrp, pgid); + if (errno) { + posix_error(); + goto fail; + } + all_flags |= POSIX_SPAWN_SETPGROUP; + } + + if (resetids) { + all_flags |= POSIX_SPAWN_RESETIDS; + } + + if (setsid) { +#ifdef HAVE_POSIX_SPAWN_SETSID_RUNTIME + if (HAVE_POSIX_SPAWN_SETSID_RUNTIME) { +#endif +#ifdef POSIX_SPAWN_SETSID + all_flags |= POSIX_SPAWN_SETSID; +#elif defined(POSIX_SPAWN_SETSID_NP) + all_flags |= POSIX_SPAWN_SETSID_NP; +#else + argument_unavailable_error(func_name, "setsid"); + return -1; +#endif + +#ifdef HAVE_POSIX_SPAWN_SETSID_RUNTIME + } else { + argument_unavailable_error(func_name, "setsid"); + return -1; + } +#endif /* HAVE_POSIX_SPAWN_SETSID_RUNTIME */ + + } + +#ifdef HAVE_SIGSET_T + if (setsigmask) { + sigset_t set; + if (!_Py_Sigset_Converter(setsigmask, &set)) { + goto fail; + } + errno = posix_spawnattr_setsigmask(attrp, &set); + if (errno) { + posix_error(); + goto fail; + } + all_flags |= POSIX_SPAWN_SETSIGMASK; + } + + if (setsigdef) { + sigset_t set; + if (!_Py_Sigset_Converter(setsigdef, &set)) { + goto fail; + } + errno = posix_spawnattr_setsigdefault(attrp, &set); + if (errno) { + posix_error(); + goto fail; + } + all_flags |= POSIX_SPAWN_SETSIGDEF; + } +#else + if (setsigmask || setsigdef) { + PyErr_SetString(PyExc_NotImplementedError, + "sigset is not supported on this platform"); + goto fail; + } +#endif + + if (scheduler) { +#ifdef POSIX_SPAWN_SETSCHEDULER + PyObject *py_schedpolicy; + PyObject *schedparam_obj; + struct sched_param schedparam; + + if (!PyArg_ParseTuple(scheduler, "OO" + ";A scheduler tuple must have two elements", + &py_schedpolicy, &schedparam_obj)) { + goto fail; + } + if (!convert_sched_param(module, schedparam_obj, &schedparam)) { + goto fail; + } + if (py_schedpolicy != Py_None) { + int schedpolicy = PyLong_AsInt(py_schedpolicy); + + if (schedpolicy == -1 && PyErr_Occurred()) { + goto fail; + } + errno = posix_spawnattr_setschedpolicy(attrp, schedpolicy); + if (errno) { + posix_error(); + goto fail; + } + all_flags |= POSIX_SPAWN_SETSCHEDULER; + } + errno = posix_spawnattr_setschedparam(attrp, &schedparam); + if (errno) { + posix_error(); + goto fail; + } + all_flags |= POSIX_SPAWN_SETSCHEDPARAM; +#else + PyErr_SetString(PyExc_NotImplementedError, + "The scheduler option is not supported in this system."); + goto fail; +#endif + } + + errno = posix_spawnattr_setflags(attrp, all_flags); + if (errno) { + posix_error(); + goto fail; + } + + return 0; + +fail: + (void)posix_spawnattr_destroy(attrp); + return -1; +} + +static int +parse_file_actions(PyObject *file_actions, + posix_spawn_file_actions_t *file_actionsp, + PyObject *temp_buffer) +{ + PyObject *seq; + PyObject *file_action = NULL; + PyObject *tag_obj; + + seq = PySequence_Fast(file_actions, + "file_actions must be a sequence or None"); + if (seq == NULL) { + return -1; + } + + errno = posix_spawn_file_actions_init(file_actionsp); + if (errno) { + posix_error(); + Py_DECREF(seq); + return -1; + } + + for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(seq); ++i) { + file_action = PySequence_Fast_GET_ITEM(seq, i); + Py_INCREF(file_action); + if (!PyTuple_Check(file_action) || !PyTuple_GET_SIZE(file_action)) { + PyErr_SetString(PyExc_TypeError, + "Each file_actions element must be a non-empty tuple"); + goto fail; + } + long tag = PyLong_AsLong(PyTuple_GET_ITEM(file_action, 0)); + if (tag == -1 && PyErr_Occurred()) { + goto fail; + } + + /* Populate the file_actions object */ + switch (tag) { + case POSIX_SPAWN_OPEN: { + int fd, oflag; + PyObject *path; + unsigned long mode; + if (!PyArg_ParseTuple(file_action, "OiO&ik" + ";A open file_action tuple must have 5 elements", + &tag_obj, &fd, PyUnicode_FSConverter, &path, + &oflag, &mode)) + { + goto fail; + } + if (PyList_Append(temp_buffer, path)) { + Py_DECREF(path); + goto fail; + } + errno = posix_spawn_file_actions_addopen(file_actionsp, + fd, PyBytes_AS_STRING(path), oflag, (mode_t)mode); + if (errno) { + posix_error(); + Py_DECREF(path); + goto fail; + } + Py_DECREF(path); + break; + } + case POSIX_SPAWN_CLOSE: { + int fd; + if (!PyArg_ParseTuple(file_action, "Oi" + ";A close file_action tuple must have 2 elements", + &tag_obj, &fd)) + { + goto fail; + } + errno = posix_spawn_file_actions_addclose(file_actionsp, fd); + if (errno) { + posix_error(); + goto fail; + } + break; + } + case POSIX_SPAWN_DUP2: { + int fd1, fd2; + if (!PyArg_ParseTuple(file_action, "Oii" + ";A dup2 file_action tuple must have 3 elements", + &tag_obj, &fd1, &fd2)) + { + goto fail; + } + errno = posix_spawn_file_actions_adddup2(file_actionsp, + fd1, fd2); + if (errno) { + posix_error(); + goto fail; + } + break; + } +#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP + case POSIX_SPAWN_CLOSEFROM: { + int fd; + if (!PyArg_ParseTuple(file_action, "Oi" + ";A closefrom file_action tuple must have 2 elements", + &tag_obj, &fd)) + { + goto fail; + } + errno = posix_spawn_file_actions_addclosefrom_np(file_actionsp, + fd); + if (errno) { + posix_error(); + goto fail; + } + break; + } +#endif + default: { + PyErr_SetString(PyExc_TypeError, + "Unknown file_actions identifier"); + goto fail; + } + } + Py_DECREF(file_action); + } + + Py_DECREF(seq); + return 0; + +fail: + Py_DECREF(seq); + Py_DECREF(file_action); + (void)posix_spawn_file_actions_destroy(file_actionsp); + return -1; +} + + +static PyObject * +py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *argv, + PyObject *env, PyObject *file_actions, + PyObject *setpgroup, int resetids, int setsid, PyObject *setsigmask, + PyObject *setsigdef, PyObject *scheduler) +{ + const char *func_name = use_posix_spawnp ? "posix_spawnp" : "posix_spawn"; + EXECV_CHAR **argvlist = NULL; + EXECV_CHAR **envlist = NULL; + posix_spawn_file_actions_t file_actions_buf; + posix_spawn_file_actions_t *file_actionsp = NULL; + posix_spawnattr_t attr; + posix_spawnattr_t *attrp = NULL; + Py_ssize_t argc, envc; + PyObject *result = NULL; + PyObject *temp_buffer = NULL; + pid_t pid; + int err_code; + + /* posix_spawn and posix_spawnp have three arguments: (path, argv, env), where + argv is a list or tuple of strings and env is a dictionary + like posix.environ. */ + + if (!PyList_Check(argv) && !PyTuple_Check(argv)) { + PyErr_Format(PyExc_TypeError, + "%s: argv must be a tuple or list", func_name); + goto exit; + } + argc = PySequence_Size(argv); + if (argc < 1) { + PyErr_Format(PyExc_ValueError, + "%s: argv must not be empty", func_name); + return NULL; + } + + if (!PyMapping_Check(env) && env != Py_None) { + PyErr_Format(PyExc_TypeError, + "%s: environment must be a mapping object or None", func_name); + goto exit; + } + + argvlist = parse_arglist(argv, &argc); + if (argvlist == NULL) { + goto exit; + } + if (!argvlist[0][0]) { + PyErr_Format(PyExc_ValueError, + "%s: argv first element cannot be empty", func_name); + goto exit; + } + +#ifdef USE_DARWIN_NS_GET_ENVIRON + // There is no environ global in this situation. + char **environ = NULL; +#endif + + if (env == Py_None) { +#ifdef USE_DARWIN_NS_GET_ENVIRON + environ = *_NSGetEnviron(); +#endif + envlist = environ; + } else { + envlist = parse_envlist(env, &envc); + if (envlist == NULL) { + goto exit; + } + } + + if (file_actions != NULL && file_actions != Py_None) { + /* There is a bug in old versions of glibc that makes some of the + * helper functions for manipulating file actions not copy the provided + * buffers. The problem is that posix_spawn_file_actions_addopen does not + * copy the value of path for some old versions of glibc (<2.20). + * The use of temp_buffer here is a workaround that keeps the + * python objects that own the buffers alive until posix_spawn gets called. + * Check https://bugs.python.org/issue33630 and + * https://sourceware.org/bugzilla/show_bug.cgi?id=17048 for more info.*/ + temp_buffer = PyList_New(0); + if (!temp_buffer) { + goto exit; + } + if (parse_file_actions(file_actions, &file_actions_buf, temp_buffer)) { + goto exit; + } + file_actionsp = &file_actions_buf; + } + + if (parse_posix_spawn_flags(module, func_name, setpgroup, resetids, setsid, + setsigmask, setsigdef, scheduler, &attr)) { + goto exit; + } + attrp = &attr; + + if (PySys_Audit("os.posix_spawn", "OOO", path->object, argv, env) < 0) { + goto exit; + } + + _Py_BEGIN_SUPPRESS_IPH +#ifdef HAVE_POSIX_SPAWNP + if (use_posix_spawnp) { + err_code = posix_spawnp(&pid, path->narrow, + file_actionsp, attrp, argvlist, envlist); + } + else +#endif /* HAVE_POSIX_SPAWNP */ + { + err_code = posix_spawn(&pid, path->narrow, + file_actionsp, attrp, argvlist, envlist); + } + _Py_END_SUPPRESS_IPH + + if (err_code) { + errno = err_code; + PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object); + goto exit; + } +#ifdef _Py_MEMORY_SANITIZER + __msan_unpoison(&pid, sizeof(pid)); +#endif + result = PyLong_FromPid(pid); + +exit: + if (file_actionsp) { + (void)posix_spawn_file_actions_destroy(file_actionsp); + } + if (attrp) { + (void)posix_spawnattr_destroy(attrp); + } + if (envlist && envlist != environ) { + free_string_array(envlist, envc); + } + if (argvlist) { + free_string_array(argvlist, argc); + } + Py_XDECREF(temp_buffer); + return result; +} + + +/*[clinic input] + +os.posix_spawn + path: path_t + Path of executable file. + argv: object + Tuple or list of strings. + env: object + Dictionary of strings mapping to strings. + / + * + file_actions: object(c_default='NULL') = () + A sequence of file action tuples. + setpgroup: object = NULL + The pgroup to use with the POSIX_SPAWN_SETPGROUP flag. + resetids: bool = False + If the value is `true` the POSIX_SPAWN_RESETIDS will be activated. + setsid: bool = False + If the value is `true` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP will be activated. + setsigmask: object(c_default='NULL') = () + The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag. + setsigdef: object(c_default='NULL') = () + The sigmask to use with the POSIX_SPAWN_SETSIGDEF flag. + scheduler: object = NULL + A tuple with the scheduler policy (optional) and parameters. + +Execute the program specified by path in a new process. +[clinic start generated code]*/ + +static PyObject * +os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv, + PyObject *env, PyObject *file_actions, + PyObject *setpgroup, int resetids, int setsid, + PyObject *setsigmask, PyObject *setsigdef, + PyObject *scheduler) +/*[clinic end generated code: output=14a1098c566bc675 input=808aed1090d84e33]*/ +{ + return py_posix_spawn(0, module, path, argv, env, file_actions, + setpgroup, resetids, setsid, setsigmask, setsigdef, + scheduler); +} + #endif /* HAVE_POSIX_SPAWN */ + + + +#ifdef HAVE_POSIX_SPAWNP +/*[clinic input] + +os.posix_spawnp + path: path_t + Path of executable file. + argv: object + Tuple or list of strings. + env: object + Dictionary of strings mapping to strings. + / + * + file_actions: object(c_default='NULL') = () + A sequence of file action tuples. + setpgroup: object = NULL + The pgroup to use with the POSIX_SPAWN_SETPGROUP flag. + resetids: bool = False + If the value is `True` the POSIX_SPAWN_RESETIDS will be activated. + setsid: bool = False + If the value is `True` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP will be activated. + setsigmask: object(c_default='NULL') = () + The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag. + setsigdef: object(c_default='NULL') = () + The sigmask to use with the POSIX_SPAWN_SETSIGDEF flag. + scheduler: object = NULL + A tuple with the scheduler policy (optional) and parameters. + +Execute the program specified by path in a new process. +[clinic start generated code]*/ + +static PyObject * +os_posix_spawnp_impl(PyObject *module, path_t *path, PyObject *argv, + PyObject *env, PyObject *file_actions, + PyObject *setpgroup, int resetids, int setsid, + PyObject *setsigmask, PyObject *setsigdef, + PyObject *scheduler) +/*[clinic end generated code: output=7b9aaefe3031238d input=9e89e616116752a1]*/ +{ + return py_posix_spawn(1, module, path, argv, env, file_actions, + setpgroup, resetids, setsid, setsigmask, setsigdef, + scheduler); +} +#endif /* HAVE_POSIX_SPAWNP */ + +#ifdef HAVE_RTPSPAWN +static intptr_t +_rtp_spawn(int mode, const char *rtpFileName, const char *argv[], + const char *envp[]) +{ + RTP_ID rtpid; + int status; + pid_t res; + int async_err = 0; + + /* Set priority=100 and uStackSize=16 MiB (0x1000000) for new processes. + uStackSize=0 cannot be used, the default stack size is too small for + Python. */ + if (envp) { + rtpid = rtpSpawn(rtpFileName, argv, envp, + 100, 0x1000000, 0, VX_FP_TASK); + } + else { + rtpid = rtpSpawn(rtpFileName, argv, (const char **)environ, + 100, 0x1000000, 0, VX_FP_TASK); + } + if ((rtpid != RTP_ID_ERROR) && (mode == _P_WAIT)) { + do { + res = waitpid((pid_t)rtpid, &status, 0); + } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + + if (res < 0) + return RTP_ID_ERROR; + return ((intptr_t)status); + } + return ((intptr_t)rtpid); +} +#endif + +#if defined(HAVE_SPAWNV) || defined(HAVE_WSPAWNV) || defined(HAVE_RTPSPAWN) +/*[clinic input] +os.spawnv + + mode: int + Mode of process creation. + path: path_t + Path of executable file. + argv: object + Tuple or list of strings. + / + +Execute the program specified by path in a new process. +[clinic start generated code]*/ + +static PyObject * +os_spawnv_impl(PyObject *module, int mode, path_t *path, PyObject *argv) +/*[clinic end generated code: output=71cd037a9d96b816 input=43224242303291be]*/ +{ + EXECV_CHAR **argvlist; + int i; + Py_ssize_t argc; + intptr_t spawnval; + PyObject *(*getitem)(PyObject *, Py_ssize_t); + + /* spawnv has three arguments: (mode, path, argv), where + argv is a list or tuple of strings. */ + + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, + "spawnv() arg 2 must be a tuple or list"); + return NULL; + } + if (argc == 0) { + PyErr_SetString(PyExc_ValueError, + "spawnv() arg 2 cannot be empty"); + return NULL; + } + + argvlist = PyMem_NEW(EXECV_CHAR *, argc+1); + if (argvlist == NULL) { + return PyErr_NoMemory(); + } + for (i = 0; i < argc; i++) { + if (!fsconvert_strdup((*getitem)(argv, i), + &argvlist[i])) { + free_string_array(argvlist, i); + PyErr_SetString( + PyExc_TypeError, + "spawnv() arg 2 must contain only strings"); + return NULL; + } + if (i == 0 && !argvlist[0][0]) { + free_string_array(argvlist, i + 1); + PyErr_SetString( + PyExc_ValueError, + "spawnv() arg 2 first element cannot be empty"); + return NULL; + } + } + argvlist[argc] = NULL; + +#if !defined(HAVE_RTPSPAWN) + if (mode == _OLD_P_OVERLAY) + mode = _P_OVERLAY; +#endif + + if (PySys_Audit("os.spawn", "iOOO", mode, path->object, argv, + Py_None) < 0) { + free_string_array(argvlist, argc); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH +#ifdef HAVE_WSPAWNV + spawnval = _wspawnv(mode, path->wide, argvlist); +#elif defined(HAVE_RTPSPAWN) + spawnval = _rtp_spawn(mode, path->narrow, (const char **)argvlist, NULL); +#else + spawnval = _spawnv(mode, path->narrow, argvlist); +#endif + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + + int saved_errno = errno; + free_string_array(argvlist, argc); + + if (spawnval == -1) { + errno = saved_errno; + posix_error(); + return NULL; + } + return Py_BuildValue(_Py_PARSE_INTPTR, spawnval); +} + +/*[clinic input] +os.spawnve + + mode: int + Mode of process creation. + path: path_t + Path of executable file. + argv: object + Tuple or list of strings. + env: object + Dictionary of strings mapping to strings. + / + +Execute the program specified by path in a new process. +[clinic start generated code]*/ + +static PyObject * +os_spawnve_impl(PyObject *module, int mode, path_t *path, PyObject *argv, + PyObject *env) +/*[clinic end generated code: output=30fe85be56fe37ad input=3e40803ee7c4c586]*/ +{ + EXECV_CHAR **argvlist; + EXECV_CHAR **envlist; + PyObject *res = NULL; + Py_ssize_t argc, i, envc; + intptr_t spawnval; + PyObject *(*getitem)(PyObject *, Py_ssize_t); + Py_ssize_t lastarg = 0; + + /* spawnve has four arguments: (mode, path, argv, env), where + argv is a list or tuple of strings and env is a dictionary + like posix.environ. */ + + if (PyList_Check(argv)) { + argc = PyList_Size(argv); + getitem = PyList_GetItem; + } + else if (PyTuple_Check(argv)) { + argc = PyTuple_Size(argv); + getitem = PyTuple_GetItem; + } + else { + PyErr_SetString(PyExc_TypeError, + "spawnve() arg 2 must be a tuple or list"); + goto fail_0; + } + if (argc == 0) { + PyErr_SetString(PyExc_ValueError, + "spawnve() arg 2 cannot be empty"); + goto fail_0; + } + if (!PyMapping_Check(env)) { + PyErr_SetString(PyExc_TypeError, + "spawnve() arg 3 must be a mapping object"); + goto fail_0; + } + + argvlist = PyMem_NEW(EXECV_CHAR *, argc+1); + if (argvlist == NULL) { + PyErr_NoMemory(); + goto fail_0; + } + for (i = 0; i < argc; i++) { + if (!fsconvert_strdup((*getitem)(argv, i), + &argvlist[i])) + { + lastarg = i; + goto fail_1; + } + if (i == 0 && !argvlist[0][0]) { + lastarg = i + 1; + PyErr_SetString( + PyExc_ValueError, + "spawnv() arg 2 first element cannot be empty"); + goto fail_1; + } + } + lastarg = argc; + argvlist[argc] = NULL; + + envlist = parse_envlist(env, &envc); + if (envlist == NULL) + goto fail_1; + +#if !defined(HAVE_RTPSPAWN) + if (mode == _OLD_P_OVERLAY) + mode = _P_OVERLAY; +#endif + + if (PySys_Audit("os.spawn", "iOOO", mode, path->object, argv, env) < 0) { + goto fail_2; + } + + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH +#ifdef HAVE_WSPAWNV + spawnval = _wspawnve(mode, path->wide, argvlist, envlist); +#elif defined(HAVE_RTPSPAWN) + spawnval = _rtp_spawn(mode, path->narrow, (const char **)argvlist, + (const char **)envlist); +#else + spawnval = _spawnve(mode, path->narrow, argvlist, envlist); +#endif + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + + if (spawnval == -1) + (void) posix_error(); + else + res = Py_BuildValue(_Py_PARSE_INTPTR, spawnval); + + fail_2: + while (--envc >= 0) { + PyMem_Free(envlist[envc]); + } + PyMem_Free(envlist); + fail_1: + free_string_array(argvlist, lastarg); + fail_0: + return res; +} + +#endif /* HAVE_SPAWNV */ + +#ifdef HAVE_FORK + +/* Helper function to validate arguments. + Returns 0 on success. non-zero on failure with a TypeError raised. + If obj is non-NULL it must be callable. */ +static int +check_null_or_callable(PyObject *obj, const char* obj_name) +{ + if (obj && !PyCallable_Check(obj)) { + PyErr_Format(PyExc_TypeError, "'%s' must be callable, not %s", + obj_name, _PyType_Name(Py_TYPE(obj))); + return -1; + } + return 0; +} + +/*[clinic input] +os.register_at_fork + + * + before: object=NULL + A callable to be called in the parent before the fork() syscall. + after_in_child: object=NULL + A callable to be called in the child after fork(). + after_in_parent: object=NULL + A callable to be called in the parent after fork(). + +Register callables to be called when forking a new process. + +'before' callbacks are called in reverse order. +'after_in_child' and 'after_in_parent' callbacks are called in order. + +[clinic start generated code]*/ + +static PyObject * +os_register_at_fork_impl(PyObject *module, PyObject *before, + PyObject *after_in_child, PyObject *after_in_parent) +/*[clinic end generated code: output=5398ac75e8e97625 input=cd1187aa85d2312e]*/ +{ + PyInterpreterState *interp; + + if (!before && !after_in_child && !after_in_parent) { + PyErr_SetString(PyExc_TypeError, "At least one argument is required."); + return NULL; + } + if (check_null_or_callable(before, "before") || + check_null_or_callable(after_in_child, "after_in_child") || + check_null_or_callable(after_in_parent, "after_in_parent")) { + return NULL; + } + interp = _PyInterpreterState_GET(); + + if (register_at_forker(&interp->before_forkers, before)) { + return NULL; + } + if (register_at_forker(&interp->after_forkers_child, after_in_child)) { + return NULL; + } + if (register_at_forker(&interp->after_forkers_parent, after_in_parent)) { + return NULL; + } + Py_RETURN_NONE; +} +#endif /* HAVE_FORK */ + +#if defined(HAVE_FORK1) || defined(HAVE_FORKPTY) || defined(HAVE_FORK) +// Common code to raise a warning if we detect there is more than one thread +// running in the process. Best effort, silent if unable to count threads. +// Constraint: Quick. Never overcounts. Never leaves an error set. +// +// This should only be called from the parent process after +// PyOS_AfterFork_Parent(). +static void +warn_about_fork_with_threads(const char* name) +{ + // It's not safe to issue the warning while the world is stopped, because + // other threads might be holding locks that we need, which would deadlock. + assert(!_PyRuntime.stoptheworld.world_stopped); + + // TODO: Consider making an `os` module API to return the current number + // of threads in the process. That'd presumably use this platform code but + // raise an error rather than using the inaccurate fallback. + Py_ssize_t num_python_threads = 0; +#if defined(__APPLE__) && defined(HAVE_GETPID) + mach_port_t macos_self = mach_task_self(); + mach_port_t macos_task; + if (task_for_pid(macos_self, getpid(), &macos_task) == KERN_SUCCESS) { + thread_array_t macos_threads; + mach_msg_type_number_t macos_n_threads; + if (task_threads(macos_task, &macos_threads, + &macos_n_threads) == KERN_SUCCESS) { + num_python_threads = macos_n_threads; + } + } +#elif defined(__linux__) + // Linux /proc/self/stat 20th field is the number of threads. + FILE* proc_stat = fopen("/proc/self/stat", "r"); + if (proc_stat) { + size_t n; + // Size chosen arbitrarily. ~60% more bytes than a 20th column index + // observed on the author's workstation. + char stat_line[160]; + n = fread(&stat_line, 1, 159, proc_stat); + stat_line[n] = '\0'; + fclose(proc_stat); + + char *saveptr = NULL; + char *field = strtok_r(stat_line, " ", &saveptr); + unsigned int idx; + for (idx = 19; idx && field; --idx) { + field = strtok_r(NULL, " ", &saveptr); + } + if (idx == 0 && field) { // found the 20th field + num_python_threads = atoi(field); // 0 on error + } + } +#endif + if (num_python_threads <= 0) { + // Fall back to just the number our threading module knows about. + // An incomplete view of the world, but better than nothing. + PyObject *threading = PyImport_GetModule(&_Py_ID(threading)); + if (!threading) { + PyErr_Clear(); + return; + } + PyObject *threading_active = + PyObject_GetAttr(threading, &_Py_ID(_active)); + if (!threading_active) { + PyErr_Clear(); + Py_DECREF(threading); + return; + } + PyObject *threading_limbo = + PyObject_GetAttr(threading, &_Py_ID(_limbo)); + if (!threading_limbo) { + PyErr_Clear(); + Py_DECREF(threading); + Py_DECREF(threading_active); + return; + } + Py_DECREF(threading); + // Duplicating what threading.active_count() does but without holding + // threading._active_limbo_lock so our count could be inaccurate if + // these dicts are mid-update from another thread. Not a big deal. + // Worst case if someone replaced threading._active or threading._limbo + // with non-dicts, we get -1 from *Length() below and undercount. + // Nobody should, but we're best effort so we clear errors and move on. + num_python_threads = (PyMapping_Length(threading_active) + + PyMapping_Length(threading_limbo)); + PyErr_Clear(); + Py_DECREF(threading_active); + Py_DECREF(threading_limbo); + } + if (num_python_threads > 1) { + PyErr_WarnFormat( + PyExc_DeprecationWarning, 1, +#ifdef HAVE_GETPID + "This process (pid=%d) is multi-threaded, " +#else + "This process is multi-threaded, " +#endif + "use of %s() may lead to deadlocks in the child.", +#ifdef HAVE_GETPID + getpid(), +#endif + name); + PyErr_Clear(); + } +} +#endif // HAVE_FORK1 || HAVE_FORKPTY || HAVE_FORK + +#ifdef HAVE_FORK1 +/*[clinic input] +os.fork1 + +Fork a child process with a single multiplexed (i.e., not bound) thread. + +Return 0 to child process and PID of child to parent process. +[clinic start generated code]*/ + +static PyObject * +os_fork1_impl(PyObject *module) +/*[clinic end generated code: output=0de8e67ce2a310bc input=12db02167893926e]*/ +{ + pid_t pid; + + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (_PyInterpreterState_GetFinalizing(interp) != NULL) { + PyErr_SetString(PyExc_PythonFinalizationError, + "can't fork at interpreter shutdown"); + return NULL; + } + if (!_Py_IsMainInterpreter(interp)) { + PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters"); + return NULL; + } + PyOS_BeforeFork(); + pid = fork1(); + int saved_errno = errno; + if (pid == 0) { + /* child: this clobbers and resets the import lock. */ + PyOS_AfterFork_Child(); + } else { + /* parent: release the import lock. */ + PyOS_AfterFork_Parent(); + // After PyOS_AfterFork_Parent() starts the world to avoid deadlock. + warn_about_fork_with_threads("fork1"); + } + if (pid == -1) { + errno = saved_errno; + return posix_error(); + } + return PyLong_FromPid(pid); +} +#endif /* HAVE_FORK1 */ + + +#ifdef HAVE_FORK +/*[clinic input] +os.fork + +Fork a child process. + +Return 0 to child process and PID of child to parent process. +[clinic start generated code]*/ + +static PyObject * +os_fork_impl(PyObject *module) +/*[clinic end generated code: output=3626c81f98985d49 input=13c956413110eeaa]*/ +{ + pid_t pid; + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (_PyInterpreterState_GetFinalizing(interp) != NULL) { + PyErr_SetString(PyExc_PythonFinalizationError, + "can't fork at interpreter shutdown"); + return NULL; + } + if (!_PyInterpreterState_HasFeature(interp, Py_RTFLAGS_FORK)) { + PyErr_SetString(PyExc_RuntimeError, + "fork not supported for isolated subinterpreters"); + return NULL; + } + if (PySys_Audit("os.fork", NULL) < 0) { + return NULL; + } + PyOS_BeforeFork(); + pid = fork(); + int saved_errno = errno; + if (pid == 0) { + /* child: this clobbers and resets the import lock. */ + PyOS_AfterFork_Child(); + } else { + /* parent: release the import lock. */ + PyOS_AfterFork_Parent(); + // After PyOS_AfterFork_Parent() starts the world to avoid deadlock. + warn_about_fork_with_threads("fork"); + } + if (pid == -1) { + errno = saved_errno; + return posix_error(); + } + return PyLong_FromPid(pid); +} +#endif /* HAVE_FORK */ + + +#ifdef HAVE_SCHED_H +#ifdef HAVE_SCHED_GET_PRIORITY_MAX +/*[clinic input] +os.sched_get_priority_max + + policy: int + +Get the maximum scheduling priority for policy. +[clinic start generated code]*/ + +static PyObject * +os_sched_get_priority_max_impl(PyObject *module, int policy) +/*[clinic end generated code: output=9e465c6e43130521 input=2097b7998eca6874]*/ +{ + int max; + + max = sched_get_priority_max(policy); + if (max < 0) + return posix_error(); + return PyLong_FromLong(max); +} + + +/*[clinic input] +os.sched_get_priority_min + + policy: int + +Get the minimum scheduling priority for policy. +[clinic start generated code]*/ + +static PyObject * +os_sched_get_priority_min_impl(PyObject *module, int policy) +/*[clinic end generated code: output=7595c1138cc47a6d input=21bc8fa0d70983bf]*/ +{ + int min = sched_get_priority_min(policy); + if (min < 0) + return posix_error(); + return PyLong_FromLong(min); +} +#endif /* HAVE_SCHED_GET_PRIORITY_MAX */ + + +#ifdef HAVE_SCHED_SETSCHEDULER +/*[clinic input] +os.sched_getscheduler + pid: pid_t + / + +Get the scheduling policy for the process identified by pid. + +Passing 0 for pid returns the scheduling policy for the calling process. +[clinic start generated code]*/ + +static PyObject * +os_sched_getscheduler_impl(PyObject *module, pid_t pid) +/*[clinic end generated code: output=dce4c0bd3f1b34c8 input=8d99dac505485ac8]*/ +{ + int policy; + + policy = sched_getscheduler(pid); + if (policy < 0) + return posix_error(); + return PyLong_FromLong(policy); +} +#endif /* HAVE_SCHED_SETSCHEDULER */ + + +#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) +/*[clinic input] +class os.sched_param "PyObject *" "SchedParamType" + +@classmethod +os.sched_param.__new__ + + sched_priority: object + A scheduling parameter. + +Currently has only one field: sched_priority +[clinic start generated code]*/ + +static PyObject * +os_sched_param_impl(PyTypeObject *type, PyObject *sched_priority) +/*[clinic end generated code: output=48f4067d60f48c13 input=eb42909a2c0e3e6c]*/ +{ + PyObject *res; + + res = PyStructSequence_New(type); + if (!res) + return NULL; + PyStructSequence_SET_ITEM(res, 0, Py_NewRef(sched_priority)); + return res; +} + +static PyObject * +os_sched_param_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return Py_BuildValue("(O(N))", Py_TYPE(self), PyStructSequence_GetItem(self, 0)); +} + +static PyMethodDef os_sched_param_reduce_method = { + "__reduce__", (PyCFunction)os_sched_param_reduce, METH_NOARGS|METH_COEXIST, NULL, +}; + +PyDoc_VAR(os_sched_param__doc__); + +static PyStructSequence_Field sched_param_fields[] = { + {"sched_priority", "the scheduling priority"}, + {0} +}; + +static PyStructSequence_Desc sched_param_desc = { + "sched_param", /* name */ + os_sched_param__doc__, /* doc */ + sched_param_fields, + 1 +}; + +static int +convert_sched_param(PyObject *module, PyObject *param, struct sched_param *res) +{ + long priority; + + if (!Py_IS_TYPE(param, (PyTypeObject *)get_posix_state(module)->SchedParamType)) { + PyErr_SetString(PyExc_TypeError, "must have a sched_param object"); + return 0; + } + priority = PyLong_AsLong(PyStructSequence_GET_ITEM(param, 0)); + if (priority == -1 && PyErr_Occurred()) + return 0; + if (priority > INT_MAX || priority < INT_MIN) { + PyErr_SetString(PyExc_OverflowError, "sched_priority out of range"); + return 0; + } + res->sched_priority = Py_SAFE_DOWNCAST(priority, long, int); + return 1; +} +#endif /* defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) */ + + +#ifdef HAVE_SCHED_SETSCHEDULER +/*[clinic input] +os.sched_setscheduler + + pid: pid_t + policy: int + param as param_obj: object + / + +Set the scheduling policy for the process identified by pid. + +If pid is 0, the calling process is changed. +param is an instance of sched_param. +[clinic start generated code]*/ + +static PyObject * +os_sched_setscheduler_impl(PyObject *module, pid_t pid, int policy, + PyObject *param_obj) +/*[clinic end generated code: output=cde27faa55dc993e input=73013d731bd8fbe9]*/ +{ + struct sched_param param; + if (!convert_sched_param(module, param_obj, ¶m)) { + return NULL; + } + + /* + ** sched_setscheduler() returns 0 in Linux, but the previous + ** scheduling policy under Solaris/Illumos, and others. + ** On error, -1 is returned in all Operating Systems. + */ + if (sched_setscheduler(pid, policy, ¶m) == -1) + return posix_error(); + Py_RETURN_NONE; +} +#endif /* HAVE_SCHED_SETSCHEDULER*/ + + +#ifdef HAVE_SCHED_SETPARAM +/*[clinic input] +os.sched_getparam + pid: pid_t + / + +Returns scheduling parameters for the process identified by pid. + +If pid is 0, returns parameters for the calling process. +Return value is an instance of sched_param. +[clinic start generated code]*/ + +static PyObject * +os_sched_getparam_impl(PyObject *module, pid_t pid) +/*[clinic end generated code: output=b194e8708dcf2db8 input=18a1ef9c2efae296]*/ +{ + struct sched_param param; + PyObject *result; + PyObject *priority; + + if (sched_getparam(pid, ¶m)) + return posix_error(); + PyObject *SchedParamType = get_posix_state(module)->SchedParamType; + result = PyStructSequence_New((PyTypeObject *)SchedParamType); + if (!result) + return NULL; + priority = PyLong_FromLong(param.sched_priority); + if (!priority) { + Py_DECREF(result); + return NULL; + } + PyStructSequence_SET_ITEM(result, 0, priority); + return result; +} + + +/*[clinic input] +os.sched_setparam + pid: pid_t + param as param_obj: object + / + +Set scheduling parameters for the process identified by pid. + +If pid is 0, sets parameters for the calling process. +param should be an instance of sched_param. +[clinic start generated code]*/ + +static PyObject * +os_sched_setparam_impl(PyObject *module, pid_t pid, PyObject *param_obj) +/*[clinic end generated code: output=f19fe020a53741c1 input=27b98337c8b2dcc7]*/ +{ + struct sched_param param; + if (!convert_sched_param(module, param_obj, ¶m)) { + return NULL; + } + + if (sched_setparam(pid, ¶m)) + return posix_error(); + Py_RETURN_NONE; +} +#endif /* HAVE_SCHED_SETPARAM */ + + +#ifdef HAVE_SCHED_RR_GET_INTERVAL +/*[clinic input] +os.sched_rr_get_interval -> double + pid: pid_t + / + +Return the round-robin quantum for the process identified by pid, in seconds. + +Value returned is a float. +[clinic start generated code]*/ + +static double +os_sched_rr_get_interval_impl(PyObject *module, pid_t pid) +/*[clinic end generated code: output=7e2d935833ab47dc input=2a973da15cca6fae]*/ +{ + struct timespec interval; + if (sched_rr_get_interval(pid, &interval)) { + posix_error(); + return -1.0; + } +#ifdef _Py_MEMORY_SANITIZER + __msan_unpoison(&interval, sizeof(interval)); +#endif + return (double)interval.tv_sec + 1e-9*interval.tv_nsec; +} +#endif /* HAVE_SCHED_RR_GET_INTERVAL */ + + +/*[clinic input] +os.sched_yield + +Voluntarily relinquish the CPU. +[clinic start generated code]*/ + +static PyObject * +os_sched_yield_impl(PyObject *module) +/*[clinic end generated code: output=902323500f222cac input=e54d6f98189391d4]*/ +{ + int result; + Py_BEGIN_ALLOW_THREADS + result = sched_yield(); + Py_END_ALLOW_THREADS + if (result < 0) { + return posix_error(); + } + Py_RETURN_NONE; +} + +#ifdef HAVE_SCHED_SETAFFINITY +/* The minimum number of CPUs allocated in a cpu_set_t */ +static const int NCPUS_START = sizeof(unsigned long) * CHAR_BIT; + +/*[clinic input] +os.sched_setaffinity + pid: pid_t + mask : object + / + +Set the CPU affinity of the process identified by pid to mask. + +mask should be an iterable of integers identifying CPUs. +[clinic start generated code]*/ + +static PyObject * +os_sched_setaffinity_impl(PyObject *module, pid_t pid, PyObject *mask) +/*[clinic end generated code: output=882d7dd9a229335b input=a0791a597c7085ba]*/ +{ + int ncpus; + size_t setsize; + cpu_set_t *cpu_set = NULL; + PyObject *iterator = NULL, *item; + + iterator = PyObject_GetIter(mask); + if (iterator == NULL) + return NULL; + + ncpus = NCPUS_START; + setsize = CPU_ALLOC_SIZE(ncpus); + cpu_set = CPU_ALLOC(ncpus); + if (cpu_set == NULL) { + PyErr_NoMemory(); + goto error; + } + CPU_ZERO_S(setsize, cpu_set); + + while ((item = PyIter_Next(iterator))) { + long cpu; + if (!PyLong_Check(item)) { + PyErr_Format(PyExc_TypeError, + "expected an iterator of ints, " + "but iterator yielded %R", + Py_TYPE(item)); + Py_DECREF(item); + goto error; + } + cpu = PyLong_AsLong(item); + Py_DECREF(item); + if (cpu < 0) { + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_ValueError, "negative CPU number"); + goto error; + } + if (cpu > INT_MAX - 1) { + PyErr_SetString(PyExc_OverflowError, "CPU number too large"); + goto error; + } + if (cpu >= ncpus) { + /* Grow CPU mask to fit the CPU number */ + int newncpus = ncpus; + cpu_set_t *newmask; + size_t newsetsize; + while (newncpus <= cpu) { + if (newncpus > INT_MAX / 2) + newncpus = cpu + 1; + else + newncpus = newncpus * 2; + } + newmask = CPU_ALLOC(newncpus); + if (newmask == NULL) { + PyErr_NoMemory(); + goto error; + } + newsetsize = CPU_ALLOC_SIZE(newncpus); + CPU_ZERO_S(newsetsize, newmask); + memcpy(newmask, cpu_set, setsize); + CPU_FREE(cpu_set); + setsize = newsetsize; + cpu_set = newmask; + ncpus = newncpus; + } + CPU_SET_S(cpu, setsize, cpu_set); + } + if (PyErr_Occurred()) { + goto error; + } + Py_CLEAR(iterator); + + if (sched_setaffinity(pid, setsize, cpu_set)) { + posix_error(); + goto error; + } + CPU_FREE(cpu_set); + Py_RETURN_NONE; + +error: + if (cpu_set) + CPU_FREE(cpu_set); + Py_XDECREF(iterator); + return NULL; +} + + +/*[clinic input] +os.sched_getaffinity + pid: pid_t + / + +Return the affinity of the process identified by pid (or the current process if zero). + +The affinity is returned as a set of CPU identifiers. +[clinic start generated code]*/ + +static PyObject * +os_sched_getaffinity_impl(PyObject *module, pid_t pid) +/*[clinic end generated code: output=f726f2c193c17a4f input=983ce7cb4a565980]*/ +{ + int ncpus = NCPUS_START; + size_t setsize; + cpu_set_t *mask; + + while (1) { + setsize = CPU_ALLOC_SIZE(ncpus); + mask = CPU_ALLOC(ncpus); + if (mask == NULL) { + return PyErr_NoMemory(); + } + if (sched_getaffinity(pid, setsize, mask) == 0) { + break; + } + CPU_FREE(mask); + if (errno != EINVAL) { + return posix_error(); + } + if (ncpus > INT_MAX / 2) { + PyErr_SetString(PyExc_OverflowError, + "could not allocate a large enough CPU set"); + return NULL; + } + ncpus *= 2; + } + + PyObject *res = PySet_New(NULL); + if (res == NULL) { + goto error; + } + + int cpu = 0; + int count = CPU_COUNT_S(setsize, mask); + for (; count; cpu++) { + if (CPU_ISSET_S(cpu, setsize, mask)) { + PyObject *cpu_num = PyLong_FromLong(cpu); + --count; + if (cpu_num == NULL) { + goto error; + } + if (PySet_Add(res, cpu_num)) { + Py_DECREF(cpu_num); + goto error; + } + Py_DECREF(cpu_num); + } + } + CPU_FREE(mask); + return res; + +error: + if (mask) { + CPU_FREE(mask); + } + Py_XDECREF(res); + return NULL; +} +#endif /* HAVE_SCHED_SETAFFINITY */ + +#endif /* HAVE_SCHED_H */ + + +#ifdef HAVE_POSIX_OPENPT +/*[clinic input] +os.posix_openpt -> int + + oflag: int + / + +Open and return a file descriptor for a master pseudo-terminal device. + +Performs a posix_openpt() C function call. The oflag argument is used to +set file status flags and file access modes as specified in the manual page +of posix_openpt() of your system. +[clinic start generated code]*/ + +static int +os_posix_openpt_impl(PyObject *module, int oflag) +/*[clinic end generated code: output=ee0bc2624305fc79 input=0de33d0e29693caa]*/ +{ + int fd; + +#if defined(O_CLOEXEC) + oflag |= O_CLOEXEC; +#endif + + fd = posix_openpt(oflag); + if (fd == -1) { + posix_error(); + return -1; + } + + // Just in case, likely a no-op given O_CLOEXEC above. + if (_Py_set_inheritable(fd, 0, NULL) < 0) { + close(fd); + return -1; + } + + return fd; +} +#endif /* HAVE_POSIX_OPENPT */ + +#ifdef HAVE_GRANTPT +/*[clinic input] +os.grantpt + + fd: fildes + File descriptor of a master pseudo-terminal device. + / + +Grant access to the slave pseudo-terminal device. + +Performs a grantpt() C function call. +[clinic start generated code]*/ + +static PyObject * +os_grantpt_impl(PyObject *module, int fd) +/*[clinic end generated code: output=dfd580015cf548ab input=0668e3b96760e849]*/ +{ + int ret; + int saved_errno; + PyOS_sighandler_t sig_saved; + + sig_saved = PyOS_setsig(SIGCHLD, SIG_DFL); + + ret = grantpt(fd); + if (ret == -1) + saved_errno = errno; + + PyOS_setsig(SIGCHLD, sig_saved); + + if (ret == -1) { + errno = saved_errno; + return posix_error(); + } + + Py_RETURN_NONE; +} +#endif /* HAVE_GRANTPT */ + +#ifdef HAVE_UNLOCKPT +/*[clinic input] +os.unlockpt + + fd: fildes + File descriptor of a master pseudo-terminal device. + / + +Unlock a pseudo-terminal master/slave pair. + +Performs an unlockpt() C function call. +[clinic start generated code]*/ + +static PyObject * +os_unlockpt_impl(PyObject *module, int fd) +/*[clinic end generated code: output=e08d354dec12d30c input=de7ab1f59f69a2b4]*/ +{ + if (unlockpt(fd) == -1) + return posix_error(); + + Py_RETURN_NONE; +} +#endif /* HAVE_UNLOCKPT */ + +#if defined(HAVE_PTSNAME) || defined(HAVE_PTSNAME_R) +static PyObject * +py_ptsname(int fd) +{ + // POSIX manpage: Upon failure, ptsname() shall return a null pointer + // and may set errno. Always initialize errno to avoid undefined behavior. + errno = 0; + char *name = ptsname(fd); + if (name == NULL) { + return posix_error(); + } + return PyUnicode_DecodeFSDefault(name); +} + +/*[clinic input] +os.ptsname + + fd: fildes + File descriptor of a master pseudo-terminal device. + / + +Return the name of the slave pseudo-terminal device. + +If the ptsname_r() C function is available, it is called; +otherwise, performs a ptsname() C function call. +[clinic start generated code]*/ + +static PyObject * +os_ptsname_impl(PyObject *module, int fd) +/*[clinic end generated code: output=ef300fadc5675872 input=1369ccc0546f3130]*/ +{ +#ifdef HAVE_PTSNAME_R + int ret; + char name[MAXPATHLEN+1]; + + if (HAVE_PTSNAME_R_RUNTIME) { + ret = ptsname_r(fd, name, sizeof(name)); + } + else { + // fallback to ptsname() if ptsname_r() is not available in runtime. + return py_ptsname(fd); + } + if (ret != 0) { + errno = ret; + return posix_error(); + } + + return PyUnicode_DecodeFSDefault(name); +#else + return py_ptsname(fd); +#endif /* HAVE_PTSNAME_R */ +} +#endif /* defined(HAVE_PTSNAME) || defined(HAVE_PTSNAME_R) */ + +/* AIX uses /dev/ptc but is otherwise the same as /dev/ptmx */ +#if defined(HAVE_DEV_PTC) && !defined(HAVE_DEV_PTMX) +# define DEV_PTY_FILE "/dev/ptc" +# define HAVE_DEV_PTMX +#else +# define DEV_PTY_FILE "/dev/ptmx" +#endif + +#if defined(HAVE_OPENPTY) || defined(HAVE_FORKPTY) || defined(HAVE_LOGIN_TTY) || defined(HAVE_DEV_PTMX) +#ifdef HAVE_PTY_H +#include +#ifdef HAVE_UTMP_H +#include +#endif /* HAVE_UTMP_H */ +#elif defined(HAVE_LIBUTIL_H) +#include +#elif defined(HAVE_UTIL_H) +#include +#endif /* HAVE_PTY_H */ +#ifdef HAVE_STROPTS_H +#include +#endif +#endif /* defined(HAVE_OPENPTY) || defined(HAVE_FORKPTY) || defined(HAVE_LOGIN_TTY) || defined(HAVE_DEV_PTMX) */ + + +#if defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) +/*[clinic input] +os.openpty + +Open a pseudo-terminal. + +Return a tuple of (master_fd, slave_fd) containing open file descriptors +for both the master and slave ends. +[clinic start generated code]*/ + +static PyObject * +os_openpty_impl(PyObject *module) +/*[clinic end generated code: output=98841ce5ec9cef3c input=f3d99fd99e762907]*/ +{ + int master_fd = -1, slave_fd = -1; +#ifndef HAVE_OPENPTY + char * slave_name; +#endif +#if defined(HAVE_DEV_PTMX) && !defined(HAVE_OPENPTY) && !defined(HAVE__GETPTY) + PyOS_sighandler_t sig_saved; +#if defined(__sun) && defined(__SVR4) + extern char *ptsname(int fildes); +#endif +#endif + +#ifdef HAVE_OPENPTY + if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) != 0) + goto posix_error; + + if (_Py_set_inheritable(master_fd, 0, NULL) < 0) + goto error; + if (_Py_set_inheritable(slave_fd, 0, NULL) < 0) + goto error; + +#elif defined(HAVE__GETPTY) + slave_name = _getpty(&master_fd, O_RDWR, 0666, 0); + if (slave_name == NULL) + goto posix_error; + if (_Py_set_inheritable(master_fd, 0, NULL) < 0) + goto error; + + slave_fd = _Py_open(slave_name, O_RDWR); + if (slave_fd < 0) + goto error; + +#else + master_fd = open(DEV_PTY_FILE, O_RDWR | O_NOCTTY); /* open master */ + if (master_fd < 0) + goto posix_error; + + sig_saved = PyOS_setsig(SIGCHLD, SIG_DFL); + + /* change permission of slave */ + if (grantpt(master_fd) < 0) { + int saved_errno = errno; + PyOS_setsig(SIGCHLD, sig_saved); + errno = saved_errno; + goto posix_error; + } + + /* unlock slave */ + if (unlockpt(master_fd) < 0) { + int saved_errno = errno; + PyOS_setsig(SIGCHLD, sig_saved); + errno = saved_errno; + goto posix_error; + } + + PyOS_setsig(SIGCHLD, sig_saved); + + slave_name = ptsname(master_fd); /* get name of slave */ + if (slave_name == NULL) + goto posix_error; + + slave_fd = _Py_open(slave_name, O_RDWR | O_NOCTTY); /* open slave */ + if (slave_fd == -1) + goto error; + + if (_Py_set_inheritable(master_fd, 0, NULL) < 0) + goto posix_error; + +#if !defined(__CYGWIN__) && !defined(__ANDROID__) && !defined(HAVE_DEV_PTC) + ioctl(slave_fd, I_PUSH, "ptem"); /* push ptem */ + ioctl(slave_fd, I_PUSH, "ldterm"); /* push ldterm */ +#ifndef __hpux + ioctl(slave_fd, I_PUSH, "ttcompat"); /* push ttcompat */ +#endif /* __hpux */ +#endif /* HAVE_CYGWIN */ +#endif /* HAVE_OPENPTY */ + + return Py_BuildValue("(ii)", master_fd, slave_fd); + +posix_error: + posix_error(); +error: + if (master_fd != -1) + close(master_fd); + if (slave_fd != -1) + close(slave_fd); + return NULL; +} +#endif /* defined(HAVE_OPENPTY) || defined(HAVE__GETPTY) || defined(HAVE_DEV_PTMX) */ + + +#if defined(HAVE_SETSID) && defined(TIOCSCTTY) +#define HAVE_FALLBACK_LOGIN_TTY 1 +#endif /* defined(HAVE_SETSID) && defined(TIOCSCTTY) */ + +#if defined(HAVE_LOGIN_TTY) || defined(HAVE_FALLBACK_LOGIN_TTY) +/*[clinic input] +os.login_tty + + fd: fildes + / + +Prepare the tty of which fd is a file descriptor for a new login session. + +Make the calling process a session leader; make the tty the +controlling tty, the stdin, the stdout, and the stderr of the +calling process; close fd. +[clinic start generated code]*/ + +static PyObject * +os_login_tty_impl(PyObject *module, int fd) +/*[clinic end generated code: output=495a79911b4cc1bc input=5f298565099903a2]*/ +{ +#ifdef HAVE_LOGIN_TTY + if (login_tty(fd) == -1) { + return posix_error(); + } +#else /* defined(HAVE_FALLBACK_LOGIN_TTY) */ + /* Establish a new session. */ + if (setsid() == -1) { + return posix_error(); + } + + /* The tty becomes the controlling terminal. */ + if (ioctl(fd, TIOCSCTTY, (char *)NULL) == -1) { + return posix_error(); + } + + /* The tty becomes stdin/stdout/stderr */ + if (dup2(fd, 0) == -1 || dup2(fd, 1) == -1 || dup2(fd, 2) == -1) { + return posix_error(); + } + if (fd > 2) { + close(fd); + } +#endif /* HAVE_LOGIN_TTY */ + Py_RETURN_NONE; +} +#endif /* defined(HAVE_LOGIN_TTY) || defined(HAVE_FALLBACK_LOGIN_TTY) */ + + +#ifdef HAVE_FORKPTY +/*[clinic input] +os.forkpty + +Fork a new process with a new pseudo-terminal as controlling tty. + +Returns a tuple of (pid, master_fd). +Like fork(), return pid of 0 to the child process, +and pid of child to the parent process. +To both, return fd of newly opened pseudo-terminal. +[clinic start generated code]*/ + +static PyObject * +os_forkpty_impl(PyObject *module) +/*[clinic end generated code: output=60d0a5c7512e4087 input=f1f7f4bae3966010]*/ +{ + int master_fd = -1; + pid_t pid; + + PyInterpreterState *interp = _PyInterpreterState_GET(); + if (_PyInterpreterState_GetFinalizing(interp) != NULL) { + PyErr_SetString(PyExc_PythonFinalizationError, + "can't fork at interpreter shutdown"); + return NULL; + } + if (!_Py_IsMainInterpreter(interp)) { + PyErr_SetString(PyExc_RuntimeError, "fork not supported for subinterpreters"); + return NULL; + } + if (PySys_Audit("os.forkpty", NULL) < 0) { + return NULL; + } + PyOS_BeforeFork(); + pid = forkpty(&master_fd, NULL, NULL, NULL); + if (pid == 0) { + /* child: this clobbers and resets the import lock. */ + PyOS_AfterFork_Child(); + } else { + /* parent: release the import lock. */ + PyOS_AfterFork_Parent(); + // After PyOS_AfterFork_Parent() starts the world to avoid deadlock. + warn_about_fork_with_threads("forkpty"); + } + if (pid == -1) { + return posix_error(); + } + return Py_BuildValue("(Ni)", PyLong_FromPid(pid), master_fd); +} +#endif /* HAVE_FORKPTY */ + + +#ifdef HAVE_GETEGID +/*[clinic input] +os.getegid + +Return the current process's effective group id. +[clinic start generated code]*/ + +static PyObject * +os_getegid_impl(PyObject *module) +/*[clinic end generated code: output=67d9be7ac68898a2 input=1596f79ad1107d5d]*/ +{ + return _PyLong_FromGid(getegid()); +} +#endif /* HAVE_GETEGID */ + + +#ifdef HAVE_GETEUID +/*[clinic input] +os.geteuid + +Return the current process's effective user id. +[clinic start generated code]*/ + +static PyObject * +os_geteuid_impl(PyObject *module) +/*[clinic end generated code: output=ea1b60f0d6abb66e input=4644c662d3bd9f19]*/ +{ + return _PyLong_FromUid(geteuid()); +} +#endif /* HAVE_GETEUID */ + + +#ifdef HAVE_GETGID +/*[clinic input] +os.getgid + +Return the current process's group id. +[clinic start generated code]*/ + +static PyObject * +os_getgid_impl(PyObject *module) +/*[clinic end generated code: output=4f28ebc9d3e5dfcf input=58796344cd87c0f6]*/ +{ + return _PyLong_FromGid(getgid()); +} +#endif /* HAVE_GETGID */ + + +#if defined(HAVE_GETPID) +/*[clinic input] +os.getpid + +Return the current process id. +[clinic start generated code]*/ + +static PyObject * +os_getpid_impl(PyObject *module) +/*[clinic end generated code: output=9ea6fdac01ed2b3c input=5a9a00f0ab68aa00]*/ +{ +#if !defined(MS_WINDOWS) || defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) + return PyLong_FromPid(getpid()); +#else + return PyLong_FromUnsignedLong(GetCurrentProcessId()); +#endif +} +#endif /* defined(HAVE_GETPID) */ + +#ifdef NGROUPS_MAX +#define MAX_GROUPS NGROUPS_MAX +#else + /* defined to be 16 on Solaris7, so this should be a small number */ +#define MAX_GROUPS 64 +#endif + +#ifdef HAVE_GETGROUPLIST + +#ifdef __APPLE__ +/*[clinic input] +os.getgrouplist + + user: str + username to lookup + group as basegid: int + base group id of the user + / + +Returns a list of groups to which a user belongs. +[clinic start generated code]*/ + +static PyObject * +os_getgrouplist_impl(PyObject *module, const char *user, int basegid) +/*[clinic end generated code: output=6e734697b8c26de0 input=f8d870374b09a490]*/ +#else +/*[clinic input] +os.getgrouplist + + user: str + username to lookup + group as basegid: gid_t + base group id of the user + / + +Returns a list of groups to which a user belongs. +[clinic start generated code]*/ + +static PyObject * +os_getgrouplist_impl(PyObject *module, const char *user, gid_t basegid) +/*[clinic end generated code: output=0ebd7fb70115575b input=cc61d5c20b08958d]*/ +#endif +{ + int i, ngroups; + PyObject *list; +#ifdef __APPLE__ + int *groups; +#else + gid_t *groups; +#endif + + /* + * NGROUPS_MAX is defined by POSIX.1 as the maximum + * number of supplimental groups a users can belong to. + * We have to increment it by one because + * getgrouplist() returns both the supplemental groups + * and the primary group, i.e. all of the groups the + * user belongs to. + */ + ngroups = 1 + MAX_GROUPS; + + while (1) { +#ifdef __APPLE__ + groups = PyMem_New(int, ngroups); +#else + groups = PyMem_New(gid_t, ngroups); +#endif + if (groups == NULL) { + return PyErr_NoMemory(); + } + + int old_ngroups = ngroups; + if (getgrouplist(user, basegid, groups, &ngroups) != -1) { + /* Success */ + break; + } + + /* getgrouplist() fails if the group list is too small */ + PyMem_Free(groups); + + if (ngroups > old_ngroups) { + /* If the group list is too small, the glibc implementation of + getgrouplist() sets ngroups to the total number of groups and + returns -1. */ + } + else { + /* Double the group list size */ + if (ngroups > INT_MAX / 2) { + return PyErr_NoMemory(); + } + ngroups *= 2; + } + + /* Retry getgrouplist() with a larger group list */ + } + +#ifdef _Py_MEMORY_SANITIZER + /* Clang memory sanitizer libc intercepts don't know getgrouplist. */ + __msan_unpoison(&ngroups, sizeof(ngroups)); + __msan_unpoison(groups, ngroups*sizeof(*groups)); +#endif + + list = PyList_New(ngroups); + if (list == NULL) { + PyMem_Free(groups); + return NULL; + } + + for (i = 0; i < ngroups; i++) { +#ifdef __APPLE__ + PyObject *o = PyLong_FromUnsignedLong((unsigned long)groups[i]); +#else + PyObject *o = _PyLong_FromGid(groups[i]); +#endif + if (o == NULL) { + Py_DECREF(list); + PyMem_Free(groups); + return NULL; + } + PyList_SET_ITEM(list, i, o); + } + + PyMem_Free(groups); + + return list; +} +#endif /* HAVE_GETGROUPLIST */ + + +#ifdef HAVE_GETGROUPS +/*[clinic input] +os.getgroups + +Return list of supplemental group IDs for the process. +[clinic start generated code]*/ + +static PyObject * +os_getgroups_impl(PyObject *module) +/*[clinic end generated code: output=42b0c17758561b56 input=d3f109412e6a155c]*/ +{ + // Call getgroups with length 0 to get the actual number of groups + int n = getgroups(0, NULL); + if (n < 0) { + return posix_error(); + } + + if (n == 0) { + return PyList_New(0); + } + + gid_t *grouplist = PyMem_New(gid_t, n); + if (grouplist == NULL) { + return PyErr_NoMemory(); + } + + n = getgroups(n, grouplist); + if (n == -1) { + posix_error(); + PyMem_Free(grouplist); + return NULL; + } + + PyObject *result = PyList_New(n); + if (result == NULL) { + goto error; + } + + for (int i = 0; i < n; ++i) { + PyObject *group = _PyLong_FromGid(grouplist[i]); + if (group == NULL) { + goto error; + } + PyList_SET_ITEM(result, i, group); + } + PyMem_Free(grouplist); + + return result; + +error: + PyMem_Free(grouplist); + Py_XDECREF(result); + return NULL; +} +#endif /* HAVE_GETGROUPS */ + +#ifdef HAVE_INITGROUPS +#ifdef __APPLE__ +/*[clinic input] +os.initgroups + + username as oname: FSConverter + gid: int + / + +Initialize the group access list. + +Call the system initgroups() to initialize the group access list with all of +the groups of which the specified username is a member, plus the specified +group id. +[clinic start generated code]*/ + +static PyObject * +os_initgroups_impl(PyObject *module, PyObject *oname, int gid) +/*[clinic end generated code: output=7f074d30a425fd3a input=df3d54331b0af204]*/ +#else +/*[clinic input] +os.initgroups + + username as oname: FSConverter + gid: gid_t + / + +Initialize the group access list. + +Call the system initgroups() to initialize the group access list with all of +the groups of which the specified username is a member, plus the specified +group id. +[clinic start generated code]*/ + +static PyObject * +os_initgroups_impl(PyObject *module, PyObject *oname, gid_t gid) +/*[clinic end generated code: output=59341244521a9e3f input=0cb91bdc59a4c564]*/ +#endif +{ + const char *username = PyBytes_AS_STRING(oname); + + if (initgroups(username, gid) == -1) + return PyErr_SetFromErrno(PyExc_OSError); + + Py_RETURN_NONE; +} +#endif /* HAVE_INITGROUPS */ + + +#ifdef HAVE_GETPGID +/*[clinic input] +os.getpgid + + pid: pid_t + +Call the system call getpgid(), and return the result. +[clinic start generated code]*/ + +static PyObject * +os_getpgid_impl(PyObject *module, pid_t pid) +/*[clinic end generated code: output=1db95a97be205d18 input=39d710ae3baaf1c7]*/ +{ + pid_t pgid = getpgid(pid); + if (pgid < 0) + return posix_error(); + return PyLong_FromPid(pgid); +} +#endif /* HAVE_GETPGID */ + + +#ifdef HAVE_GETPGRP +/*[clinic input] +os.getpgrp + +Return the current process group id. +[clinic start generated code]*/ + +static PyObject * +os_getpgrp_impl(PyObject *module) +/*[clinic end generated code: output=c4fc381e51103cf3 input=6846fb2bb9a3705e]*/ +{ +#ifdef GETPGRP_HAVE_ARG + return PyLong_FromPid(getpgrp(0)); +#else /* GETPGRP_HAVE_ARG */ + return PyLong_FromPid(getpgrp()); +#endif /* GETPGRP_HAVE_ARG */ +} +#endif /* HAVE_GETPGRP */ + + +#ifdef HAVE_SETPGRP +/*[clinic input] +os.setpgrp + +Make the current process the leader of its process group. +[clinic start generated code]*/ + +static PyObject * +os_setpgrp_impl(PyObject *module) +/*[clinic end generated code: output=2554735b0a60f0a0 input=1f0619fcb5731e7e]*/ +{ +#ifdef SETPGRP_HAVE_ARG + if (setpgrp(0, 0) < 0) +#else /* SETPGRP_HAVE_ARG */ + if (setpgrp() < 0) +#endif /* SETPGRP_HAVE_ARG */ + return posix_error(); + Py_RETURN_NONE; +} +#endif /* HAVE_SETPGRP */ + +#ifdef HAVE_GETPPID + +#ifdef MS_WINDOWS +#include +#include + +// The structure definition in winternl.h may be incomplete. +// This structure is the full version from the MSDN documentation. +typedef struct _PROCESS_BASIC_INFORMATION_FULL { + NTSTATUS ExitStatus; + PVOID PebBaseAddress; + ULONG_PTR AffinityMask; + LONG BasePriority; + ULONG_PTR UniqueProcessId; + ULONG_PTR InheritedFromUniqueProcessId; +} PROCESS_BASIC_INFORMATION_FULL; + +typedef NTSTATUS (NTAPI *PNT_QUERY_INFORMATION_PROCESS) ( + IN HANDLE ProcessHandle, + IN PROCESSINFOCLASS ProcessInformationClass, + OUT PVOID ProcessInformation, + IN ULONG ProcessInformationLength, + OUT PULONG ReturnLength OPTIONAL); + +// This function returns the process ID of the parent process. +// Returns 0 on failure. +static ULONG +win32_getppid_fast(void) +{ + NTSTATUS status; + HMODULE ntdll; + PNT_QUERY_INFORMATION_PROCESS pNtQueryInformationProcess; + PROCESS_BASIC_INFORMATION_FULL basic_information; + static ULONG cached_ppid = 0; + + if (cached_ppid) { + // No need to query the kernel again. + return cached_ppid; + } + + ntdll = GetModuleHandleW(L"ntdll.dll"); + if (!ntdll) { + return 0; + } + + pNtQueryInformationProcess = (PNT_QUERY_INFORMATION_PROCESS) GetProcAddress(ntdll, "NtQueryInformationProcess"); + if (!pNtQueryInformationProcess) { + return 0; + } + + status = pNtQueryInformationProcess(GetCurrentProcess(), + ProcessBasicInformation, + &basic_information, + sizeof(basic_information), + NULL); + + if (!NT_SUCCESS(status)) { + return 0; + } + + // Perform sanity check on the parent process ID we received from NtQueryInformationProcess. + // The check covers values which exceed the 32-bit range (if running on x64) as well as + // zero and (ULONG) -1. + + if (basic_information.InheritedFromUniqueProcessId == 0 || + basic_information.InheritedFromUniqueProcessId >= ULONG_MAX) + { + return 0; + } + + // Now that we have reached this point, the BasicInformation.InheritedFromUniqueProcessId + // structure member contains a ULONG_PTR which represents the process ID of our parent + // process. This process ID will be correctly returned even if the parent process has + // exited or been terminated. + + cached_ppid = (ULONG) basic_information.InheritedFromUniqueProcessId; + return cached_ppid; +} + +static PyObject* +win32_getppid(void) +{ + DWORD error; + PyObject* result = NULL; + HANDLE process = GetCurrentProcess(); + HPSS snapshot = NULL; + ULONG pid; + + pid = win32_getppid_fast(); + if (pid != 0) { + return PyLong_FromUnsignedLong(pid); + } + + // If failure occurs in win32_getppid_fast(), fall back to using the PSS API. + + error = PssCaptureSnapshot(process, PSS_CAPTURE_NONE, 0, &snapshot); + if (error != ERROR_SUCCESS) { + return PyErr_SetFromWindowsErr(error); + } + + PSS_PROCESS_INFORMATION info; + error = PssQuerySnapshot(snapshot, PSS_QUERY_PROCESS_INFORMATION, &info, + sizeof(info)); + if (error == ERROR_SUCCESS) { + result = PyLong_FromUnsignedLong(info.ParentProcessId); + } + else { + result = PyErr_SetFromWindowsErr(error); + } + + PssFreeSnapshot(process, snapshot); + return result; +} +#endif /*MS_WINDOWS*/ + + +/*[clinic input] +os.getppid + +Return the parent's process id. + +If the parent process has already exited, Windows machines will still +return its id; others systems will return the id of the 'init' process (1). +[clinic start generated code]*/ + +static PyObject * +os_getppid_impl(PyObject *module) +/*[clinic end generated code: output=43b2a946a8c603b4 input=e637cb87539c030e]*/ +{ +#ifdef MS_WINDOWS + return win32_getppid(); +#else + return PyLong_FromPid(getppid()); +#endif +} +#endif /* HAVE_GETPPID */ + + +#ifdef HAVE_GETLOGIN +/*[clinic input] +os.getlogin + +Return the actual login name. +[clinic start generated code]*/ + +static PyObject * +os_getlogin_impl(PyObject *module) +/*[clinic end generated code: output=a32e66a7e5715dac input=2a21ab1e917163df]*/ +{ + PyObject *result = NULL; +#ifdef MS_WINDOWS + wchar_t user_name[UNLEN + 1]; + DWORD num_chars = Py_ARRAY_LENGTH(user_name); + + if (GetUserNameW(user_name, &num_chars)) { + /* num_chars is the number of unicode chars plus null terminator */ + result = PyUnicode_FromWideChar(user_name, num_chars - 1); + } + else + result = PyErr_SetFromWindowsErr(GetLastError()); +#else + char *name; + int old_errno = errno; + + errno = 0; + name = getlogin(); + if (name == NULL) { + if (errno) + posix_error(); + else + PyErr_SetString(PyExc_OSError, "unable to determine login name"); + } + else + result = PyUnicode_DecodeFSDefault(name); + errno = old_errno; +#endif + return result; +} +#endif /* HAVE_GETLOGIN */ + + +#ifdef HAVE_GETUID +/*[clinic input] +os.getuid + +Return the current process's user id. +[clinic start generated code]*/ + +static PyObject * +os_getuid_impl(PyObject *module) +/*[clinic end generated code: output=415c0b401ebed11a input=b53c8b35f110a516]*/ +{ + return _PyLong_FromUid(getuid()); +} +#endif /* HAVE_GETUID */ + + +#ifdef MS_WINDOWS +#define HAVE_KILL +#endif /* MS_WINDOWS */ + +#ifdef HAVE_KILL +/*[clinic input] +os.kill + + pid: pid_t + signal: Py_ssize_t + / + +Kill a process with a signal. +[clinic start generated code]*/ + +static PyObject * +os_kill_impl(PyObject *module, pid_t pid, Py_ssize_t signal) +/*[clinic end generated code: output=8e346a6701c88568 input=61a36b86ca275ab9]*/ +{ + if (PySys_Audit("os.kill", "in", pid, signal) < 0) { + return NULL; + } +#ifndef MS_WINDOWS + if (kill(pid, (int)signal) == -1) { + return posix_error(); + } + + // Check immediately if the signal was sent to the current process. + // Don't micro-optimize pid == getpid(), since PyErr_SetString() check + // is cheap. + if (PyErr_CheckSignals()) { + return NULL; + } + + Py_RETURN_NONE; +#else /* !MS_WINDOWS */ + PyObject *result; + DWORD sig = (DWORD)signal; + DWORD err; + HANDLE handle; + +#ifdef HAVE_WINDOWS_CONSOLE_IO + /* Console processes which share a common console can be sent CTRL+C or + CTRL+BREAK events, provided they handle said events. */ + if (sig == CTRL_C_EVENT || sig == CTRL_BREAK_EVENT) { + if (GenerateConsoleCtrlEvent(sig, (DWORD)pid) == 0) { + err = GetLastError(); + PyErr_SetFromWindowsErr(err); + } + else { + Py_RETURN_NONE; + } + } +#endif /* HAVE_WINDOWS_CONSOLE_IO */ + + /* If the signal is outside of what GenerateConsoleCtrlEvent can use, + attempt to open and terminate the process. */ + handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)pid); + if (handle == NULL) { + err = GetLastError(); + return PyErr_SetFromWindowsErr(err); + } + + if (TerminateProcess(handle, sig) == 0) { + err = GetLastError(); + result = PyErr_SetFromWindowsErr(err); + } else { + result = Py_NewRef(Py_None); + } + + CloseHandle(handle); + return result; +#endif /* !MS_WINDOWS */ +} +#endif /* HAVE_KILL */ + + +#ifdef HAVE_KILLPG +/*[clinic input] +os.killpg + + pgid: pid_t + signal: int + / + +Kill a process group with a signal. +[clinic start generated code]*/ + +static PyObject * +os_killpg_impl(PyObject *module, pid_t pgid, int signal) +/*[clinic end generated code: output=6dbcd2f1fdf5fdba input=38b5449eb8faec19]*/ +{ + if (PySys_Audit("os.killpg", "ii", pgid, signal) < 0) { + return NULL; + } + /* XXX some man pages make the `pgid` parameter an int, others + a pid_t. Since getpgrp() returns a pid_t, we assume killpg should + take the same type. Moreover, pid_t is always at least as wide as + int (else compilation of this module fails), which is safe. */ + if (killpg(pgid, signal) == -1) + return posix_error(); + Py_RETURN_NONE; +} +#endif /* HAVE_KILLPG */ + + +#ifdef HAVE_PLOCK +#ifdef HAVE_SYS_LOCK_H +#include +#endif + +/*[clinic input] +os.plock + op: int + / + +Lock program segments into memory."); +[clinic start generated code]*/ + +static PyObject * +os_plock_impl(PyObject *module, int op) +/*[clinic end generated code: output=81424167033b168e input=e6e5e348e1525f60]*/ +{ + if (plock(op) == -1) + return posix_error(); + Py_RETURN_NONE; +} +#endif /* HAVE_PLOCK */ + + +#ifdef HAVE_SETUID +/*[clinic input] +os.setuid + + uid: uid_t + / + +Set the current process's user id. +[clinic start generated code]*/ + +static PyObject * +os_setuid_impl(PyObject *module, uid_t uid) +/*[clinic end generated code: output=a0a41fd0d1ec555f input=c921a3285aa22256]*/ +{ + if (setuid(uid) < 0) + return posix_error(); + Py_RETURN_NONE; +} +#endif /* HAVE_SETUID */ + + +#ifdef HAVE_SETEUID +/*[clinic input] +os.seteuid + + euid: uid_t + / + +Set the current process's effective user id. +[clinic start generated code]*/ + +static PyObject * +os_seteuid_impl(PyObject *module, uid_t euid) +/*[clinic end generated code: output=102e3ad98361519a input=ba93d927e4781aa9]*/ +{ + if (seteuid(euid) < 0) + return posix_error(); + Py_RETURN_NONE; +} +#endif /* HAVE_SETEUID */ + + +#ifdef HAVE_SETEGID +/*[clinic input] +os.setegid + + egid: gid_t + / + +Set the current process's effective group id. +[clinic start generated code]*/ + +static PyObject * +os_setegid_impl(PyObject *module, gid_t egid) +/*[clinic end generated code: output=4e4b825a6a10258d input=4080526d0ccd6ce3]*/ +{ + if (setegid(egid) < 0) + return posix_error(); + Py_RETURN_NONE; +} +#endif /* HAVE_SETEGID */ + + +#ifdef HAVE_SETREUID +/*[clinic input] +os.setreuid + + ruid: uid_t + euid: uid_t + / + +Set the current process's real and effective user ids. +[clinic start generated code]*/ + +static PyObject * +os_setreuid_impl(PyObject *module, uid_t ruid, uid_t euid) +/*[clinic end generated code: output=62d991210006530a input=0ca8978de663880c]*/ +{ + if (setreuid(ruid, euid) < 0) { + return posix_error(); + } else { + Py_RETURN_NONE; + } +} +#endif /* HAVE_SETREUID */ + + +#ifdef HAVE_SETREGID +/*[clinic input] +os.setregid + + rgid: gid_t + egid: gid_t + / + +Set the current process's real and effective group ids. +[clinic start generated code]*/ + +static PyObject * +os_setregid_impl(PyObject *module, gid_t rgid, gid_t egid) +/*[clinic end generated code: output=aa803835cf5342f3 input=c59499f72846db78]*/ +{ + if (setregid(rgid, egid) < 0) + return posix_error(); + Py_RETURN_NONE; +} +#endif /* HAVE_SETREGID */ + + +#ifdef HAVE_SETGID +/*[clinic input] +os.setgid + gid: gid_t + / + +Set the current process's group id. +[clinic start generated code]*/ + +static PyObject * +os_setgid_impl(PyObject *module, gid_t gid) +/*[clinic end generated code: output=bdccd7403f6ad8c3 input=27d30c4059045dc6]*/ +{ + if (setgid(gid) < 0) + return posix_error(); + Py_RETURN_NONE; +} +#endif /* HAVE_SETGID */ + + +#ifdef HAVE_SETGROUPS +/*[clinic input] +os.setgroups + + groups: object + / + +Set the groups of the current process to list. +[clinic start generated code]*/ + +static PyObject * +os_setgroups(PyObject *module, PyObject *groups) +/*[clinic end generated code: output=3fcb32aad58c5ecd input=fa742ca3daf85a7e]*/ +{ + if (!PySequence_Check(groups)) { + PyErr_SetString(PyExc_TypeError, "setgroups argument must be a sequence"); + return NULL; + } + Py_ssize_t len = PySequence_Size(groups); + if (len < 0) { + return NULL; + } + if (len > MAX_GROUPS) { + PyErr_SetString(PyExc_ValueError, "too many groups"); + return NULL; + } + + gid_t *grouplist = PyMem_New(gid_t, len); + if (grouplist == NULL) { + PyErr_NoMemory(); + return NULL; + } + for (Py_ssize_t i = 0; i < len; i++) { + PyObject *elem; + elem = PySequence_GetItem(groups, i); + if (!elem) { + PyMem_Free(grouplist); + return NULL; + } + if (!PyLong_Check(elem)) { + PyErr_SetString(PyExc_TypeError, + "groups must be integers"); + Py_DECREF(elem); + PyMem_Free(grouplist); + return NULL; + } else { + if (!_Py_Gid_Converter(elem, &grouplist[i])) { + Py_DECREF(elem); + PyMem_Free(grouplist); + return NULL; + } + } + Py_DECREF(elem); + } + + if (setgroups(len, grouplist) < 0) { + posix_error(); + PyMem_Free(grouplist); + return NULL; + } + PyMem_Free(grouplist); + Py_RETURN_NONE; +} +#endif /* HAVE_SETGROUPS */ + +#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4) +static PyObject * +wait_helper(PyObject *module, pid_t pid, int status, struct rusage *ru) +{ + PyObject *result; + PyObject *struct_rusage; + + if (pid == -1) + return posix_error(); + + // If wait succeeded but no child was ready to report status, ru will not + // have been populated. + if (pid == 0) { + memset(ru, 0, sizeof(*ru)); + } + + struct_rusage = _PyImport_GetModuleAttrString("resource", "struct_rusage"); + if (struct_rusage == NULL) + return NULL; + + /* XXX(nnorwitz): Copied (w/mods) from resource.c, there should be only one. */ + result = PyStructSequence_New((PyTypeObject*) struct_rusage); + Py_DECREF(struct_rusage); + if (!result) + return NULL; + + int pos = 0; + +#ifndef doubletime +#define doubletime(TV) ((double)(TV).tv_sec + (TV).tv_usec * 0.000001) +#endif + +#define SET_RESULT(CALL) \ + do { \ + PyObject *item = (CALL); \ + if (item == NULL) { \ + Py_DECREF(result); \ + return NULL; \ + } \ + PyStructSequence_SET_ITEM(result, pos++, item); \ + } while(0) + + SET_RESULT(PyFloat_FromDouble(doubletime(ru->ru_utime))); + SET_RESULT(PyFloat_FromDouble(doubletime(ru->ru_stime))); + SET_RESULT(PyLong_FromLong(ru->ru_maxrss)); + SET_RESULT(PyLong_FromLong(ru->ru_ixrss)); + SET_RESULT(PyLong_FromLong(ru->ru_idrss)); + SET_RESULT(PyLong_FromLong(ru->ru_isrss)); + SET_RESULT(PyLong_FromLong(ru->ru_minflt)); + SET_RESULT(PyLong_FromLong(ru->ru_majflt)); + SET_RESULT(PyLong_FromLong(ru->ru_nswap)); + SET_RESULT(PyLong_FromLong(ru->ru_inblock)); + SET_RESULT(PyLong_FromLong(ru->ru_oublock)); + SET_RESULT(PyLong_FromLong(ru->ru_msgsnd)); + SET_RESULT(PyLong_FromLong(ru->ru_msgrcv)); + SET_RESULT(PyLong_FromLong(ru->ru_nsignals)); + SET_RESULT(PyLong_FromLong(ru->ru_nvcsw)); + SET_RESULT(PyLong_FromLong(ru->ru_nivcsw)); +#undef SET_RESULT + + return Py_BuildValue("NiN", PyLong_FromPid(pid), status, result); +} +#endif /* HAVE_WAIT3 || HAVE_WAIT4 */ + + +#ifdef HAVE_WAIT3 +/*[clinic input] +os.wait3 + + options: int +Wait for completion of a child process. + +Returns a tuple of information about the child process: + (pid, status, rusage) +[clinic start generated code]*/ + +static PyObject * +os_wait3_impl(PyObject *module, int options) +/*[clinic end generated code: output=92c3224e6f28217a input=8ac4c56956b61710]*/ +{ + pid_t pid; + struct rusage ru; + int async_err = 0; + WAIT_TYPE status; + WAIT_STATUS_INT(status) = 0; + + do { + Py_BEGIN_ALLOW_THREADS + pid = wait3(&status, options, &ru); + Py_END_ALLOW_THREADS + } while (pid < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + if (pid < 0) + return (!async_err) ? posix_error() : NULL; + + return wait_helper(module, pid, WAIT_STATUS_INT(status), &ru); +} +#endif /* HAVE_WAIT3 */ + + +#ifdef HAVE_WAIT4 +/*[clinic input] + +os.wait4 + + pid: pid_t + options: int + +Wait for completion of a specific child process. + +Returns a tuple of information about the child process: + (pid, status, rusage) +[clinic start generated code]*/ + +static PyObject * +os_wait4_impl(PyObject *module, pid_t pid, int options) +/*[clinic end generated code: output=66195aa507b35f70 input=d11deed0750600ba]*/ +{ + pid_t res; + struct rusage ru; + int async_err = 0; + WAIT_TYPE status; + WAIT_STATUS_INT(status) = 0; + + do { + Py_BEGIN_ALLOW_THREADS + res = wait4(pid, &status, options, &ru); + Py_END_ALLOW_THREADS + } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + if (res < 0) + return (!async_err) ? posix_error() : NULL; + + return wait_helper(module, res, WAIT_STATUS_INT(status), &ru); +} +#endif /* HAVE_WAIT4 */ + + +#if defined(HAVE_WAITID) +/*[clinic input] +os.waitid + + idtype: idtype_t + Must be one of be P_PID, P_PGID or P_ALL. + id: id_t + The id to wait on. + options: int + Constructed from the ORing of one or more of WEXITED, WSTOPPED + or WCONTINUED and additionally may be ORed with WNOHANG or WNOWAIT. + / + +Returns the result of waiting for a process or processes. + +Returns either waitid_result or None if WNOHANG is specified and there are +no children in a waitable state. +[clinic start generated code]*/ + +static PyObject * +os_waitid_impl(PyObject *module, idtype_t idtype, id_t id, int options) +/*[clinic end generated code: output=5d2e1c0bde61f4d8 input=d8e7f76e052b7920]*/ +{ + PyObject *result; + int res; + int async_err = 0; + siginfo_t si; + si.si_pid = 0; + + do { + Py_BEGIN_ALLOW_THREADS + res = waitid(idtype, id, &si, options); + Py_END_ALLOW_THREADS + } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + if (res < 0) + return (!async_err) ? posix_error() : NULL; + + if (si.si_pid == 0) + Py_RETURN_NONE; + + PyObject *WaitidResultType = get_posix_state(module)->WaitidResultType; + result = PyStructSequence_New((PyTypeObject *)WaitidResultType); + if (!result) + return NULL; + + int pos = 0; + +#define SET_RESULT(CALL) \ + do { \ + PyObject *item = (CALL); \ + if (item == NULL) { \ + Py_DECREF(result); \ + return NULL; \ + } \ + PyStructSequence_SET_ITEM(result, pos++, item); \ + } while(0) + + SET_RESULT(PyLong_FromPid(si.si_pid)); + SET_RESULT(_PyLong_FromUid(si.si_uid)); + SET_RESULT(PyLong_FromLong((long)(si.si_signo))); + SET_RESULT(PyLong_FromLong((long)(si.si_status))); + SET_RESULT(PyLong_FromLong((long)(si.si_code))); + +#undef SET_RESULT + + return result; +} +#endif /* defined(HAVE_WAITID) */ + + +#if defined(HAVE_WAITPID) +/*[clinic input] +os.waitpid + pid: pid_t + options: int + / + +Wait for completion of a given child process. + +Returns a tuple of information regarding the child process: + (pid, status) + +The options argument is ignored on Windows. +[clinic start generated code]*/ + +static PyObject * +os_waitpid_impl(PyObject *module, pid_t pid, int options) +/*[clinic end generated code: output=5c37c06887a20270 input=0bf1666b8758fda3]*/ +{ + pid_t res; + int async_err = 0; + WAIT_TYPE status; + WAIT_STATUS_INT(status) = 0; + + do { + Py_BEGIN_ALLOW_THREADS + res = waitpid(pid, &status, options); + Py_END_ALLOW_THREADS + } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + if (res < 0) + return (!async_err) ? posix_error() : NULL; + + return Py_BuildValue("Ni", PyLong_FromPid(res), WAIT_STATUS_INT(status)); +} +#elif defined(HAVE_CWAIT) +/* MS C has a variant of waitpid() that's usable for most purposes. */ +/*[clinic input] +os.waitpid + pid: intptr_t + options: int + / + +Wait for completion of a given process. + +Returns a tuple of information regarding the process: + (pid, status << 8) + +The options argument is ignored on Windows. +[clinic start generated code]*/ + +static PyObject * +os_waitpid_impl(PyObject *module, intptr_t pid, int options) +/*[clinic end generated code: output=be836b221271d538 input=40f2440c515410f8]*/ +{ + int status; + intptr_t res; + int async_err = 0; + + do { + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH + res = _cwait(&status, pid, options); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + } while (res < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + if (res < 0) + return (!async_err) ? posix_error() : NULL; + + unsigned long long ustatus = (unsigned int)status; + + /* shift the status left a byte so this is more like the POSIX waitpid */ + return Py_BuildValue(_Py_PARSE_INTPTR "K", res, ustatus << 8); +} +#endif + + +#ifdef HAVE_WAIT +/*[clinic input] +os.wait + +Wait for completion of a child process. + +Returns a tuple of information about the child process: + (pid, status) +[clinic start generated code]*/ + +static PyObject * +os_wait_impl(PyObject *module) +/*[clinic end generated code: output=6bc419ac32fb364b input=03b0182d4a4700ce]*/ +{ + pid_t pid; + int async_err = 0; + WAIT_TYPE status; + WAIT_STATUS_INT(status) = 0; + + do { + Py_BEGIN_ALLOW_THREADS + pid = wait(&status); + Py_END_ALLOW_THREADS + } while (pid < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + if (pid < 0) + return (!async_err) ? posix_error() : NULL; + + return Py_BuildValue("Ni", PyLong_FromPid(pid), WAIT_STATUS_INT(status)); +} +#endif /* HAVE_WAIT */ + + +// This system call always crashes on older Android versions. +#if defined(__linux__) && defined(__NR_pidfd_open) && \ + !(defined(__ANDROID__) && __ANDROID_API__ < 31) +/*[clinic input] +os.pidfd_open + pid: pid_t + flags: unsigned_int = 0 + +Return a file descriptor referring to the process *pid*. + +The descriptor can be used to perform process management without races and +signals. +[clinic start generated code]*/ + +static PyObject * +os_pidfd_open_impl(PyObject *module, pid_t pid, unsigned int flags) +/*[clinic end generated code: output=5c7252698947dc41 input=c3fd99ce947ccfef]*/ +{ + int fd = syscall(__NR_pidfd_open, pid, flags); + if (fd < 0) { + return posix_error(); + } + return PyLong_FromLong(fd); +} +#endif + + +#ifdef HAVE_SETNS +/*[clinic input] +os.setns + fd: fildes + A file descriptor to a namespace. + nstype: int = 0 + Type of namespace. + +Move the calling thread into different namespaces. +[clinic start generated code]*/ + +static PyObject * +os_setns_impl(PyObject *module, int fd, int nstype) +/*[clinic end generated code: output=5dbd055bfb66ecd0 input=42787871226bf3ee]*/ +{ + int res; + + Py_BEGIN_ALLOW_THREADS + res = setns(fd, nstype); + Py_END_ALLOW_THREADS + + if (res != 0) { + return posix_error(); + } + + Py_RETURN_NONE; +} +#endif + + +#ifdef HAVE_UNSHARE +/*[clinic input] +os.unshare + flags: int + Namespaces to be unshared. + +Disassociate parts of a process (or thread) execution context. +[clinic start generated code]*/ + +static PyObject * +os_unshare_impl(PyObject *module, int flags) +/*[clinic end generated code: output=1b3177906dd237ee input=9e065db3232b8b1b]*/ +{ + int res; + + Py_BEGIN_ALLOW_THREADS + res = unshare(flags); + Py_END_ALLOW_THREADS + + if (res != 0) { + return posix_error(); + } + + Py_RETURN_NONE; +} +#endif + + +#if defined(HAVE_READLINK) || defined(MS_WINDOWS) +/*[clinic input] +os.readlink + + path: path_t + * + dir_fd: dir_fd(requires='readlinkat') = None + +Return a string representing the path to which the symbolic link points. + +If dir_fd is not None, it should be a file descriptor open to a directory, +and path should be relative; path will then be relative to that directory. + +dir_fd may not be implemented on your platform. If it is unavailable, +using it will raise a NotImplementedError. +[clinic start generated code]*/ + +static PyObject * +os_readlink_impl(PyObject *module, path_t *path, int dir_fd) +/*[clinic end generated code: output=d21b732a2e814030 input=113c87e0db1ecaf2]*/ +{ +#if defined(HAVE_READLINK) + char buffer[MAXPATHLEN+1]; + ssize_t length; +#ifdef HAVE_READLINKAT + int readlinkat_unavailable = 0; +#endif + + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_READLINKAT + if (dir_fd != DEFAULT_DIR_FD) { + if (HAVE_READLINKAT_RUNTIME) { + length = readlinkat(dir_fd, path->narrow, buffer, MAXPATHLEN); + } else { + readlinkat_unavailable = 1; + } + } else +#endif + length = readlink(path->narrow, buffer, MAXPATHLEN); + Py_END_ALLOW_THREADS + +#ifdef HAVE_READLINKAT + if (readlinkat_unavailable) { + argument_unavailable_error(NULL, "dir_fd"); + return NULL; + } +#endif + + if (length < 0) { + return path_error(path); + } + buffer[length] = '\0'; + + if (PyUnicode_Check(path->object)) + return PyUnicode_DecodeFSDefaultAndSize(buffer, length); + else + return PyBytes_FromStringAndSize(buffer, length); +#elif defined(MS_WINDOWS) + DWORD n_bytes_returned; + DWORD io_result = 0; + HANDLE reparse_point_handle; + char target_buffer[_Py_MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + _Py_REPARSE_DATA_BUFFER *rdb = (_Py_REPARSE_DATA_BUFFER *)target_buffer; + PyObject *result = NULL; + + /* First get a handle to the reparse point */ + Py_BEGIN_ALLOW_THREADS + reparse_point_handle = CreateFileW( + path->wide, + 0, + 0, + 0, + OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, + 0); + if (reparse_point_handle != INVALID_HANDLE_VALUE) { + /* New call DeviceIoControl to read the reparse point */ + io_result = DeviceIoControl( + reparse_point_handle, + FSCTL_GET_REPARSE_POINT, + 0, 0, /* in buffer */ + target_buffer, sizeof(target_buffer), + &n_bytes_returned, + 0 /* we're not using OVERLAPPED_IO */ + ); + CloseHandle(reparse_point_handle); + } + Py_END_ALLOW_THREADS + + if (io_result == 0) { + return path_error(path); + } + + wchar_t *name = NULL; + Py_ssize_t nameLen = 0; + if (rdb->ReparseTag == IO_REPARSE_TAG_SYMLINK) + { + name = (wchar_t *)((char*)rdb->SymbolicLinkReparseBuffer.PathBuffer + + rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset); + nameLen = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar_t); + } + else if (rdb->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) + { + name = (wchar_t *)((char*)rdb->MountPointReparseBuffer.PathBuffer + + rdb->MountPointReparseBuffer.SubstituteNameOffset); + nameLen = rdb->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t); + } + else + { + PyErr_SetString(PyExc_ValueError, "not a symbolic link"); + } + if (name) { + if (nameLen > 4 && wcsncmp(name, L"\\??\\", 4) == 0) { + /* Our buffer is mutable, so this is okay */ + name[1] = L'\\'; + } + result = PyUnicode_FromWideChar(name, nameLen); + if (result && PyBytes_Check(path->object)) { + Py_SETREF(result, PyUnicode_EncodeFSDefault(result)); + } + } + return result; +#endif +} +#endif /* defined(HAVE_READLINK) || defined(MS_WINDOWS) */ + +#if defined(MS_WINDOWS) + +/* Remove the last portion of the path - return 0 on success */ +static int +_dirnameW(WCHAR *path) +{ + WCHAR *ptr; + size_t length = wcsnlen_s(path, MAX_PATH); + if (length == MAX_PATH) { + return -1; + } + + /* walk the path from the end until a backslash is encountered */ + for(ptr = path + length; ptr != path; ptr--) { + if (*ptr == L'\\' || *ptr == L'/') { + break; + } + } + *ptr = 0; + return 0; +} + +#endif + +#ifdef HAVE_SYMLINK + +#if defined(MS_WINDOWS) + +/* Is this path absolute? */ +static int +_is_absW(const WCHAR *path) +{ + return path[0] == L'\\' || path[0] == L'/' || + (path[0] && path[1] == L':'); +} + +/* join root and rest with a backslash - return 0 on success */ +static int +_joinW(WCHAR *dest_path, const WCHAR *root, const WCHAR *rest) +{ + if (_is_absW(rest)) { + return wcscpy_s(dest_path, MAX_PATH, rest); + } + + if (wcscpy_s(dest_path, MAX_PATH, root)) { + return -1; + } + + if (dest_path[0] && wcscat_s(dest_path, MAX_PATH, L"\\")) { + return -1; + } + + return wcscat_s(dest_path, MAX_PATH, rest); +} + +/* Return True if the path at src relative to dest is a directory */ +static int +_check_dirW(LPCWSTR src, LPCWSTR dest) +{ + WIN32_FILE_ATTRIBUTE_DATA src_info; + WCHAR dest_parent[MAX_PATH]; + WCHAR src_resolved[MAX_PATH] = L""; + + /* dest_parent = os.path.dirname(dest) */ + if (wcscpy_s(dest_parent, MAX_PATH, dest) || + _dirnameW(dest_parent)) { + return 0; + } + /* src_resolved = os.path.join(dest_parent, src) */ + if (_joinW(src_resolved, dest_parent, src)) { + return 0; + } + return ( + GetFileAttributesExW(src_resolved, GetFileExInfoStandard, &src_info) + && src_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY + ); +} +#endif + + +/*[clinic input] +os.symlink + src: path_t + dst: path_t + target_is_directory: bool = False + * + dir_fd: dir_fd(requires='symlinkat')=None + +# "symlink(src, dst, target_is_directory=False, *, dir_fd=None)\n\n\ + +Create a symbolic link pointing to src named dst. + +target_is_directory is required on Windows if the target is to be + interpreted as a directory. (On Windows, symlink requires + Windows 6.0 or greater, and raises a NotImplementedError otherwise.) + target_is_directory is ignored on non-Windows platforms. + +If dir_fd is not None, it should be a file descriptor open to a directory, + and path should be relative; path will then be relative to that directory. +dir_fd may not be implemented on your platform. + If it is unavailable, using it will raise a NotImplementedError. + +[clinic start generated code]*/ + +static PyObject * +os_symlink_impl(PyObject *module, path_t *src, path_t *dst, + int target_is_directory, int dir_fd) +/*[clinic end generated code: output=08ca9f3f3cf960f6 input=e820ec4472547bc3]*/ +{ +#ifdef MS_WINDOWS + DWORD result; + DWORD flags = 0; + + /* Assumed true, set to false if detected to not be available. */ + static int windows_has_symlink_unprivileged_flag = TRUE; +#else + int result; +#ifdef HAVE_SYMLINKAT + int symlinkat_unavailable = 0; +#endif +#endif + + if (PySys_Audit("os.symlink", "OOi", src->object, dst->object, + dir_fd == DEFAULT_DIR_FD ? -1 : dir_fd) < 0) { + return NULL; + } + +#ifdef MS_WINDOWS + + if (windows_has_symlink_unprivileged_flag) { + /* Allow non-admin symlinks if system allows it. */ + flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; + } + + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH + /* if src is a directory, ensure flags==1 (target_is_directory bit) */ + if (target_is_directory || _check_dirW(src->wide, dst->wide)) { + flags |= SYMBOLIC_LINK_FLAG_DIRECTORY; + } + + result = CreateSymbolicLinkW(dst->wide, src->wide, flags); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + + if (windows_has_symlink_unprivileged_flag && !result && + ERROR_INVALID_PARAMETER == GetLastError()) { + + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH + /* This error might be caused by + SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE not being supported. + Try again, and update windows_has_symlink_unprivileged_flag if we + are successful this time. + + NOTE: There is a risk of a race condition here if there are other + conditions than the flag causing ERROR_INVALID_PARAMETER, and + another process (or thread) changes that condition in between our + calls to CreateSymbolicLink. + */ + flags &= ~(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE); + result = CreateSymbolicLinkW(dst->wide, src->wide, flags); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + + if (result || ERROR_INVALID_PARAMETER != GetLastError()) { + windows_has_symlink_unprivileged_flag = FALSE; + } + } + + if (!result) + return path_error2(src, dst); + +#else + + if ((src->narrow && dst->wide) || (src->wide && dst->narrow)) { + PyErr_SetString(PyExc_ValueError, + "symlink: src and dst must be the same type"); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_SYMLINKAT + if (dir_fd != DEFAULT_DIR_FD) { + if (HAVE_SYMLINKAT_RUNTIME) { + result = symlinkat(src->narrow, dir_fd, dst->narrow); + } else { + symlinkat_unavailable = 1; + } + } else +#endif + result = symlink(src->narrow, dst->narrow); + Py_END_ALLOW_THREADS + +#ifdef HAVE_SYMLINKAT + if (symlinkat_unavailable) { + argument_unavailable_error(NULL, "dir_fd"); + return NULL; + } +#endif + + if (result) + return path_error2(src, dst); +#endif + + Py_RETURN_NONE; +} +#endif /* HAVE_SYMLINK */ + + +static PyStructSequence_Field times_result_fields[] = { + {"user", "user time"}, + {"system", "system time"}, + {"children_user", "user time of children"}, + {"children_system", "system time of children"}, + {"elapsed", "elapsed time since an arbitrary point in the past"}, + {NULL} +}; + +PyDoc_STRVAR(times_result__doc__, +"times_result: Result from os.times().\n\n\ +This object may be accessed either as a tuple of\n\ + (user, system, children_user, children_system, elapsed),\n\ +or via the attributes user, system, children_user, children_system,\n\ +and elapsed.\n\ +\n\ +See os.times for more information."); + +static PyStructSequence_Desc times_result_desc = { + "times_result", /* name */ + times_result__doc__, /* doc */ + times_result_fields, + 5 +}; + +static PyObject * +build_times_result(PyObject *module, double user, double system, + double children_user, double children_system, + double elapsed) +{ + PyObject *TimesResultType = get_posix_state(module)->TimesResultType; + PyObject *value = PyStructSequence_New((PyTypeObject *)TimesResultType); + if (value == NULL) + return NULL; + +#define SET(i, field) \ + { \ + PyObject *o = PyFloat_FromDouble(field); \ + if (!o) { \ + Py_DECREF(value); \ + return NULL; \ + } \ + PyStructSequence_SET_ITEM(value, i, o); \ + } \ + + SET(0, user); + SET(1, system); + SET(2, children_user); + SET(3, children_system); + SET(4, elapsed); + +#undef SET + + return value; +} + + +/*[clinic input] +os.times + +Return a collection containing process timing information. + +The object returned behaves like a named tuple with these fields: + (utime, stime, cutime, cstime, elapsed_time) +All fields are floating-point numbers. +[clinic start generated code]*/ + +static PyObject * +os_times_impl(PyObject *module) +/*[clinic end generated code: output=35f640503557d32a input=8dbfe33a2dcc3df3]*/ +{ +#ifdef MS_WINDOWS + FILETIME create, exit, kernel, user; + HANDLE hProc; + hProc = GetCurrentProcess(); + GetProcessTimes(hProc, &create, &exit, &kernel, &user); + /* The fields of a FILETIME structure are the hi and lo part + of a 64-bit value expressed in 100 nanosecond units. + 1e7 is one second in such units; 1e-7 the inverse. + 429.4967296 is 2**32 / 1e7 or 2**32 * 1e-7. + */ + return build_times_result(module, + (double)(user.dwHighDateTime*429.4967296 + + user.dwLowDateTime*1e-7), + (double)(kernel.dwHighDateTime*429.4967296 + + kernel.dwLowDateTime*1e-7), + (double)0, + (double)0, + (double)0); +#else /* MS_WINDOWS */ + _posixstate *state = get_posix_state(module); + long ticks_per_second = state->ticks_per_second; + + struct tms process; + clock_t elapsed; + errno = 0; + elapsed = times(&process); + if (elapsed == (clock_t) -1) { + return posix_error(); + } + + return build_times_result(module, + (double)process.tms_utime / ticks_per_second, + (double)process.tms_stime / ticks_per_second, + (double)process.tms_cutime / ticks_per_second, + (double)process.tms_cstime / ticks_per_second, + (double)elapsed / ticks_per_second); +#endif /* MS_WINDOWS */ +} + + +#if defined(HAVE_TIMERFD_CREATE) +#define ONE_SECOND_IN_NS (1000 * 1000 * 1000) +#define EXTRACT_NSEC(value) (long)( ( (double)(value) - (time_t)(value) ) * 1e9) +#define CONVERT_SEC_AND_NSEC_TO_DOUBLE(sec, nsec) ( (double)(sec) + (double)(nsec) * 1e-9 ) + +static PyObject * +build_itimerspec(const struct itimerspec* curr_value) +{ + double _value = CONVERT_SEC_AND_NSEC_TO_DOUBLE(curr_value->it_value.tv_sec, + curr_value->it_value.tv_nsec); + PyObject *value = PyFloat_FromDouble(_value); + if (value == NULL) { + return NULL; + } + double _interval = CONVERT_SEC_AND_NSEC_TO_DOUBLE(curr_value->it_interval.tv_sec, + curr_value->it_interval.tv_nsec); + PyObject *interval = PyFloat_FromDouble(_interval); + if (interval == NULL) { + Py_DECREF(value); + return NULL; + } + PyObject *tuple = PyTuple_Pack(2, value, interval); + Py_DECREF(interval); + Py_DECREF(value); + return tuple; +} + +static PyObject * +build_itimerspec_ns(const struct itimerspec* curr_value) +{ + PyTime_t value, interval; + if (_PyTime_FromTimespec(&value, &curr_value->it_value) < 0) { + return NULL; + } + if (_PyTime_FromTimespec(&interval, &curr_value->it_interval) < 0) { + return NULL; + } + return Py_BuildValue("LL", value, interval); +} + +/*[clinic input] +os.timerfd_create + + clockid: int + A valid clock ID constant as timer file descriptor. + + time.CLOCK_REALTIME + time.CLOCK_MONOTONIC + time.CLOCK_BOOTTIME + / + * + flags: int = 0 + 0 or a bit mask of os.TFD_NONBLOCK or os.TFD_CLOEXEC. + + os.TFD_NONBLOCK + If *TFD_NONBLOCK* is set as a flag, read doesn't blocks. + If *TFD_NONBLOCK* is not set as a flag, read block until the timer fires. + + os.TFD_CLOEXEC + If *TFD_CLOEXEC* is set as a flag, enable the close-on-exec flag + +Create and return a timer file descriptor. +[clinic start generated code]*/ + +static PyObject * +os_timerfd_create_impl(PyObject *module, int clockid, int flags) +/*[clinic end generated code: output=1caae80fb168004a input=64b7020c5ac0b8f4]*/ + +{ + int fd; + Py_BEGIN_ALLOW_THREADS + flags |= TFD_CLOEXEC; // PEP 446: always create non-inheritable FD + fd = timerfd_create(clockid, flags); + Py_END_ALLOW_THREADS + if (fd == -1) { + return PyErr_SetFromErrno(PyExc_OSError); + } + return PyLong_FromLong(fd); +} + +/*[clinic input] +os.timerfd_settime + + fd: fildes + A timer file descriptor. + / + * + flags: int = 0 + 0 or a bit mask of TFD_TIMER_ABSTIME or TFD_TIMER_CANCEL_ON_SET. + initial as initial_double: double = 0.0 + The initial expiration time, in seconds. + interval as interval_double: double = 0.0 + The timer's interval, in seconds. + +Alter a timer file descriptor's internal timer in seconds. +[clinic start generated code]*/ + +static PyObject * +os_timerfd_settime_impl(PyObject *module, int fd, int flags, + double initial_double, double interval_double) +/*[clinic end generated code: output=df4c1bce6859224e input=81d2c0d7e936e8a7]*/ +{ + PyTime_t initial, interval; + if (_PyTime_FromSecondsDouble(initial_double, _PyTime_ROUND_FLOOR, + &initial) < 0) { + return NULL; + } + if (_PyTime_FromSecondsDouble(interval_double, _PyTime_ROUND_FLOOR, + &interval) < 0) { + return NULL; + } + + struct itimerspec new_value, old_value; + if (_PyTime_AsTimespec(initial, &new_value.it_value) < 0) { + PyErr_SetString(PyExc_ValueError, "invalid initial value"); + return NULL; + } + if (_PyTime_AsTimespec(interval, &new_value.it_interval) < 0) { + PyErr_SetString(PyExc_ValueError, "invalid interval value"); + return NULL; + } + + int result; + Py_BEGIN_ALLOW_THREADS + result = timerfd_settime(fd, flags, &new_value, &old_value); + Py_END_ALLOW_THREADS + if (result == -1) { + return PyErr_SetFromErrno(PyExc_OSError); + } + return build_itimerspec(&old_value); +} + + +/*[clinic input] +os.timerfd_settime_ns + + fd: fildes + A timer file descriptor. + / + * + flags: int = 0 + 0 or a bit mask of TFD_TIMER_ABSTIME or TFD_TIMER_CANCEL_ON_SET. + initial: long_long = 0 + initial expiration timing in seconds. + interval: long_long = 0 + interval for the timer in seconds. + +Alter a timer file descriptor's internal timer in nanoseconds. +[clinic start generated code]*/ + +static PyObject * +os_timerfd_settime_ns_impl(PyObject *module, int fd, int flags, + long long initial, long long interval) +/*[clinic end generated code: output=6273ec7d7b4cc0b3 input=261e105d6e42f5bc]*/ +{ + struct itimerspec new_value; + struct itimerspec old_value; + int result; + if (_PyTime_AsTimespec(initial, &new_value.it_value) < 0) { + PyErr_SetString(PyExc_ValueError, "invalid initial value"); + return NULL; + } + if (_PyTime_AsTimespec(interval, &new_value.it_interval) < 0) { + PyErr_SetString(PyExc_ValueError, "invalid interval value"); + return NULL; + } + Py_BEGIN_ALLOW_THREADS + result = timerfd_settime(fd, flags, &new_value, &old_value); + Py_END_ALLOW_THREADS + if (result == -1) { + return PyErr_SetFromErrno(PyExc_OSError); + } + return build_itimerspec_ns(&old_value); +} + +/*[clinic input] +os.timerfd_gettime + + fd: fildes + A timer file descriptor. + / + +Return a tuple of a timer file descriptor's (interval, next expiration) in float seconds. +[clinic start generated code]*/ + +static PyObject * +os_timerfd_gettime_impl(PyObject *module, int fd) +/*[clinic end generated code: output=ec5a94a66cfe6ab4 input=8148e3430870da1c]*/ +{ + struct itimerspec curr_value; + int result; + Py_BEGIN_ALLOW_THREADS + result = timerfd_gettime(fd, &curr_value); + Py_END_ALLOW_THREADS + if (result == -1) { + return PyErr_SetFromErrno(PyExc_OSError); + } + return build_itimerspec(&curr_value); +} + + +/*[clinic input] +os.timerfd_gettime_ns + + fd: fildes + A timer file descriptor. + / + +Return a tuple of a timer file descriptor's (interval, next expiration) in nanoseconds. +[clinic start generated code]*/ + +static PyObject * +os_timerfd_gettime_ns_impl(PyObject *module, int fd) +/*[clinic end generated code: output=580633a4465f39fe input=a825443e4c6b40ac]*/ +{ + struct itimerspec curr_value; + int result; + Py_BEGIN_ALLOW_THREADS + result = timerfd_gettime(fd, &curr_value); + Py_END_ALLOW_THREADS + if (result == -1) { + return PyErr_SetFromErrno(PyExc_OSError); + } + return build_itimerspec_ns(&curr_value); +} + +#undef ONE_SECOND_IN_NS +#undef EXTRACT_NSEC + +#endif /* HAVE_TIMERFD_CREATE */ + +#ifdef HAVE_GETSID +/*[clinic input] +os.getsid + + pid: pid_t + / + +Call the system call getsid(pid) and return the result. +[clinic start generated code]*/ + +static PyObject * +os_getsid_impl(PyObject *module, pid_t pid) +/*[clinic end generated code: output=112deae56b306460 input=eeb2b923a30ce04e]*/ +{ + int sid; + sid = getsid(pid); + if (sid < 0) + return posix_error(); + return PyLong_FromLong((long)sid); +} +#endif /* HAVE_GETSID */ + + +#ifdef HAVE_SETSID +/*[clinic input] +os.setsid + +Call the system call setsid(). +[clinic start generated code]*/ + +static PyObject * +os_setsid_impl(PyObject *module) +/*[clinic end generated code: output=e2ddedd517086d77 input=5fff45858e2f0776]*/ +{ + if (setsid() < 0) + return posix_error(); + Py_RETURN_NONE; +} +#endif /* HAVE_SETSID */ + + +#ifdef HAVE_SETPGID +/*[clinic input] +os.setpgid + + pid: pid_t + pgrp: pid_t + / + +Call the system call setpgid(pid, pgrp). +[clinic start generated code]*/ + +static PyObject * +os_setpgid_impl(PyObject *module, pid_t pid, pid_t pgrp) +/*[clinic end generated code: output=6461160319a43d6a input=fceb395eca572e1a]*/ +{ + if (setpgid(pid, pgrp) < 0) + return posix_error(); + Py_RETURN_NONE; +} +#endif /* HAVE_SETPGID */ + + +#ifdef HAVE_TCGETPGRP +/*[clinic input] +os.tcgetpgrp + + fd: int + / + +Return the process group associated with the terminal specified by fd. +[clinic start generated code]*/ + +static PyObject * +os_tcgetpgrp_impl(PyObject *module, int fd) +/*[clinic end generated code: output=f865e88be86c272b input=7f6c18eac10ada86]*/ +{ + pid_t pgid = tcgetpgrp(fd); + if (pgid < 0) + return posix_error(); + return PyLong_FromPid(pgid); +} +#endif /* HAVE_TCGETPGRP */ + + +#ifdef HAVE_TCSETPGRP +/*[clinic input] +os.tcsetpgrp + + fd: int + pgid: pid_t + / + +Set the process group associated with the terminal specified by fd. +[clinic start generated code]*/ + +static PyObject * +os_tcsetpgrp_impl(PyObject *module, int fd, pid_t pgid) +/*[clinic end generated code: output=f1821a381b9daa39 input=5bdc997c6a619020]*/ +{ + if (tcsetpgrp(fd, pgid) < 0) + return posix_error(); + Py_RETURN_NONE; +} +#endif /* HAVE_TCSETPGRP */ + +/* Functions acting on file descriptors */ + +#ifdef O_CLOEXEC +extern int _Py_open_cloexec_works; +#endif + + +/*[clinic input] +os.open -> int + path: path_t + flags: int + mode: int = 0o777 + * + dir_fd: dir_fd(requires='openat') = None + +# "open(path, flags, mode=0o777, *, dir_fd=None)\n\n\ + +Open a file for low level IO. Returns a file descriptor (integer). + +If dir_fd is not None, it should be a file descriptor open to a directory, + and path should be relative; path will then be relative to that directory. +dir_fd may not be implemented on your platform. + If it is unavailable, using it will raise a NotImplementedError. +[clinic start generated code]*/ + +static int +os_open_impl(PyObject *module, path_t *path, int flags, int mode, int dir_fd) +/*[clinic end generated code: output=abc7227888c8bc73 input=ad8623b29acd2934]*/ +{ + int fd; + int async_err = 0; +#ifdef HAVE_OPENAT + int openat_unavailable = 0; +#endif + +#ifdef O_CLOEXEC + int *atomic_flag_works = &_Py_open_cloexec_works; +#elif !defined(MS_WINDOWS) + int *atomic_flag_works = NULL; +#endif + +#ifdef MS_WINDOWS + flags |= O_NOINHERIT; +#elif defined(O_CLOEXEC) + flags |= O_CLOEXEC; +#endif + + if (PySys_Audit("open", "OOi", path->object, Py_None, flags) < 0) { + return -1; + } + + _Py_BEGIN_SUPPRESS_IPH + do { + Py_BEGIN_ALLOW_THREADS +#ifdef MS_WINDOWS + fd = _wopen(path->wide, flags, mode); +#else +#ifdef HAVE_OPENAT + if (dir_fd != DEFAULT_DIR_FD) { + if (HAVE_OPENAT_RUNTIME) { + fd = openat(dir_fd, path->narrow, flags, mode); + + } else { + openat_unavailable = 1; + fd = -1; + } + } else +#endif /* HAVE_OPENAT */ + fd = open(path->narrow, flags, mode); +#endif /* !MS_WINDOWS */ + Py_END_ALLOW_THREADS + } while (fd < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + _Py_END_SUPPRESS_IPH + +#ifdef HAVE_OPENAT + if (openat_unavailable) { + argument_unavailable_error(NULL, "dir_fd"); + return -1; + } +#endif + + if (fd < 0) { + if (!async_err) + PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object); + return -1; + } + +#ifndef MS_WINDOWS + if (_Py_set_inheritable(fd, 0, atomic_flag_works) < 0) { + close(fd); + return -1; + } +#endif + + return fd; +} + + +/*[clinic input] +os.close + + fd: int + +Close a file descriptor. +[clinic start generated code]*/ + +static PyObject * +os_close_impl(PyObject *module, int fd) +/*[clinic end generated code: output=2fe4e93602822c14 input=2bc42451ca5c3223]*/ +{ + int res; + /* We do not want to retry upon EINTR: see http://lwn.net/Articles/576478/ + * and http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html + * for more details. + */ + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH + res = close(fd); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + if (res < 0) + return posix_error(); + Py_RETURN_NONE; +} + +/*[clinic input] +os.closerange + + fd_low: int + fd_high: int + / + +Closes all file descriptors in [fd_low, fd_high), ignoring errors. +[clinic start generated code]*/ + +static PyObject * +os_closerange_impl(PyObject *module, int fd_low, int fd_high) +/*[clinic end generated code: output=0ce5c20fcda681c2 input=5855a3d053ebd4ec]*/ +{ + Py_BEGIN_ALLOW_THREADS + _Py_closerange(fd_low, fd_high - 1); + Py_END_ALLOW_THREADS + Py_RETURN_NONE; +} + + +/*[clinic input] +os.dup -> int + + fd: int + / + +Return a duplicate of a file descriptor. +[clinic start generated code]*/ + +static int +os_dup_impl(PyObject *module, int fd) +/*[clinic end generated code: output=486f4860636b2a9f input=6f10f7ea97f7852a]*/ +{ + return _Py_dup(fd); +} + +// dup2() is either provided by libc or dup2.c with AC_REPLACE_FUNCS(). +// dup2.c provides working dup2() if and only if F_DUPFD is available. +#if (defined(HAVE_DUP3) || defined(F_DUPFD) || defined(MS_WINDOWS)) +/*[clinic input] +os.dup2 -> int + fd: int + fd2: int + inheritable: bool=True + +Duplicate file descriptor. +[clinic start generated code]*/ + +static int +os_dup2_impl(PyObject *module, int fd, int fd2, int inheritable) +/*[clinic end generated code: output=bc059d34a73404d1 input=c3cddda8922b038d]*/ +{ + int res = 0; +#if defined(HAVE_DUP3) && \ + !(defined(HAVE_FCNTL_H) && defined(F_DUP2FD_CLOEXEC)) + /* dup3() is available on Linux 2.6.27+ and glibc 2.9 */ + static int dup3_works = -1; +#endif + + /* dup2() can fail with EINTR if the target FD is already open, because it + * then has to be closed. See os_close_impl() for why we don't handle EINTR + * upon close(), and therefore below. + */ +#ifdef MS_WINDOWS + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH + res = dup2(fd, fd2); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + if (res < 0) { + posix_error(); + return -1; + } + res = fd2; // msvcrt dup2 returns 0 on success. + + /* Character files like console cannot be make non-inheritable */ + if (!inheritable && _Py_set_inheritable(fd2, 0, NULL) < 0) { + close(fd2); + return -1; + } + +#elif defined(HAVE_FCNTL_H) && defined(F_DUP2FD_CLOEXEC) + Py_BEGIN_ALLOW_THREADS + if (!inheritable) + res = fcntl(fd, F_DUP2FD_CLOEXEC, fd2); + else + res = dup2(fd, fd2); + Py_END_ALLOW_THREADS + if (res < 0) { + posix_error(); + return -1; + } + +#else + +#ifdef HAVE_DUP3 + if (!inheritable && dup3_works != 0) { + Py_BEGIN_ALLOW_THREADS + res = dup3(fd, fd2, O_CLOEXEC); + Py_END_ALLOW_THREADS + if (res < 0) { + if (dup3_works == -1) + dup3_works = (errno != ENOSYS); + if (dup3_works) { + posix_error(); + return -1; + } + } + } + + if (inheritable || dup3_works == 0) + { +#endif + Py_BEGIN_ALLOW_THREADS + res = dup2(fd, fd2); + Py_END_ALLOW_THREADS + if (res < 0) { + posix_error(); + return -1; + } + + if (!inheritable && _Py_set_inheritable(fd2, 0, NULL) < 0) { + close(fd2); + return -1; + } +#ifdef HAVE_DUP3 + } +#endif + +#endif + + return res; +} +#endif + + +#ifdef HAVE_LOCKF +/*[clinic input] +os.lockf + + fd: int + An open file descriptor. + command: int + One of F_LOCK, F_TLOCK, F_ULOCK or F_TEST. + length: Py_off_t + The number of bytes to lock, starting at the current position. + / + +Apply, test or remove a POSIX lock on an open file descriptor. + +[clinic start generated code]*/ + +static PyObject * +os_lockf_impl(PyObject *module, int fd, int command, Py_off_t length) +/*[clinic end generated code: output=af7051f3e7c29651 input=65da41d2106e9b79]*/ +{ + int res; + + if (PySys_Audit("os.lockf", "iiL", fd, command, length) < 0) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + res = lockf(fd, command, length); + Py_END_ALLOW_THREADS + + if (res < 0) + return posix_error(); + + Py_RETURN_NONE; +} +#endif /* HAVE_LOCKF */ + + +/*[clinic input] +os.lseek -> Py_off_t + + fd: int + An open file descriptor, as returned by os.open(). + position: Py_off_t + Position, interpreted relative to 'whence'. + whence as how: int + The relative position to seek from. Valid values are: + - SEEK_SET: seek from the start of the file. + - SEEK_CUR: seek from the current file position. + - SEEK_END: seek from the end of the file. + / + +Set the position of a file descriptor. Return the new position. + +The return value is the number of bytes relative to the beginning of the file. +[clinic start generated code]*/ + +static Py_off_t +os_lseek_impl(PyObject *module, int fd, Py_off_t position, int how) +/*[clinic end generated code: output=971e1efb6b30bd2f input=f096e754c5367504]*/ +{ + Py_off_t result; + +#ifdef SEEK_SET + /* Turn 0, 1, 2 into SEEK_{SET,CUR,END} */ + switch (how) { + case 0: how = SEEK_SET; break; + case 1: how = SEEK_CUR; break; + case 2: how = SEEK_END; break; + } +#endif /* SEEK_END */ + + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH +#ifdef MS_WINDOWS + result = _lseeki64(fd, position, how); +#else + result = lseek(fd, position, how); +#endif + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + if (result < 0) + posix_error(); + + return result; +} + + +/*[clinic input] +os.read + fd: int + length: Py_ssize_t + / + +Read from a file descriptor. Returns a bytes object. +[clinic start generated code]*/ + +static PyObject * +os_read_impl(PyObject *module, int fd, Py_ssize_t length) +/*[clinic end generated code: output=dafbe9a5cddb987b input=1df2eaa27c0bf1d3]*/ +{ + Py_ssize_t n; + PyObject *buffer; + + if (length < 0) { + errno = EINVAL; + return posix_error(); + } + + length = Py_MIN(length, _PY_READ_MAX); + + buffer = PyBytes_FromStringAndSize((char *)NULL, length); + if (buffer == NULL) + return NULL; + + n = _Py_read(fd, PyBytes_AS_STRING(buffer), length); + if (n == -1) { + Py_DECREF(buffer); + return NULL; + } + + if (n != length) + _PyBytes_Resize(&buffer, n); + + return buffer; +} + +#if (defined(HAVE_SENDFILE) && (defined(__FreeBSD__) || defined(__DragonFly__) \ + || defined(__APPLE__))) \ + || defined(HAVE_READV) || defined(HAVE_PREADV) || defined (HAVE_PREADV2) \ + || defined(HAVE_WRITEV) || defined(HAVE_PWRITEV) || defined (HAVE_PWRITEV2) +static int +iov_setup(struct iovec **iov, Py_buffer **buf, PyObject *seq, Py_ssize_t cnt, int type) +{ + Py_ssize_t i, j; + + *iov = PyMem_New(struct iovec, cnt); + if (*iov == NULL) { + PyErr_NoMemory(); + return -1; + } + + *buf = PyMem_New(Py_buffer, cnt); + if (*buf == NULL) { + PyMem_Free(*iov); + PyErr_NoMemory(); + return -1; + } + + for (i = 0; i < cnt; i++) { + PyObject *item = PySequence_GetItem(seq, i); + if (item == NULL) + goto fail; + if (PyObject_GetBuffer(item, &(*buf)[i], type) == -1) { + Py_DECREF(item); + goto fail; + } + Py_DECREF(item); + (*iov)[i].iov_base = (*buf)[i].buf; + (*iov)[i].iov_len = (*buf)[i].len; + } + return 0; + +fail: + PyMem_Free(*iov); + for (j = 0; j < i; j++) { + PyBuffer_Release(&(*buf)[j]); + } + PyMem_Free(*buf); + return -1; +} + +static void +iov_cleanup(struct iovec *iov, Py_buffer *buf, int cnt) +{ + int i; + PyMem_Free(iov); + for (i = 0; i < cnt; i++) { + PyBuffer_Release(&buf[i]); + } + PyMem_Free(buf); +} +#endif + + +#ifdef HAVE_READV +/*[clinic input] +os.readv -> Py_ssize_t + + fd: int + buffers: object + / + +Read from a file descriptor fd into an iterable of buffers. + +The buffers should be mutable buffers accepting bytes. +readv will transfer data into each buffer until it is full +and then move on to the next buffer in the sequence to hold +the rest of the data. + +readv returns the total number of bytes read, +which may be less than the total capacity of all the buffers. +[clinic start generated code]*/ + +static Py_ssize_t +os_readv_impl(PyObject *module, int fd, PyObject *buffers) +/*[clinic end generated code: output=792da062d3fcebdb input=e679eb5dbfa0357d]*/ +{ + Py_ssize_t cnt, n; + int async_err = 0; + struct iovec *iov; + Py_buffer *buf; + + if (!PySequence_Check(buffers)) { + PyErr_SetString(PyExc_TypeError, + "readv() arg 2 must be a sequence"); + return -1; + } + + cnt = PySequence_Size(buffers); + if (cnt < 0) + return -1; + + if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_WRITABLE) < 0) + return -1; + + do { + Py_BEGIN_ALLOW_THREADS + n = readv(fd, iov, cnt); + Py_END_ALLOW_THREADS + } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + + int saved_errno = errno; + iov_cleanup(iov, buf, cnt); + if (n < 0) { + if (!async_err) { + errno = saved_errno; + posix_error(); + } + return -1; + } + + return n; +} +#endif /* HAVE_READV */ + + +#ifdef HAVE_PREAD +/*[clinic input] +os.pread + + fd: int + length: Py_ssize_t + offset: Py_off_t + / + +Read a number of bytes from a file descriptor starting at a particular offset. + +Read length bytes from file descriptor fd, starting at offset bytes from +the beginning of the file. The file offset remains unchanged. +[clinic start generated code]*/ + +static PyObject * +os_pread_impl(PyObject *module, int fd, Py_ssize_t length, Py_off_t offset) +/*[clinic end generated code: output=3f875c1eef82e32f input=85cb4a5589627144]*/ +{ + Py_ssize_t n; + int async_err = 0; + PyObject *buffer; + + if (length < 0) { + errno = EINVAL; + return posix_error(); + } + buffer = PyBytes_FromStringAndSize((char *)NULL, length); + if (buffer == NULL) + return NULL; + + do { + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH + n = pread(fd, PyBytes_AS_STRING(buffer), length, offset); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + + if (n < 0) { + if (!async_err) { + posix_error(); + } + Py_DECREF(buffer); + return NULL; + } + if (n != length) + _PyBytes_Resize(&buffer, n); + return buffer; +} +#endif /* HAVE_PREAD */ + +#if defined(HAVE_PREADV) || defined (HAVE_PREADV2) +/*[clinic input] +os.preadv -> Py_ssize_t + + fd: int + buffers: object + offset: Py_off_t + flags: int = 0 + / + +Reads from a file descriptor into a number of mutable bytes-like objects. + +Combines the functionality of readv() and pread(). As readv(), it will +transfer data into each buffer until it is full and then move on to the next +buffer in the sequence to hold the rest of the data. Its fourth argument, +specifies the file offset at which the input operation is to be performed. It +will return the total number of bytes read (which can be less than the total +capacity of all the objects). + +The flags argument contains a bitwise OR of zero or more of the following flags: + +- RWF_HIPRI +- RWF_NOWAIT + +Using non-zero flags requires Linux 4.6 or newer. +[clinic start generated code]*/ + +static Py_ssize_t +os_preadv_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset, + int flags) +/*[clinic end generated code: output=26fc9c6e58e7ada5 input=4173919dc1f7ed99]*/ +{ + Py_ssize_t cnt, n; + int async_err = 0; + struct iovec *iov; + Py_buffer *buf; + + if (!PySequence_Check(buffers)) { + PyErr_SetString(PyExc_TypeError, + "preadv2() arg 2 must be a sequence"); + return -1; + } + + cnt = PySequence_Size(buffers); + if (cnt < 0) { + return -1; + } + +#ifndef HAVE_PREADV2 + if(flags != 0) { + argument_unavailable_error("preadv2", "flags"); + return -1; + } +#endif + + if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_WRITABLE) < 0) { + return -1; + } +#ifdef HAVE_PREADV2 + do { + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH + n = preadv2(fd, iov, cnt, offset, flags); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); +#else + do { +#if defined(__APPLE__) && defined(__clang__) +/* This entire function will be removed from the module dict when the API + * is not available. + */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" +#pragma clang diagnostic ignored "-Wunguarded-availability-new" +#endif + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH + n = preadv(fd, iov, cnt, offset); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + +#if defined(__APPLE__) && defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif + + int saved_errno = errno; + iov_cleanup(iov, buf, cnt); + if (n < 0) { + if (!async_err) { + errno = saved_errno; + posix_error(); + } + return -1; + } + + return n; +} +#endif /* HAVE_PREADV */ + + +/*[clinic input] +os.write -> Py_ssize_t + + fd: int + data: Py_buffer + / + +Write a bytes object to a file descriptor. +[clinic start generated code]*/ + +static Py_ssize_t +os_write_impl(PyObject *module, int fd, Py_buffer *data) +/*[clinic end generated code: output=e4ef5bc904b58ef9 input=3207e28963234f3c]*/ +{ + return _Py_write(fd, data->buf, data->len); +} + +#ifdef HAVE_SENDFILE +#ifdef __APPLE__ +/*[clinic input] +os.sendfile + + out_fd: int + in_fd: int + offset: Py_off_t + count as sbytes: Py_off_t + headers: object(c_default="NULL") = () + trailers: object(c_default="NULL") = () + flags: int = 0 + +Copy count bytes from file descriptor in_fd to file descriptor out_fd. +[clinic start generated code]*/ + +static PyObject * +os_sendfile_impl(PyObject *module, int out_fd, int in_fd, Py_off_t offset, + Py_off_t sbytes, PyObject *headers, PyObject *trailers, + int flags) +/*[clinic end generated code: output=81c4bcd143f5c82b input=b0d72579d4c69afa]*/ +#elif defined(__FreeBSD__) || defined(__DragonFly__) +/*[clinic input] +os.sendfile + + out_fd: int + in_fd: int + offset: Py_off_t + count: Py_ssize_t + headers: object(c_default="NULL") = () + trailers: object(c_default="NULL") = () + flags: int = 0 + +Copy count bytes from file descriptor in_fd to file descriptor out_fd. +[clinic start generated code]*/ + +static PyObject * +os_sendfile_impl(PyObject *module, int out_fd, int in_fd, Py_off_t offset, + Py_ssize_t count, PyObject *headers, PyObject *trailers, + int flags) +/*[clinic end generated code: output=329ea009bdd55afc input=338adb8ff84ae8cd]*/ +#else +/*[clinic input] +os.sendfile + + out_fd: int + in_fd: int + offset as offobj: object + count: Py_ssize_t + +Copy count bytes from file descriptor in_fd to file descriptor out_fd. +[clinic start generated code]*/ + +static PyObject * +os_sendfile_impl(PyObject *module, int out_fd, int in_fd, PyObject *offobj, + Py_ssize_t count) +/*[clinic end generated code: output=ae81216e40f167d8 input=76d64058c74477ba]*/ +#endif +{ + Py_ssize_t ret; + int async_err = 0; + +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__) +#ifndef __APPLE__ + off_t sbytes; +#endif + Py_buffer *hbuf, *tbuf; + struct sf_hdtr sf; + + sf.headers = NULL; + sf.trailers = NULL; + + if (headers != NULL) { + if (!PySequence_Check(headers)) { + PyErr_SetString(PyExc_TypeError, + "sendfile() headers must be a sequence"); + return NULL; + } else { + Py_ssize_t i = PySequence_Size(headers); + if (i < 0) + return NULL; + if (i > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "sendfile() header is too large"); + return NULL; + } + if (i > 0) { + sf.hdr_cnt = (int)i; + if (iov_setup(&(sf.headers), &hbuf, + headers, sf.hdr_cnt, PyBUF_SIMPLE) < 0) + return NULL; +#ifdef __APPLE__ + for (i = 0; i < sf.hdr_cnt; i++) { + Py_ssize_t blen = sf.headers[i].iov_len; +# define OFF_T_MAX 0x7fffffffffffffff + if (sbytes >= OFF_T_MAX - blen) { + PyErr_SetString(PyExc_OverflowError, + "sendfile() header is too large"); + return NULL; + } + sbytes += blen; + } +#endif + } + } + } + if (trailers != NULL) { + if (!PySequence_Check(trailers)) { + PyErr_SetString(PyExc_TypeError, + "sendfile() trailers must be a sequence"); + return NULL; + } else { + Py_ssize_t i = PySequence_Size(trailers); + if (i < 0) + return NULL; + if (i > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "sendfile() trailer is too large"); + return NULL; + } + if (i > 0) { + sf.trl_cnt = (int)i; + if (iov_setup(&(sf.trailers), &tbuf, + trailers, sf.trl_cnt, PyBUF_SIMPLE) < 0) + return NULL; + } + } + } + + _Py_BEGIN_SUPPRESS_IPH + do { + Py_BEGIN_ALLOW_THREADS +#ifdef __APPLE__ + ret = sendfile(in_fd, out_fd, offset, &sbytes, &sf, flags); +#else + ret = sendfile(in_fd, out_fd, offset, count, &sf, &sbytes, flags); +#endif + Py_END_ALLOW_THREADS + } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + _Py_END_SUPPRESS_IPH + + int saved_errno = errno; + if (sf.headers != NULL) + iov_cleanup(sf.headers, hbuf, sf.hdr_cnt); + if (sf.trailers != NULL) + iov_cleanup(sf.trailers, tbuf, sf.trl_cnt); + + if (ret < 0) { + if ((saved_errno == EAGAIN) || (saved_errno == EBUSY)) { + if (sbytes != 0) { + // some data has been sent + goto done; + } + // no data has been sent; upper application is supposed + // to retry on EAGAIN or EBUSY + } + if (!async_err) { + errno = saved_errno; + posix_error(); + } + return NULL; + } + goto done; + +done: + #if !defined(HAVE_LARGEFILE_SUPPORT) + return PyLong_FromLong(sbytes); + #else + return PyLong_FromLongLong(sbytes); + #endif + +#else +#ifdef __linux__ + if (offobj == Py_None) { + do { + Py_BEGIN_ALLOW_THREADS + ret = sendfile(out_fd, in_fd, NULL, count); + Py_END_ALLOW_THREADS + } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + if (ret < 0) + return (!async_err) ? posix_error() : NULL; + return PyLong_FromSsize_t(ret); + } +#endif + off_t offset; + if (!Py_off_t_converter(offobj, &offset)) + return NULL; + +#if defined(__sun) && defined(__SVR4) + // On Solaris, sendfile raises EINVAL rather than returning 0 + // when the offset is equal or bigger than the in_fd size. + struct stat st; + + do { + Py_BEGIN_ALLOW_THREADS + ret = fstat(in_fd, &st); + Py_END_ALLOW_THREADS + } while (ret != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + if (ret < 0) + return (!async_err) ? posix_error() : NULL; + + if (offset >= st.st_size) { + return PyLong_FromLong(0); + } + + // On illumos specifically sendfile() may perform a partial write but + // return -1/an error (in one confirmed case the destination socket + // had a 5 second timeout set and errno was EAGAIN) and it's on the client + // code to check if the offset parameter was modified by sendfile(). + // + // We need this variable to track said change. + off_t original_offset = offset; +#endif + + do { + Py_BEGIN_ALLOW_THREADS + ret = sendfile(out_fd, in_fd, &offset, count); +#if defined(__sun) && defined(__SVR4) + // This handles illumos-specific sendfile() partial write behavior, + // see a comment above for more details. + if (ret < 0 && offset != original_offset) { + ret = offset - original_offset; + } +#endif + Py_END_ALLOW_THREADS + } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + if (ret < 0) + return (!async_err) ? posix_error() : NULL; + return PyLong_FromSsize_t(ret); +#endif +} +#endif /* HAVE_SENDFILE */ + + +#if defined(__APPLE__) +/*[clinic input] +os._fcopyfile + + in_fd: int + out_fd: int + flags: int + / + +Efficiently copy content or metadata of 2 regular file descriptors (macOS). +[clinic start generated code]*/ + +static PyObject * +os__fcopyfile_impl(PyObject *module, int in_fd, int out_fd, int flags) +/*[clinic end generated code: output=c9d1a35a992e401b input=1e34638a86948795]*/ +{ + int ret; + + Py_BEGIN_ALLOW_THREADS + ret = fcopyfile(in_fd, out_fd, NULL, flags); + Py_END_ALLOW_THREADS + if (ret < 0) + return posix_error(); + Py_RETURN_NONE; +} +#endif + + +/*[clinic input] +os.fstat + + fd : int + +Perform a stat system call on the given file descriptor. + +Like stat(), but for an open file descriptor. +Equivalent to os.stat(fd). +[clinic start generated code]*/ + +static PyObject * +os_fstat_impl(PyObject *module, int fd) +/*[clinic end generated code: output=efc038cb5f654492 input=27e0e0ebbe5600c9]*/ +{ + STRUCT_STAT st; + int res; + int async_err = 0; + + do { + Py_BEGIN_ALLOW_THREADS + res = FSTAT(fd, &st); + Py_END_ALLOW_THREADS + } while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + if (res != 0) { +#ifdef MS_WINDOWS + return PyErr_SetFromWindowsErr(0); +#else + return (!async_err) ? posix_error() : NULL; +#endif + } + + return _pystat_fromstructstat(module, &st); +} + + +/*[clinic input] +os.isatty -> bool + fd: int + / + +Return True if the fd is connected to a terminal. + +Return True if the file descriptor is an open file descriptor +connected to the slave end of a terminal. +[clinic start generated code]*/ + +static int +os_isatty_impl(PyObject *module, int fd) +/*[clinic end generated code: output=6a48c8b4e644ca00 input=08ce94aa1eaf7b5e]*/ +{ + int return_value; + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH + return_value = isatty(fd) || is_cygpty(fd); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + return return_value; +} + + +#ifdef HAVE_PIPE +/*[clinic input] +os.pipe + +Create a pipe. + +Returns a tuple of two file descriptors: + (read_fd, write_fd) +[clinic start generated code]*/ + +static PyObject * +os_pipe_impl(PyObject *module) +/*[clinic end generated code: output=ff9b76255793b440 input=02535e8c8fa6c4d4]*/ +{ + int fds[2]; +#ifdef MS_WINDOWS + HANDLE read, write; + SECURITY_ATTRIBUTES attr; + BOOL ok; +#else + int res; +#endif + +#ifdef MS_WINDOWS + attr.nLength = sizeof(attr); + attr.lpSecurityDescriptor = NULL; + attr.bInheritHandle = FALSE; + + Py_BEGIN_ALLOW_THREADS + ok = CreatePipe(&read, &write, &attr, 0); + if (ok) { + fds[0] = _Py_open_osfhandle_noraise(read, _O_RDONLY | _O_NOINHERIT); + fds[1] = _Py_open_osfhandle_noraise(write, _O_WRONLY | _O_NOINHERIT); + if (fds[0] == -1 || fds[1] == -1) { + CloseHandle(read); + CloseHandle(write); + ok = 0; + } + } + Py_END_ALLOW_THREADS + + if (!ok) + return PyErr_SetFromWindowsErr(0); +#else + +#ifdef HAVE_PIPE2 + Py_BEGIN_ALLOW_THREADS + res = pipe2(fds, O_CLOEXEC); + Py_END_ALLOW_THREADS + + if (res != 0 && errno == ENOSYS) + { +#endif + Py_BEGIN_ALLOW_THREADS + res = pipe(fds); + Py_END_ALLOW_THREADS + + if (res == 0) { + if (_Py_set_inheritable(fds[0], 0, NULL) < 0) { + close(fds[0]); + close(fds[1]); + return NULL; + } + if (_Py_set_inheritable(fds[1], 0, NULL) < 0) { + close(fds[0]); + close(fds[1]); + return NULL; + } + } +#ifdef HAVE_PIPE2 + } +#endif + + if (res != 0) + return PyErr_SetFromErrno(PyExc_OSError); +#endif /* !MS_WINDOWS */ + return Py_BuildValue("(ii)", fds[0], fds[1]); +} +#endif /* HAVE_PIPE */ + + +#ifdef HAVE_PIPE2 +/*[clinic input] +os.pipe2 + + flags: int + / + +Create a pipe with flags set atomically. + +Returns a tuple of two file descriptors: + (read_fd, write_fd) + +flags can be constructed by ORing together one or more of these values: +O_NONBLOCK, O_CLOEXEC. +[clinic start generated code]*/ + +static PyObject * +os_pipe2_impl(PyObject *module, int flags) +/*[clinic end generated code: output=25751fb43a45540f input=f261b6e7e63c6817]*/ +{ + int fds[2]; + int res; + + res = pipe2(fds, flags); + if (res != 0) + return posix_error(); + return Py_BuildValue("(ii)", fds[0], fds[1]); +} +#endif /* HAVE_PIPE2 */ + + +#ifdef HAVE_WRITEV +/*[clinic input] +os.writev -> Py_ssize_t + fd: int + buffers: object + / + +Iterate over buffers, and write the contents of each to a file descriptor. + +Returns the total number of bytes written. +buffers must be a sequence of bytes-like objects. +[clinic start generated code]*/ + +static Py_ssize_t +os_writev_impl(PyObject *module, int fd, PyObject *buffers) +/*[clinic end generated code: output=56565cfac3aac15b input=5b8d17fe4189d2fe]*/ +{ + Py_ssize_t cnt; + Py_ssize_t result; + int async_err = 0; + struct iovec *iov; + Py_buffer *buf; + + if (!PySequence_Check(buffers)) { + PyErr_SetString(PyExc_TypeError, + "writev() arg 2 must be a sequence"); + return -1; + } + cnt = PySequence_Size(buffers); + if (cnt < 0) + return -1; + + if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_SIMPLE) < 0) { + return -1; + } + + do { + Py_BEGIN_ALLOW_THREADS + result = writev(fd, iov, cnt); + Py_END_ALLOW_THREADS + } while (result < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + + if (result < 0 && !async_err) + posix_error(); + + iov_cleanup(iov, buf, cnt); + return result; +} +#endif /* HAVE_WRITEV */ + + +#ifdef HAVE_PWRITE +/*[clinic input] +os.pwrite -> Py_ssize_t + + fd: int + buffer: Py_buffer + offset: Py_off_t + / + +Write bytes to a file descriptor starting at a particular offset. + +Write buffer to fd, starting at offset bytes from the beginning of +the file. Returns the number of bytes written. Does not change the +current file offset. +[clinic start generated code]*/ + +static Py_ssize_t +os_pwrite_impl(PyObject *module, int fd, Py_buffer *buffer, Py_off_t offset) +/*[clinic end generated code: output=c74da630758ee925 input=614acbc7e5a0339a]*/ +{ + Py_ssize_t size; + int async_err = 0; + + do { + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH + size = pwrite(fd, buffer->buf, (size_t)buffer->len, offset); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + } while (size < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + + if (size < 0 && !async_err) + posix_error(); + return size; +} +#endif /* HAVE_PWRITE */ + +#if defined(HAVE_PWRITEV) || defined (HAVE_PWRITEV2) +/*[clinic input] +os.pwritev -> Py_ssize_t + + fd: int + buffers: object + offset: Py_off_t + flags: int = 0 + / + +Writes the contents of bytes-like objects to a file descriptor at a given offset. + +Combines the functionality of writev() and pwrite(). All buffers must be a sequence +of bytes-like objects. Buffers are processed in array order. Entire contents of first +buffer is written before proceeding to second, and so on. The operating system may +set a limit (sysconf() value SC_IOV_MAX) on the number of buffers that can be used. +This function writes the contents of each object to the file descriptor and returns +the total number of bytes written. + +The flags argument contains a bitwise OR of zero or more of the following flags: + +- RWF_DSYNC +- RWF_SYNC +- RWF_APPEND + +Using non-zero flags requires Linux 4.7 or newer. +[clinic start generated code]*/ + +static Py_ssize_t +os_pwritev_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset, + int flags) +/*[clinic end generated code: output=e3dd3e9d11a6a5c7 input=35358c327e1a2a8e]*/ +{ + Py_ssize_t cnt; + Py_ssize_t result; + int async_err = 0; + struct iovec *iov; + Py_buffer *buf; + + if (!PySequence_Check(buffers)) { + PyErr_SetString(PyExc_TypeError, + "pwritev() arg 2 must be a sequence"); + return -1; + } + + cnt = PySequence_Size(buffers); + if (cnt < 0) { + return -1; + } + +#ifndef HAVE_PWRITEV2 + if(flags != 0) { + argument_unavailable_error("pwritev2", "flags"); + return -1; + } +#endif + + if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_SIMPLE) < 0) { + return -1; + } +#ifdef HAVE_PWRITEV2 + do { + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH + result = pwritev2(fd, iov, cnt, offset, flags); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + } while (result < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); +#else + +#if defined(__APPLE__) && defined(__clang__) +/* This entire function will be removed from the module dict when the API + * is not available. + */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" +#pragma clang diagnostic ignored "-Wunguarded-availability-new" +#endif + do { + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH + result = pwritev(fd, iov, cnt, offset); + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + } while (result < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + +#if defined(__APPLE__) && defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif + + if (result < 0) { + if (!async_err) { + posix_error(); + } + result = -1; + } + iov_cleanup(iov, buf, cnt); + + return result; +} +#endif /* HAVE_PWRITEV */ + +#ifdef HAVE_COPY_FILE_RANGE +/*[clinic input] + +os.copy_file_range + src: int + Source file descriptor. + dst: int + Destination file descriptor. + count: Py_ssize_t + Number of bytes to copy. + offset_src: object = None + Starting offset in src. + offset_dst: object = None + Starting offset in dst. + +Copy count bytes from one file descriptor to another. + +If offset_src is None, then src is read from the current position; +respectively for offset_dst. +[clinic start generated code]*/ + +static PyObject * +os_copy_file_range_impl(PyObject *module, int src, int dst, Py_ssize_t count, + PyObject *offset_src, PyObject *offset_dst) +/*[clinic end generated code: output=1a91713a1d99fc7a input=42fdce72681b25a9]*/ +{ + off_t offset_src_val, offset_dst_val; + off_t *p_offset_src = NULL; + off_t *p_offset_dst = NULL; + Py_ssize_t ret; + int async_err = 0; + /* The flags argument is provided to allow + * for future extensions and currently must be to 0. */ + int flags = 0; + + + if (count < 0) { + PyErr_SetString(PyExc_ValueError, "negative value for 'count' not allowed"); + return NULL; + } + + if (offset_src != Py_None) { + if (!Py_off_t_converter(offset_src, &offset_src_val)) { + return NULL; + } + p_offset_src = &offset_src_val; + } + + if (offset_dst != Py_None) { + if (!Py_off_t_converter(offset_dst, &offset_dst_val)) { + return NULL; + } + p_offset_dst = &offset_dst_val; + } + + do { + Py_BEGIN_ALLOW_THREADS + ret = copy_file_range(src, p_offset_src, dst, p_offset_dst, count, flags); + Py_END_ALLOW_THREADS + } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + + if (ret < 0) { + return (!async_err) ? posix_error() : NULL; + } + + return PyLong_FromSsize_t(ret); +} +#endif /* HAVE_COPY_FILE_RANGE*/ + +#if (defined(HAVE_SPLICE) && !defined(_AIX)) +/*[clinic input] + +os.splice + src: int + Source file descriptor. + dst: int + Destination file descriptor. + count: Py_ssize_t + Number of bytes to copy. + offset_src: object = None + Starting offset in src. + offset_dst: object = None + Starting offset in dst. + flags: unsigned_int = 0 + Flags to modify the semantics of the call. + +Transfer count bytes from one pipe to a descriptor or vice versa. + +If offset_src is None, then src is read from the current position; +respectively for offset_dst. The offset associated to the file +descriptor that refers to a pipe must be None. +[clinic start generated code]*/ + +static PyObject * +os_splice_impl(PyObject *module, int src, int dst, Py_ssize_t count, + PyObject *offset_src, PyObject *offset_dst, + unsigned int flags) +/*[clinic end generated code: output=d0386f25a8519dc5 input=047527c66c6d2e0a]*/ +{ + off_t offset_src_val, offset_dst_val; + off_t *p_offset_src = NULL; + off_t *p_offset_dst = NULL; + Py_ssize_t ret; + int async_err = 0; + + if (count < 0) { + PyErr_SetString(PyExc_ValueError, "negative value for 'count' not allowed"); + return NULL; + } + + if (offset_src != Py_None) { + if (!Py_off_t_converter(offset_src, &offset_src_val)) { + return NULL; + } + p_offset_src = &offset_src_val; + } + + if (offset_dst != Py_None) { + if (!Py_off_t_converter(offset_dst, &offset_dst_val)) { + return NULL; + } + p_offset_dst = &offset_dst_val; + } + + do { + Py_BEGIN_ALLOW_THREADS + ret = splice(src, p_offset_src, dst, p_offset_dst, count, flags); + Py_END_ALLOW_THREADS + } while (ret < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); + + if (ret < 0) { + return (!async_err) ? posix_error() : NULL; + } + + return PyLong_FromSsize_t(ret); +} +#endif /* HAVE_SPLICE*/ + +#ifdef HAVE_MKFIFO +/*[clinic input] +os.mkfifo + + path: path_t + mode: int=0o666 + * + dir_fd: dir_fd(requires='mkfifoat')=None + +Create a "fifo" (a POSIX named pipe). + +If dir_fd is not None, it should be a file descriptor open to a directory, + and path should be relative; path will then be relative to that directory. +dir_fd may not be implemented on your platform. + If it is unavailable, using it will raise a NotImplementedError. +[clinic start generated code]*/ + +static PyObject * +os_mkfifo_impl(PyObject *module, path_t *path, int mode, int dir_fd) +/*[clinic end generated code: output=ce41cfad0e68c940 input=73032e98a36e0e19]*/ +{ + int result; + int async_err = 0; +#ifdef HAVE_MKFIFOAT + int mkfifoat_unavailable = 0; +#endif + + do { + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_MKFIFOAT + if (dir_fd != DEFAULT_DIR_FD) { + if (HAVE_MKFIFOAT_RUNTIME) { + result = mkfifoat(dir_fd, path->narrow, mode); + + } else { + mkfifoat_unavailable = 1; + result = 0; + } + } else +#endif + result = mkfifo(path->narrow, mode); + Py_END_ALLOW_THREADS + } while (result != 0 && errno == EINTR && + !(async_err = PyErr_CheckSignals())); + +#ifdef HAVE_MKFIFOAT + if (mkfifoat_unavailable) { + argument_unavailable_error(NULL, "dir_fd"); + return NULL; + } +#endif + + if (result != 0) + return (!async_err) ? posix_error() : NULL; + + Py_RETURN_NONE; +} +#endif /* HAVE_MKFIFO */ + + +#if defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) +/*[clinic input] +os.mknod + + path: path_t + mode: int=0o600 + device: dev_t=0 + * + dir_fd: dir_fd(requires='mknodat')=None + +Create a node in the file system. + +Create a node in the file system (file, device special file or named pipe) +at path. mode specifies both the permissions to use and the +type of node to be created, being combined (bitwise OR) with one of +S_IFREG, S_IFCHR, S_IFBLK, and S_IFIFO. If S_IFCHR or S_IFBLK is set on mode, +device defines the newly created device special file (probably using +os.makedev()). Otherwise device is ignored. + +If dir_fd is not None, it should be a file descriptor open to a directory, + and path should be relative; path will then be relative to that directory. +dir_fd may not be implemented on your platform. + If it is unavailable, using it will raise a NotImplementedError. +[clinic start generated code]*/ + +static PyObject * +os_mknod_impl(PyObject *module, path_t *path, int mode, dev_t device, + int dir_fd) +/*[clinic end generated code: output=92e55d3ca8917461 input=ee44531551a4d83b]*/ +{ + int result; + int async_err = 0; +#ifdef HAVE_MKNODAT + int mknodat_unavailable = 0; +#endif + + do { + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_MKNODAT + if (dir_fd != DEFAULT_DIR_FD) { + if (HAVE_MKNODAT_RUNTIME) { + result = mknodat(dir_fd, path->narrow, mode, device); + + } else { + mknodat_unavailable = 1; + result = 0; + } + } else +#endif + result = mknod(path->narrow, mode, device); + Py_END_ALLOW_THREADS + } while (result != 0 && errno == EINTR && + !(async_err = PyErr_CheckSignals())); +#ifdef HAVE_MKNODAT + if (mknodat_unavailable) { + argument_unavailable_error(NULL, "dir_fd"); + return NULL; + } +#endif + if (result != 0) + return (!async_err) ? posix_error() : NULL; + + Py_RETURN_NONE; +} +#endif /* defined(HAVE_MKNOD) && defined(HAVE_MAKEDEV) */ + + +#ifdef HAVE_DEVICE_MACROS +static PyObject * +major_minor_conv(unsigned int value) +{ +#ifdef NODEV + if (value == (unsigned int)NODEV) { + return PyLong_FromLong((int)NODEV); + } +#endif + return PyLong_FromUnsignedLong(value); +} + +static int +major_minor_check(dev_t value) +{ +#ifdef NODEV + if (value == NODEV) { + return 1; + } +#endif + return (dev_t)(unsigned int)value == value; +} + +/*[clinic input] +os.major + + device: dev_t + / + +Extracts a device major number from a raw device number. +[clinic start generated code]*/ + +static PyObject * +os_major_impl(PyObject *module, dev_t device) +/*[clinic end generated code: output=4071ffee17647891 input=b1a0a14ec9448229]*/ +{ + return major_minor_conv(major(device)); +} + + +/*[clinic input] +os.minor + + device: dev_t + / + +Extracts a device minor number from a raw device number. +[clinic start generated code]*/ + +static PyObject * +os_minor_impl(PyObject *module, dev_t device) +/*[clinic end generated code: output=306cb78e3bc5004f input=2f686e463682a9da]*/ +{ + return major_minor_conv(minor(device)); +} + + +/*[clinic input] +os.makedev -> dev_t + + major: dev_t + minor: dev_t + / + +Composes a raw device number from the major and minor device numbers. +[clinic start generated code]*/ + +static dev_t +os_makedev_impl(PyObject *module, dev_t major, dev_t minor) +/*[clinic end generated code: output=cad6125c51f5af80 input=2146126ec02e55c1]*/ +{ + if (!major_minor_check(major) || !major_minor_check(minor)) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C unsigned int"); + return (dev_t)-1; + } + return makedev(major, minor); +} +#endif /* HAVE_DEVICE_MACROS */ + + +#if defined HAVE_FTRUNCATE || defined MS_WINDOWS +/*[clinic input] +os.ftruncate + + fd: int + length: Py_off_t + / + +Truncate a file, specified by file descriptor, to a specific length. +[clinic start generated code]*/ + +static PyObject * +os_ftruncate_impl(PyObject *module, int fd, Py_off_t length) +/*[clinic end generated code: output=fba15523721be7e4 input=63b43641e52818f2]*/ +{ + int result; + int async_err = 0; + + if (PySys_Audit("os.truncate", "in", fd, length) < 0) { + return NULL; + } + + do { + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH +#ifdef MS_WINDOWS + result = _chsize_s(fd, length); +#else + result = ftruncate(fd, length); +#endif + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + } while (result != 0 && errno == EINTR && + !(async_err = PyErr_CheckSignals())); + if (result != 0) + return (!async_err) ? posix_error() : NULL; + Py_RETURN_NONE; +} +#endif /* HAVE_FTRUNCATE || MS_WINDOWS */ + + +#if defined HAVE_TRUNCATE || defined MS_WINDOWS +/*[clinic input] +os.truncate + path: path_t(allow_fd='PATH_HAVE_FTRUNCATE') + length: Py_off_t + +Truncate a file, specified by path, to a specific length. + +On some platforms, path may also be specified as an open file descriptor. + If this functionality is unavailable, using it raises an exception. +[clinic start generated code]*/ + +static PyObject * +os_truncate_impl(PyObject *module, path_t *path, Py_off_t length) +/*[clinic end generated code: output=43009c8df5c0a12b input=77229cf0b50a9b77]*/ +{ + int result; +#ifdef MS_WINDOWS + int fd; +#endif + + if (path->fd != -1) + return os_ftruncate_impl(module, path->fd, length); + + if (PySys_Audit("os.truncate", "On", path->object, length) < 0) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH +#ifdef MS_WINDOWS + fd = _wopen(path->wide, _O_WRONLY | _O_BINARY | _O_NOINHERIT); + if (fd < 0) + result = -1; + else { + result = _chsize_s(fd, length); + close(fd); + if (result < 0) + errno = result; + } +#else + result = truncate(path->narrow, length); +#endif + _Py_END_SUPPRESS_IPH + Py_END_ALLOW_THREADS + if (result < 0) + return posix_path_error(path); + + Py_RETURN_NONE; +} +#endif /* HAVE_TRUNCATE || MS_WINDOWS */ + + +/* Issue #22396: On 32-bit AIX platform, the prototypes of os.posix_fadvise() + and os.posix_fallocate() in system headers are wrong if _LARGE_FILES is + defined, which is the case in Python on AIX. AIX bug report: + http://www-01.ibm.com/support/docview.wss?uid=isg1IV56170 */ +#if defined(_AIX) && defined(_LARGE_FILES) && !defined(__64BIT__) +# define POSIX_FADVISE_AIX_BUG +#endif + + +/* GH-111804: Due to posix_fallocate() not having consistent semantics across + OSs, support was dropped in WASI preview2. */ +#if defined(HAVE_POSIX_FALLOCATE) && !defined(POSIX_FADVISE_AIX_BUG) && \ + !defined(__wasi__) +/*[clinic input] +os.posix_fallocate + + fd: int + offset: Py_off_t + length: Py_off_t + / + +Ensure a file has allocated at least a particular number of bytes on disk. + +Ensure that the file specified by fd encompasses a range of bytes +starting at offset bytes from the beginning and continuing for length bytes. +[clinic start generated code]*/ + +static PyObject * +os_posix_fallocate_impl(PyObject *module, int fd, Py_off_t offset, + Py_off_t length) +/*[clinic end generated code: output=73f107139564aa9d input=d7a2ef0ab2ca52fb]*/ +{ + int result; + int async_err = 0; + + do { + Py_BEGIN_ALLOW_THREADS + result = posix_fallocate(fd, offset, length); + Py_END_ALLOW_THREADS + } while (result == EINTR && !(async_err = PyErr_CheckSignals())); + + if (result == 0) + Py_RETURN_NONE; + + if (async_err) + return NULL; + + errno = result; + return posix_error(); +} +#endif /* HAVE_POSIX_FALLOCATE) && !POSIX_FADVISE_AIX_BUG && !defined(__wasi__) */ + + +#if defined(HAVE_POSIX_FADVISE) && !defined(POSIX_FADVISE_AIX_BUG) +/*[clinic input] +os.posix_fadvise + + fd: int + offset: Py_off_t + length: Py_off_t + advice: int + / + +Announce an intention to access data in a specific pattern. + +Announce an intention to access data in a specific pattern, thus allowing +the kernel to make optimizations. +The advice applies to the region of the file specified by fd starting at +offset and continuing for length bytes. +advice is one of POSIX_FADV_NORMAL, POSIX_FADV_SEQUENTIAL, +POSIX_FADV_RANDOM, POSIX_FADV_NOREUSE, POSIX_FADV_WILLNEED, or +POSIX_FADV_DONTNEED. +[clinic start generated code]*/ + +static PyObject * +os_posix_fadvise_impl(PyObject *module, int fd, Py_off_t offset, + Py_off_t length, int advice) +/*[clinic end generated code: output=412ef4aa70c98642 input=0fbe554edc2f04b5]*/ +{ + int result; + int async_err = 0; + + do { + Py_BEGIN_ALLOW_THREADS + result = posix_fadvise(fd, offset, length, advice); + Py_END_ALLOW_THREADS + } while (result == EINTR && !(async_err = PyErr_CheckSignals())); + + if (result == 0) + Py_RETURN_NONE; + + if (async_err) + return NULL; + + errno = result; + return posix_error(); +} +#endif /* HAVE_POSIX_FADVISE && !POSIX_FADVISE_AIX_BUG */ + + +#ifdef MS_WINDOWS +static PyObject* +win32_putenv(PyObject *name, PyObject *value) +{ + /* Search from index 1 because on Windows starting '=' is allowed for + defining hidden environment variables. */ + if (PyUnicode_GET_LENGTH(name) == 0 || + PyUnicode_FindChar(name, '=', 1, PyUnicode_GET_LENGTH(name), 1) != -1) + { + PyErr_SetString(PyExc_ValueError, "illegal environment variable name"); + return NULL; + } + PyObject *unicode; + if (value != NULL) { + unicode = PyUnicode_FromFormat("%U=%U", name, value); + } + else { + unicode = PyUnicode_FromFormat("%U=", name); + } + if (unicode == NULL) { + return NULL; + } + + Py_ssize_t size; + wchar_t *env = PyUnicode_AsWideCharString(unicode, &size); + Py_DECREF(unicode); + + if (env == NULL) { + return NULL; + } + if (size > _MAX_ENV) { + PyErr_Format(PyExc_ValueError, + "the environment variable is longer than %u characters", + _MAX_ENV); + PyMem_Free(env); + return NULL; + } + if (wcslen(env) != (size_t)size) { + PyErr_SetString(PyExc_ValueError, + "embedded null character"); + PyMem_Free(env); + return NULL; + } + + /* _wputenv() and SetEnvironmentVariableW() update the environment in the + Process Environment Block (PEB). _wputenv() also updates CRT 'environ' + and '_wenviron' variables, whereas SetEnvironmentVariableW() does not. + + Prefer _wputenv() to be compatible with C libraries using CRT + variables and CRT functions using these variables (ex: getenv()). */ + int err = _wputenv(env); + + if (err) { + posix_error(); + PyMem_Free(env); + return NULL; + } + PyMem_Free(env); + + Py_RETURN_NONE; +} +#endif + + +#ifdef MS_WINDOWS +/*[clinic input] +os.putenv + + name: unicode + value: unicode + / + +Change or add an environment variable. +[clinic start generated code]*/ + +static PyObject * +os_putenv_impl(PyObject *module, PyObject *name, PyObject *value) +/*[clinic end generated code: output=d29a567d6b2327d2 input=ba586581c2e6105f]*/ +{ + if (PySys_Audit("os.putenv", "OO", name, value) < 0) { + return NULL; + } + return win32_putenv(name, value); +} +#else +/*[clinic input] +os.putenv + + name: FSConverter + value: FSConverter + / + +Change or add an environment variable. +[clinic start generated code]*/ + +static PyObject * +os_putenv_impl(PyObject *module, PyObject *name, PyObject *value) +/*[clinic end generated code: output=d29a567d6b2327d2 input=a97bc6152f688d31]*/ +{ + const char *name_string = PyBytes_AS_STRING(name); + const char *value_string = PyBytes_AS_STRING(value); + + if (strchr(name_string, '=') != NULL) { + PyErr_SetString(PyExc_ValueError, "illegal environment variable name"); + return NULL; + } + + if (PySys_Audit("os.putenv", "OO", name, value) < 0) { + return NULL; + } + + if (setenv(name_string, value_string, 1)) { + return posix_error(); + } + Py_RETURN_NONE; +} +#endif /* !defined(MS_WINDOWS) */ + + +#ifdef MS_WINDOWS +/*[clinic input] +os.unsetenv + name: unicode + / + +Delete an environment variable. +[clinic start generated code]*/ + +static PyObject * +os_unsetenv_impl(PyObject *module, PyObject *name) +/*[clinic end generated code: output=54c4137ab1834f02 input=4d6a1747cc526d2f]*/ +{ + if (PySys_Audit("os.unsetenv", "(O)", name) < 0) { + return NULL; + } + return win32_putenv(name, NULL); +} +#else +/*[clinic input] +os.unsetenv + name: FSConverter + / + +Delete an environment variable. +[clinic start generated code]*/ + +static PyObject * +os_unsetenv_impl(PyObject *module, PyObject *name) +/*[clinic end generated code: output=54c4137ab1834f02 input=2bb5288a599c7107]*/ +{ + if (PySys_Audit("os.unsetenv", "(O)", name) < 0) { + return NULL; + } +#ifdef HAVE_BROKEN_UNSETENV + unsetenv(PyBytes_AS_STRING(name)); +#else + int err = unsetenv(PyBytes_AS_STRING(name)); + if (err) { + return posix_error(); + } +#endif + + Py_RETURN_NONE; +} +#endif /* !MS_WINDOWS */ + + +/*[clinic input] +os.strerror + + code: int + / + +Translate an error code to a message string. +[clinic start generated code]*/ + +static PyObject * +os_strerror_impl(PyObject *module, int code) +/*[clinic end generated code: output=baebf09fa02a78f2 input=75a8673d97915a91]*/ +{ + char *message = strerror(code); + if (message == NULL) { + PyErr_SetString(PyExc_ValueError, + "strerror() argument out of range"); + return NULL; + } + return PyUnicode_DecodeLocale(message, "surrogateescape"); +} + + +#ifdef HAVE_SYS_WAIT_H +#ifdef WCOREDUMP +/*[clinic input] +os.WCOREDUMP -> bool + + status: int + / + +Return True if the process returning status was dumped to a core file. +[clinic start generated code]*/ + +static int +os_WCOREDUMP_impl(PyObject *module, int status) +/*[clinic end generated code: output=1a584b147b16bd18 input=8b05e7ab38528d04]*/ +{ + WAIT_TYPE wait_status; + WAIT_STATUS_INT(wait_status) = status; + return WCOREDUMP(wait_status); +} +#endif /* WCOREDUMP */ + + +#ifdef WIFCONTINUED +/*[clinic input] +os.WIFCONTINUED -> bool + + status: int + +Return True if a particular process was continued from a job control stop. + +Return True if the process returning status was continued from a +job control stop. +[clinic start generated code]*/ + +static int +os_WIFCONTINUED_impl(PyObject *module, int status) +/*[clinic end generated code: output=1e35295d844364bd input=e777e7d38eb25bd9]*/ +{ + WAIT_TYPE wait_status; + WAIT_STATUS_INT(wait_status) = status; + return WIFCONTINUED(wait_status); +} +#endif /* WIFCONTINUED */ + + +#ifdef WIFSTOPPED +/*[clinic input] +os.WIFSTOPPED -> bool + + status: int + +Return True if the process returning status was stopped. +[clinic start generated code]*/ + +static int +os_WIFSTOPPED_impl(PyObject *module, int status) +/*[clinic end generated code: output=fdb57122a5c9b4cb input=043cb7f1289ef904]*/ +{ + WAIT_TYPE wait_status; + WAIT_STATUS_INT(wait_status) = status; + return WIFSTOPPED(wait_status); +} +#endif /* WIFSTOPPED */ + + +#ifdef WIFSIGNALED +/*[clinic input] +os.WIFSIGNALED -> bool + + status: int + +Return True if the process returning status was terminated by a signal. +[clinic start generated code]*/ + +static int +os_WIFSIGNALED_impl(PyObject *module, int status) +/*[clinic end generated code: output=d1dde4dcc819a5f5 input=d55ba7cc9ce5dc43]*/ +{ + WAIT_TYPE wait_status; + WAIT_STATUS_INT(wait_status) = status; + return WIFSIGNALED(wait_status); +} +#endif /* WIFSIGNALED */ + + +#ifdef WIFEXITED +/*[clinic input] +os.WIFEXITED -> bool + + status: int + +Return True if the process returning status exited via the exit() system call. +[clinic start generated code]*/ + +static int +os_WIFEXITED_impl(PyObject *module, int status) +/*[clinic end generated code: output=01c09d6ebfeea397 input=d63775a6791586c0]*/ +{ + WAIT_TYPE wait_status; + WAIT_STATUS_INT(wait_status) = status; + return WIFEXITED(wait_status); +} +#endif /* WIFEXITED */ + + +#ifdef WEXITSTATUS +/*[clinic input] +os.WEXITSTATUS -> int + + status: int + +Return the process return code from status. +[clinic start generated code]*/ + +static int +os_WEXITSTATUS_impl(PyObject *module, int status) +/*[clinic end generated code: output=6e3efbba11f6488d input=e1fb4944e377585b]*/ +{ + WAIT_TYPE wait_status; + WAIT_STATUS_INT(wait_status) = status; + return WEXITSTATUS(wait_status); +} +#endif /* WEXITSTATUS */ + + +#ifdef WTERMSIG +/*[clinic input] +os.WTERMSIG -> int + + status: int + +Return the signal that terminated the process that provided the status value. +[clinic start generated code]*/ + +static int +os_WTERMSIG_impl(PyObject *module, int status) +/*[clinic end generated code: output=172f7dfc8dcfc3ad input=727fd7f84ec3f243]*/ +{ + WAIT_TYPE wait_status; + WAIT_STATUS_INT(wait_status) = status; + return WTERMSIG(wait_status); +} +#endif /* WTERMSIG */ + + +#ifdef WSTOPSIG +/*[clinic input] +os.WSTOPSIG -> int + + status: int + +Return the signal that stopped the process that provided the status value. +[clinic start generated code]*/ + +static int +os_WSTOPSIG_impl(PyObject *module, int status) +/*[clinic end generated code: output=0ab7586396f5d82b input=46ebf1d1b293c5c1]*/ +{ + WAIT_TYPE wait_status; + WAIT_STATUS_INT(wait_status) = status; + return WSTOPSIG(wait_status); +} +#endif /* WSTOPSIG */ +#endif /* HAVE_SYS_WAIT_H */ + + +#if defined(HAVE_FSTATVFS) && defined(HAVE_SYS_STATVFS_H) +#ifdef _SCO_DS +/* SCO OpenServer 5.0 and later requires _SVID3 before it reveals the + needed definitions in sys/statvfs.h */ +#define _SVID3 +#endif +#include + +#ifdef __APPLE__ +/* On macOS struct statvfs uses 32-bit integers for block counts, + * resulting in overflow when filesystems are larger than 4TB. Therefore + * os.statvfs is implemented in terms of statfs(2). + */ + +static PyObject* +_pystatvfs_fromstructstatfs(PyObject *module, struct statfs st) { + PyObject *StatVFSResultType = get_posix_state(module)->StatVFSResultType; + PyObject *v = PyStructSequence_New((PyTypeObject *)StatVFSResultType); + if (v == NULL) { + return NULL; + } + + long flags = 0; + if (st.f_flags & MNT_RDONLY) { + flags |= ST_RDONLY; + } + if (st.f_flags & MNT_NOSUID) { + flags |= ST_NOSUID; + } + + _Static_assert(sizeof(st.f_blocks) == sizeof(long long), "assuming large file"); + +#define SET_ITEM(SEQ, INDEX, EXPR) \ + do { \ + PyObject *obj = (EXPR); \ + if (obj == NULL) { \ + Py_DECREF((SEQ)); \ + return NULL; \ + } \ + PyStructSequence_SET_ITEM((SEQ), (INDEX), obj); \ + } while (0) + + SET_ITEM(v, 0, PyLong_FromLong((long) st.f_iosize)); + SET_ITEM(v, 1, PyLong_FromLong((long) st.f_bsize)); + SET_ITEM(v, 2, PyLong_FromLongLong((long long) st.f_blocks)); + SET_ITEM(v, 3, PyLong_FromLongLong((long long) st.f_bfree)); + SET_ITEM(v, 4, PyLong_FromLongLong((long long) st.f_bavail)); + SET_ITEM(v, 5, PyLong_FromLongLong((long long) st.f_files)); + SET_ITEM(v, 6, PyLong_FromLongLong((long long) st.f_ffree)); + SET_ITEM(v, 7, PyLong_FromLongLong((long long) st.f_ffree)); + SET_ITEM(v, 8, PyLong_FromLong((long) flags)); + + SET_ITEM(v, 9, PyLong_FromLong((long) NAME_MAX)); + SET_ITEM(v, 10, PyLong_FromUnsignedLong(st.f_fsid.val[0])); + +#undef SET_ITEM + + return v; +} + +#else + + + +static PyObject* +_pystatvfs_fromstructstatvfs(PyObject *module, struct statvfs st) { + PyObject *StatVFSResultType = get_posix_state(module)->StatVFSResultType; + PyObject *v = PyStructSequence_New((PyTypeObject *)StatVFSResultType); + if (v == NULL) + return NULL; + + int pos = 0; + +#define SET_RESULT(CALL) \ + do { \ + PyObject *item = (CALL); \ + if (item == NULL) { \ + Py_DECREF(v); \ + return NULL; \ + } \ + PyStructSequence_SET_ITEM(v, pos++, item); \ + } while(0) + +#if !defined(HAVE_LARGEFILE_SUPPORT) + SET_RESULT(PyLong_FromLong((long) st.f_bsize)); + SET_RESULT(PyLong_FromLong((long) st.f_frsize)); + SET_RESULT(PyLong_FromLong((long) st.f_blocks)); + SET_RESULT(PyLong_FromLong((long) st.f_bfree)); + SET_RESULT(PyLong_FromLong((long) st.f_bavail)); + SET_RESULT(PyLong_FromLong((long) st.f_files)); + SET_RESULT(PyLong_FromLong((long) st.f_ffree)); + SET_RESULT(PyLong_FromLong((long) st.f_favail)); + SET_RESULT(PyLong_FromLong((long) st.f_flag)); + SET_RESULT(PyLong_FromLong((long) st.f_namemax)); +#else + SET_RESULT(PyLong_FromLong((long) st.f_bsize)); + SET_RESULT(PyLong_FromLong((long) st.f_frsize)); + SET_RESULT(PyLong_FromLongLong((long long) st.f_blocks)); + SET_RESULT(PyLong_FromLongLong((long long) st.f_bfree)); + SET_RESULT(PyLong_FromLongLong((long long) st.f_bavail)); + SET_RESULT(PyLong_FromLongLong((long long) st.f_files)); + SET_RESULT(PyLong_FromLongLong((long long) st.f_ffree)); + SET_RESULT(PyLong_FromLongLong((long long) st.f_favail)); + SET_RESULT(PyLong_FromLong((long) st.f_flag)); + SET_RESULT(PyLong_FromLong((long) st.f_namemax)); +#endif +/* The _ALL_SOURCE feature test macro defines f_fsid as a structure + * (issue #32390). */ +#if defined(_AIX) && defined(_ALL_SOURCE) + SET_RESULT(PyLong_FromUnsignedLong(st.f_fsid.val[0])); +#else + SET_RESULT(PyLong_FromUnsignedLong(st.f_fsid)); +#endif + +#undef SET_RESULT + + return v; +} + +#endif + + +/*[clinic input] +os.fstatvfs + fd: int + / + +Perform an fstatvfs system call on the given fd. + +Equivalent to statvfs(fd). +[clinic start generated code]*/ + +static PyObject * +os_fstatvfs_impl(PyObject *module, int fd) +/*[clinic end generated code: output=53547cf0cc55e6c5 input=d8122243ac50975e]*/ +{ + int result; + int async_err = 0; +#ifdef __APPLE__ + struct statfs st; + /* On macOS os.fstatvfs is implemented using fstatfs(2) because + * the former uses 32-bit values for block counts. + */ + do { + Py_BEGIN_ALLOW_THREADS + result = fstatfs(fd, &st); + Py_END_ALLOW_THREADS + } while (result != 0 && errno == EINTR && + !(async_err = PyErr_CheckSignals())); + if (result != 0) + return (!async_err) ? posix_error() : NULL; + + return _pystatvfs_fromstructstatfs(module, st); +#else + struct statvfs st; + + do { + Py_BEGIN_ALLOW_THREADS + result = fstatvfs(fd, &st); + Py_END_ALLOW_THREADS + } while (result != 0 && errno == EINTR && + !(async_err = PyErr_CheckSignals())); + if (result != 0) + return (!async_err) ? posix_error() : NULL; + + return _pystatvfs_fromstructstatvfs(module, st); +#endif +} +#endif /* defined(HAVE_FSTATVFS) && defined(HAVE_SYS_STATVFS_H) */ + + +#if defined(HAVE_STATVFS) && defined(HAVE_SYS_STATVFS_H) +#include +/*[clinic input] +os.statvfs + + path: path_t(allow_fd='PATH_HAVE_FSTATVFS') + +Perform a statvfs system call on the given path. + +path may always be specified as a string. +On some platforms, path may also be specified as an open file descriptor. + If this functionality is unavailable, using it raises an exception. +[clinic start generated code]*/ + +static PyObject * +os_statvfs_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=87106dd1beb8556e input=3f5c35791c669bd9]*/ +{ + int result; + +#ifdef __APPLE__ + /* On macOS os.statvfs is implemented using statfs(2)/fstatfs(2) because + * the former uses 32-bit values for block counts. + */ + struct statfs st; + + Py_BEGIN_ALLOW_THREADS + if (path->fd != -1) { + result = fstatfs(path->fd, &st); + } + else + result = statfs(path->narrow, &st); + Py_END_ALLOW_THREADS + + if (result) { + return path_error(path); + } + + return _pystatvfs_fromstructstatfs(module, st); + +#else + struct statvfs st; + + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_FSTATVFS + if (path->fd != -1) { + result = fstatvfs(path->fd, &st); + } + else +#endif + result = statvfs(path->narrow, &st); + Py_END_ALLOW_THREADS + + if (result) { + return path_error(path); + } + + return _pystatvfs_fromstructstatvfs(module, st); +#endif +} +#endif /* defined(HAVE_STATVFS) && defined(HAVE_SYS_STATVFS_H) */ + + +#ifdef MS_WINDOWS +/*[clinic input] +os._getdiskusage + + path: path_t + +Return disk usage statistics about the given path as a (total, free) tuple. +[clinic start generated code]*/ + +static PyObject * +os__getdiskusage_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=3bd3991f5e5c5dfb input=6af8d1b7781cc042]*/ +{ + BOOL retval; + ULARGE_INTEGER _, total, free; + DWORD err = 0; + + Py_BEGIN_ALLOW_THREADS + retval = GetDiskFreeSpaceExW(path->wide, &_, &total, &free); + Py_END_ALLOW_THREADS + if (retval == 0) { + if (GetLastError() == ERROR_DIRECTORY) { + wchar_t *dir_path = NULL; + + dir_path = PyMem_New(wchar_t, path->length + 1); + if (dir_path == NULL) { + return PyErr_NoMemory(); + } + + wcscpy_s(dir_path, path->length + 1, path->wide); + + if (_dirnameW(dir_path) != -1) { + Py_BEGIN_ALLOW_THREADS + retval = GetDiskFreeSpaceExW(dir_path, &_, &total, &free); + Py_END_ALLOW_THREADS + } + /* Record the last error in case it's modified by PyMem_Free. */ + err = GetLastError(); + PyMem_Free(dir_path); + if (retval) { + goto success; + } + } + return PyErr_SetFromWindowsErr(err); + } + +success: + return Py_BuildValue("(LL)", total.QuadPart, free.QuadPart); +} +#endif /* MS_WINDOWS */ + + +/* This is used for fpathconf(), pathconf(), confstr() and sysconf(). + * It maps strings representing configuration variable names to + * integer values, allowing those functions to be called with the + * magic names instead of polluting the module's namespace with tons of + * rarely-used constants. There are three separate tables that use + * these definitions. + * + * This code is always included, even if none of the interfaces that + * need it are included. The #if hackery needed to avoid it would be + * sufficiently pervasive that it's not worth the loss of readability. + */ +struct constdef { + const char *name; + int value; +}; + +static int +conv_confname(PyObject *arg, int *valuep, struct constdef *table, + size_t tablesize) +{ + if (PyLong_Check(arg)) { + int value = PyLong_AsInt(arg); + if (value == -1 && PyErr_Occurred()) + return 0; + *valuep = value; + return 1; + } + else { + /* look up the value in the table using a binary search */ + size_t lo = 0; + size_t mid; + size_t hi = tablesize; + int cmp; + const char *confname; + if (!PyUnicode_Check(arg)) { + PyErr_SetString(PyExc_TypeError, + "configuration names must be strings or integers"); + return 0; + } + confname = PyUnicode_AsUTF8(arg); + if (confname == NULL) + return 0; + while (lo < hi) { + mid = (lo + hi) / 2; + cmp = strcmp(confname, table[mid].name); + if (cmp < 0) + hi = mid; + else if (cmp > 0) + lo = mid + 1; + else { + *valuep = table[mid].value; + return 1; + } + } + PyErr_SetString(PyExc_ValueError, "unrecognized configuration name"); + return 0; + } +} + + +#if defined(HAVE_FPATHCONF) || defined(HAVE_PATHCONF) +static struct constdef posix_constants_pathconf[] = { +#ifdef _PC_ABI_AIO_XFER_MAX + {"PC_ABI_AIO_XFER_MAX", _PC_ABI_AIO_XFER_MAX}, +#endif +#ifdef _PC_ABI_ASYNC_IO + {"PC_ABI_ASYNC_IO", _PC_ABI_ASYNC_IO}, +#endif +#ifdef _PC_ASYNC_IO + {"PC_ASYNC_IO", _PC_ASYNC_IO}, +#endif +#ifdef _PC_CHOWN_RESTRICTED + {"PC_CHOWN_RESTRICTED", _PC_CHOWN_RESTRICTED}, +#endif +#ifdef _PC_FILESIZEBITS + {"PC_FILESIZEBITS", _PC_FILESIZEBITS}, +#endif +#ifdef _PC_LAST + {"PC_LAST", _PC_LAST}, +#endif +#ifdef _PC_LINK_MAX + {"PC_LINK_MAX", _PC_LINK_MAX}, +#endif +#ifdef _PC_MAX_CANON + {"PC_MAX_CANON", _PC_MAX_CANON}, +#endif +#ifdef _PC_MAX_INPUT + {"PC_MAX_INPUT", _PC_MAX_INPUT}, +#endif +#ifdef _PC_NAME_MAX + {"PC_NAME_MAX", _PC_NAME_MAX}, +#endif +#ifdef _PC_NO_TRUNC + {"PC_NO_TRUNC", _PC_NO_TRUNC}, +#endif +#ifdef _PC_PATH_MAX + {"PC_PATH_MAX", _PC_PATH_MAX}, +#endif +#ifdef _PC_PIPE_BUF + {"PC_PIPE_BUF", _PC_PIPE_BUF}, +#endif +#ifdef _PC_PRIO_IO + {"PC_PRIO_IO", _PC_PRIO_IO}, +#endif +#ifdef _PC_SOCK_MAXBUF + {"PC_SOCK_MAXBUF", _PC_SOCK_MAXBUF}, +#endif +#ifdef _PC_SYNC_IO + {"PC_SYNC_IO", _PC_SYNC_IO}, +#endif +#ifdef _PC_VDISABLE + {"PC_VDISABLE", _PC_VDISABLE}, +#endif +#ifdef _PC_ACL_ENABLED + {"PC_ACL_ENABLED", _PC_ACL_ENABLED}, +#endif +#ifdef _PC_MIN_HOLE_SIZE + {"PC_MIN_HOLE_SIZE", _PC_MIN_HOLE_SIZE}, +#endif +#ifdef _PC_ALLOC_SIZE_MIN + {"PC_ALLOC_SIZE_MIN", _PC_ALLOC_SIZE_MIN}, +#endif +#ifdef _PC_REC_INCR_XFER_SIZE + {"PC_REC_INCR_XFER_SIZE", _PC_REC_INCR_XFER_SIZE}, +#endif +#ifdef _PC_REC_MAX_XFER_SIZE + {"PC_REC_MAX_XFER_SIZE", _PC_REC_MAX_XFER_SIZE}, +#endif +#ifdef _PC_REC_MIN_XFER_SIZE + {"PC_REC_MIN_XFER_SIZE", _PC_REC_MIN_XFER_SIZE}, +#endif +#ifdef _PC_REC_XFER_ALIGN + {"PC_REC_XFER_ALIGN", _PC_REC_XFER_ALIGN}, +#endif +#ifdef _PC_SYMLINK_MAX + {"PC_SYMLINK_MAX", _PC_SYMLINK_MAX}, +#endif +#ifdef _PC_XATTR_ENABLED + {"PC_XATTR_ENABLED", _PC_XATTR_ENABLED}, +#endif +#ifdef _PC_XATTR_EXISTS + {"PC_XATTR_EXISTS", _PC_XATTR_EXISTS}, +#endif +#ifdef _PC_TIMESTAMP_RESOLUTION + {"PC_TIMESTAMP_RESOLUTION", _PC_TIMESTAMP_RESOLUTION}, +#endif +}; + +static int +conv_path_confname(PyObject *arg, int *valuep) +{ + return conv_confname(arg, valuep, posix_constants_pathconf, + sizeof(posix_constants_pathconf) + / sizeof(struct constdef)); +} +#endif + + +#ifdef HAVE_FPATHCONF +/*[clinic input] +os.fpathconf -> long + + fd: fildes + name: path_confname + / + +Return the configuration limit name for the file descriptor fd. + +If there is no limit, return -1. +[clinic start generated code]*/ + +static long +os_fpathconf_impl(PyObject *module, int fd, int name) +/*[clinic end generated code: output=d5b7042425fc3e21 input=5b8d2471cfaae186]*/ +{ + long limit; + + errno = 0; + limit = fpathconf(fd, name); + if (limit == -1 && errno != 0) + posix_error(); + + return limit; +} +#endif /* HAVE_FPATHCONF */ + + +#ifdef HAVE_PATHCONF +/*[clinic input] +os.pathconf -> long + path: path_t(allow_fd='PATH_HAVE_FPATHCONF') + name: path_confname + +Return the configuration limit name for the file or directory path. + +If there is no limit, return -1. +On some platforms, path may also be specified as an open file descriptor. + If this functionality is unavailable, using it raises an exception. +[clinic start generated code]*/ + +static long +os_pathconf_impl(PyObject *module, path_t *path, int name) +/*[clinic end generated code: output=5bedee35b293a089 input=bc3e2a985af27e5e]*/ +{ + long limit; + + errno = 0; +#ifdef HAVE_FPATHCONF + if (path->fd != -1) + limit = fpathconf(path->fd, name); + else +#endif + limit = pathconf(path->narrow, name); + if (limit == -1 && errno != 0) { + if (errno == EINVAL) + /* could be a path or name problem */ + posix_error(); + else + path_error(path); + } + + return limit; +} +#endif /* HAVE_PATHCONF */ + +#ifdef HAVE_CONFSTR +static struct constdef posix_constants_confstr[] = { +#ifdef _CS_ARCHITECTURE + {"CS_ARCHITECTURE", _CS_ARCHITECTURE}, +#endif +#ifdef _CS_GNU_LIBC_VERSION + {"CS_GNU_LIBC_VERSION", _CS_GNU_LIBC_VERSION}, +#endif +#ifdef _CS_GNU_LIBPTHREAD_VERSION + {"CS_GNU_LIBPTHREAD_VERSION", _CS_GNU_LIBPTHREAD_VERSION}, +#endif +#ifdef _CS_HOSTNAME + {"CS_HOSTNAME", _CS_HOSTNAME}, +#endif +#ifdef _CS_HW_PROVIDER + {"CS_HW_PROVIDER", _CS_HW_PROVIDER}, +#endif +#ifdef _CS_HW_SERIAL + {"CS_HW_SERIAL", _CS_HW_SERIAL}, +#endif +#ifdef _CS_INITTAB_NAME + {"CS_INITTAB_NAME", _CS_INITTAB_NAME}, +#endif +#ifdef _CS_LFS64_CFLAGS + {"CS_LFS64_CFLAGS", _CS_LFS64_CFLAGS}, +#endif +#ifdef _CS_LFS64_LDFLAGS + {"CS_LFS64_LDFLAGS", _CS_LFS64_LDFLAGS}, +#endif +#ifdef _CS_LFS64_LIBS + {"CS_LFS64_LIBS", _CS_LFS64_LIBS}, +#endif +#ifdef _CS_LFS64_LINTFLAGS + {"CS_LFS64_LINTFLAGS", _CS_LFS64_LINTFLAGS}, +#endif +#ifdef _CS_LFS_CFLAGS + {"CS_LFS_CFLAGS", _CS_LFS_CFLAGS}, +#endif +#ifdef _CS_LFS_LDFLAGS + {"CS_LFS_LDFLAGS", _CS_LFS_LDFLAGS}, +#endif +#ifdef _CS_LFS_LIBS + {"CS_LFS_LIBS", _CS_LFS_LIBS}, +#endif +#ifdef _CS_LFS_LINTFLAGS + {"CS_LFS_LINTFLAGS", _CS_LFS_LINTFLAGS}, +#endif +#ifdef _CS_MACHINE + {"CS_MACHINE", _CS_MACHINE}, +#endif +#ifdef _CS_PATH + {"CS_PATH", _CS_PATH}, +#endif +#ifdef _CS_RELEASE + {"CS_RELEASE", _CS_RELEASE}, +#endif +#ifdef _CS_SRPC_DOMAIN + {"CS_SRPC_DOMAIN", _CS_SRPC_DOMAIN}, +#endif +#ifdef _CS_SYSNAME + {"CS_SYSNAME", _CS_SYSNAME}, +#endif +#ifdef _CS_VERSION + {"CS_VERSION", _CS_VERSION}, +#endif +#ifdef _CS_XBS5_ILP32_OFF32_CFLAGS + {"CS_XBS5_ILP32_OFF32_CFLAGS", _CS_XBS5_ILP32_OFF32_CFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFF32_LDFLAGS + {"CS_XBS5_ILP32_OFF32_LDFLAGS", _CS_XBS5_ILP32_OFF32_LDFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFF32_LIBS + {"CS_XBS5_ILP32_OFF32_LIBS", _CS_XBS5_ILP32_OFF32_LIBS}, +#endif +#ifdef _CS_XBS5_ILP32_OFF32_LINTFLAGS + {"CS_XBS5_ILP32_OFF32_LINTFLAGS", _CS_XBS5_ILP32_OFF32_LINTFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFFBIG_CFLAGS + {"CS_XBS5_ILP32_OFFBIG_CFLAGS", _CS_XBS5_ILP32_OFFBIG_CFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFFBIG_LDFLAGS + {"CS_XBS5_ILP32_OFFBIG_LDFLAGS", _CS_XBS5_ILP32_OFFBIG_LDFLAGS}, +#endif +#ifdef _CS_XBS5_ILP32_OFFBIG_LIBS + {"CS_XBS5_ILP32_OFFBIG_LIBS", _CS_XBS5_ILP32_OFFBIG_LIBS}, +#endif +#ifdef _CS_XBS5_ILP32_OFFBIG_LINTFLAGS + {"CS_XBS5_ILP32_OFFBIG_LINTFLAGS", _CS_XBS5_ILP32_OFFBIG_LINTFLAGS}, +#endif +#ifdef _CS_XBS5_LP64_OFF64_CFLAGS + {"CS_XBS5_LP64_OFF64_CFLAGS", _CS_XBS5_LP64_OFF64_CFLAGS}, +#endif +#ifdef _CS_XBS5_LP64_OFF64_LDFLAGS + {"CS_XBS5_LP64_OFF64_LDFLAGS", _CS_XBS5_LP64_OFF64_LDFLAGS}, +#endif +#ifdef _CS_XBS5_LP64_OFF64_LIBS + {"CS_XBS5_LP64_OFF64_LIBS", _CS_XBS5_LP64_OFF64_LIBS}, +#endif +#ifdef _CS_XBS5_LP64_OFF64_LINTFLAGS + {"CS_XBS5_LP64_OFF64_LINTFLAGS", _CS_XBS5_LP64_OFF64_LINTFLAGS}, +#endif +#ifdef _CS_XBS5_LPBIG_OFFBIG_CFLAGS + {"CS_XBS5_LPBIG_OFFBIG_CFLAGS", _CS_XBS5_LPBIG_OFFBIG_CFLAGS}, +#endif +#ifdef _CS_XBS5_LPBIG_OFFBIG_LDFLAGS + {"CS_XBS5_LPBIG_OFFBIG_LDFLAGS", _CS_XBS5_LPBIG_OFFBIG_LDFLAGS}, +#endif +#ifdef _CS_XBS5_LPBIG_OFFBIG_LIBS + {"CS_XBS5_LPBIG_OFFBIG_LIBS", _CS_XBS5_LPBIG_OFFBIG_LIBS}, +#endif +#ifdef _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS + {"CS_XBS5_LPBIG_OFFBIG_LINTFLAGS", _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS}, +#endif +#ifdef _MIPS_CS_AVAIL_PROCESSORS + {"MIPS_CS_AVAIL_PROCESSORS", _MIPS_CS_AVAIL_PROCESSORS}, +#endif +#ifdef _MIPS_CS_BASE + {"MIPS_CS_BASE", _MIPS_CS_BASE}, +#endif +#ifdef _MIPS_CS_HOSTID + {"MIPS_CS_HOSTID", _MIPS_CS_HOSTID}, +#endif +#ifdef _MIPS_CS_HW_NAME + {"MIPS_CS_HW_NAME", _MIPS_CS_HW_NAME}, +#endif +#ifdef _MIPS_CS_NUM_PROCESSORS + {"MIPS_CS_NUM_PROCESSORS", _MIPS_CS_NUM_PROCESSORS}, +#endif +#ifdef _MIPS_CS_OSREL_MAJ + {"MIPS_CS_OSREL_MAJ", _MIPS_CS_OSREL_MAJ}, +#endif +#ifdef _MIPS_CS_OSREL_MIN + {"MIPS_CS_OSREL_MIN", _MIPS_CS_OSREL_MIN}, +#endif +#ifdef _MIPS_CS_OSREL_PATCH + {"MIPS_CS_OSREL_PATCH", _MIPS_CS_OSREL_PATCH}, +#endif +#ifdef _MIPS_CS_OS_NAME + {"MIPS_CS_OS_NAME", _MIPS_CS_OS_NAME}, +#endif +#ifdef _MIPS_CS_OS_PROVIDER + {"MIPS_CS_OS_PROVIDER", _MIPS_CS_OS_PROVIDER}, +#endif +#ifdef _MIPS_CS_PROCESSORS + {"MIPS_CS_PROCESSORS", _MIPS_CS_PROCESSORS}, +#endif +#ifdef _MIPS_CS_SERIAL + {"MIPS_CS_SERIAL", _MIPS_CS_SERIAL}, +#endif +#ifdef _MIPS_CS_VENDOR + {"MIPS_CS_VENDOR", _MIPS_CS_VENDOR}, +#endif +}; + +static int +conv_confstr_confname(PyObject *arg, int *valuep) +{ + return conv_confname(arg, valuep, posix_constants_confstr, + sizeof(posix_constants_confstr) + / sizeof(struct constdef)); +} + + +/*[clinic input] +os.confstr + + name: confstr_confname + / + +Return a string-valued system configuration variable. +[clinic start generated code]*/ + +static PyObject * +os_confstr_impl(PyObject *module, int name) +/*[clinic end generated code: output=bfb0b1b1e49b9383 input=18fb4d0567242e65]*/ +{ + PyObject *result = NULL; + char buffer[255]; + size_t len; + + errno = 0; + len = confstr(name, buffer, sizeof(buffer)); + if (len == 0) { + if (errno) { + posix_error(); + return NULL; + } + else { + Py_RETURN_NONE; + } + } + + if (len >= sizeof(buffer)) { + size_t len2; + char *buf = PyMem_Malloc(len); + if (buf == NULL) + return PyErr_NoMemory(); + len2 = confstr(name, buf, len); + assert(len == len2); + result = PyUnicode_DecodeFSDefaultAndSize(buf, len2-1); + PyMem_Free(buf); + } + else + result = PyUnicode_DecodeFSDefaultAndSize(buffer, len-1); + return result; +} +#endif /* HAVE_CONFSTR */ + + +#ifdef HAVE_SYSCONF +static struct constdef posix_constants_sysconf[] = { +#ifdef _SC_2_CHAR_TERM + {"SC_2_CHAR_TERM", _SC_2_CHAR_TERM}, +#endif +#ifdef _SC_2_C_BIND + {"SC_2_C_BIND", _SC_2_C_BIND}, +#endif +#ifdef _SC_2_C_DEV + {"SC_2_C_DEV", _SC_2_C_DEV}, +#endif +#ifdef _SC_2_C_VERSION + {"SC_2_C_VERSION", _SC_2_C_VERSION}, +#endif +#ifdef _SC_2_FORT_DEV + {"SC_2_FORT_DEV", _SC_2_FORT_DEV}, +#endif +#ifdef _SC_2_FORT_RUN + {"SC_2_FORT_RUN", _SC_2_FORT_RUN}, +#endif +#ifdef _SC_2_LOCALEDEF + {"SC_2_LOCALEDEF", _SC_2_LOCALEDEF}, +#endif +#ifdef _SC_2_SW_DEV + {"SC_2_SW_DEV", _SC_2_SW_DEV}, +#endif +#ifdef _SC_2_UPE + {"SC_2_UPE", _SC_2_UPE}, +#endif +#ifdef _SC_2_VERSION + {"SC_2_VERSION", _SC_2_VERSION}, +#endif +#ifdef _SC_ABI_ASYNCHRONOUS_IO + {"SC_ABI_ASYNCHRONOUS_IO", _SC_ABI_ASYNCHRONOUS_IO}, +#endif +#ifdef _SC_ACL + {"SC_ACL", _SC_ACL}, +#endif +#ifdef _SC_AIO_LISTIO_MAX + {"SC_AIO_LISTIO_MAX", _SC_AIO_LISTIO_MAX}, +#endif +#ifdef _SC_AIO_MAX + {"SC_AIO_MAX", _SC_AIO_MAX}, +#endif +#ifdef _SC_AIO_PRIO_DELTA_MAX + {"SC_AIO_PRIO_DELTA_MAX", _SC_AIO_PRIO_DELTA_MAX}, +#endif +#ifdef _SC_ARG_MAX + {"SC_ARG_MAX", _SC_ARG_MAX}, +#endif +#ifdef _SC_ASYNCHRONOUS_IO + {"SC_ASYNCHRONOUS_IO", _SC_ASYNCHRONOUS_IO}, +#endif +#ifdef _SC_ATEXIT_MAX + {"SC_ATEXIT_MAX", _SC_ATEXIT_MAX}, +#endif +#ifdef _SC_AUDIT + {"SC_AUDIT", _SC_AUDIT}, +#endif +#ifdef _SC_AVPHYS_PAGES + {"SC_AVPHYS_PAGES", _SC_AVPHYS_PAGES}, +#endif +#ifdef _SC_BC_BASE_MAX + {"SC_BC_BASE_MAX", _SC_BC_BASE_MAX}, +#endif +#ifdef _SC_BC_DIM_MAX + {"SC_BC_DIM_MAX", _SC_BC_DIM_MAX}, +#endif +#ifdef _SC_BC_SCALE_MAX + {"SC_BC_SCALE_MAX", _SC_BC_SCALE_MAX}, +#endif +#ifdef _SC_BC_STRING_MAX + {"SC_BC_STRING_MAX", _SC_BC_STRING_MAX}, +#endif +#ifdef _SC_CAP + {"SC_CAP", _SC_CAP}, +#endif +#ifdef _SC_CHARCLASS_NAME_MAX + {"SC_CHARCLASS_NAME_MAX", _SC_CHARCLASS_NAME_MAX}, +#endif +#ifdef _SC_CHAR_BIT + {"SC_CHAR_BIT", _SC_CHAR_BIT}, +#endif +#ifdef _SC_CHAR_MAX + {"SC_CHAR_MAX", _SC_CHAR_MAX}, +#endif +#ifdef _SC_CHAR_MIN + {"SC_CHAR_MIN", _SC_CHAR_MIN}, +#endif +#ifdef _SC_CHILD_MAX + {"SC_CHILD_MAX", _SC_CHILD_MAX}, +#endif +#ifdef _SC_CLK_TCK + {"SC_CLK_TCK", _SC_CLK_TCK}, +#endif +#ifdef _SC_COHER_BLKSZ + {"SC_COHER_BLKSZ", _SC_COHER_BLKSZ}, +#endif +#ifdef _SC_COLL_WEIGHTS_MAX + {"SC_COLL_WEIGHTS_MAX", _SC_COLL_WEIGHTS_MAX}, +#endif +#ifdef _SC_DCACHE_ASSOC + {"SC_DCACHE_ASSOC", _SC_DCACHE_ASSOC}, +#endif +#ifdef _SC_DCACHE_BLKSZ + {"SC_DCACHE_BLKSZ", _SC_DCACHE_BLKSZ}, +#endif +#ifdef _SC_DCACHE_LINESZ + {"SC_DCACHE_LINESZ", _SC_DCACHE_LINESZ}, +#endif +#ifdef _SC_DCACHE_SZ + {"SC_DCACHE_SZ", _SC_DCACHE_SZ}, +#endif +#ifdef _SC_DCACHE_TBLKSZ + {"SC_DCACHE_TBLKSZ", _SC_DCACHE_TBLKSZ}, +#endif +#ifdef _SC_DELAYTIMER_MAX + {"SC_DELAYTIMER_MAX", _SC_DELAYTIMER_MAX}, +#endif +#ifdef _SC_EQUIV_CLASS_MAX + {"SC_EQUIV_CLASS_MAX", _SC_EQUIV_CLASS_MAX}, +#endif +#ifdef _SC_EXPR_NEST_MAX + {"SC_EXPR_NEST_MAX", _SC_EXPR_NEST_MAX}, +#endif +#ifdef _SC_FSYNC + {"SC_FSYNC", _SC_FSYNC}, +#endif +#ifdef _SC_GETGR_R_SIZE_MAX + {"SC_GETGR_R_SIZE_MAX", _SC_GETGR_R_SIZE_MAX}, +#endif +#ifdef _SC_GETPW_R_SIZE_MAX + {"SC_GETPW_R_SIZE_MAX", _SC_GETPW_R_SIZE_MAX}, +#endif +#ifdef _SC_ICACHE_ASSOC + {"SC_ICACHE_ASSOC", _SC_ICACHE_ASSOC}, +#endif +#ifdef _SC_ICACHE_BLKSZ + {"SC_ICACHE_BLKSZ", _SC_ICACHE_BLKSZ}, +#endif +#ifdef _SC_ICACHE_LINESZ + {"SC_ICACHE_LINESZ", _SC_ICACHE_LINESZ}, +#endif +#ifdef _SC_ICACHE_SZ + {"SC_ICACHE_SZ", _SC_ICACHE_SZ}, +#endif +#ifdef _SC_INF + {"SC_INF", _SC_INF}, +#endif +#ifdef _SC_INT_MAX + {"SC_INT_MAX", _SC_INT_MAX}, +#endif +#ifdef _SC_INT_MIN + {"SC_INT_MIN", _SC_INT_MIN}, +#endif +#ifdef _SC_IOV_MAX + {"SC_IOV_MAX", _SC_IOV_MAX}, +#endif +#ifdef _SC_IP_SECOPTS + {"SC_IP_SECOPTS", _SC_IP_SECOPTS}, +#endif +#ifdef _SC_JOB_CONTROL + {"SC_JOB_CONTROL", _SC_JOB_CONTROL}, +#endif +#ifdef _SC_KERN_POINTERS + {"SC_KERN_POINTERS", _SC_KERN_POINTERS}, +#endif +#ifdef _SC_KERN_SIM + {"SC_KERN_SIM", _SC_KERN_SIM}, +#endif +#ifdef _SC_LINE_MAX + {"SC_LINE_MAX", _SC_LINE_MAX}, +#endif +#ifdef _SC_LOGIN_NAME_MAX + {"SC_LOGIN_NAME_MAX", _SC_LOGIN_NAME_MAX}, +#endif +#ifdef _SC_LOGNAME_MAX + {"SC_LOGNAME_MAX", _SC_LOGNAME_MAX}, +#endif +#ifdef _SC_LONG_BIT + {"SC_LONG_BIT", _SC_LONG_BIT}, +#endif +#ifdef _SC_MAC + {"SC_MAC", _SC_MAC}, +#endif +#ifdef _SC_MAPPED_FILES + {"SC_MAPPED_FILES", _SC_MAPPED_FILES}, +#endif +#ifdef _SC_MAXPID + {"SC_MAXPID", _SC_MAXPID}, +#endif +#ifdef _SC_MB_LEN_MAX + {"SC_MB_LEN_MAX", _SC_MB_LEN_MAX}, +#endif +#ifdef _SC_MEMLOCK + {"SC_MEMLOCK", _SC_MEMLOCK}, +#endif +#ifdef _SC_MEMLOCK_RANGE + {"SC_MEMLOCK_RANGE", _SC_MEMLOCK_RANGE}, +#endif +#ifdef _SC_MEMORY_PROTECTION + {"SC_MEMORY_PROTECTION", _SC_MEMORY_PROTECTION}, +#endif +#ifdef _SC_MESSAGE_PASSING + {"SC_MESSAGE_PASSING", _SC_MESSAGE_PASSING}, +#endif +#ifdef _SC_MMAP_FIXED_ALIGNMENT + {"SC_MMAP_FIXED_ALIGNMENT", _SC_MMAP_FIXED_ALIGNMENT}, +#endif +#ifdef _SC_MQ_OPEN_MAX + {"SC_MQ_OPEN_MAX", _SC_MQ_OPEN_MAX}, +#endif +#ifdef _SC_MQ_PRIO_MAX + {"SC_MQ_PRIO_MAX", _SC_MQ_PRIO_MAX}, +#endif +#ifdef _SC_NACLS_MAX + {"SC_NACLS_MAX", _SC_NACLS_MAX}, +#endif +#ifdef _SC_NGROUPS_MAX + {"SC_NGROUPS_MAX", _SC_NGROUPS_MAX}, +#endif +#ifdef _SC_NL_ARGMAX + {"SC_NL_ARGMAX", _SC_NL_ARGMAX}, +#endif +#ifdef _SC_NL_LANGMAX + {"SC_NL_LANGMAX", _SC_NL_LANGMAX}, +#endif +#ifdef _SC_NL_MSGMAX + {"SC_NL_MSGMAX", _SC_NL_MSGMAX}, +#endif +#ifdef _SC_NL_NMAX + {"SC_NL_NMAX", _SC_NL_NMAX}, +#endif +#ifdef _SC_NL_SETMAX + {"SC_NL_SETMAX", _SC_NL_SETMAX}, +#endif +#ifdef _SC_NL_TEXTMAX + {"SC_NL_TEXTMAX", _SC_NL_TEXTMAX}, +#endif +#ifdef _SC_NPROCESSORS_CONF + {"SC_NPROCESSORS_CONF", _SC_NPROCESSORS_CONF}, +#endif +#ifdef _SC_NPROCESSORS_ONLN + {"SC_NPROCESSORS_ONLN", _SC_NPROCESSORS_ONLN}, +#endif +#ifdef _SC_NPROC_CONF + {"SC_NPROC_CONF", _SC_NPROC_CONF}, +#endif +#ifdef _SC_NPROC_ONLN + {"SC_NPROC_ONLN", _SC_NPROC_ONLN}, +#endif +#ifdef _SC_NZERO + {"SC_NZERO", _SC_NZERO}, +#endif +#ifdef _SC_OPEN_MAX + {"SC_OPEN_MAX", _SC_OPEN_MAX}, +#endif +#ifdef _SC_PAGESIZE + {"SC_PAGESIZE", _SC_PAGESIZE}, +#endif +#ifdef _SC_PAGE_SIZE + {"SC_PAGE_SIZE", _SC_PAGE_SIZE}, +#endif +#ifdef _SC_AIX_REALMEM + {"SC_AIX_REALMEM", _SC_AIX_REALMEM}, +#endif +#ifdef _SC_PASS_MAX + {"SC_PASS_MAX", _SC_PASS_MAX}, +#endif +#ifdef _SC_PHYS_PAGES + {"SC_PHYS_PAGES", _SC_PHYS_PAGES}, +#endif +#ifdef _SC_PII + {"SC_PII", _SC_PII}, +#endif +#ifdef _SC_PII_INTERNET + {"SC_PII_INTERNET", _SC_PII_INTERNET}, +#endif +#ifdef _SC_PII_INTERNET_DGRAM + {"SC_PII_INTERNET_DGRAM", _SC_PII_INTERNET_DGRAM}, +#endif +#ifdef _SC_PII_INTERNET_STREAM + {"SC_PII_INTERNET_STREAM", _SC_PII_INTERNET_STREAM}, +#endif +#ifdef _SC_PII_OSI + {"SC_PII_OSI", _SC_PII_OSI}, +#endif +#ifdef _SC_PII_OSI_CLTS + {"SC_PII_OSI_CLTS", _SC_PII_OSI_CLTS}, +#endif +#ifdef _SC_PII_OSI_COTS + {"SC_PII_OSI_COTS", _SC_PII_OSI_COTS}, +#endif +#ifdef _SC_PII_OSI_M + {"SC_PII_OSI_M", _SC_PII_OSI_M}, +#endif +#ifdef _SC_PII_SOCKET + {"SC_PII_SOCKET", _SC_PII_SOCKET}, +#endif +#ifdef _SC_PII_XTI + {"SC_PII_XTI", _SC_PII_XTI}, +#endif +#ifdef _SC_POLL + {"SC_POLL", _SC_POLL}, +#endif +#ifdef _SC_PRIORITIZED_IO + {"SC_PRIORITIZED_IO", _SC_PRIORITIZED_IO}, +#endif +#ifdef _SC_PRIORITY_SCHEDULING + {"SC_PRIORITY_SCHEDULING", _SC_PRIORITY_SCHEDULING}, +#endif +#ifdef _SC_REALTIME_SIGNALS + {"SC_REALTIME_SIGNALS", _SC_REALTIME_SIGNALS}, +#endif +#ifdef _SC_RE_DUP_MAX + {"SC_RE_DUP_MAX", _SC_RE_DUP_MAX}, +#endif +#ifdef _SC_RTSIG_MAX + {"SC_RTSIG_MAX", _SC_RTSIG_MAX}, +#endif +#ifdef _SC_SAVED_IDS + {"SC_SAVED_IDS", _SC_SAVED_IDS}, +#endif +#ifdef _SC_SCHAR_MAX + {"SC_SCHAR_MAX", _SC_SCHAR_MAX}, +#endif +#ifdef _SC_SCHAR_MIN + {"SC_SCHAR_MIN", _SC_SCHAR_MIN}, +#endif +#ifdef _SC_SELECT + {"SC_SELECT", _SC_SELECT}, +#endif +#ifdef _SC_SEMAPHORES + {"SC_SEMAPHORES", _SC_SEMAPHORES}, +#endif +#ifdef _SC_SEM_NSEMS_MAX + {"SC_SEM_NSEMS_MAX", _SC_SEM_NSEMS_MAX}, +#endif +#ifdef _SC_SEM_VALUE_MAX + {"SC_SEM_VALUE_MAX", _SC_SEM_VALUE_MAX}, +#endif +#ifdef _SC_SHARED_MEMORY_OBJECTS + {"SC_SHARED_MEMORY_OBJECTS", _SC_SHARED_MEMORY_OBJECTS}, +#endif +#ifdef _SC_SHRT_MAX + {"SC_SHRT_MAX", _SC_SHRT_MAX}, +#endif +#ifdef _SC_SHRT_MIN + {"SC_SHRT_MIN", _SC_SHRT_MIN}, +#endif +#ifdef _SC_SIGQUEUE_MAX + {"SC_SIGQUEUE_MAX", _SC_SIGQUEUE_MAX}, +#endif +#ifdef _SC_SIGRT_MAX + {"SC_SIGRT_MAX", _SC_SIGRT_MAX}, +#endif +#ifdef _SC_SIGRT_MIN + {"SC_SIGRT_MIN", _SC_SIGRT_MIN}, +#endif +#ifdef _SC_SOFTPOWER + {"SC_SOFTPOWER", _SC_SOFTPOWER}, +#endif +#ifdef _SC_SPLIT_CACHE + {"SC_SPLIT_CACHE", _SC_SPLIT_CACHE}, +#endif +#ifdef _SC_SSIZE_MAX + {"SC_SSIZE_MAX", _SC_SSIZE_MAX}, +#endif +#ifdef _SC_STACK_PROT + {"SC_STACK_PROT", _SC_STACK_PROT}, +#endif +#ifdef _SC_STREAM_MAX + {"SC_STREAM_MAX", _SC_STREAM_MAX}, +#endif +#ifdef _SC_SYNCHRONIZED_IO + {"SC_SYNCHRONIZED_IO", _SC_SYNCHRONIZED_IO}, +#endif +#ifdef _SC_THREADS + {"SC_THREADS", _SC_THREADS}, +#endif +#ifdef _SC_THREAD_ATTR_STACKADDR + {"SC_THREAD_ATTR_STACKADDR", _SC_THREAD_ATTR_STACKADDR}, +#endif +#ifdef _SC_THREAD_ATTR_STACKSIZE + {"SC_THREAD_ATTR_STACKSIZE", _SC_THREAD_ATTR_STACKSIZE}, +#endif +#ifdef _SC_THREAD_DESTRUCTOR_ITERATIONS + {"SC_THREAD_DESTRUCTOR_ITERATIONS", _SC_THREAD_DESTRUCTOR_ITERATIONS}, +#endif +#ifdef _SC_THREAD_KEYS_MAX + {"SC_THREAD_KEYS_MAX", _SC_THREAD_KEYS_MAX}, +#endif +#ifdef _SC_THREAD_PRIORITY_SCHEDULING + {"SC_THREAD_PRIORITY_SCHEDULING", _SC_THREAD_PRIORITY_SCHEDULING}, +#endif +#ifdef _SC_THREAD_PRIO_INHERIT + {"SC_THREAD_PRIO_INHERIT", _SC_THREAD_PRIO_INHERIT}, +#endif +#ifdef _SC_THREAD_PRIO_PROTECT + {"SC_THREAD_PRIO_PROTECT", _SC_THREAD_PRIO_PROTECT}, +#endif +#ifdef _SC_THREAD_PROCESS_SHARED + {"SC_THREAD_PROCESS_SHARED", _SC_THREAD_PROCESS_SHARED}, +#endif +#ifdef _SC_THREAD_SAFE_FUNCTIONS + {"SC_THREAD_SAFE_FUNCTIONS", _SC_THREAD_SAFE_FUNCTIONS}, +#endif +#ifdef _SC_THREAD_STACK_MIN + {"SC_THREAD_STACK_MIN", _SC_THREAD_STACK_MIN}, +#endif +#ifdef _SC_THREAD_THREADS_MAX + {"SC_THREAD_THREADS_MAX", _SC_THREAD_THREADS_MAX}, +#endif +#ifdef _SC_TIMERS + {"SC_TIMERS", _SC_TIMERS}, +#endif +#ifdef _SC_TIMER_MAX + {"SC_TIMER_MAX", _SC_TIMER_MAX}, +#endif +#ifdef _SC_TTY_NAME_MAX + {"SC_TTY_NAME_MAX", _SC_TTY_NAME_MAX}, +#endif +#ifdef _SC_TZNAME_MAX + {"SC_TZNAME_MAX", _SC_TZNAME_MAX}, +#endif +#ifdef _SC_T_IOV_MAX + {"SC_T_IOV_MAX", _SC_T_IOV_MAX}, +#endif +#ifdef _SC_UCHAR_MAX + {"SC_UCHAR_MAX", _SC_UCHAR_MAX}, +#endif +#ifdef _SC_UINT_MAX + {"SC_UINT_MAX", _SC_UINT_MAX}, +#endif +#ifdef _SC_UIO_MAXIOV + {"SC_UIO_MAXIOV", _SC_UIO_MAXIOV}, +#endif +#ifdef _SC_ULONG_MAX + {"SC_ULONG_MAX", _SC_ULONG_MAX}, +#endif +#ifdef _SC_USHRT_MAX + {"SC_USHRT_MAX", _SC_USHRT_MAX}, +#endif +#ifdef _SC_VERSION + {"SC_VERSION", _SC_VERSION}, +#endif +#ifdef _SC_WORD_BIT + {"SC_WORD_BIT", _SC_WORD_BIT}, +#endif +#ifdef _SC_XBS5_ILP32_OFF32 + {"SC_XBS5_ILP32_OFF32", _SC_XBS5_ILP32_OFF32}, +#endif +#ifdef _SC_XBS5_ILP32_OFFBIG + {"SC_XBS5_ILP32_OFFBIG", _SC_XBS5_ILP32_OFFBIG}, +#endif +#ifdef _SC_XBS5_LP64_OFF64 + {"SC_XBS5_LP64_OFF64", _SC_XBS5_LP64_OFF64}, +#endif +#ifdef _SC_XBS5_LPBIG_OFFBIG + {"SC_XBS5_LPBIG_OFFBIG", _SC_XBS5_LPBIG_OFFBIG}, +#endif +#ifdef _SC_XOPEN_CRYPT + {"SC_XOPEN_CRYPT", _SC_XOPEN_CRYPT}, +#endif +#ifdef _SC_XOPEN_ENH_I18N + {"SC_XOPEN_ENH_I18N", _SC_XOPEN_ENH_I18N}, +#endif +#ifdef _SC_XOPEN_LEGACY + {"SC_XOPEN_LEGACY", _SC_XOPEN_LEGACY}, +#endif +#ifdef _SC_XOPEN_REALTIME + {"SC_XOPEN_REALTIME", _SC_XOPEN_REALTIME}, +#endif +#ifdef _SC_XOPEN_REALTIME_THREADS + {"SC_XOPEN_REALTIME_THREADS", _SC_XOPEN_REALTIME_THREADS}, +#endif +#ifdef _SC_XOPEN_SHM + {"SC_XOPEN_SHM", _SC_XOPEN_SHM}, +#endif +#ifdef _SC_XOPEN_UNIX + {"SC_XOPEN_UNIX", _SC_XOPEN_UNIX}, +#endif +#ifdef _SC_XOPEN_VERSION + {"SC_XOPEN_VERSION", _SC_XOPEN_VERSION}, +#endif +#ifdef _SC_XOPEN_XCU_VERSION + {"SC_XOPEN_XCU_VERSION", _SC_XOPEN_XCU_VERSION}, +#endif +#ifdef _SC_XOPEN_XPG2 + {"SC_XOPEN_XPG2", _SC_XOPEN_XPG2}, +#endif +#ifdef _SC_XOPEN_XPG3 + {"SC_XOPEN_XPG3", _SC_XOPEN_XPG3}, +#endif +#ifdef _SC_XOPEN_XPG4 + {"SC_XOPEN_XPG4", _SC_XOPEN_XPG4}, +#endif +#ifdef _SC_MINSIGSTKSZ + {"SC_MINSIGSTKSZ", _SC_MINSIGSTKSZ}, +#endif +}; + +static int +conv_sysconf_confname(PyObject *arg, int *valuep) +{ + return conv_confname(arg, valuep, posix_constants_sysconf, + sizeof(posix_constants_sysconf) + / sizeof(struct constdef)); +} + + +/*[clinic input] +os.sysconf -> long + name: sysconf_confname + / + +Return an integer-valued system configuration variable. +[clinic start generated code]*/ + +static long +os_sysconf_impl(PyObject *module, int name) +/*[clinic end generated code: output=3662f945fc0cc756 input=279e3430a33f29e4]*/ +{ + long value; + + errno = 0; + value = sysconf(name); + if (value == -1 && errno != 0) + posix_error(); + return value; +} +#endif /* HAVE_SYSCONF */ + + +/* This code is used to ensure that the tables of configuration value names + * are in sorted order as required by conv_confname(), and also to build + * the exported dictionaries that are used to publish information about the + * names available on the host platform. + * + * Sorting the table at runtime ensures that the table is properly ordered + * when used, even for platforms we're not able to test on. It also makes + * it easier to add additional entries to the tables. + */ + +static int +cmp_constdefs(const void *v1, const void *v2) +{ + const struct constdef *c1 = + (const struct constdef *) v1; + const struct constdef *c2 = + (const struct constdef *) v2; + + return strcmp(c1->name, c2->name); +} + +static int +setup_confname_table(struct constdef *table, size_t tablesize, + const char *tablename, PyObject *module) +{ + PyObject *d = NULL; + size_t i; + + qsort(table, tablesize, sizeof(struct constdef), cmp_constdefs); + d = PyDict_New(); + if (d == NULL) + return -1; + + for (i=0; i < tablesize; ++i) { + PyObject *o = PyLong_FromLong(table[i].value); + if (o == NULL || PyDict_SetItemString(d, table[i].name, o) == -1) { + Py_XDECREF(o); + Py_DECREF(d); + return -1; + } + Py_DECREF(o); + } + return PyModule_Add(module, tablename, d); +} + +/* Return -1 on failure, 0 on success. */ +static int +setup_confname_tables(PyObject *module) +{ +#if defined(HAVE_FPATHCONF) || defined(HAVE_PATHCONF) + if (setup_confname_table(posix_constants_pathconf, + sizeof(posix_constants_pathconf) + / sizeof(struct constdef), + "pathconf_names", module)) + return -1; +#endif +#ifdef HAVE_CONFSTR + if (setup_confname_table(posix_constants_confstr, + sizeof(posix_constants_confstr) + / sizeof(struct constdef), + "confstr_names", module)) + return -1; +#endif +#ifdef HAVE_SYSCONF + if (setup_confname_table(posix_constants_sysconf, + sizeof(posix_constants_sysconf) + / sizeof(struct constdef), + "sysconf_names", module)) + return -1; +#endif + return 0; +} + + +/*[clinic input] +os.abort + +Abort the interpreter immediately. + +This function 'dumps core' or otherwise fails in the hardest way possible +on the hosting operating system. This function never returns. +[clinic start generated code]*/ + +static PyObject * +os_abort_impl(PyObject *module) +/*[clinic end generated code: output=dcf52586dad2467c input=cf2c7d98bc504047]*/ +{ + abort(); + /*NOTREACHED*/ +#ifndef __clang__ + /* Issue #28152: abort() is declared with __attribute__((__noreturn__)). + GCC emits a warning without "return NULL;" (compiler bug?), but Clang + is smarter and emits a warning on the return. */ + Py_FatalError("abort() called from Python code didn't abort!"); + return NULL; +#endif +} + +#ifdef MS_WINDOWS +/* Grab ShellExecute dynamically from shell32 */ +static int has_ShellExecute = -1; +static HINSTANCE (CALLBACK *Py_ShellExecuteW)(HWND, LPCWSTR, LPCWSTR, LPCWSTR, + LPCWSTR, INT); +static int +check_ShellExecute(void) +{ + HINSTANCE hShell32; + + /* only recheck */ + if (-1 == has_ShellExecute) { + Py_BEGIN_ALLOW_THREADS + /* Security note: this call is not vulnerable to "DLL hijacking". + SHELL32 is part of "KnownDLLs" and so Windows always load + the system SHELL32.DLL, even if there is another SHELL32.DLL + in the DLL search path. */ + hShell32 = LoadLibraryW(L"SHELL32"); + if (hShell32) { + *(FARPROC*)&Py_ShellExecuteW = GetProcAddress(hShell32, + "ShellExecuteW"); + has_ShellExecute = Py_ShellExecuteW != NULL; + } else { + has_ShellExecute = 0; + } + Py_END_ALLOW_THREADS + } + return has_ShellExecute; +} + + +/*[clinic input] +os.startfile + filepath: path_t + operation: Py_UNICODE = NULL + arguments: Py_UNICODE = NULL + cwd: path_t(nullable=True) = None + show_cmd: int = 1 + +Start a file with its associated application. + +When "operation" is not specified or "open", this acts like +double-clicking the file in Explorer, or giving the file name as an +argument to the DOS "start" command: the file is opened with whatever +application (if any) its extension is associated. +When another "operation" is given, it specifies what should be done with +the file. A typical operation is "print". + +"arguments" is passed to the application, but should be omitted if the +file is a document. + +"cwd" is the working directory for the operation. If "filepath" is +relative, it will be resolved against this directory. This argument +should usually be an absolute path. + +"show_cmd" can be used to override the recommended visibility option. +See the Windows ShellExecute documentation for values. + +startfile returns as soon as the associated application is launched. +There is no option to wait for the application to close, and no way +to retrieve the application's exit status. + +The filepath is relative to the current directory. If you want to use +an absolute path, make sure the first character is not a slash ("/"); +the underlying Win32 ShellExecute function doesn't work if it is. +[clinic start generated code]*/ + +static PyObject * +os_startfile_impl(PyObject *module, path_t *filepath, + const wchar_t *operation, const wchar_t *arguments, + path_t *cwd, int show_cmd) +/*[clinic end generated code: output=1c6f2f3340e31ffa input=8248997b80669622]*/ +{ + HINSTANCE rc; + + if(!check_ShellExecute()) { + /* If the OS doesn't have ShellExecute, return a + NotImplementedError. */ + return PyErr_Format(PyExc_NotImplementedError, + "startfile not available on this platform"); + } + + if (PySys_Audit("os.startfile", "Ou", filepath->object, operation) < 0) { + return NULL; + } + if (PySys_Audit("os.startfile/2", "OuuOi", filepath->object, operation, + arguments, cwd->object ? cwd->object : Py_None, + show_cmd) < 0) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + rc = Py_ShellExecuteW((HWND)0, operation, filepath->wide, + arguments, cwd->wide, show_cmd); + Py_END_ALLOW_THREADS + + if (rc <= (HINSTANCE)32) { + win32_error_object("startfile", filepath->object); + return NULL; + } + Py_RETURN_NONE; +} +#endif /* MS_WINDOWS */ + + +#ifdef HAVE_GETLOADAVG +/*[clinic input] +os.getloadavg + +Return average recent system load information. + +Return the number of processes in the system run queue averaged over +the last 1, 5, and 15 minutes as a tuple of three floats. +Raises OSError if the load average was unobtainable. +[clinic start generated code]*/ + +static PyObject * +os_getloadavg_impl(PyObject *module) +/*[clinic end generated code: output=9ad3a11bfb4f4bd2 input=3d6d826b76d8a34e]*/ +{ + double loadavg[3]; + if (getloadavg(loadavg, 3)!=3) { + PyErr_SetString(PyExc_OSError, "Load averages are unobtainable"); + return NULL; + } else + return Py_BuildValue("ddd", loadavg[0], loadavg[1], loadavg[2]); +} +#endif /* HAVE_GETLOADAVG */ + + +/*[clinic input] +os.device_encoding + fd: int + +Return a string describing the encoding of a terminal's file descriptor. + +The file descriptor must be attached to a terminal. +If the device is not a terminal, return None. +[clinic start generated code]*/ + +static PyObject * +os_device_encoding_impl(PyObject *module, int fd) +/*[clinic end generated code: output=e0d294bbab7e8c2b input=9e1d4a42b66df312]*/ +{ + return _Py_device_encoding(fd); +} + + +#ifdef HAVE_SETRESUID +/*[clinic input] +os.setresuid + + ruid: uid_t + euid: uid_t + suid: uid_t + / + +Set the current process's real, effective, and saved user ids. +[clinic start generated code]*/ + +static PyObject * +os_setresuid_impl(PyObject *module, uid_t ruid, uid_t euid, uid_t suid) +/*[clinic end generated code: output=834a641e15373e97 input=9e33cb79a82792f3]*/ +{ + if (setresuid(ruid, euid, suid) < 0) + return posix_error(); + Py_RETURN_NONE; +} +#endif /* HAVE_SETRESUID */ + + +#ifdef HAVE_SETRESGID +/*[clinic input] +os.setresgid + + rgid: gid_t + egid: gid_t + sgid: gid_t + / + +Set the current process's real, effective, and saved group ids. +[clinic start generated code]*/ + +static PyObject * +os_setresgid_impl(PyObject *module, gid_t rgid, gid_t egid, gid_t sgid) +/*[clinic end generated code: output=6aa402f3d2e514a9 input=33e9e0785ef426b1]*/ +{ + if (setresgid(rgid, egid, sgid) < 0) + return posix_error(); + Py_RETURN_NONE; +} +#endif /* HAVE_SETRESGID */ + + +#ifdef HAVE_GETRESUID +/*[clinic input] +os.getresuid + +Return a tuple of the current process's real, effective, and saved user ids. +[clinic start generated code]*/ + +static PyObject * +os_getresuid_impl(PyObject *module) +/*[clinic end generated code: output=8e0becff5dece5bf input=41ccfa8e1f6517ad]*/ +{ + uid_t ruid, euid, suid; + if (getresuid(&ruid, &euid, &suid) < 0) + return posix_error(); + return Py_BuildValue("(NNN)", _PyLong_FromUid(ruid), + _PyLong_FromUid(euid), + _PyLong_FromUid(suid)); +} +#endif /* HAVE_GETRESUID */ + + +#ifdef HAVE_GETRESGID +/*[clinic input] +os.getresgid + +Return a tuple of the current process's real, effective, and saved group ids. +[clinic start generated code]*/ + +static PyObject * +os_getresgid_impl(PyObject *module) +/*[clinic end generated code: output=2719c4bfcf27fb9f input=517e68db9ca32df6]*/ +{ + gid_t rgid, egid, sgid; + if (getresgid(&rgid, &egid, &sgid) < 0) + return posix_error(); + return Py_BuildValue("(NNN)", _PyLong_FromGid(rgid), + _PyLong_FromGid(egid), + _PyLong_FromGid(sgid)); +} +#endif /* HAVE_GETRESGID */ + + +#ifdef USE_XATTRS +/*[clinic input] +os.getxattr + + path: path_t(allow_fd=True) + attribute: path_t + * + follow_symlinks: bool = True + +Return the value of extended attribute attribute on path. + +path may be either a string, a path-like object, or an open file descriptor. +If follow_symlinks is False, and the last element of the path is a symbolic + link, getxattr will examine the symbolic link itself instead of the file + the link points to. + +[clinic start generated code]*/ + +static PyObject * +os_getxattr_impl(PyObject *module, path_t *path, path_t *attribute, + int follow_symlinks) +/*[clinic end generated code: output=5f2f44200a43cff2 input=025789491708f7eb]*/ +{ + Py_ssize_t i; + PyObject *buffer = NULL; + + if (fd_and_follow_symlinks_invalid("getxattr", path->fd, follow_symlinks)) + return NULL; + + if (PySys_Audit("os.getxattr", "OO", path->object, attribute->object) < 0) { + return NULL; + } + + for (i = 0; ; i++) { + void *ptr; + ssize_t result; + static const Py_ssize_t buffer_sizes[] = {128, XATTR_SIZE_MAX, 0}; + Py_ssize_t buffer_size = buffer_sizes[i]; + if (!buffer_size) { + path_error(path); + return NULL; + } + buffer = PyBytes_FromStringAndSize(NULL, buffer_size); + if (!buffer) + return NULL; + ptr = PyBytes_AS_STRING(buffer); + + Py_BEGIN_ALLOW_THREADS; + if (path->fd >= 0) + result = fgetxattr(path->fd, attribute->narrow, ptr, buffer_size); + else if (follow_symlinks) + result = getxattr(path->narrow, attribute->narrow, ptr, buffer_size); + else + result = lgetxattr(path->narrow, attribute->narrow, ptr, buffer_size); + Py_END_ALLOW_THREADS; + + if (result < 0) { + if (errno == ERANGE) { + Py_DECREF(buffer); + continue; + } + path_error(path); + Py_DECREF(buffer); + return NULL; + } + + if (result != buffer_size) { + /* Can only shrink. */ + _PyBytes_Resize(&buffer, result); + } + break; + } + + return buffer; +} + + +/*[clinic input] +os.setxattr + + path: path_t(allow_fd=True) + attribute: path_t + value: Py_buffer + flags: int = 0 + * + follow_symlinks: bool = True + +Set extended attribute attribute on path to value. + +path may be either a string, a path-like object, or an open file descriptor. +If follow_symlinks is False, and the last element of the path is a symbolic + link, setxattr will modify the symbolic link itself instead of the file + the link points to. + +[clinic start generated code]*/ + +static PyObject * +os_setxattr_impl(PyObject *module, path_t *path, path_t *attribute, + Py_buffer *value, int flags, int follow_symlinks) +/*[clinic end generated code: output=98b83f63fdde26bb input=c17c0103009042f0]*/ +{ + ssize_t result; + + if (fd_and_follow_symlinks_invalid("setxattr", path->fd, follow_symlinks)) + return NULL; + + if (PySys_Audit("os.setxattr", "OOy#i", path->object, attribute->object, + value->buf, value->len, flags) < 0) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS; + if (path->fd > -1) + result = fsetxattr(path->fd, attribute->narrow, + value->buf, value->len, flags); + else if (follow_symlinks) + result = setxattr(path->narrow, attribute->narrow, + value->buf, value->len, flags); + else + result = lsetxattr(path->narrow, attribute->narrow, + value->buf, value->len, flags); + Py_END_ALLOW_THREADS; + + if (result) { + path_error(path); + return NULL; + } + + Py_RETURN_NONE; +} + + +/*[clinic input] +os.removexattr + + path: path_t(allow_fd=True) + attribute: path_t + * + follow_symlinks: bool = True + +Remove extended attribute attribute on path. + +path may be either a string, a path-like object, or an open file descriptor. +If follow_symlinks is False, and the last element of the path is a symbolic + link, removexattr will modify the symbolic link itself instead of the file + the link points to. + +[clinic start generated code]*/ + +static PyObject * +os_removexattr_impl(PyObject *module, path_t *path, path_t *attribute, + int follow_symlinks) +/*[clinic end generated code: output=521a51817980cda6 input=3d9a7d36fe2f7c4e]*/ +{ + ssize_t result; + + if (fd_and_follow_symlinks_invalid("removexattr", path->fd, follow_symlinks)) + return NULL; + + if (PySys_Audit("os.removexattr", "OO", path->object, attribute->object) < 0) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS; + if (path->fd > -1) + result = fremovexattr(path->fd, attribute->narrow); + else if (follow_symlinks) + result = removexattr(path->narrow, attribute->narrow); + else + result = lremovexattr(path->narrow, attribute->narrow); + Py_END_ALLOW_THREADS; + + if (result) { + return path_error(path); + } + + Py_RETURN_NONE; +} + + +/*[clinic input] +os.listxattr + + path: path_t(allow_fd=True, nullable=True) = None + * + follow_symlinks: bool = True + +Return a list of extended attributes on path. + +path may be either None, a string, a path-like object, or an open file descriptor. +if path is None, listxattr will examine the current directory. +If follow_symlinks is False, and the last element of the path is a symbolic + link, listxattr will examine the symbolic link itself instead of the file + the link points to. +[clinic start generated code]*/ + +static PyObject * +os_listxattr_impl(PyObject *module, path_t *path, int follow_symlinks) +/*[clinic end generated code: output=bebdb4e2ad0ce435 input=9826edf9fdb90869]*/ +{ + Py_ssize_t i; + PyObject *result = NULL; + const char *name; + char *buffer = NULL; + + if (fd_and_follow_symlinks_invalid("listxattr", path->fd, follow_symlinks)) + goto exit; + + if (PySys_Audit("os.listxattr", "(O)", + path->object ? path->object : Py_None) < 0) { + return NULL; + } + + name = path->narrow ? path->narrow : "."; + + for (i = 0; ; i++) { + const char *start, *trace, *end; + ssize_t length; + static const Py_ssize_t buffer_sizes[] = { 256, XATTR_LIST_MAX, 0 }; + Py_ssize_t buffer_size = buffer_sizes[i]; + if (!buffer_size) { + /* ERANGE */ + path_error(path); + break; + } + buffer = PyMem_Malloc(buffer_size); + if (!buffer) { + PyErr_NoMemory(); + break; + } + + Py_BEGIN_ALLOW_THREADS; + if (path->fd > -1) + length = flistxattr(path->fd, buffer, buffer_size); + else if (follow_symlinks) + length = listxattr(name, buffer, buffer_size); + else + length = llistxattr(name, buffer, buffer_size); + Py_END_ALLOW_THREADS; + + if (length < 0) { + if (errno == ERANGE) { + PyMem_Free(buffer); + buffer = NULL; + continue; + } + path_error(path); + break; + } + + result = PyList_New(0); + if (!result) { + goto exit; + } + + end = buffer + length; + for (trace = start = buffer; trace != end; trace++) { + if (!*trace) { + int error; + PyObject *attribute = PyUnicode_DecodeFSDefaultAndSize(start, + trace - start); + if (!attribute) { + Py_SETREF(result, NULL); + goto exit; + } + error = PyList_Append(result, attribute); + Py_DECREF(attribute); + if (error) { + Py_SETREF(result, NULL); + goto exit; + } + start = trace + 1; + } + } + break; + } +exit: + if (buffer) + PyMem_Free(buffer); + return result; +} +#endif /* USE_XATTRS */ + + +/*[clinic input] +os.urandom + + size: Py_ssize_t + / + +Return a bytes object containing random bytes suitable for cryptographic use. +[clinic start generated code]*/ + +static PyObject * +os_urandom_impl(PyObject *module, Py_ssize_t size) +/*[clinic end generated code: output=42c5cca9d18068e9 input=4067cdb1b6776c29]*/ +{ + PyObject *bytes; + int result; + + if (size < 0) + return PyErr_Format(PyExc_ValueError, + "negative argument not allowed"); + bytes = PyBytes_FromStringAndSize(NULL, size); + if (bytes == NULL) + return NULL; + + result = _PyOS_URandom(PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes)); + if (result == -1) { + Py_DECREF(bytes); + return NULL; + } + return bytes; +} + +#ifdef HAVE_MEMFD_CREATE +/*[clinic input] +os.memfd_create + + name: FSConverter + flags: unsigned_int(bitwise=True, c_default="MFD_CLOEXEC") = MFD_CLOEXEC + +[clinic start generated code]*/ + +static PyObject * +os_memfd_create_impl(PyObject *module, PyObject *name, unsigned int flags) +/*[clinic end generated code: output=6681ede983bdb9a6 input=a42cfc199bcd56e9]*/ +{ + int fd; + const char *bytes = PyBytes_AS_STRING(name); + Py_BEGIN_ALLOW_THREADS + fd = memfd_create(bytes, flags); + Py_END_ALLOW_THREADS + if (fd == -1) { + return PyErr_SetFromErrno(PyExc_OSError); + } + return PyLong_FromLong(fd); +} +#endif + +#if defined(HAVE_EVENTFD) && defined(EFD_CLOEXEC) +/*[clinic input] +os.eventfd + + initval: unsigned_int + flags: int(c_default="EFD_CLOEXEC") = EFD_CLOEXEC + +Creates and returns an event notification file descriptor. +[clinic start generated code]*/ + +static PyObject * +os_eventfd_impl(PyObject *module, unsigned int initval, int flags) +/*[clinic end generated code: output=ce9c9bbd1446f2de input=66203e3c50c4028b]*/ + +{ + /* initval is limited to uint32_t, internal counter is uint64_t */ + int fd; + Py_BEGIN_ALLOW_THREADS + fd = eventfd(initval, flags); + Py_END_ALLOW_THREADS + if (fd == -1) { + return PyErr_SetFromErrno(PyExc_OSError); + } + return PyLong_FromLong(fd); +} + +/*[clinic input] +os.eventfd_read + + fd: fildes + +Read eventfd value +[clinic start generated code]*/ + +static PyObject * +os_eventfd_read_impl(PyObject *module, int fd) +/*[clinic end generated code: output=8f2c7b59a3521fd1 input=110f8b57fa596afe]*/ +{ + eventfd_t value; + int result; + Py_BEGIN_ALLOW_THREADS + result = eventfd_read(fd, &value); + Py_END_ALLOW_THREADS + if (result == -1) { + return PyErr_SetFromErrno(PyExc_OSError); + } + return PyLong_FromUnsignedLongLong(value); +} + +/*[clinic input] +os.eventfd_write + + fd: fildes + value: unsigned_long_long + +Write eventfd value. +[clinic start generated code]*/ + +static PyObject * +os_eventfd_write_impl(PyObject *module, int fd, unsigned long long value) +/*[clinic end generated code: output=bebd9040bbf987f5 input=156de8555be5a949]*/ +{ + int result; + Py_BEGIN_ALLOW_THREADS + result = eventfd_write(fd, value); + Py_END_ALLOW_THREADS + if (result == -1) { + return PyErr_SetFromErrno(PyExc_OSError); + } + Py_RETURN_NONE; +} +#endif /* HAVE_EVENTFD && EFD_CLOEXEC */ + +/* Terminal size querying */ + +PyDoc_STRVAR(TerminalSize_docstring, + "A tuple of (columns, lines) for holding terminal window size"); + +static PyStructSequence_Field TerminalSize_fields[] = { + {"columns", "width of the terminal window in characters"}, + {"lines", "height of the terminal window in characters"}, + {NULL, NULL} +}; + +static PyStructSequence_Desc TerminalSize_desc = { + "os.terminal_size", + TerminalSize_docstring, + TerminalSize_fields, + 2, +}; + +#if defined(TERMSIZE_USE_CONIO) || defined(TERMSIZE_USE_IOCTL) +/*[clinic input] +os.get_terminal_size + + fd: int(c_default="fileno(stdout)", py_default="") = -1 + / + +Return the size of the terminal window as (columns, lines). + +The optional argument fd (default standard output) specifies +which file descriptor should be queried. + +If the file descriptor is not connected to a terminal, an OSError +is thrown. + +This function will only be defined if an implementation is +available for this system. + +shutil.get_terminal_size is the high-level function which should +normally be used, os.get_terminal_size is the low-level implementation. +[clinic start generated code]*/ + +static PyObject * +os_get_terminal_size_impl(PyObject *module, int fd) +/*[clinic end generated code: output=fbab93acef980508 input=ead5679b82ddb920]*/ +{ + int columns, lines; + PyObject *termsize; + + /* Under some conditions stdout may not be connected and + * fileno(stdout) may point to an invalid file descriptor. For example + * GUI apps don't have valid standard streams by default. + * + * If this happens, and the optional fd argument is not present, + * the ioctl below will fail returning EBADF. This is what we want. + */ + +#ifdef TERMSIZE_USE_IOCTL + { + struct winsize w; + if (ioctl(fd, TIOCGWINSZ, &w)) + return PyErr_SetFromErrno(PyExc_OSError); + columns = w.ws_col; + lines = w.ws_row; + } +#endif /* TERMSIZE_USE_IOCTL */ + +#ifdef TERMSIZE_USE_CONIO + { + HANDLE handle; + CONSOLE_SCREEN_BUFFER_INFO csbi; + handle = _Py_get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) + return NULL; + + if (!GetConsoleScreenBufferInfo(handle, &csbi)) + return PyErr_SetFromWindowsErr(0); + + columns = csbi.srWindow.Right - csbi.srWindow.Left + 1; + lines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; + } +#endif /* TERMSIZE_USE_CONIO */ + + PyObject *TerminalSizeType = get_posix_state(module)->TerminalSizeType; + termsize = PyStructSequence_New((PyTypeObject *)TerminalSizeType); + if (termsize == NULL) + return NULL; + + int pos = 0; + +#define SET_TERMSIZE(CALL) \ + do { \ + PyObject *item = (CALL); \ + if (item == NULL) { \ + Py_DECREF(termsize); \ + return NULL; \ + } \ + PyStructSequence_SET_ITEM(termsize, pos++, item); \ + } while(0) + + SET_TERMSIZE(PyLong_FromLong(columns)); + SET_TERMSIZE(PyLong_FromLong(lines)); +#undef SET_TERMSIZE + + return termsize; +} +#endif /* defined(TERMSIZE_USE_CONIO) || defined(TERMSIZE_USE_IOCTL) */ + +/*[clinic input] +os.cpu_count + +Return the number of logical CPUs in the system. + +Return None if indeterminable. +[clinic start generated code]*/ + +static PyObject * +os_cpu_count_impl(PyObject *module) +/*[clinic end generated code: output=5fc29463c3936a9c input=ba2f6f8980a0e2eb]*/ +{ + const PyConfig *config = _Py_GetConfig(); + if (config->cpu_count > 0) { + return PyLong_FromLong(config->cpu_count); + } + + int ncpu = 0; +#ifdef MS_WINDOWS +# ifdef MS_WINDOWS_DESKTOP + ncpu = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); +# else + ncpu = 0; +# endif + +#elif defined(__hpux) + ncpu = mpctl(MPC_GETNUMSPUS, NULL, NULL); + +#elif defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN) + ncpu = sysconf(_SC_NPROCESSORS_ONLN); + +#elif defined(__VXWORKS__) + ncpu = _Py_popcount32(vxCpuEnabledGet()); + +#elif defined(__DragonFly__) || \ + defined(__OpenBSD__) || \ + defined(__FreeBSD__) || \ + defined(__NetBSD__) || \ + defined(__APPLE__) + ncpu = 0; + size_t len = sizeof(ncpu); + int mib[2] = {CTL_HW, HW_NCPU}; + if (sysctl(mib, 2, &ncpu, &len, NULL, 0) != 0) { + ncpu = 0; + } +#endif + + if (ncpu < 1) { + Py_RETURN_NONE; + } + return PyLong_FromLong(ncpu); +} + + +/*[clinic input] +os.get_inheritable -> bool + + fd: int + / + +Get the close-on-exe flag of the specified file descriptor. +[clinic start generated code]*/ + +static int +os_get_inheritable_impl(PyObject *module, int fd) +/*[clinic end generated code: output=0445e20e149aa5b8 input=89ac008dc9ab6b95]*/ +{ + int return_value; + _Py_BEGIN_SUPPRESS_IPH + return_value = _Py_get_inheritable(fd); + _Py_END_SUPPRESS_IPH + return return_value; +} + + +/*[clinic input] +os.set_inheritable + fd: int + inheritable: int + / + +Set the inheritable flag of the specified file descriptor. +[clinic start generated code]*/ + +static PyObject * +os_set_inheritable_impl(PyObject *module, int fd, int inheritable) +/*[clinic end generated code: output=f1b1918a2f3c38c2 input=9ceaead87a1e2402]*/ +{ + int result; + + _Py_BEGIN_SUPPRESS_IPH + result = _Py_set_inheritable(fd, inheritable, NULL); + _Py_END_SUPPRESS_IPH + if (result < 0) + return NULL; + Py_RETURN_NONE; +} + + +#ifdef MS_WINDOWS +#ifndef HANDLE_FLAG_INHERIT +#define HANDLE_FLAG_INHERIT 0x00000001 +#endif + +/*[clinic input] +os.get_handle_inheritable -> bool + handle: intptr_t + / + +Get the close-on-exe flag of the specified file descriptor. +[clinic start generated code]*/ + +static int +os_get_handle_inheritable_impl(PyObject *module, intptr_t handle) +/*[clinic end generated code: output=36be5afca6ea84d8 input=cfe99f9c05c70ad1]*/ +{ + DWORD flags; + + if (!GetHandleInformation((HANDLE)handle, &flags)) { + PyErr_SetFromWindowsErr(0); + return -1; + } + + return flags & HANDLE_FLAG_INHERIT; +} + + +/*[clinic input] +os.set_handle_inheritable + handle: intptr_t + inheritable: bool + / + +Set the inheritable flag of the specified handle. +[clinic start generated code]*/ + +static PyObject * +os_set_handle_inheritable_impl(PyObject *module, intptr_t handle, + int inheritable) +/*[clinic end generated code: output=021d74fe6c96baa3 input=7a7641390d8364fc]*/ +{ + DWORD flags = inheritable ? HANDLE_FLAG_INHERIT : 0; + if (!SetHandleInformation((HANDLE)handle, HANDLE_FLAG_INHERIT, flags)) { + PyErr_SetFromWindowsErr(0); + return NULL; + } + Py_RETURN_NONE; +} +#endif /* MS_WINDOWS */ + +/*[clinic input] +os.get_blocking -> bool + fd: int + / + +Get the blocking mode of the file descriptor. + +Return False if the O_NONBLOCK flag is set, True if the flag is cleared. +[clinic start generated code]*/ + +static int +os_get_blocking_impl(PyObject *module, int fd) +/*[clinic end generated code: output=336a12ad76a61482 input=f4afb59d51560179]*/ +{ + int blocking; + + _Py_BEGIN_SUPPRESS_IPH + blocking = _Py_get_blocking(fd); + _Py_END_SUPPRESS_IPH + return blocking; +} + +/*[clinic input] +os.set_blocking + fd: int + blocking: bool + / + +Set the blocking mode of the specified file descriptor. + +Set the O_NONBLOCK flag if blocking is False, +clear the O_NONBLOCK flag otherwise. +[clinic start generated code]*/ + +static PyObject * +os_set_blocking_impl(PyObject *module, int fd, int blocking) +/*[clinic end generated code: output=384eb43aa0762a9d input=7e9dfc9b14804dd4]*/ +{ + int result; + + _Py_BEGIN_SUPPRESS_IPH + result = _Py_set_blocking(fd, blocking); + _Py_END_SUPPRESS_IPH + if (result < 0) + return NULL; + Py_RETURN_NONE; +} + + +/*[clinic input] +class os.DirEntry "DirEntry *" "DirEntryType" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=3c18c7a448247980]*/ + +typedef struct { + PyObject_HEAD + PyObject *name; + PyObject *path; + PyObject *stat; + PyObject *lstat; +#ifdef MS_WINDOWS + struct _Py_stat_struct win32_lstat; + uint64_t win32_file_index; + uint64_t win32_file_index_high; + int got_file_index; +#else /* POSIX */ +#ifdef HAVE_DIRENT_D_TYPE + unsigned char d_type; +#endif + ino_t d_ino; + int dir_fd; +#endif +} DirEntry; + +static void +DirEntry_dealloc(DirEntry *entry) +{ + PyTypeObject *tp = Py_TYPE(entry); + Py_XDECREF(entry->name); + Py_XDECREF(entry->path); + Py_XDECREF(entry->stat); + Py_XDECREF(entry->lstat); + freefunc free_func = PyType_GetSlot(tp, Py_tp_free); + free_func(entry); + Py_DECREF(tp); +} + +/* Forward reference */ +static int +DirEntry_test_mode(PyTypeObject *defining_class, DirEntry *self, + int follow_symlinks, unsigned short mode_bits); + +/*[clinic input] +os.DirEntry.is_symlink -> bool + defining_class: defining_class + / + +Return True if the entry is a symbolic link; cached per entry. +[clinic start generated code]*/ + +static int +os_DirEntry_is_symlink_impl(DirEntry *self, PyTypeObject *defining_class) +/*[clinic end generated code: output=293096d589b6d47c input=e9acc5ee4d511113]*/ +{ +#ifdef MS_WINDOWS + return (self->win32_lstat.st_mode & S_IFMT) == S_IFLNK; +#elif defined(HAVE_DIRENT_D_TYPE) + /* POSIX */ + if (self->d_type != DT_UNKNOWN) + return self->d_type == DT_LNK; + else + return DirEntry_test_mode(defining_class, self, 0, S_IFLNK); +#else + /* POSIX without d_type */ + return DirEntry_test_mode(defining_class, self, 0, S_IFLNK); +#endif +} + +/*[clinic input] +os.DirEntry.is_junction -> bool + +Return True if the entry is a junction; cached per entry. +[clinic start generated code]*/ + +static int +os_DirEntry_is_junction_impl(DirEntry *self) +/*[clinic end generated code: output=97f64d5d99eeccb5 input=4fc8e701eea118a1]*/ +{ +#ifdef MS_WINDOWS + return self->win32_lstat.st_reparse_tag == IO_REPARSE_TAG_MOUNT_POINT; +#else + return 0; +#endif +} + +static PyObject * +DirEntry_fetch_stat(PyObject *module, DirEntry *self, int follow_symlinks) +{ + int result; + STRUCT_STAT st; + PyObject *ub; + +#ifdef MS_WINDOWS + if (!PyUnicode_FSDecoder(self->path, &ub)) + return NULL; + wchar_t *path = PyUnicode_AsWideCharString(ub, NULL); + Py_DECREF(ub); +#else /* POSIX */ + if (!PyUnicode_FSConverter(self->path, &ub)) + return NULL; + const char *path = PyBytes_AS_STRING(ub); + if (self->dir_fd != DEFAULT_DIR_FD) { +#ifdef HAVE_FSTATAT + if (HAVE_FSTATAT_RUNTIME) { + Py_BEGIN_ALLOW_THREADS + result = fstatat(self->dir_fd, path, &st, + follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW); + Py_END_ALLOW_THREADS + } else + +#endif /* HAVE_FSTATAT */ + { + Py_DECREF(ub); + PyErr_SetString(PyExc_NotImplementedError, "can't fetch stat"); + return NULL; + } + } + else +#endif + { + Py_BEGIN_ALLOW_THREADS + if (follow_symlinks) { + result = STAT(path, &st); + } + else { + result = LSTAT(path, &st); + } + Py_END_ALLOW_THREADS + } + + int saved_errno = errno; +#if defined(MS_WINDOWS) + PyMem_Free(path); +#else + Py_DECREF(ub); +#endif + + if (result != 0) { + errno = saved_errno; + path_object_error(self->path); + return NULL; + } + + return _pystat_fromstructstat(module, &st); +} + +static PyObject * +DirEntry_get_lstat(PyTypeObject *defining_class, DirEntry *self) +{ + if (!self->lstat) { + PyObject *module = PyType_GetModule(defining_class); +#ifdef MS_WINDOWS + self->lstat = _pystat_fromstructstat(module, &self->win32_lstat); +#else /* POSIX */ + self->lstat = DirEntry_fetch_stat(module, self, 0); +#endif + } + return Py_XNewRef(self->lstat); +} + +/*[clinic input] +os.DirEntry.stat + defining_class: defining_class + / + * + follow_symlinks: bool = True + +Return stat_result object for the entry; cached per entry. +[clinic start generated code]*/ + +static PyObject * +os_DirEntry_stat_impl(DirEntry *self, PyTypeObject *defining_class, + int follow_symlinks) +/*[clinic end generated code: output=23f803e19c3e780e input=e816273c4e67ee98]*/ +{ + if (!follow_symlinks) { + return DirEntry_get_lstat(defining_class, self); + } + + if (!self->stat) { + int result = os_DirEntry_is_symlink_impl(self, defining_class); + if (result == -1) { + return NULL; + } + if (result) { + PyObject *module = PyType_GetModule(defining_class); + self->stat = DirEntry_fetch_stat(module, self, 1); + } + else { + self->stat = DirEntry_get_lstat(defining_class, self); + } + } + + return Py_XNewRef(self->stat); +} + +/* Set exception and return -1 on error, 0 for False, 1 for True */ +static int +DirEntry_test_mode(PyTypeObject *defining_class, DirEntry *self, + int follow_symlinks, unsigned short mode_bits) +{ + PyObject *stat = NULL; + PyObject *st_mode = NULL; + long mode; + int result; +#if defined(MS_WINDOWS) || defined(HAVE_DIRENT_D_TYPE) + int is_symlink; + int need_stat; +#endif +#ifdef MS_WINDOWS + unsigned long dir_bits; +#endif + +#ifdef MS_WINDOWS + is_symlink = (self->win32_lstat.st_mode & S_IFMT) == S_IFLNK; + need_stat = follow_symlinks && is_symlink; +#elif defined(HAVE_DIRENT_D_TYPE) + is_symlink = self->d_type == DT_LNK; + need_stat = self->d_type == DT_UNKNOWN || (follow_symlinks && is_symlink); +#endif + +#if defined(MS_WINDOWS) || defined(HAVE_DIRENT_D_TYPE) + if (need_stat) { +#endif + stat = os_DirEntry_stat_impl(self, defining_class, follow_symlinks); + if (!stat) { + if (PyErr_ExceptionMatches(PyExc_FileNotFoundError)) { + /* If file doesn't exist (anymore), then return False + (i.e., say it's not a file/directory) */ + PyErr_Clear(); + return 0; + } + goto error; + } + _posixstate* state = get_posix_state(PyType_GetModule(defining_class)); + st_mode = PyObject_GetAttr(stat, state->st_mode); + if (!st_mode) + goto error; + + mode = PyLong_AsLong(st_mode); + if (mode == -1 && PyErr_Occurred()) + goto error; + Py_CLEAR(st_mode); + Py_CLEAR(stat); + result = (mode & S_IFMT) == mode_bits; +#if defined(MS_WINDOWS) || defined(HAVE_DIRENT_D_TYPE) + } + else if (is_symlink) { + assert(mode_bits != S_IFLNK); + result = 0; + } + else { + assert(mode_bits == S_IFDIR || mode_bits == S_IFREG); +#ifdef MS_WINDOWS + dir_bits = self->win32_lstat.st_file_attributes & FILE_ATTRIBUTE_DIRECTORY; + if (mode_bits == S_IFDIR) + result = dir_bits != 0; + else + result = dir_bits == 0; +#else /* POSIX */ + if (mode_bits == S_IFDIR) + result = self->d_type == DT_DIR; + else + result = self->d_type == DT_REG; +#endif + } +#endif + + return result; + +error: + Py_XDECREF(st_mode); + Py_XDECREF(stat); + return -1; +} + +/*[clinic input] +os.DirEntry.is_dir -> bool + defining_class: defining_class + / + * + follow_symlinks: bool = True + +Return True if the entry is a directory; cached per entry. +[clinic start generated code]*/ + +static int +os_DirEntry_is_dir_impl(DirEntry *self, PyTypeObject *defining_class, + int follow_symlinks) +/*[clinic end generated code: output=0cd453b9c0987fdf input=1a4ffd6dec9920cb]*/ +{ + return DirEntry_test_mode(defining_class, self, follow_symlinks, S_IFDIR); +} + +/*[clinic input] +os.DirEntry.is_file -> bool + defining_class: defining_class + / + * + follow_symlinks: bool = True + +Return True if the entry is a file; cached per entry. +[clinic start generated code]*/ + +static int +os_DirEntry_is_file_impl(DirEntry *self, PyTypeObject *defining_class, + int follow_symlinks) +/*[clinic end generated code: output=f7c277ab5ba80908 input=0a64c5a12e802e3b]*/ +{ + return DirEntry_test_mode(defining_class, self, follow_symlinks, S_IFREG); +} + +/*[clinic input] +os.DirEntry.inode + +Return inode of the entry; cached per entry. +[clinic start generated code]*/ + +static PyObject * +os_DirEntry_inode_impl(DirEntry *self) +/*[clinic end generated code: output=156bb3a72162440e input=3ee7b872ae8649f0]*/ +{ +#ifdef MS_WINDOWS + if (!self->got_file_index) { + PyObject *unicode; + STRUCT_STAT stat; + int result; + + if (!PyUnicode_FSDecoder(self->path, &unicode)) + return NULL; + wchar_t *path = PyUnicode_AsWideCharString(unicode, NULL); + Py_DECREF(unicode); + result = LSTAT(path, &stat); + + int saved_errno = errno; + PyMem_Free(path); + + if (result != 0) { + errno = saved_errno; + return path_object_error(self->path); + } + + self->win32_file_index = stat.st_ino; + self->win32_file_index_high = stat.st_ino_high; + self->got_file_index = 1; + } + return _pystat_l128_from_l64_l64(self->win32_file_index, self->win32_file_index_high); +#else /* POSIX */ + static_assert(sizeof(unsigned long long) >= sizeof(self->d_ino), + "DirEntry.d_ino is larger than unsigned long long"); + return PyLong_FromUnsignedLongLong(self->d_ino); +#endif +} + +static PyObject * +DirEntry_repr(DirEntry *self) +{ + return PyUnicode_FromFormat("", self->name); +} + +/*[clinic input] +os.DirEntry.__fspath__ + +Returns the path for the entry. +[clinic start generated code]*/ + +static PyObject * +os_DirEntry___fspath___impl(DirEntry *self) +/*[clinic end generated code: output=6dd7f7ef752e6f4f input=3c49d0cf38df4fac]*/ +{ + return Py_NewRef(self->path); +} + +static PyMemberDef DirEntry_members[] = { + {"name", Py_T_OBJECT_EX, offsetof(DirEntry, name), Py_READONLY, + "the entry's base filename, relative to scandir() \"path\" argument"}, + {"path", Py_T_OBJECT_EX, offsetof(DirEntry, path), Py_READONLY, + "the entry's full path name; equivalent to os.path.join(scandir_path, entry.name)"}, + {NULL} +}; + +#include "clinic/posixmodule.c.h" + +static PyMethodDef DirEntry_methods[] = { + OS_DIRENTRY_IS_DIR_METHODDEF + OS_DIRENTRY_IS_FILE_METHODDEF + OS_DIRENTRY_IS_SYMLINK_METHODDEF + OS_DIRENTRY_IS_JUNCTION_METHODDEF + OS_DIRENTRY_STAT_METHODDEF + OS_DIRENTRY_INODE_METHODDEF + OS_DIRENTRY___FSPATH___METHODDEF + {"__class_getitem__", Py_GenericAlias, + METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, + {NULL} +}; + +static PyType_Slot DirEntryType_slots[] = { + {Py_tp_dealloc, DirEntry_dealloc}, + {Py_tp_repr, DirEntry_repr}, + {Py_tp_methods, DirEntry_methods}, + {Py_tp_members, DirEntry_members}, + {0, 0}, +}; + +static PyType_Spec DirEntryType_spec = { + MODNAME ".DirEntry", + sizeof(DirEntry), + 0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, + DirEntryType_slots +}; + + +#ifdef MS_WINDOWS + +static wchar_t * +join_path_filenameW(const wchar_t *path_wide, const wchar_t *filename) +{ + Py_ssize_t path_len; + Py_ssize_t size; + wchar_t *result; + wchar_t ch; + + if (!path_wide) { /* Default arg: "." */ + path_wide = L"."; + path_len = 1; + } + else { + path_len = wcslen(path_wide); + } + + /* The +1's are for the path separator and the NUL */ + size = path_len + 1 + wcslen(filename) + 1; + result = PyMem_New(wchar_t, size); + if (!result) { + PyErr_NoMemory(); + return NULL; + } + wcscpy(result, path_wide); + if (path_len > 0) { + ch = result[path_len - 1]; + if (ch != SEP && ch != ALTSEP && ch != L':') + result[path_len++] = SEP; + wcscpy(result + path_len, filename); + } + return result; +} + +static PyObject * +DirEntry_from_find_data(PyObject *module, path_t *path, WIN32_FIND_DATAW *dataW) +{ + DirEntry *entry; + BY_HANDLE_FILE_INFORMATION file_info; + ULONG reparse_tag; + wchar_t *joined_path; + + PyObject *DirEntryType = get_posix_state(module)->DirEntryType; + entry = PyObject_New(DirEntry, (PyTypeObject *)DirEntryType); + if (!entry) + return NULL; + entry->name = NULL; + entry->path = NULL; + entry->stat = NULL; + entry->lstat = NULL; + entry->got_file_index = 0; + + entry->name = PyUnicode_FromWideChar(dataW->cFileName, -1); + if (!entry->name) + goto error; + int return_bytes = path->wide && PyBytes_Check(path->object); + if (return_bytes) { + Py_SETREF(entry->name, PyUnicode_EncodeFSDefault(entry->name)); + if (!entry->name) + goto error; + } + + joined_path = join_path_filenameW(path->wide, dataW->cFileName); + if (!joined_path) + goto error; + + entry->path = PyUnicode_FromWideChar(joined_path, -1); + PyMem_Free(joined_path); + if (!entry->path) + goto error; + if (return_bytes) { + Py_SETREF(entry->path, PyUnicode_EncodeFSDefault(entry->path)); + if (!entry->path) + goto error; + } + + find_data_to_file_info(dataW, &file_info, &reparse_tag); + _Py_attribute_data_to_stat(&file_info, reparse_tag, NULL, NULL, &entry->win32_lstat); + + /* ctime is only deprecated from 3.12, so we copy birthtime across */ + entry->win32_lstat.st_ctime = entry->win32_lstat.st_birthtime; + entry->win32_lstat.st_ctime_nsec = entry->win32_lstat.st_birthtime_nsec; + + return (PyObject *)entry; + +error: + Py_DECREF(entry); + return NULL; +} + +#else /* POSIX */ + +static char * +join_path_filename(const char *path_narrow, const char* filename, Py_ssize_t filename_len) +{ + Py_ssize_t path_len; + Py_ssize_t size; + char *result; + + if (!path_narrow) { /* Default arg: "." */ + path_narrow = "."; + path_len = 1; + } + else { + path_len = strlen(path_narrow); + } + + if (filename_len == -1) + filename_len = strlen(filename); + + /* The +1's are for the path separator and the NUL */ + size = path_len + 1 + filename_len + 1; + result = PyMem_New(char, size); + if (!result) { + PyErr_NoMemory(); + return NULL; + } + strcpy(result, path_narrow); + if (path_len > 0 && result[path_len - 1] != '/') + result[path_len++] = '/'; + strcpy(result + path_len, filename); + return result; +} + +static PyObject * +DirEntry_from_posix_info(PyObject *module, path_t *path, const char *name, + Py_ssize_t name_len, ino_t d_ino +#ifdef HAVE_DIRENT_D_TYPE + , unsigned char d_type +#endif + ) +{ + DirEntry *entry; + char *joined_path; + + PyObject *DirEntryType = get_posix_state(module)->DirEntryType; + entry = PyObject_New(DirEntry, (PyTypeObject *)DirEntryType); + if (!entry) + return NULL; + entry->name = NULL; + entry->path = NULL; + entry->stat = NULL; + entry->lstat = NULL; + + if (path->fd != -1) { + entry->dir_fd = path->fd; + joined_path = NULL; + } + else { + entry->dir_fd = DEFAULT_DIR_FD; + joined_path = join_path_filename(path->narrow, name, name_len); + if (!joined_path) + goto error; + } + + if (!path->narrow || !PyBytes_Check(path->object)) { + entry->name = PyUnicode_DecodeFSDefaultAndSize(name, name_len); + if (joined_path) + entry->path = PyUnicode_DecodeFSDefault(joined_path); + } + else { + entry->name = PyBytes_FromStringAndSize(name, name_len); + if (joined_path) + entry->path = PyBytes_FromString(joined_path); + } + PyMem_Free(joined_path); + if (!entry->name) + goto error; + + if (path->fd != -1) { + entry->path = Py_NewRef(entry->name); + } + else if (!entry->path) + goto error; + +#ifdef HAVE_DIRENT_D_TYPE + entry->d_type = d_type; +#endif + entry->d_ino = d_ino; + + return (PyObject *)entry; + +error: + Py_XDECREF(entry); + return NULL; +} + +#endif + + +typedef struct { + PyObject_HEAD + path_t path; +#ifdef MS_WINDOWS + HANDLE handle; + WIN32_FIND_DATAW file_data; + int first_time; +#else /* POSIX */ + DIR *dirp; +#endif +#ifdef HAVE_FDOPENDIR + int fd; +#endif +} ScandirIterator; + +#ifdef MS_WINDOWS + +static int +ScandirIterator_is_closed(ScandirIterator *iterator) +{ + return iterator->handle == INVALID_HANDLE_VALUE; +} + +static void +ScandirIterator_closedir(ScandirIterator *iterator) +{ + HANDLE handle = iterator->handle; + + if (handle == INVALID_HANDLE_VALUE) + return; + + iterator->handle = INVALID_HANDLE_VALUE; + Py_BEGIN_ALLOW_THREADS + FindClose(handle); + Py_END_ALLOW_THREADS +} + +static PyObject * +ScandirIterator_iternext(ScandirIterator *iterator) +{ + WIN32_FIND_DATAW *file_data = &iterator->file_data; + BOOL success; + PyObject *entry; + + /* Happens if the iterator is iterated twice, or closed explicitly */ + if (iterator->handle == INVALID_HANDLE_VALUE) + return NULL; + + while (1) { + if (!iterator->first_time) { + Py_BEGIN_ALLOW_THREADS + success = FindNextFileW(iterator->handle, file_data); + Py_END_ALLOW_THREADS + if (!success) { + /* Error or no more files */ + if (GetLastError() != ERROR_NO_MORE_FILES) + path_error(&iterator->path); + break; + } + } + iterator->first_time = 0; + + /* Skip over . and .. */ + if (wcscmp(file_data->cFileName, L".") != 0 && + wcscmp(file_data->cFileName, L"..") != 0) + { + PyObject *module = PyType_GetModule(Py_TYPE(iterator)); + entry = DirEntry_from_find_data(module, &iterator->path, file_data); + if (!entry) + break; + return entry; + } + + /* Loop till we get a non-dot directory or finish iterating */ + } + + /* Error or no more files */ + ScandirIterator_closedir(iterator); + return NULL; +} + +#else /* POSIX */ + +static int +ScandirIterator_is_closed(ScandirIterator *iterator) +{ + return !iterator->dirp; +} + +static void +ScandirIterator_closedir(ScandirIterator *iterator) +{ + DIR *dirp = iterator->dirp; + + if (!dirp) + return; + + iterator->dirp = NULL; + Py_BEGIN_ALLOW_THREADS +#ifdef HAVE_FDOPENDIR + if (iterator->path.fd != -1) + rewinddir(dirp); +#endif + closedir(dirp); + Py_END_ALLOW_THREADS + return; +} + +static PyObject * +ScandirIterator_iternext(ScandirIterator *iterator) +{ + struct dirent *direntp; + Py_ssize_t name_len; + int is_dot; + PyObject *entry; + + /* Happens if the iterator is iterated twice, or closed explicitly */ + if (!iterator->dirp) + return NULL; + + while (1) { + errno = 0; + Py_BEGIN_ALLOW_THREADS + direntp = readdir(iterator->dirp); + Py_END_ALLOW_THREADS + + if (!direntp) { + /* Error or no more files */ + if (errno != 0) + path_error(&iterator->path); + break; + } + + /* Skip over . and .. */ + name_len = NAMLEN(direntp); + is_dot = direntp->d_name[0] == '.' && + (name_len == 1 || (direntp->d_name[1] == '.' && name_len == 2)); + if (!is_dot) { + PyObject *module = PyType_GetModule(Py_TYPE(iterator)); + entry = DirEntry_from_posix_info(module, + &iterator->path, direntp->d_name, + name_len, direntp->d_ino +#ifdef HAVE_DIRENT_D_TYPE + , direntp->d_type +#endif + ); + if (!entry) + break; + return entry; + } + + /* Loop till we get a non-dot directory or finish iterating */ + } + + /* Error or no more files */ + ScandirIterator_closedir(iterator); + return NULL; +} + +#endif + +static PyObject * +ScandirIterator_close(ScandirIterator *self, PyObject *args) +{ + ScandirIterator_closedir(self); + Py_RETURN_NONE; +} + +static PyObject * +ScandirIterator_enter(PyObject *self, PyObject *args) +{ + return Py_NewRef(self); +} + +static PyObject * +ScandirIterator_exit(ScandirIterator *self, PyObject *args) +{ + ScandirIterator_closedir(self); + Py_RETURN_NONE; +} + +static void +ScandirIterator_finalize(ScandirIterator *iterator) +{ + + /* Save the current exception, if any. */ + PyObject *exc = PyErr_GetRaisedException(); + + if (!ScandirIterator_is_closed(iterator)) { + ScandirIterator_closedir(iterator); + + if (PyErr_ResourceWarning((PyObject *)iterator, 1, + "unclosed scandir iterator %R", iterator)) { + /* Spurious errors can appear at shutdown */ + if (PyErr_ExceptionMatches(PyExc_Warning)) { + PyErr_WriteUnraisable((PyObject *) iterator); + } + } + } + + path_cleanup(&iterator->path); + + /* Restore the saved exception. */ + PyErr_SetRaisedException(exc); +} + +static void +ScandirIterator_dealloc(ScandirIterator *iterator) +{ + PyTypeObject *tp = Py_TYPE(iterator); + if (PyObject_CallFinalizerFromDealloc((PyObject *)iterator) < 0) + return; + + freefunc free_func = PyType_GetSlot(tp, Py_tp_free); + free_func(iterator); + Py_DECREF(tp); +} + +static PyMethodDef ScandirIterator_methods[] = { + {"__enter__", (PyCFunction)ScandirIterator_enter, METH_NOARGS}, + {"__exit__", (PyCFunction)ScandirIterator_exit, METH_VARARGS}, + {"close", (PyCFunction)ScandirIterator_close, METH_NOARGS}, + {NULL} +}; + +static PyType_Slot ScandirIteratorType_slots[] = { + {Py_tp_dealloc, ScandirIterator_dealloc}, + {Py_tp_finalize, ScandirIterator_finalize}, + {Py_tp_iter, PyObject_SelfIter}, + {Py_tp_iternext, ScandirIterator_iternext}, + {Py_tp_methods, ScandirIterator_methods}, + {0, 0}, +}; + +static PyType_Spec ScandirIteratorType_spec = { + MODNAME ".ScandirIterator", + sizeof(ScandirIterator), + 0, + // bpo-40549: Py_TPFLAGS_BASETYPE should not be used, since + // PyType_GetModule(Py_TYPE(self)) doesn't work on a subclass instance. + (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_FINALIZE + | Py_TPFLAGS_DISALLOW_INSTANTIATION), + ScandirIteratorType_slots +}; + +/*[clinic input] +os.scandir + + path : path_t(nullable=True, allow_fd='PATH_HAVE_FDOPENDIR') = None + +Return an iterator of DirEntry objects for given path. + +path can be specified as either str, bytes, or a path-like object. If path +is bytes, the names of yielded DirEntry objects will also be bytes; in +all other circumstances they will be str. + +If path is None, uses the path='.'. +[clinic start generated code]*/ + +static PyObject * +os_scandir_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=6eb2668b675ca89e input=6bdd312708fc3bb0]*/ +{ + ScandirIterator *iterator; +#ifdef MS_WINDOWS + wchar_t *path_strW; +#else + const char *path_str; +#ifdef HAVE_FDOPENDIR + int fd = -1; +#endif +#endif + + if (PySys_Audit("os.scandir", "O", + path->object ? path->object : Py_None) < 0) { + return NULL; + } + + PyObject *ScandirIteratorType = get_posix_state(module)->ScandirIteratorType; + iterator = PyObject_New(ScandirIterator, (PyTypeObject *)ScandirIteratorType); + if (!iterator) + return NULL; + +#ifdef MS_WINDOWS + iterator->handle = INVALID_HANDLE_VALUE; +#else + iterator->dirp = NULL; +#endif + + /* Move the ownership to iterator->path */ + memcpy(&iterator->path, path, sizeof(path_t)); + memset(path, 0, sizeof(path_t)); + +#ifdef MS_WINDOWS + iterator->first_time = 1; + + path_strW = join_path_filenameW(iterator->path.wide, L"*.*"); + if (!path_strW) + goto error; + + Py_BEGIN_ALLOW_THREADS + iterator->handle = FindFirstFileW(path_strW, &iterator->file_data); + Py_END_ALLOW_THREADS + + if (iterator->handle == INVALID_HANDLE_VALUE) { + path_error(&iterator->path); + PyMem_Free(path_strW); + goto error; + } + PyMem_Free(path_strW); +#else /* POSIX */ + errno = 0; +#ifdef HAVE_FDOPENDIR + if (iterator->path.fd != -1) { + if (HAVE_FDOPENDIR_RUNTIME) { + /* closedir() closes the FD, so we duplicate it */ + fd = _Py_dup(iterator->path.fd); + if (fd == -1) + goto error; + + Py_BEGIN_ALLOW_THREADS + iterator->dirp = fdopendir(fd); + Py_END_ALLOW_THREADS + } else { + PyErr_SetString(PyExc_TypeError, + "scandir: path should be string, bytes, os.PathLike or None, not int"); + return NULL; + } + } + else +#endif + { + if (iterator->path.narrow) + path_str = iterator->path.narrow; + else + path_str = "."; + + Py_BEGIN_ALLOW_THREADS + iterator->dirp = opendir(path_str); + Py_END_ALLOW_THREADS + } + + if (!iterator->dirp) { + path_error(&iterator->path); +#ifdef HAVE_FDOPENDIR + if (fd != -1) { + Py_BEGIN_ALLOW_THREADS + close(fd); + Py_END_ALLOW_THREADS + } +#endif + goto error; + } +#endif + + return (PyObject *)iterator; + +error: + Py_DECREF(iterator); + return NULL; +} + +/* + Return the file system path representation of the object. + + If the object is str or bytes, then allow it to pass through with + an incremented refcount. If the object defines __fspath__(), then + return the result of that method. All other types raise a TypeError. +*/ +PyObject * +PyOS_FSPath(PyObject *path) +{ + /* For error message reasons, this function is manually inlined in + path_converter(). */ + PyObject *func = NULL; + PyObject *path_repr = NULL; + + if (PyUnicode_Check(path) || PyBytes_Check(path)) { + return Py_NewRef(path); + } + + func = _PyObject_LookupSpecial(path, &_Py_ID(__fspath__)); + if ((NULL == func) || (func == Py_None)) { + return PyErr_Format(PyExc_TypeError, + "expected str, bytes or os.PathLike object, " + "not %.200s", + _PyType_Name(Py_TYPE(path))); + } + + path_repr = _PyObject_CallNoArgs(func); + Py_DECREF(func); + if (NULL == path_repr) { + return NULL; + } + + if (!(PyUnicode_Check(path_repr) || PyBytes_Check(path_repr))) { + PyErr_Format(PyExc_TypeError, + "expected %.200s.__fspath__() to return str or bytes, " + "not %.200s", _PyType_Name(Py_TYPE(path)), + _PyType_Name(Py_TYPE(path_repr))); + Py_DECREF(path_repr); + return NULL; + } + + return path_repr; +} + +/*[clinic input] +os.fspath + + path: object + +Return the file system path representation of the object. + +If the object is str or bytes, then allow it to pass through as-is. If the +object defines __fspath__(), then return the result of that method. All other +types raise a TypeError. +[clinic start generated code]*/ + +static PyObject * +os_fspath_impl(PyObject *module, PyObject *path) +/*[clinic end generated code: output=c3c3b78ecff2914f input=e357165f7b22490f]*/ +{ + return PyOS_FSPath(path); +} + +#ifdef HAVE_GETRANDOM_SYSCALL +/*[clinic input] +os.getrandom + + size: Py_ssize_t + flags: int=0 + +Obtain a series of random bytes. +[clinic start generated code]*/ + +static PyObject * +os_getrandom_impl(PyObject *module, Py_ssize_t size, int flags) +/*[clinic end generated code: output=b3a618196a61409c input=59bafac39c594947]*/ +{ + PyObject *bytes; + Py_ssize_t n; + + if (size < 0) { + errno = EINVAL; + return posix_error(); + } + + bytes = PyBytes_FromStringAndSize(NULL, size); + if (bytes == NULL) { + PyErr_NoMemory(); + return NULL; + } + + while (1) { + n = syscall(SYS_getrandom, + PyBytes_AS_STRING(bytes), + PyBytes_GET_SIZE(bytes), + flags); + if (n < 0 && errno == EINTR) { + if (PyErr_CheckSignals() < 0) { + goto error; + } + + /* getrandom() was interrupted by a signal: retry */ + continue; + } + break; + } + + if (n < 0) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + + if (n != size) { + _PyBytes_Resize(&bytes, n); + } + + return bytes; + +error: + Py_DECREF(bytes); + return NULL; +} +#endif /* HAVE_GETRANDOM_SYSCALL */ + +#if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_APP) || defined(MS_WINDOWS_SYSTEM) + +/* bpo-36085: Helper functions for managing DLL search directories + * on win32 + */ + +/*[clinic input] +os._add_dll_directory + + path: path_t + +Add a path to the DLL search path. + +This search path is used when resolving dependencies for imported +extension modules (the module itself is resolved through sys.path), +and also by ctypes. + +Returns an opaque value that may be passed to os.remove_dll_directory +to remove this directory from the search path. +[clinic start generated code]*/ + +static PyObject * +os__add_dll_directory_impl(PyObject *module, path_t *path) +/*[clinic end generated code: output=80b025daebb5d683 input=1de3e6c13a5808c8]*/ +{ + DLL_DIRECTORY_COOKIE cookie = 0; + DWORD err = 0; + + if (PySys_Audit("os.add_dll_directory", "(O)", path->object) < 0) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + if (!(cookie = AddDllDirectory(path->wide))) { + err = GetLastError(); + } + Py_END_ALLOW_THREADS + + if (err) { + return win32_error_object_err("add_dll_directory", + path->object, err); + } + + return PyCapsule_New(cookie, "DLL directory cookie", NULL); +} + +/*[clinic input] +os._remove_dll_directory + + cookie: object + +Removes a path from the DLL search path. + +The parameter is an opaque value that was returned from +os.add_dll_directory. You can only remove directories that you added +yourself. +[clinic start generated code]*/ + +static PyObject * +os__remove_dll_directory_impl(PyObject *module, PyObject *cookie) +/*[clinic end generated code: output=594350433ae535bc input=c1d16a7e7d9dc5dc]*/ +{ + DLL_DIRECTORY_COOKIE cookieValue; + DWORD err = 0; + + if (!PyCapsule_IsValid(cookie, "DLL directory cookie")) { + PyErr_SetString(PyExc_TypeError, + "Provided cookie was not returned from os.add_dll_directory"); + return NULL; + } + + cookieValue = (DLL_DIRECTORY_COOKIE)PyCapsule_GetPointer( + cookie, "DLL directory cookie"); + + Py_BEGIN_ALLOW_THREADS + if (!RemoveDllDirectory(cookieValue)) { + err = GetLastError(); + } + Py_END_ALLOW_THREADS + + if (err) { + return win32_error_object_err("remove_dll_directory", + NULL, err); + } + + if (PyCapsule_SetName(cookie, NULL)) { + return NULL; + } + + Py_RETURN_NONE; +} + +#endif /* MS_WINDOWS_APP || MS_WINDOWS_SYSTEM */ + + +/* Only check if WIFEXITED is available: expect that it comes + with WEXITSTATUS, WIFSIGNALED, etc. + + os.waitstatus_to_exitcode() is implemented in C and not in Python, so + subprocess can safely call it during late Python finalization without + risking that used os attributes were set to None by finalize_modules(). */ +#if defined(WIFEXITED) || defined(MS_WINDOWS) +/*[clinic input] +os.waitstatus_to_exitcode + + status as status_obj: object + +Convert a wait status to an exit code. + +On Unix: + +* If WIFEXITED(status) is true, return WEXITSTATUS(status). +* If WIFSIGNALED(status) is true, return -WTERMSIG(status). +* Otherwise, raise a ValueError. + +On Windows, return status shifted right by 8 bits. + +On Unix, if the process is being traced or if waitpid() was called with +WUNTRACED option, the caller must first check if WIFSTOPPED(status) is true. +This function must not be called if WIFSTOPPED(status) is true. +[clinic start generated code]*/ + +static PyObject * +os_waitstatus_to_exitcode_impl(PyObject *module, PyObject *status_obj) +/*[clinic end generated code: output=db50b1b0ba3c7153 input=7fe2d7fdaea3db42]*/ +{ +#ifndef MS_WINDOWS + int status = PyLong_AsInt(status_obj); + if (status == -1 && PyErr_Occurred()) { + return NULL; + } + + WAIT_TYPE wait_status; + WAIT_STATUS_INT(wait_status) = status; + int exitcode; + if (WIFEXITED(wait_status)) { + exitcode = WEXITSTATUS(wait_status); + /* Sanity check to provide warranty on the function behavior. + It should not occur in practice */ + if (exitcode < 0) { + PyErr_Format(PyExc_ValueError, "invalid WEXITSTATUS: %i", exitcode); + return NULL; + } + } + else if (WIFSIGNALED(wait_status)) { + int signum = WTERMSIG(wait_status); + /* Sanity check to provide warranty on the function behavior. + It should not occurs in practice */ + if (signum <= 0) { + PyErr_Format(PyExc_ValueError, "invalid WTERMSIG: %i", signum); + return NULL; + } + exitcode = -signum; + } else if (WIFSTOPPED(wait_status)) { + /* Status only received if the process is being traced + or if waitpid() was called with WUNTRACED option. */ + int signum = WSTOPSIG(wait_status); + PyErr_Format(PyExc_ValueError, + "process stopped by delivery of signal %i", + signum); + return NULL; + } + else { + PyErr_Format(PyExc_ValueError, "invalid wait status: %i", status); + return NULL; + } + return PyLong_FromLong(exitcode); +#else + /* Windows implementation: see os.waitpid() implementation + which uses _cwait(). */ + unsigned long long status = PyLong_AsUnsignedLongLong(status_obj); + if (status == (unsigned long long)-1 && PyErr_Occurred()) { + return NULL; + } + + unsigned long long exitcode = (status >> 8); + /* ExitProcess() accepts an UINT type: + reject exit code which doesn't fit in an UINT */ + if (exitcode > UINT_MAX) { + PyErr_Format(PyExc_ValueError, "invalid exit code: %llu", exitcode); + return NULL; + } + return PyLong_FromUnsignedLong((unsigned long)exitcode); +#endif +} +#endif + +#if defined(MS_WINDOWS) +/*[clinic input] +os._supports_virtual_terminal + +Checks if virtual terminal is supported in windows +[clinic start generated code]*/ + +static PyObject * +os__supports_virtual_terminal_impl(PyObject *module) +/*[clinic end generated code: output=bd0556a6d9d99fe6 input=0752c98e5d321542]*/ +{ + DWORD mode = 0; + HANDLE handle = GetStdHandle(STD_ERROR_HANDLE); + if (!GetConsoleMode(handle, &mode)) { + Py_RETURN_FALSE; + } + return PyBool_FromLong(mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING); +} +#endif + +/*[clinic input] +os._inputhook + +Calls PyOS_CallInputHook droppong the GIL first +[clinic start generated code]*/ + +static PyObject * +os__inputhook_impl(PyObject *module) +/*[clinic end generated code: output=525aca4ef3c6149f input=fc531701930d064f]*/ +{ + int result = 0; + if (PyOS_InputHook) { + Py_BEGIN_ALLOW_THREADS; + result = PyOS_InputHook(); + Py_END_ALLOW_THREADS; + } + return PyLong_FromLong(result); +} + +/*[clinic input] +os._is_inputhook_installed + +Checks if PyOS_CallInputHook is set +[clinic start generated code]*/ + +static PyObject * +os__is_inputhook_installed_impl(PyObject *module) +/*[clinic end generated code: output=3b3eab4f672c689a input=ff177c9938dd76d8]*/ +{ + return PyBool_FromLong(PyOS_InputHook != NULL); +} + +static PyMethodDef posix_methods[] = { + + OS_STAT_METHODDEF + OS_ACCESS_METHODDEF + OS_TTYNAME_METHODDEF + OS_CHDIR_METHODDEF + OS_CHFLAGS_METHODDEF + OS_CHMOD_METHODDEF + OS_FCHMOD_METHODDEF + OS_LCHMOD_METHODDEF + OS_CHOWN_METHODDEF + OS_FCHOWN_METHODDEF + OS_LCHOWN_METHODDEF + OS_LCHFLAGS_METHODDEF + OS_CHROOT_METHODDEF + OS_CTERMID_METHODDEF + OS_GETCWD_METHODDEF + OS_GETCWDB_METHODDEF + OS_LINK_METHODDEF + OS_LISTDIR_METHODDEF + OS_LISTDRIVES_METHODDEF + OS_LISTMOUNTS_METHODDEF + OS_LISTVOLUMES_METHODDEF + OS_LSTAT_METHODDEF + OS_MKDIR_METHODDEF + OS_NICE_METHODDEF + OS_GETPRIORITY_METHODDEF + OS_SETPRIORITY_METHODDEF + OS_POSIX_SPAWN_METHODDEF + OS_POSIX_SPAWNP_METHODDEF + OS_READLINK_METHODDEF + OS_COPY_FILE_RANGE_METHODDEF + OS_SPLICE_METHODDEF + OS_RENAME_METHODDEF + OS_REPLACE_METHODDEF + OS_RMDIR_METHODDEF + OS_SYMLINK_METHODDEF + OS_SYSTEM_METHODDEF + OS_UMASK_METHODDEF + OS_UNAME_METHODDEF + OS_UNLINK_METHODDEF + OS_REMOVE_METHODDEF + OS_UTIME_METHODDEF + OS_TIMES_METHODDEF + OS__EXIT_METHODDEF + OS__FCOPYFILE_METHODDEF + OS_EXECV_METHODDEF + OS_EXECVE_METHODDEF + OS_SPAWNV_METHODDEF + OS_SPAWNVE_METHODDEF + OS_FORK1_METHODDEF + OS_FORK_METHODDEF + OS_REGISTER_AT_FORK_METHODDEF + OS_SCHED_GET_PRIORITY_MAX_METHODDEF + OS_SCHED_GET_PRIORITY_MIN_METHODDEF + OS_SCHED_GETPARAM_METHODDEF + OS_SCHED_GETSCHEDULER_METHODDEF + OS_SCHED_RR_GET_INTERVAL_METHODDEF + OS_SCHED_SETPARAM_METHODDEF + OS_SCHED_SETSCHEDULER_METHODDEF + OS_SCHED_YIELD_METHODDEF + OS_SCHED_SETAFFINITY_METHODDEF + OS_SCHED_GETAFFINITY_METHODDEF + OS_POSIX_OPENPT_METHODDEF + OS_GRANTPT_METHODDEF + OS_UNLOCKPT_METHODDEF + OS_PTSNAME_METHODDEF + OS_OPENPTY_METHODDEF + OS_LOGIN_TTY_METHODDEF + OS_FORKPTY_METHODDEF + OS_GETEGID_METHODDEF + OS_GETEUID_METHODDEF + OS_GETGID_METHODDEF + OS_GETGROUPLIST_METHODDEF + OS_GETGROUPS_METHODDEF + OS_GETPID_METHODDEF + OS_GETPGRP_METHODDEF + OS_GETPPID_METHODDEF + OS_GETUID_METHODDEF + OS_GETLOGIN_METHODDEF + OS_KILL_METHODDEF + OS_KILLPG_METHODDEF + OS_PLOCK_METHODDEF + OS_STARTFILE_METHODDEF + OS_SETUID_METHODDEF + OS_SETEUID_METHODDEF + OS_SETREUID_METHODDEF + OS_SETGID_METHODDEF + OS_SETEGID_METHODDEF + OS_SETREGID_METHODDEF + OS_SETGROUPS_METHODDEF + OS_INITGROUPS_METHODDEF + OS_GETPGID_METHODDEF + OS_SETPGRP_METHODDEF + OS_WAIT_METHODDEF + OS_WAIT3_METHODDEF + OS_WAIT4_METHODDEF + OS_WAITID_METHODDEF + OS_WAITPID_METHODDEF + OS_PIDFD_OPEN_METHODDEF + OS_GETSID_METHODDEF + OS_SETSID_METHODDEF + OS_SETPGID_METHODDEF + OS_TCGETPGRP_METHODDEF + OS_TCSETPGRP_METHODDEF + OS_OPEN_METHODDEF + OS_CLOSE_METHODDEF + OS_CLOSERANGE_METHODDEF + OS_DEVICE_ENCODING_METHODDEF + OS_DUP_METHODDEF + OS_DUP2_METHODDEF + OS_LOCKF_METHODDEF + OS_LSEEK_METHODDEF + OS_READ_METHODDEF + OS_READV_METHODDEF + OS_PREAD_METHODDEF + OS_PREADV_METHODDEF + OS_WRITE_METHODDEF + OS_WRITEV_METHODDEF + OS_PWRITE_METHODDEF + OS_PWRITEV_METHODDEF + OS_SENDFILE_METHODDEF + OS_FSTAT_METHODDEF + OS_ISATTY_METHODDEF + OS_PIPE_METHODDEF + OS_PIPE2_METHODDEF + OS_MKFIFO_METHODDEF + OS_MKNOD_METHODDEF + OS_MAJOR_METHODDEF + OS_MINOR_METHODDEF + OS_MAKEDEV_METHODDEF + OS_FTRUNCATE_METHODDEF + OS_TRUNCATE_METHODDEF + OS_POSIX_FALLOCATE_METHODDEF + OS_POSIX_FADVISE_METHODDEF + OS_PUTENV_METHODDEF + OS_UNSETENV_METHODDEF + OS_STRERROR_METHODDEF + OS_FCHDIR_METHODDEF + OS_FSYNC_METHODDEF + OS_SYNC_METHODDEF + OS_FDATASYNC_METHODDEF + OS_WCOREDUMP_METHODDEF + OS_WIFCONTINUED_METHODDEF + OS_WIFSTOPPED_METHODDEF + OS_WIFSIGNALED_METHODDEF + OS_WIFEXITED_METHODDEF + OS_WEXITSTATUS_METHODDEF + OS_WTERMSIG_METHODDEF + OS_WSTOPSIG_METHODDEF + OS_FSTATVFS_METHODDEF + OS_STATVFS_METHODDEF + OS_CONFSTR_METHODDEF + OS_SYSCONF_METHODDEF + OS_FPATHCONF_METHODDEF + OS_PATHCONF_METHODDEF + OS_ABORT_METHODDEF + OS__GETFULLPATHNAME_METHODDEF + OS__GETDISKUSAGE_METHODDEF + OS__GETFINALPATHNAME_METHODDEF + OS__FINDFIRSTFILE_METHODDEF + OS__GETVOLUMEPATHNAME_METHODDEF + OS__PATH_SPLITROOT_METHODDEF + OS__PATH_SPLITROOT_EX_METHODDEF + OS__PATH_NORMPATH_METHODDEF + OS_GETLOADAVG_METHODDEF + OS_URANDOM_METHODDEF + OS_SETRESUID_METHODDEF + OS_SETRESGID_METHODDEF + OS_GETRESUID_METHODDEF + OS_GETRESGID_METHODDEF + + OS_GETXATTR_METHODDEF + OS_SETXATTR_METHODDEF + OS_REMOVEXATTR_METHODDEF + OS_LISTXATTR_METHODDEF + + OS_GET_TERMINAL_SIZE_METHODDEF + OS_CPU_COUNT_METHODDEF + OS_GET_INHERITABLE_METHODDEF + OS_SET_INHERITABLE_METHODDEF + OS_GET_HANDLE_INHERITABLE_METHODDEF + OS_SET_HANDLE_INHERITABLE_METHODDEF + OS_GET_BLOCKING_METHODDEF + OS_SET_BLOCKING_METHODDEF + OS_SCANDIR_METHODDEF + OS_FSPATH_METHODDEF + OS_GETRANDOM_METHODDEF + OS_MEMFD_CREATE_METHODDEF + OS_EVENTFD_METHODDEF + OS_EVENTFD_READ_METHODDEF + OS_EVENTFD_WRITE_METHODDEF + OS__ADD_DLL_DIRECTORY_METHODDEF + OS__REMOVE_DLL_DIRECTORY_METHODDEF + OS_WAITSTATUS_TO_EXITCODE_METHODDEF + OS_SETNS_METHODDEF + OS_UNSHARE_METHODDEF + OS_TIMERFD_CREATE_METHODDEF + OS_TIMERFD_SETTIME_METHODDEF + OS_TIMERFD_SETTIME_NS_METHODDEF + OS_TIMERFD_GETTIME_METHODDEF + OS_TIMERFD_GETTIME_NS_METHODDEF + + OS__PATH_ISDEVDRIVE_METHODDEF + OS__PATH_ISDIR_METHODDEF + OS__PATH_ISFILE_METHODDEF + OS__PATH_ISLINK_METHODDEF + OS__PATH_ISJUNCTION_METHODDEF + OS__PATH_EXISTS_METHODDEF + OS__PATH_LEXISTS_METHODDEF + + OS__SUPPORTS_VIRTUAL_TERMINAL_METHODDEF + OS__INPUTHOOK_METHODDEF + OS__IS_INPUTHOOK_INSTALLED_METHODDEF + {NULL, NULL} /* Sentinel */ +}; + +static int +all_ins(PyObject *m) +{ +#ifdef F_OK + if (PyModule_AddIntMacro(m, F_OK)) return -1; +#endif +#ifdef R_OK + if (PyModule_AddIntMacro(m, R_OK)) return -1; +#endif +#ifdef W_OK + if (PyModule_AddIntMacro(m, W_OK)) return -1; +#endif +#ifdef X_OK + if (PyModule_AddIntMacro(m, X_OK)) return -1; +#endif +#ifdef NGROUPS_MAX + if (PyModule_AddIntMacro(m, NGROUPS_MAX)) return -1; +#endif +#ifdef TMP_MAX + if (PyModule_AddIntMacro(m, TMP_MAX)) return -1; +#endif +#ifdef WCONTINUED + if (PyModule_AddIntMacro(m, WCONTINUED)) return -1; +#endif +#ifdef WNOHANG + if (PyModule_AddIntMacro(m, WNOHANG)) return -1; +#endif +#ifdef WUNTRACED + if (PyModule_AddIntMacro(m, WUNTRACED)) return -1; +#endif +#ifdef O_RDONLY + if (PyModule_AddIntMacro(m, O_RDONLY)) return -1; +#endif +#ifdef O_WRONLY + if (PyModule_AddIntMacro(m, O_WRONLY)) return -1; +#endif +#ifdef O_RDWR + if (PyModule_AddIntMacro(m, O_RDWR)) return -1; +#endif +#ifdef O_NDELAY + if (PyModule_AddIntMacro(m, O_NDELAY)) return -1; +#endif +#ifdef O_NONBLOCK + if (PyModule_AddIntMacro(m, O_NONBLOCK)) return -1; +#endif +#ifdef O_APPEND + if (PyModule_AddIntMacro(m, O_APPEND)) return -1; +#endif +#ifdef O_DSYNC + if (PyModule_AddIntMacro(m, O_DSYNC)) return -1; +#endif +#ifdef O_RSYNC + if (PyModule_AddIntMacro(m, O_RSYNC)) return -1; +#endif +#ifdef O_SYNC + if (PyModule_AddIntMacro(m, O_SYNC)) return -1; +#endif +#ifdef O_NOCTTY + if (PyModule_AddIntMacro(m, O_NOCTTY)) return -1; +#endif +#ifdef O_CREAT + if (PyModule_AddIntMacro(m, O_CREAT)) return -1; +#endif +#ifdef O_EXCL + if (PyModule_AddIntMacro(m, O_EXCL)) return -1; +#endif +#ifdef O_TRUNC + if (PyModule_AddIntMacro(m, O_TRUNC)) return -1; +#endif +#ifdef O_BINARY + if (PyModule_AddIntMacro(m, O_BINARY)) return -1; +#endif +#ifdef O_TEXT + if (PyModule_AddIntMacro(m, O_TEXT)) return -1; +#endif +#ifdef O_XATTR + if (PyModule_AddIntMacro(m, O_XATTR)) return -1; +#endif +#ifdef O_LARGEFILE + if (PyModule_AddIntMacro(m, O_LARGEFILE)) return -1; +#endif +#ifndef __GNU__ +#ifdef O_SHLOCK + if (PyModule_AddIntMacro(m, O_SHLOCK)) return -1; +#endif +#ifdef O_EXLOCK + if (PyModule_AddIntMacro(m, O_EXLOCK)) return -1; +#endif +#endif +#ifdef O_EXEC + if (PyModule_AddIntMacro(m, O_EXEC)) return -1; +#endif +#ifdef O_SEARCH + if (PyModule_AddIntMacro(m, O_SEARCH)) return -1; +#endif +#ifdef O_PATH + if (PyModule_AddIntMacro(m, O_PATH)) return -1; +#endif +#ifdef O_TTY_INIT + if (PyModule_AddIntMacro(m, O_TTY_INIT)) return -1; +#endif +#ifdef O_TMPFILE + if (PyModule_AddIntMacro(m, O_TMPFILE)) return -1; +#endif +#ifdef PRIO_PROCESS + if (PyModule_AddIntMacro(m, PRIO_PROCESS)) return -1; +#endif +#ifdef PRIO_PGRP + if (PyModule_AddIntMacro(m, PRIO_PGRP)) return -1; +#endif +#ifdef PRIO_USER + if (PyModule_AddIntMacro(m, PRIO_USER)) return -1; +#endif +#ifdef PRIO_DARWIN_THREAD + if (PyModule_AddIntMacro(m, PRIO_DARWIN_THREAD)) return -1; +#endif +#ifdef PRIO_DARWIN_PROCESS + if (PyModule_AddIntMacro(m, PRIO_DARWIN_PROCESS)) return -1; +#endif +#ifdef PRIO_DARWIN_BG + if (PyModule_AddIntMacro(m, PRIO_DARWIN_BG)) return -1; +#endif +#ifdef PRIO_DARWIN_NONUI + if (PyModule_AddIntMacro(m, PRIO_DARWIN_NONUI)) return -1; +#endif +#ifdef O_CLOEXEC + if (PyModule_AddIntMacro(m, O_CLOEXEC)) return -1; +#endif +#ifdef O_ACCMODE + if (PyModule_AddIntMacro(m, O_ACCMODE)) return -1; +#endif +#ifdef O_EVTONLY + if (PyModule_AddIntMacro(m, O_EVTONLY)) return -1; +#endif +#ifdef O_FSYNC + if (PyModule_AddIntMacro(m, O_FSYNC)) return -1; +#endif +#ifdef O_SYMLINK + if (PyModule_AddIntMacro(m, O_SYMLINK)) return -1; +#endif + +#ifdef SEEK_HOLE + if (PyModule_AddIntMacro(m, SEEK_HOLE)) return -1; +#endif +#ifdef SEEK_DATA + if (PyModule_AddIntMacro(m, SEEK_DATA)) return -1; +#endif + +/* MS Windows */ +#ifdef O_NOINHERIT + /* Don't inherit in child processes. */ + if (PyModule_AddIntMacro(m, O_NOINHERIT)) return -1; +#endif +#ifdef _O_SHORT_LIVED + /* Optimize for short life (keep in memory). */ + /* MS forgot to define this one with a non-underscore form too. */ + if (PyModule_AddIntConstant(m, "O_SHORT_LIVED", _O_SHORT_LIVED)) return -1; +#endif +#ifdef O_TEMPORARY + /* Automatically delete when last handle is closed. */ + if (PyModule_AddIntMacro(m, O_TEMPORARY)) return -1; +#endif +#ifdef O_RANDOM + /* Optimize for random access. */ + if (PyModule_AddIntMacro(m, O_RANDOM)) return -1; +#endif +#ifdef O_SEQUENTIAL + /* Optimize for sequential access. */ + if (PyModule_AddIntMacro(m, O_SEQUENTIAL)) return -1; +#endif + +/* GNU extensions. */ +#ifdef O_ASYNC + /* Send a SIGIO signal whenever input or output + becomes available on file descriptor */ + if (PyModule_AddIntMacro(m, O_ASYNC)) return -1; +#endif +#ifdef O_DIRECT + /* Direct disk access. */ + if (PyModule_AddIntMacro(m, O_DIRECT)) return -1; +#endif +#ifdef O_DIRECTORY + /* Must be a directory. */ + if (PyModule_AddIntMacro(m, O_DIRECTORY)) return -1; +#endif +#ifdef O_NOFOLLOW + /* Do not follow links. */ + if (PyModule_AddIntMacro(m, O_NOFOLLOW)) return -1; +#endif +#ifdef O_NOFOLLOW_ANY + if (PyModule_AddIntMacro(m, O_NOFOLLOW_ANY)) return -1; +#endif +#ifdef O_NOLINKS + /* Fails if link count of the named file is greater than 1 */ + if (PyModule_AddIntMacro(m, O_NOLINKS)) return -1; +#endif +#ifdef O_NOATIME + /* Do not update the access time. */ + if (PyModule_AddIntMacro(m, O_NOATIME)) return -1; +#endif + + /* These come from sysexits.h */ +#ifdef EX_OK + if (PyModule_AddIntMacro(m, EX_OK)) return -1; +#endif /* EX_OK */ +#ifdef EX_USAGE + if (PyModule_AddIntMacro(m, EX_USAGE)) return -1; +#endif /* EX_USAGE */ +#ifdef EX_DATAERR + if (PyModule_AddIntMacro(m, EX_DATAERR)) return -1; +#endif /* EX_DATAERR */ +#ifdef EX_NOINPUT + if (PyModule_AddIntMacro(m, EX_NOINPUT)) return -1; +#endif /* EX_NOINPUT */ +#ifdef EX_NOUSER + if (PyModule_AddIntMacro(m, EX_NOUSER)) return -1; +#endif /* EX_NOUSER */ +#ifdef EX_NOHOST + if (PyModule_AddIntMacro(m, EX_NOHOST)) return -1; +#endif /* EX_NOHOST */ +#ifdef EX_UNAVAILABLE + if (PyModule_AddIntMacro(m, EX_UNAVAILABLE)) return -1; +#endif /* EX_UNAVAILABLE */ +#ifdef EX_SOFTWARE + if (PyModule_AddIntMacro(m, EX_SOFTWARE)) return -1; +#endif /* EX_SOFTWARE */ +#ifdef EX_OSERR + if (PyModule_AddIntMacro(m, EX_OSERR)) return -1; +#endif /* EX_OSERR */ +#ifdef EX_OSFILE + if (PyModule_AddIntMacro(m, EX_OSFILE)) return -1; +#endif /* EX_OSFILE */ +#ifdef EX_CANTCREAT + if (PyModule_AddIntMacro(m, EX_CANTCREAT)) return -1; +#endif /* EX_CANTCREAT */ +#ifdef EX_IOERR + if (PyModule_AddIntMacro(m, EX_IOERR)) return -1; +#endif /* EX_IOERR */ +#ifdef EX_TEMPFAIL + if (PyModule_AddIntMacro(m, EX_TEMPFAIL)) return -1; +#endif /* EX_TEMPFAIL */ +#ifdef EX_PROTOCOL + if (PyModule_AddIntMacro(m, EX_PROTOCOL)) return -1; +#endif /* EX_PROTOCOL */ +#ifdef EX_NOPERM + if (PyModule_AddIntMacro(m, EX_NOPERM)) return -1; +#endif /* EX_NOPERM */ +#ifdef EX_CONFIG + if (PyModule_AddIntMacro(m, EX_CONFIG)) return -1; +#endif /* EX_CONFIG */ +#ifdef EX_NOTFOUND + if (PyModule_AddIntMacro(m, EX_NOTFOUND)) return -1; +#endif /* EX_NOTFOUND */ + + /* statvfs */ +#ifdef ST_RDONLY + if (PyModule_AddIntMacro(m, ST_RDONLY)) return -1; +#endif /* ST_RDONLY */ +#ifdef ST_NOSUID + if (PyModule_AddIntMacro(m, ST_NOSUID)) return -1; +#endif /* ST_NOSUID */ + + /* GNU extensions */ +#ifdef ST_NODEV + if (PyModule_AddIntMacro(m, ST_NODEV)) return -1; +#endif /* ST_NODEV */ +#ifdef ST_NOEXEC + if (PyModule_AddIntMacro(m, ST_NOEXEC)) return -1; +#endif /* ST_NOEXEC */ +#ifdef ST_SYNCHRONOUS + if (PyModule_AddIntMacro(m, ST_SYNCHRONOUS)) return -1; +#endif /* ST_SYNCHRONOUS */ +#ifdef ST_MANDLOCK + if (PyModule_AddIntMacro(m, ST_MANDLOCK)) return -1; +#endif /* ST_MANDLOCK */ +#ifdef ST_WRITE + if (PyModule_AddIntMacro(m, ST_WRITE)) return -1; +#endif /* ST_WRITE */ +#ifdef ST_APPEND + if (PyModule_AddIntMacro(m, ST_APPEND)) return -1; +#endif /* ST_APPEND */ +#ifdef ST_NOATIME + if (PyModule_AddIntMacro(m, ST_NOATIME)) return -1; +#endif /* ST_NOATIME */ +#ifdef ST_NODIRATIME + if (PyModule_AddIntMacro(m, ST_NODIRATIME)) return -1; +#endif /* ST_NODIRATIME */ +#ifdef ST_RELATIME + if (PyModule_AddIntMacro(m, ST_RELATIME)) return -1; +#endif /* ST_RELATIME */ + + /* FreeBSD sendfile() constants */ +#ifdef SF_NODISKIO + if (PyModule_AddIntMacro(m, SF_NODISKIO)) return -1; +#endif + /* is obsolete since the 11.x release */ +#ifdef SF_MNOWAIT + if (PyModule_AddIntMacro(m, SF_MNOWAIT)) return -1; +#endif +#ifdef SF_SYNC + if (PyModule_AddIntMacro(m, SF_SYNC)) return -1; +#endif +#ifdef SF_NOCACHE + if (PyModule_AddIntMacro(m, SF_NOCACHE)) return -1; +#endif + +#ifdef TFD_NONBLOCK + if (PyModule_AddIntMacro(m, TFD_NONBLOCK)) return -1; +#endif +#ifdef TFD_CLOEXEC + if (PyModule_AddIntMacro(m, TFD_CLOEXEC)) return -1; +#endif +#ifdef TFD_TIMER_ABSTIME + if (PyModule_AddIntMacro(m, TFD_TIMER_ABSTIME)) return -1; +#endif +#ifdef TFD_TIMER_CANCEL_ON_SET + if (PyModule_AddIntMacro(m, TFD_TIMER_CANCEL_ON_SET)) return -1; +#endif + + /* constants for posix_fadvise */ +#ifdef POSIX_FADV_NORMAL + if (PyModule_AddIntMacro(m, POSIX_FADV_NORMAL)) return -1; +#endif +#ifdef POSIX_FADV_SEQUENTIAL + if (PyModule_AddIntMacro(m, POSIX_FADV_SEQUENTIAL)) return -1; +#endif +#ifdef POSIX_FADV_RANDOM + if (PyModule_AddIntMacro(m, POSIX_FADV_RANDOM)) return -1; +#endif +#ifdef POSIX_FADV_NOREUSE + if (PyModule_AddIntMacro(m, POSIX_FADV_NOREUSE)) return -1; +#endif +#ifdef POSIX_FADV_WILLNEED + if (PyModule_AddIntMacro(m, POSIX_FADV_WILLNEED)) return -1; +#endif +#ifdef POSIX_FADV_DONTNEED + if (PyModule_AddIntMacro(m, POSIX_FADV_DONTNEED)) return -1; +#endif + + /* constants for waitid */ +#if defined(HAVE_SYS_WAIT_H) && defined(HAVE_WAITID) + if (PyModule_AddIntMacro(m, P_PID)) return -1; + if (PyModule_AddIntMacro(m, P_PGID)) return -1; + if (PyModule_AddIntMacro(m, P_ALL)) return -1; +#ifdef P_PIDFD + if (PyModule_AddIntMacro(m, P_PIDFD)) return -1; +#endif +#ifdef PIDFD_NONBLOCK + if (PyModule_AddIntMacro(m, PIDFD_NONBLOCK)) return -1; +#endif +#endif +#ifdef WEXITED + if (PyModule_AddIntMacro(m, WEXITED)) return -1; +#endif +#ifdef WNOWAIT + if (PyModule_AddIntMacro(m, WNOWAIT)) return -1; +#endif +#ifdef WSTOPPED + if (PyModule_AddIntMacro(m, WSTOPPED)) return -1; +#endif +#ifdef CLD_EXITED + if (PyModule_AddIntMacro(m, CLD_EXITED)) return -1; +#endif +#ifdef CLD_KILLED + if (PyModule_AddIntMacro(m, CLD_KILLED)) return -1; +#endif +#ifdef CLD_DUMPED + if (PyModule_AddIntMacro(m, CLD_DUMPED)) return -1; +#endif +#ifdef CLD_TRAPPED + if (PyModule_AddIntMacro(m, CLD_TRAPPED)) return -1; +#endif +#ifdef CLD_STOPPED + if (PyModule_AddIntMacro(m, CLD_STOPPED)) return -1; +#endif +#ifdef CLD_CONTINUED + if (PyModule_AddIntMacro(m, CLD_CONTINUED)) return -1; +#endif + + /* constants for lockf */ +#ifdef F_LOCK + if (PyModule_AddIntMacro(m, F_LOCK)) return -1; +#endif +#ifdef F_TLOCK + if (PyModule_AddIntMacro(m, F_TLOCK)) return -1; +#endif +#ifdef F_ULOCK + if (PyModule_AddIntMacro(m, F_ULOCK)) return -1; +#endif +#ifdef F_TEST + if (PyModule_AddIntMacro(m, F_TEST)) return -1; +#endif + +#ifdef RWF_DSYNC + if (PyModule_AddIntConstant(m, "RWF_DSYNC", RWF_DSYNC)) return -1; +#endif +#ifdef RWF_HIPRI + if (PyModule_AddIntConstant(m, "RWF_HIPRI", RWF_HIPRI)) return -1; +#endif +#ifdef RWF_SYNC + if (PyModule_AddIntConstant(m, "RWF_SYNC", RWF_SYNC)) return -1; +#endif +#ifdef RWF_NOWAIT + if (PyModule_AddIntConstant(m, "RWF_NOWAIT", RWF_NOWAIT)) return -1; +#endif +#ifdef RWF_APPEND + if (PyModule_AddIntConstant(m, "RWF_APPEND", RWF_APPEND)) return -1; +#endif + +/* constants for splice */ +#if defined(HAVE_SPLICE) && defined(__linux__) + if (PyModule_AddIntConstant(m, "SPLICE_F_MOVE", SPLICE_F_MOVE)) return -1; + if (PyModule_AddIntConstant(m, "SPLICE_F_NONBLOCK", SPLICE_F_NONBLOCK)) return -1; + if (PyModule_AddIntConstant(m, "SPLICE_F_MORE", SPLICE_F_MORE)) return -1; +#endif + +/* constants for posix_spawn */ +#ifdef HAVE_POSIX_SPAWN + if (PyModule_AddIntConstant(m, "POSIX_SPAWN_OPEN", POSIX_SPAWN_OPEN)) return -1; + if (PyModule_AddIntConstant(m, "POSIX_SPAWN_CLOSE", POSIX_SPAWN_CLOSE)) return -1; + if (PyModule_AddIntConstant(m, "POSIX_SPAWN_DUP2", POSIX_SPAWN_DUP2)) return -1; +#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP + if (PyModule_AddIntMacro(m, POSIX_SPAWN_CLOSEFROM)) return -1; +#endif +#endif + +#if defined(HAVE_SPAWNV) || defined (HAVE_RTPSPAWN) + if (PyModule_AddIntConstant(m, "P_WAIT", _P_WAIT)) return -1; + if (PyModule_AddIntConstant(m, "P_NOWAIT", _P_NOWAIT)) return -1; + if (PyModule_AddIntConstant(m, "P_NOWAITO", _P_NOWAITO)) return -1; +#endif +#ifdef HAVE_SPAWNV + if (PyModule_AddIntConstant(m, "P_OVERLAY", _OLD_P_OVERLAY)) return -1; + if (PyModule_AddIntConstant(m, "P_DETACH", _P_DETACH)) return -1; +#endif + +#ifdef HAVE_SCHED_H +#ifdef SCHED_OTHER + if (PyModule_AddIntMacro(m, SCHED_OTHER)) return -1; +#endif +#ifdef SCHED_FIFO + if (PyModule_AddIntMacro(m, SCHED_FIFO)) return -1; +#endif +#ifdef SCHED_RR + if (PyModule_AddIntMacro(m, SCHED_RR)) return -1; +#endif +#ifdef SCHED_SPORADIC + if (PyModule_AddIntMacro(m, SCHED_SPORADIC)) return -1; +#endif +#ifdef SCHED_BATCH + if (PyModule_AddIntMacro(m, SCHED_BATCH)) return -1; +#endif +#ifdef SCHED_IDLE + if (PyModule_AddIntMacro(m, SCHED_IDLE)) return -1; +#endif +#ifdef SCHED_RESET_ON_FORK + if (PyModule_AddIntMacro(m, SCHED_RESET_ON_FORK)) return -1; +#endif +#ifdef SCHED_SYS + if (PyModule_AddIntMacro(m, SCHED_SYS)) return -1; +#endif +#ifdef SCHED_IA + if (PyModule_AddIntMacro(m, SCHED_IA)) return -1; +#endif +#ifdef SCHED_FSS + if (PyModule_AddIntMacro(m, SCHED_FSS)) return -1; +#endif +#ifdef SCHED_FX + if (PyModule_AddIntConstant(m, "SCHED_FX", SCHED_FSS)) return -1; +#endif + +/* constants for namespaces */ +#if defined(HAVE_SETNS) || defined(HAVE_UNSHARE) +#ifdef CLONE_FS + if (PyModule_AddIntMacro(m, CLONE_FS)) return -1; +#endif +#ifdef CLONE_FILES + if (PyModule_AddIntMacro(m, CLONE_FILES)) return -1; +#endif +#ifdef CLONE_NEWNS + if (PyModule_AddIntMacro(m, CLONE_NEWNS)) return -1; +#endif +#ifdef CLONE_NEWCGROUP + if (PyModule_AddIntMacro(m, CLONE_NEWCGROUP)) return -1; +#endif +#ifdef CLONE_NEWUTS + if (PyModule_AddIntMacro(m, CLONE_NEWUTS)) return -1; +#endif +#ifdef CLONE_NEWIPC + if (PyModule_AddIntMacro(m, CLONE_NEWIPC)) return -1; +#endif +#ifdef CLONE_NEWUSER + if (PyModule_AddIntMacro(m, CLONE_NEWUSER)) return -1; +#endif +#ifdef CLONE_NEWPID + if (PyModule_AddIntMacro(m, CLONE_NEWPID)) return -1; +#endif +#ifdef CLONE_NEWNET + if (PyModule_AddIntMacro(m, CLONE_NEWNET)) return -1; +#endif +#ifdef CLONE_NEWTIME + if (PyModule_AddIntMacro(m, CLONE_NEWTIME)) return -1; +#endif +#ifdef CLONE_SYSVSEM + if (PyModule_AddIntMacro(m, CLONE_SYSVSEM)) return -1; +#endif +#ifdef CLONE_THREAD + if (PyModule_AddIntMacro(m, CLONE_THREAD)) return -1; +#endif +#ifdef CLONE_SIGHAND + if (PyModule_AddIntMacro(m, CLONE_SIGHAND)) return -1; +#endif +#ifdef CLONE_VM + if (PyModule_AddIntMacro(m, CLONE_VM)) return -1; +#endif +#endif + +#endif + +#ifdef USE_XATTRS + if (PyModule_AddIntMacro(m, XATTR_CREATE)) return -1; + if (PyModule_AddIntMacro(m, XATTR_REPLACE)) return -1; + if (PyModule_AddIntMacro(m, XATTR_SIZE_MAX)) return -1; +#endif + +#if HAVE_DECL_RTLD_LAZY + if (PyModule_AddIntMacro(m, RTLD_LAZY)) return -1; +#endif +#if HAVE_DECL_RTLD_NOW + if (PyModule_AddIntMacro(m, RTLD_NOW)) return -1; +#endif +#if HAVE_DECL_RTLD_GLOBAL + if (PyModule_AddIntMacro(m, RTLD_GLOBAL)) return -1; +#endif +#if HAVE_DECL_RTLD_LOCAL + if (PyModule_AddIntMacro(m, RTLD_LOCAL)) return -1; +#endif +#if HAVE_DECL_RTLD_NODELETE + if (PyModule_AddIntMacro(m, RTLD_NODELETE)) return -1; +#endif +#if HAVE_DECL_RTLD_NOLOAD + if (PyModule_AddIntMacro(m, RTLD_NOLOAD)) return -1; +#endif +#if HAVE_DECL_RTLD_DEEPBIND + if (PyModule_AddIntMacro(m, RTLD_DEEPBIND)) return -1; +#endif +#if HAVE_DECL_RTLD_MEMBER + if (PyModule_AddIntMacro(m, RTLD_MEMBER)) return -1; +#endif + +#ifdef HAVE_GETRANDOM_SYSCALL + if (PyModule_AddIntMacro(m, GRND_RANDOM)) return -1; + if (PyModule_AddIntMacro(m, GRND_NONBLOCK)) return -1; +#endif +#ifdef HAVE_MEMFD_CREATE + if (PyModule_AddIntMacro(m, MFD_CLOEXEC)) return -1; + if (PyModule_AddIntMacro(m, MFD_ALLOW_SEALING)) return -1; +#ifdef MFD_HUGETLB + if (PyModule_AddIntMacro(m, MFD_HUGETLB)) return -1; +#endif +#ifdef MFD_HUGE_SHIFT + if (PyModule_AddIntMacro(m, MFD_HUGE_SHIFT)) return -1; +#endif +#ifdef MFD_HUGE_MASK + if (PyModule_AddIntMacro(m, MFD_HUGE_MASK)) return -1; +#endif +#ifdef MFD_HUGE_64KB + if (PyModule_AddIntMacro(m, MFD_HUGE_64KB)) return -1; +#endif +#ifdef MFD_HUGE_512KB + if (PyModule_AddIntMacro(m, MFD_HUGE_512KB)) return -1; +#endif +#ifdef MFD_HUGE_1MB + if (PyModule_AddIntMacro(m, MFD_HUGE_1MB)) return -1; +#endif +#ifdef MFD_HUGE_2MB + if (PyModule_AddIntMacro(m, MFD_HUGE_2MB)) return -1; +#endif +#ifdef MFD_HUGE_8MB + if (PyModule_AddIntMacro(m, MFD_HUGE_8MB)) return -1; +#endif +#ifdef MFD_HUGE_16MB + if (PyModule_AddIntMacro(m, MFD_HUGE_16MB)) return -1; +#endif +#ifdef MFD_HUGE_32MB + if (PyModule_AddIntMacro(m, MFD_HUGE_32MB)) return -1; +#endif +#ifdef MFD_HUGE_256MB + if (PyModule_AddIntMacro(m, MFD_HUGE_256MB)) return -1; +#endif +#ifdef MFD_HUGE_512MB + if (PyModule_AddIntMacro(m, MFD_HUGE_512MB)) return -1; +#endif +#ifdef MFD_HUGE_1GB + if (PyModule_AddIntMacro(m, MFD_HUGE_1GB)) return -1; +#endif +#ifdef MFD_HUGE_2GB + if (PyModule_AddIntMacro(m, MFD_HUGE_2GB)) return -1; +#endif +#ifdef MFD_HUGE_16GB + if (PyModule_AddIntMacro(m, MFD_HUGE_16GB)) return -1; +#endif +#endif /* HAVE_MEMFD_CREATE */ + +#if defined(HAVE_EVENTFD) && defined(EFD_CLOEXEC) + if (PyModule_AddIntMacro(m, EFD_CLOEXEC)) return -1; +#ifdef EFD_NONBLOCK + if (PyModule_AddIntMacro(m, EFD_NONBLOCK)) return -1; +#endif +#ifdef EFD_SEMAPHORE + if (PyModule_AddIntMacro(m, EFD_SEMAPHORE)) return -1; +#endif +#endif /* HAVE_EVENTFD && EFD_CLOEXEC */ + +#if defined(__APPLE__) + if (PyModule_AddIntConstant(m, "_COPYFILE_DATA", COPYFILE_DATA)) return -1; + if (PyModule_AddIntConstant(m, "_COPYFILE_STAT", COPYFILE_STAT)) return -1; + if (PyModule_AddIntConstant(m, "_COPYFILE_ACL", COPYFILE_ACL)) return -1; + if (PyModule_AddIntConstant(m, "_COPYFILE_XATTR", COPYFILE_XATTR)) return -1; +#endif + +#ifdef MS_WINDOWS + if (PyModule_AddIntConstant(m, "_LOAD_LIBRARY_SEARCH_DEFAULT_DIRS", LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)) return -1; + if (PyModule_AddIntConstant(m, "_LOAD_LIBRARY_SEARCH_APPLICATION_DIR", LOAD_LIBRARY_SEARCH_APPLICATION_DIR)) return -1; + if (PyModule_AddIntConstant(m, "_LOAD_LIBRARY_SEARCH_SYSTEM32", LOAD_LIBRARY_SEARCH_SYSTEM32)) return -1; + if (PyModule_AddIntConstant(m, "_LOAD_LIBRARY_SEARCH_USER_DIRS", LOAD_LIBRARY_SEARCH_USER_DIRS)) return -1; + if (PyModule_AddIntConstant(m, "_LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR", LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR)) return -1; +#endif + + return 0; +} + + + +#define PROBE(name, test) \ + static int name(void) \ + { \ + if (test) { \ + return 1; \ + } else { \ + return 0; \ + } \ + } + +#ifdef HAVE_FSTATAT +PROBE(probe_fstatat, HAVE_FSTATAT_RUNTIME) +#endif + +#ifdef HAVE_FACCESSAT +PROBE(probe_faccessat, HAVE_FACCESSAT_RUNTIME) +#endif + +#ifdef HAVE_FCHMODAT +PROBE(probe_fchmodat, HAVE_FCHMODAT_RUNTIME) +#endif + +#ifdef HAVE_FCHOWNAT +PROBE(probe_fchownat, HAVE_FCHOWNAT_RUNTIME) +#endif + +#ifdef HAVE_LINKAT +PROBE(probe_linkat, HAVE_LINKAT_RUNTIME) +#endif + +#ifdef HAVE_FDOPENDIR +PROBE(probe_fdopendir, HAVE_FDOPENDIR_RUNTIME) +#endif + +#ifdef HAVE_MKDIRAT +PROBE(probe_mkdirat, HAVE_MKDIRAT_RUNTIME) +#endif + +#ifdef HAVE_MKFIFOAT +PROBE(probe_mkfifoat, HAVE_MKFIFOAT_RUNTIME) +#endif + +#ifdef HAVE_MKNODAT +PROBE(probe_mknodat, HAVE_MKNODAT_RUNTIME) +#endif + +#ifdef HAVE_RENAMEAT +PROBE(probe_renameat, HAVE_RENAMEAT_RUNTIME) +#endif + +#ifdef HAVE_UNLINKAT +PROBE(probe_unlinkat, HAVE_UNLINKAT_RUNTIME) +#endif + +#ifdef HAVE_OPENAT +PROBE(probe_openat, HAVE_OPENAT_RUNTIME) +#endif + +#ifdef HAVE_READLINKAT +PROBE(probe_readlinkat, HAVE_READLINKAT_RUNTIME) +#endif + +#ifdef HAVE_SYMLINKAT +PROBE(probe_symlinkat, HAVE_SYMLINKAT_RUNTIME) +#endif + +#ifdef HAVE_FUTIMENS +PROBE(probe_futimens, HAVE_FUTIMENS_RUNTIME) +#endif + +#ifdef HAVE_UTIMENSAT +PROBE(probe_utimensat, HAVE_UTIMENSAT_RUNTIME) +#endif + +#ifdef HAVE_PTSNAME_R +PROBE(probe_ptsname_r, HAVE_PTSNAME_R_RUNTIME) +#endif + + + +static const struct have_function { + const char * const label; + int (*probe)(void); +} have_functions[] = { + +#ifdef HAVE_EVENTFD + {"HAVE_EVENTFD", NULL}, +#endif + +#ifdef HAVE_TIMERFD_CREATE + {"HAVE_TIMERFD_CREATE", NULL}, +#endif + +#ifdef HAVE_FACCESSAT + { "HAVE_FACCESSAT", probe_faccessat }, +#endif + +#ifdef HAVE_FCHDIR + { "HAVE_FCHDIR", NULL }, +#endif + +#ifdef HAVE_FCHMOD + { "HAVE_FCHMOD", NULL }, +#endif + +#ifdef HAVE_FCHMODAT + { "HAVE_FCHMODAT", probe_fchmodat }, +#endif + +#ifdef HAVE_FCHOWN + { "HAVE_FCHOWN", NULL }, +#endif + +#ifdef HAVE_FCHOWNAT + { "HAVE_FCHOWNAT", probe_fchownat }, +#endif + +#ifdef HAVE_FEXECVE + { "HAVE_FEXECVE", NULL }, +#endif + +#ifdef HAVE_FDOPENDIR + { "HAVE_FDOPENDIR", probe_fdopendir }, +#endif + +#ifdef HAVE_FPATHCONF + { "HAVE_FPATHCONF", NULL }, +#endif + +#ifdef HAVE_FSTATAT + { "HAVE_FSTATAT", probe_fstatat }, +#endif + +#ifdef HAVE_FSTATVFS + { "HAVE_FSTATVFS", NULL }, +#endif + +#if defined HAVE_FTRUNCATE || defined MS_WINDOWS + { "HAVE_FTRUNCATE", NULL }, +#endif + +#ifdef HAVE_FUTIMENS + { "HAVE_FUTIMENS", probe_futimens }, +#endif + +#ifdef HAVE_FUTIMES + { "HAVE_FUTIMES", NULL }, +#endif + +#ifdef HAVE_FUTIMESAT + { "HAVE_FUTIMESAT", NULL }, +#endif + +#ifdef HAVE_LINKAT + { "HAVE_LINKAT", probe_linkat }, +#endif + +#ifdef HAVE_LCHFLAGS + { "HAVE_LCHFLAGS", NULL }, +#endif + +#ifdef HAVE_LCHMOD + { "HAVE_LCHMOD", NULL }, +#endif + +#ifdef HAVE_LCHOWN + { "HAVE_LCHOWN", NULL }, +#endif + +#ifdef HAVE_LSTAT + { "HAVE_LSTAT", NULL }, +#endif + +#ifdef HAVE_LUTIMES + { "HAVE_LUTIMES", NULL }, +#endif + +#ifdef HAVE_MEMFD_CREATE + { "HAVE_MEMFD_CREATE", NULL }, +#endif + +#ifdef HAVE_MKDIRAT + { "HAVE_MKDIRAT", probe_mkdirat }, +#endif + +#ifdef HAVE_MKFIFOAT + { "HAVE_MKFIFOAT", probe_mkfifoat }, +#endif + +#ifdef HAVE_MKNODAT + { "HAVE_MKNODAT", probe_mknodat }, +#endif + +#ifdef HAVE_OPENAT + { "HAVE_OPENAT", probe_openat }, +#endif + +#ifdef HAVE_READLINKAT + { "HAVE_READLINKAT", probe_readlinkat }, +#endif + +#ifdef HAVE_RENAMEAT + { "HAVE_RENAMEAT", probe_renameat }, +#endif + +#ifdef HAVE_SYMLINKAT + { "HAVE_SYMLINKAT", probe_symlinkat }, +#endif + +#ifdef HAVE_UNLINKAT + { "HAVE_UNLINKAT", probe_unlinkat }, +#endif + +#ifdef HAVE_UTIMENSAT + { "HAVE_UTIMENSAT", probe_utimensat }, +#endif + +#ifdef HAVE_PTSNAME_R + { "HAVE_PTSNAME_R", probe_ptsname_r }, +#endif + +#ifdef MS_WINDOWS + { "MS_WINDOWS", NULL }, +#endif + + { NULL, NULL } +}; + + +static int +posixmodule_exec(PyObject *m) +{ + _posixstate *state = get_posix_state(m); + +#if defined(HAVE_PWRITEV) + if (HAVE_PWRITEV_RUNTIME) {} else { + PyObject* dct = PyModule_GetDict(m); + + if (dct == NULL) { + return -1; + } + + if (PyDict_PopString(dct, "pwritev", NULL) < 0) { + return -1; + } + if (PyDict_PopString(dct, "preadv", NULL) < 0) { + return -1; + } + } +#endif + + /* Initialize environ dictionary */ + if (PyModule_Add(m, "environ", convertenviron()) != 0) { + return -1; + } + + if (all_ins(m)) + return -1; + + if (setup_confname_tables(m)) + return -1; + + if (PyModule_AddObjectRef(m, "error", PyExc_OSError) < 0) { + return -1; + } + +#if defined(HAVE_WAITID) + waitid_result_desc.name = MODNAME ".waitid_result"; + state->WaitidResultType = (PyObject *)PyStructSequence_NewType(&waitid_result_desc); + if (PyModule_AddObjectRef(m, "waitid_result", state->WaitidResultType) < 0) { + return -1; + } +#endif + + stat_result_desc.name = "os.stat_result"; /* see issue #19209 */ + stat_result_desc.fields[7].name = PyStructSequence_UnnamedField; + stat_result_desc.fields[8].name = PyStructSequence_UnnamedField; + stat_result_desc.fields[9].name = PyStructSequence_UnnamedField; + state->StatResultType = (PyObject *)PyStructSequence_NewType(&stat_result_desc); + if (PyModule_AddObjectRef(m, "stat_result", state->StatResultType) < 0) { + return -1; + } + state->statresult_new_orig = ((PyTypeObject *)state->StatResultType)->tp_new; + ((PyTypeObject *)state->StatResultType)->tp_new = statresult_new; + + statvfs_result_desc.name = "os.statvfs_result"; /* see issue #19209 */ + state->StatVFSResultType = (PyObject *)PyStructSequence_NewType(&statvfs_result_desc); + if (PyModule_AddObjectRef(m, "statvfs_result", state->StatVFSResultType) < 0) { + return -1; + } + +#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM) + sched_param_desc.name = MODNAME ".sched_param"; + state->SchedParamType = (PyObject *)PyStructSequence_NewType(&sched_param_desc); + if (PyModule_AddObjectRef(m, "sched_param", state->SchedParamType) < 0) { + return -1; + } + ((PyTypeObject *)state->SchedParamType)->tp_new = os_sched_param; + if (_PyType_AddMethod((PyTypeObject *)state->SchedParamType, + &os_sched_param_reduce_method) < 0) + { + return -1; + } + PyType_Modified((PyTypeObject *)state->SchedParamType); +#endif + + /* initialize TerminalSize_info */ + state->TerminalSizeType = (PyObject *)PyStructSequence_NewType(&TerminalSize_desc); + if (PyModule_AddObjectRef(m, "terminal_size", state->TerminalSizeType) < 0) { + return -1; + } + + /* initialize scandir types */ + PyObject *ScandirIteratorType = PyType_FromModuleAndSpec(m, &ScandirIteratorType_spec, NULL); + if (ScandirIteratorType == NULL) { + return -1; + } + state->ScandirIteratorType = ScandirIteratorType; + + state->DirEntryType = PyType_FromModuleAndSpec(m, &DirEntryType_spec, NULL); + if (PyModule_AddObjectRef(m, "DirEntry", state->DirEntryType) < 0) { + return -1; + } + + times_result_desc.name = MODNAME ".times_result"; + state->TimesResultType = (PyObject *)PyStructSequence_NewType(×_result_desc); + if (PyModule_AddObjectRef(m, "times_result", state->TimesResultType) < 0) { + return -1; + } + + state->UnameResultType = (PyObject *)PyStructSequence_NewType(&uname_result_desc); + if (PyModule_AddObjectRef(m, "uname_result", state->UnameResultType) < 0) { + return -1; + } + + if ((state->billion = PyLong_FromLong(1000000000)) == NULL) + return -1; +#if defined(HAVE_WAIT3) || defined(HAVE_WAIT4) + state->struct_rusage = PyUnicode_InternFromString("struct_rusage"); + if (state->struct_rusage == NULL) + return -1; +#endif + state->st_mode = PyUnicode_InternFromString("st_mode"); + if (state->st_mode == NULL) + return -1; + + /* suppress "function not used" warnings */ + { + int ignored; + fd_specified("", -1); + follow_symlinks_specified("", 1); + dir_fd_and_follow_symlinks_invalid("chmod", DEFAULT_DIR_FD, 1); + dir_fd_converter(Py_None, &ignored); + dir_fd_unavailable(Py_None, &ignored); + } + + /* + * provide list of locally available functions + * so os.py can populate support_* lists + */ + PyObject *list = PyList_New(0); + if (!list) { + return -1; + } + for (const struct have_function *trace = have_functions; trace->label; trace++) { + PyObject *unicode; + if (trace->probe && !trace->probe()) continue; + unicode = PyUnicode_DecodeASCII(trace->label, strlen(trace->label), NULL); + if (!unicode) + return -1; + if (PyList_Append(list, unicode)) + return -1; + Py_DECREF(unicode); + } + +#ifndef MS_WINDOWS + if (_Py_GetTicksPerSecond(&state->ticks_per_second) < 0) { + PyErr_SetString(PyExc_RuntimeError, + "cannot read ticks_per_second"); + return -1; + } + assert(state->ticks_per_second >= 1); +#endif + + return PyModule_Add(m, "_have_functions", list); +} + + +static PyModuleDef_Slot posixmodile_slots[] = { + {Py_mod_exec, posixmodule_exec}, + {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {0, NULL} +}; + +static struct PyModuleDef posixmodule = { + PyModuleDef_HEAD_INIT, + .m_name = MODNAME, + .m_doc = posix__doc__, + .m_size = sizeof(_posixstate), + .m_methods = posix_methods, + .m_slots = posixmodile_slots, + .m_traverse = _posix_traverse, + .m_clear = _posix_clear, + .m_free = _posix_free, +}; + +PyMODINIT_FUNC +INITFUNC(void) +{ + return PyModuleDef_Init(&posixmodule); +} diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 5bd9b7732a44a4..11ba70d58d03dc 100644 --- a/Modules/selectmodule.c +++ b/Modules/selectmodule.c @@ -168,9 +168,9 @@ seq2set(PyObject *seq, fd_set *set, pylist fd2obj[FD_SETSIZE + 1]) v = PyObject_AsFileDescriptor( o ); if (v == -1) goto finally; -#if defined(_MSC_VER) +#if defined(MS_WIN32) max = 0; /* not used for Win32 */ -#else /* !_MSC_VER */ +#else /* !MS_WIN32 */ if (!_PyIsSelectable_fd(v)) { PyErr_SetString(PyExc_ValueError, "filedescriptor out of range in select()"); @@ -178,7 +178,7 @@ seq2set(PyObject *seq, fd_set *set, pylist fd2obj[FD_SETSIZE + 1]) } if (v > max) max = v; -#endif /* _MSC_VER */ +#endif /* MS_WIN32 */ FD_SET(v, set); /* add object and its file descriptor to the list */ diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 1c0bc796732120..8878dd39154970 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -279,9 +279,10 @@ shutdown(how) -- shut down traffic in one or both directions\n\ # endif /* Helpers needed for AF_HYPERV */ -# include +# include /* Macros based on the IPPROTO enum, see: https://bugs.python.org/issue29515 */ +#ifdef _MSC_VER #define IPPROTO_ICMP IPPROTO_ICMP #define IPPROTO_IGMP IPPROTO_IGMP #define IPPROTO_GGP IPPROTO_GGP @@ -312,6 +313,7 @@ shutdown(how) -- shut down traffic in one or both directions\n\ #define IPPROTO_PGM IPPROTO_PGM // WinSock2 only #define IPPROTO_L2TP IPPROTO_L2TP // WinSock2 only #define IPPROTO_SCTP IPPROTO_SCTP // WinSock2 only +#endif /* _MSC_VER */ /* Provides the IsWindows7SP1OrGreater() function */ #include @@ -420,6 +422,10 @@ remove_unusable_flags(PyObject *m) /* Do not include addrinfo.h for MSVC7 or greater. 'addrinfo' and * EAI_* constants are defined in (the already included) ws2tcpip.h. */ +#elif defined(__MINGW32__) + /* Do not include addrinfo.h as minimum supported version is + * _WIN32_WINNT >= WindowsXP(0x0501) + */ #else # include "addrinfo.h" #endif diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h index 09fd70f351f1d8..b0595ed7f9b0e6 100644 --- a/Modules/socketmodule.h +++ b/Modules/socketmodule.h @@ -68,8 +68,10 @@ struct SOCKADDR_BTH_REDEF { */ # ifdef SIO_GET_MULTICAST_FILTER # include /* for SIO_RCVALL */ +#ifndef __MINGW32__ /* resolve by configure */ # define HAVE_ADDRINFO # define HAVE_SOCKADDR_STORAGE +#endif # define HAVE_GETADDRINFO # define HAVE_GETNAMEINFO # define ENABLE_IPV6 diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 94260ba83f680c..449fa41c8f40c1 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -890,7 +890,7 @@ time_strftime(PyObject *module, PyObject *args) // // Android works with negative years on the emulator, but fails on some // physical devices (#123017). -#if defined(_MSC_VER) || (defined(__sun) && defined(__SVR4)) || defined(_AIX) \ +#if defined(MS_WINDOWS) || (defined(__sun) && defined(__SVR4)) || defined(_AIX) \ || defined(__VXWORKS__) || defined(__ANDROID__) if (buf.tm_year + 1900 < 1 || 9999 < buf.tm_year + 1900) { PyErr_SetString(PyExc_ValueError, diff --git a/Objects/dictobject.c b/Objects/dictobject.c index f1f9110ff73e6d..233bd1f01819d5 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -582,7 +582,7 @@ calculate_log2_keysize(Py_ssize_t minsize) #if SIZEOF_LONG == SIZEOF_SIZE_T minsize = (minsize | PyDict_MINSIZE) - 1; return _Py_bit_length(minsize | (PyDict_MINSIZE-1)); -#elif defined(_MSC_VER) +#elif defined(MS_WINDOWS) // On 64bit Windows, sizeof(long) == 4. minsize = (minsize | PyDict_MINSIZE) - 1; unsigned long msb; diff --git a/Objects/fileobject.c b/Objects/fileobject.c index bae49d367b65ee..e0c1cfdfefafd4 100644 --- a/Objects/fileobject.c +++ b/Objects/fileobject.c @@ -2,6 +2,7 @@ #include "Python.h" #include "pycore_call.h" // _PyObject_CallNoArgs() +#include "iscygpty.h" #include "pycore_runtime.h" // _PyRuntime #ifdef HAVE_UNISTD_H @@ -382,7 +383,7 @@ stdprinter_isatty(PyStdPrinter_Object *self, PyObject *Py_UNUSED(ignored)) } Py_BEGIN_ALLOW_THREADS - res = isatty(self->fd); + res = isatty(self->fd) || is_cygpty(self->fd); Py_END_ALLOW_THREADS return PyBool_FromLong(res); diff --git a/PC/_testconsole.c b/PC/_testconsole.c index 0dcea866f65d35..c38378af425c12 100644 --- a/PC/_testconsole.c +++ b/PC/_testconsole.c @@ -140,7 +140,7 @@ _testconsole_read_output_impl(PyObject *module, PyObject *file) } -#include "clinic\_testconsole.c.h" +#include "clinic/_testconsole.c.h" PyMethodDef testconsole_methods[] = { _TESTCONSOLE_WRITE_INPUT_METHODDEF diff --git a/PC/launcher.c b/PC/launcher.c index 8e60ab9303cb95..6c8cd760818517 100644 --- a/PC/launcher.c +++ b/PC/launcher.c @@ -924,7 +924,7 @@ static COMMAND path_command; static COMMAND * find_on_path(wchar_t * name) { wchar_t * pathext; - size_t varsize; + size_t requiredSize; wchar_t * context = NULL; wchar_t * extension; COMMAND * result = NULL; @@ -941,18 +941,23 @@ static COMMAND * find_on_path(wchar_t * name) } else { /* No extension - search using registered extensions. */ - rc = _wdupenv_s(&pathext, &varsize, L"PATHEXT"); - if (rc == 0) { - extension = wcstok_s(pathext, L";", &context); - while (extension) { - len = SearchPathW(NULL, name, extension, MSGSIZE, path_command.value, NULL); - if (len) { - result = &path_command; - break; + _wgetenv_s(&requiredSize, NULL, 0, L"PATHEXT"); + if (requiredSize > 0) { + pathext = (wchar_t *)malloc(requiredSize * sizeof(wchar_t)); + /* No extension - search using registered extensions. */ + rc = _wgetenv_s(&requiredSize, pathext, requiredSize, L"PATHEXT"); + if (rc == 0) { + extension = wcstok_s(pathext, L";", &context); + while (extension) { + len = SearchPathW(NULL, name, extension, MSGSIZE, path_command.value, NULL); + if (len) { + result = &path_command; + break; + } + extension = wcstok_s(NULL, L";", &context); } - extension = wcstok_s(NULL, L";", &context); + free(pathext); } - free(pathext); } } return result; @@ -1913,7 +1918,7 @@ process(int argc, wchar_t ** argv) if (_wfopen_s(&f, venv_cfg_path, L"r")) { error(RC_BAD_VENV_CFG, L"Cannot read '%ls'", venv_cfg_path); } - cb = fread_s(buffer, sizeof(buffer), sizeof(buffer[0]), + cb = fread(buffer, sizeof(buffer[0]), sizeof(buffer) / sizeof(buffer[0]), f); fclose(f); @@ -1926,7 +1931,8 @@ process(int argc, wchar_t ** argv) if (!cch) { error(0, L"Cannot determine memory for home path"); } - cch += (DWORD)wcslen(PYTHON_EXECUTABLE) + 4; /* include sep, null and quotes */ + cch += (DWORD)max(wcslen(PYTHON_EXECUTABLE_WITH_VERSION), + wcslen(PYTHON_EXECUTABLE)) + 4; /* include sep, null and quotes */ executable = (wchar_t *)malloc(cch * sizeof(wchar_t)); if (executable == NULL) { error(RC_NO_MEMORY, L"A memory allocation failed"); @@ -1944,13 +1950,22 @@ process(int argc, wchar_t ** argv) executable[cch_actual++] = L'\\'; executable[cch_actual] = L'\0'; } - if (wcscat_s(&executable[1], cch - 1, PYTHON_EXECUTABLE)) { + if (wcscat_s(&executable[1], cch - 1, PYTHON_EXECUTABLE_WITH_VERSION)) { error(RC_BAD_VENV_CFG, L"Cannot create executable path from '%ls'", venv_cfg_path); } /* there's no trailing quote, so we only have to skip one character for the test */ + // Check if the versioned executable (PYTHON_EXECUTABLE_WITH_VERSION) exists first if (GetFileAttributesW(&executable[1]) == INVALID_FILE_ATTRIBUTES) { - error(RC_NO_PYTHON, L"No Python at '%ls'", executable); + // If not found, try PYTHON_EXECUTABLE + executable[cch_actual] = L'\0'; // Reset the path + if (wcscat_s(&executable[1], cch - 1, PYTHON_EXECUTABLE)) { + error(RC_BAD_VENV_CFG, L"Cannot create executable path from '%ls'", + venv_cfg_path); + } + if (GetFileAttributesW(&executable[1]) == INVALID_FILE_ATTRIBUTES) { + error(RC_NO_PYTHON, L"No Python at '%ls'", executable); + } } /* now append the final quote */ wcscat_s(executable, cch, L"\""); diff --git a/PC/msvcrtmodule.c b/PC/msvcrtmodule.c index b170e06b47dd59..b4c3f92b54129e 100644 --- a/PC/msvcrtmodule.c +++ b/PC/msvcrtmodule.c @@ -22,7 +22,9 @@ #include #include #include +#ifdef _DEBUG #include +#endif #include #ifdef _MSC_VER diff --git a/PC/pylauncher.rc b/PC/pylauncher.rc index 11862643aa6989..84b291722f6633 100644 --- a/PC/pylauncher.rc +++ b/PC/pylauncher.rc @@ -12,17 +12,17 @@ 1 RT_MANIFEST "python.manifest" #if defined(PY_ICON) -1 ICON DISCARDABLE "icons\python.ico" +1 ICON DISCARDABLE "icons/python.ico" #elif defined(PYW_ICON) -1 ICON DISCARDABLE "icons\pythonw.ico" +1 ICON DISCARDABLE "icons/pythonw.ico" #else -1 ICON DISCARDABLE "icons\launcher.ico" -2 ICON DISCARDABLE "icons\py.ico" -3 ICON DISCARDABLE "icons\pyc.ico" -4 ICON DISCARDABLE "icons\pyd.ico" -5 ICON DISCARDABLE "icons\python.ico" -6 ICON DISCARDABLE "icons\pythonw.ico" -7 ICON DISCARDABLE "icons\setup.ico" +1 ICON DISCARDABLE "icons/launcher.ico" +2 ICON DISCARDABLE "icons/py.ico" +3 ICON DISCARDABLE "icons/pyc.ico" +4 ICON DISCARDABLE "icons/pyd.ico" +5 ICON DISCARDABLE "icons/python.ico" +6 ICON DISCARDABLE "icons/pythonw.ico" +7 ICON DISCARDABLE "icons/setup.ico" #endif 1 USAGE "launcher-usage.txt" @@ -64,4 +64,4 @@ BEGIN BEGIN VALUE "Translation", 0x0, 1200 END -END \ No newline at end of file +END diff --git a/PC/python3dll.c b/PC/python3dll.c index eb077df70a57d6..453e8cb977ace0 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -2,6 +2,7 @@ /* Generated by Tools/build/stable_abi.py */ +#ifdef _MSC_VER #ifdef _M_IX86 #define DECORATE "_" #else @@ -12,6 +13,13 @@ __pragma(comment(linker, "/EXPORT:" DECORATE #name "=" PYTHON_DLL_NAME "." #name)) #define EXPORT_DATA(name) \ __pragma(comment(linker, "/EXPORT:" DECORATE #name "=" PYTHON_DLL_NAME "." #name ",DATA")) +#else +// XXX: Why do we need the .dll extension and no DECORATE compared to the MSVC case? +#define EXPORT_FUNC(name) \ + asm(".section .drectve\n\t.ascii \" -export:" #name "=\\\"" PYTHON_DLL_NAME "." #name "\\\" \""); +#define EXPORT_DATA(name) \ + asm(".section .drectve\n\t.ascii \" -export:" #name "=\\\"" PYTHON_DLL_NAME "." #name "\\\",DATA \""); +#endif EXPORT_FUNC(_Py_BuildValue_SizeT) EXPORT_FUNC(_Py_CheckRecursiveCall) diff --git a/PC/python_exe.rc b/PC/python_exe.rc index c3d3bff019895e..dde0e5384201fa 100644 --- a/PC/python_exe.rc +++ b/PC/python_exe.rc @@ -12,7 +12,7 @@ // current versions of Windows. 1 RT_MANIFEST "python.manifest" -1 ICON DISCARDABLE "icons\python.ico" +1 ICON DISCARDABLE "icons/python.ico" ///////////////////////////////////////////////////////////////////////////// diff --git a/PC/pythonw_exe.rc b/PC/pythonw_exe.rc index 38570b74fa3e02..7ce1043298eac4 100644 --- a/PC/pythonw_exe.rc +++ b/PC/pythonw_exe.rc @@ -12,7 +12,7 @@ // current versions of Windows. 1 RT_MANIFEST "python.manifest" -1 ICON DISCARDABLE "icons\pythonw.ico" +1 ICON DISCARDABLE "icons/pythonw.ico" ///////////////////////////////////////////////////////////////////////////// diff --git a/PC/winreg.c b/PC/winreg.c index efdf8addc06186..2cc175cc4796e0 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -18,6 +18,25 @@ #include +#ifndef SIZEOF_HKEY +/* used only here */ +#if defined(MS_WIN64) +# define SIZEOF_HKEY 8 +#elif defined(MS_WIN32) +# define SIZEOF_HKEY 4 +#else +# error "SIZEOF_HKEY is not defined" +#endif +#endif + +#ifndef REG_LEGAL_CHANGE_FILTER +#define REG_LEGAL_CHANGE_FILTER (\ + REG_NOTIFY_CHANGE_NAME |\ + REG_NOTIFY_CHANGE_ATTRIBUTES |\ + REG_NOTIFY_CHANGE_LAST_SET |\ + REG_NOTIFY_CHANGE_SECURITY ) +#endif + #if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) || defined(MS_WINDOWS_GAMES) typedef struct { @@ -816,6 +835,7 @@ Reg2Py(BYTE *retDataBuf, DWORD retDataSize, DWORD typ) case REG_BINARY: /* ALSO handle ALL unknown data types here. Even if we can't support it natively, we should handle the bits. */ + /* fallthrough */ default: if (retDataSize == 0) { obData = Py_NewRef(Py_None); diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 5afa94cf2217d7..ef6ed7ba905e37 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -16,6 +16,8 @@ #include "clinic/bltinmodule.c.h" +#include "iscygpty.h" + #ifdef HAVE_UNISTD_H # include // isatty() #endif @@ -2202,7 +2204,7 @@ builtin_input_impl(PyObject *module, PyObject *prompt) Py_DECREF(tmp); if (fd < 0 && PyErr_Occurred()) return NULL; - tty = fd == fileno(stdin) && isatty(fd); + tty = fd == fileno(stdin) && (isatty(fd) || is_cygpty(fd)); } if (tty) { tmp = PyObject_CallMethodNoArgs(fout, &_Py_ID(fileno)); @@ -2215,7 +2217,7 @@ builtin_input_impl(PyObject *module, PyObject *prompt) Py_DECREF(tmp); if (fd < 0 && PyErr_Occurred()) return NULL; - tty = fd == fileno(stdout) && isatty(fd); + tty = fd == fileno(stdout) && (isatty(fd) || is_cygpty(fd)); } } diff --git a/Python/dynamic_annotations.c b/Python/dynamic_annotations.c index 7febaa09df1950..70d5b3dc72069c 100644 --- a/Python/dynamic_annotations.c +++ b/Python/dynamic_annotations.c @@ -27,7 +27,7 @@ * Author: Kostya Serebryany */ -#ifdef _MSC_VER +#ifdef MS_WINDOWS # include #endif diff --git a/Python/dynload_win.c b/Python/dynload_win.c index a0ac31c80a5f6e..8242e947745572 100644 --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -4,6 +4,7 @@ #include "Python.h" #include "pycore_fileutils.h" // _Py_add_relfile() #include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_initconfig.h" #include "pycore_importdl.h" // dl_funcptr #include "patchlevel.h" // PY_MAJOR_VERSION @@ -36,6 +37,16 @@ const char *_PyImport_DynLoadFiletab[] = { #define DWORD_AT(mem) (*(DWORD *)(mem)) #define WORD_AT(mem) (*(WORD *)(mem)) +#ifdef __MINGW32__ +#define DLL_PREFIX "libpython" +#define DLL_PREFIX_LEN 9 +#define DLL_NAME_FORMAT "libpython%d.%d" +#else +#define DLL_PREFIX "python" +#define DLL_PREFIX_LEN 6 +#define DLL_NAME_FORMAT "python%d%d" +#endif + static char *GetPythonImport (HINSTANCE hModule) { unsigned char *dllbase, *import_data, *import_name; @@ -102,15 +113,15 @@ static char *GetPythonImport (HINSTANCE hModule) import_off); while (DWORD_AT(import_data)) { import_name = dllbase + DWORD_AT(import_data+12); - if (strlen(import_name) >= 6 && - !strncmp(import_name,"python",6)) { + if (strlen(import_name) >= DLL_PREFIX_LEN && + !strncmp(import_name, DLL_PREFIX, DLL_PREFIX_LEN)) { char *pch; /* Don't claim that python3.dll is a Python DLL. */ #ifdef _DEBUG - if (strcmp(import_name, "python3_d.dll") == 0) { + if (strcmp(import_name, DLL_PREFIX "3_d.dll") == 0) { #else - if (strcmp(import_name, "python3.dll") == 0) { + if (strcmp(import_name, DLL_PREFIX "3.dll") == 0) { #endif import_data += 20; continue; @@ -118,7 +129,7 @@ static char *GetPythonImport (HINSTANCE hModule) /* Ensure python prefix is followed only by numbers to the end of the basename */ - pch = import_name + 6; + pch = import_name + DLL_PREFIX_LEN; #ifdef _DEBUG while (*pch && pch[0] != '_' && pch[1] != 'd' && pch[2] != '.') { #else @@ -152,8 +163,7 @@ static char *GetPythonImport (HINSTANCE hModule) Return whether the DLL was found. */ extern HMODULE PyWin_DLLhModule; -static int -_Py_CheckPython3(void) +int _Py_CheckPython3(void) { static int python3_checked = 0; static HANDLE hPython3; @@ -207,14 +217,38 @@ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix, dl_funcptr p; char funcname[258], *import_python; -#ifdef Py_ENABLE_SHARED - _Py_CheckPython3(); -#endif /* Py_ENABLE_SHARED */ + int use_legacy = 0; + DWORD load_library_flags = 0; + + _Py_get_env_flag(1, &use_legacy, "PYTHONLEGACYWINDOWSDLLLOADING"); + + if (use_legacy) { + load_library_flags = LOAD_WITH_ALTERED_SEARCH_PATH; + } else { + load_library_flags = LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | + LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR; + } + +#ifdef _MSC_VER + _Py_CheckPython3(); +#endif /* _MSC_VER */ + +// So we can adjust the separators in the path below +#define USE_UNICODE_WCHAR_CACHE 0 wchar_t *wpathname = PyUnicode_AsWideCharString(pathname, NULL); if (wpathname == NULL) return NULL; + // LoadLibraryExW only considers paths using backslashes as "fully qualified", + // and for example LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR doesn't work with forward slashes. + // https://github.com/msys2-contrib/cpython-mingw/issues/151 + for (size_t i = 0; wpathname[i] != L'\0'; ++i) { + if (wpathname[i] == L'/') { + wpathname[i] = L'\\'; + } + } + PyOS_snprintf(funcname, sizeof(funcname), "%.20s_%.200s", prefix, shortname); { @@ -231,9 +265,7 @@ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix, AddDllDirectory function. We add SEARCH_DLL_LOAD_DIR to ensure DLLs adjacent to the PYD are preferred. */ Py_BEGIN_ALLOW_THREADS - hDLL = LoadLibraryExW(wpathname, NULL, - LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | - LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR); + hDLL = LoadLibraryExW(wpathname, NULL, load_library_flags); Py_END_ALLOW_THREADS PyMem_Free(wpathname); @@ -300,9 +332,9 @@ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix, PyOS_snprintf(buffer, sizeof(buffer), #ifdef _DEBUG - "python%d%d_d.dll", + DLL_NAME_FORMAT "_d.dll", #else - "python%d%d.dll", + DLL_NAME_FORMAT ".dll", #endif PY_MAJOR_VERSION,PY_MINOR_VERSION); import_python = GetPythonImport(hDLL); diff --git a/Python/fileutils.c b/Python/fileutils.c index 9529b14d377c60..f6d364c84fd0cd 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -1,4 +1,5 @@ #include "Python.h" +#include "iscygpty.h" #include "pycore_fileutils.h" // fileutils definitions #include "pycore_runtime.h" // _PyRuntime #include "osdefs.h" // SEP @@ -80,7 +81,7 @@ _Py_device_encoding(int fd) int valid; Py_BEGIN_ALLOW_THREADS _Py_BEGIN_SUPPRESS_IPH - valid = isatty(fd); + valid = isatty(fd) || is_cygpty(fd); _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS if (!valid) @@ -1930,12 +1931,12 @@ _Py_write_impl(int fd, const void *buf, size_t count, int gil_held) depending on heap usage). */ if (gil_held) { Py_BEGIN_ALLOW_THREADS - if (isatty(fd)) { + if (isatty(fd) || is_cygpty(fd)) { count = 32767; } Py_END_ALLOW_THREADS } else { - if (isatty(fd)) { + if (isatty(fd) || is_cygpty(fd)) { count = 32767; } } @@ -2148,19 +2149,31 @@ int _Py_isabs(const wchar_t *path) { #ifdef MS_WINDOWS + // create a copy of path and replace all forward slashes with backslashes + // pathccskiproot does not handle forward slashes + wchar_t *path_copy = _wcsdup(path); + if (path_copy == NULL) { + return 0; + } + Py_NormalizeSepsPathcchW(path_copy); + const wchar_t *tail; - HRESULT hr = PathCchSkipRoot(path, &tail); - if (FAILED(hr) || path == tail) { + HRESULT hr = PathCchSkipRoot(path_copy, &tail); + if (FAILED(hr) || path_copy == tail) { + free(path_copy); return 0; } - if (tail == &path[1] && (path[0] == SEP || path[0] == ALTSEP)) { + if (tail == &path_copy[1] && (path_copy[0] == SEP || path_copy[0] == ALTSEP)) { // Exclude paths with leading SEP + free(path_copy); return 0; } - if (tail == &path[2] && path[1] == L':') { + if (tail == &path_copy[2] && path_copy[1] == L':') { // Exclude drive-relative paths (e.g. C:filename.ext) + free(path_copy); return 0; } + free(path_copy); return 1; #else return (path[0] == SEP); @@ -2193,7 +2206,11 @@ _Py_abspath(const wchar_t *path, wchar_t **abspath_p) } #ifdef MS_WINDOWS - return _PyOS_getfullpathname(path, abspath_p); + if (_PyOS_getfullpathname(path, abspath_p) < 0){ + return -1; + } + *abspath_p = _Py_normpath(*abspath_p, -1); + return 0; #else wchar_t cwd[MAXPATHLEN + 1]; cwd[Py_ARRAY_LENGTH(cwd) - 1] = 0; @@ -2396,6 +2413,8 @@ join_relfile(wchar_t *buffer, size_t bufsize, const wchar_t *dirname, const wchar_t *relfile) { #ifdef MS_WINDOWS + Py_NormalizeSepsPathcchW(dirname); + Py_NormalizeSepsPathcchW(relfile); if (FAILED(PathCchCombineEx(buffer, bufsize, dirname, relfile, PATHCCH_ALLOW_LONG_PATHS))) { return -1; @@ -2498,11 +2517,16 @@ _Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize) wchar_t *minP2 = path; // the beginning of the destination range wchar_t lastC = L'\0'; // the last ljusted character, p2[-1] in most cases + const wchar_t sep = Py_GetSepW(NULL); +#ifdef ALTSEP + const wchar_t altsep = Py_GetAltSepW(NULL); +#endif + #define IS_END(x) (pEnd ? (x) == pEnd : !*(x)) #ifdef ALTSEP -#define IS_SEP(x) (*(x) == SEP || *(x) == ALTSEP) +#define IS_SEP(x) (*(x) == sep || *(x) == altsep) #else -#define IS_SEP(x) (*(x) == SEP) +#define IS_SEP(x) (*(x) == sep) #endif #define SEP_OR_END(x) (IS_SEP(x) || IS_END(x)) @@ -2515,15 +2539,15 @@ _Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize) p2 = p1; #else for (; p2 < p1; ++p2) { - if (*p2 == ALTSEP) { - *p2 = SEP; + if (*p2 == altsep) { + *p2 = sep; } } #endif minP2 = p2 - 1; lastC = *minP2; #ifdef MS_WINDOWS - if (lastC != SEP) { + if (lastC != sep) { minP2++; } #endif @@ -2532,8 +2556,8 @@ _Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize) // Skip leading '.\' lastC = *++p1; #ifdef ALTSEP - if (lastC == ALTSEP) { - lastC = SEP; + if (lastC == altsep) { + lastC = sep; } #endif while (IS_SEP(p1)) { @@ -2545,18 +2569,18 @@ _Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize) for (; !IS_END(p1); ++p1) { wchar_t c = *p1; #ifdef ALTSEP - if (c == ALTSEP) { - c = SEP; + if (c == altsep) { + c = sep; } #endif - if (lastC == SEP) { + if (lastC == sep) { if (c == L'.') { int sep_at_1 = SEP_OR_END(&p1[1]); int sep_at_2 = !sep_at_1 && SEP_OR_END(&p1[2]); if (sep_at_2 && p1[1] == L'.') { wchar_t *p3 = p2; - while (p3 != minP2 && *--p3 == SEP) { } - while (p3 != minP2 && *(p3 - 1) != SEP) { --p3; } + while (p3 != minP2 && *--p3 == sep) { } + while (p3 != minP2 && *(p3 - 1) != sep) { --p3; } if (p2 == minP2 || (p3[0] == L'.' && p3[1] == L'.' && IS_SEP(&p3[2]))) { @@ -2565,7 +2589,7 @@ _Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize) *p2++ = L'.'; *p2++ = L'.'; lastC = L'.'; - } else if (p3[0] == SEP) { + } else if (p3[0] == sep) { // Absolute path, so absorb segment p2 = p3 + 1; } else { @@ -2576,7 +2600,7 @@ _Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize) } else { *p2++ = lastC = c; } - } else if (c == SEP) { + } else if (c == sep) { } else { *p2++ = lastC = c; } @@ -2586,7 +2610,7 @@ _Py_normpath_and_size(wchar_t *path, Py_ssize_t size, Py_ssize_t *normsize) } *p2 = L'\0'; if (p2 != minP2) { - while (--p2 != minP2 && *p2 == SEP) { + while (--p2 != minP2 && *p2 == sep) { *p2 = L'\0'; } } else { diff --git a/Python/frozenmain.c b/Python/frozenmain.c index ec4566bd4f84bc..cce7c9d2c8ce07 100644 --- a/Python/frozenmain.c +++ b/Python/frozenmain.c @@ -8,6 +8,7 @@ # include // isatty() #endif +#include "iscygpty.h" #ifdef MS_WINDOWS extern void PyWinFreeze_ExeInit(void); @@ -82,7 +83,7 @@ Py_FrozenMain(int argc, char **argv) sts = 0; } - if (inspect && isatty((int)fileno(stdin))) { + if (inspect && (isatty((int)fileno(stdin)) || is_cygpty((int)fileno(stdin)))) sts = PyRun_AnyFile(stdin, "") != 0; } diff --git a/Python/getcompiler.c b/Python/getcompiler.c index a5d26239e8772e..dd4d3c8f101468 100644 --- a/Python/getcompiler.c +++ b/Python/getcompiler.c @@ -7,10 +7,45 @@ // Note the __clang__ conditional has to come before the __GNUC__ one because // clang pretends to be GCC. -#if defined(__clang__) +#if defined(__clang__) && !defined(_WIN32) #define COMPILER "[Clang " __clang_version__ "]" #elif defined(__GNUC__) -#define COMPILER "[GCC " __VERSION__ "]" +/* To not break compatibility with things that determine + CPU arch by calling get_build_version in msvccompiler.py + (such as NumPy) add "32 bit" or "64 bit (AMD64)" on Windows + and also use a space as a separator rather than a newline. */ +#if defined(_WIN32) +#define COMP_SEP " " +#if defined(__x86_64__) +#define ARCH_SUFFIX " 64 bit (AMD64)" +#elif defined(__aarch64__) +#define ARCH_SUFFIX " 64 bit (ARM64)" +#elif defined(__arm__) +#define ARCH_SUFFIX " 32 bit (ARM)" +#else +#define ARCH_SUFFIX " 32 bit" +#endif +#else +#define COMP_SEP "\n" +#define ARCH_SUFFIX "" +#endif +#if defined(__clang__) +#define str(x) #x +#define xstr(x) str(x) +#if defined(_UCRT) +#define COMPILER COMP_SEP "[GCC UCRT Clang " xstr(__clang_major__) "." \ + xstr(__clang_minor__) "." xstr(__clang_patchlevel__) ARCH_SUFFIX "]" +#else +#define COMPILER COMP_SEP "[GCC Clang " xstr(__clang_major__) "." \ + xstr(__clang_minor__) "." xstr(__clang_patchlevel__) ARCH_SUFFIX "]" +#endif +#else +#if defined(_UCRT) +#define COMPILER COMP_SEP "[GCC UCRT " __VERSION__ ARCH_SUFFIX "]" +#else +#define COMPILER COMP_SEP "[GCC " __VERSION__ ARCH_SUFFIX "]" +#endif +#endif // Generic fallbacks. #elif defined(__cplusplus) #define COMPILER "[C++]" diff --git a/Python/initconfig.c b/Python/initconfig.c index 84717b4e3c934b..2451e05a368308 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -300,7 +300,7 @@ static const char usage_envvars[] = "PYTHONWARNINGS : warning control (-W)\n" ; -#if defined(MS_WINDOWS) +#if defined(_MSC_VER) # define PYTHONHOMEHELP "\\python{major}{minor}" #else # define PYTHONHOMEHELP "/lib/pythonX.X" diff --git a/Python/iscygpty.c b/Python/iscygpty.c new file mode 100644 index 00000000000000..722f88f2f46dcb --- /dev/null +++ b/Python/iscygpty.c @@ -0,0 +1,185 @@ +/* + * iscygpty.c -- part of ptycheck + * https://github.com/k-takata/ptycheck + * + * Copyright (c) 2015-2017 K.Takata + * + * You can redistribute it and/or modify it under the terms of either + * the MIT license (as described below) or the Vim license. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef _WIN32 + +#include +#include +#include +#include + +#ifdef USE_FILEEXTD +/* VC 7.1 or earlier doesn't support SAL. */ +# if !defined(_MSC_VER) || (_MSC_VER < 1400) +# define __out +# define __in +# define __in_opt +# endif +/* Win32 FileID API Library: + * http://www.microsoft.com/en-us/download/details.aspx?id=22599 + * Needed for WinXP. */ +# include +#else /* USE_FILEEXTD */ +/* VC 8 or earlier. */ +# if defined(_MSC_VER) && (_MSC_VER < 1500) +# ifdef ENABLE_STUB_IMPL +# define STUB_IMPL +# else +# error "Win32 FileID API Library is required for VC2005 or earlier." +# endif +# endif +#endif /* USE_FILEEXTD */ + + +#include "iscygpty.h" + +//#define USE_DYNFILEID +#ifdef USE_DYNFILEID +typedef BOOL (WINAPI *pfnGetFileInformationByHandleEx)( + HANDLE hFile, + FILE_INFO_BY_HANDLE_CLASS FileInformationClass, + LPVOID lpFileInformation, + DWORD dwBufferSize +); +static pfnGetFileInformationByHandleEx pGetFileInformationByHandleEx = NULL; + +# ifndef USE_FILEEXTD +static BOOL WINAPI stub_GetFileInformationByHandleEx( + HANDLE hFile, + FILE_INFO_BY_HANDLE_CLASS FileInformationClass, + LPVOID lpFileInformation, + DWORD dwBufferSize + ) +{ + return FALSE; +} +# endif + +static void setup_fileid_api(void) +{ + if (pGetFileInformationByHandleEx != NULL) { + return; + } + pGetFileInformationByHandleEx = (pfnGetFileInformationByHandleEx) + GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), + "GetFileInformationByHandleEx"); + if (pGetFileInformationByHandleEx == NULL) { +# ifdef USE_FILEEXTD + pGetFileInformationByHandleEx = GetFileInformationByHandleEx; +# else + pGetFileInformationByHandleEx = stub_GetFileInformationByHandleEx; +# endif + } +} +#else +# define pGetFileInformationByHandleEx GetFileInformationByHandleEx +# define setup_fileid_api() +#endif + + +#define is_wprefix(s, prefix) \ + (wcsncmp((s), (prefix), sizeof(prefix) / sizeof(WCHAR) - 1) == 0) + +/* Check if the fd is a cygwin/msys's pty. */ +int is_cygpty(int fd) +{ +#ifdef STUB_IMPL + return 0; +#else + HANDLE h; + int size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * (MAX_PATH - 1); + FILE_NAME_INFO *nameinfo; + WCHAR *p = NULL; + + setup_fileid_api(); + + h = (HANDLE) _get_osfhandle(fd); + if (h == INVALID_HANDLE_VALUE) { + return 0; + } + /* Cygwin/msys's pty is a pipe. */ + if (GetFileType(h) != FILE_TYPE_PIPE) { + return 0; + } + nameinfo = malloc(size + sizeof(WCHAR)); + if (nameinfo == NULL) { + return 0; + } + /* Check the name of the pipe: + * '\{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master' */ + if (pGetFileInformationByHandleEx(h, FileNameInfo, nameinfo, size)) { + nameinfo->FileName[nameinfo->FileNameLength / sizeof(WCHAR)] = L'\0'; + p = nameinfo->FileName; + if (is_wprefix(p, L"\\cygwin-")) { /* Cygwin */ + p += 8; + } else if (is_wprefix(p, L"\\msys-")) { /* MSYS and MSYS2 */ + p += 6; + } else { + p = NULL; + } + if (p != NULL) { + while (*p && isxdigit(*p)) /* Skip 16-digit hexadecimal. */ + ++p; + if (is_wprefix(p, L"-pty")) { + p += 4; + } else { + p = NULL; + } + } + if (p != NULL) { + while (*p && isdigit(*p)) /* Skip pty number. */ + ++p; + if (is_wprefix(p, L"-from-master")) { + //p += 12; + } else if (is_wprefix(p, L"-to-master")) { + //p += 10; + } else { + p = NULL; + } + } + } + free(nameinfo); + return (p != NULL); +#endif /* STUB_IMPL */ +} + +/* Check if at least one cygwin/msys pty is used. */ +int is_cygpty_used(void) +{ + int fd, ret = 0; + + for (fd = 0; fd < 3; fd++) { + ret |= is_cygpty(fd); + } + return ret; +} + +#endif /* _WIN32 */ + +/* vim: set ts=4 sw=4: */ diff --git a/Python/pathconfig.c b/Python/pathconfig.c index 33abaddc1b5df4..69b751fc22c774 100644 --- a/Python/pathconfig.c +++ b/Python/pathconfig.c @@ -8,7 +8,7 @@ #include #include "marshal.h" // PyMarshal_ReadObjectFromString -#include "osdefs.h" // DELIM +#include "osdefs.h" // DELIM, SEP #ifdef MS_WINDOWS # include // GetFullPathNameW(), MAX_PATH @@ -17,6 +17,159 @@ #endif +#ifdef __MINGW32__ +#define wcstok wcstok_s +#include +#endif + +static int +Py_StartsWithA(const char * str, const char * prefix) +{ + while(*prefix) + { + if(*prefix++ != *str++) + return 0; + } + + return 1; +} + +static int +Py_StartsWithW(const wchar_t * str, const wchar_t * prefix) +{ + while(*prefix) + { + if(*prefix++ != *str++) + return 0; + } + + return 1; +} + +char +Py_GetSepA(const char *name) +{ + static char sep = '\0'; +#ifdef _WIN32 + /* https://msdn.microsoft.com/en-gb/library/windows/desktop/aa365247%28v=vs.85%29.aspx + * The "\\?\" prefix .. indicate that the path should be passed to the system with minimal + * modification, which means that you cannot use forward slashes to represent path separators + */ + if (name != NULL && Py_StartsWithA(name, "\\\\?\\") != 0) + { + return '\\'; + } +#endif + if (sep != '\0') + return sep; +#if defined(__MINGW32__) + char* msystem = getenv("MSYSTEM"); + if (msystem != NULL && strcmp(msystem, "") != 0) + sep = '/'; + else + sep = '\\'; +#else + sep = SEP; +#endif + return sep; +} + +static char +Py_GetAltSepA(const char *name) +{ + char sep = Py_GetSepA(name); + if (sep == '/') + return '\\'; + return '/'; +} + +void +Py_NormalizeSepsA(char *name) +{ + assert(name != NULL); + char sep = Py_GetSepA(name); + char altsep = Py_GetAltSepA(name); + char* seps; + if (name[0] != '\0' && name[1] == ':') { + name[0] = toupper(name[0]); + } + seps = strchr(name, altsep); + while(seps) { + *seps = sep; + seps = strchr(seps, altsep); + } +} + +wchar_t +Py_GetSepW(const wchar_t *name) +{ + static wchar_t sep = L'\0'; +#ifdef _WIN32 + /* https://msdn.microsoft.com/en-gb/library/windows/desktop/aa365247%28v=vs.85%29.aspx + * The "\\?\" prefix .. indicate that the path should be passed to the system with minimal + * modification, which means that you cannot use forward slashes to represent path separators + */ + if (name != NULL && Py_StartsWithW(name, L"\\\\?\\") != 0) + { + return L'\\'; + } +#endif + if (sep != L'\0') + return sep; +#if defined(__MINGW32__) + char* msystem = getenv("MSYSTEM"); + if (msystem != NULL && strcmp(msystem, "") != 0) + sep = L'/'; + else + sep = L'\\'; +#else + sep = SEP; +#endif + return sep; +} + +wchar_t +Py_GetAltSepW(const wchar_t *name) +{ + char sep = Py_GetSepW(name); + if (sep == L'/') + return L'\\'; + return L'/'; +} + +void +Py_NormalizeSepsW(wchar_t *name) +{ + assert(name != NULL); + wchar_t sep = Py_GetSepW(name); + wchar_t altsep = Py_GetAltSepW(name); + wchar_t* seps; + if (name[0] != L'\0' && name[1] == L':') { + name[0] = towupper(name[0]); + } + seps = wcschr(name, altsep); + while(seps) { + *seps = sep; + seps = wcschr(seps, altsep); + } +} + +void +Py_NormalizeSepsPathcchW(wchar_t *name) +{ +#ifdef MS_WINDOWS + assert(name != NULL); + wchar_t sep = '\\'; + wchar_t altsep = '/'; + wchar_t* seps; + seps = wcschr(name, altsep); + while(seps) { + *seps = sep; + seps = wcschr(seps, altsep); + } +#endif +} + /* External interface */ /* Stored values set by C API functions */ @@ -486,7 +639,7 @@ _PyPathConfig_ComputeSysPath0(const PyWideStringList *argv, PyObject **path0_p) } #endif /* All others */ - PyObject *path0_obj = PyUnicode_FromWideChar(path0, n); + PyObject *path0_obj = PyUnicode_FromWideChar(_Py_normpath(path0, -1), n); if (path0_obj == NULL) { return -1; } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 8fe5bb8b3007d9..3dfa8afed00d50 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -36,6 +36,7 @@ #include "opcode.h" +#include "iscygpty.h" #include // setlocale() #include // getenv() #ifdef HAVE_UNISTD_H @@ -3422,7 +3423,7 @@ Py_Exit(int sts) int Py_FdIsInteractive(FILE *fp, const char *filename) { - if (isatty(fileno(fp))) { + if (isatty(fileno(fp)) || is_cygpty(fileno(fp))) { return 1; } if (!_Py_GetConfig()->interactive) { @@ -3437,7 +3438,7 @@ Py_FdIsInteractive(FILE *fp, const char *filename) int _Py_FdIsInteractive(FILE *fp, PyObject *filename) { - if (isatty(fileno(fp))) { + if (isatty(fileno(fp)) || is_cygpty(fileno(fp))) { return 1; } if (!_Py_GetConfig()->interactive) { diff --git a/Python/pytime.c b/Python/pytime.c index 560aea33f201a0..f4b6a7aede289c 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -1,5 +1,4 @@ #include "Python.h" -#include "pycore_time.h" // PyTime_t #include // gmtime_r() #ifdef HAVE_SYS_TIME_H @@ -9,6 +8,8 @@ # include // struct timeval #endif +#include "pycore_time.h" // PyTime_t + #if defined(__APPLE__) # include // mach_absolute_time(), mach_timebase_info() diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 3f170fff156fcd..a5191df9537f9e 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -48,7 +48,7 @@ Data members: # include #endif /* MS_WINDOWS */ -#ifdef MS_COREDLL +#if defined(MS_WINDOWS) && defined(Py_ENABLE_SHARED) extern void *PyWin_DLLhModule; /* A string loaded from the DLL at startup: */ extern const char *PyWin_DLLVersionString; @@ -3518,7 +3518,7 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict) SET_SYS_FROM_STRING("byteorder", "little"); #endif -#ifdef MS_COREDLL +#if defined(MS_WINDOWS) && defined(Py_ENABLE_SHARED) SET_SYS("dllhandle", PyLong_FromVoidPtr(PyWin_DLLhModule)); SET_SYS_FROM_STRING("winver", PyWin_DLLVersionString); #endif diff --git a/Python/traceback.c b/Python/traceback.c index e819909b6045c3..1b07de1b12da0d 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -338,7 +338,7 @@ _Py_FindSourceFile(PyObject *filename, char* namebuf, size_t namelen, PyObject * filepath = PyBytes_AS_STRING(filebytes); /* Search tail of filename in sys.path before giving up */ - tail = strrchr(filepath, SEP); + tail = strrchr(filepath, Py_GetSepA(filepath)); if (tail == NULL) tail = filepath; else diff --git a/Tools/build/check_extension_modules.py b/Tools/build/check_extension_modules.py index 66b2a262e11c57..e94062dc719c74 100644 --- a/Tools/build/check_extension_modules.py +++ b/Tools/build/check_extension_modules.py @@ -61,6 +61,10 @@ "winsound", } +if sys.platform == "win32": + # Enable DLL loading from PATH. + os.environ["PYTHONLEGACYWINDOWSDLLLOADING"] = "1" + logger = logging.getLogger(__name__) diff --git a/configure.ac b/configure.ac index a764028e49fcfd..b2b0eb55a9cb2e 100644 --- a/configure.ac +++ b/configure.ac @@ -12,7 +12,7 @@ dnl # Set VERSION so we only need to edit in one place (i.e., here) m4_define([PYTHON_VERSION], [3.13]) -AC_PREREQ([2.71]) +AC_PREREQ([2.69]) AC_INIT([python],[PYTHON_VERSION],[https://github.com/python/cpython/issues/]) @@ -204,9 +204,11 @@ AC_SUBST([FREEZE_MODULE]) AC_SUBST([FREEZE_MODULE_DEPS]) AC_SUBST([PYTHON_FOR_BUILD_DEPS]) +NATIVE_PYTHON_SEARCH_PATH_MINGW=`echo $host | grep -Eq 'mingw*' && echo "$MINGW_PREFIX/bin" || echo $PATH` AC_CHECK_PROGS([PYTHON_FOR_REGEN], [python$PACKAGE_VERSION python3.13 python3.12 python3.11 python3.10 python3 python], - [python3]) + [python3], + [$NATIVE_PYTHON_SEARCH_PATH_MINGW]) AC_SUBST([PYTHON_FOR_REGEN]) AC_MSG_CHECKING([Python for regen version]) @@ -327,6 +329,9 @@ then *-*-cygwin*) ac_sys_system=Cygwin ;; + *-*-mingw*) + ac_sys_system=MINGW + ;; *-apple-ios*) ac_sys_system=iOS ;; @@ -366,6 +371,7 @@ then linux*) MACHDEP="linux";; cygwin*) MACHDEP="cygwin";; darwin*) MACHDEP="darwin";; + mingw*) MACHDEP="win32";; '') MACHDEP="unknown";; esac @@ -776,6 +782,9 @@ if test "$cross_compiling" = yes; then ;; wasm32-*-* | wasm64-*-*) _host_ident=$host_cpu + ;; + *-*-mingw*) + _host_ident= ;; *) # for now, limit cross builds to known configurations @@ -783,6 +792,14 @@ if test "$cross_compiling" = yes; then AC_MSG_ERROR([cross build not supported for $host]) esac _PYTHON_HOST_PLATFORM="$MACHDEP${_host_ident:+-$_host_ident}" + + case "$host_os" in + mingw*) + # As sys.platform() return 'win32' to build python and extantions + # we will use 'mingw' (in setup.py and etc.) + _PYTHON_HOST_PLATFORM=mingw + ;; + esac fi # Some systems cannot stand _XOPEN_SOURCE being defined at all; they @@ -901,6 +918,65 @@ then [Define to include mbstate_t for mbrtowc]) fi +# On 'semi-native' build systems (MSYS*/Cygwin targeting MinGW-w64) +# _sysconfigdata.py will contain paths that are correct only in the +# build environment. This means external modules will fail to build +# without setting up the same env and also that the build of Python +# itself will fail as the paths are not correct for the host tools. +# +# To work around these issues a set of _b2h variables are created: +# prefix_b2h, srcdir_b2h, abs_srcdir_b2h +# and abs_builddir_b2h +# .. where b2h stands for build to host. sysconfig.py replaces path +# prefixes matching the non-b2h versions with the b2h equivalents. +# +# (note this assumes the host compilers are native and *not* cross +# - in the 'semi-native' scenario only that is.) + +AC_DEFUN([ABS_PATH_HOST], +[$1=$(cd $$2 && pwd) + case $build_os in + mingw*) + case $host_os in + mingw*) $1=$(cd $$2 && pwd -W) ;; + *) ;; + esac + ;; + cygwin*) + case $host_os in + mingw*) $1=$(cygpath -w -m $$2) ;; + *) ;; + esac + ;; + esac +AC_SUBST([$1]) +]) + +AC_MSG_CHECKING(absolute host location of prefix) +ABS_PATH_HOST([prefix_b2h],[prefix]) +AC_MSG_RESULT([$prefix_b2h]) + +AC_MSG_CHECKING(absolute host location of srcdir) +ABS_PATH_HOST([srcdir_b2h],[srcdir]) +AC_MSG_RESULT([$srcdir_b2h]) + +AC_MSG_CHECKING(absolute host location of abs_srcdir) +ABS_PATH_HOST([abs_srcdir_b2h],[srcdir]) +AC_MSG_RESULT([$abs_srcdir_b2h]) + +my_builddir=. +AC_MSG_CHECKING(Absolute host location of abs_builddir) +ABS_PATH_HOST([abs_builddir_b2h],[my_builddir]) +AC_MSG_RESULT([$abs_builddir_b2h]) + +AC_MSG_CHECKING([for init system calls]) +AC_SUBST(INITSYS) +case $host in + *-*-mingw*) INITSYS=nt;; + *) INITSYS=posix;; +esac +AC_MSG_RESULT([$INITSYS]) + # Record the configure-time value of MACOSX_DEPLOYMENT_TARGET, # it may influence the way we can build extensions, so distutils # needs to check it @@ -1218,6 +1294,28 @@ AC_CACHE_CHECK([for -Wl,--no-as-needed], [ac_cv_wl_no_as_needed], [ ]) AC_SUBST([NO_AS_NEEDED]) +# initialize default configuration +py_config= +case $host in + *-*-mingw*) py_config=mingw ;; +esac +if test -n "$py_config" ; then + AC_MSG_NOTICE([loading configure defaults from .../Misc/config_$py_config"]) + . "$srcdir/Misc/config_$py_config" +fi + +# initialize defaults for cross-builds +if test "$cross_compiling" = yes; then + py_config=$host_os + case $py_config in + mingw32*) py_config=mingw32 ;; + esac + if test -f "$srcdir/Misc/cross_$py_config" ; then + AC_MSG_NOTICE([loading cross defaults from .../Misc/cross_$py_config"]) + . "$srcdir/Misc/cross_$py_config" + fi +fi + AC_MSG_CHECKING([for the Android API level]) cat > conftest.c <]],[[_beginthread(0, 0, 0);]]) + ], + [AC_MSG_RESULT([yes])], + [AC_MSG_ERROR([failed to link with nt-threads])]) +fi + +if test $with_nt_threads = yes ; then + dnl temporary default flag to avoid additional pthread checks + dnl and initilize other ac..thread flags to no + ac_cv_pthread_is_default=no + ac_cv_kthread=no + ac_cv_pthread=no + dnl ac_cv_kpthread is set to no if default is yes (see below) +else # On some compilers, pthreads are available without further options # (e.g. MacOS X). On some of these systems, the compiler will not # complain if unaccepted options are passed (e.g. gcc on Mac OS X). @@ -2922,6 +3092,8 @@ int main(void){ CC="$ac_save_cc"]) fi +fi + # If we have set a CC compiler flag for thread support then # check if it works for CXX, too. if test ! -z "$CXX" @@ -2941,6 +3113,10 @@ elif test "$ac_cv_pthread" = "yes" then CXX="$CXX -pthread" ac_cv_cxx_thread=yes +elif test "$with_nt_threads" = "yes" +then + dnl set to always to skip extra pthread check below + ac_cv_cxx_thread=always else ac_cv_cxx_thread=no fi @@ -2979,11 +3155,11 @@ AC_DEFINE([STDC_HEADERS], [1], # checks for header files AC_CHECK_HEADERS([ \ - alloca.h asm/types.h bluetooth.h conio.h direct.h dlfcn.h endian.h errno.h fcntl.h grp.h \ + alloca.h asm/types.h bluetooth.h conio.h direct.h endian.h errno.h fcntl.h grp.h \ io.h langinfo.h libintl.h libutil.h linux/auxvec.h sys/auxv.h linux/fs.h linux/limits.h linux/memfd.h \ linux/random.h linux/soundcard.h \ - linux/tipc.h linux/wait.h netdb.h net/ethernet.h netinet/in.h netpacket/packet.h poll.h process.h pthread.h pty.h \ - sched.h setjmp.h shadow.h signal.h spawn.h stropts.h sys/audioio.h sys/bsdtty.h sys/devpoll.h \ + linux/tipc.h linux/wait.h netdb.h net/ethernet.h netinet/in.h netpacket/packet.h poll.h process.h pty.h \ + setjmp.h shadow.h signal.h spawn.h stropts.h sys/audioio.h sys/bsdtty.h sys/devpoll.h \ sys/endian.h sys/epoll.h sys/event.h sys/eventfd.h sys/file.h sys/ioctl.h sys/kern_control.h \ sys/loadavg.h sys/lock.h sys/memfd.h sys/mkdev.h sys/mman.h sys/modem.h sys/param.h sys/poll.h \ sys/random.h sys/resource.h sys/select.h sys/sendfile.h sys/socket.h sys/soundcard.h sys/stat.h \ @@ -2991,9 +3167,24 @@ AC_CHECK_HEADERS([ \ sys/types.h sys/uio.h sys/un.h sys/utsname.h sys/wait.h sys/xattr.h sysexits.h syslog.h \ termios.h util.h utime.h utmp.h \ ]) + +case $host in + *-*-mingw*) ;; + *) AC_CHECK_HEADERS([dlfcn.h]);; +esac + + AC_HEADER_DIRENT AC_HEADER_MAJOR +# If using nt threads, don't look for pthread.h or thread.h +if test "x$with_nt_threads" = xno ; then +AC_HEADER_STDC +AC_CHECK_HEADERS(pthread.h sched.h thread.h) +AC_HEADER_DIRENT +AC_HEADER_MAJOR +fi + # bluetooth/bluetooth.h has been known to not compile with -std=c99. # http://permalink.gmane.org/gmane.linux.bluez.kernel/22294 SAVE_CFLAGS=$CFLAGS @@ -3145,10 +3336,17 @@ AC_CHECK_SIZEOF([double], [8]) AC_CHECK_SIZEOF([fpos_t], [4]) AC_CHECK_SIZEOF([size_t], [4]) AC_CHECK_ALIGNOF([size_t]) -AC_CHECK_SIZEOF([pid_t], [4]) AC_CHECK_SIZEOF([uintptr_t]) AC_CHECK_ALIGNOF([max_align_t]) +case $host_os in + mingw*) ;; + *) + AC_CHECK_SIZEOF([pid_t], [4]) + ;; +esac + + AC_TYPE_LONG_DOUBLE AC_CHECK_SIZEOF([long double], [16]) @@ -3171,6 +3369,10 @@ dnl LFS does not work with Emscripten 3.1 AS_CASE([$ac_sys_system], [Emscripten], [have_largefile_support="no"] ) +dnl Activate on windows platforms (32&64-bit) where off_t(4) < fpos_t(8) +AS_CASE([$ac_sys_system], + [MINGW], [have_largefile_support="yes"] +) AS_VAR_IF([have_largefile_support], [yes], [ AC_DEFINE([HAVE_LARGEFILE_SUPPORT], [1], [Defined to enable large file support when an off_t is bigger than a long @@ -3201,6 +3403,10 @@ elif test "$ac_cv_pthread" = "yes" then CC="$CC -pthread" fi +if test $with_nt_threads = yes ; then + dnl skip check for pthread_t if NT-thread model is enabled + ac_cv_have_pthread_t=skip +else AC_CACHE_CHECK([for pthread_t], [ac_cv_have_pthread_t], [ AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[@%:@include ]], [[pthread_t x; x = *(pthread_t*)0;]]) @@ -3232,7 +3438,7 @@ AS_VAR_IF([ac_cv_pthread_key_t_is_arithmetic_type], [yes], [ AC_DEFINE([PTHREAD_KEY_T_IS_COMPATIBLE_WITH_INT], [1], [Define if pthread_key_t is compatible with int.]) ]) - +fi CC="$ac_save_cc" AC_MSG_CHECKING([for --enable-framework]) @@ -3387,6 +3593,9 @@ if test -z "$SHLIB_SUFFIX"; then CYGWIN*) SHLIB_SUFFIX=.dll;; *) SHLIB_SUFFIX=.so;; esac + case $host_os in + mingw*) SHLIB_SUFFIX=.pyd;; + esac fi AC_MSG_RESULT([$SHLIB_SUFFIX]) @@ -3521,6 +3730,10 @@ then CYGWIN*) LDSHARED="gcc -shared -Wl,--enable-auto-image-base" LDCXXSHARED="g++ -shared -Wl,--enable-auto-image-base";; + MINGW*) + LDSHARED='$(CC) -shared -Wl,--enable-auto-image-base' + LDCXXSHARED='$(CXX) -shared -Wl,--enable-auto-image-base' + ;; *) LDSHARED="ld";; esac fi @@ -3647,6 +3860,11 @@ then VxWorks*) LINKFORSHARED='-Wl,-export-dynamic';; esac + case $host in + *-*-mingw*) + # for https://bugs.python.org/issue40458 on MINGW + LINKFORSHARED="-Wl,--stack,4194304";; + esac fi AC_MSG_RESULT([$LINKFORSHARED]) @@ -3711,7 +3929,12 @@ AC_SUBST([PERF_TRAMPOLINE_OBJ]) # checks for libraries AC_CHECK_LIB([sendfile], [sendfile]) -AC_CHECK_LIB([dl], [dlopen]) # Dynamic linking for SunOS/Solaris and SYSV + +case $host in + *-*-mingw*) ;; + *) AC_CHECK_LIB([dl], [dlopen]) ;; # Dynamic linking for SunOS/Solaris and SYSV +esac + AC_CHECK_LIB([dld], [shl_load]) # Dynamic linking for HP-UX @@ -3772,16 +3995,30 @@ AS_VAR_IF([have_uuid], [missing], [ AS_VAR_IF([have_uuid], [missing], [have_uuid=no]) +if test $with_nt_threads = yes ; then + dnl do not search for sem_init if NT-thread model is enabled + : +else # 'Real Time' functions on Solaris # posix4 on Solaris 2.6 # pthread (first!) on Linux AC_SEARCH_LIBS([sem_init], [pthread rt posix4]) +fi # check if we need libintl for locale functions +case $host in + *-*-mingw*) + dnl Native windows build don't use libintl (see _localemodule.c). + dnl Also we don't like setup.py to add "intl" library to the list + dnl when build _locale module. + ;; + *) AC_CHECK_LIB([intl], [textdomain], [AC_DEFINE([WITH_LIBINTL], [1], [Define to 1 if libintl is needed for locale functions.]) LIBS="-lintl $LIBS"]) + ;; +esac # checks for system dependent C++ extensions support case "$ac_sys_system" in @@ -4162,6 +4399,36 @@ AC_SUBST([LIBMPDEC_CFLAGS]) AC_SUBST([LIBMPDEC_INTERNAL]) +dnl On MINGW, you need to link against ws2_32 and iphlpapi for sockets to work +AS_CASE([$MACHDEP], + [win32], [SOCKET_LIBS="-lws2_32 -liphlpapi -lrpcrt4"], + [SOCKET_LIBS=""] +) + +dnl On MINGW, you need to link againt ws2_32 for _multiprocessing +AS_CASE([$MACHDEP], + [win32], [MULTIPROCESSING_LIBS="-lws2_32"], + [MULTIPROCESSING_LIBS=""] +) + +dnl On MINGW, you need to link againt ws2_32 for selectmodule +AS_CASE([$MACHDEP], + [win32], [SELECTMODULE_LIBS="-lws2_32"], + [SELECTMODULE_LIBS=""] +) + +dnl On MINGW, you need to link againt ole32, oleaut32 and uuid for ctypes +AS_CASE([$MACHDEP], + [win32], [CTYPES_LIBS="-lole32 -loleaut32 -luuid"], + [CTYPES_LIBS=""] +) + +dnl On MINGW, you need to link againt rpcrt4 for _uuid +AS_CASE([$MACHDEP], + [win32], [UUID_LIBS="-lrpcrt4"], + [UUID_LIBS=""] +) + dnl detect sqlite3 from Emscripten emport PY_CHECK_EMSCRIPTEN_PORT([LIBSQLITE3], [-sUSE_SQLITE3]) @@ -4476,6 +4743,18 @@ done IFS=$as_save_IFS AC_MSG_RESULT([$DBM_CFLAGS $DBM_LIBS]) +case $host in + *-*-mingw*) + CFLAGS_NODIST="$CFLAGS_NODIST -D_WIN32_WINNT=0x0602";; +esac + +# Determine if windows modules should be used. +AC_SUBST(USE_WIN32_MODULE) +USE_WIN32_MODULE='#' +case $host in + *-*-mingw*) USE_WIN32_MODULE=;; +esac + # Templates for things AC_DEFINEd more than once. # For a single AC_DEFINE, no template is needed. AH_TEMPLATE([_REENTRANT], @@ -4510,6 +4789,11 @@ then CXX="$CXX -pthread" fi posix_threads=yes +elif test $with_nt_threads = yes +then + posix_threads=no + AC_DEFINE(NT_THREADS, 1, + [Define to 1 if you want to use native NT threads]) else if test ! -z "$withval" -a -d "$withval" then LDFLAGS="$LDFLAGS -L$withval" @@ -4931,11 +5215,14 @@ AC_MSG_RESULT([$with_freelists]) AC_MSG_CHECKING([for --with-c-locale-coercion]) AC_ARG_WITH( [c-locale-coercion], - [AS_HELP_STRING([--with-c-locale-coercion], [enable C locale coercion to a UTF-8 based locale (default is yes)])]) + [AS_HELP_STRING([--with-c-locale-coercion], [enable C locale coercion to a UTF-8 based locale (default is yes on Unix, no on Windows)])]) if test -z "$with_c_locale_coercion" then - with_c_locale_coercion="yes" + case $host in + *-*-mingw*) with_c_locale_coercion="no";; + *) with_c_locale_coercion="yes";; + esac fi if test "$with_c_locale_coercion" != "no" then @@ -5040,12 +5327,36 @@ then fi ;; esac + case $host in + *-*-mingw*) + DYNLOADFILE="dynload_win.o" + extra_machdep_objs="$extra_machdep_objs PC/dl_nt.o" + CFLAGS_NODIST="$CFLAGS_NODIST -DPY3_DLLNAME='L\"$DLLLIBRARY\"'" + case $host in + i686*) + CFLAGS_NODIST="$CFLAGS_NODIST -DMS_DLL_ID='\"${VERSION}-32\"'" + ;; + armv7*) + CFLAGS_NODIST="$CFLAGS_NODIST -DMS_DLL_ID='\"${VERSION}-arm32\"'" + ;; + aarch64*) + CFLAGS_NODIST="$CFLAGS_NODIST -DMS_DLL_ID='\"${VERSION}-arm64\"'" + ;; + *) + CFLAGS_NODIST="$CFLAGS_NODIST -DMS_DLL_ID='\"$VERSION\"'" + ;; + esac + ;; + esac fi AC_MSG_RESULT([$DYNLOADFILE]) if test "$DYNLOADFILE" != "dynload_stub.o" then + have_dynamic_loading=yes AC_DEFINE([HAVE_DYNAMIC_LOADING], [1], [Defined when any dynamic module loading is enabled.]) +else + have_dynamic_loading=no fi # MACHDEP_OBJS can be set to platform-specific object files needed by Python @@ -5089,13 +5400,22 @@ if test "$ac_sys_system" = "Linux-android"; then fi # checks for library functions +if test $with_nt_threads = yes ; then + dnl GCC(mingw) 4.4+ require and use posix threads(pthreads-w32) + dnl and host may contain installed pthreads-w32. + dnl Skip checks for some functions declared in pthreads-w32 if + dnl NT-thread model is enabled. + ac_cv_func_pthread_kill=skip + ac_cv_func_sem_open=skip + ac_cv_func_sched_setscheduler=skip +fi AC_CHECK_FUNCS([ \ accept4 alarm bind_textdomain_codeset chmod chown clock closefrom close_range confstr \ copy_file_range ctermid dup dup3 execv explicit_bzero explicit_memset \ faccessat fchmod fchmodat fchown fchownat fdopendir fdwalk fexecve \ fork fork1 fpathconf fstatat ftime ftruncate futimens futimes futimesat \ gai_strerror getegid geteuid getgid getgrent getgrgid getgrgid_r \ - getgrnam_r getgrouplist gethostname getitimer getloadavg getlogin \ + getgrnam_r getgrouplist getitimer getloadavg getlogin \ getpeername getpgid getpid getppid getpriority _getpty \ getpwent getpwnam_r getpwuid getpwuid_r getresgid getresuid getrusage getsid getspent \ getspnam getuid getwd grantpt if_nameindex initgroups kill killpg lchown linkat \ @@ -5109,7 +5429,7 @@ AC_CHECK_FUNCS([ \ sched_setparam sched_setscheduler sem_clockwait sem_getvalue sem_open \ sem_timedwait sem_unlink sendfile setegid seteuid setgid sethostname \ setitimer setlocale setpgid setpgrp setpriority setregid setresgid \ - setresuid setreuid setsid setuid setvbuf shutdown sigaction sigaltstack \ + setresuid setreuid setsid setuid setvbuf sigaction sigaltstack \ sigfillset siginterrupt sigpending sigrelse sigtimedwait sigwait \ sigwaitinfo snprintf splice strftime strlcpy strsignal symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile \ @@ -5338,7 +5658,13 @@ PKG_CHECK_MODULES([LIBLZMA], [liblzma], [have_liblzma=yes], [ ]) dnl PY_CHECK_NETDB_FUNC(FUNCTION) -AC_DEFUN([PY_CHECK_NETDB_FUNC], [PY_CHECK_FUNC([$1], [@%:@include ])]) +AC_DEFUN([PY_CHECK_NETDB_FUNC], [PY_CHECK_FUNC([$1], [ +#ifdef _WIN32 + #include +#else + #include +#endif +])]) PY_CHECK_NETDB_FUNC([hstrerror]) dnl not available in WASI yet @@ -5347,13 +5673,19 @@ PY_CHECK_NETDB_FUNC([getservbyport]) PY_CHECK_NETDB_FUNC([gethostbyname]) PY_CHECK_NETDB_FUNC([gethostbyaddr]) PY_CHECK_NETDB_FUNC([getprotobyname]) +PY_CHECK_NETDB_FUNC([gethostname]) +PY_CHECK_NETDB_FUNC([shutdown]) dnl PY_CHECK_SOCKET_FUNC(FUNCTION) AC_DEFUN([PY_CHECK_SOCKET_FUNC], [PY_CHECK_FUNC([$1], [ +#ifdef _WIN32 +#include +#else #include #include #include #include +#endif ])]) PY_CHECK_SOCKET_FUNC([inet_aton]) @@ -5417,6 +5749,9 @@ AC_CHECK_FUNCS([setpgrp], # check for namespace functions AC_CHECK_FUNCS([setns unshare]) +case $host in + *-*-mingw*) ;; + *) AC_CHECK_FUNCS([clock_gettime], [], [ AC_CHECK_LIB([rt], [clock_gettime], [ LIBS="$LIBS -lrt" @@ -5455,6 +5790,9 @@ if ! { test "$ac_sys_system" = "Linux-android" && ]) fi + ;; +esac + AC_CHECK_FUNCS([nanosleep], [], [ AC_CHECK_LIB([rt], [nanosleep], [ AC_DEFINE([HAVE_NANOSLEEP], [1]) @@ -5645,18 +5983,33 @@ if test $ac_cv_header_time_altzone = yes; then [Define this if your time.h defines altzone.]) fi +AC_CHECK_HEADERS([ws2tcpip.h]) AC_CACHE_CHECK([for addrinfo], [ac_cv_struct_addrinfo], -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], [[struct addrinfo a]])], +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#ifdef HAVE_WS2TCPIP_H +# include +#else +# include +#endif]], + [[struct addrinfo a]])], [ac_cv_struct_addrinfo=yes], [ac_cv_struct_addrinfo=no])) if test $ac_cv_struct_addrinfo = yes; then - AC_DEFINE([HAVE_ADDRINFO], [1], [struct addrinfo (netdb.h)]) + AC_DEFINE([HAVE_ADDRINFO], [1], [struct addrinfo]) fi AC_CACHE_CHECK([for sockaddr_storage], [ac_cv_struct_sockaddr_storage], AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -# include -@%:@ include ]], [[struct sockaddr_storage s]])], +#ifdef HAVE_WS2TCPIP_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif]], + [[struct sockaddr_storage s]])], [ac_cv_struct_sockaddr_storage=yes], [ac_cv_struct_sockaddr_storage=no])) if test $ac_cv_struct_sockaddr_storage = yes; then @@ -5970,6 +6323,10 @@ dnl actually works. For FreeBSD versions <= 7.2, dnl the kernel module that provides POSIX semaphores dnl isn't loaded by default, so an attempt to call dnl sem_open results in a 'Signal 12' error. +if test $with_nt_threads = yes ; then + dnl skip posix semaphores test if NT-thread model is enabled + ac_cv_posix_semaphores_enabled=no +fi AC_CACHE_CHECK([whether POSIX semaphores are enabled], [ac_cv_posix_semaphores_enabled], AC_RUN_IFELSE([ AC_LANG_SOURCE([ @@ -6003,6 +6360,14 @@ AS_VAR_IF([ac_cv_posix_semaphores_enabled], [no], [ ]) dnl Multiprocessing check for broken sem_getvalue +if test $with_nt_threads = yes ; then + dnl Skip test if NT-thread model is enabled. + dnl NOTE the test case below fail for pthreads-w32 as: + dnl - SEM_FAILED is not defined; + dnl - sem_open is a stub; + dnl - sem_getvalue work(!). + ac_cv_broken_sem_getvalue=skip +fi AC_CACHE_CHECK([for broken sem_getvalue], [ac_cv_broken_sem_getvalue], AC_RUN_IFELSE([ AC_LANG_SOURCE([ @@ -6039,7 +6404,10 @@ AS_VAR_IF([ac_cv_broken_sem_getvalue], [yes], [ ) ]) -AC_CHECK_DECLS([RTLD_LAZY, RTLD_NOW, RTLD_GLOBAL, RTLD_LOCAL, RTLD_NODELETE, RTLD_NOLOAD, RTLD_DEEPBIND, RTLD_MEMBER], [], [], [[@%:@include ]]) +case $host in + *-*-mingw*) ;; + *) AC_CHECK_DECLS([RTLD_LAZY, RTLD_NOW, RTLD_GLOBAL, RTLD_LOCAL, RTLD_NODELETE, RTLD_NOLOAD, RTLD_DEEPBIND, RTLD_MEMBER], [], [], [[@%:@include ]]) +esac # determine what size digit to use for Python's longs AC_MSG_CHECKING([digit size for Python's longs]) @@ -6129,6 +6497,61 @@ esac # check for endianness AC_C_BIGENDIAN +AC_SUBST(PYD_PLATFORM_TAG) +# Special case of PYD_PLATFORM_TAG with python build with mingw. +# Python can with different cpu arch and c runtime as well as different +# toolchain. We follow this`mingw___` +# convention for PYD_PLATFORM_TAG. Where: +# `cpu_arch` = `x86_64`, `aarch64` or `i686` +# `c_runtime` = `msvcrt` or `ucrt` +# `toolchain` = `gnu` or `llvm` +PYD_PLATFORM_TAG="" +case $host in + *-*-mingw*) + # check if we are linking to ucrt + AC_MSG_CHECKING(whether linking to ucrt) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ + #include + #ifndef _UCRT + #error no ucrt + #endif + int main(){ return 0; } + ]])],[linking_to_ucrt=yes],[linking_to_ucrt=no]) + AC_MSG_RESULT($linking_to_ucrt) + ;; +esac +case $host_os in + mingw*) + AC_MSG_CHECKING(PYD_PLATFORM_TAG) + PYD_PLATFORM_TAG="mingw" + case $host in + i686-*-mingw*) + PYD_PLATFORM_TAG+="_i686" + ;; + x86_64-*-mingw*) + PYD_PLATFORM_TAG+="_x86_64" + ;; + aarch64-*-mingw*) + PYD_PLATFORM_TAG+="_aarch64" + ;; + armv7-*-mingw*) + PYD_PLATFORM_TAG+="_armv7" + ;; + esac + if test $linking_to_ucrt = no; then + PYD_PLATFORM_TAG+="_msvcrt" + else + PYD_PLATFORM_TAG+="_ucrt" + fi + if test -n "${cc_is_clang}"; then + # it is CLANG32 + PYD_PLATFORM_TAG+="_llvm" + else + PYD_PLATFORM_TAG+="_gnu" + fi + AC_MSG_RESULT($PYD_PLATFORM_TAG) +esac + # ABI version string for Python extension modules. This appears between the # periods in shared library file names, e.g. foo..so. It is calculated # from the following attributes which affect the ABI of this Python build (in @@ -6163,7 +6586,12 @@ if test "$Py_DEBUG" = 'true'; then fi AC_SUBST([EXT_SUFFIX]) -EXT_SUFFIX=.${SOABI}${SHLIB_SUFFIX} +VERSION_NO_DOTS=$(echo $LDVERSION | tr -d .) +if test -n "${PYD_PLATFORM_TAG}"; then + EXT_SUFFIX=".cp${VERSION_NO_DOTS}-${PYD_PLATFORM_TAG}${SHLIB_SUFFIX}" +else + EXT_SUFFIX=.${SOABI}${SHLIB_SUFFIX} +fi AC_MSG_CHECKING([LDVERSION]) LDVERSION='$(VERSION)$(ABIFLAGS)' @@ -6178,8 +6606,8 @@ AC_SUBST([LIBPYTHON]) MODULE_DEPS_SHARED='$(MODULE_DEPS_STATIC) $(EXPORTSYMS)' LIBPYTHON='' -# On Android and Cygwin the shared libraries must be linked with libpython. -if test "$PY_ENABLE_SHARED" = "1" && ( test -n "$ANDROID_API_LEVEL" || test "$MACHDEP" = "cygwin"); then +# On Android, Cygwin and MINGW the shared libraries must be linked with libpython. +if test "$PY_ENABLE_SHARED" = "1" && ( test -n "$ANDROID_API_LEVEL" || test "$MACHDEP" = "cygwin"|| test "$MACHDEP" = "win32"); then MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(LDLIBRARY)" LIBPYTHON="\$(BLDLIBRARY)" fi @@ -6829,7 +7257,10 @@ fi AC_CHECK_TYPES([socklen_t], [], [AC_DEFINE([socklen_t], [int], - [Define to 'int' if does not define.])], [ + [Define to 'int' if or does not define.])], [ +#ifdef HAVE_WS2TCPIP_H +#include +#endif #ifdef HAVE_SYS_TYPES_H #include #endif @@ -6923,6 +7354,27 @@ do THREADHEADERS="$THREADHEADERS \$(srcdir)/$h" done +case $host in + *-*-mingw*) + dnl Required for windows builds as Objects/exceptions.c require + dnl "errmap.h" from $srcdir/PC. + dnl Note we cannot use BASECPPFLAGS as autogenerated pyconfig.h + dnl has to be before customized located in ../PC. + dnl (-I. at end is workaround for setup.py logic) + CPPFLAGS="-I\$(srcdir)/PC $CPPFLAGS -I." + ;; +esac + +dnl Python interpreter main program for frozen scripts +AC_SUBST(PYTHON_OBJS_FROZENMAIN) +PYTHON_OBJS_FROZENMAIN="Python/frozenmain.o" +case $host in + *-*-mingw*) + dnl 'PC/frozen_dllmain.c' - not yet + PYTHON_OBJS_FROZENMAIN= + ;; +esac + AC_SUBST([SRCDIRS]) SRCDIRS="\ Modules \ @@ -6950,6 +7402,11 @@ SRCDIRS="\ Programs \ Python \ Python/frozen_modules" + +case $host in + *-*-mingw*) SRCDIRS="$SRCDIRS PC";; +esac + AC_MSG_CHECKING([for build directories]) for dir in $SRCDIRS; do if test ! -d $dir; then @@ -6958,6 +7415,38 @@ for dir in $SRCDIRS; do done AC_MSG_RESULT([done]) +# For mingw build need additional library for linking +case $host in + *-*-mingw*) + LIBS="$LIBS -lversion -lshlwapi -lpathcch -lbcrypt" + AC_PROG_AWK + if test "$AWK" = "gawk"; then + awk_extra_flag="--non-decimal-data" + fi + AC_MSG_CHECKING([FIELD3]) + FIELD3=$($AWK $awk_extra_flag '\ + /^#define PY_RELEASE_LEVEL_/ {levels[$2]=$3} \ + /^#define PY_MICRO_VERSION[[:space:]]+/ {micro=$3} \ + /^#define PY_RELEASE_LEVEL[[:space:]]+/ {level=levels[$3]} \ + /^#define PY_RELEASE_SERIAL[[:space:]]+/ {serial=$3} \ + END {print micro * 1000 + level * 10 + serial}' \ + $srcdir/Include/patchlevel.h + ) + + AC_MSG_RESULT([${FIELD3}]) + RCFLAGS="$RCFLAGS -DFIELD3=$FIELD3 -O COFF" + + case $host in + i686*) RCFLAGS="$RCFLAGS --target=pe-i386" ;; + x86_64*) RCFLAGS="$RCFLAGS --target=pe-x86-64" ;; + *) ;; + esac + ;; + *) + ;; +esac +AC_SUBST(RCFLAGS) + # Availability of -O2: AC_CACHE_CHECK([for -O2], [ac_cv_compile_o2], [ saved_cflags="$CFLAGS" @@ -7481,6 +7970,7 @@ AS_CASE([$ac_sys_system], ) ], [CYGWIN*], [PY_STDLIB_MOD_SET_NA([_scproxy])], + [MINGW*], [PY_STDLIB_MOD_SET_NA([readline])], [QNX*], [PY_STDLIB_MOD_SET_NA([_scproxy])], [FreeBSD*], [PY_STDLIB_MOD_SET_NA([_scproxy])], [Emscripten|WASI], [ @@ -7618,10 +8108,9 @@ PY_STDLIB_MOD_SIMPLE([_json]) PY_STDLIB_MOD_SIMPLE([_lsprof]) PY_STDLIB_MOD_SIMPLE([_opcode]) PY_STDLIB_MOD_SIMPLE([_pickle]) -PY_STDLIB_MOD_SIMPLE([_posixsubprocess]) PY_STDLIB_MOD_SIMPLE([_queue]) PY_STDLIB_MOD_SIMPLE([_random]) -PY_STDLIB_MOD_SIMPLE([select]) +PY_STDLIB_MOD_SIMPLE([select], [], [$SELECTMODULE_LIBS]) PY_STDLIB_MOD_SIMPLE([_struct]) PY_STDLIB_MOD_SIMPLE([_typing]) PY_STDLIB_MOD_SIMPLE([_interpreters]) @@ -7631,8 +8120,8 @@ PY_STDLIB_MOD_SIMPLE([_zoneinfo]) dnl multiprocessing modules PY_STDLIB_MOD([_multiprocessing], - [], [test "$ac_cv_func_sem_unlink" = "yes"], - [-I\$(srcdir)/Modules/_multiprocessing]) + [], [test "$ac_cv_func_sem_unlink" = "yes" -o "$MACHDEP" = "win32"], + [-I\$(srcdir)/Modules/_multiprocessing], [$MULTIPROCESSING_LIBS]) PY_STDLIB_MOD([_posixshmem], [], [test "$have_posix_shmem" = "yes"], [$POSIXSHMEM_CFLAGS], [$POSIXSHMEM_LIBS]) @@ -7650,11 +8139,15 @@ PY_STDLIB_MOD([fcntl], [], [test "$ac_cv_header_sys_ioctl_h" = "yes" -a "$ac_cv_header_fcntl_h" = "yes"], [], [$FCNTL_LIBS]) PY_STDLIB_MOD([mmap], - [], [test "$ac_cv_header_sys_mman_h" = "yes" -a "$ac_cv_header_sys_stat_h" = "yes"]) + [], m4_flatten([test "$ac_cv_header_sys_mman_h" = "yes" + -a "$ac_cv_header_sys_stat_h" = "yes" + -o "$MACHDEP" = "win32"])) PY_STDLIB_MOD([_socket], [], m4_flatten([test "$ac_cv_header_sys_socket_h" = "yes" -a "$ac_cv_header_sys_types_h" = "yes" - -a "$ac_cv_header_netinet_in_h" = "yes"])) + -a "$ac_cv_header_netinet_in_h" = "yes" + -o "$MACHDEP" = "win32"]), + [], [$SOCKET_LIBS]) dnl platform specific extensions PY_STDLIB_MOD([grp], [], @@ -7667,6 +8160,7 @@ PY_STDLIB_MOD([_scproxy], [], [-framework SystemConfiguration -framework CoreFoundation]) PY_STDLIB_MOD([syslog], [], [test "$ac_cv_header_syslog_h" = yes]) PY_STDLIB_MOD([termios], [], [test "$ac_cv_header_termios_h" = yes]) +PY_STDLIB_MOD([_posixsubprocess], [], [test "$MACHDEP" != "win32"]) dnl _elementtree loads libexpat via CAPI hook in pyexpat PY_STDLIB_MOD([pyexpat], @@ -7700,7 +8194,7 @@ PY_STDLIB_MOD([_blake2], PY_STDLIB_MOD([_ctypes], [], [test "$have_libffi" = yes], - [$NO_STRICT_OVERFLOW_CFLAGS $LIBFFI_CFLAGS], [$LIBFFI_LIBS]) + [$NO_STRICT_OVERFLOW_CFLAGS $LIBFFI_CFLAGS], [$LIBFFI_LIBS $CTYPES_LIBS]) PY_STDLIB_MOD([_curses], [], [test "$have_curses" = "yes"], [$CURSES_CFLAGS], [$CURSES_LIBS] @@ -7729,8 +8223,8 @@ PY_STDLIB_MOD([_tkinter], [], [test "$have_tcltk" = "yes"], [$TCLTK_CFLAGS], [$TCLTK_LIBS]) PY_STDLIB_MOD([_uuid], - [], [test "$have_uuid" = "yes"], - [$LIBUUID_CFLAGS], [$LIBUUID_LIBS]) + [], [test "$have_uuid" = "yes" -o "$MACHDEP" = "win32"], + [$LIBUUID_CFLAGS], [$LIBUUID_LIBS $UUID_LIBS]) dnl compression libs PY_STDLIB_MOD([zlib], [], [test "$have_zlib" = yes], @@ -7744,10 +8238,18 @@ PY_STDLIB_MOD([_lzma], [], [test "$have_liblzma" = yes], dnl OpenSSL bindings PY_STDLIB_MOD([_ssl], [], [test "$ac_cv_working_openssl_ssl" = yes], - [$OPENSSL_INCLUDES], [$OPENSSL_LDFLAGS $OPENSSL_LDFLAGS_RPATH $OPENSSL_LIBS]) + [$OPENSSL_INCLUDES], [$OPENSSL_LDFLAGS $OPENSSL_LDFLAGS_RPATH $OPENSSL_LIBS -lws2_32]) PY_STDLIB_MOD([_hashlib], [], [test "$ac_cv_working_openssl_hashlib" = yes], [$OPENSSL_INCLUDES], [$OPENSSL_LDFLAGS $OPENSSL_LDFLAGS_RPATH $LIBCRYPTO_LIBS]) +dnl windows specific modules +PY_STDLIB_MOD([msvcrt], [test "$MACHDEP" = "win32"]) +PY_STDLIB_MOD([_winapi], [test "$MACHDEP" = "win32"]) +PY_STDLIB_MOD([winsound], [test "$MACHDEP" = "win32"], [], [], + [-lwinmm]) +PY_STDLIB_MOD([_overlapped], [test "$MACHDEP" = "win32"], [], [], + [-lws2_32]) + dnl test modules PY_STDLIB_MOD([_testcapi], [test "$TEST_MODULES" = yes], @@ -7756,22 +8258,23 @@ PY_STDLIB_MOD([_testcapi], PY_STDLIB_MOD([_testclinic], [test "$TEST_MODULES" = yes]) PY_STDLIB_MOD([_testclinic_limited], [test "$TEST_MODULES" = yes]) PY_STDLIB_MOD([_testlimitedcapi], [test "$TEST_MODULES" = yes]) -PY_STDLIB_MOD([_testinternalcapi], [test "$TEST_MODULES" = yes]) +PY_STDLIB_MOD([_testinternalcapi], [test "$TEST_MODULES" = yes], [], [-DPY3_DLLNAME="\"$DLLLIBRARY\""], []) PY_STDLIB_MOD([_testbuffer], [test "$TEST_MODULES" = yes]) -PY_STDLIB_MOD([_testimportmultiple], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes]) -PY_STDLIB_MOD([_testmultiphase], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes]) -PY_STDLIB_MOD([_testsinglephase], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes]) +PY_STDLIB_MOD([_testimportmultiple], [test "$TEST_MODULES" = yes], [test "$have_dynamic_loading" = yes]) +PY_STDLIB_MOD([_testmultiphase], [test "$TEST_MODULES" = yes], [test "$have_dynamic_loading" = yes]) +PY_STDLIB_MOD([_testsinglephase], [test "$TEST_MODULES" = yes], [test "$have_dynamic_loading" = yes]) PY_STDLIB_MOD([_testexternalinspection], [test "$TEST_MODULES" = yes]) PY_STDLIB_MOD([xxsubtype], [test "$TEST_MODULES" = yes]) PY_STDLIB_MOD([_xxtestfuzz], [test "$TEST_MODULES" = yes]) +PY_STDLIB_MOD([_testconsole], [test "$TEST_MODULES" = yes -a "$MACHDEP" = "win32"]) PY_STDLIB_MOD([_ctypes_test], - [test "$TEST_MODULES" = yes], [test "$have_libffi" = yes -a "$ac_cv_func_dlopen" = yes], - [], [$LIBM]) + [test "$TEST_MODULES" = yes], [test "$have_libffi" = yes -a "$have_dynamic_loading" = yes], + [], [$LIBM $CTYPES_LIBS]) dnl Limited API template modules. dnl Emscripten does not support shared libraries yet. -PY_STDLIB_MOD([xxlimited], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes]) -PY_STDLIB_MOD([xxlimited_35], [test "$TEST_MODULES" = yes], [test "$ac_cv_func_dlopen" = yes]) +PY_STDLIB_MOD([xxlimited], [test "$TEST_MODULES" = yes], [test "$have_dynamic_loading" = yes]) +PY_STDLIB_MOD([xxlimited_35], [test "$TEST_MODULES" = yes], [test "$have_dynamic_loading" = yes]) # substitute multiline block, must come after last PY_STDLIB_MOD() AC_SUBST([MODULE_BLOCK]) diff --git a/mingw_ignorefile.txt b/mingw_ignorefile.txt new file mode 100644 index 00000000000000..550e72413a4f4e --- /dev/null +++ b/mingw_ignorefile.txt @@ -0,0 +1,25 @@ +test.test_ctypes.test_loading.LoaderTest.test_load_dll_with_flags +distutils.tests.test_bdist_dumb.BuildDumbTestCase.test_simple_built +distutils.tests.test_cygwinccompiler.CygwinCCompilerTestCase.test_get_versions +distutils.tests.test_util.UtilTestCase.test_change_root +test.test_compileall.CommandLineTestsNoSourceEpoch.* +test.test_compileall.CommandLineTestsWithSourceEpoch.* +test.test_compileall.CompileallTestsWithoutSourceEpoch.* +test.test_compileall.CompileallTestsWithSourceEpoch.* +test.test_import.ImportTests.test_dll_dependency_import +test.test_ntpath.NtCommonTest.test_import +test.test_os.StatAttributeTests.test_stat_block_device +test.test_os.TestScandir.test_attributes +test.test_os.UtimeTests.test_large_time +test.test_platform.PlatformTest.test_architecture_via_symlink +test.test_regrtest.ProgramsTestCase.test_pcbuild_rt +test.test_regrtest.ProgramsTestCase.test_tools_buildbot_test +test.test_site._pthFileTests.* +test.test_site.HelperFunctionsTests.* +test.test_site.StartupImportTests.* +test.test_ssl.* +test.test_venv.BasicTest.* +test.test_venv.EnsurePipTest.* + +test.test_dict.DictTest.test_splittable_to_generic_combinedtable +test.test_capi.test_run.CAPITest.test_run_fileexflags diff --git a/mingw_ignorefile_msvcrt.txt b/mingw_ignorefile_msvcrt.txt new file mode 100644 index 00000000000000..be363bd0faf404 --- /dev/null +++ b/mingw_ignorefile_msvcrt.txt @@ -0,0 +1,8 @@ +test.datetimetester.TestLocalTimeDisambiguation_Fast.* +test.datetimetester.TestLocalTimeDisambiguation_Pure.* +test.test_cmath.CMathTests.test_specific_values +test.test_math.MathTests.* +test.test_strptime.CalculationTests.* +test.test_strptime.StrptimeTests.test_weekday +test.test_strptime.TimeRETests.test_compile +test.test_tools.test_i18n.Test_pygettext.test_POT_Creation_Date diff --git a/mingw_smoketests.py b/mingw_smoketests.py new file mode 100644 index 00000000000000..addcd68292b35b --- /dev/null +++ b/mingw_smoketests.py @@ -0,0 +1,375 @@ +#!/usr/bin/env python3 +# Copyright 2017 Christoph Reiter +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +"""The goal of this test suite is collect tests for update regressions +and to test msys2 related modifications like for path handling. +Feel free to extend. +""" + +import os +import unittest +import sysconfig + +if os.environ.get("MSYSTEM", ""): + SEP = "/" +else: + SEP = "\\" + +if sysconfig.is_python_build(): + os.environ["PYTHONLEGACYWINDOWSDLLLOADING"] = "1" + +_UCRT = 'ucrt' in sysconfig.get_platform() + + +class Tests(unittest.TestCase): + + def test_zoneinfo(self): + # https://github.com/msys2-contrib/cpython-mingw/issues/32 + import zoneinfo + self.assertTrue(any(os.path.exists(p) for p in zoneinfo.TZPATH)) + zoneinfo.ZoneInfo("America/Sao_Paulo") + + def test_userdir_path_sep(self): + # Make sure os.path and pathlib use the same path separators + from unittest import mock + from os.path import expanduser + from pathlib import Path + + profiles = ["C:\\foo", "C:/foo"] + for profile in profiles: + with mock.patch.dict(os.environ, {"USERPROFILE": profile}): + self.assertEqual(expanduser("~"), os.path.normpath(expanduser("~"))) + self.assertEqual(str(Path("~").expanduser()), expanduser("~")) + self.assertEqual(str(Path.home()), expanduser("~")) + + # def test_sysconfig_schemes(self): + # # https://github.com/msys2/MINGW-packages/issues/9319 + # import sysconfig + # from distutils.dist import Distribution + # from distutils.command.install import install + + # names = ['scripts', 'purelib', 'platlib', 'data', 'include'] + # for scheme in ["nt", "nt_user"]: + # for name in names: + # c = install(Distribution({"name": "foobar"})) + # c.user = (scheme == "nt_user") + # c.finalize_options() + # if name == "include": + # dist_path = os.path.dirname(getattr(c, "install_" + "headers")) + # else: + # dist_path = getattr(c, "install_" + name) + # sys_path = sysconfig.get_path(name, scheme) + # self.assertEqual(dist_path, sys_path, (scheme, name)) + + def test_ctypes_find_library(self): + from ctypes.util import find_library + from ctypes import cdll + self.assertTrue(cdll.msvcrt) + if _UCRT: + self.assertIsNone(find_library('c')) + else: + self.assertEqual(find_library('c'), 'msvcrt.dll') + + def test_ctypes_dlopen(self): + import ctypes + import sys + self.assertEqual(ctypes.RTLD_GLOBAL, 0) + self.assertEqual(ctypes.RTLD_GLOBAL, ctypes.RTLD_LOCAL) + self.assertFalse(hasattr(sys, 'setdlopenflags')) + self.assertFalse(hasattr(sys, 'getdlopenflags')) + self.assertFalse([n for n in dir(os) if n.startswith("RTLD_")]) + + def test_time_no_unix_stuff(self): + import time + self.assertFalse([n for n in dir(time) if n.startswith("clock_")]) + self.assertFalse([n for n in dir(time) if n.startswith("CLOCK_")]) + self.assertFalse([n for n in dir(time) if n.startswith("pthread_")]) + self.assertFalse(hasattr(time, 'tzset')) + + def test_strftime(self): + import time + with self.assertRaises(ValueError): + time.strftime('%Y', (12345,) + (0,) * 8) + + def test_sep(self): + self.assertEqual(os.sep, SEP) + + def test_module_file_path(self): + import asyncio + import zlib + self.assertEqual(zlib.__file__, os.path.normpath(zlib.__file__)) + self.assertEqual(asyncio.__file__, os.path.normpath(asyncio.__file__)) + + def test_importlib_frozen_path_sep(self): + import importlib._bootstrap_external + self.assertEqual(importlib._bootstrap_external.path_sep, SEP) + + def test_os_commonpath(self): + self.assertEqual( + os.path.commonpath( + [os.path.join("C:", os.sep, "foo", "bar"), + os.path.join("C:", os.sep, "foo")]), + os.path.join("C:", os.sep, "foo")) + + def test_pathlib(self): + import pathlib + + p = pathlib.Path("foo") / pathlib.Path("foo") + self.assertEqual(str(p), os.path.normpath(p)) + + def test_modules_import(self): + import sqlite3 + import ssl + import ctypes + import curses + + def test_c_modules_import(self): + import _decimal + + def test_socket_inet_ntop(self): + import socket + self.assertTrue(hasattr(socket, "inet_ntop")) + + def test_socket_inet_pton(self): + import socket + self.assertTrue(hasattr(socket, "inet_pton")) + + def test_multiprocessing_queue(self): + from multiprocessing import Queue + Queue(0) + + #def test_socket_timout_normal_error(self): + # import urllib.request + # from urllib.error import URLError + + # try: + # urllib.request.urlopen( + # 'http://localhost', timeout=0.0001).close() + # except URLError: + # pass + + def test_threads(self): + from concurrent.futures import ThreadPoolExecutor + + with ThreadPoolExecutor(1) as pool: + for res in pool.map(lambda *x: None, range(10000)): + pass + + def test_sysconfig(self): + import sysconfig + # This should be able to execute without exceptions + sysconfig.get_config_vars() + + def test_sqlite_enable_load_extension(self): + # Make sure --enable-loadable-sqlite-extensions is used + import sqlite3 + self.assertTrue(sqlite3.Connection.enable_load_extension) + + def test_venv_creation(self): + import tempfile + import venv + import subprocess + import shutil + with tempfile.TemporaryDirectory() as tmp: + builder = venv.EnvBuilder() + builder.create(tmp) + assert os.path.exists(os.path.join(tmp, "bin", "activate")) + assert os.path.exists(os.path.join(tmp, "bin", "python.exe")) + assert os.path.exists(os.path.join(tmp, "bin", "python3.exe")) + subprocess.check_call([shutil.which("bash.exe"), os.path.join(tmp, "bin", "activate")]) + + # This will not work in in-tree build + if not sysconfig.is_python_build(): + op = subprocess.check_output( + [ + os.path.join(tmp, "bin", "python.exe"), + "-c", + "print('Hello World')" + ], + cwd=tmp, + ) + assert op.decode().strip() == "Hello World" + + def test_has_mktime(self): + from time import mktime, gmtime + mktime(gmtime()) + + def test_platform_things(self): + import sys + import sysconfig + import platform + import importlib.machinery + import tempfile + import venv + import subprocess + self.assertEqual(sys.implementation.name, "cpython") + self.assertEqual(sys.platform, "win32") + self.assertTrue(sysconfig.get_platform().startswith("mingw")) + self.assertTrue(sysconfig.get_config_var('SOABI').startswith("cpython-")) + ext_suffix = sysconfig.get_config_var('EXT_SUFFIX') + self.assertTrue(ext_suffix.endswith(".pyd")) + self.assertTrue("mingw" in ext_suffix) + self.assertEqual(sysconfig.get_config_var('SHLIB_SUFFIX'), ".pyd") + ext_suffixes = importlib.machinery.EXTENSION_SUFFIXES + self.assertTrue(ext_suffix in ext_suffixes) + self.assertTrue(".pyd" in ext_suffixes) + if sysconfig.get_platform().startswith('mingw_i686'): + self.assertEqual(sys.winver, ".".join(map(str, sys.version_info[:2])) + '-32') + elif sysconfig.get_platform().startswith('mingw_aarch64'): + self.assertEqual(sys.winver, ".".join(map(str, sys.version_info[:2])) + '-arm64') + elif sysconfig.get_platform().startswith('mingw_armv7'): + self.assertEqual(sys.winver, ".".join(map(str, sys.version_info[:2])) + '-arm32') + else: + self.assertEqual(sys.winver, ".".join(map(str, sys.version_info[:2]))) + self.assertEqual(platform.python_implementation(), "CPython") + self.assertEqual(platform.system(), "Windows") + self.assertTrue(isinstance(sys.api_version, int) and sys.api_version > 0) + + with tempfile.TemporaryDirectory() as tmp: + builder = venv.EnvBuilder() + builder.create(tmp) + # This will not work in in-tree build + if not sysconfig.is_python_build(): + op = subprocess.check_output( + [ + os.path.join(tmp, "bin", "python.exe"), + "-c", + "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))" + ], + cwd=tmp, + ) + self.assertTrue(op.decode().strip().startswith(sys.base_prefix)) + + def test_sys_getpath(self): + # everything sourced from getpath.py + import sys + + def assertNormpath(path): + self.assertEqual(path, os.path.normpath(path)) + + assertNormpath(sys.executable) + assertNormpath(sys._base_executable) + assertNormpath(sys.prefix) + assertNormpath(sys.base_prefix) + assertNormpath(sys.exec_prefix) + assertNormpath(sys.base_exec_prefix) + assertNormpath(sys.platlibdir) + assertNormpath(sys._stdlib_dir) + for p in sys.path: + assertNormpath(p) + + def test_site(self): + import site + + self.assertEqual(len(site.getsitepackages()), 1) + + def test_c_ext_build(self): + # This will not work in in-tree build + if sysconfig.is_python_build(): + raise unittest.SkipTest("in-tree build") + + import tempfile + import sys + import subprocess + import textwrap + import venv + from pathlib import Path + + with tempfile.TemporaryDirectory() as tmppro: + builder = venv.EnvBuilder(with_pip=True) + builder.create(tmppro) + venv_exe = os.path.join(tmppro, "bin", os.path.basename(sys.executable)) + + with Path(tmppro, "setup.py").open("w") as f: + f.write( + textwrap.dedent( + """\ + from setuptools import setup, Extension + + setup( + name='cwrapper', + version='1.0', + ext_modules=[ + Extension( + 'cwrapper', + sources=['cwrapper.c']), + ], + ) + """ + ) + ) + with Path(tmppro, "cwrapper.c").open("w") as f: + f.write( + textwrap.dedent( + """\ + #include + static PyObject * + helloworld(PyObject *self, PyObject *args) + { + printf("Hello World\\n"); + Py_RETURN_NONE; + } + static PyMethodDef + myMethods[] = { + { "helloworld", helloworld, METH_NOARGS, "Prints Hello World" }, + { NULL, NULL, 0, NULL } + }; + static struct PyModuleDef cwrapper = { + PyModuleDef_HEAD_INIT, + "cwrapper", + "Test Module", + -1, + myMethods + }; + + PyMODINIT_FUNC + PyInit_cwrapper(void) + { + return PyModule_Create(&cwrapper); + } + """ + ) + ) + subprocess.check_call( + [venv_exe, "-c", "import struct"], + ) + subprocess.check_call( + [ + venv_exe, + "-m", + "pip", + "install", + tmppro, + ], + ) + subprocess.check_call( + [venv_exe, "-c", "import cwrapper"], + ) + + + +def suite(): + return unittest.TestLoader().loadTestsFromName(__name__) + + +if __name__ == '__main__': + unittest.main(defaultTest='suite') diff --git a/pyconfig.h.in b/pyconfig.h.in index 4531dadee384a4..8a869c62cef6c5 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -59,7 +59,7 @@ /* Define to 1 if you have the `acosh' function. */ #undef HAVE_ACOSH -/* struct addrinfo (netdb.h) */ +/* struct addrinfo */ #undef HAVE_ADDRINFO /* Define to 1 if you have the `alarm' function. */ @@ -1614,6 +1614,9 @@ /* Define if mvwdelch in curses.h is an expression. */ #undef MVWDELCH_IS_EXPRESSION +/* Define to 1 if you want to use native NT threads */ +#undef NT_THREADS + /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT @@ -1994,7 +1997,7 @@ /* Define to `unsigned int' if does not define. */ #undef size_t -/* Define to 'int' if does not define. */ +/* Define to 'int' if or does not define. */ #undef socklen_t /* Define to `int' if doesn't define. */