ckb_script_ipc_common/spawn.rs
1use crate::{channel::Channel, error::IpcError, ipc::Serve, pipe::Pipe};
2use alloc::vec::Vec;
3use ckb_std::{
4 ckb_constants::Source,
5 ckb_types::core::ScriptHashType,
6 high_level::{inherited_fds, spawn_cell},
7 syscalls::{self, pipe},
8};
9use core::ffi::CStr;
10use serde::{Deserialize, Serialize};
11/// Spawns a new server process and sets up pipes.
12///
13/// This function creates two pairs of pipes for communication between the parent and child processes.
14/// It then spawns a new process using the specified index and source, passing the provided arguments
15/// to the new process. The function returns the read and write file descriptors for the parent process
16/// to communicate with the child process.
17///
18/// # Arguments
19///
20/// * `index` - The index of the cell to spawn.
21/// * `source` - The source of the cell (e.g., `Source::CellDep`).
22/// * `argv` - A slice of C strings representing the arguments to pass to the new process.
23///
24/// # Returns
25///
26/// A `Result` containing a tuple of two `Pipe` representing the read and write file descriptors
27/// for the parent process, or an `IpcError` if an error occurs.
28///
29/// # Errors
30///
31/// This function returns an `IpcError` if any of the following syscalls fail:
32/// * `pipe` - If creating a pipe fails.
33/// * `spawn` - If spawning the new process fails.
34///
35/// # Example
36///
37/// ```rust,ignore
38/// use ckb_script_ipc_common::spawn::spawn_server;
39///
40/// let (read_pipe, write_pipe) = spawn_server(
41/// 0,
42/// Source::CellDep,
43/// &[CString::new("demo").unwrap().as_ref()],
44/// ).expect("Failed to spawn server");
45/// ```
46pub fn spawn_server(
47 index: usize,
48 source: Source,
49 argv: &[&CStr],
50) -> Result<(Pipe, Pipe), IpcError> {
51 let (r1, w1) = pipe().map_err(IpcError::CkbSysError)?;
52 let (r2, w2) = pipe().map_err(IpcError::CkbSysError)?;
53 let inherited_fds = &[r2, w1];
54
55 let argc = argv.len();
56 let mut process_id: u64 = 0;
57 // Convert CStr pointers to raw pointers for syscall
58 // Note: c_char is platform-specific (i8 on most platforms, u8 on some)
59 // The cast is necessary for FFI compatibility but triggers clippy warning
60 #[allow(clippy::unnecessary_cast)]
61 let argv_ptr: Vec<*const i8> = argv.iter().map(|&e| e.as_ptr() as *const i8).collect();
62 let mut spgs = syscalls::SpawnArgs {
63 argc: argc as u64,
64 argv: argv_ptr.as_ptr(),
65 process_id: &mut process_id,
66 inherited_fds: inherited_fds.as_ptr(),
67 };
68 syscalls::spawn(index, source, 0, 0, &mut spgs).map_err(IpcError::CkbSysError)?;
69 Ok((r1.into(), w2.into()))
70}
71/// Spawns a new server process using the provided code hash and hash type. This function is similar
72/// to `spawn_server`, but it uses a specific cell identified by the `code_hash` and `hash_type` to
73/// spawn the new process. The function returns the read and write file descriptors for the parent
74/// process to communicate with the child process.
75///
76/// # Arguments
77///
78/// * `code_hash` - A byte slice representing the code hash of the cell to spawn.
79/// * `hash_type` - The hash type of the cell (e.g., `ScriptHashType::Type`).
80/// * `argv` - A slice of C strings representing the arguments to pass to the new process.
81///
82/// # Returns
83///
84/// A `Result` containing a tuple of two `Pipe` representing the read and write file descriptors
85/// for the parent process, or an `IpcError` if an error occurs.
86///
87/// # Errors
88///
89/// This function returns an `IpcError` if any of the following syscalls fail:
90/// * `pipe` - If creating a pipe fails.
91/// * `spawn_cell` - If spawning the new process using the cell fails.
92///
93/// # Example
94///
95/// ```rust,ignore
96/// use ckb_script_ipc_common::spawn::spawn_cell_server;
97///
98/// let (read_pipe, write_pipe) = spawn_cell_server(
99/// code_hash,
100/// hash_type,
101/// &[CString::new("demo").unwrap().as_ref()],
102/// ).expect("Failed to spawn cell server");
103/// ```
104pub fn spawn_cell_server(
105 code_hash: &[u8],
106 hash_type: ScriptHashType,
107 argv: &[&CStr],
108) -> Result<(Pipe, Pipe), IpcError> {
109 let (r1, w1) = pipe().map_err(IpcError::CkbSysError)?;
110 let (r2, w2) = pipe().map_err(IpcError::CkbSysError)?;
111 let inherited_fds = &[r2, w1];
112
113 spawn_cell(code_hash, hash_type, argv, inherited_fds).map_err(IpcError::CkbSysError)?;
114 Ok((r1.into(), w2.into()))
115}
116/// Runs the server with the provided service implementation. This function listens for incoming
117/// requests, processes them using the provided service, and sends back the responses. It uses
118/// the inherited file descriptors for communication.
119///
120/// # Arguments
121///
122/// * `serve` - A mutable reference to the service implementation that handles the requests and
123/// generates the responses. The service must implement the `Serve` trait with the appropriate
124/// request and response types.
125///
126/// # Type Parameters
127///
128/// * `Req` - The type of the request messages. It must implement `Serialize` and `Deserialize`.
129/// * `Resp` - The type of the response messages. It must implement `Serialize` and `Deserialize`.
130/// * `S` - The type of the service implementation. It must implement the `Serve` trait with
131/// `Req` as the request type and `Resp` as the response type.
132///
133/// # Returns
134///
135/// A `Result` indicating the success or failure of the server execution. If the server runs
136/// successfully, it never returns. If an error occurs, it returns an `IpcError`.
137///
138/// # Errors
139///
140/// This function returns an `IpcError` if any of the following conditions occur:
141/// * The inherited file descriptors are not exactly two.
142/// * An error occurs during the execution of the channel.
143pub fn run_server<Req, Resp, S>(mut serve: S) -> Result<(), IpcError>
144where
145 Req: Serialize + for<'de> Deserialize<'de>,
146 Resp: Serialize + for<'de> Deserialize<'de>,
147 S: Serve<Req = Req, Resp = Resp>,
148{
149 let fds = inherited_fds();
150 assert_eq!(fds.len(), 2);
151
152 let reader: Pipe = fds[0].into();
153 let writer: Pipe = fds[1].into();
154 let channel = Channel::new(reader, writer);
155 channel.execute(&mut serve)
156}