Skip to content

Commit 8257b62

Browse files
committed
(Merge 3.4) Closes #22258: Fix the the internal function set_inheritable() on
Illumos. This platform exposes the function ioctl(FIOCLEX), but calling it fails with errno is ENOTTY: "Inappropriate ioctl for device". set_inheritable() now falls back to the slower fcntl() (F_GETFD and then F_SETFD).
2 parents 7809569 + 282124b commit 8257b62

2 files changed

Lines changed: 39 additions & 14 deletions

File tree

‎Misc/NEWS‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ Release date: TBA
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #22258: Fix the the internal function set_inheritable() on Illumos.
14+
This platform exposes the function ``ioctl(FIOCLEX)``, but calling it fails
15+
with errno is ENOTTY: "Inappropriate ioctl for device". set_inheritable()
16+
now falls back to the slower ``fcntl()`` (``F_GETFD`` and then ``F_SETFD``).
17+
1318
- Issue #21389: Displaying the __qualname__ of the underlying function in the
1419
repr of a bound method.
1520

‎Python/fileutils.c‎

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -625,10 +625,12 @@ set_inheritable(int fd, int inheritable, int raise, int *atomic_flag_works)
625625
#ifdef MS_WINDOWS
626626
HANDLE handle;
627627
DWORD flags;
628-
#elif defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX)
628+
#else
629+
#if defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX)
630+
static int ioctl_works = -1;
629631
int request;
630632
int err;
631-
#elif defined(HAVE_FCNTL_H)
633+
#endif
632634
int flags;
633635
int res;
634636
#endif
@@ -674,20 +676,38 @@ set_inheritable(int fd, int inheritable, int raise, int *atomic_flag_works)
674676
}
675677
return 0;
676678

677-
#elif defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX)
678-
if (inheritable)
679-
request = FIONCLEX;
680-
else
681-
request = FIOCLEX;
682-
err = ioctl(fd, request, NULL);
683-
if (err) {
684-
if (raise)
685-
PyErr_SetFromErrno(PyExc_OSError);
686-
return -1;
679+
#else
680+
681+
#if defined(HAVE_SYS_IOCTL_H) && defined(FIOCLEX) && defined(FIONCLEX)
682+
if (ioctl_works != 0) {
683+
/* fast-path: ioctl() only requires one syscall */
684+
if (inheritable)
685+
request = FIONCLEX;
686+
else
687+
request = FIOCLEX;
688+
err = ioctl(fd, request, NULL);
689+
if (!err) {
690+
ioctl_works = 1;
691+
return 0;
692+
}
693+
694+
if (errno != ENOTTY) {
695+
if (raise)
696+
PyErr_SetFromErrno(PyExc_OSError);
697+
return -1;
698+
}
699+
else {
700+
/* Issue #22258: Here, ENOTTY means "Inappropriate ioctl for
701+
device". The ioctl is declared but not supported by the kernel.
702+
Remember that ioctl() doesn't work. It is the case on
703+
Illumos-based OS for example. */
704+
ioctl_works = 0;
705+
}
706+
/* fallback to fcntl() if ioctl() does not work */
687707
}
688-
return 0;
708+
#endif
689709

690-
#else
710+
/* slow-path: fcntl() requires two syscalls */
691711
flags = fcntl(fd, F_GETFD);
692712
if (flags < 0) {
693713
if (raise)

0 commit comments

Comments
 (0)