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