ctoolbox/cli/
routing.rs

1use anyhow::{Result, anyhow};
2use clap::{Parser, Subcommand};
3use futures::StreamExt;
4use std::path::PathBuf;
5
6use crate::cli::base_conversion::{BaseArgs, run_base_convert};
7use crate::cli::{StringInput, ToolResult, generate_help_bytes};
8use crate::utilities::{
9    fork, get_this_executable, upgrade_in_place,
10    wait_for_ctoolbox_exit_and_clean_up,
11};
12
13/// Return true if this is a command that can be run without booting.
14pub fn is_lightweight_command(command: String) -> bool {
15    matches!(
16        String::as_str(&command),
17        "base2base"
18            | "hex2dec"
19            | "dec2hex"
20            | "hexfmt"
21            | "gdb_instructions_generate"
22            | "help"
23    )
24}
25
26#[derive(Subcommand, Debug)]
27pub enum Command {
28    /// Wait for the provided PID to shutdown, then clean up
29    #[command(name = "waitshutdown")]
30    WaitShutdown {
31        /// Process ID to wait for
32        #[arg(long)]
33        pid: u32,
34    },
35    /// Wait for the provided PID to shutdown, then start ctoolbox with the
36    /// given port
37    #[command(name = "waitrestart")]
38    WaitRestart {
39        /// Process ID to wait for
40        #[arg(long)]
41        pid: u32,
42        /// Port to pass to the new ctoolbox instance
43        #[arg(long)]
44        port: u16,
45    },
46    /// Wait for the provided PID to shutdown, then upgrade the ctoolbox
47    /// instance in place, then restart it. (Will need to copy the executable
48    /// before upgrading it probably because Windows locks running executables.)
49    #[command(name = "waitupgrade")]
50    WaitUpgrade {
51        /// Process ID to wait for
52        #[arg(long)]
53        pid: u32,
54        /// Path to the temporary file holding the new ctoolbox executable
55        #[arg(long)]
56        temp_path: PathBuf,
57        /// Path to the installed ctoolbox executable
58        #[arg(long)]
59        target_path: PathBuf,
60        /// Port to pass to the new ctoolbox instance
61        #[arg(long)]
62        port: u16,
63    },
64    /// Convert from one base to another (for base <= 36)
65    #[command(name = "base2base")]
66    Base2Base {
67        /// All positional arguments for custom parsing
68        #[arg(required = true)]
69        args: Vec<String>,
70        #[command(flatten)]
71        base_args: BaseArgs,
72    },
73    /// Convert from hexadecimal to decimal
74    #[command(name = "hex2dec")]
75    Hex2Dec {
76        #[command(flatten)]
77        string_input: StringInput,
78        #[command(flatten)]
79        base_args: BaseArgs,
80    },
81    /// Convert from decimal to hexadecimal
82    #[command(name = "dec2hex")]
83    Dec2Hex {
84        #[command(flatten)]
85        string_input: StringInput,
86        #[command(flatten)]
87        base_args: BaseArgs,
88    },
89    /// Reformat hexdumps
90    #[command(name = "hexfmt")]
91    Hexfmt {
92        #[command(flatten)]
93        string_input: StringInput,
94        #[command(flatten)]
95        base_args: BaseArgs,
96    },
97    /// Generate GDB instructions from symbols
98    #[command(name = "gdb_instructions_generate")]
99    GdbInstructionsGenerate {},
100    /// Show help (lightweight)
101    Help,
102    /// Unimplemented, just an example
103    ShowNode {
104        /// Example parameter
105        #[arg(short, long)]
106        id: i128,
107    },
108}
109
110// ---------------------------
111// Command Execution
112// ---------------------------
113
114pub async fn run_lightweight_command(cmd: &Command) -> Result<ToolResult> {
115    match cmd {
116        Command::WaitShutdown { pid } => {
117            wait_for_ctoolbox_exit_and_clean_up(*pid);
118            Ok(ToolResult::immediate_ok(Vec::new()))
119        }
120        Command::WaitRestart { pid, port } => {
121            wait_for_ctoolbox_exit_and_clean_up(*pid);
122            fork(
123                &get_this_executable(),
124                vec!["--ctoolbox-ipc-port", &port.to_string().as_str()],
125            );
126            Ok(ToolResult::immediate_ok(Vec::new()))
127        }
128        Command::WaitUpgrade {
129            pid,
130            temp_path,
131            target_path,
132            port,
133        } => {
134            wait_for_ctoolbox_exit_and_clean_up(*pid);
135            upgrade_in_place(temp_path, target_path)?;
136            fork(
137                target_path,
138                vec!["--ctoolbox-ipc-port", &port.to_string().as_str()],
139            );
140            Ok(ToolResult::immediate_ok(Vec::new()))
141        }
142        Command::Base2Base { args, base_args } => {
143            let (input, from_base, to_base) = match args.len() {
144                1 => (&args[0], None, None),
145                3 => {
146                    let from_base = Some(args[0].parse::<u8>()?);
147                    let to_base = Some(args[1].parse::<u8>()?);
148                    let input = &args[2];
149                    (input, from_base, to_base)
150                }
151                _ => {
152                    eprintln!(
153                        "Invalid arguments! Usage: base2base [FROM_BASE TO_BASE INPUT] or [INPUT]"
154                    );
155                    std::process::exit(1);
156                }
157            };
158
159            run_base_convert(
160                &from_base,
161                &to_base,
162                &StringInput {
163                    input: input.clone(),
164                },
165                base_args,
166            )
167        }
168        Command::Hex2Dec {
169            string_input,
170            base_args,
171        } => run_base_convert(&Some(16), &Some(10), string_input, base_args),
172        Command::Dec2Hex {
173            string_input,
174            base_args,
175        } => run_base_convert(&Some(10), &Some(16), string_input, base_args),
176        Command::Hexfmt {
177            string_input,
178            base_args,
179        } => run_base_convert(&Some(16), &Some(16), string_input, base_args),
180        Command::GdbInstructionsGenerate {} => {
181            // FIXME: Use a streaming ToolResult here
182            crate::utilities::debug_tools::generate_gdb_instructions_streaming(
183            )?;
184            Ok(ToolResult::immediate_ok(Vec::new()))
185        }
186        Command::Help => Ok(ToolResult::immediate_ok(generate_help_bytes())),
187        Command::ShowNode { .. } => Err(anyhow!(
188            "ShowNode should not be run as lightweight command; it needs the full environment"
189        )),
190    }
191}