folk_runtime_pipe/
spawn.rs1use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd};
4
5use anyhow::Result;
6use tokio::net::UnixStream;
7use tokio::process::Command;
8use tracing::debug;
9
10use crate::socket::create_socketpair;
11
12pub const TASK_FD: libc::c_int = 3;
14pub const CONTROL_FD: libc::c_int = 4;
15
16pub struct SpawnedWorker {
18 pub child: tokio::process::Child,
19 pub task_master: UnixStream,
20 pub control_master: UnixStream,
21}
22
23#[allow(unsafe_code)]
36pub fn spawn_worker(php: &str, script: &str) -> Result<SpawnedWorker> {
37 spawn_worker_with_runtime(php, script, "pipe")
38}
39
40#[allow(unsafe_code)]
42pub fn spawn_worker_with_runtime(php: &str, script: &str, runtime: &str) -> Result<SpawnedWorker> {
43 let (task_master_fd, task_child_fd) = create_socketpair()?;
44 let (ctrl_master_fd, ctrl_child_fd) = create_socketpair()?;
45
46 let task_child_raw = task_child_fd.as_raw_fd();
47 let ctrl_child_raw = ctrl_child_fd.as_raw_fd();
48
49 let mut cmd = Command::new(php);
50 cmd.arg(script)
51 .env("FOLK_RUNTIME", runtime)
52 .env("FOLK_TASK_FD", TASK_FD.to_string())
53 .env("FOLK_CONTROL_FD", CONTROL_FD.to_string())
54 .stdin(std::process::Stdio::null())
55 .stdout(std::process::Stdio::piped())
56 .stderr(std::process::Stdio::inherit());
57
58 unsafe {
61 cmd.pre_exec(move || {
62 if libc::dup2(task_child_raw, TASK_FD) < 0 {
63 return Err(std::io::Error::last_os_error());
64 }
65 if libc::dup2(ctrl_child_raw, CONTROL_FD) < 0 {
66 return Err(std::io::Error::last_os_error());
67 }
68
69 if task_child_raw != TASK_FD {
70 libc::close(task_child_raw);
71 }
72 if ctrl_child_raw != CONTROL_FD {
73 libc::close(ctrl_child_raw);
74 }
75
76 libc::fcntl(TASK_FD, libc::F_SETFD, 0);
77 libc::fcntl(CONTROL_FD, libc::F_SETFD, 0);
78
79 Ok(())
80 });
81 }
82
83 let child = cmd.spawn()?;
84
85 drop(task_child_fd);
86 drop(ctrl_child_fd);
87
88 let task_master = {
89 let std_sock =
90 unsafe { std::os::unix::net::UnixStream::from_raw_fd(task_master_fd.into_raw_fd()) };
91 std_sock.set_nonblocking(true)?;
92 UnixStream::from_std(std_sock)?
93 };
94
95 let control_master = {
96 let std_sock =
97 unsafe { std::os::unix::net::UnixStream::from_raw_fd(ctrl_master_fd.into_raw_fd()) };
98 std_sock.set_nonblocking(true)?;
99 UnixStream::from_std(std_sock)?
100 };
101
102 debug!(pid = ?child.id(), "worker spawned");
103
104 Ok(SpawnedWorker {
105 child,
106 task_master,
107 control_master,
108 })
109}