1pub(crate) use module::module_def;
4
5pub use rustpython_host_env::posix::set_inheritable;
6
7#[pymodule(name = "posix", with(
8 super::os::_os,
9 super::posix_unix_like::_posix_unix_like,
10 #[cfg(any(
11 target_os = "linux",
12 target_os = "netbsd",
13 target_os = "freebsd",
14 target_os = "android"
15 ))]
16 posix_sched
17))]
18pub mod module {
19 use crate::{
20 AsObject, Py, PyObject, PyObjectRef, PyResult, VirtualMachine,
21 builtins::{PyBytesRef, PyDictRef, PyInt, PyListRef, PyTuple, PyTupleRef, PyUtf8Str},
22 convert::{IntoPyException, ToPyException, ToPyObject, TryFromObject},
23 exceptions::OSErrorBuilder,
24 function::{ArgBytesLike, ArgMapping, Either, OptionalArg, PySsize},
25 ospath::{OsPath, OsPathOrFd},
26 stdlib::os::{
27 _os, DirFd, FollowSymlinks, SupportFunc, SymlinkArgs, fs_metadata, warn_if_bool_fd,
28 },
29 };
30 #[cfg(any(
31 target_os = "android",
32 target_os = "freebsd",
33 target_os = "linux",
34 target_os = "openbsd"
35 ))]
36 use crate::{builtins::PyUtf8StrRef, utils::ToCString};
37 use alloc::ffi::CString;
38 use core::ffi::CStr;
39 use rustpython_host_env::os::ffi::OsStringExt;
40 use rustpython_host_env::posix as host_posix;
41 use std::{
42 fs, io,
43 os::fd::{AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd},
44 };
45 use strum::IntoEnumIterator;
46 use strum_macros::{EnumIter, EnumString};
47
48 #[cfg(target_os = "linux")]
49 #[pyattr]
50 use rustpython_host_env::posix::PIDFD_NONBLOCK;
51
52 #[cfg(target_os = "macos")]
53 #[pyattr]
54 use rustpython_host_env::posix::{
55 COPYFILE_DATA as _COPYFILE_DATA, PRIO_DARWIN_BG, PRIO_DARWIN_NONUI, PRIO_DARWIN_PROCESS,
56 PRIO_DARWIN_THREAD,
57 };
58
59 #[cfg(target_os = "macos")]
60 #[pyattr]
61 use rustpython_host_env::os::{O_EVTONLY, O_NOFOLLOW_ANY};
62
63 #[cfg(target_os = "freebsd")]
64 #[pyattr]
65 use rustpython_host_env::posix::{SF_MNOWAIT, SF_NOCACHE, SF_NODISKIO, SF_SYNC};
66
67 #[cfg(any(target_os = "android", target_os = "linux"))]
68 #[pyattr]
69 use rustpython_host_env::posix::{
70 CLONE_FILES, CLONE_FS, CLONE_NEWCGROUP, CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWNS,
71 CLONE_NEWPID, CLONE_NEWUSER, CLONE_NEWUTS, CLONE_SIGHAND, CLONE_SYSVSEM, CLONE_THREAD,
72 CLONE_VM, MFD_HUGE_SHIFT, P_PIDFD, SCHED_BATCH, SCHED_DEADLINE, SCHED_IDLE, SCHED_NORMAL,
73 SCHED_RESET_ON_FORK, SPLICE_F_MORE, SPLICE_F_MOVE, SPLICE_F_NONBLOCK,
74 };
75
76 #[cfg(any(target_os = "android", target_os = "linux"))]
77 #[pyattr]
78 use rustpython_host_env::os::{O_NOATIME, O_TMPFILE};
79
80 #[cfg(any(target_os = "macos", target_os = "redox"))]
81 #[pyattr]
82 use rustpython_host_env::os::O_SYMLINK;
83
84 #[cfg(any(target_os = "android", target_os = "redox", unix))]
85 #[pyattr]
86 use rustpython_host_env::posix::{PRIO_PGRP, PRIO_PROCESS, PRIO_USER};
87
88 #[cfg(any(target_os = "android", target_os = "redox", unix))]
89 #[pyattr]
90 use rustpython_host_env::os::O_NOFOLLOW;
91
92 #[cfg(any(target_os = "linux", target_os = "macos", target_os = "netbsd"))]
93 #[pyattr]
94 use rustpython_host_env::posix::{XATTR_CREATE, XATTR_REPLACE};
95
96 #[cfg(any(target_os = "android", target_os = "linux", target_os = "netbsd"))]
97 #[pyattr]
98 use rustpython_host_env::os::O_RSYNC;
99
100 #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
101 #[pyattr]
102 use rustpython_host_env::posix::{
103 MFD_ALLOW_SEALING, MFD_CLOEXEC, MFD_HUGE_MASK, MFD_HUGETLB, POSIX_FADV_DONTNEED,
104 POSIX_FADV_NOREUSE, POSIX_FADV_NORMAL, POSIX_FADV_RANDOM, POSIX_FADV_SEQUENTIAL,
105 POSIX_FADV_WILLNEED,
106 };
107
108 #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox", unix))]
109 #[pyattr]
110 use rustpython_host_env::posix::{RTLD_LAZY, RTLD_NOW, WNOHANG};
111
112 #[cfg(any(target_os = "android", target_os = "macos", target_os = "redox", unix))]
113 #[pyattr]
114 use rustpython_host_env::posix::RTLD_GLOBAL;
115
116 #[cfg(any(
117 target_os = "android",
118 target_os = "freebsd",
119 target_os = "linux",
120 target_os = "redox"
121 ))]
122 #[pyattr]
123 use rustpython_host_env::os::O_PATH;
124
125 #[cfg(any(
126 target_os = "android",
127 target_os = "freebsd",
128 target_os = "linux",
129 target_os = "netbsd"
130 ))]
131 #[pyattr]
132 use rustpython_host_env::posix::{
133 EFD_CLOEXEC, EFD_NONBLOCK, EFD_SEMAPHORE, TFD_CLOEXEC, TFD_NONBLOCK, TFD_TIMER_ABSTIME,
134 TFD_TIMER_CANCEL_ON_SET,
135 };
136
137 #[cfg(any(
138 target_os = "android",
139 target_os = "dragonfly",
140 target_os = "linux",
141 target_os = "netbsd"
142 ))]
143 #[pyattr]
144 use rustpython_host_env::posix::{GRND_NONBLOCK, GRND_RANDOM};
145
146 #[cfg(any(
147 target_os = "android",
148 target_os = "linux",
149 target_os = "macos",
150 target_os = "redox",
151 unix
152 ))]
153 #[pyattr]
154 use rustpython_host_env::os::{F_OK, R_OK, W_OK, X_OK};
155
156 #[cfg(any(
157 target_os = "android",
158 target_os = "freebsd",
159 target_os = "netbsd",
160 target_os = "redox",
161 unix
162 ))]
163 #[pyattr]
164 use rustpython_host_env::os::O_NONBLOCK;
165
166 #[cfg(any(
167 target_os = "android",
168 target_os = "freebsd",
169 target_os = "linux",
170 target_os = "macos",
171 target_os = "netbsd"
172 ))]
173 #[pyattr]
174 use rustpython_host_env::os::O_DSYNC;
175
176 #[cfg(any(
177 target_os = "dragonfly",
178 target_os = "freebsd",
179 target_os = "linux",
180 target_os = "macos",
181 target_os = "netbsd"
182 ))]
183 #[pyattr]
184 use rustpython_host_env::posix::SCHED_OTHER;
185
186 #[cfg(any(
187 target_os = "android",
188 target_os = "dragonfly",
189 target_os = "freebsd",
190 target_os = "linux",
191 target_os = "macos"
192 ))]
193 #[pyattr]
194 use rustpython_host_env::posix::{RTLD_NODELETE, SEEK_DATA, SEEK_HOLE};
195
196 #[cfg(any(
197 target_os = "android",
198 target_os = "dragonfly",
199 target_os = "freebsd",
200 target_os = "linux",
201 target_os = "netbsd"
202 ))]
203 #[pyattr]
204 use rustpython_host_env::os::O_DIRECT;
205
206 #[cfg(any(
207 target_os = "dragonfly",
208 target_os = "freebsd",
209 target_os = "macos",
210 target_os = "netbsd",
211 target_os = "redox"
212 ))]
213 #[pyattr]
214 use rustpython_host_env::os::{O_EXLOCK, O_FSYNC, O_SHLOCK};
215
216 #[cfg(any(
217 target_os = "android",
218 target_os = "linux",
219 target_os = "macos",
220 target_os = "netbsd",
221 target_os = "redox",
222 unix
223 ))]
224 #[pyattr]
225 use rustpython_host_env::posix::RTLD_LOCAL;
226
227 #[cfg(any(
228 target_os = "android",
229 target_os = "freebsd",
230 target_os = "linux",
231 target_os = "netbsd",
232 target_os = "redox",
233 unix
234 ))]
235 #[pyattr]
236 use rustpython_host_env::posix::WUNTRACED;
237
238 #[cfg(any(
239 target_os = "android",
240 target_os = "dragonfly",
241 target_os = "freebsd",
242 target_os = "linux",
243 target_os = "macos",
244 target_os = "netbsd"
245 ))]
246 #[pyattr]
247 use rustpython_host_env::posix::{
248 CLD_CONTINUED, CLD_DUMPED, CLD_EXITED, CLD_KILLED, CLD_STOPPED, CLD_TRAPPED, P_ALL, P_PGID,
249 P_PID, SCHED_FIFO, SCHED_RR,
250 };
251
252 #[cfg(any(
253 target_os = "android",
254 target_os = "dragonfly",
255 target_os = "freebsd",
256 target_os = "linux",
257 target_os = "macos",
258 target_os = "netbsd"
259 ))]
260 #[pyattr]
261 use rustpython_host_env::os::O_SYNC;
262
263 #[cfg(any(
264 target_os = "android",
265 target_os = "dragonfly",
266 target_os = "freebsd",
267 target_os = "macos",
268 target_os = "netbsd",
269 target_os = "redox",
270 unix
271 ))]
272 #[pyattr]
273 use rustpython_host_env::os::O_DIRECTORY;
274
275 #[cfg(any(
276 target_os = "android",
277 target_os = "dragonfly",
278 target_os = "freebsd",
279 target_os = "linux",
280 target_os = "macos",
281 target_os = "netbsd",
282 target_os = "redox"
283 ))]
284 #[pyattr]
285 use rustpython_host_env::fcntl::{F_LOCK, F_TEST, F_TLOCK, F_ULOCK};
286 #[cfg(any(
287 target_os = "android",
288 target_os = "dragonfly",
289 target_os = "freebsd",
290 target_os = "linux",
291 target_os = "macos",
292 target_os = "netbsd",
293 target_os = "redox"
294 ))]
295 #[pyattr]
296 use rustpython_host_env::posix::{RTLD_NOLOAD, WEXITED, WNOWAIT, WSTOPPED};
297
298 #[cfg(any(
299 target_os = "android",
300 target_os = "dragonfly",
301 target_os = "freebsd",
302 target_os = "linux",
303 target_os = "macos",
304 target_os = "netbsd",
305 target_os = "redox"
306 ))]
307 #[pyattr]
308 use rustpython_host_env::os::{O_ASYNC, O_NDELAY, O_NOCTTY};
309
310 #[cfg(any(
311 target_os = "android",
312 target_os = "dragonfly",
313 target_os = "freebsd",
314 target_os = "linux",
315 target_os = "macos",
316 target_os = "netbsd",
317 target_os = "redox",
318 unix
319 ))]
320 #[pyattr]
321 use rustpython_host_env::posix::WCONTINUED;
322
323 #[cfg(any(
324 target_os = "android",
325 target_os = "dragonfly",
326 target_os = "freebsd",
327 target_os = "linux",
328 target_os = "macos",
329 target_os = "netbsd",
330 target_os = "redox",
331 unix
332 ))]
333 #[pyattr]
334 use rustpython_host_env::os::O_CLOEXEC;
335
336 #[pyattr]
337 use rustpython_host_env::posix::{
338 EX_CANTCREAT, EX_CONFIG, EX_DATAERR, EX_IOERR, EX_NOHOST, EX_NOINPUT, EX_NOPERM, EX_NOUSER,
339 EX_OK, EX_OSERR, EX_OSFILE, EX_PROTOCOL, EX_SOFTWARE, EX_TEMPFAIL, EX_UNAVAILABLE,
340 EX_USAGE,
341 };
342
343 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
344 #[pyattr]
345 const POSIX_SPAWN_OPEN: i32 = PosixSpawnFileActionIdentifier::Open as i32;
346
347 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
348 #[pyattr]
349 const POSIX_SPAWN_CLOSE: i32 = PosixSpawnFileActionIdentifier::Close as i32;
350
351 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
352 #[pyattr]
353 const POSIX_SPAWN_DUP2: i32 = PosixSpawnFileActionIdentifier::Dup2 as i32;
354
355 impl TryFromObject for BorrowedFd<'_> {
356 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
357 crate::stdlib::os::warn_if_bool_fd(&obj, vm)?;
358 let fd = i32::try_from_object(vm, obj)?;
359 if fd == -1 {
360 return Err(io::Error::from_raw_os_error(libc::EBADF).into_pyexception(vm));
361 }
362 Ok(unsafe { BorrowedFd::borrow_raw(fd) })
365 }
366 }
367
368 impl TryFromObject for OwnedFd {
369 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
370 let fd = i32::try_from_object(vm, obj)?;
371 if fd == -1 {
372 return Err(io::Error::from_raw_os_error(libc::EBADF).into_pyexception(vm));
373 }
374 Ok(unsafe { Self::from_raw_fd(fd) })
377 }
378 }
379
380 impl ToPyObject for OwnedFd {
381 fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
382 self.into_raw_fd().to_pyobject(vm)
383 }
384 }
385
386 #[pyfunction]
387 fn getgroups(vm: &VirtualMachine) -> PyResult<Vec<PyObjectRef>> {
388 let group_ids =
389 rustpython_host_env::posix::getgroups().map_err(|e| e.into_pyexception(vm))?;
390 Ok(group_ids
391 .into_iter()
392 .map(|gid| vm.ctx.new_int(gid).into())
393 .collect())
394 }
395
396 #[derive(FromArgs)]
397 pub(super) struct AccessArgs<'a> {
398 #[pyarg(any)]
399 path: OsPath,
400 #[pyarg(any)]
401 mode: u8,
402 #[pyarg(flatten)]
403 dir_fd: DirFd<'a, 0>,
404 #[pyarg(named, default)]
405 effective_ids: bool,
406 #[pyarg(flatten)]
407 follow_symlinks: FollowSymlinks,
408 }
409
410 #[pyfunction]
411 pub(super) fn access(args: AccessArgs<'_>, vm: &VirtualMachine) -> PyResult<bool> {
412 let [] = args.dir_fd.0;
413 if args.effective_ids {
414 return Err(
415 vm.new_not_implemented_error("access: effective_ids unavailable on this platform")
416 );
417 }
418 if !args.follow_symlinks.0 {
419 return Err(vm.new_not_implemented_error(
420 "access: follow_symlinks unavailable on this platform",
421 ));
422 }
423 rustpython_host_env::posix::check_access(args.path.as_ref(), args.mode)
424 .map_err(|err| err.to_pyexception(vm))
425 }
426
427 #[pyfunction]
428 fn _create_environ(vm: &VirtualMachine) -> PyDictRef {
429 let environ = vm.ctx.new_dict();
430 for (key, value) in crate::host_env::os::vars_os() {
431 let key: PyObjectRef = vm.ctx.new_bytes(key.into_vec()).into();
432 let value: PyObjectRef = vm.ctx.new_bytes(value.into_vec()).into();
433 environ.set_item(&*key, value, vm).unwrap();
434 }
435 environ
436 }
437
438 #[pyfunction]
439 pub(super) fn symlink(args: SymlinkArgs<'_>, vm: &VirtualMachine) -> PyResult<()> {
440 let src = args.src.into_cstring(vm)?;
441 let dst = args.dst.into_cstring(vm)?;
442 #[cfg(not(target_os = "redox"))]
443 {
444 rustpython_host_env::posix::symlinkat(&src, args.dir_fd.get().into(), &dst)
445 .map_err(|err| err.into_pyexception(vm))
446 }
447 #[cfg(target_os = "redox")]
448 {
449 let [] = args.dir_fd.0;
450 rustpython_host_env::posix::symlink(&src, &dst).map_err(|err| err.into_pyexception(vm))
451 }
452 }
453
454 #[cfg(not(target_os = "redox"))]
455 #[derive(FromArgs)]
456 struct FchdirArgs {
457 #[pyarg(any)]
458 fd: PyObjectRef,
459 }
460
461 #[cfg(not(target_os = "redox"))]
462 #[pyfunction]
463 fn fchdir(fd: FchdirArgs, vm: &VirtualMachine) -> PyResult<()> {
464 let fd = fd.fd;
465 warn_if_bool_fd(&fd, vm)?;
466 let fd = i32::try_from_object(vm, fd)?;
467 rustpython_host_env::posix::fchdir(fd).map_err(|err| err.into_pyexception(vm))
468 }
469
470 #[cfg(not(target_os = "redox"))]
471 #[derive(FromArgs)]
472 struct ChrootArgs {
473 #[pyarg(any)]
474 path: OsPath,
475 }
476
477 #[cfg(not(target_os = "redox"))]
478 #[pyfunction]
479 fn chroot(path: ChrootArgs, vm: &VirtualMachine) -> PyResult<()> {
480 use crate::exceptions::OSErrorBuilder;
481
482 let path = path.path;
483 rustpython_host_env::posix::chroot(std::path::Path::new(&path.path))
484 .map_err(|err| OSErrorBuilder::with_filename(&err, path, vm))
485 }
486
487 #[cfg(not(target_os = "redox"))]
489 fn chown_inner(
490 path: OsPathOrFd<'_>,
491 uid: isize,
492 gid: isize,
493 dir_fd: DirFd<'_, 1>,
494 follow_symlinks: FollowSymlinks,
495 vm: &VirtualMachine,
496 ) -> PyResult<()> {
497 let uid = if uid >= 0 {
498 Some(uid as u32)
499 } else if uid == -1 {
500 None
501 } else {
502 return Err(vm.new_os_error("Specified uid is not valid."));
503 };
504
505 let gid = if gid >= 0 {
506 Some(gid as u32)
507 } else if gid == -1 {
508 None
509 } else {
510 return Err(vm.new_os_error("Specified gid is not valid."));
511 };
512
513 match path {
514 OsPathOrFd::Path(ref p) => rustpython_host_env::posix::fchownat(
515 dir_fd.get().into(),
516 p.path.as_os_str(),
517 uid,
518 gid,
519 follow_symlinks.0,
520 ),
521 OsPathOrFd::Fd(fd) => rustpython_host_env::posix::fchown(fd.into(), uid, gid),
522 }
523 .map_err(|err| OSErrorBuilder::with_filename(&err, path, vm))
524 }
525
526 #[cfg(not(target_os = "redox"))]
527 #[derive(FromArgs)]
528 struct ChownArgs<'a> {
529 #[pyarg(any)]
530 path: OsPathOrFd<'a>,
531 #[pyarg(any)]
532 uid: isize,
533 #[pyarg(any)]
534 gid: isize,
535 #[pyarg(flatten)]
536 dir_fd: DirFd<'a, 1>,
537 #[pyarg(flatten)]
538 follow_symlinks: FollowSymlinks,
539 }
540
541 #[cfg(not(target_os = "redox"))]
542 #[pyfunction]
543 fn chown(args: ChownArgs<'_>, vm: &VirtualMachine) -> PyResult<()> {
544 chown_inner(
545 args.path,
546 args.uid,
547 args.gid,
548 args.dir_fd,
549 args.follow_symlinks,
550 vm,
551 )
552 }
553
554 #[cfg(not(target_os = "redox"))]
555 #[derive(FromArgs)]
556 struct LchownArgs {
557 #[pyarg(any)]
558 path: OsPath,
559 #[pyarg(any)]
560 uid: isize,
561 #[pyarg(any)]
562 gid: isize,
563 }
564
565 #[cfg(not(target_os = "redox"))]
566 #[pyfunction]
567 fn lchown(args: LchownArgs, vm: &VirtualMachine) -> PyResult<()> {
568 let LchownArgs { path, uid, gid } = args;
569 chown_inner(
570 OsPathOrFd::Path(path),
571 uid,
572 gid,
573 DirFd::default(),
574 FollowSymlinks(false),
575 vm,
576 )
577 }
578
579 #[cfg(not(target_os = "redox"))]
580 #[derive(FromArgs)]
581 struct FchownArgs<'a> {
582 #[pyarg(any)]
583 fd: BorrowedFd<'a>,
584 #[pyarg(any)]
585 uid: isize,
586 #[pyarg(any)]
587 gid: isize,
588 }
589
590 #[cfg(not(target_os = "redox"))]
591 #[pyfunction]
592 fn fchown(args: FchownArgs<'_>, vm: &VirtualMachine) -> PyResult<()> {
593 let FchownArgs { fd, uid, gid } = args;
594 chown_inner(
595 OsPathOrFd::Fd(fd.into()),
596 uid,
597 gid,
598 DirFd::default(),
599 FollowSymlinks(true),
600 vm,
601 )
602 }
603
604 #[derive(FromArgs)]
605 struct RegisterAtForkArgs {
606 #[pyarg(named, optional)]
607 before: OptionalArg<PyObjectRef>,
608 #[pyarg(named, optional)]
609 after_in_child: OptionalArg<PyObjectRef>,
610 #[pyarg(named, optional)]
611 after_in_parent: OptionalArg<PyObjectRef>,
612 }
613
614 impl RegisterAtForkArgs {
615 fn into_validated(
616 self,
617 vm: &VirtualMachine,
618 ) -> PyResult<(
619 Option<PyObjectRef>,
620 Option<PyObjectRef>,
621 Option<PyObjectRef>,
622 )> {
623 fn into_option(
624 arg: OptionalArg<PyObjectRef>,
625 vm: &VirtualMachine,
626 ) -> PyResult<Option<PyObjectRef>> {
627 match arg {
628 OptionalArg::Present(obj) => {
629 if !obj.is_callable() {
630 return Err(vm.new_type_error("Args must be callable"));
631 }
632 Ok(Some(obj))
633 }
634 OptionalArg::Missing => Ok(None),
635 }
636 }
637 let before = into_option(self.before, vm)?;
638 let after_in_parent = into_option(self.after_in_parent, vm)?;
639 let after_in_child = into_option(self.after_in_child, vm)?;
640 if before.is_none() && after_in_parent.is_none() && after_in_child.is_none() {
641 return Err(vm.new_type_error("At least one arg must be present"));
642 }
643 Ok((before, after_in_parent, after_in_child))
644 }
645 }
646
647 #[pyfunction]
648 fn register_at_fork(args: RegisterAtForkArgs, vm: &VirtualMachine) -> PyResult<()> {
649 let (before, after_in_parent, after_in_child) = args.into_validated(vm)?;
650
651 if let Some(before) = before {
652 vm.state.before_forkers.lock().push(before);
653 }
654 if let Some(after_in_parent) = after_in_parent {
655 vm.state.after_forkers_parent.lock().push(after_in_parent);
656 }
657 if let Some(after_in_child) = after_in_child {
658 vm.state.after_forkers_child.lock().push(after_in_child);
659 }
660 Ok(())
661 }
662
663 fn run_at_forkers(mut funcs: Vec<PyObjectRef>, reversed: bool, vm: &VirtualMachine) {
664 if !funcs.is_empty() {
665 if reversed {
666 funcs.reverse();
667 }
668 for func in funcs {
669 if let Err(e) = func.call((), vm) {
670 let exit = e.fast_isinstance(vm.ctx.exceptions.system_exit);
671 vm.run_unraisable(e, Some("Exception ignored in".to_owned()), func);
672 if exit {
673 }
675 }
676 }
677 }
678 }
679
680 fn py_os_before_fork(vm: &VirtualMachine) {
681 let before_forkers: Vec<PyObjectRef> = vm.state.before_forkers.lock().clone();
682 run_at_forkers(before_forkers, true, vm);
686
687 #[cfg(feature = "threading")]
688 crate::stdlib::_imp::acquire_imp_lock_for_fork(vm);
689
690 #[cfg(feature = "threading")]
691 vm.state.stop_the_world.stop_the_world(&vm.state);
692 }
693
694 fn py_os_after_fork_child(vm: &VirtualMachine) {
695 #[cfg(all(unix, feature = "threading"))]
698 unsafe {
699 crate::vm::runtime::reinit_after_fork()
700 };
701
702 #[cfg(feature = "threading")]
703 vm.state.stop_the_world.reset_after_fork();
704
705 #[cfg(feature = "threading")]
709 reinit_locks_after_fork(vm);
710
711 #[cfg(all(unix, feature = "threading"))]
715 reinit_other_interpreters_after_fork(vm);
716
717 #[cfg(feature = "threading")]
721 unsafe {
722 crate::stdlib::_io::reinit_std_streams_after_fork(vm)
723 };
724
725 crate::signal::clear_after_fork();
727 crate::stdlib::_signal::_signal::clear_wakeup_fd_after_fork();
728
729 #[cfg(any(
730 target_os = "macos",
731 target_os = "ios",
732 target_os = "freebsd",
733 target_os = "netbsd",
734 target_os = "openbsd",
735 target_os = "dragonfly",
736 ))]
737 rustpython_host_env::select::kqueue::mark_closed_after_fork();
738
739 #[cfg(feature = "threading")]
741 crate::object::reset_weakref_locks_after_fork();
742
743 unsafe { crate::builtins::type_::type_cache_after_fork() };
745
746 #[cfg(feature = "threading")]
750 unsafe {
751 crate::object::qsbr::QSBR.reset_after_fork()
752 };
753
754 #[cfg(feature = "threading")]
757 crate::stdlib::_thread::after_fork_child(vm);
758
759 #[cfg(feature = "threading")]
762 unsafe {
763 crate::stdlib::_imp::after_fork_child_imp_lock_release()
764 };
765
766 vm.signal_handlers
769 .get_or_init(crate::signal::SignalHandlers::default);
770
771 let after_forkers_child: Vec<PyObjectRef> = vm.state.after_forkers_child.lock().clone();
773 run_at_forkers(after_forkers_child, false, vm);
774 }
775
776 #[cfg(all(unix, feature = "threading"))]
782 fn reinit_locks_after_fork(vm: &VirtualMachine) {
783 use rustpython_common::lock::reinit_mutex_after_fork;
784
785 unsafe {
786 reinit_mutex_after_fork(&vm.state.before_forkers);
788 reinit_mutex_after_fork(&vm.state.after_forkers_child);
789 reinit_mutex_after_fork(&vm.state.after_forkers_parent);
790 reinit_mutex_after_fork(&vm.state.atexit_funcs);
791 reinit_mutex_after_fork(&vm.state.global_trace_func);
792 reinit_mutex_after_fork(&vm.state.global_profile_func);
793 reinit_mutex_after_fork(&vm.state.type_mutex);
794 reinit_mutex_after_fork(&vm.state.monitoring);
795
796 reinit_mutex_after_fork(&vm.state.thread_frames);
798 reinit_mutex_after_fork(&vm.state.thread_handles);
799 reinit_mutex_after_fork(&vm.state.shutdown_handles);
800
801 vm.ctx.string_pool.reinit_after_fork();
803
804 vm.state.codec_registry.reinit_after_fork();
806
807 crate::gc_state::gc_state().reinit_after_fork();
810 vm.state.gc.reinit_after_fork();
811
812 crate::stdlib::_imp::reinit_imp_lock_after_fork();
814 }
815 }
816
817 #[cfg(all(unix, feature = "threading"))]
828 fn reinit_other_interpreters_after_fork(vm: &VirtualMachine) {
829 use rustpython_common::lock::reinit_mutex_after_fork;
830
831 for state in crate::vm::runtime::live_interpreter_states() {
832 if state.interpreter_id == vm.state.interpreter_id {
833 continue;
834 }
835
836 unsafe {
837 reinit_mutex_after_fork(&state.before_forkers);
838 reinit_mutex_after_fork(&state.after_forkers_child);
839 reinit_mutex_after_fork(&state.after_forkers_parent);
840 reinit_mutex_after_fork(&state.atexit_funcs);
841 reinit_mutex_after_fork(&state.global_trace_func);
842 reinit_mutex_after_fork(&state.global_profile_func);
843 reinit_mutex_after_fork(&state.type_mutex);
844 reinit_mutex_after_fork(&state.monitoring);
845 reinit_mutex_after_fork(&state.thread_frames);
846 reinit_mutex_after_fork(&state.thread_handles);
847 reinit_mutex_after_fork(&state.shutdown_handles);
848
849 state.codec_registry.reinit_after_fork();
850 state.gc.reinit_after_fork();
851 }
852
853 state.stop_the_world.reset_after_fork();
854
855 state.thread_frames.lock().clear();
858 state.thread_handles.lock().clear();
859 state.shutdown_handles.lock().clear();
860 }
861
862 crate::vm::thread::purge_other_interpreter_slots_after_fork(vm.state.interpreter_id);
863 }
864
865 fn py_os_after_fork_parent(vm: &VirtualMachine) {
866 #[cfg(feature = "threading")]
867 vm.state.stop_the_world.start_the_world(&vm.state);
868
869 #[cfg(feature = "threading")]
870 crate::stdlib::_imp::release_imp_lock_after_fork_parent();
871
872 let after_forkers_parent: Vec<PyObjectRef> = vm.state.after_forkers_parent.lock().clone();
873 run_at_forkers(after_forkers_parent, false, vm);
874 }
875
876 fn get_number_of_os_threads() -> isize {
879 rustpython_host_env::posix::get_number_of_os_threads()
880 }
881
882 fn warn_if_multi_threaded(name: &str, num_os_threads: isize, vm: &VirtualMachine) {
885 let num_threads = if num_os_threads > 0 {
886 num_os_threads as usize
887 } else {
888 let threading = match vm
893 .sys_module
894 .get_attr("modules", vm)
895 .and_then(|m| m.get_item("threading", vm))
896 {
897 Ok(m) => m,
898 Err(_) => return,
899 };
900 let active = threading.get_attr("_active", vm).ok();
901 let limbo = threading.get_attr("_limbo", vm).ok();
902
903 let count_dict = |obj: Option<crate::PyObjectRef>| -> usize {
906 obj.and_then(|o| {
907 o.downcast_ref::<crate::builtins::PyDict>()
908 .map(|d| d.__len__())
909 })
910 .unwrap_or(0)
911 };
912
913 count_dict(active) + count_dict(limbo)
914 };
915
916 if num_threads > 1 {
917 let pid = rustpython_host_env::posix::getpid();
918 let msg = format!(
919 "This process (pid={pid}) is multi-threaded, use of {name}() may lead to deadlocks in the child."
920 );
921
922 let _ =
925 crate::stdlib::_warnings::warn(vm.ctx.exceptions.deprecation_warning, msg, 1, vm);
926 }
927 }
928
929 #[pyfunction]
930 fn fork(vm: &VirtualMachine) -> PyResult<i32> {
931 if vm
932 .state
933 .finalizing
934 .load(core::sync::atomic::Ordering::Acquire)
935 {
936 return Err(vm.new_exception_msg(
937 vm.ctx.exceptions.python_finalization_error.to_owned(),
938 "can't fork at interpreter shutdown".into(),
939 ));
940 }
941 if !vm.state.allow_fork() {
942 return Err(
943 vm.new_runtime_error("fork not supported for isolated subinterpreters".to_owned())
944 );
945 }
946
947 vm.audit("os.fork", || ())?;
948
949 py_os_before_fork(vm);
950 let pid = rustpython_host_env::posix::fork();
951
952 match pid {
953 Ok(0) => {
954 py_os_after_fork_child(vm);
955 Ok(0)
956 }
957 Ok(pid) => {
958 let num_os_threads = get_number_of_os_threads();
961 py_os_after_fork_parent(vm);
962 warn_if_multi_threaded("fork", num_os_threads, vm);
964 Ok(pid)
965 }
966 Err(err) => {
967 py_os_after_fork_parent(vm);
968 Err(err.into_pyexception(vm))
969 }
970 }
971 }
972
973 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
974 #[pyfunction]
975 fn forkpty(vm: &VirtualMachine) -> PyResult<(i32, i32)> {
976 if vm
977 .state
978 .finalizing
979 .load(core::sync::atomic::Ordering::Acquire)
980 {
981 return Err(vm.new_exception_msg(
982 vm.ctx.exceptions.python_finalization_error.to_owned(),
983 "can't fork at interpreter shutdown".into(),
984 ));
985 }
986 if !vm.state.allow_fork() {
987 return Err(
988 vm.new_runtime_error("fork not supported for isolated subinterpreters".to_owned())
989 );
990 }
991
992 vm.audit("os.forkpty", || ())?;
993
994 py_os_before_fork(vm);
995 let result = rustpython_host_env::posix::forkpty();
996
997 match result {
998 Ok((0, master)) => {
999 py_os_after_fork_child(vm);
1000 Ok((0, master))
1001 }
1002 Ok((pid, master)) => {
1003 let num_os_threads = get_number_of_os_threads();
1004 py_os_after_fork_parent(vm);
1005 warn_if_multi_threaded("forkpty", num_os_threads, vm);
1006 Ok((pid, master))
1007 }
1008 Err(err) => {
1009 py_os_after_fork_parent(vm);
1010 Err(err.into_pyexception(vm))
1011 }
1012 }
1013 }
1014
1015 #[cfg(not(target_os = "redox"))]
1016 const MKNOD_DIR_FD: bool = cfg!(not(target_vendor = "apple"));
1017
1018 #[cfg(not(target_os = "redox"))]
1019 #[derive(FromArgs)]
1020 struct MknodArgs<'fd> {
1021 #[pyarg(any)]
1022 path: OsPath,
1023 #[pyarg(any, default = 0o600)]
1024 mode: libc::mode_t,
1025 #[pyarg(any, default = 0)]
1026 device: libc::dev_t,
1027 #[pyarg(flatten)]
1028 dir_fd: DirFd<'fd, { MKNOD_DIR_FD as usize }>,
1029 }
1030
1031 #[cfg(not(target_os = "redox"))]
1032 impl MknodArgs<'_> {
1033 #[cfg(not(target_vendor = "apple"))]
1034 fn mknod(self, vm: &VirtualMachine) -> PyResult<()> {
1035 let c_path = self.path.clone().into_cstring(vm)?;
1036 match self.dir_fd.raw_opt() {
1037 None => rustpython_host_env::posix::mknod(&c_path, self.mode, self.device),
1038 Some(non_default_fd) => rustpython_host_env::posix::mknodat(
1039 non_default_fd,
1040 &c_path,
1041 self.mode,
1042 self.device,
1043 ),
1044 }
1045 .map_err(|err| err.into_pyexception(vm))
1046 }
1047
1048 #[cfg(target_vendor = "apple")]
1049 fn mknod(self, vm: &VirtualMachine) -> PyResult<()> {
1050 let [] = self.dir_fd.0;
1051 let c_path = self.path.clone().into_cstring(vm)?;
1052 rustpython_host_env::posix::mknod(&c_path, self.mode, self.device)
1053 .map_err(|err| err.into_pyexception(vm))
1054 }
1055 }
1056
1057 #[cfg(not(target_os = "redox"))]
1058 #[pyfunction]
1059 fn mknod(args: MknodArgs<'_>, vm: &VirtualMachine) -> PyResult<()> {
1060 args.mknod(vm)
1061 }
1062
1063 #[cfg(not(target_os = "redox"))]
1064 #[pyfunction]
1065 fn nice(increment: i32, vm: &VirtualMachine) -> PyResult<i32> {
1066 rustpython_host_env::posix::nice(increment).map_err(|err| err.into_pyexception(vm))
1067 }
1068
1069 #[cfg(not(target_os = "redox"))]
1070 #[derive(FromArgs)]
1071 struct SchedPolicyArgs {
1072 #[pyarg(any)]
1073 policy: i32,
1074 }
1075
1076 #[cfg(not(target_os = "redox"))]
1077 #[pyfunction]
1078 fn sched_get_priority_max(policy: SchedPolicyArgs, vm: &VirtualMachine) -> PyResult<i32> {
1079 rustpython_host_env::posix::sched_get_priority_max(policy.policy)
1080 .map_err(|err| err.into_pyexception(vm))
1081 }
1082
1083 #[cfg(not(target_os = "redox"))]
1084 #[pyfunction]
1085 fn sched_get_priority_min(policy: SchedPolicyArgs, vm: &VirtualMachine) -> PyResult<i32> {
1086 rustpython_host_env::posix::sched_get_priority_min(policy.policy)
1087 .map_err(|err| err.into_pyexception(vm))
1088 }
1089
1090 #[pyfunction]
1091 fn sched_yield(vm: &VirtualMachine) -> PyResult<()> {
1092 rustpython_host_env::posix::sched_yield().map_err(|e| e.into_pyexception(vm))
1093 }
1094
1095 #[pyfunction]
1096 fn get_inheritable(fd: BorrowedFd<'_>, vm: &VirtualMachine) -> PyResult<bool> {
1097 rustpython_host_env::fcntl::get_inheritable(fd).map_err(|err| err.into_pyexception(vm))
1098 }
1099
1100 #[pyfunction]
1101 fn set_inheritable(fd: BorrowedFd<'_>, inheritable: bool, vm: &VirtualMachine) -> PyResult<()> {
1102 super::set_inheritable(fd, inheritable).map_err(|err| err.into_pyexception(vm))
1103 }
1104
1105 #[pyfunction]
1106 fn get_blocking(fd: BorrowedFd<'_>, vm: &VirtualMachine) -> PyResult<bool> {
1107 rustpython_host_env::fcntl::get_blocking(fd).map_err(|err| err.into_pyexception(vm))
1108 }
1109
1110 #[pyfunction]
1111 fn set_blocking(fd: BorrowedFd<'_>, blocking: bool, vm: &VirtualMachine) -> PyResult<()> {
1112 rustpython_host_env::fcntl::set_blocking(fd, blocking)
1113 .map_err(|err| err.into_pyexception(vm))
1114 }
1115
1116 #[pyfunction]
1117 fn pread(
1118 fd: i32,
1119 length: PySsize,
1120 offset: libc::off_t,
1121 vm: &VirtualMachine,
1122 ) -> PyResult<PyBytesRef> {
1123 let (fd, n, offset) = (fd, length, offset);
1124 if n < 0 {
1125 return Err(io::Error::from_raw_os_error(libc::EINVAL).into_pyexception(vm));
1126 }
1127 let mut buffer = vm.new_zeroed_bytes(n as usize)?;
1128 loop {
1129 match vm.allow_threads(|| rustpython_host_env::posix::pread(fd, &mut buffer, offset)) {
1130 Ok(n) => {
1131 buffer.truncate(n);
1132 return Ok(vm.ctx.new_bytes(buffer));
1133 }
1134 Err(err) if err.raw_os_error() == Some(libc::EINTR) => {
1135 vm.check_signals()?;
1136 continue;
1137 }
1138 Err(err) => return Err(err.into_pyexception(vm)),
1139 }
1140 }
1141 }
1142
1143 #[pyfunction]
1144 fn pwrite(
1145 fd: i32,
1146 buffer: ArgBytesLike,
1147 offset: libc::off_t,
1148 vm: &VirtualMachine,
1149 ) -> PyResult<usize> {
1150 let data = buffer.borrow_buf_unlocked(vm)?;
1153 loop {
1154 match vm.allow_threads(|| rustpython_host_env::posix::pwrite(fd, &data, offset)) {
1155 Ok(n) => return Ok(n),
1156 Err(err) if err.raw_os_error() == Some(libc::EINTR) => {
1157 vm.check_signals()?;
1158 continue;
1159 }
1160 Err(err) => return Err(err.into_pyexception(vm)),
1161 }
1162 }
1163 }
1164
1165 #[pyfunction]
1166 fn pipe(vm: &VirtualMachine) -> PyResult<(OwnedFd, OwnedFd)> {
1167 rustpython_host_env::posix::pipe().map_err(|err| err.into_pyexception(vm))
1168 }
1169
1170 #[cfg(any(
1172 target_os = "android",
1173 target_os = "dragonfly",
1174 target_os = "emscripten",
1175 target_os = "freebsd",
1176 target_os = "linux",
1177 target_os = "netbsd",
1178 target_os = "openbsd"
1179 ))]
1180 #[pyfunction]
1181 fn pipe2(flags: libc::c_int, vm: &VirtualMachine) -> PyResult<(OwnedFd, OwnedFd)> {
1182 rustpython_host_env::posix::pipe2(flags).map_err(|err| err.into_pyexception(vm))
1183 }
1184
1185 fn _chmod(
1186 path: OsPath,
1187 dir_fd: DirFd<'_, 0>,
1188 mode: u32,
1189 follow_symlinks: FollowSymlinks,
1190 vm: &VirtualMachine,
1191 ) -> PyResult<()> {
1192 let [] = dir_fd.0;
1193 #[cfg(all(
1194 unix,
1195 not(target_os = "redox"),
1196 not(any(target_os = "macos", target_os = "freebsd", target_os = "netbsd"))
1197 ))]
1198 if !follow_symlinks.0 {
1199 let err_path = path.clone();
1200 let c_path = path.into_cstring(vm)?;
1201 return rustpython_host_env::posix::fchmodat(
1202 libc::AT_FDCWD,
1203 &c_path,
1204 mode as libc::mode_t,
1205 libc::AT_SYMLINK_NOFOLLOW,
1206 )
1207 .map_err(|err| {
1208 let enotsup = err.raw_os_error() == Some(libc::EOPNOTSUPP)
1209 || err.raw_os_error() == Some(libc::ENOTSUP);
1210 if enotsup {
1211 vm.new_not_implemented_error(
1212 "chmod: follow_symlinks unavailable on this platform".to_owned(),
1213 )
1214 } else {
1215 OSErrorBuilder::with_filename(&err, err_path, vm)
1216 }
1217 });
1218 }
1219 let err_path = path.clone();
1220 let body = move || {
1221 use std::os::unix::fs::PermissionsExt;
1222 let meta = fs_metadata(&path, follow_symlinks.0)?;
1223 let mut permissions = meta.permissions();
1224 permissions.set_mode(mode);
1225 fs::set_permissions(&path, permissions)
1226 };
1227 body().map_err(|err| OSErrorBuilder::with_filename(&err, err_path, vm))
1228 }
1229
1230 #[cfg(not(target_os = "redox"))]
1231 fn _fchmod(fd: BorrowedFd<'_>, mode: u32, vm: &VirtualMachine) -> PyResult<()> {
1232 rustpython_host_env::posix::fchmod(fd, mode).map_err(|err| err.into_pyexception(vm))
1233 }
1234
1235 #[cfg(not(target_os = "redox"))]
1236 #[pyfunction]
1237 fn chmod(
1238 path: OsPathOrFd<'_>,
1239 dir_fd: DirFd<'_, 0>,
1240 mode: u32,
1241 follow_symlinks: FollowSymlinks,
1242 vm: &VirtualMachine,
1243 ) -> PyResult<()> {
1244 match path {
1245 OsPathOrFd::Path(path) => {
1246 #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "netbsd",))]
1247 if !follow_symlinks.0 && dir_fd == Default::default() {
1248 return lchmod(LchmodArgs { path, mode }, vm);
1249 }
1250 _chmod(path, dir_fd, mode, follow_symlinks, vm)
1251 }
1252 OsPathOrFd::Fd(fd) => _fchmod(fd.into(), mode, vm),
1253 }
1254 }
1255
1256 #[cfg(target_os = "redox")]
1257 #[pyfunction]
1258 fn chmod(
1259 path: OsPath,
1260 dir_fd: DirFd<0>,
1261 mode: u32,
1262 follow_symlinks: FollowSymlinks,
1263 vm: &VirtualMachine,
1264 ) -> PyResult<()> {
1265 _chmod(path, dir_fd, mode, follow_symlinks, vm)
1266 }
1267
1268 #[cfg(not(target_os = "redox"))]
1269 #[derive(FromArgs)]
1270 struct FchmodArgs<'a> {
1271 #[pyarg(any)]
1272 fd: BorrowedFd<'a>,
1273 #[pyarg(any)]
1274 mode: u32,
1275 }
1276
1277 #[cfg(not(target_os = "redox"))]
1278 #[pyfunction]
1279 fn fchmod(args: FchmodArgs<'_>, vm: &VirtualMachine) -> PyResult<()> {
1280 _fchmod(args.fd, args.mode, vm)
1281 }
1282
1283 #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "netbsd",))]
1284 #[derive(FromArgs)]
1285 struct LchmodArgs {
1286 #[pyarg(any)]
1287 path: OsPath,
1288 #[pyarg(any)]
1289 mode: u32,
1290 }
1291
1292 #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "netbsd",))]
1293 #[pyfunction]
1294 fn lchmod(args: LchmodArgs, vm: &VirtualMachine) -> PyResult<()> {
1295 let LchmodArgs { path, mode } = args;
1296 let c_path = path.clone().into_cstring(vm)?;
1297 rustpython_host_env::posix::lchmod(&c_path, mode as libc::mode_t)
1298 .map_err(|err| OSErrorBuilder::with_filename(&err, path, vm))
1299 }
1300
1301 #[pyfunction]
1302 fn execv(
1303 path: OsPath,
1304 argv: Either<PyListRef, PyTupleRef>,
1305 vm: &VirtualMachine,
1306 ) -> PyResult<()> {
1307 if !vm.state.allow_exec() {
1308 return Err(
1309 vm.new_runtime_error("exec not supported for isolated subinterpreters".to_owned())
1310 );
1311 }
1312 let path = path.into_cstring(vm)?;
1313
1314 let argv = vm.extract_elements_with(argv.as_ref(), |obj| {
1315 OsPath::try_from_object(vm, obj)?.into_cstring(vm)
1316 })?;
1317 let argv: Vec<&CStr> = argv.iter().map(|entry| entry.as_c_str()).collect();
1318
1319 let first = argv
1320 .first()
1321 .ok_or_else(|| vm.new_value_error("execv() arg 2 must not be empty"))?;
1322 if first.to_bytes().is_empty() {
1323 return Err(vm.new_value_error("execv() arg 2 first element cannot be empty"));
1324 }
1325
1326 rustpython_host_env::posix::execv(&path, &argv).map_err(|err| err.into_pyexception(vm))
1327 }
1328
1329 #[derive(FromArgs)]
1330 struct ExecveArgs {
1331 #[pyarg(any)]
1332 path: OsPath,
1333 #[pyarg(any)]
1334 argv: Either<PyListRef, PyTupleRef>,
1335 #[pyarg(any)]
1336 env: ArgMapping,
1337 }
1338
1339 #[pyfunction]
1340 fn execve(args: ExecveArgs, vm: &VirtualMachine) -> PyResult<()> {
1341 let ExecveArgs { path, argv, env } = args;
1342 if !vm.state.allow_exec() {
1343 return Err(
1344 vm.new_runtime_error("exec not supported for isolated subinterpreters".to_owned())
1345 );
1346 }
1347 let path = path.into_cstring(vm)?;
1348
1349 let argv = vm.extract_elements_with(argv.as_ref(), |obj| {
1350 OsPath::try_from_object(vm, obj)?.into_cstring(vm)
1351 })?;
1352 let argv: Vec<&CStr> = argv.iter().map(|entry| entry.as_c_str()).collect();
1353
1354 let first = argv
1355 .first()
1356 .ok_or_else(|| vm.new_value_error("execve() arg 2 must not be empty"))?;
1357
1358 if first.to_bytes().is_empty() {
1359 return Err(vm.new_value_error("execve() arg 2 first element cannot be empty"));
1360 }
1361
1362 let env = crate::stdlib::os::envobj_to_dict(env, vm)?;
1363 let env = env
1364 .into_iter()
1365 .map(|(k, v)| -> PyResult<_> {
1366 let (key, value) = (
1367 OsPath::try_from_object(vm, k)?.into_bytes(),
1368 OsPath::try_from_object(vm, v)?.into_bytes(),
1369 );
1370
1371 if key.is_empty() || memchr::memchr(b'=', &key).is_some() {
1372 return Err(vm.new_value_error("illegal environment variable name"));
1373 }
1374
1375 let mut entry = key;
1376 entry.push(b'=');
1377 entry.extend_from_slice(&value);
1378
1379 CString::new(entry).map_err(|err| err.into_pyexception(vm))
1380 })
1381 .collect::<Result<Vec<_>, _>>()?;
1382
1383 let env: Vec<&CStr> = env.iter().map(|entry| entry.as_c_str()).collect();
1384
1385 rustpython_host_env::posix::execve(&path, &argv, &env)
1386 .map_err(|err| err.into_pyexception(vm))?;
1387 Ok(())
1388 }
1389
1390 #[pyfunction]
1391 fn getppid(vm: &VirtualMachine) -> PyObjectRef {
1392 let ppid = rustpython_host_env::posix::getppid();
1393 vm.ctx.new_int(ppid).into()
1394 }
1395
1396 #[pyfunction]
1397 fn getgid(vm: &VirtualMachine) -> PyObjectRef {
1398 let gid = rustpython_host_env::posix::getgid();
1399 vm.ctx.new_int(gid).into()
1400 }
1401
1402 #[pyfunction]
1403 fn getegid(vm: &VirtualMachine) -> PyObjectRef {
1404 let egid = rustpython_host_env::posix::getegid();
1405 vm.ctx.new_int(egid).into()
1406 }
1407
1408 #[derive(FromArgs)]
1409 struct GetPgidArgs {
1410 #[pyarg(any)]
1411 pid: u32,
1412 }
1413
1414 #[pyfunction]
1415 fn getpgid(pid: GetPgidArgs, vm: &VirtualMachine) -> PyResult {
1416 let pid = pid.pid;
1417 let pgid = rustpython_host_env::posix::getpgid(pid).map_err(|e| e.into_pyexception(vm))?;
1418 Ok(vm.new_pyobj(pgid))
1419 }
1420
1421 #[pyfunction]
1422 fn getpgrp(vm: &VirtualMachine) -> PyObjectRef {
1423 vm.ctx.new_int(rustpython_host_env::posix::getpgrp()).into()
1424 }
1425
1426 #[cfg(not(target_os = "redox"))]
1427 #[pyfunction]
1428 fn getsid(pid: u32, vm: &VirtualMachine) -> PyResult {
1429 let sid = rustpython_host_env::posix::getsid(pid).map_err(|e| e.into_pyexception(vm))?;
1430 Ok(vm.new_pyobj(sid))
1431 }
1432
1433 #[pyfunction]
1434 fn getuid(vm: &VirtualMachine) -> PyObjectRef {
1435 let uid = rustpython_host_env::posix::getuid();
1436 vm.ctx.new_int(uid).into()
1437 }
1438
1439 #[pyfunction]
1440 fn geteuid(vm: &VirtualMachine) -> PyObjectRef {
1441 let euid = rustpython_host_env::posix::geteuid();
1442 vm.ctx.new_int(euid).into()
1443 }
1444
1445 #[cfg(not(any(target_os = "wasi", target_os = "android")))]
1446 #[pyfunction]
1447 fn setgid(gid: RawGid, vm: &VirtualMachine) -> PyResult<()> {
1448 rustpython_host_env::posix::setgid(gid.0).map_err(|err| err.into_pyexception(vm))
1449 }
1450
1451 #[cfg(not(any(target_os = "wasi", target_os = "android", target_os = "redox")))]
1452 #[pyfunction]
1453 fn setegid(egid: RawGid, vm: &VirtualMachine) -> PyResult<()> {
1454 rustpython_host_env::posix::setegid(egid.0).map_err(|err| err.into_pyexception(vm))
1455 }
1456
1457 #[pyfunction]
1458 fn setpgid(pid: u32, pgrp: u32, vm: &VirtualMachine) -> PyResult<()> {
1459 rustpython_host_env::posix::setpgid(pid, pgrp).map_err(|err| err.into_pyexception(vm))
1460 }
1461
1462 #[pyfunction]
1463 fn setpgrp(vm: &VirtualMachine) -> PyResult<()> {
1464 rustpython_host_env::posix::setpgrp().map_err(|err| err.into_pyexception(vm))
1466 }
1467
1468 #[cfg(not(any(target_os = "wasi", target_os = "redox")))]
1469 #[pyfunction]
1470 fn setsid(vm: &VirtualMachine) -> PyResult<()> {
1471 rustpython_host_env::posix::setsid().map_err(|err| err.into_pyexception(vm))
1472 }
1473
1474 #[cfg(not(any(target_os = "wasi", target_os = "redox")))]
1475 #[pyfunction]
1476 fn tcgetpgrp(fd: i32, vm: &VirtualMachine) -> PyResult<libc::pid_t> {
1477 use std::os::fd::BorrowedFd;
1478 let fd = unsafe { BorrowedFd::borrow_raw(fd) };
1479 rustpython_host_env::posix::tcgetpgrp(fd).map_err(|err| err.into_pyexception(vm))
1480 }
1481
1482 #[cfg(not(any(target_os = "wasi", target_os = "redox")))]
1483 #[pyfunction]
1484 fn tcsetpgrp(fd: i32, pgid: libc::pid_t, vm: &VirtualMachine) -> PyResult<()> {
1485 use std::os::fd::BorrowedFd;
1486 let fd = unsafe { BorrowedFd::borrow_raw(fd) };
1487 rustpython_host_env::posix::tcsetpgrp(fd, pgid).map_err(|err| err.into_pyexception(vm))
1488 }
1489
1490 fn try_from_id(vm: &VirtualMachine, obj: &PyObject, typ_name: &str) -> PyResult<u32> {
1491 use core::cmp::Ordering;
1492 let i = obj
1493 .try_to_ref::<PyInt>(vm)
1494 .map_err(|_| {
1495 vm.new_type_error(format!(
1496 "an integer is required (got type {})",
1497 obj.class().name()
1498 ))
1499 })?
1500 .try_to_primitive::<i64>(vm)?;
1501
1502 match i.cmp(&-1) {
1503 Ordering::Greater => Ok(i.try_into().map_err(|_| {
1504 vm.new_overflow_error(format!("{typ_name} is larger than maximum"))
1505 })?),
1506 Ordering::Less => {
1507 Err(vm.new_overflow_error(format!("{typ_name} is less than minimum")))
1508 }
1509 Ordering::Equal => Ok(-1i32 as u32),
1513 }
1514 }
1515
1516 #[derive(Clone, Copy)]
1517 struct RawUid(u32);
1518
1519 #[derive(Clone, Copy)]
1520 struct RawGid(u32);
1521
1522 impl TryFromObject for RawUid {
1523 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
1524 try_from_id(vm, &obj, "uid").map(Self)
1525 }
1526 }
1527
1528 impl TryFromObject for RawGid {
1529 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
1530 try_from_id(vm, &obj, "gid").map(Self)
1531 }
1532 }
1533
1534 #[cfg(not(any(target_os = "wasi", target_os = "android")))]
1535 #[pyfunction]
1536 fn setuid(uid: RawUid, vm: &VirtualMachine) -> PyResult<()> {
1537 rustpython_host_env::posix::setuid(uid.0).map_err(|err| err.into_pyexception(vm))
1538 }
1539
1540 #[cfg(not(any(target_os = "wasi", target_os = "android", target_os = "redox")))]
1541 #[pyfunction]
1542 fn seteuid(euid: RawUid, vm: &VirtualMachine) -> PyResult<()> {
1543 rustpython_host_env::posix::seteuid(euid.0).map_err(|err| err.into_pyexception(vm))
1544 }
1545
1546 #[cfg(not(any(target_os = "wasi", target_os = "android", target_os = "redox")))]
1547 #[pyfunction]
1548 fn setreuid(ruid: RawUid, euid: RawUid, vm: &VirtualMachine) -> PyResult<()> {
1549 rustpython_host_env::posix::setreuid(ruid.0, euid.0).map_err(|err| err.into_pyexception(vm))
1550 }
1551
1552 #[cfg(any(
1554 target_os = "android",
1555 target_os = "freebsd",
1556 target_os = "linux",
1557 target_os = "openbsd"
1558 ))]
1559 #[pyfunction]
1560 fn setresuid(ruid: RawUid, euid: RawUid, suid: RawUid, vm: &VirtualMachine) -> PyResult<()> {
1561 rustpython_host_env::posix::setresuid(ruid.0, euid.0, suid.0)
1562 .map_err(|err| err.into_pyexception(vm))
1563 }
1564
1565 #[cfg(not(target_os = "wasi"))]
1566 #[pyfunction]
1567 fn login_tty(fd: BorrowedFd<'_>, vm: &VirtualMachine) -> PyResult<()> {
1568 rustpython_host_env::posix::login_tty(fd.as_raw_fd())
1569 .map_err(|err| err.into_pyexception(vm))
1570 }
1571
1572 #[cfg(not(target_os = "redox"))]
1573 #[pyfunction]
1574 fn openpty(vm: &VirtualMachine) -> PyResult<(OwnedFd, OwnedFd)> {
1575 rustpython_host_env::posix::openpty().map_err(|err| err.into_pyexception(vm))
1576 }
1577
1578 #[pyfunction]
1579 fn ttyname(fd: BorrowedFd<'_>, vm: &VirtualMachine) -> PyResult {
1580 let name = rustpython_host_env::posix::ttyname(fd).map_err(|e| e.into_pyexception(vm))?;
1581 let name = name.into_string().unwrap();
1582 Ok(vm.ctx.new_str(name).into())
1583 }
1584
1585 #[pyfunction]
1586 fn umask(mask: libc::mode_t) -> libc::mode_t {
1587 rustpython_host_env::posix::umask(mask)
1588 }
1589
1590 #[pyfunction]
1591 fn uname(vm: &VirtualMachine) -> PyResult<_os::UnameResultData> {
1592 let info = rustpython_host_env::posix::uname_info().map_err(|err| {
1593 let start = err.error.valid_up_to();
1594 let end = err
1595 .error
1596 .error_len()
1597 .map_or(err.bytes.len(), |len| start + len);
1598 vm.new_unicode_decode_error(
1599 vm.ctx.new_str("utf-8"),
1600 vm.ctx.new_bytes(err.bytes),
1601 start,
1602 end,
1603 vm.ctx.new_str(err.error.to_string()),
1604 )
1605 })?;
1606 Ok(_os::UnameResultData {
1607 sysname: info.sysname,
1608 nodename: info.nodename,
1609 release: info.release,
1610 version: info.version,
1611 machine: info.machine,
1612 })
1613 }
1614
1615 #[pyfunction]
1616 fn sync() {
1617 #[cfg(not(any(target_os = "redox", target_os = "android")))]
1618 rustpython_host_env::posix::sync();
1619 }
1620
1621 #[cfg(any(target_os = "android", target_os = "linux", target_os = "openbsd"))]
1623 #[pyfunction]
1624 fn getresuid(vm: &VirtualMachine) -> PyResult<(u32, u32, u32)> {
1625 rustpython_host_env::posix::getresuid().map_err(|err| err.into_pyexception(vm))
1626 }
1627
1628 #[cfg(any(target_os = "android", target_os = "linux", target_os = "openbsd"))]
1630 #[pyfunction]
1631 fn getresgid(vm: &VirtualMachine) -> PyResult<(u32, u32, u32)> {
1632 rustpython_host_env::posix::getresgid().map_err(|err| err.into_pyexception(vm))
1633 }
1634
1635 #[cfg(any(target_os = "freebsd", target_os = "linux", target_os = "openbsd"))]
1637 #[pyfunction]
1638 fn setresgid(rgid: RawGid, egid: RawGid, sgid: RawGid, vm: &VirtualMachine) -> PyResult<()> {
1639 rustpython_host_env::posix::setresgid(rgid.0, egid.0, sgid.0)
1640 .map_err(|err| err.into_pyexception(vm))
1641 }
1642
1643 #[cfg(not(any(target_os = "wasi", target_os = "android", target_os = "redox")))]
1644 #[pyfunction]
1645 fn setregid(rgid: RawGid, egid: RawGid, vm: &VirtualMachine) -> PyResult<()> {
1646 rustpython_host_env::posix::setregid(rgid.0, egid.0).map_err(|err| err.into_pyexception(vm))
1647 }
1648
1649 #[cfg(any(target_os = "freebsd", target_os = "linux", target_os = "openbsd"))]
1651 #[pyfunction]
1652 fn initgroups(username: PyUtf8StrRef, gid: RawGid, vm: &VirtualMachine) -> PyResult<()> {
1653 let user = username.to_cstring(vm)?;
1654 rustpython_host_env::posix::initgroups(&user, gid.0).map_err(|err| err.into_pyexception(vm))
1655 }
1656
1657 #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
1659 #[pyfunction]
1660 fn setgroups(groups: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
1661 groups
1662 .try_sequence(vm)
1663 .map_err(|_| vm.new_type_error("setgroups argument must be a sequence"))?;
1664 let gids = vm.extract_elements_with(&groups, |gid| {
1665 RawGid::try_from_object(vm, gid).map(|gid| gid.0)
1666 })?;
1667 rustpython_host_env::posix::setgroups_raw(&gids).map_err(|err| err.into_pyexception(vm))
1668 }
1669
1670 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1671 fn envp_from_dict(
1672 env: crate::function::ArgMapping,
1673 vm: &VirtualMachine,
1674 ) -> PyResult<Vec<CString>> {
1675 let items = env.mapping().items(vm)?;
1676
1677 let items = vm.ctx.new_list(
1679 items
1680 .get_iter(vm)?
1681 .iter(vm)?
1682 .collect::<PyResult<Vec<_>>>()?,
1683 );
1684
1685 items
1686 .borrow_vec()
1687 .iter()
1688 .map(|item| {
1689 let tuple = item
1690 .downcast_ref::<crate::builtins::PyTuple>()
1691 .ok_or_else(|| vm.new_type_error("items() should return tuples"))?;
1692 let tuple_items = tuple.as_slice();
1693 if tuple_items.len() != 2 {
1694 return Err(vm.new_value_error("items() tuples should have exactly 2 elements"));
1695 }
1696 Ok((tuple_items[0].clone(), tuple_items[1].clone()))
1697 })
1698 .collect::<PyResult<Vec<_>>>()?
1699 .into_iter()
1700 .map(|(k, v)| {
1701 let k = OsPath::try_from_object(vm, k)?.into_bytes();
1702 let v = OsPath::try_from_object(vm, v)?.into_bytes();
1703 if k.contains(&0) {
1704 return Err(vm.new_value_error("envp dict key cannot contain a nul byte"));
1705 }
1706 if k.contains(&b'=') {
1707 return Err(vm.new_value_error("envp dict key cannot contain a '=' character"));
1708 }
1709 if v.contains(&0) {
1710 return Err(vm.new_value_error("envp dict value cannot contain a nul byte"));
1711 }
1712 let mut env = k;
1713 env.push(b'=');
1714 env.extend(v);
1715 Ok(unsafe { CString::from_vec_unchecked(env) })
1716 })
1717 .collect()
1718 }
1719
1720 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1721 #[derive(FromArgs)]
1722 pub(super) struct PosixSpawnArgs {
1723 #[pyarg(positional)]
1724 path: OsPath,
1725 #[pyarg(positional)]
1726 argv: PyObjectRef,
1727 #[pyarg(positional)]
1728 env: Option<crate::function::ArgMapping>,
1729 #[pyarg(named, default, py_default = "()")]
1730 file_actions: Option<crate::function::ArgIterable<PyTupleRef>>,
1731 #[pyarg(named, optional)]
1732 setpgroup: Option<libc::pid_t>,
1733 #[pyarg(named, default)]
1734 resetids: bool,
1735 #[pyarg(named, default)]
1736 setsid: bool,
1737 #[pyarg(named, default, py_default = "()")]
1738 setsigmask: Option<crate::function::ArgIterable<i32>>,
1739 #[pyarg(named, default, py_default = "()")]
1740 setsigdef: Option<crate::function::ArgIterable<i32>>,
1741 #[pyarg(named, optional)]
1742 scheduler: Option<PyObjectRef>,
1743 }
1744
1745 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1746 #[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
1747 #[repr(i32)]
1748 enum PosixSpawnFileActionIdentifier {
1749 Open,
1750 Close,
1751 Dup2,
1752 }
1753
1754 #[cfg(all(
1755 any(target_os = "linux", target_os = "freebsd", target_os = "android"),
1756 not(target_env = "musl")
1757 ))]
1758 fn parse_posix_spawn_scheduler(
1759 scheduler: Option<PyObjectRef>,
1760 vm: &VirtualMachine,
1761 ) -> PyResult<Option<rustpython_host_env::posix::PosixSpawnScheduler>> {
1762 let Some(obj) = scheduler else {
1763 return Ok(None);
1764 };
1765 if obj.is(&vm.ctx.none()) {
1766 return Ok(None);
1767 }
1768 let Some(tuple) = obj.downcast_ref::<PyTuple>() else {
1769 return Err(vm.new_type_error("scheduler must be a tuple or None"));
1770 };
1771 if tuple.as_slice().len() != 2 {
1772 return Err(vm.new_type_error("A scheduler tuple must have two elements"));
1773 }
1774 let policy = if tuple.as_slice()[0].is(&vm.ctx.none()) {
1775 None
1776 } else {
1777 Some(i32::try_from_object(vm, tuple.as_slice()[0].clone())?)
1778 };
1779 let param = super::posix_sched::convert_sched_param(&tuple.as_slice()[1], vm)?;
1780 Ok(Some(rustpython_host_env::posix::PosixSpawnScheduler {
1781 policy,
1782 param,
1783 }))
1784 }
1785
1786 #[cfg(any(target_os = "macos", target_env = "musl"))]
1787 fn parse_posix_spawn_scheduler(
1788 scheduler: Option<PyObjectRef>,
1789 vm: &VirtualMachine,
1790 ) -> PyResult<()> {
1791 let Some(obj) = scheduler else {
1792 return Ok(());
1793 };
1794 if obj.is(&vm.ctx.none()) {
1795 return Ok(());
1796 }
1797 let Some(tuple) = obj.downcast_ref::<PyTuple>() else {
1798 return Err(vm.new_type_error("scheduler must be a tuple or None"));
1799 };
1800 if tuple.as_slice().len() != 2 {
1801 return Err(vm.new_type_error("A scheduler tuple must have two elements"));
1802 }
1803 Err(vm.new_not_implemented_error("The scheduler option is not supported in this system."))
1804 }
1805
1806 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1807 impl PosixSpawnArgs {
1808 fn spawn(self, spawnp: bool, vm: &VirtualMachine) -> PyResult<libc::pid_t> {
1809 use crate::TryFromBorrowedObject;
1810
1811 let path = self
1812 .path
1813 .clone()
1814 .into_cstring(vm)
1815 .map_err(|_| vm.new_value_error("path should not have nul bytes"))?;
1816
1817 let function_name = if spawnp {
1818 "posix_spawnp"
1819 } else {
1820 "posix_spawn"
1821 };
1822 if !self.argv.fast_isinstance(vm.ctx.types.list_type)
1823 && !self.argv.fast_isinstance(vm.ctx.types.tuple_type)
1824 {
1825 return Err(
1826 vm.new_type_error(format!("{function_name}: argv must be a tuple or list"))
1827 );
1828 }
1829
1830 let mut file_actions = Vec::new();
1831 if let Some(it) = self.file_actions {
1832 for action in it.iter(vm)? {
1833 let action = action?;
1834 let (id, args) = action.as_slice().split_first().ok_or_else(|| {
1835 vm.new_type_error("Each file_actions element must be a non-empty tuple")
1836 })?;
1837 let id = i32::try_from_borrowed_object(vm, id)?;
1838 let id = PosixSpawnFileActionIdentifier::try_from(id)
1839 .map_err(|_| vm.new_type_error("Unknown file_actions identifier"))?;
1840 let args: crate::function::FuncArgs = args.to_vec().into();
1841 let parsed = match id {
1842 PosixSpawnFileActionIdentifier::Open => {
1843 let (fd, path, oflag, mode): (_, OsPath, _, _) = args.bind(vm)?;
1844 let path = CString::new(path.into_bytes()).map_err(|_| {
1845 vm.new_value_error(
1846 "POSIX_SPAWN_OPEN path should not have nul bytes",
1847 )
1848 })?;
1849 rustpython_host_env::posix::PosixSpawnFileAction::Open {
1850 fd,
1851 path,
1852 oflag,
1853 mode,
1854 }
1855 }
1856 PosixSpawnFileActionIdentifier::Close => {
1857 let (fd,) = args.bind(vm)?;
1858 rustpython_host_env::posix::PosixSpawnFileAction::Close { fd }
1859 }
1860 PosixSpawnFileActionIdentifier::Dup2 => {
1861 let (fd, newfd) = args.bind(vm)?;
1862 rustpython_host_env::posix::PosixSpawnFileAction::Dup2 { fd, newfd }
1863 }
1864 };
1865 file_actions.push(parsed);
1866 }
1867 }
1868
1869 let collect_signals = |sigs: crate::function::ArgIterable<i32>| {
1870 let mut collected = Vec::new();
1871 for sig in sigs.iter(vm)? {
1872 let sig = sig?;
1873 if !rustpython_host_env::posix::validate_posix_spawn_signal(sig) {
1874 return Err(vm.new_value_error(format!("signal number {sig} out of range")));
1875 }
1876 if !collected.contains(&sig) {
1877 collected.push(sig);
1878 }
1879 }
1880 Ok(collected)
1881 };
1882
1883 let setsigdef = self.setsigdef.map(collect_signals).transpose()?;
1884
1885 #[cfg(all(
1886 any(target_os = "linux", target_os = "freebsd", target_os = "android"),
1887 not(target_env = "musl")
1888 ))]
1889 let scheduler = parse_posix_spawn_scheduler(self.scheduler, vm)?;
1890 #[cfg(any(target_os = "macos", target_env = "musl"))]
1891 parse_posix_spawn_scheduler(self.scheduler, vm)?;
1892
1893 if self.setsid && !rustpython_host_env::posix::supports_posix_spawn_setsid() {
1894 return Err(vm.new_not_implemented_error(
1895 "setsid parameter is not supported on this platform",
1896 ));
1897 }
1898
1899 let setsigmask = self.setsigmask.map(collect_signals).transpose()?;
1900
1901 let args = vm.extract_elements_with(&self.argv, |arg| {
1902 CString::new(OsPath::try_from_object(vm, arg)?.into_bytes())
1903 .map_err(|_| vm.new_value_error("path should not have nul bytes"))
1904 })?;
1905 let env = if let Some(env_dict) = self.env {
1906 envp_from_dict(env_dict, vm)?
1907 } else {
1908 crate::host_env::os::vars_os()
1911 .map(|(k, v)| {
1912 let mut entry = k.into_vec();
1913 entry.push(b'=');
1914 entry.extend(v.into_vec());
1915 CString::new(entry).map_err(|_| {
1916 vm.new_value_error("environment string contains null byte")
1917 })
1918 })
1919 .collect::<PyResult<Vec<_>>>()?
1920 };
1921
1922 rustpython_host_env::posix::posix_spawn(rustpython_host_env::posix::PosixSpawnConfig {
1923 path: &path,
1924 args: &args,
1925 env: &env,
1926 file_actions: &file_actions,
1927 setsigdef: setsigdef.as_deref(),
1928 setpgroup: self.setpgroup,
1929 resetids: self.resetids,
1930 setsid: self.setsid,
1931 setsigmask: setsigmask.as_deref(),
1932 spawnp,
1933 #[cfg(all(
1934 any(target_os = "linux", target_os = "freebsd", target_os = "android"),
1935 not(target_env = "musl")
1936 ))]
1937 scheduler,
1938 })
1939 .map_err(|err| OSErrorBuilder::with_filename(&err, self.path, vm))
1940 }
1941 }
1942
1943 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1944 #[pyfunction]
1945 fn posix_spawn(args: PosixSpawnArgs, vm: &VirtualMachine) -> PyResult<libc::pid_t> {
1946 args.spawn(false, vm)
1947 }
1948
1949 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1950 #[pyfunction]
1951 fn posix_spawnp(args: PosixSpawnArgs, vm: &VirtualMachine) -> PyResult<libc::pid_t> {
1952 args.spawn(true, vm)
1953 }
1954
1955 #[derive(FromArgs)]
1956 struct StatusArg {
1957 #[pyarg(any)]
1958 status: i32,
1959 }
1960
1961 #[pyfunction(name = "WCOREDUMP")]
1962 fn wcoredump(status: i32) -> bool {
1963 rustpython_host_env::posix::wcoredump(status)
1964 }
1965
1966 #[pyfunction(name = "WIFCONTINUED")]
1967 fn wifcontinued(status: StatusArg) -> bool {
1968 rustpython_host_env::posix::wifcontinued(status.status)
1969 }
1970
1971 #[pyfunction(name = "WIFSTOPPED")]
1972 fn wifstopped(status: StatusArg) -> bool {
1973 rustpython_host_env::posix::wifstopped(status.status)
1974 }
1975
1976 #[pyfunction(name = "WIFSIGNALED")]
1977 fn wifsignaled(status: StatusArg) -> bool {
1978 rustpython_host_env::posix::wifsignaled(status.status)
1979 }
1980
1981 #[pyfunction(name = "WIFEXITED")]
1982 fn wifexited(status: StatusArg) -> bool {
1983 rustpython_host_env::posix::wifexited(status.status)
1984 }
1985
1986 #[pyfunction(name = "WEXITSTATUS")]
1987 fn wexitstatus(status: StatusArg) -> i32 {
1988 rustpython_host_env::posix::wexitstatus(status.status)
1989 }
1990
1991 #[pyfunction(name = "WSTOPSIG")]
1992 fn wstopsig(status: StatusArg) -> i32 {
1993 rustpython_host_env::posix::wstopsig(status.status)
1994 }
1995
1996 #[pyfunction(name = "WTERMSIG")]
1997 fn wtermsig(status: StatusArg) -> i32 {
1998 rustpython_host_env::posix::wtermsig(status.status)
1999 }
2000
2001 #[cfg(target_os = "linux")]
2002 #[pyfunction]
2003 fn pidfd_open(
2004 pid: libc::pid_t,
2005 flags: OptionalArg<u32>,
2006 vm: &VirtualMachine,
2007 ) -> PyResult<OwnedFd> {
2008 let flags = flags.unwrap_or(0);
2009 rustpython_host_env::posix::pidfd_open(pid, flags).map_err(|err| err.into_pyexception(vm))
2010 }
2011
2012 #[pyfunction]
2013 fn waitpid(
2014 pid: libc::pid_t,
2015 options: i32,
2016 vm: &VirtualMachine,
2017 ) -> PyResult<(libc::pid_t, i32)> {
2018 let mut status = 0;
2019 loop {
2020 let res =
2021 vm.allow_threads(|| rustpython_host_env::posix::waitpid(pid, &mut status, options));
2022 match res {
2023 Err(err) if err.raw_os_error() == Some(libc::EINTR) => {
2024 vm.check_signals()?;
2025 continue;
2026 }
2027 Err(err) => return Err(err.into_pyexception(vm)),
2028 Ok(res) => return Ok((res, status)),
2029 }
2030 }
2031 }
2032
2033 #[pyfunction]
2034 fn wait(vm: &VirtualMachine) -> PyResult<(libc::pid_t, i32)> {
2035 waitpid(-1, 0, vm)
2036 }
2037
2038 fn rusage_to_py(
2039 ru: rustpython_host_env::resource::RUsage,
2040 vm: &VirtualMachine,
2041 ) -> PyResult<PyObjectRef> {
2042 let resource = vm.import("resource", 0)?;
2043 let struct_rusage = resource.get_attr("struct_rusage", vm)?;
2044 let tv = |t: rustpython_host_env::resource::timeval| {
2045 t.tv_sec as f64 + (t.tv_usec as f64 / 1_000_000.0)
2046 };
2047 let fields = vm.ctx.new_tuple(vec![
2048 vm.ctx.new_float(tv(ru.ru_utime)).into(),
2049 vm.ctx.new_float(tv(ru.ru_stime)).into(),
2050 vm.ctx.new_int(ru.ru_maxrss).into(),
2051 vm.ctx.new_int(ru.ru_ixrss).into(),
2052 vm.ctx.new_int(ru.ru_idrss).into(),
2053 vm.ctx.new_int(ru.ru_isrss).into(),
2054 vm.ctx.new_int(ru.ru_minflt).into(),
2055 vm.ctx.new_int(ru.ru_majflt).into(),
2056 vm.ctx.new_int(ru.ru_nswap).into(),
2057 vm.ctx.new_int(ru.ru_inblock).into(),
2058 vm.ctx.new_int(ru.ru_oublock).into(),
2059 vm.ctx.new_int(ru.ru_msgsnd).into(),
2060 vm.ctx.new_int(ru.ru_msgrcv).into(),
2061 vm.ctx.new_int(ru.ru_nsignals).into(),
2062 vm.ctx.new_int(ru.ru_nvcsw).into(),
2063 vm.ctx.new_int(ru.ru_nivcsw).into(),
2064 ]);
2065 struct_rusage.call((fields,), vm)
2066 }
2067
2068 fn wait_with_rusage<F>(vm: &VirtualMachine, wait: F) -> PyResult<PyTupleRef>
2069 where
2070 F: Fn() -> std::io::Result<(libc::pid_t, i32, rustpython_host_env::resource::RUsage)>,
2071 {
2072 loop {
2073 match vm.allow_threads(&wait) {
2074 Err(err) if err.raw_os_error() == Some(libc::EINTR) => {
2075 vm.check_signals()?;
2076 continue;
2077 }
2078 Err(err) => return Err(err.into_pyexception(vm)),
2079 Ok((pid, status, ru)) => {
2080 let rusage = rusage_to_py(ru, vm)?;
2081 return Ok(vm.new_tuple((pid, status, rusage)));
2082 }
2083 }
2084 }
2085 }
2086
2087 #[derive(FromArgs)]
2088 struct Wait3Args {
2089 #[pyarg(any)]
2090 options: i32,
2091 }
2092
2093 #[pyfunction]
2094 fn wait3(options: Wait3Args, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
2095 let options = options.options;
2096 wait_with_rusage(vm, || rustpython_host_env::posix::wait3(options))
2097 }
2098
2099 #[derive(FromArgs)]
2100 struct Wait4Args {
2101 #[pyarg(any)]
2102 pid: libc::pid_t,
2103 #[pyarg(any)]
2104 options: i32,
2105 }
2106
2107 #[pyfunction]
2108 fn wait4(args: Wait4Args, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
2109 wait_with_rusage(vm, || {
2110 rustpython_host_env::posix::wait4(args.pid, args.options)
2111 })
2112 }
2113
2114 #[pyfunction]
2115 fn kill(pid: i32, signal: isize, vm: &VirtualMachine) -> PyResult<()> {
2116 rustpython_host_env::posix::kill(pid, signal as i32).map_err(|err| err.into_pyexception(vm))
2117 }
2118
2119 #[pyfunction]
2120 fn killpg(pgid: i32, signal: isize, vm: &VirtualMachine) -> PyResult<()> {
2121 rustpython_host_env::posix::killpg(pgid, signal as i32)
2122 .map_err(|err| err.into_pyexception(vm))
2123 }
2124
2125 #[pyfunction]
2126 fn get_terminal_size(
2127 fd: OptionalArg<i32>,
2128 vm: &VirtualMachine,
2129 ) -> PyResult<_os::TerminalSizeData> {
2130 let (columns, lines) =
2131 rustpython_host_env::posix::get_terminal_size(fd.unwrap_or(libc::STDOUT_FILENO))
2132 .map(|(columns, lines)| (columns.into(), lines.into()))
2133 .map_err(|err| err.into_pyexception(vm))?;
2134 Ok(_os::TerminalSizeData { columns, lines })
2135 }
2136
2137 #[cfg(target_os = "macos")]
2138 #[pyfunction]
2139 fn _fcopyfile(in_fd: i32, out_fd: i32, flags: i32, vm: &VirtualMachine) -> PyResult<()> {
2140 rustpython_host_env::posix::fcopyfile(in_fd, out_fd, flags as u32)
2141 .map_err(|err| err.into_pyexception(vm))
2142 }
2143
2144 #[pyfunction]
2145 fn dup(fd: BorrowedFd<'_>, vm: &VirtualMachine) -> PyResult<OwnedFd> {
2146 rustpython_host_env::posix::dup_noninheritable(fd).map_err(|e| e.into_pyexception(vm))
2147 }
2148
2149 #[derive(FromArgs)]
2150 struct Dup2Args<'fd> {
2151 #[pyarg(any)]
2152 fd: BorrowedFd<'fd>,
2153 #[pyarg(any)]
2154 fd2: OwnedFd,
2155 #[pyarg(any, default = true)]
2156 inheritable: bool,
2157 }
2158
2159 #[pyfunction]
2160 fn dup2(args: Dup2Args<'_>, vm: &VirtualMachine) -> PyResult<OwnedFd> {
2161 rustpython_host_env::posix::dup2(args.fd, args.fd2, args.inheritable)
2162 .map_err(|e| e.into_pyexception(vm))
2163 }
2164
2165 pub(crate) fn support_funcs() -> Vec<SupportFunc> {
2166 vec![
2167 SupportFunc::new(
2168 "chmod",
2169 Some(false),
2170 Some(false),
2171 Some(cfg!(any(
2172 target_os = "macos",
2173 target_os = "freebsd",
2174 target_os = "netbsd"
2175 ))),
2176 ),
2177 #[cfg(not(target_os = "redox"))]
2178 SupportFunc::new("chroot", Some(false), None, None),
2179 #[cfg(not(target_os = "redox"))]
2180 SupportFunc::new("chown", Some(true), Some(true), Some(true)),
2181 #[cfg(not(target_os = "redox"))]
2182 SupportFunc::new("lchown", None, None, None),
2183 #[cfg(not(target_os = "redox"))]
2184 SupportFunc::new("fchown", Some(true), None, Some(true)),
2185 #[cfg(not(target_os = "redox"))]
2186 SupportFunc::new("mknod", Some(true), Some(MKNOD_DIR_FD), Some(false)),
2187 SupportFunc::new("umask", Some(false), Some(false), Some(false)),
2188 SupportFunc::new("execv", None, None, None),
2189 SupportFunc::new("pathconf", Some(true), None, None),
2190 SupportFunc::new("fpathconf", Some(true), None, None),
2191 SupportFunc::new("fchdir", Some(true), None, None),
2192 ]
2193 }
2194
2195 #[pyfunction]
2196 fn getlogin(vm: &VirtualMachine) -> PyResult<String> {
2197 let Some(login) = rustpython_host_env::posix::getlogin() else {
2201 return Err(vm.new_os_error("unable to determine login name"));
2202 };
2203 login.to_str().map(|s| s.to_owned()).map_err(|e| {
2204 vm.new_unicode_decode_error(
2205 vm.ctx.new_str("utf-8"),
2206 vm.ctx.new_bytes(login.as_bytes().to_vec()),
2207 e.valid_up_to(),
2208 e.error_len()
2209 .map_or(login.as_bytes().len(), |n| e.valid_up_to() + n),
2210 vm.ctx.new_str("unable to decode login name"),
2211 )
2212 })
2213 }
2214
2215 #[cfg(any(
2217 target_os = "android",
2218 target_os = "freebsd",
2219 target_os = "linux",
2220 target_os = "openbsd"
2221 ))]
2222 #[pyfunction]
2223 fn getgrouplist(
2224 user: PyUtf8StrRef,
2225 group: u32,
2226 vm: &VirtualMachine,
2227 ) -> PyResult<Vec<PyObjectRef>> {
2228 let user = user.to_cstring(vm)?;
2229 let group_ids = rustpython_host_env::posix::getgrouplist(&user, group)
2230 .map_err(|err| err.into_pyexception(vm))?;
2231 Ok(group_ids.into_iter().map(|gid| vm.new_pyobj(gid)).collect())
2232 }
2233
2234 #[cfg(not(target_os = "redox"))]
2235 #[derive(FromArgs)]
2236 struct GetPriorityArgs {
2237 #[pyarg(any)]
2238 which: rustpython_host_env::posix::PriorityWhichType,
2239 #[pyarg(any)]
2240 who: rustpython_host_env::posix::PriorityWhoType,
2241 }
2242
2243 #[cfg(not(target_os = "redox"))]
2244 #[pyfunction]
2245 fn getpriority(args: GetPriorityArgs, vm: &VirtualMachine) -> PyResult {
2246 let GetPriorityArgs { which, who } = args;
2247 rustpython_host_env::posix::getpriority(which, who)
2248 .map(|retval| vm.ctx.new_int(retval).into())
2249 .map_err(|err| err.into_pyexception(vm))
2250 }
2251
2252 #[cfg(not(target_os = "redox"))]
2253 #[derive(FromArgs)]
2254 struct SetPriorityArgs {
2255 #[pyarg(any)]
2256 which: rustpython_host_env::posix::PriorityWhichType,
2257 #[pyarg(any)]
2258 who: rustpython_host_env::posix::PriorityWhoType,
2259 #[pyarg(any)]
2260 priority: i32,
2261 }
2262
2263 #[cfg(not(target_os = "redox"))]
2264 #[pyfunction]
2265 fn setpriority(args: SetPriorityArgs, vm: &VirtualMachine) -> PyResult<()> {
2266 let SetPriorityArgs {
2267 which,
2268 who,
2269 priority,
2270 } = args;
2271 rustpython_host_env::posix::setpriority(which, who, priority)
2272 .map_err(|err| err.into_pyexception(vm))
2273 }
2274
2275 struct PathconfName(i32);
2276
2277 impl TryFromObject for PathconfName {
2278 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
2279 let i = match obj.downcast::<PyInt>() {
2280 Ok(int) => int.try_to_primitive(vm)?,
2281 Err(obj) => {
2282 let s = obj.downcast::<PyUtf8Str>().map_err(|_| {
2283 vm.new_type_error("configuration names must be strings or integers")
2284 })?;
2285 s.as_str()
2286 .parse::<PathconfVar>()
2287 .map_err(|_| vm.new_value_error("unrecognized configuration name"))?
2288 as i32
2289 }
2290 };
2291 Ok(Self(i))
2292 }
2293 }
2294
2295 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, EnumIter, EnumString)]
2297 #[repr(i32)]
2298 #[allow(non_camel_case_types)]
2299 pub enum PathconfVar {
2300 #[cfg(any(
2301 target_os = "dragonfly",
2302 target_os = "freebsd",
2303 target_os = "linux",
2304 target_os = "netbsd",
2305 target_os = "openbsd",
2306 target_os = "redox"
2307 ))]
2308 PC_FILESIZEBITS = host_posix::_PC_FILESIZEBITS,
2311 PC_LINK_MAX = host_posix::_PC_LINK_MAX,
2313 PC_MAX_CANON = host_posix::_PC_MAX_CANON,
2315 PC_MAX_INPUT = host_posix::_PC_MAX_INPUT,
2319 PC_NAME_MAX = host_posix::_PC_NAME_MAX,
2322 PC_PATH_MAX = host_posix::_PC_PATH_MAX,
2327 PC_PIPE_BUF = host_posix::_PC_PIPE_BUF,
2330 #[cfg(any(
2331 target_os = "android",
2332 target_os = "dragonfly",
2333 target_os = "illumos",
2334 target_os = "linux",
2335 target_os = "netbsd",
2336 target_os = "openbsd",
2337 target_os = "redox",
2338 target_os = "solaris"
2339 ))]
2340 PC_2_SYMLINKS = host_posix::_PC_2_SYMLINKS,
2342 #[cfg(any(
2343 target_os = "android",
2344 target_os = "dragonfly",
2345 target_os = "freebsd",
2346 target_os = "linux",
2347 target_os = "openbsd",
2348 target_os = "redox"
2349 ))]
2350 PC_ALLOC_SIZE_MIN = host_posix::_PC_ALLOC_SIZE_MIN,
2353 #[cfg(any(
2354 target_os = "android",
2355 target_os = "dragonfly",
2356 target_os = "freebsd",
2357 target_os = "linux",
2358 target_os = "openbsd"
2359 ))]
2360 PC_REC_INCR_XFER_SIZE = host_posix::_PC_REC_INCR_XFER_SIZE,
2363 #[cfg(any(
2364 target_os = "android",
2365 target_os = "dragonfly",
2366 target_os = "freebsd",
2367 target_os = "linux",
2368 target_os = "openbsd",
2369 target_os = "redox"
2370 ))]
2371 PC_REC_MAX_XFER_SIZE = host_posix::_PC_REC_MAX_XFER_SIZE,
2373 #[cfg(any(
2374 target_os = "android",
2375 target_os = "dragonfly",
2376 target_os = "freebsd",
2377 target_os = "linux",
2378 target_os = "openbsd",
2379 target_os = "redox"
2380 ))]
2381 PC_REC_MIN_XFER_SIZE = host_posix::_PC_REC_MIN_XFER_SIZE,
2383 #[cfg(any(
2384 target_os = "android",
2385 target_os = "dragonfly",
2386 target_os = "freebsd",
2387 target_os = "linux",
2388 target_os = "openbsd",
2389 target_os = "redox"
2390 ))]
2391 PC_REC_XFER_ALIGN = host_posix::_PC_REC_XFER_ALIGN,
2393 #[cfg(any(
2394 target_os = "android",
2395 target_os = "dragonfly",
2396 target_os = "freebsd",
2397 target_os = "illumos",
2398 target_os = "linux",
2399 target_os = "netbsd",
2400 target_os = "openbsd",
2401 target_os = "redox",
2402 target_os = "solaris"
2403 ))]
2404 PC_SYMLINK_MAX = host_posix::_PC_SYMLINK_MAX,
2406 PC_CHOWN_RESTRICTED = host_posix::_PC_CHOWN_RESTRICTED,
2411 PC_NO_TRUNC = host_posix::_PC_NO_TRUNC,
2413 PC_VDISABLE = host_posix::_PC_VDISABLE,
2416 #[cfg(any(
2417 target_os = "android",
2418 target_os = "dragonfly",
2419 target_os = "freebsd",
2420 target_os = "illumos",
2421 target_os = "linux",
2422 target_os = "openbsd",
2423 target_os = "redox",
2424 target_os = "solaris"
2425 ))]
2426 PC_ASYNC_IO = host_posix::_PC_ASYNC_IO,
2429 #[cfg(any(
2430 target_os = "android",
2431 target_os = "dragonfly",
2432 target_os = "freebsd",
2433 target_os = "illumos",
2434 target_os = "linux",
2435 target_os = "openbsd",
2436 target_os = "redox",
2437 target_os = "solaris"
2438 ))]
2439 PC_PRIO_IO = host_posix::_PC_PRIO_IO,
2442 #[cfg(any(
2443 target_os = "android",
2444 target_os = "dragonfly",
2445 target_os = "freebsd",
2446 target_os = "illumos",
2447 target_os = "linux",
2448 target_os = "netbsd",
2449 target_os = "openbsd",
2450 target_os = "redox",
2451 target_os = "solaris"
2452 ))]
2453 PC_SYNC_IO = host_posix::_PC_SYNC_IO,
2456 #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))]
2457 PC_TIMESTAMP_RESOLUTION = host_posix::_PC_TIMESTAMP_RESOLUTION,
2459 }
2460
2461 #[cfg(unix)]
2462 #[derive(FromArgs)]
2463 struct PathconfArgs {
2464 #[pyarg(any)]
2465 path: OsPathOrFd<'static>,
2466 #[pyarg(any)]
2467 name: PathconfName,
2468 }
2469
2470 #[cfg(unix)]
2471 #[pyfunction]
2472 fn pathconf(
2473 PathconfArgs {
2474 path,
2475 name: PathconfName(name),
2476 }: PathconfArgs,
2477 vm: &VirtualMachine,
2478 ) -> PyResult<Option<libc::c_long>> {
2479 match &path {
2480 OsPathOrFd::Path(path) => {
2481 let c_path = path.clone().into_cstring(vm)?;
2482 rustpython_host_env::posix::pathconf(&c_path, name)
2483 .map_err(|err| OSErrorBuilder::with_filename(&err, path.clone(), vm))
2484 }
2485 OsPathOrFd::Fd(fd) => rustpython_host_env::posix::fpathconf(fd.as_raw(), name)
2486 .map_err(|err| OSErrorBuilder::with_filename(&err, path, vm)),
2487 }
2488 }
2489
2490 #[pyfunction]
2491 fn fpathconf(
2492 fd: BorrowedFd<'_>,
2493 name: PathconfName,
2494 vm: &VirtualMachine,
2495 ) -> PyResult<Option<libc::c_long>> {
2496 let path = OsPathOrFd::Fd(fd.into());
2497 let OsPathOrFd::Fd(fd) = &path else {
2498 unreachable!()
2499 };
2500 rustpython_host_env::posix::fpathconf(fd.as_raw(), name.0)
2501 .map_err(|err| OSErrorBuilder::with_filename(&err, path, vm))
2502 }
2503
2504 #[pyattr]
2505 fn pathconf_names(vm: &VirtualMachine) -> PyDictRef {
2506 let pathname = vm.ctx.new_dict();
2507 for variant in PathconfVar::iter() {
2508 let key = vm.ctx.new_str(format!("{variant:?}"));
2510 let value = vm.ctx.new_int(variant as u8);
2512 pathname
2513 .set_item(&*key, value.into(), vm)
2514 .expect("dict set_item unexpectedly failed");
2515 }
2516 pathname
2517 }
2518
2519 #[cfg(not(target_os = "redox"))]
2520 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, EnumIter, EnumString)]
2521 #[repr(i32)]
2522 #[allow(non_camel_case_types)]
2523 pub enum SysconfVar {
2524 SC_2_CHAR_TERM = host_posix::_SC_2_CHAR_TERM,
2525 SC_2_C_BIND = host_posix::_SC_2_C_BIND,
2526 SC_2_C_DEV = host_posix::_SC_2_C_DEV,
2527 SC_2_FORT_DEV = host_posix::_SC_2_FORT_DEV,
2528 SC_2_FORT_RUN = host_posix::_SC_2_FORT_RUN,
2529 SC_2_LOCALEDEF = host_posix::_SC_2_LOCALEDEF,
2530 SC_2_SW_DEV = host_posix::_SC_2_SW_DEV,
2531 SC_2_UPE = host_posix::_SC_2_UPE,
2532 SC_2_VERSION = host_posix::_SC_2_VERSION,
2533 SC_AIO_LISTIO_MAX = host_posix::_SC_AIO_LISTIO_MAX,
2534 SC_AIO_MAX = host_posix::_SC_AIO_MAX,
2535 SC_AIO_PRIO_DELTA_MAX = host_posix::_SC_AIO_PRIO_DELTA_MAX,
2536 SC_ARG_MAX = host_posix::_SC_ARG_MAX,
2537 SC_ASYNCHRONOUS_IO = host_posix::_SC_ASYNCHRONOUS_IO,
2538 SC_ATEXIT_MAX = host_posix::_SC_ATEXIT_MAX,
2539 SC_BC_BASE_MAX = host_posix::_SC_BC_BASE_MAX,
2540 SC_BC_DIM_MAX = host_posix::_SC_BC_DIM_MAX,
2541 SC_BC_SCALE_MAX = host_posix::_SC_BC_SCALE_MAX,
2542 SC_BC_STRING_MAX = host_posix::_SC_BC_STRING_MAX,
2543 SC_CHILD_MAX = host_posix::_SC_CHILD_MAX,
2544 SC_CLK_TCK = host_posix::_SC_CLK_TCK,
2545 SC_COLL_WEIGHTS_MAX = host_posix::_SC_COLL_WEIGHTS_MAX,
2546 SC_DELAYTIMER_MAX = host_posix::_SC_DELAYTIMER_MAX,
2547 SC_EXPR_NEST_MAX = host_posix::_SC_EXPR_NEST_MAX,
2548 SC_FSYNC = host_posix::_SC_FSYNC,
2549 SC_GETGR_R_SIZE_MAX = host_posix::_SC_GETGR_R_SIZE_MAX,
2550 SC_GETPW_R_SIZE_MAX = host_posix::_SC_GETPW_R_SIZE_MAX,
2551 SC_IOV_MAX = host_posix::_SC_IOV_MAX,
2552 SC_JOB_CONTROL = host_posix::_SC_JOB_CONTROL,
2553 SC_LINE_MAX = host_posix::_SC_LINE_MAX,
2554 SC_LOGIN_NAME_MAX = host_posix::_SC_LOGIN_NAME_MAX,
2555 SC_MAPPED_FILES = host_posix::_SC_MAPPED_FILES,
2556 SC_MEMLOCK = host_posix::_SC_MEMLOCK,
2557 SC_MEMLOCK_RANGE = host_posix::_SC_MEMLOCK_RANGE,
2558 SC_MEMORY_PROTECTION = host_posix::_SC_MEMORY_PROTECTION,
2559 SC_MESSAGE_PASSING = host_posix::_SC_MESSAGE_PASSING,
2560 SC_MQ_OPEN_MAX = host_posix::_SC_MQ_OPEN_MAX,
2561 SC_MQ_PRIO_MAX = host_posix::_SC_MQ_PRIO_MAX,
2562 SC_NGROUPS_MAX = host_posix::_SC_NGROUPS_MAX,
2563 SC_NPROCESSORS_CONF = host_posix::_SC_NPROCESSORS_CONF,
2564 SC_NPROCESSORS_ONLN = host_posix::_SC_NPROCESSORS_ONLN,
2565 SC_OPEN_MAX = host_posix::_SC_OPEN_MAX,
2566 SC_PAGE_SIZE = host_posix::_SC_PAGE_SIZE,
2567 #[cfg(any(
2568 target_os = "linux",
2569 target_vendor = "apple",
2570 target_os = "netbsd",
2571 target_os = "fuchsia"
2572 ))]
2573 SC_PASS_MAX = host_posix::_SC_PASS_MAX,
2574 SC_PHYS_PAGES = host_posix::_SC_PHYS_PAGES,
2575 SC_PRIORITIZED_IO = host_posix::_SC_PRIORITIZED_IO,
2576 SC_PRIORITY_SCHEDULING = host_posix::_SC_PRIORITY_SCHEDULING,
2577 SC_REALTIME_SIGNALS = host_posix::_SC_REALTIME_SIGNALS,
2578 SC_RE_DUP_MAX = host_posix::_SC_RE_DUP_MAX,
2579 SC_RTSIG_MAX = host_posix::_SC_RTSIG_MAX,
2580 SC_SAVED_IDS = host_posix::_SC_SAVED_IDS,
2581 SC_SEMAPHORES = host_posix::_SC_SEMAPHORES,
2582 SC_SEM_NSEMS_MAX = host_posix::_SC_SEM_NSEMS_MAX,
2583 SC_SEM_VALUE_MAX = host_posix::_SC_SEM_VALUE_MAX,
2584 SC_SHARED_MEMORY_OBJECTS = host_posix::_SC_SHARED_MEMORY_OBJECTS,
2585 SC_SIGQUEUE_MAX = host_posix::_SC_SIGQUEUE_MAX,
2586 SC_STREAM_MAX = host_posix::_SC_STREAM_MAX,
2587 SC_SYNCHRONIZED_IO = host_posix::_SC_SYNCHRONIZED_IO,
2588 SC_THREADS = host_posix::_SC_THREADS,
2589 SC_THREAD_ATTR_STACKADDR = host_posix::_SC_THREAD_ATTR_STACKADDR,
2590 SC_THREAD_ATTR_STACKSIZE = host_posix::_SC_THREAD_ATTR_STACKSIZE,
2591 SC_THREAD_DESTRUCTOR_ITERATIONS = host_posix::_SC_THREAD_DESTRUCTOR_ITERATIONS,
2592 SC_THREAD_KEYS_MAX = host_posix::_SC_THREAD_KEYS_MAX,
2593 SC_THREAD_PRIORITY_SCHEDULING = host_posix::_SC_THREAD_PRIORITY_SCHEDULING,
2594 SC_THREAD_PRIO_INHERIT = host_posix::_SC_THREAD_PRIO_INHERIT,
2595 SC_THREAD_PRIO_PROTECT = host_posix::_SC_THREAD_PRIO_PROTECT,
2596 SC_THREAD_PROCESS_SHARED = host_posix::_SC_THREAD_PROCESS_SHARED,
2597 SC_THREAD_SAFE_FUNCTIONS = host_posix::_SC_THREAD_SAFE_FUNCTIONS,
2598 SC_THREAD_STACK_MIN = host_posix::_SC_THREAD_STACK_MIN,
2599 SC_THREAD_THREADS_MAX = host_posix::_SC_THREAD_THREADS_MAX,
2600 SC_TIMERS = host_posix::_SC_TIMERS,
2601 SC_TIMER_MAX = host_posix::_SC_TIMER_MAX,
2602 SC_TTY_NAME_MAX = host_posix::_SC_TTY_NAME_MAX,
2603 SC_TZNAME_MAX = host_posix::_SC_TZNAME_MAX,
2604 SC_VERSION = host_posix::_SC_VERSION,
2605 SC_XOPEN_CRYPT = host_posix::_SC_XOPEN_CRYPT,
2606 SC_XOPEN_ENH_I18N = host_posix::_SC_XOPEN_ENH_I18N,
2607 SC_XOPEN_LEGACY = host_posix::_SC_XOPEN_LEGACY,
2608 SC_XOPEN_REALTIME = host_posix::_SC_XOPEN_REALTIME,
2609 SC_XOPEN_REALTIME_THREADS = host_posix::_SC_XOPEN_REALTIME_THREADS,
2610 SC_XOPEN_SHM = host_posix::_SC_XOPEN_SHM,
2611 SC_XOPEN_UNIX = host_posix::_SC_XOPEN_UNIX,
2612 SC_XOPEN_VERSION = host_posix::_SC_XOPEN_VERSION,
2613 SC_XOPEN_XCU_VERSION = host_posix::_SC_XOPEN_XCU_VERSION,
2614 #[cfg(any(
2615 target_os = "linux",
2616 target_vendor = "apple",
2617 target_os = "netbsd",
2618 target_os = "fuchsia"
2619 ))]
2620 SC_XBS5_ILP32_OFF32 = host_posix::_SC_XBS5_ILP32_OFF32,
2621 #[cfg(any(
2622 target_os = "linux",
2623 target_vendor = "apple",
2624 target_os = "netbsd",
2625 target_os = "fuchsia"
2626 ))]
2627 SC_XBS5_ILP32_OFFBIG = host_posix::_SC_XBS5_ILP32_OFFBIG,
2628 #[cfg(any(
2629 target_os = "linux",
2630 target_vendor = "apple",
2631 target_os = "netbsd",
2632 target_os = "fuchsia"
2633 ))]
2634 SC_XBS5_LP64_OFF64 = host_posix::_SC_XBS5_LP64_OFF64,
2635 #[cfg(any(
2636 target_os = "linux",
2637 target_vendor = "apple",
2638 target_os = "netbsd",
2639 target_os = "fuchsia"
2640 ))]
2641 SC_XBS5_LPBIG_OFFBIG = host_posix::_SC_XBS5_LPBIG_OFFBIG,
2642 }
2643
2644 #[cfg(target_os = "redox")]
2645 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, EnumIter, EnumString)]
2646 #[repr(i32)]
2647 #[allow(non_camel_case_types)]
2648 pub enum SysconfVar {
2649 SC_ARG_MAX = host_posix::_SC_ARG_MAX,
2650 SC_CHILD_MAX = host_posix::_SC_CHILD_MAX,
2651 SC_CLK_TCK = host_posix::_SC_CLK_TCK,
2652 SC_NGROUPS_MAX = host_posix::_SC_NGROUPS_MAX,
2653 SC_OPEN_MAX = host_posix::_SC_OPEN_MAX,
2654 SC_STREAM_MAX = host_posix::_SC_STREAM_MAX,
2655 SC_TZNAME_MAX = host_posix::_SC_TZNAME_MAX,
2656 SC_VERSION = host_posix::_SC_VERSION,
2657 SC_PAGE_SIZE = host_posix::_SC_PAGE_SIZE,
2658 SC_RE_DUP_MAX = host_posix::_SC_RE_DUP_MAX,
2659 SC_LOGIN_NAME_MAX = host_posix::_SC_LOGIN_NAME_MAX,
2660 SC_TTY_NAME_MAX = host_posix::_SC_TTY_NAME_MAX,
2661 SC_SYMLOOP_MAX = host_posix::_SC_SYMLOOP_MAX,
2662 SC_HOST_NAME_MAX = host_posix::_SC_HOST_NAME_MAX,
2663 }
2664
2665 impl SysconfVar {
2666 pub const SC_PAGESIZE: Self = Self::SC_PAGE_SIZE;
2667 }
2668
2669 struct SysconfName(i32);
2670
2671 impl TryFromObject for SysconfName {
2672 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
2673 let i = match obj.downcast::<PyInt>() {
2674 Ok(int) => int.try_to_primitive(vm)?,
2675 Err(obj) => {
2676 let s = obj.downcast::<PyUtf8Str>().map_err(|_| {
2677 vm.new_type_error("configuration names must be strings or integers")
2678 })?;
2679 {
2680 let name = s.as_str();
2681 name.parse::<SysconfVar>().or_else(|_| {
2682 if name == "SC_PAGESIZE" {
2683 Ok(SysconfVar::SC_PAGESIZE)
2684 } else {
2685 Err(vm.new_value_error("unrecognized configuration name"))
2686 }
2687 })? as i32
2688 }
2689 }
2690 };
2691 Ok(Self(i))
2692 }
2693 }
2694
2695 #[pyfunction]
2696 fn sysconf(name: SysconfName, vm: &VirtualMachine) -> PyResult<libc::c_long> {
2697 rustpython_host_env::posix::sysconf(name.0).map_err(|err| err.into_pyexception(vm))
2698 }
2699
2700 #[pyattr]
2701 fn sysconf_names(vm: &VirtualMachine) -> PyDictRef {
2702 let names = vm.ctx.new_dict();
2703 for variant in SysconfVar::iter() {
2704 let key = vm.ctx.new_str(format!("{variant:?}"));
2706 let value = vm.ctx.new_int(variant as u8);
2708 names
2709 .set_item(&*key, value.into(), vm)
2710 .expect("dict set_item unexpectedly failed");
2711 }
2712 names
2713 }
2714
2715 #[cfg(any(target_os = "linux", target_os = "macos"))]
2716 #[derive(FromArgs)]
2717 struct SendFileArgs<'fd> {
2718 out_fd: BorrowedFd<'fd>,
2719 in_fd: BorrowedFd<'fd>,
2720 offset: rustpython_host_env::crt_fd::Offset,
2721 count: i64,
2722 #[cfg(target_os = "macos")]
2723 #[pyarg(any, optional, py_default = "()")]
2725 headers: OptionalArg<PyObjectRef>,
2726 #[cfg(target_os = "macos")]
2727 #[pyarg(any, optional, py_default = "()")]
2729 trailers: OptionalArg<PyObjectRef>,
2730 #[cfg(target_os = "macos")]
2731 #[allow(dead_code)]
2732 #[pyarg(any, default)]
2733 flags: i32,
2735 }
2736
2737 #[cfg(target_os = "linux")]
2738 #[pyfunction]
2739 fn sendfile(args: SendFileArgs<'_>, vm: &VirtualMachine) -> PyResult {
2740 let mut file_offset = args.offset;
2741
2742 let res = rustpython_host_env::posix::sendfile(
2743 args.out_fd,
2744 args.in_fd,
2745 &mut file_offset,
2746 args.count as usize,
2747 )
2748 .map_err(|err| err.into_pyexception(vm))?;
2749 Ok(vm.ctx.new_int(res as u64).into())
2750 }
2751
2752 #[cfg(target_os = "macos")]
2753 fn _extract_vec_bytes(
2754 x: OptionalArg,
2755 vm: &VirtualMachine,
2756 ) -> PyResult<Option<Vec<crate::function::ArgBytesLike>>> {
2757 x.into_option()
2758 .map(|x| {
2759 let v: Vec<crate::function::ArgBytesLike> = x.try_to_value(vm)?;
2760 Ok(if v.is_empty() { None } else { Some(v) })
2761 })
2762 .transpose()
2763 .map(Option::flatten)
2764 }
2765
2766 #[cfg(target_os = "macos")]
2767 #[pyfunction]
2768 fn sendfile(args: SendFileArgs<'_>, vm: &VirtualMachine) -> PyResult {
2769 let headers = _extract_vec_bytes(args.headers, vm)?;
2770 let count = headers
2771 .as_ref()
2772 .map_or(0, |v| v.iter().map(|s| s.len()).sum()) as i64
2773 + args.count;
2774
2775 let headers = headers
2776 .as_ref()
2777 .map(|v| v.iter().map(|b| b.borrow_buf()).collect::<Vec<_>>());
2778 let headers = headers
2779 .as_ref()
2780 .map(|v| v.iter().map(|borrowed| &**borrowed).collect::<Vec<_>>());
2781 let headers = headers.as_deref();
2782
2783 let trailers = _extract_vec_bytes(args.trailers, vm)?;
2784 let trailers = trailers
2785 .as_ref()
2786 .map(|v| v.iter().map(|b| b.borrow_buf()).collect::<Vec<_>>());
2787 let trailers = trailers
2788 .as_ref()
2789 .map(|v| v.iter().map(|borrowed| &**borrowed).collect::<Vec<_>>());
2790 let trailers = trailers.as_deref();
2791
2792 let (res, written) = rustpython_host_env::posix::sendfile(
2793 args.in_fd,
2794 args.out_fd,
2795 args.offset,
2796 count,
2797 headers,
2798 trailers,
2799 );
2800 if let Err(err) = res
2804 && written == 0
2805 {
2806 return Err(err.into_pyexception(vm));
2807 }
2808 Ok(vm.ctx.new_int(written as u64).into())
2809 }
2810
2811 #[cfg(target_os = "linux")]
2812 #[pyfunction]
2813 fn getrandom(size: isize, flags: OptionalArg<u32>, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
2814 let size = usize::try_from(size)
2815 .map_err(|_| vm.new_os_error(format!("Invalid argument for size: {size}")))?;
2816 let mut buf = Vec::with_capacity(size);
2817 unsafe {
2818 let len = rustpython_host_env::posix::getrandom(
2819 buf.as_mut_ptr() as *mut libc::c_void,
2820 size,
2821 flags.unwrap_or(0),
2822 )
2823 .map_err(|_| vm.new_last_os_error())?;
2824 buf.set_len(len);
2825 }
2826 Ok(buf)
2827 }
2828
2829 pub(crate) fn module_exec(
2830 vm: &VirtualMachine,
2831 module: &Py<crate::builtins::PyModule>,
2832 ) -> PyResult<()> {
2833 __module_exec(vm, module);
2834 super::super::os::module_exec(vm, module)?;
2835 Ok(())
2836 }
2837
2838 #[pyfunction]
2839 fn _is_inputhook_installed() -> bool {
2840 false
2842 }
2843}
2844
2845#[cfg(any(
2846 target_os = "linux",
2847 target_os = "netbsd",
2848 target_os = "freebsd",
2849 target_os = "android"
2850))]
2851#[pymodule(sub, name = "posix")]
2852mod posix_sched {
2853 use crate::{
2854 AsObject, Py, PyObject, PyObjectRef, PyResult, VirtualMachine,
2855 builtins::PyTupleRef,
2856 class::PyClassDef,
2857 convert::{IntoPyException, ToPyObject},
2858 function::FuncArgs,
2859 types::PyStructSequence,
2860 };
2861
2862 #[derive(FromArgs)]
2863 struct SchedParamArgs {
2864 #[pyarg(any)]
2865 sched_priority: PyObjectRef,
2866 }
2867
2868 #[pystruct_sequence_data]
2869 struct SchedParamData {
2870 pub sched_priority: PyObjectRef,
2871 }
2872
2873 #[pyattr]
2874 #[pystruct_sequence(name = "sched_param", module = "posix", data = "SchedParamData")]
2875 struct PySchedParam;
2876
2877 #[pyclass(with(PyStructSequence))]
2878 impl PySchedParam {
2879 #[pyslot]
2880 fn slot_new(
2881 cls: crate::builtins::PyTypeRef,
2882 args: FuncArgs,
2883 vm: &VirtualMachine,
2884 ) -> PyResult {
2885 use crate::PyPayload;
2886 let SchedParamArgs { sched_priority } = args.bind_for(vm, Self::NAME)?;
2887 let items = vec![sched_priority];
2888 crate::builtins::PyTuple::new_unchecked(items.into_boxed_slice())
2889 .into_ref_with_type(vm, cls)
2890 .map(Into::into)
2891 }
2892
2893 #[extend_class]
2894 fn extend_pyclass(ctx: &crate::vm::Context, class: &'static Py<crate::builtins::PyType>) {
2895 const SCHED_PARAM_REDUCE: crate::function::PyMethodDef =
2899 crate::function::PyMethodDef::new_const(
2900 "__reduce__",
2901 |zelf: crate::PyRef<crate::builtins::PyTuple>,
2902 vm: &VirtualMachine|
2903 -> PyTupleRef {
2904 vm.new_tuple((zelf.class().to_owned(), (zelf.as_slice()[0].clone(),)))
2905 },
2906 crate::function::PyMethodFlags::METHOD,
2907 crate::function::ItemDoc::NONE,
2908 );
2909 class.set_attr(
2910 ctx.intern_str("__reduce__"),
2911 SCHED_PARAM_REDUCE.to_proper_method(class, ctx),
2912 );
2913 }
2914 }
2915
2916 #[cfg(not(target_env = "musl"))]
2917 pub(super) fn convert_sched_param(
2918 obj: &PyObject,
2919 vm: &VirtualMachine,
2920 ) -> PyResult<libc::sched_param> {
2921 use crate::{
2922 builtins::{PyInt, PyTuple},
2923 class::StaticType,
2924 };
2925 if !obj.fast_isinstance(PySchedParam::static_type()) {
2926 return Err(vm.new_type_error("must have a sched_param object"));
2927 }
2928 let tuple = obj.downcast_ref::<PyTuple>().unwrap();
2929 let priority = tuple.as_slice()[0].clone();
2930 let priority_type = priority.class().name().to_string();
2931 let value = priority.downcast::<PyInt>().map_err(|_| {
2932 vm.new_type_error(format!("an integer is required (got type {priority_type})"))
2933 })?;
2934 let sched_priority = value.try_to_primitive(vm)?;
2935 Ok(libc::sched_param { sched_priority })
2936 }
2937
2938 #[pyfunction]
2939 fn sched_getscheduler(pid: libc::pid_t, vm: &VirtualMachine) -> PyResult<i32> {
2940 rustpython_host_env::posix::sched_getscheduler(pid).map_err(|err| err.into_pyexception(vm))
2941 }
2942
2943 #[cfg(not(target_env = "musl"))]
2944 #[derive(FromArgs)]
2945 struct SchedSetschedulerArgs {
2946 #[pyarg(positional)]
2947 pid: i32,
2948 #[pyarg(positional)]
2949 policy: i32,
2950 #[pyarg(positional)]
2951 sched_param: PyObjectRef,
2952 }
2953
2954 #[cfg(not(target_env = "musl"))]
2955 #[pyfunction]
2956 fn sched_setscheduler(args: SchedSetschedulerArgs, vm: &VirtualMachine) -> PyResult<i32> {
2957 let libc_sched_param = convert_sched_param(&args.sched_param, vm)?;
2958 rustpython_host_env::posix::sched_setscheduler(args.pid, args.policy, &libc_sched_param)
2959 .map_err(|err| err.into_pyexception(vm))
2960 }
2961
2962 #[pyfunction]
2963 fn sched_getparam(pid: libc::pid_t, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
2964 let param = rustpython_host_env::posix::sched_getparam(pid)
2965 .map_err(|err| err.into_pyexception(vm))?;
2966 Ok(PySchedParam::from_data(
2967 SchedParamData {
2968 sched_priority: param.sched_priority.to_pyobject(vm),
2969 },
2970 vm,
2971 ))
2972 }
2973
2974 #[cfg(not(target_env = "musl"))]
2975 #[derive(FromArgs)]
2976 struct SchedSetParamArgs {
2977 #[pyarg(positional)]
2978 pid: i32,
2979 #[pyarg(positional)]
2980 sched_param: PyObjectRef,
2981 }
2982
2983 #[cfg(not(target_env = "musl"))]
2984 #[pyfunction]
2985 fn sched_setparam(args: SchedSetParamArgs, vm: &VirtualMachine) -> PyResult<i32> {
2986 let libc_sched_param = convert_sched_param(&args.sched_param, vm)?;
2987 rustpython_host_env::posix::sched_setparam(args.pid, &libc_sched_param)
2988 .map_err(|err| err.into_pyexception(vm))
2989 }
2990}