1use std::io;
6
7#[cfg(unix)]
9pub fn clone_shell(tty_path: &str) -> io::Result<u32> {
10 use std::ffi::CString;
11 use std::os::unix::io::RawFd;
12
13 let tty_c = CString::new(tty_path)
14 .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid tty path"))?;
15
16 let ttyfd: RawFd = unsafe { libc::open(tty_c.as_ptr(), libc::O_RDWR | libc::O_NOCTTY) };
17
18 if ttyfd < 0 {
19 return Err(io::Error::last_os_error());
20 }
21
22 let pid = unsafe { libc::fork() };
23
24 match pid {
25 -1 => {
26 unsafe { libc::close(ttyfd) };
27 Err(io::Error::last_os_error())
28 }
29 0 => {
30 unsafe {
31 if libc::setsid() == -1 {
32 eprintln!(
33 "clone: failed to create new session: {}",
34 io::Error::last_os_error()
35 );
36 }
37
38 libc::dup2(ttyfd, 0);
39 libc::dup2(ttyfd, 1);
40 libc::dup2(ttyfd, 2);
41
42 if ttyfd > 2 {
43 libc::close(ttyfd);
44 }
45
46 let cttyfd = libc::open(tty_c.as_ptr(), libc::O_RDWR);
47 if cttyfd >= 0 {
48 #[cfg(any(target_os = "linux", target_os = "macos"))]
49 {
50 libc::ioctl(cttyfd, libc::TIOCSCTTY as libc::c_ulong, 0);
51 }
52 libc::close(cttyfd);
53 }
54 }
55
56 Ok(0)
57 }
58 child_pid => {
59 unsafe { libc::close(ttyfd) };
60 Ok(child_pid as u32)
61 }
62 }
63}
64
65#[cfg(not(unix))]
66pub fn clone_shell(_tty_path: &str) -> io::Result<u32> {
67 Err(io::Error::new(
68 io::ErrorKind::Unsupported,
69 "clone not supported",
70 ))
71}
72
73pub fn builtin_clone(args: &[&str]) -> (i32, String, Option<u32>) {
75 if args.is_empty() {
76 return (1, "clone: terminal required\n".to_string(), None);
77 }
78
79 match clone_shell(args[0]) {
80 Ok(pid) => (0, String::new(), Some(pid)),
81 Err(e) => (1, format!("clone: {}: {}\n", args[0], e), None),
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn test_builtin_clone_no_args() {
91 let (status, _, _) = builtin_clone(&[]);
92 assert_eq!(status, 1);
93 }
94
95 #[test]
96 fn test_builtin_clone_invalid_tty() {
97 let (status, output, _) = builtin_clone(&["/nonexistent/tty"]);
98 assert_eq!(status, 1);
99 assert!(output.contains("clone"));
100 }
101}