From 3232f4a5ac36117342bb85b0a9a5ff2df1088c4d Mon Sep 17 00:00:00 2001 From: Without Boats Date: Sun, 11 Oct 2015 13:33:48 -0700 Subject: [PATCH 1/2] Add a method to set the controlling tty of child processes to unix::CommandExt. --- src/libstd/sys/unix/ext/process.rs | 30 +++++++++++++++++++++++++++++- src/libstd/sys/unix/process.rs | 18 ++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/libstd/sys/unix/ext/process.rs b/src/libstd/sys/unix/ext/process.rs index dcfa376c81e3b..ea2a29a480f3e 100644 --- a/src/libstd/sys/unix/ext/process.rs +++ b/src/libstd/sys/unix/ext/process.rs @@ -41,9 +41,26 @@ pub trait CommandExt { /// process exit. Moreover, a daemon should not have a controlling terminal. /// To achieve this, a session leader (the child) must spawn another process /// (the daemon) in the same session. + /// + /// If a new controlling terminal has been assigned to the child, calling + /// this method is redundant; a new session will be created for the child + /// process regardless of calls to this method. #[unstable(feature = "process_session_leader", reason = "recently added", issue = "27811")] fn session_leader(&mut self, on: bool) -> &mut process::Command; + + /// Assign the file handle to be the controlling terminal for the child + /// process. Because the controlling terminal can only be changed for a + /// session lead, this also creates a new session for the child process. + /// + /// Unlike login_tty(3), this does not automatically set the stdio handles + /// to the same handle as the controlling terminal, to do that you must set + /// them using `Stdio::from_raw_fd()`. + /// + /// For more information, see login_tty(3) and tty_ioctl(4), specifically + /// TIOCSCTTY. + #[unstable(feature = "process_tty", reason = "recently added")] + fn tty(&mut self, fd: &T) -> &mut process::Command; } #[stable(feature = "rust1", since = "1.0.0")] @@ -59,7 +76,18 @@ impl CommandExt for process::Command { } fn session_leader(&mut self, on: bool) -> &mut process::Command { - self.as_inner_mut().session_leader = on; + if self.as_inner().tty.is_none() { + self.as_inner_mut().session_leader = on; + } + self + } + + fn tty(&mut self, fd: &T) -> &mut process::Command { + { + let inner = self.as_inner_mut(); + inner.tty = Some(fd.as_raw_fd()); + inner.session_leader = true; + } self } } diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index 7f50e75f6fc64..f8f0e59b3b7ca 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -37,6 +37,7 @@ pub struct Command { pub uid: Option, pub gid: Option, pub session_leader: bool, + pub tty: Option, } impl Command { @@ -49,6 +50,7 @@ impl Command { uid: None, gid: None, session_leader: false, + tty: None, } } @@ -307,7 +309,23 @@ impl Process { // process leader already. We just forked so it shouldn't return // error, but ignore it anyway. let _ = libc::setsid(); + + // Associate the tty file descriptor with the controlling terminal, + // then close it unless it is an stdio handle. Errors returned by + // close are ignored. + if let Some(tty) = cfg.tty { + + const TIOCSCTTY: u64 = 0x540E; + + if cvt_r(|| libc::funcs::bsd44::ioctl(tty, TIOCSCTTY)).is_err() { + fail(&mut output); + } else if tty > 2 { + let _ = libc::close(tty); + } + + } } + if !dirp.is_null() && libc::chdir(dirp) == -1 { fail(&mut output); } From 65fbca461cfaaabdaed3d34e5ed004c0bd6b0233 Mon Sep 17 00:00:00 2001 From: Without Boats Date: Mon, 12 Oct 2015 14:50:21 -0700 Subject: [PATCH 2/2] Modify the API so that session_leader takes an optional file handle, which will be set to be the controlling terminal. --- src/libstd/sys/unix/ext/process.rs | 38 +++++++----------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/src/libstd/sys/unix/ext/process.rs b/src/libstd/sys/unix/ext/process.rs index ea2a29a480f3e..dd140d708e4e5 100644 --- a/src/libstd/sys/unix/ext/process.rs +++ b/src/libstd/sys/unix/ext/process.rs @@ -36,31 +36,18 @@ pub trait CommandExt { /// that the child is the leader of a new process group. The parent process /// remains the child reaper of the new process. /// + /// If the tty argument is not `None`, this file will be the controlling + /// terminal for the new session (cf. `login_tty(3)` and tty_ioctl(4), + /// specifically the section on `TIOCSCTTY`). Note that this does not set + /// that file to be the stdio handles for the child process, to do that you + /// must set them directly with the `stdin()` et al methods. + /// /// This is not enough to create a daemon process. The *init* process should /// be the child reaper of a daemon. This can be achieved if the parent /// process exit. Moreover, a daemon should not have a controlling terminal. - /// To achieve this, a session leader (the child) must spawn another process - /// (the daemon) in the same session. - /// - /// If a new controlling terminal has been assigned to the child, calling - /// this method is redundant; a new session will be created for the child - /// process regardless of calls to this method. #[unstable(feature = "process_session_leader", reason = "recently added", issue = "27811")] - fn session_leader(&mut self, on: bool) -> &mut process::Command; - - /// Assign the file handle to be the controlling terminal for the child - /// process. Because the controlling terminal can only be changed for a - /// session lead, this also creates a new session for the child process. - /// - /// Unlike login_tty(3), this does not automatically set the stdio handles - /// to the same handle as the controlling terminal, to do that you must set - /// them using `Stdio::from_raw_fd()`. - /// - /// For more information, see login_tty(3) and tty_ioctl(4), specifically - /// TIOCSCTTY. - #[unstable(feature = "process_tty", reason = "recently added")] - fn tty(&mut self, fd: &T) -> &mut process::Command; + fn session_leader(&mut self, tty: Option<&T>) -> &mut process::Command; } #[stable(feature = "rust1", since = "1.0.0")] @@ -75,18 +62,11 @@ impl CommandExt for process::Command { self } - fn session_leader(&mut self, on: bool) -> &mut process::Command { - if self.as_inner().tty.is_none() { - self.as_inner_mut().session_leader = on; - } - self - } - - fn tty(&mut self, fd: &T) -> &mut process::Command { + fn session_leader(&mut self, tty: Option<&T>) -> &mut process::Command { { let inner = self.as_inner_mut(); - inner.tty = Some(fd.as_raw_fd()); inner.session_leader = true; + inner.tty = tty.map(T::as_raw_fd); } self }