rust/chg/src/locator.rs
author Yuya Nishihara <yuya@tcha.org>
Sun, 07 Oct 2018 10:23:57 +0900
changeset 44673 0a2516efc463
parent 44672 7bf45ed9e25e
child 44675 97e6d435ff7e
permissions -rw-r--r--
rust-chg: move set_current_dir() to Locator This is necessary to run config validation in proper environment. Differential Revision: https://phab.mercurial-scm.org/D8362
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
39976
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
44671
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
     8
use futures::future::{self, Either, Loop};
39976
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
     9
use std::env;
40289
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    10
use std::ffi::{OsStr, OsString};
39976
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    11
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
    12
use std::io;
40289
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    13
use std::os::unix::ffi::{OsStrExt, OsStringExt};
39976
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    14
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
    15
use std::path::{Path, PathBuf};
44671
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    16
use std::process::{self, Command};
40289
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    17
use std::time::Duration;
44671
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    18
use tokio::prelude::*;
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    19
use tokio_hglib::UnixClient;
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    20
use tokio_process::{Child, CommandExt};
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    21
use tokio_timer;
39976
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    22
44673
0a2516efc463 rust-chg: move set_current_dir() to Locator
Yuya Nishihara <yuya@tcha.org>
parents: 44672
diff changeset
    23
use super::clientext::ChgClientExt;
44672
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
    24
use super::message::ServerSpec;
39976
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    25
use super::procutil;
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
    26
44672
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
    27
const REQUIRED_SERVER_CAPABILITIES: &[&str] = &["attachio", "chdir", "runcommand"];
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
    28
40289
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    29
/// 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: 39976
diff changeset
    30
#[derive(Clone, Debug)]
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    31
pub struct Locator {
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    32
    hg_command: OsString,
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    33
    current_dir: PathBuf,
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    34
    env_vars: Vec<(OsString, OsString)>,
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    35
    process_id: u32,
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    36
    base_sock_path: PathBuf,
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    37
    timeout: Duration,
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    38
}
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    39
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    40
impl Locator {
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    41
    /// 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: 39976
diff changeset
    42
    ///
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    43
    /// 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: 39976
diff changeset
    44
    /// created as necessary.
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    45
    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: 39976
diff changeset
    46
        Ok(Locator {
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    47
            hg_command: default_hg_command(),
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    48
            current_dir: env::current_dir()?,
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    49
            env_vars: env::vars_os().collect(),
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    50
            process_id: process::id(),
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    51
            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: 39976
diff changeset
    52
            timeout: default_timeout(),
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    53
        })
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    54
    }
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    55
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    56
    /// 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: 39976
diff changeset
    57
    fn temp_sock_path(&self) -> PathBuf {
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    58
        let src = self.base_sock_path.as_os_str().as_bytes();
44668
c11d98cff883 rust-chg: add brief comment about initial capacity of temp_sock_path()
Yuya Nishihara <yuya@tcha.org>
parents: 43818
diff changeset
    59
        let mut buf = Vec::with_capacity(src.len() + 6); // "{src}.{pid}".len()
40289
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    60
        buf.extend_from_slice(src);
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    61
        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: 39976
diff changeset
    62
        OsString::from_vec(buf).into()
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
    63
    }
44671
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    64
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    65
    /// Connects to the server.
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    66
    ///
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    67
    /// The server process will be spawned if not running.
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    68
    pub fn connect(self) -> impl Future<Item = (Self, UnixClient), Error = io::Error> {
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    69
        self.try_connect()
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    70
    }
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    71
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    72
    /// Tries to connect to the existing server, or spawns new if not running.
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    73
    fn try_connect(self) -> impl Future<Item = (Self, UnixClient), Error = io::Error> {
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    74
        debug!("try connect to {}", self.base_sock_path.display());
44672
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
    75
        UnixClient::connect(self.base_sock_path.clone())
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
    76
            .then(|res| match res {
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
    77
                Ok(client) => Either::A(future::ok((self, client))),
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
    78
                Err(_) => Either::B(self.spawn_connect()),
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
    79
            })
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
    80
            .and_then(|(loc, client)| {
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
    81
                check_server_capabilities(client.server_spec())?;
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
    82
                Ok((loc, client))
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
    83
            })
44673
0a2516efc463 rust-chg: move set_current_dir() to Locator
Yuya Nishihara <yuya@tcha.org>
parents: 44672
diff changeset
    84
            .and_then(|(loc, client)| {
0a2516efc463 rust-chg: move set_current_dir() to Locator
Yuya Nishihara <yuya@tcha.org>
parents: 44672
diff changeset
    85
                client
0a2516efc463 rust-chg: move set_current_dir() to Locator
Yuya Nishihara <yuya@tcha.org>
parents: 44672
diff changeset
    86
                    .set_current_dir(&loc.current_dir)
0a2516efc463 rust-chg: move set_current_dir() to Locator
Yuya Nishihara <yuya@tcha.org>
parents: 44672
diff changeset
    87
                    .map(|client| (loc, client))
0a2516efc463 rust-chg: move set_current_dir() to Locator
Yuya Nishihara <yuya@tcha.org>
parents: 44672
diff changeset
    88
            })
44671
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    89
    }
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    90
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    91
    /// Spawns new server process and connects to it.
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    92
    ///
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    93
    /// The server will be spawned at the current working directory, then
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    94
    /// chdir to "/", so that the server will load configs from the target
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    95
    /// repository.
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    96
    fn spawn_connect(self) -> impl Future<Item = (Self, UnixClient), Error = io::Error> {
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    97
        let sock_path = self.temp_sock_path();
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    98
        debug!("start cmdserver at {}", sock_path.display());
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
    99
        Command::new(&self.hg_command)
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   100
            .arg("serve")
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   101
            .arg("--cmdserver")
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   102
            .arg("chgunix")
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   103
            .arg("--address")
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   104
            .arg(&sock_path)
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   105
            .arg("--daemon-postexec")
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   106
            .arg("chdir:/")
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   107
            .current_dir(&self.current_dir)
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   108
            .env_clear()
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   109
            .envs(self.env_vars.iter().cloned())
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   110
            .env("CHGINTERNALMARK", "")
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   111
            .spawn_async()
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   112
            .into_future()
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   113
            .and_then(|server| self.connect_spawned(server, sock_path))
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   114
            .and_then(|(loc, client, sock_path)| {
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   115
                debug!(
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   116
                    "rename {} to {}",
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   117
                    sock_path.display(),
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   118
                    loc.base_sock_path.display()
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   119
                );
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   120
                fs::rename(&sock_path, &loc.base_sock_path)?;
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   121
                Ok((loc, client))
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   122
            })
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   123
    }
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   124
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   125
    /// Tries to connect to the just spawned server repeatedly until timeout
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   126
    /// exceeded.
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   127
    fn connect_spawned(
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   128
        self,
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   129
        server: Child,
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   130
        sock_path: PathBuf,
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   131
    ) -> impl Future<Item = (Self, UnixClient, PathBuf), Error = io::Error> {
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   132
        debug!("try connect to {} repeatedly", sock_path.display());
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   133
        let connect = future::loop_fn(sock_path, |sock_path| {
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   134
            UnixClient::connect(sock_path.clone()).then(|res| {
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   135
                match res {
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   136
                    Ok(client) => Either::A(future::ok(Loop::Break((client, sock_path)))),
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   137
                    Err(_) => {
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   138
                        // try again with slight delay
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   139
                        let fut = tokio_timer::sleep(Duration::from_millis(10))
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   140
                            .map(|()| Loop::Continue(sock_path))
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   141
                            .map_err(|err| io::Error::new(io::ErrorKind::Other, err));
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   142
                        Either::B(fut)
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   143
                    }
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   144
                }
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   145
            })
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   146
        });
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   147
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   148
        // waits for either connection established or server failed to start
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   149
        connect
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   150
            .select2(server)
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   151
            .map_err(|res| res.split().0)
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   152
            .timeout(self.timeout)
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   153
            .map_err(|err| {
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   154
                err.into_inner().unwrap_or_else(|| {
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   155
                    io::Error::new(
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   156
                        io::ErrorKind::TimedOut,
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   157
                        "timed out while connecting to server",
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   158
                    )
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   159
                })
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   160
            })
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   161
            .and_then(|res| {
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   162
                match res {
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   163
                    Either::A(((client, sock_path), server)) => {
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   164
                        server.forget(); // continue to run in background
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   165
                        Ok((self, client, sock_path))
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   166
                    }
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   167
                    Either::B((st, _)) => Err(io::Error::new(
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   168
                        io::ErrorKind::Other,
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   169
                        format!("server exited too early: {}", st),
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   170
                    )),
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   171
                }
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   172
            })
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   173
    }
40289
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
   174
}
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
   175
39976
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   176
/// 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
   177
///
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   178
/// 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
   179
/// as necessary.
44671
bb936e25a84a rust-chg: spawn server process if not running
Yuya Nishihara <yuya@tcha.org>
parents: 44668
diff changeset
   180
fn prepare_server_socket_path() -> io::Result<PathBuf> {
39976
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   181
    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
   182
        Ok(PathBuf::from(s))
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   183
    } else {
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   184
        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
   185
        create_secure_dir(&path)?;
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   186
        path.push("server");
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   187
        Ok(path)
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   188
    }
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   189
}
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   190
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   191
/// 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
   192
///
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   193
/// 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
   194
/// 2. `$TMPDIR/chg$UID`
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   195
/// 3. `/tmp/chg$UID`
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   196
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
   197
    // 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
   198
    // 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
   199
    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
   200
        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
   201
        path.push("chg");
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   202
        path
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   203
    } else {
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   204
        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
   205
        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
   206
        path
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   207
    }
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   208
}
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   209
40289
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
   210
/// Determines the default hg command.
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
   211
pub fn default_hg_command() -> OsString {
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
   212
    // TODO: maybe allow embedding the path at compile time (or load from hgrc)
43818
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   213
    env::var_os("CHGHG")
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   214
        .or(env::var_os("HG"))
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   215
        .unwrap_or(OsStr::new("hg").to_owned())
40289
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
   216
}
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
   217
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
   218
fn default_timeout() -> Duration {
43818
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   219
    let secs = env::var("CHGTIMEOUT")
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   220
        .ok()
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   221
        .and_then(|s| s.parse().ok())
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   222
        .unwrap_or(60);
40289
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
   223
    Duration::from_secs(secs)
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
   224
}
7d3285f799cc rust-chg: add struct holding information needed to spawn server process
Yuya Nishihara <yuya@tcha.org>
parents: 39976
diff changeset
   225
39976
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   226
/// 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
   227
///
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   228
/// 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
   229
fn create_secure_dir<P>(path: P) -> io::Result<()>
43818
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   230
where
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   231
    P: AsRef<Path>,
39976
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   232
{
43818
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   233
    DirBuilder::new()
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   234
        .mode(0o700)
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   235
        .create(path.as_ref())
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   236
        .or_else(|err| {
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   237
            if err.kind() == io::ErrorKind::AlreadyExists {
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   238
                check_secure_dir(path).map(|_| ())
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   239
            } else {
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   240
                Err(err)
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   241
            }
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   242
        })
39976
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   243
}
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   244
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   245
fn check_secure_dir<P>(path: P) -> io::Result<P>
43818
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   246
where
ce088b38f92b rust: run rustfmt
Gregory Szorc <gregory.szorc@gmail.com>
parents: 40289
diff changeset
   247
    P: AsRef<Path>,
39976
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   248
{
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   249
    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
   250
    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
   251
        Ok(path)
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   252
    } else {
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   253
        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
   254
    }
44840bcc411a rust-chg: port basic socket path handling from cHg of C
Yuya Nishihara <yuya@tcha.org>
parents:
diff changeset
   255
}
44672
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
   256
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
   257
fn check_server_capabilities(spec: &ServerSpec) -> io::Result<()> {
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
   258
    let unsupported: Vec<_> = REQUIRED_SERVER_CAPABILITIES
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
   259
        .iter()
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
   260
        .cloned()
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
   261
        .filter(|&s| !spec.capabilities.contains(s))
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
   262
        .collect();
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
   263
    if unsupported.is_empty() {
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
   264
        Ok(())
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
   265
    } else {
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
   266
        let msg = format!(
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
   267
            "insufficient server capabilities: {}",
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
   268
            unsupported.join(", ")
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
   269
        );
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
   270
        Err(io::Error::new(io::ErrorKind::Other, msg))
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
   271
    }
7bf45ed9e25e rust-chg: abort if server doesn't have required capabilities
Yuya Nishihara <yuya@tcha.org>
parents: 44671
diff changeset
   272
}