uihandler.rs
87 lines
| 2.6 KiB
| application/rls-services+xml
|
RustLexer
Yuya Nishihara
|
r40010 | // Copyright 2018 Yuya Nishihara <yuya@tcha.org> | ||
// | ||||
// This software may be used and distributed according to the terms of the | ||||
// GNU General Public License version 2 or any later version. | ||||
Yuya Nishihara
|
r45234 | use async_trait::async_trait; | ||
Yuya Nishihara
|
r40010 | use std::io; | ||
use std::os::unix::io::AsRawFd; | ||||
use std::os::unix::process::ExitStatusExt; | ||||
Yuya Nishihara
|
r45231 | use std::process::Stdio; | ||
Yuya Nishihara
|
r40010 | use tokio; | ||
Yuya Nishihara
|
r45239 | use tokio::process::{Child, ChildStdin, Command}; | ||
Yuya Nishihara
|
r40010 | |||
Yuya Nishihara
|
r45180 | use crate::message::CommandSpec; | ||
use crate::procutil; | ||||
Yuya Nishihara
|
r40010 | |||
/// Callback to process shell command requests received from server. | ||||
Yuya Nishihara
|
r45234 | #[async_trait] | ||
pub trait SystemHandler { | ||||
Yuya Nishihara
|
r40010 | type PagerStdin: AsRawFd; | ||
/// Handles pager command request. | ||||
/// | ||||
/// Returns the pipe to be attached to the server if the pager is spawned. | ||||
Yuya Nishihara
|
r45234 | async fn spawn_pager(&mut self, spec: &CommandSpec) -> io::Result<Self::PagerStdin>; | ||
Yuya Nishihara
|
r40010 | |||
/// Handles system command request. | ||||
/// | ||||
/// Returns command exit code (positive) or signal number (negative). | ||||
Yuya Nishihara
|
r45234 | async fn run_system(&mut self, spec: &CommandSpec) -> io::Result<i32>; | ||
Yuya Nishihara
|
r40010 | } | ||
/// Default cHg implementation to process requests received from server. | ||||
Yuya Nishihara
|
r45239 | pub struct ChgUiHandler { | ||
pager: Option<Child>, | ||||
} | ||||
Yuya Nishihara
|
r40010 | |||
impl ChgUiHandler { | ||||
pub fn new() -> ChgUiHandler { | ||||
Yuya Nishihara
|
r45239 | ChgUiHandler { pager: None } | ||
} | ||||
/// Waits until the pager process exits. | ||||
pub async fn wait_pager(&mut self) -> io::Result<()> { | ||||
if let Some(p) = self.pager.take() { | ||||
p.await?; | ||||
} | ||||
Ok(()) | ||||
Yuya Nishihara
|
r40010 | } | ||
} | ||||
Yuya Nishihara
|
r45234 | #[async_trait] | ||
Yuya Nishihara
|
r40010 | impl SystemHandler for ChgUiHandler { | ||
type PagerStdin = ChildStdin; | ||||
Yuya Nishihara
|
r45234 | async fn spawn_pager(&mut self, spec: &CommandSpec) -> io::Result<Self::PagerStdin> { | ||
Yuya Nishihara
|
r45231 | let mut pager = new_shell_command(&spec).stdin(Stdio::piped()).spawn()?; | ||
let pin = pager.stdin.take().unwrap(); | ||||
Yuya Nishihara
|
r40010 | procutil::set_blocking_fd(pin.as_raw_fd())?; | ||
Yuya Nishihara
|
r40155 | // TODO: if pager exits, notify the server with SIGPIPE immediately. | ||
// otherwise the server won't get SIGPIPE if it does not write | ||||
// anything. (issue5278) | ||||
// kill(peerpid, SIGPIPE); | ||||
Yuya Nishihara
|
r45239 | self.pager = Some(pager); | ||
Yuya Nishihara
|
r45234 | Ok(pin) | ||
Yuya Nishihara
|
r40010 | } | ||
Yuya Nishihara
|
r45234 | async fn run_system(&mut self, spec: &CommandSpec) -> io::Result<i32> { | ||
let status = new_shell_command(&spec).spawn()?.await?; | ||||
Yuya Nishihara
|
r45240 | let code = status | ||
.code() | ||||
.or_else(|| status.signal().map(|n| -n)) | ||||
.expect("either exit code or signal should be set"); | ||||
Ok(code) | ||||
Yuya Nishihara
|
r40010 | } | ||
} | ||||
fn new_shell_command(spec: &CommandSpec) -> Command { | ||||
let mut builder = Command::new("/bin/sh"); | ||||
builder | ||||
.arg("-c") | ||||
.arg(&spec.command) | ||||
.current_dir(&spec.current_dir) | ||||
.env_clear() | ||||
.envs(spec.envs.iter().cloned()); | ||||
builder | ||||
Gregory Szorc
|
r44270 | } | ||