Show More
@@ -3,7 +3,7 b' clang-format:command = clang-format --st' | |||||
3 | clang-format:pattern = set:(**.c or **.cc or **.h) and not "include:contrib/clang-format-ignorelist" |
|
3 | clang-format:pattern = set:(**.c or **.cc or **.h) and not "include:contrib/clang-format-ignorelist" | |
4 |
|
4 | |||
5 | rustfmt:command = rustfmt +nightly |
|
5 | rustfmt:command = rustfmt +nightly | |
6 | rustfmt:pattern = set:**.rs |
|
6 | rustfmt:pattern = set:"**.rs" - "mercurial/thirdparty/**" | |
7 |
|
7 | |||
8 | black:command = black --config=black.toml - |
|
8 | black:command = black --config=black.toml - | |
9 | black:pattern = set:**.py - mercurial/thirdparty/** |
|
9 | black:pattern = set:**.py - mercurial/thirdparty/** |
@@ -43,11 +43,20 b' impl ChgClient {' | |||||
43 | stdout: &impl AsRawFd, |
|
43 | stdout: &impl AsRawFd, | |
44 | stderr: &impl AsRawFd, |
|
44 | stderr: &impl AsRawFd, | |
45 | ) -> io::Result<()> { |
|
45 | ) -> io::Result<()> { | |
46 | attachio::attach_io(self.client.borrow_protocol_mut(), stdin, stdout, stderr).await |
|
46 | attachio::attach_io( | |
|
47 | self.client.borrow_protocol_mut(), | |||
|
48 | stdin, | |||
|
49 | stdout, | |||
|
50 | stderr, | |||
|
51 | ) | |||
|
52 | .await | |||
47 | } |
|
53 | } | |
48 |
|
54 | |||
49 | /// Changes the working directory of the server. |
|
55 | /// Changes the working directory of the server. | |
50 | pub async fn set_current_dir(&mut self, dir: impl AsRef<Path>) -> io::Result<()> { |
|
56 | pub async fn set_current_dir( | |
|
57 | &mut self, | |||
|
58 | dir: impl AsRef<Path>, | |||
|
59 | ) -> io::Result<()> { | |||
51 | let dir_bytes = dir.as_ref().as_os_str().as_bytes().to_owned(); |
|
60 | let dir_bytes = dir.as_ref().as_os_str().as_bytes().to_owned(); | |
52 | self.client |
|
61 | self.client | |
53 | .borrow_protocol_mut() |
|
62 | .borrow_protocol_mut() | |
@@ -67,7 +76,10 b' impl ChgClient {' | |||||
67 | } |
|
76 | } | |
68 |
|
77 | |||
69 | /// Changes the process title of the server. |
|
78 | /// Changes the process title of the server. | |
70 | pub async fn set_process_name(&mut self, name: impl AsRef<OsStr>) -> io::Result<()> { |
|
79 | pub async fn set_process_name( | |
|
80 | &mut self, | |||
|
81 | name: impl AsRef<OsStr>, | |||
|
82 | ) -> io::Result<()> { | |||
71 | let name_bytes = name.as_ref().as_bytes().to_owned(); |
|
83 | let name_bytes = name.as_ref().as_bytes().to_owned(); | |
72 | self.client |
|
84 | self.client | |
73 | .borrow_protocol_mut() |
|
85 | .borrow_protocol_mut() |
@@ -71,8 +71,12 b' impl Locator {' | |||||
71 | } |
|
71 | } | |
72 |
|
72 | |||
73 | /// Specifies the arguments to be passed to the server at start. |
|
73 | /// Specifies the arguments to be passed to the server at start. | |
74 | pub fn set_early_args(&mut self, args: impl IntoIterator<Item = impl AsRef<OsStr>>) { |
|
74 | pub fn set_early_args( | |
75 | self.hg_early_args = args.into_iter().map(|a| a.as_ref().to_owned()).collect(); |
|
75 | &mut self, | |
|
76 | args: impl IntoIterator<Item = impl AsRef<OsStr>>, | |||
|
77 | ) { | |||
|
78 | self.hg_early_args = | |||
|
79 | args.into_iter().map(|a| a.as_ref().to_owned()).collect(); | |||
76 | } |
|
80 | } | |
77 |
|
81 | |||
78 | /// Connects to the server. |
|
82 | /// Connects to the server. | |
@@ -104,7 +108,10 b' impl Locator {' | |||||
104 | /// Runs instructions received from the server. |
|
108 | /// Runs instructions received from the server. | |
105 | /// |
|
109 | /// | |
106 | /// Returns true if the client should try connecting to the other server. |
|
110 | /// Returns true if the client should try connecting to the other server. | |
107 | fn run_instructions(&mut self, instructions: &[Instruction]) -> io::Result<bool> { |
|
111 | fn run_instructions( | |
|
112 | &mut self, | |||
|
113 | instructions: &[Instruction], | |||
|
114 | ) -> io::Result<bool> { | |||
108 | let mut reconnect = false; |
|
115 | let mut reconnect = false; | |
109 | for inst in instructions { |
|
116 | for inst in instructions { | |
110 | debug!("instruction: {:?}", inst); |
|
117 | debug!("instruction: {:?}", inst); | |
@@ -123,7 +130,10 b' impl Locator {' | |||||
123 | "insecure redirect instruction from server: {}", |
|
130 | "insecure redirect instruction from server: {}", | |
124 | path.display() |
|
131 | path.display() | |
125 | ); |
|
132 | ); | |
126 |
return Err(io::Error::new( |
|
133 | return Err(io::Error::new( | |
|
134 | io::ErrorKind::InvalidData, | |||
|
135 | msg, | |||
|
136 | )); | |||
127 | } |
|
137 | } | |
128 | self.redirect_sock_path = Some(path.to_owned()); |
|
138 | self.redirect_sock_path = Some(path.to_owned()); | |
129 | reconnect = true; |
|
139 | reconnect = true; | |
@@ -134,7 +144,10 b' impl Locator {' | |||||
134 | "insecure unlink instruction from server: {}", |
|
144 | "insecure unlink instruction from server: {}", | |
135 | path.display() |
|
145 | path.display() | |
136 | ); |
|
146 | ); | |
137 |
return Err(io::Error::new( |
|
147 | return Err(io::Error::new( | |
|
148 | io::ErrorKind::InvalidData, | |||
|
149 | msg, | |||
|
150 | )); | |||
138 | } |
|
151 | } | |
139 | fs::remove_file(path).unwrap_or(()); // may race |
|
152 | fs::remove_file(path).unwrap_or(()); // may race | |
140 | } |
|
153 | } | |
@@ -319,7 +332,10 b' where' | |||||
319 | P: AsRef<Path>, |
|
332 | P: AsRef<Path>, | |
320 | { |
|
333 | { | |
321 | let a = fs::symlink_metadata(path.as_ref())?; |
|
334 | let a = fs::symlink_metadata(path.as_ref())?; | |
322 | if a.is_dir() && a.uid() == procutil::get_effective_uid() && (a.mode() & 0o777) == 0o700 { |
|
335 | if a.is_dir() | |
|
336 | && a.uid() == procutil::get_effective_uid() | |||
|
337 | && (a.mode() & 0o777) == 0o700 | |||
|
338 | { | |||
323 | Ok(path) |
|
339 | Ok(path) | |
324 | } else { |
|
340 | } else { | |
325 | Err(io::Error::new(io::ErrorKind::Other, "insecure directory")) |
|
341 | Err(io::Error::new(io::ErrorKind::Other, "insecure directory")) | |
@@ -344,7 +360,9 b' fn check_server_capabilities(spec: &Serv' | |||||
344 | } |
|
360 | } | |
345 |
|
361 | |||
346 | /// Collects arguments which need to be passed to the server at start. |
|
362 | /// Collects arguments which need to be passed to the server at start. | |
347 | pub fn collect_early_args(args: impl IntoIterator<Item = impl AsRef<OsStr>>) -> Vec<OsString> { |
|
363 | pub fn collect_early_args( | |
|
364 | args: impl IntoIterator<Item = impl AsRef<OsStr>>, | |||
|
365 | ) -> Vec<OsString> { | |||
348 | let mut args_iter = args.into_iter(); |
|
366 | let mut args_iter = args.into_iter(); | |
349 | let mut early_args = Vec::new(); |
|
367 | let mut early_args = Vec::new(); | |
350 | while let Some(arg) = args_iter.next() { |
|
368 | while let Some(arg) = args_iter.next() { |
@@ -32,11 +32,16 b' pub struct CommandSpec {' | |||||
32 | } |
|
32 | } | |
33 |
|
33 | |||
34 | /// Parses "S" channel request into command type and spec. |
|
34 | /// Parses "S" channel request into command type and spec. | |
35 | pub fn parse_command_spec(data: Bytes) -> io::Result<(CommandType, CommandSpec)> { |
|
35 | pub fn parse_command_spec( | |
|
36 | data: Bytes, | |||
|
37 | ) -> io::Result<(CommandType, CommandSpec)> { | |||
36 | let mut split = data.split(|&c| c == b'\0'); |
|
38 | let mut split = data.split(|&c| c == b'\0'); | |
37 | let ctype = parse_command_type(split.next().ok_or(new_parse_error("missing type"))?)?; |
|
39 | let ctype = parse_command_type( | |
|
40 | split.next().ok_or(new_parse_error("missing type"))?, | |||
|
41 | )?; | |||
38 | let command = split.next().ok_or(new_parse_error("missing command"))?; |
|
42 | let command = split.next().ok_or(new_parse_error("missing command"))?; | |
39 | let current_dir = split.next().ok_or(new_parse_error("missing current dir"))?; |
|
43 | let current_dir = | |
|
44 | split.next().ok_or(new_parse_error("missing current dir"))?; | |||
40 |
|
45 | |||
41 | let mut envs = Vec::new(); |
|
46 | let mut envs = Vec::new(); | |
42 | for l in split { |
|
47 | for l in split { | |
@@ -89,14 +94,21 b' pub fn parse_instructions(data: Bytes) -' | |||||
89 | (b"exit", Some(arg)) => decode_latin1(arg) |
|
94 | (b"exit", Some(arg)) => decode_latin1(arg) | |
90 | .parse() |
|
95 | .parse() | |
91 | .map(Instruction::Exit) |
|
96 | .map(Instruction::Exit) | |
92 | .map_err(|_| new_parse_error(format!("invalid exit code: {:?}", arg)))?, |
|
97 | .map_err(|_| { | |
|
98 | new_parse_error(format!("invalid exit code: {:?}", arg)) | |||
|
99 | })?, | |||
93 | (b"reconnect", None) => Instruction::Reconnect, |
|
100 | (b"reconnect", None) => Instruction::Reconnect, | |
94 | (b"redirect", Some(arg)) => { |
|
101 | (b"redirect", Some(arg)) => { | |
95 | Instruction::Redirect(OsStr::from_bytes(arg).to_owned().into()) |
|
102 | Instruction::Redirect(OsStr::from_bytes(arg).to_owned().into()) | |
96 | } |
|
103 | } | |
97 | (b"unlink", Some(arg)) => Instruction::Unlink(OsStr::from_bytes(arg).to_owned().into()), |
|
104 | (b"unlink", Some(arg)) => { | |
|
105 | Instruction::Unlink(OsStr::from_bytes(arg).to_owned().into()) | |||
|
106 | } | |||
98 | _ => { |
|
107 | _ => { | |
99 |
return Err(new_parse_error(format!( |
|
108 | return Err(new_parse_error(format!( | |
|
109 | "unknown command: {:?}", | |||
|
110 | l | |||
|
111 | ))); | |||
100 | } |
|
112 | } | |
101 | }; |
|
113 | }; | |
102 | instructions.push(inst); |
|
114 | instructions.push(inst); | |
@@ -118,7 +130,8 b' pub fn pack_env_vars_os(' | |||||
118 | ) -> Bytes { |
|
130 | ) -> Bytes { | |
119 | let mut vars_iter = vars.into_iter(); |
|
131 | let mut vars_iter = vars.into_iter(); | |
120 | if let Some((k, v)) = vars_iter.next() { |
|
132 | if let Some((k, v)) = vars_iter.next() { | |
121 | let mut dst = BytesMut::with_capacity(INITIAL_PACKED_ENV_VARS_CAPACITY); |
|
133 | let mut dst = | |
|
134 | BytesMut::with_capacity(INITIAL_PACKED_ENV_VARS_CAPACITY); | |||
122 | pack_env_into(&mut dst, k.as_ref(), v.as_ref()); |
|
135 | pack_env_into(&mut dst, k.as_ref(), v.as_ref()); | |
123 | for (k, v) in vars_iter { |
|
136 | for (k, v) in vars_iter { | |
124 | dst.reserve(1); |
|
137 | dst.reserve(1); | |
@@ -145,7 +158,9 b' fn decode_latin1(s: impl AsRef<[u8]>) ->' | |||||
145 | s.as_ref().iter().map(|&c| c as char).collect() |
|
158 | s.as_ref().iter().map(|&c| c as char).collect() | |
146 | } |
|
159 | } | |
147 |
|
160 | |||
148 | fn new_parse_error(error: impl Into<Box<dyn error::Error + Send + Sync>>) -> io::Error { |
|
161 | fn new_parse_error( | |
|
162 | error: impl Into<Box<dyn error::Error + Send + Sync>>, | |||
|
163 | ) -> io::Error { | |||
149 | io::Error::new(io::ErrorKind::InvalidData, error) |
|
164 | io::Error::new(io::ErrorKind::InvalidData, error) | |
150 | } |
|
165 | } | |
151 |
|
166 | |||
@@ -183,17 +198,24 b' mod tests {' | |||||
183 | fn parse_command_spec_too_short() { |
|
198 | fn parse_command_spec_too_short() { | |
184 | assert!(parse_command_spec(Bytes::from_static(b"")).is_err()); |
|
199 | assert!(parse_command_spec(Bytes::from_static(b"")).is_err()); | |
185 | assert!(parse_command_spec(Bytes::from_static(b"pager")).is_err()); |
|
200 | assert!(parse_command_spec(Bytes::from_static(b"pager")).is_err()); | |
186 | assert!(parse_command_spec(Bytes::from_static(b"pager\0less")).is_err()); |
|
201 | assert!( | |
|
202 | parse_command_spec(Bytes::from_static(b"pager\0less")).is_err() | |||
|
203 | ); | |||
187 | } |
|
204 | } | |
188 |
|
205 | |||
189 | #[test] |
|
206 | #[test] | |
190 | fn parse_command_spec_malformed_env() { |
|
207 | fn parse_command_spec_malformed_env() { | |
191 |
assert!(parse_command_spec(Bytes::from_static( |
|
208 | assert!(parse_command_spec(Bytes::from_static( | |
|
209 | b"pager\0less\0/tmp\0HOME" | |||
|
210 | )) | |||
|
211 | .is_err()); | |||
192 | } |
|
212 | } | |
193 |
|
213 | |||
194 | #[test] |
|
214 | #[test] | |
195 | fn parse_command_spec_unknown_type() { |
|
215 | fn parse_command_spec_unknown_type() { | |
196 | assert!(parse_command_spec(Bytes::from_static(b"paper\0less")).is_err()); |
|
216 | assert!( | |
|
217 | parse_command_spec(Bytes::from_static(b"paper\0less")).is_err() | |||
|
218 | ); | |||
197 | } |
|
219 | } | |
198 |
|
220 | |||
199 | #[test] |
|
221 | #[test] |
@@ -44,7 +44,8 b' pub fn set_blocking_fd(fd: RawFd) -> io:' | |||||
44 | if flags < 0 { |
|
44 | if flags < 0 { | |
45 | return Err(io::Error::last_os_error()); |
|
45 | return Err(io::Error::last_os_error()); | |
46 | } |
|
46 | } | |
47 | let r = unsafe { libc::fcntl(fd, libc::F_SETFL, flags & !libc::O_NONBLOCK) }; |
|
47 | let r = | |
|
48 | unsafe { libc::fcntl(fd, libc::F_SETFL, flags & !libc::O_NONBLOCK) }; | |||
48 | if r < 0 { |
|
49 | if r < 0 { | |
49 | return Err(io::Error::last_os_error()); |
|
50 | return Err(io::Error::last_os_error()); | |
50 | } |
|
51 | } | |
@@ -69,7 +70,10 b' static RESTORE_SIGNAL_HANDLER: sync::Onc' | |||||
69 | /// |
|
70 | /// | |
70 | /// This touches global states, and thus synchronized as a one-time |
|
71 | /// This touches global states, and thus synchronized as a one-time | |
71 | /// initialization function. |
|
72 | /// initialization function. | |
72 | pub fn setup_signal_handler_once(pid: u32, pgid: Option<u32>) -> io::Result<()> { |
|
73 | pub fn setup_signal_handler_once( | |
|
74 | pid: u32, | |||
|
75 | pgid: Option<u32>, | |||
|
76 | ) -> io::Result<()> { | |||
73 | let pid_signed = pid as i32; |
|
77 | let pid_signed = pid as i32; | |
74 | let pgid_signed = pgid.map(|n| n as i32).unwrap_or(0); |
|
78 | let pgid_signed = pgid.map(|n| n as i32).unwrap_or(0); | |
75 | let mut r = 0; |
|
79 | let mut r = 0; |
@@ -36,7 +36,8 b' pub async fn run_command(' | |||||
36 | ChannelMessage::Data(..) => { |
|
36 | ChannelMessage::Data(..) => { | |
37 | // just ignores data sent to optional channel |
|
37 | // just ignores data sent to optional channel | |
38 | } |
|
38 | } | |
39 |
ChannelMessage::InputRequest(..) |
|
39 | ChannelMessage::InputRequest(..) | |
|
40 | | ChannelMessage::LineRequest(..) => { | |||
40 | return Err(io::Error::new( |
|
41 | return Err(io::Error::new( | |
41 | io::ErrorKind::InvalidData, |
|
42 | io::ErrorKind::InvalidData, | |
42 | "unsupported request", |
|
43 | "unsupported request", | |
@@ -49,7 +50,8 b' pub async fn run_command(' | |||||
49 | // server spins new command loop while pager request is |
|
50 | // server spins new command loop while pager request is | |
50 | // in progress, which can be terminated by "" command. |
|
51 | // in progress, which can be terminated by "" command. | |
51 | let pin = handler.spawn_pager(&cmd_spec).await?; |
|
52 | let pin = handler.spawn_pager(&cmd_spec).await?; | |
52 |
attachio::attach_io(proto, &io::stdin(), &pin, &pin) |
|
53 | attachio::attach_io(proto, &io::stdin(), &pin, &pin) | |
|
54 | .await?; | |||
53 | proto.send_command("").await?; // terminator |
|
55 | proto.send_command("").await?; // terminator | |
54 | } |
|
56 | } | |
55 | CommandType::System => { |
|
57 | CommandType::System => { |
@@ -22,7 +22,10 b' pub trait SystemHandler {' | |||||
22 | /// Handles pager command request. |
|
22 | /// Handles pager command request. | |
23 | /// |
|
23 | /// | |
24 | /// Returns the pipe to be attached to the server if the pager is spawned. |
|
24 | /// Returns the pipe to be attached to the server if the pager is spawned. | |
25 | async fn spawn_pager(&mut self, spec: &CommandSpec) -> io::Result<Self::PagerStdin>; |
|
25 | async fn spawn_pager( | |
|
26 | &mut self, | |||
|
27 | spec: &CommandSpec, | |||
|
28 | ) -> io::Result<Self::PagerStdin>; | |||
26 |
|
29 | |||
27 | /// Handles system command request. |
|
30 | /// Handles system command request. | |
28 | /// |
|
31 | /// | |
@@ -53,8 +56,12 b' impl ChgUiHandler {' | |||||
53 | impl SystemHandler for ChgUiHandler { |
|
56 | impl SystemHandler for ChgUiHandler { | |
54 | type PagerStdin = ChildStdin; |
|
57 | type PagerStdin = ChildStdin; | |
55 |
|
58 | |||
56 | async fn spawn_pager(&mut self, spec: &CommandSpec) -> io::Result<Self::PagerStdin> { |
|
59 | async fn spawn_pager( | |
57 | let mut pager = new_shell_command(&spec).stdin(Stdio::piped()).spawn()?; |
|
60 | &mut self, | |
|
61 | spec: &CommandSpec, | |||
|
62 | ) -> io::Result<Self::PagerStdin> { | |||
|
63 | let mut pager = | |||
|
64 | new_shell_command(&spec).stdin(Stdio::piped()).spawn()?; | |||
58 | let pin = pager.stdin.take().unwrap(); |
|
65 | let pin = pager.stdin.take().unwrap(); | |
59 | procutil::set_blocking_fd(pin.as_raw_fd())?; |
|
66 | procutil::set_blocking_fd(pin.as_raw_fd())?; | |
60 | // TODO: if pager exits, notify the server with SIGPIPE immediately. |
|
67 | // TODO: if pager exits, notify the server with SIGPIPE immediately. |
@@ -5,7 +5,9 b'' | |||||
5 | /*! Build script to integrate PyOxidizer. */ |
|
5 | /*! Build script to integrate PyOxidizer. */ | |
6 |
|
6 | |||
7 | fn main() { |
|
7 | fn main() { | |
8 | if let Ok(config_rs) = std::env::var("DEP_PYTHONXY_DEFAULT_PYTHON_CONFIG_RS") { |
|
8 | if let Ok(config_rs) = | |
|
9 | std::env::var("DEP_PYTHONXY_DEFAULT_PYTHON_CONFIG_RS") | |||
|
10 | { | |||
9 | println!( |
|
11 | println!( | |
10 | "cargo:rustc-env=PYOXIDIZER_DEFAULT_PYTHON_CONFIG_RS={}", |
|
12 | "cargo:rustc-env=PYOXIDIZER_DEFAULT_PYTHON_CONFIG_RS={}", | |
11 | config_rs |
|
13 | config_rs |
@@ -9,21 +9,22 b' use pyembed::MainPythonInterpreter;' | |||||
9 | include!(env!("PYOXIDIZER_DEFAULT_PYTHON_CONFIG_RS")); |
|
9 | include!(env!("PYOXIDIZER_DEFAULT_PYTHON_CONFIG_RS")); | |
10 |
|
10 | |||
11 | fn main() { |
|
11 | fn main() { | |
12 |
// The following code is in a block so the MainPythonInterpreter is |
|
12 | // The following code is in a block so the MainPythonInterpreter is | |
13 | // orderly manner, before process exit. |
|
13 | // destroyed in an orderly manner, before process exit. | |
14 | let code = { |
|
14 | let code = { | |
15 |
// Load the default Python configuration as derived by the PyOxidizer |
|
15 | // Load the default Python configuration as derived by the PyOxidizer | |
16 | // file used at build time. |
|
16 | // config file used at build time. | |
17 | let config = default_python_config(); |
|
17 | let config = default_python_config(); | |
18 |
|
18 | |||
19 |
// Construct a new Python interpreter using that config, handling any |
|
19 | // Construct a new Python interpreter using that config, handling any | |
20 | // from construction. |
|
20 | // errors from construction. | |
21 | match MainPythonInterpreter::new(config) { |
|
21 | match MainPythonInterpreter::new(config) { | |
22 | Ok(mut interp) => { |
|
22 | Ok(mut interp) => { | |
23 |
// And run it using the default run configuration as specified |
|
23 | // And run it using the default run configuration as specified | |
24 |
// configuration. If an uncaught Python |
|
24 | // by the configuration. If an uncaught Python | |
25 | // This includes the special SystemExit, which is a request to terminate the |
|
25 | // exception is raised, handle it. | |
26 | // process. |
|
26 | // This includes the special SystemExit, which is a request to | |
|
27 | // terminate the process. | |||
27 | interp.run_as_main() |
|
28 | interp.run_as_main() | |
28 | } |
|
29 | } | |
29 | Err(msg) => { |
|
30 | Err(msg) => { |
@@ -1,3 +1,4 b'' | |||||
|
1 | edition = "2018" | |||
1 | max_width = 79 |
|
2 | max_width = 79 | |
2 | wrap_comments = true |
|
3 | wrap_comments = true | |
3 | error_on_line_overflow = true |
|
4 | error_on_line_overflow = true |
@@ -69,6 +69,7 b' Prevent adding new files in the root dir' | |||||
69 | hg |
|
69 | hg | |
70 | hgeditor |
|
70 | hgeditor | |
71 | hgweb.cgi |
|
71 | hgweb.cgi | |
|
72 | rustfmt.toml | |||
72 | setup.py |
|
73 | setup.py | |
73 |
|
74 | |||
74 | Prevent adding modules which could be shadowed by ancient .so/.dylib. |
|
75 | Prevent adding modules which could be shadowed by ancient .so/.dylib. |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now