comparison rust/chg/src/locator.rs @ 39976:44840bcc411a

rust-chg: port basic socket path handling from cHg of C This is basically modeled after setcmdserveropts() of chg.c.
author Yuya Nishihara <yuya@tcha.org>
date Mon, 24 Sep 2018 18:33:46 +0900
parents
children 7d3285f799cc
comparison
equal deleted inserted replaced
39975:571d8eb39095 39976:44840bcc411a
1 // Copyright 2011, 2018 Yuya Nishihara <yuya@tcha.org>
2 //
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.
5
6 //! Utility for locating command-server process.
7
8 use std::env;
9 use std::fs::{self, DirBuilder};
10 use std::io;
11 use std::os::unix::fs::{DirBuilderExt, MetadataExt};
12 use std::path::{Path, PathBuf};
13
14 use super::procutil;
15
16 /// Determines the server socket to connect to.
17 ///
18 /// If no `$CHGSOCKNAME` is specified, the socket directory will be created
19 /// as necessary.
20 pub fn prepare_server_socket_path() -> io::Result<PathBuf> {
21 if let Some(s) = env::var_os("CHGSOCKNAME") {
22 Ok(PathBuf::from(s))
23 } else {
24 let mut path = default_server_socket_dir();
25 create_secure_dir(&path)?;
26 path.push("server");
27 Ok(path)
28 }
29 }
30
31 /// Determines the default server socket path as follows.
32 ///
33 /// 1. `$XDG_RUNTIME_DIR/chg`
34 /// 2. `$TMPDIR/chg$UID`
35 /// 3. `/tmp/chg$UID`
36 pub fn default_server_socket_dir() -> PathBuf {
37 // XDG_RUNTIME_DIR should be ignored if it has an insufficient permission.
38 // 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) {
40 let mut path = PathBuf::from(s);
41 path.push("chg");
42 path
43 } else {
44 let mut path = env::temp_dir();
45 path.push(format!("chg{}", procutil::get_effective_uid()));
46 path
47 }
48 }
49
50 /// Creates a directory which the other users cannot access to.
51 ///
52 /// If the directory already exists, tests its permission.
53 fn create_secure_dir<P>(path: P) -> io::Result<()>
54 where P: AsRef<Path>,
55 {
56 DirBuilder::new().mode(0o700).create(path.as_ref()).or_else(|err| {
57 if err.kind() == io::ErrorKind::AlreadyExists {
58 check_secure_dir(path).map(|_| ())
59 } else {
60 Err(err)
61 }
62 })
63 }
64
65 fn check_secure_dir<P>(path: P) -> io::Result<P>
66 where P: AsRef<Path>,
67 {
68 let a = fs::symlink_metadata(path.as_ref())?;
69 if a.is_dir() && a.uid() == procutil::get_effective_uid() && (a.mode() & 0o777) == 0o700 {
70 Ok(path)
71 } else {
72 Err(io::Error::new(io::ErrorKind::Other, "insecure directory"))
73 }
74 }