Skip to content

Commit 8001ff8

Browse files
authored
Merge pull request RustPython#4140 from tgsong827/fcntl.ioctl
update ioctl first arg
2 parents ef4bea5 + 3e10f52 commit 8001ff8

File tree

2 files changed

+105
-1
lines changed

2 files changed

+105
-1
lines changed

Lib/test/test_ioctl.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import array
2+
import unittest
3+
from test.support import get_attribute
4+
from test.support.import_helper import import_module
5+
import os, struct
6+
fcntl = import_module('fcntl')
7+
termios = import_module('termios')
8+
get_attribute(termios, 'TIOCGPGRP') #Can't run tests without this feature
9+
10+
try:
11+
tty = open("/dev/tty", "rb")
12+
except OSError:
13+
raise unittest.SkipTest("Unable to open /dev/tty")
14+
else:
15+
with tty:
16+
# Skip if another process is in foreground
17+
r = fcntl.ioctl(tty, termios.TIOCGPGRP, " ")
18+
rpgrp = struct.unpack("i", r)[0]
19+
if rpgrp not in (os.getpgrp(), os.getsid(0)):
20+
raise unittest.SkipTest("Neither the process group nor the session "
21+
"are attached to /dev/tty")
22+
del tty, r, rpgrp
23+
24+
try:
25+
import pty
26+
except ImportError:
27+
pty = None
28+
29+
class IoctlTests(unittest.TestCase):
30+
def test_ioctl(self):
31+
# If this process has been put into the background, TIOCGPGRP returns
32+
# the session ID instead of the process group id.
33+
ids = (os.getpgrp(), os.getsid(0))
34+
with open("/dev/tty", "rb") as tty:
35+
r = fcntl.ioctl(tty, termios.TIOCGPGRP, " ")
36+
rpgrp = struct.unpack("i", r)[0]
37+
self.assertIn(rpgrp, ids)
38+
39+
def _check_ioctl_mutate_len(self, nbytes=None):
40+
buf = array.array('i')
41+
intsize = buf.itemsize
42+
ids = (os.getpgrp(), os.getsid(0))
43+
# A fill value unlikely to be in `ids`
44+
fill = -12345
45+
if nbytes is not None:
46+
# Extend the buffer so that it is exactly `nbytes` bytes long
47+
buf.extend([fill] * (nbytes // intsize))
48+
self.assertEqual(len(buf) * intsize, nbytes) # sanity check
49+
else:
50+
buf.append(fill)
51+
with open("/dev/tty", "rb") as tty:
52+
r = fcntl.ioctl(tty, termios.TIOCGPGRP, buf, True)
53+
rpgrp = buf[0]
54+
self.assertEqual(r, 0)
55+
self.assertIn(rpgrp, ids)
56+
57+
def test_ioctl_mutate(self):
58+
self._check_ioctl_mutate_len()
59+
60+
def test_ioctl_mutate_1024(self):
61+
# Issue #9758: a mutable buffer of exactly 1024 bytes wouldn't be
62+
# copied back after the system call.
63+
self._check_ioctl_mutate_len(1024)
64+
65+
def test_ioctl_mutate_2048(self):
66+
# Test with a larger buffer, just for the record.
67+
self._check_ioctl_mutate_len(2048)
68+
69+
# TODO: RUSTPYTHON
70+
@unittest.expectedFailure
71+
def test_ioctl_signed_unsigned_code_param(self):
72+
if not pty:
73+
raise unittest.SkipTest('pty module required')
74+
mfd, sfd = pty.openpty()
75+
try:
76+
if termios.TIOCSWINSZ < 0:
77+
set_winsz_opcode_maybe_neg = termios.TIOCSWINSZ
78+
set_winsz_opcode_pos = termios.TIOCSWINSZ & 0xffffffff
79+
else:
80+
set_winsz_opcode_pos = termios.TIOCSWINSZ
81+
set_winsz_opcode_maybe_neg, = struct.unpack("i",
82+
struct.pack("I", termios.TIOCSWINSZ))
83+
84+
our_winsz = struct.pack("HHHH",80,25,0,0)
85+
# test both with a positive and potentially negative ioctl code
86+
new_winsz = fcntl.ioctl(mfd, set_winsz_opcode_pos, our_winsz)
87+
new_winsz = fcntl.ioctl(mfd, set_winsz_opcode_maybe_neg, our_winsz)
88+
finally:
89+
os.close(mfd)
90+
os.close(sfd)
91+
92+
93+
if __name__ == "__main__":
94+
unittest.main()

stdlib/src/fcntl.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ mod fcntl {
5252
#[cfg(any(target_os = "dragonfly", target_os = "netbsd", target_vendor = "apple"))]
5353
#[pyattr]
5454
use libc::F_GETPATH;
55+
use rustpython_vm::PyObjectRef;
5556

5657
#[pyfunction]
5758
fn fcntl(
@@ -89,12 +90,21 @@ mod fcntl {
8990

9091
#[pyfunction]
9192
fn ioctl(
92-
fd: i32,
93+
obj: PyObjectRef,
9394
request: u32,
9495
arg: OptionalArg<Either<Either<ArgMemoryBuffer, ArgStrOrBytesLike>, i32>>,
9596
mutate_flag: OptionalArg<bool>,
9697
vm: &VirtualMachine,
9798
) -> PyResult {
99+
let fd = obj.try_to_value(vm).or_else(|_| {
100+
let meth = vm.get_method_or_type_error(
101+
obj.clone(),
102+
vm.ctx.interned_str("fileno").unwrap(),
103+
|| "ioctl first arg must be an int or object with a fileno() method".to_owned(),
104+
)?;
105+
vm.invoke(&meth, ())?.try_into_value(vm)
106+
})?;
107+
98108
let arg = arg.unwrap_or_else(|| Either::B(0));
99109
match arg {
100110
Either::A(buf_kind) => {

0 commit comments

Comments
 (0)