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 target = crate::json_wire::ExecTarget::new(vps_name.clone(), opts.target_source);
35 let path = resolve_config_path(config_override.as_deref())?;
36 let mut file = load(&path)?;
37 let mut vps = file
38 .hosts
39 .remove(&vps_name)
40 .ok_or(SshCliError::VpsNotFound(vps_name))?;
41 crate::json_wire::set_resolved_target(&target);
45
46 apply_overrides(&mut vps, opts.take_auth_overrides());
47 if opts.disable_sudo || vps.disable_sudo {
48 return Err(SshCliError::SudoDisabled.into());
49 }
50 let cmd = append_description(command, opts.description.as_deref());
51 validate_command_length(&cmd, vps.max_command_chars.wire())?;
52 for s in &opts.steps {
53 validate_command_length(s.as_str(), vps.max_command_chars.wire())?;
54 }
55 let cfg = build_connection_config(&vps, Some(&path), opts.replace_host_key);
56 let client: Box<dyn SshClientTrait> = <SshClient as SshClientTrait>::connect(cfg).await?;
57 run_sudo_exec_with_client_steps(&vps, &cmd, &opts.steps, client, format, json, &target).await
58}
59
60pub async fn run_sudo_exec_with_client(
62 vps: &VpsRecord,
63 command: &str,
64 client: Box<dyn SshClientTrait>,
65 format: OutputFormat,
66 json: bool,
67) -> Result<()> {
68 let target =
69 crate::json_wire::ExecTarget::new(vps.name.as_str(), crate::json_wire::TargetSource::Argv);
70 run_sudo_exec_with_client_steps(vps, command, &[], client, format, json, &target).await
71}
72
73pub async fn run_sudo_exec_with_client_steps(
79 vps: &VpsRecord,
80 command: &str,
81 steps: &[crate::domain::RemoteCommand],
82 client: Box<dyn SshClientTrait>,
83 format: OutputFormat,
84 json: bool,
85 target: &crate::json_wire::ExecTarget,
86) -> Result<()> {
87 if crate::signals::should_stop() {
88 return Err(cancelled_err());
89 }
90 if vps.disable_sudo {
91 return Err(SshCliError::SudoDisabled.into());
92 }
93 let prepared = step_labels(command, steps)
94 .iter()
95 .map(|c| PreparedStep::packed(c, pack_sudo(c, vps.sudo_password.as_ref())))
96 .collect();
97 run_prepared_steps(vps, prepared, client, format, json, target).await
98}
99
100pub async fn run_su_exec(
104 selection: HostSelection,
105 command: &str,
106 config_override: Option<PathBuf>,
107 format: OutputFormat,
108 json: bool,
109 mut opts: ExecOptions,
110) -> Result<()> {
111 if crate::signals::should_stop() {
112 return Err(cancelled_err());
113 }
114 if selection.is_batch() {
115 return run_exec_all(
116 &selection,
117 command,
118 config_override,
119 format,
120 json,
121 opts,
122 ExecKind::Su,
123 )
124 .await;
125 }
126 let vps_name = expect_single(selection)?;
127 let target = crate::json_wire::ExecTarget::new(vps_name.clone(), opts.target_source);
128 let path = resolve_config_path(config_override.as_deref())?;
129 let mut file = load(&path)?;
130 let mut vps = file
131 .hosts
132 .remove(&vps_name)
133 .ok_or(SshCliError::VpsNotFound(vps_name))?;
134 crate::json_wire::set_resolved_target(&target);
138
139 apply_overrides(&mut vps, opts.take_auth_overrides());
140 if opts.disable_sudo || vps.disable_sudo {
141 return Err(SshCliError::SudoDisabled.into());
142 }
143 let su_password = vps
145 .su_password
146 .take()
147 .ok_or(SshCliError::SuPasswordMissing)?;
148 let cmd = append_description(command, opts.description.as_deref());
149 validate_command_length(&cmd, vps.max_command_chars.wire())?;
150 for s in &opts.steps {
151 validate_command_length(s.as_str(), vps.max_command_chars.wire())?;
152 }
153 let prepared = step_labels(&cmd, &opts.steps)
156 .iter()
157 .map(|c| PreparedStep::packed(c, pack_su(c, &su_password)))
158 .collect();
159 let cfg = build_connection_config(&vps, Some(&path), opts.replace_host_key);
160 let client: Box<dyn SshClientTrait> = <SshClient as SshClientTrait>::connect(cfg).await?;
161 run_prepared_steps(&vps, prepared, client, format, json, &target).await
162}