Show More
@@ -21,10 +21,11 b' use tokio_process::{Child, CommandExt};' | |||||
21 | use tokio_timer; |
|
21 | use tokio_timer; | |
22 |
|
22 | |||
23 | use super::clientext::ChgClientExt; |
|
23 | use super::clientext::ChgClientExt; | |
24 | use super::message::ServerSpec; |
|
24 | use super::message::{Instruction, ServerSpec}; | |
25 | use super::procutil; |
|
25 | use super::procutil; | |
26 |
|
26 | |||
27 |
const REQUIRED_SERVER_CAPABILITIES: &[&str] = |
|
27 | const REQUIRED_SERVER_CAPABILITIES: &[&str] = | |
|
28 | &["attachio", "chdir", "runcommand", "setenv", "validate"]; | |||
28 |
|
29 | |||
29 | /// Helper to connect to and spawn a server process. |
|
30 | /// Helper to connect to and spawn a server process. | |
30 | #[derive(Clone, Debug)] |
|
31 | #[derive(Clone, Debug)] | |
@@ -35,6 +36,7 b' pub struct Locator {' | |||||
35 | env_vars: Vec<(OsString, OsString)>, |
|
36 | env_vars: Vec<(OsString, OsString)>, | |
36 | process_id: u32, |
|
37 | process_id: u32, | |
37 | base_sock_path: PathBuf, |
|
38 | base_sock_path: PathBuf, | |
|
39 | redirect_sock_path: Option<PathBuf>, | |||
38 | timeout: Duration, |
|
40 | timeout: Duration, | |
39 | } |
|
41 | } | |
40 |
|
42 | |||
@@ -51,6 +53,7 b' impl Locator {' | |||||
51 | env_vars: env::vars_os().collect(), |
|
53 | env_vars: env::vars_os().collect(), | |
52 | process_id: process::id(), |
|
54 | process_id: process::id(), | |
53 | base_sock_path: prepare_server_socket_path()?, |
|
55 | base_sock_path: prepare_server_socket_path()?, | |
|
56 | redirect_sock_path: None, | |||
54 | timeout: default_timeout(), |
|
57 | timeout: default_timeout(), | |
55 | }) |
|
58 | }) | |
56 | } |
|
59 | } | |
@@ -77,16 +80,110 b' impl Locator {' | |||||
77 | /// |
|
80 | /// | |
78 | /// The server process will be spawned if not running. |
|
81 | /// The server process will be spawned if not running. | |
79 | pub fn connect(self) -> impl Future<Item = (Self, UnixClient), Error = io::Error> { |
|
82 | pub fn connect(self) -> impl Future<Item = (Self, UnixClient), Error = io::Error> { | |
80 | self.try_connect() |
|
83 | future::loop_fn((self, 0), |(loc, cnt)| { | |
|
84 | if cnt < 10 { | |||
|
85 | let fut = loc | |||
|
86 | .try_connect() | |||
|
87 | .and_then(|(loc, client)| { | |||
|
88 | client | |||
|
89 | .validate(&loc.hg_early_args) | |||
|
90 | .map(|(client, instructions)| (loc, client, instructions)) | |||
|
91 | }) | |||
|
92 | .and_then(move |(loc, client, instructions)| { | |||
|
93 | loc.run_instructions(client, instructions, cnt) | |||
|
94 | }); | |||
|
95 | Either::A(fut) | |||
|
96 | } else { | |||
|
97 | let msg = format!( | |||
|
98 | concat!( | |||
|
99 | "too many redirections.\n", | |||
|
100 | "Please make sure {:?} is not a wrapper which ", | |||
|
101 | "changes sensitive environment variables ", | |||
|
102 | "before executing hg. If you have to use a ", | |||
|
103 | "wrapper, wrap chg instead of hg.", | |||
|
104 | ), | |||
|
105 | loc.hg_command | |||
|
106 | ); | |||
|
107 | Either::B(future::err(io::Error::new(io::ErrorKind::Other, msg))) | |||
|
108 | } | |||
|
109 | }) | |||
|
110 | } | |||
|
111 | ||||
|
112 | /// Runs instructions received from the server. | |||
|
113 | fn run_instructions( | |||
|
114 | mut self, | |||
|
115 | client: UnixClient, | |||
|
116 | instructions: Vec<Instruction>, | |||
|
117 | cnt: usize, | |||
|
118 | ) -> io::Result<Loop<(Self, UnixClient), (Self, usize)>> { | |||
|
119 | let mut reconnect = false; | |||
|
120 | for inst in instructions { | |||
|
121 | debug!("instruction: {:?}", inst); | |||
|
122 | match inst { | |||
|
123 | Instruction::Exit(_) => { | |||
|
124 | // Just returns the current connection to run the | |||
|
125 | // unparsable command and report the error | |||
|
126 | return Ok(Loop::Break((self, client))); | |||
|
127 | } | |||
|
128 | Instruction::Reconnect => { | |||
|
129 | reconnect = true; | |||
|
130 | } | |||
|
131 | Instruction::Redirect(path) => { | |||
|
132 | if path.parent() != self.base_sock_path.parent() { | |||
|
133 | let msg = format!( | |||
|
134 | "insecure redirect instruction from server: {}", | |||
|
135 | path.display() | |||
|
136 | ); | |||
|
137 | return Err(io::Error::new(io::ErrorKind::InvalidData, msg)); | |||
|
138 | } | |||
|
139 | self.redirect_sock_path = Some(path); | |||
|
140 | reconnect = true; | |||
|
141 | } | |||
|
142 | Instruction::Unlink(path) => { | |||
|
143 | if path.parent() != self.base_sock_path.parent() { | |||
|
144 | let msg = format!( | |||
|
145 | "insecure unlink instruction from server: {}", | |||
|
146 | path.display() | |||
|
147 | ); | |||
|
148 | return Err(io::Error::new(io::ErrorKind::InvalidData, msg)); | |||
|
149 | } | |||
|
150 | fs::remove_file(path).unwrap_or(()); // may race | |||
|
151 | } | |||
|
152 | } | |||
|
153 | } | |||
|
154 | ||||
|
155 | if reconnect { | |||
|
156 | Ok(Loop::Continue((self, cnt + 1))) | |||
|
157 | } else { | |||
|
158 | Ok(Loop::Break((self, client))) | |||
|
159 | } | |||
81 | } |
|
160 | } | |
82 |
|
161 | |||
83 | /// Tries to connect to the existing server, or spawns new if not running. |
|
162 | /// Tries to connect to the existing server, or spawns new if not running. | |
84 | fn try_connect(self) -> impl Future<Item = (Self, UnixClient), Error = io::Error> { |
|
163 | fn try_connect(self) -> impl Future<Item = (Self, UnixClient), Error = io::Error> { | |
85 | debug!("try connect to {}", self.base_sock_path.display()); |
|
164 | let sock_path = self | |
86 | UnixClient::connect(self.base_sock_path.clone()) |
|
165 | .redirect_sock_path | |
87 | .then(|res| match res { |
|
166 | .as_ref() | |
88 | Ok(client) => Either::A(future::ok((self, client))), |
|
167 | .unwrap_or(&self.base_sock_path) | |
89 | Err(_) => Either::B(self.spawn_connect()), |
|
168 | .clone(); | |
|
169 | debug!("try connect to {}", sock_path.display()); | |||
|
170 | UnixClient::connect(sock_path) | |||
|
171 | .then(|res| { | |||
|
172 | match res { | |||
|
173 | Ok(client) => Either::A(future::ok((self, client))), | |||
|
174 | Err(_) => { | |||
|
175 | // Prevent us from being re-connected to the outdated | |||
|
176 | // master server: We were told by the server to redirect | |||
|
177 | // to redirect_sock_path, which didn't work. We do not | |||
|
178 | // want to connect to the same master server again | |||
|
179 | // because it would probably tell us the same thing. | |||
|
180 | if self.redirect_sock_path.is_some() { | |||
|
181 | fs::remove_file(&self.base_sock_path).unwrap_or(()); | |||
|
182 | // may race | |||
|
183 | } | |||
|
184 | Either::B(self.spawn_connect()) | |||
|
185 | } | |||
|
186 | } | |||
90 | }) |
|
187 | }) | |
91 | .and_then(|(loc, client)| { |
|
188 | .and_then(|(loc, client)| { | |
92 | check_server_capabilities(client.server_spec())?; |
|
189 | check_server_capabilities(client.server_spec())?; |
General Comments 0
You need to be logged in to leave comments.
Login now