Skip to main content

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}