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 let argv_ptr: Vec<*const i8> = argv.iter().map(|&e| e.as_ptr() as *const i8).collect();
58 let mut spgs = syscalls::SpawnArgs {
59 argc: argc as u64,
60 argv: argv_ptr.as_ptr(),
61 process_id: &mut process_id,
62 inherited_fds: inherited_fds.as_ptr(),
63 };
64 syscalls::spawn(index, source, 0, 0, &mut spgs).map_err(IpcError::CkbSysError)?;
65 Ok((r1.into(), w2.into()))
66}
67/// Spawns a new server process using the provided code hash and hash type. This function is similar
68/// to `spawn_server`, but it uses a specific cell identified by the `code_hash` and `hash_type` to
69/// spawn the new process. The function returns the read and write file descriptors for the parent
70/// process to communicate with the child process.
71///
72/// # Arguments
73///
74/// * `code_hash` - A byte slice representing the code hash of the cell to spawn.
75/// * `hash_type` - The hash type of the cell (e.g., `ScriptHashType::Type`).
76/// * `argv` - A slice of C strings representing the arguments to pass to the new process.
77///
78/// # Returns
79///
80/// A `Result` containing a tuple of two `Pipe` representing the read and write file descriptors
81/// for the parent process, or an `IpcError` if an error occurs.
82///
83/// # Errors
84///
85/// This function returns an `IpcError` if any of the following syscalls fail:
86/// * `pipe` - If creating a pipe fails.
87/// * `spawn_cell` - If spawning the new process using the cell fails.
88///
89/// # Example
90///
91/// ```rust,ignore
92/// use ckb_script_ipc_common::spawn::spawn_cell_server;
93///
94/// let (read_pipe, write_pipe) = spawn_cell_server(
95/// code_hash,
96/// hash_type,
97/// &[CString::new("demo").unwrap().as_ref()],
98/// ).expect("Failed to spawn cell server");
99/// ```
100pub fn spawn_cell_server(
101 code_hash: &[u8],
102 hash_type: ScriptHashType,
103 argv: &[&CStr],
104) -> Result<(Pipe, Pipe), IpcError> {
105 let (r1, w1) = pipe().map_err(IpcError::CkbSysError)?;
106 let (r2, w2) = pipe().map_err(IpcError::CkbSysError)?;
107 let inherited_fds = &[r2, w1];
108
109 spawn_cell(code_hash, hash_type, argv, inherited_fds).map_err(IpcError::CkbSysError)?;
110 Ok((r1.into(), w2.into()))
111}
112/// Runs the server with the provided service implementation. This function listens for incoming
113/// requests, processes them using the provided service, and sends back the responses. It uses
114/// the inherited file descriptors for communication.
115///
116/// # Arguments
117///
118/// * `serve` - A mutable reference to the service implementation that handles the requests and
119/// generates the responses. The service must implement the `Serve` trait with the appropriate
120/// request and response types.
121///
122/// # Type Parameters
123///
124/// * `Req` - The type of the request messages. It must implement `Serialize` and `Deserialize`.
125/// * `Resp` - The type of the response messages. It must implement `Serialize` and `Deserialize`.
126/// * `S` - The type of the service implementation. It must implement the `Serve` trait with
127/// `Req` as the request type and `Resp` as the response type.
128///
129/// # Returns
130///
131/// A `Result` indicating the success or failure of the server execution. If the server runs
132/// successfully, it never returns. If an error occurs, it returns an `IpcError`.
133///
134/// # Errors
135///
136/// This function returns an `IpcError` if any of the following conditions occur:
137/// * The inherited file descriptors are not exactly two.
138/// * An error occurs during the execution of the channel.
139pub fn run_server<Req, Resp, S>(mut serve: S) -> Result<(), IpcError>
140where
141 Req: Serialize + for<'de> Deserialize<'de>,
142 Resp: Serialize + for<'de> Deserialize<'de>,
143 S: Serve<Req = Req, Resp = Resp>,
144{
145 let fds = inherited_fds();
146 assert_eq!(fds.len(), 2);
147
148 let reader: Pipe = fds[0].into();
149 let writer: Pipe = fds[1].into();
150 let channel = Channel::new(reader, writer);
151 channel.execute(&mut serve)
152}