From 7b6cfcc30aac834b4fe0f236977a99cd3bc927a0 Mon Sep 17 00:00:00 2001 From: Ray Donnelly Date: Thu, 17 Jun 2021 18:51:10 +0530 Subject: [PATCH 001/123] sysconfig: make _sysconfigdata.py relocatable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей Co-authored-by: lovetox <8661218+lovetox@users.noreply.github.com> Co-authored-by: Naveen M K --- Lib/sysconfig/__main__.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Lib/sysconfig/__main__.py b/Lib/sysconfig/__main__.py index d7257b9d2d00db..a551c51106fc3c 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, @@ -209,11 +210,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: From d98f356a02b8cef4dd7d39ce6c58ab0f5829bdff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:12 +0530 Subject: [PATCH 002/123] build: add --with-nt-threads and make it default on mingw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Naveen M K Co-authored-by: Алексей Co-authored-by: Christoph Reiter --- Include/internal/pycore_condvar.h | 5 + Include/internal/pycore_pythread.h | 6 ++ Include/pythread.h | 6 ++ Modules/_multiprocessing/multiprocessing.h | 5 +- configure.ac | 102 ++++++++++++++++++++- pyconfig.h.in | 3 + 6 files changed, 123 insertions(+), 4 deletions(-) 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_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/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/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/configure.ac b/configure.ac index a764028e49fcfd..848f5371d9bc26 100644 --- a/configure.ac +++ b/configure.ac @@ -2811,6 +2811,53 @@ then BASECFLAGS="$BASECFLAGS $ac_arch_flags" fi +dnl NOTE: +dnl - GCC 4.4+ for mingw* require and use posix threads(pthreads-w32) +dnl - Host may contain installed pthreads-w32. +dnl - On windows platform only NT-thread model is supported. +dnl To avoid miss detection scipt first will check for NT-thread model +dnl and if is not found will try to detect build options for pthread +dnl model. Autodetection could be overiden if variable with_nt_threads +dnl is set in "Site Configuration" (see autoconf manual). +dnl If NT-thread model is enabled script skips some checks that +dnl impact build process. When a new functionality is added, developers +dnl are responsible to update configure script to avoid thread models +dnl to be mixed. + +AC_MSG_CHECKING([for --with-nt-threads]) +AC_ARG_WITH(nt-threads, + AS_HELP_STRING([--with-nt-threads], [build with windows threads (default is system-dependent)]), +[ + case $withval in + no) with_nt_threads=no;; + yes) with_nt_threads=yes;; + *) with_nt_threads=yes;; + esac +], [ + case $host in + *-*-mingw*) with_nt_threads=yes;; + *) with_nt_threads=no;; + esac +]) +AC_MSG_RESULT([$with_nt_threads]) + +if test $with_nt_threads = yes ; then +AC_MSG_CHECKING([whether linking with nt-threads work]) +AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[]],[[_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 +2969,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 +2990,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 @@ -2982,8 +3035,8 @@ AC_CHECK_HEADERS([ \ alloca.h asm/types.h bluetooth.h conio.h direct.h dlfcn.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 \ @@ -2994,6 +3047,14 @@ AC_CHECK_HEADERS([ \ 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 @@ -3201,6 +3262,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 +3297,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]) @@ -3772,10 +3837,15 @@ 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 AC_CHECK_LIB([intl], [textdomain], @@ -4510,6 +4580,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" @@ -5089,6 +5164,15 @@ 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 \ @@ -5970,6 +6054,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 +6091,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([ diff --git a/pyconfig.h.in b/pyconfig.h.in index 4531dadee384a4..03283d68f630c8 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -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 From 3f02a9d48c467075b7e0b2ddfa5feaa7ebb63d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:13 +0530 Subject: [PATCH 003/123] Define `MS_WINDOWS` (and others) when compiling with MINGW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is used throughout the Python code but is not defined when compiling with Mingw-w64 compilers, so define it. In MSVC these are defined in PC/pyconfig.h, we have to define it manually because pyconfig.h is created by autotools. Co-authored-by: Алексей Co-authored-by: Christoph Reiter Co-authored-by: Naveen M K --- Include/pyport.h | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Include/pyport.h b/Include/pyport.h index 2ba81a4be42822..2c8bb661877bed 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. From 4fbe16a013efc79a371dd74dea457b6c6c563ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:14 +0530 Subject: [PATCH 004/123] configure: add `MACHDEP` and `platform` on MINGW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей --- configure.ac | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/configure.ac b/configure.ac index 848f5371d9bc26..59c52b9df1033a 100644 --- a/configure.ac +++ b/configure.ac @@ -327,6 +327,9 @@ then *-*-cygwin*) ac_sys_system=Cygwin ;; + *-*-mingw*) + ac_sys_system=MINGW + ;; *-apple-ios*) ac_sys_system=iOS ;; @@ -366,6 +369,7 @@ then linux*) MACHDEP="linux";; cygwin*) MACHDEP="cygwin";; darwin*) MACHDEP="darwin";; + mingw*) MACHDEP="win32";; '') MACHDEP="unknown";; esac @@ -776,6 +780,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 +790,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 From e93e83b3a0a35d97297a740c8f0baa3718f320c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:15 +0530 Subject: [PATCH 005/123] Add default configuration for MINGW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей Co-authored-by: Christoph Reiter --- Misc/config_mingw | 12 ++++++++++++ Misc/cross_mingw32 | 11 +++++++++++ configure.ac | 22 ++++++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 Misc/config_mingw create mode 100644 Misc/cross_mingw32 diff --git a/Misc/config_mingw b/Misc/config_mingw new file mode 100644 index 00000000000000..513065dec9b717 --- /dev/null +++ b/Misc/config_mingw @@ -0,0 +1,12 @@ +# 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 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/configure.ac b/configure.ac index 59c52b9df1033a..05cbc4aefef28a 100644 --- a/configure.ac +++ b/configure.ac @@ -1233,6 +1233,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 < Date: Thu, 17 Jun 2021 18:51:16 +0530 Subject: [PATCH 006/123] configure: enable largefile support by default for Mingw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Largefile is supported on Windows. Co-authored-by: Naveen M K Co-authored-by: Алексей Co-authored-by: Christoph Reiter --- configure.ac | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configure.ac b/configure.ac index 05cbc4aefef28a..584e07371c0a33 100644 --- a/configure.ac +++ b/configure.ac @@ -3269,6 +3269,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 From 699b1be43bedbdb21b7da72c7c9f34bb3519ee85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:18 +0530 Subject: [PATCH 007/123] Add `PC/` to `CPPFLAGS` and to `SRCDIRS` on Mingw Co-authored-by: Naveen M K --- configure.ac | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/configure.ac b/configure.ac index 584e07371c0a33..fe2f49b7754fbc 100644 --- a/configure.ac +++ b/configure.ac @@ -7060,6 +7060,16 @@ 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. + CPPFLAGS="-I\$(srcdir)/PC $CPPFLAGS" + ;; +esac + AC_SUBST([SRCDIRS]) SRCDIRS="\ Modules \ @@ -7087,6 +7097,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 From b3637a9f70c5fa6c21da42a31954c336d0b5980d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:19 +0530 Subject: [PATCH 008/123] posixmodule.c: Define necessary headers for compiling on MINGW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also, build `nt` module instead of `posix` when compiling with MINGW. Co-authored-by: Naveen M K Co-authored-by: Алексей --- Modules/Setup.bootstrap.in | 2 +- Modules/posixmodule.c | 28 ++++++++++++++++++++++++---- configure.ac | 8 ++++++++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/Modules/Setup.bootstrap.in b/Modules/Setup.bootstrap.in index aa4e60e272653b..bca16508cbae73 100644 --- a/Modules/Setup.bootstrap.in +++ b/Modules/Setup.bootstrap.in @@ -8,7 +8,7 @@ # module C APIs are used in core atexit atexitmodule.c faulthandler faulthandler.c -posix posixmodule.c +@INITSYS@ posixmodule.c _signal signalmodule.c _tracemalloc _tracemalloc.c _suggestions _suggestions.c diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index a09305c91ecbba..5052434eff24af 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -390,6 +390,26 @@ corresponding Unix manual entries for more information on calls."); # 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 +/*# 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] @@ -467,7 +487,7 @@ extern char *ctermid_r(char *); # endif #endif -#ifdef _MSC_VER +#ifdef MS_WINDOWS # ifdef HAVE_DIRECT_H # include # endif @@ -478,7 +498,7 @@ extern char *ctermid_r(char *); # include # endif # include -#endif /* _MSC_VER */ +#endif /* MS_WINDOWS */ #ifndef MAXPATHLEN # if defined(PATH_MAX) && PATH_MAX > 1024 @@ -1650,9 +1670,9 @@ _Py_Sigset_Converter(PyObject *obj, void *addr) */ #include #define USE_DARWIN_NS_GET_ENVIRON 1 -#elif !defined(_MSC_VER) && (!defined(__WATCOMC__) || defined(__QNX__) || defined(__VXWORKS__)) +#elif !defined(MS_WINDOWS) && (!defined(__WATCOMC__) || defined(__QNX__) || defined(__VXWORKS__)) extern char **environ; -#endif /* !_MSC_VER */ +#endif /* !MS_WINDOWS */ static PyObject * convertenviron(void) diff --git a/configure.ac b/configure.ac index fe2f49b7754fbc..719bfb640f76e0 100644 --- a/configure.ac +++ b/configure.ac @@ -916,6 +916,14 @@ then [Define to include mbstate_t for mbrtowc]) fi +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 From ddb028ad579f0e113e595a84bc95be3bfe0cf8e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:20 +0530 Subject: [PATCH 009/123] Build winreg by default when compiling for MINGW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей Co-authored-by: Christoph Reiter Co-authored-by: Naveen M K --- Modules/Setup.bootstrap.in | 3 +++ PC/winreg.c | 19 +++++++++++++++++++ configure.ac | 7 +++++++ 3 files changed, 29 insertions(+) diff --git a/Modules/Setup.bootstrap.in b/Modules/Setup.bootstrap.in index bca16508cbae73..fe9f16895b797d 100644 --- a/Modules/Setup.bootstrap.in +++ b/Modules/Setup.bootstrap.in @@ -36,3 +36,6 @@ _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 diff --git a/PC/winreg.c b/PC/winreg.c index efdf8addc06186..0e070139900a18 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 { diff --git a/configure.ac b/configure.ac index 719bfb640f76e0..474d13fe1e6412 100644 --- a/configure.ac +++ b/configure.ac @@ -4595,6 +4595,13 @@ done IFS=$as_save_IFS AC_MSG_RESULT([$DBM_CFLAGS $DBM_LIBS]) +# 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], From 37b8dd28a9a6b31cd4d3b5b73580dfb37b60dde9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:23 +0530 Subject: [PATCH 010/123] exports.h: Add support for MINGW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Naveen M K Co-authored-by: Алексей --- Include/exports.h | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) 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) From d4ea550e1aca9159330bec45d80f5a8b436d81ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:24 +0530 Subject: [PATCH 011/123] configure: add options so that shared build is possible on MINGW Co-authored-by: Naveen M K --- configure.ac | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 474d13fe1e6412..1dcfd03c19fbd0 100644 --- a/configure.ac +++ b/configure.ac @@ -1592,6 +1592,11 @@ if test $enable_shared = "yes"; then BLDLIBRARY='-L. -lpython$(LDVERSION)' DLLLIBRARY='libpython$(LDVERSION).dll' ;; + MINGW*) + LDLIBRARY='libpython$(LDVERSION).dll.a' + DLLLIBRARY='libpython$(LDVERSION).dll' + BLDLIBRARY='-L. -lpython$(LDVERSION)' + ;; SunOS*) LDLIBRARY='libpython$(LDVERSION).so' BLDLIBRARY='-Wl,-R,$(LIBDIR) -L. -lpython$(LDVERSION)' @@ -1651,6 +1656,9 @@ else # shared is disabled BLDLIBRARY='$(LIBRARY)' LDLIBRARY='libpython$(LDVERSION).dll.a' ;; + MINGW*) + LDLIBRARY='libpython$(LDVERSION).a' + ;; esac fi AC_MSG_RESULT([$LDLIBRARY]) @@ -3635,6 +3643,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 @@ -7081,7 +7093,8 @@ case $host in 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. - CPPFLAGS="-I\$(srcdir)/PC $CPPFLAGS" + dnl (-I. at end is workaround for setup.py logic) + CPPFLAGS="-I\$(srcdir)/PC $CPPFLAGS -I." ;; esac From 9bcfb0999f140ac7453deb08af38df1ced274c6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:25 +0530 Subject: [PATCH 012/123] Add dynamic loading support for MINGW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей --- Makefile.pre.in | 6 ++++++ Python/dynload_win.c | 9 +++++++-- configure.ac | 16 ++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index 03ca4cb635bd38..755d0e7ef10ef5 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1732,6 +1732,12 @@ 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) \ + -DSHLIB_SUFFIX='"$(SHLIB_SUFFIX)"' \ + -DEXT_SUFFIX='"$(EXT_SUFFIX)"' \ + -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)"' \ diff --git a/Python/dynload_win.c b/Python/dynload_win.c index a0ac31c80a5f6e..386a2c374c273a 100644 --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -10,6 +10,12 @@ #include const char *_PyImport_DynLoadFiletab[] = { +#ifdef EXT_SUFFIX + EXT_SUFFIX, /* include SOABI flags where is encoded debug */ +#endif +#ifdef SHLIB_SUFFIX + "-abi" PYTHON_ABI_STRING SHLIB_SUFFIX, +#endif PYD_TAGGED_SUFFIX, PYD_UNTAGGED_SUFFIX, NULL @@ -232,8 +238,7 @@ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix, 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); + LOAD_WITH_ALTERED_SEARCH_PATH); Py_END_ALLOW_THREADS PyMem_Free(wpathname); diff --git a/configure.ac b/configure.ac index 1dcfd03c19fbd0..8220d1cf8ef64e 100644 --- a/configure.ac +++ b/configure.ac @@ -3509,6 +3509,9 @@ if test -z "$SHLIB_SUFFIX"; then CYGWIN*) SHLIB_SUFFIX=.dll;; *) SHLIB_SUFFIX=.so;; esac + case $host_os in + mingw*) SHLIB_SUFFIX=.dll;; + esac fi AC_MSG_RESULT([$SHLIB_SUFFIX]) @@ -5183,6 +5186,13 @@ 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 -DMS_DLL_ID='\"$VERSION\"'" + ;; + esac fi AC_MSG_RESULT([$DYNLOADFILE]) if test "$DYNLOADFILE" != "dynload_stub.o" @@ -7072,6 +7082,12 @@ case "$ac_cv_computed_gotos" in yes*) AC_DEFINE([HAVE_COMPUTED_GOTOS], [1], [Define if the C compiler supports computed gotos.]) esac +case $host_os in + mingw*) + dnl Synchronized with _PyImport_DynLoadFiletab (dynload_win.c) + dnl Do not use more then one dot on this platform ! + EXT_SUFFIX=-$SOABI$SHLIB_SUFFIX;; +esac case $ac_sys_system in AIX*) From 1b00d760a1821eaf5a149cb90f1c94b1482a1838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:26 +0530 Subject: [PATCH 013/123] Ignore main program for frozen scripts on MINGW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей --- Makefile.pre.in | 2 +- configure.ac | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index 755d0e7ef10ef5..247d37072bb605 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -431,7 +431,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 \ diff --git a/configure.ac b/configure.ac index 8220d1cf8ef64e..4dbae034524e77 100644 --- a/configure.ac +++ b/configure.ac @@ -7114,6 +7114,16 @@ case $host in ;; 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 \ From 7844491c014d4df85655eda698b28c5e95806996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:30 +0530 Subject: [PATCH 014/123] Detect winsock2 and setup _socket module on MINGW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей Co-authored-by: Naveen M K --- Misc/config_mingw | 3 +++ Modules/socketmodule.c | 6 ++++++ configure.ac | 38 ++++++++++++++++++++++++++++++++------ pyconfig.h.in | 4 ++-- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/Misc/config_mingw b/Misc/config_mingw index 513065dec9b717..9be43fd3a11407 100644 --- a/Misc/config_mingw +++ b/Misc/config_mingw @@ -10,3 +10,6 @@ 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/Modules/socketmodule.c b/Modules/socketmodule.c index 1c0bc796732120..9cdbda2cff0b47 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -282,6 +282,7 @@ shutdown(how) -- shut down traffic in one or both directions\n\ # 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/configure.ac b/configure.ac index 4dbae034524e77..8e253cba687376 100644 --- a/configure.ac +++ b/configure.ac @@ -4296,6 +4296,12 @@ 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 detect sqlite3 from Emscripten emport PY_CHECK_EMSCRIPTEN_PORT([LIBSQLITE3], [-sUSE_SQLITE3]) @@ -5807,18 +5813,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 @@ -7003,7 +7024,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 @@ -7860,7 +7884,9 @@ PY_STDLIB_MOD([mmap], 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], [], diff --git a/pyconfig.h.in b/pyconfig.h.in index 03283d68f630c8..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. */ @@ -1997,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. */ From be45d4371f60ae0b1095c7d5f6a242c346143b8f Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Tue, 14 Feb 2023 20:40:42 +0530 Subject: [PATCH 015/123] Enable some windows specific modules - remove _msi module --- Modules/Setup.bootstrap.in | 2 ++ Modules/Setup.stdlib.in | 6 ++++++ Modules/_winapi.c | 2 ++ PC/msvcrtmodule.c | 2 ++ Python/dynamic_annotations.c | 2 +- configure.ac | 8 ++++++++ 6 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Modules/Setup.bootstrap.in b/Modules/Setup.bootstrap.in index fe9f16895b797d..f0b3b733d9e74b 100644 --- a/Modules/Setup.bootstrap.in +++ b/Modules/Setup.bootstrap.in @@ -39,3 +39,5 @@ _symtable symtablemodule.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..0f5350ddca2e70 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 diff --git a/Modules/_winapi.c b/Modules/_winapi.c index bd80c5c94fe36d..c6852b0b6b0f95 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) 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/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/configure.ac b/configure.ac index 8e253cba687376..be4fb7c54bed5e 100644 --- a/configure.ac +++ b/configure.ac @@ -7980,6 +7980,14 @@ PY_STDLIB_MOD([_ssl], [], [test "$ac_cv_working_openssl_ssl" = yes], 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], From 02f3a6b5ecf378ebe85754a096c4220ec6cace87 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Tue, 21 Sep 2021 20:52:42 +0200 Subject: [PATCH 016/123] sysconfig: MINGW build extensions with GCC --- Lib/sysconfig/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index ec3b638f00766d..4f014ca4beb015 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -601,6 +601,8 @@ def get_platform(): """ if os.name == 'nt': + if 'GCC' in sys.version: + return 'mingw' if 'amd64' in sys.version.lower(): return 'win-amd64' if '(arm)' in sys.version.lower(): From b78b6d746b2004b1f1b21b3358f6df3cc2974085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:38 +0530 Subject: [PATCH 017/123] sysconfig: treat MINGW builds as POSIX builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей Co-authored-by: Naveen M K --- Lib/sysconfig/__init__.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index 4f014ca4beb015..37c06f1a81e1d9 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -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()) @@ -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', @@ -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 @@ -489,10 +494,10 @@ def _init_config_vars(): except AttributeError: _CONFIG_VARS['py_version_nodot_plat'] = '' - 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 +510,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 From 1defca96cad6d2ce8c88e1bc0f59feeccd8cb0f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:39 +0530 Subject: [PATCH 018/123] Add support for stdcall without underscore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the case used in MINGW Co-authored-by: Алексей --- Modules/_ctypes/_ctypes.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) 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 From c5977b1ebcf1330e6610948857444a7a53e11970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:42 +0530 Subject: [PATCH 019/123] site.py: customize for MINGW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей Co-authored-by: Christoph Reiter --- Lib/site.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Lib/site.py b/Lib/site.py index 89a81c55cb63dd..0153598af1afcd 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()) @@ -319,7 +325,7 @@ def _get_path(userbase): implementation = _get_implementation() implementation_lower = implementation.lower() - if os.name == 'nt': + if os.name == 'nt' and not _POSIX_BUILD: ver_nodot = sys.winver.replace('.', '') return f'{userbase}\\{implementation}{ver_nodot}\\site-packages' @@ -398,7 +404,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") From 14f7b176f9b981182cf40226b4a394dbfeeb4ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:43 +0530 Subject: [PATCH 020/123] add python config sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей Co-authored-by: Christoph Reiter --- Misc/python-config.sh.in | 62 +++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 23 deletions(-) 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 From c0432a6571fee20d3d968ee278feae1a205eb48e Mon Sep 17 00:00:00 2001 From: Ray Donnelly Date: Thu, 17 Jun 2021 18:51:46 +0530 Subject: [PATCH 021/123] mingw: prefer unix sep if MSYSTEM environment variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей Co-authored-by: Christoph Reiter Co-authored-by: cat Co-authored-by: Naveen M K --- Include/pylifecycle.h | 6 ++ Lib/ntpath.py | 118 ++++++++++++++++++++---------------- Modules/posixmodule.c | 2 + Python/initconfig.c | 2 +- Python/pathconfig.c | 135 ++++++++++++++++++++++++++++++++++++++++++ Python/traceback.c | 2 +- 6 files changed, 211 insertions(+), 54 deletions(-) diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h index de1bcb1d2cb632..9f842cb3b5a3b6 100644 --- a/Include/pylifecycle.h +++ b/Include/pylifecycle.h @@ -21,6 +21,12 @@ PyAPI_FUNC(int) Py_IsInitialized(void); PyAPI_FUNC(PyThreadState *) Py_NewInterpreter(void); PyAPI_FUNC(void) Py_EndInterpreter(PyThreadState *); +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 *); + /* Py_PyAtExit is for the atexit module, Py_AtExit is for low-level * exit functions. diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 5481bb8888ef59..af91c6f2785339 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 "MSYSTEM" in os.environ: + 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 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 '\\/' + 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__ @@ -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) @@ -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/Modules/posixmodule.c b/Modules/posixmodule.c index 5052434eff24af..3ec5c2e077c987 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -4141,6 +4141,7 @@ posix_getcwd(int use_bytes) return NULL; } + Py_NormalizeSepsW(wbuf2); PyObject *resobj = PyUnicode_FromWideChar(wbuf2, len); if (wbuf2 != wbuf) { PyMem_RawFree(wbuf2); @@ -5041,6 +5042,7 @@ os__getfinalpathname_impl(PyObject *module, path_t *path) 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)); 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/pathconfig.c b/Python/pathconfig.c index 33abaddc1b5df4..dacf360c62437f 100644 --- a/Python/pathconfig.c +++ b/Python/pathconfig.c @@ -17,6 +17,141 @@ #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) +{ + char* msystem = (char*)2; /* So that non Windows use / as sep */ + 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__) + msystem = Py_GETENV("MSYSTEM"); +#endif + if (msystem != NULL) + sep = '/'; + else + sep = '\\'; + 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) +{ + char* msystem = (char*)2; /* So that non Windows use / as sep */ + 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__) + msystem = Py_GETENV("MSYSTEM"); +#endif + if (msystem != NULL) + sep = L'/'; + else + sep = L'\\'; + return sep; +} + +static 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); + } +} + /* External interface */ /* Stored values set by C API functions */ 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 From 3df74cee918854fcd00cadd703a977722393b7f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:50 +0530 Subject: [PATCH 022/123] msys cygwin semi native build sysconfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей --- Lib/sysconfig/__main__.py | 8 ++++++ Makefile.pre.in | 7 +++++ configure.ac | 59 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/Lib/sysconfig/__main__.py b/Lib/sysconfig/__main__.py index a551c51106fc3c..dad8f903c7df49 100644 --- a/Lib/sysconfig/__main__.py +++ b/Lib/sysconfig/__main__.py @@ -146,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 diff --git a/Makefile.pre.in b/Makefile.pre.in index 247d37072bb605..f34df0d1939343 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -144,6 +144,13 @@ exec_prefix= @exec_prefix@ # Install prefix for data files datarootdir= @datarootdir@ +# Locations needed for semi-native fixup of sysconfig. +srcdir_b2h= @srcdir_b2h@ +VPATH_b2h= @VPATH_b2h@ +abs_srcdir_b2h= @abs_srcdir_b2h@ +abs_builddir_b2h= @abs_builddir_b2h@ +prefix_b2h= @prefix_b2h@ + # Expanded directories BINDIR= @bindir@ LIBDIR= @libdir@ diff --git a/configure.ac b/configure.ac index be4fb7c54bed5e..e58529966ea537 100644 --- a/configure.ac +++ b/configure.ac @@ -916,6 +916,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. +# +# Also, getpath.c uses GetModuleFileNameW (replacing \ with /) and +# compares that with the define VPATH (passed in via command-line) +# to determine whether it's the build- or the installed-Python. +# +# To work around these issues a set of _b2h variables are created: +# VPATH_b2h, 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 VPATH) +ABS_PATH_HOST([VPATH_b2h],[srcdir]) +AC_MSG_RESULT([$VPATH_b2h]) + +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 From 72f71db412afd9fda6e6e32e192f79e321e17c03 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Tue, 21 Sep 2021 20:53:59 +0200 Subject: [PATCH 023/123] sysconfig: mingw sysconfig like posix Co-authored-by: Naveen M K --- Lib/sysconfig/__init__.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index 37c06f1a81e1d9..8672ae2ccc6393 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}', }, @@ -144,12 +144,12 @@ 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}{abi_thread}', + 'platstdlib': '{userbase}/lib/{implementation}{py_version_short}{abi_thread}', + 'purelib': '{userbase}/lib/{implementation}{py_version_short}{abi_thread}/site-packages', + 'platlib': '{userbase}/lib/{implementation}{py_version_short}{abi_thread}/site-packages', + 'include': '{userbase}/include/{implementation}{py_version_short}{abi_thread}', + 'scripts': '{userbase}/bin', 'data': '{userbase}', }, 'posix_user': { @@ -378,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'] = '' From c72ee4a959df29b77630ce9b58db2a6d3a9e4823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:53 +0530 Subject: [PATCH 024/123] mingw use backslashes in compileall py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей --- Lib/compileall.py | 2 ++ 1 file changed, 2 insertions(+) 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 From d938e64fc3f424e4955c74a246319544e6e13608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:55 +0530 Subject: [PATCH 025/123] py_curses.h: define mingw pdcurses _ISPAD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей --- Include/py_curses.h | 7 +++++++ 1 file changed, 7 insertions(+) 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. */ From be30615b2fc8cdf7bacd35f742ddbb1088c352c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:51:59 +0530 Subject: [PATCH 026/123] remove_path_max.default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей --- Include/osdefs.h | 1 - 1 file changed, 1 deletion(-) 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 From b793385fb39bcda0e9e33ab01439b285becc51c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:52:00 +0530 Subject: [PATCH 027/123] dont link with gettext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей --- Modules/_localemodule.c | 8 ++++++++ configure.ac | 9 +++++++++ 2 files changed, 17 insertions(+) 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/configure.ac b/configure.ac index e58529966ea537..8f57669da63cbf 100644 --- a/configure.ac +++ b/configure.ac @@ -3971,10 +3971,19 @@ 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 From d185d3868836a3f0a87dac6832ed5b095bba3166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:52:01 +0530 Subject: [PATCH 028/123] ctypes python dll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей Co-authored-by: Christoph Reiter --- Lib/ctypes/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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]) From 90d57c8817141a1a03b78365e2fca56a93a9fec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:52:02 +0530 Subject: [PATCH 029/123] gdbm module includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей --- Modules/_gdbmmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 08a21e48504846940ee14a61c8660ea8752f5f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:52:03 +0530 Subject: [PATCH 030/123] use gnu_printf in format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей --- Include/bytesobject.h | 4 ++-- Include/pyerrors.h | 4 ++-- Include/pyport.h | 6 ++++++ Include/sysmodule.h | 4 ++-- 4 files changed, 12 insertions(+), 6 deletions(-) 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/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/pyport.h b/Include/pyport.h index 2c8bb661877bed..a50347d097f189 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -477,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/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, ...); From bd3a9cabd9514c8c3f329878d82d3ccbab0485d3 Mon Sep 17 00:00:00 2001 From: Ray Donnelly Date: Thu, 17 Jun 2021 18:52:03 +0530 Subject: [PATCH 031/123] mingw fix ssl dont use enum_certificates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей Co-authored-by: Christoph Reiter --- Lib/ssl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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() From c76158f69e437958617770c5fe6a68336925b502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:52:06 +0530 Subject: [PATCH 032/123] fix using dllhandle and winver mingw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей --- Python/sysmodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From cd5830aac24dce65679e07ccbe55cc98e1145bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:52:07 +0530 Subject: [PATCH 033/123] Add AMD64 to sys config so msvccompiler get_build_version works --- Python/getcompiler.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Python/getcompiler.c b/Python/getcompiler.c index a5d26239e8772e..3e31c80bdf4fb1 100644 --- a/Python/getcompiler.c +++ b/Python/getcompiler.c @@ -10,7 +10,22 @@ #if defined(__clang__) #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)" +#else +#define ARCH_SUFFIX " 32 bit" +#endif +#else +#define COMP_SEP "\n" +#define ARCH_SUFFIX "" +#endif +#define COMPILER COMP_SEP "[GCC " __VERSION__ ARCH_SUFFIX "]" // Generic fallbacks. #elif defined(__cplusplus) #define COMPILER "[C++]" From a48c4b0b30c516f55f28934b0dd255541b0f9ccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:52:08 +0530 Subject: [PATCH 034/123] MINGW link with additional library --- configure.ac | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/configure.ac b/configure.ac index 8f57669da63cbf..9cee791eb89ade 100644 --- a/configure.ac +++ b/configure.ac @@ -7256,6 +7256,15 @@ 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" + ;; + *) + ;; +esac + # Availability of -O2: AC_CACHE_CHECK([for -O2], [ac_cv_compile_o2], [ saved_cflags="$CFLAGS" From 2ffc75617231c3379dd1a6771725539a6171ade3 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Thu, 17 Jun 2021 18:52:10 +0530 Subject: [PATCH 035/123] fix signal module build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей Co-authored-by: Naveen M K --- Modules/Setup.bootstrap.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Setup.bootstrap.in b/Modules/Setup.bootstrap.in index f0b3b733d9e74b..692abbaae5e0ff 100644 --- a/Modules/Setup.bootstrap.in +++ b/Modules/Setup.bootstrap.in @@ -9,7 +9,7 @@ atexit atexitmodule.c faulthandler faulthandler.c @INITSYS@ posixmodule.c -_signal signalmodule.c +_signal signalmodule.c -lws2_32 _tracemalloc _tracemalloc.c _suggestions _suggestions.c From 4ef37a4619a4081f2496a35b59ea7fa2ad599b68 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Thu, 17 Jun 2021 18:52:11 +0530 Subject: [PATCH 036/123] build: build winconsoleio and _testconsole MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей Co-authored-by: Naveen M K --- Modules/Setup.bootstrap.in | 2 +- Modules/Setup.stdlib.in | 1 + configure.ac | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Modules/Setup.bootstrap.in b/Modules/Setup.bootstrap.in index 692abbaae5e0ff..0b53035c6787c9 100644 --- a/Modules/Setup.bootstrap.in +++ b/Modules/Setup.bootstrap.in @@ -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 diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 0f5350ddca2e70..78471f1dcf09c7 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -181,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/configure.ac b/configure.ac index 9cee791eb89ade..a58743518dcab2 100644 --- a/configure.ac +++ b/configure.ac @@ -8081,6 +8081,7 @@ PY_STDLIB_MOD([_testsinglephase], [test "$TEST_MODULES" = yes], [test "$ac_cv_fu 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]) From 127f694ba510cbbb6b11d3364d6484540a1eaaea Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Thu, 17 Jun 2021 18:52:12 +0530 Subject: [PATCH 037/123] multiprocessing: expose sem_unlink MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей --- Modules/_multiprocessing/multiprocessing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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} From 89796aaa3c44fe497ec741b18ba854166a8db515 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Thu, 17 Jun 2021 18:52:14 +0530 Subject: [PATCH 038/123] Use cygpty while using isatty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit so that msys shells are detected as interactive ones Co-authored-by: Алексей Co-authored-by: Naveen M K --- Include/iscygpty.h | 41 ++++++++++ Makefile.pre.in | 2 + Modules/_io/fileio.c | 3 +- Modules/main.c | 3 +- Modules/posixmodule.c | 3 +- Objects/fileobject.c | 3 +- Python/bltinmodule.c | 6 +- Python/fileutils.c | 7 +- Python/frozenmain.c | 3 +- Python/iscygpty.c | 185 ++++++++++++++++++++++++++++++++++++++++++ Python/pylifecycle.c | 5 +- 11 files changed, 249 insertions(+), 12 deletions(-) create mode 100644 Include/iscygpty.h create mode 100644 Python/iscygpty.c 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/Makefile.pre.in b/Makefile.pre.in index f34df0d1939343..f04ff08330cebb 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -458,6 +458,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 \ @@ -1039,6 +1040,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 \ 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/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/posixmodule.c b/Modules/posixmodule.c index 3ec5c2e077c987..ab390e824182c9 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -67,6 +67,7 @@ #ifdef __ANDROID__ # undef HAVE_FACCESSAT #endif +#include "iscygpty.h" #include // ctermid() #include // system() @@ -12021,7 +12022,7 @@ os_isatty_impl(PyObject *module, int fd) int return_value; Py_BEGIN_ALLOW_THREADS _Py_BEGIN_SUPPRESS_IPH - return_value = isatty(fd); + return_value = isatty(fd) || is_cygpty(fd); _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS return return_value; 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/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/fileutils.c b/Python/fileutils.c index 9529b14d377c60..20c004f50aeff6 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; } } 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/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/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) { From 9bef749dc71e723a58bd56a157c0a4e84e3b096c Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Thu, 17 Jun 2021 18:52:16 +0530 Subject: [PATCH 039/123] build: link win resource files and build pythonw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей --- Makefile.pre.in | 26 +++++++++++++++++++++----- configure.ac | 27 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index f04ff08330cebb..95de88e4fbfe1c 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 @@ -301,6 +303,7 @@ LIBOBJS= @LIBOBJS@ PYTHON= python$(EXE) BUILDPYTHON= python$(BUILDEXE) +BUILDPYTHONW= pythonw$(BUILDEXE) HOSTRUNNER= @HOSTRUNNER@ @@ -703,7 +706,7 @@ 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 \ +build_all: check-clean-src check-app-store-compliance $(BUILDPYTHON) $(BUILDPYTHONW) platform sharedmods \ gdbhooks Programs/_testembed scripts checksharedmods rundsymutil .PHONY: build_wasm @@ -884,9 +887,21 @@ 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 $@ + +$(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 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 @@ -987,10 +1002,10 @@ $(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) $(MODLIBS) $(SYSLIBS) python_nt.o; \ else true; \ fi @@ -2222,6 +2237,7 @@ 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); \ else \ $(INSTALL_PROGRAM) $(STRIPFLAG) Mac/pythonw $(DESTDIR)$(BINDIR)/python$(LDVERSION)$(EXE); \ fi diff --git a/configure.ac b/configure.ac index a58743518dcab2..c6e106bca611da 100644 --- a/configure.ac +++ b/configure.ac @@ -1810,6 +1810,10 @@ AC_SUBST([LINK_PYTHON_OBJS]) AC_SUBST([AR]) AC_CHECK_TOOLS([AR], [ar aal], [ar]) +# windres program +AC_SUBST(WINDRES) +AC_CHECK_TOOL(WINDRES, windres) + # tweak ARFLAGS only if the user didn't set it on the command line AC_SUBST([ARFLAGS]) if test -z "$ARFLAGS" @@ -7260,10 +7264,33 @@ AC_MSG_RESULT([done]) case $host in *-*-mingw*) LIBS="$LIBS -lversion -lshlwapi -lpathcch" + 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], [ From a381a0881722ff1aa1135c453e5fd87ebfb69857 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Thu, 17 Jun 2021 18:52:18 +0530 Subject: [PATCH 040/123] fileutils: fix isselectable on windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей --- Include/internal/pycore_fileutils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From fd84f7b59a54ca8e23c6d2f8300d30b176241427 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Thu, 17 Jun 2021 18:52:23 +0530 Subject: [PATCH 041/123] configure.ac: fix inet_pton check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей --- configure.ac | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configure.ac b/configure.ac index c6e106bca611da..be7d9cf136700e 100644 --- a/configure.ac +++ b/configure.ac @@ -5590,10 +5590,14 @@ PY_CHECK_NETDB_FUNC([getprotobyname]) 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]) From e0076dc8ce84f88958d8d0a21ec44ee41231f733 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Thu, 17 Jun 2021 18:52:27 +0530 Subject: [PATCH 042/123] pass gen profile ldflags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей --- Makefile.pre.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index 95de88e4fbfe1c..0b6af92f195875 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1005,7 +1005,7 @@ $(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK): \ $(DLLLIBRARY) libpython$(LDVERSION).dll.a: $(LIBRARY_OBJS) python_nt.o if test -n "$(DLLLIBRARY)"; then \ $(LDSHARED) -Wl,--out-implib=$@ -o $(DLLLIBRARY) $^ \ - $(LIBS) $(MODLIBS) $(SYSLIBS) python_nt.o; \ + $(LIBS) $(LDFLAGS_NODIST) $(MODLIBS) $(SYSLIBS) python_nt.o; \ else true; \ fi From ecbb7ac772960a08f11754b5c510d5e97c2ffe21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:52:29 +0530 Subject: [PATCH 043/123] pkg config windows must link ext with python lib --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index be7d9cf136700e..e1e51590bff837 100644 --- a/configure.ac +++ b/configure.ac @@ -6449,8 +6449,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 From a129c8e142b91b3f2d7bd7e462c2e9cb35f25053 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Thu, 17 Jun 2021 18:52:30 +0530 Subject: [PATCH 044/123] importlib bootstrap path sep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Алексей Co-authored-by: Naveen M K --- Lib/importlib/_bootstrap_external.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index eb8fea1aad0919..e9a7a84dd46c90 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -42,6 +42,10 @@ path_separators = ['\\', '/'] else: path_separators = ['/'] + +if 'MSYSTEM' in _os.environ: + 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] From 175d03832059124f40878a4bdeb1490bb5244ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9?= Date: Thu, 17 Jun 2021 18:52:33 +0530 Subject: [PATCH 045/123] warnings fixes --- Modules/_winapi.c | 2 +- Modules/posixmodule.c | 2 +- Modules/socketmodule.h | 2 ++ PC/python_exe.rc | 2 +- PC/pythonw_exe.rc | 2 +- PC/winreg.c | 1 + 6 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Modules/_winapi.c b/Modules/_winapi.c index c6852b0b6b0f95..af40e07b33aa72 100644 --- a/Modules/_winapi.c +++ b/Modules/_winapi.c @@ -1229,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/posixmodule.c b/Modules/posixmodule.c index ab390e824182c9..442a637d01f13b 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -6549,7 +6549,7 @@ os_utime_impl(PyObject *module, path_t *path, PyObject *times, PyObject *ns, /*[clinic end generated code: output=cfcac69d027b82cf input=2fbd62a2f228f8f4]*/ { #ifdef MS_WINDOWS - HANDLE hFile; + HANDLE hFile = 0; FILETIME atime, mtime; #else int result; 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/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 0e070139900a18..2cc175cc4796e0 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -835,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); From 1e2a40149d05913405e2d5be21c200f823e398bc Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Sat, 17 Jun 2023 00:21:25 +0530 Subject: [PATCH 046/123] fix build testinternalcapi --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index e1e51590bff837..d464c8d5e65879 100644 --- a/configure.ac +++ b/configure.ac @@ -8104,7 +8104,7 @@ 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]) From 4401bc925fa80256e2a782774c847596a444fd27 Mon Sep 17 00:00:00 2001 From: jeremyd2019 <4524874+jeremyd2019@users.noreply.github.com> Date: Thu, 17 Jun 2021 18:52:36 +0530 Subject: [PATCH 047/123] clang arm64 Co-authored-by: Naveen M K --- Python/getcompiler.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Python/getcompiler.c b/Python/getcompiler.c index 3e31c80bdf4fb1..7e7be712a6fbdd 100644 --- a/Python/getcompiler.c +++ b/Python/getcompiler.c @@ -7,7 +7,7 @@ // 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__) /* To not break compatibility with things that determine @@ -18,6 +18,8 @@ #define COMP_SEP " " #if defined(__x86_64__) #define ARCH_SUFFIX " 64 bit (AMD64)" +#elif defined(__aarch64__) +#define ARCH_SUFFIX " 64 bit (ARM64)" #else #define ARCH_SUFFIX " 32 bit" #endif @@ -25,7 +27,14 @@ #define COMP_SEP "\n" #define ARCH_SUFFIX "" #endif +#if defined(__clang__) +#define str(x) #x +#define xstr(x) str(x) +#define COMPILER COMP_SEP "[GCC Clang " xstr(__clang_major__) "." \ + xstr(__clang_minor__) "." xstr(__clang_patchlevel__) ARCH_SUFFIX "]" +#else #define COMPILER COMP_SEP "[GCC " __VERSION__ ARCH_SUFFIX "]" +#endif // Generic fallbacks. #elif defined(__cplusplus) #define COMPILER "[C++]" From 6966ce721e385ebb136783b310935aaf15dfcd1f Mon Sep 17 00:00:00 2001 From: jeremyd2019 <4524874+jeremyd2019@users.noreply.github.com> Date: Thu, 17 Jun 2021 18:52:37 +0530 Subject: [PATCH 048/123] configure.ac: set MINGW stack reserve --- configure.ac | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/configure.ac b/configure.ac index d464c8d5e65879..fc2702c5d91dad 100644 --- a/configure.ac +++ b/configure.ac @@ -3839,6 +3839,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]) From acbba6d732ac5bd19580c3a2e80f0eb04c5a2a37 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Fri, 18 Jun 2021 12:38:11 +0530 Subject: [PATCH 049/123] Don't use os.pathsep to find EOF not all distributions in win32 have them as \ instead check using sys.platform Signed-off-by: Naveen M K --- Lib/site.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/site.py b/Lib/site.py index 0153598af1afcd..8ab4b465b9442a 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -435,7 +435,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)' From 8ac9509f9b988e8d63a4dd3c88d795105afab4eb Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Wed, 23 Jun 2021 18:12:12 +0530 Subject: [PATCH 050/123] Fix extension suffix for c-extensions on mingw Python is compiled with various compilers which previously had same platform tags or extension suffix. This can be error prone while loading c-extensions, so now each compiler or runtime has a different extension suffix. Also, changed all extension to end with .pyd rather than .dll file. Fixes https://github.com/msys2/MINGW-packages/issues/8843 Signed-off-by: Naveen M K --- Makefile.pre.in | 6 ++-- Python/dynload_win.c | 6 ---- configure.ac | 77 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 72 insertions(+), 17 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index 0b6af92f195875..c5809d002e2708 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -175,6 +175,7 @@ CONFINCLUDEPY= $(CONFINCLUDEDIR)/python$(LDVERSION) # 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) @@ -1758,8 +1759,7 @@ Python/dynload_hpux.o: $(srcdir)/Python/dynload_hpux.c Makefile Python/dynload_win.o: $(srcdir)/Python/dynload_win.c Makefile $(CC) -c $(PY_CORE_CFLAGS) \ - -DSHLIB_SUFFIX='"$(SHLIB_SUFFIX)"' \ - -DEXT_SUFFIX='"$(EXT_SUFFIX)"' \ + -DPYD_PLATFORM_TAG='"$(PYD_PLATFORM_TAG)"' \ -o $@ $(srcdir)/Python/dynload_win.c Python/sysmodule.o: $(srcdir)/Python/sysmodule.c Makefile $(srcdir)/Include/pydtrace.h @@ -2732,7 +2732,7 @@ 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 \ + if test "$(SHLIB_SUFFIX)" = .dll -o "$(SHLIB_SUFFIX)" = .pyd; then \ $(INSTALL_DATA) $(LDLIBRARY) $(DESTDIR)$(LIBPL) ; \ else \ $(INSTALL_DATA) $(LIBRARY) $(DESTDIR)$(LIBPL)/$(LIBRARY) ; \ diff --git a/Python/dynload_win.c b/Python/dynload_win.c index 386a2c374c273a..b993b006691e4d 100644 --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -10,12 +10,6 @@ #include const char *_PyImport_DynLoadFiletab[] = { -#ifdef EXT_SUFFIX - EXT_SUFFIX, /* include SOABI flags where is encoded debug */ -#endif -#ifdef SHLIB_SUFFIX - "-abi" PYTHON_ABI_STRING SHLIB_SUFFIX, -#endif PYD_TAGGED_SUFFIX, PYD_UNTAGGED_SUFFIX, NULL diff --git a/configure.ac b/configure.ac index fc2702c5d91dad..9f212e9ebfcf79 100644 --- a/configure.ac +++ b/configure.ac @@ -3573,7 +3573,7 @@ if test -z "$SHLIB_SUFFIX"; then *) SHLIB_SUFFIX=.so;; esac case $host_os in - mingw*) SHLIB_SUFFIX=.dll;; + mingw*) SHLIB_SUFFIX=.pyd;; esac fi AC_MSG_RESULT([$SHLIB_SUFFIX]) @@ -6405,6 +6405,68 @@ 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 compiled with clang or gcc and linked +# to msvcrt or ucrt. To avoid conflicts between them +# we are selecting the extension as based on the compiler +# and the runtime they link to +# gcc + x86_64 + msvcrt = cp{version number}-x86_64 +# gcc + i686 + msvcrt = cp{version number}-i686 +# gcc + x86_64 + ucrt = cp{version number}-x86_64-ucrt +# clang + x86_64 + ucrt = cp{version number}-x86_64-clang +# clang + i686 + ucrt = cp{version number}-i686-clang + +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) + case $host in + i686-*-mingw*) + if test -n "${cc_is_clang}"; then + # it is CLANG32 + PYD_PLATFORM_TAG="mingw_i686_clang" + else + if test $linking_to_ucrt = no; then + PYD_PLATFORM_TAG="mingw_i686" + else + PYD_PLATFORM_TAG="mingw_i686_ucrt" + fi + fi + ;; + x86_64-*-mingw*) + if test -n "${cc_is_clang}"; then + # it is CLANG64 + PYD_PLATFORM_TAG="mingw_x86_64_clang" + else + if test $linking_to_ucrt = no; then + PYD_PLATFORM_TAG="mingw_x86_64" + else + PYD_PLATFORM_TAG="mingw_x86_64_ucrt" + fi + fi + ;; + aarch64-*-mingw*) + PYD_PLATFORM_TAG+="mingw_aarch64" + ;; + esac + 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 @@ -6439,7 +6501,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)' @@ -7187,12 +7254,6 @@ case "$ac_cv_computed_gotos" in yes*) AC_DEFINE([HAVE_COMPUTED_GOTOS], [1], [Define if the C compiler supports computed gotos.]) esac -case $host_os in - mingw*) - dnl Synchronized with _PyImport_DynLoadFiletab (dynload_win.c) - dnl Do not use more then one dot on this platform ! - EXT_SUFFIX=-$SOABI$SHLIB_SUFFIX;; -esac case $ac_sys_system in AIX*) From 0ce6d6afa852656b1599b7029894cccf286f813f Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Tue, 21 Sep 2021 21:36:58 +0200 Subject: [PATCH 051/123] Change the `get_platform()` method in sysconfig This would possibly fix building wheels when mingw python is used and would be unique to each python same as EXT_SUFFIX. Also, this modifies the `sys.version` argument to include UCRT in it. Signed-off-by: Naveen M K --- Lib/sysconfig/__init__.py | 16 ++++++++++++++-- Python/getcompiler.c | 4 ++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index 8672ae2ccc6393..20d03b21a0de38 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -606,8 +606,20 @@ def get_platform(): """ if os.name == 'nt': - if 'GCC' in sys.version: - return 'mingw' + if 'gcc' in sys.version.lower(): + if 'ucrt' in sys.version.lower(): + if 'amd64' in sys.version.lower(): + return 'mingw_x86_64_ucrt' + return 'mingw_i686_ucrt' + if 'clang' in sys.version.lower(): + if 'amd64' in sys.version.lower(): + return 'mingw_x86_64_clang' + if 'arm64' in sys.version.lower(): + return 'mingw_aarch64' + return 'mingw_i686_clang' + if 'amd64' in sys.version.lower(): + return 'mingw_x86_64' + return 'mingw_i686' if 'amd64' in sys.version.lower(): return 'win-amd64' if '(arm)' in sys.version.lower(): diff --git a/Python/getcompiler.c b/Python/getcompiler.c index 7e7be712a6fbdd..275f79997b4241 100644 --- a/Python/getcompiler.c +++ b/Python/getcompiler.c @@ -33,8 +33,12 @@ #define COMPILER COMP_SEP "[GCC Clang " xstr(__clang_major__) "." \ xstr(__clang_minor__) "." xstr(__clang_patchlevel__) ARCH_SUFFIX "]" #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++]" From 15a3f1eec76cc59497079aa7292e0f5828b72e2b Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Fri, 23 Jul 2021 08:51:32 +0200 Subject: [PATCH 052/123] tests: fix test_bytes %p has different casing with mingw-w64, but it's implementation defined. Change the test to the mingw-w64 variant. --- Lib/test/test_bytes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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: From 850e8ac898d5d2dc437304e66836a92bb096e816 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Fri, 23 Jul 2021 08:52:50 +0200 Subject: [PATCH 053/123] time: fix strftime not raising for invalid year values It's crt specific and not compiler specific. This fixes a test case in test_time --- Modules/timemodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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, From e98135b7268c04d021f5551c861b307552babd0e Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Fri, 23 Jul 2021 18:23:58 +0200 Subject: [PATCH 054/123] ctypes: find_library('c') should return None with ucrt Just like with MSVC. This fixes a test in test_ctypes. --- Lib/ctypes/util.py | 9 +++++++++ 1 file changed, 9 insertions(+) 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): From c2a3cc56bb6159b109718e002cbbb3a1d024486b Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sun, 25 Jul 2021 13:54:12 +0200 Subject: [PATCH 055/123] build: Disable checks for dlopen/dlfcn While it is (potentially) available with mingw we don't want to use it, so skip any checks for it. --- configure.ac | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 9f212e9ebfcf79..6032d37ba393b5 100644 --- a/configure.ac +++ b/configure.ac @@ -3148,7 +3148,7 @@ 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 pty.h \ @@ -3160,6 +3160,13 @@ 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 @@ -3908,7 +3915,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 @@ -6315,7 +6327,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]) From 28e5ac5a790ae1267cad82e80f8aa7f2c4af0fe9 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sun, 25 Jul 2021 15:46:01 +0200 Subject: [PATCH 056/123] Fix install location of the import library --- Makefile.pre.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index c5809d002e2708..69ce34f2a2c5f2 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -2733,7 +2733,7 @@ libainstall: all scripts if test -d $(LIBRARY); then :; else \ if test "$(PYTHONFRAMEWORKDIR)" = no-framework; then \ if test "$(SHLIB_SUFFIX)" = .dll -o "$(SHLIB_SUFFIX)" = .pyd; then \ - $(INSTALL_DATA) $(LDLIBRARY) $(DESTDIR)$(LIBPL) ; \ + $(INSTALL_DATA) $(LDLIBRARY) $(DESTDIR)$(LIBDIR) ; \ else \ $(INSTALL_DATA) $(LIBRARY) $(DESTDIR)$(LIBPL)/$(LIBRARY) ; \ fi; \ From 01f1a0b3aa91d7162d6d3c723e817ec052ee95a8 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sun, 25 Jul 2021 16:53:35 +0200 Subject: [PATCH 057/123] build: Integrate venvlauncher build/installation into the Makefile This is required for venv creation on Windows. Ideally this would use the venv specific launcher (PC/launcher.c), but a copy of main binary seems to work as well for now. --- Makefile.pre.in | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index 69ce34f2a2c5f2..9b73ed85e0b9a6 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -171,6 +171,7 @@ 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@ @@ -305,6 +306,8 @@ LIBOBJS= @LIBOBJS@ PYTHON= python$(EXE) BUILDPYTHON= python$(BUILDEXE) BUILDPYTHONW= pythonw$(BUILDEXE) +BUILDVENVLAUNCHER= venvlauncher$(BUILDEXE) +BUILDVENVWLAUNCHER= venvwlauncher$(BUILDEXE) HOSTRUNNER= @HOSTRUNNER@ @@ -707,7 +710,7 @@ 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) $(BUILDPYTHONW) platform sharedmods \ +build_all: check-clean-src check-app-store-compliance $(BUILDPYTHON) $(BUILDPYTHONW) $(BUILDVENVLAUNCHER) $(BUILDVENVWLAUNCHER) platform sharedmods \ gdbhooks Programs/_testembed scripts checksharedmods rundsymutil .PHONY: build_wasm @@ -904,6 +907,14 @@ $(BUILDPYTHONW): Programs/python.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) pythonw $(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 +# FIXME: build these from PC/launcher.c instead +$(BUILDVENVLAUNCHER): $(BUILDPYTHON) + cp $(BUILDPYTHON) $(BUILDVENVLAUNCHER) + +# FIXME: build these from PC/launcher.c instead +$(BUILDVENVWLAUNCHER): $(BUILDPYTHONW) + cp $(BUILDPYTHONW) $(BUILDVENVWLAUNCHER) + 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 @@ -2227,7 +2238,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"; \ @@ -2238,6 +2249,8 @@ altbininstall: $(BUILDPYTHON) @FRAMEWORKPYTHONW@ if test "$(PYTHONFRAMEWORKDIR)" = "no-framework" ; then \ $(INSTALL_PROGRAM) $(BUILDPYTHON) $(DESTDIR)$(BINDIR)/python$(LDVERSION)$(EXE); \ $(INSTALL_PROGRAM) $(BUILDPYTHONW) $(DESTDIR)$(BINDIR)/python3w$(EXE); \ + $(INSTALL_PROGRAM) $(BUILDPYTHON) $(DESTDIR)$(VENVLAUNCHERDIR)/python$(EXE); \ + $(INSTALL_PROGRAM) $(BUILDPYTHONW) $(DESTDIR)$(VENVLAUNCHERDIR)/pythonw$(EXE); \ else \ $(INSTALL_PROGRAM) $(STRIPFLAG) Mac/pythonw $(DESTDIR)$(BINDIR)/python$(LDVERSION)$(EXE); \ fi From 704f4bc2fc7194748be4f0a495e78c231c1f5a07 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sun, 1 Aug 2021 15:18:50 +0200 Subject: [PATCH 058/123] configure.ac: set _WIN32_WINNT version --- configure.ac | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/configure.ac b/configure.ac index 6032d37ba393b5..4ef6f2aad0998c 100644 --- a/configure.ac +++ b/configure.ac @@ -4705,6 +4705,11 @@ 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='#' From 2bfc607eddf871b4a30f947c39428d6b1b6f7ee7 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sun, 1 Aug 2021 18:35:17 +0200 Subject: [PATCH 059/123] configure.ac: don't check for clock_ functions They shouldn't be exposed on Windows and lead to winpthread being linked in --- configure.ac | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configure.ac b/configure.ac index 4ef6f2aad0998c..a98afb0d49ddad 100644 --- a/configure.ac +++ b/configure.ac @@ -5683,6 +5683,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" @@ -5721,6 +5724,9 @@ if ! { test "$ac_sys_system" = "Linux-android" && ]) fi + ;; +esac + AC_CHECK_FUNCS([nanosleep], [], [ AC_CHECK_LIB([rt], [nanosleep], [ AC_DEFINE([HAVE_NANOSLEEP], [1]) From ca092e6e415ab87d198f5212a33965f035938ddd Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sun, 8 Aug 2021 10:17:35 +0200 Subject: [PATCH 060/123] expanduser: normpath paths coming from env vars This makes sure we get the same paths as with related functions in pathlib. --- Lib/ntpath.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index af91c6f2785339..f0df0192ce4ad8 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -380,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']) @@ -404,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. From dc7a50d2ac84f1fb5d45788210fb0f8737b8dfa9 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Fri, 18 Jun 2021 17:51:59 +0530 Subject: [PATCH 061/123] CI: test the build and add some mingw specific tests - Use actions/setup-python for setting up correct version in cross build - CI: add cross llvm-mingw jobs - CI: fix sed pattern for python-config.py shebang The shebang (`#!`) does not include `${pkgdir}` so this sed did nothing - CI: remove --without-c-locale-coercion No longer needed since the default now works on Windows - Make sure we always use the stdlib distutils - CI: update actions and images ubuntu-18.04 is gone now - CI: updates for mstorsjo/llvm-mingw moving to 20.04 - CI: 3.10 -> 3.11 - CI: remove `--with-system-libmpdec` for cross builds - CI: move gcc cross build to Ubuntu for newer mingw-w64 The build requires headers that are only available in v12 (processsnapshot.h) and Arch is still on v11. So move things to Ubuntu. Similarely to other cross builds remove the system libs configure flags, those were never there and now fail properly with the 3.12 build system. Co-authored-by: Christoph Reiter --- .github/workflows/mingw.yml | 289 ++++++++++++++++++++++++++++++++ mingw_ignorefile.txt | 34 ++++ mingw_smoketests.py | 317 ++++++++++++++++++++++++++++++++++++ 3 files changed, 640 insertions(+) create mode 100644 .github/workflows/mingw.yml create mode 100644 mingw_ignorefile.txt create mode 100644 mingw_smoketests.py diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml new file mode 100644 index 00000000000000..62abd585fa77f7 --- /dev/null +++ b/.github/workflows/mingw.yml @@ -0,0 +1,289 @@ +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 }}" == "CLANG64" ]]; 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" + cd _build + MSYSTEM= ./python.exe -m test -j8 --ignorefile "$IGNOREFILE" -W + + - name: Run broken tests + continue-on-error: true + shell: msys2 {0} + run: | + IGNOREFILE="$(pwd)/mingw_ignorefile.txt" + cd _build + MSYSTEM= ./python.exe -m test -j8 --matchfile "$IGNOREFILE" -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 + + - name: Run Smoke Test (installed) + shell: msys2 {0} + run: | + export PYTHONTZPATH="${MINGW_PREFIX}/share/zoneinfo" + SMOKETESTS="$(pwd)/mingw_smoketests.py" + cd _build + cd python_pkgdir/${MINGW_PREFIX}/bin + ./python.exe "$SMOKETESTS" + MSYSTEM= ./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/mingw_ignorefile.txt b/mingw_ignorefile.txt new file mode 100644 index 00000000000000..dc3802e6c51313 --- /dev/null +++ b/mingw_ignorefile.txt @@ -0,0 +1,34 @@ +ctypes.test.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.datetimetester.TestLocalTimeDisambiguation_Fast.* +test.datetimetester.TestLocalTimeDisambiguation_Pure.* +test.test_cmath.CMathTests.test_specific_values +test.test_cmd_line_script.CmdLineTest.test_consistent_sys_path_for_direct_execution +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_math.MathTests.* +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_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 +test.test_venv.BasicTest.* +test.test_venv.EnsurePipTest.* +# flaky +test.test__xxsubinterpreters.* +test.test_asyncio.test_subprocess.SubprocessProactorTests.test_stdin_broken_pipe \ No newline at end of file diff --git a/mingw_smoketests.py b/mingw_smoketests.py new file mode 100644 index 00000000000000..755d27efa43d2a --- /dev/null +++ b/mingw_smoketests.py @@ -0,0 +1,317 @@ +#!/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 "MSYSTEM" in os.environ: + SEP = "/" +else: + SEP = "\\" + +_UCRT = "clang" in sysconfig.get_platform() or "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")]) + + 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 + 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) + 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) + + 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 + from pathlib import Path + + with tempfile.TemporaryDirectory() as tmppro: + subprocess.check_call([sys.executable, "-m", "ensurepip", "--user"]) + 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( + [sys.executable, "-c", "import struct"], + ) + subprocess.check_call( + [ + sys.executable, + "-m", + "pip", + "install", + "wheel", + ], + ) + subprocess.check_call( + [ + sys.executable, + "-m", + "pip", + "install", + tmppro, + ], + ) + subprocess.check_call( + [sys.executable, "-c", "import cwrapper"], + ) + + + +def suite(): + return unittest.TestLoader().loadTestsFromName(__name__) + + +if __name__ == '__main__': + unittest.main(defaultTest='suite') From 0d59851737b43e6494364174eaa3c12ec13e2676 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Tue, 12 Oct 2021 18:35:21 +0530 Subject: [PATCH 062/123] Define PY3_DLLNAME to fix build PY3_DLLNAME is used in Python/pathconfig.c but isn't defined. I guess it is useless but it will fix the build. Co-authored-by: jeremyd2019 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index a98afb0d49ddad..6e3e1ce54e9a9d 100644 --- a/configure.ac +++ b/configure.ac @@ -5290,7 +5290,7 @@ then *-*-mingw*) DYNLOADFILE="dynload_win.o" extra_machdep_objs="$extra_machdep_objs PC/dl_nt.o" - CFLAGS_NODIST="$CFLAGS_NODIST -DMS_DLL_ID='\"$VERSION\"'" + CFLAGS_NODIST="$CFLAGS_NODIST -DMS_DLL_ID='\"$VERSION\"' -DPY3_DLLNAME='L\"$DLLLIBRARY\"'" ;; esac fi From 1dd219c6bdfd1ab5592bb51344edf8a4eebfc662 Mon Sep 17 00:00:00 2001 From: Long Nguyen Date: Sun, 14 Nov 2021 14:55:35 +0700 Subject: [PATCH 063/123] _testconsole.c: Fix casing & path sep --- PC/_testconsole.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From d3f80d1a6ec82b5a4651c146c53f59982e5be8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Fri, 19 Nov 2021 12:18:06 +0200 Subject: [PATCH 064/123] Return consistent architecture markers for python on mingw/armv7 --- Lib/sysconfig/__init__.py | 2 ++ Python/getcompiler.c | 2 ++ configure.ac | 3 +++ 3 files changed, 7 insertions(+) diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index 20d03b21a0de38..afe71cfad8c1a0 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -616,6 +616,8 @@ def get_platform(): return 'mingw_x86_64_clang' if 'arm64' in sys.version.lower(): return 'mingw_aarch64' + if 'arm' in sys.version.lower(): + return 'mingw_armv7' return 'mingw_i686_clang' if 'amd64' in sys.version.lower(): return 'mingw_x86_64' diff --git a/Python/getcompiler.c b/Python/getcompiler.c index 275f79997b4241..4b0b9b328ba2f8 100644 --- a/Python/getcompiler.c +++ b/Python/getcompiler.c @@ -20,6 +20,8 @@ #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 diff --git a/configure.ac b/configure.ac index 6e3e1ce54e9a9d..3780a233694dca 100644 --- a/configure.ac +++ b/configure.ac @@ -6489,6 +6489,9 @@ case $host_os in aarch64-*-mingw*) PYD_PLATFORM_TAG+="mingw_aarch64" ;; + armv7-*-mingw*) + PYD_PLATFORM_TAG+="mingw_armv7" + ;; esac AC_MSG_RESULT($PYD_PLATFORM_TAG) esac From 79ac1d2299613924e71c825fef9a34ef57223407 Mon Sep 17 00:00:00 2001 From: jeremyd2019 Date: Sat, 22 Jan 2022 11:33:00 -0800 Subject: [PATCH 065/123] mingw_smoketests: fix _UCRT condition The prior detection missed ARM (32 and 64) being UCRT. Since there are fewer non-UCRT platforms, list those two instead of trying to extend the list of UCRT. --- mingw_smoketests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mingw_smoketests.py b/mingw_smoketests.py index 755d27efa43d2a..d40509812de265 100644 --- a/mingw_smoketests.py +++ b/mingw_smoketests.py @@ -34,7 +34,7 @@ else: SEP = "\\" -_UCRT = "clang" in sysconfig.get_platform() or "ucrt" in sysconfig.get_platform() +_UCRT = sysconfig.get_platform() not in ('mingw_x86_64', 'mingw_i686') class Tests(unittest.TestCase): From ed53bd22351b7cce7aa58fc51147fe4ae6946476 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Wed, 19 Jan 2022 19:39:20 +0530 Subject: [PATCH 066/123] Modify `sys.winver` to match upstream With this change `sys.winver` will add the Arch for which python was compiled on, for example in 32-bits, `sys.winver` will be `3.10-32`, for arm32 it would be `3.10-arm32` and so on. See https://github.com/msys2-contrib/cpython-mingw/issues/40 --- configure.ac | 22 ++++++++++++++++++---- mingw_smoketests.py | 9 ++++++++- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index 3780a233694dca..cea86759654290 100644 --- a/configure.ac +++ b/configure.ac @@ -5288,10 +5288,24 @@ then esac case $host in *-*-mingw*) - DYNLOADFILE="dynload_win.o" - extra_machdep_objs="$extra_machdep_objs PC/dl_nt.o" - CFLAGS_NODIST="$CFLAGS_NODIST -DMS_DLL_ID='\"$VERSION\"' -DPY3_DLLNAME='L\"$DLLLIBRARY\"'" - ;; + 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]) diff --git a/mingw_smoketests.py b/mingw_smoketests.py index d40509812de265..f1943912613dec 100644 --- a/mingw_smoketests.py +++ b/mingw_smoketests.py @@ -214,7 +214,14 @@ def test_platform_things(self): ext_suffixes = importlib.machinery.EXTENSION_SUFFIXES self.assertTrue(ext_suffix in ext_suffixes) self.assertTrue(".pyd" in ext_suffixes) - self.assertEqual(sys.winver, ".".join(map(str, sys.version_info[:2]))) + 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) From e7fb3ba2e34ca4a202616b7fe6e95816111f2b95 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Wed, 19 Jan 2022 20:01:45 +0530 Subject: [PATCH 067/123] Change user site-packages path to include the environment info This should avoid mixing of user site-packages between python from various environments. Previously, the user site-packages should be located at `~/.local/lib/python3.10` for all environment including 32-bits variants which caused problems with 64-bit trying to load 32-bit extensions. Now this path will be changed to `~/.local/lib/python3.10-`, for example, in CLANG64 this would be `~/.local/lib/python3.10-mingw_x86_64_clang`. Fixes https://github.com/msys2-contrib/cpython-mingw/issues/40 --- Lib/site.py | 28 +++++++++++++++++++++++++--- Lib/sysconfig/__init__.py | 26 +++++++++++++++----------- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/Lib/site.py b/Lib/site.py index 8ab4b465b9442a..0317e71f2f9473 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -314,6 +314,26 @@ 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(): + if 'ucrt' in sys.version.lower(): + if 'amd64' in sys.version.lower(): + return 'mingw_x86_64_ucrt' + return 'mingw_i686_ucrt' + if 'clang' in sys.version.lower(): + if 'amd64' in sys.version.lower(): + return 'mingw_x86_64_clang' + if 'arm64' in sys.version.lower(): + return 'mingw_aarch64' + if 'arm' in sys.version.lower(): + return 'mingw_armv7' + return 'mingw_i686_clang' + if 'amd64' in sys.version.lower(): + return 'mingw_x86_64' + return 'mingw_i686' + return sys.platform # Same to sysconfig.get_path('purelib', os.name+'_user') def _get_path(userbase): @@ -325,9 +345,11 @@ def _get_path(userbase): implementation = _get_implementation() implementation_lower = implementation.lower() - if os.name == 'nt' and not _POSIX_BUILD: - ver_nodot = sys.winver.replace('.', '') - return f'{userbase}\\{implementation}{ver_nodot}\\site-packages' + if os.name == 'nt': + 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' diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index afe71cfad8c1a0..48a1edd876f500 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -144,20 +144,20 @@ def joinuser(*args): _INSTALL_SCHEMES |= { # NOTE: When modifying "purelib" scheme, update site._get_path() too. 'nt_user': { - 'stdlib': '{userbase}/lib/{implementation}{py_version_short}{abi_thread}', - 'platstdlib': '{userbase}/lib/{implementation}{py_version_short}{abi_thread}', - 'purelib': '{userbase}/lib/{implementation}{py_version_short}{abi_thread}/site-packages', - 'platlib': '{userbase}/lib/{implementation}{py_version_short}{abi_thread}/site-packages', - 'include': '{userbase}/include/{implementation}{py_version_short}{abi_thread}', + '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}', }, @@ -493,6 +493,10 @@ 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' and not _POSIX_BUILD: _init_non_posix(_CONFIG_VARS) @@ -583,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. From 255ce8d550971c2b6263f0a45c7d8566d7795842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Thu, 21 Apr 2022 14:25:51 +0300 Subject: [PATCH 068/123] configure: Include a header in the check for _beginthread Previously, the test tried compiling a call to the _beginthread function without either declaring the function (and its parameters) or including the corresponding header. Since Clang 15 (which still is under development, so this may still change before it's released) [1], implicit function declarations are a hard error by default, when building code in C99 mode (or newer). [1] https://github.com/llvm/llvm-project/commit/7d644e1215b376ec5e915df9ea2eeb56e2d94626 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index cea86759654290..4ddf96f31e70fd 100644 --- a/configure.ac +++ b/configure.ac @@ -2960,7 +2960,7 @@ AC_MSG_RESULT([$with_nt_threads]) if test $with_nt_threads = yes ; then AC_MSG_CHECKING([whether linking with nt-threads work]) AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[]],[[_beginthread(0, 0, 0);]]) + AC_LANG_PROGRAM([[#include ]],[[_beginthread(0, 0, 0);]]) ], [AC_MSG_RESULT([yes])], [AC_MSG_ERROR([failed to link with nt-threads])]) From 4b6d9f19d8f6ed39eed910411a874302e2716b6c Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Fri, 22 Apr 2022 17:31:19 +0200 Subject: [PATCH 069/123] configure.ac: Default to --without-c-locale-coercion on Windows --with-c-locale-coercion otherwise defaults to yes and enables code that isn't compatible on Windows, mainly because the feature is Unix related. Default to "no" on Windows instead. Fixes #36 --- configure.ac | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 4ddf96f31e70fd..4a3cba50a23efb 100644 --- a/configure.ac +++ b/configure.ac @@ -5177,11 +5177,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 From 818d2123a05ff5364f0d096e2ef650db5497b5da Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Sun, 5 Jun 2022 20:28:53 +0530 Subject: [PATCH 070/123] Fix failing tests - In `test_sysconfig`, ignore `test_sysconfig.TestSysConfig.test_user_similar` test failure. - Copy `get_platform()` from from distutils.utils to test_importlib/test_windows.py. - In `test_tcl`, ignore `test_tcl.TclTest.testLoadWithUNC` test failure. - Disable `test.test_asynchat.TestAsynchat.test_line_terminator2`, seems flaky. - skip some more flaky tests - some basic fixes for test_getpath - test_sysconfig.py: fix tests related to mingw --- Lib/test/test_getpath.py | 3 +++ Lib/test/test_importlib/test_windows.py | 17 +++++++++++++++++ Lib/test/test_sysconfig.py | 8 ++++++-- mingw_ignorefile.txt | 13 +++++++++++-- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_getpath.py b/Lib/test/test_getpath.py index d5dcdad9614ecc..0c4ab74bd1b9a6 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"..\.." @@ -1067,6 +1069,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) diff --git a/Lib/test/test_importlib/test_windows.py b/Lib/test/test_importlib/test_windows.py index 8a9a8fffcd10d4..84c717596153ae 100644 --- a/Lib/test/test_importlib/test_windows.py +++ b/Lib/test/test_importlib/test_windows.py @@ -24,6 +24,23 @@ def get_platform(): 'x64' : 'win-amd64', 'arm' : 'win-arm32', } + if os.name == 'nt': + if 'gcc' in sys.version.lower(): + if 'ucrt' in sys.version.lower(): + if 'amd64' in sys.version.lower(): + return 'mingw_x86_64_ucrt' + return 'mingw_i686_ucrt' + if 'clang' in sys.version.lower(): + if 'amd64' in sys.version.lower(): + return 'mingw_x86_64_clang' + if 'arm64' in sys.version.lower(): + return 'mingw_aarch64' + if 'arm' in sys.version.lower(): + return 'mingw_armv7' + return 'mingw_i686_clang' + if 'amd64' in sys.version.lower(): + return 'mingw_x86_64' + return 'mingw_i686' 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/mingw_ignorefile.txt b/mingw_ignorefile.txt index dc3802e6c51313..e692d7ff26eb4d 100644 --- a/mingw_ignorefile.txt +++ b/mingw_ignorefile.txt @@ -1,4 +1,4 @@ -ctypes.test.test_loading.LoaderTest.test_load_dll_with_flags +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 @@ -29,6 +29,15 @@ test.test_strptime.TimeRETests.test_compile test.test_tools.test_i18n.Test_pygettext.test_POT_Creation_Date test.test_venv.BasicTest.* test.test_venv.EnsurePipTest.* +test.test_sysconfig.TestSysConfig.test_user_similar +test.test_tcl.TclTest.testLoadWithUNC +test.test_wmi # flaky test.test__xxsubinterpreters.* -test.test_asyncio.test_subprocess.SubprocessProactorTests.test_stdin_broken_pipe \ No newline at end of file +test.test_asyncio.test_subprocess.SubprocessProactorTests.test_stdin_broken_pipe +test.test_asynchat.TestAsynchat.test_line_terminator2 +test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_gc_aclose_09 +test.test_concurrent_futures.ThreadPoolShutdownTest.test_interpreter_shutdown +test.test_asynchat.TestNotConnected.test_disallow_negative_terminator +test.test_logging.SysLogHandlerTest.* +test.test_logging.IPv6SysLogHandlerTest.* From cca744730967e8640f1d0e65bf5cf07654614c61 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Thu, 22 Sep 2022 17:51:33 +0200 Subject: [PATCH 071/123] Don't change os.sep with an empty MSYSTEM env var, not just a missing one Up until now this didn't really happen when calling from cygwin because empty env vars were removed before Python would run. But https://github.com/msys2/msys2-runtime/pull/101 changed that. To avoid breaking users that did something like MSYSTEM= python ... not only check that MSYSTEM isn't set but also that it isn't empty when deciding if os.sep/os.altsep should be switched. Also, guard the msystem env check to execute only on MINGW Co-authored-by: Naveen M K --- Lib/importlib/_bootstrap_external.py | 2 +- Lib/ntpath.py | 2 +- Python/pathconfig.c | 20 +++++++++++--------- mingw_smoketests.py | 2 +- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index e9a7a84dd46c90..520025e7aa2d11 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -43,7 +43,7 @@ else: path_separators = ['/'] -if 'MSYSTEM' in _os.environ: +if _os.environ.get('MSYSTEM', ''): path_separators = path_separators[::-1] # Assumption made in _path_join() diff --git a/Lib/ntpath.py b/Lib/ntpath.py index f0df0192ce4ad8..4186b7a84571da 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -20,7 +20,7 @@ import genericpath from genericpath import * -if sys.platform == "win32" and "MSYSTEM" in os.environ: +if sys.platform == "win32" and os.environ.get("MSYSTEM", ""): sep = '/' altsep = '\\' else: diff --git a/Python/pathconfig.c b/Python/pathconfig.c index dacf360c62437f..1465e5fd460886 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 @@ -49,7 +49,6 @@ Py_StartsWithW(const wchar_t * str, const wchar_t * prefix) char Py_GetSepA(const char *name) { - char* msystem = (char*)2; /* So that non Windows use / as sep */ static char sep = '\0'; #ifdef _WIN32 /* https://msdn.microsoft.com/en-gb/library/windows/desktop/aa365247%28v=vs.85%29.aspx @@ -64,12 +63,14 @@ Py_GetSepA(const char *name) if (sep != '\0') return sep; #if defined(__MINGW32__) - msystem = Py_GETENV("MSYSTEM"); -#endif - if (msystem != NULL) + char* msystem = getenv("MSYSTEM"); + if (msystem != NULL && strcmp(msystem, "") != 0) sep = '/'; else sep = '\\'; +#else + sep = SEP; +#endif return sep; } @@ -102,7 +103,6 @@ Py_NormalizeSepsA(char *name) wchar_t Py_GetSepW(const wchar_t *name) { - char* msystem = (char*)2; /* So that non Windows use / as sep */ static wchar_t sep = L'\0'; #ifdef _WIN32 /* https://msdn.microsoft.com/en-gb/library/windows/desktop/aa365247%28v=vs.85%29.aspx @@ -117,12 +117,14 @@ Py_GetSepW(const wchar_t *name) if (sep != L'\0') return sep; #if defined(__MINGW32__) - msystem = Py_GETENV("MSYSTEM"); -#endif - if (msystem != NULL) + char* msystem = getenv("MSYSTEM"); + if (msystem != NULL && strcmp(msystem, "") != 0) sep = L'/'; else sep = L'\\'; +#else + sep = SEP; +#endif return sep; } diff --git a/mingw_smoketests.py b/mingw_smoketests.py index f1943912613dec..ff288820ea80da 100644 --- a/mingw_smoketests.py +++ b/mingw_smoketests.py @@ -29,7 +29,7 @@ import unittest import sysconfig -if "MSYSTEM" in os.environ: +if os.environ.get("MSYSTEM", ""): SEP = "/" else: SEP = "\\" From d4ea2cff4eebe22b4675c37f50d356035f6b6503 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Sun, 18 Jun 2023 13:12:56 +0530 Subject: [PATCH 072/123] def VPATH when compiling `Python/sysmodule.c` --- Makefile.pre.in | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.pre.in b/Makefile.pre.in index 9b73ed85e0b9a6..2519156e3a3a8b 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1776,6 +1776,7 @@ Python/dynload_win.o: $(srcdir)/Python/dynload_win.c Makefile 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 From d4f04a145d59fdd6548696dca2c2356159c34fae Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Sun, 18 Jun 2023 13:17:48 +0530 Subject: [PATCH 073/123] Make `_Py_CheckPython3` extern it's declared in headers but defined as static here, remove it also run `_Py_CheckPython3` only when using MSVC --- Python/dynload_win.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Python/dynload_win.c b/Python/dynload_win.c index b993b006691e4d..56588ef21bf1ef 100644 --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -152,8 +152,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,9 +206,9 @@ 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 */ +#ifdef _MSC_VER + _Py_CheckPython3(); +#endif /* _MSC_VER */ wchar_t *wpathname = PyUnicode_AsWideCharString(pathname, NULL); if (wpathname == NULL) From 6267ec9c51817e559a3cc07fd3a707c36f40a56f Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Sun, 18 Jun 2023 14:20:44 +0530 Subject: [PATCH 074/123] link with bcrypt --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 4a3cba50a23efb..1f926992fd1ccc 100644 --- a/configure.ac +++ b/configure.ac @@ -7379,7 +7379,7 @@ AC_MSG_RESULT([done]) # For mingw build need additional library for linking case $host in *-*-mingw*) - LIBS="$LIBS -lversion -lshlwapi -lpathcch" + LIBS="$LIBS -lversion -lshlwapi -lpathcch -lbcrypt" AC_PROG_AWK if test "$AWK" = "gawk"; then awk_extra_flag="--non-decimal-data" From 821e3dd0491e602faeca8f69c3bd3dbd0f92b562 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Sun, 18 Jun 2023 14:29:50 +0530 Subject: [PATCH 075/123] correctly find native python it's required for regen-modules --- configure.ac | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 1f926992fd1ccc..ae3103a4193a67 100644 --- a/configure.ac +++ b/configure.ac @@ -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]) From 9fc4705bb5734c428d63182fe76af41a534b8efb Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Sun, 18 Jun 2023 14:30:09 +0530 Subject: [PATCH 076/123] Add extra flags for `_bootstrap_python` --- Makefile.pre.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index 2519156e3a3a8b..a118bcb4ca01c2 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1481,7 +1481,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) From b4a36690e02db37fb33aef5e4f89183cba950e0c Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Tue, 20 Jun 2023 18:32:58 +0530 Subject: [PATCH 077/123] posixmodule: undefine `HAVE_OPENDIR` 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. --- Modules/posixmodule.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 442a637d01f13b..15307172b8ebb6 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -394,6 +394,12 @@ corresponding Unix manual entries for more information on calls."); #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 From dc3fd8df36e83604719a38b61e427ee2a0a0d8b1 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Tue, 20 Jun 2023 18:43:59 +0530 Subject: [PATCH 078/123] getpath.py: add support for mingw - always normalize the PREFIX to an absolute path - use `/` when MSYSTEM is defined --- Include/pylifecycle.h | 1 + Modules/getpath.c | 9 ++++++++ Modules/getpath.py | 48 +++++++++++++++++++++++++++++++------------ Python/fileutils.c | 41 +++++++++++++++++++++--------------- Python/pathconfig.c | 2 +- 5 files changed, 71 insertions(+), 30 deletions(-) diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h index 9f842cb3b5a3b6..6eb171db082240 100644 --- a/Include/pylifecycle.h +++ b/Include/pylifecycle.h @@ -21,6 +21,7 @@ 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 *); diff --git a/Modules/getpath.c b/Modules/getpath.c index d0128b20faeeae..eff9ccc7de5584 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -71,6 +71,7 @@ getpath_abspath(PyObject *Py_UNUSED(self), PyObject *args) if (path) { wchar_t *abs; if (_Py_abspath((const wchar_t *)_Py_normpath(path, -1), &abs) == 0 && abs) { + abs = _Py_normpath(abs, -1); r = PyUnicode_FromWideChar(abs, -1); PyMem_RawFree((void *)abs); } else { @@ -927,6 +928,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 +964,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..93e99a3b0eca05 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 = '/' # ****************************************************************************** @@ -264,10 +286,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 +524,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) @@ -605,7 +627,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 +637,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: @@ -668,7 +690,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 +703,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 +744,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) @@ -750,8 +772,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 diff --git a/Python/fileutils.c b/Python/fileutils.c index 20c004f50aeff6..b56ef5d20adeb9 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -2194,7 +2194,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; @@ -2499,11 +2503,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)) @@ -2516,15 +2525,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 @@ -2533,8 +2542,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)) { @@ -2546,18 +2555,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]))) { @@ -2566,7 +2575,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 { @@ -2577,7 +2586,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; } @@ -2587,7 +2596,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/pathconfig.c b/Python/pathconfig.c index 1465e5fd460886..d9e7c7c3f26a78 100644 --- a/Python/pathconfig.c +++ b/Python/pathconfig.c @@ -128,7 +128,7 @@ Py_GetSepW(const wchar_t *name) return sep; } -static wchar_t +wchar_t Py_GetAltSepW(const wchar_t *name) { char sep = Py_GetSepW(name); From bd5f070fdd4735a4478fd72776cd03cd1289215a Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Tue, 20 Jun 2023 20:17:23 +0530 Subject: [PATCH 079/123] Don't build _posixsubprocess on Windows. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index ae3103a4193a67..c1500371320f98 100644 --- a/configure.ac +++ b/configure.ac @@ -8070,7 +8070,6 @@ 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]) @@ -8121,6 +8120,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], From 145be6f2b910a98f2a163654bea83204fb3ff552 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Tue, 20 Jun 2023 20:35:27 +0530 Subject: [PATCH 080/123] `_ssl`: link with `ws2_32` --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index c1500371320f98..990c3e94c1e013 100644 --- a/configure.ac +++ b/configure.ac @@ -8198,7 +8198,7 @@ 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]) From 24cb8aa855d7419721f92da01055ea71cb6aaac1 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Tue, 20 Jun 2023 23:00:43 +0530 Subject: [PATCH 081/123] Always normalize path in abspath --- Lib/ntpath.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 4186b7a84571da..added25ebd4360 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -591,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 From 581b8745d7d78d4ead77d84eb757f72bc958bc95 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Wed, 21 Jun 2023 15:42:39 +0530 Subject: [PATCH 082/123] Include `winsock.h` when checking for netdb function also move `gethostname` to that check --- configure.ac | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 990c3e94c1e013..cb1460bceba536 100644 --- a/configure.ac +++ b/configure.ac @@ -5376,7 +5376,7 @@ AC_CHECK_FUNCS([ \ 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 \ @@ -5619,7 +5619,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 @@ -5628,6 +5634,7 @@ 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]) dnl PY_CHECK_SOCKET_FUNC(FUNCTION) AC_DEFUN([PY_CHECK_SOCKET_FUNC], [PY_CHECK_FUNC([$1], [ From a008a9d6ce01682cfb49491feac5b6edeab0b5cc Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Fri, 23 Jun 2023 20:13:23 +0530 Subject: [PATCH 083/123] include `_multiprocessing/semaphore.c` on win32 while building `_multiprocessing` extension also, always build that module on win32 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index cb1460bceba536..372ec832ac1855 100644 --- a/configure.ac +++ b/configure.ac @@ -8089,7 +8089,7 @@ PY_STDLIB_MOD_SIMPLE([_zoneinfo]) dnl multiprocessing modules PY_STDLIB_MOD([_multiprocessing], - [], [test "$ac_cv_func_sem_unlink" = "yes"], + [], [test "$ac_cv_func_sem_unlink" = "yes" -o "$MACHDEP" = "win32"], [-I\$(srcdir)/Modules/_multiprocessing]) PY_STDLIB_MOD([_posixshmem], [], [test "$have_posix_shmem" = "yes"], From 77720cf6df624338eeb8dc31300380b7e40aeaff Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Fri, 23 Jun 2023 20:15:38 +0530 Subject: [PATCH 084/123] configure: build `mmap` module on win32 --- configure.ac | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 372ec832ac1855..6ed487a69b6214 100644 --- a/configure.ac +++ b/configure.ac @@ -8108,7 +8108,9 @@ 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" From 55176299f44b1b92528d490bc073b6cdd0c437a0 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Fri, 23 Jun 2023 20:49:15 +0530 Subject: [PATCH 085/123] venv creation fixes --- Lib/venv/__init__.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) 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) From daf52f64f5d474a57160eccd295ac1bebe089b11 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Fri, 23 Jun 2023 23:34:01 +0530 Subject: [PATCH 086/123] move the `shutdown` function where `winsock.h` is included --- configure.ac | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 6ed487a69b6214..6cf05555f8dc90 100644 --- a/configure.ac +++ b/configure.ac @@ -5390,7 +5390,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 \ @@ -5635,6 +5635,7 @@ 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], [ From a6f09894f0c0e8ac667a9258efcac926ff0eb001 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Fri, 23 Jun 2023 21:47:32 +0200 Subject: [PATCH 087/123] configure.ac: set BUILDEXEEXT and EXEEXT --- configure.ac | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/configure.ac b/configure.ac index 6cf05555f8dc90..0ff773ba55b15c 100644 --- a/configure.ac +++ b/configure.ac @@ -1440,6 +1440,7 @@ AC_ARG_WITH([suffix], [Emscripten/browser*], [EXEEXT=.js], [Emscripten/node*], [EXEEXT=.js], [WASI/*], [EXEEXT=.wasm], + [MINGW*], [EXEEXT=.exe], [EXEEXT=] ) ]) @@ -1466,6 +1467,10 @@ else fi rmdir CaseSensitiveTestDir +AS_CASE([$ac_sys_system], + [MINGW], [BUILDEXEEXT=".exe"] +) + case $ac_sys_system in hp*|HP*) case $CC in From a68ed875a8f6cf9a2f37252cc1d5a220029df638 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Fri, 23 Jun 2023 23:42:39 +0200 Subject: [PATCH 088/123] configure.ac: fix building some test modules they are guarded by dlopen being present, but if module loading is enabled is more correct. --- configure.ac | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index 0ff773ba55b15c..b30e068f062cf2 100644 --- a/configure.ac +++ b/configure.ac @@ -5321,8 +5321,11 @@ 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 @@ -8235,21 +8238,21 @@ 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], [], [-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], + [test "$TEST_MODULES" = yes], [test "$have_libffi" = yes -a "$have_dynamic_loading" = yes], [], [$LIBM]) 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]) From 63740f0eacc453bbf3e89d38401066b5f4e61598 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Sun, 25 Jun 2023 17:20:52 +0530 Subject: [PATCH 089/123] Don't convert `sysconfig.get_config_var('VPATH')` to an absolute path this is required for venv to work properly --- Makefile.pre.in | 1 - configure.ac | 10 +--------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index a118bcb4ca01c2..06fd12ee94cdc5 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -148,7 +148,6 @@ datarootdir= @datarootdir@ # Locations needed for semi-native fixup of sysconfig. srcdir_b2h= @srcdir_b2h@ -VPATH_b2h= @VPATH_b2h@ abs_srcdir_b2h= @abs_srcdir_b2h@ abs_builddir_b2h= @abs_builddir_b2h@ prefix_b2h= @prefix_b2h@ diff --git a/configure.ac b/configure.ac index b30e068f062cf2..8efeffb01446d2 100644 --- a/configure.ac +++ b/configure.ac @@ -924,12 +924,8 @@ fi # 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. # -# Also, getpath.c uses GetModuleFileNameW (replacing \ with /) and -# compares that with the define VPATH (passed in via command-line) -# to determine whether it's the build- or the installed-Python. -# # To work around these issues a set of _b2h variables are created: -# VPATH_b2h, prefix_b2h, srcdir_b2h, abs_srcdir_b2h +# 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. @@ -956,10 +952,6 @@ AC_DEFUN([ABS_PATH_HOST], AC_SUBST([$1]) ]) -AC_MSG_CHECKING(absolute host location of VPATH) -ABS_PATH_HOST([VPATH_b2h],[srcdir]) -AC_MSG_RESULT([$VPATH_b2h]) - AC_MSG_CHECKING(absolute host location of prefix) ABS_PATH_HOST([prefix_b2h],[prefix]) AC_MSG_RESULT([$prefix_b2h]) From bf445a626f7c8b78f509cc32a00d9a92ba438296 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Sun, 25 Jun 2023 17:24:11 +0530 Subject: [PATCH 090/123] Always convert `/` to `\\` before passing though pathcch functions they don't seems to handle `/` as path separator correctly --- Include/pylifecycle.h | 2 ++ Python/fileutils.c | 22 ++++++++++++++++++---- Python/pathconfig.c | 16 ++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h index 6eb171db082240..29632398332887 100644 --- a/Include/pylifecycle.h +++ b/Include/pylifecycle.h @@ -28,6 +28,8 @@ 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/Python/fileutils.c b/Python/fileutils.c index b56ef5d20adeb9..f6d364c84fd0cd 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -2149,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); @@ -2401,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; diff --git a/Python/pathconfig.c b/Python/pathconfig.c index d9e7c7c3f26a78..fe3dbe22d217b6 100644 --- a/Python/pathconfig.c +++ b/Python/pathconfig.c @@ -154,6 +154,22 @@ Py_NormalizeSepsW(wchar_t *name) } } +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 */ From 12e0b3d9fa61fe7c53fa59630dd5eb0d67538df9 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Sun, 25 Jun 2023 17:26:55 +0530 Subject: [PATCH 091/123] Build venvlauncher.exe from PC/launcher.c - Use function available in original msvcrt.dll _wdupenv_s -> _wgetenv_s fread_s -> fread - Add a test for checking the new launchers --- Makefile.pre.in | 20 ++++++++++++-------- PC/launcher.c | 29 +++++++++++++++++------------ PC/pylauncher.rc | 20 ++++++++++---------- mingw_smoketests.py | 12 ++++++++++++ 4 files changed, 51 insertions(+), 30 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index 06fd12ee94cdc5..b2fd276b5fa5ca 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -899,6 +899,12 @@ pythonw_exe.o: $(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 $@ +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 @@ -906,13 +912,11 @@ $(BUILDPYTHONW): Programs/python.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) pythonw $(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 -# FIXME: build these from PC/launcher.c instead -$(BUILDVENVLAUNCHER): $(BUILDPYTHON) - cp $(BUILDPYTHON) $(BUILDVENVLAUNCHER) +$(BUILDVENVLAUNCHER): $(BUILDPYTHON) venvlauncher.o $(srcdir)/PC/launcher.c + $(LINKCC) -D_CONSOLE -DVENV_REDIRECT $(PY_STDMODULE_CFLAGS) -municode -static -static-libgcc -static-libstdc++ venvlauncher.o $(srcdir)/PC/launcher.c -o $@ -lversion -# FIXME: build these from PC/launcher.c instead -$(BUILDVENVWLAUNCHER): $(BUILDPYTHONW) - cp $(BUILDPYTHONW) $(BUILDVENVWLAUNCHER) +$(BUILDVENVWLAUNCHER): $(BUILDPYTHONW) venvwlauncher.o $(srcdir)/PC/launcher.c + $(LINKCC) -D_WINDOWS -DVENV_REDIRECT $(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 @@ -2249,8 +2253,8 @@ altbininstall: $(BUILDPYTHON) @FRAMEWORKPYTHONW@ if test "$(PYTHONFRAMEWORKDIR)" = "no-framework" ; then \ $(INSTALL_PROGRAM) $(BUILDPYTHON) $(DESTDIR)$(BINDIR)/python$(LDVERSION)$(EXE); \ $(INSTALL_PROGRAM) $(BUILDPYTHONW) $(DESTDIR)$(BINDIR)/python3w$(EXE); \ - $(INSTALL_PROGRAM) $(BUILDPYTHON) $(DESTDIR)$(VENVLAUNCHERDIR)/python$(EXE); \ - $(INSTALL_PROGRAM) $(BUILDPYTHONW) $(DESTDIR)$(VENVLAUNCHERDIR)/pythonw$(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 diff --git a/PC/launcher.c b/PC/launcher.c index 8e60ab9303cb95..796bf73957e4ec 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); 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/mingw_smoketests.py b/mingw_smoketests.py index ff288820ea80da..2324c7c8918145 100644 --- a/mingw_smoketests.py +++ b/mingw_smoketests.py @@ -194,6 +194,18 @@ def test_venv_creation(self): 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()) From c05639006a4f049a3515cd68aab3976011081bf5 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Sun, 25 Jun 2023 19:39:30 +0530 Subject: [PATCH 092/123] getpath.py: fix `dirname` also, fix finding prefix when in a venv --- Modules/getpath.c | 12 ++++++++++++ Modules/getpath.py | 3 +++ 2 files changed, 15 insertions(+) diff --git a/Modules/getpath.c b/Modules/getpath.c index eff9ccc7de5584..59a57fb2f995e4 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -92,6 +92,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); } @@ -108,6 +114,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); } diff --git a/Modules/getpath.py b/Modules/getpath.py index 93e99a3b0eca05..3adcf040bbd76f 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -581,6 +581,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 From 364b1ef9a9b10050eb744d44f137056b9a487396 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Fri, 28 Jul 2023 20:13:58 +0200 Subject: [PATCH 093/123] getpath: use normpath on all generated paths Instead of just calling normpath in abspath just call normpath on all the config results. This makes sure we don't change getpath.py too much and still cover all outputs. This fixes sys.exec_prefix not matching sys.prefix, see https://github.com/msys2-contrib/cpython-mingw/issues/142 --- Lib/test/test_getpath.py | 6 ++++++ Modules/getpath.c | 21 ++++++++++++++++++++- Modules/getpath.py | 30 ++++++++++++++++-------------- 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/Lib/test/test_getpath.py b/Lib/test/test_getpath.py index 0c4ab74bd1b9a6..f43877b59aa541 100644 --- a/Lib/test/test_getpath.py +++ b/Lib/test/test_getpath.py @@ -928,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 @@ -1106,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/Modules/getpath.c b/Modules/getpath.c index 59a57fb2f995e4..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) { @@ -71,7 +90,6 @@ getpath_abspath(PyObject *Py_UNUSED(self), PyObject *args) if (path) { wchar_t *abs; if (_Py_abspath((const wchar_t *)_Py_normpath(path, -1), &abs) == 0 && abs) { - abs = _Py_normpath(abs, -1); r = PyUnicode_FromWideChar(abs, -1); PyMem_RawFree((void *)abs); } else { @@ -574,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}, diff --git a/Modules/getpath.py b/Modules/getpath.py index 3adcf040bbd76f..e743161eed3501 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -235,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 @@ -678,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: @@ -765,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 @@ -800,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 '') From f7bdd04fff1fe7f8a81b57ed3b1e378cc02a7a94 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Fri, 28 Jul 2023 20:14:38 +0200 Subject: [PATCH 094/123] pathconfig: normpath sys.path[0] this gets added as first element in sys.path. use normpath to make sure it uses the right path separator --- Python/pathconfig.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/pathconfig.c b/Python/pathconfig.c index fe3dbe22d217b6..69b751fc22c774 100644 --- a/Python/pathconfig.c +++ b/Python/pathconfig.c @@ -639,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; } From a9d209aa9bce621c50c365e8a2310b75d838efa8 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Fri, 28 Jul 2023 20:15:01 +0200 Subject: [PATCH 095/123] smoketests: add some tests for sys/site paths --- mingw_smoketests.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/mingw_smoketests.py b/mingw_smoketests.py index 2324c7c8918145..de2a878b738f97 100644 --- a/mingw_smoketests.py +++ b/mingw_smoketests.py @@ -238,6 +238,29 @@ def test_platform_things(self): self.assertEqual(platform.system(), "Windows") self.assertTrue(isinstance(sys.api_version, int) and sys.api_version > 0) + 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(): From 8d4891fc33019d8a391ee8b2ba2ac2a8c22ee1e2 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Sat, 5 Aug 2023 14:10:27 +0530 Subject: [PATCH 096/123] Search DLLs only on paths added using `add_dll_directory()`. This is the default behavior on upstream Python. We previously reverted this behavior because it broke some use cases. The old behavior can be restored by setting the environment variable PYTHONLEGACYWINDOWSDLLLOADING to 1. Fixes https://github.com/msys2-contrib/cpython-mingw/issues/48 Also fixes https://github.com/msys2-contrib/cpython-mingw/issues/141 Signed-off-by: Naveen M K --- .github/workflows/mingw.yml | 10 +++++----- Lib/test/__main__.py | 8 ++++++++ Python/dynload_win.c | 16 ++++++++++++++-- Tools/build/check_extension_modules.py | 4 ++++ mingw_smoketests.py | 3 +++ 5 files changed, 34 insertions(+), 7 deletions(-) diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index 62abd585fa77f7..00b23bb05c63b4 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -124,15 +124,15 @@ jobs: 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: | - export PYTHONTZPATH="${MINGW_PREFIX}/share/zoneinfo" SMOKETESTS="$(pwd)/mingw_smoketests.py" - cd _build - cd python_pkgdir/${MINGW_PREFIX}/bin - ./python.exe "$SMOKETESTS" - MSYSTEM= ./python.exe "$SMOKETESTS" + ${MINGW_PREFIX}/bin/python.exe "$SMOKETESTS" + MSYSTEM= ${MINGW_PREFIX}/bin/python.exe "$SMOKETESTS" - name: Compress if: always() 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/Python/dynload_win.c b/Python/dynload_win.c index 56588ef21bf1ef..7e0eca3a5adf51 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 @@ -206,6 +207,18 @@ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix, dl_funcptr p; char funcname[258], *import_python; + 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 */ @@ -230,8 +243,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_WITH_ALTERED_SEARCH_PATH); + hDLL = LoadLibraryExW(wpathname, NULL, load_library_flags); Py_END_ALLOW_THREADS PyMem_Free(wpathname); 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/mingw_smoketests.py b/mingw_smoketests.py index de2a878b738f97..6722ddb3047c5b 100644 --- a/mingw_smoketests.py +++ b/mingw_smoketests.py @@ -34,6 +34,9 @@ else: SEP = "\\" +if sysconfig.is_python_build(): + os.environ["PYTHONLEGACYWINDOWSDLLLOADING"] = "1" + _UCRT = sysconfig.get_platform() not in ('mingw_x86_64', 'mingw_i686') From 80c4dd32d1b3a6aac6cfdcc47693f5395affb4fa Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Mon, 21 Aug 2023 08:19:28 +0200 Subject: [PATCH 097/123] Build and install libpython3.dll This provides the limited ABI as a separate DLL, forwarding to the real one. This makes linking with "-lpython3" work. Fixes #147 --- Makefile.pre.in | 15 +++++++++++++-- PC/python3dll.c | 8 ++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index b2fd276b5fa5ca..d3464df176b6ec 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -288,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@ @@ -710,7 +712,7 @@ list-targets: .PHONY: build_all build_all: check-clean-src check-app-store-compliance $(BUILDPYTHON) $(BUILDPYTHONW) $(BUILDVENVLAUNCHER) $(BUILDVENVWLAUNCHER) platform sharedmods \ - gdbhooks Programs/_testembed scripts checksharedmods rundsymutil + gdbhooks Programs/_testembed scripts checksharedmods rundsymutil $(ABI3DLLLIBRARY) $(ABI3LDLIBRARY) .PHONY: build_wasm build_wasm: check-clean-src $(BUILDPYTHON) platform sharedmods \ @@ -899,6 +901,9 @@ pythonw_exe.o: $(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 $@ @@ -1024,6 +1029,10 @@ $(DLLLIBRARY) libpython$(LDVERSION).dll.a: $(LIBRARY_OBJS) 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. @@ -2268,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 \ @@ -2751,6 +2761,7 @@ libainstall: all scripts if test "$(PYTHONFRAMEWORKDIR)" = no-framework; then \ 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; \ @@ -3059,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/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) From 09a4b1c217dfce06e0e7057c4da890f157b7d2c3 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sun, 27 Aug 2023 15:00:32 +0200 Subject: [PATCH 098/123] Port GetPythonImport() to mingw This looks for DLL names in the import table but while with MSVC the DLL is named python311.dll in our case it is named libpython3.11.dll. Adjust the strings and lengths accordingly. --- Python/dynload_win.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/Python/dynload_win.c b/Python/dynload_win.c index 7e0eca3a5adf51..eac065eeaae2f1 100644 --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -37,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; @@ -103,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; @@ -119,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 @@ -310,9 +320,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); From ab9c0a74507e641946e7bb82b20d30e82ed42c4b Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sun, 27 Aug 2023 16:04:50 +0200 Subject: [PATCH 099/123] LoadLibraryExW: make sure to only use backslashes for paths It seems like in case the path passed to it is absolute, but contains forward slashes then LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR does not work and DLLs in the same directory as the extension are not considered. This occurs in our fork because in MSYS2-mode the extension loader will normalize to forward slashes before. Normalize everything to backslashes again before passing it to LoadLibraryExW. Fixes #151 --- Python/dynload_win.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Python/dynload_win.c b/Python/dynload_win.c index eac065eeaae2f1..8242e947745572 100644 --- a/Python/dynload_win.c +++ b/Python/dynload_win.c @@ -233,10 +233,22 @@ dl_funcptr _PyImport_FindSharedFuncptrWindows(const char *prefix, _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); { From 8fcb6a5b42392de9b7eeea744067728a8fa06b72 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Wed, 8 Nov 2023 19:44:22 +0530 Subject: [PATCH 100/123] mingw_smoketests: add a test to check if sysconfig returns correct values for `LIBDIR` --- mingw_smoketests.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/mingw_smoketests.py b/mingw_smoketests.py index 6722ddb3047c5b..7d9429a98c1f21 100644 --- a/mingw_smoketests.py +++ b/mingw_smoketests.py @@ -218,6 +218,9 @@ def test_platform_things(self): 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")) @@ -241,6 +244,21 @@ def test_platform_things(self): 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 From 27e6f5aba2246a375ac533cb126dd36b4ac72342 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Mon, 1 Jan 2024 13:42:01 +0530 Subject: [PATCH 101/123] Add missing library for multiprocessing module `ws2_32` is required when building that module --- configure.ac | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 8efeffb01446d2..995ceefc9e0bd2 100644 --- a/configure.ac +++ b/configure.ac @@ -4390,6 +4390,13 @@ AS_CASE([$MACHDEP], [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 detect sqlite3 from Emscripten emport PY_CHECK_EMSCRIPTEN_PORT([LIBSQLITE3], [-sUSE_SQLITE3]) @@ -8091,7 +8098,7 @@ PY_STDLIB_MOD_SIMPLE([_zoneinfo]) dnl multiprocessing modules PY_STDLIB_MOD([_multiprocessing], [], [test "$ac_cv_func_sem_unlink" = "yes" -o "$MACHDEP" = "win32"], - [-I\$(srcdir)/Modules/_multiprocessing]) + [-I\$(srcdir)/Modules/_multiprocessing], [$MULTIPROCESSING_LIBS]) PY_STDLIB_MOD([_posixshmem], [], [test "$have_posix_shmem" = "yes"], [$POSIXSHMEM_CFLAGS], [$POSIXSHMEM_LIBS]) From bf8be996c35462837be94e950b4f6ab24b5cd196 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Mon, 1 Jan 2024 14:14:45 +0530 Subject: [PATCH 102/123] selectmodule: add support for MINGW --- Modules/selectmodule.c | 6 +++--- configure.ac | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) 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/configure.ac b/configure.ac index 995ceefc9e0bd2..da2e13a00a27f5 100644 --- a/configure.ac +++ b/configure.ac @@ -4396,6 +4396,11 @@ AS_CASE([$MACHDEP], [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 detect sqlite3 from Emscripten emport PY_CHECK_EMSCRIPTEN_PORT([LIBSQLITE3], [-sUSE_SQLITE3]) @@ -8087,7 +8092,7 @@ PY_STDLIB_MOD_SIMPLE([_opcode]) PY_STDLIB_MOD_SIMPLE([_pickle]) 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]) From 30c4df4854c90e5d53dba3fc812ca215eb27dfb0 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Mon, 1 Jan 2024 14:19:41 +0530 Subject: [PATCH 103/123] readline: disable on MINGW --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index da2e13a00a27f5..6542a6b5b954bb 100644 --- a/configure.ac +++ b/configure.ac @@ -7953,6 +7953,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], [ From 41ab1f5bef9d49536d06c9fd119367de7f0451f9 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Mon, 1 Jan 2024 14:25:11 +0530 Subject: [PATCH 104/123] Add libraries to fix compile of ctypes on MINGW --- configure.ac | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 6542a6b5b954bb..364bccf993bc2d 100644 --- a/configure.ac +++ b/configure.ac @@ -4402,6 +4402,12 @@ AS_CASE([$MACHDEP], [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 detect sqlite3 from Emscripten emport PY_CHECK_EMSCRIPTEN_PORT([LIBSQLITE3], [-sUSE_SQLITE3]) @@ -8177,7 +8183,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] @@ -8252,7 +8258,7 @@ 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 "$have_dynamic_loading" = yes], - [], [$LIBM]) + [], [$LIBM $CTYPES_LIBS]) dnl Limited API template modules. dnl Emscripten does not support shared libraries yet. From a1c78397c3e8c5d95b541656a8d57b19e1d0f3a4 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Mon, 1 Jan 2024 15:04:43 +0530 Subject: [PATCH 105/123] Enable `_uuid` on MINGW --- configure.ac | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 364bccf993bc2d..1009df58e02e6e 100644 --- a/configure.ac +++ b/configure.ac @@ -4408,6 +4408,12 @@ AS_CASE([$MACHDEP], [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]) @@ -8212,8 +8218,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], From ef51e5b096c46bf24f146c81f6bf56ec6866c14e Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Fri, 2 Feb 2024 14:43:29 +0530 Subject: [PATCH 106/123] mingw_smoketests: disable distutils related tests --- mingw_smoketests.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/mingw_smoketests.py b/mingw_smoketests.py index 7d9429a98c1f21..dc314232e604a1 100644 --- a/mingw_smoketests.py +++ b/mingw_smoketests.py @@ -61,24 +61,24 @@ def test_userdir_path_sep(self): 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_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 2c20fbb27da4f51996dc351700a8fecc322bd6a0 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Mon, 19 Feb 2024 22:09:25 +0530 Subject: [PATCH 107/123] configure.ac: use autoconf 2.69 llvm-mingw only has autoconf 2.69 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 1009df58e02e6e..1a6addac446fd5 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/]) From 786c189cccc298209060860bb27d156694853cb0 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Tue, 20 Feb 2024 11:04:39 +0530 Subject: [PATCH 108/123] CI: clean up ignored tests Remove tests which are passing now, or no longer exist, and move tests which only fail with msvcrt into a separate file. --- .github/workflows/mingw.yml | 12 ++++++++++-- Lib/test/test_wmi.py | 2 +- mingw_ignorefile.txt | 24 +++--------------------- mingw_ignorefile_msvcrt.txt | 8 ++++++++ 4 files changed, 22 insertions(+), 24 deletions(-) create mode 100644 mingw_ignorefile_msvcrt.txt diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index 00b23bb05c63b4..49b371b3856f4f 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -89,16 +89,24 @@ jobs: 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" -W + 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" -W + MSYSTEM= ./python.exe -m test -j8 --matchfile "$IGNOREFILE" --matchfile "$IGNOREFILE_EXTRA" -W - name: Install shell: msys2 {0} 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/mingw_ignorefile.txt b/mingw_ignorefile.txt index e692d7ff26eb4d..550e72413a4f4e 100644 --- a/mingw_ignorefile.txt +++ b/mingw_ignorefile.txt @@ -2,16 +2,11 @@ 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.datetimetester.TestLocalTimeDisambiguation_Fast.* -test.datetimetester.TestLocalTimeDisambiguation_Pure.* -test.test_cmath.CMathTests.test_specific_values -test.test_cmd_line_script.CmdLineTest.test_consistent_sys_path_for_direct_execution 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_math.MathTests.* test.test_ntpath.NtCommonTest.test_import test.test_os.StatAttributeTests.test_stat_block_device test.test_os.TestScandir.test_attributes @@ -23,21 +18,8 @@ test.test_site._pthFileTests.* test.test_site.HelperFunctionsTests.* test.test_site.StartupImportTests.* test.test_ssl.* -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 test.test_venv.BasicTest.* test.test_venv.EnsurePipTest.* -test.test_sysconfig.TestSysConfig.test_user_similar -test.test_tcl.TclTest.testLoadWithUNC -test.test_wmi -# flaky -test.test__xxsubinterpreters.* -test.test_asyncio.test_subprocess.SubprocessProactorTests.test_stdin_broken_pipe -test.test_asynchat.TestAsynchat.test_line_terminator2 -test.test_asyncgen.AsyncGenAsyncioTest.test_async_gen_asyncio_gc_aclose_09 -test.test_concurrent_futures.ThreadPoolShutdownTest.test_interpreter_shutdown -test.test_asynchat.TestNotConnected.test_disallow_negative_terminator -test.test_logging.SysLogHandlerTest.* -test.test_logging.IPv6SysLogHandlerTest.* + +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 From e620935ae6cc9aec6aa9e3b4dd8d52c6541620cf Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Thu, 11 Jul 2024 13:58:20 +0530 Subject: [PATCH 109/123] gcc: make `incompatible-pointer-types` as warning gcc 14 changed `incompatible-pointer-types` from warning to error for now, make it as warning to avoid build failure --- configure.ac | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/configure.ac b/configure.ac index 1a6addac446fd5..b022ee754fae45 100644 --- a/configure.ac +++ b/configure.ac @@ -2618,6 +2618,14 @@ case $GCC in yes) CFLAGS_NODIST="$CFLAGS_NODIST -std=c11" + # GCC 14 and later error out with incompatible-pointer-types + # this only occues on mingw + case $host in + *-*-mingw*) + CFLAGS_NODIST="$CFLAGS_NODIST -Wno-error=incompatible-pointer-types" + ;; + esac + PY_CHECK_CC_WARNING([enable], [extra], [if we can add -Wextra]) AS_VAR_IF([ac_cv_enable_extra_warning], [yes], [CFLAGS_NODIST="$CFLAGS_NODIST -Wextra"]) From d7cb2fab7a803390da33b052e48c07444140f906 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Sun, 8 Sep 2024 15:04:42 +0530 Subject: [PATCH 110/123] test_makefile: normalize path so that it passes on mingw --- Lib/test/test_tools/test_makefile.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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]) From bb0bc39c0f299ef4962aba48badc1ed6d188dca9 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Thu, 12 Sep 2024 17:42:31 +0530 Subject: [PATCH 111/123] `sysconfig.get_platform()`: use consistent naming Currently, the platform names are hardcoded in many places, and are not named consistently. This commit fixes it by standardizing the names to be returned by `sysconfig.get_platform`. The naming is based on https://github.com/msys2-contrib/cpython-mingw/issues/167#issuecomment-1952215164 Similarly, `EXT_SUFFIX` is also standardized to be consistent with the platform names. Signed-off-by: Naveen M K --- Lib/site.py | 32 +++++++++------ Lib/sysconfig/__init__.py | 32 +++++++++------ Lib/test/test_importlib/test_windows.py | 38 +++++++++-------- configure.ac | 54 ++++++++++--------------- mingw_smoketests.py | 2 +- 5 files changed, 83 insertions(+), 75 deletions(-) diff --git a/Lib/site.py b/Lib/site.py index 0317e71f2f9473..c216d485b8f872 100644 --- a/Lib/site.py +++ b/Lib/site.py @@ -318,21 +318,27 @@ def joinuser(*args): 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(): - if 'amd64' in sys.version.lower(): - return 'mingw_x86_64_ucrt' - return 'mingw_i686_ucrt' + platform += '_ucrt' + else: + platform += "_msvcrt" + if 'clang' in sys.version.lower(): - if 'amd64' in sys.version.lower(): - return 'mingw_x86_64_clang' - if 'arm64' in sys.version.lower(): - return 'mingw_aarch64' - if 'arm' in sys.version.lower(): - return 'mingw_armv7' - return 'mingw_i686_clang' - if 'amd64' in sys.version.lower(): - return 'mingw_x86_64' - return 'mingw_i686' + platform += "_llvm" + else: + platform += "_gnu" + + return platform return sys.platform # Same to sysconfig.get_path('purelib', os.name+'_user') diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index 48a1edd876f500..19ca3bfd7fbc5e 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -611,21 +611,27 @@ 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(): - if 'amd64' in sys.version.lower(): - return 'mingw_x86_64_ucrt' - return 'mingw_i686_ucrt' + platform += '_ucrt' + else: + platform += "_msvcrt" + if 'clang' in sys.version.lower(): - if 'amd64' in sys.version.lower(): - return 'mingw_x86_64_clang' - if 'arm64' in sys.version.lower(): - return 'mingw_aarch64' - if 'arm' in sys.version.lower(): - return 'mingw_armv7' - return 'mingw_i686_clang' - if 'amd64' in sys.version.lower(): - return 'mingw_x86_64' - return 'mingw_i686' + 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/test/test_importlib/test_windows.py b/Lib/test/test_importlib/test_windows.py index 84c717596153ae..03be17b0c4c087 100644 --- a/Lib/test/test_importlib/test_windows.py +++ b/Lib/test/test_importlib/test_windows.py @@ -25,22 +25,28 @@ def get_platform(): 'arm' : 'win-arm32', } if os.name == 'nt': - if 'gcc' in sys.version.lower(): - if 'ucrt' in sys.version.lower(): - if 'amd64' in sys.version.lower(): - return 'mingw_x86_64_ucrt' - return 'mingw_i686_ucrt' - if 'clang' in sys.version.lower(): - if 'amd64' in sys.version.lower(): - return 'mingw_x86_64_clang' - if 'arm64' in sys.version.lower(): - return 'mingw_aarch64' - if 'arm' in sys.version.lower(): - return 'mingw_armv7' - return 'mingw_i686_clang' - if 'amd64' in sys.version.lower(): - return 'mingw_x86_64' - return 'mingw_i686' + 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/configure.ac b/configure.ac index b022ee754fae45..c93dcf60cc7e0e 100644 --- a/configure.ac +++ b/configure.ac @@ -6492,16 +6492,12 @@ AC_C_BIGENDIAN AC_SUBST(PYD_PLATFORM_TAG) # Special case of PYD_PLATFORM_TAG with python build with mingw. -# Python can with compiled with clang or gcc and linked -# to msvcrt or ucrt. To avoid conflicts between them -# we are selecting the extension as based on the compiler -# and the runtime they link to -# gcc + x86_64 + msvcrt = cp{version number}-x86_64 -# gcc + i686 + msvcrt = cp{version number}-i686 -# gcc + x86_64 + ucrt = cp{version number}-x86_64-ucrt -# clang + x86_64 + ucrt = cp{version number}-x86_64-clang -# clang + i686 + ucrt = cp{version number}-i686-clang - +# 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*) @@ -6520,38 +6516,32 @@ esac case $host_os in mingw*) AC_MSG_CHECKING(PYD_PLATFORM_TAG) + PYD_PLATFORM_TAG="mingw" case $host in i686-*-mingw*) - if test -n "${cc_is_clang}"; then - # it is CLANG32 - PYD_PLATFORM_TAG="mingw_i686_clang" - else - if test $linking_to_ucrt = no; then - PYD_PLATFORM_TAG="mingw_i686" - else - PYD_PLATFORM_TAG="mingw_i686_ucrt" - fi - fi + PYD_PLATFORM_TAG+="_i686" ;; x86_64-*-mingw*) - if test -n "${cc_is_clang}"; then - # it is CLANG64 - PYD_PLATFORM_TAG="mingw_x86_64_clang" - else - if test $linking_to_ucrt = no; then - PYD_PLATFORM_TAG="mingw_x86_64" - else - PYD_PLATFORM_TAG="mingw_x86_64_ucrt" - fi - fi + PYD_PLATFORM_TAG+="_x86_64" ;; aarch64-*-mingw*) - PYD_PLATFORM_TAG+="mingw_aarch64" + PYD_PLATFORM_TAG+="_aarch64" ;; armv7-*-mingw*) - PYD_PLATFORM_TAG+="mingw_armv7" + 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 diff --git a/mingw_smoketests.py b/mingw_smoketests.py index dc314232e604a1..59057bb72c4ea0 100644 --- a/mingw_smoketests.py +++ b/mingw_smoketests.py @@ -37,7 +37,7 @@ if sysconfig.is_python_build(): os.environ["PYTHONLEGACYWINDOWSDLLLOADING"] = "1" -_UCRT = sysconfig.get_platform() not in ('mingw_x86_64', 'mingw_i686') +_UCRT = 'ucrt' in sysconfig.get_platform() class Tests(unittest.TestCase): From 174b23c74833ae78e4811cddf304c5eeccb12bcd Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Fri, 1 Nov 2024 11:47:01 +0530 Subject: [PATCH 112/123] Add ucrt to version string --- Python/getcompiler.c | 5 +++++ configure.ac | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Python/getcompiler.c b/Python/getcompiler.c index 4b0b9b328ba2f8..dd4d3c8f101468 100644 --- a/Python/getcompiler.c +++ b/Python/getcompiler.c @@ -32,8 +32,13 @@ #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 "]" diff --git a/configure.ac b/configure.ac index c93dcf60cc7e0e..752a5a6f234f75 100644 --- a/configure.ac +++ b/configure.ac @@ -6534,7 +6534,7 @@ case $host_os in if test $linking_to_ucrt = no; then PYD_PLATFORM_TAG+="_msvcrt" else - PYD_PLATFORM_TAG += "_ucrt" + PYD_PLATFORM_TAG+="_ucrt" fi if test -n "${cc_is_clang}"; then # it is CLANG32 From 0649b85789af42e9656882acdce071e269c24f93 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Sat, 2 Nov 2024 11:30:54 +0530 Subject: [PATCH 113/123] socketmodule: fix captilization of headers `Rpc` should be `rpc`. It error's out in cross-compilation. --- Modules/socketmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 9cdbda2cff0b47..8878dd39154970 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -279,7 +279,7 @@ 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 From 3b5339c6b2203e26875449609d57db76d57b6d6e Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Sun, 22 Dec 2024 13:45:24 +0100 Subject: [PATCH 114/123] mingw_smoketests: build extension in a venv ensurepip no longer works since we implement PEP 668, so do everything in a venv. --- mingw_smoketests.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/mingw_smoketests.py b/mingw_smoketests.py index 59057bb72c4ea0..addcd68292b35b 100644 --- a/mingw_smoketests.py +++ b/mingw_smoketests.py @@ -291,10 +291,14 @@ def test_c_ext_build(self): import sys import subprocess import textwrap + import venv from pathlib import Path with tempfile.TemporaryDirectory() as tmppro: - subprocess.check_call([sys.executable, "-m", "ensurepip", "--user"]) + 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( @@ -346,20 +350,11 @@ def test_c_ext_build(self): ) ) subprocess.check_call( - [sys.executable, "-c", "import struct"], - ) - subprocess.check_call( - [ - sys.executable, - "-m", - "pip", - "install", - "wheel", - ], + [venv_exe, "-c", "import struct"], ) subprocess.check_call( [ - sys.executable, + venv_exe, "-m", "pip", "install", @@ -367,7 +362,7 @@ def test_c_ext_build(self): ], ) subprocess.check_call( - [sys.executable, "-c", "import cwrapper"], + [venv_exe, "-c", "import cwrapper"], ) From 385c6d6f04c44c5b35567bbeb90deacc2aced5b4 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Mon, 23 Dec 2024 06:03:11 +0100 Subject: [PATCH 115/123] venvlauncher: try looking for the versioned .exe first and then fall back By default venvlauncher only looks for python.exe in the python "home", which fails in the MSYS2 case where there could be multiple python versions in the same prefix i.e. python.exe could be 3.12 while the venv was created with python3.13.exe. Upstream CPython doesn't have this problem since they never have multiple Python versions in the same prefix. On the other hand the Python test suite (test_sysconfig specifically) creates a venv for the uninstalled Python build dir, where only python.exe exists. To cover both cases we first try to look for the python3.XY.exe and then fall back to python.exe. --- Makefile.pre.in | 4 ++-- PC/launcher.c | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index d3464df176b6ec..aed64743c3902a 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -918,10 +918,10 @@ $(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 $(PY_STDMODULE_CFLAGS) -municode -static -static-libgcc -static-libstdc++ venvlauncher.o $(srcdir)/PC/launcher.c -o $@ -lversion + $(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 $(PY_STDMODULE_CFLAGS) -mwindows -municode -static -static-libgcc -static-libstdc++ venvwlauncher.o $(srcdir)/PC/launcher.c -o $@ -lversion + $(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 diff --git a/PC/launcher.c b/PC/launcher.c index 796bf73957e4ec..6c8cd760818517 100644 --- a/PC/launcher.c +++ b/PC/launcher.c @@ -1931,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"); @@ -1949,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"\""); From 9ba27e70d84b87fdb5649592c5132ca56effacd6 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Tue, 24 Dec 2024 05:56:31 +0100 Subject: [PATCH 116/123] fixup! CI: test the build and add some mingw specific tests --- .github/workflows/mingw.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index 49b371b3856f4f..29c28ba415d870 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -55,7 +55,7 @@ jobs: run: | set -ex - if [[ "${{ matrix.msystem }}" == "CLANG64" ]]; then + if [[ "${{ matrix.msystem }}" == CLANG* ]]; then export CC=clang export CXX=clang++ fi From 82c98eb9a527a4a14f5f36280db9abed4723d8d8 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Wed, 25 Dec 2024 22:20:39 +0530 Subject: [PATCH 117/123] configure.ac: disable check sizeof pid_t for mingw it's not correct to check for it in mingw --- configure.ac | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 752a5a6f234f75..b2b0eb55a9cb2e 100644 --- a/configure.ac +++ b/configure.ac @@ -3336,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]) From 50dc0eb3190229ebfd8ee894f5bf43e4cfff0111 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Wed, 25 Dec 2024 23:16:27 +0530 Subject: [PATCH 118/123] pytime.c: change the order of includes a structure wasn't defined without this change --- Python/pytime.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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() From 3545d4487f91d68e73bf6b26806d77f34c3e03a5 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Wed, 25 Dec 2024 23:17:38 +0530 Subject: [PATCH 119/123] mmapmodule: disable exceptions handling `__try` and `__except` are not available in gcc --- Modules/mmapmodule.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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; \ From 6166614ed7bb5102ba8d38f1890435244c9501a5 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Thu, 26 Dec 2024 00:11:39 +0530 Subject: [PATCH 120/123] Makefile.pre.in: allow pgo to fail This partially reverts https://github.com/python/cpython/commit/6ab6040054e5ca2d3eb7833dc8bf4eb0bbaa0aac#diff-1f0a8db227d22005511b0d90f5339b97db345917b863954b3b3ccb9ec308767cL709 --- Makefile.pre.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index aed64743c3902a..5af177048eee1e 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -768,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 @@ -817,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"; \ From 70b1f4d4c1e3a60209082ab5f7306539ba6d8b78 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Fri, 27 Dec 2024 13:17:52 +0530 Subject: [PATCH 121/123] Export `Py_GetBuildInfo` to enable stable ABI previously for some reason `Py_GetBuildInfo` wasn't exported and it failed some tests. Not sure how it doesn't fail on other platforms. --- Makefile.pre.in | 2 +- Modules/getbuildinfo.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index 5af177048eee1e..30d17142bd2086 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1726,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)`\"" \ 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) { From 26b8ab0637dc6b6396fd8ab8782fa4c30732e6f5 Mon Sep 17 00:00:00 2001 From: Christoph Reiter Date: Fri, 27 Dec 2024 16:45:59 +0100 Subject: [PATCH 122/123] calculate_log2_keysize: use Windows implementation not just with MSVC The inner loop of build_indices_generic() otherwise never stops since it gets a full dict and can never find a free slot. --- Objects/dictobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; From 00d0dfbe205fd41e34f1c6a7557e9afec2c3b6c3 Mon Sep 17 00:00:00 2001 From: Naveen M K Date: Fri, 27 Dec 2024 21:30:24 +0530 Subject: [PATCH 123/123] posixmodule: use lowercase for headers it fails in cross-builds --- Modules/posixmodule.c | 36274 ++++++++++++++++++++-------------------- 1 file changed, 18137 insertions(+), 18137 deletions(-) diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 15307172b8ebb6..fa14d4199fb04f 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -1,18137 +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 "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); -} +/* 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); +}