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}