##// 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 1 // Copyright 2011, 2018 Yuya Nishihara <yuya@tcha.org>
2 2 //
3 3 // This software may be used and distributed according to the terms of the
4 4 // GNU General Public License version 2 or any later version.
5 5
6 6 //! Utility for locating command-server process.
7 7
8 8 use std::env;
9 use std::ffi::{OsStr, OsString};
9 10 use std::fs::{self, DirBuilder};
10 11 use std::io;
12 use std::os::unix::ffi::{OsStrExt, OsStringExt};
11 13 use std::os::unix::fs::{DirBuilderExt, MetadataExt};
12 14 use std::path::{Path, PathBuf};
15 use std::process;
16 use std::time::Duration;
13 17
14 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 57 /// Determines the server socket to connect to.
17 58 ///
18 59 /// If no `$CHGSOCKNAME` is specified, the socket directory will be created
19 60 /// as necessary.
20 61 pub fn prepare_server_socket_path() -> io::Result<PathBuf> {
21 62 if let Some(s) = env::var_os("CHGSOCKNAME") {
22 63 Ok(PathBuf::from(s))
23 64 } else {
24 65 let mut path = default_server_socket_dir();
25 66 create_secure_dir(&path)?;
26 67 path.push("server");
27 68 Ok(path)
28 69 }
29 70 }
30 71
31 72 /// Determines the default server socket path as follows.
32 73 ///
33 74 /// 1. `$XDG_RUNTIME_DIR/chg`
34 75 /// 2. `$TMPDIR/chg$UID`
35 76 /// 3. `/tmp/chg$UID`
36 77 pub fn default_server_socket_dir() -> PathBuf {
37 78 // XDG_RUNTIME_DIR should be ignored if it has an insufficient permission.
38 79 // https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
39 80 if let Some(Ok(s)) = env::var_os("XDG_RUNTIME_DIR").map(check_secure_dir) {
40 81 let mut path = PathBuf::from(s);
41 82 path.push("chg");
42 83 path
43 84 } else {
44 85 let mut path = env::temp_dir();
45 86 path.push(format!("chg{}", procutil::get_effective_uid()));
46 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 102 /// Creates a directory which the other users cannot access to.
51 103 ///
52 104 /// If the directory already exists, tests its permission.
53 105 fn create_secure_dir<P>(path: P) -> io::Result<()>
54 106 where P: AsRef<Path>,
55 107 {
56 108 DirBuilder::new().mode(0o700).create(path.as_ref()).or_else(|err| {
57 109 if err.kind() == io::ErrorKind::AlreadyExists {
58 110 check_secure_dir(path).map(|_| ())
59 111 } else {
60 112 Err(err)
61 113 }
62 114 })
63 115 }
64 116
65 117 fn check_secure_dir<P>(path: P) -> io::Result<P>
66 118 where P: AsRef<Path>,
67 119 {
68 120 let a = fs::symlink_metadata(path.as_ref())?;
69 121 if a.is_dir() && a.uid() == procutil::get_effective_uid() && (a.mode() & 0o777) == 0o700 {
70 122 Ok(path)
71 123 } else {
72 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