about summary refs log tree commit diff
path: root/library
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-07-12 07:44:04 +0000
committerbors <bors@rust-lang.org>2025-07-12 07:44:04 +0000
commit2f9c9cede68be26774ea44efc79d0391f1c58af2 (patch)
treedbdb3f88ed95d7017cf388cda3eaedcd9a0f2852 /library
parent9535feebd5741a55fc24e84060e82d41a75dac6e (diff)
parente43481e362431442f2a6e39c3c2d3001ff0cf917 (diff)
downloadrust-2f9c9cede68be26774ea44efc79d0391f1c58af2.tar.gz
rust-2f9c9cede68be26774ea44efc79d0391f1c58af2.zip
Auto merge of #143766 - matthiaskrgr:rollup-0x7t69s, r=matthiaskrgr
Rollup of 8 pull requests

Successful merges:

 - rust-lang/rust#142391 (rust: library: Add `setsid` method to `CommandExt` trait)
 - rust-lang/rust#143302 (`tests/ui`: A New Order [27/N])
 - rust-lang/rust#143303 (`tests/ui`: A New Order [28/28] FINAL PART)
 - rust-lang/rust#143568 (std: sys: net: uefi: tcp4: Add timeout support)
 - rust-lang/rust#143611 (Mention more APIs in `ParseIntError` docs)
 - rust-lang/rust#143661 (chore: Improve how the other suggestions message gets rendered)
 - rust-lang/rust#143708 (fix: Include frontmatter in -Zunpretty output )
 - rust-lang/rust#143718 (Make UB transmutes really UB in LLVM)

r? `@ghost`
`@rustbot` modify labels: rollup

try-job: i686-gnu-nopt-1
try-job: test-various
Diffstat (limited to 'library')
-rw-r--r--library/core/src/num/error.rs7
-rw-r--r--library/std/src/os/unix/process.rs8
-rw-r--r--library/std/src/sys/net/connection/uefi/mod.rs41
-rw-r--r--library/std/src/sys/net/connection/uefi/tcp.rs13
-rw-r--r--library/std/src/sys/net/connection/uefi/tcp4.rs68
-rw-r--r--library/std/src/sys/process/unix/common.rs9
-rw-r--r--library/std/src/sys/process/unix/common/tests.rs58
-rw-r--r--library/std/src/sys/process/unix/unix.rs14
8 files changed, 191 insertions, 27 deletions
diff --git a/library/core/src/num/error.rs b/library/core/src/num/error.rs
index a5242d60bf1..f9c4cdd0ebe 100644
--- a/library/core/src/num/error.rs
+++ b/library/core/src/num/error.rs
@@ -45,8 +45,11 @@ impl From<!> for TryFromIntError {
 
 /// An error which can be returned when parsing an integer.
 ///
-/// This error is used as the error type for the `from_str_radix()` functions
-/// on the primitive integer types, such as [`i8::from_str_radix`].
+/// For example, this error is returned by the `from_str_radix()` functions
+/// on the primitive integer types (such as [`i8::from_str_radix`])
+/// and is used as the error type in their [`FromStr`] implementations.
+///
+/// [`FromStr`]: crate::str::FromStr
 ///
 /// # Potential causes
 ///
diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs
index 466b134d8fa..76e63a69e45 100644
--- a/library/std/src/os/unix/process.rs
+++ b/library/std/src/os/unix/process.rs
@@ -210,6 +210,9 @@ pub trait CommandExt: Sealed {
     /// intentional difference from the underlying `chroot` system call.)
     #[unstable(feature = "process_chroot", issue = "141298")]
     fn chroot<P: AsRef<Path>>(&mut self, dir: P) -> &mut process::Command;
+
+    #[unstable(feature = "process_setsid", issue = "105376")]
+    fn setsid(&mut self, setsid: bool) -> &mut process::Command;
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -260,6 +263,11 @@ impl CommandExt for process::Command {
         self.as_inner_mut().chroot(dir.as_ref());
         self
     }
+
+    fn setsid(&mut self, setsid: bool) -> &mut process::Command {
+        self.as_inner_mut().setsid(setsid);
+        self
+    }
 }
 
 /// Unix-specific extensions to [`process::ExitStatus`] and
diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs
index 6835ba44ee2..884cbd4ac1d 100644
--- a/library/std/src/sys/net/connection/uefi/mod.rs
+++ b/library/std/src/sys/net/connection/uefi/mod.rs
@@ -1,37 +1,54 @@
 use crate::fmt;
 use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
 use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
+use crate::sync::{Arc, Mutex};
 use crate::sys::unsupported;
 use crate::time::Duration;
 
 mod tcp;
 pub(crate) mod tcp4;
 
-pub struct TcpStream(tcp::Tcp);
+pub struct TcpStream {
+    inner: tcp::Tcp,
+    read_timeout: Arc<Mutex<Option<Duration>>>,
+    write_timeout: Arc<Mutex<Option<Duration>>>,
+}
 
 impl TcpStream {
     pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
-        tcp::Tcp::connect(addr?).map(Self)
+        let inner = tcp::Tcp::connect(addr?, None)?;
+        Ok(Self {
+            inner,
+            read_timeout: Arc::new(Mutex::new(None)),
+            write_timeout: Arc::new(Mutex::new(None)),
+        })
     }
 
-    pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> {
-        unsupported()
+    pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result<TcpStream> {
+        let inner = tcp::Tcp::connect(addr, Some(timeout))?;
+        Ok(Self {
+            inner,
+            read_timeout: Arc::new(Mutex::new(None)),
+            write_timeout: Arc::new(Mutex::new(None)),
+        })
     }
 
-    pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
-        unsupported()
+    pub fn set_read_timeout(&self, t: Option<Duration>) -> io::Result<()> {
+        self.read_timeout.set(t).unwrap();
+        Ok(())
     }
 
-    pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
-        unsupported()
+    pub fn set_write_timeout(&self, t: Option<Duration>) -> io::Result<()> {
+        self.write_timeout.set(t).unwrap();
+        Ok(())
     }
 
     pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
-        unsupported()
+        Ok(self.read_timeout.get_cloned().unwrap())
     }
 
     pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
-        unsupported()
+        Ok(self.write_timeout.get_cloned().unwrap())
     }
 
     pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
@@ -39,7 +56,7 @@ impl TcpStream {
     }
 
     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
-        self.0.read(buf)
+        self.inner.read(buf, self.read_timeout()?)
     }
 
     pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
@@ -56,7 +73,7 @@ impl TcpStream {
     }
 
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
-        self.0.write(buf)
+        self.inner.write(buf, self.write_timeout()?)
     }
 
     pub fn write_vectored(&self, buf: &[IoSlice<'_>]) -> io::Result<usize> {
diff --git a/library/std/src/sys/net/connection/uefi/tcp.rs b/library/std/src/sys/net/connection/uefi/tcp.rs
index 55b6dbf2490..1152f69446e 100644
--- a/library/std/src/sys/net/connection/uefi/tcp.rs
+++ b/library/std/src/sys/net/connection/uefi/tcp.rs
@@ -1,33 +1,34 @@
 use super::tcp4;
 use crate::io;
 use crate::net::SocketAddr;
+use crate::time::Duration;
 
 pub(crate) enum Tcp {
     V4(tcp4::Tcp4),
 }
 
 impl Tcp {
-    pub(crate) fn connect(addr: &SocketAddr) -> io::Result<Self> {
+    pub(crate) fn connect(addr: &SocketAddr, timeout: Option<Duration>) -> io::Result<Self> {
         match addr {
             SocketAddr::V4(x) => {
                 let temp = tcp4::Tcp4::new()?;
                 temp.configure(true, Some(x), None)?;
-                temp.connect()?;
+                temp.connect(timeout)?;
                 Ok(Tcp::V4(temp))
             }
             SocketAddr::V6(_) => todo!(),
         }
     }
 
-    pub(crate) fn write(&self, buf: &[u8]) -> io::Result<usize> {
+    pub(crate) fn write(&self, buf: &[u8], timeout: Option<Duration>) -> io::Result<usize> {
         match self {
-            Self::V4(client) => client.write(buf),
+            Self::V4(client) => client.write(buf, timeout),
         }
     }
 
-    pub(crate) fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+    pub(crate) fn read(&self, buf: &mut [u8], timeout: Option<Duration>) -> io::Result<usize> {
         match self {
-            Self::V4(client) => client.read(buf),
+            Self::V4(client) => client.read(buf, timeout),
         }
     }
 }
diff --git a/library/std/src/sys/net/connection/uefi/tcp4.rs b/library/std/src/sys/net/connection/uefi/tcp4.rs
index af1ba2be47a..6342718929a 100644
--- a/library/std/src/sys/net/connection/uefi/tcp4.rs
+++ b/library/std/src/sys/net/connection/uefi/tcp4.rs
@@ -6,6 +6,7 @@ use crate::net::SocketAddrV4;
 use crate::ptr::NonNull;
 use crate::sync::atomic::{AtomicBool, Ordering};
 use crate::sys::pal::helpers;
+use crate::time::{Duration, Instant};
 
 const TYPE_OF_SERVICE: u8 = 8;
 const TIME_TO_LIVE: u8 = 255;
@@ -66,7 +67,7 @@ impl Tcp4 {
         if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
     }
 
-    pub(crate) fn connect(&self) -> io::Result<()> {
+    pub(crate) fn connect(&self, timeout: Option<Duration>) -> io::Result<()> {
         let evt = unsafe { self.create_evt() }?;
         let completion_token =
             tcp4::CompletionToken { event: evt.as_ptr(), status: Status::SUCCESS };
@@ -79,7 +80,7 @@ impl Tcp4 {
             return Err(io::Error::from_raw_os_error(r.as_usize()));
         }
 
-        self.wait_for_flag();
+        unsafe { self.wait_or_cancel(timeout, &mut conn_token.completion_token) }?;
 
         if completion_token.status.is_error() {
             Err(io::Error::from_raw_os_error(completion_token.status.as_usize()))
@@ -88,7 +89,7 @@ impl Tcp4 {
         }
     }
 
-    pub(crate) fn write(&self, buf: &[u8]) -> io::Result<usize> {
+    pub(crate) fn write(&self, buf: &[u8], timeout: Option<Duration>) -> io::Result<usize> {
         let evt = unsafe { self.create_evt() }?;
         let completion_token =
             tcp4::CompletionToken { event: evt.as_ptr(), status: Status::SUCCESS };
@@ -119,7 +120,7 @@ impl Tcp4 {
             return Err(io::Error::from_raw_os_error(r.as_usize()));
         }
 
-        self.wait_for_flag();
+        unsafe { self.wait_or_cancel(timeout, &mut token.completion_token) }?;
 
         if completion_token.status.is_error() {
             Err(io::Error::from_raw_os_error(completion_token.status.as_usize()))
@@ -128,7 +129,7 @@ impl Tcp4 {
         }
     }
 
-    pub(crate) fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+    pub(crate) fn read(&self, buf: &mut [u8], timeout: Option<Duration>) -> io::Result<usize> {
         let evt = unsafe { self.create_evt() }?;
         let completion_token =
             tcp4::CompletionToken { event: evt.as_ptr(), status: Status::SUCCESS };
@@ -158,7 +159,7 @@ impl Tcp4 {
             return Err(io::Error::from_raw_os_error(r.as_usize()));
         }
 
-        self.wait_for_flag();
+        unsafe { self.wait_or_cancel(timeout, &mut token.completion_token) }?;
 
         if completion_token.status.is_error() {
             Err(io::Error::from_raw_os_error(completion_token.status.as_usize()))
@@ -167,6 +168,50 @@ impl Tcp4 {
         }
     }
 
+    /// Wait for an event to finish. This is checked by an atomic boolean that is supposed to be set
+    /// to true in the event callback.
+    ///
+    /// Optionally, allow specifying a timeout.
+    ///
+    /// If a timeout is provided, the operation (specified by its `EFI_TCP4_COMPLETION_TOKEN`) is
+    /// canceled and Error of kind TimedOut is returned.
+    ///
+    /// # SAFETY
+    ///
+    /// Pointer to a valid `EFI_TCP4_COMPLETION_TOKEN`
+    unsafe fn wait_or_cancel(
+        &self,
+        timeout: Option<Duration>,
+        token: *mut tcp4::CompletionToken,
+    ) -> io::Result<()> {
+        if !self.wait_for_flag(timeout) {
+            let _ = unsafe { self.cancel(token) };
+            return Err(io::Error::new(io::ErrorKind::TimedOut, "Operation Timed out"));
+        }
+
+        Ok(())
+    }
+
+    /// Abort an asynchronous connection, listen, transmission or receive request.
+    ///
+    /// If token is NULL, then all pending tokens issued by EFI_TCP4_PROTOCOL.Connect(),
+    /// EFI_TCP4_PROTOCOL.Accept(), EFI_TCP4_PROTOCOL.Transmit() or EFI_TCP4_PROTOCOL.Receive() are
+    /// aborted.
+    ///
+    /// # SAFETY
+    ///
+    /// Pointer to a valid `EFI_TCP4_COMPLETION_TOKEN` or NULL
+    unsafe fn cancel(&self, token: *mut tcp4::CompletionToken) -> io::Result<()> {
+        let protocol = self.protocol.as_ptr();
+
+        let r = unsafe { ((*protocol).cancel)(protocol, token) };
+        if r.is_error() {
+            return Err(io::Error::from_raw_os_error(r.as_usize()));
+        } else {
+            Ok(())
+        }
+    }
+
     unsafe fn create_evt(&self) -> io::Result<helpers::OwnedEvent> {
         self.flag.store(false, Ordering::Relaxed);
         helpers::OwnedEvent::new(
@@ -177,10 +222,19 @@ impl Tcp4 {
         )
     }
 
-    fn wait_for_flag(&self) {
+    fn wait_for_flag(&self, timeout: Option<Duration>) -> bool {
+        let start = Instant::now();
+
         while !self.flag.load(Ordering::Relaxed) {
             let _ = self.poll();
+            if let Some(t) = timeout {
+                if Instant::now().duration_since(start) >= t {
+                    return false;
+                }
+            }
         }
+
+        true
     }
 
     fn poll(&self) -> io::Result<()> {
diff --git a/library/std/src/sys/process/unix/common.rs b/library/std/src/sys/process/unix/common.rs
index b6777b76668..6219be60caf 100644
--- a/library/std/src/sys/process/unix/common.rs
+++ b/library/std/src/sys/process/unix/common.rs
@@ -98,6 +98,7 @@ pub struct Command {
     #[cfg(target_os = "linux")]
     create_pidfd: bool,
     pgroup: Option<pid_t>,
+    setsid: bool,
 }
 
 // passed back to std::process with the pipes connected to the child, if any
@@ -185,6 +186,7 @@ impl Command {
             #[cfg(target_os = "linux")]
             create_pidfd: false,
             pgroup: None,
+            setsid: false,
         }
     }
 
@@ -220,6 +222,9 @@ impl Command {
             self.cwd(&OsStr::new("/"));
         }
     }
+    pub fn setsid(&mut self, setsid: bool) {
+        self.setsid = setsid;
+    }
 
     #[cfg(target_os = "linux")]
     pub fn create_pidfd(&mut self, val: bool) {
@@ -298,6 +303,10 @@ impl Command {
     pub fn get_chroot(&self) -> Option<&CStr> {
         self.chroot.as_deref()
     }
+    #[allow(dead_code)]
+    pub fn get_setsid(&self) -> bool {
+        self.setsid
+    }
 
     pub fn get_closures(&mut self) -> &mut Vec<Box<dyn FnMut() -> io::Result<()> + Send + Sync>> {
         &mut self.closures
diff --git a/library/std/src/sys/process/unix/common/tests.rs b/library/std/src/sys/process/unix/common/tests.rs
index e5c8dd6e341..5f71bf051f8 100644
--- a/library/std/src/sys/process/unix/common/tests.rs
+++ b/library/std/src/sys/process/unix/common/tests.rs
@@ -135,6 +135,64 @@ fn test_process_group_no_posix_spawn() {
 }
 
 #[test]
+#[cfg_attr(
+    any(
+        // See test_process_mask
+        target_os = "macos",
+        target_arch = "arm",
+        target_arch = "aarch64",
+        target_arch = "riscv64",
+    ),
+    ignore
+)]
+fn test_setsid_posix_spawn() {
+    // Spawn a cat subprocess that's just going to hang since there is no I/O.
+    let mut cmd = Command::new(OsStr::new("cat"));
+    cmd.setsid(true);
+    cmd.stdin(Stdio::MakePipe);
+    cmd.stdout(Stdio::MakePipe);
+    let (mut cat, _pipes) = t!(cmd.spawn(Stdio::Null, true));
+
+    unsafe {
+        // Setsid will create a new session and process group, so check that
+        // we can kill the process group, which means there *is* one.
+        t!(cvt(libc::kill(-(cat.id() as libc::pid_t), libc::SIGINT)));
+
+        t!(cat.wait());
+    }
+}
+
+#[test]
+#[cfg_attr(
+    any(
+        // See test_process_mask
+        target_os = "macos",
+        target_arch = "arm",
+        target_arch = "aarch64",
+        target_arch = "riscv64",
+    ),
+    ignore
+)]
+fn test_setsid_no_posix_spawn() {
+    let mut cmd = Command::new(OsStr::new("cat"));
+    cmd.setsid(true);
+    cmd.stdin(Stdio::MakePipe);
+    cmd.stdout(Stdio::MakePipe);
+
+    unsafe {
+        // Same as above, create hang-y cat. This time, force using the non-posix_spawn path.
+        cmd.pre_exec(Box::new(|| Ok(()))); // pre_exec forces fork + exec rather than posix spawn.
+        let (mut cat, _pipes) = t!(cmd.spawn(Stdio::Null, true));
+
+        // Setsid will create a new session and process group, so check that
+        // we can kill the process group, which means there *is* one.
+        t!(cvt(libc::kill(-(cat.id() as libc::pid_t), libc::SIGINT)));
+
+        t!(cat.wait());
+    }
+}
+
+#[test]
 fn test_program_kind() {
     let vectors = &[
         ("foo", ProgramKind::PathLookup),
diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs
index bbd03e2b0c4..5d13d6da185 100644
--- a/library/std/src/sys/process/unix/unix.rs
+++ b/library/std/src/sys/process/unix/unix.rs
@@ -340,6 +340,10 @@ impl Command {
             cvt(libc::setpgid(0, pgroup))?;
         }
 
+        if self.get_setsid() {
+            cvt(libc::setsid())?;
+        }
+
         // emscripten has no signal support.
         #[cfg(not(target_os = "emscripten"))]
         {
@@ -741,6 +745,16 @@ impl Command {
                 flags |= libc::POSIX_SPAWN_SETSIGDEF;
             }
 
+            if self.get_setsid() {
+                cfg_if::cfg_if! {
+                    if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
+                        flags |= libc::POSIX_SPAWN_SETSID;
+                    } else {
+                        return Ok(None);
+                    }
+                }
+            }
+
             cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?;
 
             // Make sure we synchronize access to the global `environ` resource