rust/chg/src/locator.rs
author Yuya Nishihara <yuya@tcha.org>
Fri, 14 Dec 2018 21:19:19 +0900
changeset 40955 e48fdeb08c77
parent 40289 7d3285f799cc
child 43836 ce088b38f92b
permissions -rw-r--r--
py3: fix bad escapes of sub() replacement pattern in test-template-basic.t Python 3.7 starts complaining about it. We have to double the backslash or '\x5c' to get around.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
39977
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
     1
// Copyright 2011, 2018 Yuya Nishihara <yuya@tcha.org>
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
     2
//
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
     3
// This software may be used and distributed according to the terms of the
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
     4
// GNU General Public License version 2 or any later version.
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
     5
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
     6
//! Utility for locating command-server process.
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
     7
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
     8
use std::env;
40289
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
     9
use std::ffi::{OsStr, OsString};
39977
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    10
use std::fs::{self, DirBuilder};
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    11
use std::io;
40289
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    12
use std::os::unix::ffi::{OsStrExt, OsStringExt};
39977
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    13
use std::os::unix::fs::{DirBuilderExt, MetadataExt};
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    14
use std::path::{Path, PathBuf};
40289
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    15
use std::process;
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    16
use std::time::Duration;
39977
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    17
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    18
use super::procutil;
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    19
40289
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    20
/// Helper to connect to and spawn a server process.
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    21
#[derive(Clone, Debug)]
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    22
pub struct Locator {
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    23
    hg_command: OsString,
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    24
    current_dir: PathBuf,
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    25
    env_vars: Vec<(OsString, OsString)>,
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    26
    process_id: u32,
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    27
    base_sock_path: PathBuf,
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    28
    timeout: Duration,
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    29
}
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    30
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    31
impl Locator {
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    32
    /// Creates locator capturing the current process environment.
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    33
    ///
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    34
    /// If no `$CHGSOCKNAME` is specified, the socket directory will be
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    35
    /// created as necessary.
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    36
    pub fn prepare_from_env() -> io::Result<Locator> {
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    37
        Ok(Locator {
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    38
            hg_command: default_hg_command(),
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    39
            current_dir: env::current_dir()?,
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    40
            env_vars: env::vars_os().collect(),
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    41
            process_id: process::id(),
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    42
            base_sock_path: prepare_server_socket_path()?,
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    43
            timeout: default_timeout(),
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    44
        })
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    45
    }
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    46
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    47
    /// Temporary socket path for this client process.
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    48
    fn temp_sock_path(&self) -> PathBuf {
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    49
        let src = self.base_sock_path.as_os_str().as_bytes();
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    50
        let mut buf = Vec::with_capacity(src.len() + 6);
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    51
        buf.extend_from_slice(src);
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    52
        buf.extend_from_slice(format!(".{}", self.process_id).as_bytes());
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    53
        OsString::from_vec(buf).into()
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    54
    }
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    55
}
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    56
39977
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    57
/// Determines the server socket to connect to.
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    58
///
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    59
/// If no `$CHGSOCKNAME` is specified, the socket directory will be created
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    60
/// as necessary.
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    61
pub fn prepare_server_socket_path() -> io::Result<PathBuf> {
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    62
    if let Some(s) = env::var_os("CHGSOCKNAME") {
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    63
        Ok(PathBuf::from(s))
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    64
    } else {
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    65
        let mut path = default_server_socket_dir();
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    66
        create_secure_dir(&path)?;
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    67
        path.push("server");
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    68
        Ok(path)
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    69
    }
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    70
}
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    71
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    72
/// Determines the default server socket path as follows.
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    73
///
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    74
/// 1. `$XDG_RUNTIME_DIR/chg`
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    75
/// 2. `$TMPDIR/chg$UID`
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    76
/// 3. `/tmp/chg$UID`
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    77
pub fn default_server_socket_dir() -> PathBuf {
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    78
    // XDG_RUNTIME_DIR should be ignored if it has an insufficient permission.
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    79
    // https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    80
    if let Some(Ok(s)) = env::var_os("XDG_RUNTIME_DIR").map(check_secure_dir) {
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    81
        let mut path = PathBuf::from(s);
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    82
        path.push("chg");
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    83
        path
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    84
    } else {
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    85
        let mut path = env::temp_dir();
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    86
        path.push(format!("chg{}", procutil::get_effective_uid()));
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    87
        path
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    88
    }
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    89
}
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    90
40289
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    91
/// Determines the default hg command.
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    92
pub fn default_hg_command() -> OsString {
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    93
    // TODO: maybe allow embedding the path at compile time (or load from hgrc)
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    94
    env::var_os("CHGHG").or(env::var_os("HG")).unwrap_or(OsStr::new("hg").to_owned())
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    95
}
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    96
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    97
fn default_timeout() -> Duration {
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    98
    let secs = env::var("CHGTIMEOUT").ok().and_then(|s| s.parse().ok()).unwrap_or(60);
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
    99
    Duration::from_secs(secs)
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
   100
}
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39977
diff changeset
   101
39977
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   102
/// Creates a directory which the other users cannot access to.
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   103
///
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   104
/// If the directory already exists, tests its permission.
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   105
fn create_secure_dir<P>(path: P) -> io::Result<()>
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   106
    where P: AsRef<Path>,
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   107
{
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   108
    DirBuilder::new().mode(0o700).create(path.as_ref()).or_else(|err| {
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   109
        if err.kind() == io::ErrorKind::AlreadyExists {
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   110
            check_secure_dir(path).map(|_| ())
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   111
        } else {
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   112
            Err(err)
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   113
        }
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   114
    })
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   115
}
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   116
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   117
fn check_secure_dir<P>(path: P) -> io::Result<P>
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   118
    where P: AsRef<Path>,
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   119
{
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   120
    let a = fs::symlink_metadata(path.as_ref())?;
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   121
    if a.is_dir() && a.uid() == procutil::get_effective_uid() && (a.mode() & 0o777) == 0o700 {
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   122
        Ok(path)
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   123
    } else {
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   124
        Err(io::Error::new(io::ErrorKind::Other, "insecure directory"))
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   125
    }
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   126
}