Show More
@@ -8,9 +8,9 b'' | |||||
8 | use futures::{Async, Future, Poll}; |
|
8 | use futures::{Async, Future, Poll}; | |
9 | use std::io; |
|
9 | use std::io; | |
10 | use std::os::unix::io::AsRawFd; |
|
10 | use std::os::unix::io::AsRawFd; | |
11 | use tokio_hglib::{Client, Connection}; |
|
|||
12 | use tokio_hglib::codec::ChannelMessage; |
|
11 | use tokio_hglib::codec::ChannelMessage; | |
13 | use tokio_hglib::protocol::MessageLoop; |
|
12 | use tokio_hglib::protocol::MessageLoop; | |
|
13 | use tokio_hglib::{Client, Connection}; | |||
14 |
|
14 | |||
15 | use super::message; |
|
15 | use super::message; | |
16 | use super::procutil; |
|
16 | use super::procutil; | |
@@ -28,7 +28,8 b' use super::procutil;' | |||||
28 | /// dispose of the client-side handle once attached. |
|
28 | /// dispose of the client-side handle once attached. | |
29 | #[must_use = "futures do nothing unless polled"] |
|
29 | #[must_use = "futures do nothing unless polled"] | |
30 | pub struct AttachIo<C, I, O, E> |
|
30 | pub struct AttachIo<C, I, O, E> | |
31 | where C: Connection, |
|
31 | where | |
|
32 | C: Connection, | |||
32 | { |
|
33 | { | |
33 | msg_loop: MessageLoop<C>, |
|
34 | msg_loop: MessageLoop<C>, | |
34 | stdin: I, |
|
35 | stdin: I, | |
@@ -37,23 +38,34 b' pub struct AttachIo<C, I, O, E>' | |||||
37 | } |
|
38 | } | |
38 |
|
39 | |||
39 | impl<C, I, O, E> AttachIo<C, I, O, E> |
|
40 | impl<C, I, O, E> AttachIo<C, I, O, E> | |
40 | where C: Connection + AsRawFd, |
|
41 | where | |
41 |
|
|
42 | C: Connection + AsRawFd, | |
42 |
|
|
43 | I: AsRawFd, | |
43 |
|
|
44 | O: AsRawFd, | |
|
45 | E: AsRawFd, | |||
44 | { |
|
46 | { | |
45 | pub fn with_client(client: Client<C>, stdin: I, stdout: O, stderr: Option<E>) |
|
47 | pub fn with_client( | |
46 | -> AttachIo<C, I, O, E> { |
|
48 | client: Client<C>, | |
|
49 | stdin: I, | |||
|
50 | stdout: O, | |||
|
51 | stderr: Option<E>, | |||
|
52 | ) -> AttachIo<C, I, O, E> { | |||
47 | let msg_loop = MessageLoop::start(client, b"attachio"); |
|
53 | let msg_loop = MessageLoop::start(client, b"attachio"); | |
48 | AttachIo { msg_loop, stdin, stdout, stderr } |
|
54 | AttachIo { | |
|
55 | msg_loop, | |||
|
56 | stdin, | |||
|
57 | stdout, | |||
|
58 | stderr, | |||
|
59 | } | |||
49 | } |
|
60 | } | |
50 | } |
|
61 | } | |
51 |
|
62 | |||
52 | impl<C, I, O, E> Future for AttachIo<C, I, O, E> |
|
63 | impl<C, I, O, E> Future for AttachIo<C, I, O, E> | |
53 | where C: Connection + AsRawFd, |
|
64 | where | |
54 |
|
|
65 | C: Connection + AsRawFd, | |
55 |
|
|
66 | I: AsRawFd, | |
56 |
|
|
67 | O: AsRawFd, | |
|
68 | E: AsRawFd, | |||
57 | { |
|
69 | { | |
58 | type Item = Client<C>; |
|
70 | type Item = Client<C>; | |
59 | type Error = io::Error; |
|
71 | type Error = io::Error; | |
@@ -67,8 +79,10 b' impl<C, I, O, E> Future for AttachIo<C, ' | |||||
67 | if fd_cnt == 3 { |
|
79 | if fd_cnt == 3 { | |
68 | return Ok(Async::Ready(client)); |
|
80 | return Ok(Async::Ready(client)); | |
69 | } else { |
|
81 | } else { | |
70 |
return Err(io::Error::new( |
|
82 | return Err(io::Error::new( | |
71 | "unexpected attachio result")); |
|
83 | io::ErrorKind::InvalidData, | |
|
84 | "unexpected attachio result", | |||
|
85 | )); | |||
72 | } |
|
86 | } | |
73 | } |
|
87 | } | |
74 | ChannelMessage::Data(..) => { |
|
88 | ChannelMessage::Data(..) => { | |
@@ -86,10 +100,13 b' impl<C, I, O, E> Future for AttachIo<C, ' | |||||
86 | procutil::send_raw_fds(sock_fd, &[ifd, ofd, efd])?; |
|
100 | procutil::send_raw_fds(sock_fd, &[ifd, ofd, efd])?; | |
87 | self.msg_loop = MessageLoop::resume(client); |
|
101 | self.msg_loop = MessageLoop::resume(client); | |
88 | } |
|
102 | } | |
89 |
ChannelMessage::InputRequest(..) |
|
103 | ChannelMessage::InputRequest(..) | |
90 |
ChannelMessage:: |
|
104 | | ChannelMessage::LineRequest(..) | |
91 | return Err(io::Error::new(io::ErrorKind::InvalidData, |
|
105 | | ChannelMessage::SystemRequest(..) => { | |
92 | "unsupported request while attaching io")); |
|
106 | return Err(io::Error::new( | |
|
107 | io::ErrorKind::InvalidData, | |||
|
108 | "unsupported request while attaching io", | |||
|
109 | )); | |||
93 | } |
|
110 | } | |
94 | } |
|
111 | } | |
95 | } |
|
112 | } |
@@ -9,8 +9,8 b' use std::ffi::OsStr;' | |||||
9 | use std::os::unix::ffi::OsStrExt; |
|
9 | use std::os::unix::ffi::OsStrExt; | |
10 | use std::os::unix::io::AsRawFd; |
|
10 | use std::os::unix::io::AsRawFd; | |
11 | use std::path::Path; |
|
11 | use std::path::Path; | |
|
12 | use tokio_hglib::protocol::OneShotRequest; | |||
12 | use tokio_hglib::{Client, Connection}; |
|
13 | use tokio_hglib::{Client, Connection}; | |
13 | use tokio_hglib::protocol::OneShotRequest; |
|
|||
14 |
|
14 | |||
15 | use super::attachio::AttachIo; |
|
15 | use super::attachio::AttachIo; | |
16 | use super::message; |
|
16 | use super::message; | |
@@ -18,46 +18,54 b' use super::runcommand::ChgRunCommand;' | |||||
18 | use super::uihandler::SystemHandler; |
|
18 | use super::uihandler::SystemHandler; | |
19 |
|
19 | |||
20 | pub trait ChgClientExt<C> |
|
20 | pub trait ChgClientExt<C> | |
21 | where C: Connection + AsRawFd, |
|
21 | where | |
|
22 | C: Connection + AsRawFd, | |||
22 | { |
|
23 | { | |
23 | /// Attaches the client file descriptors to the server. |
|
24 | /// Attaches the client file descriptors to the server. | |
24 | fn attach_io<I, O, E>(self, stdin: I, stdout: O, stderr: E) -> AttachIo<C, I, O, E> |
|
25 | fn attach_io<I, O, E>(self, stdin: I, stdout: O, stderr: E) -> AttachIo<C, I, O, E> | |
25 | where I: AsRawFd, |
|
26 | where | |
26 |
|
|
27 | I: AsRawFd, | |
27 |
|
|
28 | O: AsRawFd, | |
|
29 | E: AsRawFd; | |||
28 |
|
30 | |||
29 | /// Changes the working directory of the server. |
|
31 | /// Changes the working directory of the server. | |
30 | fn set_current_dir<P>(self, dir: P) -> OneShotRequest<C> |
|
32 | fn set_current_dir<P>(self, dir: P) -> OneShotRequest<C> | |
31 | where P: AsRef<Path>; |
|
33 | where | |
|
34 | P: AsRef<Path>; | |||
32 |
|
35 | |||
33 | /// Runs the specified Mercurial command with cHg extension. |
|
36 | /// Runs the specified Mercurial command with cHg extension. | |
34 | fn run_command_chg<I, P, H>(self, handler: H, args: I) -> ChgRunCommand<C, H> |
|
37 | fn run_command_chg<I, P, H>(self, handler: H, args: I) -> ChgRunCommand<C, H> | |
35 | where I: IntoIterator<Item = P>, |
|
38 | where | |
36 | P: AsRef<OsStr>, |
|
39 | I: IntoIterator<Item = P>, | |
37 | H: SystemHandler; |
|
40 | P: AsRef<OsStr>, | |
|
41 | H: SystemHandler; | |||
38 | } |
|
42 | } | |
39 |
|
43 | |||
40 | impl<C> ChgClientExt<C> for Client<C> |
|
44 | impl<C> ChgClientExt<C> for Client<C> | |
41 | where C: Connection + AsRawFd, |
|
45 | where | |
|
46 | C: Connection + AsRawFd, | |||
42 | { |
|
47 | { | |
43 | fn attach_io<I, O, E>(self, stdin: I, stdout: O, stderr: E) -> AttachIo<C, I, O, E> |
|
48 | fn attach_io<I, O, E>(self, stdin: I, stdout: O, stderr: E) -> AttachIo<C, I, O, E> | |
44 | where I: AsRawFd, |
|
49 | where | |
45 |
|
|
50 | I: AsRawFd, | |
46 |
|
|
51 | O: AsRawFd, | |
|
52 | E: AsRawFd, | |||
47 | { |
|
53 | { | |
48 | AttachIo::with_client(self, stdin, stdout, Some(stderr)) |
|
54 | AttachIo::with_client(self, stdin, stdout, Some(stderr)) | |
49 | } |
|
55 | } | |
50 |
|
56 | |||
51 | fn set_current_dir<P>(self, dir: P) -> OneShotRequest<C> |
|
57 | fn set_current_dir<P>(self, dir: P) -> OneShotRequest<C> | |
52 | where P: AsRef<Path>, |
|
58 | where | |
|
59 | P: AsRef<Path>, | |||
53 | { |
|
60 | { | |
54 | OneShotRequest::start_with_args(self, b"chdir", dir.as_ref().as_os_str().as_bytes()) |
|
61 | OneShotRequest::start_with_args(self, b"chdir", dir.as_ref().as_os_str().as_bytes()) | |
55 | } |
|
62 | } | |
56 |
|
63 | |||
57 | fn run_command_chg<I, P, H>(self, handler: H, args: I) -> ChgRunCommand<C, H> |
|
64 | fn run_command_chg<I, P, H>(self, handler: H, args: I) -> ChgRunCommand<C, H> | |
58 | where I: IntoIterator<Item = P>, |
|
65 | where | |
59 | P: AsRef<OsStr>, |
|
66 | I: IntoIterator<Item = P>, | |
60 | H: SystemHandler, |
|
67 | P: AsRef<OsStr>, | |
|
68 | H: SystemHandler, | |||
61 | { |
|
69 | { | |
62 | ChgRunCommand::with_client(self, handler, message::pack_args_os(args)) |
|
70 | ChgRunCommand::with_client(self, handler, message::pack_args_os(args)) | |
63 | } |
|
71 | } |
@@ -91,11 +91,16 b' pub fn default_server_socket_dir() -> Pa' | |||||
91 | /// Determines the default hg command. |
|
91 | /// Determines the default hg command. | |
92 | pub fn default_hg_command() -> OsString { |
|
92 | pub fn default_hg_command() -> OsString { | |
93 | // TODO: maybe allow embedding the path at compile time (or load from hgrc) |
|
93 | // TODO: maybe allow embedding the path at compile time (or load from hgrc) | |
94 | env::var_os("CHGHG").or(env::var_os("HG")).unwrap_or(OsStr::new("hg").to_owned()) |
|
94 | env::var_os("CHGHG") | |
|
95 | .or(env::var_os("HG")) | |||
|
96 | .unwrap_or(OsStr::new("hg").to_owned()) | |||
95 | } |
|
97 | } | |
96 |
|
98 | |||
97 | fn default_timeout() -> Duration { |
|
99 | fn default_timeout() -> Duration { | |
98 |
let secs = env::var("CHGTIMEOUT") |
|
100 | let secs = env::var("CHGTIMEOUT") | |
|
101 | .ok() | |||
|
102 | .and_then(|s| s.parse().ok()) | |||
|
103 | .unwrap_or(60); | |||
99 | Duration::from_secs(secs) |
|
104 | Duration::from_secs(secs) | |
100 | } |
|
105 | } | |
101 |
|
106 | |||
@@ -103,19 +108,24 b' fn default_timeout() -> Duration {' | |||||
103 | /// |
|
108 | /// | |
104 | /// If the directory already exists, tests its permission. |
|
109 | /// If the directory already exists, tests its permission. | |
105 | fn create_secure_dir<P>(path: P) -> io::Result<()> |
|
110 | fn create_secure_dir<P>(path: P) -> io::Result<()> | |
106 | where P: AsRef<Path>, |
|
111 | where | |
|
112 | P: AsRef<Path>, | |||
107 | { |
|
113 | { | |
108 | DirBuilder::new().mode(0o700).create(path.as_ref()).or_else(|err| { |
|
114 | DirBuilder::new() | |
109 | if err.kind() == io::ErrorKind::AlreadyExists { |
|
115 | .mode(0o700) | |
110 | check_secure_dir(path).map(|_| ()) |
|
116 | .create(path.as_ref()) | |
111 |
|
|
117 | .or_else(|err| { | |
112 | Err(err) |
|
118 | if err.kind() == io::ErrorKind::AlreadyExists { | |
113 | } |
|
119 | check_secure_dir(path).map(|_| ()) | |
114 | }) |
|
120 | } else { | |
|
121 | Err(err) | |||
|
122 | } | |||
|
123 | }) | |||
115 | } |
|
124 | } | |
116 |
|
125 | |||
117 | fn check_secure_dir<P>(path: P) -> io::Result<P> |
|
126 | fn check_secure_dir<P>(path: P) -> io::Result<P> | |
118 | where P: AsRef<Path>, |
|
127 | where | |
|
128 | P: AsRef<Path>, | |||
119 | { |
|
129 | { | |
120 | let a = fs::symlink_metadata(path.as_ref())?; |
|
130 | let a = fs::symlink_metadata(path.as_ref())?; | |
121 | if a.is_dir() && a.uid() == procutil::get_effective_uid() && (a.mode() & 0o777) == 0o700 { |
|
131 | if a.is_dir() && a.uid() == procutil::get_effective_uid() && (a.mode() & 0o777) == 0o700 { |
@@ -9,9 +9,9 b' extern crate log;' | |||||
9 | extern crate tokio; |
|
9 | extern crate tokio; | |
10 | extern crate tokio_hglib; |
|
10 | extern crate tokio_hglib; | |
11 |
|
11 | |||
12 | use chg::{ChgClientExt, ChgUiHandler}; |
|
|||
13 | use chg::locator; |
|
12 | use chg::locator; | |
14 | use chg::procutil; |
|
13 | use chg::procutil; | |
|
14 | use chg::{ChgClientExt, ChgUiHandler}; | |||
15 | use futures::sync::oneshot; |
|
15 | use futures::sync::oneshot; | |
16 | use std::env; |
|
16 | use std::env; | |
17 | use std::io; |
|
17 | use std::io; | |
@@ -42,13 +42,19 b' impl log::Log for DebugLogger {' | |||||
42 | // just make the output looks similar to chg of C |
|
42 | // just make the output looks similar to chg of C | |
43 | let l = format!("{}", record.level()).to_lowercase(); |
|
43 | let l = format!("{}", record.level()).to_lowercase(); | |
44 | let t = self.start.elapsed(); |
|
44 | let t = self.start.elapsed(); | |
45 | writeln!(io::stderr(), "chg: {}: {}.{:06} {}", |
|
45 | writeln!( | |
46 | l, t.as_secs(), t.subsec_micros(), record.args()).unwrap_or(()); |
|
46 | io::stderr(), | |
|
47 | "chg: {}: {}.{:06} {}", | |||
|
48 | l, | |||
|
49 | t.as_secs(), | |||
|
50 | t.subsec_micros(), | |||
|
51 | record.args() | |||
|
52 | ) | |||
|
53 | .unwrap_or(()); | |||
47 | } |
|
54 | } | |
48 | } |
|
55 | } | |
49 |
|
56 | |||
50 | fn flush(&self) { |
|
57 | fn flush(&self) {} | |
51 | } |
|
|||
52 | } |
|
58 | } | |
53 |
|
59 | |||
54 | fn main() { |
|
60 | fn main() { | |
@@ -71,28 +77,24 b' fn run() -> io::Result<i32> {' | |||||
71 | let handler = ChgUiHandler::new(); |
|
77 | let handler = ChgUiHandler::new(); | |
72 | let (result_tx, result_rx) = oneshot::channel(); |
|
78 | let (result_tx, result_rx) = oneshot::channel(); | |
73 | let fut = UnixClient::connect(sock_path) |
|
79 | let fut = UnixClient::connect(sock_path) | |
74 | .and_then(|client| { |
|
80 | .and_then(|client| client.set_current_dir(current_dir)) | |
75 | client.set_current_dir(current_dir) |
|
81 | .and_then(|client| client.attach_io(io::stdin(), io::stdout(), io::stderr())) | |
76 | }) |
|
|||
77 | .and_then(|client| { |
|
|||
78 | client.attach_io(io::stdin(), io::stdout(), io::stderr()) |
|
|||
79 | }) |
|
|||
80 | .and_then(|client| { |
|
82 | .and_then(|client| { | |
81 | let pid = client.server_spec().process_id.unwrap(); |
|
83 | let pid = client.server_spec().process_id.unwrap(); | |
82 | let pgid = client.server_spec().process_group_id; |
|
84 | let pgid = client.server_spec().process_group_id; | |
83 | procutil::setup_signal_handler_once(pid, pgid)?; |
|
85 | procutil::setup_signal_handler_once(pid, pgid)?; | |
84 | Ok(client) |
|
86 | Ok(client) | |
85 | }) |
|
87 | }) | |
86 | .and_then(|client| { |
|
88 | .and_then(|client| client.run_command_chg(handler, env::args_os().skip(1))) | |
87 | client.run_command_chg(handler, env::args_os().skip(1)) |
|
|||
88 | }) |
|
|||
89 | .map(|(_client, _handler, code)| { |
|
89 | .map(|(_client, _handler, code)| { | |
90 | procutil::restore_signal_handler_once()?; |
|
90 | procutil::restore_signal_handler_once()?; | |
91 | Ok(code) |
|
91 | Ok(code) | |
92 | }) |
|
92 | }) | |
93 |
.or_else(|err| Ok(Err(err))) |
|
93 | .or_else(|err| Ok(Err(err))) // pass back error to caller | |
94 | .map(|res| result_tx.send(res).unwrap()); |
|
94 | .map(|res| result_tx.send(res).unwrap()); | |
95 | tokio::run(fut); |
|
95 | tokio::run(fut); | |
96 |
result_rx.wait().unwrap_or(Err(io::Error::new( |
|
96 | result_rx.wait().unwrap_or(Err(io::Error::new( | |
97 | "no exit code set"))) |
|
97 | io::ErrorKind::Other, | |
|
98 | "no exit code set", | |||
|
99 | ))) | |||
98 | } |
|
100 | } |
@@ -11,7 +11,7 b' use std::ffi::{OsStr, OsString};' | |||||
11 | use std::io; |
|
11 | use std::io; | |
12 | use std::os::unix::ffi::OsStrExt; |
|
12 | use std::os::unix::ffi::OsStrExt; | |
13 |
|
13 | |||
14 |
pub use tokio_hglib::message::*; |
|
14 | pub use tokio_hglib::message::*; // re-exports | |
15 |
|
15 | |||
16 | /// Shell command type requested by the server. |
|
16 | /// Shell command type requested by the server. | |
17 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
|
17 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] | |
@@ -42,7 +42,10 b' pub fn parse_command_spec(data: Bytes) -' | |||||
42 | let mut s = l.splitn(2, |&c| c == b'='); |
|
42 | let mut s = l.splitn(2, |&c| c == b'='); | |
43 | let k = s.next().unwrap(); |
|
43 | let k = s.next().unwrap(); | |
44 | let v = s.next().ok_or(new_parse_error("malformed env"))?; |
|
44 | let v = s.next().ok_or(new_parse_error("malformed env"))?; | |
45 | envs.push((OsStr::from_bytes(k).to_owned(), OsStr::from_bytes(v).to_owned())); |
|
45 | envs.push(( | |
|
46 | OsStr::from_bytes(k).to_owned(), | |||
|
47 | OsStr::from_bytes(v).to_owned(), | |||
|
48 | )); | |||
46 | } |
|
49 | } | |
47 |
|
50 | |||
48 | let spec = CommandSpec { |
|
51 | let spec = CommandSpec { | |
@@ -57,41 +60,54 b' fn parse_command_type(value: &[u8]) -> i' | |||||
57 | match value { |
|
60 | match value { | |
58 | b"pager" => Ok(CommandType::Pager), |
|
61 | b"pager" => Ok(CommandType::Pager), | |
59 | b"system" => Ok(CommandType::System), |
|
62 | b"system" => Ok(CommandType::System), | |
60 |
_ => Err(new_parse_error(format!( |
|
63 | _ => Err(new_parse_error(format!( | |
|
64 | "unknown command type: {}", | |||
|
65 | decode_latin1(value) | |||
|
66 | ))), | |||
61 | } |
|
67 | } | |
62 | } |
|
68 | } | |
63 |
|
69 | |||
64 | fn decode_latin1<S>(s: S) -> String |
|
70 | fn decode_latin1<S>(s: S) -> String | |
65 | where S: AsRef<[u8]>, |
|
71 | where | |
|
72 | S: AsRef<[u8]>, | |||
66 | { |
|
73 | { | |
67 | s.as_ref().iter().map(|&c| c as char).collect() |
|
74 | s.as_ref().iter().map(|&c| c as char).collect() | |
68 | } |
|
75 | } | |
69 |
|
76 | |||
70 | fn new_parse_error<E>(error: E) -> io::Error |
|
77 | fn new_parse_error<E>(error: E) -> io::Error | |
71 | where E: Into<Box<error::Error + Send + Sync>>, |
|
78 | where | |
|
79 | E: Into<Box<error::Error + Send + Sync>>, | |||
72 | { |
|
80 | { | |
73 | io::Error::new(io::ErrorKind::InvalidData, error) |
|
81 | io::Error::new(io::ErrorKind::InvalidData, error) | |
74 | } |
|
82 | } | |
75 |
|
83 | |||
76 | #[cfg(test)] |
|
84 | #[cfg(test)] | |
77 | mod tests { |
|
85 | mod tests { | |
|
86 | use super::*; | |||
78 | use std::os::unix::ffi::OsStringExt; |
|
87 | use std::os::unix::ffi::OsStringExt; | |
79 | use super::*; |
|
|||
80 |
|
88 | |||
81 | #[test] |
|
89 | #[test] | |
82 | fn parse_command_spec_good() { |
|
90 | fn parse_command_spec_good() { | |
83 |
let src = [ |
|
91 | let src = [ | |
84 |
|
|
92 | b"pager".as_ref(), | |
85 |
|
|
93 | b"less -FRX".as_ref(), | |
86 |
|
|
94 | b"/tmp".as_ref(), | |
87 |
|
|
95 | b"LANG=C".as_ref(), | |
|
96 | b"HGPLAIN=".as_ref(), | |||
|
97 | ] | |||
|
98 | .join(&0); | |||
88 | let spec = CommandSpec { |
|
99 | let spec = CommandSpec { | |
89 | command: os_string_from(b"less -FRX"), |
|
100 | command: os_string_from(b"less -FRX"), | |
90 | current_dir: os_string_from(b"/tmp"), |
|
101 | current_dir: os_string_from(b"/tmp"), | |
91 | envs: vec![(os_string_from(b"LANG"), os_string_from(b"C")), |
|
102 | envs: vec![ | |
92 |
|
|
103 | (os_string_from(b"LANG"), os_string_from(b"C")), | |
|
104 | (os_string_from(b"HGPLAIN"), os_string_from(b"")), | |||
|
105 | ], | |||
93 | }; |
|
106 | }; | |
94 | assert_eq!(parse_command_spec(Bytes::from(src)).unwrap(), (CommandType::Pager, spec)); |
|
107 | assert_eq!( | |
|
108 | parse_command_spec(Bytes::from(src)).unwrap(), | |||
|
109 | (CommandType::Pager, spec) | |||
|
110 | ); | |||
95 | } |
|
111 | } | |
96 |
|
112 | |||
97 | #[test] |
|
113 | #[test] |
@@ -33,7 +33,7 b' pub fn set_blocking_fd(fd: RawFd) -> io:' | |||||
33 | } |
|
33 | } | |
34 | let r = unsafe { libc::fcntl(fd, libc::F_SETFL, flags & !libc::O_NONBLOCK) }; |
|
34 | let r = unsafe { libc::fcntl(fd, libc::F_SETFL, flags & !libc::O_NONBLOCK) }; | |
35 | if r < 0 { |
|
35 | if r < 0 { | |
36 | return Err(io::Error::last_os_error()) |
|
36 | return Err(io::Error::last_os_error()); | |
37 | } |
|
37 | } | |
38 | Ok(()) |
|
38 | Ok(()) | |
39 | } |
|
39 | } |
@@ -11,9 +11,9 b' use futures::{Async, Future, Poll};' | |||||
11 | use std::io; |
|
11 | use std::io; | |
12 | use std::mem; |
|
12 | use std::mem; | |
13 | use std::os::unix::io::AsRawFd; |
|
13 | use std::os::unix::io::AsRawFd; | |
14 | use tokio_hglib::{Client, Connection}; |
|
|||
15 | use tokio_hglib::codec::ChannelMessage; |
|
14 | use tokio_hglib::codec::ChannelMessage; | |
16 | use tokio_hglib::protocol::MessageLoop; |
|
15 | use tokio_hglib::protocol::MessageLoop; | |
|
16 | use tokio_hglib::{Client, Connection}; | |||
17 |
|
17 | |||
18 | use super::attachio::AttachIo; |
|
18 | use super::attachio::AttachIo; | |
19 | use super::message::{self, CommandType}; |
|
19 | use super::message::{self, CommandType}; | |
@@ -26,8 +26,9 b' enum AsyncS<R, S> {' | |||||
26 | } |
|
26 | } | |
27 |
|
27 | |||
28 | enum CommandState<C, H> |
|
28 | enum CommandState<C, H> | |
29 | where C: Connection, |
|
29 | where | |
30 | H: SystemHandler, |
|
30 | C: Connection, | |
|
31 | H: SystemHandler, | |||
31 | { |
|
32 | { | |
32 | Running(MessageLoop<C>, H), |
|
33 | Running(MessageLoop<C>, H), | |
33 | SpawningPager(Client<C>, <H::SpawnPagerResult as IntoFuture>::Future), |
|
34 | SpawningPager(Client<C>, <H::SpawnPagerResult as IntoFuture>::Future), | |
@@ -41,18 +42,19 b' type CommandPoll<C, H> = io::Result<(Asy' | |||||
41 | /// Future resolves to `(exit_code, client)`. |
|
42 | /// Future resolves to `(exit_code, client)`. | |
42 | #[must_use = "futures do nothing unless polled"] |
|
43 | #[must_use = "futures do nothing unless polled"] | |
43 | pub struct ChgRunCommand<C, H> |
|
44 | pub struct ChgRunCommand<C, H> | |
44 | where C: Connection, |
|
45 | where | |
45 | H: SystemHandler, |
|
46 | C: Connection, | |
|
47 | H: SystemHandler, | |||
46 | { |
|
48 | { | |
47 | state: CommandState<C, H>, |
|
49 | state: CommandState<C, H>, | |
48 | } |
|
50 | } | |
49 |
|
51 | |||
50 | impl<C, H> ChgRunCommand<C, H> |
|
52 | impl<C, H> ChgRunCommand<C, H> | |
51 | where C: Connection + AsRawFd, |
|
53 | where | |
52 | H: SystemHandler, |
|
54 | C: Connection + AsRawFd, | |
|
55 | H: SystemHandler, | |||
53 | { |
|
56 | { | |
54 | pub fn with_client(client: Client<C>, handler: H, packed_args: Bytes) |
|
57 | pub fn with_client(client: Client<C>, handler: H, packed_args: Bytes) -> ChgRunCommand<C, H> { | |
55 | -> ChgRunCommand<C, H> { |
|
|||
56 | let msg_loop = MessageLoop::start_with_args(client, b"runcommand", packed_args); |
|
58 | let msg_loop = MessageLoop::start_with_args(client, b"runcommand", packed_args); | |
57 | ChgRunCommand { |
|
59 | ChgRunCommand { | |
58 | state: CommandState::Running(msg_loop, handler), |
|
60 | state: CommandState::Running(msg_loop, handler), | |
@@ -61,8 +63,9 b' impl<C, H> ChgRunCommand<C, H>' | |||||
61 | } |
|
63 | } | |
62 |
|
64 | |||
63 | impl<C, H> Future for ChgRunCommand<C, H> |
|
65 | impl<C, H> Future for ChgRunCommand<C, H> | |
64 | where C: Connection + AsRawFd, |
|
66 | where | |
65 | H: SystemHandler, |
|
67 | C: Connection + AsRawFd, | |
|
68 | H: SystemHandler, | |||
66 | { |
|
69 | { | |
67 | type Item = (Client<C>, H, i32); |
|
70 | type Item = (Client<C>, H, i32); | |
68 | type Error = io::Error; |
|
71 | type Error = io::Error; | |
@@ -87,8 +90,9 b' impl<C, H> Future for ChgRunCommand<C, H' | |||||
87 | } |
|
90 | } | |
88 |
|
91 | |||
89 | impl<C, H> CommandState<C, H> |
|
92 | impl<C, H> CommandState<C, H> | |
90 | where C: Connection + AsRawFd, |
|
93 | where | |
91 | H: SystemHandler, |
|
94 | C: Connection + AsRawFd, | |
|
95 | H: SystemHandler, | |||
92 | { |
|
96 | { | |
93 | fn poll(self) -> CommandPoll<C, H> { |
|
97 | fn poll(self) -> CommandPoll<C, H> { | |
94 | match self { |
|
98 | match self { | |
@@ -102,14 +106,16 b' impl<C, H> CommandState<C, H>' | |||||
102 | CommandState::SpawningPager(client, mut fut) => { |
|
106 | CommandState::SpawningPager(client, mut fut) => { | |
103 | if let Async::Ready((handler, pin)) = fut.poll()? { |
|
107 | if let Async::Ready((handler, pin)) = fut.poll()? { | |
104 | let fut = AttachIo::with_client(client, io::stdin(), pin, None); |
|
108 | let fut = AttachIo::with_client(client, io::stdin(), pin, None); | |
105 |
Ok(AsyncS::PollAgain(CommandState::AttachingPager( |
|
109 | Ok(AsyncS::PollAgain(CommandState::AttachingPager( | |
|
110 | fut, handler, | |||
|
111 | ))) | |||
106 | } else { |
|
112 | } else { | |
107 | Ok(AsyncS::NotReady(CommandState::SpawningPager(client, fut))) |
|
113 | Ok(AsyncS::NotReady(CommandState::SpawningPager(client, fut))) | |
108 | } |
|
114 | } | |
109 | } |
|
115 | } | |
110 | CommandState::AttachingPager(mut fut, handler) => { |
|
116 | CommandState::AttachingPager(mut fut, handler) => { | |
111 | if let Async::Ready(client) = fut.poll()? { |
|
117 | if let Async::Ready(client) = fut.poll()? { | |
112 |
let msg_loop = MessageLoop::start(client, b""); |
|
118 | let msg_loop = MessageLoop::start(client, b""); // terminator | |
113 | Ok(AsyncS::PollAgain(CommandState::Running(msg_loop, handler))) |
|
119 | Ok(AsyncS::PollAgain(CommandState::Running(msg_loop, handler))) | |
114 | } else { |
|
120 | } else { | |
115 | Ok(AsyncS::NotReady(CommandState::AttachingPager(fut, handler))) |
|
121 | Ok(AsyncS::NotReady(CommandState::AttachingPager(fut, handler))) | |
@@ -124,14 +130,15 b' impl<C, H> CommandState<C, H>' | |||||
124 | Ok(AsyncS::NotReady(CommandState::WaitingSystem(client, fut))) |
|
130 | Ok(AsyncS::NotReady(CommandState::WaitingSystem(client, fut))) | |
125 | } |
|
131 | } | |
126 | } |
|
132 | } | |
127 | CommandState::Finished => panic!("poll ChgRunCommand after it's done") |
|
133 | CommandState::Finished => panic!("poll ChgRunCommand after it's done"), | |
128 | } |
|
134 | } | |
129 | } |
|
135 | } | |
130 | } |
|
136 | } | |
131 |
|
137 | |||
132 | fn process_message<C, H>(client: Client<C>, handler: H, msg: ChannelMessage) -> CommandPoll<C, H> |
|
138 | fn process_message<C, H>(client: Client<C>, handler: H, msg: ChannelMessage) -> CommandPoll<C, H> | |
133 | where C: Connection, |
|
139 | where | |
134 | H: SystemHandler, |
|
140 | C: Connection, | |
|
141 | H: SystemHandler, | |||
135 | { |
|
142 | { | |
136 | match msg { |
|
143 | match msg { | |
137 | ChannelMessage::Data(b'r', data) => { |
|
144 | ChannelMessage::Data(b'r', data) => { | |
@@ -143,9 +150,10 b' fn process_message<C, H>(client: Client<' | |||||
143 | let msg_loop = MessageLoop::resume(client); |
|
150 | let msg_loop = MessageLoop::resume(client); | |
144 | Ok(AsyncS::PollAgain(CommandState::Running(msg_loop, handler))) |
|
151 | Ok(AsyncS::PollAgain(CommandState::Running(msg_loop, handler))) | |
145 | } |
|
152 | } | |
146 |
ChannelMessage::InputRequest(..) | ChannelMessage::LineRequest(..) => |
|
153 | ChannelMessage::InputRequest(..) | ChannelMessage::LineRequest(..) => Err(io::Error::new( | |
147 |
|
|
154 | io::ErrorKind::InvalidData, | |
148 | } |
|
155 | "unsupported request", | |
|
156 | )), | |||
149 | ChannelMessage::SystemRequest(data) => { |
|
157 | ChannelMessage::SystemRequest(data) => { | |
150 | let (cmd_type, cmd_spec) = message::parse_command_spec(data)?; |
|
158 | let (cmd_type, cmd_spec) = message::parse_command_spec(data)?; | |
151 | match cmd_type { |
|
159 | match cmd_type { |
@@ -3,8 +3,8 b'' | |||||
3 | // This software may be used and distributed according to the terms of the |
|
3 | // This software may be used and distributed according to the terms of the | |
4 | // GNU General Public License version 2 or any later version. |
|
4 | // GNU General Public License version 2 or any later version. | |
5 |
|
5 | |||
|
6 | use futures::future::IntoFuture; | |||
6 | use futures::Future; |
|
7 | use futures::Future; | |
7 | use futures::future::IntoFuture; |
|
|||
8 | use std::io; |
|
8 | use std::io; | |
9 | use std::os::unix::io::AsRawFd; |
|
9 | use std::os::unix::io::AsRawFd; | |
10 | use std::os::unix::process::ExitStatusExt; |
|
10 | use std::os::unix::process::ExitStatusExt; | |
@@ -33,8 +33,7 b' pub trait SystemHandler: Sized {' | |||||
33 | } |
|
33 | } | |
34 |
|
34 | |||
35 | /// Default cHg implementation to process requests received from server. |
|
35 | /// Default cHg implementation to process requests received from server. | |
36 | pub struct ChgUiHandler { |
|
36 | pub struct ChgUiHandler {} | |
37 | } |
|
|||
38 |
|
37 | |||
39 | impl ChgUiHandler { |
|
38 | impl ChgUiHandler { | |
40 | pub fn new() -> ChgUiHandler { |
|
39 | pub fn new() -> ChgUiHandler { | |
@@ -57,7 +56,7 b' impl SystemHandler for ChgUiHandler {' | |||||
57 | // otherwise the server won't get SIGPIPE if it does not write |
|
56 | // otherwise the server won't get SIGPIPE if it does not write | |
58 | // anything. (issue5278) |
|
57 | // anything. (issue5278) | |
59 | // kill(peerpid, SIGPIPE); |
|
58 | // kill(peerpid, SIGPIPE); | |
60 |
tokio::spawn(pager.map(|_| ()).map_err(|_| ())); |
|
59 | tokio::spawn(pager.map(|_| ()).map_err(|_| ())); // just ignore errors | |
61 | Ok((self, pin)) |
|
60 | Ok((self, pin)) | |
62 | } |
|
61 | } | |
63 |
|
62 | |||
@@ -67,7 +66,9 b' impl SystemHandler for ChgUiHandler {' | |||||
67 | .into_future() |
|
66 | .into_future() | |
68 | .flatten() |
|
67 | .flatten() | |
69 | .map(|status| { |
|
68 | .map(|status| { | |
70 | let code = status.code().or_else(|| status.signal().map(|n| -n)) |
|
69 | let code = status | |
|
70 | .code() | |||
|
71 | .or_else(|| status.signal().map(|n| -n)) | |||
71 | .expect("either exit code or signal should be set"); |
|
72 | .expect("either exit code or signal should be set"); | |
72 | (self, code) |
|
73 | (self, code) | |
73 | }); |
|
74 | }); | |
@@ -84,4 +85,4 b' fn new_shell_command(spec: &CommandSpec)' | |||||
84 | .env_clear() |
|
85 | .env_clear() | |
85 | .envs(spec.envs.iter().cloned()); |
|
86 | .envs(spec.envs.iter().cloned()); | |
86 | builder |
|
87 | builder | |
87 |
|
|
88 | } |
@@ -170,8 +170,8 b" pub struct StatusResult<'a> {" | |||||
170 | pub removed: Vec<&'a HgPath>, |
|
170 | pub removed: Vec<&'a HgPath>, | |
171 | pub deleted: Vec<&'a HgPath>, |
|
171 | pub deleted: Vec<&'a HgPath>, | |
172 | pub clean: Vec<&'a HgPath>, |
|
172 | pub clean: Vec<&'a HgPath>, | |
173 |
/ |
|
173 | /* TODO ignored | |
174 |
|
|
174 | * TODO unknown */ | |
175 | } |
|
175 | } | |
176 |
|
176 | |||
177 | fn build_response( |
|
177 | fn build_response( |
@@ -8,7 +8,6 b'' | |||||
8 | //! Bindings for the `hg::status` module provided by the |
|
8 | //! Bindings for the `hg::status` module provided by the | |
9 | //! `hg-core` crate. From Python, this will be seen as |
|
9 | //! `hg-core` crate. From Python, this will be seen as | |
10 | //! `rustext.dirstate.status`. |
|
10 | //! `rustext.dirstate.status`. | |
11 | //! |
|
|||
12 |
|
11 | |||
13 | use crate::dirstate::DirstateMap; |
|
12 | use crate::dirstate::DirstateMap; | |
14 | use cpython::exc::ValueError; |
|
13 | use cpython::exc::ValueError; |
@@ -10,7 +10,6 b'' | |||||
10 | //! `hg-core` crate. From Python, this will be seen as `rustext.filepatterns` |
|
10 | //! `hg-core` crate. From Python, this will be seen as `rustext.filepatterns` | |
11 | //! and can be used as replacement for the the pure `filepatterns` Python |
|
11 | //! and can be used as replacement for the the pure `filepatterns` Python | |
12 | //! module. |
|
12 | //! module. | |
13 | //! |
|
|||
14 | use crate::exceptions::{PatternError, PatternFileError}; |
|
13 | use crate::exceptions::{PatternError, PatternFileError}; | |
15 | use cpython::{ |
|
14 | use cpython::{ | |
16 | PyBytes, PyDict, PyModule, PyObject, PyResult, PyTuple, Python, ToPyObject, |
|
15 | PyBytes, PyDict, PyModule, PyObject, PyResult, PyTuple, Python, ToPyObject, |
@@ -9,7 +9,6 b'' | |||||
9 | //! `hg-core` package. |
|
9 | //! `hg-core` package. | |
10 | //! |
|
10 | //! | |
11 | //! From Python, this will be seen as `mercurial.rustext.parsers` |
|
11 | //! From Python, this will be seen as `mercurial.rustext.parsers` | |
12 | //! |
|
|||
13 | use cpython::{ |
|
12 | use cpython::{ | |
14 | exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyResult, PyTuple, Python, |
|
13 | exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyResult, PyTuple, Python, | |
15 | PythonObject, ToPyObject, |
|
14 | PythonObject, ToPyObject, |
@@ -18,9 +18,8 b' struct PythonConfig {' | |||||
18 | fn get_python_config() -> PythonConfig { |
|
18 | fn get_python_config() -> PythonConfig { | |
19 | // The python27-sys crate exports a Cargo variable defining the full |
|
19 | // The python27-sys crate exports a Cargo variable defining the full | |
20 | // path to the interpreter being used. |
|
20 | // path to the interpreter being used. | |
21 |
let python = env::var("DEP_PYTHON27_PYTHON_INTERPRETER") |
|
21 | let python = env::var("DEP_PYTHON27_PYTHON_INTERPRETER") | |
22 |
"Missing DEP_PYTHON27_PYTHON_INTERPRETER; bad python27-sys crate?" |
|
22 | .expect("Missing DEP_PYTHON27_PYTHON_INTERPRETER; bad python27-sys crate?"); | |
23 | ); |
|
|||
24 |
|
23 | |||
25 | if !Path::new(&python).exists() { |
|
24 | if !Path::new(&python).exists() { | |
26 | panic!( |
|
25 | panic!( | |
@@ -33,8 +32,8 b' fn get_python_config() -> PythonConfig {' | |||||
33 | let separator = "SEPARATOR STRING"; |
|
32 | let separator = "SEPARATOR STRING"; | |
34 |
|
33 | |||
35 | let script = "import sysconfig; \ |
|
34 | let script = "import sysconfig; \ | |
36 | c = sysconfig.get_config_vars(); \ |
|
35 | c = sysconfig.get_config_vars(); \ | |
37 | print('SEPARATOR STRING'.join('%s=%s' % i for i in c.items()))"; |
|
36 | print('SEPARATOR STRING'.join('%s=%s' % i for i in c.items()))"; | |
38 |
|
37 | |||
39 | let mut command = Command::new(&python); |
|
38 | let mut command = Command::new(&python); | |
40 | command.arg("-c").arg(script); |
|
39 | command.arg("-c").arg(script); |
@@ -5,18 +5,18 b'' | |||||
5 | // This software may be used and distributed according to the terms of the |
|
5 | // This software may be used and distributed according to the terms of the | |
6 | // GNU General Public License version 2 or any later version. |
|
6 | // GNU General Public License version 2 or any later version. | |
7 |
|
7 | |||
|
8 | extern crate cpython; | |||
8 | extern crate libc; |
|
9 | extern crate libc; | |
9 | extern crate cpython; |
|
|||
10 | extern crate python27_sys; |
|
10 | extern crate python27_sys; | |
11 |
|
11 | |||
12 | use cpython::{NoArgs, ObjectProtocol, PyModule, PyResult, Python}; |
|
12 | use cpython::{NoArgs, ObjectProtocol, PyModule, PyResult, Python}; | |
13 | use libc::{c_char, c_int}; |
|
13 | use libc::{c_char, c_int}; | |
14 |
|
14 | |||
15 | use std::env; |
|
15 | use std::env; | |
16 | use std::path::PathBuf; |
|
|||
17 | use std::ffi::{CString, OsStr}; |
|
16 | use std::ffi::{CString, OsStr}; | |
18 | #[cfg(target_family = "unix")] |
|
17 | #[cfg(target_family = "unix")] | |
19 | use std::os::unix::ffi::{OsStrExt, OsStringExt}; |
|
18 | use std::os::unix::ffi::{OsStrExt, OsStringExt}; | |
|
19 | use std::path::PathBuf; | |||
20 |
|
20 | |||
21 | #[derive(Debug)] |
|
21 | #[derive(Debug)] | |
22 | struct Environment { |
|
22 | struct Environment { |
General Comments 0
You need to be logged in to leave comments.
Login now