##// END OF EJS Templates
rust-chg: add brief comment about initial capacity of temp_sock_path()...
Yuya Nishihara -
r45158:c11d98cf default
parent child Browse files
Show More
@@ -1,136 +1,136 b''
1 // Copyright 2011, 2018 Yuya Nishihara <yuya@tcha.org>
1 // Copyright 2011, 2018 Yuya Nishihara <yuya@tcha.org>
2 //
2 //
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 //! Utility for locating command-server process.
6 //! Utility for locating command-server process.
7
7
8 use std::env;
8 use std::env;
9 use std::ffi::{OsStr, OsString};
9 use std::ffi::{OsStr, OsString};
10 use std::fs::{self, DirBuilder};
10 use std::fs::{self, DirBuilder};
11 use std::io;
11 use std::io;
12 use std::os::unix::ffi::{OsStrExt, OsStringExt};
12 use std::os::unix::ffi::{OsStrExt, OsStringExt};
13 use std::os::unix::fs::{DirBuilderExt, MetadataExt};
13 use std::os::unix::fs::{DirBuilderExt, MetadataExt};
14 use std::path::{Path, PathBuf};
14 use std::path::{Path, PathBuf};
15 use std::process;
15 use std::process;
16 use std::time::Duration;
16 use std::time::Duration;
17
17
18 use super::procutil;
18 use super::procutil;
19
19
20 /// Helper to connect to and spawn a server process.
20 /// Helper to connect to and spawn a server process.
21 #[derive(Clone, Debug)]
21 #[derive(Clone, Debug)]
22 pub struct Locator {
22 pub struct Locator {
23 hg_command: OsString,
23 hg_command: OsString,
24 current_dir: PathBuf,
24 current_dir: PathBuf,
25 env_vars: Vec<(OsString, OsString)>,
25 env_vars: Vec<(OsString, OsString)>,
26 process_id: u32,
26 process_id: u32,
27 base_sock_path: PathBuf,
27 base_sock_path: PathBuf,
28 timeout: Duration,
28 timeout: Duration,
29 }
29 }
30
30
31 impl Locator {
31 impl Locator {
32 /// Creates locator capturing the current process environment.
32 /// Creates locator capturing the current process environment.
33 ///
33 ///
34 /// If no `$CHGSOCKNAME` is specified, the socket directory will be
34 /// If no `$CHGSOCKNAME` is specified, the socket directory will be
35 /// created as necessary.
35 /// created as necessary.
36 pub fn prepare_from_env() -> io::Result<Locator> {
36 pub fn prepare_from_env() -> io::Result<Locator> {
37 Ok(Locator {
37 Ok(Locator {
38 hg_command: default_hg_command(),
38 hg_command: default_hg_command(),
39 current_dir: env::current_dir()?,
39 current_dir: env::current_dir()?,
40 env_vars: env::vars_os().collect(),
40 env_vars: env::vars_os().collect(),
41 process_id: process::id(),
41 process_id: process::id(),
42 base_sock_path: prepare_server_socket_path()?,
42 base_sock_path: prepare_server_socket_path()?,
43 timeout: default_timeout(),
43 timeout: default_timeout(),
44 })
44 })
45 }
45 }
46
46
47 /// Temporary socket path for this client process.
47 /// Temporary socket path for this client process.
48 fn temp_sock_path(&self) -> PathBuf {
48 fn temp_sock_path(&self) -> PathBuf {
49 let src = self.base_sock_path.as_os_str().as_bytes();
49 let src = self.base_sock_path.as_os_str().as_bytes();
50 let mut buf = Vec::with_capacity(src.len() + 6);
50 let mut buf = Vec::with_capacity(src.len() + 6); // "{src}.{pid}".len()
51 buf.extend_from_slice(src);
51 buf.extend_from_slice(src);
52 buf.extend_from_slice(format!(".{}", self.process_id).as_bytes());
52 buf.extend_from_slice(format!(".{}", self.process_id).as_bytes());
53 OsString::from_vec(buf).into()
53 OsString::from_vec(buf).into()
54 }
54 }
55 }
55 }
56
56
57 /// Determines the server socket to connect to.
57 /// Determines the server socket to connect to.
58 ///
58 ///
59 /// If no `$CHGSOCKNAME` is specified, the socket directory will be created
59 /// If no `$CHGSOCKNAME` is specified, the socket directory will be created
60 /// as necessary.
60 /// as necessary.
61 pub fn prepare_server_socket_path() -> io::Result<PathBuf> {
61 pub fn prepare_server_socket_path() -> io::Result<PathBuf> {
62 if let Some(s) = env::var_os("CHGSOCKNAME") {
62 if let Some(s) = env::var_os("CHGSOCKNAME") {
63 Ok(PathBuf::from(s))
63 Ok(PathBuf::from(s))
64 } else {
64 } else {
65 let mut path = default_server_socket_dir();
65 let mut path = default_server_socket_dir();
66 create_secure_dir(&path)?;
66 create_secure_dir(&path)?;
67 path.push("server");
67 path.push("server");
68 Ok(path)
68 Ok(path)
69 }
69 }
70 }
70 }
71
71
72 /// Determines the default server socket path as follows.
72 /// Determines the default server socket path as follows.
73 ///
73 ///
74 /// 1. `$XDG_RUNTIME_DIR/chg`
74 /// 1. `$XDG_RUNTIME_DIR/chg`
75 /// 2. `$TMPDIR/chg$UID`
75 /// 2. `$TMPDIR/chg$UID`
76 /// 3. `/tmp/chg$UID`
76 /// 3. `/tmp/chg$UID`
77 pub fn default_server_socket_dir() -> PathBuf {
77 pub fn default_server_socket_dir() -> PathBuf {
78 // XDG_RUNTIME_DIR should be ignored if it has an insufficient permission.
78 // XDG_RUNTIME_DIR should be ignored if it has an insufficient permission.
79 // https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
79 // https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
80 if let Some(Ok(s)) = env::var_os("XDG_RUNTIME_DIR").map(check_secure_dir) {
80 if let Some(Ok(s)) = env::var_os("XDG_RUNTIME_DIR").map(check_secure_dir) {
81 let mut path = PathBuf::from(s);
81 let mut path = PathBuf::from(s);
82 path.push("chg");
82 path.push("chg");
83 path
83 path
84 } else {
84 } else {
85 let mut path = env::temp_dir();
85 let mut path = env::temp_dir();
86 path.push(format!("chg{}", procutil::get_effective_uid()));
86 path.push(format!("chg{}", procutil::get_effective_uid()));
87 path
87 path
88 }
88 }
89 }
89 }
90
90
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")
94 env::var_os("CHGHG")
95 .or(env::var_os("HG"))
95 .or(env::var_os("HG"))
96 .unwrap_or(OsStr::new("hg").to_owned())
96 .unwrap_or(OsStr::new("hg").to_owned())
97 }
97 }
98
98
99 fn default_timeout() -> Duration {
99 fn default_timeout() -> Duration {
100 let secs = env::var("CHGTIMEOUT")
100 let secs = env::var("CHGTIMEOUT")
101 .ok()
101 .ok()
102 .and_then(|s| s.parse().ok())
102 .and_then(|s| s.parse().ok())
103 .unwrap_or(60);
103 .unwrap_or(60);
104 Duration::from_secs(secs)
104 Duration::from_secs(secs)
105 }
105 }
106
106
107 /// Creates a directory which the other users cannot access to.
107 /// Creates a directory which the other users cannot access to.
108 ///
108 ///
109 /// If the directory already exists, tests its permission.
109 /// If the directory already exists, tests its permission.
110 fn create_secure_dir<P>(path: P) -> io::Result<()>
110 fn create_secure_dir<P>(path: P) -> io::Result<()>
111 where
111 where
112 P: AsRef<Path>,
112 P: AsRef<Path>,
113 {
113 {
114 DirBuilder::new()
114 DirBuilder::new()
115 .mode(0o700)
115 .mode(0o700)
116 .create(path.as_ref())
116 .create(path.as_ref())
117 .or_else(|err| {
117 .or_else(|err| {
118 if err.kind() == io::ErrorKind::AlreadyExists {
118 if err.kind() == io::ErrorKind::AlreadyExists {
119 check_secure_dir(path).map(|_| ())
119 check_secure_dir(path).map(|_| ())
120 } else {
120 } else {
121 Err(err)
121 Err(err)
122 }
122 }
123 })
123 })
124 }
124 }
125
125
126 fn check_secure_dir<P>(path: P) -> io::Result<P>
126 fn check_secure_dir<P>(path: P) -> io::Result<P>
127 where
127 where
128 P: AsRef<Path>,
128 P: AsRef<Path>,
129 {
129 {
130 let a = fs::symlink_metadata(path.as_ref())?;
130 let a = fs::symlink_metadata(path.as_ref())?;
131 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 {
132 Ok(path)
132 Ok(path)
133 } else {
133 } else {
134 Err(io::Error::new(io::ErrorKind::Other, "insecure directory"))
134 Err(io::Error::new(io::ErrorKind::Other, "insecure directory"))
135 }
135 }
136 }
136 }
General Comments 0
You need to be logged in to leave comments. Login now