ssh_cli/vps/exec_ops/
elevation.rs1#![forbid(unsafe_code)]
4#![allow(unused_imports)]
5use super::*;
6
7pub async fn run_sudo_exec(
11 selection: HostSelection,
12 command: &str,
13 config_override: Option<PathBuf>,
14 format: OutputFormat,
15 json: bool,
16 mut opts: ExecOptions,
17) -> Result<()> {
18 if crate::signals::should_stop() {
19 return Err(cancelled_err());
20 }
21 if selection.is_batch() {
22 return run_exec_all(
23 &selection,
24 command,
25 config_override,
26 format,
27 json,
28 opts,
29 ExecKind::Sudo,
30 )
31 .await;
32 }
33 let vps_name = expect_single(selection)?;
34 let path = resolve_config_path(config_override.as_deref())?;
35 let mut file = load(&path)?;
36 let mut vps = file
37 .hosts
38 .remove(&vps_name)
39 .ok_or(SshCliError::VpsNotFound(vps_name))?;
40
41 apply_overrides(&mut vps, opts.take_auth_overrides());
42 if opts.disable_sudo || vps.disable_sudo {
43 return Err(SshCliError::SudoDisabled.into());
44 }
45 let cmd = append_description(command, opts.description.as_deref());
46 validate_command_length(&cmd, vps.max_command_chars.wire())?;
47 for s in &opts.steps {
48 validate_command_length(s.as_str(), vps.max_command_chars.wire())?;
49 }
50 let cfg = build_connection_config(&vps, Some(&path), opts.replace_host_key);
51 let client: Box<dyn SshClientTrait> = <SshClient as SshClientTrait>::connect(cfg).await?;
52 run_sudo_exec_with_client_steps(&vps, &cmd, &opts.steps, client, format, json).await
53}
54
55pub async fn run_sudo_exec_with_client(
57 vps: &VpsRecord,
58 command: &str,
59 client: Box<dyn SshClientTrait>,
60 format: OutputFormat,
61 json: bool,
62) -> Result<()> {
63 run_sudo_exec_with_client_steps(vps, command, &[], client, format, json).await
64}
65
66pub async fn run_sudo_exec_with_client_steps(
72 vps: &VpsRecord,
73 command: &str,
74 steps: &[crate::domain::RemoteCommand],
75 client: Box<dyn SshClientTrait>,
76 format: OutputFormat,
77 json: bool,
78) -> Result<()> {
79 if crate::signals::should_stop() {
80 return Err(cancelled_err());
81 }
82 if vps.disable_sudo {
83 return Err(SshCliError::SudoDisabled.into());
84 }
85 let prepared = step_labels(command, steps)
86 .iter()
87 .map(|c| PreparedStep::packed(c, pack_sudo(c, vps.sudo_password.as_ref())))
88 .collect();
89 run_prepared_steps(vps, prepared, client, format, json).await
90}
91
92pub async fn run_su_exec(
96 selection: HostSelection,
97 command: &str,
98 config_override: Option<PathBuf>,
99 format: OutputFormat,
100 json: bool,
101 mut opts: ExecOptions,
102) -> Result<()> {
103 if crate::signals::should_stop() {
104 return Err(cancelled_err());
105 }
106 if selection.is_batch() {
107 return run_exec_all(
108 &selection,
109 command,
110 config_override,
111 format,
112 json,
113 opts,
114 ExecKind::Su,
115 )
116 .await;
117 }
118 let vps_name = expect_single(selection)?;
119 let path = resolve_config_path(config_override.as_deref())?;
120 let mut file = load(&path)?;
121 let mut vps = file
122 .hosts
123 .remove(&vps_name)
124 .ok_or(SshCliError::VpsNotFound(vps_name))?;
125
126 apply_overrides(&mut vps, opts.take_auth_overrides());
127 if opts.disable_sudo || vps.disable_sudo {
128 return Err(SshCliError::SudoDisabled.into());
129 }
130 let su_password = vps
132 .su_password
133 .take()
134 .ok_or(SshCliError::SuPasswordMissing)?;
135 let cmd = append_description(command, opts.description.as_deref());
136 validate_command_length(&cmd, vps.max_command_chars.wire())?;
137 for s in &opts.steps {
138 validate_command_length(s.as_str(), vps.max_command_chars.wire())?;
139 }
140 let prepared = step_labels(&cmd, &opts.steps)
143 .iter()
144 .map(|c| PreparedStep::packed(c, pack_su(c, &su_password)))
145 .collect();
146 let cfg = build_connection_config(&vps, Some(&path), opts.replace_host_key);
147 let client: Box<dyn SshClientTrait> = <SshClient as SshClientTrait>::connect(cfg).await?;
148 run_prepared_steps(&vps, prepared, client, format, json).await
149}