1use crate::{builtins::PyModule, PyRef, VirtualMachine};
2use std::os::unix::io::RawFd;
3
4pub fn raw_set_inheritable(fd: RawFd, inheritable: bool) -> nix::Result<()> {
5 use nix::fcntl;
6 let flags = fcntl::FdFlag::from_bits_truncate(fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFD)?);
7 let mut new_flags = flags;
8 new_flags.set(fcntl::FdFlag::FD_CLOEXEC, !inheritable);
9 if flags != new_flags {
10 fcntl::fcntl(fd, fcntl::FcntlArg::F_SETFD(new_flags))?;
11 }
12 Ok(())
13}
14
15pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
16 let module = module::make_module(vm);
17 super::os::extend_module(vm, &module);
18 module
19}
20
21#[pymodule(name = "posix", with(super::os::_os))]
22pub mod module {
23 use crate::{
24 builtins::{PyDictRef, PyInt, PyListRef, PyStrRef, PyTupleRef, PyTypeRef},
25 convert::{IntoPyException, ToPyObject, TryFromObject},
26 function::{Either, KwArgs, OptionalArg},
27 ospath::{IOErrorBuilder, OsPath, OsPathOrFd},
28 stdlib::os::{
29 errno_err, DirFd, FollowSymlinks, SupportFunc, TargetIsDirectory, _os, fs_metadata,
30 },
31 types::{Constructor, Representable},
32 utils::ToCString,
33 AsObject, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine,
34 };
35 use bitflags::bitflags;
36 use nix::{
37 fcntl,
38 unistd::{self, Gid, Pid, Uid},
39 };
40 use std::{
41 env,
42 ffi::{CStr, CString},
43 fs, io,
44 os::fd::{AsRawFd, BorrowedFd, IntoRawFd, OwnedFd, RawFd},
45 };
46 use strum_macros::{EnumIter, EnumString};
47
48 #[pyattr]
49 use libc::{PRIO_PGRP, PRIO_PROCESS, PRIO_USER};
50
51 #[cfg(any(
52 target_os = "dragonfly",
53 target_os = "freebsd",
54 target_os = "linux",
55 target_os = "macos"
56 ))]
57 #[pyattr]
58 use libc::{SEEK_DATA, SEEK_HOLE};
59
60 #[cfg(not(any(target_os = "redox", target_os = "freebsd")))]
61 #[pyattr]
62 use libc::O_DSYNC;
63 #[pyattr]
64 use libc::{O_CLOEXEC, O_NONBLOCK, WNOHANG};
65 #[cfg(target_os = "macos")]
66 #[pyattr]
67 use libc::{O_EVTONLY, O_FSYNC, O_NOFOLLOW_ANY, O_SYMLINK};
68 #[cfg(not(target_os = "redox"))]
69 #[pyattr]
70 use libc::{O_NDELAY, O_NOCTTY};
71
72 #[pyattr]
73 use libc::{RTLD_GLOBAL, RTLD_LAZY, RTLD_LOCAL, RTLD_NOW};
74
75 #[cfg(target_os = "linux")]
76 #[pyattr]
77 use libc::{GRND_NONBLOCK, GRND_RANDOM};
78
79 #[pyattr]
80 const EX_OK: i8 = exitcode::OK as i8;
81 #[pyattr]
82 const EX_USAGE: i8 = exitcode::USAGE as i8;
83 #[pyattr]
84 const EX_DATAERR: i8 = exitcode::DATAERR as i8;
85 #[pyattr]
86 const EX_NOINPUT: i8 = exitcode::NOINPUT as i8;
87 #[pyattr]
88 const EX_NOUSER: i8 = exitcode::NOUSER as i8;
89 #[pyattr]
90 const EX_NOHOST: i8 = exitcode::NOHOST as i8;
91 #[pyattr]
92 const EX_UNAVAILABLE: i8 = exitcode::UNAVAILABLE as i8;
93 #[pyattr]
94 const EX_SOFTWARE: i8 = exitcode::SOFTWARE as i8;
95 #[pyattr]
96 const EX_OSERR: i8 = exitcode::OSERR as i8;
97 #[pyattr]
98 const EX_OSFILE: i8 = exitcode::OSFILE as i8;
99 #[pyattr]
100 const EX_CANTCREAT: i8 = exitcode::CANTCREAT as i8;
101 #[pyattr]
102 const EX_IOERR: i8 = exitcode::IOERR as i8;
103 #[pyattr]
104 const EX_TEMPFAIL: i8 = exitcode::TEMPFAIL as i8;
105 #[pyattr]
106 const EX_PROTOCOL: i8 = exitcode::PROTOCOL as i8;
107 #[pyattr]
108 const EX_NOPERM: i8 = exitcode::NOPERM as i8;
109 #[pyattr]
110 const EX_CONFIG: i8 = exitcode::CONFIG as i8;
111
112 #[cfg(any(
113 target_os = "macos",
114 target_os = "linux",
115 target_os = "android",
116 target_os = "freebsd",
117 target_os = "dragonfly",
118 target_os = "netbsd",
119 target_os = "macos"
120 ))]
121 #[pyattr]
122 const SCHED_RR: i32 = libc::SCHED_RR;
123 #[cfg(any(
124 target_os = "macos",
125 target_os = "linux",
126 target_os = "android",
127 target_os = "freebsd",
128 target_os = "dragonfly",
129 target_os = "netbsd",
130 target_os = "macos"
131 ))]
132 #[pyattr]
133 const SCHED_FIFO: i32 = libc::SCHED_FIFO;
134 #[cfg(any(
135 target_os = "macos",
136 target_os = "linux",
137 target_os = "freebsd",
138 target_os = "dragonfly",
139 target_os = "netbsd",
140 target_os = "macos"
141 ))]
142 #[pyattr]
143 const SCHED_OTHER: i32 = libc::SCHED_OTHER;
144 #[cfg(any(target_os = "linux", target_os = "android"))]
145 #[pyattr]
146 const SCHED_IDLE: i32 = libc::SCHED_IDLE;
147 #[cfg(any(target_os = "linux", target_os = "android"))]
148 #[pyattr]
149 const SCHED_BATCH: i32 = libc::SCHED_BATCH;
150
151 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
152 #[pyattr]
153 const POSIX_SPAWN_OPEN: i32 = PosixSpawnFileActionIdentifier::Open as i32;
154 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
155 #[pyattr]
156 const POSIX_SPAWN_CLOSE: i32 = PosixSpawnFileActionIdentifier::Close as i32;
157 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
158 #[pyattr]
159 const POSIX_SPAWN_DUP2: i32 = PosixSpawnFileActionIdentifier::Dup2 as i32;
160
161 #[cfg(target_os = "macos")]
162 #[pyattr]
163 const _COPYFILE_DATA: u32 = 1 << 3;
164
165 impl TryFromObject for BorrowedFd<'_> {
166 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
167 let fd = i32::try_from_object(vm, obj)?;
168 if fd == -1 {
169 return Err(io::Error::from_raw_os_error(libc::EBADF).into_pyexception(vm));
170 }
171 Ok(unsafe { BorrowedFd::borrow_raw(fd) })
174 }
175 }
176
177 impl ToPyObject for OwnedFd {
178 fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
179 self.into_raw_fd().to_pyobject(vm)
180 }
181 }
182
183 bitflags! {
185 #[derive(Copy, Clone, Debug, PartialEq)]
186 pub struct AccessFlags: u8 {
187 const F_OK = _os::F_OK;
188 const R_OK = _os::R_OK;
189 const W_OK = _os::W_OK;
190 const X_OK = _os::X_OK;
191 }
192 }
193
194 struct Permissions {
195 is_readable: bool,
196 is_writable: bool,
197 is_executable: bool,
198 }
199
200 fn get_permissions(mode: u32) -> Permissions {
201 Permissions {
202 is_readable: mode & 4 != 0,
203 is_writable: mode & 2 != 0,
204 is_executable: mode & 1 != 0,
205 }
206 }
207
208 fn get_right_permission(
209 mode: u32,
210 file_owner: Uid,
211 file_group: Gid,
212 ) -> nix::Result<Permissions> {
213 let owner_mode = (mode & 0o700) >> 6;
214 let owner_permissions = get_permissions(owner_mode);
215
216 let group_mode = (mode & 0o070) >> 3;
217 let group_permissions = get_permissions(group_mode);
218
219 let others_mode = mode & 0o007;
220 let others_permissions = get_permissions(others_mode);
221
222 let user_id = nix::unistd::getuid();
223 let groups_ids = getgroups_impl()?;
224
225 if file_owner == user_id {
226 Ok(owner_permissions)
227 } else if groups_ids.contains(&file_group) {
228 Ok(group_permissions)
229 } else {
230 Ok(others_permissions)
231 }
232 }
233
234 #[cfg(any(target_os = "macos", target_os = "ios"))]
235 fn getgroups_impl() -> nix::Result<Vec<Gid>> {
236 use libc::{c_int, gid_t};
237 use nix::errno::Errno;
238 use std::ptr;
239 let ret = unsafe { libc::getgroups(0, ptr::null_mut()) };
240 let mut groups = Vec::<Gid>::with_capacity(Errno::result(ret)? as usize);
241 let ret = unsafe {
242 libc::getgroups(
243 groups.capacity() as c_int,
244 groups.as_mut_ptr() as *mut gid_t,
245 )
246 };
247
248 Errno::result(ret).map(|s| {
249 unsafe { groups.set_len(s as usize) };
250 groups
251 })
252 }
253
254 #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "redox")))]
255 use nix::unistd::getgroups as getgroups_impl;
256
257 #[cfg(target_os = "redox")]
258 fn getgroups_impl() -> nix::Result<Vec<Gid>> {
259 Err(nix::Error::EOPNOTSUPP)
260 }
261
262 #[pyfunction]
263 fn getgroups(vm: &VirtualMachine) -> PyResult<Vec<PyObjectRef>> {
264 let group_ids = getgroups_impl().map_err(|e| e.into_pyexception(vm))?;
265 Ok(group_ids
266 .into_iter()
267 .map(|gid| vm.ctx.new_int(gid.as_raw()).into())
268 .collect())
269 }
270
271 #[pyfunction]
272 pub(super) fn access(path: OsPath, mode: u8, vm: &VirtualMachine) -> PyResult<bool> {
273 use std::os::unix::fs::MetadataExt;
274
275 let flags = AccessFlags::from_bits(mode).ok_or_else(|| {
276 vm.new_value_error(
277 "One of the flags is wrong, there are only 4 possibilities F_OK, R_OK, W_OK and X_OK"
278 .to_owned(),
279 )
280 })?;
281
282 let metadata = fs::metadata(&path.path);
283
284 if flags == AccessFlags::F_OK {
286 return Ok(metadata.is_ok());
287 }
288
289 let metadata =
290 metadata.map_err(|err| IOErrorBuilder::with_filename(&err, path.clone(), vm))?;
291
292 let user_id = metadata.uid();
293 let group_id = metadata.gid();
294 let mode = metadata.mode();
295
296 let perm = get_right_permission(mode, Uid::from_raw(user_id), Gid::from_raw(group_id))
297 .map_err(|err| err.into_pyexception(vm))?;
298
299 let r_ok = !flags.contains(AccessFlags::R_OK) || perm.is_readable;
300 let w_ok = !flags.contains(AccessFlags::W_OK) || perm.is_writable;
301 let x_ok = !flags.contains(AccessFlags::X_OK) || perm.is_executable;
302
303 Ok(r_ok && w_ok && x_ok)
304 }
305
306 #[pyattr]
307 fn environ(vm: &VirtualMachine) -> PyDictRef {
308 use rustpython_common::os::ffi::OsStringExt;
309
310 let environ = vm.ctx.new_dict();
311 for (key, value) in env::vars_os() {
312 let key: PyObjectRef = vm.ctx.new_bytes(key.into_vec()).into();
313 let value: PyObjectRef = vm.ctx.new_bytes(value.into_vec()).into();
314 environ.set_item(&*key, value, vm).unwrap();
315 }
316
317 environ
318 }
319
320 #[derive(FromArgs)]
321 pub(super) struct SymlinkArgs {
322 src: OsPath,
323 dst: OsPath,
324 #[pyarg(flatten)]
325 _target_is_directory: TargetIsDirectory,
326 #[pyarg(flatten)]
327 dir_fd: DirFd<{ _os::SYMLINK_DIR_FD as usize }>,
328 }
329
330 #[pyfunction]
331 pub(super) fn symlink(args: SymlinkArgs, vm: &VirtualMachine) -> PyResult<()> {
332 let src = args.src.into_cstring(vm)?;
333 let dst = args.dst.into_cstring(vm)?;
334 #[cfg(not(target_os = "redox"))]
335 {
336 nix::unistd::symlinkat(&*src, args.dir_fd.get_opt(), &*dst)
337 .map_err(|err| err.into_pyexception(vm))
338 }
339 #[cfg(target_os = "redox")]
340 {
341 let [] = args.dir_fd.0;
342 let res = unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) };
343 if res < 0 {
344 Err(errno_err(vm))
345 } else {
346 Ok(())
347 }
348 }
349 }
350
351 #[cfg(not(target_os = "redox"))]
352 #[pyfunction]
353 fn fchdir(fd: RawFd, vm: &VirtualMachine) -> PyResult<()> {
354 nix::unistd::fchdir(fd).map_err(|err| err.into_pyexception(vm))
355 }
356
357 #[cfg(not(target_os = "redox"))]
358 #[pyfunction]
359 fn chroot(path: OsPath, vm: &VirtualMachine) -> PyResult<()> {
360 use crate::ospath::IOErrorBuilder;
361
362 nix::unistd::chroot(&*path.path).map_err(|err| {
363 let err = io::Error::from_raw_os_error(err as i32);
365 IOErrorBuilder::with_filename(&err, path, vm)
366 })
367 }
368
369 #[cfg(not(target_os = "redox"))]
371 #[pyfunction]
372 fn chown(
373 path: OsPathOrFd,
374 uid: isize,
375 gid: isize,
376 dir_fd: DirFd<1>,
377 follow_symlinks: FollowSymlinks,
378 vm: &VirtualMachine,
379 ) -> PyResult<()> {
380 let uid = if uid >= 0 {
381 Some(nix::unistd::Uid::from_raw(uid as u32))
382 } else if uid == -1 {
383 None
384 } else {
385 return Err(vm.new_os_error(String::from("Specified uid is not valid.")));
386 };
387
388 let gid = if gid >= 0 {
389 Some(nix::unistd::Gid::from_raw(gid as u32))
390 } else if gid == -1 {
391 None
392 } else {
393 return Err(vm.new_os_error(String::from("Specified gid is not valid.")));
394 };
395
396 let flag = if follow_symlinks.0 {
397 nix::unistd::FchownatFlags::FollowSymlink
398 } else {
399 nix::unistd::FchownatFlags::NoFollowSymlink
400 };
401
402 let dir_fd = dir_fd.get_opt();
403 match path {
404 OsPathOrFd::Path(ref p) => {
405 nix::unistd::fchownat(dir_fd, p.path.as_os_str(), uid, gid, flag)
406 }
407 OsPathOrFd::Fd(fd) => nix::unistd::fchown(fd, uid, gid),
408 }
409 .map_err(|err| {
410 let err = io::Error::from_raw_os_error(err as i32);
412 IOErrorBuilder::with_filename(&err, path, vm)
413 })
414 }
415
416 #[cfg(not(target_os = "redox"))]
417 #[pyfunction]
418 fn lchown(path: OsPath, uid: isize, gid: isize, vm: &VirtualMachine) -> PyResult<()> {
419 chown(
420 OsPathOrFd::Path(path),
421 uid,
422 gid,
423 DirFd::default(),
424 FollowSymlinks(false),
425 vm,
426 )
427 }
428
429 #[cfg(not(target_os = "redox"))]
430 #[pyfunction]
431 fn fchown(fd: i32, uid: isize, gid: isize, vm: &VirtualMachine) -> PyResult<()> {
432 chown(
433 OsPathOrFd::Fd(fd),
434 uid,
435 gid,
436 DirFd::default(),
437 FollowSymlinks(true),
438 vm,
439 )
440 }
441
442 #[derive(FromArgs)]
443 struct RegisterAtForkArgs {
444 #[pyarg(named, optional)]
445 before: OptionalArg<PyObjectRef>,
446 #[pyarg(named, optional)]
447 after_in_parent: OptionalArg<PyObjectRef>,
448 #[pyarg(named, optional)]
449 after_in_child: OptionalArg<PyObjectRef>,
450 }
451
452 impl RegisterAtForkArgs {
453 fn into_validated(
454 self,
455 vm: &VirtualMachine,
456 ) -> PyResult<(
457 Option<PyObjectRef>,
458 Option<PyObjectRef>,
459 Option<PyObjectRef>,
460 )> {
461 fn into_option(
462 arg: OptionalArg<PyObjectRef>,
463 vm: &VirtualMachine,
464 ) -> PyResult<Option<PyObjectRef>> {
465 match arg {
466 OptionalArg::Present(obj) => {
467 if !obj.is_callable() {
468 return Err(vm.new_type_error("Args must be callable".to_owned()));
469 }
470 Ok(Some(obj))
471 }
472 OptionalArg::Missing => Ok(None),
473 }
474 }
475 let before = into_option(self.before, vm)?;
476 let after_in_parent = into_option(self.after_in_parent, vm)?;
477 let after_in_child = into_option(self.after_in_child, vm)?;
478 if before.is_none() && after_in_parent.is_none() && after_in_child.is_none() {
479 return Err(vm.new_type_error("At least one arg must be present".to_owned()));
480 }
481 Ok((before, after_in_parent, after_in_child))
482 }
483 }
484
485 #[pyfunction]
486 fn register_at_fork(
487 args: RegisterAtForkArgs,
488 _ignored: KwArgs,
489 vm: &VirtualMachine,
490 ) -> PyResult<()> {
491 let (before, after_in_parent, after_in_child) = args.into_validated(vm)?;
492
493 if let Some(before) = before {
494 vm.state.before_forkers.lock().push(before);
495 }
496 if let Some(after_in_parent) = after_in_parent {
497 vm.state.after_forkers_parent.lock().push(after_in_parent);
498 }
499 if let Some(after_in_child) = after_in_child {
500 vm.state.after_forkers_child.lock().push(after_in_child);
501 }
502 Ok(())
503 }
504
505 fn run_at_forkers(mut funcs: Vec<PyObjectRef>, reversed: bool, vm: &VirtualMachine) {
506 if !funcs.is_empty() {
507 if reversed {
508 funcs.reverse();
509 }
510 for func in funcs.into_iter() {
511 if let Err(e) = func.call((), vm) {
512 let exit = e.fast_isinstance(vm.ctx.exceptions.system_exit);
513 vm.run_unraisable(e, Some("Exception ignored in".to_owned()), func);
514 if exit {
515 }
517 }
518 }
519 }
520 }
521
522 fn py_os_before_fork(vm: &VirtualMachine) {
523 let before_forkers: Vec<PyObjectRef> = vm.state.before_forkers.lock().clone();
524 run_at_forkers(before_forkers, true, vm);
528 }
529
530 fn py_os_after_fork_child(vm: &VirtualMachine) {
531 let after_forkers_child: Vec<PyObjectRef> = vm.state.after_forkers_child.lock().clone();
532 run_at_forkers(after_forkers_child, false, vm);
533 }
534
535 fn py_os_after_fork_parent(vm: &VirtualMachine) {
536 let after_forkers_parent: Vec<PyObjectRef> = vm.state.after_forkers_parent.lock().clone();
537 run_at_forkers(after_forkers_parent, false, vm);
538 }
539
540 #[pyfunction]
541 fn fork(vm: &VirtualMachine) -> i32 {
542 let pid: i32;
543 py_os_before_fork(vm);
544 unsafe {
545 pid = libc::fork();
546 }
547 if pid == 0 {
548 py_os_after_fork_child(vm);
549 } else {
550 py_os_after_fork_parent(vm);
551 }
552 pid
553 }
554
555 #[cfg(not(target_os = "redox"))]
556 const MKNOD_DIR_FD: bool = cfg!(not(target_vendor = "apple"));
557
558 #[cfg(not(target_os = "redox"))]
559 #[derive(FromArgs)]
560 struct MknodArgs {
561 #[pyarg(any)]
562 path: OsPath,
563 #[pyarg(any)]
564 mode: libc::mode_t,
565 #[pyarg(any)]
566 device: libc::dev_t,
567 #[pyarg(flatten)]
568 dir_fd: DirFd<{ MKNOD_DIR_FD as usize }>,
569 }
570
571 #[cfg(not(target_os = "redox"))]
572 impl MknodArgs {
573 fn _mknod(self, vm: &VirtualMachine) -> PyResult<i32> {
574 Ok(unsafe {
575 libc::mknod(
576 self.path.clone().into_cstring(vm)?.as_ptr(),
577 self.mode,
578 self.device,
579 )
580 })
581 }
582 #[cfg(not(target_vendor = "apple"))]
583 fn mknod(self, vm: &VirtualMachine) -> PyResult<()> {
584 let ret = match self.dir_fd.get_opt() {
585 None => self._mknod(vm)?,
586 Some(non_default_fd) => unsafe {
587 libc::mknodat(
588 non_default_fd,
589 self.path.clone().into_cstring(vm)?.as_ptr(),
590 self.mode,
591 self.device,
592 )
593 },
594 };
595 if ret != 0 {
596 Err(errno_err(vm))
597 } else {
598 Ok(())
599 }
600 }
601 #[cfg(target_vendor = "apple")]
602 fn mknod(self, vm: &VirtualMachine) -> PyResult<()> {
603 let [] = self.dir_fd.0;
604 let ret = self._mknod(vm)?;
605 if ret != 0 {
606 Err(errno_err(vm))
607 } else {
608 Ok(())
609 }
610 }
611 }
612
613 #[cfg(not(target_os = "redox"))]
614 #[pyfunction]
615 fn mknod(args: MknodArgs, vm: &VirtualMachine) -> PyResult<()> {
616 args.mknod(vm)
617 }
618
619 #[cfg(not(target_os = "redox"))]
620 #[pyfunction]
621 fn nice(increment: i32, vm: &VirtualMachine) -> PyResult<i32> {
622 use nix::errno::{errno, Errno};
623 Errno::clear();
624 let res = unsafe { libc::nice(increment) };
625 if res == -1 && errno() != 0 {
626 Err(errno_err(vm))
627 } else {
628 Ok(res)
629 }
630 }
631
632 #[cfg(not(target_os = "redox"))]
633 #[pyfunction]
634 fn sched_get_priority_max(policy: i32, vm: &VirtualMachine) -> PyResult<i32> {
635 let max = unsafe { libc::sched_get_priority_max(policy) };
636 if max == -1 {
637 Err(errno_err(vm))
638 } else {
639 Ok(max)
640 }
641 }
642
643 #[cfg(not(target_os = "redox"))]
644 #[pyfunction]
645 fn sched_get_priority_min(policy: i32, vm: &VirtualMachine) -> PyResult<i32> {
646 let min = unsafe { libc::sched_get_priority_min(policy) };
647 if min == -1 {
648 Err(errno_err(vm))
649 } else {
650 Ok(min)
651 }
652 }
653
654 #[pyfunction]
655 fn sched_yield(vm: &VirtualMachine) -> PyResult<()> {
656 nix::sched::sched_yield().map_err(|e| e.into_pyexception(vm))
657 }
658
659 #[pyattr]
660 #[pyclass(name = "sched_param")]
661 #[derive(Debug, PyPayload)]
662 struct SchedParam {
663 sched_priority: PyObjectRef,
664 }
665
666 impl TryFromObject for SchedParam {
667 fn try_from_object(_vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
668 Ok(SchedParam {
669 sched_priority: obj,
670 })
671 }
672 }
673
674 #[pyclass(with(Constructor, Representable))]
675 impl SchedParam {
676 #[pygetset]
677 fn sched_priority(&self, vm: &VirtualMachine) -> PyObjectRef {
678 self.sched_priority.clone().to_pyobject(vm)
679 }
680
681 #[cfg(any(
682 target_os = "linux",
683 target_os = "netbsd",
684 target_os = "freebsd",
685 target_os = "android"
686 ))]
687 #[cfg(not(target_env = "musl"))]
688 fn try_to_libc(&self, vm: &VirtualMachine) -> PyResult<libc::sched_param> {
689 use crate::AsObject;
690 let priority_class = self.sched_priority.class();
691 let priority_type = priority_class.name();
692 let priority = self.sched_priority.clone();
693 let value = priority.downcast::<PyInt>().map_err(|_| {
694 vm.new_type_error(format!("an integer is required (got type {priority_type})"))
695 })?;
696 let sched_priority = value.try_to_primitive(vm)?;
697 Ok(libc::sched_param { sched_priority })
698 }
699 }
700
701 #[derive(FromArgs)]
702 pub struct SchedParamArg {
703 sched_priority: PyObjectRef,
704 }
705 impl Constructor for SchedParam {
706 type Args = SchedParamArg;
707 fn py_new(cls: PyTypeRef, arg: Self::Args, vm: &VirtualMachine) -> PyResult {
708 SchedParam {
709 sched_priority: arg.sched_priority,
710 }
711 .into_ref_with_type(vm, cls)
712 .map(Into::into)
713 }
714 }
715
716 impl Representable for SchedParam {
717 #[inline]
718 fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
719 let sched_priority_repr = zelf.sched_priority.repr(vm)?;
720 Ok(format!(
721 "posix.sched_param(sched_priority = {})",
722 sched_priority_repr.as_str()
723 ))
724 }
725 }
726
727 #[cfg(any(
728 target_os = "linux",
729 target_os = "netbsd",
730 target_os = "freebsd",
731 target_os = "android"
732 ))]
733 #[pyfunction]
734 fn sched_getscheduler(pid: libc::pid_t, vm: &VirtualMachine) -> PyResult<i32> {
735 let policy = unsafe { libc::sched_getscheduler(pid) };
736 if policy == -1 {
737 Err(errno_err(vm))
738 } else {
739 Ok(policy)
740 }
741 }
742
743 #[cfg(any(
744 target_os = "linux",
745 target_os = "netbsd",
746 target_os = "freebsd",
747 target_os = "android"
748 ))]
749 #[derive(FromArgs)]
750 struct SchedSetschedulerArgs {
751 #[pyarg(positional)]
752 pid: i32,
753 #[pyarg(positional)]
754 policy: i32,
755 #[pyarg(positional)]
756 sched_param_obj: crate::PyRef<SchedParam>,
757 }
758
759 #[cfg(any(
760 target_os = "linux",
761 target_os = "netbsd",
762 target_os = "freebsd",
763 target_os = "android"
764 ))]
765 #[cfg(not(target_env = "musl"))]
766 #[pyfunction]
767 fn sched_setscheduler(args: SchedSetschedulerArgs, vm: &VirtualMachine) -> PyResult<i32> {
768 let libc_sched_param = args.sched_param_obj.try_to_libc(vm)?;
769 let policy = unsafe { libc::sched_setscheduler(args.pid, args.policy, &libc_sched_param) };
770 if policy == -1 {
771 Err(errno_err(vm))
772 } else {
773 Ok(policy)
774 }
775 }
776 #[cfg(any(
777 target_os = "linux",
778 target_os = "netbsd",
779 target_os = "freebsd",
780 target_os = "android"
781 ))]
782 #[pyfunction]
783 fn sched_getparam(pid: libc::pid_t, vm: &VirtualMachine) -> PyResult<SchedParam> {
784 let param = unsafe {
785 let mut param = std::mem::MaybeUninit::uninit();
786 if -1 == libc::sched_getparam(pid, param.as_mut_ptr()) {
787 return Err(errno_err(vm));
788 }
789 param.assume_init()
790 };
791 Ok(SchedParam {
792 sched_priority: param.sched_priority.to_pyobject(vm),
793 })
794 }
795
796 #[cfg(any(
797 target_os = "linux",
798 target_os = "netbsd",
799 target_os = "freebsd",
800 target_os = "android"
801 ))]
802 #[derive(FromArgs)]
803 struct SchedSetParamArgs {
804 #[pyarg(positional)]
805 pid: i32,
806 #[pyarg(positional)]
807 sched_param_obj: crate::PyRef<SchedParam>,
808 }
809
810 #[cfg(any(
811 target_os = "linux",
812 target_os = "netbsd",
813 target_os = "freebsd",
814 target_os = "android"
815 ))]
816 #[cfg(not(target_env = "musl"))]
817 #[pyfunction]
818 fn sched_setparam(args: SchedSetParamArgs, vm: &VirtualMachine) -> PyResult<i32> {
819 let libc_sched_param = args.sched_param_obj.try_to_libc(vm)?;
820 let ret = unsafe { libc::sched_setparam(args.pid, &libc_sched_param) };
821 if ret == -1 {
822 Err(errno_err(vm))
823 } else {
824 Ok(ret)
825 }
826 }
827
828 #[pyfunction]
829 fn get_inheritable(fd: RawFd, vm: &VirtualMachine) -> PyResult<bool> {
830 let flags = fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFD);
831 match flags {
832 Ok(ret) => Ok((ret & libc::FD_CLOEXEC) == 0),
833 Err(err) => Err(err.into_pyexception(vm)),
834 }
835 }
836
837 #[pyfunction]
838 fn set_inheritable(fd: i32, inheritable: bool, vm: &VirtualMachine) -> PyResult<()> {
839 super::raw_set_inheritable(fd, inheritable).map_err(|err| err.into_pyexception(vm))
840 }
841
842 #[pyfunction]
843 fn get_blocking(fd: RawFd, vm: &VirtualMachine) -> PyResult<bool> {
844 let flags = fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFL);
845 match flags {
846 Ok(ret) => Ok((ret & libc::O_NONBLOCK) == 0),
847 Err(err) => Err(err.into_pyexception(vm)),
848 }
849 }
850
851 #[pyfunction]
852 fn set_blocking(fd: RawFd, blocking: bool, vm: &VirtualMachine) -> PyResult<()> {
853 let _set_flag = || {
854 use nix::fcntl::{fcntl, FcntlArg, OFlag};
855
856 let flags = OFlag::from_bits_truncate(fcntl(fd, FcntlArg::F_GETFL)?);
857 let mut new_flags = flags;
858 new_flags.set(OFlag::from_bits_truncate(libc::O_NONBLOCK), !blocking);
859 if flags != new_flags {
860 fcntl(fd, FcntlArg::F_SETFL(new_flags))?;
861 }
862 Ok(())
863 };
864 _set_flag().map_err(|err: nix::Error| err.into_pyexception(vm))
865 }
866
867 #[pyfunction]
868 fn pipe(vm: &VirtualMachine) -> PyResult<(RawFd, RawFd)> {
869 use nix::unistd::close;
870 use nix::unistd::pipe;
871 let (rfd, wfd) = pipe().map_err(|err| err.into_pyexception(vm))?;
872 set_inheritable(rfd, false, vm)
873 .and_then(|_| set_inheritable(wfd, false, vm))
874 .inspect_err(|_| {
875 let _ = close(rfd);
876 let _ = close(wfd);
877 })?;
878 Ok((rfd, wfd))
879 }
880
881 #[cfg(any(
883 target_os = "android",
884 target_os = "dragonfly",
885 target_os = "emscripten",
886 target_os = "freebsd",
887 target_os = "linux",
888 target_os = "netbsd",
889 target_os = "openbsd"
890 ))]
891 #[pyfunction]
892 fn pipe2(flags: libc::c_int, vm: &VirtualMachine) -> PyResult<(RawFd, RawFd)> {
893 let oflags = fcntl::OFlag::from_bits_truncate(flags);
894 nix::unistd::pipe2(oflags).map_err(|err| err.into_pyexception(vm))
895 }
896
897 #[pyfunction]
898 fn system(command: PyStrRef, vm: &VirtualMachine) -> PyResult<i32> {
899 let cstr = command.to_cstring(vm)?;
900 let x = unsafe { libc::system(cstr.as_ptr()) };
901 Ok(x)
902 }
903
904 fn _chmod(
905 path: OsPath,
906 dir_fd: DirFd<0>,
907 mode: u32,
908 follow_symlinks: FollowSymlinks,
909 vm: &VirtualMachine,
910 ) -> PyResult<()> {
911 let [] = dir_fd.0;
912 let err_path = path.clone();
913 let body = move || {
914 use std::os::unix::fs::PermissionsExt;
915 let meta = fs_metadata(&path, follow_symlinks.0)?;
916 let mut permissions = meta.permissions();
917 permissions.set_mode(mode);
918 fs::set_permissions(&path, permissions)
919 };
920 body().map_err(|err| IOErrorBuilder::with_filename(&err, err_path, vm))
921 }
922
923 #[cfg(not(target_os = "redox"))]
924 fn _fchmod(fd: RawFd, mode: u32, vm: &VirtualMachine) -> PyResult<()> {
925 nix::sys::stat::fchmod(
926 fd,
927 nix::sys::stat::Mode::from_bits(mode as libc::mode_t).unwrap(),
928 )
929 .map_err(|err| err.into_pyexception(vm))
930 }
931
932 #[cfg(not(target_os = "redox"))]
933 #[pyfunction]
934 fn chmod(
935 path: OsPathOrFd,
936 dir_fd: DirFd<0>,
937 mode: u32,
938 follow_symlinks: FollowSymlinks,
939 vm: &VirtualMachine,
940 ) -> PyResult<()> {
941 match path {
942 OsPathOrFd::Path(path) => {
943 #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "netbsd",))]
944 if !follow_symlinks.0 && dir_fd == Default::default() {
945 return lchmod(path, mode, vm);
946 }
947 _chmod(path, dir_fd, mode, follow_symlinks, vm)
948 }
949 OsPathOrFd::Fd(fd) => _fchmod(fd, mode, vm),
950 }
951 }
952
953 #[cfg(target_os = "redox")]
954 #[pyfunction]
955 fn chmod(
956 path: OsPath,
957 dir_fd: DirFd<0>,
958 mode: u32,
959 follow_symlinks: FollowSymlinks,
960 vm: &VirtualMachine,
961 ) -> PyResult<()> {
962 _chmod(path, dir_fd, mode, follow_symlinks, vm)
963 }
964
965 #[cfg(not(target_os = "redox"))]
966 #[pyfunction]
967 fn fchmod(fd: RawFd, mode: u32, vm: &VirtualMachine) -> PyResult<()> {
968 _fchmod(fd, mode, vm)
969 }
970
971 #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "netbsd",))]
972 #[pyfunction]
973 fn lchmod(path: OsPath, mode: u32, vm: &VirtualMachine) -> PyResult<()> {
974 extern "C" {
975 fn lchmod(path: *const libc::c_char, mode: libc::mode_t) -> libc::c_int;
976 }
977 let c_path = path.clone().into_cstring(vm)?;
978 if unsafe { lchmod(c_path.as_ptr(), mode as libc::mode_t) } == 0 {
979 Ok(())
980 } else {
981 let err = std::io::Error::last_os_error();
982 Err(IOErrorBuilder::with_filename(&err, path, vm))
983 }
984 }
985
986 #[pyfunction]
987 fn execv(
988 path: OsPath,
989 argv: Either<PyListRef, PyTupleRef>,
990 vm: &VirtualMachine,
991 ) -> PyResult<()> {
992 let path = path.into_cstring(vm)?;
993
994 let argv = vm.extract_elements_with(argv.as_ref(), |obj| {
995 PyStrRef::try_from_object(vm, obj)?.to_cstring(vm)
996 })?;
997 let argv: Vec<&CStr> = argv.iter().map(|entry| entry.as_c_str()).collect();
998
999 let first = argv
1000 .first()
1001 .ok_or_else(|| vm.new_value_error("execv() arg 2 must not be empty".to_owned()))?;
1002 if first.to_bytes().is_empty() {
1003 return Err(
1004 vm.new_value_error("execv() arg 2 first element cannot be empty".to_owned())
1005 );
1006 }
1007
1008 unistd::execv(&path, &argv)
1009 .map(|_ok| ())
1010 .map_err(|err| err.into_pyexception(vm))
1011 }
1012
1013 #[pyfunction]
1014 fn execve(
1015 path: OsPath,
1016 argv: Either<PyListRef, PyTupleRef>,
1017 env: PyDictRef,
1018 vm: &VirtualMachine,
1019 ) -> PyResult<()> {
1020 let path = path.into_cstring(vm)?;
1021
1022 let argv = vm.extract_elements_with(argv.as_ref(), |obj| {
1023 PyStrRef::try_from_object(vm, obj)?.to_cstring(vm)
1024 })?;
1025 let argv: Vec<&CStr> = argv.iter().map(|entry| entry.as_c_str()).collect();
1026
1027 let first = argv
1028 .first()
1029 .ok_or_else(|| vm.new_value_error("execve() arg 2 must not be empty".to_owned()))?;
1030
1031 if first.to_bytes().is_empty() {
1032 return Err(
1033 vm.new_value_error("execve() arg 2 first element cannot be empty".to_owned())
1034 );
1035 }
1036
1037 let env = env
1038 .into_iter()
1039 .map(|(k, v)| -> PyResult<_> {
1040 let (key, value) = (
1041 OsPath::try_from_object(vm, k)?.into_bytes(),
1042 OsPath::try_from_object(vm, v)?.into_bytes(),
1043 );
1044
1045 if memchr::memchr(b'=', &key).is_some() {
1046 return Err(vm.new_value_error("illegal environment variable name".to_owned()));
1047 }
1048
1049 let mut entry = key;
1050 entry.push(b'=');
1051 entry.extend_from_slice(&value);
1052
1053 CString::new(entry).map_err(|err| err.into_pyexception(vm))
1054 })
1055 .collect::<Result<Vec<_>, _>>()?;
1056
1057 let env: Vec<&CStr> = env.iter().map(|entry| entry.as_c_str()).collect();
1058
1059 unistd::execve(&path, &argv, &env).map_err(|err| err.into_pyexception(vm))?;
1060 Ok(())
1061 }
1062
1063 #[pyfunction]
1064 fn getppid(vm: &VirtualMachine) -> PyObjectRef {
1065 let ppid = unistd::getppid().as_raw();
1066 vm.ctx.new_int(ppid).into()
1067 }
1068
1069 #[pyfunction]
1070 fn getgid(vm: &VirtualMachine) -> PyObjectRef {
1071 let gid = unistd::getgid().as_raw();
1072 vm.ctx.new_int(gid).into()
1073 }
1074
1075 #[pyfunction]
1076 fn getegid(vm: &VirtualMachine) -> PyObjectRef {
1077 let egid = unistd::getegid().as_raw();
1078 vm.ctx.new_int(egid).into()
1079 }
1080
1081 #[pyfunction]
1082 fn getpgid(pid: u32, vm: &VirtualMachine) -> PyResult {
1083 let pgid =
1084 unistd::getpgid(Some(Pid::from_raw(pid as i32))).map_err(|e| e.into_pyexception(vm))?;
1085 Ok(vm.new_pyobj(pgid.as_raw()))
1086 }
1087
1088 #[pyfunction]
1089 fn getpgrp(vm: &VirtualMachine) -> PyObjectRef {
1090 vm.ctx.new_int(unistd::getpgrp().as_raw()).into()
1091 }
1092
1093 #[cfg(not(target_os = "redox"))]
1094 #[pyfunction]
1095 fn getsid(pid: u32, vm: &VirtualMachine) -> PyResult {
1096 let sid =
1097 unistd::getsid(Some(Pid::from_raw(pid as i32))).map_err(|e| e.into_pyexception(vm))?;
1098 Ok(vm.new_pyobj(sid.as_raw()))
1099 }
1100
1101 #[pyfunction]
1102 fn getuid(vm: &VirtualMachine) -> PyObjectRef {
1103 let uid = unistd::getuid().as_raw();
1104 vm.ctx.new_int(uid).into()
1105 }
1106
1107 #[pyfunction]
1108 fn geteuid(vm: &VirtualMachine) -> PyObjectRef {
1109 let euid = unistd::geteuid().as_raw();
1110 vm.ctx.new_int(euid).into()
1111 }
1112
1113 #[pyfunction]
1114 fn setgid(gid: Option<Gid>, vm: &VirtualMachine) -> PyResult<()> {
1115 let gid =
1116 gid.ok_or_else(|| vm.new_errno_error(1, "Operation not permitted".to_string()))?;
1117 unistd::setgid(gid).map_err(|err| err.into_pyexception(vm))
1118 }
1119
1120 #[cfg(not(target_os = "redox"))]
1121 #[pyfunction]
1122 fn setegid(egid: Option<Gid>, vm: &VirtualMachine) -> PyResult<()> {
1123 let egid =
1124 egid.ok_or_else(|| vm.new_errno_error(1, "Operation not permitted".to_string()))?;
1125 unistd::setegid(egid).map_err(|err| err.into_pyexception(vm))
1126 }
1127
1128 #[pyfunction]
1129 fn setpgid(pid: u32, pgid: u32, vm: &VirtualMachine) -> PyResult<()> {
1130 unistd::setpgid(Pid::from_raw(pid as i32), Pid::from_raw(pgid as i32))
1131 .map_err(|err| err.into_pyexception(vm))
1132 }
1133
1134 #[cfg(not(target_os = "redox"))]
1135 #[pyfunction]
1136 fn setsid(vm: &VirtualMachine) -> PyResult<()> {
1137 unistd::setsid()
1138 .map(|_ok| ())
1139 .map_err(|err| err.into_pyexception(vm))
1140 }
1141
1142 fn try_from_id(vm: &VirtualMachine, obj: PyObjectRef, typ_name: &str) -> PyResult<Option<u32>> {
1143 use std::cmp::Ordering;
1144 let i = obj
1145 .try_to_ref::<PyInt>(vm)
1146 .map_err(|_| {
1147 vm.new_type_error(format!(
1148 "an integer is required (got type {})",
1149 obj.class().name()
1150 ))
1151 })?
1152 .try_to_primitive::<i64>(vm)?;
1153
1154 match i.cmp(&-1) {
1155 Ordering::Greater => Ok(Some(i.try_into().map_err(|_| {
1156 vm.new_overflow_error(format!("{typ_name} is larger than maximum"))
1157 })?)),
1158 Ordering::Less => {
1159 Err(vm.new_overflow_error(format!("{typ_name} is less than minimum")))
1160 }
1161 Ordering::Equal => Ok(None), }
1163 }
1164
1165 impl TryFromObject for Option<Uid> {
1166 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
1167 Ok(try_from_id(vm, obj, "uid")?.map(Uid::from_raw))
1168 }
1169 }
1170
1171 impl TryFromObject for Option<Gid> {
1172 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
1173 Ok(try_from_id(vm, obj, "gid")?.map(Gid::from_raw))
1174 }
1175 }
1176
1177 #[pyfunction]
1178 fn setuid(uid: Option<Uid>, vm: &VirtualMachine) -> PyResult<()> {
1179 let uid =
1180 uid.ok_or_else(|| vm.new_errno_error(1, "Operation not permitted".to_string()))?;
1181 unistd::setuid(uid).map_err(|err| err.into_pyexception(vm))
1182 }
1183
1184 #[cfg(not(target_os = "redox"))]
1185 #[pyfunction]
1186 fn seteuid(euid: Option<Uid>, vm: &VirtualMachine) -> PyResult<()> {
1187 let euid =
1188 euid.ok_or_else(|| vm.new_errno_error(1, "Operation not permitted".to_string()))?;
1189 unistd::seteuid(euid).map_err(|err| err.into_pyexception(vm))
1190 }
1191
1192 #[cfg(not(target_os = "redox"))]
1193 #[pyfunction]
1194 fn setreuid(ruid: Option<Uid>, euid: Option<Uid>, vm: &VirtualMachine) -> PyResult<()> {
1195 if let Some(ruid) = ruid {
1196 unistd::setuid(ruid).map_err(|err| err.into_pyexception(vm))?;
1197 }
1198 if let Some(euid) = euid {
1199 unistd::seteuid(euid).map_err(|err| err.into_pyexception(vm))?;
1200 }
1201 Ok(())
1202 }
1203
1204 #[cfg(any(
1206 target_os = "android",
1207 target_os = "freebsd",
1208 target_os = "linux",
1209 target_os = "openbsd"
1210 ))]
1211 #[pyfunction]
1212 fn setresuid(
1213 ruid: Option<Uid>,
1214 euid: Option<Uid>,
1215 suid: Option<Uid>,
1216 vm: &VirtualMachine,
1217 ) -> PyResult<()> {
1218 let unwrap_or_unchanged =
1219 |u: Option<Uid>| u.unwrap_or_else(|| Uid::from_raw(libc::uid_t::MAX));
1220 unistd::setresuid(
1221 unwrap_or_unchanged(ruid),
1222 unwrap_or_unchanged(euid),
1223 unwrap_or_unchanged(suid),
1224 )
1225 .map_err(|err| err.into_pyexception(vm))
1226 }
1227
1228 #[cfg(not(target_os = "redox"))]
1229 #[pyfunction]
1230 fn openpty(vm: &VirtualMachine) -> PyResult<(OwnedFd, OwnedFd)> {
1231 let r = nix::pty::openpty(None, None).map_err(|err| err.into_pyexception(vm))?;
1232 for fd in [&r.master, &r.slave] {
1233 super::raw_set_inheritable(fd.as_raw_fd(), false)
1234 .map_err(|e| e.into_pyexception(vm))?;
1235 }
1236 Ok((r.master, r.slave))
1237 }
1238
1239 #[pyfunction]
1240 fn ttyname(fd: i32, vm: &VirtualMachine) -> PyResult {
1241 let name = unistd::ttyname(fd).map_err(|e| e.into_pyexception(vm))?;
1242 let name = name.into_os_string().into_string().unwrap();
1243 Ok(vm.ctx.new_str(name).into())
1244 }
1245
1246 #[pyfunction]
1247 fn umask(mask: libc::mode_t) -> libc::mode_t {
1248 unsafe { libc::umask(mask) }
1249 }
1250
1251 #[pyfunction]
1252 fn uname(vm: &VirtualMachine) -> PyResult<_os::UnameResult> {
1253 let info = uname::uname().map_err(|err| err.into_pyexception(vm))?;
1254 Ok(_os::UnameResult {
1255 sysname: info.sysname,
1256 nodename: info.nodename,
1257 release: info.release,
1258 version: info.version,
1259 machine: info.machine,
1260 })
1261 }
1262
1263 #[pyfunction]
1264 fn sync() {
1265 #[cfg(not(any(target_os = "redox", target_os = "android")))]
1266 unsafe {
1267 libc::sync();
1268 }
1269 }
1270
1271 #[cfg(any(target_os = "android", target_os = "linux", target_os = "openbsd"))]
1273 #[pyfunction]
1274 fn getresuid(vm: &VirtualMachine) -> PyResult<(u32, u32, u32)> {
1275 let ret = unistd::getresuid().map_err(|e| e.into_pyexception(vm))?;
1276 Ok((
1277 ret.real.as_raw(),
1278 ret.effective.as_raw(),
1279 ret.saved.as_raw(),
1280 ))
1281 }
1282
1283 #[cfg(any(target_os = "android", target_os = "linux", target_os = "openbsd"))]
1285 #[pyfunction]
1286 fn getresgid(vm: &VirtualMachine) -> PyResult<(u32, u32, u32)> {
1287 let ret = unistd::getresgid().map_err(|e| e.into_pyexception(vm))?;
1288 Ok((
1289 ret.real.as_raw(),
1290 ret.effective.as_raw(),
1291 ret.saved.as_raw(),
1292 ))
1293 }
1294
1295 #[cfg(any(
1297 target_os = "android",
1298 target_os = "freebsd",
1299 target_os = "linux",
1300 target_os = "openbsd"
1301 ))]
1302 #[pyfunction]
1303 fn setresgid(
1304 rgid: Option<Gid>,
1305 egid: Option<Gid>,
1306 sgid: Option<Gid>,
1307 vm: &VirtualMachine,
1308 ) -> PyResult<()> {
1309 let unwrap_or_unchanged =
1310 |u: Option<Gid>| u.unwrap_or_else(|| Gid::from_raw(libc::gid_t::MAX));
1311 unistd::setresgid(
1312 unwrap_or_unchanged(rgid),
1313 unwrap_or_unchanged(egid),
1314 unwrap_or_unchanged(sgid),
1315 )
1316 .map_err(|err| err.into_pyexception(vm))
1317 }
1318
1319 #[cfg(not(target_os = "redox"))]
1320 #[pyfunction]
1321 fn setregid(rgid: Option<Gid>, egid: Option<Gid>, vm: &VirtualMachine) -> PyResult<()> {
1322 if let Some(rgid) = rgid {
1323 unistd::setgid(rgid).map_err(|err| err.into_pyexception(vm))?;
1324 }
1325 if let Some(egid) = egid {
1326 unistd::setegid(egid).map_err(|err| err.into_pyexception(vm))?;
1327 }
1328 Ok(())
1329 }
1330
1331 #[cfg(any(
1333 target_os = "android",
1334 target_os = "freebsd",
1335 target_os = "linux",
1336 target_os = "openbsd"
1337 ))]
1338 #[pyfunction]
1339 fn initgroups(user_name: PyStrRef, gid: Option<Gid>, vm: &VirtualMachine) -> PyResult<()> {
1340 let user = user_name.to_cstring(vm)?;
1341 let gid =
1342 gid.ok_or_else(|| vm.new_errno_error(1, "Operation not permitted".to_string()))?;
1343 unistd::initgroups(&user, gid).map_err(|err| err.into_pyexception(vm))
1344 }
1345
1346 #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
1348 #[pyfunction]
1349 fn setgroups(
1350 group_ids: crate::function::ArgIterable<Option<Gid>>,
1351 vm: &VirtualMachine,
1352 ) -> PyResult<()> {
1353 let gids = group_ids
1354 .iter(vm)?
1355 .collect::<Result<Option<Vec<_>>, _>>()?
1356 .ok_or_else(|| vm.new_errno_error(1, "Operation not permitted".to_string()))?;
1357 let ret = unistd::setgroups(&gids);
1358 ret.map_err(|err| err.into_pyexception(vm))
1359 }
1360
1361 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1362 fn envp_from_dict(
1363 env: crate::function::ArgMapping,
1364 vm: &VirtualMachine,
1365 ) -> PyResult<Vec<CString>> {
1366 let keys = env.mapping().keys(vm)?;
1367 let values = env.mapping().values(vm)?;
1368
1369 let keys = PyListRef::try_from_object(vm, keys)
1370 .map_err(|_| vm.new_type_error("env.keys() is not a list".to_owned()))?
1371 .borrow_vec()
1372 .to_vec();
1373 let values = PyListRef::try_from_object(vm, values)
1374 .map_err(|_| vm.new_type_error("env.values() is not a list".to_owned()))?
1375 .borrow_vec()
1376 .to_vec();
1377
1378 keys.into_iter()
1379 .zip(values)
1380 .map(|(k, v)| {
1381 let k = OsPath::try_from_object(vm, k)?.into_bytes();
1382 let v = OsPath::try_from_object(vm, v)?.into_bytes();
1383 if k.contains(&0) {
1384 return Err(
1385 vm.new_value_error("envp dict key cannot contain a nul byte".to_owned())
1386 );
1387 }
1388 if k.contains(&b'=') {
1389 return Err(vm.new_value_error(
1390 "envp dict key cannot contain a '=' character".to_owned(),
1391 ));
1392 }
1393 if v.contains(&0) {
1394 return Err(
1395 vm.new_value_error("envp dict value cannot contain a nul byte".to_owned())
1396 );
1397 }
1398 let mut env = k;
1399 env.push(b'=');
1400 env.extend(v);
1401 Ok(unsafe { CString::from_vec_unchecked(env) })
1402 })
1403 .collect()
1404 }
1405
1406 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1407 #[derive(FromArgs)]
1408 pub(super) struct PosixSpawnArgs {
1409 #[pyarg(positional)]
1410 path: OsPath,
1411 #[pyarg(positional)]
1412 args: crate::function::ArgIterable<OsPath>,
1413 #[pyarg(positional)]
1414 env: crate::function::ArgMapping,
1415 #[pyarg(named, default)]
1416 file_actions: Option<crate::function::ArgIterable<PyTupleRef>>,
1417 #[pyarg(named, default)]
1418 setsigdef: Option<crate::function::ArgIterable<i32>>,
1419 }
1420
1421 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1422 #[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive)]
1423 #[repr(i32)]
1424 enum PosixSpawnFileActionIdentifier {
1425 Open,
1426 Close,
1427 Dup2,
1428 }
1429
1430 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1431 impl PosixSpawnArgs {
1432 fn spawn(self, spawnp: bool, vm: &VirtualMachine) -> PyResult<libc::pid_t> {
1433 use crate::TryFromBorrowedObject;
1434
1435 let path = self
1436 .path
1437 .clone()
1438 .into_cstring(vm)
1439 .map_err(|_| vm.new_value_error("path should not have nul bytes".to_owned()))?;
1440
1441 let mut file_actions = unsafe {
1442 let mut fa = std::mem::MaybeUninit::uninit();
1443 assert!(libc::posix_spawn_file_actions_init(fa.as_mut_ptr()) == 0);
1444 fa.assume_init()
1445 };
1446 if let Some(it) = self.file_actions {
1447 for action in it.iter(vm)? {
1448 let action = action?;
1449 let (id, args) = action.split_first().ok_or_else(|| {
1450 vm.new_type_error(
1451 "Each file_actions element must be a non-empty tuple".to_owned(),
1452 )
1453 })?;
1454 let id = i32::try_from_borrowed_object(vm, id)?;
1455 let id = PosixSpawnFileActionIdentifier::try_from(id).map_err(|_| {
1456 vm.new_type_error("Unknown file_actions identifier".to_owned())
1457 })?;
1458 let args: crate::function::FuncArgs = args.to_vec().into();
1459 let ret = match id {
1460 PosixSpawnFileActionIdentifier::Open => {
1461 let (fd, path, oflag, mode): (_, OsPath, _, _) = args.bind(vm)?;
1462 let path = CString::new(path.into_bytes()).map_err(|_| {
1463 vm.new_value_error(
1464 "POSIX_SPAWN_OPEN path should not have nul bytes".to_owned(),
1465 )
1466 })?;
1467 unsafe {
1468 libc::posix_spawn_file_actions_addopen(
1469 &mut file_actions,
1470 fd,
1471 path.as_ptr(),
1472 oflag,
1473 mode,
1474 )
1475 }
1476 }
1477 PosixSpawnFileActionIdentifier::Close => {
1478 let (fd,) = args.bind(vm)?;
1479 unsafe {
1480 libc::posix_spawn_file_actions_addclose(&mut file_actions, fd)
1481 }
1482 }
1483 PosixSpawnFileActionIdentifier::Dup2 => {
1484 let (fd, newfd) = args.bind(vm)?;
1485 unsafe {
1486 libc::posix_spawn_file_actions_adddup2(&mut file_actions, fd, newfd)
1487 }
1488 }
1489 };
1490 if ret != 0 {
1491 let err = std::io::Error::from_raw_os_error(ret);
1492 return Err(IOErrorBuilder::with_filename(&err, self.path, vm));
1493 }
1494 }
1495 }
1496
1497 let mut attrp = unsafe {
1498 let mut sa = std::mem::MaybeUninit::uninit();
1499 assert!(libc::posix_spawnattr_init(sa.as_mut_ptr()) == 0);
1500 sa.assume_init()
1501 };
1502 if let Some(sigs) = self.setsigdef {
1503 use nix::sys::signal;
1504 let mut set = signal::SigSet::empty();
1505 for sig in sigs.iter(vm)? {
1506 let sig = sig?;
1507 let sig = signal::Signal::try_from(sig).map_err(|_| {
1508 vm.new_value_error(format!("signal number {sig} out of range"))
1509 })?;
1510 set.add(sig);
1511 }
1512 assert!(
1513 unsafe { libc::posix_spawnattr_setsigdefault(&mut attrp, set.as_ref()) } == 0
1514 );
1515 }
1516
1517 let mut args: Vec<CString> = self
1518 .args
1519 .iter(vm)?
1520 .map(|res| {
1521 CString::new(res?.into_bytes()).map_err(|_| {
1522 vm.new_value_error("path should not have nul bytes".to_owned())
1523 })
1524 })
1525 .collect::<Result<_, _>>()?;
1526 let argv: Vec<*mut libc::c_char> = args
1527 .iter_mut()
1528 .map(|s| s.as_ptr() as _)
1529 .chain(std::iter::once(std::ptr::null_mut()))
1530 .collect();
1531 let mut env = envp_from_dict(self.env, vm)?;
1532 let envp: Vec<*mut libc::c_char> = env
1533 .iter_mut()
1534 .map(|s| s.as_ptr() as _)
1535 .chain(std::iter::once(std::ptr::null_mut()))
1536 .collect();
1537
1538 let mut pid = 0;
1539 let ret = unsafe {
1540 if spawnp {
1541 libc::posix_spawnp(
1542 &mut pid,
1543 path.as_ptr(),
1544 &file_actions,
1545 &attrp,
1546 argv.as_ptr(),
1547 envp.as_ptr(),
1548 )
1549 } else {
1550 libc::posix_spawn(
1551 &mut pid,
1552 path.as_ptr(),
1553 &file_actions,
1554 &attrp,
1555 argv.as_ptr(),
1556 envp.as_ptr(),
1557 )
1558 }
1559 };
1560
1561 if ret == 0 {
1562 Ok(pid)
1563 } else {
1564 let err = std::io::Error::from_raw_os_error(ret);
1565 Err(IOErrorBuilder::with_filename(&err, self.path, vm))
1566 }
1567 }
1568 }
1569
1570 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1571 #[pyfunction]
1572 fn posix_spawn(args: PosixSpawnArgs, vm: &VirtualMachine) -> PyResult<libc::pid_t> {
1573 args.spawn(false, vm)
1574 }
1575 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
1576 #[pyfunction]
1577 fn posix_spawnp(args: PosixSpawnArgs, vm: &VirtualMachine) -> PyResult<libc::pid_t> {
1578 args.spawn(true, vm)
1579 }
1580
1581 #[pyfunction(name = "WIFSIGNALED")]
1582 fn wifsignaled(status: i32) -> bool {
1583 libc::WIFSIGNALED(status)
1584 }
1585 #[pyfunction(name = "WIFSTOPPED")]
1586 fn wifstopped(status: i32) -> bool {
1587 libc::WIFSTOPPED(status)
1588 }
1589 #[pyfunction(name = "WIFEXITED")]
1590 fn wifexited(status: i32) -> bool {
1591 libc::WIFEXITED(status)
1592 }
1593 #[pyfunction(name = "WTERMSIG")]
1594 fn wtermsig(status: i32) -> i32 {
1595 libc::WTERMSIG(status)
1596 }
1597 #[pyfunction(name = "WSTOPSIG")]
1598 fn wstopsig(status: i32) -> i32 {
1599 libc::WSTOPSIG(status)
1600 }
1601 #[pyfunction(name = "WEXITSTATUS")]
1602 fn wexitstatus(status: i32) -> i32 {
1603 libc::WEXITSTATUS(status)
1604 }
1605
1606 #[pyfunction]
1607 fn waitpid(pid: libc::pid_t, opt: i32, vm: &VirtualMachine) -> PyResult<(libc::pid_t, i32)> {
1608 let mut status = 0;
1609 let pid = unsafe { libc::waitpid(pid, &mut status, opt) };
1610 let pid = nix::Error::result(pid).map_err(|err| err.into_pyexception(vm))?;
1611 Ok((pid, status))
1612 }
1613 #[pyfunction]
1614 fn wait(vm: &VirtualMachine) -> PyResult<(libc::pid_t, i32)> {
1615 waitpid(-1, 0, vm)
1616 }
1617
1618 #[pyfunction]
1619 fn kill(pid: i32, sig: isize, vm: &VirtualMachine) -> PyResult<()> {
1620 {
1621 let ret = unsafe { libc::kill(pid, sig as i32) };
1622 if ret == -1 {
1623 Err(errno_err(vm))
1624 } else {
1625 Ok(())
1626 }
1627 }
1628 }
1629
1630 #[pyfunction]
1631 fn get_terminal_size(
1632 fd: OptionalArg<i32>,
1633 vm: &VirtualMachine,
1634 ) -> PyResult<_os::PyTerminalSize> {
1635 let (columns, lines) = {
1636 nix::ioctl_read_bad!(winsz, libc::TIOCGWINSZ, libc::winsize);
1637 let mut w = libc::winsize {
1638 ws_row: 0,
1639 ws_col: 0,
1640 ws_xpixel: 0,
1641 ws_ypixel: 0,
1642 };
1643 unsafe { winsz(fd.unwrap_or(libc::STDOUT_FILENO), &mut w) }
1644 .map_err(|err| err.into_pyexception(vm))?;
1645 (w.ws_col.into(), w.ws_row.into())
1646 };
1647 Ok(_os::PyTerminalSize { columns, lines })
1648 }
1649
1650 #[cfg(target_os = "macos")]
1653 extern "C" {
1654 fn fcopyfile(
1655 in_fd: libc::c_int,
1656 out_fd: libc::c_int,
1657 state: *mut libc::c_void, flags: u32, ) -> libc::c_int;
1660 }
1661
1662 #[cfg(target_os = "macos")]
1663 #[pyfunction]
1664 fn _fcopyfile(in_fd: i32, out_fd: i32, flags: i32, vm: &VirtualMachine) -> PyResult<()> {
1665 let ret = unsafe { fcopyfile(in_fd, out_fd, std::ptr::null_mut(), flags as u32) };
1666 if ret < 0 {
1667 Err(errno_err(vm))
1668 } else {
1669 Ok(())
1670 }
1671 }
1672
1673 #[pyfunction]
1674 fn dup(fd: i32, vm: &VirtualMachine) -> PyResult<i32> {
1675 let fd = nix::unistd::dup(fd).map_err(|e| e.into_pyexception(vm))?;
1676 super::raw_set_inheritable(fd, false)
1677 .map(|()| fd)
1678 .map_err(|e| {
1679 let _ = nix::unistd::close(fd);
1680 e.into_pyexception(vm)
1681 })
1682 }
1683
1684 #[derive(FromArgs)]
1685 struct Dup2Args {
1686 #[pyarg(positional)]
1687 fd: i32,
1688 #[pyarg(positional)]
1689 fd2: i32,
1690 #[pyarg(any, default = "true")]
1691 inheritable: bool,
1692 }
1693
1694 #[pyfunction]
1695 fn dup2(args: Dup2Args, vm: &VirtualMachine) -> PyResult<i32> {
1696 let fd = nix::unistd::dup2(args.fd, args.fd2).map_err(|e| e.into_pyexception(vm))?;
1697 if !args.inheritable {
1698 super::raw_set_inheritable(fd, false).map_err(|e| {
1699 let _ = nix::unistd::close(fd);
1700 e.into_pyexception(vm)
1701 })?
1702 }
1703 Ok(fd)
1704 }
1705
1706 pub(crate) fn support_funcs() -> Vec<SupportFunc> {
1707 vec![
1708 SupportFunc::new(
1709 "chmod",
1710 Some(false),
1711 Some(false),
1712 Some(cfg!(any(
1713 target_os = "macos",
1714 target_os = "freebsd",
1715 target_os = "netbsd"
1716 ))),
1717 ),
1718 #[cfg(not(target_os = "redox"))]
1719 SupportFunc::new("chroot", Some(false), None, None),
1720 #[cfg(not(target_os = "redox"))]
1721 SupportFunc::new("chown", Some(true), Some(true), Some(true)),
1722 #[cfg(not(target_os = "redox"))]
1723 SupportFunc::new("lchown", None, None, None),
1724 #[cfg(not(target_os = "redox"))]
1725 SupportFunc::new("fchown", Some(true), None, Some(true)),
1726 #[cfg(not(target_os = "redox"))]
1727 SupportFunc::new("mknod", Some(true), Some(MKNOD_DIR_FD), Some(false)),
1728 SupportFunc::new("umask", Some(false), Some(false), Some(false)),
1729 SupportFunc::new("execv", None, None, None),
1730 SupportFunc::new("pathconf", Some(true), None, None),
1731 ]
1732 }
1733
1734 #[pyfunction]
1735 fn getlogin(vm: &VirtualMachine) -> PyResult<String> {
1736 let ptr = unsafe { libc::getlogin() };
1740 if ptr.is_null() {
1741 return Err(vm.new_os_error("unable to determine login name".to_owned()));
1742 }
1743 let slice = unsafe { CStr::from_ptr(ptr) };
1744 slice
1745 .to_str()
1746 .map(|s| s.to_owned())
1747 .map_err(|e| vm.new_unicode_decode_error(format!("unable to decode login name: {e}")))
1748 }
1749
1750 #[cfg(any(
1752 target_os = "android",
1753 target_os = "freebsd",
1754 target_os = "linux",
1755 target_os = "openbsd"
1756 ))]
1757 #[pyfunction]
1758 fn getgrouplist(user: PyStrRef, group: u32, vm: &VirtualMachine) -> PyResult<Vec<PyObjectRef>> {
1759 let user = CString::new(user.as_str()).unwrap();
1760 let gid = Gid::from_raw(group);
1761 let group_ids = unistd::getgrouplist(&user, gid).map_err(|err| err.into_pyexception(vm))?;
1762 Ok(group_ids
1763 .into_iter()
1764 .map(|gid| vm.new_pyobj(gid.as_raw()))
1765 .collect())
1766 }
1767
1768 #[cfg(not(target_os = "redox"))]
1769 cfg_if::cfg_if! {
1770 if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
1771 type PriorityWhichType = libc::__priority_which_t;
1772 } else {
1773 type PriorityWhichType = libc::c_int;
1774 }
1775 }
1776 #[cfg(not(target_os = "redox"))]
1777 cfg_if::cfg_if! {
1778 if #[cfg(target_os = "freebsd")] {
1779 type PriorityWhoType = i32;
1780 } else {
1781 type PriorityWhoType = u32;
1782 }
1783 }
1784
1785 #[cfg(not(target_os = "redox"))]
1786 #[pyfunction]
1787 fn getpriority(
1788 which: PriorityWhichType,
1789 who: PriorityWhoType,
1790 vm: &VirtualMachine,
1791 ) -> PyResult {
1792 use nix::errno::{errno, Errno};
1793 Errno::clear();
1794 let retval = unsafe { libc::getpriority(which, who) };
1795 if errno() != 0 {
1796 Err(errno_err(vm))
1797 } else {
1798 Ok(vm.ctx.new_int(retval).into())
1799 }
1800 }
1801
1802 #[cfg(not(target_os = "redox"))]
1803 #[pyfunction]
1804 fn setpriority(
1805 which: PriorityWhichType,
1806 who: PriorityWhoType,
1807 priority: i32,
1808 vm: &VirtualMachine,
1809 ) -> PyResult<()> {
1810 let retval = unsafe { libc::setpriority(which, who, priority) };
1811 if retval == -1 {
1812 Err(errno_err(vm))
1813 } else {
1814 Ok(())
1815 }
1816 }
1817
1818 struct PathconfName(i32);
1819
1820 impl TryFromObject for PathconfName {
1821 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
1822 let i = match obj.downcast::<PyInt>() {
1823 Ok(int) => int.try_to_primitive(vm)?,
1824 Err(obj) => {
1825 let s = PyStrRef::try_from_object(vm, obj)?;
1826 s.as_str().parse::<PathconfVar>().map_err(|_| {
1827 vm.new_value_error("unrecognized configuration name".to_string())
1828 })? as i32
1829 }
1830 };
1831 Ok(Self(i))
1832 }
1833 }
1834
1835 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, EnumIter, EnumString)]
1838 #[repr(i32)]
1839 #[allow(non_camel_case_types)]
1840 pub enum PathconfVar {
1841 #[cfg(any(
1842 target_os = "dragonfly",
1843 target_os = "freebsd",
1844 target_os = "linux",
1845 target_os = "netbsd",
1846 target_os = "openbsd",
1847 target_os = "redox"
1848 ))]
1849 PC_FILESIZEBITS = libc::_PC_FILESIZEBITS,
1852 PC_LINK_MAX = libc::_PC_LINK_MAX,
1854 PC_MAX_CANON = libc::_PC_MAX_CANON,
1856 PC_MAX_INPUT = libc::_PC_MAX_INPUT,
1860 PC_NAME_MAX = libc::_PC_NAME_MAX,
1863 PC_PATH_MAX = libc::_PC_PATH_MAX,
1868 PC_PIPE_BUF = libc::_PC_PIPE_BUF,
1871 #[cfg(any(
1872 target_os = "android",
1873 target_os = "dragonfly",
1874 target_os = "illumos",
1875 target_os = "linux",
1876 target_os = "netbsd",
1877 target_os = "openbsd",
1878 target_os = "redox",
1879 target_os = "solaris"
1880 ))]
1881 PC_2_SYMLINKS = libc::_PC_2_SYMLINKS,
1883 #[cfg(any(
1884 target_os = "android",
1885 target_os = "dragonfly",
1886 target_os = "freebsd",
1887 target_os = "linux",
1888 target_os = "openbsd",
1889 target_os = "redox"
1890 ))]
1891 PC_ALLOC_SIZE_MIN = libc::_PC_ALLOC_SIZE_MIN,
1894 #[cfg(any(
1895 target_os = "android",
1896 target_os = "dragonfly",
1897 target_os = "freebsd",
1898 target_os = "linux",
1899 target_os = "openbsd"
1900 ))]
1901 PC_REC_INCR_XFER_SIZE = libc::_PC_REC_INCR_XFER_SIZE,
1904 #[cfg(any(
1905 target_os = "android",
1906 target_os = "dragonfly",
1907 target_os = "freebsd",
1908 target_os = "linux",
1909 target_os = "openbsd",
1910 target_os = "redox"
1911 ))]
1912 PC_REC_MAX_XFER_SIZE = libc::_PC_REC_MAX_XFER_SIZE,
1914 #[cfg(any(
1915 target_os = "android",
1916 target_os = "dragonfly",
1917 target_os = "freebsd",
1918 target_os = "linux",
1919 target_os = "openbsd",
1920 target_os = "redox"
1921 ))]
1922 PC_REC_MIN_XFER_SIZE = libc::_PC_REC_MIN_XFER_SIZE,
1924 #[cfg(any(
1925 target_os = "android",
1926 target_os = "dragonfly",
1927 target_os = "freebsd",
1928 target_os = "linux",
1929 target_os = "openbsd",
1930 target_os = "redox"
1931 ))]
1932 PC_REC_XFER_ALIGN = libc::_PC_REC_XFER_ALIGN,
1934 #[cfg(any(
1935 target_os = "android",
1936 target_os = "dragonfly",
1937 target_os = "freebsd",
1938 target_os = "illumos",
1939 target_os = "linux",
1940 target_os = "netbsd",
1941 target_os = "openbsd",
1942 target_os = "redox",
1943 target_os = "solaris"
1944 ))]
1945 PC_SYMLINK_MAX = libc::_PC_SYMLINK_MAX,
1947 PC_CHOWN_RESTRICTED = libc::_PC_CHOWN_RESTRICTED,
1952 PC_NO_TRUNC = libc::_PC_NO_TRUNC,
1954 PC_VDISABLE = libc::_PC_VDISABLE,
1957 #[cfg(any(
1958 target_os = "android",
1959 target_os = "dragonfly",
1960 target_os = "freebsd",
1961 target_os = "illumos",
1962 target_os = "linux",
1963 target_os = "openbsd",
1964 target_os = "redox",
1965 target_os = "solaris"
1966 ))]
1967 PC_ASYNC_IO = libc::_PC_ASYNC_IO,
1970 #[cfg(any(
1971 target_os = "android",
1972 target_os = "dragonfly",
1973 target_os = "freebsd",
1974 target_os = "illumos",
1975 target_os = "linux",
1976 target_os = "openbsd",
1977 target_os = "redox",
1978 target_os = "solaris"
1979 ))]
1980 PC_PRIO_IO = libc::_PC_PRIO_IO,
1983 #[cfg(any(
1984 target_os = "android",
1985 target_os = "dragonfly",
1986 target_os = "freebsd",
1987 target_os = "illumos",
1988 target_os = "linux",
1989 target_os = "netbsd",
1990 target_os = "openbsd",
1991 target_os = "redox",
1992 target_os = "solaris"
1993 ))]
1994 PC_SYNC_IO = libc::_PC_SYNC_IO,
1997 #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))]
1998 PC_TIMESTAMP_RESOLUTION = libc::_PC_TIMESTAMP_RESOLUTION,
2000 }
2001
2002 #[cfg(unix)]
2003 #[pyfunction]
2004 fn pathconf(
2005 path: OsPathOrFd,
2006 PathconfName(name): PathconfName,
2007 vm: &VirtualMachine,
2008 ) -> PyResult<Option<libc::c_long>> {
2009 use nix::errno::{self, Errno};
2010
2011 Errno::clear();
2012 debug_assert_eq!(errno::errno(), 0);
2013 let raw = match &path {
2014 OsPathOrFd::Path(path) => {
2015 let path = path.clone().into_cstring(vm)?;
2016 unsafe { libc::pathconf(path.as_ptr(), name) }
2017 }
2018 OsPathOrFd::Fd(fd) => unsafe { libc::fpathconf(*fd, name) },
2019 };
2020
2021 if raw == -1 {
2022 if errno::errno() == 0 {
2023 Ok(None)
2024 } else {
2025 Err(IOErrorBuilder::with_filename(
2026 &io::Error::from(Errno::last()),
2027 path,
2028 vm,
2029 ))
2030 }
2031 } else {
2032 Ok(Some(raw))
2033 }
2034 }
2035
2036 #[pyfunction]
2037 fn fpathconf(
2038 fd: i32,
2039 name: PathconfName,
2040 vm: &VirtualMachine,
2041 ) -> PyResult<Option<libc::c_long>> {
2042 pathconf(OsPathOrFd::Fd(fd), name, vm)
2043 }
2044
2045 #[pyattr]
2046 fn pathconf_names(vm: &VirtualMachine) -> PyDictRef {
2047 use strum::IntoEnumIterator;
2048 let pathname = vm.ctx.new_dict();
2049 for variant in PathconfVar::iter() {
2050 let key = vm.ctx.new_str(format!("{:?}", variant));
2052 let value = vm.ctx.new_int(variant as u8);
2054 pathname
2055 .set_item(&*key, value.into(), vm)
2056 .expect("dict set_item unexpectedly failed");
2057 }
2058 pathname
2059 }
2060
2061 #[cfg(not(target_os = "redox"))]
2062 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, EnumIter, EnumString)]
2063 #[repr(i32)]
2064 #[allow(non_camel_case_types)]
2065 pub enum SysconfVar {
2066 SC_2_CHAR_TERM = libc::_SC_2_CHAR_TERM,
2067 SC_2_C_BIND = libc::_SC_2_C_BIND,
2068 SC_2_C_DEV = libc::_SC_2_C_DEV,
2069 SC_2_FORT_DEV = libc::_SC_2_FORT_DEV,
2070 SC_2_FORT_RUN = libc::_SC_2_FORT_RUN,
2071 SC_2_LOCALEDEF = libc::_SC_2_LOCALEDEF,
2072 SC_2_SW_DEV = libc::_SC_2_SW_DEV,
2073 SC_2_UPE = libc::_SC_2_UPE,
2074 SC_2_VERSION = libc::_SC_2_VERSION,
2075 SC_AIO_LISTIO_MAX = libc::_SC_AIO_LISTIO_MAX,
2076 SC_AIO_MAX = libc::_SC_AIO_MAX,
2077 SC_AIO_PRIO_DELTA_MAX = libc::_SC_AIO_PRIO_DELTA_MAX,
2078 SC_ARG_MAX = libc::_SC_ARG_MAX,
2079 SC_ASYNCHRONOUS_IO = libc::_SC_ASYNCHRONOUS_IO,
2080 SC_ATEXIT_MAX = libc::_SC_ATEXIT_MAX,
2081 SC_BC_BASE_MAX = libc::_SC_BC_BASE_MAX,
2082 SC_BC_DIM_MAX = libc::_SC_BC_DIM_MAX,
2083 SC_BC_SCALE_MAX = libc::_SC_BC_SCALE_MAX,
2084 SC_BC_STRING_MAX = libc::_SC_BC_STRING_MAX,
2085 SC_CHILD_MAX = libc::_SC_CHILD_MAX,
2086 SC_CLK_TCK = libc::_SC_CLK_TCK,
2087 SC_COLL_WEIGHTS_MAX = libc::_SC_COLL_WEIGHTS_MAX,
2088 SC_DELAYTIMER_MAX = libc::_SC_DELAYTIMER_MAX,
2089 SC_EXPR_NEST_MAX = libc::_SC_EXPR_NEST_MAX,
2090 SC_FSYNC = libc::_SC_FSYNC,
2091 SC_GETGR_R_SIZE_MAX = libc::_SC_GETGR_R_SIZE_MAX,
2092 SC_GETPW_R_SIZE_MAX = libc::_SC_GETPW_R_SIZE_MAX,
2093 SC_IOV_MAX = libc::_SC_IOV_MAX,
2094 SC_JOB_CONTROL = libc::_SC_JOB_CONTROL,
2095 SC_LINE_MAX = libc::_SC_LINE_MAX,
2096 SC_LOGIN_NAME_MAX = libc::_SC_LOGIN_NAME_MAX,
2097 SC_MAPPED_FILES = libc::_SC_MAPPED_FILES,
2098 SC_MEMLOCK = libc::_SC_MEMLOCK,
2099 SC_MEMLOCK_RANGE = libc::_SC_MEMLOCK_RANGE,
2100 SC_MEMORY_PROTECTION = libc::_SC_MEMORY_PROTECTION,
2101 SC_MESSAGE_PASSING = libc::_SC_MESSAGE_PASSING,
2102 SC_MQ_OPEN_MAX = libc::_SC_MQ_OPEN_MAX,
2103 SC_MQ_PRIO_MAX = libc::_SC_MQ_PRIO_MAX,
2104 SC_NGROUPS_MAX = libc::_SC_NGROUPS_MAX,
2105 SC_NPROCESSORS_CONF = libc::_SC_NPROCESSORS_CONF,
2106 SC_NPROCESSORS_ONLN = libc::_SC_NPROCESSORS_ONLN,
2107 SC_OPEN_MAX = libc::_SC_OPEN_MAX,
2108 SC_PAGE_SIZE = libc::_SC_PAGE_SIZE,
2109 #[cfg(any(
2110 target_os = "linux",
2111 target_vendor = "apple",
2112 target_os = "netbsd",
2113 target_os = "fuchsia"
2114 ))]
2115 SC_PASS_MAX = libc::_SC_PASS_MAX,
2116 SC_PHYS_PAGES = libc::_SC_PHYS_PAGES,
2117 SC_PRIORITIZED_IO = libc::_SC_PRIORITIZED_IO,
2118 SC_PRIORITY_SCHEDULING = libc::_SC_PRIORITY_SCHEDULING,
2119 SC_REALTIME_SIGNALS = libc::_SC_REALTIME_SIGNALS,
2120 SC_RE_DUP_MAX = libc::_SC_RE_DUP_MAX,
2121 SC_RTSIG_MAX = libc::_SC_RTSIG_MAX,
2122 SC_SAVED_IDS = libc::_SC_SAVED_IDS,
2123 SC_SEMAPHORES = libc::_SC_SEMAPHORES,
2124 SC_SEM_NSEMS_MAX = libc::_SC_SEM_NSEMS_MAX,
2125 SC_SEM_VALUE_MAX = libc::_SC_SEM_VALUE_MAX,
2126 SC_SHARED_MEMORY_OBJECTS = libc::_SC_SHARED_MEMORY_OBJECTS,
2127 SC_SIGQUEUE_MAX = libc::_SC_SIGQUEUE_MAX,
2128 SC_STREAM_MAX = libc::_SC_STREAM_MAX,
2129 SC_SYNCHRONIZED_IO = libc::_SC_SYNCHRONIZED_IO,
2130 SC_THREADS = libc::_SC_THREADS,
2131 SC_THREAD_ATTR_STACKADDR = libc::_SC_THREAD_ATTR_STACKADDR,
2132 SC_THREAD_ATTR_STACKSIZE = libc::_SC_THREAD_ATTR_STACKSIZE,
2133 SC_THREAD_DESTRUCTOR_ITERATIONS = libc::_SC_THREAD_DESTRUCTOR_ITERATIONS,
2134 SC_THREAD_KEYS_MAX = libc::_SC_THREAD_KEYS_MAX,
2135 SC_THREAD_PRIORITY_SCHEDULING = libc::_SC_THREAD_PRIORITY_SCHEDULING,
2136 SC_THREAD_PRIO_INHERIT = libc::_SC_THREAD_PRIO_INHERIT,
2137 SC_THREAD_PRIO_PROTECT = libc::_SC_THREAD_PRIO_PROTECT,
2138 SC_THREAD_PROCESS_SHARED = libc::_SC_THREAD_PROCESS_SHARED,
2139 SC_THREAD_SAFE_FUNCTIONS = libc::_SC_THREAD_SAFE_FUNCTIONS,
2140 SC_THREAD_STACK_MIN = libc::_SC_THREAD_STACK_MIN,
2141 SC_THREAD_THREADS_MAX = libc::_SC_THREAD_THREADS_MAX,
2142 SC_TIMERS = libc::_SC_TIMERS,
2143 SC_TIMER_MAX = libc::_SC_TIMER_MAX,
2144 SC_TTY_NAME_MAX = libc::_SC_TTY_NAME_MAX,
2145 SC_TZNAME_MAX = libc::_SC_TZNAME_MAX,
2146 SC_VERSION = libc::_SC_VERSION,
2147 SC_XOPEN_CRYPT = libc::_SC_XOPEN_CRYPT,
2148 SC_XOPEN_ENH_I18N = libc::_SC_XOPEN_ENH_I18N,
2149 SC_XOPEN_LEGACY = libc::_SC_XOPEN_LEGACY,
2150 SC_XOPEN_REALTIME = libc::_SC_XOPEN_REALTIME,
2151 SC_XOPEN_REALTIME_THREADS = libc::_SC_XOPEN_REALTIME_THREADS,
2152 SC_XOPEN_SHM = libc::_SC_XOPEN_SHM,
2153 SC_XOPEN_UNIX = libc::_SC_XOPEN_UNIX,
2154 SC_XOPEN_VERSION = libc::_SC_XOPEN_VERSION,
2155 SC_XOPEN_XCU_VERSION = libc::_SC_XOPEN_XCU_VERSION,
2156 #[cfg(any(
2157 target_os = "linux",
2158 target_vendor = "apple",
2159 target_os = "netbsd",
2160 target_os = "fuchsia"
2161 ))]
2162 SC_XBS5_ILP32_OFF32 = libc::_SC_XBS5_ILP32_OFF32,
2163 #[cfg(any(
2164 target_os = "linux",
2165 target_vendor = "apple",
2166 target_os = "netbsd",
2167 target_os = "fuchsia"
2168 ))]
2169 SC_XBS5_ILP32_OFFBIG = libc::_SC_XBS5_ILP32_OFFBIG,
2170 #[cfg(any(
2171 target_os = "linux",
2172 target_vendor = "apple",
2173 target_os = "netbsd",
2174 target_os = "fuchsia"
2175 ))]
2176 SC_XBS5_LP64_OFF64 = libc::_SC_XBS5_LP64_OFF64,
2177 #[cfg(any(
2178 target_os = "linux",
2179 target_vendor = "apple",
2180 target_os = "netbsd",
2181 target_os = "fuchsia"
2182 ))]
2183 SC_XBS5_LPBIG_OFFBIG = libc::_SC_XBS5_LPBIG_OFFBIG,
2184 }
2185
2186 #[cfg(target_os = "redox")]
2187 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, EnumIter, EnumString)]
2188 #[repr(i32)]
2189 #[allow(non_camel_case_types)]
2190 pub enum SysconfVar {
2191 SC_ARG_MAX = libc::_SC_ARG_MAX,
2192 SC_CHILD_MAX = libc::_SC_CHILD_MAX,
2193 SC_CLK_TCK = libc::_SC_CLK_TCK,
2194 SC_NGROUPS_MAX = libc::_SC_NGROUPS_MAX,
2195 SC_OPEN_MAX = libc::_SC_OPEN_MAX,
2196 SC_STREAM_MAX = libc::_SC_STREAM_MAX,
2197 SC_TZNAME_MAX = libc::_SC_TZNAME_MAX,
2198 SC_VERSION = libc::_SC_VERSION,
2199 SC_PAGE_SIZE = libc::_SC_PAGE_SIZE,
2200 SC_RE_DUP_MAX = libc::_SC_RE_DUP_MAX,
2201 SC_LOGIN_NAME_MAX = libc::_SC_LOGIN_NAME_MAX,
2202 SC_TTY_NAME_MAX = libc::_SC_TTY_NAME_MAX,
2203 SC_SYMLOOP_MAX = libc::_SC_SYMLOOP_MAX,
2204 SC_HOST_NAME_MAX = libc::_SC_HOST_NAME_MAX,
2205 }
2206
2207 impl SysconfVar {
2208 pub const SC_PAGESIZE: SysconfVar = Self::SC_PAGE_SIZE;
2209 }
2210
2211 struct SysconfName(i32);
2212
2213 impl TryFromObject for SysconfName {
2214 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
2215 let i = match obj.downcast::<PyInt>() {
2216 Ok(int) => int.try_to_primitive(vm)?,
2217 Err(obj) => {
2218 let s = PyStrRef::try_from_object(vm, obj)?;
2219 s.as_str().parse::<SysconfVar>().or_else(|_| {
2220 if s.as_str() == "SC_PAGESIZE" {
2221 Ok(SysconfVar::SC_PAGESIZE)
2222 } else {
2223 Err(vm.new_value_error("unrecognized configuration name".to_string()))
2224 }
2225 })? as i32
2226 }
2227 };
2228 Ok(Self(i))
2229 }
2230 }
2231
2232 #[pyfunction]
2233 fn sysconf(name: SysconfName, vm: &VirtualMachine) -> PyResult<libc::c_long> {
2234 let r = unsafe { libc::sysconf(name.0) };
2235 if r == -1 {
2236 return Err(errno_err(vm));
2237 }
2238 Ok(r)
2239 }
2240
2241 #[pyattr]
2242 fn sysconf_names(vm: &VirtualMachine) -> PyDictRef {
2243 use strum::IntoEnumIterator;
2244 let names = vm.ctx.new_dict();
2245 for variant in SysconfVar::iter() {
2246 let key = vm.ctx.new_str(format!("{:?}", variant));
2248 let value = vm.ctx.new_int(variant as u8);
2250 names
2251 .set_item(&*key, value.into(), vm)
2252 .expect("dict set_item unexpectedly failed");
2253 }
2254 names
2255 }
2256
2257 #[cfg(any(target_os = "linux", target_os = "macos"))]
2258 #[derive(FromArgs)]
2259 struct SendFileArgs<'fd> {
2260 out_fd: BorrowedFd<'fd>,
2261 in_fd: BorrowedFd<'fd>,
2262 offset: crate::common::crt_fd::Offset,
2263 count: i64,
2264 #[cfg(target_os = "macos")]
2265 #[pyarg(any, optional)]
2266 headers: OptionalArg<PyObjectRef>,
2267 #[cfg(target_os = "macos")]
2268 #[pyarg(any, optional)]
2269 trailers: OptionalArg<PyObjectRef>,
2270 #[cfg(target_os = "macos")]
2271 #[allow(dead_code)]
2272 #[pyarg(any, default)]
2273 flags: OptionalArg<i32>,
2275 }
2276
2277 #[cfg(target_os = "linux")]
2278 #[pyfunction]
2279 fn sendfile(args: SendFileArgs<'_>, vm: &VirtualMachine) -> PyResult {
2280 let mut file_offset = args.offset;
2281
2282 let res = nix::sys::sendfile::sendfile(
2283 args.out_fd,
2284 args.in_fd,
2285 Some(&mut file_offset),
2286 args.count as usize,
2287 )
2288 .map_err(|err| err.into_pyexception(vm))?;
2289 Ok(vm.ctx.new_int(res as u64).into())
2290 }
2291
2292 #[cfg(target_os = "macos")]
2293 fn _extract_vec_bytes(
2294 x: OptionalArg,
2295 vm: &VirtualMachine,
2296 ) -> PyResult<Option<Vec<crate::function::ArgBytesLike>>> {
2297 x.into_option()
2298 .map(|x| {
2299 let v: Vec<crate::function::ArgBytesLike> = x.try_to_value(vm)?;
2300 Ok(if v.is_empty() { None } else { Some(v) })
2301 })
2302 .transpose()
2303 .map(Option::flatten)
2304 }
2305
2306 #[cfg(target_os = "macos")]
2307 #[pyfunction]
2308 fn sendfile(args: SendFileArgs<'_>, vm: &VirtualMachine) -> PyResult {
2309 let headers = _extract_vec_bytes(args.headers, vm)?;
2310 let count = headers
2311 .as_ref()
2312 .map(|v| v.iter().map(|s| s.len()).sum())
2313 .unwrap_or(0) as i64
2314 + args.count;
2315
2316 let headers = headers
2317 .as_ref()
2318 .map(|v| v.iter().map(|b| b.borrow_buf()).collect::<Vec<_>>());
2319 let headers = headers
2320 .as_ref()
2321 .map(|v| v.iter().map(|borrowed| &**borrowed).collect::<Vec<_>>());
2322 let headers = headers.as_deref();
2323
2324 let trailers = _extract_vec_bytes(args.trailers, vm)?;
2325 let trailers = trailers
2326 .as_ref()
2327 .map(|v| v.iter().map(|b| b.borrow_buf()).collect::<Vec<_>>());
2328 let trailers = trailers
2329 .as_ref()
2330 .map(|v| v.iter().map(|borrowed| &**borrowed).collect::<Vec<_>>());
2331 let trailers = trailers.as_deref();
2332
2333 let (res, written) = nix::sys::sendfile::sendfile(
2334 args.in_fd,
2335 args.out_fd,
2336 args.offset,
2337 Some(count),
2338 headers,
2339 trailers,
2340 );
2341 res.map_err(|err| err.into_pyexception(vm))?;
2342 Ok(vm.ctx.new_int(written as u64).into())
2343 }
2344
2345 #[cfg(target_os = "linux")]
2346 unsafe fn sys_getrandom(buf: *mut libc::c_void, buflen: usize, flags: u32) -> isize {
2347 libc::syscall(libc::SYS_getrandom, buf, buflen, flags as usize) as _
2348 }
2349
2350 #[cfg(target_os = "linux")]
2351 #[pyfunction]
2352 fn getrandom(size: isize, flags: OptionalArg<u32>, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
2353 let size = usize::try_from(size)
2354 .map_err(|_| vm.new_os_error(format!("Invalid argument for size: {size}")))?;
2355 let mut buf = Vec::with_capacity(size);
2356 unsafe {
2357 let len = sys_getrandom(
2358 buf.as_mut_ptr() as *mut libc::c_void,
2359 size,
2360 flags.unwrap_or(0),
2361 )
2362 .try_into()
2363 .map_err(|_| errno_err(vm))?;
2364 buf.set_len(len);
2365 }
2366 Ok(buf)
2367 }
2368}