uihandler.rs
86 lines
| 2.8 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. | ||||
Gregory Szorc
|
r44270 | use futures::future::IntoFuture; | ||
Yuya Nishihara
|
r40010 | use futures::Future; | ||
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
|
r45231 | use tokio::process::{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. | ||||
pub trait SystemHandler: Sized { | ||||
type PagerStdin: AsRawFd; | ||||
type SpawnPagerResult: IntoFuture<Item = (Self, Self::PagerStdin), Error = io::Error>; | ||||
type RunSystemResult: IntoFuture<Item = (Self, i32), Error = io::Error>; | ||||
/// Handles pager command request. | ||||
/// | ||||
/// Returns the pipe to be attached to the server if the pager is spawned. | ||||
fn spawn_pager(self, spec: CommandSpec) -> Self::SpawnPagerResult; | ||||
/// Handles system command request. | ||||
/// | ||||
/// Returns command exit code (positive) or signal number (negative). | ||||
fn run_system(self, spec: CommandSpec) -> Self::RunSystemResult; | ||||
} | ||||
/// Default cHg implementation to process requests received from server. | ||||
Gregory Szorc
|
r44270 | pub struct ChgUiHandler {} | ||
Yuya Nishihara
|
r40010 | |||
impl ChgUiHandler { | ||||
pub fn new() -> ChgUiHandler { | ||||
ChgUiHandler {} | ||||
} | ||||
} | ||||
impl SystemHandler for ChgUiHandler { | ||||
type PagerStdin = ChildStdin; | ||||
type SpawnPagerResult = io::Result<(Self, Self::PagerStdin)>; | ||||
type RunSystemResult = Box<dyn Future<Item = (Self, i32), Error = io::Error> + Send>; | ||||
fn spawn_pager(self, spec: CommandSpec) -> Self::SpawnPagerResult { | ||||
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); | ||||
Gregory Szorc
|
r44270 | tokio::spawn(pager.map(|_| ()).map_err(|_| ())); // just ignore errors | ||
Yuya Nishihara
|
r40010 | Ok((self, pin)) | ||
} | ||||
fn run_system(self, spec: CommandSpec) -> Self::RunSystemResult { | ||||
let fut = new_shell_command(&spec) | ||||
Yuya Nishihara
|
r45231 | .spawn() | ||
Yuya Nishihara
|
r40010 | .into_future() | ||
.flatten() | ||||
.map(|status| { | ||||
Gregory Szorc
|
r44270 | let code = status | ||
.code() | ||||
.or_else(|| status.signal().map(|n| -n)) | ||||
Yuya Nishihara
|
r40010 | .expect("either exit code or signal should be set"); | ||
(self, code) | ||||
}); | ||||
Box::new(fut) | ||||
} | ||||
} | ||||
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 | } | ||