1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//! Standalone binary lair server used by the 'lair-keystore' executable.
//! You probably only want to use this directly if you're writing tests.

use crate::*;
use lair_keystore_api::ipc_keystore::IpcKeystoreServer;
use std::future::Future;

/// Standalone binary lair server used by the 'lair-keystore' executable.
/// You probably only want to use this directly if you're writing tests.
pub struct StandaloneServer {
    config: LairServerConfig,
    srv_hnd: Option<IpcKeystoreServer>,
}

impl StandaloneServer {
    /// Construct a new standalone server. Note, pid-check is acquired here.
    pub async fn new(config: LairServerConfig) -> LairResult<Self> {
        // first make sure we can acquire a pid_file for the given location
        {
            let config = config.clone();
            // TODO - make pid_check async friendly
            tokio::task::spawn_blocking(move || {
                crate::pid_check::pid_check(&config)
            })
            .await
            .map_err(one_err::OneErr::new)??;
        }

        // sanity check that store_file's parent is a directory
        if !tokio::fs::metadata(
            config.store_file.parent().expect("invalid store_file dir"),
        )
        .await?
        .is_dir()
        {
            return Err("invalid store file directory".into());
        }

        // sanity check that store file either doesn't exist or is a file
        match tokio::fs::metadata(&config.store_file).await {
            // it's ok if the store file doesn't exist yet
            Err(_) => (),
            Ok(m) => {
                // if it exists, it must be a file
                if !m.is_file() {
                    return Err("store file is not a file".into());
                }
            }
        }

        Ok(Self {
            config,
            srv_hnd: None,
        })
    }

    /// Run the server.
    pub async fn run<P>(&mut self, passphrase: P) -> LairResult<()>
    where
        P: Into<sodoken::BufRead> + 'static + Send,
    {
        let passphrase = passphrase.into();

        // construct our sqlite store factory
        let store_factory = crate::store_sqlite::create_sql_pool_factory(
            &self.config.store_file,
        );

        // spawn the server
        let srv_hnd = IpcKeystoreServer::new(
            self.config.clone(),
            store_factory,
            passphrase,
        )
        .await?;

        println!(
            "# lair-keystore connection_url # {} #",
            srv_hnd.get_config().connection_url
        );
        println!("# lair-keystore running #");

        self.srv_hnd = Some(srv_hnd);

        Ok(())
    }

    /// get a handle to the LairStore instantiated by this server,
    /// may error if a store has not yet been created.
    pub fn store(
        &self,
    ) -> impl Future<Output = LairResult<LairStore>> + 'static + Send {
        let srv_hnd = self.srv_hnd.clone();
        async move {
            let srv_hnd = srv_hnd.ok_or_else(|| {
                one_err::OneErr::new("server not yet running")
            })?;
            srv_hnd.store().await
        }
    }
}