Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Include/internal/pycore_fileutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,9 @@ extern int _Py_GetTicksPerSecond(long *ticks_per_second);
// Export for '_testcapi' shared extension
PyAPI_FUNC(int) _Py_IsValidFD(int fd);

// Export for '_remote_debugging' shared extension
PyAPI_FUNC(PyObject*) _Py_strerror(int code);

#ifdef __cplusplus
}
#endif
Expand Down
21 changes: 15 additions & 6 deletions Modules/_remote_debugging/subprocess.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
******************************************************************************/

#include "_remote_debugging.h"
#include "pycore_fileutils.h" // _Py_strerror()

#ifndef MS_WINDOWS
#include <unistd.h>
Expand Down Expand Up @@ -229,9 +230,13 @@ get_child_pids_platform(pid_t target_pid, int recursive, pid_array_t *result)
if (entry == NULL) {
if (errno != 0) {
int err = errno;
_set_debug_oserror_from_errno_with_filename(err, "/proc",
"Failed to read process directory '/proc': %s",
strerror(err));
PyObject *message = _Py_strerror(err);
if (message != NULL) {
_set_debug_oserror_from_errno_with_filename(err, "/proc",
"Failed to read process directory '/proc': %S",
message);
Py_DECREF(message);
}
goto done;
}
break;
Expand Down Expand Up @@ -259,9 +264,13 @@ get_child_pids_platform(pid_t target_pid, int recursive, pid_array_t *result)
if (closedir(proc_dir) != 0) {
int err = errno;
proc_dir = NULL;
_set_debug_oserror_from_errno_with_filename(err, "/proc",
"Failed to close process directory '/proc': %s",
strerror(err));
PyObject *message = _Py_strerror(err);
if (message != NULL) {
_set_debug_oserror_from_errno_with_filename(err, "/proc",
"Failed to close process directory '/proc': %S",
message);
Py_DECREF(message);
}
goto done;
}
proc_dir = NULL;
Expand Down
45 changes: 33 additions & 12 deletions Modules/_remote_debugging/threads.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
******************************************************************************/

#include "_remote_debugging.h"
#include "pycore_fileutils.h" // _Py_strerror()

#ifndef MS_WINDOWS
#include <unistd.h>
Expand Down Expand Up @@ -716,9 +717,13 @@ read_thread_ids(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st
int err = errno;
closedir(dir);
_Py_RemoteDebug_InitThreadsState(unwinder, st);
_set_debug_oserror_from_errno_with_filename(err, task_path,
"Failed to read process task directory '%s': %s",
task_path, strerror(err));
PyObject *message = _Py_strerror(err);
if (message != NULL) {
_set_debug_oserror_from_errno_with_filename(err, task_path,
"Failed to read process task directory '%s': %S",
task_path, message);
Py_DECREF(message);
}
return -1;
}
break;
Expand Down Expand Up @@ -749,9 +754,13 @@ read_thread_ids(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_ThreadsState *st
if (closedir(dir) != 0) {
int err = errno;
_Py_RemoteDebug_InitThreadsState(unwinder, st);
_set_debug_oserror_from_errno_with_filename(err, task_path,
"Failed to close process task directory '%s': %s",
task_path, strerror(err));
PyObject *message = _Py_strerror(err);
if (message != NULL) {
_set_debug_oserror_from_errno_with_filename(err, task_path,
"Failed to close process task directory '%s': %S",
task_path, message);
Py_DECREF(message);
}
return -1;
}
st->tids = unwinder->thread_tids;
Expand Down Expand Up @@ -816,8 +825,12 @@ _Py_RemoteDebug_StopAllThreads(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_T
}
if (ret < 0) {
detach_threads(st, seized);
_set_debug_oserror_from_errno(err,
"Failed to seize thread %d: %s", tid, strerror(err));
PyObject *message = _Py_strerror(err);
if (message != NULL) {
_set_debug_oserror_from_errno(err,
"Failed to seize thread %d: %S", tid, message);
Py_DECREF(message);
}
_Py_RemoteDebug_InitThreadsState(unwinder, st);
return -1;
}
Expand All @@ -827,8 +840,12 @@ _Py_RemoteDebug_StopAllThreads(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_T
err = errno;
if (err != ESRCH) {
detach_threads(st, seized);
_set_debug_oserror_from_errno(err,
"Failed to interrupt thread %d: %s", tid, strerror(err));
PyObject *message = _Py_strerror(err);
if (message != NULL) {
_set_debug_oserror_from_errno(err,
"Failed to interrupt thread %d: %S", tid, message);
Py_DECREF(message);
}
_Py_RemoteDebug_InitThreadsState(unwinder, st);
return -1;
}
Expand All @@ -839,8 +856,12 @@ _Py_RemoteDebug_StopAllThreads(RemoteUnwinderObject *unwinder, _Py_RemoteDebug_T
err = errno;
if (err != ECHILD && err != ESRCH) {
detach_threads(st, seized);
_set_debug_oserror_from_errno(err,
"waitpid failed for thread %d: %s", tid, strerror(err));
PyObject *message = _Py_strerror(err);
if (message != NULL) {
_set_debug_oserror_from_errno(err,
"waitpid failed for thread %d: %S", tid, message);
Py_DECREF(message);
}
_Py_RemoteDebug_InitThreadsState(unwinder, st);
return -1;
}
Expand Down
81 changes: 1 addition & 80 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -13923,86 +13923,7 @@ static PyObject *
os_strerror_impl(PyObject *module, int code)
/*[clinic end generated code: output=baebf09fa02a78f2 input=75a8673d97915a91]*/
{
#ifdef _Py_HAVE_STRERROR_R
// Check which strerror_r() API is used
# if defined(__GLIBC__) && !((_POSIX_C_SOURCE >= 200112L) && !defined(_GNU_SOURCE))
# define Py_STRERROR_R_GNU
# elif defined(__ANDROID__) && defined(_GNU_SOURCE)
# define Py_STRERROR_R_GNU
# endif
#endif

#ifdef Py_STRERROR_R_GNU
// Implementation for the GNU flavor of strerror_r()

// On Linux, the longest translated strerror() message is 86 bytes
// (including the NUL byte).
char buffer[100];
char *message = strerror_r(code, buffer, Py_ARRAY_LENGTH(buffer));
// The strerror_r() GNU flavor doesn't provide a way to check if the error
// message was truncated or not.
//
// When the buffer is used, a trailing NUL byte is always written.
assert(message != buffer || memchr(buffer, 0, Py_ARRAY_LENGTH(buffer)) != NULL);
return PyUnicode_DecodeLocale(message, "surrogateescape");

#elif defined(_Py_HAVE_STRERROR_R)
// Implementation for the XSI-compliant flavor of strerror_r()

// On Linux and FreeBSD, the longest translated strerror() message is 86
// bytes (including the NUL byte).
char small_buffer[100];
size_t buflen = Py_ARRAY_LENGTH(small_buffer);
char *buffer = NULL;
#ifndef NDEBUG
// Make sure that strerror_r() writes a trailing null byte
small_buffer[buflen - 1] = '#';
#endif
int len = strerror_r(code, small_buffer, buflen);
if (len == ERANGE) {
while (len == ERANGE) {
if (buflen > (size_t)PY_SSIZE_T_MAX / 2) {
PyMem_Free(buffer);
PyErr_NoMemory();
return NULL;
}
buflen = buflen * 2;

char *new_buffer = PyMem_Realloc(buffer, buflen);
if (new_buffer == NULL) {
PyMem_Free(buffer);
PyErr_NoMemory();
return NULL;
}
buffer = new_buffer;
#ifndef NDEBUG
buffer[buflen - 1] = '#';
#endif
len = strerror_r(code, buffer, buflen);
}
}
else {
buffer = small_buffer;
}

// strerror_r() always writes a trailing NUL byte
assert(memchr(buffer, 0, buflen) != NULL);
PyObject *result = PyUnicode_DecodeLocale(buffer, "surrogateescape");
if (buffer != small_buffer) {
PyMem_Free(buffer);
}
return result;

#else
// strerror() implementation
char *message = strerror(code);
if (message == NULL) {
PyErr_SetString(PyExc_ValueError,
"strerror() argument out of range");
return NULL;
}
return PyUnicode_DecodeLocale(message, "surrogateescape");
#endif
return _Py_strerror(code);
}


Expand Down
3 changes: 1 addition & 2 deletions Python/errors.c
Original file line number Diff line number Diff line change
Expand Up @@ -842,8 +842,7 @@ PyErr_SetFromErrnoWithFilenameObjects(PyObject *exc, PyObject *filenameObject, P

#ifndef MS_WINDOWS
if (i != 0) {
const char *s = strerror(i);
message = PyUnicode_DecodeLocale(s, "surrogateescape");
message = _Py_strerror(i);
}
else {
/* Sometimes errno didn't get set */
Expand Down
94 changes: 94 additions & 0 deletions Python/fileutils.c
Original file line number Diff line number Diff line change
Expand Up @@ -3302,3 +3302,97 @@ _Py_IsValidFD(int fd)
return (fstat(fd, &st) == 0);
#endif
}


// Call strerror_r(code) if available, or use strerror() otherwise. Decode the
// result from the locale encoding using surrogateescape error handler.
//
// On success, return a Unicode string. On error, set an exception and return
// NULL.
PyObject*
_Py_strerror(int code)
/*[clinic end generated code: output=baebf09fa02a78f2 input=75a8673d97915a91]*/
{
const char *errors = "surrogateescape";

#ifdef _Py_HAVE_STRERROR_R
// Check which strerror_r() API is used
# if defined(__GLIBC__) && !((_POSIX_C_SOURCE >= 200112L) && !defined(_GNU_SOURCE))
# define Py_STRERROR_R_GNU
# elif defined(__ANDROID__) && defined(_GNU_SOURCE)
# define Py_STRERROR_R_GNU
# endif
#endif

#ifdef Py_STRERROR_R_GNU
// Implementation for the GNU flavor of strerror_r()

// On Linux, the longest translated strerror() message is 86 bytes
// (including the NUL byte).
char buffer[100];
char *message = strerror_r(code, buffer, Py_ARRAY_LENGTH(buffer));
// The strerror_r() GNU flavor doesn't provide a way to check if the error
// message was truncated or not.
//
// When the buffer is used, a trailing NUL byte is always written.
assert(message != buffer || memchr(buffer, 0, Py_ARRAY_LENGTH(buffer)) != NULL);
return PyUnicode_DecodeLocale(message, errors);

#elif defined(_Py_HAVE_STRERROR_R)
// Implementation for the XSI-compliant flavor of strerror_r()

// On Linux and FreeBSD, the longest translated strerror() message is 86
// bytes (including the NUL byte).
char small_buffer[100];
size_t buflen = Py_ARRAY_LENGTH(small_buffer);
char *buffer = NULL;
#ifndef NDEBUG
// Make sure that strerror_r() writes a trailing null byte
small_buffer[buflen - 1] = '#';
#endif
int len = strerror_r(code, small_buffer, buflen);
if (len == ERANGE) {
while (len == ERANGE) {
if (buflen > (size_t)PY_SSIZE_T_MAX / 2) {
PyMem_Free(buffer);
PyErr_NoMemory();
return NULL;
}
buflen = buflen * 2;

char *new_buffer = PyMem_Realloc(buffer, buflen);
if (new_buffer == NULL) {
PyMem_Free(buffer);
PyErr_NoMemory();
return NULL;
}
buffer = new_buffer;
#ifndef NDEBUG
buffer[buflen - 1] = '#';
#endif
len = strerror_r(code, buffer, buflen);
}
}
else {
buffer = small_buffer;
}

// strerror_r() always writes a trailing NUL byte
assert(memchr(buffer, 0, buflen) != NULL);
PyObject *result = PyUnicode_DecodeLocale(buffer, errors);
if (buffer != small_buffer) {
PyMem_Free(buffer);
}
return result;

#else
// strerror() implementation (usually not thread-safe)
char *message = strerror(code);
if (message == NULL) {
PyErr_SetString(PyExc_ValueError,
"strerror() argument out of range");
return NULL;
}
return PyUnicode_DecodeLocale(message, errors);
#endif
}
Loading